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/private/pipe.h>
#include <xpc/private/endpoint.h>
#include <xpc/private/mach_send.h>
#include <xpc/private/mach_recv.h>
#ifdef __cplusplus
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;
pthread_rwlock_t server_peers_lock;
xpc_genarr_connection_t server_peers;
pthread_rwlock_t remote_credentials_lock;
audit_token_t remote_credentials;
//
// 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;
- (xpc_object_t)sendMessageWithSynchronousReply: (XPC_CLASS(dictionary)*)message;
- (void)setRemoteCredentials: (audit_token_t*)token;
- (void)copyRemoteCredentials: (audit_token_t*)outToken;
@end
#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;
};
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) {
XPC_CLASS(connection)* self = context;
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 receivePort = MACH_PORT_NULL;
XPC_CLASS(connection)* serverPeer = nil;
audit_token_t* token = NULL;
if (header->msgh_id != XPC_MSGH_ID_CHECKIN) {
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;
}
token = get_audit_token(header);
[message retain]; // because the deserializer consumes a reference on the message
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];
if (token) {
[serverPeer setRemoteCredentials: token];
}
[self addServerPeer: serverPeer]; // takes ownership of the server peer connection
this->event_handler(serverPeer);
} else {
XPC_CLASS(dictionary)* dict = nil;
audit_token_t* token = NULL;
if (header->msgh_id != XPC_MSGH_ID_MESSAGE) {
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;
}
token = get_audit_token(header);
if (token) {
[self setRemoteCredentials: token];
}
[message retain]; // because the deserializer consumes a reference on the message
dict = [XPC_CLASS(deserializer) process: message];
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;
mach_msg_header_t* header = dispatch_mach_msg_get_msg(message, NULL);
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));
switch (reason) {
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
result = [XPC_CLASS(deserializer) process: message];
if (!result) {
@ -480,6 +509,7 @@ OS_OBJECT_USES_XREF_DISPOSE();
pthread_rwlock_destroy(&this->activated_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->checkin_port);
@ -511,10 +541,16 @@ OS_OBJECT_USES_XREF_DISPOSE();
pthread_rwlock_init(&this->activated_lock, NULL);
pthread_rwlock_init(&this->server_peers_lock, NULL);
pthread_rwlock_init(&this->remote_credentials_lock, NULL);
this->suspension_count = 1;
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
@ -905,6 +941,22 @@ error_out:
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
//
@ -1011,27 +1063,53 @@ const char* xpc_connection_get_name(xpc_connection_t xconn) {
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
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;
};
XPC_EXPORT
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;
};
XPC_EXPORT
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;
};
XPC_EXPORT
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;
};
@ -1127,7 +1205,9 @@ void xpc_connection_enable_termination_imminent_event(xpc_connection_t xconn) {
XPC_EXPORT
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

View File

@ -1,6 +1,7 @@
#import <xpc/xpc.h>
#import <launch.h>
#import <xpc/util.h>
#import <xpc/private.h>
//
// private C API
@ -186,10 +187,69 @@ char* launch_version_for_user_service_4coresim(const char* name) {
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_object_t ld2xpc(launch_data_t data) {
xpc_stub();
return NULL;
xpc_object_t result = 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

View File

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