Browse code

Add --compat-names option

With this option, users can basically undo the changes of the UTF-8
support commit 5e86fd93779482b90a191f929edebe414cd78a4f. It's here for
short term compatibility and should be removed again as soon as possible.

When OpenSSL is used, the subject strings will be in the proprietary
format again. Generally username, X.509 CN, and X.509 subject will again
be subject to '_' replacemant, unless the "no-remapping" flag is
also specified. That flag ensures compatibility with setups using the
--no-name-remapping option, that has been removed in 2.3.

[v2: More comments related to compat_flags() added by DS plus using
COMPAT_FLAG_QUERY expclit]
[v3: Improved the man page entry for --compat-names, after suggestions
from Bernhard R. Link]

Signed-off-by: Heiko Hund <heiko.hund@sophos.com>
Signed-off-by: David Sommerseth <davids@redhat.com>
Acked-by: Gert Doering <gert@greenie.muc.de>
Acked-by: David Sommerseth <davids@redhat.com>
Message-Id: 1347377664-15462-1-git-send-email-dazo@users.sourceforge.net
URL: http://article.gmane.org/gmane.network.openvpn.devel/7053

Heiko Hund authored on 2012/09/12 00:34:24
Showing 6 changed files
... ...
@@ -3403,6 +3403,58 @@ the authenticated username as the common name,
3403 3403
 rather than the common name from the client cert.
3404 3404
 .\"*********************************************************
3405 3405
 .TP
3406
+.B \-\-compat\-names [no\-remapping]
3407
+Until OpenVPN v2.3 the format of the X.509 Subject fields was formatted
3408
+like this:
3409
+.IP
3410
+.B
3411
+/C=US/L=Somewhere/CN=John Doe/emailAddress=john@example.com
3412
+.IP
3413
+In addition the old behavivour was to remap any character other than
3414
+alphanumeric, underscore ('_'), dash ('-'), dot ('.'), and slash ('/') to
3415
+underscore ('_').  The X.509 Subject string as returned by the
3416
+.B tls_id
3417
+environmental variable, could additionally contain colon (':') or equal ('=').
3418
+.IP
3419
+When using the
3420
+.B \-\-compat\-names
3421
+option, this old formatting and remapping will be re-enabled again.  This is
3422
+purely implemented for compatibility reasons when using older plug-ins or
3423
+scripts which does not handle the new formatting or UTF-8 characters.
3424
+.IP
3425
+In OpenVPN v2.3 the formatting of these fields changed into a more
3426
+standardised format.  It now looks like:
3427
+.IP
3428
+.B
3429
+C=US, L=Somewhere, CN=John Doe, emailAddress=john@example.com
3430
+.IP
3431
+The new default format in OpenVPN v2.3 also does not do the character remapping
3432
+which happened earlier.  This new format enables proper support for UTF\-8
3433
+characters in the usernames, X.509 Subject fields and Common Name variables and
3434
+it complies to the RFC 2253, UTF\-8 String Representation of Distinguished
3435
+Names.
3436
+
3437
+As a backwards compatibility for the removed \-\-no\-name\-remapping feature in
3438
+older OpenVPN versions, the
3439
+.B no\-remapping
3440
+mode flag can be used with the
3441
+.B
3442
+\-\-compat\-names
3443
+option.
3444
+When this mode flag is used, the Common Name, Subject, and username strings are
3445
+allowed to include any printable character including space, but excluding
3446
+control characters such as tab, newline, and carriage-return. It ensures
3447
+compatibility with the
3448
+.B \-\-no\-name\-remapping
3449
+option of OpenVPN versions before v2.3.
3450
+
3451
+.B Please note:
3452
+This option will not be around for a long time.  It is only implemented
3453
+to make the transition to the new formatting less intrusive.  It will be
3454
+removed either in OpenVPN v2.4 or v2.5.  So please make sure you start
3455
+the process to support the new formatting as soon as possible.
3456
+.\"*********************************************************
3457
+.TP
3406 3458
 .B \-\-port-share host port [dir]
3407 3459
 When run in TCP server mode, share the OpenVPN port with
3408 3460
 another application, such as an HTTPS server.  If OpenVPN
... ...
@@ -2120,3 +2120,24 @@ sanitize_control_message(const char *src, struct gc_arena *gc)
2120 2120
   *dest = '\0';
