Implement some stubs and store remote credentials for connections

Also uncomment some code for pipes (I had commented it out during testing)
This commit is contained in:
Ariel Abreu 2021-04-11 09:38:30 -04:00
parent 90b934b336
commit b91537684e
No known key found for this signature in database
GPG Key ID: BB20848279B910AC
8 changed files with 205 additions and 34 deletions

View File

@ -8,6 +8,8 @@
#include <xpc/launchd.h> #include <xpc/launchd.h>
#include <xpc/private/pipe.h> #include <xpc/private/pipe.h>
#include <xpc/private/endpoint.h> #include <xpc/private/endpoint.h>
#include <xpc/private/mach_send.h>
#include <xpc/private/mach_recv.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {

View File

@ -0,0 +1,10 @@
#ifndef _XPC_PRIVATE_MACH_RECV_H_
#define _XPC_PRIVATE_MACH_RECV_H_
#include <xpc/xpc.h>
#include <mach/mach.h>
xpc_object_t xpc_mach_recv_create(mach_port_t recv);
mach_port_t xpc_mach_recv_extract_right(xpc_object_t xrecv);
#endif // _XPC_PRIVATE_MACH_RECV_H_

View File

@ -0,0 +1,14 @@
#ifndef _XPC_PRIVATE_MACH_SEND_H_
#define _XPC_PRIVATE_MACH_SEND_H_
#include <xpc/xpc.h>
#include <mach/mach.h>
xpc_object_t xpc_mach_send_create(mach_port_t send);
xpc_object_t xpc_mach_send_create_with_disposition(mach_port_t send, unsigned int disposition);
mach_port_t xpc_mach_send_copy_right(xpc_object_t xsend);
mach_port_t xpc_mach_send_get_right(xpc_object_t xsend);
mach_port_t xpc_mach_send_extract_right(xpc_object_t xsend);
#endif // _XPC_PRIVATE_MACH_SEND_H_

View File

