GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: lib/libc/gen/auth_subr.c Lines: 0 485 0.0 %
Date: 2017-11-13 Branches: 0 418 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: auth_subr.c,v 1.50 2015/12/28 22:08:18 mmcc Exp $	*/
2
3
/*
4
 * Copyright (c) 2000-2002,2004 Todd C. Miller <Todd.Miller@courtesan.com>
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.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)	/* 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];	/* 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_INTERACTIVE		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 *auth_environ[] = {
138
	"PATH=" _PATH_DEFPATH,
139
	"SHELL=" _PATH_BSHELL,
140
	NULL,
141
};
142
143
static char defservice[] = LOGIN_DEFSERVICE;
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); }
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);
155
DEF_WEAK(auth_set_va_list);
156
DEF_WEAK(auth_getstate);
157
DEF_WEAK(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) {
169
		as->service = defservice;
170
		as->fd = -1;
171
	}
172
173
	return (as);
174
}
175
DEF_WEAK(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) {
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) {
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);
210
211
	if (as->pwd != NULL) {
212
		explicit_bzero(as->pwd->pw_passwd, strlen(as->pwd->pw_passwd));
213
		free(as->pwd);
214
		as->pwd = NULL;
215
	}
216
217
	if (as->fd != -1) {
218
		close(as->fd);
219
		as->fd = -1;
220
	}
221
}
222
DEF_WEAK(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;
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) {
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) {
262
		as->optlist = opt->next;
263
		free(opt);
264
	}
265
266
	/*
267
	 * Clean out data
268
	 */
269
	while ((data = as->data) != NULL) {
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) {
277
		explicit_bzero(as->pwd->pw_passwd, strlen(as->pwd->pw_passwd));
278
		free(as->pwd);
279
		as->pwd = NULL;
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);
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_MAX];
305
	int len;
306
307
	if (as == NULL || as->style == NULL || as->name == NULL)
308
		return (NULL);
309
310
	len = snprintf(path, sizeof(path), _PATH_AUTHPROG "%s", as->style);
311
	if (len < 0 || len >= sizeof(path))
312
		return (NULL);
313
314
	as->state = 0;
315
316
	free(as->challenge);
317
	as->challenge = NULL;
318
319
	auth_call(as, path, as->style, "-s", "challenge", as->name,
320
	    as->class, (char *)NULL);
321
	if (as->state & AUTH_CHALLENGE)
322
		as->challenge = auth_getvalue(as, "challenge");
323
	as->state = 0;
324
	as->index = 0;	/* toss our data */
325
	return (as->challenge);
326
}
327
DEF_WEAK(auth_challenge);
328
329
/*
330
 * Set/unset the requested environment variables.
331
 * Mark the variables as set so they will not be set a second time.
332
 * XXX - should provide a way to detect setenv() failure.
333
 */
334
void
335
auth_setenv(auth_session_t *as)
336
{
337
	char *line, *name;
338
339
	/*
340
	 * Set any environment variables we were asked for
341
	 */
342
    	for (line = as->spool; line < as->spool + as->index;) {
343
		if (!strncasecmp(line, BI_SETENV, sizeof(BI_SETENV)-1)) {
344
			if (isblank((unsigned char)line[sizeof(BI_SETENV) - 1])) {
345
				/* only do it once! */
346
				line[0] = 'd'; line[1] = 'i'; line[2] = 'd';
347
				line += sizeof(BI_SETENV) - 1;
348
				for (name = line;
349
				    isblank((unsigned char)*name); ++name)
350
					;
351
				for (line = name;
352
				    *line && !isblank((unsigned char)*line);
353
				    ++line)
354
					;
355
				if (*line)
356
					*line++ = '\0';
357
				for (; isblank((unsigned char)*line); ++line)
358
					;
359
				if (*line != '\0' && setenv(name, line, 1))
360
					warn("setenv(%s, %s)", name, line);
361
			}
362
		} else
363
		if (!strncasecmp(line, BI_UNSETENV, sizeof(BI_UNSETENV)-1)) {
364
			if (isblank((unsigned char)line[sizeof(BI_UNSETENV) - 1])) {
365
				/* only do it once! */
366
				line[2] = 'd'; line[3] = 'i'; line[4] = 'd';
367
				line += sizeof(BI_UNSETENV) - 1;
368
				for (name = line;
369
				    isblank((unsigned char)*name); ++name)
370
					;
371
				for (line = name;
372
				    *line && !isblank((unsigned char)*line);
373
				    ++line)
374
					;
375
				if (*line)
376
					*line++ = '\0';
377
				unsetenv(name);
378
			}
379
		}
380
		while (*line++)
381
			;
382
	}
383
}
384
DEF_WEAK(auth_setenv);
385
386
/*
387
 * Clear out any requested environment variables.
388
 */
