[Openvpn-devel,v2,4/5] Implement sending SSO challenge to clients

Message ID 20191109151306.18597-4-arne@rfc2549.org
State Superseded
Headers show
Series
  • [Openvpn-devel,v2,1/5] Implement parsing and sending INFO and INFO_PRE control messages
Related show

Commit Message

Arne Schwabe Nov. 9, 2019, 3:13 p.m.
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).

Signed-off-by: Arne Schwabe <arne@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(+)

Comments

David Sommerseth March 27, 2020, 9:09 p.m. | #1
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).
> 
> Signed-off-by: Arne Schwabe <arne@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(+)

Code and management notes looks reasonable.  But again, it would be good to
have a way to test this properly to avoid regressions later on.  Since this is
also a more advanced authentication method, having good test methods is even
more critical.
Arne Schwabe April 15, 2020, 11:48 a.m. | #2
Am 27.03.20 um 22:09 schrieb David Sommerseth:
> 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).
>>
>> Signed-off-by: Arne Schwabe <arne@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(+)
> 
> Code and management notes looks reasonable.  But again, it would be good to
> have a way to test this properly to avoid regressions later on.  Since this is
> also a more advanced authentication method, having good test methods is even
> more critical.

Writing a complete framework to test management interface on the server
side is something that would a huge undertaking for this simple patch.

I think Access Server is basically the only software that I am aware of
that really uses the server side management interface.

So basically having this requirement right now of writing a testing
suite to get this into OpenVPN will mean that we will effectively fork
OpenVPN for AS.

Arne
David Sommerseth May 15, 2020, 3:36 p.m. | #3
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 <arne@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.
David Sommerseth May 15, 2020, 3:40 p.m. | #4
On 15/05/2020 17:36, David Sommerseth wrote:
> 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 <arne@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

Another alternative popped up in my head, as CR/Challenge-Response is used a
lot in this context .... client-cr-auth  .... but all of them are just
suggestions to avoid the 'sso' reference.

> 
> 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>
Jan Just Keijser May 15, 2020, 3:54 p.m. | #5
On 15/05/20 17:40, David Sommerseth wrote:
> On 15/05/2020 17:36, David Sommerseth wrote:
>> 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 <arne@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
> Another alternative popped up in my head, as CR/Challenge-Response is used a
> lot in this context .... client-cr-auth  .... but all of them are just
> suggestions to avoid the 'sso' reference.
>
>> 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>

+1 on avoiding the 'sso' reference  - this has nothing to do with 
SSO/SAML/OAuth/OpenID etc etc and I think it would be unwise to suggest 
that OpenVPN does something like "that" kind of SSO auth. Before we know 
it users will start asking how they can link their Hotmail, FB or Google 
account to their OpenVPN config....

JM2CW,

JJK

Patch

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}
+
+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);
 
 /**