1 /*
2  *  Copyright (C) 2015 Adrien Vergé
3  *
4  *  This program is free software: you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation, either version 3 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "http.h"
19 #include "xml.h"
20 #include "ssl.h"
21 #include "ipv4.h"
22 #include "userinput.h"
23 #include "log.h"
24 
25 #include <unistd.h>
26 #include <arpa/inet.h>
27 
28 #include <assert.h>
29 #include <ctype.h>
30 #include <stdarg.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 
35 /*
36  * Fixed size of the buffer for outgoing HTTP requests.
37  * Initial size of the buffer for incoming HTTP responses.
38  */
39 #define HTTP_BUFFER_SIZE 0x8000
40 
41 
42 /*
43  * URL-encodes a string for HTTP requests.
44  *
45  * The dest buffer size MUST be at least strlen(str) * 3 + 1.
46  *
47  * @param[out] dest  the buffer to write the URL-encoded string
48  * @param[in]  str   the input string to be escaped
49  */
url_encode(char * dest,const char * str)50 static void url_encode(char *dest, const char *str)
51 {
52 	while (*str != '\0') {
53 		if (isalnum(*str) || *str == '-' || *str == '_' ||
54 		    *str == '.' || *str == '~') {
55 			*dest++ = *str;
56 		} else {
57 			static const char hex[] = "0123456789ABCDEF";
58 
59 			*dest++ = '%';
60 			*dest++ = hex[(unsigned char)*str >> 4];
61 			*dest++ = hex[(unsigned char)*str & 15];
62 		}
63 		str++;
64 	}
65 	*dest = '\0';
66 }
67 
68 
69 /*
70  * Sends data to the HTTP server.
71  *
72  * @param[in] request  the data to send to the server
73  * @return    1        in case of success
74  *            < 0      in case of error
75  */
http_send(struct tunnel * tunnel,const char * request,...)76 int http_send(struct tunnel *tunnel, const char *request, ...)
77 {
78 	va_list args;
79 	char buffer[HTTP_BUFFER_SIZE];
80 	char logbuffer[HTTP_BUFFER_SIZE];
81 	int length;
82 	int n = 0;
83 
84 	va_start(args, request);
85 	length = vsnprintf(buffer, HTTP_BUFFER_SIZE, request, args);
86 	va_end(args);
87 	strcpy(logbuffer, buffer);
88 	if (loglevel <= OFV_LOG_DEBUG_DETAILS && tunnel->config->password[0] != '\0') {
89 		char *pwstart;
90 		char password[3 * PASSWORD_SIZE + 1];
91 
92 		url_encode(password, tunnel->config->password);
93 		pwstart = strstr(logbuffer, password);
94 
95 		if (pwstart != NULL) {
96 			int pos, pwlen, i;
97 
98 			pos = pwstart - logbuffer;
99 			pwlen = strlen(tunnel->config->password);
100 			for (i = pos; i < pos + pwlen; i++)
101 				logbuffer[i] = '*';
102 		}
103 	}
104 
105 	if (length < 0)
106 		return ERR_HTTP_INVALID;
107 	else if (length >= HTTP_BUFFER_SIZE)
108 		return ERR_HTTP_TOO_LONG;
109 
110 	log_debug_details("%s:\n%s\n", __func__, logbuffer);
111 
112 	while (n == 0)
113 		n = safe_ssl_write(tunnel->ssl_handle, (uint8_t *) buffer,
114 		                   length);
115 	if (n < 0) {
116 		log_debug("Error writing to SSL connection (%s).\n",
117 		          err_ssl_str(n));
118 		return ERR_HTTP_SSL;
119 	}
120 
121 	return 1;
122 }
123 
124 
find_header(const char * res,const char * header,uint32_t response_size)125 static const char *find_header(const char *res, const char *header,
126                                uint32_t response_size)
127 {
128 	const char *line = res;
129 
130 	while (memcmp(line, "\r\n", 2)) {
131 		int line_len = (char *) memmem(
132 		                       line, response_size - (line - res), "\r\n", 2
133 		               ) - line;
134 		int head_len = strlen(header);
135 
136 		if (line_len >= head_len && !strncasecmp(line, header, head_len))
137 			return line + head_len;
138 		line += line_len + 2;
139 	}
140 
141 	return NULL;
142 }
143 
144 
145 /*
146  * Receives data from the HTTP server.
147  *
148  * @param[out] response  if not NULL, this pointer is set to reference
149  *                       the new allocated buffer containing the data
150  *                       sent by the server
151  * @return     1         in case of success
152  *             < 0       in case of error
153  */
http_receive(struct tunnel * tunnel,char ** response,uint32_t * response_size)154 int http_receive(struct tunnel *tunnel,
155                  char **response, uint32_t *response_size)
156 {
157 	uint32_t capacity = HTTP_BUFFER_SIZE;
158 	char *buffer;
159 	uint32_t bytes_read = 0;
160 	uint32_t header_size = 0;
161 	uint32_t content_size = 0;
162 	int chunked = 0;
163 
164 	buffer = malloc(capacity + 1); // room for terminal '\0'
165 	if (buffer == NULL)
166 		return ERR_HTTP_NO_MEM;
167 
168 	while (1) {
169 		int n;
170 
171 		while ((n = safe_ssl_read(tunnel->ssl_handle,
172 		                          (uint8_t *) buffer + bytes_read,
173 		                          capacity - bytes_read)) == ERR_SSL_AGAIN)
174 			;
175 		if (n < 0) {
176 			log_debug("Error reading from SSL connection (%s).\n",
177 			          err_ssl_str(n));
178 			free(buffer);
179 			return ERR_HTTP_SSL;
180 		}
181 		bytes_read += n;
182 
183 		log_debug_details("%s:\n%s\n", __func__, buffer);
184 
185 		if (!header_size) {
186 			/* Have we reached the end of the HTTP header? */
187 			static const char EOH[4] = "\r\n\r\n";
188 			const char *eoh = memmem(buffer, bytes_read,
189 			                         EOH, sizeof(EOH));
190 
191 			if (eoh) {
192 				header_size = eoh - buffer + sizeof(EOH);
193 
194 				/* Get the body size. */
195 				const char *header = find_header(buffer,
196 				                                 "Content-Length: ",
197 				                                 header_size);
198 
199 				if (header)
200 					content_size = strtol(header, NULL, 10);
201 
202 				if (find_header(buffer,
203 				                "Transfer-Encoding: chunked",
204 				                header_size))
205 					chunked = 1;
206 			}
207 		}
208 
209 		if (header_size) {
210 			/* Have we reached the end of the HTTP body? */
211 			if (chunked) {
212 				static const char EOB[7] = "\r\n0\r\n\r\n";
213 
214 				/* Last chunk terminator. Done naively. */
215 				if (bytes_read >= sizeof(EOB) &&
216 				    !memcmp(&buffer[bytes_read - sizeof(EOB)],
217 				            EOB, sizeof(EOB)))
218 					break;
219 			} else {
220 				if (bytes_read >= header_size + content_size)
221 					break;
222 			}
223 		}
224 
225 		/* expand the buffer if necessary */
226 		if (bytes_read == capacity) {
227 			char *new_buffer;
228 
229 			if ((UINT32_MAX - 1) / capacity < 2) {
230 				free(buffer);
231 				return ERR_HTTP_TOO_LONG;
232 			}
233 			capacity *= 2;
234 
235 			new_buffer = realloc(buffer, capacity + 1);
236 			if (new_buffer == NULL) {
237 				free(buffer);
238 				return ERR_HTTP_NO_MEM;
239 			}
240 			buffer = new_buffer;
241 		}
242 	}
243 
244 	if (memmem(&buffer[header_size], bytes_read - header_size,
245 	           "<!--sslvpnerrmsgkey=sslvpn_login_permission_denied-->", 53) ||
246 	    memmem(buffer, header_size, "permission_denied denied", 24) ||
247 	    memmem(buffer, header_size, "Permission denied", 17)) {
248 		free(buffer);
249 		return ERR_HTTP_PERMISSION;
250 	}
251 
252 	if (response == NULL) {
253 		free(buffer);
254 	} else {
255 		assert(bytes_read < capacity);
256 		buffer[bytes_read] = '\0';
257 		*response = buffer;
258 
259 		if (response_size != NULL)
260 			*response_size = bytes_read + 1;
261 	}
262 	return 1;
263 }
264 
265 
do_http_request(struct tunnel * tunnel,const char * method,const char * uri,const char * data,char ** response,uint32_t * response_size)266 static int do_http_request(struct tunnel *tunnel,
267                            const char *method,
268                            const char *uri,
269                            const char *data,
270                            char **response,
271                            uint32_t *response_size
272                           )
273 {
274 	int ret;
275 	const char *template = ("%s %s HTTP/1.1\r\n"
276 	                        "Host: %s:%d\r\n"
277 	                        "User-Agent: %s\r\n"
278 	                        "Accept: */*\r\n"
279 	                        "Accept-Encoding: gzip, deflate, br\r\n"
280 	                        "Pragma: no-cache\r\n"
281 	                        "Cache-Control: no-store, no-cache, must-revalidate\r\n"
282 	                        "If-Modified-Since: Sat, 1 Jan 2000 00:00:00 GMT\r\n"
283 	                        "Content-Type: application/x-www-form-urlencoded\r\n"
284 	                        "Cookie: %s\r\n"
285 	                        "Content-Length: %d\r\n"
286 	                        "\r\n%s");
287 
288 	ret = http_send(tunnel, template, method, uri,
289 	                tunnel->config->gateway_host, tunnel->config->gateway_port,
290 	                tunnel->config->user_agent, tunnel->cookie,
291 	                strlen(data), data);
292 	if (ret != 1)
293 		return ret;
294 
295 	return http_receive(tunnel, response, response_size);
296 }
297 
298 
299 /*
300  * Sends and receives data from the HTTP server.
301  *
302  * @param[out] response  if not NULL, this pointer is set to reference
303  *                       the new allocated buffer containing the data
304  *                       sent by the server
305  * @return     1         in case of success
306  *             < 0       in case of error
307  */
http_request(struct tunnel * tunnel,const char * method,const char * uri,const char * data,char ** response,uint32_t * response_size)308 static int http_request(struct tunnel *tunnel, const char *method,
309                         const char *uri,
310                         const char *data,
311                         char **response,
312                         uint32_t *response_size
313                        )
314 {
315 	int ret;
316 
317 	ret = do_http_request(tunnel, method, uri, data,
318 	                      response, response_size);
319 	if (ret == ERR_HTTP_SSL) {
320 		ssl_connect(tunnel);
321 		ret = do_http_request(tunnel, method, uri, data,
322 		                      response, response_size);
323 	}
324 	if (ret != 1)
325 		log_debug("Error issuing %s request\n", uri);
326 
327 	return ret;
328 }
329 
330 
331 /*
332  * Read value for key from a string like "key1=value1&key2=value2".
333  * The `key` arg is supposed to contains the final "=".
334  *
335  * @return  1   in case of success
336  *          -1  key not found
337  *          -2  value too large for buffer
338  *          -3  if no memory
339  */
get_value_from_response(const char * buf,const char * key,char * retbuf,size_t retbuflen)340 static int get_value_from_response(const char *buf, const char *key,
341                                    char *retbuf, size_t retbuflen)
342 {
343 	int ret = -1;
344 	char *tokens;
345 	size_t keylen = strlen(key);
346 	char *saveptr = NULL;
347 
348 	tokens = strdup(buf);
349 	if (tokens == NULL) {
350 		ret = -3;
351 		goto end;
352 	}
353 
354 	for (const char *kv_pair = strtok_r(tokens, "&,\r\n", &saveptr);
355 	     kv_pair != NULL;
356 	     kv_pair = strtok_r(NULL, "&,\r\n", &saveptr)) {
357 		if (strncmp(key, kv_pair, keylen) == 0) {
358 			const char *val = &kv_pair[keylen];
359 
360 			if (strlen(val) > retbuflen - 1) {  // value too long
361 				ret = -2;
362 			} else {
363 				strcpy(retbuf, val);
364 				ret = 1;
365 			}
366 			break;
367 		}
368 	}
369 
370 	free(tokens);
371 end:
372 	return ret;
373 }
374 
get_action_url(const char * buf,const char * key,char * retbuf,size_t retbuflen)375 static int get_action_url(const char *buf, const char *key,
376                           char *retbuf, size_t retbuflen)
377 {
378 
379 	int ret = -1;
380 	char *tokens;
381 	size_t keylen = strlen(key);
382 	char *saveptr = NULL;
383 
384 	tokens = strdup(buf);
385 	if (tokens == NULL) {
386 		ret = -3;
387 		goto end;
388 	}
389 
390 	for (const char *kv_pair = strtok_r(tokens, " \"\r\n", &saveptr);
391 	     kv_pair != NULL;
392 	     kv_pair = strtok_r(NULL, " \"\r\n", &saveptr)) {
393 		if (strncmp(key, kv_pair, keylen) == 0) {
394 			const char *val = strtok_r(NULL, "\"\r\n", &saveptr);
395 
396 			if (strlen(val) > retbuflen - 1) {  // value too long
397 				ret = -2;
398 			} else {
399 				strcpy(retbuf, val);
400 				ret = 1;
401 			}
402 			break;
403 		}
404 	}
405 
406 	free(tokens);
407 end:
408 	return ret;
409 }
410 
get_auth_cookie(struct tunnel * tunnel,char * buf,uint32_t buffer_size)411 static int get_auth_cookie(struct tunnel *tunnel, char *buf, uint32_t buffer_size)
412 {
413 	int ret = 0;
414 	const char *line;
415 
416 	ret = ERR_HTTP_NO_COOKIE;
417 
418 	line = find_header(buf, "Set-Cookie: ", buffer_size);
419 	if (line) {
420 		if (strncmp(line, "SVPNCOOKIE=", 11) == 0) {
421 			if (line[11] == ';' || line[11] == '\0') {
422 				log_debug("Empty cookie.\n");
423 			} else {
424 				char *end1;
425 				char *end2;
426 				char end1_save = '\0';
427 				char end2_save = '\0';
428 
429 				end1 = strstr(line, "\r");
430 				if (end1 != NULL) {
431 					end1_save = *end1;
432 					end1[0] = '\0';
433 				}
434 				end2 = strstr(line, ";");
435 				if (end2 != NULL) {
436 					end2_save = *end2;
437 					end2[0] = '\0';
438 				}
439 				log_debug("Cookie: %s\n", line);
440 				strncpy(tunnel->cookie, line, COOKIE_SIZE);
441 				tunnel->cookie[COOKIE_SIZE] = '\0';
442 				if (strlen(line) > COOKIE_SIZE) {
443 					log_error("Cookie larger than expected: %zu > %d\n",
444 					          strlen(line), COOKIE_SIZE);
445 				} else {
446 					ret = 1; // success
447 				}
448 				if (end1 != NULL)
449 					end1[0] = end1_save;
450 				if (end2 != NULL)
451 					end2[0] = end2_save;
452 			}
453 		}
454 	}
455 	return ret;
456 }
457 
458 
delay_otp(struct tunnel * tunnel)459 static void delay_otp(struct tunnel *tunnel)
460 {
461 	if (tunnel->config->otp_delay > 0) {
462 		log_info("Delaying OTP by %d seconds...\n", tunnel->config->otp_delay);
463 		sleep(tunnel->config->otp_delay);
464 	}
465 }
466 
467 
try_otp_auth(struct tunnel * tunnel,const char * buffer,char ** res,uint32_t * response_size)468 static int try_otp_auth(struct tunnel *tunnel, const char *buffer,
469                         char **res, uint32_t *response_size)
470 {
471 	char data[256];
472 	char path[40];
473 	char tmp[40];
474 	char prompt[80];
475 	const char *t = NULL, *n = NULL, *v = NULL, *e = NULL;
476 	const char *s = buffer;
477 	char *d = data;
478 	const char *p = NULL;
479 	/* Length-check for destination buffer */
480 #define SPACE_AVAILABLE(sz) (sizeof(data) - (d - data) >= (sz))
481 	/* Get the form action */
482 	s = strcasestr(s, "<FORM");
483 	if (s == NULL)
484 		return -1;
485 	s = strcasestr(s + 5, "ACTION=\"");
486 	if (s == NULL)
487 		return -1;
488 	s += 8;
489 	e = strchr(s, '"');
490 	if (e == NULL)
491 		return -1;
492 	if (e - s + 1 > sizeof(path))
493 		return -1;
494 	strncpy(path, s, e - s);
495 	path[e - s] = '\0';
496 	/*
497 	 * Try to get password prompt, assume it starts with 'Please'
498 	 * Fall back to default prompt if not found/parseable
499 	 */
500 	p = strstr(s, "Please");
501 	if (tunnel->config->otp_prompt != NULL)
502 		p = strstr(s, tunnel->config->otp_prompt);
503 	if (p) {
504 		e = strchr(p, '<');
505 		if (e != NULL) {
506 			if (e - p + 1 < sizeof(prompt)) {
507 				strncpy(prompt, p, e - p);
508 				prompt[e - p] = '\0';
509 				p = prompt;
510 			} else {
511 				p = NULL;
512 			}
513 		} else {
514 			p = NULL;
515 		}
516 	}
517 	if (p == NULL)
518 		p = "Please enter one-time password: ";
519 	/* Search for all inputs */
520 	while ((s = strcasestr(s, "<INPUT"))) {
521 		s += 6;
522 		/*
523 		 * check if we found parameters for a later INPUT
524 		 * during last round
525 		 */
526 		if (s < t || s < n || (v && s < v))
527 			return -1;
528 		t = strcasestr(s, "TYPE=\"");
529 		n = strcasestr(s, "NAME=\"");
530 		v = strcasestr(s, "VALUE=\"");
531 		if (t == NULL)
532 			return -1;
533 		if (n == NULL)
534 			continue;
535 		n += 6;
536 		t += 6;
537 		if (strncmp(t, "hidden", 6) == 0 || strncmp(t, "password", 8) == 0) {
538 			/*
539 			 * We try to be on the safe side
540 			 * and URL-encode the variable name
541 			 *
542 			 * Append '&' if we found something in last round
543 			 */
544 			if (d > data) {
545 				if (!SPACE_AVAILABLE(1))
546 					return -1;
547 				*d++ = '&';
548 			}
549 			e = strchr(n, '"');
550 			if (e == NULL)
551 				return -1;
552 			if (e - n + 1 > sizeof(tmp))
553 				return -1;
554 			strncpy(tmp, n, e - n);
555 			tmp[e - n] = '\0';
556 			if (!SPACE_AVAILABLE(3 * (e - n) + 1))
557 				return -1;
558 			url_encode(d, tmp);
559 			d += strlen(d);
560 			if (!SPACE_AVAILABLE(1))
561 				return -1;
562 			*d++ = '=';
563 		}
564 		if (strncmp(t, "hidden", 6) == 0) {
565 			/* Require value for hidden fields */
566 			if (v == NULL)
567 				return -1;
568 			v += 7;
569 			e = strchr(v, '"');
570 			if (e == NULL)
571 				return -1;
572 			if (e - v + 1 > sizeof(tmp))
573 				return -1;
574 			strncpy(tmp, v, e - v);
575 			tmp[e - v] = '\0';
576 			if (!SPACE_AVAILABLE(3 * (e - v) + 1))
577 				return -1;
578 			url_encode(d, tmp);
579 			d += strlen(d);
580 		} else if (strncmp(t, "password", 8) == 0) {
581 			struct vpn_config *cfg = tunnel->config;
582 			size_t l;
583 
584 			v = NULL;
585 			if (cfg->otp[0] == '\0') {
586 				// Interactively ask user for OTP
587 				char hint[USERNAME_SIZE + 1 + REALM_SIZE + 1 + GATEWAY_HOST_SIZE + 5];
588 
589 				sprintf(hint, "%s_%s_%s_otp",
590 				        cfg->username, cfg->realm, cfg->gateway_host);
591 				read_password(cfg->pinentry, hint,
592 				              p, cfg->otp, OTP_SIZE);
593 				if (cfg->otp[0] == '\0') {
594 					log_error("No OTP specified\n");
595 					return 0;
596 				}
597 			}
598 			l = strlen(cfg->otp);
599 			if (!SPACE_AVAILABLE(3 * l + 1))
600 				return -1;
601 			url_encode(d, cfg->otp);
602 			d += strlen(d);
603 			/*  realm workaround */
604 			if (cfg->realm[0] != '\0') {
605 				l = strlen(cfg->realm);
606 				if (!SPACE_AVAILABLE(3 * l + 8))
607 					return -1;
608 				strcat(d, "&realm=");
609 				d += strlen(d);
610 				url_encode(d, cfg->realm);
611 				d += strlen(d);
612 			}
613 		} else if (strncmp(t, "submit", 6) == 0) {
614 			/* avoid adding another '&' */
615 			n = v = e = NULL;
616 		}
617 	}
618 	if (!SPACE_AVAILABLE(1))
619 		return -1;
620 	*d++ = '\0';
621 	return http_request(tunnel, "POST", path, data, res, response_size);
622 #undef SPACE_AVAILABLE
623 }
624 
625 
626 /*
627  * Authenticates to gateway by sending username and password.
628  *
629  * @return  1   in case of success
630  *          < 0 in case of error
631  */
auth_log_in(struct tunnel * tunnel)632 int auth_log_in(struct tunnel *tunnel)
633 {
634 	int ret;
635 	char username[3 * USERNAME_SIZE + 1];
636 	char password[3 * PASSWORD_SIZE + 1];
637 	char realm[3 * REALM_SIZE + 1];
638 	char reqid[32] = { '\0' };
639 	char polid[32] = { '\0' };
640 	char group[128] = { '\0' };
641 	char portal[64] = { '\0' };
642 	char magic[32] = {'\0' };
643 	char peer[32]  = { '\0' };
644 	char data[9 + 3 * USERNAME_SIZE + 12 + 3 * PASSWORD_SIZE + 7 + 3 * REALM_SIZE + 7 + 1];
645 	char token[128], tokenresponse[256], tokenparams[320];
646 	char action_url[1024] = { '\0' };
647 	char *res = NULL;
648 	uint32_t response_size;
649 
650 	url_encode(username, tunnel->config->username);
651 	url_encode(realm, tunnel->config->realm);
652 
653 	tunnel->cookie[0] = '\0';
654 
655 	if (username[0] == '\0' && tunnel->config->password[0] == '\0') {
656 		snprintf(data, sizeof(data), "cert=&nup=1");
657 		ret = http_request(tunnel, "GET", "/remote/login",
658 		                   data, &res, &response_size);
659 	} else {
660 		if (tunnel->config->password[0] == '\0') {
661 			snprintf(data, sizeof(data),
662 			         "username=%s&realm=%s&ajax=1&redir=%%2Fremote%%2Findex&just_logged_in=1",
663 			         username, realm);
664 		} else {
665 			url_encode(password, tunnel->config->password);
666 			snprintf(data, sizeof(data),
667 			         "username=%s&credential=%s&realm=%s&ajax=1",
668 			         username, password, realm);
669 		}
670 		ret = http_request(tunnel, "POST", "/remote/logincheck",
671 		                   data, &res, &response_size);
672 	}
673 
674 	if (ret != 1)
675 		goto end;
676 
677 	/* Probably one-time password required */
678 	if (strncmp(res, "HTTP/1.1 401 Authorization Required\r\n", 37) == 0) {
679 		delay_otp(tunnel);
680 
681 		ret = try_otp_auth(tunnel, res, &res, &response_size);
682 		if (ret != 1)
683 			goto end;
684 	}
685 
686 	if (strncmp(res, "HTTP/1.1 200 OK\r\n", 17)) {
687 		char word[17];
688 
689 		if (sscanf(res, "%16s %d", word, &ret) < 2)
690 			ret = ERR_HTTP_BAD_RES_CODE;
691 		goto end;
692 	}
693 	ret = get_auth_cookie(tunnel, res, response_size);
694 	if (ret == ERR_HTTP_NO_COOKIE) {
695 		struct vpn_config *cfg = tunnel->config;
696 
697 		/*
698 		 * If the response body includes a tokeninfo= parameter,
699 		 * it means the VPN gateway expects two-factor authentication.
700 		 * It sends a one-time authentication credential for example
701 		 * by email or SMS, and expects to receive it back in the
702 		 * second authentication stage. No SVPNCOOKIE will be provided
703 		 * until after the second call to /remote/logincheck.
704 		 *
705 		 * If we receive neither a tokeninfo= parameter nor an
706 		 * SVPNCOOKIE, it means our authentication attempt was
707 		 * rejected.
708 		 */
709 
710 		ret = get_value_from_response(res, "tokeninfo=", token, 128);
711 		if (ret != 1) {
712 			// No SVPNCOOKIE and no tokeninfo, return error.
713 			ret = ERR_HTTP_NO_COOKIE;
714 			goto end;
715 		}
716 		// Two-factor authentication needed.
717 		get_value_from_response(res, "grp=", group, 128);
718 		get_value_from_response(res, "reqid=", reqid, 32);
719 		get_value_from_response(res, "polid=", polid, 32);
720 		get_value_from_response(res, "portal=", portal, 64);
721 		get_value_from_response(res, "magic=", magic, 32);
722 		get_value_from_response(res, "peer=", peer, 32);
723 
724 		if (cfg->otp[0] == '\0' &&
725 		    strncmp(token, "ftm_push", 8) == 0 &&
726 		    cfg->no_ftm_push == 0) {
727 			/*
728 			 * The server supports FTM push if `tokeninfo` is `ftm_push`,
729 			 * but only try this if the OTP is not provided by the config
730 			 * file or command line.
731 			 */
732 			snprintf(tokenparams, sizeof(tokenparams), "ftmpush=1");
733 		} else {
734 			if (cfg->otp[0] == '\0') {
735 				// Interactively ask user for 2FA token
736 				char hint[USERNAME_SIZE + 1 + REALM_SIZE + 1 + GATEWAY_HOST_SIZE + 5];
737 
738 				sprintf(hint, "%s_%s_%s_2fa",
739 				        cfg->username, cfg->realm, cfg->gateway_host);
740 				read_password(cfg->pinentry, hint,
741 				              "Two-factor authentication token: ",
742 				              cfg->otp, OTP_SIZE);
743 
744 				if (cfg->otp[0] == '\0') {
745 					log_error("No token specified\n");
746 					return 0;
747 				}
748 			}
749 
750 			url_encode(tokenresponse, cfg->otp);
751 			snprintf(tokenparams, sizeof(tokenparams),
752 			         "code=%s&code2=&magic=%s",
753 			         tokenresponse, magic);
754 		}
755 
756 		snprintf(data, sizeof(data),
757 		         "username=%s&realm=%s&reqid=%s&polid=%s&grp=%s&portal=%s&peer=%s&%s",
758 		         username, realm, reqid, polid, group, portal, peer,
759 		         tokenparams);
760 
761 		delay_otp(tunnel);
762 		ret = http_request(tunnel, "POST", "/remote/logincheck",
763 		                   data, &res, &response_size);
764 		if (ret != 1)
765 			goto end;
766 
767 		if (strncmp(res, "HTTP/1.1 200 OK\r\n", 17)) {
768 			char word[17];
769 
770 			if (sscanf(res, "%16s %d", word, &ret) < 2)
771 				ret = ERR_HTTP_BAD_RES_CODE;
772 			goto end;
773 		}
774 
775 		ret = get_auth_cookie(tunnel, res, response_size);
776 	}
777 
778 	/*
779 	 * If hostchecking enabled, get action url
780 	 */
781 	get_action_url(res, "action=", action_url, 1024);
782 	if (strlen(action_url) != 0) {
783 		snprintf(data, sizeof(data), "hostcheck=%s&check_virtual_desktop=%s",
784 		         tunnel->config->hostcheck,
785 		         tunnel->config->check_virtual_desktop);
786 		ret = http_request(tunnel, "POST", action_url,
787 		                   data, &res, &response_size);
788 	}
789 
790 end:
791 	free(res);
792 	return ret;
793 }
794 
795 
auth_log_out(struct tunnel * tunnel)796 int auth_log_out(struct tunnel *tunnel)
797 {
798 	return http_request(tunnel, "GET", "/remote/logout", "", NULL, NULL);
799 }
800 
801 
auth_request_vpn_allocation(struct tunnel * tunnel)802 int auth_request_vpn_allocation(struct tunnel *tunnel)
803 {
804 	int ret = http_request(tunnel, "GET", "/remote/index", "", NULL, NULL);
805 
806 	if (ret != 1)
807 		return ret;
808 
809 	return http_request(tunnel, "GET", "/remote/fortisslvpn", "", NULL, NULL);
810 }
811 
812 
parse_xml_config(struct tunnel * tunnel,const char * buffer)813 static int parse_xml_config(struct tunnel *tunnel, const char *buffer)
814 {
815 	const char *val;
816 	char *gateway;
817 	char *dns_server;
818 	int ret = 0;
819 
820 	if (strncmp(buffer, "HTTP/1.1 200 OK\r\n", 17)) {
821 		char word[17];
822 
823 		if (sscanf(buffer, "%16s %d", word, &ret) < 2)
824 			ret = ERR_HTTP_BAD_RES_CODE;
825 		return ret;
826 	}
827 
828 	// Skip the HTTP header
829 	buffer = strstr(buffer, "\r\n\r\n");
830 
831 	// The address of a local end of a router
832 	val = xml_find('<', "assigned-addr", buffer, 1);
833 	gateway = xml_get(xml_find(' ', "ipv4=", val, 1));
834 	if (!gateway)
835 		log_warn("No gateway address, using interface for routing\n");
836 
837 	// The dns search string
838 	val = buffer;
839 	while ((val = xml_find('<', "dns", val, 2))) {
840 		if (xml_find(' ', "domain=", val, 1)) {
841 			tunnel->ipv4.dns_suffix
842 			        = xml_get(xml_find(' ', "domain=", val, 1));
843 			log_debug("found dns suffix %s in xml config\n",
844 			          tunnel->ipv4.dns_suffix);
845 			break;
846 		}
847 	}
848 
849 	// The dns servers
850 	val = buffer;
851 	while ((val = xml_find('<', "dns", val, 2))) {
852 		if (xml_find(' ', "ip=", val, 1)) {
853 			dns_server = xml_get(xml_find(' ', "ip=", val, 1));
854 			log_debug("found dns server %s in xml config\n", dns_server);
855 			if (!tunnel->ipv4.ns1_addr.s_addr)
856 				tunnel->ipv4.ns1_addr.s_addr = inet_addr(dns_server);
857 			else if (!tunnel->ipv4.ns2_addr.s_addr)
858 				tunnel->ipv4.ns2_addr.s_addr = inet_addr(dns_server);
859 			free(dns_server);
860 		}
861 	}
862 
863 	// Routes the tunnel wants to push
864 	val = xml_find('<', "split-tunnel-info", buffer, 1);
865 	while ((val = xml_find('<', "addr", val, 2))) {
866 		char *dest, *mask;
867 
868 		dest = xml_get(xml_find(' ', "ip=", val, 1));
869 		if (!dest) {
870 			log_warn("No ip address for a route\n");
871 			continue;
872 		}
873 
874 		mask = xml_get(xml_find(' ', "mask=", val, 1));
875 		if (!mask) {
876 			log_warn("No mask for a route\n");
877 			free(dest);
878 			continue;
879 		}
880 
881 		ipv4_add_split_vpn_route(tunnel, dest, mask, gateway);
882 
883 		free(dest);
884 		free(mask);
885 	}
886 
887 	free(gateway);
888 
889 	return 1;
890 }
891 
892 
893 #ifdef SUPPORT_OBSOLETE_CODE
parse_config(struct tunnel * tunnel,const char * buffer)894 static int parse_config(struct tunnel *tunnel, const char *buffer)
895 {
896 	const char *c, *end;
897 
898 	buffer = strcasestr(buffer, "NAME=\"text6\"");
899 	if (!buffer)
900 		return 1;
901 	buffer = strcasestr(buffer, "VALUE=\"");
902 	if (!buffer)
903 		return 1;
904 	buffer += 7;
905 
906 	end = strchr(buffer, '"');
907 	if (end == NULL || end == buffer) {
908 		log_info("No split VPN route\n");
909 		return 1;
910 	}
911 
912 	do {
913 		char dest[16], mask[16];
914 
915 		c = strchr(buffer, '/');
916 		if (c == NULL || c >= end || c - buffer > 15) {
917 			log_warn("Wrong addresses in split VPN route: expected <dest>/<mask>\n");
918 			return 1;
919 		}
920 		memcpy(dest, buffer, c - buffer);
921 		dest[c - buffer] = '\0';
922 		buffer = c + 1;
923 
924 		c = strchr(buffer, ',');
925 		if (c == NULL || c > end)
926 			c = end;
927 
928 		if (c - buffer > 15) {
929 			log_warn("Wrong addresses in split VPN route: expected <dest>/<mask>\n");
930 			return 1;
931 		}
932 		memcpy(mask, buffer, c - buffer);
933 		mask[c - buffer] = '\0';
934 		buffer = c + 1;
935 
936 		ipv4_add_split_vpn_route(tunnel, dest, mask, NULL);
937 
938 	} while (c < end && *c == ',');
939 
940 	return 1;
941 }
942 #endif
943 
944 
auth_get_config(struct tunnel * tunnel)945 int auth_get_config(struct tunnel *tunnel)
946 {
947 	char *buffer;
948 	int ret;
949 
950 	ret = http_request(tunnel, "GET", "/remote/fortisslvpn_xml", "", &buffer, NULL);
951 	if (ret == 1) {
952 		ret = parse_xml_config(tunnel, buffer);
953 		free(buffer);
954 	}
955 
956 #ifdef SUPPORT_OBSOLETE_CODE
957 	if (ret == 1)
958 		return ret;
959 
960 	log_warn("Configuration cannot be retrieved in XML format. This VPN-SSL portal might be outdated and vulnerable, you might not be able to connect from systems with recent OpenSSL libraries.\n");
961 
962 	ret = http_request(tunnel, "GET", "/remote/fortisslvpn", "", &buffer, NULL);
963 	if (ret == 1) {
964 		ret = parse_config(tunnel, buffer);
965 		free(buffer);
966 	}
967 #endif
968 
969 	return ret;
970 }
971