Пример #1
0
static struct oc_text_buf *get_qs(char **str)
{
	struct oc_text_buf *res;
	int escaped = 0;
	char *p = *str;

	if (*p != '\"')
		return NULL;

	res = buf_alloc();

	while (*++p) {
		if (!escaped && *p == '\"') {
			*str = p+1;
			if (buf_error(res))
				break;
			return res;
		}
		if (escaped)
			escaped = 0;
		else if (*p == '\\')
			escaped = 1;
		buf_append_bytes(res, p, 1);
	}
	buf_free(res);
	return NULL;
}
Пример #2
0
static SSL_SESSION *generate_dtls_session(struct openconnect_info *vpninfo,
					  int dtlsver, const SSL_CIPHER *cipher)
{
	struct oc_text_buf *buf = buf_alloc();
	SSL_SESSION *dtls_session;
	const unsigned char *asn;
	uint16_t cid;

	buf_append_bytes(buf, "\x30\x80", 2); // SEQUENCE, indeterminate length
	buf_append_INTEGER(buf, 1 /* SSL_SESSION_ASN1_VERSION */);
	buf_append_INTEGER(buf, dtlsver);
	store_be16(&cid, SSL_CIPHER_get_id(cipher) & 0xffff);
	buf_append_OCTET_STRING(buf, &cid, 2);
	buf_append_OCTET_STRING(buf, vpninfo->dtls_session_id,
				sizeof(vpninfo->dtls_session_id));
	buf_append_OCTET_STRING(buf, vpninfo->dtls_secret,
				sizeof(vpninfo->dtls_secret));
	/* If the length actually fits in one byte (which it should), do
	 * it that way.  Else, leave it indeterminate and add two
	 * end-of-contents octets to mark the end of the SEQUENCE. */
	if (!buf_error(buf) && buf->pos <= 0x80)
		buf->data[1] = buf->pos - 2;
	else
		buf_append_bytes(buf, "\0\0", 2);

	if (buf_error(buf)) {
		vpn_progress(vpninfo, PRG_ERR,
			     _("Failed to create SSL_SESSION ASN.1 for OpenSSL: %s\n"),
			     strerror(buf_error(buf)));
		buf_free(buf);
		return NULL;
	}

	asn = (void *)buf->data;
	dtls_session = d2i_SSL_SESSION(NULL, &asn, buf->pos);
	buf_free(buf);
	if (!dtls_session) {
		vpn_progress(vpninfo, PRG_ERR,
			     _("OpenSSL failed to parse SSL_SESSION ASN.1\n"));
		openconnect_report_ssl_errors(vpninfo);
		return NULL;
	}

	return dtls_session;
}
Пример #3
0
static void
vdev_disk_io_intr(struct buf *bp, void *arg)
{
	zio_t *zio = (zio_t *)arg;

    zio->io_error = buf_error(bp);

	if (zio->io_error == 0 && buf_resid(bp) != 0) {
		zio->io_error = EIO;
	}
	buf_free(bp);
	//zio_next_stage_async(zio);
    zio_interrupt(zio);
}
Пример #4
0
static void
vdev_disk_io_intr(struct buf *bp, void *arg)
{
	zio_t *zio = (zio_t *)arg;

	/*
	 * The rest of the zio stack only deals with EIO, ECKSUM, and ENXIO.
	 * Rather than teach the rest of the stack about other error
	 * possibilities (EFAULT, etc), we normalize the error value here.
	 */
	int error;
	error=buf_error(bp);

	zio->io_error = (error != 0 ? EIO : 0);
	if (zio->io_error == 0 && buf_resid(bp) != 0) {
		zio->io_error = EIO;
	}
	buf_free(bp);

	zio_delay_interrupt(zio);
}
Пример #5
0
/*
 * Vnode op for write
 */
