Browse code

Allow pending auth to be send from a auth plugin

Patch v2: removed change that slipped into this patch and belongs
into the next

Signed-off-by: Arne Schwabe <arne@rfc2549.org>
Acked-by: David Sommerseth <davids@openvpn.net>
Message-Id: <20210125125628.30364-9-arne@rfc2549.org>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg21489.html
Signed-off-by: Gert Doering <gert@greenie.muc.de>

Arne Schwabe authored on 2021/01/25 21:56:25
Showing 6 changed files
... ...
@@ -409,7 +409,8 @@ which mode OpenVPN is configured as.
409 409
 
410 410
   * :code:`OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY` plug-in hooks returns
411 411
     success/failure via :code:`auth_control_file` when using deferred auth
412
-    method
412
+    method and pending authentification via :code:`pending_auth_file`.
413
+
413 414
 
414 415
   * :code:`OPENVPN_PLUGIN_ENABLE_PF` plugin hook to pass filtering rules
415 416
     via ``pf_file``
... ...
@@ -567,6 +567,14 @@ OPENVPN_PLUGIN_DEF openvpn_plugin_handle_t OPENVPN_PLUGIN_FUNC(openvpn_plugin_op
567 567
  * auth_control_file/client_connect_deferred_file
568 568
  * in the environmental variable list (envp).
569 569
  *
570
+ * Additionally the auth_pending_file can be written, which causes the openvpn
571
+ * server to send a pending auth request to the client. See doc/management.txt
572
+ * for more details on this authentication mechanism. The format of the
573
+ * auth_pending_file is
574
+ * line 1: timeout in seconds
575
+ * line 2: Pending auth method the client needs to support (e.g. openurl)
576
+ * line 3: EXTRA (e.g. OPEN_URL:http://www.example.com)
577
+ *
570 578
  * In addition the OPENVPN_PLUGIN_CLIENT_CONNECT_DEFER and
571 579
  * OPENVPN_PLUGIN_CLIENT_CONNECT_DEFER_V2 are called when OpenVPN tries to
572 580
  * get the deferred result. For a V2 call implementing this function is
... ...
@@ -993,7 +993,7 @@ key_state_free(struct key_state *ks, bool clear)
993 993
     packet_id_free(&ks->crypto_options.packet_id);
994 994
 
995 995
 #ifdef PLUGIN_DEF_AUTH
996
-    key_state_rm_auth_control_file(ks);
996
+    key_state_rm_auth_control_files(ks);
997 997
 #endif
998 998
 
999 999
     if (clear)
... ...
@@ -215,6 +215,7 @@ struct key_state
215 215
     unsigned int auth_control_status;
216 216
     time_t acf_last_mod;
217 217
     char *auth_control_file;
218
+    char *auth_pending_file;
218 219
 };
219 220
 
220 221
 /** Control channel wrapping (--tls-auth/--tls-crypt) context */
... ...
@@ -865,13 +865,129 @@ man_def_auth_test(const struct key_state *ks)
865 865
 }
866 866
 #endif /* ifdef ENABLE_MANAGEMENT */
867 867
 
868
+/**
869
+ *  Removes auth_pending file from the file system
870
+ *  and key_state structure
871
+ */
872
+static void
873
+key_state_rm_auth_pending_file(struct key_state *ks)
874
+{
875
+    if (ks && ks->auth_pending_file)
876
+    {
877
+        platform_unlink(ks->auth_pending_file);
878
+        free(ks->auth_pending_file);
879
+        ks->auth_pending_file = NULL;
880
+    }
881
+}
868 882
 
