xref: /openbsd-src/lib/libc/gen/authenticate.c (revision 9b9d2a55a62c8e82206c25f94fcc7f4e2765250e)
1 /*	$OpenBSD: authenticate.c,v 1.22 2015/08/31 02:53:57 guenther 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 
55 static int _auth_checknologin(login_cap_t *, int);
56 
57 char *
58 auth_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 }
103 
104 void
105 auth_checknologin(login_cap_t *lc)
106 {
107 	if (_auth_checknologin(lc, 1))
108 		exit(1);
109 }
110 
111 static 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 
147 print_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 
159 int
160 auth_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 
173 int
174 auth_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 
293 out:
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 
304 auth_session_t *
305 auth_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 		explicit_bzero(password, 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 
359 int
360 auth_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 
369 auth_session_t *
370 auth_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 
421 int
422 auth_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 		explicit_bzero(response, 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  */
481 auth_session_t *
482 auth_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