xref: /openbsd-src/lib/libc/gen/auth_subr.c (revision 036a3c97db0a437193eb7e0c8716ea40cb440a8b)
1*036a3c97Sguenther /*	$OpenBSD: auth_subr.c,v 1.56 2020/10/13 04:42:28 guenther Exp $	*/
2e802aa69Smillert 
39cb2850aSmillert /*
4bf198cc6Smillert  * Copyright (c) 2000-2002,2004 Todd C. Miller <millert@openbsd.org>
59cb2850aSmillert  *
69cb2850aSmillert  * Permission to use, copy, modify, and distribute this software for any
79cb2850aSmillert  * purpose with or without fee is hereby granted, provided that the above
89cb2850aSmillert  * copyright notice and this permission notice appear in all copies.
99cb2850aSmillert  *
109cb2850aSmillert  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
119cb2850aSmillert  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
129cb2850aSmillert  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
139cb2850aSmillert  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
149cb2850aSmillert  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
159cb2850aSmillert  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
169cb2850aSmillert  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
179cb2850aSmillert  */
18e802aa69Smillert /*-
19e802aa69Smillert  * Copyright (c) 1995,1996,1997 Berkeley Software Design, Inc.
20e802aa69Smillert  * All rights reserved.
21e802aa69Smillert  *
22e802aa69Smillert  * Redistribution and use in source and binary forms, with or without
23e802aa69Smillert  * modification, are permitted provided that the following conditions
24e802aa69Smillert  * are met:
25e802aa69Smillert  * 1. Redistributions of source code must retain the above copyright
26e802aa69Smillert  *    notice, this list of conditions and the following disclaimer.
27e802aa69Smillert  * 2. Redistributions in binary form must reproduce the above copyright
28e802aa69Smillert  *    notice, this list of conditions and the following disclaimer in the
29e802aa69Smillert  *    documentation and/or other materials provided with the distribution.
30e802aa69Smillert  * 3. All advertising materials mentioning features or use of this software
31e802aa69Smillert  *    must display the following acknowledgement:
32e802aa69Smillert  *	This product includes software developed by Berkeley Software Design,
33e802aa69Smillert  *	Inc.
34e802aa69Smillert  * 4. The name of Berkeley Software Design, Inc.  may not be used to endorse
35e802aa69Smillert  *    or promote products derived from this software without specific prior
36e802aa69Smillert  *    written permission.
37e802aa69Smillert  *
38e802aa69Smillert  * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``AS IS'' AND
39e802aa69Smillert  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
40e802aa69Smillert  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
41e802aa69Smillert  * ARE DISCLAIMED.  IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN, INC. BE LIABLE
42e802aa69Smillert  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
43e802aa69Smillert  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
44e802aa69Smillert  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
45e802aa69Smillert  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
46e802aa69Smillert  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
47e802aa69Smillert  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48e802aa69Smillert  * SUCH DAMAGE.
49e802aa69Smillert  *
50e802aa69Smillert  *	BSDI $From: auth_subr.c,v 2.4 1999/09/08 04:10:40 prb Exp $
51e802aa69Smillert  */
5269245ebdSmillert 
53e802aa69Smillert #include <sys/time.h>
54e802aa69Smillert #include <sys/resource.h>
55e802aa69Smillert #include <sys/socket.h>
56e802aa69Smillert #include <sys/wait.h>
57e802aa69Smillert 
58e802aa69Smillert #include <ctype.h>
59e802aa69Smillert #include <err.h>
60e802aa69Smillert #include <errno.h>
61e802aa69Smillert #include <fcntl.h>
6269245ebdSmillert #include <limits.h>
63e802aa69Smillert #include <paths.h>
64e802aa69Smillert #include <pwd.h>
65e802aa69Smillert #include <stdarg.h>
66e802aa69Smillert #include <stdio.h>
67e802aa69Smillert #include <stdlib.h>
68e802aa69Smillert #include <string.h>
69e802aa69Smillert #include <syslog.h>
70e802aa69Smillert #include <unistd.h>
71e802aa69Smillert 
72e802aa69Smillert #include <login_cap.h>
73e802aa69Smillert 
74e802aa69Smillert #define	MAXSPOOLSIZE	(8*1024)	/* Spool up to 8K of back info */
75e802aa69Smillert 
76e802aa69Smillert struct rmfiles {
77e802aa69Smillert 	struct rmfiles	*next;
78e802aa69Smillert 	char		*file;
79e802aa69Smillert };
80e802aa69Smillert 
81e802aa69Smillert struct authopts {
82e802aa69Smillert 	struct authopts	*next;
83e802aa69Smillert 	char		*opt;
84e802aa69Smillert };
85e802aa69Smillert 
86e802aa69Smillert struct authdata {
87e802aa69Smillert 	struct	authdata *next;
88e802aa69Smillert 	void	*ptr;
89e802aa69Smillert 	size_t	len;
90e802aa69Smillert };
91e802aa69Smillert 
92e802aa69Smillert struct auth_session_t {
93e802aa69Smillert 	char	*name;			/* name of use being authenticated */
94e802aa69Smillert 	char	*style;			/* style of authentication used */
95e802aa69Smillert 	char	*class;			/* class of user */
96e802aa69Smillert 	char	*service;		/* type of service being performed */
97e802aa69Smillert 	char	*challenge;		/* last challenge issued */
98e802aa69Smillert 	int	flags;			/* see below */
99e802aa69Smillert 	struct	passwd *pwd;		/* password entry for user */
100e802aa69Smillert 	struct	timeval now;		/* time of authentication */
101e802aa69Smillert 
102e802aa69Smillert 	int	state;			/* authenticated state */
103e802aa69Smillert 
104e802aa69Smillert 	struct	rmfiles *rmlist;	/* list of files to remove on failure */
105e802aa69Smillert 	struct	authopts *optlist;	/* list of options to scripts */
106e802aa69Smillert 	struct	authdata *data;		/* additional data to send to scripts */
107e802aa69Smillert 
108e802aa69Smillert 	char	spool[MAXSPOOLSIZE];	/* data returned from login script */
109e802aa69Smillert 	int	index;			/* how much returned thus far */
110e802aa69Smillert 
1116e2de6bbSmillert 	int	fd;			/* connection to authenticator */
1126e2de6bbSmillert 
113e802aa69Smillert 	va_list	ap0;			/* argument list to auth_call */
114e802aa69Smillert 	va_list	ap;			/* additional arguments to auth_call */
115e802aa69Smillert };
116e802aa69Smillert 
117e802aa69Smillert /*
118e802aa69Smillert  * Internal flags
119e802aa69Smillert  */
120e802aa69Smillert #define	AF_INTERACTIVE		0x0001	/* This is an interactive session */
121e802aa69Smillert 
122e802aa69Smillert /*
123e802aa69Smillert  * We cannot include bsd_auth.h until we define the above structures
124e802aa69Smillert  */
125e802aa69Smillert #include <bsd_auth.h>
126e802aa69Smillert 
127e802aa69Smillert /*
128e802aa69Smillert  * Internally used functions
129e802aa69Smillert  */
130e802aa69Smillert static void _add_rmlist(auth_session_t *, char *);
131e802aa69Smillert static void _auth_spool(auth_session_t *, int);
1326e2de6bbSmillert static void _recv_fd(auth_session_t *, int);
133e802aa69Smillert static char *_auth_next_arg(auth_session_t *);
134e802aa69Smillert /*
135e802aa69Smillert  * Set up a known environment for all authentication scripts.
136e802aa69Smillert  */
137*036a3c97Sguenther static char * const auth_environ[] = {
138e802aa69Smillert 	"PATH=" _PATH_DEFPATH,
139e802aa69Smillert 	"SHELL=" _PATH_BSHELL,
140e802aa69Smillert 	NULL,
141e802aa69Smillert };
142e802aa69Smillert 
143e802aa69Smillert static char defservice[] = LOGIN_DEFSERVICE;
144e802aa69Smillert 
1454808f726Smillert static va_list nilap;
1464808f726Smillert 
147e802aa69Smillert /*
148e802aa69Smillert  * Quick one liners that only exist to keep auth_session_t opaque
149e802aa69Smillert  */
auth_setstate(auth_session_t * as,int s)150e802aa69Smillert void	auth_setstate(auth_session_t *as, int s){ as->state = s; }
auth_set_va_list(auth_session_t * as,va_list ap)151c942b0a5Sdaniel void	auth_set_va_list(auth_session_t *as, va_list ap) { va_copy(as->ap, ap); }
auth_getstate(auth_session_t * as)152e802aa69Smillert int	auth_getstate(auth_session_t *as)	{ return (as->state); }
auth_getpwd(auth_session_t * as)153e802aa69Smillert struct passwd *auth_getpwd(auth_session_t *as)	{ return (as->pwd); }
154753e8795Sguenther DEF_WEAK(auth_setstate);
155753e8795Sguenther DEF_WEAK(auth_set_va_list);
156753e8795Sguenther DEF_WEAK(auth_getstate);
157753e8795Sguenther DEF_WEAK(auth_getpwd);
158e802aa69Smillert 
159e802aa69Smillert /*
160e802aa69Smillert  * Open a new BSD Authentication session with the default service
161e802aa69Smillert  * (which can be changed later).
162e802aa69Smillert  */
163e802aa69Smillert auth_session_t *
auth_open(void)164f723aa39Sderaadt auth_open(void)
165e802aa69Smillert {
166e802aa69Smillert 	auth_session_t *as;
167e802aa69Smillert 
168c7421165Stedu 	if ((as = calloc(1, sizeof(auth_session_t))) != NULL) {
169e802aa69Smillert 		as->service = defservice;
1706e2de6bbSmillert 		as->fd = -1;
171e802aa69Smillert 	}
172e802aa69Smillert 
173e802aa69Smillert 	return (as);
174e802aa69Smillert }
175753e8795Sguenther DEF_WEAK(auth_open);
176e802aa69Smillert 
177e802aa69Smillert /*
178e802aa69Smillert  * Clean the specified BSD Authentication session.
179e802aa69Smillert  */
180e802aa69Smillert void
auth_clean(auth_session_t * as)181e802aa69Smillert auth_clean(auth_session_t *as)
182e802aa69Smillert {
183e802aa69Smillert 	struct rmfiles *rm;
184e802aa69Smillert 	struct authdata *data;
185e802aa69Smillert 
186e802aa69Smillert 	as->state = 0;
187e802aa69Smillert 
188e802aa69Smillert 	auth_clrenv(as);
189e802aa69Smillert 
190e802aa69Smillert 	/*
191e802aa69Smillert 	 * Clean out the rmlist and remove specified files
192e802aa69Smillert 	 */
193e802aa69Smillert 	while ((rm = as->rmlist) != NULL) {
194e802aa69Smillert 		as->rmlist = rm->next;
195e802aa69Smillert 		unlink(rm->file);
196e802aa69Smillert 		free(rm);
197e802aa69Smillert 	}
198e802aa69Smillert 
199e802aa69Smillert 	/*
200e802aa69Smillert 	 * Clean out data
201e802aa69Smillert 	 */
202e802aa69Smillert 	while ((data = as->data) != NULL) {
203e802aa69Smillert 		if (as->data->len)
2048fbd7fcbSdoug 			explicit_bzero(as->data->ptr, as->data->len);
205e802aa69Smillert 		as->data = data->next;
206e802aa69Smillert 		free(data);
207e802aa69Smillert 	}
208e802aa69Smillert 
209e802aa69Smillert 	auth_setitem(as, AUTHV_ALL, NULL);
210e802aa69Smillert 
211e802aa69Smillert 	if (as->pwd != NULL) {
2128fbd7fcbSdoug 		explicit_bzero(as->pwd->pw_passwd, strlen(as->pwd->pw_passwd));
213e802aa69Smillert 		free(as->pwd);
214e802aa69Smillert 		as->pwd = NULL;
215e802aa69Smillert 	}
2166e2de6bbSmillert 
2176e2de6bbSmillert 	if (as->fd != -1) {
2186e2de6bbSmillert 		close(as->fd);
2196e2de6bbSmillert 		as->fd = -1;
2206e2de6bbSmillert 	}
221e802aa69Smillert }
222753e8795Sguenther DEF_WEAK(auth_clean);
223e802aa69Smillert 
224e802aa69Smillert /*
225e802aa69Smillert  * Close the specified BSD Authentication session.
226e802aa69Smillert  * Return 0 if not authenticated.
227e802aa69Smillert  */
228e802aa69Smillert int
auth_close(auth_session_t * as)229e802aa69Smillert auth_close(auth_session_t *as)
230e802aa69Smillert {
231e802aa69Smillert 	struct rmfiles *rm;
232e802aa69Smillert 	struct authopts *opt;
233e802aa69Smillert 	struct authdata *data;
234e802aa69Smillert 	int s;
235e802aa69Smillert 
236e802aa69Smillert 	/*
237e802aa69Smillert 	 * Save our return value
238e802aa69Smillert 	 */
239e802aa69Smillert 	s = as->state & AUTH_ALLOW;
240e802aa69Smillert 
241e802aa69Smillert 	if (s == 0)
242e802aa69Smillert 		as->index = 0;
243e802aa69Smillert 
244e802aa69Smillert 	auth_setenv(as);
245e802aa69Smillert 
246e802aa69Smillert 
247e802aa69Smillert 	/*
248e802aa69Smillert 	 * Clean out the rmlist and remove specified files if the
249e802aa69Smillert 	 * authentication failed
250e802aa69Smillert 	 */
251e802aa69Smillert 	while ((rm = as->rmlist) != NULL) {
252e802aa69Smillert 		as->rmlist = rm->next;
253e802aa69Smillert 		if (s == 0)
254e802aa69Smillert 			unlink(rm->file);
255e802aa69Smillert 		free(rm);
256e802aa69Smillert 	}
257e802aa69Smillert 
258e802aa69Smillert 	/*
259e802aa69Smillert 	 * Clean out the opt list
260e802aa69Smillert 	 */
261e802aa69Smillert 	while ((opt = as->optlist) != NULL) {
262e802aa69Smillert 		as->optlist = opt->next;
263e802aa69Smillert 		free(opt);
264e802aa69Smillert 	}
265e802aa69Smillert 
266e802aa69Smillert 	/*
267e802aa69Smillert 	 * Clean out data
268e802aa69Smillert 	 */
269e802aa69Smillert 	while ((data = as->data) != NULL) {
270e802aa69Smillert 		if (as->data->len)
2718fbd7fcbSdoug 			explicit_bzero(as->data->ptr, as->data->len);
272e802aa69Smillert 		as->data = data->next;
273e802aa69Smillert 		free(data);
274e802aa69Smillert 	}
275e802aa69Smillert 
2762877ca5fSmpech 	if (as->pwd != NULL) {
2778fbd7fcbSdoug 		explicit_bzero(as->pwd->pw_passwd, strlen(as->pwd->pw_passwd));
2782877ca5fSmpech 		free(as->pwd);
2792877ca5fSmpech 		as->pwd = NULL;
2802877ca5fSmpech 	}
2812877ca5fSmpech 
282e802aa69Smillert 	/*
283e802aa69Smillert 	 * Clean up random variables
284e802aa69Smillert 	 */
285e802aa69Smillert 	if (as->service && as->service != defservice)
286e802aa69Smillert 		free(as->service);
287e802aa69Smillert 	free(as->challenge);
288e802aa69Smillert 	free(as->class);
289e802aa69Smillert 	free(as->style);
290e802aa69Smillert 	free(as->name);
291e802aa69Smillert 
292e802aa69Smillert 	free(as);
293e802aa69Smillert 	return (s);
294e802aa69Smillert }
295753e8795Sguenther DEF_WEAK(auth_close);
296e802aa69Smillert 
297e802aa69Smillert /*
298c0141d96Sderaadt  * Request a challenge for the session.
299e802aa69Smillert  * The name and style must have already been specified
300e802aa69Smillert  */
301e802aa69Smillert char *
auth_challenge(auth_session_t * as)302e802aa69Smillert auth_challenge(auth_session_t *as)
303e802aa69Smillert {
30469245ebdSmillert 	char path[PATH_MAX];
305bb14a393Smoritz 	int len;
306e802aa69Smillert 
307a314d10bSderaadt 	if (as == NULL || as->style == NULL || as->name == NULL ||
308a314d10bSderaadt 	    !_auth_validuser(as->name))
309e802aa69Smillert 		return (NULL);
310e802aa69Smillert 
311bb14a393Smoritz 	len = snprintf(path, sizeof(path), _PATH_AUTHPROG "%s", as->style);
312bb14a393Smoritz 	if (len < 0 || len >= sizeof(path))
313bb14a393Smoritz 		return (NULL);
314bb14a393Smoritz 
3157ed757dfSmillert 	as->state = 0;
3167ed757dfSmillert 
317e802aa69Smillert 	free(as->challenge);
318e802aa69Smillert 	as->challenge = NULL;
319e802aa69Smillert 
320a314d10bSderaadt 	auth_call(as, path, as->style, "-s", "challenge", "--", as->name,
32102f55c3dSmillert 	    as->class, (char *)NULL);
322e802aa69Smillert 	if (as->state & AUTH_CHALLENGE)
323e802aa69Smillert 		as->challenge = auth_getvalue(as, "challenge");
324e802aa69Smillert 	as->state = 0;
325e802aa69Smillert 	as->index = 0;	/* toss our data */
326e802aa69Smillert 	return (as->challenge);
327e802aa69Smillert }
328753e8795Sguenther DEF_WEAK(auth_challenge);
329e802aa69Smillert 
330e802aa69Smillert /*
331e802aa69Smillert  * Set/unset the requested environment variables.
332e802aa69Smillert  * Mark the variables as set so they will not be set a second time.
333e802aa69Smillert  * XXX - should provide a way to detect setenv() failure.
334e802aa69Smillert  */
335e802aa69Smillert void
auth_setenv(auth_session_t * as)336e802aa69Smillert auth_setenv(auth_session_t *as)
337e802aa69Smillert {
338e802aa69Smillert 	char *line, *name;
339e802aa69Smillert 
340e802aa69Smillert 	/*
341e802aa69Smillert 	 * Set any environment variables we were asked for
342e802aa69Smillert 	 */
343e802aa69Smillert     	for (line = as->spool; line < as->spool + as->index;) {
344e802aa69Smillert 		if (!strncasecmp(line, BI_SETENV, sizeof(BI_SETENV)-1)) {
345dfe5467eSderaadt 			if (isblank((unsigned char)line[sizeof(BI_SETENV) - 1])) {
346e802aa69Smillert 				/* only do it once! */
347e802aa69Smillert 				line[0] = 'd'; line[1] = 'i'; line[2] = 'd';
348e802aa69Smillert 				line += sizeof(BI_SETENV) - 1;
349dfe5467eSderaadt 				for (name = line;
350dfe5467eSderaadt 				    isblank((unsigned char)*name); ++name)
351e802aa69Smillert 					;
352dfe5467eSderaadt 				for (line = name;
353dfe5467eSderaadt 				    *line && !isblank((unsigned char)*line);
354e802aa69Smillert 				    ++line)
355e802aa69Smillert 					;
356e802aa69Smillert 				if (*line)
357e802aa69Smillert 					*line++ = '\0';
358dfe5467eSderaadt 				for (; isblank((unsigned char)*line); ++line)
359e802aa69Smillert 					;
360e802aa69Smillert 				if (*line != '\0' && setenv(name, line, 1))
3619b9d2a55Sguenther 					warn("setenv(%s, %s)", name, line);
362e802aa69Smillert 			}
363e802aa69Smillert 		} else
364e802aa69Smillert 		if (!strncasecmp(line, BI_UNSETENV, sizeof(BI_UNSETENV)-1)) {
365fb6201afSmmcc 			if (isblank((unsigned char)line[sizeof(BI_UNSETENV) - 1])) {
366e802aa69Smillert 				/* only do it once! */
367e802aa69Smillert 				line[2] = 'd'; line[3] = 'i'; line[4] = 'd';
368e802aa69Smillert 				line += sizeof(BI_UNSETENV) - 1;
369dfe5467eSderaadt 				for (name = line;
370dfe5467eSderaadt 				    isblank((unsigned char)*name); ++name)
371e802aa69Smillert 					;
372dfe5467eSderaadt 				for (line = name;
373dfe5467eSderaadt 				    *line && !isblank((unsigned char)*line);
374e802aa69Smillert 				    ++line)
375e802aa69Smillert 					;
376e802aa69Smillert 				if (*line)
377e802aa69Smillert 					*line++ = '\0';
378e802aa69Smillert 				unsetenv(name);
379e802aa69Smillert 			}
380e802aa69Smillert 		}
381e802aa69Smillert 		while (*line++)
382e802aa69Smillert 			;
383e802aa69Smillert 	}
384e802aa69Smillert }
385753e8795Sguenther DEF_WEAK(auth_setenv);
386e802aa69Smillert 
387e802aa69Smillert /*
388e802aa69Smillert  * Clear out any requested environment variables.
389e802aa69Smillert  */
390e802aa69Smillert void
auth_clrenv(auth_session_t * as)391e802aa69Smillert auth_clrenv(auth_session_t *as)
392e802aa69Smillert {
393e802aa69Smillert 	char *line;
394e802aa69Smillert 
395e802aa69Smillert 	for (line = as->spool; line < as->spool + as->index;) {
396e802aa69Smillert 		if (!strncasecmp(line, BI_SETENV, sizeof(BI_SETENV)-1)) {
397dfe5467eSderaadt 			if (isblank((unsigned char)line[sizeof(BI_SETENV) - 1])) {
398e802aa69Smillert 				line[0] = 'i'; line[1] = 'g'; line[2] = 'n';
399e802aa69Smillert 			}
400e802aa69Smillert 		} else
401e802aa69Smillert 		if (!strncasecmp(line, BI_UNSETENV, sizeof(BI_UNSETENV)-1)) {
402dfe5467eSderaadt 			if (isblank((unsigned char)line[sizeof(BI_UNSETENV) - 1])) {
403e802aa69Smillert 				line[2] = 'i'; line[3] = 'g'; line[4] = 'n';
404e802aa69Smillert 			}
405e802aa69Smillert 		}
406e802aa69Smillert 		while (*line++)
407e802aa69Smillert 			;
408e802aa69Smillert 	}
409e802aa69Smillert }
410753e8795Sguenther DEF_WEAK(auth_clrenv);
411e802aa69Smillert 
412e802aa69Smillert char *
auth_getitem(auth_session_t * as,auth_item_t item)413e802aa69Smillert auth_getitem(auth_session_t *as, auth_item_t item)
414e802aa69Smillert {
415e802aa69Smillert 	if (as != NULL) {
416e802aa69Smillert 		switch (item) {
417e802aa69Smillert 		case AUTHV_CHALLENGE:
418e802aa69Smillert 			return (as->challenge);
419e802aa69Smillert 		case AUTHV_CLASS:
420e802aa69Smillert 			return (as->class);
421e802aa69Smillert 		case AUTHV_NAME:
422e802aa69Smillert 			return (as->name);
423e802aa69Smillert 		case AUTHV_SERVICE:
424e802aa69Smillert 			return (as->service ? as->service : defservice);
425e802aa69Smillert 		case AUTHV_STYLE:
426e802aa69Smillert 			return (as->style);
427e802aa69Smillert 		case AUTHV_INTERACTIVE:
428e802aa69Smillert 			return ((as->flags & AF_INTERACTIVE) ? "True" : NULL);
429e802aa69Smillert 		default:
430e802aa69Smillert 			break;
431e802aa69Smillert 		}
432e802aa69Smillert 	}
433e802aa69Smillert 	return (NULL);
434e802aa69Smillert }
435753e8795Sguenther DEF_WEAK(auth_getitem);
436e802aa69Smillert 
437e802aa69Smillert int
auth_setitem(auth_session_t * as,auth_item_t item,char * value)438e802aa69Smillert auth_setitem(auth_session_t *as, auth_item_t item, char *value)
439e802aa69Smillert {
440e802aa69Smillert 	if (as == NULL) {
441e802aa69Smillert 		errno = EINVAL;
442e802aa69Smillert 		return (-1);
443e802aa69Smillert 	}
444e802aa69Smillert 
445e802aa69Smillert 	switch (item) {
446e802aa69Smillert 	case AUTHV_ALL:
447e802aa69Smillert 		if (value != NULL) {
448e802aa69Smillert 			errno = EINVAL;
449e802aa69Smillert 			return (-1);
450e802aa69Smillert 		}
451e802aa69Smillert 		auth_setitem(as, AUTHV_CHALLENGE, NULL);
452e802aa69Smillert 		auth_setitem(as, AUTHV_CLASS, NULL);
453e802aa69Smillert 		auth_setitem(as, AUTHV_NAME, NULL);
454e802aa69Smillert 		auth_setitem(as, AUTHV_SERVICE, NULL);
455e802aa69Smillert 		auth_setitem(as, AUTHV_STYLE, NULL);
456e802aa69Smillert 		auth_setitem(as, AUTHV_INTERACTIVE, NULL);
457e802aa69Smillert 		return (0);
458e802aa69Smillert 
459e802aa69Smillert 	case AUTHV_CHALLENGE:
46008d1e6e6Smillert 		if (value == as->challenge)
461f4f98523Smillert 			return (0);
462e802aa69Smillert 		if (value != NULL && (value = strdup(value)) == NULL)
463e802aa69Smillert 			return (-1);
464e802aa69Smillert 		free(as->challenge);
465e802aa69Smillert 		as->challenge = value;
466e802aa69Smillert 		return (0);
467e802aa69Smillert 
468e802aa69Smillert 	case AUTHV_CLASS:
46908d1e6e6Smillert 		if (value == as->class)
470f4f98523Smillert 			return (0);
471e802aa69Smillert 		if (value != NULL && (value = strdup(value)) == NULL)
472e802aa69Smillert 			return (-1);
473e802aa69Smillert 		free(as->class);
474e802aa69Smillert 		as->class = value;
475e802aa69Smillert 		return (0);
476e802aa69Smillert 
477e802aa69Smillert 	case AUTHV_NAME:
47808d1e6e6Smillert 		if (value == as->name)
479f4f98523Smillert 			return (0);
480a314d10bSderaadt 		if (value != NULL && !_auth_validuser(value)) {
481a314d10bSderaadt 			errno = EINVAL;
482a314d10bSderaadt 			return (-1);
483a314d10bSderaadt 		}
484e802aa69Smillert 		if (value != NULL && (value = strdup(value)) == NULL)
485e802aa69Smillert 			return (-1);
486e802aa69Smillert 		free(as->name);
487e802aa69Smillert 		as->name = value;
488e802aa69Smillert 		return (0);
489e802aa69Smillert 
490e802aa69Smillert 	case AUTHV_SERVICE:
49108d1e6e6Smillert 		if (value == as->service)
492f4f98523Smillert 			return (0);
493e802aa69Smillert 		if (value == NULL || strcmp(value, defservice) == 0)
494e802aa69Smillert 			value = defservice;
495e802aa69Smillert 		else if ((value = strdup(value)) == NULL)
496e802aa69Smillert 			return (-1);
497e802aa69Smillert 		if (as->service && as->service != defservice)
498e802aa69Smillert 			free(as->service);
499e802aa69Smillert 		as->service = value;
500e802aa69Smillert 		return (0);
501e802aa69Smillert 
502e802aa69Smillert 	case AUTHV_STYLE:
50308d1e6e6Smillert 		if (value == as->style)
504f4f98523Smillert 			return (0);
505e802aa69Smillert 		if (value == NULL || strchr(value, '/') != NULL ||
506e802aa69Smillert 		    (value = strdup(value)) == NULL)
507e802aa69Smillert 			return (-1);
508e802aa69Smillert 		free(as->style);
509e802aa69Smillert 		as->style = value;
510e802aa69Smillert 		return (0);
511e802aa69Smillert 
512e802aa69Smillert 	case AUTHV_INTERACTIVE:
513e802aa69Smillert 		if (value == NULL)
514e802aa69Smillert 			as->flags &= ~AF_INTERACTIVE;
515e802aa69Smillert 		else
516e802aa69Smillert 			as->flags |= ~AF_INTERACTIVE;
517e802aa69Smillert 		return (0);
518e802aa69Smillert 
519e802aa69Smillert 	default:
520e802aa69Smillert 		errno = EINVAL;
521e802aa69Smillert 		return (-1);
522e802aa69Smillert 	}
523e802aa69Smillert }
524753e8795Sguenther DEF_WEAK(auth_setitem);
525e802aa69Smillert 
526e802aa69Smillert int
auth_setoption(auth_session_t * as,char * n,char * v)527e802aa69Smillert auth_setoption(auth_session_t *as, char *n, char *v)
528e802aa69Smillert {
529e802aa69Smillert 	struct authopts *opt;
530bb14a393Smoritz 	size_t len = strlen(n) + strlen(v) + 2;
531bb14a393Smoritz 	int ret;
532e802aa69Smillert 
533bb14a393Smoritz 	if ((opt = malloc(sizeof(*opt) + len)) == NULL)
534e802aa69Smillert 		return (-1);
535e802aa69Smillert 
536e802aa69Smillert 	opt->opt = (char *)(opt + 1);
537e802aa69Smillert 
538bb14a393Smoritz 	ret = snprintf(opt->opt, len, "%s=%s", n, v);
539bb14a393Smoritz 	if (ret < 0 || ret >= len) {
540bb14a393Smoritz 		free(opt);
541bb14a393Smoritz 		errno = ENAMETOOLONG;
542bb14a393Smoritz 		return (-1);
543bb14a393Smoritz 	}
544e802aa69Smillert 	opt->next = as->optlist;
545e802aa69Smillert 	as->optlist = opt;
546e802aa69Smillert 	return(0);
547e802aa69Smillert }
548753e8795Sguenther DEF_WEAK(auth_setoption);
549e802aa69Smillert 
550e802aa69Smillert void
auth_clroptions(auth_session_t * as)551e802aa69Smillert auth_clroptions(auth_session_t *as)
552e802aa69Smillert {
553e802aa69Smillert 	struct authopts *opt;
554e802aa69Smillert 
555e802aa69Smillert 	while ((opt = as->optlist) != NULL) {
556e802aa69Smillert 		as->optlist = opt->next;
557e802aa69Smillert 		free(opt);
558e802aa69Smillert 	}
559e802aa69Smillert }
560753e8795Sguenther DEF_WEAK(auth_clroptions);
561e802aa69Smillert 
562e802aa69Smillert void
auth_clroption(auth_session_t * as,char * option)563e802aa69Smillert auth_clroption(auth_session_t *as, char *option)
564e802aa69Smillert {
565e802aa69Smillert 	struct authopts *opt, *oopt;
566e17b29d3Sderaadt 	size_t len;
567e802aa69Smillert 
568e802aa69Smillert 	len = strlen(option);
569e802aa69Smillert 
570e802aa69Smillert 	if ((opt = as->optlist) == NULL)
571e802aa69Smillert 		return;
572e802aa69Smillert 
573e802aa69Smillert 	if (strncmp(opt->opt, option, len) == 0 &&
574e802aa69Smillert 	    (opt->opt[len] == '=' || opt->opt[len] == '\0')) {
575e802aa69Smillert 		as->optlist = opt->next;
576e802aa69Smillert 		free(opt);
577e802aa69Smillert 		return;
578e802aa69Smillert 	}
579e802aa69Smillert 
580e802aa69Smillert 	while ((oopt = opt->next) != NULL) {
581e802aa69Smillert 		if (strncmp(oopt->opt, option, len) == 0 &&
582e802aa69Smillert 		    (oopt->opt[len] == '=' || oopt->opt[len] == '\0')) {
583e802aa69Smillert 			opt->next = oopt->next;
584e802aa69Smillert 			free(oopt);
585e802aa69Smillert 			return;
586e802aa69Smillert 		}
587e802aa69Smillert 		opt = oopt;
588e802aa69Smillert 	}
589e802aa69Smillert }
590753e8795Sguenther DEF_WEAK(auth_clroption);
591e802aa69Smillert 
592e802aa69Smillert int
auth_setdata(auth_session_t * as,void * ptr,size_t len)593e802aa69Smillert auth_setdata(auth_session_t *as, void *ptr, size_t len)
594e802aa69Smillert {
595e802aa69Smillert 	struct authdata *data, *dp;
596e802aa69Smillert 
597e802aa69Smillert 	if (len <= 0)
598e802aa69Smillert 		return (0);
599e802aa69Smillert 
600e802aa69Smillert 	if ((data = malloc(sizeof(*data) + len)) == NULL)
601e802aa69Smillert 		return (-1);
602e802aa69Smillert 
603e802aa69Smillert 	data->next = NULL;
604e802aa69Smillert 	data->len = len;
605e802aa69Smillert 	data->ptr = data + 1;
606e802aa69Smillert 	memcpy(data->ptr, ptr, len);
607e802aa69Smillert 
608e802aa69Smillert 	if (as->data == NULL)
609e802aa69Smillert 		as->data = data;
610e802aa69Smillert 	else {
611e802aa69Smillert 		for (dp = as->data; dp->next != NULL; dp = dp->next)
612e802aa69Smillert 			;
613e802aa69Smillert 		dp->next = data;
614e802aa69Smillert 	}
615e802aa69Smillert 	return (0);
616e802aa69Smillert }
617753e8795Sguenther DEF_WEAK(auth_setdata);
618e802aa69Smillert 
619e802aa69Smillert int
auth_setpwd(auth_session_t * as,struct passwd * pwd)620e802aa69Smillert auth_setpwd(auth_session_t *as, struct passwd *pwd)
621e802aa69Smillert {
622cd245bcaSmillert 	struct passwd pwstore;
623cd245bcaSmillert 	char *instance, pwbuf[_PW_BUF_LEN];
624e802aa69Smillert 
625e802aa69Smillert 	if (pwd == NULL && as->pwd == NULL && as->name == NULL)
626e802aa69Smillert 		return (-1);		/* true failure */
627e802aa69Smillert 
628e802aa69Smillert 	if (pwd == NULL) {
629e802aa69Smillert 		/*
630e802aa69Smillert 		 * If we were not passed in a pwd structure we need to
631e802aa69Smillert 		 * go find one for ourself.  Always look up the username
632e802aa69Smillert 		 * (if it is defined) in the passwd database to see if there
633e802aa69Smillert 		 * is an entry for the user.  If not, either use the current
634e802aa69Smillert 		 * entry or simply return a 1 which implies there is
635e802aa69Smillert 		 * no user by that name here.  This is not a failure, just
636e802aa69Smillert 		 * a point of information.
637e802aa69Smillert 		 */
638e802aa69Smillert 		if (as->name == NULL)
639e802aa69Smillert 			return (0);
640cd245bcaSmillert 		getpwnam_r(as->name, &pwstore, pwbuf, sizeof(pwbuf), &pwd);
641cd245bcaSmillert 		if (pwd == NULL) {
642fa843023Smillert 			instance = strchr(as->name, '/');
643fa843023Smillert 			if (instance == NULL)
644e802aa69Smillert 				return (as->pwd ? 0 : 1);
645cd245bcaSmillert 			if (strcmp(instance, "/root") == 0) {
646cd245bcaSmillert 				getpwnam_r(instance + 1, &pwstore, pwbuf,
647cd245bcaSmillert 				    sizeof(pwbuf), &pwd);
648cd245bcaSmillert 			}
649e802aa69Smillert 			if (pwd == NULL)
650e802aa69Smillert 				return (as->pwd ? 0 : 1);
651e802aa69Smillert 		}
652e802aa69Smillert 	}
653e802aa69Smillert 	if ((pwd = pw_dup(pwd)) == NULL)
654e802aa69Smillert 		return (-1);		/* true failure */
655efca3e07Smillert 	if (as->pwd) {
6568fbd7fcbSdoug 		explicit_bzero(as->pwd->pw_passwd, strlen(as->pwd->pw_passwd));
657e802aa69Smillert 		free(as->pwd);
658efca3e07Smillert 	}
659e802aa69Smillert 	as->pwd = pwd;
660e802aa69Smillert 	return (0);
661e802aa69Smillert }
662753e8795Sguenther DEF_WEAK(auth_setpwd);
663e802aa69Smillert 
664e802aa69Smillert char *
auth_getvalue(auth_session_t * as,char * what)665e802aa69Smillert auth_getvalue(auth_session_t *as, char *what)
666e802aa69Smillert {
667e802aa69Smillert 	char *line, *v, *value;
668e802aa69Smillert 	int n, len;
669e802aa69Smillert 
670e802aa69Smillert 	len = strlen(what);
671e802aa69Smillert 
672e802aa69Smillert     	for (line = as->spool; line < as->spool + as->index;) {
673e802aa69Smillert 		if (strncasecmp(line, BI_VALUE, sizeof(BI_VALUE)-1) != 0)
674e802aa69Smillert 			goto next;
675e802aa69Smillert 		line += sizeof(BI_VALUE) - 1;
676e802aa69Smillert 
677dfe5467eSderaadt 		if (!isblank((unsigned char)*line))
678e802aa69Smillert 			goto next;
679e802aa69Smillert 
680dfe5467eSderaadt 		while (isblank((unsigned char)*++line))
681e802aa69Smillert 			;
682e802aa69Smillert 
683e802aa69Smillert 		if (strncmp(line, what, len) != 0 ||
684dfe5467eSderaadt 		    !isblank((unsigned char)line[len]))
685e802aa69Smillert 			goto next;
686e802aa69Smillert 		line += len;
687dfe5467eSderaadt 		while (isblank((unsigned char)*++line))
688e802aa69Smillert 			;
689e802aa69Smillert 		value = strdup(line);
690e802aa69Smillert 		if (value == NULL)
691e802aa69Smillert 			return (NULL);
692e802aa69Smillert 
693e802aa69Smillert 		/*
694e802aa69Smillert 		 * XXX - There should be a more standardized
695e802aa69Smillert 		 * routine for doing this sort of thing.
696e802aa69Smillert 		 */
697e802aa69Smillert 		for (line = v = value; *line; ++line) {
698e802aa69Smillert 			if (*line == '\\') {
699e802aa69Smillert 				switch (*++line) {
700e802aa69Smillert 				case 'r':
701e802aa69Smillert 					*v++ = '\r';
702e802aa69Smillert 					break;
703e802aa69Smillert 				case 'n':
704e802aa69Smillert 					*v++ = '\n';
705e802aa69Smillert 					break;
706e802aa69Smillert 				case 't':
707e802aa69Smillert 					*v++ = '\t';
708e802aa69Smillert 					break;
709e802aa69Smillert 				case '0': case '1': case '2':
710e802aa69Smillert 				case '3': case '4': case '5':
711e802aa69Smillert 				case '6': case '7':
712e802aa69Smillert 					n = *line - '0';
713dfe5467eSderaadt 					if (isdigit((unsigned char)line[1])) {
714e802aa69Smillert 						++line;
715e802aa69Smillert 						n <<= 3;
716e802aa69Smillert 						n |= *line-'0';
717e802aa69Smillert 					}
718dfe5467eSderaadt 					if (isdigit((unsigned char)line[1])) {
719e802aa69Smillert 						++line;
720e802aa69Smillert 						n <<= 3;
721e802aa69Smillert 						n |= *line-'0';
722e802aa69Smillert 					}
723e802aa69Smillert 					break;
724e802aa69Smillert 				default:
725e802aa69Smillert 					*v++ = *line;
726e802aa69Smillert 					break;
727e802aa69Smillert 				}
728e802aa69Smillert 			} else
729e802aa69Smillert 				*v++ = *line;
730e802aa69Smillert 		}
731e802aa69Smillert 		*v = '\0';
732e802aa69Smillert 		return (value);
733e802aa69Smillert next:
734e802aa69Smillert 		while (*line++)
735e802aa69Smillert 			;
736e802aa69Smillert 	}
737e802aa69Smillert 	return (NULL);
738e802aa69Smillert }
739753e8795Sguenther DEF_WEAK(auth_getvalue);
740e802aa69Smillert 
741e802aa69Smillert quad_t
auth_check_expire(auth_session_t * as)742e802aa69Smillert auth_check_expire(auth_session_t *as)
743e802aa69Smillert {
744e802aa69Smillert 	if (as->pwd == NULL && auth_setpwd(as, NULL) < 0) {
745e802aa69Smillert 		as->state &= ~AUTH_ALLOW;
746e802aa69Smillert 		as->state |= AUTH_EXPIRED;	/* XXX */
747e802aa69Smillert 		return (-1);
748e802aa69Smillert 	}
749e802aa69Smillert 
750e802aa69Smillert 	if (as->pwd == NULL)
751e802aa69Smillert 		return (0);
752e802aa69Smillert 
753e802aa69Smillert 	if (as->pwd && (quad_t)as->pwd->pw_expire != 0) {
754e802aa69Smillert 		if (as->now.tv_sec == 0)
755d82e6535Spirofti 			WRAP(gettimeofday)(&as->now, NULL);
756e802aa69Smillert 		if ((quad_t)as->now.tv_sec >= (quad_t)as->pwd->pw_expire) {
757e802aa69Smillert 			as->state &= ~AUTH_ALLOW;
758e802aa69Smillert 			as->state |= AUTH_EXPIRED;
759e802aa69Smillert 		}
760e802aa69Smillert 		if ((quad_t)as->now.tv_sec == (quad_t)as->pwd->pw_expire)
761e802aa69Smillert 			return (-1);
762e802aa69Smillert 		return ((quad_t)as->pwd->pw_expire - (quad_t)as->now.tv_sec);
763e802aa69Smillert 	}
764e802aa69Smillert 	return (0);
765e802aa69Smillert }
766753e8795Sguenther DEF_WEAK(auth_check_expire);
767e802aa69Smillert 
768e802aa69Smillert quad_t
auth_check_change(auth_session_t * as)769e802aa69Smillert auth_check_change(auth_session_t *as)
770e802aa69Smillert {
771e802aa69Smillert 	if (as->pwd == NULL && auth_setpwd(as, NULL) < 0) {
772e802aa69Smillert 		as->state &= ~AUTH_ALLOW;
773e802aa69Smillert 		as->state |= AUTH_PWEXPIRED;	/* XXX */
774e802aa69Smillert 		return (-1);
775e802aa69Smillert 	}
776e802aa69Smillert 
777e802aa69Smillert 	if (as->pwd == NULL)
778e802aa69Smillert 		return (0);
779e802aa69Smillert 
780e802aa69Smillert 	if (as->pwd && (quad_t)as->pwd->pw_change) {
781e802aa69Smillert 		if (as->now.tv_sec == 0)
782d82e6535Spirofti 			WRAP(gettimeofday)(&as->now, NULL);
783e802aa69Smillert 		if (as->now.tv_sec >= (quad_t)as->pwd->pw_change) {
784e802aa69Smillert 			as->state &= ~AUTH_ALLOW;
785e802aa69Smillert 			as->state |= AUTH_PWEXPIRED;
786e802aa69Smillert 		}
787e802aa69Smillert 		if ((quad_t)as->now.tv_sec == (quad_t)as->pwd->pw_change)
788e802aa69Smillert 			return (-1);
789e802aa69Smillert 		return ((quad_t)as->pwd->pw_change - (quad_t)as->now.tv_sec);
790e802aa69Smillert 	}
791e802aa69Smillert 	return (0);
792e802aa69Smillert }
793753e8795Sguenther DEF_WEAK(auth_check_change);
794e802aa69Smillert 
795e802aa69Smillert /*
796e802aa69Smillert  * The down and dirty call to the login script
797e802aa69Smillert  * okay contains the default return value, typically 0 but
798e802aa69Smillert  * is AUTH_OKAY for approval like scripts.
799e802aa69Smillert  *
800e802aa69Smillert  * Internally additional trailing arguments can be read from as->ap
8017ed757dfSmillert  * Options will be placed just after the first argument (not including path).
802e802aa69Smillert  *
8037ed757dfSmillert  * Any data will be sent to (and freed by) the script
804e802aa69Smillert  */
805e802aa69Smillert int
auth_call(auth_session_t * as,char * path,...)806e802aa69Smillert auth_call(auth_session_t *as, char *path, ...)
807e802aa69Smillert {
808e802aa69Smillert 	char *line;
809e802aa69Smillert 	struct authdata *data;
810e802aa69Smillert 	struct authopts *opt;
811e802aa69Smillert 	pid_t pid;
812e802aa69Smillert 	int status;
813e802aa69Smillert 	int okay;
814e802aa69Smillert 	int pfd[2];
815e802aa69Smillert 	int argc;
8167ed757dfSmillert 	char *argv[64];		/* 64 args should be more than enough */
817e802aa69Smillert #define	Nargc	(sizeof(argv)/sizeof(argv[0]))
818e802aa69Smillert 
819e802aa69Smillert 	va_start(as->ap0, path);
820e802aa69Smillert 
821e802aa69Smillert 	argc = 0;
822e802aa69Smillert 	if ((argv[argc] = _auth_next_arg(as)) != NULL)
823e802aa69Smillert 		++argc;
824e802aa69Smillert 
8256e2de6bbSmillert 	if (as->fd != -1) {
8266e2de6bbSmillert 		argv[argc++] = "-v";
8276e2de6bbSmillert 		argv[argc++] = "fd=4";		/* AUTH_FD, see below */
8286e2de6bbSmillert 	}
829a314d10bSderaadt 	/* XXX - fail if out of space in argv */
830e802aa69Smillert 	for (opt = as->optlist; opt != NULL; opt = opt->next) {
831e802aa69Smillert 		if (argc < Nargc - 2) {
832e802aa69Smillert 			argv[argc++] = "-v";
833e802aa69Smillert 			argv[argc++] = opt->opt;
834e802aa69Smillert 		} else {
835e802aa69Smillert 			syslog(LOG_ERR, "too many authentication options");
836e802aa69Smillert 			goto fail;
837e802aa69Smillert 		}
838e802aa69Smillert 	}
839e802aa69Smillert 	while (argc < Nargc - 1 && (argv[argc] = _auth_next_arg(as)))
840e802aa69Smillert 		++argc;
841e802aa69Smillert 
842e802aa69Smillert 	if (argc >= Nargc - 1 && _auth_next_arg(as)) {
8434808f726Smillert 		if (memcmp(&nilap, &(as->ap0), sizeof(nilap)) != 0) {
844e802aa69Smillert 			va_end(as->ap0);
8458fbd7fcbSdoug 			explicit_bzero(&(as->ap0), sizeof(as->ap0));
846e802aa69Smillert 		}
8474808f726Smillert 		if (memcmp(&nilap, &(as->ap), sizeof(nilap)) != 0) {
848e802aa69Smillert 			va_end(as->ap);
8498fbd7fcbSdoug 			explicit_bzero(&(as->ap), sizeof(as->ap));
850e802aa69Smillert 		}
851e802aa69Smillert 		syslog(LOG_ERR, "too many arguments");
852e802aa69Smillert 		goto fail;
853e802aa69Smillert 	}
854e802aa69Smillert 
855e802aa69Smillert 	argv[argc] = NULL;
856e802aa69Smillert 
857df69c215Sderaadt 	if (socketpair(PF_LOCAL, SOCK_STREAM, 0, pfd) == -1) {
858e802aa69Smillert 		syslog(LOG_ERR, "unable to create backchannel %m");
8599b9d2a55Sguenther 		warnx("internal resource failure");
860e802aa69Smillert 		goto fail;
861e802aa69Smillert 	}
862e802aa69Smillert 
863e802aa69Smillert 	switch (pid = fork()) {
864e802aa69Smillert 	case -1:
865e802aa69Smillert 		syslog(LOG_ERR, "%s: %m", path);
8669b9d2a55Sguenther 		warnx("internal resource failure");
867fb18cfd4Smillert 		close(pfd[0]);
868fb18cfd4Smillert 		close(pfd[1]);
869e802aa69Smillert 		goto fail;
870e802aa69Smillert 	case 0:
871e802aa69Smillert #define	COMM_FD	3
8726e2de6bbSmillert #define	AUTH_FD	4
873df69c215Sderaadt 		if (dup2(pfd[1], COMM_FD) == -1)
874e802aa69Smillert 			err(1, "dup of backchannel");
8756e2de6bbSmillert 		if (as->fd != -1) {
876df69c215Sderaadt 			if (dup2(as->fd, AUTH_FD) == -1)
8776e2de6bbSmillert 				err(1, "dup of auth fd");
8786e2de6bbSmillert 			closefrom(AUTH_FD + 1);
8796e2de6bbSmillert 		} else
880188b81b3Sderaadt 			closefrom(COMM_FD + 1);
881e802aa69Smillert 		execve(path, argv, auth_environ);
882e802aa69Smillert 		syslog(LOG_ERR, "%s: %m", path);
883e802aa69Smillert 		err(1, "%s", path);
884e802aa69Smillert 	default:
885e802aa69Smillert 		close(pfd[1]);
8866e2de6bbSmillert 		if (as->fd != -1) {
8876e2de6bbSmillert 			close(as->fd);		/* so child has only ref */
8886e2de6bbSmillert 			as->fd = -1;
8896e2de6bbSmillert 		}
890e802aa69Smillert 		while ((data = as->data) != NULL) {
891e802aa69Smillert 			as->data = data->next;
892e802aa69Smillert 			if (data->len > 0) {
893e802aa69Smillert 				write(pfd[0], data->ptr, data->len);
8948fbd7fcbSdoug 				explicit_bzero(data->ptr, data->len);
895e802aa69Smillert 			}
896e802aa69Smillert 			free(data);
897e802aa69Smillert 		}
898e802aa69Smillert 		as->index = 0;
899e802aa69Smillert 		_auth_spool(as, pfd[0]);
900e802aa69Smillert 		close(pfd[0]);
901b2251c56Sguenther 		do {
902b2251c56Sguenther 			if (waitpid(pid, &status, 0) != -1) {
903b2251c56Sguenther 				if (!WIFEXITED(status))
904e802aa69Smillert 					goto fail;
905b2251c56Sguenther 				break;
906e802aa69Smillert 			}
907b2251c56Sguenther 			/*
908b2251c56Sguenther 			 * could get ECHILD if it was waited for by
909b2251c56Sguenther 			 * another thread or from a signal handler
910b2251c56Sguenther 			 */
911b2251c56Sguenther 		} while (errno == EINTR);
912e802aa69Smillert 	}
913e802aa69Smillert 
914e802aa69Smillert 	/*
915e802aa69Smillert 	 * Now scan the spooled data
916e802aa69Smillert 	 * It is easier to wait for all the data before starting
917e802aa69Smillert 	 * to scan it.
918e802aa69Smillert 	 */
919e802aa69Smillert     	for (line = as->spool; line < as->spool + as->index;) {
920e802aa69Smillert 		if (!strncasecmp(line, BI_REJECT, sizeof(BI_REJECT)-1)) {
921e802aa69Smillert 			line += sizeof(BI_REJECT) - 1;
922e802aa69Smillert 			if (!*line || *line == ' ' || *line == '\t') {
923e802aa69Smillert 				while (*line == ' ' || *line == '\t')
924e802aa69Smillert 					++line;
925e802aa69Smillert 				if (!strcasecmp(line, "silent")) {
926e802aa69Smillert 					as->state = AUTH_SILENT;
927e802aa69Smillert 					break;
928e802aa69Smillert 				}
929e802aa69Smillert 				if (!strcasecmp(line, "challenge")) {
930e802aa69Smillert 					as->state  = AUTH_CHALLENGE;
931e802aa69Smillert 					break;
932e802aa69Smillert 				}
933e802aa69Smillert 				if (!strcasecmp(line, "expired")) {
934e802aa69Smillert 					as->state  = AUTH_EXPIRED;
935e802aa69Smillert 					break;
936e802aa69Smillert 				}
937e802aa69Smillert 				if (!strcasecmp(line, "pwexpired")) {
938e802aa69Smillert 					as->state  = AUTH_PWEXPIRED;
939e802aa69Smillert 					break;
940e802aa69Smillert 				}
941e802aa69Smillert 			}
942e802aa69Smillert 			break;
943e802aa69Smillert 		} else if (!strncasecmp(line, BI_AUTH, sizeof(BI_AUTH)-1)) {
944e802aa69Smillert 			line += sizeof(BI_AUTH) - 1;
945e802aa69Smillert 			if (!*line || *line == ' ' || *line == '\t') {
946e802aa69Smillert 				while (*line == ' ' || *line == '\t')
947e802aa69Smillert 					++line;
948e802aa69Smillert 				if (*line == '\0')
949e802aa69Smillert 					as->state |= AUTH_OKAY;
950e802aa69Smillert 				else if (!strcasecmp(line, "root"))
951e802aa69Smillert 					as->state |= AUTH_ROOTOKAY;
952e802aa69Smillert 				else if (!strcasecmp(line, "secure"))
953e802aa69Smillert 					as->state |= AUTH_SECURE;
954e802aa69Smillert 			}
955e802aa69Smillert 		} else if (!strncasecmp(line, BI_REMOVE, sizeof(BI_REMOVE)-1)) {
956e802aa69Smillert 			line += sizeof(BI_REMOVE) - 1;
957e802aa69Smillert 			while (*line == ' ' || *line == '\t')
958e802aa69Smillert 				++line;
959e802aa69Smillert 			if (*line)
960e802aa69Smillert 				_add_rmlist(as, line);
961e802aa69Smillert 		}
962e802aa69Smillert 		while (*line++)
963e802aa69Smillert 			;
964e802aa69Smillert 	}
965e802aa69Smillert 
966e802aa69Smillert 	if (WEXITSTATUS(status))
967e802aa69Smillert 		as->state &= ~AUTH_ALLOW;
968e802aa69Smillert 
969e802aa69Smillert 	okay = as->state & AUTH_ALLOW;
970e802aa69Smillert 
971e802aa69Smillert 	if (!okay)
972e802aa69Smillert 		auth_clrenv(as);
973e802aa69Smillert 
974e802aa69Smillert 	if (0) {
975e802aa69Smillert fail:
976e802aa69Smillert 		auth_clrenv(as);
977e802aa69Smillert 		as->state = 0;
978e802aa69Smillert 		okay = -1;
979e802aa69Smillert 	}
980e802aa69Smillert 
981e802aa69Smillert 	while ((data = as->data) != NULL) {
982e802aa69Smillert 		as->data = data->next;
983e802aa69Smillert 		free(data);
984e802aa69Smillert 	}
985e802aa69Smillert 
9864808f726Smillert 	if (memcmp(&nilap, &(as->ap0), sizeof(nilap)) != 0) {
987e802aa69Smillert 		va_end(as->ap0);
9888fbd7fcbSdoug 		explicit_bzero(&(as->ap0), sizeof(as->ap0));
989e802aa69Smillert 	}
990e802aa69Smillert 
9914808f726Smillert 	if (memcmp(&nilap, &(as->ap), sizeof(nilap)) != 0) {
992e802aa69Smillert 		va_end(as->ap);
9938fbd7fcbSdoug 		explicit_bzero(&(as->ap), sizeof(as->ap));
994e802aa69Smillert 	}
995e802aa69Smillert 	return (okay);
996e802aa69Smillert }
997753e8795Sguenther DEF_WEAK(auth_call);
998e802aa69Smillert 
999e802aa69Smillert static void
_recv_fd(auth_session_t * as,int fd)10006e2de6bbSmillert _recv_fd(auth_session_t *as, int fd)
10016e2de6bbSmillert {
10026e2de6bbSmillert 	struct msghdr msg;
10036e2de6bbSmillert 	struct cmsghdr *cmp;
10040827ab61Sderaadt 	union {
10050827ab61Sderaadt 		struct cmsghdr hdr;
10060827ab61Sderaadt 		char buf[CMSG_SPACE(sizeof(int))];
10070827ab61Sderaadt 	} cmsgbuf;
10086e2de6bbSmillert 
10096e2de6bbSmillert 	memset(&msg, 0, sizeof(msg));
10100827ab61Sderaadt 	msg.msg_control = &cmsgbuf.buf;
1011da15c7b9Sderaadt 	msg.msg_controllen = sizeof(cmsgbuf.buf);
1012df69c215Sderaadt 	if (recvmsg(fd, &msg, 0) == -1)
10136e2de6bbSmillert 		syslog(LOG_ERR, "recvmsg: %m");
10146e2de6bbSmillert 	else if (msg.msg_flags & MSG_TRUNC)
10156e2de6bbSmillert 		syslog(LOG_ERR, "message truncated");
10166e2de6bbSmillert 	else if (msg.msg_flags & MSG_CTRUNC)
10176e2de6bbSmillert 		syslog(LOG_ERR, "control message truncated");
10186e2de6bbSmillert 	else if ((cmp = CMSG_FIRSTHDR(&msg)) == NULL)
10196e2de6bbSmillert 		syslog(LOG_ERR, "missing control message");
10206e2de6bbSmillert 	else {
10216e2de6bbSmillert 		if (cmp->cmsg_level != SOL_SOCKET)
10226e2de6bbSmillert 			syslog(LOG_ERR, "unexpected cmsg_level %d",
10236e2de6bbSmillert 			    cmp->cmsg_level);
10246e2de6bbSmillert 		else if (cmp->cmsg_type != SCM_RIGHTS)
10256e2de6bbSmillert 			syslog(LOG_ERR, "unexpected cmsg_type %d",
10266e2de6bbSmillert 			    cmp->cmsg_type);
10278a182787Smillert 		else if (cmp->cmsg_len != CMSG_LEN(sizeof(int)))
10286e2de6bbSmillert 			syslog(LOG_ERR, "bad cmsg_len %d",
10296e2de6bbSmillert 			    cmp->cmsg_len);
10306e2de6bbSmillert 		else {
10316e2de6bbSmillert 			if (as->fd != -1)
10326e2de6bbSmillert 				close(as->fd);
10336e2de6bbSmillert 			as->fd = *(int *)CMSG_DATA(cmp);
10346e2de6bbSmillert 		}
10356e2de6bbSmillert 	}
10366e2de6bbSmillert }
10376e2de6bbSmillert 
10386e2de6bbSmillert static void
_auth_spool(auth_session_t * as,int fd)1039e802aa69Smillert _auth_spool(auth_session_t *as, int fd)
1040e802aa69Smillert {
10416e2de6bbSmillert 	ssize_t r;
10426e2de6bbSmillert 	char *b, *s;
1043e802aa69Smillert 
10446e2de6bbSmillert 	for (s = as->spool + as->index; as->index < sizeof(as->spool) - 1; ) {
1045e802aa69Smillert 		r = read(fd, as->spool + as->index,
1046e802aa69Smillert 		    sizeof(as->spool) - as->index);
1047e802aa69Smillert 		if (r <= 0) {
1048e802aa69Smillert 			as->spool[as->index] = '\0';
1049e802aa69Smillert 			return;
1050e802aa69Smillert 		}
1051e802aa69Smillert 		b = as->spool + as->index;
1052e802aa69Smillert 		as->index += r;
1053e802aa69Smillert 		/*
10546e2de6bbSmillert 		 * Convert newlines into NULs to allow easy scanning of the
10556e2de6bbSmillert 		 * file and receive an fd if there is a BI_FDPASS message.
10566e2de6bbSmillert 		 * XXX - checking for BI_FDPASS here is annoying but
10576e2de6bbSmillert 		 *       we need to avoid the read() slurping in control data.
1058e802aa69Smillert 		 */
10596e2de6bbSmillert 		while (r-- > 0) {
10606e2de6bbSmillert 			if (*b++ == '\n') {
1061e802aa69Smillert 				b[-1] = '\0';
10626e2de6bbSmillert 				if (strcasecmp(s, BI_FDPASS) == 0)
10636e2de6bbSmillert 					_recv_fd(as, fd);
10646e2de6bbSmillert 				s = b;
10656e2de6bbSmillert 			}
10666e2de6bbSmillert 		}
1067e802aa69Smillert 	}
1068e802aa69Smillert 
1069e802aa69Smillert 	syslog(LOG_ERR, "Overflowed backchannel spool buffer");
1070e802aa69Smillert 	errx(1, "System error in authentication program");
1071e802aa69Smillert }
1072e802aa69Smillert 
1073e802aa69Smillert static void
_add_rmlist(auth_session_t * as,char * file)1074e802aa69Smillert _add_rmlist(auth_session_t *as, char *file)
1075e802aa69Smillert {
1076e802aa69Smillert 	struct rmfiles *rm;
1077e17b29d3Sderaadt 	size_t i = strlen(file) + 1;
1078e17b29d3Sderaadt 
1079e17b29d3Sderaadt 	// XXX should rangecheck i since we are about to add?
1080e802aa69Smillert 
1081e802aa69Smillert 	if ((rm = malloc(sizeof(struct rmfiles) + i)) == NULL) {
1082e802aa69Smillert 		syslog(LOG_ERR, "Failed to allocate rmfiles: %m");
1083e802aa69Smillert 		return;
1084e802aa69Smillert 	}
1085e802aa69Smillert 	rm->file = (char *)(rm + 1);
1086e802aa69Smillert 	rm->next = as->rmlist;
10875af14704Sderaadt 	strlcpy(rm->file, file, i);
1088e802aa69Smillert 	as->rmlist = rm;
1089e802aa69Smillert }
1090e802aa69Smillert 
1091e802aa69Smillert static char *
_auth_next_arg(auth_session_t * as)1092e802aa69Smillert _auth_next_arg(auth_session_t *as)
1093e802aa69Smillert {
1094e802aa69Smillert 	char *arg;
1095e802aa69Smillert 
10964808f726Smillert 	if (memcmp(&nilap, &(as->ap0), sizeof(nilap)) != 0) {
1097e802aa69Smillert 		if ((arg = va_arg(as->ap0, char *)) != NULL)
1098e802aa69Smillert 			return (arg);
1099e802aa69Smillert 		va_end(as->ap0);
11008fbd7fcbSdoug 		explicit_bzero(&(as->ap0), sizeof(as->ap0));
1101e802aa69Smillert 	}
11024808f726Smillert 	if (memcmp(&nilap, &(as->ap), sizeof(nilap)) != 0) {
1103e802aa69Smillert 		if ((arg = va_arg(as->ap, char *)) != NULL)
1104e802aa69Smillert 			return (arg);
1105e802aa69Smillert 		va_end(as->ap);
11068fbd7fcbSdoug 		explicit_bzero(&(as->ap), sizeof(as->ap));
1107e802aa69Smillert 	}
1108e802aa69Smillert 	return (NULL);
1109e802aa69Smillert }
1110