summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ssl.c125
-rw-r--r--ssl_verify_backend.h16
-rw-r--r--ssl_verify_openssl.c127
3 files changed, 144 insertions, 124 deletions
diff --git a/ssl.c b/ssl.c
index e600ca7..17ef478 100644
--- a/ssl.c
+++ b/ssl.c
@@ -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;
+}