1 /* $NetBSD: login_cap.c,v 1.28 2007/10/06 21:51:22 christos Exp $ */
4 * Copyright (c) 1995,1997 Berkeley Software Design, Inc. All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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,
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
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
34 * BSDI login_cap.c,v 2.13 1998/02/07 03:17:05 prb Exp
37 #include <sys/cdefs.h>
38 #if defined(LIBC_SCCS) && !defined(lint)
39 __RCSID("$NetBSD: login_cap.c,v 1.28 2007/10/06 21:51:22 christos Exp $");
40 #endif /* LIBC_SCCS and not lint */
42 #include <sys/types.h>
45 #include <sys/resource.h>
46 #include <sys/param.h>
54 #include <login_cap.h>
64 static u_quad_t
multiply(u_quad_t
, u_quad_t
);
65 static u_quad_t
strtolimit(const char *, char **, int);
66 static u_quad_t
strtosize(const char *, char **, int);
67 static int gsetrl(login_cap_t
*, int, const char *, int type
);
68 static int isinfinite(const char *);
69 static int envset(void *, const char *, const char *, int);
72 login_getclass(const char *class)
74 const char *classfiles
[2];
78 /* class may be NULL */
80 if (secure_path(_PATH_LOGIN_CONF
) == 0) {
81 classfiles
[0] = _PATH_LOGIN_CONF
;
87 if ((lc
= malloc(sizeof(login_cap_t
))) == NULL
) {
88 syslog(LOG_ERR
, "%s:%d malloc: %m", __FILE__
, __LINE__
);
95 if (class == NULL
|| class[0] == '\0')
96 class = LOGIN_DEFCLASS
;
98 if ((lc
->lc_class
= strdup(class)) == NULL
) {
99 syslog(LOG_ERR
, "%s:%d strdup: %m", __FILE__
, __LINE__
);
105 * Not having a login.conf file is not an error condition.
106 * The individual routines deal reasonably with missing
107 * capabilities and use default values.
109 if (classfiles
[0] == NULL
)
112 if ((res
= cgetent(&lc
->lc_cap
, classfiles
, lc
->lc_class
)) != 0) {
116 syslog(LOG_ERR
, "%s: couldn't resolve 'tc'",
120 if (strcmp(lc
->lc_class
, LOGIN_DEFCLASS
) == 0)
122 syslog(LOG_ERR
, "%s: unknown class", lc
->lc_class
);
125 syslog(LOG_ERR
, "%s: getting class information: %m",
129 syslog(LOG_ERR
, "%s: 'tc' reference loop",
133 syslog(LOG_ERR
, "%s: unexpected cgetent error",
145 login_getpwclass(const struct passwd
*pwd
)
148 /* pwd may be NULL */
150 return login_getclass(pwd
? pwd
->pw_class
: NULL
);
154 login_getcapstr(login_cap_t
*lc
, const char *cap
, char *def
, char *e
)
161 _DIAGASSERT(cap
!= NULL
);
163 if (!lc
|| !lc
->lc_cap
)
166 switch (status
= cgetstr(lc
->lc_cap
, cap
, &res
)) {
172 syslog(LOG_ERR
, "%s: getting capability %s: %m",
180 syslog(LOG_ERR
, "%s: unexpected error with capability %s",
189 login_getcaptime(login_cap_t
*lc
, const char *cap
, quad_t def
, quad_t e
)
192 char *res
= NULL
, *sres
;
196 _DIAGASSERT(cap
!= NULL
);
199 if (!lc
|| !lc
->lc_cap
)
202 switch (status
= cgetstr(lc
->lc_cap
, cap
, &res
)) {
208 syslog(LOG_ERR
, "%s: getting capability %s: %m",
217 syslog(LOG_ERR
, "%s: unexpected error with capability %s",
226 return (RLIM_INFINITY
);
233 r
= strtoq(res
, &ep
, 0);
234 if (!ep
|| ep
== res
||
235 ((r
== QUAD_MIN
|| r
== QUAD_MAX
) && errno
== ERANGE
)) {
237 syslog(LOG_ERR
, "%s:%s=%s: invalid time",
238 lc
->lc_class
, cap
, sres
);
259 r
*= 60 * 60 * 24 * 7;
261 case 'y': case 'Y': /* Pretty absurd */
262 r
*= 60 * 60 * 24 * 365;
275 login_getcapnum(login_cap_t
*lc
, const char *cap
, quad_t def
, quad_t e
)
282 _DIAGASSERT(cap
!= NULL
);
285 if (!lc
|| !lc
->lc_cap
)
288 switch (status
= cgetstr(lc
->lc_cap
, cap
, &res
)) {
294 syslog(LOG_ERR
, "%s: getting capability %s: %m",
303 syslog(LOG_ERR
, "%s: unexpected error with capability %s",
312 return (RLIM_INFINITY
);
315 q
= strtoq(res
, &ep
, 0);
316 if (!ep
|| ep
== res
|| ep
[0] ||
317 ((q
== QUAD_MIN
|| q
== QUAD_MAX
) && errno
== ERANGE
)) {
318 syslog(LOG_ERR
, "%s:%s=%s: invalid number",
319 lc
->lc_class
, cap
, res
);
329 login_getcapsize(login_cap_t
*lc
, const char *cap
, quad_t def
, quad_t e
)
336 _DIAGASSERT(cap
!= NULL
);
340 if (!lc
|| !lc
->lc_cap
)
343 switch (status
= cgetstr(lc
->lc_cap
, cap
, &res
)) {
349 syslog(LOG_ERR
, "%s: getting capability %s: %m",
358 syslog(LOG_ERR
, "%s: unexpected error with capability %s",
367 q
= strtolimit(res
, &ep
, 0);
368 if (!ep
|| ep
== res
|| (ep
[0] && ep
[1]) ||
369 ((q
== QUAD_MIN
|| q
== QUAD_MAX
) && errno
== ERANGE
)) {
370 syslog(LOG_ERR
, "%s:%s=%s: invalid size",
371 lc
->lc_class
, cap
, res
);
381 login_getcapbool(login_cap_t
*lc
, const char *cap
, u_int def
)
384 _DIAGASSERT(cap
!= NULL
);
386 if (!lc
|| !lc
->lc_cap
)
389 return (cgetcap(lc
->lc_cap
, cap
, ':') != NULL
);
393 login_close(login_cap_t
*lc
)
416 { RLIMIT_CPU
, R_CTIME
, "cputime", },
417 { RLIMIT_FSIZE
, R_CSIZE
, "filesize", },
418 { RLIMIT_DATA
, R_CSIZE
, "datasize", },
419 { RLIMIT_STACK
, R_CSIZE
, "stacksize", },
420 { RLIMIT_RSS
, R_CSIZE
, "memoryuse", },
421 { RLIMIT_MEMLOCK
, R_CSIZE
, "memorylocked", },
422 { RLIMIT_NPROC
, R_CNUMB
, "maxproc", },
423 { RLIMIT_NOFILE
, R_CNUMB
, "openfiles", },
424 { RLIMIT_CORE
, R_CSIZE
, "coredumpsize", },
425 { RLIMIT_SBSIZE
, R_CSIZE
, "sbsize", },
430 gsetrl(login_cap_t
*lc
, int what
, const char *name
, int type
)
437 _DIAGASSERT(name
!= NULL
);
439 (void)snprintf(name_cur
, sizeof(name_cur
), "%s-cur", name
);
440 (void)snprintf(name_max
, sizeof(name_max
), "%s-max", name
);
442 if (getrlimit(what
, &r
)) {
443 syslog(LOG_ERR
, "getting resource limit: %m");
447 #define RCUR r.rlim_cur
448 #define RMAX r.rlim_max
452 RCUR
= login_getcaptime(lc
, name
, RCUR
, RCUR
);
453 RMAX
= login_getcaptime(lc
, name
, RMAX
, RMAX
);
454 rl
.rlim_cur
= login_getcaptime(lc
, name_cur
, RCUR
, RCUR
);
455 rl
.rlim_max
= login_getcaptime(lc
, name_max
, RMAX
, RMAX
);
458 RCUR
= login_getcapsize(lc
, name
, RCUR
, RCUR
);
459 RMAX
= login_getcapsize(lc
, name
, RMAX
, RMAX
);
460 rl
.rlim_cur
= login_getcapsize(lc
, name_cur
, RCUR
, RCUR
);
461 rl
.rlim_max
= login_getcapsize(lc
, name_max
, RMAX
, RMAX
);
464 RCUR
= login_getcapnum(lc
, name
, RCUR
, RCUR
);
465 RMAX
= login_getcapnum(lc
, name
, RMAX
, RMAX
);
466 rl
.rlim_cur
= login_getcapnum(lc
, name_cur
, RCUR
, RCUR
);
467 rl
.rlim_max
= login_getcapnum(lc
, name_max
, RMAX
, RMAX
);
470 syslog(LOG_ERR
, "%s: invalid type %d setting resource limit %s",
471 lc
->lc_class
, type
, name
);
475 if (setrlimit(what
, &rl
)) {
476 syslog(LOG_ERR
, "%s: setting resource limit %s: %m",
487 envset(void *envp __unused
, const char *name
, const char *value
, int overwrite
)
489 return setenv(name
, value
, overwrite
);
493 setuserenv(login_cap_t
*lc
, envfunc_t senv
, void *envp
)
495 const char *stop
= ", \t";
499 char *str
= login_getcapstr(lc
, "setenv", NULL
, NULL
);
501 if (str
== NULL
|| *str
== '\0')
505 * count the sub-strings, this may over-count since we don't
506 * account for escaped delimiters.
508 for (i
= 1, ptr
= str
; *ptr
; i
++) {
509 ptr
+= strcspn(ptr
, stop
);
514 /* allocate ptr array and string */
516 res
= malloc(count
* sizeof(char *) + strlen(str
) + 1);
521 ptr
= (char *)(void *)&res
[count
];
522 (void)strcpy(ptr
, str
);
525 for (i
= 0; (res
[i
] = stresep(&ptr
, stop
, '\\')) != NULL
; )
531 for (i
= 0; i
< count
; i
++) {
532 if ((ptr
= strchr(res
[i
], '=')) != NULL
)
536 (void)(*senv
)(envp
, res
[i
], ptr
? ptr
: "", 1);
544 setclasscontext(const char *class, u_int flags
)
549 flags
&= LOGIN_SETRESOURCES
| LOGIN_SETPRIORITY
| LOGIN_SETUMASK
|
552 lc
= login_getclass(class);
553 ret
= lc
? setusercontext(lc
, NULL
, 0, flags
) : -1;
559 setusercontext(login_cap_t
*lc
, struct passwd
*pwd
, uid_t uid
, u_int flags
)
561 char per_user_tmp
[MAXPATHLEN
+ 1];
562 const char *component_name
;
571 flc
= lc
= login_getclass(pwd
? pwd
->pw_class
: NULL
);
574 * Without the pwd entry being passed we cannot set either
575 * the group or the login. We could complain about it.
578 flags
&= ~(LOGIN_SETGROUP
|LOGIN_SETLOGIN
);
580 #ifdef LOGIN_OSETGROUP
582 flags
&= ~LOGIN_OSETGROUP
;
583 if (flags
& LOGIN_OSETGROUP
)
584 flags
= (flags
& ~LOGIN_OSETGROUP
) | LOGIN_SETGROUP
;
586 if (flags
& LOGIN_SETRESOURCES
)
587 for (i
= 0; r_list
[i
].name
; ++i
)
588 (void)gsetrl(lc
, r_list
[i
].what
, r_list
[i
].name
,
591 if (flags
& LOGIN_SETPRIORITY
) {
592 p
= login_getcapnum(lc
, "priority", (quad_t
)0, (quad_t
)0);
594 if (setpriority(PRIO_PROCESS
, 0, (int)p
) == -1)
595 syslog(LOG_ERR
, "%s: setpriority: %m", lc
->lc_class
);
598 if (flags
& LOGIN_SETUMASK
) {
599 p
= login_getcapnum(lc
, "umask", (quad_t
) LOGIN_DEFUMASK
,
600 (quad_t
)LOGIN_DEFUMASK
);
604 if (flags
& LOGIN_SETGID
) {
605 if (setgid(pwd
->pw_gid
) == -1) {
606 syslog(LOG_ERR
, "setgid(%d): %m", pwd
->pw_gid
);
612 if (flags
& LOGIN_SETGROUPS
) {
613 if (initgroups(pwd
->pw_name
, pwd
->pw_gid
) == -1) {
614 syslog(LOG_ERR
, "initgroups(%s,%d): %m",
615 pwd
->pw_name
, pwd
->pw_gid
);
621 /* Create per-user temporary directories if needed. */
622 if ((len
= readlink("/tmp", per_user_tmp
,
623 sizeof(per_user_tmp
) - 6)) != -1) {
625 static const char atuid
[] = "/@ruid";
628 /* readlink does not nul-terminate the string */
629 per_user_tmp
[len
] = '\0';
631 /* Check if it's magic symlink. */
632 lp
= strstr(per_user_tmp
, atuid
);
633 if (lp
!= NULL
&& *(lp
+ (sizeof(atuid
) - 1)) == '\0') {
636 if (snprintf(lp
, 11, "/%u", pwd
->pw_uid
) > 10) {
637 syslog(LOG_ERR
, "real temporary path too long");
641 if (mkdir(per_user_tmp
, S_IRWXU
) != -1) {
642 if (chown(per_user_tmp
, pwd
->pw_uid
,
644 component_name
= "chown";
649 * Must set sticky bit for tmp directory, some
650 * programs rely on this.
652 if(chmod(per_user_tmp
, S_IRWXU
| S_ISVTX
)) {
653 component_name
= "chmod";
657 if (errno
!= EEXIST
) {
658 component_name
= "mkdir";
662 * We must ensure that we own the
663 * directory and that is has the correct
664 * permissions, otherwise a DOS attack
668 if (stat(per_user_tmp
, &sb
) == -1) {
669 component_name
= "stat";
673 if (sb
.st_uid
!= pwd
->pw_uid
) {
674 if (chown(per_user_tmp
,
675 pwd
->pw_uid
, pwd
->pw_gid
)) {
676 component_name
= "chown";
681 if (sb
.st_mode
!= (S_IRWXU
| S_ISVTX
)) {
682 if (chmod(per_user_tmp
,
683 S_IRWXU
| S_ISVTX
)) {
684 component_name
= "chmod";
694 if (flags
& LOGIN_SETLOGIN
)
695 if (setlogin(pwd
->pw_name
) == -1) {
696 syslog(LOG_ERR
, "setlogin(%s) failure: %m",
702 if (flags
& LOGIN_SETUSER
)
703 if (setuid(uid
) == -1) {
704 syslog(LOG_ERR
, "setuid(%d): %m", uid
);
709 if (flags
& LOGIN_SETENV
)
710 setuserenv(lc
, envset
, NULL
);
712 if (flags
& LOGIN_SETPATH
)
713 setuserpath(lc
, pwd
? pwd
->pw_dir
: "", envset
, NULL
);
719 if (component_name
!= NULL
) {
720 syslog(LOG_ERR
, "%s %s: %m", component_name
, per_user_tmp
);
724 syslog(LOG_ERR
, "%s: %m", per_user_tmp
);
731 setuserpath(login_cap_t
*lc
, const char *home
, envfunc_t senv
, void *envp
)
739 _DIAGASSERT(home
!= NULL
);
743 p
= path
= login_getcapstr(lc
, "path", NULL
, NULL
);
748 plen
= (p
- path
) + cnt
* (hlen
+ 1) + 1;
750 q
= path
= malloc(plen
);
753 p
+= strspn(p
, " \t");
756 plen
= strcspn(p
, " \t");
757 if (hlen
== 0 && *p
== '~') {
776 cpath
= _PATH_DEFPATH
;
778 cpath
= _PATH_DEFPATH
;
779 if ((*senv
)(envp
, "PATH", cpath
, 1))
780 warn("could not set PATH");
784 * Convert an expression of the following forms
786 * 2) A number followed by a b (mult by 512).
787 * 3) A number followed by a k (mult by 1024).
788 * 5) A number followed by a m (mult by 1024 * 1024).
789 * 6) A number followed by a g (mult by 1024 * 1024 * 1024).
790 * 7) A number followed by a t (mult by 1024 * 1024 * 1024 * 1024).
791 * 8) Two or more numbers (with/without k,b,m,g, or t).
792 * separated by x (also * for backwards compatibility), specifying
793 * the product of the indicated values.
796 strtosize(const char *str
, char **endptr
, int radix
)
801 _DIAGASSERT(str
!= NULL
);
802 /* endptr may be NULL */
805 num
= strtouq(str
, &expr
, radix
);
806 if (errno
|| expr
== str
) {
814 num
= multiply(num
, (u_quad_t
)512);
818 num
= multiply(num
, (u_quad_t
)1024);
822 num
= multiply(num
, (u_quad_t
)1024 * 1024);
826 num
= multiply(num
, (u_quad_t
)1024 * 1024 * 1024);
830 num
= multiply(num
, (u_quad_t
)1024 * 1024);
831 num
= multiply(num
, (u_quad_t
)1024 * 1024);
840 case '*': /* Backward compatible. */
842 num2
= strtosize(expr
+1, &expr2
, radix
);
848 if (expr2
== expr
+ 1) {
854 num
= multiply(num
, num2
);
870 strtolimit(const char *str
, char **endptr
, int radix
)
873 _DIAGASSERT(str
!= NULL
);
874 /* endptr may be NULL */
876 if (isinfinite(str
)) {
878 *endptr
= (char *)__UNCONST(str
) + strlen(str
);
879 return ((u_quad_t
)RLIM_INFINITY
);
881 return (strtosize(str
, endptr
, radix
));
885 isinfinite(const char *s
)
887 static const char *infs
[] = {
896 _DIAGASSERT(s
!= NULL
);
898 for (i
= infs
; *i
; i
++) {
899 if (!strcasecmp(s
, *i
))
906 multiply(u_quad_t n1
, u_quad_t n2
)
914 * Get rid of the simple cases
916 if (n1
== 0 || n2
== 0)
924 * sizeof() returns number of bytes needed for storage.
925 * This may be different from the actual number of useful bits.
928 bpw
= sizeof(u_quad_t
) * 8;
929 while (((u_quad_t
)1 << (bpw
-1)) == 0)
934 * First check the magnitude of each number. If the sum of the
935 * magnatude is way to high, reject the number. (If this test
936 * is not done then the first multiply below may overflow.)
938 for (b1
= bpw
; (((u_quad_t
)1 << (b1
-1)) & n1
) == 0; --b1
)
940 for (b2
= bpw
; (((u_quad_t
)1 << (b2
-1)) & n2
) == 0; --b2
)
942 if (b1
+ b2
- 2 > bpw
) {
948 * Decompose the multiplication to be:
953 * (h1 + l1) * (h2 + l2)
954 * (h1 * h2) + (h1 * l2) + (l1 * h2) + (l1 * l2)
956 * Since h1 && h2 do not have the low bit set, we can then say:
958 * (h1>>1 * h2>>1 * 4) + ...
960 * So if (h1>>1 * h2>>1) > (1<<(bpw - 2)) then the result will
963 * Finally, if MAX - ((h1 * l2) + (l1 * h2) + (l1 * l2)) < (h1*h2)
964 * then adding in residual amout will cause an overflow.
967 m
= (n1
>> 1) * (n2
>> 1);
969 if (m
>= ((u_quad_t
)1 << (bpw
-2))) {
977 + (n2
& 1) * (n1
& ~(u_quad_t
)1)
978 + (n1
& 1) * (n2
& ~(u_quad_t
)1);
980 if ((u_quad_t
)(m
+ r
) < m
) {