Commit | Line | Data |
---|---|---|
b0322a85 CE |
1 | /* |
2 | ** Copyright 2012 Double Precision, Inc. See COPYING for | |
3 | ** distribution information. | |
4 | */ | |
5 | ||
6 | #if HAVE_CONFIG_H | |
7 | #include "courier_auth_config.h" | |
8 | #endif | |
9 | #include "auth.h" | |
10 | #include "courierauthdebug.h" | |
11 | #include <string.h> | |
12 | #include <stdio.h> | |
13 | #include <stdlib.h> | |
14 | #include <ctype.h> | |
15 | #include <sys/types.h> | |
16 | #include <sys/stat.h> | |
17 | ||
18 | static const char *configfilename=0; | |
19 | static char *configauth=0; | |
20 | static size_t configauth_size=0; | |
21 | ||
22 | #define err courier_auth_err | |
23 | ||
24 | const char *authgetconfig(const char *filename, const char *env) | |
25 | { | |
26 | size_t i; | |
27 | char *p=0; | |
28 | int l=strlen(env); | |
29 | ||
30 | if (configfilename && strcmp(filename, configfilename)) | |
31 | { | |
32 | if (configauth) | |
33 | free(configauth); | |
34 | configauth=0; | |
35 | configauth_size=0; | |
36 | } | |
37 | ||
38 | configfilename=filename; | |
39 | ||
40 | if (!configauth) | |
41 | { | |
42 | FILE *f=fopen(filename, "r"); | |
43 | struct stat buf; | |
44 | ||
45 | if (!f) return (0); | |
46 | if (fstat(fileno(f), &buf) || | |
47 | (configauth=malloc(buf.st_size+2)) == 0) | |
48 | { | |
49 | fclose(f); | |
50 | return (0); | |
51 | } | |
52 | if (fread(configauth, buf.st_size, 1, f) != 1) | |
53 | { | |
54 | free(configauth); | |
55 | configauth=0; | |
56 | fclose(f); | |
57 | return (0); | |
58 | } | |
59 | configauth[configauth_size=buf.st_size]=0; | |
60 | ||
61 | for (i=0; i<configauth_size; i++) | |
62 | if (configauth[i] == '\n') | |
63 | { /* siefca@pld.org.pl */ | |
64 | if (!i || configauth[i-1] != '\\') | |
65 | { | |
66 | configauth[i]='\0'; | |
67 | } | |
68 | else | |
69 | { | |
70 | configauth[i]=configauth[i-1]= ' '; | |
71 | } | |
72 | } | |
73 | fclose(f); | |
74 | } | |
75 | ||
76 | for (i=0; i<configauth_size; ) | |
77 | { | |
78 | p=configauth+i; | |
79 | if (memcmp(p, env, l) == 0 && | |
80 | isspace((int)(unsigned char)p[l])) | |
81 | { | |
82 | p += l; | |
83 | while (*p && *p != '\n' && | |
84 | isspace((int)(unsigned char)*p)) | |
85 | ++p; | |
86 | break; | |
87 | } | |
88 | ||
89 | while (i < configauth_size) | |
90 | if (configauth[i++] == 0) break; | |
91 | } | |
92 | ||
93 | if (i < configauth_size) | |
94 | return (p); | |
95 | return (0); | |
96 | } | |
97 | ||
98 | /* siefca@pld.org.pl */ | |
99 | #define MAX_SUBSTITUTION_LEN 32 | |
100 | #define SV_BEGIN_MARK "$(" | |
101 | #define SV_END_MARK ")" | |
102 | #define SV_BEGIN_LEN ((sizeof(SV_BEGIN_MARK))-1) | |
103 | #define SV_END_LEN ((sizeof(SV_END_MARK))-1) | |
104 | ||
105 | /* siefca@pld.org.pl */ | |
106 | struct var_data { | |
107 | const char *name; | |
108 | const char *value; | |
109 | const size_t size; | |
110 | size_t value_length; | |
111 | } ; | |
112 | ||
113 | /* siefca@pld.org.pl */ | |
114 | typedef int (*parsefunc)(const char *, size_t, void *); | |
115 | ||
116 | /* siefca@pld.org.pl */ | |
117 | static struct var_data *get_variable (const char *begin, size_t len, | |
118 | struct var_data *vdt) | |
119 | { | |
120 | struct var_data *vdp; | |
121 | ||
122 | if (!begin || !vdt) /* should never happend */ | |
123 | { | |
124 | err("get_variable: critical error while " | |
125 | "parsing substitution variable"); | |
126 | return NULL; | |
127 | } | |
128 | if (len < 1) | |
129 | { | |
130 | err("get_variable: unknown empty substitution " | |
131 | "variable - aborting"); | |
132 | return NULL; | |
133 | } | |
134 | if (len > MAX_SUBSTITUTION_LEN) | |
135 | { | |
136 | err("get_variable: variable name too long " | |
137 | "while parsing substitution. " | |
138 | "name begins with " | |
139 | SV_BEGIN_MARK | |
140 | "%.*s...", MAX_SUBSTITUTION_LEN, begin); | |
141 | return NULL; | |
142 | } | |
143 | ||
144 | for (vdp=vdt; vdp->name; vdp++) | |
145 | if (vdp->size == len+1 && | |
146 | !strncmp(begin, vdp->name, len)) | |
147 | { | |
148 | if (!vdp->value) | |
149 | vdp->value = ""; | |
150 | if (!vdp->value_length) /* length cache */ | |
151 | vdp->value_length = strlen (vdp->value); | |
152 | return vdp; | |
153 | } | |
154 | ||
155 | err("get_variable: unknown substitution variable " | |
156 | SV_BEGIN_MARK | |
157 | "%.*s" | |
158 | SV_END_MARK | |
159 | , (int)len, begin); | |
160 | ||
161 | return NULL; | |
162 | } | |
163 | ||
164 | /* siefca@pld.org.pl */ | |
165 | static int ParsePlugin_counter (const char *p, size_t length, void *vp) | |
166 | { | |
167 | if (!p || !vp || length < 0) | |
168 | { | |
169 | err("get_variable: bad arguments while counting " | |
170 | "query string"); | |
171 | return -1; | |
172 | } | |
173 | ||
174 | *((size_t *)vp) += length; | |
175 | ||
176 | return 0; | |
177 | } | |
178 | ||
179 | /* siefca@pld.org.pl */ | |
180 | static int ParsePlugin_builder (const char *p, size_t length, void *vp) | |
181 | { | |
182 | char **strptr = (char **) vp; | |
183 | ||
184 | if (!p || !vp || length < 0) | |
185 | { | |
186 | err("get_variable: bad arguments while building " | |
187 | "query string"); | |
188 | return -1; | |
189 | } | |
190 | ||
191 | if (!length) return 0; | |
192 | memcpy ((void *) *strptr, (void *) p, length); | |
193 | *strptr += length; | |
194 | ||
195 | return 0; | |
196 | } | |
197 | ||
198 | /* siefca@pld.org.pl */ | |
199 | static int parse_core (const char *source, struct var_data *vdt, | |
200 | parsefunc outfn, void *result) | |
201 | { | |
202 | size_t v_size = 0, | |
203 | t_size = 0; | |
204 | const char *p, *q, *e, | |
205 | *v_begin, *v_end, | |
206 | *t_begin, *t_end; | |
207 | struct var_data *v_ptr; | |
208 | ||
209 | if (!source) | |
210 | source = ""; | |
211 | if (!result) | |
212 | { | |
213 | err("auth_parse: no memory allocated for result " | |
214 | "while parser core was invoked"); | |
215 | return -1; | |
216 | } | |
217 | if (!vdt) | |
218 | { | |
219 | err("auth_parse: no substitution table found " | |
220 | "while parser core was invoked"); | |
221 | return -1; | |
222 | } | |
223 | ||
224 | q = source; | |
225 | while ( (p=strstr(q, SV_BEGIN_MARK)) ) | |
226 | { | |
227 | e = strstr (p, SV_END_MARK); | |
228 | if (!e) | |
229 | { | |
230 | err("auth_parse: syntax error in " | |
231 | "substitution " | |
232 | "- no closing symbol found! " | |
233 | "bad variable begins with:" | |
234 | "%.*s...", MAX_SUBSTITUTION_LEN, p); | |
235 | return -1; | |
236 | } | |
237 | ||
238 | /* | |
239 | ** | |
240 | ** __________sometext$(variable_name)_________ | |
241 | ** | | | | | |
242 | ** t_begin' t_end' `v_begin `v_end | |
243 | ** | |
244 | */ | |
245 | ||
246 | v_begin = p+SV_BEGIN_LEN; /* variable field ptr */ | |
247 | v_end = e-SV_END_LEN; /* variable field last character */ | |
248 | v_size = v_end-v_begin+1;/* variable field length */ | |
249 | ||
250 | t_begin = q; /* text field ptr */ | |
251 | t_end = p-1; /* text field last character */ | |
252 | t_size = t_end-t_begin+1;/* text field length */ | |
253 | ||
254 | /* work on text */ | |
255 | if ( (outfn (t_begin, t_size, result)) == -1 ) | |
256 | return -1; | |
257 | ||
258 | /* work on variable */ | |
259 | v_ptr = get_variable (v_begin, v_size, vdt); | |
260 | if (!v_ptr) return -1; | |
261 | ||
262 | if ( (outfn (v_ptr->value, v_ptr->value_length, result)) == -1 ) | |
263 | return -1; | |
264 | ||
265 | q = e + 1; | |
266 | } | |
267 | ||
268 | /* work on last part of text if any */ | |
269 | if (*q != '\0') | |
270 | if ( (outfn (q, strlen(q), result)) == -1 ) | |
271 | return -1; | |
272 | ||
273 | return 0; | |
274 | } | |
275 | ||
276 | /* siefca@pld.org.pl */ | |
277 | static char *parse_string (const char *source, struct var_data *vdt) | |
278 | { | |
279 | struct var_data *vdp = NULL; | |
280 | char *output_buf = NULL, | |
281 | *pass_buf = NULL; | |
282 | size_t buf_size = 2; | |
283 | ||
284 | if (source == NULL || *source == '\0' || | |
285 | vdt == NULL || vdt[0].name == NULL) | |
286 | { | |
287 | err("auth_parse: source clause is empty " | |
288 | "- this is critical error"); | |
289 | return NULL; | |
290 | } | |
291 | ||
292 | /* zero var_data length cache - important! */ | |
293 | for (vdp=vdt; vdp->name; vdp++) | |
294 | vdp->value_length = 0; | |
295 | ||
296 | ||
297 | /* phase 1 - count and validate string */ | |
298 | if ( (parse_core (source, vdt, &ParsePlugin_counter, &buf_size)) != 0) | |
299 | return NULL; | |
300 | ||
301 | /* phase 2 - allocate memory */ | |
302 | output_buf = malloc (buf_size); | |
303 | if (!output_buf) | |
304 | { | |
305 | perror ("malloc"); | |
306 | return NULL; | |
307 | } | |
308 | pass_buf = output_buf; | |
309 | ||
310 | /* phase 3 - build the output string */ | |
311 | if ( (parse_core (source, vdt, &ParsePlugin_builder, &pass_buf)) != 0) | |
312 | { | |
313 | free (output_buf); | |
314 | return NULL; | |
315 | } | |
316 | *pass_buf = '\0'; | |
317 | ||
318 | return output_buf; | |
319 | } | |
320 | ||
321 | static char *local_part_escaped(const char *username, | |
322 | char *(*escape_func)(const char *, size_t)) | |
323 | { | |
324 | const char *p=strchr(username, '@'); | |
325 | size_t n=p ? p-username:strlen(username); | |
326 | ||
327 | return escape_func(username, n); | |
328 | } | |
329 | ||
330 | static char *domain_part_escaped(const char *username, | |
331 | const char *defdomain, | |
332 | char *(*escape_func)(const char *, size_t)) | |
333 | { | |
334 | const char *p=strchr(username, '@'); | |
335 | size_t n; | |
336 | ||
337 | if (p) | |
338 | ++p; | |
339 | else | |
340 | p=defdomain; | |
341 | ||
342 | n=strlen(p); | |
343 | ||
344 | return escape_func(p, n); | |
345 | } | |
346 | ||
347 | static int local_and_domain_part_escaped(char *(*escape_func)(const char *, size_t), | |
348 | const char *username, | |
349 | const char *defdomain, | |
350 | char **local_ret, | |
351 | char **domain_ret) | |
352 | { | |
353 | if ((*local_ret=local_part_escaped(username, escape_func)) == NULL) | |
354 | return 0; | |
355 | ||
356 | if ((*domain_ret=domain_part_escaped(username, defdomain, | |
357 | escape_func)) == NULL) | |
358 | { | |
359 | free(*local_ret); | |
360 | return 0; | |
361 | } | |
362 | ||
363 | return 1; | |
364 | } | |
365 | ||
366 | /* siefca@pld.org.pl */ | |
367 | char *auth_parse_select_clause (char *(*escape_func)(const char *, size_t), | |
368 | const char *clause, const char *username, | |
369 | const char *defdomain, | |
370 | const char *service) | |
371 | { | |
372 | char *str; | |
373 | ||
374 | static struct var_data vd[]={ | |
375 | {"local_part", NULL, sizeof("local_part"), 0}, | |
376 | {"domain", NULL, sizeof("domain"), 0}, | |
377 | {"service", NULL, sizeof("service"), 0}, | |
378 | {NULL, NULL, 0, 0}}; | |
379 | ||
380 | char *l_part; | |
381 | char *d_part; | |
382 | ||
383 | if (clause == NULL || *clause == '\0' || | |
384 | !username || *username == '\0') | |
385 | return NULL; | |
386 | ||
387 | if (!local_and_domain_part_escaped(escape_func, | |
388 | username, defdomain, | |
389 | &l_part, &d_part)) | |
390 | return NULL; | |
391 | ||
392 | vd[0].value=l_part; | |
393 | vd[1].value=d_part; | |
394 | vd[2].value = service; | |
395 | ||
396 | str=parse_string (clause, vd); | |
397 | free(l_part); | |
398 | free(d_part); | |
399 | return str; | |
400 | } | |
401 | ||
402 | /* siefca@pld.org.pl */ | |
403 | char *auth_parse_chpass_clause (char *(*escape_func)(const char *, size_t), | |
404 | const char *clause, const char *username, | |
405 | const char *defdomain, const char *newpass, | |
406 | const char *newpass_crypt) | |
407 | { | |
408 | char *str; | |
409 | ||
410 | static struct var_data vd[]={ | |
411 | {"local_part", NULL, sizeof("local_part"), 0}, | |
412 | {"domain", NULL, sizeof("domain"), 0}, | |
413 | {"newpass", NULL, sizeof("newpass"), 0}, | |
414 | {"newpass_crypt", NULL, sizeof("newpass_crypt"), 0}, | |
415 | {NULL, NULL, 0, 0}}; | |
416 | char *l_part; | |
417 | char *d_part; | |
418 | ||
419 | if (clause == NULL || *clause == '\0' || | |
420 | !username || *username == '\0' || | |
421 | !newpass || *newpass == '\0' || | |
422 | !newpass_crypt || *newpass_crypt == '\0') return NULL; | |
423 | ||
424 | if (!local_and_domain_part_escaped(escape_func, | |
425 | username, defdomain, | |
426 | &l_part, &d_part)) | |
427 | return NULL; | |
428 | ||
429 | vd[0].value=l_part; | |
430 | vd[1].value=d_part; | |
431 | vd[2].value = newpass; | |
432 | vd[3].value = newpass_crypt; | |
433 | ||
434 | if (!vd[0].value || !vd[1].value || | |
435 | !vd[2].value || !vd[3].value) | |
436 | { | |
437 | free(l_part); | |
438 | free(d_part); | |
439 | return NULL; | |
440 | } | |
441 | ||
442 | str=parse_string (clause, vd); | |
443 | free(l_part); | |
444 | free(d_part); | |
445 | return str; | |
446 | } |