xref: /netbsd-src/lib/libutil/login_cap.c (revision ed6e96ca70de0617f00b76405550a78656c1af07)
1*ed6e96caSkamil /*	$NetBSD: login_cap.c,v 1.33 2015/10/29 20:29:24 kamil Exp $	*/
2142f3694Smjl 
3142f3694Smjl /*-
4142f3694Smjl  * Copyright (c) 1995,1997 Berkeley Software Design, Inc. All rights reserved.
5142f3694Smjl  *
6142f3694Smjl  * Redistribution and use in source and binary forms, with or without
7142f3694Smjl  * modification, are permitted provided that the following conditions
8142f3694Smjl  * are met:
9142f3694Smjl  * 1. Redistributions of source code must retain the above copyright
10142f3694Smjl  *    notice, this list of conditions and the following disclaimer.
11142f3694Smjl  * 2. Redistributions in binary form must reproduce the above copyright
12142f3694Smjl  *    notice, this list of conditions and the following disclaimer in the
13142f3694Smjl  *    documentation and/or other materials provided with the distribution.
14142f3694Smjl  * 3. All advertising materials mentioning features or use of this software
15142f3694Smjl  *    must display the following acknowledgement:
16142f3694Smjl  *	This product includes software developed by Berkeley Software Design,
17142f3694Smjl  *	Inc.
18142f3694Smjl  * 4. The name of Berkeley Software Design, Inc.  may not be used to endorse
19142f3694Smjl  *    or promote products derived from this software without specific prior
20142f3694Smjl  *    written permission.
21142f3694Smjl  *
22142f3694Smjl  * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``AS IS'' AND
23142f3694Smjl  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24142f3694Smjl  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25142f3694Smjl  * ARE DISCLAIMED.  IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN, INC. BE LIABLE
26142f3694Smjl  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27142f3694Smjl  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28142f3694Smjl  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29142f3694Smjl  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30142f3694Smjl  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31142f3694Smjl  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32142f3694Smjl  * SUCH DAMAGE.
33142f3694Smjl  *
34142f3694Smjl  *	BSDI login_cap.c,v 2.13 1998/02/07 03:17:05 prb Exp
35142f3694Smjl  */
36142f3694Smjl 
37fce98185Sad #include <sys/cdefs.h>
38fce98185Sad #if defined(LIBC_SCCS) && !defined(lint)
39*ed6e96caSkamil __RCSID("$NetBSD: login_cap.c,v 1.33 2015/10/29 20:29:24 kamil Exp $");
40fce98185Sad #endif /* LIBC_SCCS and not lint */
41fce98185Sad 
42142f3694Smjl #include <sys/types.h>
43142f3694Smjl #include <sys/stat.h>
44142f3694Smjl #include <sys/time.h>
45142f3694Smjl #include <sys/resource.h>
465e2e282fSelad #include <sys/param.h>
47142f3694Smjl 
48d06a762aSlukem #include <assert.h>
49b3204d53Smjl #include <ctype.h>
50142f3694Smjl #include <err.h>
51142f3694Smjl #include <errno.h>
52142f3694Smjl #include <fcntl.h>
53142f3694Smjl #include <limits.h>
54142f3694Smjl #include <login_cap.h>
55142f3694Smjl #include <paths.h>
56142f3694Smjl #include <pwd.h>
57142f3694Smjl #include <stdio.h>
58142f3694Smjl #include <stdlib.h>
59142f3694Smjl #include <string.h>
60142f3694Smjl #include <syslog.h>
61142f3694Smjl #include <unistd.h>
6288ec60f2Sad #include <util.h>
63142f3694Smjl 
64fce98185Sad static u_quad_t	multiply(u_quad_t, u_quad_t);
6570756f97Schristos static u_quad_t	strtolimit(const char *, char **, int);
6670756f97Schristos static u_quad_t	strtosize(const char *, char **, int);
6770756f97Schristos static int	gsetrl(login_cap_t *, int, const char *, int type);
68fce98185Sad static int	isinfinite(const char *);
69349633b5Schristos static int	envset(void *, const char *, const char *, int);
70142f3694Smjl 
71142f3694Smjl login_cap_t *
login_getclass(const char * class)7270756f97Schristos login_getclass(const char *class)
73142f3694Smjl {
74dea77b18Schristos 	const char *classfiles[2];
75142f3694Smjl 	login_cap_t *lc;
76142f3694Smjl 	int res;
77142f3694Smjl 
78d06a762aSlukem 	/* class may be NULL */
79d06a762aSlukem 
804c0a4838Sitojun 	if (secure_path(_PATH_LOGIN_CONF) == 0) {
814c0a4838Sitojun 		classfiles[0] = _PATH_LOGIN_CONF;
824c0a4838Sitojun 		classfiles[1] = NULL;
834c0a4838Sitojun 	} else {
844c0a4838Sitojun 		classfiles[0] = NULL;
854c0a4838Sitojun 	}
86142f3694Smjl 
87142f3694Smjl 	if ((lc = malloc(sizeof(login_cap_t))) == NULL) {
88142f3694Smjl 		syslog(LOG_ERR, "%s:%d malloc: %m", __FILE__, __LINE__);
89142f3694Smjl 		return (0);
90142f3694Smjl 	}
91142f3694Smjl 
92142f3694Smjl 	lc->lc_cap = 0;
93142f3694Smjl 	lc->lc_style = 0;
94142f3694Smjl 
95142f3694Smjl 	if (class == NULL || class[0] == '\0')
96142f3694Smjl 		class = LOGIN_DEFCLASS;
97142f3694Smjl 
98142f3694Smjl     	if ((lc->lc_class = strdup(class)) == NULL) {
99142f3694Smjl 		syslog(LOG_ERR, "%s:%d strdup: %m", __FILE__, __LINE__);
100142f3694Smjl 		free(lc);
101142f3694Smjl 		return (0);
102142f3694Smjl 	}
103142f3694Smjl 
1044c0a4838Sitojun 	/*
1054c0a4838Sitojun 	 * Not having a login.conf file is not an error condition.
1064c0a4838Sitojun 	 * The individual routines deal reasonably with missing
1074c0a4838Sitojun 	 * capabilities and use default values.
1084c0a4838Sitojun 	 */
1094c0a4838Sitojun 	if (classfiles[0] == NULL)
1104c0a4838Sitojun 		return(lc);
1114c0a4838Sitojun 
112142f3694Smjl 	if ((res = cgetent(&lc->lc_cap, classfiles, lc->lc_class)) != 0) {
113142f3694Smjl 		lc->lc_cap = 0;
114142f3694Smjl 		switch (res) {
115142f3694Smjl 		case 1:
116142f3694Smjl 			syslog(LOG_ERR, "%s: couldn't resolve 'tc'",
117142f3694Smjl 				lc->lc_class);
118142f3694Smjl 			break;
119142f3694Smjl 		case -1:
1201a771b22Sdrochner 			if (strcmp(lc->lc_class, LOGIN_DEFCLASS) == 0)
121142f3694Smjl 				return (lc);
122142f3694Smjl 			syslog(LOG_ERR, "%s: unknown class", lc->lc_class);
123142f3694Smjl 			break;
124142f3694Smjl 		case -2:
125142f3694Smjl 			syslog(LOG_ERR, "%s: getting class information: %m",
126142f3694Smjl 				lc->lc_class);
127142f3694Smjl 			break;
128142f3694Smjl 		case -3:
129142f3694Smjl 			syslog(LOG_ERR, "%s: 'tc' reference loop",
130142f3694Smjl 				lc->lc_class);
131142f3694Smjl 			break;
132142f3694Smjl 		default:
133142f3694Smjl 			syslog(LOG_ERR, "%s: unexpected cgetent error",
134142f3694Smjl 				lc->lc_class);
135142f3694Smjl 			break;
136142f3694Smjl 		}
137142f3694Smjl 		free(lc->lc_class);
138142f3694Smjl 		free(lc);
139142f3694Smjl 		return (0);
140142f3694Smjl 	}
141142f3694Smjl 	return (lc);
142142f3694Smjl }
143142f3694Smjl 
144b3204d53Smjl login_cap_t *
login_getpwclass(const struct passwd * pwd)145fce98185Sad login_getpwclass(const struct passwd *pwd)
146b3204d53Smjl {
147d06a762aSlukem 
148d06a762aSlukem 	/* pwd may be NULL */
149d06a762aSlukem 
150b3204d53Smjl 	return login_getclass(pwd ? pwd->pw_class : NULL);
151b3204d53Smjl }
152b3204d53Smjl 
153142f3694Smjl char *
login_getcapstr(login_cap_t * lc,const char * cap,char * def,char * e)15470756f97Schristos login_getcapstr(login_cap_t *lc, const char *cap, char *def, char *e)
155142f3694Smjl {
156c469f037Sitojun 	char *res = NULL;
157142f3694Smjl 	int status;
158142f3694Smjl 
159142f3694Smjl 	errno = 0;
160142f3694Smjl 
161d06a762aSlukem 	_DIAGASSERT(cap != NULL);
162d06a762aSlukem 
163923f1864Smjl 	if (!lc || !lc->lc_cap)
164142f3694Smjl 		return (def);
165142f3694Smjl 
166142f3694Smjl 	switch (status = cgetstr(lc->lc_cap, cap, &res)) {
167142f3694Smjl 	case -1:
16806763966Sitojun 		if (res)
16906763966Sitojun 			free(res);
170142f3694Smjl 		return (def);
171142f3694Smjl 	case -2:
172142f3694Smjl 		syslog(LOG_ERR, "%s: getting capability %s: %m",
173142f3694Smjl 		    lc->lc_class, cap);
17406763966Sitojun 		if (res)
17506763966Sitojun 			free(res);
176142f3694Smjl 		return (e);
177142f3694Smjl 	default:
178142f3694Smjl 		if (status >= 0)
179142f3694Smjl 			return (res);
180142f3694Smjl 		syslog(LOG_ERR, "%s: unexpected error with capability %s",
181142f3694Smjl 		    lc->lc_class, cap);
18206763966Sitojun 		if (res)
18306763966Sitojun 			free(res);
184142f3694Smjl 		return (e);
185142f3694Smjl 	}
186142f3694Smjl }
187142f3694Smjl 
188142f3694Smjl quad_t
login_getcaptime(login_cap_t * lc,const char * cap,quad_t def,quad_t e)18970756f97Schristos login_getcaptime(login_cap_t *lc, const char *cap, quad_t def, quad_t e)
190142f3694Smjl {
191142f3694Smjl 	char *ep;
192c469f037Sitojun 	char *res = NULL, *sres;
193142f3694Smjl 	int status;
194142f3694Smjl 	quad_t q, r;
195142f3694Smjl 
196d06a762aSlukem 	_DIAGASSERT(cap != NULL);
197d06a762aSlukem 
198142f3694Smjl 	errno = 0;
199923f1864Smjl 	if (!lc || !lc->lc_cap)
200142f3694Smjl 		return (def);
201142f3694Smjl 
202142f3694Smjl 	switch (status = cgetstr(lc->lc_cap, cap, &res)) {
203142f3694Smjl 	case -1:
20406763966Sitojun 		if (res)
20506763966Sitojun 			free(res);
206142f3694Smjl 		return (def);
207142f3694Smjl 	case -2:
208142f3694Smjl 		syslog(LOG_ERR, "%s: getting capability %s: %m",
209142f3694Smjl 		    lc->lc_class, cap);
210142f3694Smjl 		errno = ERANGE;
21106763966Sitojun 		if (res)
21206763966Sitojun 			free(res);
213142f3694Smjl 		return (e);
214142f3694Smjl 	default:
215142f3694Smjl 		if (status >= 0)
216142f3694Smjl 			break;
217142f3694Smjl 		syslog(LOG_ERR, "%s: unexpected error with capability %s",
218142f3694Smjl 		    lc->lc_class, cap);
219142f3694Smjl 		errno = ERANGE;
22006763966Sitojun 		if (res)
22106763966Sitojun 			free(res);
222142f3694Smjl 		return (e);
223142f3694Smjl 	}
224142f3694Smjl 
225b562264aSmjl 	if (isinfinite(res))
226142f3694Smjl 		return (RLIM_INFINITY);
227142f3694Smjl 
228142f3694Smjl 	errno = 0;
229142f3694Smjl 
230142f3694Smjl 	q = 0;
231142f3694Smjl 	sres = res;
232142f3694Smjl 	while (*res) {
233142f3694Smjl 		r = strtoq(res, &ep, 0);
234142f3694Smjl 		if (!ep || ep == res ||
235142f3694Smjl 		    ((r == QUAD_MIN || r == QUAD_MAX) && errno == ERANGE)) {
236142f3694Smjl invalid:
237142f3694Smjl 			syslog(LOG_ERR, "%s:%s=%s: invalid time",
238142f3694Smjl 			    lc->lc_class, cap, sres);
239142f3694Smjl 			errno = ERANGE;
24006763966Sitojun 			free(sres);
241142f3694Smjl 			return (e);
242142f3694Smjl 		}
243142f3694Smjl 		switch (*ep++) {
244142f3694Smjl 		case '\0':
245142f3694Smjl 			--ep;
246142f3694Smjl 			break;
247142f3694Smjl 		case 's': case 'S':
248142f3694Smjl 			break;
249142f3694Smjl 		case 'm': case 'M':
250142f3694Smjl 			r *= 60;
251142f3694Smjl 			break;
252142f3694Smjl 		case 'h': case 'H':
253142f3694Smjl 			r *= 60 * 60;
254142f3694Smjl 			break;
255142f3694Smjl 		case 'd': case 'D':
256142f3694Smjl 			r *= 60 * 60 * 24;
257142f3694Smjl 			break;
258142f3694Smjl 		case 'w': case 'W':
259142f3694Smjl 			r *= 60 * 60 * 24 * 7;
260142f3694Smjl 			break;
261142f3694Smjl 		case 'y': case 'Y':	/* Pretty absurd */
262142f3694Smjl 			r *= 60 * 60 * 24 * 365;
263142f3694Smjl 			break;
264142f3694Smjl 		default:
265142f3694Smjl 			goto invalid;
266142f3694Smjl 		}
267142f3694Smjl 		res = ep;
268142f3694Smjl 		q += r;
269142f3694Smjl 	}
27006763966Sitojun 	free(sres);
271142f3694Smjl 	return (q);
272142f3694Smjl }
273142f3694Smjl 
274142f3694Smjl quad_t
login_getcapnum(login_cap_t * lc,const char * cap,quad_t def,quad_t e)27570756f97Schristos login_getcapnum(login_cap_t *lc, const char *cap, quad_t def, quad_t e)
276142f3694Smjl {
277142f3694Smjl 	char *ep;
278c469f037Sitojun 	char *res = NULL;
279142f3694Smjl 	int status;
280142f3694Smjl 	quad_t q;
281142f3694Smjl 
282d06a762aSlukem 	_DIAGASSERT(cap != NULL);
283d06a762aSlukem 
284142f3694Smjl 	errno = 0;
285923f1864Smjl 	if (!lc || !lc->lc_cap)
286142f3694Smjl 		return (def);
287142f3694Smjl 
288142f3694Smjl 	switch (status = cgetstr(lc->lc_cap, cap, &res)) {
289142f3694Smjl 	case -1:
29006763966Sitojun 		if (res)
29106763966Sitojun 			free(res);
292142f3694Smjl 		return (def);
293142f3694Smjl 	case -2:
294142f3694Smjl 		syslog(LOG_ERR, "%s: getting capability %s: %m",
295142f3694Smjl 		    lc->lc_class, cap);
296142f3694Smjl 		errno = ERANGE;
29706763966Sitojun 		if (res)
29806763966Sitojun 			free(res);
299142f3694Smjl 		return (e);
300142f3694Smjl 	default:
301142f3694Smjl 		if (status >= 0)
302142f3694Smjl 			break;
303142f3694Smjl 		syslog(LOG_ERR, "%s: unexpected error with capability %s",
304142f3694Smjl 		    lc->lc_class, cap);
305142f3694Smjl 		errno = ERANGE;
30606763966Sitojun 		if (res)
30706763966Sitojun 			free(res);
308142f3694Smjl 		return (e);
309142f3694Smjl 	}
310142f3694Smjl 
311b562264aSmjl 	if (isinfinite(res))
312142f3694Smjl 		return (RLIM_INFINITY);
313142f3694Smjl 
314142f3694Smjl 	errno = 0;
315142f3694Smjl     	q = strtoq(res, &ep, 0);
316142f3694Smjl 	if (!ep || ep == res || ep[0] ||
317142f3694Smjl 	    ((q == QUAD_MIN || q == QUAD_MAX) && errno == ERANGE)) {
318142f3694Smjl 		syslog(LOG_ERR, "%s:%s=%s: invalid number",
319142f3694Smjl 		    lc->lc_class, cap, res);
320142f3694Smjl 		errno = ERANGE;
32106763966Sitojun 		free(res);
322142f3694Smjl 		return (e);
323142f3694Smjl 	}
32406763966Sitojun 	free(res);
325142f3694Smjl 	return (q);
326142f3694Smjl }
327142f3694Smjl 
328142f3694Smjl quad_t
login_getcapsize(login_cap_t * lc,const char * cap,quad_t def,quad_t e)32970756f97Schristos login_getcapsize(login_cap_t *lc, const char *cap, quad_t def, quad_t e)
330142f3694Smjl {
331142f3694Smjl 	char *ep;
332c469f037Sitojun 	char *res = NULL;
333142f3694Smjl 	int status;
334142f3694Smjl 	quad_t q;
335142f3694Smjl 
336d06a762aSlukem 	_DIAGASSERT(cap != NULL);
337d06a762aSlukem 
338142f3694Smjl 	errno = 0;
339142f3694Smjl 
340923f1864Smjl 	if (!lc || !lc->lc_cap)
341142f3694Smjl 		return (def);
342142f3694Smjl 
343142f3694Smjl 	switch (status = cgetstr(lc->lc_cap, cap, &res)) {
344142f3694Smjl 	case -1:
34506763966Sitojun 		if (res)
34606763966Sitojun 			free(res);
347142f3694Smjl 		return (def);
348142f3694Smjl 	case -2:
349142f3694Smjl 		syslog(LOG_ERR, "%s: getting capability %s: %m",
350142f3694Smjl 		    lc->lc_class, cap);
351142f3694Smjl 		errno = ERANGE;
35206763966Sitojun 		if (res)
35306763966Sitojun 			free(res);
354142f3694Smjl 		return (e);
355142f3694Smjl 	default:
356142f3694Smjl 		if (status >= 0)
357142f3694Smjl 			break;
358142f3694Smjl 		syslog(LOG_ERR, "%s: unexpected error with capability %s",
359142f3694Smjl 		    lc->lc_class, cap);
360142f3694Smjl 		errno = ERANGE;
36106763966Sitojun 		if (res)
36206763966Sitojun 			free(res);
363142f3694Smjl 		return (e);
364142f3694Smjl 	}
365142f3694Smjl 
366142f3694Smjl 	errno = 0;
367142f3694Smjl 	q = strtolimit(res, &ep, 0);
368142f3694Smjl 	if (!ep || ep == res || (ep[0] && ep[1]) ||
369142f3694Smjl 	    ((q == QUAD_MIN || q == QUAD_MAX) && errno == ERANGE)) {
370142f3694Smjl 		syslog(LOG_ERR, "%s:%s=%s: invalid size",
371142f3694Smjl 		    lc->lc_class, cap, res);
372142f3694Smjl 		errno = ERANGE;
37306763966Sitojun 		free(res);
374142f3694Smjl 		return (e);
375142f3694Smjl 	}
37606763966Sitojun 	free(res);
377142f3694Smjl 	return (q);
378142f3694Smjl }
379142f3694Smjl 
380142f3694Smjl int
login_getcapbool(login_cap_t * lc,const char * cap,u_int def)38170756f97Schristos login_getcapbool(login_cap_t *lc, const char *cap, u_int def)
382142f3694Smjl {
383d06a762aSlukem 
384d06a762aSlukem 	_DIAGASSERT(cap != NULL);
385d06a762aSlukem 
386923f1864Smjl 	if (!lc || !lc->lc_cap)
387142f3694Smjl 		return (def);
388142f3694Smjl 
389142f3694Smjl 	return (cgetcap(lc->lc_cap, cap, ':') != NULL);
390142f3694Smjl }
391142f3694Smjl 
392142f3694Smjl void
login_close(login_cap_t * lc)393fce98185Sad login_close(login_cap_t *lc)
394142f3694Smjl {
395d06a762aSlukem 
396142f3694Smjl 	if (lc) {
397142f3694Smjl 		if (lc->lc_class)
398142f3694Smjl 			free(lc->lc_class);
399142f3694Smjl 		if (lc->lc_cap)
400142f3694Smjl 			free(lc->lc_cap);
401142f3694Smjl 		if (lc->lc_style)
402142f3694Smjl 			free(lc->lc_style);
403142f3694Smjl 		free(lc);
404142f3694Smjl 	}
405142f3694Smjl }
406142f3694Smjl 
40788ec60f2Sad #define	R_CTIME	1
40888ec60f2Sad #define	R_CSIZE	2
40988ec60f2Sad #define	R_CNUMB	3
410142f3694Smjl 
411142f3694Smjl static struct {
412142f3694Smjl 	int	what;
413142f3694Smjl 	int	type;
41470756f97Schristos 	const char *name;
415142f3694Smjl } r_list[] = {
41688ec60f2Sad 	{ RLIMIT_CPU,		R_CTIME, "cputime", },
41788ec60f2Sad 	{ RLIMIT_FSIZE,		R_CSIZE, "filesize", },
41888ec60f2Sad 	{ RLIMIT_DATA,		R_CSIZE, "datasize", },
41988ec60f2Sad 	{ RLIMIT_STACK,		R_CSIZE, "stacksize", },
42088ec60f2Sad 	{ RLIMIT_RSS,		R_CSIZE, "memoryuse", },
42188ec60f2Sad 	{ RLIMIT_MEMLOCK,	R_CSIZE, "memorylocked", },
42288ec60f2Sad 	{ RLIMIT_NPROC,		R_CNUMB, "maxproc", },
423006f500dSyamt 	{ RLIMIT_NTHR,		R_CNUMB, "maxthread", },
42488ec60f2Sad 	{ RLIMIT_NOFILE,	R_CNUMB, "openfiles", },
42588ec60f2Sad 	{ RLIMIT_CORE,		R_CSIZE, "coredumpsize", },
4262ebcc581Slukem 	{ RLIMIT_SBSIZE,	R_CSIZE, "sbsize", },
4273867e136Skamil 	{ RLIMIT_AS,		R_CSIZE, "vmemoryuse", },
428142f3694Smjl 	{ -1, 0, 0 }
429142f3694Smjl };
430142f3694Smjl 
431142f3694Smjl static int
gsetrl(login_cap_t * lc,int what,const char * name,int type)43270756f97Schristos gsetrl(login_cap_t *lc, int what, const char *name, int type)
433142f3694Smjl {
434142f3694Smjl 	struct rlimit rl;
435142f3694Smjl 	struct rlimit r;
436142f3694Smjl 	char name_cur[32];
437142f3694Smjl 	char name_max[32];
438142f3694Smjl 
439d06a762aSlukem 	_DIAGASSERT(name != NULL);
440d06a762aSlukem 
4412979ced6Schristos 	(void)snprintf(name_cur, sizeof(name_cur), "%s-cur", name);
4422979ced6Schristos 	(void)snprintf(name_max, sizeof(name_max), "%s-max", name);
443142f3694Smjl 
444142f3694Smjl 	if (getrlimit(what, &r)) {
445142f3694Smjl 		syslog(LOG_ERR, "getting resource limit: %m");
446142f3694Smjl 		return (-1);
447142f3694Smjl 	}
448142f3694Smjl 
449920e3814Schristos #define	RCUR	((quad_t)r.rlim_cur)
450920e3814Schristos #define	RMAX	((quad_t)r.rlim_max)
451142f3694Smjl 
452142f3694Smjl 	switch (type) {
45388ec60f2Sad 	case R_CTIME:
454920e3814Schristos 		r.rlim_cur = login_getcaptime(lc, name, RCUR, RCUR);
455920e3814Schristos 		r.rlim_max = login_getcaptime(lc, name, RMAX, RMAX);
456142f3694Smjl 		rl.rlim_cur = login_getcaptime(lc, name_cur, RCUR, RCUR);
457142f3694Smjl 		rl.rlim_max = login_getcaptime(lc, name_max, RMAX, RMAX);
458142f3694Smjl 		break;
45988ec60f2Sad 	case R_CSIZE:
460920e3814Schristos 		r.rlim_cur = login_getcapsize(lc, name, RCUR, RCUR);
461920e3814Schristos 		r.rlim_max = login_getcapsize(lc, name, RMAX, RMAX);
462142f3694Smjl 		rl.rlim_cur = login_getcapsize(lc, name_cur, RCUR, RCUR);
463142f3694Smjl 		rl.rlim_max = login_getcapsize(lc, name_max, RMAX, RMAX);
464142f3694Smjl 		break;
46588ec60f2Sad 	case R_CNUMB:
466920e3814Schristos 		r.rlim_cur = login_getcapnum(lc, name, RCUR, RCUR);
467920e3814Schristos 		r.rlim_max = login_getcapnum(lc, name, RMAX, RMAX);
468142f3694Smjl 		rl.rlim_cur = login_getcapnum(lc, name_cur, RCUR, RCUR);
469142f3694Smjl 		rl.rlim_max = login_getcapnum(lc, name_max, RMAX, RMAX);
470142f3694Smjl 		break;
471142f3694Smjl 	default:
4722979ced6Schristos 		syslog(LOG_ERR, "%s: invalid type %d setting resource limit %s",
4732979ced6Schristos 		    lc->lc_class, type, name);
474142f3694Smjl 		return (-1);
475142f3694Smjl 	}
476142f3694Smjl 
477142f3694Smjl 	if (setrlimit(what, &rl)) {
478142f3694Smjl 		syslog(LOG_ERR, "%s: setting resource limit %s: %m",
479142f3694Smjl 		    lc->lc_class, name);
480142f3694Smjl 		return (-1);
481142f3694Smjl 	}
482142f3694Smjl #undef	RCUR
483142f3694Smjl #undef	RMAX
484142f3694Smjl 	return (0);
485142f3694Smjl }
486142f3694Smjl 
487b3204d53Smjl static int
488349633b5Schristos /*ARGSUSED*/
envset(void * envp __unused,const char * name,const char * value,int overwrite)489af4b0903Schristos envset(void *envp __unused, const char *name, const char *value, int overwrite)
490349633b5Schristos {
491349633b5Schristos 	return setenv(name, value, overwrite);
492349633b5Schristos }
493349633b5Schristos 
494349633b5Schristos int
setuserenv(login_cap_t * lc,envfunc_t senv,void * envp)495349633b5Schristos setuserenv(login_cap_t *lc, envfunc_t senv, void *envp)
496b3204d53Smjl {
49770756f97Schristos 	const char *stop = ", \t";
498e7acb44bSchristos 	size_t i, count;
499b3204d53Smjl 	char *ptr;
500b3204d53Smjl 	char **res;
501b3204d53Smjl 	char *str = login_getcapstr(lc, "setenv", NULL, NULL);
502b3204d53Smjl 
503b3204d53Smjl 	if (str == NULL || *str == '\0')
504b3204d53Smjl 		return 0;
505b3204d53Smjl 
506e7acb44bSchristos 	/*
507e7acb44bSchristos 	 * count the sub-strings, this may over-count since we don't
508e7acb44bSchristos 	 * account for escaped delimiters.
509e7acb44bSchristos 	 */
510b3204d53Smjl 	for (i = 1, ptr = str; *ptr; i++) {
511b3204d53Smjl 		ptr += strcspn(ptr, stop);
512b3204d53Smjl 		if (*ptr)
513b3204d53Smjl 			ptr++;
514b3204d53Smjl 	}
515b3204d53Smjl 
516b3204d53Smjl 	/* allocate ptr array and string */
517b3204d53Smjl 	count = i;
518920e3814Schristos 	res = malloc(count * sizeof(*res) + strlen(str) + 1);
519b3204d53Smjl 
520b3204d53Smjl 	if (!res)
521b3204d53Smjl 		return -1;
522b3204d53Smjl 
523e7acb44bSchristos 	ptr = (char *)(void *)&res[count];
524e7acb44bSchristos 	(void)strcpy(ptr, str);
525b3204d53Smjl 
526b3204d53Smjl 	/* split string */
527e7acb44bSchristos 	for (i = 0; (res[i] = stresep(&ptr, stop, '\\')) != NULL; )
528e7acb44bSchristos 		if (*res[i])
529e7acb44bSchristos 			i++;
530b3204d53Smjl 
531e7acb44bSchristos 	count = i;
532b3204d53Smjl 
533e7acb44bSchristos 	for (i = 0; i < count; i++) {
534fce98185Sad 		if ((ptr = strchr(res[i], '=')) != NULL)
535b3204d53Smjl 			*ptr++ = '\0';
536b3204d53Smjl 		else
53770756f97Schristos 			ptr = NULL;
538349633b5Schristos 		(void)(*senv)(envp, res[i], ptr ? ptr : "", 1);
539b3204d53Smjl 	}
540b3204d53Smjl 
541b562264aSmjl 	free(res);
542b3204d53Smjl 	return 0;
543b3204d53Smjl }
544b3204d53Smjl 
545142f3694Smjl int
setclasscontext(const char * class,u_int flags)54670756f97Schristos setclasscontext(const char *class, u_int flags)
547142f3694Smjl {
548142f3694Smjl 	int ret;
549142f3694Smjl 	login_cap_t *lc;
550142f3694Smjl 
551142f3694Smjl 	flags &= LOGIN_SETRESOURCES | LOGIN_SETPRIORITY | LOGIN_SETUMASK |
552142f3694Smjl 	    LOGIN_SETPATH;
553142f3694Smjl 
554142f3694Smjl 	lc = login_getclass(class);
555142f3694Smjl 	ret = lc ? setusercontext(lc, NULL, 0, flags) : -1;
556142f3694Smjl 	login_close(lc);
557142f3694Smjl 	return (ret);
558142f3694Smjl }
559142f3694Smjl 
560142f3694Smjl int
setusercontext(login_cap_t * lc,struct passwd * pwd,uid_t uid,u_int flags)561fce98185Sad setusercontext(login_cap_t *lc, struct passwd *pwd, uid_t uid, u_int flags)
562142f3694Smjl {
5635e2e282fSelad 	char per_user_tmp[MAXPATHLEN + 1];
564d4a648c3Smjf 	const char *component_name;
565142f3694Smjl 	login_cap_t *flc;
566142f3694Smjl 	quad_t p;
567142f3694Smjl 	int i;
568d4a648c3Smjf 	ssize_t len;
569142f3694Smjl 
570142f3694Smjl 	flc = NULL;
571142f3694Smjl 
572633ab39aSmjl 	if (!lc)
573633ab39aSmjl 		flc = lc = login_getclass(pwd ? pwd->pw_class : NULL);
574142f3694Smjl 
575142f3694Smjl 	/*
576142f3694Smjl 	 * Without the pwd entry being passed we cannot set either
577142f3694Smjl 	 * the group or the login.  We could complain about it.
578142f3694Smjl 	 */
579142f3694Smjl 	if (pwd == NULL)
580142f3694Smjl 		flags &= ~(LOGIN_SETGROUP|LOGIN_SETLOGIN);
581142f3694Smjl 
582b093a779Schristos #ifdef LOGIN_OSETGROUP
583b093a779Schristos 	if (pwd == NULL)
584b093a779Schristos 		flags &= ~LOGIN_OSETGROUP;
585b093a779Schristos 	if (flags & LOGIN_OSETGROUP)
586b093a779Schristos 		flags = (flags & ~LOGIN_OSETGROUP) | LOGIN_SETGROUP;
587b093a779Schristos #endif
588142f3694Smjl 	if (flags & LOGIN_SETRESOURCES)
589142f3694Smjl 		for (i = 0; r_list[i].name; ++i)
5902979ced6Schristos 			(void)gsetrl(lc, r_list[i].what, r_list[i].name,
5912979ced6Schristos 			    r_list[i].type);
592142f3694Smjl 
593142f3694Smjl 	if (flags & LOGIN_SETPRIORITY) {
594232f61faSelad 		p = login_getcapnum(lc, "priority", (quad_t)0, (quad_t)0);
595142f3694Smjl 
596b093a779Schristos 		if (setpriority(PRIO_PROCESS, 0, (int)p) == -1)
597142f3694Smjl 			syslog(LOG_ERR, "%s: setpriority: %m", lc->lc_class);
598142f3694Smjl 	}
599142f3694Smjl 
600142f3694Smjl 	if (flags & LOGIN_SETUMASK) {
601142f3694Smjl 		p = login_getcapnum(lc, "umask", (quad_t) LOGIN_DEFUMASK,
602142f3694Smjl 		    (quad_t)LOGIN_DEFUMASK);
603142f3694Smjl 		umask((mode_t)p);
604142f3694Smjl 	}
605142f3694Smjl 
606b093a779Schristos 	if (flags & LOGIN_SETGID) {
607b093a779Schristos 		if (setgid(pwd->pw_gid) == -1) {
608142f3694Smjl 			syslog(LOG_ERR, "setgid(%d): %m", pwd->pw_gid);
609142f3694Smjl 			login_close(flc);
610142f3694Smjl 			return (-1);
611142f3694Smjl 		}
612b093a779Schristos 	}
613142f3694Smjl 
614b093a779Schristos 	if (flags & LOGIN_SETGROUPS) {
615b093a779Schristos 		if (initgroups(pwd->pw_name, pwd->pw_gid) == -1) {
616142f3694Smjl 			syslog(LOG_ERR, "initgroups(%s,%d): %m",
617142f3694Smjl 			    pwd->pw_name, pwd->pw_gid);
618142f3694Smjl 			login_close(flc);
619142f3694Smjl 			return (-1);
620142f3694Smjl 		}
621142f3694Smjl 	}
622142f3694Smjl 
6235e2e282fSelad 	/* Create per-user temporary directories if needed. */
624d4a648c3Smjf 	if ((len = readlink("/tmp", per_user_tmp,
625d4a648c3Smjf 	    sizeof(per_user_tmp) - 6)) != -1) {
626d4a648c3Smjf 
627d4a648c3Smjf 		static const char atuid[] = "/@ruid";
6285e2e282fSelad 		char *lp;
6295e2e282fSelad 
630d4a648c3Smjf 		/* readlink does not nul-terminate the string */
631d4a648c3Smjf 		per_user_tmp[len] = '\0';
632d4a648c3Smjf 
6335e2e282fSelad 		/* Check if it's magic symlink. */
6345e2e282fSelad 		lp = strstr(per_user_tmp, atuid);
6355e2e282fSelad 		if (lp != NULL && *(lp + (sizeof(atuid) - 1)) == '\0') {
6365e2e282fSelad 			lp++;
6375e2e282fSelad 
638d4a648c3Smjf 			if (snprintf(lp, 11, "/%u", pwd->pw_uid) > 10) {
6395e2e282fSelad 				syslog(LOG_ERR, "real temporary path too long");
6405e2e282fSelad 				login_close(flc);
6415e2e282fSelad 				return (-1);
6425e2e282fSelad 			}
6435e2e282fSelad 			if (mkdir(per_user_tmp, S_IRWXU) != -1) {
644d4a648c3Smjf 				if (chown(per_user_tmp, pwd->pw_uid,
645d4a648c3Smjf 				    pwd->pw_gid)) {
646d4a648c3Smjf 					component_name = "chown";
647d4a648c3Smjf 					goto out;
648d4a648c3Smjf 				}
649d4a648c3Smjf 
650d4a648c3Smjf 				/*
651d4a648c3Smjf 			 	 * Must set sticky bit for tmp directory, some
652d4a648c3Smjf 			 	 * programs rely on this.
653d4a648c3Smjf 			 	 */
654d4a648c3Smjf 				if(chmod(per_user_tmp, S_IRWXU | S_ISVTX)) {
655d4a648c3Smjf 					component_name = "chmod";
656d4a648c3Smjf 					goto out;
657d4a648c3Smjf 				}
6585e2e282fSelad 			} else {
659d4a648c3Smjf 				if (errno != EEXIST) {
660d4a648c3Smjf 					component_name = "mkdir";
661d4a648c3Smjf 					goto out;
662d4a648c3Smjf 				} else {
663d4a648c3Smjf 					/*
664d4a648c3Smjf 					 * We must ensure that we own the
665d4a648c3Smjf 					 * directory and that is has the correct
666d4a648c3Smjf 					 * permissions, otherwise a DOS attack
667d4a648c3Smjf 					 * is possible.
668d4a648c3Smjf 					 */
669d4a648c3Smjf 					struct stat sb;
670d4a648c3Smjf 					if (stat(per_user_tmp, &sb) == -1) {
671d4a648c3Smjf 						component_name = "stat";
672d4a648c3Smjf 						goto out;
673d4a648c3Smjf 					}
674d4a648c3Smjf 
675d4a648c3Smjf 					if (sb.st_uid != pwd->pw_uid) {
676d4a648c3Smjf 						if (chown(per_user_tmp,
677d4a648c3Smjf 						    pwd->pw_uid, pwd->pw_gid)) {
678d4a648c3Smjf 							component_name = "chown";
679d4a648c3Smjf 							goto out;
680d4a648c3Smjf 						}
681d4a648c3Smjf 					}
682d4a648c3Smjf 
683d4a648c3Smjf 					if (sb.st_mode != (S_IRWXU | S_ISVTX)) {
684d4a648c3Smjf 						if (chmod(per_user_tmp,
685d4a648c3Smjf 						    S_IRWXU | S_ISVTX)) {
686d4a648c3Smjf 							component_name = "chmod";
687d4a648c3Smjf 							goto out;
688d4a648c3Smjf 						}
689d4a648c3Smjf 					}
690d4a648c3Smjf 				}
6915e2e282fSelad 			}
6925e2e282fSelad 		}
6935e2e282fSelad 	}
6945e2e282fSelad 	errno = 0;
6955e2e282fSelad 
696142f3694Smjl 	if (flags & LOGIN_SETLOGIN)
697b093a779Schristos 		if (setlogin(pwd->pw_name) == -1) {
698142f3694Smjl 			syslog(LOG_ERR, "setlogin(%s) failure: %m",
699142f3694Smjl 			    pwd->pw_name);
700142f3694Smjl 			login_close(flc);
701142f3694Smjl 			return (-1);
702142f3694Smjl 		}
703142f3694Smjl 
704142f3694Smjl 	if (flags & LOGIN_SETUSER)
705b093a779Schristos 		if (setuid(uid) == -1) {
706142f3694Smjl 			syslog(LOG_ERR, "setuid(%d): %m", uid);
707142f3694Smjl 			login_close(flc);
708142f3694Smjl 			return (-1);
709142f3694Smjl 		}
710142f3694Smjl 
711b3204d53Smjl 	if (flags & LOGIN_SETENV)
712349633b5Schristos 		setuserenv(lc, envset, NULL);
713b3204d53Smjl 
714142f3694Smjl 	if (flags & LOGIN_SETPATH)
715349633b5Schristos 		setuserpath(lc, pwd ? pwd->pw_dir : "", envset, NULL);
716142f3694Smjl 
717142f3694Smjl 	login_close(flc);
718142f3694Smjl 	return (0);
719d4a648c3Smjf 
720d4a648c3Smjf out:
721d4a648c3Smjf 	if (component_name != NULL) {
722d4a648c3Smjf 		syslog(LOG_ERR, "%s %s: %m", component_name, per_user_tmp);
723d4a648c3Smjf 		login_close(flc);
724d4a648c3Smjf 		return (-1);
725d4a648c3Smjf 	} else {
726d4a648c3Smjf 		syslog(LOG_ERR, "%s: %m", per_user_tmp);
727d4a648c3Smjf 		login_close(flc);
728d4a648c3Smjf 		return (-1);
729d4a648c3Smjf 	}
730142f3694Smjl }
731142f3694Smjl 
732349633b5Schristos void
setuserpath(login_cap_t * lc,const char * home,envfunc_t senv,void * envp)733349633b5Schristos setuserpath(login_cap_t *lc, const char *home, envfunc_t senv, void *envp)
734142f3694Smjl {
735142f3694Smjl 	size_t hlen, plen;
736142f3694Smjl 	int cnt = 0;
737142f3694Smjl 	char *path;
73870756f97Schristos 	const char *cpath;
739142f3694Smjl 	char *p, *q;
740142f3694Smjl 
741d06a762aSlukem 	_DIAGASSERT(home != NULL);
742d06a762aSlukem 
743142f3694Smjl 	hlen = strlen(home);
744142f3694Smjl 
745f1a325d1Schristos 	p = path = login_getcapstr(lc, "path", NULL, NULL);
746142f3694Smjl 	if (p) {
747142f3694Smjl 		while (*p)
748142f3694Smjl 			if (*p++ == '~')
749142f3694Smjl 				++cnt;
750142f3694Smjl 		plen = (p - path) + cnt * (hlen + 1) + 1;
751142f3694Smjl 		p = path;
752142f3694Smjl 		q = path = malloc(plen);
753142f3694Smjl 		if (q) {
754142f3694Smjl 			while (*p) {
755142f3694Smjl 				p += strspn(p, " \t");
756142f3694Smjl 				if (*p == '\0')
757142f3694Smjl 					break;
758142f3694Smjl 				plen = strcspn(p, " \t");
759142f3694Smjl 				if (hlen == 0 && *p == '~') {
760142f3694Smjl 					p += plen;
761142f3694Smjl 					continue;
762142f3694Smjl 				}
763142f3694Smjl 				if (q != path)
764142f3694Smjl 					*q++ = ':';
765142f3694Smjl 				if (*p == '~') {
766142f3694Smjl 					strcpy(q, home);
767142f3694Smjl 					q += hlen;
768142f3694Smjl 					++p;
769142f3694Smjl 					--plen;
770142f3694Smjl 				}
771142f3694Smjl 				memcpy(q, p, plen);
772142f3694Smjl 				p += plen;
773142f3694Smjl 				q += plen;
774142f3694Smjl 			}
775142f3694Smjl 			*q = '\0';
776f1a325d1Schristos 			cpath = path;
777142f3694Smjl 		} else
77870756f97Schristos 			cpath = _PATH_DEFPATH;
779142f3694Smjl 	} else
78070756f97Schristos 		cpath = _PATH_DEFPATH;
781349633b5Schristos 	if ((*senv)(envp, "PATH", cpath, 1))
782142f3694Smjl 		warn("could not set PATH");
783142f3694Smjl }
784142f3694Smjl 
785142f3694Smjl /*
786142f3694Smjl  * Convert an expression of the following forms
787142f3694Smjl  * 	1) A number.
788142f3694Smjl  *	2) A number followed by a b (mult by 512).
789142f3694Smjl  *	3) A number followed by a k (mult by 1024).
790142f3694Smjl  *	5) A number followed by a m (mult by 1024 * 1024).
791142f3694Smjl  *	6) A number followed by a g (mult by 1024 * 1024 * 1024).
792142f3694Smjl  *	7) A number followed by a t (mult by 1024 * 1024 * 1024 * 1024).
793142f3694Smjl  *	8) Two or more numbers (with/without k,b,m,g, or t).
794a9356936Swiz  *	   separated by x (also * for backwards compatibility), specifying
795142f3694Smjl  *	   the product of the indicated values.
796142f3694Smjl  */
797fce98185Sad static u_quad_t
strtosize(const char * str,char ** endptr,int radix)79870756f97Schristos strtosize(const char *str, char **endptr, int radix)
799142f3694Smjl {
800142f3694Smjl 	u_quad_t num, num2;
801142f3694Smjl 	char *expr, *expr2;
802142f3694Smjl 
803d06a762aSlukem 	_DIAGASSERT(str != NULL);
804d06a762aSlukem 	/* endptr may be NULL */
805d06a762aSlukem 
806142f3694Smjl 	errno = 0;
807142f3694Smjl 	num = strtouq(str, &expr, radix);
808142f3694Smjl 	if (errno || expr == str) {
809142f3694Smjl 		if (endptr)
810142f3694Smjl 			*endptr = expr;
811142f3694Smjl 		return (num);
812142f3694Smjl 	}
813142f3694Smjl 
814142f3694Smjl 	switch(*expr) {
815142f3694Smjl 	case 'b': case 'B':
816142f3694Smjl 		num = multiply(num, (u_quad_t)512);
817142f3694Smjl 		++expr;
818142f3694Smjl 		break;
819142f3694Smjl 	case 'k': case 'K':
820142f3694Smjl 		num = multiply(num, (u_quad_t)1024);
821142f3694Smjl 		++expr;
822142f3694Smjl 		break;
823142f3694Smjl 	case 'm': case 'M':
824142f3694Smjl 		num = multiply(num, (u_quad_t)1024 * 1024);
825142f3694Smjl 		++expr;
826142f3694Smjl 		break;
827142f3694Smjl 	case 'g': case 'G':
828142f3694Smjl 		num = multiply(num, (u_quad_t)1024 * 1024 * 1024);
829142f3694Smjl 		++expr;
830142f3694Smjl 		break;
831142f3694Smjl 	case 't': case 'T':
832142f3694Smjl 		num = multiply(num, (u_quad_t)1024 * 1024);
833142f3694Smjl 		num = multiply(num, (u_quad_t)1024 * 1024);
834142f3694Smjl 		++expr;
835142f3694Smjl 		break;
836142f3694Smjl 	}
837142f3694Smjl 
838142f3694Smjl 	if (errno)
839142f3694Smjl 		goto erange;
840142f3694Smjl 
841142f3694Smjl 	switch(*expr) {
842142f3694Smjl 	case '*':			/* Backward compatible. */
843142f3694Smjl 	case 'x':
844142f3694Smjl 		num2 = strtosize(expr+1, &expr2, radix);
845142f3694Smjl 		if (errno) {
846142f3694Smjl 			expr = expr2;
847142f3694Smjl 			goto erange;
848142f3694Smjl 		}
849142f3694Smjl 
850142f3694Smjl 		if (expr2 == expr + 1) {
851142f3694Smjl 			if (endptr)
852142f3694Smjl 				*endptr = expr;
853142f3694Smjl 			return (num);
854142f3694Smjl 		}
855142f3694Smjl 		expr = expr2;
856142f3694Smjl 		num = multiply(num, num2);
857142f3694Smjl 		if (errno)
858142f3694Smjl 			goto erange;
859142f3694Smjl 		break;
860142f3694Smjl 	}
861142f3694Smjl 	if (endptr)
862142f3694Smjl 		*endptr = expr;
863142f3694Smjl 	return (num);
864142f3694Smjl erange:
865142f3694Smjl 	if (endptr)
866142f3694Smjl 		*endptr = expr;
867142f3694Smjl 	errno = ERANGE;
868142f3694Smjl 	return (UQUAD_MAX);
869142f3694Smjl }
870142f3694Smjl 
871fce98185Sad static u_quad_t
strtolimit(const char * str,char ** endptr,int radix)87270756f97Schristos strtolimit(const char *str, char **endptr, int radix)
873142f3694Smjl {
874d06a762aSlukem 
875d06a762aSlukem 	_DIAGASSERT(str != NULL);
876d06a762aSlukem 	/* endptr may be NULL */
877d06a762aSlukem 
878b562264aSmjl 	if (isinfinite(str)) {
879142f3694Smjl 		if (endptr)
88070756f97Schristos 			*endptr = (char *)__UNCONST(str) + strlen(str);
881142f3694Smjl 		return ((u_quad_t)RLIM_INFINITY);
882142f3694Smjl 	}
883142f3694Smjl 	return (strtosize(str, endptr, radix));
884142f3694Smjl }
885142f3694Smjl 
886b562264aSmjl static int
isinfinite(const char * s)887b562264aSmjl isinfinite(const char *s)
888b562264aSmjl {
889b562264aSmjl 	static const char *infs[] = {
890b562264aSmjl 		"infinity",
891b562264aSmjl 		"inf",
892b562264aSmjl 		"unlimited",
893b562264aSmjl 		"unlimit",
894b562264aSmjl 		NULL
895b562264aSmjl 	};
896b562264aSmjl 	const char **i;
897b562264aSmjl 
898d06a762aSlukem 	_DIAGASSERT(s != NULL);
899d06a762aSlukem 
900b562264aSmjl 	for (i = infs; *i; i++) {
901b562264aSmjl 		if (!strcasecmp(s, *i))
902b562264aSmjl 			return 1;
903b562264aSmjl 	}
904b562264aSmjl 	return 0;
905b562264aSmjl }
906b562264aSmjl 
907142f3694Smjl static u_quad_t
multiply(u_quad_t n1,u_quad_t n2)908fce98185Sad multiply(u_quad_t n1, u_quad_t n2)
909142f3694Smjl {
910142f3694Smjl 	static int bpw = 0;
911142f3694Smjl 	u_quad_t m;
912142f3694Smjl 	u_quad_t r;
913142f3694Smjl 	int b1, b2;
914142f3694Smjl 
915142f3694Smjl 	/*
916142f3694Smjl 	 * Get rid of the simple cases
917142f3694Smjl 	 */
918142f3694Smjl 	if (n1 == 0 || n2 == 0)
919142f3694Smjl 		return (0);
920142f3694Smjl 	if (n1 == 1)
921142f3694Smjl 		return (n2);
922142f3694Smjl 	if (n2 == 1)
923142f3694Smjl 		return (n1);
924142f3694Smjl 
925142f3694Smjl 	/*
926142f3694Smjl 	 * sizeof() returns number of bytes needed for storage.
927142f3694Smjl 	 * This may be different from the actual number of useful bits.
928142f3694Smjl 	 */
929142f3694Smjl 	if (!bpw) {
930142f3694Smjl 		bpw = sizeof(u_quad_t) * 8;
931142f3694Smjl 		while (((u_quad_t)1 << (bpw-1)) == 0)
932142f3694Smjl 			--bpw;
933142f3694Smjl 	}
934142f3694Smjl 
935142f3694Smjl 	/*
936142f3694Smjl 	 * First check the magnitude of each number.  If the sum of the
937*ed6e96caSkamil 	 * magnitude is to high, reject the number.  (If this test
938142f3694Smjl 	 * is not done then the first multiply below may overflow.)
939142f3694Smjl 	 */
940142f3694Smjl 	for (b1 = bpw; (((u_quad_t)1 << (b1-1)) & n1) == 0; --b1)
941142f3694Smjl 		;
942142f3694Smjl 	for (b2 = bpw; (((u_quad_t)1 << (b2-1)) & n2) == 0; --b2)
943142f3694Smjl 		;
944142f3694Smjl 	if (b1 + b2 - 2 > bpw) {
945142f3694Smjl 		errno = ERANGE;
946142f3694Smjl 		return (UQUAD_MAX);
947142f3694Smjl 	}
948142f3694Smjl 
949142f3694Smjl 	/*
950142f3694Smjl 	 * Decompose the multiplication to be:
951142f3694Smjl 	 * h1 = n1 & ~1
952142f3694Smjl 	 * h2 = n2 & ~1
953142f3694Smjl 	 * l1 = n1 & 1
954142f3694Smjl 	 * l2 = n2 & 1
955142f3694Smjl 	 * (h1 + l1) * (h2 + l2)
956142f3694Smjl 	 * (h1 * h2) + (h1 * l2) + (l1 * h2) + (l1 * l2)
957142f3694Smjl 	 *
958142f3694Smjl 	 * Since h1 && h2 do not have the low bit set, we can then say:
959142f3694Smjl 	 *
960142f3694Smjl 	 * (h1>>1 * h2>>1 * 4) + ...
961142f3694Smjl 	 *
962142f3694Smjl 	 * So if (h1>>1 * h2>>1) > (1<<(bpw - 2)) then the result will
963142f3694Smjl 	 * overflow.
964142f3694Smjl 	 *
965142f3694Smjl 	 * Finally, if MAX - ((h1 * l2) + (l1 * h2) + (l1 * l2)) < (h1*h2)
966*ed6e96caSkamil 	 * then adding in residual amount will cause an overflow.
967142f3694Smjl 	 */
968142f3694Smjl 
969142f3694Smjl 	m = (n1 >> 1) * (n2 >> 1);
970142f3694Smjl 
971142f3694Smjl 	if (m >= ((u_quad_t)1 << (bpw-2))) {
972142f3694Smjl 		errno = ERANGE;
973142f3694Smjl 		return (UQUAD_MAX);
974142f3694Smjl 	}
975142f3694Smjl 
976142f3694Smjl 	m *= 4;
977142f3694Smjl 
978142f3694Smjl 	r = (n1 & n2 & 1)
979142f3694Smjl 	  + (n2 & 1) * (n1 & ~(u_quad_t)1)
980142f3694Smjl 	  + (n1 & 1) * (n2 & ~(u_quad_t)1);
981142f3694Smjl 
982142f3694Smjl 	if ((u_quad_t)(m + r) < m) {
983142f3694Smjl 		errno = ERANGE;
984142f3694Smjl 		return (UQUAD_MAX);
985142f3694Smjl 	}
986142f3694Smjl 	m += r;
987142f3694Smjl 
988142f3694Smjl 	return (m);
989142f3694Smjl }
990