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