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