http: Add new X-* HTTP headers
[platform/upstream/openconnect.git] / http.c
1 /*
2  * OpenConnect (SSL + DTLS) VPN client
3  *
4  * Copyright © 2008-2012 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  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to:
20  *
21  *   Free Software Foundation, Inc.
22  *   51 Franklin Street, Fifth Floor,
23  *   Boston, MA 02110-1301 USA
24  */
25
26 #include <netdb.h>
27 #include <unistd.h>
28 #include <fcntl.h>
29 #include <time.h>
30 #include <string.h>
31 #include <ctype.h>
32 #include <pwd.h>
33 #include <sys/stat.h>
34 #include <sys/types.h>
35 #include <errno.h>
36 #include <stdlib.h>
37 #include <stdio.h>
38 #include <stdarg.h>
39
40 #include "openconnect-internal.h"
41
42 static int proxy_write(struct openconnect_info *vpninfo, int fd,
43                        unsigned char *buf, size_t len);
44 static int proxy_read(struct openconnect_info *vpninfo, int fd,
45                       unsigned char *buf, size_t len);
46
47 #define MAX_BUF_LEN 131072
48 #define BUF_CHUNK_SIZE 4096
49
50 struct oc_text_buf {
51         char *data;
52         int pos;
53         int buf_len;
54         int error;
55 };
56
57 static struct oc_text_buf *buf_alloc(void)
58 {
59         return calloc(1, sizeof(struct oc_text_buf));
60 }
61
62 static void buf_append(struct oc_text_buf *buf, const char *fmt, ...)
63 {
64         va_list ap;
65
66         if (!buf || buf->error)
67                 return;
68
69         if (!buf->data) {
70                 buf->data = malloc(BUF_CHUNK_SIZE);
71                 if (!buf->data) {
72                         buf->error = -ENOMEM;
73                         return;
74                 }
75                 buf->buf_len = BUF_CHUNK_SIZE;
76         }
77
78         while (1) {
79                 int max_len = buf->buf_len - buf->pos, ret;
80
81                 va_start(ap, fmt);
82                 ret = vsnprintf(buf->data + buf->pos, max_len, fmt, ap);
83                 va_end(ap);
84                 if (ret < 0) {
85                         buf->error = -EIO;
86                         break;
87                 } else if (ret < max_len) {
88                         buf->pos += ret;
89                         break;
90                 } else {
91                         int new_buf_len = buf->buf_len + BUF_CHUNK_SIZE;
92
93                         if (new_buf_len > MAX_BUF_LEN) {
94                                 /* probably means somebody is messing with us */
95                                 buf->error = -E2BIG;
96                                 break;
97                         }
98
99                         buf->data = realloc(buf->data, new_buf_len);
100                         if (!buf->data) {
101                                 buf->error = -ENOMEM;
102                                 break;
103                         }
104                         buf->buf_len = new_buf_len;
105                 }
106         }
107 }
108
109 static int buf_error(struct oc_text_buf *buf)
110 {
111         return buf ? buf->error : -ENOMEM;
112 }
113
114 static int buf_free(struct oc_text_buf *buf)
115 {
116         int error = buf_error(buf);
117
118         if (buf) {
119                 if (buf->data)
120                         free(buf->data);
121                 free(buf);
122         }
123
124         return error;
125 }
126
127 /*
128  * We didn't really want to have to do this for ourselves -- one might have
129  * thought that it would be available in a library somewhere. But neither
130  * cURL nor Neon have reliable cross-platform ways of either using a cert
131  * from the TPM, or just reading from / writing to a transport which is
132  * provided by their caller.
133  */
134
135 static int http_add_cookie(struct openconnect_info *vpninfo,
136                            const char *option, const char *value)
137 {
138         struct vpn_option *new, **this;
139
140         if (*value) {
141                 new = malloc(sizeof(*new));
142                 if (!new) {
143                         vpn_progress(vpninfo, PRG_ERR,
144                                      _("No memory for allocating cookies\n"));
145                         return -ENOMEM;
146                 }
147                 new->next = NULL;
148                 new->option = strdup(option);
149                 new->value = strdup(value);
150                 if (!new->option || !new->value) {
151                         free(new->option);
152                         free(new->value);
153                         free(new);
154                         return -ENOMEM;
155                 }
156         } else {
157                 /* Kill cookie; don't replace it */
158                 new = NULL;
159         }
160         for (this = &vpninfo->cookies; *this; this = &(*this)->next) {
161                 if (!strcmp(option, (*this)->option)) {
162                         /* Replace existing cookie */
163                         if (new)
164                                 new->next = (*this)->next;
165                         else
166                                 new = (*this)->next;
167                         
168                         free((*this)->option);
169                         free((*this)->value);
170                         free(*this);
171                         *this = new;
172                         break;
173                 }
174         }
175         if (new && !*this) {
176                 *this = new;
177                 new->next = NULL;
178         }
179         return 0;
180 }
181
182 #define BODY_HTTP10 -1
183 #define BODY_CHUNKED -2
184
185 static int process_http_response(struct openconnect_info *vpninfo, int *result,
186                                  int (*header_cb)(struct openconnect_info *, char *, char *),
187                                  char **body_ret)
188 {
189         char buf[MAX_BUF_LEN];
190         char *body = NULL;
191         int bodylen = BODY_HTTP10;
192         int done = 0;
193         int closeconn = 0;
194         int i;
195
196  cont:
197         if (openconnect_SSL_gets(vpninfo, buf, sizeof(buf)) < 0) {
198                 vpn_progress(vpninfo, PRG_ERR,
199                              _("Error fetching HTTPS response\n"));
200                 return -EINVAL;
201         }
202
203         if (!strncmp(buf, "HTTP/1.0 ", 9))
204                 closeconn = 1;
205         
206         if ((!closeconn && strncmp(buf, "HTTP/1.1 ", 9)) || !(*result = atoi(buf+9))) {
207                 vpn_progress(vpninfo, PRG_ERR,
208                              _("Failed to parse HTTP response '%s'\n"), buf);
209                 return -EINVAL;
210         }
211
212         vpn_progress(vpninfo, (*result==200)?PRG_TRACE:PRG_INFO,
213                      _("Got HTTP response: %s\n"), buf);
214
215         /* Eat headers... */
216         while ((i = openconnect_SSL_gets(vpninfo, buf, sizeof(buf)))) {
217                 char *colon;
218
219                 if (i < 0) {
220                         vpn_progress(vpninfo, PRG_ERR,
221                                      _("Error processing HTTP response\n"));
222                         return -EINVAL;
223                 }
224                 colon = strchr(buf, ':');
225                 if (!colon) {
226                         vpn_progress(vpninfo, PRG_ERR,
227                                      _("Ignoring unknown HTTP response line '%s'\n"), buf);
228                         continue;
229                 }
230                 *(colon++) = 0;
231                 if (*colon == ' ')
232                         colon++;
233
234                 /* Handle Set-Cookie first so that we can avoid printing the
235                    webvpn cookie in the verbose debug output */
236                 if (!strcasecmp(buf, "Set-Cookie")) {
237                         char *semicolon = strchr(colon, ';');
238                         const char *print_equals;
239                         char *equals = strchr(colon, '=');
240                         int ret;
241
242                         if (semicolon)
243                                 *semicolon = 0;
244
245                         if (!equals) {
246                                 vpn_progress(vpninfo, PRG_ERR,
247                                              _("Invalid cookie offered: %s\n"), buf);
248                                 return -EINVAL;
249                         }
250                         *(equals++) = 0;
251
252                         print_equals = equals;
253                         /* Don't print the webvpn cookie unless it's empty; we don't
254                            want people posting it in public with debugging output */
255                         if (!strcmp(colon, "webvpn") && *equals)
256                                 print_equals = _("<elided>");
257                         vpn_progress(vpninfo, PRG_TRACE, "%s: %s=%s%s%s\n",
258                                      buf, colon, print_equals, semicolon?";":"",
259                                      semicolon?(semicolon+1):"");
260
261                         /* The server tends to ask for the username and password as
262                            usual, even if we've already failed because it didn't like
263                            our cert. Thankfully it does give us this hint... */
264                         if (!strcmp(colon, "ClientCertAuthFailed"))
265                                 vpn_progress(vpninfo, PRG_ERR,
266                                              _("SSL certificate authentication failed\n"));
267
268                         ret = http_add_cookie(vpninfo, colon, equals);
269                         if (ret)
270                                 return ret;
271                 } else {
272                         vpn_progress(vpninfo, PRG_TRACE, "%s: %s\n", buf, colon);
273                 }
274
275                 if (!strcasecmp(buf, "Connection")) {
276                         if (!strcasecmp(colon, "Close"))
277                                 closeconn = 1;
278 #if 0
279                         /* This might seem reasonable, but in fact it breaks
280                            certificate authentication with some servers. If
281                            they give an HTTP/1.0 response, even if they
282                            explicitly give a Connection: Keep-Alive header,
283                            just close the connection. */
284                         else if (!strcasecmp(colon, "Keep-Alive"))
285                                 closeconn = 0;
286 #endif
287                 }
288                 if (!strcasecmp(buf, "Location")) {
289                         vpninfo->redirect_url = strdup(colon);
290                         if (!vpninfo->redirect_url)
291                                 return -ENOMEM;
292                 }
293                 if (!strcasecmp(buf, "Content-Length")) {
294                         bodylen = atoi(colon);
295                         if (bodylen < 0) {
296                                 vpn_progress(vpninfo, PRG_ERR,
297                                              _("Response body has negative size (%d)\n"),
298                                              bodylen);
299                                 return -EINVAL;
300                         }
301                 }
302                 if (!strcasecmp(buf, "Transfer-Encoding")) {
303                         if (!strcasecmp(colon, "chunked"))
304                                 bodylen = BODY_CHUNKED;
305                         else {
306                                 vpn_progress(vpninfo, PRG_ERR,
307                                              _("Unknown Transfer-Encoding: %s\n"),
308                                              colon);
309                                 return -EINVAL;
310                         }
311                 }
312                 if (header_cb && !strncmp(buf, "X-", 2))
313                         header_cb(vpninfo, buf, colon);
314         }
315
316         /* Handle 'HTTP/1.1 100 Continue'. Not that we should ever see it */
317         if (*result == 100)
318                 goto cont;
319
320         /* Now the body, if there is one */
321         vpn_progress(vpninfo, PRG_TRACE, _("HTTP body %s (%d)\n"), 
322                      bodylen==BODY_HTTP10?"http 1.0" :
323                      bodylen==BODY_CHUNKED?"chunked" : "length: ",
324                      bodylen);
325
326         /* If we were given Content-Length, it's nice and easy... */
327         if (bodylen > 0) {
328                 body = malloc(bodylen + 1);
329                 if (!body)
330                         return -ENOMEM;
331                 while (done < bodylen) {
332                         i = openconnect_SSL_read(vpninfo, body + done, bodylen - done);
333                         if (i < 0) {
334                                 vpn_progress(vpninfo, PRG_ERR,
335                                              _("Error reading HTTP response body\n"));
336                                 free(body);
337                                 return -EINVAL;
338                         }
339                         done += i;
340                 }
341         } else if (bodylen == BODY_CHUNKED) {
342                 /* ... else, chunked */
343                 while ((i = openconnect_SSL_gets(vpninfo, buf, sizeof(buf)))) {
344                         int chunklen, lastchunk = 0;
345
346                         if (i < 0) {
347                                 vpn_progress(vpninfo, PRG_ERR,
348                                              _("Error fetching chunk header\n"));
349                                 return i;
350                         }
351                         chunklen = strtol(buf, NULL, 16);
352                         if (!chunklen) {
353                                 lastchunk = 1;
354                                 goto skip;
355                         }
356                         body = realloc(body, done + chunklen + 1);
357                         if (!body)
358                                 return -ENOMEM;
359                         while (chunklen) {
360                                 i = openconnect_SSL_read(vpninfo, body + done, chunklen);
361                                 if (i < 0) {
362                                         vpn_progress(vpninfo, PRG_ERR,
363                                                      _("Error reading HTTP response body\n"));
364                                         free(body);
365                                         return -EINVAL;
366                                 }
367                                 chunklen -= i;
368                                 done += i;
369                         }
370                 skip:
371                         if ((i = openconnect_SSL_gets(vpninfo, buf, sizeof(buf)))) {
372                                 if (i < 0) {
373                                         vpn_progress(vpninfo, PRG_ERR,
374                                                      _("Error fetching HTTP response body\n"));
375                                 } else {
376                                         vpn_progress(vpninfo, PRG_ERR,
377                                                      _("Error in chunked decoding. Expected '', got: '%s'"),
378                                                      buf);
379                                 }
380                                 free(body);
381                                 return -EINVAL;
382                         }
383
384                         if (lastchunk)
385                                 break;
386                 }
387         } else if (bodylen == BODY_HTTP10) {
388                 if (!closeconn) {
389                         vpn_progress(vpninfo, PRG_ERR,
390                                      _("Cannot receive HTTP 1.0 body without closing connection\n"));
391                         return -EINVAL;
392                 }
393
394                 /* HTTP 1.0 response. Just eat all we can in 16KiB chunks */
395                 while (1) {
396                         body = realloc(body, done + 16384);
397                         if (!body)
398                                 return -ENOMEM;
399                         i = openconnect_SSL_read(vpninfo, body + done, 16384);
400                         if (i > 0) {
401                                 /* Got more data */
402                                 done += i;
403                         } else if (i < 0) {
404                                 /* Error */
405                                 free(body);
406                                 return i;
407                         } else {
408                                 /* Connection closed. Reduce allocation to just what we need */
409                                 body = realloc(body, done + 1);
410                                 if (!body)
411                                         return -ENOMEM;
412                                 break;
413                         }
414                 }
415         }
416
417         if (closeconn || vpninfo->no_http_keepalive)
418                 openconnect_close_https(vpninfo, 0);
419
420         if (body)
421                 body[done] = 0;
422         *body_ret = body;
423         return done;
424 }
425
426 static void add_common_headers(struct openconnect_info *vpninfo, struct oc_text_buf *buf)
427 {
428         struct vpn_option *opt;
429
430         buf_append(buf, "Host: %s\r\n", vpninfo->hostname);
431         buf_append(buf, "User-Agent: %s\r\n", vpninfo->useragent);
432         buf_append(buf, "Accept: */*\r\n");
433         buf_append(buf, "Accept-Encoding: identity\r\n");
434
435         if (vpninfo->cookies) {
436                 buf_append(buf, "Cookie: ");
437                 for (opt = vpninfo->cookies; opt; opt = opt->next)
438                         buf_append(buf, "%s=%s%s", opt->option,
439                                       opt->value, opt->next ? "; " : "\r\n");
440         }
441         buf_append(buf, "X-Transcend-Version: 1\r\n");
442         buf_append(buf, "X-Aggregate-Auth: 1\r\n");
443         buf_append(buf, "X-AnyConnect-Platform: %s\r\n", vpninfo->platname);
444 }
445
446 static int fetch_config(struct openconnect_info *vpninfo, char *fu, char *bu,
447                         char *server_sha1)
448 {
449         struct oc_text_buf *buf;
450         char *config_buf = NULL;
451         int result, buflen;
452         unsigned char local_sha1_bin[SHA1_SIZE];
453         char local_sha1_ascii[(SHA1_SIZE * 2)+1];
454         int i;
455
456         if (openconnect_open_https(vpninfo)) {
457                 vpn_progress(vpninfo, PRG_ERR,
458                              _("Failed to open HTTPS connection to %s\n"),
459                              vpninfo->hostname);
460                 return -EINVAL;
461         }
462
463         buf = buf_alloc();
464         buf_append(buf, "GET %s%s HTTP/1.1\r\n", fu, bu);
465         add_common_headers(vpninfo, buf);
466         buf_append(buf, "\r\n");
467
468         if (buf_error(buf))
469                 return buf_free(buf);
470
471         if (openconnect_SSL_write(vpninfo, buf->data, buf->pos) != buf->pos) {
472                 vpn_progress(vpninfo, PRG_ERR,
473                              _("Failed to send GET request for new config\n"));
474                 buf_free(buf);
475                 return -EIO;
476         }
477         buf_free(buf);
478
479         buflen = process_http_response(vpninfo, &result, NULL, &config_buf);
480         if (buflen < 0) {
481                 /* We'll already have complained about whatever offended us */
482                 return -EINVAL;
483         }
484
485         if (result != 200) {
486                 free(config_buf);
487                 return -EINVAL;
488         }
489
490         openconnect_sha1(local_sha1_bin, config_buf, buflen);
491
492         for (i = 0; i < SHA1_SIZE; i++)
493                 sprintf(&local_sha1_ascii[i*2], "%02x", local_sha1_bin[i]);
494
495         if (strcasecmp(server_sha1, local_sha1_ascii)) {
496                 vpn_progress(vpninfo, PRG_ERR,
497                              _("Downloaded config file did not match intended SHA1\n"));
498                 free(config_buf);
499                 return -EINVAL;
500         }
501
502         result = vpninfo->write_new_config(vpninfo->cbdata, config_buf, buflen);
503         free(config_buf);
504         return result;
505 }
506
507 static int run_csd_script(struct openconnect_info *vpninfo, char *buf, int buflen)
508 {
509         char fname[16];
510         int fd, ret;
511
512         if (!vpninfo->uid_csd_given && !vpninfo->csd_wrapper) {
513                 vpn_progress(vpninfo, PRG_ERR,
514                              _("Error: Server asked us to download and run a 'Cisco Secure Desktop' trojan.\n"
515                                "This facility is disabled by default for security reasons, so you may wish to enable it."));
516                 return -EPERM;
517         }
518
519 #ifndef __linux__
520         vpn_progress(vpninfo, PRG_INFO,
521                      _("Trying to run Linux CSD trojan script."));
522 #endif
523
524         sprintf(fname, "/tmp/csdXXXXXX");
525         fd = mkstemp(fname);
526         if (fd < 0) {
527                 int err = -errno;
528                 vpn_progress(vpninfo, PRG_ERR,
529                              _("Failed to open temporary CSD script file: %s\n"),
530                              strerror(errno));
531                 return err;
532         }
533
534         ret = proxy_write(vpninfo, fd, (void *)buf, buflen);
535         if (ret) {
536                 vpn_progress(vpninfo, PRG_ERR,
537                              _("Failed to write temporary CSD script file: %s\n"),
538                              strerror(-ret));
539                 return ret;
540         }
541         fchmod(fd, 0755);
542         close(fd);
543
544         if (!fork()) {
545                 char scertbuf[MD5_SIZE * 2 + 1];
546                 char ccertbuf[MD5_SIZE * 2 + 1];
547                 char *csd_argv[32];
548                 int i = 0;
549
550                 if (vpninfo->uid_csd_given && vpninfo->uid_csd != getuid()) {
551                         struct passwd *pw;
552
553                         if (setuid(vpninfo->uid_csd)) {
554                                 fprintf(stderr, _("Failed to set uid %ld\n"),
555                                         (long)vpninfo->uid_csd);
556                                 exit(1);
557                         }
558                         if (!(pw = getpwuid(vpninfo->uid_csd))) {
559                                 fprintf(stderr, _("Invalid user uid=%ld\n"),
560                                         (long)vpninfo->uid_csd);
561                                 exit(1);
562                         }
563                         setenv("HOME", pw->pw_dir, 1);
564                         if (chdir(pw->pw_dir)) {
565                                 fprintf(stderr, _("Failed to change to CSD home directory '%s': %s\n"),
566                                         pw->pw_dir, strerror(errno));
567                                 exit(1);
568                         }
569                 }
570                 if (getuid() == 0 && !vpninfo->csd_wrapper) {
571                         fprintf(stderr, _("Warning: you are running insecure "
572                                           "CSD code with root privileges\n"
573                                           "\t Use command line option \"--csd-user\"\n"));
574                 }
575                 if (vpninfo->uid_csd_given == 2) {
576                         /* The NM tool really needs not to get spurious output
577                            on stdout, which the CSD trojan spews. */
578                         dup2(2, 1);
579                 }
580                 if (vpninfo->csd_wrapper)
581                         csd_argv[i++] = vpninfo->csd_wrapper;
582                 csd_argv[i++] = fname;
583                 csd_argv[i++]= (char *)"-ticket";
584                 if (asprintf(&csd_argv[i++], "\"%s\"", vpninfo->csd_ticket) == -1)
585                         return -ENOMEM;
586                 csd_argv[i++]= (char *)"-stub";
587                 csd_argv[i++]= (char *)"\"0\"";
588                 csd_argv[i++]= (char *)"-group";
589                 if (asprintf(&csd_argv[i++], "\"%s\"", vpninfo->authgroup?:"") == -1)
590                         return -ENOMEM;
591
592                 openconnect_local_cert_md5(vpninfo, ccertbuf);
593                 scertbuf[0] = 0;
594                 get_cert_md5_fingerprint(vpninfo, vpninfo->peer_cert, scertbuf);
595                 csd_argv[i++]= (char *)"-certhash";
596                 if (asprintf(&csd_argv[i++], "\"%s:%s\"", scertbuf, ccertbuf) == -1)
597                         return -ENOMEM;
598
599                 csd_argv[i++]= (char *)"-url";
600                 if (asprintf(&csd_argv[i++], "\"https://%s%s\"", vpninfo->hostname, vpninfo->csd_starturl) == -1)
601                         return -ENOMEM;
602
603                 csd_argv[i++]= (char *)"-langselen";
604                 csd_argv[i++] = NULL;
605
606                 execv(csd_argv[0], csd_argv);
607                 vpn_progress(vpninfo, PRG_ERR,
608                              _("Failed to exec CSD script %s\n"), csd_argv[0]);
609                 exit(1);
610         }
611
612         free(vpninfo->csd_stuburl);
613         vpninfo->csd_stuburl = NULL;
614         vpninfo->urlpath = strdup(vpninfo->csd_waiturl +
615                                   (vpninfo->csd_waiturl[0] == '/' ? 1 : 0));
616         vpninfo->csd_waiturl = NULL;
617         vpninfo->csd_scriptname = strdup(fname);
618
619         http_add_cookie(vpninfo, "sdesktop", vpninfo->csd_token);
620
621         return 0;
622 }
623
624 int internal_parse_url(char *url, char **res_proto, char **res_host,
625                        int *res_port, char **res_path, int default_port)
626 {
627         char *proto = url;
628         char *host, *path, *port_str;
629         int port;
630
631         host = strstr(url, "://");
632         if (host) {
633                 *host = 0;
634                 host += 3;
635
636                 if (!strcasecmp(proto, "https"))
637                         port = 443;
638                 else if (!strcasecmp(proto, "http"))
639                         port = 80;
640                 else if (!strcasecmp(proto, "socks") ||
641                          !strcasecmp(proto, "socks4") ||
642                          !strcasecmp(proto, "socks5"))
643                         port = 1080;
644                 else
645                         return -EPROTONOSUPPORT;
646         } else {
647                 if (default_port) {
648                         proto = NULL;
649                         port = default_port;
650                         host = url;
651                 } else
652                         return -EINVAL;
653         }
654
655         path = strchr(host, '/');
656         if (path)
657                 *(path++) = 0;
658
659         port_str = strrchr(host, ':');
660         if (port_str) {
661                 char *end;
662                 int new_port = strtol(port_str + 1, &end, 10);
663
664                 if (!*end) {
665                         *port_str = 0;
666                         port = new_port;
667                 }
668         }
669
670         if (res_proto)
671                 *res_proto = proto ? strdup(proto) : NULL;
672         if (res_host)
673                 *res_host = strdup(host);
674         if (res_port)
675                 *res_port = port;
676         if (res_path)
677                 *res_path = (path && *path) ? strdup(path) : NULL;
678
679         /* Undo the damage we did to the original string */
680         if (port_str)
681                 *(port_str) = ':';
682         if (path)
683                 *(path - 1) = '/';
684         if (proto)
685                 *(host - 3) = ':';
686         return 0;
687 }
688
689 static void clear_cookies(struct openconnect_info *vpninfo)
690 {
691         struct vpn_option *opt, *next;
692
693         for (opt = vpninfo->cookies; opt; opt = next) {
694                 next = opt->next;
695
696                 free(opt->option);
697                 free(opt->value);
698                 free(opt);
699         }
700         vpninfo->cookies = NULL;
701 }
702
703 /* Return value:
704  *  < 0, on error
705  *  = 0, on success (go ahead and retry with the latest vpninfo->{hostname,urlpath,port,...})
706  */
707 static int handle_redirect(struct openconnect_info *vpninfo)
708 {
709         if (!strncmp(vpninfo->redirect_url, "https://", 8)) {
710                 /* New host. Tear down the existing connection and make a new one */
711                 char *host;
712                 int port;
713                 int ret;
714
715                 free(vpninfo->urlpath);
716                 vpninfo->urlpath = NULL;
717
718                 ret = internal_parse_url(vpninfo->redirect_url, NULL, &host, &port, &vpninfo->urlpath, 0);
719                 if (ret) {
720                         vpn_progress(vpninfo, PRG_ERR,
721                                      _("Failed to parse redirected URL '%s': %s\n"),
722                                      vpninfo->redirect_url, strerror(-ret));
723                         free(vpninfo->redirect_url);
724                         vpninfo->redirect_url = NULL;
725                         return ret;
726                 }
727
728                 if (strcasecmp(vpninfo->hostname, host) || port != vpninfo->port) {
729                         free(vpninfo->hostname);
730                         vpninfo->hostname = host;
731                         vpninfo->port = port;
732
733                         /* Kill the existing connection, and a new one will happen */
734                         free(vpninfo->peer_addr);
735                         vpninfo->peer_addr = NULL;
736                         openconnect_close_https(vpninfo, 0);
737                         clear_cookies(vpninfo);
738                 } else
739                         free(host);
740
741                 free(vpninfo->redirect_url);
742                 vpninfo->redirect_url = NULL;
743
744                 return 0;
745         } else if (strstr(vpninfo->redirect_url, "://")) {
746                 vpn_progress(vpninfo, PRG_ERR,
747                              _("Cannot follow redirection to non-https URL '%s'\n"),
748                              vpninfo->redirect_url);
749                 free(vpninfo->redirect_url);
750                 vpninfo->redirect_url = NULL;
751                 return -EINVAL;
752         } else if (vpninfo->redirect_url[0] == '/') {
753                 /* Absolute redirect within same host */
754                 free(vpninfo->urlpath);
755                 vpninfo->urlpath = strdup(vpninfo->redirect_url + 1);
756                 free(vpninfo->redirect_url);
757                 vpninfo->redirect_url = NULL;
758                 return 0;
759         } else {
760                 char *lastslash = NULL;
761                 if (vpninfo->urlpath)
762                         lastslash = strrchr(vpninfo->urlpath, '/');
763                 if (!lastslash) {
764                         free(vpninfo->urlpath);
765                         vpninfo->urlpath = vpninfo->redirect_url;
766                         vpninfo->redirect_url = NULL;
767                 } else {
768                         char *oldurl = vpninfo->urlpath;
769                         *lastslash = 0;
770                         vpninfo->urlpath = NULL;
771                         if (asprintf(&vpninfo->urlpath, "%s/%s",
772                                      oldurl, vpninfo->redirect_url) == -1) {
773                                 int err = -errno;
774                                 vpn_progress(vpninfo, PRG_ERR,
775                                              _("Allocating new path for relative redirect failed: %s\n"),
776                                              strerror(-err));
777                                 return err;
778                         }
779                         free(oldurl);
780                         free(vpninfo->redirect_url);
781                         vpninfo->redirect_url = NULL;
782                 }
783                 return 0;
784         }
785 }
786
787 /* Inputs:
788  *  method:             GET or POST
789  *  vpninfo->hostname:  Host DNS name
790  *  vpninfo->port:      TCP port, typically 443
791  *  vpninfo->urlpath:   Relative path, e.g. /+webvpn+/foo.html
792  *  request_body_type:  Content type for a POST (e.g. text/html).  Can be NULL.
793  *  request_body:       POST content
794  *  form_buf:           Callee-allocated buffer for server content
795  *
796  * Return value:
797  *  < 0, on error
798  *  >=0, on success, indicating the length of the data in *form_buf
799  */
800 static int do_https_request(struct openconnect_info *vpninfo, const char *method,
801                             const char *request_body_type, const char *request_body,
802                             char **form_buf)
803 {
804         struct oc_text_buf *buf;
805         int result, buflen;
806
807  retry:
808         if (*form_buf) {
809                 free(*form_buf);
810                 *form_buf = NULL;
811         }
812         if (openconnect_open_https(vpninfo)) {
813                 vpn_progress(vpninfo, PRG_ERR,
814                              _("Failed to open HTTPS connection to %s\n"),
815                              vpninfo->hostname);
816                 return -EINVAL;
817         }
818
819         /*
820          * It would be nice to use cURL for this, but we really need to guarantee
821          * that we'll be using OpenSSL (for the TPM stuff), and it doesn't seem
822          * to have any way to let us provide our own socket read/write functions.
823          * We can only provide a socket _open_ function. Which would require having
824          * a socketpair() and servicing the "other" end of it.
825          *
826          * So we process the HTTP for ourselves...
827          */
828         buf = buf_alloc();
829         buf_append(buf, "%s /%s HTTP/1.1\r\n", method, vpninfo->urlpath ?: "");
830         add_common_headers(vpninfo, buf);
831
832         if (request_body_type) {
833                 buf_append(buf, "Content-Type: %s\r\n", request_body_type);
834                 buf_append(buf, "Content-Length: %zd\r\n", strlen(request_body));
835         }
836         buf_append(buf, "\r\n");
837
838         if (request_body_type)
839                 buf_append(buf, "%s", request_body);
840
841         if (vpninfo->port == 443)
842                 vpn_progress(vpninfo, PRG_INFO, "%s https://%s/%s\n",
843                              method, vpninfo->hostname,
844                              vpninfo->urlpath ?: "");
845         else
846                 vpn_progress(vpninfo, PRG_INFO, "%s https://%s:%d/%s\n",
847                              method, vpninfo->hostname, vpninfo->port,
848                              vpninfo->urlpath ?: "");
849
850         if (buf_error(buf))
851                 return buf_free(buf);
852
853         result = openconnect_SSL_write(vpninfo, buf->data, buf->pos);
854         buf_free(buf);
855         if (result < 0)
856                 return result;
857
858         buflen = process_http_response(vpninfo, &result, NULL, form_buf);
859         if (buflen < 0) {
860                 /* We'll already have complained about whatever offended us */
861                 return buflen;
862         }
863
864         if (result != 200 && vpninfo->redirect_url) {
865                 result = handle_redirect(vpninfo);
866                 if (result == 0)
867                         goto retry;
868                 goto out;
869         }
870         if (!*form_buf || result != 200) {
871                 vpn_progress(vpninfo, PRG_ERR,
872                              _("Unexpected %d result from server\n"),
873                              result);
874                 result = -EINVAL;
875                 goto out;
876         }
877
878         return buflen;
879
880  out:
881         free(*form_buf);
882         *form_buf = NULL;
883         return result;
884 }
885
886 /* Return value:
887  *  < 0, on error
888  *  > 0, no cookie (user cancel)
889  *  = 0, obtained cookie
890  */
891 int openconnect_obtain_cookie(struct openconnect_info *vpninfo)
892 {
893         struct vpn_option *opt;
894         char *form_buf = NULL;
895         struct oc_auth_form *form;
896         int result, buflen;
897         char request_body[2048];
898         const char *request_body_type = NULL;
899         const char *method = "GET";
900
901         if (vpninfo->use_stoken) {
902                 result = prepare_stoken(vpninfo);
903                 if (result)
904                         return result;
905         }
906
907  retry:
908         buflen = do_https_request(vpninfo, method, request_body_type, request_body, &form_buf);
909         if (buflen < 0)
910                 return buflen;
911
912         if (vpninfo->csd_stuburl) {
913                 /* This is the CSD stub script, which we now need to run */
914                 result = run_csd_script(vpninfo, form_buf, buflen);
915                 if (result) {
916                         free(form_buf);
917                         return result;
918                 }
919
920                 /* Now we'll be redirected to the waiturl */
921                 goto retry;
922         }
923         if (strncmp(form_buf, "<?xml", 5)) {
924                 /* Not XML? Perhaps it's HTML with a refresh... */
925                 if (strcasestr(form_buf, "http-equiv=\"refresh\"")) {
926                         vpn_progress(vpninfo, PRG_INFO,
927                                      _("Refreshing %s after 1 second...\n"),
928                                      vpninfo->urlpath);
929                         sleep(1);
930                         goto retry;
931                 }
932                 vpn_progress(vpninfo, PRG_ERR,
933                              _("Unknown response from server\n"));
934                 free(form_buf);
935                 return -EINVAL;
936         }
937         result = parse_xml_response(vpninfo, form_buf, &form);
938         if (result) {
939                 free(form_buf);
940                 return -ENOMEM;
941         }
942         request_body[0] = 0;
943         result = handle_auth_form(vpninfo, form, request_body, sizeof(request_body),
944                                   &method, &request_body_type, 0);
945         free_auth_form(form);
946
947         free(form_buf);
948         form_buf = NULL;
949
950         if (!result) {
951                 result = handle_redirect(vpninfo);
952                 if (result == 0)
953                         goto retry;
954                 return result;
955         }
956
957         if (result != 2)
958                 return result;
959
960         /* A return value of 2 means the XML form indicated
961            success. We _should_ have a cookie... */
962
963         for (opt = vpninfo->cookies; opt; opt = opt->next) {
964
965                 if (!strcmp(opt->option, "webvpn"))
966                         vpninfo->cookie = opt->value;
967                 else if (vpninfo->write_new_config && !strcmp(opt->option, "webvpnc")) {
968                         char *tok = opt->value;
969                         char *bu = NULL, *fu = NULL, *sha = NULL;
970
971                         do {
972                                 if (tok != opt->value)
973                                         *(tok++) = 0;
974
975                                 if (!strncmp(tok, "bu:", 3))
976                                         bu = tok + 3;
977                                 else if (!strncmp(tok, "fu:", 3))
978                                         fu = tok + 3;
979                                 else if (!strncmp(tok, "fh:", 3)) {
980                                         if (!strncasecmp(tok+3, vpninfo->xmlsha1,
981                                                          SHA1_SIZE * 2))
982                                                 break;
983                                         sha = tok + 3;
984                                 }
985                         } while ((tok = strchr(tok, '&')));
986
987                         if (bu && fu && sha)
988                                 fetch_config(vpninfo, bu, fu, sha);
989                 }
990         }
991         if (vpninfo->csd_scriptname) {
992                 unlink(vpninfo->csd_scriptname);
993                 free(vpninfo->csd_scriptname);
994                 vpninfo->csd_scriptname = NULL;
995         }
996         return 0;
997 }
998
999 char *openconnect_create_useragent(const char *base)
1000 {
1001         char *uagent;
1002
1003         if (asprintf(&uagent, "%s %s", base, openconnect_version_str) < 0)
1004                 return NULL;
1005
1006         return uagent;
1007 }
1008
1009 static int proxy_gets(struct openconnect_info *vpninfo, int fd,
1010                       char *buf, size_t len)
1011 {
1012         int i = 0;
1013         int ret;
1014
1015         if (len < 2)
1016                 return -EINVAL;
1017
1018         while ( (ret = proxy_read(vpninfo, fd, (void *)(buf + i), 1)) == 0) {
1019                 if (buf[i] == '\n') {
1020                         buf[i] = 0;
1021                         if (i && buf[i-1] == '\r') {
1022                                 buf[i-1] = 0;
1023                                 i--;
1024                         }
1025                         return i;
1026                 }
1027                 i++;
1028
1029                 if (i >= len - 1) {
1030                         buf[i] = 0;
1031                         return i;
1032                 }
1033         }
1034         buf[i] = 0;
1035         return i ?: ret;
1036 }
1037
1038 static int proxy_write(struct openconnect_info *vpninfo, int fd,
1039                        unsigned char *buf, size_t len)
1040 {
1041         size_t count;
1042
1043         for (count = 0; count < len; ) {
1044                 fd_set rd_set, wr_set;
1045                 int maxfd = fd;
1046                 int i;
1047
1048                 FD_ZERO(&wr_set);
1049                 FD_ZERO(&rd_set);
1050                 FD_SET(fd, &wr_set);
1051                 if (vpninfo->cancel_fd != -1) {
1052                         FD_SET(vpninfo->cancel_fd, &rd_set);
1053                         if (vpninfo->cancel_fd > fd)
1054                                 maxfd = vpninfo->cancel_fd;
1055                 }
1056
1057                 select(maxfd + 1, &rd_set, &wr_set, NULL, NULL);
1058                 if (vpninfo->cancel_fd != -1 &&
1059                     FD_ISSET(vpninfo->cancel_fd, &rd_set))
1060                         return -EINTR;
1061
1062                 /* Not that this should ever be able to happen... */
1063                 if (!FD_ISSET(fd, &wr_set))
1064                         continue;
1065
1066                 i = write(fd, buf + count, len - count);
1067                 if (i < 0)
1068                         return -errno;
1069
1070                 count += i;
1071         }
1072         return 0;
1073 }
1074
1075 static int proxy_read(struct openconnect_info *vpninfo, int fd,
1076                       unsigned char *buf, size_t len)
1077 {
1078         size_t count;
1079
1080         for (count = 0; count < len; ) {
1081                 fd_set rd_set;
1082                 int maxfd = fd;
1083                 int i;
1084
1085                 FD_ZERO(&rd_set);
1086                 FD_SET(fd, &rd_set);
1087                 if (vpninfo->cancel_fd != -1) {
1088                         FD_SET(vpninfo->cancel_fd, &rd_set);
1089                         if (vpninfo->cancel_fd > fd)
1090                                 maxfd = vpninfo->cancel_fd;
1091                 }
1092
1093                 select(maxfd + 1, &rd_set, NULL, NULL, NULL);
1094                 if (vpninfo->cancel_fd != -1 &&
1095                     FD_ISSET(vpninfo->cancel_fd, &rd_set))
1096                         return -EINTR;
1097
1098                 /* Not that this should ever be able to happen... */
1099                 if (!FD_ISSET(fd, &rd_set))
1100                         continue;
1101
1102                 i = read(fd, buf + count, len - count);
1103                 if (i < 0)
1104                         return -errno;
1105
1106                 count += i;
1107         }
1108         return 0;
1109 }
1110
1111 static const char *socks_errors[] = {
1112         N_("request granted"),
1113         N_("general failure"),
1114         N_("connection not allowed by ruleset"),
1115         N_("network unreachable"),
1116         N_("host unreachable"),
1117         N_("connection refused by destination host"),
1118         N_("TTL expired"),
1119         N_("command not supported / protocol error"),
1120         N_("address type not supported")
1121 };
1122
1123 static int process_socks_proxy(struct openconnect_info *vpninfo, int ssl_sock)
1124 {
1125         unsigned char buf[1024];
1126         int i;
1127
1128         buf[0] = 5; /* SOCKS version */
1129         buf[1] = 1; /* # auth methods */
1130         buf[2] = 0; /* No auth supported */
1131
1132         if ((i = proxy_write(vpninfo, ssl_sock, buf, 3))) {
1133                 vpn_progress(vpninfo, PRG_ERR,
1134                              _("Error writing auth request to SOCKS proxy: %s\n"),
1135                              strerror(-i));
1136                 return i;
1137         }
1138         
1139         if ((i = proxy_read(vpninfo, ssl_sock, buf, 2))) {
1140                 vpn_progress(vpninfo, PRG_ERR,
1141                              _("Error reading auth response from SOCKS proxy: %s\n"),
1142                              strerror(-i));
1143                 return i;
1144         }
1145         if (buf[0] != 5) {
1146                 vpn_progress(vpninfo, PRG_ERR,
1147                              _("Unexpected auth response from SOCKS proxy: %02x %02x\n"),
1148                              buf[0], buf[1]);
1149                 return -EIO;
1150         }
1151         if (buf[1]) {
1152         socks_err:
1153                 if (buf[1] < sizeof(socks_errors) / sizeof(socks_errors[0]))
1154                         vpn_progress(vpninfo, PRG_ERR,
1155                                      _("SOCKS proxy error %02x: %s\n"),
1156                                      buf[1], _(socks_errors[buf[1]]));
1157                 else
1158                         vpn_progress(vpninfo, PRG_ERR,
1159                                      _("SOCKS proxy error %02x\n"),
1160                                      buf[1]);
1161                 return -EIO;
1162         }
1163
1164         vpn_progress(vpninfo, PRG_INFO,
1165                      _("Requesting SOCKS proxy connection to %s:%d\n"),
1166                      vpninfo->hostname, vpninfo->port);
1167
1168         buf[0] = 5; /* SOCKS version */
1169         buf[1] = 1; /* CONNECT */
1170         buf[2] = 0; /* Reserved */
1171         buf[3] = 3; /* Address type is domain name */
1172         buf[4] = strlen(vpninfo->hostname);
1173         strcpy((char *)buf + 5, vpninfo->hostname);
1174         i = strlen(vpninfo->hostname) + 5;
1175         buf[i++] = vpninfo->port >> 8;
1176         buf[i++] = vpninfo->port & 0xff;
1177
1178         if ((i = proxy_write(vpninfo, ssl_sock, buf, i))) {
1179                 vpn_progress(vpninfo, PRG_ERR,
1180                              _("Error writing connect request to SOCKS proxy: %s\n"),
1181                              strerror(-i));
1182                 return i;
1183         }
1184         /* Read 5 bytes -- up to and including the first byte of the returned
1185            address (which might be the length byte of a domain name) */
1186         if ((i = proxy_read(vpninfo, ssl_sock, buf, 5))) {
1187                 vpn_progress(vpninfo, PRG_ERR,
1188                              _("Error reading connect response from SOCKS proxy: %s\n"),
1189                              strerror(-i));
1190                 return i;
1191         }
1192         if (buf[0] != 5) {
1193                 vpn_progress(vpninfo, PRG_ERR,
1194                              _("Unexpected connect response from SOCKS proxy: %02x %02x...\n"),
1195                              buf[0], buf[1]);
1196                 return -EIO;
1197         }
1198         if (buf[1])
1199                 goto socks_err;
1200
1201         /* Connect responses contain an address */
1202         switch(buf[3]) {
1203         case 1: /* Legacy IP */
1204                 i = 5;
1205                 break;
1206         case 3: /* Domain name */
1207                 i = buf[4] + 2;
1208                 break;
1209         case 4: /* IPv6 */
1210                 i = 17;
1211                 break;
1212         default:
1213                 vpn_progress(vpninfo, PRG_ERR,
1214                              _("Unexpected address type %02x in SOCKS connect response\n"),
1215                              buf[3]);
1216                 return -EIO;
1217         }
1218
1219         if ((i = proxy_read(vpninfo, ssl_sock, buf, i))) {
1220                 vpn_progress(vpninfo, PRG_ERR,
1221                              _("Error reading connect response from SOCKS proxy: %s\n"),
1222                              strerror(-i));
1223                 return i;
1224         }
1225         return 0;
1226 }
1227
1228 static int process_http_proxy(struct openconnect_info *vpninfo, int ssl_sock)
1229 {
1230         char buf[MAX_BUF_LEN];
1231         struct oc_text_buf *reqbuf;
1232         int buflen, result;
1233
1234         reqbuf = buf_alloc();
1235         buf_append(reqbuf, "CONNECT %s:%d HTTP/1.1\r\n", vpninfo->hostname, vpninfo->port);
1236         buf_append(reqbuf, "Host: %s\r\n", vpninfo->hostname);
1237         buf_append(reqbuf, "User-Agent: %s\r\n", vpninfo->useragent);
1238         buf_append(reqbuf, "Proxy-Connection: keep-alive\r\n");
1239         buf_append(reqbuf, "Connection: keep-alive\r\n");
1240         buf_append(reqbuf, "Accept-Encoding: identity\r\n");
1241         buf_append(reqbuf, "\r\n");
1242
1243         if (buf_error(reqbuf))
1244                 return buf_free(reqbuf);
1245
1246         vpn_progress(vpninfo, PRG_INFO,
1247                      _("Requesting HTTP proxy connection to %s:%d\n"),
1248                      vpninfo->hostname, vpninfo->port);
1249
1250         result = proxy_write(vpninfo, ssl_sock, (unsigned char *)reqbuf->data, reqbuf->pos);
1251         buf_free(reqbuf);
1252
1253         if (result) {
1254                 vpn_progress(vpninfo, PRG_ERR,
1255                              _("Sending proxy request failed: %s\n"),
1256                              strerror(-result));
1257                 return result;
1258         }
1259
1260         if (proxy_gets(vpninfo, ssl_sock, buf, sizeof(buf)) < 0) {
1261                 vpn_progress(vpninfo, PRG_ERR,
1262                              _("Error fetching proxy response\n"));
1263                 return -EIO;
1264         }
1265
1266         if (strncmp(buf, "HTTP/1.", 7) || (buf[7] != '0' && buf[7] != '1') ||
1267             buf[8] != ' ' || !(result = atoi(buf+9))) {
1268                 vpn_progress(vpninfo, PRG_ERR,
1269                              _("Failed to parse proxy response '%s'\n"), buf);
1270                 return -EINVAL;
1271         }
1272
1273         if (result != 200) {
1274                 vpn_progress(vpninfo, PRG_ERR,
1275                              _("Proxy CONNECT request failed: %s\n"), buf);
1276                 return -EIO;
1277         }
1278
1279         while ((buflen = proxy_gets(vpninfo, ssl_sock, buf, sizeof(buf)))) {
1280                 if (buflen < 0) {
1281                         vpn_progress(vpninfo, PRG_ERR,
1282                                      _("Failed to read proxy response\n"));
1283                         return -EIO;
1284                 }
1285                 vpn_progress(vpninfo, PRG_ERR,
1286                              _("Unexpected continuation line after CONNECT response: '%s'\n"),
1287                              buf);
1288         }
1289
1290         return 0;
1291 }
1292
1293 int process_proxy(struct openconnect_info *vpninfo, int ssl_sock)
1294 {
1295         if (!vpninfo->proxy_type || !strcmp(vpninfo->proxy_type, "http"))
1296                 return process_http_proxy(vpninfo, ssl_sock);
1297         
1298         if (!strcmp(vpninfo->proxy_type, "socks") ||
1299             !strcmp(vpninfo->proxy_type, "socks5"))
1300                 return process_socks_proxy(vpninfo, ssl_sock);
1301
1302         vpn_progress(vpninfo, PRG_ERR, _("Unknown proxy type '%s'\n"),
1303                      vpninfo->proxy_type);
1304         return -EIO;
1305 }
1306
1307 int openconnect_set_http_proxy(struct openconnect_info *vpninfo, char *proxy)
1308 {
1309         char *url = proxy;
1310         int ret;
1311
1312         if (!url)
1313                 return -ENOMEM;
1314
1315         free(vpninfo->proxy_type);
1316         vpninfo->proxy_type = NULL;
1317         free(vpninfo->proxy);
1318         vpninfo->proxy = NULL;
1319
1320         ret = internal_parse_url(url, &vpninfo->proxy_type, &vpninfo->proxy,
1321                                  &vpninfo->proxy_port, NULL, 80);
1322         if (ret)
1323                 goto out;
1324
1325         if (vpninfo->proxy_type &&
1326             strcmp(vpninfo->proxy_type, "http") &&
1327             strcmp(vpninfo->proxy_type, "socks") &&
1328             strcmp(vpninfo->proxy_type, "socks5")) {
1329                 vpn_progress(vpninfo, PRG_ERR,
1330                              _("Only http or socks(5) proxies supported\n"));
1331                 free(vpninfo->proxy_type);
1332                 vpninfo->proxy_type = NULL;
1333                 free(vpninfo->proxy);
1334                 vpninfo->proxy = NULL;
1335                 return -EINVAL;
1336         }
1337  out:
1338         free(url);
1339         return ret;
1340 }