Skip to content

Commit

Permalink
Implement server side of AUTH_PENDING with extending timeout
Browse files Browse the repository at this point in the history
Signed-off-by: Arne Schwabe <arne@rfc2549.org>
  • Loading branch information
schwabe committed Jan 15, 2021
1 parent 3901e46 commit 42ae41d
Show file tree
Hide file tree
Showing 7 changed files with 87 additions and 36 deletions.
26 changes: 18 additions & 8 deletions src/openvpn/manage.c
Expand Up @@ -972,19 +972,26 @@ parse_cid(const char *str, unsigned long *cid)
}

static bool
parse_kid(const char *str, unsigned int *kid)
parse_uint(const char *str, const char* what, unsigned int *uint)
{
if (sscanf(str, "%u", kid) == 1)
if (sscanf(str, "%u", uint) == 1)
{
return true;
}
else
{
msg(M_CLIENT, "ERROR: cannot parse KID");
msg(M_CLIENT, "ERROR: cannot parse %s", what);
return false;
}
}

static bool
parse_kid(const char *str, unsigned int *kid)
{
return parse_uint(str, "KID", kid);
}


/**
* Will send a notification to the client that succesful authentication
* will require an additional step (web based SSO/2-factor auth/etc)
Expand All @@ -995,15 +1002,18 @@ parse_kid(const char *str, unsigned int *kid)
* the information of the additional steps
*/
static void
man_client_pending_auth(struct management *man, const char *cid_str, const char *extra)
man_client_pending_auth(struct management *man, const char *cid_str,
const char *extra, const char *timeout_str)
{
unsigned long cid = 0;
if (parse_cid(cid_str, &cid))
unsigned int timeout = 0;
if (parse_cid(cid_str, &cid)
&& parse_uint(timeout_str, "TIMEOUT", &timeout))
{
if (man->persist.callback.client_pending_auth)
{
bool ret = (*man->persist.callback.client_pending_auth)
(man->persist.callback.arg, cid, extra);
(man->persist.callback.arg, cid, extra, timeout);

if (ret)
{
Expand Down Expand Up @@ -1560,9 +1570,9 @@ man_dispatch_command(struct management *man, struct status_output *so, const cha
}
else if (streq(p[0], "client-pending-auth"))
{
if (man_need(man, p, 2, 0))
if (man_need(man, p, 3, 0))
{
man_client_pending_auth(man, p[1], p[2]);
man_client_pending_auth(man, p[1], p[2], p[3]);
}
}
#ifdef MANAGEMENT_PF
Expand Down
3 changes: 2 additions & 1 deletion src/openvpn/manage.h
Expand Up @@ -173,7 +173,8 @@ struct management_callback
struct buffer_list *cc_config); /* ownership transferred */
bool (*client_pending_auth) (void *arg,
const unsigned long cid,
const char *url);
const char *extra,
unsigned int timeout);
char *(*get_peer_info) (void *arg, const unsigned long cid);
#ifdef MANAGEMENT_PF
bool (*client_pf)(void *arg,
Expand Down
27 changes: 3 additions & 24 deletions src/openvpn/multi.c
Expand Up @@ -1768,28 +1768,6 @@ multi_client_connect_setenv(struct multi_context *m,
gc_free(&gc);
}

/**
* Extracts the IV_PROTO variable and returns its value or 0
* if it cannot be extracted.
*
*/
static unsigned int
extract_iv_proto(const char *peer_info)
{

const char *optstr = peer_info ? strstr(peer_info, "IV_PROTO=") : NULL;
if (optstr)
{
int proto = 0;
int r = sscanf(optstr, "IV_PROTO=%d", &proto);
if (r == 1 && proto > 0)
{
return proto;
}
}
return 0;
}

/**
* Calculates the options that depend on the client capabilities
* based on local options and available peer info
Expand Down Expand Up @@ -3918,14 +3896,15 @@ management_kill_by_cid(void *arg, const unsigned long cid, const char *kill_msg)
static bool
management_client_pending_auth(void *arg,
const unsigned long cid,
const char *extra)
const char *extra,
unsigned int timeout)
{
struct multi_context *m = (struct multi_context *) arg;
struct multi_instance *mi = lookup_by_cid(m, cid);
if (mi)
{
/* sends INFO_PRE and AUTH_PENDING messages to client */
bool ret = send_auth_pending_messages(&mi->context, extra);
bool ret = send_auth_pending_messages(&mi->context, extra, timeout);
multi_schedule_context_wakeup(m, mi);
return ret;
}
Expand Down
55 changes: 52 additions & 3 deletions src/openvpn/push.c
Expand Up @@ -347,26 +347,58 @@ send_auth_failed(struct context *c, const char *client_reason)
gc_free(&gc);
}


bool
send_auth_pending_messages(struct context *c, const char *extra)
send_auth_pending_messages(struct context *c, const char *extra,
unsigned int timeout)
{
send_control_channel_string(c, "AUTH_PENDING", D_PUSH);
struct key_state *ks = &tls_multi->session[TM_ACTIVE].key[KS_PRIMARY];

static const char info_pre[] = "INFO_PRE,";

struct tls_multi *tls_multi = c->c2.tls_multi;
const char *const peer_info = tls_multi->peer_info;
unsigned int proto = extract_iv_proto(peer_info);


/* Calculate the maximum timeout and subtract the time we already waited */
unsigned int max_timeout = max_uint(tls_multi->opt.renegotiate_seconds/2,
tls_multi->opt.handshake_window);
max_timeout = max_timeout - (now - ks->initial);
timeout = min_uint(max_timeout, timeout);

struct gc_arena gc = gc_new();
if ((proto & IV_PROTO_AUTH_PENDING_KW) == 0)
{
send_control_channel_string(c, "AUTH_PENDING", D_PUSH);
}
else
{
static const char auth_pre[] = "AUTH_PENDING,timeout ";
// Assume a worst case of 8 byte uint64 in decimal which
// needs 20 bytes
size_t len = 20 + 1 + sizeof(auth_pre);
struct buffer buf = alloc_buf_gc(len, &gc);
buf_printf(&buf, auth_pre);
buf_printf(&buf, "%u", timeout);
send_control_channel_string(c, BSTR(&buf), D_PUSH);
}


size_t len = strlen(extra)+1 + sizeof(info_pre);
if (len > PUSH_BUNDLE_SIZE)
{
gc_free(&gc);
return false;
}
struct gc_arena gc = gc_new();

struct buffer buf = alloc_buf_gc(len, &gc);
buf_printf(&buf, info_pre);
buf_printf(&buf, "%s", extra);
send_control_channel_string(c, BSTR(&buf), D_PUSH);

ks->auth_deferred_expire = now + timeout;

gc_free(&gc);
return true;
}
Expand Down Expand Up @@ -1014,4 +1046,21 @@ remove_iroutes_from_push_route_list(struct options *o)
}
}

