adt.c revision c529a23fbecacf8a1e97ef658aa4bd8d2e2953b9
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <bsm/adt_event.h>
#include <assert.h>
#include <bsm/audit_record.h>
#include <door.h>
#include <errno.h>
#include <generic.h>
#include <md5.h>
#include <netdb.h>
#include <nss_dbdefs.h>
#include <pwd.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include <synch.h>
#include <sys/systeminfo.h>
#include <syslog.h>
#include <thread.h>
#include <unistd.h>
#include <adt_xlate.h>
#include <adt_ucred.h>
static int adt_init(adt_internal_state_t *, int);
#ifdef C2_DEBUG
#else
#define DPRINTF(x)
#define DFLUSH
#endif
extern int _mutex_lock(mutex_t *);
extern int _mutex_unlock(mutex_t *);
/*
* adt_write_syslog
*
* errors that are not the user's fault (bugs or whatever in
* the underlying audit code are noted in syslog.)
*
* Avoid calling adt_write_syslog for things that can happen
* at high volume.
*
* syslog's open (openlog) and close (closelog) are interesting;
* openlog *may* create a file descriptor and is optional. closelog
* *will* close any open file descriptors and is also optional.
*
* Since syslog may also be used by the calling application, the
* choice is to avoid openlog, which sets some otherwise useful
* parameters, and to embed "Solaris_audit" in the log message.
*/
void
{
int save_errno;
int mask_priority;
save_errno = errno;
(void) setlogmask(mask_priority);
errno = save_errno;
}
/*
* return true if audit is enabled. "Enabled" is any state
* other than AUC_DISABLED.
*
* states are
* AUC_INIT_AUDIT -- c2audit queuing enabled.
* AUC_AUDITING -- up and running
* AUC_DISABLED -- no audit subsystem loaded
* AUC_UNSET -- early boot state
* AUC_NOAUDIT -- subsystem loaded, turned off via
* auditon(A_SETCOND...)
* AUC_NOSPACE -- up and running, but log partitions are full
*
* For purpose of this API, anything but AUC_DISABLED or
* AUC_UNSET is enabled; however one never actually sees
* AUC_DISABLED since auditon returns EINVAL in that case. Any
* auditon error is considered the same as EINVAL for our
* purpose. auditstate is not changed by auditon if an error
* is returned.
*/
adt_audit_enabled(void) {
return (auditstate != AUC_DISABLED);
}
/*
* The man page for getpwuid_r says the buffer must be big enough
* or ERANGE will be returned, but offers no guidance for how big
* the buffer should be or a way to calculate it. If you get
* ERANGE, double pwd_buff's size.
*
* This may be called even when auditing is off.
*/
#define NAFLAG_LEN 512
static int
{
char pwd_buff[NSS_BUFSIZ];
char naflag_buf[NAFLAG_LEN];
if (auditstate == AUC_DISABLED) {
mask->am_success = 0;
mask->am_failure = 0;
} else if (uid >= 0) {
/*
* getpwuid_r returns NULL without setting
* errno if the user does not exist; only
* if the input is the wrong length does it
* set errno.
*/
return (-1);
}
return (-1);
}
return (-1);
} else {
return (-1);
}
return (0);
}
/*
* adt_get_unique_id -- generate a hopefully unique 32 bit value
*
*
* An MD5 hash is taken on a buffer of
* hostname . audit id . unix time . pid . count
*
* "count = noise++;" is subject to a race condition but I don't
* see a need to put a lock around it.
*/
static au_id_t
{
char hostname[MAXHOSTNAMELEN];
union {
au_id_t v[4];
} output;
static int noise = 0;
}
while (retval == 0) { /* 0 is the only invalid result */
sizeof (pid_t));
}
return (retval);
}
/*
* the following "port" function deals with the following issues:
*
* 1 the kernel and ucred deal with a dev_t as a 64 bit value made
* up from a 32 bit major and 32 bit minor.
* 2 User space deals with a dev_t as either the above 64 bit value
* or a 32 bit value made from a 14 bit major and an 18 bit minor.
* 3 The various audit interfaces (except ucred) pass the 32 or
* 64 bit version depending the architecture of the userspace
* application. If you get a port value from ucred and pass it
* to the kernel via auditon(), it must be squeezed into a 32
* bit value because the kernel knows the userspace app's bit
* size.
*
* The internal state structure for adt (adt_internal_state_t) uses
* from 64 or 32 bit applications, so they always send 64 bits and
* the 32 bit end(s) are responsible to convert 32 -> 64 -> 32 as
* appropriate.
*/
/*
* adt_cpy_tid() -- if lib is 64 bit, just copy it (dev_t and port are
* both 64 bits). If lib is 32 bits, squeeze the two-int port into
* a 32 bit dev_t. A port fits in the "minor" part of au_port_t,
* so it isn't broken up into pieces. (When it goes to the kernel
* pieces.)
*/
static void
{
#ifdef _LP64
#else
#endif
}
/*
* adt_start_session -- create interface handle, create context
*
* The imported_state input is normally NULL, if not, it represents
* a continued session; its values obviate the need for a subsequent
* call to adt_set_user().
*
* The flag is used to decide how to set the initial state of the session.
* If 0, the session is "no audit" until a call to adt_set_user; if
* ADT_USE_PROC_DATA, the session is built from the process audit
* characteristics obtained from the kernel. If imported_state is
* not NULL, the resulting audit mask is an OR of the current process
* audit mask and that passed in.
*
* The basic model is that the caller can use the pointer returned
* by adt_start_session whether or not auditing is enabled or an
* error was returned. The functions that take the session handle
* as input generally return without doing anything if auditing is
* disabled.
*/
int
{
/* ensure that auditstate is set */
(void) adt_audit_enabled();
goto return_err;
}
goto return_err;
goto return_err_free; /* errno from adt_init() */
/*
* The imported state overwrites the initial state if the
* imported state represents a valid audit trail
*/
if (imported_state != NULL) {
goto return_err_free;
}
} else if (flags & ADT_USE_PROC_DATA) {
}
DPRINTF(("(%d) Starting session id = %08X\n",
if (state->as_audit_enabled) {
} else {
}
return (0);
return (-1);
}
/*
* adt_get_asid() and adt_set_asid()
*
* if you use this interface, you are responsible to insure that the
* rest of the session data is populated correctly before calling
* adt_proccess_attr()
*
* neither of these are intended for general use and will likely
* remain private interfaces for a long time. Forever is a long
* time. In the case of adt_set_asid(), you should have a very,
* very good reason for setting your own session id. The process
* audit characteristics are not changed by put, use adt_set_proc().
*
* These are "volatile" (more changable than "evolving") and will
* probably change in the S10 period.
*/
void
{
if (session_data == NULL) {
*asid = 0;
} else {
}
}
void
{
if (session_data != NULL) {
}
}
/*
* adt_get_auid() and adt_set_auid()
*
* neither of these are intended for general use and will likely
* remain private interfaces for a long time. Forever is a long
* time. In the case of adt_set_auid(), you should have a very,
* very good reason for setting your own audit id. The process
* audit characteristics are not changed by put, use adt_set_proc().
*/
void
{
if (session_data == NULL) {
*auid = AU_NOAUDITID;
} else {
}
}
void
{
if (session_data != NULL) {
}
}
/*
* adt_get_termid(), adt_set_termid()
*
* if you use this interface, you are responsible to insure that the
* rest of the session data is populated correctly before calling
* adt_proccess_attr()
*
* The process audit characteristics are not changed by put, use
* adt_set_proc().
*/
void
{
if (session_data == NULL) {
} else {
*termid =
}
}
void
const au_tid_addr_t *termid)
{
if (session_data != NULL) {
*termid;
}
}
/*
* adt_get_mask(), adt_set_mask()
*
* if you use this interface, you are responsible to insure that the
* rest of the session data is populated correctly before calling
* adt_proccess_attr()
*
* The process audit characteristics are not changed by put, use
* adt_set_proc().
*/
void
{
if (session_data == NULL) {
mask->am_success = 0;
mask->am_failure = 0;
} else {
}
}
void
{
if (session_data != NULL) {
}
}
/*
* helpers for adt_load_termid
*/
static void
{
}
static void
{
}
/*
* adt_load_termid: convenience function; inputs file handle and
* outputs an au_tid_addr struct.
*
* This code was stolen from audit_settid.c; it differs from audit_settid()
* in that it does not write the terminal id to the process.
*/
int
{
struct sockaddr_in6 peer;
struct sockaddr_in6 sock;
/* get peer name if its a socket, else assume local terminal */
< 0) {
goto return_err;
}
goto return_err;
/* get sock name */
goto return_err_free;
} else {
}
return (0);
return (-1);
}
static boolean_t
{
struct auditinfo_addr audit_data;
return (B_FALSE);
}
return (B_FALSE);
sizeof (au_tid_addr_t));
return (B_TRUE);
}
static int
{
void *p;
return (-1);
case AF_INET:
/* LINTED */
break;
case AF_INET6:
/* LINTED */
break;
default:
return (-1);
}
return (0);
}
/*
* adt_load_hostname() is called when the caller does not have a file
* handle that gives access to the socket info or any other way to
* pass in both port and ip address. The hostname input is ignored if
* the terminal id has already been set; instead it returns the
* existing terminal id.
*
* If audit is off and the hostname lookup fails, no error is
* returned, since an error may be interpreted by the caller
* as grounds for denying a login. Otherwise the caller would
* need to be aware of the audit state.
*/
int
{
if (!adt_audit_enabled())
return (0);
goto return_err;
if (adt_have_termid(p_term)) {
return (0);
}
}
goto return_err_free;
return (0);
if ((auditstate == AUC_DISABLED) ||
(auditstate == AUC_NOAUDIT))
return (0);
return (-1);
}
/*
* adt_load_ttyname() is called when the caller does not have a file
* handle that gives access to the local terminal or any other way
* of determining the device id. The ttyname input is ignored if
* the terminal id has already been set; instead it returns the
* existing terminal id.
*
* If audit is off and the ttyname lookup fails, no error is
* returned, since an error may be interpreted by the caller
* as grounds for denying a login. Otherwise the caller would
* need to be aware of the audit state.
*/
int
{
if (!adt_audit_enabled())
return (0);
goto return_err;
if (adt_have_termid(p_term)) {
return (0);
}
goto return_err_free; /* errno from sysinfo */
goto return_err_free;
}
goto return_err_free;
return (0);
if ((auditstate == AUC_DISABLED) ||
(auditstate == AUC_NOAUDIT))
return (0);
return (-1);
}
/*
* adt_get_session_id returns a stringified representation of
* the audit session id. See also adt_get_asid() for how to
* get the unexpurgated version. No guarantees as to how long
* the returned string will be or its general form; hex for now.
*
* An empty string is returned if auditing is off; length = 1
* and the pointer is valid.
*
* returns strlen + 1 if buffer is valid; else 0 and errno.
*/
{
/*
* output is 0x followed by
* two characters per byte
* plus terminator,
* except leading 0's are suppressed, so a few bytes may
* be unused.
*/
return (0);
}
**buff = '\0';
return (1);
}
/* length < 1 is a bug: the session data type may have changed */
return (length);
}
/*
* adt_end_session -- close handle, clear context
*
* if as_check is invalid, no harm, no foul, EXCEPT that this could
* be an attempt to free data already free'd, so output to syslog
* to help explain why the process cored dumped.
*/
int
{
if (session_data != NULL) {
} else {
}
}
/* no errors yet defined */
return (0);
}
/*
* adt_dup_session -- copy the session data
*/
int
{
int rc = 0;
if (dest_state == NULL) {
rc = -1;
goto return_rc;
}
sizeof (struct adt_internal_state));
source_state->as_label)) != 0) {
dest_state = NULL;
}
}
}
return (rc);
}
/*
* from_export_format()
* read from a network order buffer into struct adt_session_data
*/
static size_t
const adt_export_data_t *external)
{
struct export_header head;
struct export_link link;
char *p = (char *)external;
return (0);
}
/*
* Skip newer versions.
*/
while (version > PROTOCOL_VERSION_2) {
if (offset < 1) {
return (0); /* failed to match version */
}
p += offset; /* point to next version # */
return (0);
}
adrm_start(&context, p);
}
/*
* Adjust buffer pointer to the first data item (euid).
*/
if (p == (char *)external) {
} else {
}
/*
* if down rev version, neither pid nor label are included
* in v1 ax_size_of_tsol_data intentionally ignored
*/
if (version == PROTOCOL_VERSION_1) {
} else if (version == PROTOCOL_VERSION_2) {
if (label_len > 0) {
/* read in and deal with different sized labels. */
return (0);
}
if (label_len > my_label_len) {
return (0);
}
} else {
}
}
return (length);
}
/*
* adt_to_export_format
* read from struct adt_session_data into a network order buffer.
*
* (network order 'cause this data may be shared with a remote host.)
*/
static size_t
{
struct export_header head;
struct export_link tail;
label_len = blabel_size();
}
/* version 2 first */
sizeof (struct adt_export_v2) + label_len;
/* serialize the label */
}
/* now version 1 */
/* ignored in v1 */
/* finally terminator */
return (head.ax_buffer_length);
}
/*
* adt_import_proc() is used by a server acting on behalf
* of a client which has connected via an ipc mechanism such as
* a door.
*
* Since the interface is via ucred, the info.ap_termid.port
* value is always the 64 bit version. What is stored depends
* on how libbsm is compiled.
*/
{
const au_tid64_addr_t *tid;
return (0);
goto return_length_free; /* errno from adt_init() */
/*
* ucred_getauid() returns AU_NOAUDITID if audit is off, which
* is the right answer for adt_import_proc().
*
* Create a local context as near as possible.
*/
goto return_length_free;
goto return_all_free;
} else {
else
goto return_all_free;
}
} else {
sizeof (au_tid_addr_t));
}
DPRINTF(("import_proc/masks = %X %X\n",
} else {
}
goto return_all_free;
/*
* yes, state is supposed to be free'd for both pass and fail
*/
return (length);
}
/*
* adt_ucred_label() -- if label is available, duplicate it.
*/
static m_label_t *
{
}
return (ul);
}
/*
* adt_import() -- convert from network order to machine-specific order
*/
static int
{
/* save local audit enabled state */
return (-1); /* errno from adt_from_export_format */
/*
* If audit isn't enabled on the remote, they were unable
* to generate the audit mask, so generate it based on
* local configuration. If the user id has changed, the
* resulting mask may miss some subtleties that occurred
* on the remote system.
*
* If the remote failed to generate a terminal id, it is not
* recoverable.
*/
if (!internal->as_audit_enabled) {
return (-1);
&mask))
return (-1);
}
}
return (0);
}
/*
* adt_export_session_data()
* copies a adt_session_data struct into a network order buffer
*
* In a misconfigured network, the local host may have auditing
* off while the destination may have auditing on, so if there
* is sufficient memory, a buffer will be returned even in the
* audit off case.
*/
{
length = blabel_size();
}
return (0);
goto return_length_free;
goto return_length_free;
}
} else {
}
return (length);
return (0);
}
static void
{
if (state->as_audit_enabled) {
sizeof (au_tid_addr_t));
sizeof (au_mask_t));
state->as_have_user_data = 0;
}
}
/*
* adt_init -- set session context by copying the audit characteristics
*
* By default, an audit session is based on the process; the default
* is overriden by adt_set_user()
*/
static int
{
if (use_proc_data) {
if (state->as_audit_enabled) {
const au_tid64_addr_t *tid;
/*
* Even if the ucred is NULL, the underlying
* credential may have a valid terminal id; if the
* terminal id is set, then that's good enough. An
* example of where this matters is failed login,
* calling login; login does not load the credential
* since auth failed.
*/
if (!adt_have_termid(
return (-1);
} else {
} else {
return (-1);
}
tid);
} else {
return (-1);
}
}
}
} else {
}
if (state->as_audit_enabled &&
sizeof (state->as_kernel_audit_policy))) {
return (-1); /* errno set by auditon */
}
return (0);
}
/*
* adt_set_proc
*
* Copy the current session state to the process. If this function
* is called, the model becomes a process model rather than a
* session model.
*
* In the current implementation, the value state->as_have_user_data
* must contain all of: ADT_HAVE_{AUID,MASK,TID,ASID}. These are all set
* by adt_set_user() when the ADT_SETTID or ADT_NEW flag is passed in.
*
*/
int
{
int rc;
return (0);
(ADT_HAVE_ALL & ~ADT_HAVE_IDS)) {
goto return_err;
}
sizeof (auditinfo_addr_t));
if (rc < 0)
goto return_err; /* errno set by setaudit_addr() */
return (0);
return (-1);
}
static int
{
if (ruid == ADT_NO_AUDIT) {
return (0);
}
return (-1);
return (0);
}
static int
{
if (ruid >= 0) {
return (-1);
}
DPRINTF(("changed mask to %08X/%08X for ruid=%d\n",
ruid));
return (0);
}
/*
* adt_set_user -- see also adt_set_from_ucred()
*
* "unattributed." If ruid, change the model to session.
*
* only valid with ADT_UPDATE.
*
* ADT_NO_AUDIT is the external equivalent to AU_NOAUDITID -- there
* isn't a good reason to call adt_set_user() with it unless you don't
* have a good value yet and intend to replace it later; auid will be
* AU_NOAUDITID.
*
* adt_set_user should be called even if auditing is not enabled
* so that adt_export_session_data() will have useful stuff to
* work with.
*
* See the note preceding adt_set_proc() about the use of ADT_HAVE_TID
* and ADT_HAVE_ALL.
*/
int
enum adt_user_context user_context)
{
int rc;
return (0);
switch (user_context) {
case ADT_NEW:
return (-1);
}
(au_tid_addr_t *)termid)) != 0)
return (rc);
break;
case ADT_UPDATE:
return (-1);
}
if (ruid != ADT_NO_CHANGE)
return (rc);
break;
case ADT_USER:
return (-1);
}
break;
case ADT_SETTID:
/* avoid fooling pam_setcred()... */
return (0);
default:
return (-1);
}
if (ruid == ADT_NO_AUDIT) {
} else {
if (ruid != ADT_NO_CHANGE)
if (euid != ADT_NO_CHANGE)
if (rgid != ADT_NO_CHANGE)
if (egid != ADT_NO_CHANGE)
}
if (ruid == ADT_NO_ATTRIB) {
}
return (0);
}
/*
* adt_set_from_ucred()
*
* an alternate to adt_set_user that fills the same role but uses
* a pointer to a ucred rather than a list of id's. If the ucred
* pointer is NULL, use the credential from the this process.
*
* A key difference is that for ADT_NEW, adt_set_from_ucred() does
* not overwrite the asid and auid unless auid has not been set.
* ADT_NEW differs from ADT_UPDATE in that it does not OR together
* the incoming audit mask with the one that already exists.
*
* adt_set_from_ucred should be called even if auditing is not enabled
* so that adt_export_session_data() will have useful stuff to
* work with.
*/
int
enum adt_user_context user_context)
{
int rc = -1;
const au_tid64_addr_t *tid64;
return (0);
goto return_rc;
}
switch (user_context) {
case ADT_NEW:
} else {
}
/* if unaudited, adt_newuser cleans up */
tid)) != 0)
goto return_rc;
} else {
}
break;
case ADT_UPDATE:
goto return_rc;
}
goto return_rc;
break;
case ADT_USER:
goto return_rc;
}
break;
default:
goto return_rc;
}
rc = 0;
if (local_uc) {
}
return (rc);
}
/*
* adt_alloc_event() returns a pointer to allocated memory
*
*/
{
struct adt_event_state *event_state;
/*
* need to return a valid event pointer even if audit is
* off, else the caller will end up either (1) keeping its
* If auditing is on, the session data must be valid; otherwise
* we don't care.
*/
if (session_data != NULL) {
}
if (event_state == NULL)
goto return_ptr;
/*
* preload data so the adt_au_*() functions can detect un-supplied
* values (0 and NULL are free via calloc()).
*/
return (return_event);
}
/*
* adt_getXlateTable -- look up translation table address for event id
*/
static struct translation *
{
/* xlate_table is global in adt_xlate.c */
struct translation *p_event;
return (p_event);
p_xlate++;
}
return (NULL);
}
/*
* adt_calcOffsets
*
* the call to this function is surrounded by a mutex.
*
* i walks down the table picking up next_token. j walks again to
* calculate the offset to the input data. k points to the next
* token's row. Finally, l, is used to sum the values in the
* datadef array.
*
* What's going on? The entry array is in the order of the input
* fields but the processing of array entries is in the order of
* the output (see next_token). Calculating the offset to the
* "next" input can't be done in the outer loop (i) since i doesn't
* point to the current entry and it can't be done with the k index
* because it doesn't represent the order of input fields.
*
* While the resulting algorithm is n**2, it is only done once per
* event type.
*/
/*
* adt_calcOffsets is only called once per event type, but it uses
* the address alignment of memory allocated for that event as if it
* were the same for all subsequently allocated memory. This is
* what matters for figuring out the correct alignment is the size
* of the array element.
*/
static void
{
int i, j;
void *struct_start = p_data;
for (i = 0; i < tablesize; i++) {
continue;
}
prev_size = 0;
for (j = 0; j < p_entry[i].en_count_types; j++) {
this_size = sizeof (enum adt_generic);
else
/* adj for first entry */
if (prev_size == 0)
} else {
}
}
}
}
/*
* adt_generate_event
* generate event record from external struct. The order is based on
* the output tokens, allowing for the possibility that the input data
* is in a different order.
*
*/
static void
struct adt_event_state *p_event,
struct translation *p_xlate)
{
/*
* offsets are not pre-calculated; the initial offsets are all
* 0; valid offsets are >= 0. Offsets for no-input tokens such
* as subject are set to -1 by adt_calcOffset()
*/
if (p_xlate->tx_offsetsCalculated == 0) {
(void) _mutex_lock(&lock);
(void *)p_extdata);
(void) _mutex_unlock(&lock);
}
p_event);
}
}
/*
* adt_put_event -- main event generation function.
* The input "event" is the address of the struct containing
* event-specific data.
*
* However if auditing is off or the session handle
* is NULL, no attempt to write a record is made.
*/
int
{
struct adt_event_state *event_state;
struct translation *xlate;
int rc = 0;
rc = -1;
goto return_rc;
}
/* if audit off or this is a broken session, exit */
goto return_rc;
/* look up the event */
rc = -1;
goto return_rc;
}
return (rc);
}
/*
* adt_free_event -- invalidate and free
*/
void
{
struct adt_event_state *event_state;
return;
event_state->ae_check = 0;
}
/*
* adt_is_selected -- helper to adt_selected(), below.
*
* "sorf" is "success or fail" status; au_preselect compares
* that with success, fail, or both.
*/
static int
{
int prs_sorf;
if (sorf == 0)
else
}
/*
* selected -- see if this event is preselected.
*
* if errors are encountered trying to check a preselection mask
* or look up a user name, the event is selected. Otherwise, the
* preselection mask is used for the job.
*/
static int
{
return (1); /* default is "selected" */
}
/* non-attributable? */
sizeof (namask)) != 0) {
return (1);
}
} else {
status));
}
}