The UTF-8 support that came with commit 2627335 does allow international usernames and passwords. This patch introduces UTF-8 support for X.509 DNs. Additionally, instead of using the legacy openssl format, DNs are now displayed in RFC 2253 format; "/C=ru/L=\xD0\x9C\xD0\xBE\xD1\x81\xD0\xBA\xD0 \xB2\xD0\xB0/O=\xD0\x9A\xD1\x80\xD0\xB5\xD0\xBC\xD0\xBB\xD1\x8C/CN=kreml.ru" becomes "C=ru, L=Москва, O=Кремль, CN=kreml.ru".
Since the specific character classes for X.509 names are removed, the "no-name-remapping" configuration option has no use anymore and is removed as well. Signed-off-by: Heiko Hund <heiko.h...@sophos.com> --- openvpn.8 | 23 +---------------------- options.c | 12 +----------- pkcs11_openssl.c | 2 +- sample-scripts/verify-cn | 6 +++--- ssl_openssl.c | 36 ++++++++++++++++++++++++++++++++++++ ssl_openssl.h | 1 + ssl_verify.c | 45 +++++++++------------------------------------ ssl_verify_openssl.c | 4 ++-- 8 files changed, 54 insertions(+), 75 deletions(-) diff --git a/openvpn.8 b/openvpn.8 index b8594e1..e96c1e4 100644 --- a/openvpn.8 +++ b/openvpn.8 @@ -3322,27 +3322,6 @@ the authenticated username as the common name, rather than the common name from the client cert. .\"********************************************************* .TP -.B \-\-no-name-remapping -Allow Common Name, X509 Subject, and username strings to include -any printable character including space, but excluding control -characters such as tab, newline, and carriage-return. - -By default, OpenVPN will remap -any character other than alphanumeric, underbar ('_'), dash -('-'), dot ('.'), and slash ('/') to underbar ('_'). The X509 -Subject string as returned by the -.B tls_id -environmental variable, can additionally contain colon (':') or -equal ('='). - -While name remapping is performed for security reasons to reduce -the possibility of introducing string expansion security vulnerabilities -in user-defined authentication -scripts, this option is provided for those cases where it is desirable to -disable the remapping feature. Don't use this option unless you -know what you are doing! -.\"********************************************************* -.TP .B \-\-port-share host port [dir] When run in TCP server mode, share the OpenVPN port with another application, such as an HTTPS server. If OpenVPN @@ -4463,7 +4442,7 @@ When .B cmd is executed two arguments are appended, as follows: -.B cmd certificate_depth X509_NAME_oneline +.B cmd certificate_depth subject These arguments are, respectively, the current certificate depth and the X509 common name (cn) of the peer. diff --git a/options.c b/options.c index 0d86cd0..a01ae76 100644 --- a/options.c +++ b/options.c @@ -597,7 +597,7 @@ static const char usage_message[] = " pending TLS connection that has otherwise passed all other\n" " tests of certification. cmd should return 0 to allow\n" " TLS handshake to proceed, or 1 to fail. (cmd is\n" - " executed as 'cmd certificate_depth X509_NAME_oneline')\n" + " executed as 'cmd certificate_depth subject')\n" "--tls-export-cert [directory] : Get peer cert in PEM format and store it \n" " in an openvpn temporary file in [directory]. Peer cert is \n" " stored before tls-verify script execution and deleted after.\n" @@ -2160,9 +2160,6 @@ options_postprocess_verify_ce (const struct options *options, const struct conne if ((options->ssl_flags & SSLF_AUTH_USER_PASS_OPTIONAL) && !ccnr) msg (M_USAGE, "--auth-user-pass-optional %s", postfix); } - - if ((options->ssl_flags & SSLF_NO_NAME_REMAPPING) && script_method == SM_SYSTEM) - msg (M_USAGE, "--script-security method='system' cannot be combined with --no-name-remapping"); } else { @@ -2197,8 +2194,6 @@ options_postprocess_verify_ce (const struct options *options, const struct conne msg (M_USAGE, "--username-as-common-name requires --mode server"); if (options->ssl_flags & SSLF_AUTH_USER_PASS_OPTIONAL) msg (M_USAGE, "--auth-user-pass-optional requires --mode server"); - if (options->ssl_flags & SSLF_NO_NAME_REMAPPING) - msg (M_USAGE, "--no-name-remapping requires --mode server"); if (options->ssl_flags & SSLF_OPT_VERIFY) msg (M_USAGE, "--opt-verify requires --mode server"); if (options->server_flags & SF_TCP_NODELAY_HELPER) @@ -5389,11 +5384,6 @@ add_option (struct options *options, VERIFY_PERMISSION (OPT_P_GENERAL); options->ssl_flags |= SSLF_AUTH_USER_PASS_OPTIONAL; } - else if (streq (p[0], "no-name-remapping")) - { - VERIFY_PERMISSION (OPT_P_GENERAL); - options->ssl_flags |= SSLF_NO_NAME_REMAPPING; - } else if (streq (p[0], "opt-verify")) { VERIFY_PERMISSION (OPT_P_GENERAL); diff --git a/pkcs11_openssl.c b/pkcs11_openssl.c index e3463dc..943b90f 100644 --- a/pkcs11_openssl.c +++ b/pkcs11_openssl.c @@ -129,7 +129,7 @@ pkcs11_certificate_dn (pkcs11h_certificate_t certificate, char *dn, goto cleanup; } - X509_NAME_oneline (X509_get_subject_name (x509), dn, dn_len); + openssl_get_subject (x509, dn, dn_len); ret = 0; diff --git a/sample-scripts/verify-cn b/sample-scripts/verify-cn index f9fea0f..6e747ef 100755 --- a/sample-scripts/verify-cn +++ b/sample-scripts/verify-cn @@ -3,7 +3,7 @@ # verify-cn -- a sample OpenVPN tls-verify script # # Return 0 if cn matches the common name component of -# X509_NAME_oneline, 1 otherwise. +# subject, 1 otherwise. # # For example in OpenVPN, you could use the directive: # @@ -13,7 +13,7 @@ # the client common name is listed on a line in the # allowed_clients file. -die "usage: verify-cn cnfile certificate_depth X509_NAME_oneline" if (@ARGV != 3); +die "usage: verify-cn cnfile certificate_depth subject" if (@ARGV != 3); # Parse out arguments: # cnfile -- The file containing the list of common names, one per @@ -37,7 +37,7 @@ if ($depth == 0) { # If so, parse out the common name substring in # the X509 subject string. - if ($x509 =~ /\/CN=([^\/]+)/) { + if ($x509 =~ / CN=([^,]+)/) { $cn = $1; # Accept the connection if the X509 common name # string matches the passed cn argument. diff --git a/ssl_openssl.c b/ssl_openssl.c index 2e9fc4d..536b5f0 100644 --- a/ssl_openssl.c +++ b/ssl_openssl.c @@ -1290,4 +1290,40 @@ get_highest_preference_tls_cipher (char *buf, int size) SSL_CTX_free (ctx); } +char * +openssl_get_subject (X509 *cert, char *buf, int size) +{ + BIO *subject_bio; + BUF_MEM *subject_mem; + char *subject = buf; + int maxlen = size; + + subject_bio = BIO_new (BIO_s_mem ()); + if (subject_bio == NULL) + goto out; + + X509_NAME_print_ex (subject_bio, X509_get_subject_name (cert), + 0, XN_FLAG_SEP_CPLUS_SPC | XN_FLAG_FN_SN | + ASN1_STRFLGS_UTF8_CONVERT | ASN1_STRFLGS_ESC_CTRL); + + if (BIO_eof (subject_bio)) + goto out_free; + + BIO_get_mem_ptr (subject_bio, &subject_mem); + if (subject == NULL) + { + maxlen = subject_mem->length + 1; + subject = malloc (maxlen); + check_malloc_return (subject); + } + + memcpy (subject, subject_mem->data, maxlen); + subject[maxlen - 1] = '\0'; + +out_free: + BIO_free (subject_bio); +out: + return subject; +} + #endif /* defined(USE_SSL) && defined(USE_OPENSSL) */ diff --git a/ssl_openssl.h b/ssl_openssl.h index fc2052c..e5e303a 100644 --- a/ssl_openssl.h +++ b/ssl_openssl.h @@ -54,5 +54,6 @@ struct key_state_ssl { extern int mydata_index; /* GLOBAL */ void openssl_set_mydata_index (void); +char *openssl_get_subject (X509 *cert, char *buf, int size); #endif /* SSL_OPENSSL_H_ */ diff --git a/ssl_verify.c b/ssl_verify.c index 326b005..0b2a1fb 100644 --- a/ssl_verify.c +++ b/ssl_verify.c @@ -40,24 +40,9 @@ #include "ssl_verify_openssl.h" #endif -/** Legal characters in an X509 name */ -#define X509_NAME_CHAR_CLASS (CC_ALNUM|CC_UNDERBAR|CC_DASH|CC_DOT|CC_AT|CC_COLON|CC_SLASH|CC_EQUAL) - -/** Legal characters in a common name */ -#define COMMON_NAME_CHAR_CLASS (CC_ALNUM|CC_UNDERBAR|CC_DASH|CC_DOT|CC_AT|CC_SLASH) - /** Maximum length of common name */ #define TLS_USERNAME_LEN 64 -static void -string_mod_sslname (char *str, const unsigned int restrictive_flags, const unsigned int ssl_flags) -{ - if (ssl_flags & SSLF_NO_NAME_REMAPPING) - string_mod (str, CC_PRINT, CC_CRLF, '_'); - else - string_mod (str, restrictive_flags, 0, '_'); -} - /* * Export the untrusted IP address and port to the environment */ @@ -595,7 +580,7 @@ verify_cert(struct tls_session *session, x509_cert_t *cert, int cert_depth) } /* enforce character class restrictions in X509 name */ - string_mod_sslname (subject, X509_NAME_CHAR_CLASS, opt->ssl_flags); + string_mod (subject, CC_PRINT, CC_CRLF, '_'); string_replace_leading (subject, '-', '_'); /* extract the username (default is CN) */ @@ -615,7 +600,7 @@ verify_cert(struct tls_session *session, x509_cert_t *cert, int cert_depth) } /* enforce character class restrictions in common name */ - string_mod_sslname (common_name, COMMON_NAME_CHAR_CLASS, opt->ssl_flags); + string_mod (common_name, CC_PRINT, CC_CRLF, '_'); /* warn if cert chain is too deep */ if (cert_depth >= MAX_CERT_DEPTH) @@ -1005,7 +990,7 @@ verify_user_pass_script (struct tls_session *session, const struct user_pass *up * Verify the username and password using a plugin */ static int -verify_user_pass_plugin (struct tls_session *session, const struct user_pass *up, const char *raw_username) +verify_user_pass_plugin (struct tls_session *session, const struct user_pass *up) { int retval = OPENVPN_PLUGIN_FUNC_ERROR; struct key_state *ks = &session->key[KS_PRIMARY]; /* primary key */ @@ -1014,7 +999,7 @@ verify_user_pass_plugin (struct tls_session *session, const struct user_pass *up if ((session->opt->ssl_flags & SSLF_AUTH_USER_PASS_OPTIONAL) || strlen (up->username)) { /* set username/password in private env space */ - setenv_str (session->opt->es, "username", raw_username); + setenv_str (session->opt->es, "username", up->username); setenv_str (session->opt->es, "password", up->password); /* setenv incoming cert common name for script */ @@ -1038,7 +1023,6 @@ verify_user_pass_plugin (struct tls_session *session, const struct user_pass *up #endif setenv_del (session->opt->es, "password"); - setenv_str (session->opt->es, "username", up->username); } else { @@ -1059,7 +1043,7 @@ verify_user_pass_plugin (struct tls_session *session, const struct user_pass *up #define KMDA_DEF 3 static int -verify_user_pass_management (struct tls_session *session, const struct user_pass *up, const char *raw_username) +verify_user_pass_management (struct tls_session *session, const struct user_pass *up) { int retval = KMDA_ERROR; struct key_state *ks = &session->key[KS_PRIMARY]; /* primary key */ @@ -1068,7 +1052,7 @@ verify_user_pass_management (struct tls_session *session, const struct user_pass if ((session->opt->ssl_flags & SSLF_AUTH_USER_PASS_OPTIONAL) || strlen (up->username)) { /* set username/password in private env space */ - setenv_str (session->opt->es, "username", raw_username); + setenv_str (session->opt->es, "username", up->username); setenv_str (session->opt->es, "password", up->password); /* setenv incoming cert common name for script */ @@ -1081,7 +1065,6 @@ verify_user_pass_management (struct tls_session *session, const struct user_pass management_notify_client_needing_auth (management, ks->mda_key_id, session->opt->mda_context, session->opt->es); setenv_del (session->opt->es, "password"); - setenv_str (session->opt->es, "username", up->username); retval = KMDA_SUCCESS; } @@ -1105,9 +1088,6 @@ verify_user_pass(struct user_pass *up, struct tls_multi *multi, bool s2 = true; struct key_state *ks = &session->key[KS_PRIMARY]; /* primary key */ - struct gc_arena gc = gc_new (); - char *raw_username; - #ifdef MANAGEMENT_DEF_AUTH int man_def_auth = KMDA_UNDEF; @@ -1115,22 +1095,17 @@ verify_user_pass(struct user_pass *up, struct tls_multi *multi, man_def_auth = KMDA_DEF; #endif - /* preserve raw username before string_mod remapping, for plugins */ - ALLOC_ARRAY_CLEAR_GC (raw_username, char, USER_PASS_LEN, &gc); - strcpy (raw_username, up->username); - string_mod (raw_username, CC_PRINT, CC_CRLF, '_'); - /* enforce character class restrictions in username/password */ - string_mod_sslname (up->username, COMMON_NAME_CHAR_CLASS, session->opt->ssl_flags); + string_mod (up->username, CC_PRINT, CC_CRLF, '_'); string_mod (up->password, CC_PRINT, CC_CRLF, '_'); /* call plugin(s) and/or script */ #ifdef MANAGEMENT_DEF_AUTH if (man_def_auth == KMDA_DEF) - man_def_auth = verify_user_pass_management (session, up, raw_username); + man_def_auth = verify_user_pass_management (session, up); #endif if (plugin_defined (session->opt->plugins, OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY)) - s1 = verify_user_pass_plugin (session, up, raw_username); + s1 = verify_user_pass_plugin (session, up); if (session->opt->auth_user_pass_verify_script) s2 = verify_user_pass_script (session, up); @@ -1179,8 +1154,6 @@ verify_user_pass(struct user_pass *up, struct tls_multi *multi, { msg (D_TLS_ERRORS, "TLS Auth Error: Auth Username/Password verification failed for peer"); } - - gc_free (&gc); } void diff --git a/ssl_verify_openssl.c b/ssl_verify_openssl.c index 13c2f4e..7a99469 100644 --- a/ssl_verify_openssl.c +++ b/ssl_verify_openssl.c @@ -248,14 +248,14 @@ x509_free_sha1_hash (unsigned char *hash) char * x509_get_subject (X509 *cert) { - return X509_NAME_oneline (X509_get_subject_name (cert), NULL, 0); + return openssl_get_subject (cert, NULL, 0); } void x509_free_subject (char *subject) { if (subject) - OPENSSL_free(subject); + free(subject); } -- 1.7.5.4