2 * Copyright (c) 2019-2020 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
31 * An SUID credential is a port type which allows a process to create a new
32 * process with a specific user id. It provides an alternative means to acheive
33 * this to the more traditional SUID bit file permission.
35 * To create a new SUID credential the process must be running as root and must
36 * have a special entitlement. When created, the credential is associated with a
37 * specific vnode and UID so the unprivileged owner of the credential may only
38 * create a new process from the file associated with that vnode and the
39 * resulting effective UID will be that of the UID in the credential.
42 #include <kern/ipc_kobject.h>
43 #include <kern/queue.h>
44 #include <kern/suid_cred.h>
46 #include <mach/mach_types.h>
47 #include <mach/task.h>
49 #include <IOKit/IOBSD.h>
51 /* Declarations necessary to call vnode_lookup()/vnode_put(). */
54 extern int vnode_lookup(const char *, int, struct vnode
**,
55 struct vfs_context
*);
56 extern struct vfs_context
* vfs_context_current(void);
57 extern int vnode_put(struct vnode
*);
59 /* Declarations necessary to call kauth_cred_issuser(). */
61 extern int kauth_cred_issuser(struct ucred
*);
62 extern struct ucred
*kauth_cred_get(void);
64 /* Data associated with the suid cred port. Consumed during posix_spawn(). */
71 static ZONE_DECLARE(suid_cred_zone
, "suid_cred",
72 sizeof(struct suid_cred
), ZC_NONE
);
74 /* Allocs a new suid credential. The vnode reference will be owned by the newly
75 * created suid_cred_t. */
77 suid_cred_alloc(struct vnode
*vnode
, uint32_t uid
)
79 suid_cred_t sc
= SUID_CRED_NULL
;
81 assert(vnode
!= NULL
);
83 sc
= zalloc(suid_cred_zone
);
85 // Lazily allocated in convert_suid_cred_to_port().
95 suid_cred_free(suid_cred_t sc
)
98 assert(sc
->vnode
!= NULL
);
100 vnode_put(sc
->vnode
);
102 sc
->uid
= UINT32_MAX
;
106 zfree(suid_cred_zone
, sc
);
110 suid_cred_destroy(ipc_port_t port
)
112 suid_cred_t sc
= NULL
;
115 assert(ip_kotype(port
) == IKOT_SUID_CRED
);
116 sc
= (suid_cred_t
)ipc_kobject_get(port
);
117 ipc_kobject_set_atomically(port
, IKO_NULL
, IKOT_NONE
);
120 assert(sc
->port
== port
);
126 suid_cred_notify(mach_msg_header_t
*msg
)
128 assert(msg
->msgh_id
== MACH_NOTIFY_NO_SENDERS
);
130 mach_no_senders_notification_t
*not = (mach_no_senders_notification_t
*)msg
;
131 ipc_port_t port
= not->not_header
.msgh_remote_port
;
133 if (IP_VALID(port
)) {
134 ipc_port_dealloc_kernel(port
);
139 convert_suid_cred_to_port(suid_cred_t sc
)
145 if (!ipc_kobject_make_send_lazy_alloc_port(&sc
->port
,
146 (ipc_kobject_t
) sc
, IKOT_SUID_CRED
, IPC_KOBJECT_ALLOC_NONE
, false, 0)) {
155 * Verify the suid cred port. The cached vnode should match the passed vnode.
156 * The uid to be used to spawn the new process is returned in 'uid'.
159 suid_cred_verify(ipc_port_t port
, struct vnode
*vnode
, uint32_t *uid
)
161 suid_cred_t sc
= NULL
;
164 if (!IP_VALID(port
)) {
170 if (ip_kotype(port
) != IKOT_SUID_CRED
) {
175 if (!ip_active(port
)) {
180 sc
= (suid_cred_t
)ipc_kobject_get(port
);
182 if (vnode
!= sc
->vnode
) {
190 ipc_port_destroy(port
);
195 task_create_suid_cred(
197 suid_cred_path_t path
,
201 suid_cred_t sc
= NULL
;
205 if (task
== TASK_NULL
|| task
!= current_task()) {
206 return KERN_INVALID_ARGUMENT
;
209 // Task must have entitlement.
210 if (!IOTaskHasEntitlement(task
, "com.apple.private.suid_cred")) {
211 return KERN_NO_ACCESS
;
214 // Thread must be root owned.
215 if (!kauth_cred_issuser(kauth_cred_get())) {
216 return KERN_NO_ACCESS
;
219 // Find the vnode for the path.
220 err
= vnode_lookup(path
, 0, &vnode
, vfs_context_current());
222 return KERN_INVALID_ARGUMENT
;
225 sc
= suid_cred_alloc(vnode
, uid
);
227 (void) vnode_put(vnode
);
228 return KERN_RESOURCE_SHORTAGE
;