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