Browse code

Make push-peer-info visible in "normal" per-instance environment.

Without this patch, peer-info pushed by clients in the TLS handshake
is only visible on the management interface, and only if
--management-client-auth is enabled.

With this patch, received records are sanitized and put into the normal
"multi instance" environment, where it can be evaluated by --client-connect
or --auth-user-pass-verify scripts and plugins, etc. Only records matching
a fairly strict "name=value" format are accepted, and only names starting
with IV_ or UV_ are exported, to avoid clients sending funny stuff and
playing havoc with script/plugin environments on the server. In the
"value" part, spaces, non-printable characters and shell metacharacters
are replaced by '_'.

The change is somewhat invasive as reception of the peer_info string was
only done when username+password are expected from the client, but the
data is always there (if the client sends no username/password, it will
send 0-length strings, so always extracting 3 strings is safe). Also,
the sanitation function validate_peer_info_line() and the opts->peer_info
field were only compiled in #ifdef MANGEMENT_DEF_AUTH...

Patch v3: do not call the old man_output_peer_info_env() anymore, unless
a management env-filter has been set (= ensure IV_ and UV_ stuff is sent
at most *once*, and exactly the way OpenVPN AS expects it). Add
substituting of "bad" characters in the environment values.

Signed-off-by: Gert Doering <gert@greenie.muc.de>
Acked-by: Arne Schwabe <arne@rfc2549.org>
Message-Id: <1367757373-31637-1-git-send-email-gert@greenie.muc.de>
URL: http://article.gmane.org/gmane.network.openvpn.devel/7582

Gert Doering authored on 2013/05/05 21:36:13
Showing 5 changed files
... ...
@@ -2462,33 +2462,6 @@ management_notify_generic (struct management *man, const char *str)
2462 2462
 
2463 2463
 #ifdef MANAGEMENT_DEF_AUTH
2464 2464
 
2465
-static bool
2466
-validate_peer_info_line(const char *line)
2467
-{
2468
-  uint8_t c;
2469
-  int state = 0;
2470
-  while ((c=*line++))
2471
-    {
2472
-      switch (state)
2473
-	{
2474
-	case 0:
2475
-	case 1:
2476
-	  if (c == '=' && state == 1)
2477
-	    state = 2;
2478
-	  else if (isalnum(c) || c == '_')
2479
-	    state = 1;
2480
-	  else
2481
-	    return false;
2482
-	case 2:
2483
-	  if (isprint(c))
2484
-	    ;
2485
-	  else
2486
-	    return false;
2487
-	}
2488
-    }
2489
-  return (state == 2);
2490
-}
2491
-
2492 2465
 static void
2493 2466
 man_output_peer_info_env (struct management *man, struct man_def_auth_context *mdac)