869
-/*
870
- * auth_control_file functions
883
+/**
884
+ * Check peer_info if the client supports the requested pending auth method
885
+ */
886
+static bool
887
+check_auth_pending_method(const char *peer_info, const char *method)
888
+{
889
+    struct gc_arena gc = gc_new();
890
+
891
+    char *iv_sso = extract_var_peer_info(peer_info, "IV_SSO=", &gc);
892
+    if (!iv_sso)
893
+    {
894
+        gc_free(&gc);
895
+        return false;
896
+    }
897
+
898
+    const char *client_method = strtok(iv_sso, ",");
899
+    bool supported = false;
900
+
901
+    while (client_method)
902
+    {
903
+        if (0 == strcmp(client_method, method))
904
+        {
905
+            supported = true;
906
+            break;
907
+        }
908
+        client_method = strtok(NULL, ":");
909
+    }
910
+
911
+    gc_free(&gc);
912
+    return supported;
913
+}
914
+
915
+/**
916
+ *  Checks if the deferred state should also send auth pending
917
+ *  request to the client. Also removes the auth_pending control file
918
+ *
919
+ *  @returns true   if file was either processed sucessfully or did not
920
+ *                  exist at all
921
+ *  @returns false  The file had an invlaid format or another error occured
871 922
  */
923
+static bool
924
+key_state_check_auth_pending_file(struct key_state *ks,
925
+                                  struct tls_multi *multi)
926
+{
927
+    bool ret = true;
928
+    if (ks && ks->auth_pending_file)
929
+    {
930
+        struct buffer_list *lines = buffer_list_file(ks->auth_pending_file,
931
+                                                     1024);
932
+        if (lines && lines->head)
933
+        {
934
+            /* Must have at least three lines. further lines are ignored for
935
+             * forward compatibility */
936
+            if (!lines->head || !lines->head->next || !lines->head->next->next)
937
+            {
938
+                msg(M_WARN, "auth pending control file is not at least "
939
+                            "three lines long.");
940
+                buffer_list_free(lines);
941
+                return false;
942
+            }
943
+            struct buffer *timeout_buf = &lines->head->buf;
944
+            struct buffer *iv_buf = &lines->head->next->buf;
945
+            struct buffer *extra_buf = &lines->head->next->next->buf;
946
+
947
+            /* Remove newline chars at the end of the lines */
948
+            buf_chomp(timeout_buf);
949
+            buf_chomp(iv_buf);
950
+            buf_chomp(extra_buf);
951
+
952
+            long timeout = strtol(BSTR(timeout_buf), NULL, 10);
953
+            if (timeout == 0)
954
+            {
955
+                msg(M_WARN, "could not parse auth pending file timeout");
956
+                buffer_list_free(lines);
957
+                return false;
958
+            }
959
+
960
+            const char* pending_method = BSTR(iv_buf);
961
+            if (!check_auth_pending_method(multi->peer_info, pending_method))
962
+            {
963
+                char buf[128];
964
+                openvpn_snprintf(buf, sizeof(buf),
965
+                                 "Authentication failed, required pending auth "
966
+                                 "method '%s' not supported", pending_method);
967
+                auth_set_client_reason(multi, buf);
968
+                msg(M_INFO, "Client does not supported auth pending method "
969
+                            "'%s'", pending_method);
970
+                ret = false;
971
+            }
972
+            else
973
+            {
974
+                send_auth_pending_messages(multi, BSTR(extra_buf), timeout);
975
+            }
976
+        }
977
+
978
+        buffer_list_free(lines);
979
+    }
980
+    key_state_rm_auth_pending_file(ks);
981
+    return ret;
982
+}
872 983
 
984
+
985
+/**
986
+ *  Removes auth_pending and auth_control files from file system
987
+ *  and key_state structure
988
+ */
873 989
 void
874
-key_state_rm_auth_control_file(struct key_state *ks)
990
+key_state_rm_auth_control_files(struct key_state *ks)
875 991
 {
876 992
     if (ks && ks->auth_control_file)
877 993
     {
... ...
@@ -879,23 +995,34 @@ key_state_rm_auth_control_file(struct key_state *ks)
879 879
         free(ks->auth_control_file);
880 880
         ks->auth_control_file = NULL;
881 881
     }
882
+    key_state_rm_auth_pending_file(ks);
882 883
 }
883 884
 
885
+/**
886
+ * Generates and creates the control files used for deferred authentification
887
+ * in the temporary directory.
888
+ *
889
+ * @return  true if file creation was successful
890
+ */
884 891
 static bool
