diff --git a/libexec/rc/rc.d/bluetooth b/libexec/rc/rc.d/bluetooth --- a/libexec/rc/rc.d/bluetooth +++ b/libexec/rc/rc.d/bluetooth @@ -131,6 +131,15 @@ ${hccontrol} -n ${dev}hci reset \ > /dev/null 2>&1 || return 1 + # Enable SSP if supported ignoring the result if it is + # unsupported. + ${hccontrol} -n ${dev}hci write_simple_pairing_mode 1 \ + > /dev/null 2>&1 + + # Set event mask for SSP features + ${hccontrol} -n ${dev}hci set_event_mask \ + > /dev/null 2>&1 + ${hccontrol} -n ${dev}hci read_bd_addr \ > /dev/null 2>&1 || return 1 diff --git a/sys/netgraph/bluetooth/include/ng_hci.h b/sys/netgraph/bluetooth/include/ng_hci.h --- a/sys/netgraph/bluetooth/include/ng_hci.h +++ b/sys/netgraph/bluetooth/include/ng_hci.h @@ -228,61 +228,61 @@ #define NG_HCI_EVMSK_DEFAULT 0x00001fffffffffff #define NG_HCI_EVMSK_ALL 0x1fffffffffffffff #define NG_HCI_EVMSK_NONE 0x0000000000000000 -#define NG_HCI_EVMSK_INQUIRY_COMPL 0x0000000000000001 -#define NG_HCI_EVMSK_INQUIRY_RESULT 0x0000000000000002 -#define NG_HCI_EVMSK_CON_COMPL 0x0000000000000004 -#define NG_HCI_EVMSK_CON_REQ 0x0000000000000008 -#define NG_HCI_EVMSK_DISCON_COMPL 0x0000000000000010 -#define NG_HCI_EVMSK_AUTH_COMPL 0x0000000000000020 -#define NG_HCI_EVMSK_REMOTE_NAME_REQ_COMPL 0x0000000000000040 -#define NG_HCI_EVMSK_ENCRYPTION_CHANGE 0x0000000000000080 -#define NG_HCI_EVMSK_CHANGE_CON_LINK_KEY_COMPL 0x0000000000000100 -#define NG_HCI_EVMSK_MASTER_LINK_KEY_COMPL 0x0000000000000200 -#define NG_HCI_EVMSK_READ_REMOTE_FEATURES_COMPL 0x0000000000000400 -#define NG_HCI_EVMSK_READ_REMOTE_VER_INFO_COMPL 0x0000000000000800 -#define NG_HCI_EVMSK_QOS_SETUP_COMPL 0x0000000000001000 -#define NG_HCI_EVMSK_COMMAND_COMPL 0x0000000000002000 -#define NG_HCI_EVMSK_COMMAND_STATUS 0x0000000000004000 -#define NG_HCI_EVMSK_HARDWARE_ERROR 0x0000000000008000 -#define NG_HCI_EVMSK_FLUSH_OCCUR 0x0000000000010000 -#define NG_HCI_EVMSK_ROLE_CHANGE 0x0000000000020000 -#define NG_HCI_EVMSK_NUM_COMPL_PKTS 0x0000000000040000 -#define NG_HCI_EVMSK_MODE_CHANGE 0x0000000000080000 -#define NG_HCI_EVMSK_RETURN_LINK_KEYS 0x0000000000100000 -#define NG_HCI_EVMSK_PIN_CODE_REQ 0x0000000000200000 -#define NG_HCI_EVMSK_LINK_KEY_REQ 0x0000000000400000 -#define NG_HCI_EVMSK_LINK_KEY_NOTIFICATION 0x0000000000800000 -#define NG_HCI_EVMSK_LOOPBACK_COMMAND 0x0000000001000000 -#define NG_HCI_EVMSK_DATA_BUFFER_OVERFLOW 0x0000000002000000 -#define NG_HCI_EVMSK_MAX_SLOT_CHANGE 0x0000000004000000 -#define NG_HCI_EVMSK_READ_CLOCK_OFFSET_COMLETE 0x0000000008000000 -#define NG_HCI_EVMSK_CON_PKT_TYPE_CHANGED 0x0000000010000000 -#define NG_HCI_EVMSK_QOS_VIOLATION 0x0000000020000000 -#define NG_HCI_EVMSK_PAGE_SCAN_MODE_CHANGE 0x0000000040000000 -#define NG_HCI_EVMSK_PAGE_SCAN_REP_MODE_CHANGE 0x0000000080000000 -#define NG_HCI_EVMSK_FLOW_SPEC_COMPL 0x0000000100000000 -#define NG_HCI_EVMSK_INQUIRY_RESULT_W_RSSI 0x0000000200000000 -#define NG_HCI_EVMSK_READ_REM_EXT_FEAT_COMPL 0x0000000400000000 - -/* 0x0000000800000000 - 0x0000080000000000 - not in use */ - -#define NG_HCI_EVMSK_SYNC_CONN_COMPL 0x0000100000000000 -#define NG_HCI_EVMSK_SYNC_CONN_CHANGED 0x0000200000000000 -#define NG_HCI_EVMSK_SNIFF_SUBRATING 0x0000400000000000 -#define NG_HCI_EVMSK_EXT_INQUIRY_RESULT 0x0000800000000000 -#define NG_HCI_EVMSK_ENC_KEY_REFRESH_COMPL 0x0001000000000000 -#define NG_HCI_EVMSK_IO_CAPABILITY_REQ 0x0002000000000000 -#define NG_HCI_EVMSK_IO_CAPABILITY_RESP 0x0004000000000000 -#define NG_HCI_EVMSK_USER_CONFIRMATION_REQ 0x0008000000000000 -#define NG_HCI_EVMSK_USER_PASSKEY_REQ 0x0010000000000000 -#define NG_HCI_EVMSK_REM_OOB_DATA_REQ 0x0020000000000000 -#define NG_HCI_EVMSK_SIMPLE_PAIRING_COMPL 0x0040000000000000 -#define NG_HCI_EVMSK_LINK_SUPERV_TO_CHANGED 0x0080000000000000 -#define NG_HCI_EVMSK_ENH_FLUSH_COMPL 0x0100000000000000 -#define NG_HCI_EVMSK_USER_PASSKEY_NOTIFICATION 0x0200000000000000 -#define NG_HCI_EVMSK_KEYPRESS_NOTIFICATION 0x0400000000000000 -#define NG_HCI_EVMSK_REM_HOST_SUPP_FEAT_NOTIFI 0x0800000000000000 -#define NG_HCI_EVMSK_LE_META 0x1000000000000000 +#define NG_HCI_EVMSK_INQUIRY_COMPL (1lu << 0) +#define NG_HCI_EVMSK_INQUIRY_RESULT (1lu << 1) +#define NG_HCI_EVMSK_CON_COMPL (1lu << 2) +#define NG_HCI_EVMSK_CON_REQ (1lu << 3) +#define NG_HCI_EVMSK_DISCON_COMPL (1lu << 4) +#define NG_HCI_EVMSK_AUTH_COMPL (1lu << 5) +#define NG_HCI_EVMSK_REMOTE_NAME_REQ_COMPL (1lu << 6) +#define NG_HCI_EVMSK_ENCRYPTION_CHANGE (1lu << 7) +#define NG_HCI_EVMSK_CHANGE_CON_LINK_KEY_COMPL (1lu << 8) +#define NG_HCI_EVMSK_MASTER_LINK_KEY_COMPL (1lu << 9) +#define NG_HCI_EVMSK_READ_REMOTE_FEATURES_COMPL (1lu << 10) +#define NG_HCI_EVMSK_READ_REMOTE_VER_INFO_COMPL (1lu << 11) +#define NG_HCI_EVMSK_QOS_SETUP_COMPL (1lu << 12) +#define NG_HCI_EVMSK_COMMAND_COMPL (1lu << 13) +#define NG_HCI_EVMSK_COMMAND_STATUS (1lu << 14) +#define NG_HCI_EVMSK_HARDWARE_ERROR (1lu << 15) +#define NG_HCI_EVMSK_FLUSH_OCCUR (1lu << 16) +#define NG_HCI_EVMSK_ROLE_CHANGE (1lu << 17) +#define NG_HCI_EVMSK_NUM_COMPL_PKTS (1lu << 18) +#define NG_HCI_EVMSK_MODE_CHANGE (1lu << 19) +#define NG_HCI_EVMSK_RETURN_LINK_KEYS (1lu << 20) +#define NG_HCI_EVMSK_PIN_CODE_REQ (1lu << 21) +#define NG_HCI_EVMSK_LINK_KEY_REQ (1lu << 22) +#define NG_HCI_EVMSK_LINK_KEY_NOTIFICATION (1lu << 23) +#define NG_HCI_EVMSK_LOOPBACK_COMMAND (1lu << 24) +#define NG_HCI_EVMSK_DATA_BUFFER_OVERFLOW (1lu << 25) +#define NG_HCI_EVMSK_MAX_SLOT_CHANGE (1lu << 26) +#define NG_HCI_EVMSK_READ_CLOCK_OFFSET_COMLETE (1lu << 27) +#define NG_HCI_EVMSK_CON_PKT_TYPE_CHANGED (1lu << 28) +#define NG_HCI_EVMSK_QOS_VIOLATION (1lu << 29) +#define NG_HCI_EVMSK_PAGE_SCAN_MODE_CHANGE (1lu << 30) +#define NG_HCI_EVMSK_PAGE_SCAN_REP_MODE_CHANGE (1lu << 31) +#define NG_HCI_EVMSK_FLOW_SPEC_COMPL (1lu << 32) +#define NG_HCI_EVMSK_INQUIRY_RESULT_W_RSSI (1lu << 33) +#define NG_HCI_EVMSK_READ_REM_EXT_FEAT_COMPL (1lu << 34) + +/* 0x0000000800000000 - 0x0000080000000000 - not in use */ + +#define NG_HCI_EVMSK_SYNC_CONN_COMPL (1lu << 43) +#define NG_HCI_EVMSK_SYNC_CONN_CHANGED (1lu << 44) +#define NG_HCI_EVMSK_SNIFF_SUBRATING (1lu << 45) +#define NG_HCI_EVMSK_EXT_INQUIRY_RESULT (1lu << 46) +#define NG_HCI_EVMSK_ENC_KEY_REFRESH_COMPL (1lu << 47) +#define NG_HCI_EVMSK_IO_CAPABILITY_REQ (1lu << 48) +#define NG_HCI_EVMSK_IO_CAPABILITY_RESP (1lu << 49) +#define NG_HCI_EVMSK_USER_CONFIRMATION_REQ (1lu << 50) +#define NG_HCI_EVMSK_USER_PASSKEY_REQ (1lu << 51) +#define NG_HCI_EVMSK_REM_OOB_DATA_REQ (1lu << 52) +#define NG_HCI_EVMSK_SIMPLE_PAIRING_COMPL (1lu << 53) +#define NG_HCI_EVMSK_LINK_SUPERV_TO_CHANGED (1lu << 55) +#define NG_HCI_EVMSK_ENH_FLUSH_COMPL (1lu << 56) +#define NG_HCI_EVMSK_USER_PASSKEY_NOTIFICATION (1lu << 58) +#define NG_HCI_EVMSK_KEYPRESS_NOTIFICATION (1lu << 59) +#define NG_HCI_EVMSK_REM_HOST_SUPP_FEAT_NOTIFI (1lu << 60) +#define NG_HCI_EVMSK_LE_META (1lu << 61) /* 0x1000000100000000 - 0x8000000000000000 - reserved for future use */ /* LE events masks*/ @@ -861,6 +861,13 @@ } __attribute__ ((packed)) ng_hci_read_clock_offset_cp; /* No return parameter(s) */ +#define NG_HCI_OCF_USER_CONFIRM_REP 0x002c +typedef struct { + bdaddr_t bdaddr; +} __attribute__ ((packed)) ng_hci_user_confirm_rp; + +#define NG_HCI_OCF_USER_CONFIRM_NEGATIVE_REP 0x002d + /************************************************************************** ************************************************************************** ** Link policy commands and return parameters @@ -1781,6 +1788,26 @@ } __attribute__((packed)) ng_le_transmitter_test_cp; typedef ng_hci_status_rp ng_hci_le_transmitter_test_rp; +#define NG_HCI_OCF_IO_CAPABILITY_REP 0x02b +typedef struct { + bdaddr_t addr; + u_int8_t io_cap; +#define NG_HCI_IO_CAP_DISPLAY_ONLY 0x00 +#define NG_HCI_IO_CAP_DISPLAY_YESNO 0x01 +#define NG_HCI_IO_CAP_KEYBOAD_ONLY 0x02 +#define NG_HCI_IO_CAP_NO_INPUT_NO_OUTPUT 0x03 +#define NG_HCI_IO_CAP_KEYBOAD_DISPLAY 0x04 + u_int8_t oob_data_present; + u_int8_t auth_requirements; +#define NG_HCI_AUTH_REQ_MITMPROT_NOTREQ 0x00 +#define NG_HCI_AUTH_REQ_MITMPROT_REQ 0x01 +#define NG_HCI_AUTH_REQ_NO_BONDING 0x00 +#define NG_HCI_AUTH_REQ_DEDICATED_BONDING 0x02 +#define NG_HCI_AUTH_REQ_GENERAL_BONDING 0x04 +#define NG_HCI_AUTH_REQ_NUMERIC_COMP 0x00 +#define NG_HCI_AUTH_REQ_USE_IO_CAPS 0x01 +} __attribute__((packed)) ng_hci_io_capability_request_rp; + #define NG_HCI_OCF_LE_TEST_END 0x001f /* No command parameter. */ typedef struct { @@ -2026,6 +2053,17 @@ bdaddr_t bdaddr; /* destination address */ u_int8_t page_scan_rep_mode; /* page scan repetition mode */ } __attribute__ ((packed)) ng_hci_page_scan_rep_mode_change_ep; + +#define NG_HCI_EVENT_IO_CAPABILITY_REQ 0x31 +typedef struct { + bdaddr_t bdaddr; /* remote address */ +} __attribute__ ((packed)) ng_hci_io_capability_req_ep; + +#define NG_HCI_EVENT_USER_CONFIRM_REQ 0x33 +typedef struct { + u_int32_t numeric_value; +} __attribute__ ((packed)) ng_hci_user_confirm_ep; + #define NG_HCI_EVENT_LE 0x3e typedef struct { u_int8_t subevent_code; diff --git a/usr.sbin/bluetooth/hccontrol/hccontrol.c b/usr.sbin/bluetooth/hccontrol/hccontrol.c --- a/usr.sbin/bluetooth/hccontrol/hccontrol.c +++ b/usr.sbin/bluetooth/hccontrol/hccontrol.c @@ -140,6 +140,8 @@ bit_set(filter.event_mask, NG_HCI_EVENT_INQUIRY_COMPL - 1); bit_set(filter.event_mask, NG_HCI_EVENT_INQUIRY_RESULT - 1); bit_set(filter.event_mask, NG_HCI_EVENT_CON_COMPL - 1); + bit_set(filter.event_mask, NG_HCI_EVENT_AUTH_COMPL - 1); + bit_set(filter.event_mask, NG_HCI_EVENT_ENCRYPTION_CHANGE - 1); bit_set(filter.event_mask, NG_HCI_EVENT_DISCON_COMPL - 1); bit_set(filter.event_mask, NG_HCI_EVENT_REMOTE_NAME_REQ_COMPL - 1); bit_set(filter.event_mask, NG_HCI_EVENT_READ_REMOTE_FEATURES_COMPL - 1); diff --git a/usr.sbin/bluetooth/hccontrol/host_controller_baseband.c b/usr.sbin/bluetooth/hccontrol/host_controller_baseband.c --- a/usr.sbin/bluetooth/hccontrol/host_controller_baseband.c +++ b/usr.sbin/bluetooth/hccontrol/host_controller_baseband.c @@ -34,6 +34,7 @@ #define L2CAP_SOCKET_CHECKED #include #include +#include #include #include #include "hccontrol.h" @@ -393,6 +394,47 @@ return (OK); } /* hci_delete_stored_link_key */ +/* Send Set_Event_Mask command to the unit */ +static int +hci_set_event_mask(int s, int argc, char **argv) +{ + ng_hci_set_event_mask_cp cp; + ng_hci_set_event_mask_rp rp; + int32_t n; + uint64_t mask = NG_HCI_EVENT_MASK_DEFAULT; + uint64_t extra = 0; + + /* enable notifications for IO capability req/rep and simple + * pairing complete events */ + mask |= + NG_HCI_EVMSK_USER_CONFIRMATION_REQ | + NG_HCI_EVMSK_SIMPLE_PAIRING_COMPL | + NG_HCI_EVMSK_IO_CAPABILITY_REQ | + NG_HCI_EVMSK_IO_CAPABILITY_RESP; + memset(&cp, 0, sizeof(cp)); + + if (argc == 1 && sscanf(argv[0], "%"SCNx64, &extra) != 1) + return (USAGE); + + le64enc(cp.event_mask, mask | extra); + + /* send command */ + n = sizeof(cp); + if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, + NG_HCI_OCF_SET_EVENT_MASK), + (char const *) &cp, sizeof(cp), + (char *) &rp, &n) == ERROR) + return (ERROR); + + if (rp.status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(rp.status), rp.status); + return (FAILED); + } + + return (OK); +} /* hci_set_event_mask */ + /* Send Change_Local_Name command to the unit */ static int hci_change_local_name(int s, int argc, char **argv) @@ -1211,6 +1253,44 @@ return (OK); } /* hci_write_hold_mode_activity */ +/* Send Write_Simple_Pairing_Mode command to the unit */ +static int +hci_write_simple_pairing_mode(int s, int argc, char **argv) +{ + uint8_t enable = 0; + uint8_t rp_status = 0; + int n; + + /* parse command parameters */ + switch (argc) { + case 1: + if (sscanf(argv[0], "%d", &n) != 1 || n < 0 || n > 1) + return (USAGE); + + enable = (uint8_t) n; + break; + + default: + return (USAGE); + } + + /* send command */ + n = sizeof(rp_status); + if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, + 0x056), + (char const *) &enable, sizeof(enable), + (char *) &rp_status, &n) == ERROR) + return (ERROR); + + if (rp_status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(rp_status), rp_status); + return (FAILED); + } + + return (OK); +} /* hci_write_simple_pairing_mode */ + /* Send Read_SCO_Flow_Control_Enable command to the unit */ static int hci_read_sco_flow_control_enable(int s, int argc, char **argv) @@ -1617,6 +1697,11 @@ &hci_delete_stored_link_key }, { +"set_event_mask mask", +"Sets the event mask of the HC including for use in SSP.\n", +&hci_set_event_mask +}, +{ "change_local_name ", "\nThe Change_Local_Name command provides the ability to modify the user\n" \ "friendly name for the Bluetooth unit.\n\n" \ @@ -1857,6 +1942,11 @@ &hci_write_hold_mode_activity }, { +"write_simple_pairing_mode enable(0|1)", +"\nEnable or disable simple pairing mode\n", +&hci_write_simple_pairing_mode +}, +{ "read_sco_flow_control_enable", "\nThe Read_SCO_Flow_Control_Enable command provides the ability to read\n" \ "the SCO_Flow_Control_Enable setting. By using this setting, the Host can\n" \ diff --git a/usr.sbin/bluetooth/hccontrol/link_control.c b/usr.sbin/bluetooth/hccontrol/link_control.c --- a/usr.sbin/bluetooth/hccontrol/link_control.c +++ b/usr.sbin/bluetooth/hccontrol/link_control.c @@ -288,6 +288,121 @@ return (OK); } /* hci_create_connection */ +/* Send Request_Authentication command to the unit */ +static int +hci_request_authentication(int s, int argc, char **argv) +{ + int n0; + char b[512]; + ng_hci_auth_req_cp cp; + ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b; + + /* Set defaults */ + memset(&cp, 0, sizeof(cp)); + + if (argc != 1) + return (USAGE); + + if (sscanf(argv[0], "%d", &n0) != 1) + return (USAGE); + + cp.con_handle = htole16(n0); + + /* send request and expect status response */ + n0 = sizeof(b); + if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL, + NG_HCI_OCF_AUTH_REQ), + (char const *) &cp, sizeof(cp), b, &n0) == ERROR) + return (ERROR); + + if (*b != 0x00) + return (FAILED); + + /* wait for event */ +again: + n0 = sizeof(b); + if (hci_recv(s, b, &n0) == ERROR) + return (ERROR); + if (n0 < sizeof(*e)) { + errno = EIO; + return (ERROR); + } + + if (e->event == NG_HCI_EVENT_AUTH_COMPL) { + ng_hci_auth_compl_ep *ep = (ng_hci_auth_compl_ep *)(e + 1); + + if (ep->status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(ep->status), ep->status); + return (FAILED); + } + + } else + goto again; + + return (OK); +} /* hci_request_authentication */ + +/* Send Set_Connection_Encryption */ +static int +hci_set_connection_encryption(int s, int argc, char **argv) +{ + int n0; + char b[512]; + ng_hci_set_con_encryption_cp cp; + ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b; + + /* Set defaults */ + memset(&cp, 0, sizeof(cp)); + + if (argc != 2) + return (USAGE); + + if (sscanf(argv[0], "%d", &n0) != 1) + return (USAGE); + + cp.con_handle = htole16(n0); + + if (sscanf(argv[1], "%d", &n0) != 1) + return (USAGE); + + cp.encryption_enable = (u_int16_t)n0; + + /* send request and expect status response */ + n0 = sizeof(b); + if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL, + NG_HCI_OCF_SET_CON_ENCRYPTION), + (char const *) &cp, sizeof(cp), b, &n0) == ERROR) + return (ERROR); + + if (*b != 0x00) + return (FAILED); + + /* wait for event */ +again: + n0 = sizeof(b); + if (hci_recv(s, b, &n0) == ERROR) + return (ERROR); + if (n0 < sizeof(*e)) { + errno = EIO; + return (ERROR); + } + + if (e->event == NG_HCI_EVENT_ENCRYPTION_CHANGE) { + ng_hci_encryption_change_ep *ep = (ng_hci_encryption_change_ep *)(e + 1); + + if (ep->status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(ep->status), ep->status); + return (FAILED); + } + + } else + goto again; + + return (OK); +} /* hci_set_connection_encryption */ + /* Send Disconnect command to the unit */ static int hci_disconnect(int s, int argc, char **argv) @@ -958,6 +1073,19 @@ &hci_read_clock_offset }, { +"request_authentication ", +"\nThis command allows to manually request authentication for a given unit.\n" \ +"Requires that the hcsecd daemon is running.\n" \ +"\t - dddd; connection handle", +&hci_request_authentication +}, +{ +"set_connection_encryption ", +"\nThis command allows to set the connection encryption for a given connection.\n" \ +"\t - dddd; connection handle" \ +"\tenable(1|0); enable or disable connection", +&hci_set_connection_encryption +}, +{ NULL, }}; - diff --git a/usr.sbin/bluetooth/hcsecd/hcsecd.h b/usr.sbin/bluetooth/hcsecd/hcsecd.h --- a/usr.sbin/bluetooth/hcsecd/hcsecd.h +++ b/usr.sbin/bluetooth/hcsecd/hcsecd.h @@ -45,6 +45,7 @@ char *name; /* remote device name */ uint8_t *key; /* link key (or NULL if no key) */ char *pin; /* pin (or NULL if no pin) */ + int autoaccept; /* automatically accept user confirm requests if non-zero */ LIST_ENTRY(link_key) next; /* link to the next */ }; typedef struct link_key link_key_t; diff --git a/usr.sbin/bluetooth/hcsecd/hcsecd.8 b/usr.sbin/bluetooth/hcsecd/hcsecd.8 --- a/usr.sbin/bluetooth/hcsecd/hcsecd.8 +++ b/usr.sbin/bluetooth/hcsecd/hcsecd.8 @@ -41,13 +41,16 @@ daemon controls link keys and PIN codes for Bluetooth devices. It opens a raw HCI socket and listens for .Dv Link_Key_Request , -.Dv PIN_Code_Request +.Dv PIN_Code_Request , +.Dv Link_Key_Notification , +.Dv IO_Capability_Request , and -.Dv Link_Key_Notification +.Dv User_Confirmation_Request HCI events. .Pp Once a -.Dv Link_Key_Request +.Dv Link_Key_Request , +.Dv User_Confirmation_Request or .Dv PIN_Code_Request HCI event is received, the daemon scans the configuration file for a @@ -69,6 +72,12 @@ (or .Dv PIN_Code_Request_Negative_Reply ) command will be sent back to the device. +If the request was a +.Dv User_Confirmation_Request +and the autoaccept option was set for the matching entry in the +configuration file, +.Nm +will accept the request. This is needed for Secure Simple Pairing. .Pp The .Nm diff --git a/usr.sbin/bluetooth/hcsecd/hcsecd.c b/usr.sbin/bluetooth/hcsecd/hcsecd.c --- a/usr.sbin/bluetooth/hcsecd/hcsecd.c +++ b/usr.sbin/bluetooth/hcsecd/hcsecd.c @@ -51,6 +51,10 @@ (int sock, struct sockaddr_hci *addr, bdaddr_p bdaddr); static int process_link_key_request_event (int sock, struct sockaddr_hci *addr, bdaddr_p bdaddr); +static int process_user_confirm_request + (int sock, struct sockaddr_hci *addr, bdaddr_p bdaddr); +static int process_io_capability_request + (int sock, struct sockaddr_hci *addr, bdaddr_p bdaddr); static int send_pin_code_reply (int sock, struct sockaddr_hci *addr, bdaddr_p bdaddr, char const *pin); static int send_link_key_reply @@ -126,6 +130,8 @@ bit_set(filter.event_mask, NG_HCI_EVENT_PIN_CODE_REQ - 1); bit_set(filter.event_mask, NG_HCI_EVENT_LINK_KEY_REQ - 1); bit_set(filter.event_mask, NG_HCI_EVENT_LINK_KEY_NOTIFICATION - 1); + bit_set(filter.event_mask, NG_HCI_EVENT_USER_CONFIRM_REQ - 1); + bit_set(filter.event_mask, NG_HCI_EVENT_IO_CAPABILITY_REQ - 1); if (setsockopt(sock, SOL_HCI_RAW, SO_HCI_RAW_FILTER, (void * const) &filter, sizeof(filter)) < 0) @@ -188,6 +194,16 @@ (ng_hci_link_key_notification_ep *)(event + 1)); break; + case NG_HCI_EVENT_USER_CONFIRM_REQ: + process_user_confirm_request(sock, &addr, + (bdaddr_p)(event + 1)); + break; + + case NG_HCI_EVENT_IO_CAPABILITY_REQ: + process_io_capability_request(sock, &addr, + (bdaddr_p)(event + 1)); + break; + default: syslog(LOG_ERR, "Received unexpected HCI event, " \ "event=%#x", event->event); @@ -415,7 +431,97 @@ return (0); } -/* Signal handlers */ +static int +process_user_confirm_request(int sock, struct sockaddr_hci *addr, bdaddr_p bdaddr) +{ + uint8_t buffer[HCSECD_BUFFER_SIZE] = {0}; + ng_hci_cmd_pkt_t *cmd = NULL; + ng_hci_user_confirm_rp *rp = NULL; + link_key_p key = NULL; + + syslog(LOG_DEBUG, "Got User Confirmation Request from '%s'\n", + addr->hci_node); + + /* prepare command */ + cmd = (ng_hci_cmd_pkt_t *) buffer; + cmd->type = NG_HCI_CMD_PKT; + cmd->length = sizeof(*rp); + rp = (ng_hci_user_confirm_rp *)(cmd + 1); + memcpy(&rp->bdaddr, bdaddr, sizeof(rp->bdaddr)); + + /* Now look if we should accept the command */ + if (((key = get_key(bdaddr, 1)) != NULL) && key->autoaccept) { + cmd->opcode = htole32(NG_HCI_OPCODE( + NG_HCI_OGF_LINK_CONTROL, + NG_HCI_OCF_USER_CONFIRM_REP)); + syslog(LOG_DEBUG, "Accepting User Confirmation Request from '%s'\n", + addr->hci_node); + } else { + cmd->opcode = htole32(NG_HCI_OPCODE( + NG_HCI_OGF_LINK_CONTROL, + NG_HCI_OCF_USER_CONFIRM_NEGATIVE_REP)); + syslog(LOG_ERR, + "No configuration for '%s' or missing autoaccept option. " + "Rejecting the user confirmation request.\n", + addr->hci_node); + } + +again: + if (sendto(sock, buffer, sizeof(*cmd) + cmd->length, 0, + (struct sockaddr *) addr, sizeof(*addr)) < 0) { + if (errno == EINTR) + goto again; + + syslog(LOG_ERR, "Could not send reply to user confirmation request to '%s' " \ + "for remote bdaddr %s. %s (%d)", + addr->hci_node, bt_ntoa(bdaddr, NULL), + strerror(errno), errno); + return (-1); + } + return (0); +} + +static int +process_io_capability_request(int sock, struct sockaddr_hci *addr, bdaddr_p bdaddr) +{ + uint8_t buffer[HCSECD_BUFFER_SIZE] = {0}; + ng_hci_cmd_pkt_t *cmd = NULL; + ng_hci_io_capability_request_rp *rp = NULL; + + syslog(LOG_DEBUG, "Got IO Capability Request from '%s'\n", + addr->hci_node); + + cmd = (ng_hci_cmd_pkt_t *) buffer; + cmd->type = NG_HCI_CMD_PKT; + cmd->opcode = htole32(NG_HCI_OPCODE( + NG_HCI_OGF_LINK_CONTROL, + NG_HCI_OCF_IO_CAPABILITY_REP)); + cmd->length = sizeof(*rp); + + rp = (ng_hci_io_capability_request_rp *)(cmd + 1); + memcpy(&rp->addr, bdaddr, sizeof(rp->addr)); + rp->io_cap = NG_HCI_IO_CAP_NO_INPUT_NO_OUTPUT; + rp->auth_requirements = + NG_HCI_AUTH_REQ_MITMPROT_REQ | + NG_HCI_AUTH_REQ_DEDICATED_BONDING | + NG_HCI_AUTH_REQ_USE_IO_CAPS; + +again: + if (sendto(sock, buffer, sizeof(*cmd) + cmd->length, 0, + (struct sockaddr *) addr, sizeof(*addr)) < 0) { + if (errno == EINTR) + goto again; + + syslog(LOG_ERR, "Could not send IO capability reply to '%s' " \ + "for remote bdaddr %s. %s (%d)", + addr->hci_node, bt_ntoa(bdaddr, NULL), + strerror(errno), errno); + return (-1); + } + return (0); +} + +/* SIGNAL handlers */ static void sighup(int s) { diff --git a/usr.sbin/bluetooth/hcsecd/hcsecd.conf b/usr.sbin/bluetooth/hcsecd/hcsecd.conf --- a/usr.sbin/bluetooth/hcsecd/hcsecd.conf +++ b/usr.sbin/bluetooth/hcsecd/hcsecd.conf @@ -17,6 +17,7 @@ # name "any char" ; - to set user friendly device name # key 0x11223344 | nokey ; - to set link key for the device # pin "secret" | nopin ; - to PIN code for the device +# autoaccept - to accept user confirmation requests # # Notes: # diff --git a/usr.sbin/bluetooth/hcsecd/hcsecd.conf.5 b/usr.sbin/bluetooth/hcsecd/hcsecd.conf.5 --- a/usr.sbin/bluetooth/hcsecd/hcsecd.conf.5 +++ b/usr.sbin/bluetooth/hcsecd/hcsecd.conf.5 @@ -94,6 +94,8 @@ PIN code is a string up to 16 characters in length in straight double quotes. .It Cm pin nopin Specify no PIN code for the entry. +.It Cm autoaccept +Set this to automatically accept user confirmation requests. .El .Sh EXAMPLES A sample @@ -124,6 +126,16 @@ key 0x00112233445566778899aabbccddeeff; # 16 bytes key pin nopin; } + +# A device that allows Secure Simple Pairing that we should +# automatically accept incoming user confirmation requests from. +device { + bdaddr f0:0f:de:ad:c0:de; + name "Some SSP capable Device"; + key nokey; + pin "0000"; + autoaccept; +} .Ed .Sh SEE ALSO .Xr hcsecd 8 diff --git a/usr.sbin/bluetooth/hcsecd/lexer.l b/usr.sbin/bluetooth/hcsecd/lexer.l --- a/usr.sbin/bluetooth/hcsecd/lexer.l +++ b/usr.sbin/bluetooth/hcsecd/lexer.l @@ -53,6 +53,7 @@ nokey_word nokey pin_word pin nopin_word nopin +autoaccept_word autoaccept bdaddrstring {hexbyte}:{hexbyte}:{hexbyte}:{hexbyte}:{hexbyte}:{hexbyte} hexstring 0x{hexbyte}+ @@ -76,6 +77,7 @@ {nokey_word} return (T_NOKEY); {pin_word} return (T_PIN); {nopin_word} return (T_NOPIN); +{autoaccept_word} return (T_AUTOACCEPT); {bdaddrstring} { yylval.string = yytext; diff --git a/usr.sbin/bluetooth/hcsecd/parser.y b/usr.sbin/bluetooth/hcsecd/parser.y --- a/usr.sbin/bluetooth/hcsecd/parser.y +++ b/usr.sbin/bluetooth/hcsecd/parser.y @@ -65,7 +65,8 @@ } %token T_BDADDRSTRING T_HEXSTRING T_STRING -%token T_DEVICE T_BDADDR T_NAME T_KEY T_PIN T_NOKEY T_NOPIN T_JUNK +%token T_DEVICE T_BDADDR T_NAME T_KEY T_PIN T_NOKEY +%token T_NOPIN T_AUTOACCEPT T_JUNK %% @@ -106,6 +107,7 @@ | name | key | pin + | autoaccept ; bdaddr: T_BDADDR T_BDADDRSTRING @@ -185,6 +187,11 @@ } ; +autoaccept: T_AUTOACCEPT + { + key->autoaccept = 1; + } + %% /* Display parser error message */