updates.
[silc.git] / lib / silcclient / protocol.c
index d0b8064c1093adc736e96f4cd8dbd5f3bd179f79..d0734863b377f9bc9df1208c1d5c7016e27fce5b 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
 
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Copyright (C) 1997 - 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
@@ -119,6 +119,34 @@ static void silc_client_protocol_ke_set_keys(SilcSKE ske,
   silc_hmac_set_key(conn->hmac, keymat->hmac_key, keymat->hmac_key_len);
 }
 
+/* Checks the version string of the server. */
+
+SilcSKEStatus silc_ske_check_version(SilcSKE ske, unsigned char *version,
+                                    unsigned int len)
+{
+  SilcClientConnection conn = (SilcClientConnection)ske->sock->user_data;
+  SilcClient client = (SilcClient)ske->user_data;
+  SilcSKEStatus status = SILC_SKE_STATUS_OK;
+
+  /* Check for initial version string */
+  if (!strstr(version, "SILC-1.0-"))
+    status = SILC_SKE_STATUS_BAD_VERSION;
+
+  /* Check software version */
+
+  if (len < strlen(silc_version_string))
+    status = SILC_SKE_STATUS_BAD_VERSION;
+
+  /* XXX for now there is no other tests due to the abnormal version
+     string that is used */
+
+  if (status != SILC_SKE_STATUS_OK)
+    client->ops->say(client, conn, 
+                    "We don't support server version `%s'", version);
+
+  return status;
+}
+
 /* Performs key exchange protocol. This is used for both initiator
    and responder key exchange. This may be called recursively. */
 
@@ -129,7 +157,7 @@ SILC_TASK_CALLBACK(silc_client_protocol_key_exchange)
     (SilcClientKEInternalContext *)protocol->context;
   SilcClient client = (SilcClient)ctx->client;
   SilcClientConnection conn = ctx->sock->user_data;
-  SilcSKEStatus status;
+  SilcSKEStatus status = 0;
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -147,6 +175,8 @@ SILC_TASK_CALLBACK(silc_client_protocol_key_exchange)
       /* Allocate Key Exchange object */
       ske = silc_ske_alloc();
       ctx->ske = ske;
+      ske->rng = client->rng;
+      ske->user_data = (void *)client;
       
       if (ctx->responder == TRUE) {
 #if 0
@@ -164,7 +194,8 @@ SILC_TASK_CALLBACK(silc_client_protocol_key_exchange)
        SilcSKEStartPayload *start_payload;
 
        /* Assemble security properties. */
-       silc_ske_assemble_security_properties(ske, silc_version_string,
+       silc_ske_assemble_security_properties(ske, SILC_SKE_SP_FLAG_NONE, 
+                                             silc_version_string,
                                              &start_payload);
 
        /* Start the key exchange by sending our security properties
@@ -176,11 +207,14 @@ SILC_TASK_CALLBACK(silc_client_protocol_key_exchange)
       }
 
       if (status != SILC_SKE_STATUS_OK) {
-       switch(status) {
-         
-       default:
-         break;
-       }
+       SILC_LOG_WARNING(("Error (type %d) during Key Exchange protocol",
+                         status));
+       SILC_LOG_DEBUG(("Error (type %d) during Key Exchange protocol",
+                       status));
+
+       protocol->state = SILC_PROTOCOL_STATE_ERROR;
+       protocol->execute(client->timeout_queue, 0, protocol, fd, 0, 0);
+       return;
       }
 
       /* Advance the state of the protocol. */
@@ -205,12 +239,19 @@ SILC_TASK_CALLBACK(silc_client_protocol_key_exchange)
           paylaod reply we just got from the responder. The callback
           function will receive the processed payload where we will
           save it. */
-       status = silc_ske_initiator_phase_1(ctx->ske, ctx->packet, NULL, NULL);
+       status = silc_ske_initiator_phase_1(ctx->ske, ctx->packet->buffer, 
+                                           NULL, NULL);
       }
 
-      switch(status) {
-      default:
-       break;
+      if (status != SILC_SKE_STATUS_OK) {
+       SILC_LOG_WARNING(("Error (type %d) during Key Exchange protocol",
+                         status));
+       SILC_LOG_DEBUG(("Error (type %d) during Key Exchange protocol",
+                       status));
+
+       protocol->state = SILC_PROTOCOL_STATE_ERROR;
+       protocol->execute(client->timeout_queue, 0, protocol, fd, 0, 0);
+       return;
       }
 
       /* Advance the state of the protocol and call the next state. */
@@ -242,9 +283,15 @@ SILC_TASK_CALLBACK(silc_client_protocol_key_exchange)
                                     context);
       }
 
