+ my_depth = OSBacktrace(&my_stack[0], MAX_STACK_DEPTH);
+ if ( my_depth == 0 ) {
+ printf("%s - OSBacktrace failed \n", __FUNCTION__);
+ return;
+ }
+
+ /* fill new backtrace */
+ my_slot = cred_debug_buf_p->next_slot;
+ cred_debug_buf_p->next_slot++;
+ cred_debug_buf_p->stack_buffer[ my_slot ].depth = my_depth;
+ for ( i = 0; i < my_depth; i++ ) {
+ cred_debug_buf_p->stack_buffer[ my_slot ].stack[ i ] = my_stack[ i ];
+ }
+
+ return;
+}
+
+
+/* subset of struct ucred for use in sysctl_dump_creds */
+struct debug_ucred {
+ void *credp;
+ u_long cr_ref; /* reference count */
+ uid_t cr_uid; /* effective user id */
+ uid_t cr_ruid; /* real user id */
+ uid_t cr_svuid; /* saved user id */
+ short cr_ngroups; /* number of groups in advisory list */
+ gid_t cr_groups[NGROUPS]; /* advisory group list */
+ gid_t cr_rgid; /* real group id */
+ gid_t cr_svgid; /* saved group id */
+ uid_t cr_gmuid; /* UID for group membership purposes */
+ struct auditinfo_addr cr_audit; /* user auditing data. */
+ void *cr_label; /* MACF label */
+ int cr_flags; /* flags on credential */
+};
+typedef struct debug_ucred debug_ucred;
+
+SYSCTL_PROC(_kern, OID_AUTO, dump_creds, CTLFLAG_RD,
+ NULL, 0, sysctl_dump_creds, "S,debug_ucred", "List of credentials in the cred hash");
+
+/* accessed by:
+ * err = sysctlbyname( "kern.dump_creds", bufp, &len, NULL, 0 );
+ */
+
+static int
+sysctl_dump_creds( __unused struct sysctl_oid *oidp, __unused void *arg1, __unused int arg2, struct sysctl_req *req )
+{
+ int i, j, counter = 0;
+ int error;
+ size_t space;
+ kauth_cred_t found_cred;
+ debug_ucred * cred_listp;
+ debug_ucred * nextp;
+
+ /* This is a readonly node. */
+ if (req->newptr != USER_ADDR_NULL)
+ return (EPERM);
+
+ /* calculate space needed */
+ for (i = 0; i < KAUTH_CRED_TABLE_SIZE; i++) {
+ TAILQ_FOREACH(found_cred, &kauth_cred_table_anchor[i], cr_link) {
+ counter++;
+ }
+ }
+
+ /* they are querying us so just return the space required. */
+ if (req->oldptr == USER_ADDR_NULL) {
+ counter += 10; // add in some padding;
+ req->oldidx = counter * sizeof(debug_ucred);
+ return 0;
+ }
+
+ MALLOC( cred_listp, debug_ucred *, req->oldlen, M_TEMP, M_WAITOK | M_ZERO);
+ if ( cred_listp == NULL ) {
+ return (ENOMEM);
+ }
+
+ /* fill in creds to send back */
+ nextp = cred_listp;
+ space = 0;
+ for (i = 0; i < KAUTH_CRED_TABLE_SIZE; i++) {
+ TAILQ_FOREACH(found_cred, &kauth_cred_table_anchor[i], cr_link) {
+ nextp->credp = found_cred;
+ nextp->cr_ref = found_cred->cr_ref;
+ nextp->cr_uid = found_cred->cr_uid;
+ nextp->cr_ruid = found_cred->cr_ruid;
+ nextp->cr_svuid = found_cred->cr_svuid;
+ nextp->cr_ngroups = found_cred->cr_ngroups;
+ for ( j = 0; j < nextp->cr_ngroups; j++ ) {
+ nextp->cr_groups[ j ] = found_cred->cr_groups[ j ];
+ }
+ nextp->cr_rgid = found_cred->cr_rgid;
+ nextp->cr_svgid = found_cred->cr_svgid;
+ nextp->cr_gmuid = found_cred->cr_gmuid;
+ nextp->cr_audit.ai_auid =
+ found_cred->cr_audit.as_aia_p->ai_auid;
+ nextp->cr_audit.ai_mask.am_success =
+ found_cred->cr_audit.as_mask.am_success;
+ nextp->cr_audit.ai_mask.am_failure =
+ found_cred->cr_audit.as_mask.am_failure;
+ nextp->cr_audit.ai_termid.at_port =
+ found_cred->cr_audit.as_aia_p->ai_termid.at_port;
+ nextp->cr_audit.ai_termid.at_type =
+ found_cred->cr_audit.as_aia_p->ai_termid.at_type;
+ nextp->cr_audit.ai_termid.at_addr[0] =
+ found_cred->cr_audit.as_aia_p->ai_termid.at_addr[0];
+ nextp->cr_audit.ai_termid.at_addr[1] =
+ found_cred->cr_audit.as_aia_p->ai_termid.at_addr[1];
+ nextp->cr_audit.ai_termid.at_addr[2] =
+ found_cred->cr_audit.as_aia_p->ai_termid.at_addr[2];
+ nextp->cr_audit.ai_termid.at_addr[3] =
+ found_cred->cr_audit.as_aia_p->ai_termid.at_addr[3];
+ nextp->cr_audit.ai_asid =
+ found_cred->cr_audit.as_aia_p->ai_asid;
+ nextp->cr_audit.ai_flags =
+ found_cred->cr_audit.as_aia_p->ai_flags;
+ nextp->cr_label = found_cred->cr_label;
+ nextp->cr_flags = found_cred->cr_flags;
+ nextp++;
+ space += sizeof(debug_ucred);
+ if ( space > req->oldlen ) {
+ FREE(cred_listp, M_TEMP);
+ return (ENOMEM);
+ }
+ }
+ }
+ req->oldlen = space;
+ error = SYSCTL_OUT(req, cred_listp, req->oldlen);
+ FREE(cred_listp, M_TEMP);
+ return (error);
+}
+
+
+SYSCTL_PROC(_kern, OID_AUTO, cred_bt, CTLFLAG_RD,
+ NULL, 0, sysctl_dump_cred_backtraces, "S,cred_debug_buffer", "dump credential backtrace");
+
+/* accessed by:
+ * err = sysctlbyname( "kern.cred_bt", bufp, &len, NULL, 0 );
+ */
+
+static int
+sysctl_dump_cred_backtraces( __unused struct sysctl_oid *oidp, __unused void *arg1, __unused int arg2, struct sysctl_req *req )
+{
+ int i, j;
+ int error;
+ size_t space;
+ cred_debug_buffer * bt_bufp;
+ cred_backtrace * nextp;
+
+ /* This is a readonly node. */
+ if (req->newptr != USER_ADDR_NULL)
+ return (EPERM);
+
+ if ( cred_debug_buf_p == NULL ) {
+ return (EAGAIN);
+ }
+
+ /* calculate space needed */
+ space = sizeof( cred_debug_buf_p->next_slot );
+ space += (sizeof( cred_backtrace ) * cred_debug_buf_p->next_slot);
+
+ /* they are querying us so just return the space required. */
+ if (req->oldptr == USER_ADDR_NULL) {
+ req->oldidx = space;
+ return 0;
+ }
+
+ if ( space > req->oldlen ) {
+ return (ENOMEM);
+ }
+
+ MALLOC( bt_bufp, cred_debug_buffer *, req->oldlen, M_TEMP, M_WAITOK | M_ZERO);
+ if ( bt_bufp == NULL ) {
+ return (ENOMEM);
+ }
+
+ /* fill in backtrace info to send back */
+ bt_bufp->next_slot = cred_debug_buf_p->next_slot;
+ space = sizeof(bt_bufp->next_slot);
+
+ nextp = &bt_bufp->stack_buffer[ 0 ];
+ for (i = 0; i < cred_debug_buf_p->next_slot; i++) {
+ nextp->depth = cred_debug_buf_p->stack_buffer[ i ].depth;
+ for ( j = 0; j < nextp->depth; j++ ) {
+ nextp->stack[ j ] = cred_debug_buf_p->stack_buffer[ i ].stack[ j ];
+ }
+ space += sizeof(*nextp);
+ nextp++;
+ }
+ req->oldlen = space;
+ error = SYSCTL_OUT(req, bt_bufp, req->oldlen);
+ FREE(bt_bufp, M_TEMP);
+ return (error);
+}
+
+#endif /* KAUTH_CRED_HASH_DEBUG || DEBUG_CRED */
+
+
+/*
+ **********************************************************************
+ * The following routines will be moved to a policy_posix.c module at
+ * some future point.
+ **********************************************************************
+ */
+
+/*
+ * posix_cred_create
+ *
+ * Description: Helper function to create a kauth_cred_t credential that is
+ * initally labelled with a specific POSIX credential label
+ *
+ * Parameters: pcred The posix_cred_t to use as the initial
+ * label value
+ *
+ * Returns: (kauth_cred_t) The credential that was found in the
+ * hash or creates
+ * NULL kauth_cred_add() failed, or there was
+ * no egid specified, or we failed to
+ * attach a label to the new credential
+ *
+ * Notes: This function currently wraps kauth_cred_create(), and is the
+ * only consumer of that ill-fated function, apart from bsd_init().
+ * It exists solely to support the NFS server code creation of
+ * credentials based on the over-the-wire RPC calls containing
+ * traditional POSIX credential information being tunneled to
+ * the server host from the client machine.
+ *
+ * In the future, we hope this function goes away.
+ *
+ * In the short term, it creates a temporary credential, puts
+ * the POSIX information from NFS into it, and then calls
+ * kauth_cred_create(), as an internal implementation detail.
+ *
+ * If we have to keep it around in the medium term, it will
+ * create a new kauth_cred_t, then label it with a POSIX label
+ * corresponding to the contents of the kauth_cred_t. If the
+ * policy_posix MACF module is not loaded, it will instead
+ * substitute a posix_cred_t which GRANTS all access (effectively
+ * a "root" credential) in order to not prevent NFS from working
+ * in the case that we are not supporting POSIX credentials.
+ */
+kauth_cred_t
+posix_cred_create(posix_cred_t pcred)
+{
+ struct ucred temp_cred;
+
+ bzero(&temp_cred, sizeof(temp_cred));
+ temp_cred.cr_posix = *pcred;
+
+ return kauth_cred_create(&temp_cred);
+}
+
+
+/*
+ * posix_cred_get
+ *
+ * Description: Given a kauth_cred_t, return the POSIX credential label, if
+ * any, which is associated with it.
+ *
+ * Parameters: cred The credential to obtain the label from
+ *
+ * Returns: posix_cred_t The POSIX credential label
+ *
+ * Notes: In the event that the policy_posix MACF module IS NOT loaded,
+ * this function will return a pointer to a posix_cred_t which
+ * GRANTS all access (effectively, a "root" credential). This is
+ * necessary to support legacy code which insists on tightly
+ * integrating POSIX credentials into its APIs, including, but
+ * not limited to, System V IPC mechanisms, POSIX IPC mechanisms,
+ * NFSv3, signals, dtrace, and a large number of kauth routines
+ * used to implement POSIX permissions related system calls.
+ *
+ * In the event that the policy_posix MACF module IS loaded, and
+ * there is no POSIX label on the kauth_cred_t credential, this
+ * function will return a pointer to a posix_cred_t which DENIES
+ * all access (effectively, a "deny rights granted by POSIX"
+ * credential). This is necessary to support the concept of a
+ * transiently loaded POSIX policy, or kauth_cred_t credentials
+ * which can not be used in conjunctions with POSIX permissions
+ * checks.
+ *
+ * This function currently returns the address of the cr_posix
+ * field of the supplied kauth_cred_t credential, and as such
+ * currently can not fail. In the future, this will not be the
+ * case.
+ */
+posix_cred_t
+posix_cred_get(kauth_cred_t cred)
+{
+ return(&cred->cr_posix);
+}
+
+
+/*
+ * posix_cred_label
+ *
+ * Description: Label a kauth_cred_t with a POSIX credential label
+ *
+ * Parameters: cred The credential to label
+ * pcred The POSIX credential t label it with
+ *
+ * Returns: (void)
+ *
+ * Notes: This function is currently void in order to permit it to fit
+ * in with the current MACF framework label methods which allow
+ * labeling to fail silently. This is like acceptable for
+ * mandatory access controls, but not for POSIX, since those
+ * access controls are advisory. We will need to consider a
+ * return value in a future version of the MACF API.
+ *
+ * This operation currently cannot fail, as currently the POSIX
+ * credential is a subfield of the kauth_cred_t (ucred), which
+ * MUST be valid. In the future, this will not be the case.
+ */
+void
+posix_cred_label(kauth_cred_t cred, posix_cred_t pcred)
+{
+ cred->cr_posix = *pcred; /* structure assign for now */
+}
+
+
+/*
+ * posix_cred_access
+ *
+ * Description: Perform a POSIX access check for a protected object
+ *
+ * Parameters: cred The credential to check
+ * object_uid The POSIX UID of the protected object
+ * object_gid The POSIX GID of the protected object
+ * object_mode The POSIX mode of the protected object
+ * mode_req The requested POSIX access rights
+ *
+ * Returns 0 Access is granted
+ * EACCES Access is denied
+ *
+ * Notes: This code optimizes the case where the world and group rights
+ * would both grant the requested rights to avoid making a group
+ * membership query. This is a big performance win in the case
+ * where this is true.
+ */
+int
+posix_cred_access(kauth_cred_t cred, id_t object_uid, id_t object_gid, mode_t object_mode, mode_t mode_req)
+{
+ int is_member;
+ mode_t mode_owner = (object_mode & S_IRWXU);
+ mode_t mode_group = (object_mode & S_IRWXG) << 3;
+ mode_t mode_world = (object_mode & S_IRWXO) << 6;
+
+ /*
+ * Check first for owner rights
+ */
+ if (kauth_cred_getuid(cred) == object_uid && (mode_req & mode_owner) == mode_req)
+ return (0);
+
+ /*
+ * Combined group and world rights check, if we don't have owner rights
+ *
+ * OPTIMIZED: If group and world rights would grant the same bits, and
+ * they set of requested bits is in both, then we can simply check the
+ * world rights, avoiding a group membership check, which is expensive.
+ */
+ if ((mode_req & mode_group & mode_world) == mode_req) {
+ return (0);
+ } else {
+ /*
+ * NON-OPTIMIZED: requires group membership check.
+ */
+ if ((mode_req & mode_group) != mode_req) {
+ /*
+ * exclusion group : treat errors as "is a member"
+ *
+ * NON-OPTIMIZED: +group would deny; must check group
+ */
+ if (!kauth_cred_ismember_gid(cred, object_gid, &is_member) && is_member) {
+ /*
+ * DENY: +group denies
+ */
+ return (EACCES);
+ } else {
+ if ((mode_req & mode_world) != mode_req) {
+ /*
+ * DENY: both -group & world would deny
+ */
+ return (EACCES);
+ } else {
+ /*
+ * ALLOW: allowed by -group and +world
+ */
+ return (0);
+ }
+ }
+ } else {
+ /*
+ * inclusion group; treat errors as "not a member"
+ *
+ * NON-OPTIMIZED: +group allows, world denies; must
+ * check group
+ */
+ if (!kauth_cred_ismember_gid(cred, object_gid, &is_member) && is_member) {
+ /*
+ * ALLOW: allowed by +group
+ */
+ return (0);
+ } else {
+ if ((mode_req & mode_world) != mode_req) {
+ /*
+ * DENY: both -group & world would deny
+ */
+ return (EACCES);
+ } else {
+ /*
+ * ALLOW: allowed by -group and +world
+ */
+ return (0);
+ }
+ }
+ }
+ }