From 45018b4f872c03c48414d24f4a9ecaec27393889 Mon Sep 17 00:00:00 2001
From: Andreas Sigfridsson <sigge@lysator.liu.se>
Date: Sun, 22 Oct 2000 13:35:14 +0200
Subject: [PATCH] Added SSL client certificate support.

Rev: lib/modules/SSL.pmod/DOC:1.2
Rev: lib/modules/SSL.pmod/connection.pike:1.14
Rev: lib/modules/SSL.pmod/context.pike:1.12
Rev: lib/modules/SSL.pmod/handshake.pike:1.21
Rev: lib/modules/SSL.pmod/session.pike:1.12
Rev: lib/modules/SSL.pmod/sslfile.pike:1.25
Rev: lib/modules/SSL.pmod/sslport.pike:1.7
---
 lib/modules/SSL.pmod/DOC             | 17 +++--
 lib/modules/SSL.pmod/connection.pike |  4 +-
 lib/modules/SSL.pmod/context.pike    |  7 +-
 lib/modules/SSL.pmod/handshake.pike  | 97 ++++++++++++++++++++--------
 lib/modules/SSL.pmod/session.pike    |  6 +-
 lib/modules/SSL.pmod/sslfile.pike    | 13 +++-
 lib/modules/SSL.pmod/sslport.pike    |  1 +
 7 files changed, 100 insertions(+), 45 deletions(-)

diff --git a/lib/modules/SSL.pmod/DOC b/lib/modules/SSL.pmod/DOC
index f53a447223..45b55b93ad 100644
--- a/lib/modules/SSL.pmod/DOC
+++ b/lib/modules/SSL.pmod/DOC
@@ -145,11 +145,6 @@ SSL.session (inherits cipher)
 
 SSL.context
 
-  int auth_level
-
-    Policy for client authentication. One of AUTHLEVEL_none,
-    AUTHLEVEL_ask and AUTHLEVEL_require.
-
   object rsa
 
     The server's private key
@@ -181,7 +176,6 @@ SSL.context
     The server's certificate, or a chain of certificates, with the
     server's certificate first and root certificate last.
 
-  array(string) authorities
   array(int) preferred_auth_methods
 
     For client authentication. Used only if auth_level is AUTH_ask or
@@ -231,6 +225,17 @@ SSL.context
 
 SSL.handshake
 
+  int auth_level
+
+    Policy for client authentication. One of AUTHLEVEL_none,
+    AUTHLEVEL_ask and AUTHLEVEL_require.
+
+  array(string) authorities
+
+    Array of authorities that are accepted for client certificates.
+    The client will only send certificates that are signed by any of
+    these authorities. The string is the DER-encoded issuer.
+
   object session
   object context
 
diff --git a/lib/modules/SSL.pmod/connection.pike b/lib/modules/SSL.pmod/connection.pike
index e45f44698b..c5137ded5c 100644
--- a/lib/modules/SSL.pmod/connection.pike
+++ b/lib/modules/SSL.pmod/connection.pike
@@ -1,4 +1,4 @@
-/* $Id: connection.pike,v 1.13 2000/08/04 19:07:11 sigge Exp $
+/* $Id: connection.pike,v 1.14 2000/10/22 11:35:13 sigge Exp $
  *
  * SSL packet layer
  */
