Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security

Pull security subsystem updates from James Morris.

Mostly ima, selinux, smack and key handling updates.

* 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security: (65 commits)
  integrity: do zero padding of the key id
  KEYS: output last portion of fingerprint in /proc/keys
  KEYS: strip 'id:' from ca_keyid
  KEYS: use swapped SKID for performing partial matching
  KEYS: Restore partial ID matching functionality for asymmetric keys
  X.509: If available, use the raw subjKeyId to form the key description
  KEYS: handle error code encoded in pointer
  selinux: normalize audit log formatting
  selinux: cleanup error reporting in selinux_nlmsg_perm()
  KEYS: Check hex2bin()'s return when generating an asymmetric key ID
  ima: detect violations for mmaped files
  ima: fix race condition on ima_rdwr_violation_check and process_measurement
  ima: added ima_policy_flag variable
  ima: return an error code from ima_add_boot_aggregate()
  ima: provide 'ima_appraise=log' kernel option
  ima: move keyring initialization to ima_init()
  PKCS#7: Handle PKCS#7 messages that contain no X.509 certs
  PKCS#7: Better handling of unsupported crypto
  KEYS: Overhaul key identification when searching for asymmetric keys
  KEYS: Implement binary asymmetric key ID handling
  ...
This commit is contained in:
Linus Torvalds 2014-10-12 10:13:55 -04:00
Родитель d0ca47575a 594081ee71
Коммит 5e40d331bd
67 изменённых файлов: 1606 добавлений и 878 удалений

Просмотреть файл

@ -1323,7 +1323,7 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
Set number of hash buckets for inode cache. Set number of hash buckets for inode cache.
ima_appraise= [IMA] appraise integrity measurements ima_appraise= [IMA] appraise integrity measurements
Format: { "off" | "enforce" | "fix" } Format: { "off" | "enforce" | "fix" | "log" }
default: "enforce" default: "enforce"
ima_appraise_tcb [IMA] ima_appraise_tcb [IMA]

Просмотреть файл

@ -888,11 +888,11 @@ payload contents" for more information.
const char *callout_info); const char *callout_info);
This is used to request a key or keyring with a description that matches This is used to request a key or keyring with a description that matches
the description specified according to the key type's match function. This the description specified according to the key type's match_preparse()
permits approximate matching to occur. If callout_string is not NULL, then method. This permits approximate matching to occur. If callout_string is
/sbin/request-key will be invoked in an attempt to obtain the key from not NULL, then /sbin/request-key will be invoked in an attempt to obtain
userspace. In that case, callout_string will be passed as an argument to the key from userspace. In that case, callout_string will be passed as an
the program. argument to the program.
Should the function fail error ENOKEY, EKEYEXPIRED or EKEYREVOKED will be Should the function fail error ENOKEY, EKEYEXPIRED or EKEYREVOKED will be
returned. returned.
@ -1170,7 +1170,7 @@ The structure has a number of fields, some of which are mandatory:
The method should return 0 if successful or a negative error code The method should return 0 if successful or a negative error code
otherwise. otherwise.
(*) void (*free_preparse)(struct key_preparsed_payload *prep); (*) void (*free_preparse)(struct key_preparsed_payload *prep);
This method is only required if the preparse() method is provided, This method is only required if the preparse() method is provided,
@ -1225,16 +1225,55 @@ The structure has a number of fields, some of which are mandatory:
It is safe to sleep in this method. It is safe to sleep in this method.
(*) int (*match)(const struct key *key, const void *desc); (*) int (*match_preparse)(struct key_match_data *match_data);
This method is called to match a key against a description. It should This method is optional. It is called when a key search is about to be
return non-zero if the two match, zero if they don't. performed. It is given the following structure:
This method should not need to lock the key in any way. The type and struct key_match_data {
description can be considered invariant, and the payload should not be bool (*cmp)(const struct key *key,
accessed (the key may not yet be instantiated). const struct key_match_data *match_data);
const void *raw_data;
void *preparsed;
unsigned lookup_type;
};
It is not safe to sleep in this method; the caller may hold spinlocks. On entry, raw_data will be pointing to the criteria to be used in matching
a key by the caller and should not be modified. (*cmp)() will be pointing
to the default matcher function (which does an exact description match
against raw_data) and lookup_type will be set to indicate a direct lookup.
The following lookup_type values are available:
[*] KEYRING_SEARCH_LOOKUP_DIRECT - A direct lookup hashes the type and
description to narrow down the search to a small number of keys.
[*] KEYRING_SEARCH_LOOKUP_ITERATE - An iterative lookup walks all the
keys in the keyring until one is matched. This must be used for any
search that's not doing a simple direct match on the key description.
The method may set cmp to point to a function of its choice that does some
other form of match, may set lookup_type to KEYRING_SEARCH_LOOKUP_ITERATE
and may attach something to the preparsed pointer for use by (*cmp)().
(*cmp)() should return true if a key matches and false otherwise.
If preparsed is set, it may be necessary to use the match_free() method to
clean it up.
The method should return 0 if successful or a negative error code
otherwise.
It is permitted to sleep in this method, but (*cmp)() may not sleep as
locks will be held over it.
If match_preparse() is not provided, keys of this type will be matched
exactly by their description.
(*) void (*match_free)(struct key_match_data *match_data);
This method is optional. If given, it called to clean up
match_data->preparsed after a successful call to match_preparse().
(*) void (*revoke)(struct key *key); (*) void (*revoke)(struct key *key);

Просмотреть файл

@ -8198,6 +8198,8 @@ F: drivers/mmc/host/sdhci-pltfm.[ch]
SECURE COMPUTING SECURE COMPUTING
M: Kees Cook <keescook@chromium.org> M: Kees Cook <keescook@chromium.org>
R: Andy Lutomirski <luto@amacapital.net>
R: Will Drewry <wad@chromium.org>
T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git seccomp T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git seccomp
S: Supported S: Supported
F: kernel/seccomp.c F: kernel/seccomp.c

Просмотреть файл

@ -9,9 +9,10 @@
* 2 of the Licence, or (at your option) any later version. * 2 of the Licence, or (at your option) any later version.
*/ */
int asymmetric_keyid_match(const char *kid, const char *id); extern struct asymmetric_key_id *asymmetric_key_hex_to_key_id(const char *id);
static inline const char *asymmetric_key_id(const struct key *key) static inline
const struct asymmetric_key_ids *asymmetric_key_ids(const struct key *key)
{ {
return key->type_data.p[1]; return key->type_data.p[1];
} }

Просмотреть файл

@ -15,6 +15,7 @@
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/ctype.h>
#include "asymmetric_keys.h" #include "asymmetric_keys.h"
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
@ -22,75 +23,197 @@ MODULE_LICENSE("GPL");
static LIST_HEAD(asymmetric_key_parsers); static LIST_HEAD(asymmetric_key_parsers);
static DECLARE_RWSEM(asymmetric_key_parsers_sem); static DECLARE_RWSEM(asymmetric_key_parsers_sem);
/* /**
* Match asymmetric key id with partial match * asymmetric_key_generate_id: Construct an asymmetric key ID
* @id: key id to match in a form "id:<id>" * @val_1: First binary blob
*/ * @len_1: Length of first binary blob
int asymmetric_keyid_match(const char *kid, const char *id) * @val_2: Second binary blob
{ * @len_2: Length of second binary blob
size_t idlen, kidlen;
if (!kid || !id)
return 0;
/* make it possible to use id as in the request: "id:<id>" */
if (strncmp(id, "id:", 3) == 0)
id += 3;
/* Anything after here requires a partial match on the ID string */
idlen = strlen(id);
kidlen = strlen(kid);
if (idlen > kidlen)
return 0;
kid += kidlen - idlen;
if (strcasecmp(id, kid) != 0)
return 0;
return 1;
}
EXPORT_SYMBOL_GPL(asymmetric_keyid_match);
/*
* Match asymmetric keys on (part of) their name
* We have some shorthand methods for matching keys. We allow:
* *
* "<desc>" - request a key by description * Construct an asymmetric key ID from a pair of binary blobs.
* "id:<id>" - request a key matching the ID
* "<subtype>:<id>" - request a key of a subtype
*/ */
static int asymmetric_key_match(const struct key *key, const void *description) struct asymmetric_key_id *asymmetric_key_generate_id(const void *val_1,
size_t len_1,
const void *val_2,
size_t len_2)
{ {
const struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key); struct asymmetric_key_id *kid;
const char *spec = description;
kid = kmalloc(sizeof(struct asymmetric_key_id) + len_1 + len_2,
GFP_KERNEL);
if (!kid)
return ERR_PTR(-ENOMEM);
kid->len = len_1 + len_2;
memcpy(kid->data, val_1, len_1);
memcpy(kid->data + len_1, val_2, len_2);
return kid;
}
EXPORT_SYMBOL_GPL(asymmetric_key_generate_id);
/**
* asymmetric_key_id_same - Return true if two asymmetric keys IDs are the same.
* @kid_1, @kid_2: The key IDs to compare
*/
bool asymmetric_key_id_same(const struct asymmetric_key_id *kid1,
const struct asymmetric_key_id *kid2)
{
if (!kid1 || !kid2)
return false;
if (kid1->len != kid2->len)
return false;
return memcmp(kid1->data, kid2->data, kid1->len) == 0;
}
EXPORT_SYMBOL_GPL(asymmetric_key_id_same);
/**
* asymmetric_key_id_partial - Return true if two asymmetric keys IDs
* partially match
* @kid_1, @kid_2: The key IDs to compare
*/
bool asymmetric_key_id_partial(const struct asymmetric_key_id *kid1,
const struct asymmetric_key_id *kid2)
{
if (!kid1 || !kid2)
return false;
if (kid1->len < kid2->len)
return false;
return memcmp(kid1->data + (kid1->len - kid2->len),
kid2->data, kid2->len) == 0;
}
EXPORT_SYMBOL_GPL(asymmetric_key_id_partial);
/**
* asymmetric_match_key_ids - Search asymmetric key IDs
* @kids: The list of key IDs to check
* @match_id: The key ID we're looking for
* @match: The match function to use
*/
static bool asymmetric_match_key_ids(
const struct asymmetric_key_ids *kids,
const struct asymmetric_key_id *match_id,
bool (*match)(const struct asymmetric_key_id *kid1,
const struct asymmetric_key_id *kid2))
{
int i;
if (!kids || !match_id)
return false;
for (i = 0; i < ARRAY_SIZE(kids->id); i++)
if (match(kids->id[i], match_id))
return true;
return false;
}
/**
* asymmetric_key_hex_to_key_id - Convert a hex string into a key ID.
* @id: The ID as a hex string.
*/
struct asymmetric_key_id *asymmetric_key_hex_to_key_id(const char *id)
{
struct asymmetric_key_id *match_id;
size_t hexlen;
int ret;
if (!*id)
return ERR_PTR(-EINVAL);
hexlen = strlen(id);
if (hexlen & 1)
return ERR_PTR(-EINVAL);
match_id = kmalloc(sizeof(struct asymmetric_key_id) + hexlen / 2,
GFP_KERNEL);
if (!match_id)
return ERR_PTR(-ENOMEM);
match_id->len = hexlen / 2;
ret = hex2bin(match_id->data, id, hexlen / 2);
if (ret < 0) {
kfree(match_id);
return ERR_PTR(-EINVAL);
}
return match_id;
}
/*
* Match asymmetric keys by an exact match on an ID.
*/
static bool asymmetric_key_cmp(const struct key *key,
const struct key_match_data *match_data)
{
const struct asymmetric_key_ids *kids = asymmetric_key_ids(key);
const struct asymmetric_key_id *match_id = match_data->preparsed;
return asymmetric_match_key_ids(kids, match_id,
asymmetric_key_id_same);
}
/*
* Match asymmetric keys by a partial match on an IDs.
*/
static bool asymmetric_key_cmp_partial(const struct key *key,
const struct key_match_data *match_data)
{
const struct asymmetric_key_ids *kids = asymmetric_key_ids(key);
const struct asymmetric_key_id *match_id = match_data->preparsed;
return asymmetric_match_key_ids(kids, match_id,
asymmetric_key_id_partial);
}
/*
* Preparse the match criterion. If we don't set lookup_type and cmp,
* the default will be an exact match on the key description.
*
* There are some specifiers for matching key IDs rather than by the key
* description:
*
* "id:<id>" - find a key by partial match on any available ID
* "ex:<id>" - find a key by exact match on any available ID
*
* These have to be searched by iteration rather than by direct lookup because
* the key is hashed according to its description.
*/
static int asymmetric_key_match_preparse(struct key_match_data *match_data)
{
struct asymmetric_key_id *match_id;
const char *spec = match_data->raw_data;
const char *id; const char *id;
ptrdiff_t speclen; bool (*cmp)(const struct key *, const struct key_match_data *) =
asymmetric_key_cmp;
if (!subtype || !spec || !*spec) if (!spec || !*spec)
return 0; return -EINVAL;
if (spec[0] == 'i' &&
spec[1] == 'd' &&
spec[2] == ':') {
id = spec + 3;
cmp = asymmetric_key_cmp_partial;
} else if (spec[0] == 'e' &&
spec[1] == 'x' &&
spec[2] == ':') {
id = spec + 3;
} else {
goto default_match;
}
/* See if the full key description matches as is */ match_id = asymmetric_key_hex_to_key_id(id);
if (key->description && strcmp(key->description, description) == 0) if (IS_ERR(match_id))
return 1; return PTR_ERR(match_id);
/* All tests from here on break the criterion description into a
* specifier, a colon and then an identifier.
*/
id = strchr(spec, ':');
if (!id)
return 0;
speclen = id - spec;
id++;
if (speclen == 2 && memcmp(spec, "id", 2) == 0)
return asymmetric_keyid_match(asymmetric_key_id(key), id);
if (speclen == subtype->name_len &&
memcmp(spec, subtype->name, speclen) == 0)
return 1;
match_data->preparsed = match_id;
match_data->cmp = cmp;
match_data->lookup_type = KEYRING_SEARCH_LOOKUP_ITERATE;
return 0; return 0;
default_match:
return 0;
}
/*
* Free the preparsed the match criterion.
*/
static void asymmetric_key_match_free(struct key_match_data *match_data)
{
kfree(match_data->preparsed);
} }
/* /*
@ -99,8 +222,10 @@ static int asymmetric_key_match(const struct key *key, const void *description)
static void asymmetric_key_describe(const struct key *key, struct seq_file *m) static void asymmetric_key_describe(const struct key *key, struct seq_file *m)
{ {
const struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key); const struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key);
const char *kid = asymmetric_key_id(key); const struct asymmetric_key_ids *kids = asymmetric_key_ids(key);
size_t n; const struct asymmetric_key_id *kid;
const unsigned char *p;
int n;
seq_puts(m, key->description); seq_puts(m, key->description);
@ -108,13 +233,16 @@ static void asymmetric_key_describe(const struct key *key, struct seq_file *m)
seq_puts(m, ": "); seq_puts(m, ": ");
subtype->describe(key, m); subtype->describe(key, m);
if (kid) { if (kids && kids->id[1]) {
kid = kids->id[1];
seq_putc(m, ' '); seq_putc(m, ' ');
n = strlen(kid); n = kid->len;
if (n <= 8) p = kid->data;
seq_puts(m, kid); if (n > 4) {
else p += n - 4;
seq_puts(m, kid + n - 8); n = 4;
}
seq_printf(m, "%*phN", n, p);
} }
seq_puts(m, " ["); seq_puts(m, " [");
@ -165,6 +293,8 @@ static int asymmetric_key_preparse(struct key_preparsed_payload *prep)
static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep) static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep)
{ {
struct asymmetric_key_subtype *subtype = prep->type_data[0]; struct asymmetric_key_subtype *subtype = prep->type_data[0];
struct asymmetric_key_ids *kids = prep->type_data[1];
int i;
pr_devel("==>%s()\n", __func__); pr_devel("==>%s()\n", __func__);
@ -172,7 +302,11 @@ static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep)
subtype->destroy(prep->payload[0]); subtype->destroy(prep->payload[0]);
module_put(subtype->owner); module_put(subtype->owner);
} }
kfree(prep->type_data[1]); if (kids) {
for (i = 0; i < ARRAY_SIZE(kids->id); i++)
kfree(kids->id[i]);
kfree(kids);
}
kfree(prep->description); kfree(prep->description);
} }
@ -182,13 +316,20 @@ static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep)
static void asymmetric_key_destroy(struct key *key) static void asymmetric_key_destroy(struct key *key)
{ {
struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key); struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key);
struct asymmetric_key_ids *kids = key->type_data.p[1];
if (subtype) { if (subtype) {
subtype->destroy(key->payload.data); subtype->destroy(key->payload.data);
module_put(subtype->owner); module_put(subtype->owner);
key->type_data.p[0] = NULL; key->type_data.p[0] = NULL;
} }
kfree(key->type_data.p[1]);
key->type_data.p[1] = NULL; if (kids) {
kfree(kids->id[0]);
kfree(kids->id[1]);
kfree(kids);
key->type_data.p[1] = NULL;
}
} }
struct key_type key_type_asymmetric = { struct key_type key_type_asymmetric = {
@ -196,10 +337,10 @@ struct key_type key_type_asymmetric = {
.preparse = asymmetric_key_preparse, .preparse = asymmetric_key_preparse,
.free_preparse = asymmetric_key_free_preparse, .free_preparse = asymmetric_key_free_preparse,
.instantiate = generic_key_instantiate, .instantiate = generic_key_instantiate,
.match = asymmetric_key_match, .match_preparse = asymmetric_key_match_preparse,
.match_free = asymmetric_key_match_free,
.destroy = asymmetric_key_destroy, .destroy = asymmetric_key_destroy,
.describe = asymmetric_key_describe, .describe = asymmetric_key_describe,
.def_lookup_type = KEYRING_SEARCH_LOOKUP_ITERATE,
}; };
EXPORT_SYMBOL_GPL(key_type_asymmetric); EXPORT_SYMBOL_GPL(key_type_asymmetric);

Просмотреть файл

@ -72,11 +72,9 @@ error:
*/ */
static struct key_type key_type_pkcs7 = { static struct key_type key_type_pkcs7 = {
.name = "pkcs7_test", .name = "pkcs7_test",
.def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
.preparse = pkcs7_preparse, .preparse = pkcs7_preparse,
.free_preparse = user_free_preparse, .free_preparse = user_free_preparse,
.instantiate = generic_key_instantiate, .instantiate = generic_key_instantiate,
.match = user_match,
.revoke = user_revoke, .revoke = user_revoke,
.destroy = user_destroy, .destroy = user_destroy,
.describe = user_describe, .describe = user_describe,

Просмотреть файл

@ -29,8 +29,25 @@ struct pkcs7_parse_context {
enum OID last_oid; /* Last OID encountered */ enum OID last_oid; /* Last OID encountered */
unsigned x509_index; unsigned x509_index;
unsigned sinfo_index; unsigned sinfo_index;
const void *raw_serial;
unsigned raw_serial_size;
unsigned raw_issuer_size;
const void *raw_issuer;
}; };
/*
* Free a signed information block.
*/
static void pkcs7_free_signed_info(struct pkcs7_signed_info *sinfo)
{
if (sinfo) {
mpi_free(sinfo->sig.mpi[0]);
kfree(sinfo->sig.digest);
kfree(sinfo->signing_cert_id);
kfree(sinfo);
}
}
/** /**
* pkcs7_free_message - Free a PKCS#7 message * pkcs7_free_message - Free a PKCS#7 message
* @pkcs7: The PKCS#7 message to free * @pkcs7: The PKCS#7 message to free
@ -54,9 +71,7 @@ void pkcs7_free_message(struct pkcs7_message *pkcs7)
while (pkcs7->signed_infos) { while (pkcs7->signed_infos) {
sinfo = pkcs7->signed_infos; sinfo = pkcs7->signed_infos;
pkcs7->signed_infos = sinfo->next; pkcs7->signed_infos = sinfo->next;
mpi_free(sinfo->sig.mpi[0]); pkcs7_free_signed_info(sinfo);
kfree(sinfo->sig.digest);
kfree(sinfo);
} }
kfree(pkcs7); kfree(pkcs7);
} }
@ -71,51 +86,46 @@ EXPORT_SYMBOL_GPL(pkcs7_free_message);
struct pkcs7_message *pkcs7_parse_message(const void *data, size_t datalen) struct pkcs7_message *pkcs7_parse_message(const void *data, size_t datalen)
{ {
struct pkcs7_parse_context *ctx; struct pkcs7_parse_context *ctx;
struct pkcs7_message *msg; struct pkcs7_message *msg = ERR_PTR(-ENOMEM);
long ret; int ret;
ret = -ENOMEM;
msg = kzalloc(sizeof(struct pkcs7_message), GFP_KERNEL);
if (!msg)
goto error_no_sig;
ctx = kzalloc(sizeof(struct pkcs7_parse_context), GFP_KERNEL); ctx = kzalloc(sizeof(struct pkcs7_parse_context), GFP_KERNEL);
if (!ctx) if (!ctx)
goto error_no_ctx; goto out_no_ctx;
ctx->msg = kzalloc(sizeof(struct pkcs7_message), GFP_KERNEL);
if (!ctx->msg)
goto out_no_msg;
ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL); ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL);
if (!ctx->sinfo) if (!ctx->sinfo)
goto error_no_sinfo; goto out_no_sinfo;
ctx->msg = msg;
ctx->data = (unsigned long)data; ctx->data = (unsigned long)data;
ctx->ppcerts = &ctx->certs; ctx->ppcerts = &ctx->certs;
ctx->ppsinfo = &ctx->msg->signed_infos; ctx->ppsinfo = &ctx->msg->signed_infos;
/* Attempt to decode the signature */ /* Attempt to decode the signature */
ret = asn1_ber_decoder(&pkcs7_decoder, ctx, data, datalen); ret = asn1_ber_decoder(&pkcs7_decoder, ctx, data, datalen);
if (ret < 0) if (ret < 0) {
goto error_decode; msg = ERR_PTR(ret);
goto out;
}
msg = ctx->msg;
ctx->msg = NULL;
out:
while (ctx->certs) { while (ctx->certs) {
struct x509_certificate *cert = ctx->certs; struct x509_certificate *cert = ctx->certs;
ctx->certs = cert->next; ctx->certs = cert->next;
x509_free_certificate(cert); x509_free_certificate(cert);
} }
mpi_free(ctx->sinfo->sig.mpi[0]); pkcs7_free_signed_info(ctx->sinfo);
kfree(ctx->sinfo->sig.digest); out_no_sinfo:
kfree(ctx->sinfo); pkcs7_free_message(ctx->msg);
out_no_msg:
kfree(ctx); kfree(ctx);
out_no_ctx:
return msg; return msg;
error_decode:
mpi_free(ctx->sinfo->sig.mpi[0]);
kfree(ctx->sinfo->sig.digest);
kfree(ctx->sinfo);
error_no_sinfo:
kfree(ctx);
error_no_ctx:
pkcs7_free_message(msg);
error_no_sig:
return ERR_PTR(ret);
} }
EXPORT_SYMBOL_GPL(pkcs7_parse_message); EXPORT_SYMBOL_GPL(pkcs7_parse_message);
@ -246,10 +256,10 @@ int pkcs7_extract_cert(void *context, size_t hdrlen,
if (IS_ERR(x509)) if (IS_ERR(x509))
return PTR_ERR(x509); return PTR_ERR(x509);
pr_debug("Got cert for %s\n", x509->subject);
pr_debug("- fingerprint %s\n", x509->fingerprint);
x509->index = ++ctx->x509_index; x509->index = ++ctx->x509_index;
pr_debug("Got cert %u for %s\n", x509->index, x509->subject);
pr_debug("- fingerprint %*phN\n", x509->id->len, x509->id->data);
*ctx->ppcerts = x509; *ctx->ppcerts = x509;
ctx->ppcerts = &x509->next; ctx->ppcerts = &x509->next;
return 0; return 0;
@ -338,8 +348,8 @@ int pkcs7_sig_note_serial(void *context, size_t hdrlen,
const void *value, size_t vlen) const void *value, size_t vlen)
{ {
struct pkcs7_parse_context *ctx = context; struct pkcs7_parse_context *ctx = context;
ctx->sinfo->raw_serial = value; ctx->raw_serial = value;
ctx->sinfo->raw_serial_size = vlen; ctx->raw_serial_size = vlen;
return 0; return 0;
} }
@ -351,8 +361,8 @@ int pkcs7_sig_note_issuer(void *context, size_t hdrlen,
const void *value, size_t vlen) const void *value, size_t vlen)
{ {
struct pkcs7_parse_context *ctx = context; struct pkcs7_parse_context *ctx = context;
ctx->sinfo->raw_issuer = value; ctx->raw_issuer = value;
ctx->sinfo->raw_issuer_size = vlen; ctx->raw_issuer_size = vlen;
return 0; return 0;
} }
@ -385,10 +395,21 @@ int pkcs7_note_signed_info(void *context, size_t hdrlen,
const void *value, size_t vlen) const void *value, size_t vlen)
{ {
struct pkcs7_parse_context *ctx = context; struct pkcs7_parse_context *ctx = context;
struct pkcs7_signed_info *sinfo = ctx->sinfo;
struct asymmetric_key_id *kid;
ctx->sinfo->index = ++ctx->sinfo_index; /* Generate cert issuer + serial number key ID */
*ctx->ppsinfo = ctx->sinfo; kid = asymmetric_key_generate_id(ctx->raw_serial,
ctx->ppsinfo = &ctx->sinfo->next; ctx->raw_serial_size,
ctx->raw_issuer,
ctx->raw_issuer_size);
if (IS_ERR(kid))
return PTR_ERR(kid);
sinfo->signing_cert_id = kid;
sinfo->index = ++ctx->sinfo_index;
*ctx->ppsinfo = sinfo;
ctx->ppsinfo = &sinfo->next;
ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL); ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL);
if (!ctx->sinfo) if (!ctx->sinfo)
return -ENOMEM; return -ENOMEM;

