From 021bddd22c3bba4a82037bd79aaf72b5a95098bf Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Thu, 19 Feb 2015 17:04:24 +0000 Subject: [PATCH] Start making HTTP authentication less proxy-specific Signed-off-by: David Woodhouse --- digest.c | 13 +++++------ gssapi.c | 22 ++++++++++--------- http.c | 40 ++++++++++++++++++---------------- ntlm.c | 49 ++++++++++++++++++++++++------------------ openconnect-internal.h | 8 +++---- 5 files changed, 73 insertions(+), 59 deletions(-) diff --git a/digest.c b/digest.c index a568aa5a..69c0661a 100644 --- a/digest.c +++ b/digest.c @@ -78,7 +78,8 @@ static void buf_append_md5(struct oc_text_buf *buf, void *data, int len) buf_append(buf, "%02x", md5[i]); } -int digest_authorization(struct openconnect_info *vpninfo, struct oc_text_buf *hdrbuf) +int digest_authorization(struct openconnect_info *vpninfo, struct http_auth_state *auth_state, + struct oc_text_buf *hdrbuf) { char *chall; int ret = -EINVAL; @@ -93,15 +94,15 @@ int digest_authorization(struct openconnect_info *vpninfo, struct oc_text_buf *h if (!vpninfo->proxy_user || !vpninfo->proxy_pass) return -EINVAL; - if (vpninfo->auth[AUTH_TYPE_DIGEST].state < AUTH_AVAILABLE) + if (auth_state->state < AUTH_AVAILABLE) return -EINVAL; - if (vpninfo->auth[AUTH_TYPE_DIGEST].state == AUTH_IN_PROGRESS) { - vpninfo->auth[AUTH_TYPE_DIGEST].state = AUTH_FAILED; + if (auth_state->state == AUTH_IN_PROGRESS) { + auth_state->state = AUTH_FAILED; return -EAGAIN; } - chall = vpninfo->auth[AUTH_TYPE_DIGEST].challenge; + chall = auth_state->challenge; if (!chall) return -EINVAL; @@ -237,7 +238,7 @@ int digest_authorization(struct openconnect_info *vpninfo, struct oc_text_buf *h ret = 0; - vpninfo->auth[AUTH_TYPE_DIGEST].state = AUTH_IN_PROGRESS; + auth_state->state = AUTH_IN_PROGRESS; vpn_progress(vpninfo, PRG_INFO, _("Attempting Digest authentication to proxy\n")); err: diff --git a/gssapi.c b/gssapi.c index 72729701..b0dfff70 100644 --- a/gssapi.c +++ b/gssapi.c @@ -80,25 +80,27 @@ static int gssapi_setup(struct openconnect_info *vpninfo, const char *service) #define GSSAPI_CONTINUE 2 #define GSSAPI_COMPLETE 3 -int gssapi_authorization(struct openconnect_info *vpninfo, struct oc_text_buf *hdrbuf) +int gssapi_authorization(struct openconnect_info *vpninfo, + struct http_auth_state *auth_state, + struct oc_text_buf *hdrbuf) { OM_uint32 major, minor; gss_buffer_desc in = GSS_C_EMPTY_BUFFER; gss_buffer_desc out = GSS_C_EMPTY_BUFFER; gss_OID mech = GSS_C_NO_OID; - if (vpninfo->auth[AUTH_TYPE_GSSAPI].state == AUTH_AVAILABLE && gssapi_setup(vpninfo, "HTTP")) { - vpninfo->auth[AUTH_TYPE_GSSAPI].state = AUTH_FAILED; + if (auth_state->state == AUTH_AVAILABLE && gssapi_setup(vpninfo, "HTTP")) { + auth_state->state = AUTH_FAILED; return -EIO; } - if (vpninfo->auth[AUTH_TYPE_GSSAPI].challenge && *vpninfo->auth[AUTH_TYPE_GSSAPI].challenge) { + if (auth_state->challenge && *auth_state->challenge) { int len = -EINVAL; - in.value = openconnect_base64_decode(&len, vpninfo->auth[AUTH_TYPE_GSSAPI].challenge); + in.value = openconnect_base64_decode(&len, auth_state->challenge); if (!in.value) return len; in.length = len; - } else if (vpninfo->auth[AUTH_TYPE_GSSAPI].state > AUTH_AVAILABLE) { + } else if (auth_state->state > AUTH_AVAILABLE) { /* This indicates failure. We were trying, but got an empty 'Proxy-Authorization: Negotiate' header back from the server implying that we should start again... */ @@ -113,15 +115,15 @@ int gssapi_authorization(struct openconnect_info *vpninfo, struct oc_text_buf *h free(in.value); if (major == GSS_S_COMPLETE) - vpninfo->auth[AUTH_TYPE_GSSAPI].state = GSSAPI_COMPLETE; + auth_state->state = GSSAPI_COMPLETE; else if (major == GSS_S_CONTINUE_NEEDED) - vpninfo->auth[AUTH_TYPE_GSSAPI].state = GSSAPI_CONTINUE; + auth_state->state = GSSAPI_CONTINUE; else { vpn_progress(vpninfo, PRG_ERR, _("Error generating GSSAPI response:\n")); print_gss_err(vpninfo, "gss_init_sec_context()", mech, major, minor); fail_gssapi: - vpninfo->auth[AUTH_TYPE_GSSAPI].state = AUTH_FAILED; + auth_state->state = AUTH_FAILED; cleanup_gssapi_auth(vpninfo); /* If we were *trying*, then -EAGAIN. Else -ENOENT to let another auth method try without having to reconnect first. */ @@ -132,7 +134,7 @@ int gssapi_authorization(struct openconnect_info *vpninfo, struct oc_text_buf *h buf_append(hdrbuf, "\r\n"); gss_release_buffer(&minor, &out); - if (!vpninfo->auth[AUTH_TYPE_GSSAPI].challenge) + if (!auth_state->challenge) vpn_progress(vpninfo, PRG_INFO, _("Attempting GSSAPI authentication to proxy\n")); return 0; diff --git a/http.c b/http.c index a8b43b93..922efaac 100644 --- a/http.c +++ b/http.c @@ -1118,11 +1118,11 @@ static int process_socks_proxy(struct openconnect_info *vpninfo) buf[2 + nr_auth_methods++] = SOCKS_AUTH_NONE; #if defined(HAVE_GSSAPI) || defined(_WIN32) - if (vpninfo->auth[AUTH_TYPE_GSSAPI].state != AUTH_DISABLED && + if (vpninfo->proxy_auth[AUTH_TYPE_GSSAPI].state != AUTH_DISABLED && !vpninfo->proxy_user && !vpninfo->proxy_pass) buf[2 + nr_auth_methods++] = SOCKS_AUTH_GSSAPI; #endif - if (vpninfo->auth[AUTH_TYPE_BASIC].state != AUTH_DISABLED && + if (vpninfo->proxy_auth[AUTH_TYPE_BASIC].state != AUTH_DISABLED && vpninfo->proxy_user && vpninfo->proxy_pass) buf[2 + nr_auth_methods++] = SOCKS_AUTH_PASSWORD; @@ -1376,7 +1376,9 @@ void buf_append_base64(struct oc_text_buf *buf, const void *bytes, int len) buf->data[buf->pos] = 0; } -static int basic_authorization(struct openconnect_info *vpninfo, struct oc_text_buf *hdrbuf) +static int basic_authorization(struct openconnect_info *vpninfo, + struct http_auth_state *auth_state, + struct oc_text_buf *hdrbuf) { struct oc_text_buf *text; @@ -1386,12 +1388,12 @@ static int basic_authorization(struct openconnect_info *vpninfo, struct oc_text_ if (!vpninfo->authmethods_set) { vpn_progress(vpninfo, PRG_ERR, _("Proxy requested Basic authentication which is disabled by default\n")); - vpninfo->auth[AUTH_TYPE_BASIC].state = AUTH_FAILED; + auth_state->state = AUTH_FAILED; return -EINVAL; } - if (vpninfo->auth[AUTH_TYPE_BASIC].state == AUTH_IN_PROGRESS) { - vpninfo->auth[AUTH_TYPE_BASIC].state = AUTH_FAILED; + if (auth_state->state == AUTH_IN_PROGRESS) { + auth_state->state = AUTH_FAILED; return -EAGAIN; } @@ -1408,17 +1410,19 @@ static int basic_authorization(struct openconnect_info *vpninfo, struct oc_text_ buf_free(text); vpn_progress(vpninfo, PRG_INFO, _("Attempting HTTP Basic authentication to proxy\n")); - vpninfo->auth[AUTH_TYPE_BASIC].state = AUTH_IN_PROGRESS; + auth_state->state = AUTH_IN_PROGRESS; return 0; } #if !defined(HAVE_GSSAPI) && !defined(_WIN32) -static int no_gssapi_authorization(struct openconnect_info *vpninfo, struct oc_text_buf *hdrbuf) +static int no_gssapi_authorization(struct openconnect_info *vpninfo, + struct http_auth_State *auth_state, + struct oc_text_buf *hdrbuf) { /* This comes last so just complain. We're about to bail. */ vpn_progress(vpninfo, PRG_ERR, _("This version of OpenConnect was built without GSSAPI support\n")); - vpninfo->auth[AUTH_TYPE_GSSAPI].state = AUTH_FAILED; + auth_state->state = AUTH_FAILED; return -ENOENT; } #endif @@ -1426,7 +1430,7 @@ static int no_gssapi_authorization(struct openconnect_info *vpninfo, struct oc_t struct auth_method { int state_index; const char *name; - int (*authorization)(struct openconnect_info *, struct oc_text_buf *); + int (*authorization)(struct openconnect_info *, struct http_auth_state *, struct oc_text_buf *); void (*cleanup)(struct openconnect_info *); } auth_methods[] = { #if defined(HAVE_GSSAPI) || defined(_WIN32) @@ -1447,9 +1451,9 @@ static int proxy_authorization(struct openconnect_info *vpninfo, struct oc_text_ int i; for (i = 0; i < sizeof(auth_methods) / sizeof(auth_methods[0]); i++) { - int index = auth_methods[i].state_index; - if (vpninfo->auth[index].state > AUTH_UNSEEN) { - ret = auth_methods[i].authorization(vpninfo, buf); + struct http_auth_state *auth_state = &vpninfo->proxy_auth[auth_methods[i].state_index]; + if (auth_state->state > AUTH_UNSEEN) { + ret = auth_methods[i].authorization(vpninfo, auth_state, buf); if (ret == -EAGAIN || !ret) return ret; } @@ -1460,9 +1464,9 @@ static int proxy_authorization(struct openconnect_info *vpninfo, struct oc_text_ /* Returns non-zero if it matched */ static int handle_auth_proto(struct openconnect_info *vpninfo, - struct auth_method *method, char *hdr) + struct auth_method *method, char *hdr) { - struct http_auth_state *auth = &vpninfo->auth[method->state_index]; + struct http_auth_state *auth = &vpninfo->proxy_auth[method->state_index]; int l = strlen(method->name); if (auth->state <= AUTH_FAILED) @@ -1511,7 +1515,7 @@ static int proxy_hdrs(struct openconnect_info *vpninfo, char *hdr, char *val) static void clear_auth_state(struct openconnect_info *vpninfo, struct auth_method *method, int reset) { - struct http_auth_state *auth = &vpninfo->auth[method->state_index]; + struct http_auth_state *auth = &vpninfo->proxy_auth[method->state_index]; /* The 'reset' argument is set when we're connected successfully, to fully reset the state to allow another connection to start @@ -1642,7 +1646,7 @@ int openconnect_set_proxy_auth(struct openconnect_info *vpninfo, const char *met const char *p; for (i = 0; i < sizeof(auth_methods) / sizeof(auth_methods[0]); i++) - vpninfo->auth[auth_methods[i].state_index].state = AUTH_DISABLED; + vpninfo->proxy_auth[auth_methods[i].state_index].state = AUTH_DISABLED; while (methods) { p = strchr(methods, ','); @@ -1656,7 +1660,7 @@ int openconnect_set_proxy_auth(struct openconnect_info *vpninfo, const char *met if (strprefix_match(methods, len, auth_methods[i].name) || (auth_methods[i].state_index == AUTH_TYPE_GSSAPI && strprefix_match(methods, len, "gssapi"))) { - vpninfo->auth[auth_methods[i].state_index].state = AUTH_UNSEEN; + vpninfo->proxy_auth[auth_methods[i].state_index].state = AUTH_UNSEEN; break; } } diff --git a/ntlm.c b/ntlm.c index f81a2572..0bdfdab5 100644 --- a/ntlm.c +++ b/ntlm.c @@ -118,9 +118,11 @@ static int ntlm_helper_spawn(struct openconnect_info *vpninfo, struct oc_text_bu return ret; } -static int ntlm_helper_challenge(struct openconnect_info *vpninfo, struct oc_text_buf *buf) +static int ntlm_helper_challenge(struct openconnect_info *vpninfo, + struct http_auth_state *auth_state, + struct oc_text_buf *buf) { - return ntlm_sspi(vpninfo, buf, vpninfo->auth[AUTH_TYPE_NTLM].challenge); + return ntlm_sspi(vpninfo, buf, auth_state->challenge); } void cleanup_ntlm_auth(struct openconnect_info *vpninfo) @@ -221,15 +223,17 @@ static int ntlm_helper_spawn(struct openconnect_info *vpninfo, struct oc_text_bu return 0; } -static int ntlm_helper_challenge(struct openconnect_info *vpninfo, struct oc_text_buf *buf) +static int ntlm_helper_challenge(struct openconnect_info *vpninfo, + struct http_auth_state *auth_state, + struct oc_text_buf *buf) { char helperbuf[4096]; int len; - if (!vpninfo->auth[AUTH_TYPE_NTLM].challenge || + if (!auth_state->challenge || write(vpninfo->ntlm_helper_fd, "TT ", 3) != 3 || - write(vpninfo->ntlm_helper_fd, vpninfo->auth[AUTH_TYPE_NTLM].challenge, - strlen(vpninfo->auth[AUTH_TYPE_NTLM].challenge)) != strlen(vpninfo->auth[AUTH_TYPE_NTLM].challenge) || + write(vpninfo->ntlm_helper_fd, auth_state->challenge, + strlen(auth_state->challenge)) != strlen(auth_state->challenge) || write(vpninfo->ntlm_helper_fd, "\n", 1) != 1) { err: vpn_progress(vpninfo, PRG_ERR, _("Error communicating with ntlm_auth helper\n")); @@ -841,7 +845,9 @@ static void ntlm_set_string_binary(struct oc_text_buf *buf, int offset, buf_append_bytes(buf, data, len); } -static int ntlm_manual_challenge(struct openconnect_info *vpninfo, struct oc_text_buf *hdrbuf) +static int ntlm_manual_challenge(struct openconnect_info *vpninfo, + struct http_auth_state *auth_state, + struct oc_text_buf *hdrbuf) { struct oc_text_buf *resp; char *user; @@ -850,14 +856,14 @@ static int ntlm_manual_challenge(struct openconnect_info *vpninfo, struct oc_tex int token_len = -EINVAL; int ntlmver; - if (!vpninfo->auth[AUTH_TYPE_NTLM].challenge) + if (!auth_state->challenge) return -EINVAL; if (ntlm_nt_hash (vpninfo->proxy_pass, (char *) hash)) return -EINVAL; token = openconnect_base64_decode(&token_len, - vpninfo->auth[AUTH_TYPE_NTLM].challenge); + auth_state->challenge); if (!token) return token_len; @@ -956,22 +962,23 @@ static int ntlm_manual_challenge(struct openconnect_info *vpninfo, struct oc_tex return 0; } -int ntlm_authorization(struct openconnect_info *vpninfo, struct oc_text_buf *buf) +int ntlm_authorization(struct openconnect_info *vpninfo, + struct http_auth_state *auth_state, struct oc_text_buf *buf) { - if (vpninfo->auth[AUTH_TYPE_NTLM].state == AUTH_AVAILABLE) { - vpninfo->auth[AUTH_TYPE_NTLM].state = NTLM_MANUAL; + if (auth_state->state == AUTH_AVAILABLE) { + auth_state->state = NTLM_MANUAL; /* Don't attempt automatic NTLM auth if we were given a password */ if (!vpninfo->proxy_pass && !ntlm_helper_spawn(vpninfo, buf)) { - vpninfo->auth[AUTH_TYPE_NTLM].state = NTLM_SSO_REQ; + auth_state->state = NTLM_SSO_REQ; return 0; } } - if (vpninfo->auth[AUTH_TYPE_NTLM].state == NTLM_SSO_REQ) { + if (auth_state->state == NTLM_SSO_REQ) { int ret; - ret = ntlm_helper_challenge(vpninfo, buf); + ret = ntlm_helper_challenge(vpninfo, auth_state, buf); /* Clean up after it. We're done here, whether it worked or not */ cleanup_ntlm_auth(vpninfo); - vpninfo->auth[AUTH_TYPE_NTLM].state = NTLM_MANUAL; + auth_state->state = NTLM_MANUAL; if (ret == -EAGAIN) { /* Don't let it reset our state when it reconnects */ vpninfo->proxy_close_during_auth = 1; @@ -980,19 +987,19 @@ int ntlm_authorization(struct openconnect_info *vpninfo, struct oc_text_buf *buf if (!ret) return ret; } - if (vpninfo->auth[AUTH_TYPE_NTLM].state == NTLM_MANUAL && vpninfo->proxy_user && + if (auth_state->state == NTLM_MANUAL && vpninfo->proxy_user && vpninfo->proxy_pass) { buf_append(buf, "Proxy-Authorization: NTLM %s\r\n", "TlRMTVNTUAABAAAABYIIAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAwAAAA"); - vpninfo->auth[AUTH_TYPE_NTLM].state = NTLM_MANUAL_REQ; + auth_state->state = NTLM_MANUAL_REQ; return 0; } - if (vpninfo->auth[AUTH_TYPE_NTLM].state == NTLM_MANUAL_REQ && - !ntlm_manual_challenge(vpninfo, buf)) { + if (auth_state->state == NTLM_MANUAL_REQ && + !ntlm_manual_challenge(vpninfo, auth_state, buf)) { /* Leave the state as it is. If we come back there'll be no challenge string and we'll fail then. */ return 0; } - vpninfo->auth[AUTH_TYPE_NTLM].state = AUTH_FAILED; + auth_state->state = AUTH_FAILED; return -EAGAIN; } diff --git a/openconnect-internal.h b/openconnect-internal.h index 4ba67bb6..83e80710 100644 --- a/openconnect-internal.h +++ b/openconnect-internal.h @@ -351,7 +351,7 @@ struct openconnect_info { char *proxy_user; char *proxy_pass; int proxy_close_during_auth; - struct http_auth_state auth[MAX_AUTH_TYPES]; + struct http_auth_state proxy_auth[MAX_AUTH_TYPES]; #ifdef HAVE_GSSAPI gss_name_t gss_target_name; gss_ctx_id_t gss_context; @@ -946,16 +946,16 @@ int handle_redirect(struct openconnect_info *vpninfo); void http_common_headers(struct openconnect_info *vpninfo, struct oc_text_buf *buf); /* ntlm.c */ -int ntlm_authorization(struct openconnect_info *vpninfo, struct oc_text_buf *buf); +int ntlm_authorization(struct openconnect_info *vpninfo, struct http_auth_state *auth_state, struct oc_text_buf *buf); void cleanup_ntlm_auth(struct openconnect_info *vpninfo); /* gssapi.c */ -int gssapi_authorization(struct openconnect_info *vpninfo, struct oc_text_buf *buf); +int gssapi_authorization(struct openconnect_info *vpninfo, struct http_auth_state *auth_state, struct oc_text_buf *buf); void cleanup_gssapi_auth(struct openconnect_info *vpninfo); int socks_gssapi_auth(struct openconnect_info *vpninfo); /* digest.c */ -int digest_authorization(struct openconnect_info *vpninfo, struct oc_text_buf *buf); +int digest_authorization(struct openconnect_info *vpninfo, struct http_auth_state *auth_state, struct oc_text_buf *buf); /* library.c */ void nuke_opt_values(struct oc_form_opt *opt);