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]
21 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
22 * Copyright 2015, Joyent, Inc.
25 #define _POSIX_PTHREAD_SEMANTICS 1
27 #include <sys/param.h>
29 #include <sys/syscall.h>
30 #include <sys/systeminfo.h>
49 #include <auth_attr.h>
50 #include <exec_attr.h>
51 #include <prof_attr.h>
52 #include <user_attr.h>
54 static int doorfd
= -1;
56 static size_t repsz
, setsz
;
58 static uid_t
get_uid(const char *, boolean_t
*, char *);
59 static gid_t
get_gid(const char *, boolean_t
*, char *);
60 static priv_set_t
*get_privset(const char *, boolean_t
*, char *);
61 static priv_set_t
*get_granted_privs(uid_t
);
64 * Remove the isaexec path of an executable if we can't find the
65 * executable at the first attempt.
69 static boolean_t cansplice
= B_TRUE
;
75 size_t isalen
= 255; /* wild guess */
82 * Extract the isalist(5) for userland from the kernel.
84 isalist
= malloc(isalen
);
86 ret
= sysinfo(SI_ISALIST
, isalist
, isalen
);
93 isalist
= realloc(isalist
, isalen
);
96 } while (isalist
!= NULL
);
102 /* allocate room for the regex + (/())/[^/]*$ + needed \\. */
104 #define RIGHT "))/[^/]*$"
106 regexpr
= alloca(ret
* 2 + sizeof (LEFT RIGHT
));
107 (void) strcpy(regexpr
, LEFT
);
108 len
= strlen(regexpr
);
110 for (p
= isalist
; *p
; p
++) {
120 regexpr
[len
++] = '\\';
127 regexpr
[len
++] = '|';
134 (void) strcat(regexpr
, RIGHT
);
136 if (regcomp(®c
, regexpr
, REG_EXTENDED
) != 0)
145 removeisapath(char *path
)
147 regmatch_t match
[NMATCH
];
149 if (!cansplice
|| regexec(®c
, path
, NMATCH
, match
, 0) != 0)
153 * The first match includes the whole matched expression including the
154 * end of the string. The second match includes the "/" + "isa" and
155 * that is the part we need to remove.
158 if (match
[1].rm_so
== -1)
161 /* match[0].rm_eo == strlen(path) */
162 (void) memmove(path
+ match
[1].rm_so
, path
+ match
[1].rm_eo
,
163 match
[0].rm_eo
- match
[1].rm_eo
+ 1);
169 register_pfexec(int fd
)
171 int ret
= syscall(SYS_privsys
, PRIVSYS_PFEXEC_REG
, fd
);
178 unregister_pfexec(int sig
)
181 (void) syscall(SYS_privsys
, PRIVSYS_PFEXEC_UNREG
, doorfd
);
186 alldigits(const char *s
)
193 while ((c
= *s
++) != '\0') {
203 get_uid(const char *v
, boolean_t
*ok
, char *path
)
205 struct passwd
*pwd
, pwdm
;
208 if (getpwnam_r(v
, &pwdm
, buf
, sizeof (buf
), &pwd
) == 0 && pwd
!= NULL
)
209 return (pwd
->pw_uid
);
215 syslog(LOG_ERR
, "%s: %s: unknown username\n", path
, v
);
220 get_gid(const char *v
, boolean_t
*ok
, char *path
)
222 struct group
*grp
, grpm
;
225 if (getgrnam_r(v
, &grpm
, buf
, sizeof (buf
), &grp
) == 0 && grp
!= NULL
)
226 return (grp
->gr_gid
);
232 syslog(LOG_ERR
, "%s: %s: unknown groupname\n", path
, v
);
237 get_privset(const char *s
, boolean_t
*ok
, char *path
)
241 if ((res
= priv_str_to_set(s
, ",", NULL
)) == NULL
) {
242 syslog(LOG_ERR
, "%s: %s: bad privilege set\n", path
, s
);
251 ggp_callback(const char *prof
, kva_t
*attr
, void *ctxt
, void *vres
)
253 priv_set_t
*res
= vres
;
259 /* get privs from this profile */
260 privs
= kva_match(attr
, PROFATTR_PRIVS_KW
);
262 priv_set_t
*tmp
= priv_str_to_set(privs
, ",", NULL
);
264 priv_union(tmp
, res
);
273 * This routine exists on failure and returns NULL if no granted privileges
277 get_granted_privs(uid_t uid
)
280 struct passwd
*pwd
, pwdm
;
283 if (getpwuid_r(uid
, &pwdm
, buf
, sizeof (buf
), &pwd
) != 0 || pwd
== NULL
)
286 res
= priv_allocset();
292 (void) _enum_profs(pwd
->pw_name
, ggp_callback
, NULL
, res
);
298 callback_forced_privs(pfexec_arg_t
*pap
)
303 void *res
= alloca(setsz
);
305 /* Empty set signifies no forced privileges. */
308 exec
= getexecprof("Forced Privilege", KV_COMMAND
, pap
->pfa_path
,
311 if (exec
== NULL
&& removeisapath(pap
->pfa_path
)) {
312 exec
= getexecprof("Forced Privilege", KV_COMMAND
,
313 pap
->pfa_path
, GET_ONE
);
317 (void) door_return(res
, setsz
, NULL
, 0);
321 if ((value
= kva_match(exec
->attr
, EXECATTR_IPRIV_KW
)) == NULL
||
322 (fset
= get_privset(value
, NULL
, pap
->pfa_path
)) == NULL
) {
324 (void) door_return(res
, setsz
, NULL
, 0);
328 priv_copyset(fset
, res
);
332 (void) door_return(res
, setsz
, NULL
, 0);
336 callback_user_privs(pfexec_arg_t
*pap
)
338 priv_set_t
*gset
, *wset
;
341 wset
= (priv_set_t
*)&pap
->pfa_buf
;
342 gset
= get_granted_privs(pap
->pfa_uid
);
344 res
= priv_issubset(wset
, gset
);
347 (void) door_return((char *)&res
, sizeof (res
), NULL
, 0);
351 callback_pfexec(pfexec_arg_t
*pap
)
353 pfexec_reply_t
*res
= alloca(repsz
);
354 uid_t uid
, euid
, uuid
;
356 struct passwd pw
, *pwd
;
358 execattr_t
*exec
= NULL
;
360 priv_set_t
*lset
, *iset
;
361 size_t mysz
= repsz
- 2 * setsz
;
362 char *path
= pap
->pfa_path
;
365 * Initialize the pfexec_reply_t to a sane state.
367 res
->pfr_vers
= pap
->pfa_vers
;
369 res
->pfr_ruid
= PFEXEC_NOTSET
;
370 res
->pfr_euid
= PFEXEC_NOTSET
;
371 res
->pfr_rgid
= PFEXEC_NOTSET
;
372 res
->pfr_egid
= PFEXEC_NOTSET
;
373 res
->pfr_setcred
= B_FALSE
;
374 res
->pfr_scrubenv
= B_TRUE
;
375 res
->pfr_allowed
= B_FALSE
;
381 if (getpwuid_r(uuid
, &pw
, buf
, sizeof (buf
), &pwd
) != 0 || pwd
== NULL
)
384 exec
= getexecuser(pwd
->pw_name
, KV_COMMAND
, path
, GET_ONE
);
386 if ((exec
== NULL
|| exec
->attr
== NULL
) && removeisapath(path
)) {
388 exec
= getexecuser(pwd
->pw_name
, KV_COMMAND
, path
, GET_ONE
);
392 res
->pfr_allowed
= B_FALSE
;
396 if (exec
->attr
== NULL
)
399 /* Found in execattr, so clearly we can use it */
400 res
->pfr_allowed
= B_TRUE
;
402 uid
= euid
= (uid_t
)-1;
403 gid
= egid
= (gid_t
)-1;
407 * If there's an error in parsing uid, gid, privs, then return
410 if ((value
= kva_match(exec
->attr
, EXECATTR_UID_KW
)) != NULL
)
411 euid
= uid
= get_uid(value
, &res
->pfr_allowed
, path
);
413 if ((value
= kva_match(exec
->attr
, EXECATTR_GID_KW
)) != NULL
)
414 egid
= gid
= get_gid(value
, &res
->pfr_allowed
, path
);
416 if ((value
= kva_match(exec
->attr
, EXECATTR_EUID_KW
)) != NULL
)
417 euid
= get_uid(value
, &res
->pfr_allowed
, path
);
419 if ((value
= kva_match(exec
->attr
, EXECATTR_EGID_KW
)) != NULL
)
420 egid
= get_gid(value
, &res
->pfr_allowed
, path
);
422 if ((value
= kva_match(exec
->attr
, EXECATTR_LPRIV_KW
)) != NULL
)
423 lset
= get_privset(value
, &res
->pfr_allowed
, path
);
425 if ((value
= kva_match(exec
->attr
, EXECATTR_IPRIV_KW
)) != NULL
)
426 iset
= get_privset(value
, &res
->pfr_allowed
, path
);
429 * Remove LD_* variables in the kernel when the runtime linker might
430 * use them later on because the uids are equal.
432 res
->pfr_scrubenv
= (uid
!= (uid_t
)-1 && euid
== uid
) ||
433 (gid
!= (gid_t
)-1 && egid
== gid
) || iset
!= NULL
;
435 res
->pfr_euid
= euid
;
437 res
->pfr_egid
= egid
;
440 /* Now add the privilege sets */
441 res
->pfr_ioff
= res
->pfr_loff
= 0;
443 res
->pfr_ioff
= mysz
;
444 priv_copyset(iset
, PFEXEC_REPLY_IPRIV(res
));
449 res
->pfr_loff
= mysz
;
450 priv_copyset(lset
, PFEXEC_REPLY_LPRIV(res
));
455 res
->pfr_setcred
= uid
!= (uid_t
)-1 || euid
!= (uid_t
)-1 ||
456 egid
!= (gid_t
)-1 || gid
!= (gid_t
)-1 || iset
!= NULL
||
459 /* If the real uid changes, we stop running under a profile shell */
460 res
->pfr_clearflag
= uid
!= (uid_t
)-1 && uid
!= uuid
;
463 (void) door_return((char *)res
, mysz
, NULL
, 0);
469 res
->pfr_scrubenv
= B_FALSE
;
470 res
->pfr_setcred
= B_FALSE
;
471 res
->pfr_allowed
= B_TRUE
;
473 (void) door_return((char *)res
, mysz
, NULL
, 0);
478 callback(void *cookie
, char *argp
, size_t asz
, door_desc_t
*dp
, uint_t ndesc
)
480 /* LINTED ALIGNMENT */
481 pfexec_arg_t
*pap
= (pfexec_arg_t
*)argp
;
483 if (asz
< sizeof (pfexec_arg_t
) || pap
->pfa_vers
!= PFEXEC_ARG_VERS
) {
484 (void) door_return(NULL
, 0, NULL
, 0);
488 switch (pap
->pfa_call
) {
489 case PFEXEC_EXEC_ATTRS
:
490 callback_pfexec(pap
);
492 case PFEXEC_FORCED_PRIVS
:
493 callback_forced_privs(pap
);
495 case PFEXEC_USER_PRIVS
:
496 callback_user_privs(pap
);
499 syslog(LOG_ERR
, "Bad Call: %d\n", pap
->pfa_call
);
504 * If the door_return(ptr, size, NULL, 0) fails, make sure we
505 * don't lose server threads.
507 (void) door_return(NULL
, 0, NULL
, 0);
513 const priv_impl_info_t
*info
;
515 (void) signal(SIGINT
, unregister_pfexec
);
516 (void) signal(SIGQUIT
, unregister_pfexec
);
517 (void) signal(SIGTERM
, unregister_pfexec
);
518 (void) signal(SIGHUP
, unregister_pfexec
);
520 info
= getprivimplinfo();
527 openlog("pfexecd", LOG_PID
, LOG_DAEMON
);
528 setsz
= info
->priv_setsize
* sizeof (priv_chunk_t
);
529 repsz
= 2 * setsz
+ sizeof (pfexec_reply_t
);
533 doorfd
= door_create(callback
, NULL
, DOOR_REFUSE_DESC
);
535 if (doorfd
== -1 || register_pfexec(doorfd
) != 0) {
540 /* LINTED CONSTCOND */
542 (void) sigpause(SIGINT
);