int
spec_write(struct vnop_write_args *ap)
{
	struct vnode *vp = ap->a_vp;
	struct uio *uio = ap->a_uio;
	struct buf *bp;
	daddr64_t bn;
	int bsize, blkmask, bscale;
	int io_sync;
	int devBlockSize=0;
	int n, on;
	int error = 0;
	dev_t dev;

#if DIAGNOSTIC
	if (uio->uio_rw != UIO_WRITE)
		panic("spec_write mode");
	if (UIO_SEG_IS_USER_SPACE(uio->uio_segflg))
		panic("spec_write proc");
#endif

	switch (vp->v_type) {

	case VCHR:
		error = (*cdevsw[major(vp->v_rdev)].d_write)
			(vp->v_rdev, uio, ap->a_ioflag);
		return (error);

	case VBLK:
		if (uio_resid(uio) == 0)
			return (0);
		if (uio->uio_offset < 0)
			return (EINVAL);

		io_sync = (ap->a_ioflag & IO_SYNC);

		dev = (vp->v_rdev);

		devBlockSize = vp->v_specsize;
		if (devBlockSize > PAGE_SIZE)
			return(EINVAL);

	        bscale = PAGE_SIZE / devBlockSize;
		blkmask = bscale - 1;
		bsize = bscale * devBlockSize;
		

		do {
			bn = (daddr64_t)((uio->uio_offset / devBlockSize) &~ blkmask);
			on = uio->uio_offset % bsize;

			n = min((unsigned)(bsize - on), uio_resid(uio));

			/*
			 * Use buf_getblk() as an optimization IFF:
			 *
			 * 1)	We are reading exactly a block on a block
			 *	aligned boundary
			 * 2)	We know the size of the device from spec_open
			 * 3)	The read doesn't span the end of the device
			 *
			 * Otherwise, we fall back on buf_bread().
			 */
			if (n == bsize &&
			    vp->v_specdevsize != (u_int64_t)0 &&
			    (uio->uio_offset + (u_int64_t)n) > vp->v_specdevsize) {
			    /* reduce the size of the read to what is there */
			    n = (uio->uio_offset + (u_int64_t)n) - vp->v_specdevsize;
			}

			if (n == bsize)
			        bp = buf_getblk(vp, bn, bsize, 0, 0, BLK_WRITE);
			else
			        error = (int)buf_bread(vp, bn, bsize, NOCRED, &bp);

			/* Translate downstream error for upstream, if needed */
			if (!error)
				error = (int)buf_error(bp);
			if (error) {
				buf_brelse(bp);
				return (error);
			}
			n = min(n, bsize - buf_resid(bp));

			error = uiomove((char *)0 + buf_dataptr(bp) + on, n, uio);
			if (error) {
				buf_brelse(bp);
				return (error);
			}
			buf_markaged(bp);

			if (io_sync) 
			        error = buf_bwrite(bp);
			else {
			        if ((n + on) == bsize)
				        error = buf_bawrite(bp);
				else
				        error = buf_bdwrite(bp);
			}
		} while (error == 0 && uio_resid(uio) > 0 && n != 0);
		return (error);

	default:
		panic("spec_write type");
	}
	/* NOTREACHED */

	return (0);
}
Пример #6
0
struct dt_dentry * dtread_readfile(FILE *file, unsigned int *maxid, char *md5buf)
{
    int c;
    char ch;
    struct buf_str *bs;
    struct cuckoo_ctx *cu;
    struct umd5_ctx md5;
    struct dtread_data *dr;
    struct dt_dentry *root = NULL;


    LOG_ASSERT(file != NULL, "Bad arguments\n");

    if ((bs = buf_alloc()) == NULL)
        return NULL;
    
    if ((cu = cuckoo_alloc(0)) == NULL)
        goto clear_bs;

    umd5_init(&md5);

    while ((c = fgetc(file)) != EOF) {
        if (c == '\n') {
            if (buf_error(bs))
                goto clear_cu;
            umd5_update(&md5, buf_string(bs), buf_strlen(bs));
            umd5_update(&md5, "\n", 1);
            if (!dtread_readline(buf_string(bs), cu, maxid))
                goto clear_cu;
            buf_clear(bs);
        } else {
            ch = c;
            buf_appendn(bs, &ch, 1);
        }
    }

