From c26d459047effd90e1721924e0c08b6a7abd5f0c Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 24 Feb 2015 13:22:10 +0000 Subject: [PATCH] Finally add (non-proxy) HTTP authentication support This is what a lot of the previous changes from Nikos and myself were working towards. Signed-off-by: David Woodhouse --- http-auth.c | 21 +++++++++++++++++++-- http.c | 23 ++++++++++++++++++++--- ntlm.c | 2 -- openconnect-internal.h | 2 +- 4 files changed, 40 insertions(+), 8 deletions(-) diff --git a/http-auth.c b/http-auth.c index 7f4a590f..e4878244 100644 --- a/http-auth.c +++ b/http-auth.c @@ -253,9 +253,10 @@ int gen_authorization_hdr(struct openconnect_info *vpninfo, int proxy, /* Returns non-zero if it matched */ static int handle_auth_proto(struct openconnect_info *vpninfo, + struct http_auth_state *auth_states, struct auth_method *method, char *hdr) { - struct http_auth_state *auth = &vpninfo->proxy_auth[method->state_index]; + struct http_auth_state *auth = &auth_states[method->state_index]; int l = strlen(method->name); if (auth->state <= AUTH_FAILED) @@ -294,7 +295,23 @@ int proxy_auth_hdrs(struct openconnect_info *vpninfo, char *hdr, char *val) for (i = 0; i < sizeof(auth_methods) / sizeof(auth_methods[0]); i++) { /* Return once we've found a match */ - if (handle_auth_proto(vpninfo, &auth_methods[i], val)) + if (handle_auth_proto(vpninfo, vpninfo->proxy_auth, &auth_methods[i], val)) + return 0; + } + + return 0; +} + +int http_auth_hdrs(struct openconnect_info *vpninfo, char *hdr, char *val) +{ + int i; + + if (strcasecmp(hdr, "WWW-Authenticate")) + return 0; + + for (i = 0; i < sizeof(auth_methods) / sizeof(auth_methods[0]); i++) { + /* Return once we've found a match */ + if (handle_auth_proto(vpninfo, vpninfo->http_auth, &auth_methods[i], val)) return 0; } diff --git a/http.c b/http.c index ab8c8987..b4012521 100644 --- a/http.c +++ b/http.c @@ -790,6 +790,7 @@ int do_https_request(struct openconnect_info *vpninfo, const char *method, int result; int rq_retry; int rlen, pad; + int auth = 0; if (request_body_type && buf_error(request_body)) return buf_error(request_body); @@ -815,6 +816,14 @@ int do_https_request(struct openconnect_info *vpninfo, const char *method, buf_append(buf, "%s /%s HTTP/1.1\r\n", method, vpninfo->urlpath ?: ""); if (vpninfo->proto.add_http_headers) vpninfo->proto.add_http_headers(vpninfo, buf); + if (auth) { + result = gen_authorization_hdr(vpninfo, 0, buf); + if (result) + goto out; + + /* Forget existing challenges */ + clear_auth_states(vpninfo, vpninfo->http_auth, 0); + } if (request_body_type) { rlen = request_body->pos; @@ -856,12 +865,12 @@ int do_https_request(struct openconnect_info *vpninfo, const char *method, vpn_progress(vpninfo, PRG_ERR, _("Failed to open HTTPS connection to %s\n"), vpninfo->hostname); - buf_free(buf); /* We really don't want to return -EINVAL if we have failed to even connect to the server, because if we do that openconnect_obtain_cookie() might try again without XMLPOST... with the same result. */ - return -EIO; + result = -EIO; + goto out; } } @@ -876,7 +885,7 @@ int do_https_request(struct openconnect_info *vpninfo, const char *method, if (result < 0) goto out; - result = process_http_response(vpninfo, 0, NULL, buf); + result = process_http_response(vpninfo, 0, http_auth_hdrs, buf); if (result < 0) { /* We'll already have complained about whatever offended us */ goto out; @@ -884,6 +893,10 @@ int do_https_request(struct openconnect_info *vpninfo, const char *method, if (vpninfo->dump_http_traffic && buf->pos) dump_buf(vpninfo, '<', buf->data); + if (result == 401) { + auth = 1; + goto redirected; + } if (result != 200 && vpninfo->redirect_url) { result = handle_redirect(vpninfo); if (result == 0) { @@ -894,6 +907,8 @@ int do_https_request(struct openconnect_info *vpninfo, const char *method, method = "GET"; request_body_type = NULL; } + if (vpninfo->redirect_type == REDIR_TYPE_NEWHOST) + clear_auth_states(vpninfo, vpninfo->http_auth, 1); goto redirected; } goto out; @@ -912,6 +927,8 @@ int do_https_request(struct openconnect_info *vpninfo, const char *method, out: buf_free(buf); + /* On success, clear out all authentication state for the next request */ + clear_auth_states(vpninfo, vpninfo->http_auth, 1); return result; } diff --git a/ntlm.c b/ntlm.c index 67845436..bdb68d0b 100644 --- a/ntlm.c +++ b/ntlm.c @@ -1014,8 +1014,6 @@ int ntlm_authorization(struct openconnect_info *vpninfo, int proxy, /* Don't let it reset our state when it reconnects */ if (proxy) vpninfo->proxy_close_during_auth = 1; - else - vpninfo->http_close_during_auth = 1; return ret; } if (!ret) diff --git a/openconnect-internal.h b/openconnect-internal.h index 80c6bfe3..d5216e93 100644 --- a/openconnect-internal.h +++ b/openconnect-internal.h @@ -374,7 +374,6 @@ struct openconnect_info { char *proxy_user; char *proxy_pass; int proxy_close_during_auth; - int http_close_during_auth; struct http_auth_state http_auth[MAX_AUTH_TYPES]; struct http_auth_state proxy_auth[MAX_AUTH_TYPES]; int authmethods_set; @@ -960,6 +959,7 @@ void *openconnect_base64_decode(int *len, const char *in); void clear_auth_states(struct openconnect_info *vpninfo, struct http_auth_state *auth_states, int reset); int proxy_auth_hdrs(struct openconnect_info *vpninfo, char *hdr, char *val); +int http_auth_hdrs(struct openconnect_info *vpninfo, char *hdr, char *val); int gen_authorization_hdr(struct openconnect_info *vpninfo, int proxy, struct oc_text_buf *buf); /* ntlm.c */