@@ -176,7 +176,7 @@ int handle_alert(string s)
   }
   if (description == ALERT_no_certificate)
   {
-    if ((certificate_state == CERT_requested) && (context->auth_level == AUTHLEVEL_ask))
+    if ((certificate_state == CERT_requested) && (auth_level == AUTHLEVEL_ask))
     {
       certificate_state = CERT_no_certificate;
       return 0;
diff --git a/lib/modules/SSL.pmod/context.pike b/lib/modules/SSL.pmod/context.pike
index eec99d2182..2f05e5baf8 100644
--- a/lib/modules/SSL.pmod/context.pike
+++ b/lib/modules/SSL.pmod/context.pike
@@ -1,4 +1,4 @@
-/* $Id: context.pike,v 1.11 2000/08/04 19:08:07 sigge Exp $
+/* $Id: context.pike,v 1.12 2000/10/22 11:35:13 sigge Exp $
  *
  * Keeps track of global data for an SSL server,
  * such as preferred encryption algorithms and session cache.
@@ -6,8 +6,6 @@
 
 inherit "constants";
 
-int auth_level;
-
 object rsa;  /* Servers private key */
 
 /* These temporary keys, of non-zero, are used for the
@@ -24,9 +22,6 @@ function(int:string) random; /* Random number generator */
  * Senders certificate first, root certificate last .*/
 array(string) certificates; 
 
-/* For client authentication */
-array(string) authorities; /* List of authorities distinguished names */
-
 array(int) preferred_auth_methods =
 ({ AUTH_rsa_sign });
 
diff --git a/lib/modules/SSL.pmod/handshake.pike b/lib/modules/SSL.pmod/handshake.pike
index a5f798d59c..0d224b0db1 100644
--- a/lib/modules/SSL.pmod/handshake.pike
+++ b/lib/modules/SSL.pmod/handshake.pike
@@ -1,4 +1,4 @@
-/* $Id: handshake.pike,v 1.20 2000/09/05 15:06:40 per Exp $
+/* $Id: handshake.pike,v 1.21 2000/10/22 11:35:13 sigge Exp $
  *
  */
 
@@ -12,6 +12,11 @@ inherit "cipher";
 #define SSL3_DEBUG_MSG
 #endif /* SSL3_DEBUG */
 
+/* For client authentication */
+int auth_level;			// Wether to ask or require a client certificate
+array(string) authorities;	// List of authorities accepted for client
+				// certificates (DER-encoded)
+
 object session;
 object context;
 
@@ -72,6 +77,11 @@ object handshake_packet(int type, string data)
   return packet;
 }
 
+object hello_request()
+{
+  return handshake_packet(HANDSHAKE_hello_request, "");
+}
+
 object server_hello_packet()
 {
   object struct = Struct();
@@ -255,15 +265,15 @@ int reply_new_session(array(int) cipher_suites, array(int) compression_methods)
   if (key_exchange)
     send_packet(key_exchange);
   
-  if (context->auth_level >= AUTHLEVEL_ask)
+  if (auth_level >= AUTHLEVEL_ask)
   {
     /* Send a CertificateRequest message */
     object struct = Struct();
     struct->put_var_uint_array(context->preferred_auth_methods, 1, 1);
 
-    int len = `+(@ Array.map(context->authorities, strlen));
-    struct->put_uint(len + 2 * sizeof(context->authorities), 2);
-    foreach(context->authorities, string auth)
+    int len = `+(@ Array.map(authorities, strlen));
+    struct->put_uint(len + 2 * sizeof(authorities), 2);
+    foreach(authorities, string auth)
       struct->put_var_string(auth, 2);
     send_packet(handshake_packet(HANDSHAKE_certificate_request,
 				 struct->pop_data()));
@@ -334,7 +344,9 @@ string server_derive_master_secret(string data)
 	  dh_state->set_other(struct->get_bignum);
 	} || !struct->is_empty())
       {
-	send_packet(Alert(ALERT_fatal, ALERT_unexpected_message));
+	send_packet(Alert(ALERT_fatal, ALERT_unexpected_message,
+		      "SSL.session->handle_handshake: unexpected message\n",
+		      backtrace()));
 	return 0;
       }
 
@@ -502,7 +514,9 @@ int handle_handshake(int type, string data, string raw)
 
 	} || (version[0] != 3) || (cipher_len & 1))
 	{
-	  send_packet(Alert(ALERT_fatal, ALERT_unexpected_message));
+	  send_packet(Alert(ALERT_fatal, ALERT_unexpected_message,
+		      "SSL.session->handle_handshake: unexpected message\n",
+		      backtrace()));
 	  return -1;
 	}
 
@@ -574,7 +588,9 @@ int handle_handshake(int type, string data, string raw)
 	  werror(sprintf("SSL.handshake: Error decoding SSL2 handshake:\n"
 			 "%s\n", describe_backtrace(err)));
 #endif /* SSL3_DEBUG */
-	  send_packet(Alert(ALERT_fatal, ALERT_unexpected_message));
+	  send_packet(Alert(ALERT_fatal, ALERT_unexpected_message,
+		      "SSL.session->handle_handshake: unexpected message\n",
+		      backtrace()));
 	  return -1;
 	}
 
