diff options
-rw-r--r-- | ssl.c | 218 | ||||
-rw-r--r-- | ssl.h | 15 | ||||
-rw-r--r-- | ssl_verify.c | 75 | ||||
-rw-r--r-- | ssl_verify.h | 22 | ||||
-rw-r--r-- | ssl_verify_backend.h | 43 | ||||
-rw-r--r-- | ssl_verify_openssl.c | 168 |
6 files changed, 313 insertions, 228 deletions
@@ -296,150 +296,6 @@ ssl_put_auth_challenge (const char *cr_str) #endif -#ifdef ENABLE_X509_TRACK -/* - * setenv_x509_track function -- save X509 fields to environment, - * using the naming convention: - * - * X509_{cert_depth}_{name}={value} - * - * This function differs from setenv_x509 below in the following ways: - * - * (1) Only explicitly named attributes in xt are saved, per usage - * of --x509-track program options. - * (2) Only the level 0 cert info is saved unless the XT_FULL_CHAIN - * flag is set in xt->flags (corresponds with prepending a '+' - * to the name when specified by --x509-track program option). - * (3) This function supports both X509 subject name fields as - * well as X509 V3 extensions. - */ - -/* worker method for setenv_x509_track */ -static void -do_setenv_x509 (struct env_set *es, const char *name, char *value, int depth) -{ - char *name_expand; - size_t name_expand_size; - - string_mod (value, CC_ANY, CC_CRLF, '?'); - msg (D_X509_ATTR, "X509 ATTRIBUTE name='%s' value='%s' depth=%d", name, value, depth); - name_expand_size = 64 + strlen (name); - name_expand = (char *) malloc (name_expand_size); - check_malloc_return (name_expand); - openvpn_snprintf (name_expand, name_expand_size, "X509_%d_%s", depth, name); - setenv_str (es, name_expand, value); - free (name_expand); -} - -static void -setenv_x509_track (const struct x509_track *xt, struct env_set *es, const int depth, X509 *x509) -{ - X509_NAME *x509_name = X509_get_subject_name (x509); - const char nullc = '\0'; - int i; - - while (xt) - { - if (depth == 0 || (xt->flags & XT_FULL_CHAIN)) - { - i = X509_NAME_get_index_by_NID(x509_name, xt->nid, -1); - if (i >= 0) - { - X509_NAME_ENTRY *ent = X509_NAME_get_entry(x509_name, i); - if (ent) - { - ASN1_STRING *val = X509_NAME_ENTRY_get_data (ent); - unsigned char *buf; - buf = (unsigned char *)1; /* bug in OpenSSL 0.9.6b ASN1_STRING_to_UTF8 requires this workaround */ - if (ASN1_STRING_to_UTF8 (&buf, val) > 0) - { - do_setenv_x509(es, xt->name, (char *)buf, depth); - OPENSSL_free (buf); - } - } - } - else - { - i = X509_get_ext_by_NID(x509, xt->nid, -1); - if (i >= 0) - { - X509_EXTENSION *ext = X509_get_ext(x509, i); - if (ext) - { - BIO *bio = BIO_new(BIO_s_mem()); - if (bio) - { - if (X509V3_EXT_print(bio, ext, 0, 0)) - { - if (BIO_write(bio, &nullc, 1) == 1) - { - char *str; - BIO_get_mem_data(bio, &str); - do_setenv_x509(es, xt->name, str, depth); - } - } - BIO_free(bio); - } - } - } - } - } - xt = xt->next; - } -} -#endif - -/* - * Save X509 fields to environment, using the naming convention: - * - * X509_{cert_depth}_{name}={value} - */ -static void -setenv_x509 (struct env_set *es, const int error_depth, X509_NAME *x509) -{ - int i, n; - int fn_nid; - ASN1_OBJECT *fn; - ASN1_STRING *val; - X509_NAME_ENTRY *ent; - const char *objbuf; - unsigned char *buf; - char *name_expand; - size_t name_expand_size; - - n = X509_NAME_entry_count (x509); - for (i = 0; i < n; ++i) - { - ent = X509_NAME_get_entry (x509, i); - if (!ent) - continue; - fn = X509_NAME_ENTRY_get_object (ent); - if (!fn) - continue; - val = X509_NAME_ENTRY_get_data (ent); - if (!val) - continue; - fn_nid = OBJ_obj2nid (fn); - if (fn_nid == NID_undef) - continue; - objbuf = OBJ_nid2sn (fn_nid); - if (!objbuf) - continue; - buf = (unsigned char *)1; /* bug in OpenSSL 0.9.6b ASN1_STRING_to_UTF8 requires this workaround */ - if (ASN1_STRING_to_UTF8 (&buf, val) <= 0) - continue; - name_expand_size = 64 + strlen (objbuf); - name_expand = (char *) malloc (name_expand_size); - check_malloc_return (name_expand); - openvpn_snprintf (name_expand, name_expand_size, "X509_%d_%s", error_depth, objbuf); - string_mod (name_expand, CC_PRINT, CC_CRLF, '_'); - string_mod ((char*)buf, CC_PRINT, CC_CRLF, '_'); - setenv_str (es, name_expand, (char*)buf); - free (name_expand); - OPENSSL_free (buf); - } -} - static void setenv_untrusted (struct tls_session *session) { @@ -602,7 +458,6 @@ verify_cert(struct tls_session *session, x509_cert_t *cert, int cert_depth) char common_name[TLS_USERNAME_LEN] = {0}; const struct tls_options *opt; struct argv argv = argv_new (); - char *serial = NULL; opt = session->opt; ASSERT (opt); @@ -617,14 +472,6 @@ verify_cert(struct tls_session *session, x509_cert_t *cert, int cert_depth) goto err; } - /* Save X509 fields in environment */ -#ifdef ENABLE_X509_TRACK - if (opt->x509_track) - setenv_x509_track (opt->x509_track, opt->es, cert_depth, cert); - else -#endif - setenv_x509 (opt->es, cert_depth, X509_get_subject_name (cert)); - /* enforce character class restrictions in X509 name */ string_mod_sslname (subject, X509_NAME_CHAR_CLASS, opt->ssl_flags); string_replace_leading (subject, '-', '_'); @@ -677,39 +524,10 @@ verify_cert(struct tls_session *session, x509_cert_t *cert, int cert_depth) if (cert_depth == 0) set_common_name (session, common_name); - /* export subject name string as environmental variable */ session->verify_maxlevel = max_int (session->verify_maxlevel, cert_depth); - openvpn_snprintf (envname, sizeof(envname), "tls_id_%d", cert_depth); - setenv_str (opt->es, envname, subject); - -#ifdef ENABLE_EUREPHIA - /* export X509 cert SHA1 fingerprint */ - { - struct gc_arena gc = gc_new (); - openvpn_snprintf (envname, sizeof(envname), "tls_digest_%d", cert_depth); - setenv_str (opt->es, envname, - format_hex_ex(cert->sha1_hash, SHA_DIGEST_LENGTH, 0, 1, ":", &gc)); - gc_free(&gc); - } -#endif -#if 0 - /* export common name string as environmental variable */ - openvpn_snprintf (envname, sizeof(envname), "tls_common_name_%d", cert_depth); - setenv_str (opt->es, envname, common_name); -#endif - /* export serial number as environmental variable, - use bignum in case serial number is large */ - { - ASN1_INTEGER *asn1_i; - BIGNUM *bignum; - asn1_i = X509_get_serialNumber(cert); - bignum = ASN1_INTEGER_to_BN(asn1_i, NULL); - serial = BN_bn2dec(bignum); - openvpn_snprintf (envname, sizeof(envname), "tls_serial_%d", cert_depth); - setenv_str (opt->es, envname, serial); - BN_free(bignum); - } + /* export certificate values to the environment */ + verify_cert_set_env(opt->es, cert, cert_depth, subject, common_name, opt->x509_track); /* export current untrusted IP */ setenv_untrusted (session); @@ -852,18 +670,22 @@ verify_cert(struct tls_session *session, x509_cert_t *cert, int cert_depth) { char fn[256]; int fd; + char *serial = verify_get_serial(cert); if (!openvpn_snprintf(fn, sizeof(fn), "%s%c%s", opt->crl_file, OS_SPECIFIC_DIRSEP, serial)) { msg (D_HANDSHAKE, "VERIFY CRL: filename overflow"); + verify_free_serial(serial); goto err; } fd = open (fn, O_RDONLY); if (fd >= 0) { msg (D_HANDSHAKE, "VERIFY CRL: certificate serial number %s is revoked", serial); + verify_free_serial(serial); close(fd); goto err; } + verify_free_serial(serial); } else { @@ -922,8 +744,6 @@ verify_cert(struct tls_session *session, x509_cert_t *cert, int cert_depth) done: OPENSSL_free (subject); - if (serial) - OPENSSL_free(serial); argv_reset (&argv); return (session->verified == true) ? 1 : 0; @@ -935,32 +755,6 @@ verify_cert(struct tls_session *session, x509_cert_t *cert, int cert_depth) /** @} name Function for authenticating a new connection from a remote OpenVPN peer */ -#ifdef ENABLE_X509_TRACK - -void -x509_track_add (const struct x509_track **ll_head, const char *name, int msglevel, struct gc_arena *gc) -{ - struct x509_track *xt; - ALLOC_OBJ_CLEAR_GC (xt, struct x509_track, gc); - if (*name == '+') - { - xt->flags |= XT_FULL_CHAIN; - ++name; - } - xt->name = name; - xt->nid = OBJ_txt2nid(name); - if (xt->nid != NID_undef) - { - xt->next = *ll_head; - *ll_head = xt; - } - else - msg(msglevel, "x509_track: no such attribute '%s'", name); -} - -#endif - - /* * Initialize SSL context. * All files are in PEM format. @@ -145,21 +145,6 @@ */ /* #define MEASURE_TLS_HANDSHAKE_STATS */ -#ifdef ENABLE_X509_TRACK - -struct x509_track -{ - const struct x509_track *next; - const char *name; -# define XT_FULL_CHAIN (1<<0) - unsigned int flags; - int nid; -}; - -void x509_track_add (const struct x509_track **ll_head, const char *name, int msglevel, struct gc_arena *gc); - -#endif - /* * Used in --mode server mode to check tls-auth signature on initial * packets received from new clients. diff --git a/ssl_verify.c b/ssl_verify.c index 928344c..5f743da 100644 --- a/ssl_verify.c +++ b/ssl_verify.c @@ -281,6 +281,81 @@ tls_lock_cert_hash_set (struct tls_multi *multi) multi->locked_cert_hash_set = cert_hash_copy (chs); } +#ifdef ENABLE_X509_TRACK + +void +x509_track_add (const struct x509_track **ll_head, const char *name, int msglevel, struct gc_arena *gc) +{ + struct x509_track *xt; + ALLOC_OBJ_CLEAR_GC (xt, struct x509_track, gc); + if (*name == '+') + { + xt->flags |= XT_FULL_CHAIN; + ++name; + } + xt->name = name; + xt->nid = OBJ_txt2nid(name); + if (xt->nid != NID_undef) + { + xt->next = *ll_head; + *ll_head = xt; + } + else + msg(msglevel, "x509_track: no such attribute '%s'", name); +} + +#endif + +/* + * Export the subject, common_name, and raw certificate fields to the + * environment for later verification by scripts and plugins. + */ +void +verify_cert_set_env(struct env_set *es, x509_cert_t *peer_cert, int cert_depth, + const char *subject, const char *common_name, + const struct x509_track *x509_track) +{ + char envname[64]; + + /* Save X509 fields in environment */ +#ifdef ENABLE_X509_TRACK + if (x509_track) + setenv_x509_track (x509_track, es, cert_depth, peer_cert); + else +#endif + setenv_x509 (es, cert_depth, peer_cert); + + /* export subject name string as environmental variable */ + openvpn_snprintf (envname, sizeof(envname), "tls_id_%d", cert_depth); + setenv_str (es, envname, subject); + +#if 0 + /* export common name string as environmental variable */ + openvpn_snprintf (envname, sizeof(envname), "tls_common_name_%d", cert_depth); + setenv_str (es, envname, common_name); +#endif + +#ifdef ENABLE_EUREPHIA + /* export X509 cert SHA1 fingerprint */ + { + struct gc_arena gc = gc_new (); + openvpn_snprintf (envname, sizeof(envname), "tls_digest_%d", cert_depth); + setenv_str (es, envname, + format_hex_ex(peer_cert->sha1_hash, SHA_DIGEST_LENGTH, 0, 1, ":", &gc)); + gc_free(&gc); + } +#endif + + /* export serial number as environmental variable, + use bignum in case serial number is large */ + { + char *serial = verify_get_serial(peer_cert); + openvpn_snprintf (envname, sizeof(envname), "tls_serial_%d", cert_depth); + setenv_str (es, envname, serial); + verify_free_serial(serial); + } +} + /* *************************************************************************** * Functions for the management of deferred authentication when using diff --git a/ssl_verify.h b/ssl_verify.h index ad94bc6..03a7942 100644 --- a/ssl_verify.h +++ b/ssl_verify.h @@ -40,6 +40,8 @@ #include "ssl_verify_openssl.h" #endif +#include "ssl_verify_backend.h" + /* * Keep track of certificate hashes at various depths */ @@ -197,6 +199,21 @@ void verify_user_pass(struct user_pass *up, struct tls_multi *multi, */ void verify_final_auth_checks(struct tls_multi *multi, struct tls_session *session); +#ifdef ENABLE_X509_TRACK + +struct x509_track +{ + const struct x509_track *next; + const char *name; +# define XT_FULL_CHAIN (1<<0) + unsigned int flags; + int nid; +}; + +void x509_track_add (const struct x509_track **ll_head, const char *name, int msglevel, struct gc_arena *gc); + +#endif + /* * TODO: document */ @@ -215,6 +232,11 @@ tls_client_reason (struct tls_multi *multi) #endif } +/* TEMP */ +void +verify_cert_set_env(struct env_set *es, x509_cert_t *peer_cert, int cert_depth, + const char *subject, const char *common_name, + const struct x509_track *x509_track); #endif /* SSL_VERIFY_H_ */ diff --git a/ssl_verify_backend.h b/ssl_verify_backend.h index 82109c8..a32f0ea 100644 --- a/ssl_verify_backend.h +++ b/ssl_verify_backend.h @@ -98,6 +98,47 @@ bool verify_get_subject (char **subject, x509_cert_t *cert); * @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); + char * x509_username_field, x509_cert_t *peer_cert); + +/* + * Return the certificate's serial number. + * + * The serial number is returned as a string, since it might be a bignum. + * The returened string must be freed with \c verify_free_serial() + * + * @param cert Certificate to retrieve the serial number from. + * + * @return The certificate's serial number. + */ +char *verify_get_serial (x509_cert_t *cert); + +/* + * Free a serial number string as returned by \c verify_get_serial() + * + * @param serial The string to be freed. + */ +void verify_free_serial (char *serial); + +/* + * TODO: document + * + * @param xt + * @param es Environment set to save variables in + * @param cert_depth Depth of the certificate + * @param cert Certificate to set the environment for + */ +void setenv_x509_track (const struct x509_track *xt, struct env_set *es, + const int depth, x509_cert_t *x509); + +/* + * Save X509 fields to environment, using the naming convention: + * + * X509_{cert_depth}_{name}={value} + * + * @param es Environment set to save variables in + * @param cert_depth Depth of the certificate + * @param cert Certificate to set the environment for + */ +void setenv_x509 (struct env_set *es, int cert_depth, x509_cert_t *cert); #endif /* SSL_VERIFY_BACKEND_H_ */ diff --git a/ssl_verify_openssl.c b/ssl_verify_openssl.c index 1037474..417e5d7 100644 --- a/ssl_verify_openssl.c +++ b/ssl_verify_openssl.c @@ -209,3 +209,171 @@ verify_get_username (char *common_name, int cn_len, return false; } + +char * +verify_get_serial (x509_cert_t *cert) +{ + ASN1_INTEGER *asn1_i; + BIGNUM *bignum; + char *serial; + + asn1_i = X509_get_serialNumber(cert); + bignum = ASN1_INTEGER_to_BN(asn1_i, NULL); + serial = BN_bn2dec(bignum); + + BN_free(bignum); + return serial; +} + +void +verify_free_serial (char *serial) +{ + if (serial) + OPENSSL_free(serial); +} + +#ifdef ENABLE_X509_TRACK +/* + * setenv_x509_track function -- save X509 fields to environment, + * using the naming convention: + * + * X509_{cert_depth}_{name}={value} + * + * This function differs from setenv_x509 below in the following ways: + * + * (1) Only explicitly named attributes in xt are saved, per usage + * of --x509-track program options. + * (2) Only the level 0 cert info is saved unless the XT_FULL_CHAIN + * flag is set in xt->flags (corresponds with prepending a '+' + * to the name when specified by --x509-track program option). + * (3) This function supports both X509 subject name fields as + * well as X509 V3 extensions. + */ + +/* worker method for setenv_x509_track */ +static void +do_setenv_x509 (struct env_set *es, const char *name, char *value, int depth) +{ + char *name_expand; + size_t name_expand_size; + + string_mod (value, CC_ANY, CC_CRLF, '?'); + msg (D_X509_ATTR, "X509 ATTRIBUTE name='%s' value='%s' depth=%d", name, value, depth); + name_expand_size = 64 + strlen (name); + name_expand = (char *) malloc (name_expand_size); + check_malloc_return (name_expand); + openvpn_snprintf (name_expand, name_expand_size, "X509_%d_%s", depth, name); + setenv_str (es, name_expand, value); + free (name_expand); +} + +void +setenv_x509_track (const struct x509_track *xt, struct env_set *es, const int depth, X509 *x509) +{ + X509_NAME *x509_name = X509_get_subject_name (x509); + const char nullc = '\0'; + int i; + + while (xt) + { + if (depth == 0 || (xt->flags & XT_FULL_CHAIN)) + { + i = X509_NAME_get_index_by_NID(x509_name, xt->nid, -1); + if (i >= 0) + { + X509_NAME_ENTRY *ent = X509_NAME_get_entry(x509_name, i); + if (ent) + { + ASN1_STRING *val = X509_NAME_ENTRY_get_data (ent); + unsigned char *buf; + buf = (unsigned char *)1; /* bug in OpenSSL 0.9.6b ASN1_STRING_to_UTF8 requires this workaround */ + if (ASN1_STRING_to_UTF8 (&buf, val) > 0) + { + do_setenv_x509(es, xt->name, (char *)buf, depth); + OPENSSL_free (buf); + } + } + } + else + { + i = X509_get_ext_by_NID(x509, xt->nid, -1); + if (i >= 0) + { + X509_EXTENSION *ext = X509_get_ext(x509, i); + if (ext) + { + BIO *bio = BIO_new(BIO_s_mem()); + if (bio) + { + if (X509V3_EXT_print(bio, ext, 0, 0)) + { + if (BIO_write(bio, &nullc, 1) == 1) + { + char *str; + BIO_get_mem_data(bio, &str); + do_setenv_x509(es, xt->name, str, depth); + } + } + BIO_free(bio); + } + } + } + } + } + xt = xt->next; + } +} +#endif + +/* + * Save X509 fields to environment, using the naming convention: + * + * X509_{cert_depth}_{name}={value} + */ +void +setenv_x509 (struct env_set *es, int cert_depth, x509_cert_t *peer_cert) +{ + int i, n; + int fn_nid; + ASN1_OBJECT *fn; + ASN1_STRING *val; + X509_NAME_ENTRY *ent; + const char *objbuf; + unsigned char *buf; + char *name_expand; + size_t name_expand_size; + X509_NAME *x509 = X509_get_subject_name (peer_cert); + + n = X509_NAME_entry_count (x509); + for (i = 0; i < n; ++i) + { + ent = X509_NAME_get_entry (x509, i); + if (!ent) + continue; + fn = X509_NAME_ENTRY_get_object (ent); + if (!fn) + continue; + val = X509_NAME_ENTRY_get_data (ent); + if (!val) + continue; + fn_nid = OBJ_obj2nid (fn); + if (fn_nid == NID_undef) + continue; + objbuf = OBJ_nid2sn (fn_nid); + if (!objbuf) + continue; + buf = (unsigned char *)1; /* bug in OpenSSL 0.9.6b ASN1_STRING_to_UTF8 requires this workaround */ + if (ASN1_STRING_to_UTF8 (&buf, val) <= 0) + continue; + name_expand_size = 64 + strlen (objbuf); + name_expand = (char *) malloc (name_expand_size); + check_malloc_return (name_expand); + openvpn_snprintf (name_expand, name_expand_size, "X509_%d_%s", cert_depth, + objbuf); + string_mod (name_expand, CC_PRINT, CC_CRLF, '_'); + string_mod ((char*)buf, CC_PRINT, CC_CRLF, '_'); + setenv_str (es, name_expand, (char*)buf); + free (name_expand); + OPENSSL_free (buf); + } +} |