summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
authortw21770 <none@none>2008-01-07 14:04:56 -0800
committertw21770 <none@none>2008-01-07 14:04:56 -0800
commit5b7f77ad52bf657ba49d64d16f527e958d0fb820 (patch)
tree70272848c29d0888e287ec1532089578b1769830 /usr/src
parent3323877de6db742e6657b6081ffe6acd0b007436 (diff)
downloadillumos-joyent-5b7f77ad52bf657ba49d64d16f527e958d0fb820.tar.gz
5079356 Framework should provide administrative audit trail/history
6405683 svc.configd audit events need to be defined.
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/cmd/svc/configd/Makefile4
-rw-r--r--usr/src/cmd/svc/configd/backend.c6
-rw-r--r--usr/src/cmd/svc/configd/client.c230
-rw-r--r--usr/src/cmd/svc/configd/configd.c23
-rw-r--r--usr/src/cmd/svc/configd/configd.h89
-rw-r--r--usr/src/cmd/svc/configd/object.c238
-rw-r--r--usr/src/cmd/svc/configd/rc_node.c1832
-rw-r--r--usr/src/cmd/svc/startd/libscf.c3
-rw-r--r--usr/src/cmd/svc/startd/startd.c10
-rw-r--r--usr/src/cmd/svc/svccfg/svccfg.h4
-rw-r--r--usr/src/cmd/svc/svccfg/svccfg_engine.c4
-rw-r--r--usr/src/cmd/svc/svccfg/svccfg_libscf.c76
-rw-r--r--usr/src/common/svc/repcache_protocol.h18
-rw-r--r--usr/src/lib/libbsm/audit_event.txt38
-rw-r--r--usr/src/lib/libbsm/auditxml3
-rw-r--r--usr/src/lib/libbsm/common/adt.c18
-rw-r--r--usr/src/lib/libbsm/common/adt.xml374
-rw-r--r--usr/src/lib/libbsm/common/mapfile-vers3
-rw-r--r--usr/src/lib/libscf/common/libscf_impl.h16
-rw-r--r--usr/src/lib/libscf/common/lowlevel.c48
-rw-r--r--usr/src/lib/libscf/common/mapfile-vers3
-rw-r--r--usr/src/lib/libscf/inc/libscf_priv.h28
22 files changed, 2888 insertions, 180 deletions
diff --git a/usr/src/cmd/svc/configd/Makefile b/usr/src/cmd/svc/configd/Makefile
index 910651f997..5ad64d9ba5 100644
--- a/usr/src/cmd/svc/configd/Makefile
+++ b/usr/src/cmd/svc/configd/Makefile
@@ -19,7 +19,7 @@
# CDDL HEADER END
#
#
-# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# ident "%Z%%M% %I% %E% SMI"
@@ -53,7 +53,7 @@ ROOTCMDDIR= $(ROOT)/lib/svc/bin
MYCPPFLAGS = -I. -I../common -I../../../common/svc -I$(ROOT)/usr/include/sqlite -D_REENTRANT
CPPFLAGS += $(MYCPPFLAGS)
CFLAGS += -v
-MYLDLIBS = -lumem -luutil
+MYLDLIBS = -lumem -luutil -lbsm
LDLIBS += -lsecdb $(MYLDLIBS)
LINTFLAGS += -errtags -erroff=E_BAD_FORMAT_ARG_TYPE2
diff --git a/usr/src/cmd/svc/configd/backend.c b/usr/src/cmd/svc/configd/backend.c
index 50bb3052c5..3f39f068f8 100644
--- a/usr/src/cmd/svc/configd/backend.c
+++ b/usr/src/cmd/svc/configd/backend.c
@@ -18,8 +18,9 @@
*
* CDDL HEADER END
*/
+
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -1891,6 +1892,9 @@ backend_init(const char *db_file, const char *npdb_file, int have_np)
}
if (db_file == NULL)
db_file = REPOSITORY_DB;
+ if (strcmp(db_file, REPOSITORY_DB) != 0) {
+ is_main_repository = 0;
+ }
r = backend_create(BACKEND_TYPE_NORMAL, db_file, &be);
switch (r) {
diff --git a/usr/src/cmd/svc/configd/client.c b/usr/src/cmd/svc/configd/client.c
index 840e36f64a..2a0b49242d 100644
--- a/usr/src/cmd/svc/configd/client.c
+++ b/usr/src/cmd/svc/configd/client.c
@@ -18,8 +18,9 @@
*
* CDDL HEADER END
*/
+
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -36,13 +37,17 @@
#include <alloca.h>
#include <assert.h>
+#include <bsm/adt_event.h>
#include <door.h>
#include <errno.h>
+#include <libintl.h>
#include <limits.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <syslog.h>
+#include <ucred.h>
#include <unistd.h>
#include <libuutil.h>
@@ -279,6 +284,7 @@ client_alloc(void)
cp->rc_doorid = INVALID_DOORID;
(void) pthread_mutex_init(&cp->rc_lock, NULL);
+ (void) pthread_mutex_init(&cp->rc_annotate_lock, NULL);
rc_node_ptr_init(&cp->rc_notify_ptr);
@@ -306,6 +312,8 @@ client_free(repcache_client_t *cp)
uu_avl_destroy(cp->rc_iters);
uu_list_node_fini(cp, &cp->rc_link, client_pool);
(void) pthread_mutex_destroy(&cp->rc_lock);
+ (void) pthread_mutex_destroy(&cp->rc_annotate_lock);
+ rc_node_ptr_free_mem(&cp->rc_notify_ptr);
uu_free(cp);
}
@@ -497,6 +505,7 @@ entity_destroy(repcache_entity_t *entity)
uu_avl_node_fini(entity, &entity->re_link, entity_pool);
(void) pthread_mutex_destroy(&entity->re_lock);
+ rc_node_ptr_free_mem(&entity->re_node);
uu_free(entity);
}
@@ -715,6 +724,19 @@ client_destroy(uint32_t id)
*/
rc_pg_notify_fini(&cp->rc_pg_notify);
+ /*
+ * clean up annotations
+ */
+ if (cp->rc_operation != NULL)
+ free((void *)cp->rc_operation);
+ if (cp->rc_file != NULL)
+ free((void *)cp->rc_file);
+
+ /*
+ * End audit session.
+ */
+ (void) adt_end_session(cp->rc_adt_session);
+
client_free(cp);
}
@@ -1770,6 +1792,207 @@ backup_repository(repcache_client_t *cp,
return (result);
}
+/*
+ * This function captures the information that will be used for an
+ * annotation audit event. Specifically, it captures the operation to be
+ * performed and the name of the file that is being used. These values are
+ * copied from the rep_protocol_annotation request at rpr to the client
+ * structure. If both these values are null, the client is turning
+ * annotation off.
+ *
+ * Fails with
+ * _NO_RESOURCES - unable to allocate memory
+ */
+static rep_protocol_responseid_t
+set_annotation(repcache_client_t *cp, struct rep_protocol_annotation *rpr)
+{
+ au_id_t audit_uid;
+ const char *file = NULL;
+ const char *old_ptrs[2];
+ const char *operation = NULL;
+ rep_protocol_responseid_t rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
+ au_asid_t sessionid;
+
+ (void) memset((void *)old_ptrs, 0, sizeof (old_ptrs));
+
+ /* Copy rpr_operation and rpr_file if they are not empty strings. */
+ if (rpr->rpr_operation[0] != 0) {
+ /*
+ * Make sure that client did not send us an unterminated buffer.
+ */
+ rpr->rpr_operation[sizeof (rpr->rpr_operation) - 1] = 0;
+ if ((operation = strdup(rpr->rpr_operation)) == NULL)
+ goto out;
+ }
+ if (rpr->rpr_file[0] != 0) {
+ /*
+ * Make sure that client did not send us an unterminated buffer.
+ */
+ rpr->rpr_file[sizeof (rpr->rpr_file) - 1] = 0;
+ if ((file = strdup(rpr->rpr_file)) == NULL)
+ goto out;
+ }
+
+ (void) pthread_mutex_lock(&cp->rc_annotate_lock);
+ /* Save addresses of memory to free when not locked */
+ old_ptrs[0] = cp->rc_operation;
+ old_ptrs[1] = cp->rc_file;
+
+ /* Save pointers to annotation strings. */
+ cp->rc_operation = operation;
+ cp->rc_file = file;
+
+ /*
+ * Set annotation flag. Annotations should be turned on if either
+ * operation or file are not NULL.
+ */
+ cp->rc_annotate = (operation != NULL) || (file != NULL);
+ (void) pthread_mutex_unlock(&cp->rc_annotate_lock);
+
+ /*
+ * operation and file pointers are saved in cp, so don't free them
+ * during cleanup.
+ */
+ operation = NULL;
+ file = NULL;
+ rc = REP_PROTOCOL_SUCCESS;
+
+ /*
+ * Native builds are done to create svc.configd-native. This
+ * program runs only on the Open Solaris build machines to create
+ * the seed repository. Until the SMF auditing code is distributed
+ * to the Open Solaris build machines, adt_get_unique_id() in the
+ * following code is not a global function in libbsm. Hence the
+ * following conditional compilation.
+ */
+#ifndef NATIVE_BUILD
+ /*
+ * Set the appropriate audit session id.
+ */
+ if (cp->rc_annotate) {
+ /*
+ * We're starting a group of annotated audit events, so
+ * create and set an audit session ID for this annotation.
+ */
+ adt_get_auid(cp->rc_adt_session, &audit_uid);
+ sessionid = adt_get_unique_id(audit_uid);
+ } else {
+ /*
+ * Annotation is done so restore our client audit session
+ * id.
+ */
+ sessionid = cp->rc_adt_sessionid;
+ }
+ adt_set_asid(cp->rc_adt_session, sessionid);
+#endif /* NATIVE_BUILD */
+
+out:
+ if (operation != NULL)
+ free((void *)operation);
+ if (file != NULL)
+ free((void *)file);
+ free((void *)old_ptrs[0]);
+ free((void *)old_ptrs[1]);
+ return (rc);
+}
+
+/*
+ * Determine if an annotation event needs to be generated. If it does
+ * provide the operation and file name that should be used in the event.
+ *
+ * Can return:
+ * 0 No annotation event needed or buffers are not large
+ * enough. Either way an event should not be
+ * generated.
+ * 1 Generate annotation event.
+ */
+int
+client_annotation_needed(char *operation, size_t oper_sz,
+ char *file, size_t file_sz)
+{
+ thread_info_t *ti = thread_self();
+ repcache_client_t *cp = ti->ti_active_client;
+ int rc = 0;
+
+ (void) pthread_mutex_lock(&cp->rc_annotate_lock);
+ if (cp->rc_annotate) {
+ rc = 1;
+ if (cp->rc_operation == NULL) {
+ if (oper_sz > 0)
+ operation[0] = 0;
+ } else {
+ if (strlcpy(operation, cp->rc_operation, oper_sz) >=
+ oper_sz) {
+ /* Buffer overflow, so do not generate event */
+ rc = 0;
+ }
+ }
+ if (cp->rc_file == NULL) {
+ if (file_sz > 0)
+ file[0] = 0;
+ } else if (rc == 1) {
+ if (strlcpy(file, cp->rc_file, file_sz) >= file_sz) {
+ /* Buffer overflow, so do not generate event */
+ rc = 0;
+ }
+ }
+ }
+ (void) pthread_mutex_unlock(&cp->rc_annotate_lock);
+ return (rc);
+}
+
+void
+client_annotation_finished()
+{
+ thread_info_t *ti = thread_self();
+ repcache_client_t *cp = ti->ti_active_client;
+
+ (void) pthread_mutex_lock(&cp->rc_annotate_lock);
+ cp->rc_annotate = 0;
+ (void) pthread_mutex_unlock(&cp->rc_annotate_lock);
+}
+
+static void
+start_audit_session(repcache_client_t *cp)
+{
+ ucred_t *cred = NULL;
+ int adt_rc = 0;
+ adt_session_data_t *session;
+
+ if ((adt_rc = door_ucred(&cred)) != 0) {
+ syslog(LOG_ERR, gettext("start_audit_session(): cannot "
+ "get ucred. %m\n"));
+ }
+ if ((adt_rc == 0) &&
+ (adt_rc = adt_start_session(&session, NULL, 0)) != 0) {
+ /*
+ * Log the failure, but don't quit because of inability to
+ * audit.
+ */
+ syslog(LOG_ERR, gettext("start_audit_session(): could not "
+ "start audit session.\n"));
+ }
+ if ((adt_rc == 0) &&
+ ((adt_rc = adt_set_from_ucred(session, cred, ADT_NEW)) != 0)) {
+ syslog(LOG_ERR, gettext("start_audit_session(): cannot set "
+ "audit session data from ucred\n"));
+ }
+ if (adt_rc == 0) {
+ /* All went well. Save the session data and session ID */
+ cp->rc_adt_session = session;
+ adt_get_asid(session, &cp->rc_adt_sessionid);
+ } else {
+ /*
+ * Something went wrong. End the session. A NULL session
+ * pointer value can legally be used in all subsequent
+ * calls to adt_ functions.
+ */
+ (void) adt_end_session(session);
+ cp->rc_adt_session = NULL;
+ }
+
+ ucred_free(cred);
+}
typedef rep_protocol_responseid_t protocol_simple_f(repcache_client_t *cp,
const void *rpr);
@@ -1942,6 +2165,9 @@ static struct protocol_entry {
PROTO(REP_PROTOCOL_BACKUP, backup_repository,
struct rep_protocol_backup_request),
+ PROTO(REP_PROTOCOL_SET_AUDIT_ANNOTATION, set_annotation,
+ struct rep_protocol_annotation),
+
PROTO_END()
};
#undef PROTO
@@ -2176,6 +2402,8 @@ create_client(pid_t pid, uint32_t debugflags, int privileged, int *out_fd)
cp->rc_pid = pid;
cp->rc_debug = debugflags;
+ start_audit_session(cp);
+
cp->rc_doorfd = door_create(client_switcher, (void *)cp->rc_id,
door_flags);
diff --git a/usr/src/cmd/svc/configd/configd.c b/usr/src/cmd/svc/configd/configd.c
index 8aa37492f1..cdbec408c4 100644
--- a/usr/src/cmd/svc/configd/configd.c
+++ b/usr/src/cmd/svc/configd/configd.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -201,6 +201,10 @@ thread_self(void)
return (pthread_getspecific(thread_info_key));
}
+/*
+ * get_ucred() returns NULL if it was unable to get the credential
+ * information.
+ */
ucred_t *
get_ucred(void)
{
@@ -234,6 +238,21 @@ ucred_is_privileged(ucred_t *uc)
return (0);
}
+/*
+ * The purpose of this function is to get the audit session data for use in
+ * generating SMF audit events. We use a single audit session per client.
+ *
+ * get_audit_session() may return NULL. It is legal to use a NULL pointer
+ * in subsequent calls to adt_* functions.
+ */
+adt_session_data_t *
+get_audit_session(void)
+{
+ thread_info_t *ti = thread_self();
+
+ return (ti->ti_active_client->rc_adt_session);
+}
+
static void *
thread_start(void *arg)
{
@@ -534,12 +553,10 @@ main(int argc, char *argv[])
break;
case 'd':
doorpath = regularize_path(curdir, optarg, doortmp);
- is_main_repository = 0;
have_npdb = 0; /* default to no non-persist */
break;
case 'p':
log_to_syslog = 0; /* don't use syslog */
- is_main_repository = 0;
/*
* If our parent exits while we're opening its /proc
diff --git a/usr/src/cmd/svc/configd/configd.h b/usr/src/cmd/svc/configd/configd.h
index dc170fc2d7..8d53c15c79 100644
--- a/usr/src/cmd/svc/configd/configd.h
+++ b/usr/src/cmd/svc/configd/configd.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * 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
* or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -29,6 +28,7 @@
#pragma ident "%Z%%M% %I% %E% SMI"
+#include <bsm/adt.h>
#include <door.h>
#include <pthread.h>
#include <string.h>
@@ -62,6 +62,7 @@ extern "C" {
*
* leaf locks: (no other locks may be aquired while holding one)
* rc_pg_notify_lock
+ * rc_annotate_lock
*/
/*
@@ -251,6 +252,7 @@ struct rc_node {
rc_node_t *rn_parent; /* set if on child list */
rc_node_t *rn_former; /* next former node */
rc_node_t *rn_parent_ref; /* reference count target */
+ const char *rn_fmri;
/*
* external state (protected by hash chain lock)
@@ -307,9 +309,30 @@ struct rc_node {
(RC_NODE_DYING_FLAGS | RC_NODE_USING_PARENT)
+typedef enum rc_auth_state {
+ RC_AUTH_UNKNOWN = 0, /* No checks done yet. */
+ RC_AUTH_FAILED, /* Authorization checked & failed. */
+ RC_AUTH_PASSED /* Authorization succeeded. */
+} rc_auth_state_t;
+
+/*
+ * Some authorization checks are performed in rc_node_setup_tx() in
+ * response to the REP_PROTOCOL_PROPERTYGRP_TX_START message. Other checks
+ * must wait until the actual transaction operations are received in the
+ * REP_PROTOCOL_PROPERTYGRP_TX_COMMIT message. This second set of checks
+ * is performed in rc_tx_commit(). rnp_auth_string and rnp_authorized in
+ * the following structure are used to hold the results of the
+ * authorization checking done in rc_node_setup_tx() for later use by
+ * rc_tx_commit().
+ *
+ * In client.c transactions are represented by rc_node_ptr structures which
+ * point to a property group rc_node_t. Thus, this is an appropriate place
+ * to hold authorization state.
+ */
typedef struct rc_node_ptr {
rc_node_t *rnp_node;
- char rnp_authorized; /* transaction pre-authed */
+ const char *rnp_auth_string; /* authorization string */
+ rc_auth_state_t rnp_authorized; /* transaction pre-auth rslt. */
char rnp_deleted; /* object was deleted */
} rc_node_ptr_t;
@@ -353,6 +376,14 @@ typedef struct cache_bucket {
} cache_bucket_t;
/*
+ * tx_commit_data_tx is an opaque structure which is defined in object.c.
+ * It contains the data of the transaction that is to be committed.
+ * Accessor functions in object.c allow other modules to retrieve
+ * information.
+ */
+typedef struct tx_commit_data tx_commit_data_t;
+
+/*
* Snapshots
*/
struct rc_snapshot {
@@ -436,6 +467,21 @@ typedef struct repcache_client {
int rc_doorfd; /* our door's FD */
/*
+ * Constants used for security auditing
+ *
+ * rc_adt_session points to the audit session data that is used for
+ * the life of the client. rc_adt_sessionid is the session ID that
+ * is initially assigned when the audit session is started. See
+ * start_audit_session() in client.c. This session id is used for
+ * audit events except when we are processing a set of annotated
+ * events. Annotated events use a separate session id so that they
+ * can be grouped. See set_annotation() in client.c.
+ */
+ adt_session_data_t *rc_adt_session; /* Session data. */
+ au_asid_t rc_adt_sessionid; /* Main session ID for */
+ /* auditing */
+
+ /*
* client list linkage, protected by hash chain lock
*/
uu_list_node_t rc_link;
@@ -461,13 +507,26 @@ typedef struct repcache_client {
* Variables, protected by rc_lock
*/
int rc_refcnt; /* in-progress door calls */
- int rc_flags; /* state */
+ int rc_flags; /* see RC_CLIENT_* symbols below */
uint32_t rc_changeid; /* used to make backups idempotent */
pthread_t rc_insert_thr; /* single thread trying to insert */
pthread_t rc_notify_thr; /* single thread waiting for notify */
pthread_cond_t rc_cv;
pthread_mutex_t rc_lock;
+
+ /*
+ * Per-client audit information. These fields must be protected by
+ * rc_annotate_lock separately from rc_lock because they may need
+ * to be accessed from rc_node.c with an entity or iterator lock
+ * held, and those must be taken after rc_lock.
+ */
+ int rc_annotate; /* generate annotation event if set */
+ const char *rc_operation; /* operation for audit annotation */
+ const char *rc_file; /* file name for audit annotation */
+ pthread_mutex_t rc_annotate_lock;
} repcache_client_t;
+
+/* Bit definitions for rc_flags. */
#define RC_CLIENT_DEAD 0x00000001
typedef struct client_bucket {
@@ -559,6 +618,8 @@ void thread_newstate(thread_info_t *, thread_state_t);
ucred_t *get_ucred(void);
int ucred_is_privileged(ucred_t *);
+adt_session_data_t *get_audit_session(void);
+
void configd_critical(const char *, ...);
void configd_vcritical(const char *, va_list);
@@ -573,6 +634,8 @@ int setup_main_door(const char *);
/*
* client.c
*/
+int client_annotation_needed(char *, size_t, char *, size_t);
+void client_annotation_finished(void);
int create_client(pid_t, uint32_t, int, int *);
int client_init(void);
int client_is_privileged(void);
@@ -584,6 +647,7 @@ void log_enter(request_log_entry_t *);
int rc_node_init();
int rc_check_type_name(uint32_t, const char *);
+void rc_node_ptr_free_mem(rc_node_ptr_t *);
void rc_node_rele(rc_node_t *);
rc_node_t *rc_node_setup(rc_node_t *, rc_node_lookup_t *,
const char *, rc_node_t *);
@@ -669,7 +733,18 @@ int object_snapshot_attach(rc_node_lookup_t *, uint32_t *, int);
/*
* object.c
*/
-int object_tx_commit(rc_node_lookup_t *, const void *, size_t, uint32_t *);
+int object_tx_commit(rc_node_lookup_t *, tx_commit_data_t *, uint32_t *);
+
+/* Functions to access transaction commands. */
+int tx_cmd_action(tx_commit_data_t *, size_t,
+ enum rep_protocol_transaction_action *);
+size_t tx_cmd_count(tx_commit_data_t *);
+int tx_cmd_nvalues(tx_commit_data_t *, size_t, uint32_t *);
+int tx_cmd_prop(tx_commit_data_t *, size_t, const char **);
+int tx_cmd_prop_type(tx_commit_data_t *, size_t, uint32_t *);
+int tx_cmd_value(tx_commit_data_t *, size_t, uint32_t, const char **);
+void tx_commit_data_free(tx_commit_data_t *);
+int tx_commit_data_new(const void *, size_t, tx_commit_data_t **);
/*
* snapshot.c
diff --git a/usr/src/cmd/svc/configd/object.c b/usr/src/cmd/svc/configd/object.c
index 7bc4155033..2c7d6a8652 100644
--- a/usr/src/cmd/svc/configd/object.c
+++ b/usr/src/cmd/svc/configd/object.c
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * 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
* or http://www.opensolaris.org/os/licensing.
@@ -19,8 +18,9 @@
*
* CDDL HEADER END
*/
+
/*
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -79,7 +79,7 @@ struct tx_commit_data {
static int
tx_check_genid(void *data_arg, int columns, char **vals, char **names)
{
- struct tx_commit_data *data = data_arg;
+ tx_commit_data_t *data = data_arg;
assert(columns == 1);
if (atoi(vals[0]) != data->txc_oldgen)
data->txc_result = REP_PROTOCOL_FAIL_NOT_LATEST;
@@ -104,7 +104,7 @@ tx_check_genid(void *data_arg, int columns, char **vals, char **names)
static int
tx_process_property(void *data_arg, int columns, char **vals, char **names)
{
- struct tx_commit_data *data = data_arg;
+ tx_commit_data_t *data = data_arg;
struct tx_cmd *elem;
const char *prop_name = vals[0];
@@ -183,7 +183,7 @@ tx_process_property(void *data_arg, int columns, char **vals, char **names)
* Finally, we check all of the commands, and fail if anything was marked bad.
*/
static int
-tx_process_cmds(struct tx_commit_data *data)
+tx_process_cmds(tx_commit_data_t *data)
{
int idx;
int r;
@@ -194,6 +194,7 @@ tx_process_cmds(struct tx_commit_data *data)
backend_query_t *q;
int do_delete;
+ uint32_t nvalues;
/*
* For persistent pgs, we use backend_fail_if_seen to abort the
@@ -298,8 +299,9 @@ tx_process_cmds(struct tx_commit_data *data)
v = elem->tx_values;
+ nvalues = elem->tx_nvalues;
while (r == REP_PROTOCOL_SUCCESS &&
- elem->tx_nvalues--) {
+ nvalues--) {
str = (const char *)&v[1];
r = backend_tx_run_update(data->txc_tx,
@@ -337,7 +339,7 @@ check_string(uintptr_t loc, uint32_t len, uint32_t sz)
}
static int
-tx_check_and_setup(struct tx_commit_data *data, const void *cmds_arg,
+tx_check_and_setup(tx_commit_data_t *data, const void *cmds_arg,
uint32_t count)
{
const struct rep_protocol_transaction_cmd *cmds;
@@ -409,26 +411,46 @@ tx_check_and_setup(struct tx_commit_data *data, const void *cmds_arg,
return (REP_PROTOCOL_SUCCESS);
}
+/*
+ * Free the memory associated with a tx_commit_data structure.
+ */
+void
+tx_commit_data_free(tx_commit_data_t *tx_data)
+{
+ uu_free(tx_data);
+}
+
+/*
+ * Parse the data of a REP_PROTOCOL_PROPERTYGRP_TX_COMMIT message into a
+ * more useful form. The data in the message will be represented by a
+ * tx_commit_data_t structure which is allocated by this function. The
+ * address of the allocated structure is returned to *tx_data and must be
+ * freed by calling tx_commit_data_free().
+ *
+ * Parameters:
+ * cmds_arg Address of the commands in the
+ * REP_PROTOCOL_PROPERTYGRP_TX_COMMIT message.
+ *
+ * cmds_sz Number of message bytes at cmds_arg.
+ *
+ * tx_data Points to the place to receive the address of the
+ * allocated memory.
+ *
+ * Fails with
+ * _BAD_REQUEST
+ * _NO_RESOURCES
+ */
int
-object_tx_commit(rc_node_lookup_t *lp, const void *cmds_arg, size_t cmds_sz,
- uint32_t *gen)
+tx_commit_data_new(const void *cmds_arg, size_t cmds_sz,
+ tx_commit_data_t **tx_data)
{
const struct rep_protocol_transaction_cmd *cmds;
+ tx_commit_data_t *data;
uintptr_t loc;
-
- struct tx_commit_data *data;
- uint32_t count, sz;
- uint32_t new_gen;
-
+ uint32_t count;
+ uint32_t sz;
int ret;
- rep_protocol_responseid_t r;
-
- backend_tx_t *tx;
- backend_query_t *q;
-
- int backend = lp->rl_backend;
-
/*
* First, verify that the reported sizes make sense, and count
* the number of commands.
@@ -455,16 +477,175 @@ object_tx_commit(rc_node_lookup_t *lp, const void *cmds_arg, size_t cmds_sz,
count++;
}
- data = alloca(TX_COMMIT_DATA_SIZE(count));
- (void) memset(data, 0, TX_COMMIT_DATA_SIZE(count));
+ data = uu_zalloc(TX_COMMIT_DATA_SIZE(count));
+ if (data == NULL)
+ return (REP_PROTOCOL_FAIL_NO_RESOURCES);
/*
* verify that everything looks okay, and set up our command
* datastructures.
*/
+ data->txc_count = count;
ret = tx_check_and_setup(data, cmds_arg, count);
- if (ret != REP_PROTOCOL_SUCCESS)
- return (ret);
+ if (ret == REP_PROTOCOL_SUCCESS) {
+ *tx_data = data;
+ } else {
+ *tx_data = NULL;
+ uu_free(data);
+ }
+ return (ret);
+}
+
+/*
+ * The following are a set of accessor functions to retrieve data from a
+ * tx_commit_data_t that has been allocated by tx_commit_data_new().
+ */
+
+/*
+ * Return the action of the transaction command whose command number is
+ * cmd_no. The action is placed at *action.
+ *
+ * Returns:
+ * _FAIL_BAD_REQUEST cmd_no is out of range.
+ */
+int
+tx_cmd_action(tx_commit_data_t *tx_data, size_t cmd_no,
+ enum rep_protocol_transaction_action *action)
+{
+ struct tx_cmd *cur;
+
+ assert(cmd_no < tx_data->txc_count);
+ if (cmd_no >= tx_data->txc_count)
+ return (REP_PROTOCOL_FAIL_BAD_REQUEST);
+
+ cur = &tx_data->txc_cmds[cmd_no];
+ *action = cur->tx_cmd->rptc_action;
+ return (REP_PROTOCOL_SUCCESS);
+}
+
+/*
+ * Return the number of transaction commands held in tx_data.
+ */
+size_t
+tx_cmd_count(tx_commit_data_t *tx_data)
+{
+ return (tx_data->txc_count);
+}
+
+/*
+ * Return the number of property values that are associated with the
+ * transaction command whose number is cmd_no. The number of values is
+ * returned to *nvalues.
+ *
+ * Returns:
+ * _FAIL_BAD_REQUEST cmd_no is out of range.
+ */
+int
+tx_cmd_nvalues(tx_commit_data_t *tx_data, size_t cmd_no, uint32_t *nvalues)
+{
+ struct tx_cmd *cur;
+
+ assert(cmd_no < tx_data->txc_count);
+ if (cmd_no >= tx_data->txc_count)
+ return (REP_PROTOCOL_FAIL_BAD_REQUEST);
+
+ cur = &tx_data->txc_cmds[cmd_no];
+ *nvalues = cur->tx_nvalues;
+ return (REP_PROTOCOL_SUCCESS);
+}
+
+/*
+ * Return a pointer to the property name of the command whose number is
+ * cmd_no. The property name pointer is returned to *pname.
+ *
+ * Returns:
+ * _FAIL_BAD_REQUEST cmd_no is out of range.
+ */
+int
+tx_cmd_prop(tx_commit_data_t *tx_data, size_t cmd_no, const char **pname)
+{
+ struct tx_cmd *cur;
+
+ assert(cmd_no < tx_data->txc_count);
+ if (cmd_no >= tx_data->txc_count)
+ return (REP_PROTOCOL_FAIL_BAD_REQUEST);
+
+ cur = &tx_data->txc_cmds[cmd_no];
+ *pname = cur->tx_prop;
+ return (REP_PROTOCOL_SUCCESS);
+}
+
+/*
+ * Return the property type of the property whose command number is
+ * cmd_no. The property type is returned to *ptype.
+ *
+ * Returns:
+ * _FAIL_BAD_REQUEST cmd_no is out of range.
+ */
+int
+tx_cmd_prop_type(tx_commit_data_t *tx_data, size_t cmd_no, uint32_t *ptype)
+{
+ struct tx_cmd *cur;
+
+ assert(cmd_no < tx_data->txc_count);
+ if (cmd_no >= tx_data->txc_count)
+ return (REP_PROTOCOL_FAIL_BAD_REQUEST);
+
+ cur = &tx_data->txc_cmds[cmd_no];
+ *ptype = cur->tx_cmd->rptc_type;
+ return (REP_PROTOCOL_SUCCESS);
+}
+
+/*
+ * This function is used to retrieve a property value from the transaction
+ * data. val_no specifies which value is to be retrieved from the
+ * transaction command whose number is cmd_no. A pointer to the specified
+ * value is placed in *val.
+ *
+ * Returns:
+ * _FAIL_BAD_REQUEST cmd_no or val_no is out of range.
+ */
+int
+tx_cmd_value(tx_commit_data_t *tx_data, size_t cmd_no, uint32_t val_no,
+ const char **val)
+{
+ const char *bp;
+ struct tx_cmd *cur;
+ uint32_t i;
+ uint32_t value_len;
+
+ assert(cmd_no < tx_data->txc_count);
+ if (cmd_no >= tx_data->txc_count)
+ return (REP_PROTOCOL_FAIL_BAD_REQUEST);
+
+ cur = &tx_data->txc_cmds[cmd_no];
+ assert(val_no < cur->tx_nvalues);
+ if (val_no >= cur->tx_nvalues)
+ return (REP_PROTOCOL_FAIL_BAD_REQUEST);
+
+ /* Find the correct value */
+ bp = (char *)cur->tx_values;
+ for (i = 0; i < val_no; i++) {
+ /* LINTED alignment */
+ value_len = *(uint32_t *)bp;
+ bp += sizeof (uint32_t) + TX_SIZE(value_len);
+ }
+
+ /* Bypass the count & return pointer to value. */
+ bp += sizeof (uint32_t);
+ *val = bp;
+ return (REP_PROTOCOL_SUCCESS);
+}
+
+int
+object_tx_commit(rc_node_lookup_t *lp, tx_commit_data_t *data, uint32_t *gen)
+{
+ uint32_t new_gen;
+ int ret;
+ rep_protocol_responseid_t r;
+ backend_tx_t *tx;
+ backend_query_t *q;
+ int backend = lp->rl_backend;
ret = backend_tx_begin(backend, &tx);
if (ret != REP_PROTOCOL_SUCCESS)
@@ -488,7 +669,7 @@ object_tx_commit(rc_node_lookup_t *lp, const void *cmds_arg, size_t cmds_sz,
}
/* If the transaction is empty, cut out early. */
- if (count == 0) {
+ if (data->txc_count == 0) {
backend_tx_rollback(tx);
r = REP_PROTOCOL_DONE;
goto end;
@@ -503,7 +684,6 @@ object_tx_commit(rc_node_lookup_t *lp, const void *cmds_arg, size_t cmds_sz,
data->txc_pg_id = lp->rl_main_id;
data->txc_gen = new_gen;
data->txc_tx = tx;
- data->txc_count = count;
r = backend_tx_run_update(tx,
"UPDATE pg_tbl SET pg_gen_id = %d "
diff --git a/usr/src/cmd/svc/configd/rc_node.c b/usr/src/cmd/svc/configd/rc_node.c
index bed6c9ed3f..8182c709b6 100644
--- a/usr/src/cmd/svc/configd/rc_node.c
+++ b/usr/src/cmd/svc/configd/rc_node.c
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -128,6 +128,11 @@
* 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.
*/
/*
@@ -159,9 +164,187 @@
* 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
+ * usr/src/lib/libbsm/common/adt.c to generate the audit
+ * 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
+ * group/property name combinations. Many of these are
+ * 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:
+ * ------------------
+ *
+ * There are combinations of property group/property name that are special.
+ * 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>
@@ -172,6 +355,7 @@
#include <stdlib.h>
#include <strings.h>
#include <sys/types.h>
+#include <syslog.h>
#include <unistd.h>
#include <user_attr.h>
@@ -197,6 +381,17 @@
#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 {
uint32_t rt_type; /* matches array index */
uint32_t rt_num_ids;
@@ -233,13 +428,58 @@ 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. */
+} pc_auth_type_t;
+
/* An authorization set hash table. */
typedef struct {
struct pc_elt **pc_buckets;
uint_t pc_bnum; /* number of buckets */
uint_t pc_enum; /* number of elements */
+ struct pc_elt *pc_specific; /* most specific element */
+ pc_auth_type_t pc_specific_type; /* type of pc_specific */
+ 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. */
+} audit_event_data_t;
+
+/*
+ * 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.
+ */
+typedef int (*spc_getid_fn_t)(tx_commit_data_t *, size_t, const char *,
+ au_event_t *);
+static int general_enable_id(tx_commit_data_t *, size_t, const char *,
+ 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;
@@ -253,6 +493,61 @@ static uint_t rc_notify_in_use; /* blocks removals */
static pthread_mutex_t perm_lock = PTHREAD_MUTEX_INITIALIZER;
+/*
+ * Some combinations of property group/property name require a special
+ * 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. */
+ au_event_t api_event_id; /* event id or 0. */
+ spc_getid_fn_t api_event_func; /* function to get event id. */
+} audit_special_prop_item_t;
+
+/*
+ * 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
+/*
+ * The following special_props_list array specifies property group/property
+ * 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[] = {
+ {SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_DEGRADED, ADT_smf_degrade,
+ NULL},
+ {SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_DEGRADE_IMMEDIATE,
+ ADT_smf_immediate_degrade, NULL},
+ {SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_MAINT_OFF, ADT_smf_clear, NULL},
+ {SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_MAINT_ON,
+ ADT_smf_maintenance, NULL},
+ {SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_MAINT_ON_IMMEDIATE,
+ ADT_smf_immediate_maintenance, NULL},
+ {SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_MAINT_ON_IMMTEMP,
+ ADT_smf_immtmp_maintenance, NULL},
+ {SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_MAINT_ON_TEMPORARY,
+ ADT_smf_tmp_maintenance, NULL},
+ {SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_REFRESH, ADT_smf_refresh, NULL},
+ {SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_RESTART, ADT_smf_restart, NULL},
+ {SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_RESTORE, ADT_smf_clear, NULL},
+ {SCF_PG_OPTIONS, SCF_PROPERTY_MILESTONE, ADT_smf_milestone, NULL},
+ {SCF_PG_OPTIONS_OVR, SCF_PROPERTY_MILESTONE, ADT_smf_milestone, NULL},
+ {SCF_PG_GENERAL, SCF_PROPERTY_ENABLED, 0, general_enable_id},
+ {SCF_PG_GENERAL_OVR, SCF_PROPERTY_ENABLED, 0, general_enable_id}
+};
+#define SPECIAL_PROP_COUNT (sizeof (special_props_list) /\
+ sizeof (audit_special_prop_item_t))
+#endif /* NATIVE_BUILD */
+
static void rc_node_unrefed(rc_node_t *np);
/*
@@ -588,6 +883,233 @@ rc_check_pgtype_name(const char *name)
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
+rc_node_free_fmri(rc_node_t *np)
+{
+ if (np->rn_fmri != NULL) {
+ free((void *)np->rn_fmri);
+ np->rn_fmri = NULL;
+ }
+}
+
+/*
+ * 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
+rc_concat_fmri_element(
+ char *fmri, /* base fmri */
+ size_t bufsize, /* size of buf at fmri */
+ size_t *sz_out, /* receives result size. */
+ const char *element, /* element name to concat */
+ rep_protocol_entity_t type) /* type of element */
+{
+ size_t actual;
+ const char *name = element;
+ int rc;
+ const char *separator;
+
+ if (bufsize > 0)
+ *sz_out = strlen(fmri);
+ else
+ *sz_out = 0;
+
+ switch (type) {
+ case REP_PROTOCOL_ENTITY_SCOPE:
+ if (strcmp(element, SCF_FMRI_LOCAL_SCOPE) == 0) {
+ /*
+ * No need to display scope information if we are
+ * in the local scope.
+ */
+ separator = SCF_FMRI_SVC_PREFIX;
+ name = NULL;
+ } else {
+ /*
+ * Need to display scope information, because it is
+ * not the local scope.
+ */
+ separator = SCF_FMRI_SVC_PREFIX SCF_FMRI_SCOPE_PREFIX;
+ }
+ break;
+ case REP_PROTOCOL_ENTITY_SERVICE:
+ separator = SCF_FMRI_SERVICE_PREFIX;
+ break;
+ case REP_PROTOCOL_ENTITY_INSTANCE:
+ separator = SCF_FMRI_INSTANCE_PREFIX;
+ break;
+ case REP_PROTOCOL_ENTITY_PROPERTYGRP:
+ case REP_PROTOCOL_ENTITY_CPROPERTYGRP:
+ separator = SCF_FMRI_PROPERTYGRP_PREFIX;
+ break;
+ case REP_PROTOCOL_ENTITY_PROPERTY:
+ separator = SCF_FMRI_PROPERTY_PREFIX;
+ break;
+ case REP_PROTOCOL_ENTITY_VALUE:
+ /*
+ * A value does not have a separate FMRI from its property,
+ * so there is nothing to concat.
+ */
+ return (REP_PROTOCOL_SUCCESS);
+ case REP_PROTOCOL_ENTITY_SNAPSHOT:
+ case REP_PROTOCOL_ENTITY_SNAPLEVEL:
+ /* Snapshots do not have FMRIs, so there is nothing to do. */
+ return (REP_PROTOCOL_SUCCESS);
+ default:
+ (void) fprintf(stderr, "%s:%d: Unknown protocol type %d.\n",
+ __FILE__, __LINE__, type);
+ abort(); /* Missing a case in switch if we get here. */
+ }
+
+ /* Concatenate separator and element to the fmri buffer. */
+
+ actual = strlcat(fmri, separator, bufsize);
+ if (name != NULL) {
+ if (actual < bufsize) {
+ actual = strlcat(fmri, name, bufsize);
+ } else {
+ actual += strlen(name);
+ }
+ }
+ if (actual < bufsize) {
+ rc = REP_PROTOCOL_SUCCESS;
+ } else {
+ rc = REP_PROTOCOL_FAIL_TRUNCATED;
+ }
+ *sz_out = actual;
+ 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
+rc_node_get_fmri_or_fragment(rc_node_t *np, char *buf, size_t bufsize,
+ size_t *sz_out)
+{
+ size_t fmri_len = 0;
+ int r;
+
+ if (bufsize > 0)
+ *buf = 0;
+ *sz_out = 0;
+
+ if (np->rn_fmri == NULL) {
+ /*
+ * 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.
+ */
+ assert(np->rn_parent == NULL);
+ r = rc_concat_fmri_element(buf, bufsize, &fmri_len, np->rn_name,
+ np->rn_id.rl_type);
+ if (r != REP_PROTOCOL_SUCCESS)
+ return (r);
+ } else {
+ /* We have an fmri, so return it. */
+ fmri_len = strlcpy(buf, np->rn_fmri, bufsize);
+ }
+
+ *sz_out = fmri_len;
+
+ if (fmri_len >= bufsize)
+ 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
+rc_node_build_fmri(rc_node_t *np)
+{
+ size_t actual;
+ char fmri[REP_PROTOCOL_FMRI_LEN];
+ int rc;
+ size_t sz = REP_PROTOCOL_FMRI_LEN;
+
+ assert(MUTEX_HELD(&np->rn_lock));
+ assert(np->rn_flags & RC_NODE_USING_PARENT);
+
+ rc_node_free_fmri(np);
+
+ rc = rc_node_get_fmri_or_fragment(np->rn_parent, fmri, sz, &actual);
+ assert(rc == REP_PROTOCOL_SUCCESS);
+
+ if (np->rn_name != NULL) {
+ rc = rc_concat_fmri_element(fmri, sz, &actual, np->rn_name,
+ np->rn_id.rl_type);
+ assert(rc == REP_PROTOCOL_SUCCESS);
+ np->rn_fmri = strdup(fmri);
+ } else {
+ np->rn_fmri = strdup(fmri);
+ }
+ if (np->rn_fmri == NULL) {
+ rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
+ } else {
+ rc = REP_PROTOCOL_SUCCESS;
+ }
+
+ 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
+rc_get_fmri_and_concat(rc_node_t *np, char *fmri, size_t size, size_t *sz_out,
+ const char *element, rep_protocol_entity_t type)
+{
+ int rc;
+
+ if ((rc = rc_node_get_fmri_or_fragment(np, fmri, size, sz_out)) !=
+ REP_PROTOCOL_SUCCESS) {
+ return (rc);
+ }
+ if ((rc = rc_concat_fmri_element(fmri, size, sz_out, element, type)) !=
+ REP_PROTOCOL_SUCCESS) {
+ return (rc);
+ }
+
+ return (REP_PROTOCOL_SUCCESS);
+}
+
static int
rc_notify_info_interested(rc_notify_info_t *rnip, rc_notify_t *np)
{
@@ -774,7 +1296,7 @@ pc_hash(const char *auth)
}
static int
-pc_exists(const permcheck_t *pcp, const char *auth)
+pc_exists(permcheck_t *pcp, const char *auth)
{
uint32_t h;
struct pc_elt *ep;
@@ -783,23 +1305,27 @@ pc_exists(const permcheck_t *pcp, const char *auth)
for (ep = pcp->pc_buckets[h & (pcp->pc_bnum - 1)];
ep != NULL;
ep = ep->pce_next) {
- if (strcmp(auth, ep->pce_auth) == 0)
+ if (strcmp(auth, ep->pce_auth) == 0) {
+ pcp->pc_auth_string = ep->pce_auth;
return (1);
+ }
}
return (0);
}
static int
-pc_match(const permcheck_t *pcp, const char *pattern)
+pc_match(permcheck_t *pcp, const char *pattern)
{
uint_t i;
struct pc_elt *ep;
for (i = 0; i < pcp->pc_bnum; ++i) {
for (ep = pcp->pc_buckets[i]; ep != NULL; ep = ep->pce_next) {
- if (_auth_match(pattern, ep->pce_auth))
+ if (_auth_match(pattern, ep->pce_auth)) {
+ pcp->pc_auth_string = ep->pce_auth;
return (1);
+ }
}
}
@@ -839,7 +1365,7 @@ pc_grow(permcheck_t *pcp)
}
static int
-pc_add(permcheck_t *pcp, const char *auth)
+pc_add(permcheck_t *pcp, const char *auth, pc_auth_type_t auth_type)
{
struct pc_elt *ep;
uint_t i;
@@ -859,6 +1385,11 @@ pc_add(permcheck_t *pcp, const char *auth)
ep->pce_next = pcp->pc_buckets[i];
pcp->pc_buckets[i] = ep;
+ if (auth_type > pcp->pc_specific_type) {
+ pcp->pc_specific_type = auth_type;
+ pcp->pc_specific = ep;
+ }
+
++pcp->pc_enum;
return (0);
@@ -888,12 +1419,23 @@ perm_auth_for_pgtype(const char *pgtype)
* _NO_RESOURCES - out of memory
*/
static int
-perm_add_enabling(permcheck_t *pcp, const char *auth)
+perm_add_enabling_type(permcheck_t *pcp, const char *auth,
+ pc_auth_type_t auth_type)
{
- return (pc_add(pcp, auth) == 0 ? REP_PROTOCOL_SUCCESS :
+ return (pc_add(pcp, auth, auth_type) == 0 ? REP_PROTOCOL_SUCCESS :
REP_PROTOCOL_FAIL_NO_RESOURCES);
}
+/*
+ * Fails with
+ * _NO_RESOURCES - out of memory
+ */
+static int
+perm_add_enabling(permcheck_t *pcp, const char *auth)
+{
+ return (perm_add_enabling_type(pcp, auth, PC_AUTH_SMF));
+}
+
/* Note that perm_add_enabling_values() is defined below. */
/*
@@ -904,7 +1446,7 @@ perm_add_enabling(permcheck_t *pcp, const char *auth)
* authorizations granted to an RBAC_AUTH_SEP-separated list of profiles.
*/
static int
-check_auth_list(const permcheck_t *pcp, char *authlist)
+check_auth_list(permcheck_t *pcp, char *authlist)
{
char *auth, *lasts;
int ret;
@@ -921,11 +1463,18 @@ check_auth_list(const permcheck_t *pcp, char *authlist)
return (ret);
}
+ /*
+ * If we failed, choose the most specific auth string for use in
+ * the audit event.
+ */
+ assert(pcp->pc_specific != NULL);
+ pcp->pc_auth_string = pcp->pc_specific->pce_auth;
+
return (0);
}
static int
-check_prof_list(const permcheck_t *pcp, char *proflist)
+check_prof_list(permcheck_t *pcp, char *proflist)
{
char *prof, *lasts, *authlist, *subproflist;
profattr_t *pap;
@@ -945,7 +1494,7 @@ check_prof_list(const permcheck_t *pcp, char *proflist)
if (!ret) {
subproflist = kva_match(pap->attr, PROFATTR_PROFS_KW);
if (subproflist != NULL)
- /* depth check to avoid invinite recursion? */
+ /* depth check to avoid infinite recursion? */
ret = check_prof_list(pcp, subproflist);
}
@@ -958,7 +1507,7 @@ check_prof_list(const permcheck_t *pcp, char *proflist)
}
static int
-perm_granted(const permcheck_t *pcp)
+perm_granted(permcheck_t *pcp)
{
ucred_t *uc;
@@ -1024,8 +1573,9 @@ perm_granted(const permcheck_t *pcp)
if (uap != NULL) {
/* Get the authorizations from user_attr. */
userattr_authlist = kva_match(uap->attr, USERATTR_AUTHS_KW);
- if (userattr_authlist != NULL)
+ if (userattr_authlist != NULL) {
ret = check_auth_list(pcp, userattr_authlist);
+ }
}
if (!ret && def_prof != NULL) {
@@ -1200,6 +1750,7 @@ rc_node_destroy(rc_node_t *np)
object_free_values(np->rn_values, np->rn_valtype,
np->rn_values_count, np->rn_values_size);
np->rn_values = NULL;
+ rc_node_free_fmri(np);
if (np->rn_snaplevel != NULL)
rc_snaplevel_rele(np->rn_snaplevel);
@@ -1245,6 +1796,7 @@ rc_node_link_child(rc_node_t *np, rc_node_t *cp)
cp->rn_parent = np;
cp->rn_flags |= RC_NODE_IN_PARENT;
(void) uu_list_insert_before(np->rn_children, NULL, cp);
+ (void) rc_node_build_fmri(cp);
(void) pthread_mutex_unlock(&np->rn_lock);
@@ -1338,6 +1890,7 @@ rc_node_relink_child(rc_node_t *pp, rc_node_t *np, rc_node_t *newp)
* keeps iterators on the list from missing us.
*/
(void) uu_list_insert_after(pp->rn_children, np, newp);
+ (void) rc_node_build_fmri(newp);
(void) uu_list_remove(pp->rn_children, np);
/*
@@ -1707,6 +2260,92 @@ rc_node_create_property(rc_node_t *pp, rc_node_lookup_t *nip,
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
+general_enable_id(tx_commit_data_t *tx_data, size_t cmd_no, const char *pg,
+ au_event_t *event_id)
+{
+ const char *value;
+ uint32_t nvalues;
+ int enable;
+
+ /*
+ * First, check property value.
+ */
+ if (tx_cmd_nvalues(tx_data, cmd_no, &nvalues) != REP_PROTOCOL_SUCCESS)
+ return (-1);
+ if (nvalues == 0)
+ return (-1);
+ if (tx_cmd_value(tx_data, cmd_no, 0, &value) != REP_PROTOCOL_SUCCESS)
+ return (-1);
+ if (strcmp(value, "0") == 0) {
+ enable = 0;
+ } else if (strcmp(value, "1") == 0) {
+ enable = 1;
+ } else {
+ return (-1);
+ }
+
+ /*
+ * Now check property group name.
+ */
+ if (strcmp(pg, SCF_PG_GENERAL) == 0) {
+ *event_id = enable ? ADT_smf_enable : ADT_smf_disable;
+ return (0);
+ } else if (strcmp(pg, SCF_PG_GENERAL_OVR) == 0) {
+ *event_id = enable ? ADT_smf_tmp_enable : ADT_smf_tmp_disable;
+ 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
+special_prop_compare(const void *item1, const void *item2)
+{
+ const audit_special_prop_item_t *a = (audit_special_prop_item_t *)item1;
+ const audit_special_prop_item_t *b = (audit_special_prop_item_t *)item2;
+ int r;
+
+ r = strcmp(a->api_prop_name, b->api_prop_name);
+ if (r == 0) {
+ /*
+ * Primary keys are the same, so check the secondary key.
+ */
+ r = strcmp(a->api_pg_name, b->api_pg_name);
+ }
+ return (r);
+}
+
int
rc_node_init(void)
{
@@ -1744,6 +2383,19 @@ rc_node_init(void)
if (rc_notify_list == NULL || rc_notify_info_list == NULL)
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
+ qsort(special_props_list, SPECIAL_PROP_COUNT,
+ sizeof (special_props_list[0]), special_prop_compare);
+#endif /* NATIVE_BUILD */
+
if ((np = rc_node_alloc()) == NULL)
uu_die("out of memory");
@@ -1924,7 +2576,9 @@ perm_add_pg_prop_values(permcheck_t *pcp, rc_node_t *pg, const char *propname)
for (count = prop->rn_values_count, cp = prop->rn_values;
count > 0;
--count) {
- result = perm_add_enabling(pcp, cp);
+ result = perm_add_enabling_type(pcp, cp,
+ (pg->rn_id.rl_ids[ID_INSTANCE]) ? PC_AUTH_INST :
+ PC_AUTH_SVC);
if (result != REP_PROTOCOL_SUCCESS)
break;
@@ -2088,10 +2742,20 @@ void
rc_node_ptr_init(rc_node_ptr_t *out)
{
out->rnp_node = NULL;
- out->rnp_authorized = 0;
+ out->rnp_auth_string = NULL;
+ out->rnp_authorized = RC_AUTH_UNKNOWN;
out->rnp_deleted = 0;
}
+void
+rc_node_ptr_free_mem(rc_node_ptr_t *npp)
+{
+ if (npp->rnp_auth_string != NULL) {
+ free((void *)npp->rnp_auth_string);
+ npp->rnp_auth_string = NULL;
+ }
+}
+
static void
rc_node_assign(rc_node_ptr_t *out, rc_node_t *val)
{
@@ -2101,7 +2765,8 @@ rc_node_assign(rc_node_ptr_t *out, rc_node_t *val)
out->rnp_node = val;
if (cur != NULL)
rc_node_rele(cur);
- out->rnp_authorized = 0;
+ out->rnp_authorized = RC_AUTH_UNKNOWN;
+ rc_node_ptr_free_mem(out);
out->rnp_deleted = 0;
}
@@ -2223,6 +2888,19 @@ rc_node_ptr_check_and_lock(rc_node_ptr_t *npp, int *res)
} \
}
+#define HOLD_PTR_FLAG_OR_FREE_AND_RETURN(np, npp, flag, mem) { \
+ assert(MUTEX_HELD(&(np)->rn_lock)); \
+ assert(!((np)->rn_flags & RC_NODE_DEAD)); \
+ if (!rc_node_hold_flag((np), flag)) { \
+ (void) pthread_mutex_unlock(&(np)->rn_lock); \
+ assert((np) == (npp)->rnp_node); \
+ rc_node_clear(npp, 1); \
+ if ((mem) != NULL) \
+ free((mem)); \
+ return (REP_PROTOCOL_FAIL_DELETED); \
+ } \
+}
+
int
rc_local_scope(uint32_t type, rc_node_ptr_t *out)
{
@@ -2638,39 +3316,534 @@ rc_node_update(rc_node_ptr_t *npp)
/*
* 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
-rc_node_modify_permission_check(void)
+rc_node_modify_permission_check(char **match_auth)
{
int rc = REP_PROTOCOL_SUCCESS;
permcheck_t *pcp;
int granted;
- if (!client_is_privileged()) {
+ *match_auth = NULL;
#ifdef NATIVE_BUILD
+ if (!client_is_privileged()) {
rc = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
+ }
+ return (rc);
#else
- pcp = pc_create();
- if (pcp != NULL) {
- rc = perm_add_enabling(pcp, AUTH_MODIFY);
+ if (is_main_repository == 0)
+ return (REP_PROTOCOL_SUCCESS);
+ pcp = pc_create();
+ if (pcp != NULL) {
+ rc = perm_add_enabling(pcp, AUTH_MODIFY);
- if (rc == REP_PROTOCOL_SUCCESS) {
- granted = perm_granted(pcp);
+ if (rc == REP_PROTOCOL_SUCCESS) {
+ granted = perm_granted(pcp);
- if (granted < 0)
+ if (granted < 0) {
+ rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
+ } else {
+ /*
+ * Copy off the authorization
+ * string before freeing pcp.
+ */
+ *match_auth =
+ strdup(pcp->pc_auth_string);
+ if (*match_auth == NULL)
rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
}
-
- pc_free(pcp);
- } else {
- rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
}
- if (rc == REP_PROTOCOL_SUCCESS && !granted)
- rc = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
-#endif /* NATIVE_BUILD */
+ pc_free(pcp);
+ } else {
+ rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
}
+
+ if (rc == REP_PROTOCOL_SUCCESS && !granted)
+ rc = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
+
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
+smf_annotation_event(int status, int return_val)
+{
+ adt_session_data_t *session;
+ adt_event_data_t *event = NULL;
+ 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;
+
+ if (client_annotation_needed(operation, sizeof (operation), file,
+ sizeof (file)) == 0) {
+ return;
+ }
+ if (file[0] == 0) {
+ (void) strlcpy(file, "NO FILE", sizeof (file));
+ }
+ if (operation[0] == 0) {
+ (void) strlcpy(operation, "NO OPERATION",
+ sizeof (operation));
+ }
+ if ((session = get_audit_session()) == NULL)
+ return;
+ if ((event = adt_alloc_event(session, ADT_smf_annotation)) == NULL) {
+ uu_warn("smf_annotation_event cannot allocate event "
+ "data. %s\n", strerror(errno));
+ return;
+ }
+ event->adt_smf_annotation.operation = operation;
+ event->adt_smf_annotation.file = file;
+ if (adt_put_event(event, status, return_val) == 0) {
+ client_annotation_finished();
+ } else {
+ uu_warn("smf_annotation_event failed to put event. "
+ "%s\n", strerror(errno));
+ }
+ adt_free_event(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
+_smf_audit_event(au_event_t event_id, int status, int return_val,
+ audit_event_data_t *data)
+{
+ char *auth_used;
+ char *fmri;
+ char *prop_value;
+ adt_session_data_t *session;
+ adt_event_data_t *event = NULL;
+
+ /* Don't audit if we're using an alternate repository */
+ if (is_main_repository == 0)
+ return;
+
+ smf_annotation_event(status, return_val);
+ if ((session = get_audit_session()) == NULL)
+ return;
+ if ((event = adt_alloc_event(session, event_id)) == NULL) {
+ uu_warn("_smf_audit_event cannot allocate event "
+ "data. %s\n", strerror(errno));
+ return;
+ }
+
+ /*
+ * Handle possibility of NULL authorization strings, FMRIs and
+ * property values.
+ */
+ if (data->ed_auth == NULL) {
+ auth_used = "PRIVILEGED";
+ } else {
+ auth_used = data->ed_auth;
+ }
+ if (data->ed_fmri == NULL) {
+ syslog(LOG_WARNING, "_smf_audit_event called with "
+ "empty FMRI string");
+ fmri = "UNKNOWN FMRI";
+ } else {
+ fmri = data->ed_fmri;
+ }
+ if (data->ed_prop_value == NULL) {
+ prop_value = "";
+ } else {
+ prop_value = data->ed_prop_value;
+ }
+
+ /* Fill in the event data. */
+ switch (event_id) {
+ case ADT_smf_attach_snap:
+ event->adt_smf_attach_snap.auth_used = auth_used;
+ event->adt_smf_attach_snap.old_fmri = data->ed_old_fmri;
+ event->adt_smf_attach_snap.old_name = data->ed_old_name;
+ event->adt_smf_attach_snap.new_fmri = fmri;
+ event->adt_smf_attach_snap.new_name = data->ed_snapname;
+ break;
+ case ADT_smf_change_prop:
+ event->adt_smf_change_prop.auth_used = auth_used;
+ event->adt_smf_change_prop.fmri = fmri;
+ event->adt_smf_change_prop.type = data->ed_type;
+ event->adt_smf_change_prop.value = prop_value;
+ break;
+ case ADT_smf_clear:
+ event->adt_smf_clear.auth_used = auth_used;
+ event->adt_smf_clear.fmri = fmri;
+ break;
+ case ADT_smf_create:
+ event->adt_smf_create.fmri = fmri;
+ event->adt_smf_create.auth_used = auth_used;
+ break;
+ case ADT_smf_create_npg:
+ event->adt_smf_create_npg.auth_used = auth_used;
+ event->adt_smf_create_npg.fmri = fmri;
+ event->adt_smf_create_npg.type = data->ed_type;
+ break;
+ case ADT_smf_create_pg:
+ event->adt_smf_create_pg.auth_used = auth_used;
+ event->adt_smf_create_pg.fmri = fmri;
+ event->adt_smf_create_pg.type = data->ed_type;
+ break;
+ case ADT_smf_create_prop:
+ event->adt_smf_create_prop.auth_used = auth_used;
+ event->adt_smf_create_prop.fmri = fmri;
+ event->adt_smf_create_prop.type = data->ed_type;
+ event->adt_smf_create_prop.value = prop_value;
+ break;
+ case ADT_smf_create_snap:
+ event->adt_smf_create_snap.auth_used = auth_used;
+ event->adt_smf_create_snap.fmri = fmri;
+ event->adt_smf_create_snap.name = data->ed_snapname;
+ break;
+ case ADT_smf_degrade:
+ event->adt_smf_degrade.auth_used = auth_used;
+ event->adt_smf_degrade.fmri = fmri;
+ break;
+ case ADT_smf_delete:
+ event->adt_smf_delete.fmri = fmri;
+ event->adt_smf_delete.auth_used = auth_used;
+ break;
+ case ADT_smf_delete_npg:
+ event->adt_smf_delete_npg.auth_used = auth_used;
+ event->adt_smf_delete_npg.fmri = fmri;
+ event->adt_smf_delete_npg.type = data->ed_type;
+ break;
+ case ADT_smf_delete_pg:
+ event->adt_smf_delete_pg.auth_used = auth_used;
+ event->adt_smf_delete_pg.fmri = fmri;
+ event->adt_smf_delete_pg.type = data->ed_type;
+ break;
+ case ADT_smf_delete_prop:
+ event->adt_smf_delete_prop.auth_used = auth_used;
+ event->adt_smf_delete_prop.fmri = fmri;
+ break;
+ case ADT_smf_delete_snap:
+ event->adt_smf_delete_snap.auth_used = auth_used;
+ event->adt_smf_delete_snap.fmri = fmri;
+ event->adt_smf_delete_snap.name = data->ed_snapname;
+ break;
+ case ADT_smf_disable:
+ event->adt_smf_disable.auth_used = auth_used;
+ event->adt_smf_disable.fmri = fmri;
+ break;
+ case ADT_smf_enable:
+ event->adt_smf_enable.auth_used = auth_used;
+ event->adt_smf_enable.fmri = fmri;
+ break;
+ case ADT_smf_immediate_degrade:
+ event->adt_smf_immediate_degrade.auth_used = auth_used;
+ event->adt_smf_immediate_degrade.fmri = fmri;
+ break;
+ case ADT_smf_immediate_maintenance:
+ event->adt_smf_immediate_maintenance.auth_used = auth_used;
+ event->adt_smf_immediate_maintenance.fmri = fmri;
+ break;
+ case ADT_smf_immtmp_maintenance:
+ event->adt_smf_immtmp_maintenance.auth_used = auth_used;
+ event->adt_smf_immtmp_maintenance.fmri = fmri;
+ break;
+ case ADT_smf_maintenance:
+ event->adt_smf_maintenance.auth_used = auth_used;
+ event->adt_smf_maintenance.fmri = fmri;
+ break;
+ case ADT_smf_milestone:
+ event->adt_smf_milestone.auth_used = auth_used;
+ event->adt_smf_milestone.fmri = fmri;
+ break;
+ case ADT_smf_read_prop:
+ event->adt_smf_read_prop.auth_used = auth_used;
+ event->adt_smf_read_prop.fmri = fmri;
+ break;
+ case ADT_smf_refresh:
+ event->adt_smf_refresh.auth_used = auth_used;
+ event->adt_smf_refresh.fmri = fmri;
+ break;
+ case ADT_smf_restart:
+ event->adt_smf_restart.auth_used = auth_used;
+ event->adt_smf_restart.fmri = fmri;
+ break;
+ case ADT_smf_tmp_disable:
+ event->adt_smf_tmp_disable.auth_used = auth_used;
+ event->adt_smf_tmp_disable.fmri = fmri;
+ break;
+ case ADT_smf_tmp_enable:
+ event->adt_smf_tmp_enable.auth_used = auth_used;
+ event->adt_smf_tmp_enable.fmri = fmri;
+ break;
+ case ADT_smf_tmp_maintenance:
+ event->adt_smf_tmp_maintenance.auth_used = auth_used;
+ event->adt_smf_tmp_maintenance.fmri = fmri;
+ break;
+ default:
+ abort(); /* Need to cover all SMF event IDs */
+ }
+
+ if (adt_put_event(event, status, return_val) != 0) {
+ uu_warn("_smf_audit_event failed to put event. %s\n",
+ strerror(errno));
+ }
+ adt_free_event(event);
+}
+
+/*
+ * 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
+special_property_event(audit_event_data_t *evdp, const char *prop_name,
+ char *pg_name, int status, int return_val, tx_commit_data_t *tx_data,
+ size_t cmd_no)
+{
+ au_event_t event_id;
+ audit_special_prop_item_t search_key;
+ audit_special_prop_item_t *found;
+
+ /* Use bsearch to find the special property information. */
+ search_key.api_prop_name = prop_name;
+ search_key.api_pg_name = pg_name;
+ found = (audit_special_prop_item_t *)bsearch(&search_key,
+ special_props_list, SPECIAL_PROP_COUNT,
+ sizeof (special_props_list[0]), special_prop_compare);
+ if (found == NULL) {
+ /* Not a special property. */
+ return;
+ }
+
+ /* Get the event id */
+ if (found->api_event_func == NULL) {
+ event_id = found->api_event_id;
+ } else {
+ if ((*found->api_event_func)(tx_data, cmd_no,
+ found->api_pg_name, &event_id) < 0)
+ return;
+ }
+
+ /* Generate the event. */
+ smf_audit_event(event_id, status, return_val, evdp);
+}
+#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 *
+generate_value_list(tx_commit_data_t *tx_data, size_t cmd_no)
+{
+ const char *cp;
+ const char *cur_value;
+ size_t byte_count = 0;
+ uint32_t i;
+ uint32_t nvalues;
+ size_t str_size = 0;
+ char *values = NULL;
+ char *vp;
+
+ if (tx_cmd_nvalues(tx_data, cmd_no, &nvalues) != REP_PROTOCOL_SUCCESS)
+ 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++) {
+ if (tx_cmd_value(tx_data, cmd_no, i, &cur_value) !=
+ REP_PROTOCOL_SUCCESS)
+ return (NULL);
+ for (cp = cur_value; *cp != 0; cp++) {
+ byte_count += (*cp == '"') ? 2 : 1;
+ }
+ byte_count += 3; /* surrounding quotes & space */
+ }
+ byte_count++; /* nul terminator */
+ values = malloc(byte_count);
+ if (values == NULL)
+ return (NULL);
+ *values = 0;
+
+ /* Now build up the string of values. */
+ for (i = 0; i < nvalues; i++) {
+ if (tx_cmd_value(tx_data, cmd_no, i, &cur_value) !=
+ REP_PROTOCOL_SUCCESS) {
+ free(values);
+ return (NULL);
+ }
+ (void) strlcat(values, "\"", byte_count);
+ for (cp = cur_value, vp = values + strlen(values);
+ *cp != 0; cp++) {
+ if (*cp == '"') {
+ *vp++ = '\\';
+ *vp++ = '"';
+ } else {
+ *vp++ = *cp;
+ }
+ }
+ *vp = 0;
+ str_size = strlcat(values, "\" ", byte_count);
+ assert(str_size < byte_count);
+ }
+ if (str_size > 0)
+ values[str_size - 1] = 0; /* get rid of trailing space */
+ 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
+generate_property_events(
+ tx_commit_data_t *tx_data,
+ char *pg_fmri, /* FMRI of property group */
+ char *auth_string,
+ int auth_status,
+ int auth_ret_value)
+{
+#ifndef NATIVE_BUILD
+ enum rep_protocol_transaction_action action;
+ audit_event_data_t audit_data;
+ size_t count;
+ size_t cmd_no;
+ char *cp;
+ au_event_t event_id;
+ 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;
+ uint32_t ptype;
+ char prop_type[3];
+ enum rep_protocol_responseid rc;
+ size_t sz_out;
+
+ /* Make sure we have something to do. */
+ if (tx_data == NULL)
+ return;
+ if ((count = tx_cmd_count(tx_data)) == 0)
+ return;
+
+ /* Copy the property group fmri */
+ pg_end = fmri;
+ pg_end += strlcpy(fmri, pg_fmri, sizeof (fmri));
+
+ /*
+ * Get the property group name. It is the first component after
+ * the last occurance of SCF_FMRI_PROPERTYGRP_PREFIX in the fmri.
+ */
+ cp = strstr(pg_fmri, SCF_FMRI_PROPERTYGRP_PREFIX);
+ if (cp == NULL) {
+ pg_name[0] = 0;
+ } else {
+ cp += strlen(SCF_FMRI_PROPERTYGRP_PREFIX);
+ (void) strlcpy(pg_name, cp, sizeof (pg_name));
+ }
+
+ audit_data.ed_auth = auth_string;
+ audit_data.ed_fmri = fmri;
+ audit_data.ed_type = prop_type;
+
+ /*
+ * Property type is two characters (see
+ * rep_protocol_value_type_t), so terminate the string.
+ */
+ prop_type[2] = 0;
+
+ for (cmd_no = 0; cmd_no < count; cmd_no++) {
+ /* Construct FMRI of the property */
+ *pg_end = 0;
+ if (tx_cmd_prop(tx_data, cmd_no, &prop_name) !=
+ REP_PROTOCOL_SUCCESS) {
+ continue;
+ }
+ rc = rc_concat_fmri_element(fmri, sizeof (fmri), &sz_out,
+ prop_name, REP_PROTOCOL_ENTITY_PROPERTY);
+ if (rc != REP_PROTOCOL_SUCCESS) {
+ /*
+ * If we can't get the FMRI, we'll abandon this
+ * command
+ */
+ continue;
+ }
+
+ /* Generate special property event if necessary. */
+ special_property_event(&audit_data, prop_name, pg_name,
+ auth_status, auth_ret_value, tx_data, cmd_no);
+
+ /* Capture rest of audit data. */
+ if (tx_cmd_prop_type(tx_data, cmd_no, &ptype) !=
+ REP_PROTOCOL_SUCCESS) {
+ continue;
+ }
+ prop_type[0] = REP_PROTOCOL_BASE_TYPE(ptype);
+ prop_type[1] = REP_PROTOCOL_SUBTYPE(ptype);
+ audit_data.ed_prop_value = generate_value_list(tx_data, cmd_no);
+
+ /* Determine the event type. */
+ if (tx_cmd_action(tx_data, cmd_no, &action) !=
+ REP_PROTOCOL_SUCCESS) {
+ free(audit_data.ed_prop_value);
+ continue;
+ }
+ switch (action) {
+ case REP_PROTOCOL_TX_ENTRY_NEW:
+ event_id = ADT_smf_create_prop;
+ break;
+ case REP_PROTOCOL_TX_ENTRY_CLEAR:
+ event_id = ADT_smf_change_prop;
+ break;
+ case REP_PROTOCOL_TX_ENTRY_REPLACE:
+ event_id = ADT_smf_change_prop;
+ break;
+ case REP_PROTOCOL_TX_ENTRY_DELETE:
+ event_id = ADT_smf_delete_prop;
+ break;
+ default:
+ assert(0); /* Missing a case */
+ free(audit_data.ed_prop_value);
+ continue;
+ }
+
+ /* Generate the event. */
+ smf_audit_event(event_id, auth_status, auth_ret_value,
+ &audit_data);
+ free(audit_data.ed_prop_value);
+ }
+#endif /* NATIVE_BUILD */
}
/*
@@ -2687,6 +3860,7 @@ rc_node_modify_permission_check(void)
* _BACKEND_ACCESS
* _BACKEND_READONLY
* _EXISTS - child already exists
+ * _TRUNCATED - truncated FMRI for the audit record
*/
int
rc_node_create_child(rc_node_ptr_t *npp, uint32_t type, const char *name,
@@ -2694,19 +3868,26 @@ rc_node_create_child(rc_node_ptr_t *npp, uint32_t type, const char *name,
{
rc_node_t *np;
rc_node_t *cp = NULL;
- int rc, perm_rc;
+ int rc, perm_rc;
+ size_t sz_out;
+ char fmri[REP_PROTOCOL_FMRI_LEN];
+ audit_event_data_t audit_data;
rc_node_clear(cpp, 0);
- perm_rc = rc_node_modify_permission_check();
+ perm_rc = rc_node_modify_permission_check(&audit_data.ed_auth);
RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);
+ audit_data.ed_fmri = fmri;
+ audit_data.ed_auth = NULL;
+
/*
* there is a separate interface for creating property groups
*/
if (type == REP_PROTOCOL_ENTITY_PROPERTYGRP) {
(void) pthread_mutex_unlock(&np->rn_lock);
+ free(audit_data.ed_auth);
return (REP_PROTOCOL_FAIL_NOT_APPLICABLE);
}
@@ -2719,19 +3900,31 @@ rc_node_create_child(rc_node_ptr_t *npp, uint32_t type, const char *name,
if ((rc = rc_check_parent_child(np->rn_id.rl_type, type)) !=
REP_PROTOCOL_SUCCESS) {
(void) pthread_mutex_unlock(&np->rn_lock);
+ free(audit_data.ed_auth);
return (rc);
}
if ((rc = rc_check_type_name(type, name)) != REP_PROTOCOL_SUCCESS) {
(void) pthread_mutex_unlock(&np->rn_lock);
+ free(audit_data.ed_auth);
return (rc);
}
+ if ((rc = rc_get_fmri_and_concat(np, fmri, sizeof (fmri), &sz_out,
+ name, type)) != REP_PROTOCOL_SUCCESS) {
+ (void) pthread_mutex_unlock(&np->rn_lock);
+ free(audit_data.ed_auth);
+ return (rc);
+ }
if (perm_rc != REP_PROTOCOL_SUCCESS) {
(void) pthread_mutex_unlock(&np->rn_lock);
+ smf_audit_event(ADT_smf_create, ADT_FAILURE,
+ ADT_FAIL_VALUE_AUTH, &audit_data);
+ free(audit_data.ed_auth);
return (perm_rc);
}
- HOLD_PTR_FLAG_OR_RETURN(np, npp, RC_NODE_CREATING_CHILD);
+ HOLD_PTR_FLAG_OR_FREE_AND_RETURN(np, npp, RC_NODE_CREATING_CHILD,
+ audit_data.ed_auth);
(void) pthread_mutex_unlock(&np->rn_lock);
rc = object_create(np, type, name, &cp);
@@ -2746,6 +3939,13 @@ rc_node_create_child(rc_node_ptr_t *npp, uint32_t type, const char *name,
rc_node_rele_flag(np, RC_NODE_CREATING_CHILD);
(void) pthread_mutex_unlock(&np->rn_lock);
+ if (rc == REP_PROTOCOL_SUCCESS) {
+ smf_audit_event(ADT_smf_create, ADT_SUCCESS, ADT_SUCCESS,
+ &audit_data);
+ }
+
+ free(audit_data.ed_auth);
+
return (rc);
}
@@ -2758,6 +3958,14 @@ rc_node_create_child_pg(rc_node_ptr_t *npp, uint32_t type, const char *name,
int rc;
permcheck_t *pcp;
int granted;
+ char fmri[REP_PROTOCOL_FMRI_LEN];
+ audit_event_data_t audit_data;
+ au_event_t event_id;
+ size_t sz_out;
+
+ audit_data.ed_auth = NULL;
+ audit_data.ed_fmri = fmri;
+ audit_data.ed_type = (char *)pgtype;
rc_node_clear(cpp, 0);
@@ -2783,10 +3991,23 @@ rc_node_create_child_pg(rc_node_ptr_t *npp, uint32_t type, const char *name,
return (rc);
}
- if (!client_is_privileged()) {
#ifdef NATIVE_BUILD
+ if (!client_is_privileged()) {
rc = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
+ }
#else
+ if (flags & SCF_PG_FLAG_NONPERSISTENT) {
+ event_id = ADT_smf_create_npg;
+ } else {
+ event_id = ADT_smf_create_pg;
+ }
+ if ((rc = rc_get_fmri_and_concat(np, fmri, sizeof (fmri), &sz_out,
+ name, REP_PROTOCOL_ENTITY_PROPERTYGRP)) != REP_PROTOCOL_SUCCESS) {
+ rc_node_rele(np);
+ return (rc);
+ }
+
+ if (is_main_repository) {
/* Must have .smf.modify or smf.modify.<type> authorization */
pcp = pc_create();
if (pcp != NULL) {
@@ -2820,8 +4041,28 @@ rc_node_create_child_pg(rc_node_ptr_t *npp, uint32_t type, const char *name,
if (rc == REP_PROTOCOL_SUCCESS) {
granted = perm_granted(pcp);
- if (granted < 0)
+ if (granted < 0) {
rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
+ } else {
+ /*
+ * Copy out the authorization
+ * string before freeing pcp.
+ */
+ audit_data.ed_auth =
+ strdup(pcp->pc_auth_string);
+ if (audit_data.ed_auth == NULL) {
+ /*
+ * Following code line
+ * cannot meet both the
+ * indentation and the line
+ * length requirements of
+ * cstyle. Indendation has
+ * been sacrificed.
+ */
+ /* CSTYLED */
+ rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
+ }
+ }
}
pc_free(pcp);
@@ -2831,16 +4072,23 @@ rc_node_create_child_pg(rc_node_ptr_t *npp, uint32_t type, const char *name,
if (rc == REP_PROTOCOL_SUCCESS && !granted)
rc = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
+ } else {
+ rc = REP_PROTOCOL_SUCCESS;
+ }
#endif /* NATIVE_BUILD */
- if (rc != REP_PROTOCOL_SUCCESS) {
- rc_node_rele(np);
- return (rc);
- }
+ if (rc != REP_PROTOCOL_SUCCESS) {
+ rc_node_rele(np);
+ smf_audit_event(event_id, ADT_FAILURE,
+ ADT_FAIL_VALUE_AUTH, &audit_data);
+ if (audit_data.ed_auth != NULL)
+ free(audit_data.ed_auth);
+ return (rc);
}
(void) pthread_mutex_lock(&np->rn_lock);
- HOLD_PTR_FLAG_OR_RETURN(np, npp, RC_NODE_CREATING_CHILD);
+ HOLD_PTR_FLAG_OR_FREE_AND_RETURN(np, npp, RC_NODE_CREATING_CHILD,
+ audit_data.ed_auth);
(void) pthread_mutex_unlock(&np->rn_lock);
rc = object_create_pg(np, type, name, pgtype, flags, &cp);
@@ -2854,6 +4102,13 @@ rc_node_create_child_pg(rc_node_ptr_t *npp, uint32_t type, const char *name,
rc_node_rele_flag(np, RC_NODE_CREATING_CHILD);
(void) pthread_mutex_unlock(&np->rn_lock);
+ if (rc == REP_PROTOCOL_SUCCESS) {
+ smf_audit_event(event_id, ADT_SUCCESS, ADT_SUCCESS,
+ &audit_data);
+ }
+ if (audit_data.ed_auth != NULL)
+ free(audit_data.ed_auth);
+
return (rc);
}
@@ -3032,6 +4287,7 @@ rc_node_finish_delete(rc_node_t *cp)
}
cp->rn_flags &= ~RC_NODE_IN_PARENT;
cp->rn_parent = NULL;
+ rc_node_free_fmri(cp);
}
cp->rn_flags |= RC_NODE_DEAD;
@@ -3245,6 +4501,35 @@ died:
rc_node_destroy(np);
}
+static au_event_t
+get_delete_event_id(rep_protocol_entity_t entity, uint32_t pgflags)
+{
+ au_event_t id = 0;
+
+#ifndef NATIVE_BUILD
+ switch (entity) {
+ case REP_PROTOCOL_ENTITY_SERVICE:
+ case REP_PROTOCOL_ENTITY_INSTANCE:
+ id = ADT_smf_delete;
+ break;
+ case REP_PROTOCOL_ENTITY_SNAPSHOT:
+ id = ADT_smf_delete_snap;
+ break;
+ case REP_PROTOCOL_ENTITY_PROPERTYGRP:
+ case REP_PROTOCOL_ENTITY_CPROPERTYGRP:
+ if (pgflags & SCF_PG_FLAG_NONPERSISTENT) {
+ id = ADT_smf_delete_npg;
+ } else {
+ id = ADT_smf_delete_pg;
+ }
+ break;
+ default:
+ abort();
+ }
+#endif /* NATIVE_BUILD */
+ return (id);
+}
+
/*
* Fails with
* _NOT_SET
@@ -3252,6 +4537,7 @@ died:
* _BAD_REQUEST
* _PERMISSION_DENIED
* _NO_RESOURCES
+ * _TRUNCATED
* and whatever object_delete() fails with.
*/
int
@@ -3265,13 +4551,35 @@ rc_node_delete(rc_node_ptr_t *npp)
rc_notify_delete_t *ndp;
permcheck_t *pcp;
int granted;
+ au_event_t event_id = 0;
+ size_t sz_out;
+ audit_event_data_t audit_data;
+ int audit_failure = 0;
RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);
+ audit_data.ed_fmri = NULL;
+ audit_data.ed_auth = NULL;
+ audit_data.ed_snapname = NULL;
+ audit_data.ed_type = NULL;
+
switch (np->rn_id.rl_type) {
case REP_PROTOCOL_ENTITY_SERVICE:
+ event_id = get_delete_event_id(REP_PROTOCOL_ENTITY_SERVICE,
+ np->rn_pgflags);
+ break;
case REP_PROTOCOL_ENTITY_INSTANCE:
+ event_id = get_delete_event_id(REP_PROTOCOL_ENTITY_INSTANCE,
+ np->rn_pgflags);
+ break;
case REP_PROTOCOL_ENTITY_SNAPSHOT:
+ event_id = get_delete_event_id(REP_PROTOCOL_ENTITY_SNAPSHOT,
+ np->rn_pgflags);
+ audit_data.ed_snapname = strdup(np->rn_name);
+ if (audit_data.ed_snapname == NULL) {
+ (void) pthread_mutex_unlock(&np->rn_lock);
+ return (REP_PROTOCOL_FAIL_NO_RESOURCES);
+ }
break; /* deletable */
case REP_PROTOCOL_ENTITY_SCOPE:
@@ -3284,11 +4592,22 @@ rc_node_delete(rc_node_ptr_t *npp)
(void) pthread_mutex_unlock(&np->rn_lock);
np = np->rn_cchain[0];
RC_NODE_CHECK_AND_LOCK(np);
+ event_id = get_delete_event_id(REP_PROTOCOL_ENTITY_CPROPERTYGRP,
+ np->rn_pgflags);
break;
case REP_PROTOCOL_ENTITY_PROPERTYGRP:
- if (np->rn_id.rl_ids[ID_SNAPSHOT] == 0)
+ if (np->rn_id.rl_ids[ID_SNAPSHOT] == 0) {
+ event_id =
+ get_delete_event_id(REP_PROTOCOL_ENTITY_PROPERTYGRP,
+ np->rn_pgflags);
+ audit_data.ed_type = strdup(np->rn_type);
+ if (audit_data.ed_type == NULL) {
+ (void) pthread_mutex_unlock(&np->rn_lock);
+ return (REP_PROTOCOL_FAIL_NO_RESOURCES);
+ }
break;
+ }
/* Snapshot property groups are indelible. */
(void) pthread_mutex_unlock(&np->rn_lock);
@@ -3304,6 +4623,11 @@ rc_node_delete(rc_node_ptr_t *npp)
break;
}
+ audit_data.ed_fmri = malloc(REP_PROTOCOL_FMRI_LEN);
+ if (audit_data.ed_fmri == NULL) {
+ rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
+ goto cleanout;
+ }
np_orig = np;
rc_node_hold_locked(np); /* simplifies rest of the code */
@@ -3318,7 +4642,8 @@ again:
if (!rc_node_wait_flag(np,
RC_NODE_IN_TX | RC_NODE_USING_PARENT)) {
rc_node_rele_locked(np);
- return (REP_PROTOCOL_FAIL_DELETED);
+ rc = REP_PROTOCOL_FAIL_DELETED;
+ goto cleanout;
}
if (np->rn_flags & RC_NODE_OLD) {
@@ -3337,7 +4662,7 @@ again:
if (!rc_node_hold_flag(np, RC_NODE_USING_PARENT)) {
rc_node_rele_locked(np);
rc_node_clear(npp, 1);
- return (REP_PROTOCOL_FAIL_DELETED);
+ rc = REP_PROTOCOL_FAIL_DELETED;
}
/*
@@ -3351,7 +4676,8 @@ again:
rc_node_rele(np);
rc_node_clear(npp, 1);
- return (REP_PROTOCOL_FAIL_DELETED);
+ rc = REP_PROTOCOL_FAIL_DELETED;
+ goto cleanout;
}
rc_node_hold_locked(pp); /* hold for later */
@@ -3382,13 +4708,21 @@ again:
assert(!(np->rn_flags & RC_NODE_OLD));
- if (!client_is_privileged()) {
- /* permission check */
+ if ((rc = rc_node_get_fmri_or_fragment(np, audit_data.ed_fmri,
+ REP_PROTOCOL_FMRI_LEN, &sz_out)) != REP_PROTOCOL_SUCCESS) {
+ rc_node_rele_flag(np, RC_NODE_DYING_FLAGS);
(void) pthread_mutex_unlock(&np->rn_lock);
+ goto fail;
+ }
#ifdef NATIVE_BUILD
+ if (!client_is_privileged()) {
rc = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
+ }
#else
+ if (is_main_repository) {
+ /* permission check */
+ (void) pthread_mutex_unlock(&np->rn_lock);
pcp = pc_create();
if (pcp != NULL) {
rc = perm_add_enabling(pcp, AUTH_MODIFY);
@@ -3406,8 +4740,28 @@ again:
if (rc == REP_PROTOCOL_SUCCESS) {
granted = perm_granted(pcp);
- if (granted < 0)
+ if (granted < 0) {
rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
+ } else {
+ /*
+ * Copy out the authorization
+ * string before freeing pcp.
+ */
+ audit_data.ed_auth =
+ strdup(pcp->pc_auth_string);
+ if (audit_data.ed_auth == NULL) {
+ /*
+ * Following code line
+ * cannot meet both the
+ * indentation and the line
+ * length requirements of
+ * cstyle. Indendation has
+ * been sacrificed.
+ */
+ /* CSTYLED */
+ rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
+ }
+ }
}
pc_free(pcp);
@@ -3415,18 +4769,20 @@ again:
rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
}
- if (rc == REP_PROTOCOL_SUCCESS && !granted)
+ if (rc == REP_PROTOCOL_SUCCESS && !granted) {
rc = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
-#endif /* NATIVE_BUILD */
-
- if (rc != REP_PROTOCOL_SUCCESS) {
- (void) pthread_mutex_lock(&np->rn_lock);
- rc_node_rele_flag(np, RC_NODE_DYING_FLAGS);
- (void) pthread_mutex_unlock(&np->rn_lock);
- goto fail;
+ audit_failure = 1;
}
-
(void) pthread_mutex_lock(&np->rn_lock);
+ } else {
+ rc = REP_PROTOCOL_SUCCESS;
+ }
+#endif /* NATIVE_BUILD */
+
+ if (rc != REP_PROTOCOL_SUCCESS) {
+ rc_node_rele_flag(np, RC_NODE_DYING_FLAGS);
+ (void) pthread_mutex_unlock(&np->rn_lock);
+ goto fail;
}
ndp = uu_zalloc(sizeof (*ndp));
@@ -3491,6 +4847,12 @@ again:
rc_node_rele(np);
+ smf_audit_event(event_id, ADT_SUCCESS, ADT_SUCCESS,
+ &audit_data);
+ free(audit_data.ed_auth);
+ free(audit_data.ed_snapname);
+ free(audit_data.ed_type);
+ free(audit_data.ed_fmri);
return (rc);
fail:
@@ -3502,6 +4864,15 @@ fail:
rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING);
rc_node_rele_locked(pp); /* drop ref and lock */
}
+ if (audit_failure) {
+ smf_audit_event(event_id, ADT_FAILURE,
+ ADT_FAIL_VALUE_AUTH, &audit_data);
+ }
+cleanout:
+ free(audit_data.ed_auth);
+ free(audit_data.ed_snapname);
+ free(audit_data.ed_type);
+ free(audit_data.ed_fmri);
return (rc);
}
@@ -3594,26 +4965,107 @@ rc_node_next_snaplevel(rc_node_ptr_t *npp, rc_node_ptr_t *cpp)
* 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
-rc_attach_snapshot(rc_node_t *np, uint32_t snapid, rc_node_t *parentp)
+rc_attach_snapshot(
+ rc_node_t *np,
+ uint32_t snapid,
+ rc_node_t *parentp,
+ char *old_fmri,
+ char *old_name)
{
rc_node_t *np_orig;
rc_node_t *nnp, *prev;
rc_node_t *pp;
int rc;
+ size_t sz_out;
+ au_event_t event_id;
+ audit_event_data_t audit_data;
- if (parentp != NULL)
+ if (parentp == NULL) {
+ assert(old_fmri != NULL);
+ } else {
assert(snapid == 0);
-
+ }
assert(MUTEX_HELD(&np->rn_lock));
+ /* Gather the audit data. */
+ /*
+ * ADT_smf_* symbols may not be defined in the /usr/include header
+ * files on the build machine. Thus, the following if-else will
+ * not be compiled when doing native builds.
+ */
+#ifndef NATIVE_BUILD
+ if (parentp == NULL) {
+ event_id = ADT_smf_attach_snap;
+ } else {
+ event_id = ADT_smf_create_snap;
+ }
+#endif /* NATIVE_BUILD */
+ audit_data.ed_fmri = malloc(REP_PROTOCOL_FMRI_LEN);
+ audit_data.ed_snapname = malloc(REP_PROTOCOL_NAME_LEN);
+ if ((audit_data.ed_fmri == NULL) || (audit_data.ed_snapname == NULL)) {
+ (void) pthread_mutex_unlock(&np->rn_lock);
+ free(audit_data.ed_fmri);
+ free(audit_data.ed_snapname);
+ return (REP_PROTOCOL_FAIL_NO_RESOURCES);
+ }
+ audit_data.ed_auth = NULL;
+ if (strlcpy(audit_data.ed_snapname, np->rn_name,
+ REP_PROTOCOL_NAME_LEN) >= REP_PROTOCOL_NAME_LEN) {
+ abort();
+ }
+ audit_data.ed_old_fmri = old_fmri;
+ audit_data.ed_old_name = old_name ? old_name : "NO NAME";
+
+ if (parentp == NULL) {
+ /*
+ * In the attach case, get the instance FMRIs of the
+ * snapshots.
+ */
+ if ((rc = rc_node_get_fmri_or_fragment(np, audit_data.ed_fmri,
+ REP_PROTOCOL_FMRI_LEN, &sz_out)) != REP_PROTOCOL_SUCCESS) {
+ (void) pthread_mutex_unlock(&np->rn_lock);
+ free(audit_data.ed_fmri);
+ free(audit_data.ed_snapname);
+ return (rc);
+ }
+ } else {
+ /*
+ * Capture the FMRI of the parent if we're actually going
+ * to take the snapshot.
+ */
+ if ((rc = rc_node_get_fmri_or_fragment(parentp,
+ audit_data.ed_fmri, REP_PROTOCOL_FMRI_LEN, &sz_out)) !=
+ REP_PROTOCOL_SUCCESS) {
+ (void) pthread_mutex_unlock(&np->rn_lock);
+ free(audit_data.ed_fmri);
+ free(audit_data.ed_snapname);
+ return (rc);
+ }
+ }
+
np_orig = np;
rc_node_hold_locked(np); /* simplifies the remainder */
(void) pthread_mutex_unlock(&np->rn_lock);
- if ((rc = rc_node_modify_permission_check()) != REP_PROTOCOL_SUCCESS)
- return (rc);
+ if ((rc = rc_node_modify_permission_check(&audit_data.ed_auth)) !=
+ REP_PROTOCOL_SUCCESS) {
+ smf_audit_event(event_id, ADT_FAILURE, ADT_FAIL_VALUE_AUTH,
+ &audit_data);
+ goto cleanout;
+ }
(void) pthread_mutex_lock(&np->rn_lock);
/*
@@ -3653,8 +5105,10 @@ again:
rc_node_rele_locked(np);
np = cache_lookup(&np_orig->rn_id);
- if (np == NULL)
- return (REP_PROTOCOL_FAIL_DELETED);
+ if (np == NULL) {
+ rc = REP_PROTOCOL_FAIL_DELETED;
+ goto cleanout;
+ }
(void) pthread_mutex_lock(&np->rn_lock);
}
@@ -3676,7 +5130,8 @@ again:
(void) pthread_mutex_lock(&pp->rn_lock);
rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING);
(void) pthread_mutex_unlock(&pp->rn_lock);
- return (REP_PROTOCOL_SUCCESS); /* nothing to do */
+ rc = REP_PROTOCOL_SUCCESS; /* nothing to do */
+ goto cleanout;
}
prev = np;
@@ -3741,8 +5196,14 @@ again:
rc_node_relink_child(pp, np, nnp);
rc_node_rele(np);
+ smf_audit_event(event_id, ADT_SUCCESS, ADT_SUCCESS, &audit_data);
+ rc = REP_PROTOCOL_SUCCESS;
- return (REP_PROTOCOL_SUCCESS);
+cleanout:
+ free(audit_data.ed_auth);
+ free(audit_data.ed_fmri);
+ free(audit_data.ed_snapname);
+ return (rc);
fail:
rc_node_rele_flag(np, RC_NODE_IN_TX);
@@ -3758,6 +5219,9 @@ fail:
rc_node_rele(nnp);
}
+ free(audit_data.ed_auth);
+ free(audit_data.ed_fmri);
+ free(audit_data.ed_snapname);
return (rc);
}
@@ -3768,20 +5232,25 @@ rc_snapshot_take_new(rc_node_ptr_t *npp, const char *svcname,
rc_node_t *np;
rc_node_t *outp = NULL;
int rc, perm_rc;
+ char fmri[REP_PROTOCOL_FMRI_LEN];
+ audit_event_data_t audit_data;
+ size_t sz_out;
rc_node_clear(outpp, 0);
- perm_rc = rc_node_modify_permission_check();
+ perm_rc = rc_node_modify_permission_check(&audit_data.ed_auth);
RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);
if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_INSTANCE) {
(void) pthread_mutex_unlock(&np->rn_lock);
+ free(audit_data.ed_auth);
return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
}
rc = rc_check_type_name(REP_PROTOCOL_ENTITY_SNAPSHOT, name);
if (rc != REP_PROTOCOL_SUCCESS) {
(void) pthread_mutex_unlock(&np->rn_lock);
+ free(audit_data.ed_auth);
return (rc);
}
@@ -3789,6 +5258,7 @@ rc_snapshot_take_new(rc_node_ptr_t *npp, const char *svcname,
rc_check_type_name(REP_PROTOCOL_ENTITY_SERVICE, svcname)) !=
REP_PROTOCOL_SUCCESS) {
(void) pthread_mutex_unlock(&np->rn_lock);
+ free(audit_data.ed_auth);
return (rc);
}
@@ -3796,15 +5266,30 @@ rc_snapshot_take_new(rc_node_ptr_t *npp, const char *svcname,
rc_check_type_name(REP_PROTOCOL_ENTITY_INSTANCE, instname)) !=
REP_PROTOCOL_SUCCESS) {
(void) pthread_mutex_unlock(&np->rn_lock);
+ free(audit_data.ed_auth);
return (rc);
}
+ audit_data.ed_auth = NULL;
+ audit_data.ed_fmri = fmri;
+ audit_data.ed_snapname = (char *)name;
+
+ if ((rc = rc_node_get_fmri_or_fragment(np, fmri, sizeof (fmri),
+ &sz_out)) != REP_PROTOCOL_SUCCESS) {
+ (void) pthread_mutex_unlock(&np->rn_lock);
+ free(audit_data.ed_auth);
+ return (rc);
+ }
if (perm_rc != REP_PROTOCOL_SUCCESS) {
(void) pthread_mutex_unlock(&np->rn_lock);
+ smf_audit_event(ADT_smf_create_snap, ADT_FAILURE,
+ ADT_FAIL_VALUE_AUTH, &audit_data);
+ free(audit_data.ed_auth);
return (perm_rc);
}
- HOLD_PTR_FLAG_OR_RETURN(np, npp, RC_NODE_CREATING_CHILD);
+ HOLD_PTR_FLAG_OR_FREE_AND_RETURN(np, npp, RC_NODE_CREATING_CHILD,
+ audit_data.ed_auth);
(void) pthread_mutex_unlock(&np->rn_lock);
rc = object_snapshot_take_new(np, svcname, instname, name, &outp);
@@ -3818,6 +5303,12 @@ rc_snapshot_take_new(rc_node_ptr_t *npp, const char *svcname,
rc_node_rele_flag(np, RC_NODE_CREATING_CHILD);
(void) pthread_mutex_unlock(&np->rn_lock);
+ if (rc == REP_PROTOCOL_SUCCESS) {
+ smf_audit_event(ADT_smf_create_snap, ADT_SUCCESS, ADT_SUCCESS,
+ &audit_data);
+ }
+ if (audit_data.ed_auth != NULL)
+ free(audit_data.ed_auth);
return (rc);
}
@@ -3837,7 +5328,8 @@ rc_snapshot_take_attach(rc_node_ptr_t *npp, rc_node_ptr_t *outpp)
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
}
- return (rc_attach_snapshot(outp, 0, np)); /* drops outp's lock */
+ return (rc_attach_snapshot(outp, 0, np, NULL,
+ NULL)); /* drops outp's lock */
}
int
@@ -3846,6 +5338,10 @@ rc_snapshot_attach(rc_node_ptr_t *npp, rc_node_ptr_t *cpp)
rc_node_t *np;
rc_node_t *cp;
uint32_t snapid;
+ char old_name[REP_PROTOCOL_NAME_LEN];
+ int rc;
+ size_t sz_out;
+ char old_fmri[REP_PROTOCOL_FMRI_LEN];
RC_NODE_PTR_GET_CHECK_AND_LOCK(np, npp);
if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPSHOT) {
@@ -3853,7 +5349,17 @@ rc_snapshot_attach(rc_node_ptr_t *npp, rc_node_ptr_t *cpp)
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
}
snapid = np->rn_snapshot_id;
+ rc = rc_node_get_fmri_or_fragment(np, old_fmri, sizeof (old_fmri),
+ &sz_out);
(void) pthread_mutex_unlock(&np->rn_lock);
+ if (rc != REP_PROTOCOL_SUCCESS)
+ return (rc);
+ if (np->rn_name != NULL) {
+ if (strlcpy(old_name, np->rn_name, sizeof (old_name)) >=
+ sizeof (old_name)) {
+ return (REP_PROTOCOL_FAIL_TRUNCATED);
+ }
+ }
RC_NODE_PTR_GET_CHECK_AND_LOCK(cp, cpp);
if (cp->rn_id.rl_type != REP_PROTOCOL_ENTITY_SNAPSHOT) {
@@ -3861,7 +5367,9 @@ rc_snapshot_attach(rc_node_ptr_t *npp, rc_node_ptr_t *cpp)
return (REP_PROTOCOL_FAIL_BAD_REQUEST);
}
- return (rc_attach_snapshot(cp, snapid, NULL)); /* drops cp's lock */
+ rc = rc_attach_snapshot(cp, snapid, NULL,
+ old_fmri, old_name); /* drops cp's lock */
+ return (rc);
}
/*
@@ -4090,6 +5598,8 @@ rc_node_property_may_read(rc_node_t *np)
int ret, granted = 0;
rc_node_t *pgp;
permcheck_t *pcp;
+ audit_event_data_t audit_data;
+ size_t sz_out;
if (np->rn_id.rl_type != REP_PROTOCOL_ENTITY_PROPERTY)
return (REP_PROTOCOL_FAIL_TYPE_MISMATCH);
@@ -4162,6 +5672,31 @@ rc_node_property_may_read(rc_node_t *np)
if (granted < 0)
ret = REP_PROTOCOL_FAIL_NO_RESOURCES;
}
+ if (ret == REP_PROTOCOL_SUCCESS) {
+ /* Generate a read_prop audit event. */
+ audit_data.ed_fmri = malloc(REP_PROTOCOL_FMRI_LEN);
+ if (audit_data.ed_fmri == NULL)
+ ret = REP_PROTOCOL_FAIL_NO_RESOURCES;
+ }
+ ret = rc_node_get_fmri_or_fragment(np, audit_data.ed_fmri,
+ REP_PROTOCOL_FMRI_LEN, &sz_out);
+ assert(ret == REP_PROTOCOL_SUCCESS);
+ if (ret == REP_PROTOCOL_SUCCESS) {
+ int status;
+ int ret_value;
+
+ if (granted == 0) {
+ status = ADT_FAILURE;
+ ret_value = ADT_FAIL_VALUE_AUTH;
+ } else {
+ status = ADT_SUCCESS;
+ ret_value = ADT_SUCCESS;
+ }
+ audit_data.ed_auth = pcp->pc_auth_string;
+ smf_audit_event(ADT_smf_read_prop,
+ status, ret_value, &audit_data);
+ }
+ free(audit_data.ed_fmri);
pc_free(pcp);
@@ -4898,7 +6433,8 @@ rc_node_setup_tx(rc_node_ptr_t *npp, rc_node_ptr_t *txp)
rc_node_t *np;
permcheck_t *pcp;
int ret;
- int authorized = 0;
+ rc_auth_state_t authorized = RC_AUTH_UNKNOWN;
+ char *auth_string = NULL;
RC_NODE_PTR_GET_CHECK_AND_HOLD(np, npp);
@@ -4918,13 +6454,15 @@ rc_node_setup_tx(rc_node_ptr_t *npp, rc_node_ptr_t *txp)
return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
}
+#ifdef NATIVE_BUILD
if (client_is_privileged())
goto skip_checks;
-
-#ifdef NATIVE_BUILD
rc_node_rele(np);
return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
#else
+ if (is_main_repository == 0)
+ goto skip_checks;
+
/* permission check */
pcp = pc_create();
if (pcp == NULL) {
@@ -4976,7 +6514,7 @@ rc_node_setup_tx(rc_node_ptr_t *npp, rc_node_ptr_t *txp)
}
if (strcmp(np->rn_name, AUTH_PG_ACTIONS) == 0)
- authorized = 1; /* Don't check on commit. */
+ authorized = RC_AUTH_PASSED; /* No check on commit. */
} else {
ret = perm_add_enabling(pcp, AUTH_MODIFY);
@@ -5013,21 +6551,45 @@ rc_node_setup_tx(rc_node_ptr_t *npp, rc_node_ptr_t *txp)
}
ret = perm_granted(pcp);
- if (ret != 1) {
- pc_free(pcp);
+ /*
+ * Copy out the authorization string before freeing pcp.
+ */
+ if (ret >= 0) {
+ auth_string = strdup(pcp->pc_auth_string);
+ }
+ pc_free(pcp);
+ if ((auth_string == NULL) || (ret < 0)) {
rc_node_rele(np);
- return (ret == 0 ? REP_PROTOCOL_FAIL_PERMISSION_DENIED :
- REP_PROTOCOL_FAIL_NO_RESOURCES);
+ return (REP_PROTOCOL_FAIL_NO_RESOURCES);
}
- pc_free(pcp);
+ 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.
+ */
+ authorized = RC_AUTH_FAILED;
+ }
#endif /* NATIVE_BUILD */
skip_checks:
rc_node_assign(txp, np);
txp->rnp_authorized = authorized;
+ if (authorized != RC_AUTH_UNKNOWN) {
+ /* Save the authorization string. */
+ if (txp->rnp_auth_string != NULL)
+ free((void *)txp->rnp_auth_string);
+ txp->rnp_auth_string = auth_string;
+ auth_string = NULL; /* Don't free until done with txp. */
+ }
rc_node_rele(np);
+ if (auth_string != NULL)
+ free(auth_string);
return (REP_PROTOCOL_SUCCESS);
}
@@ -5187,12 +6749,29 @@ rc_tx_commit(rc_node_ptr_t *txp, const void *cmds, size_t cmds_sz)
int rc;
permcheck_t *pcp;
int granted, normal;
+ char *pg_fmri = NULL;
+ char *auth_string = NULL;
+ int auth_status = ADT_SUCCESS;
+ int auth_ret_value = ADT_SUCCESS;
+ size_t sz_out;
+ int tx_flag = 1;
+ tx_commit_data_t *tx_data = NULL;
RC_NODE_CHECK(np);
- if (!client_is_privileged() && !txp->rnp_authorized) {
+ if ((txp->rnp_authorized != RC_AUTH_UNKNOWN) &&
+ (txp->rnp_auth_string != NULL)) {
+ auth_string = strdup(txp->rnp_auth_string);
+ if (auth_string == NULL)
+ return (REP_PROTOCOL_FAIL_NO_RESOURCES);
+ }
+
+ if ((txp->rnp_authorized == RC_AUTH_UNKNOWN) &&
+ is_main_repository) {
#ifdef NATIVE_BUILD
- return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
+ if (!client_is_privileged()) {
+ return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
+ }
#else
/* permission check: depends on contents of transaction */
pcp = pc_create();
@@ -5291,23 +6870,67 @@ rc_tx_commit(rc_node_ptr_t *txp, const void *cmds, size_t cmds_sz)
if (rc == REP_PROTOCOL_SUCCESS) {
granted = perm_granted(pcp);
- if (granted < 0)
+ if (granted < 0) {
rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
+ } else {
+ /*
+ * Copy out the authorization string before
+ * freeing pcp.
+ */
+ auth_string = strdup(pcp->pc_auth_string);
+ if (auth_string == NULL)
+ rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
+ }
}
pc_free(pcp);
if (rc != REP_PROTOCOL_SUCCESS)
- return (rc);
+ goto cleanout;
- if (!granted)
- return (REP_PROTOCOL_FAIL_PERMISSION_DENIED);
+ if (!granted) {
+ auth_status = ADT_FAILURE;
+ auth_ret_value = ADT_FAIL_VALUE_AUTH;
+ tx_flag = 0;
+ }
#endif /* NATIVE_BUILD */
+ } else if (txp->rnp_authorized == RC_AUTH_FAILED) {
+ auth_status = ADT_FAILURE;
+ auth_ret_value = ADT_FAIL_VALUE_AUTH;
+ tx_flag = 0;
+ }
+
+ pg_fmri = malloc(REP_PROTOCOL_FMRI_LEN);
+ if (pg_fmri == NULL) {
+ rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
+ goto cleanout;
+ }
+ if ((rc = rc_node_get_fmri_or_fragment(np, pg_fmri,
+ REP_PROTOCOL_FMRI_LEN, &sz_out)) != REP_PROTOCOL_SUCCESS) {
+ goto cleanout;
+ }
+
+ /*
+ * Parse the transaction commands into a useful form.
+ */
+ if ((rc = tx_commit_data_new(cmds, cmds_sz, &tx_data)) !=
+ REP_PROTOCOL_SUCCESS) {
+ goto cleanout;
+ }
+
+ if (tx_flag == 0) {
+ /* Authorization failed. Generate audit events. */
+ generate_property_events(tx_data, pg_fmri, auth_string,
+ auth_status, auth_ret_value);
+ rc = REP_PROTOCOL_FAIL_PERMISSION_DENIED;
+ goto cleanout;
}
nnp = rc_node_alloc();
- if (nnp == NULL)
- return (REP_PROTOCOL_FAIL_NO_RESOURCES);
+ if (nnp == NULL) {
+ rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
+ goto cleanout;
+ }
nnp->rn_id = np->rn_id; /* structure assignment */
nnp->rn_hash = np->rn_hash;
@@ -5319,10 +6942,12 @@ rc_tx_commit(rc_node_ptr_t *txp, const void *cmds, size_t cmds_sz)
if (nnp->rn_name == NULL || nnp->rn_type == NULL) {
rc_node_destroy(nnp);
- return (REP_PROTOCOL_FAIL_NO_RESOURCES);
+ rc = REP_PROTOCOL_FAIL_NO_RESOURCES;
+ goto cleanout;
}
(void) pthread_mutex_lock(&np->rn_lock);
+
/*
* We must have all of the old properties in the cache, or the
* database deletions could cause inconsistencies.
@@ -5331,20 +6956,22 @@ rc_tx_commit(rc_node_ptr_t *txp, const void *cmds, size_t cmds_sz)
REP_PROTOCOL_SUCCESS) {
(void) pthread_mutex_unlock(&np->rn_lock);
rc_node_destroy(nnp);
- return (rc);
+ goto cleanout;
}
if (!rc_node_hold_flag(np, RC_NODE_USING_PARENT)) {
(void) pthread_mutex_unlock(&np->rn_lock);
rc_node_destroy(nnp);
- return (REP_PROTOCOL_FAIL_DELETED);
+ rc = REP_PROTOCOL_FAIL_DELETED;
+ goto cleanout;
}
if (np->rn_flags & RC_NODE_OLD) {
rc_node_rele_flag(np, RC_NODE_USING_PARENT);
(void) pthread_mutex_unlock(&np->rn_lock);
rc_node_destroy(nnp);
- return (REP_PROTOCOL_FAIL_NOT_LATEST);
+ rc = REP_PROTOCOL_FAIL_NOT_LATEST;
+ goto cleanout;
}
pp = rc_node_hold_parent_flag(np, RC_NODE_CHILDREN_CHANGING);
@@ -5354,10 +6981,12 @@ rc_tx_commit(rc_node_ptr_t *txp, const void *cmds, size_t cmds_sz)
(void) pthread_mutex_lock(&np->rn_lock);
if (np->rn_flags & RC_NODE_OLD) {
(void) pthread_mutex_unlock(&np->rn_lock);
- return (REP_PROTOCOL_FAIL_NOT_LATEST);
+ rc = REP_PROTOCOL_FAIL_NOT_LATEST;
+ goto cleanout;
}
(void) pthread_mutex_unlock(&np->rn_lock);
- return (REP_PROTOCOL_FAIL_DELETED);
+ rc = REP_PROTOCOL_FAIL_DELETED;
+ goto cleanout;
}
(void) pthread_mutex_unlock(&pp->rn_lock);
@@ -5371,13 +7000,14 @@ rc_tx_commit(rc_node_ptr_t *txp, const void *cmds, size_t cmds_sz)
rc_node_rele_flag(pp, RC_NODE_CHILDREN_CHANGING);
(void) pthread_mutex_unlock(&pp->rn_lock);
rc_node_destroy(nnp);
- return (REP_PROTOCOL_FAIL_DELETED);
+ rc = REP_PROTOCOL_FAIL_DELETED;
+ goto cleanout;
}
nnp->rn_gen_id = np->rn_gen_id;
(void) pthread_mutex_unlock(&np->rn_lock);
/* Sets nnp->rn_gen_id on success. */
- rc = object_tx_commit(&np->rn_id, cmds, cmds_sz, &nnp->rn_gen_id);
+ rc = object_tx_commit(&np->rn_id, tx_data, &nnp->rn_gen_id);
(void) pthread_mutex_lock(&np->rn_lock);
if (rc != REP_PROTOCOL_SUCCESS) {
@@ -5390,7 +7020,7 @@ rc_tx_commit(rc_node_ptr_t *txp, const void *cmds, size_t cmds_sz)
rc_node_clear(txp, 0);
if (rc == REP_PROTOCOL_DONE)
rc = REP_PROTOCOL_SUCCESS; /* successful empty tx */
- return (rc);
+ goto cleanout;
}
/*
@@ -5415,8 +7045,16 @@ rc_tx_commit(rc_node_ptr_t *txp, const void *cmds, size_t cmds_sz)
* all done -- clear the transaction.
*/
rc_node_clear(txp, 0);
+ generate_property_events(tx_data, pg_fmri, auth_string,
+ auth_status, auth_ret_value);
- return (REP_PROTOCOL_SUCCESS);
+ rc = REP_PROTOCOL_SUCCESS;
+
+cleanout:
+ free(auth_string);
+ free(pg_fmri);
+ tx_commit_data_free(tx_data);
+ return (rc);
}
void
diff --git a/usr/src/cmd/svc/startd/libscf.c b/usr/src/cmd/svc/startd/libscf.c
index c94080325b..741d17cfe6 100644
--- a/usr/src/cmd/svc/startd/libscf.c
+++ b/usr/src/cmd/svc/startd/libscf.c
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -257,6 +257,7 @@ lookup:
goto out;
case SCF_ERROR_HANDLE_MISMATCH:
+ case SCF_ERROR_INTERNAL:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_SET:
bad_error("_scf_snapshot_take_new",
diff --git a/usr/src/cmd/svc/startd/startd.c b/usr/src/cmd/svc/startd/startd.c
index 04078b059f..bb098d8f59 100644
--- a/usr/src/cmd/svc/startd/startd.c
+++ b/usr/src/cmd/svc/startd/startd.c
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -56,6 +56,14 @@
* all libscf state associated with that handle, so functions which do this
* should communicate the event to their callers (usually by returning
* ECONNRESET) so they may reset their state appropriately.
+ *
+ * External references
+ *
+ * svc.configd generates special security audit events for changes to some
+ * restarter related properties. See the special_props_list array in
+ * usr/src/cmd/svc/configd/rc_node.c for the properties that cause these audit
+ * events. If you change the semantics of these propereties within startd, you
+ * will probably need to update rc_node.c
*/
#include <stdio.h>
diff --git a/usr/src/cmd/svc/svccfg/svccfg.h b/usr/src/cmd/svc/svccfg/svccfg.h
index e15fc47afa..ee26bc6dcf 100644
--- a/usr/src/cmd/svc/svccfg/svccfg.h
+++ b/usr/src/cmd/svc/svccfg/svccfg.h
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -327,7 +327,7 @@ void lscf_cleanup(void);
void lscf_prep_hndl(void);
void lscf_init(void);
int lscf_bundle_import(bundle_t *, const char *, uint_t);
-int lscf_bundle_apply(bundle_t *);
+int lscf_bundle_apply(bundle_t *, const char *);
void lscf_delete(const char *, int);
void lscf_list(const char *);
void lscf_select(const char *);
diff --git a/usr/src/cmd/svc/svccfg/svccfg_engine.c b/usr/src/cmd/svc/svccfg/svccfg_engine.c
index 4d69b7941f..cf7ba71437 100644
--- a/usr/src/cmd/svc/svccfg/svccfg_engine.c
+++ b/usr/src/cmd/svc/svccfg/svccfg_engine.c
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -629,7 +629,7 @@ engine_apply(const char *file)
return (-1);
}
- if (lscf_bundle_apply(b) != 0) {
+ if (lscf_bundle_apply(b, file) != 0) {
internal_bundle_free(b);
return (-1);
}
diff --git a/usr/src/cmd/svc/svccfg/svccfg_libscf.c b/usr/src/cmd/svc/svccfg/svccfg_libscf.c
index 0397af151c..8463b26331 100644
--- a/usr/src/cmd/svc/svccfg/svccfg_libscf.c
+++ b/usr/src/cmd/svc/svccfg/svccfg_libscf.c
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -5044,6 +5044,7 @@ again:
return (-1);
case SCF_ERROR_NOT_SET:
+ case SCF_ERROR_INTERNAL:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_HANDLE_MISMATCH:
bad_error("_scf_snapshot_take_new",
@@ -5525,6 +5526,7 @@ nosnap:
goto deltemp;
case SCF_ERROR_NOT_SET:
+ case SCF_ERROR_INTERNAL:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_HANDLE_MISMATCH:
bad_error("_scf_snapshot_take_new",
@@ -6717,6 +6719,7 @@ lscf_bundle_import(bundle_t *bndl, const char *filename, uint_t flags)
int r;
pgroup_t *old_dpt;
void *cookie;
+ int annotation_set = 0;
const char * const emsg_nomem = gettext("Out of memory.\n");
const char * const emsg_nores =
@@ -6803,6 +6806,35 @@ lscf_bundle_import(bundle_t *bndl, const char *filename, uint_t flags)
}
}
+ /* Set up the auditing annotation. */
+ if (_scf_set_annotation(g_hndl, "svccfg import", filename) == 0) {
+ annotation_set = 1;
+ } else {
+ switch (scf_error()) {
+ case SCF_ERROR_CONNECTION_BROKEN:
+ warn(gettext("Repository connection broken.\n"));
+ repository_teardown();
+ result = -1;
+ goto out;
+
+ case SCF_ERROR_INVALID_ARGUMENT:
+ case SCF_ERROR_NOT_BOUND:
+ case SCF_ERROR_NO_RESOURCES:
+ case SCF_ERROR_INTERNAL:
+ bad_error("_scf_set_annotation", scf_error());
+ /* NOTREACHED */
+
+ default:
+ /*
+ * Do not terminate import because of inability to
+ * generate annotation audit event.
+ */
+ warn(gettext("_scf_set_annotation() unexpectedly "
+ "failed with return code of %d\n"), scf_error());
+ break;
+ }
+ }
+
/*
* Clear the sc_import_state's of all services & instances so we can
* report how far we got if we fail.
@@ -6954,6 +6986,10 @@ progress:
result = -1;
out:
+ if (annotation_set != 0) {
+ /* Turn off annotation. It is no longer needed. */
+ (void) _scf_set_annotation(g_hndl, NULL, NULL);
+ }
load_fini();
free(ud_ctarg);
@@ -7033,12 +7069,13 @@ out:
* -1 - lscf_import_instance_pgs() failed.
*/
int
-lscf_bundle_apply(bundle_t *bndl)
+lscf_bundle_apply(bundle_t *bndl, const char *file)
{
entity_t *svc, *inst;
scf_scope_t *rscope;
scf_service_t *rsvc;
scf_instance_t *rinst;
+ int annotation_set = 0;
int r;
lscf_prep_hndl();
@@ -7053,6 +7090,36 @@ lscf_bundle_apply(bundle_t *bndl)
if (scf_handle_get_scope(g_hndl, SCF_SCOPE_LOCAL, rscope) != 0)
scfdie();
+ /*
+ * Set the strings to be used for the security audit annotation
+ * event.
+ */
+ if (_scf_set_annotation(g_hndl, "svccfg apply", file) == 0) {
+ annotation_set = 1;
+ } else {
+ switch (scf_error()) {
+ case SCF_ERROR_CONNECTION_BROKEN:
+ warn(gettext("Repository connection broken.\n"));
+ goto out;
+
+ case SCF_ERROR_INVALID_ARGUMENT:
+ case SCF_ERROR_NOT_BOUND:
+ case SCF_ERROR_NO_RESOURCES:
+ case SCF_ERROR_INTERNAL:
+ bad_error("_scf_set_annotation", scf_error());
+ /* NOTREACHED */
+
+ default:
+ /*
+ * Do not abort apply operation because of
+ * inability to create annotation audit event.
+ */
+ warn(gettext("_scf_set_annotation() unexpectedly "
+ "failed with return code of %d\n"), scf_error());
+ break;
+ }
+ }
+
for (svc = uu_list_first(bndl->sc_bundle_services);
svc != NULL;
svc = uu_list_next(bndl->sc_bundle_services, svc)) {
@@ -7148,6 +7215,11 @@ lscf_bundle_apply(bundle_t *bndl)
}
out:
+ if (annotation_set) {
+ /* Remove security audit annotation strings. */
+ (void) _scf_set_annotation(g_hndl, NULL, NULL);
+ }
+
scf_transaction_destroy(imp_tx);
imp_tx = NULL;
scf_pg_destroy(imp_pg);
diff --git a/usr/src/common/svc/repcache_protocol.h b/usr/src/common/svc/repcache_protocol.h
index cdceb76456..9f76adb816 100644
--- a/usr/src/common/svc/repcache_protocol.h
+++ b/usr/src/common/svc/repcache_protocol.h
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -353,6 +353,12 @@
*
* BACKUP(name) -> result
* Backs up the persistant repository with a particular name.
+ *
+ * SET_ANNOTATION(operation, file)
+ * Set up a security audit annotation event. operation is the name of
+ * the operation that is being annotated, and file is the file being
+ * processed. This will be used to mark operations which comprise
+ * multiple primitive operations such as svccfg import.
*/
#include <door.h>
@@ -376,7 +382,7 @@ extern "C" {
* This value should be incremented any time the protocol changes. When in
* doubt, bump it.
*/
-#define REPOSITORY_DOOR_VERSION (19 + REPOSITORY_DOOR_BASEVER)
+#define REPOSITORY_DOOR_VERSION (20 + REPOSITORY_DOOR_BASEVER)
/*
* flags for rdr_flags
@@ -470,6 +476,8 @@ enum rep_protocol_requestid {
REP_PROTOCOL_BACKUP,
+ REP_PROTOCOL_SET_AUDIT_ANNOTATION,
+
REP_PROTOCOL_MAX_REQUEST
};
@@ -808,6 +816,12 @@ struct rep_protocol_backup_request {
char rpr_name[REP_PROTOCOL_NAME_LEN];
};
+struct rep_protocol_annotation {
+ enum rep_protocol_requestid rpr_request; /* SET_ANNOTATION */
+ char rpr_operation[REP_PROTOCOL_NAME_LEN];
+ char rpr_file[MAXPATHLEN];
+};
+
/*
* Response structures
*/
diff --git a/usr/src/lib/libbsm/audit_event.txt b/usr/src/lib/libbsm/audit_event.txt
index 9664c3b9e1..6614459b90 100644
--- a/usr/src/lib/libbsm/audit_event.txt
+++ b/usr/src/lib/libbsm/audit_event.txt
@@ -1,5 +1,5 @@
#
-# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
#
@@ -452,6 +452,42 @@
6249:AUE_ndmp_backup:ndmp backup:na
6250:AUE_ndmp_restore:ndmp restore:na
#
+# SMF(5) svc.configd events (svcadm(1M) related)
+#
+6260:AUE_smf_enable:persistently enable service instance:ss
+6261:AUE_smf_tmp_enable:temporarily enable service instance:ss
+6262:AUE_smf_disable:persistently disable service instance:ss
+6263:AUE_smf_tmp_disable:temporarily disable service instance:ss
+6264:AUE_smf_restart:restart service instance:ss
+6265:AUE_smf_refresh:refresh service instance:ss
+6266:AUE_smf_clear:clear service instance state:ss
+6267:AUE_smf_degrade:set service instance degraded state:ss
+6268:AUE_smf_immediate_degrade:immediately set service instance degraded state:ss
+6269:AUE_smf_maintenance:set service instance persistent maintenance state:ss
+6270:AUE_smf_immediate_maintenance:immediately set service instance persistent maintenance state:ss
+6271:AUE_smf_immtmp_maintenance:immediately set service instance temporary maintenance state:ss
+6272:AUE_smf_tmp_maintenance:set service instance maintenance temporary state:ss
+6273:AUE_smf_milestone:set service management facility milestone:ss
+#
+# SMF(5) svc.configd miscellaneous events
+#
+6275:AUE_smf_read_prop:read restricted access property value:as
+#
+# SMF(5) svc.configd events (svccfg(1M) related)
+#
+6280:AUE_smf_create:create service instance object:as
+6281:AUE_smf_delete:delete service instance object:as
+6282:AUE_smf_create_pg:create persistent service property group:as
+6283:AUE_smf_create_npg:create non-persistent service property group:as
+6284:AUE_smf_delete_pg:delete persistent service property group:as
+6285:AUE_smf_delete_npg:delete non-persistent service property group:as
+6286:AUE_smf_create_snap:create repository snapshot:as
+6287:AUE_smf_delete_snap:delete repository snapshot:as
+6288:AUE_smf_attach_snap:attach repository snapshot:as
+6289:AUE_smf_annotation:annotate transaction:as,ss
+6290:AUE_smf_create_prop:create service instance property:as
+6291:AUE_smf_change_prop:change service instance property:as
+6292:AUE_smf_delete_prop:delete service instance property:as
#
# Trusted Extensions events:
#
diff --git a/usr/src/lib/libbsm/auditxml b/usr/src/lib/libbsm/auditxml
index 8681d98b35..c78cc9ae43 100644
--- a/usr/src/lib/libbsm/auditxml
+++ b/usr/src/lib/libbsm/auditxml
@@ -20,7 +20,7 @@
# CDDL HEADER END
#
#
-# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# ident "%Z%%M% %I% %E% SMI"
@@ -534,6 +534,7 @@ extern void adt_set_termid(const adt_session_data_t *,
extern void adt_get_asid(const adt_session_data_t *, au_asid_t *);
extern void adt_set_asid(const adt_session_data_t *, const au_asid_t);
+extern au_id_t adt_get_unique_id(au_id_t);
#endif
diff --git a/usr/src/lib/libbsm/common/adt.c b/usr/src/lib/libbsm/common/adt.c
index 473ecbcc51..aa1b01751b 100644
--- a/usr/src/lib/libbsm/common/adt.c
+++ b/usr/src/lib/libbsm/common/adt.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -192,8 +192,8 @@ adt_get_mask_from_user(uid_t uid, au_mask_t *mask)
* see a need to put a lock around it.
*/
-static au_id_t
-adt_get_unique_id(uid_t uid)
+au_id_t
+adt_get_unique_id(au_id_t uid)
{
char hostname[MAXHOSTNAMELEN];
union {
@@ -480,10 +480,10 @@ adt_set_termid(const adt_session_data_t *session_data,
ADT_VALID);
((adt_internal_state_t *)session_data)->as_info.ai_termid =
- *termid;
+ *termid;
((adt_internal_state_t *)session_data)->as_have_user_data |=
- ADT_HAVE_TID;
+ ADT_HAVE_TID;
}
}
@@ -649,7 +649,7 @@ adt_get_hostIP(const char *hostname, au_tid_addr_t *p_term)
case AF_INET6:
/* LINTED */
p = &((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr,
- (void) memcpy(p_term->at_addr, p,
+ (void) memcpy(p_term->at_addr, p,
sizeof (((struct sockaddr_in6 *)NULL)->sin6_addr));
p_term->at_type = AU_IPv6;
break;
@@ -1515,9 +1515,9 @@ adt_changeuser(adt_internal_state_t *state, uid_t ruid)
state->as_info.ai_mask.am_failure |= mask.am_failure;
}
DPRINTF(("changed mask to %08X/%08X for ruid=%d\n",
- state->as_info.ai_mask.am_success,
- state->as_info.ai_mask.am_failure,
- ruid));
+ state->as_info.ai_mask.am_success,
+ state->as_info.ai_mask.am_failure,
+ ruid));
return (0);
}
diff --git a/usr/src/lib/libbsm/common/adt.xml b/usr/src/lib/libbsm/common/adt.xml
index 85a5e0cbd9..589eb9744b 100644
--- a/usr/src/lib/libbsm/common/adt.xml
+++ b/usr/src/lib/libbsm/common/adt.xml
@@ -20,7 +20,7 @@
CDDL HEADER END
-Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+Copyright 2008 Sun Microsystems, Inc. All rights reserved.
Use is subject to license terms.
ident "%Z%%M% %I% %E% SMI"
@@ -1386,8 +1386,378 @@ Use is subject to license terms.
</entry>
</event>
+<!-- SMF related events -->
+ <event id="AUE_smf_generic" type="generic" omit="always">
+ <!--
+ This is a template for the event types that have no tokens
+ other than the header and return. There is no allowed_type
+ list because the template is not externally visible due to the
+ omit="always".
+ -->
+ <entry id="subject">
+ <internal token="subject"/>
+ <external opt="none"/>
+ </entry>
+ <entry id="auth_used">
+ <internal token="uauth"/>
+ <external opt="required" type="char *"/>
+ <comment>authorization used</comment>
+ </entry>
+ <entry id="fmri">
+ <internal token="fmri"/>
+ <external opt="required" type="char *"/>
+ <comment>name</comment>
+ </entry>
+ <entry id="return">
+ <internal token="return"/>
+ <external opt="none"/>
+ </entry>
+ </event>
+
+ <event id="AUE_smf_generic_pg" type="generic" omit="always">
+ <!--
+ This is a template for the event types related to property groups.
+ There is no allowed_type list because the template is not externally
+ visible due to the omit="always".
+ -->
+ <entry id="subject">
+ <internal token="subject"/>
+ <external opt="none"/>
+ </entry>
+ <entry id="auth_used">
+ <internal token="uauth"/>
+ <external opt="required" type="char *"/>
+ <comment>authorization used</comment>
+ </entry>
+ <entry id="fmri">
+ <internal token="fmri"/>
+ <external opt="required" type="char *"/>
+ </entry>
+ <entry id="type">
+ <internal token="text"/>
+ <external opt="required" type="char *"/>
+ <comment>property group type</comment>
+ </entry>
+ <entry id="return">
+ <internal token="return"/>
+ <external opt="none"/>
+ </entry>
+ </event>
+
+ <event id="AUE_smf_enable" instance_of="AUE_smf_generic" header="0"
+ idNo="65" omit="JNI">
+ <program>svc.configd(1M)</program>
+ <see>svcadm(1M)</see>
+ </event>
+ <event id="AUE_smf_tmp_enable" instance_of="AUE_smf_generic" header="0"
+ idNo="66" omit="JNI">
+ <program>svc.configd(1M)</program>
+ <see>svcadm(1M)</see>
+ </event>
+ <event id="AUE_smf_disable" instance_of="AUE_smf_generic" header="0"
+ idNo="67" omit="JNI">
+ <program>svc.configd(1M)</program>
+ <see>svcadm(1M)</see>
+ </event>
+ <event id="AUE_smf_tmp_disable" instance_of="AUE_smf_generic" header="0"
+ idNo="68" omit="JNI">
+ <program>svc.configd(1M)</program>
+ <see>svcadm(1M)</see>
+ </event>
+ <event id="AUE_smf_restart" instance_of="AUE_smf_generic" header="0"
+ idNo="69" omit="JNI">
+ <program>svc.configd(1M)</program>
+ <see>svcadm(1M)</see>
+ </event>
+ <event id="AUE_smf_refresh" instance_of="AUE_smf_generic" header="0"
+ idNo="70" omit="JNI">
+ <program>svc.configd(1M)</program>
+ <see>svcadm(1M)</see>
+ </event>
+ <event id="AUE_smf_clear" instance_of="AUE_smf_generic" header="0"
+ idNo="71" omit="JNI">
+ <program>svc.configd(1M)</program>
+ <see>svcadm(1M)</see>
+ </event>
+ <event id="AUE_smf_degrade" instance_of="AUE_smf_generic" header="0"
+ idNo="72" omit="JNI">
+ <program>svc.configd(1M)</program>
+ <see>svcadm(1M)</see>
+ </event>
+ <event id="AUE_smf_immediate_degrade" instance_of="AUE_smf_generic"
+ header="0" idNo="73" omit="JNI">
+ <program>svc.configd(1M)</program>
+ <see>svcadm(1M)</see>
+ </event>
+ <event id="AUE_smf_maintenance" instance_of="AUE_smf_generic" header="0"
+ idNo="74" omit="JNI">
+ <program>svc.configd(1M)</program>
+ <see>svcadm(1M)</see>
+ </event>
+ <event id="AUE_smf_immediate_maintenance" instance_of="AUE_smf_generic"
+ header="0" idNo="75" omit="JNI">
+ <program>svc.configd(1M)</program>
+ <see>svcadm(1M)</see>
+ </event>
+ <event id="AUE_smf_immtmp_maintenance" instance_of="AUE_smf_generic"
+ header="0" idNo="76" omit="JNI">
+ <program>svc.configd(1M)</program>
+ <see>svcadm(1M)</see>
+ </event>
+ <event id="AUE_smf_tmp_maintenance" instance_of="AUE_smf_generic" header="0"
+ idNo="77" omit="JNI">
+ <program>svc.configd(1M)</program>
+ <see>svcadm(1M)</see>
+ </event>
+ <event id="AUE_smf_milestone" instance_of="AUE_smf_generic" header="0"
+ idNo="78" omit="JNI">
+ <program>svc.configd(1M)</program>
+ <see>svcadm(1M)</see>
+ </event>
+
+ <event id="AUE_smf_create" instance_of="AUE_smf_generic" header="0"
+ idNo="79" omit="JNI">
+ <program>svc.configd(1M)</program>
+ <see>svccfg(1M)</see>
+ </event>
+ <event id="AUE_smf_delete" instance_of="AUE_smf_generic" header="0"
+ idNo="80" omit="JNI">
+ <program>svc.configd(1M)</program>
+ <see>svccfg(1M)</see>
+ </event>
+
+ <event id="AUE_smf_create_pg" instance_of="AUE_smf_generic_pg" header="0"
+ idNo="81" omit="JNI">
+ <program>svc.configd(1M)</program>
+ <see>svccfg(1M)</see>
+ </event>
+ <event id="AUE_smf_create_npg" instance_of="AUE_smf_generic_pg" header="0"
+ idNo="82" omit="JNI">
+ <program>svc.configd(1M)</program>
+ <see>svccfg(1M)</see>
+ </event>
+ <event id="AUE_smf_delete_pg" instance_of="AUE_smf_generic_pg" header="0"
+ idNo="83" omit="JNI">
+ <program>svc.configd(1M)</program>
+ <see>svccfg(1M)</see>
+ </event>
+ <event id="AUE_smf_delete_npg" instance_of="AUE_smf_generic_pg" header="0"
+ idNo="84" omit="JNI">
+ <program>svc.configd(1M)</program>
+ <see>svccfg(1M)</see>
+ </event>
+
+ <event id="AUE_smf_create_snap" header="0" idNo="85" omit="JNI">
+ <program>svc.configd(1M)</program>
+ <see>svccfg(1M)</see>
+ <entry id="subject">
+ <internal token="subject"/>
+ <external opt="none"/>
+ </entry>
+ <entry id="auth_used">
+ <internal token="uauth"/>
+ <external opt="required" type="char *"/>
+ <comment>authorization used</comment>
+ </entry>
+ <entry id="fmri">
+ <internal token="fmri"/>
+ <external opt="required" type="char *"/>
+ <comment>name</comment>
+ </entry>
+ <entry id="name">
+ <internal token="text"/>
+ <external opt="required" type="char *"/>
+ <comment>snapshot name</comment>
+ </entry>
+ <entry id="return">
+ <internal token="return"/>
+ <external opt="none"/>
+ </entry>
+ </event>
+ <event id="AUE_smf_delete_snap" header="0" idNo="86" omit="JNI">
+ <program>svc.configd(1M)</program>
+ <see>svccfg(1M)</see>
+ <entry id="subject">
+ <internal token="subject"/>
+ <external opt="none"/>
+ </entry>
+ <entry id="auth_used">
+ <internal token="uauth"/>
+ <external opt="required" type="char *"/>
+ <comment>authorization used</comment>
+ </entry>
+ <entry id="fmri">
+ <internal token="fmri"/>
+ <external opt="required" type="char *"/>
+ <comment>name</comment>
+ </entry>
+ <entry id="name">
+ <internal token="text"/>
+ <external opt="required" type="char *"/>
+ <comment>snapshot name</comment>
+ </entry>
+ <entry id="return">
+ <internal token="return"/>
+ <external opt="none"/>
+ </entry>
+ </event>
+ <event id="AUE_smf_attach_snap" header="0" idNo="87" omit="JNI">
+ <program>svc.configd(1M)</program>
+ <see>svccfg(1M)</see>
+ <entry id="subject">
+ <internal token="subject"/>
+ <external opt="none"/>
+ </entry>
+ <entry id="auth_used">
+ <internal token="uauth"/>
+ <external opt="required" type="char *"/>
+ <comment>authorization used</comment>
+ </entry>
+ <entry id="old_fmri">
+ <internal token="fmri"/>
+ <external opt="required" type="char *"/>
+ <comment>old name</comment>
+ </entry>
+ <entry id="old_name">
+ <internal token="text"/>
+ <external opt="required" type="char *"/>
+ <comment>old snapshot</comment>
+ </entry>
+ <entry id="new_fmri">
+ <internal token="fmri"/>
+ <external opt="required" type="char *"/>
+ <comment>new name</comment>
+ </entry>
+ <entry id="new_name">
+ <internal token="text"/>
+ <external opt="required" type="char *"/>
+ <comment>new snapshot</comment>
+ </entry>
+ <entry id="return">
+ <internal token="return"/>
+ <external opt="none"/>
+ </entry>
+ </event>
+
+ <event id="AUE_smf_annotation" header="0" idNo="88" omit="JNI">
+ <program>svc.configd(1M)</program>
+ <see>svccfg(1M)</see>
+ <entry id="subject">
+ <internal token="subject"/>
+ <external opt="none"/>
+ </entry>
+ <entry id="operation">
+ <internal token="text"/>
+ <external opt="required" type="char *"/>
+ <comment>operation</comment>
+ </entry>
+ <entry id="file">
+ <internal token="path"/>
+ <external opt="required" type="char *"/>
+ <comment>imported file</comment>
+ </entry>
+ <entry id="return">
+ <internal token="return"/>
+ <external opt="none"/>
+ </entry>
+ </event>
+
+ <event id="AUE_smf_create_prop" header="0" idNo="89" omit="JNI">
+ <program>svc.configd(1M)</program>
+ <see>svccfg(1M)</see>
+ <entry id="subject">
+ <internal token="subject"/>
+ <external opt="none"/>
+ </entry>
+ <entry id="auth_used">
+ <internal token="uauth"/>
+ <external opt="required" type="char *"/>
+ <comment>authorization used</comment>
+ </entry>
+ <entry id="fmri">
+ <internal token="fmri"/>
+ <external opt="required" type="char *"/>
+ <comment>name</comment>
+ </entry>
+ <entry id="type">
+ <internal token="text"/>
+ <external opt="required" type="char *"/>
+ <comment>type</comment>
+ </entry>
+ <entry id="value">
+ <internal token="text"/>
+ <external opt="optional" type="char *"/>
+ <comment>value</comment>
+ </entry>
+ <entry id="return">
+ <internal token="return"/>
+ <external opt="none"/>
+ </entry>
+ </event>
+
+ <event id="AUE_smf_change_prop" header="0" idNo="90" omit="JNI">
+ <program>svc.configd(1M)</program>
+ <see>svccfg(1M)</see>
+ <entry id="subject">
+ <internal token="subject"/>
+ <external opt="none"/>
+ </entry>
+ <entry id="auth_used">
+ <internal token="uauth"/>
+ <external opt="required" type="char *"/>
+ <comment>authorization used</comment>
+ </entry>
+ <entry id="fmri">
+ <internal token="fmri"/>
+ <external opt="required" type="char *"/>
+ <comment>name</comment>
+ </entry>
+ <entry id="type">
+ <internal token="text"/>
+ <external opt="required" type="char *"/>
+ <comment>type</comment>
+ </entry>
+ <entry id="value">
+ <internal token="text"/>
+ <external opt="optional" type="char *"/>
+ <comment>value</comment>
+ </entry>
+ <entry id="return">
+ <internal token="return"/>
+ <external opt="none"/>
+ </entry>
+ </event>
+ <event id="AUE_smf_delete_prop" header="0" idNo="91" omit="JNI">
+ <program>svc.configd(1M)</program>
+ <see>svccfg(1M)</see>
+ <entry id="subject">
+ <internal token="subject"/>
+ <external opt="none"/>
+ </entry>
+ <entry id="auth_used">
+ <internal token="uauth"/>
+ <external opt="required" type="char *"/>
+ <comment>authorization used</comment>
+ </entry>
+ <entry id="fmri">
+ <internal token="fmri"/>
+ <external opt="required" type="char *"/>
+ <comment>name</comment>
+ </entry>
+ <entry id="return">
+ <internal token="return"/>
+ <external opt="none"/>
+ </entry>
+ </event>
+
+ <event id="AUE_smf_read_prop" instance_of="AUE_smf_generic" header="0"
+ idNo="92" omit="JNI">
+ <program>svc.configd(1M)</program>
+ <see>svccfg(1M)</see>
+ </event>
+
<!-- add new events here with the next higher idNo -->
-<!-- Highest idNo is 64, so next is 65, then fix this comment -->
+<!-- Highest idNo is 92, so next is 93, then fix this comment -->
<!-- end of C Only events -->
diff --git a/usr/src/lib/libbsm/common/mapfile-vers b/usr/src/lib/libbsm/common/mapfile-vers
index cdadac09e6..6cff4e7554 100644
--- a/usr/src/lib/libbsm/common/mapfile-vers
+++ b/usr/src/lib/libbsm/common/mapfile-vers
@@ -19,7 +19,7 @@
# CDDL HEADER END
#
#
-# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# ident "%Z%%M% %I% %E% SMI"
@@ -153,6 +153,7 @@ SUNWprivate_1.1 {
adt_get_mask;
adt_get_session_id;
adt_get_termid;
+ adt_get_unique_id;
adt_import_proc;
adt_load_hostname;
adt_load_termid;
diff --git a/usr/src/lib/libscf/common/libscf_impl.h b/usr/src/lib/libscf/common/libscf_impl.h
index 6dfef8577c..f0947de5a0 100644
--- a/usr/src/lib/libscf/common/libscf_impl.h
+++ b/usr/src/lib/libscf/common/libscf_impl.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * 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
* or http://www.opensolaris.org/os/licensing.
@@ -21,7 +20,7 @@
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -41,15 +40,6 @@
extern "C" {
#endif
-#define SCF_FMRI_SVC_PREFIX "svc:"
-#define SCF_FMRI_FILE_PREFIX "file:"
-#define SCF_FMRI_SCOPE_PREFIX "//"
-#define SCF_FMRI_LOCAL_SCOPE "localhost"
-#define SCF_FMRI_SCOPE_SUFFIX "@localhost"
-#define SCF_FMRI_SERVICE_PREFIX "/"
-#define SCF_FMRI_INSTANCE_PREFIX ":"
-#define SCF_FMRI_PROPERTYGRP_PREFIX "/:properties/"
-#define SCF_FMRI_PROPERTY_PREFIX "/"
/*
* This macro must be extended if additional FMRI prefixes are defined
*/
diff --git a/usr/src/lib/libscf/common/lowlevel.c b/usr/src/lib/libscf/common/lowlevel.c
index 2e31aa6e1f..ce40d0e74f 100644
--- a/usr/src/lib/libscf/common/lowlevel.c
+++ b/usr/src/lib/libscf/common/lowlevel.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -6871,3 +6871,49 @@ _scf_pg_is_read_protected(const scf_propertygroup_t *pg, boolean_t *out)
return (scf_set_error(SCF_ERROR_INTERNAL));
return (SCF_SUCCESS);
}
+
+/*
+ * _scf_set_annotation: a wrapper to set the annotation fields for SMF
+ * security auditing.
+ *
+ * Fails with following in scf_error_key thread specific data:
+ * _INVALID_ARGUMENT - operation or file too large
+ * _NOT_BOUND
+ * _CONNECTION_BROKEN
+ * _INTERNAL
+ * _NO_RESOURCES
+ */
+int
+_scf_set_annotation(scf_handle_t *h, const char *operation, const char *file)
+{
+ struct rep_protocol_annotation request;
+ struct rep_protocol_response response;
+ size_t copied;
+ int r;
+
+ request.rpr_request = REP_PROTOCOL_SET_AUDIT_ANNOTATION;
+ copied = strlcpy(request.rpr_operation,
+ (operation == NULL) ? "" : operation,
+ sizeof (request.rpr_operation));
+ if (copied >= sizeof (request.rpr_operation))
+ return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
+
+ copied = strlcpy(request.rpr_file,
+ (file == NULL) ? "" : file,
+ sizeof (request.rpr_file));
+ if (copied >= sizeof (request.rpr_operation))
+ return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
+
+ (void) pthread_mutex_lock(&h->rh_lock);
+ r = make_door_call(h, &request, sizeof (request),
+ &response, sizeof (response));
+ (void) pthread_mutex_unlock(&h->rh_lock);
+
+ if (r < 0) {
+ DOOR_ERRORS_BLOCK(r);
+ }
+
+ if (response.rpr_response != REP_PROTOCOL_SUCCESS)
+ return (scf_set_error(proto_error(response.rpr_response)));
+ return (0);
+}
diff --git a/usr/src/lib/libscf/common/mapfile-vers b/usr/src/lib/libscf/common/mapfile-vers
index d64e43b026..898e8307ac 100644
--- a/usr/src/lib/libscf/common/mapfile-vers
+++ b/usr/src/lib/libscf/common/mapfile-vers
@@ -19,7 +19,7 @@
# CDDL HEADER END
#
#
-# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# ident "%Z%%M% %I% %E% SMI"
@@ -216,6 +216,7 @@ SUNWprivate_1.1 {
scf_parse_svc_fmri;
_scf_pg_wait;
_scf_request_backup;
+ _scf_set_annotation;
_scf_snapshot_attach;
_scf_snapshot_delete;
_scf_snapshot_take_attach;
diff --git a/usr/src/lib/libscf/inc/libscf_priv.h b/usr/src/lib/libscf/inc/libscf_priv.h
index acffe5b5d8..39c92d20b7 100644
--- a/usr/src/lib/libscf/inc/libscf_priv.h
+++ b/usr/src/lib/libscf/inc/libscf_priv.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -60,6 +60,9 @@ extern "C" {
#define SCF_PG_RESTARTER_ACTIONS_TYPE SCF_GROUP_FRAMEWORK
#define SCF_PG_RESTARTER_ACTIONS_FLAGS SCF_PG_FLAG_NONPERSISTENT
+#define SCF_PROPERTY_CLEAR ((const char *)"maint_off")
+#define SCF_PROPERTY_MAINTENANCE ((const char *)"maint_on")
+
#define SCF_PROPERTY_LOGFILE ((const char *)"logfile")
#define SCF_PROPERTY_ALT_LOGFILE ((const char *)"alt_logfile")
@@ -72,6 +75,19 @@ extern "C" {
#define SCF_FMRI_TYPE_SVC 0x1
#define SCF_FMRI_TYPE_FILE 0x2
+/*
+ * Strings for use in constructing FMRIs
+ */
+#define SCF_FMRI_SVC_PREFIX "svc:"
+#define SCF_FMRI_FILE_PREFIX "file:"
+#define SCF_FMRI_SCOPE_PREFIX "//"
+#define SCF_FMRI_LOCAL_SCOPE "localhost"
+#define SCF_FMRI_SCOPE_SUFFIX "@localhost"
+#define SCF_FMRI_SERVICE_PREFIX "/"
+#define SCF_FMRI_INSTANCE_PREFIX ":"
+#define SCF_FMRI_PROPERTYGRP_PREFIX "/:properties/"
+#define SCF_FMRI_PROPERTY_PREFIX "/"
+
typedef struct scf_decoration_info {
const char *sdi_name;
scf_type_t sdi_type;
@@ -304,6 +320,16 @@ int _scf_request_backup(scf_handle_t *, const char *);
int _scf_pg_is_read_protected(const scf_propertygroup_t *, boolean_t *);
/*
+ * Sets annotation data for SMF audit logging. Once this function has been
+ * set, the next audit record will be preceded by an ADT_smf_annotation
+ * with the information provided in this function. This function is used
+ * to mark operations which comprise multiple primitive operations such as
+ * svccfg import.
+ */
+int _scf_set_annotation(scf_handle_t *h, const char *operation,
+ const char *file);
+
+/*
* scf_pattern_t
*/
typedef struct scf_pattern {