389
void
390
auth_clrenv(auth_session_t *as)
391
{
392
	char *line;
393
394
	for (line = as->spool; line < as->spool + as->index;) {
395
		if (!strncasecmp(line, BI_SETENV, sizeof(BI_SETENV)-1)) {
396
			if (isblank((unsigned char)line[sizeof(BI_SETENV) - 1])) {
397
				line[0] = 'i'; line[1] = 'g'; line[2] = 'n';
398
			}
399
		} else
400
		if (!strncasecmp(line, BI_UNSETENV, sizeof(BI_UNSETENV)-1)) {
401
			if (isblank((unsigned char)line[sizeof(BI_UNSETENV) - 1])) {
402
				line[2] = 'i'; line[3] = 'g'; line[4] = 'n';
403
			}
404
		}
405
		while (*line++)
406
			;
407
	}
408
}
409
DEF_WEAK(auth_clrenv);
410
411
char *
412
auth_getitem(auth_session_t *as, auth_item_t item)
413
{
414
	if (as != NULL) {
415
		switch (item) {
416
		case AUTHV_CHALLENGE:
417
			return (as->challenge);
418
		case AUTHV_CLASS:
419
			return (as->class);
420
		case AUTHV_NAME:
421
			return (as->name);
422
		case AUTHV_SERVICE:
423
			return (as->service ? as->service : defservice);
424
		case AUTHV_STYLE:
425
			return (as->style);
426
		case AUTHV_INTERACTIVE:
427
			return ((as->flags & AF_INTERACTIVE) ? "True" : NULL);
428
		default:
429
			break;
430
		}
431
	}
432
	return (NULL);
433
}
434
DEF_WEAK(auth_getitem);
435
436
int
437
auth_setitem(auth_session_t *as, auth_item_t item, char *value)
438
{
439
	if (as == NULL) {
440
		errno = EINVAL;
441
		return (-1);
442
	}
443
444
	switch (item) {
445
	case AUTHV_ALL:
446
		if (value != NULL) {
447
			errno = EINVAL;
448
			return (-1);
449
		}
450
		auth_setitem(as, AUTHV_CHALLENGE, NULL);
451
		auth_setitem(as, AUTHV_CLASS, NULL);
452
		auth_setitem(as, AUTHV_NAME, NULL);
453
		auth_setitem(as, AUTHV_SERVICE, NULL);
454
		auth_setitem(as, AUTHV_STYLE, NULL);
455
		auth_setitem(as, AUTHV_INTERACTIVE, NULL);
456
		return (0);
457
458
	case AUTHV_CHALLENGE:
459
		if (value == as->challenge)
460
			return (0);
461
		if (value != NULL && (value = strdup(value)) == NULL)
462
			return (-1);
463
		free(as->challenge);
464
		as->challenge = value;
465
		return (0);
466
467
	case AUTHV_CLASS:
468
		if (value == as->class)
469
			return (0);
470
		if (value != NULL && (value = strdup(value)) == NULL)
471
			return (-1);
472
		free(as->class);
473
		as->class = value;
474
		return (0);
475
476
	case AUTHV_NAME:
477
		if (value == as->name)
478
			return (0);
479
		if (value != NULL && (value = strdup(value)) == NULL)
480
			return (-1);
481
		free(as->name);
482
		as->name = value;
483
		return (0);
484
485
	case AUTHV_SERVICE:
486
		if (value == as->service)
487
			return (0);
488
		if (value == NULL || strcmp(value, defservice) == 0)
489
			value = defservice;
490
		else if ((value = strdup(value)) == NULL)
491
			return (-1);
492
		if (as->service && as->service != defservice)
493
			free(as->service);
494
		as->service = value;
495
		return (0);
496
497
	case AUTHV_STYLE:
498
		if (value == as->style)
499
			return (0);
500
		if (value == NULL || strchr(value, '/') != NULL ||
501
		    (value = strdup(value)) == NULL)
502
			return (-1);
503
		free(as->style);
504
		as->style = value;
505
		return (0);
506
507
	case AUTHV_INTERACTIVE:
508
		if (value == NULL)
509
			as->flags &= ~AF_INTERACTIVE;
510
		else
511
			as->flags |= ~AF_INTERACTIVE;
512
		return (0);
513
514
	default:
515
		errno = EINVAL;
516
		return (-1);
517
	}
518
}
519
DEF_WEAK(auth_setitem);
520
521
int
522
auth_setoption(auth_session_t *as, char *n, char *v)
523
{
524
	struct authopts *opt;
525
	size_t len = strlen(n) + strlen(v) + 2;
526
	int ret;
527
528
	if ((opt = malloc(sizeof(*opt) + len)) == NULL)
529
		return (-1);
530
531
	opt->opt = (char *)(opt + 1);
532
533
	ret = snprintf(opt->opt, len, "%s=%s", n, v);
534
	if (ret < 0 || ret >= len) {
535
		free(opt);
536
		errno = ENAMETOOLONG;
537
		return (-1);
538
	}
539
	opt->next = as->optlist;
540
	as->optlist = opt;
541
	return(0);
542
}
543
DEF_WEAK(auth_setoption);
544
545
void
546
auth_clroptions(auth_session_t *as)
547
{
548
	struct authopts *opt;
549
550
	while ((opt = as->optlist) != NULL) {
551
		as->optlist = opt->next;
552
		free(opt);
553
	}
554
}
555
DEF_WEAK(auth_clroptions);
556
557
void
558
auth_clroption(auth_session_t *as, char *option)
559
{
560
	struct authopts *opt, *oopt;
561
	size_t len;
562
563
	len = strlen(option);
564
565
	if ((opt = as->optlist) == NULL)
566
		return;
567
568
	if (strncmp(opt->opt, option, len) == 0 &&
569
	    (opt->opt[len] == '=' || opt->opt[len] == '\0')) {
570
		as->optlist = opt->next;
571
		free(opt);
572
		return;
573
	}
574
575
	while ((oopt = opt->next) != NULL) {
576
		if (strncmp(oopt->opt, option, len) == 0 &&
577
		    (oopt->opt[len] == '=' || oopt->opt[len] == '\0')) {
578
			opt->next = oopt->next;
579
			free(oopt);
580
			return;
581
		}
582
		opt = oopt;
583
	}
584
}
585
DEF_WEAK(auth_clroption);
586
587
int
588
auth_setdata(auth_session_t *as, void *ptr, size_t len)
589
{
590
	struct authdata *data, *dp;
591
592
	if (len <= 0)
593
		return (0);
594
595
	if ((data = malloc(sizeof(*data) + len)) == NULL)
596
		return (-1);
597
598
	data->next = NULL;
599
	data->len = len;
600
	data->ptr = data + 1;
601
	memcpy(data->ptr, ptr, len);
602
603
	if (as->data == NULL)
604
		as->data = data;
605
	else {
606
		for (dp = as->data; dp->next != NULL; dp = dp->next)
607
			;
608
		dp->next = data;
609
	}
610
	return (0);
611
}
612
DEF_WEAK(auth_setdata);
613
614
int
615
auth_setpwd(auth_session_t *as, struct passwd *pwd)
616
{
617
	struct passwd pwstore;
618
	char *instance, pwbuf[_PW_BUF_LEN];
619
620
	if (pwd == NULL && as->pwd == NULL && as->name == NULL)
621
		return (-1);		/* true failure */
622
623
	if (pwd == NULL) {
624
		/*
625
		 * If we were not passed in a pwd structure we need to
626
		 * go find one for ourself.  Always look up the username
627
		 * (if it is defined) in the passwd database to see if there
628
		 * is an entry for the user.  If not, either use the current
629
		 * entry or simply return a 1 which implies there is
630
		 * no user by that name here.  This is not a failure, just
631
		 * a point of information.
632
		 */
633
		if (as->name == NULL)
634
			return (0);
635
		getpwnam_r(as->name, &pwstore, pwbuf, sizeof(pwbuf), &pwd);
636
		if (pwd == NULL) {
637
			instance = strchr(as->name, '/');
638
			if (instance == NULL)
639
				return (as->pwd ? 0 : 1);
640
			if (strcmp(instance, "/root") == 0) {
641
				getpwnam_r(instance + 1, &pwstore, pwbuf,
642
				    sizeof(pwbuf), &pwd);
643
			}
644
			if (pwd == NULL)
645
				return (as->pwd ? 0 : 1);
646
		}
647
	}
648
	if ((pwd = pw_dup(pwd)) == NULL)
649
		return (-1);		/* true failure */
650
	if (as->pwd) {
651
		explicit_bzero(as->pwd->pw_passwd, strlen(as->pwd->pw_passwd));
652
		free(as->pwd);
653
	}
654
	as->pwd = pwd;
655
	return (0);
656
}
657
DEF_WEAK(auth_setpwd);
658
659
char *
660
auth_getvalue(auth_session_t *as, char *what)
661
{
662
	char *line, *v, *value;
663
	int n, len;
664
665
	len = strlen(what);
666
667
    	for (line = as->spool; line < as->spool + as->index;) {
668
		if (strncasecmp(line, BI_VALUE, sizeof(BI_VALUE)-1) != 0)
669
			goto next;
670
		line += sizeof(BI_VALUE) - 1;
671
672
		if (!isblank((unsigned char)*line))
673
			goto next;
674
675
		while (isblank((unsigned char)*++line))
676
			;
677
678
		if (strncmp(line, what, len) != 0 ||
679
		    !isblank((unsigned char)line[len]))
680
			goto next;
681
		line += len;
682
		while (isblank((unsigned char)*++line))
683
			;
684
		value = strdup(line);
685
		if (value == NULL)
686
			return (NULL);
687
688
		/*
689
		 * XXX - There should be a more standardized
690
		 * routine for doing this sort of thing.
691
		 */
692
		for (line = v = value; *line; ++line) {
693
			if (*line == '\\') {
694
				switch (*++line) {
695
				case 'r':
696
					*v++ = '\r';
697
					break;
698
				case 'n':
699
					*v++ = '\n';
700
					break;
701
				case 't':
702
					*v++ = '\t';
703
					break;
704
				case '0': case '1': case '2':
705
				case '3': case '4': case '5':
706
				case '6': case '7':
707
					n = *line - '0';
708
					if (isdigit((unsigned char)line[1])) {
709
						++line;
710
						n <<= 3;
711
						n |= *line-'0';
712
					}
713
					if (isdigit((unsigned char)line[1])) {
714
						++line;
715
						n <<= 3;
716
						n |= *line-'0';
717
					}
718
					break;
719
				default:
720
					*v++ = *line;
721
					break;
722
				}
723
			} else
724
				*v++ = *line;
725
		}
726
		*v = '\0';
727
		return (value);
728
next:
729
		while (*line++)
730
			;
731
	}
732
	return (NULL);
733
}
734
DEF_WEAK(auth_getvalue);
735
736
quad_t
737
auth_check_expire(auth_session_t *as)
738
{
739
	if (as->pwd == NULL && auth_setpwd(as, NULL) < 0) {
740
		as->state &= ~AUTH_ALLOW;
741
		as->state |= AUTH_EXPIRED;	/* XXX */
742
		return (-1);
743
	}
744
745
	if (as->pwd == NULL)
746
		return (0);
747
748
	if (as->pwd && (quad_t)as->pwd->pw_expire != 0) {
749
		if (as->now.tv_sec == 0)
750
			gettimeofday(&as->now, NULL);
751
		if ((quad_t)as->now.tv_sec >= (quad_t)as->pwd->pw_expire) {
752
			as->state &= ~AUTH_ALLOW;
753
			as->state |= AUTH_EXPIRED;
754
		}
755
		if ((quad_t)as->now.tv_sec == (quad_t)as->pwd->pw_expire)
756
			return (-1);
757
		return ((quad_t)as->pwd->pw_expire - (quad_t)as->now.tv_sec);
758
	}
759
	return (0);
760
}
761
DEF_WEAK(auth_check_expire);
762
763
quad_t
764
auth_check_change(auth_session_t *as)
765
{
766
	if (as->pwd == NULL && auth_setpwd(as, NULL) < 0) {
767
		as->state &= ~AUTH_ALLOW;
768
		as->state |= AUTH_PWEXPIRED;	/* XXX */
769
		return (-1);
770
	}
771
772
	if (as->pwd == NULL)
773
		return (0);
774
775
	if (as->pwd && (quad_t)as->pwd->pw_change) {
776
		if (as->now.tv_sec == 0)
777
			gettimeofday(&as->now, NULL);
778
		if (as->now.tv_sec >= (quad_t)as->pwd->pw_change) {
779
			as->state &= ~AUTH_ALLOW;
780
			as->state |= AUTH_PWEXPIRED;
781
		}
782
		if ((quad_t)as->now.tv_sec == (quad_t)as->pwd->pw_change)
783
			return (-1);
784
		return ((quad_t)as->pwd->pw_change - (quad_t)as->now.tv_sec);
785
	}
786
	return (0);
787
}
788
DEF_WEAK(auth_check_change);
789
790
/*
791
 * The down and dirty call to the login script
792
 * okay contains the default return value, typically 0 but
793
 * is AUTH_OKAY for approval like scripts.
794
 *
795
 * Internally additional trailing arguments can be read from as->ap
796
 * Options will be placed just after the first argument (not including path).
797
 *
798
 * Any data will be sent to (and freed by) the script
799
 */
