Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
First pass at adding proxy auth support
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
  • Loading branch information
David Woodhouse authored and David Woodhouse committed Jun 18, 2014
1 parent 010ff23 commit 14fed28
Show file tree
Hide file tree
Showing 4 changed files with 195 additions and 8 deletions.
190 changes: 182 additions & 8 deletions http.c
Expand Up @@ -203,7 +203,7 @@ static int process_http_response(struct openconnect_info *vpninfo, int *result,
return -EINVAL;
}

vpn_progress(vpninfo, (*result == 200) ? PRG_DEBUG : PRG_INFO,
vpn_progress(vpninfo, (*result == 200 || *result == 407) ? PRG_DEBUG : PRG_INFO,
_("Got HTTP response: %s\n"), buf);

/* Eat headers... */
Expand Down Expand Up @@ -306,7 +306,7 @@ static int process_http_response(struct openconnect_info *vpninfo, int *result,
return -EINVAL;
}
}
if (header_cb && !strncmp(buf, "X-", 2))
if (header_cb)
header_cb(vpninfo, buf, colon);
}

Expand Down Expand Up @@ -1516,29 +1516,182 @@ 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 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', '+', '/'
};

static void b64_frag(struct oc_text_buf *buf, int len, unsigned char *in)
{
int hibits;
char b64[5];

b64[0] = b64_table[in[0] >> 2];
hibits = (in[0] << 4) & 0x30;
if (len == 1) {
b64[1] = b64_table[hibits];
b64[2] = '=';
b64[3] = '=';
goto out;
}
b64[1] = b64_table[hibits | (in[1] >> 4)];
hibits = (in[1] << 2) & 0x3c;
if (len == 2) {
b64[2] = b64_table[hibits];
b64[3] = '=';
goto out;
}
b64[2] = b64_table[hibits | (in[2] >> 6)];
b64[3] = b64_table[in[2] & 0x3f];
out:
b64[4] = 0;
buf_append(buf, b64);
}

/* State in vpninfo->proxy_auth_state */
#define AUTH_UNSEEN 0 /* Server has not offered it */
#define AUTH_AVAILABLE 1 /* Server has offered it, we have not tried it */
#define AUTH_IN_PROGRESS 2 /* In-progress attempt */
#define AUTH_FAILED 3 /* Failed */

/* Generate Proxy-Authorization: header for request if appropriate */

static int proxy_authorization(struct openconnect_info *vpninfo, struct oc_text_buf *buf)
{
if (vpninfo->basic_auth.state == AUTH_AVAILABLE &&
vpninfo->proxy_user && vpninfo->proxy_pass) {
char *p = vpninfo->proxy_user;
unsigned char buf2[3];
int i;

buf_append(buf, "Proxy-Authorization: Basic ");

i = strlen(p);
while (i >= 3) {
b64_frag(buf, 3, (unsigned char *)p);
p += 3;
i -= 3;
}

/* Fill base64 chunk of 3 chars with end of username, colon, and
start of password. */
if (i)
memcpy(buf2, p, i);
buf2[i++] = ':';
p = vpninfo->proxy_pass;
while (i < 3 && *p)
buf2[i++] = *(p++);

b64_frag(buf, i, buf2);

i = strlen(p);
while (i) {
int j = (i > 3) ? 3 : i;

b64_frag(buf, j, (unsigned char *)p);
p += j;
i -= j;
}

buf_append(buf, "\r\n");

vpn_progress(vpninfo, PRG_INFO, _("Attempting HTTP Basic authentication to proxy\n"));
vpninfo->basic_auth.state = AUTH_IN_PROGRESS;
return 0;
}

vpn_progress(vpninfo, PRG_INFO, _("No more authentication methods to try\n"));
return -ENOENT;
}

static void handle_auth_proto(struct openconnect_info *vpninfo, struct proxy_auth_state *auth,
const char *name, char *hdr)
{
int l = strlen(name);

if (auth->state == AUTH_FAILED)
return;

if (strncmp(name, hdr, l))
return;
if (hdr[l] != ' ' && hdr[l] != 0)
return;

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;
}

static int proxy_hdrs(struct openconnect_info *vpninfo, char *hdr, char *val)
{
if (strcasecmp(hdr, "Proxy-Authenticate"))
return 0;

handle_auth_proto(vpninfo, &vpninfo->basic_auth, "Basic", val);
handle_auth_proto(vpninfo, &vpninfo->ntlm_auth, "NTLM", val);
handle_auth_proto(vpninfo, &vpninfo->gssapi_auth, "Negotiate", val);

return 0;
}

