|  | @@ -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;
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -		/* TODO: store the information in buf1-3 somewhere */
 | 
	
		
			
				|  |  | -        (void)ret1;
 | 
	
		
			
				|  |  | -        (void)ret2;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		/* 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));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		/* 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);
 | 
	
		
			
				|  |  |  			}
 | 
	
		
			
				|  |  |  
 |