diff options
Diffstat (limited to 'hcid')
-rw-r--r-- | hcid/hcid.conf | 16 | ||||
-rw-r--r-- | hcid/hcid.h | 7 | ||||
-rw-r--r-- | hcid/kword.c | 8 | ||||
-rw-r--r-- | hcid/kword.h | 1 | ||||
-rw-r--r-- | hcid/main.c | 11 | ||||
-rw-r--r-- | hcid/parser.y | 20 | ||||
-rw-r--r-- | hcid/security.c | 105 |
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; } |