800
int
801
auth_call(auth_session_t *as, char *path, ...)
802
{
803
	char *line;
804
	struct authdata *data;
805
	struct authopts *opt;
806
	pid_t pid;
807
	int status;
808
	int okay;
809
	int pfd[2];
810
	int argc;
811
	char *argv[64];		/* 64 args should be more than enough */
812
#define	Nargc	(sizeof(argv)/sizeof(argv[0]))
813
814
	va_start(as->ap0, path);
815
816
	argc = 0;
817
	if ((argv[argc] = _auth_next_arg(as)) != NULL)
818
		++argc;
819
820
	if (as->fd != -1) {
821
		argv[argc++] = "-v";
822
		argv[argc++] = "fd=4";		/* AUTH_FD, see below */
823
	}
824
	for (opt = as->optlist; opt != NULL; opt = opt->next) {
825
		if (argc < Nargc - 2) {
826
			argv[argc++] = "-v";
827
			argv[argc++] = opt->opt;
828
		} else {
829
			syslog(LOG_ERR, "too many authentication options");
830
			goto fail;
831
		}
832
	}
833
	while (argc < Nargc - 1 && (argv[argc] = _auth_next_arg(as)))
834
		++argc;
835
836
	if (argc >= Nargc - 1 && _auth_next_arg(as)) {
837
		if (memcmp(&nilap, &(as->ap0), sizeof(nilap)) != 0) {
838
			va_end(as->ap0);
839
			explicit_bzero(&(as->ap0), sizeof(as->ap0));
840
		}
841
		if (memcmp(&nilap, &(as->ap), sizeof(nilap)) != 0) {
842
			va_end(as->ap);
843
			explicit_bzero(&(as->ap), sizeof(as->ap));
844
		}
845
		syslog(LOG_ERR, "too many arguments");
846
		goto fail;
847
	}
848
849
	argv[argc] = NULL;
850
851
	if (secure_path(path) < 0) {
852
		syslog(LOG_ERR, "%s: path not secure", path);
853
		warnx("invalid script: %s", path);
854
		goto fail;
855
	}
856
857
	if (socketpair(PF_LOCAL, SOCK_STREAM, 0, pfd) < 0) {
858
		syslog(LOG_ERR, "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_ERR, "%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_FD	3
872
#define	AUTH_FD	4
873
		if (dup2(pfd[1], COMM_FD) < 0)
874
			err(1, "dup of backchannel");
875
		if (as->fd != -1) {
876
			if (dup2(as->fd, AUTH_FD) < 0)
877
				err(1, "dup of auth fd");
878
			closefrom(AUTH_FD + 1);
879
		} else
880
			closefrom(COMM_FD + 1);
881
		execve(path, argv, auth_environ);
882
		syslog(LOG_ERR, "%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) {
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))
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 == EINTR);
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, sizeof(BI_REJECT)-1)) {
921
			line += sizeof(BI_REJECT) - 1;
922
			if (!*line || *line == ' ' || *line == '\t') {
923
				while (*line == ' ' || *line == '\t')
924
					++line;
925
				if (!strcasecmp(line, "silent")) {
926
					as->state = AUTH_SILENT;
927
					break;
928
				}
929
				if (!strcasecmp(line, "challenge")) {
930
					as->state  = AUTH_CHALLENGE;
931
					break;
932
				}
933
				if (!strcasecmp(line, "expired")) {
934
					as->state  = AUTH_EXPIRED;
935
					break;
936
				}
937
				if (!strcasecmp(line, "pwexpired")) {
938
					as->state  = AUTH_PWEXPIRED;
939
					break;
940
				}
941
			}
942
			break;
943
		} else if (!strncasecmp(line, BI_AUTH, sizeof(BI_AUTH)-1)) {
944
			line += sizeof(BI_AUTH) - 1;
945
			if (!*line || *line == ' ' || *line == '\t') {
946
				while (*line == ' ' || *line == '\t')
947
					++line;
948
				if (*line == '\0')
949
					as->state |= AUTH_OKAY;
950
				else if (!strcasecmp(line, "root"))
951
					as->state |= AUTH_ROOTOKAY;
952
				else if (!strcasecmp(line, "secure"))
953
					as->state |= AUTH_SECURE;
954
			}
955
		} else if (!strncasecmp(line, BI_REMOVE, sizeof(BI_REMOVE)-1)) {
956
			line += sizeof(BI_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))
