rather than the common name from the client cert.
.\"*********************************************************
.TP
+.B \-\-compat\-names [no\-remapping]
+Until OpenVPN v2.3 the format of the X.509 Subject fields was formatted
+like this:
+.IP
+.B
+/C=US/L=Somewhere/CN=John Doe/emailAddress=john@example.com
+.IP
+In addition the old behavivour was to remap any character other than
+alphanumeric, underscore ('_'), dash ('-'), dot ('.'), and slash ('/') to
+underscore ('_'). The X.509 Subject string as returned by the
+.B tls_id
+environmental variable, could additionally contain colon (':') or equal ('=').
+.IP
+When using the
+.B \-\-compat\-names
+option, this old formatting and remapping will be re-enabled again. This is
+purely implemented for compatibility reasons when using older plug-ins or
+scripts which does not handle the new formatting or UTF-8 characters.
+.IP
+In OpenVPN v2.3 the formatting of these fields changed into a more
+standardised format. It now looks like:
+.IP
+.B
+C=US, L=Somewhere, CN=John Doe, emailAddress=john@example.com
+.IP
+The new default format in OpenVPN v2.3 also does not do the character remapping
+which happened earlier. This new format enables proper support for UTF\-8
+characters in the usernames, X.509 Subject fields and Common Name variables and
+it complies to the RFC 2253, UTF\-8 String Representation of Distinguished
+Names.
+
+As a backwards compatibility for the removed \-\-no\-name\-remapping feature in
+older OpenVPN versions, the
+.B no\-remapping
+mode flag can be used with the
+.B
+\-\-compat\-names
+option.
+When this mode flag is used, the Common Name, Subject, and username strings are
+allowed to include any printable character including space, but excluding
+control characters such as tab, newline, and carriage-return. It ensures
+compatibility with the
+.B \-\-no\-name\-remapping
+option of OpenVPN versions before v2.3.
+
+.B Please note:
+This option will not be around for a long time. It is only implemented
+to make the transition to the new formatting less intrusive. It will be
+removed either in OpenVPN v2.4 or v2.5. So please make sure you start
+the process to support the new formatting as soon as possible.
+.\"*********************************************************
+.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
/** Maximum length of common name */
#define TLS_USERNAME_LEN 64
+/** Legal characters in an X509 name with --compat-names */
+#define X509_NAME_CHAR_CLASS (CC_ALNUM|CC_UNDERBAR|CC_DASH|CC_DOT|CC_AT|CC_SLASH|CC_COLON|CC_EQUAL)
+
+/** Legal characters in a common name with --compat-names */
+#define COMMON_NAME_CHAR_CLASS (CC_ALNUM|CC_UNDERBAR|CC_DASH|CC_DOT|CC_AT|CC_SLASH)
+
+static void
+string_mod_remap_name (char *str, const unsigned int restrictive_flags)
+{
+ if (compat_flag (COMPAT_FLAG_QUERY | COMPAT_NAMES)
+ && !compat_flag (COMPAT_FLAG_QUERY | COMPAT_NO_NAME_REMAPPING))
+ string_mod (str, restrictive_flags, 0, '_');
+ else
+ string_mod (str, CC_PRINT, CC_CRLF, '_');
+}
+
/*
* Export the untrusted IP address and port to the environment
*/
}
/* enforce character class restrictions in X509 name */
- string_mod (subject, CC_PRINT, CC_CRLF, '_');
+ string_mod_remap_name (subject, X509_NAME_CHAR_CLASS);
string_replace_leading (subject, '-', '_');
/* extract the username (default is CN) */
}
/* enforce character class restrictions in common name */
- string_mod (common_name, CC_PRINT, CC_CRLF, '_');
+ string_mod_remap_name (common_name, COMMON_NAME_CHAR_CLASS);
/* warn if cert chain is too deep */
if (cert_depth >= MAX_CERT_DEPTH)
* Verify the username and password using a plugin
*/
static int
-verify_user_pass_plugin (struct tls_session *session, const struct user_pass *up)
+verify_user_pass_plugin (struct tls_session *session, const struct user_pass *up, const char *raw_username)
{
int retval = OPENVPN_PLUGIN_FUNC_ERROR;
struct key_state *ks = &session->key[KS_PRIMARY]; /* primary key */
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", up->username);
+ setenv_str (session->opt->es, "username", (raw_username ? raw_username : up->username));
setenv_str (session->opt->es, "password", up->password);
/* setenv incoming cert common name for script */
#endif
setenv_del (session->opt->es, "password");
+ if (raw_username)
+ setenv_str (session->opt->es, "username", up->username);
}
else
{
#define KMDA_DEF 3
static int
-verify_user_pass_management (struct tls_session *session, const struct user_pass *up)
+verify_user_pass_management (struct tls_session *session, const struct user_pass *up, const char *raw_username)
{
int retval = KMDA_ERROR;
struct key_state *ks = &session->key[KS_PRIMARY]; /* primary key */
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", up->username);
+ setenv_str (session->opt->es, "username", (raw_username ? raw_username : up->username));
setenv_str (session->opt->es, "password", up->password);
/* setenv incoming cert common name for script */
management_notify_client_needing_auth (management, ks->mda_key_id, session->opt->mda_context, session->opt->es);
setenv_del (session->opt->es, "password");
+ if (raw_username)
+ setenv_str (session->opt->es, "username", up->username);
retval = KMDA_SUCCESS;
}
bool s2 = true;
struct key_state *ks = &session->key[KS_PRIMARY]; /* primary key */
+ struct gc_arena gc = gc_new ();
+ char *raw_username = NULL;
+
#ifdef MANAGEMENT_DEF_AUTH
int man_def_auth = KMDA_UNDEF;
man_def_auth = KMDA_DEF;
#endif
+ /*
+ * Preserve the raw username before string_mod remapping, for plugins
+ * and management clients when in --compat-names mode
+ */
+ if (compat_flag (COMPAT_FLAG_QUERY | COMPAT_NAMES))
+ {
+ 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 (up->username, CC_PRINT, CC_CRLF, '_');
+ string_mod_remap_name (up->username, COMMON_NAME_CHAR_CLASS);
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);
+ man_def_auth = verify_user_pass_management (session, up, raw_username);
#endif
if (plugin_defined (session->opt->plugins, OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY))
- s1 = verify_user_pass_plugin (session, up);
+ s1 = verify_user_pass_plugin (session, up, raw_username);
if (session->opt->auth_user_pass_verify_script)
s2 = verify_user_pass_script (session, up);
{
msg (D_TLS_ERRORS, "TLS Auth Error: Auth Username/Password verification failed for peer");
}
+
+ gc_free (&gc);
}
void