1 /* $NetBSD: login_cap.c,v 1.30 2012/04/07 16:16:34 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.30 2012/04/07 16:16:34 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", },
421 { RLIMIT_RSS
, R_CSIZE
, "memoryuse", },
422 { RLIMIT_MEMLOCK
, R_CSIZE
, "memorylocked", },
423 { RLIMIT_NPROC
, R_CNUMB
, "maxproc", },
425 { RLIMIT_NOFILE
, R_CNUMB
, "openfiles", },
426 { RLIMIT_CORE
, R_CSIZE
, "coredumpsize", },
428 { RLIMIT_SBSIZE
, R_CSIZE
, "sbsize", },
434 gsetrl(login_cap_t
*lc
, int what
, const char *name
, int type
)
441 _DIAGASSERT(name
!= NULL
);
443 (void)snprintf(name_cur
, sizeof(name_cur
), "%s-cur", name
);
444 (void)snprintf(name_max
, sizeof(name_max
), "%s-max", name
);
446 if (getrlimit(what
, &r
)) {
447 syslog(LOG_ERR
, "getting resource limit: %m");
451 #define RCUR ((quad_t)r.rlim_cur)
452 #define RMAX ((quad_t)r.rlim_max)
456 r
.rlim_cur
= login_getcaptime(lc
, name
, RCUR
, RCUR
);
457 r
.rlim_max
= login_getcaptime(lc
, name
, RMAX
, RMAX
);
458 rl
.rlim_cur
= login_getcaptime(lc
, name_cur
, RCUR
, RCUR
);
459 rl
.rlim_max
= login_getcaptime(lc
, name_max
, RMAX
, RMAX
);
462 r
.rlim_cur
= login_getcapsize(lc
, name
, RCUR
, RCUR
);
463 r
.rlim_max
= login_getcapsize(lc
, name
, RMAX
, RMAX
);
464 rl
.rlim_cur
= login_getcapsize(lc
, name_cur
, RCUR
, RCUR
);
465 rl
.rlim_max
= login_getcapsize(lc
, name_max
, RMAX
, RMAX
);
468 r
.rlim_cur
= login_getcapnum(lc
, name
, RCUR
, RCUR
);
469 r
.rlim_max
= login_getcapnum(lc
, name
, RMAX
, RMAX
);
470 rl
.rlim_cur
= login_getcapnum(lc
, name_cur
, RCUR
, RCUR
);
471 rl
.rlim_max
= login_getcapnum(lc
, name_max
, RMAX
, RMAX
);
474 syslog(LOG_ERR
, "%s: invalid type %d setting resource limit %s",
475 lc
->lc_class
, type
, name
);
480 if (setrlimit(what
, &rl
)) {
481 syslog(LOG_ERR
, "%s: setting resource limit %s: %m",
493 envset(void *envp __unused
, const char *name
, const char *value
, int overwrite
)
495 return setenv(name
, value
, overwrite
);
499 setuserenv(login_cap_t
*lc
, envfunc_t senv
, void *envp
)
501 const char *stop
= ", \t";
505 char *str
= login_getcapstr(lc
, "setenv", NULL
, NULL
);
507 if (str
== NULL
|| *str
== '\0')
511 * count the sub-strings, this may over-count since we don't
512 * account for escaped delimiters.
514 for (i
= 1, ptr
= str
; *ptr
; i
++) {
515 ptr
+= strcspn(ptr
, stop
);
520 /* allocate ptr array and string */
522 res
= malloc(count
* sizeof(*res
) + strlen(str
) + 1);
527 ptr
= (char *)(void *)&res
[count
];
528 (void)strcpy(ptr
, str
);
531 for (i
= 0; (res
[i
] = stresep(&ptr
, stop
, '\\')) != NULL
; )
537 for (i
= 0; i
< count
; i
++) {
538 if ((ptr
= strchr(res
[i
], '=')) != NULL
)
542 (void)(*senv
)(envp
, res
[i
], ptr
? ptr
: "", 1);
550 setclasscontext(const char *class, u_int flags
)
555 flags
&= LOGIN_SETRESOURCES
| LOGIN_SETPRIORITY
| LOGIN_SETUMASK
|
558 lc
= login_getclass(class);
559 ret
= lc
? setusercontext(lc
, NULL
, 0, flags
) : -1;
565 setusercontext(login_cap_t
*lc
, struct passwd
*pwd
, uid_t uid
, u_int flags
)
567 char per_user_tmp
[MAXPATHLEN
+ 1];
568 const char *component_name
;
577 flc
= lc
= login_getclass(pwd
? pwd
->pw_class
: NULL
);
579 #define LOGIN_SETLOGIN 0
583 * Without the pwd entry being passed we cannot set either
584 * the group or the login. We could complain about it.
587 flags
&= ~(LOGIN_SETGROUP
|LOGIN_SETLOGIN
);
589 #ifdef LOGIN_OSETGROUP
591 flags
&= ~LOGIN_OSETGROUP
;
592 if (flags
& LOGIN_OSETGROUP
)
593 flags
= (flags
& ~LOGIN_OSETGROUP
) | LOGIN_SETGROUP
;
595 if (flags
& LOGIN_SETRESOURCES
)
596 for (i
= 0; r_list
[i
].name
; ++i
)
597 (void)gsetrl(lc
, r_list
[i
].what
, r_list
[i
].name
,
600 if (flags
& LOGIN_SETPRIORITY
) {
601 p
= login_getcapnum(lc
, "priority", (quad_t
)0, (quad_t
)0);
603 if (setpriority(PRIO_PROCESS
, 0, (int)p
) == -1)
604 syslog(LOG_ERR
, "%s: setpriority: %m", lc
->lc_class
);
607 if (flags
& LOGIN_SETUMASK
) {
608 p
= login_getcapnum(lc
, "umask", (quad_t
) LOGIN_DEFUMASK
,
609 (quad_t
)LOGIN_DEFUMASK
);
613 if (flags
& LOGIN_SETGID
) {
614 if (setgid(pwd
->pw_gid
) == -1) {
615 syslog(LOG_ERR
, "setgid(%d): %m", pwd
->pw_gid
);
621 if (flags
& LOGIN_SETGROUPS
) {
622 if (initgroups(pwd
->pw_name
, pwd
->pw_gid
) == -1) {
623 syslog(LOG_ERR
, "initgroups(%s,%d): %m",
624 pwd
->pw_name
, pwd
->pw_gid
);
630 /* Create per-user temporary directories if needed. */
631 if ((len
= readlink("/tmp", per_user_tmp
,
632 sizeof(per_user_tmp
) - 6)) != -1) {
634 static const char atuid
[] = "/@ruid";
637 /* readlink does not nul-terminate the string */
638 per_user_tmp
[len
] = '\0';
640 /* Check if it's magic symlink. */
641 lp
= strstr(per_user_tmp
, atuid
);
642 if (lp
!= NULL
&& *(lp
+ (sizeof(atuid
) - 1)) == '\0') {
645 if (snprintf(lp
, 11, "/%u", pwd
->pw_uid
) > 10) {
646 syslog(LOG_ERR
, "real temporary path too long");
650 if (mkdir(per_user_tmp
, S_IRWXU
) != -1) {
651 if (chown(per_user_tmp
, pwd
->pw_uid
,
653 component_name
= "chown";
658 * Must set sticky bit for tmp directory, some
659 * programs rely on this.
661 if(chmod(per_user_tmp
, S_IRWXU
| S_ISVTX
)) {
662 component_name
= "chmod";
666 if (errno
!= EEXIST
) {
667 component_name
= "mkdir";
671 * We must ensure that we own the
672 * directory and that is has the correct
673 * permissions, otherwise a DOS attack
677 if (stat(per_user_tmp
, &sb
) == -1) {
678 component_name
= "stat";
682 if (sb
.st_uid
!= pwd
->pw_uid
) {
683 if (chown(per_user_tmp
,
684 pwd
->pw_uid
, pwd
->pw_gid
)) {
685 component_name
= "chown";
690 if (sb
.st_mode
!= (S_IRWXU
| S_ISVTX
)) {
691 if (chmod(per_user_tmp
,
692 S_IRWXU
| S_ISVTX
)) {
693 component_name
= "chmod";
703 if (flags
& LOGIN_SETLOGIN
)
704 if (setlogin(pwd
->pw_name
) == -1) {
705 syslog(LOG_ERR
, "setlogin(%s) failure: %m",
711 if (flags
& LOGIN_SETUSER
)
712 if (setuid(uid
) == -1) {
713 syslog(LOG_ERR
, "setuid(%d): %m", uid
);
718 if (flags
& LOGIN_SETENV
)
719 setuserenv(lc
, envset
, NULL
);
721 if (flags
& LOGIN_SETPATH
)
722 setuserpath(lc
, pwd
? pwd
->pw_dir
: "", envset
, NULL
);
728 if (component_name
!= NULL
) {
729 syslog(LOG_ERR
, "%s %s: %m", component_name
, per_user_tmp
);
733 syslog(LOG_ERR
, "%s: %m", per_user_tmp
);
740 setuserpath(login_cap_t
*lc
, const char *home
, envfunc_t senv
, void *envp
)
748 _DIAGASSERT(home
!= NULL
);
752 p
= path
= login_getcapstr(lc
, "path", NULL
, NULL
);
757 plen
= (p
- path
) + cnt
* (hlen
+ 1) + 1;
759 q
= path
= malloc(plen
);
762 p
+= strspn(p
, " \t");
765 plen
= strcspn(p
, " \t");
766 if (hlen
== 0 && *p
== '~') {
785 cpath
= _PATH_DEFPATH
;
787 cpath
= _PATH_DEFPATH
;
788 if ((*senv
)(envp
, "PATH", cpath
, 1))
789 warn("could not set PATH");
793 * Convert an expression of the following forms
795 * 2) A number followed by a b (mult by 512).
796 * 3) A number followed by a k (mult by 1024).
797 * 5) A number followed by a m (mult by 1024 * 1024).
798 * 6) A number followed by a g (mult by 1024 * 1024 * 1024).
799 * 7) A number followed by a t (mult by 1024 * 1024 * 1024 * 1024).
800 * 8) Two or more numbers (with/without k,b,m,g, or t).
801 * separated by x (also * for backwards compatibility), specifying
802 * the product of the indicated values.
805 strtosize(const char *str
, char **endptr
, int radix
)
810 _DIAGASSERT(str
!= NULL
);
811 /* endptr may be NULL */
814 num
= strtouq(str
, &expr
, radix
);
815 if (errno
|| expr
== str
) {
823 num
= multiply(num
, (u_quad_t
)512);
827 num
= multiply(num
, (u_quad_t
)1024);
831 num
= multiply(num
, (u_quad_t
)1024 * 1024);
835 num
= multiply(num
, (u_quad_t
)1024 * 1024 * 1024);
839 num
= multiply(num
, (u_quad_t
)1024 * 1024);
840 num
= multiply(num
, (u_quad_t
)1024 * 1024);
849 case '*': /* Backward compatible. */
851 num2
= strtosize(expr
+1, &expr2
, radix
);
857 if (expr2
== expr
+ 1) {
863 num
= multiply(num
, num2
);
879 strtolimit(const char *str
, char **endptr
, int radix
)
882 _DIAGASSERT(str
!= NULL
);
883 /* endptr may be NULL */
885 if (isinfinite(str
)) {
887 *endptr
= (char *)__UNCONST(str
) + strlen(str
);
888 return ((u_quad_t
)RLIM_INFINITY
);
890 return (strtosize(str
, endptr
, radix
));
894 isinfinite(const char *s
)
896 static const char *infs
[] = {
905 _DIAGASSERT(s
!= NULL
);
907 for (i
= infs
; *i
; i
++) {
908 if (!strcasecmp(s
, *i
))
915 multiply(u_quad_t n1
, u_quad_t n2
)
923 * Get rid of the simple cases
925 if (n1
== 0 || n2
== 0)
933 * sizeof() returns number of bytes needed for storage.
934 * This may be different from the actual number of useful bits.
937 bpw
= sizeof(u_quad_t
) * 8;
938 while (((u_quad_t
)1 << (bpw
-1)) == 0)
943 * First check the magnitude of each number. If the sum of the
944 * magnatude is way to high, reject the number. (If this test
945 * is not done then the first multiply below may overflow.)
947 for (b1
= bpw
; (((u_quad_t
)1 << (b1
-1)) & n1
) == 0; --b1
)
949 for (b2
= bpw
; (((u_quad_t
)1 << (b2
-1)) & n2
) == 0; --b2
)
951 if (b1
+ b2
- 2 > bpw
) {
957 * Decompose the multiplication to be:
962 * (h1 + l1) * (h2 + l2)
963 * (h1 * h2) + (h1 * l2) + (l1 * h2) + (l1 * l2)
965 * Since h1 && h2 do not have the low bit set, we can then say:
967 * (h1>>1 * h2>>1 * 4) + ...
969 * So if (h1>>1 * h2>>1) > (1<<(bpw - 2)) then the result will
972 * Finally, if MAX - ((h1 * l2) + (l1 * h2) + (l1 * l2)) < (h1*h2)
973 * then adding in residual amout will cause an overflow.
976 m
= (n1
>> 1) * (n2
>> 1);
978 if (m
>= ((u_quad_t
)1 << (bpw
-2))) {
986 + (n2
& 1) * (n1
& ~(u_quad_t
)1)
987 + (n1
& 1) * (n2
& ~(u_quad_t
)1);
989 if ((u_quad_t
)(m
+ r
) < m
) {
998 __weak_alias(login_getpwclass
, __login_getpwclass50
)
999 __weak_alias(setusercontext
, __setusercontext50
)