authenticate.c revision 1.19
1/*	$OpenBSD: authenticate.c,v 1.19 2013/09/30 12:02:32 millert Exp $	*/
2
3/*-
4 * Copyright (c) 1997 Berkeley Software Design, Inc. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 *    must display the following acknowledgement:
16 *	This product includes software developed by Berkeley Software Design,
17 *	Inc.
18 * 4. The name of Berkeley Software Design, Inc.  may not be used to endorse
19 *    or promote products derived from this software without specific prior
20 *    written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN, INC. BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 *
34 *	BSDI $From: authenticate.c,v 2.21 1999/09/08 22:33:26 prb Exp $
35 */
36
37#include <sys/stat.h>
38
39#include <ctype.h>
40#include <err.h>
41#include <fcntl.h>
42#include <limits.h>
43#include <login_cap.h>
44#include <paths.h>
45#include <pwd.h>
46#include <stdarg.h>
47#include <stdio.h>
48#include <stdlib.h>
49#include <string.h>
50#include <syslog.h>
51#include <unistd.h>
52
53#include <bsd_auth.h>
54
55static int _auth_checknologin(login_cap_t *, int);
56
57char *
58auth_mkvalue(char *value)
59{
60	char *big, *p;
61
62	big = malloc(strlen(value) * 4 + 1);
63	if (big == NULL)
64		return (NULL);
65	/*
66	 * XXX - There should be a more standardized
67	 * routine for doing this sort of thing.
68	 */
69	for (p = big; *value; ++value) {
70		switch (*value) {
71		case '\r':
72			*p++ = '\\';
73			*p++ = 'r';
74			break;
75		case '\n':
76			*p++ = '\\';
77			*p++ = 'n';
78			break;
79		case '\\':
80			*p++ = '\\';
81			*p++ = *value;
82			break;
83		case '\t':
84		case ' ':
85			if (p == big)
86				*p++ = '\\';
87			*p++ = *value;
88			break;
89		default:
90			if (!isprint(*value)) {
91				*p++ = '\\';
92				*p++ = ((*value >> 6) & 0x3) + '0';
93				*p++ = ((*value >> 3) & 0x7) + '0';
94				*p++ = ((*value     ) & 0x7) + '0';
95			} else
96				*p++ = *value;
97			break;
98		}
99	}
100	*p = '\0';
101	return (big);
102}
103
104void
105auth_checknologin(login_cap_t *lc)
106{
107	if (_auth_checknologin(lc, 1))
108		exit(1);
109}
110
111static int
112_auth_checknologin(login_cap_t *lc, int print)
113{
114	struct stat sb;
115	char *nologin;
116	int mustfree;
117
118	if (login_getcapbool(lc, "ignorenologin", 0))
119		return (0);
120
121	/*
122	 * If we fail to get the nologin file due to a database error,
123	 * assume there should have been one...
124	 */
125	nologin = login_getcapstr(lc, "nologin", "", NULL);
126	mustfree = nologin && *nologin != '\0';
127	if (nologin == NULL)
128		goto print_nologin;
129
130	/* First try the nologin file specified in login.conf. */
131	if (*nologin != '\0' && stat(nologin, &sb) == 0)
132		goto print_nologin;
133	if (mustfree) {
134		free(nologin);
135		mustfree = 0;
136	}
137
138	/* If that doesn't exist try _PATH_NOLOGIN. */
139	if (stat(_PATH_NOLOGIN, &sb) == 0) {
140		nologin = _PATH_NOLOGIN;
141		goto print_nologin;
142	}
143
144	/* Couldn't stat any nologin files, must be OK to login. */
145	return (0);
146
147print_nologin:
148	if (print) {
149		if (!nologin || *nologin == '\0' || auth_cat(nologin) == 0) {
150			puts("Logins are not allowed at this time.");
151			fflush(stdout);
152		}
153	}
154	if (mustfree)
155		free(nologin);
156	return (-1);
157}
158
159int
160auth_cat(char *file)
161{
162	int fd, nchars;
163	char tbuf[8192];
164
165	if ((fd = open(file, O_RDONLY, 0)) < 0)
166		return (0);
167	while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
168		(void)write(fileno(stdout), tbuf, nchars);
169	(void)close(fd);
170	return (1);
171}
172
173int
174auth_approval(auth_session_t *as, login_cap_t *lc, char *name, char *type)
175{
176	int close_on_exit, close_lc_on_exit, len;
177	struct passwd *pwd;
178	char *approve, *s, path[PATH_MAX];
179
180	pwd = NULL;
181	close_on_exit = as == NULL;
182	close_lc_on_exit = lc == NULL;
183
184	if (as != NULL && name == NULL)
185		name = auth_getitem(as, AUTHV_NAME);
186
187	if (as != NULL)
188		pwd = auth_getpwd(as);
189
190	if (pwd == NULL) {
191		if (name != NULL)
192			pwd = getpwnam(name);
193		else {
194			if ((pwd = getpwuid(getuid())) == NULL) {
195				syslog(LOG_ERR, "no such user id %u", getuid());
196				_warnx("cannot approve who we don't recognize");
197				return (0);
198			}
199			name = pwd->pw_name;
200		}
201	}
202
203	if (name == NULL)
204		name = pwd->pw_name;
205
206	if (lc == NULL) {
207		if (strlen(name) >= PATH_MAX) {
208			syslog(LOG_ERR, "username to login %.*s...",
209			    PATH_MAX, name);
210			_warnx("username too long");
211			return (0);
212		}
213		if (pwd == NULL && (approve = strchr(name, '.')) != NULL) {
214			strlcpy(path, name, sizeof path);
215			path[approve-name] = '\0';
216			pwd = getpwnam(name);
217		}
218		lc = login_getclass(pwd ? pwd->pw_class : NULL);
219		if (lc == NULL) {
220			_warnx("unable to classify user");
221			return (0);
222		}
223	}
224
225	if (!type)
226		type = LOGIN_DEFSERVICE;
227	else {
228		if (strncmp(type, "approve-", 8) == 0)
229			type += 8;
230
231		len = snprintf(path, sizeof(path), "approve-%s", type);
232		if (len < 0 || len >= sizeof(path)) {
233			if (close_lc_on_exit)
234				login_close(lc);
235			syslog(LOG_ERR, "approval path too long %.*s...",
236			    PATH_MAX, type);
237			_warnx("approval script path too long");
238			return (0);
239		}
240	}
241
242	if ((approve = login_getcapstr(lc, s = path, NULL, NULL)) == NULL)
243		approve = login_getcapstr(lc, s = "approve", NULL, NULL);
244
245	if (approve && approve[0] != '/') {
246		if (close_lc_on_exit)
247			login_close(lc);
248		syslog(LOG_ERR, "Invalid %s script: %s", s, approve);
249		_warnx("invalid path to approval script");
250		free(approve);
251		return (0);
252	}
253
254	if (as == NULL && (as = auth_open()) == NULL) {
255		if (close_lc_on_exit)
256			login_close(lc);
257		syslog(LOG_ERR, "%m");
258		_warn(NULL);
259		if (approve)
260			free(approve);
261		return (0);
262	}
263
264	auth_setstate(as, AUTH_OKAY);
265	if (auth_setitem(as, AUTHV_NAME, name) < 0) {
266		syslog(LOG_ERR, "%m");
267		_warn(NULL);
268		goto out;
269	}
270	if (auth_check_expire(as) < 0)	/* is this account expired */
271		goto out;
272	if (_auth_checknologin(lc,
273	    auth_getitem(as, AUTHV_INTERACTIVE) != NULL)) {
274		auth_setstate(as, (auth_getstate(as) & ~AUTH_ALLOW));
275		goto out;
276	}
277	if (login_getcapbool(lc, "requirehome", 0) && pwd && pwd->pw_dir &&
278	    pwd->pw_dir[0]) {
279		struct stat sb;
280
281		if (stat(pwd->pw_dir, &sb) < 0 ||
282		    (sb.st_mode & 0170000) != S_IFDIR ||
283		    (pwd->pw_uid && sb.st_uid == pwd->pw_uid &&
284		    (sb.st_mode & S_IXUSR) == 0)) {
285			auth_setstate(as, (auth_getstate(as) & ~AUTH_ALLOW));
286			goto out;
287		}
288	}
289	if (approve)
290		auth_call(as, approve, strrchr(approve, '/') + 1, name,
291		    lc->lc_class, type, (char *)NULL);
292
293out:
294	if (approve)
295		free(approve);
296	if (close_lc_on_exit)
297		login_close(lc);
298
299	if (close_on_exit)
300		return (auth_close(as));
301	return (auth_getstate(as) & AUTH_ALLOW);
302}
303
304auth_session_t *
305auth_usercheck(char *name, char *style, char *type, char *password)
306{
307	char namebuf[LOGIN_NAME_MAX + 1 + NAME_MAX + 1];
308	auth_session_t *as;
309	login_cap_t *lc;
310	struct passwd *pwd;
311	char *slash;
312
313	if (strlcpy(namebuf, name, sizeof(namebuf)) >= sizeof(namebuf))
314		return (NULL);
315	name = namebuf;
316
317	/*
318	 * Split up user:style names if we were not given a style
319	 */
320	if (style == NULL && (style = strchr(name, ':')) != NULL)
321		*style++ = '\0';
322
323	/*
324	 * Cope with user/instance.  We are only using this to get
325	 * the class so it is okay if we strip a /root instance
326	 * The actual login script will pay attention to the instance.
327	 */
328	if ((pwd = getpwnam(name)) == NULL) {
329		if ((slash = strchr(name, '/')) != NULL) {
330			*slash = '\0';
331			pwd = getpwnam(name);
332			*slash = '/';
333		}
334	}
335	if ((lc = login_getclass(pwd ? pwd->pw_class : NULL)) == NULL)
336		return (NULL);
337
338	if ((style = login_getstyle(lc, style, type)) == NULL) {
339		login_close(lc);
340		return (NULL);
341	}
342
343	if (password) {
344		if ((as = auth_open()) == NULL) {
345			login_close(lc);
346			return (NULL);
347		}
348		auth_setitem(as, AUTHV_SERVICE, "response");
349		auth_setdata(as, "", 1);
350		auth_setdata(as, password, strlen(password) + 1);
351		memset(password, 0, strlen(password));
352	} else
353		as = NULL;
354	as = auth_verify(as, style, name, lc->lc_class, (char *)NULL);
355	login_close(lc);
356	return (as);
357}
358
359int
360auth_userokay(char *name, char *style, char *type, char *password)
361{
362	auth_session_t *as;
363
364	as = auth_usercheck(name, style, type, password);
365
366	return (as != NULL ? auth_close(as) : 0);
367}
368
369auth_session_t *
370auth_userchallenge(char *name, char *style, char *type, char **challengep)
371{
372	char namebuf[LOGIN_NAME_MAX + 1 + NAME_MAX + 1];
373	auth_session_t *as;
374	login_cap_t *lc;
375	struct passwd *pwd;
376	char *slash;
377
378	if (strlen(name) >= sizeof(namebuf))
379		return (NULL);
380	strlcpy(namebuf, name, sizeof namebuf);
381	name = namebuf;
382
383	/*
384	 * Split up user:style names if we were not given a style
385	 */
386	if (style == NULL && (style = strchr(name, ':')) != NULL)
387		*style++ = '\0';
388
389	/*
390	 * Cope with user/instance.  We are only using this to get
391	 * the class so it is okay if we strip a /root instance
392	 * The actual login script will pay attention to the instance.
393	 */
394	if ((pwd = getpwnam(name)) == NULL) {
395		if ((slash = strchr(name, '/')) != NULL) {
396			*slash = '\0';
397			pwd = getpwnam(name);
398			*slash = '/';
399		}
400	}
401	if ((lc = login_getclass(pwd ? pwd->pw_class : NULL)) == NULL)
402		return (NULL);
403
404	if ((style = login_getstyle(lc, style, type)) == NULL ||
405	    (as = auth_open()) == NULL) {
406		login_close(lc);
407		return (NULL);
408	}
409	if (auth_setitem(as, AUTHV_STYLE, style) < 0 ||
410	    auth_setitem(as, AUTHV_NAME, name) < 0 ||
411	    auth_setitem(as, AUTHV_CLASS, lc->lc_class) < 0) {
412		auth_close(as);
413		login_close(lc);
414		return (NULL);
415	}
416	login_close(lc);
417	*challengep = auth_challenge(as);
418	return (as);
419}
420
421int
422auth_userresponse(auth_session_t *as, char *response, int more)
423{
424	char path[PATH_MAX];
425	char *style, *name, *challenge, *class;
426	int len;
427
428	if (as == NULL)
429		return (0);
430
431	auth_setstate(as, 0);
432
433	if ((style = auth_getitem(as, AUTHV_STYLE)) == NULL ||
434	    (name = auth_getitem(as, AUTHV_NAME)) == NULL) {
435		if (more == 0)
436			return (auth_close(as));
437		return(0);
438	}
439
440	len = snprintf(path, sizeof(path), _PATH_AUTHPROG "%s", style);
441	if (len < 0 || len >= sizeof(path)) {
442		if (more == 0)
443			return (auth_close(as));
444		return (0);
445	}
446
447	challenge = auth_getitem(as, AUTHV_CHALLENGE);
448	class = auth_getitem(as, AUTHV_CLASS);
449
450	if (challenge)
451		auth_setdata(as, challenge, strlen(challenge) + 1);
452	else
453		auth_setdata(as, "", 1);
454	if (response) {
455		auth_setdata(as, response, strlen(response) + 1);
456		memset(response, 0, strlen(response));
457	} else
458		auth_setdata(as, "", 1);
459
460	auth_call(as, path, style, "-s", "response", name, class, (char *)NULL);
461
462	/*
463	 * If they authenticated then make sure they did not expire
464	 */
465	if (auth_getstate(as) & AUTH_ALLOW)
466		auth_check_expire(as);
467	if (more == 0)
468		return (auth_close(as));
469	return (auth_getstate(as) & AUTH_ALLOW);
470}
471
472/*
473 * Authenticate name with the specified style.
474 * If ``as'' is NULL a new session is formed with the default service.
475 * Returns NULL only if ``as'' is NULL and we were unable to allocate
476 * a new session.
477 *
478 * Use auth_close() or auth_getstate() to determine if the authentication
479 * worked.
480 */
481auth_session_t *
482auth_verify(auth_session_t *as, char *style, char *name, ...)
483{
484	va_list ap;
485	char path[PATH_MAX];
486
487	if ((name == NULL || style == NULL) && as == NULL)
488		return (as);
489
490	if (as == NULL && (as = auth_open()) == NULL)
491		return (NULL);
492	auth_setstate(as, 0);
493
494	if (style != NULL && auth_setitem(as, AUTHV_STYLE, style) < 0)
495		return (as);
496
497	if (name != NULL && auth_setitem(as, AUTHV_NAME, name) < 0)
498		return (as);
499
500	style = auth_getitem(as, AUTHV_STYLE);
501	name = auth_getitem(as, AUTHV_NAME);
502
503	snprintf(path, sizeof(path), _PATH_AUTHPROG "%s", style);
504	va_start(ap, name);
505	auth_set_va_list(as, ap);
506	auth_call(as, path, auth_getitem(as, AUTHV_STYLE), "-s",
507	    auth_getitem(as, AUTHV_SERVICE), name, (char *)NULL);
508	va_end(ap);
509	return (as);
510}
511