xref: /netbsd/lib/libutil/login_cap.c (revision bf9ec67e)
1 /*	$NetBSD: login_cap.c,v 1.11 2001/07/22 13:34:01 wiz 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.11 2001/07/22 13:34:01 wiz 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 
47 #include <assert.h>
48 #include <ctype.h>
49 #include <err.h>
50 #include <errno.h>
51 #include <fcntl.h>
52 #include <limits.h>
53 #include <login_cap.h>
54 #include <paths.h>
55 #include <pwd.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <syslog.h>
60 #include <unistd.h>
61 #include <util.h>
62 
63 static void	setuserpath(login_cap_t *, char *);
64 static u_quad_t	multiply(u_quad_t, u_quad_t);
65 static u_quad_t	strtolimit(char *, char **, int);
66 static u_quad_t	strtosize(char *, char **, int);
67 static int	gsetrl(login_cap_t *, int, char *, int type);
68 static int	setuserenv(login_cap_t *);
69 static int	isinfinite(const char *);
70 
71 login_cap_t *
72 login_getclass(char *class)
73 {
74 	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 ((res = open(classfiles[0], 0)) >= 0)
121 				close(res);
122 			if (strcmp(lc->lc_class, LOGIN_DEFCLASS) == NULL &&
123 			    res < 0)
124 				return (lc);
125 			syslog(LOG_ERR, "%s: unknown class", lc->lc_class);
126 			break;
127 		case -2:
128 			syslog(LOG_ERR, "%s: getting class information: %m",
129 				lc->lc_class);
130 			break;
131 		case -3:
132 			syslog(LOG_ERR, "%s: 'tc' reference loop",
133 				lc->lc_class);
134 			break;
135 		default:
136 			syslog(LOG_ERR, "%s: unexpected cgetent error",
137 				lc->lc_class);
138 			break;
139 		}
140 		free(lc->lc_class);
141 		free(lc);
142 		return (0);
143 	}
144 	return (lc);
145 }
146 
147 login_cap_t *
148 login_getpwclass(const struct passwd *pwd)
149 {
150 
151 	/* pwd may be NULL */
152 
153 	return login_getclass(pwd ? pwd->pw_class : NULL);
154 }
155 
156 char *
157 login_getcapstr(login_cap_t *lc, char *cap, char *def, char *e)
158 {
159 	char *res;
160 	int status;
161 
162 	errno = 0;
163 
164 	_DIAGASSERT(cap != NULL);
165 
166 	if (!lc || !lc->lc_cap)
167 		return (def);
168 
169 	switch (status = cgetstr(lc->lc_cap, cap, &res)) {
170 	case -1:
171 		return (def);
172 	case -2:
173 		syslog(LOG_ERR, "%s: getting capability %s: %m",
174 		    lc->lc_class, cap);
175 		return (e);
176 	default:
177 		if (status >= 0)
178 			return (res);
179 		syslog(LOG_ERR, "%s: unexpected error with capability %s",
180 		    lc->lc_class, cap);
181 		return (e);
182 	}
183 }
184 
185 quad_t
186 login_getcaptime(login_cap_t *lc, char *cap, quad_t def, quad_t e)
187 {
188 	char *ep;
189 	char *res, *sres;
190 	int status;
191 	quad_t q, r;
192 
193 	_DIAGASSERT(cap != NULL);
194 
195 	errno = 0;
196 	if (!lc || !lc->lc_cap)
197 		return (def);
198 
199 	switch (status = cgetstr(lc->lc_cap, cap, &res)) {
200 	case -1:
201 		return (def);
202 	case -2:
203 		syslog(LOG_ERR, "%s: getting capability %s: %m",
204 		    lc->lc_class, cap);
205 		errno = ERANGE;
206 		return (e);
207 	default:
208 		if (status >= 0)
209 			break;
210 		syslog(LOG_ERR, "%s: unexpected error with capability %s",
211 		    lc->lc_class, cap);
212 		errno = ERANGE;
213 		return (e);
214 	}
215 
216 	if (isinfinite(res))
217 		return (RLIM_INFINITY);
218 
219 	errno = 0;
220 
221 	q = 0;
222 	sres = res;
223 	while (*res) {
224 		r = strtoq(res, &ep, 0);
225 		if (!ep || ep == res ||
226 		    ((r == QUAD_MIN || r == QUAD_MAX) && errno == ERANGE)) {
227 invalid:
228 			syslog(LOG_ERR, "%s:%s=%s: invalid time",
229 			    lc->lc_class, cap, sres);
230 			errno = ERANGE;
231 			return (e);
232 		}
233 		switch (*ep++) {
234 		case '\0':
235 			--ep;
236 			break;
237 		case 's': case 'S':
238 			break;
239 		case 'm': case 'M':
240 			r *= 60;
241 			break;
242 		case 'h': case 'H':
243 			r *= 60 * 60;
244 			break;
245 		case 'd': case 'D':
246 			r *= 60 * 60 * 24;
247 			break;
248 		case 'w': case 'W':
249 			r *= 60 * 60 * 24 * 7;
250 			break;
251 		case 'y': case 'Y':	/* Pretty absurd */
252 			r *= 60 * 60 * 24 * 365;
253 			break;
254 		default:
255 			goto invalid;
256 		}
257 		res = ep;
258 		q += r;
259 	}
260 	return (q);
261 }
262 
263 quad_t
264 login_getcapnum(login_cap_t *lc, char *cap, quad_t def, quad_t e)
265 {
266 	char *ep;
267 	char *res;
268 	int status;
269 	quad_t q;
270 
271 	_DIAGASSERT(cap != NULL);
272 
273 	errno = 0;
274 	if (!lc || !lc->lc_cap)
275 		return (def);
276 
277 	switch (status = cgetstr(lc->lc_cap, cap, &res)) {
278 	case -1:
279 		return (def);
280 	case -2:
281 		syslog(LOG_ERR, "%s: getting capability %s: %m",
282 		    lc->lc_class, cap);
283 		errno = ERANGE;
284 		return (e);
285 	default:
286 		if (status >= 0)
287 			break;
288 		syslog(LOG_ERR, "%s: unexpected error with capability %s",
289 		    lc->lc_class, cap);
290 		errno = ERANGE;
291 		return (e);
292 	}
293 
294 	if (isinfinite(res))
295 		return (RLIM_INFINITY);
296 
297 	errno = 0;
298     	q = strtoq(res, &ep, 0);
299 	if (!ep || ep == res || ep[0] ||
300 	    ((q == QUAD_MIN || q == QUAD_MAX) && errno == ERANGE)) {
301 		syslog(LOG_ERR, "%s:%s=%s: invalid number",
302 		    lc->lc_class, cap, res);
303 		errno = ERANGE;
304 		return (e);
305 	}
306 	return (q);
307 }
308 
309 quad_t
310 login_getcapsize(login_cap_t *lc, char *cap, quad_t def, quad_t e)
311 {
312 	char *ep;
313 	char *res;
314 	int status;
315 	quad_t q;
316 
317 	_DIAGASSERT(cap != NULL);
318 
319 	errno = 0;
320 
321 	if (!lc || !lc->lc_cap)
322 		return (def);
323 
324 	switch (status = cgetstr(lc->lc_cap, cap, &res)) {
325 	case -1:
326 		return (def);
327 	case -2:
328 		syslog(LOG_ERR, "%s: getting capability %s: %m",
329 		    lc->lc_class, cap);
330 		errno = ERANGE;
331 		return (e);
332 	default:
333 		if (status >= 0)
334 			break;
335 		syslog(LOG_ERR, "%s: unexpected error with capability %s",
336 		    lc->lc_class, cap);
337 		errno = ERANGE;
338 		return (e);
339 	}
340 
341 	errno = 0;
342 	q = strtolimit(res, &ep, 0);
343 	if (!ep || ep == res || (ep[0] && ep[1]) ||
344 	    ((q == QUAD_MIN || q == QUAD_MAX) && errno == ERANGE)) {
345 		syslog(LOG_ERR, "%s:%s=%s: invalid size",
346 		    lc->lc_class, cap, res);
347 		errno = ERANGE;
348 		return (e);
349 	}
350 	return (q);
351 }
352 
353 int
354 login_getcapbool(login_cap_t *lc, char *cap, u_int def)
355 {
356 
357 	_DIAGASSERT(cap != NULL);
358 
359 	if (!lc || !lc->lc_cap)
360 		return (def);
361 
362 	return (cgetcap(lc->lc_cap, cap, ':') != NULL);
363 }
364 
365 void
366 login_close(login_cap_t *lc)
367 {
368 
369 	if (lc) {
370 		if (lc->lc_class)
371 			free(lc->lc_class);
372 		if (lc->lc_cap)
373 			free(lc->lc_cap);
374 		if (lc->lc_style)
375 			free(lc->lc_style);
376 		free(lc);
377 	}
378 }
379 
380 #define	R_CTIME	1
381 #define	R_CSIZE	2
382 #define	R_CNUMB	3
383 
384 static struct {
385 	int	what;
386 	int	type;
387 	char *	name;
388 } r_list[] = {
389 	{ RLIMIT_CPU,		R_CTIME, "cputime", },
390 	{ RLIMIT_FSIZE,		R_CSIZE, "filesize", },
391 	{ RLIMIT_DATA,		R_CSIZE, "datasize", },
392 	{ RLIMIT_STACK,		R_CSIZE, "stacksize", },
393 	{ RLIMIT_RSS,		R_CSIZE, "memoryuse", },
394 	{ RLIMIT_MEMLOCK,	R_CSIZE, "memorylocked", },
395 	{ RLIMIT_NPROC,		R_CNUMB, "maxproc", },
396 	{ RLIMIT_NOFILE,	R_CNUMB, "openfiles", },
397 	{ RLIMIT_CORE,		R_CSIZE, "coredumpsize", },
398 	{ -1, 0, 0 }
399 };
400 
401 static int
402 gsetrl(login_cap_t *lc, int what, char *name, int type)
403 {
404 	struct rlimit rl;
405 	struct rlimit r;
406 	char name_cur[32];
407 	char name_max[32];
408 
409 	_DIAGASSERT(name != NULL);
410 
411 	sprintf(name_cur, "%s-cur", name);
412 	sprintf(name_max, "%s-max", name);
413 
414 	if (getrlimit(what, &r)) {
415 		syslog(LOG_ERR, "getting resource limit: %m");
416 		return (-1);
417 	}
418 
419 #define	RCUR	r.rlim_cur
420 #define	RMAX	r.rlim_max
421 
422 	switch (type) {
423 	case R_CTIME:
424 		RCUR = login_getcaptime(lc, name, RCUR, RCUR);
425 		RMAX = login_getcaptime(lc, name, RMAX, RMAX);
426 		rl.rlim_cur = login_getcaptime(lc, name_cur, RCUR, RCUR);
427 		rl.rlim_max = login_getcaptime(lc, name_max, RMAX, RMAX);
428 		break;
429 	case R_CSIZE:
430 		RCUR = login_getcapsize(lc, name, RCUR, RCUR);
431 		RMAX = login_getcapsize(lc, name, RMAX, RMAX);
432 		rl.rlim_cur = login_getcapsize(lc, name_cur, RCUR, RCUR);
433 		rl.rlim_max = login_getcapsize(lc, name_max, RMAX, RMAX);
434 		break;
435 	case R_CNUMB:
436 		RCUR = login_getcapnum(lc, name, RCUR, RCUR);
437 		RMAX = login_getcapnum(lc, name, RMAX, RMAX);
438 		rl.rlim_cur = login_getcapnum(lc, name_cur, RCUR, RCUR);
439 		rl.rlim_max = login_getcapnum(lc, name_max, RMAX, RMAX);
440 		break;
441 	default:
442 		return (-1);
443 	}
444 
445 	if (setrlimit(what, &rl)) {
446 		syslog(LOG_ERR, "%s: setting resource limit %s: %m",
447 		    lc->lc_class, name);
448 		return (-1);
449 	}
450 #undef	RCUR
451 #undef	RMAX
452 	return (0);
453 }
454 
455 static int
456 setuserenv(login_cap_t *lc)
457 {
458 	char *stop = ", \t";
459 	int i, count;
460 	char *ptr;
461 	char **res;
462 	char *str = login_getcapstr(lc, "setenv", NULL, NULL);
463 
464 	if (str == NULL || *str == '\0')
465 		return 0;
466 
467 	/* count the sub-strings */
468 	for (i = 1, ptr = str; *ptr; i++) {
469 		ptr += strcspn(ptr, stop);
470 		if (*ptr)
471 			ptr++;
472 	}
473 
474 	/* allocate ptr array and string */
475 	count = i;
476 	res = malloc(count * sizeof(char *) + strlen(str) + 1);
477 
478 	if (!res)
479 		return -1;
480 
481 	ptr = (char *)res + count * sizeof(char *);
482 	strcpy(ptr, str);
483 
484 	/* split string */
485 	for (i = 0; *ptr && i < count; i++) {
486 		res[i] = ptr;
487 		ptr += strcspn(ptr, stop);
488 		if (*ptr)
489 			*ptr++ = '\0';
490 	}
491 
492 	res[i] = NULL;
493 
494 	for (i = 0; i < count && res[i]; i++) {
495 		if (*res[i] != '\0') {
496 			if ((ptr = strchr(res[i], '=')) != NULL)
497 				*ptr++ = '\0';
498 			else
499 				ptr = "";
500 			setenv(res[i], ptr, 1);
501 		}
502 	}
503 
504 	free(res);
505 	return 0;
506 }
507 
508 int
509 setclasscontext(char *class, u_int flags)
510 {
511 	int ret;
512 	login_cap_t *lc;
513 
514 	flags &= LOGIN_SETRESOURCES | LOGIN_SETPRIORITY | LOGIN_SETUMASK |
515 	    LOGIN_SETPATH;
516 
517 	lc = login_getclass(class);
518 	ret = lc ? setusercontext(lc, NULL, 0, flags) : -1;
519 	login_close(lc);
520 	return (ret);
521 }
522 
523 int
524 setusercontext(login_cap_t *lc, struct passwd *pwd, uid_t uid, u_int flags)
525 {
526 	login_cap_t *flc;
527 	quad_t p;
528 	int i;
529 
530 	flc = NULL;
531 
532 	if (!lc)
533 		flc = lc = login_getclass(pwd ? pwd->pw_class : NULL);
534 
535 	/*
536 	 * Without the pwd entry being passed we cannot set either
537 	 * the group or the login.  We could complain about it.
538 	 */
539 	if (pwd == NULL)
540 		flags &= ~(LOGIN_SETGROUP|LOGIN_SETLOGIN);
541 
542 	if (flags & LOGIN_SETRESOURCES)
543 		for (i = 0; r_list[i].name; ++i)
544 			if (gsetrl(lc, r_list[i].what, r_list[i].name,
545 			    r_list[i].type))
546 				/* XXX - call syslog()? */;
547 
548 	if (flags & LOGIN_SETPRIORITY) {
549 		p = login_getcapnum(lc, "priority", 0LL, 0LL);
550 
551 		if (setpriority(PRIO_PROCESS, 0, (int)p) < 0)
552 			syslog(LOG_ERR, "%s: setpriority: %m", lc->lc_class);
553 	}
554 
555 	if (flags & LOGIN_SETUMASK) {
556 		p = login_getcapnum(lc, "umask", (quad_t) LOGIN_DEFUMASK,
557 												   (quad_t) LOGIN_DEFUMASK);
558 		umask((mode_t)p);
559 	}
560 
561 	if (flags & LOGIN_SETGROUP) {
562 		if (setgid(pwd->pw_gid) < 0) {
563 			syslog(LOG_ERR, "setgid(%d): %m", pwd->pw_gid);
564 			login_close(flc);
565 			return (-1);
566 		}
567 
568 		if (initgroups(pwd->pw_name, pwd->pw_gid) < 0) {
569 			syslog(LOG_ERR, "initgroups(%s,%d): %m",
570 			    pwd->pw_name, pwd->pw_gid);
571 			login_close(flc);
572 			return (-1);
573 		}
574 	}
575 
576 	if (flags & LOGIN_SETLOGIN)
577 		if (setlogin(pwd->pw_name) < 0) {
578 			syslog(LOG_ERR, "setlogin(%s) failure: %m",
579 			    pwd->pw_name);
580 			login_close(flc);
581 			return (-1);
582 		}
583 
584 	if (flags & LOGIN_SETUSER)
585 		if (setuid(uid) < 0) {
586 			syslog(LOG_ERR, "setuid(%d): %m", uid);
587 			login_close(flc);
588 			return (-1);
589 		}
590 
591 	if (flags & LOGIN_SETENV)
592 		setuserenv(lc);
593 
594 	if (flags & LOGIN_SETPATH)
595 		setuserpath(lc, pwd ? pwd->pw_dir : "");
596 
597 	login_close(flc);
598 	return (0);
599 }
600 
601 static void
602 setuserpath(login_cap_t *lc, char *home)
603 {
604 	size_t hlen, plen;
605 	int cnt = 0;
606 	char *path;
607 	char *p, *q;
608 
609 	_DIAGASSERT(home != NULL);
610 
611 	hlen = strlen(home);
612 
613 	p = path = login_getcapstr(lc, "path", NULL, NULL);
614 	if (p) {
615 		while (*p)
616 			if (*p++ == '~')
617 				++cnt;
618 		plen = (p - path) + cnt * (hlen + 1) + 1;
619 		p = path;
620 		q = path = malloc(plen);
621 		if (q) {
622 			while (*p) {
623 				p += strspn(p, " \t");
624 				if (*p == '\0')
625 					break;
626 				plen = strcspn(p, " \t");
627 				if (hlen == 0 && *p == '~') {
628 					p += plen;
629 					continue;
630 				}
631 				if (q != path)
632 					*q++ = ':';
633 				if (*p == '~') {
634 					strcpy(q, home);
635 					q += hlen;
636 					++p;
637 					--plen;
638 				}
639 				memcpy(q, p, plen);
640 				p += plen;
641 				q += plen;
642 			}
643 			*q = '\0';
644 		} else
645 			path = _PATH_DEFPATH;
646 	} else
647 		path = _PATH_DEFPATH;
648 	if (setenv("PATH", path, 1))
649 		warn("could not set PATH");
650 }
651 
652 /*
653  * Convert an expression of the following forms
654  * 	1) A number.
655  *	2) A number followed by a b (mult by 512).
656  *	3) A number followed by a k (mult by 1024).
657  *	5) A number followed by a m (mult by 1024 * 1024).
658  *	6) A number followed by a g (mult by 1024 * 1024 * 1024).
659  *	7) A number followed by a t (mult by 1024 * 1024 * 1024 * 1024).
660  *	8) Two or more numbers (with/without k,b,m,g, or t).
661  *	   separated by x (also * for backwards compatibility), specifying
662  *	   the product of the indicated values.
663  */
664 static u_quad_t
665 strtosize(char *str, char **endptr, int radix)
666 {
667 	u_quad_t num, num2;
668 	char *expr, *expr2;
669 
670 	_DIAGASSERT(str != NULL);
671 	/* endptr may be NULL */
672 
673 	errno = 0;
674 	num = strtouq(str, &expr, radix);
675 	if (errno || expr == str) {
676 		if (endptr)
677 			*endptr = expr;
678 		return (num);
679 	}
680 
681 	switch(*expr) {
682 	case 'b': case 'B':
683 		num = multiply(num, (u_quad_t)512);
684 		++expr;
685 		break;
686 	case 'k': case 'K':
687 		num = multiply(num, (u_quad_t)1024);
688 		++expr;
689 		break;
690 	case 'm': case 'M':
691 		num = multiply(num, (u_quad_t)1024 * 1024);
692 		++expr;
693 		break;
694 	case 'g': case 'G':
695 		num = multiply(num, (u_quad_t)1024 * 1024 * 1024);
696 		++expr;
697 		break;
698 	case 't': case 'T':
699 		num = multiply(num, (u_quad_t)1024 * 1024);
700 		num = multiply(num, (u_quad_t)1024 * 1024);
701 		++expr;
702 		break;
703 	}
704 
705 	if (errno)
706 		goto erange;
707 
708 	switch(*expr) {
709 	case '*':			/* Backward compatible. */
710 	case 'x':
711 		num2 = strtosize(expr+1, &expr2, radix);
712 		if (errno) {
713 			expr = expr2;
714 			goto erange;
715 		}
716 
717 		if (expr2 == expr + 1) {
718 			if (endptr)
719 				*endptr = expr;
720 			return (num);
721 		}
722 		expr = expr2;
723 		num = multiply(num, num2);
724 		if (errno)
725 			goto erange;
726 		break;
727 	}
728 	if (endptr)
729 		*endptr = expr;
730 	return (num);
731 erange:
732 	if (endptr)
733 		*endptr = expr;
734 	errno = ERANGE;
735 	return (UQUAD_MAX);
736 }
737 
738 static u_quad_t
739 strtolimit(char *str, char **endptr, int radix)
740 {
741 
742 	_DIAGASSERT(str != NULL);
743 	/* endptr may be NULL */
744 
745 	if (isinfinite(str)) {
746 		if (endptr)
747 			*endptr = str + strlen(str);
748 		return ((u_quad_t)RLIM_INFINITY);
749 	}
750 	return (strtosize(str, endptr, radix));
751 }
752 
753 static int
754 isinfinite(const char *s)
755 {
756 	static const char *infs[] = {
757 		"infinity",
758 		"inf",
759 		"unlimited",
760 		"unlimit",
761 		NULL
762 	};
763 	const char **i;
764 
765 	_DIAGASSERT(s != NULL);
766 
767 	for (i = infs; *i; i++) {
768 		if (!strcasecmp(s, *i))
769 			return 1;
770 	}
771 	return 0;
772 }
773 
774 static u_quad_t
775 multiply(u_quad_t n1, u_quad_t n2)
776 {
777 	static int bpw = 0;
778 	u_quad_t m;
779 	u_quad_t r;
780 	int b1, b2;
781 
782 	/*
783 	 * Get rid of the simple cases
784 	 */
785 	if (n1 == 0 || n2 == 0)
786 		return (0);
787 	if (n1 == 1)
788 		return (n2);
789 	if (n2 == 1)
790 		return (n1);
791 
792 	/*
793 	 * sizeof() returns number of bytes needed for storage.
794 	 * This may be different from the actual number of useful bits.
795 	 */
796 	if (!bpw) {
797 		bpw = sizeof(u_quad_t) * 8;
798 		while (((u_quad_t)1 << (bpw-1)) == 0)
799 			--bpw;
800 	}
801 
802 	/*
803 	 * First check the magnitude of each number.  If the sum of the
804 	 * magnatude is way to high, reject the number.  (If this test
805 	 * is not done then the first multiply below may overflow.)
806 	 */
807 	for (b1 = bpw; (((u_quad_t)1 << (b1-1)) & n1) == 0; --b1)
808 		;
809 	for (b2 = bpw; (((u_quad_t)1 << (b2-1)) & n2) == 0; --b2)
810 		;
811 	if (b1 + b2 - 2 > bpw) {
812 		errno = ERANGE;
813 		return (UQUAD_MAX);
814 	}
815 
816 	/*
817 	 * Decompose the multiplication to be:
818 	 * h1 = n1 & ~1
819 	 * h2 = n2 & ~1
820 	 * l1 = n1 & 1
821 	 * l2 = n2 & 1
822 	 * (h1 + l1) * (h2 + l2)
823 	 * (h1 * h2) + (h1 * l2) + (l1 * h2) + (l1 * l2)
824 	 *
825 	 * Since h1 && h2 do not have the low bit set, we can then say:
826 	 *
827 	 * (h1>>1 * h2>>1 * 4) + ...
828 	 *
829 	 * So if (h1>>1 * h2>>1) > (1<<(bpw - 2)) then the result will
830 	 * overflow.
831 	 *
832 	 * Finally, if MAX - ((h1 * l2) + (l1 * h2) + (l1 * l2)) < (h1*h2)
833 	 * then adding in residual amout will cause an overflow.
834 	 */
835 
836 	m = (n1 >> 1) * (n2 >> 1);
837 
838 	if (m >= ((u_quad_t)1 << (bpw-2))) {
839 		errno = ERANGE;
840 		return (UQUAD_MAX);
841 	}
842 
843 	m *= 4;
844 
845 	r = (n1 & n2 & 1)
846 	  + (n2 & 1) * (n1 & ~(u_quad_t)1)
847 	  + (n1 & 1) * (n2 & ~(u_quad_t)1);
848 
849 	if ((u_quad_t)(m + r) < m) {
850 		errno = ERANGE;
851 		return (UQUAD_MAX);
852 	}
853 	m += r;
854 
855 	return (m);
856 }
857