2121 2121
   return ret;
2122 2122
 }
2123
+
2124
+/**
2125
+ * Will set or query for a global compat flag.  To modify the compat flags
2126
+ * the COMPAT_FLAG_SET must be bitwise ORed together with the flag to set.
2127
+ * If no "operator" flag is given it defaults to COMPAT_FLAG_QUERY,
2128
+ * which returns the flag state.
2129
+ *
2130
+ * @param  flag  Flag to be set/queried for bitwise ORed with the operator flag
2131
+ * @return Returns 0 if the flag is not set, otherwise the 'flag' value is returned
2132
+ */
2133
+bool
2134
+compat_flag (unsigned int flag)
2135
+{
2136
+  static unsigned int compat_flags = 0;
2137
+
2138
+  if (flag & COMPAT_FLAG_SET)
2139
+    compat_flags |= (flag >> 1);
2140
+
2141
+  return (compat_flags & (flag >> 1));
2142
+
2143
+}
... ...
@@ -368,4 +368,10 @@ void argv_printf_cat (struct argv *a, const char *format, ...)
368 368
 #endif
369 369
   ;
370 370
 
371
+#define COMPAT_FLAG_QUERY         0       /** compat_flags operator: Query for a flag */
372
+#define COMPAT_FLAG_SET           (1<<0)  /** compat_flags operator: Set a compat flag */
373
+#define COMPAT_NAMES              (1<<1)  /** compat flag: --compat-names set */
374
+#define COMPAT_NO_NAME_REMAPPING  (1<<2)  /** compat flag: --compat-names without char remapping */
375
+bool compat_flag (unsigned int flag);
376
+
371 377
 #endif
... ...
@@ -2130,6 +2130,9 @@ options_postprocess_verify_ce (const struct options *options, const struct conne
2130 2130
 
2131 2131
       if (options->stale_routes_check_interval)
2132 2132
         msg (M_USAGE, "--stale-routes-check requires --mode server");
2133
+
2134
+      if (compat_flag (COMPAT_FLAG_QUERY | COMPAT_NO_NAME_REMAPPING))
2135
+        msg (M_USAGE, "--compat-x509-names no-remapping requires --mode server");
2133 2136
     }
2134 2137
 #endif /* P2MP_SERVER */
2135 2138
 
... ...
@@ -5548,6 +5551,13 @@ add_option (struct options *options,
5548 5548
       VERIFY_PERMISSION (OPT_P_GENERAL);
5549 5549
       options->ssl_flags |= SSLF_AUTH_USER_PASS_OPTIONAL;
5550 5550
     }
5551
+  else if (streq (p[0], "compat-names"))
5552
+    {
5553
+      VERIFY_PERMISSION (OPT_P_GENERAL);
5554
+      compat_flag (COMPAT_FLAG_SET | COMPAT_NAMES);
5555
+      if (p[1] && streq (p[1], "no-remapping"))
5556
+        compat_flag (COMPAT_FLAG_SET | COMPAT_NO_NAME_REMAPPING);
5557
+    }
5551 5558
   else if (streq (p[0], "opt-verify"))
