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