4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2015 Gary Mills
24 * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
27 #pragma weak _getprivimplinfo = getprivimplinfo
28 #pragma weak _priv_addset = priv_addset
29 #pragma weak _priv_allocset = priv_allocset
30 #pragma weak _priv_copyset = priv_copyset
31 #pragma weak _priv_delset = priv_delset
32 #pragma weak _priv_emptyset = priv_emptyset
33 #pragma weak _priv_basicset = priv_basicset
34 #pragma weak _priv_fillset = priv_fillset
35 #pragma weak _priv_freeset = priv_freeset
36 #pragma weak _priv_getbyname = priv_getbyname
37 #pragma weak _priv_getbynum = priv_getbynum
38 #pragma weak _priv_getsetbyname = priv_getsetbyname
39 #pragma weak _priv_getsetbynum = priv_getsetbynum
40 #pragma weak _priv_ineffect = priv_ineffect
41 #pragma weak _priv_intersect = priv_intersect
42 #pragma weak _priv_inverse = priv_inverse
43 #pragma weak _priv_isemptyset = priv_isemptyset
44 #pragma weak _priv_isequalset = priv_isequalset
45 #pragma weak _priv_isfullset = priv_isfullset
46 #pragma weak _priv_ismember = priv_ismember
47 #pragma weak _priv_issubset = priv_issubset
48 #pragma weak _priv_set = priv_set
49 #pragma weak _priv_union = priv_union
53 #define _STRUCTURED_PROC 1
55 #include "priv_private.h"
66 #include <sys/ucred.h>
67 #include <sys/procfs.h>
68 #include <sys/param.h>
69 #include <sys/corectl.h>
70 #include <priv_utils.h>
73 /* Include each string only once - until the compiler/linker are fixed */
74 static const char *permitted
= PRIV_PERMITTED
;
75 static const char *effective
= PRIV_EFFECTIVE
;
76 static const char *limit
= PRIV_LIMIT
;
77 static const char *inheritable
= PRIV_INHERITABLE
;
79 * Data independent privilege set operations.
81 * Only a few functions are provided that do not default to
82 * the system implementation of privileges. A limited set of
83 * interfaces is provided that accepts a priv_data_t *
84 * argument; this set of interfaces is a private interface between libc
85 * and libproc. It is delivered in order to interpret privilege sets
86 * in debuggers in a implementation independent way. As such, we
87 * don't need to provide the bulk of the interfaces, only a few
88 * boolean tests (isfull, isempty) the name<->num mappings and
89 * set pretty print functions. The boolean tests are only needed for
90 * the latter, so those aren't provided externally.
92 * Additionally, we provide the function that maps the kernel implementation
93 * structure into a libc private data structure.
96 priv_data_t
*privdata
;
98 static mutex_t pd_lock
= DEFAULTMUTEX
;
101 parseninfo(priv_info_names_t
*na
, char ***buf
, int *cp
)
106 *buf
= libc_malloc(sizeof (char *) * na
->cnt
);
113 for (i
= 0; i
< na
->cnt
; i
++) {
129 strintcmp(const void *a
, const void *b
)
131 const struct strint
*ap
= a
;
132 const struct strint
*bp
= b
;
134 return (strcasecmp(ap
->name
, bp
->name
));
138 __priv_parse_info(priv_impl_info_t
*ip
)
142 size_t size
= PRIV_IMPL_INFO_SIZE(ip
);
145 tmp
= libc_malloc(sizeof (*tmp
));
150 (void) memset(tmp
, 0, sizeof (*tmp
));
153 tmp
->pd_setsize
= sizeof (priv_chunk_t
) * ip
->priv_setsize
;
154 tmp
->pd_ucredsize
= UCRED_SIZE(ip
);
157 x
+= ip
->priv_headersize
;
159 while (x
< ((char *)ip
) + size
) {
160 /* LINTED: alignment */
161 priv_info_names_t
*na
= (priv_info_names_t
*)x
;
162 /* LINTED: alignment */
163 priv_info_set_t
*st
= (priv_info_set_t
*)x
;
164 struct strint
*tmparr
;
166 switch (na
->info
.priv_info_type
) {
167 case PRIV_INFO_SETNAMES
:
168 if (parseninfo(na
, &tmp
->pd_setnames
, &tmp
->pd_nsets
))
171 case PRIV_INFO_PRIVNAMES
:
172 if (parseninfo(na
, &tmp
->pd_privnames
, &tmp
->pd_nprivs
))
175 * We compute a sorted index which allows us
176 * to present a sorted list of privileges
177 * without actually having to sort it each time.
179 tmp
->pd_setsort
= libc_malloc(tmp
->pd_nprivs
*
181 if (tmp
->pd_setsort
== NULL
)
184 tmparr
= libc_malloc(tmp
->pd_nprivs
*
185 sizeof (struct strint
));
190 for (i
= 0; i
< tmp
->pd_nprivs
; i
++) {
192 tmparr
[i
].name
= tmp
->pd_privnames
[i
];
194 qsort(tmparr
, tmp
->pd_nprivs
, sizeof (struct strint
),
196 for (i
= 0; i
< tmp
->pd_nprivs
; i
++)
197 tmp
->pd_setsort
[i
] = tmparr
[i
].rank
;
200 case PRIV_INFO_BASICPRIVS
:
201 tmp
->pd_basicset
= (priv_set_t
*)&st
->set
[0];
204 /* unknown, ignore */
207 x
+= na
->info
.priv_info_size
;
211 libc_free(tmp
->pd_setnames
);
212 libc_free(tmp
->pd_privnames
);
213 libc_free(tmp
->pd_setsort
);
219 * Caller must have allocated d->pd_pinfo and should free it,
223 __priv_free_info(priv_data_t
*d
)
225 libc_free(d
->pd_setnames
);
226 libc_free(d
->pd_privnames
);
227 libc_free(d
->pd_setsort
);
232 * Return with the pd_lock held and data loaded or indicate failure.
237 if (__priv_getdata() == NULL
)
240 lmutex_lock(&pd_lock
);
247 priv_impl_info_t
*ip
, ii
;
253 if (getprivinfo(&ii
, sizeof (ii
)) != 0 ||
254 ii
.priv_max
== privdata
->pd_nprivs
)
257 ip
= alloca(PRIV_IMPL_INFO_SIZE(&ii
));
259 (void) getprivinfo(ip
, PRIV_IMPL_INFO_SIZE(&ii
));
261 /* Parse the info; then copy the additional bits */
262 tmp
= __priv_parse_info(ip
);
266 oldn
= privdata
->pd_nprivs
;
267 p0
= privdata
->pd_privnames
[0];
269 newn
= tmp
->pd_nprivs
;
270 q0
= tmp
->pd_privnames
[0];
272 /* copy the extra information to the old datastructure */
273 (void) memcpy((char *)privdata
->pd_pinfo
+ sizeof (priv_impl_info_t
),
274 (char *)ip
+ sizeof (priv_impl_info_t
),
275 PRIV_IMPL_INFO_SIZE(ip
) - sizeof (priv_impl_info_t
));
277 /* Copy the first oldn pointers */
278 (void) memcpy(tmp
->pd_privnames
, privdata
->pd_privnames
,
279 oldn
* sizeof (char *));
281 /* Adjust the rest */
282 for (i
= oldn
; i
< newn
; i
++)
283 tmp
->pd_privnames
[i
] += p0
- q0
;
285 /* Install the larger arrays */
286 libc_free(privdata
->pd_privnames
);
287 privdata
->pd_privnames
= tmp
->pd_privnames
;
288 tmp
->pd_privnames
= NULL
;
290 libc_free(privdata
->pd_setsort
);
291 privdata
->pd_setsort
= tmp
->pd_setsort
;
292 tmp
->pd_setsort
= NULL
;
294 /* Copy the rest of the data */
295 *privdata
->pd_pinfo
= *ip
;
297 privdata
->pd_nprivs
= newn
;
299 __priv_free_info(tmp
);
306 lmutex_unlock(&pd_lock
);
309 static priv_set_t
*__priv_allocset(priv_data_t
*);
314 if (privdata
== NULL
) {
315 lmutex_lock(&pd_lock
);
316 if (privdata
== NULL
) {
318 priv_impl_info_t
*ip
;
319 size_t size
= sizeof (priv_impl_info_t
) + 2048;
321 priv_impl_info_t
*aip
= alloca(size
);
323 if (getprivinfo(aip
, size
) != 0)
326 realsize
= PRIV_IMPL_INFO_SIZE(aip
);
328 ip
= libc_malloc(realsize
);
333 if (realsize
<= size
) {
334 (void) memcpy(ip
, aip
, realsize
);
335 } else if (getprivinfo(ip
, realsize
) != 0) {
340 if ((tmp
= __priv_parse_info(ip
)) == NULL
) {
345 /* Allocate the zoneset just once, here */
346 tmp
->pd_zoneset
= __priv_allocset(tmp
);
347 if (tmp
->pd_zoneset
== NULL
)
350 if (zone_getattr(getzoneid(), ZONE_ATTR_PRIVSET
,
351 tmp
->pd_zoneset
, tmp
->pd_setsize
)
352 == tmp
->pd_setsize
) {
358 priv_freeset(tmp
->pd_zoneset
);
360 __priv_free_info(tmp
);
364 lmutex_unlock(&pd_lock
);
370 const priv_impl_info_t
*
371 getprivimplinfo(void)
377 return (d
->pd_pinfo
);
381 priv_vlist(va_list ap
)
383 priv_set_t
*pset
= priv_allocset();
391 while ((priv
= va_arg(ap
, const char *)) != NULL
) {
392 if (priv_addset(pset
, priv
) < 0) {
401 * priv_set(op, set, priv_id1, priv_id2, ..., NULL)
403 * Library routine to enable a user process to set a specific
404 * privilege set appropriately using a single call. User is
405 * required to terminate the list of privileges with NULL.
408 priv_set(priv_op_t op
, priv_ptype_t setname
, ...)
414 va_start(ap
, setname
);
416 pset
= priv_vlist(ap
);
424 if (setname
== NULL
) {
430 for (set
= 0; set
< d
->pd_nsets
; set
++)
431 if ((ret
= syscall(SYS_privsys
, PRIVSYS_SETPPRIV
, op
,
432 set
, (void *)pset
, d
->pd_setsize
)) != 0)
435 ret
= setppriv(op
, setname
, pset
);
443 * priv_ineffect(privilege).
444 * tests the existence of a privilege against the effective set.
447 priv_ineffect(const char *priv
)
452 curset
= priv_allocset();
457 if (getppriv(effective
, curset
) != 0 ||
458 !priv_ismember(curset
, priv
))
463 priv_freeset(curset
);
469 * The routine __init_daemon_priv() is private to Solaris and is
470 * used by daemons to limit the privileges they can use and
471 * to set the uid they run under.
474 static const char root_cp
[] = "/core.%f.%t";
475 static const char daemon_cp
[] = "/var/tmp/core.%f.%t";
478 __init_daemon_priv(int flags
, uid_t uid
, gid_t gid
, ...)
481 priv_set_t
*perm
= NULL
;
491 nset
= priv_vlist(pa
);
498 /* Always add the basic set */
499 if (d
->pd_basicset
!= NULL
)
500 priv_union(d
->pd_basicset
, nset
);
503 * This is not a significant failure: it allows us to start programs
504 * with sufficient privileges and with the proper uid. We don't
505 * care enough about the extra groups in that case.
507 if (flags
& PU_RESETGROUPS
)
508 (void) setgroups(0, NULL
);
510 if (gid
!= (gid_t
)-1 && setgid(gid
) != 0)
513 perm
= priv_allocset();
518 (void) getppriv(permitted
, perm
);
519 (void) setppriv(PRIV_SET
, effective
, perm
);
521 /* Now reset suid and euid */
522 if (uid
!= (uid_t
)-1 && setreuid(uid
, uid
) != 0)
525 /* Check for the limit privs */
526 if ((flags
& PU_LIMITPRIVS
) &&
527 setppriv(PRIV_SET
, limit
, nset
) != 0)
530 if (flags
& PU_CLEARLIMITSET
) {
532 if (setppriv(PRIV_SET
, limit
, perm
) != 0)
536 /* Remove the privileges from all the other sets */
537 if (setppriv(PRIV_SET
, permitted
, nset
) != 0)
540 if (!(flags
& PU_INHERITPRIVS
))
543 ret
= setppriv(PRIV_SET
, inheritable
, nset
);
548 if (core_get_process_path(buf
, sizeof (buf
), getpid()) == 0 &&
549 strcmp(buf
, "core") == 0) {
551 if ((uid
== (uid_t
)-1 ? geteuid() : uid
) == 0) {
552 (void) core_set_process_path(root_cp
, sizeof (root_cp
),
555 (void) core_set_process_path(daemon_cp
,
556 sizeof (daemon_cp
), getpid());
559 (void) setpflags(__PROC_PROTECT
, 0);
565 * The routine __fini_daemon_priv() is private to Solaris and is
566 * used by daemons to clear remaining unwanted privileges and
567 * reenable core dumps.
570 __fini_daemon_priv(const char *priv
, ...)
578 nset
= priv_vlist(pa
);
584 (void) priv_addset(nset
, priv
);
585 (void) setppriv(PRIV_OFF
, permitted
, nset
);
589 (void) setpflags(__PROC_PROTECT
, 0);
593 * The routine __init_suid_priv() is private to Solaris and is
594 * used by set-uid root programs to limit the privileges acquired
595 * to those actually needed.
598 static priv_set_t
*bracketpriv
;
601 __init_suid_priv(int flags
, ...)
603 priv_set_t
*nset
= NULL
;
604 priv_set_t
*tmpset
= NULL
;
611 /* If we're not set-uid root, don't reset the uid */
614 /* If we're running as root, keep everything */
619 /* Can call this only once */
620 if (bracketpriv
!= NULL
)
625 nset
= priv_vlist(pa
);
632 tmpset
= priv_allocset();
637 /* We cannot grow our privileges beyond P, so start there */
638 (void) getppriv(permitted
, tmpset
);
640 /* Is the privilege we need even in P? */
641 if (!priv_issubset(nset
, tmpset
))
644 bracketpriv
= priv_allocset();
645 if (bracketpriv
== NULL
)
648 priv_copyset(nset
, bracketpriv
);
650 /* Always add the basic set */
651 priv_union(priv_basic(), nset
);
653 /* But don't add what we don't have */
654 priv_intersect(tmpset
, nset
);
656 (void) getppriv(inheritable
, tmpset
);
658 /* And stir in the inheritable privileges */
659 priv_union(tmpset
, nset
);
661 if ((r
= setppriv(PRIV_SET
, effective
, tmpset
)) != 0)
664 if ((r
= setppriv(PRIV_SET
, permitted
, nset
)) != 0)
667 if (flags
& PU_CLEARLIMITSET
)
670 if ((flags
& (PU_LIMITPRIVS
|PU_CLEARLIMITSET
)) != 0 &&
671 (r
= setppriv(PRIV_SET
, limit
, nset
)) != 0)
675 r
= setreuid(ruid
, ruid
);
678 priv_freeset(tmpset
);
681 /* Fail without leaving uid 0 around */
683 (void) setreuid(ruid
, ruid
);
684 priv_freeset(bracketpriv
);
692 * Toggle privileges on/off in the effective set.
695 __priv_bracket(priv_op_t op
)
697 /* We're running fully privileged or didn't check errors first time */
698 if (bracketpriv
== NULL
)
701 /* Only PRIV_ON and PRIV_OFF are valid */
705 return (setppriv(op
, effective
, bracketpriv
));
709 * Remove privileges from E & P.
712 __priv_relinquish(void)
714 if (bracketpriv
!= NULL
) {
715 (void) setppriv(PRIV_OFF
, permitted
, bracketpriv
);
716 priv_freeset(bracketpriv
);
722 * Use binary search on the ordered list.
725 __priv_getbyname(const priv_data_t
*d
, const char *name
)
735 list
= d
->pd_privnames
;
736 order
= d
->pd_setsort
;
737 hi
= d
->pd_nprivs
- 1;
739 if (strncasecmp(name
, "priv_", 5) == 0)
743 int mid
= (lo
+ hi
) / 2;
744 int res
= strcasecmp(name
, list
[order
[mid
]]);
759 priv_getbyname(const char *name
)
761 WITHPRIVLOCKED(int, -1, __priv_getbyname(GETPRIVDATA(), name
))
765 __priv_getsetbyname(const priv_data_t
*d
, const char *name
)
769 char *const *list
= d
->pd_setnames
;
771 if (strncasecmp(name
, "priv_", 5) == 0)
774 for (i
= 0; i
< n
; i
++) {
775 if (strcasecmp(list
[i
], name
) == 0)
784 priv_getsetbyname(const char *name
)
786 /* Not locked: sets don't change */
787 return (__priv_getsetbyname(GETPRIVDATA(), name
));
791 priv_bynum(int i
, int n
, char **list
)
800 __priv_getbynum(const priv_data_t
*d
, int num
)
804 return (priv_bynum(num
, d
->pd_nprivs
, d
->pd_privnames
));
808 priv_getbynum(int num
)
810 WITHPRIVLOCKED(const char *, NULL
, __priv_getbynum(GETPRIVDATA(), num
))
814 __priv_getsetbynum(const priv_data_t
*d
, int num
)
818 return (priv_bynum(num
, d
->pd_nsets
, d
->pd_setnames
));
822 priv_getsetbynum(int num
)
824 return (__priv_getsetbynum(GETPRIVDATA(), num
));
829 * Privilege manipulation functions
831 * Without knowing the details of the privilege set implementation,
832 * opaque pointers can be used to manipulate sets at will.
836 __priv_allocset(priv_data_t
*d
)
841 return (libc_malloc(d
->pd_setsize
));
847 return (__priv_allocset(GETPRIVDATA()));
851 priv_freeset(priv_set_t
*p
)
860 __priv_emptyset(priv_data_t
*d
, priv_set_t
*set
)
862 (void) memset(set
, 0, d
->pd_setsize
);
866 priv_emptyset(priv_set_t
*set
)
868 __priv_emptyset(GETPRIVDATA(), set
);
872 priv_basicset(priv_set_t
*set
)
874 priv_copyset(priv_basic(), set
);
878 __priv_fillset(priv_data_t
*d
, priv_set_t
*set
)
880 (void) memset(set
, ~0, d
->pd_setsize
);
884 priv_fillset(priv_set_t
*set
)
886 __priv_fillset(GETPRIVDATA(), set
);
890 #define PRIV_TEST_BODY_D(d, test) \
893 for (i = d->pd_pinfo->priv_setsize; i-- > 0; ) \
900 priv_isequalset(const priv_set_t
*a
, const priv_set_t
*b
)
906 return ((boolean_t
)(memcmp(a
, b
, d
->pd_setsize
) == 0));
910 __priv_isemptyset(priv_data_t
*d
, const priv_set_t
*set
)
912 PRIV_TEST_BODY_D(d
, ((priv_chunk_t
*)set
)[i
] == 0);
916 priv_isemptyset(const priv_set_t
*set
)
918 return (__priv_isemptyset(GETPRIVDATA(), set
));
922 __priv_isfullset(priv_data_t
*d
, const priv_set_t
*set
)
924 PRIV_TEST_BODY_D(d
, ((priv_chunk_t
*)set
)[i
] == ~(priv_chunk_t
)0);
928 priv_isfullset(const priv_set_t
*set
)
930 return (__priv_isfullset(GETPRIVDATA(), set
));
934 * Return true if a is a subset of b
937 __priv_issubset(priv_data_t
*d
, const priv_set_t
*a
, const priv_set_t
*b
)
939 PRIV_TEST_BODY_D(d
, (((priv_chunk_t
*)a
)[i
] | ((priv_chunk_t
*)b
)[i
]) ==
940 ((priv_chunk_t
*)b
)[i
]);
944 priv_issubset(const priv_set_t
*a
, const priv_set_t
*b
)
946 return (__priv_issubset(GETPRIVDATA(), a
, b
));
949 #define PRIV_CHANGE_BODY(a, op, b) \
955 for (i = 0; i < d->pd_pinfo->priv_setsize; i++) \
956 ((priv_chunk_t *)a)[i] op \
957 ((priv_chunk_t *)b)[i]
961 priv_intersect(const priv_set_t
*a
, priv_set_t
*b
)
964 PRIV_CHANGE_BODY(b
, &=, a
);
969 priv_copyset(const priv_set_t
*a
, priv_set_t
*b
)
972 PRIV_CHANGE_BODY(b
, =, a
);
977 priv_union(const priv_set_t
*a
, priv_set_t
*b
)
980 PRIV_CHANGE_BODY(b
, |=, a
);
985 priv_inverse(priv_set_t
*a
)
987 PRIV_CHANGE_BODY(a
, = ~, a
);
991 * Manipulating single privileges.
995 priv_addset(priv_set_t
*a
, const char *p
)
997 int priv
= priv_getbyname(p
);
1002 PRIV_ADDSET(a
, priv
);
1008 priv_delset(priv_set_t
*a
, const char *p
)
1010 int priv
= priv_getbyname(p
);
1015 PRIV_DELSET(a
, priv
);
1020 priv_ismember(const priv_set_t
*a
, const char *p
)
1022 int priv
= priv_getbyname(p
);
1027 return ((boolean_t
)PRIV_ISMEMBER(a
, priv
));