summaryrefslogtreecommitdiffstats
path: root/hcid
diff options
context:
space:
mode:
Diffstat (limited to 'hcid')
-rw-r--r--hcid/Makefile.am28
-rw-r--r--hcid/Makefile.in362
-rw-r--r--hcid/hcid.conf63
-rw-r--r--hcid/hcid.h92
-rw-r--r--hcid/kword.c75
-rw-r--r--hcid/kword.h36
-rw-r--r--hcid/lexer.l129
-rw-r--r--hcid/lib.c171
-rw-r--r--hcid/lib.h85
-rw-r--r--hcid/main.c430
-rw-r--r--hcid/parser.y272
-rw-r--r--hcid/security.c477
12 files changed, 2220 insertions, 0 deletions
diff --git a/hcid/Makefile.am b/hcid/Makefile.am
new file mode 100644
index 00000000..edc4f653
--- /dev/null
+++ b/hcid/Makefile.am
@@ -0,0 +1,28 @@
+#
+# $Id$
+#
+
+sbin_PROGRAMS = hcid
+
+hcid_SOURCES = main.c security.c hcid.h lib.c lib.h parser.y lexer.l kword.c
+hcid_LDADD = @GLIB_LDFLAGS@
+
+CFLAGS += @GLIB_CFLAGS@
+YFLAGS += -d
+
+CLEANFILES = lexer.c parser.c parser.h
+
+confdir = $(prefix)/etc/bluetooth
+conf_FILE = $(confdir)/hcid.conf
+pin_FILE = $(confdir)/pin
+
+#
+# Install configuration files
+#
+install-data-local:
+ $(mkinstalldirs) $(DESTDIR)$(confdir)
+ [ -f $(DESTDIR)$(conf_FILE) ] || \
+ $(INSTALL_DATA) $(srcdir)/hcid.conf $(DESTDIR)$(conf_FILE)
+ [ -f $(DESTDIR)$(pin_FILE) ] || \
+ echo "BlueZ" > $(DESTDIR)$(pin_FILE); \
+ chmod 600 $(DESTDIR)$(pin_FILE)
diff --git a/hcid/Makefile.in b/hcid/Makefile.in
new file mode 100644
index 00000000..037379a9
--- /dev/null
+++ b/hcid/Makefile.in
@@ -0,0 +1,362 @@
+# Makefile.in generated automatically by automake 1.4-p5 from Makefile.am
+
+# Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+#
+# $Id$
+#
+
+
+SHELL = @SHELL@
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+
+bindir = @bindir@
+sbindir = @sbindir@
+libexecdir = @libexecdir@
+datadir = @datadir@
+sysconfdir = @sysconfdir@
+sharedstatedir = @sharedstatedir@
+localstatedir = @localstatedir@
+libdir = @libdir@
+infodir = @infodir@
+mandir = @mandir@
+includedir = @includedir@
+oldincludedir = /usr/include
+
+DESTDIR =
+
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+
+top_builddir = ..
+
+ACLOCAL = @ACLOCAL@
+AUTOCONF = @AUTOCONF@
+AUTOMAKE = @AUTOMAKE@
+AUTOHEADER = @AUTOHEADER@
+
+INSTALL = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS)
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+transform = @program_transform_name@
+
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_alias = @build_alias@
+build_triplet = @build@
+host_alias = @host_alias@
+host_triplet = @host@
+target_alias = @target_alias@
+target_triplet = @target@
+AR = @AR@
+AWK = @AWK@
+CC = @CC@
+DISTRO = @DISTRO@
+GLIB = @GLIB@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_LDFLAGS = @GLIB_LDFLAGS@
+LD = @LD@
+LEX = @LEX@
+MAKEINFO = @MAKEINFO@
+PACKAGE = @PACKAGE@
+PCMCIA = @PCMCIA@
+VERSION = @VERSION@
+YACC = @YACC@
+
+sbin_PROGRAMS = hcid
+
+hcid_SOURCES = main.c security.c hcid.h lib.c lib.h parser.y lexer.l kword.c
+hcid_LDADD = @GLIB_LDFLAGS@
+
+CFLAGS = @GLIB_CFLAGS@
+YFLAGS = -d
+
+CLEANFILES = lexer.c parser.c parser.h
+
+confdir = $(prefix)/etc/bluetooth
+conf_FILE = $(confdir)/hcid.conf
+pin_FILE = $(confdir)/pin
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_CLEAN_FILES =
+PROGRAMS = $(sbin_PROGRAMS)
+
+
+DEFS = @DEFS@ -I. -I$(srcdir)
+CPPFLAGS = @CPPFLAGS@
+LDFLAGS = @LDFLAGS@
+LIBS = @LIBS@
+hcid_OBJECTS = main.o security.o lib.o parser.o lexer.o kword.o
+hcid_DEPENDENCIES =
+hcid_LDFLAGS =
+LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@
+LEXLIB = @LEXLIB@
+COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@
+DIST_COMMON = Makefile.am Makefile.in lexer.c parser.c
+
+
+DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST)
+
+TAR = gtar
+GZIP_ENV = --best
+DEP_FILES = .deps/kword.P .deps/lexer.P .deps/lib.P .deps/main.P \
+.deps/parser.P .deps/security.P
+SOURCES = $(hcid_SOURCES)
+OBJECTS = $(hcid_OBJECTS)
+
+all: all-redirect
+.SUFFIXES:
+.SUFFIXES: .S .c .l .o .s .y
+$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4)
+ cd $(top_srcdir) && $(AUTOMAKE) --gnu hcid/Makefile
+
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status $(BUILT_SOURCES)
+ cd $(top_builddir) \
+ && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status
+
+
+mostlyclean-sbinPROGRAMS:
+
+clean-sbinPROGRAMS:
+ -test -z "$(sbin_PROGRAMS)" || rm -f $(sbin_PROGRAMS)
+
+distclean-sbinPROGRAMS:
+
+maintainer-clean-sbinPROGRAMS:
+
+install-sbinPROGRAMS: $(sbin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ $(mkinstalldirs) $(DESTDIR)$(sbindir)
+ @list='$(sbin_PROGRAMS)'; for p in $$list; do \
+ if test -f $$p; then \
+ echo " $(INSTALL_PROGRAM) $$p $(DESTDIR)$(sbindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`"; \
+ $(INSTALL_PROGRAM) $$p $(DESTDIR)$(sbindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \
+ else :; fi; \
+ done
+
+uninstall-sbinPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ list='$(sbin_PROGRAMS)'; for p in $$list; do \
+ rm -f $(DESTDIR)$(sbindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \
+ done
+
+.s.o:
+ $(COMPILE) -c $<
+
+.S.o:
+ $(COMPILE) -c $<
+
+mostlyclean-compile:
+ -rm -f *.o core *.core
+
+clean-compile:
+
+distclean-compile:
+ -rm -f *.tab.c
+
+maintainer-clean-compile:
+
+hcid: $(hcid_OBJECTS) $(hcid_DEPENDENCIES)
+ @rm -f hcid
+ $(LINK) $(hcid_LDFLAGS) $(hcid_OBJECTS) $(hcid_LDADD) $(LIBS)
+.l.c:
+ $(LEX) $(AM_LFLAGS) $(LFLAGS) $< && mv $(LEX_OUTPUT_ROOT).c $@
+.y.c:
+ $(YACC) $(AM_YFLAGS) $(YFLAGS) $< && mv y.tab.c $*.c
+ if test -f y.tab.h; then \
+ if cmp -s y.tab.h $*.h; then rm -f y.tab.h; else mv y.tab.h $*.h; fi; \
+ else :; fi
+parser.h: parser.c
+
+
+tags: TAGS
+
+ID: $(HEADERS) $(SOURCES) $(LISP)
+ list='$(SOURCES) $(HEADERS)'; \
+ unique=`for i in $$list; do echo $$i; done | \
+ awk ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ here=`pwd` && cd $(srcdir) \
+ && mkid -f$$here/ID $$unique $(LISP)
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS)'; \
+ unique=`for i in $$list; do echo $$i; done | \
+ awk ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \
+ || (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags $$unique $(LISP) -o $$here/TAGS)
+
+mostlyclean-tags:
+
+clean-tags:
+
+distclean-tags:
+ -rm -f TAGS ID
+
+maintainer-clean-tags:
+
+distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir)
+
+subdir = hcid
+
+distdir: $(DISTFILES)
+ here=`cd $(top_builddir) && pwd`; \
+ top_distdir=`cd $(top_distdir) && pwd`; \
+ distdir=`cd $(distdir) && pwd`; \
+ cd $(top_srcdir) \
+ && $(AUTOMAKE) --include-deps --build-dir=$$here --srcdir-name=$(top_srcdir) --output-dir=$$top_distdir --gnu hcid/Makefile
+ @for file in $(DISTFILES); do \
+ d=$(srcdir); \
+ if test -d $$d/$$file; then \
+ cp -pr $$d/$$file $(distdir)/$$file; \
+ else \
+ test -f $(distdir)/$$file \
+ || ln $$d/$$file $(distdir)/$$file 2> /dev/null \
+ || cp -p $$d/$$file $(distdir)/$$file || :; \
+ fi; \
+ done
+
+DEPS_MAGIC := $(shell mkdir .deps > /dev/null 2>&1 || :)
+
+-include $(DEP_FILES)
+
+mostlyclean-depend:
+
+clean-depend:
+
+distclean-depend:
+ -rm -rf .deps
+
+maintainer-clean-depend:
+
+%.o: %.c
+ @echo '$(COMPILE) -c $<'; \
+ $(COMPILE) -Wp,-MD,.deps/$(*F).pp -c $<
+ @-cp .deps/$(*F).pp .deps/$(*F).P; \
+ tr ' ' '\012' < .deps/$(*F).pp \
+ | sed -e 's/^\\$$//' -e '/^$$/ d' -e '/:$$/ d' -e 's/$$/ :/' \
+ >> .deps/$(*F).P; \
+ rm .deps/$(*F).pp
+
+%.lo: %.c
+ @echo '$(LTCOMPILE) -c $<'; \
+ $(LTCOMPILE) -Wp,-MD,.deps/$(*F).pp -c $<
+ @-sed -e 's/^\([^:]*\)\.o[ ]*:/\1.lo \1.o :/' \
+ < .deps/$(*F).pp > .deps/$(*F).P; \
+ tr ' ' '\012' < .deps/$(*F).pp \
+ | sed -e 's/^\\$$//' -e '/^$$/ d' -e '/:$$/ d' -e 's/$$/ :/' \
+ >> .deps/$(*F).P; \
+ rm -f .deps/$(*F).pp
+info-am:
+info: info-am
+dvi-am:
+dvi: dvi-am
+check-am: all-am
+check: check-am
+installcheck-am:
+installcheck: installcheck-am
+install-exec-am: install-sbinPROGRAMS
+install-exec: install-exec-am
+
+install-data-am: install-data-local
+install-data: install-data-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+install: install-am
+uninstall-am: uninstall-sbinPROGRAMS
+uninstall: uninstall-am
+all-am: Makefile $(PROGRAMS)
+all-redirect: all-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install
+installdirs:
+ $(mkinstalldirs) $(DESTDIR)$(sbindir)
+
+
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -rm -f Makefile $(CONFIG_CLEAN_FILES)
+ -rm -f config.cache config.log stamp-h stamp-h[0-9]*
+
+maintainer-clean-generic:
+ -test -z "lexerlparserhparserc" || rm -f lexerl parserh parserc
+mostlyclean-am: mostlyclean-sbinPROGRAMS mostlyclean-compile \
+ mostlyclean-tags mostlyclean-depend mostlyclean-generic
+
+mostlyclean: mostlyclean-am
+
+clean-am: clean-sbinPROGRAMS clean-compile clean-tags clean-depend \
+ clean-generic mostlyclean-am
+
+clean: clean-am
+
+distclean-am: distclean-sbinPROGRAMS distclean-compile distclean-tags \
+ distclean-depend distclean-generic clean-am
+
+distclean: distclean-am
+
+maintainer-clean-am: maintainer-clean-sbinPROGRAMS \
+ maintainer-clean-compile maintainer-clean-tags \
+ maintainer-clean-depend maintainer-clean-generic \
+ distclean-am
+ @echo "This command is intended for maintainers to use;"
+ @echo "it deletes files that may require special tools to rebuild."
+
+maintainer-clean: maintainer-clean-am
+
+.PHONY: mostlyclean-sbinPROGRAMS distclean-sbinPROGRAMS \
+clean-sbinPROGRAMS maintainer-clean-sbinPROGRAMS uninstall-sbinPROGRAMS \
+install-sbinPROGRAMS mostlyclean-compile distclean-compile \
+clean-compile maintainer-clean-compile tags mostlyclean-tags \
+distclean-tags clean-tags maintainer-clean-tags distdir \
+mostlyclean-depend distclean-depend clean-depend \
+maintainer-clean-depend info-am info dvi-am dvi check check-am \
+installcheck-am installcheck install-exec-am install-exec \
+install-data-local install-data-am install-data install-am install \
+uninstall-am uninstall all-redirect all-am all installdirs \
+mostlyclean-generic distclean-generic clean-generic \
+maintainer-clean-generic clean mostlyclean distclean maintainer-clean
+
+
+#
+# Install configuration files
+#
+install-data-local:
+ $(mkinstalldirs) $(DESTDIR)$(confdir)
+ [ -f $(DESTDIR)$(conf_FILE) ] || \
+ $(INSTALL_DATA) $(srcdir)/hcid.conf $(DESTDIR)$(conf_FILE)
+ [ -f $(DESTDIR)$(pin_FILE) ] || \
+ echo "BlueZ" > $(DESTDIR)$(pin_FILE); \
+ chmod 600 $(DESTDIR)$(pin_FILE)
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/hcid/hcid.conf b/hcid/hcid.conf
new file mode 100644
index 00000000..cfd06e5f
--- /dev/null
+++ b/hcid/hcid.conf
@@ -0,0 +1,63 @@
+#
+# HCI daemon configuration file.
+#
+# $Id$
+#
+
+# HCId options
+options {
+ # Automaticaly initialize new devices
+ autoinit yes;
+
+ # Security Manager mode
+ # none - Security manager disabled
+ # auto - Use local PIN for incomming connections
+ # user - Always ask user for a PIN
+ #
+ security auto;
+
+ # PIN helper
+ pin_helper /bin/bluepin;
+}
+
+# Default settings for HCI devices
+device {
+ # Local device name
+ # %d - device id
+ # %h - host name
+ name "BlueZ (%d)";
+
+ # Local device class
+ class 0x100;
+
+ # Default packet type
+ pkt_type DH1,DM1,HV1;
+
+ # Inquiry and Page scan
+ iscan enable; pscan enable;
+
+ # Default link mode
+ # none - no specific policy
+ # accept - always accept incomming connections
+ # master - become master on incomming connections,
+ # deny role switch on outgoint connections
+ #
+ #lm accept,master;
+ #
+ lm accept;
+
+ # Default link policy
+ # none - no specific policy
+ # rswitch - allow role switch
+ # hold - allow hold mode
+ # sniff - allow sniff mode
+ # park - allow park mode
+ #
+ #lp hold,sniff;
+ #
+ lp hold,sniff,park;
+
+ # Authentication and Encryption
+ #auth enable;
+ #encrypt enable;
+}
diff --git a/hcid/hcid.h b/hcid/hcid.h
new file mode 100644
index 00000000..677a02dd
--- /dev/null
+++ b/hcid/hcid.h
@@ -0,0 +1,92 @@
+/*
+ BlueZ - Bluetooth protocol stack for Linux
+ Copyright (C) 2000-2001 Qualcomm Incorporated
+
+ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+
+ 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$
+ */
+
+#include <sys/types.h>
+
+#include <glib.h>
+
+#include <bluetooth.h>
+
+#define HCID_CONFIG_FILE "/etc/bluetooth/hcid.conf"
+#define HCID_PIN_FILE "/etc/bluetooth/pin"
+#define HCID_KEY_FILE "/etc/bluetooth/link_key"
+#define HCID_PIN_HELPER "/bin/bluepin"
+#define HCID_KEY_NUM 20
+#define HCID_KEY_TTL 172800 /* 2 days */
+
+struct device_opts {
+ char *name;
+ uint32_t class;
+ uint16_t pkt_type;
+ uint16_t scan;
+ uint16_t link_mode;
+ uint16_t link_policy;
+ uint16_t auth;
+ uint16_t encrypt;
+};
+extern struct device_opts devi;
+
+struct link_key {
+ bdaddr_t sba;
+ bdaddr_t dba;
+ uint8_t key[16];
+ uint8_t type;
+ time_t time;
+};
+
+struct hcid_opts {
+ char *host_name;
+ int auto_init;
+ int security;
+
+ char *config_file;
+
+ uint8_t pin_code[16];
+ int pin_len;
+ char *pin_helper;
+ char *pin_file;
+
+ struct link_key **link_key;
+ int key_num;
+ char *key_file;
+
+ int sock;
+};
+extern struct hcid_opts hcid;
+
+#define HCID_SEC_NONE 0
+#define HCID_SEC_AUTO 1
+#define HCID_SEC_USER 2
+
+int read_config(char *file);
+
+gboolean io_stack_event(GIOChannel *chan, GIOCondition cond, gpointer data);
+gboolean io_security_event(GIOChannel *chan, GIOCondition cond, gpointer data);
+
+void start_security_manager(int hdev);
+void stop_security_manager(int hdev);
+void save_link_keys(void);
+void flush_link_keys(void);
diff --git a/hcid/kword.c b/hcid/kword.c
new file mode 100644
index 00000000..a496591f
--- /dev/null
+++ b/hcid/kword.c
@@ -0,0 +1,75 @@
+/*
+ BlueZ - Bluetooth protocol stack for Linux
+ Copyright (C) 2000-2001 Qualcomm Incorporated
+
+ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+
+ 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$
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "hcid.h"
+#include "kword.h"
+#include "parser.h"
+
+struct kword cfg_keyword[] = {
+ { "options", K_OPTIONS },
+ { "default", K_DEVICE },
+ { "device", K_DEVICE },
+ { "autoinit", K_AUTOINIT },
+ { "security", K_SECURITY },
+ { "pkt_type", K_PTYPE },
+ { "lm", K_LM },
+ { "lp", K_LP },
+ { "iscan", K_ISCAN },
+ { "pscan", K_PSCAN },
+ { "name", K_NAME },
+ { "class", K_CLASS },
+ { "auth", K_AUTH },
+ { "encrypt", K_ENCRYPT },
+ { "pin_helper", K_PINHELP },
+
+ { "yes", K_YES },
+ { "no", K_NO },
+ { "enable", K_YES },
+ { "disable", K_NO },
+ { NULL , 0 }
+};
+
+struct kword sec_param[] = {
+ { "none", HCID_SEC_NONE },
+ { "auto", HCID_SEC_AUTO },
+ { "user", HCID_SEC_USER },
+ { NULL , 0 }
+};
+
+int lineno;
+
+int find_keyword(struct kword *kw, char *str)
+{
+ while( kw->str ){
+ if( !strcmp(str,kw->str) )
+ return kw->type;
+ kw++;
+ }
+ return -1;
+}
diff --git a/hcid/kword.h b/hcid/kword.h
new file mode 100644
index 00000000..854c91d0
--- /dev/null
+++ b/hcid/kword.h
@@ -0,0 +1,36 @@
+/*
+ BlueZ - Bluetooth protocol stack for Linux
+ Copyright (C) 2000-2001 Qualcomm Incorporated
+
+ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+
+ 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$
+ */
+
+struct kword {
+ char *str;
+ int type;
+};
+extern int lineno;
+
+extern struct kword cfg_keyword[];
+extern struct kword sec_param[];
+
+int find_keyword(struct kword *kw, char *str);
diff --git a/hcid/lexer.l b/hcid/lexer.l
new file mode 100644
index 00000000..a7af9c63
--- /dev/null
+++ b/hcid/lexer.l
@@ -0,0 +1,129 @@
+%{
+/*
+ BlueZ - Bluetooth protocol stack for Linux
+ Copyright (C) 2000-2001 Qualcomm Incorporated
+
+ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+
+ 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$
+ */
+
+#include <string.h>
+
+#include "hcid.h"
+#include "kword.h"
+#include "parser.h"
+
+static char str_buf[255];
+
+#define ECHO {;}
+#define YY_DECL int yylex(void)
+
+int cfg_error(const char *ftm, ...);
+int yyerror(char *str);
+
+%}
+
+hex 0x[0-9a-zA-Z]+
+num [0-9]+
+kword [A-Za-z0-9\_\-]+
+word [A-Za-z0-9\-\_+=\!\$\#\%\&\*\^\@@\\\~\.]+
+wordnm {word}:{num}
+list ({word}\,*)+
+comment \#.*\n
+fname [A-Za-z0-9\_\.\-]+
+path (\/{fname})+
+string \".*\"
+
+%x OPTION PARAM
+
+%%
+[ \t] {
+ /* Skip spaces and tabs */
+ ;
+}
+
+{comment} {
+ /* Skip comments */
+ lineno++;
+}
+
+\n {
+ lineno++;
+}
+
+{hex} {
+ yylval.num = strtol(yytext, NULL, 16);
+ return NUM;
+}
+
+{num} {
+ yylval.num = atoi(yytext);
+ return NUM;
+}
+
+{kword} {
+ int kw = find_keyword(cfg_keyword, yytext);
+ if( kw != -1 )
+ return kw;
+
+ yylval.str = yytext;
+ return WORD;
+}
+
+{word} {
+ yylval.str = yytext;
+ return WORD;
+}
+
+{string} {
+ if(yyleng > sizeof(str_buf)-1){
+ yyerror("string too long");
+ return 0;
+ }
+
+ strncpy(str_buf, yytext+1, yyleng-2);
+ str_buf[yyleng-2] = '\0';
+
+ yylval.str = str_buf;
+ return STRING;
+}
+
+{list} {
+ yylval.str = yytext;
+ return LIST;
+}
+
+{path} {
+ yylval.str = yytext;
+ return PATH;
+}
+
+. {
+ return *yytext;
+}
+
+%%
+
+int yywrap(void)
+{
+ return 1;
+}
diff --git a/hcid/lib.c b/hcid/lib.c
new file mode 100644
index 00000000..ac47603d
--- /dev/null
+++ b/hcid/lib.c
@@ -0,0 +1,171 @@
+/*
+ BlueZ - Bluetooth protocol stack for Linux
+ Copyright (C) 2000-2001 Qualcomm Incorporated
+
+ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+
+ 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$
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <signal.h>
+
+#include "hcid.h"
+#include "lib.h"
+
+volatile sig_atomic_t __io_canceled;
+
+/*
+ * Device name expansion
+ * %d - device id
+ */
+char * expand_name(char *dst, char *str, int dev_id)
+{
+ register int sp, np, olen;
+ char *opt, buf[10];
+
+ if (!str && !dst)
+ return NULL;
+
+ sp = np = 0;
+ while (str[sp]) {
+ switch (str[sp]) {
+ case '%':
+ opt = NULL;
+
+ switch (str[sp+1]) {
+ case 'd':
+ sprintf(buf, "%d", dev_id);
+ opt = buf;
+ break;
+
+ case 'h':
+ opt = hcid.host_name;
+ break;
+
+ case '%':
+ dst[np++] = str[sp++];
+ /* fall through */
+ default:
+ sp++;
+ continue;
+ }
+
+ if (opt) {
+ /* substitute */
+ olen = strlen(opt);
+ memcpy(dst + np, opt, olen);
+ np += olen;
+ }
+ sp += 2;
+ continue;
+
+ case '\\':
+ sp++;
+ /* fall through */
+ default:
+ dst[np++] = str[sp++];
+ break;
+ }
+ }
+ dst[np] = '\0';
+ return dst;
+}
+
+/* Returns current host name */
+char * get_host_name(void)
+{
+ char name[40];
+
+ if (!gethostname(name, sizeof(name)-1)) {
+ name[sizeof(name)-1] = 0;
+ return strdup(name);
+ }
+ return strdup("noname");
+}
+
+/* Functions to manipulate program title */
+extern char **environ;
+char *title_start; /* start of the proc title space */
+char *title_end; /* end of the proc title space */
+int title_size;
+
+void init_title(int argc, char *argv[], char *envp[], const char *name)
+{
+ int i;
+
+ /*
+ * Move the environment so settitle can use the space at
+ * the top of memory.
+ */
+
+ for (i = 0; envp[i]; i++);
+
+ environ = (char **) malloc(sizeof (char *) * (i + 1));
+
+ for (i = 0; envp[i]; i++)
+ environ[i] = strdup(envp[i]);
+ environ[i] = NULL;
+
+ /*
+ * Save start and extent of argv for set_title.
+ */
+
+ title_start = argv[0];
+
+ /*
+ * Determine how much space we can use for set_title.
+ * Use all contiguous argv and envp pointers starting at argv[0]
+ */
+ for (i=0; i<argc; i++)
+ if (!i || title_end == argv[i])
+ title_end = argv[i] + strlen(argv[i]) + 1;
+
+ for (i=0; envp[i]; i++)
+ if (title_end == envp[i])
+ title_end = envp[i] + strlen(envp[i]) + 1;
+
+ strcpy(title_start, name);
+ title_start += strlen(name);
+ title_size = title_end - title_start;
+}
+
+void set_title(const char *fmt, ...)
+{
+ char buf[255];
+ va_list ap;
+
+ memset(title_start,0,title_size);
+
+ /* print the argument string */
+ va_start(ap, fmt);
+ vsprintf(buf, fmt, ap);
+ va_end(ap);
+
+ if (strlen(buf) > title_size - 1)
+ buf[title_size - 1] = '\0';
+
+ strcat(title_start, buf);
+}
diff --git a/hcid/lib.h b/hcid/lib.h
new file mode 100644
index 00000000..4683c0ff
--- /dev/null
+++ b/hcid/lib.h
@@ -0,0 +1,85 @@
+/*
+ BlueZ - Bluetooth protocol stack for Linux
+ Copyright (C) 2000-2001 Qualcomm Incorporated
+
+ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+
+ 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$
+ */
+
+#include <errno.h>
+
+char * expand_name(char *dst, char *str, int dev_id);
+
+char * get_host_name(void);
+
+void init_title(int argc, char *argv[], char *env[], const char *name);
+void set_title(const char *ftm, ...);
+
+/* IO cancelation */
+extern volatile sig_atomic_t __io_canceled;
+
+static inline void io_init(void)
+{
+ __io_canceled = 0;
+}
+
+static inline void io_cancel(void)
+{
+ __io_canceled = 1;
+}
+
+/* Read exactly len bytes (Signal safe)*/
+static inline int read_n(int fd, void *buf, int len)
+{
+ register int t=0, w;
+
+ while (!__io_canceled && len > 0) {
+ if( (w = read(fd, buf, len)) < 0 ){
+ if( errno == EINTR || errno == EAGAIN )
+ continue;
+ return -1;
+ }
+ if( !w )
+ return 0;
+ len -= w; buf += w; t += w;
+ }
+
+ return t;
+}
+
+/* Write exactly len bytes (Signal safe)*/
+static inline int write_n(int fd, void *buf, int len)
+{
+ register int t=0, w;
+
+ while (!__io_canceled && len > 0) {
+ if( (w = write(fd, buf, len)) < 0 ){
+ if( errno == EINTR || errno == EAGAIN )
+ continue;
+ return -1;
+ }
+ if( !w )
+ return 0;
+ len -= w; buf += w; t += w;
+ }
+
+ return t;
+}
diff --git a/hcid/main.c b/hcid/main.c
new file mode 100644
index 00000000..5b3c3d30
--- /dev/null
+++ b/hcid/main.c
@@ -0,0 +1,430 @@
+/*
+ BlueZ - Bluetooth protocol stack for Linux
+ Copyright (C) 2000-2001 Qualcomm Incorporated
+
+ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+
+ 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$
+ */
+
+#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 <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <asm/types.h>
+
+#include <bluetooth.h>
+#include <hci.h>
+#include <hci_lib.h>
+
+#include <glib.h>
+
+#include "hcid.h"
+#include "lib.h"
+
+#define VERSION "1.1"
+
+struct hcid_opts hcid;
+struct device_opts devi;
+
+static GMainLoop *event_loop;
+
+gboolean io_stack_event(GIOChannel *chan, GIOCondition cond, gpointer data);
+gboolean io_security_event(GIOChannel *chan, GIOCondition cond, gpointer data);
+
+static void usage(void)
+{
+ printf("hcid - HCI daemon ver %s\n", VERSION);
+ printf("Usage: \n");
+ printf("\thcid [-n not_daemon] [-f config file]\n");
+}
+
+static void init_device(int hdev)
+{
+ struct hci_dev_req dr;
+ int s;
+
+ /* Do initialization in the separate process */
+ switch (fork()) {
+ case 0:
+ break;
+ case -1:
+ syslog(LOG_ERR, "Fork failed. Can't init device hci%d. %s(%d)\n",
+ hdev, strerror(errno), errno);
+ default:
+ return;
+ }
+
+ set_title("hci%d init", hdev);
+
+ if ((s = hci_open_dev(hdev)) < 0) {
+ syslog(LOG_ERR, "Can't open device hci%d. %s(%d)\n", hdev, strerror(errno), errno);
+ exit(1);
+ }
+
+ /* Start HCI device */
+ if (ioctl(s, HCIDEVUP, hdev) < 0 && errno != EALREADY) {
+ syslog(LOG_ERR, "Can't init device hci%d. %s(%d)\n", hdev,
+ strerror(errno), errno);
+ exit(1);
+ }
+
+ dr.dev_id = hdev;
+
+ /* Set packet type */
+ if (devi.pkt_type) {
+ dr.dev_opt = devi.pkt_type;
+ if (ioctl(s, HCISETPTYPE, (unsigned long)&dr) < 0) {
+ syslog(LOG_ERR, "Can't set packet type on hci%d. %s(%d)\n",
+ hdev, strerror(errno), errno);
+ }
+ }
+
+ /* Set link mode */
+ if (devi.link_mode) {
+ dr.dev_opt = devi.link_mode;
+ if (ioctl(s, HCISETLINKMODE, (unsigned long)&dr) < 0) {
+ syslog(LOG_ERR, "Can't set link mode on hci%d. %s(%d)\n",
+ hdev, strerror(errno), errno);
+ }
+ }
+
+ /* Set link policy */
+ if (devi.link_policy) {
+ dr.dev_opt = devi.link_policy;
+ if (ioctl(s, HCISETLINKPOL, (unsigned long)&dr) < 0) {
+ syslog(LOG_ERR, "Can't set link policy on hci%d. %s(%d)\n",
+ hdev, strerror(errno), errno);
+ }
+ }
+
+ /* Set scan mode */
+ dr.dev_opt = devi.scan;
+ if (ioctl(s, HCISETSCAN, (unsigned long)&dr) < 0) {
+ syslog(LOG_ERR, "Can't set scan mode on hci%d. %s(%d)\n",
+ hdev, strerror(errno), errno);
+ }
+
+ /* Set authentication */
+ if (devi.auth)
+ dr.dev_opt = AUTH_ENABLED;
+ else
+ dr.dev_opt = AUTH_DISABLED;
+
+ if (ioctl(s, HCISETAUTH, (unsigned long)&dr) < 0) {
+ syslog(LOG_ERR, "Can't set auth on hci%d. %s(%d)\n",
+ hdev, strerror(errno), errno);
+ }
+
+ /* Set encryption */
+ if (devi.encrypt)
+ dr.dev_opt = ENCRYPT_P2P;
+ else
+ dr.dev_opt = ENCRYPT_DISABLED;
+
+ if (ioctl(s, HCISETENCRYPT, (unsigned long)&dr) < 0) {
+ syslog(LOG_ERR, "Can't set encrypt on hci%d. %s(%d)\n",
+ hdev, strerror(errno), errno);
+ }
+
+ /* Set device class */
+ if (devi.class) {
+ uint32_t class = htobl(devi.class);
+ write_class_of_dev_cp cp;
+
+ memcpy(cp.dev_class, &class, 3);
+ hci_send_cmd(s, OGF_HOST_CTL, OCF_WRITE_CLASS_OF_DEV,
+ WRITE_CLASS_OF_DEV_CP_SIZE, (void *) &cp);
+ }
+
+ /* Set device name */
+ if (devi.name) {
+ change_local_name_cp cp;
+ expand_name(cp.name, devi.name, hdev);
+
+ hci_send_cmd(s, OGF_HOST_CTL, OCF_CHANGE_LOCAL_NAME,
+ CHANGE_LOCAL_NAME_CP_SIZE, (void *) &cp);
+ }
+
+ exit(0);
+}
+
+static void init_all_devices(int ctl)
+{
+ struct hci_dev_list_req *dl;
+ struct hci_dev_req *dr;
+ int i;
+
+ if (!(dl = malloc(HCI_MAX_DEV * sizeof(struct hci_dev_req) + sizeof(uint16_t)))) {
+ syslog(LOG_INFO, "Can't allocate devlist buffer. %s(%d)",
+ strerror(errno), errno);
+ exit(1);
+ }
+ dl->dev_num = HCI_MAX_DEV;
+ dr = dl->dev_req;
+
+ if (ioctl(ctl, HCIGETDEVLIST, (void*)dl)) {
+ syslog(LOG_INFO, "Can't get device list. %s(%d)",
+ strerror(errno), errno);
+ exit(1);
+ }
+
+ for (i=0; i < dl->dev_num; i++, dr++) {
+ if (hcid.auto_init)
+ init_device(dr->dev_id);
+
+ if (hcid.security && (dr->dev_opt & (1<<HCI_UP)))
+ start_security_manager(dr->dev_id);
+ }
+
+ free(dl);
+}
+
+static void init_defaults(void)
+{
+ hcid.auto_init = 0;
+ hcid.security = 0;
+
+ devi.pkt_type = 0;
+ devi.scan = SCAN_PAGE | SCAN_INQUIRY;
+ devi.auth = 0;
+ devi.encrypt = 0;
+}
+
+static void sig_usr1(int sig)
+{
+ syslog(LOG_INFO, "Flushing link keys");
+ flush_link_keys();
+}
+
+static void sig_term(int sig)
+{
+ syslog(LOG_INFO, "Terminating");
+ g_main_quit(event_loop);
+ save_link_keys();
+}
+
+static void sig_hup(int sig)
+{
+ syslog(LOG_INFO, "Reloading config file");
+ init_defaults();
+ if (read_config(hcid.config_file) < 0)
+ syslog(LOG_ERR, "Config reload failed");
+
+ init_all_devices(hcid.sock);
+}
+
+static inline void device_event(GIOChannel *chan, evt_stack_internal *si)
+{
+ evt_si_device *sd = (void *) &si->data;
+
+ switch (sd->event) {
+ case HCI_DEV_REG:
+ syslog(LOG_INFO, "HCI dev %d registered", sd->dev_id);
+ if (hcid.auto_init)
+ init_device(sd->dev_id);
+ break;
+
+ case HCI_DEV_UNREG:
+ syslog(LOG_INFO, "HCI dev %d unregistered", sd->dev_id);
+ break;
+
+ case HCI_DEV_UP:
+ syslog(LOG_INFO, "HCI dev %d up", sd->dev_id);
+ if (hcid.security)
+ start_security_manager(sd->dev_id);
+ break;
+
+ case HCI_DEV_DOWN:
+ syslog(LOG_INFO, "HCI dev %d down", sd->dev_id);
+ if (hcid.security)
+ stop_security_manager(sd->dev_id);
+ break;
+ }
+}
+
+gboolean io_stack_event(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+ char buf[HCI_MAX_FRAME_SIZE], *ptr;
+ evt_stack_internal *si;
+ hci_event_hdr *eh;
+ int len, type;
+ GIOError err;
+
+ ptr = buf;
+
+ if ((err = g_io_channel_read(chan, buf, sizeof(buf), &len))) {
+ if (err == G_IO_ERROR_AGAIN)
+ return TRUE;
+
+ syslog(LOG_ERR, "Read from control socket failed. %s(%d)",
+ strerror(errno), errno);
+ g_main_quit(event_loop);
+ return FALSE;
+ }
+
+ type = *ptr++;
+
+ if (type != HCI_EVENT_PKT)
+ return TRUE;
+
+ eh = (hci_event_hdr *) ptr;
+ if (eh->evt != EVT_STACK_INTERNAL)
+ return TRUE;
+
+ ptr += HCI_EVENT_HDR_SIZE;
+
+ si = (evt_stack_internal *) ptr;
+ switch (si->type) {
+ case EVT_SI_DEVICE:
+ device_event(chan, si);
+ break;
+ }
+
+ return TRUE;
+}
+
+extern int optind,opterr,optopt;
+extern char *optarg;
+
+int main(int argc, char *argv[], char *env[])
+{
+ int daemon, dofork, opt, fd;
+ struct sockaddr_hci addr;
+ struct hci_filter flt;
+ struct sigaction sa;
+ GIOChannel *ctl_io;
+
+ daemon = 1; dofork = 1;
+
+ /* Default HCId settings */
+ hcid.config_file = HCID_CONFIG_FILE;
+ hcid.host_name = get_host_name();
+
+ hcid.pin_file = strdup(HCID_PIN_FILE);
+ hcid.pin_helper = strdup(HCID_PIN_HELPER);
+ hcid.key_file = strdup(HCID_KEY_FILE);
+ hcid.key_num = HCID_KEY_NUM;
+
+ init_defaults();
+
+ while ((opt=getopt(argc,argv,"f:n")) != EOF) {
+ switch(opt) {
+ case 'n':
+ daemon = 0;
+ break;
+
+ case 'f':
+ hcid.config_file = strdup(optarg);
+ break;
+
+ default:
+ usage();
+ exit(1);
+ }
+ }
+
+ if (daemon) {
+ if (dofork && fork())
+ exit(0);
+
+ /* Direct stdin,stdout,stderr to '/dev/null' */
+ fd = open("/dev/null", O_RDWR);
+ dup2(fd, 0); dup2(fd, 1); dup2(fd, 2);
+ close(fd);
+
+ setsid();
+
+ chdir("/");
+ }
+
+ init_title(argc, argv, env, "hcid: ");
+ set_title("initializing");
+
+ /* Start logging to syslog and stderr */
+ openlog("hcid", LOG_PID | LOG_NDELAY | LOG_PERROR, LOG_DAEMON);
+ syslog(LOG_INFO, "HCI daemon ver %s started", VERSION);
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_flags = SA_NOCLDSTOP;
+ sa.sa_handler = sig_term;
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+ sa.sa_handler = sig_hup;
+ sigaction(SIGHUP, &sa, NULL);
+ sa.sa_handler = sig_usr1;
+ sigaction(SIGUSR1, &sa, NULL);
+
+ sa.sa_handler = SIG_IGN;
+ sigaction(SIGCHLD, &sa, NULL);
+ sigaction(SIGPIPE, &sa, NULL);
+
+ /* Create and bind HCI socket */
+ if ((hcid.sock = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI)) < 0) {
+ syslog(LOG_ERR, "Can't open HCI socket. %s(%d)", strerror(errno), errno);
+ exit(1);
+ }
+
+ /* Set filter */
+ hci_filter_clear(&flt);
+ hci_filter_set_ptype(HCI_EVENT_PKT, &flt);
+ hci_filter_set_event(EVT_STACK_INTERNAL, &flt);
+ if (setsockopt(hcid.sock, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) {
+ syslog(LOG_ERR, "Can't set filter. %s(%d)", strerror(errno), errno);
+ exit(1);
+ }
+
+ addr.hci_family = AF_BLUETOOTH;
+ addr.hci_dev = HCI_DEV_NONE;
+ if (bind(hcid.sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ syslog(LOG_ERR, "Can't bind HCI socket. %s(%d)\n", strerror(errno), errno);
+ exit(1);
+ }
+
+ if (read_config(hcid.config_file) < 0)
+ syslog(LOG_ERR, "Config load failed");
+
+ /* Create event loop */
+ event_loop = g_main_new(FALSE);
+
+ /* Initialize already connected devices */
+ init_all_devices(hcid.sock);
+
+ set_title("processing events");
+
+ ctl_io = g_io_channel_unix_new(hcid.sock);
+ g_io_add_watch(ctl_io, G_IO_IN, io_stack_event, NULL);
+
+ /* Start event processor */
+ g_main_run(event_loop);
+
+ syslog(LOG_INFO, "Exit.");
+ return 0;
+}
diff --git a/hcid/parser.y b/hcid/parser.y
new file mode 100644
index 00000000..e8be6c99
--- /dev/null
+++ b/hcid/parser.y
@@ -0,0 +1,272 @@
+%{
+/*
+ BlueZ - Bluetooth protocol stack for Linux
+ Copyright (C) 2000-2001 Qualcomm Incorporated
+
+ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+
+ 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$
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <stdarg.h>
+
+#include <sys/socket.h>
+#include <asm/types.h>
+
+#include <bluetooth.h>
+#include <hci.h>
+#include <hci_lib.h>
+
+#include "hcid.h"
+#include "kword.h"
+
+int cfg_error(const char *fmt, ...);
+
+int yyparse(void);
+int yylex(void);
+int yyerror(char *s);
+
+%}
+
+%union {
+ char *str;
+ long num;
+}
+
+%token K_OPTIONS K_DEVICE
+%token K_AUTOINIT K_SECURITY
+%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
+
+%token <str> WORD PATH STRING LIST
+%token <num> NUM
+
+%type <num> bool pkt_type link_mode link_policy sec_mode
+%type <str> dev_name
+
+%%
+config: statement | config statement;
+statement:
+ K_OPTIONS hcid_options
+
+ | K_DEVICE device_options
+
+ | WORD {
+ cfg_error("Invalid statement '%s'", $1);
+ }
+ | error {
+ yyclearin; yyerrok;
+ }
+ ;
+
+hcid_options: '{' hcid_opts '}'
+hcid_opts: | hcid_opt ';' | error ';' | hcid_opts hcid_opt ';';
+hcid_opt:
+ K_AUTOINIT bool {
+ hcid.auto_init = $2;
+ }
+
+ | K_SECURITY sec_mode {
+ hcid.security = $2;
+ }
+
+ | K_PINHELP PATH {
+ if (hcid.pin_helper)
+ free(hcid.pin_helper);
+ hcid.pin_helper = strdup($2);
+ }
+
+ | WORD {
+ cfg_error("Unknown option '%s'", $1);
+ }
+ ;
+
+sec_mode:
+ WORD {
+ int opt = find_keyword(sec_param, $1);
+ if (opt < 0) {
+ cfg_error("Unknown security mode '%s'", $1);
+ $$ = 0;
+ } else
+ $$ = opt;
+ }
+
+ | K_NO { $$ = HCID_SEC_NONE; }
+ ;
+
+device_options: '{' device_opts '}'
+device_opts: | device_opt ';' | error ';' | device_opts device_opt ';';
+device_opt:
+ K_PTYPE pkt_type {
+ devi.pkt_type = $2;
+ }
+
+ | K_LM link_mode {
+ devi.link_mode = $2;
+ }
+
+ | K_LP link_policy {
+ devi.link_policy = $2;
+ }
+
+ | K_NAME dev_name {
+ if (devi.name)
+ free(devi.name);
+ devi.name = $2;
+ }
+
+ | K_CLASS NUM {
+ devi.class = $2;
+ }
+
+ | K_AUTH bool {
+ devi.auth = $2;
+ }
+
+ | K_ENCRYPT bool {
+ devi.encrypt = $2;
+ }
+
+ | K_ISCAN bool {
+ if ($2)
+ devi.scan |= SCAN_INQUIRY;
+ else
+ devi.scan &= ~SCAN_INQUIRY;
+ }
+
+ | K_PSCAN bool {
+ if ($2)
+ devi.scan |= SCAN_PAGE;
+ else
+ devi.scan &= ~SCAN_PAGE;
+ }
+
+ | WORD {
+ cfg_error("Unknown option '%s'",$1);
+ YYABORT;
+ }
+ ;
+
+dev_name:
+ WORD {
+ $$ = strdup($1);
+ }
+
+ | STRING {
+ $$ = strdup($1);
+ }
+ ;
+
+pkt_type:
+ WORD {
+ int opt;
+ if (!hci_strtoptype($1, &opt))
+ cfg_error("Unknown packet type '%s'", $1);
+ $$ = opt;
+ }
+
+ | LIST {
+ int opt;
+ if (!hci_strtoptype($1, &opt))
+ cfg_error("Unknown packet type '%s'", $1);
+ $$ = opt;
+ }
+ ;
+
+link_mode:
+ WORD {
+ int opt;
+ if (!hci_strtolm($1, &opt))
+ cfg_error("Unknown link mode '%s'", $1);
+ $$ = opt;
+ }
+
+ | LIST {
+ int opt;
+ if (!hci_strtolm($1, &opt))
+ cfg_error("Unknown link mode '%s'", $1);
+ $$ = opt;
+ }
+ ;
+
+link_policy:
+ WORD {
+ int opt;
+ if (!hci_strtolp($1, &opt))
+ cfg_error("Unknown link policy '%s'", $1);
+ $$ = opt;
+ }
+
+ | LIST {
+ int opt;
+ if (!hci_strtolp($1, &opt))
+ cfg_error("Unknown link policy '%s'", $1);
+ $$ = opt;
+ }
+ ;
+
+bool: K_YES { $$ = 1; } | K_NO { $$ = 0; };
+
+%%
+
+int yyerror(char *s)
+{
+ syslog(LOG_ERR, "%s line %d\n", s, lineno);
+ return 0;
+}
+
+int cfg_error(const char *fmt, ...)
+{
+ char buf[255];
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(buf,sizeof(buf),fmt,ap);
+ va_end(ap);
+
+ yyerror(buf);
+ return 0;
+}
+
+/*
+ * Read config file.
+ */
+int read_config(char *file)
+{
+ extern FILE *yyin;
+
+ if( !(yyin = fopen(file,"r")) ){
+ syslog(LOG_ERR,"Can not open %s", file);
+ return -1;
+ }
+
+ lineno = 1;
+ yyparse();
+
+ fclose(yyin);
+
+ return 0;
+}
diff --git a/hcid/security.c b/hcid/security.c
new file mode 100644
index 00000000..bca27805
--- /dev/null
+++ b/hcid/security.c
@@ -0,0 +1,477 @@
+/*
+ BlueZ - Bluetooth protocol stack for Linux
+ Copyright (C) 2000-2001 Qualcomm Incorporated
+
+ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+
+ 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$
+ */
+
+#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 <asm/types.h>
+
+#include <bluetooth.h>
+#include <hci.h>
+#include <hci_lib.h>
+
+#include <glib.h>
+
+#include "hcid.h"
+#include "lib.h"
+
+static GIOChannel *io_chan[HCI_MAX_DEV];
+
+void save_link_keys(void)
+{
+ int n, f;
+
+ umask(0077);
+ if (!(f = open(hcid.key_file, O_WRONLY | O_CREAT | O_TRUNC, 0))) {
+ syslog(LOG_ERR, "Can't save key database %s. %s(%d)",
+ hcid.key_file, strerror(errno), errno);
+ return;
+ }
+
+ for (n = 0; n < hcid.key_num; n++) {
+ if (!hcid.link_key[n])
+ continue;
+
+ if (write_n(f, hcid.link_key[n], sizeof(struct link_key)) < 0)
+ break;
+ }
+
+ close(f);
+}
+
+void flush_link_keys(void)
+{
+ int n;
+ for (n=0; n < hcid.key_num; n++) {
+ if (hcid.link_key[n]) {
+ free(hcid.link_key[n]);
+ hcid.link_key[n] = NULL;
+ }
+ }
+}
+
+int read_link_keys(void)
+{
+ int f, n = 0;
+
+ if (!(f = open(hcid.key_file, O_RDONLY))) {
+ syslog(LOG_ERR, "Can't open key database %s. %s(%d)",
+ hcid.key_file, strerror(errno), errno);
+ return -1;
+ }
+
+ while (n < hcid.key_num) {
+ struct link_key *key;
+ int r;
+
+ key = malloc(sizeof(*key));
+ if (!key)
+ continue;
+
+ r = read_n(f, key, sizeof(*key));
+ if (r <= 0) {
+ free(key);
+ break;
+ }
+
+ hcid.link_key[n++] = key;
+ }
+
+ close(f);
+ return n;
+}
+
+int read_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, struct hci_conn_info *ci)
+{
+ pin_code_reply_cp pr;
+ char str[255], *pin, name[20];
+ bdaddr_t ba;
+ FILE *pipe;
+ int 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;
+ }
+
+ name[0] = 0;
+ //hci_remote_name(dev, &ci->bdaddr, sizeof(name), name, 0);
+
+ baswap(&ba, &ci->bdaddr);
+ sprintf(str, "%s %s %s \'%s\'", hcid.pin_helper,
+ ci->out ? "out" : "in",
+ batostr(&ba), name);
+
+ setenv("PATH", "/bin:/usr/bin:/usr/local/bin", 1);
+
+ 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);
+ pclose(pipe);
+
+ if (!pin || strlen(pin) < 5)
+ goto reject;
+
+ strtok(pin, "\n\r");
+
+ if (strncmp("PIN:", pin, 4))
+ goto reject;
+
+ 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);
+
+reject:
+ hci_send_cmd(dev, OGF_LINK_CTL, OCF_PIN_CODE_NEG_REPLY, 6, &ci->bdaddr);
+ exit(0);
+}
+
+static void link_key_request(int dev, bdaddr_t *sba, bdaddr_t *dba)
+{
+ struct link_key *key = NULL;
+ int n;
+
+ /* Find the key */
+ for (n=0; n < hcid.key_num; n++) {
+ if (!hcid.link_key[n])
+ continue;
+ if (!bacmp(&hcid.link_key[n]->sba, sba) &&
+ !bacmp(&hcid.link_key[n]->dba, dba)) {
+ key = hcid.link_key[n];
+ break;
+ }
+ }
+
+ if (key) {
+ /* Link key found */
+ link_key_reply_cp lr;
+ memcpy(lr.link_key, key->key, 16);
+ bacpy(&lr.bdaddr, dba);
+ hci_send_cmd(dev, OGF_LINK_CTL, OCF_LINK_KEY_REPLY,
+ LINK_KEY_REPLY_CP_SIZE, &lr);
+ key->time = time(0);
+ } else {
+ /* Link key not found */
+ hci_send_cmd(dev, OGF_LINK_CTL, OCF_LINK_KEY_NEG_REPLY, 6, dba);
+ }
+}
+
+static void pin_code_request(int dev, bdaddr_t *ba)
+{
+ struct hci_conn_info_req *cr;
+ struct hci_conn_info *ci;
+
+ cr = malloc(sizeof(*cr) + sizeof(*ci));
+ if (!cr)
+ return;
+
+ bacpy(&cr->bdaddr, ba);
+ 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;
+ }
+ ci = cr->conn_info;
+
+ 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);
+ 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 */
+
+ /* Let PIN helper handle that */
+ call_pin_helper(dev, ci);
+ }
+ } else {
+ /* Let PIN helper handle that */
+ call_pin_helper(dev, ci);
+ }
+ free(cr);
+}
+
+static 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;
+ time_t tm = time(0);
+ int n, k = -1;
+
+ /* Find a slot */
+ for (n=0; n < hcid.key_num; n++) {
+ key = hcid.link_key[n];
+ if (!key || (!bacmp(&key->sba, sba) && !bacmp(&key->dba, dba)) ||
+ (tm - key->time) > HCID_KEY_TTL) {
+ k = n;
+ break;
+ }
+ }
+
+ if (k != -1) {
+ /* Update link key */
+ key = hcid.link_key[k];
+ if (!key && !(key = malloc(sizeof(*key))))
+ return;
+
+ bacpy(&key->sba, sba);
+ bacpy(&key->dba, dba);
+ memcpy(key->key, evt->link_key, 16);
+ key->type = evt->key_type;
+ key->time = tm;
+
+ hcid.link_key[k] = key;
+ }
+}
+
+gboolean io_security_event(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+ char buf[HCI_MAX_EVENT_SIZE], *ptr = buf;
+ struct hci_dev_info *di = (void *) data;
+ int len, type, dev;
+ hci_event_hdr *eh;
+ GIOError err;
+
+ if (cond & G_IO_NVAL) {
+ free(data);
+ return FALSE;
+ }
+
+ if (cond & (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);
+ 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);
+
+ switch (eh->evt) {
+ case EVT_PIN_CODE_REQ:
+ pin_code_request(dev, (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;
+ }
+
+ return TRUE;
+}
+
+int init_security_data(void)
+{
+ void *buf;
+
+ buf = calloc(hcid.key_num, sizeof(void*));
+ if (!buf) {
+ syslog(LOG_ERR, "Can't allocate link key database. %s(%d)",
+ strerror(errno), errno);
+ return -1;
+ }
+ hcid.link_key = buf;
+ read_link_keys();
+
+ /* Set local PIN code */
+ if (hcid.security == HCID_SEC_AUTO) {
+ if (read_pin_code() < 0) {
+ strcpy(hcid.pin_code, "bluez");
+ hcid.pin_len = 5;
+ }
+ }
+
+ return 0;
+}
+
+void start_security_manager(int hdev)
+{
+ GIOChannel *chan = io_chan[hdev];
+ struct hci_dev_info *di;
+ struct hci_filter flt;
+ int dev;
+
+ if (chan)
+ return;
+
+ syslog(LOG_INFO, "Starting security manager %d", hdev);
+
+ if (!hcid.link_key && init_security_data())
+ return;
+
+ 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);
+ 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;
+}
+
+void stop_security_manager(int hdev)
+{
+ GIOChannel *chan = io_chan[hdev];
+
+ if (!chan)
+ return;
+
+ syslog(LOG_INFO, "Stoping security manager %d", hdev);
+
+ close(g_io_channel_unix_get_fd(chan));
+ io_chan[hdev] = NULL;
+}