1 /*
2  * OpenConnect (SSL + DTLS) VPN client
3  *
4  * Copyright © 2008-2015 Intel Corporation.
5  * Copyright © 2012-2014 Kevin Cernekee <cernekee@gmail.com>
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 
19 #include <config.h>
20 
21 #include <ctype.h>
22 #include <errno.h>
23 #include <stdlib.h>
24 #include <string.h>
25 
26 #include <stoken.h>
27 
28 #include "openconnect-internal.h"
29 
30 #ifndef STOKEN_CHECK_VER
31 #define STOKEN_CHECK_VER(x,y) 0
32 #endif
33 
set_libstoken_mode(struct openconnect_info * vpninfo,const char * token_str)34 int set_libstoken_mode(struct openconnect_info *vpninfo, const char *token_str)
35 {
36 	int ret;
37 	char *file_token = NULL;
38 
39 	if (!vpninfo->stoken_ctx) {
40 		vpninfo->stoken_ctx = stoken_new();
41 		if (!vpninfo->stoken_ctx)
42 			return -EIO;
43 	}
44 
45 	if (token_str) {
46 		switch(token_str[0]) {
47 		case '@':
48 			token_str++;
49 			/* fall through */
50 		case '/':
51 			ret = openconnect_read_file(vpninfo, token_str, &file_token);
52 			if (ret < 0)
53 				return ret;
54 		}
55 	}
56 
57 	/* Ug. If it's an XML STDID file or a raw token string, we need to
58 	 * pass its contents to stoken_import_string(). If it's an rcfile,
59 	 * we need to pass the *filename* to stoken_import_rcfile(). So
60 	 * let's just depend on stoken_import_string() failing gracefully. */
61 	if (file_token) {
62 		ret = stoken_import_string(vpninfo->stoken_ctx, file_token);
63 		free(file_token);
64 		if (ret == -EINVAL)
65 			ret = stoken_import_rcfile(vpninfo->stoken_ctx, token_str);
66 	} else if (token_str) {
67 		ret = stoken_import_string(vpninfo->stoken_ctx, token_str);
68 	} else {
69 		ret = stoken_import_rcfile(vpninfo->stoken_ctx, NULL);
70 	}
71 
72 	if (ret)
73 		return ret;
74 
75 	vpninfo->token_mode = OC_TOKEN_MODE_STOKEN;
76 	return 0;
77 }
78 
79 
80 /*
81  * A SecurID token can be encrypted with a device ID, a password, both,
82  * or neither.  Gather the required information, decrypt the token, and
83  * check the hash to make sure it is sane.
84  *
85  * Return value:
86  *  < 0, on error
87  *  = 0, on success
88  *  = 1, if the user cancelled the form submission
89  *  = 2, if the user left the entire form blank and clicked OK
90  */
decrypt_stoken(struct openconnect_info * vpninfo)91 static int decrypt_stoken(struct openconnect_info *vpninfo)
92 {
93 	struct oc_auth_form form;
94 	struct oc_form_opt opts[2], *opt = opts;
95 	char **devid = NULL, **pass = NULL;
96 	int ret = 0;
97 
98 	memset(&form, 0, sizeof(form));
99 	memset(&opts, 0, sizeof(opts));
100 
101 	form.opts = opts;
102 	form.message = _("Enter credentials to unlock software token.");
103 
104 	if (stoken_devid_required(vpninfo->stoken_ctx)) {
105 		opt->type = OC_FORM_OPT_TEXT;
106 		opt->name = (char *)"devid";
107 		opt->label = _("Device ID:");
108 		devid = &opt->_value;
109 		opt++;
110 	}
111 	if (stoken_pass_required(vpninfo->stoken_ctx)) {
112 		opt->type = OC_FORM_OPT_PASSWORD;
113 		opt->name = (char *)"password";
114 		opt->label = _("Password:");
115 		pass = &opt->_value;
116 		opt++;
117 	}
118 
119 	opts[0].next = opts[1].type ? &opts[1] : NULL;
120 
121 	while (1) {
122 		nuke_opt_values(opts);
123 
124 		if (!opts[0].type) {
125 			/* don't bug the user if there's nothing to enter */
126 			ret = 0;
127 		} else {
128 			int some_empty = 0, all_empty = 1;
129 
130 			/* < 0 for error; 1 if cancelled */
131 			ret = process_auth_form(vpninfo, &form);
132 			if (ret)
133 				break;
134 
135 			for (opt = opts; opt; opt = opt->next) {
136 				if (!opt->_value || !strlen(opt->_value))
137 					some_empty = 1;
138 				else
139 					all_empty = 0;
140 			}
141 			if (all_empty) {
142 				vpn_progress(vpninfo, PRG_INFO,
143 					     _("User bypassed soft token.\n"));
144 				ret = 2;
145 				break;
146 			}
147 			if (some_empty) {
148 				vpn_progress(vpninfo, PRG_INFO,
149 					     _("All fields are required; try again.\n"));
150 				continue;
151 			}
152 		}
153 
154 		ret = stoken_decrypt_seed(vpninfo->stoken_ctx,
155 					  pass ? *pass : NULL,
156 					  devid ? *devid : NULL);
157 		if (ret == -EIO || (ret && !devid && !pass)) {
158 			vpn_progress(vpninfo, PRG_ERR,
159 				     _("General failure in libstoken.\n"));
160 			break;
161 		} else if (ret != 0) {
162 			vpn_progress(vpninfo, PRG_INFO,
163 				     _("Incorrect device ID or password; try again.\n"));
164 			continue;
165 		}
166 
167 		vpn_progress(vpninfo, PRG_DEBUG, _("Soft token init was successful.\n"));
168 		ret = 0;
169 		break;
170 	}
171 
172 	nuke_opt_values(opts);
173 	return ret;
174 }
175 
get_stoken_details(struct openconnect_info * vpninfo)176 static void get_stoken_details(struct openconnect_info *vpninfo)
177 {
178 #if STOKEN_CHECK_VER(1,3)
179 	struct stoken_info *info = stoken_get_info(vpninfo->stoken_ctx);
180 
181 	if (info) {
182 		vpninfo->stoken_concat_pin = !info->uses_pin;
183 		vpninfo->stoken_interval = info->interval;
184 		return;
185 	}
186 #endif
187 	vpninfo->stoken_concat_pin = 0;
188 	vpninfo->stoken_interval = 60;
189 }
190 
191 /*
192  * Return value:
193  *  < 0, on error
194  *  = 0, on success
195  *  = 1, if the user cancelled the form submission
196  */
request_stoken_pin(struct openconnect_info * vpninfo)197 static int request_stoken_pin(struct openconnect_info *vpninfo)
198 {
199 	struct oc_auth_form form;
200 	struct oc_form_opt opts[1], *opt = opts;
201 	int ret = 0;
202 
203 	if (!vpninfo->stoken_concat_pin && !stoken_pin_required(vpninfo->stoken_ctx))
204 		return 0;
205 
206 	memset(&form, 0, sizeof(form));
207 	memset(&opts, 0, sizeof(opts));
208 
209 	form.opts = opts;
210 	form.message = _("Enter software token PIN.");
211 
212 	opt->type = OC_FORM_OPT_PASSWORD;
213 	opt->name = (char *)"password";
214 	opt->label = _("PIN:");
215 	opt->flags = OC_FORM_OPT_NUMERIC;
216 
217 	while (1) {
218 		char *pin;
219 
220 		nuke_opt_values(opts);
221 
222 		/* < 0 for error; 1 if cancelled */
223 		ret = process_auth_form(vpninfo, &form);
224 		if (ret)
225 			break;
226 
227 		pin = opt->_value;
228 		if (!pin || !strlen(pin)) {
229 			/* in some cases there really is no PIN */
230 			if (vpninfo->stoken_concat_pin)
231 				return 0;
232 
233 			vpn_progress(vpninfo, PRG_INFO,
234 				     _("All fields are required; try again.\n"));
235 			continue;
236 		}
237 
238 		if (!vpninfo->stoken_concat_pin &&
239 		    stoken_check_pin(vpninfo->stoken_ctx, pin) != 0) {
240 			vpn_progress(vpninfo, PRG_INFO,
241 				     _("Invalid PIN format; try again.\n"));
242 			continue;
243 		}
244 
245 		free(vpninfo->stoken_pin);
246 		vpninfo->stoken_pin = strdup(pin);
247 		if (!vpninfo->stoken_pin)
248 			ret = -ENOMEM;
249 		break;
250 	}
251 
252 	nuke_opt_values(opts);
253 	return ret;
254 }
255 
256 /*
257  * If the user clicks OK on the devid/password prompt without entering
258  * any data, we will continue connecting but bypass soft token generation
259  * for the duration of this "obtain_cookie" session.  (They might not even
260  * have the credentials that we're prompting for.)
261  *
262  * If the user clicks Cancel, we will abort the connection.
263  *
264  * Return value:
265  *  < 0, on error
266  *  = 0, on success (or if the user bypassed soft token init)
267  *  = 1, if the user cancelled the form submission
268  */
prepare_stoken(struct openconnect_info * vpninfo)269 int prepare_stoken(struct openconnect_info *vpninfo)
270 {
271 	int ret;
272 
273 	vpninfo->token_tries = 0;
274 	vpninfo->token_bypassed = 0;
275 
276 	ret = decrypt_stoken(vpninfo);
277 	if (ret == 2) {
278 		vpninfo->token_bypassed = 1;
279 		return 0;
280 	} else if (ret != 0)
281 		return ret;
282 
283 	get_stoken_details(vpninfo);
284 	return request_stoken_pin(vpninfo);
285 }
286 
287 /* Return value:
288  *  < 0, if unable to generate a tokencode
289  *  = 0, on success
290  */
can_gen_stoken_code(struct openconnect_info * vpninfo,struct oc_auth_form * form,struct oc_form_opt * opt)291 int can_gen_stoken_code(struct openconnect_info *vpninfo,
292 			struct oc_auth_form *form,
293 			struct oc_form_opt *opt)
294 {
295 	if (vpninfo->token_tries == 0) {
296 		vpn_progress(vpninfo, PRG_DEBUG,
297 			     _("OK to generate INITIAL tokencode\n"));
298 		vpninfo->token_time = 0;
299 	} else if (vpninfo->token_tries == 1 && form->message &&
300 		   strcasestr(form->message, "next tokencode")) {
301 		vpn_progress(vpninfo, PRG_DEBUG,
302 			     _("OK to generate NEXT tokencode\n"));
303 		vpninfo->token_time += vpninfo->stoken_interval;
304 	} else {
305 		/* limit the number of retries, to avoid account lockouts */
306 		vpn_progress(vpninfo, PRG_INFO,
307 			     _("Server is rejecting the soft token; switching to manual entry\n"));
308 		return -ENOENT;
309 	}
310 	return 0;
311 }
312 
do_gen_stoken_code(struct openconnect_info * vpninfo,struct oc_auth_form * form,struct oc_form_opt * opt)313 int do_gen_stoken_code(struct openconnect_info *vpninfo,
314 		       struct oc_auth_form *form,
315 		       struct oc_form_opt *opt)
316 {
317 	char tokencode[STOKEN_MAX_TOKENCODE + 1];
318 
319 	if (!vpninfo->token_time)
320 		vpninfo->token_time = time(NULL);
321 	vpn_progress(vpninfo, PRG_INFO, _("Generating RSA token code\n"));
322 
323 	/* This doesn't normally fail */
324 	if (stoken_compute_tokencode(vpninfo->stoken_ctx, vpninfo->token_time,
325 				     vpninfo->stoken_pin, tokencode) < 0) {
326 		vpn_progress(vpninfo, PRG_ERR, _("General failure in libstoken.\n"));
327 		return -EIO;
328 	}
329 
330 	vpninfo->token_tries++;
331 
332 	if (asprintf(&opt->_value, "%s%s",
333 	    (vpninfo->stoken_concat_pin && vpninfo->stoken_pin) ? vpninfo->stoken_pin : "",
334 	    tokencode) < 0)
335 		return -ENOMEM;
336 	return 0;
337 }
338 
339