This list is closed, nobody may subscribe to it.
2007 |
Jan
|
Feb
(1) |
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
(1) |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2009 |
Jan
|
Feb
|
Mar
|
Apr
(1) |
May
(1) |
Jun
(2) |
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
2011 |
Jan
|
Feb
|
Mar
(1) |
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
2013 |
Jan
|
Feb
|
Mar
(7) |
Apr
|
May
(7) |
Jun
(7) |
Jul
(26) |
Aug
|
Sep
(7) |
Oct
(1) |
Nov
(35) |
Dec
(18) |
2014 |
Jan
(1) |
Feb
(2) |
Mar
(3) |
Apr
|
May
(16) |
Jun
(35) |
Jul
(103) |
Aug
(45) |
Sep
(226) |
Oct
(200) |
Nov
(66) |
Dec
(42) |
2015 |
Jan
(47) |
Feb
(3) |
Mar
(6) |
Apr
(14) |
May
(38) |
Jun
(10) |
Jul
(10) |
Aug
(15) |
Sep
(23) |
Oct
(78) |
Nov
(56) |
Dec
(70) |
2016 |
Jan
(9) |
Feb
(8) |
Mar
(15) |
Apr
(18) |
May
(78) |
Jun
(39) |
Jul
(3) |
Aug
(136) |
Sep
(134) |
Oct
(19) |
Nov
(48) |
Dec
(30) |
2017 |
Jan
(33) |
Feb
(35) |
Mar
(100) |
Apr
(87) |
May
(169) |
Jun
(119) |
Jul
(165) |
Aug
(241) |
Sep
(128) |
Oct
(42) |
Nov
|
Dec
|
From: Roberto S. <rob...@po...> - 2013-11-07 15:51:30
|
Hi everyone one functionality that is missing in the new template management mechanism is the possibility to specify a custom template format per-measurement entry. Until now, once the template is determined from the kernel configuration or from the kernel command line parameters 'ima_template' and ima_template_fmt', this template is used to generate all measurements entries in the list. However, it is desirable to differentiate information included in measurement entries depending on the event type from which they are generated. To explain the usefulness of the proposed modification, I'll make an example. One feature coming soon is the addition of new template fields that represent LSM labels applied either to the current process and to the inode being measured. However, these information are not enough to capture the mapping between the executable code loaded for a given subject. Indeed, for example in SELinux, a type transition may occur if the type of the current process and the type of the inode being executed are matched in a 'type_transition' policy rule. In this case, the code should be mapped not to the label of the current process but instead to the label in the credentials (stored in the 'linux_binprm' structure) being installed during the execution of the execve() system call. To correctly perform the mapping code - LSM label, it is needed to introduce a new template field to represent the LSM label in the 'linux_binprm' structure (e.g. with identifier 'bprm-label') and a new IMA policy action (e.g. measure_log_all) to record a measurement for every event that match rule criteria, although the accessed inode has already been measured. Then, assuming that the format of the default template is "d-ng|n-ng|subj|obj" (digest + hash algo, long event name, subject LSM label, object LSM label), the policy to capture the mapping should be: --- measure_log_all func=BPRM_CHECK mask=MAY_EXEC \ ima_template_fmt=d-ng|n-ng|subj|obj|lsm-label measure_log_all func=FILE_MMAP mask=MAY_EXEC --- In the first rule, the default template is overridden with a template that contains the label stored in the 'linux_binprm' structure. Thus, in the resulting measurements list, all entries that record the execution of binaries will include the additional template field, while those generated from the mapping into memory of shared libraries will contain only fields listed in the default template. Roberto Sassu Roberto Sassu (6): ima: connect defined IMA templates through a linked list ima: added new template helper lookup_template_desc_by_fmt() ima: added ima_get_template_desc() for templates dynamic registration ima: replace ima_template_desc_current() with ima_get_template_desc() ima: added ima_template and ima_template_fmt new policy options ima: use custom template obtained from a matched policy rule Documentation/ABI/testing/ima_policy | 6 ++- Documentation/security/IMA-templates.txt | 19 +++++---- security/integrity/ima/ima.h | 14 ++++--- security/integrity/ima/ima_api.c | 22 +++++++---- security/integrity/ima/ima_init.c | 2 +- security/integrity/ima/ima_main.c | 13 +++++-- security/integrity/ima/ima_policy.c | 52 ++++++++++++++++++++++++- security/integrity/ima/ima_template.c | 67 +++++++++++++++++++++++++++++--- 8 files changed, 161 insertions(+), 34 deletions(-) -- 1.8.1.4 |
From: Roberto S. <rob...@po...> - 2013-11-07 14:03:13
|
This patch allows users to provide a custom template format through the new kernel command line parameter 'ima_template_fmt'. If the supplied format is not valid, IMA uses the default template descriptor. Signed-off-by: Roberto Sassu <rob...@po...> --- Documentation/kernel-parameters.txt | 4 +++ Documentation/security/IMA-templates.txt | 29 +++++++++--------- security/integrity/ima/ima_template.c | 50 ++++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 15 deletions(-) diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 1e8761c..27b14b2 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1199,6 +1199,10 @@ bytes respectively. Such letter suffixes can also be entirely omitted. Formats: { "ima" | "ima-ng" } Default: "ima-ng" + ima_template_fmt= + [IMA] Define a custom template format. + Format: { "field1|...|fieldN" } + init= [KNL] Format: <full_path> Run specified binary instead of /sbin/init as init diff --git a/Documentation/security/IMA-templates.txt b/Documentation/security/IMA-templates.txt index a777e5f..08ea2da 100644 --- a/Documentation/security/IMA-templates.txt +++ b/Documentation/security/IMA-templates.txt @@ -27,25 +27,22 @@ Managing templates with these structures is very simple. To support a new data type, developers define the field identifier and implement two functions, init() and show(), respectively to generate and display measurement entries. Defining a new template descriptor requires -specifying the template format, a string of field identifiers separated -by the '|' character. While in the current implementation it is possible -to define new template descriptors only by adding their definition in the -template specific code (ima_template.c), in a future version it will be -possible to register a new template on a running kernel by supplying to IMA -the desired format string. In this version, IMA initializes at boot time -all defined template descriptors by translating the format into an array -of template fields structures taken from the set of the supported ones. +specifying the template format (a string of field identifiers separated +by the '|' character) through the 'ima_template_fmt' kernel command line +parameter. At boot time, IMA initializes all defined template descriptors +by translating the format into an array of template fields structures taken +from the set of the supported ones. After the initialization step, IMA will call ima_alloc_init_template() (new function defined within the patches for the new template management mechanism) to generate a new measurement entry by using the template descriptor chosen through the kernel configuration or through the newly -introduced 'ima_template=' kernel command line parameter. It is during this -phase that the advantages of the new architecture are clearly shown: -the latter function will not contain specific code to handle a given template -but, instead, it simply calls the init() method of the template fields -associated to the chosen template descriptor and store the result (pointer -to allocated data and data length) in the measurement entry structure. +introduced 'ima_template' and 'ima_template_fmt' kernel command line parameters. +It is during this phase that the advantages of the new architecture are +clearly shown: the latter function will not contain specific code to handle +a given template but, instead, it simply calls the init() method of the template +fields associated to the chosen template descriptor and store the result +(pointer to allocated data and data length) in the measurement entry structure. The same mechanism is employed to display measurements entries. The functions ima[_ascii]_measurements_show() retrieve, for each entry, @@ -84,4 +81,6 @@ currently the following methods are supported: - select a template descriptor among those supported in the kernel configuration ('ima-ng' is the default choice); - specify a template descriptor name from the kernel command line through - the 'ima_template=' parameter. + the 'ima_template=' parameter; + - register a new template descriptor with custom format through the kernel + command line parameter 'ima_template_fmt='. diff --git a/security/integrity/ima/ima_template.c b/security/integrity/ima/ima_template.c index bb33576..5a95d06 100644 --- a/security/integrity/ima/ima_template.c +++ b/security/integrity/ima/ima_template.c @@ -21,6 +21,7 @@ static struct ima_template_desc defined_templates[] = { {.name = IMA_TEMPLATE_IMA_NAME, .fmt = IMA_TEMPLATE_IMA_FMT}, {.name = "ima-ng",.fmt = "d-ng|n-ng"}, {.name = "ima-sig",.fmt = "d-ng|n-ng|sig"}, + {.name = "",.fmt = ""}, /* placeholder for a custom format */ }; static struct ima_template_field supported_fields[] = { @@ -38,12 +39,16 @@ static struct ima_template_field supported_fields[] = { static struct ima_template_desc *ima_template; static struct ima_template_desc *lookup_template_desc(const char *name); +static struct ima_template_field *lookup_template_field(const char *field_id); static int __init ima_template_setup(char *str) { struct ima_template_desc *template_desc; int template_len = strlen(str); + if (ima_template) + return 1; + /* * Verify that a template with the supplied name exists. * If not, use CONFIG_IMA_DEFAULT_TEMPLATE. @@ -70,6 +75,48 @@ static int __init ima_template_setup(char *str) } __setup("ima_template=", ima_template_setup); +static int __init ima_template_fmt_setup(char *str) +{ + int num_templates = ARRAY_SIZE(defined_templates); + char *str_ptr_start = str; + char *str_ptr_end = str_ptr_start; + + if (ima_template) + return 1; + + while (str_ptr_start != NULL) { + char field_id[IMA_TEMPLATE_FIELD_ID_MAX_LEN]; + int len; + + str_ptr_end = strpbrk(str_ptr_start, "|"); + if (str_ptr_end == NULL) + len = str + strlen(str) - str_ptr_start; + else + len = str_ptr_end++ - str_ptr_start; + + if (len >= IMA_TEMPLATE_FIELD_ID_MAX_LEN) { + pr_err("IMA: field too long, using template %s\n", + CONFIG_IMA_DEFAULT_TEMPLATE); + return 1; + } + + memcpy(field_id, str_ptr_start, len); + field_id[len] = '\0'; + if (lookup_template_field(field_id) == NULL) { + pr_err("IMA: field '%s' not found, using template %s\n", + field_id, CONFIG_IMA_DEFAULT_TEMPLATE); + return 1; + } + + str_ptr_start = str_ptr_end; + } + + defined_templates[num_templates - 1].fmt = str; + ima_template = defined_templates + num_templates - 1; + return 1; +} +__setup("ima_template_fmt=", ima_template_fmt_setup); + static struct ima_template_desc *lookup_template_desc(const char *name) { int i; @@ -160,6 +207,9 @@ static int init_defined_templates(void) for (i = 0; i < ARRAY_SIZE(defined_templates); i++) { struct ima_template_desc *template = &defined_templates[i]; + if (strlen(template->fmt) == 0) + continue; + result = template_desc_init_fields(template->fmt, &(template->fields), &(template->num_fields)); -- 1.8.1.4 |
From: Roberto S. <rob...@po...> - 2013-11-07 14:03:04
|
With the introduction of the 'ima_template_fmt' kernel cmdline parameter, an user can define a new template descriptor with custom format. However, in this case, userspace tools will be unable to parse the measurements list because the new template is unknown. For this reason, this patch modifies the current IMA behavior to display in the list the template format instead of the name so that a tool can extract needed information if it can handle listed fields. Signed-off-by: Roberto Sassu <rob...@po...> --- security/integrity/ima/ima_fs.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index d47a7c8..6db74ff 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -118,6 +118,7 @@ static int ima_measurements_show(struct seq_file *m, void *v) /* the list never shrinks, so we don't need a lock here */ struct ima_queue_entry *qe = v; struct ima_template_entry *e; + char *template_name; int namelen; u32 pcr = CONFIG_IMA_MEASURE_PCR_IDX; int i; @@ -127,6 +128,10 @@ static int ima_measurements_show(struct seq_file *m, void *v) if (e == NULL) return -1; + template_name = e->template_desc->name; + if (strlen(e->template_desc->name) == 0) + template_name = e->template_desc->fmt; + /* * 1st: PCRIndex * PCR used is always the same (config option) in @@ -138,14 +143,14 @@ static int ima_measurements_show(struct seq_file *m, void *v) ima_putc(m, e->digest, TPM_DIGEST_SIZE); /* 3rd: template name size */ - namelen = strlen(e->template_desc->name); + namelen = strlen(template_name); ima_putc(m, &namelen, sizeof namelen); /* 4th: template name */ - ima_putc(m, e->template_desc->name, namelen); + ima_putc(m, template_name, namelen); /* 5th: template length (except for 'ima' template) */ - if (strcmp(e->template_desc->name, IMA_TEMPLATE_IMA_NAME) != 0) + if (strcmp(template_name, IMA_TEMPLATE_IMA_NAME) != 0) ima_putc(m, &e->template_data_len, sizeof(e->template_data_len)); @@ -190,6 +195,7 @@ static int ima_ascii_measurements_show(struct seq_file *m, void *v) /* the list never shrinks, so we don't need a lock here */ struct ima_queue_entry *qe = v; struct ima_template_entry *e; + char *template_name; int i; /* get entry */ @@ -197,6 +203,10 @@ static int ima_ascii_measurements_show(struct seq_file *m, void *v) if (e == NULL) return -1; + template_name = e->template_desc->name; + if (strlen(e->template_desc->name) == 0) + template_name = e->template_desc->fmt; + /* 1st: PCR used (config option) */ seq_printf(m, "%2d ", CONFIG_IMA_MEASURE_PCR_IDX); @@ -204,7 +214,7 @@ static int ima_ascii_measurements_show(struct seq_file *m, void *v) ima_print_digest(m, e->digest, TPM_DIGEST_SIZE); /* 3th: template name */ - seq_printf(m, " %s", e->template_desc->name); + seq_printf(m, " %s", template_name); /* 4th: template specific data */ for (i = 0; i < e->template_desc->num_fields; i++) { -- 1.8.1.4 |
From: Roberto S. <rob...@po...> - 2013-11-07 14:02:59
|
This patch makes a copy of the 'template_fmt' function argument so that the latter will not be modified by strsep(), which does the splitting by replacing the given separator with '\0'. Signed-off-by: Roberto Sassu <rob...@po...> --- security/integrity/ima/ima_template.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/security/integrity/ima/ima_template.c b/security/integrity/ima/ima_template.c index 7bcff5c..bb33576 100644 --- a/security/integrity/ima/ima_template.c +++ b/security/integrity/ima/ima_template.c @@ -113,13 +113,19 @@ static int template_desc_init_fields(char *template_fmt, struct ima_template_field ***fields, int *num_fields) { - char *c, *template_fmt_ptr = template_fmt; + char *c, *template_fmt_ptr, *template_fmt_copy = NULL; int template_num_fields = template_fmt_size(template_fmt); int i, result = 0; if (template_num_fields > IMA_TEMPLATE_NUM_FIELDS_MAX) return -EINVAL; + /* copying is needed as strsep() modifies the original buffer */ + template_fmt_copy = kstrdup(template_fmt, GFP_KERNEL); + if (template_fmt_copy == NULL) + return -ENOMEM; + + template_fmt_ptr = template_fmt_copy; *fields = kzalloc(template_num_fields * sizeof(*fields), GFP_KERNEL); if (*fields == NULL) { result = -ENOMEM; @@ -139,6 +145,7 @@ static int template_desc_init_fields(char *template_fmt, *num_fields = i; return 0; out: + kfree(template_fmt_copy); kfree(*fields); *fields = NULL; return result; -- 1.8.1.4 |
From: Roberto S. <rob...@po...> - 2013-11-07 14:02:54
|
This patch adds some error messages to inform users about the following events: template descriptor not found, template field not found, and template initialization failed. Signed-off-by: Roberto Sassu <rob...@po...> --- security/integrity/ima/ima_template.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/security/integrity/ima/ima_template.c b/security/integrity/ima/ima_template.c index 4e5da99..7bcff5c 100644 --- a/security/integrity/ima/ima_template.c +++ b/security/integrity/ima/ima_template.c @@ -49,8 +49,11 @@ static int __init ima_template_setup(char *str) * If not, use CONFIG_IMA_DEFAULT_TEMPLATE. */ template_desc = lookup_template_desc(str); - if (!template_desc) + if (!template_desc) { + pr_err("IMA: template %s not found, using %s\n", + str, CONFIG_IMA_DEFAULT_TEMPLATE); return 1; + } /* * Verify whether the current hash algorithm is supported @@ -127,6 +130,7 @@ static int template_desc_init_fields(char *template_fmt, struct ima_template_field *f = lookup_template_field(c); if (!f) { + pr_err("IMA: field '%s' not found\n", c); result = -ENOENT; goto out; } @@ -152,8 +156,12 @@ static int init_defined_templates(void) result = template_desc_init_fields(template->fmt, &(template->fields), &(template->num_fields)); - if (result < 0) + if (result < 0) { + pr_err("IMA: template %s init failed, result: %d\n", + (strlen(template->name) ? + template->name : template->fmt), result); return result; + } } return result; } -- 1.8.1.4 |
From: Roberto S. <rob...@po...> - 2013-11-07 14:02:48
|
Hi everyone currently accepted patches for the new template management mechanism allow to choose among a list of supported templates, statically defined in the code. This functionality is not flexible enough as users may want to include in their measurements list only information needed and not use predefined combinations. For this reason, this patch set introduce the new kernel command line parameter 'ima_template_fmt' to specify a custom template format at boot time, i.e. a string of template fields identifiers concatenated with the '|' separator character. The complete list of defined template fields can be found in Documentation/security/IMA-templates.txt. The format string is checked at the very beginning in the setup function ima_template_fmt_setup() so that, if it is wrong, IMA can go back to the default template, selected through a kernel configuration option. To allow userspace tools parse a measurements list with a custom format, IMA provides as template name the same format string provided by users at boot time, so that tools know which information are included in a entry and extract them if they can handle listed template fields. Roberto Sassu Roberto Sassu (4): ima: added error messages to template-related functions ima: make a copy of template_fmt in template_desc_init_fields() ima: display template format in meas. list if template name length is zero ima: added support for new kernel cmdline parameter ima_template_fmt Documentation/kernel-parameters.txt | 4 ++ Documentation/security/IMA-templates.txt | 29 +++++++------ security/integrity/ima/ima_fs.c | 18 ++++++-- security/integrity/ima/ima_template.c | 71 ++++++++++++++++++++++++++++++-- 4 files changed, 100 insertions(+), 22 deletions(-) -- 1.8.1.4 |
From: Mimi Z. <zo...@li...> - 2013-10-31 13:02:27
|
Hi - We need to discuss what should be upstreamed in the 3.14 open window and start preparing/posting patches now. James is asking that the patches be ready by -rc4. Please base the changes on the next-trusted-keys branch. thanks, Mimi |
From: Dmitry K. <d.k...@sa...> - 2013-09-10 16:11:33
|
On 06/09/13 22:38, Vivek Goyal wrote: > Hi, > > This is an RFC patch series to get early feedback on stuff I am working > on. > > This series does few things. > > - Adds an extra structure to ima signature (security.ima) which will signal > the elf loader that this executable needs to be locked. This will be > useful for secureboot where signed /sbin/kexec needs to run memory > locked. > > I have posted RFC kernel patches on Fedora kernel mailing list. > > https://lists.fedoraproject.org/pipermail/kernel/2013-September/004432.html > > kexec-tools patches are posted here. > > https://lists.fedoraproject.org/pipermail/kernel/2013-September/004469.html > > - Add a functionality to import signatures signed externally. (Patch 2) > - Add functionality to allow signing using external crypto card. (Patch 3) > - Add a functionality to create a daemon which cilents can connect to > and request file signing (Patch 4 and Patch 5). > > All the signing enhancements I need so that various build servers can > make use of it to sign /sbin/kexec and bzImage using appropriate keys. > > This is still a work in progress and code is very raw. I wanted to get > the code out to get early feedback. > > Thanks > Vivek > > Vivek Goyal (5): > evmctl: Allow adding a memlock information in security.ima > evmctl: Allow importing external signature > evmctl: Allow signing using external crypto engine > evmctl-allow-launching-daemon > evmctl-client: A simple client to request signing from evmctl daemon > > configure.ac | 1 + > src/Makefile.am | 9 +- > src/client.c | 697 +++++++++++++++++++++++++++++++++ > src/daemon.h | 83 ++++ > src/evmctl.c | 1166 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- > 5 files changed, 1934 insertions(+), 22 deletions(-) > create mode 100644 src/client.c > create mode 100644 src/daemon.h > Hi Vivek, I am looking into patches.. It would be great if you could share your tree somewhere so that it would simplify pulling your code. - Dmitry |
From: Vivek G. <vg...@re...> - 2013-09-06 19:38:36
|
evmctl uses openssl API for signing a file. One might want to use a external crypto engine for signing. For example, I am trying to use a smartcard for signing. Openssl provides engine API to deal with external crypto. This patch adds support where one can specify external engine to load and then sign a file using that. For example, I am doing this on my machine. #Sign a file evmctl ima_sign -e pkcs11 -x -v --key slot_1-id_bb82253c8ab1337a74b95a6c68da4a658859c71a /tmp/data.txt -p <enter-pin> --engine_so /usr/lib64/openssl/engines/engine_pkcs11.so --engine_module opensc-pkcs11.so Or evmctl ima_sign -v -x --key id_bb82253c8ab1337a74b95a6c68da4a658859c71a /tmp/data.txt -p <enter-pin> -e pkcs11 --engine_so /usr/lib64/openssl/engines/engine_pkcs11.so --engine_module opensc-pkcs11.so -t "OpenSC Card (Fedora Signing CA)" --pkcs11_module opensc-pkcs11.so # Verify signature evmctl ima_verify /tmp/data.txt -k signer-x509-cert.der Signed-off-by: Vivek Goyal <vg...@re...> --- configure.ac | 1 + src/Makefile.am | 2 +- src/evmctl.c | 283 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 280 insertions(+), 6 deletions(-) diff --git a/configure.ac b/configure.ac index 5decc4f..137c88a 100644 --- a/configure.ac +++ b/configure.ac @@ -27,6 +27,7 @@ AC_HEADER_STDC PKG_CHECK_MODULES(OPENSSL, [ openssl >= 0.9.8 ]) AC_SUBST(OPENSSL_CFLAGS) AC_SUBST(OPENSSL_LIBS) +PKG_CHECK_MODULES(OPENSC,libp11) AC_CHECK_HEADER(unistd.h) AC_CHECK_HEADERS(openssl/conf.h) diff --git a/src/Makefile.am b/src/Makefile.am index 6779baf..472479f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -4,7 +4,7 @@ bin_PROGRAMS = evmctl evmctl_SOURCES = evmctl.c evmctl_CPPFLAGS = $(OPENSSL_CFLAGS) evmctl_LDFLAGS = $(LDFLAGS_READLINE) -evmctl_LDADD = $(OPENSSL_LIBS) -lkeyutils +evmctl_LDADD = $(OPENSSL_LIBS) -lkeyutils -lp11 INCLUDES = -I$(top_srcdir) -include config.h diff --git a/src/evmctl.c b/src/evmctl.c index 3679e68..2205d1e 100644 --- a/src/evmctl.c +++ b/src/evmctl.c @@ -46,6 +46,7 @@ #include <dirent.h> #include <ctype.h> #include <stdbool.h> +#include <libp11.h> #include <openssl/sha.h> #include <openssl/rsa.h> @@ -260,7 +261,7 @@ static int digest; static int digsig; static const char *hash_algo = "sha1"; static int user_hash_algo; -static char *keypass; +static char *keypass = NULL; static int sigfile; static int modsig; static char *uuid_str; @@ -268,6 +269,12 @@ static int x509; static int user_sig_type; static char *keyfile; static bool memlock = false; +static char *engine_id = NULL; +static char *engine_so = NULL; +static char *engine_module = NULL; +static ENGINE *engine = NULL; +static char *token_label = NULL; +static char *pkcs11_module = NULL; typedef int (*sign_hash_fn_t)(const char *algo, const unsigned char *hash, int size, const char *keyfile, unsigned char *sig); @@ -475,11 +482,34 @@ static void calc_keyid_v2(uint32_t *keyid, char *str, RSA *key) free(pkey); } +static RSA *read_priv_key_from_engine(ENGINE *e, const char *keyfile) +{ + RSA *key; + EVP_PKEY *evp_key = NULL; + + evp_key = ENGINE_load_private_key(e, keyfile, NULL, NULL); + if (!evp_key) { + log_err("ENGINE_load_private_key: key=%s failed\n", keyfile); + return NULL; + } + + key = EVP_PKEY_get1_RSA(evp_key); + if (!key) { + log_err("Getting RSA key from EVP_PKEY failed\n"); + return NULL; + } + + return key; +} + static RSA *read_priv_key(const char *keyfile) { FILE *fp; RSA *key; + if (engine_id != NULL) + return read_priv_key_from_engine(engine, keyfile); + fp = fopen(keyfile, "r"); if (!fp) { log_err("Unable to open keyfile %s\n", keyfile); @@ -992,6 +1022,205 @@ static int calc_hash(const char *file, uint8_t *hash) return mdlen; } +static int token_label_to_slot_id(char *tk_label) +{ + int rc = -1; + PKCS11_CTX *ctx; + PKCS11_SLOT *slots, *slot, *temp_slots; + unsigned int nslots, temp_nslots; + + if (pkcs11_module == NULL) { + log_err("No pkcs11_module specified\n"); + return -1; + } + + ctx = PKCS11_CTX_new(); + + /* load pkcs #11 module */ + rc = PKCS11_CTX_load(ctx, pkcs11_module); + if (rc) { + log_err("Loading pkcs11 engine failed: %s\n", + ERR_reason_error_string(ERR_get_error())); + rc = -1; + goto nolib; + } + + /* get information on all slots */ + rc = PKCS11_enumerate_slots(ctx, &slots, &nslots); + if (rc < 0) { + log_err("No slots available\n"); + rc = -1; + goto noslots; + } + + /* search for slot with given label */ + temp_slots = slots; + temp_nslots = nslots; + + while (1) { + /* get first slot with a token */ + slot = PKCS11_find_token(ctx, temp_slots, temp_nslots); + if (!slot || !slot->token) { + log_err("No token available with given label:%s\n", + token_label); + rc = -1; + goto notoken; + } + + if (!strcmp(slot->token->label, tk_label)) { + rc = PKCS11_get_slotid_from_slot(slot); + break; + } + + temp_slots = slot + 1; + temp_nslots -= temp_slots - slots; + } + + log_info("token label=%s slot_id=%u\n", tk_label, rc); + +notoken: + PKCS11_release_all_slots(ctx, slots, nslots); +noslots: + PKCS11_CTX_unload(ctx); +nolib: + PKCS11_CTX_free(ctx); + return rc; +} + +/* + * Parse a token determine slot and prefix slot info to supplied key in a + * format openssl likes. New key memmory is allocated and returned in + * newkey which should be freed by caller + */ +static int parse_token_label(char *tk_label, char *key, char **newkey) +{ + char *p; + char *modified_key; + int slot_id; + char slot_str[32]; + + /* + * If user specified token label, convert that into slot and + * prefix it to key in a format understood by openssl + */ + if (tk_label == NULL) + return 0; + + slot_id = token_label_to_slot_id(tk_label); + if (slot_id < 0) + return 1; + + if (!strncmp(key, "id_", 3) || !strncmp(key, "label_", 6)) { + sprintf(slot_str, "slot_%d-", slot_id); + } else + sprintf(slot_str, "%d:", slot_id); + + modified_key = malloc(strlen(slot_str) + strlen(key) + 1); + if (modified_key == NULL) { + log_err("Memory allocation failed\n"); + return 1; + } + + strcpy(modified_key, slot_str); + p = modified_key; + p += strlen(slot_str); + strcpy(p, key); + *newkey = modified_key; + log_info("Using key:%s\n", modified_key); + return 0; +} + +static void unload_engine(ENGINE *e) +{ + /* Free up functional reference */ + if (e != NULL) + ENGINE_finish(e); + + ENGINE_cleanup(); +} + +static ENGINE *load_engine(void) +{ + ENGINE *e; + + if (engine_id == NULL) { + log_err("Provide an engine_id\n"); + return NULL; + } + + if (engine_so == NULL) { + log_err("Provide engine shared library (engine_so)\n"); + return NULL; + } + + if (engine_module == NULL) { + log_err("Provide engine module (engine_module)\n"); + return NULL; + } + + ENGINE_load_dynamic(); + + /* TODO: Allow using built-in engines */ + e = ENGINE_by_id("dynamic"); + if (e == NULL) { + log_err("ENGINE_by_id(dynamic) failed\n"); + return NULL; + } + + if (!ENGINE_ctrl_cmd_string(e, "SO_PATH", engine_so, 0)) { + log_err("ENGINE_ctrl_cmd_string(SO_PATH,%s) failed\n", + engine_so); + goto out; + } + + if (!ENGINE_ctrl_cmd_string(e, "ID", engine_id, 0)) { + log_err("ENGINE_ctrl_cmd_string(ID,%s) failed\n", + engine_id); + goto out; + } + + if (!ENGINE_ctrl_cmd_string(e, "LIST_ADD", "1", 0)) { + log_err("ENGINE_ctrl_cmd_string(LIST_ADD,1) failed\n"); + goto out; + } + + if (!ENGINE_ctrl_cmd_string(e, "LOAD", NULL, 0)) { + log_err("ENGINE_ctrl_cmd_string(LOAD) failed\n"); + goto out; + } + + if (!ENGINE_ctrl_cmd_string(e, "MODULE_PATH", engine_module, 0)) { + log_err("ENGINE_ctrl_cmd_string(MODULE_PATH,%s) failed\n", + engine_module); + goto out; + } + + log_info("Using engine with engine_id: %s\n", ENGINE_get_id(e)); + + ENGINE_set_default(e, ENGINE_METHOD_ALL); + + if (!ENGINE_init(e)) { + log_err("ENGINE_init(e) failed\n"); + goto out; + } + + /* Engine initialized. Free up structural reference */ + ENGINE_free(e); + + /* Use keypass as PIN to login into the card */ + if (keypass != NULL) + ENGINE_ctrl_cmd_string(e, "PIN", keypass, 0); + + + ENGINE_set_default(e, ENGINE_METHOD_RSA); + return e; + +out: + if (e) + ENGINE_free(e); + return NULL; +} + static int hash_ima(const char *file) { unsigned char hash[65] = "\x01"; /* MAX hash size + 1 */ @@ -1761,6 +1990,11 @@ static void usage(void) " -u, --uuid use file system UUID in HMAC calculation (EVM v2)\n" " -n print result to stdout instead of setting xattr\n" " -l, --memlock run executable file locked in memory.\n" + " -e, --engine_id Specify engine id to use for signing (pkcs11)\n" + " -g, --engine_so Specify engine library/shared object file\n" + " -b --engine_module Specify engine module file path\n" + " -c, --pkcs11_module Specify pkcs11 module file path\n" + " -t, --token token label to use\n" " -v increase verbosity level\n" " -h, --help display this help and exit\n" "\n"); @@ -1793,6 +2027,11 @@ static struct option opts[] = { {"x509", 0, 0, 'x'}, {"key", 1, 0, 'k'}, {"memlock", 0, 0, 'l'}, + {"engine_id", 1, 0, 'e'}, + {"engine_so", 1, 0, 'g'}, + {"engine_module", 1, 0, 'b'}, + {"pkcs11_module", 1, 0, 'c'}, + {"token", 1, 0, 't'}, {} }; @@ -1808,7 +2047,8 @@ int main(int argc, char *argv[]) verify_hash = verify_hash_v1; while (1) { - c = getopt_long(argc, argv, "hvnsda:p:fu::xk:l", opts, &lind); + c = getopt_long(argc, argv, "hvnsda:p:fu::xk:le:t:c:g:b:", + opts, &lind); if (c == -1) break; @@ -1861,6 +2101,21 @@ int main(int argc, char *argv[]) case 'l': memlock = true; break; + case 'e': + engine_id = optarg; + break; + case 'g': + engine_so = optarg; + break; + case 'b': + engine_module = optarg; + break; + case 't': + token_label = optarg; + break; + case 'c': + pkcs11_module = optarg; + break; case '?': exit(1); break; @@ -1872,10 +2127,26 @@ int main(int argc, char *argv[]) OpenSSL_add_all_algorithms(); ERR_load_crypto_strings(); - if (argv[optind] == NULL) + if (argv[optind] == NULL) { usage(); - else - err = call_command(cmds, argv[optind++]); + goto out; + } + + if (token_label) { + char *temp_key = strdup(keyfile); + err = parse_token_label(token_label, temp_key, &keyfile); + free(temp_key); + if (err) + goto out; + } + + if (engine_id) { + engine = load_engine(); + if (engine == NULL) + goto out; + } + + err = call_command(cmds, argv[optind++]); if (err) { unsigned long error; @@ -1889,8 +2160,10 @@ int main(int argc, char *argv[]) } } +out: ERR_free_strings(); EVP_cleanup(); + unload_engine(engine); return err; } -- 1.8.3.1 |
From: Vivek G. <vg...@re...> - 2013-09-06 19:38:35
|
This is evmctl-client program which can connect to evmctl daemon and request for file signing. For example one could use it as follows. # make sure /var/run/evmctl is present. $ mkdir -p /var/run/evmctl # start daemon. Use V2 signature with sha256 hash. $ evmctl daemonize -x -a sha256 # Sign a file $ evmctl-client ima_sign --key /etc/keys/privkey_evm.pem /tmp/data.txt # verify signature of file $ evmctl ima_verify /tmp/data.txt -k /etc/keys/x509_evm.der Similar stuff could be done for signing using a key which is on crypto card. - Make sure /var/run/evmctl exists mkdir -p /var/run/evmctl - Start daemon evmctl daemonize -x -a sha256 --engine_id pkcs11 --engine_so /usr/lib64/openssl/engines/engine_pkcs11.so --engine_module opensc-pkcs11.so - Set pin fort the smart card. evmctl-client set_pin <card-pin> - Sign file evmctl-client ima_sign --key slot_1-id_bb82253c8ab1337a74b95a6c68da4a658859c71a /tmp/data.txt Or evmctl-client ima_sign --key id_bb82253c8ab74b95a6c68da4a658859c71a --token "OpenSC Card (Fedora Signing CA)" --pkcs11_module opensc-pkcs11.so /tmp/data.txt - Verify file evmctl ima_verify /tmp/data.txt --key /root/keys/fedora-signer-x509-cert.der Signed-off-by: Vivek Goyal <vg...@re...> --- src/Makefile.am | 7 +- src/client.c | 697 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 703 insertions(+), 1 deletion(-) create mode 100644 src/client.c diff --git a/src/Makefile.am b/src/Makefile.am index 472479f..45d5ea8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,11 +1,16 @@ -bin_PROGRAMS = evmctl +bin_PROGRAMS = evmctl evmctl-client evmctl_SOURCES = evmctl.c evmctl_CPPFLAGS = $(OPENSSL_CFLAGS) evmctl_LDFLAGS = $(LDFLAGS_READLINE) evmctl_LDADD = $(OPENSSL_LIBS) -lkeyutils -lp11 +evmctl_client_SOURCES = client.c +evmctl_client_CPPFLAGS = $(OPENSSL_CFLAGS) +evmctl_client_LDFLAGS = $(LDFLAGS_READLINE) +evmctl_client_LDADD = $(OPENSSL_LIBS) -lkeyutils -lp11 + INCLUDES = -I$(top_srcdir) -include config.h DISTCLEANFILES = @DISTCLEANFILES@ diff --git a/src/client.c b/src/client.c new file mode 100644 index 0000000..92bafa3 --- /dev/null +++ b/src/client.c @@ -0,0 +1,697 @@ +/* + * evm-utils - IMA/EVM support utilities + * + * Copyright (C) 2013 Red Hat Inc. + * + * Authors: + * Vivek Goyal <vg...@re...> + * + * 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: client.c + * IMA/EVM client program + */ + +#define _GNU_SOURCE +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <getopt.h> +#include <syslog.h> +#include <ctype.h> +#include <stdbool.h> +#include <libp11.h> + +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/prctl.h> +#include <poll.h> + +#include "daemon.h" + +#define USE_FPRINTF + +#ifdef DEBUG +#define log_debug(fmt, args...) do_log(LOG_DEBUG, "%s:%d " fmt, __func__ , __LINE__ , ##args) +#else +#define log_debug(fmt, args...) +#define log_debug_dump(p, len) +#endif + +#define log_info(fmt, args...) do_log(LOG_INFO, fmt, ##args) +#define log_warn(fmt, args...) do_log(LOG_WARNING, fmt, ##args) +#define log_notice(fmt, args...) do_log(LOG_NOTICE, fmt, ##args) +#define log_err(fmt, args...) do_log(LOG_ERR, fmt, ##args) +#define log_errno(fmt, args...) do_log(LOG_ERR, fmt ": errno: %s (%d)\n", ##args, strerror(errno), errno) + + +struct client_info { + char *key; + char *token_label; + bool memlock; + char *file; + bool detached; + char *pkcs11_module; +}; + +struct command { + char *name; + int (*func)(struct command *cmd, struct client_info *ci); + int cmd; + char *arg; + char *msg; /* extra info message */ +}; + +static int verbose = LOG_INFO - 1; +static int g_argc; +static char **g_argv; + +struct command cmds[]; +static void print_usage(struct command *cmd); + +static void do_log(int level, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); +#ifdef USE_FPRINTF + vfprintf(stderr, fmt, ap); +#else + vsyslog(level, fmt, ap); +#endif + va_end(ap); +} + +static int token_label_to_slot_id(struct client_info *ci, char *tk_label) +{ + int rc = -1; + PKCS11_CTX *ctx; + PKCS11_SLOT *slots, *slot, *temp_slots; + unsigned int nslots, temp_nslots; + + if (ci->pkcs11_module == NULL) { + log_err("No pkcs11_module specified\n"); + return -1; + } + + ctx = PKCS11_CTX_new(); + + /* load pkcs #11 module */ + rc = PKCS11_CTX_load(ctx, ci->pkcs11_module); + if (rc) { + log_err("Loading pkcs11 engine failed: %s\n", + ERR_reason_error_string(ERR_get_error())); + rc = -1; + goto nolib; + } + + /* get information on all slots */ + rc = PKCS11_enumerate_slots(ctx, &slots, &nslots); + if (rc < 0) { + log_err("No slots available\n"); + rc = -1; + goto noslots; + } + + /* search for slot with given label */ + temp_slots = slots; + temp_nslots = nslots; + + while (1) { + /* get first slot with a token */ + slot = PKCS11_find_token(ctx, temp_slots, temp_nslots); + if (!slot || !slot->token) { + log_err("No token available with given label:%s\n", + tk_label); + rc = -1; + goto notoken; + } + + if (!strcmp(slot->token->label, tk_label)) { + rc = PKCS11_get_slotid_from_slot(slot); + break; + } + + temp_slots = slot + 1; + temp_nslots -= temp_slots - slots; + } + + log_info("token label=%s slot_id=%u\n", tk_label, rc); + +notoken: + PKCS11_release_all_slots(ctx, slots, nslots); +noslots: + PKCS11_CTX_unload(ctx); +nolib: + PKCS11_CTX_free(ctx); + return rc; +} + +/* + * Parse a token determine slot and prefix slot info to supplied key in a + * format openssl likes. New key memmory is allocated and returned in + * newkey which should be freed by caller + */ +static int parse_token_label(struct client_info *ci, + char *tk_label, char *key, char **newkey) +{ + char *p; + char *modified_key; + int slot_id; + char slot_str[32]; + + /* + * If user specified token label, convert that into slot and + * prefix it to key in a format understood by openssl + */ + if (tk_label == NULL) + return 0; + + slot_id = token_label_to_slot_id(ci, tk_label); + if (slot_id < 0) + return 1; + + if (!strncmp(key, "id_", 3) || !strncmp(key, "label_", 6)) { + sprintf(slot_str, "slot_%d-", slot_id); + } else + sprintf(slot_str, "%d:", slot_id); + + modified_key = malloc(strlen(slot_str) + strlen(key) + 1); + if (modified_key == NULL) { + log_err("Memory allocation failed\n"); + return 1; + } + + strcpy(modified_key, slot_str); + p = modified_key; + p += strlen(slot_str); + strcpy(p, key); + *newkey = modified_key; + log_info("Using key:%s\n", modified_key); + return 0; +} + +static int check_response(int sd, char **srvmsg) +{ + ssize_t n; + struct msghdr msg; + struct iovec iov; + char buffer[1024]; + + evmctld_msghdr *em; + + msg.msg_name = NULL; + msg.msg_namelen = 0; + + memset(&msg, '\0', sizeof(msg)); + memset(buffer, '\0', sizeof(buffer)); + + iov.iov_base = buffer; + iov.iov_len = 1023; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + n = recvmsg(sd, &msg, 0); + if (n < 0) { + fprintf(stderr, "evmctl-client: could not get response from " + "server: %m\n"); + exit(1); + } + + em = (evmctld_msghdr *)buffer; + + if (em->version != EVMCTLD_VERSION) { + fprintf(stderr, "evmctl-client: got version %d, " + "expected version %d\n", em->version, EVMCTLD_VERSION); + exit(1); + } + + if (em->command != CMD_RESPONSE) { + fprintf(stderr, "evmctl-client: got unexpected response: %d\n", + em->command); + exit(1); + } + + evmctld_cmd_response *resp = (evmctld_cmd_response *)((uint8_t *)em + + offsetof(evmctld_msghdr, size) + + sizeof(em->size)); + + if (resp->rc == 0) + return 0; + + *srvmsg = strdup((char *)resp->errmsg); + return resp->rc; +} + +static void set_pin(int sd, char *pin) +{ + struct msghdr msg; + struct iovec iov[1]; + evmctld_msghdr em; + + uint32_t size0 = evmctld_string_size(pin); + + em.version = EVMCTLD_VERSION; + em.command = CMD_SET_PIN; + em.size = size0; + iov[0].iov_base = &em; + iov[0].iov_len = sizeof (em); + + memset(&msg, '\0', sizeof(msg)); + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + ssize_t n; + n = sendmsg(sd, &msg, 0); + if (n < 0) { + fprintf(stderr, "evmctl-client: set pin: sendmsg failed: " + "%m\n"); + exit(1); + } + + uint8_t *buffer = NULL; + buffer = calloc(1, size0); + if (!buffer) { + fprintf(stderr, "evmctl-client: could not allocate memory: " + "%m\n"); + exit(1); + } + + evmctld_string *ep = (evmctld_string *)buffer; + evmctld_string_set(ep, pin); + iov[0].iov_base = ep; + iov[0].iov_len = size0; + + n = sendmsg(sd, &msg, 0); + if (n < 0) { + fprintf(stderr, "evmctl-client: set pin: sendmsg failed: " + "%m\n"); + exit(1); + } + + char *srvmsg = NULL; + int rc = check_response(sd, &srvmsg); + if (rc < 0) { + fprintf(stderr, "evmctl-client: %s\n", + srvmsg); + exit(1); + } + + free(buffer); +} + +static int connect_to_server(void) +{ + int rc = access(SOCKPATH, R_OK); + if (rc != 0) { + fprintf(stderr, "evmctl-client: could not connect to server: " + "%m\n"); + exit(1); + } + + struct sockaddr_un addr_un = { + .sun_family = AF_UNIX, + .sun_path = SOCKPATH, + }; + + int sd = socket(AF_UNIX, SOCK_STREAM, 0); + if (sd < 0) { + fprintf(stderr, "pesign-client: could not open socket: %m\n"); + exit(1); + } + + socklen_t len = strlen(addr_un.sun_path) + + sizeof(addr_un.sun_family); + + rc = connect(sd, (struct sockaddr *)&addr_un, len); + if (rc < 0) { + fprintf(stderr, "pesign-client: could not connect to daemon: " + "%m\n"); + exit(1); + } + + return sd; +} + +/* + * Concanate two input strings to a destination. Also allocate memory for + * destination which needs to be freed by caller + */ +static char *strcat_alloc(char *src1, char *src2) +{ + int len; + char *dest; + + len = strlen(src1) + strlen(src2) + 1; + dest = calloc(len, 1); + if (!dest) { + fprintf(stderr, "evmctl-client: failed to allocate memory.\n"); + exit(1); + } + + strcpy(dest, src1); + strcat(dest, src2); + + return dest; +} + +static int sign_ima(int sd, struct client_info *ci) +{ + struct msghdr msg; + int max_nr_strings = 4; + struct iovec iov[max_nr_strings]; /* Key, file, token, memlock str */ + evmctld_msghdr em; + uint32_t total_size = 0; + int nr_strings = 0, len, idx; + char *strings[max_nr_strings], *temp; + + if (!ci->key) { + fprintf(stderr, "evmctl-client: No key specified.\n"); + exit(1); + } + + memset(strings, 0, max_nr_strings * sizeof(char *)); + + /* key to use for signing */ + nr_strings++; + idx = 0; + len = sizeof(evmctld_string) + strlen("--key ") + strlen(ci->key) + 1; + strings[idx] = calloc(len, 1); + if (!strings[idx]) { + fprintf(stderr, "evmctl-client: failed to allocate memory.\n"); + exit(1); + } + + temp = strcat_alloc("--key ", ci->key); + total_size += len; + evmctld_string_set((evmctld_string *)strings[idx], temp); + free(temp); + + /* memlock info */ + if (ci->memlock) { + nr_strings++; + idx++; + len = sizeof(evmctld_string) + strlen("--memlock") + 1; + strings[idx] = calloc(len, 1); + if (!strings[idx]) { + fprintf(stderr, "evmctl-client: failed to allocate" + " memory.\n"); + exit(1); + } + + total_size += len; + evmctld_string_set((evmctld_string *)strings[idx], + "--memlock"); + } + + /* File to be signed */ + nr_strings++; + idx++; + len = sizeof(evmctld_string) + strlen(ci->file) + 1; + strings[idx] = calloc(len, 1); + if (!strings[idx]) { + fprintf(stderr, "evmctl-client: failed to allocate memory.\n"); + exit(1); + } + evmctld_string_set((evmctld_string *)strings[idx], ci->file); + total_size += len; + + + em.version = EVMCTLD_VERSION; + if (ci->detached == true) + em.command = CMD_IMA_SIGN_DETACHED; + else + em.command = CMD_IMA_SIGN_ATTACHED; + em.size = total_size; + iov[0].iov_base = &em; + iov[0].iov_len = sizeof (em); + + memset(&msg, '\0', sizeof(msg)); + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + ssize_t n; + n = sendmsg(sd, &msg, 0); + if (n < 0) { + fprintf(stderr, "evmctl-client: set pin: sendmsg failed: " + "%m\n"); + exit(1); + } + + for (idx = 0; idx < nr_strings; idx++) { + iov[idx].iov_base = strings[idx]; + iov[idx].iov_len = ((evmctld_string *)strings[idx])->size + + sizeof(evmctld_string); + } + + msg.msg_iov = iov; + msg.msg_iovlen = nr_strings; + + n = sendmsg(sd, &msg, 0); + if (n < 0) { + fprintf(stderr, "evmctl-client: set pin: sendmsg failed: " + "%m\n"); + exit(1); + } + + char *srvmsg = NULL; + int rc = check_response(sd, &srvmsg); + if (rc < 0) { + fprintf(stderr, "evmctl-client: %s\n", + srvmsg); + exit(1); + } + + /* Free strings array */ + for (idx = 0; idx < max_nr_strings; idx++) + free(strings[idx]); + + return 0; +} + +static int cmd_ima_sign(struct command *cmd, struct client_info *ci) +{ + char *file = g_argv[optind++]; + int sd = -1, rc = 0; + + if (!file) { + log_err("Parameters missing\n"); + print_usage(cmd); + return -1; + } + + ci->file = file; + + if (!ci->key) { + log_err("Key missing\n"); + print_usage(cmd); + return -1; + } + + /* If a token is specified, convert it to slot id */ + if (ci->token_label) { + char *temp = strdup(ci->key); + if (!temp) { + log_err("Memory allocation failed\n"); + return -ENOMEM; + } + rc = parse_token_label(ci, ci->token_label, temp, &ci->key); + free(temp); + if (rc) { + log_err("Parsing token label failed\n"); + return rc; + } + } + sd = connect_to_server(); + + return sign_ima(sd, ci); + +} + +static int cmd_set_pin(struct command *cmd, struct client_info *ci) +{ + char *pin = g_argv[optind++]; + int sd = -1; + + if (!pin) { + log_err("Parameters missing\n"); + print_usage(cmd); + return -1; + } + + sd = connect_to_server(); + set_pin(sd, pin); + 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("%s", cmd->msg); +} + +static int print_command_usage(struct command *cmds, char *command) +{ + struct command *cmd; + + for (cmd = cmds; cmd->name; cmd++) { + 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; + + printf("commands:\n"); + + for (cmd = cmds; cmd->name; cmd++) { + if (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 client_info *ci) +{ + struct command *cmd; + + for (cmd = cmds; cmd->name; cmd++) { + if (strcasecmp(cmd->name, command) == 0) + return cmd->func(cmd, ci); + } + printf("Invalid command: %s\n", command); + return -1; +} + +static int cmd_help(struct command *cmd, struct client_info *ci) +{ + 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 [-v] <command> [OPTIONS]\n"); + + print_all_usage(cmds); + + printf( + "\n" + " -k, --key path to signing key (default keys are /etc/keys/{privkey,pubkey}_evm.pem)\n" + " -l, --memlock run executable file locked in memory.\n" + " -t, --token token label to use\n" + " -f, --sigfile Generate detached signature.\n" + " -c, --pkcs11_module Specify pkcs11 module to use. Needed if token label is used.\n" + " -v increase verbosity level\n" + " -h, --help display this help and exit\n" + "\n"); +} + +struct command cmds[] = { + {"help", cmd_help, 0, "<command>"}, + {"set_pin", cmd_set_pin, 0, "pin", "Set evmctld engine pin.\n"}, + {"ima_sign", cmd_ima_sign, 0, "--key key [--token token_label] [--sigfile] [--memlock] file", "Make file content signature.\n"}, + {0, 0, 0, NULL} +}; + +static struct option opts[] = { + {"help", 0, 0, 'h'}, + {"sigfile", 0, 0, 'f'}, + {"key", 1, 0, 'k'}, + {"memlock", 0, 0, 'l'}, + {"token", 1, 0, 't'}, + {"pkcs11_module", 1, 0, 'c'}, + {} + +}; + +int main(int argc, char *argv[]) +{ + int err = 0, c, lind; + struct client_info client_info, *ci; + + + g_argv = argv; + g_argc = argc; + + ci = &client_info; + memset(ci, 0, sizeof(struct client_info)); + + while (1) { + c = getopt_long(argc, argv, "hfk:lt:", opts, &lind); + if (c == -1) + break; + + switch (c) { + case 'h': + usage(); + exit(0); + break; + case 'v': + verbose++; + break; + case 'f': + ci->detached = true; + break; + case 'k': + ci->key = optarg; + break; + case 'l': + ci->memlock = true; + break; + case 't': + ci->token_label = optarg; + break; + case 'c': + ci->pkcs11_module = optarg; + 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++], ci); + + if (err) + log_err("evmctl-client exited with error %d\n, err"); + + return err; +} -- 1.8.3.1 |
From: Vivek G. <vg...@re...> - 2013-09-06 19:38:35
|
Launch evmctl as daemon so that clients can connect to it and request file signing. This can be useful when signing needs to happen using an crypto card and client does not know the pin of the card. Machine owner can setup evmctl daemon, set pin and later clients can request file signing without knowing pin. I have taken lot of daemon and client code from pesign project (written by peter jones). Signed-off-by: Vivek Goyal <vg...@re...> --- src/daemon.h | 83 +++++++ src/evmctl.c | 776 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 829 insertions(+), 30 deletions(-) create mode 100644 src/daemon.h diff --git a/src/daemon.h b/src/daemon.h new file mode 100644 index 0000000..0a29ae6 --- /dev/null +++ b/src/daemon.h @@ -0,0 +1,83 @@ +/* + * Copyright 2013 Red Hat, Inc. + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Author(s): Vivek Goyal <vg...@re...> + */ +#ifndef DAEMON_H +#define DAEMON_H 1 + +/* Daemon related stuff */ +#define SOCKPATH "/var/run/evmctl/socket" +#define PIDFILE "/var/run/evmctld.pid" +#define EVMCTLD_VERSION 0xb4d052dc + +extern int daemonize(void); + +typedef struct { + uint32_t version; + uint32_t command; + uint32_t size; +} evmctld_msghdr; + +typedef struct { + uint32_t rc; + uint8_t errmsg[]; +} evmctld_cmd_response; + +typedef struct { + uint32_t size; + uint8_t value[]; +} evmctld_string; + +typedef enum { + CMD_KILL_DAEMON, + CMD_SET_PIN, + CMD_IMA_SIGN_ATTACHED, + CMD_IMA_SIGN_DETACHED, + CMD_RESPONSE, + CMD_LIST_END +} evmctld_cmd; + +static inline uint32_t +__attribute__ ((unused)) +evmctld_string_size(char *buffer) +{ + evmctld_string *s; + return sizeof(s->size) + (buffer ? strlen(buffer) : 0) + 1; +} + +static inline void +__attribute__ ((unused)) +evmctld_string_set(evmctld_string *str, char *value) +{ + str->size = (value ? strlen(value) : 0) + 1; + if (value) + strcpy((char *)str->value, value); + else + str->value[0] = '\0'; +} + +static inline evmctld_string * +__attribute__ ((unused)) +evmctld_string_next(evmctld_string *str) +{ + char *buffer = (char *)str; + buffer += sizeof(str->size) + str->size; + return (evmctld_string *)buffer; +} + + +#endif /* DAEMON_H */ diff --git a/src/evmctl.c b/src/evmctl.c index 2205d1e..6638410 100644 --- a/src/evmctl.c +++ b/src/evmctl.c @@ -26,6 +26,7 @@ * IMA/EVM control program */ +#define _GNU_SOURCE #include <sys/types.h> #include <sys/stat.h> #include <sys/ioctl.h> @@ -48,6 +49,11 @@ #include <stdbool.h> #include <libp11.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/prctl.h> +#include <poll.h> + #include <openssl/sha.h> #include <openssl/rsa.h> #include <openssl/pem.h> @@ -57,15 +63,9 @@ #include <openssl/err.h> #include <openssl/x509.h> -#define USE_FPRINTF +#include "daemon.h" -#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 +#define USE_FPRINTF #ifdef DEBUG #define log_debug(fmt, args...) do_log(LOG_DEBUG, "%s:%d " fmt, __func__ , __LINE__ , ##args) @@ -77,6 +77,8 @@ #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_warn(fmt, args...) do_log(LOG_WARNING, fmt, ##args) +#define log_notice(fmt, args...) do_log(LOG_NOTICE, fmt, ##args) #define log_err(fmt, args...) do_log(LOG_ERR, fmt, ##args) #define log_errno(fmt, args...) do_log(LOG_ERR, fmt ": errno: %s (%d)\n", ##args, strerror(errno), errno) @@ -91,6 +93,10 @@ #define FS_IOC32_GETFLAGS _IOR('f', 1, int) #define FS_IOC32_SETFLAGS _IOW('f', 2, int) +static int should_exit = 0; +static int socket_desc; +static bool daemon_mode = false; + struct h_misc { unsigned long ino; uint32_t generation; @@ -252,6 +258,14 @@ struct command { char *msg; /* extra info message */ }; +/* When in daemon mode, this keep info about client context */ +struct client_context { + bool memlock; /* Client wants to memlock file */ + char *token_label; + char *key; + char *file; +}; + static int verbose = LOG_INFO - 1; static int g_argc; static char **g_argv; @@ -287,6 +301,26 @@ static verify_hash_fn_t verify_hash; struct command cmds[]; static void print_usage(struct command *cmd); +static void do_log(int level, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + if (daemon_mode == true) { + vsyslog(level, fmt, ap); + va_end(ap); + return; + } + +#ifdef USE_FPRINTF + if (level <= verbose) + vfprintf(stderr, fmt, ap); +#else + vsyslog(level, fmt, ap); +#endif + va_end(ap); +} + static void do_dump(FILE *fp, const void *ptr, int len) { int i; @@ -297,8 +331,26 @@ static void do_dump(FILE *fp, const void *ptr, int len) fprintf(fp, "\n"); } +static void do_log_dump(int level, const void *p, int len) +{ +#ifdef USE_FPRINTF + if (level > verbose) + return; + + /* May be we can dump to syslog in daemon mode ? */ + if (daemon_mode == true) + return; + + do_dump(stderr, p, len); +#endif +} + static void dump(const void *ptr, int len) { + /* May be we can dump to syslog in daemon mode ? */ + if (daemon_mode == true) + return; + do_dump(stdout, ptr, len); } @@ -1133,10 +1185,10 @@ static int parse_token_label(char *tk_label, char *key, char **newkey) static void unload_engine(ENGINE *e) { /* Free up functional reference */ - if (e != NULL) + if (e != NULL) { ENGINE_finish(e); - - ENGINE_cleanup(); + ENGINE_cleanup(); + } } static ENGINE *load_engine(void) @@ -1271,7 +1323,8 @@ static int add_memlock_info(unsigned char *ptr) return sizeof(struct memlock_hdr); } -static int sign_ima(const char *file, const char *key) +static int sign_ima(const char *file, const char *key, + struct client_context *cc) { unsigned char hash[64]; unsigned char sig[1024] = "\x03"; @@ -1299,7 +1352,7 @@ static int sign_ima(const char *file, const char *key) return 0; } - if (memlock) { + if (memlock || (cc && cc->memlock)) { memlock_len = add_memlock_info(sig + len); len += memlock_len; } @@ -1321,6 +1374,7 @@ static int sign_ima(const char *file, const char *key) static int cmd_sign_ima(struct command *cmd) { char *key, *file = g_argv[optind++]; + int rc; if (!file) { log_err("Parameters missing\n"); @@ -1330,7 +1384,23 @@ static int cmd_sign_ima(struct command *cmd) key = keyfile ? : "/etc/keys/privkey_evm.pem"; - return sign_ima(file, key); + if (token_label) { + char *temp_key = strdup(keyfile); + rc = parse_token_label(token_label, temp_key, &keyfile); + free(temp_key); + if (rc) + return -1; + } + + if (engine_id) { + engine = load_engine(); + if (engine == NULL) { + log_err("Failed to load engine\n"); + return -1; + } + } + + return sign_ima(file, key, NULL); } @@ -1348,7 +1418,7 @@ static int cmd_sign_evm(struct command *cmd) key = keyfile ? : "/etc/keys/privkey_evm.pem"; if (digsig) { - err = sign_ima(file, key); + err = sign_ima(file, key, NULL); if (err) return err; } @@ -1420,6 +1490,665 @@ out: return ret; } +static void write_pid_file(int pid) +{ + int fd = open(PIDFILE, O_WRONLY|O_CREAT|O_TRUNC, 0644); + if (fd < 0) { +err: + fprintf(stderr, "couldn't open pidfile: %m\n"); + exit(1); + } + char *pidstr = NULL; + int rc = asprintf(&pidstr, "%d\n", pid); + if (rc < 0) + goto err; + + rc = write(fd, pidstr, strlen(pidstr)+1); + if (rc < 0) + goto err; + + free(pidstr); + close(fd); +} + +static void check_socket(void) +{ + errno = 0; + int rc = access(SOCKPATH, R_OK); + if (rc == 0) { + struct sockaddr_un addr_un = { + .sun_family = AF_UNIX, + .sun_path = SOCKPATH, + }; + + int sd = socket(AF_UNIX, SOCK_SEQPACKET, 0); + if (sd < 0) { + fprintf(stderr, "unable to create socket: %m"); + exit(1); + } + + socklen_t len = strlen(addr_un.sun_path) + + sizeof(addr_un.sun_family); + + rc = connect(sd, (struct sockaddr *)&addr_un, len); + if (rc < 0) { + close(sd); + unlink(SOCKPATH); + return; + } + + struct sockaddr_un remote; + socklen_t size = sizeof(remote); + rc = getpeername(sd, &remote, &size); + if (rc < 0) { + close(sd); + return; + } else { + fprintf(stderr, "already running"); + exit(1); + } + } else { + /* It could be something other than EEXIST, but it really + * doesn't matter since the daemon isn't running. Blindly + * remove it. */ + unlink(SOCKPATH); + } +} + +static void daemon_setup_std_descriptors(void) +{ + int rc; + + /* Handle stdio, stdout and stderr */ + int fd = open("/dev/zero", O_RDONLY); + if (fd < 0) { + log_err("could not open /dev/zero: %m"); + exit(1); + } + close(STDIN_FILENO); + + rc = dup2(fd, STDIN_FILENO); + if (rc < 0) { + log_err("could not set up standard input: %m"); + exit(1); + } + close(fd); + + fd = open("/dev/null", O_WRONLY); + if (fd < 0) { + log_err("could not open /dev/null: %m"); + exit(1); + } + close(STDOUT_FILENO); + + rc = dup2(fd, STDOUT_FILENO); + if (rc < 0) { + log_err("could not set up standard output: %m"); + exit(1); + } + close(STDERR_FILENO); + + rc = dup2(fd, STDERR_FILENO); + if (rc < 0) { + log_err("could not set up standard error: %m"); + exit(1); + } + close(fd); +} + +static void quit_handler(int signal) +{ + should_exit = 1; +} + +static int set_up_socket(void) +{ + int sd = socket(AF_UNIX, SOCK_STREAM, 0); + if (sd < 0) { + log_err("unable to create socket: %m"); + exit(1); + } + + int one = 1; + int rc = setsockopt(sd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)); + if (rc < 0) { + log_err("unable to set socket options: %m"); + exit(1); + } + + struct sockaddr_un addr_un = { + .sun_family = AF_UNIX, + .sun_path = SOCKPATH, + }; + + rc = bind(sd, &addr_un, sizeof(addr_un)); + if (rc < 0) { + log_err("unable to bind to \"%s\": %m", addr_un.sun_path); + exit(1); + } + rc = chmod(SOCKPATH, 0660); + if (rc < 0) { + log_err("could not set permissions for \"%s\": %m", SOCKPATH); + exit(1); + } + + rc = listen(sd, 5); + if (rc < 0) { + log_err("unable to listen on socket: %m"); + exit(1); + } + + socket_desc = sd; + return 0; +} + +static void send_response(struct pollfd *pollfd, int rc, char *response) +{ + struct msghdr msg; + struct iovec iov; + ssize_t n; + int msglen = response ? strlen(response) + 1 : 0; + + iov.iov_len = sizeof(evmctld_msghdr) + sizeof(evmctld_cmd_response) + + msglen; + + void *buffer = calloc(1, iov.iov_len); + if (!buffer) { + log_err("could not allocate memory: %m"); + exit(1); + } + + iov.iov_base = buffer; + + evmctld_msghdr *em = buffer; + evmctld_cmd_response *resp = (evmctld_cmd_response *)((uint8_t *)em + + offsetof(evmctld_msghdr, size) + + sizeof (em->size)); + + em->version = EVMCTLD_VERSION; + em->command = CMD_RESPONSE; + em->size = sizeof(resp->rc) + msglen; + + memset(&msg, '\0', sizeof(msg)); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + resp->rc = rc; + + if (response) + memcpy(resp->errmsg, response, msglen); + + n = sendmsg(pollfd->fd, &msg, 0); + if (n < 0) + log_warn("could not send response to client: %m"); + + free(buffer); +} + +static void handle_kill_daemon(struct pollfd *pollfd, socklen_t size) +{ + should_exit = 1; +} + +static void free_client_context(struct client_context *cc) +{ + if (cc == NULL) + return; + + if (cc->key) + free(cc->key); + if (cc->token_label) + free(cc->token_label); + free(cc); +} + +static void handle_ima_sign(struct pollfd *pollfd, socklen_t size) +{ + struct msghdr msg; + struct iovec iov; + ssize_t n; + evmctld_string *ekey = NULL, *etoken_label = NULL, *efile = NULL, *etemp; + bool ememlock = false; + char *key, *temp; + int rc; + struct client_context *cc; + + /* Allocate client context */ + cc = calloc(sizeof(struct client_context), 1); + if (!cc) + goto oom; + + char *buffer = malloc(size); + if (!buffer) { +oom: + log_err("unable to allocate memory: %m"); + exit(1); + } + + memset(&msg, '\0', sizeof(msg)); + + iov.iov_base = buffer; + iov.iov_len = size; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + n = recvmsg(pollfd->fd, &msg, MSG_WAITALL); + + etemp = (evmctld_string *)buffer; + if (n < sizeof(etemp->size)) { +malformed: + log_err("sign: invalid data\n"); + log_err("possible exploit attempt. closing.\n"); + close(pollfd->fd); + return; + } + + n -= sizeof(etemp->size); + if (n < etemp->size) + goto malformed; + n -= etemp->size; + + if (etemp->value[etemp->size - 1] != '\0') + goto malformed; + + ekey = etemp; + log_info("sign: key=%s.\n", (char *)ekey->value); + +next_string: + etemp = evmctld_string_next(etemp); + n -= sizeof(etemp->size); + if (n < etemp->size) + goto malformed; + n -= etemp->size; + + if (etemp->value[etemp->size - 1] != '\0') + goto malformed; + + log_info("sign: etemp->value=%s", (char *)etemp->value); + + if (etemp->size > 15 && + !strncmp((char *)etemp->value, "--token_label ", 14)) { + if (etoken_label != NULL) + goto malformed; + etoken_label = etemp; + temp = (char *)etemp->value + strlen("--token_label "); + cc->token_label = strdup(temp); + if (!cc->token_label) + goto oom; + } else if (etemp->size > 9 && + !strncmp((char *)etemp->value, "--memlock", 9)) { + if (ememlock != false) + goto malformed; + ememlock = true; + cc->memlock = true; + } else { + efile = etemp; + cc->file = strdup((char *)efile->value); + if (!cc->file) + goto oom; + } + + /* file to be signed is last string */ + if (!efile && n > 0) + goto next_string; + + if (n != 0) + goto malformed; + + if (!efile) { + log_info("Did not find efile\n"); + goto malformed; + } + + log_info("sign: key=%s efile=%s, memlock=%d token_label=%s\n", + ekey->value, efile->value, ememlock, + etoken_label ? etoken_label->value : NULL); + + if (sigfile) + log_notice("ima signing detached"); + else + log_notice("ima signing attached"); + + /* Strip --key from key */ + if (strncmp((char *)ekey->value, "--key ", 6)) { + /* key is not prefixed with --key. Bad message */ + goto malformed; + } + + key = (char *)ekey->value + strlen("--key "); + + /* Convert token label to slot and modify key accordingly */ + if (cc->token_label) { + rc = parse_token_label(cc->token_label, key, &cc->key); + if (rc) { + log_notice("Failed to parse token label"); + send_response(pollfd, rc, "Failed to parse token label"); + goto out; + } + } else { + cc->key = strdup(key); + if (!cc->key) + goto oom; + } + + rc = sign_ima((char *)cc->file, cc->key, cc); + if (!rc) { + log_notice("Signing successful"); + send_response(pollfd, rc, "Signing successful"); + } else { + log_notice("Signing Failed"); + send_response(pollfd, rc, "Signing Failed"); + } + +out: + free(buffer); + free_client_context(cc); +} + +static void handle_ima_sign_detached(struct pollfd *pollfd, socklen_t size) +{ + /* emulate passing -f */ + sigfile = 1; + xattr = 0; + handle_ima_sign(pollfd, size); +} + +static void handle_ima_sign_attached(struct pollfd *pollfd, socklen_t size) +{ + handle_ima_sign(pollfd, size); +} + +static void handle_set_pin(struct pollfd *pollfd, socklen_t size) +{ + struct msghdr msg; + struct iovec iov; + ssize_t n; + + char *buffer = malloc(size); + if (!buffer) { +oom: + log_err("unable to allocate memory: %m"); + exit(1); + } + + memset(&msg, '\0', sizeof(msg)); + + iov.iov_base = buffer; + iov.iov_len = size; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + n = recvmsg(pollfd->fd, &msg, MSG_WAITALL); + + evmctld_string *ep = (evmctld_string *)buffer; + if (n < sizeof(ep->size)) { +malformed: + log_err("set-pin: invalid data\n"); + log_err("possible exploit attempt. closing.\n"); + close(pollfd->fd); + return; + } + + n -= sizeof(ep->size); + if (n < ep->size) + goto malformed; + n -= ep->size; + + if (ep->value[ep->size - 1] != '\0') + goto malformed; + + if (n != 0) + goto malformed; + + log_notice("setting pin"); + + char *pin = (char *)ep->value; + if (!pin) + goto oom; + + if (engine) { + ENGINE_ctrl_cmd_string(engine, "PIN", pin, 0); + log_notice("set pin done."); + send_response(pollfd, 0, "set pin done"); + } else { + log_notice("No engine loaded. set pin failed.\n"); + send_response(pollfd, 1, "No engine loaded. set pin failed."); + } + + free(buffer); +} + +static void handle_invalid_input(evmctld_cmd cmd, struct pollfd *pollfd, + socklen_t size) +{ + log_err("got unexpected command 0x%x", cmd); + log_err("possible exploit attempt"); +} + +typedef void (*cmd_handler)(struct pollfd *pollfd, socklen_t size); + +typedef struct { + evmctld_cmd cmd; + cmd_handler func; +} cmd_table_t; + +cmd_table_t cmd_table[] = { + { CMD_KILL_DAEMON, handle_kill_daemon }, + { CMD_SET_PIN, handle_set_pin }, + { CMD_IMA_SIGN_ATTACHED, handle_ima_sign_attached }, + { CMD_IMA_SIGN_DETACHED, handle_ima_sign_detached }, + { CMD_RESPONSE, NULL }, + { CMD_LIST_END, NULL } + }; + +static int handle_event(struct pollfd *pollfd) +{ + struct msghdr msg; + struct iovec iov; + ssize_t n; + evmctld_msghdr em; + int i; + + msg.msg_name = NULL; + msg.msg_namelen = 0; + + iov.iov_base = &em; + iov.iov_len = sizeof(em); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + char control[1024]; + msg.msg_controllen = 1024; + msg.msg_control = control; + + n = recvmsg(pollfd->fd, &msg, MSG_WAITALL); + if (n < 0) { + log_warn("recvmsg failed: %m"); + return n; + } + + /* if recvmsg returned 0, we're not going to get any valid data. */ + /* This *probably* means we were hung up on. */ + if (n == 0) + return n; + + if (n < sizeof (em)) { + log_err("got message with invalid size %zu", n); + log_err("possible exploit attempt. closing."); + close(pollfd->fd); + return -1; + } + + if (em.version != EVMCTLD_VERSION) { + log_err("got version %d, expected version %d", + em.version, EVMCTLD_VERSION); + log_err("possible exploit attempt. closing."); + close(pollfd->fd); + return -1; + } + + for (i = 0; cmd_table[i].cmd != CMD_LIST_END; i++) { + if (cmd_table[i].cmd == em.command) { + if (cmd_table[i].func == NULL) { + handle_invalid_input(em.command, pollfd, + em.size); + close(pollfd->fd); + } + cmd_table[i].func(pollfd, em.size); + return 0; + } + } + + handle_invalid_input(em.command, pollfd, em.size); + close(pollfd->fd); + return 0; +} + +static void do_shutdown(int nsockets, struct pollfd *pollfds) +{ + int i; + + unlink(SOCKPATH); + unlink(PIDFILE); + + log_notice("evmctld exiting (pid %d)", getpid()); + + for (i = 0; i < nsockets; i++) + close(pollfds[i].fd); + free(pollfds); +} + +static int handle_events(void) +{ + int rc; + int nsockets = 1; + int i, j; + + struct pollfd *pollfds = calloc(1, sizeof(struct pollfd)); + + if (!pollfds) { + log_err("could not allocate memory: %m"); + exit(1); + } + + pollfds[0].fd = socket_desc; + pollfds[0].events = POLLIN|POLLPRI|POLLHUP; + + while (1) { + if (should_exit != 0) { +shutdown: + do_shutdown(nsockets, pollfds); + return 0; + } + rc = ppoll(pollfds, nsockets, NULL, NULL); + if (should_exit != 0) + goto shutdown; + if (rc < 0) { + log_warn("ppoll: %m"); + continue; + } + + if (pollfds[0].revents & POLLIN) { + nsockets++; + struct pollfd *newpollfds = realloc(pollfds, + nsockets * sizeof(struct pollfd)); + + if (!newpollfds) { + log_err("could not allocate memory: %m"); + exit(1); + } + pollfds = newpollfds; + + struct sockaddr_un remote; + socklen_t len = sizeof(remote); + pollfds[nsockets-1].fd = accept(pollfds[0].fd, &remote, + &len); + pollfds[nsockets-1].events = POLLIN|POLLPRI|POLLHUP; + pollfds[nsockets-1].revents = pollfds[0].revents; + } + + for (i = 1; i < nsockets; i++) { + if (pollfds[i].revents & (POLLHUP|POLLNVAL)) { + close(pollfds[i].fd); + if (i == nsockets-1) { + nsockets--; + continue; + } + for (j = i; j < nsockets - 1; j++) { + pollfds[j].fd = pollfds[j+1].fd; + pollfds[j].events = + pollfds[j].events; + pollfds[j].revents = + pollfds[j].revents; + } + nsockets--; + i--; + continue; + } + + if (pollfds[i].revents & (POLLIN|POLLPRI)) + handle_event(&pollfds[i]); + } + } + return 0; +} + +static int cmd_daemonize(struct command *cmd) +{ + pid_t pid; + int rc; + + if (getuid() != 0) { + fprintf(stderr, "evmctl daemon must be started as root"); + exit(1); + } + + check_socket(); + + openlog("evmctld", LOG_PID, LOG_DAEMON); + + if ((pid = fork())) { + sleep(2); + return 0; + } + + daemon_mode = true; + + write_pid_file(getpid()); + + /* Load engine */ + if (engine_id) { + engine = load_engine(); + if (engine == NULL) { + log_err("Failed to load engine\n"); + exit(1); + } + } + + log_notice("evmctld starting(pid %d)", getpid()); + + /* Handle stdio, stdout and stderr */ + daemon_setup_std_descriptors(); + prctl(PR_SET_NAME, "evmctld", 0, 0, 0); + setsid(); + + struct sigaction sa = { + .sa_handler = quit_handler, + }; + sigaction(SIGQUIT, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + + chdir("/"); + + set_up_socket(); + + /* TODO: Handling related to password functions */ + + rc = handle_events(); + return 0; +} + static int cmd_ima_sig_import(struct command *cmd) { char *rawsigfile, *file, *key; @@ -1895,7 +2624,7 @@ static int cmd_hmac_evm(struct command *cmd) key = keyfile ? : "/etc/keys/privkey_evm.pem"; if (digsig) { - err = sign_ima(file, key); + err = sign_ima(file, key, NULL); if (err) return err; } @@ -2012,6 +2741,7 @@ struct command cmds[] = { {"hmac", cmd_hmac_evm, 0, "[--imahash | --imasig ] file", "Sign file metadata with HMAC using symmetric key (for testing purpose).\n"}, #endif {"ima_sig_import", cmd_ima_sig_import, 0, "[--x509] [-a hashalgo] [-k pubkey] signature file", "Make IMA signature from externally signed raw signature blob.\n"}, + {"daemonize", cmd_daemonize, 0, "", "Start evmctl daemon for signing\n"}, {0, 0, 0, NULL} }; @@ -2132,20 +2862,6 @@ int main(int argc, char *argv[]) goto out; } - if (token_label) { - char *temp_key = strdup(keyfile); - err = parse_token_label(token_label, temp_key, &keyfile); - free(temp_key); - if (err) - goto out; - } - - if (engine_id) { - engine = load_engine(); - if (engine == NULL) - goto out; - } - err = call_command(cmds, argv[optind++]); if (err) { -- 1.8.3.1 |
From: Vivek G. <vg...@re...> - 2013-09-06 19:38:35
|
Hi, This is an RFC patch series to get early feedback on stuff I am working on. This series does few things. - Adds an extra structure to ima signature (security.ima) which will signal the elf loader that this executable needs to be locked. This will be useful for secureboot where signed /sbin/kexec needs to run memory locked. I have posted RFC kernel patches on Fedora kernel mailing list. https://lists.fedoraproject.org/pipermail/kernel/2013-September/004432.html kexec-tools patches are posted here. https://lists.fedoraproject.org/pipermail/kernel/2013-September/004469.html - Add a functionality to import signatures signed externally. (Patch 2) - Add functionality to allow signing using external crypto card. (Patch 3) - Add a functionality to create a daemon which cilents can connect to and request file signing (Patch 4 and Patch 5). All the signing enhancements I need so that various build servers can make use of it to sign /sbin/kexec and bzImage using appropriate keys. This is still a work in progress and code is very raw. I wanted to get the code out to get early feedback. Thanks Vivek Vivek Goyal (5): evmctl: Allow adding a memlock information in security.ima evmctl: Allow importing external signature evmctl: Allow signing using external crypto engine evmctl-allow-launching-daemon evmctl-client: A simple client to request signing from evmctl daemon configure.ac | 1 + src/Makefile.am | 9 +- src/client.c | 697 +++++++++++++++++++++++++++++++++ src/daemon.h | 83 ++++ src/evmctl.c | 1166 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 1934 insertions(+), 22 deletions(-) create mode 100644 src/client.c create mode 100644 src/daemon.h -- 1.8.3.1 |
From: Vivek G. <vg...@re...> - 2013-09-06 19:38:34
|
Signed executables need to run locked in memory otherwise it might happen that they can be swapped out and then there is a possiblity that these can be attacked by directly writing to swap. So add a memlock structure in security.ima xattr. Kernel will parse it and memlock the executable file if signature verification was successful. Currently this will happen only for elf binaries. Signed-off-by: Vivek Goyal <vg...@re...> --- src/evmctl.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 57 insertions(+), 4 deletions(-) diff --git a/src/evmctl.c b/src/evmctl.c index aa61338..e24b9ed 100644 --- a/src/evmctl.c +++ b/src/evmctl.c @@ -45,6 +45,7 @@ #include <attr/xattr.h> #include <dirent.h> #include <ctype.h> +#include <stdbool.h> #include <openssl/sha.h> #include <openssl/rsa.h> @@ -165,6 +166,14 @@ struct signature_v2_hdr { uint8_t sig[0]; /* signature payload */ } __attribute__ ((packed)); +/* memlocking info header */ +#define MEMLOCK_MAGIC_STR "MEMLOCK" +struct memlock_hdr { + uint8_t magic_str[8]; /* magic to detect memlock hdr presence */ + uint8_t version; /* memlock info hdr version */ + uint8_t memlock_file; /* If set, run executable locked in memory */ +} __attribute__ ((packed)); + /* * Hash algorithm OIDs plus ASN.1 DER wrappings [RFC4880 sec 5.2.2]. @@ -258,6 +267,7 @@ static char *uuid_str; static int x509; static int user_sig_type; static char *keyfile; +static bool memlock = false; typedef int (*sign_hash_fn_t)(const char *algo, const unsigned char *hash, int size, const char *keyfile, unsigned char *sig); @@ -1021,12 +1031,23 @@ static int cmd_hash_ima(struct command *cmd) return hash_ima(file); } +static int add_memlock_info(unsigned char *ptr) +{ + struct memlock_hdr *memlock_hdr = (struct memlock_hdr *)ptr; + strcpy((char *)memlock_hdr->magic_str, MEMLOCK_MAGIC_STR); + + memlock_hdr->version = 1; + memlock_hdr->memlock_file = 1; + + return sizeof(struct memlock_hdr); +} + static int sign_ima(const char *file, const char *key) { unsigned char hash[64]; unsigned char sig[1024] = "\x03"; char magic[] = "This Is A Crypto Signed Module"; - int len, err; + int len, err, memlock_len = 0; len = calc_hash(file, hash); if (len <= 1) @@ -1049,6 +1070,11 @@ static int sign_ima(const char *file, const char *key) return 0; } + if (memlock) { + memlock_len = add_memlock_info(sig + len); + len += memlock_len; + } + if (sigfile) bin2file(file, "sig", sig, len); @@ -1262,11 +1288,27 @@ static int get_hash_algo_from_sig(unsigned char *sig) return -1; } +static int get_digsig_len(const unsigned char *sig) +{ + uint16_t sz; + + if (sig[0] == 1) { + sz = *((uint16_t *)(sig + sizeof(struct signature_hdr))); + sz = __be16_to_cpu(sz); + return sizeof(struct signature_hdr) + 2 + (sz >> 3); + } else if (sig[0] == 2 ) { + sz = ((struct signature_v2_hdr *)sig)->sig_size; + return sizeof(struct signature_v2_hdr) + __be16_to_cpu(sz); + } + + return -EBADMSG; +} + static int verify_ima(const char *file) { unsigned char hash[64]; unsigned char sig[1024]; - int len, hashlen; + int len, hashlen, digsiglen; int sig_hash_algo; char *key; @@ -1322,7 +1364,13 @@ static int verify_ima(const char *file) "/etc/keys/x509_evm.der" : "/etc/keys/pubkey_evm.pem"; - return verify_hash(hash, hashlen, sig + 1, len - 1, key); + digsiglen = get_digsig_len(sig + 1); + if (digsiglen < 0) { + log_err("Bad digital signature"); + return -1; + } + + return verify_hash(hash, hashlen, sig + 1, digsiglen, key); } static int cmd_verify_ima(struct command *cmd) @@ -1629,6 +1677,7 @@ static void usage(void) " -p, --pass password for encrypted signing key\n" " -u, --uuid use file system UUID in HMAC calculation (EVM v2)\n" " -n print result to stdout instead of setting xattr\n" + " -l, --memlock run executable file locked in memory.\n" " -v increase verbosity level\n" " -h, --help display this help and exit\n" "\n"); @@ -1659,6 +1708,7 @@ static struct option opts[] = { {"uuid", 2, 0, 'u'}, {"x509", 0, 0, 'x'}, {"key", 1, 0, 'k'}, + {"memlock", 0, 0, 'l'}, {} }; @@ -1674,7 +1724,7 @@ int main(int argc, char *argv[]) verify_hash = verify_hash_v1; while (1) { - c = getopt_long(argc, argv, "hvnsda:p:fu::xk:", opts, &lind); + c = getopt_long(argc, argv, "hvnsda:p:fu::xk:l", opts, &lind); if (c == -1) break; @@ -1724,6 +1774,9 @@ int main(int argc, char *argv[]) case 'k': keyfile = optarg; break; + case 'l': + memlock = true; + break; case '?': exit(1); break; -- 1.8.3.1 |
From: Vivek G. <vg...@re...> - 2013-09-06 19:38:34
|
It is possible that one uses external cryptographic engine to create RSA PKCS1.5 signature of a file. Import these signatures and wrap it with IMA specific metadata to convert it into IMA signature. For example, one could do following. $ echo "Hello World" > /tmp/data.txt $ openssl dgst -sha256 -sign signing_key.priv.pem -out /tmp/data.sig /tmp/data.txt $ evmctl ima_sig_import -x -a sha256 -k signing_key.x509.der /tmp/data.sig /tmp/data.txt # Verify signature $ evmctl ima_verify -k signing_key.x509.der /tmp/data.txt Signed-off-by: Vivek Goyal <vg...@re...> --- src/evmctl.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/src/evmctl.c b/src/evmctl.c index e24b9ed..3679e68 100644 --- a/src/evmctl.c +++ b/src/evmctl.c @@ -1133,6 +1133,89 @@ static int cmd_sign_evm(struct command *cmd) return sign_evm(file, key); } +static int ima_sig_import_v2(const char *hashalgo, const char *keyfile, const char *rawsigfile, const char *file) +{ + unsigned char *rawsig; + int rawsigsz, len, ret = 0, memlock_len; + unsigned char sig[1024] = "\x03"; + char name[20]; + struct signature_v2_hdr *hdr = (struct signature_v2_hdr *)(sig + 1); + RSA *key; + + rawsig = file2bin(rawsigfile, NULL, &rawsigsz); + if (!rawsig) { + log_err("Unable to read file %s\n", rawsigfile); + return 1; + } + + key = read_pub_key(keyfile); + if (!key) { + free(rawsig); + return 1; + } + + hdr->version = 2; + hdr->hash_algo = get_hash_algo(hashalgo); + + calc_keyid_v2(&hdr->keyid, name, key); + memcpy(hdr->sig, rawsig, rawsigsz); + + len = rawsigsz; + hdr->sig_size = __cpu_to_be16(len); + len += sizeof(*hdr); + log_info("evm/ima signature: %d bytes\n", len); + if (sigdump || verbose >= LOG_INFO) + dump(sig + 1, len); + + /* Add ima header */ + len++; + if (memlock) { + memlock_len = add_memlock_info(sig + len); + len += memlock_len; + } + + if (sigfile) + bin2file(file, "sig", sig, len); + + if (xattr) { + ret = setxattr(file, "security.ima", sig, len, 0); + if (ret < 0) { + log_err("setxattr failed: %s\n", file); + goto out; + } + } + +out: + RSA_free(key); + free(rawsig); + return ret; +} + +static int cmd_ima_sig_import(struct command *cmd) +{ + char *rawsigfile, *file, *key; + + rawsigfile = g_argv[optind++]; + file = g_argv[optind++]; + + if (!file || !rawsigfile) { + log_err("Parameters missing\n"); + print_usage(cmd); + return -1; + } + + key = keyfile ? : x509 ? + "/etc/keys/x509_evm.der" : + "/etc/keys/pubkey_evm.pem"; + + if (x509) + return ima_sig_import_v2(hash_algo, key, rawsigfile, file); + else { + log_err("Signature version 1 import not supported.\n"); + return 1; + } +} + static int verify_hash_v1(const unsigned char *hash, int size, unsigned char *sig, int siglen, const char *keyfile) { int err, len; @@ -1694,6 +1777,7 @@ struct command cmds[] = { #ifdef DEBUG {"hmac", cmd_hmac_evm, 0, "[--imahash | --imasig ] file", "Sign file metadata with HMAC using symmetric key (for testing purpose).\n"}, #endif + {"ima_sig_import", cmd_ima_sig_import, 0, "[--x509] [-a hashalgo] [-k pubkey] signature file", "Make IMA signature from externally signed raw signature blob.\n"}, {0, 0, 0, NULL} }; -- 1.8.3.1 |
From: Dmitry K. <dmi...@gm...> - 2013-07-16 19:38:55
|
On Tue, Jul 16, 2013 at 9:44 PM, Vivek Goyal <vg...@re...> wrote: > On Mon, Jul 15, 2013 at 10:20:00PM +0300, Dmitry Kasatkin wrote: > > [..] >> You might look again to the code and based on my explanation to think >> what can be changed. > > Ok, I understand. Can you please apply the other patch I sent so that > we are good with current assumptions. (signature version 2 will also > set x509=1 internally). > > I will think more about it and see if there are any good alternatives. > > Thanks > Vivek Hello, I will push them out tomorrow morning... Btw, what is your timezone? - Dmitry |
From: Vivek G. <vg...@re...> - 2013-07-16 18:44:14
|
On Mon, Jul 15, 2013 at 10:20:00PM +0300, Dmitry Kasatkin wrote: [..] > You might look again to the code and based on my explanation to think > what can be changed. Ok, I understand. Can you please apply the other patch I sent so that we are good with current assumptions. (signature version 2 will also set x509=1 internally). I will think more about it and see if there are any good alternatives. Thanks Vivek |
From: Dmitry K. <dmi...@gm...> - 2013-07-15 19:20:09
|
On Mon, Jul 15, 2013 at 8:08 PM, Vivek Goyal <vg...@re...> wrote: > On Mon, Jul 15, 2013 at 06:59:24PM +0300, Dmitry Kasatkin wrote: >> On Fri, Jul 12, 2013 at 9:52 PM, Vivek Goyal <vg...@re...> wrote: >> > Currently we assume signature version is v1 until and unless -x is >> > specified on kernel command line. Given the fact that signature version >> > information is available in signature itself, it is much better to get >> > it from there and not require user to pass -x during verification phase. >> > >> > If user passed -x on command line, then honor it. >> > >> > Now one can do following. >> > >> > evmctl ima_sign -x /tmp/data.txt >> > evmctl ima_verify /tmp/data.txt >> > >> > Signed-off-by: Vivek Goyal <vg...@re...> >> > --- >> > src/evmctl.c | 10 ++++++++++ >> > 1 files changed, 10 insertions(+), 0 deletions(-) >> > >> > diff --git a/src/evmctl.c b/src/evmctl.c >> > index 03a81ae..ca467c5 100644 >> > --- a/src/evmctl.c >> > +++ b/src/evmctl.c >> > @@ -255,6 +255,7 @@ static int sigfile; >> > static int modsig; >> > static char *uuid_str; >> > static int x509; >> > +static bool user_sig_type = false; >> > static char *keyfile; >> > >> > typedef int (*sign_hash_fn_t)(const char *algo, const unsigned char *hash, int size, const char *keyfile, unsigned char *sig); >> > @@ -1304,6 +1305,14 @@ static int verify_ima(const char *file, const char *key) >> > if (hashlen <= 1) >> > return hashlen; >> > >> > + /* Get signature type from sig header if user did not enforce it */ >> > + if (!user_sig_type) { >> > + if (sig[1] == DIGSIG_VERSION_1) >> > + verify_hash = verify_hash_v1; >> > + else if (sig[1] == DIGSIG_VERSION_2) >> > + verify_hash = verify_hash_v2; >> > + } >> > + >> > return verify_hash(hash, hashlen, sig + 1, len - 1, key); >> > } >> > >> > @@ -1716,6 +1725,7 @@ int main(int argc, char *argv[]) >> > x509 = 1; >> > sign_hash = sign_hash_v2; >> > verify_hash = verify_hash_v2; >> > + user_sig_type = true; >> >> Command line set x509 = 1, which will select "correct" public key/cert file... >> >> In the case of reading the signature version from the header, this >> fact is not addressed. >> >> Right? > > Hmm.., so if read signature version from header and say it is v2, but > we will still be reading public key from a key file and not x509 cert. > > That raises an important question. We seem to have tied key format ( > plain rsa key or x509 certificate) to signature version and that does > not seem to be right. > This is yes and no. Kernel key import code works with x509 certs in DER format only. Old format is using plain PEM public key. I wanted to make signing and verification '-x' in line with import '-x' option... so '-x' option forcing using x509, also forcing v2 and reverse is true as well... So, signing uses the same default key. Verification and import use different default keys, either plain PEM or DER x509 cert.. Well, it is possible to use still different formats for verification, but import must be in DER for v2... > One should be able to sign a file in V1 or V2 form using either RSA > key or using an x509 cert. They both contain the same info just that > certificate carries more metadata about the key. AFAIK, form of the > key does not enforce what kind of signature it is. > > So to me, -x should just say that we want to use an x509 cert for > signing but it should not enforce version of a signature. To clarify again.. signing does not use public key/cert... Verification uses either... > > And we should either deprecate V1 of signauture (if nobody is using > it ) or introduce a new option which specifies what kind of signature > we want to generate. > > Thoughts? > You might look again to the code and based on my explanation to think what can be changed. Thanks, Dmitry > Thanks > Vivek |
From: Vivek G. <vg...@re...> - 2013-07-15 18:12:22
|
On Mon, Jul 15, 2013 at 06:59:24PM +0300, Dmitry Kasatkin wrote: [..] > > @@ -1716,6 +1725,7 @@ int main(int argc, char *argv[]) > > x509 = 1; > > sign_hash = sign_hash_v2; > > verify_hash = verify_hash_v2; > > + user_sig_type = true; > > Command line set x509 = 1, which will select "correct" public key/cert file... > > In the case of reading the signature version from the header, this > fact is not addressed. > > Right? > Hi Dmitry, Please find inline the patch which will read x509 cert key if signature version is 2. This should apply on next branch. Thanks Vivek verify_ima(): Determine which key to use based on signature version First determine what is the signature version in signature file and then determine what key to use (RSA key in .pem format or x509 cert). For version 1 we assume that we will read RSA key in .pem format while for version 2 we assume that we will read x509 cert in der format. At some point of time we should break down the coupling between key format and signature version though. Signed-off-by: Vivek Goyal <vg...@re...> --- src/evmctl.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) Index: ima-evm-utils/src/evmctl.c =================================================================== --- ima-evm-utils.orig/src/evmctl.c 2013-07-15 13:48:01.416384052 -0400 +++ ima-evm-utils/src/evmctl.c 2013-07-15 14:01:52.607910232 -0400 @@ -1263,12 +1263,13 @@ static int get_hash_algo_from_sig(unsign return -1; } -static int verify_ima(const char *file, const char *key) +static int verify_ima(const char *file) { unsigned char hash[64]; unsigned char sig[1024]; int len, hashlen; int sig_hash_algo; + char *key; if (xattr) { len = getxattr(file, "security.ima", sig, sizeof(sig)); @@ -1310,16 +1311,24 @@ static int verify_ima(const char *file, if (!user_sig_type) { if (sig[1] == DIGSIG_VERSION_1) verify_hash = verify_hash_v1; - else if (sig[1] == DIGSIG_VERSION_2) + else if (sig[1] == DIGSIG_VERSION_2) { verify_hash = verify_hash_v2; + /* Read pubkey from x509 cert */ + x509 = 1; + } } + /* Determien what key to use for verification*/ + key = keyfile ? : x509 ? + "/etc/keys/x509_evm.der" : + "/etc/keys/pubkey_evm.pem"; + return verify_hash(hash, hashlen, sig + 1, len - 1, key); } static int cmd_verify_ima(struct command *cmd) { - char *key, *file = g_argv[optind++]; + char *file = g_argv[optind++]; if (!file) { log_err("Parameters missing\n"); @@ -1327,11 +1336,7 @@ static int cmd_verify_ima(struct command return -1; } - key = keyfile ? : x509 ? - "/etc/keys/x509_evm.der" : - "/etc/keys/pubkey_evm.pem"; - - return verify_ima(file, key); + return verify_ima(file); } static int cmd_import(struct command *cmd) |
From: Vivek G. <vg...@re...> - 2013-07-15 18:10:43
|
On Mon, Jul 15, 2013 at 06:56:17PM +0300, Dmitry Kasatkin wrote: > On Fri, Jul 12, 2013 at 9:52 PM, Vivek Goyal <vg...@re...> wrote: > > Using macros for fixed values looks cleaner. Also I am planning to use > > version field in more places in next patch. So use macros intead of > > numbers like 1 and 2. > > > > Signed-off-by: Vivek Goyal <vg...@re...> > > --- > > src/evmctl.c | 7 +++++-- > > 1 files changed, 5 insertions(+), 2 deletions(-) > > > > diff --git a/src/evmctl.c b/src/evmctl.c > > index 08b2b81..03a81ae 100644 > > --- a/src/evmctl.c > > +++ b/src/evmctl.c > > @@ -90,6 +90,9 @@ > > #define FS_IOC32_GETFLAGS _IOR('f', 1, int) > > #define FS_IOC32_SETFLAGS _IOW('f', 2, int) > > > > +#define DIGSIG_VERSION_1 1 > > +#define DIGSIG_VERSION_2 2 > > + > > Why not enums? Hi Dmitry, Please find attached patch to use enum instead of macros. This should apply on top of next branch. Thanks Vivek evmctl: Use enum for signature version instead of macros Dmitry prefers using enums over macros. Signed-off-by: Vivek Goyal <vg...@re...> --- src/evmctl.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) Index: ima-evm-utils/src/evmctl.c =================================================================== --- ima-evm-utils.orig/src/evmctl.c 2013-07-15 13:47:57.707341605 -0400 +++ ima-evm-utils/src/evmctl.c 2013-07-15 13:48:01.416384052 -0400 @@ -90,9 +90,6 @@ #define FS_IOC32_GETFLAGS _IOR('f', 1, int) #define FS_IOC32_SETFLAGS _IOW('f', 2, int) -#define DIGSIG_VERSION_1 1 -#define DIGSIG_VERSION_2 2 - struct h_misc { unsigned long ino; uint32_t generation; @@ -112,6 +109,11 @@ enum digest_algo { DIGEST_ALGO_MAX }; +enum digsig_version { + DIGSIG_VERSION_1 = 1, + DIGSIG_VERSION_2 +}; + struct pubkey_hdr { uint8_t version; /* key format version */ uint32_t timestamp; /* key made, always 0 for now */ |
From: Vivek G. <vg...@re...> - 2013-07-15 17:08:35
|
On Mon, Jul 15, 2013 at 06:59:24PM +0300, Dmitry Kasatkin wrote: > On Fri, Jul 12, 2013 at 9:52 PM, Vivek Goyal <vg...@re...> wrote: > > Currently we assume signature version is v1 until and unless -x is > > specified on kernel command line. Given the fact that signature version > > information is available in signature itself, it is much better to get > > it from there and not require user to pass -x during verification phase. > > > > If user passed -x on command line, then honor it. > > > > Now one can do following. > > > > evmctl ima_sign -x /tmp/data.txt > > evmctl ima_verify /tmp/data.txt > > > > Signed-off-by: Vivek Goyal <vg...@re...> > > --- > > src/evmctl.c | 10 ++++++++++ > > 1 files changed, 10 insertions(+), 0 deletions(-) > > > > diff --git a/src/evmctl.c b/src/evmctl.c > > index 03a81ae..ca467c5 100644 > > --- a/src/evmctl.c > > +++ b/src/evmctl.c > > @@ -255,6 +255,7 @@ static int sigfile; > > static int modsig; > > static char *uuid_str; > > static int x509; > > +static bool user_sig_type = false; > > static char *keyfile; > > > > typedef int (*sign_hash_fn_t)(const char *algo, const unsigned char *hash, int size, const char *keyfile, unsigned char *sig); > > @@ -1304,6 +1305,14 @@ static int verify_ima(const char *file, const char *key) > > if (hashlen <= 1) > > return hashlen; > > > > + /* Get signature type from sig header if user did not enforce it */ > > + if (!user_sig_type) { > > + if (sig[1] == DIGSIG_VERSION_1) > > + verify_hash = verify_hash_v1; > > + else if (sig[1] == DIGSIG_VERSION_2) > > + verify_hash = verify_hash_v2; > > + } > > + > > return verify_hash(hash, hashlen, sig + 1, len - 1, key); > > } > > > > @@ -1716,6 +1725,7 @@ int main(int argc, char *argv[]) > > x509 = 1; > > sign_hash = sign_hash_v2; > > verify_hash = verify_hash_v2; > > + user_sig_type = true; > > Command line set x509 = 1, which will select "correct" public key/cert file... > > In the case of reading the signature version from the header, this > fact is not addressed. > > Right? Hmm.., so if read signature version from header and say it is v2, but we will still be reading public key from a key file and not x509 cert. That raises an important question. We seem to have tied key format ( plain rsa key or x509 certificate) to signature version and that does not seem to be right. One should be able to sign a file in V1 or V2 form using either RSA key or using an x509 cert. They both contain the same info just that certificate carries more metadata about the key. AFAIK, form of the key does not enforce what kind of signature it is. So to me, -x should just say that we want to use an x509 cert for signing but it should not enforce version of a signature. And we should either deprecate V1 of signauture (if nobody is using it ) or introduce a new option which specifies what kind of signature we want to generate. Thoughts? Thanks Vivek |
From: Vivek G. <vg...@re...> - 2013-07-15 16:57:02
|
On Mon, Jul 15, 2013 at 06:56:17PM +0300, Dmitry Kasatkin wrote: > On Fri, Jul 12, 2013 at 9:52 PM, Vivek Goyal <vg...@re...> wrote: > > Using macros for fixed values looks cleaner. Also I am planning to use > > version field in more places in next patch. So use macros intead of > > numbers like 1 and 2. > > > > Signed-off-by: Vivek Goyal <vg...@re...> > > --- > > src/evmctl.c | 7 +++++-- > > 1 files changed, 5 insertions(+), 2 deletions(-) > > > > diff --git a/src/evmctl.c b/src/evmctl.c > > index 08b2b81..03a81ae 100644 > > --- a/src/evmctl.c > > +++ b/src/evmctl.c > > @@ -90,6 +90,9 @@ > > #define FS_IOC32_GETFLAGS _IOR('f', 1, int) > > #define FS_IOC32_SETFLAGS _IOW('f', 2, int) > > > > +#define DIGSIG_VERSION_1 1 > > +#define DIGSIG_VERSION_2 2 > > + > > Why not enums? enums are fine too. Didn't think about that. Thanks Vivek |
From: Dmitry K. <dmi...@gm...> - 2013-07-15 16:43:52
|
On Wed, Jul 10, 2013 at 10:57 PM, Vivek Goyal <vg...@re...> wrote: > On Wed, Jul 10, 2013 at 04:58:57PM +0100, Dmitry Kasatkin wrote: >> Hello, >> >> Done and committed. >> Also one more back is fixed when verifying signature from .sig files. >> > > Thanks Dmitry. > > Also looked at other fix for verifying signature from a file. I was > wondering that why don't we dump whole of security.ima attribute when > sigfile is specified and we just dump the signature. > > If we dump the whole security.ima xattr, then that would act like a > detached signature and can be used to install later using setfattr. > We can use this functionality to sign a file at build time and install > signature at rpm installation time. > > That way verify_ima does not have to assume that it is a digital > signature > > Anyway, signautre format is specific to ima and no other tool can > understand it. So creating a detached signature which contains whole of > the security.ima xattr makes sense to me. > > What do you think? > Initially it was like that. But then it was changed for module signature passing long time ago with module-init-tools. I do not remember details now. We can revert this behavior now.. Thanks, Dmitry > Thanks > Vivek |
From: Dmitry K. <dmi...@gm...> - 2013-07-15 15:59:32
|
On Fri, Jul 12, 2013 at 9:52 PM, Vivek Goyal <vg...@re...> wrote: > Currently we assume signature version is v1 until and unless -x is > specified on kernel command line. Given the fact that signature version > information is available in signature itself, it is much better to get > it from there and not require user to pass -x during verification phase. > > If user passed -x on command line, then honor it. > > Now one can do following. > > evmctl ima_sign -x /tmp/data.txt > evmctl ima_verify /tmp/data.txt > > Signed-off-by: Vivek Goyal <vg...@re...> > --- > src/evmctl.c | 10 ++++++++++ > 1 files changed, 10 insertions(+), 0 deletions(-) > > diff --git a/src/evmctl.c b/src/evmctl.c > index 03a81ae..ca467c5 100644 > --- a/src/evmctl.c > +++ b/src/evmctl.c > @@ -255,6 +255,7 @@ static int sigfile; > static int modsig; > static char *uuid_str; > static int x509; > +static bool user_sig_type = false; > static char *keyfile; > > typedef int (*sign_hash_fn_t)(const char *algo, const unsigned char *hash, int size, const char *keyfile, unsigned char *sig); > @@ -1304,6 +1305,14 @@ static int verify_ima(const char *file, const char *key) > if (hashlen <= 1) > return hashlen; > > + /* Get signature type from sig header if user did not enforce it */ > + if (!user_sig_type) { > + if (sig[1] == DIGSIG_VERSION_1) > + verify_hash = verify_hash_v1; > + else if (sig[1] == DIGSIG_VERSION_2) > + verify_hash = verify_hash_v2; > + } > + > return verify_hash(hash, hashlen, sig + 1, len - 1, key); > } > > @@ -1716,6 +1725,7 @@ int main(int argc, char *argv[]) > x509 = 1; > sign_hash = sign_hash_v2; > verify_hash = verify_hash_v2; > + user_sig_type = true; Command line set x509 = 1, which will select "correct" public key/cert file... In the case of reading the signature version from the header, this fact is not addressed. Right? - Dmitry > break; > case 'k': > keyfile = optarg; > -- > 1.7.7.6 > |
From: Dmitry K. <dmi...@gm...> - 2013-07-15 15:56:25
|
On Fri, Jul 12, 2013 at 9:52 PM, Vivek Goyal <vg...@re...> wrote: > Using macros for fixed values looks cleaner. Also I am planning to use > version field in more places in next patch. So use macros intead of > numbers like 1 and 2. > > Signed-off-by: Vivek Goyal <vg...@re...> > --- > src/evmctl.c | 7 +++++-- > 1 files changed, 5 insertions(+), 2 deletions(-) > > diff --git a/src/evmctl.c b/src/evmctl.c > index 08b2b81..03a81ae 100644 > --- a/src/evmctl.c > +++ b/src/evmctl.c > @@ -90,6 +90,9 @@ > #define FS_IOC32_GETFLAGS _IOR('f', 1, int) > #define FS_IOC32_SETFLAGS _IOW('f', 2, int) > > +#define DIGSIG_VERSION_1 1 > +#define DIGSIG_VERSION_2 2 > + Why not enums? > struct h_misc { > unsigned long ino; > uint32_t generation; > @@ -508,7 +511,7 @@ static int sign_hash_v1(const char *hashalgo, const unsigned char *hash, int siz > return -1; > > /* now create a new hash */ > - hdr->version = 1; > + hdr->version = (uint8_t) DIGSIG_VERSION_1; > hdr->timestamp = time(NULL); > hdr->algo = PUBKEY_ALGO_RSA; > hashalgo_idx = get_hash_algo_v1(hashalgo); > @@ -578,7 +581,7 @@ static int sign_hash_v2(const char *algo, const unsigned char *hash, int size, c > if (!key) > return -1; > > - hdr->version = 2; > + hdr->version = (uint8_t) DIGSIG_VERSION_2; > hdr->hash_algo = get_hash_algo(algo); > > calc_keyid_v2(&hdr->keyid, name, key); > -- > 1.7.7.6 > |
From: Dmitry K. <dmi...@gm...> - 2013-07-15 15:55:31
|
Hi Vivek, Thanks for address extensions in your patches... On Fri, Jul 12, 2013 at 9:52 PM, Vivek Goyal <vg...@re...> wrote: > Hi Dmitry, > > This is the series of miscellaneous fixes to evmctl. I think these fixes > make life little easier for the user and get rid of some of the confusion. > These also allow dealing with detached signatures easily. > > Can you please have a look. > I put your new patches with modified patch commits here to "next" branch: https://sourceforge.net/p/linux-ima/ima-evm-utils/ci/next/tree/ SourceForge GIT browser is tremendously awful.. So it is better to clone from repo and use gitk or gitg I have also one issue I noticed.. - Dmitry > Thanks > Vivek > > Vivek Goyal (7): > evmctl: Fix hash array size in verify_ima() > evmctl: Put right hash algo info in signature digital signature > version 1 header > ima_verify(): Get hash algorithm info from signature > ima_verify(): Let user specified hash algo take precedence > evmctl: Use macros for signature versions > ima_verify(): Get signatuer version from header > evmctl: Save full security.ima attribute to a file > > src/evmctl.c | 104 +++++++++++++++++++++++++++++++++++++++++++++++++--------- > 1 files changed, 88 insertions(+), 16 deletions(-) > > -- > 1.7.7.6 > |