mirror of
https://github.com/OpenVPN/openvpn.git
synced 2026-06-11 01:40:05 -04:00
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
This commit is contained in:
parent
598e03f0e7
commit
a8be73799b
5 changed files with 84 additions and 44 deletions
|
|
@ -2462,33 +2462,6 @@ management_notify_generic (struct management *man, const char *str)
|
|||
|
||||
#ifdef MANAGEMENT_DEF_AUTH
|
||||
|
||||
static bool
|
||||
validate_peer_info_line(const char *line)
|
||||
{
|
||||
uint8_t c;
|
||||
int state = 0;
|
||||
while ((c=*line++))
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
if (c == '=' && state == 1)
|
||||
state = 2;
|
||||
else if (isalnum(c) || c == '_')
|
||||
state = 1;
|
||||
else
|
||||
return false;
|
||||
case 2:
|
||||
if (isprint(c))
|
||||
;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return (state == 2);
|
||||
}
|
||||
|
||||
static void
|
||||
man_output_peer_info_env (struct management *man, struct man_def_auth_context *mdac)
|
||||
{
|
||||
|
|
@ -2527,7 +2500,8 @@ management_notify_client_needing_auth (struct management *management,
|
|||
mode = "REAUTH";
|
||||
msg (M_CLIENT, ">CLIENT:%s,%lu,%u", mode, mdac->cid, mda_key_id);
|
||||
man_output_extra_env (management, "CLIENT");
|
||||
man_output_peer_info_env(management, mdac);
|
||||
if (management->connection.env_filter_level>0)
|
||||
man_output_peer_info_env(management, mdac);
|
||||
man_output_env (es, true, management->connection.env_filter_level, "CLIENT");
|
||||
mdac->flags |= DAF_INITIAL_AUTH;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1562,6 +1562,58 @@ multi_client_connect_mda (struct multi_context *m,
|
|||
|
||||
#endif
|
||||
|
||||
/* helper to parse peer_info received from multi client, validate
|
||||
* (this is untrusted data) and put into environment
|
||||
*/
|
||||
bool
|
||||
validate_peer_info_line(char *line)
|
||||
{
|
||||
uint8_t c;
|
||||
int state = 0;
|
||||
while (*line)
|
||||
{
|
||||
c = *line;
|
||||
switch (state)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
if (c == '=' && state == 1)
|
||||
state = 2;
|
||||
else if (isalnum(c) || c == '_')
|
||||
state = 1;
|
||||
else
|
||||
return false;
|
||||
case 2:
|
||||
/* after the '=', replace non-printable or shell meta with '_' */
|
||||
if (!isprint(c) || isspace(c) ||
|
||||
c == '$' || c == '(' || c == '`' )
|
||||
*line = '_';
|
||||
}
|
||||
line++;
|
||||
}
|
||||
return (state == 2);
|
||||
}
|
||||
|
||||
void
|
||||
multi_output_peer_info_env (struct env_set *es, const char * peer_info)
|
||||
{
|
||||
char line[256];
|
||||
struct buffer buf;
|
||||
buf_set_read (&buf, (const uint8_t *) peer_info, strlen(peer_info));
|
||||
while (buf_parse (&buf, '\n', line, sizeof (line)))
|
||||
{
|
||||
chomp (line);
|
||||
if (validate_peer_info_line(line) &&
|
||||
(strncmp(line, "IV_", 3) == 0 || strncmp(line, "UV_", 3) == 0) )
|
||||
{
|
||||
msg (M_INFO, "peer info: %s", line);
|
||||
env_set_add(es, line);
|
||||
}
|
||||
else
|
||||
msg (M_WARN, "validation failed on peer_info line received from client");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
multi_client_connect_setenv (struct multi_context *m,
|
||||
struct multi_instance *mi)
|
||||
|
|
|
|||
|
|
@ -312,6 +312,9 @@ void multi_close_instance_on_signal (struct multi_context *m, struct multi_insta
|
|||
void init_management_callback_multi (struct multi_context *m);
|
||||
void uninit_management_callback_multi (struct multi_context *m);
|
||||
|
||||
bool validate_peer_info_line(char *line);
|
||||
void multi_output_peer_info_env (struct env_set *es, const char * peer_info);
|
||||
|
||||
/*
|
||||
* Return true if our output queue is not full
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1106,6 +1106,8 @@ tls_multi_free (struct tls_multi *multi, bool clear)
|
|||
#ifdef MANAGEMENT_DEF_AUTH
|
||||
man_def_auth_set_client_reason(multi, NULL);
|
||||
|
||||
#endif
|
||||
#ifdef P2MP_SERVER
|
||||
free (multi->peer_info);
|
||||
#endif
|
||||
|
||||
|
|
@ -1997,6 +1999,7 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessi
|
|||
|
||||
struct gc_arena gc = gc_new ();
|
||||
char *options;
|
||||
struct user_pass *up;
|
||||
|
||||
/* allocate temporary objects */
|
||||
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
|
|||
|
||||
ks->authenticated = false;
|
||||
|
||||
/* always extract username + password fields from buf, even if not
|
||||
* authenticating for it, because otherwise we can't get at the
|
||||
* peer_info data which follows behind
|
||||
*/
|
||||
ALLOC_OBJ_CLEAR_GC (up, struct user_pass, &gc);
|
||||
username_status = read_string (buf, up->username, USER_PASS_LEN);
|
||||
password_status = read_string (buf, up->password, USER_PASS_LEN);
|
||||
|
||||
#ifdef P2MP_SERVER
|
||||
/* get peer info from control channel */
|
||||
free (multi->peer_info);
|
||||
multi->peer_info = read_string_alloc (buf);
|
||||
if ( multi->peer_info )
|
||||
multi_output_peer_info_env (session->opt->es, multi->peer_info);
|
||||
#endif
|
||||
|
||||
if (verify_user_pass_enabled(session))
|
||||
{
|
||||
/* Perform username/password authentication */
|
||||
struct user_pass *up;
|
||||
|
||||
ALLOC_OBJ_CLEAR_GC (up, struct user_pass, &gc);
|
||||
username_status = read_string (buf, up->username, USER_PASS_LEN);
|
||||
password_status = read_string (buf, up->password, USER_PASS_LEN);
|
||||
|
||||
if (!username_status || !password_status)
|
||||
{
|
||||
CLEAR (*up);
|
||||
|
|
@ -2051,14 +2064,7 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessi
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef MANAGEMENT_DEF_AUTH
|
||||
/* get peer info from control channel */
|
||||
free (multi->peer_info);
|
||||
multi->peer_info = read_string_alloc (buf);
|
||||
#endif
|
||||
|
||||
verify_user_pass(up, multi, session);
|
||||
CLEAR (*up);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -2072,6 +2078,9 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessi
|
|||
ks->authenticated = true;
|
||||
}
|
||||
|
||||
/* clear username and password from memory */
|
||||
CLEAR (*up);
|
||||
|
||||
/* Perform final authentication checks */
|
||||
if (ks->authenticated)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -481,14 +481,16 @@ struct tls_multi
|
|||
*/
|
||||
char *client_reason;
|
||||
|
||||
/* Time of last call to tls_authentication_status */
|
||||
time_t tas_last;
|
||||
#endif
|
||||
|
||||
#ifdef P2MP_SERVER
|
||||
/*
|
||||
* A multi-line string of general-purpose info received from peer
|
||||
* over control channel.
|
||||
*/
|
||||
char *peer_info;
|
||||
|
||||
/* Time of last call to tls_authentication_status */
|
||||
time_t tas_last;
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
|
|
|||
Loading…
Reference in a new issue