login_cap.c revision 1.10 1 /* $NetBSD: login_cap.c,v 1.10 2001/01/03 15:41:19 lukem 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.10 2001/01/03 15:41:19 lukem 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 * seperated 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