File: | src/lib/libc/gen/auth_subr.c |
Warning: | line 721, column 7 Value stored to 'n' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: auth_subr.c,v 1.56 2020/10/13 04:42:28 guenther Exp $ */ |
2 | |
3 | /* |
4 | * Copyright (c) 2000-2002,2004 Todd C. Miller <millert@openbsd.org> |
5 | * |
6 | * Permission to use, copy, modify, and distribute this software for any |
7 | * purpose with or without fee is hereby granted, provided that the above |
8 | * copyright notice and this permission notice appear in all copies. |
9 | * |
10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
17 | */ |
18 | /*- |
19 | * Copyright (c) 1995,1996,1997 Berkeley Software Design, Inc. |
20 | * All rights reserved. |
21 | * |
22 | * Redistribution and use in source and binary forms, with or without |
23 | * modification, are permitted provided that the following conditions |
24 | * are met: |
25 | * 1. Redistributions of source code must retain the above copyright |
26 | * notice, this list of conditions and the following disclaimer. |
27 | * 2. Redistributions in binary form must reproduce the above copyright |
28 | * notice, this list of conditions and the following disclaimer in the |
29 | * documentation and/or other materials provided with the distribution. |
30 | * 3. All advertising materials mentioning features or use of this software |
31 | * must display the following acknowledgement: |
32 | * This product includes software developed by Berkeley Software Design, |
33 | * Inc. |
34 | * 4. The name of Berkeley Software Design, Inc. may not be used to endorse |
35 | * or promote products derived from this software without specific prior |
36 | * written permission. |
37 | * |
38 | * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``AS IS'' AND |
39 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
40 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
41 | * ARE DISCLAIMED. IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN, INC. BE LIABLE |
42 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
43 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
44 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
45 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
46 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
47 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
48 | * SUCH DAMAGE. |
49 | * |
50 | * BSDI $From: auth_subr.c,v 2.4 1999/09/08 04:10:40 prb Exp $ |
51 | */ |
52 | |
53 | #include <sys/time.h> |
54 | #include <sys/resource.h> |
55 | #include <sys/socket.h> |
56 | #include <sys/wait.h> |
57 | |
58 | #include <ctype.h> |
59 | #include <err.h> |
60 | #include <errno(*__errno()).h> |
61 | #include <fcntl.h> |
62 | #include <limits.h> |
63 | #include <paths.h> |
64 | #include <pwd.h> |
65 | #include <stdarg.h> |
66 | #include <stdio.h> |
67 | #include <stdlib.h> |
68 | #include <string.h> |
69 | #include <syslog.h> |
70 | #include <unistd.h> |
71 | |
72 | #include <login_cap.h> |
73 | |
74 | #define MAXSPOOLSIZE(8*1024) (8*1024) /* Spool up to 8K of back info */ |
75 | |
76 | struct rmfiles { |
77 | struct rmfiles *next; |
78 | char *file; |
79 | }; |
80 | |
81 | struct authopts { |
82 | struct authopts *next; |
83 | char *opt; |
84 | }; |
85 | |
86 | struct authdata { |
87 | struct authdata *next; |
88 | void *ptr; |
89 | size_t len; |
90 | }; |
91 | |
92 | struct auth_session_t { |
93 | char *name; /* name of use being authenticated */ |
94 | char *style; /* style of authentication used */ |
95 | char *class; /* class of user */ |
96 | char *service; /* type of service being performed */ |
97 | char *challenge; /* last challenge issued */ |
98 | int flags; /* see below */ |
99 | struct passwd *pwd; /* password entry for user */ |
100 | struct timeval now; /* time of authentication */ |
101 | |
102 | int state; /* authenticated state */ |
103 | |
104 | struct rmfiles *rmlist; /* list of files to remove on failure */ |
105 | struct authopts *optlist; /* list of options to scripts */ |
106 | struct authdata *data; /* additional data to send to scripts */ |
107 | |
108 | char spool[MAXSPOOLSIZE(8*1024)]; /* data returned from login script */ |
109 | int index; /* how much returned thus far */ |
110 | |
111 | int fd; /* connection to authenticator */ |
112 | |
113 | va_list ap0; /* argument list to auth_call */ |
114 | va_list ap; /* additional arguments to auth_call */ |
115 | }; |
116 | |
117 | /* |
118 | * Internal flags |
119 | */ |
120 | #define AF_INTERACTIVE0x0001 0x0001 /* This is an interactive session */ |
121 | |
122 | /* |
123 | * We cannot include bsd_auth.h until we define the above structures |
124 | */ |
125 | #include <bsd_auth.h> |
126 | |
127 | /* |
128 | * Internally used functions |
129 | */ |
130 | static void _add_rmlist(auth_session_t *, char *); |
131 | static void _auth_spool(auth_session_t *, int); |
132 | static void _recv_fd(auth_session_t *, int); |
133 | static char *_auth_next_arg(auth_session_t *); |
134 | /* |
135 | * Set up a known environment for all authentication scripts. |
136 | */ |
137 | static char * const auth_environ[] = { |
138 | "PATH=" _PATH_DEFPATH"/usr/bin:/bin:/usr/sbin:/sbin:/usr/X11R6/bin:/usr/local/bin:/usr/local/sbin", |
139 | "SHELL=" _PATH_BSHELL"/bin/sh", |
140 | NULL((void *)0), |
141 | }; |
142 | |
143 | static char defservice[] = LOGIN_DEFSERVICE"login"; |
144 | |
145 | static va_list nilap; |
146 | |
147 | /* |
148 | * Quick one liners that only exist to keep auth_session_t opaque |
149 | */ |
150 | void auth_setstate(auth_session_t *as, int s){ as->state = s; } |
151 | void auth_set_va_list(auth_session_t *as, va_list ap) { va_copy(as->ap, ap)__builtin_va_copy(((as->ap)),((ap))); } |
152 | int auth_getstate(auth_session_t *as) { return (as->state); } |
153 | struct passwd *auth_getpwd(auth_session_t *as) { return (as->pwd); } |
154 | DEF_WEAK(auth_setstate)__asm__(".weak " "auth_setstate" " ; " "auth_setstate" " = " "_libc_auth_setstate" ); |
155 | DEF_WEAK(auth_set_va_list)__asm__(".weak " "auth_set_va_list" " ; " "auth_set_va_list" " = " "_libc_auth_set_va_list"); |
156 | DEF_WEAK(auth_getstate)__asm__(".weak " "auth_getstate" " ; " "auth_getstate" " = " "_libc_auth_getstate" ); |
157 | DEF_WEAK(auth_getpwd)__asm__(".weak " "auth_getpwd" " ; " "auth_getpwd" " = " "_libc_auth_getpwd" ); |
158 | |
159 | /* |
160 | * Open a new BSD Authentication session with the default service |
161 | * (which can be changed later). |
162 | */ |
163 | auth_session_t * |
164 | auth_open(void) |
165 | { |
166 | auth_session_t *as; |
167 | |
168 | if ((as = calloc(1, sizeof(auth_session_t))) != NULL((void *)0)) { |
169 | as->service = defservice; |
170 | as->fd = -1; |
171 | } |
172 | |
173 | return (as); |
174 | } |
175 | DEF_WEAK(auth_open)__asm__(".weak " "auth_open" " ; " "auth_open" " = " "_libc_auth_open" ); |
176 | |
177 | /* |
178 | * Clean the specified BSD Authentication session. |
179 | */ |
180 | void |
181 | auth_clean(auth_session_t *as) |
182 | { |
183 | struct rmfiles *rm; |
184 | struct authdata *data; |
185 | |
186 | as->state = 0; |
187 | |
188 | auth_clrenv(as); |
189 | |
190 | /* |
191 | * Clean out the rmlist and remove specified files |
192 | */ |
193 | while ((rm = as->rmlist) != NULL((void *)0)) { |
194 | as->rmlist = rm->next; |
195 | unlink(rm->file); |
196 | free(rm); |
197 | } |
198 | |
199 | /* |
200 | * Clean out data |
201 | */ |
202 | while ((data = as->data) != NULL((void *)0)) { |
203 | if (as->data->len) |
204 | explicit_bzero(as->data->ptr, as->data->len); |
205 | as->data = data->next; |
206 | free(data); |
207 | } |
208 | |
209 | auth_setitem(as, AUTHV_ALL, NULL((void *)0)); |
210 | |
211 | if (as->pwd != NULL((void *)0)) { |
212 | explicit_bzero(as->pwd->pw_passwd, strlen(as->pwd->pw_passwd)); |
213 | free(as->pwd); |
214 | as->pwd = NULL((void *)0); |
215 | } |
216 | |
217 | if (as->fd != -1) { |
218 | close(as->fd); |
219 | as->fd = -1; |
220 | } |
221 | } |
222 | DEF_WEAK(auth_clean)__asm__(".weak " "auth_clean" " ; " "auth_clean" " = " "_libc_auth_clean" ); |
223 | |
224 | /* |
225 | * Close the specified BSD Authentication session. |
226 | * Return 0 if not authenticated. |
227 | */ |
228 | int |
229 | auth_close(auth_session_t *as) |
230 | { |
231 | struct rmfiles *rm; |
232 | struct authopts *opt; |
233 | struct authdata *data; |
234 | int s; |
235 | |
236 | /* |
237 | * Save our return value |
238 | */ |
239 | s = as->state & AUTH_ALLOW(0x01 | 0x02 | 0x04); |
240 | |
241 | if (s == 0) |
242 | as->index = 0; |
243 | |
244 | auth_setenv(as); |
245 | |
246 | |
247 | /* |
248 | * Clean out the rmlist and remove specified files if the |
249 | * authentication failed |
250 | */ |
251 | while ((rm = as->rmlist) != NULL((void *)0)) { |
252 | as->rmlist = rm->next; |
253 | if (s == 0) |
254 | unlink(rm->file); |
255 | free(rm); |
256 | } |
257 | |
258 | /* |
259 | * Clean out the opt list |
260 | */ |
261 | while ((opt = as->optlist) != NULL((void *)0)) { |
262 | as->optlist = opt->next; |
263 | free(opt); |
264 | } |
265 | |
266 | /* |
267 | * Clean out data |
268 | */ |
269 | while ((data = as->data) != NULL((void *)0)) { |
270 | if (as->data->len) |
271 | explicit_bzero(as->data->ptr, as->data->len); |
272 | as->data = data->next; |
273 | free(data); |
274 | } |
275 | |
276 | if (as->pwd != NULL((void *)0)) { |
277 | explicit_bzero(as->pwd->pw_passwd, strlen(as->pwd->pw_passwd)); |
278 | free(as->pwd); |
279 | as->pwd = NULL((void *)0); |
280 | } |
281 | |
282 | /* |
283 | * Clean up random variables |
284 | */ |
285 | if (as->service && as->service != defservice) |
286 | free(as->service); |
287 | free(as->challenge); |
288 | free(as->class); |
289 | free(as->style); |
290 | free(as->name); |
291 | |
292 | free(as); |
293 | return (s); |
294 | } |
295 | DEF_WEAK(auth_close)__asm__(".weak " "auth_close" " ; " "auth_close" " = " "_libc_auth_close" ); |
296 | |
297 | /* |
298 | * Request a challenge for the session. |
299 | * The name and style must have already been specified |
300 | */ |
301 | char * |
302 | auth_challenge(auth_session_t *as) |
303 | { |
304 | char path[PATH_MAX1024]; |
305 | int len; |
306 | |
307 | if (as == NULL((void *)0) || as->style == NULL((void *)0) || as->name == NULL((void *)0) || |
308 | !_auth_validuser(as->name)) |
309 | return (NULL((void *)0)); |
310 | |
311 | len = snprintf(path, sizeof(path), _PATH_AUTHPROG"/usr/libexec/auth/login_" "%s", as->style); |
312 | if (len < 0 || len >= sizeof(path)) |
313 | return (NULL((void *)0)); |
314 | |
315 | as->state = 0; |
316 | |
317 | free(as->challenge); |
318 | as->challenge = NULL((void *)0); |
319 | |
320 | auth_call(as, path, as->style, "-s", "challenge", "--", as->name, |
321 | as->class, (char *)NULL((void *)0)); |
322 | if (as->state & AUTH_CHALLENGE0x10) |
323 | as->challenge = auth_getvalue(as, "challenge"); |
324 | as->state = 0; |
325 | as->index = 0; /* toss our data */ |
326 | return (as->challenge); |
327 | } |
328 | DEF_WEAK(auth_challenge)__asm__(".weak " "auth_challenge" " ; " "auth_challenge" " = " "_libc_auth_challenge"); |
329 | |
330 | /* |
331 | * Set/unset the requested environment variables. |
332 | * Mark the variables as set so they will not be set a second time. |
333 | * XXX - should provide a way to detect setenv() failure. |
334 | */ |
335 | void |
336 | auth_setenv(auth_session_t *as) |
337 | { |
338 | char *line, *name; |
339 | |
340 | /* |
341 | * Set any environment variables we were asked for |
342 | */ |
343 | for (line = as->spool; line < as->spool + as->index;) { |
344 | if (!strncasecmp(line, BI_SETENV"setenv", sizeof(BI_SETENV"setenv")-1)) { |
345 | if (isblank((unsigned char)line[sizeof(BI_SETENV"setenv") - 1])) { |
346 | /* only do it once! */ |
347 | line[0] = 'd'; line[1] = 'i'; line[2] = 'd'; |
348 | line += sizeof(BI_SETENV"setenv") - 1; |
349 | for (name = line; |
350 | isblank((unsigned char)*name); ++name) |
351 | ; |
352 | for (line = name; |
353 | *line && !isblank((unsigned char)*line); |
354 | ++line) |
355 | ; |
356 | if (*line) |
357 | *line++ = '\0'; |
358 | for (; isblank((unsigned char)*line); ++line) |
359 | ; |
360 | if (*line != '\0' && setenv(name, line, 1)) |
361 | warn("setenv(%s, %s)", name, line); |
362 | } |
363 | } else |
364 | if (!strncasecmp(line, BI_UNSETENV"unsetenv", sizeof(BI_UNSETENV"unsetenv")-1)) { |
365 | if (isblank((unsigned char)line[sizeof(BI_UNSETENV"unsetenv") - 1])) { |
366 | /* only do it once! */ |
367 | line[2] = 'd'; line[3] = 'i'; line[4] = 'd'; |
368 | line += sizeof(BI_UNSETENV"unsetenv") - 1; |
369 | for (name = line; |
370 | isblank((unsigned char)*name); ++name) |
371 | ; |
372 | for (line = name; |
373 | *line && !isblank((unsigned char)*line); |
374 | ++line) |
375 | ; |
376 | if (*line) |
377 | *line++ = '\0'; |
378 | unsetenv(name); |
379 | } |
380 | } |
381 | while (*line++) |
382 | ; |
383 | } |
384 | } |
385 | DEF_WEAK(auth_setenv)__asm__(".weak " "auth_setenv" " ; " "auth_setenv" " = " "_libc_auth_setenv" ); |
386 | |
387 | /* |
388 | * Clear out any requested environment variables. |
389 | */ |
390 | void |
391 | auth_clrenv(auth_session_t *as) |
392 | { |
393 | char *line; |
394 | |
395 | for (line = as->spool; line < as->spool + as->index;) { |
396 | if (!strncasecmp(line, BI_SETENV"setenv", sizeof(BI_SETENV"setenv")-1)) { |
397 | if (isblank((unsigned char)line[sizeof(BI_SETENV"setenv") - 1])) { |
398 | line[0] = 'i'; line[1] = 'g'; line[2] = 'n'; |
399 | } |
400 | } else |
401 | if (!strncasecmp(line, BI_UNSETENV"unsetenv", sizeof(BI_UNSETENV"unsetenv")-1)) { |
402 | if (isblank((unsigned char)line[sizeof(BI_UNSETENV"unsetenv") - 1])) { |
403 | line[2] = 'i'; line[3] = 'g'; line[4] = 'n'; |
404 | } |
405 | } |
406 | while (*line++) |
407 | ; |
408 | } |
409 | } |
410 | DEF_WEAK(auth_clrenv)__asm__(".weak " "auth_clrenv" " ; " "auth_clrenv" " = " "_libc_auth_clrenv" ); |
411 | |
412 | char * |
413 | auth_getitem(auth_session_t *as, auth_item_t item) |
414 | { |
415 | if (as != NULL((void *)0)) { |
416 | switch (item) { |
417 | case AUTHV_CHALLENGE: |
418 | return (as->challenge); |
419 | case AUTHV_CLASS: |
420 | return (as->class); |
421 | case AUTHV_NAME: |
422 | return (as->name); |
423 | case AUTHV_SERVICE: |
424 | return (as->service ? as->service : defservice); |
425 | case AUTHV_STYLE: |
426 | return (as->style); |
427 | case AUTHV_INTERACTIVE: |
428 | return ((as->flags & AF_INTERACTIVE0x0001) ? "True" : NULL((void *)0)); |
429 | default: |
430 | break; |
431 | } |
432 | } |
433 | return (NULL((void *)0)); |
434 | } |
435 | DEF_WEAK(auth_getitem)__asm__(".weak " "auth_getitem" " ; " "auth_getitem" " = " "_libc_auth_getitem" ); |
436 | |
437 | int |
438 | auth_setitem(auth_session_t *as, auth_item_t item, char *value) |
439 | { |
440 | if (as == NULL((void *)0)) { |
441 | errno(*__errno()) = EINVAL22; |
442 | return (-1); |
443 | } |
444 | |
445 | switch (item) { |
446 | case AUTHV_ALL: |
447 | if (value != NULL((void *)0)) { |
448 | errno(*__errno()) = EINVAL22; |
449 | return (-1); |
450 | } |
451 | auth_setitem(as, AUTHV_CHALLENGE, NULL((void *)0)); |
452 | auth_setitem(as, AUTHV_CLASS, NULL((void *)0)); |
453 | auth_setitem(as, AUTHV_NAME, NULL((void *)0)); |
454 | auth_setitem(as, AUTHV_SERVICE, NULL((void *)0)); |
455 | auth_setitem(as, AUTHV_STYLE, NULL((void *)0)); |
456 | auth_setitem(as, AUTHV_INTERACTIVE, NULL((void *)0)); |
457 | return (0); |
458 | |
459 | case AUTHV_CHALLENGE: |
460 | if (value == as->challenge) |
461 | return (0); |
462 | if (value != NULL((void *)0) && (value = strdup(value)) == NULL((void *)0)) |
463 | return (-1); |
464 | free(as->challenge); |
465 | as->challenge = value; |
466 | return (0); |
467 | |
468 | case AUTHV_CLASS: |
469 | if (value == as->class) |
470 | return (0); |
471 | if (value != NULL((void *)0) && (value = strdup(value)) == NULL((void *)0)) |
472 | return (-1); |
473 | free(as->class); |
474 | as->class = value; |
475 | return (0); |
476 | |
477 | case AUTHV_NAME: |
478 | if (value == as->name) |
479 | return (0); |
480 | if (value != NULL((void *)0) && !_auth_validuser(value)) { |
481 | errno(*__errno()) = EINVAL22; |
482 | return (-1); |
483 | } |
484 | if (value != NULL((void *)0) && (value = strdup(value)) == NULL((void *)0)) |
485 | return (-1); |
486 | free(as->name); |
487 | as->name = value; |
488 | return (0); |
489 | |
490 | case AUTHV_SERVICE: |
491 | if (value == as->service) |
492 | return (0); |
493 | if (value == NULL((void *)0) || strcmp(value, defservice) == 0) |
494 | value = defservice; |
495 | else if ((value = strdup(value)) == NULL((void *)0)) |
496 | return (-1); |
497 | if (as->service && as->service != defservice) |
498 | free(as->service); |
499 | as->service = value; |
500 | return (0); |
501 | |
502 | case AUTHV_STYLE: |
503 | if (value == as->style) |
504 | return (0); |
505 | if (value == NULL((void *)0) || strchr(value, '/') != NULL((void *)0) || |
506 | (value = strdup(value)) == NULL((void *)0)) |
507 | return (-1); |
508 | free(as->style); |
509 | as->style = value; |
510 | return (0); |
511 | |
512 | case AUTHV_INTERACTIVE: |
513 | if (value == NULL((void *)0)) |
514 | as->flags &= ~AF_INTERACTIVE0x0001; |
515 | else |
516 | as->flags |= ~AF_INTERACTIVE0x0001; |
517 | return (0); |
518 | |
519 | default: |
520 | errno(*__errno()) = EINVAL22; |
521 | return (-1); |
522 | } |
523 | } |
524 | DEF_WEAK(auth_setitem)__asm__(".weak " "auth_setitem" " ; " "auth_setitem" " = " "_libc_auth_setitem" ); |
525 | |
526 | int |
527 | auth_setoption(auth_session_t *as, char *n, char *v) |
528 | { |
529 | struct authopts *opt; |
530 | size_t len = strlen(n) + strlen(v) + 2; |
531 | int ret; |
532 | |
533 | if ((opt = malloc(sizeof(*opt) + len)) == NULL((void *)0)) |
534 | return (-1); |
535 | |
536 | opt->opt = (char *)(opt + 1); |
537 | |
538 | ret = snprintf(opt->opt, len, "%s=%s", n, v); |
539 | if (ret < 0 || ret >= len) { |
540 | free(opt); |
541 | errno(*__errno()) = ENAMETOOLONG63; |
542 | return (-1); |
543 | } |
544 | opt->next = as->optlist; |
545 | as->optlist = opt; |
546 | return(0); |
547 | } |
548 | DEF_WEAK(auth_setoption)__asm__(".weak " "auth_setoption" " ; " "auth_setoption" " = " "_libc_auth_setoption"); |
549 | |
550 | void |
551 | auth_clroptions(auth_session_t *as) |
552 | { |
553 | struct authopts *opt; |
554 | |
555 | while ((opt = as->optlist) != NULL((void *)0)) { |
556 | as->optlist = opt->next; |
557 | free(opt); |
558 | } |
559 | } |
560 | DEF_WEAK(auth_clroptions)__asm__(".weak " "auth_clroptions" " ; " "auth_clroptions" " = " "_libc_auth_clroptions"); |
561 | |
562 | void |
563 | auth_clroption(auth_session_t *as, char *option) |
564 | { |
565 | struct authopts *opt, *oopt; |
566 | size_t len; |
567 | |
568 | len = strlen(option); |
569 | |
570 | if ((opt = as->optlist) == NULL((void *)0)) |
571 | return; |
572 | |
573 | if (strncmp(opt->opt, option, len) == 0 && |
574 | (opt->opt[len] == '=' || opt->opt[len] == '\0')) { |
575 | as->optlist = opt->next; |
576 | free(opt); |
577 | return; |
578 | } |
579 | |
580 | while ((oopt = opt->next) != NULL((void *)0)) { |
581 | if (strncmp(oopt->opt, option, len) == 0 && |
582 | (oopt->opt[len] == '=' || oopt->opt[len] == '\0')) { |
583 | opt->next = oopt->next; |
584 | free(oopt); |
585 | return; |
586 | } |
587 | opt = oopt; |
588 | } |
589 | } |
590 | DEF_WEAK(auth_clroption)__asm__(".weak " "auth_clroption" " ; " "auth_clroption" " = " "_libc_auth_clroption"); |
591 | |
592 | int |
593 | auth_setdata(auth_session_t *as, void *ptr, size_t len) |
594 | { |
595 | struct authdata *data, *dp; |
596 | |
597 | if (len <= 0) |
598 | return (0); |
599 | |
600 | if ((data = malloc(sizeof(*data) + len)) == NULL((void *)0)) |
601 | return (-1); |
602 | |
603 | data->next = NULL((void *)0); |
604 | data->len = len; |
605 | data->ptr = data + 1; |
606 | memcpy(data->ptr, ptr, len); |
607 | |
608 | if (as->data == NULL((void *)0)) |
609 | as->data = data; |
610 | else { |
611 | for (dp = as->data; dp->next != NULL((void *)0); dp = dp->next) |
612 | ; |
613 | dp->next = data; |
614 | } |
615 | return (0); |
616 | } |
617 | DEF_WEAK(auth_setdata)__asm__(".weak " "auth_setdata" " ; " "auth_setdata" " = " "_libc_auth_setdata" ); |
618 | |
619 | int |
620 | auth_setpwd(auth_session_t *as, struct passwd *pwd) |
621 | { |
622 | struct passwd pwstore; |
623 | char *instance, pwbuf[_PW_BUF_LEN1024]; |
624 | |
625 | if (pwd == NULL((void *)0) && as->pwd == NULL((void *)0) && as->name == NULL((void *)0)) |
626 | return (-1); /* true failure */ |
627 | |
628 | if (pwd == NULL((void *)0)) { |
629 | /* |
630 | * If we were not passed in a pwd structure we need to |
631 | * go find one for ourself. Always look up the username |
632 | * (if it is defined) in the passwd database to see if there |
633 | * is an entry for the user. If not, either use the current |
634 | * entry or simply return a 1 which implies there is |
635 | * no user by that name here. This is not a failure, just |
636 | * a point of information. |
637 | */ |
638 | if (as->name == NULL((void *)0)) |
639 | return (0); |
640 | getpwnam_r(as->name, &pwstore, pwbuf, sizeof(pwbuf), &pwd); |
641 | if (pwd == NULL((void *)0)) { |
642 | instance = strchr(as->name, '/'); |
643 | if (instance == NULL((void *)0)) |
644 | return (as->pwd ? 0 : 1); |
645 | if (strcmp(instance, "/root") == 0) { |
646 | getpwnam_r(instance + 1, &pwstore, pwbuf, |
647 | sizeof(pwbuf), &pwd); |
648 | } |
649 | if (pwd == NULL((void *)0)) |
650 | return (as->pwd ? 0 : 1); |
651 | } |
652 | } |
653 | if ((pwd = pw_dup(pwd)) == NULL((void *)0)) |
654 | return (-1); /* true failure */ |
655 | if (as->pwd) { |
656 | explicit_bzero(as->pwd->pw_passwd, strlen(as->pwd->pw_passwd)); |
657 | free(as->pwd); |
658 | } |
659 | as->pwd = pwd; |
660 | return (0); |
661 | } |
662 | DEF_WEAK(auth_setpwd)__asm__(".weak " "auth_setpwd" " ; " "auth_setpwd" " = " "_libc_auth_setpwd" ); |
663 | |
664 | char * |
665 | auth_getvalue(auth_session_t *as, char *what) |
666 | { |
667 | char *line, *v, *value; |
668 | int n, len; |
669 | |
670 | len = strlen(what); |
671 | |
672 | for (line = as->spool; line < as->spool + as->index;) { |
673 | if (strncasecmp(line, BI_VALUE"value", sizeof(BI_VALUE"value")-1) != 0) |
674 | goto next; |
675 | line += sizeof(BI_VALUE"value") - 1; |
676 | |
677 | if (!isblank((unsigned char)*line)) |
678 | goto next; |
679 | |
680 | while (isblank((unsigned char)*++line)) |
681 | ; |
682 | |
683 | if (strncmp(line, what, len) != 0 || |
684 | !isblank((unsigned char)line[len])) |
685 | goto next; |
686 | line += len; |
687 | while (isblank((unsigned char)*++line)) |
688 | ; |
689 | value = strdup(line); |
690 | if (value == NULL((void *)0)) |
691 | return (NULL((void *)0)); |
692 | |
693 | /* |
694 | * XXX - There should be a more standardized |
695 | * routine for doing this sort of thing. |
696 | */ |
697 | for (line = v = value; *line; ++line) { |
698 | if (*line == '\\') { |
699 | switch (*++line) { |
700 | case 'r': |
701 | *v++ = '\r'; |
702 | break; |
703 | case 'n': |
704 | *v++ = '\n'; |
705 | break; |
706 | case 't': |
707 | *v++ = '\t'; |
708 | break; |
709 | case '0': case '1': case '2': |
710 | case '3': case '4': case '5': |
711 | case '6': case '7': |
712 | n = *line - '0'; |
713 | if (isdigit((unsigned char)line[1])) { |
714 | ++line; |
715 | n <<= 3; |
716 | n |= *line-'0'; |
717 | } |
718 | if (isdigit((unsigned char)line[1])) { |
719 | ++line; |
720 | n <<= 3; |
721 | n |= *line-'0'; |
Value stored to 'n' is never read | |
722 | } |
723 | break; |
724 | default: |
725 | *v++ = *line; |
726 | break; |
727 | } |
728 | } else |
729 | *v++ = *line; |
730 | } |
731 | *v = '\0'; |
732 | return (value); |
733 | next: |
734 | while (*line++) |
735 | ; |
736 | } |
737 | return (NULL((void *)0)); |
738 | } |
739 | DEF_WEAK(auth_getvalue)__asm__(".weak " "auth_getvalue" " ; " "auth_getvalue" " = " "_libc_auth_getvalue" ); |
740 | |
741 | quad_t |
742 | auth_check_expire(auth_session_t *as) |
743 | { |
744 | if (as->pwd == NULL((void *)0) && auth_setpwd(as, NULL((void *)0)) < 0) { |
745 | as->state &= ~AUTH_ALLOW(0x01 | 0x02 | 0x04); |
746 | as->state |= AUTH_EXPIRED0x20; /* XXX */ |
747 | return (-1); |
748 | } |
749 | |
750 | if (as->pwd == NULL((void *)0)) |
751 | return (0); |
752 | |
753 | if (as->pwd && (quad_t)as->pwd->pw_expire != 0) { |
754 | if (as->now.tv_sec == 0) |
755 | WRAP(gettimeofday)_libc_gettimeofday_wrap(&as->now, NULL((void *)0)); |
756 | if ((quad_t)as->now.tv_sec >= (quad_t)as->pwd->pw_expire) { |
757 | as->state &= ~AUTH_ALLOW(0x01 | 0x02 | 0x04); |
758 | as->state |= AUTH_EXPIRED0x20; |
759 | } |
760 | if ((quad_t)as->now.tv_sec == (quad_t)as->pwd->pw_expire) |
761 | return (-1); |
762 | return ((quad_t)as->pwd->pw_expire - (quad_t)as->now.tv_sec); |
763 | } |
764 | return (0); |
765 | } |
766 | DEF_WEAK(auth_check_expire)__asm__(".weak " "auth_check_expire" " ; " "auth_check_expire" " = " "_libc_auth_check_expire"); |
767 | |
768 | quad_t |
769 | auth_check_change(auth_session_t *as) |
770 | { |
771 | if (as->pwd == NULL((void *)0) && auth_setpwd(as, NULL((void *)0)) < 0) { |
772 | as->state &= ~AUTH_ALLOW(0x01 | 0x02 | 0x04); |
773 | as->state |= AUTH_PWEXPIRED0x40; /* XXX */ |
774 | return (-1); |
775 | } |
776 | |
777 | if (as->pwd == NULL((void *)0)) |
778 | return (0); |
779 | |
780 | if (as->pwd && (quad_t)as->pwd->pw_change) { |
781 | if (as->now.tv_sec == 0) |
782 | WRAP(gettimeofday)_libc_gettimeofday_wrap(&as->now, NULL((void *)0)); |
783 | if (as->now.tv_sec >= (quad_t)as->pwd->pw_change) { |
784 | as->state &= ~AUTH_ALLOW(0x01 | 0x02 | 0x04); |
785 | as->state |= AUTH_PWEXPIRED0x40; |
786 | } |
787 | if ((quad_t)as->now.tv_sec == (quad_t)as->pwd->pw_change) |
788 | return (-1); |
789 | return ((quad_t)as->pwd->pw_change - (quad_t)as->now.tv_sec); |
790 | } |
791 | return (0); |
792 | } |
793 | DEF_WEAK(auth_check_change)__asm__(".weak " "auth_check_change" " ; " "auth_check_change" " = " "_libc_auth_check_change"); |
794 | |
795 | /* |
796 | * The down and dirty call to the login script |
797 | * okay contains the default return value, typically 0 but |
798 | * is AUTH_OKAY for approval like scripts. |
799 | * |
800 | * Internally additional trailing arguments can be read from as->ap |
801 | * Options will be placed just after the first argument (not including path). |
802 | * |
803 | * Any data will be sent to (and freed by) the script |
804 | */ |
805 | int |
806 | auth_call(auth_session_t *as, char *path, ...) |
807 | { |
808 | char *line; |
809 | struct authdata *data; |
810 | struct authopts *opt; |
811 | pid_t pid; |
812 | int status; |
813 | int okay; |
814 | int pfd[2]; |
815 | int argc; |
816 | char *argv[64]; /* 64 args should be more than enough */ |
817 | #define Nargc(sizeof(argv)/sizeof(argv[0])) (sizeof(argv)/sizeof(argv[0])) |
818 | |
819 | va_start(as->ap0, path)__builtin_va_start((as->ap0), path); |
820 | |
821 | argc = 0; |
822 | if ((argv[argc] = _auth_next_arg(as)) != NULL((void *)0)) |
823 | ++argc; |
824 | |
825 | if (as->fd != -1) { |
826 | argv[argc++] = "-v"; |
827 | argv[argc++] = "fd=4"; /* AUTH_FD, see below */ |
828 | } |
829 | /* XXX - fail if out of space in argv */ |
830 | for (opt = as->optlist; opt != NULL((void *)0); opt = opt->next) { |
831 | if (argc < Nargc(sizeof(argv)/sizeof(argv[0])) - 2) { |
832 | argv[argc++] = "-v"; |
833 | argv[argc++] = opt->opt; |
834 | } else { |
835 | syslog(LOG_ERR3, "too many authentication options"); |
836 | goto fail; |
837 | } |
838 | } |
839 | while (argc < Nargc(sizeof(argv)/sizeof(argv[0])) - 1 && (argv[argc] = _auth_next_arg(as))) |
840 | ++argc; |
841 | |
842 | if (argc >= Nargc(sizeof(argv)/sizeof(argv[0])) - 1 && _auth_next_arg(as)) { |
843 | if (memcmp(&nilap, &(as->ap0), sizeof(nilap)) != 0) { |
844 | va_end(as->ap0)__builtin_va_end((as->ap0)); |
845 | explicit_bzero(&(as->ap0), sizeof(as->ap0)); |
846 | } |
847 | if (memcmp(&nilap, &(as->ap), sizeof(nilap)) != 0) { |
848 | va_end(as->ap)__builtin_va_end((as->ap)); |
849 | explicit_bzero(&(as->ap), sizeof(as->ap)); |
850 | } |
851 | syslog(LOG_ERR3, "too many arguments"); |
852 | goto fail; |
853 | } |
854 | |
855 | argv[argc] = NULL((void *)0); |
856 | |
857 | if (socketpair(PF_LOCAL1, SOCK_STREAM1, 0, pfd) == -1) { |
858 | syslog(LOG_ERR3, "unable to create backchannel %m"); |
859 | warnx("internal resource failure"); |
860 | goto fail; |
861 | } |
862 | |
863 | switch (pid = fork()) { |
864 | case -1: |
865 | syslog(LOG_ERR3, "%s: %m", path); |
866 | warnx("internal resource failure"); |
867 | close(pfd[0]); |
868 | close(pfd[1]); |
869 | goto fail; |
870 | case 0: |
871 | #define COMM_FD3 3 |
872 | #define AUTH_FD4 4 |
873 | if (dup2(pfd[1], COMM_FD3) == -1) |
874 | err(1, "dup of backchannel"); |
875 | if (as->fd != -1) { |
876 | if (dup2(as->fd, AUTH_FD4) == -1) |
877 | err(1, "dup of auth fd"); |
878 | closefrom(AUTH_FD4 + 1); |
879 | } else |
880 | closefrom(COMM_FD3 + 1); |
881 | execve(path, argv, auth_environ); |
882 | syslog(LOG_ERR3, "%s: %m", path); |
883 | err(1, "%s", path); |
884 | default: |
885 | close(pfd[1]); |
886 | if (as->fd != -1) { |
887 | close(as->fd); /* so child has only ref */ |
888 | as->fd = -1; |
889 | } |
890 | while ((data = as->data) != NULL((void *)0)) { |
891 | as->data = data->next; |
892 | if (data->len > 0) { |
893 | write(pfd[0], data->ptr, data->len); |
894 | explicit_bzero(data->ptr, data->len); |
895 | } |
896 | free(data); |
897 | } |
898 | as->index = 0; |
899 | _auth_spool(as, pfd[0]); |
900 | close(pfd[0]); |
901 | do { |
902 | if (waitpid(pid, &status, 0) != -1) { |
903 | if (!WIFEXITED(status)(((status) & 0177) == 0)) |
904 | goto fail; |
905 | break; |
906 | } |
907 | /* |
908 | * could get ECHILD if it was waited for by |
909 | * another thread or from a signal handler |
910 | */ |
911 | } while (errno(*__errno()) == EINTR4); |
912 | } |
913 | |
914 | /* |
915 | * Now scan the spooled data |
916 | * It is easier to wait for all the data before starting |
917 | * to scan it. |
918 | */ |
919 | for (line = as->spool; line < as->spool + as->index;) { |
920 | if (!strncasecmp(line, BI_REJECT"reject", sizeof(BI_REJECT"reject")-1)) { |
921 | line += sizeof(BI_REJECT"reject") - 1; |
922 | if (!*line || *line == ' ' || *line == '\t') { |
923 | while (*line == ' ' || *line == '\t') |
924 | ++line; |
925 | if (!strcasecmp(line, "silent")) { |
926 | as->state = AUTH_SILENT0x08; |
927 | break; |
928 | } |
929 | if (!strcasecmp(line, "challenge")) { |
930 | as->state = AUTH_CHALLENGE0x10; |
931 | break; |
932 | } |
933 | if (!strcasecmp(line, "expired")) { |
934 | as->state = AUTH_EXPIRED0x20; |
935 | break; |
936 | } |
937 | if (!strcasecmp(line, "pwexpired")) { |
938 | as->state = AUTH_PWEXPIRED0x40; |
939 | break; |
940 | } |
941 | } |
942 | break; |
943 | } else if (!strncasecmp(line, BI_AUTH"authorize", sizeof(BI_AUTH"authorize")-1)) { |
944 | line += sizeof(BI_AUTH"authorize") - 1; |
945 | if (!*line || *line == ' ' || *line == '\t') { |
946 | while (*line == ' ' || *line == '\t') |
947 | ++line; |
948 | if (*line == '\0') |
949 | as->state |= AUTH_OKAY0x01; |
950 | else if (!strcasecmp(line, "root")) |
951 | as->state |= AUTH_ROOTOKAY0x02; |
952 | else if (!strcasecmp(line, "secure")) |
953 | as->state |= AUTH_SECURE0x04; |
954 | } |
955 | } else if (!strncasecmp(line, BI_REMOVE"remove", sizeof(BI_REMOVE"remove")-1)) { |
956 | line += sizeof(BI_REMOVE"remove") - 1; |
957 | while (*line == ' ' || *line == '\t') |
958 | ++line; |
959 | if (*line) |
960 | _add_rmlist(as, line); |
961 | } |
962 | while (*line++) |
963 | ; |
964 | } |
965 | |
966 | if (WEXITSTATUS(status)(int)(((unsigned)(status) >> 8) & 0xff)) |
967 | as->state &= ~AUTH_ALLOW(0x01 | 0x02 | 0x04); |
968 | |
969 | okay = as->state & AUTH_ALLOW(0x01 | 0x02 | 0x04); |
970 | |
971 | if (!okay) |
972 | auth_clrenv(as); |
973 | |
974 | if (0) { |
975 | fail: |
976 | auth_clrenv(as); |
977 | as->state = 0; |
978 | okay = -1; |
979 | } |
980 | |
981 | while ((data = as->data) != NULL((void *)0)) { |
982 | as->data = data->next; |
983 | free(data); |
984 | } |
985 | |
986 | if (memcmp(&nilap, &(as->ap0), sizeof(nilap)) != 0) { |
987 | va_end(as->ap0)__builtin_va_end((as->ap0)); |
988 | explicit_bzero(&(as->ap0), sizeof(as->ap0)); |
989 | } |
990 | |
991 | if (memcmp(&nilap, &(as->ap), sizeof(nilap)) != 0) { |
992 | va_end(as->ap)__builtin_va_end((as->ap)); |
993 | explicit_bzero(&(as->ap), sizeof(as->ap)); |
994 | } |
995 | return (okay); |
996 | } |
997 | DEF_WEAK(auth_call)__asm__(".weak " "auth_call" " ; " "auth_call" " = " "_libc_auth_call" ); |
998 | |
999 | static void |
1000 | _recv_fd(auth_session_t *as, int fd) |
1001 | { |
1002 | struct msghdr msg; |
1003 | struct cmsghdr *cmp; |
1004 | union { |
1005 | struct cmsghdr hdr; |
1006 | char buf[CMSG_SPACE(sizeof(int))((((unsigned long)(sizeof(struct cmsghdr)) + (sizeof(long) - 1 )) &~(sizeof(long) - 1)) + (((unsigned long)(sizeof(int)) + (sizeof(long) - 1)) &~(sizeof(long) - 1)))]; |
1007 | } cmsgbuf; |
1008 | |
1009 | memset(&msg, 0, sizeof(msg)); |
1010 | msg.msg_control = &cmsgbuf.buf; |
1011 | msg.msg_controllen = sizeof(cmsgbuf.buf); |
1012 | if (recvmsg(fd, &msg, 0) == -1) |
1013 | syslog(LOG_ERR3, "recvmsg: %m"); |
1014 | else if (msg.msg_flags & MSG_TRUNC0x10) |
1015 | syslog(LOG_ERR3, "message truncated"); |
1016 | else if (msg.msg_flags & MSG_CTRUNC0x20) |
1017 | syslog(LOG_ERR3, "control message truncated"); |
1018 | else if ((cmp = CMSG_FIRSTHDR(&msg)((&msg)->msg_controllen >= sizeof(struct cmsghdr) ? (struct cmsghdr *)(&msg)->msg_control : (struct cmsghdr *)((void *)0))) == NULL((void *)0)) |
1019 | syslog(LOG_ERR3, "missing control message"); |
1020 | else { |
1021 | if (cmp->cmsg_level != SOL_SOCKET0xffff) |
1022 | syslog(LOG_ERR3, "unexpected cmsg_level %d", |
1023 | cmp->cmsg_level); |
1024 | else if (cmp->cmsg_type != SCM_RIGHTS0x01) |
1025 | syslog(LOG_ERR3, "unexpected cmsg_type %d", |
1026 | cmp->cmsg_type); |
1027 | else if (cmp->cmsg_len != CMSG_LEN(sizeof(int))((((unsigned long)(sizeof(struct cmsghdr)) + (sizeof(long) - 1 )) &~(sizeof(long) - 1)) + (sizeof(int)))) |
1028 | syslog(LOG_ERR3, "bad cmsg_len %d", |
1029 | cmp->cmsg_len); |
1030 | else { |
1031 | if (as->fd != -1) |
1032 | close(as->fd); |
1033 | as->fd = *(int *)CMSG_DATA(cmp)((unsigned char *)(cmp) + (((unsigned long)(sizeof(struct cmsghdr )) + (sizeof(long) - 1)) &~(sizeof(long) - 1))); |
1034 | } |
1035 | } |
1036 | } |
1037 | |
1038 | static void |
1039 | _auth_spool(auth_session_t *as, int fd) |
1040 | { |
1041 | ssize_t r; |
1042 | char *b, *s; |
1043 | |
1044 | for (s = as->spool + as->index; as->index < sizeof(as->spool) - 1; ) { |
1045 | r = read(fd, as->spool + as->index, |
1046 | sizeof(as->spool) - as->index); |
1047 | if (r <= 0) { |
1048 | as->spool[as->index] = '\0'; |
1049 | return; |
1050 | } |
1051 | b = as->spool + as->index; |
1052 | as->index += r; |
1053 | /* |
1054 | * Convert newlines into NULs to allow easy scanning of the |
1055 | * file and receive an fd if there is a BI_FDPASS message. |
1056 | * XXX - checking for BI_FDPASS here is annoying but |
1057 | * we need to avoid the read() slurping in control data. |
1058 | */ |
1059 | while (r-- > 0) { |
1060 | if (*b++ == '\n') { |
1061 | b[-1] = '\0'; |
1062 | if (strcasecmp(s, BI_FDPASS"fd") == 0) |
1063 | _recv_fd(as, fd); |
1064 | s = b; |
1065 | } |
1066 | } |
1067 | } |
1068 | |
1069 | syslog(LOG_ERR3, "Overflowed backchannel spool buffer"); |
1070 | errx(1, "System error in authentication program"); |
1071 | } |
1072 | |
1073 | static void |
1074 | _add_rmlist(auth_session_t *as, char *file) |
1075 | { |
1076 | struct rmfiles *rm; |
1077 | size_t i = strlen(file) + 1; |
1078 | |
1079 | // XXX should rangecheck i since we are about to add? |
1080 | |
1081 | if ((rm = malloc(sizeof(struct rmfiles) + i)) == NULL((void *)0)) { |
1082 | syslog(LOG_ERR3, "Failed to allocate rmfiles: %m"); |
1083 | return; |
1084 | } |
1085 | rm->file = (char *)(rm + 1); |
1086 | rm->next = as->rmlist; |
1087 | strlcpy(rm->file, file, i); |
1088 | as->rmlist = rm; |
1089 | } |
1090 | |
1091 | static char * |
1092 | _auth_next_arg(auth_session_t *as) |
1093 | { |
1094 | char *arg; |
1095 | |
1096 | if (memcmp(&nilap, &(as->ap0), sizeof(nilap)) != 0) { |
1097 | if ((arg = va_arg(as->ap0, char *)__builtin_va_arg((as->ap0), char *)) != NULL((void *)0)) |
1098 | return (arg); |
1099 | va_end(as->ap0)__builtin_va_end((as->ap0)); |
1100 | explicit_bzero(&(as->ap0), sizeof(as->ap0)); |
1101 | } |
1102 | if (memcmp(&nilap, &(as->ap), sizeof(nilap)) != 0) { |
1103 | if ((arg = va_arg(as->ap, char *)__builtin_va_arg((as->ap), char *)) != NULL((void *)0)) |
1104 | return (arg); |
1105 | va_end(as->ap)__builtin_va_end((as->ap)); |
1106 | explicit_bzero(&(as->ap), sizeof(as->ap)); |
1107 | } |
1108 | return (NULL((void *)0)); |
1109 | } |