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
... | ... |
@@ -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 |
/* |