/*
* 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 Gary Mills
*/
#include "lint.h"
#include "priv_private.h"
#include "mtlib.h"
#include "libc.h"
#include <errno.h>
#include <stdarg.h>
#include <stdlib.h>
#include <unistd.h>
#include <strings.h>
#include <synch.h>
#include <alloca.h>
#include <atomic.h>
#include <priv_utils.h>
#include <zone.h>
/*
* Data independent privilege set operations.
*
* Only a few functions are provided that do not default to
* the system implementation of privileges. A limited set of
* interfaces is provided that accepts a priv_data_t *
* argument; this set of interfaces is a private interface between libc
* and libproc. It is delivered in order to interpret privilege sets
* in debuggers in a implementation independent way. As such, we
* don't need to provide the bulk of the interfaces, only a few
* boolean tests (isfull, isempty) the name<->num mappings and
* set pretty print functions. The boolean tests are only needed for
* the latter, so those aren't provided externally.
*
* Additionally, we provide the function that maps the kernel implementation
* structure into a libc private data structure.
*/
static int
{
char *q;
int i;
return (-1);
int l = strlen(q);
(*buf)[i] = q;
q += l + 1;
}
return (0);
}
struct strint {
char *name;
int rank;
};
static int
strintcmp(const void *a, const void *b)
{
}
{
char *x;
int i;
return (NULL);
x = (char *)ip;
x += ip->priv_headersize;
/* LINTED: alignment */
/* LINTED: alignment */
case PRIV_INFO_SETNAMES:
goto out;
break;
case PRIV_INFO_PRIVNAMES:
goto out;
/*
* We compute a sorted index which allows us
* to present a sorted list of privileges
* without actually having to sort it each time.
*/
sizeof (int));
goto out;
sizeof (struct strint));
goto out;
}
break;
case PRIV_INFO_BASICPRIVS:
break;
default:
/* unknown, ignore */
break;
}
}
return (tmp);
out:
return (NULL);
}
/*
* Caller must have allocated d->pd_pinfo and should free it,
* if necessary.
*/
void
{
libc_free(d->pd_setnames);
libc_free(d->pd_privnames);
libc_free(d->pd_setsort);
libc_free(d);
}
/*
* Return with the pd_lock held and data loaded or indicate failure.
*/
int
lock_data(void)
{
if (__priv_getdata() == NULL)
return (-1);
return (0);
}
refresh_data(void)
{
int i;
return (B_FALSE);
/* Parse the info; then copy the additional bits */
return (B_FALSE);
/* copy the extra information to the old datastructure */
(char *)ip + sizeof (priv_impl_info_t),
/* Copy the first oldn pointers */
oldn * sizeof (char *));
/* Adjust the rest */
/* Install the larger arrays */
/* Copy the rest of the data */
return (B_TRUE);
}
void
unlock_data(void)
{
}
__priv_getdata(void)
{
goto out;
goto out;
goto out;
}
goto out;
}
/* Allocate the zoneset just once, here */
goto clean;
== tmp->pd_setsize) {
goto out;
}
}
out:
}
return (privdata);
}
const priv_impl_info_t *
getprivimplinfo(void)
{
priv_data_t *d;
LOADPRIVDATA(d);
return (d->pd_pinfo);
}
static priv_set_t *
{
const char *priv;
return (NULL);
return (NULL);
}
}
return (pset);
}
/*
* priv_set(op, set, priv_id1, priv_id2, ..., NULL)
*
* Library routine to enable a user process to set a specific
* privilege set appropriately using a single call. User is
* required to terminate the list of privileges with NULL.
*/
int
{
int ret;
return (-1);
/* All sets */
priv_data_t *d;
int set;
LOADPRIVDATA(d);
break;
} else {
}
return (ret);
}
/*
* priv_ineffect(privilege).
* tests the existence of a privilege against the effective set.
*/
{
curset = priv_allocset();
return (B_FALSE);
else
return (res);
}
/*
* The routine __init_daemon_priv() is private to Solaris and is
* used by daemons to limit the privileges they can use and
* to set the uid they run under.
*/
int
{
priv_data_t *d;
LOADPRIVDATA(d);
return (-1);
/* Always add the basic set */
if (d->pd_basicset != NULL)
/*
* This is not a significant failure: it allows us to start programs
* with sufficient privileges and with the proper uid. We don't
* care enough about the extra groups in that case.
*/
if (flags & PU_RESETGROUPS)
goto end;
perm = priv_allocset();
goto end;
/* E = P */
/* Now reset suid and euid */
goto end;
/* Check for the limit privs */
if ((flags & PU_LIMITPRIVS) &&
goto end;
if (flags & PU_CLEARLIMITSET) {
goto end;
}
/* Remove the privileges from all the other sets */
goto end;
if (!(flags & PU_INHERITPRIVS))
end:
getpid());
} else {
(void) core_set_process_path(daemon_cp,
}
}
(void) setpflags(__PROC_PROTECT, 0);
return (ret);
}
/*
* The routine __fini_daemon_priv() is private to Solaris and is
* used by daemons to clear remaining unwanted privileges and
* reenable core dumps.
*/
void
{
return;
}
(void) setpflags(__PROC_PROTECT, 0);
}
/*
* The routine __init_suid_priv() is private to Solaris and is
* used by set-uid root programs to limit the privileges acquired
* to those actually needed.
*/
int
{
int r = -1;
/* If we're not set-uid root, don't reset the uid */
if (euid == 0) {
/* If we're running as root, keep everything */
if (ruid == 0)
return (0);
}
/* Can call this only once */
if (bracketpriv != NULL)
return (-1);
goto end;
tmpset = priv_allocset();
goto end;
/* We cannot grow our privileges beyond P, so start there */
/* Is the privilege we need even in P? */
goto end;
bracketpriv = priv_allocset();
if (bracketpriv == NULL)
goto end;
/* Always add the basic set */
/* But don't add what we don't have */
/* And stir in the inheritable privileges */
goto end;
goto end;
if (flags & PU_CLEARLIMITSET)
goto end;
if (euid == 0)
end:
if (r != 0) {
/* Fail without leaving uid 0 around */
if (euid == 0)
bracketpriv = NULL;
}
return (r);
}
/*
*/
int
{
/* We're running fully privileged or didn't check errors first time */
if (bracketpriv == NULL)
return (0);
/* Only PRIV_ON and PRIV_OFF are valid */
return (-1);
}
/*
* Remove privileges from E & P.
*/
void
__priv_relinquish(void)
{
if (bracketpriv != NULL) {
bracketpriv = NULL;
}
}
/*
* Use binary search on the ordered list.
*/
int
{
char *const *list;
const int *order;
int lo = 0;
int hi;
if (d == NULL)
return (-1);
list = d->pd_privnames;
order = d->pd_setsort;
name += 5;
do {
if (res == 0)
else if (res < 0)
else
return (-1);
}
int
{
}
int
{
int i;
int n = d->pd_nsets;
name += 5;
for (i = 0; i < n; i++) {
return (i);
}
return (-1);
}
int
{
/* Not locked: sets don't change */
}
static const char *
{
if (i < 0 || i >= n)
return (NULL);
return (list[i]);
}
const char *
{
if (d == NULL)
return (NULL);
}
const char *
{
}
const char *
{
if (d == NULL)
return (NULL);
}
const char *
{
}
/*
* Privilege manipulation functions
*
* Without knowing the details of the privilege set implementation,
* opaque pointers can be used to manipulate sets at will.
*/
static priv_set_t *
{
if (d == NULL)
return (NULL);
return (libc_malloc(d->pd_setsize));
}
priv_allocset(void)
{
return (__priv_allocset(GETPRIVDATA()));
}
void
{
libc_free(p);
}
void
{
}
void
{
}
void
{
}
void
{
}
void
{
}
int i; \
\
for (i = d->pd_pinfo->priv_setsize; i-- > 0; ) \
if (!(test)) \
return (B_FALSE); \
\
return (B_TRUE)
{
priv_data_t *d;
LOADPRIVDATA(d);
}
{
}
{
}
{
}
{
}
/*
* Return true if a is a subset of b
*/
{
((priv_chunk_t *)b)[i]);
}
{
return (__priv_issubset(GETPRIVDATA(), a, b));
}
int i; \
priv_data_t *d; \
\
LOADPRIVDATA(d); \
\
for (i = 0; i < d->pd_pinfo->priv_setsize; i++) \
((priv_chunk_t *)a)[i] op \
((priv_chunk_t *)b)[i]
/* B = A ^ B */
void
{
/* CSTYLED */
PRIV_CHANGE_BODY(b, &=, a);
}
/* B = A */
void
{
/* CSTYLED */
PRIV_CHANGE_BODY(b, =, a);
}
/* B = A v B */
void
{
/* CSTYLED */
PRIV_CHANGE_BODY(b, |=, a);
}
/* A = ! A */
void
{
PRIV_CHANGE_BODY(a, = ~, a);
}
/*
* Manipulating single privileges.
*/
int
{
if (priv < 0)
return (-1);
PRIV_ADDSET(a, priv);
return (0);
}
int
{
if (priv < 0)
return (-1);
PRIV_DELSET(a, priv);
return (0);
}
{
if (priv < 0)
return (B_FALSE);
}