967
		as->state &= ~AUTH_ALLOW;
968
969
	okay = as->state & AUTH_ALLOW;
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) {
982
		as->data = data->next;
983
		free(data);
984
	}
985
986
	if (memcmp(&nilap, &(as->ap0), sizeof(nilap)) != 0) {
987
		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);
993
		explicit_bzero(&(as->ap), sizeof(as->ap));
994
	}
995
	return (okay);
996
}
997
DEF_WEAK(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))];
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) < 0)
1013
		syslog(LOG_ERR, "recvmsg: %m");
1014
	else if (msg.msg_flags & MSG_TRUNC)
1015
		syslog(LOG_ERR, "message truncated");
1016
	else if (msg.msg_flags & MSG_CTRUNC)
1017
		syslog(LOG_ERR, "control message truncated");
1018
	else if ((cmp = CMSG_FIRSTHDR(&msg)) == NULL)
1019
		syslog(LOG_ERR, "missing control message");
1020
	else {
1021
		if (cmp->cmsg_level != SOL_SOCKET)
1022
			syslog(LOG_ERR, "unexpected cmsg_level %d",
1023
			    cmp->cmsg_level);
1024
		else if (cmp->cmsg_type != SCM_RIGHTS)
1025
			syslog(LOG_ERR, "unexpected cmsg_type %d",
1026
			    cmp->cmsg_type);
1027
		else if (cmp->cmsg_len != CMSG_LEN(sizeof(int)))
1028
			syslog(LOG_ERR, "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);
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) == 0)
1063
					_recv_fd(as, fd);
1064
				s = b;
1065
			}
1066
		}
1067
	}
1068
1069
	syslog(LOG_ERR, "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) {
1082
		syslog(LOG_ERR, "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 *)) != NULL)
1098
			return (arg);
1099
		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 *)) != NULL)
1104
			return (arg);
1105
		va_end(as->ap);
1106
		explicit_bzero(&(as->ap), sizeof(as->ap));
1107
	}
1108
	return (NULL);
1109
}