From: Vibhav Pant vibhavp@gmail.com
When the registered pairing agent receives a "RequestConfirmation" method request from BlueZ, relay it to the PE driver as WINEBLUETOOTH_EVENT_AUTH_EVENT, alongside the remote device the request was for. The PE driver then broadcasts a GUID_WINEBTH_AUTHENTICATION_REQUEST PnP event on the newly added WINEBTHAUTH device, alongside a BTH_AUTHENTICATION_REQUEST buffer containing details for the pairing request.
The auxiliary device \Device\WINEBTHAUTH handles authentication-related functionality for the entire system, and also gets used as the device for which WM_DEVICECHANGE auth events are broadcasted. This way, the user-mode Bluetooth APIs don't need to handle individual radio devices. --- dlls/bluetoothapis/main.c | 2 +- dlls/winebth.sys/dbus.c | 180 ++++++++++++++++++++++++++++--- dlls/winebth.sys/unixlib.c | 20 +++- dlls/winebth.sys/unixlib_priv.h | 8 +- dlls/winebth.sys/winebluetooth.c | 33 ++++++ dlls/winebth.sys/winebth.c | 151 ++++++++++++++++++++------ dlls/winebth.sys/winebth_priv.h | 14 +++ include/wine/winebth.h | 13 +++ 8 files changed, 365 insertions(+), 56 deletions(-)
diff --git a/dlls/bluetoothapis/main.c b/dlls/bluetoothapis/main.c index 7cf89b23fc9..df85e44913a 100644 --- a/dlls/bluetoothapis/main.c +++ b/dlls/bluetoothapis/main.c @@ -32,11 +32,11 @@ #include "bluetoothapis.h" #include "setupapi.h" #include "winioctl.h" -#include "wine/winebth.h"
#include "initguid.h" #include "bthdef.h" #include "bthioctl.h" +#include "wine/winebth.h"
#include "wine/debug.h"
diff --git a/dlls/winebth.sys/dbus.c b/dlls/winebth.sys/dbus.c index 39e5a1e4a89..7ea264e8a0a 100644 --- a/dlls/winebth.sys/dbus.c +++ b/dlls/winebth.sys/dbus.c @@ -45,6 +45,7 @@ #include <winbase.h> #include <bthsdpdef.h> #include <bluetoothapis.h> +#include <bthdef.h> #include <wine/winebth.h>
#include <wine/debug.h> @@ -814,6 +815,34 @@ struct bluez_init_entry struct list entry; };
+/* The status of a pairing session initiated by BlueZ. */ +enum bluez_pairing_session_status +{ + /* No pairing session is active. */ + BLUEZ_PAIRING_SESSION_NONE, + /* We have received an authentication request from BlueZ, which should be queued to the PE driver. */ + BLUEZ_PAIRING_SESSION_INCOMING, + /* The authentication request has been relayed to the driver, and we're waiting for a response from userspace. */ + BLUEZ_PAIRING_SESSION_PENDING_REPLY, + /* The authentication request has been cancelled by BlueZ. */ + BLUEZ_PAIRING_SESSION_CANCELLED, +}; + +struct bluez_auth_agent_ctx +{ + LONG refcnt; + + pthread_mutex_t lock; /* Guards all fields below. */ + enum bluez_pairing_session_status status; + struct unix_name *device; + BLUETOOTH_AUTHENTICATION_METHOD method; + UINT32 passkey; + /* The auth request we received from BlueZ. */ + DBusMessage *auth_request; + DBusPreallocatedSend *preallocate_send; + DBusConnection *connection; +}; + void *bluez_dbus_init( void ) { DBusError error; @@ -850,22 +879,97 @@ void bluez_dbus_free( void *connection ) p_dbus_connection_unref( connection ); }
+static struct bluez_auth_agent_ctx *bluez_auth_agent_ctx_incref( struct bluez_auth_agent_ctx *ctx ) +{ + InterlockedIncrement( &ctx->refcnt ); + return ctx; +} + +static void bluez_auth_agent_ctx_decref( struct bluez_auth_agent_ctx *ctx ) +{ + if (InterlockedDecrement( &ctx->refcnt )) + return; + + if (ctx->status != BLUEZ_PAIRING_SESSION_NONE) + { + unix_name_free( ctx->device ); + if (ctx->status != BLUEZ_PAIRING_SESSION_CANCELLED) + { + p_dbus_connection_free_preallocated_send( ctx->connection, ctx->preallocate_send ); + p_dbus_message_unref( ctx->auth_request ); + p_dbus_connection_unref( ctx->connection ); + } + } + pthread_mutex_destroy( &ctx->lock ); + free( ctx ); +} + static DBusHandlerResult bluez_auth_agent_vtable_message_handler( DBusConnection *connection, DBusMessage *message, void *data ) { + struct bluez_auth_agent_ctx *ctx = data; + DBusPreallocatedSend *prealloc_send; DBusMessage *reply; - dbus_bool_t success;
- FIXME_(dbus)( "(%s, %s, %p): stub!\n", dbgstr_dbus_connection( connection ), dbgstr_dbus_message( message ), + TRACE_(dbus)( "(%s, %s, %p)\n", dbgstr_dbus_connection( connection ), dbgstr_dbus_message( message ), data );
- reply = p_dbus_message_new_error( message, "org.bluez.Error.Rejected", "" ); - if (!reply) + prealloc_send = p_dbus_connection_preallocate_send( connection ); + if (!prealloc_send) return DBUS_HANDLER_RESULT_NEED_MEMORY;
- success = p_dbus_connection_send( connection, reply, NULL ); + if (p_dbus_message_is_method_call( message, BLUEZ_INTERFACE_AGENT, "RequestConfirmation" )) + { + struct unix_name *device; + const char *device_path; + dbus_uint32_t passkey; + DBusError 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_INVALID )) + { + ERR( "Failed to get message args: %s: %s\n", debugstr_a( error.name ), debugstr_a( error.message ) ); + p_dbus_error_free( &error ); + p_dbus_connection_free_preallocated_send( connection, prealloc_send ); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + p_dbus_error_free( &error ); + device = unix_name_get_or_create( device_path ); + if (!device) + { + ERR( "Failed to allocate memory for device path %s\n", device_path ); + p_dbus_connection_free_preallocated_send( connection, prealloc_send ); + return DBUS_HANDLER_RESULT_NEED_MEMORY; + } + + TRACE( "Received a numeric confirmation request for device %s\n", debugstr_a( device_path ) ); + pthread_mutex_lock( &ctx->lock ); + ctx->status = BLUEZ_PAIRING_SESSION_INCOMING; + ctx->device = device; + ctx->method = BLUETOOTH_AUTHENTICATION_METHOD_NUMERIC_COMPARISON; + ctx->passkey = passkey; + ctx->auth_request = p_dbus_message_ref( message ); + ctx->preallocate_send = prealloc_send; + ctx->connection = p_dbus_connection_ref( connection ); + pthread_mutex_unlock( &ctx->lock ); + + return DBUS_HANDLER_RESULT_HANDLED; + } + else + { + FIXME( "Unsupported method call: %s\n", debugstr_a( p_dbus_message_get_member( message ) ) ); + reply = p_dbus_message_new_error( message, "org.bluez.Error.Rejected", "" ); + } + + if (!reply) + { + p_dbus_connection_free_preallocated_send( connection, prealloc_send ); + return DBUS_HANDLER_RESULT_NEED_MEMORY; + } + p_dbus_connection_send_preallocated( connection, prealloc_send, reply, NULL ); p_dbus_message_unref( reply ); - return success ? DBUS_HANDLER_RESULT_HANDLED : DBUS_HANDLER_RESULT_NEED_MEMORY; + return DBUS_HANDLER_RESULT_HANDLED; }
const static struct DBusObjectPathVTable bluez_auth_agent_object_vtable = { @@ -873,26 +977,38 @@ const static struct DBusObjectPathVTable bluez_auth_agent_object_vtable = {
#define WINE_BLUEZ_AUTH_AGENT_PATH "/org/winehq/wine/winebth/AuthAgent"
-NTSTATUS bluez_auth_agent_start( void *connection ) +NTSTATUS bluez_auth_agent_start( void *connection, void **auth_agent_ctx ) { static const char *wine_bluez_auth_agent_path = WINE_BLUEZ_AUTH_AGENT_PATH; static const char *capability = "KeyboardDisplay"; + struct bluez_auth_agent_ctx *ctx; DBusMessage *request, *reply; dbus_bool_t success; DBusError error; NTSTATUS status;
- TRACE( "(%s)\n", dbgstr_dbus_connection( connection ) ); + TRACE( "(%s, %p)\n", dbgstr_dbus_connection( connection ), auth_agent_ctx ); + + ctx = malloc( sizeof( *ctx ) ); + if (!ctx) + return STATUS_NO_MEMORY; + + pthread_mutex_init( &ctx->lock, NULL ); + ctx->status = BLUEZ_PAIRING_SESSION_NONE; + ctx->refcnt = 1;
p_dbus_error_init( &error ); TRACE_(dbus)( "Registering an org.bluez.Agent1 object at %s\n", WINE_BLUEZ_AUTH_AGENT_PATH ); + /* No need to increase the reference count on the auth agent context here, as the vtable handlers only get called + * inside bluez_dbus_loop, where the reference count gets incremented at start. */ success = p_dbus_connection_try_register_object_path( connection, WINE_BLUEZ_AUTH_AGENT_PATH, - &bluez_auth_agent_object_vtable, NULL, &error ); + &bluez_auth_agent_object_vtable, ctx, &error ); if (!success) { ERR_(dbus)( "Failed to register object: %s: %s\n", debugstr_a( error.name ), debugstr_a( error.message ) ); status = bluez_dbus_error_to_ntstatus( &error ); + bluez_auth_agent_ctx_decref( ctx ); goto done; }
@@ -924,25 +1040,29 @@ NTSTATUS bluez_auth_agent_start( void *connection ) } p_dbus_message_unref( reply ); status = STATUS_SUCCESS; + *auth_agent_ctx = ctx; goto done;
failure: p_dbus_connection_unregister_object_path( connection, WINE_BLUEZ_AUTH_AGENT_PATH ); + bluez_auth_agent_ctx_decref( ctx ); done: p_dbus_error_free( &error ); return status; }
-NTSTATUS bluez_auth_agent_stop( void *connection ) +NTSTATUS bluez_auth_agent_stop( void *connection, void *auth_agent_ctx ) { static const char *wine_bluez_auth_agent_path = WINE_BLUEZ_AUTH_AGENT_PATH; + struct bluez_auth_agent_ctx *ctx = auth_agent_ctx; DBusMessage *request, *reply; dbus_bool_t success; NTSTATUS status; DBusError error;
- TRACE( "(%s)\n", dbgstr_dbus_connection( connection ) ); + TRACE( "(%s, %p)\n", dbgstr_dbus_connection( connection ), auth_agent_ctx );
+ bluez_auth_agent_ctx_decref( ctx ); request = p_dbus_message_new_method_call( BLUEZ_DEST, "/org/bluez", BLUEZ_INTERFACE_AGENT_MANAGER, "UnregisterAgent" ); if (!request) @@ -1778,11 +1898,32 @@ static BOOL bluez_watcher_event_queue_ready( struct bluez_watcher_ctx *ctx, stru return FALSE; }
-NTSTATUS bluez_dbus_loop( void *c, void *watcher, +static BOOL bluez_auth_agent_ctx_have_event( struct bluez_auth_agent_ctx *ctx, + struct winebluetooth_auth_event *event ) +{ + BOOL have_event = FALSE; + + pthread_mutex_lock( &ctx->lock ); + if (ctx->status == BLUEZ_PAIRING_SESSION_INCOMING) + { + event->device.handle = (UINT_PTR)unix_name_dup( ctx->device ); + event->method = ctx->method; + event->numeric_value_or_passkey = ctx->passkey; + + ctx->status = BLUEZ_PAIRING_SESSION_PENDING_REPLY; + have_event = TRUE; + } + pthread_mutex_unlock( &ctx->lock ); + + return have_event; +} + +NTSTATUS bluez_dbus_loop( void *c, void *watcher, void *auth_agent, struct winebluetooth_event *result ) { DBusConnection *connection; struct bluez_watcher_ctx *watcher_ctx = watcher; + auth_agent = bluez_auth_agent_ctx_incref( auth_agent );
TRACE( "(%p, %p, %p)\n", c, watcher, result ); connection = p_dbus_connection_ref( c ); @@ -1793,11 +1934,20 @@ NTSTATUS bluez_dbus_loop( void *c, void *watcher, { result->status = WINEBLUETOOTH_EVENT_WATCHER_EVENT; p_dbus_connection_unref( connection ); + bluez_auth_agent_ctx_decref( auth_agent ); + return STATUS_PENDING; + } + else if (bluez_auth_agent_ctx_have_event( auth_agent, &result->data.auth_event )) + { + result->status = WINEBLUETOOTH_EVENT_AUTH_EVENT; + p_dbus_connection_unref( connection ); + bluez_auth_agent_ctx_decref( auth_agent ); return STATUS_PENDING; } else if (!p_dbus_connection_read_write_dispatch( connection, 100 )) { bluez_watcher_free( watcher_ctx ); + bluez_auth_agent_ctx_decref( auth_agent ); p_dbus_connection_unref( connection ); TRACE( "Disconnected from DBus\n" ); return STATUS_SUCCESS; @@ -1821,6 +1971,7 @@ NTSTATUS bluez_dbus_loop( void *c, void *watcher, p_dbus_error_free( &error ); p_dbus_message_unref( reply ); p_dbus_connection_unref( connection ); + bluez_auth_agent_ctx_decref( auth_agent ); return STATUS_NO_MEMORY; } status = bluez_build_initial_device_lists( reply, &watcher_ctx->initial_radio_list, @@ -1830,6 +1981,7 @@ NTSTATUS bluez_dbus_loop( void *c, void *watcher, { WARN( "Error building initial bluetooth devices list: %#x\n", (int)status ); p_dbus_connection_unref( connection ); + bluez_auth_agent_ctx_decref( auth_agent ); return status; } } @@ -1842,7 +1994,7 @@ void bluez_dbus_close( void *connection ) {} void bluez_dbus_free( void *connection ) {} NTSTATUS bluez_watcher_init( void *connection, void **ctx ) { return STATUS_NOT_SUPPORTED; } void bluez_watcher_close( void *connection, void *ctx ) {} -NTSTATUS bluez_dbus_loop( void *c, void *watcher, struct winebluetooth_event *result ) +NTSTATUS bluez_dbus_loop( void *c, void *watcher, void *auth_agent, struct winebluetooth_event *result ) { return STATUS_NOT_SUPPORTED; } @@ -1858,7 +2010,7 @@ NTSTATUS bluez_adapter_stop_discovery( void *connection, const char *adapter_pat { return STATUS_NOT_SUPPORTED; } -NTSTATUS bluez_auth_agent_start( void *connection ) { return STATUS_NOT_SUPPORTED; } +NTSTATUS bluez_auth_agent_start( void *connection, void **ctx ) { return STATUS_NOT_SUPPORTED; } NTSTATUS bluez_auth_agent_stop( void *connection ) { return STATUS_NOT_SUPPORTED; }
#endif /* SONAME_LIBDBUS_1 */ diff --git a/dlls/winebth.sys/unixlib.c b/dlls/winebth.sys/unixlib.c index ebbbe2f8fcb..6afb3bacb00 100644 --- a/dlls/winebth.sys/unixlib.c +++ b/dlls/winebth.sys/unixlib.c @@ -53,6 +53,14 @@ static int compare_string( const void *key, const struct wine_rb_entry *entry ) static struct rb_tree names = { .compare = compare_string }; static pthread_mutex_t names_mutex = PTHREAD_MUTEX_INITIALIZER;
+struct unix_name *unix_name_dup( struct unix_name *name ) +{ + pthread_mutex_lock( &names_mutex ); + name->refcnt++; + pthread_mutex_unlock( &names_mutex ); + return name; +} + struct unix_name *unix_name_get_or_create( const char *str ) { struct rb_entry *entry; @@ -93,6 +101,7 @@ void unix_name_free( struct unix_name *name )
static void *dbus_connection; static void *bluetooth_watcher; +static void *bluetooth_auth_agent;
static NTSTATUS bluetooth_init ( void *params ) { @@ -102,7 +111,7 @@ static NTSTATUS bluetooth_init ( void *params ) if (!dbus_connection) return STATUS_INTERNAL_ERROR;
- status = bluez_auth_agent_start( dbus_connection ); + status = bluez_auth_agent_start( dbus_connection, &bluetooth_auth_agent ); if (status) { bluez_dbus_close( dbus_connection ); @@ -112,11 +121,12 @@ static NTSTATUS bluetooth_init ( void *params ) status = bluez_watcher_init( dbus_connection, &bluetooth_watcher ); if (status) { - bluez_auth_agent_stop( dbus_connection ); + bluez_auth_agent_stop( dbus_connection, bluetooth_auth_agent ); bluez_dbus_close( dbus_connection ); } else - TRACE( "dbus_connection=%p bluetooth_watcher=%p\n", dbus_connection, bluetooth_watcher ); + TRACE( "dbus_connection=%p bluetooth_watcher=%p bluetooth_auth_agent=%p\n", dbus_connection, bluetooth_watcher, + bluetooth_auth_agent ); return status; }
@@ -124,7 +134,7 @@ static NTSTATUS bluetooth_shutdown( void *params ) { if (!dbus_connection) return STATUS_NOT_SUPPORTED;
- bluez_auth_agent_stop( dbus_connection ); + bluez_auth_agent_stop( dbus_connection, bluetooth_auth_agent ); bluez_dbus_close( dbus_connection ); bluez_watcher_close( dbus_connection, bluetooth_watcher ); bluez_dbus_free( dbus_connection ); @@ -204,7 +214,7 @@ static NTSTATUS bluetooth_get_event( void *args )
if (!dbus_connection) return STATUS_NOT_SUPPORTED; memset( ¶ms->result, 0, sizeof( params->result ) ); - return bluez_dbus_loop( dbus_connection, bluetooth_watcher, ¶ms->result ); + return bluez_dbus_loop( dbus_connection, bluetooth_watcher, bluetooth_auth_agent, ¶ms->result ); }
const unixlib_entry_t __wine_unix_call_funcs[] = { diff --git a/dlls/winebth.sys/unixlib_priv.h b/dlls/winebth.sys/unixlib_priv.h index 61656e0cf80..a81162b605e 100644 --- a/dlls/winebth.sys/unixlib_priv.h +++ b/dlls/winebth.sys/unixlib_priv.h @@ -43,17 +43,19 @@ struct unix_name
extern struct unix_name *unix_name_get_or_create( const char *str ); extern void unix_name_free( struct unix_name *name ); +extern struct unix_name *unix_name_dup( 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 *result ); +extern NTSTATUS bluez_dbus_loop( void *connection, void *watcher_ctx, void *auth_agent, + struct winebluetooth_event *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 ); extern NTSTATUS bluez_adapter_stop_discovery( void *connection, const char *adapter_path ); -extern NTSTATUS bluez_auth_agent_start( void *connection ); -extern NTSTATUS bluez_auth_agent_stop( void *connection ); +extern NTSTATUS bluez_auth_agent_start( void *connection, void **ctx ); +extern NTSTATUS bluez_auth_agent_stop( void *connection, void *ctx ); extern NTSTATUS bluez_watcher_init( void *connection, void **ctx ); extern void bluez_watcher_close( void *connection, void *ctx ); #endif /* __WINE_WINEBTH_UNIXLIB_PRIV_H */ diff --git a/dlls/winebth.sys/winebluetooth.c b/dlls/winebth.sys/winebluetooth.c index 370f102d6f3..cb3a27ba25d 100644 --- a/dlls/winebth.sys/winebluetooth.c +++ b/dlls/winebth.sys/winebluetooth.c @@ -105,6 +105,39 @@ void winebluetooth_device_free( winebluetooth_device_t device ) UNIX_BLUETOOTH_CALL( bluetooth_device_free, &args ); }
+void winebluetooth_device_properties_to_info( winebluetooth_device_props_mask_t props_mask, + const struct winebluetooth_device_properties *props, + BTH_DEVICE_INFO *info ) +{ + if (props_mask & WINEBLUETOOTH_DEVICE_PROPERTY_NAME) + { + info->flags |= BDIF_NAME; + memcpy( info->name, props->name, sizeof( info->name ) ); + } + if (props_mask & WINEBLUETOOTH_DEVICE_PROPERTY_ADDRESS) + { + info->flags |= BDIF_ADDRESS; + info->address = RtlUlonglongByteSwap( props->address.ullLong ) >> 16; + } + if (props_mask & WINEBLUETOOTH_DEVICE_PROPERTY_CONNECTED && props->connected) + info->flags |= BDIF_CONNECTED; + if (props_mask & WINEBLUETOOTH_DEVICE_PROPERTY_PAIRED && props->paired) + info->flags |= (BDIF_PAIRED | BDIF_PERSONAL); + if (props_mask & WINEBLUETOOTH_DEVICE_PROPERTY_LEGACY_PAIRING && !props->legacy_pairing) + { + info->flags |= BDIF_SSP_SUPPORTED; + if (props_mask & WINEBLUETOOTH_DEVICE_PROPERTY_PAIRED && props->paired) + info->flags |= BDIF_SSP_PAIRED; + } + if (props_mask & WINEBLUETOOTH_DEVICE_PROPERTY_TRUSTED && props->trusted) + info->flags |= BDIF_PERSONAL; + if (props_mask & WINEBLUETOOTH_DEVICE_PROPERTY_CLASS) + { + info->classOfDevice = props->class; + info->flags |= BDIF_COD; + } +} + NTSTATUS winebluetooth_get_event( struct winebluetooth_event *result ) { struct bluetooth_get_event_params params = {0}; diff --git a/dlls/winebth.sys/winebth.c b/dlls/winebth.sys/winebth.c index 5fe6339d9c5..fd825c1cba3 100644 --- a/dlls/winebth.sys/winebth.c +++ b/dlls/winebth.sys/winebth.c @@ -47,7 +47,7 @@ WINE_DEFAULT_DEBUG_CHANNEL( winebth );
static DRIVER_OBJECT *driver_obj;
-static DEVICE_OBJECT *bus_fdo, *bus_pdo; +static DEVICE_OBJECT *bus_fdo, *bus_pdo, *device_auth;
#define DECLARE_CRITICAL_SECTION( cs ) \ static CRITICAL_SECTION cs; \ @@ -93,6 +93,17 @@ struct bluetooth_remote_device struct winebluetooth_device_properties props; /* Guarded by props_cs */ };
+static NTSTATUS WINAPI dispatch_auth( DEVICE_OBJECT *device, IRP *irp ) +{ + IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation( irp ); + ULONG code = stack->Parameters.DeviceIoControl.IoControlCode; + + FIXME( "device %p irp %p code %#lx: stub!\n", device, irp, code ); + + IoCompleteRequest( irp, IO_NO_INCREMENT ); + return irp->IoStatus.Status; +} + static NTSTATUS WINAPI dispatch_bluetooth( DEVICE_OBJECT *device, IRP *irp ) { struct bluetooth_radio *ext = (struct bluetooth_radio *)device->DeviceExtension; @@ -104,6 +115,9 @@ static NTSTATUS WINAPI dispatch_bluetooth( DEVICE_OBJECT *device, IRP *irp )
TRACE( "device %p irp %p code %#lx\n", device, irp, code );
+ if (device == device_auth) + return dispatch_auth( device, irp ); + switch (code) { case IOCTL_BTH_GET_LOCAL_INFO: @@ -182,38 +196,7 @@ static NTSTATUS WINAPI dispatch_bluetooth( DEVICE_OBJECT *device, IRP *irp ) memset( info, 0, sizeof( *info ) );
EnterCriticalSection( &device->props_cs ); - if (device->props_mask & WINEBLUETOOTH_DEVICE_PROPERTY_NAME) - { - info->flags |= BDIF_NAME; - memcpy( info->name, device->props.name, sizeof( info->name ) ); - } - if (device->props_mask & WINEBLUETOOTH_DEVICE_PROPERTY_ADDRESS) - { - info->flags |= BDIF_ADDRESS; - info->address = RtlUlonglongByteSwap( device->props.address.ullLong ) >> 16; - } - if (device->props_mask & WINEBLUETOOTH_DEVICE_PROPERTY_CONNECTED && - device->props.connected) - info->flags |= BDIF_CONNECTED; - if (device->props_mask & WINEBLUETOOTH_DEVICE_PROPERTY_PAIRED && - device->props.paired) - info->flags |= (BDIF_PAIRED | BDIF_PERSONAL); - if (device->props_mask & WINEBLUETOOTH_DEVICE_PROPERTY_LEGACY_PAIRING && - !device->props.legacy_pairing) - { - info->flags |= BDIF_SSP_SUPPORTED; - if (device->props_mask & WINEBLUETOOTH_DEVICE_PROPERTY_PAIRED && - device->props.paired) - info->flags |= BDIF_SSP_PAIRED; - } - if (device->props_mask & WINEBLUETOOTH_DEVICE_PROPERTY_TRUSTED && - device->props.trusted) - info->flags |= BDIF_PERSONAL; - if (device->props_mask & WINEBLUETOOTH_DEVICE_PROPERTY_CLASS) - { - info->classOfDevice = device->props.class; - info->flags |= BDIF_COD; - } + winebluetooth_device_properties_to_info( device->props_mask, &device->props, info ); LeaveCriticalSection( &device->props_cs );
irp->IoStatus.Information += sizeof( *info ); @@ -559,6 +542,61 @@ done: winebluetooth_device_free( event.device ); }
+static void bluetooth_radio_report_auth_event( struct winebluetooth_auth_event event ) +{ + TARGET_DEVICE_CUSTOM_NOTIFICATION *notification; + struct winebth_authentication_request *request; + struct bluetooth_radio *radio; + SIZE_T notif_size; + + notif_size = offsetof( TARGET_DEVICE_CUSTOM_NOTIFICATION, CustomDataBuffer[sizeof( *request )] ); + notification = ExAllocatePool( PagedPool, notif_size ); + if (!notification) + return; + + notification->Version = 1; + notification->Size = notif_size; + notification->Event = GUID_WINEBTH_AUTHENTICATION_REQUEST; + notification->FileObject = NULL; + notification->NameBufferOffset = -1; + request = (struct winebth_authentication_request *)notification->CustomDataBuffer; + memset( request, 0, sizeof( *request ) ); + request->auth_method = event.method; + request->numeric_value_or_passkey = event.numeric_value_or_passkey; + + EnterCriticalSection( &device_list_cs ); + LIST_FOR_EACH_ENTRY( radio, &device_list, struct bluetooth_radio, entry ) + { + struct bluetooth_remote_device *device; + + EnterCriticalSection( &radio->remote_devices_cs ); + LIST_FOR_EACH_ENTRY( device, &radio->remote_devices, struct bluetooth_remote_device, entry ) + { + if (winebluetooth_device_equal( event.device, device->device )) + { + NTSTATUS ret; + + EnterCriticalSection( &device->props_cs ); + winebluetooth_device_properties_to_info( device->props_mask, &device->props, &request->device_info ); + LeaveCriticalSection( &device->props_cs ); + LeaveCriticalSection( &radio->remote_devices_cs ); + LeaveCriticalSection( &device_list_cs ); + + ret = IoReportTargetDeviceChange( device_auth, notification ); + if (ret) + ERR( "IoReportTargetDeviceChange failed: %#lx\n", ret ); + + ExFreePool( notification ); + return; + } + } + LeaveCriticalSection( &radio->remote_devices_cs ); + } + LeaveCriticalSection( &device_list_cs ); + + ExFreePool( notification ); +} + static DWORD CALLBACK bluetooth_event_loop_thread_proc( void *arg ) { NTSTATUS status; @@ -599,6 +637,10 @@ static DWORD CALLBACK bluetooth_event_loop_thread_proc( void *arg ) } break; } + case WINEBLUETOOTH_EVENT_AUTH_EVENT: + bluetooth_radio_report_auth_event( result.data.auth_event); + winebluetooth_device_free( result.data.auth_event.device ); + break; default: FIXME( "Unknown bluetooth event loop status code: %#x\n", result.status ); } @@ -824,10 +866,40 @@ static NTSTATUS WINAPI pdo_pnp( DEVICE_OBJECT *device_obj, IRP *irp ) return ret; }
+static NTSTATUS auth_pnp( DEVICE_OBJECT *device, IRP *irp ) +{ + IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp); + NTSTATUS ret = irp->IoStatus.Status; + + TRACE( "device_obj %p, irp %p, minor function %s\n", device, irp, debugstr_minor_function_code( stack->MinorFunction ) ); + switch (stack->MinorFunction) + { + case IRP_MN_QUERY_ID: + case IRP_MN_START_DEVICE: + case IRP_MN_SURPRISE_REMOVAL: + ret = STATUS_SUCCESS; + break; + case IRP_MN_REMOVE_DEVICE: + IoDeleteDevice( device ); + ret = STATUS_SUCCESS; + break; + ret = STATUS_SUCCESS; + default: + FIXME( "Unhandled minor function %s.\n", debugstr_minor_function_code( stack->MinorFunction ) ); + break; + } + + irp->IoStatus.Status = ret; + IoCompleteRequest( irp, IO_NO_INCREMENT ); + return ret; +} + static NTSTATUS WINAPI bluetooth_pnp( DEVICE_OBJECT *device, IRP *irp ) { if (device == bus_fdo) return fdo_pnp( device, irp ); + else if (device == device_auth) + return auth_pnp( device, irp ); return pdo_pnp( device, irp ); }
@@ -853,7 +925,10 @@ static void WINAPI driver_unload( DRIVER_OBJECT *driver ) {}
NTSTATUS WINAPI DriverEntry( DRIVER_OBJECT *driver, UNICODE_STRING *path ) { + UNICODE_STRING device_winebth_auth = RTL_CONSTANT_STRING( L"\Device\WINEBTHAUTH" ); + UNICODE_STRING object_winebth_auth = RTL_CONSTANT_STRING( WINEBTH_AUTH_DEVICE_PATH ); NTSTATUS status; + TRACE( "(%p, %s)\n", driver, debugstr_w( path->Buffer ) );
status = winebluetooth_init(); @@ -866,5 +941,15 @@ NTSTATUS WINAPI DriverEntry( DRIVER_OBJECT *driver, UNICODE_STRING *path ) driver->DriverUnload = driver_unload; driver->MajorFunction[IRP_MJ_PNP] = bluetooth_pnp; driver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = dispatch_bluetooth; + + status = IoCreateDevice( driver, 0, &device_winebth_auth, 0, 0, FALSE, &device_auth ); + if (!status) + { + status = IoCreateSymbolicLink( &object_winebth_auth, &device_winebth_auth ); + if (status) + ERR( "IoCreateSymbolicLink failed: %#lx\n", status ); + } + else + ERR( "IoCreateDevice failed: %#lx\n", status ); return STATUS_SUCCESS; } diff --git a/dlls/winebth.sys/winebth_priv.h b/dlls/winebth.sys/winebth_priv.h index e664043eb63..47ae3b03b15 100644 --- a/dlls/winebth.sys/winebth_priv.h +++ b/dlls/winebth.sys/winebth_priv.h @@ -24,6 +24,7 @@
#include <bthsdpdef.h> #include <bluetoothapis.h> +#include <bthdef.h> #include <ddk/wdm.h>
#include <wine/debug.h> @@ -198,12 +199,16 @@ NTSTATUS winebluetooth_radio_set_property( winebluetooth_radio_t radio, union winebluetooth_property *property ); NTSTATUS winebluetooth_radio_start_discovery( winebluetooth_radio_t radio ); NTSTATUS winebluetooth_radio_stop_discovery( winebluetooth_radio_t radio ); +NTSTATUS winebluetooth_auth_agent_enable_incoming( void );
void winebluetooth_device_free( winebluetooth_device_t device ); static inline BOOL winebluetooth_device_equal( winebluetooth_device_t d1, winebluetooth_device_t d2 ) { return d1.handle == d2.handle; } +void winebluetooth_device_properties_to_info( winebluetooth_device_props_mask_t props_mask, + const struct winebluetooth_device_properties *props, + BTH_DEVICE_INFO *info );
enum winebluetooth_watcher_event_type { @@ -272,6 +277,14 @@ struct winebluetooth_watcher_event enum winebluetooth_event_type { WINEBLUETOOTH_EVENT_WATCHER_EVENT, + WINEBLUETOOTH_EVENT_AUTH_EVENT +}; + +struct winebluetooth_auth_event +{ + winebluetooth_device_t device; + BLUETOOTH_AUTHENTICATION_METHOD method; + UINT32 numeric_value_or_passkey; };
struct winebluetooth_event @@ -279,6 +292,7 @@ struct winebluetooth_event enum winebluetooth_event_type status; union { struct winebluetooth_watcher_event watcher_event; + struct winebluetooth_auth_event auth_event; } data; };
diff --git a/include/wine/winebth.h b/include/wine/winebth.h index d05e4cad529..d6e3b87c259 100644 --- a/include/wine/winebth.h +++ b/include/wine/winebth.h @@ -30,6 +30,12 @@ /* Stop device inquiry for a local radio. */ #define IOCTL_WINEBTH_RADIO_STOP_DISCOVERY CTL_CODE(FILE_DEVICE_BLUETOOTH, 0xa7, METHOD_BUFFERED, FILE_ANY_ACCESS)
+ +DEFINE_GUID( GUID_WINEBTH_AUTHENTICATION_REQUEST, 0xca67235f, 0xf621, 0x4c27, 0x85, 0x65, 0xa4, + 0xd5, 0x5e, 0xa1, 0x26, 0xe8 ); + +#define WINEBTH_AUTH_DEVICE_PATH L"\??\WINEBTHAUTH" + #include <pshpack1.h>
#define LOCAL_RADIO_DISCOVERABLE 0x0001 @@ -41,6 +47,13 @@ struct winebth_radio_set_flag_params unsigned int enable : 1; };
+/* Associated data for GUID_WINEBTH_AUTHENTICATION_REQUEST events. */ +struct winebth_authentication_request +{ + BTH_DEVICE_INFO device_info; + BLUETOOTH_AUTHENTICATION_METHOD auth_method; + ULONG numeric_value_or_passkey; +}; #include <poppack.h>
#endif /* __WINEBTH_H__ */