@@ -590,15 +606,19 @@ int handle_handshake(int type, string data, string raw)
 	  challenge = input->get_fix_string(ch_len);
 	} || !input->is_empty()) 
 	{
-	  send_packet(Alert(ALERT_fatal, ALERT_unexpected_message));
+	  send_packet(Alert(ALERT_fatal, ALERT_unexpected_message,
+		      "SSL.session->handle_handshake: unexpected message\n",
+		      backtrace()));
 	  return -1;
 	}
 	client_random = ("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + challenge)[..31];
-	err = reply_new_session(cipher_suites, ({ COMPRESSION_null }) );
-	if (err)
-	  return err;
+	{
+	  int err = reply_new_session(cipher_suites, ({ COMPRESSION_null }) );
+	  if (err)
+	    return err;
+	}
 	handshake_state = STATE_server_wait_for_client;
-	
+
 	break;
       }
      }
@@ -620,7 +640,9 @@ int handle_handshake(int type, string data, string raw)
 	 digest = input->get_fix_string(36);
        } || !input->is_empty())
        {
-	 send_packet(Alert(ALERT_fatal, ALERT_unexpected_message));
+	 send_packet(Alert(ALERT_fatal, ALERT_unexpected_message,
+		      "SSL.session->handle_handshake: unexpected message\n",
+		      backtrace()));
 	 return -1;
        }
        if (rsa_message_was_bad       /* Error delayed until now */
@@ -630,7 +652,9 @@ int handle_handshake(int type, string data, string raw)
 	   SSL3_DEBUG_MSG("rsa_message_was_bad\n");
 	 if(my_digest != digest)
 	   SSL3_DEBUG_MSG("digests differ\n");
-	 send_packet(Alert(ALERT_fatal, ALERT_unexpected_message));
+	 send_packet(Alert(ALERT_fatal, ALERT_unexpected_message,
+		      "SSL.session->handle_handshake: unexpected message\n",
+		      backtrace()));
 	 return -1;
        }
        handshake_messages += raw; /* Second hash includes this message,
@@ -692,11 +716,11 @@ int handle_handshake(int type, string data, string raw)
       }
       else
 	handshake_state = STATE_server_wait_for_verify;
-      
+
       break;
     case HANDSHAKE_certificate:
      {
-       if (certificate_state == CERT_requested) //FIXME: huh?
+       if (certificate_state != CERT_requested)
        {
 	 send_packet(Alert(ALERT_fatal, ALERT_unexpected_message,
 			   "SSL.session->handle_handshake: unexpected message\n",
@@ -704,7 +728,11 @@ int handle_handshake(int type, string data, string raw)
 	 return -1;
        }
        if (catch {
-	 session->client_certificate = input->get_var_string(3);
+	 int certs_len = input->get_uint(3);
+	 array certs = ({ });
+	 while(!input->is_empty())
+	   certs += ({ input->get_var_string(3) });
+	 session->client_certificate_chain = certs;
        } || !input->is_empty())
        {
 	 send_packet(Alert(ALERT_fatal, ALERT_unexpected_message,
@@ -718,7 +746,8 @@ int handle_handshake(int type, string data, string raw)
     }
     break;
   case STATE_server_wait_for_verify:
-    handshake_messages += raw;
+    // handshake_messages += raw;
+    // compute challenge first, then update handshake_messages /Sigge
     switch(type)
     {
     default:
@@ -729,10 +758,26 @@ int handle_handshake(int type, string data, string raw)
     case HANDSHAKE_certificate_verify:
       if (!rsa_message_was_bad)
       {
-	session->client_challenge =
-	  mac_md5(session->master_secret)->hash_master(handshake_messages) +
-	  mac_sha(session->master_secret)->hash_master(handshake_messages);
-	session->client_signature = data;
+	int verification_ok;
+	if( catch
+	{
+	  object(Gmp.mpz) signature = input->get_bignum();
+	  Struct handshake_messages_struct = Struct();
+	  handshake_messages_struct->put_fix_string(handshake_messages);
+	  verification_ok = session->cipher_spec->verify(
+	    context, "", handshake_messages_struct, signature);
+	} || verification_ok)
+	{
+	  send_packet(Alert(ALERT_fatal, ALERT_unexpected_message,
+			    "SSL.session->handle_handshake: verification of"
+			    " CertificateVerify message failed\n",
+			    backtrace()));
+	  return -1;
+	}
+// 	session->client_challenge =
+// 	  mac_md5(session->master_secret)->hash_master(handshake_messages) +
+// 	  mac_sha(session->master_secret)->hash_master(handshake_messages);
+// 	session->client_signature = data;
       }
       handshake_messages += raw;
       handshake_state = STATE_server_wait_for_finish;
@@ -804,15 +849,14 @@ int handle_handshake(int type, string data, string raw)
       SSL3_DEBUG_MSG("Handshake: Certificate message recieved\n");
       int certs_len = input->get_uint(3);
       array certs = ({ });
-      int i=0;
       while(!input->is_empty())
 	certs += ({ input->get_var_string(3) });
-      session->server_certificate = certs[0];
+      session->server_certificate_chain = certs;
 
       if (catch
       {
 	object public_key = Tools.X509.decode_certificate(
-	  session->server_certificate)->public_key;
+	  session->server_certificate_chain[0])->public_key;
 	if(public_key->type == "rsa")
 	{
 	  object rsa = Crypto.rsa();
@@ -940,6 +984,7 @@ int handle_handshake(int type, string data, string raw)
 
 void create(int is_server)
 {
+  auth_level = context->auth_level;
   if (is_server)
     handshake_state = STATE_server_wait_for_hello;
   else
diff --git a/lib/modules/SSL.pmod/session.pike b/lib/modules/SSL.pmod/session.pike
index 76843c4dcf..2455bc254c 100644
--- a/lib/modules/SSL.pmod/session.pike
+++ b/lib/modules/SSL.pmod/session.pike
@@ -1,4 +1,4 @@
-/* $Id: session.pike,v 1.11 2000/08/04 19:07:11 sigge Exp $
+/* $Id: session.pike,v 1.12 2000/10/22 11:35:13 sigge Exp $
  *
  */
 
