klpd.c revision 7d8cb570f5834dc4f535a463c1c0839a57ada912
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2015, Joyent, Inc.
*/
#include <sys/cred_impl.h>
#include <sys/sysmacros.h>
#include <sys/pathname.h>
static kmutex_t klpd_mutex;
typedef struct klpd_reg {
int klpd_indel; /* Disabled */
} klpd_reg_t;
/*
* This data structure hangs off the credential of a process; the
* credential is finalized and cannot be changed; but this structure
* can be changed when a new door server for the particular group
* needs to be registered. It is refcounted and shared between
* processes with common ancestry.
*
* The reference count is atomically updated.
*
* But the registration probably needs to be updated under a lock.
*/
typedef struct credklpd {
} credklpd_t;
static void klpd_unlink(klpd_reg_t *);
static int klpd_unreg_dh(door_handle_t);
static credklpd_t *crklpd_alloc(void);
extern size_t max_vnode_path;
void
klpd_rele(klpd_reg_t *p)
{
if (atomic_dec_32_nv(&p->klpd_ref) == 0) {
klpd_unlink(p);
door_ki_rele(p->klpd_door);
kmem_free(p, sizeof (*p));
}
}
/*
* In order to be able to walk the lists, we can't unlink the entry
* until the reference count drops to 0. If we remove it too soon,
* list walkers will terminate when they happen to call a now orphaned
* entry.
*/
static klpd_reg_t *
{
klpd_reg_t *r = p->klpd_next;
klpd_rele(p);
return (r);
}
static void
klpd_hold(klpd_reg_t *p)
{
atomic_inc_32(&p->klpd_ref);
}
/*
* Remove registration from where it is registered. Returns next in list.
*/
static void
{
}
/*
* Remove all elements of the klpd list and decrement their refcnts.
* The lock guarding the list should be held; this function is
* called when we are sure we want to destroy the list completely
* list but not so sure that the reference counts of all elements have
* dropped back to 1.
*/
void
{
klpd_reg_t *p;
klpd_unlink(p);
klpd_rele(p);
}
}
/*
* Link new entry in list. The Boolean argument specifies whether this
* list can contain only a single item or multiple items.
* Returns the entry which needs to be released if single is B_TRUE.
*/
static klpd_reg_t *
{
*listp = p;
if (single) {
return (old);
} else
}
return (NULL);
}
/*
* The typical call consists of:
* - priv_set_t
* - some integer data (type, value)
* for now, it's just one bit.
*/
static klpd_head_t *
{
char *tmp;
int proto;
switch (type) {
case KLPDARG_NOMORE:
khp->klh_argoff = 0;
break;
case KLPDARG_VNODE:
return (NULL);
else
clen = 0;
return (NULL);
}
if (clen != 0) {
return (NULL);
}
/* Don't make root into a double "/" */
if (plen <= 2)
plen = 0;
}
break;
case KLPDARG_PORT:
switch (proto) {
break;
break;
break;
break;
}
/* FALLTHROUGH */
case KLPDARG_INT:
case KLPDARG_TCPPORT:
case KLPDARG_UDPPORT:
case KLPDARG_SCTPPORT:
case KLPDARG_SDPPORT:
break;
default:
return (NULL);
}
}
return (khp);
}
static int
{
int res;
int dres;
return (-1);
return (-1);
SIZE_MAX, 0)) != 0) {
switch (dres) {
case EAGAIN:
delay(1);
continue;
case EINVAL:
case EBADF:
/* Bad door, don't call it again. */
(void) klpd_unreg_dh(p->klpd_door);
/* FALLTHROUGH */
case EINTR:
/* Pending signal, nothing we can do. */
/* FALLTHROUGH */
default:
return (-1);
}
}
/* Bogus return value, must be a failure */
return (-1);
}
return (res);
}
int
{
klpd_reg_t *p;
int rv = -1;
/*
* These locks must not be held when this code is called;
* callbacks to userland with these locks held will result
* in issues. That said, the code at the call sides was
* restructured not to call with any of the locks held and
* no policies operate by default on most processes.
*/
return (-1);
}
/*
* Enforce the limit set for the call process (still).
*/
return (-1);
/* Try 1: get the credential specific klpd */
p->klpd_indel == 0 &&
klpd_hold(p);
klpd_rele(p);
if (rv != -1)
return (rv == 0 ? 0 : -1);
} else {
}
}
/* Try 2: get the project specific klpd */
klpd_hold(p);
if (p->klpd_indel == 0 &&
}
klpd_rele(p);
if (rv != -1)
return (rv == 0 ? 0 : -1);
} else {
}
/* Try 3: get the global klpd list */
p->klpd_indel == 0 &&
klpd_hold(p);
p = klpd_rele_next(p);
if (rv != -1)
break;
} else {
p = p->klpd_next;
}
}
return (rv == 0 ? 0 : -1);
}
/*
* Register the klpd.
* If the pid_t passed in is positive, update the registration for
* the specific process; that is only possible if the process already
* has a registration on it. This change of registration will affect
* all processes which share common ancestry.
*
* MY_PID (pid 0) can be used to create or change the context for
* the current process, typically done after fork().
*
* A negative value can be used to register a klpd globally.
*
* The per-credential klpd needs to be cleaned up when entering
* a zone or unsetting the flag.
*/
int
{
switch (type) {
case P_PID:
ckp = crklpd_alloc();
break;
case P_PROJID:
break;
default:
}
/*
* Verify the door passed in; it must be a door and we won't
* allow processes to be called on their own behalf.
*/
}
}
} else if ((int)pid < 0) {
/* Global daemon */
/* No need to lock, sole reference to ckp */
mutex_enter(&p->p_crlock);
mutex_exit(&p->p_crlock);
} else {
proc_t *p;
}
mutex_enter(&p->p_crlock);
mutex_exit(&p->p_crlock);
/*
* We're going to update the credential's ckp in place;
* this requires that it exists.
*/
}
}
return (0);
}
static int
{
klpd_reg_t *p;
break;
}
if (p == NULL) {
return (EINVAL);
}
if (p->klpd_indel != 0) {
return (EAGAIN);
}
p->klpd_indel = 1;
klpd_rele(p);
return (0);
}
int
{
int res = 0;
proc_t *p;
switch (type) {
case P_PID:
break;
case P_PROJID:
break;
default:
}
}
else
goto out;
} else if ((int)pid > 0) {
if (p == NULL) {
}
mutex_enter(&p->p_crlock);
} else if (pid == 0) {
p = curproc;
mutex_enter(&p->p_crlock);
} else {
goto out;
}
} else {
}
mutex_exit(&p->p_crlock);
out:
if (res != 0)
return (0);
}
void
{
}
void
{
}
}
static credklpd_t *
crklpd_alloc(void)
{
return (res);
}
void
{
} else {
}
}
/* Allocate and register the pfexec specific callback */
int
pfexec_reg(int did)
{
if (err != 0)
return (0);
}
int
pfexec_unreg(int did)
{
int err = 0;
} else {
}
/*
* crfree() cannot be called with zone_lock held; it is called
* indirectly through closing the door handle
*/
if (err != 0)
return (0);
}
static int
{
char *s;
if (len < 0)
return (0);
}
/*
* Build the pathname using the current directory + resolve pathname.
* The resolve pathname either starts with a normal component and
* we can just concatenate them or it starts with one
* or more ".." component and we can remove those; the
* last one cannot be a ".." and the current directory has
* more components than the number of ".." in the resolved pathname.
*/
return (-1);
len -= 3;
path += 3;
return (-1);
*s = '\0';
}
/* Add a "/" and a NUL */
return (-1);
return (0);
}
/*
* Perform the pfexec upcall.
*
* The pfexec upcall is different from the klpd_upcall in that a failure
* will lead to a denial of execution.
*/
int
{
int dres;
/* Find registration */
uprintf("pfexecd not running; pid %d privileges not "
return (0);
}
return (0);
}
goto out1;
switch (dres) {
case EAGAIN:
delay(1);
continue;
case EINVAL:
case EBADF:
/* FALLTHROUGH */
case EINTR:
/* FALLTHROUGH */
default:
goto out;
}
}
/*
* Check the size of the result and the alignment of the
* privilege sets.
*/
goto out;
/*
* Get results:
* allow/allow with additional credentials/disallow[*]
*
* euid, uid, egid, gid, privs, and limitprivs
* We now have somewhat more flexibility we could even set E and P
* judiciously but that would break some currently valid assumptions
* [*] Disallow is not readily supported by always including
* the Basic Solaris User profile in all user's profiles.
*/
if (!prp->pfr_allowed) {
goto out;
}
if (!prp->pfr_setcred) {
err = 0;
goto out;
}
/*
* Generate the new credential set scrubenv if ruid != euid (or set)
* the "I'm set-uid flag" but that is not inherited so scrubbing
* the environment is a requirement.
*/
/* Set uids or gids, note that -1 will do the right thing */
goto out;
goto out;
if (prp->pfr_clearflag)
/* We cannot exceed our Limit set, no matter what */
goto out;
}
/* Nor can we increate our Limit set itself */
goto out;
}
/* Exec will do the standard set operations */
err = 0;
out:
out1:
if (err == 0)
else
}
return (err);
}
int
{
int dres;
int err = -1;
return (-1);
return (0);
}
goto out1;
switch (dres) {
case EAGAIN:
delay(1);
continue;
case EINVAL:
case EBADF:
case EINTR:
default:
goto out;
}
}
/*
* Check the size of the result, it's a privilege set.
*/
goto out;
/*
* We restrict the forced privileges with whatever is available in
* the current zone.
*/
zkcr = zone_kcred();
/*
* But we fail if the forced privileges are not found in the current
* Limit set.
*/
} else if (!priv_isemptyset(fset)) {
err = 0;
}
out:
out1:
return (err);
}
int
{
int dres;
int err = -1;
return (-1);
return (0);
}
switch (dres) {
case EAGAIN:
delay(1);
continue;
case EINVAL:
case EBADF:
case EINTR:
default:
goto out;
}
}
/*
* Check the size of the result.
*/
goto out;
err = 0;
out:
out1:
return (err);
}