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
++] = '\\';
126 regexpr
[len
++] = '|';
133 (void) strcat(regexpr
, RIGHT
);
135 if (regcomp(®c
, regexpr
, REG_EXTENDED
) != 0)
144 removeisapath(char *path
)
146 regmatch_t match
[NMATCH
];
148 if (!cansplice
|| regexec(®c
, path
, NMATCH
, match
, 0) != 0)
152 * The first match includes the whole matched expression including the
153 * end of the string. The second match includes the "/" + "isa" and
154 * that is the part we need to remove.
157 if (match
[1].rm_so
== -1)
160 /* match[0].rm_eo == strlen(path) */
161 (void) memmove(path
+ match
[1].rm_so
, path
+ match
[1].rm_eo
,
162 match
[0].rm_eo
- match
[1].rm_eo
+ 1);
168 register_pfexec(int fd
)
170 int ret
= syscall(SYS_privsys
, PRIVSYS_PFEXEC_REG
, fd
);
177 unregister_pfexec(int sig
)
180 (void) syscall(SYS_privsys
, PRIVSYS_PFEXEC_UNREG
, doorfd
);
185 alldigits(const char *s
)
192 while ((c
= *s
++) != '\0') {
202 get_uid(const char *v
, boolean_t
*ok
, char *path
)
204 struct passwd
*pwd
, pwdm
;
207 if (getpwnam_r(v
, &pwdm
, buf
, sizeof (buf
), &pwd
) == 0 && pwd
!= NULL
)
208 return (pwd
->pw_uid
);
214 syslog(LOG_ERR
, "%s: %s: unknown username\n", path
, v
);
219 get_gid(const char *v
, boolean_t
*ok
, char *path
)
221 struct group
*grp
, grpm
;
224 if (getgrnam_r(v
, &grpm
, buf
, sizeof (buf
), &grp
) == 0 && grp
!= NULL
)
225 return (grp
->gr_gid
);
231 syslog(LOG_ERR
, "%s: %s: unknown groupname\n", path
, v
);
236 get_privset(const char *s
, boolean_t
*ok
, char *path
)
240 if ((res
= priv_str_to_set(s
, ",", NULL
)) == NULL
) {
241 syslog(LOG_ERR
, "%s: %s: bad privilege set\n", path
, s
);
250 ggp_callback(const char *prof
, kva_t
*attr
, void *ctxt
, void *vres
)
252 priv_set_t
*res
= vres
;
258 /* get privs from this profile */
259 privs
= kva_match(attr
, PROFATTR_PRIVS_KW
);
261 priv_set_t
*tmp
= priv_str_to_set(privs
, ",", NULL
);
263 priv_union(tmp
, res
);
272 * This routine exists on failure and returns NULL if no granted privileges
276 get_granted_privs(uid_t uid
)
279 struct passwd
*pwd
, pwdm
;
282 if (getpwuid_r(uid
, &pwdm
, buf
, sizeof (buf
), &pwd
) != 0 || pwd
== NULL
)
285 res
= priv_allocset();
291 (void) _enum_profs(pwd
->pw_name
, ggp_callback
, NULL
, res
);
297 callback_forced_privs(pfexec_arg_t
*pap
)
302 void *res
= alloca(setsz
);
304 /* Empty set signifies no forced privileges. */
307 exec
= getexecprof("Forced Privilege", KV_COMMAND
, pap
->pfa_path
,
310 if (exec
== NULL
&& removeisapath(pap
->pfa_path
)) {
311 exec
= getexecprof("Forced Privilege", KV_COMMAND
,
312 pap
->pfa_path
, GET_ONE
);
316 (void) door_return(res
, setsz
, NULL
, 0);
320 if ((value
= kva_match(exec
->attr
, EXECATTR_IPRIV_KW
)) == NULL
||
321 (fset
= get_privset(value
, NULL
, pap
->pfa_path
)) == NULL
) {
323 (void) door_return(res
, setsz
, NULL
, 0);
327 priv_copyset(fset
, res
);
331 (void) door_return(res
, setsz
, NULL
, 0);
335 callback_user_privs(pfexec_arg_t
*pap
)
337 priv_set_t
*gset
, *wset
;
340 wset
= (priv_set_t
*)&pap
->pfa_buf
;
341 gset
= get_granted_privs(pap
->pfa_uid
);
343 res
= priv_issubset(wset
, gset
);
346 (void) door_return((char *)&res
, sizeof (res
), NULL
, 0);
350 callback_pfexec(pfexec_arg_t
*pap
)
352 pfexec_reply_t
*res
= alloca(repsz
);
353 uid_t uid
, euid
, uuid
;
355 struct passwd pw
, *pwd
;
357 execattr_t
*exec
= NULL
;
359 priv_set_t
*lset
, *iset
;
360 size_t mysz
= repsz
- 2 * setsz
;
361 char *path
= pap
->pfa_path
;
364 * Initialize the pfexec_reply_t to a sane state.
366 res
->pfr_vers
= pap
->pfa_vers
;
368 res
->pfr_ruid
= PFEXEC_NOTSET
;
369 res
->pfr_euid
= PFEXEC_NOTSET
;
370 res
->pfr_rgid
= PFEXEC_NOTSET
;
371 res
->pfr_egid
= PFEXEC_NOTSET
;
372 res
->pfr_setcred
= B_FALSE
;
373 res
->pfr_scrubenv
= B_TRUE
;
374 res
->pfr_allowed
= B_FALSE
;
380 if (getpwuid_r(uuid
, &pw
, buf
, sizeof (buf
), &pwd
) != 0 || pwd
== NULL
)
383 exec
= getexecuser(pwd
->pw_name
, KV_COMMAND
, path
, GET_ONE
);
385 if ((exec
== NULL
|| exec
->attr
== NULL
) && removeisapath(path
)) {
387 exec
= getexecuser(pwd
->pw_name
, KV_COMMAND
, path
, GET_ONE
);
391 res
->pfr_allowed
= B_FALSE
;
395 if (exec
->attr
== NULL
)
398 /* Found in execattr, so clearly we can use it */
399 res
->pfr_allowed
= B_TRUE
;
401 uid
= euid
= (uid_t
)-1;
402 gid
= egid
= (gid_t
)-1;
406 * If there's an error in parsing uid, gid, privs, then return
409 if ((value
= kva_match(exec
->attr
, EXECATTR_UID_KW
)) != NULL
)
410 euid
= uid
= get_uid(value
, &res
->pfr_allowed
, path
);
412 if ((value
= kva_match(exec
->attr
, EXECATTR_GID_KW
)) != NULL
)
413 egid
= gid
= get_gid(value
, &res
->pfr_allowed
, path
);
415 if ((value
= kva_match(exec
->attr
, EXECATTR_EUID_KW
)) != NULL
)
416 euid
= get_uid(value
, &res
->pfr_allowed
, path
);
418 if ((value
= kva_match(exec
->attr
, EXECATTR_EGID_KW
)) != NULL
)
419 egid
= get_gid(value
, &res
->pfr_allowed
, path
);
421 if ((value
= kva_match(exec
->attr
, EXECATTR_LPRIV_KW
)) != NULL
)
422 lset
= get_privset(value
, &res
->pfr_allowed
, path
);
424 if ((value
= kva_match(exec
->attr
, EXECATTR_IPRIV_KW
)) != NULL
)
425 iset
= get_privset(value
, &res
->pfr_allowed
, path
);
428 * Remove LD_* variables in the kernel when the runtime linker might
429 * use them later on because the uids are equal.
431 res
->pfr_scrubenv
= (uid
!= (uid_t
)-1 && euid
== uid
) ||
432 (gid
!= (gid_t
)-1 && egid
== gid
) || iset
!= NULL
;
434 res
->pfr_euid
= euid
;
436 res
->pfr_egid
= egid
;
439 /* Now add the privilege sets */
440 res
->pfr_ioff
= res
->pfr_loff
= 0;
442 res
->pfr_ioff
= mysz
;
443 priv_copyset(iset
, PFEXEC_REPLY_IPRIV(res
));
448 res
->pfr_loff
= mysz
;
449 priv_copyset(lset
, PFEXEC_REPLY_LPRIV(res
));
454 res
->pfr_setcred
= uid
!= (uid_t
)-1 || euid
!= (uid_t
)-1 ||
455 egid
!= (gid_t
)-1 || gid
!= (gid_t
)-1 || iset
!= NULL
||
458 /* If the real uid changes, we stop running under a profile shell */
459 res
->pfr_clearflag
= uid
!= (uid_t
)-1 && uid
!= uuid
;
462 (void) door_return((char *)res
, mysz
, NULL
, 0);
468 res
->pfr_scrubenv
= B_FALSE
;
469 res
->pfr_setcred
= B_FALSE
;
470 res
->pfr_allowed
= B_TRUE
;
472 (void) door_return((char *)res
, mysz
, NULL
, 0);
477 callback(void *cookie
, char *argp
, size_t asz
, door_desc_t
*dp
, uint_t ndesc
)
479 /* LINTED ALIGNMENT */
480 pfexec_arg_t
*pap
= (pfexec_arg_t
*)argp
;
482 if (asz
< sizeof (pfexec_arg_t
) || pap
->pfa_vers
!= PFEXEC_ARG_VERS
) {
483 (void) door_return(NULL
, 0, NULL
, 0);
487 switch (pap
->pfa_call
) {
488 case PFEXEC_EXEC_ATTRS
:
489 callback_pfexec(pap
);
491 case PFEXEC_FORCED_PRIVS
:
492 callback_forced_privs(pap
);
494 case PFEXEC_USER_PRIVS
:
495 callback_user_privs(pap
);
498 syslog(LOG_ERR
, "Bad Call: %d\n", pap
->pfa_call
);
503 * If the door_return(ptr, size, NULL, 0) fails, make sure we
504 * don't lose server threads.
506 (void) door_return(NULL
, 0, NULL
, 0);
512 const priv_impl_info_t
*info
;
514 (void) signal(SIGINT
, unregister_pfexec
);
515 (void) signal(SIGQUIT
, unregister_pfexec
);
516 (void) signal(SIGTERM
, unregister_pfexec
);
517 (void) signal(SIGHUP
, unregister_pfexec
);
519 info
= getprivimplinfo();
526 openlog("pfexecd", LOG_PID
, LOG_DAEMON
);
527 setsz
= info
->priv_setsize
* sizeof (priv_chunk_t
);
528 repsz
= 2 * setsz
+ sizeof (pfexec_reply_t
);
532 doorfd
= door_create(callback
, NULL
, DOOR_REFUSE_DESC
);
534 if (doorfd
== -1 || register_pfexec(doorfd
) != 0) {
539 /* LINTED CONSTCOND */
541 (void) sigpause(SIGINT
);