Implement server side of AUTH_PENDING with extending timeout

Patch V2: eliminate parse_kid function, fix style
Patch V3: adding missing parameter in function, this was added
          by a later patch in the original series

Signed-off-by: Arne Schwabe <arne@rfc2549.org>
Acked-by: Lev Stipakov <lstipakov@gmail.com>
Message-Id: <20210303123818.16012-1-arne@rfc2549.org>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg21596.html
Signed-off-by: Gert Doering <gert@greenie.muc.de>
This commit is contained in:
Arne Schwabe 2021-03-03 13:38:18 +01:00 committed by Gert Doering
parent 4cf01c8e43
commit 53229047a2
7 changed files with 84 additions and 40 deletions

View file

@ -975,15 +975,15 @@ 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;
}
}
@ -998,15 +998,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)
{
@ -1032,7 +1035,7 @@ man_client_auth(struct management *man, const char *cid_str, const char *kid_str
mc->in_extra_cid = 0;
mc->in_extra_kid = 0;
if (parse_cid(cid_str, &mc->in_extra_cid)
&& parse_kid(kid_str, &mc->in_extra_kid))
&& parse_uint(kid_str, "KID", &mc->in_extra_kid))
{
mc->in_extra_cmd = IEC_CLIENT_AUTH;
in_extra_reset(mc, IER_NEW);
@ -1048,7 +1051,7 @@ man_client_deny(struct management *man, const char *cid_str, const char *kid_str
{
unsigned long cid = 0;
unsigned int kid = 0;
if (parse_cid(cid_str, &cid) && parse_kid(kid_str, &kid))
if (parse_cid(cid_str, &cid) && parse_uint(kid_str, "KID", &kid))
{
if (man->persist.callback.client_auth)
{
@ -1563,9 +1566,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

View file

@ -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,

View file

@ -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
@ -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;
}

View file

@ -361,26 +361,57 @@ 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 tls_multi *tls_multi = c->c2.tls_multi;
struct key_state *ks = &tls_multi->session[TM_ACTIVE].key[KS_PRIMARY];
static const char info_pre[] = "INFO_PRE,";
const char *const peer_info = tls_multi->peer_info;
unsigned int proto = extract_iv_proto(peer_info);
size_t len = strlen(extra)+1 + sizeof(info_pre);
/* 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;
}
@ -1028,4 +1059,20 @@ 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 */

View file

@ -77,7 +77,9 @@ void send_auth_failed(struct context *c, const char *client_reason);
* doc/management-notes.txt under client-pending-auth for
* more details on message format
*/
bool send_auth_pending_messages(struct context *c, const char *extra);
bool
send_auth_pending_messages(struct context *c, const char *extra,
unsigned int timeout);
void send_restart(struct context *c, const char *kill_msg);
@ -89,6 +91,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
*

View file

@ -2659,6 +2659,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);

View file

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