evmctl - IMA/EVM control tool
evmctl provides signing support for IMA/EVM. Functionality includes signing of file content (IMA), file metadata (EVM), importing public keys into kernel keyring. Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@intel.com>
This commit is contained in:
		
							
								
								
									
										53
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,53 @@ | |||||||
|  | *.swp | ||||||
|  | *~ | ||||||
|  |  | ||||||
|  | # Generated by autotools | ||||||
|  | .deps | ||||||
|  | aclocal.m4 | ||||||
|  | autom4te.cache | ||||||
|  | config.guess | ||||||
|  | config.log | ||||||
|  | config.status | ||||||
|  | config.sub | ||||||
|  | configure | ||||||
|  | depcomp | ||||||
|  | install-sh | ||||||
|  | Makefile.in | ||||||
|  | Makefile | ||||||
|  | !tests/data/Makefile | ||||||
|  | missing | ||||||
|  | compile | ||||||
|  | libtool | ||||||
|  | ltmain.sh | ||||||
|  |  | ||||||
|  | # Compiled executables | ||||||
|  | *.o | ||||||
|  | *.a | ||||||
|  | src/evmctl | ||||||
|  | tests/openclose | ||||||
|  | config.h | ||||||
|  | config.h.in | ||||||
|  | stamp-h1 | ||||||
|  | *.spec | ||||||
|  |  | ||||||
|  | # But don't ignore the symlinks with the same names in this directory | ||||||
|  | !tests/valgrind/* | ||||||
|  |  | ||||||
|  | # cscope/tags | ||||||
|  | tags | ||||||
|  | TAGS | ||||||
|  | cscope.* | ||||||
|  | ncscope.* | ||||||
|  |  | ||||||
|  | # Generated documentation | ||||||
|  | *.8 | ||||||
|  | *.5 | ||||||
|  | manpage.links | ||||||
|  | manpage.refs | ||||||
|  |  | ||||||
|  | # quilt's files | ||||||
|  | patches | ||||||
|  | series | ||||||
|  |  | ||||||
|  | # test output | ||||||
|  |  | ||||||
							
								
								
									
										5
									
								
								ChangeLog
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								ChangeLog
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | |||||||
|  | 2011-08-24  Dmitry Kasatkin  <dmitry.kasatkin@intel.com> | ||||||
|  |  | ||||||
|  | 	version 0.1 | ||||||
|  | 	* Initial public version. | ||||||
|  |  | ||||||
							
								
								
									
										365
									
								
								INSTALL
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										365
									
								
								INSTALL
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,365 @@ | |||||||
|  | Installation Instructions | ||||||
|  | ************************* | ||||||
|  |  | ||||||
|  | Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, | ||||||
|  | 2006, 2007, 2008, 2009 Free Software Foundation, Inc. | ||||||
|  |  | ||||||
|  |    Copying and distribution of this file, with or without modification, | ||||||
|  | are permitted in any medium without royalty provided the copyright | ||||||
|  | notice and this notice are preserved.  This file is offered as-is, | ||||||
|  | without warranty of any kind. | ||||||
|  |  | ||||||
|  | Basic Installation | ||||||
|  | ================== | ||||||
|  |  | ||||||
|  |    Briefly, the shell commands `./configure; make; make install' should | ||||||
|  | configure, build, and install this package.  The following | ||||||
|  | more-detailed instructions are generic; see the `README' file for | ||||||
|  | instructions specific to this package.  Some packages provide this | ||||||
|  | `INSTALL' file but do not implement all of the features documented | ||||||
|  | below.  The lack of an optional feature in a given package is not | ||||||
|  | necessarily a bug.  More recommendations for GNU packages can be found | ||||||
|  | in *note Makefile Conventions: (standards)Makefile Conventions. | ||||||
|  |  | ||||||
|  |    The `configure' shell script attempts to guess correct values for | ||||||
|  | various system-dependent variables used during compilation.  It uses | ||||||
|  | those values to create a `Makefile' in each directory of the package. | ||||||
|  | It may also create one or more `.h' files containing system-dependent | ||||||
|  | definitions.  Finally, it creates a shell script `config.status' that | ||||||
|  | you can run in the future to recreate the current configuration, and a | ||||||
|  | file `config.log' containing compiler output (useful mainly for | ||||||
|  | debugging `configure'). | ||||||
|  |  | ||||||
|  |    It can also use an optional file (typically called `config.cache' | ||||||
|  | and enabled with `--cache-file=config.cache' or simply `-C') that saves | ||||||
|  | the results of its tests to speed up reconfiguring.  Caching is | ||||||
|  | disabled by default to prevent problems with accidental use of stale | ||||||
|  | cache files. | ||||||
|  |  | ||||||
|  |    If you need to do unusual things to compile the package, please try | ||||||
|  | to figure out how `configure' could check whether to do them, and mail | ||||||
|  | diffs or instructions to the address given in the `README' so they can | ||||||
|  | be considered for the next release.  If you are using the cache, and at | ||||||
|  | some point `config.cache' contains results you don't want to keep, you | ||||||
|  | may remove or edit it. | ||||||
|  |  | ||||||
|  |    The file `configure.ac' (or `configure.in') is used to create | ||||||
|  | `configure' by a program called `autoconf'.  You need `configure.ac' if | ||||||
|  | you want to change it or regenerate `configure' using a newer version | ||||||
|  | of `autoconf'. | ||||||
|  |  | ||||||
|  |    The simplest way to compile this package is: | ||||||
|  |  | ||||||
|  |   1. `cd' to the directory containing the package's source code and type | ||||||
|  |      `./configure' to configure the package for your system. | ||||||
|  |  | ||||||
|  |      Running `configure' might take a while.  While running, it prints | ||||||
|  |      some messages telling which features it is checking for. | ||||||
|  |  | ||||||
|  |   2. Type `make' to compile the package. | ||||||
|  |  | ||||||
|  |   3. Optionally, type `make check' to run any self-tests that come with | ||||||
|  |      the package, generally using the just-built uninstalled binaries. | ||||||
|  |  | ||||||
|  |   4. Type `make install' to install the programs and any data files and | ||||||
|  |      documentation.  When installing into a prefix owned by root, it is | ||||||
|  |      recommended that the package be configured and built as a regular | ||||||
|  |      user, and only the `make install' phase executed with root | ||||||
|  |      privileges. | ||||||
|  |  | ||||||
|  |   5. Optionally, type `make installcheck' to repeat any self-tests, but | ||||||
|  |      this time using the binaries in their final installed location. | ||||||
|  |      This target does not install anything.  Running this target as a | ||||||
|  |      regular user, particularly if the prior `make install' required | ||||||
|  |      root privileges, verifies that the installation completed | ||||||
|  |      correctly. | ||||||
|  |  | ||||||
|  |   6. You can remove the program binaries and object files from the | ||||||
|  |      source code directory by typing `make clean'.  To also remove the | ||||||
|  |      files that `configure' created (so you can compile the package for | ||||||
|  |      a different kind of computer), type `make distclean'.  There is | ||||||
|  |      also a `make maintainer-clean' target, but that is intended mainly | ||||||
|  |      for the package's developers.  If you use it, you may have to get | ||||||
|  |      all sorts of other programs in order to regenerate files that came | ||||||
|  |      with the distribution. | ||||||
|  |  | ||||||
|  |   7. Often, you can also type `make uninstall' to remove the installed | ||||||
|  |      files again.  In practice, not all packages have tested that | ||||||
|  |      uninstallation works correctly, even though it is required by the | ||||||
|  |      GNU Coding Standards. | ||||||
|  |  | ||||||
|  |   8. Some packages, particularly those that use Automake, provide `make | ||||||
|  |      distcheck', which can by used by developers to test that all other | ||||||
|  |      targets like `make install' and `make uninstall' work correctly. | ||||||
|  |      This target is generally not run by end users. | ||||||
|  |  | ||||||
|  | Compilers and Options | ||||||
|  | ===================== | ||||||
|  |  | ||||||
|  |    Some systems require unusual options for compilation or linking that | ||||||
|  | the `configure' script does not know about.  Run `./configure --help' | ||||||
|  | for details on some of the pertinent environment variables. | ||||||
|  |  | ||||||
|  |    You can give `configure' initial values for configuration parameters | ||||||
|  | by setting variables in the command line or in the environment.  Here | ||||||
|  | is an example: | ||||||
|  |  | ||||||
|  |      ./configure CC=c99 CFLAGS=-g LIBS=-lposix | ||||||
|  |  | ||||||
|  |    *Note Defining Variables::, for more details. | ||||||
|  |  | ||||||
|  | Compiling For Multiple Architectures | ||||||
|  | ==================================== | ||||||
|  |  | ||||||
|  |    You can compile the package for more than one kind of computer at the | ||||||
|  | same time, by placing the object files for each architecture in their | ||||||
|  | own directory.  To do this, you can use GNU `make'.  `cd' to the | ||||||
|  | directory where you want the object files and executables to go and run | ||||||
|  | the `configure' script.  `configure' automatically checks for the | ||||||
|  | source code in the directory that `configure' is in and in `..'.  This | ||||||
|  | is known as a "VPATH" build. | ||||||
|  |  | ||||||
|  |    With a non-GNU `make', it is safer to compile the package for one | ||||||
|  | architecture at a time in the source code directory.  After you have | ||||||
|  | installed the package for one architecture, use `make distclean' before | ||||||
|  | reconfiguring for another architecture. | ||||||
|  |  | ||||||
|  |    On MacOS X 10.5 and later systems, you can create libraries and | ||||||
|  | executables that work on multiple system types--known as "fat" or | ||||||
|  | "universal" binaries--by specifying multiple `-arch' options to the | ||||||
|  | compiler but only a single `-arch' option to the preprocessor.  Like | ||||||
|  | this: | ||||||
|  |  | ||||||
|  |      ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ | ||||||
|  |                  CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ | ||||||
|  |                  CPP="gcc -E" CXXCPP="g++ -E" | ||||||
|  |  | ||||||
|  |    This is not guaranteed to produce working output in all cases, you | ||||||
|  | may have to build one architecture at a time and combine the results | ||||||
|  | using the `lipo' tool if you have problems. | ||||||
|  |  | ||||||
|  | Installation Names | ||||||
|  | ================== | ||||||
|  |  | ||||||
|  |    By default, `make install' installs the package's commands under | ||||||
|  | `/usr/local/bin', include files under `/usr/local/include', etc.  You | ||||||
|  | can specify an installation prefix other than `/usr/local' by giving | ||||||
|  | `configure' the option `--prefix=PREFIX', where PREFIX must be an | ||||||
|  | absolute file name. | ||||||
|  |  | ||||||
|  |    You can specify separate installation prefixes for | ||||||
|  | architecture-specific files and architecture-independent files.  If you | ||||||
|  | pass the option `--exec-prefix=PREFIX' to `configure', the package uses | ||||||
|  | PREFIX as the prefix for installing programs and libraries. | ||||||
|  | Documentation and other data files still use the regular prefix. | ||||||
|  |  | ||||||
|  |    In addition, if you use an unusual directory layout you can give | ||||||
|  | options like `--bindir=DIR' to specify different values for particular | ||||||
|  | kinds of files.  Run `configure --help' for a list of the directories | ||||||
|  | you can set and what kinds of files go in them.  In general, the | ||||||
|  | default for these options is expressed in terms of `${prefix}', so that | ||||||
|  | specifying just `--prefix' will affect all of the other directory | ||||||
|  | specifications that were not explicitly provided. | ||||||
|  |  | ||||||
|  |    The most portable way to affect installation locations is to pass the | ||||||
|  | correct locations to `configure'; however, many packages provide one or | ||||||
|  | both of the following shortcuts of passing variable assignments to the | ||||||
|  | `make install' command line to change installation locations without | ||||||
|  | having to reconfigure or recompile. | ||||||
|  |  | ||||||
|  |    The first method involves providing an override variable for each | ||||||
|  | affected directory.  For example, `make install | ||||||
|  | prefix=/alternate/directory' will choose an alternate location for all | ||||||
|  | directory configuration variables that were expressed in terms of | ||||||
|  | `${prefix}'.  Any directories that were specified during `configure', | ||||||
|  | but not in terms of `${prefix}', must each be overridden at install | ||||||
|  | time for the entire installation to be relocated.  The approach of | ||||||
|  | makefile variable overrides for each directory variable is required by | ||||||
|  | the GNU Coding Standards, and ideally causes no recompilation. | ||||||
|  | However, some platforms have known limitations with the semantics of | ||||||
|  | shared libraries that end up requiring recompilation when using this | ||||||
|  | method, particularly noticeable in packages that use GNU Libtool. | ||||||
|  |  | ||||||
|  |    The second method involves providing the `DESTDIR' variable.  For | ||||||
|  | example, `make install DESTDIR=/alternate/directory' will prepend | ||||||
|  | `/alternate/directory' before all installation names.  The approach of | ||||||
|  | `DESTDIR' overrides is not required by the GNU Coding Standards, and | ||||||
|  | does not work on platforms that have drive letters.  On the other hand, | ||||||
|  | it does better at avoiding recompilation issues, and works well even | ||||||
|  | when some directory options were not specified in terms of `${prefix}' | ||||||
|  | at `configure' time. | ||||||
|  |  | ||||||
|  | Optional Features | ||||||
|  | ================= | ||||||
|  |  | ||||||
|  |    If the package supports it, you can cause programs to be installed | ||||||
|  | with an extra prefix or suffix on their names by giving `configure' the | ||||||
|  | option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. | ||||||
|  |  | ||||||
|  |    Some packages pay attention to `--enable-FEATURE' options to | ||||||
|  | `configure', where FEATURE indicates an optional part of the package. | ||||||
|  | They may also pay attention to `--with-PACKAGE' options, where PACKAGE | ||||||
|  | is something like `gnu-as' or `x' (for the X Window System).  The | ||||||
|  | `README' should mention any `--enable-' and `--with-' options that the | ||||||
|  | package recognizes. | ||||||
|  |  | ||||||
|  |    For packages that use the X Window System, `configure' can usually | ||||||
|  | find the X include and library files automatically, but if it doesn't, | ||||||
|  | you can use the `configure' options `--x-includes=DIR' and | ||||||
|  | `--x-libraries=DIR' to specify their locations. | ||||||
|  |  | ||||||
|  |    Some packages offer the ability to configure how verbose the | ||||||
|  | execution of `make' will be.  For these packages, running `./configure | ||||||
|  | --enable-silent-rules' sets the default to minimal output, which can be | ||||||
|  | overridden with `make V=1'; while running `./configure | ||||||
|  | --disable-silent-rules' sets the default to verbose, which can be | ||||||
|  | overridden with `make V=0'. | ||||||
|  |  | ||||||
|  | Particular systems | ||||||
|  | ================== | ||||||
|  |  | ||||||
|  |    On HP-UX, the default C compiler is not ANSI C compatible.  If GNU | ||||||
|  | CC is not installed, it is recommended to use the following options in | ||||||
|  | order to use an ANSI C compiler: | ||||||
|  |  | ||||||
|  |      ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" | ||||||
|  |  | ||||||
|  | and if that doesn't work, install pre-built binaries of GCC for HP-UX. | ||||||
|  |  | ||||||
|  |    On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot | ||||||
|  | parse its `<wchar.h>' header file.  The option `-nodtk' can be used as | ||||||
|  | a workaround.  If GNU CC is not installed, it is therefore recommended | ||||||
|  | to try | ||||||
|  |  | ||||||
|  |      ./configure CC="cc" | ||||||
|  |  | ||||||
|  | and if that doesn't work, try | ||||||
|  |  | ||||||
|  |      ./configure CC="cc -nodtk" | ||||||
|  |  | ||||||
|  |    On Solaris, don't put `/usr/ucb' early in your `PATH'.  This | ||||||
|  | directory contains several dysfunctional programs; working variants of | ||||||
|  | these programs are available in `/usr/bin'.  So, if you need `/usr/ucb' | ||||||
|  | in your `PATH', put it _after_ `/usr/bin'. | ||||||
|  |  | ||||||
|  |    On Haiku, software installed for all users goes in `/boot/common', | ||||||
|  | not `/usr/local'.  It is recommended to use the following options: | ||||||
|  |  | ||||||
|  |      ./configure --prefix=/boot/common | ||||||
|  |  | ||||||
|  | Specifying the System Type | ||||||
|  | ========================== | ||||||
|  |  | ||||||
|  |    There may be some features `configure' cannot figure out | ||||||
|  | automatically, but needs to determine by the type of machine the package | ||||||
|  | will run on.  Usually, assuming the package is built to be run on the | ||||||
|  | _same_ architectures, `configure' can figure that out, but if it prints | ||||||
|  | a message saying it cannot guess the machine type, give it the | ||||||
|  | `--build=TYPE' option.  TYPE can either be a short name for the system | ||||||
|  | type, such as `sun4', or a canonical name which has the form: | ||||||
|  |  | ||||||
|  |      CPU-COMPANY-SYSTEM | ||||||
|  |  | ||||||
|  | where SYSTEM can have one of these forms: | ||||||
|  |  | ||||||
|  |      OS | ||||||
|  |      KERNEL-OS | ||||||
|  |  | ||||||
|  |    See the file `config.sub' for the possible values of each field.  If | ||||||
|  | `config.sub' isn't included in this package, then this package doesn't | ||||||
|  | need to know the machine type. | ||||||
|  |  | ||||||
|  |    If you are _building_ compiler tools for cross-compiling, you should | ||||||
|  | use the option `--target=TYPE' to select the type of system they will | ||||||
|  | produce code for. | ||||||
|  |  | ||||||
|  |    If you want to _use_ a cross compiler, that generates code for a | ||||||
|  | platform different from the build platform, you should specify the | ||||||
|  | "host" platform (i.e., that on which the generated programs will | ||||||
|  | eventually be run) with `--host=TYPE'. | ||||||
|  |  | ||||||
|  | Sharing Defaults | ||||||
|  | ================ | ||||||
|  |  | ||||||
|  |    If you want to set default values for `configure' scripts to share, | ||||||
|  | you can create a site shell script called `config.site' that gives | ||||||
|  | default values for variables like `CC', `cache_file', and `prefix'. | ||||||
|  | `configure' looks for `PREFIX/share/config.site' if it exists, then | ||||||
|  | `PREFIX/etc/config.site' if it exists.  Or, you can set the | ||||||
|  | `CONFIG_SITE' environment variable to the location of the site script. | ||||||
|  | A warning: not all `configure' scripts look for a site script. | ||||||
|  |  | ||||||
|  | Defining Variables | ||||||
|  | ================== | ||||||
|  |  | ||||||
|  |    Variables not defined in a site shell script can be set in the | ||||||
|  | environment passed to `configure'.  However, some packages may run | ||||||
|  | configure again during the build, and the customized values of these | ||||||
|  | variables may be lost.  In order to avoid this problem, you should set | ||||||
|  | them in the `configure' command line, using `VAR=value'.  For example: | ||||||
|  |  | ||||||
|  |      ./configure CC=/usr/local2/bin/gcc | ||||||
|  |  | ||||||
|  | causes the specified `gcc' to be used as the C compiler (unless it is | ||||||
|  | overridden in the site shell script). | ||||||
|  |  | ||||||
|  | Unfortunately, this technique does not work for `CONFIG_SHELL' due to | ||||||
|  | an Autoconf bug.  Until the bug is fixed you can use this workaround: | ||||||
|  |  | ||||||
|  |      CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash | ||||||
|  |  | ||||||
|  | `configure' Invocation | ||||||
|  | ====================== | ||||||
|  |  | ||||||
|  |    `configure' recognizes the following options to control how it | ||||||
|  | operates. | ||||||
|  |  | ||||||
|  | `--help' | ||||||
|  | `-h' | ||||||
|  |      Print a summary of all of the options to `configure', and exit. | ||||||
|  |  | ||||||
|  | `--help=short' | ||||||
|  | `--help=recursive' | ||||||
|  |      Print a summary of the options unique to this package's | ||||||
|  |      `configure', and exit.  The `short' variant lists options used | ||||||
|  |      only in the top level, while the `recursive' variant lists options | ||||||
|  |      also present in any nested packages. | ||||||
|  |  | ||||||
|  | `--version' | ||||||
|  | `-V' | ||||||
|  |      Print the version of Autoconf used to generate the `configure' | ||||||
|  |      script, and exit. | ||||||
|  |  | ||||||
|  | `--cache-file=FILE' | ||||||
|  |      Enable the cache: use and save the results of the tests in FILE, | ||||||
|  |      traditionally `config.cache'.  FILE defaults to `/dev/null' to | ||||||
|  |      disable caching. | ||||||
|  |  | ||||||
|  | `--config-cache' | ||||||
|  | `-C' | ||||||
|  |      Alias for `--cache-file=config.cache'. | ||||||
|  |  | ||||||
|  | `--quiet' | ||||||
|  | `--silent' | ||||||
|  | `-q' | ||||||
|  |      Do not print messages saying which checks are being made.  To | ||||||
|  |      suppress all normal output, redirect it to `/dev/null' (any error | ||||||
|  |      messages will still be shown). | ||||||
|  |  | ||||||
|  | `--srcdir=DIR' | ||||||
|  |      Look for the package's source code in directory DIR.  Usually | ||||||
|  |      `configure' can determine that directory automatically. | ||||||
|  |  | ||||||
|  | `--prefix=DIR' | ||||||
|  |      Use DIR as the installation prefix.  *note Installation Names:: | ||||||
|  |      for more details, including other options available for fine-tuning | ||||||
|  |      the installation locations. | ||||||
|  |  | ||||||
|  | `--no-create' | ||||||
|  | `-n' | ||||||
|  |      Run the configure checks, but stop before creating any output | ||||||
|  |      files. | ||||||
|  |  | ||||||
|  | `configure' also accepts some other, not widely useful, options.  Run | ||||||
|  | `configure --help' for more details. | ||||||
|  |  | ||||||
							
								
								
									
										6
									
								
								Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								Makefile.am
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | |||||||
|  | SUBDIRS = src tests | ||||||
|  |  | ||||||
|  | #EXTRA_DIST = LEGAL acinclude.m4 include | ||||||
|  |  | ||||||
|  | ACLOCAL_AMFLAGS = -I m4 | ||||||
|  |  | ||||||
							
								
								
									
										40
									
								
								README
									
									
									
									
									
								
							
							
						
						
									
										40
									
								
								README
									
									
									
									
									
								
							| @@ -0,0 +1,40 @@ | |||||||
|  |  | ||||||
|  | 1. Generate private key | ||||||
|  |  | ||||||
|  | openssl genrsa -out privkey_evm.pem 1024 | ||||||
|  |  | ||||||
|  | 2. Generate public key | ||||||
|  |  | ||||||
|  | openssl rsa -pubout -in privkey_evm.pem -out pubkey_evm.pem | ||||||
|  |  | ||||||
|  | 3. Copy public (+private if to sign on device) key to the device/qemu /etc/keys | ||||||
|  |  | ||||||
|  | scp pubkey_evm.pem mad:/etc/keys | ||||||
|  |  | ||||||
|  | 4. Load keys and enable EVM | ||||||
|  |  | ||||||
|  | evm_enable.sh | ||||||
|  |  | ||||||
|  | This should be done at early phase, before mounting root filesystem. | ||||||
|  |  | ||||||
|  | 5. Sign EVM and use hash value for IMA - common case | ||||||
|  |  | ||||||
|  | evmctl sign --imahash test.txt | ||||||
|  |  | ||||||
|  | 6. Sign IMA and EVM - for immutable files and modules | ||||||
|  |  | ||||||
|  | evmctl sign --imasig test.txt | ||||||
|  |  | ||||||
|  | 7. Label whole filesystem | ||||||
|  |  | ||||||
|  | evm_label_all.sh | ||||||
|  | or | ||||||
|  | find / \( -fstype rootfs -o -fstype ext3 -o -fstype ext4 \) ! -path "/lib/modules/*" -type f -uid 0 -exec evmctl sign --imahash '{}' \; | ||||||
|  | find /lib/modules ! -name "*.ko" -type f -uid 0 -exec evmctl sign --imahash '{}' \; | ||||||
|  | # security.ima needs to have signature for modules | ||||||
|  | find /lib/modules -name "*.ko" -type f -uid 0 -exec evmctl sign --imasig '{}' \; | ||||||
|  |  | ||||||
|  | 8. Label filesystem in fix mode... | ||||||
|  |  | ||||||
|  | ima_fix_dir.sh <dir> | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										14
									
								
								acinclude.m4
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								acinclude.m4
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | |||||||
|  |  | ||||||
|  | AC_DEFUN([PKG_ARG_ENABLE], | ||||||
|  | 	[ | ||||||
|  | 	AC_MSG_CHECKING(whether to enable $1) | ||||||
|  | 	AC_ARG_ENABLE([$1], AC_HELP_STRING([--enable-$1], [enable $1 (default is $2)]),  | ||||||
|  | 	[pkg_cv_enable_$1=$enableval],	 | ||||||
|  | 	[AC_CACHE_VAL([pkg_cv_enable_$1], [pkg_cv_enable_$1=$2])]) | ||||||
|  | 	if test $pkg_cv_enable_$1 = yes; then | ||||||
|  | 		AC_DEFINE([$3],, [$4]) | ||||||
|  | 	fi | ||||||
|  | 	AC_MSG_RESULT([$pkg_cv_enable_$1]) | ||||||
|  | 	AM_CONDITIONAL($3, test $pkg_cv_enable_$1 = yes) | ||||||
|  | ]) | ||||||
|  |  | ||||||
							
								
								
									
										16
									
								
								autogen.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										16
									
								
								autogen.sh
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,16 @@ | |||||||
|  | #! /bin/sh | ||||||
|  |  | ||||||
|  | set -e | ||||||
|  |  | ||||||
|  | # new way | ||||||
|  | # strange, but need this for Makefile.am, because it has -I m4 | ||||||
|  | test -d m4 || mkdir m4 | ||||||
|  | autoreconf -f -i | ||||||
|  |  | ||||||
|  | # old way | ||||||
|  | #libtoolize --automake --copy --force | ||||||
|  | #aclocal | ||||||
|  | #autoconf --force | ||||||
|  | #autoheader --force | ||||||
|  | #automake --add-missing --copy --force-missing --gnu | ||||||
|  |  | ||||||
							
								
								
									
										73
									
								
								configure.ac
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								configure.ac
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,73 @@ | |||||||
|  | # autoconf script | ||||||
|  |  | ||||||
|  | AC_PREREQ([2.65]) | ||||||
|  | AC_INIT(evm-utils, 0.1, dmitry.kasatkin@intel.com) | ||||||
|  | AM_INIT_AUTOMAKE(AC_PACKAGE_NAME, AC_PACKAGE_VERSION) | ||||||
|  | AC_CONFIG_HEADERS([config.h]) | ||||||
|  | AC_CONFIG_MACRO_DIR([m4]) | ||||||
|  |   | ||||||
|  | AC_CANONICAL_HOST | ||||||
|  |  | ||||||
|  | # Checks for programs. | ||||||
|  | AC_PROG_CC | ||||||
|  | AM_PROG_CC_C_O | ||||||
|  | #AC_PROG_CXX | ||||||
|  | #AC_PROG_CPP | ||||||
|  | AC_PROG_INSTALL | ||||||
|  | AC_PROG_LIBTOOL | ||||||
|  | #AC_PROG_LN_S | ||||||
|  | LT_INIT | ||||||
|  |  | ||||||
|  | # FIXME: Replace `main' with a function in `-lpthread': | ||||||
|  | #AC_CHECK_LIB([pthread], [main]) | ||||||
|  |  | ||||||
|  | # Checks for header files. | ||||||
|  | AC_HEADER_STDC | ||||||
|  |  | ||||||
|  | PKG_CHECK_MODULES(OPENSSL, [ openssl >= 0.9.8 ]) | ||||||
|  | AC_SUBST(OPENSSL_CFLAGS) | ||||||
|  | AC_SUBST(OPENSSL_LIBS) | ||||||
|  | AC_CHECK_HEADER(unistd.h) | ||||||
|  | AC_CHECK_HEADERS(openssl/conf.h) | ||||||
|  |  | ||||||
|  | AC_ARG_WITH(readline, [  --without-readline	without support funcy command editing for ncp]) | ||||||
|  | if test "$with_readline" != "no"; then | ||||||
|  | 	__LIBS="$LIBS" | ||||||
|  | 	AC_CHECK_LIB(readline, readline) | ||||||
|  | 	AC_CHECK_HEADERS(readline/readline.h,,  | ||||||
|  | 		[AC_MSG_ERROR([readline header files not found.  | ||||||
|  | 		Please, install libreadline for that. or use --without-readline])]) | ||||||
|  | 	LIBS="$__LIBS" | ||||||
|  | 	LDFLAGS_READLINE="-lreadline" | ||||||
|  | fi | ||||||
|  |  | ||||||
|  | #debug support - yes for a while | ||||||
|  | PKG_ARG_ENABLE(debug, "yes", DEBUG, [Enable Debug support]) | ||||||
|  | if test $pkg_cv_enable_debug = yes; then | ||||||
|  | 	CFLAGS="-g -O1 -Wall -Wstrict-prototypes -pipe" | ||||||
|  | else | ||||||
|  | 	CFLAGS="$CFLAGS -Wall -Wstrict-prototypes -pipe -fomit-frame-pointer" | ||||||
|  | fi | ||||||
|  |  | ||||||
|  | # for gcov | ||||||
|  | #CFLAGS="$CFLAGS -Wall -fprofile-arcs -ftest-coverage" | ||||||
|  | #CXXFLAGS="$CXXFLAGS -Wall -fprofile-arcs -ftest-coverage" | ||||||
|  | #LDFLAGS="$LDFLAGS -fprofile-arcs" | ||||||
|  | #DISTCLEANFILES="*.gcno *.gcda" | ||||||
|  |  | ||||||
|  | AC_SUBST(LDFLAGS_READLINE) | ||||||
|  |  | ||||||
|  | AC_CONFIG_FILES([Makefile | ||||||
|  | 		src/Makefile | ||||||
|  | 		tests/Makefile | ||||||
|  | 		evm-utils.spec | ||||||
|  | 		]) | ||||||
|  | AC_OUTPUT | ||||||
|  |  | ||||||
|  | # Give some feedback | ||||||
|  | echo | ||||||
|  | echo | ||||||
|  | echo	"Configuration:"		 | ||||||
|  | echo	"	debug:	$pkg_cv_enable_debug" | ||||||
|  | echo | ||||||
|  |  | ||||||
							
								
								
									
										53
									
								
								evm-utils.spec.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								evm-utils.spec.in
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,53 @@ | |||||||
|  | Name:		@PACKAGE_NAME@ | ||||||
|  | Version:	@PACKAGE_VERSION@ | ||||||
|  | Release:	1%{?dist} | ||||||
|  | Summary:	evm-utils - IMA/EVM support utilities | ||||||
|  | Group:		System/Libraries | ||||||
|  | License:	LGPLv2 | ||||||
|  | #URL:		 | ||||||
|  | Source0:	%{name}-%{version}.tar.gz | ||||||
|  | BuildRoot:	%{_tmppath}/%{name}-%{version}-%{release}-root | ||||||
|  |  | ||||||
|  | BuildRequires:    autoconf | ||||||
|  | BuildRequires:    automake | ||||||
|  | BuildRequires:    openssl-devel | ||||||
|  | BuildRequires:    libattr-devel | ||||||
|  | BuildRequires:    readline-devel | ||||||
|  | BuildRequires:    keyutils-libs-devel | ||||||
|  |  | ||||||
|  | %description | ||||||
|  | This library provides EVM support utilities. | ||||||
|  |  | ||||||
|  | %prep | ||||||
|  | %setup -q | ||||||
|  |  | ||||||
|  | %build | ||||||
|  | ./autogen.sh | ||||||
|  | %configure --prefix=/usr | ||||||
|  | make | ||||||
|  |  | ||||||
|  | %install | ||||||
|  | rm -rf %{buildroot} | ||||||
|  | make DESTDIR=%{buildroot} install | ||||||
|  |  | ||||||
|  | %clean | ||||||
|  | rm -rf %{buildroot} | ||||||
|  |  | ||||||
|  | %post | ||||||
|  | /sbin/ldconfig | ||||||
|  | exit 0 | ||||||
|  |  | ||||||
|  | %preun -p /sbin/ldconfig | ||||||
|  |  | ||||||
|  | %postun | ||||||
|  | /sbin/ldconfig | ||||||
|  |  | ||||||
|  | %files | ||||||
|  | %defattr(-,root,root,-) | ||||||
|  | %{_bindir}/* | ||||||
|  | %{_libdir}/* | ||||||
|  |  | ||||||
|  | %changelog | ||||||
|  | * Wed Jul 20 2011 Dmitry Kasatkin <dmitry.kasatkin@intel.com> | ||||||
|  | - Initial package for MeeGo | ||||||
|  |  | ||||||
							
								
								
									
										12
									
								
								src/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/Makefile.am
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | |||||||
|  |  | ||||||
|  | bin_PROGRAMS = evmctl | ||||||
|  |  | ||||||
|  | evmctl_SOURCES = evmctl.c | ||||||
|  | evmctl_CPPFLAGS = $(OPENSSL_CFLAGS) | ||||||
|  | evmctl_LDFLAGS = $(LDFLAGS_READLINE) | ||||||
|  | evmctl_LDADD =  $(OPENSSL_LIBS) -lkeyutils | ||||||
|  |  | ||||||
|  | INCLUDES = -I$(top_srcdir) -include config.h | ||||||
|  |  | ||||||
|  | DISTCLEANFILES = @DISTCLEANFILES@ | ||||||
|  |  | ||||||
							
								
								
									
										985
									
								
								src/evmctl.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										985
									
								
								src/evmctl.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,985 @@ | |||||||
|  | /* | ||||||
|  |  * evm-utils - IMA/EVM support utilities | ||||||
|  |  * | ||||||
|  |  * Copyright (C) 2011 Nokia Corporation | ||||||
|  |  * Copyright (C) 2011 Intel Corporation | ||||||
|  |  * | ||||||
|  |  * Authors: | ||||||
|  |  * Dmitry Kasatkin <dmitry.kasatkin@nokia.com> | ||||||
|  |  *                 <dmitry.kasatkin@intel.com> | ||||||
|  |  * | ||||||
|  |  * This library is free software; you can redistribute it and/or | ||||||
|  |  * modify it under the terms of the GNU Lesser General Public License | ||||||
|  |  * version 2.1 as published by the Free Software Foundation. | ||||||
|  |  * | ||||||
|  |  * This library is distributed in the hope that it will be useful, but | ||||||
|  |  * WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||||||
|  |  * Lesser General Public License for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU Lesser General Public | ||||||
|  |  * License along with this library; if not, write to the Free Software | ||||||
|  |  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||||||
|  |  * 02110-1301 USA | ||||||
|  |  * | ||||||
|  |  * File: evmctl.c | ||||||
|  |  *	 IMA/EVM control program | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include <sys/types.h> | ||||||
|  | #include <sys/stat.h> | ||||||
|  | #include <sys/ioctl.h> | ||||||
|  | #include <sys/param.h> | ||||||
|  | #include <fcntl.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <string.h> | ||||||
|  | #include <attr/xattr.h> | ||||||
|  | #include <getopt.h> | ||||||
|  | #include <signal.h> | ||||||
|  | #include <keyutils.h> | ||||||
|  | #include <asm/byteorder.h> | ||||||
|  | #include <syslog.h> | ||||||
|  | #include <attr/xattr.h> | ||||||
|  |  | ||||||
|  | #ifdef HAVE_READLINE_READLINE_H | ||||||
|  | #include <readline/readline.h> | ||||||
|  | #include <readline/history.h> | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #include <openssl/sha.h> | ||||||
|  | #include <openssl/sha.h> | ||||||
|  | #include <openssl/rsa.h> | ||||||
|  | #include <openssl/pem.h> | ||||||
|  |  | ||||||
|  | #define USE_FPRINTF | ||||||
|  |  | ||||||
|  | #ifdef USE_FPRINTF | ||||||
|  | #define do_log(level, fmt, args...)	if (level <= verbose) fprintf(stderr, fmt, ##args) | ||||||
|  | #define do_log_dump(level, p, len)	if (level <= verbose) do_dump(stderr, p, len) | ||||||
|  | #else | ||||||
|  | #define do_log(level, fmt, args...)	syslog(level, fmt, ##args) | ||||||
|  | #define do_log_dump(p, len) | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #ifdef DEBUG | ||||||
|  | #define log_debug(fmt, args...)		do_log(LOG_DEBUG, "%s:%d " fmt, __func__ , __LINE__ , ##args) | ||||||
|  | #define log_debug_dump(p, len)		do_log_dump(LOG_DEBUG, p, len) | ||||||
|  | #else | ||||||
|  | #define log_debug(fmt, args...) | ||||||
|  | #define log_debug_dump(p, len) | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #define log_dump(p, len)		do_log_dump(LOG_INFO, p, len) | ||||||
|  | #define log_info(fmt, args...)		do_log(LOG_INFO, fmt, ##args) | ||||||
|  | #define log_err(fmt, args...)		do_log(LOG_ERR, fmt, ##args) | ||||||
|  | #define log_errno(fmt, args...)		do_log(LOG_ERR, fmt ": %s (%d)\n", ##args, strerror(errno), errno) | ||||||
|  |  | ||||||
|  | #define	DATA_SIZE	4096 | ||||||
|  | #define SHA1_HASH_LEN   20 | ||||||
|  |  | ||||||
|  | #define	EXT2_IOC_GETVERSION	_IOR('v', 1, long) | ||||||
|  | #define	EXT34_IOC_GETVERSION	_IOR('f', 3, long) | ||||||
|  |  | ||||||
|  | #define	FS_IOC_GETFLAGS		_IOR('f', 1, long) | ||||||
|  | #define	FS_IOC_SETFLAGS		_IOW('f', 2, long) | ||||||
|  | #define FS_IOC32_GETFLAGS	_IOR('f', 1, int) | ||||||
|  | #define FS_IOC32_SETFLAGS	_IOW('f', 2, int) | ||||||
|  |  | ||||||
|  | struct h_misc { | ||||||
|  | 	unsigned long ino; | ||||||
|  | 	uint32_t generation; | ||||||
|  | 	uid_t uid; | ||||||
|  | 	gid_t gid; | ||||||
|  | 	unsigned short mode; | ||||||
|  | } hmac_misc; | ||||||
|  |  | ||||||
|  | enum pubkey_algo { | ||||||
|  | 	PUBKEY_ALGO_RSA, | ||||||
|  | 	PUBKEY_ALGO_MAX, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | enum digest_algo { | ||||||
|  | 	DIGEST_ALGO_SHA1, | ||||||
|  | 	DIGEST_ALGO_SHA256, | ||||||
|  | 	DIGEST_ALGO_MAX | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct pubkey_hdr { | ||||||
|  | 	uint8_t		version;	/* key format version */ | ||||||
|  | 	time_t		timestamp;	/* key made, always 0 for now */ | ||||||
|  | 	uint8_t		algo; | ||||||
|  | 	uint8_t		nmpi; | ||||||
|  | 	char		mpi[0]; | ||||||
|  | } __attribute__ ((packed)); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | struct signature_hdr { | ||||||
|  | 	uint8_t		version;	/* signature format version */ | ||||||
|  | 	time_t		timestamp;	/* signature made */ | ||||||
|  | 	uint8_t		algo; | ||||||
|  | 	uint8_t		hash; | ||||||
|  | 	uint8_t		keyid[8]; | ||||||
|  | 	uint8_t		nmpi; | ||||||
|  | 	char		mpi[0]; | ||||||
|  | } __attribute__ ((packed)); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static char *evm_config_xattrnames[] = { | ||||||
|  | 	"security.selinux", | ||||||
|  | 	"security.SMACK64", | ||||||
|  | 	"security.ima", | ||||||
|  | 	"security.capability", | ||||||
|  | 	NULL | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct command { | ||||||
|  | 	char	*name; | ||||||
|  | 	int	(*func)(struct command *cmd); | ||||||
|  | 	int	cmd; | ||||||
|  | 	char	*arg; | ||||||
|  | 	char	*msg;	/* extra info message */ | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static int		verbose = LOG_INFO - 1; | ||||||
|  | static int		g_argc; | ||||||
|  | static char		**g_argv; | ||||||
|  | static int		set_xattr = 1; | ||||||
|  | static int		digest = 0; | ||||||
|  | static int		digsig = 0; | ||||||
|  | static char		*hash_algo = "sha1"; | ||||||
|  | static int		binkey = 0; | ||||||
|  |  | ||||||
|  | extern struct command	cmds[]; | ||||||
|  | static void print_usage(struct command *cmd); | ||||||
|  |  | ||||||
|  | static void do_dump(FILE *fp, const void *ptr, int len) | ||||||
|  | { | ||||||
|  |         int     i; | ||||||
|  |         uint8_t *data = (uint8_t *)ptr; | ||||||
|  |  | ||||||
|  |         for (i = 0; i < len; i++) { | ||||||
|  |                 fprintf(fp, "%02x", data[i]); | ||||||
|  |         } | ||||||
|  |         fprintf(fp, "\n"); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void dump(const void *ptr, int len) | ||||||
|  | { | ||||||
|  | 	do_dump(stdout, ptr, len); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static inline int get_filesize(const char *filename) | ||||||
|  | { | ||||||
|  | 	struct stat stats; | ||||||
|  | 	/*  Need to know the file length */ | ||||||
|  | 	stat(filename, &stats); | ||||||
|  | 	return (int) stats.st_size; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static inline int get_fdsize(int fd) | ||||||
|  | { | ||||||
|  | 	struct stat stats; | ||||||
|  | 	/*  Need to know the file length */ | ||||||
|  | 	fstat(fd, &stats); | ||||||
|  | 	return (int) stats.st_size; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int bin2file(const char *file, const char *ext, const unsigned char *data, int len) | ||||||
|  | { | ||||||
|  | 	FILE *fp; | ||||||
|  | 	char name[strlen(file) + (ext ? strlen(ext) : 0) + 2]; | ||||||
|  | 	int err; | ||||||
|  | 	 | ||||||
|  | 	if (ext) | ||||||
|  | 		sprintf(name, "%s.%s", file, ext); | ||||||
|  | 	else | ||||||
|  | 		sprintf(name, "%s", file); | ||||||
|  |  | ||||||
|  | 	log_info("Writing to %s\n", name); | ||||||
|  |  | ||||||
|  | 	fp = fopen(name, "w"); | ||||||
|  | 	if (!fp) { | ||||||
|  | 		log_errno("Unable to open %s for writing", name); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  | 	err = fwrite(data, len, 1, fp); | ||||||
|  | 	fclose(fp); | ||||||
|  | 	return err; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static char *file2bin(const char *file, int *size) | ||||||
|  | { | ||||||
|  | 	FILE *fp; | ||||||
|  | 	int len; | ||||||
|  | 	char *data; | ||||||
|  | 		 | ||||||
|  | 	len = get_filesize(file); | ||||||
|  | 	fp = fopen(file, "r"); | ||||||
|  | 	if (!fp) { | ||||||
|  | 		log_errno("Unable to open %s", file); | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  | 	data = malloc(len); | ||||||
|  | 	if (!fread(data, len, 1, fp)) | ||||||
|  | 		len = 0; | ||||||
|  | 	fclose(fp); | ||||||
|  | 	 | ||||||
|  | 	*size = len; | ||||||
|  | 	return data;	 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Create binary key representation suitable for kernel | ||||||
|  |  */ | ||||||
|  | static int key2bin(RSA *key, unsigned char *pub) | ||||||
|  | { | ||||||
|  | 	int len, b, offset = 0; | ||||||
|  | 	struct pubkey_hdr *pkh = (struct pubkey_hdr *)pub; | ||||||
|  |  | ||||||
|  | 	/* add key header */ | ||||||
|  | 	pkh->version = 1; | ||||||
|  | 	pkh->timestamp = 0;	/* PEM has no timestamp?? */ | ||||||
|  | 	pkh->algo = PUBKEY_ALGO_RSA; | ||||||
|  | 	pkh->nmpi = 2; | ||||||
|  | 	 | ||||||
|  | 	offset += sizeof(*pkh); | ||||||
|  | 	 | ||||||
|  | 	// MPIs | ||||||
|  | 	len = BN_num_bytes(key->n); | ||||||
|  | 	b = BN_num_bits(key->n); | ||||||
|  | 	pub[offset++] = b >> 8; | ||||||
|  | 	pub[offset++] = b & 0xff; | ||||||
|  | 	BN_bn2bin(key->n, &pub[offset]); | ||||||
|  | 	offset += len; | ||||||
|  | 	 | ||||||
|  | 	len = BN_num_bytes(key->e); | ||||||
|  | 	b = BN_num_bits(key->e); | ||||||
|  | 	pub[offset++] = b >> 8; | ||||||
|  | 	pub[offset++] = b & 0xff; | ||||||
|  | 	BN_bn2bin(key->e, &pub[offset]); | ||||||
|  | 	offset += len; | ||||||
|  | 	 | ||||||
|  | 	return offset; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int read_key(const char *inkey, unsigned char *pub) | ||||||
|  | { | ||||||
|  | 	FILE *fp; | ||||||
|  | 	RSA *key = NULL, *key1; | ||||||
|  | 	int len; | ||||||
|  |  | ||||||
|  | 	fp = fopen(inkey, "r"); | ||||||
|  | 	if (!fp) { | ||||||
|  | 		log_errno("read key failed from file %s", inkey); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	key1 = PEM_read_RSA_PUBKEY(fp, &key, NULL, NULL); | ||||||
|  | 	fclose(fp); | ||||||
|  | 	if (!key1) { | ||||||
|  | 		log_errno("PEM_read_RSA_PUBKEY() failed"); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	len = key2bin(key, pub); | ||||||
|  | 	 | ||||||
|  | 	RSA_free(key); | ||||||
|  | 	 | ||||||
|  | 	return len; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static void calc_keyid(uint8_t *keyid, char *str, const unsigned char *pkey, int len) | ||||||
|  | { | ||||||
|  | 	uint8_t sha1[SHA_DIGEST_LENGTH]; | ||||||
|  | 	uint64_t id; | ||||||
|  | 	 | ||||||
|  | 	log_debug("pkey:\n"); | ||||||
|  | 	log_debug_dump(pkey, len); | ||||||
|  | 	SHA1(pkey, len, sha1);	 | ||||||
|  | 	 | ||||||
|  | 	//sha1[12 - 19] is exactly keyid from gpg file | ||||||
|  | 	memcpy(keyid, sha1 + 12, 8); | ||||||
|  | 	log_debug("keyid:\n"); | ||||||
|  | 	log_debug_dump(keyid, 8); | ||||||
|  | 	 | ||||||
|  | 	id = __be64_to_cpup((uint64_t *)keyid); | ||||||
|  | 	sprintf(str, "%llX", id); | ||||||
|  | 	log_info("keyid: %s\n", str);	 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int sign_hash(const unsigned char *hash, int size, const char *keyfile, unsigned char *sig) | ||||||
|  | { | ||||||
|  | 	int err, len; | ||||||
|  | 	SHA_CTX ctx; | ||||||
|  | 	unsigned char pub[1024]; | ||||||
|  | 	RSA *key = NULL, *key1; | ||||||
|  | 	FILE *fp; | ||||||
|  | 	char name[20]; | ||||||
|  | 	unsigned char sighash[20]; | ||||||
|  | 	struct signature_hdr *hdr = (struct signature_hdr *)sig; | ||||||
|  | 	uint16_t *blen; | ||||||
|  | 	 | ||||||
|  | 	log_info("hash: "); | ||||||
|  | 	log_dump(hash, size); | ||||||
|  | 	 | ||||||
|  | 	fp = fopen(keyfile, "r");	 | ||||||
|  | 	if (!fp) { | ||||||
|  | 		log_errno("Unable to open keyfile %s", keyfile); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  | 	key1 = PEM_read_RSAPrivateKey(fp, &key, NULL, NULL); | ||||||
|  | 	fclose(fp); | ||||||
|  | 	if (!key1) { | ||||||
|  | 		log_errno("RSAPrivateKey() failed"); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* now create a new hash */ | ||||||
|  | 	hdr->version = 1; | ||||||
|  | 	time(&hdr->timestamp); | ||||||
|  | 	hdr->algo = PUBKEY_ALGO_RSA; | ||||||
|  | 	hdr->hash = DIGEST_ALGO_SHA1; | ||||||
|  | 	 | ||||||
|  | 	len = key2bin(key, pub); | ||||||
|  | 	calc_keyid(hdr->keyid, name, pub, len); | ||||||
|  | 	 | ||||||
|  | 	hdr->nmpi = 1; | ||||||
|  | 	 | ||||||
|  | 	SHA1_Init(&ctx); | ||||||
|  | 	SHA1_Update(&ctx, hash, size); | ||||||
|  | 	SHA1_Update(&ctx, hdr, sizeof(*hdr)); | ||||||
|  | 	SHA1_Final(sighash, &ctx); | ||||||
|  | 	log_info("sighash: "); | ||||||
|  | 	log_dump(sighash, sizeof(sighash)); | ||||||
|  |  | ||||||
|  | 	err = RSA_private_encrypt(sizeof(sighash), sighash, sig + sizeof(*hdr) + 2, key, RSA_PKCS1_PADDING); | ||||||
|  | 	RSA_free(key); | ||||||
|  | 	if (err < 0) { | ||||||
|  | 		log_errno("RSA_private_encrypt() failed: %d", err); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	len = err; | ||||||
|  | 	 | ||||||
|  | 	/* we add bit length of the signature to make it gnupg compatible */ | ||||||
|  | 	blen = (uint16_t *)(sig + sizeof(*hdr)); | ||||||
|  | 	*blen = __cpu_to_be16(len << 3); | ||||||
|  | 	len += sizeof(*hdr) + 2; | ||||||
|  | 	log_info("evm/ima signature: %d bytes\n", len); | ||||||
|  | 	if (!set_xattr || verbose >= LOG_INFO) | ||||||
|  | 		dump(sig, len); | ||||||
|  |  | ||||||
|  | 	return len;	 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int calc_evm_hash(const char *file, const char *keyfile, unsigned char *hash) | ||||||
|  | { | ||||||
|  | 	struct stat st; | ||||||
|  | 	int fd, err, len; | ||||||
|  | 	uint32_t generation; | ||||||
|  | 	SHA_CTX ctx; | ||||||
|  | 	char **xattrname; | ||||||
|  | 	char xattr_value[1024]; | ||||||
|  | 	 | ||||||
|  | 	fd = open(file, 0); | ||||||
|  | 	if (fd < 0) { | ||||||
|  | 		log_errno("Unable to open %s", file); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	if (fstat(fd, &st)) { | ||||||
|  | 		log_errno("fstat() failed"); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	len = st.st_size; | ||||||
|  | 	 | ||||||
|  | 	if (ioctl(fd, EXT34_IOC_GETVERSION, &generation)) { | ||||||
|  | 		log_errno("ioctl() failed"); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	close(fd); | ||||||
|  | 	 | ||||||
|  | 	log_info("generation: %u\n", generation); | ||||||
|  |  | ||||||
|  | 	SHA1_Init(&ctx); | ||||||
|  |  | ||||||
|  | 	for (xattrname = evm_config_xattrnames; *xattrname != NULL; xattrname++) { | ||||||
|  | 		err = getxattr(file, *xattrname, xattr_value, sizeof(xattr_value)); | ||||||
|  | 		if (err < 0) { | ||||||
|  | 			log_info("no attr: %s\n", *xattrname); | ||||||
|  | 			continue; | ||||||
|  | 		} | ||||||
|  | 		//log_debug("name: %s, value: %s, size: %d\n", *xattrname, xattr_value, err); | ||||||
|  | 		log_info("name: %s, size: %d\n", *xattrname, err); | ||||||
|  | 		log_debug_dump(xattr_value, err); | ||||||
|  | 		SHA1_Update(&ctx, xattr_value, err); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	memset(&hmac_misc, 0, sizeof(hmac_misc)); | ||||||
|  | 	hmac_misc.ino = st.st_ino; | ||||||
|  | 	hmac_misc.generation = generation; | ||||||
|  | 	hmac_misc.uid = st.st_uid; | ||||||
|  | 	hmac_misc.gid = st.st_gid; | ||||||
|  | 	hmac_misc.mode = st.st_mode; | ||||||
|  | 	 | ||||||
|  | 	SHA1_Update(&ctx, (const unsigned char*)&hmac_misc, sizeof(hmac_misc)); | ||||||
|  | 	SHA1_Final(hash, &ctx); | ||||||
|  | 	 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int sign_evm(const char *file, const char *key) | ||||||
|  | { | ||||||
|  | 	unsigned char hash[20]; | ||||||
|  | 	unsigned char sig[1024] = "\x03"; | ||||||
|  | 	int err; | ||||||
|  |  | ||||||
|  | 	calc_evm_hash(file, key, hash); | ||||||
|  | 	 | ||||||
|  | 	err = sign_hash(hash, sizeof(hash), key, sig + 1); | ||||||
|  | 	if (err < 0) | ||||||
|  | 		return err; | ||||||
|  | 	 | ||||||
|  | 	if (set_xattr) { | ||||||
|  | 		err = setxattr(file, "security.evm", sig, err + 1, 0); | ||||||
|  | 		if (err < 0) { | ||||||
|  | 			log_errno("setxattr failed: %s", file); | ||||||
|  | 			return err; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int calc_file_hash(const char *file, uint8_t *hash) | ||||||
|  | { | ||||||
|  | 	EVP_MD_CTX ctx; | ||||||
|  | 	const EVP_MD *md; | ||||||
|  | 	uint8_t	*data; | ||||||
|  | 	int err, size, bs = DATA_SIZE; | ||||||
|  | 	size_t len, mdlen; | ||||||
|  | 	FILE *fp; | ||||||
|  |  | ||||||
|  | 	data = malloc(bs); | ||||||
|  | 	if (!data) { | ||||||
|  | 		log_errno("malloc failed"); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  | 		 | ||||||
|  | 	fp = fopen(file, "r"); | ||||||
|  | 	if (!fp) { | ||||||
|  | 		log_errno("Unable to open %s", file); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	OpenSSL_add_all_digests(); | ||||||
|  |  | ||||||
|  | 	md = EVP_get_digestbyname(hash_algo); | ||||||
|  | 	if (!md) { | ||||||
|  | 		log_errno("EVP_get_digestbyname() failed"); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	err = EVP_DigestInit(&ctx, md); | ||||||
|  | 	if (!err) { | ||||||
|  | 		log_errno("EVP_DigestInit() failed"); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for (size = get_fdsize(fileno(fp)); size; size -= len) { | ||||||
|  | 		len = MIN(size, bs); | ||||||
|  | 		err = fread(data, len, 1, fp); | ||||||
|  | 		if (!err) { | ||||||
|  | 			if (ferror(fp)) { | ||||||
|  | 				log_errno("fread() error\n"); | ||||||
|  | 				return -1; | ||||||
|  | 			} | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 		err = EVP_DigestUpdate(&ctx, data, len); | ||||||
|  | 		if (!err) { | ||||||
|  | 			log_errno("EVP_DigestUpdate() failed"); | ||||||
|  | 			return -1; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	err = EVP_DigestFinal(&ctx, hash, &mdlen); | ||||||
|  | 	if (!err) { | ||||||
|  | 		log_errno("EVP_DigestFinal() failed"); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	fclose(fp); | ||||||
|  | 	 | ||||||
|  | 	free(data); | ||||||
|  | 	 | ||||||
|  | 	return mdlen; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int hash_ima(const char *file) | ||||||
|  | { | ||||||
|  | 	unsigned char hash[65] = "\x01";// MAX hash size + 1 | ||||||
|  | 	int err; | ||||||
|  |  | ||||||
|  | 	err = calc_file_hash(file, hash + 1); | ||||||
|  | 	if (err < 0) | ||||||
|  | 		return err; | ||||||
|  | 	 | ||||||
|  | 	if (!set_xattr || verbose >= LOG_INFO) | ||||||
|  | 		dump(hash, err + 1); | ||||||
|  |  | ||||||
|  | 	if (set_xattr) { | ||||||
|  | 		err = setxattr(file, "security.ima", hash, err + 1, 0); | ||||||
|  | 		if (err < 0) { | ||||||
|  | 			log_errno("setxattr failed: %s", file); | ||||||
|  | 			return err; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int cmd_hash_ima(struct command *cmd) | ||||||
|  | { | ||||||
|  | 	char *file = g_argv[optind++]; | ||||||
|  |  | ||||||
|  | 	if (!file) { | ||||||
|  | 		log_err("Parameters missing\n"); | ||||||
|  | 		print_usage(cmd); | ||||||
|  | 		return 1; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	return hash_ima(file); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int sign_ima(const char *file, const char *key) | ||||||
|  | { | ||||||
|  | 	unsigned char hash[64]; | ||||||
|  | 	unsigned char sig[1024] = "\x03"; | ||||||
|  | 	int err; | ||||||
|  | 	 | ||||||
|  | 	err = calc_file_hash(file, hash); | ||||||
|  | 	if (err < 0) | ||||||
|  | 		return err; | ||||||
|  | 	 | ||||||
|  | 	err = sign_hash(hash, err, key, sig + 1); | ||||||
|  | 	if (err < 0) | ||||||
|  | 		return err; | ||||||
|  | 	 | ||||||
|  | 	if (set_xattr) { | ||||||
|  | 		err = setxattr(file, "security.ima", sig, err + 1, 0); | ||||||
|  | 		if (err < 0) { | ||||||
|  | 			log_errno("setxattr failed: %s", file); | ||||||
|  | 			return err; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int cmd_sign_ima(struct command *cmd) | ||||||
|  | { | ||||||
|  | 	char *key, *file = g_argv[optind++]; | ||||||
|  | 	 | ||||||
|  | 	if (!file) { | ||||||
|  | 		log_err("Parameters missing\n"); | ||||||
|  | 		print_usage(cmd); | ||||||
|  | 		return 1; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	key = g_argv[optind++]; | ||||||
|  | 	if (!key) | ||||||
|  | 		key = "/etc/keys/privkey_evm.pem"; | ||||||
|  | 	 | ||||||
|  | 	return sign_ima(file, key); | ||||||
|  | 	 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int cmd_sign_evm(struct command *cmd) | ||||||
|  | { | ||||||
|  | 	char *key, *file = g_argv[optind++]; | ||||||
|  | 	int err; | ||||||
|  | 	 | ||||||
|  | 	if (!file) { | ||||||
|  | 		log_err("Parameters missing\n"); | ||||||
|  | 		print_usage(cmd); | ||||||
|  | 		return 1; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	key = g_argv[optind++]; | ||||||
|  | 	if (!key) | ||||||
|  | 		key = "/etc/keys/privkey_evm.pem"; | ||||||
|  | 	 | ||||||
|  | 	if (digsig) { | ||||||
|  | 		err = sign_ima(file, key); | ||||||
|  | 		if (err) | ||||||
|  | 			return err; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	if (digest) { | ||||||
|  | 		err = hash_ima(file); | ||||||
|  | 		if (err) | ||||||
|  | 			return err; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	return sign_evm(file, key); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int verify_hash(const unsigned char *hash, int size, unsigned char *sig, int siglen, const char *keyfile) | ||||||
|  | { | ||||||
|  | 	int err, len; | ||||||
|  | 	SHA_CTX ctx; | ||||||
|  | 	unsigned char out[1024]; | ||||||
|  | 	RSA *key = NULL, *key1; | ||||||
|  | 	FILE *fp; | ||||||
|  | 	unsigned char sighash[20]; | ||||||
|  | 	struct signature_hdr *hdr = (struct signature_hdr *)sig; | ||||||
|  | 	 | ||||||
|  | 	log_info("hash: "); | ||||||
|  | 	log_dump(hash, size); | ||||||
|  | 	 | ||||||
|  | 	fp = fopen(keyfile, "r");	 | ||||||
|  | 	if (!fp) { | ||||||
|  | 		log_errno("Unable to open keyfile %s", keyfile); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  | 	key1 = PEM_read_RSA_PUBKEY(fp, &key, NULL, NULL); | ||||||
|  | 	fclose(fp); | ||||||
|  | 	if (!key1) { | ||||||
|  | 		log_errno("PEM_read_RSA_PUBKEY() failed"); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	SHA1_Init(&ctx); | ||||||
|  | 	SHA1_Update(&ctx, hash, size); | ||||||
|  | 	SHA1_Update(&ctx, hdr, sizeof(*hdr)); | ||||||
|  | 	SHA1_Final(sighash, &ctx); | ||||||
|  | 	log_info("sighash: "); | ||||||
|  | 	log_dump(sighash, sizeof(sighash)); | ||||||
|  |  | ||||||
|  | 	err = RSA_public_decrypt(siglen - sizeof(*hdr) - 2, sig + sizeof(*hdr) + 2, out, key, RSA_PKCS1_PADDING); | ||||||
|  | 	RSA_free(key); | ||||||
|  | 	if (err < 0) { | ||||||
|  | 		log_errno("RSA_public_decrypt() failed: %d", err); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	len = err; | ||||||
|  | 	 | ||||||
|  | 	if (len != sizeof(sighash) || memcmp(out, sighash, len) != 0) { | ||||||
|  | 		log_errno("Verification failed: %d", err); | ||||||
|  | 		return -1; | ||||||
|  | 	} else { | ||||||
|  | 		//log_info("Verification is OK\n"); | ||||||
|  | 		printf("Verification is OK\n"); | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int verify_evm(const char *file, const char *key) | ||||||
|  | { | ||||||
|  | 	unsigned char hash[20]; | ||||||
|  | 	unsigned char sig[1024]; | ||||||
|  | 	int err; | ||||||
|  |  | ||||||
|  | 	calc_evm_hash(file, key, hash); | ||||||
|  | 	 | ||||||
|  | 	err = getxattr(file, "security.evm", sig, sizeof(sig)); | ||||||
|  | 	if (err < 0) { | ||||||
|  | 		log_errno("getxattr failed"); | ||||||
|  | 		return err; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	if (sig[0] != 0x03) { | ||||||
|  | 		log_errno("security.evm has not signature"); | ||||||
|  | 		return err; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	return verify_hash(hash, sizeof(hash), sig + 1, err - 1, key); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int cmd_verify_evm(struct command *cmd) | ||||||
|  | { | ||||||
|  | 	char *key, *file = g_argv[optind++]; | ||||||
|  | 	 | ||||||
|  | 	if (!file) { | ||||||
|  | 		log_err("Parameters missing\n"); | ||||||
|  | 		print_usage(cmd); | ||||||
|  | 		return 1; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	key = g_argv[optind++]; | ||||||
|  | 	if (!key) | ||||||
|  | 		key = "/etc/keys/pubkey_evm.pem"; | ||||||
|  | 	 | ||||||
|  | 	return verify_evm(file, key); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int cmd_convert(struct command *cmd) | ||||||
|  | { | ||||||
|  | 	char *inkey, *outkey = NULL; | ||||||
|  | 	unsigned char pub[1024]; | ||||||
|  | 	char name[20]; | ||||||
|  | 	int len; | ||||||
|  | 	uint8_t keyid[8]; | ||||||
|  | 	 | ||||||
|  | 	inkey = g_argv[optind++]; | ||||||
|  | 	if (!inkey) | ||||||
|  | 		inkey = "/etc/keys/pubkey_evm.pem"; | ||||||
|  | 	else | ||||||
|  | 		outkey = g_argv[optind++]; | ||||||
|  | 		 | ||||||
|  | 	if (!outkey) | ||||||
|  | 		outkey = "pubkey_evm.bin"; | ||||||
|  |  | ||||||
|  | 	log_info("Convert public key %s to %s\n", inkey, outkey);	 | ||||||
|  |  | ||||||
|  | 	len = read_key(inkey, pub); | ||||||
|  | 	if (len < 0) | ||||||
|  | 		return -1; | ||||||
|  | 	 | ||||||
|  | 	calc_keyid(keyid, name, pub, len); | ||||||
|  | 	 | ||||||
|  | 	bin2file(outkey, name, pub, len); | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int cmd_import_bin(struct command *cmd) | ||||||
|  | { | ||||||
|  | 	int len; | ||||||
|  | 	char *inkey, *ring = NULL; | ||||||
|  | 	char *key, name[20]; | ||||||
|  | 	key_serial_t id; | ||||||
|  | 	uint8_t keyid[8]; | ||||||
|  | 	 | ||||||
|  | 	inkey = g_argv[optind++]; | ||||||
|  | 	if (!inkey) | ||||||
|  | 		inkey = "/etc/keys/pubkey_evm.bin"; | ||||||
|  |  	else | ||||||
|  |  		ring = g_argv[optind++]; | ||||||
|  |  		 | ||||||
|  |  	if (!ring) | ||||||
|  |  		id = KEY_SPEC_USER_KEYRING; | ||||||
|  | 	else | ||||||
|  | 		id = atoi(ring); | ||||||
|  |  | ||||||
|  | 	key = file2bin(inkey, &len); | ||||||
|  | 	if (!key) | ||||||
|  | 		return -1; | ||||||
|  | 	 | ||||||
|  | 	calc_keyid(keyid, name, (unsigned char *)key, len); | ||||||
|  |  | ||||||
|  |  	log_info("Importing public key %s from file %s into keyring %d\n", name, inkey, id); | ||||||
|  | 	 | ||||||
|  | 	id = add_key("user", name, key, len, id); | ||||||
|  | 	if (id < 0) { | ||||||
|  | 		log_errno("add_key failed"); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	log_info("keyid: %d\n", id); | ||||||
|  | 	printf("%d\n", id); | ||||||
|  | 	 | ||||||
|  | 	free(key); | ||||||
|  | 	 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int cmd_import(struct command *cmd) | ||||||
|  | { | ||||||
|  | 	char *inkey, *ring = NULL; | ||||||
|  | 	unsigned char key[1024]; | ||||||
|  | 	int id, len; | ||||||
|  | 	char name[20]; | ||||||
|  | 	uint8_t keyid[8]; | ||||||
|  |  | ||||||
|  | 	if (binkey) | ||||||
|  | 		return cmd_import_bin(cmd); | ||||||
|  | 	 | ||||||
|  | 	inkey = g_argv[optind++]; | ||||||
|  | 	if (!inkey) | ||||||
|  | 		inkey = "/etc/keys/pubkey_evm.pem"; | ||||||
|  |  	else | ||||||
|  |  		ring = g_argv[optind++]; | ||||||
|  |  		 | ||||||
|  |  	if (!ring) | ||||||
|  |  		id = KEY_SPEC_USER_KEYRING; | ||||||
|  | 	else | ||||||
|  | 		id = atoi(ring); | ||||||
|  |  | ||||||
|  | 	len = read_key(inkey, key); | ||||||
|  | 	if (len < 0) | ||||||
|  | 		return -1; | ||||||
|  | 	 | ||||||
|  | 	calc_keyid(keyid, name, key, len); | ||||||
|  | 	 | ||||||
|  |  	log_info("Importing public key %s from file %s into keyring %d\n", name, inkey, id); | ||||||
|  | 	 | ||||||
|  | 	id = add_key("user", name, key, len, id); | ||||||
|  | 	if (id < 0) { | ||||||
|  | 		log_errno("add_key failed"); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	log_info("keyid: %d\n", id); | ||||||
|  | 	printf("%d\n", id); | ||||||
|  | 	 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void print_usage(struct command *cmd) | ||||||
|  | { | ||||||
|  | 	printf("usage: %s %s\n", cmd->name, cmd->arg ? cmd->arg : ""); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void print_full_usage(struct command *cmd) | ||||||
|  | { | ||||||
|  | 	if (cmd->name) | ||||||
|  | 		printf("usage: %s %s\n", cmd->name, cmd->arg ? cmd->arg : ""); | ||||||
|  | 	if (cmd->msg) | ||||||
|  | 		printf("description:\n%s", cmd->msg); | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int print_command_usage(struct command *cmds, char *command) | ||||||
|  | { | ||||||
|  | 	struct command	*cmd; | ||||||
|  | 	struct command	nullcmd = {NULL, NULL, 0, NULL, NULL}; | ||||||
|  |  | ||||||
|  | 	for (cmd = cmds; memcmp(cmd, &nullcmd, sizeof(nullcmd)) != 0; cmd++) { | ||||||
|  | 		if (cmd->name) | ||||||
|  | 			if (strcmp(cmd->name, command) == 0) { | ||||||
|  | 				print_full_usage(cmd); | ||||||
|  | 				return 0; | ||||||
|  | 			} | ||||||
|  | 	} | ||||||
|  | 	printf("invalid command: %s\n", command); | ||||||
|  | 	return 	1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void print_all_usage(struct command *cmds) | ||||||
|  | { | ||||||
|  | 	struct command	*cmd; | ||||||
|  | 	struct command	nullcmd = {NULL, NULL, 0, NULL, NULL}; | ||||||
|  | 	 | ||||||
|  | 	for (cmd = cmds; memcmp(cmd, &nullcmd, sizeof(nullcmd)) != 0; cmd++) { | ||||||
|  | 		if (cmd->name && cmd->arg) | ||||||
|  | 			printf("%s %s\n", cmd->name, cmd->arg); | ||||||
|  | 		else if (cmd->msg) | ||||||
|  | 			printf("%s", cmd->msg); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int call_command(struct command *cmds, char *command) | ||||||
|  | { | ||||||
|  | 	struct command	*cmd; | ||||||
|  | 	struct command	nullcmd = {NULL, NULL, 0, NULL, NULL}; | ||||||
|  | 	 | ||||||
|  | 	for (cmd = cmds; memcmp(cmd, &nullcmd, sizeof(nullcmd)) != 0; cmd++) { | ||||||
|  | 		if (cmd->name && strcasecmp(cmd->name, command) == 0) { | ||||||
|  | 			return cmd->func(cmd); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	printf("Invalid command: %s\n", command); | ||||||
|  | 	return -1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int cmd_help(struct command *cmd) | ||||||
|  | { | ||||||
|  | 	if (!g_argv[optind]) { | ||||||
|  | 		print_usage(cmd); | ||||||
|  | 		return 0; | ||||||
|  | 	} else | ||||||
|  | 		return print_command_usage(cmds, g_argv[optind]); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void usage(void) | ||||||
|  | { | ||||||
|  | 	printf("Usage: evmctl <command> [parameters..]\n"); | ||||||
|  |  | ||||||
|  | 	print_all_usage(cmds); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct command cmds[] = { | ||||||
|  | 	{"help", cmd_help, 0, "<command>"}, | ||||||
|  | 	{"import", cmd_import, 0, "[--bin] inkey keyring", "Import public key (PEM/bin) into the keyring.\n" }, | ||||||
|  | 	{"convert", cmd_convert, 0, "inkey outkey", "Convert PEM public key into IMA/EVM kernel friendly format.\n" }, | ||||||
|  | 	{"sign", cmd_sign_evm, 0, "[--imahash | --imasig ] file [key]", "Sign file metadata.\n" }, | ||||||
|  | 	{"verify", cmd_verify_evm, 0, "file", "Verify EVM.\n" }, | ||||||
|  | 	{"ima_sign", cmd_sign_ima, 0, "file [key]", "Sign file content.\n" }, | ||||||
|  | 	{"ima_hash", cmd_hash_ima, 0, "file", "Hash file content.\n" }, | ||||||
|  | 	{0, 0, 0, NULL} | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static struct option  opts[] = { | ||||||
|  | 	{"help", 0, 0, 'h'}, | ||||||
|  | 	{"inkey", 1, 0, 'k'}, | ||||||
|  | 	{"imasig", 0, 0, 's'}, | ||||||
|  | 	{"imahash", 0, 0, 'd'}, | ||||||
|  | 	{"hashalgo", 1, 0, 'a'}, | ||||||
|  | 	{"bin", 0, 0, 'b'}, | ||||||
|  | 	{} | ||||||
|  |  | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | int main(int argc, char *argv[]) | ||||||
|  | { | ||||||
|  | 	int err = 0, c, lind; | ||||||
|  |  | ||||||
|  | 	g_argv = argv; | ||||||
|  | 	g_argc = argc; | ||||||
|  |  | ||||||
|  | 	while (1) { | ||||||
|  | 		c = getopt_long(argc, argv, "hk:vnsda:b", opts, &lind); | ||||||
|  | 		if (c == -1) | ||||||
|  | 			break; | ||||||
|  | 			 | ||||||
|  | 		switch (c) { | ||||||
|  | 		case 'h': | ||||||
|  | 			usage(); | ||||||
|  | 			exit(0); | ||||||
|  | 			break; | ||||||
|  | 		case 'k': | ||||||
|  | 			printf("inkey: %s\n", optarg); | ||||||
|  | 			break; | ||||||
|  | 		case 'v': | ||||||
|  | 			verbose++; | ||||||
|  | 			break; | ||||||
|  | 		case 'd': | ||||||
|  | 			digest = 1; | ||||||
|  | 			break; | ||||||
|  | 		case 's': | ||||||
|  | 			digsig = 1; | ||||||
|  | 			break; | ||||||
|  | 		case 'n': | ||||||
|  | 			set_xattr = 0; // do not set Extended Attributes... just print signature | ||||||
|  | 			break; | ||||||
|  | 		case 'a': | ||||||
|  | 			hash_algo = optarg; | ||||||
|  | 			break; | ||||||
|  | 		case 'b': | ||||||
|  | 			binkey = 1; | ||||||
|  | 			break; | ||||||
|  | 		case '?': | ||||||
|  | 			exit(1); | ||||||
|  | 			break; | ||||||
|  | 		default: | ||||||
|  | 			log_err("getopt() returned: %d (%c)\n", c, c); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (argv[optind] == NULL) | ||||||
|  | 		usage(); | ||||||
|  | 	else | ||||||
|  | 		err = call_command(cmds, argv[optind++]); | ||||||
|  |  | ||||||
|  | 	return err; | ||||||
|  | } | ||||||
							
								
								
									
										6
									
								
								tests/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								tests/Makefile.am
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | |||||||
|  | pkglib_PROGRAMS = openclose | ||||||
|  |  | ||||||
|  | openclose_SOURCES = openclose.c | ||||||
|  |  | ||||||
|  | dist_pkglib_SCRIPTS = evm_enable.sh evm_genkey.sh evm_label_all.sh sign_modules_dir.sh ima_fix_dir.sh | ||||||
|  |  | ||||||
							
								
								
									
										25
									
								
								tests/evm_enable.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										25
									
								
								tests/evm_enable.sh
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,25 @@ | |||||||
|  | #!/bin/sh | ||||||
|  |  | ||||||
|  | # import EVM HMAC key | ||||||
|  | keyctl clear @u | ||||||
|  | keyctl add user kmk "testing123" @u | ||||||
|  | keyctl add encrypted evm-key "load `cat /etc/keys/evm-key`" @u | ||||||
|  |  | ||||||
|  | # import Moule public key | ||||||
|  | mod_id=`keyctl newring _module @u` | ||||||
|  | evmctl import /etc/keys/pubkey_evm.pem $mod_id | ||||||
|  |  | ||||||
|  | # import IMA public key | ||||||
|  | ima_id=`keyctl newring _ima @u` | ||||||
|  | evmctl import /etc/keys/pubkey_evm.pem $ima_id | ||||||
|  |  | ||||||
|  | # import EVM public key | ||||||
|  | evm_id=`keyctl newring _evm @u` | ||||||
|  | evmctl import /etc/keys/pubkey_evm.pem $evm_id | ||||||
|  |  | ||||||
|  | # enable EVM | ||||||
|  | echo "1" > /sys/kernel/security/evm | ||||||
|  |  | ||||||
|  | # enable module checking | ||||||
|  | echo "1" > /sys/kernel/security/ima/module_check | ||||||
|  |  | ||||||
							
								
								
									
										8
									
								
								tests/evm_genkey.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										8
									
								
								tests/evm_genkey.sh
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,8 @@ | |||||||
|  | #!/bin/sh | ||||||
|  |  | ||||||
|  | keyctl add user kmk "testing123" @u | ||||||
|  | key=`keyctl add encrypted evm-key "new user:kmk 32" @u` | ||||||
|  | keyctl print $key >/etc/keys/evm-key | ||||||
|  |  | ||||||
|  | keyctl list @u | ||||||
|  |  | ||||||
							
								
								
									
										17
									
								
								tests/evm_label_all.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										17
									
								
								tests/evm_label_all.sh
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,17 @@ | |||||||
|  | #!/bin/sh | ||||||
|  |  | ||||||
|  | verbose="" | ||||||
|  | if [ "$1" = "-v" ] ; then | ||||||
|  | 	verbose="-v" | ||||||
|  | 	shift 1 | ||||||
|  | fi | ||||||
|  |  | ||||||
|  | dir=${1:-/} | ||||||
|  |  | ||||||
|  | echo "Label: $dir" | ||||||
|  |  | ||||||
|  | find $dir \( -fstype rootfs -o -fstype ext3 -o -fstype ext4 \) ! -path "/lib/modules/*" -type f -uid 0 -exec evmctl sign --imahash $verbose '{}' \; | ||||||
|  | find /lib/modules ! -name "*.ko" -type f -uid 0 -exec evmctl sign --imahash $verbose '{}' \; | ||||||
|  | # security.ima needs to have signature for modules | ||||||
|  | find /lib/modules -name "*.ko" -type f -uid 0 -exec evmctl sign --imasig $verbose '{}' \; | ||||||
|  |  | ||||||
							
								
								
									
										8
									
								
								tests/ima_fix_dir.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										8
									
								
								tests/ima_fix_dir.sh
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,8 @@ | |||||||
|  | #!/bin/sh | ||||||
|  |  | ||||||
|  | dir=${1:-/} | ||||||
|  |  | ||||||
|  | echo "Fixing dir: $dir" | ||||||
|  |  | ||||||
|  | find $dir \( -fstype rootfs -o -fstype ext3 -o -fstype ext4 \) -type f -uid 0 -exec openclose '{}' \; | ||||||
|  |  | ||||||
							
								
								
									
										20
									
								
								tests/openclose.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								tests/openclose.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | |||||||
|  | #include <unistd.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <fcntl.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  |  | ||||||
|  | int main(int argc, char *argv[]) | ||||||
|  | { | ||||||
|  | 	int fd; | ||||||
|  |  | ||||||
|  | 	fd = open(argv[1], O_RDONLY); | ||||||
|  | 	if (fd < 0) { | ||||||
|  | 		perror("open()"); | ||||||
|  | 		exit(1); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	close(fd); | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										15
									
								
								tests/sign_modules_dir.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										15
									
								
								tests/sign_modules_dir.sh
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,15 @@ | |||||||
|  | #!/bin/sh | ||||||
|  |  | ||||||
|  | verbose="" | ||||||
|  | if [ "$1" = "-v" ] ; then | ||||||
|  | 	verbose="-v" | ||||||
|  | 	shift 1 | ||||||
|  | fi | ||||||
|  |  | ||||||
|  | dir=${1:-/lib/modules} | ||||||
|  |  | ||||||
|  | echo "Signing modules: $dir" | ||||||
|  |  | ||||||
|  | find $dir -name "*.ko" -type f -uid 0 -exec evmctl sign --imasig '{}' \; | ||||||
|  | find $dir ! -name "*.ko" -type f -uid 0 -exec evmctl sign --imahash '{}' \; | ||||||
|  |  | ||||||
		Reference in New Issue
	
	Block a user
	 Dmitry Kasatkin
					Dmitry Kasatkin