1/*	$NetBSD: login_cap.c,v 1.33 2015/10/29 20:29:24 kamil Exp $	*/
2
3/*-
4 * Copyright (c) 1995,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 login_cap.c,v 2.13 1998/02/07 03:17:05 prb Exp
35 */
36
37#include <sys/cdefs.h>
38#if defined(LIBC_SCCS) && !defined(lint)
39__RCSID("$NetBSD: login_cap.c,v 1.33 2015/10/29 20:29:24 kamil Exp $");
40#endif /* LIBC_SCCS and not lint */
41
42#include <sys/types.h>
43#include <sys/stat.h>
44#include <sys/time.h>
45#include <sys/resource.h>
46#include <sys/param.h>
47
48#include <assert.h>
49#include <ctype.h>
50#include <err.h>
51#include <errno.h>
52#include <fcntl.h>
53#include <limits.h>
54#include <login_cap.h>
55#include <paths.h>
56#include <pwd.h>
57#include <stdio.h>
58#include <stdlib.h>
59#include <string.h>
60#include <syslog.h>
61#include <unistd.h>
62#include <util.h>
63
64static u_quad_t	multiply(u_quad_t, u_quad_t);
65static u_quad_t	strtolimit(const char *, char **, int);
66static u_quad_t	strtosize(const char *, char **, int);
67static int	gsetrl(login_cap_t *, int, const char *, int type);
68static int	isinfinite(const char *);
69static int	envset(void *, const char *, const char *, int);
70
71login_cap_t *
72login_getclass(const char *class)
73{
74	const char *classfiles[2];
75	login_cap_t *lc;
76	int res;
77
78	/* class may be NULL */
79
80	if (secure_path(_PATH_LOGIN_CONF) == 0) {
81		classfiles[0] = _PATH_LOGIN_CONF;
82		classfiles[1] = NULL;
83	} else {
84		classfiles[0] = NULL;
85	}
86
87	if ((lc = malloc(sizeof(login_cap_t))) == NULL) {
88		syslog(LOG_ERR, "%s:%d malloc: %m", __FILE__, __LINE__);
89		return (0);
90	}
91
92	lc->lc_cap = 0;
93	lc->lc_style = 0;
94
95	if (class == NULL || class[0] == '\0')
96		class = LOGIN_DEFCLASS;
97
98    	if ((lc->lc_class = strdup(class)) == NULL) {
99		syslog(LOG_ERR, "%s:%d strdup: %m", __FILE__, __LINE__);
100		free(lc);
101		return (0);
102	}
103
104	/*
105	 * Not having a login.conf file is not an error condition.
106	 * The individual routines deal reasonably with missing
107	 * capabilities and use default values.
108	 */
109	if (classfiles[0] == NULL)
110		return(lc);
111
112	if ((res = cgetent(&lc->lc_cap, classfiles, lc->lc_class)) != 0) {
113		lc->lc_cap = 0;
114		switch (res) {
115		case 1:
116			syslog(LOG_ERR, "%s: couldn't resolve 'tc'",
117				lc->lc_class);
118			break;
119		case -1:
120			if (strcmp(lc->lc_class, LOGIN_DEFCLASS) == 0)
121				return (lc);
122			syslog(LOG_ERR, "%s: unknown class", lc->lc_class);
123			break;
124		case -2:
125			syslog(LOG_ERR, "%s: getting class information: %m",
126				lc->lc_class);
127			break;
128		case -3:
129			syslog(LOG_ERR, "%s: 'tc' reference loop",
130				lc->lc_class);
131			break;
132		default:
133			syslog(LOG_ERR, "%s: unexpected cgetent error",
134				lc->lc_class);
135			break;
136		}
137		free(lc->lc_class);
138		free(lc);
139		return (0);
140	}
141	return (lc);
142}
143
144login_cap_t *
145login_getpwclass(const struct passwd *pwd)
146{
147
148	/* pwd may be NULL */
149
150	return login_getclass(pwd ? pwd->pw_class : NULL);
151}
152
153char *
154login_getcapstr(login_cap_t *lc, const char *cap, char *def, char *e)
155{
156	char *res = NULL;
157	int status;
158
159	errno = 0;
160
161	_DIAGASSERT(cap != NULL);
162
163	if (!lc || !lc->lc_cap)
164		return (def);
165
166	switch (status = cgetstr(lc->lc_cap, cap, &res)) {
167	case -1:
168		if (res)
169			free(res);
170		return (def);
171	case -2:
172		syslog(LOG_ERR, "%s: getting capability %s: %m",
173		    lc->lc_class, cap);
174		if (res)
175			free(res);
176		return (e);
177	default:
178		if (status >= 0)
179			return (res);
180		syslog(LOG_ERR, "%s: unexpected error with capability %s",
181		    lc->lc_class, cap);
182		if (res)
183			free(res);
184		return (e);
185	}
186}
187
188quad_t
189login_getcaptime(login_cap_t *lc, const char *cap, quad_t def, quad_t e)
190{
191	char *ep;
192	char *res = NULL, *sres;
193	int status;
194	quad_t q, r;
195
196	_DIAGASSERT(cap != NULL);
197
198	errno = 0;
199	if (!lc || !lc->lc_cap)
200		return (def);
201
202	switch (status = cgetstr(lc->lc_cap, cap, &res)) {
203	case -1:
204		if (res)
205			free(res);
206		return (def);
207	case -2:
208		syslog(LOG_ERR, "%s: getting capability %s: %m",
209		    lc->lc_class, cap);
210		errno = ERANGE;
211		if (res)
212			free(res);
213		return (e);
214	default:
215		if (status >= 0)
216			break;
217		syslog(LOG_ERR, "%s: unexpected error with capability %s",
218		    lc->lc_class, cap);
219		errno = ERANGE;
220		if (res)
221			free(res);
222		return (e);
223	}
224
225	if (isinfinite(res))
226		return (RLIM_INFINITY);
227
228	errno = 0;
229
230	q = 0;
231	sres = res;
232	while (*res) {
233		r = strtoq(res, &ep, 0);
234		if (!ep || ep == res ||
235		    ((r == QUAD_MIN || r == QUAD_MAX) && errno == ERANGE)) {
236invalid:
237			syslog(LOG_ERR, "%s:%s=%s: invalid time",
238			    lc->lc_class, cap, sres);
239			errno = ERANGE;
240			free(sres);
241			return (e);
242		}
243		switch (*ep++) {
244		case '\0':
245			--ep;
246			break;
247		case 's': case 'S':
248			break;
249		case 'm': case 'M':
250			r *= 60;
251			break;
252		case 'h': case 'H':
253			r *= 60 * 60;
254			break;
255		case 'd': case 'D':
256			r *= 60 * 60 * 24;
257			break;
258		case 'w': case 'W':
259			r *= 60 * 60 * 24 * 7;
260			break;
261		case 'y': case 'Y':	/* Pretty absurd */
262			r *= 60 * 60 * 24 * 365;
263			break;
264		default:
265			goto invalid;
266		}
267		res = ep;
268		q += r;
269	}
270	free(sres);
271	return (q);
272}
273
274quad_t
275login_getcapnum(login_cap_t *lc, const char *cap, quad_t def, quad_t e)
276{
277	char *ep;
278	char *res = NULL;
279	int status;
280	quad_t q;
281
282	_DIAGASSERT(cap != NULL);
283
284	errno = 0;
285	if (!lc || !lc->lc_cap)
286		return (def);
287
288	switch (status = cgetstr(lc->lc_cap, cap, &res)) {
289	case -1:
290		if (res)
291			free(res);
292		return (def);
293	case -2:
294		syslog(LOG_ERR, "%s: getting capability %s: %m",
295		    lc->lc_class, cap);
296		errno = ERANGE;
297		if (res)
298			free(res);
299		return (e);
300	default:
301		if (status >= 0)
302			break;
303		syslog(LOG_ERR, "%s: unexpected error with capability %s",
304		    lc->lc_class, cap);
305		errno = ERANGE;
306		if (res)
307			free(res);
308		return (e);
309	}
310
311	if (isinfinite(res))
312		return (RLIM_INFINITY);
313
314	errno = 0;
315    	q = strtoq(res, &ep, 0);
316	if (!ep || ep == res || ep[0] ||
317	    ((q == QUAD_MIN || q == QUAD_MAX) && errno == ERANGE)) {
318		syslog(LOG_ERR, "%s:%s=%s: invalid number",
319		    lc->lc_class, cap, res);
320		errno = ERANGE;
321		free(res);
322		return (e);
323	}
324	free(res);
325	return (q);
326}
327
328quad_t
329login_getcapsize(login_cap_t *lc, const char *cap, quad_t def, quad_t e)
330{
331	char *ep;
332	char *res = NULL;
333	int status;
334	quad_t q;
335
336	_DIAGASSERT(cap != NULL);
337
338	errno = 0;
339
340	if (!lc || !lc->lc_cap)
341		return (def);
342
343	switch (status = cgetstr(lc->lc_cap, cap, &res)) {
344	case -1:
345		if (res)
346			free(res);
347		return (def);
348	case -2:
349		syslog(LOG_ERR, "%s: getting capability %s: %m",
350		    lc->lc_class, cap);
351		errno = ERANGE;
352		if (res)
353			free(res);
354		return (e);
355	default:
356		if (status >= 0)
357			break;
358		syslog(LOG_ERR, "%s: unexpected error with capability %s",
359		    lc->lc_class, cap);
360		errno = ERANGE;
361		if (res)
362			free(res);
363		return (e);
364	}
365
366	errno = 0;
367	q = strtolimit(res, &ep, 0);
368	if (!ep || ep == res || (ep[0] && ep[1]) ||
369	    ((q == QUAD_MIN || q == QUAD_MAX) && errno == ERANGE)) {
370		syslog(LOG_ERR, "%s:%s=%s: invalid size",
371		    lc->lc_class, cap, res);
372		errno = ERANGE;
373		free(res);
374		return (e);
375	}
376	free(res);
377	return (q);
378}
379
380int
381login_getcapbool(login_cap_t *lc, const char *cap, u_int def)
382{
383
384	_DIAGASSERT(cap != NULL);
385
386	if (!lc || !lc->lc_cap)
387		return (def);
388
389	return (cgetcap(lc->lc_cap, cap, ':') != NULL);
390}
391
392void
393login_close(login_cap_t *lc)
394{
395
396	if (lc) {
397		if (lc->lc_class)
398			free(lc->lc_class);
399		if (lc->lc_cap)
400			free(lc->lc_cap);
401		if (lc->lc_style)
402			free(lc->lc_style);
403		free(lc);
404	}
405}
406
407#define	R_CTIME	1
408#define	R_CSIZE	2
409#define	R_CNUMB	3
410
411static struct {
412	int	what;
413	int	type;
414	const char *name;
415} r_list[] = {
416	{ RLIMIT_CPU,		R_CTIME, "cputime", },
417	{ RLIMIT_FSIZE,		R_CSIZE, "filesize", },
418	{ RLIMIT_DATA,		R_CSIZE, "datasize", },
419	{ RLIMIT_STACK,		R_CSIZE, "stacksize", },
420	{ RLIMIT_RSS,		R_CSIZE, "memoryuse", },
421	{ RLIMIT_MEMLOCK,	R_CSIZE, "memorylocked", },
422	{ RLIMIT_NPROC,		R_CNUMB, "maxproc", },
423	{ RLIMIT_NTHR,		R_CNUMB, "maxthread", },
424	{ RLIMIT_NOFILE,	R_CNUMB, "openfiles", },
425	{ RLIMIT_CORE,		R_CSIZE, "coredumpsize", },
426	{ RLIMIT_SBSIZE,	R_CSIZE, "sbsize", },
427	{ RLIMIT_AS,		R_CSIZE, "vmemoryuse", },
428	{ -1, 0, 0 }
429};
430
431static int
432gsetrl(login_cap_t *lc, int what, const char *name, int type)
433{
434	struct rlimit rl;
435	struct rlimit r;
436	char name_cur[32];
437	char name_max[32];
438
439	_DIAGASSERT(name != NULL);
440
441	(void)snprintf(name_cur, sizeof(name_cur), "%s-cur", name);
442	(void)snprintf(name_max, sizeof(name_max), "%s-max", name);
443
444	if (getrlimit(what, &r)) {
445		syslog(LOG_ERR, "getting resource limit: %m");
446		return (-1);
447	}
448
449#define	RCUR	((quad_t)r.rlim_cur)
450#define	RMAX	((quad_t)r.rlim_max)
451
452	switch (type) {
453	case R_CTIME:
454		r.rlim_cur = login_getcaptime(lc, name, RCUR, RCUR);
455		r.rlim_max = login_getcaptime(lc, name, RMAX, RMAX);
456		rl.rlim_cur = login_getcaptime(lc, name_cur, RCUR, RCUR);
457		rl.rlim_max = login_getcaptime(lc, name_max, RMAX, RMAX);
458		break;
459	case R_CSIZE:
460		r.rlim_cur = login_getcapsize(lc, name, RCUR, RCUR);
461		r.rlim_max = login_getcapsize(lc, name, RMAX, RMAX);
462		rl.rlim_cur = login_getcapsize(lc, name_cur, RCUR, RCUR);
463		rl.rlim_max = login_getcapsize(lc, name_max, RMAX, RMAX);
464		break;
465	case R_CNUMB:
466		r.rlim_cur = login_getcapnum(lc, name, RCUR, RCUR);
467		r.rlim_max = login_getcapnum(lc, name, RMAX, RMAX);
468		rl.rlim_cur = login_getcapnum(lc, name_cur, RCUR, RCUR);
469		rl.rlim_max = login_getcapnum(lc, name_max, RMAX, RMAX);
470		break;
471	default:
472		syslog(LOG_ERR, "%s: invalid type %d setting resource limit %s",
473		    lc->lc_class, type, name);
474		return (-1);
475	}
476
477	if (setrlimit(what, &rl)) {
478		syslog(LOG_ERR, "%s: setting resource limit %s: %m",
479		    lc->lc_class, name);
480		return (-1);
481	}
482#undef	RCUR
483#undef	RMAX
484	return (0);
485}
486
487static int
488/*ARGSUSED*/
489envset(void *envp __unused, const char *name, const char *value, int overwrite)
490{
491	return setenv(name, value, overwrite);
492}
493
494int
495setuserenv(login_cap_t *lc, envfunc_t senv, void *envp)
496{
497	const char *stop = ", \t";
498	size_t i, count;
499	char *ptr;
500	char **res;
501	char *str = login_getcapstr(lc, "setenv", NULL, NULL);
502
503	if (str == NULL || *str == '\0')
504		return 0;
505
506	/*
507	 * count the sub-strings, this may over-count since we don't
508	 * account for escaped delimiters.
509	 */
510	for (i = 1, ptr = str; *ptr; i++) {
511		ptr += strcspn(ptr, stop);
512		if (*ptr)
513			ptr++;
514	}
515
516	/* allocate ptr array and string */
517	count = i;
518	res = malloc(count * sizeof(*res) + strlen(str) + 1);
519
520	if (!res)
521		return -1;
522
523	ptr = (char *)(void *)&res[count];
524	(void)strcpy(ptr, str);
525
526	/* split string */
527	for (i = 0; (res[i] = stresep(&ptr, stop, '\\')) != NULL; )
528		if (*res[i])
529			i++;
530
531	count = i;
532
533	for (i = 0; i < count; i++) {
534		if ((ptr = strchr(res[i], '=')) != NULL)
535			*ptr++ = '\0';
536		else
537			ptr = NULL;
538		(void)(*senv)(envp, res[i], ptr ? ptr : "", 1);
539	}
540
541	free(res);
542	return 0;
543}
544
545int
546setclasscontext(const char *class, u_int flags)
547{
548	int ret;
549	login_cap_t *lc;
550
551	flags &= LOGIN_SETRESOURCES | LOGIN_SETPRIORITY | LOGIN_SETUMASK |
552	    LOGIN_SETPATH;
553
554	lc = login_getclass(class);
555	ret = lc ? setusercontext(lc, NULL, 0, flags) : -1;
556	login_close(lc);
557	return (ret);
558}
559
560int
561setusercontext(login_cap_t *lc, struct passwd *pwd, uid_t uid, u_int flags)
562{
563	char per_user_tmp[MAXPATHLEN + 1];
564	const char *component_name;
565	login_cap_t *flc;
566	quad_t p;
567	int i;
568	ssize_t len;
569
570	flc = NULL;
571
572	if (!lc)
573		flc = lc = login_getclass(pwd ? pwd->pw_class : NULL);
574
575	/*
576	 * Without the pwd entry being passed we cannot set either
577	 * the group or the login.  We could complain about it.
578	 */
579	if (pwd == NULL)
580		flags &= ~(LOGIN_SETGROUP|LOGIN_SETLOGIN);
581
582#ifdef LOGIN_OSETGROUP
583	if (pwd == NULL)
584		flags &= ~LOGIN_OSETGROUP;
585	if (flags & LOGIN_OSETGROUP)
586		flags = (flags & ~LOGIN_OSETGROUP) | LOGIN_SETGROUP;
587#endif
588	if (flags & LOGIN_SETRESOURCES)
589		for (i = 0; r_list[i].name; ++i)
590			(void)gsetrl(lc, r_list[i].what, r_list[i].name,
591			    r_list[i].type);
592
593	if (flags & LOGIN_SETPRIORITY) {
594		p = login_getcapnum(lc, "priority", (quad_t)0, (quad_t)0);
595
596		if (setpriority(PRIO_PROCESS, 0, (int)p) == -1)
597			syslog(LOG_ERR, "%s: setpriority: %m", lc->lc_class);
598	}
599
600	if (flags & LOGIN_SETUMASK) {
601		p = login_getcapnum(lc, "umask", (quad_t) LOGIN_DEFUMASK,
602		    (quad_t)LOGIN_DEFUMASK);
603		umask((mode_t)p);
604	}
605
606	if (flags & LOGIN_SETGID) {
607		if (setgid(pwd->pw_gid) == -1) {
608			syslog(LOG_ERR, "setgid(%d): %m", pwd->pw_gid);
609			login_close(flc);
610			return (-1);
611		}
612	}
613
614	if (flags & LOGIN_SETGROUPS) {
615		if (initgroups(pwd->pw_name, pwd->pw_gid) == -1) {
616			syslog(LOG_ERR, "initgroups(%s,%d): %m",
617			    pwd->pw_name, pwd->pw_gid);
618			login_close(flc);
619			return (-1);
620		}
621	}
622
623	/* Create per-user temporary directories if needed. */
624	if ((len = readlink("/tmp", per_user_tmp,
625	    sizeof(per_user_tmp) - 6)) != -1) {
626
627		static const char atuid[] = "/@ruid";
628		char *lp;
629
630		/* readlink does not nul-terminate the string */
631		per_user_tmp[len] = '\0';
632
633		/* Check if it's magic symlink. */
634		lp = strstr(per_user_tmp, atuid);
635		if (lp != NULL && *(lp + (sizeof(atuid) - 1)) == '\0') {
636			lp++;
637
638			if (snprintf(lp, 11, "/%u", pwd->pw_uid) > 10) {
639				syslog(LOG_ERR, "real temporary path too long");
640				login_close(flc);
641				return (-1);
642			}
643			if (mkdir(per_user_tmp, S_IRWXU) != -1) {
644				if (chown(per_user_tmp, pwd->pw_uid,
645				    pwd->pw_gid)) {
646					component_name = "chown";
647					goto out;
648				}
649
650				/*
651			 	 * Must set sticky bit for tmp directory, some
652			 	 * programs rely on this.
653			 	 */
654				if(chmod(per_user_tmp, S_IRWXU | S_ISVTX)) {
655					component_name = "chmod";
656					goto out;
657				}
658			} else {
659				if (errno != EEXIST) {
660					component_name = "mkdir";
661					goto out;
662				} else {
663					/*
664					 * We must ensure that we own the
665					 * directory and that is has the correct
666					 * permissions, otherwise a DOS attack
667					 * is possible.
668					 */
669					struct stat sb;
670					if (stat(per_user_tmp, &sb) == -1) {
671						component_name = "stat";
672						goto out;
673					}
674
675					if (sb.st_uid != pwd->pw_uid) {
676						if (chown(per_user_tmp,
677						    pwd->pw_uid, pwd->pw_gid)) {
678							component_name = "chown";
679							goto out;
680						}
681					}
682
683					if (sb.st_mode != (S_IRWXU | S_ISVTX)) {
684						if (chmod(per_user_tmp,
685						    S_IRWXU | S_ISVTX)) {
686							component_name = "chmod";
687							goto out;
688						}
689					}
690				}
691			}
692		}
693	}
694	errno = 0;
695
696	if (flags & LOGIN_SETLOGIN)
697		if (setlogin(pwd->pw_name) == -1) {
698			syslog(LOG_ERR, "setlogin(%s) failure: %m",
699			    pwd->pw_name);
700			login_close(flc);
701			return (-1);
702		}
703
704	if (flags & LOGIN_SETUSER)
705		if (setuid(uid) == -1) {
706			syslog(LOG_ERR, "setuid(%d): %m", uid);
707			login_close(flc);
708			return (-1);
709		}
710
711	if (flags & LOGIN_SETENV)
712		setuserenv(lc, envset, NULL);
713
714	if (flags & LOGIN_SETPATH)
715		setuserpath(lc, pwd ? pwd->pw_dir : "", envset, NULL);
716
717	login_close(flc);
718	return (0);
719
720out:
721	if (component_name != NULL) {
722		syslog(LOG_ERR, "%s %s: %m", component_name, per_user_tmp);
723		login_close(flc);
724		return (-1);
725	} else {
726		syslog(LOG_ERR, "%s: %m", per_user_tmp);
727		login_close(flc);
728		return (-1);
729	}
730}
731
732void
733setuserpath(login_cap_t *lc, const char *home, envfunc_t senv, void *envp)
734{
735	size_t hlen, plen;
736	int cnt = 0;
737	char *path;
738	const char *cpath;
739	char *p, *q;
740
741	_DIAGASSERT(home != NULL);
742
743	hlen = strlen(home);
744
745	p = path = login_getcapstr(lc, "path", NULL, NULL);
746	if (p) {
747		while (*p)
748			if (*p++ == '~')
749				++cnt;
750		plen = (p - path) + cnt * (hlen + 1) + 1;
751		p = path;
752		q = path = malloc(plen);
753		if (q) {
754			while (*p) {
755				p += strspn(p, " \t");
756				if (*p == '\0')
757					break;
758				plen = strcspn(p, " \t");
759				if (hlen == 0 && *p == '~') {
760					p += plen;
761					continue;
762				}
763				if (q != path)
764					*q++ = ':';
765				if (*p == '~') {
766					strcpy(q, home);
767					q += hlen;
768					++p;
769					--plen;
770				}
771				memcpy(q, p, plen);
772				p += plen;
773				q += plen;
774			}
775			*q = '\0';
776			cpath = path;
777		} else
778			cpath = _PATH_DEFPATH;
779	} else
780		cpath = _PATH_DEFPATH;
781	if ((*senv)(envp, "PATH", cpath, 1))
782		warn("could not set PATH");
783}
784
785/*
786 * Convert an expression of the following forms
787 * 	1) A number.
788 *	2) A number followed by a b (mult by 512).
789 *	3) A number followed by a k (mult by 1024).
790 *	5) A number followed by a m (mult by 1024 * 1024).
791 *	6) A number followed by a g (mult by 1024 * 1024 * 1024).
792 *	7) A number followed by a t (mult by 1024 * 1024 * 1024 * 1024).
793 *	8) Two or more numbers (with/without k,b,m,g, or t).
794 *	   separated by x (also * for backwards compatibility), specifying
795 *	   the product of the indicated values.
796 */
797static u_quad_t
798strtosize(const char *str, char **endptr, int radix)
799{
800	u_quad_t num, num2;
801	char *expr, *expr2;
802
803	_DIAGASSERT(str != NULL);
804	/* endptr may be NULL */
805
806	errno = 0;
807	num = strtouq(str, &expr, radix);
808	if (errno || expr == str) {
809		if (endptr)
810			*endptr = expr;
811		return (num);
812	}
813
814	switch(*expr) {
815	case 'b': case 'B':
816		num = multiply(num, (u_quad_t)512);
817		++expr;
818		break;
819	case 'k': case 'K':
820		num = multiply(num, (u_quad_t)1024);
821		++expr;
822		break;
823	case 'm': case 'M':
824		num = multiply(num, (u_quad_t)1024 * 1024);
825		++expr;
826		break;
827	case 'g': case 'G':
828		num = multiply(num, (u_quad_t)1024 * 1024 * 1024);
829		++expr;
830		break;
831	case 't': case 'T':
832		num = multiply(num, (u_quad_t)1024 * 1024);
833		num = multiply(num, (u_quad_t)1024 * 1024);
834		++expr;
835		break;
836	}
837
838	if (errno)
839		goto erange;
840
841	switch(*expr) {
842	case '*':			/* Backward compatible. */
843	case 'x':
844		num2 = strtosize(expr+1, &expr2, radix);
845		if (errno) {
846			expr = expr2;
847			goto erange;
848		}
849
850		if (expr2 == expr + 1) {
851			if (endptr)
852				*endptr = expr;
853			return (num);
854		}
855		expr = expr2;
856		num = multiply(num, num2);
857		if (errno)
858			goto erange;
859		break;
860	}
861	if (endptr)
862		*endptr = expr;
863	return (num);
864erange:
865	if (endptr)
866		*endptr = expr;
867	errno = ERANGE;
868	return (UQUAD_MAX);
869}
870
871static u_quad_t
872strtolimit(const char *str, char **endptr, int radix)
873{
874
875	_DIAGASSERT(str != NULL);
876	/* endptr may be NULL */
877
878	if (isinfinite(str)) {
879		if (endptr)
880			*endptr = (char *)__UNCONST(str) + strlen(str);
881		return ((u_quad_t)RLIM_INFINITY);
882	}
883	return (strtosize(str, endptr, radix));
884}
885
886static int
887isinfinite(const char *s)
888{
889	static const char *infs[] = {
890		"infinity",
891		"inf",
892		"unlimited",
893		"unlimit",
894		NULL
895	};
896	const char **i;
897
898	_DIAGASSERT(s != NULL);
899
900	for (i = infs; *i; i++) {
901		if (!strcasecmp(s, *i))
902			return 1;
903	}
904	return 0;
905}
906
907static u_quad_t
908multiply(u_quad_t n1, u_quad_t n2)
909{
910	static int bpw = 0;
911	u_quad_t m;
912	u_quad_t r;
913	int b1, b2;
914
915	/*
916	 * Get rid of the simple cases
917	 */
918	if (n1 == 0 || n2 == 0)
919		return (0);
920	if (n1 == 1)
921		return (n2);
922	if (n2 == 1)
923		return (n1);
924
925	/*
926	 * sizeof() returns number of bytes needed for storage.
927	 * This may be different from the actual number of useful bits.
928	 */
929	if (!bpw) {
930		bpw = sizeof(u_quad_t) * 8;
931		while (((u_quad_t)1 << (bpw-1)) == 0)
932			--bpw;
933	}
934
935	/*
936	 * First check the magnitude of each number.  If the sum of the
937	 * magnitude is to high, reject the number.  (If this test
938	 * is not done then the first multiply below may overflow.)
939	 */
940	for (b1 = bpw; (((u_quad_t)1 << (b1-1)) & n1) == 0; --b1)
941		;
942	for (b2 = bpw; (((u_quad_t)1 << (b2-1)) & n2) == 0; --b2)
943		;
944	if (b1 + b2 - 2 > bpw) {
945		errno = ERANGE;
946		return (UQUAD_MAX);
947	}
948
949	/*
950	 * Decompose the multiplication to be:
951	 * h1 = n1 & ~1
952	 * h2 = n2 & ~1
953	 * l1 = n1 & 1
954	 * l2 = n2 & 1
955	 * (h1 + l1) * (h2 + l2)
956	 * (h1 * h2) + (h1 * l2) + (l1 * h2) + (l1 * l2)
957	 *
958	 * Since h1 && h2 do not have the low bit set, we can then say:
959	 *
960	 * (h1>>1 * h2>>1 * 4) + ...
961	 *
962	 * So if (h1>>1 * h2>>1) > (1<<(bpw - 2)) then the result will
963	 * overflow.
964	 *
965	 * Finally, if MAX - ((h1 * l2) + (l1 * h2) + (l1 * l2)) < (h1*h2)
966	 * then adding in residual amount will cause an overflow.
967	 */
968
969	m = (n1 >> 1) * (n2 >> 1);
970
971	if (m >= ((u_quad_t)1 << (bpw-2))) {
972		errno = ERANGE;
973		return (UQUAD_MAX);
974	}
975
976	m *= 4;
977
978	r = (n1 & n2 & 1)
979	  + (n2 & 1) * (n1 & ~(u_quad_t)1)
980	  + (n1 & 1) * (n2 & ~(u_quad_t)1);
981
982	if ((u_quad_t)(m + r) < m) {
983		errno = ERANGE;
984		return (UQUAD_MAX);
985	}
986	m += r;
987
988	return (m);
989}
990