login_cap.c revision 1.25 1 /* $NetBSD: login_cap.c,v 1.25 2006/10/15 19:33:03 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.25 2006/10/15 19:33:03 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 (void)snprintf(name_cur, sizeof(name_cur), "%s-cur", name);
439 (void)snprintf(name_max, sizeof(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 syslog(LOG_ERR, "%s: invalid type %d setting resource limit %s",
470 lc->lc_class, type, name);
471 return (-1);
472 }
473
474 if (setrlimit(what, &rl)) {
475 syslog(LOG_ERR, "%s: setting resource limit %s: %m",
476 lc->lc_class, name);
477 return (-1);
478 }
479 #undef RCUR
480 #undef RMAX
481 return (0);
482 }
483
484 static int
485 /*ARGSUSED*/
486 envset(void *envp, const char *name, const char *value, int overwrite)
487 {
488 return setenv(name, value, overwrite);
489 }
490
491 int
492 setuserenv(login_cap_t *lc, envfunc_t senv, void *envp)
493 {
494 const char *stop = ", \t";
495 size_t i, count;
496 char *ptr;
497 char **res;
498 char *str = login_getcapstr(lc, "setenv", NULL, NULL);
499
500 if (str == NULL || *str == '\0')
501 return 0;
502
503 /*
504 * count the sub-strings, this may over-count since we don't
505 * account for escaped delimiters.
506 */
507 for (i = 1, ptr = str; *ptr; i++) {
508 ptr += strcspn(ptr, stop);
509 if (*ptr)
510 ptr++;
511 }
512
513 /* allocate ptr array and string */
514 count = i;
515 res = malloc(count * sizeof(char *) + strlen(str) + 1);
516
517 if (!res)
518 return -1;
519
520 ptr = (char *)(void *)&res[count];
521 (void)strcpy(ptr, str);
522
523 /* split string */
524 for (i = 0; (res[i] = stresep(&ptr, stop, '\\')) != NULL; )
525 if (*res[i])
526 i++;
527
528 count = i;
529
530 for (i = 0; i < count; i++) {
531 if ((ptr = strchr(res[i], '=')) != NULL)
532 *ptr++ = '\0';
533 else
534 ptr = NULL;
535 (void)(*senv)(envp, res[i], ptr ? ptr : "", 1);
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 (void)gsetrl(lc, r_list[i].what, r_list[i].name,
579 r_list[i].type);
580
581 if (flags & LOGIN_SETPRIORITY) {
582 p = login_getcapnum(lc, "priority", (quad_t)0, (quad_t)0);
583
584 if (setpriority(PRIO_PROCESS, 0, (int)p) < 0)
585 syslog(LOG_ERR, "%s: setpriority: %m", lc->lc_class);
586 }
587
588 if (flags & LOGIN_SETUMASK) {
589 p = login_getcapnum(lc, "umask", (quad_t) LOGIN_DEFUMASK,
590 (quad_t) LOGIN_DEFUMASK);
591 umask((mode_t)p);
592 }
593
594 if (flags & LOGIN_SETGROUP) {
595 if (setgid(pwd->pw_gid) < 0) {
596 syslog(LOG_ERR, "setgid(%d): %m", pwd->pw_gid);
597 login_close(flc);
598 return (-1);
599 }
600
601 if (initgroups(pwd->pw_name, pwd->pw_gid) < 0) {
602 syslog(LOG_ERR, "initgroups(%s,%d): %m",
603 pwd->pw_name, pwd->pw_gid);
604 login_close(flc);
605 return (-1);
606 }
607 }
608
609 if (flags & LOGIN_SETLOGIN)
610 if (setlogin(pwd->pw_name) < 0) {
611 syslog(LOG_ERR, "setlogin(%s) failure: %m",
612 pwd->pw_name);
613 login_close(flc);
614 return (-1);
615 }
616
617 if (flags & LOGIN_SETUSER)
618 if (setuid(uid) < 0) {
619 syslog(LOG_ERR, "setuid(%d): %m", uid);
620 login_close(flc);
621 return (-1);
622 }
623
624 if (flags & LOGIN_SETENV)
625 setuserenv(lc, envset, NULL);
626
627 if (flags & LOGIN_SETPATH)
628 setuserpath(lc, pwd ? pwd->pw_dir : "", envset, NULL);
629
630 login_close(flc);
631 return (0);
632 }
633
634 void
635 setuserpath(login_cap_t *lc, const char *home, envfunc_t senv, void *envp)
636 {
637 size_t hlen, plen;
638 int cnt = 0;
639 char *path;
640 const char *cpath;
641 char *p, *q;
642
643 _DIAGASSERT(home != NULL);
644
645 hlen = strlen(home);
646
647 p = path = login_getcapstr(lc, "path", NULL, NULL);
648 if (p) {
649 while (*p)
650 if (*p++ == '~')
651 ++cnt;
652 plen = (p - path) + cnt * (hlen + 1) + 1;
653 p = path;
654 q = path = malloc(plen);
655 if (q) {
656 while (*p) {
657 p += strspn(p, " \t");
658 if (*p == '\0')
659 break;
660 plen = strcspn(p, " \t");
661 if (hlen == 0 && *p == '~') {
662 p += plen;
663 continue;
664 }
665 if (q != path)
666 *q++ = ':';
667 if (*p == '~') {
668 strcpy(q, home);
669 q += hlen;
670 ++p;
671 --plen;
672 }
673 memcpy(q, p, plen);
674 p += plen;
675 q += plen;
676 }
677 *q = '\0';
678 cpath = path;
679 } else
680 cpath = _PATH_DEFPATH;
681 } else
682 cpath = _PATH_DEFPATH;
683 if ((*senv)(envp, "PATH", cpath, 1))
684 warn("could not set PATH");
685 }
686
687 /*
688 * Convert an expression of the following forms
689 * 1) A number.
690 * 2) A number followed by a b (mult by 512).
691 * 3) A number followed by a k (mult by 1024).
692 * 5) A number followed by a m (mult by 1024 * 1024).
693 * 6) A number followed by a g (mult by 1024 * 1024 * 1024).
694 * 7) A number followed by a t (mult by 1024 * 1024 * 1024 * 1024).
695 * 8) Two or more numbers (with/without k,b,m,g, or t).
696 * separated by x (also * for backwards compatibility), specifying
697 * the product of the indicated values.
698 */
699 static u_quad_t
700 strtosize(const char *str, char **endptr, int radix)
701 {
702 u_quad_t num, num2;
703 char *expr, *expr2;
704
705 _DIAGASSERT(str != NULL);
706 /* endptr may be NULL */
707
708 errno = 0;
709 num = strtouq(str, &expr, radix);
710 if (errno || expr == str) {
711 if (endptr)
712 *endptr = expr;
713 return (num);
714 }
715
716 switch(*expr) {
717 case 'b': case 'B':
718 num = multiply(num, (u_quad_t)512);
719 ++expr;
720 break;
721 case 'k': case 'K':
722 num = multiply(num, (u_quad_t)1024);
723 ++expr;
724 break;
725 case 'm': case 'M':
726 num = multiply(num, (u_quad_t)1024 * 1024);
727 ++expr;
728 break;
729 case 'g': case 'G':
730 num = multiply(num, (u_quad_t)1024 * 1024 * 1024);
731 ++expr;
732 break;
733 case 't': case 'T':
734 num = multiply(num, (u_quad_t)1024 * 1024);
735 num = multiply(num, (u_quad_t)1024 * 1024);
736 ++expr;
737 break;
738 }
739
740 if (errno)
741 goto erange;
742
743 switch(*expr) {
744 case '*': /* Backward compatible. */
745 case 'x':
746 num2 = strtosize(expr+1, &expr2, radix);
747 if (errno) {
748 expr = expr2;
749 goto erange;
750 }
751
752 if (expr2 == expr + 1) {
753 if (endptr)
754 *endptr = expr;
755 return (num);
756 }
757 expr = expr2;
758 num = multiply(num, num2);
759 if (errno)
760 goto erange;
761 break;
762 }
763 if (endptr)
764 *endptr = expr;
765 return (num);
766 erange:
767 if (endptr)
768 *endptr = expr;
769 errno = ERANGE;
770 return (UQUAD_MAX);
771 }
772
773 static u_quad_t
774 strtolimit(const char *str, char **endptr, int radix)
775 {
776
777 _DIAGASSERT(str != NULL);
778 /* endptr may be NULL */
779
780 if (isinfinite(str)) {
781 if (endptr)
782 *endptr = (char *)__UNCONST(str) + strlen(str);
783 return ((u_quad_t)RLIM_INFINITY);
784 }
785 return (strtosize(str, endptr, radix));
786 }
787
788 static int
789 isinfinite(const char *s)
790 {
791 static const char *infs[] = {
792 "infinity",
793 "inf",
794 "unlimited",
795 "unlimit",
796 NULL
797 };
798 const char **i;
799
800 _DIAGASSERT(s != NULL);
801
802 for (i = infs; *i; i++) {
803 if (!strcasecmp(s, *i))
804 return 1;
805 }
806 return 0;
807 }
808
809 static u_quad_t
810 multiply(u_quad_t n1, u_quad_t n2)
811 {
812 static int bpw = 0;
813 u_quad_t m;
814 u_quad_t r;
815 int b1, b2;
816
817 /*
818 * Get rid of the simple cases
819 */
820 if (n1 == 0 || n2 == 0)
821 return (0);
822 if (n1 == 1)
823 return (n2);
824 if (n2 == 1)
825 return (n1);
826
827 /*
828 * sizeof() returns number of bytes needed for storage.
829 * This may be different from the actual number of useful bits.
830 */
831 if (!bpw) {
832 bpw = sizeof(u_quad_t) * 8;
833 while (((u_quad_t)1 << (bpw-1)) == 0)
834 --bpw;
835 }
836
837 /*
838 * First check the magnitude of each number. If the sum of the
839 * magnatude is way to high, reject the number. (If this test
840 * is not done then the first multiply below may overflow.)
841 */
842 for (b1 = bpw; (((u_quad_t)1 << (b1-1)) & n1) == 0; --b1)
843 ;
844 for (b2 = bpw; (((u_quad_t)1 << (b2-1)) & n2) == 0; --b2)
845 ;
846 if (b1 + b2 - 2 > bpw) {
847 errno = ERANGE;
848 return (UQUAD_MAX);
849 }
850
851 /*
852 * Decompose the multiplication to be:
853 * h1 = n1 & ~1
854 * h2 = n2 & ~1
855 * l1 = n1 & 1
856 * l2 = n2 & 1
857 * (h1 + l1) * (h2 + l2)
858 * (h1 * h2) + (h1 * l2) + (l1 * h2) + (l1 * l2)
859 *
860 * Since h1 && h2 do not have the low bit set, we can then say:
861 *
862 * (h1>>1 * h2>>1 * 4) + ...
863 *
864 * So if (h1>>1 * h2>>1) > (1<<(bpw - 2)) then the result will
865 * overflow.
866 *
867 * Finally, if MAX - ((h1 * l2) + (l1 * h2) + (l1 * l2)) < (h1*h2)
868 * then adding in residual amout will cause an overflow.
869 */
870
871 m = (n1 >> 1) * (n2 >> 1);
872
873 if (m >= ((u_quad_t)1 << (bpw-2))) {
874 errno = ERANGE;
875 return (UQUAD_MAX);
876 }
877
878 m *= 4;
879
880 r = (n1 & n2 & 1)
881 + (n2 & 1) * (n1 & ~(u_quad_t)1)
882 + (n1 & 1) * (n2 & ~(u_quad_t)1);
883
884 if ((u_quad_t)(m + r) < m) {
885 errno = ERANGE;
886 return (UQUAD_MAX);
887 }
888 m += r;
889
890 return (m);
891 }
892