98d6dae925b95e2de7b6a0759c48cade7d78f5d1
[platform/upstream/openconnect.git] / main.c
1 /*
2  * OpenConnect (SSL + DTLS) VPN client
3  *
4  * Copyright © 2008-2010 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 <stdio.h>
27 #include <syslog.h>
28 #include <stdarg.h>
29 #include <stdlib.h>
30 #include <signal.h>
31 #include <string.h>
32 #include <errno.h>
33 #include <fcntl.h>
34 #include <unistd.h>
35 #include <pwd.h>
36 #include <sys/syslog.h>
37 #include <sys/utsname.h>
38 #include <sys/types.h>
39 #include <openssl/ui.h>
40 #ifdef OPENCONNECT_LIBPROXY
41 #include LIBPROXY_HDR
42 #endif
43
44 #define _GNU_SOURCE
45 #include <getopt.h>
46
47 #include "openconnect-internal.h"
48
49 static int write_new_config(struct openconnect_info *vpninfo, char *buf, int buflen);
50 static void write_progress(struct openconnect_info *info, int level, const char *fmt, ...);
51 static void syslog_progress(struct openconnect_info *info, int level, const char *fmt, ...);
52 static int validate_peer_cert(struct openconnect_info *info, X509 *peer_cert, const char *reason);
53
54 int verbose = PRG_INFO;
55 int background;
56 int do_passphrase_from_fsid;
57 int nocertcheck;
58
59 enum {
60         OPT_AUTHGROUP = 0x100,
61         OPT_CAFILE,
62         OPT_COOKIEONLY,
63         OPT_COOKIE_ON_STDIN,
64         OPT_CSD_USER,
65         OPT_CSD_WRAPPER,
66         OPT_DISABLE_IPV6,
67         OPT_DTLS_CIPHERS,
68         OPT_FORCE_DPD,
69         OPT_KEY_PASSWORD_FROM_FSID,
70         OPT_LIBPROXY,
71         OPT_NO_CERT_CHECK,
72         OPT_NO_DTLS,
73         OPT_NO_HTTP_KEEPALIVE,
74         OPT_NO_PASSWD,
75         OPT_NO_PROXY,
76         OPT_PASSWORD_ON_STDIN,
77         OPT_PRINTCOOKIE,
78         OPT_RECONNECT_TIMEOUT,
79         OPT_SERVERCERT,
80         OPT_USERAGENT,
81 };
82
83 static struct option long_options[] = {
84         {"background", 0, 0, 'b'},
85         {"certificate", 1, 0, 'c'},
86         {"sslkey", 1, 0, 'k'},
87         {"cookie", 1, 0, 'C'},
88         {"deflate", 0, 0, 'd'},
89         {"no-deflate", 0, 0, 'D'},
90         {"usergroup", 1, 0, 'g'},
91         {"help", 0, 0, 'h'},
92         {"interface", 1, 0, 'i'},
93         {"mtu", 1, 0, 'm'},
94         {"setuid", 1, 0, 'U'},
95         {"script", 1, 0, 's'},
96         {"script-tun", 0, 0, 'S'},
97         {"syslog", 0, 0, 'l'},
98         {"key-type", 1, 0, 'K'},
99         {"key-password", 1, 0, 'p'},
100         {"proxy", 1, 0, 'P'},
101         {"user", 1, 0, 'u'},
102         {"verbose", 0, 0, 'v'},
103         {"version", 0, 0, 'V'},
104         {"cafile", 1, 0, OPT_CAFILE},
105         {"no-dtls", 0, 0, OPT_NO_DTLS},
106         {"cookieonly", 0, 0, OPT_COOKIEONLY},
107         {"printcookie", 0, 0, OPT_PRINTCOOKIE},
108         {"quiet", 0, 0, 'q'},
109         {"queue-len", 1, 0, 'Q'},
110         {"xmlconfig", 1, 0, 'x'},
111         {"cookie-on-stdin", 0, 0, OPT_COOKIE_ON_STDIN},
112         {"passwd-on-stdin", 0, 0, OPT_PASSWORD_ON_STDIN},
113         {"no-passwd", 0, 0, OPT_NO_PASSWD},
114         {"reconnect-timeout", 1, 0, OPT_RECONNECT_TIMEOUT},
115         {"dtls-ciphers", 1, 0, OPT_DTLS_CIPHERS},
116         {"authgroup", 1, 0, OPT_AUTHGROUP},
117         {"servercert", 1, 0, OPT_SERVERCERT},
118         {"key-password-from-fsid", 0, 0, OPT_KEY_PASSWORD_FROM_FSID},
119         {"useragent", 1, 0, OPT_USERAGENT},
120         {"csd-user", 1, 0, OPT_CSD_USER},
121         {"csd-wrapper", 1, 0, OPT_CSD_WRAPPER},
122         {"disable-ipv6", 0, 0, OPT_DISABLE_IPV6},
123         {"no-proxy", 0, 0, OPT_NO_PROXY},
124         {"libproxy", 0, 0, OPT_LIBPROXY},
125         {"no-http-keepalive", 0, 0, OPT_NO_HTTP_KEEPALIVE},
126         {"no-cert-check", 0, 0, OPT_NO_CERT_CHECK},
127         {"force-dpd", 1, 0, OPT_FORCE_DPD},
128         {NULL, 0, 0, 0},
129 };
130
131 void usage(void)
132 {
133         printf("Usage:  openconnect [options] <server>\n");
134         printf("Open client for Cisco AnyConnect VPN, version %s\n\n", openconnect_version);
135         printf("  -b, --background                Continue in background after startup\n");
136         printf("  -c, --certificate=CERT          Use SSL client certificate CERT\n");
137         printf("  -k, --sslkey=KEY                Use SSL private key file KEY\n");
138         printf("  -K, --key-type=TYPE             Private key type (PKCS#12 / TPM / PEM)\n");
139         printf("  -C, --cookie=COOKIE             Use WebVPN cookie COOKIE\n");
140         printf("      --cookie-on-stdin           Read cookie from standard input\n");
141         printf("  -d, --deflate                   Enable compression (default)\n");
142         printf("  -D, --no-deflate                Disable compression\n");
143         printf("      --force-dpd=INTERVAL        Set minimum Dead Peer Detection interval\n");
144         printf("  -g, --usergroup=GROUP           Set login usergroup\n");
145         printf("  -h, --help                      Display help text\n");
146         printf("  -i, --interface=IFNAME          Use IFNAME for tunnel interface\n");
147         printf("  -l, --syslog                    Use syslog for progress messages\n");
148         printf("  -U, --setuid=USER               Drop privileges after connecting\n");
149         printf("      --csd-user=USER             Drop privileges during CSD execution\n");
150         printf("      --csd-wrapper=SCRIPT        Run SCRIPT instead of CSD binary\n");
151         printf("  -m, --mtu=MTU                   Request MTU from server\n");
152         printf("  -p, --key-password=PASS         Set key passphrase or TPM SRK PIN\n");
153         printf("      --key-password-from-fsid    Key passphrase is fsid of file system\n");
154         printf("  -P, --proxy=URL                 Set proxy server\n");
155         printf("      --no-proxy                  Disable proxy\n");
156         printf("      --libproxy                  Use libproxy to automatically configure proxy\n");
157 #ifndef OPENCONNECT_LIBPROXY
158         printf("                                  (NOTE: libproxy disabled in this build)\n");
159 #endif
160         printf("  -q, --quiet                     Less output\n");
161         printf("  -Q, --queue-len=LEN             Set packet queue limit to LEN pkts\n");
162         printf("  -s, --script=SCRIPT             Use vpnc-compatible config script\n");
163         printf("  -S, --script-tun                Pass traffic to 'script' program, not tun\n");
164         printf("  -u, --user=NAME                 Set login username\n");
165         printf("  -V, --version                   Report version number\n");
166         printf("  -v, --verbose                   More output\n");
167         printf("  -x, --xmlconfig=CONFIG          XML config file\n");
168         printf("      --authgroup=GROUP           Choose authentication login selection\n");
169         printf("      --cookieonly                Fetch webvpn cookie only; don't connect\n");
170         printf("      --printcookie               Print webvpn cookie before connecting\n");
171         printf("      --cafile=FILE               Cert file for server verification\n");
172         printf("      --disable-ipv6              Do not ask for IPv6 connectivity\n");
173         printf("      --dtls-ciphers=LIST         OpenSSL ciphers to support for DTLS\n");
174         printf("      --no-dtls                   Disable DTLS\n");
175         printf("      --no-http-keepalive         Disable HTTP connection re-use\n");
176         printf("      --no-passwd                 Disable password/SecurID authentication\n");
177         printf("      --no-cert-check             Do not require server SSL cert to be valid\n");
178         printf("      --passwd-on-stdin           Read password from standard input\n");
179         printf("      --reconnect-timeout         Connection retry timeout in seconds\n");
180         printf("      --servercert=FINGERPRINT    Server's certificate SHA1 fingerprint\n");
181         printf("      --useragent=STRING          HTTP header User-Agent: field\n");
182         exit(1);
183 }
184
185 static void read_stdin(char **string)
186 {
187         char *c = malloc(100);
188         if (!c) {
189                 fprintf(stderr, "Allocation failure for string from stdin\n");
190                 exit(1);
191         }
192         if (!fgets(c, 100, stdin)) {
193                 perror("fgets (stdin)");
194                 exit(1);
195         }
196
197         *string = c;
198
199         c = strchr(*string, '\n');
200         if (c)
201                 *c = 0;
202 }
203 static void handle_sigusr(int sig)
204 {
205         if (sig == SIGUSR1)
206                 verbose = PRG_TRACE;
207         else if (sig == SIGUSR2)
208                 verbose = PRG_INFO;
209 }
210
211 int main(int argc, char **argv)
212 {
213         struct openconnect_info *vpninfo;
214         struct utsname utsbuf;
215         struct sigaction sa;
216         int cookieonly = 0;
217         int use_syslog = 0;
218         char *proxy = getenv("https_proxy");
219         int autoproxy = 0;
220         uid_t uid = getuid();
221         int opt;
222
223         openconnect_init_openssl();
224
225         vpninfo = malloc(sizeof(*vpninfo));
226         if (!vpninfo) {
227                 fprintf(stderr, "Failed to allocate vpninfo structure\n");
228                 exit(1);
229         }
230         memset(vpninfo, 0, sizeof(*vpninfo));
231
232         /* Set up some defaults */
233         vpninfo->tun_fd = vpninfo->ssl_fd = vpninfo->dtls_fd = vpninfo->new_dtls_fd = -1;
234         vpninfo->useragent = openconnect_create_useragent("Open AnyConnect VPN Agent");
235         vpninfo->mtu = 1406;
236         vpninfo->deflate = 1;
237         vpninfo->dtls_attempt_period = 60;
238         vpninfo->max_qlen = 10;
239         vpninfo->reconnect_interval = RECONNECT_INTERVAL_MIN;
240         vpninfo->reconnect_timeout = 300;
241         vpninfo->uid_csd = 0;
242         vpninfo->uid_csd_given = 0;
243         vpninfo->validate_peer_cert = validate_peer_cert;
244
245         if (!uname(&utsbuf))
246                 vpninfo->localname = utsbuf.nodename;
247         else
248                 vpninfo->localname = "localhost";
249
250         while ((opt = getopt_long(argc, argv, "bC:c:Ddg:hi:k:K:lpP:Q:qSs:U:u:Vvx:",
251                                   long_options, NULL))) {
252                 if (opt < 0)
253                         break;
254
255                 switch (opt) {
256                 case OPT_CAFILE:
257                         vpninfo->cafile = optarg;
258                         break;
259                 case OPT_SERVERCERT:
260                         vpninfo->servercert = optarg;
261                         break;
262                 case OPT_NO_DTLS:
263                         vpninfo->dtls_attempt_period = 0;
264                         break;
265                 case OPT_COOKIEONLY:
266                         cookieonly = 1;
267                         break;
268                 case OPT_PRINTCOOKIE:
269                         cookieonly = 2;
270                         break;
271                 case OPT_COOKIE_ON_STDIN:
272                         read_stdin(&vpninfo->cookie);
273                         /* If the cookie is empty, ignore it */
274                         if (! *vpninfo->cookie) {
275                                 vpninfo->cookie = NULL;
276                         }
277                         break;
278                 case OPT_PASSWORD_ON_STDIN:
279                         read_stdin(&vpninfo->password);
280                         break;
281                 case OPT_NO_PASSWD:
282                         vpninfo->nopasswd = 1;
283                         break;
284                 case OPT_RECONNECT_TIMEOUT:
285                         vpninfo->reconnect_timeout = atoi(optarg);
286                         break;
287                 case OPT_DTLS_CIPHERS:
288                         vpninfo->dtls_ciphers = optarg;
289                         break;
290                 case OPT_AUTHGROUP:
291                         vpninfo->authgroup = optarg;
292                         break;
293                 case 'b':
294                         background = 1;
295                         break;
296                 case 'C':
297                         vpninfo->cookie = optarg;
298                         break;
299                 case 'c':
300                         vpninfo->cert = optarg;
301                         break;
302                 case 'k':
303                         vpninfo->sslkey = optarg;
304                         break;
305                 case 'K':
306                         if (!strcasecmp(optarg, "PKCS#12") ||
307                             !strcasecmp(optarg, "PKCS12")) {
308                                 vpninfo->cert_type = CERT_TYPE_PKCS12;
309                         } else if (!strcasecmp(optarg, "TPM")) {
310                                 vpninfo->cert_type = CERT_TYPE_TPM;
311                         } else if (!strcasecmp(optarg, "PEM")) {
312                                 vpninfo->cert_type = CERT_TYPE_PEM;
313                         } else {
314                                 fprintf(stderr, "Unknown certificate type '%s'\n",
315                                         optarg);
316                                 usage();
317                         }
318                 case 'd':
319                         vpninfo->deflate = 1;
320                         break;
321                 case 'D':
322                         vpninfo->deflate = 0;
323                         break;
324                 case 'g':
325                         free(vpninfo->urlpath);
326                         vpninfo->urlpath = strdup(optarg);
327                         break;
328                 case 'h':
329                         usage();
330                 case 'i':
331                         vpninfo->ifname = optarg;
332                         break;
333                 case 'l':
334                         use_syslog = 1;
335                         break;
336                 case 'm':
337                         vpninfo->mtu = atol(optarg);
338                         if (vpninfo->mtu < 576) {
339                                 fprintf(stderr, "MTU %d too small\n", vpninfo->mtu);
340                                 vpninfo->mtu = 576;
341                         }
342                         break;
343                 case 'p':
344                         vpninfo->cert_password = optarg;
345                         break;
346                 case 'P': 
347                         proxy = optarg;
348                         autoproxy = 0;
349                         break;
350                 case OPT_NO_PROXY:
351                         autoproxy = 0;
352                         proxy = NULL;
353                 case OPT_LIBPROXY:
354 #ifndef OPENCONNECT_LIBPROXY
355                         fprintf(stderr, "This version of openconnect was built without libproxy support\n");
356                         exit(1);
357 #endif
358                         autoproxy = 1;
359                         proxy = NULL;
360                         break;
361                 case OPT_NO_HTTP_KEEPALIVE:
362                         fprintf(stderr, "Disabling all HTTP connection re-use due to --no-http-keepalive option.\n"
363                                 "If this helps, please report to <openconnect-devel@lists.infradead.org>.\n");
364                         vpninfo->no_http_keepalive = 1;
365                         break;
366                 case OPT_NO_CERT_CHECK:
367                         nocertcheck = 1;
368                         break;
369                 case 's':
370                         vpninfo->vpnc_script = optarg;
371                         break;
372                 case 'S':
373                         vpninfo->script_tun = 1;
374                         break;
375                 case 'u':
376                         vpninfo->username = optarg;
377                         break;
378                 case 'U': {
379                         char *strend;
380                         uid = strtol(optarg, &strend, 0);
381                         if (strend[0]) {
382                                 struct passwd *pw = getpwnam(optarg);
383                                 if (!pw) {
384                                         fprintf(stderr, "Invalid user \"%s\"\n",
385                                                 optarg);
386                                         exit(1);
387                                 }
388                                 uid = pw->pw_uid;
389                         }
390                         break;
391                 }
392                 case OPT_CSD_USER: {
393                         char *strend;
394                         vpninfo->uid_csd = strtol(optarg, &strend, 0);
395                         if (strend[0]) {
396                                 struct passwd *pw = getpwnam(optarg);
397                                 if (!pw) {
398                                         fprintf(stderr, "Invalid user \"%s\"\n",
399                                                 optarg);
400                                         exit(1);
401                                 }
402                                 vpninfo->uid_csd = pw->pw_uid;
403                         }
404                         vpninfo->uid_csd_given = 1;
405                         break;
406                 }
407                 case OPT_CSD_WRAPPER:
408                         vpninfo->csd_wrapper = optarg;
409                         break;
410                 case OPT_DISABLE_IPV6:
411                         vpninfo->disable_ipv6 = 1;
412                         break;
413                 case 'Q':
414                         vpninfo->max_qlen = atol(optarg);
415                         if (!vpninfo->max_qlen) {
416                                 fprintf(stderr, "Queue length zero not permitted; using 1\n");
417                                 vpninfo->max_qlen = 1;
418                         }
419                         break;
420                 case 'q':
421                         verbose = PRG_ERR;
422                         break;
423                 case 'v':
424                         verbose = PRG_TRACE;
425                         break;
426                 case 'V':
427                         printf("OpenConnect version %s\n", openconnect_version);
428                         exit(0);
429                 case 'x':
430                         vpninfo->xmlconfig = optarg;
431                         vpninfo->write_new_config = write_new_config;
432                         break;
433                 case OPT_KEY_PASSWORD_FROM_FSID:
434                         do_passphrase_from_fsid = 1;
435                         break;
436                 case OPT_USERAGENT:
437                         free(vpninfo->useragent);
438                         vpninfo->useragent = optarg;
439                         break;
440                 case OPT_FORCE_DPD:
441                         vpninfo->dtls_times.dpd = vpninfo->ssl_times.dpd = atoi(optarg);
442                         break;
443                 default:
444                         usage();
445                 }
446         }
447
448         if (optind != argc - 1) {
449                 fprintf(stderr, "No server specified\n");
450                 usage();
451         }
452
453         if (!vpninfo->sslkey)
454                 vpninfo->sslkey = vpninfo->cert;
455
456         vpninfo->progress = write_progress;
457
458 #ifdef OPENCONNECT_LIBPROXY
459         if (autoproxy)
460                 vpninfo->proxy_factory = px_proxy_factory_new();
461 #endif
462         if (proxy && openconnect_set_http_proxy(vpninfo, proxy))
463                 exit(1);
464
465         if (use_syslog) {
466                 openlog("openconnect", LOG_PID, LOG_DAEMON);
467                 vpninfo->progress = syslog_progress;
468         }
469
470         memset(&sa, 0, sizeof(sa));
471         sa.sa_handler = handle_sigusr;
472
473         sigaction(SIGUSR1, &sa, NULL);
474         sigaction(SIGUSR2, &sa, NULL);
475
476         if (vpninfo->sslkey && do_passphrase_from_fsid)
477                 openconnect_passphrase_from_fsid(vpninfo);
478
479         if (config_lookup_host(vpninfo, argv[optind]))
480                 exit(1);
481
482         if (!vpninfo->hostname) {
483                 char *url = strdup(argv[optind]);
484                 char *scheme;
485                 char *group;
486
487                 if (openconnect_parse_url(url, &scheme, &vpninfo->hostname, &vpninfo->port,
488                               &group, 443)) {
489                         fprintf(stderr, "Failed to parse server URL '%s'\n",
490                                 url);
491                         exit(1);
492                 }
493                 if (scheme && strcmp(scheme, "https")) {
494                         fprintf(stderr, "Only https:// permitted for server URL\n");
495                         exit(1);
496                 }
497                 if (group) {
498                         free(vpninfo->urlpath);
499                         vpninfo->urlpath = group;
500                 }
501                 free(scheme);
502                 free(url);
503         }
504
505 #ifdef SSL_UI
506         set_openssl_ui();
507 #endif
508
509         if (!vpninfo->cookie && openconnect_obtain_cookie(vpninfo)) {
510                 fprintf(stderr, "Failed to obtain WebVPN cookie\n");
511                 exit(1);
512         }
513
514         if (cookieonly) {
515                 printf("%s\n", vpninfo->cookie);
516                 if (cookieonly == 1)
517                         /* We use cookieonly=2 for 'print it and continue' */
518                         exit(0);
519         }
520         if (make_cstp_connection(vpninfo)) {
521                 fprintf(stderr, "Creating SSL connection failed\n");
522                 exit(1);
523         }
524
525         if (setup_tun(vpninfo)) {
526                 fprintf(stderr, "Set up tun device failed\n");
527                 exit(1);
528         }
529
530         if (uid != getuid()) {
531                 if (setuid(uid)) {
532                         fprintf(stderr, "Failed to set uid %d\n", uid);
533                         exit(1);
534                 }
535         }
536
537         if (vpninfo->dtls_attempt_period && setup_dtls(vpninfo))
538                 fprintf(stderr, "Set up DTLS failed; using SSL instead\n");
539
540         vpninfo->progress(vpninfo, PRG_INFO,
541                           "Connected %s as %s%s%s, using %s\n", vpninfo->ifname,
542                           vpninfo->vpn_addr?:"",
543                           (vpninfo->vpn_addr6 && vpninfo->vpn_addr)?" + ":"",
544                           vpninfo->vpn_addr6?:"",
545                           (vpninfo->dtls_fd == -1) ?
546                               (vpninfo->deflate ? "SSL + deflate" : "SSL")
547                               : "DTLS");
548
549         if (!vpninfo->vpnc_script)
550                 vpninfo->progress(vpninfo, PRG_INFO,
551                                   "No --script argument provided; DNS and routing are not configured\n");
552
553         if (background) {
554                 int pid;
555                 if ((pid = fork())) {
556                         vpninfo->progress(vpninfo, PRG_INFO,
557                                           "Continuing in background; pid %d\n",
558                                           pid);
559                         exit(0);
560                 }
561         }
562         vpn_mainloop(vpninfo);
563         exit(1);
564 }
565
566 static int write_new_config(struct openconnect_info *vpninfo, char *buf, int buflen)
567 {
568         int config_fd;
569         int err;
570
571         config_fd = open(vpninfo->xmlconfig, O_WRONLY|O_TRUNC|O_CREAT, 0644);
572         if (config_fd < 0) {
573                 err = errno;
574                 fprintf(stderr, "Failed to open %s for write: %s\n",
575                         vpninfo->xmlconfig, strerror(err));
576                 return -err;
577         }
578
579         /* FIXME: We should actually write to a new tempfile, then rename */
580         if(write(config_fd, buf, buflen) != buflen) {
581                 err = errno;
582                 fprintf(stderr, "Failed to write config to %s: %s\n",
583                         vpninfo->xmlconfig, strerror(err));
584
585                 return -err;
586         }
587           
588         return 0;
589 }
590
591 void write_progress(struct openconnect_info *info, int level, const char *fmt, ...)
592 {
593         FILE *outf = level ? stdout : stderr;
594         va_list args;
595
596         if (verbose >= level) {
597                 va_start(args, fmt);
598                 vfprintf(outf, fmt, args);
599                 va_end(args);
600         }
601 }
602
603 void syslog_progress(struct openconnect_info *info, int level,
604                      const char *fmt, ...)
605 {
606         int priority = level ? LOG_INFO : LOG_NOTICE;
607         va_list args;
608
609         if (verbose >= level) {
610                 va_start(args, fmt);
611                 vsyslog(priority, fmt, args);
612                 va_end(args);
613         }
614 }
615
616
617 struct accepted_cert {
618         struct accepted_cert *next;
619         char fingerprint[EVP_MAX_MD_SIZE * 2 + 1];
620         char host[0];
621 } *accepted_certs;
622
623 static int validate_peer_cert(struct openconnect_info *vpninfo, X509 *peer_cert,
624                               const char *reason)
625 {
626         char fingerprint[EVP_MAX_MD_SIZE * 2 + 1];
627         struct accepted_cert *this;
628         int ret;
629
630         if (nocertcheck)
631                 return 0;
632
633         ret = get_cert_sha1_fingerprint(vpninfo, peer_cert, fingerprint);
634         if (ret)
635                 return ret;
636
637         for (this = accepted_certs; this; this = this->next) {
638                 if (!strcasecmp(this->host, vpninfo->hostname) &&
639                     !strcasecmp(this->fingerprint, fingerprint))
640                         return 0;
641         }
642         
643         while (1) {
644                 UI *ui;
645                 char buf[6];
646                 int ret;
647
648                 fprintf(stderr, "\nCertificate from VPN server \"%s\" failed verification.\n"
649                         "Reason: %s\n", vpninfo->hostname, reason);
650                 fflush(stderr);
651
652                 ui = UI_new();
653                 UI_add_input_string(ui, "Enter 'yes' to accept, 'no' to abort; anything else to view: ",
654                                     UI_INPUT_FLAG_ECHO, buf, 2, 5);
655                 ret = UI_process(ui);
656                 UI_free(ui);
657                 if (ret == -2)
658                         return -EINVAL;
659                 if (ret == -1)
660                         buf[0] = 0;
661
662                 if (!strcasecmp(buf, "yes")) {
663                         struct accepted_cert *newcert = malloc(sizeof(*newcert) +
664                                                                strlen(vpninfo->hostname) + 1);
665                         if (newcert) {
666                                 newcert->next = accepted_certs;
667                                 accepted_certs = newcert;
668                                 strcpy(newcert->fingerprint, fingerprint);
669                                 strcpy(newcert->host, vpninfo->hostname);
670                         }
671                         return 0;
672                 }
673                 if (!strcasecmp(buf, "no"))
674                         return -EINVAL;
675
676                 X509_print_fp(stderr, peer_cert);
677         }
678                                 
679 }