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