+ * kauth_cred_label_update
+ *
+ * Description: Update the MAC label associated with a credential
+ *
+ * Parameters: cred The original credential
+ * label The MAC label to set
+ *
+ * Returns: (kauth_cred_t) The updated credential
+ *
+ * IMPORTANT: This function is implemented via kauth_cred_update(), which,
+ * if it returns a credential other than the one it is passed,
+ * will have dropped the reference on the passed credential. All
+ * callers should be aware of this, and treat this function as an
+ * unref + ref, potentially on different credentials.
+ *
+ * Because of this, the caller is expected to take its own
+ * reference on the credential passed as the first parameter,
+ * and be prepared to release the reference on the credential
+ * that is returned to them, if it is not intended to be a
+ * persistent reference.
+ */
+kauth_cred_t
+kauth_cred_label_update(kauth_cred_t cred, struct label *label)
+{
+ kauth_cred_t newcred;
+ struct ucred temp_cred;
+
+ bcopy(cred, &temp_cred, sizeof(temp_cred));
+
+ mac_cred_label_init(&temp_cred);
+ mac_cred_label_associate(cred, &temp_cred);
+ mac_cred_label_update(&temp_cred, label);
+
+ newcred = kauth_cred_update(cred, &temp_cred, TRUE);
+ mac_cred_label_destroy(&temp_cred);
+ return (newcred);
+}
+
+/*
+ * kauth_cred_label_update_execve
+ *
+ * Description: Update the MAC label associated with a credential as
+ * part of exec
+ *
+ * Parameters: cred The original credential
+ * vp The exec vnode
+ * scriptl The script MAC label
+ * execl The executable MAC label
+ *
+ * Returns: (kauth_cred_t) The updated credential
+ *
+ * IMPORTANT: This function is implemented via kauth_cred_update(), which,
+ * if it returns a credential other than the one it is passed,
+ * will have dropped the reference on the passed credential. All
+ * callers should be aware of this, and treat this function as an
+ * unref + ref, potentially on different credentials.
+ *
+ * Because of this, the caller is expected to take its own
+ * reference on the credential passed as the first parameter,
+ * and be prepared to release the reference on the credential
+ * that is returned to them, if it is not intended to be a
+ * persistent reference.
+ */
+static
+kauth_cred_t
+kauth_cred_label_update_execve(kauth_cred_t cred, vfs_context_t ctx,
+ struct vnode *vp, struct label *scriptl, struct label *execl)
+{
+ kauth_cred_t newcred;
+ struct ucred temp_cred;
+
+ bcopy(cred, &temp_cred, sizeof(temp_cred));
+
+ mac_cred_label_init(&temp_cred);
+ mac_cred_label_associate(cred, &temp_cred);
+ mac_cred_label_update_execve(ctx, &temp_cred,
+ vp, scriptl, execl);
+
+ newcred = kauth_cred_update(cred, &temp_cred, TRUE);
+ mac_cred_label_destroy(&temp_cred);
+ return (newcred);
+}
+
+/*
+ * kauth_proc_label_update
+ *
+ * Description: Update the label inside the credential associated with the process.
+ *
+ * Parameters: p The process to modify
+ * label The label to place in the process credential
+ *
+ * Notes: The credential associated with the process may change as a result
+ * of this call. The caller should not assume the process reference to
+ * the old credential still exists.
+ */
+int kauth_proc_label_update(struct proc *p, struct label *label)
+{
+ kauth_cred_t my_cred, my_new_cred;
+
+ my_cred = kauth_cred_proc_ref(p);
+
+ DEBUG_CRED_ENTER("kauth_proc_label_update: %p\n", my_cred);
+
+ /* get current credential and take a reference while we muck with it */
+ for (;;) {
+
+ /*
+ * Set the credential with new info. If there is no change,
+ * we get back the same credential we passed in; if there is
+ * a change, we drop the reference on the credential we
+ * passed in. The subsequent compare is safe, because it is
+ * a pointer compare rather than a contents compare.
+ */
+ my_new_cred = kauth_cred_label_update(my_cred, label);
+ if (my_cred != my_new_cred) {
+
+ DEBUG_CRED_CHANGE("kauth_proc_setlabel_unlocked CH(%d): %p/0x%08x -> %p/0x%08x\n", p->p_pid, my_cred, my_cred->cr_flags, my_new_cred, my_new_cred->cr_flags);
+
+ proc_lock(p);
+ /*
+ * We need to protect for a race where another thread
+ * also changed the credential after we took our
+ * reference. If p_ucred has changed then we should
+ * restart this again with the new cred.
+ */
+ if (p->p_ucred != my_cred) {
+ proc_unlock(p);
+ kauth_cred_unref(&my_new_cred);
+ my_cred = kauth_cred_proc_ref(p);
+ /* try again */
+ continue;
+ }
+ p->p_ucred = my_new_cred;
+ mac_proc_set_enforce(p, MAC_ALL_ENFORCE);
+ proc_unlock(p);
+ }
+ break;
+ }
+ /* Drop old proc reference or our extra reference */
+ kauth_cred_unref(&my_cred);
+
+ return (0);
+}
+
+/*
+ * kauth_proc_label_update_execve
+ *
+ * Description: Update the label inside the credential associated with the
+ * process as part of a transitioning execve. The label will
+ * be updated by the policies as part of this processing, not
+ * provided up front.
+ *
+ * Parameters: p The process to modify
+ * ctx The context of the exec
+ * vp The vnode being exec'ed
+ * scriptl The script MAC label
+ * execl The executable MAC label
+ *
+ * Notes: The credential associated with the process WILL change as a
+ * result of this call. The caller should not assume the process
+ * reference to the old credential still exists.
+ */
+int kauth_proc_label_update_execve(struct proc *p, vfs_context_t ctx,
+ struct vnode *vp, struct label *scriptl, struct label *execl)
+{
+ kauth_cred_t my_cred, my_new_cred;
+
+ my_cred = kauth_cred_proc_ref(p);
+
+ DEBUG_CRED_ENTER("kauth_proc_label_update_execve: %p\n", my_cred);
+
+ /* get current credential and take a reference while we muck with it */
+ for (;;) {
+
+ /*
+ * Set the credential with new info. If there is no change,
+ * we get back the same credential we passed in; if there is
+ * a change, we drop the reference on the credential we
+ * passed in. The subsequent compare is safe, because it is
+ * a pointer compare rather than a contents compare.
+ */
+ my_new_cred = kauth_cred_label_update_execve(my_cred, ctx, vp, scriptl, execl);
+ if (my_cred != my_new_cred) {
+
+ DEBUG_CRED_CHANGE("kauth_proc_label_update_execve_unlocked CH(%d): %p/0x%08x -> %p/0x%08x\n", p->p_pid, my_cred, my_cred->cr_flags, my_new_cred, my_new_cred->cr_flags);
+
+ proc_lock(p);
+ /*
+ * We need to protect for a race where another thread
+ * also changed the credential after we took our
+ * reference. If p_ucred has changed then we should
+ * restart this again with the new cred.
+ */
+ if (p->p_ucred != my_cred) {
+ proc_unlock(p);
+ kauth_cred_unref(&my_new_cred);
+ my_cred = kauth_cred_proc_ref(p);
+ /* try again */
+ continue;
+ }
+ p->p_ucred = my_new_cred;
+ mac_proc_set_enforce(p, MAC_ALL_ENFORCE);
+ proc_unlock(p);
+ }
+ break;
+ }
+ /* Drop old proc reference or our extra reference */
+ kauth_cred_unref(&my_cred);
+
+ return (0);
+}
+
+#if 1
+/*
+ * for temporary binary compatibility
+ */
+kauth_cred_t kauth_cred_setlabel(kauth_cred_t cred, struct label *label);
+kauth_cred_t
+kauth_cred_setlabel(kauth_cred_t cred, struct label *label)
+{
+ return kauth_cred_label_update(cred, label);
+}
+
+int kauth_proc_setlabel(struct proc *p, struct label *label);
+int
+kauth_proc_setlabel(struct proc *p, struct label *label)
+{
+ return kauth_proc_label_update(p, label);
+}
+#endif
+
+#else
+
+/* this is a temp hack to cover us when MACF is not built in a kernel configuration.
+ * Since we cannot build our export lists based on the kernel configuration we need
+ * to define a stub.
+ */
+kauth_cred_t
+kauth_cred_label_update(__unused kauth_cred_t cred, __unused void *label)
+{
+ return(NULL);
+}
+
+int
+kauth_proc_label_update(__unused struct proc *p, __unused void *label)
+{
+ return (0);
+}
+
+#if 1
+/*
+ * for temporary binary compatibility
+ */
+kauth_cred_t kauth_cred_setlabel(kauth_cred_t cred, void *label);
+kauth_cred_t
+kauth_cred_setlabel(__unused kauth_cred_t cred, __unused void *label)
+{
+ return NULL;
+}
+
+int kauth_proc_setlabel(struct proc *p, void *label);
+int
+kauth_proc_setlabel(__unused struct proc *p, __unused void *label)
+{
+ return (0);
+}
+#endif
+#endif
+
+/*
+ * kauth_cred_ref
+ *
+ * Description: Add a reference to the passed credential
+ *
+ * Parameters: cred The credential to reference
+ *
+ * Returns: (void)
+ *
+ * Notes: This function adds a reference to the provided credential;
+ * the existing reference on the credential is assumed to be
+ * held stable over this operation by taking the appropriate
+ * lock to protect the pointer from which it is being referenced,
+ * if necessary (e.g. the proc lock is held over the call if the
+ * credential being referenced is from p_ucred, the vnode lock
+ * if from the per vnode name cache cred cache, and so on).
+ *
+ * This is safe from the kauth_cred_unref() path, since an atomic
+ * add is used, and the unref path specifically checks to see that
+ * the value has not been changed to add a reference between the
+ * time the credential is unreferenced by another pointer and the
+ * time it is unreferenced from the cred hash cache.