From c98b2f82a4e532ca61592b08e3ad60749eb9f8d7 Mon Sep 17 00:00:00 2001 From: Max Krasnyansky Date: Fri, 8 Mar 2002 21:12:35 +0000 Subject: Initial revision --- hcid/Makefile.am | 28 ++++ hcid/Makefile.in | 362 +++++++++++++++++++++++++++++++++++++++++ hcid/hcid.conf | 63 ++++++++ hcid/hcid.h | 92 +++++++++++ hcid/kword.c | 75 +++++++++ hcid/kword.h | 36 +++++ hcid/lexer.l | 129 +++++++++++++++ hcid/lib.c | 171 ++++++++++++++++++++ hcid/lib.h | 85 ++++++++++ hcid/main.c | 430 +++++++++++++++++++++++++++++++++++++++++++++++++ hcid/parser.y | 272 +++++++++++++++++++++++++++++++ hcid/security.c | 477 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 12 files changed, 2220 insertions(+) create mode 100644 hcid/Makefile.am create mode 100644 hcid/Makefile.in create mode 100644 hcid/hcid.conf create mode 100644 hcid/hcid.h create mode 100644 hcid/kword.c create mode 100644 hcid/kword.h create mode 100644 hcid/lexer.l create mode 100644 hcid/lib.c create mode 100644 hcid/lib.h create mode 100644 hcid/main.c create mode 100644 hcid/parser.y create mode 100644 hcid/security.c (limited to 'hcid') 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 + + 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 + +#include + +#include + +#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 + + 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 +#include + +#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 + + 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 + + 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 + +#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 + + 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 +#include +#include +#include +#include +#include +#include + +#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 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 + + 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 + +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 + + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#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<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 + + 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 +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#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 WORD PATH STRING LIST +%token NUM + +%type bool pkt_type link_mode link_policy sec_mode +%type 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 + + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#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; +} -- cgit