From: Vibhav Pant vibhavp@gmail.com
--- dlls/winebth.sys/bthenum.c | 143 ++++++ dlls/winebth.sys/dbus.c | 844 ++++++++++++++++++++++++++++++- dlls/winebth.sys/unixlib.c | 36 +- dlls/winebth.sys/unixlib.h | 21 + dlls/winebth.sys/unixlib_priv.h | 6 +- dlls/winebth.sys/winebluetooth.c | 44 +- dlls/winebth.sys/winebluetooth.h | 40 ++ dlls/winebth.sys/winebth.c | 56 +- dlls/winebth.sys/winebth_priv.h | 2 + include/bthdef.h | 41 ++ include/bthioctl.h | 2 + 11 files changed, 1227 insertions(+), 8 deletions(-)
diff --git a/dlls/winebth.sys/bthenum.c b/dlls/winebth.sys/bthenum.c index 2d9ed16c070..b12c8d9ab94 100644 --- a/dlls/winebth.sys/bthenum.c +++ b/dlls/winebth.sys/bthenum.c @@ -116,6 +116,7 @@ struct bthenum_phys_remote_radio_ext winebluetooth_device_t remote_radio_handle; winebluetooth_device_props_mask_t known_props; struct winebluetooth_device_properties props; + UINT16 passkey_keys_entered;
UNICODE_STRING device_link_name;
@@ -160,6 +161,7 @@ NTSTATUS WINAPI bthenum_device_added( DEVICE_OBJECT *fdo_bus, child_device->props = event.props; child_device->known_props = event.known_props_mask; child_device->device_obj = child_pdo; + child_device->passkey_keys_entered = 0;
list_init( &child_device->services );
@@ -255,6 +257,147 @@ static void device_send_radio_in_range_event( DEVICE_OBJECT *device_obj, UINT32 free( notification ); }
+static NTSTATUS device_send_auth_event( DEVICE_OBJECT *radio_pdo, DEVICE_OBJECT *device_pdo, + struct winebluetooth_auth_event event ) +{ + TARGET_DEVICE_CUSTOM_NOTIFICATION *notification = NULL; + DWORD size; + NTSTATUS status; + + switch (event.event_type) + { + case BLUETOOTH_AUTH_EVENT_DEVICE_AUTH_REQUEST: + { + BTH_AUTHENTICATION_REQUEST *req; + + size = offsetof( TARGET_DEVICE_CUSTOM_NOTIFICATION, CustomDataBuffer[sizeof( *req )] ); + if (event.auth_method == BLUETOOTH_AUTHENTICATION_METHOD_LEGACY) + size += sizeof( event.auth_data.pincode ); + + notification = calloc( 1, size ); + if (!notification) + { + status = STATUS_NO_MEMORY; + break; + } + + notification->Version = 1; + notification->Size = size; + notification->Event = GUID_BLUETOOTH_AUTHENTICATION_REQUEST; + notification->FileObject = NULL; + notification->NameBufferOffset = -1; + + req = (BTH_AUTHENTICATION_REQUEST *)notification->CustomDataBuffer; + device_get_properties( device_pdo, &req->deviceInfo ); + req->authenticationMethod = event.auth_method; + /* We don't have a way to get the authentication requirements for a device right now. */ + req->AuthenticationRequirements = BLUETOOTH_MITM_ProtectionNotDefined; + /* Same for IO capabilities, so we try to guess them from the authentication method. */ + switch (event.auth_method) + { + case BLUETOOTH_AUTHENTICATION_METHOD_LEGACY: + { + UCHAR *pin = (UCHAR *)¬ification->CustomDataBuffer[sizeof(*req)]; + notification->NameBufferOffset = sizeof( *req ); + memcpy( pin, event.auth_data.pincode, sizeof( event.auth_data.pincode ) ); + req->IoCapability = BLUETOOTH_IO_CAPABILITY_KEYBOARDONLY; + break; + } + case BLUETOOTH_AUTHENTICATION_METHOD_PASSKEY: + req->Passkey = event.auth_data.passkey; + req->IoCapability = BLUETOOTH_IO_CAPABILITY_KEYBOARDONLY; + break; + case BLUETOOTH_AUTHENTICATION_METHOD_NUMERIC_COMPARISON: + req->Numeric_Value = event.auth_data.passkey; + req->IoCapability = BLUETOOTH_IO_CAPABILITY_DISPLAYYESNO; + break; + case BLUETOOTH_AUTHENTICATION_METHOD_PASSKEY_NOTIFICATION: + req->Passkey = event.auth_data.passkey; + req->IoCapability = BLUETOOTH_IO_CAPABILITY_DISPLAYONLY; + break; + default: + req->IoCapability = BLUETOOTH_IO_CAPABILITY_UNDEFINED; + } + break; + } + case BLUETOOTH_AUTH_EVENT_DEVICE_KEYPRESS: + { + BTH_HCI_KEYPRESS_INFO *info; + ULONG *passkey; + struct bthenum_phys_remote_radio_ext *ext = + (struct bthenum_phys_remote_radio_ext *)device_pdo->DeviceExtension; + + size = offsetof( TARGET_DEVICE_CUSTOM_NOTIFICATION, + CustomDataBuffer[sizeof( *info ) + sizeof( ULONG )] ); + notification = calloc( 1, size ); + if (!notification) + { + status = STATUS_NO_MEMORY; + break; + } + + info = (BTH_HCI_KEYPRESS_INFO *)notification->CustomDataBuffer; + passkey = (ULONG *)¬ification->CustomDataBuffer[sizeof(*info)]; + + notification->Version = 1; + notification->Size = size; + notification->Event = GUID_BLUETOOTH_AUTHENTICATION_REQUEST; + notification->FileObject = NULL; + notification->NameBufferOffset = -1; + + *passkey = event.auth_data.passkey; + if (event.auth_data.keypress.entered > ext->passkey_keys_entered) + info->NotificationType = BTH_KEYPRESS_NOTIFICATION_PASSKEY_DIGIT_ENTERED; + else if (event.auth_data.keypress.entered < ext->passkey_keys_entered) + info->NotificationType = !event.auth_data.keypress.entered + ? BTH_KEYPRESS_NOTIFICATION_PASSKEY_CLEARED + : BTH_KEYPRESS_NOTIFICATION_PASSKEY_DIGIT_ERASED; + else if (!event.auth_data.keypress.entered) + info->NotificationType = BTH_KEYPRESS_NOTIFICATION_PASSKEY_STARTED; + else + info->NotificationType = BTH_KEYPRESS_NOTIFICATION_PASSKEY_ENTRY_COMPLETED; + ext->passkey_keys_entered = + info->NotificationType == BTH_KEYPRESS_NOTIFICATION_PASSKEY_ENTRY_COMPLETED + ? 0 + : event.auth_data.keypress.entered; + } + case BLUETOOTH_AUTH_EVENT_DEVICE_SERVICE_AUTH_REQUEST: + default: + FIXME( "Unsupported auth event: %#x\n", event.event_type ); + status = STATUS_NOT_SUPPORTED; + } + + if (notification) + { + status = IoReportTargetDeviceChange( radio_pdo, notification ); + free( notification ); + } + return status; +} + +NTSTATUS WINAPI bthenum_device_auth_event( DEVICE_OBJECT *fdo_bus, + struct winebluetooth_auth_event event ) +{ + struct bthenum_func_local_radio_ext *fdo; + struct bthenum_phys_remote_radio_ext *remote_device; + NTSTATUS status = STATUS_NOT_FOUND; + + fdo = fdo_from_DEVICE_OBJECT( fdo_bus ); + RtlEnterCriticalSection( &fdo->cs ); + LIST_FOR_EACH_ENTRY( remote_device, &fdo->devices, struct bthenum_phys_remote_radio_ext, entry ) + { + if (winebluetooth_device_equal( remote_device->remote_radio_handle, event.device )) + { + status = + device_send_auth_event( fdo->parent_radio_pdo, remote_device->device_obj, event ); + break; + } + } + RtlLeaveCriticalSection( &fdo->cs ); + winebluetooth_device_free( event.device ); + return status; +} + static void device_refresh_properties( struct bthenum_phys_remote_radio_ext *remote_device, struct winebluetooth_device_properties new_props, winebluetooth_device_props_mask_t changed_props ) diff --git a/dlls/winebth.sys/dbus.c b/dlls/winebth.sys/dbus.c index 5f5f5d4c2d3..31251806822 100644 --- a/dlls/winebth.sys/dbus.c +++ b/dlls/winebth.sys/dbus.c @@ -29,6 +29,7 @@ #include <dlfcn.h> #include <assert.h> #include <pthread.h> +#include <errno.h>
#ifdef SONAME_LIBDBUS_1 #include <dbus/dbus.h> @@ -43,6 +44,7 @@ #include <bluetoothapis.h>
#include <wine/debug.h> +#include <wine/rbtree.h>
#include "winebluetooth.h"
@@ -87,7 +89,19 @@ const int bluez_timeout = -1;
#define BLUEZ_DEST "org.bluez" #define BLUEZ_INTERFACE_ADAPTER "org.bluez.Adapter1" -#define BLUEZ_INTERFACE_DEVICE "org.bluez.Device1" +#define BLUEZ_INTERFACE_DEVICE "org.bluez.Device1" +#define BLUEZ_INTERFACE_AGENT "org.bluez.Agent1" +#define BLUEZ_INTERFACE_AGENT_MANAGER "org.bluez.AgentManager1" + +#define BLUEZ_AGENT_METHOD_RELEASE "Release" +#define BLUEZ_AGENT_METHOD_REQUEST_PIN_CODE "RequestPinCode" +#define BLUEZ_AGENT_METHOD_DISPLAY_PIN_CODE "DisplayPinCode" +#define BLUEZ_AGENT_METHOD_REQUEST_PASSKEY "RequestPasskey" +#define BLUEZ_AGENT_METHOD_DISPLAY_PASSKEY "DisplayPasskey" +#define BLUEZ_AGENT_METHOD_REQUEST_CONFIRMATION "RequestConfirmation" +#define BLUEZ_AGENT_METHOD_REQUEST_AUTHORIZATION "RequestAuthorization" +#define BLUEZ_AGENT_METHOD_AUTHORIZE_SERVICE "AuthorizeService" +#define BLUEZ_AGENT_METHOD_CANCEL "Cancel"
#define DO_FUNC( f ) typeof( f ) (*p_##f) DBUS_FUNCS; @@ -1745,7 +1759,825 @@ static struct bluez_watcher_event *bluez_watcher_event_queue_ready( struct bluez return NULL; }
-NTSTATUS bluez_dbus_loop( void *c, void *watcher, +enum bluetooth_auth_request_status +{ + /* No authorisation request is active. */ + BTH_AUTH_REQ_STATUS_NONE, + /* We have received an authorisation request, and are waiting for a response from the user. */ + BTH_AUTH_REQ_STATUS_WAITING_FOR_RESPONSE, + /* A passkey keypress event. Requires no response from the user. */ + BTH_AUTH_REQ_STATUS_KEYPRESS_EVENT +}; + +struct bluez_auth_agent_request +{ + enum bluetooth_auth_request_status status; + + DBusConnection *connection; + DBusMessage *message; + DBusPreallocatedSend *prealloc; + + struct unix_name *device_path; + struct unix_name *adapter_path; + BOOL auth_service; + BLUETOOTH_AUTHENTICATION_METHOD auth_method; + union winebluetooth_auth_event_data auth_data; +}; + +/* BlueZ does not have a way to distingish between method calls to the auth agent for incoming + * authentication/pairing requests, and pairing procedures initiated by us (for instance, by calling + * Pair() on a device). To implement BluetoothAuthenticateDevice, we need to first store the + * user-provided authentication data, initiate the auth process, and then return this data once + * BlueZ calls the auth agent. This struct is used to store that data. */ +struct bluez_auth_agent_reply +{ + struct rb_tree entry; + + struct unix_name *device_path; + char passkey[17]; +}; + +struct bluez_auth_agent_ctx +{ + pthread_mutex_t lock; + /* BlueZ only supports a single authentication session at one time. */ + struct bluez_auth_agent_request cur_request; + /* Replies to auth agent methods for authentication processes initiated by us. Entries are + * removed once they have been sent to BlueZ. */ + struct rb_tree auth_replies; +}; + +static int auth_reply_compare( const void *key, const struct rb_entry *entry ) +{ + struct bluez_auth_agent_reply *reply = + RB_ENTRY_VALUE( entry, struct bluez_auth_agent_reply, entry ); + + return strcmp( key, reply->device_path->str ); +} + +UINT_PTR bluez_auth_agent_ctx_init( void ) +{ + struct bluez_auth_agent_ctx *ctx; + + ctx = calloc( 1, sizeof( *ctx ) ); + if (!ctx) + return (UINT_PTR)NULL; + + pthread_mutex_init( &ctx->lock, NULL ); + ctx->cur_request.status = BTH_AUTH_REQ_STATUS_NONE; + rb_init( &ctx->auth_replies, auth_reply_compare ); + return (UINT_PTR)ctx; +} + +/* Requires cur_request_lock to be held. */ +static void bluez_auth_agent_ctx_reset( struct bluez_auth_agent_ctx *ctx, BOOL free_prealloc ) +{ + if (free_prealloc) + p_dbus_connection_free_preallocated_send( ctx->cur_request.connection, + ctx->cur_request.prealloc ); + p_dbus_message_unref( ctx->cur_request.message ); + p_dbus_connection_unref( ctx->cur_request.connection ); + if (ctx->cur_request.device_path) + unix_name_free( ctx->cur_request.device_path ); + ctx->cur_request.status = BTH_AUTH_REQ_STATUS_NONE; +} + +static void bluez_auth_agent_request_pin_code( DBusConnection *connection, DBusMessage *message, + DBusPreallocatedSend *prealloc, + struct bluez_auth_agent_ctx *ctx ) +{ + DBusMessage *reply_error = NULL; + DBusError error; + const char *device_path = NULL; + struct rb_entry *reply_entry; + + TRACE( "(%p, %s, %p, %p)\n", connection, dbgstr_dbus_message( message ), prealloc, ctx ); + + pthread_mutex_lock( &ctx->lock ); + if (ctx->cur_request.status != BTH_AUTH_REQ_STATUS_NONE) + { + /* This shouldn't happen, as BlueZ only seems to have one auth session active at a time. + * Still, doesn't hurt to check. */ + reply_error = p_dbus_message_new_error_printf( + message, DBUS_ERROR_FAILED, "an authorisation request is already pending" ); + goto error; + } + + p_dbus_error_init( &error ); + if(!p_dbus_message_get_args( message, &error, DBUS_TYPE_OBJECT_PATH, &device_path, + DBUS_TYPE_INVALID )) + { + ERR( "Could not get device parameter from RequestPinCode call: %s: %s.\n", + debugstr_a( error.name ), debugstr_a( error.message ) ); + reply_error = p_dbus_message_new_error( message, error.name, error.message ); + p_dbus_error_free( &error ); + goto error; + } + p_dbus_error_free( &error ); + + reply_entry = rb_get( &ctx->auth_replies, device_path ); + if (reply_entry) + { + /* This auth procedure has been initiated by us, and we have a reply. */ + DBusMessage *reply; + struct bluez_auth_agent_reply *auth_reply = + RB_ENTRY_VALUE( reply_entry, struct bluez_auth_agent_reply, entry ); + + rb_remove( &ctx->auth_replies, reply_entry ); + unix_name_free( auth_reply->device_path ); + reply = p_dbus_message_new_method_return( message ); + if (!reply) + { + free( auth_reply ); + goto error; + } + p_dbus_message_append_args( reply, DBUS_TYPE_STRING, &auth_reply->passkey, + DBUS_TYPE_INVALID ); + free( auth_reply ); + p_dbus_connection_send_preallocated( connection, prealloc, reply, NULL ); + p_dbus_message_unref( reply ); + } + else + { + struct unix_name *device, *adapter; + device = unix_name_get_or_create( device_path ); + if (!device) + goto error; + adapter = bluez_device_path_get_adapter( device_path ); + if (!adapter) + { + unix_name_free( device ); + goto error; + } + + ctx->cur_request.status = BTH_AUTH_REQ_STATUS_WAITING_FOR_RESPONSE; + ctx->cur_request.connection = p_dbus_connection_ref( connection ); + ctx->cur_request.message = p_dbus_message_ref( message ); + ctx->cur_request.prealloc = prealloc; + ctx->cur_request.auth_method = BLUETOOTH_AUTHENTICATION_METHOD_LEGACY; + ctx->cur_request.auth_service = FALSE; + ctx->cur_request.device_path = device; + ctx->cur_request.adapter_path = adapter; + } + + goto done; +error: + if (reply_error) + { + p_dbus_connection_send_preallocated( connection, prealloc, reply_error, NULL ); + p_dbus_message_unref( reply_error ); + } + else p_dbus_connection_free_preallocated_send( connection, prealloc ); +done: + pthread_mutex_unlock( &ctx->lock ); +} + +static void bluez_auth_agent_display_pin_code( DBusConnection *connection, DBusMessage *message, + DBusPreallocatedSend *prealloc, + struct bluez_auth_agent_ctx *ctx ) +{ + DBusMessage *reply_error = NULL; + DBusError error; + const char *device_path = NULL, *pin_code; + struct unix_name *device, *adapter; + ULONG passkey; + + TRACE( "(%p, %s, %p, %p)\n", connection, dbgstr_dbus_message( message ), prealloc, ctx ); + + pthread_mutex_lock( &ctx->lock ); + if (ctx->cur_request.status != BTH_AUTH_REQ_STATUS_NONE) + { + reply_error = p_dbus_message_new_error_printf( + message, DBUS_ERROR_FAILED, "an authorisation request is already pending" ); + goto error; + } + + p_dbus_error_init( &error ); + if (!p_dbus_message_get_args( message, &error, DBUS_TYPE_OBJECT_PATH, + &device_path, DBUS_TYPE_STRING, &pin_code, + DBUS_TYPE_INVALID )) + { + reply_error = p_dbus_message_new_error( message, error.name, error.message ); + goto error; + } + p_dbus_error_free( &error ); + + errno = 0; + passkey = strtol( pin_code, NULL, 10 ); + if (errno) + { + reply_error = + p_dbus_message_new_error( message, "org.bluez.Error.Rejected", "invalid pin code" ); + goto error; + } + + device = unix_name_get_or_create( device_path ); + if (!device) + goto error; + adapter = bluez_device_path_get_adapter( device_path ); + if (!adapter) + { + unix_name_free( device ); + goto error; + } + + ctx->cur_request.status = BTH_AUTH_REQ_STATUS_WAITING_FOR_RESPONSE; + ctx->cur_request.connection = p_dbus_connection_ref( connection ); + ctx->cur_request.message = p_dbus_message_ref( message ); + ctx->cur_request.prealloc = prealloc; + ctx->cur_request.auth_method = BLUETOOTH_AUTHENTICATION_METHOD_PASSKEY_NOTIFICATION; + ctx->cur_request.auth_service = FALSE; + ctx->cur_request.device_path = device; + ctx->cur_request.adapter_path = adapter; + ctx->cur_request.auth_data.passkey = passkey; + + goto done; + error: + if (reply_error) + { + p_dbus_connection_send_preallocated( connection, prealloc, reply_error, NULL ); + p_dbus_message_unref( reply_error ); + } + else p_dbus_connection_free_preallocated_send( connection, prealloc ); +done: + pthread_mutex_unlock( &ctx->lock ); +} + +static void bluez_auth_agent_display_passkey( DBusConnection *connection, DBusMessage *message, + DBusPreallocatedSend *prealloc, + struct bluez_auth_agent_ctx *ctx ) +{ + DBusMessage *reply, *reply_error = NULL; + DBusError error; + const char *device_path = NULL; + struct unix_name *device, *adapter; + dbus_uint32_t passkey; + dbus_uint16_t entered; + + TRACE( "(%p, %s, %p, %p)\n", connection, dbgstr_dbus_message( message ), prealloc, ctx ); + + pthread_mutex_lock( &ctx->lock ); + if (ctx->cur_request.status != BTH_AUTH_REQ_STATUS_NONE) + { + reply_error = p_dbus_message_new_error_printf( + message, DBUS_ERROR_FAILED, "an authorisation request is already pending" ); + goto error; + } + + p_dbus_error_init( &error ); + if (!p_dbus_message_get_args( message, &error, DBUS_TYPE_OBJECT_PATH, + &device_path, DBUS_TYPE_UINT32, &passkey, + DBUS_TYPE_UINT16, &entered, DBUS_TYPE_INVALID )) + { + reply_error = p_dbus_message_new_error( message, error.name, error.message ); + goto error; + } + p_dbus_error_free( &error ); + + device = unix_name_get_or_create( device_path ); + if (!device) + goto error; + adapter = bluez_device_path_get_adapter( device_path ); + if (!adapter) + { + unix_name_free( device ); + goto error; + } + + ctx->cur_request.status = BTH_AUTH_REQ_STATUS_KEYPRESS_EVENT; + ctx->cur_request.device_path = device; + ctx->cur_request.adapter_path = adapter; + ctx->cur_request.auth_data.keypress.passkey = passkey; + ctx->cur_request.auth_data.keypress.entered = entered; + reply = p_dbus_message_new_method_return( message ); + if (!reply) + goto error; + p_dbus_connection_send_preallocated( connection, prealloc, reply, NULL ); + goto done; + + error: + if (reply_error) + { + p_dbus_connection_send_preallocated( connection, prealloc, reply_error, NULL ); + p_dbus_message_unref( reply_error ); + } + else p_dbus_connection_free_preallocated_send( connection, prealloc ); +done: + pthread_mutex_unlock( &ctx->lock ); +} + +static void bluez_auth_agent_request_passkey( DBusConnection *connection, DBusMessage *message, + DBusPreallocatedSend *prealloc, + struct bluez_auth_agent_ctx *ctx ) +{ + DBusMessage *reply_error = NULL; + DBusError error; + const char *device_path = NULL; + struct unix_name *device, *adapter; + struct rb_entry *reply_entry; + + TRACE( "(%p, %s, %p, %p)\n", connection, dbgstr_dbus_message( message ), prealloc, ctx ); + + pthread_mutex_lock( &ctx->lock ); + if (ctx->cur_request.status != BTH_AUTH_REQ_STATUS_NONE) + { + reply_error = p_dbus_message_new_error_printf( + message, DBUS_ERROR_FAILED, "an authorisation request is already pending" ); + goto error; + } + + p_dbus_error_init( &error ); + if (!p_dbus_message_get_args( message, &error, DBUS_TYPE_OBJECT_PATH, &device_path, + DBUS_TYPE_INVALID )) + { + reply_error = p_dbus_message_new_error( message, error.name, error.message ); + p_dbus_error_free( &error ); + goto error; + } + p_dbus_error_free( &error ); + + reply_entry = rb_get( &ctx->auth_replies, device_path ); + if (reply_entry) + { + DBusMessage *reply; + struct bluez_auth_agent_reply *auth_reply = + RB_ENTRY_VALUE( reply_entry, struct bluez_auth_agent_reply, entry ); + dbus_uint32_t passkey; + + rb_remove( &ctx->auth_replies, reply_entry ); + unix_name_free( auth_reply->device_path ); + reply = p_dbus_message_new_method_return( message ); + if (!reply) + { + free( auth_reply ); + goto error; + } + + errno = 0; + passkey = strtol( auth_reply->passkey, NULL, 10 ); + if (errno) + { + /* The user has provided a non-numerical passkey, which wouldn't work. */ + reply_error = p_dbus_message_new_error( message, "org.bluez.Error.Rejected", "" ); + free( auth_reply ); + goto error; + } + p_dbus_message_append_args( message, DBUS_TYPE_UINT32, &passkey, DBUS_TYPE_INVALID ); + free( auth_reply ); + p_dbus_connection_send_preallocated( connection, prealloc, reply, NULL ); + p_dbus_message_unref( reply ); + } + else + { + device = unix_name_get_or_create( device_path ); + if (!device) + goto error; + adapter = bluez_device_path_get_adapter( device_path ); + if (!adapter) + { + unix_name_free( device ); + goto error; + } + + ctx->cur_request.status = BTH_AUTH_REQ_STATUS_WAITING_FOR_RESPONSE; + ctx->cur_request.connection = p_dbus_connection_ref( connection ); + ctx->cur_request.message = p_dbus_message_ref( message ); + ctx->cur_request.prealloc = prealloc; + ctx->cur_request.auth_method = BLUETOOTH_AUTHENTICATION_METHOD_PASSKEY; + ctx->cur_request.auth_service = FALSE; + ctx->cur_request.device_path = device; + ctx->cur_request.adapter_path = adapter; + } + + goto done; + +error: + if (reply_error) + { + p_dbus_connection_send_preallocated( connection, prealloc, reply_error, NULL ); + p_dbus_message_unref( reply_error ); + } + else p_dbus_connection_free_preallocated_send( connection, prealloc ); +done: + pthread_mutex_unlock( &ctx->lock ); +} + +static void bluez_auth_agent_request_confirmation( DBusConnection *connection, DBusMessage *message, + DBusPreallocatedSend *prealloc, + struct bluez_auth_agent_ctx *ctx ) +{ + DBusMessage *reply_error = NULL; + DBusError error; + const char *device_path = NULL; + dbus_uint32_t num; + struct unix_name *device, *adapter; + + TRACE( "(%p, %s, %p, %p)\n", connection, dbgstr_dbus_message( message ), prealloc, ctx ); + + pthread_mutex_lock( &ctx->lock ); + if (ctx->cur_request.status != BTH_AUTH_REQ_STATUS_NONE) + { + reply_error = p_dbus_message_new_error_printf( + message, DBUS_ERROR_FAILED, "an authorisation request is already pending" ); + goto error; + } + + p_dbus_error_init( &error ); + if (!p_dbus_message_get_args( message, &error, DBUS_TYPE_OBJECT_PATH, &device_path, + DBUS_TYPE_UINT32, &num, DBUS_TYPE_INVALID )) + { + reply_error = p_dbus_message_new_error( message, error.name, error.message ); + p_dbus_error_free( &error ); + goto error; + } + p_dbus_error_free( &error ); + + TRACE( "Received a numerical confirmation request from BlueZ for the device %s.\n", + debugstr_a( device_path ) ); + device = unix_name_get_or_create( device_path ); + if (!device) + goto error; + adapter = bluez_device_path_get_adapter( device_path ); + if (!adapter) + { + unix_name_free( device ); + goto error; + } + + ctx->cur_request.status = BTH_AUTH_REQ_STATUS_WAITING_FOR_RESPONSE; + ctx->cur_request.connection = p_dbus_connection_ref( connection ); + ctx->cur_request.message = p_dbus_message_ref( message ); + ctx->cur_request.prealloc = prealloc; + ctx->cur_request.auth_method = BLUETOOTH_AUTHENTICATION_METHOD_NUMERIC_COMPARISON; + ctx->cur_request.auth_data.passkey = num; + ctx->cur_request.auth_service = FALSE; + ctx->cur_request.device_path = device; + ctx->cur_request.adapter_path = adapter; + goto done; + +error: + if (reply_error) + { + p_dbus_connection_send_preallocated( connection, prealloc, reply_error, NULL ); + p_dbus_message_unref( reply_error ); + } + else p_dbus_connection_free_preallocated_send( connection, prealloc ); +done: + pthread_mutex_unlock( &ctx->lock ); +} + +/* BlueZ uses this for Just-Works pairing. In Win32, the equivalent seems to be Numeric + * Comparison with MITMProtectionNotRequired. */ +static void bluez_auth_agent_request_authorization( DBusConnection *connection, + DBusMessage *message, + DBusPreallocatedSend *prealloc, + struct bluez_auth_agent_ctx *ctx ) +{ + DBusMessage *reply_error = NULL; + DBusError error; + const char *device_path = NULL; + struct unix_name *device, *adapter; + + TRACE( "(%p, %s, %p, %p)\n", connection, dbgstr_dbus_message( message ), prealloc, ctx ); + + pthread_mutex_lock( &ctx->lock ); + if (ctx->cur_request.status != BTH_AUTH_REQ_STATUS_NONE) + { + reply_error = p_dbus_message_new_error_printf( + message, DBUS_ERROR_FAILED, "an authorisation request is already pending" ); + goto error; + } + + p_dbus_error_init( &error ); + if (!p_dbus_message_get_args( message, &error, DBUS_TYPE_OBJECT_PATH, &device_path, + DBUS_TYPE_INVALID )) + { + reply_error = p_dbus_message_new_error( message, error.name, error.message ); + p_dbus_error_free( &error ); + goto error; + } + p_dbus_error_free( &error ); + + device = unix_name_get_or_create( device_path ); + if (!device) + goto error; + adapter = bluez_device_path_get_adapter( device_path ); + if (!adapter) + { + unix_name_free( device ); + goto error; + } + + ctx->cur_request.status = BTH_AUTH_REQ_STATUS_WAITING_FOR_RESPONSE; + ctx->cur_request.connection = p_dbus_connection_ref( connection ); + ctx->cur_request.message = p_dbus_message_ref( message ); + ctx->cur_request.prealloc = prealloc; + ctx->cur_request.auth_method = BLUETOOTH_AUTHENTICATION_METHOD_NUMERIC_COMPARISON; + /* Set the passkey to 0 for Just-Works pairing. */ + ctx->cur_request.auth_data.passkey = 0; + ctx->cur_request.auth_service = FALSE; + ctx->cur_request.device_path = device; + ctx->cur_request.adapter_path = adapter; + goto done; + +error: + if (reply_error) + { + p_dbus_connection_send_preallocated( connection, prealloc, reply_error, NULL ); + p_dbus_message_unref( reply_error ); + } + else + p_dbus_connection_free_preallocated_send( connection, prealloc ); + done: + pthread_mutex_unlock( &ctx->lock ); +} + +static void bluez_auth_agent_authorize_service( DBusConnection *connection, DBusMessage *message, + DBusPreallocatedSend *prealloc, + struct bluez_auth_agent_ctx *ctx ) + +{ + DBusMessage *reply_error = NULL; + DBusError error; + GUID guid; + const char *device_path = NULL, *uuid; + struct unix_name *device; + + TRACE( "(%p, %s, %p, %p)\n", connection, dbgstr_dbus_message( message ), prealloc, ctx ); + + pthread_mutex_lock( &ctx->lock ); + if (ctx->cur_request.status != BTH_AUTH_REQ_STATUS_NONE) + { + reply_error = p_dbus_message_new_error_printf( + message, DBUS_ERROR_FAILED, "an authorisation request is already pending" ); + goto error; + } + + p_dbus_error_init( &error ); + if (!p_dbus_message_get_args( message, &error, DBUS_TYPE_OBJECT_PATH, &device_path, + DBUS_TYPE_STRING, &uuid, DBUS_TYPE_INVALID )) + { + reply_error = p_dbus_message_new_error( message, error.name, error.message ); + p_dbus_error_free( &error ); + goto error; + } + p_dbus_error_free( &error ); + + if (!parse_uuid( &guid, uuid )) + { + reply_error = + p_dbus_message_new_error( message, "org.bluez.Error.Rejected", "invalid service uuid" ); + goto error; + } + + TRACE( "Received a service authorisation request from BlueZ for device %s uuid %s.\n", + debugstr_a( device_path ), uuid ); + device = unix_name_get_or_create( device_path ); + if (!device) + goto error; + + ctx->cur_request.status = BTH_AUTH_REQ_STATUS_WAITING_FOR_RESPONSE; + ctx->cur_request.connection = p_dbus_connection_ref( connection ); + ctx->cur_request.message = p_dbus_message_ref( message ); + ctx->cur_request.prealloc = prealloc; + ctx->cur_request.auth_service = TRUE; + ctx->cur_request.device_path = device; + ctx->cur_request.auth_data.service_uuid = guid; + goto done; + +error: + if (reply_error) + { + p_dbus_connection_send_preallocated( connection, prealloc, reply_error, NULL ); + p_dbus_message_unref( reply_error ); + } + else + p_dbus_connection_free_preallocated_send( connection, prealloc ); +done: + pthread_mutex_lock( &ctx->lock ); +} + +static void bluez_auth_agent_cancel( DBusConnection *connection, DBusMessage *message, + DBusPreallocatedSend *prealloc, + struct bluez_auth_agent_ctx *ctx ) +{ + DBusMessage *reply; + TRACE( "(%p, %s, %p, %p)\n", connection, dbgstr_dbus_message( message ), prealloc, ctx ); + + pthread_mutex_lock( &ctx->lock ); + if (ctx->cur_request.status == BTH_AUTH_REQ_STATUS_WAITING_FOR_RESPONSE) + bluez_auth_agent_ctx_reset( ctx, TRUE ); + else + { + ERR("Cancel called without a pending authorisation request\n"); + } + ctx->cur_request.status = BTH_AUTH_REQ_STATUS_NONE; + pthread_mutex_unlock( &ctx->lock ); + + reply = p_dbus_message_new_method_return( message ); + if (reply) + { + p_dbus_connection_send_preallocated( connection, prealloc, reply, NULL ); + } + else p_dbus_connection_free_preallocated_send( connection, prealloc ); +} + +static void bluez_auth_agent_release( DBusConnection *connection, DBusMessage *message, + DBusPreallocatedSend *prealloc, + struct bluez_auth_agent_ctx *ctx ) +{ + DBusMessage *reply; + TRACE( "(%p, %s, %p, %p)\n", connection, dbgstr_dbus_message( message ), prealloc, ctx ); + + pthread_mutex_lock( &ctx->lock ); + if (ctx->cur_request.status == BTH_AUTH_REQ_STATUS_WAITING_FOR_RESPONSE) + bluez_auth_agent_ctx_reset( ctx, TRUE ); + pthread_mutex_unlock( &ctx->lock ); + + reply = p_dbus_message_new_method_return( message ); + if (reply == NULL) + { + p_dbus_connection_free_preallocated_send( connection, prealloc ); + return; + } + p_dbus_connection_send_preallocated( connection, prealloc, reply, NULL ); +} + +struct dbus_object_method_entry +{ + const char *interface; + const char *method_name; + void (*method_callback)( DBusConnection *, DBusMessage *, DBusPreallocatedSend *, + struct bluez_auth_agent_ctx * ); +}; + +const static struct dbus_object_method_entry + bluez_auth_agent_object_methods[] = { + {BLUEZ_INTERFACE_AGENT, BLUEZ_AGENT_METHOD_RELEASE, bluez_auth_agent_release}, + {BLUEZ_INTERFACE_AGENT, BLUEZ_AGENT_METHOD_REQUEST_PIN_CODE, bluez_auth_agent_request_pin_code}, + {BLUEZ_INTERFACE_AGENT, BLUEZ_AGENT_METHOD_DISPLAY_PIN_CODE, bluez_auth_agent_display_pin_code}, + {BLUEZ_INTERFACE_AGENT, BLUEZ_AGENT_METHOD_REQUEST_PASSKEY, bluez_auth_agent_request_passkey}, + {BLUEZ_INTERFACE_AGENT, BLUEZ_AGENT_METHOD_DISPLAY_PASSKEY, bluez_auth_agent_display_passkey}, + {BLUEZ_INTERFACE_AGENT, BLUEZ_AGENT_METHOD_REQUEST_CONFIRMATION, bluez_auth_agent_request_confirmation}, + {BLUEZ_INTERFACE_AGENT, BLUEZ_AGENT_METHOD_REQUEST_AUTHORIZATION, bluez_auth_agent_request_authorization}, + {BLUEZ_INTERFACE_AGENT, BLUEZ_AGENT_METHOD_AUTHORIZE_SERVICE, bluez_auth_agent_authorize_service}, + {BLUEZ_INTERFACE_AGENT, BLUEZ_AGENT_METHOD_CANCEL, bluez_auth_agent_cancel} +}; + +static DBusHandlerResult bluez_auth_agent_vtable_message_handler( DBusConnection *connection, + DBusMessage *message, void *data ) +{ + struct bluez_auth_agent_ctx *ctx = data; + SIZE_T i; + + TRACE_(dbus)("(%p, %s, %p)", connection, dbgstr_dbus_message( message ), data); + + for (i = 0; i < ARRAYSIZE( bluez_auth_agent_object_methods ); i++) + { + const char *iface = bluez_auth_agent_object_methods[i].interface; + const char *name = bluez_auth_agent_object_methods[i].method_name; + + if (p_dbus_message_is_method_call( message, iface, name )) + { + DBusPreallocatedSend *prealloc = p_dbus_connection_preallocate_send( connection ); + if (!prealloc) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + bluez_auth_agent_object_methods[i].method_callback( connection, message, prealloc, + ctx ); + return DBUS_HANDLER_RESULT_HANDLED; + } + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +const static struct DBusObjectPathVTable bluez_auth_agent_object_vtable = { + .unregister_function = NULL, .message_function = bluez_auth_agent_vtable_message_handler }; + +#define WINE_BLUEZ_AUTH_AGENT_PATH "/org/winehq/wine/winebth/AuthAgent" + +NTSTATUS bluez_auth_agent_register( void *connection, void *agent_ctx, + BLUETOOTH_IO_CAPABILITY capabilities ) +{ + DBusMessage *request, *reply; + DBusError error; + dbus_bool_t success; + NTSTATUS result; + const char *capability; + struct bluez_auth_agent_ctx *ctx = agent_ctx; + static const char *wine_bluez_auth_agent_path = WINE_BLUEZ_AUTH_AGENT_PATH; + + TRACE( "(%p, %p, %d)\n", connection, ctx, capabilities ); + + switch (capabilities) + { + case BLUETOOTH_IO_CAPABILITY_KEYBOARDONLY: + capability = "KeyboardOnly"; + break; + case BLUETOOTH_IO_CAPABILITY_DISPLAYONLY: + capability = "DisplayOnly"; + break; + case BLUETOOTH_IO_CAPABILITY_DISPLAYYESNO: + capability = "DisplayYesNo"; + case BLUETOOTH_IO_CAPABILITY_NOINPUTNOOUTPUT: + capability = "NoInputNoOutput"; + break; + case BLUETOOTH_IO_CAPABILITY_UNDEFINED: + capability = ""; + break; + default: + return STATUS_INVALID_PARAMETER; + } + p_dbus_error_init( &error ); + + TRACE_(dbus)( "Registering an org.bluez.Agent1 object at %s\n", WINE_BLUEZ_AUTH_AGENT_PATH ); + success = p_dbus_connection_try_register_object_path( + connection, WINE_BLUEZ_AUTH_AGENT_PATH, &bluez_auth_agent_object_vtable, ctx, + &error ); + if (!success) + { + ERR_(dbus)( "Could not register BlueZ agent: %s: %s\n", + debugstr_a( error.name ), debugstr_a( error.message ) ); + result = bluez_dbus_error_to_ntstatus( &error ); + goto done; + } + + request = p_dbus_message_new_method_call( BLUEZ_DEST, "/org/bluez", + BLUEZ_INTERFACE_AGENT_MANAGER, "RegisterAgent" ); + if (!request) + { + result = STATUS_NO_MEMORY; + goto failure; + } + + p_dbus_message_append_args( request, DBUS_TYPE_OBJECT_PATH, &wine_bluez_auth_agent_path, + DBUS_TYPE_STRING, &capability, DBUS_TYPE_INVALID ); + reply = + p_dbus_connection_send_with_reply_and_block( connection, request, bluez_timeout, &error ); + p_dbus_message_unref( request ); + if (!reply) + { + ERR_(dbus)( "failed to register agent: '%s': %s\n", error.name, error.message ); + result = bluez_dbus_error_to_ntstatus( &error ); + goto failure; + } + p_dbus_message_unref( reply ); + result = STATUS_SUCCESS; + goto done; + +failure: + p_dbus_connection_unregister_object_path( connection, WINE_BLUEZ_AUTH_AGENT_PATH ); + free( ctx ); +done: + p_dbus_error_free( &error ); + return result; +} + +BOOL bluez_auth_agent_unregister( void *connection, void *agent_ctx ) +{ + dbus_bool_t success; + success = p_dbus_connection_unregister_object_path( connection, WINE_BLUEZ_AUTH_AGENT_PATH ); + return success != 0; +} + + +static BOOL bluez_auth_agent_have_auth_event( struct bluez_auth_agent_ctx *ctx, + struct winebluetooth_auth_event *auth_event ) +{ + struct bluez_auth_agent_request *req = &ctx->cur_request; + BOOL have_event; + + pthread_mutex_lock( &ctx->lock ); + switch (ctx->cur_request.status) + { + case BTH_AUTH_REQ_STATUS_WAITING_FOR_RESPONSE: + { + struct bluez_auth_agent_request *req = &ctx->cur_request; + + auth_event->auth_data = req->auth_data; + auth_event->event_type = req->auth_service + ? BLUETOOTH_AUTH_EVENT_DEVICE_SERVICE_AUTH_REQUEST + : BLUETOOTH_AUTH_EVENT_DEVICE_AUTH_REQUEST; + auth_event->auth_method = req->auth_method; + auth_event->device.handle = req->device_path; + have_event = TRUE; + } + case BTH_AUTH_REQ_STATUS_KEYPRESS_EVENT: + { + auth_event->event_type = BLUETOOTH_AUTH_EVENT_DEVICE_KEYPRESS; + auth_event->device.handle = req->device_path; + auth_event->auth_data = req->auth_data; + have_event = TRUE; + } + default: + have_event = FALSE; + } + pthread_mutex_unlock( &ctx->lock ); + + return have_event; +} + +NTSTATUS bluez_dbus_loop( void *c, void *watcher, void *auth_agent_ctx, struct winebluetooth_event_loop_result *result ) { DBusConnection *connection; @@ -1757,6 +2589,7 @@ NTSTATUS bluez_dbus_loop( void *c, void *watcher, while(TRUE) { struct bluez_watcher_event *bluez_event; + struct winebluetooth_auth_event auth_event = {0};
/* The first two conditionals can probably be nested within a single boolean like * 'init_objects_enumerated', but I have decided to not do that for now to keep this state @@ -1799,6 +2632,13 @@ NTSTATUS bluez_dbus_loop( void *c, void *watcher, p_dbus_connection_unref( connection ); return STATUS_PENDING; } + else if (bluez_auth_agent_have_auth_event( auth_agent_ctx, &auth_event )) + { + result->status = WINEBLUETOOTH_EVENT_LOOP_STATUS_AUTH_EVENT; + result->data.auth_event = auth_event; + p_dbus_connection_unref( connection ); + return STATUS_PENDING; + } else if (!p_dbus_connection_read_write_dispatch( connection, -1 )) { p_dbus_connection_unref( connection ); diff --git a/dlls/winebth.sys/unixlib.c b/dlls/winebth.sys/unixlib.c index 017e073207e..70162631747 100644 --- a/dlls/winebth.sys/unixlib.c +++ b/dlls/winebth.sys/unixlib.c @@ -201,7 +201,8 @@ static NTSTATUS bluetooth_event_loop_once( void *args )
if (!dbus_connection) return STATUS_NOT_SUPPORTED; memset( ¶ms->result, 0, sizeof( params->result ) ); - return bluez_dbus_loop( dbus_connection, params->unix_watcher_ctx, ¶ms->result ); + return bluez_dbus_loop( dbus_connection, params->unix_watcher_ctx, (void *)params->auth_agent, + ¶ms->result ); }
static NTSTATUS bluetooth_guid_array_copy( void *args ) @@ -219,6 +220,35 @@ static NTSTATUS bluetooth_guid_array_free( void *args ) return STATUS_SUCCESS; }
+static NTSTATUS bluetooth_auth_agent_init( void *args ) +{ + struct bluetooth_auth_agent_init_params *params = args; + + if (!dbus_connection) return STATUS_NOT_SUPPORTED; + + params->auth_agent = bluez_auth_agent_ctx_init(); + return !params->auth_agent ? STATUS_NO_MEMORY : STATUS_SUCCESS; +} + +static NTSTATUS bluetooth_auth_agent_start( void *args ) +{ + struct bluetooth_auth_agent_start_params *params = args; + + if (!dbus_connection) return STATUS_NOT_SUPPORTED; + return bluez_auth_agent_register( dbus_connection, (void *)params->auth_agent, + params->capability ); +} + +static NTSTATUS bluetooth_auth_agent_stop( void *args ) +{ + struct bluetooth_auth_agent_stop_params *params = args; + + if (!dbus_connection) return STATUS_NOT_SUPPORTED; + return bluez_auth_agent_unregister( dbus_connection, (void *)params->auth_agent ) + ? STATUS_SUCCESS + : STATUS_INTERNAL_ERROR; +} + const unixlib_entry_t __wine_unix_call_funcs[] = { bluetooth_init, bluetooth_shutdown, @@ -239,6 +269,10 @@ const unixlib_entry_t __wine_unix_call_funcs[] = {
bluetooth_guid_array_copy, bluetooth_guid_array_free, + + bluetooth_auth_agent_init, + bluetooth_auth_agent_start, + bluetooth_auth_agent_stop, };
C_ASSERT( ARRAYSIZE( __wine_unix_call_funcs ) == unix_funcs_count ); diff --git a/dlls/winebth.sys/unixlib.h b/dlls/winebth.sys/unixlib.h index ba468e54b19..fa5b8e70066 100644 --- a/dlls/winebth.sys/unixlib.h +++ b/dlls/winebth.sys/unixlib.h @@ -118,6 +118,7 @@ struct bluetooth_watcher_close_params struct bluetooth_event_loop_once_params { unix_handle_t unix_watcher_ctx; + unix_handle_t auth_agent;
struct winebluetooth_event_loop_result result; }; @@ -135,6 +136,22 @@ struct bluetooth_guid_array_free_params UINT_PTR arr; };
+struct bluetooth_auth_agent_init_params +{ + UINT_PTR auth_agent; +}; + +struct bluetooth_auth_agent_start_params +{ + UINT_PTR auth_agent; + BLUETOOTH_IO_CAPABILITY capability; +}; + +struct bluetooth_auth_agent_stop_params +{ + UINT_PTR auth_agent; +}; + enum bluetoothapis_funcs { unix_bluetooth_init, @@ -157,6 +174,10 @@ enum bluetoothapis_funcs unix_bluetooth_guid_array_copy, unix_bluetooth_guid_array_free,
+ unix_bluetooth_auth_agent_init, + unix_bluetooth_auth_agent_start, + unix_bluetooth_auth_agent_stop, + unix_funcs_count };
diff --git a/dlls/winebth.sys/unixlib_priv.h b/dlls/winebth.sys/unixlib_priv.h index 2375aee531f..56042a959a9 100644 --- a/dlls/winebth.sys/unixlib_priv.h +++ b/dlls/winebth.sys/unixlib_priv.h @@ -48,7 +48,7 @@ extern void unix_name_free( struct unix_name *name ); extern void *bluez_dbus_init( void ); extern void bluez_dbus_close( void *connection ); extern void bluez_dbus_free( void *connection ); -extern NTSTATUS bluez_dbus_loop( void *connection, void *watcher_ctx, struct winebluetooth_event_loop_result *result ); +extern NTSTATUS bluez_dbus_loop( void *connection, void *watcher_ctx, void *auth_agent_ctx, struct winebluetooth_event_loop_result *result ); extern NTSTATUS bluez_adapter_set_prop( void *connection, struct bluetooth_adapter_set_prop_params *params ); extern NTSTATUS bluez_adapter_start_discovery( void *connection, const char *adapter_path, @@ -56,5 +56,9 @@ extern NTSTATUS bluez_adapter_start_discovery( void *connection, const char *ada extern NTSTATUS bluez_adapter_stop_discovery( void *connection, const char *adapter_path ); extern NTSTATUS bluez_watcher_init( void *connection, void **ctx ); extern void bluez_watcher_close( void *connection, void *ctx ); +extern NTSTATUS bluez_auth_agent_register( void *connection, void *ctx, + BLUETOOTH_IO_CAPABILITY capabilities ); +extern BOOL bluez_auth_agent_unregister( void *connection, void *agent_ctx ); +extern UINT_PTR bluez_auth_agent_ctx_init( void ); #endif /* SONAME_LIBDBUS_1 */ #endif /* __WINE_BLUETOOTHAPIS_UNIXLIB_PRIV_H */ diff --git a/dlls/winebth.sys/winebluetooth.c b/dlls/winebth.sys/winebluetooth.c index 525db1fa2e1..98f0afb9269 100644 --- a/dlls/winebth.sys/winebluetooth.c +++ b/dlls/winebth.sys/winebluetooth.c @@ -162,7 +162,8 @@ NTSTATUS winebluetooth_watcher_close( winebluetooth_watcher_t watcher ) }
NTSTATUS winebluetooth_event_loop_run( winebluetooth_watcher_t watcher, - struct winebluetooth_event_loop_result *result ) + winebluetooth_auth_agent_t agent, + struct winebluetooth_event_loop_result *result ) { struct bluetooth_event_loop_once_params params = {0}; NTSTATUS status; @@ -170,11 +171,52 @@ NTSTATUS winebluetooth_event_loop_run( winebluetooth_watcher_t watcher, TRACE( "(%p, %p, %p)\n", watcher, result );
params.unix_watcher_ctx = watcher.handle; + params.auth_agent = agent.handle; status = UNIX_BLUETOOTH_CALL( bluetooth_event_loop_once, ¶ms ); *result = params.result; return status; }
+NTSTATUS winebluetooth_auth_agent_init( winebluetooth_auth_agent_t *agent ) +{ + NTSTATUS status; + struct bluetooth_auth_agent_init_params params = {0}; + + TRACE( "(%p)\n", agent ); + + status = UNIX_BLUETOOTH_CALL( bluetooth_auth_agent_init, ¶ms ); + if (status) + return status; + + agent->handle = params.auth_agent; + return STATUS_SUCCESS; +} + +NTSTATUS winebluetooth_auth_agent_start( winebluetooth_auth_agent_t agent, BLUETOOTH_IO_CAPABILITY capability ) +{ + struct bluetooth_auth_agent_start_params params = {0}; + NTSTATUS status; + + TRACE( "(%p, %d)\n", agent.handle, capability ); + + params.auth_agent = agent.handle; + params.capability = capability; + status = UNIX_BLUETOOTH_CALL( bluetooth_auth_agent_start, ¶ms ); + return status; +} + +NTSTATUS winebluetooth_auth_agent_stop( winebluetooth_auth_agent_t agent ) +{ + struct bluetooth_auth_agent_stop_params params = {0}; + NTSTATUS status; + + TRACE( "(%p)\n", agent.handle ); + + params.auth_agent = agent.handle; + status = UNIX_BLUETOOTH_CALL( bluetooth_auth_agent_stop, ¶ms ); + return status; +} + NTSTATUS winebluetooth_init( void ) { NTSTATUS status; diff --git a/dlls/winebth.sys/winebluetooth.h b/dlls/winebth.sys/winebluetooth.h index f3cf220ae31..5efb3527ad0 100644 --- a/dlls/winebth.sys/winebluetooth.h +++ b/dlls/winebth.sys/winebluetooth.h @@ -270,9 +270,47 @@ struct winebluetooth_watcher_event NTSTATUS winebluetooth_watcher_init( winebluetooth_watcher_t *watcher ); NTSTATUS winebluetooth_watcher_close( winebluetooth_watcher_t watcher );
+typedef struct +{ + UINT_PTR handle; +} winebluetooth_auth_agent_t; + +NTSTATUS winebluetooth_auth_agent_init( winebluetooth_auth_agent_t *agent ); +NTSTATUS winebluetooth_auth_agent_start( winebluetooth_auth_agent_t agent, + BLUETOOTH_IO_CAPABILITY capability ); +NTSTATUS winebluetooth_auth_agent_stop( winebluetooth_auth_agent_t agent ); + +union winebluetooth_auth_event_data +{ + UCHAR pincode[BLUETOOTH_MAX_PASSKEY_BUFFER_SIZE]; + ULONG passkey; + GUID service_uuid; + struct { + ULONG passkey; + UINT16 entered; + } keypress; +}; + +enum winebluetooth_auth_event_type +{ + BLUETOOTH_AUTH_EVENT_DEVICE_AUTH_REQUEST, + BLUETOOTH_AUTH_EVENT_DEVICE_SERVICE_AUTH_REQUEST, + BLUETOOTH_AUTH_EVENT_DEVICE_KEYPRESS, +}; + +struct winebluetooth_auth_event +{ + enum winebluetooth_auth_event_type event_type; + winebluetooth_device_t device; + winebluetooth_radio_t radio; + BLUETOOTH_AUTHENTICATION_METHOD auth_method; + union winebluetooth_auth_event_data auth_data; +}; + enum winebluetooth_event_loop_status { WINEBLUETOOTH_EVENT_LOOP_STATUS_WATCHER_EVENT, + WINEBLUETOOTH_EVENT_LOOP_STATUS_AUTH_EVENT, };
struct winebluetooth_event_loop_result @@ -280,10 +318,12 @@ struct winebluetooth_event_loop_result enum winebluetooth_event_loop_status status; union { struct winebluetooth_watcher_event watcher_event; + struct winebluetooth_auth_event auth_event; } data; };
NTSTATUS winebluetooth_event_loop_run( winebluetooth_watcher_t watcher, + winebluetooth_auth_agent_t auth_agent, struct winebluetooth_event_loop_result *result ); NTSTATUS winebluetooth_init( void ); NTSTATUS winebluetooth_shutdown( void ); diff --git a/dlls/winebth.sys/winebth.c b/dlls/winebth.sys/winebth.c index 1f7adfb4eb0..59256369870 100644 --- a/dlls/winebth.sys/winebth.c +++ b/dlls/winebth.sys/winebth.c @@ -67,6 +67,7 @@ DECLARE_CRITICAL_SECTION( device_list_cs ); static struct list device_list = LIST_INIT( device_list );
static winebluetooth_watcher_t watcher; +static winebluetooth_auth_agent_t auth_agent; struct bluetooth_radio { struct list entry; @@ -81,6 +82,7 @@ struct bluetooth_radio UNICODE_STRING bthport_symlink_name; UNICODE_STRING bthradio_symlink_name; LIST_ENTRY irp_list; + BOOL auth_agent_registered; };
static NTSTATUS WINAPI dispatch_bluetooth(DEVICE_OBJECT *device, IRP *irp ) @@ -213,10 +215,37 @@ static NTSTATUS WINAPI dispatch_bluetooth(DEVICE_OBJECT *device, IRP *irp ) } case IOCTL_WINEBTH_RADIO_STOP_DISCOVERY: status = winebluetooth_radio_stop_discovery( ext->radio ); - default: break; - } + case IOCTL_WINEBTH_START_AUTH_AGENT: + { + BLUETOOTH_IO_CAPABILITY *capability = + (BLUETOOTH_IO_CAPABILITY *)irp->AssociatedIrp.SystemBuffer; + if (!capability) + { + status = STATUS_INVALID_PARAMETER; + break; + } + if (insize < sizeof(*capability)) + { + status = STATUS_BUFFER_TOO_SMALL; + break; + }
+ status = ext->auth_agent_registered + ? STATUS_SUCCESS + : winebluetooth_auth_agent_start( auth_agent, *capability ); + ext->auth_agent_registered = TRUE; + break; + } + case IOCTL_WINEBTH_STOP_AUTH_AGENT: + { + status = winebluetooth_auth_agent_stop( auth_agent ); + ext->auth_agent_registered = !status ? FALSE : TRUE; + break; + } + default: + break; + }
irp->IoStatus.Status = status; IoCompleteRequest( irp, IO_NO_INCREMENT ); @@ -460,7 +489,7 @@ static DWORD CALLBACK bluetooth_event_loop_thread_proc( void *arg ) { struct winebluetooth_event_loop_result result = {0};
- status = winebluetooth_event_loop_run( watcher, &result ); + status = winebluetooth_event_loop_run( watcher, auth_agent, &result ); if (status != STATUS_PENDING) break;
switch (result.status) @@ -494,6 +523,21 @@ static DWORD CALLBACK bluetooth_event_loop_thread_proc( void *arg ) } break; } + case WINEBLUETOOTH_EVENT_LOOP_STATUS_AUTH_EVENT: + { + struct bluetooth_radio *device; + EnterCriticalSection( &device_list_cs ); + LIST_FOR_EACH_ENTRY( device, &device_list, struct bluetooth_radio, entry ) + { + if (winebluetooth_radio_equal( device->radio, result.data.auth_event.radio )) + { + bthenum_device_auth_event( device->device_fdo_bus, result.data.auth_event ); + winebluetooth_radio_free( result.data.auth_event.radio ); + break; + } + } + LeaveCriticalSection( &device_list_cs ); + } default: FIXME( "Unknown bluetooth event loop status code: %#x\n", result.status ); } @@ -840,6 +884,12 @@ NTSTATUS WINAPI DriverEntry( DRIVER_OBJECT *driver, UNICODE_STRING *path ) ERR( "Failed to initialize bluetooth watcher: %#lx\n", status ); return status; } + status = winebluetooth_auth_agent_init( &auth_agent ); + if (status) + { + ERR( "Failed to initialize bluetooth authentication agent: %#lx\n", status ); + return status; + }
driver_obj = driver;
diff --git a/dlls/winebth.sys/winebth_priv.h b/dlls/winebth.sys/winebth_priv.h index 32d0d2ad95b..9fe425846e6 100644 --- a/dlls/winebth.sys/winebth_priv.h +++ b/dlls/winebth.sys/winebth_priv.h @@ -40,6 +40,8 @@ extern NTSTATUS WINAPI bthenum_device_removed( DEVICE_OBJECT *fdo_bus, const winebluetooth_device_t device_handle ); extern NTSTATUS WINAPI bthenum_device_props_changed( DEVICE_OBJECT *fdo_bus, struct winebluetooth_watcher_event_device_props_changed event ); +extern NTSTATUS WINAPI bthenum_device_auth_event( DEVICE_OBJECT *fdo_bus, + struct winebluetooth_auth_event event ); extern NTSTATUS WINAPI bthenum_create_fdo_bus( DEVICE_OBJECT *pdo, DEVICE_OBJECT **fdo_bus, UINT16 localmfg ); extern NTSTATUS WINAPI bthenum_entry_point( DRIVER_OBJECT *driver, UNICODE_STRING *path ); diff --git a/include/bthdef.h b/include/bthdef.h index 6c200073995..5b89834a711 100644 --- a/include/bthdef.h +++ b/include/bthdef.h @@ -51,6 +51,12 @@ DEFINE_GUID( GUID_BTHPORT_DEVICE_INTERFACE, 0x850302a, 0xb344, 0x4fda, 0x9b, 0xe
DEFINE_GUID( GUID_BLUETOOTH_RADIO_IN_RANGE, 0xea3b5b82, 0x26ee, 0x450e, 0xb0, 0xd8, 0xd2, 0x6f, 0xe3, 0x0a, 0x38, 0x69 ); + +DEFINE_GUID( GUID_BLUETOOTH_AUTHENTICATION_REQUEST, 0x5dc9136d, 0x996c, 0x46db, 0x84, 0xf5, 0x32, + 0xc0, 0xa3, 0xf4, 0x73, 0x52 ); + +DEFINE_GUID( GUID_BLUETOOTH_KEYPRESS_EVENT, 0xd668dfcd, 0x0f4e, 0x4efc, 0xbf, 0xe0, 0x39, 0x2e, + 0xee, 0xc5, 0x10, 0x9c ); typedef struct _BTH_RADIO_IN_RANGE { BTH_DEVICE_INFO deviceInfo; @@ -58,6 +64,41 @@ typedef struct _BTH_RADIO_IN_RANGE ULONG previousDeviceFlags; } BTH_RADIO_IN_RANGE, *PBTH_RADIO_IN_RANGE;
+/* Associated data for GUID_BLUETOOTH_AUTHENTICATION_REQUEST events. */ +typedef struct _BTH_AUTHENTICATION_REQUEST +{ + BTH_DEVICE_INFO deviceInfo; + BLUETOOTH_AUTHENTICATION_METHOD authenticationMethod; + BLUETOOTH_IO_CAPABILITY IoCapability; + BLUETOOTH_AUTHENTICATION_REQUIREMENTS AuthenticationRequirements; + + /* Not sure what this field does. */ + ULONG flag; + + union + { + ULONG Numeric_Value; + ULONG Passkey; + }; +} BTH_AUTHENTICATION_REQUEST, *PBTH_AUTHENTICATION_REQUEST; + +typedef enum _BTH_KEYPRESS_NOTIFICATION_TYPE +{ + BTH_KEYPRESS_NOTIFICATION_PASSKEY_STARTED, + BTH_KEYPRESS_NOTIFICATION_PASSKEY_DIGIT_ENTERED, + BTH_KEYPRESS_NOTIFICATION_PASSKEY_DIGIT_ERASED, + BTH_KEYPRESS_NOTIFICATION_PASSKEY_CLEARED, + BTH_KEYPRESS_NOTIFICATION_PASSKEY_ENTRY_COMPLETED, +} BTH_KEYPRESS_NOTIFICATION_TYPE, *PBTH_KEYPRESS_NOTIFICATION_TYPE; + +/* Associated data for GUID_BLUETOOTH_KEYPRESS_EVENT events. */ + +typedef struct _BTH_HCI_KEYPRESS_INFO +{ + BTH_ADDR BTH_ADDR; + BTH_KEYPRESS_NOTIFICATION_TYPE NotificationType; +} BTH_HCI_KEYPRESS_INFO, *PBTH_HCI_KEYPRESS_INFO; + #ifdef __cplusplus } #endif diff --git a/include/bthioctl.h b/include/bthioctl.h index 0b9cc6164a4..f858f30c93e 100644 --- a/include/bthioctl.h +++ b/include/bthioctl.h @@ -46,6 +46,8 @@ #define IOCTL_WINEBTH_RADIO_UNSET_FLAG CTL_CODE(FILE_DEVICE_BLUETOOTH, 0xa4, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_WINEBTH_RADIO_START_DISCOVERY CTL_CODE(FILE_DEVICE_BLUETOOTH, 0xa6, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_WINEBTH_RADIO_STOP_DISCOVERY CTL_CODE(FILE_DEVICE_BLUETOOTH, 0xa7, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_WINEBTH_START_AUTH_AGENT CTL_CODE(FILE_DEVICE_BLUETOOTH, 0xa8, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_WINEBTH_STOP_AUTH_AGENT CTL_CODE(FILE_DEVICE_BLUETOOTH, 0xa9, METHOD_BUFFERED, FILE_ANY_ACCESS) #endif
#define LOCAL_RADIO_DISCOVERABLE (0x0001)