diff options
-rw-r--r-- | ssl.c | 125 | ||||
-rw-r--r-- | ssl_verify_backend.h | 16 | ||||
-rw-r--r-- | ssl_verify_openssl.c | 127 |
3 files changed, 144 insertions, 124 deletions
@@ -296,114 +296,6 @@ ssl_put_auth_challenge (const char *cr_str) #endif -/* - * Extract a field from an X509 subject name. - * - * Example: - * - * /C=US/ST=CO/L=Denver/O=ORG/CN=First-CN/CN=Test-CA/Email=jim@yonan.net - * - * The common name is 'Test-CA' - * - * Return true on success, false on error (insufficient buffer size in 'out' - * to contain result is grounds for error). - */ -static bool -extract_x509_field_ssl (X509_NAME *x509, const char *field_name, char *out, int size) -{ - int lastpos = -1; - int tmp = -1; - X509_NAME_ENTRY *x509ne = 0; - ASN1_STRING *asn1 = 0; - unsigned char *buf = (unsigned char *)1; /* bug in OpenSSL 0.9.6b ASN1_STRING_to_UTF8 requires this workaround */ - int nid = OBJ_txt2nid((char *)field_name); - - ASSERT (size > 0); - *out = '\0'; - do { - lastpos = tmp; - tmp = X509_NAME_get_index_by_NID(x509, nid, lastpos); - } while (tmp > -1); - - /* Nothing found */ - if (lastpos == -1) - return false; - - x509ne = X509_NAME_get_entry(x509, lastpos); - if (!x509ne) - return false; - - asn1 = X509_NAME_ENTRY_get_data(x509ne); - if (!asn1) - return false; - tmp = ASN1_STRING_to_UTF8(&buf, asn1); - if (tmp <= 0) - return false; - - strncpynt(out, (char *)buf, size); - - { - const bool ret = (strlen ((char *)buf) < size); - OPENSSL_free (buf); - return ret; - } -} - -#ifdef ENABLE_X509ALTUSERNAME -static -bool extract_x509_extension(X509 *cert, char *fieldname, char *out, int size) -{ - bool retval = false; - X509_EXTENSION *pExt; - char *buf = 0; - int length = 0; - GENERAL_NAMES *extensions; - int nid = OBJ_txt2nid(fieldname); - - extensions = (GENERAL_NAMES *)X509_get_ext_d2i(cert, nid, NULL, NULL); - if ( extensions ) - { - int numalts; - int i; - /* get amount of alternatives, - * RFC2459 claims there MUST be at least - * one, but we don't depend on it... - */ - - numalts = sk_GENERAL_NAME_num(extensions); - - /* loop through all alternatives */ - for (i=0; i<numalts; i++) - { - /* get a handle to alternative name number i */ - const GENERAL_NAME *name = sk_GENERAL_NAME_value (extensions, i ); - - switch (name->type) - { - case GEN_EMAIL: - ASN1_STRING_to_UTF8((unsigned char**)&buf, name->d.ia5); - if ( strlen (buf) != name->d.ia5->length ) - { - msg (D_TLS_ERRORS, "ASN1 ERROR: string contained terminating zero"); - OPENSSL_free (buf); - } else { - strncpynt(out, buf, size); - OPENSSL_free(buf); - retval = true; - } - break; - default: - msg (D_TLS_ERRORS, "ASN1 ERROR: can not handle field type %i", - name->type); - break; - } - } - sk_GENERAL_NAME_free (extensions); - } - return retval; -} -#endif /* ENABLE_X509ALTUSERNAME */ - #ifdef ENABLE_X509_TRACK /* * setenv_x509_track function -- save X509 fields to environment, @@ -740,21 +632,7 @@ verify_cert(struct tls_session *session, x509_cert_t *cert, int cert_depth) string_replace_leading (subject, '-', '_'); /* extract the username (default is CN) */ -#ifdef ENABLE_X509ALTUSERNAME - if (strncmp("ext:",x509_username_field,4) == 0) - { - if (!extract_x509_extension (ctx->current_cert, x509_username_field+4, common_name, sizeof(common_name))) - { - msg (D_TLS_ERRORS, "VERIFY ERROR: could not extract %s extension from X509 subject string ('%s') " - "-- note that the username length is limited to %d characters", - x509_username_field+4, - subject, - TLS_USERNAME_LEN); - goto err; - } - } else -#endif - if (!extract_x509_field_ssl (X509_get_subject_name (cert), x509_username_field, common_name, sizeof(common_name))) + if (verify_get_username (common_name, TLS_USERNAME_LEN, x509_username_field, cert)) { if (!cert_depth) { @@ -768,7 +646,6 @@ verify_cert(struct tls_session *session, x509_cert_t *cert, int cert_depth) } } - string_mod_sslname (common_name, COMMON_NAME_CHAR_CLASS, opt->ssl_flags); #if 0 /* print some debugging info */ diff --git a/ssl_verify_backend.h b/ssl_verify_backend.h index 31b5210..82109c8 100644 --- a/ssl_verify_backend.h +++ b/ssl_verify_backend.h @@ -84,4 +84,20 @@ void cert_hash_remember (struct tls_session *session, const int cert_depth, */ bool verify_get_subject (char **subject, x509_cert_t *cert); +/* + * Retrieve the certificate's username from the specified field. + * + * If the field is prepended with ext: and ENABLE_X509ALTUSERNAME is enabled, + * it will be loaded from an X.509 extension + * + * @param cn Buffer to return the common name in. + * @param cn_len Length of the cn buffer. + * @param x509_username_field Name of the field to load from + * @param cert Certificate to retrieve the common name from. + * + * @return \c 1 on failure, \c 0 on success + */ +bool verify_get_username (char *common_name, int cn_len, + char * x509_username_field, X509 *peer_cert); + #endif /* SSL_VERIFY_BACKEND_H_ */ diff --git a/ssl_verify_openssl.c b/ssl_verify_openssl.c index 64b71c3..1037474 100644 --- a/ssl_verify_openssl.c +++ b/ssl_verify_openssl.c @@ -82,3 +82,130 @@ verify_get_subject (char **subject, X509 *cert) return 0; } + +#ifdef ENABLE_X509ALTUSERNAME +static +bool extract_x509_extension(X509 *cert, char *fieldname, char *out, int size) +{ + bool retval = false; + X509_EXTENSION *pExt; + char *buf = 0; + int length = 0; + GENERAL_NAMES *extensions; + int nid = OBJ_txt2nid(fieldname); + + extensions = (GENERAL_NAMES *)X509_get_ext_d2i(cert, nid, NULL, NULL); + if ( extensions ) + { + int numalts; + int i; + /* get amount of alternatives, + * RFC2459 claims there MUST be at least + * one, but we don't depend on it... + */ + + numalts = sk_GENERAL_NAME_num(extensions); + + /* loop through all alternatives */ + for (i=0; i<numalts; i++) + { + /* get a handle to alternative name number i */ + const GENERAL_NAME *name = sk_GENERAL_NAME_value (extensions, i ); + + switch (name->type) + { + case GEN_EMAIL: + ASN1_STRING_to_UTF8((unsigned char**)&buf, name->d.ia5); + if ( strlen (buf) != name->d.ia5->length ) + { + msg (D_TLS_ERRORS, "ASN1 ERROR: string contained terminating zero"); + OPENSSL_free (buf); + } else { + strncpynt(out, buf, size); + OPENSSL_free(buf); + retval = true; + } + break; + default: + msg (D_TLS_ERRORS, "ASN1 ERROR: can not handle field type %i", + name->type); + break; + } + } + sk_GENERAL_NAME_free (extensions); + } + return retval; +} +#endif /* ENABLE_X509ALTUSERNAME */ + +/* + * Extract a field from an X509 subject name. + * + * Example: + * + * /C=US/ST=CO/L=Denver/O=ORG/CN=First-CN/CN=Test-CA/Email=jim@yonan.net + * + * The common name is 'Test-CA' + * + * Return true on success, false on error (insufficient buffer size in 'out' + * to contain result is grounds for error). + */ +static bool +extract_x509_field_ssl (X509_NAME *x509, const char *field_name, char *out, + int size) +{ + int lastpos = -1; + int tmp = -1; + X509_NAME_ENTRY *x509ne = 0; + ASN1_STRING *asn1 = 0; + unsigned char *buf = (unsigned char *)1; /* bug in OpenSSL 0.9.6b ASN1_STRING_to_UTF8 requires this workaround */ + int nid = OBJ_txt2nid((char *)field_name); + + ASSERT (size > 0); + *out = '\0'; + do { + lastpos = tmp; + tmp = X509_NAME_get_index_by_NID(x509, nid, lastpos); + } while (tmp > -1); + + /* Nothing found */ + if (lastpos == -1) + return false; + + x509ne = X509_NAME_get_entry(x509, lastpos); + if (!x509ne) + return false; + + asn1 = X509_NAME_ENTRY_get_data(x509ne); + if (!asn1) + return false; + tmp = ASN1_STRING_to_UTF8(&buf, asn1); + if (tmp <= 0) + return false; + + strncpynt(out, (char *)buf, size); + + { + const bool ret = (strlen ((char *)buf) < size); + OPENSSL_free (buf); + return ret; + } +} + +bool +verify_get_username (char *common_name, int cn_len, + char * x509_username_field, X509 *peer_cert) +{ +#ifdef ENABLE_X509ALTUSERNAME + if (strncmp("ext:",x509_username_field,4) == 0) + { + if (!extract_x509_extension (peer_cert, x509_username_field+4, common_name, cn_len)) + return true; + } else +#endif + if (!extract_x509_field_ssl (X509_get_subject_name (peer_cert), + x509_username_field, common_name, cn_len)) + return true; + + return false; +} |