-      switch(status) {
-      default:
-       break;
+      if (status != SILC_SKE_STATUS_OK) {
+       SILC_LOG_WARNING(("Error (type %d) during Key Exchange protocol",
+                         status));
+       SILC_LOG_DEBUG(("Error (type %d) during Key Exchange protocol",
+                       status));
+
+       protocol->state = SILC_PROTOCOL_STATE_ERROR;
+       protocol->execute(client->timeout_queue, 0, protocol, fd, 0, 0);
+       return;
       }
 
       /* Advance the state of the protocol. */
@@ -268,7 +315,7 @@ SILC_TASK_CALLBACK(silc_client_protocol_key_exchange)
       } else {
        /* Finish the protocol. This verifies the Key Exchange 2 payload
           sent by responder. */
-       status = silc_ske_initiator_finish(ctx->ske, ctx->packet,
+       status = silc_ske_initiator_finish(ctx->ske, ctx->packet->buffer,
                                           silc_client_protocol_ke_verify_key,
                                           context, NULL, NULL);
       }
@@ -297,16 +344,20 @@ SILC_TASK_CALLBACK(silc_client_protocol_key_exchange)
       protocol->state = SILC_PROTOCOL_STATE_END;
     }
     break;
+
   case SILC_PROTOCOL_STATE_END:
     {
       /* 
        * End protocol
        */
       SilcSKEKeyMaterial *keymat;
+      int key_len = silc_cipher_get_key_len(ctx->ske->prop->cipher, NULL);
+      int hash_len = ctx->ske->prop->hash->hash->hash_len;
 
       /* Process the key material */
       keymat = silc_calloc(1, sizeof(*keymat));
-      silc_ske_process_key_material(ctx->ske, 16, (16 * 8), 16, keymat);
+      silc_ske_process_key_material(ctx->ske, 16, key_len, hash_len, 
+                                   keymat);
 
       /* Take the negotiated keys into use. */
       silc_client_protocol_ke_set_keys(ctx->ske, ctx->sock, keymat,
@@ -314,6 +365,8 @@ SILC_TASK_CALLBACK(silc_client_protocol_key_exchange)
                                       ctx->ske->prop->pkcs,
                                       ctx->ske->prop->hash);
 
+      silc_ske_free_key_material(keymat);
+
       /* Protocol has ended, call the final callback */
       if (protocol->final_callback)
        protocol->execute_final(client->timeout_queue, 0, protocol, fd);
@@ -321,8 +374,29 @@ SILC_TASK_CALLBACK(silc_client_protocol_key_exchange)
        silc_protocol_free(protocol);
     }
     break;
+
   case SILC_PROTOCOL_STATE_ERROR:
+    /*
+     * Error during protocol
+     */
     
+    /* Send abort notification */
+    silc_ske_abort(ctx->ske, ctx->ske->status, 
+                  silc_client_protocol_ke_send_packet,
+                  context);
+
+    /* On error the final callback is always called. */
+    if (protocol->final_callback)
+      protocol->execute_final(client->timeout_queue, 0, protocol, fd);
+    else
+      silc_protocol_free(protocol);
+    break;
+
+  case SILC_PROTOCOL_STATE_FAILURE:
+    /*
+     * Received failure from remote.
+     */
+
     /* On error the final callback is always called. */
     if (protocol->final_callback)
       protocol->execute_final(client->timeout_queue, 0, protocol, fd);
@@ -364,11 +438,11 @@ SILC_TASK_CALLBACK(silc_client_protocol_connection_auth)
       unsigned int auth_data_len = 0;
 
       switch(ctx->auth_meth) {
-      case SILC_PROTOCOL_CONN_AUTH_NONE:
+      case SILC_AUTH_NONE:
        /* No authentication required */
        break;
 
-      case SILC_PROTOCOL_CONN_AUTH_PASSWORD:
+      case SILC_AUTH_PASSWORD:
        /* Password authentication */
        if (ctx->auth_data && ctx->auth_data_len) {
          auth_data = ctx->auth_data;
@@ -383,7 +457,7 @@ SILC_TASK_CALLBACK(silc_client_protocol_connection_auth)
        auth_data_len = strlen(auth_data);
        break;
 
-      case SILC_PROTOCOL_CONN_AUTH_PUBLIC_KEY:
+      case SILC_AUTH_PUBLIC_KEY:
        /* XXX */
        break;
       }
@@ -431,13 +505,16 @@ SILC_TASK_CALLBACK(silc_client_protocol_connection_auth)
   case SILC_PROTOCOL_STATE_ERROR:
     {
       /* 
-       * Error
+       * Error. Send notify to remote.
        */
+      unsigned char error[4];
+
+      SILC_PUT32_MSB(SILC_AUTH_FAILED, error);
 
       /* Error in protocol. Send FAILURE packet. Although I don't think
         this could ever happen on client side. */
       silc_client_packet_send(client, ctx->sock, SILC_PACKET_FAILURE,
-                             NULL, 0, NULL, NULL, NULL, 0, TRUE);
+                             NULL, 0, NULL, NULL, error, 4, TRUE);
 
       /* On error the final callback is always called. */
       if (protocol->final_callback)
@@ -445,7 +522,19 @@ SILC_TASK_CALLBACK(silc_client_protocol_connection_auth)
       else
        silc_protocol_free(protocol);
     }
+
+  case SILC_PROTOCOL_STATE_FAILURE:
+    /*
+     * Received failure from remote.
+     */
+
+    /* On error the final callback is always called. */
+    if (protocol->final_callback)
+      protocol->execute_final(client->timeout_queue, 0, protocol, fd);
+    else
+      silc_protocol_free(protocol);
     break;
+
   case SILC_PROTOCOL_STATE_UNKNOWN:
     break;
   }