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