1 /*	$OpenBSD: login_cap.c,v 1.40 2021/10/24 14:40:30 deraadt Exp $	*/
2 
3 /*
4  * Copyright (c) 2000-2004 Todd C. Miller <millert@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 /*-
19  * Copyright (c) 1995,1997 Berkeley Software Design, Inc. All rights reserved.
20  *
21  * Redistribution and use in source and binary forms, with or without
22  * modification, are permitted provided that the following conditions
23  * are met:
24  * 1. Redistributions of source code must retain the above copyright
25  *    notice, this list of conditions and the following disclaimer.
26  * 2. Redistributions in binary form must reproduce the above copyright
27  *    notice, this list of conditions and the following disclaimer in the
28  *    documentation and/or other materials provided with the distribution.
29  * 3. All advertising materials mentioning features or use of this software
30  *    must display the following acknowledgement:
31  *	This product includes software developed by Berkeley Software Design,
32  *	Inc.
33  * 4. The name of Berkeley Software Design, Inc.  may not be used to endorse
34  *    or promote products derived from this software without specific prior
35  *    written permission.
36  *
37  * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``AS IS'' AND
38  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
39  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
40  * ARE DISCLAIMED.  IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN, INC. BE LIABLE
41  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
42  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
43  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
44  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
45  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
46  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47  * SUCH DAMAGE.
48  *
49  *	BSDI $From: login_cap.c,v 2.16 2000/03/22 17:10:55 donn Exp $
50  */
51 #include <sys/types.h>
52 #include <sys/stat.h>
53 #include <sys/time.h>
54 #include <sys/resource.h>
55 
56 #include <err.h>
57 #include <errno.h>
58 #include <fcntl.h>
59 #include <limits.h>
60 #include <login_cap.h>
61 #include <paths.h>
62 #include <pwd.h>
63 #include <stdio.h>
64 #include <stdlib.h>
65 #include <string.h>
66 #include <syslog.h>
67 #include <unistd.h>
68 
69 
70 static	char *_authtypes[] = { LOGIN_DEFSTYLE, 0 };
71 static	char *expandstr(const char *, const struct passwd *, int);
72 static	int login_setenv(char *, char *, const struct passwd *, int);
73 static	int setuserenv(login_cap_t *, const struct passwd *);
74 static	int setuserpath(login_cap_t *, const struct passwd *);
75 static	u_quad_t multiply(u_quad_t, u_quad_t);
76 static	u_quad_t strtolimit(char *, char **, int);
77 static	u_quad_t strtosize(char *, char **, int);
78 static	int gsetrl(login_cap_t *, int, char *, int);
79 
80 login_cap_t *
login_getclass(char * class)81 login_getclass(char *class)
82 {
83 	char *classfiles[2] = {_PATH_LOGIN_CONF, NULL};
84 	login_cap_t *lc;
85 	int res;
86 
87 	if ((lc = calloc(1, sizeof(login_cap_t))) == NULL) {
88 		syslog(LOG_ERR, "%s:%d malloc: %m", __FILE__, __LINE__);
89 		return (0);
90 	}
91 
92 	if (class == NULL || class[0] == '\0')
93 		class = LOGIN_DEFCLASS;
94 
95     	if ((lc->lc_class = strdup(class)) == NULL) {
96 		syslog(LOG_ERR, "%s:%d strdup: %m", __FILE__, __LINE__);
97 		free(lc);
98 		return (0);
99 	}
100 
101 	if ((res = cgetent(&lc->lc_cap, classfiles, lc->lc_class)) != 0) {
102 		lc->lc_cap = 0;
103 		switch (res) {
104 		case 1:
105 			syslog(LOG_ERR, "%s: couldn't resolve 'tc'",
106 				lc->lc_class);
107 			break;
108 		case -1:
109 			if ((res = open(classfiles[0], O_RDONLY)) >= 0)
110 				close(res);
111 			if (strcmp(lc->lc_class, LOGIN_DEFCLASS) == 0 &&
112 			    res < 0)
113 				return (lc);
114 			syslog(LOG_ERR, "%s: unknown class", lc->lc_class);
115 			break;
116 		case -2:
117 			/*
118 			 * A missing login.conf file is not an error condition.
119 			 * The individual routines deal reasonably with missing
120 			 * capabilities and use default values.
121 			 */
122 			if (errno == ENOENT)
123 				return (lc);
124 			syslog(LOG_ERR, "%s: getting class information: %m",
125 			    lc->lc_class);
126 			break;
127 		case -3:
128 			syslog(LOG_ERR, "%s: 'tc' reference loop",
129 				lc->lc_class);
130 			break;
131 		default:
132 			syslog(LOG_ERR, "%s: unexpected cgetent error",
133 				lc->lc_class);
134 			break;
135 		}
136 		free(lc->lc_class);
137 		free(lc);
138 		return (0);
139 	}
140 	return (lc);
141 }
142 DEF_WEAK(login_getclass);
143 
144 char *
login_getstyle(login_cap_t * lc,char * style,char * atype)145 login_getstyle(login_cap_t *lc, char *style, char *atype)
146 {
147     	char **authtypes = _authtypes;
148 	char *auths, *ta;
149     	char *f1 = NULL, **f2 = NULL;
150 	int i;
151 
152 	/* Silently convert 's/key' -> 'skey' */
153 	if (style && strcmp(style, "s/key") == 0)
154 		style = "skey";
155 
156 	free(lc->lc_style);
157 	lc->lc_style = NULL;
158 
159     	if (!atype || !(auths = login_getcapstr(lc, atype, NULL, NULL)))
160 		auths = login_getcapstr(lc, "auth", NULL, NULL);
161 
162 	if (auths) {
163 		f1 = ta = auths;	/* auths malloced by login_getcapstr */
164 		i = 2;
165 		while (*ta)
166 			if (*ta++ == ',')
167 				++i;
168 		f2 = authtypes = calloc(sizeof(char *), i);
169 		if (!authtypes) {
170 			syslog(LOG_ERR, "malloc: %m");
171 			free(f1);
172 			return (0);
173 		}
174 		i = 0;
175 		while (*auths) {
176 			authtypes[i] = auths;
177 			while (*auths && *auths != ',')
178 				++auths;
179 			if (*auths)
180 				*auths++ = 0;
181 			if (!*authtypes[i])
182 				authtypes[i] = LOGIN_DEFSTYLE;
183 			++i;
184 		}
185 		authtypes[i] = 0;
186 	}
187 
188 	if (!style)
189 		style = authtypes[0];
190 
191 	while (*authtypes && strcmp(style, *authtypes))
192 		++authtypes;
193 
194 	if (*authtypes) {
195 		lc->lc_style = strdup(*authtypes);
196 		if (lc->lc_style == NULL)
197 			syslog(LOG_ERR, "strdup: %m");
198 	}
199 	free(f1);
200 	free(f2);
201 	return (lc->lc_style);
202 }
203 DEF_WEAK(login_getstyle);
204 
205 char *
login_getcapstr(login_cap_t * lc,char * cap,char * def,char * e)206 login_getcapstr(login_cap_t *lc, char *cap, char *def, char *e)
207 {
208 	char *res = NULL, *str = e;
209 	int stat;
210 
211 	errno = 0;
212 
213     	if (!lc->lc_cap)
214 		return (def);
215 
216 	switch (stat = cgetstr(lc->lc_cap, cap, &res)) {
217 	case -1:
218 		str = def;
219 		break;
220 	case -2:
221 		syslog(LOG_ERR, "%s: getting capability %s: %m",
222 		    lc->lc_class, cap);
223 		break;
224 	default:
225 		if (stat >= 0)
226 			str = res;
227 		else
228 			syslog(LOG_ERR,
229 			    "%s: unexpected error with capability %s",
230 			    lc->lc_class, cap);
231 		break;
232 	}
233 
234 	if (res != NULL && str != res)
235 		free(res);
236 	return(str);
237 }
238 DEF_WEAK(login_getcapstr);
239 
240 quad_t
login_getcaptime(login_cap_t * lc,char * cap,quad_t def,quad_t e)241 login_getcaptime(login_cap_t *lc, char *cap, quad_t def, quad_t e)
242 {
243 	char *ep;
244 	char *res = NULL, *sres;
245 	int stat;
246 	quad_t q, r;
247 
248 	errno = 0;
249 
250     	if (!lc->lc_cap)
251 		return (def);
252 
253 	switch (stat = cgetstr(lc->lc_cap, cap, &res)) {
254 	case -1:
255 		free(res);
256 		return (def);
257 	case -2:
258 		free(res);
259 		syslog(LOG_ERR, "%s: getting capability %s: %m",
260 		    lc->lc_class, cap);
261 		errno = ERANGE;
262 		return (e);
263 	default:
264 		if (stat >= 0)
265 			break;
266 		free(res);
267 		syslog(LOG_ERR, "%s: unexpected error with capability %s",
268 		    lc->lc_class, cap);
269 		errno = ERANGE;
270 		return (e);
271 	}
272 
273 	errno = 0;
274 
275 	if (strcasecmp(res, "infinity") == 0) {
276 		free(res);
277 		return (RLIM_INFINITY);
278 	}
279 
280 	q = 0;
281 	sres = res;
282 	while (*res) {
283 		r = strtoll(res, &ep, 0);
284 		if (!ep || ep == res ||
285 		    ((r == QUAD_MIN || r == QUAD_MAX) && errno == ERANGE)) {
286 invalid:
287 			syslog(LOG_ERR, "%s:%s=%s: invalid time",
288 			    lc->lc_class, cap, sres);
289 			free(sres);
290 			errno = ERANGE;
291 			return (e);
292 		}
293 		switch (*ep++) {
294 		case '\0':
295 			--ep;
296 			break;
297 		case 's': case 'S':
298 			break;
299 		case 'm': case 'M':
300 			r *= 60;
301 			break;
302 		case 'h': case 'H':
303 			r *= 60 * 60;
304 			break;
305 		case 'd': case 'D':
306 			r *= 60 * 60 * 24;
307 			break;
308 		case 'w': case 'W':
309 			r *= 60 * 60 * 24 * 7;
310 			break;
311 		case 'y': case 'Y':	/* Pretty absurd */
312 			r *= 60 * 60 * 24 * 365;
313 			break;
314 		default:
315 			goto invalid;
316 		}
317 		res = ep;
318 		q += r;
319 	}
320 	free(sres);
321 	return (q);
322 }
323 DEF_WEAK(login_getcaptime);
324 
325 quad_t
login_getcapnum(login_cap_t * lc,char * cap,quad_t def,quad_t e)326 login_getcapnum(login_cap_t *lc, char *cap, quad_t def, quad_t e)
327 {
328 	char *ep;
329 	char *res = NULL;
330 	int stat;
331 	quad_t q;
332 
333 	errno = 0;
334 
335     	if (!lc->lc_cap)
336 		return (def);
337 
338 	switch (stat = cgetstr(lc->lc_cap, cap, &res)) {
339 	case -1:
340 		free(res);
341 		return (def);
342 	case -2:
343 		free(res);
344 		syslog(LOG_ERR, "%s: getting capability %s: %m",
345 		    lc->lc_class, cap);
346 		errno = ERANGE;
347 		return (e);
348 	default:
349 		if (stat >= 0)
350 			break;
351 		free(res);
352 		syslog(LOG_ERR, "%s: unexpected error with capability %s",
353 		    lc->lc_class, cap);
354 		errno = ERANGE;
355 		return (e);
356 	}
357 
358 	errno = 0;
359 
360 	if (strcasecmp(res, "infinity") == 0) {
361 		free(res);
362 		return (RLIM_INFINITY);
363 	}
364 
365     	q = strtoll(res, &ep, 0);
366 	if (!ep || ep == res || ep[0] ||
367 	    ((q == QUAD_MIN || q == QUAD_MAX) && errno == ERANGE)) {
368 		syslog(LOG_ERR, "%s:%s=%s: invalid number",
369 		    lc->lc_class, cap, res);
370 		free(res);
371 		errno = ERANGE;
372 		return (e);
373 	}
374 	free(res);
375 	return (q);
376 }
377 DEF_WEAK(login_getcapnum);
378 
379 quad_t
login_getcapsize(login_cap_t * lc,char * cap,quad_t def,quad_t e)380 login_getcapsize(login_cap_t *lc, char *cap, quad_t def, quad_t e)
381 {
382 	char *ep;
383 	char *res = NULL;
384 	int stat;
385 	quad_t q;
386 
387 	errno = 0;
388 
389     	if (!lc->lc_cap)
390 		return (def);
391 
392 	switch (stat = cgetstr(lc->lc_cap, cap, &res)) {
393 	case -1:
394 		free(res);
395 		return (def);
396 	case -2:
397 		free(res);
398 		syslog(LOG_ERR, "%s: getting capability %s: %m",
399 		    lc->lc_class, cap);
400 		errno = ERANGE;
401 		return (e);
402 	default:
403 		if (stat >= 0)
404 			break;
405 		free(res);
406 		syslog(LOG_ERR, "%s: unexpected error with capability %s",
407 		    lc->lc_class, cap);
408 		errno = ERANGE;
409 		return (e);
410 	}
411 
412 	errno = 0;
413 	q = strtolimit(res, &ep, 0);
414 	if (!ep || ep == res || (ep[0] && ep[1]) ||
415 	    ((q == QUAD_MIN || q == QUAD_MAX) && errno == ERANGE)) {
416 		syslog(LOG_ERR, "%s:%s=%s: invalid size",
417 		    lc->lc_class, cap, res);
418 		free(res);
419 		errno = ERANGE;
420 		return (e);
421 	}
422 	free(res);
423 	return (q);
424 }
425 DEF_WEAK(login_getcapsize);
426 
427 int
login_getcapbool(login_cap_t * lc,char * cap,u_int def)428 login_getcapbool(login_cap_t *lc, char *cap, u_int def)
429 {
430     	if (!lc->lc_cap)
431 		return (def);
432 
433 	return (cgetcap(lc->lc_cap, cap, ':') != NULL);
434 }
435 DEF_WEAK(login_getcapbool);
436 
437 void
login_close(login_cap_t * lc)438 login_close(login_cap_t *lc)
439 {
440 	if (lc) {
441 		free(lc->lc_class);
442 		free(lc->lc_cap);
443 		free(lc->lc_style);
444 		free(lc);
445 	}
446 }
447 DEF_WEAK(login_close);
448 
449 #define	CTIME	1
450 #define	CSIZE	2
451 #define	CNUMB	3
452 
453 static struct {
454 	int	what;
455 	int	type;
456 	char *	name;
457 } r_list[] = {
458 	{ RLIMIT_CPU,		CTIME, "cputime", },
459 	{ RLIMIT_FSIZE,		CSIZE, "filesize", },
460 	{ RLIMIT_DATA,		CSIZE, "datasize", },
461 	{ RLIMIT_STACK,		CSIZE, "stacksize", },
462 	{ RLIMIT_RSS,		CSIZE, "memoryuse", },
463 	{ RLIMIT_MEMLOCK,	CSIZE, "memorylocked", },
464 	{ RLIMIT_NPROC,		CNUMB, "maxproc", },
465 	{ RLIMIT_NOFILE,	CNUMB, "openfiles", },
466 	{ RLIMIT_CORE,		CSIZE, "coredumpsize", },
467 #ifdef RLIMIT_VMEM
468 	{ RLIMIT_VMEM,		CSIZE, "vmemoryuse", },
469 #endif
470 	{ -1, 0, 0 }
471 };
472 
473 static int
gsetrl(login_cap_t * lc,int what,char * name,int type)474 gsetrl(login_cap_t *lc, int what, char *name, int type)
475 {
476 	struct rlimit rl;
477 	struct rlimit r;
478 	char name_cur[32];
479 	char name_max[32];
480     	char *v;
481 	int len;
482 
483 	/*
484 	 * If we have no capabilities then there is nothing to do and
485 	 * we can just return success.
486 	 */
487 	if (lc->lc_cap == NULL)
488 		return (0);
489 
490 	len = snprintf(name_cur, sizeof name_cur, "%s-cur", name);
491 	if (len < 0 || len >= sizeof name_cur) {
492 		syslog(LOG_ERR, "current resource limit name too large");
493 		return (-1);
494 	}
495 	len = snprintf(name_max, sizeof name_max, "%s-max", name);
496 	if (len < 0 || len >= sizeof name_max) {
497 		syslog(LOG_ERR, "max resource limit name too large");
498 		return (-1);
499 	}
500 
501 	if (getrlimit(what, &r)) {
502 		syslog(LOG_ERR, "getting resource limit: %m");
503 		return (-1);
504 	}
505 
506 	/*
507 	 * We need to pre-fetch the 3 possible strings we will look
508 	 * up to see what order they come in.  If the one without
509 	 * the -cur or -max comes in first then we ignore any later
510 	 * -cur or -max entries.
511 	 * Note that the cgetent routines will always return failure
512 	 * on the entry "".  This will cause our login_get* routines
513 	 * to use the default entry.
514 	 */
515 	if ((v = cgetcap(lc->lc_cap, name, '=')) != NULL) {
516 		if (v < cgetcap(lc->lc_cap, name_cur, '='))
517 			name_cur[0] = '\0';
518 		if (v < cgetcap(lc->lc_cap, name_max, '='))
519 			name_max[0] = '\0';
520 	}
521 
522 #define	RCUR	r.rlim_cur
523 #define	RMAX	r.rlim_max
524 
525 	switch (type) {
526 	case CTIME:
527 		RCUR = (rlim_t)login_getcaptime(lc, name, RCUR, RCUR);
528 		RMAX = (rlim_t)login_getcaptime(lc, name, RMAX, RMAX);
529 		rl.rlim_cur = (rlim_t)login_getcaptime(lc, name_cur, RCUR, RCUR);
530 		rl.rlim_max = (rlim_t)login_getcaptime(lc, name_max, RMAX, RMAX);
531 		break;
532 	case CSIZE:
533 		RCUR = (rlim_t)login_getcapsize(lc, name, RCUR, RCUR);
534 		RMAX = (rlim_t)login_getcapsize(lc, name, RMAX, RMAX);
535 		rl.rlim_cur = (rlim_t)login_getcapsize(lc, name_cur, RCUR, RCUR);
536 		rl.rlim_max = (rlim_t)login_getcapsize(lc, name_max, RMAX, RMAX);
537 		break;
538 	case CNUMB:
539 		RCUR = (rlim_t)login_getcapnum(lc, name, RCUR, RCUR);
540 		RMAX = (rlim_t)login_getcapnum(lc, name, RMAX, RMAX);
541 		rl.rlim_cur = (rlim_t)login_getcapnum(lc, name_cur, RCUR, RCUR);
542 		rl.rlim_max = (rlim_t)login_getcapnum(lc, name_max, RMAX, RMAX);
543 		break;
544 	default:
545 		return (-1);
546 	}
547 
548 	if (setrlimit(what, &rl)) {
549 		syslog(LOG_ERR, "%s: setting resource limit %s: %m",
550 		    lc->lc_class, name);
551 		return (-1);
552 	}
553 #undef	RCUR
554 #undef	RMAX
555 	return (0);
556 }
557 
558 int
setclasscontext(char * class,u_int flags)559 setclasscontext(char *class, u_int flags)
560 {
561 	int ret;
562 	login_cap_t *lc;
563 
564 	flags &= LOGIN_SETRESOURCES | LOGIN_SETPRIORITY | LOGIN_SETUMASK |
565 	    LOGIN_SETPATH;
566 
567 	lc = login_getclass(class);
568 	ret = lc ? setusercontext(lc, NULL, 0, flags) : -1;
569 	login_close(lc);
570 	return (ret);
571 }
572 
573 int
setusercontext(login_cap_t * lc,struct passwd * pwd,uid_t uid,u_int flags)574 setusercontext(login_cap_t *lc, struct passwd *pwd, uid_t uid, u_int flags)
575 {
576 	login_cap_t *flc;
577 	quad_t p;
578 	int i;
579 
580 	flc = NULL;
581 
582 	if (!lc && !(flc = lc = login_getclass(pwd ? pwd->pw_class : NULL)))
583 		return (-1);
584 
585 	/*
586 	 * Without the pwd entry being passed we cannot set either
587 	 * the group or the login.  We could complain about it.
588 	 */
589 	if (pwd == NULL)
590 		flags &= ~(LOGIN_SETGROUP|LOGIN_SETLOGIN);
591 
592 	/*
593 	 * Verify that we haven't been given invalid values.
594 	 */
595 	if (flags & LOGIN_SETGROUP) {
596 		if (pwd->pw_gid == -1) {
597 			syslog(LOG_ERR, "setusercontext with invalid gid");
598 			login_close(flc);
599 			return (-1);
600 		}
601 	}
602 	if (flags & LOGIN_SETUSER) {
603 		if (uid == -1) {
604 			syslog(LOG_ERR, "setusercontext with invalid uid");
605 			login_close(flc);
606 			return (-1);
607 		}
608 	}
609 
610 	if (flags & LOGIN_SETRESOURCES)
611 		for (i = 0; r_list[i].name; ++i)
612 			if (gsetrl(lc, r_list[i].what, r_list[i].name,
613 			    r_list[i].type))
614 				/* XXX - call syslog()? */;
615 
616 	if (flags & LOGIN_SETPRIORITY) {
617 		p = login_getcapnum(lc, "priority", 0, 0);
618 
619 		if (setpriority(PRIO_PROCESS, 0, (int)p) == -1)
620 			syslog(LOG_ERR, "%s: setpriority: %m", lc->lc_class);
621 	}
622 
623 	if (flags & LOGIN_SETUMASK) {
624 		p = login_getcapnum(lc, "umask", LOGIN_DEFUMASK,LOGIN_DEFUMASK);
625 		umask((mode_t)p);
626 	}
627 
628 	if (flags & LOGIN_SETGROUP) {
629 		if (setresgid(pwd->pw_gid, pwd->pw_gid, pwd->pw_gid) == -1) {
630 			syslog(LOG_ERR, "setresgid(%u,%u,%u): %m",
631 			    pwd->pw_gid, pwd->pw_gid, pwd->pw_gid);
632 			login_close(flc);
633 			return (-1);
634 		}
635 
636 		if (initgroups(pwd->pw_name, pwd->pw_gid) == -1) {
637 			syslog(LOG_ERR, "initgroups(%s,%u): %m",
638 			    pwd->pw_name, pwd->pw_gid);
639 			login_close(flc);
640 			return (-1);
641 		}
642 	}
643 
644 	if (flags & LOGIN_SETLOGIN)
645 		if (setlogin(pwd->pw_name) == -1) {
646 			syslog(LOG_ERR, "setlogin(%s) failure: %m",
647 			    pwd->pw_name);
648 			login_close(flc);
649 			return (-1);
650 		}
651 
652 	if (flags & LOGIN_SETUSER) {
653 		if (setresuid(uid, uid, uid) == -1) {
654 			syslog(LOG_ERR, "setresuid(%u,%u,%u): %m",
655 			    uid, uid, uid);
656 			login_close(flc);
657 			return (-1);
658 		}
659 	}
660 
661 	if (flags & LOGIN_SETENV) {
662 		if (setuserenv(lc, pwd) == -1) {
663 			syslog(LOG_ERR, "could not set user environment: %m");
664 			login_close(flc);
665 			return (-1);
666 		}
667 	}
668 
669 	if (flags & LOGIN_SETPATH) {
670 		if (setuserpath(lc, pwd) == -1) {
671 			syslog(LOG_ERR, "could not set PATH: %m");
672 			login_close(flc);
673 			return (-1);
674 		}
675 	}
676 
677 	login_close(flc);
678 	return (0);
679 }
680 DEF_WEAK(setusercontext);
681 
682 /*
683  * Look up "path" for this user in login.conf and replace whitespace
684  * with ':' while expanding '~' and '$'.  Sets the PATH environment
685  * variable to the result or _PATH_DEFPATH on error.
686  */
687 static int
setuserpath(login_cap_t * lc,const struct passwd * pwd)688 setuserpath(login_cap_t *lc, const struct passwd *pwd)
689 {
690 	char *path = NULL, *opath = NULL, *op, *np;
691 	int len, error;
692 
693 	if (lc->lc_cap == NULL)
694 		goto setit;		/* impossible */
695 
696 	if ((len = cgetustr(lc->lc_cap, "path", &opath)) <= 0)
697 		goto setit;
698 
699 	if ((path = malloc(len + 1)) == NULL)
700 		goto setit;
701 
702 	/* Convert opath from space-separated to colon-separated path. */
703 	for (op = opath, np = path; *op != '\0'; ) {
704 		switch (*op) {
705 		case ' ':
706 		case '\t':
707 			/*
708 			 * Collapse consecutive spaces and trim any space
709 			 * at the very end.
710 			 */
711 			do {
712 				op++;
713 			} while (*op == ' ' || *op == '\t');
714 			if (*op != '\0')
715 				*np++ = ':';
716 			break;
717 		case '\\':
718 			/* check for escaped whitespace */
719 			if (*(op + 1) == ' ' || *(op + 1) == '\t')
720 				*np++ = *op++;
721 			/* FALLTHROUGH */
722 		default:
723 			*np++ = *op++;
724 			break;
725 		}
726 
727 	}
728 	*np = '\0';
729 setit:
730 	error = login_setenv("PATH", path ? path : _PATH_DEFPATH, pwd, 1);
731 	free(opath);
732 	free(path);
733 	return (error);
734 }
735 
736 /*
737  * Look up "setenv" for this user in login.conf and set the comma-separated
738  * list of environment variables, expanding '~' and '$'.
739  */
740 static int
setuserenv(login_cap_t * lc,const struct passwd * pwd)741 setuserenv(login_cap_t *lc, const struct passwd *pwd)
742 {
743 	char *beg, *end, *ep, *list, *value;
744 	int len, error;
745 
746 	if (lc->lc_cap == NULL)
747 		return (-1);		/* impossible */
748 
749 	if ((len = cgetustr(lc->lc_cap, "setenv", &list)) <= 0)
750 		return (0);
751 
752 	for (beg = end = list, ep = list + len + 1; end < ep; end++) {
753 		switch (*end) {
754 		case '\\':
755 			if (*(end + 1) == ',')
756 				end++;	/* skip escaped comma */
757 			continue;
758 		case ',':
759 		case '\0':
760 			*end = '\0';
761 			if (beg == end) {
762 				beg++;
763 				continue;
764 			}
765 			break;
766 		default:
767 			continue;
768 		}
769 
770 		if ((value = strchr(beg, '=')) != NULL)
771 			*value++ = '\0';
772 		else
773 			value = "";
774 		if ((error = login_setenv(beg, value, pwd, 0)) != 0) {
775 			free(list);
776 			return (error);
777 		}
778 		beg = end + 1;
779 	}
780 	free(list);
781 	return (0);
782 }
783 
784 /*
785  * Set an environment variable, substituting for ~ and $
786  */
787 static int
login_setenv(char * name,char * ovalue,const struct passwd * pwd,int ispath)788 login_setenv(char *name, char *ovalue, const struct passwd *pwd, int ispath)
789 {
790 	char *value = NULL;
791 	int error;
792 
793 	if (*ovalue != '\0')
794 		value = expandstr(ovalue, pwd, ispath);
795 	error = setenv(name, value ? value : ovalue, 1);
796 	free(value);
797 	return (error);
798 }
799 
800 /*
801  * Convert an expression of the following forms
802  * 	1) A number.
803  *	2) A number followed by a b (mult by 512).
804  *	3) A number followed by a k (mult by 1024).
805  *	5) A number followed by a m (mult by 1024 * 1024).
806  *	6) A number followed by a g (mult by 1024 * 1024 * 1024).
807  *	7) A number followed by a t (mult by 1024 * 1024 * 1024 * 1024).
808  *	8) Two or more numbers (with/without k,b,m,g, or t).
809  *	   separated by x (also * for backwards compatibility), specifying
810  *	   the product of the indicated values.
811  */
812 static
813 u_quad_t
strtosize(char * str,char ** endptr,int radix)814 strtosize(char *str, char **endptr, int radix)
815 {
816 	u_quad_t num, num2;
817 	char *expr, *expr2;
818 
819 	errno = 0;
820 	num = strtoull(str, &expr, radix);
821 	if (errno || expr == str) {
822 		if (endptr)
823 			*endptr = expr;
824 		return (num);
825 	}
826 
827 	switch(*expr) {
828 	case 'b': case 'B':
829 		num = multiply(num, (u_quad_t)512);
830 		++expr;
831 		break;
832 	case 'k': case 'K':
833 		num = multiply(num, (u_quad_t)1024);
834 		++expr;
835 		break;
836 	case 'm': case 'M':
837 		num = multiply(num, (u_quad_t)1024 * 1024);
838 		++expr;
839 		break;
840 	case 'g': case 'G':
841 		num = multiply(num, (u_quad_t)1024 * 1024 * 1024);
842 		++expr;
843 		break;
844 	case 't': case 'T':
845 		num = multiply(num, (u_quad_t)1024 * 1024);
846 		num = multiply(num, (u_quad_t)1024 * 1024);
847 		++expr;
848 		break;
849 	}
850 
851 	if (errno)
852 		goto erange;
853 
854 	switch(*expr) {
855 	case '*':			/* Backward compatible. */
856 	case 'x':
857 		num2 = strtosize(expr+1, &expr2, radix);
858 		if (errno) {
859 			expr = expr2;
860 			goto erange;
861 		}
862 
863 		if (expr2 == expr + 1) {
864 			if (endptr)
865 				*endptr = expr;
866 			return (num);
867 		}
868 		expr = expr2;
869 		num = multiply(num, num2);
870 		if (errno)
871 			goto erange;
872 		break;
873 	}
874 	if (endptr)
875 		*endptr = expr;
876 	return (num);
877 erange:
878 	if (endptr)
879 		*endptr = expr;
880 	errno = ERANGE;
881 	return (UQUAD_MAX);
882 }
883 
884 static
885 u_quad_t
strtolimit(char * str,char ** endptr,int radix)886 strtolimit(char *str, char **endptr, int radix)
887 {
888 	if (strcasecmp(str, "infinity") == 0 || strcasecmp(str, "inf") == 0) {
889 		if (endptr)
890 			*endptr = str + strlen(str);
891 		return ((u_quad_t)RLIM_INFINITY);
892 	}
893 	return (strtosize(str, endptr, radix));
894 }
895 
896 static u_quad_t
multiply(u_quad_t n1,u_quad_t n2)897 multiply(u_quad_t n1, u_quad_t n2)
898 {
899 	static int bpw = 0;
900 	u_quad_t m;
901 	u_quad_t r;
902 	int b1, b2;
903 
904 	/*
905 	 * Get rid of the simple cases
906 	 */
907 	if (n1 == 0 || n2 == 0)
908 		return (0);
909 	if (n1 == 1)
910 		return (n2);
911 	if (n2 == 1)
912 		return (n1);
913 
914 	/*
915 	 * sizeof() returns number of bytes needed for storage.
916 	 * This may be different from the actual number of useful bits.
917 	 */
918 	if (!bpw) {
919 		bpw = sizeof(u_quad_t) * 8;
920 		while (((u_quad_t)1 << (bpw-1)) == 0)
921 			--bpw;
922 	}
923 
924 	/*
925 	 * First check the magnitude of each number.  If the sum of the
926 	 * magnatude is way to high, reject the number.  (If this test
927 	 * is not done then the first multiply below may overflow.)
928 	 */
929 	for (b1 = bpw; (((u_quad_t)1 << (b1-1)) & n1) == 0; --b1)
930 		;
931 	for (b2 = bpw; (((u_quad_t)1 << (b2-1)) & n2) == 0; --b2)
932 		;
933 	if (b1 + b2 - 2 > bpw) {
934 		errno = ERANGE;
935 		return (UQUAD_MAX);
936 	}
937 
938 	/*
939 	 * Decompose the multiplication to be:
940 	 * h1 = n1 & ~1
941 	 * h2 = n2 & ~1
942 	 * l1 = n1 & 1
943 	 * l2 = n2 & 1
944 	 * (h1 + l1) * (h2 + l2)
945 	 * (h1 * h2) + (h1 * l2) + (l1 * h2) + (l1 * l2)
946 	 *
947 	 * Since h1 && h2 do not have the low bit set, we can then say:
948 	 *
949 	 * (h1>>1 * h2>>1 * 4) + ...
950 	 *
951 	 * So if (h1>>1 * h2>>1) > (1<<(bpw - 2)) then the result will
952 	 * overflow.
953 	 *
954 	 * Finally, if MAX - ((h1 * l2) + (l1 * h2) + (l1 * l2)) < (h1*h2)
955 	 * then adding in residual amount will cause an overflow.
956 	 */
957 
958 	m = (n1 >> 1) * (n2 >> 1);
959 
960 	if (m >= ((u_quad_t)1 << (bpw-2))) {
961 		errno = ERANGE;
962 		return (UQUAD_MAX);
963 	}
964 
965 	m *= 4;
966 
967 	r = (n1 & n2 & 1)
968 	  + (n2 & 1) * (n1 & ~(u_quad_t)1)
969 	  + (n1 & 1) * (n2 & ~(u_quad_t)1);
970 
971 	if ((u_quad_t)(m + r) < m) {
972 		errno = ERANGE;
973 		return (UQUAD_MAX);
974 	}
975 	m += r;
976 
977 	return (m);
978 }
979 
980 /*
981  * Check whether or not a tilde in a string should be expanded.
982  * We only do expansion for things like "~", "~/...", ~me", "~me/...".
983  * Additionally, for paths the tilde must be a the beginning.
984  */
985 #define tilde_valid(s, b, u, l, ip) \
986     ((!(ip) || (s) == (b) || (s)[-1] == ':') && \
987     ((s)[1] == '/' || (s)[1] == '\0' || \
988     (strncmp((s)+1, u, l) == 0 && ((s)[l+1] == '/' || (s)[l+1] == '\0'))))
989 
990 /*
991  * Make a copy of a string, expanding '~' to the user's homedir, '$' to the
992  * login name and other escape sequences as per cgetstr(3).
993  */
994 static char *
expandstr(const char * ostr,const struct passwd * pwd,int ispath)995 expandstr(const char *ostr, const struct passwd *pwd, int ispath)
996 {
997 	size_t n, olen, nlen, ulen, dlen;
998 	const char *ep, *eo, *op;
999 	char *nstr, *np;
1000 	int ch;
1001 
1002 	if (pwd != NULL) {
1003 		ulen = strlen(pwd->pw_name);
1004 		dlen = strlen(pwd->pw_dir);
1005 	}
1006 
1007 	/* calculate the size of the new string */
1008 	olen = nlen = strlen(ostr);
1009 	for (op = ostr, ep = ostr + olen; op < ep; op++) {
1010 		switch (*op) {
1011 		case '~':
1012 			if (pwd == NULL ||
1013 			    !tilde_valid(op, ostr, pwd->pw_name, ulen, ispath))
1014 				break;
1015 			if (op[1] != '/' && op[1] != '\0') {
1016 				op += ulen;	/* ~username */
1017 				nlen = nlen - ulen - 1 + dlen;
1018 			} else
1019 				nlen += dlen - 1;
1020 			break;
1021 		case '$':
1022 			if (pwd != NULL)
1023 				nlen += ulen - 1;
1024 			break;
1025 		case '^':
1026 			/* control char */
1027 			if (*++op != '\0')
1028 				nlen--;
1029 			break;
1030 		case '\\':
1031 			if (op[1] == '\0')
1032 				break;
1033 			/*
1034 			 * Byte in octal notation (\123) or an escaped char (\t)
1035 			 */
1036 			eo = op + 4;
1037 			do {
1038 				op++;
1039 				nlen--;
1040 			} while (op < eo && *op >= '0' && *op <= '7');
1041 			break;
1042 		}
1043 	}
1044 	if ((np = nstr = malloc(++nlen)) == NULL)
1045 		return (NULL);
1046 
1047 	for (op = ostr, ep = ostr + olen; op < ep; op++) {
1048 		switch ((ch = *op)) {
1049 		case '~':
1050 			if (pwd == NULL ||
1051 			    !tilde_valid(op, ostr, pwd->pw_name, ulen, ispath))
1052 				break;
1053 			if (op[1] != '/' && op[1] != '\0')
1054 				op += ulen;	/* ~username */
1055 			strlcpy(np, pwd->pw_dir, nlen);
1056 			nlen -= dlen;
1057 			np += dlen;
1058 			continue;
1059 		case '$':
1060 			if (pwd == NULL)
1061 				break;
1062 			strlcpy(np, pwd->pw_name, nlen);
1063 			nlen -= ulen;
1064 			np += ulen;
1065 			continue;
1066 		case '^':
1067 			if (op[1] != '\0')
1068 				ch = *++op & 037;
1069 			break;
1070 		case '\\':
1071 			if (op[1] == '\0')
1072 				break;
1073 			switch(*++op) {
1074 			case '0': case '1': case '2': case '3':
1075 			case '4': case '5': case '6': case '7':
1076 				/* byte in octal up to 3 digits long */
1077 				ch = 0;
1078 				n = 3;
1079 				do {
1080 					ch = ch * 8 + (*op++ - '0');
1081 				} while (--n && *op >= '0' && *op <= '7');
1082 				break;
1083 			case 'b': case 'B':
1084 				ch = '\b';
1085 				break;
1086 			case 't': case 'T':
1087 				ch = '\t';
1088 				break;
1089 			case 'n': case 'N':
1090 				ch = '\n';
1091 				break;
1092 			case 'f': case 'F':
1093 				ch = '\f';
1094 				break;
1095 			case 'r': case 'R':
1096 				ch = '\r';
1097 				break;
1098 			case 'e': case 'E':
1099 				ch = '\033';
1100 				break;
1101 			case 'c': case 'C':
1102 				ch = ':';
1103 				break;
1104 			default:
1105 				ch = *op;
1106 				break;
1107 			}
1108 			break;
1109 		}
1110 		*np++ = ch;
1111 		nlen--;
1112 	}
1113 	*np = '\0';
1114 	return (nstr);
1115 }
1116