1 /* $NetBSD: login_cap.c,v 1.32 2015/07/11 09:21:22 kamil 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.32 2015/07/11 09:21:22 kamil 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_NTHR
, R_CNUMB
, "maxthread", },
424 { RLIMIT_NOFILE
, R_CNUMB
, "openfiles", },
425 { RLIMIT_CORE
, R_CSIZE
, "coredumpsize", },
426 { RLIMIT_SBSIZE
, R_CSIZE
, "sbsize", },
427 { RLIMIT_AS
, R_CSIZE
, "vmemoryuse", },
432 gsetrl(login_cap_t
*lc
, int what
, const char *name
, int type
)
439 _DIAGASSERT(name
!= NULL
);
441 (void)snprintf(name_cur
, sizeof(name_cur
), "%s-cur", name
);
442 (void)snprintf(name_max
, sizeof(name_max
), "%s-max", name
);
444 if (getrlimit(what
, &r
)) {
445 syslog(LOG_ERR
, "getting resource limit: %m");
449 #define RCUR ((quad_t)r.rlim_cur)
450 #define RMAX ((quad_t)r.rlim_max)
454 r
.rlim_cur
= login_getcaptime(lc
, name
, RCUR
, RCUR
);
455 r
.rlim_max
= login_getcaptime(lc
, name
, RMAX
, RMAX
);
456 rl
.rlim_cur
= login_getcaptime(lc
, name_cur
, RCUR
, RCUR
);
457 rl
.rlim_max
= login_getcaptime(lc
, name_max
, RMAX
, RMAX
);
460 r
.rlim_cur
= login_getcapsize(lc
, name
, RCUR
, RCUR
);
461 r
.rlim_max
= login_getcapsize(lc
, name
, RMAX
, RMAX
);
462 rl
.rlim_cur
= login_getcapsize(lc
, name_cur
, RCUR
, RCUR
);
463 rl
.rlim_max
= login_getcapsize(lc
, name_max
, RMAX
, RMAX
);
466 r
.rlim_cur
= login_getcapnum(lc
, name
, RCUR
, RCUR
);
467 r
.rlim_max
= login_getcapnum(lc
, name
, RMAX
, RMAX
);
468 rl
.rlim_cur
= login_getcapnum(lc
, name_cur
, RCUR
, RCUR
);
469 rl
.rlim_max
= login_getcapnum(lc
, name_max
, RMAX
, RMAX
);
472 syslog(LOG_ERR
, "%s: invalid type %d setting resource limit %s",
473 lc
->lc_class
, type
, name
);
477 if (setrlimit(what
, &rl
)) {
478 syslog(LOG_ERR
, "%s: setting resource limit %s: %m",
489 envset(void *envp __unused
, const char *name
, const char *value
, int overwrite
)
491 return setenv(name
, value
, overwrite
);
495 setuserenv(login_cap_t
*lc
, envfunc_t senv
, void *envp
)
497 const char *stop
= ", \t";
501 char *str
= login_getcapstr(lc
, "setenv", NULL
, NULL
);
503 if (str
== NULL
|| *str
== '\0')
507 * count the sub-strings, this may over-count since we don't
508 * account for escaped delimiters.
510 for (i
= 1, ptr
= str
; *ptr
; i
++) {
511 ptr
+= strcspn(ptr
, stop
);
516 /* allocate ptr array and string */
518 res
= malloc(count
* sizeof(*res
) + strlen(str
) + 1);
523 ptr
= (char *)(void *)&res
[count
];
524 (void)strcpy(ptr
, str
);
527 for (i
= 0; (res
[i
] = stresep(&ptr
, stop
, '\\')) != NULL
; )
533 for (i
= 0; i
< count
; i
++) {
534 if ((ptr
= strchr(res
[i
], '=')) != NULL
)
538 (void)(*senv
)(envp
, res
[i
], ptr
? ptr
: "", 1);
546 setclasscontext(const char *class, u_int flags
)
551 flags
&= LOGIN_SETRESOURCES
| LOGIN_SETPRIORITY
| LOGIN_SETUMASK
|
554 lc
= login_getclass(class);
555 ret
= lc
? setusercontext(lc
, NULL
, 0, flags
) : -1;
561 setusercontext(login_cap_t
*lc
, struct passwd
*pwd
, uid_t uid
, u_int flags
)
563 char per_user_tmp
[MAXPATHLEN
+ 1];
564 const char *component_name
;
573 flc
= lc
= login_getclass(pwd
? pwd
->pw_class
: NULL
);
576 * Without the pwd entry being passed we cannot set either
577 * the group or the login. We could complain about it.
580 flags
&= ~(LOGIN_SETGROUP
|LOGIN_SETLOGIN
);
582 #ifdef LOGIN_OSETGROUP
584 flags
&= ~LOGIN_OSETGROUP
;
585 if (flags
& LOGIN_OSETGROUP
)
586 flags
= (flags
& ~LOGIN_OSETGROUP
) | LOGIN_SETGROUP
;
588 if (flags
& LOGIN_SETRESOURCES
)
589 for (i
= 0; r_list
[i
].name
; ++i
)
590 (void)gsetrl(lc
, r_list
[i
].what
, r_list
[i
].name
,
593 if (flags
& LOGIN_SETPRIORITY
) {
594 p
= login_getcapnum(lc
, "priority", (quad_t
)0, (quad_t
)0);
596 if (setpriority(PRIO_PROCESS
, 0, (int)p
) == -1)
597 syslog(LOG_ERR
, "%s: setpriority: %m", lc
->lc_class
);
600 if (flags
& LOGIN_SETUMASK
) {
601 p
= login_getcapnum(lc
, "umask", (quad_t
) LOGIN_DEFUMASK
,
602 (quad_t
)LOGIN_DEFUMASK
);
606 if (flags
& LOGIN_SETGID
) {
607 if (setgid(pwd
->pw_gid
) == -1) {
608 syslog(LOG_ERR
, "setgid(%d): %m", pwd
->pw_gid
);
614 if (flags
& LOGIN_SETGROUPS
) {
615 if (initgroups(pwd
->pw_name
, pwd
->pw_gid
) == -1) {
616 syslog(LOG_ERR
, "initgroups(%s,%d): %m",
617 pwd
->pw_name
, pwd
->pw_gid
);
623 /* Create per-user temporary directories if needed. */
624 if ((len
= readlink("/tmp", per_user_tmp
,
625 sizeof(per_user_tmp
) - 6)) != -1) {
627 static const char atuid
[] = "/@ruid";
630 /* readlink does not nul-terminate the string */
631 per_user_tmp
[len
] = '\0';
633 /* Check if it's magic symlink. */
634 lp
= strstr(per_user_tmp
, atuid
);
635 if (lp
!= NULL
&& *(lp
+ (sizeof(atuid
) - 1)) == '\0') {
638 if (snprintf(lp
, 11, "/%u", pwd
->pw_uid
) > 10) {
639 syslog(LOG_ERR
, "real temporary path too long");
643 if (mkdir(per_user_tmp
, S_IRWXU
) != -1) {
644 if (chown(per_user_tmp
, pwd
->pw_uid
,
646 component_name
= "chown";
651 * Must set sticky bit for tmp directory, some
652 * programs rely on this.
654 if(chmod(per_user_tmp
, S_IRWXU
| S_ISVTX
)) {
655 component_name
= "chmod";
659 if (errno
!= EEXIST
) {
660 component_name
= "mkdir";
664 * We must ensure that we own the
665 * directory and that is has the correct
666 * permissions, otherwise a DOS attack
670 if (stat(per_user_tmp
, &sb
) == -1) {
671 component_name
= "stat";
675 if (sb
.st_uid
!= pwd
->pw_uid
) {
676 if (chown(per_user_tmp
,
677 pwd
->pw_uid
, pwd
->pw_gid
)) {
678 component_name
= "chown";
683 if (sb
.st_mode
!= (S_IRWXU
| S_ISVTX
)) {
684 if (chmod(per_user_tmp
,
685 S_IRWXU
| S_ISVTX
)) {
686 component_name
= "chmod";
696 #if !defined(__minix)
697 if (flags
& LOGIN_SETLOGIN
)
698 if (setlogin(pwd
->pw_name
) == -1) {
699 syslog(LOG_ERR
, "setlogin(%s) failure: %m",
704 #endif /* !defined(__minix) */
706 if (flags
& LOGIN_SETUSER
)
707 if (setuid(uid
) == -1) {
708 syslog(LOG_ERR
, "setuid(%d): %m", uid
);
713 if (flags
& LOGIN_SETENV
)
714 setuserenv(lc
, envset
, NULL
);
716 if (flags
& LOGIN_SETPATH
)
717 setuserpath(lc
, pwd
? pwd
->pw_dir
: "", envset
, NULL
);
723 if (component_name
!= NULL
) {
724 syslog(LOG_ERR
, "%s %s: %m", component_name
, per_user_tmp
);
728 syslog(LOG_ERR
, "%s: %m", per_user_tmp
);
735 setuserpath(login_cap_t
*lc
, const char *home
, envfunc_t senv
, void *envp
)
743 _DIAGASSERT(home
!= NULL
);
747 p
= path
= login_getcapstr(lc
, "path", NULL
, NULL
);
752 plen
= (p
- path
) + cnt
* (hlen
+ 1) + 1;
754 q
= path
= malloc(plen
);
757 p
+= strspn(p
, " \t");
760 plen
= strcspn(p
, " \t");
761 if (hlen
== 0 && *p
== '~') {
780 cpath
= _PATH_DEFPATH
;
782 cpath
= _PATH_DEFPATH
;
783 if ((*senv
)(envp
, "PATH", cpath
, 1))
784 warn("could not set PATH");
788 * Convert an expression of the following forms
790 * 2) A number followed by a b (mult by 512).
791 * 3) A number followed by a k (mult by 1024).
792 * 5) A number followed by a m (mult by 1024 * 1024).
793 * 6) A number followed by a g (mult by 1024 * 1024 * 1024).
794 * 7) A number followed by a t (mult by 1024 * 1024 * 1024 * 1024).
795 * 8) Two or more numbers (with/without k,b,m,g, or t).
796 * separated by x (also * for backwards compatibility), specifying
797 * the product of the indicated values.
800 strtosize(const char *str
, char **endptr
, int radix
)
805 _DIAGASSERT(str
!= NULL
);
806 /* endptr may be NULL */
809 num
= strtouq(str
, &expr
, radix
);
810 if (errno
|| expr
== str
) {
818 num
= multiply(num
, (u_quad_t
)512);
822 num
= multiply(num
, (u_quad_t
)1024);
826 num
= multiply(num
, (u_quad_t
)1024 * 1024);
830 num
= multiply(num
, (u_quad_t
)1024 * 1024 * 1024);
834 num
= multiply(num
, (u_quad_t
)1024 * 1024);
835 num
= multiply(num
, (u_quad_t
)1024 * 1024);
844 case '*': /* Backward compatible. */
846 num2
= strtosize(expr
+1, &expr2
, radix
);
852 if (expr2
== expr
+ 1) {
858 num
= multiply(num
, num2
);
874 strtolimit(const char *str
, char **endptr
, int radix
)
877 _DIAGASSERT(str
!= NULL
);
878 /* endptr may be NULL */
880 if (isinfinite(str
)) {
882 *endptr
= (char *)__UNCONST(str
) + strlen(str
);
883 return ((u_quad_t
)RLIM_INFINITY
);
885 return (strtosize(str
, endptr
, radix
));
889 isinfinite(const char *s
)
891 static const char *infs
[] = {
900 _DIAGASSERT(s
!= NULL
);
902 for (i
= infs
; *i
; i
++) {
903 if (!strcasecmp(s
, *i
))
910 multiply(u_quad_t n1
, u_quad_t n2
)
918 * Get rid of the simple cases
920 if (n1
== 0 || n2
== 0)
928 * sizeof() returns number of bytes needed for storage.
929 * This may be different from the actual number of useful bits.
932 bpw
= sizeof(u_quad_t
) * 8;
933 while (((u_quad_t
)1 << (bpw
-1)) == 0)
938 * First check the magnitude of each number. If the sum of the
939 * magnatude is way to high, reject the number. (If this test
940 * is not done then the first multiply below may overflow.)
942 for (b1
= bpw
; (((u_quad_t
)1 << (b1
-1)) & n1
) == 0; --b1
)
944 for (b2
= bpw
; (((u_quad_t
)1 << (b2
-1)) & n2
) == 0; --b2
)
946 if (b1
+ b2
- 2 > bpw
) {
952 * Decompose the multiplication to be:
957 * (h1 + l1) * (h2 + l2)
958 * (h1 * h2) + (h1 * l2) + (l1 * h2) + (l1 * l2)
960 * Since h1 && h2 do not have the low bit set, we can then say:
962 * (h1>>1 * h2>>1 * 4) + ...
964 * So if (h1>>1 * h2>>1) > (1<<(bpw - 2)) then the result will
967 * Finally, if MAX - ((h1 * l2) + (l1 * h2) + (l1 * l2)) < (h1*h2)
968 * then adding in residual amout will cause an overflow.
971 m
= (n1
>> 1) * (n2
>> 1);
973 if (m
>= ((u_quad_t
)1 << (bpw
-2))) {
981 + (n2
& 1) * (n1
& ~(u_quad_t
)1)
982 + (n1
& 1) * (n2
& ~(u_quad_t
)1);
984 if ((u_quad_t
)(m
+ r
) < m
) {