diff options
author | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2001-08-07 12:16:24 +0000 |
---|---|---|
committer | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2001-08-07 12:16:24 +0000 |
commit | ac2a96f653a8f83bca32307aa818a7cf7097e95a (patch) | |
tree | 0127bd85ce8cb7c9f1d98b03dbe0e0e3c2157889 /lib | |
parent | edb7b3a0c735885e85bcde45e9f0a4d441382b46 (diff) | |
download | gnutls-ac2a96f653a8f83bca32307aa818a7cf7097e95a.tar.gz |
additions in order for gnutls server to support client authentication
Diffstat (limited to 'lib')
-rw-r--r-- | lib/auth_anon.c | 2 | ||||
-rw-r--r-- | lib/auth_rsa.c | 237 | ||||
-rw-r--r-- | lib/auth_srp.c | 2 | ||||
-rw-r--r-- | lib/auth_x509.h | 1 | ||||
-rw-r--r-- | lib/gnutls.h.in | 12 | ||||
-rw-r--r-- | lib/gnutls_auth.h | 6 | ||||
-rw-r--r-- | lib/gnutls_cert.c | 16 | ||||
-rw-r--r-- | lib/gnutls_errors.c | 1 | ||||
-rw-r--r-- | lib/gnutls_errors_int.h | 1 | ||||
-rw-r--r-- | lib/gnutls_handshake.c | 41 | ||||
-rw-r--r-- | lib/gnutls_int.h | 16 | ||||
-rw-r--r-- | lib/gnutls_kx.c | 208 | ||||
-rw-r--r-- | lib/gnutls_kx.h | 7 |
13 files changed, 482 insertions, 68 deletions
diff --git a/lib/auth_anon.c b/lib/auth_anon.c index 1b510bbef1..f0977b07f1 100644 --- a/lib/auth_anon.c +++ b/lib/auth_anon.c @@ -36,6 +36,7 @@ int proc_anon_client_kx( GNUTLS_STATE, opaque*, int); MOD_AUTH_STRUCT anon_auth_struct = { "ANON", NULL, + NULL, gen_anon_server_kx, NULL, NULL, @@ -43,6 +44,7 @@ MOD_AUTH_STRUCT anon_auth_struct = { NULL, NULL, + NULL, NULL, /* certificate */ proc_anon_server_kx, NULL, diff --git a/lib/auth_rsa.c b/lib/auth_rsa.c index 69e6e4689f..d5876d42b3 100644 --- a/lib/auth_rsa.c +++ b/lib/auth_rsa.c @@ -35,30 +35,37 @@ #include "debug.h" #include <gnutls_sig.h> -int gen_rsa_certificate(GNUTLS_STATE, opaque **); +int gen_rsa_server_certificate(GNUTLS_STATE, opaque **); +int gen_rsa_client_certificate(GNUTLS_STATE, opaque **); int gen_rsa_client_cert_vrfy(GNUTLS_STATE, opaque **); int proc_rsa_cert_req(GNUTLS_STATE, opaque *, int); int gen_rsa_client_kx(GNUTLS_STATE, opaque **); +int gen_rsa_server_cert_req(GNUTLS_STATE, opaque **); int proc_rsa_client_kx(GNUTLS_STATE, opaque *, int); -int proc_rsa_certificate(GNUTLS_STATE, opaque *, int); +int proc_rsa_client_cert_vrfy(GNUTLS_STATE, opaque *, int); + +int proc_rsa_server_certificate(GNUTLS_STATE, opaque *, int); +#define proc_rsa_client_certificate proc_rsa_server_certificate MOD_AUTH_STRUCT rsa_auth_struct = { "RSA", - gen_rsa_certificate, + gen_rsa_server_certificate, + gen_rsa_client_certificate, NULL, /* gen server kx */ NULL, /* gen server kx2 */ NULL, /* gen client kx0 */ gen_rsa_client_kx, gen_rsa_client_cert_vrfy, /* gen client cert vrfy */ - NULL, + gen_rsa_server_cert_req, /* server cert request */ - proc_rsa_certificate, + proc_rsa_server_certificate, + proc_rsa_client_certificate, NULL, /* proc server kx */ NULL, /* proc server kx2 */ NULL, /* proc client kx0 */ proc_rsa_client_kx, /* proc client kx */ - NULL, /* proc client cert vrfy */ + proc_rsa_client_cert_vrfy, /* proc client cert vrfy */ proc_rsa_cert_req /* proc server cert request */ }; @@ -214,7 +221,76 @@ static int _gnutls_get_private_rsa_params(GNUTLS_KEY key, } -int gen_rsa_certificate(GNUTLS_STATE state, opaque ** data) +int gen_rsa_client_certificate(GNUTLS_STATE state, opaque ** data) +{ + const X509PKI_CREDENTIALS cred; + int ret, i, ind, pdatasize; + opaque *pdata; + gnutls_cert *apr_cert_list; + gnutls_private_key *apr_pkey; + int apr_cert_list_length; + + cred = _gnutls_get_cred(state->gnutls_key, GNUTLS_X509PKI, NULL); + if (cred == NULL) { + gnutls_assert(); + return GNUTLS_E_INSUFICIENT_CRED; + } + if (cred->ncerts == 0) { + apr_cert_list = NULL; + apr_cert_list_length = 0; + apr_pkey = NULL; + } else { + ind = state->gnutls_internals.client_certificate_index; + + if (ind < 0) { + apr_cert_list = NULL; + apr_cert_list_length = 0; + apr_pkey = NULL; + } else { + apr_cert_list = cred->cert_list[ind]; + apr_cert_list_length = cred->cert_list_length[ind]; + apr_pkey = &cred->pkey[ind]; + } + } + + ret = 3; + for (i = 0; i < apr_cert_list_length; i++) { + ret += apr_cert_list[i].raw.size + 3; + /* hold size + * for uint24 */ + } + + (*data) = gnutls_malloc(ret); + pdata = (*data); + + if (pdata == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + WRITEuint24(ret - 3, pdata); + pdata += 3; + for (i = 0; i < apr_cert_list_length; i++) { + WRITEdatum24(pdata, apr_cert_list[i].raw); + pdata += (3 + apr_cert_list[i].raw.size); + } + pdatasize = ret; + + /* read the rsa parameters now, since later we will + * not know which certificate we used! + */ + if (i != 0) /* if we parsed at least one certificate */ + ret = _gnutls_get_private_rsa_params(state->gnutls_key, apr_pkey); + else + ret = 0; + + if (ret < 0) { + gnutls_assert(); + return ret; + } + return pdatasize; +} + +int gen_rsa_server_certificate(GNUTLS_STATE state, opaque ** data) { const X509PKI_CREDENTIALS cred; int ret, i, ind, pdatasize; @@ -233,10 +309,7 @@ int gen_rsa_certificate(GNUTLS_STATE state, opaque ** data) apr_cert_list_length = 0; apr_pkey = NULL; } else { - if (state->security_parameters.entity == GNUTLS_CLIENT) - ind = state->gnutls_internals.client_certificate_index; - else /* server */ - ind = _gnutls_find_cert_list_index(cred->cert_list, cred->ncerts, state->security_parameters.extensions.dnsname); + ind = _gnutls_find_cert_list_index(cred->cert_list, cred->ncerts, state->security_parameters.extensions.dnsname); if (ind < 0) { apr_cert_list = NULL; @@ -356,7 +429,7 @@ return ret; } -int proc_rsa_certificate(GNUTLS_STATE state, opaque * data, int data_size) +int proc_rsa_server_certificate(GNUTLS_STATE state, opaque * data, int data_size) { int size, len, ret; opaque *p = data; @@ -369,7 +442,6 @@ int proc_rsa_certificate(GNUTLS_STATE state, opaque * data, int data_size) gnutls_datum tmp; CertificateStatus verify; -#warning "NO SERVER SIDE YET" cred = _gnutls_get_cred(state->gnutls_key, GNUTLS_X509PKI, NULL); if (cred == NULL) { gnutls_assert(); @@ -389,7 +461,7 @@ int proc_rsa_certificate(GNUTLS_STATE state, opaque * data, int data_size) if (size == 0) { gnutls_assert(); - return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + return GNUTLS_E_NO_CERTIFICATE_FOUND; } info = state->gnutls_key->auth_info; i = dsize; @@ -471,6 +543,7 @@ int proc_rsa_certificate(GNUTLS_STATE state, opaque * data, int data_size) return 0; } + /* return RSA(random) using the peers public key */ int gen_rsa_client_kx(GNUTLS_STATE state, opaque ** data) @@ -524,16 +597,52 @@ int gen_rsa_client_kx(GNUTLS_STATE state, opaque ** data) } +int _gnutls_find_dn( gnutls_datum* odn, gnutls_cert* cert) { +node_asn* dn; +int len, result; +int start, end; + + if (asn1_create_structure(_gnutls_get_pkix(), "PKIX1Implicit88.Certificate", &dn, "dn") != ASN_OK) { + gnutls_assert(); + return GNUTLS_E_ASN1_ERROR; + } + + result = asn1_get_der( dn, cert->raw.data, cert->raw.size); + if (result != ASN_OK) { + /* couldn't decode DER */ + gnutls_assert(); + asn1_delete_structure( dn); + return GNUTLS_E_ASN1_PARSING_ERROR; + } + + result = asn1_get_start_end_der( dn, cert->raw.data, cert->raw.size, + "dn.tbsCertificate.issuer", &start, &end); + + if (result != ASN_OK) { + /* couldn't decode DER */ + gnutls_assert(); + asn1_delete_structure( dn); + return GNUTLS_E_ASN1_PARSING_ERROR; + } + asn1_delete_structure( dn); + + len = end - start + 1; + + odn->size = len; + odn->data = &cert->raw.data[start]; + + return 0; +} + /* Finds the appropriate certificate depending on the cA Distinguished name * advertized by the server */ static int _gnutls_find_acceptable_client_cert( const X509PKI_CREDENTIALS cred, const opaque* data, int data_size, int *ind) { -node_asn *dn; int result, size; int indx = -1; -int start, end, len, i, j; - +int i, j; +gnutls_datum odn; do { @@ -544,37 +653,16 @@ int start, end, len, i, j; for(i=0;i<cred->ncerts;i++) { for (j=0;j<cred->cert_list_length[i];j++) { - if (asn1_create_structure(_gnutls_get_pkix(), "PKIX1Implicit88.Certificate", &dn, "dn") != ASN_OK) { + if ( (result=_gnutls_find_dn( &odn, &cred->cert_list[i][j])) < 0) { gnutls_assert(); - return GNUTLS_E_ASN1_ERROR; - } - - result = asn1_get_der( dn, cred->cert_list[i][j].raw.data, cred->cert_list[i][j].raw.size); - if (result != ASN_OK) { - /* couldn't decode DER */ - gnutls_assert(); - asn1_delete_structure( dn); - return GNUTLS_E_ASN1_PARSING_ERROR; + return result; } - result = asn1_get_start_end_der( dn, cred->cert_list[i][j].raw.data, cred->cert_list[i][j].raw.size, - "dn.tbsCertificate.issuer", &start, &end); - - if (result != ASN_OK) { - /* couldn't decode DER */ - gnutls_assert(); - asn1_delete_structure( dn); - return GNUTLS_E_ASN1_PARSING_ERROR; - } - asn1_delete_structure( dn); - - len = end - start + 1; - - if ( len != size) continue; + if ( odn.size != size) continue; if (memcmp( - &cred->cert_list[i][j].raw.data[start], - data, len) == 0 ) { + odn.data, + data, size) == 0 ) { indx = i; break; } @@ -728,3 +816,66 @@ int gen_rsa_client_cert_vrfy(GNUTLS_STATE state, opaque ** data) return size+2; } + +int proc_rsa_client_cert_vrfy(GNUTLS_STATE state, opaque * data, int data_size) +{ + #warning "CHECK THE CERT VERIFY MESSAGE" + + return 0; +} + +#define CERTTYPE_SIZE 2 +int gen_rsa_server_cert_req(GNUTLS_STATE state, opaque ** data) +{ + const X509PKI_CREDENTIALS cred; + int ret, i, size; + opaque *pdata; + gnutls_datum dn; + + cred = _gnutls_get_cred(state->gnutls_key, GNUTLS_X509PKI, NULL); + if (cred == NULL) { + gnutls_assert(); + return GNUTLS_E_INSUFICIENT_CRED; + } + + size = CERTTYPE_SIZE+2; /* 2 for CertType + 2 for size */ + + for (i = 0; i < cred->ncas; i++) { + size += cred->ca_list[i].raw.size + 2; + /* hold size + * for uint16 */ + } + + (*data) = gnutls_malloc(size); + pdata = (*data); + + if (pdata == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + pdata[0] = CERTTYPE_SIZE - 1; + pdata[1] = RSA_SIGN; /* only this for now */ + pdata += CERTTYPE_SIZE; + size = CERTTYPE_SIZE; + + /* leave space to write the actual size */ + pdata += 2; + size += 2; + + for (i = 0; i < cred->ncas; i++) { + if ( (ret=_gnutls_find_dn( &dn, &cred->ca_list[i])) < 0) { + gnutls_free( (*data)); + gnutls_assert(); + return ret; + } + WRITEdatum16(pdata, dn); + pdata += (2 + dn.size); + size += (2 + dn.size); + } + + /* write the recalculated size */ + WRITEuint16( size-CERTTYPE_SIZE-2, &(*data)[CERTTYPE_SIZE]); + + return size; +} diff --git a/lib/auth_srp.c b/lib/auth_srp.c index 8497c4d9ab..08fafd9dae 100644 --- a/lib/auth_srp.c +++ b/lib/auth_srp.c @@ -38,12 +38,14 @@ MOD_AUTH_STRUCT srp_auth_struct = { "SRP", NULL, NULL, + NULL, gen_srp_server_kx2, gen_srp_client_kx0, NULL, NULL, NULL, + NULL, NULL, /* certificate */ NULL, proc_srp_server_kx2, diff --git a/lib/auth_x509.h b/lib/auth_x509.h index cc3bdc6d14..d584000c85 100644 --- a/lib/auth_x509.h +++ b/lib/auth_x509.h @@ -44,6 +44,7 @@ typedef struct X509PKI_CLIENT_AUTH_INFO_INT { } *X509PKI_CLIENT_AUTH_INFO; typedef struct X509PKI_CLIENT_AUTH_INFO_INT X509PKI_CLIENT_AUTH_INFO_INT; +typedef X509PKI_CLIENT_AUTH_INFO X509PKI_SERVER_AUTH_INFO; void _gnutls_copy_x509_client_auth_info( X509PKI_CLIENT_AUTH_INFO info, gnutls_cert* cert, CertificateStatus verify); diff --git a/lib/gnutls.h.in b/lib/gnutls.h.in index 55891e2f4f..4ea10a2bd9 100644 --- a/lib/gnutls.h.in +++ b/lib/gnutls.h.in @@ -28,17 +28,21 @@ typedef enum CredType { GNUTLS_X509PKI=1, GNUTLS_ANON, GNUTLS_SRP } CredType; typedef enum MACAlgorithm { GNUTLS_NULL_MAC=1, GNUTLS_MAC_MD5, GNUTLS_MAC_SHA } MACAlgorithm; typedef enum CompressionMethod { GNUTLS_NULL_COMPRESSION=1, GNUTLS_ZLIB } CompressionMethod; typedef enum ConnectionEnd { GNUTLS_SERVER=1, GNUTLS_CLIENT } ConnectionEnd; +typedef enum AlertLevel { GNUTLS_WARNING=1, GNUTLS_FATAL } AlertLevel; typedef enum AlertDescription { GNUTLS_CLOSE_NOTIFY, GNUTLS_UNEXPECTED_MESSAGE=10, GNUTLS_BAD_RECORD_MAC=20, GNUTLS_DECRYPTION_FAILED, GNUTLS_RECORD_OVERFLOW, GNUTLS_DECOMPRESSION_FAILURE=30, - GNUTLS_HANDSHAKE_FAILURE=40, GNUTLS_BAD_CERTIFICATE=42, GNUTLS_UNSUPPORTED_CERTIFICATE, + GNUTLS_HANDSHAKE_FAILURE=40, GNUTLS_NETSCAPE_NO_CLIENT_CERTIFICATE=41, + GNUTLS_BAD_CERTIFICATE=42, GNUTLS_UNSUPPORTED_CERTIFICATE, GNUTLS_CERTIFICATE_REVOKED, GNUTLS_CERTIFICATE_EXPIRED, GNUTLS_CERTIFICATE_UNKNOWN, GNUTLS_ILLEGAL_PARAMETER, GNUTLS_UNKNOWN_CA, GNUTLS_ACCESS_DENIED, GNUTLS_DECODE_ERROR=50, GNUTLS_DECRYPT_ERROR, GNUTLS_EXPORT_RESTRICTION=60, GNUTLS_PROTOCOL_VERSION=70, GNUTLS_INSUFFICIENT_SECURITY, GNUTLS_INTERNAL_ERROR=80, GNUTLS_USER_CANCELED=90, GNUTLS_NO_RENEGOTIATION=100 } AlertDescription; -typedef enum AlertLevel { GNUTLS_WARNING=1, GNUTLS_FATAL } AlertLevel; + + typedef enum CertificateStatus { GNUTLS_CERT_TRUSTED=1, GNUTLS_CERT_NOT_TRUSTED, GNUTLS_CERT_EXPIRED, GNUTLS_CERT_INVALID } CertificateStatus; +typedef enum CertificateRequest { GNUTLS_CERT_REQUEST=1, GNUTLS_CERT_REQUIRE } CertificateRequest; typedef enum GNUTLS_Version { GNUTLS_SSL3=1, GNUTLS_TLS1 } GNUTLS_Version; @@ -105,7 +109,7 @@ int gnutls_set_mac_priority( GNUTLS_STATE state, LIST); int gnutls_set_compression_priority( GNUTLS_STATE state, LIST); int gnutls_set_kx_priority( GNUTLS_STATE state, LIST); int gnutls_set_protocol_priority( GNUTLS_STATE state, LIST); - +int gnutls_set_certificate_request( GNUTLS_STATE, CertificateRequest); /* set our version - 0 for TLS 1.0 and 1 for SSL3 */ GNUTLS_Version gnutls_get_current_version(GNUTLS_STATE state); @@ -211,6 +215,8 @@ void gnutls_global_set_recv_func( RECV_FUNC recv_func); /* Auth_Info structures */ typedef struct X509PKI_CLIENT_AUTH_INFO_INT *X509PKI_CLIENT_AUTH_INFO; +typedef X509PKI_CLIENT_AUTH_INFO X509PKI_SERVER_AUTH_INFO; + typedef struct SRP_CLIENT_AUTH_INFO_INT *SRP_CLIENT_AUTH_INFO; typedef struct SRP_SERVER_AUTH_INFO_INT *SRP_SERVER_AUTH_INFO; typedef struct ANON_CLIENT_AUTH_INFO_INT *ANON_CLIENT_AUTH_INFO; diff --git a/lib/gnutls_auth.h b/lib/gnutls_auth.h index 8017c50eb8..2a9442ce53 100644 --- a/lib/gnutls_auth.h +++ b/lib/gnutls_auth.h @@ -3,7 +3,8 @@ typedef struct MOD_AUTH_STRUCT_INT { char* name; /* null terminated */ - int (*gnutls_generate_certificate)( GNUTLS_STATE, opaque**); + int (*gnutls_generate_server_certificate)( GNUTLS_STATE, opaque**); + int (*gnutls_generate_client_certificate)( GNUTLS_STATE, opaque**); int (*gnutls_generate_server_kx)( GNUTLS_STATE, opaque**); int (*gnutls_generate_server_kx2)( GNUTLS_STATE, opaque**); /* used in SRP */ int (*gnutls_generate_client_kx0)( GNUTLS_STATE, opaque**); @@ -11,7 +12,8 @@ typedef struct MOD_AUTH_STRUCT_INT { int (*gnutls_generate_client_cert_vrfy) ( GNUTLS_STATE, opaque**); int (*gnutls_generate_server_certificate_request) ( GNUTLS_STATE, opaque**); - int (*gnutls_process_certificate)( GNUTLS_STATE, opaque*, int); + int (*gnutls_process_server_certificate)( GNUTLS_STATE, opaque*, int); + int (*gnutls_process_client_certificate)( GNUTLS_STATE, opaque*, int); int (*gnutls_process_server_kx)( GNUTLS_STATE, opaque*, int); int (*gnutls_process_server_kx2)( GNUTLS_STATE, opaque*, int); int (*gnutls_process_client_kx0)( GNUTLS_STATE, opaque*, int); diff --git a/lib/gnutls_cert.c b/lib/gnutls_cert.c index 6f3b74f58c..f87139eac3 100644 --- a/lib/gnutls_cert.c +++ b/lib/gnutls_cert.c @@ -900,3 +900,19 @@ int _gnutls_find_cert_list_index(gnutls_cert ** cert_list, return index; } +/** + * gnutls_set_certificate_request - Used to set whether to request a client certificate + * @state: is an &GNUTLS_STATE structure. + * @req: is one of GNUTLS_CERT_REQUEST, GNUTLS_CERT_REQUIRE + * + * This function specifies if we (in case of a server) are going + * to send a certificate request message to the client. If 'req' + * is GNUTLS_CERT_REQUIRE then the server will return an error if + * the peer does not provide a certificate. If you do not + * call this function then the client will not be asked to + * send a certificate. + **/ +int gnutls_set_certificate_request( GNUTLS_STATE state, CertificateRequest req) { + state->gnutls_internals.send_cert_req = req; + return 0; +} diff --git a/lib/gnutls_errors.c b/lib/gnutls_errors.c index 6e2289bfa9..3e8dafa705 100644 --- a/lib/gnutls_errors.c +++ b/lib/gnutls_errors.c @@ -74,6 +74,7 @@ static gnutls_error_entry error_algorithms[] = { GNUTLS_ERROR_ENTRY( GNUTLS_E_HASH_FAILED, 1), GNUTLS_ERROR_ENTRY( GNUTLS_E_PARSING_ERROR, 1), GNUTLS_ERROR_ENTRY( GNUTLS_E_AUTH_FAILED, 1), + GNUTLS_ERROR_ENTRY( GNUTLS_E_NO_CERTIFICATE_FOUND, 1), GNUTLS_ERROR_ENTRY( GNUTLS_E_RECORD_LIMIT_REACHED, 1), GNUTLS_ERROR_ENTRY( GNUTLS_E_ASN1_PARSING_ERROR, 1), GNUTLS_ERROR_ENTRY( GNUTLS_E_ASN1_ERROR, 1), diff --git a/lib/gnutls_errors_int.h b/lib/gnutls_errors_int.h index 066ea2c839..5eaeaf2ad3 100644 --- a/lib/gnutls_errors_int.h +++ b/lib/gnutls_errors_int.h @@ -50,5 +50,6 @@ #define GNUTLS_E_X509_UNSUPPORTED_CRITICAL_EXTENSION -47 #define GNUTLS_E_X509_KEY_USAGE_VIOLATION -48 #define GNUTLS_E_PKCS1_WRONG_PAD -48 +#define GNUTLS_E_NO_CERTIFICATE_FOUND -49 #define GNUTLS_E_UNIMPLEMENTED_FEATURE -250 diff --git a/lib/gnutls_handshake.c b/lib/gnutls_handshake.c index 6cbf35a114..2f425202f7 100644 --- a/lib/gnutls_handshake.c +++ b/lib/gnutls_handshake.c @@ -712,6 +712,8 @@ int _gnutls_recv_handshake(SOCKET cd, GNUTLS_STATE state, uint8 ** data, if (ret < 0) { if (ret == GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET && optional==OPTIONAL_PACKET) { gnutls_assert(); + *datalen = 0; + *data = NULL; return 0; /* ok just ignore the packet */ } gnutls_assert(); @@ -722,7 +724,8 @@ int _gnutls_recv_handshake(SOCKET cd, GNUTLS_STATE state, uint8 ** data, if (length32 > 0) dataptr = gnutls_malloc(length32); - + else +fprintf(stderr, "recv_type: %d\nLenght: %d\n", recv_type, length32); if (dataptr == NULL) { gnutls_assert(); @@ -1292,7 +1295,7 @@ int gnutls_handshake_begin(SOCKET cd, GNUTLS_STATE state) { /* RECV CERTIFICATE */ if (state->gnutls_internals.resumed == RESUME_FALSE) /* if we are not resuming */ - ret = _gnutls_recv_certificate(cd, state); + ret = _gnutls_recv_server_certificate(cd, state); if (ret < 0) { gnutls_assert(); ERR("recv server certificate", ret); @@ -1323,7 +1326,7 @@ int gnutls_handshake_begin(SOCKET cd, GNUTLS_STATE state) { /* SEND CERTIFICATE + KEYEXCHANGE + CERTIFICATE_REQUEST */ if (state->gnutls_internals.resumed == RESUME_FALSE) - ret = _gnutls_send_certificate(cd, state); + ret = _gnutls_send_server_certificate(cd, state); if (ret < 0) { ERR("send server certificate", ret); gnutls_assert(); @@ -1339,7 +1342,16 @@ int gnutls_handshake_begin(SOCKET cd, GNUTLS_STATE state) { gnutls_clearHashDataBuffer(state); return ret; } - /* FIXME: Send certificate request */ + + /* Send certificate request - if requested to */ + if (state->gnutls_internals.resumed == RESUME_FALSE) + ret = _gnutls_send_server_certificate_request(cd, state); + if (ret < 0) { + ERR("send server cert request", ret); + gnutls_assert(); + gnutls_clearHashDataBuffer(state); + return ret; + } /* Added for SRP which uses a different handshake */ /* receive the client key exchange message */ @@ -1351,6 +1363,7 @@ int gnutls_handshake_begin(SOCKET cd, GNUTLS_STATE state) { gnutls_clearHashDataBuffer(state); return ret; } + /* send server key exchange (B) */ if (state->gnutls_internals.resumed == RESUME_FALSE) ret = _gnutls_send_server_kx_message2(cd, state); @@ -1554,6 +1567,16 @@ int gnutls_handshake_finish(SOCKET cd, GNUTLS_STATE state) { } /* RECV CERTIFICATE + KEYEXCHANGE + CERTIFICATE_VERIFY */ + /* receive the client certificate message */ + if (state->gnutls_internals.resumed == RESUME_FALSE) /* if we are not resuming */ + ret = _gnutls_recv_client_certificate(cd, state); + if (ret < 0) { + gnutls_assert(); + ERR("recv client certificate", ret); + gnutls_clearHashDataBuffer(state); + return ret; + } + /* receive the client key exchange message */ if (state->gnutls_internals.resumed == RESUME_FALSE) /* if we are not resuming */ ret = _gnutls_recv_client_kx_message(cd, state); @@ -1563,6 +1586,16 @@ int gnutls_handshake_finish(SOCKET cd, GNUTLS_STATE state) { gnutls_clearHashDataBuffer(state); return ret; } + + /* receive the client certificate verify message */ + if (state->gnutls_internals.resumed == RESUME_FALSE) /* if we are not resuming */ + ret = _gnutls_recv_client_certificate_verify_message(cd, state); + if (ret < 0) { + gnutls_assert(); + ERR("recv client certificate verify", ret); + gnutls_clearHashDataBuffer(state); + return ret; + } } /* send and recv the change cipher spec and finished messages */ diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h index 7fc8242ff5..315078cec2 100644 --- a/lib/gnutls_int.h +++ b/lib/gnutls_int.h @@ -81,7 +81,8 @@ typedef enum ChangeCipherSpecType { GNUTLS_TYPE_CHANGE_CIPHER_SPEC=1 } ChangeCip typedef enum AlertLevel { GNUTLS_WARNING=1, GNUTLS_FATAL } AlertLevel; typedef enum AlertDescription { GNUTLS_CLOSE_NOTIFY, GNUTLS_UNEXPECTED_MESSAGE=10, GNUTLS_BAD_RECORD_MAC=20, GNUTLS_DECRYPTION_FAILED, GNUTLS_RECORD_OVERFLOW, GNUTLS_DECOMPRESSION_FAILURE=30, - GNUTLS_HANDSHAKE_FAILURE=40, GNUTLS_BAD_CERTIFICATE=42, GNUTLS_UNSUPPORTED_CERTIFICATE, + GNUTLS_HANDSHAKE_FAILURE=40, GNUTLS_NETSCAPE_NO_CLIENT_CERTIFICATE=41, + GNUTLS_BAD_CERTIFICATE=42, GNUTLS_UNSUPPORTED_CERTIFICATE, GNUTLS_CERTIFICATE_REVOKED, GNUTLS_CERTIFICATE_EXPIRED, GNUTLS_CERTIFICATE_UNKNOWN, GNUTLS_ILLEGAL_PARAMETER, GNUTLS_UNKNOWN_CA, GNUTLS_ACCESS_DENIED, GNUTLS_DECODE_ERROR=50, GNUTLS_DECRYPT_ERROR, GNUTLS_EXPORT_RESTRICTION=60, GNUTLS_PROTOCOL_VERSION=70, @@ -89,6 +90,7 @@ typedef enum AlertDescription { GNUTLS_CLOSE_NOTIFY, GNUTLS_UNEXPECTED_MESSAGE=1 GNUTLS_NO_RENEGOTIATION=100 } AlertDescription; typedef enum CertificateStatus { GNUTLS_CERT_TRUSTED=1, GNUTLS_CERT_NOT_TRUSTED, GNUTLS_CERT_EXPIRED, GNUTLS_CERT_INVALID } CertificateStatus; +typedef enum CertificateRequest { GNUTLS_CERT_REQUEST=1, GNUTLS_CERT_REQUIRE } CertificateRequest; typedef enum HandshakeType { GNUTLS_HELLO_REQUEST, GNUTLS_CLIENT_HELLO, GNUTLS_SERVER_HELLO, GNUTLS_CERTIFICATE=11, GNUTLS_SERVER_KEY_EXCHANGE, @@ -178,6 +180,9 @@ struct GNUTLS_KEY_INT { * to provide client authentication. * 1 if client auth was requested * by the peer, 0 otherwise + *** In case of a server this + * holds 1 if we should wait + * for a client certificate verify */ }; typedef struct GNUTLS_KEY_INT* GNUTLS_KEY; @@ -315,10 +320,11 @@ typedef struct { /* sockets internals */ int lowat; + /* gdbm */ char* db_name; int expire_time; - struct MOD_AUTH_STRUCT_INT* auth_struct; /* used in handshake packets and KX algorithms */ + struct MOD_AUTH_STRUCT_INT* auth_struct; /* used in handshake packets and KX algorithms */ int v2_hello; /* set 0 normally - 1 if v2 hello was received - server side only */ #ifdef HAVE_LIBGDBM GDBM_FILE db_reader; @@ -336,6 +342,12 @@ typedef struct { */ uint8 adv_version_major; uint8 adv_version_minor; + + /* if this is non zero a certificate request message + * will be sent to the client. - only if the ciphersuite + * supports it. + */ + int send_cert_req; } GNUTLS_INTERNALS; struct GNUTLS_STATE_INT { diff --git a/lib/gnutls_kx.c b/lib/gnutls_kx.c index 4e55b35f87..49786440dd 100644 --- a/lib/gnutls_kx.c +++ b/lib/gnutls_kx.c @@ -26,6 +26,10 @@ #include "gnutls_algorithms.h" #include "debug.h" #include "gnutls_gcry.h" +#include <gnutls_record.h> + +/* This file contains important thing for the TLS handshake procedure. + */ #define MASTER_SECRET "master secret" static int generate_normal_master( GNUTLS_STATE state); @@ -116,6 +120,43 @@ int _gnutls_send_server_kx_message(SOCKET cd, GNUTLS_STATE state) return data_size; } +/* This function sends a certificate request message to the + * client. + */ +int _gnutls_send_server_certificate_request(SOCKET cd, GNUTLS_STATE state) +{ + uint8 *data = NULL; + int data_size = 0; + int ret = 0; + + if (state->gnutls_internals.auth_struct->gnutls_generate_server_certificate_request==NULL) + return 0; + + if (state->gnutls_internals.send_cert_req <= 0) + return 0; + +#ifdef HANDSHAKE_DEBUG + _gnutls_log( "Sending server Certificate request message\n"); +#endif + + + data_size = state->gnutls_internals.auth_struct->gnutls_generate_server_certificate_request( state, &data); + + if (data_size < 0) { + gnutls_assert(); + return data_size; + } + + ret = _gnutls_send_handshake(cd, state, data, data_size, GNUTLS_CERTIFICATE_REQUEST); + gnutls_free(data); + + if (ret < 0) { + gnutls_assert(); + return ret; + } + return data_size; +} + /* Currently only used in SRP */ int _gnutls_send_server_kx_message2(SOCKET cd, GNUTLS_STATE state) { @@ -298,9 +339,11 @@ int _gnutls_recv_server_certificate_request(SOCKET cd, GNUTLS_STATE state) _gnutls_recv_handshake(cd, state, &data, &datasize, GNUTLS_CERTIFICATE_REQUEST, OPTIONAL_PACKET); - if (ret <= 0) + if (ret < 0) return ret; + if (ret==0 && datasize == 0) + return 0; /* ignored */ ret = state->gnutls_internals.auth_struct->gnutls_process_server_certificate_request( state, data, datasize); gnutls_free(data); @@ -404,21 +447,59 @@ int _gnutls_recv_client_kx_message0(SOCKET cd, GNUTLS_STATE state) /* This is called when we want send our certificate */ -int _gnutls_send_certificate(SOCKET cd, GNUTLS_STATE state) +int _gnutls_send_client_certificate(SOCKET cd, GNUTLS_STATE state) +{ + uint8 *data = NULL; + int data_size = 0; + int ret = 0; + + + if (state->gnutls_key->certificate_requested == 0) + return 0; + + if (state->gnutls_internals.auth_struct->gnutls_generate_client_certificate==NULL) + return 0; + +#ifdef HANDSHAKE_DEBUG + _gnutls_log( "Sending client certificate message\n"); +#endif + + data_size = state->gnutls_internals.auth_struct->gnutls_generate_client_certificate( state, &data); + + if (data_size < 0) { + gnutls_assert(); + return data_size; + } + + ret = _gnutls_send_handshake(cd, state, data, data_size, GNUTLS_CERTIFICATE); + gnutls_free(data); + + if (ret<0) { + gnutls_assert(); + return ret; + } + + return data_size; +} + + +/* This is called when we want send our certificate + */ +int _gnutls_send_server_certificate(SOCKET cd, GNUTLS_STATE state) { uint8 *data = NULL; int data_size = 0; int ret = 0; - if (state->gnutls_internals.auth_struct->gnutls_generate_certificate==NULL) + if (state->gnutls_internals.auth_struct->gnutls_generate_server_certificate==NULL) return 0; #ifdef HANDSHAKE_DEBUG _gnutls_log( "Sending certificate message\n"); #endif - data_size = state->gnutls_internals.auth_struct->gnutls_generate_certificate( state, &data); + data_size = state->gnutls_internals.auth_struct->gnutls_generate_server_certificate( state, &data); if (data_size < 0) { gnutls_assert(); @@ -436,13 +517,83 @@ int _gnutls_send_certificate(SOCKET cd, GNUTLS_STATE state) return data_size; } -int _gnutls_recv_certificate(SOCKET cd, GNUTLS_STATE state) + +int _gnutls_recv_client_certificate(SOCKET cd, GNUTLS_STATE state) { int datasize; opaque * data; int ret = 0; + int optional; + + if (state->gnutls_internals.auth_struct->gnutls_process_client_certificate!=NULL) { + + /* if we have not requested a certificate then just return + */ + if ( state->gnutls_internals.send_cert_req == 0) { + return 0; + } - if (state->gnutls_internals.auth_struct->gnutls_process_certificate!=NULL) { + if ( state->gnutls_internals.send_cert_req == GNUTLS_CERT_REQUIRE) + optional = MANDATORY_PACKET; + else + optional = OPTIONAL_PACKET; + + ret = + _gnutls_recv_handshake(cd, state, &data, + &datasize, + GNUTLS_CERTIFICATE, optional); + if (ret < 0) { + if (optional == OPTIONAL_PACKET && + ret==GNUTLS_E_WARNING_ALERT_RECEIVED && + gnutls_get_last_alert(state)==GNUTLS_NETSCAPE_NO_CLIENT_CERTIFICATE) { + + /* netscape does not send an empty certificate, + * but this alert. So we just ignore it. + */ + gnutls_assert(); + return 0; + } + /* certificate was required */ + gnutls_send_alert( cd, state, GNUTLS_FATAL, GNUTLS_BAD_CERTIFICATE); + gnutls_assert(); + return ret; + } + + if (ret == 0 && datasize == 0 && optional == OPTIONAL_PACKET) { + /* well I'm not sure we should accept this + * behaviour. + */ + gnutls_assert(); + return 0; + } + + + ret = state->gnutls_internals.auth_struct->gnutls_process_client_certificate( state, data, datasize); + gnutls_free(data); + if (ret < 0 && ret != GNUTLS_E_NO_CERTIFICATE_FOUND) { + gnutls_assert(); + return ret; + } + + /* ok we should expect a certificate verify message now + */ + if (ret==GNUTLS_E_NO_CERTIFICATE_FOUND && optional == OPTIONAL_PACKET) + ret = 0; + else + state->gnutls_key->certificate_requested = 1; + + } + + return ret; +} + +int _gnutls_recv_server_certificate(SOCKET cd, GNUTLS_STATE state) +{ + int datasize; + opaque * data; + int ret = 0; + + if (state->gnutls_internals.auth_struct->gnutls_process_server_certificate!=NULL) { ret = _gnutls_recv_handshake(cd, state, &data, @@ -453,7 +604,7 @@ int _gnutls_recv_certificate(SOCKET cd, GNUTLS_STATE state) return ret; } - ret = state->gnutls_internals.auth_struct->gnutls_process_certificate( state, data, datasize); + ret = state->gnutls_internals.auth_struct->gnutls_process_server_certificate( state, data, datasize); gnutls_free(data); if (ret < 0) { gnutls_assert(); @@ -464,15 +615,48 @@ int _gnutls_recv_certificate(SOCKET cd, GNUTLS_STATE state) return ret; } -int _gnutls_send_client_certificate(SOCKET cd, GNUTLS_STATE state) + +/* Recv the client certificate verify. This packet may not + * arrive if the peer did not send us a certificate. + */ +int _gnutls_recv_client_certificate_verify_message(SOCKET cd, GNUTLS_STATE state) { + uint8 *data; + int datasize; + int ret = 0; - if (state->gnutls_key->certificate_requested == 0) - return 0; + + if (state->gnutls_internals.auth_struct->gnutls_process_client_cert_vrfy != NULL) { #ifdef HANDSHAKE_DEBUG - _gnutls_log( "Sending Client Certificate\n"); + _gnutls_log( "Receiving client certificate verify message\n"); #endif - return _gnutls_send_certificate(cd, state); + if ( state->gnutls_internals.send_cert_req == 0 || + state->gnutls_key->certificate_requested == 0) { + return 0; + } + + ret = + _gnutls_recv_handshake(cd, state, &data, + &datasize, + GNUTLS_CLIENT_KEY_EXCHANGE, OPTIONAL_PACKET); + if (ret < 0) + return ret; + + if (ret==0 && datasize == 0 && state->gnutls_internals.send_cert_req == GNUTLS_CERT_REQUIRE) { + /* certificate was required */ + gnutls_send_alert( cd, state, GNUTLS_FATAL, GNUTLS_BAD_CERTIFICATE); + gnutls_assert(); + return GNUTLS_E_NO_CERTIFICATE_FOUND; + } + + ret = state->gnutls_internals.auth_struct->gnutls_process_client_cert_vrfy( state, data, datasize); + gnutls_free(data); + if (ret < 0) + return ret; + + } + + return ret; } diff --git a/lib/gnutls_kx.h b/lib/gnutls_kx.h index 010d528d5a..a84ff0b4be 100644 --- a/lib/gnutls_kx.h +++ b/lib/gnutls_kx.h @@ -27,9 +27,12 @@ int _gnutls_recv_server_kx_message2(int cd, GNUTLS_STATE state); int _gnutls_recv_client_kx_message(int cd, GNUTLS_STATE state); int _gnutls_recv_client_kx_message0(int cd, GNUTLS_STATE state); int _gnutls_send_client_certificate_verify(int cd, GNUTLS_STATE state); -int _gnutls_send_certificate(int cd, GNUTLS_STATE state); +int _gnutls_send_server_certificate(int cd, GNUTLS_STATE state); int _gnutls_generate_master( GNUTLS_STATE state); -int _gnutls_recv_certificate(SOCKET cd, GNUTLS_STATE state); +int _gnutls_recv_client_certificate(SOCKET cd, GNUTLS_STATE state); +int _gnutls_recv_server_certificate(SOCKET cd, GNUTLS_STATE state); int _gnutls_send_client_certificate(SOCKET cd, GNUTLS_STATE state); int _gnutls_recv_server_certificate_request(SOCKET cd, GNUTLS_STATE state); +int _gnutls_send_server_certificate_request(SOCKET cd, GNUTLS_STATE state); +int _gnutls_recv_client_certificate_verify_message(SOCKET cd, GNUTLS_STATE state); |