/*-
* 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);
}