diff options
Diffstat (limited to 'src/plugins/lanplus')
-rw-r--r-- | src/plugins/lanplus/Makefile.am | 45 | ||||
-rw-r--r-- | src/plugins/lanplus/Makefile.in | 550 | ||||
-rw-r--r-- | src/plugins/lanplus/asf.h | 75 | ||||
-rw-r--r-- | src/plugins/lanplus/lanplus.c | 3680 | ||||
-rw-r--r-- | src/plugins/lanplus/lanplus.h | 126 | ||||
-rw-r--r-- | src/plugins/lanplus/lanplus_crypt.c | 934 | ||||
-rw-r--r-- | src/plugins/lanplus/lanplus_crypt.h | 75 | ||||
-rw-r--r-- | src/plugins/lanplus/lanplus_crypt_impl.c | 293 | ||||
-rw-r--r-- | src/plugins/lanplus/lanplus_crypt_impl.h | 66 | ||||
-rw-r--r-- | src/plugins/lanplus/lanplus_dump.c | 192 | ||||
-rw-r--r-- | src/plugins/lanplus/lanplus_dump.h | 45 | ||||
-rw-r--r-- | src/plugins/lanplus/lanplus_strings.c | 39 | ||||
-rw-r--r-- | src/plugins/lanplus/rmcp.h | 82 |
13 files changed, 6202 insertions, 0 deletions
diff --git a/src/plugins/lanplus/Makefile.am b/src/plugins/lanplus/Makefile.am new file mode 100644 index 0000000..428eb04 --- /dev/null +++ b/src/plugins/lanplus/Makefile.am @@ -0,0 +1,45 @@ +# Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistribution of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistribution in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# Neither the name of Sun Microsystems, Inc. or the names of +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# This software is provided "AS IS," without a warranty of any kind. +# ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, +# INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A +# PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. +# SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE +# FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING +# OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL +# SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, +# OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR +# PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF +# LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, +# EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +MAINTAINERCLEANFILES = Makefile.in + +INCLUDES = -I$(top_srcdir)/include + +EXTRA_LTLIBRARIES = libintf_lanplus.la +noinst_LTLIBRARIES = @INTF_LANPLUS_LIB@ +libintf_lanplus_la_LIBADD = $(top_builddir)/lib/libipmitool.la +libintf_lanplus_la_SOURCES = \ + rmcp.h asf.h \ + lanplus.c lanplus.h \ + lanplus_strings.c \ + lanplus_crypt.c lanplus_crypt.h \ + lanplus_dump.h lanplus_dump.c \ + lanplus_crypt_impl.h lanplus_crypt_impl.c + diff --git a/src/plugins/lanplus/Makefile.in b/src/plugins/lanplus/Makefile.in new file mode 100644 index 0000000..6860f1b --- /dev/null +++ b/src/plugins/lanplus/Makefile.in @@ -0,0 +1,550 @@ +# Makefile.in generated by automake 1.11.3 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 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. + +@SET_MAKE@ + +# Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistribution of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistribution in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# Neither the name of Sun Microsystems, Inc. or the names of +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# This software is provided "AS IS," without a warranty of any kind. +# ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, +# INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A +# PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. +# SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE +# FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING +# OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL +# SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, +# OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR +# PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF +# LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, +# EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +subdir = src/plugins/lanplus +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) +libintf_lanplus_la_DEPENDENCIES = $(top_builddir)/lib/libipmitool.la +am_libintf_lanplus_la_OBJECTS = lanplus.lo lanplus_strings.lo \ + lanplus_crypt.lo lanplus_dump.lo lanplus_crypt_impl.lo +libintf_lanplus_la_OBJECTS = $(am_libintf_lanplus_la_OBJECTS) +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +SOURCES = $(libintf_lanplus_la_SOURCES) +DIST_SOURCES = $(libintf_lanplus_la_SOURCES) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AR = @AR@ +ARCH = @ARCH@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BASEDIR = @BASEDIR@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DISTRO = @DISTRO@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INTF_BMC = @INTF_BMC@ +INTF_BMC_LIB = @INTF_BMC_LIB@ +INTF_DUMMY = @INTF_DUMMY@ +INTF_DUMMY_LIB = @INTF_DUMMY_LIB@ +INTF_FREE = @INTF_FREE@ +INTF_FREE_LIB = @INTF_FREE_LIB@ +INTF_IMB = @INTF_IMB@ +INTF_IMB_LIB = @INTF_IMB_LIB@ +INTF_LAN = @INTF_LAN@ +INTF_LANPLUS = @INTF_LANPLUS@ +INTF_LANPLUS_LIB = @INTF_LANPLUS_LIB@ +INTF_LAN_LIB = @INTF_LAN_LIB@ +INTF_LIPMI = @INTF_LIPMI@ +INTF_LIPMI_LIB = @INTF_LIPMI_LIB@ +INTF_OPEN = @INTF_OPEN@ +INTF_OPEN_LIB = @INTF_OPEN_LIB@ +INTF_SERIAL = @INTF_SERIAL@ +INTF_SERIAL_LIB = @INTF_SERIAL_LIB@ +IPMITOOL_INTF_LIB = @IPMITOOL_INTF_LIB@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OS = @OS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +POW_LIB = @POW_LIB@ +PSTAMP = @PSTAMP@ +RANLIB = @RANLIB@ +RPMBUILD = @RPMBUILD@ +RPM_RELEASE = @RPM_RELEASE@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_configure_args = @ac_configure_args@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +MAINTAINERCLEANFILES = Makefile.in +INCLUDES = -I$(top_srcdir)/include +EXTRA_LTLIBRARIES = libintf_lanplus.la +noinst_LTLIBRARIES = @INTF_LANPLUS_LIB@ +libintf_lanplus_la_LIBADD = $(top_builddir)/lib/libipmitool.la +libintf_lanplus_la_SOURCES = \ + rmcp.h asf.h \ + lanplus.c lanplus.h \ + lanplus_strings.c \ + lanplus_crypt.c lanplus_crypt.h \ + lanplus_dump.h lanplus_dump.c \ + lanplus_crypt_impl.h lanplus_crypt_impl.c + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/plugins/lanplus/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/plugins/lanplus/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libintf_lanplus.la: $(libintf_lanplus_la_OBJECTS) $(libintf_lanplus_la_DEPENDENCIES) $(EXTRA_libintf_lanplus_la_DEPENDENCIES) + $(LINK) $(libintf_lanplus_la_OBJECTS) $(libintf_lanplus_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lanplus.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lanplus_crypt.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lanplus_crypt_impl.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lanplus_dump.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lanplus_strings.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-noinstLTLIBRARIES ctags distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags uninstall uninstall-am + + +# 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/src/plugins/lanplus/asf.h b/src/plugins/lanplus/asf.h new file mode 100644 index 0000000..7a30418 --- /dev/null +++ b/src/plugins/lanplus/asf.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#ifndef IPMI_ASF_H +#define IPMI_ASF_H + +#include <ipmitool/helper.h> +#include "lanplus.h" + +#define ASF_RMCP_IANA 0x000011be + +#define ASF_TYPE_PING 0x80 +#define ASF_TYPE_PONG 0x40 + +static const struct valstr asf_type_vals[] __attribute__((unused)) = { + { 0x10, "Reset" }, + { 0x11, "Power-up" }, + { 0x12, "Unconditional Power-down" }, + { 0x13, "Power Cycle" }, + { 0x40, "Presence Pong" }, + { 0x41, "Capabilities Response" }, + { 0x42, "System State Response" }, + { 0x80, "Presence Ping" }, + { 0x81, "Capabilities Request" }, + { 0x82, "System State Request" }, + { 0x00, NULL } +}; + +/* ASF message header */ +#ifdef HAVE_PRAGMA_PACK +#pragma pack(1) +#endif +struct asf_hdr { + uint32_t iana; + uint8_t type; + uint8_t tag; + uint8_t __reserved; + uint8_t len; +} ATTRIBUTE_PACKING; +#ifdef HAVE_PRAGMA_PACK +#pragma pack(0) +#endif + +int handle_asf(struct ipmi_intf * intf, uint8_t * data, int data_len); + +#endif /* IPMI_ASF_H */ diff --git a/src/plugins/lanplus/lanplus.c b/src/plugins/lanplus/lanplus.c new file mode 100644 index 0000000..27b9610 --- /dev/null +++ b/src/plugins/lanplus/lanplus.c @@ -0,0 +1,3680 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <inttypes.h> +#include <string.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <errno.h> +#include <unistd.h> +#include <netdb.h> +#include <time.h> +#include <fcntl.h> +#include <assert.h> + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +#include <ipmitool/helper.h> +#include <ipmitool/log.h> +#include <ipmitool/ipmi.h> +#include <ipmitool/ipmi_lanp.h> +#include <ipmitool/ipmi_channel.h> +#include <ipmitool/ipmi_intf.h> +#include <ipmitool/ipmi_strings.h> +#include <ipmitool/hpm2.h> +#include <ipmitool/bswap.h> +#include <openssl/rand.h> + +#include "lanplus.h" +#include "lanplus_crypt.h" +#include "lanplus_crypt_impl.h" +#include "lanplus_dump.h" +#include "rmcp.h" +#include "asf.h" + +/* + * LAN interface is required to support 45 byte request transactions and + * 42 byte response transactions. + */ +#define IPMI_LAN_MAX_REQUEST_SIZE 38 /* 45 - 7 */ +#define IPMI_LAN_MAX_RESPONSE_SIZE 34 /* 42 - 8 */ + +extern const struct valstr ipmi_rakp_return_codes[]; +extern const struct valstr ipmi_priv_levels[]; +extern const struct valstr ipmi_auth_algorithms[]; +extern const struct valstr ipmi_integrity_algorithms[]; +extern const struct valstr ipmi_encryption_algorithms[]; + +static struct ipmi_rq_entry * ipmi_req_entries; +static struct ipmi_rq_entry * ipmi_req_entries_tail; + + +static int ipmi_lanplus_setup(struct ipmi_intf * intf); +static int ipmi_lanplus_keepalive(struct ipmi_intf * intf); +static int ipmi_lan_send_packet(struct ipmi_intf * intf, uint8_t * data, int data_len); +static struct ipmi_rs * ipmi_lan_recv_packet(struct ipmi_intf * intf); +static struct ipmi_rs * ipmi_lan_poll_recv(struct ipmi_intf * intf); +static struct ipmi_rs * ipmi_lanplus_send_ipmi_cmd(struct ipmi_intf * intf, struct ipmi_rq * req); +static struct ipmi_rs * ipmi_lanplus_send_payload(struct ipmi_intf * intf, + struct ipmi_v2_payload * payload); +static void getIpmiPayloadWireRep( + struct ipmi_intf * intf, + struct ipmi_v2_payload * payload, /* in */ + uint8_t * out, + struct ipmi_rq * req, + uint8_t rq_seq, + uint8_t curr_seq); +static void getSolPayloadWireRep( + struct ipmi_intf * intf, + uint8_t * msg, + struct ipmi_v2_payload * payload); +static void read_open_session_response(struct ipmi_rs * rsp, int offset); +static void read_rakp2_message(struct ipmi_rs * rsp, int offset, uint8_t alg); +static void read_rakp4_message(struct ipmi_rs * rsp, int offset, uint8_t alg); +static void read_session_data(struct ipmi_rs * rsp, int * offset, struct ipmi_session *s); +static void read_session_data_v15(struct ipmi_rs * rsp, int * offset, struct ipmi_session *s); +static void read_session_data_v2x(struct ipmi_rs * rsp, int * offset, struct ipmi_session *s); +static void read_ipmi_response(struct ipmi_rs * rsp, int * offset); +static void read_sol_packet(struct ipmi_rs * rsp, int * offset); +static struct ipmi_rs * ipmi_lanplus_recv_sol(struct ipmi_intf * intf); +static struct ipmi_rs * ipmi_lanplus_send_sol( + struct ipmi_intf * intf, + struct ipmi_v2_payload * payload); +static int check_sol_packet_for_new_data( + struct ipmi_intf * intf, + struct ipmi_rs *rsp); +static void ack_sol_packet( + struct ipmi_intf * intf, + struct ipmi_rs * rsp); +static void ipmi_lanp_set_max_rq_data_size(struct ipmi_intf * intf, uint16_t size); +static void ipmi_lanp_set_max_rp_data_size(struct ipmi_intf * intf, uint16_t size); + +static uint8_t bridgePossible = 0; + +struct ipmi_intf ipmi_lanplus_intf = { + name: "lanplus", + desc: "IPMI v2.0 RMCP+ LAN Interface", + setup: ipmi_lanplus_setup, + open: ipmi_lanplus_open, + close: ipmi_lanplus_close, + sendrecv: ipmi_lanplus_send_ipmi_cmd, + recv_sol: ipmi_lanplus_recv_sol, + send_sol: ipmi_lanplus_send_sol, + keepalive: ipmi_lanplus_keepalive, + set_max_request_data_size: ipmi_lanp_set_max_rq_data_size, + set_max_response_data_size: ipmi_lanp_set_max_rp_data_size, + target_addr: IPMI_BMC_SLAVE_ADDR, +}; + + +extern int verbose; + + + +/* + * lanplus_get_requested_ciphers + * + * Set the authentication, integrity and encryption algorithms based + * on the cipher suite ID. See table 22-19 in the IPMIv2 spec for the + * source of this information. + * + * param cipher_suite_id [in] + * param auth_alg [out] + * param integrity_alg [out] + * param crypt_alg [out] + * + * returns 0 on success + * 1 on failure + */ +int lanplus_get_requested_ciphers(int cipher_suite_id, + uint8_t * auth_alg, + uint8_t * integrity_alg, + uint8_t * crypt_alg) +{ + if ((cipher_suite_id < 0) || (cipher_suite_id > 14)) + return 1; + + /* See table 22-19 for the source of the statement */ + switch (cipher_suite_id) + { + case 0: + *auth_alg = IPMI_AUTH_RAKP_NONE; + *integrity_alg = IPMI_INTEGRITY_NONE; + *crypt_alg = IPMI_CRYPT_NONE; + break; + case 1: + *auth_alg = IPMI_AUTH_RAKP_HMAC_SHA1; + *integrity_alg = IPMI_INTEGRITY_NONE; + *crypt_alg = IPMI_CRYPT_NONE; + break; + case 2: + *auth_alg = IPMI_AUTH_RAKP_HMAC_SHA1; + *integrity_alg = IPMI_INTEGRITY_HMAC_SHA1_96; + *crypt_alg = IPMI_CRYPT_NONE; + break; + case 3: + *auth_alg = IPMI_AUTH_RAKP_HMAC_SHA1; + *integrity_alg = IPMI_INTEGRITY_HMAC_SHA1_96; + *crypt_alg = IPMI_CRYPT_AES_CBC_128; + break; + case 4: + *auth_alg = IPMI_AUTH_RAKP_HMAC_SHA1; + *integrity_alg = IPMI_INTEGRITY_HMAC_SHA1_96; + *crypt_alg = IPMI_CRYPT_XRC4_128; + break; + case 5: + *auth_alg = IPMI_AUTH_RAKP_HMAC_SHA1; + *integrity_alg = IPMI_INTEGRITY_HMAC_SHA1_96; + *crypt_alg = IPMI_CRYPT_XRC4_40; + break; + case 6: + *auth_alg = IPMI_AUTH_RAKP_HMAC_MD5; + *integrity_alg = IPMI_INTEGRITY_NONE; + *crypt_alg = IPMI_CRYPT_NONE; + break; + case 7: + *auth_alg = IPMI_AUTH_RAKP_HMAC_MD5; + *integrity_alg = IPMI_INTEGRITY_HMAC_MD5_128; + *crypt_alg = IPMI_CRYPT_NONE; + break; + case 8: + *auth_alg = IPMI_AUTH_RAKP_HMAC_MD5; + *integrity_alg = IPMI_INTEGRITY_HMAC_MD5_128; + *crypt_alg = IPMI_CRYPT_AES_CBC_128; + break; + case 9: + *auth_alg = IPMI_AUTH_RAKP_HMAC_MD5; + *integrity_alg = IPMI_INTEGRITY_HMAC_MD5_128; + *crypt_alg = IPMI_CRYPT_XRC4_128; + break; + case 10: + *auth_alg = IPMI_AUTH_RAKP_HMAC_MD5; + *integrity_alg = IPMI_INTEGRITY_HMAC_MD5_128; + *crypt_alg = IPMI_CRYPT_XRC4_40; + break; + case 11: + *auth_alg = IPMI_AUTH_RAKP_HMAC_MD5; + *integrity_alg = IPMI_INTEGRITY_MD5_128; + *crypt_alg = IPMI_CRYPT_NONE; + break; + case 12: + *auth_alg = IPMI_AUTH_RAKP_HMAC_MD5; + *integrity_alg = IPMI_INTEGRITY_MD5_128; + *crypt_alg = IPMI_CRYPT_AES_CBC_128; + break; + case 13: + *auth_alg = IPMI_AUTH_RAKP_HMAC_MD5; + *integrity_alg = IPMI_INTEGRITY_MD5_128; + *crypt_alg = IPMI_CRYPT_XRC4_128; + break; + case 14: + *auth_alg = IPMI_AUTH_RAKP_HMAC_MD5; + *integrity_alg = IPMI_INTEGRITY_MD5_128; + *crypt_alg = IPMI_CRYPT_XRC4_40; + break; + } + + return 0; +} + + + +/* + * Reverse the order of arbitrarily long strings of bytes + */ +void lanplus_swap( + uint8_t * buffer, + int length) +{ + int i; + uint8_t temp; + + for (i =0; i < length/2; ++i) + { + temp = buffer[i]; + buffer[i] = buffer[length - 1 - i]; + buffer[length - 1 - i] = temp; + } +} + + + +static const struct valstr plus_payload_types_vals[] = { + { IPMI_PAYLOAD_TYPE_IPMI, "IPMI (0)" }, // IPMI Message + { IPMI_PAYLOAD_TYPE_SOL, "SOL (1)" }, // SOL (Serial over LAN) + { IPMI_PAYLOAD_TYPE_OEM, "OEM (2)" }, // OEM Explicid + + { IPMI_PAYLOAD_TYPE_RMCP_OPEN_REQUEST, "OpenSession Req (0x10)" }, + { IPMI_PAYLOAD_TYPE_RMCP_OPEN_RESPONSE,"OpenSession Resp (0x11)" }, + { IPMI_PAYLOAD_TYPE_RAKP_1, "RAKP1 (0x12)" }, + { IPMI_PAYLOAD_TYPE_RAKP_2, "RAKP2 (0x13)" }, + { IPMI_PAYLOAD_TYPE_RAKP_3, "RAKP3 (0x14)" }, + { IPMI_PAYLOAD_TYPE_RAKP_4, "RAKP4 (0x15)" }, + { 0x00, NULL }, +}; + + +static struct ipmi_rq_entry * +ipmi_req_add_entry(struct ipmi_intf * intf, struct ipmi_rq * req, uint8_t req_seq) +{ + struct ipmi_rq_entry * e; + + e = malloc(sizeof(struct ipmi_rq_entry)); + if (e == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + return NULL; + } + + memset(e, 0, sizeof(struct ipmi_rq_entry)); + memcpy(&e->req, req, sizeof(struct ipmi_rq)); + + e->intf = intf; + e->rq_seq = req_seq; + + if (ipmi_req_entries == NULL) + ipmi_req_entries = e; + else + ipmi_req_entries_tail->next = e; + + ipmi_req_entries_tail = e; + lprintf(LOG_DEBUG+3, "added list entry seq=0x%02x cmd=0x%02x", + e->rq_seq, e->req.msg.cmd); + return e; +} + + +static struct ipmi_rq_entry * +ipmi_req_lookup_entry(uint8_t seq, uint8_t cmd) +{ + struct ipmi_rq_entry * e = ipmi_req_entries; + + while (e && (e->rq_seq != seq || e->req.msg.cmd != cmd)) { + if (e == e->next) + return NULL; + e = e->next; + } + return e; +} + +static void +ipmi_req_remove_entry(uint8_t seq, uint8_t cmd) +{ + struct ipmi_rq_entry * p, * e, * saved_next_entry; + + e = p = ipmi_req_entries; + + while (e && (e->rq_seq != seq || e->req.msg.cmd != cmd)) { + p = e; + e = e->next; + } + if (e) { + lprintf(LOG_DEBUG+3, "removed list entry seq=0x%02x cmd=0x%02x", + seq, cmd); + saved_next_entry = e->next; + p->next = (p->next == e->next) ? NULL : e->next; + /* If entry being removed is first in list, fix up list head */ + if (ipmi_req_entries == e) { + if (ipmi_req_entries != p) + ipmi_req_entries = p; + else + ipmi_req_entries = saved_next_entry; + } + /* If entry being removed is last in list, fix up list tail */ + if (ipmi_req_entries_tail == e) { + if (ipmi_req_entries_tail != p) + ipmi_req_entries_tail = p; + else + ipmi_req_entries_tail = NULL; + } + + if (e->msg_data) { + free(e->msg_data); + e->msg_data = NULL; + } + free(e); + e = NULL; + } +} + +static void +ipmi_req_clear_entries(void) +{ + struct ipmi_rq_entry * p, * e; + + e = ipmi_req_entries; + while (e) { + lprintf(LOG_DEBUG+3, "cleared list entry seq=0x%02x cmd=0x%02x", + e->rq_seq, e->req.msg.cmd); + p = e->next; + free(e); + e = p; + } +} + + +int +ipmi_lan_send_packet( + struct ipmi_intf * intf, + uint8_t * data, int + data_len) +{ + if (verbose >= 5) + printbuf(data, data_len, ">> sending packet"); + + return send(intf->fd, data, data_len, 0); +} + + + +struct ipmi_rs * +ipmi_lan_recv_packet(struct ipmi_intf * intf) +{ + static struct ipmi_rs rsp; + fd_set read_set, err_set; + struct timeval tmout; + int ret; + + FD_ZERO(&read_set); + FD_SET(intf->fd, &read_set); + + FD_ZERO(&err_set); + FD_SET(intf->fd, &err_set); + + tmout.tv_sec = intf->session->timeout; + tmout.tv_usec = 0; + + ret = select(intf->fd + 1, &read_set, NULL, &err_set, &tmout); + if (ret < 0 || FD_ISSET(intf->fd, &err_set) || !FD_ISSET(intf->fd, &read_set)) + return NULL; + + /* the first read may return ECONNREFUSED because the rmcp ping + * packet--sent to UDP port 623--will be processed by both the + * BMC and the OS. + * + * The problem with this is that the ECONNREFUSED takes + * priority over any other received datagram; that means that + * the Connection Refused shows up _before_ the response packet, + * regardless of the order they were sent out. (unless the + * response is read before the connection refused is returned) + */ + ret = recv(intf->fd, &rsp.data, IPMI_BUF_SIZE, 0); + + if (ret < 0) { + FD_ZERO(&read_set); + FD_SET(intf->fd, &read_set); + + FD_ZERO(&err_set); + FD_SET(intf->fd, &err_set); + + tmout.tv_sec = intf->session->timeout; + tmout.tv_usec = 0; + + ret = select(intf->fd + 1, &read_set, NULL, &err_set, &tmout); + if (ret < 0 || FD_ISSET(intf->fd, &err_set) || !FD_ISSET(intf->fd, &read_set)) + return NULL; + + ret = recv(intf->fd, &rsp.data, IPMI_BUF_SIZE, 0); + if (ret < 0) + return NULL; + } + + if (ret == 0) + return NULL; + + rsp.data[ret] = '\0'; + rsp.data_len = ret; + + if (verbose >= 5) + printbuf(rsp.data, rsp.data_len, "<< received packet"); + + return &rsp; +} + + + +/* + * parse response RMCP "pong" packet + * + * return -1 if ping response not received + * returns 0 if IPMI is NOT supported + * returns 1 if IPMI is supported + * + * udp.source = 0x026f // RMCP_UDP_PORT + * udp.dest = ? // udp.source from rmcp-ping + * udp.len = ? + * udp.check = ? + * rmcp.ver = 0x06 // RMCP Version 1.0 + * rmcp.__res = 0x00 // RESERVED + * rmcp.seq = 0xff // no RMCP ACK + * rmcp.class = 0x06 // RMCP_CLASS_ASF + * asf.iana = 0x000011be // ASF_RMCP_IANA + * asf.type = 0x40 // ASF_TYPE_PONG + * asf.tag = ? // asf.tag from rmcp-ping + * asf.__res = 0x00 // RESERVED + * asf.len = 0x10 // 16 bytes + * asf.data[3:0]= 0x000011be // IANA# = RMCP_ASF_IANA if no OEM + * asf.data[7:4]= 0x00000000 // OEM-defined (not for IPMI) + * asf.data[8] = 0x81 // supported entities + * // [7]=IPMI [6:4]=RES [3:0]=ASF_1.0 + * asf.data[9] = 0x00 // supported interactions (reserved) + * asf.data[f:a]= 0x000000000000 + */ +static int +ipmi_handle_pong(struct ipmi_intf * intf, struct ipmi_rs * rsp) +{ + struct rmcp_pong { + struct rmcp_hdr rmcp; + struct asf_hdr asf; + uint32_t iana; + uint32_t oem; + uint8_t sup_entities; + uint8_t sup_interact; + uint8_t reserved[6]; + } * pong; + + if (!rsp) + return -1; + + pong = (struct rmcp_pong *)rsp->data; + + if (verbose) + printf("Received IPMI/RMCP response packet: " + "IPMI%s Supported\n", + (pong->sup_entities & 0x80) ? "" : " NOT"); + + if (verbose > 1) + printf(" ASF Version %s\n" + " RMCP Version %s\n" + " RMCP Sequence %d\n" + " IANA Enterprise %lu\n\n", + (pong->sup_entities & 0x01) ? "1.0" : "unknown", + (pong->rmcp.ver == 6) ? "1.0" : "unknown", + pong->rmcp.seq, + (unsigned long)ntohl(pong->iana)); + + return (pong->sup_entities & 0x80) ? 1 : 0; +} + + +/* build and send RMCP presence ping packet + * + * RMCP ping + * + * udp.source = ? + * udp.dest = 0x026f // RMCP_UDP_PORT + * udp.len = ? + * udp.check = ? + * rmcp.ver = 0x06 // RMCP Version 1.0 + * rmcp.__res = 0x00 // RESERVED + * rmcp.seq = 0xff // no RMCP ACK + * rmcp.class = 0x06 // RMCP_CLASS_ASF + * asf.iana = 0x000011be // ASF_RMCP_IANA + * asf.type = 0x80 // ASF_TYPE_PING + * asf.tag = ? // ASF sequence number + * asf.__res = 0x00 // RESERVED + * asf.len = 0x00 + * + */ +int +ipmiv2_lan_ping(struct ipmi_intf * intf) +{ + struct asf_hdr asf_ping = { + .iana = htonl(ASF_RMCP_IANA), + .type = ASF_TYPE_PING, + }; + struct rmcp_hdr rmcp_ping = { + .ver = RMCP_VERSION_1, + .class = RMCP_CLASS_ASF, + .seq = 0xff, + }; + uint8_t * data; + int len = sizeof(rmcp_ping) + sizeof(asf_ping); + int rv; + + data = malloc(len); + if (data == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + return -1; + } + memset(data, 0, len); + memcpy(data, &rmcp_ping, sizeof(rmcp_ping)); + memcpy(data+sizeof(rmcp_ping), &asf_ping, sizeof(asf_ping)); + + lprintf(LOG_DEBUG, "Sending IPMI/RMCP presence ping packet"); + + rv = ipmi_lan_send_packet(intf, data, len); + + free(data); + data = NULL; + + if (rv < 0) { + lprintf(LOG_ERR, "Unable to send IPMI presence ping packet"); + return -1; + } + + if (ipmi_lan_poll_recv(intf) == 0) + return 0; + + return 1; +} + + +/** + * + * ipmi_lan_poll_recv + * + * Receive whatever comes back. Ignore received packets that don't correspond + * to a request we've sent. + * + * Returns: the ipmi_rs packet describing the/a reponse we expect. + */ +static struct ipmi_rs * +ipmi_lan_poll_recv(struct ipmi_intf * intf) +{ + struct rmcp_hdr rmcp_rsp; + struct ipmi_rs * rsp; + struct ipmi_session * session = intf->session; + int offset, rv; + uint16_t payload_size; + uint8_t ourAddress = intf->my_addr; + + if (ourAddress == 0) { + ourAddress = IPMI_BMC_SLAVE_ADDR; + } + + rsp = ipmi_lan_recv_packet(intf); + + /* + * Not positive why we're looping. Do we sometimes get stuff we don't + * expect? + */ + while (rsp != NULL) { + + /* parse response headers */ + memcpy(&rmcp_rsp, rsp->data, 4); + + if (rmcp_rsp.class == RMCP_CLASS_ASF) { + /* might be ping response packet */ + rv = ipmi_handle_pong(intf, rsp); + return (rv <= 0) ? NULL : rsp; + } + + if (rmcp_rsp.class != RMCP_CLASS_IPMI) { + lprintf(LOG_DEBUG, "Invalid RMCP class: %x", + rmcp_rsp.class); + rsp = ipmi_lan_recv_packet(intf); + continue; + } + + + /* + * The authtype / payload type determines what we are receiving + */ + offset = 4; + + + /*-------------------------------------------------------------------- + * + * The current packet could be one of several things: + * + * 1) An IPMI 1.5 packet (the response to our GET CHANNEL + * AUTHENTICATION CAPABILITIES request) + * 2) An RMCP+ message with an IPMI reponse payload + * 3) AN RMCP+ open session response + * 4) An RAKP-2 message (response to an RAKP 1 message) + * 5) An RAKP-4 message (response to an RAKP 3 message) + * 6) A Serial Over LAN packet + * 7) An Invalid packet (one that doesn't match a request) + * ------------------------------------------------------------------- + */ + + read_session_data(rsp, &offset, intf->session); + + if (lanplus_has_valid_auth_code(rsp, intf->session) == 0) + { + lprintf(LOG_ERR, "ERROR: Received message with invalid authcode!"); + rsp = ipmi_lan_recv_packet(intf); + assert(0); + //continue; + } + + if ((session->v2_data.session_state == LANPLUS_STATE_ACTIVE) && + (rsp->session.authtype == IPMI_SESSION_AUTHTYPE_RMCP_PLUS) && + (rsp->session.bEncrypted)) + + { + lanplus_decrypt_payload(session->v2_data.crypt_alg, + session->v2_data.k2, + rsp->data + offset, + rsp->session.msglen, + rsp->data + offset, + &payload_size); + } + else + payload_size = rsp->session.msglen; + + + /* + * Handle IPMI responses (case #1 and #2) -- all IPMI reponses + */ + if (rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_IPMI) + { + struct ipmi_rq_entry * entry; + int payload_start = offset; + int extra_data_length; + read_ipmi_response(rsp, &offset); + + lprintf(LOG_DEBUG+1, "<< IPMI Response Session Header"); + lprintf(LOG_DEBUG+1, "<< Authtype : %s", + val2str(rsp->session.authtype, ipmi_authtype_session_vals)); + lprintf(LOG_DEBUG+1, "<< Payload type : %s", + val2str(rsp->session.payloadtype, plus_payload_types_vals)); + lprintf(LOG_DEBUG+1, "<< Session ID : 0x%08lx", + (long)rsp->session.id); + lprintf(LOG_DEBUG+1, "<< Sequence : 0x%08lx", + (long)rsp->session.seq); + lprintf(LOG_DEBUG+1, "<< IPMI Msg/Payload Length : %d", + rsp->session.msglen); + lprintf(LOG_DEBUG+1, "<< IPMI Response Message Header"); + lprintf(LOG_DEBUG+1, "<< Rq Addr : %02x", + rsp->payload.ipmi_response.rq_addr); + lprintf(LOG_DEBUG+1, "<< NetFn : %02x", + rsp->payload.ipmi_response.netfn); + lprintf(LOG_DEBUG+1, "<< Rq LUN : %01x", + rsp->payload.ipmi_response.rq_lun); + lprintf(LOG_DEBUG+1, "<< Rs Addr : %02x", + rsp->payload.ipmi_response.rs_addr); + lprintf(LOG_DEBUG+1, "<< Rq Seq : %02x", + rsp->payload.ipmi_response.rq_seq); + lprintf(LOG_DEBUG+1, "<< Rs Lun : %01x", + rsp->payload.ipmi_response.rs_lun); + lprintf(LOG_DEBUG+1, "<< Command : %02x", + rsp->payload.ipmi_response.cmd); + lprintf(LOG_DEBUG+1, "<< Compl Code : 0x%02x", + rsp->ccode); + + /* Are we expecting this packet? */ + entry = ipmi_req_lookup_entry(rsp->payload.ipmi_response.rq_seq, + rsp->payload.ipmi_response.cmd); + + if (entry != NULL) { + lprintf(LOG_DEBUG+2, "IPMI Request Match found"); + if ( intf->target_addr != intf->my_addr && + bridgePossible && + rsp->data_len && + rsp->payload.ipmi_response.cmd == 0x34 && + (rsp->payload.ipmi_response.netfn == 0x06 || + rsp->payload.ipmi_response.netfn == 0x07) && + rsp->payload.ipmi_response.rs_lun == 0 ) + { + /* Check completion code */ + if (rsp->data[offset-1] == 0) + { + lprintf(LOG_DEBUG, "Bridged command answer," + " waiting for next answer... "); + ipmi_req_remove_entry( + rsp->payload.ipmi_response.rq_seq, + rsp->payload.ipmi_response.cmd); + return ipmi_lan_poll_recv(intf); + } + else + { + lprintf(LOG_DEBUG, "WARNING: Bridged " + "cmd ccode = 0x%02x", + rsp->data[offset-1]); + } + + if (rsp->data_len && + rsp->payload.ipmi_response.cmd == 0x34) { + memcpy(rsp->data, &rsp->data[offset], + (rsp->data_len-offset)); + if (verbose > 2) + printbuf( &rsp->data[offset], + (rsp->data_len-offset), + "bridge command response"); + } + } + + ipmi_req_remove_entry(rsp->payload.ipmi_response.rq_seq, + rsp->payload.ipmi_response.cmd); + } else { + lprintf(LOG_INFO, "IPMI Request Match NOT FOUND"); + rsp = ipmi_lan_recv_packet(intf); + continue; + } + + /* + * Good packet. Shift response data to start of array. + * rsp->data becomes the variable length IPMI response data + * rsp->data_len becomes the length of that data + */ + extra_data_length = payload_size - (offset - payload_start) - 1; + if (rsp != NULL && extra_data_length) + { + rsp->data_len = extra_data_length; + memmove(rsp->data, rsp->data + offset, extra_data_length); + } + else + rsp->data_len = 0; + + break; + } + + + /* + * Open Response + */ + else if (rsp->session.payloadtype == + IPMI_PAYLOAD_TYPE_RMCP_OPEN_RESPONSE) + { + if (session->v2_data.session_state != + LANPLUS_STATE_OPEN_SESSION_SENT) + { + lprintf(LOG_ERR, "Error: Received an Unexpected Open Session " + "Response"); + rsp = ipmi_lan_recv_packet(intf); + continue; + } + + read_open_session_response(rsp, offset); + break; + } + + + /* + * RAKP 2 + */ + else if (rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_RAKP_2) + { + if (session->v2_data.session_state != LANPLUS_STATE_RAKP_1_SENT) + { + lprintf(LOG_ERR, "Error: Received an Unexpected RAKP 2 message"); + rsp = ipmi_lan_recv_packet(intf); + continue; + } + + read_rakp2_message(rsp, offset, session->v2_data.auth_alg); + break; + } + + + /* + * RAKP 4 + */ + else if (rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_RAKP_4) + { + if (session->v2_data.session_state != LANPLUS_STATE_RAKP_3_SENT) + { + lprintf(LOG_ERR, "Error: Received an Unexpected RAKP 4 message"); + rsp = ipmi_lan_recv_packet(intf); + continue; + } + + read_rakp4_message(rsp, offset, session->v2_data.auth_alg); + break; + } + + + /* + * SOL + */ + else if (rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_SOL) + { + int payload_start = offset; + int extra_data_length; + + if (session->v2_data.session_state != LANPLUS_STATE_ACTIVE) + { + lprintf(LOG_ERR, "Error: Received an Unexpected SOL packet"); + rsp = ipmi_lan_recv_packet(intf); + continue; + } + + read_sol_packet(rsp, &offset); + extra_data_length = payload_size - (offset - payload_start); + if (rsp && extra_data_length) + { + rsp->data_len = extra_data_length; + memmove(rsp->data, rsp->data + offset, extra_data_length); + } + else + rsp->data_len = 0; + + break; + } + + else + { + lprintf(LOG_ERR, "Invalid RMCP+ payload type : 0x%x", + rsp->session.payloadtype); + assert(0); + } + } + + return rsp; +} + + + +/* + * read_open_session_reponse + * + * Initialize the ipmi_rs from the IPMI 2.x open session response data. + * + * The offset should point to the first byte of the the Open Session Response + * payload when this function is called. + * + * param rsp [in/out] reading from the data and writing to the open_session_response + * section + * param offset [in] tells us where the Open Session Response payload starts + * + * returns 0 on success, 1 on error + */ +void +read_open_session_response(struct ipmi_rs * rsp, int offset) +{ + memset(&rsp->payload.open_session_response, 0, + sizeof(rsp->payload.open_session_response)); + + /* Message tag */ + rsp->payload.open_session_response.message_tag = rsp->data[offset]; + + /* RAKP reponse code */ + rsp->payload.open_session_response.rakp_return_code = rsp->data[offset + 1]; + + /* Maximum privilege level */ + rsp->payload.open_session_response.max_priv_level = rsp->data[offset + 2]; + + /*** offset + 3 is reserved ***/ + + /* Remote console session ID */ + memcpy(&(rsp->payload.open_session_response.console_id), + rsp->data + offset + 4, + 4); + #if WORDS_BIGENDIAN + rsp->payload.open_session_response.console_id = + BSWAP_32(rsp->payload.open_session_response.console_id); + #endif + + /* only tag, status, privlvl, and console id are returned if error */ + if (rsp->payload.open_session_response.rakp_return_code != + IPMI_RAKP_STATUS_NO_ERRORS) + return; + + /* BMC session ID */ + memcpy(&(rsp->payload.open_session_response.bmc_id), + rsp->data + offset + 8, + 4); + #if WORDS_BIGENDIAN + rsp->payload.open_session_response.bmc_id = + BSWAP_32(rsp->payload.open_session_response.bmc_id); + #endif + + /* And of course, our negotiated algorithms */ + rsp->payload.open_session_response.auth_alg = rsp->data[offset + 16]; + rsp->payload.open_session_response.integrity_alg = rsp->data[offset + 24]; + rsp->payload.open_session_response.crypt_alg = rsp->data[offset + 32]; +} + + + +/* + * read_rakp2_message + * + * Initialize the ipmi_rs from the IPMI 2.x RAKP 2 message + * + * The offset should point the first byte of the the RAKP 2 payload when this + * function is called. + * + * param rsp [in/out] reading from the data variable and writing to the rakp 2 + * section + * param offset [in] tells us where hte rakp2 payload starts + * param auth_alg [in] describes the authentication algorithm was agreed upon in + * the open session request/response phase. We need to know that here so + * that we know how many bytes (if any) to read fromt the packet. + * + * returns 0 on success, 1 on error + */ +void +read_rakp2_message( + struct ipmi_rs * rsp, + int offset, + uint8_t auth_alg) +{ + int i; + + /* Message tag */ + rsp->payload.rakp2_message.message_tag = rsp->data[offset]; + + /* RAKP reponse code */ + rsp->payload.rakp2_message.rakp_return_code = rsp->data[offset + 1]; + + /* Console session ID */ + memcpy(&(rsp->payload.rakp2_message.console_id), + rsp->data + offset + 4, + 4); + #if WORDS_BIGENDIAN + rsp->payload.rakp2_message.console_id = + BSWAP_32(rsp->payload.rakp2_message.console_id); + #endif + + /* BMC random number */ + memcpy(&(rsp->payload.rakp2_message.bmc_rand), + rsp->data + offset + 8, + 16); + #if WORDS_BIGENDIAN + lanplus_swap(rsp->payload.rakp2_message.bmc_rand, 16); + #endif + + /* BMC GUID */ + memcpy(&(rsp->payload.rakp2_message.bmc_guid), + rsp->data + offset + 24, + 16); + #if WORDS_BIGENDIAN + lanplus_swap(rsp->payload.rakp2_message.bmc_guid, 16); + #endif + + /* Key exchange authentication code */ + switch (auth_alg) + { + case IPMI_AUTH_RAKP_NONE: + /* Nothing to do here */ + break; + + case IPMI_AUTH_RAKP_HMAC_SHA1: + /* We need to copy 20 bytes */ + for (i = 0; i < 20; ++i) + rsp->payload.rakp2_message.key_exchange_auth_code[i] = + rsp->data[offset + 40 + i]; + break; + + case IPMI_AUTH_RAKP_HMAC_MD5: + lprintf(LOG_ERR, "read_rakp2_message: no support for " + "IPMI_AUTH_RAKP_HMAC_MD5"); + assert(0); + break; + } +} + + + +/* + * read_rakp4_message + * + * Initialize the ipmi_rs from the IPMI 2.x RAKP 4 message + * + * The offset should point the first byte of the the RAKP 4 payload when this + * function is called. + * + * param rsp [in/out] reading from the data variable and writing to the rakp + * 4 section + * param offset [in] tells us where hte rakp4 payload starts + * param integrity_alg [in] describes the authentication algorithm was + * agreed upon in the open session request/response phase. We need + * to know that here so that we know how many bytes (if any) to read + * from the packet. + * + * returns 0 on success, 1 on error + */ +void +read_rakp4_message( + struct ipmi_rs * rsp, + int offset, + uint8_t auth_alg) +{ + int i; + + /* Message tag */ + rsp->payload.rakp4_message.message_tag = rsp->data[offset]; + + /* RAKP reponse code */ + rsp->payload.rakp4_message.rakp_return_code = rsp->data[offset + 1]; + + /* Console session ID */ + memcpy(&(rsp->payload.rakp4_message.console_id), + rsp->data + offset + 4, + 4); + #if WORDS_BIGENDIAN + rsp->payload.rakp4_message.console_id = + BSWAP_32(rsp->payload.rakp4_message.console_id); + #endif + + + /* Integrity check value */ + switch (auth_alg) + { + case IPMI_AUTH_RAKP_NONE: + /* Nothing to do here */ + break; + + case IPMI_AUTH_RAKP_HMAC_SHA1: + /* We need to copy 12 bytes */ + for (i = 0; i < 12; ++i) + rsp->payload.rakp4_message.integrity_check_value[i] = + rsp->data[offset + 8 + i]; + break; + + case IPMI_AUTH_RAKP_HMAC_MD5: + lprintf(LOG_ERR, "read_rakp4_message: no support " + "for authentication algorithm 0x%x", auth_alg); + assert(0); + break; + } +} + + + + +/* + * read_session_data + * + * Initialize the ipmi_rsp from the session data in the packet + * + * The offset should point the first byte of the the IPMI session when this + * function is called. + * + * param rsp [in/out] we read from the data buffer and populate the session + * specific fields. + * param offset [in/out] should point to the beginning of the session when + * this function is called. The offset will be adjusted to + * point to the end of the session when this function exits. + * param session holds our session state + */ +void +read_session_data( + struct ipmi_rs * rsp, + int * offset, + struct ipmi_session * s) +{ + /* We expect to read different stuff depending on the authtype */ + rsp->session.authtype = rsp->data[*offset]; + + if (rsp->session.authtype == IPMI_SESSION_AUTHTYPE_RMCP_PLUS) + read_session_data_v2x(rsp, offset, s); + else + read_session_data_v15(rsp, offset, s); +} + + + +/* + * read_session_data_v2x + * + * Initialize the ipmi_rsp from the v2.x session header of the packet. + * + * The offset should point to the first byte of the the IPMI session when this + * function is called. When this function exits, offset will point to the + * start of payload. + * + * Should decrypt and perform integrity checking here? + * + * param rsp [in/out] we read from the data buffer and populate the session + * specific fields. + * param offset [in/out] should point to the beginning of the session when this + * function is called. The offset will be adjusted to point to + * the end of the session when this function exits. + * param s holds our session state + */ +void +read_session_data_v2x( + struct ipmi_rs * rsp, + int * offset, + struct ipmi_session * s) +{ + rsp->session.authtype = rsp->data[(*offset)++]; + + rsp->session.bEncrypted = (rsp->data[*offset] & 0x80 ? 1 : 0); + rsp->session.bAuthenticated = (rsp->data[*offset] & 0x40 ? 1 : 0); + + + /* Payload type */ + rsp->session.payloadtype = rsp->data[(*offset)++] & 0x3F; + + /* Session ID */ + memcpy(&rsp->session.id, rsp->data + *offset, 4); + *offset += 4; + #if WORDS_BIGENDIAN + rsp->session.id = BSWAP_32(rsp->session.id); + #endif + + + /* + * Verify that the session ID is what we think it should be + */ + if ((s->v2_data.session_state == LANPLUS_STATE_ACTIVE) && + (rsp->session.id != s->v2_data.console_id)) + { + lprintf(LOG_ERR, "packet session id 0x%x does not " + "match active session 0x%0x", + rsp->session.id, s->v2_data.console_id); + assert(0); + } + + + /* Ignored, so far */ + memcpy(&rsp->session.seq, rsp->data + *offset, 4); + *offset += 4; + #if WORDS_BIGENDIAN + rsp->session.seq = BSWAP_32(rsp->session.seq); + #endif + + memcpy(&rsp->session.msglen, rsp->data + *offset, 2); + *offset += 2; + #if WORDS_BIGENDIAN + rsp->session.msglen = BSWAP_16(rsp->session.msglen); + #endif +} + + + +/* + * read_session_data_v15 + * + * Initialize the ipmi_rsp from the session header of the packet. + * + * The offset should point the first byte of the the IPMI session when this + * function is called. When this function exits, the offset will point to + * the start of the IPMI message. + * + * param rsp [in/out] we read from the data buffer and populate the session + * specific fields. + * param offset [in/out] should point to the beginning of the session when this + * function is called. The offset will be adjusted to point to the + * end of the session when this function exits. + * param s holds our session state + */ +void read_session_data_v15( + struct ipmi_rs * rsp, + int * offset, + struct ipmi_session * s) +{ + /* All v15 messages are IPMI messages */ + rsp->session.payloadtype = IPMI_PAYLOAD_TYPE_IPMI; + + rsp->session.authtype = rsp->data[(*offset)++]; + + /* All v15 messages that we will receive are unencrypted/unauthenticated */ + rsp->session.bEncrypted = 0; + rsp->session.bAuthenticated = 0; + + /* skip the session id and sequence number fields */ + *offset += 8; + + /* This is the size of the whole payload */ + rsp->session.msglen = rsp->data[(*offset)++]; +} + + + +/* + * read_ipmi_response + * + * Initialize the ipmi_rs from with the IPMI response specific data + * + * The offset should point the first byte of the the IPMI payload when this + * function is called. + * + * param rsp [in/out] we read from the data buffer and populate the IPMI + * specific fields. + * param offset [in/out] should point to the beginning of the IPMI payload when + * this function is called. + */ +void read_ipmi_response(struct ipmi_rs * rsp, int * offset) +{ + /* + * The data here should be decrypted by now. + */ + rsp->payload.ipmi_response.rq_addr = rsp->data[(*offset)++]; + rsp->payload.ipmi_response.netfn = rsp->data[*offset] >> 2; + rsp->payload.ipmi_response.rq_lun = rsp->data[(*offset)++] & 0x3; + (*offset)++; /* checksum */ + rsp->payload.ipmi_response.rs_addr = rsp->data[(*offset)++]; + rsp->payload.ipmi_response.rq_seq = rsp->data[*offset] >> 2; + rsp->payload.ipmi_response.rs_lun = rsp->data[(*offset)++] & 0x3; + rsp->payload.ipmi_response.cmd = rsp->data[(*offset)++]; + rsp->ccode = rsp->data[(*offset)++]; + +} + + + +/* + * read_sol_packet + * + * Initialize the ipmi_rs with the SOL response data + * + * The offset should point the first byte of the the SOL payload when this + * function is called. + * + * param rsp [in/out] we read from the data buffer and populate the + * SOL specific fields. + * param offset [in/out] should point to the beginning of the SOL payload + * when this function is called. + */ +void read_sol_packet(struct ipmi_rs * rsp, int * offset) +{ + + /* + * The data here should be decrypted by now. + */ + rsp->payload.sol_packet.packet_sequence_number = + rsp->data[(*offset)++] & 0x0F; + + rsp->payload.sol_packet.acked_packet_number = + rsp->data[(*offset)++] & 0x0F; + + rsp->payload.sol_packet.accepted_character_count = + rsp->data[(*offset)++]; + + rsp->payload.sol_packet.is_nack = + rsp->data[*offset] & 0x40; + + rsp->payload.sol_packet.transfer_unavailable = + rsp->data[*offset] & 0x20; + + rsp->payload.sol_packet.sol_inactive = + rsp->data[*offset] & 0x10; + + rsp->payload.sol_packet.transmit_overrun = + rsp->data[*offset] & 0x08; + + rsp->payload.sol_packet.break_detected = + rsp->data[(*offset)++] & 0x04; + + lprintf(LOG_DEBUG, "<<<<<<<<<< RECV FROM BMC <<<<<<<<<<<"); + lprintf(LOG_DEBUG, "< SOL sequence number : 0x%02x", + rsp->payload.sol_packet.packet_sequence_number); + lprintf(LOG_DEBUG, "< SOL acked packet : 0x%02x", + rsp->payload.sol_packet.acked_packet_number); + lprintf(LOG_DEBUG, "< SOL accepted char count : 0x%02x", + rsp->payload.sol_packet.accepted_character_count); + lprintf(LOG_DEBUG, "< SOL is nack : %s", + rsp->payload.sol_packet.is_nack? "true" : "false"); + lprintf(LOG_DEBUG, "< SOL xfer unavailable : %s", + rsp->payload.sol_packet.transfer_unavailable? "true" : "false"); + lprintf(LOG_DEBUG, "< SOL inactive : %s", + rsp->payload.sol_packet.sol_inactive? "true" : "false"); + lprintf(LOG_DEBUG, "< SOL transmit overrun : %s", + rsp->payload.sol_packet.transmit_overrun? "true" : "false"); + lprintf(LOG_DEBUG, "< SOL break detected : %s", + rsp->payload.sol_packet.break_detected? "true" : "false"); + lprintf(LOG_DEBUG, "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"); + + if (verbose >= 5) + printbuf(rsp->data + *offset - 4, 4, "SOL MSG FROM BMC"); +} + + + +/* + * getIpmiPayloadWireRep + * + * param out [out] will contain our wire representation + * param req [in] is the IPMI request to be written + * param crypt_alg [in] specifies the encryption to use + * param rq_seq [in] is the IPMI command sequence number. + */ +void getIpmiPayloadWireRep( + struct ipmi_intf * intf, /* in out */ + struct ipmi_v2_payload * payload, /* in */ + uint8_t * msg, + struct ipmi_rq * req, + uint8_t rq_seq, + uint8_t curr_seq) +{ + int cs, tmp, len; + int cs2 = 0; + int cs3 = 0; + uint8_t ourAddress = intf->my_addr; + uint8_t bridgedRequest = 0; + + if (ourAddress == 0) + ourAddress = IPMI_BMC_SLAVE_ADDR; + + len = 0; + + /* IPMI Message Header -- Figure 13-4 of the IPMI v2.0 spec */ + if ((intf->target_addr == ourAddress) || (!bridgePossible)) { + cs = len; + } else { + bridgedRequest = 1; + + if(intf->transit_addr != ourAddress && intf->transit_addr != 0) + { + bridgedRequest++; + } + /* bridged request: encapsulate w/in Send Message */ + cs = len; + msg[len++] = IPMI_BMC_SLAVE_ADDR; + msg[len++] = IPMI_NETFN_APP << 2; + tmp = len - cs; + msg[len++] = ipmi_csum(msg+cs, tmp); + cs2 = len; + msg[len++] = IPMI_REMOTE_SWID; + msg[len++] = curr_seq << 2; + + + msg[len++] = 0x34; /* Send Message rqst */ + if(bridgedRequest == 2) + msg[len++] = (0x40|intf->transit_channel); /* Track request*/ + else + msg[len++] = (0x40|intf->target_channel); /* Track request*/ + + payload->payload_length += 7; + cs = len; + + if(bridgedRequest == 2) + { + /* bridged request: encapsulate w/in Send Message */ + cs = len; + msg[len++] = intf->transit_addr; + msg[len++] = IPMI_NETFN_APP << 2; + tmp = len - cs; + msg[len++] = ipmi_csum(msg+cs, tmp); + cs3 = len; + msg[len++] = intf->my_addr; + msg[len++] = curr_seq << 2; + msg[len++] = 0x34; /* Send Message rqst */ + #if 0 /* From lan.c example */ + entry->req.msg.target_cmd = entry->req.msg.cmd; /* Save target command */ + entry->req.msg.cmd = 0x34; /* (fixup request entry) */ + #endif + msg[len++] = (0x40|intf->target_channel); /* Track request*/ + + payload->payload_length += 7; + + cs = len; + } + } + + lprintf(LOG_DEBUG,"%s RqAddr %#x transit %#x:%#x target %#x:%#x " + "bridgePossible %d", + bridgedRequest ? "Bridging" : "Local", + intf->my_addr, intf->transit_addr, intf->transit_channel, + intf->target_addr, intf->target_channel, + bridgePossible); + + /* rsAddr */ + msg[len++] = intf->target_addr; /* IPMI_BMC_SLAVE_ADDR; */ + + /* net Fn */ + msg[len++] = req->msg.netfn << 2 | (req->msg.lun & 3); + tmp = len - cs; + + /* checkSum */ + msg[len++] = ipmi_csum(msg+cs, tmp); + cs = len; + + /* rqAddr */ + if (!bridgedRequest) + msg[len++] = IPMI_REMOTE_SWID; + else /* Bridged message */ + msg[len++] = intf->my_addr; + + /* rqSeq / rqLUN */ + msg[len++] = rq_seq << 2; + + /* cmd */ + msg[len++] = req->msg.cmd; + + /* message data */ + if (req->msg.data_len) { + memcpy(msg + len, req->msg.data, req->msg.data_len); + len += req->msg.data_len; + } + + /* second checksum */ + tmp = len - cs; + msg[len++] = ipmi_csum(msg+cs, tmp); + + /* Dual bridged request: 2nd checksum */ + if (bridgedRequest == 2) { + tmp = len - cs3; + msg[len++] = ipmi_csum(msg+cs3, tmp); + payload->payload_length += 1; + } + + /* bridged request: 2nd checksum */ + if (bridgedRequest) { + tmp = len - cs2; + msg[len++] = ipmi_csum(msg+cs2, tmp); + payload->payload_length += 1; + } +} + + + +/* + * getSolPayloadWireRep + * + * param msg [out] will contain our wire representation + * param payload [in] holds the v2 payload with our SOL data + */ +void getSolPayloadWireRep( + struct ipmi_intf * intf, /* in out */ + uint8_t * msg, /* output */ + struct ipmi_v2_payload * payload) /* input */ +{ + int i = 0; + + lprintf(LOG_DEBUG, ">>>>>>>>>> SENDING TO BMC >>>>>>>>>>"); + lprintf(LOG_DEBUG, "> SOL sequence number : 0x%02x", + payload->payload.sol_packet.packet_sequence_number); + lprintf(LOG_DEBUG, "> SOL acked packet : 0x%02x", + payload->payload.sol_packet.acked_packet_number); + lprintf(LOG_DEBUG, "> SOL accepted char count : 0x%02x", + payload->payload.sol_packet.accepted_character_count); + lprintf(LOG_DEBUG, "> SOL is nack : %s", + payload->payload.sol_packet.is_nack ? "true" : "false"); + lprintf(LOG_DEBUG, "> SOL assert ring wor : %s", + payload->payload.sol_packet.assert_ring_wor ? "true" : "false"); + lprintf(LOG_DEBUG, "> SOL generate break : %s", + payload->payload.sol_packet.generate_break ? "true" : "false"); + lprintf(LOG_DEBUG, "> SOL deassert cts : %s", + payload->payload.sol_packet.deassert_cts ? "true" : "false"); + lprintf(LOG_DEBUG, "> SOL deassert dcd dsr : %s", + payload->payload.sol_packet.deassert_dcd_dsr ? "true" : "false"); + lprintf(LOG_DEBUG, "> SOL flush inbound : %s", + payload->payload.sol_packet.flush_inbound ? "true" : "false"); + lprintf(LOG_DEBUG, "> SOL flush outbound : %s", + payload->payload.sol_packet.flush_outbound ? "true" : "false"); + + msg[i++] = payload->payload.sol_packet.packet_sequence_number; + msg[i++] = payload->payload.sol_packet.acked_packet_number; + msg[i++] = payload->payload.sol_packet.accepted_character_count; + + msg[i] = payload->payload.sol_packet.is_nack ? 0x40 : 0; + msg[i] |= payload->payload.sol_packet.assert_ring_wor ? 0x20 : 0; + msg[i] |= payload->payload.sol_packet.generate_break ? 0x10 : 0; + msg[i] |= payload->payload.sol_packet.deassert_cts ? 0x08 : 0; + msg[i] |= payload->payload.sol_packet.deassert_dcd_dsr ? 0x04 : 0; + msg[i] |= payload->payload.sol_packet.flush_inbound ? 0x02 : 0; + msg[i++] |= payload->payload.sol_packet.flush_outbound ? 0x01 : 0; + + /* We may have data to add */ + memcpy(msg + i, + payload->payload.sol_packet.data, + payload->payload.sol_packet.character_count); + + lprintf(LOG_DEBUG, "> SOL character count : %d", + payload->payload.sol_packet.character_count); + lprintf(LOG_DEBUG, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); + + if (verbose >= 5 && payload->payload.sol_packet.character_count) + printbuf(payload->payload.sol_packet.data, payload->payload.sol_packet.character_count, "SOL SEND DATA"); + + /* + * At this point, the payload length becomes the whole payload + * length, including the 4 bytes at the beginning of the SOL + * packet + */ + payload->payload_length = payload->payload.sol_packet.character_count + 4; +} + + + +/* + * ipmi_lanplus_build_v2x_msg + * + * Encapsulates the payload data to create the IPMI v2.0 / RMCP+ packet. + * + * + * IPMI v2.0 LAN Request Message Format + * +----------------------+ + * | rmcp.ver | 4 bytes + * | rmcp.__reserved | + * | rmcp.seq | + * | rmcp.class | + * +----------------------+ + * | session.authtype | 10 bytes + * | session.payloadtype | + * | session.id | + * | session.seq | + * +----------------------+ + * | message length | 2 bytes + * +----------------------+ + * | Confidentiality Hdr | var (possibly absent) + * +----------------------+ + * | Paylod | var Payload + * +----------------------+ + * | Confidentiality Trlr | var (possibly absent) + * +----------------------+ + * | Integrity pad | var (possibly absent) + * +----------------------+ + * | Pad length | 1 byte (WTF?) + * +----------------------+ + * | Next Header | 1 byte (WTF?) + * +----------------------+ + * | Authcode | var (possibly absent) + * +----------------------+ + */ +void +ipmi_lanplus_build_v2x_msg( + struct ipmi_intf * intf, /* in */ + struct ipmi_v2_payload * payload, /* in */ + int * msg_len, /* out */ + uint8_t ** msg_data, /* out */ + uint8_t curr_seq) +{ + uint32_t session_trailer_length = 0; + struct ipmi_session * session = intf->session; + struct rmcp_hdr rmcp = { + .ver = RMCP_VERSION_1, + .class = RMCP_CLASS_IPMI, + .seq = 0xff, + }; + + /* msg will hold the entire message to be sent */ + uint8_t * msg; + int len = 0; + + + len = + sizeof(rmcp) + // RMCP Header (4) + 10 + // IPMI Session Header + 2 + // Message length + payload->payload_length + // The actual payload + IPMI_MAX_INTEGRITY_PAD_SIZE + // Integrity Pad + 1 + // Pad Length + 1 + // Next Header + IPMI_MAX_AUTH_CODE_SIZE; // Authcode + + + msg = malloc(len); + if (msg == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + return; + } + memset(msg, 0, len); + + /* + *------------------------------------------ + * RMCP HEADER + *------------------------------------------ + */ + memcpy(msg, &rmcp, sizeof(rmcp)); + len = sizeof(rmcp); + + + /* + *------------------------------------------ + * IPMI SESSION HEADER + *------------------------------------------ + */ + /* ipmi session Auth Type / Format is always 0x06 for IPMI v2 */ + msg[IPMI_LANPLUS_OFFSET_AUTHTYPE] = 0x06; + + /* Payload Type -- also specifies whether were authenticated/encyrpted */ + msg[IPMI_LANPLUS_OFFSET_PAYLOAD_TYPE] = payload->payload_type; + + if (session->v2_data.session_state == LANPLUS_STATE_ACTIVE) + { + msg[IPMI_LANPLUS_OFFSET_PAYLOAD_TYPE] |= + ((session->v2_data.crypt_alg != IPMI_CRYPT_NONE )? 0x80 : 0x00); + msg[IPMI_LANPLUS_OFFSET_PAYLOAD_TYPE] |= + ((session->v2_data.integrity_alg != IPMI_INTEGRITY_NONE)? 0x40 : 0x00); + } + + if (session->v2_data.session_state == LANPLUS_STATE_ACTIVE) + { + /* Session ID -- making it LSB */ + msg[IPMI_LANPLUS_OFFSET_SESSION_ID ] = session->v2_data.bmc_id & 0xff; + msg[IPMI_LANPLUS_OFFSET_SESSION_ID + 1] = (session->v2_data.bmc_id >> 8) & 0xff; + msg[IPMI_LANPLUS_OFFSET_SESSION_ID + 2] = (session->v2_data.bmc_id >> 16) & 0xff; + msg[IPMI_LANPLUS_OFFSET_SESSION_ID + 3] = (session->v2_data.bmc_id >> 24) & 0xff; + + /* Sequence Number -- making it LSB */ + msg[IPMI_LANPLUS_OFFSET_SEQUENCE_NUM ] = session->out_seq & 0xff; + msg[IPMI_LANPLUS_OFFSET_SEQUENCE_NUM + 1] = (session->out_seq >> 8) & 0xff; + msg[IPMI_LANPLUS_OFFSET_SEQUENCE_NUM + 2] = (session->out_seq >> 16) & 0xff; + msg[IPMI_LANPLUS_OFFSET_SEQUENCE_NUM + 3] = (session->out_seq >> 24) & 0xff; + } + + /* + * Payload Length is set below (we don't know how big the payload is until after + * encryption). + */ + + /* + * Payload + * + * At this point we are ready to slam the payload in. + * This includes: + * 1) The confidentiality header + * 2) The payload proper (possibly encrypted) + * 3) The confidentiality trailer + * + */ + switch (payload->payload_type) + { + case IPMI_PAYLOAD_TYPE_IPMI: + getIpmiPayloadWireRep(intf, + payload, /* in */ + msg + IPMI_LANPLUS_OFFSET_PAYLOAD, + payload->payload.ipmi_request.request, + payload->payload.ipmi_request.rq_seq, + curr_seq); + break; + + case IPMI_PAYLOAD_TYPE_SOL: + getSolPayloadWireRep(intf, + msg + IPMI_LANPLUS_OFFSET_PAYLOAD, + payload); + + if (verbose >= 5) + printbuf(msg + IPMI_LANPLUS_OFFSET_PAYLOAD, 4, "SOL MSG TO BMC"); + + len += payload->payload_length; + + break; + + case IPMI_PAYLOAD_TYPE_RMCP_OPEN_REQUEST: + /* never encrypted, so our job is easy */ + memcpy(msg + IPMI_LANPLUS_OFFSET_PAYLOAD, + payload->payload.open_session_request.request, + payload->payload_length); + len += payload->payload_length; + break; + + case IPMI_PAYLOAD_TYPE_RAKP_1: + /* never encrypted, so our job is easy */ + memcpy(msg + IPMI_LANPLUS_OFFSET_PAYLOAD, + payload->payload.rakp_1_message.message, + payload->payload_length); + len += payload->payload_length; + break; + + case IPMI_PAYLOAD_TYPE_RAKP_3: + /* never encrypted, so our job is easy */ + memcpy(msg + IPMI_LANPLUS_OFFSET_PAYLOAD, + payload->payload.rakp_3_message.message, + payload->payload_length); + len += payload->payload_length; + break; + + default: + lprintf(LOG_ERR, "unsupported payload type 0x%x", + payload->payload_type); + free(msg); + msg = NULL; + assert(0); + break; + } + + + /* + *------------------------------------------ + * ENCRYPT THE PAYLOAD IF NECESSARY + *------------------------------------------ + */ + if (session->v2_data.session_state == LANPLUS_STATE_ACTIVE) + { + /* Payload len is adjusted as necessary by lanplus_encrypt_payload */ + lanplus_encrypt_payload(session->v2_data.crypt_alg, /* input */ + session->v2_data.k2, /* input */ + msg + IPMI_LANPLUS_OFFSET_PAYLOAD, /* input */ + payload->payload_length, /* input */ + msg + IPMI_LANPLUS_OFFSET_PAYLOAD, /* output */ + &(payload->payload_length)); /* output */ + + } + + /* Now we know the payload length */ + msg[IPMI_LANPLUS_OFFSET_PAYLOAD_SIZE ] = + payload->payload_length & 0xff; + msg[IPMI_LANPLUS_OFFSET_PAYLOAD_SIZE + 1] = + (payload->payload_length >> 8) & 0xff; + + + /* + *------------------------------------------ + * SESSION TRAILER + *------------------------------------------ + */ + if ((session->v2_data.session_state == LANPLUS_STATE_ACTIVE) && + (session->v2_data.integrity_alg != IPMI_INTEGRITY_NONE)) + { + uint32_t i, hmac_length, integrity_pad_size = 0, hmac_input_size; + uint8_t * hmac_output; + uint32_t start_of_session_trailer = + IPMI_LANPLUS_OFFSET_PAYLOAD + + payload->payload_length; + + + /* + * Determine the required integrity pad length. We have to make the + * data range covered by the authcode a multiple of 4. + */ + uint32_t length_before_authcode; + + if (ipmi_oem_active(intf, "icts")) { + length_before_authcode = + 12 + /* the stuff before the payload */ + payload->payload_length; + } else { + length_before_authcode = + 12 + /* the stuff before the payload */ + payload->payload_length + + 1 + /* pad length field */ + 1; /* next header field */ + } + + if (length_before_authcode % 4) + integrity_pad_size = 4 - (length_before_authcode % 4); + + for (i = 0; i < integrity_pad_size; ++i) + msg[start_of_session_trailer + i] = 0xFF; + + /* Pad length */ + msg[start_of_session_trailer + integrity_pad_size] = integrity_pad_size; + + /* Next Header */ + msg[start_of_session_trailer + integrity_pad_size + 1] = + 0x07; /* Hardcoded per the spec, table 13-8 */ + + hmac_input_size = + 12 + + payload->payload_length + + integrity_pad_size + + 2; + + hmac_output = + msg + + IPMI_LANPLUS_OFFSET_PAYLOAD + + payload->payload_length + + integrity_pad_size + + 2; + + if (verbose > 2) + printbuf(msg + IPMI_LANPLUS_OFFSET_AUTHTYPE, hmac_input_size, "authcode input"); + + + /* Auth Code */ + lanplus_HMAC(session->v2_data.integrity_alg, + session->v2_data.k1, /* key */ + 20, /* key length */ + msg + IPMI_LANPLUS_OFFSET_AUTHTYPE, /* hmac input */ + hmac_input_size, + hmac_output, + &hmac_length); + + assert(hmac_length == 20); + + if (verbose > 2) + printbuf(hmac_output, 12, "authcode output"); + + /* Set session_trailer_length appropriately */ + session_trailer_length = + integrity_pad_size + + 2 + /* pad length + next header */ + 12; /* Size of the authcode (we only use the first 12 bytes) */ + } + + + ++(session->out_seq); + if (!session->out_seq) + ++(session->out_seq); + + *msg_len = + IPMI_LANPLUS_OFFSET_PAYLOAD + + payload->payload_length + + session_trailer_length; + *msg_data = msg; +} + + + +/* + * ipmi_lanplus_build_v2x_ipmi_cmd + * + * Wraps ipmi_lanplus_build_v2x_msg and returns a new entry object for the + * command + * + */ +static struct ipmi_rq_entry * +ipmi_lanplus_build_v2x_ipmi_cmd( + struct ipmi_intf * intf, + struct ipmi_rq * req, + int isRetry) +{ + struct ipmi_v2_payload v2_payload; + struct ipmi_rq_entry * entry; + + /* + * We have a problem. we need to know the sequence number here, + * because we use it in our stored entry. But we also need to + * know the sequence number when we generate our IPMI + * representation far below. + */ + static uint8_t curr_seq = 0; + + if( isRetry == 0 ) + curr_seq += 1; + + if (curr_seq >= 64) + curr_seq = 0; + + + /* IPMI Message Header -- Figure 13-4 of the IPMI v2.0 spec */ + if ((intf->target_addr == intf->my_addr) || (!bridgePossible)) + { + entry = ipmi_req_add_entry(intf, req, curr_seq); + } + else /* it's a bridge command */ + { + unsigned char backup_cmd; + + /* Add entry for cmd */ + entry = ipmi_req_add_entry(intf, req, curr_seq); + + if(entry) + { + /* Add entry for bridge cmd */ + backup_cmd = req->msg.cmd; + req->msg.cmd = 0x34; + entry = ipmi_req_add_entry(intf, req, curr_seq); + req->msg.cmd = backup_cmd; + } + } + + if (entry == NULL) + return NULL; + + // Build our payload + v2_payload.payload_type = IPMI_PAYLOAD_TYPE_IPMI; + v2_payload.payload_length = req->msg.data_len + 7; + v2_payload.payload.ipmi_request.request = req; + v2_payload.payload.ipmi_request.rq_seq = curr_seq; + + ipmi_lanplus_build_v2x_msg(intf, // in + &v2_payload, // in + &(entry->msg_len), // out + &(entry->msg_data), // out + curr_seq); // in + + return entry; +} + + + + + +/* + * IPMI LAN Request Message Format + * +--------------------+ + * | rmcp.ver | 4 bytes + * | rmcp.__reserved | + * | rmcp.seq | + * | rmcp.class | + * +--------------------+ + * | session.authtype | 9 bytes + * | session.seq | + * | session.id | + * +--------------------+ + * | [session.authcode] | 16 bytes (AUTHTYPE != none) + * +--------------------+ + * | message length | 1 byte + * +--------------------+ + * | message.rs_addr | 6 bytes + * | message.netfn_lun | + * | message.checksum | + * | message.rq_addr | + * | message.rq_seq | + * | message.cmd | + * +--------------------+ + * | [request data] | data_len bytes + * +--------------------+ + * | checksum | 1 byte + * +--------------------+ + */ +static struct ipmi_rq_entry * +ipmi_lanplus_build_v15_ipmi_cmd( + struct ipmi_intf * intf, + struct ipmi_rq * req) +{ + struct rmcp_hdr rmcp = { + .ver = RMCP_VERSION_1, + .class = RMCP_CLASS_IPMI, + .seq = 0xff, + }; + uint8_t * msg; + int cs, mp, len = 0, tmp; + struct ipmi_session * session = intf->session; + struct ipmi_rq_entry * entry; + + entry = ipmi_req_add_entry(intf, req, 0); + if (entry == NULL) + return NULL; + + len = req->msg.data_len + 21; + + msg = malloc(len); + if (msg == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + return NULL; + } + memset(msg, 0, len); + + /* rmcp header */ + memcpy(msg, &rmcp, sizeof(rmcp)); + len = sizeof(rmcp); + + /* + * ipmi session header + */ + /* Authtype should always be none for 1.5 packets sent from this + * interface + */ + msg[len++] = IPMI_SESSION_AUTHTYPE_NONE; + + msg[len++] = session->out_seq & 0xff; + msg[len++] = (session->out_seq >> 8) & 0xff; + msg[len++] = (session->out_seq >> 16) & 0xff; + msg[len++] = (session->out_seq >> 24) & 0xff; + + /* + * The session ID should be all zeroes for pre-session commands. We + * should only be using the 1.5 interface for the pre-session Get + * Channel Authentication Capabilities command + */ + msg[len++] = 0; + msg[len++] = 0; + msg[len++] = 0; + msg[len++] = 0; + + /* message length */ + msg[len++] = req->msg.data_len + 7; + + /* ipmi message header */ + cs = mp = len; + msg[len++] = IPMI_BMC_SLAVE_ADDR; + msg[len++] = req->msg.netfn << 2; + tmp = len - cs; + msg[len++] = ipmi_csum(msg+cs, tmp); + cs = len; + msg[len++] = IPMI_REMOTE_SWID; + + entry->rq_seq = 0; + + msg[len++] = entry->rq_seq << 2; + msg[len++] = req->msg.cmd; + + lprintf(LOG_DEBUG+1, ">> IPMI Request Session Header"); + lprintf(LOG_DEBUG+1, ">> Authtype : %s", + val2str(IPMI_SESSION_AUTHTYPE_NONE, ipmi_authtype_session_vals)); + lprintf(LOG_DEBUG+1, ">> Sequence : 0x%08lx", + (long)session->out_seq); + lprintf(LOG_DEBUG+1, ">> Session ID : 0x%08lx", + (long)0); + + lprintf(LOG_DEBUG+1, ">> IPMI Request Message Header"); + lprintf(LOG_DEBUG+1, ">> Rs Addr : %02x", IPMI_BMC_SLAVE_ADDR); + lprintf(LOG_DEBUG+1, ">> NetFn : %02x", req->msg.netfn); + lprintf(LOG_DEBUG+1, ">> Rs LUN : %01x", 0); + lprintf(LOG_DEBUG+1, ">> Rq Addr : %02x", IPMI_REMOTE_SWID); + lprintf(LOG_DEBUG+1, ">> Rq Seq : %02x", entry->rq_seq); + lprintf(LOG_DEBUG+1, ">> Rq Lun : %01x", 0); + lprintf(LOG_DEBUG+1, ">> Command : %02x", req->msg.cmd); + + /* message data */ + if (req->msg.data_len) { + memcpy(msg+len, req->msg.data, req->msg.data_len); + len += req->msg.data_len; + } + + /* second checksum */ + tmp = len - cs; + msg[len++] = ipmi_csum(msg+cs, tmp); + + entry->msg_len = len; + entry->msg_data = msg; + + return entry; +} + + + +/* + * is_sol_packet + */ +static int +is_sol_packet(struct ipmi_rs * rsp) +{ + return (rsp && + (rsp->session.authtype == IPMI_SESSION_AUTHTYPE_RMCP_PLUS) && + (rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_SOL)); +} + + + +/* + * sol_response_acks_packet + */ +static int +sol_response_acks_packet( + struct ipmi_rs * rsp, + struct ipmi_v2_payload * payload) +{ + return (is_sol_packet(rsp) && + payload && + (payload->payload_type == IPMI_PAYLOAD_TYPE_SOL) && + (rsp->payload.sol_packet.acked_packet_number == + payload->payload.sol_packet.packet_sequence_number)); +} + + + +/* + * ipmi_lanplus_send_payload + * + */ +struct ipmi_rs * +ipmi_lanplus_send_payload( + struct ipmi_intf * intf, + struct ipmi_v2_payload * payload) +{ + struct ipmi_rs * rsp = NULL; + uint8_t * msg_data; + int msg_length; + struct ipmi_session * session = intf->session; + struct ipmi_rq_entry * entry = NULL; + int try = 0; + int xmit = 1; + time_t ltime; + uint32_t saved_timeout; + + if (!intf->opened && intf->open && intf->open(intf) < 0) + return NULL; + + /* + * The session timeout is initialized in the above interface open, + * so it will only be valid after the open completes. + */ + saved_timeout = session->timeout; + while (try < session->retry) { + //ltime = time(NULL); + + if (xmit) { + ltime = time(NULL); + + if (payload->payload_type == IPMI_PAYLOAD_TYPE_IPMI) + { + /* + * Build an IPMI v1.5 or v2 command + */ + struct ipmi_rq * ipmi_request = payload->payload.ipmi_request.request; + + lprintf(LOG_DEBUG, ""); + lprintf(LOG_DEBUG, ">> Sending IPMI command payload"); + lprintf(LOG_DEBUG, ">> netfn : 0x%02x", ipmi_request->msg.netfn); + lprintf(LOG_DEBUG, ">> command : 0x%02x", ipmi_request->msg.cmd); + + if (verbose > 1) + { + uint16_t i; + fprintf(stderr, ">> data : "); + for (i = 0; i < ipmi_request->msg.data_len; ++i) + fprintf(stderr, "0x%02x ", ipmi_request->msg.data[i]); + fprintf(stderr, "\n\n"); + } + + + /* + * If we are presession, and the command is GET CHANNEL AUTHENTICATION + * CAPABILITIES, we will build the command in v1.5 format. This is so + * that we can ask any server whether it supports IPMI v2 / RMCP+ + * before we attempt to open a v2.x session. + */ + if ((ipmi_request->msg.netfn == IPMI_NETFN_APP) && + (ipmi_request->msg.cmd == IPMI_GET_CHANNEL_AUTH_CAP) && + (session->v2_data.bmc_id == 0)) // jme - check + { + lprintf(LOG_DEBUG+1, "BUILDING A v1.5 COMMAND"); + entry = ipmi_lanplus_build_v15_ipmi_cmd(intf, ipmi_request); + } + else + { + int isRetry = ( try > 0 ? 1 : 0 ); + + lprintf(LOG_DEBUG+1, "BUILDING A v2 COMMAND"); + entry = ipmi_lanplus_build_v2x_ipmi_cmd(intf, ipmi_request, isRetry); + } + + if (entry == NULL) { + lprintf(LOG_ERR, "Aborting send command, unable to build"); + return NULL; + } + + msg_data = entry->msg_data; + msg_length = entry->msg_len; + } + + else if (payload->payload_type == IPMI_PAYLOAD_TYPE_RMCP_OPEN_REQUEST) + { + lprintf(LOG_DEBUG, ">> SENDING AN OPEN SESSION REQUEST\n"); + assert(session->v2_data.session_state == LANPLUS_STATE_PRESESSION + || session->v2_data.session_state == LANPLUS_STATE_OPEN_SESSION_SENT); + + ipmi_lanplus_build_v2x_msg(intf, /* in */ + payload, /* in */ + &msg_length, /* out */ + &msg_data, /* out */ + 0); /* irrelevant for this msg*/ + + } + + else if (payload->payload_type == IPMI_PAYLOAD_TYPE_RAKP_1) + { + lprintf(LOG_DEBUG, ">> SENDING A RAKP 1 MESSAGE\n"); + assert(session->v2_data.session_state == + LANPLUS_STATE_OPEN_SESSION_RECEIEVED); + + ipmi_lanplus_build_v2x_msg(intf, /* in */ + payload, /* in */ + &msg_length, /* out */ + &msg_data, /* out */ + 0); /* irrelevant for this msg*/ + + } + + else if (payload->payload_type == IPMI_PAYLOAD_TYPE_RAKP_3) + { + lprintf(LOG_DEBUG, ">> SENDING A RAKP 3 MESSAGE\n"); + assert(session->v2_data.session_state == + LANPLUS_STATE_RAKP_2_RECEIVED); + + ipmi_lanplus_build_v2x_msg(intf, /* in */ + payload, /* in */ + &msg_length, /* out */ + &msg_data, /* out */ + 0); /* irrelevant for this msg*/ + + } + + else if (payload->payload_type == IPMI_PAYLOAD_TYPE_SOL) + { + lprintf(LOG_DEBUG, ">> SENDING A SOL MESSAGE\n"); + assert(session->v2_data.session_state == LANPLUS_STATE_ACTIVE); + + ipmi_lanplus_build_v2x_msg(intf, /* in */ + payload, /* in */ + &msg_length, /* out */ + &msg_data, /* out */ + 0); /* irrelevant for this msg*/ + } + + else + { + lprintf(LOG_ERR, "Payload type 0x%0x is unsupported!", + payload->payload_type); + assert(0); + } + + + if (ipmi_lan_send_packet(intf, msg_data, msg_length) < 0) { + lprintf(LOG_ERR, "IPMI LAN send command failed"); + return NULL; + } + } + + /* if we are set to noanswer we do not expect response */ + if (intf->noanswer) + break; + + usleep(100); /* Not sure what this is for */ + + /* Remember our connection state */ + switch (payload->payload_type) + { + case IPMI_PAYLOAD_TYPE_RMCP_OPEN_REQUEST: + session->v2_data.session_state = LANPLUS_STATE_OPEN_SESSION_SENT; + /* not retryable for timeouts, force no retry */ + try = session->retry; + break; + case IPMI_PAYLOAD_TYPE_RAKP_1: + session->v2_data.session_state = LANPLUS_STATE_RAKP_1_SENT; + /* not retryable for timeouts, force no retry */ + try = session->retry; + break; + case IPMI_PAYLOAD_TYPE_RAKP_3: + /* not retryable for timeouts, force no retry */ + try = session->retry; + session->v2_data.session_state = LANPLUS_STATE_RAKP_3_SENT; + break; + } + + + /* + * Special case for SOL outbound packets. + */ + if (payload->payload_type == IPMI_PAYLOAD_TYPE_SOL) + { + if (! payload->payload.sol_packet.packet_sequence_number) + { + /* We're just sending an ACK. No need to retry. */ + break; + } + + + rsp = ipmi_lanplus_recv_sol(intf); /* Grab the next packet */ + + if (sol_response_acks_packet(rsp, payload)) + break; + + else if (is_sol_packet(rsp) && rsp->data_len) + { + /* + * We're still waiting for our ACK, but we more data from + * the BMC + */ + intf->session->sol_data.sol_input_handler(rsp); + /* In order to avoid duplicate output, just set data_len to 0 */ + rsp->data_len = 0; + } + } + + + /* Non-SOL processing */ + else + { + rsp = ipmi_lan_poll_recv(intf); + + /* Duplicate Request ccode most likely indicates a response to + a previous retry. Ignore and keep polling. */ + while ((rsp != NULL) && (rsp->ccode == 0xcf)) + { + rsp = NULL; + rsp = ipmi_lan_poll_recv(intf); + } + + if (rsp) + break; + /* This payload type is retryable for timeouts. */ + if ((payload->payload_type == IPMI_PAYLOAD_TYPE_IPMI) && entry) { + ipmi_req_remove_entry( entry->rq_seq, entry->req.msg.cmd); + } + } + + /* only timeout if time exceeds the timeout value */ + xmit = ((time(NULL) - ltime) >= session->timeout); + + usleep(5000); + + if (xmit) { + /* increment session timeout by 1 second each retry */ + session->timeout++; + } + + try++; + } + session->timeout = saved_timeout; + + /* IPMI messages are deleted under ipmi_lan_poll_recv() */ + switch (payload->payload_type) { + case IPMI_PAYLOAD_TYPE_RMCP_OPEN_REQUEST: + case IPMI_PAYLOAD_TYPE_RAKP_1: + case IPMI_PAYLOAD_TYPE_RAKP_3: + free(msg_data); + msg_data = NULL; + break; + } + + return rsp; +} + + + +/* + * is_sol_partial_ack + * + * Determine if the response is a partial ACK/NACK that indicates + * we need to resend part of our packet. + * + * returns the number of characters we need to resend, or + * 0 if this isn't an ACK or we don't need to resend anything + */ +int is_sol_partial_ack( + struct ipmi_intf * intf, + struct ipmi_v2_payload * v2_payload, + struct ipmi_rs * rs) +{ + int chars_to_resend = 0; + + if (v2_payload && + rs && + is_sol_packet(rs) && + sol_response_acks_packet(rs, v2_payload) && + (rs->payload.sol_packet.accepted_character_count < + v2_payload->payload.sol_packet.character_count)) + { + if (ipmi_oem_active(intf, "intelplus") && + rs->payload.sol_packet.accepted_character_count == 0) + return 0; + + chars_to_resend = + v2_payload->payload.sol_packet.character_count - + rs->payload.sol_packet.accepted_character_count; + } + + return chars_to_resend; +} + + + +/* + * set_sol_packet_sequence_number + */ +static void set_sol_packet_sequence_number( + struct ipmi_intf * intf, + struct ipmi_v2_payload * v2_payload) +{ + /* Keep our sequence number sane */ + if (intf->session->sol_data.sequence_number > 0x0F) + intf->session->sol_data.sequence_number = 1; + + v2_payload->payload.sol_packet.packet_sequence_number = + intf->session->sol_data.sequence_number++; +} + + + +/* + * ipmi_lanplus_send_sol + * + * Sends a SOL packet.. We handle partial ACK/NACKs from the BMC here. + * + * Returns a pointer to the SOL ACK we received, or + * 0 on failure + * + */ +struct ipmi_rs * +ipmi_lanplus_send_sol( + struct ipmi_intf * intf, + struct ipmi_v2_payload * v2_payload) +{ + struct ipmi_rs * rs; + + /* + * chars_to_resend indicates either that we got a NACK telling us + * that we need to resend some part of our data. + */ + int chars_to_resend = 0; + + v2_payload->payload_type = IPMI_PAYLOAD_TYPE_SOL; + + /* + * Payload length is just the length of the character + * data here. + */ + v2_payload->payload_length = v2_payload->payload.sol_packet.character_count; + + v2_payload->payload.sol_packet.acked_packet_number = 0; /* NA */ + + set_sol_packet_sequence_number(intf, v2_payload); + + v2_payload->payload.sol_packet.accepted_character_count = 0; /* NA */ + + rs = ipmi_lanplus_send_payload(intf, v2_payload); + + /* Determine if we need to resend some of our data */ + chars_to_resend = is_sol_partial_ack(intf, v2_payload, rs); + + while (rs && !rs->payload.sol_packet.transfer_unavailable && + !rs->payload.sol_packet.is_nack && + chars_to_resend) + { + /* + * We first need to handle any new data we might have + * received in our NACK + */ + if (rs->data_len) + intf->session->sol_data.sol_input_handler(rs); + + set_sol_packet_sequence_number(intf, v2_payload); + + /* Just send the required data */ + memmove(v2_payload->payload.sol_packet.data, + v2_payload->payload.sol_packet.data + + rs->payload.sol_packet.accepted_character_count, + chars_to_resend); + + v2_payload->payload.sol_packet.character_count = chars_to_resend; + + v2_payload->payload_length = v2_payload->payload.sol_packet.character_count; + + rs = ipmi_lanplus_send_payload(intf, v2_payload); + + chars_to_resend = is_sol_partial_ack(intf, v2_payload, rs); + } + + return rs; +} + + + +/* + * check_sol_packet_for_new_data + * + * Determine whether the SOL packet has already been seen + * and whether the packet has new data for us. + * + * This function has the side effect of removing an previously + * seen data, and moving new data to the front. + * + * It also "Remembers" the data so we don't get repeats. + * + * returns the number of new bytes in the SOL packet + */ +static int +check_sol_packet_for_new_data( + struct ipmi_intf * intf, + struct ipmi_rs *rsp) +{ + static uint8_t last_received_sequence_number = 0; + static uint8_t last_received_byte_count = 0; + int new_data_size = 0; + + + if (rsp && + (rsp->session.authtype == IPMI_SESSION_AUTHTYPE_RMCP_PLUS) && + (rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_SOL)) + { + /* Store the data length before we mod it */ + uint8_t unaltered_data_len = rsp->data_len; + + if (rsp->payload.sol_packet.packet_sequence_number == + last_received_sequence_number) + { + + /* + * This is the same as the last packet, but may include + * extra data + */ + new_data_size = rsp->data_len - last_received_byte_count; + + if (new_data_size > 0) + { + /* We have more data to process */ + memmove(rsp->data, + rsp->data + + rsp->data_len - new_data_size, + new_data_size); + } + + rsp->data_len = new_data_size; + } + + + /* + *Rember the data for next round + */ + if (rsp->payload.sol_packet.packet_sequence_number) + { + last_received_sequence_number = + rsp->payload.sol_packet.packet_sequence_number; + + last_received_byte_count = unaltered_data_len; + } + } + + + return new_data_size; +} + + + +/* + * ack_sol_packet + * + * Provided the specified packet looks reasonable, ACK it. + */ +static void +ack_sol_packet( + struct ipmi_intf * intf, + struct ipmi_rs * rsp) +{ + if (rsp && + (rsp->session.authtype == IPMI_SESSION_AUTHTYPE_RMCP_PLUS) && + (rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_SOL) && + (rsp->payload.sol_packet.packet_sequence_number)) + { + struct ipmi_v2_payload ack; + + bzero(&ack, sizeof(struct ipmi_v2_payload)); + + ack.payload_type = IPMI_PAYLOAD_TYPE_SOL; + + /* + * Payload length is just the length of the character + * data here. + */ + ack.payload_length = 0; + + /* ACK packets have sequence numbers of 0 */ + ack.payload.sol_packet.packet_sequence_number = 0; + + ack.payload.sol_packet.acked_packet_number = + rsp->payload.sol_packet.packet_sequence_number; + + ack.payload.sol_packet.accepted_character_count = rsp->data_len; + + ipmi_lanplus_send_payload(intf, &ack); + } +} + + + +/* + * ipmi_lanplus_recv_sol + * + * Receive a SOL packet and send an ACK in response. + * + */ +struct ipmi_rs * +ipmi_lanplus_recv_sol(struct ipmi_intf * intf) +{ + struct ipmi_rs * rsp = ipmi_lan_poll_recv(intf); + + if (rsp && rsp->session.authtype != 0) + { + ack_sol_packet(intf, rsp); + + /* + * Remembers the data sent, and alters the data to just + * include the new stuff. + */ + check_sol_packet_for_new_data(intf, rsp); + } + return rsp; +} + + + +/** + * ipmi_lanplus_send_ipmi_cmd + * + * Build a payload request and dispatch it. + */ +struct ipmi_rs * +ipmi_lanplus_send_ipmi_cmd( + struct ipmi_intf * intf, + struct ipmi_rq * req) +{ + struct ipmi_v2_payload v2_payload; + + v2_payload.payload_type = IPMI_PAYLOAD_TYPE_IPMI; + v2_payload.payload.ipmi_request.request = req; + + return ipmi_lanplus_send_payload(intf, &v2_payload); +} + + +/* + * ipmi_get_auth_capabilities_cmd + * + * This command may have to be sent twice. We first ask for the + * authentication capabilities with the "request IPMI v2 data bit" + * set. If this fails, we send the same command without that bit + * set. + * + * param intf is the initialized (but possibly) pre-session interface + * on which we will send the command + * param auth_cap [out] will be initialized to hold the Get Channel + * Authentication Capabilities return data on success. Its + * contents will be undefined on error. + * + * returns 0 on success + * non-zero if we were unable to contact the BMC, or we cannot + * get a successful response + * + */ +static int +ipmi_get_auth_capabilities_cmd( + struct ipmi_intf * intf, + struct get_channel_auth_cap_rsp * auth_cap) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t msg_data[2]; + uint8_t backupBridgePossible; + + backupBridgePossible = bridgePossible; + + bridgePossible = 0; + + msg_data[0] = IPMI_LAN_CHANNEL_E | 0x80; // Ask for IPMI v2 data as well + msg_data[1] = intf->session->privlvl; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; // 0x06 + req.msg.cmd = IPMI_GET_CHANNEL_AUTH_CAP; // 0x38 + req.msg.data = msg_data; + req.msg.data_len = 2; + + rsp = intf->sendrecv(intf, &req); + + if (rsp == NULL || rsp->ccode > 0) { + /* + * It's very possible that this failed because we asked for IPMI + * v2 data. Ask again, without requesting IPMI v2 data. + */ + msg_data[0] &= 0x7F; + + rsp = intf->sendrecv(intf, &req); + + if (rsp == NULL) { + lprintf(LOG_INFO, "Get Auth Capabilities error"); + return 1; + } + if (rsp->ccode > 0) { + lprintf(LOG_INFO, "Get Auth Capabilities error: %s", + val2str(rsp->ccode, completion_code_vals)); + return 1; + } + } + + + memcpy(auth_cap, + rsp->data, + sizeof(struct get_channel_auth_cap_rsp)); + + bridgePossible = backupBridgePossible; + + return 0; +} + + + +static int +ipmi_close_session_cmd(struct ipmi_intf * intf) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t msg_data[4]; + uint32_t bmc_session_lsbf; + uint8_t backupBridgePossible; + + if (intf->session->v2_data.session_state != LANPLUS_STATE_ACTIVE) + return -1; + + backupBridgePossible = bridgePossible; + + intf->target_addr = IPMI_BMC_SLAVE_ADDR; + bridgePossible = 0; + + bmc_session_lsbf = intf->session->v2_data.bmc_id; +#if WORDS_BIGENDIAN + bmc_session_lsbf = BSWAP_32(bmc_session_lsbf); +#endif + + memcpy(&msg_data, &bmc_session_lsbf, 4); + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = 0x3c; + req.msg.data = msg_data; + req.msg.data_len = 4; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + /* Looks like the session was closed */ + lprintf(LOG_ERR, "Close Session command failed"); + return -1; + } + if (verbose > 2) + printbuf(rsp->data, rsp->data_len, "close_session"); + + if (rsp->ccode == 0x87) { + lprintf(LOG_ERR, "Failed to Close Session: invalid " + "session ID %08lx", + (long)intf->session->v2_data.bmc_id); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Close Session command failed: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + lprintf(LOG_DEBUG, "Closed Session %08lx\n", + (long)intf->session->v2_data.bmc_id); + + bridgePossible = backupBridgePossible; + + return 0; +} + + + +/* + * ipmi_lanplus_open_session + * + * Build and send the open session command. See section 13.17 of the IPMI + * v2 specification for details. + */ +static int +ipmi_lanplus_open_session(struct ipmi_intf * intf) +{ + struct ipmi_v2_payload v2_payload; + struct ipmi_session * session = intf->session; + uint8_t * msg; + struct ipmi_rs * rsp; + /* 0 = success, 1 = error, 2 = timeout */ + int rc = 0; + + + /* + * Build an Open Session Request Payload + */ + msg = (uint8_t*)malloc(IPMI_OPEN_SESSION_REQUEST_SIZE); + if (msg == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + return 1; + } + + memset(msg, 0, IPMI_OPEN_SESSION_REQUEST_SIZE); + + msg[0] = 0; /* Message tag */ + if (ipmi_oem_active(intf, "intelplus") || session->privlvl != IPMI_SESSION_PRIV_ADMIN) + msg[1] = session->privlvl; + else + msg[1] = 0; /* Give us highest privlg level based on supported algorithms */ + msg[2] = 0; /* reserved */ + msg[3] = 0; /* reserved */ + + /* Choose our session ID for easy recognition in the packet dump */ + session->v2_data.console_id = 0xA0A2A3A4; + msg[4] = session->v2_data.console_id & 0xff; + msg[5] = (session->v2_data.console_id >> 8) & 0xff; + msg[6] = (session->v2_data.console_id >> 16) & 0xff; + msg[7] = (session->v2_data.console_id >> 24) & 0xff; + + + if (lanplus_get_requested_ciphers(session->cipher_suite_id, + &(session->v2_data.requested_auth_alg), + &(session->v2_data.requested_integrity_alg), + &(session->v2_data.requested_crypt_alg))) + { + lprintf(LOG_WARNING, "Unsupported cipher suite ID : %d\n", + session->cipher_suite_id); + free(msg); + msg = NULL; + return 1; + } + + + /* + * Authentication payload + */ + msg[8] = 0; /* specifies authentication payload */ + msg[9] = 0; /* reserved */ + msg[10] = 0; /* reserved */ + msg[11] = 8; /* payload length */ + msg[12] = session->v2_data.requested_auth_alg; + msg[13] = 0; /* reserved */ + msg[14] = 0; /* reserved */ + msg[15] = 0; /* reserved */ + + /* + * Integrity payload + */ + msg[16] = 1; /* specifies integrity payload */ + msg[17] = 0; /* reserved */ + msg[18] = 0; /* reserved */ + msg[19] = 8; /* payload length */ + msg[20] = session->v2_data.requested_integrity_alg; + msg[21] = 0; /* reserved */ + msg[22] = 0; /* reserved */ + msg[23] = 0; /* reserved */ + + /* + * Confidentiality/Encryption payload + */ + msg[24] = 2; /* specifies confidentiality payload */ + msg[25] = 0; /* reserved */ + msg[26] = 0; /* reserved */ + msg[27] = 8; /* payload length */ + msg[28] = session->v2_data.requested_crypt_alg; + msg[29] = 0; /* reserved */ + msg[30] = 0; /* reserved */ + msg[31] = 0; /* reserved */ + + + v2_payload.payload_type = IPMI_PAYLOAD_TYPE_RMCP_OPEN_REQUEST; + v2_payload.payload_length = IPMI_OPEN_SESSION_REQUEST_SIZE; + v2_payload.payload.open_session_request.request = msg; + + rsp = ipmi_lanplus_send_payload(intf, &v2_payload); + + free(msg); + msg = NULL; + if (rsp == NULL ) { + lprintf(LOG_DEBUG, "Timeout in open session response message."); + return 2; + } + if (verbose) + lanplus_dump_open_session_response(rsp); + + if (rsp->payload.open_session_response.rakp_return_code != + IPMI_RAKP_STATUS_NO_ERRORS) + { + lprintf(LOG_WARNING, "Error in open session response message : %s\n", + val2str(rsp->payload.open_session_response.rakp_return_code, + ipmi_rakp_return_codes)); + return 1; + } + else + { + if (rsp->payload.open_session_response.console_id != + session->v2_data.console_id) { + lprintf(LOG_WARNING, "Warning: Console session ID is not " + "what we requested"); + } + + session->v2_data.max_priv_level = + rsp->payload.open_session_response.max_priv_level; + session->v2_data.bmc_id = + rsp->payload.open_session_response.bmc_id; + session->v2_data.auth_alg = + rsp->payload.open_session_response.auth_alg; + session->v2_data.integrity_alg = + rsp->payload.open_session_response.integrity_alg; + session->v2_data.crypt_alg = + rsp->payload.open_session_response.crypt_alg; + session->v2_data.session_state = + LANPLUS_STATE_OPEN_SESSION_RECEIEVED; + + + /* + * Verify that we have agreed on a cipher suite + */ + if (rsp->payload.open_session_response.auth_alg != + session->v2_data.requested_auth_alg) + { + lprintf(LOG_WARNING, "Authentication algorithm 0x%02x is " + "not what we requested 0x%02x\n", + rsp->payload.open_session_response.auth_alg, + session->v2_data.requested_auth_alg); + rc = 1; + } + else if (rsp->payload.open_session_response.integrity_alg != + session->v2_data.requested_integrity_alg) + { + lprintf(LOG_WARNING, "Integrity algorithm 0x%02x is " + "not what we requested 0x%02x\n", + rsp->payload.open_session_response.integrity_alg, + session->v2_data.requested_integrity_alg); + rc = 1; + } + else if (rsp->payload.open_session_response.crypt_alg != + session->v2_data.requested_crypt_alg) + { + lprintf(LOG_WARNING, "Encryption algorithm 0x%02x is " + "not what we requested 0x%02x\n", + rsp->payload.open_session_response.crypt_alg, + session->v2_data.requested_crypt_alg); + rc = 1; + } + + } + + return rc; +} + + + +/* + * ipmi_lanplus_rakp1 + * + * Build and send the RAKP 1 message as part of the IPMI v2 / RMCP+ session + * negotiation protocol. We also read and validate the RAKP 2 message received + * from the BMC, here. See section 13.20 of the IPMI v2 specification for + * details. + * + * returns 0 on success + * 1 on failure + * + * Note that failure is only indicated if we have an internal error of + * some kind. If we actually get a RAKP 2 message in response to our + * RAKP 1 message, any errors will be stored in + * session->v2_data.rakp2_return_code and sent to the BMC in the RAKP + * 3 message. + */ +static int +ipmi_lanplus_rakp1(struct ipmi_intf * intf) +{ + struct ipmi_v2_payload v2_payload; + struct ipmi_session * session = intf->session; + uint8_t * msg; + struct ipmi_rs * rsp; + int rc = 0; /* 0 = success, 1 = error, 2 = timeout */ + + /* + * Build a RAKP 1 message + */ + msg = (uint8_t*)malloc(IPMI_RAKP1_MESSAGE_SIZE); + if (msg == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + return 1; + } + memset(msg, 0, IPMI_RAKP1_MESSAGE_SIZE); + + + msg[0] = 0; /* Message tag */ + + msg[1] = 0; /* reserved */ + msg[2] = 0; /* reserved */ + msg[3] = 0; /* reserved */ + + /* BMC session ID */ + msg[4] = session->v2_data.bmc_id & 0xff; + msg[5] = (session->v2_data.bmc_id >> 8) & 0xff; + msg[6] = (session->v2_data.bmc_id >> 16) & 0xff; + msg[7] = (session->v2_data.bmc_id >> 24) & 0xff; + + + /* We need a 16 byte random number */ + if (lanplus_rand(session->v2_data.console_rand, 16)) + { + // ERROR; + lprintf(LOG_ERR, "ERROR generating random number " + "in ipmi_lanplus_rakp1"); + free(msg); + msg = NULL; + return 1; + } + memcpy(msg + 8, session->v2_data.console_rand, 16); + #if WORDS_BIGENDIAN + lanplus_swap(msg + 8, 16); + #endif + + if (verbose > 1) + printbuf(session->v2_data.console_rand, 16, + ">> Console generated random number"); + + + /* + * Requested maximum privilege level. + */ + msg[24] = session->privlvl | session->v2_data.lookupbit; + session->v2_data.requested_role = msg[24]; + msg[25] = 0; /* reserved */ + msg[26] = 0; /* reserved */ + + + /* Username specification */ + msg[27] = strlen((const char *)session->username); + if (msg[27] > IPMI_MAX_USER_NAME_LENGTH) + { + lprintf(LOG_ERR, "ERROR: user name too long. " + "(Exceeds %d characters)", + IPMI_MAX_USER_NAME_LENGTH); + free(msg); + msg = NULL; + return 1; + } + memcpy(msg + 28, session->username, msg[27]); + + v2_payload.payload_type = IPMI_PAYLOAD_TYPE_RAKP_1; + v2_payload.payload_length = + IPMI_RAKP1_MESSAGE_SIZE - (16 - msg[27]); + v2_payload.payload.rakp_1_message.message = msg; + + rsp = ipmi_lanplus_send_payload(intf, &v2_payload); + + free(msg); + msg = NULL; + + if (rsp == NULL) + { + lprintf(LOG_WARNING, "> Error: no response from RAKP 1 message"); + return 2; + } + + session->v2_data.session_state = LANPLUS_STATE_RAKP_2_RECEIVED; + + if (verbose) + lanplus_dump_rakp2_message(rsp, session->v2_data.auth_alg); + + + + if (rsp->payload.rakp2_message.rakp_return_code != IPMI_RAKP_STATUS_NO_ERRORS) + { + lprintf(LOG_INFO, "RAKP 2 message indicates an error : %s", + val2str(rsp->payload.rakp2_message.rakp_return_code, + ipmi_rakp_return_codes)); + rc = 1; + } + + else + { + memcpy(session->v2_data.bmc_rand, rsp->payload.rakp2_message.bmc_rand, 16); + memcpy(session->v2_data.bmc_guid, rsp->payload.rakp2_message.bmc_guid, 16); + + if (verbose > 2) + printbuf(session->v2_data.bmc_rand, 16, "bmc_rand"); + + /* + * It is at this point that we have to decode the random number and determine + * whether the BMC has authenticated. + */ + if (! lanplus_rakp2_hmac_matches(session, + rsp->payload.rakp2_message.key_exchange_auth_code, + intf)) + { + /* Error */ + lprintf(LOG_INFO, "> RAKP 2 HMAC is invalid"); + session->v2_data.rakp2_return_code = IPMI_RAKP_STATUS_INVALID_INTEGRITY_CHECK_VALUE; + rc = 1; + } + else + { + /* Success */ + session->v2_data.rakp2_return_code = IPMI_RAKP_STATUS_NO_ERRORS; + } + } + + return rc; +} + + + +/* + * ipmi_lanplus_rakp3 + * + * Build and send the RAKP 3 message as part of the IPMI v2 / RMCP+ session + * negotiation protocol. We also read and validate the RAKP 4 message received + * from the BMC, here. See section 13.20 of the IPMI v2 specification for + * details. + * + * If the RAKP 2 return code is not IPMI_RAKP_STATUS_NO_ERRORS, we will + * exit with an error code immediately after sendint the RAKP 3 message. + * + * param intf is the intf that holds all the state we are concerned with + * + * returns 0 on success + * 1 on failure + */ +static int +ipmi_lanplus_rakp3(struct ipmi_intf * intf) +{ + struct ipmi_v2_payload v2_payload; + struct ipmi_session * session = intf->session; + uint8_t * msg; + struct ipmi_rs * rsp; + + assert(session->v2_data.session_state == LANPLUS_STATE_RAKP_2_RECEIVED); + + /* + * Build a RAKP 3 message + */ + msg = (uint8_t*)malloc(IPMI_RAKP3_MESSAGE_MAX_SIZE); + if (msg == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + return 1; + } + memset(msg, 0, IPMI_RAKP3_MESSAGE_MAX_SIZE); + + + msg[0] = 0; /* Message tag */ + msg[1] = session->v2_data.rakp2_return_code; + + msg[2] = 0; /* reserved */ + msg[3] = 0; /* reserved */ + + /* BMC session ID */ + msg[4] = session->v2_data.bmc_id & 0xff; + msg[5] = (session->v2_data.bmc_id >> 8) & 0xff; + msg[6] = (session->v2_data.bmc_id >> 16) & 0xff; + msg[7] = (session->v2_data.bmc_id >> 24) & 0xff; + + v2_payload.payload_type = IPMI_PAYLOAD_TYPE_RAKP_3; + v2_payload.payload_length = 8; + v2_payload.payload.rakp_3_message.message = msg; + + /* + * If the rakp2 return code indicates and error, we don't have to + * generate an authcode or session integrity key. In that case, we + * are simply sending a RAKP 3 message to indicate to the BMC that the + * RAKP 2 message caused an error. + */ + if (session->v2_data.rakp2_return_code == IPMI_RAKP_STATUS_NO_ERRORS) + { + uint32_t auth_length; + + if (lanplus_generate_rakp3_authcode(msg + 8, session, &auth_length, intf)) + { + /* Error */ + lprintf(LOG_INFO, "> Error generating RAKP 3 authcode"); + free(msg); + msg = NULL; + return 1; + } + else + { + /* Success */ + v2_payload.payload_length += auth_length; + } + + /* Generate our Session Integrity Key, K1, and K2 */ + if (lanplus_generate_sik(session, intf)) + { + /* Error */ + lprintf(LOG_INFO, "> Error generating session integrity key"); + free(msg); + msg = NULL; + return 1; + } + else if (lanplus_generate_k1(session)) + { + /* Error */ + lprintf(LOG_INFO, "> Error generating K1 key"); + free(msg); + msg = NULL; + return 1; + } + else if (lanplus_generate_k2(session)) + { + /* Error */ + lprintf(LOG_INFO, "> Error generating K1 key"); + free(msg); + msg = NULL; + return 1; + } + } + + + rsp = ipmi_lanplus_send_payload(intf, &v2_payload); + + free(msg); + msg = NULL; + + if (session->v2_data.rakp2_return_code != IPMI_RAKP_STATUS_NO_ERRORS) + { + /* + * If the previous RAKP 2 message received was deemed erroneous, + * we have nothing else to do here. We only sent the RAKP 3 message + * to indicate to the BMC that the RAKP 2 message failed. + */ + return 1; + } + else if (rsp == NULL) + { + lprintf(LOG_WARNING, "> Error: no response from RAKP 3 message"); + return 2; + } + + + /* + * We have a RAKP 4 message to chew on. + */ + if (verbose) + lanplus_dump_rakp4_message(rsp, session->v2_data.auth_alg); + + + if (rsp->payload.open_session_response.rakp_return_code != IPMI_RAKP_STATUS_NO_ERRORS) + { + lprintf(LOG_INFO, "RAKP 4 message indicates an error : %s", + val2str(rsp->payload.rakp4_message.rakp_return_code, + ipmi_rakp_return_codes)); + return 1; + } + + else + { + /* Validate the authcode */ + if (lanplus_rakp4_hmac_matches(session, + rsp->payload.rakp4_message.integrity_check_value, + intf)) + { + /* Success */ + session->v2_data.session_state = LANPLUS_STATE_ACTIVE; + } + else + { + /* Error */ + lprintf(LOG_INFO, "> RAKP 4 message has invalid integrity check value"); + return 1; + } + } + + intf->abort = 0; + return 0; +} + + + +/** + * ipmi_lan_close + */ +void +ipmi_lanplus_close(struct ipmi_intf * intf) +{ + if (!intf->abort) + ipmi_close_session_cmd(intf); + + if (intf->fd >= 0) + close(intf->fd); + + ipmi_req_clear_entries(); + + if (intf->session) { + free(intf->session); + intf->session = NULL; + } + + intf->session = NULL; + intf->opened = 0; + intf->manufacturer_id = IPMI_OEM_UNKNOWN; + intf = NULL; +} + + + +static int +ipmi_set_session_privlvl_cmd(struct ipmi_intf * intf) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t backupBridgePossible; + uint8_t privlvl = intf->session->privlvl; + + if (privlvl <= IPMI_SESSION_PRIV_USER) + return 0; /* no need to set higher */ + + backupBridgePossible = bridgePossible; + + bridgePossible = 0; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_APP; + req.msg.cmd = 0x3b; + req.msg.data = &privlvl; + req.msg.data_len = 1; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Set Session Privilege Level to %s failed", + val2str(privlvl, ipmi_privlvl_vals)); + bridgePossible = backupBridgePossible; + return -1; + } + if (verbose > 2) + printbuf(rsp->data, rsp->data_len, "set_session_privlvl"); + + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Set Session Privilege Level to %s failed: %s", + val2str(privlvl, ipmi_privlvl_vals), + val2str(rsp->ccode, completion_code_vals)); + bridgePossible = backupBridgePossible; + return -1; + } + + lprintf(LOG_DEBUG, "Set Session Privilege Level to %s\n", + val2str(rsp->data[0], ipmi_privlvl_vals)); + + bridgePossible = backupBridgePossible; + + return 0; +} + +/** + * ipmi_lanplus_open + */ +int +ipmi_lanplus_open(struct ipmi_intf * intf) +{ + int rc; + int retry; + struct get_channel_auth_cap_rsp auth_cap; + struct ipmi_session *session; + + if (!intf || !intf->session) + return -1; + session = intf->session; + + + if (!session->port) + session->port = IPMI_LANPLUS_PORT; + if (!session->privlvl) + session->privlvl = IPMI_SESSION_PRIV_ADMIN; + if (!session->timeout) + session->timeout = IPMI_LAN_TIMEOUT; + if (!session->retry) + session->retry = IPMI_LAN_RETRY; + + if (session->hostname == NULL || strlen((const char *)session->hostname) == 0) { + lprintf(LOG_ERR, "No hostname specified!"); + return -1; + } + + intf->abort = 1; + + + /* Setup our lanplus session state */ + session->v2_data.auth_alg = IPMI_AUTH_RAKP_NONE; + session->v2_data.crypt_alg = IPMI_CRYPT_NONE; + session->v2_data.console_id = 0x00; + session->v2_data.bmc_id = 0x00; + session->sol_data.sequence_number = 1; + //session->sol_data.last_received_sequence_number = 0; + //session->sol_data.last_received_byte_count = 0; + memset(session->v2_data.sik, 0, IPMI_SIK_BUFFER_SIZE); + + /* Kg is set in ipmi_intf */ + //memset(session->v2_data.kg, 0, IPMI_KG_BUFFER_SIZE); + + if (ipmi_intf_socket_connect (intf) == -1) { + lprintf(LOG_ERR, "Could not open socket!"); + return -1; + } + + if (intf->fd < 0) { + lperror(LOG_ERR, "Connect to %s failed", + session->hostname); + intf->close(intf); + return -1; + } + + intf->opened = 1; + + /* + * + * Make sure the BMC supports IPMI v2 / RMCP+ + */ + if (!ipmi_oem_active(intf, "i82571spt") && + ipmi_get_auth_capabilities_cmd(intf, &auth_cap)) { + lprintf(LOG_INFO, "Error issuing Get Channel " + "Authentication Capabilies request"); + goto fail; + } + + if (!ipmi_oem_active(intf, "i82571spt") && ! auth_cap.v20_data_available) + { + lprintf(LOG_INFO, "This BMC does not support IPMI v2 / RMCP+"); + goto fail; + } + + /* + * If the open/rakp1/rakp3 sequence encounters a timeout, the whole sequence + * needs to restart. The individual messages are not individually retryable, + * as the session state is advancing. + */ + for (retry = 0; retry < IPMI_LAN_RETRY; retry++) { + session->v2_data.session_state = LANPLUS_STATE_PRESESSION; + /* + * Open session + */ + if ((rc = ipmi_lanplus_open_session(intf)) == 1) { + intf->close(intf); + goto fail; + } + if (rc == 2) { + lprintf(LOG_DEBUG, "Retry lanplus open session, %d", retry); + continue; + } + /* + * RAKP 1 + */ + if ((rc = ipmi_lanplus_rakp1(intf)) == 1) { + intf->close(intf); + goto fail; + } + if (rc == 2) { + lprintf(LOG_DEBUG, "Retry lanplus rakp1, %d", retry); + continue; + } + /* + * RAKP 3 + */ + if ((rc = ipmi_lanplus_rakp3(intf)) == 1) { + intf->close(intf); + goto fail; + } + if (rc == 0) break; + lprintf(LOG_DEBUG,"Retry lanplus rakp3, %d", retry); + } + + lprintf(LOG_DEBUG, "IPMIv2 / RMCP+ SESSION OPENED SUCCESSFULLY\n"); + + if (!ipmi_oem_active(intf, "i82571spt")) { + rc = ipmi_set_session_privlvl_cmd(intf); + if (rc < 0) { + intf->close(intf); + goto fail; + } + } + intf->manufacturer_id = ipmi_get_oem(intf); + bridgePossible = 1; + + /* automatically detect interface request and response sizes */ + hpm2_detect_max_payload_size(intf); + + return intf->fd; + + fail: + lprintf(LOG_ERR, "Error: Unable to establish IPMI v2 / RMCP+ session"); + intf->opened = 0; + return -1; +} + + + +void test_crypt1(void) +{ + uint8_t key[] = + {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, + 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14}; + + uint16_t bytes_encrypted; + uint16_t bytes_decrypted; + uint8_t decrypt_buffer[1000]; + uint8_t encrypt_buffer[1000]; + + uint8_t data[] = + {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, + 0x11, 0x12}; + + printbuf(data, sizeof(data), "original data"); + + if (lanplus_encrypt_payload(IPMI_CRYPT_AES_CBC_128, + key, + data, + sizeof(data), + encrypt_buffer, + &bytes_encrypted)) + { + lprintf(LOG_ERR, "Encrypt test failed"); + assert(0); + } + printbuf(encrypt_buffer, bytes_encrypted, "encrypted payload"); + + + if (lanplus_decrypt_payload(IPMI_CRYPT_AES_CBC_128, + key, + encrypt_buffer, + bytes_encrypted, + decrypt_buffer, + &bytes_decrypted)) + { + lprintf(LOG_ERR, "Decrypt test failed\n"); + assert(0); + } + printbuf(decrypt_buffer, bytes_decrypted, "decrypted payload"); + + lprintf(LOG_DEBUG, "\nDone testing the encrypt/decyrpt methods!\n"); + exit(0); +} + + + +void test_crypt2(void) +{ + uint8_t key[] = + {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, + 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14}; + uint8_t iv[] = + {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, + 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14}; + uint8_t data[8] = "12345678"; + + uint8_t encrypt_buffer[1000]; + uint8_t decrypt_buffer[1000]; + uint32_t bytes_encrypted; + uint32_t bytes_decrypted; + + printbuf((const uint8_t *)data, strlen((const char *)data), "input data"); + + lanplus_encrypt_aes_cbc_128(iv, + key, + data, + strlen((const char *)data), + encrypt_buffer, + &bytes_encrypted); + printbuf((const uint8_t *)encrypt_buffer, bytes_encrypted, "encrypt_buffer"); + + lanplus_decrypt_aes_cbc_128(iv, + key, + encrypt_buffer, + bytes_encrypted, + decrypt_buffer, + &bytes_decrypted); + printbuf((const uint8_t *)decrypt_buffer, bytes_decrypted, "decrypt_buffer"); + + lprintf(LOG_INFO, "\nDone testing the encrypt/decyrpt methods!\n"); + exit(0); +} + + +/** + * send a get device id command to keep session active + */ +static int +ipmi_lanplus_keepalive(struct ipmi_intf * intf) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req = { msg: { + netfn: IPMI_NETFN_APP, + cmd: 1, + }}; + + if (!intf->opened) + return 0; + + rsp = intf->sendrecv(intf, &req); + while (rsp != NULL && is_sol_packet(rsp)) { + /* rsp was SOL data instead of our answer */ + /* since it didn't go through the sol recv, do sol recv stuff here */ + ack_sol_packet(intf, rsp); + check_sol_packet_for_new_data(intf, rsp); + if (rsp->data_len) + intf->session->sol_data.sol_input_handler(rsp); + rsp = ipmi_lan_poll_recv(intf); + if (rsp == NULL) /* the get device id answer never got back, but retry mechanism was bypassed by SOL data */ + return 0; /* so get device id command never returned, the connection is still alive */ + } + + if (rsp == NULL) + return -1; + if (rsp->ccode > 0) + return -1; + + return 0; +} + + +/** + * ipmi_lanplus_setup + */ +static int ipmi_lanplus_setup(struct ipmi_intf * intf) +{ + //test_crypt1(); + assert("ipmi_lanplus_setup"); + + if (lanplus_seed_prng(16)) + return -1; + + intf->session = malloc(sizeof(struct ipmi_session)); + if (intf->session == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + return -1; + } + memset(intf->session, 0, sizeof(struct ipmi_session)); + + /* setup default LAN maximum request and response sizes */ + intf->max_request_data_size = IPMI_LAN_MAX_REQUEST_SIZE; + intf->max_response_data_size = IPMI_LAN_MAX_RESPONSE_SIZE; + + return 0; +} + +static void ipmi_lanp_set_max_rq_data_size(struct ipmi_intf * intf, uint16_t size) +{ + if (intf->session->cipher_suite_id == 3) { + /* + * encrypted payload can only be multiple of 16 bytes + */ + size &= ~15; + + /* + * decrement payload size on confidentiality header size + * plus minimal confidentiality trailer size + */ + size -= (16 + 1); + } + + intf->max_request_data_size = size; +} + +static void ipmi_lanp_set_max_rp_data_size(struct ipmi_intf * intf, uint16_t size) +{ + if (intf->session->cipher_suite_id == 3) { + /* + * encrypted payload can only be multiple of 16 bytes + */ + size &= ~15; + + /* + * decrement payload size on confidentiality header size + * plus minimal confidentiality trailer size + */ + size -= (16 + 1); + } + + intf->max_response_data_size = size; +} diff --git a/src/plugins/lanplus/lanplus.h b/src/plugins/lanplus/lanplus.h new file mode 100644 index 0000000..4b6ae1e --- /dev/null +++ b/src/plugins/lanplus/lanplus.h @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#ifndef IPMI_LANPLUS_H +#define IPMI_LANPLUS_H + +#include <ipmitool/ipmi.h> + +#define IPMI_LANPLUS_PORT 0x26f + +/* + * RAKP return codes. These values come from table 13-15 of the IPMI v2 + * specification. + */ +#define IPMI_RAKP_STATUS_NO_ERRORS 0x00 +#define IPMI_RAKP_STATUS_INSUFFICIENT_RESOURCES_FOR_SESSION 0x01 +#define IPMI_RAKP_STATUS_INVALID_SESSION_ID 0x02 +#define IPMI_RAKP_STATUS_INVALID_PAYLOAD_TYPE 0x03 +#define IPMI_RAKP_STATUS_INVALID_AUTHENTICATION_ALGORITHM 0x04 +#define IPMI_RAKP_STATUS_INVALID_INTEGRITTY_ALGORITHM 0x05 +#define IPMI_RAKP_STATUS_NO_MATCHING_AUTHENTICATION_PAYLOAD 0x06 +#define IPMI_RAKP_STATUS_NO_MATCHING_INTEGRITY_PAYLOAD 0x07 +#define IPMI_RAKP_STATUS_INACTIVE_SESSION_ID 0x08 +#define IPMI_RAKP_STATUS_INVALID_ROLE 0x09 +#define IPMI_RAKP_STATUS_UNAUTHORIZED_ROLE_REQUESTED 0x0A +#define IPMI_RAKP_STATUS_INSUFFICIENT_RESOURCES_FOR_ROLE 0x0B +#define IPMI_RAKP_STATUS_INVALID_NAME_LENGTH 0x0C +#define IPMI_RAKP_STATUS_UNAUTHORIZED_NAME 0x0D +#define IPMI_RAKP_STATUS_UNAUTHORIZED_GUID 0x0E +#define IPMI_RAKP_STATUS_INVALID_INTEGRITY_CHECK_VALUE 0x0F +#define IPMI_RAKP_STATUS_INVALID_CONFIDENTIALITY_ALGORITHM 0x10 +#define IPMI_RAKP_STATUS_NO_CIPHER_SUITE_MATCH 0x11 +#define IPMI_RAKP_STATUS_ILLEGAL_PARAMTER 0x12 + + +#define IPMI_LAN_CHANNEL_1 0x07 +#define IPMI_LAN_CHANNEL_2 0x06 +#define IPMI_LAN_CHANNEL_E 0x0e + +#define IPMI_LAN_TIMEOUT 1 +#define IPMI_LAN_RETRY 4 + +#define IPMI_PRIV_CALLBACK 1 +#define IPMI_PRIV_USER 2 +#define IPMI_PRIV_OPERATOR 3 +#define IPMI_PRIV_ADMIN 4 +#define IPMI_PRIV_OEM 5 + + +#define IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE 0x10 + + +/* Session message offsets, from table 13-8 of the v2 specification */ +#define IPMI_LANPLUS_OFFSET_AUTHTYPE 0x04 +#define IPMI_LANPLUS_OFFSET_PAYLOAD_TYPE 0x05 +#define IPMI_LANPLUS_OFFSET_SESSION_ID 0x06 +#define IPMI_LANPLUS_OFFSET_SEQUENCE_NUM 0x0A +#define IPMI_LANPLUS_OFFSET_PAYLOAD_SIZE 0x0E +#define IPMI_LANPLUS_OFFSET_PAYLOAD 0x10 + + +#define IPMI_GET_CHANNEL_AUTH_CAP 0x38 + +/* + * TODO: these are wild guesses and should be checked + */ +#define IPMI_MAX_CONF_HEADER_SIZE 0x20 +#define IPMI_MAX_PAYLOAD_SIZE 0xFFFF /* Includes confidentiality header/trailer */ +#define IPMI_MAX_CONF_TRAILER_SIZE 0x20 +#define IPMI_MAX_INTEGRITY_PAD_SIZE 0x20 +#define IPMI_MAX_AUTH_CODE_SIZE 0x20 + +#define IPMI_REQUEST_MESSAGE_SIZE 0x07 +#define IPMI_MAX_MAC_SIZE 0x14 /* The largest mac we ever expect to generate */ +#define IPMI_SHA1_AUTHCODE_SIZE 0x0C + +/* + *This is accurate, as long as we're only passing 1 auth algorithm, + * one integrity algorithm, and 1 encyrption alogrithm + */ +#define IPMI_OPEN_SESSION_REQUEST_SIZE 32 +#define IPMI_RAKP1_MESSAGE_SIZE 44 +#define IPMI_RAKP3_MESSAGE_MAX_SIZE 28 + +#define IPMI_MAX_USER_NAME_LENGTH 16 + +extern const struct valstr ipmi_privlvl_vals[]; +extern const struct valstr ipmi_authtype_vals[]; + +extern struct ipmi_intf ipmi_lanplus_intf; + +struct ipmi_rs * ipmi_lan_send_cmd(struct ipmi_intf * intf, struct ipmi_rq * req); +int ipmi_lanplus_open(struct ipmi_intf * intf); +void ipmi_lanplus_close(struct ipmi_intf * intf); +int ipmiv2_lan_ping(struct ipmi_intf * intf); + +#endif /*IPMI_LAN_H*/ diff --git a/src/plugins/lanplus/lanplus_crypt.c b/src/plugins/lanplus/lanplus_crypt.c new file mode 100644 index 0000000..54fd5cb --- /dev/null +++ b/src/plugins/lanplus/lanplus_crypt.c @@ -0,0 +1,934 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include <assert.h> +#include <string.h> +#if defined(HAVE_CONFIG_H) +# include <config.h> +#endif +#include <ipmitool/bswap.h> +#include <ipmitool/log.h> +#include "lanplus.h" +#include "lanplus_crypt.h" +#include "lanplus_crypt_impl.h" + + + +/* + * lanplus_rakp2_hmac_matches + * + * param session holds all the state data that we need to generate the hmac + * param hmac is the HMAC sent by the BMC in the RAKP 2 message + * + * The HMAC was generated [per RFC2404] from : + * + * SIDm - Remote console session ID + * SIDc - BMC session ID + * Rm - Remote console random number + * Rc - BMC random number + * GUIDc - BMC guid + * ROLEm - Requested privilege level (entire byte) + * ULENGTHm - Username length + * <UNAMEm> - Username (absent for null user names) + * + * generated by using Kuid. I am aware that the subscripts on the values + * look backwards, but that's the way they are written in the specification. + * + * If the authentication algorithm is IPMI_AUTH_RAKP_NONE, we return success. + * + * return 0 on success (the authcode matches) + * 1 on failure (the authcode does not match) + */ +int +lanplus_rakp2_hmac_matches(const struct ipmi_session * session, + const uint8_t * bmc_mac, struct ipmi_intf * intf) +{ + uint8_t * buffer; + int bufferLength, i; + uint8_t mac[20]; + uint32_t macLength; + + uint32_t SIDm_lsbf, SIDc_lsbf; + + + if (session->v2_data.auth_alg == IPMI_AUTH_RAKP_NONE) + return 1; + + /* We don't yet support other algorithms */ + assert(session->v2_data.auth_alg == IPMI_AUTH_RAKP_HMAC_SHA1); + + + bufferLength = + 4 + /* SIDm */ + 4 + /* SIDc */ + 16 + /* Rm */ + 16 + /* Rc */ + 16 + /* GUIDc */ + 1 + /* ROLEm */ + 1 + /* ULENGTHm */ + strlen((const char *)session->username); /* optional */ + + buffer = malloc(bufferLength); + if (buffer == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + return 1; + } + + /* + * Fill the buffer. I'm assuming that we're using the LSBF representation of the + * multibyte numbers in use. + */ + + /* SIDm */ + SIDm_lsbf = session->v2_data.console_id; + #if WORDS_BIGENDIAN + SIDm_lsbf = BSWAP_32(SIDm_lsbf); + #endif + + memcpy(buffer, &SIDm_lsbf, 4); + + /* SIDc */ + SIDc_lsbf = session->v2_data.bmc_id; + #if WORDS_BIGENDIAN + SIDc_lsbf = BSWAP_32(SIDc_lsbf); + #endif + memcpy(buffer + 4, &SIDc_lsbf, 4); + + /* Rm */ + #if WORDS_BIGENDIAN + for (i = 0; i < 16; ++i) + buffer[8 + i] = session->v2_data.console_rand[16 - 1 - i]; + #else + for (i = 0; i < 16; ++i) + buffer[8 + i] = session->v2_data.console_rand[i]; + #endif + + /* Rc */ + #if WORDS_BIGENDIAN + for (i = 0; i < 16; ++i) + buffer[24 + i] = session->v2_data.bmc_rand[16 - 1 - i]; + #else + for (i = 0; i < 16; ++i) + buffer[24 + i] = session->v2_data.bmc_rand[i]; + #endif + + /* GUIDc */ + #if WORDS_BIGENDIAN + for (i = 0; i < 16; ++i) + buffer[40 + i] = session->v2_data.bmc_guid[16 - 1 - i]; + #else + for (i = 0; i < 16; ++i) + buffer[40 + i] = session->v2_data.bmc_guid[i]; + #endif + + /* ROLEm */ + buffer[56] = session->v2_data.requested_role; + + if (ipmi_oem_active(intf, "i82571spt")) { + /* + * The HMAC calculation code in the Intel 82571 GbE + * skips this bit! Looks like a GbE bug, but we need + * to work around it here anyway... + */ + buffer[56] &= ~0x10; + } + + /* ULENGTHm */ + buffer[57] = strlen((const char *)session->username); + + /* UserName [optional] */ + for (i = 0; i < buffer[57]; ++i) + buffer[58 + i] = session->username[i]; + + if (verbose > 2) + { + printbuf((const uint8_t *)buffer, bufferLength, ">> rakp2 mac input buffer"); + printbuf((const uint8_t *)session->authcode, IPMI_AUTHCODE_BUFFER_SIZE, ">> rakp2 mac key"); + } + + /* + * The buffer is complete. Let's hash. + */ + lanplus_HMAC(session->v2_data.auth_alg, + session->authcode, + IPMI_AUTHCODE_BUFFER_SIZE, + buffer, + bufferLength, + mac, + &macLength); + + free(buffer); + buffer = NULL; + + + if (verbose > 2) + { + printbuf(mac, macLength, ">> rakp2 mac as computed by the remote console"); + } + + return (memcmp(bmc_mac, mac, macLength) == 0); +} + + + +/* + * lanplus_rakp4_hmac_matches + * + * param session holds all the state data that we need to generate the hmac + * param hmac is the HMAC sent by the BMC in the RAKP 4 message + * + * The HMAC was generated [per RFC2404] from : + * + * Rm - Remote console random number + * SIDc - BMC session ID + * GUIDc - BMC guid + * + * generated by using SIK (the session integrity key). I am aware that the + * subscripts on the values look backwards, but that's the way they are + * written in the specification. + * + * If the authentication algorithm is IPMI_AUTH_RAKP_NONE, we return success. + * + * return 1 on success (the authcode matches) + * 0 on failure (the authcode does not match) + * + */ +int +lanplus_rakp4_hmac_matches(const struct ipmi_session * session, + const uint8_t * bmc_mac, struct ipmi_intf * intf) +{ + uint8_t * buffer; + int bufferLength, i; + uint8_t mac[20]; + uint32_t macLength; + uint32_t SIDc_lsbf; + + if (ipmi_oem_active(intf, "intelplus")){ + /* Intel BMC responds with the integrity Algorithm in RAKP4 */ + if (session->v2_data.integrity_alg == IPMI_INTEGRITY_NONE) + return 1; + + /* We don't yet support other algorithms */ + assert(session->v2_data.integrity_alg == IPMI_INTEGRITY_HMAC_SHA1_96); + } else { + if (session->v2_data.auth_alg == IPMI_AUTH_RAKP_NONE) + return 1; + + /* We don't yet support other algorithms */ + assert(session->v2_data.auth_alg == IPMI_AUTH_RAKP_HMAC_SHA1); + } + + bufferLength = + 16 + /* Rm */ + 4 + /* SIDc */ + 16; /* GUIDc */ + + buffer = (uint8_t *)malloc(bufferLength); + if (buffer == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + return 1; + } + + /* + * Fill the buffer. I'm assuming that we're using the LSBF representation of the + * multibyte numbers in use. + */ + + /* Rm */ + #if WORDS_BIGENDIAN + for (i = 0; i < 16; ++i) + buffer[i] = session->v2_data.console_rand[16 - 1 - i]; + #else + for (i = 0; i < 16; ++i) + buffer[i] = session->v2_data.console_rand[i]; + #endif + + + /* SIDc */ + SIDc_lsbf = session->v2_data.bmc_id; + #if WORDS_BIGENDIAN + SIDc_lsbf = BSWAP_32(SIDc_lsbf); + #endif + memcpy(buffer + 16, &SIDc_lsbf, 4); + + + /* GUIDc */ + #if WORDS_BIGENDIAN + for (i = 0; i < 16; ++i) + buffer[i + 20] = session->v2_data.bmc_guid[16 - 1 - i]; + #else + for (i = 0; i < 16; ++i) + buffer[i + 20] = session->v2_data.bmc_guid[i]; + #endif + + + if (verbose > 2) + { + printbuf((const uint8_t *)buffer, bufferLength, ">> rakp4 mac input buffer"); + printbuf(session->v2_data.sik, 20l, ">> rakp4 mac key (sik)"); + } + + + /* + * The buffer is complete. Let's hash. + */ + lanplus_HMAC((ipmi_oem_active(intf, "intelplus")) + ? session->v2_data.integrity_alg + : session->v2_data.auth_alg , + session->v2_data.sik, + IPMI_SIK_BUFFER_SIZE, + buffer, + bufferLength, + mac, + &macLength); + + if (verbose > 2) + { + printbuf(bmc_mac, macLength, ">> rakp4 mac as computed by the BMC"); + printbuf(mac, macLength, ">> rakp4 mac as computed by the remote console"); + } + + + + free(buffer); + buffer = NULL; + assert(macLength == 20); + return (memcmp(bmc_mac, mac, 12) == 0); +} + + + +/* + * lanplus_generate_rakp3_auth_code + * + * This auth code is an HMAC generated with : + * + * Rc - BMC random number + * SIDm - Console session ID + * ROLEm - Requested privilege level (entire byte) + * ULENGTHm - Username length + * <USERNAME> - Usename (absent for null usernames) + * + * The key used to generated the MAC is Kuid + * + * I am aware that the subscripts look backwards, but that is the way they are + * written in the spec. + * + * param output_buffer [out] will hold the generated MAC + * param session [in] holds all the state data we need to generate the auth code + * param mac_length [out] will be set to the length of the auth code + * + * returns 0 on success + * 1 on failure + */ +int +lanplus_generate_rakp3_authcode(uint8_t * output_buffer, + const struct ipmi_session * session, + uint32_t * mac_length, struct ipmi_intf * intf) +{ + int ret = 0; + int input_buffer_length, i; + uint8_t * input_buffer; + uint32_t SIDm_lsbf; + + + if (session->v2_data.auth_alg == IPMI_AUTH_RAKP_NONE) + { + *mac_length = 0; + return 0; + } + + /* We don't yet support other algorithms */ + assert(session->v2_data.auth_alg == IPMI_AUTH_RAKP_HMAC_SHA1); + + input_buffer_length = + 16 + /* Rc */ + 4 + /* SIDm */ + 1 + /* ROLEm */ + 1 + /* ULENGTHm */ + strlen((const char *)session->username); + + input_buffer = malloc(input_buffer_length); + if (input_buffer == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + return 1; + } + + /* + * Fill the buffer. I'm assuming that we're using the LSBF representation of the + * multibyte numbers in use. + */ + + /* Rc */ + #if WORDS_BIGENDIAN + for (i = 0; i < 16; ++i) + input_buffer[i] = session->v2_data.bmc_rand[16 - 1 - i]; + #else + for (i = 0; i < 16; ++i) + input_buffer[i] = session->v2_data.bmc_rand[i]; + #endif + + /* SIDm */ + SIDm_lsbf = session->v2_data.console_id; + #if WORDS_BIGENDIAN + SIDm_lsbf = BSWAP_32(SIDm_lsbf); + #endif + memcpy(input_buffer + 16, &SIDm_lsbf, 4); + + /* ROLEm */ + if (ipmi_oem_active(intf, "intelplus") || ipmi_oem_active(intf, "i82571spt")) + input_buffer[20] = session->privlvl; + else + input_buffer[20] = session->v2_data.requested_role; + + /* ULENGTHm */ + input_buffer[21] = strlen((const char *)session->username); + + /* USERNAME */ + for (i = 0; i < input_buffer[21]; ++i) + input_buffer[22 + i] = session->username[i]; + + if (verbose > 2) + { + printbuf((const uint8_t *)input_buffer, input_buffer_length, ">> rakp3 mac input buffer"); + printbuf((const uint8_t *)session->authcode, IPMI_AUTHCODE_BUFFER_SIZE, ">> rakp3 mac key"); + } + + lanplus_HMAC(session->v2_data.auth_alg, + session->authcode, + IPMI_AUTHCODE_BUFFER_SIZE, + input_buffer, + input_buffer_length, + output_buffer, + mac_length); + + if (verbose > 2) + printbuf((const uint8_t *)output_buffer, *mac_length, "generated rakp3 mac"); + + + free(input_buffer); + input_buffer = NULL; + + return ret; +} + + + +/* + * lanplus_generate_sik + * + * Generate the session integrity key (SIK) used for integrity checking + * during the IPMI v2 / RMCP+ session + * + * This session integrity key is a HMAC generated with : + * + * Rm - Console generated random number + * Rc - BMC generated random number + * ROLEm - Requested privilege level (entire byte) + * ULENGTHm - Username length + * <USERNAME> - Usename (absent for null usernames) + * + * The key used to generated the SIK is Kg if Kg is not null (two-key logins are + * enabled). Otherwise Kuid (the user authcode) is used as the key to genereate + * the SIK. + * + * I am aware that the subscripts look backwards, but that is the way they are + * written in the spec. + * + * param session [in/out] contains our input and output fields. + * + * returns 0 on success + * 1 on failure + */ +int +lanplus_generate_sik(struct ipmi_session * session, struct ipmi_intf * intf) +{ + uint8_t * input_buffer; + int input_buffer_length, i; + uint8_t * input_key; + uint32_t mac_length; + + + memset(session->v2_data.sik, 0, IPMI_SIK_BUFFER_SIZE); + + if (session->v2_data.auth_alg == IPMI_AUTH_RAKP_NONE) + return 0; + + /* We don't yet support other algorithms */ + assert(session->v2_data.auth_alg == IPMI_AUTH_RAKP_HMAC_SHA1); + + input_buffer_length = + 16 + /* Rm */ + 16 + /* Rc */ + 1 + /* ROLEm */ + 1 + /* ULENGTHm */ + strlen((const char *)session->username); + + input_buffer = malloc(input_buffer_length); + if (input_buffer == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + return 1; + } + + /* + * Fill the buffer. I'm assuming that we're using the LSBF representation of the + * multibyte numbers in use. + */ + + /* Rm */ + #if WORDS_BIGENDIAN + for (i = 0; i < 16; ++i) + input_buffer[i] = session->v2_data.console_rand[16 - 1 - i]; + #else + for (i = 0; i < 16; ++i) + input_buffer[i] = session->v2_data.console_rand[i]; + #endif + + + /* Rc */ + #if WORDS_BIGENDIAN + for (i = 0; i < 16; ++i) + input_buffer[16 + i] = session->v2_data.bmc_rand[16 - 1 - i]; + #else + for (i = 0; i < 16; ++i) + input_buffer[16 + i] = session->v2_data.bmc_rand[i]; + #endif + + /* ROLEm */ + input_buffer[32] = session->v2_data.requested_role; + + if (ipmi_oem_active(intf, "i82571spt")) { + /* + * The HMAC calculation code in the Intel 82571 GbE + * skips this bit! Looks like a GbE bug, but we need + * to work around it here anyway... + */ + input_buffer[32] &= ~0x10; + } + + /* ULENGTHm */ + input_buffer[33] = strlen((const char *)session->username); + + /* USERNAME */ + for (i = 0; i < input_buffer[33]; ++i) + input_buffer[34 + i] = session->username[i]; + + if (session->v2_data.kg[0]) + { + /* We will be hashing with Kg */ + /* + * Section 13.31 of the IPMI v2 spec describes the SIK creation + * using Kg. It specifies that Kg should not be truncated. + * Kg is set in ipmi_intf. + */ + input_key = session->v2_data.kg; + } + else + { + /* We will be hashing with Kuid */ + input_key = session->authcode; + } + + + if (verbose >= 2) + printbuf((const uint8_t *)input_buffer, input_buffer_length, "session integrity key input"); + + lanplus_HMAC(session->v2_data.auth_alg, + input_key, + IPMI_AUTHCODE_BUFFER_SIZE, + input_buffer, + input_buffer_length, + session->v2_data.sik, + &mac_length); + + free(input_buffer); + input_buffer = NULL; + assert(mac_length == 20); + + /* + * The key MAC generated is 20 bytes, but we will only be using the first + * 12 for SHA1 96 + */ + if (verbose >= 2) + printbuf(session->v2_data.sik, 20, "Generated session integrity key"); + + return 0; +} + + + +/* + * lanplus_generate_k1 + * + * Generate K1, the key presumably used to generate integrity authcodes + * + * We use the authentication algorithm to generated the HMAC, using + * the session integrity key (SIK) as our key. + * + * param session [in/out]. + * + * returns 0 on success + * 1 on failure + */ +int +lanplus_generate_k1(struct ipmi_session * session) +{ + uint32_t mac_length; + + uint8_t CONST_1[] = + {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}; + + if (session->v2_data.auth_alg == IPMI_AUTH_RAKP_NONE) + memcpy(session->v2_data.k1, CONST_1, 20); + else + { + lanplus_HMAC(session->v2_data.auth_alg, + session->v2_data.sik, + IPMI_SIK_BUFFER_SIZE, /* SIK length */ + CONST_1, + 20, + session->v2_data.k1, + &mac_length); + assert(mac_length == 20); + } + + if (verbose >= 2) + printbuf(session->v2_data.k1, 20, "Generated K1"); + + return 0; +} + + + +/* + * lanplus_generate_k2 + * + * Generate K2, the key used for RMCP+ AES encryption. + * + * We use the authentication algorithm to generated the HMAC, using + * the session integrity key (SIK) as our key. + * + * param session [in/out]. + * + * returns 0 on success + * 1 on failure + */ +int +lanplus_generate_k2(struct ipmi_session * session) +{ + uint32_t mac_length; + + uint8_t CONST_2[] = + {0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02}; + + if (session->v2_data.auth_alg == IPMI_AUTH_RAKP_NONE) + memcpy(session->v2_data.k2, CONST_2, 20); + else + { + lanplus_HMAC(session->v2_data.auth_alg, + session->v2_data.sik, + IPMI_SIK_BUFFER_SIZE, /* SIK length */ + CONST_2, + 20, + session->v2_data.k2, + &mac_length); + assert(mac_length == 20); + } + + if (verbose >= 2) + printbuf(session->v2_data.k2, 20, "Generated K2"); + + return 0; +} + + + +/* + * lanplus_encrypt_payload + * + * Perform the appropriate encryption on the input data. Output the encrypted + * data to output, including the required confidentiality header and trailer. + * If the crypt_alg is IPMI_CRYPT_NONE, simply copy the input to the output and + * set bytes_written to input_length. + * + * param crypt_alg specifies the encryption algorithm (from table 13-19 of the + * IPMI v2 spec) + * param key is the used as input to the encryption algorithmf + * param input is the input data to be encrypted + * param input_length is the length of the input data to be encrypted + * param output is the cipher text generated by the encryption process + * param bytes_written is the number of bytes written during the encryption + * process + * + * returns 0 on success + * 1 on failure + */ +int +lanplus_encrypt_payload(uint8_t crypt_alg, + const uint8_t * key, const uint8_t * input, + uint32_t input_length, uint8_t * output, + uint16_t * bytes_written) +{ + uint8_t * padded_input; + uint32_t mod, i, bytes_encrypted; + uint8_t pad_length = 0; + + if (crypt_alg == IPMI_CRYPT_NONE) + { + /* Just copy the input to the output */ + *bytes_written = input_length; + return 0; + } + + /* Currently, we only support AES */ + assert(crypt_alg == IPMI_CRYPT_AES_CBC_128); + assert(input_length <= IPMI_MAX_PAYLOAD_SIZE); + + + /* + * The input to the AES encryption algorithm has to be a multiple of the + * block size (16 bytes). The extra byte we are adding is the pad length + * byte. + */ + mod = (input_length + 1) % IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE; + if (mod) + pad_length = IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE - mod; + + padded_input = (uint8_t*)malloc(input_length + pad_length + 1); + if (padded_input == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + return 1; + } + memcpy(padded_input, input, input_length); + + /* add the pad */ + for (i = 0; i < pad_length; ++i) + padded_input[input_length + i] = i + 1; + + /* add the pad length */ + padded_input[input_length + pad_length] = pad_length; + + /* Generate an initialization vector, IV, for the encryption process */ + if (lanplus_rand(output, IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE)) + { + lprintf(LOG_ERR, "lanplus_encrypt_payload: Error generating IV"); + if (padded_input != NULL) { + free(padded_input); + padded_input = NULL; + } + return 1; + } + + if (verbose > 2) + printbuf(output, IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE, ">> Initialization vector"); + + + + lanplus_encrypt_aes_cbc_128(output, /* IV */ + key, /* K2 */ + padded_input, /* Data to encrypt */ + input_length + pad_length + 1, /* Input length */ + output + IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE, /* output */ + &bytes_encrypted); /* bytes written */ + + *bytes_written = + IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE + /* IV */ + bytes_encrypted; + + free(padded_input); + padded_input = NULL; + + return 0; +} + + + +/* + * lanplus_has_valid_auth_code + * + * Determine whether the packets authcode field is valid for packet. + * + * We always return success if any of the following are true. + * - this is not an IPMIv2 packet + * - the session is not yet active + * - the packet specifies that it is not authenticated + * - the integrity algorithm agreed upon during session creation is "none" + * + * The authcode is computed using the specified integrity algorithm starting + * with the AuthType / Format field, and ending with the field immediately + * preceeding the authcode itself. + * + * The key key used to generate the authcode MAC is K1. + * + * param rs holds the response structure. + * param session holds our session state, including our chosen algorithm, key, etc. + * + * returns 1 on success (authcode is valid) + * 0 on failure (autchode integrity check failed) + */ +int +lanplus_has_valid_auth_code(struct ipmi_rs * rs, struct ipmi_session * session) +{ + uint8_t * bmc_authcode; + uint8_t generated_authcode[IPMI_MAX_MAC_SIZE]; + uint32_t generated_authcode_length; + + + if ((rs->session.authtype != IPMI_SESSION_AUTHTYPE_RMCP_PLUS) || + (session->v2_data.session_state != LANPLUS_STATE_ACTIVE) || + (! rs->session.bAuthenticated) || + (session->v2_data.integrity_alg == IPMI_INTEGRITY_NONE)) + return 1; + + /* We only support SHA1-96 now */ + assert(session->v2_data.integrity_alg == IPMI_INTEGRITY_HMAC_SHA1_96); + + /* + * For SHA1-96, the authcode will be the last 12 bytes in the packet + */ + bmc_authcode = rs->data + (rs->data_len - IPMI_SHA1_AUTHCODE_SIZE); + + lanplus_HMAC(session->v2_data.integrity_alg, + session->v2_data.k1, + IPMI_AUTHCODE_BUFFER_SIZE, + rs->data + IPMI_LANPLUS_OFFSET_AUTHTYPE, + rs->data_len - IPMI_LANPLUS_OFFSET_AUTHTYPE - IPMI_SHA1_AUTHCODE_SIZE, + generated_authcode, + &generated_authcode_length); + + if (verbose > 3) + { + lprintf(LOG_DEBUG+2, "Validating authcode"); + printbuf(session->v2_data.k1, 20, "K1"); + printbuf(rs->data + IPMI_LANPLUS_OFFSET_AUTHTYPE, + rs->data_len - IPMI_LANPLUS_OFFSET_AUTHTYPE - IPMI_SHA1_AUTHCODE_SIZE, + "Authcode Input Data"); + printbuf(generated_authcode, 12, "Generated authcode"); + printbuf(bmc_authcode, 12, "Expected authcode"); + } + + + assert(generated_authcode_length == 20); + return (memcmp(bmc_authcode, generated_authcode, 12) == 0); +} + + + +/* + * lanplus_decrypt_payload + * + * + * param input points to the beginning of the payload (which will be the IV if + * we are using AES) + * param payload_size [out] will be set to the size of the payload EXCLUDING + * padding + * + * returns 0 on success (we were able to successfully decrypt the packet) + * 1 on failure (we were unable to successfully decrypt the packet) + */ +int +lanplus_decrypt_payload(uint8_t crypt_alg, const uint8_t * key, + const uint8_t * input, uint32_t input_length, + uint8_t * output, uint16_t * payload_size) +{ + uint8_t * decrypted_payload; + uint32_t bytes_decrypted; + + if (crypt_alg == IPMI_CRYPT_NONE) + { + /* We are not encrypted. The paylaod size is is everything. */ + *payload_size = input_length; + memmove(output, input, input_length); + return 0; + } + + /* We only support AES */ + assert(crypt_alg == IPMI_CRYPT_AES_CBC_128); + + decrypted_payload = (uint8_t*)malloc(input_length); + if (decrypted_payload == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + return 1; + } + + + lanplus_decrypt_aes_cbc_128(input, /* IV */ + key, /* Key */ + input + + IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE, /* Data to decrypt */ + input_length - + IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE, /* Input length */ + decrypted_payload, /* output */ + &bytes_decrypted); /* bytes written */ + + if (bytes_decrypted != 0) + { + /* Success */ + uint8_t conf_pad_length; + int i; + + memmove(output, + decrypted_payload, + bytes_decrypted); + + /* + * We have to determine the payload size, by substracting the padding, etc. + * The last byte of the decrypted payload is the confidentiality pad length. + */ + conf_pad_length = decrypted_payload[bytes_decrypted - 1]; + *payload_size = bytes_decrypted - conf_pad_length - 1; + + /* + * Extra test to make sure that the padding looks like it should (should start + * with 0x01, 0x02, 0x03, etc... + */ + for (i = 0; i < conf_pad_length; ++i) + { + if (decrypted_payload[*payload_size + i] != (i + 1)) + { + lprintf(LOG_ERR, "Malformed payload padding"); + assert(0); + } + } + } + else + { + lprintf(LOG_ERR, "ERROR: lanplus_decrypt_aes_cbc_128 decryptd 0 bytes"); + assert(0); + } + + free(decrypted_payload); + decrypted_payload = NULL; + return (bytes_decrypted == 0); +} diff --git a/src/plugins/lanplus/lanplus_crypt.h b/src/plugins/lanplus/lanplus_crypt.h new file mode 100644 index 0000000..d69cc9b --- /dev/null +++ b/src/plugins/lanplus/lanplus_crypt.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#ifndef IPMI_LANPLUS_CRYPT_H +#define IPMI_LANPLUS_CRYPT_H + +#include <ipmitool/ipmi_intf.h> + +/* + * See the implementation file for documentation + * ipmi_intf can be used for oem specific implementations + * e.g. if (ipmi_oem_active(intf, "OEM_XYZ")) + */ + +int lanplus_rakp2_hmac_matches(const struct ipmi_session * session, + const uint8_t * hmac, + struct ipmi_intf * intf); +int lanplus_rakp4_hmac_matches(const struct ipmi_session * session, + const uint8_t * hmac, + struct ipmi_intf * intf); +int lanplus_generate_rakp3_authcode(uint8_t * buffer, + const struct ipmi_session * session, + uint32_t * auth_length, + struct ipmi_intf * intf); +int lanplus_generate_sik(struct ipmi_session * session, struct ipmi_intf * intf); +int lanplus_generate_k1(struct ipmi_session * session); +int lanplus_generate_k2(struct ipmi_session * session); +int lanplus_encrypt_payload(uint8_t crypt_alg, + const uint8_t * key, + const uint8_t * input, + uint32_t input_length, + uint8_t * output, + uint16_t * bytesWritten); +int lanplus_decrypt_payload(uint8_t crypt_alg, + const uint8_t * key, + const uint8_t * input, + uint32_t input_length, + uint8_t * output, + uint16_t * payload_size); +int lanplus_has_valid_auth_code(struct ipmi_rs * rs, + struct ipmi_session * session); + + + + +#endif /* IPMI_LANPLUS_CRYPT_H */ diff --git a/src/plugins/lanplus/lanplus_crypt_impl.c b/src/plugins/lanplus/lanplus_crypt_impl.c new file mode 100644 index 0000000..cde6c54 --- /dev/null +++ b/src/plugins/lanplus/lanplus_crypt_impl.c @@ -0,0 +1,293 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include "ipmitool/log.h" +#include "ipmitool/ipmi_constants.h" +#include "lanplus.h" +#include "lanplus_crypt_impl.h" +#include <openssl/hmac.h> +#include <openssl/evp.h> +#include <openssl/rand.h> +#include <openssl/err.h> +#include <assert.h> + + + +/* + * lanplus_seed_prng + * + * Seed our PRNG with the specified number of bytes from /dev/random + * + * param bytes specifies the number of bytes to read from /dev/random + * + * returns 0 on success + * 1 on failure + */ +int lanplus_seed_prng(uint32_t bytes) +{ + if (! RAND_load_file("/dev/urandom", bytes)) + return 1; + else + return 0; +} + + + +/* + * lanplus_rand + * + * Generate a random number of the specified size + * + * param num_bytes [in] is the size of the random number to be + * generated + * param buffer [out] is where we will place our random number + * + * return 0 on success + * 1 on failure + */ +int +lanplus_rand(uint8_t * buffer, uint32_t num_bytes) +{ +#undef IPMI_LANPLUS_FAKE_RAND +#ifdef IPMI_LANPLUS_FAKE_RAND + + /* + * This code exists so that we can easily find the generated random number + * in the hex dumps. + */ + int i; + for (i = 0; i < num_bytes; ++i) + buffer[i] = 0x70 | i; + + return 0; +#else + return (! RAND_bytes(buffer, num_bytes)); +#endif +} + + + +/* + * lanplus_HMAC + * + * param mac specifies the algorithm to be used, currently only SHA1 is supported + * param key is the key used for HMAC generation + * param key_len is the lenght of key + * param d is the data to be MAC'd + * param n is the length of the data at d + * param md is the result of the HMAC algorithm + * param md_len is the length of md + * + * returns a pointer to md + */ +uint8_t * +lanplus_HMAC(uint8_t mac, + const void *key, + int key_len, + const uint8_t *d, + int n, + uint8_t *md, + uint32_t *md_len) +{ + const EVP_MD *evp_md = NULL; + + if ((mac == IPMI_AUTH_RAKP_HMAC_SHA1) || + (mac == IPMI_INTEGRITY_HMAC_SHA1_96)) + evp_md = EVP_sha1(); + else + { + lprintf(LOG_DEBUG, "Invalid mac type 0x%x in lanplus_HMAC\n", mac); + assert(0); + } + + return HMAC(evp_md, key, key_len, d, n, md, (unsigned int *)md_len); +} + + +/* + * lanplus_encrypt_aes_cbc_128 + * + * Encrypt with the AES CBC 128 algorithm + * + * param iv is the 16 byte initialization vector + * param key is the 16 byte key used by the AES algorithm + * param input is the data to be encrypted + * param input_length is the number of bytes to be encrypted. This MUST + * be a multiple of the block size, 16. + * param output is the encrypted output + * param bytes_written is the number of bytes written. This param is set + * to 0 on failure, or if 0 bytes were input. + */ +void +lanplus_encrypt_aes_cbc_128(const uint8_t * iv, + const uint8_t * key, + const uint8_t * input, + uint32_t input_length, + uint8_t * output, + uint32_t * bytes_written) +{ + EVP_CIPHER_CTX ctx; + EVP_CIPHER_CTX_init(&ctx); + EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv); + EVP_CIPHER_CTX_set_padding(&ctx, 0); + + + *bytes_written = 0; + + if (input_length == 0) + return; + + if (verbose >= 5) + { + printbuf(iv, 16, "encrypting with this IV"); + printbuf(key, 16, "encrypting with this key"); + printbuf(input, input_length, "encrypting this data"); + } + + + /* + * The default implementation adds a whole block of padding if the input + * data is perfectly aligned. We would like to keep that from happening. + * We have made a point to have our input perfectly padded. + */ + assert((input_length % IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE) == 0); + + + if(!EVP_EncryptUpdate(&ctx, output, (int *)bytes_written, input, input_length)) + { + /* Error */ + *bytes_written = 0; + return; + } + else + { + uint32_t tmplen; + + if(!EVP_EncryptFinal_ex(&ctx, output + *bytes_written, (int *)&tmplen)) + { + *bytes_written = 0; + return; /* Error */ + } + else + { + /* Success */ + *bytes_written += tmplen; + EVP_CIPHER_CTX_cleanup(&ctx); + } + } +} + + + +/* + * lanplus_decrypt_aes_cbc_128 + * + * Decrypt with the AES CBC 128 algorithm + * + * param iv is the 16 byte initialization vector + * param key is the 16 byte key used by the AES algorithm + * param input is the data to be decrypted + * param input_length is the number of bytes to be decrypted. This MUST + * be a multiple of the block size, 16. + * param output is the decrypted output + * param bytes_written is the number of bytes written. This param is set + * to 0 on failure, or if 0 bytes were input. + */ +void +lanplus_decrypt_aes_cbc_128(const uint8_t * iv, + const uint8_t * key, + const uint8_t * input, + uint32_t input_length, + uint8_t * output, + uint32_t * bytes_written) +{ + EVP_CIPHER_CTX ctx; + EVP_CIPHER_CTX_init(&ctx); + EVP_DecryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv); + EVP_CIPHER_CTX_set_padding(&ctx, 0); + + + if (verbose >= 5) + { + printbuf(iv, 16, "decrypting with this IV"); + printbuf(key, 16, "decrypting with this key"); + printbuf(input, input_length, "decrypting this data"); + } + + + *bytes_written = 0; + + if (input_length == 0) + return; + + /* + * The default implementation adds a whole block of padding if the input + * data is perfectly aligned. We would like to keep that from happening. + * We have made a point to have our input perfectly padded. + */ + assert((input_length % IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE) == 0); + + + if (!EVP_DecryptUpdate(&ctx, output, (int *)bytes_written, input, input_length)) + { + /* Error */ + lprintf(LOG_DEBUG, "ERROR: decrypt update failed"); + *bytes_written = 0; + return; + } + else + { + uint32_t tmplen; + + if (!EVP_DecryptFinal_ex(&ctx, output + *bytes_written, (int *)&tmplen)) + { + char buffer[1000]; + ERR_error_string(ERR_get_error(), buffer); + lprintf(LOG_DEBUG, "the ERR error %s", buffer); + lprintf(LOG_DEBUG, "ERROR: decrypt final failed"); + *bytes_written = 0; + return; /* Error */ + } + else + { + /* Success */ + *bytes_written += tmplen; + EVP_CIPHER_CTX_cleanup(&ctx); + } + } + + if (verbose >= 5) + { + lprintf(LOG_DEBUG, "Decrypted %d encrypted bytes", input_length); + printbuf(output, *bytes_written, "Decrypted this data"); + } +} diff --git a/src/plugins/lanplus/lanplus_crypt_impl.h b/src/plugins/lanplus/lanplus_crypt_impl.h new file mode 100644 index 0000000..ff534bc --- /dev/null +++ b/src/plugins/lanplus/lanplus_crypt_impl.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#ifndef IPMI_LANPLUS_CRYPT_IMPL_H +#define IPMI_LANPLUS_CRYPT_IMPL_H + + +int +lanplus_seed_prng(uint32_t bytes); + +int +lanplus_rand(uint8_t * buffer, uint32_t num_bytes); + +uint8_t * +lanplus_HMAC(uint8_t mac, const void *key, int key_len, + const uint8_t *d, int n, uint8_t *md, + uint32_t *md_len); + +void +lanplus_encrypt_aes_cbc_128(const uint8_t * iv, + const uint8_t * key, + const uint8_t * input, + uint32_t input_length, + uint8_t * output, + uint32_t * bytes_written); + + +void +lanplus_decrypt_aes_cbc_128(const uint8_t * iv, + const uint8_t * key, + const uint8_t * input, + uint32_t input_length, + uint8_t * output, + uint32_t * bytes_written); + + +#endif /* IPMI_LANPLUS_CRYPT_IMPL_H */ diff --git a/src/plugins/lanplus/lanplus_dump.c b/src/plugins/lanplus/lanplus_dump.c new file mode 100644 index 0000000..8d52fab --- /dev/null +++ b/src/plugins/lanplus/lanplus_dump.c @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include "lanplus.h" +#include "lanplus_dump.h" + +extern const struct valstr ipmi_rakp_return_codes[]; +extern const struct valstr ipmi_priv_levels[]; +extern const struct valstr ipmi_auth_algorithms[]; +extern const struct valstr ipmi_integrity_algorithms[]; +extern const struct valstr ipmi_encryption_algorithms[]; + +#define DUMP_PREFIX_INCOMING "<<" + +void lanplus_dump_open_session_response(const struct ipmi_rs * rsp) +{ + if (verbose < 2) + return; + + printf("%sOPEN SESSION RESPONSE\n", DUMP_PREFIX_INCOMING); + + printf("%s Message tag : 0x%02x\n", + DUMP_PREFIX_INCOMING, + rsp->payload.open_session_response.message_tag); + printf("%s RMCP+ status : %s\n", + DUMP_PREFIX_INCOMING, + val2str(rsp->payload.open_session_response.rakp_return_code, + ipmi_rakp_return_codes)); + printf("%s Maximum privilege level : %s\n", + DUMP_PREFIX_INCOMING, + val2str(rsp->payload.open_session_response.max_priv_level, + ipmi_priv_levels)); + printf("%s Console Session ID : 0x%08lx\n", + DUMP_PREFIX_INCOMING, + (long)rsp->payload.open_session_response.console_id); + + /* only tag, status, privlvl, and console id are returned if error */ + if (rsp->payload.open_session_response.rakp_return_code != + IPMI_RAKP_STATUS_NO_ERRORS) + return; + + printf("%s BMC Session ID : 0x%08lx\n", + DUMP_PREFIX_INCOMING, + (long)rsp->payload.open_session_response.bmc_id); + printf("%s Negotiated authenticatin algorithm : %s\n", + DUMP_PREFIX_INCOMING, + val2str(rsp->payload.open_session_response.auth_alg, + ipmi_auth_algorithms)); + printf("%s Negotiated integrity algorithm : %s\n", + DUMP_PREFIX_INCOMING, + val2str(rsp->payload.open_session_response.integrity_alg, + ipmi_integrity_algorithms)); + printf("%s Negotiated encryption algorithm : %s\n", + DUMP_PREFIX_INCOMING, + val2str(rsp->payload.open_session_response.crypt_alg, + ipmi_encryption_algorithms)); + printf("\n"); +} + + + +void lanplus_dump_rakp2_message(const struct ipmi_rs * rsp, uint8_t auth_alg) +{ + int i; + + if (verbose < 2) + return; + + printf("%sRAKP 2 MESSAGE\n", DUMP_PREFIX_INCOMING); + + printf("%s Message tag : 0x%02x\n", + DUMP_PREFIX_INCOMING, + rsp->payload.rakp2_message.message_tag); + + printf("%s RMCP+ status : %s\n", + DUMP_PREFIX_INCOMING, + val2str(rsp->payload.rakp2_message.rakp_return_code, + ipmi_rakp_return_codes)); + + printf("%s Console Session ID : 0x%08lx\n", + DUMP_PREFIX_INCOMING, + (long)rsp->payload.rakp2_message.console_id); + + printf("%s BMC random number : 0x", DUMP_PREFIX_INCOMING); + for (i = 0; i < 16; ++i) + printf("%02x", rsp->payload.rakp2_message.bmc_rand[i]); + printf("\n"); + + printf("%s BMC GUID : 0x", DUMP_PREFIX_INCOMING); + for (i = 0; i < 16; ++i) + printf("%02x", rsp->payload.rakp2_message.bmc_guid[i]); + printf("\n"); + + switch(auth_alg) + { + case IPMI_AUTH_RAKP_NONE: + printf("%s Key exchange auth code : none\n", DUMP_PREFIX_INCOMING); + break; + case IPMI_AUTH_RAKP_HMAC_SHA1: + printf("%s Key exchange auth code [sha1] : 0x", DUMP_PREFIX_INCOMING); + for (i = 0; i < 20; ++i) + printf("%02x", rsp->payload.rakp2_message.key_exchange_auth_code[i]); + printf("\n"); + break; + case IPMI_AUTH_RAKP_HMAC_MD5: + printf("%s Key exchange auth code [md5] : 0x", DUMP_PREFIX_INCOMING); + for (i = 0; i < 16; ++i) + printf("%02x", rsp->payload.rakp2_message.key_exchange_auth_code[i]); + printf("\n"); + break; + default: + printf("%s Key exchange auth code : invalid", DUMP_PREFIX_INCOMING); + } + printf("\n"); +} + + + +void lanplus_dump_rakp4_message(const struct ipmi_rs * rsp, uint8_t auth_alg) +{ + int i; + + if (verbose < 2) + return; + + printf("%sRAKP 4 MESSAGE\n", DUMP_PREFIX_INCOMING); + + printf("%s Message tag : 0x%02x\n", + DUMP_PREFIX_INCOMING, + rsp->payload.rakp4_message.message_tag); + + printf("%s RMCP+ status : %s\n", + DUMP_PREFIX_INCOMING, + val2str(rsp->payload.rakp4_message.rakp_return_code, + ipmi_rakp_return_codes)); + + printf("%s Console Session ID : 0x%08lx\n", + DUMP_PREFIX_INCOMING, + (long)rsp->payload.rakp4_message.console_id); + + switch(auth_alg) + { + case IPMI_AUTH_RAKP_NONE: + printf("%s Key exchange auth code : none\n", DUMP_PREFIX_INCOMING); + break; + case IPMI_AUTH_RAKP_HMAC_SHA1: + printf("%s Key exchange auth code [sha1] : 0x", DUMP_PREFIX_INCOMING); + for (i = 0; i < 12; ++i) + printf("%02x", rsp->payload.rakp4_message.integrity_check_value[i]); + printf("\n"); + break; + case IPMI_AUTH_RAKP_HMAC_MD5: + printf("%s Key exchange auth code [md5] : 0x", DUMP_PREFIX_INCOMING); + for (i = 0; i < 12; ++i) + printf("%02x", rsp->payload.rakp4_message.integrity_check_value[i]); + printf("\n"); + break; + default: + printf("%s Key exchange auth code : invalid", DUMP_PREFIX_INCOMING); + } + printf("\n"); +} + diff --git a/src/plugins/lanplus/lanplus_dump.h b/src/plugins/lanplus/lanplus_dump.h new file mode 100644 index 0000000..4e29ebb --- /dev/null +++ b/src/plugins/lanplus/lanplus_dump.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + + +#ifndef IPMI_LANPLUS_DUMP_H +#define IPMI_LANPLUS_DUMP_H + +#include <ipmitool/ipmi_intf.h> + +/* See the implementation file for documentation */ +void lanplus_dump_open_session_response(const struct ipmi_rs * rsp); +void lanplus_dump_rakp2_message(const struct ipmi_rs * rsp, uint8_t auth_alg); +void lanplus_dump_rakp4_message(const struct ipmi_rs * rsp, uint8_t auth_alg); + + +#endif /* IPMI_LANPLUS_DUMP_H */ diff --git a/src/plugins/lanplus/lanplus_strings.c b/src/plugins/lanplus/lanplus_strings.c new file mode 100644 index 0000000..074f898 --- /dev/null +++ b/src/plugins/lanplus/lanplus_strings.c @@ -0,0 +1,39 @@ +#include "lanplus.h" +#include "ipmitool/ipmi_constants.h" + +const struct valstr ipmi_rakp_return_codes[] = { + + { IPMI_RAKP_STATUS_NO_ERRORS, "no errors" }, + { IPMI_RAKP_STATUS_INSUFFICIENT_RESOURCES_FOR_SESSION, "insufficient resources for session" }, + { IPMI_RAKP_STATUS_INVALID_SESSION_ID, "invalid session ID" }, + { IPMI_RAKP_STATUS_INVALID_PAYLOAD_TYPE, "invalid payload type" }, + { IPMI_RAKP_STATUS_INVALID_AUTHENTICATION_ALGORITHM, "invalid authentication algorithm" }, + { IPMI_RAKP_STATUS_INVALID_INTEGRITTY_ALGORITHM, "invalid integrity algorithm" }, + { IPMI_RAKP_STATUS_NO_MATCHING_AUTHENTICATION_PAYLOAD, "no matching authentication algorithm"}, + { IPMI_RAKP_STATUS_NO_MATCHING_INTEGRITY_PAYLOAD, "no matching integrity payload" }, + { IPMI_RAKP_STATUS_INACTIVE_SESSION_ID, "inactive session ID" }, + { IPMI_RAKP_STATUS_INVALID_ROLE, "invalid role" }, + { IPMI_RAKP_STATUS_UNAUTHORIZED_ROLE_REQUESTED, "unauthorized role requested" }, + { IPMI_RAKP_STATUS_INSUFFICIENT_RESOURCES_FOR_ROLE, "insufficient resources for role" }, + { IPMI_RAKP_STATUS_INVALID_NAME_LENGTH, "invalid name length" }, + { IPMI_RAKP_STATUS_UNAUTHORIZED_NAME, "unauthorized name" }, + { IPMI_RAKP_STATUS_UNAUTHORIZED_GUID, "unauthorized GUID" }, + { IPMI_RAKP_STATUS_INVALID_INTEGRITY_CHECK_VALUE, "invalid integrity check value" }, + { IPMI_RAKP_STATUS_INVALID_CONFIDENTIALITY_ALGORITHM, "invalid confidentiality algorithm" }, + { IPMI_RAKP_STATUS_NO_CIPHER_SUITE_MATCH, "no matching cipher suite" }, + { IPMI_RAKP_STATUS_ILLEGAL_PARAMTER, "illegal parameter" }, + { 0, 0 }, +}; + + +const struct valstr ipmi_priv_levels[] = { + { IPMI_PRIV_CALLBACK, "callback" }, + { IPMI_PRIV_USER, "user" }, + { IPMI_PRIV_OPERATOR, "operator" }, + { IPMI_PRIV_ADMIN, "admin" }, + { IPMI_PRIV_OEM, "oem" }, + { 0, 0 }, +}; + + + diff --git a/src/plugins/lanplus/rmcp.h b/src/plugins/lanplus/rmcp.h new file mode 100644 index 0000000..51dc44d --- /dev/null +++ b/src/plugins/lanplus/rmcp.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#ifndef IPMI_RMCP_H +#define IPMI_RMCP_H + +#include <ipmitool/helper.h> +#include "lanplus.h" + +#define RMCP_VERSION_1 0x06 + +#define RMCP_UDP_PORT 0x26f /* port 623 */ +#define RMCP_UDP_SECURE_PORT 0x298 /* port 664 */ + +#define RMCP_TYPE_MASK 0x80 +#define RMCP_TYPE_NORM 0x00 +#define RMCP_TYPE_ACK 0x01 + +static const struct valstr rmcp_type_vals[] __attribute__((unused)) = { + { RMCP_TYPE_NORM, "Normal RMCP" }, + { RMCP_TYPE_ACK, "RMCP ACK" }, + { 0, NULL } +}; + +#define RMCP_CLASS_MASK 0x1f +#define RMCP_CLASS_ASF 0x06 +#define RMCP_CLASS_IPMI 0x07 +#define RMCP_CLASS_OEM 0x08 + +static const struct valstr rmcp_class_vals[] __attribute__((unused)) = { + { RMCP_CLASS_ASF, "ASF" }, + { RMCP_CLASS_IPMI, "IPMI" }, + { RMCP_CLASS_OEM, "OEM" }, + { 0, NULL } +}; + +/* RMCP message header */ +#ifdef HAVE_PRAGMA_PACK +#pragma pack(1) +#endif +struct rmcp_hdr { + uint8_t ver; + uint8_t __reserved; + uint8_t seq; + uint8_t class; +} ATTRIBUTE_PACKING; +#ifdef HAVE_PRAGMA_PACK +#pragma pack(0) +#endif + +int handle_rmcp(struct ipmi_intf * intf, uint8_t * data, int data_len); + +#endif /* IPMI_RMCP_H */ |