885
-key_state_gen_auth_control_file(struct key_state *ks, const struct tls_options *opt)
892
+key_state_gen_auth_control_files(struct key_state *ks, const struct tls_options *opt)
886 893
 {
887 894
     struct gc_arena gc = gc_new();
888 895
 
889
-    key_state_rm_auth_control_file(ks);
896
+    key_state_rm_auth_control_files(ks);
890 897
     const char *acf = platform_create_temp_file(opt->tmp_dir, "acf", &gc);
891
-    if (acf)
898
+    const char *apf = platform_create_temp_file(opt->tmp_dir, "apf", &gc);
899
+
900
+    if (acf && apf)
892 901
     {
893 902
         ks->auth_control_file = string_alloc(acf, NULL);
903
+        ks->auth_pending_file = string_alloc(apf, NULL);
894 904
         setenv_str(opt->es, "auth_control_file", ks->auth_control_file);
905
+        setenv_str(opt->es, "auth_pending_file", ks->auth_pending_file);
895 906
     }
896 907
 
897 908
     gc_free(&gc);
898
-    return acf;
909
+    return (acf && apf);
899 910
 }
900 911
 
901 912
 static unsigned int
... ...
@@ -1140,7 +1267,7 @@ verify_user_pass_plugin(struct tls_session *session, struct tls_multi *multi,
1140 1140
     setenv_str(session->opt->es, "password", up->password);
1141 1141
 
1142 1142
     /* generate filename for deferred auth control file */
1143
-    if (!key_state_gen_auth_control_file(ks, session->opt))
1143
+    if (!key_state_gen_auth_control_files(ks, session->opt))
1144 1144
     {
1145 1145
         msg(D_TLS_ERRORS, "TLS Auth Error (%s): "
1146 1146
             "could not create deferred auth control file", __func__);
... ...
@@ -1150,10 +1277,20 @@ verify_user_pass_plugin(struct tls_session *session, struct tls_multi *multi,
1150 1150
     /* call command */
1151 1151
     retval = plugin_call(session->opt->plugins, OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY, NULL, NULL, session->opt->es);
1152 1152
 
1153
-    /* purge auth control filename (and file itself) for non-deferred returns */
1154
-    if (retval != OPENVPN_PLUGIN_FUNC_DEFERRED)
1153
+    if (retval == OPENVPN_PLUGIN_FUNC_DEFERRED)
1154
+    {
1155
+        /* Check if the plugin has written the pending auth control
1156
+         * file and send the pending auth to the client */
1157
+        if(!key_state_check_auth_pending_file(ks, multi))
1158
+        {
1159
+            retval = OPENVPN_PLUGIN_FUNC_ERROR;
1160
+            key_state_rm_auth_control_files(ks);
1161
+        }
1162
+    }
1163
+    else
1155 1164
     {
1156
-        key_state_rm_auth_control_file(ks);
1165
+        /* purge auth control filename (and file itself) for non-deferred returns */
1166
+        key_state_rm_auth_control_files(ks);
1157 1167
     }
1158 1168
 
1159 1169
     setenv_del(session->opt->es, "password");
... ...
@@ -1224,7 +1361,7 @@ set_verify_user_pass_env(struct user_pass *up, struct tls_multi *multi,
1224 1224
     }
1225 1225
 }
1226 1226
 
1227
-/*
1227
+/**
1228 1228
  * Main username/password verification entry point
1229 1229
  *
1230 1230
  * Will set session->ks[KS_PRIMARY].authenticated according to
... ...
@@ -117,7 +117,7 @@ tls_authentication_status(struct tls_multi *multi, const int latency);
117 117
  *
118 118
  * @param ks    The key state the remove the file for
119 119
  */
120
-void key_state_rm_auth_control_file(struct key_state *ks);
120
+void key_state_rm_auth_control_files(struct key_state *ks);
121 121
 
122 122
 /**
123 123
  * Frees the given set of certificate hashes.