1/*	$OpenBSD: authenticate.c,v 1.29 2021/10/24 21:24:20 deraadt 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((unsigned char)*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}
103DEF_WEAK(auth_mkvalue);
104
105void
106auth_checknologin(login_cap_t *lc)
107{
108	if (_auth_checknologin(lc, 1))
109		exit(1);
110}
111DEF_WEAK(auth_checknologin);
112
113static int
114_auth_checknologin(login_cap_t *lc, int print)
115{
116	struct stat sb;
117	char *nologin;
118	int mustfree;
119
120	if (login_getcapbool(lc, "ignorenologin", 0))
121		return (0);
122
123	/*
124	 * If we fail to get the nologin file due to a database error,
125	 * assume there should have been one...
126	 */
127	nologin = login_getcapstr(lc, "nologin", "", NULL);
128	mustfree = nologin && *nologin != '\0';
129	if (nologin == NULL)
130		goto print_nologin;
131
132	/* First try the nologin file specified in login.conf. */
133	if (*nologin != '\0' && stat(nologin, &sb) == 0)
134		goto print_nologin;
135	if (mustfree) {
136		free(nologin);
137		mustfree = 0;
138	}
139
140	/* If that doesn't exist try _PATH_NOLOGIN. */
141	if (stat(_PATH_NOLOGIN, &sb) == 0) {
142		nologin = _PATH_NOLOGIN;
143		goto print_nologin;
144	}
145
146	/* Couldn't stat any nologin files, must be OK to login. */
147	return (0);
148
149print_nologin:
150	if (print) {
151		if (!nologin || *nologin == '\0' || auth_cat(nologin) == 0) {
152			puts("Logins are not allowed at this time.");
153			fflush(stdout);
154		}
155	}
156	if (mustfree)
157		free(nologin);
158	return (-1);
159}
160
161int
162auth_cat(char *file)
163{
164	int fd, nchars;
165	char tbuf[8192];
166
167	if ((fd = open(file, O_RDONLY)) == -1)
168		return (0);
169	while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
170		(void)write(fileno(stdout), tbuf, nchars);
171	(void)close(fd);
172	return (1);
173}
174DEF_WEAK(auth_cat);
175
176int
177_auth_validuser(const char *name)
178{
179	/* User name must be specified and may not start with a '-'. */
180	if (*name == '\0' || *name == '-') {
181		syslog(LOG_ERR, "invalid user name %s", name);
182		return 0;
183	}
184	return 1;
185}
186
187int
188auth_approval(auth_session_t *as, login_cap_t *lc, char *name, char *type)
189{
190	int close_on_exit, close_lc_on_exit, len;
191	struct passwd pwstore, *pwd;
192	char *approve, *s, path[PATH_MAX], pwbuf[_PW_BUF_LEN];
193
194	pwd = NULL;
195	close_on_exit = as == NULL;
196	close_lc_on_exit = lc == NULL;
197
198	if (as != NULL && name == NULL)
199		name = auth_getitem(as, AUTHV_NAME);
200
201	if (as != NULL)
202		pwd = auth_getpwd(as);
203
204	if (pwd == NULL) {
205		if (name != NULL) {
206			if (!_auth_validuser(name)) {
207				warnx("cannot approve who we don't recognize");
208				return (0);
209			}
210			getpwnam_r(name, &pwstore, pwbuf, sizeof(pwbuf), &pwd);
211		} else {
212			getpwuid_r(getuid(), &pwstore, pwbuf, sizeof(pwbuf),
213			    &pwd);
214			if (pwd == NULL) {
215				syslog(LOG_ERR, "no such user id %u", getuid());
216				warnx("cannot approve who we don't recognize");
217				return (0);
218			}
219			name = pwd->pw_name;
220		}
221	}
222
223	if (name == NULL)
224		name = pwd->pw_name;
225
226	if (lc == NULL) {
227		if (strlen(name) >= PATH_MAX) {
228			syslog(LOG_ERR, "username to login %.*s...",
229			    PATH_MAX, name);
230			warnx("username too long");
231			return (0);
232		}
233		if (pwd == NULL && (approve = strchr(name, '.')) != NULL) {
234			strlcpy(path, name, sizeof path);
235			path[approve - name] = '\0';
236			getpwnam_r(name, &pwstore, pwbuf, sizeof(pwbuf), &pwd);
237		}
238		lc = login_getclass(pwd ? pwd->pw_class : NULL);
239		if (lc == NULL) {
240			warnx("unable to classify user");
241			return (0);
242		}
243	}
244
245	if (!type)
246		type = LOGIN_DEFSERVICE;
247	else {
248		if (strncmp(type, "approve-", 8) == 0)
249			type += 8;
250
251		len = snprintf(path, sizeof(path), "approve-%s", type);
252		if (len < 0 || len >= sizeof(path)) {
253			if (close_lc_on_exit)
254				login_close(lc);
255			syslog(LOG_ERR, "approval path too long %.*s...",
256			    PATH_MAX, type);
257			warnx("approval script path too long");
258			return (0);
259		}
260	}
261
262	if ((approve = login_getcapstr(lc, s = path, NULL, NULL)) == NULL)
263		approve = login_getcapstr(lc, s = "approve", NULL, NULL);
264
265	if (approve && approve[0] != '/') {
266		if (close_lc_on_exit)
267			login_close(lc);
268		syslog(LOG_ERR, "Invalid %s script: %s", s, approve);
269		warnx("invalid path to approval script");
270		free(approve);
271		return (0);
272	}
273
274	if (as == NULL && (as = auth_open()) == NULL) {
275		if (close_lc_on_exit)
276			login_close(lc);
277		syslog(LOG_ERR, "%m");
278		warn(NULL);
279		free(approve);
280		return (0);
281	}
282
283	auth_setstate(as, AUTH_OKAY);
284	if (auth_setitem(as, AUTHV_NAME, name) < 0) {
285		syslog(LOG_ERR, "%m");
286		warn(NULL);
287		goto out;
288	}
289	if (auth_check_expire(as) < 0)	/* is this account expired */
290		goto out;
291	if (_auth_checknologin(lc,
292	    auth_getitem(as, AUTHV_INTERACTIVE) != NULL)) {
293		auth_setstate(as, (auth_getstate(as) & ~AUTH_ALLOW));
294		goto out;
295	}
296	if (login_getcapbool(lc, "requirehome", 0) && pwd && pwd->pw_dir &&
297	    pwd->pw_dir[0]) {
298		struct stat sb;
299
300		if (stat(pwd->pw_dir, &sb) == -1 || !S_ISDIR(sb.st_mode) ||
301		    (pwd->pw_uid && sb.st_uid == pwd->pw_uid &&
302		    (sb.st_mode & S_IXUSR) == 0)) {
303			auth_setstate(as, (auth_getstate(as) & ~AUTH_ALLOW));
304			goto out;
305		}
306	}
307	if (approve)
308		auth_call(as, approve, strrchr(approve, '/') + 1, "--", name,
309		    lc->lc_class, type, (char *)NULL);
310
311out:
312	free(approve);
313	if (close_lc_on_exit)
314		login_close(lc);
315
316	if (close_on_exit)
317		return (auth_close(as));
318	return (auth_getstate(as) & AUTH_ALLOW);
319}
320DEF_WEAK(auth_approval);
321
322auth_session_t *
323auth_usercheck(char *name, char *style, char *type, char *password)
324{
325	char namebuf[LOGIN_NAME_MAX + 1 + NAME_MAX + 1];
326	char pwbuf[_PW_BUF_LEN];
327	auth_session_t *as;
328	login_cap_t *lc;
329	struct passwd pwstore, *pwd = NULL;
330	char *slash;
331
332	if (!_auth_validuser(name))
333		return (NULL);
334	if (strlcpy(namebuf, name, sizeof(namebuf)) >= sizeof(namebuf))
335		return (NULL);
336	name = namebuf;
337
338	/*
339	 * Split up user:style names if we were not given a style
340	 */
341	if (style == NULL && (style = strchr(name, ':')) != NULL)
342		*style++ = '\0';
343
344	/*
345	 * Cope with user/instance.  We are only using this to get
346	 * the class so it is okay if we strip a /root instance
347	 * The actual login script will pay attention to the instance.
348	 */
349	getpwnam_r(name, &pwstore, pwbuf, sizeof(pwbuf), &pwd);
350	if (pwd == NULL) {
351		if ((slash = strchr(name, '/')) != NULL) {
352			*slash = '\0';
353			getpwnam_r(name, &pwstore, pwbuf, sizeof(pwbuf), &pwd);
354			*slash = '/';
355		}
356	}
357	if ((lc = login_getclass(pwd ? pwd->pw_class : NULL)) == NULL)
358		return (NULL);
359
360	if ((style = login_getstyle(lc, style, type)) == NULL) {
361		login_close(lc);
362		return (NULL);
363	}
364
365	if (password) {
366		if ((as = auth_open()) == NULL) {
367			login_close(lc);
368			return (NULL);
369		}
370		auth_setitem(as, AUTHV_SERVICE, "response");
371		auth_setdata(as, "", 1);
372		auth_setdata(as, password, strlen(password) + 1);
373		explicit_bzero(password, strlen(password));
374	} else
375		as = NULL;
376	as = auth_verify(as, style, name, lc->lc_class, (char *)NULL);
377	login_close(lc);
378	return (as);
379}
380DEF_WEAK(auth_usercheck);
381
382int
383auth_userokay(char *name, char *style, char *type, char *password)
384{
385	auth_session_t *as;
386
387	as = auth_usercheck(name, style, type, password);
388
389	return (as != NULL ? auth_close(as) : 0);
390}
391DEF_WEAK(auth_userokay);
392
393auth_session_t *
394auth_userchallenge(char *name, char *style, char *type, char **challengep)
395{
396	char namebuf[LOGIN_NAME_MAX + 1 + NAME_MAX + 1];
397	auth_session_t *as;
398	login_cap_t *lc;
399	struct passwd pwstore, *pwd = NULL;
400	char *slash, pwbuf[_PW_BUF_LEN];
401
402	if (!_auth_validuser(name))
403		return (NULL);
404	if (strlen(name) >= sizeof(namebuf))
405		return (NULL);
406	strlcpy(namebuf, name, sizeof namebuf);
407	name = namebuf;
408
409	/*
410	 * Split up user:style names if we were not given a style
411	 */
412	if (style == NULL && (style = strchr(name, ':')) != NULL)
413		*style++ = '\0';
414
415	/*
416	 * Cope with user/instance.  We are only using this to get
417	 * the class so it is okay if we strip a /root instance
418	 * The actual login script will pay attention to the instance.
419	 */
420	getpwnam_r(name, &pwstore, pwbuf, sizeof(pwbuf), &pwd);
421	if (pwd == NULL) {
422		if ((slash = strchr(name, '/')) != NULL) {
423			*slash = '\0';
424			getpwnam_r(name, &pwstore, pwbuf, sizeof(pwbuf), &pwd);
425			*slash = '/';
426		}
427	}
428	if ((lc = login_getclass(pwd ? pwd->pw_class : NULL)) == NULL)
429		return (NULL);
430
431	if ((style = login_getstyle(lc, style, type)) == NULL ||
432	    (as = auth_open()) == NULL) {
433		login_close(lc);
434		return (NULL);
435	}
436	if (auth_setitem(as, AUTHV_STYLE, style) < 0 ||
437	    auth_setitem(as, AUTHV_NAME, name) < 0 ||
438	    auth_setitem(as, AUTHV_CLASS, lc->lc_class) < 0) {
439		auth_close(as);
440		login_close(lc);
441		return (NULL);
442	}
443	login_close(lc);
444	*challengep = auth_challenge(as);
445	return (as);
446}
447DEF_WEAK(auth_userchallenge);
448
449int
450auth_userresponse(auth_session_t *as, char *response, int more)
451{
452	char path[PATH_MAX];
453	char *style, *name, *challenge, *class;
454	int len;
455
456	if (as == NULL)
457		return (0);
458
459	auth_setstate(as, 0);
460
461	if ((style = auth_getitem(as, AUTHV_STYLE)) == NULL ||
462	    (name = auth_getitem(as, AUTHV_NAME)) == NULL ||
463	    !_auth_validuser(name)) {
464		if (more == 0)
465			return (auth_close(as));
466		return(0);
467	}
468
469	len = snprintf(path, sizeof(path), _PATH_AUTHPROG "%s", style);
470	if (len < 0 || len >= sizeof(path)) {
471		if (more == 0)
472			return (auth_close(as));
473		return (0);
474	}
475
476	challenge = auth_getitem(as, AUTHV_CHALLENGE);
477	class = auth_getitem(as, AUTHV_CLASS);
478
479	if (challenge)
480		auth_setdata(as, challenge, strlen(challenge) + 1);
481	else
482		auth_setdata(as, "", 1);
483	if (response) {
484		auth_setdata(as, response, strlen(response) + 1);
485		explicit_bzero(response, strlen(response));
486	} else
487		auth_setdata(as, "", 1);
488
489	auth_call(as, path, style, "-s", "response", "--", name,
490	    class, (char *)NULL);
491
492	/*
493	 * If they authenticated then make sure they did not expire
494	 */
495	if (auth_getstate(as) & AUTH_ALLOW)
496		auth_check_expire(as);
497	if (more == 0)
498		return (auth_close(as));
499	return (auth_getstate(as) & AUTH_ALLOW);
500}
501DEF_WEAK(auth_userresponse);
502
503/*
504 * Authenticate name with the specified style.
505 * If ``as'' is NULL a new session is formed with the default service.
506 * Returns NULL only if ``as'' is NULL and we were unable to allocate
507 * a new session.
508 *
509 * Use auth_close() or auth_getstate() to determine if the authentication
510 * worked.
511 */
512auth_session_t *
513auth_verify(auth_session_t *as, char *style, char *name, ...)
514{
515	va_list ap;
516	char path[PATH_MAX];
517
518	if ((name == NULL || style == NULL) && as == NULL)
519		return (NULL);
520
521	if (as == NULL && (as = auth_open()) == NULL)
522		return (NULL);
523	auth_setstate(as, 0);
524
525	if (style != NULL && auth_setitem(as, AUTHV_STYLE, style) < 0)
526		return (as);
527
528	if (name != NULL && auth_setitem(as, AUTHV_NAME, name) < 0)
529		return (as);
530
531	style = auth_getitem(as, AUTHV_STYLE);
532	name = auth_getitem(as, AUTHV_NAME);
533	if (!_auth_validuser(name))
534		return (as);
535
536	snprintf(path, sizeof(path), _PATH_AUTHPROG "%s", style);
537	va_start(ap, name);
538	auth_set_va_list(as, ap);
539	auth_call(as, path, auth_getitem(as, AUTHV_STYLE), "-s",
540	    auth_getitem(as, AUTHV_SERVICE), "--", name, (char *)NULL);
541	va_end(ap);
542	return (as);
543}
544DEF_WEAK(auth_verify);
545