    if (cuckoo_items(cu) != 1) {
        LOG_ERR("Some directories are left in cockoo tables\n");
        goto clear_cu;
    }

    if ((dr = cuckoo_lookup(cu, 1)) == NULL) {
        LOG_ERR("No root node in cuckoo table\n");
        goto clear_cu;
    }
   
    root = dr->de;
    cuckoo_delete(cu, 1);
    free(dr);
    
    umd5_finish(&md5);
    umd5_value(&md5, md5buf);

clear_cu:
    cuckoo_rfree(cu, dtread_data_free);

clear_bs:
    buf_free(bs);

    return root;
}
Пример #7
0
int connect_https_socket(struct openconnect_info *vpninfo)
{
	int ssl_sock = -1;
	int err;

	if (!vpninfo->port)
		vpninfo->port = 443;

	/* If we're talking to a server which told us it has dynamic DNS, don't
	   just re-use its previous IP address. If we're talking to a proxy, we
	   can use *its* previous IP address. We expect it'll re-do the DNS
	   lookup for the server anyway. */
	if (vpninfo->peer_addr && (!vpninfo->is_dyndns || vpninfo->proxy)) {
	reconnect:
#ifdef SOCK_CLOEXEC
		ssl_sock = socket(vpninfo->peer_addr->sa_family, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_IP);
		if (ssl_sock < 0)
#endif
		{
			ssl_sock = socket(vpninfo->peer_addr->sa_family, SOCK_STREAM, IPPROTO_IP);
			if (ssl_sock < 0) {
#ifdef _WIN32
				err = WSAGetLastError();
#else
				err = -errno;
#endif
				goto reconn_err;
			}
			set_fd_cloexec(ssl_sock);
		}
		err = cancellable_connect(vpninfo, ssl_sock, vpninfo->peer_addr, vpninfo->peer_addrlen);
		if (err) {
			char *errstr;
		reconn_err:
#ifdef _WIN32
			if (err > 0)
				errstr = openconnect__win32_strerror(err);
			else
#endif
				errstr = strerror(-err);
			if (vpninfo->proxy) {
				vpn_progress(vpninfo, PRG_ERR,
					     _("Failed to reconnect to proxy %s: %s\n"),
					     vpninfo->proxy, errstr);
			} else {
				vpn_progress(vpninfo, PRG_ERR,
					     _("Failed to reconnect to host %s: %s\n"),
					     vpninfo->hostname, errstr);
			}
#ifdef _WIN32
			if (err > 0)
				free(errstr);
#endif
			if (ssl_sock >= 0)
				closesocket(ssl_sock);
			ssl_sock = -EINVAL;
			goto out;
		}
	} else {
		struct addrinfo hints, *result, *rp;
		char *hostname;
		char port[6];

		memset(&hints, 0, sizeof(struct addrinfo));
		hints.ai_family = AF_UNSPEC;
		hints.ai_socktype = SOCK_STREAM;
		hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV;
		hints.ai_protocol = 0;
		hints.ai_canonname = NULL;
		hints.ai_addr = NULL;
		hints.ai_next = NULL;

		/* The 'port' variable is a string because it's easier
		   this way than if we pass NULL to getaddrinfo() and
		   then try to fill in the numeric value into
		   different types of returned sockaddr_in{6,}. */
#ifdef LIBPROXY_HDR
		if (vpninfo->proxy_factory) {
			struct oc_text_buf *url_buf = buf_alloc();
			char **proxies;
			int i = 0;

			free(vpninfo->proxy_type);
			vpninfo->proxy_type = NULL;
			free(vpninfo->proxy);
			vpninfo->proxy = NULL;

			buf_append(url_buf, "https://%s", vpninfo->hostname);
			if (vpninfo->port != 443)
				buf_append(url_buf, ":%d", vpninfo->port);
			buf_append(url_buf, "/%s", vpninfo->urlpath?:"");
			if (buf_error(url_buf)) {
				buf_free(url_buf);
				ssl_sock = -ENOMEM;
				goto out;
			}

			proxies = px_proxy_factory_get_proxies(vpninfo->proxy_factory,
							       url_buf->data);
			i = 0;
			while (proxies && proxies[i]) {
				if (!vpninfo->proxy &&
				    (!strncmp(proxies[i], "http://", 7) ||
				     !strncmp(proxies[i], "socks://", 8) ||
				     !strncmp(proxies[i], "socks5://", 9)))
					internal_parse_url(proxies[i], &vpninfo->proxy_type,
						  &vpninfo->proxy, &vpninfo->proxy_port,
						  NULL, 0);
				i++;
			}
			buf_free(url_buf);
			free(proxies);
			if (vpninfo->proxy)
				vpn_progress(vpninfo, PRG_DEBUG,
					     _("Proxy from libproxy: %s://%s:%d/\n"),
					     vpninfo->proxy_type, vpninfo->proxy, vpninfo->port);
		}
#endif
		if (vpninfo->proxy) {
			hostname = vpninfo->proxy;
			snprintf(port, 6, "%d", vpninfo->proxy_port);
		} else {
			hostname = vpninfo->hostname;
			snprintf(port, 6, "%d", vpninfo->port);
		}

		if (hostname[0] == '[' && hostname[strlen(hostname)-1] == ']') {
			hostname = strndup(hostname + 1, strlen(hostname) - 2);
			if (!hostname) {
				ssl_sock = -ENOMEM;
				goto out;
			}
			hints.ai_flags |= AI_NUMERICHOST;
		}

		if (vpninfo->getaddrinfo_override)
			err = vpninfo->getaddrinfo_override(vpninfo->cbdata, hostname, port, &hints, &result);
		else
			err = getaddrinfo(hostname, port, &hints, &result);

		if (err) {
			vpn_progress(vpninfo, PRG_ERR,
				     _("getaddrinfo failed for host '%s': %s\n"),
				     hostname, gai_strerror(err));
			if (hints.ai_flags & AI_NUMERICHOST)
				free(hostname);
			ssl_sock = -EINVAL;
			/* If we were just retrying for dynamic DNS, reconnct using
			   the previously-known IP address */
			if (vpninfo->peer_addr) {
				vpn_progress(vpninfo, PRG_ERR,
					     _("Reconnecting to DynDNS server using previously cached IP address\n"));
				goto reconnect;
			}
			goto out;
		}
		if (hints.ai_flags & AI_NUMERICHOST)
			free(hostname);

		for (rp = result; rp ; rp = rp->ai_next) {
			char host[80];

			host[0] = 0;
			if (!getnameinfo(rp->ai_addr, rp->ai_addrlen, host,
					 sizeof(host), NULL, 0, NI_NUMERICHOST))
				vpn_progress(vpninfo, PRG_DEBUG, vpninfo->proxy_type ?
						     _("Attempting to connect to proxy %s%s%s:%s\n") :
						     _("Attempting to connect to server %s%s%s:%s\n"),
					     rp->ai_family == AF_INET6 ? "[" : "",
					     host,
					     rp->ai_family == AF_INET6 ? "]" : "",
					     port);

			ssl_sock = socket(rp->ai_family, rp->ai_socktype,
					  rp->ai_protocol);
			if (ssl_sock < 0)
				continue;
			set_fd_cloexec(ssl_sock);
			err = cancellable_connect(vpninfo, ssl_sock, rp->ai_addr, rp->ai_addrlen);
			if (!err) {
				/* Store the peer address we actually used, so that DTLS can
				   use it again later */
				free(vpninfo->ip_info.gateway_addr);
				vpninfo->ip_info.gateway_addr = NULL;

				if (host[0]) {
					vpninfo->ip_info.gateway_addr = strdup(host);
					vpn_progress(vpninfo, PRG_INFO, _("Connected to %s%s%s:%s\n"),
						     rp->ai_family == AF_INET6 ? "[" : "",
						     host,
						     rp->ai_family == AF_INET6 ? "]" : "",
						     port);
				}

				free(vpninfo->peer_addr);
				vpninfo->peer_addrlen = 0;
				vpninfo->peer_addr = malloc(rp->ai_addrlen);
				if (!vpninfo->peer_addr) {
					vpn_progress(vpninfo, PRG_ERR,
						     _("Failed to allocate sockaddr storage\n"));
					closesocket(ssl_sock);
					ssl_sock = -ENOMEM;
					goto out;
				}
				vpninfo->peer_addrlen = rp->ai_addrlen;
				memcpy(vpninfo->peer_addr, rp->ai_addr, rp->ai_addrlen);
				/* If no proxy, ensure that we output *this* IP address in
				 * authentication results because we're going to need to
				 * reconnect to the *same* server from the rotation. And with
				 * some trick DNS setups, it might possibly be a "rotation"
				 * even if we only got one result from getaddrinfo() this
				 * time.
				 *
				 * If there's a proxy, we're kind of screwed; we can't know
				 * which IP address we connected to. Perhaps we ought to do
				 * the DNS lookup locally and connect to a specific IP? */
				if (!vpninfo->proxy && host[0]) {
					char *p = malloc(strlen(host) + 3);
					if (p) {
						free(vpninfo->unique_hostname);
						vpninfo->unique_hostname = p;
						if (rp->ai_family == AF_INET6)
							*p++ = '[';
						memcpy(p, host, strlen(host));
						p += strlen(host);
						if (rp->ai_family == AF_INET6)
							*p++ = ']';
						*p = 0;
					}
				}
				break;
			}
			if (host[0]) {
				char *errstr;
#ifdef _WIN32
				if (err > 0)
					errstr = openconnect__win32_strerror(err);
				else
#endif
					errstr = strerror(-err);

				vpn_progress(vpninfo, PRG_INFO, _("Failed to connect to %s%s%s:%s: %s\n"),
					     rp->ai_family == AF_INET6 ? "[" : "",
					     host,
					     rp->ai_family == AF_INET6 ? "]" : "",
					     port, errstr);
#ifdef _WIN32
				if (err > 0)
					free(errstr);
#endif
			}
			closesocket(ssl_sock);
			ssl_sock = -1;

			/* If we're in DynDNS mode but this *was* the cached IP address,
			 * don't bother falling back to it if it didn't work. */
			if (vpninfo->peer_addr && vpninfo->peer_addrlen == rp->ai_addrlen &&
			    match_sockaddr(vpninfo->peer_addr, rp->ai_addr)) {
				vpn_progress(vpninfo, PRG_TRACE,
					     _("Forgetting non-functional previous peer address\n"));
				free(vpninfo->peer_addr);
				vpninfo->peer_addr = 0;
				vpninfo->peer_addrlen = 0;
				free(vpninfo->ip_info.gateway_addr);
				vpninfo->ip_info.gateway_addr = NULL;
			}
		}
		freeaddrinfo(result);

		if (ssl_sock < 0) {
			vpn_progress(vpninfo, PRG_ERR,
				     _("Failed to connect to host %s\n"),
				     vpninfo->proxy?:vpninfo->hostname);
			ssl_sock = -EINVAL;
			if (vpninfo->peer_addr) {
				vpn_progress(vpninfo, PRG_ERR,
					     _("Reconnecting to DynDNS server using previously cached IP address\n"));
				goto reconnect;
			}
			goto out;
		}
	}
Пример #8
0
static wchar_t *create_script_env(struct openconnect_info *vpninfo)
{
	struct oc_vpn_option *opt;
	struct oc_text_buf *envbuf;
	wchar_t **oldenv, **p, *newenv = NULL;
	int nr_envs = 0, i;

	/* _wenviron is NULL until we call _wgetenv() */
	(void)_wgetenv(L"PATH");

	/* Take a copy of _wenviron (but not of its strings) */
	for (p = _wenviron; *p; p++)
		nr_envs++;

	oldenv = malloc(nr_envs * sizeof(*oldenv));
	if (!oldenv)
		return NULL;
	memcpy(oldenv, _wenviron, nr_envs * sizeof(*oldenv));

	envbuf = buf_alloc();

	/* Add the script environment variables, prodding out any members of
	   oldenv which are obsoleted by them. */
	for (opt = vpninfo->script_env; opt && !buf_error(envbuf); opt = opt->next) {
		struct oc_text_buf *buf;

		buf = buf_alloc();
		buf_append_utf16le(buf, opt->option);
		buf_append_utf16le(buf, "=");

		if (buf_error(buf)) {
			buf_free(buf);
			goto err;
		}

		/* See if we can find it in the existing environment */
		for (i = 0; i < nr_envs; i++) {
			if (oldenv[i] &&
			    !wcsncmp((wchar_t *)buf->data, oldenv[i], buf->pos / 2)) {
				oldenv[i] = NULL;
				break;
			}
		}

		if (opt->value) {
			buf_append_bytes(envbuf, buf->data, buf->pos);
			buf_append_utf16le(envbuf, opt->value);
			buf_append_bytes(envbuf, "\0\0", 2);
		}

		buf_free(buf);
	}

	for (i = 0; i < nr_envs && !buf_error(envbuf); i++) {
		if (oldenv[i])
			buf_append_bytes(envbuf, oldenv[i],
					 (wcslen(oldenv[i]) + 1) * sizeof(wchar_t));
	}

	buf_append_bytes(envbuf, "\0\0", 2);

	if (!buf_error(envbuf)) {
		newenv = (wchar_t *)envbuf->data;
		envbuf->data = NULL;
	}

 err:
	free(oldenv);
	buf_free(envbuf);
	return newenv;
}
Пример #9
0
int digest_authorization(struct openconnect_info *vpninfo, int proxy,
			 struct http_auth_state *auth_state,
			 struct oc_text_buf *hdrbuf)
{
	char *chall;
	int ret = -EINVAL;
	int algo = ALGO_MD5;
	int qop_auth = 0;
	int nc = 1;
	struct oc_text_buf *realm = NULL, *nonce = NULL, *opaque = NULL;
	struct oc_text_buf *a1 = NULL, *a2 = NULL, *kd = NULL;
	struct oc_text_buf *cnonce = NULL;
	unsigned char cnonce_random[32];
	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 (auth_state->state < AUTH_AVAILABLE)
		return -EINVAL;

	if (auth_state->state == AUTH_IN_PROGRESS) {
		auth_state->state = AUTH_FAILED;
		return -EAGAIN;
	}

	chall = auth_state->challenge;
	if (!chall)
		return -EINVAL;

	while (*chall) {
		if (!realm && !strncmp(chall, "realm=", 6)) {
			chall += 6;
			realm = get_qs(&chall);
			if (!realm)
				goto err;
		} else if (!nonce && !strncmp(chall, "nonce=", 6)) {
			chall += 6;
			nonce = get_qs(&chall);
			if (!nonce)
				goto err;
		} else if (!strncmp(chall, "qop=", 4)) {
			chall += 4;
			if (strncmp(chall, "\"auth\"", 6)) {
				/* We don't support "auth-int" */
				goto err;
			}
			qop_auth = 1;
			chall += 6;
		} else if (!opaque && !strncmp(chall, "opaque=", 7)) {
			chall += 7;
			opaque = get_qs(&chall);
			if (!opaque)
				goto err;
		} else if (!strncmp(chall, "algorithm=", 10)) {
			chall += 10;
			if (!strncmp(chall, "MD5-sess", 8)) {
				algo = ALGO_MD5_SESS;
				chall += 8;
			} else if (!strncmp(chall, "MD5", 3)) {
				algo = ALGO_MD5;
				chall += 3;
			}
		} else {
			char *p = strchr(chall, '=');
			if (!p)
				goto err;
			p++;
			if (*p == '\"') {
				/* Eat and discard a quoted-string */
				int escaped = 0;
				p++;
				do  {
					if (escaped)
						escaped = 0;
					else if (*p == '\\')
						escaped = 1;
					if (!*p)
						goto err;
				} while (escaped || *p != '\"');
				chall = p+1;
			} else {
				/* Not quoted. Just find the next comma (or EOL) */
				p = strchr(p, ',');
				if (!p)
					break;
				chall = p;
			}
		}
		while (isspace((int)(unsigned char)*chall))
			chall++;
		if (!*chall)
			break;
		if (*chall != ',')
			goto err;
		chall++;
		while (isspace((int)(unsigned char)*chall))
			chall++;
		if (!*chall)
			break;
	}
	if (!nonce || !realm)
		goto err;

	if (openconnect_random(&cnonce_random, sizeof(cnonce_random)))
		goto err;
	cnonce = buf_alloc();
	buf_append_base64(cnonce, cnonce_random, sizeof(cnonce_random));
	if (buf_error(cnonce))
		goto err;

	/*
	 * According to RFC2617 §3.2.2.2:
	 *  A1       = unq(username-value) ":" unq(realm-value) ":" passwd
	 * So the username is escaped, while the password isn't.
	 */
	a1 = buf_alloc();
	buf_append_unq(a1, user);
	buf_append(a1, ":%s:%s", realm->data, pass);
	if (buf_error(a1))
		goto err;
	if (algo == ALGO_MD5_SESS) {
		struct oc_text_buf *old_a1 = a1;

		a1 = buf_alloc();
		buf_append_md5(a1, old_a1->data, old_a1->pos);
		buf_free(old_a1);
		buf_append(a1, ":%s:%s\n", nonce->data, cnonce->data);
		if (buf_error(a1))
			goto err;
	}

	a2 = buf_alloc();
	buf_append(a2, "CONNECT:%s:%d", vpninfo->hostname, vpninfo->port);
	if (buf_error(a2))
		goto err;

	kd = buf_alloc();
	buf_append_md5(kd, a1->data, a1->pos);
	buf_append(kd, ":%s:", nonce->data);
	if (qop_auth) {
		buf_append(kd, "%08x:%s:auth:", nc, cnonce->data);
	}
	buf_append_md5(kd, a2->data, a2->pos);
	if (buf_error(kd))
		goto err;

	buf_append(hdrbuf, "%sAuthorization: Digest username=\"", proxy ? "Proxy-" : "");
	buf_append_unq(hdrbuf, user);
	buf_append(hdrbuf, "\", realm=\"%s\", nonce=\"%s\", uri=\"%s:%d\", ",
		   realm->data, nonce->data, vpninfo->hostname, vpninfo->port);
	if (qop_auth)
		buf_append(hdrbuf, "cnonce=\"%s\", nc=%08x, qop=auth, ",
			   cnonce->data, nc);
	if (opaque)
		buf_append(hdrbuf, "opaque=\"%s\", ", opaque->data);
	buf_append(hdrbuf, "response=\"");
	buf_append_md5(hdrbuf, kd->data, kd->pos);
	buf_append(hdrbuf, "\"\r\n");

	ret = 0;

	auth_state->state = AUTH_IN_PROGRESS;
	if (proxy)
		vpn_progress(vpninfo, PRG_INFO,
			     _("Attempting Digest authentication to proxy\n"));
	else
		vpn_progress(vpninfo, PRG_INFO,
			     _("Attempting Digest authentication to server '%s'\n"),
			     vpninfo->hostname);
 err:
	if (a1 && a1->data)
		memset(a1->data, 0, a1->pos);
	buf_free(a1);
	buf_free(a2);
	buf_free(kd);
	buf_free(realm);
	buf_free(nonce);
	buf_free(cnonce);
	buf_free(opaque);
	return ret;
}