@@ -16,8 +16,8 @@ string master_secret; /* 48 byte secret shared between client and server */
 constant Struct = ADT.struct;
 constant State = SSL.state;
 
-string client_certificate;
-string server_certificate;
+array(string) client_certificate_chain;
+array(string) server_certificate_chain;
 
 void set_cipher_suite(int suite)
 {
diff --git a/lib/modules/SSL.pmod/sslfile.pike b/lib/modules/SSL.pmod/sslfile.pike
index 88a8307739..a0a776c16a 100644
--- a/lib/modules/SSL.pmod/sslfile.pike
+++ b/lib/modules/SSL.pmod/sslfile.pike
@@ -1,4 +1,4 @@
-/* $Id: sslfile.pike,v 1.24 2000/08/15 21:35:43 mast Exp $
+/* $Id: sslfile.pike,v 1.25 2000/10/22 11:35:14 sigge Exp $
  *
  */
 
@@ -139,7 +139,7 @@ int write(string|array(string) s)
 private void ssl_read_callback(mixed id, string s)
 {
 #ifdef SSL3_DEBUG
-  werror(sprintf("SSL.sslfile->ssl_read_callback\n"));
+  werror(sprintf("SSL.sslfile->ssl_read_callback, connected=%d, handshake_finished=%d\n", connected, handshake_finished));
 #endif
   string|int data = got_data(s);
   if (stringp(data))
@@ -352,3 +352,12 @@ void create(object f, object c, int|void is_client)
   socket->set_nonblocking(ssl_read_callback, ssl_write_callback, ssl_close_callback);
   connection::create(!is_client);
 }
+
+void renegotiate()
+{
+  expect_change_cipher = certificate_state = 0;
+  send_packet(hello_request());
+  socket->set_write_callback(ssl_write_callback);
+  handshake_finished = 0;
+  connected = 0;
+}
diff --git a/lib/modules/SSL.pmod/sslport.pike b/lib/modules/SSL.pmod/sslport.pike
index 6c6b68400a..52a3497193 100644
--- a/lib/modules/SSL.pmod/sslport.pike
+++ b/lib/modules/SSL.pmod/sslport.pike
@@ -67,4 +67,5 @@ void create()
 #endif
   context::create();
   accept_queue::create();
+  set_id(this_object());
 }
-- 
GitLab