⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 security.c

📁 Bluezan implementation of the Bluetooth&#8482 wireless standards specifications for Linux. The code
💻 C
字号:
/* * *  BlueZ - Bluetooth protocol stack for Linux * *  Copyright (C) 2000-2001  Qualcomm Incorporated *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com> *  Copyright (C) 2002-2005  Marcel Holtmann <marcel@holtmann.org> * * *  This program is free software; you can redistribute it and/or modify *  it under the terms of the GNU General Public License version 2 as *  published by the Free Software Foundation; * *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS *  OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. *  IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY *  CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES  *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN  *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF  *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * *  ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,  *  COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS  *  SOFTWARE IS DISCLAIMED. * * *  $Id: security.c,v 1.28 2005/04/30 18:46:01 holtmann Exp $ */#ifdef HAVE_CONFIG_H#include <config.h>#endif#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <signal.h>#include <fcntl.h>#include <syslog.h>#include <errno.h>#include <time.h>#include <fcntl.h>#include <time.h>#include <sys/ioctl.h>#include <sys/socket.h>#include <sys/time.h>#include <sys/stat.h>#include <sys/types.h>#include <bluetooth/bluetooth.h>#include <bluetooth/hci.h>#include <bluetooth/hci_lib.h>#include "glib-ectomy.h"#include "hcid.h"#include "lib.h"static GIOChannel *io_chan[HCI_MAX_DEV];static int pairing;void toggle_pairing(int enable){	if (enable)		pairing = hcid.pairing;	else		pairing = 0;	syslog(LOG_INFO, "Pairing %s", pairing ? "enabled" : "disabled");}static int get_bdaddr(int dev, bdaddr_t *sba, uint16_t handle, bdaddr_t *dba){	struct hci_conn_list_req *cl;	struct hci_conn_info *ci;	char addr[18];	int i;	cl = malloc(10 * sizeof(*ci) + sizeof(*cl));	if (!cl)		return -ENOMEM;	ba2str(sba, addr);	cl->dev_id = hci_devid(addr);	cl->conn_num = 10;	ci = cl->conn_info;	if (ioctl(dev, HCIGETCONNLIST, (void *) cl) < 0) {		free(cl);		return -EIO;	}	for (i = 0; i < cl->conn_num; i++, ci++)		if (ci->handle == handle) {			bacpy(dba, &ci->bdaddr);			free(cl);			return 0;		}	free(cl);	return -ENOENT;}/* Link Key handling *//* This function is not reentrable */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 r;	while ((r = read_n(f, &k, sizeof(k)))) {		if (r < 0) {			syslog(LOG_ERR, "Link key database read failed: %s (%d)",							strerror(errno), errno);			break;		}		if (!bacmp(&k.sba, sba) && !bacmp(&k.dba, dba)) {			key = &k;			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;}static void link_key_request(int dev, bdaddr_t *sba, bdaddr_t *dba){	unsigned char key[16];	char sa[18], da[18];	int err;	ba2str(sba, sa); ba2str(dba, da);	syslog(LOG_INFO, "link_key_request (sba=%s, dba=%s)", sa, da);	err = read_link_key(sba, dba, key);	if (err < 0) {		struct link_key *linkkey = get_link_key(sba, dba);		if (linkkey) {			memcpy(key, linkkey->key, 16);			linkkey->time = time(0);			err = 0;		}	}	if (err < 0) {		/* Link key not found */		hci_send_cmd(dev, OGF_LINK_CTL, OCF_LINK_KEY_NEG_REPLY, 6, dba);	} else {		/* Link key found */		link_key_reply_cp lr;		memcpy(lr.link_key, key, 16);		bacpy(&lr.bdaddr, dba);		hci_send_cmd(dev, OGF_LINK_CTL, OCF_LINK_KEY_REPLY,						LINK_KEY_REPLY_CP_SIZE, &lr);	}}#if 0static void save_link_key(struct link_key *key){	struct link_key *exist;	char sa[18], da[18];	int f, err;	f = open(hcid.key_file, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);	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);	}	ba2str(&key->sba, sa); ba2str(&key->dba, da);	syslog(LOG_INFO, "%s link key %s %s", exist ? "Replacing" : "Saving", sa, da);failed:	close(f);}#endifstatic void link_key_notify(int dev, bdaddr_t *sba, void *ptr){	evt_link_key_notify *evt = ptr;	bdaddr_t *dba = &evt->bdaddr;	struct link_key key;	char sa[18], da[18];	ba2str(sba, sa); ba2str(dba, da);	syslog(LOG_INFO, "link_key_notify (sba=%s, dba=%s)", sa, da);	memcpy(key.key, evt->link_key, 16);	bacpy(&key.sba, sba);	bacpy(&key.dba, dba);	key.type = evt->key_type;	key.time = time(0);#if 0	save_link_key(&key);#endif	write_link_key(sba, dba, evt->link_key, evt->key_type);}static void return_link_keys(int dev, bdaddr_t *sba, void *ptr){	evt_return_link_keys *evt = ptr;	uint8_t num = evt->num_keys;	unsigned char key[16];	char sa[18], da[18];	bdaddr_t dba;	int i;	ba2str(sba, sa);	ptr++;	for (i = 0; i < num; i++) {		bacpy(&dba, ptr); ba2str(&dba, da);		memcpy(key, ptr + 6, 16);		syslog(LOG_INFO, "return_link_keys (sba=%s, dba=%s)", sa, da);		ptr += 22;	}}/* PIN code handling */static int read_default_pin_code(void){	char buf[17];	FILE *f; 	int len;	if (!(f = fopen(hcid.pin_file, "r"))) {		syslog(LOG_ERR, "Can't open PIN file %s: %s (%d)",					hcid.pin_file, strerror(errno), errno);		return -1;	}	if (fgets(buf, sizeof(buf), f)) {		strtok(buf, "\n\r");		len = strlen(buf); 		memcpy(hcid.pin_code, buf, len);		hcid.pin_len = len;	} else {		syslog(LOG_ERR, "Can't read PIN file %s: %s (%d)",					hcid.pin_file, strerror(errno), errno);		len = -1;	}	fclose(f);	return len;}/*  PIN helper is an external app that asks user for a PIN. It can   implement its own PIN  code generation policy and methods like  PIN look up in some database, etc.   HCId expects following output from PIN helper:	PIN:12345678	-	PIN code	ERR		-	No PIN available*/static void call_pin_helper(int dev, bdaddr_t *sba, struct hci_conn_info *ci){	pin_code_reply_cp pr;	struct sigaction sa;	char addr[18], str[255], *pin, name[249];	FILE *pipe;	int ret, len;	/* Run PIN helper in the separate process */	switch (fork()) {		case 0:			break;		case -1:			syslog(LOG_ERR, "Can't fork PIN helper: %s (%d)",							strerror(errno), errno);		default:			return;	}	if (access(hcid.pin_helper, R_OK | X_OK)) {		syslog(LOG_ERR, "Can't exec PIN helper %s: %s (%d)",					hcid.pin_helper, strerror(errno), errno);		goto reject;	}	memset(name, 0, sizeof(name));	read_device_name(sba, &ci->bdaddr, name);	//hci_remote_name(dev, &ci->bdaddr, sizeof(name), name, 0);	ba2str(&ci->bdaddr, addr);	snprintf(str, sizeof(str), "%s %s %s \'%s\'", hcid.pin_helper,					ci->out ? "out" : "in", addr, name);	setenv("PATH", "/bin:/usr/bin:/usr/local/bin", 1);	memset(&sa, 0, sizeof(sa));	sa.sa_flags = SA_NOCLDSTOP;	sa.sa_handler = SIG_DFL;	sigaction(SIGCHLD, &sa, NULL);	pipe = popen(str, "r");	if (!pipe) {		syslog(LOG_ERR, "Can't exec PIN helper: %s (%d)",							strerror(errno), errno);		goto reject;	}	pin = fgets(str, sizeof(str), pipe);	ret = pclose(pipe);	if (!pin || strlen(pin) < 5)		goto nopin;	strtok(pin, "\n\r");	if (strncmp("PIN:", pin, 4))		goto nopin;	pin += 4;	len  = strlen(pin);	memset(&pr, 0, sizeof(pr));	bacpy(&pr.bdaddr, &ci->bdaddr);	memcpy(pr.pin_code, pin, len);	pr.pin_len = len;	hci_send_cmd(dev, OGF_LINK_CTL, OCF_PIN_CODE_REPLY,			PIN_CODE_REPLY_CP_SIZE, &pr);	exit(0);nopin:	if (!pin || strncmp("ERR", pin, 3))		syslog(LOG_ERR, "PIN helper exited abnormally with code %d", ret);reject:	hci_send_cmd(dev, OGF_LINK_CTL, OCF_PIN_CODE_NEG_REPLY, 6, &ci->bdaddr);	exit(0);}static void request_pin(int dev, bdaddr_t *sba, struct hci_conn_info *ci){#ifdef ENABLE_DBUS	if (hcid.dbus_pin_helper) {		hcid_dbus_request_pin(dev, ci);		return;	}#endif	call_pin_helper(dev, sba, ci);}static void pin_code_request(int dev, bdaddr_t *sba, bdaddr_t *dba){	pin_code_reply_cp pr;	struct hci_conn_info_req *cr;	struct hci_conn_info *ci;	char sa[18], da[18], pin[17];	int pinlen;	memset(&pr, 0, sizeof(pr));	bacpy(&pr.bdaddr, dba);	ba2str(sba, sa); ba2str(dba, da);	syslog(LOG_INFO, "pin_code_request (sba=%s, dba=%s)", sa, da);	cr = malloc(sizeof(*cr) + sizeof(*ci));	if (!cr)		return;	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);		goto reject;	}	ci = cr->conn_info;	memset(pin, 0, sizeof(pin));	pinlen = read_pin_code(sba, dba, pin);	if (pairing == HCID_PAIRING_ONCE) {		struct link_key *key = get_link_key(sba, dba);		if (key) {			ba2str(dba, da);			syslog(LOG_WARNING, "PIN code request for already paired device %s", da);			goto reject;		}	} else if (pairing == HCID_PAIRING_NONE)		goto reject;	if (hcid.security == HCID_SEC_AUTO) {		if (!ci->out) {			/* Incomming connection */			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,				PIN_CODE_REPLY_CP_SIZE, &pr);		} else {			/* Outgoing connection */			if (pinlen > 0) {				memcpy(pr.pin_code, pin, pinlen);				pr.pin_len = pinlen;				hci_send_cmd(dev, OGF_LINK_CTL, OCF_PIN_CODE_REPLY,					PIN_CODE_REPLY_CP_SIZE, &pr);			} else {				/* Let PIN helper handle that */ 				request_pin(dev, sba, ci);			}		}	} else {		/* Let PIN helper handle that */ 		request_pin(dev, sba, ci);	}	free(cr);	return;reject:	hci_send_cmd(dev, OGF_LINK_CTL, OCF_PIN_CODE_NEG_REPLY, 6, dba);	free(cr);	return;}static void remote_name_information(int dev, bdaddr_t *sba, void *ptr){	evt_remote_name_req_complete *evt = ptr;	bdaddr_t *dba = &evt->bdaddr;	if (evt->status)		return;	write_device_name(sba, dba, evt->name);}static void remote_version_information(int dev, bdaddr_t *sba, void *ptr){	evt_read_remote_version_complete *evt = ptr;	bdaddr_t dba;	if (evt->status)		return;	if (get_bdaddr(dev, sba, btohs(evt->handle), &dba) < 0)		return;	write_version_info(sba, &dba, btohs(evt->manufacturer),				evt->lmp_ver, btohs(evt->lmp_subver));}static void remote_features_information(int dev, bdaddr_t *sba, void *ptr){	evt_read_remote_features_complete *evt = ptr;	bdaddr_t dba;	if (evt->status)		return;	if (get_bdaddr(dev, sba, btohs(evt->handle), &dba) < 0)		return;	write_features_info(sba, &dba, evt->features);}static gboolean io_security_event(GIOChannel *chan, GIOCondition cond, gpointer data){	unsigned char buf[HCI_MAX_EVENT_SIZE], *ptr = buf;	struct hci_dev_info *di = (void *) data;	int type, dev;	size_t len;	hci_event_hdr *eh;	GIOError err;	if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) {		g_io_channel_close(chan);		free(data);		return FALSE;	}	if ((err = g_io_channel_read(chan, buf, sizeof(buf), &len))) {		if (err == G_IO_ERROR_AGAIN)			return TRUE;		g_io_channel_close(chan);		free(data);		return FALSE;	}	type = *ptr++;	if (type != HCI_EVENT_PKT)		return TRUE;	eh = (hci_event_hdr *) ptr;	ptr += HCI_EVENT_HDR_SIZE;	dev = g_io_channel_unix_get_fd(chan);	ioctl(dev, HCIGETDEVINFO, (void *) di);	if (hci_test_bit(HCI_RAW, &di->flags))		return TRUE;	switch (eh->evt) {	case EVT_REMOTE_NAME_REQ_COMPLETE:		remote_name_information(dev, &di->bdaddr, ptr);		break;	case EVT_READ_REMOTE_VERSION_COMPLETE:		remote_version_information(dev, &di->bdaddr, ptr);		break;	case EVT_READ_REMOTE_FEATURES_COMPLETE:		remote_features_information(dev, &di->bdaddr, ptr);		break;	}	if (hci_test_bit(HCI_SECMGR, &di->flags))		return TRUE;	switch (eh->evt) {	case EVT_PIN_CODE_REQ:		pin_code_request(dev, &di->bdaddr, (bdaddr_t *) ptr);		break;	case EVT_LINK_KEY_REQ:		link_key_request(dev, &di->bdaddr, (bdaddr_t *) ptr);		break;	case EVT_LINK_KEY_NOTIFY:		link_key_notify(dev, &di->bdaddr, ptr);		break;	case EVT_RETURN_LINK_KEYS:		return_link_keys(dev, &di->bdaddr, ptr);		break;	}	return TRUE;}void start_security_manager(int hdev){	GIOChannel *chan = io_chan[hdev];	struct hci_dev_info *di;	struct hci_filter flt;	read_stored_link_key_cp cp;	int dev;	if (chan)		return;	syslog(LOG_INFO, "Starting security manager %d", hdev);	if ((dev = hci_open_dev(hdev)) < 0) {		syslog(LOG_ERR, "Can't open device hci%d: %s (%d)",						hdev, strerror(errno), errno);		return;	}	/* Set filter */	hci_filter_clear(&flt);	hci_filter_set_ptype(HCI_EVENT_PKT, &flt);	hci_filter_set_event(EVT_PIN_CODE_REQ, &flt);	hci_filter_set_event(EVT_LINK_KEY_REQ, &flt);	hci_filter_set_event(EVT_LINK_KEY_NOTIFY, &flt);	hci_filter_set_event(EVT_RETURN_LINK_KEYS, &flt);	hci_filter_set_event(EVT_REMOTE_NAME_REQ_COMPLETE, &flt);	hci_filter_set_event(EVT_READ_REMOTE_VERSION_COMPLETE, &flt);	hci_filter_set_event(EVT_READ_REMOTE_FEATURES_COMPLETE, &flt);	if (setsockopt(dev, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) {		syslog(LOG_ERR, "Can't set filter on hci%d: %s (%d)",						hdev, strerror(errno), errno);		close(dev);		return;	}	di = malloc(sizeof(*di));	if (!di) {		syslog(LOG_ERR, "Can't allocate device info buffer: %s (%d)",							strerror(errno), errno);		close(dev);		return;	}	di->dev_id = hdev;	if (ioctl(dev, HCIGETDEVINFO, (void *)di)) {		syslog(LOG_ERR, "Can't get device info: %s (%d)",							strerror(errno), errno);		close(dev);		return;	}	chan = g_io_channel_unix_new(dev);	g_io_add_watch(chan, G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR,			io_security_event, (void *) di);	io_chan[hdev] = chan;	bacpy(&cp.bdaddr, BDADDR_ANY);	cp.read_all = 1;	hci_send_cmd(dev, OGF_HOST_CTL, OCF_READ_STORED_LINK_KEY,			READ_STORED_LINK_KEY_CP_SIZE, (void *) &cp);}void stop_security_manager(int hdev){	GIOChannel *chan = io_chan[hdev];	if (!chan)		return;	syslog(LOG_INFO, "Stoping security manager %d", hdev);	/* this is a bit sneaky. closing the fd will cause the event	   loop to call us right back with G_IO_NVAL set, at which	   point we will see it and clean things up */	close(g_io_channel_unix_get_fd(chan));	io_chan[hdev] = NULL;}void init_security_data(void){	/* Set local PIN code */	if (read_default_pin_code() < 0) {		strcpy(hcid.pin_code, "BlueZ");		hcid.pin_len = 5;	}	pairing = hcid.pairing;	return;}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -