Merge branch 'xmlpost-v2' of git://github.com/cernekee/openconnect
[platform/upstream/openconnect.git] / library.c
1 /*
2  * OpenConnect (SSL + DTLS) VPN client
3  *
4  * Copyright © 2008-2012 Intel Corporation.
5  *
6  * Authors: David Woodhouse <dwmw2@infradead.org>
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * version 2.1, as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to:
19  *
20  *   Free Software Foundation, Inc.
21  *   51 Franklin Street, Fifth Floor,
22  *   Boston, MA 02110-1301 USA
23  */
24
25 #include <string.h>
26 #include <errno.h>
27 #include <stdlib.h>
28
29 #ifdef LIBSTOKEN_HDR
30 #include LIBSTOKEN_HDR
31 #endif
32
33 #include <libxml/tree.h>
34
35 #include "openconnect-internal.h"
36
37 struct openconnect_info *openconnect_vpninfo_new (char *useragent,
38                                                   openconnect_validate_peer_cert_vfn validate_peer_cert,
39                                                   openconnect_write_new_config_vfn write_new_config,
40                                                   openconnect_process_auth_form_vfn process_auth_form,
41                                                   openconnect_progress_vfn progress,
42                                                   void *privdata)
43 {
44         struct openconnect_info *vpninfo = calloc (sizeof(*vpninfo), 1);
45
46         vpninfo->ssl_fd = -1;
47         vpninfo->cert_expire_warning = 60 * 86400;
48         vpninfo->useragent = openconnect_create_useragent (useragent);
49         vpninfo->validate_peer_cert = validate_peer_cert;
50         vpninfo->write_new_config = write_new_config;
51         vpninfo->process_auth_form = process_auth_form;
52         vpninfo->progress = progress;
53         vpninfo->cbdata = privdata?:vpninfo;
54         vpninfo->cancel_fd = -1;
55         openconnect_set_reported_os(vpninfo, NULL);
56
57 #ifdef ENABLE_NLS
58         bindtextdomain("openconnect", LOCALEDIR);
59 #endif
60
61         return vpninfo;
62 }
63
64 int openconnect_set_reported_os (struct openconnect_info *vpninfo, const char *os)
65 {
66         if (!os) {
67 #if defined(__APPLE__)
68                 os = "mac";
69 #else
70                 os = sizeof(long) > 4 ? "linux-64" : "linux";
71 #endif
72         }
73
74         /* FIXME: is there a special platname for 64-bit Windows? */
75         if (!strcmp(os, "mac"))
76                 vpninfo->csd_xmltag = "csdMac";
77         else if (!strcmp(os, "linux") || !strcmp(os, "linux-64"))
78                 vpninfo->csd_xmltag = "csdLinux";
79         else if (!strcmp(os, "win"))
80                 vpninfo->csd_xmltag = "csd";
81         else
82                 return -EINVAL;
83
84         vpninfo->platname = os;
85         return 0;
86 }
87
88 static void free_optlist (struct vpn_option *opt)
89 {
90         struct vpn_option *next;
91
92         for (; opt; opt = next) {
93                 next = opt->next;
94                 free(opt->option);
95                 free(opt->value);
96                 free(opt);
97         }
98 }
99
100 void openconnect_vpninfo_free (struct openconnect_info *vpninfo)
101 {
102         openconnect_close_https(vpninfo, 1);
103         free(vpninfo->peer_addr);
104         free_optlist(vpninfo->cookies);
105         free_optlist(vpninfo->cstp_options);
106         free_optlist(vpninfo->dtls_options);
107         free(vpninfo->hostname);
108         free(vpninfo->urlpath);
109         free(vpninfo->redirect_url);
110         free(vpninfo->proxy_type);
111         free(vpninfo->proxy);
112
113         if (vpninfo->csd_scriptname) {
114                 unlink(vpninfo->csd_scriptname);
115                 free(vpninfo->csd_scriptname);
116         }
117         free(vpninfo->csd_token);
118         free(vpninfo->csd_ticket);
119         free(vpninfo->csd_stuburl);
120         free(vpninfo->csd_starturl);
121         free(vpninfo->csd_waiturl);
122         free(vpninfo->csd_preurl);
123         if (vpninfo->opaque_srvdata)
124                 xmlFreeNode(vpninfo->opaque_srvdata);
125
126         /* These are const in openconnect itself, but for consistency of
127            the library API we do take ownership of the strings we're given,
128            and thus we have to free them too. */
129         free((void *)vpninfo->cafile);
130         if (vpninfo->cert != vpninfo->sslkey)
131                 free((void *)vpninfo->sslkey);
132         free((void *)vpninfo->cert);
133         if (vpninfo->peer_cert) {
134 #if defined (OPENCONNECT_OPENSSL)
135                 X509_free(vpninfo->peer_cert);
136 #elif defined (OPENCONNECT_GNUTLS)
137                 gnutls_x509_crt_deinit(vpninfo->peer_cert);
138 #endif
139                 vpninfo->peer_cert = NULL;
140         }
141         free(vpninfo->useragent);
142 #ifdef LIBSTOKEN_HDR
143         if (vpninfo->stoken_pin)
144                 free(vpninfo->stoken_pin);
145         if (vpninfo->stoken_ctx)
146                 stoken_destroy(vpninfo->stoken_ctx);
147 #endif
148         /* No need to free deflate streams; they weren't initialised */
149         free(vpninfo);
150 }
151
152 char *openconnect_get_hostname (struct openconnect_info *vpninfo)
153 {
154         return vpninfo->hostname;
155 }
156
157 void openconnect_set_hostname (struct openconnect_info *vpninfo, char *hostname)
158 {
159         vpninfo->hostname = hostname;
160 }
161
162 char *openconnect_get_urlpath (struct openconnect_info *vpninfo)
163 {
164         return vpninfo->urlpath;
165 }
166
167 void openconnect_set_urlpath (struct openconnect_info *vpninfo, char *urlpath)
168 {
169         vpninfo->urlpath = urlpath;
170 }
171
172 void openconnect_set_xmlsha1 (struct openconnect_info *vpninfo, const char *xmlsha1, int size)
173 {
174         if (size != sizeof (vpninfo->xmlsha1))
175                 return;
176
177         memcpy (&vpninfo->xmlsha1, xmlsha1, size);
178 }
179
180 void openconnect_set_cafile (struct openconnect_info *vpninfo, char *cafile)
181 {
182         vpninfo->cafile = cafile;
183 }
184
185 void openconnect_setup_csd (struct openconnect_info *vpninfo, uid_t uid, int silent, char *wrapper)
186 {
187         vpninfo->uid_csd = uid;
188         vpninfo->uid_csd_given = silent?2:1;
189         vpninfo->csd_wrapper = wrapper;
190 }
191
192 void openconnect_set_client_cert (struct openconnect_info *vpninfo, char *cert, char *sslkey)
193 {
194         vpninfo->cert = cert;
195         if (sslkey)
196                 vpninfo->sslkey = sslkey;
197         else
198                 vpninfo->sslkey = cert;
199 }
200
201 OPENCONNECT_X509 *openconnect_get_peer_cert (struct openconnect_info *vpninfo)
202 {
203         return vpninfo->peer_cert;
204 }
205
206 int openconnect_get_port (struct openconnect_info *vpninfo)
207 {
208         return vpninfo->port;
209 }
210
211 char *openconnect_get_cookie (struct openconnect_info *vpninfo)
212 {
213         return vpninfo->cookie;
214 }
215
216 void openconnect_clear_cookie (struct openconnect_info *vpninfo)
217 {
218         if (vpninfo->cookie)
219                 memset(vpninfo->cookie, 0, strlen(vpninfo->cookie));
220 }
221
222 void openconnect_reset_ssl (struct openconnect_info *vpninfo)
223 {
224         openconnect_close_https(vpninfo, 0);
225         if (vpninfo->peer_addr) {
226                 free(vpninfo->peer_addr);
227                 vpninfo->peer_addr = NULL;
228         }
229 }
230
231 int openconnect_parse_url (struct openconnect_info *vpninfo, char *url)
232 {
233         char *scheme = NULL;
234         int ret;
235
236         if (vpninfo->peer_addr) {
237                 free(vpninfo->peer_addr);
238                 vpninfo->peer_addr = NULL;
239         }
240
241         free(vpninfo->hostname);
242         vpninfo->hostname = NULL;
243         free(vpninfo->urlpath);
244         vpninfo->urlpath = NULL;
245
246         ret = internal_parse_url (url, &scheme, &vpninfo->hostname,
247                                   &vpninfo->port, &vpninfo->urlpath, 443);
248
249         if (ret) {
250                 vpn_progress(vpninfo, PRG_ERR,
251                              _("Failed to parse server URL '%s'\n"),
252                              url);
253                 return ret;
254         }
255         if (scheme && strcmp(scheme, "https")) {
256                 vpn_progress(vpninfo, PRG_ERR,
257                              _("Only https:// permitted for server URL\n"));
258                 ret = -EINVAL;
259         }
260         free(scheme);
261         return ret;
262 }
263
264 void openconnect_set_cert_expiry_warning (struct openconnect_info *vpninfo,
265                                           int seconds)
266 {
267         vpninfo->cert_expire_warning = seconds;
268 }
269
270 void openconnect_set_cancel_fd (struct openconnect_info *vpninfo, int fd)
271 {
272         vpninfo->cancel_fd = fd;
273 }
274
275 const char *openconnect_get_version (void)
276 {
277         return openconnect_version_str;
278 }
279
280 int openconnect_has_pkcs11_support(void)
281 {
282 #if defined (OPENCONNECT_GNUTLS) && defined (HAVE_P11KIT)
283         return 1;
284 #else
285         return 0;
286 #endif
287 }
288
289 #if defined (OPENCONNECT_OPENSSL) && defined (HAVE_ENGINE)
290 #include <openssl/engine.h>
291 #endif
292 int openconnect_has_tss_blob_support(void)
293 {
294 #if defined (OPENCONNECT_OPENSSL) && defined (HAVE_ENGINE)
295         ENGINE *e;
296
297         ENGINE_load_builtin_engines();
298
299         e = ENGINE_by_id("tpm");
300         if (e) {
301                 ENGINE_free(e);
302                 return 1;
303         }
304 #elif defined (OPENCONNECT_GNUTLS) && defined (HAVE_TROUSERS)
305         return 1;
306 #endif
307         return 0;
308 }
309
310 int openconnect_has_stoken_support(void)
311 {
312 #ifdef LIBSTOKEN_HDR
313         return 1;
314 #else
315         return 0;
316 #endif
317 }
318
319 /*
320  * Enable software token generation if use_stoken == 1.
321  *
322  * If token_str is not NULL, try to parse the string.  Otherwise, try to read
323  * the token data from ~/.stokenrc
324  *
325  * Return value:
326  *  = -EOPNOTSUPP, if libstoken is not available
327  *  = -EINVAL, if the token string is invalid (token_str was provided)
328  *  = -ENOENT, if ~/.stokenrc is missing (token_str was NULL)
329  *  = -EIO, for other libstoken failures
330  *  = 0, on success
331  */
332 int openconnect_set_stoken_mode (struct openconnect_info *vpninfo,
333                                  int use_stoken, const char *token_str)
334 {
335 #ifdef LIBSTOKEN_HDR
336         int ret;
337
338         vpninfo->use_stoken = 0;
339         if (!use_stoken)
340                 return 0;
341
342         if (!vpninfo->stoken_ctx) {
343                 vpninfo->stoken_ctx = stoken_new();
344                 if (!vpninfo->stoken_ctx)
345                         return -EIO;
346         }
347
348         ret = token_str ?
349               stoken_import_string(vpninfo->stoken_ctx, token_str) :
350               stoken_import_rcfile(vpninfo->stoken_ctx, NULL);
351         if (ret)
352                 return ret;
353
354         vpninfo->use_stoken = 1;
355         return 0;
356 #else
357         return -EOPNOTSUPP;
358 #endif
359 }