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