On 09/11/2019 16:13, Arne Schwabe wrote:
> This implements sending AUTH_PENDING and INFO_PRE messages to clients
> that indicate that the clients should be continue authentication with
> a second factor. This can currently be out of band (openurl) or a normal
> challenge/response 2FA like TOTP (CR_TEXT).

Can we settle on a single CR_TEXT vs CRTEXT terminology?  The 3/5 patch used
crtext in the documentation but cr_text in the commit message.

> Signed-off-by: Arne Schwabe <a...@rfc2549.org>
> ---
>  doc/management-notes.txt | 26 +++++++++++++++++++++++
>  src/openvpn/manage.c     | 46 ++++++++++++++++++++++++++++++++++++++++
>  src/openvpn/manage.h     |  3 +++
>  src/openvpn/multi.c      | 19 +++++++++++++++++
>  src/openvpn/push.c       | 24 +++++++++++++++++++++
>  src/openvpn/push.h       |  2 ++
>  6 files changed, 120 insertions(+)
> 
> diff --git a/doc/management-notes.txt b/doc/management-notes.txt
> index e380ca2b..4b405a9b 100644
> --- a/doc/management-notes.txt
> +++ b/doc/management-notes.txt
> @@ -592,6 +592,32 @@ interface to approve client connections.
>  CID,KID -- client ID and Key ID.  See documentation for ">CLIENT:"
>  notification for more info.
>  
> +COMMAND -- client-sso-auth  (OpenVPN 2.5 or higher)
> +----------------------------------------------------
> +
> +Instruct OpenVPN server to send AUTH_PENDING and INFO_PRE signal
> +a single sign on url to the client.
> +
> +    client-sso-auth {CID} {EXTRA}

I think we should use a different naming for this than 'sso'.  This is not
tied to only SSO (Single Sign-On).  What about:

 - client-extended-auth
 - client-external-auth
 - client-ext-auth
 - client-additional-auth
 - client-xauth

As long as the name is quite generic, I'm fine with most alternatives.  But it
should be very generic.  We have so many alternative auth methods these days:
Yubico OTP [1], TOTP/HOTP, FIDO/U2F, SAML, OAuth, Kerberos/GSSAPI, etc ...

[1] <https://developers.yubico.com/OTP/OTPs_Explained.html>

