1 /*
2  * OpenConnect (SSL + DTLS) VPN client
3  *
4  * Copyright © 2008-2015 Intel Corporation.
5  * Copyright © 2008 Nick Andrew <nick@nick-andrew.net>
6  *
7  * Author: David Woodhouse <dwmw2@infradead.org>
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * version 2.1, as published by the Free Software Foundation.
12  *
13  * This program is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  */
18 
19 #include <config.h>
20 
21 #include <unistd.h>
22 #include <fcntl.h>
23 #include <time.h>
24 #include <string.h>
25 #include <ctype.h>
26 #include <errno.h>
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <stdarg.h>
30 
31 #include <libxml/uri.h>
32 
33 #include "openconnect-internal.h"
34 
35 static int proxy_write(struct openconnect_info *vpninfo, char *buf, size_t len);
36 static int proxy_read(struct openconnect_info *vpninfo, char *buf, size_t len);
37 
38 #define BUF_CHUNK_SIZE 4096
39 
buf_alloc(void)40 struct oc_text_buf *buf_alloc(void)
41 {
42 	return calloc(1, sizeof(struct oc_text_buf));
43 }
44 
buf_append_urlencoded(struct oc_text_buf * buf,const char * str)45 void buf_append_urlencoded(struct oc_text_buf *buf, const char *str)
46 {
47 	while (str && *str) {
48 		unsigned char c = *str;
49 		if (c < 0x80 && (isalnum((int)(c)) || c=='-' || c=='_' || c=='.' || c=='~'))
50 			buf_append_bytes(buf, str, 1);
51 		else
52 			buf_append(buf, "%%%02x", c);
53 
54 		str++;
55 	}
56 }
57 
buf_append_xmlescaped(struct oc_text_buf * buf,const char * str)58 void buf_append_xmlescaped(struct oc_text_buf *buf, const char *str)
59 {
60 	while (str && *str) {
61 		unsigned char c = *str;
62 		if (c=='<' || c=='>' || c=='&' || c=='"' || c=='\'')
63 			buf_append(buf, "&#x%02x;", c);
64 		else
65 			buf_append_bytes(buf, str, 1);
66 
67 		str++;
68 	}
69 }
70 
buf_append_hex(struct oc_text_buf * buf,const void * str,unsigned len)71 void buf_append_hex(struct oc_text_buf *buf, const void *str, unsigned len)
72 {
73 	const unsigned char *data = str;
74 	unsigned i;
75 
76 	for (i = 0; i < len; i++)
77 		buf_append(buf, "%02x", (unsigned)data[i]);
78 }
79 
buf_truncate(struct oc_text_buf * buf)80 void buf_truncate(struct oc_text_buf *buf)
81 {
82 	if (!buf)
83 		return;
84 
85 	if (buf->data)
86 		memset(buf->data, 0, buf->pos);
87 
88 	buf->pos = 0;
89 }
90 
buf_ensure_space(struct oc_text_buf * buf,int len)91 int buf_ensure_space(struct oc_text_buf *buf, int len)
92 {
93 	unsigned int new_buf_len;
94 
95 	if (!buf)
96 		return -ENOMEM;
97 
98 	new_buf_len = (buf->pos + len + BUF_CHUNK_SIZE - 1) & ~(BUF_CHUNK_SIZE - 1);
99 
100 	if (new_buf_len <= buf->buf_len)
101 		return 0;
102 
103 	if (new_buf_len > INT_MAX) {
104 		buf->error = -E2BIG;
105 		return buf->error;
106 	} else {
107 		realloc_inplace(buf->data, new_buf_len);
108 		if (!buf->data)
109 			buf->error = -ENOMEM;
110 		else
111 			buf->buf_len = new_buf_len;
112 	}
113 	return buf->error;
114 }
115 
116 void  __attribute__ ((format (printf, 2, 3)))
buf_append(struct oc_text_buf * buf,const char * fmt,...)117 	buf_append(struct oc_text_buf *buf, const char *fmt, ...)
118 {
119 	va_list ap;
120 
121 	if (!buf || buf->error)
122 		return;
123 
124 	if (buf_ensure_space(buf, 1))
125 		return;
126 
127 	while (1) {
128 		int max_len = buf->buf_len - buf->pos, ret;
129 
130 		va_start(ap, fmt);
131 		ret = vsnprintf(buf->data + buf->pos, max_len, fmt, ap);
132 		va_end(ap);
133 		if (ret < 0) {
134 			buf->error = -EIO;
135 			break;
136 		} else if (ret < max_len) {
137 			buf->pos += ret;
138 			break;
139 		} else if (buf_ensure_space(buf, ret))
140 			break;
141 	}
142 }
143 
buf_append_bytes(struct oc_text_buf * buf,const void * bytes,int len)144 void buf_append_bytes(struct oc_text_buf *buf, const void *bytes, int len)
145 {
146 	if (!buf || buf->error)
147 		return;
148 
149 	if (buf_ensure_space(buf, len + 1))
150 		return;
151 
152 	memcpy(buf->data + buf->pos, bytes, len);
153 	buf->pos += len;
154 	buf->data[buf->pos] = 0;
155 }
156 
buf_append_from_utf16le(struct oc_text_buf * buf,const void * _utf16)157 void buf_append_from_utf16le(struct oc_text_buf *buf, const void *_utf16)
158 {
159 	const unsigned char *utf16 = _utf16;
160 	unsigned char utf8[4];
161 	int c;
162 
163 	if (!utf16)
164 		return;
165 
166 	while (utf16[0] || utf16[1]) {
167 		if ((utf16[1] & 0xfc) == 0xd8 && (utf16[3] & 0xfc) == 0xdc) {
168 			c = ((load_le16(utf16) & 0x3ff) << 10)|
169 				(load_le16(utf16 + 2) & 0x3ff);
170 			c += 0x10000;
171 			utf16 += 4;
172 		} else {
173 			c = load_le16(utf16);
174 			utf16 += 2;
175 		}
176 
177 		if (c < 0x80) {
178 			utf8[0] = c;
179 			buf_append_bytes(buf, utf8, 1);
180 		} else if (c < 0x800) {
181 			utf8[0] = 0xc0 | (c >> 6);
182 			utf8[1] = 0x80 | (c & 0x3f);
183 			buf_append_bytes(buf, utf8, 2);
184 		} else if (c < 0x10000) {
185 			utf8[0] = 0xe0 | (c >> 12);
186 			utf8[1] = 0x80 | ((c >> 6) & 0x3f);
187 			utf8[2] = 0x80 | (c & 0x3f);
188 			buf_append_bytes(buf, utf8, 3);
189 		} else {
190 			utf8[0] = 0xf0 | (c >> 18);
191 			utf8[1] = 0x80 | ((c >> 12) & 0x3f);
192 			utf8[2] = 0x80 | ((c >> 6) & 0x3f);
193 			utf8[3] = 0x80 | (c & 0x3f);
194 			buf_append_bytes(buf, utf8, 4);
195 		}
196 	}
197 	utf8[0] = 0;
198 	buf_append_bytes(buf, utf8, 1);
199 }
200 
get_utf8char(const char ** p)201 int get_utf8char(const char **p)
202 {
203 	const char *utf8 = *p;
204 	unsigned char c;
205 	int utfchar, nr_extra, min;
206 
207 	c = *(utf8++);
208 	if (c < 128) {
209 		utfchar = c;
210 		nr_extra = 0;
211 		min = 0;
212 	} else if ((c & 0xe0) == 0xc0) {
213 		utfchar = c & 0x1f;
214 		nr_extra = 1;
215 		min = 0x80;
216 	} else if ((c & 0xf0) == 0xe0) {
217 		utfchar = c & 0x0f;
218 		nr_extra = 2;
219 		min = 0x800;
220 	} else if ((c & 0xf8) == 0xf0) {
221 		utfchar = c & 0x07;
222 		nr_extra = 3;
223 		min = 0x10000;
224 	} else {
225 		return -EILSEQ;
226 	}
227 
228 	while (nr_extra--) {
229 		c = *(utf8++);
230 		if ((c & 0xc0) != 0x80)
231 			return -EILSEQ;
232 
233 		utfchar <<= 6;
234 		utfchar |= (c & 0x3f);
235 	}
236 	if (utfchar > 0x10ffff || utfchar < min)
237 		return -EILSEQ;
238 
239 	*p = utf8;
240 	return utfchar;
241 }
242 
buf_append_utf16le(struct oc_text_buf * buf,const char * utf8)243 int buf_append_utf16le(struct oc_text_buf *buf, const char *utf8)
244 {
245 	int utfchar, len = 0;
246 
247 	/* Ick. Now I'm implementing my own UTF8 handling too. Perhaps it's
248 	   time to bite the bullet and start requiring something like glib? */
249 	while (*utf8) {
250 		utfchar = get_utf8char(&utf8);
251 		if (utfchar < 0) {
252 			if (buf)
253 				buf->error = utfchar;
254 			return utfchar;
255 		}
256 		if (!buf)
257 			continue;
258 
259 		if (utfchar >= 0x10000) {
260 			utfchar -= 0x10000;
261 			if (buf_ensure_space(buf, 4))
262 				return buf_error(buf);
263 			store_le16(buf->data + buf->pos, (utfchar >> 10) | 0xd800);
264 			store_le16(buf->data + buf->pos + 2, (utfchar & 0x3ff) | 0xdc00);
265 			buf->pos += 4;
266 			len += 4;
267 		} else {
268 			if (buf_ensure_space(buf, 2))
269 				return buf_error(buf);
270 			store_le16(buf->data + buf->pos, utfchar);
271 			buf->pos += 2;
272 			len += 2;
273 		}
274 	}
275 
276 	/* We were only being used for validation */
277 	if (!buf)
278 		return 0;
279 
280 	/* Ensure UTF16 is NUL-terminated */
281 	if (buf_ensure_space(buf, 2))
282 		return buf_error(buf);
283 	buf->data[buf->pos] = buf->data[buf->pos + 1] = 0;
284 
285 	return len;
286 }
287 
buf_error(struct oc_text_buf * buf)288 int buf_error(struct oc_text_buf *buf)
289 {
290 	return buf ? buf->error : -ENOMEM;
291 }
292 
buf_free(struct oc_text_buf * buf)293 int buf_free(struct oc_text_buf *buf)
294 {
295 	int error = buf_error(buf);
296 
297 	if (buf) {
298 		buf_truncate(buf);
299 		if (buf->data)
300 			free(buf->data);
301 		free(buf);
302 	}
303 
304 	return error;
305 }
306 
307 /*
308  * We didn't really want to have to do this for ourselves -- one might have
309  * thought that it would be available in a library somewhere. But neither
310  * cURL nor Neon have reliable cross-platform ways of either using a cert
311  * from the TPM, or just reading from / writing to a transport which is
312  * provided by their caller.
313  */
314 
http_add_cookie(struct openconnect_info * vpninfo,const char * option,const char * value,int replace)315 int http_add_cookie(struct openconnect_info *vpninfo, const char *option,
316 		    const char *value, int replace)
317 {
318 	struct oc_vpn_option *new, **this;
319 
320 	if (*value) {
321 		new = malloc(sizeof(*new));
322 		if (!new) {
323 			vpn_progress(vpninfo, PRG_ERR,
324 				     _("No memory for allocating cookies\n"));
325 			return -ENOMEM;
326 		}
327 		new->next = NULL;
328 		new->option = strdup(option);
329 		new->value = strdup(value);
330 		if (!new->option || !new->value) {
331 			free(new->option);
332 			free(new->value);
333 			free(new);
334 			return -ENOMEM;
335 		}
336 	} else {
337 		/* Kill cookie; don't replace it */
338 		new = NULL;
339 		/* This would be meaningless */
340 		if (!replace)
341 			return -EINVAL;
342 	}
343 	for (this = &vpninfo->cookies; *this; this = &(*this)->next) {
344 		if (!strcmp(option, (*this)->option)) {
345 			if (!replace) {
346 				free(new->value);
347 				free(new->option);
348 				free(new);
349 				return 0;
350 			}
351 			/* Replace existing cookie */
352 			if (new)
353 				new->next = (*this)->next;
354 			else
355 				new = (*this)->next;
356 
357 			free((*this)->option);
358 			free((*this)->value);
359 			free(*this);
360 			*this = new;
361 			break;
362 		}
363 	}
364 	if (new && !*this) {
365 		*this = new;
366 		new->next = NULL;
367 	}
368 	return 0;
369 }
370 
371 /* Read one HTTP header line into hdrbuf, potentially allowing for
372  * continuation lines. Will never leave a character in 'nextchar' when
373  * an empty line (signifying end of headers) is received. Will only
374  * return success when hdrbuf is valid. */
read_http_header(struct openconnect_info * vpninfo,char * nextchar,struct oc_text_buf * hdrbuf,int allow_cont)375 static int read_http_header(struct openconnect_info *vpninfo, char *nextchar,
376 			    struct oc_text_buf *hdrbuf, int allow_cont)
377 {
378 	int eol = 0;
379 	int ret;
380 	char c;
381 
382 
383 	buf_truncate(hdrbuf);
384 
385 	c = *nextchar;
386 	if (c) {
387 		*nextchar = 0;
388 		goto skip_first;
389 	}
390 
391 	while (1) {
392 		ret = vpninfo->ssl_read(vpninfo, &c, 1);
393 		if (ret < 0)
394 			return ret;
395 		if (ret != 1)
396 			return -EINVAL;
397 
398 		/* If we were looking for a continuation line and didn't get it,
399 		 * stash the character we *did* get into *nextchar for next time. */
400 		if (eol && c != ' ' && c != '\t') {
401 			*nextchar = c;
402 			return buf_error(hdrbuf);
403 		}
404 		eol = 0;
405 
406 	skip_first:
407 		if (c == '\n') {
408 			if (!buf_error(hdrbuf) && hdrbuf->pos &&
409 			    hdrbuf->data[hdrbuf->pos - 1] == '\r') {
410 				hdrbuf->pos--;
411 				hdrbuf->data[hdrbuf->pos] = 0;
412 			}
413 
414 			/* For a non-empty header line, see if there's a continuation */
415 			if (allow_cont && hdrbuf->pos) {
416 				eol = 1;
417 				continue;
418 			}
419 
420 			return buf_error(hdrbuf);
421 		}
422 		buf_append_bytes(hdrbuf, &c, 1);
423 	}
424 	return buf_error(hdrbuf);
425 }
426 
427 #define BODY_HTTP10 -1
428 #define BODY_CHUNKED -2
429 
process_http_response(struct openconnect_info * vpninfo,int connect,int (* header_cb)(struct openconnect_info *,char *,char *),struct oc_text_buf * body)430 int process_http_response(struct openconnect_info *vpninfo, int connect,
431 			  int (*header_cb)(struct openconnect_info *, char *, char *),
432 			  struct oc_text_buf *body)
433 {
434 	struct oc_text_buf *hdrbuf = buf_alloc();
435 	char nextchar = 0;
436 	int bodylen = BODY_HTTP10;
437 	int closeconn = 0;
438 	int result;
439 	int ret = -EINVAL;
440 	int i;
441 
442 	buf_truncate(body);
443 
444 	/* Ensure it has *something* in it, so that we can dereference hdrbuf->data
445 	 * later without checking (for anything except buf_error(hdrbuf), which is
446 	 * what read_http_header() uses for its return code anyway). */
447 	buf_append_bytes(hdrbuf, "\0", 1);
448  cont:
449 	ret = read_http_header(vpninfo, &nextchar, hdrbuf, 0);
450 	if (ret) {
451 		vpn_progress(vpninfo, PRG_ERR, _("Error reading HTTP response: %s\n"),
452 			     strerror(-ret));
453 		goto err;
454 	}
455 
456 	if (!strncmp(hdrbuf->data, "HTTP/1.0 ", 9))
457 		closeconn = 1;
458 
459 	if ((!closeconn && strncmp(hdrbuf->data, "HTTP/1.1 ", 9)) ||
460 	    !(result = atoi(hdrbuf->data + 9))) {
461 		vpn_progress(vpninfo, PRG_ERR,
462 			     _("Failed to parse HTTP response '%s'\n"), hdrbuf->data);
463 		ret = -EINVAL;
464 		goto err;
465 	}
466 
467 	vpn_progress(vpninfo, (result == 200 || result == 407) ? PRG_DEBUG : PRG_INFO,
468 		     _("Got HTTP response: %s\n"), hdrbuf->data);
469 
470 	/* Eat headers... */
471 	while (1) {
472 		char *colon;
473 		char *hdrline;
474 
475 		ret = read_http_header(vpninfo, &nextchar, hdrbuf, 1);
476 		if (ret) {
477 			vpn_progress(vpninfo, PRG_ERR, _("Error reading HTTP response: %s\n"),
478 				     strerror(-ret));
479 			goto err;
480 		}
481 		/* Default error case */
482 		ret = -EINVAL;
483 
484 		/* Empty line ends headers */
485 		if (!hdrbuf->pos)
486 			break;
487 
488 		hdrline = hdrbuf->data;
489 
490 		colon = strchr(hdrline, ':');
491 		if (!colon) {
492 			vpn_progress(vpninfo, PRG_ERR,
493 				     _("Ignoring unknown HTTP response line '%s'\n"), hdrline);
494 			continue;
495 		}
496 		*(colon++) = 0;
497 		if (*colon == ' ')
498 			colon++;
499 
500 		/* Handle Set-Cookie first so that we can avoid printing the
501 		   webvpn cookie in the verbose debug output */
502 		if (!strcasecmp(hdrline, "Set-Cookie")) {
503 			char *semicolon = strchr(colon, ';');
504 			const char *print_equals;
505 			char *equals = strchr(colon, '=');
506 
507 			if (semicolon)
508 				*semicolon = 0;
509 
510 			if (!equals) {
511 				vpn_progress(vpninfo, PRG_ERR,
512 					     _("Invalid cookie offered: %s\n"), hdrline);
513 				ret = -EINVAL;
514 				goto err;
515 			}
516 			*(equals++) = 0;
517 
518 			print_equals = equals;
519 			/* Don't print the webvpn cookie unless it's empty; we don't
520 			   want people posting it in public with debugging output */
521 			if (!strcmp(colon, "webvpn") && *equals)
522 				print_equals = _("<elided>");
523 			vpn_progress(vpninfo, PRG_DEBUG, "%s: %s=%s%s%s\n",
524 				hdrline, colon, print_equals, semicolon ? ";" : "",
525 				     semicolon ? (semicolon+1) : "");
526 
527 			/* The server tends to ask for the username and password as
528 			   usual, even if we've already failed because it didn't like
529 			   our cert. Thankfully it does give us this hint... */
530 			if (!strcmp(colon, "ClientCertAuthFailed"))
531 				vpn_progress(vpninfo, PRG_ERR,
532 					     _("SSL certificate authentication failed\n"));
533 
534 			ret = http_add_cookie(vpninfo, colon, equals, 1);
535 			if (ret)
536 				goto err;
537 		} else {
538 			vpn_progress(vpninfo, PRG_DEBUG, "%s: %s\n", hdrline, colon);
539 		}
540 
541 		if (!strcasecmp(hdrline, "Connection")) {
542 			if (!strcasecmp(colon, "Close"))
543 				closeconn = 1;
544 #if 0
545 			/* This might seem reasonable, but in fact it breaks
546 			   certificate authentication with some servers. If
547 			   they give an HTTP/1.0 response, even if they
548 			   explicitly give a Connection: Keep-Alive header,
549 			   just close the connection. */
550 			else if (!strcasecmp(colon, "Keep-Alive"))
551 				closeconn = 0;
552 #endif
553 		}
554 		if (!strcasecmp(hdrline, "Location")) {
555 			vpninfo->redirect_url = strdup(colon);
556 			if (!vpninfo->redirect_url) {
557 				ret = -ENOMEM;
558 				goto err;
559 			}
560 		}
561 		if (!strcasecmp(hdrline, "Content-Length")) {
562 			bodylen = atoi(colon);
563 			if (bodylen < 0) {
564 				vpn_progress(vpninfo, PRG_ERR,
565 					     _("Response body has negative size (%d)\n"),
566 					     bodylen);
567 				ret = -EINVAL;
568 				goto err;
569 			}
570 		}
571 		if (!strcasecmp(hdrline, "Transfer-Encoding")) {
572 			if (!strcasecmp(colon, "chunked"))
573 				bodylen = BODY_CHUNKED;
574 			else {
575 				vpn_progress(vpninfo, PRG_ERR,
576 					     _("Unknown Transfer-Encoding: %s\n"),
577 					     colon);
578 				ret = -EINVAL;
579 				goto err;
580 			}
581 		}
582 		if (header_cb)
583 			header_cb(vpninfo, hdrline, colon);
584 	}
585 
586 	/* Handle 'HTTP/1.1 100 Continue'. Not that we should ever see it */
587 	if (result == 100)
588 		goto cont;
589 
590 	/* On successful CONNECT or upgrade, there is no body. Return success */
591 	if (connect && (result == 200 || result == 101)) {
592 		buf_free(hdrbuf);
593 		return result;
594 	}
595 
596 	/* Now the body, if there is one */
597 	vpn_progress(vpninfo, PRG_DEBUG, _("HTTP body %s (%d)\n"),
598 		     bodylen == BODY_HTTP10 ? "http 1.0" :
599 		     bodylen == BODY_CHUNKED ? "chunked" : "length: ",
600 		     bodylen);
601 
602 	/* If we were given Content-Length, it's nice and easy... */
603 	if (bodylen > 0) {
604 		if (buf_ensure_space(body, bodylen + 1)) {
605 			ret = buf_error(body);
606 			goto err;
607 		}
608 
609 		while (body->pos < bodylen) {
610 			i = vpninfo->ssl_read(vpninfo, body->data + body->pos, bodylen - body->pos);
611 			if (i < 0) {
612 				vpn_progress(vpninfo, PRG_ERR,
613 					     _("Error reading HTTP response body\n"));
614 				ret = i;
615 				goto err;
616 			}
617 			body->pos += i;
618 		}
619 	} else if (bodylen == BODY_CHUNKED) {
620 		char clen_buf[16];
621 		/* ... else, chunked */
622 		while ((i = vpninfo->ssl_gets(vpninfo, clen_buf, sizeof(clen_buf)))) {
623 			int lastchunk = 0;
624 			long chunklen;
625 
626 			if (i < 0) {
627 				vpn_progress(vpninfo, PRG_ERR,
628 					     _("Error fetching chunk header\n"));
629 				ret = i;
630 				goto err;
631 			}
632 			chunklen = strtol(clen_buf, NULL, 16);
633 			if (!chunklen) {
634 				lastchunk = 1;
635 				goto skip;
636 			}
637 			if (chunklen < 0) {
638 				vpn_progress(vpninfo, PRG_ERR,
639 					     _("HTTP chunk length is negative (%ld)\n"), chunklen);
640 				ret = -EINVAL;
641 				goto err;
642 			}
643 			if (chunklen >= INT_MAX) {
644 				vpn_progress(vpninfo, PRG_ERR,
645 					     _("HTTP chunk length is too large (%ld)\n"), chunklen);
646 				ret = -EINVAL;
647 				goto err;
648 			}
649 			if (buf_ensure_space(body, chunklen + 1)) {
650 				ret = buf_error(body);
651 				goto err;
652 			}
653 			while (chunklen) {
654 				i = vpninfo->ssl_read(vpninfo, body->data + body->pos, chunklen);
655 				if (i < 0) {
656 					vpn_progress(vpninfo, PRG_ERR,
657 						     _("Error reading HTTP response body\n"));
658 					ret = i;
659 					goto err;
660 				}
661 				chunklen -= i;
662 				body->pos += i;
663 			}
664 		skip:
665 			if ((i = vpninfo->ssl_gets(vpninfo, clen_buf, sizeof(clen_buf)))) {
666 				if (i < 0) {
667 					vpn_progress(vpninfo, PRG_ERR,
668 						     _("Error fetching HTTP response body\n"));
669 					ret = i;
670 				} else {
671 					vpn_progress(vpninfo, PRG_ERR,
672 						     _("Error in chunked decoding. Expected '', got: '%s'"),
673 						     clen_buf);
674 					ret = -EINVAL;
675 				}
676 				goto err;
677 			}
678 
679 			if (lastchunk)
680 				break;
681 		}
682 	} else if (bodylen == BODY_HTTP10) {
683 		if (!closeconn) {
684 			vpn_progress(vpninfo, PRG_ERR,
685 				     _("Cannot receive HTTP 1.0 body without closing connection\n"));
686 			openconnect_close_https(vpninfo, 0);
687 			return -EINVAL;
688 		}
689 
690 		/* HTTP 1.0 response. Just eat all we can in 4KiB chunks */
691 		while (1) {
692 			if (buf_ensure_space(body, 4096 + 1)) {
693 				ret = buf_error(body);
694 				goto err;
695 			}
696 			i = vpninfo->ssl_read(vpninfo, body->data + body->pos, 4096);
697 			if (i < 0) {
698 				/* Error */
699 				ret = i;
700 				goto err;
701 			} else if (!i)
702 				break;
703 
704 			/* Got more data */
705 			body->pos += i;
706 		}
707 	}
708 
709 	body->data[body->pos] = 0;
710 	ret = result;
711 
712 	if (closeconn || vpninfo->no_http_keepalive) {
713 	err:
714 		openconnect_close_https(vpninfo, 0);
715 	}
716 	buf_free(hdrbuf);
717 	return ret;
718 }
719 
internal_parse_url(const char * url,char ** res_proto,char ** res_host,int * res_port,char ** res_path,int default_port)720 int internal_parse_url(const char *url, char **res_proto, char **res_host,
721 		       int *res_port, char **res_path, int default_port)
722 {
723 	const char *orig_host, *orig_path;
724 	char *host, *port_str;
725 	int port, proto_len = 0;
726 
727 	orig_host = strstr(url, "://");
728 	if (orig_host) {
729 		proto_len = orig_host - url;
730 		orig_host += 3;
731 
732 		if (strprefix_match(url, proto_len, "https"))
733 			port = 443;
734 		else if (strprefix_match(url, proto_len, "http"))
735 			port = 80;
736 		else if (strprefix_match(url, proto_len, "socks") ||
737 			 strprefix_match(url, proto_len, "socks4") ||
738 			 strprefix_match(url, proto_len, "socks5"))
739 			port = 1080;
740 		else
741 			return -EPROTONOSUPPORT;
742 	} else {
743 		if (default_port) {
744 			port = default_port;
745 			orig_host = url;
746 		} else
747 			return -EINVAL;
748 	}
749 
750 	orig_path = strchr(orig_host, '/');
751 	if (orig_path) {
752 		host = strndup(orig_host, orig_path - orig_host);
753 		orig_path++;
754 	} else
755 		host = strdup(orig_host);
756 	if (!host)
757 		return -ENOMEM;
758 
759 	port_str = strrchr(host, ':');
760 	if (port_str) {
761 		char *end;
762 		int new_port = strtol(port_str + 1, &end, 10);
763 
764 		if (!*end) {
765 			*port_str = 0;
766 			port = new_port;
767 		}
768 	}
769 
770 	if (res_proto)
771 		*res_proto = proto_len ? strndup(url, proto_len) : NULL;
772 	if (res_host)
773 		*res_host = host;
774 	else
775 		free(host);
776 	if (res_port)
777 		*res_port = port;
778 	if (res_path)
779 		*res_path = (orig_path && *orig_path) ? strdup(orig_path) : NULL;
780 
781 	return 0;
782 }
783 
openconnect_clear_cookies(struct openconnect_info * vpninfo)784 void openconnect_clear_cookies(struct openconnect_info *vpninfo)
785 {
786 	struct oc_vpn_option *opt, *next;
787 
788 	for (opt = vpninfo->cookies; opt; opt = next) {
789 		next = opt->next;
790 
791 		free(opt->option);
792 		free(opt->value);
793 		free(opt);
794 	}
795 	vpninfo->cookies = NULL;
796 }
797 
798 /* Return value:
799  *  < 0, on error
800  *  = 0, on success (go ahead and retry with the latest vpninfo->{hostname,urlpath,port,...})
801  */
handle_redirect(struct openconnect_info * vpninfo)802 int handle_redirect(struct openconnect_info *vpninfo)
803 {
804 	vpninfo->redirect_type = REDIR_TYPE_LOCAL;
805 
806 	if (!strncmp(vpninfo->redirect_url, "https://", 8)) {
807 		/* New host. Tear down the existing connection and make a new one */
808 		char *host;
809 		int port;
810 		int ret;
811 
812 		free(vpninfo->urlpath);
813 		vpninfo->urlpath = NULL;
814 
815 		ret = internal_parse_url(vpninfo->redirect_url, NULL, &host, &port, &vpninfo->urlpath, 0);
816 		if (ret) {
817 			vpn_progress(vpninfo, PRG_ERR,
818 				     _("Failed to parse redirected URL '%s': %s\n"),
819 				     vpninfo->redirect_url, strerror(-ret));
820 			free(vpninfo->redirect_url);
821 			vpninfo->redirect_url = NULL;
822 			return ret;
823 		}
824 
825 		if (strcasecmp(vpninfo->hostname, host) || port != vpninfo->port) {
826 			openconnect_set_hostname(vpninfo, host);
827 			vpninfo->port = port;
828 
829 			/* Kill the existing connection, and a new one will happen */
830 			openconnect_close_https(vpninfo, 0);
831 			openconnect_clear_cookies(vpninfo);
832 			vpninfo->redirect_type = REDIR_TYPE_NEWHOST;
833 		}
834 		free(host);
835 
836 		free(vpninfo->redirect_url);
837 		vpninfo->redirect_url = NULL;
838 
839 		return 0;
840 	} else if (strstr(vpninfo->redirect_url, "://")) {
841 		vpn_progress(vpninfo, PRG_ERR,
842 			     _("Cannot follow redirection to non-https URL '%s'\n"),
843 			     vpninfo->redirect_url);
844 		free(vpninfo->redirect_url);
845 		vpninfo->redirect_url = NULL;
846 		return -EINVAL;
847 	} else if (vpninfo->redirect_url[0] == '/') {
848 		/* Absolute redirect within same host */
849 		free(vpninfo->urlpath);
850 		vpninfo->urlpath = strdup(vpninfo->redirect_url + 1);
851 		free(vpninfo->redirect_url);
852 		vpninfo->redirect_url = NULL;
853 		return 0;
854 	} else {
855 		char *lastslash = NULL;
856 		if (vpninfo->urlpath)
857 			lastslash = strrchr(vpninfo->urlpath, '/');
858 		if (!lastslash) {
859 			free(vpninfo->urlpath);
860 			vpninfo->urlpath = vpninfo->redirect_url;
861 			vpninfo->redirect_url = NULL;
862 		} else {
863 			char *oldurl = vpninfo->urlpath;
864 			*lastslash = 0;
865 			vpninfo->urlpath = NULL;
866 			if (asprintf(&vpninfo->urlpath, "%s/%s",
867 				     oldurl, vpninfo->redirect_url) == -1) {
868 				int err = -errno;
869 				vpn_progress(vpninfo, PRG_ERR,
870 					     _("Allocating new path for relative redirect failed: %s\n"),
871 					     strerror(-err));
872 				return err;
873 			}
874 			free(oldurl);
875 			free(vpninfo->redirect_url);
876 			vpninfo->redirect_url = NULL;
877 		}
878 		return 0;
879 	}
880 }
881 
dump_buf(struct openconnect_info * vpninfo,char prefix,char * buf)882 void dump_buf(struct openconnect_info *vpninfo, char prefix, char *buf)
883 {
884 	while (*buf) {
885 		char *eol = buf;
886 		char eol_char = 0;
887 
888 		while (*eol) {
889 			if (*eol == '\r' || *eol == '\n') {
890 				eol_char = *eol;
891 				*eol = 0;
892 				break;
893 			}
894 			eol++;
895 		}
896 
897 		vpn_progress(vpninfo, PRG_DEBUG, "%c %s\n", prefix, buf);
898 		if (!eol_char)
899 			break;
900 
901 		*eol = eol_char;
902 		buf = eol + 1;
903 		if (eol_char == '\r' && *buf == '\n')
904 			buf++;
905 	}
906 }
907 
dump_buf_hex(struct openconnect_info * vpninfo,int loglevel,char prefix,unsigned char * buf,int len)908 void dump_buf_hex(struct openconnect_info *vpninfo, int loglevel, char prefix, unsigned char *buf, int len)
909 {
910 	struct oc_text_buf *line = buf_alloc();
911 	int i, j;
912 
913 	for (i = 0; i < len; i+=16) {
914 		buf_truncate(line);
915 		buf_append(line, "%04x:", i);
916 		for (j = i; j < i+16; j++) {
917 			if (!(j & 7))
918 				buf_append(line, " ");
919 
920 			if (j < len)
921 				buf_append(line, " %02x", buf[j]);
922 			else
923 				buf_append(line, "   ");
924 		}
925 		buf_append(line, "  |");
926 		for (j = i; j < i+16 && j < len; j++)
927 			buf_append(line, "%c", isprint(buf[j])? buf[j] : '.');
928 		buf_append(line, "|");
929 		if (buf_error(line))
930 			break;
931 		vpn_progress(vpninfo, loglevel, "%c %s\n", prefix, line->data);
932 	}
933 	buf_free(line);
934 }
935 
https_socket_closed(struct openconnect_info * vpninfo)936 static int https_socket_closed(struct openconnect_info *vpninfo)
937 {
938 	char c;
939 
940 	if (!openconnect_https_connected(vpninfo))
941 		return 1;
942 
943 	if (recv(vpninfo->ssl_fd, &c, 1, MSG_PEEK) == 0) {
944 		vpn_progress(vpninfo, PRG_DEBUG,
945 			     _("HTTPS socket closed by peer; reopening\n"));
946 		openconnect_close_https(vpninfo, 0);
947 		return 1;
948 	}
949 
950 	return 0;
951 }
952 
953 
954 /* Inputs:
955  *  method:             GET or POST
956  *  vpninfo->hostname:  Host DNS name
957  *  vpninfo->port:      TCP port, typically 443
958  *  vpninfo->urlpath:   Relative path, e.g. /+webvpn+/foo.html
959  *  request_body_type:  Content type for a POST (e.g. text/html).  Can be NULL.
960  *  request_body:       POST content
961  *  form_buf:           Callee-allocated buffer for server content
962  *
963  * Return value:
964  *  < 0, on error
965  *  >=0, on success, indicating the length of the data in *form_buf
966  */
do_https_request(struct openconnect_info * vpninfo,const char * method,const char * request_body_type,struct oc_text_buf * request_body,char ** form_buf,int fetch_redirect)967 int do_https_request(struct openconnect_info *vpninfo, const char *method,
968 		     const char *request_body_type, struct oc_text_buf *request_body,
969 		     char **form_buf, int fetch_redirect)
970 {
971 	struct oc_text_buf *buf;
972 	int result;
973 	int rq_retry;
974 	int rlen, pad;
975 	int i, auth = 0;
976 	int max_redirects = 10;
977 
978 	if (request_body_type && buf_error(request_body))
979 		return buf_error(request_body);
980 
981 	buf = buf_alloc();
982 
983  redirected:
984 	if (max_redirects-- <= 0) {
985 		result = -EIO;
986 		goto out;
987 	}
988 
989 	vpninfo->redirect_type = REDIR_TYPE_NONE;
990 
991 	if (*form_buf) {
992 		free(*form_buf);
993 		*form_buf = NULL;
994 	}
995 
996 	/*
997 	 * A long time ago, I *wanted* to use an HTTP client library like cURL
998 	 * for this. But we need a *lot* of control over the underlying SSL
999 	 * transport, and we also have to do horrid tricks like the Juniper NC
1000 	 * 'GET' request that actaully behaves like a 'CONNECT'.
1001 	 *
1002 	 * So the world gained Yet Another HTTP Implementation. Sorry.
1003 	 *
1004 	 */
1005 	buf_truncate(buf);
1006 	buf_append(buf, "%s /%s HTTP/1.1\r\n", method, vpninfo->urlpath ?: "");
1007 	if (auth) {
1008 		result = gen_authorization_hdr(vpninfo, 0, buf);
1009 		if (result)
1010 			goto out;
1011 
1012 		/* Forget existing challenges */
1013 		clear_auth_states(vpninfo, vpninfo->http_auth, 0);
1014 	}
1015 	if (vpninfo->proto->add_http_headers)
1016 		vpninfo->proto->add_http_headers(vpninfo, buf);
1017 
1018 	if (request_body_type) {
1019 		rlen = request_body->pos;
1020 
1021 		/* force body length to be a multiple of 64, to avoid leaking
1022 		 * password length. */
1023 		pad = 64*(1+rlen/64) - rlen;
1024 		buf_append(buf, "X-Pad: %0*d\r\n", pad, 0);
1025 
1026 		buf_append(buf, "Content-Type: %s\r\n", request_body_type);
1027 		buf_append(buf, "Content-Length: %d\r\n", (int)rlen);
1028 	}
1029 	buf_append(buf, "\r\n");
1030 
1031 	if (request_body_type)
1032 		buf_append_bytes(buf, request_body->data, request_body->pos);
1033 
1034 	if (vpninfo->port == 443)
1035 		vpn_progress(vpninfo, PRG_INFO, "%s https://%s/%s\n",
1036 			     method, vpninfo->hostname,
1037 			     vpninfo->urlpath ?: "");
1038 	else
1039 		vpn_progress(vpninfo, PRG_INFO, "%s https://%s:%d/%s\n",
1040 			     method, vpninfo->hostname, vpninfo->port,
1041 			     vpninfo->urlpath ?: "");
1042 
1043 	if (buf_error(buf))
1044 		return buf_free(buf);
1045 
1046 	vpninfo->retry_on_auth_fail = 0;
1047 
1048  retry:
1049 	if (!https_socket_closed(vpninfo)) {
1050 		/* The session is already connected. If we get a failure on
1051 		* *sending* the request, try it again immediately with a new
1052 		* connection. */
1053 		rq_retry = 1;
1054 	} else {
1055 		rq_retry = 0;
1056 		if ((result = openconnect_open_https(vpninfo))) {
1057 			vpn_progress(vpninfo, PRG_ERR,
1058 				     _("Failed to open HTTPS connection to %s\n"),
1059 				     vpninfo->hostname);
1060 			/* We really don't want to return -EINVAL if we have
1061 			   failed to even connect to the server, because if
1062 			   we do that openconnect_obtain_cookie() might try
1063 			   again without XMLPOST... with the same result. */
1064 			result = -EIO;
1065 			goto out;
1066 		}
1067 	}
1068 
1069 	if (vpninfo->dump_http_traffic)
1070 		dump_buf(vpninfo, '>', buf->data);
1071 
1072 	for (i = 0; i < buf->pos; i += 16384) {
1073 		result = vpninfo->ssl_write(vpninfo, buf->data + i, MIN(buf->pos - i, 16384) );
1074 		if (result < 0) {
1075 			if (rq_retry) {
1076 				/* Retry if we failed to send the request on
1077 				   an already-open connection */
1078 				openconnect_close_https(vpninfo, 0);
1079 				goto retry;
1080 			}
1081 			/* We'll already have complained about whatever offended us */
1082 			goto out;
1083 		}
1084 	}
1085 
1086 	result = process_http_response(vpninfo, 0, http_auth_hdrs, buf);
1087 	if (result < 0) {
1088 		if (rq_retry) {
1089 			openconnect_close_https(vpninfo, 0);
1090 			vpn_progress(vpninfo, PRG_INFO,
1091 				     _("Retrying failed %s request on new connection\n"),
1092 				     method);
1093 			/* All the way back to 'redirected' since we need to rebuild
1094 			 * the request in 'buf' from scratch. */
1095 			goto redirected;
1096 		}
1097 		goto out;
1098 	}
1099 	if (vpninfo->dump_http_traffic && buf->pos)
1100 		dump_buf(vpninfo, '<', buf->data);
1101 
1102 	if (result == 401 && vpninfo->try_http_auth) {
1103 		auth = 1;
1104 		goto redirected;
1105 	}
1106 	if (result != 200 && vpninfo->redirect_url) {
1107 		result = handle_redirect(vpninfo);
1108 		if (result == 0) {
1109 			if (!fetch_redirect)
1110 				goto out;
1111 			if (fetch_redirect == 2) {
1112 				/* Juniper requires we GET after a redirected POST */
1113 				method = "GET";
1114 				request_body_type = NULL;
1115 			}
1116 			if (vpninfo->redirect_type == REDIR_TYPE_NEWHOST)
1117 				clear_auth_states(vpninfo, vpninfo->http_auth, 1);
1118 			goto redirected;
1119 		}
1120 		goto out;
1121 	}
1122 	if (!buf->pos || result != 200) {
1123 		vpn_progress(vpninfo, PRG_ERR,
1124 			     _("Unexpected %d result from server\n"),
1125 			     result);
1126 		if (result == 401 || result == 403)
1127 			result = -EPERM;
1128 		else if (result == 512) /* GlobalProtect invalid username/password */
1129 			result = -EACCES;
1130 		else
1131 			result = -EINVAL;
1132 		goto out;
1133 	}
1134 
1135 	*form_buf = buf->data;
1136 	buf->data = NULL;
1137 	result = buf->pos;
1138 
1139  out:
1140 	buf_free(buf);
1141 	/* On success, clear out all authentication state for the next request */
1142 	clear_auth_states(vpninfo, vpninfo->http_auth, 1);
1143 	return result;
1144 }
1145 
openconnect_create_useragent(const char * base)1146 char *openconnect_create_useragent(const char *base)
1147 {
1148 	char *uagent;
1149 
1150 	if (asprintf(&uagent, "%s %s", base, openconnect_version_str) < 0)
1151 		return NULL;
1152 
1153 	return uagent;
1154 }
1155 
proxy_gets(struct openconnect_info * vpninfo,char * buf,size_t len)1156 static int proxy_gets(struct openconnect_info *vpninfo, char *buf, size_t len)
1157 {
1158 	return cancellable_gets(vpninfo, vpninfo->proxy_fd, buf, len);
1159 }
1160 
proxy_write(struct openconnect_info * vpninfo,char * buf,size_t len)1161 static int proxy_write(struct openconnect_info *vpninfo, char *buf, size_t len)
1162 {
1163 	return cancellable_send(vpninfo, vpninfo->proxy_fd, buf, len);
1164 }
1165 
proxy_read(struct openconnect_info * vpninfo,char * buf,size_t len)1166 static int proxy_read(struct openconnect_info *vpninfo, char *buf, size_t len)
1167 {
1168 	return cancellable_recv(vpninfo, vpninfo->proxy_fd, buf, len);
1169 }
1170 
1171 static const char *socks_errors[] = {
1172 	N_("request granted"),
1173 	N_("general failure"),
1174 	N_("connection not allowed by ruleset"),
1175 	N_("network unreachable"),
1176 	N_("host unreachable"),
1177 	N_("connection refused by destination host"),
1178 	N_("TTL expired"),
1179 	N_("command not supported / protocol error"),
1180 	N_("address type not supported")
1181 };
1182 
socks_password_auth(struct openconnect_info * vpninfo)1183 static int socks_password_auth(struct openconnect_info *vpninfo)
1184 {
1185 	int ul, pl, i;
1186 	char buf[1024];
1187 
1188 	if (!vpninfo->proxy_user || !vpninfo->proxy_pass) {
1189 		vpn_progress(vpninfo, PRG_ERR,
1190 			     _("SOCKS server requested username/password but we have none\n"));
1191 		return -EIO;
1192 	}
1193 	ul = strlen(vpninfo->proxy_user);
1194 	pl = strlen(vpninfo->proxy_pass);
1195 
1196 	if (ul > 255 || pl > 255) {
1197 		vpn_progress(vpninfo, PRG_ERR,
1198 			     _("Username and password for SOCKS authentication must be < 255 bytes\n"));
1199 		return -EINVAL;
1200 	}
1201 
1202 	buf[0] = 1;
1203 	buf[1] = ul;
1204 	memcpy(buf + 2, vpninfo->proxy_user, ul);
1205 	buf[2 + ul] = pl;
1206 	memcpy(buf + 3 + ul, vpninfo->proxy_pass, pl);
1207 
1208 	i = proxy_write(vpninfo, buf, 3 + ul + pl);
1209 	/* Don't leave passwords lying around if we can easily avoid it... */
1210 	memset(buf, 0, sizeof(buf));
1211 	if (i < 0) {
1212 		vpn_progress(vpninfo, PRG_ERR,
1213 			     _("Error writing auth request to SOCKS proxy: %s\n"),
1214 			     strerror(-i));
1215 		return i;
1216 	}
1217 
1218 
1219 	if ((i = proxy_read(vpninfo, buf, 2)) < 0) {
1220 		vpn_progress(vpninfo, PRG_ERR,
1221 			     _("Error reading auth response from SOCKS proxy: %s\n"),
1222 			     strerror(-i));
1223 		return i;
1224 	}
1225 
1226 	if (buf[0] != 1) {
1227 		vpn_progress(vpninfo, PRG_ERR,
1228 			     _("Unexpected auth response from SOCKS proxy: %02x %02x\n"),
1229 			     buf[0], buf[1]);
1230 		return -EIO;
1231 	}
1232 	if (buf[1] == 0) {
1233 		vpn_progress(vpninfo, PRG_DEBUG,
1234 			     _("Authenticated to SOCKS server using password\n"));
1235 		return 0;
1236 	} else {
1237 		vpn_progress(vpninfo, PRG_ERR,
1238 			     _("Password authentication to SOCKS server failed\n"));
1239 		return -EIO;
1240 	}
1241 }
1242 
1243 #define SOCKS_AUTH_NONE			0	/* RFC1928 */
1244 #define SOCKS_AUTH_GSSAPI		1	/* RFC1961 */
1245 #define SOCKS_AUTH_PASSWORD		2	/* RFC1929 */
1246 #define SOCKS_AUTH_NO_ACCEPTABLE	0xff	/* RFC1928 */
1247 
process_socks_proxy(struct openconnect_info * vpninfo)1248 static int process_socks_proxy(struct openconnect_info *vpninfo)
1249 {
1250 	char buf[1024];
1251 	int i, nr_auth_methods = 0;
1252 
1253 	buf[0] = 5; /* SOCKS version */
1254 
1255 	buf[2 + nr_auth_methods++] = SOCKS_AUTH_NONE;
1256 #if defined(HAVE_GSSAPI) || defined(_WIN32)
1257 	if (vpninfo->proxy_auth[AUTH_TYPE_GSSAPI].state > AUTH_FAILED &&
1258 	    !vpninfo->proxy_user && !vpninfo->proxy_pass)
1259 		buf[2 + nr_auth_methods++] = SOCKS_AUTH_GSSAPI;
1260 #endif
1261 	/*
1262 	 * Basic auth is disabled by default. But for SOCKS, if the user has
1263 	 * actually provided a password then that should implicitly allow
1264 	 * basic auth since that's all that SOCKS can do. We shouldn't force
1265 	 * the user to also add --proxy-auth=basic on the command line.
1266 	 */
1267 	if ((vpninfo->proxy_auth[AUTH_TYPE_BASIC].state > AUTH_FAILED ||
1268 	     vpninfo->proxy_auth[AUTH_TYPE_BASIC].state == AUTH_DEFAULT_DISABLED) &&
1269 	    vpninfo->proxy_user && vpninfo->proxy_pass)
1270 		buf[2 + nr_auth_methods++] = SOCKS_AUTH_PASSWORD;
1271 
1272 	buf[1] = nr_auth_methods;
1273 
1274 	if ((i = proxy_write(vpninfo, buf, 2 + nr_auth_methods)) < 0) {
1275 		vpn_progress(vpninfo, PRG_ERR,
1276 			     _("Error writing auth request to SOCKS proxy: %s\n"),
1277 			     strerror(-i));
1278 		return i;
1279 	}
1280 
1281 	if ((i = proxy_read(vpninfo, buf, 2)) < 0) {
1282 		vpn_progress(vpninfo, PRG_ERR,
1283 			     _("Error reading auth response from SOCKS proxy: %s\n"),
1284 			     strerror(-i));
1285 		return i;
1286 	}
1287 	if (buf[0] != 5) {
1288 		vpn_progress(vpninfo, PRG_ERR,
1289 			     _("Unexpected auth response from SOCKS proxy: %02x %02x\n"),
1290 			     buf[0], buf[1]);
1291 		return -EIO;
1292 	}
1293 	switch ((unsigned char)buf[1]) {
1294 	case SOCKS_AUTH_NONE:
1295 		/* No authentication */
1296 		break;
1297 
1298 	case SOCKS_AUTH_GSSAPI:
1299 #if defined(HAVE_GSSAPI) || defined(_WIN32)
1300 		vpn_progress(vpninfo, PRG_DEBUG,
1301 			     _("SOCKS server requested GSSAPI authentication\n"));
1302 		if (socks_gssapi_auth(vpninfo))
1303 			return -EIO;
1304 		break;
1305 #else
1306 		/* This should never happen since we didn't ask for it! */
1307 		vpn_progress(vpninfo, PRG_ERR,
1308 			     _("SOCKS server requested GSSAPI authentication\n"));
1309 		return -EIO;
1310 #endif
1311 
1312 	case SOCKS_AUTH_PASSWORD:
1313 		vpn_progress(vpninfo, PRG_DEBUG,
1314 			     _("SOCKS server requested password authentication\n"));
1315 		if (socks_password_auth(vpninfo))
1316 			return -EIO;
1317 		break;
1318 
1319 	case SOCKS_AUTH_NO_ACCEPTABLE:
1320 		vpn_progress(vpninfo, PRG_ERR,
1321 			     _("SOCKS server requires authentication\n"));
1322 #if !defined(HAVE_GSSAPI) && !defined(_WIN32)
1323 		vpn_progress(vpninfo, PRG_INFO,
1324 			     _("This version of OpenConnect was built without GSSAPI support\n"));
1325 #endif
1326 		return -EIO;
1327 
1328 	default:
1329 		vpn_progress(vpninfo, PRG_ERR,
1330 			     _("SOCKS server requested unknown authentication type %02x\n"),
1331 			     (unsigned char)buf[1]);
1332 		return -EIO;
1333 	}
1334 
1335 	vpn_progress(vpninfo, PRG_INFO,
1336 		     _("Requesting SOCKS proxy connection to %s:%d\n"),
1337 		     vpninfo->hostname, vpninfo->port);
1338 
1339 	buf[0] = 5; /* SOCKS version */
1340 	buf[1] = 1; /* CONNECT */
1341 	buf[2] = 0; /* Reserved */
1342 	buf[3] = 3; /* Address type is domain name */
1343 	buf[4] = strlen(vpninfo->hostname);
1344 	strcpy((char *)buf + 5, vpninfo->hostname);
1345 	i = strlen(vpninfo->hostname) + 5;
1346 	store_be16(buf + i, vpninfo->port);
1347 	i += 2;
1348 
1349 	if ((i = proxy_write(vpninfo, buf, i)) < 0) {
1350 		vpn_progress(vpninfo, PRG_ERR,
1351 			     _("Error writing connect request to SOCKS proxy: %s\n"),
1352 			     strerror(-i));
1353 		return i;
1354 	}
1355 	/* Read 5 bytes -- up to and including the first byte of the returned
1356 	   address (which might be the length byte of a domain name) */
1357 	if ((i = proxy_read(vpninfo, buf, 5)) < 0) {
1358 		vpn_progress(vpninfo, PRG_ERR,
1359 			     _("Error reading connect response from SOCKS proxy: %s\n"),
1360 			     strerror(-i));
1361 		return i;
1362 	}
1363 	if (i != 5 || buf[0] != 5) {
1364 		vpn_progress(vpninfo, PRG_ERR,
1365 			     _("Unexpected connect response from SOCKS proxy: %02x %02x...\n"),
1366 			     buf[0], buf[1]);
1367 		return -EIO;
1368 	}
1369 	if (buf[1]) {
1370 		unsigned char err = buf[1];
1371 		if (err < sizeof(socks_errors) / sizeof(socks_errors[0]))
1372 			vpn_progress(vpninfo, PRG_ERR,
1373 				     _("SOCKS proxy error %02x: %s\n"),
1374 				     err, _(socks_errors[err]));
1375 		else
1376 			vpn_progress(vpninfo, PRG_ERR,
1377 				     _("SOCKS proxy error %02x\n"), err);
1378 		return -EIO;
1379 	}
1380 
1381 	/* Connect responses contain an address */
1382 	switch (buf[3]) {
1383 	case 1: /* Legacy IP */
1384 		i = 5;
1385 		break;
1386 	case 3: /* Domain name */
1387 		i = buf[4] + 2;
1388 		break;
1389 	case 4: /* IPv6 */
1390 		i = 17;
1391 		break;
1392 	default:
1393 		vpn_progress(vpninfo, PRG_ERR,
1394 			     _("Unexpected address type %02x in SOCKS connect response\n"),
1395 			     buf[3]);
1396 		return -EIO;
1397 	}
1398 
1399 	if ((i = proxy_read(vpninfo, buf, i)) < 0) {
1400 		vpn_progress(vpninfo, PRG_ERR,
1401 			     _("Error reading connect response from SOCKS proxy: %s\n"),
1402 			     strerror(-i));
1403 		return i;
1404 	}
1405 	return 0;
1406 }
1407 
process_http_proxy(struct openconnect_info * vpninfo)1408 static int process_http_proxy(struct openconnect_info *vpninfo)
1409 {
1410 	struct oc_text_buf *reqbuf;
1411 	int result;
1412 	int auth = vpninfo->proxy_close_during_auth;
1413 
1414 	vpninfo->proxy_close_during_auth = 0;
1415 
1416 	vpn_progress(vpninfo, PRG_INFO,
1417 		     _("Requesting HTTP proxy connection to %s:%d\n"),
1418 		     vpninfo->hostname, vpninfo->port);
1419 
1420  retry:
1421 	reqbuf = buf_alloc();
1422 	buf_append(reqbuf, "CONNECT %s:%d HTTP/1.1\r\n", vpninfo->hostname, vpninfo->port);
1423 	if (vpninfo->port == 443)
1424 		buf_append(reqbuf, "Host: %s\r\n", vpninfo->hostname);
1425 	else
1426 		buf_append(reqbuf, "Host: %s:%d\r\n", vpninfo->hostname, vpninfo->port);
1427 	buf_append(reqbuf, "User-Agent: %s\r\n", vpninfo->useragent);
1428 	buf_append(reqbuf, "Proxy-Connection: keep-alive\r\n");
1429 	buf_append(reqbuf, "Connection: keep-alive\r\n");
1430 	buf_append(reqbuf, "Accept-Encoding: identity\r\n");
1431 	if (auth) {
1432 		result = gen_authorization_hdr(vpninfo, 1, reqbuf);
1433 		if (result) {
1434 			buf_free(reqbuf);
1435 			return result;
1436 		}
1437 		/* Forget existing challenges */
1438 		clear_auth_states(vpninfo, vpninfo->proxy_auth, 0);
1439 	}
1440 	buf_append(reqbuf, "\r\n");
1441 
1442 	if (buf_error(reqbuf))
1443 		return buf_free(reqbuf);
1444 
1445 	if (vpninfo->dump_http_traffic)
1446 		dump_buf(vpninfo, '>', reqbuf->data);
1447 
1448 	result = proxy_write(vpninfo, reqbuf->data, reqbuf->pos);
1449 	if (result < 0) {
1450 		buf_free(reqbuf);
1451 		vpn_progress(vpninfo, PRG_ERR,
1452 			     _("Sending proxy request failed: %s\n"),
1453 			     strerror(-result));
1454 		return result;
1455 	}
1456 
1457 	result = process_http_response(vpninfo, 1, proxy_auth_hdrs, reqbuf);
1458 	buf_free(reqbuf);
1459 	if (result < 0)
1460 		return -EINVAL;
1461 
1462 	if (result == 407) {
1463 		/* If the proxy asked us to close the connection, do so */
1464 		if (vpninfo->proxy_close_during_auth)
1465 			return -EAGAIN;
1466 
1467 		auth = 1;
1468 		goto retry;
1469 	}
1470 
1471 	if (result == 200)
1472 		return 0;
1473 
1474 	vpn_progress(vpninfo, PRG_ERR,
1475 		     _("Proxy CONNECT request failed: %d\n"), result);
1476 	return -EIO;
1477 }
1478 
process_proxy(struct openconnect_info * vpninfo,int ssl_sock)1479 int process_proxy(struct openconnect_info *vpninfo, int ssl_sock)
1480 {
1481 	int ret;
1482 
1483 	vpninfo->proxy_fd = ssl_sock;
1484 	vpninfo->ssl_read = proxy_read;
1485 	vpninfo->ssl_write = proxy_write;
1486 	vpninfo->ssl_gets = proxy_gets;
1487 
1488 	if (!vpninfo->proxy_type || !strcmp(vpninfo->proxy_type, "http"))
1489 		ret = process_http_proxy(vpninfo);
1490 	else if (!strcmp(vpninfo->proxy_type, "socks") ||
1491 		 !strcmp(vpninfo->proxy_type, "socks5"))
1492 		ret = process_socks_proxy(vpninfo);
1493 	else {
1494 		vpn_progress(vpninfo, PRG_ERR, _("Unknown proxy type '%s'\n"),
1495 			     vpninfo->proxy_type);
1496 		ret = -EIO;
1497 	}
1498 
1499 	vpninfo->proxy_fd = -1;
1500 	if (!vpninfo->proxy_close_during_auth)
1501 		clear_auth_states(vpninfo, vpninfo->proxy_auth, 1);
1502 
1503 	return ret;
1504 }
1505 
openconnect_set_http_proxy(struct openconnect_info * vpninfo,const char * proxy)1506 int openconnect_set_http_proxy(struct openconnect_info *vpninfo,
1507 			       const char *proxy)
1508 {
1509 	char *p;
1510 	int ret;
1511 
1512 	free(vpninfo->proxy_type);
1513 	vpninfo->proxy_type = NULL;
1514 	free(vpninfo->proxy);
1515 	vpninfo->proxy = NULL;
1516 
1517 	ret = internal_parse_url(proxy, &vpninfo->proxy_type, &vpninfo->proxy,
1518 				 &vpninfo->proxy_port, NULL, 80);
1519 	if (ret) {
1520 		vpn_progress(vpninfo, PRG_ERR, _("Failed to parse proxy '%s'\n"), proxy);
1521 		return ret;
1522 	}
1523 
1524 	p = strrchr(vpninfo->proxy, '@');
1525 	if (p) {
1526 		/* Proxy username/password */
1527 		*p = 0;
1528 		vpninfo->proxy_user = vpninfo->proxy;
1529 		vpninfo->proxy = strdup(p + 1);
1530 		p = strchr(vpninfo->proxy_user, ':');
1531 		if (p) {
1532 			*p = 0;
1533 			vpninfo->proxy_pass = strdup(p + 1);
1534 			xmlURIUnescapeString(vpninfo->proxy_pass, 0, vpninfo->proxy_pass);
1535 		}
1536 		xmlURIUnescapeString(vpninfo->proxy_user, 0, vpninfo->proxy_user);
1537 	}
1538 
1539 	if (vpninfo->proxy_type &&
1540 	    strcmp(vpninfo->proxy_type, "http") &&
1541 	    strcmp(vpninfo->proxy_type, "socks") &&
1542 	    strcmp(vpninfo->proxy_type, "socks5")) {
1543 		vpn_progress(vpninfo, PRG_ERR,
1544 			     _("Only http or socks(5) proxies supported\n"));
1545 		free(vpninfo->proxy_type);
1546 		vpninfo->proxy_type = NULL;
1547 		free(vpninfo->proxy);
1548 		vpninfo->proxy = NULL;
1549 		return -EINVAL;
1550 	}
1551 
1552 	return 0;
1553 }
1554 
http_common_headers(struct openconnect_info * vpninfo,struct oc_text_buf * buf)1555 void http_common_headers(struct openconnect_info *vpninfo, struct oc_text_buf *buf)
1556 {
1557 	struct oc_vpn_option *opt;
1558 
1559 	if (vpninfo->port == 443)
1560 		buf_append(buf, "Host: %s\r\n", vpninfo->hostname);
1561 	else
1562 		buf_append(buf, "Host: %s:%d\r\n", vpninfo->hostname, vpninfo->port);
1563 	buf_append(buf, "User-Agent: %s\r\n", vpninfo->useragent);
1564 
1565 	if (vpninfo->cookies) {
1566 		buf_append(buf, "Cookie: ");
1567 		for (opt = vpninfo->cookies; opt; opt = opt->next)
1568 			buf_append(buf, "%s=%s%s", opt->option,
1569 				      opt->value, opt->next ? "; " : "\r\n");
1570 	}
1571 }
1572