1 /*
2  * OpenConnect (SSL + DTLS) VPN client
3  *
4  * Copyright © 2008-2015 Intel Corporation.
5  *
6  * Author: 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 
18 #include <config.h>
19 
20 #include <unistd.h>
21 #include <fcntl.h>
22 #include <time.h>
23 #include <string.h>
24 #include <ctype.h>
25 #include <errno.h>
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <sys/types.h>
29 #include <stdarg.h>
30 
31 #include "openconnect-internal.h"
32 
xmlnode_is_named(xmlNode * xml_node,const char * name)33 int xmlnode_is_named(xmlNode *xml_node, const char *name)
34 {
35 	return !strcmp((char *)xml_node->name, name);
36 }
37 
38 /* similar to auth.c's xmlnode_get_text, including that *var should be freed by the caller,
39    but without the hackish param / %s handling that Cisco needs. */
xmlnode_get_val(xmlNode * xml_node,const char * name,char ** var)40 int xmlnode_get_val(xmlNode *xml_node, const char *name, char **var)
41 {
42 	char *str;
43 
44 	if (name && !xmlnode_is_named(xml_node, name))
45 		return -EINVAL;
46 
47 	str = (char *)xmlNodeGetContent(xml_node);
48 	if (!str)
49 		return -ENOENT;
50 
51 	free(*var);
52 	*var = str;
53 	return 0;
54 }
55 
xmlnode_get_prop(xmlNode * xml_node,const char * name,char ** var)56 int xmlnode_get_prop(xmlNode *xml_node, const char *name, char **var)
57 {
58 	char *str = (char *)xmlGetProp(xml_node, (unsigned char *)name);
59 
60 	if (!str)
61 		return -ENOENT;
62 
63 	free(*var);
64 	*var = str;
65 	return 0;
66 }
67 
xmlnode_match_prop(xmlNode * xml_node,const char * name,const char * match)68 int xmlnode_match_prop(xmlNode *xml_node, const char *name, const char *match)
69 {
70 	char *str = (char *)xmlGetProp(xml_node, (unsigned char *)name);
71 	int ret = 0;
72 
73 	if (!str)
74 		return -ENOENT;
75 
76 	if (strcmp(str, match))
77 	    ret = -EEXIST;
78 
79 	free(str);
80 	return ret;
81 }
82 
append_opt(struct oc_text_buf * body,const char * opt,const char * name)83 int append_opt(struct oc_text_buf *body, const char *opt, const char *name)
84 {
85 	if (buf_error(body))
86 		return buf_error(body);
87 
88 	if (body->pos)
89 		buf_append(body, "&");
90 
91 	buf_append_urlencoded(body, opt);
92 	buf_append(body, "=");
93 	buf_append_urlencoded(body, name);
94 
95 	return 0;
96 }
97 
append_form_opts(struct openconnect_info * vpninfo,struct oc_auth_form * form,struct oc_text_buf * body)98 int append_form_opts(struct openconnect_info *vpninfo,
99 			    struct oc_auth_form *form, struct oc_text_buf *body)
100 {
101 	struct oc_form_opt *opt;
102 	int ret;
103 
104 	for (opt = form->opts; opt; opt = opt->next) {
105 		ret = append_opt(body, opt->name, opt->_value);
106 		if (ret)
107 			return ret;
108 	}
109 	return 0;
110 }
111 
clear_mem(void * p,size_t s)112 void clear_mem(void *p, size_t s)
113 {
114 #if defined(HAVE_MEMSET_S)
115 	memset_s(p, s, 0x5a, s);
116 #elif defined(HAVE_EXPLICIT_MEMSET)
117 	explicit_memset(p, 0x5a, s);
118 #elif defined(HAVE_EXPLICIT_BZERO)
119 	explicit_bzero(p, s);
120 #elif defined(_WIN32)
121 	SecureZeroMemory(p, s);
122 #else
123 	volatile char *pp = (volatile char *)p;
124 	while (s--)
125 		*(pp++) = 0x5a;
126 #endif
127 }
128 
free_pass(char ** p)129 void free_pass(char **p)
130 {
131 	if (!*p)
132 		return;
133 
134 	clear_mem(*p, strlen(*p));
135 	free(*p);
136 	*p = NULL;
137 }
138 
free_opt(struct oc_form_opt * opt)139 void free_opt(struct oc_form_opt *opt)
140 {
141 	if (!opt)
142 		return;
143 
144 	/* for SELECT options, opt->value is a pointer to oc_choice->name */
145 	if (opt->type != OC_FORM_OPT_SELECT) {
146 		free_pass(&opt->_value);
147 	} else {
148 		struct oc_form_opt_select *sel = (void *)opt;
149 		int i;
150 
151 		for (i = 0; i < sel->nr_choices; i++) {
152 			free(sel->choices[i]->name);
153 			free(sel->choices[i]->label);
154 			free(sel->choices[i]->auth_type);
155 			free(sel->choices[i]->override_name);
156 			free(sel->choices[i]->override_label);
157 			free(sel->choices[i]);
158 		}
159 		free(sel->choices);
160 	}
161 
162 	free(opt->name);
163 	free(opt->label);
164 	free(opt);
165 }
166 
free_auth_form(struct oc_auth_form * form)167 void free_auth_form(struct oc_auth_form *form)
168 {
169 	if (!form)
170 		return;
171 	while (form->opts) {
172 		struct oc_form_opt *tmp = form->opts->next;
173 		free_opt(form->opts);
174 		form->opts = tmp;
175 	}
176 	free(form->error);
177 	free(form->message);
178 	free(form->banner);
179 	free(form->auth_id);
180 	free(form->method);
181 	free(form->action);
182 	free(form);
183 }
184 
185 /* Return value:
186  *  < 0, if unable to generate a tokencode
187  *  = 0, on success
188  */
do_gen_tokencode(struct openconnect_info * vpninfo,struct oc_auth_form * form)189 int do_gen_tokencode(struct openconnect_info *vpninfo,
190 		     struct oc_auth_form *form)
191 {
192 	struct oc_form_opt *opt;
193 
194 	for (opt = form->opts; ; opt = opt->next) {
195 		/* this form might not have anything for us to do */
196 		if (!opt)
197 			return 0;
198 		if (opt->type == OC_FORM_OPT_TOKEN)
199 			break;
200 	}
201 
202 	switch (vpninfo->token_mode) {
203 #ifdef HAVE_LIBSTOKEN
204 	case OC_TOKEN_MODE_STOKEN:
205 		return do_gen_stoken_code(vpninfo, form, opt);
206 #endif
207 	case OC_TOKEN_MODE_TOTP:
208 		return do_gen_totp_code(vpninfo, form, opt);
209 
210 	case OC_TOKEN_MODE_HOTP:
211 		return do_gen_hotp_code(vpninfo, form, opt);
212 #ifdef HAVE_LIBPCSCLITE
213 	case OC_TOKEN_MODE_YUBIOATH:
214 		return do_gen_yubikey_code(vpninfo, form, opt);
215 #endif
216 	default:
217 		return -EINVAL;
218 	}
219 }
220 
can_gen_tokencode(struct openconnect_info * vpninfo,struct oc_auth_form * form,struct oc_form_opt * opt)221 int can_gen_tokencode(struct openconnect_info *vpninfo,
222 		      struct oc_auth_form *form,
223 		      struct oc_form_opt *opt)
224 {
225 	switch (vpninfo->token_mode) {
226 #ifdef HAVE_LIBSTOKEN
227 	case OC_TOKEN_MODE_STOKEN:
228 		return can_gen_stoken_code(vpninfo, form, opt);
229 #endif
230 	case OC_TOKEN_MODE_TOTP:
231 		return can_gen_totp_code(vpninfo, form, opt);
232 
233 	case OC_TOKEN_MODE_HOTP:
234 		return can_gen_hotp_code(vpninfo, form, opt);
235 #ifdef HAVE_LIBPCSCLITE
236 	case OC_TOKEN_MODE_YUBIOATH:
237 		return can_gen_yubikey_code(vpninfo, form, opt);
238 #endif
239 	default:
240 		return -EINVAL;
241 	}
242 }
243