diff --git a/include/civetweb.h b/include/civetweb.h index a4a04097..c8d9628f 100644 --- a/include/civetweb.h +++ b/include/civetweb.h @@ -86,6 +86,17 @@ struct mg_request_info { const char *name; /* HTTP header name */ const char *value; /* HTTP header value */ } http_headers[64]; /* Maximum 64 headers */ + + struct client_cert *client_cert; /* Client certificate information */ +}; + + +/* Client certificate information (part of mg_request_info) */ +struct client_cert { + const char *subject; + const char *issuer; + const char *serial; + const char *finger; }; diff --git a/src/civetweb.c b/src/civetweb.c index 752fc57f..b3e58b38 100644 --- a/src/civetweb.c +++ b/src/civetweb.c @@ -1024,17 +1024,11 @@ typedef struct ssl_st SSL; typedef struct ssl_method_st SSL_METHOD; typedef struct ssl_ctx_st SSL_CTX; typedef struct x509_store_ctx_st X509_STORE_CTX; -typedef struct x509 X509; typedef struct x509_name X509_NAME; typedef struct asn1_integer ASN1_INTEGER; +typedef struct evp_md EVP_MD; +typedef struct x509 X509; -/* -typedef struct asn1_bit_string_st { - int length; - int type; - unsigned char *data; -} ASN1_BIT_STRING; -*/ #define SSL_CTRL_OPTIONS (32) #define SSL_CTRL_CLEAR_OPTIONS (77) @@ -1110,6 +1104,7 @@ struct ssl_func { (*(int (*)(SSL_CTX *, const unsigned char *, unsigned int))ssl_sw[29].ptr) #define SSL_CTX_ctrl (*(long (*)(SSL_CTX *, int, long, void *))ssl_sw[30].ptr) + #define SSL_CTX_set_cipher_list \ (*(int (*)(SSL_CTX *, const char *))ssl_sw[31].ptr) #define SSL_CTX_set_options(ctx, op) \ @@ -1119,6 +1114,10 @@ struct ssl_func { #define SSL_CTX_set_ecdh_auto(ctx, onoff) \ SSL_CTX_ctrl(ctx, SSL_CTRL_SET_ECDH_AUTO, onoff, NULL) +#define X509_get_notBefore(x) ((x)->cert_info->validity->notBefore) +#define X509_get_notAfter(x) ((x)->cert_info->validity->notAfter) + + #define CRYPTO_num_locks (*(int (*)(void))crypto_sw[0].ptr) #define CRYPTO_set_locking_callback \ (*(void (*)(void (*)(int, int, const char *, int)))crypto_sw[1].ptr) @@ -1138,7 +1137,17 @@ struct ssl_func { #define X509_NAME_oneline \ (*(char *(*)(X509_NAME *, char *, int))crypto_sw[14].ptr) #define X509_get_serialNumber (*(ASN1_INTEGER * (*)(X509 *))crypto_sw[15].ptr) -#define i2c_ASN1_INTEGER (*(int (*)(ASN1_INTEGER *, unsigned char **))crypto_sw[16].ptr) +#define i2c_ASN1_INTEGER \ + (*(int (*)(ASN1_INTEGER *, unsigned char **))crypto_sw[16].ptr) +#define EVP_get_digestbyname \ + (*(const EVP_MD *(*)(const char *))crypto_sw[17].ptr) +#define ASN1_digest \ + (*(int (*)(int (*)(), \ + const EVP_MD *, \ + char *, \ + unsigned char *, \ + unsigned int *))crypto_sw[18].ptr) +#define i2d_X509 (*(int (*)(X509 *, unsigned char **))crypto_sw[19].ptr) /* set_ssl_option() function updates this array. @@ -1199,6 +1208,9 @@ static struct ssl_func crypto_sw[] = {{"CRYPTO_num_locks", NULL}, {"X509_NAME_oneline", NULL}, {"X509_get_serialNumber", NULL}, {"i2c_ASN1_INTEGER", NULL}, + {"EVP_get_digestbyname", NULL}, + {"ASN1_digest", NULL}, + {"i2d_X509", NULL}, {NULL, NULL}}; #endif /* NO_SSL_DL */ #endif /* NO_SSL */ @@ -5299,9 +5311,9 @@ remove_double_dots_and_double_slashes(char *s) { char *p = s; - while ((s[0] == '.') && (s[1] == '.')) { - s++; - } + while ((s[0] == '.') && (s[1] == '.')) { + s++; + } while (*s != '\0') { *p++ = *s++; @@ -11317,18 +11329,18 @@ hexdump2string(void *mem, int memlen, char *buf, int buflen) if (memlen <= 0 || buflen <= 0) { return 0; } - if (buflen < (3*memlen)) { - return 0; - } + if (buflen < (3 * memlen)) { + return 0; + } for (i = 0; i < memlen; i++) { if (i > 0) { buf[3 * i - 1] = ' '; } - buf[3 * i] = hexdigit[(((uint8_t *)mem)[i] >> 4)&0xF]; + buf[3 * i] = hexdigit[(((uint8_t *)mem)[i] >> 4) & 0xF]; buf[3 * i + 1] = hexdigit[((uint8_t *)mem)[i] & 0xF]; } - buf[3*memlen-1] = 0; + buf[3 * memlen - 1] = 0; return 1; } @@ -11339,30 +11351,57 @@ ssl_get_client_cert_info(struct mg_connection *conn) { X509 *cert = SSL_get_peer_certificate(conn->ssl); if (cert) { + char str_subject[1024]; + char str_issuer[1024]; + char str_serial[1024]; + char str_finger[1024]; + unsigned char buf[256]; + int len; + + /* Handle to algorithm used for fingerprint */ + const EVP_MD *digest = EVP_get_digestbyname("sha1"); + + /* Get Subject and issuer */ X509_NAME *subj = X509_get_subject_name(cert); X509_NAME *iss = X509_get_issuer_name(cert); - char buf1[1024]; - char buf2[1024]; - char buf3[1024]; - unsigned char intbuf[256]; - char *ret1 = X509_NAME_oneline(subj, buf1, (int)sizeof(buf1)); - char *ret2 = X509_NAME_oneline(iss, buf2, (int)sizeof(buf2)); + + /* Get serial number */ ASN1_INTEGER *serial = X509_get_serialNumber(cert); - int len = i2c_ASN1_INTEGER(serial, NULL); - if (len < sizeof(intbuf)) { - unsigned char *pbuf = intbuf; - int len2 = i2c_ASN1_INTEGER(serial, &pbuf); - if (!hexdump2string(intbuf, len2, buf3, (int)sizeof(buf3))) { - *buf3 = 0; - } - } else { - *buf3 = 0; - } + /* Translate subject and issuer to a string */ + (void)X509_NAME_oneline(subj, str_subject, (int)sizeof(str_subject)); + (void)X509_NAME_oneline(iss, str_issuer, (int)sizeof(str_issuer)); - /* TODO: store the information in buf1-3 somewhere */ - (void)ret1; - (void)ret2; + /* Translate serial number to a hex string */ + len = i2c_ASN1_INTEGER(serial, NULL); + if (len < sizeof(buf)) { + unsigned char *pbuf = buf; + int len2 = i2c_ASN1_INTEGER(serial, &pbuf); + if (!hexdump2string( + buf, len2, str_serial, (int)sizeof(str_serial))) { + *str_serial = 0; + } + } else { + *str_serial = 0; + } + + /* Calculate SHA1 fingerprint and store as a hex string */ + len = 0; + ASN1_digest((int (*)())i2d_X509, digest, (char *)cert, buf, &len); + if (!hexdump2string(buf, len, str_finger, (int)sizeof(str_finger))) { + *str_finger = 0; + } + + conn->request_info.client_cert = + (struct client_cert *)mg_malloc(sizeof(struct client_cert)); + if (conn->request_info.client_cert) { + conn->request_info.client_cert->subject = mg_strdup(str_subject); + conn->request_info.client_cert->issuer = mg_strdup(str_issuer); + conn->request_info.client_cert->serial = mg_strdup(str_serial); + conn->request_info.client_cert->finger = mg_strdup(str_finger); + } else { + /* TODO: write some OOM message */ + } X509_free(cert); } @@ -12971,12 +13010,29 @@ worker_thread_run(struct worker_thread_args *thread_args) #ifndef NO_SSL /* HTTPS connection */ if (sslize(conn, conn->ctx->ssl_ctx, SSL_accept)) { + /* Get SSL client certificate information (if set) */ ssl_get_client_cert_info(conn); + + /* process HTTPS connection */ process_new_connection(conn); + + /* Free client certificate info */ + if (conn->request_info.client_cert) { + mg_free((void*)(conn->request_info.client_cert->subject)); + mg_free((void*)(conn->request_info.client_cert->issuer)); + mg_free((void*)(conn->request_info.client_cert->serial)); + mg_free((void*)(conn->request_info.client_cert->finger)); + conn->request_info.client_cert->subject = 0; + conn->request_info.client_cert->issuer = 0; + conn->request_info.client_cert->serial = 0; + conn->request_info.client_cert->finger = 0; + mg_free(conn->request_info.client_cert); + conn->request_info.client_cert = 0; + } } #endif } else { - /* HTTP connection */ + /* process HTTP connection */ process_new_connection(conn); }