Просмотреть файл

@ -23,6 +23,7 @@ struct pkcs7_signed_info {
struct x509_certificate *signer; /* Signing certificate (in msg->certs) */ struct x509_certificate *signer; /* Signing certificate (in msg->certs) */
unsigned index; unsigned index;
bool trusted; bool trusted;
bool unsupported_crypto; /* T if not usable due to missing crypto */
/* Message digest - the digest of the Content Data (or NULL) */ /* Message digest - the digest of the Content Data (or NULL) */
const void *msgdigest; const void *msgdigest;
@ -33,10 +34,7 @@ struct pkcs7_signed_info {
const void *authattrs; const void *authattrs;
/* Issuing cert serial number and issuer's name */ /* Issuing cert serial number and issuer's name */
const void *raw_serial; struct asymmetric_key_id *signing_cert_id;
unsigned raw_serial_size;
unsigned raw_issuer_size;
const void *raw_issuer;
/* Message signature. /* Message signature.
* *

Просмотреть файл

@ -23,9 +23,9 @@
/** /**
* Check the trust on one PKCS#7 SignedInfo block. * Check the trust on one PKCS#7 SignedInfo block.
*/ */
int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
struct pkcs7_signed_info *sinfo, struct pkcs7_signed_info *sinfo,
struct key *trust_keyring) struct key *trust_keyring)
{ {
struct public_key_signature *sig = &sinfo->sig; struct public_key_signature *sig = &sinfo->sig;
struct x509_certificate *x509, *last = NULL, *p; struct x509_certificate *x509, *last = NULL, *p;
@ -35,6 +35,11 @@ int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
kenter(",%u,", sinfo->index); kenter(",%u,", sinfo->index);
if (sinfo->unsupported_crypto) {
kleave(" = -ENOPKG [cached]");
return -ENOPKG;
}
for (x509 = sinfo->signer; x509; x509 = x509->signer) { for (x509 = sinfo->signer; x509; x509 = x509->signer) {
if (x509->seen) { if (x509->seen) {
if (x509->verified) { if (x509->verified) {
@ -49,15 +54,18 @@ int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
/* Look to see if this certificate is present in the trusted /* Look to see if this certificate is present in the trusted
* keys. * keys.
*/ */
key = x509_request_asymmetric_key(trust_keyring, x509->subject, key = x509_request_asymmetric_key(trust_keyring, x509->id,
x509->fingerprint); false);
if (!IS_ERR(key)) if (!IS_ERR(key)) {
/* One of the X.509 certificates in the PKCS#7 message /* One of the X.509 certificates in the PKCS#7 message
* is apparently the same as one we already trust. * is apparently the same as one we already trust.
* Verify that the trusted variant can also validate * Verify that the trusted variant can also validate
* the signature on the descendant. * the signature on the descendant.
*/ */
pr_devel("sinfo %u: Cert %u as key %x\n",
sinfo->index, x509->index, key_serial(key));
goto matched; goto matched;
}
if (key == ERR_PTR(-ENOMEM)) if (key == ERR_PTR(-ENOMEM))
return -ENOMEM; return -ENOMEM;
@ -77,16 +85,36 @@ int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
/* No match - see if the root certificate has a signer amongst the /* No match - see if the root certificate has a signer amongst the
* trusted keys. * trusted keys.
*/ */
if (!last || !last->issuer || !last->authority) { if (last && last->authority) {
kleave(" = -ENOKEY [no backref]"); key = x509_request_asymmetric_key(trust_keyring, last->authority,
return -ENOKEY; false);
if (!IS_ERR(key)) {
x509 = last;
pr_devel("sinfo %u: Root cert %u signer is key %x\n",
sinfo->index, x509->index, key_serial(key));
goto matched;
}
if (PTR_ERR(key) != -ENOKEY)
return PTR_ERR(key);
} }
key = x509_request_asymmetric_key(trust_keyring, last->issuer, /* As a last resort, see if we have a trusted public key that matches
last->authority); * the signed info directly.
if (IS_ERR(key)) */
return PTR_ERR(key) == -ENOMEM ? -ENOMEM : -ENOKEY; key = x509_request_asymmetric_key(trust_keyring,
x509 = last; sinfo->signing_cert_id,
false);
if (!IS_ERR(key)) {
pr_devel("sinfo %u: Direct signer is key %x\n",
sinfo->index, key_serial(key));
x509 = NULL;
goto matched;
}
if (PTR_ERR(key) != -ENOKEY)
return PTR_ERR(key);
kleave(" = -ENOKEY [no backref]");
return -ENOKEY;
matched: matched:
ret = verify_signature(key, sig); ret = verify_signature(key, sig);
@ -100,10 +128,12 @@ matched:
} }
verified: verified:
x509->verified = true; if (x509) {
for (p = sinfo->signer; p != x509; p = p->signer) { x509->verified = true;
p->verified = true; for (p = sinfo->signer; p != x509; p = p->signer) {
p->trusted = trusted; p->verified = true;
p->trusted = trusted;
}
} }
sinfo->trusted = trusted; sinfo->trusted = trusted;
kleave(" = 0"); kleave(" = 0");
@ -141,24 +171,28 @@ int pkcs7_validate_trust(struct pkcs7_message *pkcs7,
{ {
struct pkcs7_signed_info *sinfo; struct pkcs7_signed_info *sinfo;
struct x509_certificate *p; struct x509_certificate *p;
int cached_ret = 0, ret; int cached_ret = -ENOKEY;
int ret;
for (p = pkcs7->certs; p; p = p->next) for (p = pkcs7->certs; p; p = p->next)
p->seen = false; p->seen = false;
for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) { for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) {
ret = pkcs7_validate_trust_one(pkcs7, sinfo, trust_keyring); ret = pkcs7_validate_trust_one(pkcs7, sinfo, trust_keyring);
if (ret < 0) { switch (ret) {
if (ret == -ENOPKG) { case -ENOKEY:
continue;
case -ENOPKG:
if (cached_ret == -ENOKEY)
cached_ret = -ENOPKG; cached_ret = -ENOPKG;
} else if (ret == -ENOKEY) { continue;
if (cached_ret == 0) case 0:
cached_ret = -ENOKEY; *_trusted |= sinfo->trusted;
} else { cached_ret = 0;
return ret; continue;
} default:
return ret;
} }
*_trusted |= sinfo->trusted;
} }
return cached_ret; return cached_ret;

Просмотреть файл

@ -131,8 +131,7 @@ static int pkcs7_find_key(struct pkcs7_message *pkcs7,
struct x509_certificate *x509; struct x509_certificate *x509;
unsigned certix = 1; unsigned certix = 1;
kenter("%u,%u,%u", kenter("%u", sinfo->index);
sinfo->index, sinfo->raw_serial_size, sinfo->raw_issuer_size);
for (x509 = pkcs7->certs; x509; x509 = x509->next, certix++) { for (x509 = pkcs7->certs; x509; x509 = x509->next, certix++) {
/* I'm _assuming_ that the generator of the PKCS#7 message will /* I'm _assuming_ that the generator of the PKCS#7 message will
@ -140,21 +139,11 @@ static int pkcs7_find_key(struct pkcs7_message *pkcs7,
* PKCS#7 message - but I can't be 100% sure of that. It's * PKCS#7 message - but I can't be 100% sure of that. It's
* possible this will need element-by-element comparison. * possible this will need element-by-element comparison.
*/ */
if (x509->raw_serial_size != sinfo->raw_serial_size || if (!asymmetric_key_id_same(x509->id, sinfo->signing_cert_id))
memcmp(x509->raw_serial, sinfo->raw_serial,
sinfo->raw_serial_size) != 0)
continue; continue;
pr_devel("Sig %u: Found cert serial match X.509[%u]\n", pr_devel("Sig %u: Found cert serial match X.509[%u]\n",
sinfo->index, certix); sinfo->index, certix);
if (x509->raw_issuer_size != sinfo->raw_issuer_size ||
memcmp(x509->raw_issuer, sinfo->raw_issuer,
sinfo->raw_issuer_size) != 0) {
pr_warn("Sig %u: X.509 subject and PKCS#7 issuer don't match\n",
sinfo->index);
continue;
}
if (x509->pub->pkey_algo != sinfo->sig.pkey_algo) { if (x509->pub->pkey_algo != sinfo->sig.pkey_algo) {
pr_warn("Sig %u: X.509 algo and PKCS#7 sig algo don't match\n", pr_warn("Sig %u: X.509 algo and PKCS#7 sig algo don't match\n",
sinfo->index); sinfo->index);
@ -164,9 +153,14 @@ static int pkcs7_find_key(struct pkcs7_message *pkcs7,
sinfo->signer = x509; sinfo->signer = x509;
return 0; return 0;
} }
pr_warn("Sig %u: Issuing X.509 cert not found (#%*ph)\n",
sinfo->index, sinfo->raw_serial_size, sinfo->raw_serial); /* The relevant X.509 cert isn't found here, but it might be found in
return -ENOKEY; * the trust keyring.
*/
pr_debug("Sig %u: Issuing X.509 cert not found (#%*phN)\n",
sinfo->index,
sinfo->signing_cert_id->len, sinfo->signing_cert_id->data);
return 0;
} }
/* /*
@ -184,15 +178,18 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
p->seen = false; p->seen = false;
for (;;) { for (;;) {
pr_debug("verify %s: %s\n", x509->subject, x509->fingerprint); pr_debug("verify %s: %*phN\n",
x509->subject,
x509->raw_serial_size, x509->raw_serial);
x509->seen = true; x509->seen = true;
ret = x509_get_sig_params(x509); ret = x509_get_sig_params(x509);
if (ret < 0) if (ret < 0)
return ret; goto maybe_missing_crypto_in_x509;
pr_debug("- issuer %s\n", x509->issuer); pr_debug("- issuer %s\n", x509->issuer);
if (x509->authority) if (x509->authority)
pr_debug("- authkeyid %s\n", x509->authority); pr_debug("- authkeyid %*phN\n",
x509->authority->len, x509->authority->data);
if (!x509->authority || if (!x509->authority ||
strcmp(x509->subject, x509->issuer) == 0) { strcmp(x509->subject, x509->issuer) == 0) {
@ -209,7 +206,7 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
ret = x509_check_signature(x509->pub, x509); ret = x509_check_signature(x509->pub, x509);
if (ret < 0) if (ret < 0)
return ret; goto maybe_missing_crypto_in_x509;
x509->signer = x509; x509->signer = x509;
pr_debug("- self-signed\n"); pr_debug("- self-signed\n");
return 0; return 0;
@ -218,13 +215,14 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
/* Look through the X.509 certificates in the PKCS#7 message's /* Look through the X.509 certificates in the PKCS#7 message's
* list to see if the next one is there. * list to see if the next one is there.
*/ */
pr_debug("- want %s\n", x509->authority); pr_debug("- want %*phN\n",
x509->authority->len, x509->authority->data);
for (p = pkcs7->certs; p; p = p->next) { for (p = pkcs7->certs; p; p = p->next) {
pr_debug("- cmp [%u] %s\n", p->index, p->fingerprint); if (!p->skid)
if (p->raw_subject_size == x509->raw_issuer_size && continue;
strcmp(p->fingerprint, x509->authority) == 0 && pr_debug("- cmp [%u] %*phN\n",
memcmp(p->raw_subject, x509->raw_issuer, p->index, p->skid->len, p->skid->data);
x509->raw_issuer_size) == 0) if (asymmetric_key_id_same(p->skid, x509->authority))
goto found_issuer; goto found_issuer;
} }
@ -233,7 +231,7 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
return 0; return 0;
found_issuer: found_issuer:
pr_debug("- issuer %s\n", p->subject); pr_debug("- subject %s\n", p->subject);
if (p->seen) { if (p->seen) {
pr_warn("Sig %u: X.509 chain contains loop\n", pr_warn("Sig %u: X.509 chain contains loop\n",
sinfo->index); sinfo->index);
@ -250,6 +248,17 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
x509 = p; x509 = p;
might_sleep(); might_sleep();
} }
maybe_missing_crypto_in_x509:
/* Just prune the certificate chain at this point if we lack some
* crypto module to go further. Note, however, we don't want to set
* sinfo->missing_crypto as the signed info block may still be
* validatable against an X.509 cert lower in the chain that we have a
* trusted copy of.
*/
if (ret == -ENOPKG)
return 0;
return ret;
} }
/* /*
@ -269,11 +278,14 @@ static int pkcs7_verify_one(struct pkcs7_message *pkcs7,
if (ret < 0) if (ret < 0)
return ret; return ret;
/* Find the key for the signature */ /* Find the key for the signature if there is one */
ret = pkcs7_find_key(pkcs7, sinfo); ret = pkcs7_find_key(pkcs7, sinfo);
if (ret < 0) if (ret < 0)
return ret; return ret;
if (!sinfo->signer)
return 0;
pr_devel("Using X.509[%u] for sig %u\n", pr_devel("Using X.509[%u] for sig %u\n",
sinfo->signer->index, sinfo->index); sinfo->signer->index, sinfo->index);
@ -291,11 +303,33 @@ static int pkcs7_verify_one(struct pkcs7_message *pkcs7,
/** /**
* pkcs7_verify - Verify a PKCS#7 message * pkcs7_verify - Verify a PKCS#7 message
* @pkcs7: The PKCS#7 message to be verified * @pkcs7: The PKCS#7 message to be verified
*
* Verify a PKCS#7 message is internally consistent - that is, the data digest
* matches the digest in the AuthAttrs and any signature in the message or one
* of the X.509 certificates it carries that matches another X.509 cert in the
* message can be verified.
*
* This does not look to match the contents of the PKCS#7 message against any
* external public keys.
*
* Returns, in order of descending priority:
*
* (*) -EKEYREJECTED if a signature failed to match for which we found an
* appropriate X.509 certificate, or:
*
* (*) -EBADMSG if some part of the message was invalid, or:
*
* (*) -ENOPKG if none of the signature chains are verifiable because suitable
* crypto modules couldn't be found, or:
*
* (*) 0 if all the signature chains that don't incur -ENOPKG can be verified
* (note that a signature chain may be of zero length), or:
*/ */
int pkcs7_verify(struct pkcs7_message *pkcs7) int pkcs7_verify(struct pkcs7_message *pkcs7)
{ {
struct pkcs7_signed_info *sinfo; struct pkcs7_signed_info *sinfo;
struct x509_certificate *x509; struct x509_certificate *x509;
int enopkg = -ENOPKG;
int ret, n; int ret, n;
kenter(""); kenter("");
@ -304,18 +338,24 @@ int pkcs7_verify(struct pkcs7_message *pkcs7)
ret = x509_get_sig_params(x509); ret = x509_get_sig_params(x509);
if (ret < 0) if (ret < 0)
return ret; return ret;
pr_debug("X.509[%u] %s\n", n, x509->authority); pr_debug("X.509[%u] %*phN\n",
n, x509->authority->len, x509->authority->data);
} }
for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) { for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) {
ret = pkcs7_verify_one(pkcs7, sinfo); ret = pkcs7_verify_one(pkcs7, sinfo);
if (ret < 0) { if (ret < 0) {
if (ret == -ENOPKG) {
sinfo->unsupported_crypto = true;
continue;
}
kleave(" = %d", ret); kleave(" = %d", ret);
return ret; return ret;
} }
enopkg = 0;
} }
kleave(" = 0"); kleave(" = %d", enopkg);
return 0; return enopkg;
} }
EXPORT_SYMBOL_GPL(pkcs7_verify); EXPORT_SYMBOL_GPL(pkcs7_verify);

Просмотреть файл

@ -11,6 +11,7 @@
* 2 of the Licence, or (at your option) any later version. * 2 of the Licence, or (at your option) any later version.
*/ */
#define pr_fmt(fmt) "SIG: "fmt
#include <keys/asymmetric-subtype.h> #include <keys/asymmetric-subtype.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/err.h> #include <linux/err.h>

Просмотреть файл

@ -46,7 +46,8 @@ void x509_free_certificate(struct x509_certificate *cert)
public_key_destroy(cert->pub); public_key_destroy(cert->pub);
kfree(cert->issuer); kfree(cert->issuer);
kfree(cert->subject); kfree(cert->subject);
kfree(cert->fingerprint); kfree(cert->id);
kfree(cert->skid);
kfree(cert->authority); kfree(cert->authority);
kfree(cert->sig.digest); kfree(cert->sig.digest);
mpi_free(cert->sig.rsa.s); mpi_free(cert->sig.rsa.s);
@ -62,6 +63,7 @@ struct x509_certificate *x509_cert_parse(const void *data, size_t datalen)
{ {
struct x509_certificate *cert; struct x509_certificate *cert;
struct x509_parse_context *ctx; struct x509_parse_context *ctx;
struct asymmetric_key_id *kid;
long ret; long ret;
ret = -ENOMEM; ret = -ENOMEM;
@ -89,6 +91,17 @@ struct x509_certificate *x509_cert_parse(const void *data, size_t datalen)
if (ret < 0) if (ret < 0)
goto error_decode; goto error_decode;
/* Generate cert issuer + serial number key ID */
kid = asymmetric_key_generate_id(cert->raw_serial,
cert->raw_serial_size,
cert->raw_issuer,
cert->raw_issuer_size);
if (IS_ERR(kid)) {
ret = PTR_ERR(kid);
goto error_decode;
}
cert->id = kid;
kfree(ctx); kfree(ctx);
return cert; return cert;
@ -407,36 +420,36 @@ int x509_process_extension(void *context, size_t hdrlen,
const void *value, size_t vlen) const void *value, size_t vlen)
{ {
struct x509_parse_context *ctx = context; struct x509_parse_context *ctx = context;
struct asymmetric_key_id *kid;
const unsigned char *v = value; const unsigned char *v = value;
char *f;
int i; int i;
pr_debug("Extension: %u\n", ctx->last_oid); pr_debug("Extension: %u\n", ctx->last_oid);
if (ctx->last_oid == OID_subjectKeyIdentifier) { if (ctx->last_oid == OID_subjectKeyIdentifier) {
/* Get hold of the key fingerprint */ /* Get hold of the key fingerprint */
if (vlen < 3) if (ctx->cert->skid || vlen < 3)
return -EBADMSG; return -EBADMSG;
if (v[0] != ASN1_OTS || v[1] != vlen - 2) if (v[0] != ASN1_OTS || v[1] != vlen - 2)
return -EBADMSG; return -EBADMSG;
v += 2; v += 2;
vlen -= 2; vlen -= 2;
f = kmalloc(vlen * 2 + 1, GFP_KERNEL); ctx->cert->raw_skid_size = vlen;
if (!f) ctx->cert->raw_skid = v;
return -ENOMEM; kid = asymmetric_key_generate_id(ctx->cert->raw_subject,
for (i = 0; i < vlen; i++) ctx->cert->raw_subject_size,
sprintf(f + i * 2, "%02x", v[i]); v, vlen);
pr_debug("fingerprint %s\n", f); if (IS_ERR(kid))
ctx->cert->fingerprint = f; return PTR_ERR(kid);
ctx->cert->skid = kid;
pr_debug("subjkeyid %*phN\n", kid->len, kid->data);
return 0; return 0;
} }
if (ctx->last_oid == OID_authorityKeyIdentifier) { if (ctx->last_oid == OID_authorityKeyIdentifier) {
size_t key_len;
/* Get hold of the CA key fingerprint */ /* Get hold of the CA key fingerprint */
if (vlen < 5) if (ctx->cert->authority || vlen < 5)
return -EBADMSG; return -EBADMSG;
/* Authority Key Identifier must be a Constructed SEQUENCE */ /* Authority Key Identifier must be a Constructed SEQUENCE */
@ -454,7 +467,7 @@ int x509_process_extension(void *context, size_t hdrlen,
v[3] > vlen - 4) v[3] > vlen - 4)
return -EBADMSG; return -EBADMSG;
key_len = v[3]; vlen = v[3];
v += 4; v += 4;
} else { } else {
/* Long Form length */ /* Long Form length */
@ -476,17 +489,17 @@ int x509_process_extension(void *context, size_t hdrlen,
v[sub + 1] > vlen - 4 - sub) v[sub + 1] > vlen - 4 - sub)
return -EBADMSG; return -EBADMSG;
key_len = v[sub + 1]; vlen = v[sub + 1];
v += (sub + 2); v += (sub + 2);
} }
f = kmalloc(key_len * 2 + 1, GFP_KERNEL); kid = asymmetric_key_generate_id(ctx->cert->raw_issuer,
if (!f) ctx->cert->raw_issuer_size,
return -ENOMEM; v, vlen);
for (i = 0; i < key_len; i++) if (IS_ERR(kid))
sprintf(f + i * 2, "%02x", v[i]); return PTR_ERR(kid);
pr_debug("authority %s\n", f); pr_debug("authkeyid %*phN\n", kid->len, kid->data);
ctx->cert->authority = f; ctx->cert->authority = kid;
return 0; return 0;
} }

Просмотреть файл

@ -19,8 +19,9 @@ struct x509_certificate {
struct public_key_signature sig; /* Signature parameters */ struct public_key_signature sig; /* Signature parameters */
char *issuer; /* Name of certificate issuer */ char *issuer; /* Name of certificate issuer */
char *subject; /* Name of certificate subject */ char *subject; /* Name of certificate subject */
char *fingerprint; /* Key fingerprint as hex */ struct asymmetric_key_id *id; /* Serial number + issuer */
char *authority; /* Authority key fingerprint as hex */ struct asymmetric_key_id *skid; /* Subject + subjectKeyId (optional) */
struct asymmetric_key_id *authority; /* Authority key identifier (optional) */
struct tm valid_from; struct tm valid_from;
struct tm valid_to; struct tm valid_to;
const void *tbs; /* Signed data */ const void *tbs; /* Signed data */
@ -33,10 +34,13 @@ struct x509_certificate {
const void *raw_issuer; /* Raw issuer name in ASN.1 */ const void *raw_issuer; /* Raw issuer name in ASN.1 */
const void *raw_subject; /* Raw subject name in ASN.1 */ const void *raw_subject; /* Raw subject name in ASN.1 */
unsigned raw_subject_size; unsigned raw_subject_size;
unsigned raw_skid_size;
const void *raw_skid; /* Raw subjectKeyId in ASN.1 */
unsigned index; unsigned index;
bool seen; /* Infinite recursion prevention */ bool seen; /* Infinite recursion prevention */
bool verified; bool verified;
bool trusted; bool trusted;
bool unsupported_crypto; /* T if can't be verified due to missing crypto */
}; };
/* /*

Просмотреть файл

@ -25,7 +25,7 @@
#include "x509_parser.h" #include "x509_parser.h"
static bool use_builtin_keys; static bool use_builtin_keys;
static char *ca_keyid; static struct asymmetric_key_id *ca_keyid;
#ifndef MODULE #ifndef MODULE
static int __init ca_keys_setup(char *str) static int __init ca_keys_setup(char *str)
@ -33,10 +33,16 @@ static int __init ca_keys_setup(char *str)
if (!str) /* default system keyring */ if (!str) /* default system keyring */
return 1; return 1;
if (strncmp(str, "id:", 3) == 0) if (strncmp(str, "id:", 3) == 0) {
ca_keyid = str; /* owner key 'id:xxxxxx' */ struct asymmetric_key_id *p;
else if (strcmp(str, "builtin") == 0) p = asymmetric_key_hex_to_key_id(str + 3);
if (p == ERR_PTR(-EINVAL))
pr_err("Unparsable hex string in ca_keys\n");
else if (!IS_ERR(p))
ca_keyid = p; /* owner key 'id:xxxxxx' */
} else if (strcmp(str, "builtin") == 0) {
use_builtin_keys = true; use_builtin_keys = true;
}
return 1; return 1;
} }
@ -46,31 +52,35 @@ __setup("ca_keys=", ca_keys_setup);
/** /**
* x509_request_asymmetric_key - Request a key by X.509 certificate params. * x509_request_asymmetric_key - Request a key by X.509 certificate params.
* @keyring: The keys to search. * @keyring: The keys to search.
* @subject: The name of the subject to whom the key belongs. * @kid: The key ID.
* @key_id: The subject key ID as a hex string. * @partial: Use partial match if true, exact if false.
* *
* Find a key in the given keyring by subject name and key ID. These might, * Find a key in the given keyring by subject name and key ID. These might,
* for instance, be the issuer name and the authority key ID of an X.509 * for instance, be the issuer name and the authority key ID of an X.509
* certificate that needs to be verified. * certificate that needs to be verified.
*/ */
struct key *x509_request_asymmetric_key(struct key *keyring, struct key *x509_request_asymmetric_key(struct key *keyring,
const char *subject, const struct asymmetric_key_id *kid,
const char *key_id) bool partial)
{ {
key_ref_t key; key_ref_t key;
size_t subject_len = strlen(subject), key_id_len = strlen(key_id); char *id, *p;
char *id;
/* Construct an identifier "<subjname>:<keyid>". */ /* Construct an identifier "id:<keyid>". */
id = kmalloc(subject_len + 2 + key_id_len + 1, GFP_KERNEL); p = id = kmalloc(2 + 1 + kid->len * 2 + 1, GFP_KERNEL);
if (!id) if (!id)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
memcpy(id, subject, subject_len); if (partial) {
id[subject_len + 0] = ':'; *p++ = 'i';
id[subject_len + 1] = ' '; *p++ = 'd';
memcpy(id + subject_len + 2, key_id, key_id_len); } else {
id[subject_len + 2 + key_id_len] = 0; *p++ = 'e';
*p++ = 'x';
}
*p++ = ':';
p = bin2hex(p, kid->data, kid->len);
*p = 0;
pr_debug("Look up: \"%s\"\n", id); pr_debug("Look up: \"%s\"\n", id);
@ -112,6 +122,8 @@ int x509_get_sig_params(struct x509_certificate *cert)
pr_devel("==>%s()\n", __func__); pr_devel("==>%s()\n", __func__);
if (cert->unsupported_crypto)
return -ENOPKG;
if (cert->sig.rsa.s) if (cert->sig.rsa.s)
return 0; return 0;
@ -124,8 +136,13 @@ int x509_get_sig_params(struct x509_certificate *cert)
* big the hash operational data will be. * big the hash operational data will be.
*/ */
tfm = crypto_alloc_shash(hash_algo_name[cert->sig.pkey_hash_algo], 0, 0); tfm = crypto_alloc_shash(hash_algo_name[cert->sig.pkey_hash_algo], 0, 0);
if (IS_ERR(tfm)) if (IS_ERR(tfm)) {
return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm); if (PTR_ERR(tfm) == -ENOENT) {
cert->unsupported_crypto = true;
return -ENOPKG;
}
return PTR_ERR(tfm);
}
desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); desc_size = crypto_shash_descsize(tfm) + sizeof(*desc);
digest_size = crypto_shash_digestsize(tfm); digest_size = crypto_shash_digestsize(tfm);
@ -172,6 +189,8 @@ int x509_check_signature(const struct public_key *pub,
return ret; return ret;
ret = public_key_verify_signature(pub, &cert->sig); ret = public_key_verify_signature(pub, &cert->sig);
if (ret == -ENOPKG)
cert->unsupported_crypto = true;
pr_debug("Cert Verification: %d\n", ret); pr_debug("Cert Verification: %d\n", ret);
return ret; return ret;
} }
@ -195,11 +214,11 @@ static int x509_validate_trust(struct x509_certificate *cert,
if (!trust_keyring) if (!trust_keyring)
return -EOPNOTSUPP; return -EOPNOTSUPP;
if (ca_keyid && !asymmetric_keyid_match(cert->authority, ca_keyid)) if (ca_keyid && !asymmetric_key_id_partial(cert->authority, ca_keyid))
return -EPERM; return -EPERM;
key = x509_request_asymmetric_key(trust_keyring, key = x509_request_asymmetric_key(trust_keyring, cert->authority,
cert->issuer, cert->authority); false);
if (!IS_ERR(key)) { if (!IS_ERR(key)) {
if (!use_builtin_keys if (!use_builtin_keys
|| test_bit(KEY_FLAG_BUILTIN, &key->flags)) || test_bit(KEY_FLAG_BUILTIN, &key->flags))
@ -214,9 +233,11 @@ static int x509_validate_trust(struct x509_certificate *cert,
*/ */
static int x509_key_preparse(struct key_preparsed_payload *prep) static int x509_key_preparse(struct key_preparsed_payload *prep)
{ {
struct asymmetric_key_ids *kids;
struct x509_certificate *cert; struct x509_certificate *cert;
const char *q;
size_t srlen, sulen; size_t srlen, sulen;
char *desc = NULL; char *desc = NULL, *p;
int ret; int ret;
cert = x509_cert_parse(prep->data, prep->datalen); cert = x509_cert_parse(prep->data, prep->datalen);
@ -249,19 +270,12 @@ static int x509_key_preparse(struct key_preparsed_payload *prep)
pkey_algo_name[cert->sig.pkey_algo], pkey_algo_name[cert->sig.pkey_algo],
hash_algo_name[cert->sig.pkey_hash_algo]); hash_algo_name[cert->sig.pkey_hash_algo]);
if (!cert->fingerprint) {
pr_warn("Cert for '%s' must have a SubjKeyId extension\n",
cert->subject);
ret = -EKEYREJECTED;
goto error_free_cert;
}
cert->pub->algo = pkey_algo[cert->pub->pkey_algo]; cert->pub->algo = pkey_algo[cert->pub->pkey_algo];
cert->pub->id_type = PKEY_ID_X509; cert->pub->id_type = PKEY_ID_X509;
/* Check the signature on the key if it appears to be self-signed */ /* Check the signature on the key if it appears to be self-signed */
if (!cert->authority || if (!cert->authority ||
strcmp(cert->fingerprint, cert->authority) == 0) { asymmetric_key_id_same(cert->skid, cert->authority)) {
ret = x509_check_signature(cert->pub, cert); /* self-signed */ ret = x509_check_signature(cert->pub, cert); /* self-signed */
if (ret < 0) if (ret < 0)
goto error_free_cert; goto error_free_cert;
@ -273,31 +287,52 @@ static int x509_key_preparse(struct key_preparsed_payload *prep)
/* Propose a description */ /* Propose a description */
sulen = strlen(cert->subject); sulen = strlen(cert->subject);
srlen = strlen(cert->fingerprint); if (cert->raw_skid) {
srlen = cert->raw_skid_size;
q = cert->raw_skid;
} else {
srlen = cert->raw_serial_size;
q = cert->raw_serial;
}
if (srlen > 1 && *q == 0) {
srlen--;
q++;
}
ret = -ENOMEM; ret = -ENOMEM;
desc = kmalloc(sulen + 2 + srlen + 1, GFP_KERNEL); desc = kmalloc(sulen + 2 + srlen * 2 + 1, GFP_KERNEL);
if (!desc) if (!desc)
goto error_free_cert; goto error_free_cert;
memcpy(desc, cert->subject, sulen); p = memcpy(desc, cert->subject, sulen);
desc[sulen] = ':'; p += sulen;
desc[sulen + 1] = ' '; *p++ = ':';
memcpy(desc + sulen + 2, cert->fingerprint, srlen); *p++ = ' ';
desc[sulen + 2 + srlen] = 0; p = bin2hex(p, q, srlen);
*p = 0;
kids = kmalloc(sizeof(struct asymmetric_key_ids), GFP_KERNEL);
if (!kids)
goto error_free_desc;
kids->id[0] = cert->id;
kids->id[1] = cert->skid;
/* We're pinning the module by being linked against it */ /* We're pinning the module by being linked against it */
__module_get(public_key_subtype.owner); __module_get(public_key_subtype.owner);
prep->type_data[0] = &public_key_subtype; prep->type_data[0] = &public_key_subtype;
prep->type_data[1] = cert->fingerprint; prep->type_data[1] = kids;
prep->payload[0] = cert->pub; prep->payload[0] = cert->pub;
prep->description = desc; prep->description = desc;
prep->quotalen = 100; prep->quotalen = 100;
/* We've finished with the certificate */ /* We've finished with the certificate */
cert->pub = NULL; cert->pub = NULL;
cert->fingerprint = NULL; cert->id = NULL;
cert->skid = NULL;
desc = NULL; desc = NULL;
ret = 0; ret = 0;
error_free_desc:
kfree(desc);
error_free_cert: error_free_cert:
x509_free_certificate(cert); x509_free_certificate(cert);
return ret; return ret;

Просмотреть файл

@ -62,7 +62,6 @@ cifs_spnego_key_destroy(struct key *key)
struct key_type cifs_spnego_key_type = { struct key_type cifs_spnego_key_type = {
.name = "cifs.spnego", .name = "cifs.spnego",
.instantiate = cifs_spnego_key_instantiate, .instantiate = cifs_spnego_key_instantiate,
.match = user_match,
.destroy = cifs_spnego_key_destroy, .destroy = cifs_spnego_key_destroy,
.describe = user_describe, .describe = user_describe,
}; };

Просмотреть файл

@ -84,7 +84,6 @@ static struct key_type cifs_idmap_key_type = {
.instantiate = cifs_idmap_key_instantiate, .instantiate = cifs_idmap_key_instantiate,
.destroy = cifs_idmap_key_destroy, .destroy = cifs_idmap_key_destroy,
.describe = user_describe, .describe = user_describe,
.match = user_match,
}; };
static char * static char *

Просмотреть файл

@ -3074,7 +3074,7 @@ opened:
error = open_check_o_direct(file); error = open_check_o_direct(file);
if (error) if (error)
goto exit_fput; goto exit_fput;
error = ima_file_check(file, op->acc_mode); error = ima_file_check(file, op->acc_mode, *opened);
if (error) if (error)
goto exit_fput; goto exit_fput;

Просмотреть файл

@ -177,7 +177,6 @@ static struct key_type key_type_id_resolver = {
.preparse = user_preparse, .preparse = user_preparse,
.free_preparse = user_free_preparse, .free_preparse = user_free_preparse,
.instantiate = generic_key_instantiate, .instantiate = generic_key_instantiate,
.match = user_match,
.revoke = user_revoke, .revoke = user_revoke,
.destroy = user_destroy, .destroy = user_destroy,
.describe = user_describe, .describe = user_describe,
@ -401,7 +400,6 @@ static struct key_type key_type_id_resolver_legacy = {
.preparse = user_preparse, .preparse = user_preparse,
.free_preparse = user_free_preparse, .free_preparse = user_free_preparse,
.instantiate = generic_key_instantiate, .instantiate = generic_key_instantiate,
.match = user_match,
.revoke = user_revoke, .revoke = user_revoke,
.destroy = user_destroy, .destroy = user_destroy,
.describe = user_describe, .describe = user_describe,

Просмотреть файл

@ -721,7 +721,7 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type,
goto out_nfserr; goto out_nfserr;
} }
host_err = ima_file_check(file, may_flags); host_err = ima_file_check(file, may_flags, 0);
if (host_err) { if (host_err) {
nfsd_close(file); nfsd_close(file);
goto out_nfserr; goto out_nfserr;

Просмотреть файл

@ -15,6 +15,7 @@
#define _LINUX_PUBLIC_KEY_H #define _LINUX_PUBLIC_KEY_H
#include <linux/mpi.h> #include <linux/mpi.h>
#include <keys/asymmetric-type.h>
#include <crypto/hash_info.h> #include <crypto/hash_info.h>
enum pkey_algo { enum pkey_algo {
@ -98,8 +99,9 @@ struct key;
extern int verify_signature(const struct key *key, extern int verify_signature(const struct key *key,
const struct public_key_signature *sig); const struct public_key_signature *sig);
struct asymmetric_key_id;
extern struct key *x509_request_asymmetric_key(struct key *keyring, extern struct key *x509_request_asymmetric_key(struct key *keyring,
const char *issuer, const struct asymmetric_key_id *kid,
const char *key_id); bool partial);
#endif /* _LINUX_PUBLIC_KEY_H */ #endif /* _LINUX_PUBLIC_KEY_H */

Просмотреть файл

@ -18,6 +18,47 @@
extern struct key_type key_type_asymmetric; extern struct key_type key_type_asymmetric;
/*
* Identifiers for an asymmetric key ID. We have three ways of looking up a
* key derived from an X.509 certificate:
*
* (1) Serial Number & Issuer. Non-optional. This is the only valid way to
* map a PKCS#7 signature to an X.509 certificate.
*
* (2) Issuer & Subject Unique IDs. Optional. These were the original way to
* match X.509 certificates, but have fallen into disuse in favour of (3).
*
* (3) Auth & Subject Key Identifiers. Optional. SKIDs are only provided on
* CA keys that are intended to sign other keys, so don't appear in end
* user certificates unless forced.
*
* We could also support an PGP key identifier, which is just a SHA1 sum of the
* public key and certain parameters, but since we don't support PGP keys at
* the moment, we shall ignore those.
*
* What we actually do is provide a place where binary identifiers can be
* stashed and then compare against them when checking for an id match.
*/
struct asymmetric_key_id {
unsigned short len;
unsigned char data[];
};
struct asymmetric_key_ids {
void *id[2];
};
extern bool asymmetric_key_id_same(const struct asymmetric_key_id *kid1,
const struct asymmetric_key_id *kid2);
extern bool asymmetric_key_id_partial(const struct asymmetric_key_id *kid1,
const struct asymmetric_key_id *kid2);
extern struct asymmetric_key_id *asymmetric_key_generate_id(const void *val_1,
size_t len_1,
const void *val_2,
size_t len_2);
/* /*
* The payload is at the discretion of the subtype. * The payload is at the discretion of the subtype.
*/ */

Просмотреть файл

@ -40,7 +40,6 @@ struct key_preparsed_payload;
extern int user_preparse(struct key_preparsed_payload *prep); extern int user_preparse(struct key_preparsed_payload *prep);
extern void user_free_preparse(struct key_preparsed_payload *prep); extern void user_free_preparse(struct key_preparsed_payload *prep);
extern int user_update(struct key *key, struct key_preparsed_payload *prep); extern int user_update(struct key *key, struct key_preparsed_payload *prep);
extern int user_match(const struct key *key, const void *criterion);
extern void user_revoke(struct key *key); extern void user_revoke(struct key *key);
extern void user_destroy(struct key *key); extern void user_destroy(struct key *key);
extern void user_describe(const struct key *user, struct seq_file *m); extern void user_describe(const struct key *user, struct seq_file *m);

Просмотреть файл

@ -15,7 +15,7 @@ struct linux_binprm;
#ifdef CONFIG_IMA #ifdef CONFIG_IMA
extern int ima_bprm_check(struct linux_binprm *bprm); extern int ima_bprm_check(struct linux_binprm *bprm);
extern int ima_file_check(struct file *file, int mask); extern int ima_file_check(struct file *file, int mask, int opened);
extern void ima_file_free(struct file *file); extern void ima_file_free(struct file *file);
extern int ima_file_mmap(struct file *file, unsigned long prot); extern int ima_file_mmap(struct file *file, unsigned long prot);
extern int ima_module_check(struct file *file); extern int ima_module_check(struct file *file);
@ -27,7 +27,7 @@ static inline int ima_bprm_check(struct linux_binprm *bprm)
return 0; return 0;
} }
static inline int ima_file_check(struct file *file, int mask) static inline int ima_file_check(struct file *file, int mask, int opened)
{ {
return 0; return 0;
} }

Просмотреть файл

@ -496,6 +496,7 @@ static inline char *hex_byte_pack_upper(char *buf, u8 byte)
extern int hex_to_bin(char ch); extern int hex_to_bin(char ch);
extern int __must_check hex2bin(u8 *dst, const char *src, size_t count); extern int __must_check hex2bin(u8 *dst, const char *src, size_t count);
extern char *bin2hex(char *dst, const void *src, size_t count);
bool mac_pton(const char *s, u8 *mac); bool mac_pton(const char *s, u8 *mac);

Просмотреть файл

@ -52,6 +52,24 @@ struct key_preparsed_payload {
typedef int (*request_key_actor_t)(struct key_construction *key, typedef int (*request_key_actor_t)(struct key_construction *key,
const char *op, void *aux); const char *op, void *aux);
/*
* Preparsed matching criterion.
*/
struct key_match_data {
/* Comparison function, defaults to exact description match, but can be
* overridden by type->match_preparse(). Should return true if a match
* is found and false if not.
*/
bool (*cmp)(const struct key *key,
const struct key_match_data *match_data);
const void *raw_data; /* Raw match data */
void *preparsed; /* For ->match_preparse() to stash stuff */
unsigned lookup_type; /* Type of lookup for this search. */
#define KEYRING_SEARCH_LOOKUP_DIRECT 0x0000 /* Direct lookup by description. */
#define KEYRING_SEARCH_LOOKUP_ITERATE 0x0001 /* Iterative search. */
};
/* /*
* kernel managed key type definition * kernel managed key type definition
*/ */
@ -65,11 +83,6 @@ struct key_type {
*/ */
size_t def_datalen; size_t def_datalen;
/* Default key search algorithm. */
unsigned def_lookup_type;
#define KEYRING_SEARCH_LOOKUP_DIRECT 0x0000 /* Direct lookup by description. */
#define KEYRING_SEARCH_LOOKUP_ITERATE 0x0001 /* Iterative search. */
/* vet a description */ /* vet a description */
int (*vet_description)(const char *description); int (*vet_description)(const char *description);
@ -96,8 +109,15 @@ struct key_type {
*/ */
int (*update)(struct key *key, struct key_preparsed_payload *prep); int (*update)(struct key *key, struct key_preparsed_payload *prep);
/* match a key against a description */ /* Preparse the data supplied to ->match() (optional). The
int (*match)(const struct key *key, const void *desc); * data to be preparsed can be found in match_data->raw_data.
* The lookup type can also be set by this function.
*/
int (*match_preparse)(struct key_match_data *match_data);
/* Free preparsed match data (optional). This should be supplied it
* ->match_preparse() is supplied. */
void (*match_free)(struct key_match_data *match_data);
/* clear some of the data from a key on revokation (optional) /* clear some of the data from a key on revokation (optional)
* - the key's semaphore will be write-locked by the caller * - the key's semaphore will be write-locked by the caller

Просмотреть файл

@ -2108,7 +2108,7 @@ static inline int security_dentry_init_security(struct dentry *dentry,
static inline int security_inode_init_security(struct inode *inode, static inline int security_inode_init_security(struct inode *inode,
struct inode *dir, struct inode *dir,
const struct qstr *qstr, const struct qstr *qstr,
const initxattrs initxattrs, const initxattrs xattrs,
void *fs_data) void *fs_data)
{ {
return 0; return 0;

Просмотреть файл

@ -58,6 +58,22 @@ int hex2bin(u8 *dst, const char *src, size_t count)
} }
EXPORT_SYMBOL(hex2bin); EXPORT_SYMBOL(hex2bin);
/**
* bin2hex - convert binary data to an ascii hexadecimal string
* @dst: ascii hexadecimal result
* @src: binary data
* @count: binary data length
*/
char *bin2hex(char *dst, const void *src, size_t count)
{
const unsigned char *_src = src;
while (count--)
dst = hex_byte_pack(dst, *_src++);
return dst;
}
EXPORT_SYMBOL(bin2hex);
/** /**
* hex_dump_to_buffer - convert a blob of data to "hex ASCII" in memory * hex_dump_to_buffer - convert a blob of data to "hex ASCII" in memory
* @buf: data blob to dump * @buf: data blob to dump

Просмотреть файл

@ -476,7 +476,6 @@ struct key_type key_type_ceph = {
.preparse = ceph_key_preparse, .preparse = ceph_key_preparse,
.free_preparse = ceph_key_free_preparse, .free_preparse = ceph_key_free_preparse,
.instantiate = generic_key_instantiate, .instantiate = generic_key_instantiate,
.match = user_match,
.destroy = ceph_key_destroy, .destroy = ceph_key_destroy,
}; };

Просмотреть файл

@ -176,11 +176,11 @@ static void dns_resolver_free_preparse(struct key_preparsed_payload *prep)
* The domain name may be a simple name or an absolute domain name (which * The domain name may be a simple name or an absolute domain name (which
* should end with a period). The domain name is case-independent. * should end with a period). The domain name is case-independent.
*/ */
static int static bool dns_resolver_cmp(const struct key *key,
dns_resolver_match(const struct key *key, const void *description) const struct key_match_data *match_data)
{ {
int slen, dlen, ret = 0; int slen, dlen, ret = 0;
const char *src = key->description, *dsp = description; const char *src = key->description, *dsp = match_data->raw_data;
kenter("%s,%s", src, dsp); kenter("%s,%s", src, dsp);
@ -208,6 +208,16 @@ no_match:
return ret; return ret;
} }
/*
* Preparse the match criterion.
*/
static int dns_resolver_match_preparse(struct key_match_data *match_data)
{
match_data->lookup_type = KEYRING_SEARCH_LOOKUP_ITERATE;
match_data->cmp = dns_resolver_cmp;
return 0;
}
/* /*
* Describe a DNS key * Describe a DNS key
*/ */
@ -242,7 +252,7 @@ struct key_type key_type_dns_resolver = {
.preparse = dns_resolver_preparse, .preparse = dns_resolver_preparse,
.free_preparse = dns_resolver_free_preparse, .free_preparse = dns_resolver_free_preparse,
.instantiate = generic_key_instantiate, .instantiate = generic_key_instantiate,
.match = dns_resolver_match, .match_preparse = dns_resolver_match_preparse,
.revoke = user_revoke, .revoke = user_revoke,
.destroy = user_destroy, .destroy = user_destroy,
.describe = dns_resolver_describe, .describe = dns_resolver_describe,

Просмотреть файл

@ -44,7 +44,6 @@ struct key_type key_type_rxrpc = {
.preparse = rxrpc_preparse, .preparse = rxrpc_preparse,
.free_preparse = rxrpc_free_preparse, .free_preparse = rxrpc_free_preparse,
.instantiate = generic_key_instantiate, .instantiate = generic_key_instantiate,
.match = user_match,
.destroy = rxrpc_destroy, .destroy = rxrpc_destroy,
.describe = rxrpc_describe, .describe = rxrpc_describe,
.read = rxrpc_read, .read = rxrpc_read,
@ -61,7 +60,6 @@ struct key_type key_type_rxrpc_s = {
.preparse = rxrpc_preparse_s, .preparse = rxrpc_preparse_s,
.free_preparse = rxrpc_free_preparse_s, .free_preparse = rxrpc_free_preparse_s,
.instantiate = generic_key_instantiate, .instantiate = generic_key_instantiate,
.match = user_match,
.destroy = rxrpc_destroy_s, .destroy = rxrpc_destroy_s,
.describe = rxrpc_describe, .describe = rxrpc_describe,
}; };

Просмотреть файл

@ -1,11 +1,23 @@
# #
config INTEGRITY config INTEGRITY
def_bool y bool "Integrity subsystem"
depends on IMA || EVM depends on SECURITY
default y
help
This option enables the integrity subsystem, which is comprised
of a number of different components including the Integrity
Measurement Architecture (IMA), Extended Verification Module
(EVM), IMA-appraisal extension, digital signature verification
extension and audit measurement log support.
Each of these components can be enabled/disabled separately.
Refer to the individual components for additional details.
if INTEGRITY
config INTEGRITY_SIGNATURE config INTEGRITY_SIGNATURE
boolean "Digital signature verification using multiple keyrings" boolean "Digital signature verification using multiple keyrings"
depends on INTEGRITY && KEYS depends on KEYS
default n default n
select SIGNATURE select SIGNATURE
help help
@ -17,21 +29,6 @@ config INTEGRITY_SIGNATURE
This is useful for evm and module keyrings, when keys are This is useful for evm and module keyrings, when keys are
usually only added from initramfs. usually only added from initramfs.
config INTEGRITY_AUDIT
bool "Enables integrity auditing support "
depends on INTEGRITY && AUDIT
default y
help
In addition to enabling integrity auditing support, this
option adds a kernel parameter 'integrity_audit', which
controls the level of integrity auditing messages.
0 - basic integrity auditing messages (default)
1 - additional integrity auditing messages
Additional informational integrity auditing messages would
be enabled by specifying 'integrity_audit=1' on the kernel
command line.
config INTEGRITY_ASYMMETRIC_KEYS config INTEGRITY_ASYMMETRIC_KEYS
boolean "Enable asymmetric keys support" boolean "Enable asymmetric keys support"
depends on INTEGRITY_SIGNATURE depends on INTEGRITY_SIGNATURE
@ -44,5 +41,22 @@ config INTEGRITY_ASYMMETRIC_KEYS
This option enables digital signature verification using This option enables digital signature verification using
asymmetric keys. asymmetric keys.
config INTEGRITY_AUDIT
bool "Enables integrity auditing support "
depends on AUDIT
default y
help
In addition to enabling integrity auditing support, this
option adds a kernel parameter 'integrity_audit', which
controls the level of integrity auditing messages.
0 - basic integrity auditing messages (default)
1 - additional integrity auditing messages
Additional informational integrity auditing messages would
be enabled by specifying 'integrity_audit=1' on the kernel
command line.
source security/integrity/ima/Kconfig source security/integrity/ima/Kconfig
source security/integrity/evm/Kconfig source security/integrity/evm/Kconfig
endif # if INTEGRITY

Просмотреть файл

@ -3,11 +3,11 @@
# #
obj-$(CONFIG_INTEGRITY) += integrity.o obj-$(CONFIG_INTEGRITY) += integrity.o
obj-$(CONFIG_INTEGRITY_AUDIT) += integrity_audit.o
obj-$(CONFIG_INTEGRITY_SIGNATURE) += digsig.o
obj-$(CONFIG_INTEGRITY_ASYMMETRIC_KEYS) += digsig_asymmetric.o
integrity-y := iint.o integrity-y := iint.o
integrity-$(CONFIG_INTEGRITY_AUDIT) += integrity_audit.o
integrity-$(CONFIG_INTEGRITY_SIGNATURE) += digsig.o
integrity-$(CONFIG_INTEGRITY_ASYMMETRIC_KEYS) += digsig_asymmetric.o
subdir-$(CONFIG_IMA) += ima subdir-$(CONFIG_IMA) += ima
obj-$(CONFIG_IMA) += ima/ obj-$(CONFIG_IMA) += ima/

Просмотреть файл

@ -13,6 +13,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/err.h> #include <linux/err.h>
#include <linux/ratelimit.h>
#include <linux/key-type.h> #include <linux/key-type.h>
#include <crypto/public_key.h> #include <crypto/public_key.h>
#include <keys/asymmetric-type.h> #include <keys/asymmetric-type.h>
@ -27,7 +28,7 @@ static struct key *request_asymmetric_key(struct key *keyring, uint32_t keyid)
struct key *key; struct key *key;
char name[12]; char name[12];
sprintf(name, "id:%x", keyid); sprintf(name, "id:%08x", keyid);
pr_debug("key search: \"%s\"\n", name); pr_debug("key search: \"%s\"\n", name);
@ -45,8 +46,8 @@ static struct key *request_asymmetric_key(struct key *keyring, uint32_t keyid)
} }
if (IS_ERR(key)) { if (IS_ERR(key)) {
pr_warn("Request for unknown key '%s' err %ld\n", pr_err_ratelimited("Request for unknown key '%s' err %ld\n",
name, PTR_ERR(key)); name, PTR_ERR(key));
switch (PTR_ERR(key)) { switch (PTR_ERR(key)) {
/* Hide some search errors */ /* Hide some search errors */
case -EACCES: case -EACCES:

Просмотреть файл

@ -1,6 +1,5 @@
config EVM config EVM
boolean "EVM support" boolean "EVM support"
depends on SECURITY
select KEYS select KEYS
select ENCRYPTED_KEYS select ENCRYPTED_KEYS
select CRYPTO_HMAC select CRYPTO_HMAC
@ -12,10 +11,6 @@ config EVM
If you are unsure how to answer this question, answer N. If you are unsure how to answer this question, answer N.
if EVM
menu "EVM options"
config EVM_ATTR_FSUUID config EVM_ATTR_FSUUID
bool "FSUUID (version 2)" bool "FSUUID (version 2)"
default y default y
@ -47,6 +42,3 @@ config EVM_EXTRA_SMACK_XATTRS
additional info to the calculation, requires existing EVM additional info to the calculation, requires existing EVM
labeled file systems to be relabeled. labeled file systems to be relabeled.
endmenu
endif

Просмотреть файл

@ -126,14 +126,15 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
rc = vfs_getxattr_alloc(dentry, XATTR_NAME_EVM, (char **)&xattr_data, 0, rc = vfs_getxattr_alloc(dentry, XATTR_NAME_EVM, (char **)&xattr_data, 0,
GFP_NOFS); GFP_NOFS);
if (rc <= 0) { if (rc <= 0) {
if (rc == 0) evm_status = INTEGRITY_FAIL;
evm_status = INTEGRITY_FAIL; /* empty */ if (rc == -ENODATA) {
else if (rc == -ENODATA) {
rc = evm_find_protected_xattrs(dentry); rc = evm_find_protected_xattrs(dentry);
if (rc > 0) if (rc > 0)
evm_status = INTEGRITY_NOLABEL; evm_status = INTEGRITY_NOLABEL;
else if (rc == 0) else if (rc == 0)
evm_status = INTEGRITY_NOXATTRS; /* new file */ evm_status = INTEGRITY_NOXATTRS; /* new file */
} else if (rc == -EOPNOTSUPP) {
evm_status = INTEGRITY_UNKNOWN;
} }
goto out; goto out;
} }
@ -284,6 +285,13 @@ static int evm_protect_xattr(struct dentry *dentry, const char *xattr_name,
goto out; goto out;
} }
evm_status = evm_verify_current_integrity(dentry); evm_status = evm_verify_current_integrity(dentry);
if (evm_status == INTEGRITY_NOXATTRS) {
struct integrity_iint_cache *iint;
iint = integrity_iint_find(dentry->d_inode);
if (iint && (iint->flags & IMA_NEW_FILE))
return 0;
}
out: out:
if (evm_status != INTEGRITY_PASS) if (evm_status != INTEGRITY_PASS)
integrity_audit_msg(AUDIT_INTEGRITY_METADATA, dentry->d_inode, integrity_audit_msg(AUDIT_INTEGRITY_METADATA, dentry->d_inode,
@ -352,7 +360,6 @@ void evm_inode_post_setxattr(struct dentry *dentry, const char *xattr_name,
return; return;
evm_update_evmxattr(dentry, xattr_name, xattr_value, xattr_value_len); evm_update_evmxattr(dentry, xattr_name, xattr_value, xattr_value_len);
return;
} }
/** /**
@ -372,7 +379,6 @@ void evm_inode_post_removexattr(struct dentry *dentry, const char *xattr_name)
mutex_lock(&inode->i_mutex); mutex_lock(&inode->i_mutex);
evm_update_evmxattr(dentry, xattr_name, NULL, 0); evm_update_evmxattr(dentry, xattr_name, NULL, 0);
mutex_unlock(&inode->i_mutex); mutex_unlock(&inode->i_mutex);
return;
} }
/** /**
@ -414,7 +420,6 @@ void evm_inode_post_setattr(struct dentry *dentry, int ia_valid)
if (ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID)) if (ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID))
evm_update_evmxattr(dentry, NULL, NULL, 0); evm_update_evmxattr(dentry, NULL, NULL, 0);
return;
} }
/* /*

Просмотреть файл

@ -2,8 +2,6 @@
# #
config IMA config IMA
bool "Integrity Measurement Architecture(IMA)" bool "Integrity Measurement Architecture(IMA)"
depends on SECURITY
select INTEGRITY
select SECURITYFS select SECURITYFS
select CRYPTO select CRYPTO
select CRYPTO_HMAC select CRYPTO_HMAC

Просмотреть файл

@ -43,6 +43,9 @@ enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 };
#define IMA_TEMPLATE_IMA_NAME "ima" #define IMA_TEMPLATE_IMA_NAME "ima"
#define IMA_TEMPLATE_IMA_FMT "d|n" #define IMA_TEMPLATE_IMA_FMT "d|n"
/* current content of the policy */
extern int ima_policy_flag;
/* set during initialization */ /* set during initialization */
extern int ima_initialized; extern int ima_initialized;
extern int ima_used_chip; extern int ima_used_chip;
@ -90,10 +93,7 @@ extern struct list_head ima_measurements; /* list of all measurements */
/* Internal IMA function definitions */ /* Internal IMA function definitions */
int ima_init(void); int ima_init(void);
void ima_cleanup(void);
int ima_fs_init(void); int ima_fs_init(void);
void ima_fs_cleanup(void);
int ima_inode_alloc(struct inode *inode);
int ima_add_template_entry(struct ima_template_entry *entry, int violation, int ima_add_template_entry(struct ima_template_entry *entry, int violation,
const char *op, struct inode *inode, const char *op, struct inode *inode,
const unsigned char *filename); const unsigned char *filename);
@ -110,8 +110,6 @@ void ima_print_digest(struct seq_file *m, u8 *digest, int size);
struct ima_template_desc *ima_template_desc_current(void); struct ima_template_desc *ima_template_desc_current(void);
int ima_init_template(void); int ima_init_template(void);
int ima_init_template(void);
/* /*
* used to protect h_table and sha_table * used to protect h_table and sha_table
*/ */
@ -151,12 +149,6 @@ int ima_store_template(struct ima_template_entry *entry, int violation,
void ima_free_template_entry(struct ima_template_entry *entry); void ima_free_template_entry(struct ima_template_entry *entry);
const char *ima_d_path(struct path *path, char **pathbuf); const char *ima_d_path(struct path *path, char **pathbuf);
/* rbtree tree calls to lookup, insert, delete
* integrity data associated with an inode.
*/
struct integrity_iint_cache *integrity_iint_insert(struct inode *inode);
struct integrity_iint_cache *integrity_iint_find(struct inode *inode);
/* IMA policy related functions */ /* IMA policy related functions */
enum ima_hooks { FILE_CHECK = 1, MMAP_CHECK, BPRM_CHECK, MODULE_CHECK, FIRMWARE_CHECK, POST_SETATTR }; enum ima_hooks { FILE_CHECK = 1, MMAP_CHECK, BPRM_CHECK, MODULE_CHECK, FIRMWARE_CHECK, POST_SETATTR };
@ -164,20 +156,22 @@ int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
int flags); int flags);
void ima_init_policy(void); void ima_init_policy(void);
void ima_update_policy(void); void ima_update_policy(void);
void ima_update_policy_flag(void);
ssize_t ima_parse_add_rule(char *); ssize_t ima_parse_add_rule(char *);
void ima_delete_rules(void); void ima_delete_rules(void);
/* Appraise integrity measurements */ /* Appraise integrity measurements */
#define IMA_APPRAISE_ENFORCE 0x01 #define IMA_APPRAISE_ENFORCE 0x01
#define IMA_APPRAISE_FIX 0x02 #define IMA_APPRAISE_FIX 0x02
#define IMA_APPRAISE_MODULES 0x04 #define IMA_APPRAISE_LOG 0x04
#define IMA_APPRAISE_FIRMWARE 0x08 #define IMA_APPRAISE_MODULES 0x08
#define IMA_APPRAISE_FIRMWARE 0x10
#ifdef CONFIG_IMA_APPRAISE #ifdef CONFIG_IMA_APPRAISE
int ima_appraise_measurement(int func, struct integrity_iint_cache *iint, int ima_appraise_measurement(int func, struct integrity_iint_cache *iint,
struct file *file, const unsigned char *filename, struct file *file, const unsigned char *filename,
struct evm_ima_xattr_data *xattr_value, struct evm_ima_xattr_data *xattr_value,
int xattr_len); int xattr_len, int opened);
int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func); int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func);
void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file); void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file);
enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint, enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,
@ -193,7 +187,7 @@ static inline int ima_appraise_measurement(int func,
struct file *file, struct file *file,
const unsigned char *filename, const unsigned char *filename,
struct evm_ima_xattr_data *xattr_value, struct evm_ima_xattr_data *xattr_value,
int xattr_len) int xattr_len, int opened)
{ {
return INTEGRITY_UNKNOWN; return INTEGRITY_UNKNOWN;
} }

Просмотреть файл

@ -179,11 +179,6 @@ int ima_get_action(struct inode *inode, int mask, int function)
return ima_match_policy(inode, function, mask, flags); return ima_match_policy(inode, function, mask, flags);
} }
int ima_must_measure(struct inode *inode, int mask, int function)
{
return ima_match_policy(inode, function, mask, IMA_MEASURE);
}
/* /*
* ima_collect_measurement - collect file measurement * ima_collect_measurement - collect file measurement
* *
@ -330,10 +325,9 @@ const char *ima_d_path(struct path *path, char **pathbuf)
{ {
char *pathname = NULL; char *pathname = NULL;
/* We will allow 11 spaces for ' (deleted)' to be appended */ *pathbuf = kmalloc(PATH_MAX, GFP_KERNEL);
*pathbuf = kmalloc(PATH_MAX + 11, GFP_KERNEL);
if (*pathbuf) { if (*pathbuf) {
pathname = d_path(path, *pathbuf, PATH_MAX + 11); pathname = d_absolute_path(path, *pathbuf, PATH_MAX);
if (IS_ERR(pathname)) { if (IS_ERR(pathname)) {
kfree(*pathbuf); kfree(*pathbuf);
*pathbuf = NULL; *pathbuf = NULL;

Просмотреть файл

@ -23,6 +23,8 @@ static int __init default_appraise_setup(char *str)
{ {
if (strncmp(str, "off", 3) == 0) if (strncmp(str, "off", 3) == 0)
ima_appraise = 0; ima_appraise = 0;
else if (strncmp(str, "log", 3) == 0)
ima_appraise = IMA_APPRAISE_LOG;
else if (strncmp(str, "fix", 3) == 0) else if (strncmp(str, "fix", 3) == 0)
ima_appraise = IMA_APPRAISE_FIX; ima_appraise = IMA_APPRAISE_FIX;
return 1; return 1;
@ -183,7 +185,7 @@ int ima_read_xattr(struct dentry *dentry,
int ima_appraise_measurement(int func, struct integrity_iint_cache *iint, int ima_appraise_measurement(int func, struct integrity_iint_cache *iint,
struct file *file, const unsigned char *filename, struct file *file, const unsigned char *filename,
struct evm_ima_xattr_data *xattr_value, struct evm_ima_xattr_data *xattr_value,
int xattr_len) int xattr_len, int opened)
{ {
static const char op[] = "appraise_data"; static const char op[] = "appraise_data";
char *cause = "unknown"; char *cause = "unknown";
@ -192,8 +194,6 @@ int ima_appraise_measurement(int func, struct integrity_iint_cache *iint,
enum integrity_status status = INTEGRITY_UNKNOWN; enum integrity_status status = INTEGRITY_UNKNOWN;
int rc = xattr_len, hash_start = 0; int rc = xattr_len, hash_start = 0;
if (!ima_appraise)
return 0;
if (!inode->i_op->getxattr) if (!inode->i_op->getxattr)
return INTEGRITY_UNKNOWN; return INTEGRITY_UNKNOWN;
@ -202,8 +202,11 @@ int ima_appraise_measurement(int func, struct integrity_iint_cache *iint,
goto out; goto out;
cause = "missing-hash"; cause = "missing-hash";
status = status = INTEGRITY_NOLABEL;
(inode->i_size == 0) ? INTEGRITY_PASS : INTEGRITY_NOLABEL; if (opened & FILE_CREATED) {
iint->flags |= IMA_NEW_FILE;
status = INTEGRITY_PASS;
}
goto out; goto out;
} }
@ -315,7 +318,7 @@ void ima_inode_post_setattr(struct dentry *dentry)
struct integrity_iint_cache *iint; struct integrity_iint_cache *iint;
int must_appraise, rc; int must_appraise, rc;
if (!ima_initialized || !ima_appraise || !S_ISREG(inode->i_mode) if (!(ima_policy_flag & IMA_APPRAISE) || !S_ISREG(inode->i_mode)
|| !inode->i_op->removexattr) || !inode->i_op->removexattr)
return; return;
@ -353,7 +356,7 @@ static void ima_reset_appraise_flags(struct inode *inode, int digsig)
{ {
struct integrity_iint_cache *iint; struct integrity_iint_cache *iint;
if (!ima_initialized || !ima_appraise || !S_ISREG(inode->i_mode)) if (!(ima_policy_flag & IMA_APPRAISE) || !S_ISREG(inode->i_mode))
return; return;
iint = integrity_iint_find(inode); iint = integrity_iint_find(inode);

Просмотреть файл

@ -80,24 +80,24 @@ static int ima_kernel_read(struct file *file, loff_t offset,
{ {
mm_segment_t old_fs; mm_segment_t old_fs;
char __user *buf = addr; char __user *buf = addr;
ssize_t ret; ssize_t ret = -EINVAL;
if (!(file->f_mode & FMODE_READ)) if (!(file->f_mode & FMODE_READ))
return -EBADF; return -EBADF;
if (!file->f_op->read && !file->f_op->aio_read)
return -EINVAL;
old_fs = get_fs(); old_fs = get_fs();
set_fs(get_ds()); set_fs(get_ds());
if (file->f_op->read) if (file->f_op->read)
ret = file->f_op->read(file, buf, count, &offset); ret = file->f_op->read(file, buf, count, &offset);
else else if (file->f_op->aio_read)
ret = do_sync_read(file, buf, count, &offset); ret = do_sync_read(file, buf, count, &offset);
else if (file->f_op->read_iter)
ret = new_sync_read(file, buf, count, &offset);
set_fs(old_fs); set_fs(old_fs);
return ret; return ret;
} }
int ima_init_crypto(void) int __init ima_init_crypto(void)
{ {
long rc; long rc;
@ -116,7 +116,10 @@ static struct crypto_shash *ima_alloc_tfm(enum hash_algo algo)
struct crypto_shash *tfm = ima_shash_tfm; struct crypto_shash *tfm = ima_shash_tfm;
int rc; int rc;
if (algo != ima_hash_algo && algo < HASH_ALGO__LAST) { if (algo < 0 || algo >= HASH_ALGO__LAST)
algo = ima_hash_algo;
if (algo != ima_hash_algo) {
tfm = crypto_alloc_shash(hash_algo_name[algo], 0, 0); tfm = crypto_alloc_shash(hash_algo_name[algo], 0, 0);
if (IS_ERR(tfm)) { if (IS_ERR(tfm)) {
rc = PTR_ERR(tfm); rc = PTR_ERR(tfm);
@ -200,7 +203,10 @@ static struct crypto_ahash *ima_alloc_atfm(enum hash_algo algo)
struct crypto_ahash *tfm = ima_ahash_tfm; struct crypto_ahash *tfm = ima_ahash_tfm;
int rc; int rc;
if ((algo != ima_hash_algo && algo < HASH_ALGO__LAST) || !tfm) { if (algo < 0 || algo >= HASH_ALGO__LAST)
algo = ima_hash_algo;
if (algo != ima_hash_algo || !tfm) {
tfm = crypto_alloc_ahash(hash_algo_name[algo], 0, 0); tfm = crypto_alloc_ahash(hash_algo_name[algo], 0, 0);
if (!IS_ERR(tfm)) { if (!IS_ERR(tfm)) {
if (algo == ima_hash_algo) if (algo == ima_hash_algo)

Просмотреть файл

@ -43,7 +43,7 @@ int ima_used_chip;
* a different value.) Violations add a zero entry to the measurement * a different value.) Violations add a zero entry to the measurement
* list and extend the aggregate PCR value with ff...ff's. * list and extend the aggregate PCR value with ff...ff's.
*/ */
static void __init ima_add_boot_aggregate(void) static int __init ima_add_boot_aggregate(void)
{ {
static const char op[] = "add_boot_aggregate"; static const char op[] = "add_boot_aggregate";
const char *audit_cause = "ENOMEM"; const char *audit_cause = "ENOMEM";
@ -72,17 +72,23 @@ static void __init ima_add_boot_aggregate(void)
result = ima_alloc_init_template(iint, NULL, boot_aggregate_name, result = ima_alloc_init_template(iint, NULL, boot_aggregate_name,
NULL, 0, &entry); NULL, 0, &entry);
if (result < 0) if (result < 0) {
return; audit_cause = "alloc_entry";
goto err_out;
}
result = ima_store_template(entry, violation, NULL, result = ima_store_template(entry, violation, NULL,
boot_aggregate_name); boot_aggregate_name);
if (result < 0) if (result < 0) {
ima_free_template_entry(entry); ima_free_template_entry(entry);
return; audit_cause = "store_entry";
goto err_out;
}
return 0;
err_out: err_out:
integrity_audit_msg(AUDIT_INTEGRITY_PCR, NULL, boot_aggregate_name, op, integrity_audit_msg(AUDIT_INTEGRITY_PCR, NULL, boot_aggregate_name, op,
audit_cause, result, 0); audit_cause, result, 0);
return result;
} }
int __init ima_init(void) int __init ima_init(void)
@ -98,6 +104,10 @@ int __init ima_init(void)
if (!ima_used_chip) if (!ima_used_chip)
pr_info("No TPM chip found, activating TPM-bypass!\n"); pr_info("No TPM chip found, activating TPM-bypass!\n");
rc = ima_init_keyring(INTEGRITY_KEYRING_IMA);
if (rc)
return rc;
rc = ima_init_crypto(); rc = ima_init_crypto();
if (rc) if (rc)
return rc; return rc;
@ -105,7 +115,10 @@ int __init ima_init(void)
if (rc != 0) if (rc != 0)
return rc; return rc;
ima_add_boot_aggregate(); /* boot aggregate must be first entry */ rc = ima_add_boot_aggregate(); /* boot aggregate must be first entry */
if (rc != 0)
return rc;
ima_init_policy(); ima_init_policy();
return ima_fs_init(); return ima_fs_init();

Просмотреть файл

@ -77,42 +77,39 @@ __setup("ima_hash=", hash_setup);
* could result in a file measurement error. * could result in a file measurement error.
* *
*/ */
static void ima_rdwr_violation_check(struct file *file) static void ima_rdwr_violation_check(struct file *file,
struct integrity_iint_cache *iint,
int must_measure,
char **pathbuf,
const char **pathname)
{ {
struct inode *inode = file_inode(file); struct inode *inode = file_inode(file);
fmode_t mode = file->f_mode; fmode_t mode = file->f_mode;
bool send_tomtou = false, send_writers = false; bool send_tomtou = false, send_writers = false;
char *pathbuf = NULL;
const char *pathname;
if (!S_ISREG(inode->i_mode) || !ima_initialized)
return;
if (mode & FMODE_WRITE) { if (mode & FMODE_WRITE) {
if (atomic_read(&inode->i_readcount) && IS_IMA(inode)) { if (atomic_read(&inode->i_readcount) && IS_IMA(inode)) {
struct integrity_iint_cache *iint; if (!iint)
iint = integrity_iint_find(inode); iint = integrity_iint_find(inode);
/* IMA_MEASURE is set from reader side */ /* IMA_MEASURE is set from reader side */
if (iint && (iint->flags & IMA_MEASURE)) if (iint && (iint->flags & IMA_MEASURE))
send_tomtou = true; send_tomtou = true;
} }
} else { } else {
if ((atomic_read(&inode->i_writecount) > 0) && if ((atomic_read(&inode->i_writecount) > 0) && must_measure)
ima_must_measure(inode, MAY_READ, FILE_CHECK))
send_writers = true; send_writers = true;
} }
if (!send_tomtou && !send_writers) if (!send_tomtou && !send_writers)
return; return;
pathname = ima_d_path(&file->f_path, &pathbuf); *pathname = ima_d_path(&file->f_path, pathbuf);
if (send_tomtou) if (send_tomtou)
ima_add_violation(file, pathname, "invalid_pcr", "ToMToU"); ima_add_violation(file, *pathname, "invalid_pcr", "ToMToU");
if (send_writers) if (send_writers)
ima_add_violation(file, pathname, ima_add_violation(file, *pathname,
"invalid_pcr", "open_writers"); "invalid_pcr", "open_writers");
kfree(pathbuf);
} }
static void ima_check_last_writer(struct integrity_iint_cache *iint, static void ima_check_last_writer(struct integrity_iint_cache *iint,
@ -124,11 +121,13 @@ static void ima_check_last_writer(struct integrity_iint_cache *iint,
return; return;
mutex_lock(&inode->i_mutex); mutex_lock(&inode->i_mutex);
if (atomic_read(&inode->i_writecount) == 1 && if (atomic_read(&inode->i_writecount) == 1) {
iint->version != inode->i_version) { if ((iint->version != inode->i_version) ||
iint->flags &= ~IMA_DONE_MASK; (iint->flags & IMA_NEW_FILE)) {
if (iint->flags & IMA_APPRAISE) iint->flags &= ~(IMA_DONE_MASK | IMA_NEW_FILE);
ima_update_xattr(iint, file); if (iint->flags & IMA_APPRAISE)
ima_update_xattr(iint, file);
}
} }
mutex_unlock(&inode->i_mutex); mutex_unlock(&inode->i_mutex);
} }
@ -154,19 +153,20 @@ void ima_file_free(struct file *file)
ima_check_last_writer(iint, inode, file); ima_check_last_writer(iint, inode, file);
} }
static int process_measurement(struct file *file, const char *filename, static int process_measurement(struct file *file, int mask, int function,
int mask, int function) int opened)
{ {
struct inode *inode = file_inode(file); struct inode *inode = file_inode(file);
struct integrity_iint_cache *iint; struct integrity_iint_cache *iint = NULL;
struct ima_template_desc *template_desc; struct ima_template_desc *template_desc;
char *pathbuf = NULL; char *pathbuf = NULL;
const char *pathname = NULL; const char *pathname = NULL;
int rc = -ENOMEM, action, must_appraise, _func; int rc = -ENOMEM, action, must_appraise;
struct evm_ima_xattr_data *xattr_value = NULL, **xattr_ptr = NULL; struct evm_ima_xattr_data *xattr_value = NULL, **xattr_ptr = NULL;
int xattr_len = 0; int xattr_len = 0;
bool violation_check;
if (!ima_initialized || !S_ISREG(inode->i_mode)) if (!ima_policy_flag || !S_ISREG(inode->i_mode))
return 0; return 0;
/* Return an IMA_MEASURE, IMA_APPRAISE, IMA_AUDIT action /* Return an IMA_MEASURE, IMA_APPRAISE, IMA_AUDIT action
@ -174,19 +174,33 @@ static int process_measurement(struct file *file, const char *filename,
* Included is the appraise submask. * Included is the appraise submask.
*/ */
action = ima_get_action(inode, mask, function); action = ima_get_action(inode, mask, function);
if (!action) violation_check = ((function == FILE_CHECK || function == MMAP_CHECK) &&
(ima_policy_flag & IMA_MEASURE));
if (!action && !violation_check)
return 0; return 0;
must_appraise = action & IMA_APPRAISE; must_appraise = action & IMA_APPRAISE;
/* Is the appraise rule hook specific? */ /* Is the appraise rule hook specific? */
_func = (action & IMA_FILE_APPRAISE) ? FILE_CHECK : function; if (action & IMA_FILE_APPRAISE)
function = FILE_CHECK;
mutex_lock(&inode->i_mutex); mutex_lock(&inode->i_mutex);
iint = integrity_inode_get(inode); if (action) {
if (!iint) iint = integrity_inode_get(inode);
goto out; if (!iint)
goto out;
}
if (violation_check) {
ima_rdwr_violation_check(file, iint, action & IMA_MEASURE,
&pathbuf, &pathname);
if (!action) {
rc = 0;
goto out_free;
}
}
/* Determine if already appraised/measured based on bitmask /* Determine if already appraised/measured based on bitmask
* (IMA_MEASURE, IMA_MEASURED, IMA_XXXX_APPRAISE, IMA_XXXX_APPRAISED, * (IMA_MEASURE, IMA_MEASURED, IMA_XXXX_APPRAISE, IMA_XXXX_APPRAISED,
@ -199,15 +213,13 @@ static int process_measurement(struct file *file, const char *filename,
/* Nothing to do, just return existing appraised status */ /* Nothing to do, just return existing appraised status */
if (!action) { if (!action) {
if (must_appraise) if (must_appraise)
rc = ima_get_cache_status(iint, _func); rc = ima_get_cache_status(iint, function);
goto out_digsig; goto out_digsig;
} }
template_desc = ima_template_desc_current(); template_desc = ima_template_desc_current();
if (strcmp(template_desc->name, IMA_TEMPLATE_IMA_NAME) == 0) { if ((action & IMA_APPRAISE_SUBMASK) ||
if (action & IMA_APPRAISE_SUBMASK) strcmp(template_desc->name, IMA_TEMPLATE_IMA_NAME) != 0)
xattr_ptr = &xattr_value;
} else
xattr_ptr = &xattr_value; xattr_ptr = &xattr_value;
rc = ima_collect_measurement(iint, file, xattr_ptr, &xattr_len); rc = ima_collect_measurement(iint, file, xattr_ptr, &xattr_len);
@ -217,23 +229,26 @@ static int process_measurement(struct file *file, const char *filename,
goto out_digsig; goto out_digsig;
} }
pathname = filename ?: ima_d_path(&file->f_path, &pathbuf); if (!pathname) /* ima_rdwr_violation possibly pre-fetched */
pathname = ima_d_path(&file->f_path, &pathbuf);
if (action & IMA_MEASURE) if (action & IMA_MEASURE)
ima_store_measurement(iint, file, pathname, ima_store_measurement(iint, file, pathname,
xattr_value, xattr_len); xattr_value, xattr_len);
if (action & IMA_APPRAISE_SUBMASK) if (action & IMA_APPRAISE_SUBMASK)
rc = ima_appraise_measurement(_func, iint, file, pathname, rc = ima_appraise_measurement(function, iint, file, pathname,
xattr_value, xattr_len); xattr_value, xattr_len, opened);
if (action & IMA_AUDIT) if (action & IMA_AUDIT)
ima_audit_measurement(iint, pathname); ima_audit_measurement(iint, pathname);
kfree(pathbuf);
out_digsig: out_digsig:
if ((mask & MAY_WRITE) && (iint->flags & IMA_DIGSIG)) if ((mask & MAY_WRITE) && (iint->flags & IMA_DIGSIG))
rc = -EACCES; rc = -EACCES;
kfree(xattr_value);
out_free:
kfree(pathbuf);
out: out:
mutex_unlock(&inode->i_mutex); mutex_unlock(&inode->i_mutex);
kfree(xattr_value);
if ((rc && must_appraise) && (ima_appraise & IMA_APPRAISE_ENFORCE)) if ((rc && must_appraise) && (ima_appraise & IMA_APPRAISE_ENFORCE))
return -EACCES; return -EACCES;
return 0; return 0;
@ -253,7 +268,7 @@ out:
int ima_file_mmap(struct file *file, unsigned long prot) int ima_file_mmap(struct file *file, unsigned long prot)
{ {
if (file && (prot & PROT_EXEC)) if (file && (prot & PROT_EXEC))
return process_measurement(file, NULL, MAY_EXEC, MMAP_CHECK); return process_measurement(file, MAY_EXEC, MMAP_CHECK, 0);
return 0; return 0;
} }
@ -272,10 +287,7 @@ int ima_file_mmap(struct file *file, unsigned long prot)
*/ */
int ima_bprm_check(struct linux_binprm *bprm) int ima_bprm_check(struct linux_binprm *bprm)
{ {
return process_measurement(bprm->file, return process_measurement(bprm->file, MAY_EXEC, BPRM_CHECK, 0);
(strcmp(bprm->filename, bprm->interp) == 0) ?
bprm->filename : bprm->interp,
MAY_EXEC, BPRM_CHECK);
} }
/** /**
@ -288,12 +300,11 @@ int ima_bprm_check(struct linux_binprm *bprm)
* On success return 0. On integrity appraisal error, assuming the file * On success return 0. On integrity appraisal error, assuming the file
* is in policy and IMA-appraisal is in enforcing mode, return -EACCES. * is in policy and IMA-appraisal is in enforcing mode, return -EACCES.
*/ */
int ima_file_check(struct file *file, int mask) int ima_file_check(struct file *file, int mask, int opened)
{ {
ima_rdwr_violation_check(file); return process_measurement(file,
return process_measurement(file, NULL,
mask & (MAY_READ | MAY_WRITE | MAY_EXEC), mask & (MAY_READ | MAY_WRITE | MAY_EXEC),
FILE_CHECK); FILE_CHECK, opened);
} }
EXPORT_SYMBOL_GPL(ima_file_check); EXPORT_SYMBOL_GPL(ima_file_check);
@ -316,7 +327,7 @@ int ima_module_check(struct file *file)
#endif #endif
return 0; /* We rely on module signature checking */ return 0; /* We rely on module signature checking */
} }
return process_measurement(file, NULL, MAY_EXEC, MODULE_CHECK); return process_measurement(file, MAY_EXEC, MODULE_CHECK, 0);
} }
int ima_fw_from_file(struct file *file, char *buf, size_t size) int ima_fw_from_file(struct file *file, char *buf, size_t size)
@ -327,7 +338,7 @@ int ima_fw_from_file(struct file *file, char *buf, size_t size)
return -EACCES; /* INTEGRITY_UNKNOWN */ return -EACCES; /* INTEGRITY_UNKNOWN */
return 0; return 0;
} }
return process_measurement(file, NULL, MAY_EXEC, FIRMWARE_CHECK); return process_measurement(file, MAY_EXEC, FIRMWARE_CHECK, 0);
} }
static int __init init_ima(void) static int __init init_ima(void)
@ -336,14 +347,10 @@ static int __init init_ima(void)
hash_setup(CONFIG_IMA_DEFAULT_HASH); hash_setup(CONFIG_IMA_DEFAULT_HASH);
error = ima_init(); error = ima_init();
if (error) if (!error) {
goto out; ima_initialized = 1;
ima_update_policy_flag();
error = ima_init_keyring(INTEGRITY_KEYRING_IMA); }
if (error)
goto out;
ima_initialized = 1;
out:
return error; return error;
} }

Просмотреть файл

@ -35,6 +35,8 @@
#define DONT_APPRAISE 0x0008 #define DONT_APPRAISE 0x0008
#define AUDIT 0x0040 #define AUDIT 0x0040
int ima_policy_flag;
#define MAX_LSM_RULES 6 #define MAX_LSM_RULES 6
enum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE, enum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE,
LSM_SUBJ_USER, LSM_SUBJ_ROLE, LSM_SUBJ_TYPE LSM_SUBJ_USER, LSM_SUBJ_ROLE, LSM_SUBJ_TYPE
@ -295,6 +297,26 @@ int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
return action; return action;
} }
/*
* Initialize the ima_policy_flag variable based on the currently
* loaded policy. Based on this flag, the decision to short circuit
* out of a function or not call the function in the first place
* can be made earlier.
*/
void ima_update_policy_flag(void)
{
struct ima_rule_entry *entry;
ima_policy_flag = 0;
list_for_each_entry(entry, ima_rules, list) {
if (entry->action & IMA_DO_MASK)
ima_policy_flag |= entry->action;
}
if (!ima_appraise)
ima_policy_flag &= ~IMA_APPRAISE;
}
/** /**
* ima_init_policy - initialize the default measure rules. * ima_init_policy - initialize the default measure rules.
* *
@ -341,6 +363,7 @@ void ima_update_policy(void)
if (ima_rules == &ima_default_rules) { if (ima_rules == &ima_default_rules) {
ima_rules = &ima_policy_rules; ima_rules = &ima_policy_rules;
ima_update_policy_flag();
cause = "complete"; cause = "complete";
result = 0; result = 0;
} }

Просмотреть файл

@ -152,24 +152,6 @@ out:
return result; return result;
} }
static int init_defined_templates(void)
{
int i = 0;
int result = 0;
/* Init defined templates. */
for (i = 0; i < ARRAY_SIZE(defined_templates); i++) {
struct ima_template_desc *template = &defined_templates[i];
result = template_desc_init_fields(template->fmt,
&(template->fields),
&(template->num_fields));
if (result < 0)
return result;
}
return result;
}
struct ima_template_desc *ima_template_desc_current(void) struct ima_template_desc *ima_template_desc_current(void)
{ {
if (!ima_template) if (!ima_template)
@ -178,13 +160,11 @@ struct ima_template_desc *ima_template_desc_current(void)
return ima_template; return ima_template;
} }
int ima_init_template(void) int __init ima_init_template(void)
{ {
int result; struct ima_template_desc *template = ima_template_desc_current();
result = init_defined_templates(); return template_desc_init_fields(template->fmt,
if (result < 0) &(template->fields),
return result; &(template->num_fields));
return 0;
} }

Просмотреть файл

@ -31,6 +31,7 @@
#define IMA_DIGSIG 0x01000000 #define IMA_DIGSIG 0x01000000
#define IMA_DIGSIG_REQUIRED 0x02000000 #define IMA_DIGSIG_REQUIRED 0x02000000
#define IMA_PERMIT_DIRECTIO 0x04000000 #define IMA_PERMIT_DIRECTIO 0x04000000
#define IMA_NEW_FILE 0x08000000
#define IMA_DO_MASK (IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT | \ #define IMA_DO_MASK (IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT | \
IMA_APPRAISE_SUBMASK) IMA_APPRAISE_SUBMASK)
@ -116,7 +117,6 @@ struct integrity_iint_cache {
/* rbtree tree calls to lookup, insert, delete /* rbtree tree calls to lookup, insert, delete
* integrity data associated with an inode. * integrity data associated with an inode.
*/ */
struct integrity_iint_cache *integrity_iint_insert(struct inode *inode);
struct integrity_iint_cache *integrity_iint_find(struct inode *inode); struct integrity_iint_cache *integrity_iint_find(struct inode *inode);
#define INTEGRITY_KEYRING_EVM 0 #define INTEGRITY_KEYRING_EVM 0

Просмотреть файл

@ -33,11 +33,9 @@ MODULE_LICENSE("GPL");
*/ */
struct key_type key_type_big_key = { struct key_type key_type_big_key = {
.name = "big_key", .name = "big_key",
.def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
.preparse = big_key_preparse, .preparse = big_key_preparse,
.free_preparse = big_key_free_preparse, .free_preparse = big_key_free_preparse,
.instantiate = generic_key_instantiate, .instantiate = generic_key_instantiate,
.match = user_match,
.revoke = big_key_revoke, .revoke = big_key_revoke,
.destroy = big_key_destroy, .destroy = big_key_destroy,
.describe = big_key_describe, .describe = big_key_describe,

Просмотреть файл

@ -970,7 +970,6 @@ struct key_type key_type_encrypted = {
.name = "encrypted", .name = "encrypted",
.instantiate = encrypted_instantiate, .instantiate = encrypted_instantiate,
.update = encrypted_update, .update = encrypted_update,
.match = user_match,
.destroy = encrypted_destroy, .destroy = encrypted_destroy,
.describe = user_describe, .describe = user_describe,
.read = encrypted_read, .read = encrypted_read,

Просмотреть файл

@ -107,20 +107,16 @@ extern int iterate_over_keyring(const struct key *keyring,
int (*func)(const struct key *key, void *data), int (*func)(const struct key *key, void *data),
void *data); void *data);
typedef int (*key_match_func_t)(const struct key *, const void *);
struct keyring_search_context { struct keyring_search_context {
struct keyring_index_key index_key; struct keyring_index_key index_key;
const struct cred *cred; const struct cred *cred;
key_match_func_t match; struct key_match_data match_data;
const void *match_data;
unsigned flags; unsigned flags;
#define KEYRING_SEARCH_LOOKUP_TYPE 0x0001 /* [as type->def_lookup_type] */ #define KEYRING_SEARCH_NO_STATE_CHECK 0x0001 /* Skip state checks */
#define KEYRING_SEARCH_NO_STATE_CHECK 0x0002 /* Skip state checks */ #define KEYRING_SEARCH_DO_STATE_CHECK 0x0002 /* Override NO_STATE_CHECK */
#define KEYRING_SEARCH_DO_STATE_CHECK 0x0004 /* Override NO_STATE_CHECK */ #define KEYRING_SEARCH_NO_UPDATE_TIME 0x0004 /* Don't update times */
#define KEYRING_SEARCH_NO_UPDATE_TIME 0x0008 /* Don't update times */ #define KEYRING_SEARCH_NO_CHECK_PERM 0x0008 /* Don't check permissions */
#define KEYRING_SEARCH_NO_CHECK_PERM 0x0010 /* Don't check permissions */ #define KEYRING_SEARCH_DETECT_TOO_DEEP 0x0010 /* Give an error on excessive depth */
#define KEYRING_SEARCH_DETECT_TOO_DEEP 0x0020 /* Give an error on excessive depth */
int (*iterator)(const void *object, void *iterator_data); int (*iterator)(const void *object, void *iterator_data);
@ -131,6 +127,8 @@ struct keyring_search_context {
struct timespec now; struct timespec now;
}; };
extern bool key_default_cmp(const struct key *key,
const struct key_match_data *match_data);
extern key_ref_t keyring_search_aux(key_ref_t keyring_ref, extern key_ref_t keyring_search_aux(key_ref_t keyring_ref,
struct keyring_search_context *ctx); struct keyring_search_context *ctx);
@ -152,7 +150,8 @@ extern struct key *request_key_and_link(struct key_type *type,
struct key *dest_keyring, struct key *dest_keyring,
unsigned long flags); unsigned long flags);
extern int lookup_user_key_possessed(const struct key *key, const void *target); extern bool lookup_user_key_possessed(const struct key *key,
const struct key_match_data *match_data);
extern key_ref_t lookup_user_key(key_serial_t id, unsigned long flags, extern key_ref_t lookup_user_key(key_serial_t id, unsigned long flags,
key_perm_t perm); key_perm_t perm);
#define KEY_LOOKUP_CREATE 0x01 #define KEY_LOOKUP_CREATE 0x01

Просмотреть файл

@ -799,7 +799,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
} }
key_ref = ERR_PTR(-EINVAL); key_ref = ERR_PTR(-EINVAL);
if (!index_key.type->match || !index_key.type->instantiate || if (!index_key.type->instantiate ||
(!index_key.description && !index_key.type->preparse)) (!index_key.description && !index_key.type->preparse))
goto error_put_type; goto error_put_type;

Просмотреть файл

@ -37,6 +37,8 @@ static int key_get_type_from_user(char *type,
return ret; return ret;
if (ret == 0 || ret >= len) if (ret == 0 || ret >= len)
return -EINVAL; return -EINVAL;
if (type[0] == '.')
return -EPERM;
type[len - 1] = '\0'; type[len - 1] = '\0';
return 0; return 0;
} }

Просмотреть файл

@ -89,7 +89,6 @@ struct key_type key_type_keyring = {
.preparse = keyring_preparse, .preparse = keyring_preparse,
.free_preparse = keyring_free_preparse, .free_preparse = keyring_free_preparse,
.instantiate = keyring_instantiate, .instantiate = keyring_instantiate,
.match = user_match,
.revoke = keyring_revoke, .revoke = keyring_revoke,
.destroy = keyring_destroy, .destroy = keyring_destroy,
.describe = keyring_describe, .describe = keyring_describe,
@ -511,6 +510,15 @@ struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid,
} }
EXPORT_SYMBOL(keyring_alloc); EXPORT_SYMBOL(keyring_alloc);
/*
* By default, we keys found by getting an exact match on their descriptions.
*/
bool key_default_cmp(const struct key *key,
const struct key_match_data *match_data)
{
return strcmp(key->description, match_data->raw_data) == 0;
}
/* /*
* Iteration function to consider each key found. * Iteration function to consider each key found.
*/ */
@ -545,7 +553,7 @@ static int keyring_search_iterator(const void *object, void *iterator_data)
} }
/* keys that don't match */ /* keys that don't match */
if (!ctx->match(key, ctx->match_data)) { if (!ctx->match_data.cmp(key, &ctx->match_data)) {
kleave(" = 0 [!match]"); kleave(" = 0 [!match]");
return 0; return 0;
} }
@ -585,8 +593,7 @@ skipped:
*/ */
static int search_keyring(struct key *keyring, struct keyring_search_context *ctx) static int search_keyring(struct key *keyring, struct keyring_search_context *ctx)
{ {
if ((ctx->flags & KEYRING_SEARCH_LOOKUP_TYPE) == if (ctx->match_data.lookup_type == KEYRING_SEARCH_LOOKUP_DIRECT) {
KEYRING_SEARCH_LOOKUP_DIRECT) {
const void *object; const void *object;
object = assoc_array_find(&keyring->keys, object = assoc_array_find(&keyring->keys,
@ -627,7 +634,7 @@ static bool search_nested_keyrings(struct key *keyring,
/* Check to see if this top-level keyring is what we are looking for /* Check to see if this top-level keyring is what we are looking for
* and whether it is valid or not. * and whether it is valid or not.
*/ */
if (ctx->flags & KEYRING_SEARCH_LOOKUP_ITERATE || if (ctx->match_data.lookup_type == KEYRING_SEARCH_LOOKUP_ITERATE ||
keyring_compare_object(keyring, &ctx->index_key)) { keyring_compare_object(keyring, &ctx->index_key)) {
ctx->skipped_ret = 2; ctx->skipped_ret = 2;
ctx->flags |= KEYRING_SEARCH_DO_STATE_CHECK; ctx->flags |= KEYRING_SEARCH_DO_STATE_CHECK;
@ -885,16 +892,25 @@ key_ref_t keyring_search(key_ref_t keyring,
.index_key.type = type, .index_key.type = type,
.index_key.description = description, .index_key.description = description,
.cred = current_cred(), .cred = current_cred(),
.match = type->match, .match_data.cmp = key_default_cmp,
.match_data = description, .match_data.raw_data = description,
.flags = (type->def_lookup_type | .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
KEYRING_SEARCH_DO_STATE_CHECK), .flags = KEYRING_SEARCH_DO_STATE_CHECK,
}; };
key_ref_t key;
int ret;
if (!ctx.match) if (type->match_preparse) {
return ERR_PTR(-ENOKEY); ret = type->match_preparse(&ctx.match_data);
if (ret < 0)
return ERR_PTR(ret);
}
return keyring_search_aux(keyring, &ctx); key = keyring_search_aux(keyring, &ctx);
if (type->match_free)
type->match_free(&ctx.match_data);
return key;
} }
EXPORT_SYMBOL(keyring_search); EXPORT_SYMBOL(keyring_search);
@ -1014,7 +1030,7 @@ static int keyring_detect_cycle_iterator(const void *object,
/* We might get a keyring with matching index-key that is nonetheless a /* We might get a keyring with matching index-key that is nonetheless a
* different keyring. */ * different keyring. */
if (key != ctx->match_data) if (key != ctx->match_data.raw_data)
return 0; return 0;
ctx->result = ERR_PTR(-EDEADLK); ctx->result = ERR_PTR(-EDEADLK);
@ -1031,14 +1047,14 @@ static int keyring_detect_cycle_iterator(const void *object,
static int keyring_detect_cycle(struct key *A, struct key *B) static int keyring_detect_cycle(struct key *A, struct key *B)
{ {
struct keyring_search_context ctx = { struct keyring_search_context ctx = {
.index_key = A->index_key, .index_key = A->index_key,
.match_data = A, .match_data.raw_data = A,
.iterator = keyring_detect_cycle_iterator, .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
.flags = (KEYRING_SEARCH_LOOKUP_DIRECT | .iterator = keyring_detect_cycle_iterator,
KEYRING_SEARCH_NO_STATE_CHECK | .flags = (KEYRING_SEARCH_NO_STATE_CHECK |
KEYRING_SEARCH_NO_UPDATE_TIME | KEYRING_SEARCH_NO_UPDATE_TIME |
KEYRING_SEARCH_NO_CHECK_PERM | KEYRING_SEARCH_NO_CHECK_PERM |
KEYRING_SEARCH_DETECT_TOO_DEEP), KEYRING_SEARCH_DETECT_TOO_DEEP),
}; };
rcu_read_lock(); rcu_read_lock();

Просмотреть файл

@ -194,10 +194,10 @@ static int proc_keys_show(struct seq_file *m, void *v)
.index_key.type = key->type, .index_key.type = key->type,
.index_key.description = key->description, .index_key.description = key->description,
.cred = current_cred(), .cred = current_cred(),
.match = lookup_user_key_possessed, .match_data.cmp = lookup_user_key_possessed,
.match_data = key, .match_data.raw_data = key,
.flags = (KEYRING_SEARCH_NO_STATE_CHECK | .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
KEYRING_SEARCH_LOOKUP_DIRECT), .flags = KEYRING_SEARCH_NO_STATE_CHECK,
}; };
key_ref = make_key_ref(key, 0); key_ref = make_key_ref(key, 0);

Просмотреть файл

@ -489,9 +489,10 @@ found:
/* /*
* See if the key we're looking at is the target key. * See if the key we're looking at is the target key.
*/ */
int lookup_user_key_possessed(const struct key *key, const void *target) bool lookup_user_key_possessed(const struct key *key,
const struct key_match_data *match_data)
{ {
return key == target; return key == match_data->raw_data;
} }
/* /*
@ -516,9 +517,9 @@ key_ref_t lookup_user_key(key_serial_t id, unsigned long lflags,
key_perm_t perm) key_perm_t perm)
{ {
struct keyring_search_context ctx = { struct keyring_search_context ctx = {
.match = lookup_user_key_possessed, .match_data.cmp = lookup_user_key_possessed,
.flags = (KEYRING_SEARCH_NO_STATE_CHECK | .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
KEYRING_SEARCH_LOOKUP_DIRECT), .flags = KEYRING_SEARCH_NO_STATE_CHECK,
}; };
struct request_key_auth *rka; struct request_key_auth *rka;
struct key *key; struct key *key;
@ -673,7 +674,7 @@ try_again:
ctx.index_key.type = key->type; ctx.index_key.type = key->type;
ctx.index_key.description = key->description; ctx.index_key.description = key->description;
ctx.index_key.desc_len = strlen(key->description); ctx.index_key.desc_len = strlen(key->description);
ctx.match_data = key; ctx.match_data.raw_data = key;
kdebug("check possessed"); kdebug("check possessed");
skey_ref = search_process_keyrings(&ctx); skey_ref = search_process_keyrings(&ctx);
kdebug("possessed=%p", skey_ref); kdebug("possessed=%p", skey_ref);

Просмотреть файл

@ -513,9 +513,9 @@ struct key *request_key_and_link(struct key_type *type,
.index_key.type = type, .index_key.type = type,
.index_key.description = description, .index_key.description = description,
.cred = current_cred(), .cred = current_cred(),
.match = type->match, .match_data.cmp = key_default_cmp,
.match_data = description, .match_data.raw_data = description,
.flags = KEYRING_SEARCH_LOOKUP_DIRECT, .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
}; };
struct key *key; struct key *key;
key_ref_t key_ref; key_ref_t key_ref;
@ -525,6 +525,14 @@ struct key *request_key_and_link(struct key_type *type,
ctx.index_key.type->name, ctx.index_key.description, ctx.index_key.type->name, ctx.index_key.description,
callout_info, callout_len, aux, dest_keyring, flags); callout_info, callout_len, aux, dest_keyring, flags);
if (type->match_preparse) {
ret = type->match_preparse(&ctx.match_data);
if (ret < 0) {
key = ERR_PTR(ret);
goto error;
}
}
/* search all the process keyrings for a key */ /* search all the process keyrings for a key */
key_ref = search_process_keyrings(&ctx); key_ref = search_process_keyrings(&ctx);
@ -537,7 +545,7 @@ struct key *request_key_and_link(struct key_type *type,
if (ret < 0) { if (ret < 0) {
key_put(key); key_put(key);
key = ERR_PTR(ret); key = ERR_PTR(ret);
goto error; goto error_free;
} }
} }
} else if (PTR_ERR(key_ref) != -EAGAIN) { } else if (PTR_ERR(key_ref) != -EAGAIN) {
@ -547,12 +555,15 @@ struct key *request_key_and_link(struct key_type *type,
* should consult userspace if we can */ * should consult userspace if we can */
key = ERR_PTR(-ENOKEY); key = ERR_PTR(-ENOKEY);
if (!callout_info) if (!callout_info)
goto error; goto error_free;
key = construct_key_and_link(&ctx, callout_info, callout_len, key = construct_key_and_link(&ctx, callout_info, callout_len,
aux, dest_keyring, flags); aux, dest_keyring, flags);
} }
error_free:
if (type->match_free)
type->match_free(&ctx.match_data);
error: error:
kleave(" = %p", key); kleave(" = %p", key);
return key; return key;

Просмотреть файл

@ -44,12 +44,12 @@ struct key_type key_type_request_key_auth = {
.read = request_key_auth_read, .read = request_key_auth_read,
}; };
int request_key_auth_preparse(struct key_preparsed_payload *prep) static int request_key_auth_preparse(struct key_preparsed_payload *prep)
{ {
return 0; return 0;
} }
void request_key_auth_free_preparse(struct key_preparsed_payload *prep) static void request_key_auth_free_preparse(struct key_preparsed_payload *prep)
{ {
} }
@ -246,9 +246,9 @@ struct key *key_get_instantiation_authkey(key_serial_t target_id)
.index_key.type = &key_type_request_key_auth, .index_key.type = &key_type_request_key_auth,
.index_key.description = description, .index_key.description = description,
.cred = current_cred(), .cred = current_cred(),
.match = user_match, .match_data.cmp = key_default_cmp,
.match_data = description, .match_data.raw_data = description,
.flags = KEYRING_SEARCH_LOOKUP_DIRECT, .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
}; };
struct key *authkey; struct key *authkey;
key_ref_t authkey_ref; key_ref_t authkey_ref;

Просмотреть файл

@ -1096,7 +1096,6 @@ struct key_type key_type_trusted = {
.name = "trusted", .name = "trusted",
.instantiate = trusted_instantiate, .instantiate = trusted_instantiate,
.update = trusted_update, .update = trusted_update,
.match = user_match,
.destroy = trusted_destroy, .destroy = trusted_destroy,
.describe = user_describe, .describe = user_describe,
.read = trusted_read, .read = trusted_read,

Просмотреть файл

@ -26,12 +26,10 @@ static int logon_vet_description(const char *desc);
*/ */
struct key_type key_type_user = { struct key_type key_type_user = {
.name = "user", .name = "user",
.def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
.preparse = user_preparse, .preparse = user_preparse,
.free_preparse = user_free_preparse, .free_preparse = user_free_preparse,
.instantiate = generic_key_instantiate, .instantiate = generic_key_instantiate,
.update = user_update, .update = user_update,
.match = user_match,
.revoke = user_revoke, .revoke = user_revoke,
.destroy = user_destroy, .destroy = user_destroy,
.describe = user_describe, .describe = user_describe,
@ -48,12 +46,10 @@ EXPORT_SYMBOL_GPL(key_type_user);
*/ */
struct key_type key_type_logon = { struct key_type key_type_logon = {
.name = "logon", .name = "logon",
.def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
.preparse = user_preparse, .preparse = user_preparse,
.free_preparse = user_free_preparse, .free_preparse = user_free_preparse,
.instantiate = generic_key_instantiate, .instantiate = generic_key_instantiate,
.update = user_update, .update = user_update,
.match = user_match,
.revoke = user_revoke, .revoke = user_revoke,
.destroy = user_destroy, .destroy = user_destroy,
.describe = user_describe, .describe = user_describe,
@ -138,16 +134,6 @@ error:
EXPORT_SYMBOL_GPL(user_update); EXPORT_SYMBOL_GPL(user_update);
/*
* match users on their name
*/
int user_match(const struct key *key, const void *description)
{
return strcmp(key->description, description) == 0;
}
EXPORT_SYMBOL_GPL(user_match);
/* /*
* dispose of the links from a revoked keyring * dispose of the links from a revoked keyring
* - called with the key sem write-locked * - called with the key sem write-locked

Просмотреть файл

@ -2097,6 +2097,41 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages)
/* binprm security operations */ /* binprm security operations */
static int check_nnp_nosuid(const struct linux_binprm *bprm,
const struct task_security_struct *old_tsec,
const struct task_security_struct *new_tsec)
{
int nnp = (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS);
int nosuid = (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID);
int rc;
if (!nnp && !nosuid)
return 0; /* neither NNP nor nosuid */
if (new_tsec->sid == old_tsec->sid)
return 0; /* No change in credentials */
/*
* The only transitions we permit under NNP or nosuid
* are transitions to bounded SIDs, i.e. SIDs that are
* guaranteed to only be allowed a subset of the permissions
* of the current SID.
*/
rc = security_bounded_transition(old_tsec->sid, new_tsec->sid);
if (rc) {
/*
* On failure, preserve the errno values for NNP vs nosuid.
* NNP: Operation not permitted for caller.
* nosuid: Permission denied to file.
*/
if (nnp)
return -EPERM;
else
return -EACCES;
}
return 0;
}
static int selinux_bprm_set_creds(struct linux_binprm *bprm) static int selinux_bprm_set_creds(struct linux_binprm *bprm)
{ {
const struct task_security_struct *old_tsec; const struct task_security_struct *old_tsec;
@ -2133,14 +2168,10 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm)
/* Reset exec SID on execve. */ /* Reset exec SID on execve. */
new_tsec->exec_sid = 0; new_tsec->exec_sid = 0;
/* /* Fail on NNP or nosuid if not an allowed transition. */
* Minimize confusion: if no_new_privs or nosuid and a rc = check_nnp_nosuid(bprm, old_tsec, new_tsec);
* transition is explicitly requested, then fail the exec. if (rc)
*/ return rc;
if (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS)
return -EPERM;
if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID)
return -EACCES;
} else { } else {
/* Check for a default transition on this program. */ /* Check for a default transition on this program. */
rc = security_transition_sid(old_tsec->sid, isec->sid, rc = security_transition_sid(old_tsec->sid, isec->sid,
@ -2148,15 +2179,19 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm)
&new_tsec->sid); &new_tsec->sid);
if (rc) if (rc)
return rc; return rc;
/*
* Fallback to old SID on NNP or nosuid if not an allowed
* transition.
*/
rc = check_nnp_nosuid(bprm, old_tsec, new_tsec);
if (rc)
new_tsec->sid = old_tsec->sid;
} }
ad.type = LSM_AUDIT_DATA_PATH; ad.type = LSM_AUDIT_DATA_PATH;
ad.u.path = bprm->file->f_path; ad.u.path = bprm->file->f_path;
if ((bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) ||
(bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS))
new_tsec->sid = old_tsec->sid;
if (new_tsec->sid == old_tsec->sid) { if (new_tsec->sid == old_tsec->sid) {
rc = avc_has_perm(old_tsec->sid, isec->sid, rc = avc_has_perm(old_tsec->sid, isec->sid,
SECCLASS_FILE, FILE__EXECUTE_NO_TRANS, &ad); SECCLASS_FILE, FILE__EXECUTE_NO_TRANS, &ad);
@ -4270,15 +4305,15 @@ static int selinux_socket_unix_may_send(struct socket *sock,
&ad); &ad);
} }
static int selinux_inet_sys_rcv_skb(int ifindex, char *addrp, u16 family, static int selinux_inet_sys_rcv_skb(struct net *ns, int ifindex,
u32 peer_sid, char *addrp, u16 family, u32 peer_sid,
struct common_audit_data *ad) struct common_audit_data *ad)
{ {
int err; int err;
u32 if_sid; u32 if_sid;
u32 node_sid; u32 node_sid;
err = sel_netif_sid(ifindex, &if_sid); err = sel_netif_sid(ns, ifindex, &if_sid);
if (err) if (err)
return err; return err;
err = avc_has_perm(peer_sid, if_sid, err = avc_has_perm(peer_sid, if_sid,
@ -4371,8 +4406,8 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
err = selinux_skb_peerlbl_sid(skb, family, &peer_sid); err = selinux_skb_peerlbl_sid(skb, family, &peer_sid);
if (err) if (err)
return err; return err;
err = selinux_inet_sys_rcv_skb(skb->skb_iif, addrp, family, err = selinux_inet_sys_rcv_skb(sock_net(sk), skb->skb_iif,
peer_sid, &ad); addrp, family, peer_sid, &ad);
if (err) { if (err) {
selinux_netlbl_err(skb, err, 0); selinux_netlbl_err(skb, err, 0);
return err; return err;
@ -4690,10 +4725,9 @@ static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb)
err = selinux_nlmsg_lookup(sksec->sclass, nlh->nlmsg_type, &perm); err = selinux_nlmsg_lookup(sksec->sclass, nlh->nlmsg_type, &perm);
if (err) { if (err) {
if (err == -EINVAL) { if (err == -EINVAL) {
audit_log(current->audit_context, GFP_KERNEL, AUDIT_SELINUX_ERR, WARN_ONCE(1, "selinux_nlmsg_perm: unrecognized netlink message:"
"SELinux: unrecognized netlink message" " protocol=%hu nlmsg_type=%hu sclass=%hu\n",
" type=%hu for sclass=%hu\n", sk->sk_protocol, nlh->nlmsg_type, sksec->sclass);
nlh->nlmsg_type, sksec->sclass);
if (!selinux_enforcing || security_get_allow_unknown()) if (!selinux_enforcing || security_get_allow_unknown())
err = 0; err = 0;
} }
@ -4711,7 +4745,8 @@ out:
#ifdef CONFIG_NETFILTER #ifdef CONFIG_NETFILTER
static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex, static unsigned int selinux_ip_forward(struct sk_buff *skb,
const struct net_device *indev,
u16 family) u16 family)
{ {
int err; int err;
@ -4737,14 +4772,14 @@ static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex,
ad.type = LSM_AUDIT_DATA_NET; ad.type = LSM_AUDIT_DATA_NET;
ad.u.net = &net; ad.u.net = &net;
ad.u.net->netif = ifindex; ad.u.net->netif = indev->ifindex;
ad.u.net->family = family; ad.u.net->family = family;
if (selinux_parse_skb(skb, &ad, &addrp, 1, NULL) != 0) if (selinux_parse_skb(skb, &ad, &addrp, 1, NULL) != 0)
return NF_DROP; return NF_DROP;
if (peerlbl_active) { if (peerlbl_active) {
err = selinux_inet_sys_rcv_skb(ifindex, addrp, family, err = selinux_inet_sys_rcv_skb(dev_net(indev), indev->ifindex,
peer_sid, &ad); addrp, family, peer_sid, &ad);
if (err) { if (err) {
selinux_netlbl_err(skb, err, 1); selinux_netlbl_err(skb, err, 1);
return NF_DROP; return NF_DROP;
@ -4773,7 +4808,7 @@ static unsigned int selinux_ipv4_forward(const struct nf_hook_ops *ops,
const struct net_device *out, const struct net_device *out,
int (*okfn)(struct sk_buff *)) int (*okfn)(struct sk_buff *))
{ {
return selinux_ip_forward(skb, in->ifindex, PF_INET); return selinux_ip_forward(skb, in, PF_INET);
} }
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
@ -4783,7 +4818,7 @@ static unsigned int selinux_ipv6_forward(const struct nf_hook_ops *ops,
const struct net_device *out, const struct net_device *out,
int (*okfn)(struct sk_buff *)) int (*okfn)(struct sk_buff *))
{ {
return selinux_ip_forward(skb, in->ifindex, PF_INET6); return selinux_ip_forward(skb, in, PF_INET6);
} }
#endif /* IPV6 */ #endif /* IPV6 */
@ -4871,11 +4906,13 @@ static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb,
return NF_ACCEPT; return NF_ACCEPT;
} }
static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex, static unsigned int selinux_ip_postroute(struct sk_buff *skb,
const struct net_device *outdev,
u16 family) u16 family)
{ {
u32 secmark_perm; u32 secmark_perm;
u32 peer_sid; u32 peer_sid;
int ifindex = outdev->ifindex;
struct sock *sk; struct sock *sk;
struct common_audit_data ad; struct common_audit_data ad;
struct lsm_network_audit net = {0,}; struct lsm_network_audit net = {0,};
@ -4956,6 +4993,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex,
case PF_INET6: case PF_INET6:
if (IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) if (IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED)
return NF_ACCEPT; return NF_ACCEPT;
break;
default: default:
return NF_DROP_ERR(-ECONNREFUSED); return NF_DROP_ERR(-ECONNREFUSED);
} }
@ -4987,7 +5025,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex,
u32 if_sid; u32 if_sid;
u32 node_sid; u32 node_sid;
if (sel_netif_sid(ifindex, &if_sid)) if (sel_netif_sid(dev_net(outdev), ifindex, &if_sid))
return NF_DROP; return NF_DROP;
if (avc_has_perm(peer_sid, if_sid, if (avc_has_perm(peer_sid, if_sid,
SECCLASS_NETIF, NETIF__EGRESS, &ad)) SECCLASS_NETIF, NETIF__EGRESS, &ad))
@ -5009,7 +5047,7 @@ static unsigned int selinux_ipv4_postroute(const struct nf_hook_ops *ops,
const struct net_device *out, const struct net_device *out,
int (*okfn)(struct sk_buff *)) int (*okfn)(struct sk_buff *))
{ {
return selinux_ip_postroute(skb, out->ifindex, PF_INET); return selinux_ip_postroute(skb, out, PF_INET);
} }
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
@ -5019,7 +5057,7 @@ static unsigned int selinux_ipv6_postroute(const struct nf_hook_ops *ops,
const struct net_device *out, const struct net_device *out,
int (*okfn)(struct sk_buff *)) int (*okfn)(struct sk_buff *))
{ {
return selinux_ip_postroute(skb, out->ifindex, PF_INET6); return selinux_ip_postroute(skb, out, PF_INET6);
} }
#endif /* IPV6 */ #endif /* IPV6 */
@ -6033,7 +6071,7 @@ security_initcall(selinux_init);
#if defined(CONFIG_NETFILTER) #if defined(CONFIG_NETFILTER)
static struct nf_hook_ops selinux_ipv4_ops[] = { static struct nf_hook_ops selinux_nf_ops[] = {
{ {
.hook = selinux_ipv4_postroute, .hook = selinux_ipv4_postroute,
.owner = THIS_MODULE, .owner = THIS_MODULE,
@ -6054,12 +6092,8 @@ static struct nf_hook_ops selinux_ipv4_ops[] = {
.pf = NFPROTO_IPV4, .pf = NFPROTO_IPV4,
.hooknum = NF_INET_LOCAL_OUT, .hooknum = NF_INET_LOCAL_OUT,
.priority = NF_IP_PRI_SELINUX_FIRST, .priority = NF_IP_PRI_SELINUX_FIRST,
} },
};
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
static struct nf_hook_ops selinux_ipv6_ops[] = {
{ {
.hook = selinux_ipv6_postroute, .hook = selinux_ipv6_postroute,
.owner = THIS_MODULE, .owner = THIS_MODULE,
@ -6073,32 +6107,24 @@ static struct nf_hook_ops selinux_ipv6_ops[] = {
.pf = NFPROTO_IPV6, .pf = NFPROTO_IPV6,
.hooknum = NF_INET_FORWARD, .hooknum = NF_INET_FORWARD,
.priority = NF_IP6_PRI_SELINUX_FIRST, .priority = NF_IP6_PRI_SELINUX_FIRST,
} },
};
#endif /* IPV6 */ #endif /* IPV6 */
};
static int __init selinux_nf_ip_init(void) static int __init selinux_nf_ip_init(void)
{ {
int err = 0; int err;
if (!selinux_enabled) if (!selinux_enabled)
goto out; return 0;
printk(KERN_DEBUG "SELinux: Registering netfilter hooks\n"); printk(KERN_DEBUG "SELinux: Registering netfilter hooks\n");
err = nf_register_hooks(selinux_ipv4_ops, ARRAY_SIZE(selinux_ipv4_ops)); err = nf_register_hooks(selinux_nf_ops, ARRAY_SIZE(selinux_nf_ops));
if (err) if (err)
panic("SELinux: nf_register_hooks for IPv4: error %d\n", err); panic("SELinux: nf_register_hooks: error %d\n", err);
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) return 0;
err = nf_register_hooks(selinux_ipv6_ops, ARRAY_SIZE(selinux_ipv6_ops));
if (err)
panic("SELinux: nf_register_hooks for IPv6: error %d\n", err);
#endif /* IPV6 */
out:
return err;
} }
__initcall(selinux_nf_ip_init); __initcall(selinux_nf_ip_init);
@ -6108,10 +6134,7 @@ static void selinux_nf_ip_exit(void)
{ {
printk(KERN_DEBUG "SELinux: Unregistering netfilter hooks\n"); printk(KERN_DEBUG "SELinux: Unregistering netfilter hooks\n");
nf_unregister_hooks(selinux_ipv4_ops, ARRAY_SIZE(selinux_ipv4_ops)); nf_unregister_hooks(selinux_nf_ops, ARRAY_SIZE(selinux_nf_ops));
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
nf_unregister_hooks(selinux_ipv6_ops, ARRAY_SIZE(selinux_ipv6_ops));
#endif /* IPV6 */
} }
#endif #endif

Просмотреть файл

@ -17,9 +17,11 @@
#ifndef _SELINUX_NETIF_H_ #ifndef _SELINUX_NETIF_H_
#define _SELINUX_NETIF_H_ #define _SELINUX_NETIF_H_
#include <net/net_namespace.h>
void sel_netif_flush(void); void sel_netif_flush(void);
int sel_netif_sid(int ifindex, u32 *sid); int sel_netif_sid(struct net *ns, int ifindex, u32 *sid);
#endif /* _SELINUX_NETIF_H_ */ #endif /* _SELINUX_NETIF_H_ */

Просмотреть файл

@ -24,6 +24,7 @@
#include <linux/binfmts.h> #include <linux/binfmts.h>
#include <linux/in.h> #include <linux/in.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <net/net_namespace.h>
#include "flask.h" #include "flask.h"
#include "avc.h" #include "avc.h"
@ -78,6 +79,7 @@ struct ipc_security_struct {
}; };
struct netif_security_struct { struct netif_security_struct {
struct net *ns; /* network namespace */
int ifindex; /* device index */ int ifindex; /* device index */
u32 sid; /* SID for this interface */ u32 sid; /* SID for this interface */
}; };

Просмотреть файл

@ -45,6 +45,7 @@ static struct list_head sel_netif_hash[SEL_NETIF_HASH_SIZE];
/** /**
* sel_netif_hashfn - Hashing function for the interface table * sel_netif_hashfn - Hashing function for the interface table
* @ns: the network namespace
* @ifindex: the network interface * @ifindex: the network interface
* *
* Description: * Description:
@ -52,13 +53,14 @@ static struct list_head sel_netif_hash[SEL_NETIF_HASH_SIZE];
* bucket number for the given interface. * bucket number for the given interface.
* *
*/ */
static inline u32 sel_netif_hashfn(int ifindex) static inline u32 sel_netif_hashfn(const struct net *ns, int ifindex)
{ {
return (ifindex & (SEL_NETIF_HASH_SIZE - 1)); return (((uintptr_t)ns + ifindex) & (SEL_NETIF_HASH_SIZE - 1));
} }
/** /**
* sel_netif_find - Search for an interface record * sel_netif_find - Search for an interface record
* @ns: the network namespace
* @ifindex: the network interface * @ifindex: the network interface
* *
* Description: * Description:
@ -66,15 +68,15 @@ static inline u32 sel_netif_hashfn(int ifindex)
* If an entry can not be found in the table return NULL. * If an entry can not be found in the table return NULL.
* *
*/ */
static inline struct sel_netif *sel_netif_find(int ifindex) static inline struct sel_netif *sel_netif_find(const struct net *ns,
int ifindex)
{ {
int idx = sel_netif_hashfn(ifindex); int idx = sel_netif_hashfn(ns, ifindex);
struct sel_netif *netif; struct sel_netif *netif;
list_for_each_entry_rcu(netif, &sel_netif_hash[idx], list) list_for_each_entry_rcu(netif, &sel_netif_hash[idx], list)
/* all of the devices should normally fit in the hash, so we if (net_eq(netif->nsec.ns, ns) &&
* optimize for that case */ netif->nsec.ifindex == ifindex)
if (likely(netif->nsec.ifindex == ifindex))
return netif; return netif;
return NULL; return NULL;
@ -96,7 +98,7 @@ static int sel_netif_insert(struct sel_netif *netif)
if (sel_netif_total >= SEL_NETIF_HASH_MAX) if (sel_netif_total >= SEL_NETIF_HASH_MAX)
return -ENOSPC; return -ENOSPC;
idx = sel_netif_hashfn(netif->nsec.ifindex); idx = sel_netif_hashfn(netif->nsec.ns, netif->nsec.ifindex);
list_add_rcu(&netif->list, &sel_netif_hash[idx]); list_add_rcu(&netif->list, &sel_netif_hash[idx]);
sel_netif_total++; sel_netif_total++;
@ -120,6 +122,7 @@ static void sel_netif_destroy(struct sel_netif *netif)
/** /**
* sel_netif_sid_slow - Lookup the SID of a network interface using the policy * sel_netif_sid_slow - Lookup the SID of a network interface using the policy
* @ns: the network namespace
* @ifindex: the network interface * @ifindex: the network interface
* @sid: interface SID * @sid: interface SID
* *
@ -130,7 +133,7 @@ static void sel_netif_destroy(struct sel_netif *netif)
* failure. * failure.
* *
*/ */
static int sel_netif_sid_slow(int ifindex, u32 *sid) static int sel_netif_sid_slow(struct net *ns, int ifindex, u32 *sid)
{ {
int ret; int ret;
struct sel_netif *netif; struct sel_netif *netif;
@ -140,7 +143,7 @@ static int sel_netif_sid_slow(int ifindex, u32 *sid)
/* NOTE: we always use init's network namespace since we don't /* NOTE: we always use init's network namespace since we don't
* currently support containers */ * currently support containers */
dev = dev_get_by_index(&init_net, ifindex); dev = dev_get_by_index(ns, ifindex);
if (unlikely(dev == NULL)) { if (unlikely(dev == NULL)) {
printk(KERN_WARNING printk(KERN_WARNING
"SELinux: failure in sel_netif_sid_slow()," "SELinux: failure in sel_netif_sid_slow(),"
@ -149,7 +152,7 @@ static int sel_netif_sid_slow(int ifindex, u32 *sid)
} }
spin_lock_bh(&sel_netif_lock); spin_lock_bh(&sel_netif_lock);
netif = sel_netif_find(ifindex); netif = sel_netif_find(ns, ifindex);
if (netif != NULL) { if (netif != NULL) {
*sid = netif->nsec.sid; *sid = netif->nsec.sid;
ret = 0; ret = 0;
@ -163,6 +166,7 @@ static int sel_netif_sid_slow(int ifindex, u32 *sid)
ret = security_netif_sid(dev->name, &new->nsec.sid); ret = security_netif_sid(dev->name, &new->nsec.sid);
if (ret != 0) if (ret != 0)
goto out; goto out;
new->nsec.ns = ns;
new->nsec.ifindex = ifindex; new->nsec.ifindex = ifindex;
ret = sel_netif_insert(new); ret = sel_netif_insert(new);
if (ret != 0) if (ret != 0)
@ -184,6 +188,7 @@ out:
/** /**
* sel_netif_sid - Lookup the SID of a network interface * sel_netif_sid - Lookup the SID of a network interface
* @ns: the network namespace
* @ifindex: the network interface * @ifindex: the network interface
* @sid: interface SID * @sid: interface SID
* *
@ -195,12 +200,12 @@ out:
* on failure. * on failure.
* *
*/ */
int sel_netif_sid(int ifindex, u32 *sid) int sel_netif_sid(struct net *ns, int ifindex, u32 *sid)
{ {
struct sel_netif *netif; struct sel_netif *netif;
rcu_read_lock(); rcu_read_lock();
netif = sel_netif_find(ifindex); netif = sel_netif_find(ns, ifindex);
if (likely(netif != NULL)) { if (likely(netif != NULL)) {
*sid = netif->nsec.sid; *sid = netif->nsec.sid;
rcu_read_unlock(); rcu_read_unlock();
@ -208,11 +213,12 @@ int sel_netif_sid(int ifindex, u32 *sid)
} }
rcu_read_unlock(); rcu_read_unlock();
return sel_netif_sid_slow(ifindex, sid); return sel_netif_sid_slow(ns, ifindex, sid);
} }
/** /**
* sel_netif_kill - Remove an entry from the network interface table * sel_netif_kill - Remove an entry from the network interface table
* @ns: the network namespace
* @ifindex: the network interface * @ifindex: the network interface
* *
* Description: * Description:
@ -220,13 +226,13 @@ int sel_netif_sid(int ifindex, u32 *sid)
* table if it exists. * table if it exists.
* *
*/ */
static void sel_netif_kill(int ifindex) static void sel_netif_kill(const struct net *ns, int ifindex)
{ {
struct sel_netif *netif; struct sel_netif *netif;
rcu_read_lock(); rcu_read_lock();
spin_lock_bh(&sel_netif_lock); spin_lock_bh(&sel_netif_lock);
netif = sel_netif_find(ifindex); netif = sel_netif_find(ns, ifindex);
if (netif) if (netif)
sel_netif_destroy(netif); sel_netif_destroy(netif);
spin_unlock_bh(&sel_netif_lock); spin_unlock_bh(&sel_netif_lock);
@ -257,11 +263,8 @@ static int sel_netif_netdev_notifier_handler(struct notifier_block *this,
{ {
struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct net_device *dev = netdev_notifier_info_to_dev(ptr);
if (dev_net(dev) != &init_net)
return NOTIFY_DONE;
if (event == NETDEV_DOWN) if (event == NETDEV_DOWN)
sel_netif_kill(dev->ifindex); sel_netif_kill(dev_net(dev), dev->ifindex);
return NOTIFY_DONE; return NOTIFY_DONE;
} }

Просмотреть файл

@ -728,7 +728,7 @@ static int security_validtrans_handle_fail(struct context *ocontext,
if (context_struct_to_string(tcontext, &t, &tlen)) if (context_struct_to_string(tcontext, &t, &tlen))
goto out; goto out;
audit_log(current->audit_context, GFP_ATOMIC, AUDIT_SELINUX_ERR, audit_log(current->audit_context, GFP_ATOMIC, AUDIT_SELINUX_ERR,
"security_validate_transition: denied for" "op=security_validate_transition seresult=denied"
" oldcontext=%s newcontext=%s taskcontext=%s tclass=%s", " oldcontext=%s newcontext=%s taskcontext=%s tclass=%s",
o, n, t, sym_name(&policydb, SYM_CLASSES, tclass-1)); o, n, t, sym_name(&policydb, SYM_CLASSES, tclass-1));
out: out:
@ -877,7 +877,7 @@ int security_bounded_transition(u32 old_sid, u32 new_sid)
audit_log(current->audit_context, audit_log(current->audit_context,
GFP_ATOMIC, AUDIT_SELINUX_ERR, GFP_ATOMIC, AUDIT_SELINUX_ERR,
"op=security_bounded_transition " "op=security_bounded_transition "
"result=denied " "seresult=denied "
"oldcontext=%s newcontext=%s", "oldcontext=%s newcontext=%s",
old_name, new_name); old_name, new_name);
} }
@ -1351,8 +1351,8 @@ static int compute_sid_handle_invalid_context(
if (context_struct_to_string(newcontext, &n, &nlen)) if (context_struct_to_string(newcontext, &n, &nlen))
goto out; goto out;
audit_log(current->audit_context, GFP_ATOMIC, AUDIT_SELINUX_ERR, audit_log(current->audit_context, GFP_ATOMIC, AUDIT_SELINUX_ERR,
"security_compute_sid: invalid context %s" "op=security_compute_sid invalid_context=%s"
" for scontext=%s" " scontext=%s"
" tcontext=%s" " tcontext=%s"
" tclass=%s", " tclass=%s",
n, s, t, sym_name(&policydb, SYM_CLASSES, tclass-1)); n, s, t, sym_name(&policydb, SYM_CLASSES, tclass-1));
@ -2607,8 +2607,10 @@ int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid)
rc = convert_context_handle_invalid_context(&newcon); rc = convert_context_handle_invalid_context(&newcon);
if (rc) { if (rc) {
if (!context_struct_to_string(&newcon, &s, &len)) { if (!context_struct_to_string(&newcon, &s, &len)) {
audit_log(current->audit_context, GFP_ATOMIC, AUDIT_SELINUX_ERR, audit_log(current->audit_context,
"security_sid_mls_copy: invalid context %s", s); GFP_ATOMIC, AUDIT_SELINUX_ERR,
"op=security_sid_mls_copy "
"invalid_context=%s", s);
kfree(s); kfree(s);
} }
goto out_unlock; goto out_unlock;

Просмотреть файл

@ -12,3 +12,19 @@ config SECURITY_SMACK
of other mandatory security schemes. of other mandatory security schemes.
If you are unsure how to answer this question, answer N. If you are unsure how to answer this question, answer N.
config SECURITY_SMACK_BRINGUP
bool "Reporting on access granted by Smack rules"
depends on SECURITY_SMACK
default n
help
Enable the bring-up ("b") access mode in Smack rules.
When access is granted by a rule with the "b" mode a
message about the access requested is generated. The
intention is that a process can be granted a wide set
of access initially with the bringup mode set on the
rules. The developer can use the information to
identify which rules are necessary and what accesses
may be inappropriate. The developer can reduce the
access rule set once the behavior is well understood.
This is a superior mechanism to the oft abused
"permissive" mode of other systems.

Просмотреть файл

@ -71,11 +71,11 @@ struct smack_known {
#define SMK_CIPSOLEN 24 #define SMK_CIPSOLEN 24
struct superblock_smack { struct superblock_smack {
char *smk_root; struct smack_known *smk_root;
char *smk_floor; struct smack_known *smk_floor;
char *smk_hat; struct smack_known *smk_hat;
char *smk_default; struct smack_known *smk_default;
int smk_initialized; int smk_initialized;
}; };
struct socket_smack { struct socket_smack {
@ -88,7 +88,7 @@ struct socket_smack {
* Inode smack data * Inode smack data
*/ */
struct inode_smack { struct inode_smack {
char *smk_inode; /* label of the fso */ struct smack_known *smk_inode; /* label of the fso */
struct smack_known *smk_task; /* label of the task */ struct smack_known *smk_task; /* label of the task */
struct smack_known *smk_mmap; /* label of the mmap domain */ struct smack_known *smk_mmap; /* label of the mmap domain */
struct mutex smk_lock; /* initialization lock */ struct mutex smk_lock; /* initialization lock */
@ -112,7 +112,7 @@ struct task_smack {
struct smack_rule { struct smack_rule {
struct list_head list; struct list_head list;
struct smack_known *smk_subject; struct smack_known *smk_subject;
char *smk_object; struct smack_known *smk_object;
int smk_access; int smk_access;
}; };
@ -123,7 +123,7 @@ struct smk_netlbladdr {
struct list_head list; struct list_head list;
struct sockaddr_in smk_host; /* network address */ struct sockaddr_in smk_host; /* network address */
struct in_addr smk_mask; /* network mask */ struct in_addr smk_mask; /* network mask */
char *smk_label; /* label */ struct smack_known *smk_label; /* label */
}; };
/* /*
@ -191,6 +191,7 @@ struct smk_port_label {
*/ */
#define MAY_TRANSMUTE 0x00001000 /* Controls directory labeling */ #define MAY_TRANSMUTE 0x00001000 /* Controls directory labeling */
#define MAY_LOCK 0x00002000 /* Locks should be writes, but ... */ #define MAY_LOCK 0x00002000 /* Locks should be writes, but ... */
#define MAY_BRINGUP 0x00004000 /* Report use of this rule */
/* /*
* Just to make the common cases easier to deal with * Just to make the common cases easier to deal with
@ -200,9 +201,9 @@ struct smk_port_label {
#define MAY_NOT 0 #define MAY_NOT 0
/* /*
* Number of access types used by Smack (rwxatl) * Number of access types used by Smack (rwxatlb)
*/ */
#define SMK_NUM_ACCESS_TYPE 6 #define SMK_NUM_ACCESS_TYPE 7
/* SMACK data */ /* SMACK data */
struct smack_audit_data { struct smack_audit_data {
@ -226,23 +227,23 @@ struct smk_audit_info {
/* /*
* These functions are in smack_lsm.c * These functions are in smack_lsm.c
*/ */
struct inode_smack *new_inode_smack(char *); struct inode_smack *new_inode_smack(struct smack_known *);
/* /*
* These functions are in smack_access.c * These functions are in smack_access.c
*/ */
int smk_access_entry(char *, char *, struct list_head *); int smk_access_entry(char *, char *, struct list_head *);
int smk_access(struct smack_known *, char *, int, struct smk_audit_info *); int smk_access(struct smack_known *, struct smack_known *,
int smk_tskacc(struct task_smack *, char *, u32, struct smk_audit_info *); int, struct smk_audit_info *);
int smk_curacc(char *, u32, struct smk_audit_info *); int smk_tskacc(struct task_smack *, struct smack_known *,
u32, struct smk_audit_info *);
int smk_curacc(struct smack_known *, u32, struct smk_audit_info *);
struct smack_known *smack_from_secid(const u32); struct smack_known *smack_from_secid(const u32);
char *smk_parse_smack(const char *string, int len); char *smk_parse_smack(const char *string, int len);
int smk_netlbl_mls(int, char *, struct netlbl_lsm_secattr *, int); int smk_netlbl_mls(int, char *, struct netlbl_lsm_secattr *, int);
char *smk_import(const char *, int);
struct smack_known *smk_import_entry(const char *, int); struct smack_known *smk_import_entry(const char *, int);
void smk_insert_entry(struct smack_known *skp); void smk_insert_entry(struct smack_known *skp);
struct smack_known *smk_find_entry(const char *); struct smack_known *smk_find_entry(const char *);
u32 smack_to_secid(const char *);
/* /*
* Shared data. * Shared data.
@ -252,7 +253,7 @@ extern int smack_cipso_mapped;
extern struct smack_known *smack_net_ambient; extern struct smack_known *smack_net_ambient;
extern struct smack_known *smack_onlycap; extern struct smack_known *smack_onlycap;
extern struct smack_known *smack_syslog_label; extern struct smack_known *smack_syslog_label;
extern const char *smack_cipso_option; extern struct smack_known smack_cipso_option;
extern int smack_ptrace_rule; extern int smack_ptrace_rule;
extern struct smack_known smack_known_floor; extern struct smack_known smack_known_floor;
@ -281,9 +282,9 @@ static inline int smk_inode_transmutable(const struct inode *isp)
} }
/* /*
* Present a pointer to the smack label in an inode blob. * Present a pointer to the smack label entry in an inode blob.
*/ */
static inline char *smk_of_inode(const struct inode *isp) static inline struct smack_known *smk_of_inode(const struct inode *isp)
{ {
struct inode_smack *sip = isp->i_security; struct inode_smack *sip = isp->i_security;
return sip->smk_inode; return sip->smk_inode;

Просмотреть файл

@ -94,7 +94,7 @@ int smk_access_entry(char *subject_label, char *object_label,
struct smack_rule *srp; struct smack_rule *srp;
list_for_each_entry_rcu(srp, rule_list, list) { list_for_each_entry_rcu(srp, rule_list, list) {
if (srp->smk_object == object_label && if (srp->smk_object->smk_known == object_label &&
srp->smk_subject->smk_known == subject_label) { srp->smk_subject->smk_known == subject_label) {
may = srp->smk_access; may = srp->smk_access;
break; break;
@ -111,8 +111,8 @@ int smk_access_entry(char *subject_label, char *object_label,
/** /**
* smk_access - determine if a subject has a specific access to an object * smk_access - determine if a subject has a specific access to an object
* @subject_known: a pointer to the subject's Smack label entry * @subject: a pointer to the subject's Smack label entry
* @object_label: a pointer to the object's Smack label * @object: a pointer to the object's Smack label entry
* @request: the access requested, in "MAY" format * @request: the access requested, in "MAY" format
* @a : a pointer to the audit data * @a : a pointer to the audit data
* *
@ -122,8 +122,8 @@ int smk_access_entry(char *subject_label, char *object_label,
* *
* Smack labels are shared on smack_list * Smack labels are shared on smack_list
*/ */
int smk_access(struct smack_known *subject_known, char *object_label, int smk_access(struct smack_known *subject, struct smack_known *object,
int request, struct smk_audit_info *a) int request, struct smk_audit_info *a)
{ {
int may = MAY_NOT; int may = MAY_NOT;
int rc = 0; int rc = 0;
@ -133,7 +133,7 @@ int smk_access(struct smack_known *subject_known, char *object_label,
* *
* A star subject can't access any object. * A star subject can't access any object.
*/ */
if (subject_known == &smack_known_star) { if (subject == &smack_known_star) {
rc = -EACCES; rc = -EACCES;
goto out_audit; goto out_audit;
} }
@ -142,28 +142,28 @@ int smk_access(struct smack_known *subject_known, char *object_label,
* Tasks cannot be assigned the internet label. * Tasks cannot be assigned the internet label.
* An internet subject can access any object. * An internet subject can access any object.
*/ */
if (object_label == smack_known_web.smk_known || if (object == &smack_known_web ||
subject_known == &smack_known_web) subject == &smack_known_web)
goto out_audit; goto out_audit;
/* /*
* A star object can be accessed by any subject. * A star object can be accessed by any subject.
*/ */
if (object_label == smack_known_star.smk_known) if (object == &smack_known_star)
goto out_audit; goto out_audit;
/* /*
* An object can be accessed in any way by a subject * An object can be accessed in any way by a subject
* with the same label. * with the same label.
*/ */
if (subject_known->smk_known == object_label) if (subject->smk_known == object->smk_known)
goto out_audit; goto out_audit;
/* /*
* A hat subject can read any object. * A hat subject can read any object.
* A floor object can be read by any subject. * A floor object can be read by any subject.
*/ */
if ((request & MAY_ANYREAD) == request) { if ((request & MAY_ANYREAD) == request) {
if (object_label == smack_known_floor.smk_known) if (object == &smack_known_floor)
goto out_audit; goto out_audit;
if (subject_known == &smack_known_hat) if (subject == &smack_known_hat)
goto out_audit; goto out_audit;
} }
/* /*
@ -174,27 +174,38 @@ int smk_access(struct smack_known *subject_known, char *object_label,
* indicates there is no entry for this pair. * indicates there is no entry for this pair.
*/ */
rcu_read_lock(); rcu_read_lock();
may = smk_access_entry(subject_known->smk_known, object_label, may = smk_access_entry(subject->smk_known, object->smk_known,
&subject_known->smk_rules); &subject->smk_rules);
rcu_read_unlock(); rcu_read_unlock();
if (may > 0 && (request & may) == request) if (may <= 0 || (request & may) != request) {
rc = -EACCES;
goto out_audit; goto out_audit;
}
#ifdef CONFIG_SECURITY_SMACK_BRINGUP
/*
* Return a positive value if using bringup mode.
* This allows the hooks to identify checks that
* succeed because of "b" rules.
*/
if (may & MAY_BRINGUP)
rc = MAY_BRINGUP;
#endif
rc = -EACCES;
out_audit: out_audit:
#ifdef CONFIG_AUDIT #ifdef CONFIG_AUDIT
if (a) if (a)
smack_log(subject_known->smk_known, object_label, request, smack_log(subject->smk_known, object->smk_known,
rc, a); request, rc, a);
#endif #endif
return rc; return rc;
} }
/** /**
* smk_tskacc - determine if a task has a specific access to an object * smk_tskacc - determine if a task has a specific access to an object
* @tsp: a pointer to the subject task * @tsp: a pointer to the subject's task
* @obj_label: a pointer to the object's Smack label * @obj_known: a pointer to the object's label entry
* @mode: the access requested, in "MAY" format * @mode: the access requested, in "MAY" format
* @a : common audit data * @a : common audit data
* *
@ -203,24 +214,25 @@ out_audit:
* non zero otherwise. It allows that the task may have the capability * non zero otherwise. It allows that the task may have the capability
* to override the rules. * to override the rules.
*/ */
int smk_tskacc(struct task_smack *subject, char *obj_label, int smk_tskacc(struct task_smack *tsp, struct smack_known *obj_known,
u32 mode, struct smk_audit_info *a) u32 mode, struct smk_audit_info *a)
{ {
struct smack_known *skp = smk_of_task(subject); struct smack_known *sbj_known = smk_of_task(tsp);
int may; int may;
int rc; int rc;
/* /*
* Check the global rule list * Check the global rule list
*/ */
rc = smk_access(skp, obj_label, mode, NULL); rc = smk_access(sbj_known, obj_known, mode, NULL);
if (rc == 0) { if (rc >= 0) {
/* /*
* If there is an entry in the task's rule list * If there is an entry in the task's rule list
* it can further restrict access. * it can further restrict access.
*/ */
may = smk_access_entry(skp->smk_known, obj_label, may = smk_access_entry(sbj_known->smk_known,
&subject->smk_rules); obj_known->smk_known,
&tsp->smk_rules);
if (may < 0) if (may < 0)
goto out_audit; goto out_audit;
if ((mode & may) == mode) if ((mode & may) == mode)
@ -237,14 +249,15 @@ int smk_tskacc(struct task_smack *subject, char *obj_label,
out_audit: out_audit:
#ifdef CONFIG_AUDIT #ifdef CONFIG_AUDIT
if (a) if (a)
smack_log(skp->smk_known, obj_label, mode, rc, a); smack_log(sbj_known->smk_known, obj_known->smk_known,
mode, rc, a);
#endif #endif
return rc; return rc;
} }
/** /**
* smk_curacc - determine if current has a specific access to an object * smk_curacc - determine if current has a specific access to an object
* @obj_label: a pointer to the object's Smack label * @obj_known: a pointer to the object's Smack label entry
* @mode: the access requested, in "MAY" format * @mode: the access requested, in "MAY" format
* @a : common audit data * @a : common audit data
* *
@ -253,11 +266,12 @@ out_audit:
* non zero otherwise. It allows that current may have the capability * non zero otherwise. It allows that current may have the capability
* to override the rules. * to override the rules.
*/ */
int smk_curacc(char *obj_label, u32 mode, struct smk_audit_info *a) int smk_curacc(struct smack_known *obj_known,
u32 mode, struct smk_audit_info *a)
{ {
struct task_smack *tsp = current_security(); struct task_smack *tsp = current_security();
return smk_tskacc(tsp, obj_label, mode, a); return smk_tskacc(tsp, obj_known, mode, a);
} }
#ifdef CONFIG_AUDIT #ifdef CONFIG_AUDIT
@ -328,6 +342,13 @@ void smack_log(char *subject_label, char *object_label, int request,
struct smack_audit_data *sad; struct smack_audit_data *sad;
struct common_audit_data *a = &ad->a; struct common_audit_data *a = &ad->a;
#ifdef CONFIG_SECURITY_SMACK_BRINGUP
/*
* The result may be positive in bringup mode.
*/
if (result > 0)
result = 0;
#endif
/* check if we have to log the current event */ /* check if we have to log the current event */
if (result != 0 && (log_policy & SMACK_AUDIT_DENIED) == 0) if (result != 0 && (log_policy & SMACK_AUDIT_DENIED) == 0)
return; return;
@ -543,27 +564,6 @@ unlockout:
return skp; return skp;
} }
/**
* smk_import - import a smack label
* @string: a text string that might be a Smack label
* @len: the maximum size, or zero if it is NULL terminated.
*
* Returns a pointer to the label in the label list that
* matches the passed string, adding it if necessary.
*/
char *smk_import(const char *string, int len)
{
struct smack_known *skp;
/* labels cannot begin with a '-' */
if (string[0] == '-')
return NULL;
skp = smk_import_entry(string, len);
if (skp == NULL)
return NULL;
return skp->smk_known;
}
/** /**
* smack_from_secid - find the Smack label associated with a secid * smack_from_secid - find the Smack label associated with a secid
* @secid: an integer that might be associated with a Smack label * @secid: an integer that might be associated with a Smack label
@ -590,19 +590,3 @@ struct smack_known *smack_from_secid(const u32 secid)
rcu_read_unlock(); rcu_read_unlock();
return &smack_known_invalid; return &smack_known_invalid;
} }
/**
* smack_to_secid - find the secid associated with a Smack label
* @smack: the Smack label
*
* Returns the appropriate secid if there is one,
* otherwise 0
*/
u32 smack_to_secid(const char *smack)
{
struct smack_known *skp = smk_find_entry(smack);
if (skp == NULL)
return 0;
return skp->smk_secid;
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -131,14 +131,17 @@ LIST_HEAD(smack_rule_list);
struct smack_parsed_rule { struct smack_parsed_rule {
struct smack_known *smk_subject; struct smack_known *smk_subject;
char *smk_object; struct smack_known *smk_object;
int smk_access1; int smk_access1;
int smk_access2; int smk_access2;
}; };
static int smk_cipso_doi_value = SMACK_CIPSO_DOI_DEFAULT; static int smk_cipso_doi_value = SMACK_CIPSO_DOI_DEFAULT;
const char *smack_cipso_option = SMACK_CIPSO_OPTION; struct smack_known smack_cipso_option = {
.smk_known = SMACK_CIPSO_OPTION,
.smk_secid = 0,
};
/* /*
* Values for parsing cipso rules * Values for parsing cipso rules
@ -304,6 +307,10 @@ static int smk_perm_from_str(const char *string)
case 'L': case 'L':
perm |= MAY_LOCK; perm |= MAY_LOCK;
break; break;
case 'b':
case 'B':
perm |= MAY_BRINGUP;
break;
default: default:
return perm; return perm;
} }
@ -335,7 +342,7 @@ static int smk_fill_rule(const char *subject, const char *object,
if (rule->smk_subject == NULL) if (rule->smk_subject == NULL)
return -EINVAL; return -EINVAL;
rule->smk_object = smk_import(object, len); rule->smk_object = smk_import_entry(object, len);
if (rule->smk_object == NULL) if (rule->smk_object == NULL)
return -EINVAL; return -EINVAL;
} else { } else {
@ -355,7 +362,7 @@ static int smk_fill_rule(const char *subject, const char *object,
kfree(cp); kfree(cp);
if (skp == NULL) if (skp == NULL)
return -ENOENT; return -ENOENT;
rule->smk_object = skp->smk_known; rule->smk_object = skp;
} }
rule->smk_access1 = smk_perm_from_str(access1); rule->smk_access1 = smk_perm_from_str(access1);
@ -594,13 +601,15 @@ static void smk_rule_show(struct seq_file *s, struct smack_rule *srp, int max)
* anything you read back. * anything you read back.
*/ */
if (strlen(srp->smk_subject->smk_known) >= max || if (strlen(srp->smk_subject->smk_known) >= max ||
strlen(srp->smk_object) >= max) strlen(srp->smk_object->smk_known) >= max)
return; return;
if (srp->smk_access == 0) if (srp->smk_access == 0)
return; return;
seq_printf(s, "%s %s", srp->smk_subject->smk_known, srp->smk_object); seq_printf(s, "%s %s",
srp->smk_subject->smk_known,
srp->smk_object->smk_known);
seq_putc(s, ' '); seq_putc(s, ' ');
@ -616,6 +625,8 @@ static void smk_rule_show(struct seq_file *s, struct smack_rule *srp, int max)
seq_putc(s, 't'); seq_putc(s, 't');
if (srp->smk_access & MAY_LOCK) if (srp->smk_access & MAY_LOCK)
seq_putc(s, 'l'); seq_putc(s, 'l');
if (srp->smk_access & MAY_BRINGUP)
seq_putc(s, 'b');
seq_putc(s, '\n'); seq_putc(s, '\n');
} }
@ -1067,7 +1078,7 @@ static int netlbladdr_seq_show(struct seq_file *s, void *v)
for (maskn = 0; temp_mask; temp_mask <<= 1, maskn++); for (maskn = 0; temp_mask; temp_mask <<= 1, maskn++);
seq_printf(s, "%u.%u.%u.%u/%d %s\n", seq_printf(s, "%u.%u.%u.%u/%d %s\n",
hp[0], hp[1], hp[2], hp[3], maskn, skp->smk_label); hp[0], hp[1], hp[2], hp[3], maskn, skp->smk_label->smk_known);
return 0; return 0;
} }
@ -1147,10 +1158,10 @@ static void smk_netlbladdr_insert(struct smk_netlbladdr *new)
static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf, static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
size_t count, loff_t *ppos) size_t count, loff_t *ppos)
{ {
struct smk_netlbladdr *skp; struct smk_netlbladdr *snp;
struct sockaddr_in newname; struct sockaddr_in newname;
char *smack; char *smack;
char *sp; struct smack_known *skp;
char *data; char *data;
char *host = (char *)&newname.sin_addr.s_addr; char *host = (char *)&newname.sin_addr.s_addr;
int rc; int rc;
@ -1213,15 +1224,15 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
* If smack begins with '-', it is an option, don't import it * If smack begins with '-', it is an option, don't import it
*/ */
if (smack[0] != '-') { if (smack[0] != '-') {
sp = smk_import(smack, 0); skp = smk_import_entry(smack, 0);
if (sp == NULL) { if (skp == NULL) {
rc = -EINVAL; rc = -EINVAL;
goto free_out; goto free_out;
} }
} else { } else {
/* check known options */ /* check known options */
if (strcmp(smack, smack_cipso_option) == 0) if (strcmp(smack, smack_cipso_option.smk_known) == 0)
sp = (char *)smack_cipso_option; skp = &smack_cipso_option;
else { else {
rc = -EINVAL; rc = -EINVAL;
goto free_out; goto free_out;
@ -1244,9 +1255,9 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
nsa = newname.sin_addr.s_addr; nsa = newname.sin_addr.s_addr;
/* try to find if the prefix is already in the list */ /* try to find if the prefix is already in the list */
found = 0; found = 0;
list_for_each_entry_rcu(skp, &smk_netlbladdr_list, list) { list_for_each_entry_rcu(snp, &smk_netlbladdr_list, list) {
if (skp->smk_host.sin_addr.s_addr == nsa && if (snp->smk_host.sin_addr.s_addr == nsa &&
skp->smk_mask.s_addr == mask.s_addr) { snp->smk_mask.s_addr == mask.s_addr) {
found = 1; found = 1;
break; break;
} }
@ -1254,26 +1265,26 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
smk_netlabel_audit_set(&audit_info); smk_netlabel_audit_set(&audit_info);
if (found == 0) { if (found == 0) {
skp = kzalloc(sizeof(*skp), GFP_KERNEL); snp = kzalloc(sizeof(*snp), GFP_KERNEL);
if (skp == NULL) if (snp == NULL)
rc = -ENOMEM; rc = -ENOMEM;
else { else {
rc = 0; rc = 0;
skp->smk_host.sin_addr.s_addr = newname.sin_addr.s_addr; snp->smk_host.sin_addr.s_addr = newname.sin_addr.s_addr;
skp->smk_mask.s_addr = mask.s_addr; snp->smk_mask.s_addr = mask.s_addr;
skp->smk_label = sp; snp->smk_label = skp;
smk_netlbladdr_insert(skp); smk_netlbladdr_insert(snp);
} }
} else { } else {
/* we delete the unlabeled entry, only if the previous label /* we delete the unlabeled entry, only if the previous label
* wasn't the special CIPSO option */ * wasn't the special CIPSO option */
if (skp->smk_label != smack_cipso_option) if (snp->smk_label != &smack_cipso_option)
rc = netlbl_cfg_unlbl_static_del(&init_net, NULL, rc = netlbl_cfg_unlbl_static_del(&init_net, NULL,
&skp->smk_host.sin_addr, &skp->smk_mask, &snp->smk_host.sin_addr, &snp->smk_mask,
PF_INET, &audit_info); PF_INET, &audit_info);
else else
rc = 0; rc = 0;
skp->smk_label = sp; snp->smk_label = skp;
} }
/* /*
@ -1281,10 +1292,10 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
* this host so that incoming packets get labeled. * this host so that incoming packets get labeled.
* but only if we didn't get the special CIPSO option * but only if we didn't get the special CIPSO option
*/ */
if (rc == 0 && sp != smack_cipso_option) if (rc == 0 && skp != &smack_cipso_option)
rc = netlbl_cfg_unlbl_static_add(&init_net, NULL, rc = netlbl_cfg_unlbl_static_add(&init_net, NULL,
&skp->smk_host.sin_addr, &skp->smk_mask, PF_INET, &snp->smk_host.sin_addr, &snp->smk_mask, PF_INET,
smack_to_secid(skp->smk_label), &audit_info); snp->smk_label->smk_secid, &audit_info);
if (rc == 0) if (rc == 0)
rc = count; rc = count;
@ -1677,7 +1688,7 @@ static ssize_t smk_write_onlycap(struct file *file, const char __user *buf,
if (smack_onlycap != NULL && smack_onlycap != skp) if (smack_onlycap != NULL && smack_onlycap != skp)
return -EPERM; return -EPERM;
data = kzalloc(count, GFP_KERNEL); data = kzalloc(count + 1, GFP_KERNEL);
if (data == NULL) if (data == NULL)
return -ENOMEM; return -ENOMEM;
@ -1880,7 +1891,10 @@ static ssize_t smk_user_access(struct file *file, const char __user *buf,
else if (res != -ENOENT) else if (res != -ENOENT)
return -EINVAL; return -EINVAL;
data[0] = res == 0 ? '1' : '0'; /*
* smk_access() can return a value > 0 in the "bringup" case.
*/
data[0] = res >= 0 ? '1' : '0';
data[1] = '\0'; data[1] = '\0';
simple_transaction_set(file, 2); simple_transaction_set(file, 2);
@ -2228,7 +2242,7 @@ static ssize_t smk_write_syslog(struct file *file, const char __user *buf,
if (!smack_privileged(CAP_MAC_ADMIN)) if (!smack_privileged(CAP_MAC_ADMIN))
return -EPERM; return -EPERM;
data = kzalloc(count, GFP_KERNEL); data = kzalloc(count + 1, GFP_KERNEL);
if (data == NULL) if (data == NULL)
return -ENOMEM; return -ENOMEM;