5552 5559
     {
5553 5560
       VERIFY_PERMISSION (OPT_P_GENERAL);
... ...
@@ -49,6 +49,22 @@
49 49
 /** Maximum length of common name */
50 50
 #define TLS_USERNAME_LEN 64
51 51
 
52
+/** Legal characters in an X509 name with --compat-names */
53
+#define X509_NAME_CHAR_CLASS   (CC_ALNUM|CC_UNDERBAR|CC_DASH|CC_DOT|CC_AT|CC_SLASH|CC_COLON|CC_EQUAL)
54
+
55
+/** Legal characters in a common name with --compat-names */
56
+#define COMMON_NAME_CHAR_CLASS (CC_ALNUM|CC_UNDERBAR|CC_DASH|CC_DOT|CC_AT|CC_SLASH)
57
+
58
+static void
59
+string_mod_remap_name (char *str, const unsigned int restrictive_flags)
60
+{
61
+  if (compat_flag (COMPAT_FLAG_QUERY | COMPAT_NAMES)
62
+      && !compat_flag (COMPAT_FLAG_QUERY | COMPAT_NO_NAME_REMAPPING))
63
+    string_mod (str, restrictive_flags, 0, '_');
64
+  else
65
+    string_mod (str, CC_PRINT, CC_CRLF, '_');
66
+}
67
+
52 68
 /*
53 69
  * Export the untrusted IP address and port to the environment
54 70
  */
... ...
@@ -591,7 +607,7 @@ verify_cert(struct tls_session *session, openvpn_x509_cert_t *cert, int cert_dep
591 591
     }
592 592
 
593 593
   /* enforce character class restrictions in X509 name */
594
-  string_mod (subject, CC_PRINT, CC_CRLF, '_');
594
+  string_mod_remap_name (subject, X509_NAME_CHAR_CLASS);
595 595
   string_replace_leading (subject, '-', '_');
596 596
 
597 597
   /* extract the username (default is CN) */
... ...
@@ -611,7 +627,7 @@ verify_cert(struct tls_session *session, openvpn_x509_cert_t *cert, int cert_dep
611 611
     }
612 612
 
613 613
   /* enforce character class restrictions in common name */
614
-  string_mod (common_name, CC_PRINT, CC_CRLF, '_');
614
+  string_mod_remap_name (common_name, COMMON_NAME_CHAR_CLASS);
615 615
 
616 616
   /* warn if cert chain is too deep */
617 617
   if (cert_depth >= MAX_CERT_DEPTH)
... ...
@@ -1003,7 +1019,7 @@ verify_user_pass_script (struct tls_session *session, const struct user_pass *up
1003 1003
  * Verify the username and password using a plugin
1004 1004
  */
1005 1005
 static int
1006
-verify_user_pass_plugin (struct tls_session *session, const struct user_pass *up)
1006
+verify_user_pass_plugin (struct tls_session *session, const struct user_pass *up, const char *raw_username)
1007 1007
 {
1008 1008
   int retval = OPENVPN_PLUGIN_FUNC_ERROR;
1009 1009
   struct key_state *ks = &session->key[KS_PRIMARY]; 	   /* primary key */
... ...
@@ -1012,7 +1028,7 @@ verify_user_pass_plugin (struct tls_session *session, const struct user_pass *up
1012 1012
   if ((session->opt->ssl_flags & SSLF_AUTH_USER_PASS_OPTIONAL) || strlen (up->username))
1013 1013
     {
1014 1014
       /* set username/password in private env space */
1015
-      setenv_str (session->opt->es, "username", up->username);
1015
+      setenv_str (session->opt->es, "username", (raw_username ? raw_username : up->username));
1016 1016
       setenv_str (session->opt->es, "password", up->password);
1017 1017
 
1018 1018
       /* setenv incoming cert common name for script */
... ...
@@ -1036,6 +1052,8 @@ verify_user_pass_plugin (struct tls_session *session, const struct user_pass *up
1036 1036
 #endif
1037 1037
 
1038 1038
       setenv_del (session->opt->es, "password");
1039
+      if (raw_username)
1040
+        setenv_str (session->opt->es, "username", up->username);
1039 1041
     }
1040 1042
   else
1041 1043
     {
... ...
@@ -1056,7 +1074,7 @@ verify_user_pass_plugin (struct tls_session *session, const struct user_pass *up
1056 1056
 #define KMDA_DEF     3
1057 1057
 
1058 1058
 static int
1059
-verify_user_pass_management (struct tls_session *session, const struct user_pass *up)
1059
+verify_user_pass_management (struct tls_session *session, const struct user_pass *up, const char *raw_username)
1060 1060
 {
1061 1061
   int retval = KMDA_ERROR;
1062 1062
   struct key_state *ks = &session->key[KS_PRIMARY]; 	   /* primary key */
... ...
@@ -1065,7 +1083,7 @@ verify_user_pass_management (struct tls_session *session, const struct user_pass
1065 1065
   if ((session->opt->ssl_flags & SSLF_AUTH_USER_PASS_OPTIONAL) || strlen (up->username))
1066 1066
     {
1067 1067
       /* set username/password in private env space */
1068
-      setenv_str (session->opt->es, "username", up->username);
1068
+      setenv_str (session->opt->es, "username", (raw_username ? raw_username : up->username));
1069 1069
       setenv_str (session->opt->es, "password", up->password);
1070 1070
 
1071 1071
       /* setenv incoming cert common name for script */
... ...
@@ -1078,6 +1096,8 @@ verify_user_pass_management (struct tls_session *session, const struct user_pass
1078 1078
 	management_notify_client_needing_auth (management, ks->mda_key_id, session->opt->mda_context, session->opt->es);
1079 1079
 
1080 1080
       setenv_del (session->opt->es, "password");
1081
+      if (raw_username)
1082
+        setenv_str (session->opt->es, "username", up->username);
1081 1083
 
1082 1084
       retval = KMDA_SUCCESS;
1083 1085
     }
... ...
@@ -1101,6 +1121,9 @@ verify_user_pass(struct user_pass *up, struct tls_multi *multi,
1101 1101
   bool s2 = true;
1102 1102
   struct key_state *ks = &session->key[KS_PRIMARY]; 	   /* primary key */
1103 1103
 
1104
+  struct gc_arena gc = gc_new ();
1105
+  char *raw_username = NULL;
1106
+
1104 1107
 #ifdef MANAGEMENT_DEF_AUTH
1105 1108
   int man_def_auth = KMDA_UNDEF;
1106 1109
 
... ...
@@ -1108,17 +1131,28 @@ verify_user_pass(struct user_pass *up, struct tls_multi *multi,
1108 1108
     man_def_auth = KMDA_DEF;
1109 1109
 #endif
1110 1110
 
1111
+  /*
1112
+   * Preserve the raw username before string_mod remapping, for plugins
1113
+   * and management clients when in --compat-names mode
1114
+   */
1115
+  if (compat_flag (COMPAT_FLAG_QUERY | COMPAT_NAMES))
1116
+    {
1117
+      ALLOC_ARRAY_CLEAR_GC (raw_username, char, USER_PASS_LEN, &gc);
1118
+      strcpy (raw_username, up->username);
1119
+      string_mod (raw_username, CC_PRINT, CC_CRLF, '_');
1120
+    }
1121
+
1111 1122
   /* enforce character class restrictions in username/password */
1112
-  string_mod (up->username, CC_PRINT, CC_CRLF, '_');
1123
+  string_mod_remap_name (up->username, COMMON_NAME_CHAR_CLASS);
1113 1124
   string_mod (up->password, CC_PRINT, CC_CRLF, '_');
1114 1125
 
1115 1126
   /* call plugin(s) and/or script */
1116 1127
 #ifdef MANAGEMENT_DEF_AUTH
1117 1128
   if (man_def_auth == KMDA_DEF)
1118
-    man_def_auth = verify_user_pass_management (session, up);
1129
+    man_def_auth = verify_user_pass_management (session, up, raw_username);
1119 1130
 #endif
1120 1131
   if (plugin_defined (session->opt->plugins, OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY))
1121
-    s1 = verify_user_pass_plugin (session, up);
1132
+    s1 = verify_user_pass_plugin (session, up, raw_username);
1122 1133
   if (session->opt->auth_user_pass_verify_script)
1123 1134
     s2 = verify_user_pass_script (session, up);
1124 1135
 
... ...
@@ -1167,6 +1201,8 @@ verify_user_pass(struct user_pass *up, struct tls_multi *multi,
1167 1167
     {
1168 1168
       msg (D_TLS_ERRORS, "TLS Auth Error: Auth Username/Password verification failed for peer");
1169 1169
     }
1170
+
1171
+  gc_free (&gc);
1170 1172
 }
1171 1173
 
1172 1174
 void
... ...
@@ -254,6 +254,18 @@ x509_get_subject (X509 *cert, struct gc_arena *gc)
254 254
   char *subject = NULL;
255 255
   int maxlen = 0;
256 256
 
257
+  /*
258
+   * Generate the subject string in OpenSSL proprietary format,
259
+   * when in --compat-names mode
260
+   */
261
+  if (compat_flag (COMPAT_FLAG_QUERY | COMPAT_NAMES))
262
+    {
263
+      subject = gc_malloc (256, false, gc);
264
+      X509_NAME_oneline (X509_get_subject_name (cert), subject, 256);
265
+      subject[255] = '\0';
266
+      return subject;
267
+    }
268
+
257 269
   subject_bio = BIO_new (BIO_s_mem ());
258 270
   if (subject_bio == NULL)
259 271
     goto err;