Introduce infrastructure necessary to support receiving, sending and replying (to) Bluetooth authentication/pairing requests:
- During startup, the Bluetooth driver now creates a "pairing agent" and registers it with BlueZ. The pairing agent is a DBus object implementing the `org.bluez.Agent1` interface (documented [here](https://github.com/bluez/bluez/blob/master/doc/org.bluez.Agent.rst)), which receives authentication requests from BlueZ. The pairing agent then forwards the request data to the PE driver, in form of a `WINEBLUETOOTH_EVENT_AUTH_EVENT` event. The agent is unregistered during driver shutdown. - Right now, the agent only supports Numeric Comparison pairing requests.
- Create a new auxiliary device, `??\WINEBTHAUTH` during driver startup. This device handles authentication functionality that is independent of radio PDOs on the system, which allows the userspace APIs to not track individual Bluetooth radio objects to implement authentication. `WINEBTHAUTH` right now serves two purposes: - Serve as the device object for which `GUID_WINEBTH_AUTHENTICATION_REQUEST` PnP events are broadcasted. This event is sent whenever the Bluetooth event loop in (`bluetooth_event_loop_thread_proc`) receives `WINEBLUETOOTH_EVENT_AUTH_EVENT`. The event's buffer contains a `struct winebth_authentication_request` value, which contains data relevant to the incoming auth request. - Handle the `IOCTL_WINEBTH_AUTH_REGISTER` IOCTL. This simply calls the [`RequestDefaultAgent` BlueZ method](https://github.com/bluez/bluez/blob/master/doc/org.bluez.AgentManager.rst#vo...) right now, which asks BlueZ to send all incoming Bluetooth authentication requests to Wine's pairing agent (described above). This is not always strictly necessary, but is useful if there already is a default pairing agent set. - `WINEBTHAUTH` will also ultimately be in charge of responding to authentication requests, which I'll be adding in the next-ish MR(s).
- Implement `BluetoothRegisterForAuthenticationEx` and `BluetoothUnregisterAuthentication`. Registration is done by invoking `IOCTL_WINEBTH_AUTH_REGISTER`, and then listening for `GUID_WINEBTH_AUTHENTICATION_REQUEST` events on a `HANDLE` to `??\WINEBTHAUTH` using the newly added `CM_Register_Notifications`.
To keep the MR from getting any larger, I have not included support for `BluetoothSendAuthenticationResponseEx`. That will likely be introduced in the future.
-- v5: bluetoothapis/tests: Add tests for BluetoothRegisterForAuthenticationEx and BluetoothUnregisterAuthentication.
From: Vibhav Pant vibhavp@gmail.com
The agent receives authentication requests from BlueZ, and will relay them to the PE driver. --- dlls/winebth.sys/dbus.c | 133 ++++++++++++++++++++++++++++++++ dlls/winebth.sys/unixlib.c | 11 +++ dlls/winebth.sys/unixlib_priv.h | 2 + dlls/winebth.sys/winebth.c | 2 + 4 files changed, 148 insertions(+)
diff --git a/dlls/winebth.sys/dbus.c b/dlls/winebth.sys/dbus.c index 383fd74187d..39e5a1e4a89 100644 --- a/dlls/winebth.sys/dbus.c +++ b/dlls/winebth.sys/dbus.c @@ -116,6 +116,8 @@ 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_AGENT_MANAGER "org.bluez.AgentManager1" +#define BLUEZ_INTERFACE_AGENT "org.bluez.Agent1"
#define DO_FUNC( f ) typeof( f ) (*p_##f) DBUS_FUNCS; @@ -848,6 +850,135 @@ void bluez_dbus_free( void *connection ) p_dbus_connection_unref( connection ); }
+static DBusHandlerResult bluez_auth_agent_vtable_message_handler( DBusConnection *connection, DBusMessage *message, + void *data ) +{ + DBusMessage *reply; + dbus_bool_t success; + + FIXME_(dbus)( "(%s, %s, %p): stub!\n", dbgstr_dbus_connection( connection ), dbgstr_dbus_message( message ), + data ); + + reply = p_dbus_message_new_error( message, "org.bluez.Error.Rejected", "" ); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + success = p_dbus_connection_send( connection, reply, NULL ); + p_dbus_message_unref( reply ); + return success ? DBUS_HANDLER_RESULT_HANDLED : DBUS_HANDLER_RESULT_NEED_MEMORY; +} + +const static struct DBusObjectPathVTable bluez_auth_agent_object_vtable = { + .message_function = bluez_auth_agent_vtable_message_handler }; + +#define WINE_BLUEZ_AUTH_AGENT_PATH "/org/winehq/wine/winebth/AuthAgent" + +NTSTATUS bluez_auth_agent_start( void *connection ) +{ + static const char *wine_bluez_auth_agent_path = WINE_BLUEZ_AUTH_AGENT_PATH; + static const char *capability = "KeyboardDisplay"; + DBusMessage *request, *reply; + dbus_bool_t success; + DBusError error; + NTSTATUS status; + + TRACE( "(%s)\n", dbgstr_dbus_connection( connection ) ); + + 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, NULL, &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 ); + goto done; + } + + request = p_dbus_message_new_method_call( BLUEZ_DEST, "/org/bluez", BLUEZ_INTERFACE_AGENT_MANAGER, + "RegisterAgent" ); + if (!request) + { + status = STATUS_NO_MEMORY; + goto failure; + } + + success = p_dbus_message_append_args( request, DBUS_TYPE_OBJECT_PATH, &wine_bluez_auth_agent_path, DBUS_TYPE_STRING, + &capability, DBUS_TYPE_INVALID ); + if (!success) + { + status = STATUS_NO_MEMORY; + goto failure; + } + + /* This is called before the bluez_dbus_loop, so we need to use send_with_reply_and_block. */ + reply = p_dbus_connection_send_with_reply_and_block( connection, request, -1, &error ); + p_dbus_message_unref( request ); + if (!reply) + { + ERR( "Failed to register authentication agent with BlueZ: %s: %s\n", debugstr_a( error.name ), + debugstr_a( error.message ) ); + status = bluez_dbus_error_to_ntstatus( &error ); + goto failure; + } + p_dbus_message_unref( reply ); + status = STATUS_SUCCESS; + goto done; + +failure: + p_dbus_connection_unregister_object_path( connection, WINE_BLUEZ_AUTH_AGENT_PATH ); +done: + p_dbus_error_free( &error ); + return status; +} + +NTSTATUS bluez_auth_agent_stop( void *connection ) +{ + static const char *wine_bluez_auth_agent_path = WINE_BLUEZ_AUTH_AGENT_PATH; + DBusMessage *request, *reply; + dbus_bool_t success; + NTSTATUS status; + DBusError error; + + TRACE( "(%s)\n", dbgstr_dbus_connection( connection ) ); + + request = p_dbus_message_new_method_call( BLUEZ_DEST, "/org/bluez", BLUEZ_INTERFACE_AGENT_MANAGER, + "UnregisterAgent" ); + if (!request) + return STATUS_NO_MEMORY; + + success = p_dbus_message_append_args( request, DBUS_TYPE_OBJECT_PATH, &wine_bluez_auth_agent_path, + DBUS_TYPE_INVALID ); + if (!success) + { + p_dbus_message_unref( request ); + return STATUS_NO_MEMORY; + } + + p_dbus_error_init( &error ); + status = bluez_dbus_send_and_wait_for_reply( connection, request, &reply, &error ); + if (status) + { + p_dbus_message_unref( request ); + p_dbus_error_free( &error ); + return status; + } + if (!reply) + { + status = bluez_dbus_error_to_ntstatus( &error ); + ERR( "Could not unregister authentication agent with BlueZ: %s: %s\n", debugstr_a( error.name ), + debugstr_a( error.message ) ); + p_dbus_error_free( &error ); + return status; + } + p_dbus_error_free( &error ); + p_dbus_message_unref( reply ); + + success = p_dbus_connection_unregister_object_path( connection, WINE_BLUEZ_AUTH_AGENT_PATH ); + return success ? STATUS_SUCCESS : STATUS_NO_MEMORY; +} + struct bluez_watcher_event { struct list entry; @@ -1727,5 +1858,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_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 88f6c6164d3..ebbbe2f8fcb 100644 --- a/dlls/winebth.sys/unixlib.c +++ b/dlls/winebth.sys/unixlib.c @@ -102,9 +102,19 @@ static NTSTATUS bluetooth_init ( void *params ) if (!dbus_connection) return STATUS_INTERNAL_ERROR;
+ status = bluez_auth_agent_start( dbus_connection ); + if (status) + { + bluez_dbus_close( dbus_connection ); + return status; + } + status = bluez_watcher_init( dbus_connection, &bluetooth_watcher ); if (status) + { + bluez_auth_agent_stop( dbus_connection ); bluez_dbus_close( dbus_connection ); + } else TRACE( "dbus_connection=%p bluetooth_watcher=%p\n", dbus_connection, bluetooth_watcher ); return status; @@ -114,6 +124,7 @@ static NTSTATUS bluetooth_shutdown( void *params ) { if (!dbus_connection) return STATUS_NOT_SUPPORTED;
+ bluez_auth_agent_stop( dbus_connection ); bluez_dbus_close( dbus_connection ); bluez_watcher_close( dbus_connection, bluetooth_watcher ); bluez_dbus_free( dbus_connection ); diff --git a/dlls/winebth.sys/unixlib_priv.h b/dlls/winebth.sys/unixlib_priv.h index 60570a2413a..61656e0cf80 100644 --- a/dlls/winebth.sys/unixlib_priv.h +++ b/dlls/winebth.sys/unixlib_priv.h @@ -52,6 +52,8 @@ 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_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/winebth.c b/dlls/winebth.sys/winebth.c index 94aa2869b72..5fe6339d9c5 100644 --- a/dlls/winebth.sys/winebth.c +++ b/dlls/winebth.sys/winebth.c @@ -659,6 +659,7 @@ static NTSTATUS WINAPI fdo_pnp( DEVICE_OBJECT *device_obj, IRP *irp ) CreateThread( NULL, 0, bluetooth_event_loop_thread_proc, NULL, 0, NULL ); irp->IoStatus.Status = STATUS_SUCCESS; break; + case IRP_MN_SURPRISE_REMOVAL: irp->IoStatus.Status = STATUS_SUCCESS; break; @@ -666,6 +667,7 @@ static NTSTATUS WINAPI fdo_pnp( DEVICE_OBJECT *device_obj, IRP *irp ) { struct bluetooth_radio *device, *cur; NTSTATUS ret; + winebluetooth_shutdown(); WaitForSingleObject( event_loop_thread, INFINITE ); CloseHandle( event_loop_thread );
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__ */
From: Vibhav Pant vibhavp@gmail.com
This requests the system's Bluetooth service to send all incoming authentication requests to Wine's auth agent. This may be necessary to receive requests if another pairing agent outside Wine is already running. --- dlls/winebth.sys/dbus.c | 45 ++++++++++++++++++++++++++++++++ dlls/winebth.sys/unixlib.c | 8 ++++++ dlls/winebth.sys/unixlib.h | 2 ++ dlls/winebth.sys/unixlib_priv.h | 1 + dlls/winebth.sys/winebluetooth.c | 5 ++++ dlls/winebth.sys/winebth.c | 13 ++++++++- include/wine/winebth.h | 4 ++- 7 files changed, 76 insertions(+), 2 deletions(-)
diff --git a/dlls/winebth.sys/dbus.c b/dlls/winebth.sys/dbus.c index 7ea264e8a0a..f04a840b19b 100644 --- a/dlls/winebth.sys/dbus.c +++ b/dlls/winebth.sys/dbus.c @@ -1099,6 +1099,50 @@ NTSTATUS bluez_auth_agent_stop( void *connection, void *auth_agent_ctx ) return success ? STATUS_SUCCESS : STATUS_NO_MEMORY; }
+NTSTATUS bluez_auth_agent_request_default( void *connection ) +{ + static const char *wine_bluez_auth_agent_path = WINE_BLUEZ_AUTH_AGENT_PATH; + DBusMessage *request, *reply; + dbus_bool_t success; + NTSTATUS status; + DBusError error; + + TRACE( "(%p)\n", connection ); + + request = p_dbus_message_new_method_call( BLUEZ_DEST, "/org/bluez", BLUEZ_INTERFACE_AGENT_MANAGER, + "RequestDefaultAgent" ); + if (!request) + return STATUS_NO_MEMORY; + + success = p_dbus_message_append_args( request, DBUS_TYPE_OBJECT_PATH, &wine_bluez_auth_agent_path, + DBUS_TYPE_INVALID ); + if (!success) + { + p_dbus_message_unref( request ); + return STATUS_NO_MEMORY; + } + + p_dbus_error_init( &error ); + status = bluez_dbus_send_and_wait_for_reply( connection, request, &reply, &error ); + if (status) + { + p_dbus_message_unref( request ); + p_dbus_error_free( &error ); + return status; + } + if (!reply) + { + status = bluez_dbus_error_to_ntstatus( &error ); + ERR( "RequestDefaultAgent failed: %s: %s\n", debugstr_a( error.name ), debugstr_a( error.message ) ); + p_dbus_error_free( &error ); + return status; + } + p_dbus_error_free( &error ); + p_dbus_message_unref( reply ); + + return STATUS_SUCCESS; +} + struct bluez_watcher_event { struct list entry; @@ -2012,5 +2056,6 @@ NTSTATUS bluez_adapter_stop_discovery( void *connection, const char *adapter_pat } NTSTATUS bluez_auth_agent_start( void *connection, void **ctx ) { return STATUS_NOT_SUPPORTED; } NTSTATUS bluez_auth_agent_stop( void *connection ) { return STATUS_NOT_SUPPORTED; } +NTSTATUS bluez_auth_agent_request_default( 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 6afb3bacb00..7b3edaad61a 100644 --- a/dlls/winebth.sys/unixlib.c +++ b/dlls/winebth.sys/unixlib.c @@ -208,6 +208,12 @@ static NTSTATUS bluetooth_adapter_stop_discovery( void *args ) return bluez_adapter_stop_discovery( dbus_connection, params->adapter->str ); }
+static NTSTATUS bluetooth_auth_agent_enable_incoming( void *args ) +{ + if (!dbus_connection) return STATUS_NOT_SUPPORTED; + return bluez_auth_agent_request_default( dbus_connection ); +} + static NTSTATUS bluetooth_get_event( void *args ) { struct bluetooth_get_event_params *params = args; @@ -229,6 +235,8 @@ const unixlib_entry_t __wine_unix_call_funcs[] = {
bluetooth_device_free,
+ bluetooth_auth_agent_enable_incoming, + bluetooth_get_event, };
diff --git a/dlls/winebth.sys/unixlib.h b/dlls/winebth.sys/unixlib.h index 732c76f7d64..c9630fe6676 100644 --- a/dlls/winebth.sys/unixlib.h +++ b/dlls/winebth.sys/unixlib.h @@ -98,6 +98,8 @@ enum bluetoothapis_funcs
unix_bluetooth_device_free,
+ unix_bluetooth_auth_agent_enable_incoming, + unix_bluetooth_get_event,
unix_funcs_count diff --git a/dlls/winebth.sys/unixlib_priv.h b/dlls/winebth.sys/unixlib_priv.h index a81162b605e..29b789ab025 100644 --- a/dlls/winebth.sys/unixlib_priv.h +++ b/dlls/winebth.sys/unixlib_priv.h @@ -54,6 +54,7 @@ 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_request_default( 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 ); diff --git a/dlls/winebth.sys/winebluetooth.c b/dlls/winebth.sys/winebluetooth.c index cb3a27ba25d..0f421a87828 100644 --- a/dlls/winebth.sys/winebluetooth.c +++ b/dlls/winebth.sys/winebluetooth.c @@ -87,6 +87,11 @@ NTSTATUS winebluetooth_radio_stop_discovery( winebluetooth_radio_t radio ) return UNIX_BLUETOOTH_CALL(bluetooth_adapter_stop_discovery, ¶ms); }
+NTSTATUS winebluetooth_auth_agent_enable_incoming( void ) +{ + return UNIX_BLUETOOTH_CALL( bluetooth_auth_agent_enable_incoming, NULL ); +} + void winebluetooth_radio_free( winebluetooth_radio_t radio ) { struct bluetooth_adapter_free_params args = { 0 }; diff --git a/dlls/winebth.sys/winebth.c b/dlls/winebth.sys/winebth.c index fd825c1cba3..8c885cde2fa 100644 --- a/dlls/winebth.sys/winebth.c +++ b/dlls/winebth.sys/winebth.c @@ -97,9 +97,20 @@ static NTSTATUS WINAPI dispatch_auth( DEVICE_OBJECT *device, IRP *irp ) { IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation( irp ); ULONG code = stack->Parameters.DeviceIoControl.IoControlCode; + NTSTATUS status = irp->IoStatus.Status; + + TRACE( "device %p irp %p code %#lx\n", device, irp, code );
- FIXME( "device %p irp %p code %#lx: stub!\n", device, irp, code ); + switch (code) + { + case IOCTL_WINEBTH_AUTH_REGISTER: + status = winebluetooth_auth_agent_enable_incoming(); + break; + default: + break; + }
+ irp->IoStatus.Status = status; IoCompleteRequest( irp, IO_NO_INCREMENT ); return irp->IoStatus.Status; } diff --git a/include/wine/winebth.h b/include/wine/winebth.h index d6e3b87c259..5fa702726a7 100644 --- a/include/wine/winebth.h +++ b/include/wine/winebth.h @@ -24,11 +24,13 @@
/* Set the discoverability or connectable flag for a local radio. Enabling discoverability will also enable incoming * connections, while disabling incoming connections disables discoverability as well. */ -#define IOCTL_WINEBTH_RADIO_SET_FLAG CTL_CODE(FILE_DEVICE_BLUETOOTH, 0xa3, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_WINEBTH_RADIO_SET_FLAG CTL_CODE(FILE_DEVICE_BLUETOOTH, 0xa3, METHOD_BUFFERED, FILE_ANY_ACCESS) /* Start device inquiry for a local radio. */ #define IOCTL_WINEBTH_RADIO_START_DISCOVERY CTL_CODE(FILE_DEVICE_BLUETOOTH, 0xa6, METHOD_BUFFERED, FILE_ANY_ACCESS) /* Stop device inquiry for a local radio. */ #define IOCTL_WINEBTH_RADIO_STOP_DISCOVERY CTL_CODE(FILE_DEVICE_BLUETOOTH, 0xa7, METHOD_BUFFERED, FILE_ANY_ACCESS) +/* Ask the system's Bluetooth service to send all incoming authentication requests to Wine. */ +#define IOCTL_WINEBTH_AUTH_REGISTER CTL_CODE(FILE_DEVICE_BLUETOOTH, 0xa8, METHOD_BUFFERED, FILE_ANY_ACCESS)
DEFINE_GUID( GUID_WINEBTH_AUTHENTICATION_REQUEST, 0xca67235f, 0xf621, 0x4c27, 0x85, 0x65, 0xa4,
From: Vibhav Pant vibhavp@gmail.com
--- dlls/bluetoothapis/Makefile.in | 2 +- dlls/bluetoothapis/main.c | 219 +++++++++++++++++++++++++++++++-- 2 files changed, 212 insertions(+), 9 deletions(-)
diff --git a/dlls/bluetoothapis/Makefile.in b/dlls/bluetoothapis/Makefile.in index 3e2e230fe55..996c561fc17 100644 --- a/dlls/bluetoothapis/Makefile.in +++ b/dlls/bluetoothapis/Makefile.in @@ -1,6 +1,6 @@ MODULE = bluetoothapis.dll IMPORTLIB = bluetoothapis -IMPORTS = setupapi +IMPORTS = setupapi cfgmgr32
EXTRADLLFLAGS = -Wb,--prefer-native
diff --git a/dlls/bluetoothapis/main.c b/dlls/bluetoothapis/main.c index df85e44913a..1f7a7d5673c 100644 --- a/dlls/bluetoothapis/main.c +++ b/dlls/bluetoothapis/main.c @@ -30,6 +30,7 @@
#include "bthsdpdef.h" #include "bluetoothapis.h" +#include "cfgmgr32.h" #include "setupapi.h" #include "winioctl.h"
@@ -39,6 +40,7 @@ #include "wine/winebth.h"
#include "wine/debug.h" +#include "wine/list.h"
WINE_DEFAULT_DEBUG_CHANNEL(bluetoothapis);
@@ -65,6 +67,14 @@ static const char *debugstr_BLUETOOTH_DEVICE_SEARCH_PARAMS( const BLUETOOTH_DEVI params->fIssueInquiry, params->cTimeoutMultiplier, params->hRadio ); }
+static const char *debugstr_addr( const BYTE *addr ) +{ + if (!addr) + return wine_dbg_sprintf( "(null)" ); + + return wine_dbg_sprintf( "%02x:%02x:%02x:%02x:%02x:%02x", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5] ); +} + static BOOL radio_set_inquiry( HANDLE radio, BOOL enable ) { DWORD bytes; @@ -648,22 +658,215 @@ BOOL WINAPI BluetoothFindNextDevice( HBLUETOOTH_DEVICE_FIND find, BLUETOOTH_DEVI return success; }
+struct bluetooth_auth_listener +{ + struct list entry; + + unsigned int all_devices : 1; + BLUETOOTH_ADDRESS addr; + + PFN_AUTHENTICATION_CALLBACK_EX callback; + + void *user_data; +}; + +static const char *debugstr_bluetooth_auth_listener( const struct bluetooth_auth_listener *listener ) +{ + if (!listener) + return wine_dbg_sprintf( "(null)" ); + if (listener->all_devices) + return wine_dbg_sprintf( "{ all_devices=1 %p %p }", listener->callback, listener->user_data ); + return wine_dbg_sprintf( "{ all_devices=0 %s %p %p }", debugstr_addr( listener->addr.rgBytes ), + listener->callback, listener->user_data ); +} + +static SRWLOCK bluetooth_auth_lock = SRWLOCK_INIT; +static struct list bluetooth_auth_listeners = LIST_INIT( bluetooth_auth_listeners ); /* Guarded by bluetooth_auth_lock */ +static HCMNOTIFICATION bluetooth_auth_event_notify; /* Guarded by bluetooth_auth_lock */ + +struct auth_callback_data +{ + PFN_AUTHENTICATION_CALLBACK_EX callback; + struct winebth_authentication_request request; + void *user_data; +}; + +static CALLBACK void tp_auth_callback_work_proc( TP_CALLBACK_INSTANCE *instance, void *ctx, TP_WORK *work ) +{ + BLUETOOTH_AUTHENTICATION_CALLBACK_PARAMS params; + struct auth_callback_data *data = ctx; + + device_info_from_bth_info( ¶ms.deviceInfo, &data->request.device_info ); + /* For some reason, Windows uses the *local* time here. */ + GetLocalTime( ¶ms.deviceInfo.stLastSeen ); + GetLocalTime( ¶ms.deviceInfo.stLastUsed ); + params.authenticationMethod = data->request.auth_method; + params.ioCapability = BLUETOOTH_IO_CAPABILITY_UNDEFINED; + params.authenticationRequirements = BLUETOOTH_MITM_ProtectionNotDefined; + params.Numeric_Value = data->request.numeric_value_or_passkey; + data->callback( data->user_data, ¶ms ); + free( data ); +} + +static CALLBACK DWORD bluetooth_auth_event_callback( HCMNOTIFICATION notify, void *ctx, CM_NOTIFY_ACTION action, + CM_NOTIFY_EVENT_DATA *event_data, DWORD size ) +{ + struct winebth_authentication_request *request = (struct winebth_authentication_request *)event_data->u.DeviceHandle.Data; + struct bluetooth_auth_listener *listener; + + TRACE( "(%p, %p, %d, %p, %lu)\n", notify, ctx, action, event_data, size ); + + switch (action) + { + case CM_NOTIFY_ACTION_DEVICECUSTOMEVENT: + if (!IsEqualGUID( &event_data->u.DeviceHandle.EventGuid, &GUID_WINEBTH_AUTHENTICATION_REQUEST )) + { + FIXME( "Unexpected EventGUID: %s\n", debugstr_guid(&event_data->u.DeviceHandle.EventGuid) ); + break; + } + + AcquireSRWLockShared( &bluetooth_auth_lock ); + LIST_FOR_EACH_ENTRY( listener, &bluetooth_auth_listeners, struct bluetooth_auth_listener, entry ) + { + TP_WORK *work; + struct auth_callback_data *data; + + if (!(listener->all_devices || listener->addr.ullLong == request->device_info.address)) + continue; + data = calloc( 1, sizeof( *data ) ); + if (!data) + continue; + + data->request = *request; + data->callback = listener->callback; + data->user_data = listener->user_data; + work = CreateThreadpoolWork( tp_auth_callback_work_proc, data, NULL ); + if (!work) + { + ERR( "CreateThreadpoolWork failed: %lu\n", GetLastError() ); + free( data ); + continue; + } + SubmitThreadpoolWork( work ); + } + ReleaseSRWLockShared( &bluetooth_auth_lock ); + break; + default: + FIXME( "Unexpected CM_NOTIFY_ACTION: %d\n", action ); + break; + } + + return 0; +} + +static DWORD bluetooth_auth_register( void ) +{ + DWORD ret; + CM_NOTIFY_FILTER handle_filter = {0}; + HANDLE winebth_auth; + DWORD bytes; + + + winebth_auth = CreateFileW( WINEBTH_AUTH_DEVICE_PATH, GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ); + if (winebth_auth == INVALID_HANDLE_VALUE) + return GetLastError(); + + handle_filter.cbSize = sizeof( handle_filter ); + handle_filter.FilterType = CM_NOTIFY_FILTER_TYPE_DEVICEHANDLE; + handle_filter.u.DeviceHandle.hTarget = winebth_auth; + + ret = CM_Register_Notification( &handle_filter, NULL, bluetooth_auth_event_callback, + &bluetooth_auth_event_notify ); + if (ret) + { + ERR( "CM_Register_Notification failed: %#lx\n", ret ); + CloseHandle( winebth_auth ); + return CM_MapCrToWin32Err( ret, ERROR_INTERNAL_ERROR ); + } + + if (!DeviceIoControl( winebth_auth, IOCTL_WINEBTH_AUTH_REGISTER, NULL, 0, NULL, 0, &bytes, NULL )) + ret = GetLastError(); + + CloseHandle( winebth_auth ); + return ret; +} + /********************************************************************* * BluetoothRegisterForAuthenticationEx */ -DWORD WINAPI BluetoothRegisterForAuthenticationEx(const BLUETOOTH_DEVICE_INFO *info, HBLUETOOTH_AUTHENTICATION_REGISTRATION *out, - PFN_AUTHENTICATION_CALLBACK_EX callback, void *param) +DWORD WINAPI BluetoothRegisterForAuthenticationEx( const BLUETOOTH_DEVICE_INFO *device_info, + HBLUETOOTH_AUTHENTICATION_REGISTRATION *out, + PFN_AUTHENTICATION_CALLBACK_EX callback, void *param ) { - FIXME("(%p, %p, %p, %p): stub!\n", info, out, callback, param); - return ERROR_CALL_NOT_IMPLEMENTED; + DWORD ret; + struct bluetooth_auth_listener *listener; + + TRACE( "(%p, %p, %p, %p)\n", device_info, out, callback, param ); + + if (!out || (device_info && device_info->dwSize != sizeof ( *device_info )) || !callback) + return ERROR_INVALID_PARAMETER; + + listener = calloc( 1, sizeof( *listener ) ); + if (!listener) + return ERROR_OUTOFMEMORY; + listener->all_devices = !device_info; + if (!listener->all_devices) + { + TRACE( "Registering for authentication events from %s\n", debugstr_addr( device_info->Address.rgBytes ) ); + listener->addr = device_info->Address; + } + listener->callback = callback; + listener->user_data = param; + + AcquireSRWLockExclusive( &bluetooth_auth_lock ); + if (list_empty( &bluetooth_auth_listeners )) + { + ret = bluetooth_auth_register(); + if (ret) + { + free( listener ); + ReleaseSRWLockExclusive( &bluetooth_auth_lock ); + return ret; + } + } + /* The MSDN documentation for BluetoothRegisterForAuthentication states if applications call this method + * multiple times, only the first callback registered is invoked during an authentication session. + * This is incorrect, at least for Windows 11, where all registered callbacks do get called. */ + list_add_tail( &bluetooth_auth_listeners, &listener->entry ); + ReleaseSRWLockExclusive( &bluetooth_auth_lock ); + + *out = listener; + ret = ERROR_SUCCESS; + return ret; }
/********************************************************************* * BluetoothUnregisterAuthentication */ -BOOL WINAPI BluetoothUnregisterAuthentication(HBLUETOOTH_AUTHENTICATION_REGISTRATION handle) +BOOL WINAPI BluetoothUnregisterAuthentication( HBLUETOOTH_AUTHENTICATION_REGISTRATION handle ) { - FIXME("(%p): stub!\n", handle); - if (!handle) SetLastError(ERROR_INVALID_HANDLE); - return FALSE; + struct bluetooth_auth_listener *listener = handle; + DWORD ret = ERROR_SUCCESS; + + TRACE( "(%s)\n", debugstr_bluetooth_auth_listener( handle ) ); + + if (!handle) + { + SetLastError( ERROR_INVALID_HANDLE ); + return FALSE; + } + + AcquireSRWLockExclusive( &bluetooth_auth_lock ); + list_remove( &listener->entry ); + if (list_empty( &bluetooth_auth_listeners )) + { + ret = CM_Unregister_Notification( bluetooth_auth_event_notify ); + if (ret) + ERR( "CM_Unregister_Notification failed: %#lx\n", ret ); + } + ReleaseSRWLockExclusive( &bluetooth_auth_lock ); + + SetLastError( ret ); + return !ret; }
From: Vibhav Pant vibhavp@gmail.com
--- dlls/bluetoothapis/tests/device.c | 104 +++++++++++++++++++++++++++--- 1 file changed, 96 insertions(+), 8 deletions(-)
diff --git a/dlls/bluetoothapis/tests/device.c b/dlls/bluetoothapis/tests/device.c index 13ccf8c2af3..4e62c815b58 100644 --- a/dlls/bluetoothapis/tests/device.c +++ b/dlls/bluetoothapis/tests/device.c @@ -27,6 +27,7 @@ #include <bthsdpdef.h> #include <bluetoothapis.h>
+#include <wine/debug.h> #include <wine/test.h>
extern void test_for_all_radios( const char *file, int line, void (*test)( HANDLE radio, void *data ), void *data ); @@ -36,6 +37,23 @@ static const char *debugstr_bluetooth_address( const BYTE *addr ) return wine_dbg_sprintf( "%02x:%02x:%02x:%02x:%02x:%02x", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5] ); }
+static const char *debugstr_BLUETOOTH_DEVICE_INFO( const BLUETOOTH_DEVICE_INFO *info ) +{ + WCHAR last_seen[256]; + WCHAR last_used[256]; + + last_seen[0] = last_used[0] = 0; + + GetTimeFormatEx( NULL, TIME_FORCE24HOURFORMAT, &info->stLastSeen, NULL, last_seen, ARRAY_SIZE( last_seen ) ); + GetTimeFormatEx( NULL, TIME_FORCE24HOURFORMAT, &info->stLastUsed, NULL, last_used, ARRAY_SIZE( last_used ) ); + + return wine_dbg_sprintf( "{ Address: %s ulClassOfDevice: %#lx fConnected: %d fRemembered: %d fAuthenticated: %d " + "stLastSeen: %s stLastUsed: %s szName: %s }", + debugstr_bluetooth_address( info->Address.rgBytes ), info->ulClassofDevice, + info->fConnected, info->fRemembered, info->fAuthenticated, debugstr_w( last_seen ), + debugstr_w( last_used ), debugstr_w( info->szName ) ); +} + void test_radio_BluetoothFindFirstDevice( HANDLE radio, void *data ) { BLUETOOTH_DEVICE_SEARCH_PARAMS search_params; @@ -123,7 +141,6 @@ void test_radio_BluetoothFindNextDevice( HANDLE radio, void *data ) for (;;) { BLUETOOTH_DEVICE_INFO info2 = {0}; - WCHAR buf[256]; BOOL matches;
matches = (info.fConnected && search_params.fReturnConnected) @@ -131,13 +148,7 @@ void test_radio_BluetoothFindNextDevice( HANDLE radio, void *data ) || (info.fRemembered && search_params.fReturnRemembered) || (!info.fRemembered && search_params.fReturnUnknown); ok( matches, "Device does not match filter constraints\n" ); - trace( "device %lu: %s\n", i, debugstr_bluetooth_address( info.Address.rgBytes) ); - trace( " name: %s\n", debugstr_w( info.szName ) ); - trace( " class: %#lx\n", info.ulClassofDevice ); - trace( " connected: %d, authenticated: %d, remembered: %d\n", info.fConnected, info.fAuthenticated, - info.fRemembered ); - if (GetTimeFormatEx( NULL, TIME_FORCE24HOURFORMAT, &info.stLastSeen, NULL, buf, ARRAY_SIZE( buf ) )) - trace( " last seen: %s UTC\n", debugstr_w( buf ) ); + trace( "device %lu: %s\n", i, debugstr_BLUETOOTH_DEVICE_INFO( &info ) );
info2.dwSize = sizeof( info2 ); info2.Address = info.Address; @@ -200,9 +211,86 @@ void test_BluetoothFindDeviceClose( void ) ok( err == ERROR_INVALID_HANDLE, "%lu != %d\n", err, ERROR_INVALID_HANDLE ); }
+static HANDLE auth_events[2]; + +static void test_auth_callback_params( int line, const BLUETOOTH_AUTHENTICATION_CALLBACK_PARAMS *auth_params ) +{ + ok_( __FILE__, line )( !!auth_params, "Expected authentication params to not be NULL\n" ); + if (auth_params) + { + ULARGE_INTEGER ft = {0}; + + trace_( __FILE__, line )( "Device: %s\n", debugstr_BLUETOOTH_DEVICE_INFO( &auth_params->deviceInfo ) ); + trace_( __FILE__, line )( "Method: %#x\n", auth_params->authenticationMethod ); + trace_( __FILE__, line )( "Numeric value: %lu\n", auth_params->Numeric_Value ); + + SystemTimeToFileTime( &auth_params->deviceInfo.stLastSeen, (FILETIME *)&ft ); + ok( ft.QuadPart, "Expected stLastSeen to be greater than zero\n" ); + ft.QuadPart = 0; + SystemTimeToFileTime( &auth_params->deviceInfo.stLastUsed, (FILETIME *)&ft ); + ok( ft.QuadPart, "Expected stLastUsed to be greater than zero\n" ); + } +} + +static CALLBACK BOOL auth_ex_callback( void *data, BLUETOOTH_AUTHENTICATION_CALLBACK_PARAMS *auth_params ) +{ + test_auth_callback_params( __LINE__, auth_params ); + SetEvent( auth_events[0] ); + return TRUE; +} + +static CALLBACK BOOL auth_ex_callback_2( void *data, BLUETOOTH_AUTHENTICATION_CALLBACK_PARAMS *auth_params ) +{ + test_auth_callback_params( __LINE__, auth_params ); + SetEvent( auth_events[1] ); + return TRUE; +} + +static void test_BluetoothRegisterForAuthenticationEx( void ) +{ + HBLUETOOTH_AUTHENTICATION_REGISTRATION hreg = NULL, hreg2 = NULL; + BLUETOOTH_DEVICE_INFO info; + BOOL success; + DWORD err; + + err = BluetoothRegisterForAuthenticationEx( NULL, NULL, NULL, NULL ); + ok( err == ERROR_INVALID_PARAMETER, "%lu != %d\n", err, ERROR_INVALID_PARAMETER ); + + info.dwSize = sizeof( info ) + 1; + err = BluetoothRegisterForAuthenticationEx( &info, &hreg, NULL, NULL ); + ok( err == ERROR_INVALID_PARAMETER, "%lu != %d\n", err, ERROR_INVALID_PARAMETER ); + + auth_events[0] = CreateEventA( NULL, FALSE, FALSE, NULL ); + auth_events[1] = CreateEventA( NULL, FALSE, FALSE, NULL ); + + BluetoothEnableIncomingConnections( NULL, TRUE ); + BluetoothEnableDiscovery( NULL, TRUE ); + + err = BluetoothRegisterForAuthenticationEx( NULL, &hreg, auth_ex_callback, NULL ); + ok( !err, "BluetoothRegisterForAuthenticationEx failed: %lu\n", err ); + ok( !!hreg, "Handle was not filled\n" ); + + err = BluetoothRegisterForAuthenticationEx( NULL, &hreg2, auth_ex_callback_2, NULL ); + ok( !err, "BluetoothRegisterForAuthenticationEx failed: %lu\n", err ); + ok( !!hreg2, "Handle was not filled\n" ); + + err = WaitForMultipleObjects( 2, auth_events, TRUE, 60000 ); + ok( !err, "WaitForMultipleObjects failed: %lu\n", err ); + + success = BluetoothUnregisterAuthentication( hreg ); + ok( success, "BluetoothUnregisterAuthentication failed: %lu\n", GetLastError() ); + success = BluetoothUnregisterAuthentication( hreg ); + ok( success, "BluetoothUnregisterAuthentication failed: %lu\n", GetLastError() ); + + BluetoothEnableDiscovery( NULL, FALSE ); +} + START_TEST( device ) { test_BluetoothFindFirstDevice(); test_BluetoothFindDeviceClose(); test_BluetoothFindNextDevice(); + + if (winetest_interactive) + test_BluetoothRegisterForAuthenticationEx(); }
`+ struct bluetooth_auth_listener *hreg;`
Bikeshedding, but "hreg" seems an odd variable name; why not "listener"?
A lot of documentation/Win32 code seems to use the `h` prefix for opaque handles like these, so I inadvertently ended up doing the same. `listener` is more descriptive, you're right. Renamed.
Well, as I've mentioned earlier, that's not the handle; that really is the object ;-)
Why the threadpool? Why can't we call the callback right now?
To avoid a deadlock if the callback calls `BluetoothUnregisterAuthentication` (which succeeds in Windows).
Hmm, okay, that's reasonable.
Since this is a simple one-shot function I think TrySubmitThreadpoolCallback() would work and be simpler (if only by one or two lines)?