diff options
Diffstat (limited to 'hcid/security.c')
| -rw-r--r-- | hcid/security.c | 105 | 
1 files changed, 75 insertions, 30 deletions
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;  }  | 