> +The server will send AUTH_PENDING and INFO_PRE,{EXTRA} to the client.
> +The client is expected to inform the user that authentication is pending and
> +display the extra information. For the SSO/SAML EXTRA would be OPEN_URL:url
> +and client should ask to the user to open the URL to continue.
> +
> +For the OpenVPN server this is stateless operation and needs to be
> +followed by a client-deny/client-auth[-nt] command (that is the result of the
> +out of band authentication).
> +
> +Before issuing a client-sso-auth to a client instead of a
> +client-auth/client-deny, the server should check the IV_SSO
> +environment variable if the method is support. The currently
> +defined method are crtext for challenge/response using text
> +(e.g. TOTP) and openurl for opening an URL in the client to
> +continue authentication. A client support both methods would set
> +
> +   setenv IV_SSO openurl,crtext
> +
>  COMMAND -- client-deny  (OpenVPN 2.1 or higher)
>  -----------------------------------------------
>  
> diff --git a/src/openvpn/manage.c b/src/openvpn/manage.c
> index 8dada6f2..c055f2ce 100644
> --- a/src/openvpn/manage.c
> +++ b/src/openvpn/manage.c
> @@ -105,6 +105,8 @@ man_help(void)
>      msg(M_CLIENT, "client-auth-nt CID KID : Authenticate client-id/key-id 
> CID/KID");
>      msg(M_CLIENT, "client-deny CID KID R [CR] : Deny auth client-id/key-id 
> CID/KID with log reason");
>      msg(M_CLIENT, "                             text R and optional client 
> reason text CR");
> +    msg(M_CLIENT, "client-sso-auth CID MSG : Instruct OpenVPN to send 
> AUTH_PENDING and INFO_PRE msg"
> +                  "                          to the client and wait for a 
> final client-auth/client-deny");
>      msg(M_CLIENT, "client-kill CID [M]    : Kill client instance CID with 
> message M (def=RESTART)");
>      msg(M_CLIENT, "env-filter [level]     : Set env-var filter level");
>  #ifdef MANAGEMENT_PF
> @@ -1001,6 +1003,43 @@ parse_kid(const char *str, unsigned int *kid)
>      }
>  }
>  
> +/**
> + * Will send a notification to the client that succesful authentication
> + * will require an additional step (web based SSO/2-factor auth/etc)
> + *
> + * @param man           The management interface struct
> + * @param cid_str       The CID in string form
> + * @param extra         The string to be send to the client containing
> + *                      the information of the additional steps
> + */
> +static void
> +man_client_sso_auth(struct management *man, const char *cid_str, const char 
> *extra)
> +{
> +    unsigned long cid = 0;
> +    if (parse_cid(cid_str, &cid))
> +    {
> +        if (man->persist.callback.client_sso)
> +        {
> +            bool ret = (*man->persist.callback.client_sso)
> +                           (man->persist.callback.arg, cid, extra);
> +
> +            if (ret)
> +            {
> +                msg(M_CLIENT, "SUCCESS: client-sso-auth command succeeded");
> +            }
> +            else
> +            {
> +                msg(M_CLIENT, "SUCCESS: client-sso-auth command failed."
> +                              " Extra paramter might be too long");
> +            }
> +        }
> +        else
> +        {
> +            msg(M_CLIENT, "ERROR: The client-sso-auth command is not 
> supported by the current daemon mode");
> +        }
> +    }
> +}
> +
>  static void
>  man_client_auth(struct management *man, const char *cid_str, const char 
> *kid_str, const bool extra)
>  {
> @@ -1541,6 +1580,13 @@ man_dispatch_command(struct management *man, struct 
> status_output *so, const cha
>              man_client_auth(man, p[1], p[2], true);
>          }
>      }
> +    else if (streq(p[0], "client-sso-auth"))
> +    {
> +        if (man_need(man, p, 2, 0))
> +        {
> +            man_client_sso_auth(man, p[1], p[2]);
> +        }
> +    }
>  #ifdef MANAGEMENT_PF
>      else if (streq(p[0], "client-pf"))
>      {
> diff --git a/src/openvpn/manage.h b/src/openvpn/manage.h
> index 8acc18bf..f570afc6 100644
> --- a/src/openvpn/manage.h
> +++ b/src/openvpn/manage.h
> @@ -174,6 +174,9 @@ struct management_callback
>                           const char *reason,
>                           const char *client_reason,
>                           struct buffer_list *cc_config); /* ownership 
> transferred */
> +    bool (*client_sso) (void *arg,
> +                        const unsigned long cid,
> +                        const char *url);
>      char *(*get_peer_info) (void *arg, const unsigned long cid);
>  #endif
>  #ifdef MANAGEMENT_PF
> diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c
> index d594dd25..692fb62a 100644
> --- a/src/openvpn/multi.c
> +++ b/src/openvpn/multi.c
> @@ -3288,6 +3288,24 @@ management_kill_by_cid(void *arg, const unsigned long 
> cid, const char *kill_msg)
>      }
>  }
>  
> +static bool
> +management_client_sso(void *arg,> +        const unsigned long cid,
> +        const char *extra)
> +{
> +    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_sso_messages(&mi->context, extra);
> +        multi_schedule_context_wakeup(m, mi);
> +        return ret;
> +    }
> +    return false;
> +}
> +
> +
>  static bool
>  management_client_auth(void *arg,
>                         const unsigned long cid,
> @@ -3395,6 +3413,7 @@ init_management_callback_multi(struct multi_context *m)
>  #ifdef MANAGEMENT_DEF_AUTH
>          cb.kill_by_cid = management_kill_by_cid;
>          cb.client_auth = management_client_auth;
> +        cb.client_sso = management_client_sso;
>          cb.get_peer_info = management_get_peer_info;
>  #endif
>  #ifdef MANAGEMENT_PF
> diff --git a/src/openvpn/push.c b/src/openvpn/push.c
> index ee1cb980..4b375ae3 100644
> --- a/src/openvpn/push.c
> +++ b/src/openvpn/push.c
> @@ -268,6 +268,30 @@ send_auth_failed(struct context *c, const char 
> *client_reason)
>      gc_free(&gc);
>  }
>  
> +bool
> +send_sso_messages(struct context *c, const char* extra)
> +{
> +    send_control_channel_string(c, "AUTH_PENDING", D_PUSH);
> +
> +    static const char info_pre[] = "INFO_PRE,";
> +
> +
> +    size_t len = strlen(extra)+1 + sizeof(info_pre);
> +    if (len > PUSH_BUNDLE_SIZE)
> +    {
> +        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);
> +
> +    gc_free(&gc);
> +    return true;
> +}
> +
>  /*
>   * Send restart message from server to client.
>   */
> diff --git a/src/openvpn/push.h b/src/openvpn/push.h
> index 9cf8fb34..f814f572 100644
> --- a/src/openvpn/push.h
> +++ b/src/openvpn/push.h
> @@ -70,6 +70,8 @@ void remove_iroutes_from_push_route_list(struct options *o);
>  
>  void send_auth_failed(struct context *c, const char *client_reason);
>  
> +bool send_sso_messages(struct context *c, const char *url);
> +
>  void send_restart(struct context *c, const char *kill_msg);


Several of the function names also uses 'sso', which should be aligned to a
non-sso specific function name.


-- 
kind regards,

David Sommerseth
OpenVPN Inc



_______________________________________________
Openvpn-devel mailing list
Openvpn-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/openvpn-devel

Reply via email to