diff options
Diffstat (limited to 'usr/src')
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 { |