summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/TODO2
-rw-r--r--lib/auth_anon.c2
-rw-r--r--lib/auth_rsa.c237
-rw-r--r--lib/auth_srp.c2
-rw-r--r--lib/auth_x509.h1
-rw-r--r--lib/gnutls.h.in12
-rw-r--r--lib/gnutls_auth.h6
-rw-r--r--lib/gnutls_cert.c16
-rw-r--r--lib/gnutls_errors.c1
-rw-r--r--lib/gnutls_errors_int.h1
-rw-r--r--lib/gnutls_handshake.c41
-rw-r--r--lib/gnutls_int.h16
-rw-r--r--lib/gnutls_kx.c208
-rw-r--r--lib/gnutls_kx.h7
-rw-r--r--src/serv.c25
15 files changed, 496 insertions, 81 deletions
diff --git a/doc/TODO b/doc/TODO
index 3c7610a804..8e1ce52f34 100644
--- a/doc/TODO
+++ b/doc/TODO
@@ -1,5 +1,3 @@
-* Add Client Certificate support in X509 authentication
-* Use the KeyUsage X509v3 extension to disable algorithms
* Add DHE_* ciphersuites
* Tools for processing/generating certificates (fabio.fiorina@alcatel.it is working on that)
* Documentation (of existing functions + Manual)
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);
diff --git a/src/serv.c b/src/serv.c
index 659ed85966..ab95803352 100644
--- a/src/serv.c
+++ b/src/serv.c
@@ -105,6 +105,8 @@ GNUTLS_STATE initialize_state()
gnutls_set_mac_priority(state, GNUTLS_MAC_SHA, GNUTLS_MAC_MD5, 0);
+ gnutls_set_certificate_request( state, GNUTLS_CERT_REQUIRE);
+
return state;
}
@@ -269,6 +271,16 @@ fprintf(stderr, "\n");
return n;
}
+void check_alert( GNUTLS_STATE state, int ret) {
+ if (ret == GNUTLS_E_WARNING_ALERT_RECEIVED || ret == GNUTLS_E_FATAL_ALERT_RECEIVED) {
+ ret = gnutls_get_last_alert(state);
+ if (ret == GNUTLS_NO_RENEGOTIATION)
+ printf("* Received NO_RENEGOTIATION alert. Client Does not support renegotiation.\n");
+ else
+ printf("* Received alert '%d'.\n", ret);
+ }
+}
+
int main(int argc, char **argv)
{
int err, listen_sd, i;
@@ -351,6 +363,7 @@ int main(int argc, char **argv)
gnutls_deinit(state);
fprintf(stderr, "*** Handshake has failed (%s)\n\n",
gnutls_strerror(ret));
+ check_alert( state, ret);
continue;
}
printf("- Handshake was completed\n");
@@ -394,17 +407,7 @@ int main(int argc, char **argv)
if (i == 10)
ret = gnutls_rehandshake(sd, state);
#endif
- if (ret == GNUTLS_E_WARNING_ALERT_RECEIVED
- || ret == GNUTLS_E_FATAL_ALERT_RECEIVED) {
- ret = gnutls_get_last_alert(state);
- if (ret == GNUTLS_NO_RENEGOTIATION)
- printf
- ("* Received NO_RENEGOTIATION alert. Client Does not support renegotiation.\n");
- else
- printf("* Received alert '%d'.\n",
- ret);
- }
-
+ check_alert( state, ret);
if (http != 0) {
break; /* close the connection */
}