@ -58,6 +58,9 @@ struct xpc_connection_s {
bool activated; bool activated;
pthread_rwlock_t server_peers_lock; pthread_rwlock_t server_peers_lock;
xpc_genarr_connection_t server_peers; xpc_genarr_connection_t server_peers;
pthread_rwlock_t remote_credentials_lock;
audit_token_t remote_credentials;
// //
// mutable and lock-free // mutable and lock-free
@ -139,6 +142,9 @@ struct xpc_connection_s {
- (void)sendMessage: (XPC_CLASS(dictionary)*)message queue: (dispatch_queue_t)queue withReply: (xpc_handler_t)handler; - (void)sendMessage: (XPC_CLASS(dictionary)*)message queue: (dispatch_queue_t)queue withReply: (xpc_handler_t)handler;
- (xpc_object_t)sendMessageWithSynchronousReply: (XPC_CLASS(dictionary)*)message; - (xpc_object_t)sendMessageWithSynchronousReply: (XPC_CLASS(dictionary)*)message;
- (void)setRemoteCredentials: (audit_token_t*)token;
- (void)copyRemoteCredentials: (audit_token_t*)outToken;
@end @end
#endif // _XPC_OBJECTS_CONNECTION_H_ #endif // _XPC_OBJECTS_CONNECTION_H_

View File

@ -125,6 +125,15 @@ static bool handle_send_result(dispatch_mach_msg_t message, dispatch_mach_reason
return false; return false;
}; };
static audit_token_t* get_audit_token(mach_msg_header_t* header) {
audit_token_t* token = NULL;
mach_msg_trailer_t* trailer = (mach_msg_trailer_t *)((char*)header + round_msg(header->msgh_size));
if (trailer->msgh_trailer_type == MACH_MSG_TRAILER_FORMAT_0 && trailer->msgh_trailer_size >= sizeof(mach_msg_audit_trailer_t)) {
token = &((mach_msg_audit_trailer_t*)trailer)->msgh_audit;
}
return token;
};
static void dispatch_mach_handler(void* context, dispatch_mach_reason_t reason, dispatch_mach_msg_t message, mach_error_t error) { static void dispatch_mach_handler(void* context, dispatch_mach_reason_t reason, dispatch_mach_msg_t message, mach_error_t error) {
XPC_CLASS(connection)* self = context; XPC_CLASS(connection)* self = context;
XPC_THIS_DECL(connection); XPC_THIS_DECL(connection);
@ -140,6 +149,7 @@ static void dispatch_mach_handler(void* context, dispatch_mach_reason_t reason,
mach_port_t sendPort = MACH_PORT_NULL; mach_port_t sendPort = MACH_PORT_NULL;
mach_port_t receivePort = MACH_PORT_NULL; mach_port_t receivePort = MACH_PORT_NULL;
XPC_CLASS(connection)* serverPeer = nil; XPC_CLASS(connection)* serverPeer = nil;
audit_token_t* token = NULL;
if (header->msgh_id != XPC_MSGH_ID_CHECKIN) { if (header->msgh_id != XPC_MSGH_ID_CHECKIN) {
xpc_log(XPC_LOG_NOTICE, "server connection received non-checkin message"); xpc_log(XPC_LOG_NOTICE, "server connection received non-checkin message");
@ -147,6 +157,8 @@ static void dispatch_mach_handler(void* context, dispatch_mach_reason_t reason,
return; return;
} }
token = get_audit_token(header);
[message retain]; // because the deserializer consumes a reference on the message [message retain]; // because the deserializer consumes a reference on the message
deserializer = [[[XPC_CLASS(deserializer) alloc] initWithoutHeaderWithMessage: message] autorelease]; deserializer = [[[XPC_CLASS(deserializer) alloc] initWithoutHeaderWithMessage: message] autorelease];
@ -165,11 +177,15 @@ static void dispatch_mach_handler(void* context, dispatch_mach_reason_t reason,
} }
serverPeer = [[XPC_CLASS(connection) alloc] initAsServerPeerForServer: self sendPort: sendPort receivePort: receivePort]; serverPeer = [[XPC_CLASS(connection) alloc] initAsServerPeerForServer: self sendPort: sendPort receivePort: receivePort];
if (token) {
[serverPeer setRemoteCredentials: token];
}
[self addServerPeer: serverPeer]; // takes ownership of the server peer connection [self addServerPeer: serverPeer]; // takes ownership of the server peer connection
this->event_handler(serverPeer); this->event_handler(serverPeer);
} else { } else {
XPC_CLASS(dictionary)* dict = nil; XPC_CLASS(dictionary)* dict = nil;
audit_token_t* token = NULL;
if (header->msgh_id != XPC_MSGH_ID_MESSAGE) { if (header->msgh_id != XPC_MSGH_ID_MESSAGE) {
xpc_log(XPC_LOG_NOTICE, "peer connection received non-normal message in normal event handler"); xpc_log(XPC_LOG_NOTICE, "peer connection received non-normal message in normal event handler");
@ -177,6 +193,12 @@ static void dispatch_mach_handler(void* context, dispatch_mach_reason_t reason,
return; return;
} }
token = get_audit_token(header);
if (token) {
[self setRemoteCredentials: token];
}
[message retain]; // because the deserializer consumes a reference on the message [message retain]; // because the deserializer consumes a reference on the message
dict = [XPC_CLASS(deserializer) process: message]; dict = [XPC_CLASS(deserializer) process: message];
dict.associatedConnection = self; dict.associatedConnection = self;
@ -306,11 +328,18 @@ static void dmxh_async_reply_handler(void* self_context, dispatch_mach_reason_t
xpc_object_t result = NULL; xpc_object_t result = NULL;
mach_msg_header_t* header = dispatch_mach_msg_get_msg(message, NULL); mach_msg_header_t* header = dispatch_mach_msg_get_msg(message, NULL);
mach_port_t localPort = header->msgh_local_port; mach_port_t localPort = header->msgh_local_port;
audit_token_t* token = NULL;
xpc_log(XPC_LOG_DEBUG, "connection %p: async reply handler got event %lu (%s)\n", self, reason, reason_to_string(reason)); xpc_log(XPC_LOG_DEBUG, "connection %p: async reply handler got event %lu (%s)\n", self, reason, reason_to_string(reason));
switch (reason) { switch (reason) {
case DISPATCH_MACH_MESSAGE_RECEIVED: { case DISPATCH_MACH_MESSAGE_RECEIVED: {
token = get_audit_token(header);
if (token) {
[self setRemoteCredentials: token];
}
[message retain]; // because the deserializer consumes a reference on the message [message retain]; // because the deserializer consumes a reference on the message
result = [XPC_CLASS(deserializer) process: message]; result = [XPC_CLASS(deserializer) process: message];
if (!result) { if (!result) {
@ -480,6 +509,7 @@ OS_OBJECT_USES_XREF_DISPOSE();
pthread_rwlock_destroy(&this->activated_lock); pthread_rwlock_destroy(&this->activated_lock);
pthread_rwlock_destroy(&this->server_peers_lock); pthread_rwlock_destroy(&this->server_peers_lock);
pthread_rwlock_destroy(&this->remote_credentials_lock);
xpc_mach_port_release_send(this->send_port); xpc_mach_port_release_send(this->send_port);
xpc_mach_port_release_send(this->checkin_port); xpc_mach_port_release_send(this->checkin_port);
@ -511,10 +541,16 @@ OS_OBJECT_USES_XREF_DISPOSE();
pthread_rwlock_init(&this->activated_lock, NULL); pthread_rwlock_init(&this->activated_lock, NULL);
pthread_rwlock_init(&this->server_peers_lock, NULL); pthread_rwlock_init(&this->server_peers_lock, NULL);
pthread_rwlock_init(&this->remote_credentials_lock, NULL);
this->suspension_count = 1; this->suspension_count = 1;
xpc_genarr_connection_init(&this->server_peers, true, server_peer_array_item_dtor); xpc_genarr_connection_init(&this->server_peers, true, server_peer_array_item_dtor);
// fill it with invalid values
// the default value of 0 can lead to false positives of root access
// (this doesn't matter so much for Darling, but we'll do it anyways)
memset(&this->remote_credentials, 0xff, sizeof(audit_token_t));
} }
// init helper/common init // init helper/common init
@ -905,6 +941,22 @@ error_out:
return result; return result;
} }
- (void)setRemoteCredentials: (audit_token_t*)token
{
XPC_THIS_DECL(connection);
pthread_rwlock_wrlock(&this->remote_credentials_lock);
memcpy(&this->remote_credentials, token, sizeof(audit_token_t));
pthread_rwlock_unlock(&this->remote_credentials_lock);
}
- (void)copyRemoteCredentials: (audit_token_t*)outToken
{
XPC_THIS_DECL(connection);
pthread_rwlock_rdlock(&this->remote_credentials_lock);
memcpy(outToken, &this->remote_credentials, sizeof(audit_token_t));
pthread_rwlock_unlock(&this->remote_credentials_lock);
}
@end @end
// //
@ -1011,27 +1063,53 @@ const char* xpc_connection_get_name(xpc_connection_t xconn) {
return NULL; return NULL;
}; };
void audit_token_to_au32(audit_token_t atoken, uid_t* auidp, uid_t* euidp, gid_t* egidp, uid_t* ruidp, gid_t* rgidp, pid_t* pidp, au_asid_t* asidp, au_tid_t* tidp);
XPC_EXPORT XPC_EXPORT
uid_t xpc_connection_get_euid(xpc_connection_t xconn) { uid_t xpc_connection_get_euid(xpc_connection_t xconn) {
xpc_stub(); TO_OBJC_CHECKED(connection, xconn, conn) {
audit_token_t token;
uid_t euid;
[conn copyRemoteCredentials: &token];
audit_token_to_au32(token, NULL, &euid, NULL, NULL, NULL, NULL, NULL, NULL);
return euid;
}
return UID_MAX; return UID_MAX;
}; };
XPC_EXPORT XPC_EXPORT
gid_t xpc_connection_get_egid(xpc_connection_t xconn) { gid_t xpc_connection_get_egid(xpc_connection_t xconn) {
xpc_stub(); TO_OBJC_CHECKED(connection, xconn, conn) {
audit_token_t token;
gid_t egid;
[conn copyRemoteCredentials: &token];
audit_token_to_au32(token, NULL, NULL, &egid, NULL, NULL, NULL, NULL, NULL);
return egid;
}
return GID_MAX; return GID_MAX;
}; };
XPC_EXPORT XPC_EXPORT
pid_t xpc_connection_get_pid(xpc_connection_t xconn) { pid_t xpc_connection_get_pid(xpc_connection_t xconn) {
xpc_stub(); TO_OBJC_CHECKED(connection, xconn, conn) {
audit_token_t token;
pid_t pid;
[conn copyRemoteCredentials: &token];
audit_token_to_au32(token, NULL, NULL, NULL, NULL, NULL, &pid, NULL, NULL);
return pid;
}
return -1; return -1;
}; };
XPC_EXPORT XPC_EXPORT
au_asid_t xpc_connection_get_asid(xpc_connection_t xconn) { au_asid_t xpc_connection_get_asid(xpc_connection_t xconn) {
xpc_stub(); TO_OBJC_CHECKED(connection, xconn, conn) {
audit_token_t token;
au_asid_t asid;
[conn copyRemoteCredentials: &token];
audit_token_to_au32(token, NULL, NULL, NULL, NULL, NULL, NULL, &asid, NULL);
return asid;
}
return AU_ASSIGN_ASID; return AU_ASSIGN_ASID;
}; };
@ -1127,7 +1205,9 @@ void xpc_connection_enable_termination_imminent_event(xpc_connection_t xconn) {
XPC_EXPORT XPC_EXPORT
void xpc_connection_get_audit_token(xpc_connection_t xconn, audit_token_t* out_token) { void xpc_connection_get_audit_token(xpc_connection_t xconn, audit_token_t* out_token) {
xpc_stub(); TO_OBJC_CHECKED(connection, xconn, conn) {
[conn copyRemoteCredentials: out_token];
}
}; };
XPC_EXPORT XPC_EXPORT

View File

@ -1,6 +1,7 @@
#import <xpc/xpc.h> #import <xpc/xpc.h>
#import <launch.h> #import <launch.h>
#import <xpc/util.h> #import <xpc/util.h>
#import <xpc/private.h>
// //
// private C API // private C API
@ -186,10 +187,69 @@ char* launch_version_for_user_service_4coresim(const char* name) {
return NULL; return NULL;
}; };
xpc_object_t ld2xpc(launch_data_t data);
static void launch_data_dict_iterator(launch_data_t value, const char* key, void* context) {
xpc_object_t* result = context;
xpc_object_t xpc_value = ld2xpc(value);
xpc_dictionary_set_value(*result, key, xpc_value);
};
// this function isn't present in the official libxpc,
// so i'm guessing that it's actually an inline function
XPC_EXPORT XPC_EXPORT
xpc_object_t ld2xpc(launch_data_t data) { xpc_object_t ld2xpc(launch_data_t data) {
xpc_stub(); xpc_object_t result = NULL;
return NULL;
switch (launch_data_get_type(data)) {
case LAUNCH_DATA_DICTIONARY: {
result = xpc_dictionary_create(NULL, NULL, 0);
launch_data_dict_iterate(data, launch_data_dict_iterator, &result);
} break;
case LAUNCH_DATA_ARRAY: {
size_t length = launch_data_array_get_count(data);
result = xpc_array_create(NULL, 0);
for (size_t i = 0; i < length; ++i) {
xpc_array_append_value(result, ld2xpc(launch_data_array_get_index(data, i)));
}
} break;
case LAUNCH_DATA_FD: {
result = xpc_fd_create(launch_data_get_fd(data));
} break;
case LAUNCH_DATA_INTEGER: {
result = xpc_int64_create(launch_data_get_integer(data));
} break;
case LAUNCH_DATA_REAL: {
result = xpc_double_create(launch_data_get_real(data));
} break;
case LAUNCH_DATA_BOOL: {
result = xpc_bool_create(launch_data_get_bool(data));
} break;
case LAUNCH_DATA_STRING: {
result = xpc_string_create(launch_data_get_string(data));
} break;
case LAUNCH_DATA_OPAQUE: {
result = xpc_data_create(launch_data_get_opaque(data), launch_data_get_opaque_size(data));
} break;
case LAUNCH_DATA_ERRNO: {
result = xpc_int64_create(launch_data_get_errno(data));
} break;
case LAUNCH_DATA_MACHPORT: {
// NOTE: i have verified that the contained port does in fact always hold a receive right, so this is the right XPC class to use
result = xpc_mach_recv_create(launch_data_get_machport(data));
} break;
}
return result;
}; };
XPC_EXPORT XPC_EXPORT

View File

@ -99,37 +99,37 @@ XPC_CLASS_HEADER(pipe);
switch (ret) { switch (ret) {
case MACH_RCV_INVALID_NAME: /* fallthrough */ case MACH_RCV_INVALID_NAME: /* fallthrough */
case MACH_RCV_IN_SET: /* fallthrough */ case MACH_RCV_IN_SET: /* fallthrough */
case MACH_RCV_TIMED_OUT: /* fallthrough */ case MACH_RCV_TIMED_OUT: /* fallthrough */
case MACH_RCV_INTERRUPTED: /* fallthrough */ case MACH_RCV_INTERRUPTED: /* fallthrough */
case MACH_RCV_PORT_DIED: /* fallthrough */ case MACH_RCV_PORT_DIED: /* fallthrough */
case MACH_RCV_PORT_CHANGED: { case MACH_RCV_PORT_CHANGED: {
// nothing happened to the message // nothing happened to the message
status = EIO; status = EIO;
} break; } break;
case MACH_RCV_HEADER_ERROR: /* fallthrough */ case MACH_RCV_HEADER_ERROR: /* fallthrough */
case MACH_RCV_INVALID_NOTIFY: { case MACH_RCV_INVALID_NOTIFY: {
// message was dequeued and destroyed // message was dequeued and destroyed
status = EIO; status = EIO;
} break; } break;
case MACH_RCV_TOO_LARGE: { case MACH_RCV_TOO_LARGE: {
status = EAGAIN; status = EAGAIN;
} break; } break;
case MACH_RCV_BODY_ERROR: /* fallthrough */ case MACH_RCV_BODY_ERROR: /* fallthrough */
case MACH_RCV_INVALID_DATA: { case MACH_RCV_INVALID_DATA: {
// message was received // message was received
status = EIO; status = EIO;
} break; } break;
case MACH_MSG_SUCCESS: { case MACH_MSG_SUCCESS: {
status = 0; status = 0;
} break; } break;
} }
return status; return status;
@ -378,7 +378,7 @@ out:
self = [self initWithPort: servicePort flags: flags]; self = [self initWithPort: servicePort flags: flags];
//xpc_mach_port_release_send(servicePort); xpc_mach_port_release_send(servicePort);
return self; return self;
} }
@ -390,12 +390,10 @@ out:
pthread_rwlock_init(&this->state_lock, NULL); pthread_rwlock_init(&this->state_lock, NULL);
/*
if (xpc_mach_port_retain_send(port) != KERN_SUCCESS) { if (xpc_mach_port_retain_send(port) != KERN_SUCCESS) {
[self release]; [self release];
return nil; return nil;
} }
*/
this->checkin_port = port; this->checkin_port = port;
this->send_port = xpc_mach_port_create_send_receive(); this->send_port = xpc_mach_port_create_send_receive();

View File

@ -34,6 +34,7 @@ static void handle_server_peer_error(xpc_connection_t connection, xpc_object_t e
}; };
static void handle_new_connection(xpc_connection_t connection) { static void handle_new_connection(xpc_connection_t connection) {
server_peer_log("got new client with pid=%d, euid=%d, and egid=%d", xpc_connection_get_pid(connection), xpc_connection_get_euid(connection), xpc_connection_get_egid(connection));
xpc_connection_set_event_handler(connection, ^(xpc_object_t object) { xpc_connection_set_event_handler(connection, ^(xpc_object_t object) {
xpc_type_t obj_type = xpc_get_type(object); xpc_type_t obj_type = xpc_get_type(object);
if (obj_type == (xpc_type_t)XPC_TYPE_DICTIONARY) { if (obj_type == (xpc_type_t)XPC_TYPE_DICTIONARY) {