1 /*
2  * Copyright (C) 2015 Red Hat, Inc.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include <config.h>
19 
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <unistd.h>
24 #include <sys/types.h>
25 #include <c-strcase.h>
26 #include <c-ctype.h>
27 
28 #include <sec-mod-sup-config.h>
29 #include <common.h>
30 #include <vpn.h>
31 #include "common-config.h"
32 
free_expanded_brackets_string(subcfg_val_st out[MAX_SUBOPTIONS],unsigned size)33 static void free_expanded_brackets_string(subcfg_val_st out[MAX_SUBOPTIONS], unsigned size)
34 {
35 	unsigned i;
36 	for (i=0;i<size;i++) {
37 		talloc_free(out[i].name);
38 		talloc_free(out[i].value);
39 	}
40 }
41 
42 /* Returns the number of suboptions processed.
43  */
44 static
expand_brackets_string(void * pool,const char * str,subcfg_val_st out[MAX_SUBOPTIONS])45 unsigned expand_brackets_string(void *pool, const char *str, subcfg_val_st out[MAX_SUBOPTIONS])
46 {
47 	char *p, *p2, *p3;
48 	unsigned len, len2;
49 	unsigned pos = 0, finish = 0;
50 
51 	if (str == NULL)
52 		return 0;
53 
54 	p = strchr(str, '[');
55 	if (p == NULL) {
56 		return 0;
57 	}
58 	p++;
59 	while (c_isspace(*p))
60 		p++;
61 
62 	do {
63 		p2 = strchr(p, '=');
64 		if (p2 == NULL) {
65 			fprintf(stderr, "error parsing %s\n", str);
66 			exit(1);
67 		}
68 		len = p2 - p;
69 
70 		p2++;
71 		while (c_isspace(*p2))
72 			p2++;
73 
74 		p3 = strchr(p2, ',');
75 		if (p3 == NULL) {
76 			p3 = strchr(p2, ']');
77 			if (p3 == NULL) {
78 				fprintf(stderr, "error parsing %s\n", str);
79 				exit(1);
80 			}
81 			finish = 1;
82 		}
83 		len2 = p3 - p2;
84 
85 		if (len > 0) {
86 			while (c_isspace(p[len-1]))
87 				len--;
88 		}
89 		if (len2 > 0) {
90 			while (c_isspace(p2[len2-1]))
91 				len2--;
92 		}
93 
94 		out[pos].name = talloc_strndup(pool, p, len);
95 		out[pos].value = talloc_strndup(pool, p2, len2);
96 		pos++;
97 		p = p2+len2;
98 		while (c_isspace(*p)||*p==',')
99 			p++;
100 	} while(finish == 0 && pos < MAX_SUBOPTIONS);
101 
102 	return pos;
103 }
104 
105 #ifdef HAVE_GSSAPI
gssapi_get_brackets_string(void * pool,struct perm_cfg_st * config,const char * str)106 void *gssapi_get_brackets_string(void *pool, struct perm_cfg_st *config, const char *str)
107 {
108 	subcfg_val_st vals[MAX_SUBOPTIONS];
109 	unsigned vals_size, i;
110 	gssapi_cfg_st *additional;
111 
112 	additional = talloc_zero(pool, gssapi_cfg_st);
113 	if (additional == NULL) {
114 		return NULL;
115 	}
116 
117 	vals_size = expand_brackets_string(pool, str, vals);
118 	for (i=0;i<vals_size;i++) {
119 		if (c_strcasecmp(vals[i].name, "keytab") == 0) {
120 			additional->keytab = vals[i].value;
121 			vals[i].value = NULL;
122 		} else if (c_strcasecmp(vals[i].name, "require-local-user-map") == 0) {
123 			additional->no_local_map = 1-CHECK_TRUE(vals[i].value);
124 		} else if (c_strcasecmp(vals[i].name, "tgt-freshness-time") == 0) {
125 			additional->ticket_freshness_secs = atoi(vals[i].value);
126 			if (additional->ticket_freshness_secs == 0) {
127 				fprintf(stderr, "Invalid value for '%s': %s\n", vals[i].name, vals[i].value);
128 				exit(1);
129 			}
130 		} else if (c_strcasecmp(vals[i].name, "gid-min") == 0) {
131 			additional->gid_min = atoi(vals[i].value);
132 			if (additional->gid_min < 0) {
133 				fprintf(stderr, "error in gid-min value: %d\n", additional->gid_min);
134 				exit(1);
135 			}
136 		} else {
137 			fprintf(stderr, "unknown option '%s'\n", vals[i].name);
138 			exit(1);
139 		}
140 	}
141 	free_expanded_brackets_string(vals, vals_size);
142 	return additional;
143 }
144 #endif
145 
get_brackets_string1(void * pool,const char * str)146 void *get_brackets_string1(void *pool, const char *str)
147 {
148 	char *p, *p2;
149 	unsigned len;
150 
151 	p = strchr(str, '[');
152 	if (p == NULL) {
153 		return NULL;
154 	}
155 	p++;
156 	while (c_isspace(*p))
157 		p++;
158 
159 	p2 = strchr(p, ',');
160 	if (p2 == NULL) {
161 		p2 = strchr(p, ']');
162 		if (p2 == NULL) {
163 			fprintf(stderr, "error parsing %s\n", str);
164 			exit(1);
165 		}
166 	}
167 
168 	len = p2 - p;
169 
170 	return talloc_strndup(pool, p, len);
171 }
172 
173 #ifdef HAVE_RADIUS
get_brackets_string2(void * pool,const char * str)174 static void *get_brackets_string2(void *pool, const char *str)
175 {
176 	char *p, *p2;
177 	unsigned len;
178 
179 	p = strchr(str, '[');
180 	if (p == NULL) {
181 		return NULL;
182 	}
183 	p++;
184 
185 	p = strchr(p, ',');
186 	if (p == NULL) {
187 		return NULL;
188 	}
189 	p++;
190 
191 	while (c_isspace(*p))
192 		p++;
193 
194 	p2 = strchr(p, ',');
195 	if (p2 == NULL) {
196 		p2 = strchr(p, ']');
197 		if (p2 == NULL) {
198 			fprintf(stderr, "error parsing %s\n", str);
199 			exit(1);
200 		}
201 	}
202 
203 	len = p2 - p;
204 
205 	return talloc_strndup(pool, p, len);
206 }
207 
radius_get_brackets_string(void * pool,struct perm_cfg_st * config,const char * str)208 void *radius_get_brackets_string(void *pool, struct perm_cfg_st *config, const char *str)
209 {
210 	char *p;
211 	subcfg_val_st vals[MAX_SUBOPTIONS];
212 	unsigned vals_size, i;
213 	radius_cfg_st *additional;
214 
215 	additional = talloc_zero(pool, radius_cfg_st);
216 	if (additional == NULL) {
217 		return NULL;
218 	}
219 
220 	if (str && str[0] == '[' && (str[1] == '/' || str[1] == '.')) { /* legacy format */
221 		fprintf(stderr, "Parsing radius auth method subconfig using legacy format\n");
222 
223 		additional->config = get_brackets_string1(pool, str);
224 
225 		p = get_brackets_string2(config, str);
226 		if (p != NULL) {
227 			if (strcasecmp(p, "groupconfig") != 0) {
228 				fprintf(stderr, "No known configuration option: %s\n", p);
229 				exit(1);
230 			}
231 			config->sup_config_type = SUP_CONFIG_RADIUS;
232 		}
233 	} else {
234 		/* new format */
235 		vals_size = expand_brackets_string(pool, str, vals);
236 		for (i=0;i<vals_size;i++) {
237 			if (c_strcasecmp(vals[i].name, "config") == 0) {
238 				additional->config = vals[i].value;
239 				vals[i].value = NULL;
240 			} else if (c_strcasecmp(vals[i].name, "nas-identifier") == 0) {
241 				additional->nas_identifier = vals[i].value;
242 				vals[i].value = NULL;
243 			} else if (c_strcasecmp(vals[i].name, "groupconfig") == 0) {
244 				if (CHECK_TRUE(vals[i].value))
245 					config->sup_config_type = SUP_CONFIG_RADIUS;
246 			} else {
247 				fprintf(stderr, "unknown option '%s'\n", vals[i].name);
248 				exit(1);
249 			}
250 		}
251 		free_expanded_brackets_string(vals, vals_size);
252 	}
253 
254 	if (additional->config == NULL) {
255 		fprintf(stderr, "No radius configuration specified: %s\n", str);
256 		exit(1);
257 	}
258 
259 	return additional;
260 }
261 #endif
262 
263 #ifdef HAVE_PAM
pam_get_brackets_string(void * pool,struct perm_cfg_st * config,const char * str)264 void *pam_get_brackets_string(void *pool, struct perm_cfg_st *config, const char *str)
265 {
266 	subcfg_val_st vals[MAX_SUBOPTIONS];
267 	unsigned vals_size, i;
268 	pam_cfg_st *additional;
269 
270 	additional = talloc_zero(pool, pam_cfg_st);
271 	if (additional == NULL) {
272 		return NULL;
273 	}
274 
275 	/* new format */
276 	vals_size = expand_brackets_string(pool, str, vals);
277 	for (i=0;i<vals_size;i++) {
278 		if (c_strcasecmp(vals[i].name, "gid-min") == 0) {
279 			additional->gid_min = atoi(vals[i].value);
280 			if (additional->gid_min < 0) {
281 				fprintf(stderr, "error in gid-min value: %d\n", additional->gid_min);
282 				exit(1);
283 			}
284 		} else {
285 			fprintf(stderr, "unknown option '%s'\n", vals[i].name);
286 			exit(1);
287 		}
288 	}
289 
290 	free_expanded_brackets_string(vals, vals_size);
291 	return additional;
292 }
293 #endif
294 
plain_get_brackets_string(void * pool,struct perm_cfg_st * config,const char * str)295 void *plain_get_brackets_string(void *pool, struct perm_cfg_st *config, const char *str)
296 {
297 	subcfg_val_st vals[MAX_SUBOPTIONS];
298 	unsigned vals_size, i;
299 	plain_cfg_st *additional;
300 
301 	additional = talloc_zero(pool, plain_cfg_st);
302 	if (additional == NULL) {
303 		return NULL;
304 	}
305 
306 	if (str && str[0] == '[' && (str[1] == '/' || str[1] == '.')) { /* legacy format */
307 		fprintf(stderr, "Parsing plain auth method subconfig using legacy format\n");
308 		additional->passwd = get_brackets_string1(pool, str);
309 	} else {
310 		vals_size = expand_brackets_string(pool, str, vals);
311 		for (i=0;i<vals_size;i++) {
312 			if (c_strcasecmp(vals[i].name, "passwd") == 0) {
313 				additional->passwd = vals[i].value;
314 				vals[i].value = NULL;
315 #ifdef HAVE_LIBOATH
316 			} else if (c_strcasecmp(vals[i].name, "otp") == 0) {
317 				additional->otp_file = vals[i].value;
318 				vals[i].value = NULL;
319 #endif
320 			} else {
321 				fprintf(stderr, "unknown option '%s'\n", vals[i].name);
322 				exit(1);
323 			}
324 		}
325 		free_expanded_brackets_string(vals, vals_size);
326 	}
327 
328 	if (additional->passwd == NULL && additional->otp_file == NULL) {
329 		fprintf(stderr, "plain: no password or OTP file specified\n");
330 		exit(1);
331 	}
332 
333 	return additional;
334 }
335 
336 
oidc_get_brackets_string(void * pool,struct perm_cfg_st * config,const char * str)337 void *oidc_get_brackets_string(void * pool, struct perm_cfg_st *config, const char *str)
338 {
339 	subcfg_val_st vals[MAX_SUBOPTIONS];
340 	char * additional = NULL;
341 
342 	unsigned vals_size, i;
343 
344 	vals_size  = expand_brackets_string(pool, str, vals);
345 
346 	for (i = 0; i < vals_size; i ++)	{
347 		if (c_strcasecmp(vals[i].name, "config") == 0) {
348 			additional = talloc_strdup(pool, vals[i].value);
349 		}
350 	}
351 
352 	return additional;
353 }
354