static void clear_auth_state(struct proxy_auth_state *auth, int reset)
{
/* 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->state == AUTH_AVAILABLE)
auth->state = AUTH_UNSEEN;
free(auth->challenge);
auth->challenge = NULL;
}


static int process_http_proxy(struct openconnect_info *vpninfo)
{
char *resp = NULL;
int resplen;
struct oc_text_buf *reqbuf;
int result;
int auth = 0;

vpn_progress(vpninfo, PRG_INFO,
_("Requesting HTTP proxy connection to %s:%d\n"),
vpninfo->hostname, vpninfo->port);

retry:
reqbuf = buf_alloc();
buf_append(reqbuf, "CONNECT %s:%d HTTP/1.1\r\n", vpninfo->hostname, vpninfo->port);
buf_append(reqbuf, "Host: %s\r\n", vpninfo->hostname);
buf_append(reqbuf, "User-Agent: %s\r\n", vpninfo->useragent);
buf_append(reqbuf, "Proxy-Connection: keep-alive\r\n");
buf_append(reqbuf, "Connection: keep-alive\r\n");
buf_append(reqbuf, "Accept-Encoding: identity\r\n");
if (auth) {
result = proxy_authorization(vpninfo, reqbuf);
if (result) {
buf_free(reqbuf);
return result;
}
/* Forget existing challenges */
clear_auth_state(&vpninfo->basic_auth, 0);
clear_auth_state(&vpninfo->ntlm_auth, 0);
clear_auth_state(&vpninfo->gssapi_auth, 0);
}
buf_append(reqbuf, "\r\n");

if (buf_error(reqbuf))
return buf_free(reqbuf);

vpn_progress(vpninfo, PRG_INFO,
_("Requesting HTTP proxy connection to %s:%d\n"),
vpninfo->hostname, vpninfo->port);

result = proxy_write(vpninfo, reqbuf->data, reqbuf->pos);
buf_free(reqbuf);

Expand All @@ -1549,7 +1702,7 @@ static int process_http_proxy(struct openconnect_info *vpninfo)
return result;
}

resplen = process_http_response(vpninfo, &result, 1, NULL, &resp);
resplen = process_http_response(vpninfo, &result, 1, proxy_hdrs, &resp);
if (resplen < 0)
return -EINVAL;

Expand All @@ -1558,6 +1711,11 @@ static int process_http_proxy(struct openconnect_info *vpninfo)
resp = NULL;
}

if (result == 407) {
auth = 1;
goto retry;
}

if (result == 200)
return 0;

Expand Down Expand Up @@ -1587,12 +1745,15 @@ int process_proxy(struct openconnect_info *vpninfo, int ssl_sock)
}

vpninfo->proxy_fd = -1;
clear_auth_state(&vpninfo->basic_auth, 1);
clear_auth_state(&vpninfo->ntlm_auth, 1);
clear_auth_state(&vpninfo->gssapi_auth, 1);
return ret;
}

int openconnect_set_http_proxy(struct openconnect_info *vpninfo, char *proxy)
{
char *url = proxy;
char *url = proxy, *p;
int ret;

if (!url)
Expand All @@ -1608,6 +1769,19 @@ int openconnect_set_http_proxy(struct openconnect_info *vpninfo, char *proxy)
if (ret)
goto out;

p = strchr(vpninfo->proxy, '@');
if (p) {
/* Proxy username/password */
*p = 0;
vpninfo->proxy_user = vpninfo->proxy;
vpninfo->proxy = strdup(p + 1);
p = strchr(vpninfo->proxy_user, ':');
if (p) {
*p = 0;
vpninfo->proxy_pass = strdup(p + 1);
}
}

if (vpninfo->proxy_type &&
strcmp(vpninfo->proxy_type, "http") &&
strcmp(vpninfo->proxy_type, "socks") &&
Expand Down
2 changes: 2 additions & 0 deletions library.c
Expand Up @@ -161,6 +161,8 @@ void openconnect_vpninfo_free(struct openconnect_info *vpninfo)
free(vpninfo->cookie);
free(vpninfo->proxy_type);
free(vpninfo->proxy);
free(vpninfo->proxy_user);
free(vpninfo->proxy_pass);
free(vpninfo->vpnc_script);
free(vpninfo->cafile);
free(vpninfo->servercert);
Expand Down
10 changes: 10 additions & 0 deletions openconnect-internal.h
Expand Up @@ -138,6 +138,11 @@ struct pin_cache {
#define REDIR_TYPE_NEWHOST 1
#define REDIR_TYPE_LOCAL 2

struct proxy_auth_state {
int state;
char *challenge;
};

struct openconnect_info {
char *redirect_url;
int redirect_type;
Expand Down Expand Up @@ -168,6 +173,11 @@ struct openconnect_info {
char *proxy;
int proxy_port;
int proxy_fd;
char *proxy_user;
char *proxy_pass;
struct proxy_auth_state basic_auth;
struct proxy_auth_state ntlm_auth;
struct proxy_auth_state gssapi_auth;

char *localname;
char *hostname;
Expand Down
1 change: 1 addition & 0 deletions www/changelog.xml
Expand Up @@ -15,6 +15,7 @@
<ul>
<li><b>OpenConnect HEAD</b>
<ul>
<li>Support HTTP proxy authentication (Basic only).</li>
<li>Download XML profile in XML POST mode.</li>
<li>Fix a couple of bugs involving DTLS rekeying.</li>
<li>Fix problems seen when building or connecting without DTLS enabled.</li>
Expand Down

0 comments on commit 14fed28

Please sign in to comment.