summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--hcid/hcid.conf16
-rw-r--r--hcid/hcid.h7
-rw-r--r--hcid/kword.c8
-rw-r--r--hcid/kword.h1
-rw-r--r--hcid/main.c11
-rw-r--r--hcid/parser.y20
-rw-r--r--hcid/security.c105
7 files changed, 129 insertions, 39 deletions
diff --git a/hcid/hcid.conf b/hcid/hcid.conf
index 784e1d97..89645993 100644
--- a/hcid/hcid.conf
+++ b/hcid/hcid.conf
@@ -6,16 +6,22 @@
# HCId options
options {
- # Automaticaly initialize new devices
+ # Automatically initialize new devices
autoinit yes;
# Security Manager mode
# none - Security manager disabled
- # auto - Use local PIN for incomming connections
+ # auto - Use local PIN for incoming connections
# user - Always ask user for a PIN
#
security auto;
+ # Pairing mode
+ # none - Pairing disabled
+ # multi - Allow pairing with already paired devices
+ # once - Pair once and deny successive attempts
+ pairing multi;
+
# PIN helper
pin_helper /bin/bluepin;
}
@@ -38,9 +44,9 @@ device {
# Default link mode
# none - no specific policy
- # accept - always accept incomming connections
- # master - become master on incomming connections,
- # deny role switch on outgoint connections
+ # accept - always accept incoming connections
+ # master - become master on incoming connections,
+ # deny role switch on outgoing connections
#
#lm accept,master;
#
diff --git a/hcid/hcid.h b/hcid/hcid.h
index 0715db28..cfc034e4 100644
--- a/hcid/hcid.h
+++ b/hcid/hcid.h
@@ -59,6 +59,7 @@ struct hcid_opts {
char *host_name;
int auto_init;
int security;
+ int pairing;
char *config_file;
@@ -77,6 +78,10 @@ extern struct hcid_opts hcid;
#define HCID_SEC_AUTO 1
#define HCID_SEC_USER 2
+#define HCID_PAIRING_NONE 0
+#define HCID_PAIRING_MULTI 1
+#define HCID_PAIRING_ONCE 2
+
int read_config(char *file);
gboolean io_stack_event(GIOChannel *chan, GIOCondition cond, gpointer data);
@@ -85,4 +90,4 @@ gboolean io_security_event(GIOChannel *chan, GIOCondition cond, gpointer data);
void init_security_data(void);
void start_security_manager(int hdev);
void stop_security_manager(int hdev);
-void flush_link_keys(void);
+void toggle_pairing(int enable);
diff --git a/hcid/kword.c b/hcid/kword.c
index cc69db39..0486f21b 100644
--- a/hcid/kword.c
+++ b/hcid/kword.c
@@ -37,6 +37,7 @@ struct kword cfg_keyword[] = {
{ "device", K_DEVICE },
{ "autoinit", K_AUTOINIT },
{ "security", K_SECURITY },
+ { "pairing", K_PAIRING },
{ "pkt_type", K_PTYPE },
{ "lm", K_LM },
{ "lp", K_LP },
@@ -62,6 +63,13 @@ struct kword sec_param[] = {
{ NULL , 0 }
};
+struct kword pair_param[] = {
+ { "none", HCID_PAIRING_NONE },
+ { "multi", HCID_PAIRING_MULTI },
+ { "once", HCID_PAIRING_ONCE },
+ { NULL , 0 }
+};
+
int lineno;
int find_keyword(struct kword *kw, char *str)
diff --git a/hcid/kword.h b/hcid/kword.h
index 854c91d0..10c54493 100644
--- a/hcid/kword.h
+++ b/hcid/kword.h
@@ -32,5 +32,6 @@ extern int lineno;
extern struct kword cfg_keyword[];
extern struct kword sec_param[];
+extern struct kword pair_param[];
int find_keyword(struct kword *kw, char *str);
diff --git a/hcid/main.c b/hcid/main.c
index 37e6cf65..8a5371e5 100644
--- a/hcid/main.c
+++ b/hcid/main.c
@@ -249,7 +249,12 @@ static void init_defaults(void)
static void sig_usr1(int sig)
{
- flush_link_keys();
+ toggle_pairing(0);
+}
+
+static void sig_usr2(int sig)
+{
+ toggle_pairing(1);
}
static void sig_term(int sig)
@@ -357,6 +362,8 @@ int main(int argc, char *argv[], char *env[])
/* Default HCId settings */
hcid.config_file = HCID_CONFIG_FILE;
hcid.host_name = get_host_name();
+ hcid.security = HCID_SEC_AUTO;
+ hcid.pairing = HCID_PAIRING_MULTI;
hcid.pin_file = strdup(HCID_PIN_FILE);
hcid.pin_helper = strdup(HCID_PIN_HELPER);
@@ -412,6 +419,8 @@ int main(int argc, char *argv[], char *env[])
sigaction(SIGHUP, &sa, NULL);
sa.sa_handler = sig_usr1;
sigaction(SIGUSR1, &sa, NULL);
+ sa.sa_handler = sig_usr2;
+ sigaction(SIGUSR2, &sa, NULL);
sa.sa_handler = SIG_IGN;
sigaction(SIGCHLD, &sa, NULL);
diff --git a/hcid/parser.y b/hcid/parser.y
index ff2cd1c6..b7614f85 100644
--- a/hcid/parser.y
+++ b/hcid/parser.y
@@ -56,7 +56,7 @@ int yyerror(char *s);
}
%token K_OPTIONS K_DEVICE
-%token K_AUTOINIT K_SECURITY
+%token K_AUTOINIT K_SECURITY K_PAIRING
%token K_PTYPE K_NAME K_CLASS K_LM K_LP K_AUTH K_ENCRYPT K_ISCAN K_PSCAN
%token K_PINHELP
%token K_YES K_NO
@@ -64,7 +64,7 @@ int yyerror(char *s);
%token <str> WORD PATH STRING LIST
%token <num> NUM
-%type <num> bool pkt_type link_mode link_policy sec_mode
+%type <num> bool pkt_type link_mode link_policy sec_mode pair_mode
%type <str> dev_name
%%
@@ -93,6 +93,10 @@ hcid_opt:
hcid.security = $2;
}
+ | K_PAIRING pair_mode {
+ hcid.pairing = $2;
+ }
+
| K_PINHELP PATH {
if (hcid.pin_helper)
free(hcid.pin_helper);
@@ -117,6 +121,18 @@ sec_mode:
| K_NO { $$ = HCID_SEC_NONE; }
;
+pair_mode:
+ WORD {
+ int opt = find_keyword(pair_param, $1);
+ if (opt < 0) {
+ cfg_error("Unknown pairing mode '%s'", $1);
+ $$ = 0;
+ } else
+ $$ = opt;
+ }
+ ;
+
+
device_options: '{' device_opts '}'
device_opts: | device_opt ';' | error ';' | device_opts device_opt ';';
device_opt:
diff --git a/hcid/security.c b/hcid/security.c
index 75e44421..b7839c4c 100644
--- a/hcid/security.c
+++ b/hcid/security.c
@@ -53,29 +53,27 @@
static GIOChannel *io_chan[HCI_MAX_DEV];
-/* Link Key handling */
+static int pairing;
-void flush_link_keys(void)
+void toggle_pairing(int enable)
{
- syslog(LOG_INFO, "Flushing link key database");
- truncate(hcid.key_file, 0);
+ if (enable)
+ pairing = hcid.pairing;
+ else
+ pairing = 0;
+
+ syslog(LOG_INFO, "Pairing %s", pairing ? "enabled" : "disabled");
}
+/* Link Key handling */
+
/* This function is not reentrable */
-static struct link_key *get_link_key(bdaddr_t *sba, bdaddr_t *dba)
+static struct link_key *__get_link_key(int f, bdaddr_t *sba, bdaddr_t *dba)
{
static struct link_key k;
struct link_key *key = NULL;
- int f, r;
-
- f = open(hcid.key_file, O_RDONLY);
- if (f < 0) {
- if (errno != ENOENT)
- syslog(LOG_ERR, "Link key database open failed. %s(%d)",
- strerror(errno), errno);
- return NULL;
- }
-
+ int r;
+
while ((r = read_n(f, &k, sizeof(k)))) {
if (r < 0) {
syslog(LOG_ERR, "Link key database read failed. %s(%d)",
@@ -88,7 +86,20 @@ static struct link_key *get_link_key(bdaddr_t *sba, bdaddr_t *dba)
break;
}
}
-
+ return key;
+}
+
+static struct link_key *get_link_key(bdaddr_t *sba, bdaddr_t *dba)
+{
+ struct link_key *key = NULL;
+ int f;
+
+ f = open(hcid.key_file, O_RDONLY);
+ if (f >= 0)
+ key = __get_link_key(f, sba, dba);
+ else if (errno != ENOENT)
+ syslog(LOG_ERR, "Link key database open failed. %s(%d)",
+ strerror(errno), errno);
close(f);
return key;
}
@@ -114,24 +125,43 @@ static void link_key_request(int dev, bdaddr_t *sba, bdaddr_t *dba)
static void save_link_key(struct link_key *key)
{
char sa[40], da[40];
- int f;
+ struct link_key *exist;
+ int f, err;
- f = open(hcid.key_file, O_WRONLY | O_CREAT | O_APPEND, 0);
+ f = open(hcid.key_file, O_RDWR | O_CREAT, 0);
if (f < 0) {
syslog(LOG_ERR, "Link key database open failed. %s(%d)",
strerror(errno), errno);
return;
}
+ /* Check if key already exist */
+ exist = __get_link_key(f, &key->sba, &key->dba);
+
+ err = 0;
+
+ if (exist) {
+ off_t o = lseek(f, 0, SEEK_CUR);
+ err = lseek(f, o - sizeof(*key), SEEK_SET);
+ } else
+ err = fcntl(f, F_SETFL, O_APPEND);
+
+ if (err < 0) {
+ syslog(LOG_ERR, "Link key database seek failed. %s(%d)",
+ strerror(errno), errno);
+ goto failed;
+ }
+
if (write_n(f, key, sizeof(*key)) < 0) {
syslog(LOG_ERR, "Link key database write failed. %s(%d)",
strerror(errno), errno);
}
- close(f);
-
ba2str(&key->sba, sa); ba2str(&key->dba, da);
- syslog(LOG_INFO, "Saving link key %s %s", sa, da);
+ syslog(LOG_INFO, "%s link key %s %s", exist ? "Replacing" : "Saving", sa, da);
+
+failed:
+ close(f);
}
static void link_key_notify(int dev, bdaddr_t *sba, void *ptr)
@@ -254,7 +284,7 @@ reject:
exit(0);
}
-static void pin_code_request(int dev, bdaddr_t *ba)
+static void pin_code_request(int dev, bdaddr_t *sba, bdaddr_t *dba)
{
struct hci_conn_info_req *cr;
struct hci_conn_info *ci;
@@ -263,25 +293,32 @@ static void pin_code_request(int dev, bdaddr_t *ba)
if (!cr)
return;
- bacpy(&cr->bdaddr, ba);
+ bacpy(&cr->bdaddr, dba);
cr->type = ACL_LINK;
if (ioctl(dev, HCIGETCONNINFO, (unsigned long) cr) < 0) {
syslog(LOG_ERR, "Can't get conn info %s(%d)",
strerror(errno), errno);
- /* Reject PIN */
- hci_send_cmd(dev, OGF_LINK_CTL, OCF_PIN_CODE_NEG_REPLY, 6, ba);
-
- free(cr);
- return;
+ goto reject;
}
ci = cr->conn_info;
+ if (pairing == HCID_PAIRING_ONCE) {
+ struct link_key *key = get_link_key(sba, dba);
+ if (key) {
+ char ba[40];
+ ba2str(dba, ba);
+ syslog(LOG_WARNING, "PIN code request for already paired device %s", ba);
+ goto reject;
+ }
+ } else if (pairing == HCID_PAIRING_NONE)
+ goto reject;
+
if (hcid.security == HCID_SEC_AUTO) {
if (!ci->out) {
/* Incomming connection */
pin_code_reply_cp pr;
memset(&pr, 0, sizeof(pr));
- bacpy(&pr.bdaddr, ba);
+ bacpy(&pr.bdaddr, dba);
memcpy(pr.pin_code, hcid.pin_code, hcid.pin_len);
pr.pin_len = hcid.pin_len;
hci_send_cmd(dev, OGF_LINK_CTL, OCF_PIN_CODE_REPLY,
@@ -297,6 +334,12 @@ static void pin_code_request(int dev, bdaddr_t *ba)
call_pin_helper(dev, ci);
}
free(cr);
+ return;
+
+reject:
+ hci_send_cmd(dev, OGF_LINK_CTL, OCF_PIN_CODE_NEG_REPLY, 6, dba);
+ free(cr);
+ return;
}
gboolean io_security_event(GIOChannel *chan, GIOCondition cond, gpointer data)
@@ -337,7 +380,7 @@ gboolean io_security_event(GIOChannel *chan, GIOCondition cond, gpointer data)
switch (eh->evt) {
case EVT_PIN_CODE_REQ:
- pin_code_request(dev, (bdaddr_t *) ptr);
+ pin_code_request(dev, &di->bdaddr, (bdaddr_t *) ptr);
break;
case EVT_LINK_KEY_REQ:
@@ -426,5 +469,7 @@ void init_security_data(void)
strcpy(hcid.pin_code, "BlueZ");
hcid.pin_len = 5;
}
+
+ pairing = hcid.pairing;
return;
}