Feature Request: HTTP proxy authentication

Marc-André Laverdière marc-andre at atc.tcs.com
Thu Jun 19 02:11:25 PDT 2014


Yeah, that's 100% my bad. I didn't think that my changes would be of any
interest after you made the feature work, so I didn't put the effort in
making a proper patch. I'll give you proper contributions from now on.

As for base64, there is always the option of adding one more dependency
- which I guess we are trying to avoid :)

Marc-André Laverdière-Papineau
Researcher - e-Security Team
TCS Innovation Labs

On 06/19/2014 02:34 PM, David Woodhouse wrote:
> On Thu, 2014-06-19 at 09:12 +0530, Marc-André Laverdière wrote:
>>
>> I discovered yesterday how rusty I got at C... oh well.
>> Here are some of the changes I did, straight from the "I don't know
>> what I'm doing dept."
> 
> Looks good; thanks. I'd actually already implemented the
> username/password parsing for openconnect_set_http_proxy() but it was a
> bit of a hack, just post-processing the "hostname" returned by the
> standard URL parsing function and looking for the @ sign.
> 
> You've done a much better job, making internal_parse_url() do it sanely
> and propagating that API change — and as an added bonus you've also made
> it use the xmlParseUri() function instead of its own implementation. I
> hadn't realised that was available.
> 
> There are *lots* of wheels reinvented in the OpenConnect code, which
> always makes me sad, but often it's not avoidable. This one *was*
> avoidable, and well done for spotting that.
> 
> (If you can find a suitable base64 encode/decode function that I can use
> too, that'd make me even happier. OpenSSL gives me one, but not GnuTLS
> or libxml2 AFAICT.)
> 
> It took me a while to work out what you'd changed, though. Instead of
> complete files (which were so large that the mailing list didn't even
> let them through), it's best to send a patch. That way, I don't have to
> spend time trying to work out which historical version of the code you
> *started* with, to tell your changes from all the other changes which
> have been made since then.
> 
> Here's your work as a patch, which makes it easy to read and review
> right here in the mail. When submitting a patch please also include a
> Signed-Off-By: tag as described at
> http://www.infradead.org/openconnect/contribute.html
> 
> Thanks again.
> 
> diff --git a/http.c b/http.c
> index 323ff43..8b9d87f 100644
> --- a/http.c
> +++ b/http.c
> @@ -27,6 +27,7 @@
>  #include <stdlib.h>
>  #include <stdio.h>
>  #include <stdarg.h>
> +#include <libxml/uri.h>
>  #ifndef _WIN32
>  #include <pwd.h>
>  #endif
> @@ -662,69 +663,65 @@ out:
>  #endif /* !_WIN32 */
>  }
>  
> -int internal_parse_url(char *url, char **res_proto, char **res_host,
> -		       int *res_port, char **res_path, int default_port)
> +int internal_parse_url(char *url, char **res_proto, char **res_host, int *res_port,
> +                       char **res_auth_uname, char **res_auth_pwd, char **res_path, int default_port)
>  {
> -	char *proto = url;
> -	char *host, *path, *port_str;
> -	int port;
> -
> -	host = strstr(url, "://");
> -	if (host) {
> -		*host = 0;
> -		host += 3;
> -
> -		if (!strcasecmp(proto, "https"))
> -			port = 443;
> -		else if (!strcasecmp(proto, "http"))
> -			port = 80;
> -		else if (!strcasecmp(proto, "socks") ||
> -			 !strcasecmp(proto, "socks4") ||
> -			 !strcasecmp(proto, "socks5"))
> -			port = 1080;
> -		else
> -			return -EPROTONOSUPPORT;
> -	} else {
> -		if (default_port) {
> -			proto = NULL;
> -			port = default_port;
> -			host = url;
> -		} else
> -			return -EINVAL;
> -	}
> -
> -	path = strchr(host, '/');
> -	if (path)
> -		*(path++) = 0;
>  
> -	port_str = strrchr(host, ':');
> -	if (port_str) {
> -		char *end;
> -		int new_port = strtol(port_str + 1, &end, 10);
> -
> -		if (!*end) {
> -			*port_str = 0;
> -			port = new_port;
> -		}
> -	}
> -
> -	if (res_proto)
> -		*res_proto = proto ? strdup(proto) : NULL;
> -	if (res_host)
> -		*res_host = strdup(host);
> -	if (res_port)
> -		*res_port = port;
> -	if (res_path)
> -		*res_path = (path && *path) ? strdup(path) : NULL;
> -
> -	/* Undo the damage we did to the original string */
> -	if (port_str)
> -		*(port_str) = ':';
> -	if (path)
> -		*(path - 1) = '/';
> -	if (proto)
> -		*(host - 3) = ':';
> -	return 0;
> +    int port = default_port;
> +
> +    xmlURIPtr uriInfo = xmlParseURI(url);
> +    if (uriInfo == NULL || uriInfo->scheme == NULL || uriInfo->server == NULL)
> +        return -EPROTONOSUPPORT;
> +
> +    *res_proto = strdup(uriInfo->scheme);
> +
> +    if (res_proto != NULL)
> +    {
> +        if (!strcasecmp(*res_proto, "https"))
> +            port = 443;
> +        else if (!strcasecmp(*res_proto, "http"))
> +            port = 80;
> +        else if (!strcasecmp(*res_proto, "socks") ||
> +                 !strcasecmp(*res_proto, "socks4") ||
> +                 !strcasecmp(*res_proto, "socks5"))
> +            port = 1080;
> +        else
> +            return -EPROTONOSUPPORT;
> +    }
> +
> +    if (res_port != NULL)
> +    {
> +        if (uriInfo->port >= 0)
> +            port = uriInfo->port;
> +        *res_port = port;
> +    }
> +
> +    if (res_host != NULL)
> +        *res_host = strdup(uriInfo->server);
> +
> +    //user information may contain the password too
> +    if (res_auth_uname != NULL && res_auth_pwd != NULL && uriInfo->user != NULL)
> +    {
> +        char * copy = strdup(uriInfo->user);
> +        char * separator = strchr(copy, ':');
> +        if (separator == NULL)
> +            *res_auth_uname = copy;
> +        else
> +        {
> +            *res_auth_uname = copy;
> +            *res_auth_pwd = separator ++;
> +            *separator = '\0';
> +        }
> +    }
> +
> +    if (res_path != NULL && uriInfo->path != NULL)
> +    {
> +        *res_path = strdup(uriInfo->path);
> +    }
> +
> +    xmlFreeURI(uriInfo);
> +
> +	return 0; //Success
>  }
>  
>  static void clear_cookies(struct openconnect_info *vpninfo)
> @@ -758,7 +755,7 @@ static int handle_redirect(struct openconnect_info *vpninfo)
>  		free(vpninfo->urlpath);
>  		vpninfo->urlpath = NULL;
>  
> -		ret = internal_parse_url(vpninfo->redirect_url, NULL, &host, &port, &vpninfo->urlpath, 0);
> +		ret = internal_parse_url(vpninfo->redirect_url, NULL, &host, &port, NULL, NULL, &vpninfo->urlpath, 0);
>  		if (ret) {
>  			vpn_progress(vpninfo, PRG_ERR,
>  				     _("Failed to parse redirected URL '%s': %s\n"),
> @@ -1533,6 +1530,19 @@ static int process_http_proxy(struct openconnect_info *vpninfo, int ssl_sock)
>  		return -EINVAL;
>  	}
>  
> +    //Check for authentication demand from the proxy
> +    //TODO her, here here!
> +    if (result == 407)
> +    {
> +        //Step 1: extract the token(s) provided and the method(s) supported
> +        //
> +        //Step 2: encode the usernmae+password combination in base64
> +        //
> +        //Step 3: Add the tokens and auth info
> +        //
> +        //Step 4: Send to proxy and get response
> +    }
> +
>  	if (result != 200) {
>  		vpn_progress(vpninfo, PRG_ERR,
>  			     _("Proxy CONNECT request failed: %s\n"), buf);
> @@ -1581,7 +1591,7 @@ int openconnect_set_http_proxy(struct openconnect_info *vpninfo, char *proxy)
>  	vpninfo->proxy = NULL;
>  
>  	ret = internal_parse_url(url, &vpninfo->proxy_type, &vpninfo->proxy,
> -				 &vpninfo->proxy_port, NULL, 80);
> +				 &vpninfo->proxy_port, &vpninfo->proxy_auth_uname, &vpninfo->proxy_auth_pwd, NULL, 80);
>  	if (ret)
>  		goto out;
>  
> diff --git a/library.c b/library.c
> index ae91e8d..0a8ee3c 100644
> --- a/library.c
> +++ b/library.c
> @@ -343,7 +343,7 @@ int openconnect_parse_url(struct openconnect_info *vpninfo, char *url)
>  	vpninfo->urlpath = NULL;
>  
>  	ret = internal_parse_url(url, &scheme, &vpninfo->hostname,
> -				 &vpninfo->port, &vpninfo->urlpath, 443);
> +				 &vpninfo->port, NULL, NULL, &vpninfo->urlpath, 443);
>  
>  	if (ret) {
>  		vpn_progress(vpninfo, PRG_ERR,
> 
> 



More information about the openconnect-devel mailing list