mirror of
https://github.com/darlinghq/darling-libxpc.git
synced 2024-11-23 03:39:40 +00:00
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:
parent
90b934b336
commit
b91537684e
@ -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" {
|
||||
|
10
include/xpc/private/mach_recv.h
Normal file
10
include/xpc/private/mach_recv.h
Normal 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_
|
14
include/xpc/private/mach_send.h
Normal file
14
include/xpc/private/mach_send.h
Normal 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_
|
@ -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_
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
52
src/pipe.m
52
src/pipe.m
@ -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();
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user