diff --git a/Makefile.am b/Makefile.am index 61c667bc..d823e019 100644 --- a/Makefile.am +++ b/Makefile.am @@ -23,7 +23,7 @@ openconnect_SOURCES = xml.c main.c openconnect_CFLAGS = $(AM_CFLAGS) $(SSL_CFLAGS) $(DTLS_SSL_CFLAGS) $(LIBXML2_CFLAGS) $(LIBPROXY_CFLAGS) $(ZLIB_CFLAGS) $(LIBSTOKEN_CFLAGS) $(LIBPSKC_CFLAGS) $(GSSAPI_CFLAGS) $(INTL_CFLAGS) $(ICONV_CFLAGS) $(LIBPCSCLITE_CFLAGS) openconnect_LDADD = libopenconnect.la $(SSL_LIBS) $(LIBXML2_LIBS) $(LIBPROXY_LIBS) $(INTL_LIBS) $(ICONV_LIBS) -library_srcs = ssl.c http.c auth-common.c library.c compat.c lzs.c mainloop.c script.c ntlm.c digest.c +library_srcs = ssl.c http.c http-auth.c auth-common.c library.c compat.c lzs.c mainloop.c script.c ntlm.c digest.c lib_srcs_cisco = auth.c cstp.c dtls.c lib_srcs_juniper = oncp.c lzo.c auth-juniper.c lib_srcs_gnutls = gnutls.c gnutls_pkcs12.c gnutls_tpm.c diff --git a/http-auth.c b/http-auth.c new file mode 100644 index 00000000..7f4a590f --- /dev/null +++ b/http-auth.c @@ -0,0 +1,358 @@ +/* + * OpenConnect (SSL + DTLS) VPN client + * + * Copyright © 2008-2015 Intel Corporation. + * Copyright © 2008 Nick Andrew + * + * Author: David Woodhouse + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "openconnect-internal.h" + +/* Ick. Yet another wheel to reinvent. But although we could pull it + in from OpenSSL, we can't from GnuTLS */ + +static inline int b64_char(char c) +{ + if (c >= 'A' && c <= 'Z') + return c - 'A'; + if (c >= 'a' && c <= 'z') + return c - 'a' + 26; + if (c >= '0' && c <= '9') + return c - '0' + 52; + if (c == '+') + return 62; + if (c == '/') + return 63; + return -1; +} + +void *openconnect_base64_decode(int *ret_len, const char *in) +{ + unsigned char *buf; + int b[4]; + int len = strlen(in); + + if (len & 3) { + *ret_len = -EINVAL; + return NULL; + } + len = (len * 3) / 4; + buf = malloc(len); + if (!buf) { + *ret_len = -ENOMEM; + return NULL; + } + + len = 0; + while (*in) { + if (!in[1] || !in[2] || !in[3]) + goto err; + b[0] = b64_char(in[0]); + b[1] = b64_char(in[1]); + if (b[0] < 0 || b[1] < 0) + goto err; + buf[len++] = (b[0] << 2) | (b[1] >> 4); + + if (in[2] == '=') { + if (in[3] != '=' || in[4] != 0) + goto err; + break; + } + b[2] = b64_char(in[2]); + if (b[2] < 0) + goto err; + buf[len++] = (b[1] << 4) | (b[2] >> 2); + if (in[3] == '=') { + if (in[4] != 0) + goto err; + break; + } + b[3] = b64_char(in[3]); + if (b[3] < 0) + goto err; + buf[len++] = (b[2] << 6) | b[3]; + in += 4; + } + *ret_len = len; + return buf; + + err: + free(buf); + *ret_len = EINVAL; + return NULL; +} + +static const char b64_table[] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' +}; + +void buf_append_base64(struct oc_text_buf *buf, const void *bytes, int len) +{ + const unsigned char *in = bytes; + int hibits; + + if (!buf || buf->error) + return; + + if (buf_ensure_space(buf, (4 * (len + 2) / 3) + 1)) + return; + + while (len > 0) { + buf->data[buf->pos++] = b64_table[in[0] >> 2]; + hibits = (in[0] << 4) & 0x30; + if (len == 1) { + buf->data[buf->pos++] = b64_table[hibits]; + buf->data[buf->pos++] = '='; + buf->data[buf->pos++] = '='; + break; + } + buf->data[buf->pos++] = b64_table[hibits | (in[1] >> 4)]; + hibits = (in[1] << 2) & 0x3c; + if (len == 2) { + buf->data[buf->pos++] = b64_table[hibits]; + buf->data[buf->pos++] = '='; + break; + } + buf->data[buf->pos++] = b64_table[hibits | (in[2] >> 6)]; + buf->data[buf->pos++] = b64_table[in[2] & 0x3f]; + in += 3; + len -= 3; + } + buf->data[buf->pos] = 0; +} + +static int basic_authorization(struct openconnect_info *vpninfo, int proxy, + struct http_auth_state *auth_state, + struct oc_text_buf *hdrbuf) +{ + struct oc_text_buf *text; + const char *user, *pass; + + if (proxy) { + user = vpninfo->proxy_user; + pass = vpninfo->proxy_pass; + } else { + /* Need to parse this out of the URL */ + return -EINVAL; + } + + if (!user || !pass) + return -EINVAL; + + if (!vpninfo->authmethods_set) { + vpn_progress(vpninfo, PRG_ERR, + _("Proxy requested Basic authentication which is disabled by default\n")); + auth_state->state = AUTH_FAILED; + return -EINVAL; + } + + if (auth_state->state == AUTH_IN_PROGRESS) { + auth_state->state = AUTH_FAILED; + return -EAGAIN; + } + + text = buf_alloc(); + buf_append(text, "%s:%s", user, pass); + if (buf_error(text)) + return buf_free(text); + + buf_append(hdrbuf, "%sAuthorization: Basic ", proxy ? "Proxy-" : ""); + buf_append_base64(hdrbuf, text->data, text->pos); + buf_append(hdrbuf, "\r\n"); + + memset(text->data, 0, text->pos); + buf_free(text); + + if (proxy) + vpn_progress(vpninfo, PRG_INFO, _("Attempting HTTP Basic authentication to proxy\n")); + else + vpn_progress(vpninfo, PRG_INFO, _("Attempting HTTP Basic authentication to server '%s'\n"), + vpninfo->hostname); + + auth_state->state = AUTH_IN_PROGRESS; + return 0; +} + +#if !defined(HAVE_GSSAPI) && !defined(_WIN32) +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")); + auth_state->state = AUTH_FAILED; + return -ENOENT; +} +#endif + +struct auth_method { + int state_index; + const char *name; + int (*authorization)(struct openconnect_info *, int, struct http_auth_state *, struct oc_text_buf *); + void (*cleanup)(struct openconnect_info *, struct http_auth_state *); +} auth_methods[] = { +#if defined(HAVE_GSSAPI) || defined(_WIN32) + { AUTH_TYPE_GSSAPI, "Negotiate", gssapi_authorization, cleanup_gssapi_auth }, +#endif + { AUTH_TYPE_NTLM, "NTLM", ntlm_authorization, cleanup_ntlm_auth }, + { AUTH_TYPE_DIGEST, "Digest", digest_authorization, NULL }, + { AUTH_TYPE_BASIC, "Basic", basic_authorization, NULL }, +#if !defined(HAVE_GSSAPI) && !defined(_WIN32) + { AUTH_TYPE_GSSAPI, "Negotiate", no_gssapi_authorization, NULL } +#endif +}; + +/* Generate Proxy-Authorization: header for request if appropriate */ +int gen_authorization_hdr(struct openconnect_info *vpninfo, int proxy, + struct oc_text_buf *buf) +{ + int ret; + int i; + + for (i = 0; i < sizeof(auth_methods) / sizeof(auth_methods[0]); i++) { + struct http_auth_state *auth_state; + if (proxy) + auth_state = &vpninfo->proxy_auth[auth_methods[i].state_index]; + else + auth_state = &vpninfo->http_auth[auth_methods[i].state_index]; + if (auth_state->state > AUTH_UNSEEN) { + ret = auth_methods[i].authorization(vpninfo, proxy, auth_state, buf); + if (ret == -EAGAIN || !ret) + return ret; + } + } + vpn_progress(vpninfo, PRG_INFO, _("No more authentication methods to try\n")); + return -ENOENT; +} + +/* Returns non-zero if it matched */ +static int handle_auth_proto(struct openconnect_info *vpninfo, + struct auth_method *method, char *hdr) +{ + struct http_auth_state *auth = &vpninfo->proxy_auth[method->state_index]; + int l = strlen(method->name); + + if (auth->state <= AUTH_FAILED) + return 0; + + if (strncmp(method->name, hdr, l)) + return 0; + if (hdr[l] != ' ' && hdr[l] != 0) + return 0; + + if (auth->state == AUTH_UNSEEN) + auth->state = AUTH_AVAILABLE; + + free(auth->challenge); + if (hdr[l]) + auth->challenge = strdup(hdr + l + 1); + else + auth->challenge = NULL; + + return 1; +} + +int proxy_auth_hdrs(struct openconnect_info *vpninfo, char *hdr, char *val) +{ + int i; + + if (!strcasecmp(hdr, "Proxy-Connection") || + !strcasecmp(hdr, "Connection")) { + if (!strcasecmp(val, "close")) + vpninfo->proxy_close_during_auth = 1; + return 0; + } + + if (strcasecmp(hdr, "Proxy-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, &auth_methods[i], val)) + return 0; + } + + return 0; +} + +void clear_auth_states(struct openconnect_info *vpninfo, + struct http_auth_state *auth_states, int reset) +{ + int i; + + for (i = 0; i < sizeof(auth_methods) / sizeof(auth_methods[0]); i++) { + struct http_auth_state *auth = &auth_states[auth_methods[i].state_index]; + + /* The 'reset' argument is set when we're connected successfully, + to fully reset the state to allow another connection to start + again. Otherwise, we need to remember which auth methods have + been tried and should not be attempted again. */ + if (reset && auth_methods[i].cleanup) + auth_methods[i].cleanup(vpninfo, auth); + + free(auth->challenge); + auth->challenge = NULL; + /* If it *failed* don't try it again even next time */ + if (auth->state <= AUTH_FAILED) + return; + if (reset || auth->state == AUTH_AVAILABLE) + auth->state = AUTH_UNSEEN; + } +} + +int openconnect_set_proxy_auth(struct openconnect_info *vpninfo, const char *methods) +{ + int i, len; + const char *p; + + for (i = 0; i < sizeof(auth_methods) / sizeof(auth_methods[0]); i++) + vpninfo->proxy_auth[auth_methods[i].state_index].state = AUTH_DISABLED; + + while (methods) { + p = strchr(methods, ','); + if (p) { + len = p - methods; + p++; + } else + len = strlen(methods); + + for (i = 0; i < sizeof(auth_methods) / sizeof(auth_methods[0]); i++) { + if (strprefix_match(methods, len, auth_methods[i].name) || + (auth_methods[i].state_index == AUTH_TYPE_GSSAPI && + strprefix_match(methods, len, "gssapi"))) { + vpninfo->proxy_auth[auth_methods[i].state_index].state = AUTH_UNSEEN; + break; + } + } + methods = p; + } + vpninfo->authmethods_set = 1; + return 0; +} + diff --git a/http.c b/http.c index 3a113f0e..ab8c8987 100644 --- a/http.c +++ b/http.c @@ -581,14 +581,6 @@ int process_http_response(struct openconnect_info *vpninfo, int connect, return result; } -/* strncasecmp() just checks that the first n characters match. This - function ensures that the first n characters of the left-hand side - are a *precise* match for the right-hand side. */ -static inline int strprefix_match(const char *str, int len, const char *match) -{ - return len == strlen(match) && !strncasecmp(str, match, len); -} - int internal_parse_url(const char *url, char **res_proto, char **res_host, int *res_port, char **res_path, int default_port) { @@ -1260,298 +1252,6 @@ static int process_socks_proxy(struct openconnect_info *vpninfo) return 0; } -/* Ick. Yet another wheel to reinvent. But although we could pull it - in from OpenSSL, we can't from GnuTLS */ - -static inline int b64_char(char c) -{ - if (c >= 'A' && c <= 'Z') - return c - 'A'; - if (c >= 'a' && c <= 'z') - return c - 'a' + 26; - if (c >= '0' && c <= '9') - return c - '0' + 52; - if (c == '+') - return 62; - if (c == '/') - return 63; - return -1; -} - -void *openconnect_base64_decode(int *ret_len, const char *in) -{ - unsigned char *buf; - int b[4]; - int len = strlen(in); - - if (len & 3) { - *ret_len = -EINVAL; - return NULL; - } - len = (len * 3) / 4; - buf = malloc(len); - if (!buf) { - *ret_len = -ENOMEM; - return NULL; - } - - len = 0; - while (*in) { - if (!in[1] || !in[2] || !in[3]) - goto err; - b[0] = b64_char(in[0]); - b[1] = b64_char(in[1]); - if (b[0] < 0 || b[1] < 0) - goto err; - buf[len++] = (b[0] << 2) | (b[1] >> 4); - - if (in[2] == '=') { - if (in[3] != '=' || in[4] != 0) - goto err; - break; - } - b[2] = b64_char(in[2]); - if (b[2] < 0) - goto err; - buf[len++] = (b[1] << 4) | (b[2] >> 2); - if (in[3] == '=') { - if (in[4] != 0) - goto err; - break; - } - b[3] = b64_char(in[3]); - if (b[3] < 0) - goto err; - buf[len++] = (b[2] << 6) | b[3]; - in += 4; - } - *ret_len = len; - return buf; - - err: - free(buf); - *ret_len = EINVAL; - return NULL; -} - -static const char b64_table[] = { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', - 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', - 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', - 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' -}; - -void buf_append_base64(struct oc_text_buf *buf, const void *bytes, int len) -{ - const unsigned char *in = bytes; - int hibits; - - if (!buf || buf->error) - return; - - if (buf_ensure_space(buf, (4 * (len + 2) / 3) + 1)) - return; - - while (len > 0) { - buf->data[buf->pos++] = b64_table[in[0] >> 2]; - hibits = (in[0] << 4) & 0x30; - if (len == 1) { - buf->data[buf->pos++] = b64_table[hibits]; - buf->data[buf->pos++] = '='; - buf->data[buf->pos++] = '='; - break; - } - buf->data[buf->pos++] = b64_table[hibits | (in[1] >> 4)]; - hibits = (in[1] << 2) & 0x3c; - if (len == 2) { - buf->data[buf->pos++] = b64_table[hibits]; - buf->data[buf->pos++] = '='; - break; - } - buf->data[buf->pos++] = b64_table[hibits | (in[2] >> 6)]; - buf->data[buf->pos++] = b64_table[in[2] & 0x3f]; - in += 3; - len -= 3; - } - buf->data[buf->pos] = 0; -} - -static int basic_authorization(struct openconnect_info *vpninfo, int proxy, - struct http_auth_state *auth_state, - struct oc_text_buf *hdrbuf) -{ - struct oc_text_buf *text; - const char *user, *pass; - - if (proxy) { - user = vpninfo->proxy_user; - pass = vpninfo->proxy_pass; - } else { - /* Need to parse this out of the URL */ - return -EINVAL; - } - - if (!user || !pass) - return -EINVAL; - - if (!vpninfo->authmethods_set) { - vpn_progress(vpninfo, PRG_ERR, - _("Proxy requested Basic authentication which is disabled by default\n")); - auth_state->state = AUTH_FAILED; - return -EINVAL; - } - - if (auth_state->state == AUTH_IN_PROGRESS) { - auth_state->state = AUTH_FAILED; - return -EAGAIN; - } - - text = buf_alloc(); - buf_append(text, "%s:%s", user, pass); - if (buf_error(text)) - return buf_free(text); - - buf_append(hdrbuf, "%sAuthorization: Basic ", proxy ? "Proxy-" : ""); - buf_append_base64(hdrbuf, text->data, text->pos); - buf_append(hdrbuf, "\r\n"); - - memset(text->data, 0, text->pos); - buf_free(text); - - if (proxy) - vpn_progress(vpninfo, PRG_INFO, _("Attempting HTTP Basic authentication to proxy\n")); - else - vpn_progress(vpninfo, PRG_INFO, _("Attempting HTTP Basic authentication to server '%s'\n"), - vpninfo->hostname); - - auth_state->state = AUTH_IN_PROGRESS; - return 0; -} - -#if !defined(HAVE_GSSAPI) && !defined(_WIN32) -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")); - auth_state->state = AUTH_FAILED; - return -ENOENT; -} -#endif - -struct auth_method { - int state_index; - const char *name; - int (*authorization)(struct openconnect_info *, int, struct http_auth_state *, struct oc_text_buf *); - void (*cleanup)(struct openconnect_info *, struct http_auth_state *); -} auth_methods[] = { -#if defined(HAVE_GSSAPI) || defined(_WIN32) - { AUTH_TYPE_GSSAPI, "Negotiate", gssapi_authorization, cleanup_gssapi_auth }, -#endif - { AUTH_TYPE_NTLM, "NTLM", ntlm_authorization, cleanup_ntlm_auth }, - { AUTH_TYPE_DIGEST, "Digest", digest_authorization, NULL }, - { AUTH_TYPE_BASIC, "Basic", basic_authorization, NULL }, -#if !defined(HAVE_GSSAPI) && !defined(_WIN32) - { AUTH_TYPE_GSSAPI, "Negotiate", no_gssapi_authorization, NULL } -#endif -}; - -/* Generate Proxy-Authorization: header for request if appropriate */ -static int gen_authorization_hdr(struct openconnect_info *vpninfo, int proxy, - struct oc_text_buf *buf) -{ - int ret; - int i; - - for (i = 0; i < sizeof(auth_methods) / sizeof(auth_methods[0]); i++) { - struct http_auth_state *auth_state; - if (proxy) - auth_state = &vpninfo->proxy_auth[auth_methods[i].state_index]; - else - auth_state = &vpninfo->http_auth[auth_methods[i].state_index]; - if (auth_state->state > AUTH_UNSEEN) { - ret = auth_methods[i].authorization(vpninfo, proxy, auth_state, buf); - if (ret == -EAGAIN || !ret) - return ret; - } - } - vpn_progress(vpninfo, PRG_INFO, _("No more authentication methods to try\n")); - return -ENOENT; -} - -/* Returns non-zero if it matched */ -static int handle_auth_proto(struct openconnect_info *vpninfo, - struct auth_method *method, char *hdr) -{ - struct http_auth_state *auth = &vpninfo->proxy_auth[method->state_index]; - int l = strlen(method->name); - - if (auth->state <= AUTH_FAILED) - return 0; - - if (strncmp(method->name, hdr, l)) - return 0; - if (hdr[l] != ' ' && hdr[l] != 0) - return 0; - - if (auth->state == AUTH_UNSEEN) - auth->state = AUTH_AVAILABLE; - - free(auth->challenge); - if (hdr[l]) - auth->challenge = strdup(hdr + l + 1); - else - auth->challenge = NULL; - - return 1; -} - -static int proxy_hdrs(struct openconnect_info *vpninfo, char *hdr, char *val) -{ - int i; - - if (!strcasecmp(hdr, "Proxy-Connection") || - !strcasecmp(hdr, "Connection")) { - if (!strcasecmp(val, "close")) - vpninfo->proxy_close_during_auth = 1; - return 0; - } - - if (strcasecmp(hdr, "Proxy-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, &auth_methods[i], val)) - return 0; - } - - return 0; -} - -static void clear_auth_state(struct openconnect_info *vpninfo, struct http_auth_state *auth_states, - struct auth_method *method, int reset) -{ - struct http_auth_state *auth = &auth_states[method->state_index]; - /* The 'reset' argument is set when we're connected successfully, - to fully reset the state to allow another connection to start - again. Otherwise, we need to remember which auth methods have - been tried and should not be attempted again. */ - if (reset && method->cleanup) - method->cleanup(vpninfo, auth); - - free(auth->challenge); - auth->challenge = NULL; - /* If it *failed* don't try it again even next time */ - if (auth->state <= AUTH_FAILED) - return; - if (reset || auth->state == AUTH_AVAILABLE) - auth->state = AUTH_UNSEEN; -} - - static int process_http_proxy(struct openconnect_info *vpninfo) { struct oc_text_buf *reqbuf; @@ -1573,16 +1273,13 @@ static int process_http_proxy(struct openconnect_info *vpninfo) buf_append(reqbuf, "Connection: keep-alive\r\n"); buf_append(reqbuf, "Accept-Encoding: identity\r\n"); if (auth) { - int i; - result = gen_authorization_hdr(vpninfo, 1, reqbuf); if (result) { buf_free(reqbuf); return result; } /* Forget existing challenges */ - for (i = 0; i < sizeof(auth_methods) / sizeof(auth_methods[0]); i++) - clear_auth_state(vpninfo, vpninfo->proxy_auth, &auth_methods[i], 0); + clear_auth_states(vpninfo, vpninfo->proxy_auth, 0); } buf_append(reqbuf, "\r\n"); @@ -1601,7 +1298,7 @@ static int process_http_proxy(struct openconnect_info *vpninfo) return result; } - result = process_http_response(vpninfo, 1, proxy_hdrs, reqbuf); + result = process_http_response(vpninfo, 1, proxy_auth_hdrs, reqbuf); buf_free(reqbuf); if (result < 0) return -EINVAL; @@ -1623,14 +1320,6 @@ static int process_http_proxy(struct openconnect_info *vpninfo) return -EIO; } -void cleanup_proxy_auth(struct openconnect_info *vpninfo) -{ - int i; - - for (i = 0; i < sizeof(auth_methods) / sizeof(auth_methods[0]); i++) - clear_auth_state(vpninfo, vpninfo->proxy_auth, &auth_methods[i], 1); -} - int process_proxy(struct openconnect_info *vpninfo, int ssl_sock) { int ret; @@ -1653,41 +1342,11 @@ int process_proxy(struct openconnect_info *vpninfo, int ssl_sock) vpninfo->proxy_fd = -1; if (!vpninfo->proxy_close_during_auth) - cleanup_proxy_auth(vpninfo); + clear_auth_states(vpninfo, vpninfo->proxy_auth, 1); return ret; } -int openconnect_set_proxy_auth(struct openconnect_info *vpninfo, const char *methods) -{ - int i, len; - const char *p; - - for (i = 0; i < sizeof(auth_methods) / sizeof(auth_methods[0]); i++) - vpninfo->proxy_auth[auth_methods[i].state_index].state = AUTH_DISABLED; - - while (methods) { - p = strchr(methods, ','); - if (p) { - len = p - methods; - p++; - } else - len = strlen(methods); - - for (i = 0; i < sizeof(auth_methods) / sizeof(auth_methods[0]); i++) { - if (strprefix_match(methods, len, auth_methods[i].name) || - (auth_methods[i].state_index == AUTH_TYPE_GSSAPI && - strprefix_match(methods, len, "gssapi"))) { - vpninfo->proxy_auth[auth_methods[i].state_index].state = AUTH_UNSEEN; - break; - } - } - methods = p; - } - vpninfo->authmethods_set = 1; - return 0; -} - int openconnect_set_http_proxy(struct openconnect_info *vpninfo, const char *proxy) { diff --git a/openconnect-internal.h b/openconnect-internal.h index 68faa4f0..80c6bfe3 100644 --- a/openconnect-internal.h +++ b/openconnect-internal.h @@ -932,17 +932,14 @@ int buf_ensure_space(struct oc_text_buf *buf, int len); void __attribute__ ((format (printf, 2, 3))) buf_append(struct oc_text_buf *buf, const char *fmt, ...); void buf_append_bytes(struct oc_text_buf *buf, const void *bytes, int len); -void buf_append_base64(struct oc_text_buf *buf, const void *bytes, int len); int buf_append_utf16le(struct oc_text_buf *buf, const char *utf8); int get_utf8char(const char **utf8); void buf_append_from_utf16le(struct oc_text_buf *buf, const void *utf16); -void *openconnect_base64_decode(int *len, const char *in); void buf_truncate(struct oc_text_buf *buf); void buf_append_urlencoded(struct oc_text_buf *buf, char *str); int buf_error(struct oc_text_buf *buf); int buf_free(struct oc_text_buf *buf); char *openconnect_create_useragent(const char *base); -void cleanup_proxy_auth(struct openconnect_info *vpninfo); int process_proxy(struct openconnect_info *vpninfo, int ssl_sock); int internal_parse_url(const char *url, char **res_proto, char **res_host, int *res_port, char **res_path, int default_port); @@ -957,6 +954,14 @@ int process_http_response(struct openconnect_info *vpninfo, int connect, int handle_redirect(struct openconnect_info *vpninfo); void http_common_headers(struct openconnect_info *vpninfo, struct oc_text_buf *buf); +/* http-auth.c */ +void buf_append_base64(struct oc_text_buf *buf, const void *bytes, int len); +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 gen_authorization_hdr(struct openconnect_info *vpninfo, int proxy, + struct oc_text_buf *buf); /* ntlm.c */ int ntlm_authorization(struct openconnect_info *vpninfo, int proxy, struct http_auth_state *auth_state, struct oc_text_buf *buf); void cleanup_ntlm_auth(struct openconnect_info *vpninfo, struct http_auth_state *auth_state); @@ -978,6 +983,14 @@ void openconnect_set_juniper(struct openconnect_info *vpninfo); /* version.c */ extern const char *openconnect_version_str; +/* strncasecmp() just checks that the first n characters match. This + function ensures that the first n characters of the left-hand side + are a *precise* match for the right-hand side. */ +static inline int strprefix_match(const char *str, int len, const char *match) +{ + return len == strlen(match) && !strncasecmp(str, match, len); +} + #define STRDUP(res, arg) \ do { \ free(res); \ diff --git a/ssl.c b/ssl.c index 84e32a8e..166bb173 100644 --- a/ssl.c +++ b/ssl.c @@ -369,7 +369,7 @@ int connect_https_socket(struct openconnect_info *vpninfo) out: /* If proxy processing returned -EAGAIN to reconnect before attempting further auth, and we failed to reconnect, we have to clean up here. */ - cleanup_proxy_auth(vpninfo); + clear_auth_states(vpninfo, vpninfo->proxy_auth, 1); return ssl_sock; }