rc_node.c revision 582271e8d649568c83e9a016cc0d54265389c5d9
/*
* 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 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* rc_node.c - object management primitives
*
* This layer manages entities, their data structure, its locking, iterators,
* transactions, and change notification requests. Entities (scopes,
* services, instances, snapshots, snaplevels, property groups, "composed"
* property groups (see composition below), and properties) are represented by
* rc_node_t's and are kept in the cache_hash hash table. (Property values
* are kept in the rn_values member of the respective property -- not as
* separate objects.) Iterators are represented by rc_node_iter_t's.
* Transactions are represented by rc_node_tx_t's and are only allocated as
* part of repcache_tx_t's in the client layer (client.c). Change
* notification requests are represented by rc_notify_t structures and are
* described below.
*
* The entity tree is rooted at rc_scope, which rc_node_init() initializes to
* the "localhost" scope. The tree is filled in from the database on-demand
* by rc_node_fill_children(), usually from rc_iter_create() since iterators
* are the only way to find the children of an entity.
*
* Each rc_node_t is protected by its rn_lock member. Operations which can
* take too long, however, should serialize on an RC_NODE_WAITING_FLAGS bit in
* rn_flags with the rc_node_{hold,rele}_flag() functions. And since pointers
* to rc_node_t's are allowed, rn_refs is a reference count maintained by
* rc_node_{hold,rele}(). See configd.h for locking order information.
*
* When a node (property group or snapshot) is updated, a new node takes the
* place of the old node in the global hash, and the old node is hung off of
* the rn_former list of the new node. At the same time, all of its children
* have their rn_parent_ref pointer set, and any holds they have are reflected
* in the old node's rn_other_refs count. This is automatically kept up
* to date, until the final reference to the subgraph is dropped, at which
* point the node is unrefed and destroyed, along with all of its children.
*
* Locking rules: To dereference an rc_node_t * (usually to lock it), you must
* have a hold (rc_node_hold()) on it or otherwise be sure that it hasn't been
* rc_node_destroy()ed (hold a lock on its parent or child, hold a flag,
* etc.). Once you have locked an rc_node_t you must check its rn_flags for
* RC_NODE_DEAD before you can use it. This is usually done with the
* rc_node_{wait,hold}_flag() functions (often via the rc_node_check_*()
* functions & RC_NODE_*() macros), which fail if the object has died.
*
* Because name service lookups may take a long time and, more importantly
* may trigger additional accesses to the repository, perm_granted() must be
* called without holding any locks.
*
* An ITER_START for a non-ENTITY_VALUE induces an rc_node_fill_children()
* call via rc_node_setup_iter() to populate the rn_children uu_list of the
* rc_node_t * in question and a call to uu_list_walk_start() on that list. For
* ITER_READ, rc_iter_next() uses uu_list_walk_next() to find the next
* apropriate child.
*
* An ITER_START for an ENTITY_VALUE makes sure the node has its values
* filled, and sets up the iterator. An ITER_READ_VALUE just copies out
* the proper values and updates the offset information.
*
* When a property group gets changed by a transaction, it sticks around as
* a child of its replacement property group, but is removed from the parent.
*
* To allow aliases, snapshots are implemented with a level of indirection.
* A snapshot rc_node_t has a snapid which refers to an rc_snapshot_t in
* snapshot.c which contains the authoritative snaplevel information. The
* snapid is "assigned" by rc_attach_snapshot().
*
* We provide the client layer with rc_node_ptr_t's to reference objects.
* Objects referred to by them are automatically held & released by
* rc_node_assign() & rc_node_clear(). The RC_NODE_PTR_*() macros are used at
* client.c entry points to read the pointers. They fetch the pointer to the
* object, return (from the function) if it is dead, and lock, hold, or hold
* a flag of the object.
*/
/*
* Permission checking is authorization-based: some operations may only
* proceed if the user has been assigned at least one of a set of
* authorization strings. The set of enabling authorizations depends on the
* operation and the target object. The set of authorizations assigned to
* a user is determined by reading /etc/security/policy.conf, querying the
* user_attr database, and possibly querying the prof_attr database, as per
* chkauthattr() in libsecdb.
*
* The fastest way to decide whether the two sets intersect is by entering the
* strings into a hash table and detecting collisions, which takes linear time
* in the total size of the sets. Except for the authorization patterns which
* may be assigned to users, which without advanced pattern-matching
* algorithms will take O(n) in the number of enabling authorizations, per
* pattern.
*
* We can achieve some practical speed-ups by noting that if we enter all of
* the authorizations from one of the sets into the hash table we can merely
* check the elements of the second set for existence without adding them.
* This reduces memory requirements and hash table clutter. The enabling set
* is well suited for this because it is internal to configd (for now, at
* least). Combine this with short-circuiting and we can even minimize the
* number of queries to the security databases (user_attr & prof_attr).
*
* To force this usage onto clients we provide functions for adding
* authorizations to the enabling set of a permission context structure
* (perm_add_*()) and one to decide whether the the user associated with the
* current door call client possesses any of them (perm_granted()).
*
* At some point, a generic version of this should move to libsecdb.
*
* While entering the enabling strings into the hash table, we keep track
* of which is the most specific for use in generating auditing events.
* See the "Collecting the Authorization String" section of the "SMF Audit
* Events" block comment below.
*/
/*
* Composition is the combination of sets of properties. The sets are ordered
* and properties in higher sets obscure properties of the same name in lower
* sets. Here we present a composed view of an instance's properties as the
* union of its properties and its service's properties. Similarly the
* properties of snaplevels are combined to form a composed view of the
* properties of a snapshot (which should match the composed view of the
* properties of the instance when the snapshot was taken).
*
* In terms of the client interface, the client may request that a property
* group iterator for an instance or snapshot be composed. Property groups
* traversed by such an iterator may not have the target entity as a parent.
* Similarly, the properties traversed by a property iterator for those
* property groups may not have the property groups iterated as parents.
*
* Implementation requires that iterators for instances and snapshots be
* composition-savvy, and that we have a "composed property group" entity
* which represents the composition of a number of property groups. Iteration
* over "composed property groups" yields properties which may have different
* parents, but for all other operations a composed property group behaves
* like the top-most property group it represents.
*
* The implementation is based on the rn_cchain[] array of rc_node_t pointers
* in rc_node_t. For instances, the pointers point to the instance and its
* parent service. For snapshots they point to the child snaplevels, and for
* composed property groups they point to property groups. A composed
* iterator carries an index into rn_cchain[]. Thus most of the magic ends up
* int the rc_iter_*() code.
*/
/*
* SMF Audit Events:
* ================
*
* To maintain security, SMF generates audit events whenever
* privileged operations are attempted. See the System Administration
* Guide:Security Services answerbook for a discussion of the Solaris
* audit system.
*
* The SMF audit event codes are defined in adt_event.h by symbols
* starting with ADT_smf_ and are described in audit_event.txt. The
* audit record structures are defined in the SMF section of adt.xml.
* adt.xml is used to automatically generate adt_event.h which
* contains the definitions that we code to in this file. For the
* most part the audit events map closely to actions that you would
* perform with svcadm or svccfg, but there are some special cases
* which we'll discuss later.
*
* The software associated with SMF audit events falls into three
* categories:
* - collecting information to be written to the audit
* records
* - using the adt_* functions in
* records.
* - handling special cases
*
* Collecting Information:
* ----------------------
*
* Most all of the audit events require the FMRI of the affected
* object and the authorization string that was used. The one
* exception is ADT_smf_annotation which we'll talk about later.
*
* Collecting the FMRI:
*
* The rc_node structure has a member called rn_fmri which points to
* its FMRI. This is initialized by a call to rc_node_build_fmri()
* when the node's parent is established. The reason for doing it
* at this time is that a node's FMRI is basically the concatenation
* of the parent's FMRI and the node's name with the appropriate
* decoration. rc_node_build_fmri() does this concatenation and
* decorating. It is called from rc_node_link_child() and
* rc_node_relink_child() where a node is linked to its parent.
*
* rc_node_get_fmri_or_fragment() is called to retrieve a node's FMRI
* when it is needed. It returns rn_fmri if it is set. If the node
* is at the top level, however, rn_fmri won't be set because it was
* never linked to a parent. In this case,
* rc_node_get_fmri_or_fragment() constructs an FMRI fragment based on
* its node type and its name, rn_name.
*
* Collecting the Authorization String:
*
* Naturally, the authorization string is captured during the
* authorization checking process. Acceptable authorization strings
* are added to a permcheck_t hash table as noted in the section on
* permission checking above. Once all entries have been added to the
* hash table, perm_granted() is called. If the client is authorized,
* perm_granted() returns with pc_auth_string of the permcheck_t
* structure pointing to the authorization string.
*
* This works fine if the client is authorized, but what happens if
* the client is not authorized? We need to report the required
* authorization string. This is the authorization that would have
* been used if permission had been granted. perm_granted() will
* find no match, so it needs to decide which string in the hash
* table to use as the required authorization string. It needs to do
* this, because configd is still going to generate an event. A
* design decision was made to use the most specific authorization
* in the hash table. The pc_auth_type enum designates the
* specificity of an authorization string. For example, an
* authorization string that is declared in an instance PG is more
* specific than one that is declared in a service PG.
*
* The pc_add() function keeps track of the most specific
* authorization in the hash table. It does this using the
* pc_specific and pc_specific_type members of the permcheck
* structure. pc_add() updates these members whenever a more
* specific authorization string is added to the hash table. Thus, if
* an authorization match is not found, perm_granted() will return
* with pc_auth_string in the permcheck_t pointing to the string that
* is referenced by pc_specific.
*
* Generating the Audit Events:
* ===========================
*
* As the functions in this file process requests for clients of
* configd, they gather the information that is required for an audit
* event. Eventually, the request processing gets to the point where
* the authorization is rejected or to the point where the requested
* action was attempted. At these two points smf_audit_event() is
* called.
*
* smf_audit_event() takes 4 parameters:
* - the event ID which is one of the ADT_smf_* symbols from
* adt_event.h.
* - status to pass to adt_put_event()
* - return value to pass to adt_put_event()
* - the event data (see audit_event_data structure)
*
* All interactions with the auditing software require an audit
* session. We use one audit session per configd client. We keep
* track of the audit session in the repcache_client structure.
* smf_audit_event() calls get_audit_session() to get the session
* pointer.
*
* smf_audit_event() then calls adt_alloc_event() to allocate an
* adt_event_data union which is defined in adt_event.h, copies the
* data into the appropriate members of the union and calls
* adt_put_event() to generate the event.
*
* Special Cases:
* =============
*
* There are three major types of special cases:
*
* - gathering event information for each action in a
* transaction
* - Higher level events represented by special property
* restarter actions.
* - ADT_smf_annotation event
*
* Processing Transaction Actions:
* ------------------------------
*
* A transaction can contain multiple actions to modify, create or
* delete one or more properties. We need to capture information so
* that we can generate an event for each property action. The
* transaction information is stored in a tx_commmit_data_t, and
* object.c provides accessor functions to retrieve data from this
* structure. rc_tx_commit() obtains a tx_commit_data_t by calling
* tx_commit_data_new() and passes this to object_tx_commit() to
* commit the transaction. Then we call generate_property_events() to
* generate an audit event for each property action.
*
* Special Properties:
* ------------------
*
* They are special because they have specific meaning to startd. startd
* interprets them in a service-independent fashion.
* restarter_actions/refresh and general/enabled are two examples of these.
* A special event is generated for these properties in addition to the
* regular property event described in the previous section. The special
* properties are declared as an array of audit_special_prop_item
* structures at special_props_list in rc_node.c.
*
* In the previous section, we mentioned the
* generate_property_event() function that generates an event for
* every property action. Before generating the event,
* generate_property_event() calls special_property_event().
* special_property_event() checks to see if the action involves a
* special property. If it does, it generates a special audit
* event.
*
* ADT_smf_annotation event:
* ------------------------
*
* This is a special event unlike any other. It allows the svccfg
* program to store an annotation in the event log before a series
* of transactions is processed. It is used with the import and
* apply svccfg commands. svccfg uses the rep_protocol_annotation
* message to pass the operation (import or apply) and the file name
* to configd. The set_annotation() function in client.c stores
* these away in the a repcache_client structure. The address of
* this structure is saved in the thread_info structure.
*
* Before it generates any events, smf_audit_event() calls
* smf_annotation_event(). smf_annotation_event() calls
* client_annotation_needed() which is defined in client.c. If an
* annotation is needed client_annotation_needed() returns the
* operation and filename strings that were saved from the
* rep_protocol_annotation message. smf_annotation_event() then
* generates the ADT_smf_annotation event.
*/
#include <assert.h>
#include <atomic.h>
#include <bsm/adt_event.h>
#include <errno.h>
#include <libuutil.h>
#include <libscf.h>
#include <libscf_priv.h>
#include <prof_attr.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <syslog.h>
#include <unistd.h>
#include <user_attr.h>
#include "configd.h"
#define AUTH_PREFIX "solaris.smf."
#define AUTH_PG_GENERAL SCF_PG_GENERAL
#define AUTH_PROP_ACTION "action_authorization"
#define AUTH_PROP_ENABLED "enabled"
#define AUTH_PROP_MODIFY "modify_authorization"
#define AUTH_PROP_VALUE "value_authorization"
#define AUTH_PROP_READ "read_authorization"
/* libsecdb should take care of this. */
#define RBAC_AUTH_SEP ","
#define MAX_VALID_CHILDREN 3
/*
* The ADT_smf_* symbols may not be defined on the build machine. Because
* of this, we do not want to compile the _smf_aud_event() function when
* doing native builds.
*/
#ifdef NATIVE_BUILD
#define smf_audit_event(i, s, r, d)
#else
#define smf_audit_event(i, s, r, d) _smf_audit_event(i, s, r, d)
#endif /* NATIVE_BUILD */
typedef struct rc_type_info {
#define RT_NO_NAME -1U
static rc_type_info_t rc_types[] = {
{REP_PROTOCOL_ENTITY_SCOPE, 0, 0,
{-1UL}
};
/* Element of a permcheck_t hash table. */
struct pc_elt {
char pce_auth[1];
};
/*
* If an authorization fails, we must decide which of the elements in the
* permcheck hash table to use in the audit event. That is to say of all
* the strings in the hash table, we must choose one and use it in the audit
* event. It is desirable to use the most specific string in the audit
* event.
*
* The pc_auth_type specifies the types (sources) of authorization
* strings. The enum is ordered in increasing specificity.
*/
typedef enum pc_auth_type {
PC_AUTH_NONE = 0, /* no auth string available. */
PC_AUTH_SMF, /* strings coded into SMF. */
PC_AUTH_SVC, /* strings specified in PG of a service. */
PC_AUTH_INST /* strings specified in PG of an instance. */
/* An authorization set hash table. */
typedef struct {
struct pc_elt **pc_buckets;
char *pc_auth_string; /* authorization string */
/* for audit events */
} permcheck_t;
/*
* Structure for holding audit event data. Not all events use all members
* of the structure.
*/
typedef struct audit_event_data {
char *ed_auth; /* authorization string. */
char *ed_fmri; /* affected FMRI. */
char *ed_snapname; /* name of snapshot. */
char *ed_old_fmri; /* old fmri in attach case. */
char *ed_old_name; /* old snapshot in attach case. */
char *ed_type; /* prop. group or prop. type. */
char *ed_prop_value; /* property value. */
/*
* Pointer to function to do special processing to get audit event ID.
* Audit event IDs are defined in /usr/include/bsm/adt_event.h. Function
* returns 0 if ID successfully retrieved. Otherwise it returns -1.
*/
au_event_t *);
au_event_t *);
static uu_list_pool_t *rc_children_pool;
static uu_list_pool_t *rc_pg_notify_pool;
static uu_list_pool_t *rc_notify_pool;
static uu_list_pool_t *rc_notify_info_pool;
/*
* audit event to be generated when there is a change.
* audit_special_prop_item_t is used to specify these special cases. The
* special_props_list array defines a list of these special properties.
*/
typedef struct audit_special_prop_item {
const char *api_pg_name; /* property group name. */
const char *api_prop_name; /* property name. */
/*
* Native builds are done using the build machine's standard include
* files. These files may not yet have the definitions for the ADT_smf_*
* symbols. Thus, we do not compile this table when doing native builds.
*/
#ifndef NATIVE_BUILD
/*
* name combinations that have specific meaning to startd. A special event
* is generated for these combinations in addition to the regular property
* event.
*
* At run time this array gets sorted. See the call to qsort(3C) in
* rc_node_init(). The array is sorted, so that bsearch(3C) can be used
* to do lookups.
*/
static audit_special_prop_item_t special_props_list[] = {
NULL},
};
#define SPECIAL_PROP_COUNT (sizeof (special_props_list) /\
sizeof (audit_special_prop_item_t))
#endif /* NATIVE_BUILD */
/*
* We support an arbitrary number of clients interested in events for certain
* types of changes. Each client is represented by an rc_notify_info_t, and
* all clients are chained onto the rc_notify_info_list.
*
* The rc_notify_list is the global notification list. Each entry is of
* type rc_notify_t, which is embedded in one of three other structures:
*
* rc_node_t property group update notification
* rc_notify_delete_t object deletion notification
* rc_notify_info_t notification clients
*
* Which type of object is determined by which pointer in the rc_notify_t is
* non-NULL.
*
* New notifications and clients are added to the end of the list.
* Notifications no-one is interested in are never added to the list.
*
* Clients use their position in the list to track which notifications they
* have not yet reported. As they process notifications, they move forward
* in the list past them. There is always a client at the beginning of the
* list -- as he moves past notifications, he removes them from the list and
* cleans them up.
*
* The rc_pg_notify_lock protects all notification state. The rc_pg_notify_cv
* is used for global signalling, and each client has a cv which he waits for
* events of interest on.
*/
static uu_list_t *rc_notify_info_list;
static uu_list_t *rc_notify_list;
#define HASH_SIZE 512
static uint32_t
{
while (num_ids-- > 0)
/*
* the rest should be zeroed
*/
while (left-- > 0)
return (hash);
}
static int
{
if (r->rl_main_id != l->rl_main_id)
return (0);
return (0);
while (num_ids-- > 0)
return (0);
return (1);
}
/*
* the "other" references on a node are maintained in an atomically
* updated refcount, rn_other_refs. This can be bumped from arbitrary
* context, and tracks references to a possibly out-of-date node's children.
*
* To prevent the node from disappearing between the final drop of
* rn_other_refs and the unref handling, rn_other_refs_held is bumped on
* 0->1 transitions and decremented (with the node lock held) on 1->0
* transitions.
*/
static void
{
}
}
/*
* No node locks may be held
*/
static void
{
else
}
}
static void
{
}
static void
{
}
static void
{
int unref = 0;
/*
* Composed property groups are only as good as their
* references.
*/
unref = 1;
}
if (unref)
else
}
void
{
}
static cache_bucket_t *
{
return (bp);
}
static void
{
}
static rc_node_t *
{
return (np);
}
}
return (NULL);
}
static rc_node_t *
{
uint32_t h;
h = rc_node_hash(lp);
bp = cache_hold(h);
return (np);
}
static void
{
}
static void
{
break;
}
/*
* verify that the 'parent' type can have a child typed 'child'
* Fails with
* _INVALID_TYPE - argument is invalid
* _TYPE_MISMATCH - parent type cannot have children of type child
*/
static int
{
int idx;
return (REP_PROTOCOL_FAIL_INVALID_TYPE); /* invalid types */
return (REP_PROTOCOL_SUCCESS);
}
return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
}
/*
* Fails with
* _INVALID_TYPE - type is invalid
* _BAD_REQUEST - name is an invalid name for a node of type type
*/
int
{
return (REP_PROTOCOL_FAIL_INVALID_TYPE); /* invalid types */
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
return (REP_PROTOCOL_SUCCESS);
}
static int
rc_check_pgtype_name(const char *name)
{
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
return (REP_PROTOCOL_SUCCESS);
}
/*
* rc_node_free_fmri should be called whenever a node loses its parent.
* The reason is that the node's fmri string is built up by concatenating
* its name to the parent's fmri. Thus, when the node no longer has a
* parent, its fmri is no longer valid.
*/
static void
{
}
}
/*
* Concatenate the appropriate separator and the FMRI element to the base
* FMRI string at fmri.
*
* Fails with
* _TRUNCATED Not enough room in buffer at fmri.
*/
static int
char *fmri, /* base fmri */
const char *element, /* element name to concat */
{
int rc;
const char *separator;
if (bufsize > 0)
else
*sz_out = 0;
switch (type) {
/*
* No need to display scope information if we are
* in the local scope.
*/
} else {
/*
* Need to display scope information, because it is
* not the local scope.
*/
}
break;
break;
break;
break;
break;
/*
* A value does not have a separate FMRI from its property,
* so there is nothing to concat.
*/
return (REP_PROTOCOL_SUCCESS);
/* Snapshots do not have FMRIs, so there is nothing to do. */
return (REP_PROTOCOL_SUCCESS);
default:
abort(); /* Missing a case in switch if we get here. */
}
/* Concatenate separator and element to the fmri buffer. */
} else {
}
}
} else {
}
return (rc);
}
/*
* Get the FMRI for the node at np. The fmri will be placed in buf. On
* success sz_out will be set to the size of the fmri in buf. If
* REP_PROTOCOL_FAIL_TRUNCATED is returned, sz_out will be set to the size
* of the buffer that would be required to avoid truncation.
*
* Fails with
* _TRUNCATED not enough room in buf for the FMRI.
*/
static int
{
int r;
if (bufsize > 0)
*buf = 0;
*sz_out = 0;
/*
* A NULL rn_fmri implies that this is a top level scope.
* Child nodes will always have an rn_fmri established
* because both rc_node_link_child() and
* rc_node_relink_child() call rc_node_build_fmri(). In
* this case, we'll just return our name preceded by the
* appropriate FMRI decorations.
*/
if (r != REP_PROTOCOL_SUCCESS)
return (r);
} else {
/* We have an fmri, so return it. */
}
return (REP_PROTOCOL_FAIL_TRUNCATED);
return (REP_PROTOCOL_SUCCESS);
}
/*
* Build an FMRI string for this node and save it in rn_fmri.
*
* The basic strategy here is to get the fmri of our parent and then
* concatenate the appropriate separator followed by our name. If our name
* is null, the resulting fmri will just be a copy of the parent fmri.
* rc_node_build_fmri() should be called with the RC_NODE_USING_PARENT flag
* set. Also the rn_lock for this node should be held.
*
* Fails with
* _NO_RESOURCES Could not allocate memory.
*/
static int
{
char fmri[REP_PROTOCOL_FMRI_LEN];
int rc;
} else {
}
} else {
}
return (rc);
}
/*
* Get the FMRI of the node at np placing the result in fmri. Then
* concatenate the additional element to fmri. The type variable indicates
* the type of element, so that the appropriate separator can be
* generated. size is the number of bytes in the buffer at fmri, and
* sz_out receives the size of the generated string. If the result is
* truncated, sz_out will receive the size of the buffer that would be
* required to avoid truncation.
*
* Fails with
* _TRUNCATED Not enough room in buffer at fmri.
*/
static int
{
int rc;
return (rc);
}
return (rc);
}
return (REP_PROTOCOL_SUCCESS);
}
static int
{
int i;
return (1); /* everyone likes deletes */
}
return (0);
}
for (i = 0; i < RC_NOTIFY_MAX_NAMES; i++) {
return (1);
}
return (1);
}
}
return (0);
}
static void
{
int found = 0;
return;
(void) pthread_mutex_lock(&rc_pg_notify_lock);
found++;
}
}
if (found)
else
(void) pthread_mutex_unlock(&rc_pg_notify_lock);
}
static void
{
"svc:/%s%s%s%s%s", service,
/*
* add to notification list, notify watchers
*/
(void) pthread_mutex_lock(&rc_pg_notify_lock);
(void) pthread_mutex_unlock(&rc_pg_notify_lock);
}
static void
{
(void) pthread_mutex_lock(&rc_pg_notify_lock);
if (rc_notify_in_use) {
(void) pthread_cond_wait(&rc_pg_notify_cv,
continue;
}
break;
}
(void) pthread_mutex_unlock(&rc_pg_notify_lock);
}
static void
{
assert(rc_notify_in_use == 0);
} else if (np->rcn_delete) {
} else {
assert(0); /* CAN'T HAPPEN */
}
}
/*
* Permission checking functions. See comment atop this file.
*/
#ifndef NATIVE_BUILD
static permcheck_t *
{
permcheck_t *p;
p = uu_zalloc(sizeof (*p));
if (p == NULL)
return (NULL);
if (p->pc_buckets == NULL) {
uu_free(p);
return (NULL);
}
p->pc_enum = 0;
return (p);
}
static void
{
uint_t i;
}
}
}
static uint32_t
{
uint32_t h = 0, g;
const char *p;
/*
*/
for (p = auth; *p != '\0'; ++p) {
h = (h << 4) + *p;
g = (h & 0xf0000000);
if (g != 0) {
h ^= (g >> 24);
h ^= g;
}
}
return (h);
}
static int
{
uint32_t h;
return (1);
}
}
return (0);
}
static int
{
uint_t i;
return (1);
}
}
}
return (0);
}
static int
{
struct pc_elt **new_buckets;
/* Homey don't play that. */
return (-1);
if (new_buckets == NULL)
return (-1);
new_buckets[j] = ep;
}
}
return (0);
}
static int
{
uint_t i;
return (-1);
/* Grow if pc_enum / pc_bnum > 3/4. */
/* Failure is not a stopper; we'll try again next time. */
}
return (0);
}
/*
* For the type of a property group, return the authorization which may be
* used to modify it.
*/
static const char *
perm_auth_for_pgtype(const char *pgtype)
{
return (AUTH_MODIFY_PREFIX "method");
return (AUTH_MODIFY_PREFIX "dependency");
return (AUTH_MODIFY_PREFIX "application");
return (AUTH_MODIFY_PREFIX "framework");
else
return (NULL);
}
/*
* Fails with
* _NO_RESOURCES - out of memory
*/
static int
{
}
/*
* Fails with
* _NO_RESOURCES - out of memory
*/
static int
{
}
/* Note that perm_add_enabling_values() is defined below. */
/*
* perm_granted() returns 1 if the current door caller has one of the enabling
* authorizations in pcp, 0 if it doesn't, and -1 if an error (usually lack of
* memory) occurs. check_auth_list() checks an RBAC_AUTH_SEP-separated list
* of authorizations for existence in pcp, and check_prof_list() checks the
* authorizations granted to an RBAC_AUTH_SEP-separated list of profiles.
*/
static int
{
int ret;
else
if (ret)
return (ret);
}
/*
* If we failed, choose the most specific auth string for use in
* the audit event.
*/
return (0);
}
static int
{
int ret = 0;
continue;
if (!ret) {
if (subproflist != NULL)
/* depth check to avoid infinite recursion? */
}
if (ret)
return (ret);
}
return (ret);
}
static int
{
int ret = 0;
/*
* Get generic authorizations from policy.conf
*
* Note that _get_auth_policy is not threadsafe, so we single-thread
* access to it.
*/
(void) pthread_mutex_lock(&perm_lock);
(void) pthread_mutex_unlock(&perm_lock);
if (ret != 0)
return (-1);
if (ret) {
return (ret);
}
}
/*
* Put off checking def_prof for later in an attempt to consolidate
* prof_attr accesses.
*/
/* Get the uid */
/*
* Client is no longer waiting for our response (e.g.,
* it received a signal & resumed with EINTR).
* Punting with door_return() would be nice but we
* need to release all of the locks & references we
* hold. And we must report failure to the client
* layer to keep it from ignoring retries as
* already-done (idempotency & all that). None of the
* error codes fit very well, so we might as well
* force the return of _PERMISSION_DENIED since we
* couldn't determine the user.
*/
return (0);
}
assert(0);
abort();
}
/* Get the authorizations from user_attr. */
if (userattr_authlist != NULL) {
}
}
/* Check generic profiles. */
}
}
return (ret);
}
#endif /* NATIVE_BUILD */
/*
* flags in RC_NODE_WAITING_FLAGS are broadcast when unset, and are used to
* serialize certain actions, and to wait for certain operations to complete
*
* The waiting flags are:
* RC_NODE_CHILDREN_CHANGING
* The child list is being built or changed (due to creation
* or deletion). All iterators pause.
*
* RC_NODE_USING_PARENT
* Someone is actively using the parent pointer, so we can't
* be removed from the parent list.
*
* RC_NODE_CREATING_CHILD
* A child is being created -- locks out other creations, to
* prevent insert-insert races.
*
* RC_NODE_IN_TX
* This object is running a transaction.
*
* RC_NODE_DYING
* This node might be dying. Always set as a set, using
* RC_NODE_DYING_FLAGS (which is everything but
* RC_NODE_USING_PARENT)
*/
static int
{
}
return (0);
return (1);
}
static void
{
}
/*
* wait until a particular flag has cleared. Fails if the object dies.
*/
static int
{
}
/*
* On entry, np's lock must be held, and this thread must be holding
* RC_NODE_USING_PARENT. On return, both of them are released.
*
* If the return value is NULL, np either does not have a parent, or
* the parent has been marked DEAD.
*
* If the return value is non-NULL, it is the parent of np, and both
* its lock and the requested flags are held.
*/
static rc_node_t *
{
return (NULL);
}
return (NULL);
}
return (pp);
}
rc_node_alloc(void)
{
return (NULL);
return (np);
}
void
{
int i;
return; /* being handled elsewhere */
/* Release the holds from rc_iter_next(). */
for (i = 0; i < COMPOSITION_DEPTH; ++i) {
/* rn_cchain[i] may be NULL for empty snapshots. */
}
}
}
/*
* Link in a child node.
*
* Because of the lock ordering, cp has to already be in the hash table with
* its lock dropped before we get it. To prevent anyone from noticing that
* it is parentless, the creation code sets the RC_NODE_USING_PARENT. Once
* we've linked it in, we release the flag.
*/
static void
{
(void) rc_node_build_fmri(cp);
}
/*
* Sets the rn_parent_ref field of all the children of np to pp -- always
* initially invoked as rc_node_setup_parent_ref(np, np), we then recurse.
*
* This is used when we mark a node RC_NODE_OLD, so that when the object and
* its children are no longer referenced, they will all be deleted as a unit.
*/
static void
{
} else {
}
}
}
/*
* Atomically replace 'np' with 'newp', with a parent of 'pp'.
*
* Requirements:
* *no* node locks may be held.
* pp must be held with RC_NODE_CHILDREN_CHANGING
* newp and np must be held with RC_NODE_IN_TX
* np must be marked RC_NODE_IN_PARENT, newp must not be
* np must be marked RC_NODE_OLD
*
* Afterwards:
* pp's RC_NODE_CHILDREN_CHANGING is dropped
* newp and np's RC_NODE_IN_TX is dropped
* newp->rn_former = np;
* newp is RC_NODE_IN_PARENT, np is not.
* interested notify subscribers have been notified of newp's new status.
*/
static void
{
/*
* First, swap np and nnp in the cache. newp's RC_NODE_IN_TX flag
* keeps rc_node_update() from seeing it until we are done.
*/
/*
* replace np with newp in pp's list, and attach it to newp's rn_former
* link.
*/
/*
* Note that we carefully add newp before removing np -- this
* keeps iterators on the list from missing us.
*/
(void) rc_node_build_fmri(newp);
/*
* re-set np
*/
}
/*
* makes sure a node with lookup 'nip', name 'name', and parent 'pp' exists.
* 'cp' is used (and returned) if the node does not yet exist. If it does
* exist, 'cp' is freed, and the existent node is returned instead.
*/
{
bp = cache_hold(h);
/*
* make sure it matches our expectations
*/
}
return (np);
}
/*
* No one is there -- create a new node.
*/
#if COMPOSITION_DEPTH == 2
#else
#endif
}
return (np);
}
/*
* makes sure a snapshot with lookup 'nip', name 'name', and parent 'pp' exists.
* 'cp' is used (and returned) if the node does not yet exist. If it does
* exist, 'cp' is freed, and the existent node is returned instead.
*/
{
bp = cache_hold(h);
/*
* make sure it matches our expectations
*/
}
return (np);
}
/*
* No one is there -- create a new node.
*/
return (np);
}
/*
* makes sure a snaplevel with lookup 'nip' and parent 'pp' exists. 'cp' is
* used (and returned) if the node does not yet exist. If it does exist, 'cp'
* is freed, and the existent node is returned instead.
*/
{
bp = cache_hold(h);
/*
* make sure it matches our expectations
*/
}
return (np);
}
/*
* No one is there -- create a new node.
*/
/* Add this snaplevel to the snapshot's composition chain. */
return (np);
}
/*
* Returns NULL if strdup() fails.
*/
{
bp = cache_hold(h);
/*
* make sure it matches our expectations (don't check
* the generation number or parent, since someone could
* have gotten a transaction through while we weren't
* looking)
*/
}
return (np);
}
return (NULL);
}
return (NULL);
}
return (np);
}
#if COMPOSITION_DEPTH == 2
/*
* Initialize a "composed property group" which represents the composition of
* property groups pg1 & pg2. It is ephemeral: once created & returned for an
* ITER_READ request, keeping it out of cache_hash and any child lists
* prevents it from being looked up. Operations besides iteration are passed
* through to pg1.
*
* pg1 & pg2 should be held before entering this function. They will be
* released in rc_node_destroy().
*/
static int
{
return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
return (REP_PROTOCOL_FAIL_NO_RESOURCES);
return (REP_PROTOCOL_SUCCESS);
}
#else
#endif
/*
* Fails with _NO_RESOURCES.
*/
int
{
bp = cache_hold(h);
/*
* make sure it matches our expectations
*/
}
return (REP_PROTOCOL_SUCCESS);
}
/*
* No one is there -- create a new node.
*/
np = rc_node_alloc();
return (REP_PROTOCOL_FAIL_NO_RESOURCES);
}
return (REP_PROTOCOL_FAIL_NO_RESOURCES);
}
return (REP_PROTOCOL_SUCCESS);
}
/*
* This function implements a decision table to determine the event ID for
* changes to the enabled (SCF_PROPERTY_ENABLED) property. The event ID is
* determined by the value of the first property in the command specified
* by cmd_no and the name of the property group. Here is the decision
* table:
*
* Property Group Name
* Property ------------------------------------------
* Value SCF_PG_GENERAL SCF_PG_GENERAL_OVR
* -------- -------------- ------------------
* "0" ADT_smf_disable ADT_smf_tmp_disable
* "1" ADT_smf_enable ADT_smf_tmp_enable
*
* This function is called by special_property_event through a function
* pointer in the special_props_list array.
*
* Since the ADT_smf_* symbols may not be defined in the build machine's
* include files, this function is not compiled when doing native builds.
*/
#ifndef NATIVE_BUILD
static int
{
const char *value;
int enable;
/*
* First, check property value.
*/
return (-1);
if (nvalues == 0)
return (-1);
return (-1);
enable = 0;
enable = 1;
} else {
return (-1);
}
/*
* Now check property group name.
*/
return (0);
return (0);
}
return (-1);
}
#endif /* NATIVE_BUILD */
/*
* This function compares two audit_special_prop_item_t structures
* represented by item1 and item2. It returns an integer greater than 0 if
* item1 is greater than item2. It returns 0 if they are equal and an
* integer less than 0 if item1 is less than item2. api_prop_name and
* api_pg_name are the key fields for sorting.
*
* This function is suitable for calls to bsearch(3C) and qsort(3C).
*/
static int
{
int r;
if (r == 0) {
/*
* Primary keys are the same, so check the secondary key.
*/
}
return (r);
}
int
rc_node_init(void)
{
sizeof (rc_node_pg_notify_t),
sizeof (rc_notify_info_t),
uu_die("out of memory");
&rc_notify_list, 0);
&rc_notify_info_list, 0);
uu_die("out of memory");
/*
* Sort the special_props_list array so that it can be searched
* with bsearch(3C).
*
* The special_props_list array is not compiled into the native
* build code, so there is no need to call qsort if NATIVE_BUILD is
* defined.
*/
#ifndef NATIVE_BUILD
sizeof (special_props_list[0]), special_prop_compare);
#endif /* NATIVE_BUILD */
uu_die("out of memory");
return (1);
}
/*
* Fails with
* _INVALID_TYPE - type is invalid
* _TYPE_MISMATCH - np doesn't carry children of type type
* _DELETED - np has been deleted
* _NO_RESOURCES
*/
static int
{
int rc;
return (rc);
return (REP_PROTOCOL_FAIL_DELETED);
return (REP_PROTOCOL_SUCCESS);
}
if (rc == REP_PROTOCOL_SUCCESS) {
}
return (rc);
}
/*
* Returns
* _INVALID_TYPE - type is invalid
* _TYPE_MISMATCH - np doesn't carry children of type type
* _DELETED - np has been deleted
* _NO_RESOURCES
* _SUCCESS - if *cpp is not NULL, it is held
*/
static int
{
int ret;
if (ret != REP_PROTOCOL_SUCCESS)
return (ret);
break;
}
return (REP_PROTOCOL_SUCCESS);
}
/*
* Returns
* _INVALID_TYPE - type is invalid
* _DELETED - np or an ancestor has been deleted
* _NOT_FOUND - no ancestor of specified type exists
* _SUCCESS - *app is held
*/
static int
{
int ret;
if (type >= REP_PROTOCOL_ENTITY_MAX)
return (REP_PROTOCOL_FAIL_INVALID_TYPE);
if (ret != REP_PROTOCOL_SUCCESS)
return (ret);
}
return (REP_PROTOCOL_SUCCESS);
}
return (REP_PROTOCOL_FAIL_NOT_FOUND);
}
#ifndef NATIVE_BUILD
/*
* If the propname property exists in pg, and it is of type string, add its
* values as authorizations to pcp. pg must not be locked on entry, and it is
* returned unlocked. Returns
* _DELETED - pg was deleted
* _NO_RESOURCES
* _NOT_FOUND - pg has no property named propname
* _SUCCESS
*/
static int
{
int result;
const char *cp;
if (result != REP_PROTOCOL_SUCCESS) {
switch (result) {
return (result);
default:
}
}
return (REP_PROTOCOL_FAIL_NOT_FOUND);
/* rn_valtype is immutable, so no locking. */
return (REP_PROTOCOL_SUCCESS);
}
count > 0;
--count) {
if (result != REP_PROTOCOL_SUCCESS)
break;
}
return (result);
}
/*
* Assuming that ent is a service or instance node, if the pgname property
* group has type pgtype, and it has a propname property with string type, add
* its values as authorizations to pcp. If pgtype is NULL, it is not checked.
* Returns
* _SUCCESS
* _DELETED - ent was deleted
* _NO_RESOURCES - no resources
* _NOT_FOUND - ent does not have pgname pg or propname property
*/
static int
{
int r;
switch (r) {
case REP_PROTOCOL_SUCCESS:
break;
return (r);
default:
bad_error("rc_node_find_named_child", r);
}
return (REP_PROTOCOL_FAIL_NOT_FOUND);
switch (r) {
break;
case REP_PROTOCOL_SUCCESS:
break;
default:
bad_error("perm_add_pg_prop_values", r);
}
}
return (r);
}
/*
* If pg has a property named propname, and is string typed, add its values as
* authorizations to pcp. If pg has no such property, and its parent is an
* instance, walk up to the service and try doing the same with the property
* of the same name from the property group of the same name. Returns
* _SUCCESS
* _NO_RESOURCES
* _DELETED - pg (or an ancestor) was deleted
*/
static int
{
int r;
if (r != REP_PROTOCOL_FAIL_NOT_FOUND)
return (r);
return (REP_PROTOCOL_SUCCESS);
/*
* If pg is a child of an instance or snapshot, we want to compose the
* authorization property with the service's (if it exists). The
* snapshot case applies only to read_authorization. In all other
* cases, the pg's parent will be the instance.
*/
if (r != REP_PROTOCOL_SUCCESS) {
assert(r == REP_PROTOCOL_FAIL_DELETED);
return (r);
}
if (r == REP_PROTOCOL_FAIL_NOT_FOUND)
r = REP_PROTOCOL_SUCCESS;
return (r);
}
/*
* Call perm_add_enabling_values() for the "action_authorization" property of
* the "general" property group of inst. Returns
* _DELETED - inst (or an ancestor) was deleted
* _NO_RESOURCES
* _SUCCESS
*/
static int
{
int r;
if (r != REP_PROTOCOL_FAIL_NOT_FOUND)
return (r);
if (r != REP_PROTOCOL_SUCCESS) {
assert(r == REP_PROTOCOL_FAIL_DELETED);
return (r);
}
return (r == REP_PROTOCOL_FAIL_NOT_FOUND ? REP_PROTOCOL_SUCCESS : r);
}
#endif /* NATIVE_BUILD */
void
{
out->rnp_deleted = 0;
}
void
{
}
}
static void
{
out->rnp_deleted = 0;
}
void
{
}
void
{
}
/*
* rc_node_check()/RC_NODE_CHECK()
* generic "entry" checks, run before the use of an rc_node pointer.
*
* Fails with
* _NOT_SET
* _DELETED
*/
static int
{
int result = REP_PROTOCOL_SUCCESS;
return (REP_PROTOCOL_FAIL_NOT_SET);
}
return (result);
}
/*
* Fails with
* _NOT_SET - ptr is reset
* _DELETED - node has been deleted
*/
static rc_node_t *
{
if (npp->rnp_deleted)
else
return (NULL);
}
return (NULL);
}
return (np);
}
#define RC_NODE_CHECK_AND_LOCK(n) { \
int rc__res; \
return (rc__res); \
}
#define RC_NODE_CHECK(n) { \
RC_NODE_CHECK_AND_LOCK(n); \
(void) pthread_mutex_unlock(&(n)->rn_lock); \
}
#define RC_NODE_CHECK_AND_HOLD(n) { \
RC_NODE_CHECK_AND_LOCK(n); \
rc_node_hold_locked(n); \
(void) pthread_mutex_unlock(&(n)->rn_lock); \
}
int rc__res; \
return (rc__res); \
}
}
}
return (REP_PROTOCOL_FAIL_DELETED); \
} \
}
return (REP_PROTOCOL_FAIL_DELETED); \
} \
}
return (REP_PROTOCOL_FAIL_DELETED); \
} \
}
int
{
if (type != REP_PROTOCOL_ENTITY_SCOPE) {
rc_node_clear(out, 0);
return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
}
/*
* the main scope never gets destroyed
*/
return (REP_PROTOCOL_SUCCESS);
}
/*
* Fails with
* _NOT_SET - npp is not set
* _DELETED - the node npp pointed at has been deleted
* _TYPE_MISMATCH - type is not _SCOPE
* _NOT_FOUND - scope has no parent
*/
static int
{
rc_node_clear(out, 0);
if (type != REP_PROTOCOL_ENTITY_SCOPE)
return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
return (REP_PROTOCOL_FAIL_NOT_FOUND);
}
static int rc_node_pg_check_read_protect(rc_node_t *);
/*
* Fails with
* _NOT_SET
* _DELETED
* _NOT_APPLICABLE
* _NOT_FOUND
* _BAD_REQUEST
* _TRUNCATED
* _NO_RESOURCES
*/
int
{
}
switch (answertype) {
case RP_ENTITY_NAME_NAME:
return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
break;
case RP_ENTITY_NAME_PGTYPE:
return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
break;
case RP_ENTITY_NAME_PGFLAGS:
return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
break;
return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
break;
return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
break;
return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
return (REP_PROTOCOL_FAIL_NOT_FOUND);
break;
{
int ret;
return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
switch (ret) {
break;
case REP_PROTOCOL_SUCCESS:
break;
default:
return (ret);
}
break;
}
default:
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
}
return (REP_PROTOCOL_FAIL_TRUNCATED);
return (REP_PROTOCOL_SUCCESS);
}
int
{
return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
return (REP_PROTOCOL_SUCCESS);
}
/*
* Get np's parent. If np is deleted, returns _DELETED. Otherwise puts a hold
* on the parent, returns a pointer to it in *out, and returns _SUCCESS.
*/
static int
{
} else {
}
for (;;) {
if (!rc_node_wait_flag(np,
return (REP_PROTOCOL_FAIL_DELETED);
}
break;
goto deleted;
}
/* guaranteed to succeed without dropping the lock */
return (REP_PROTOCOL_FAIL_DELETED);
}
return (REP_PROTOCOL_SUCCESS);
return (REP_PROTOCOL_FAIL_DELETED);
}
/*
* Fails with
* _NOT_SET
* _DELETED
*/
static int
{
}
/*
* Fails with
* _NOT_SET - npp is not set
* _DELETED - the node npp pointed at has been deleted
* _TYPE_MISMATCH - npp's node's parent is not of type type
*
* If npp points to a scope, can also fail with
* _NOT_FOUND - scope has no parent
*/
int
{
int rc;
rc_node_clear(out, 0);
return (rc);
}
return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
}
return (REP_PROTOCOL_SUCCESS);
}
int
{
int rc;
return (REP_PROTOCOL_SUCCESS);
}
return (rc);
return (REP_PROTOCOL_SUCCESS);
}
/*
* Fails with
* _INVALID_TYPE - type is invalid
* _TYPE_MISMATCH - np doesn't carry children of type type
* _DELETED - np has been deleted
* _NO_RESOURCES
* _BACKEND_ACCESS
*/
int
{
} else {
break;
&child);
/*
* loop only if we succeeded, but no child of
* the correct name was found.
*/
if (ret != REP_PROTOCOL_SUCCESS ||
break;
}
}
}
if (ret == REP_PROTOCOL_SUCCESS) {
else
} else {
}
return (ret);
}
int
{
/*
* If we're updating a composed property group, actually
* update the top-level property group & return the
* appropriate value. But leave *nnp pointing at us.
*/
}
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
for (;;) {
return (REP_PROTOCOL_FAIL_DELETED);
}
/*
* grab the lock before dropping the cache bucket, so
* that no one else can sneak in
*/
break;
}
/*
* If it is dead, we want to update it so that it will continue to
* report being dead.
*/
return (REP_PROTOCOL_FAIL_DELETED);
}
}
/*
* does a generic modification check, for creation, deletion, and snapshot
* management only. Property group transactions have different checks.
*
* The string returned to *match_auth must be freed.
*/
int
{
int rc = REP_PROTOCOL_SUCCESS;
int granted;
*match_auth = NULL;
#ifdef NATIVE_BUILD
if (!client_is_privileged()) {
}
return (rc);
#else
if (is_main_repository == 0)
return (REP_PROTOCOL_SUCCESS);
if (rc == REP_PROTOCOL_SUCCESS) {
if (granted < 0) {
} else {
/*
* Copy off the authorization
* string before freeing pcp.
*/
*match_auth =
if (*match_auth == NULL)
}
}
} else {
}
return (rc);
#endif /* NATIVE_BUILD */
}
/*
* Native builds are done to create svc.configd-native. This program runs
* only on the Solaris build machines to create the seed repository, and it
* is compiled against the build machine's header files. The ADT_smf_*
* symbols may not be defined in these header files. For this reason
* smf_annotation_event(), _smf_audit_event() and special_property_event()
* are not compiled for native builds.
*/
#ifndef NATIVE_BUILD
/*
* This function generates an annotation audit event if one has been setup.
* Annotation events should only be generated immediately before the audit
* record from the first attempt to modify the repository from a client
* which has requested an annotation.
*/
static void
{
char file[MAXPATHLEN];
char operation[REP_PROTOCOL_NAME_LEN];
/* Don't audit if we're using an alternate repository. */
if (is_main_repository == 0)
return;
sizeof (file)) == 0) {
return;
}
if (file[0] == 0) {
}
if (operation[0] == 0) {
sizeof (operation));
}
return;
uu_warn("smf_annotation_event cannot allocate event "
return;
}
} else {
uu_warn("smf_annotation_event failed to put event. "
}
}
/*
* _smf_audit_event interacts with the security auditing system to generate
* an audit event structure. It establishes an audit session and allocates
* an audit event. The event is filled in from the audit data, and
* adt_put_event is called to generate the event.
*/
static void
{
char *auth_used;
char *fmri;
char *prop_value;
/* Don't audit if we're using an alternate repository */
if (is_main_repository == 0)
return;
return;
uu_warn("_smf_audit_event cannot allocate event "
return;
}
/*
* Handle possibility of NULL authorization strings, FMRIs and
* property values.
*/
auth_used = "PRIVILEGED";
} else {
}
"empty FMRI string");
fmri = "UNKNOWN FMRI";
} else {
}
prop_value = "";
} else {
}
/* Fill in the event data. */
switch (event_id) {
case ADT_smf_attach_snap:
break;
case ADT_smf_change_prop:
break;
case ADT_smf_clear:
break;
case ADT_smf_create:
break;
case ADT_smf_create_npg:
break;
case ADT_smf_create_pg:
break;
case ADT_smf_create_prop:
break;
case ADT_smf_create_snap:
break;
case ADT_smf_degrade:
break;
case ADT_smf_delete:
break;
case ADT_smf_delete_npg:
break;
case ADT_smf_delete_pg:
break;
case ADT_smf_delete_prop:
break;
case ADT_smf_delete_snap:
break;
case ADT_smf_disable:
break;
case ADT_smf_enable:
break;
break;
break;
break;
case ADT_smf_maintenance:
break;
case ADT_smf_milestone:
break;
case ADT_smf_read_prop:
break;
case ADT_smf_refresh:
break;
case ADT_smf_restart:
break;
case ADT_smf_tmp_disable:
break;
case ADT_smf_tmp_enable:
break;
case ADT_smf_tmp_maintenance:
break;
default:
abort(); /* Need to cover all SMF event IDs */
}
uu_warn("_smf_audit_event failed to put event. %s\n",
}
}
/*
* Determine if the combination of the property group at pg_name and the
* property at prop_name are in the set of special startd properties. If
* they are, a special audit event will be generated.
*/
static void
{
/* Use bsearch to find the special property information. */
sizeof (special_props_list[0]), special_prop_compare);
/* Not a special property. */
return;
}
/* Get the event id */
} else {
return;
}
/* Generate the event. */
}
#endif /* NATIVE_BUILD */
/*
* Return a pointer to a string containing all the values of the command
* specified by cmd_no with each value enclosed in quotes. It is up to the
* caller to free the memory at the returned pointer.
*/
static char *
{
const char *cp;
const char *cur_value;
size_t byte_count = 0;
uint32_t i;
char *vp;
return (NULL);
/*
* First determine the size of the buffer that we will need. We
* will represent each property value surrounded by quotes with a
* space separating the values. Thus, we need to find the total
* size of all the value strings and add 3 for each value.
*
* There is one catch, though. We need to escape any internal
* quote marks in the values. So for each quote in the value we
* need to add another byte to the buffer size.
*/
for (i = 0; i < nvalues; i++) {
return (NULL);
}
}
byte_count++; /* nul terminator */
return (NULL);
*values = 0;
/* Now build up the string of values. */
for (i = 0; i < nvalues; i++) {
return (NULL);
}
if (*cp == '"') {
*vp++ = '\\';
*vp++ = '"';
} else {
}
}
*vp = 0;
}
if (str_size > 0)
return (values);
}
/*
* generate_property_events takes the transaction commit data at tx_data
* and generates an audit event for each command.
*
* Native builds are done to create svc.configd-native. This program runs
* only on the Solaris build machines to create the seed repository. Thus,
* no audit events should be generated when running svc.configd-native.
*/
static void
char *pg_fmri, /* FMRI of property group */
char *auth_string,
int auth_status,
int auth_ret_value)
{
#ifndef NATIVE_BUILD
char *cp;
char fmri[REP_PROTOCOL_FMRI_LEN];
char pg_name[REP_PROTOCOL_NAME_LEN];
char *pg_end; /* End of prop. group fmri */
const char *prop_name;
char prop_type[3];
enum rep_protocol_responseid rc;
/* Make sure we have something to do. */
return;
return;
/* Copy the property group fmri */
/*
* Get the property group name. It is the first component after
* the last occurance of SCF_FMRI_PROPERTYGRP_PREFIX in the fmri.
*/
pg_name[0] = 0;
} else {
}
/*
* Property type is two characters (see
* rep_protocol_value_type_t), so terminate the string.
*/
prop_type[2] = 0;
/* Construct FMRI of the property */
*pg_end = 0;
continue;
}
if (rc != REP_PROTOCOL_SUCCESS) {
/*
* If we can't get the FMRI, we'll abandon this
* command
*/
continue;
}
/* Generate special property event if necessary. */
/* Capture rest of audit data. */
continue;
}
/* Determine the event type. */
continue;
}
switch (action) {
break;
break;
break;
break;
default:
assert(0); /* Missing a case */
continue;
}
/* Generate the event. */
&audit_data);
}
#endif /* NATIVE_BUILD */
}
/*
* Fails with
* _DELETED - node has been deleted
* _NOT_SET - npp is reset
* _NOT_APPLICABLE - type is _PROPERTYGRP
* _INVALID_TYPE - node is corrupt or type is invalid
* _TYPE_MISMATCH - node cannot have children of type type
* _BAD_REQUEST - name is invalid
* cannot create children for this type of node
* _NO_RESOURCES - out of memory, or could not allocate new id
* _PERMISSION_DENIED
* _BACKEND_ACCESS
* _BACKEND_READONLY
* _EXISTS - child already exists
* _TRUNCATED - truncated FMRI for the audit record
*/
int
{
char fmri[REP_PROTOCOL_FMRI_LEN];
rc_node_clear(cpp, 0);
/*
* there is a separate interface for creating property groups
*/
if (type == REP_PROTOCOL_ENTITY_PROPERTYGRP) {
return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
}
}
return (rc);
}
return (rc);
}
return (rc);
}
if (perm_rc != REP_PROTOCOL_SUCCESS) {
return (perm_rc);
}
if (rc == REP_PROTOCOL_SUCCESS) {
}
if (rc == REP_PROTOCOL_SUCCESS) {
&audit_data);
}
return (rc);
}
int
{
int rc;
int granted;
char fmri[REP_PROTOCOL_FMRI_LEN];
rc_node_clear(cpp, 0);
/* verify flags is valid */
if (flags & ~SCF_PG_FLAG_NONPERSISTENT)
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
if (type != REP_PROTOCOL_ENTITY_PROPERTYGRP) {
return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
}
return (rc);
}
return (rc);
}
#ifdef NATIVE_BUILD
if (!client_is_privileged()) {
}
#else
if (flags & SCF_PG_FLAG_NONPERSISTENT) {
} else {
}
return (rc);
}
if (is_main_repository) {
/* Must have .smf.modify or smf.modify.<type> authorization */
if (rc == REP_PROTOCOL_SUCCESS) {
const char * const auth =
}
/*
* .manage or $action_authorization can be used to
* create the actions pg and the general_ovr pg.
*/
if (rc == REP_PROTOCOL_SUCCESS &&
(flags & SCF_PG_FLAG_NONPERSISTENT) != 0 &&
if (rc == REP_PROTOCOL_SUCCESS)
}
if (rc == REP_PROTOCOL_SUCCESS) {
if (granted < 0) {
} else {
/*
* Copy out the authorization
* string before freeing pcp.
*/
/*
* Following code line
* cannot meet both the
* indentation and the line
* length requirements of
* cstyle. Indendation has
* been sacrificed.
*/
/* CSTYLED */
}
}
}
} else {
}
} else {
}
#endif /* NATIVE_BUILD */
if (rc != REP_PROTOCOL_SUCCESS) {
return (rc);
}
if (rc == REP_PROTOCOL_SUCCESS) {
}
if (rc == REP_PROTOCOL_SUCCESS) {
&audit_data);
}
return (rc);
}
static void
{
} else {
}
}
static void
{
goto cleanup;
}
break;
break;
break;
default:
goto cleanup;
}
goto cleanup;
}
for (;;) {
} else
break;
}
}
/*
* N.B.: this function drops np->rn_lock on the way out.
*/
static void
{
/*
* already marked as dead -- can't happen, since that
* would require setting RC_NODE_CHILDREN_CHANGING
* in np, and we're holding that...
*/
abort();
}
}
abort(); /* can't happen, see above */
goto again; /* tail-recurse down rn_former */
}
}
/*
* N.B.: this function drops np->rn_lock on the way out.
*/
static void
{
}
goto again; /* tail-recurse down rn_former */
}
}
static void
{
abort(); /* can't happen, see above */
}
}
/*
* If this node is not out-dated, we need to remove it from
* the notify list and cache hash table.
*/
(void) pthread_mutex_lock(&rc_pg_notify_lock);
(void) pthread_mutex_unlock(&rc_pg_notify_lock);
}
}
/*
* N.B.: this function drops np->rn_lock and a reference on the way out.
*/
static void
{
}
/*
* when we drop cp's lock, all the children will be gone, so we
* can release DYING_FLAGS.
*/
goto again; /* tail-recurse down rn_former */
}
}
static void
{
int unrefed;
return;
}
return;
}
/*
* find the current in-hash object, and grab it's RC_NODE_IN_TX
* flag. That protects the entire rn_former chain.
*/
for (;;) {
goto died;
/*
* We are trying to unreference this node, but the
* owner of the former list does not exist. It must
* be the case that another thread is deleting this
* entire sub-branch, but has not yet reached us.
* We will in short order be deleted.
*/
return;
}
/*
* no longer unreferenced
*/
return;
}
continue;
}
break;
}
}
np->rn_other_refs_held != 0) {
return;
}
goto died;
}
rc_node_delete_hold(np, 0);
np->rn_other_refs_held != 0) {
rc_node_delete_rele(np, 0);
return;
}
/*
* It's gone -- remove it from the former chain and destroy it.
*/
;
return;
died:
np->rn_other_refs_held == 0);
if (unrefed)
}
static au_event_t
{
au_event_t id = 0;
#ifndef NATIVE_BUILD
switch (entity) {
id = ADT_smf_delete;
break;
break;
if (pgflags & SCF_PG_FLAG_NONPERSISTENT) {
} else {
}
break;
default:
abort();
}
#endif /* NATIVE_BUILD */
return (id);
}
/*
* Fails with
* _NOT_SET
* _DELETED
* _BAD_REQUEST
* _PERMISSION_DENIED
* _NO_RESOURCES
* _TRUNCATED
* and whatever object_delete() fails with.
*/
int
{
int rc;
int granted;
au_event_t event_id = 0;
int audit_failure = 0;
np->rn_pgflags);
break;
np->rn_pgflags);
break;
np->rn_pgflags);
return (REP_PROTOCOL_FAIL_NO_RESOURCES);
}
break; /* deletable */
/* Scopes and snaplevels are indelible. */
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
np->rn_pgflags);
break;
event_id =
np->rn_pgflags);
return (REP_PROTOCOL_FAIL_NO_RESOURCES);
}
break;
}
/* Snapshot property groups are indelible. */
return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
default:
assert(0);
abort();
break;
}
goto cleanout;
}
/*
* The following loop is to deal with the fact that snapshots and
* property groups are moving targets -- changes to them result
* in a new "child" node. Since we can only delete from the top node,
* we have to loop until we have a non-RC_NODE_OLD version.
*/
for (;;) {
if (!rc_node_wait_flag(np,
goto cleanout;
}
goto fail;
}
continue;
}
}
/*
* Mark our parent as children changing. this call drops our
* lock and the RC_NODE_USING_PARENT flag, and returns with
* pp's lock held
*/
/* our parent is gone, we're going next... */
goto cleanout;
}
break; /* not old -- we're done */
continue; /* loop around and try again */
}
/*
* Everyone out of the pool -- we grab everything but
* RC_NODE_USING_PARENT (including RC_NODE_DYING) to keep
* any changes from occurring while we are attempting to
* delete the node.
*/
goto fail;
}
goto fail;
}
#ifdef NATIVE_BUILD
if (!client_is_privileged()) {
}
#else
if (is_main_repository) {
/* permission check */
/* add .smf.modify.<type> for pgs. */
const char * const auth =
}
if (rc == REP_PROTOCOL_SUCCESS) {
if (granted < 0) {
} else {
/*
* Copy out the authorization
* string before freeing pcp.
*/
/*
* Following code line
* cannot meet both the
* indentation and the line
* length requirements of
* cstyle. Indendation has
* been sacrificed.
*/
/* CSTYLED */
}
}
}
} else {
}
audit_failure = 1;
}
} else {
}
#endif /* NATIVE_BUILD */
if (rc != REP_PROTOCOL_SUCCESS) {
goto fail;
}
goto fail;
}
if (rc != REP_PROTOCOL_SUCCESS) {
goto fail;
}
/*
* Now, delicately unlink and delete the object.
*
* Create the delete notification, atomically remove
* from the hash table and set the NODE_DEAD flag, and
* remove from the parent's children list.
*/
}
/*
* finally, propagate death to our children, handle notifications,
* and release our hold.
*/
(void) pthread_mutex_lock(&rc_pg_notify_lock);
(void) pthread_mutex_unlock(&rc_pg_notify_lock);
&audit_data);
return (rc);
fail:
if (rc == REP_PROTOCOL_FAIL_DELETED)
}
if (audit_failure) {
}
return (rc);
}
int
{
int res;
rc_node_clear(cpp, 0);
return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
}
return (res);
}
continue;
break;
}
} else {
/*
* mark our parent as children changing. This call drops our
* lock and the RC_NODE_USING_PARENT flag, and returns with
* pp's lock held
*/
/* our parent is gone, we're going next... */
return (REP_PROTOCOL_FAIL_DELETED);
}
/*
* find the next snaplevel
*/
;
/* it must match the snaplevel list */
cp->rn_snaplevel));
}
return (REP_PROTOCOL_SUCCESS);
}
return (REP_PROTOCOL_FAIL_NOT_FOUND);
}
/*
* This call takes a snapshot (np) and either:
* an existing snapid (to be associated with np), or
* a non-NULL parentp (from which a new snapshot is taken, and associated
* with np)
*
* To do the association, np is duplicated, the duplicate is made to
* represent the new snapid, and np is replaced with the new rc_node_t on
* np's parent's child list. np is placed on the new node's rn_former list,
* and replaces np in cache_hash (so rc_node_update() will find the new one).
*
* old_fmri and old_name point to the original snap shot's FMRI and name.
* These values are used when generating audit events.
*
* Fails with
* _BAD_REQUEST
* _BACKEND_READONLY
* _DELETED
* _NO_RESOURCES
* _TRUNCATED
* _TYPE_MISMATCH
*/
static int
char *old_fmri,
char *old_name)
{
int rc;
} else {
}
/* Gather the audit data. */
/*
* files on the build machine. Thus, the following if-else will
* not be compiled when doing native builds.
*/
#ifndef NATIVE_BUILD
} else {
}
#endif /* NATIVE_BUILD */
return (REP_PROTOCOL_FAIL_NO_RESOURCES);
}
abort();
}
/*
* In the attach case, get the instance FMRIs of the
* snapshots.
*/
return (rc);
}
} else {
/*
* Capture the FMRI of the parent if we're actually going
* to take the snapshot.
*/
return (rc);
}
}
&audit_data);
goto cleanout;
}
/*
* get the latest node, holding RC_NODE_IN_TX to keep the rn_former
* list from changing.
*/
for (;;) {
goto again;
}
goto again;
}
goto again;
}
/*
* Can't happen, since we're holding our
* parent's CHILDREN_CHANGING flag...
*/
abort();
}
break; /* everything's ready */
}
goto cleanout;
}
}
goto fail;
}
} else {
/*
* look for a former node with the snapid we need.
*/
goto cleanout;
}
break; /* existing node with that id */
}
}
}
nnp = rc_node_alloc();
goto fail;
}
goto fail;
}
}
else
if (rc != REP_PROTOCOL_SUCCESS)
goto fail;
/*
* fix up the former chain
*/
}
/*
* replace np with nnp
*/
return (rc);
fail:
else
}
return (rc);
}
int
{
char fmri[REP_PROTOCOL_FMRI_LEN];
rc_node_clear(outpp, 0);
return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
}
if (rc != REP_PROTOCOL_SUCCESS) {
return (rc);
}
return (rc);
}
return (rc);
}
&sz_out)) != REP_PROTOCOL_SUCCESS) {
return (rc);
}
if (perm_rc != REP_PROTOCOL_SUCCESS) {
return (perm_rc);
}
if (rc == REP_PROTOCOL_SUCCESS) {
}
if (rc == REP_PROTOCOL_SUCCESS) {
&audit_data);
}
return (rc);
}
int
{
return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
}
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
}
NULL)); /* drops outp's lock */
}
int
{
char old_name[REP_PROTOCOL_NAME_LEN];
int rc;
char old_fmri[REP_PROTOCOL_FMRI_LEN];
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
}
&sz_out);
if (rc != REP_PROTOCOL_SUCCESS)
return (rc);
sizeof (old_name)) {
return (REP_PROTOCOL_FAIL_TRUNCATED);
}
}
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
}
return (rc);
}
/*
* If the pgname property group under ent has type pgtype, and it has a
* propname property with type ptype, return _SUCCESS. If pgtype is NULL,
* it is not checked. If ent is not a service node, we will return _SUCCESS if
* a property meeting the requirements exists in either the instance or its
* parent.
*
* Returns
* _SUCCESS - see above
* _DELETED - ent or one of its ancestors was deleted
* _NO_RESOURCES - no resources
* _NOT_FOUND - no matching property was found
*/
static int
{
int ret;
switch (ret) {
case REP_PROTOCOL_SUCCESS:
break;
return (ret);
default:
}
&svc);
if (ret != REP_PROTOCOL_SUCCESS) {
return (ret);
}
switch (ret) {
case REP_PROTOCOL_SUCCESS:
break;
return (ret);
default:
}
}
}
}
return (REP_PROTOCOL_FAIL_NOT_FOUND);
}
/*
* At this point, pg is non-NULL, and is a property group node of the
* correct type. spg, if non-NULL, is also a property group node of
* the correct type. Check for the property in pg first, then spg
* (if applicable).
*/
switch (ret) {
case REP_PROTOCOL_SUCCESS:
return (REP_PROTOCOL_SUCCESS);
}
}
break;
return (ret);
break;
default:
}
return (REP_PROTOCOL_FAIL_NOT_FOUND);
switch (ret) {
case REP_PROTOCOL_SUCCESS:
return (REP_PROTOCOL_SUCCESS);
}
}
return (REP_PROTOCOL_FAIL_NOT_FOUND);
return (ret);
return (REP_PROTOCOL_FAIL_NOT_FOUND);
default:
}
return (REP_PROTOCOL_SUCCESS);
}
/*
* Given a property group node, returns _SUCCESS if the property group may
* be read without any special authorization.
*
* Fails with:
* _DELETED - np or an ancestor node was deleted
* _TYPE_MISMATCH - np does not refer to a property group
* _NO_RESOURCES - no resources
* _PERMISSION_DENIED - authorization is required
*/
static int
{
int ret;
return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
return (REP_PROTOCOL_SUCCESS);
if (ret != REP_PROTOCOL_SUCCESS)
return (ret);
switch (ret) {
return (REP_PROTOCOL_SUCCESS);
case REP_PROTOCOL_SUCCESS:
return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
return (ret);
default:
}
return (REP_PROTOCOL_SUCCESS);
}
/*
* Fails with
* _DELETED - np's node or parent has been deleted
* _TYPE_MISMATCH - np's node is not a property
* _NO_RESOURCES - out of memory
* _PERMISSION_DENIED - no authorization to read this property's value(s)
* _BAD_REQUEST - np's parent is not a property group
*/
static int
{
return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
if (client_is_privileged())
return (REP_PROTOCOL_SUCCESS);
#ifdef NATIVE_BUILD
return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
#else
if (ret != REP_PROTOCOL_SUCCESS)
return (ret);
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
}
if (ret != REP_PROTOCOL_FAIL_PERMISSION_DENIED) {
return (ret);
}
return (REP_PROTOCOL_FAIL_NO_RESOURCES);
}
if (ret == REP_PROTOCOL_SUCCESS) {
const char * const auth =
}
/*
* If you are permitted to modify the value, you may also
* read it. This means that both the MODIFY and VALUE
* authorizations are acceptable. We don't allow requests
* for AUTH_PROP_MODIFY if all you have is $AUTH_PROP_VALUE,
* however, to avoid leaking possibly valuable information
* since such a user can't change the property anyway.
*/
if (ret == REP_PROTOCOL_SUCCESS)
if (ret == REP_PROTOCOL_SUCCESS &&
if (ret == REP_PROTOCOL_SUCCESS)
if (ret == REP_PROTOCOL_SUCCESS) {
if (granted < 0)
}
if (ret == REP_PROTOCOL_SUCCESS) {
/* Generate a read_prop audit event. */
}
if (ret == REP_PROTOCOL_SUCCESS) {
int status;
int ret_value;
if (granted == 0) {
} else {
}
}
return (ret);
#endif /* NATIVE_BUILD */
}
/*
* Iteration
*/
static int
{
const char *name = s;
}
static int
{
const char *type = s;
}
/*ARGSUSED*/
static int
{
return (1);
}
/*
* Allocate & initialize an rc_node_iter_t structure. Essentially, ensure
* np->rn_children is populated and call uu_list_walk_start(np->rn_children).
* If successful, leaves a hold on np & increments np->rn_other_refs
*
* If composed is true, then set up for iteration across the top level of np's
* composition chain. If successful, leaves a hold on np and increments
* rn_other_refs for the top level of np's composition chain.
*
* Fails with
* _NO_RESOURCES
* _INVALID_TYPE
* _TYPE_MISMATCH - np cannot carry type children
* _DELETED
*/
static int
{
int res;
return (REP_PROTOCOL_FAIL_NO_RESOURCES);
/* np is held by the client's rc_node_ptr_t */
composed = 1;
if (!composed) {
return (res);
}
} else {
return (REP_PROTOCOL_FAIL_NO_RESOURCES);
}
} else {
/* rn_cchain isn't valid until children are loaded. */
if (res != REP_PROTOCOL_SUCCESS) {
return (res);
}
/* Check for an empty snapshot. */
goto empty;
}
/* Start at the top of the composition chain. */
/* Empty composition chain. */
/* It's ok, iter_next() will return _DONE. */
goto out;
}
break;
/* Someone deleted it, so try the next one. */
}
if (res == REP_PROTOCOL_SUCCESS) {
else {
}
}
if (res != REP_PROTOCOL_SUCCESS) {
return (res);
}
}
out:
return (REP_PROTOCOL_SUCCESS);
}
static void
{
if (iter->rni_clevel >= 0)
}
/*
* Fails with
* _NOT_SET - npp is reset
* _DELETED - npp's node has been deleted
* _NOT_APPLICABLE - npp's node is not a property
* _NO_RESOURCES - out of memory
*/
static int
{
return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
}
return (REP_PROTOCOL_FAIL_NO_RESOURCES);
}
nip->rni_offset = 0;
nip->rni_last_offset = 0;
return (REP_PROTOCOL_SUCCESS);
}
/*
* Returns:
* _NO_RESOURCES - out of memory
* _NOT_SET - npp is reset
* _DELETED - npp's node has been deleted
* _TYPE_MISMATCH - npp's node is not a property
* _NOT_FOUND - property has no values
* _TRUNCATED - property has >1 values (first is written into out)
* _SUCCESS - property has 1 value (which is written into out)
* _PERMISSION_DENIED - no authorization to read property value(s)
*
* We shorten *sz_out to not include anything after the final '\0'.
*/
int
{
size_t w;
int ret;
if (ret != REP_PROTOCOL_SUCCESS)
return (ret);
return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
}
if (np->rn_values_size == 0) {
return (REP_PROTOCOL_FAIL_NOT_FOUND);
}
backend_panic("value too large");
rpr_value[w + 1]);
return (ret);
}
int
{
const char *vals;
size_t w;
int ret;
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
if (ret != REP_PROTOCOL_SUCCESS)
return (ret);
} else {
backend_panic("value too large");
rpr_value[w + 1]);
/*
* update the offsets if we're not repeating
*/
if (!repeat) {
}
}
return (result);
}
/*
* Entry point for ITER_START from client.c. Validate the arguments & call
* rc_iter_create().
*
* Fails with
* _NOT_SET
* _DELETED
* _TYPE_MISMATCH - np cannot carry type children
* _BAD_REQUEST - flags is invalid
* pattern is invalid
* _NO_RESOURCES
* _INVALID_TYPE
* _TYPE_MISMATCH - *npp cannot have children of type
* _BACKEND_ACCESS
*/
int
{
rc_iter_filter_func *f = NULL;
int rc;
if (type == REP_PROTOCOL_ENTITY_VALUE) {
return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
return (rc);
}
return (rc);
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
/* Composition only works for instances & snapshots. */
if ((flags & RP_ITER_START_COMPOSED) &&
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
return (rc);
return (REP_PROTOCOL_FAIL_NO_RESOURCES);
}
switch (flags & RP_ITER_START_FILT_MASK) {
case RP_ITER_START_ALL:
f = NULL;
break;
case RP_ITER_START_EXACT:
f = rc_iter_filter_name;
break;
case RP_ITER_START_PGTYPE:
if (type != REP_PROTOCOL_ENTITY_PROPERTYGRP) {
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
}
f = rc_iter_filter_type;
break;
default:
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
}
return (rc);
}
/*
* Do uu_list_walk_next(iter->rni_iter) until we find a child which matches
* the filter.
* For composed iterators, then check to see if there's an overlapping entity
* (see embedded comments). If we reach the end of the list, start over at
* the next level.
*
* Returns
* _BAD_REQUEST - iter walks values
* _TYPE_MISMATCH - iter does not walk type entities
* _DELETED - parent was deleted
* _NO_RESOURCES
* _INVALID_TYPE - type is invalid
* _DONE
* _SUCCESS
*
* For composed property group iterators, can also return
* _TYPE_MISMATCH - parent cannot have type children
*/
int
{
int rc;
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
rc_node_clear(out, 0);
return (REP_PROTOCOL_DONE);
}
rc_node_clear(out, 0);
return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
}
return (REP_PROTOCOL_FAIL_DELETED);
}
if (iter->rni_clevel >= 0) {
/* Composed iterator. Iterate over appropriate level. */
/*
* If iter->rni_parent is an instance or a snapshot, np must
* be valid since iter holds iter->rni_parent & possible
* levels (service, instance, snaplevel) cannot be destroyed
* while rni_parent is held. If iter->rni_parent is
* a composed property group then rc_node_setup_cpg() put
* a hold on np.
*/
return (REP_PROTOCOL_FAIL_DELETED);
}
}
for (;;) {
#if COMPOSITION_DEPTH == 2
/* release walker and lock */
break;
}
/* Stop walking current level. */
/* Start walking next level. */
++iter->rni_clevel;
#else
#endif
if (rc == REP_PROTOCOL_SUCCESS) {
else {
}
}
if (rc != REP_PROTOCOL_SUCCESS) {
rc_node_clear(out, 0);
return (rc);
}
continue;
}
continue;
/*
* If we're composed and not at the top level, check to see if
* there's an entity at a higher level with the same name. If
* so, skip this one.
*/
if (iter->rni_clevel > 0) {
#if COMPOSITION_DEPTH == 2
&pg);
if (rc != REP_PROTOCOL_SUCCESS) {
rc_node_clear(out, 0);
return (rc);
}
/* Make sure np isn't being deleted all of a sudden. */
return (REP_PROTOCOL_FAIL_DELETED);
}
/* Keep going. */
continue;
#else
#endif
}
/*
* If we're composed, iterating over property groups, and not
* at the bottom level, check to see if there's a pg at lower
* level with the same name. If so, return a cpg.
*/
if (iter->rni_clevel >= 0 &&
#if COMPOSITION_DEPTH == 2
&pg);
/* holds pg if not NULL */
if (rc != REP_PROTOCOL_SUCCESS) {
rc_node_clear(out, 0);
return (rc);
}
return (REP_PROTOCOL_FAIL_DELETED);
}
} else {
/* Keep res held for rc_node_setup_cpg(). */
cpg = rc_node_alloc();
(void) pthread_mutex_unlock(
rc_node_clear(out, 0);
return (REP_PROTOCOL_FAIL_NO_RESOURCES);
}
case REP_PROTOCOL_SUCCESS:
break;
/* Nevermind. */
break;
(void) pthread_mutex_unlock(
rc_node_clear(out, 0);
return (REP_PROTOCOL_FAIL_NO_RESOURCES);
default:
assert(0);
abort();
}
}
#else
#endif
}
break;
}
return (REP_PROTOCOL_DONE);
return (REP_PROTOCOL_SUCCESS);
}
void
{
return; /* already freed */
if (nip->rni_clevel < 0)
else
(void) pthread_mutex_lock(
}
}
int
{
int ret;
char *auth_string = NULL;
}
return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
}
return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
}
#ifdef NATIVE_BUILD
if (client_is_privileged())
goto skip_checks;
return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
#else
if (is_main_repository == 0)
goto skip_checks;
/* permission check */
return (REP_PROTOCOL_FAIL_NO_RESOURCES);
}
/* solaris.smf.manage can be used. */
if (ret != REP_PROTOCOL_SUCCESS) {
return (ret);
}
/* general/action_authorization values can be used. */
if (ret != REP_PROTOCOL_SUCCESS) {
return (REP_PROTOCOL_FAIL_DELETED);
}
switch (ret) {
case REP_PROTOCOL_SUCCESS:
break;
return (ret);
default:
}
} else {
if (ret == REP_PROTOCOL_SUCCESS) {
/* propertygroup-type-specific authorization */
/* no locking because rn_type won't change anyway */
const char * const auth =
}
if (ret == REP_PROTOCOL_SUCCESS)
/* propertygroup/transaction-type-specific auths */
ret =
if (ret == REP_PROTOCOL_SUCCESS)
ret =
/* AUTH_MANAGE can manipulate general/AUTH_PROP_ACTION */
if (ret == REP_PROTOCOL_SUCCESS &&
if (ret != REP_PROTOCOL_SUCCESS) {
return (ret);
}
}
/*
* Copy out the authorization string before freeing pcp.
*/
if (ret >= 0) {
}
return (REP_PROTOCOL_FAIL_NO_RESOURCES);
}
if (ret == 0) {
/*
* If we get here, the authorization failed.
* Unfortunately, we don't have enough information at this
* point to generate the security audit events. We'll only
* get that information when the client tries to commit the
* event. Thus, we'll remember the failed authorization,
* so that we can generate the audit events later.
*/
}
#endif /* NATIVE_BUILD */
if (authorized != RC_AUTH_UNKNOWN) {
/* Save the authorization string. */
}
if (auth_string != NULL)
return (REP_PROTOCOL_SUCCESS);
}
/*
* Return 1 if the given transaction commands only modify the values of
* properties other than "modify_authorization". Return -1 if any of the
* commands are invalid, and 0 otherwise.
*/
static int
{
const struct rep_protocol_transaction_cmd *cmds;
while (cmds_sz > 0) {
return (-1);
return (-1);
return (-1);
switch (cmds[0].rptc_action) {
break;
/* Check type */
} else {
/* Return more particular error? */
}
if (ok)
break;
return (0);
default:
return (0);
}
== 0)
return (0);
}
return (1);
}
/*
* Return 1 if any of the given transaction commands affect
* "action_authorization". Return -1 if any of the commands are invalid and
* 0 in all other cases.
*/
static int
{
const struct rep_protocol_transaction_cmd *cmds;
while (cmds_sz > 0) {
return (-1);
return (-1);
return (-1);
== 0)
return (1);
}
return (0);
}
/*
* Returns 1 if the transaction commands only modify properties named
* 'enabled'.
*/
static int
{
const struct rep_protocol_transaction_cmd *cmd;
while (cmds_sz > 0) {
return (-1);
return (-1);
return (-1);
!= 0)
return (0);
}
return (1);
}
int
{
int rc;
char *auth_string = NULL;
int auth_status = ADT_SUCCESS;
int auth_ret_value = ADT_SUCCESS;
int tx_flag = 1;
if (auth_string == NULL)
return (REP_PROTOCOL_FAIL_NO_RESOURCES);
}
#ifdef NATIVE_BUILD
if (!client_is_privileged()) {
return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
}
#else
/* permission check: depends on contents of transaction */
return (REP_PROTOCOL_FAIL_NO_RESOURCES);
/* If normal is cleared, we won't do the normal checks. */
normal = 1;
/* Touching general[framework]/action_authorization? */
if (rc == -1) {
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
}
if (rc) {
/* Yes: only AUTH_MANAGE can be used. */
normal = 0;
} else {
}
if (rc == -1) {
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
}
if (rc) {
if (rc != REP_PROTOCOL_SUCCESS) {
return (rc);
}
switch (rc) {
case REP_PROTOCOL_SUCCESS:
break;
return (rc);
default:
bad_error("perm_add_inst_action_auth",
rc);
}
} else {
}
}
if (rc == REP_PROTOCOL_SUCCESS) {
/* Add pgtype-specific authorization. */
const char * const auth =
}
/* Add pg-specific modify_authorization auths. */
if (rc == REP_PROTOCOL_SUCCESS)
/* If value_authorization values are ok, add them. */
if (rc == REP_PROTOCOL_SUCCESS) {
if (rc == -1)
else if (rc)
}
}
if (rc == REP_PROTOCOL_SUCCESS) {
if (granted < 0) {
} else {
/*
* Copy out the authorization string before
* freeing pcp.
*/
if (auth_string == NULL)
}
}
if (rc != REP_PROTOCOL_SUCCESS)
goto cleanout;
if (!granted) {
tx_flag = 0;
}
#endif /* NATIVE_BUILD */
tx_flag = 0;
}
goto cleanout;
}
goto cleanout;
}
/*
* Parse the transaction commands into a useful form.
*/
goto cleanout;
}
if (tx_flag == 0) {
/* Authorization failed. Generate audit events. */
goto cleanout;
}
nnp = rc_node_alloc();
goto cleanout;
}
goto cleanout;
}
/*
* We must have all of the old properties in the cache, or the
* database deletions could cause inconsistencies.
*/
goto cleanout;
}
goto cleanout;
}
goto cleanout;
}
/* our parent is gone, we're going next... */
goto cleanout;
}
goto cleanout;
}
/*
* prepare for the transaction
*/
goto cleanout;
}
/* Sets nnp->rn_gen_id on success. */
if (rc != REP_PROTOCOL_SUCCESS) {
rc_node_clear(txp, 0);
if (rc == REP_PROTOCOL_DONE)
goto cleanout;
}
/*
* Notify waiters
*/
(void) pthread_mutex_lock(&rc_pg_notify_lock);
(void) pthread_mutex_unlock(&rc_pg_notify_lock);
/*
* replace np with nnp
*/
/*
* all done -- clear the transaction.
*/
rc_node_clear(txp, 0);
return (rc);
}
void
{
}
int
{
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
}
/*
* wait for any transaction in progress to complete
*/
return (REP_PROTOCOL_FAIL_DELETED);
}
return (REP_PROTOCOL_FAIL_NOT_LATEST);
}
(void) pthread_mutex_lock(&rc_pg_notify_lock);
(void) pthread_mutex_unlock(&rc_pg_notify_lock);
return (REP_PROTOCOL_SUCCESS);
}
void
{
(void) pthread_mutex_lock(&rc_pg_notify_lock);
(void) pthread_mutex_unlock(&rc_pg_notify_lock);
}
void
{
int i;
for (i = 0; i < RC_NOTIFY_MAX_NAMES; i++) {
}
}
static void
{
}
static void
{
/*
* clean up any notifications at the beginning of the list
*/
}
while (rnip->rni_waiters) {
(void) pthread_cond_broadcast(&rc_pg_notify_cv);
}
}
static int
const char *name)
{
int i;
int rc;
char *f;
if (rc != REP_PROTOCOL_SUCCESS)
return (rc);
if (f == NULL)
return (REP_PROTOCOL_FAIL_NO_RESOURCES);
(void) pthread_mutex_lock(&rc_pg_notify_lock);
for (i = 0; i < RC_NOTIFY_MAX_NAMES; i++)
break;
if (i == RC_NOTIFY_MAX_NAMES) {
(void) pthread_mutex_unlock(&rc_pg_notify_lock);
free(f);
return (REP_PROTOCOL_FAIL_NO_RESOURCES);
}
arr[i] = f;
(void) pthread_mutex_unlock(&rc_pg_notify_lock);
return (REP_PROTOCOL_SUCCESS);
}
int
{
}
int
{
}
/*
* Wait for and report an event of interest to rnip, a notification client
*/
int
{
int am_first_info;
if (sz > 0)
outp[0] = 0;
(void) pthread_mutex_lock(&rc_pg_notify_lock);
/*
* If I'm first on the notify list, it is my job to
* clean up any notifications I pass by. I can't do that
* if someone is blocking the list from removals, so I
* have to wait until they have all drained.
*/
if (am_first_info && rc_notify_in_use) {
rnip->rni_waiters++;
(void) pthread_cond_wait(&rc_pg_notify_cv,
rnip->rni_waiters--;
continue;
}
/*
* Search the list for a node of interest.
*/
if (am_first_info) {
/*
* Passing another client -- stop
* cleaning up notifications
*/
am_first_info = 0;
} else {
}
}
}
/*
* Nothing of interest -- wait for notification
*/
rnip->rni_waiters++;
rnip->rni_waiters--;
continue;
}
/*
* found something to report -- move myself after the
* notification and process it.
*/
if (am_first_info)
(void) pthread_mutex_unlock(&rc_pg_notify_lock);
rc_node_clear(out, 0);
return (REP_PROTOCOL_SUCCESS);
}
/*
* We can't bump nnp's reference count without grabbing its
* lock, and rc_pg_notify_lock is a leaf lock. So we
* temporarily block all removals to keep nnp from
* disappearing.
*/
assert(rc_notify_in_use > 0);
(void) pthread_mutex_unlock(&rc_pg_notify_lock);
(void) pthread_mutex_lock(&rc_pg_notify_lock);
assert(rc_notify_in_use > 0);
if (am_first_info)
if (rc_notify_in_use == 0)
(void) pthread_cond_broadcast(&rc_pg_notify_cv);
(void) pthread_mutex_unlock(&rc_pg_notify_lock);
return (REP_PROTOCOL_SUCCESS);
}
/*
* If we're the last one out, let people know it's clear.
*/
if (rnip->rni_waiters == 0)
(void) pthread_mutex_unlock(&rc_pg_notify_lock);
return (REP_PROTOCOL_DONE);
}
static void
{
int i;
(void) pthread_mutex_lock(&rc_pg_notify_lock);
(void) pthread_mutex_unlock(&rc_pg_notify_lock);
for (i = 0; i < RC_NOTIFY_MAX_NAMES; i++) {
}
}
}
(void) pthread_mutex_lock(&rc_pg_notify_lock);
(void) pthread_mutex_unlock(&rc_pg_notify_lock);
}
void
{
}