2494 2467
 {
... ...
@@ -2527,7 +2500,8 @@ management_notify_client_needing_auth (struct management *management,
2527 2527
 	mode = "REAUTH";
2528 2528
       msg (M_CLIENT, ">CLIENT:%s,%lu,%u", mode, mdac->cid, mda_key_id);
2529 2529
       man_output_extra_env (management, "CLIENT");
2530
-      man_output_peer_info_env(management, mdac);
2530
+      if (management->connection.env_filter_level>0)
2531
+        man_output_peer_info_env(management, mdac);
2531 2532
       man_output_env (es, true, management->connection.env_filter_level, "CLIENT");
2532 2533
       mdac->flags |= DAF_INITIAL_AUTH;
2533 2534
     }
... ...
@@ -1562,6 +1562,58 @@ multi_client_connect_mda (struct multi_context *m,
1562 1562
 
1563 1563
 #endif
1564 1564
 
1565
+/* helper to parse peer_info received from multi client, validate
1566
+ * (this is untrusted data) and put into environment
1567
+ */
1568
+bool
1569
+validate_peer_info_line(char *line)
1570
+{
1571
+  uint8_t c;
1572
+  int state = 0;
1573
+  while (*line)
1574
+    {
1575
+      c = *line;
1576
+      switch (state)
1577
+	{
1578
+	case 0:
1579
+	case 1:
1580
+	  if (c == '=' && state == 1)
1581
+	    state = 2;
1582
+	  else if (isalnum(c) || c == '_')
1583
+	    state = 1;
1584
+	  else
1585
+	    return false;
1586
+	case 2:
1587
+	  /* after the '=', replace non-printable or shell meta with '_' */
1588
+	  if (!isprint(c) || isspace(c) ||
1589
+	       c == '$' || c == '(' || c == '`' )
1590
+	    *line = '_';
1591
+	}
1592
+      line++;
1593
+    }
1594
+  return (state == 2);
1595
+}
1596
+
1597
+void
1598
+multi_output_peer_info_env (struct env_set *es, const char * peer_info)
1599
+{
1600
+  char line[256];
1601
+  struct buffer buf;
1602
+  buf_set_read (&buf, (const uint8_t *) peer_info, strlen(peer_info));
1603
+  while (buf_parse (&buf, '\n', line, sizeof (line)))
1604
+    {
1605
+      chomp (line);
1606
+      if (validate_peer_info_line(line) &&
1607
+            (strncmp(line, "IV_", 3) == 0 || strncmp(line, "UV_", 3) == 0) )
1608
+	{
1609
+	  msg (M_INFO, "peer info: %s", line);
1610
+	  env_set_add(es, line);
1611
+	}
1612
+      else
1613
+	msg (M_WARN, "validation failed on peer_info line received from client");
1614
+    }
1615
+}
1616
+
1565 1617
 static void
1566 1618
 multi_client_connect_setenv (struct multi_context *m,
1567 1619
 			     struct multi_instance *mi)
... ...
@@ -312,6 +312,9 @@ void multi_close_instance_on_signal (struct multi_context *m, struct multi_insta
312 312
 void init_management_callback_multi (struct multi_context *m);
313 313
 void uninit_management_callback_multi (struct multi_context *m);
314 314
 
315
+bool validate_peer_info_line(char *line);
316
+void multi_output_peer_info_env (struct env_set *es, const char * peer_info);
317
+
315 318
 /*
316 319
  * Return true if our output queue is not full
317 320
  */
... ...
@@ -1106,6 +1106,8 @@ tls_multi_free (struct tls_multi *multi, bool clear)
1106 1106
 #ifdef MANAGEMENT_DEF_AUTH
1107 1107
   man_def_auth_set_client_reason(multi, NULL);  
1108 1108
 
1109
+#endif
1110
+#ifdef P2MP_SERVER
1109 1111
   free (multi->peer_info);
1110 1112
 #endif
1111 1113
 
... ...
@@ -1997,6 +1999,7 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessi
1997 1997
 
1998 1998
   struct gc_arena gc = gc_new ();
1999 1999
   char *options;
2000
+  struct user_pass *up;
2000 2001
 
2001 2002
   /* allocate temporary objects */
2002 2003
   ALLOC_ARRAY_CLEAR_GC (options, char, TLS_OPTIONS_LEN, &gc);
... ...
@@ -2032,15 +2035,25 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessi
2032 2032
 
2033 2033
   ks->authenticated = false;
2034 2034
 
2035
+  /* always extract username + password fields from buf, even if not
2036
+   * authenticating for it, because otherwise we can't get at the
2037
+   * peer_info data which follows behind
2038
+   */
2039
+  ALLOC_OBJ_CLEAR_GC (up, struct user_pass, &gc);
2040
+  username_status = read_string (buf, up->username, USER_PASS_LEN);
2041
+  password_status = read_string (buf, up->password, USER_PASS_LEN);
2042
+
2043
+#ifdef P2MP_SERVER
2044
+  /* get peer info from control channel */
2045
+  free (multi->peer_info);
2046
+  multi->peer_info = read_string_alloc (buf);
2047
+  if ( multi->peer_info )
2048
+      multi_output_peer_info_env (session->opt->es, multi->peer_info);
2049
+#endif
2050
+
2035 2051
   if (verify_user_pass_enabled(session))
2036 2052
     {
2037 2053
       /* Perform username/password authentication */
2038
-      struct user_pass *up;
2039
-
2040
-      ALLOC_OBJ_CLEAR_GC (up, struct user_pass, &gc);
2041
-      username_status = read_string (buf, up->username, USER_PASS_LEN);
2042
-      password_status = read_string (buf, up->password, USER_PASS_LEN);
2043
-
2044 2054
       if (!username_status || !password_status)
2045 2055
 	{
2046 2056
 	  CLEAR (*up);
... ...
@@ -2051,14 +2064,7 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessi
2051 2051
 	    }
2052 2052
 	}
2053 2053
 
2054
-#ifdef MANAGEMENT_DEF_AUTH
2055
-      /* get peer info from control channel */
2056
-      free (multi->peer_info);
2057
-      multi->peer_info = read_string_alloc (buf);
2058
-#endif
2059
-
2060 2054
       verify_user_pass(up, multi, session);
2061
-      CLEAR (*up);
2062 2055
     }
2063 2056
   else
2064 2057
     {
... ...
@@ -2072,6 +2078,9 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessi
2072 2072
       ks->authenticated = true;
2073 2073
     }
2074 2074
 
2075
+  /* clear username and password from memory */
2076
+  CLEAR (*up);
2077
+
2075 2078
   /* Perform final authentication checks */
2076 2079
   if (ks->authenticated)
2077 2080
     {
... ...
@@ -481,14 +481,16 @@ struct tls_multi
481 481
    */
482 482
   char *client_reason;
483 483
 
484
+  /* Time of last call to tls_authentication_status */
485
+  time_t tas_last;
486
+#endif
487
+
488
+#ifdef P2MP_SERVER
484 489
   /*
485 490
    * A multi-line string of general-purpose info received from peer
486 491
    * over control channel.
487 492
    */
488 493
   char *peer_info;
489
-
490
-  /* Time of last call to tls_authentication_status */
491
-  time_t tas_last;
492 494
 #endif
493 495
 
494 496
   /*