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