unsigned int
extract_iv_proto(const char *peer_info)
{

const char *optstr = peer_info ? strstr(peer_info, "IV_PROTO=") : NULL;
if (optstr)
{
int proto = 0;
int r = sscanf(optstr, "IV_PROTO=%d", &proto);
if (r == 1 && proto > 0)
{
return proto;
}
}
return 0;
}

#endif /* if P2MP */
10 changes: 10 additions & 0 deletions src/openvpn/push.h
Expand Up @@ -89,6 +89,16 @@ void send_restart(struct context *c, const char *kill_msg);
*/
void send_push_reply_auth_token(struct tls_multi *multi);


/**
* Extracts the IV_PROTO variable and returns its value or 0
* if it cannot be extracted.
*
* @param peer_info peer info string to search for IV_PROTO
*/
unsigned int
extract_iv_proto(const char *peer_info);

/**
* Parses an AUTH_PENDING message and if in pull mode extends the timeout
*
Expand Down
1 change: 1 addition & 0 deletions src/openvpn/ssl.c
Expand Up @@ -2654,6 +2654,7 @@ tls_process(struct tls_multi *multi,
buf = reliable_get_buf_output_sequenced(ks->send_reliable);
if (buf)
{
ks->initial = now;
ks->must_negotiate = now + session->opt->handshake_window;
ks->auth_deferred_expire = now + auth_deferred_expire_window(session->opt);

Expand Down
1 change: 1 addition & 0 deletions src/openvpn/ssl_common.h
Expand Up @@ -175,6 +175,7 @@ struct key_state

struct key_state_ssl ks_ssl; /* contains SSL object and BIOs for the control channel */

time_t initial; /* when we created this session */
time_t established; /* when our state went S_ACTIVE */
time_t must_negotiate; /* key negotiation times out if not finished before this time */
time_t must_die; /* this object is destroyed at this time */
Expand Down

0 comments on commit 42ae41d

Please sign in to comment.