diff options
Diffstat (limited to 'usr.sbin/bluetooth/hcsecd/hcsecd.c')
| -rw-r--r-- | usr.sbin/bluetooth/hcsecd/hcsecd.c | 448 |
1 files changed, 448 insertions, 0 deletions
diff --git a/usr.sbin/bluetooth/hcsecd/hcsecd.c b/usr.sbin/bluetooth/hcsecd/hcsecd.c new file mode 100644 index 000000000000..824b0ba6971b --- /dev/null +++ b/usr.sbin/bluetooth/hcsecd/hcsecd.c @@ -0,0 +1,448 @@ +/*- + * hcsecd.c + * + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: hcsecd.c,v 1.6 2003/08/18 19:19:55 max Exp $ + */ + +#include <sys/queue.h> +#define L2CAP_SOCKET_CHECKED +#include <bluetooth.h> +#include <err.h> +#include <errno.h> +#include <signal.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> +#include "hcsecd.h" + +static int done = 0; + +static int process_pin_code_request_event + (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 send_pin_code_reply + (int sock, struct sockaddr_hci *addr, bdaddr_p bdaddr, char const *pin); +static int send_link_key_reply + (int sock, struct sockaddr_hci *addr, bdaddr_p bdaddr, uint8_t *key); +static int process_link_key_notification_event + (int sock, struct sockaddr_hci *addr, ng_hci_link_key_notification_ep *ep); +static void sighup + (int s); +static void sigint + (int s); +static void usage + (void); + +/* Main */ +int +main(int argc, char *argv[]) +{ + int n, detach, sock; + socklen_t size; + struct sigaction sa; + struct sockaddr_hci addr; + struct ng_btsocket_hci_raw_filter filter; + char buffer[HCSECD_BUFFER_SIZE]; + ng_hci_event_pkt_t *event = NULL; + + detach = 1; + + while ((n = getopt(argc, argv, "df:h")) != -1) { + switch (n) { + case 'd': + detach = 0; + break; + + case 'f': + config_file = optarg; + break; + + case 'h': + default: + usage(); + /* NOT REACHED */ + } + } + + if (config_file == NULL) + usage(); + /* NOT REACHED */ + + if (getuid() != 0) + errx(1, "** ERROR: You should run %s as privileged user!", + HCSECD_IDENT); + + /* Set signal handlers */ + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = sigint; + sa.sa_flags = SA_NOCLDWAIT; + if (sigaction(SIGINT, &sa, NULL) < 0) + err(1, "Could not sigaction(SIGINT)"); + if (sigaction(SIGTERM, &sa, NULL) < 0) + err(1, "Could not sigaction(SIGINT)"); + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = sighup; + if (sigaction(SIGHUP, &sa, NULL) < 0) + err(1, "Could not sigaction(SIGHUP)"); + + /* Open socket and set filter */ + sock = socket(PF_BLUETOOTH, SOCK_RAW, BLUETOOTH_PROTO_HCI); + if (sock < 0) + err(1, "Could not create HCI socket"); + + memset(&filter, 0, sizeof(filter)); + 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); + + if (setsockopt(sock, SOL_HCI_RAW, SO_HCI_RAW_FILTER, + (void * const) &filter, sizeof(filter)) < 0) + err(1, "Could not set HCI socket filter"); + + if (detach && daemon(0, 0) < 0) + err(1, "Could not daemon()ize"); + + openlog(HCSECD_IDENT, LOG_NDELAY|LOG_PERROR|LOG_PID, LOG_DAEMON); + + read_config_file(); + read_keys_file(); + + if (detach) { + FILE *pid = NULL; + + if ((pid = fopen(HCSECD_PIDFILE, "w")) == NULL) { + syslog(LOG_ERR, "Could not create PID file %s. %s (%d)", + HCSECD_PIDFILE, strerror(errno), errno); + exit(1); + } + + fprintf(pid, "%d", getpid()); + fclose(pid); + } + + event = (ng_hci_event_pkt_t *) buffer; + while (!done) { + size = sizeof(addr); + n = recvfrom(sock, buffer, sizeof(buffer), 0, + (struct sockaddr *) &addr, &size); + if (n < 0) { + if (errno == EINTR) + continue; + + syslog(LOG_ERR, "Could not receive from HCI socket. " \ + "%s (%d)", strerror(errno), errno); + exit(1); + } + + if (event->type != NG_HCI_EVENT_PKT) { + syslog(LOG_ERR, "Received unexpected HCI packet, " \ + "type=%#x", event->type); + continue; + } + + switch (event->event) { + case NG_HCI_EVENT_PIN_CODE_REQ: + process_pin_code_request_event(sock, &addr, + (bdaddr_p)(event + 1)); + break; + + case NG_HCI_EVENT_LINK_KEY_REQ: + process_link_key_request_event(sock, &addr, + (bdaddr_p)(event + 1)); + break; + + case NG_HCI_EVENT_LINK_KEY_NOTIFICATION: + process_link_key_notification_event(sock, &addr, + (ng_hci_link_key_notification_ep *)(event + 1)); + break; + + default: + syslog(LOG_ERR, "Received unexpected HCI event, " \ + "event=%#x", event->event); + break; + } + } + + if (detach) + if (remove(HCSECD_PIDFILE) < 0) + syslog(LOG_ERR, "Could not remove PID file %s. %s (%d)", + HCSECD_PIDFILE, strerror(errno), errno); + + dump_keys_file(); + clean_config(); + closelog(); + close(sock); + + return (0); +} + +/* Process PIN_Code_Request event */ +static int +process_pin_code_request_event(int sock, struct sockaddr_hci *addr, + bdaddr_p bdaddr) +{ + link_key_p key = NULL; + + syslog(LOG_DEBUG, "Got PIN_Code_Request event from '%s', " \ + "remote bdaddr %s", addr->hci_node, + bt_ntoa(bdaddr, NULL)); + + if ((key = get_key(bdaddr, 0)) != NULL) { + syslog(LOG_DEBUG, "Found matching entry, " \ + "remote bdaddr %s, name '%s', PIN code %s", + bt_ntoa(&key->bdaddr, NULL), + (key->name != NULL)? key->name : "No name", + (key->pin != NULL)? "exists" : "doesn't exist"); + + return (send_pin_code_reply(sock, addr, bdaddr, key->pin)); + } + + syslog(LOG_DEBUG, "Could not PIN code for remote bdaddr %s", + bt_ntoa(bdaddr, NULL)); + + return (send_pin_code_reply(sock, addr, bdaddr, NULL)); +} + +/* Process Link_Key_Request event */ +static int +process_link_key_request_event(int sock, struct sockaddr_hci *addr, + bdaddr_p bdaddr) +{ + link_key_p key = NULL; + + syslog(LOG_DEBUG, "Got Link_Key_Request event from '%s', " \ + "remote bdaddr %s", addr->hci_node, + bt_ntoa(bdaddr, NULL)); + + if ((key = get_key(bdaddr, 0)) != NULL) { + syslog(LOG_DEBUG, "Found matching entry, " \ + "remote bdaddr %s, name '%s', link key %s", + bt_ntoa(&key->bdaddr, NULL), + (key->name != NULL)? key->name : "No name", + (key->key != NULL)? "exists" : "doesn't exist"); + + return (send_link_key_reply(sock, addr, bdaddr, key->key)); + } + + syslog(LOG_DEBUG, "Could not find link key for remote bdaddr %s", + bt_ntoa(bdaddr, NULL)); + + return (send_link_key_reply(sock, addr, bdaddr, NULL)); +} + +/* Send PIN_Code_[Negative]_Reply */ +static int +send_pin_code_reply(int sock, struct sockaddr_hci *addr, + bdaddr_p bdaddr, char const *pin) +{ + uint8_t buffer[HCSECD_BUFFER_SIZE]; + ng_hci_cmd_pkt_t *cmd = NULL; + + memset(buffer, 0, sizeof(buffer)); + + cmd = (ng_hci_cmd_pkt_t *) buffer; + cmd->type = NG_HCI_CMD_PKT; + + if (pin != NULL) { + ng_hci_pin_code_rep_cp *cp = NULL; + + cmd->opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL, + NG_HCI_OCF_PIN_CODE_REP)); + cmd->length = sizeof(*cp); + + cp = (ng_hci_pin_code_rep_cp *)(cmd + 1); + memcpy(&cp->bdaddr, bdaddr, sizeof(cp->bdaddr)); + strncpy((char *) cp->pin, pin, sizeof(cp->pin)); + cp->pin_size = strlen((char const *) cp->pin); + + syslog(LOG_DEBUG, "Sending PIN_Code_Reply to '%s' " \ + "for remote bdaddr %s", + addr->hci_node, bt_ntoa(bdaddr, NULL)); + } else { + ng_hci_pin_code_neg_rep_cp *cp = NULL; + + cmd->opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL, + NG_HCI_OCF_PIN_CODE_NEG_REP)); + cmd->length = sizeof(*cp); + + cp = (ng_hci_pin_code_neg_rep_cp *)(cmd + 1); + memcpy(&cp->bdaddr, bdaddr, sizeof(cp->bdaddr)); + + syslog(LOG_DEBUG, "Sending PIN_Code_Negative_Reply to '%s' " \ + "for remote bdaddr %s", + addr->hci_node, bt_ntoa(bdaddr, NULL)); + } + +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 PIN code reply to '%s' " \ + "for remote bdaddr %s. %s (%d)", + addr->hci_node, bt_ntoa(bdaddr, NULL), + strerror(errno), errno); + return (-1); + } + + return (0); +} + +/* Send Link_Key_[Negative]_Reply */ +static int +send_link_key_reply(int sock, struct sockaddr_hci *addr, + bdaddr_p bdaddr, uint8_t *key) +{ + uint8_t buffer[HCSECD_BUFFER_SIZE]; + ng_hci_cmd_pkt_t *cmd = NULL; + + memset(buffer, 0, sizeof(buffer)); + + cmd = (ng_hci_cmd_pkt_t *) buffer; + cmd->type = NG_HCI_CMD_PKT; + + if (key != NULL) { + ng_hci_link_key_rep_cp *cp = NULL; + + cmd->opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL, + NG_HCI_OCF_LINK_KEY_REP)); + cmd->length = sizeof(*cp); + + cp = (ng_hci_link_key_rep_cp *)(cmd + 1); + memcpy(&cp->bdaddr, bdaddr, sizeof(cp->bdaddr)); + memcpy(&cp->key, key, sizeof(cp->key)); + + syslog(LOG_DEBUG, "Sending Link_Key_Reply to '%s' " \ + "for remote bdaddr %s", + addr->hci_node, bt_ntoa(bdaddr, NULL)); + } else { + ng_hci_link_key_neg_rep_cp *cp = NULL; + + cmd->opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL, + NG_HCI_OCF_LINK_KEY_NEG_REP)); + cmd->length = sizeof(*cp); + + cp = (ng_hci_link_key_neg_rep_cp *)(cmd + 1); + memcpy(&cp->bdaddr, bdaddr, sizeof(cp->bdaddr)); + + syslog(LOG_DEBUG, "Sending Link_Key_Negative_Reply to '%s' " \ + "for remote bdaddr %s", + addr->hci_node, bt_ntoa(bdaddr, NULL)); + } + +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 link key reply to '%s' " \ + "for remote bdaddr %s. %s (%d)", + addr->hci_node, bt_ntoa(bdaddr, NULL), + strerror(errno), errno); + return (-1); + } + + return (0); +} + +/* Process Link_Key_Notification event */ +static int +process_link_key_notification_event(int sock, struct sockaddr_hci *addr, + ng_hci_link_key_notification_ep *ep) +{ + link_key_p key = NULL; + + syslog(LOG_DEBUG, "Got Link_Key_Notification event from '%s', " \ + "remote bdaddr %s", addr->hci_node, + bt_ntoa(&ep->bdaddr, NULL)); + + if ((key = get_key(&ep->bdaddr, 1)) == NULL) { + syslog(LOG_ERR, "Could not find entry for remote bdaddr %s", + bt_ntoa(&ep->bdaddr, NULL)); + return (-1); + } + + syslog(LOG_DEBUG, "Updating link key for the entry, " \ + "remote bdaddr %s, name '%s', link key %s", + bt_ntoa(&key->bdaddr, NULL), + (key->name != NULL)? key->name : "No name", + (key->key != NULL)? "exists" : "doesn't exist"); + + if (key->key == NULL) { + key->key = (uint8_t *) malloc(NG_HCI_KEY_SIZE); + if (key->key == NULL) { + syslog(LOG_ERR, "Could not allocate link key"); + exit(1); + } + } + + memcpy(key->key, &ep->key, NG_HCI_KEY_SIZE); + + return (0); +} + +/* Signal handlers */ +static void +sighup(int s) +{ + syslog(LOG_DEBUG, "Got SIGHUP (%d)", s); + + dump_keys_file(); + read_config_file(); + read_keys_file(); +} + +static void +sigint(int s) +{ + syslog(LOG_DEBUG, "Got signal %d, total number of signals %d", + s, ++ done); +} + +/* Display usage and exit */ +static void +usage(void) +{ + fprintf(stderr, +"Usage: %s [-d] -f config_file [-h]\n" \ +"Where:\n" \ +"\t-d do not detach from terminal\n" \ +"\t-f config_file use <config_file>\n" \ +"\t-h display this message\n", HCSECD_IDENT); + + exit(255); +} + |
