From 341e0cb593a2b7ec86dd6ca96c68eadc3f6fe1e6 Mon Sep 17 00:00:00 2001 From: Janak Desai Date: Mon, 28 Mar 2016 11:09:46 -0400 Subject: netlabel: fix a problem with netlbl_secattr_catmap_setrng() We try to be clever and set large chunks of the bitmap at once, when possible; unfortunately we weren't very clever when we wrote the code and messed up the if-conditional. Fix this bug and restore proper operation. Signed-off-by: Janak Desai Signed-off-by: Paul Moore diff --git a/net/netlabel/netlabel_kapi.c b/net/netlabel/netlabel_kapi.c index 28cddc8..1325776 100644 --- a/net/netlabel/netlabel_kapi.c +++ b/net/netlabel/netlabel_kapi.c @@ -677,7 +677,7 @@ int netlbl_catmap_setrng(struct netlbl_lsm_catmap **catmap, u32 spot = start; while (rc == 0 && spot <= end) { - if (((spot & (BITS_PER_LONG - 1)) != 0) && + if (((spot & (BITS_PER_LONG - 1)) == 0) && ((end - spot) > BITS_PER_LONG)) { rc = netlbl_catmap_setlong(catmap, spot, -- cgit v0.10.2 From 899134f2f6e27dcae1fee12593c492577cc80987 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Mon, 28 Mar 2016 15:19:10 -0400 Subject: selinux: don't revalidate inodes in selinux_socket_getpeersec_dgram() We don't have to worry about socket inodes being invalidated so use inode_security_novalidate() to fetch the inode's security blob. Signed-off-by: Paul Moore diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 912deee..65642be 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -4598,6 +4598,7 @@ static int selinux_socket_getpeersec_dgram(struct socket *sock, struct sk_buff * { u32 peer_secid = SECSID_NULL; u16 family; + struct inode_security_struct *isec; if (skb && skb->protocol == htons(ETH_P_IP)) family = PF_INET; @@ -4608,9 +4609,10 @@ static int selinux_socket_getpeersec_dgram(struct socket *sock, struct sk_buff * else goto out; - if (sock && family == PF_UNIX) - selinux_inode_getsecid(SOCK_INODE(sock), &peer_secid); - else if (skb) + if (sock && family == PF_UNIX) { + isec = inode_security_novalidate(SOCK_INODE(sock)); + peer_secid = isec->sid; + } else if (skb) selinux_skb_peerlbl_sid(skb, family, &peer_secid); out: -- cgit v0.10.2 From 4b57d6bcd94034e2eb168bdec2474e3b2b848e44 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Mon, 28 Mar 2016 15:16:53 -0400 Subject: selinux: simply inode label states to INVALID and INITIALIZED There really is no need for LABEL_MISSING as we really only care if the inode's label is INVALID or INITIALIZED. Also adjust the revalidate code to reload the label whenever the label is not INITIALIZED so we are less sensitive to label state in the future. Signed-off-by: Paul Moore diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 65642be..dd1fbea 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -259,7 +259,7 @@ static int __inode_security_revalidate(struct inode *inode, might_sleep_if(may_sleep); - if (isec->initialized == LABEL_INVALID) { + if (isec->initialized != LABEL_INITIALIZED) { if (!may_sleep) return -ECHILD; diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h index a2ae054..c21e135 100644 --- a/security/selinux/include/objsec.h +++ b/security/selinux/include/objsec.h @@ -38,9 +38,8 @@ struct task_security_struct { }; enum label_initialized { - LABEL_MISSING, /* not initialized */ - LABEL_INITIALIZED, /* inizialized */ - LABEL_INVALID /* invalid */ + LABEL_INVALID, /* invalid or not initialized */ + LABEL_INITIALIZED /* initialized */ }; struct inode_security_struct { -- cgit v0.10.2 From 0c6181cb301fd04a5800920ba423be753b0a4a01 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Wed, 30 Mar 2016 21:41:21 -0400 Subject: selinux: consolidate the ptrace parent lookup code We lookup the tracing parent in two places, using effectively the same code, let's consolidate it. Signed-off-by: Paul Moore diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index dd1fbea..5003b5a 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2229,6 +2229,20 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages) /* binprm security operations */ +static u32 ptrace_parent_sid(struct task_struct *task) +{ + u32 sid = 0; + struct task_struct *tracer; + + rcu_read_lock(); + tracer = ptrace_parent(task); + if (tracer) + sid = task_sid(tracer); + rcu_read_unlock(); + + return sid; +} + static int check_nnp_nosuid(const struct linux_binprm *bprm, const struct task_security_struct *old_tsec, const struct task_security_struct *new_tsec) @@ -2350,18 +2364,7 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm) * changes its SID has the appropriate permit */ if (bprm->unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) { - struct task_struct *tracer; - struct task_security_struct *sec; - u32 ptsid = 0; - - rcu_read_lock(); - tracer = ptrace_parent(current); - if (likely(tracer != NULL)) { - sec = __task_cred(tracer)->security; - ptsid = sec->sid; - } - rcu_read_unlock(); - + u32 ptsid = ptrace_parent_sid(current); if (ptsid != 0) { rc = avc_has_perm(ptsid, new_tsec->sid, SECCLASS_PROCESS, @@ -5677,7 +5680,6 @@ static int selinux_setprocattr(struct task_struct *p, char *name, void *value, size_t size) { struct task_security_struct *tsec; - struct task_struct *tracer; struct cred *new; u32 sid = 0, ptsid; int error; @@ -5784,14 +5786,8 @@ static int selinux_setprocattr(struct task_struct *p, /* Check for ptracing, and update the task SID if ok. Otherwise, leave SID unchanged and fail. */ - ptsid = 0; - rcu_read_lock(); - tracer = ptrace_parent(p); - if (tracer) - ptsid = task_sid(tracer); - rcu_read_unlock(); - - if (tracer) { + ptsid = ptrace_parent_sid(p); + if (ptsid != 0) { error = avc_has_perm(ptsid, sid, SECCLASS_PROCESS, PROCESS__PTRACE, NULL); if (error) -- cgit v0.10.2 From 61d612ea731e57dc510472fb746b55cdc017f371 Mon Sep 17 00:00:00 2001 From: Jeff Vander Stoep Date: Tue, 5 Apr 2016 13:06:27 -0700 Subject: selinux: restrict kernel module loading Utilize existing kernel_read_file hook on kernel module load. Add module_load permission to the system class. Enforces restrictions on kernel module origin when calling the finit_module syscall. The hook checks that source type has permission module_load for the target type. Example for finit_module: allow foo bar_file:system module_load; Similarly restrictions are enforced on kernel module loading when calling the init_module syscall. The hook checks that source type has permission module_load with itself as the target object because the kernel module is sourced from the calling process. Example for init_module: allow foo foo:system module_load; Signed-off-by: Jeff Vander Stoep [PM: fixed return value of selinux_kernel_read_file()] Signed-off-by: Paul Moore diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 5003b5a..fce7dc8 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3722,6 +3722,52 @@ static int selinux_kernel_module_request(char *kmod_name) SYSTEM__MODULE_REQUEST, &ad); } +static int selinux_kernel_module_from_file(struct file *file) +{ + struct common_audit_data ad; + struct inode_security_struct *isec; + struct file_security_struct *fsec; + u32 sid = current_sid(); + int rc; + + /* init_module */ + if (file == NULL) + return avc_has_perm(sid, sid, SECCLASS_SYSTEM, + SYSTEM__MODULE_LOAD, NULL); + + /* finit_module */ + ad.type = LSM_AUDIT_DATA_PATH; + ad.u.path = file->f_path; + + isec = inode_security(file_inode(file)); + fsec = file->f_security; + + if (sid != fsec->sid) { + rc = avc_has_perm(sid, fsec->sid, SECCLASS_FD, FD__USE, &ad); + if (rc) + return rc; + } + + return avc_has_perm(sid, isec->sid, SECCLASS_SYSTEM, + SYSTEM__MODULE_LOAD, &ad); +} + +static int selinux_kernel_read_file(struct file *file, + enum kernel_read_file_id id) +{ + int rc = 0; + + switch (id) { + case READING_MODULE: + rc = selinux_kernel_module_from_file(file); + break; + default: + break; + } + + return rc; +} + static int selinux_task_setpgid(struct task_struct *p, pid_t pgid) { return current_has_perm(p, PROCESS__SETPGID); @@ -6018,6 +6064,7 @@ static struct security_hook_list selinux_hooks[] = { LSM_HOOK_INIT(kernel_act_as, selinux_kernel_act_as), LSM_HOOK_INIT(kernel_create_files_as, selinux_kernel_create_files_as), LSM_HOOK_INIT(kernel_module_request, selinux_kernel_module_request), + LSM_HOOK_INIT(kernel_read_file, selinux_kernel_read_file), LSM_HOOK_INIT(task_setpgid, selinux_task_setpgid), LSM_HOOK_INIT(task_getpgid, selinux_task_getpgid), LSM_HOOK_INIT(task_getsid, selinux_task_getsid), diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h index ef83c4b..8fbd138 100644 --- a/security/selinux/include/classmap.h +++ b/security/selinux/include/classmap.h @@ -32,7 +32,7 @@ struct security_class_mapping secclass_map[] = { "setsockcreate", NULL } }, { "system", { "ipc_info", "syslog_read", "syslog_mod", - "syslog_console", "module_request", NULL } }, + "syslog_console", "module_request", "module_load", NULL } }, { "capability", { "chown", "dac_override", "dac_read_search", "fowner", "fsetid", "kill", "setgid", "setuid", "setpcap", -- cgit v0.10.2 From 864e7a816a0646a6d9aecbd59a8e366c39b8ad2d Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 6 Apr 2016 16:13:33 +0100 Subject: X.509: Whitespace cleanup Clean up some whitespace. Signed-off-by: David Howells diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c index 733c046..2fcf707 100644 --- a/crypto/asymmetric_keys/x509_public_key.c +++ b/crypto/asymmetric_keys/x509_public_key.c @@ -88,7 +88,7 @@ struct key *x509_request_asymmetric_key(struct key *keyring, lookup = skid->data; len = skid->len; } - + /* Construct an identifier "id:". */ p = req = kmalloc(2 + 1 + len * 2 + 1, GFP_KERNEL); if (!req) @@ -137,7 +137,7 @@ struct key *x509_request_asymmetric_key(struct key *keyring, goto reject; } } - + pr_devel("<==%s() = 0 [%x]\n", __func__, key_serial(key)); return key; -- cgit v0.10.2 From 3b764563177c1e435ef3e2608271c07955f73ea6 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 6 Apr 2016 16:13:33 +0100 Subject: KEYS: Allow authentication data to be stored in an asymmetric key Allow authentication data to be stored in an asymmetric key in the 4th element of the key payload and provide a way for it to be destroyed. For the public key subtype, this will be a public_key_signature struct. Signed-off-by: David Howells diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c index 9f2165b..a79d301 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -331,7 +331,8 @@ static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep) pr_devel("==>%s()\n", __func__); if (subtype) { - subtype->destroy(prep->payload.data[asym_crypto]); + subtype->destroy(prep->payload.data[asym_crypto], + prep->payload.data[asym_auth]); module_put(subtype->owner); } asymmetric_key_free_kids(kids); @@ -346,13 +347,15 @@ static void asymmetric_key_destroy(struct key *key) struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key); struct asymmetric_key_ids *kids = key->payload.data[asym_key_ids]; void *data = key->payload.data[asym_crypto]; + void *auth = key->payload.data[asym_auth]; key->payload.data[asym_crypto] = NULL; key->payload.data[asym_subtype] = NULL; key->payload.data[asym_key_ids] = NULL; + key->payload.data[asym_auth] = NULL; if (subtype) { - subtype->destroy(data); + subtype->destroy(data, auth); module_put(subtype->owner); } diff --git a/crypto/asymmetric_keys/public_key.c b/crypto/asymmetric_keys/public_key.c index 0f8b264..fd76b5f 100644 --- a/crypto/asymmetric_keys/public_key.c +++ b/crypto/asymmetric_keys/public_key.c @@ -39,15 +39,23 @@ static void public_key_describe(const struct key *asymmetric_key, /* * Destroy a public key algorithm key. */ -void public_key_destroy(void *payload) +void public_key_free(struct public_key *key) { - struct public_key *key = payload; - - if (key) + if (key) { kfree(key->key); - kfree(key); + kfree(key); + } +} +EXPORT_SYMBOL_GPL(public_key_free); + +/* + * Destroy a public key algorithm key. + */ +static void public_key_destroy(void *payload0, void *payload3) +{ + public_key_free(payload0); + public_key_signature_free(payload3); } -EXPORT_SYMBOL_GPL(public_key_destroy); struct public_key_completion { struct completion completion; diff --git a/crypto/asymmetric_keys/signature.c b/crypto/asymmetric_keys/signature.c index 004d5fc..3beee39 100644 --- a/crypto/asymmetric_keys/signature.c +++ b/crypto/asymmetric_keys/signature.c @@ -15,9 +15,23 @@ #include #include #include +#include #include #include "asymmetric_keys.h" +/* + * Destroy a public key signature. + */ +void public_key_signature_free(struct public_key_signature *sig) +{ + if (sig) { + kfree(sig->s); + kfree(sig->digest); + kfree(sig); + } +} +EXPORT_SYMBOL_GPL(public_key_signature_free); + /** * verify_signature - Initiate the use of an asymmetric key to verify a signature * @key: The asymmetric key to verify against diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c index 4a29bac..05251c7 100644 --- a/crypto/asymmetric_keys/x509_cert_parser.c +++ b/crypto/asymmetric_keys/x509_cert_parser.c @@ -47,7 +47,7 @@ struct x509_parse_context { void x509_free_certificate(struct x509_certificate *cert) { if (cert) { - public_key_destroy(cert->pub); + public_key_free(cert->pub); kfree(cert->issuer); kfree(cert->subject); kfree(cert->id); diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h index aa730ea..19f557c 100644 --- a/include/crypto/public_key.h +++ b/include/crypto/public_key.h @@ -41,7 +41,7 @@ struct public_key { const char *pkey_algo; }; -extern void public_key_destroy(void *payload); +extern void public_key_free(struct public_key *key); /* * Public key cryptography signature data @@ -55,7 +55,10 @@ struct public_key_signature { const char *hash_algo; }; +extern void public_key_signature_free(struct public_key_signature *sig); + extern struct asymmetric_key_subtype public_key_subtype; + struct key; extern int verify_signature(const struct key *key, const struct public_key_signature *sig); diff --git a/include/keys/asymmetric-subtype.h b/include/keys/asymmetric-subtype.h index 4915d40..2480469 100644 --- a/include/keys/asymmetric-subtype.h +++ b/include/keys/asymmetric-subtype.h @@ -32,7 +32,7 @@ struct asymmetric_key_subtype { void (*describe)(const struct key *key, struct seq_file *m); /* Destroy a key of this subtype */ - void (*destroy)(void *payload); + void (*destroy)(void *payload_crypto, void *payload_auth); /* Verify the signature on a key of this subtype (optional) */ int (*verify_signature)(const struct key *key, diff --git a/include/keys/asymmetric-type.h b/include/keys/asymmetric-type.h index 59c1df9..70a8775 100644 --- a/include/keys/asymmetric-type.h +++ b/include/keys/asymmetric-type.h @@ -23,9 +23,10 @@ extern struct key_type key_type_asymmetric; * follows: */ enum asymmetric_payload_bits { - asym_crypto, - asym_subtype, - asym_key_ids, + asym_crypto, /* The data representing the key */ + asym_subtype, /* Pointer to an asymmetric_key_subtype struct */ + asym_key_ids, /* Pointer to an asymmetric_key_ids struct */ + asym_auth /* The key's authorisation (signature, parent key ID) */ }; /* -- cgit v0.10.2 From a022ec02691cf68e1fe237d5f79d54aa95446cc6 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 6 Apr 2016 16:13:33 +0100 Subject: KEYS: Add identifier pointers to public_key_signature struct Add key identifier pointers to public_key_signature struct so that they can be used to retain the identifier of the key to be used to verify the signature in both PKCS#7 and X.509. Signed-off-by: David Howells diff --git a/crypto/asymmetric_keys/signature.c b/crypto/asymmetric_keys/signature.c index 3beee39..11b7ba17 100644 --- a/crypto/asymmetric_keys/signature.c +++ b/crypto/asymmetric_keys/signature.c @@ -24,7 +24,11 @@ */ void public_key_signature_free(struct public_key_signature *sig) { + int i; + if (sig) { + for (i = 0; i < ARRAY_SIZE(sig->auth_ids); i++) + kfree(sig->auth_ids[i]); kfree(sig->s); kfree(sig->digest); kfree(sig); diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h index 19f557c..2f5de5c 100644 --- a/include/crypto/public_key.h +++ b/include/crypto/public_key.h @@ -47,6 +47,7 @@ extern void public_key_free(struct public_key *key); * Public key cryptography signature data */ struct public_key_signature { + struct asymmetric_key_id *auth_ids[2]; u8 *s; /* Signature */ u32 s_size; /* Number of bytes in signature */ u8 *digest; -- cgit v0.10.2 From 77d0910d153a7946df17cc15d3f423e534345f65 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 6 Apr 2016 16:13:33 +0100 Subject: X.509: Retain the key verification data Retain the key verification data (ie. the struct public_key_signature) including the digest and the key identifiers. Note that this means that we need to take a separate copy of the digest in x509_get_sig_params() rather than lumping it in with the crypto layer data. Signed-off-by: David Howells diff --git a/crypto/asymmetric_keys/pkcs7_trust.c b/crypto/asymmetric_keys/pkcs7_trust.c index 7d7a39b4..ed81282 100644 --- a/crypto/asymmetric_keys/pkcs7_trust.c +++ b/crypto/asymmetric_keys/pkcs7_trust.c @@ -80,16 +80,16 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, might_sleep(); last = x509; - sig = &last->sig; + sig = last->sig; } /* No match - see if the root certificate has a signer amongst the * trusted keys. */ - if (last && (last->akid_id || last->akid_skid)) { + if (last && (last->sig->auth_ids[0] || last->sig->auth_ids[1])) { key = x509_request_asymmetric_key(trust_keyring, - last->akid_id, - last->akid_skid, + last->sig->auth_ids[0], + last->sig->auth_ids[1], false); if (!IS_ERR(key)) { x509 = last; diff --git a/crypto/asymmetric_keys/pkcs7_verify.c b/crypto/asymmetric_keys/pkcs7_verify.c index 50be2a1..d8d8d23 100644 --- a/crypto/asymmetric_keys/pkcs7_verify.c +++ b/crypto/asymmetric_keys/pkcs7_verify.c @@ -174,6 +174,7 @@ static int pkcs7_find_key(struct pkcs7_message *pkcs7, static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, struct pkcs7_signed_info *sinfo) { + struct public_key_signature *sig; struct x509_certificate *x509 = sinfo->signer, *p; struct asymmetric_key_id *auth; int ret; @@ -193,14 +194,15 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, goto maybe_missing_crypto_in_x509; pr_debug("- issuer %s\n", x509->issuer); - if (x509->akid_id) + sig = x509->sig; + if (sig->auth_ids[0]) pr_debug("- authkeyid.id %*phN\n", - x509->akid_id->len, x509->akid_id->data); - if (x509->akid_skid) + sig->auth_ids[0]->len, sig->auth_ids[0]->data); + if (sig->auth_ids[1]) pr_debug("- authkeyid.skid %*phN\n", - x509->akid_skid->len, x509->akid_skid->data); + sig->auth_ids[1]->len, sig->auth_ids[1]->data); - if ((!x509->akid_id && !x509->akid_skid) || + if ((!x509->sig->auth_ids[0] && !x509->sig->auth_ids[1]) || strcmp(x509->subject, x509->issuer) == 0) { /* If there's no authority certificate specified, then * the certificate must be self-signed and is the root @@ -224,7 +226,7 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, /* Look through the X.509 certificates in the PKCS#7 message's * list to see if the next one is there. */ - auth = x509->akid_id; + auth = sig->auth_ids[0]; if (auth) { pr_debug("- want %*phN\n", auth->len, auth->data); for (p = pkcs7->certs; p; p = p->next) { @@ -234,7 +236,7 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, goto found_issuer_check_skid; } } else { - auth = x509->akid_skid; + auth = sig->auth_ids[1]; pr_debug("- want %*phN\n", auth->len, auth->data); for (p = pkcs7->certs; p; p = p->next) { if (!p->skid) @@ -254,8 +256,8 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, /* We matched issuer + serialNumber, but if there's an * authKeyId.keyId, that must match the CA subjKeyId also. */ - if (x509->akid_skid && - !asymmetric_key_id_same(p->skid, x509->akid_skid)) { + if (sig->auth_ids[1] && + !asymmetric_key_id_same(p->skid, sig->auth_ids[1])) { pr_warn("Sig %u: X.509 chain contains auth-skid nonmatch (%u->%u)\n", sinfo->index, x509->index, p->index); return -EKEYREJECTED; diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c index 05251c7..a2fefa7 100644 --- a/crypto/asymmetric_keys/x509_cert_parser.c +++ b/crypto/asymmetric_keys/x509_cert_parser.c @@ -48,14 +48,11 @@ void x509_free_certificate(struct x509_certificate *cert) { if (cert) { public_key_free(cert->pub); + public_key_signature_free(cert->sig); kfree(cert->issuer); kfree(cert->subject); kfree(cert->id); kfree(cert->skid); - kfree(cert->akid_id); - kfree(cert->akid_skid); - kfree(cert->sig.digest); - kfree(cert->sig.s); kfree(cert); } } @@ -78,6 +75,9 @@ struct x509_certificate *x509_cert_parse(const void *data, size_t datalen) cert->pub = kzalloc(sizeof(struct public_key), GFP_KERNEL); if (!cert->pub) goto error_no_ctx; + cert->sig = kzalloc(sizeof(struct public_key_signature), GFP_KERNEL); + if (!cert->sig) + goto error_no_ctx; ctx = kzalloc(sizeof(struct x509_parse_context), GFP_KERNEL); if (!ctx) goto error_no_ctx; @@ -188,33 +188,33 @@ int x509_note_pkey_algo(void *context, size_t hdrlen, return -ENOPKG; /* Unsupported combination */ case OID_md4WithRSAEncryption: - ctx->cert->sig.hash_algo = "md4"; - ctx->cert->sig.pkey_algo = "rsa"; + ctx->cert->sig->hash_algo = "md4"; + ctx->cert->sig->pkey_algo = "rsa"; break; case OID_sha1WithRSAEncryption: - ctx->cert->sig.hash_algo = "sha1"; - ctx->cert->sig.pkey_algo = "rsa"; + ctx->cert->sig->hash_algo = "sha1"; + ctx->cert->sig->pkey_algo = "rsa"; break; case OID_sha256WithRSAEncryption: - ctx->cert->sig.hash_algo = "sha256"; - ctx->cert->sig.pkey_algo = "rsa"; + ctx->cert->sig->hash_algo = "sha256"; + ctx->cert->sig->pkey_algo = "rsa"; break; case OID_sha384WithRSAEncryption: - ctx->cert->sig.hash_algo = "sha384"; - ctx->cert->sig.pkey_algo = "rsa"; + ctx->cert->sig->hash_algo = "sha384"; + ctx->cert->sig->pkey_algo = "rsa"; break; case OID_sha512WithRSAEncryption: - ctx->cert->sig.hash_algo = "sha512"; - ctx->cert->sig.pkey_algo = "rsa"; + ctx->cert->sig->hash_algo = "sha512"; + ctx->cert->sig->pkey_algo = "rsa"; break; case OID_sha224WithRSAEncryption: - ctx->cert->sig.hash_algo = "sha224"; - ctx->cert->sig.pkey_algo = "rsa"; + ctx->cert->sig->hash_algo = "sha224"; + ctx->cert->sig->pkey_algo = "rsa"; break; } @@ -572,14 +572,14 @@ int x509_akid_note_kid(void *context, size_t hdrlen, pr_debug("AKID: keyid: %*phN\n", (int)vlen, value); - if (ctx->cert->akid_skid) + if (ctx->cert->sig->auth_ids[1]) return 0; kid = asymmetric_key_generate_id(value, vlen, "", 0); if (IS_ERR(kid)) return PTR_ERR(kid); pr_debug("authkeyid %*phN\n", kid->len, kid->data); - ctx->cert->akid_skid = kid; + ctx->cert->sig->auth_ids[1] = kid; return 0; } @@ -611,7 +611,7 @@ int x509_akid_note_serial(void *context, size_t hdrlen, pr_debug("AKID: serial: %*phN\n", (int)vlen, value); - if (!ctx->akid_raw_issuer || ctx->cert->akid_id) + if (!ctx->akid_raw_issuer || ctx->cert->sig->auth_ids[0]) return 0; kid = asymmetric_key_generate_id(value, @@ -622,6 +622,6 @@ int x509_akid_note_serial(void *context, size_t hdrlen, return PTR_ERR(kid); pr_debug("authkeyid %*phN\n", kid->len, kid->data); - ctx->cert->akid_id = kid; + ctx->cert->sig->auth_ids[0] = kid; return 0; } diff --git a/crypto/asymmetric_keys/x509_parser.h b/crypto/asymmetric_keys/x509_parser.h index dbeed60..26a4d83 100644 --- a/crypto/asymmetric_keys/x509_parser.h +++ b/crypto/asymmetric_keys/x509_parser.h @@ -17,13 +17,11 @@ struct x509_certificate { struct x509_certificate *next; struct x509_certificate *signer; /* Certificate that signed this one */ struct public_key *pub; /* Public key details */ - struct public_key_signature sig; /* Signature parameters */ + struct public_key_signature *sig; /* Signature parameters */ char *issuer; /* Name of certificate issuer */ char *subject; /* Name of certificate subject */ struct asymmetric_key_id *id; /* Issuer + Serial number */ struct asymmetric_key_id *skid; /* Subject + subjectKeyId (optional) */ - struct asymmetric_key_id *akid_id; /* CA AuthKeyId matching ->id (optional) */ - struct asymmetric_key_id *akid_skid; /* CA AuthKeyId matching ->skid (optional) */ time64_t valid_from; time64_t valid_to; const void *tbs; /* Signed data */ diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c index 2fcf707..4cd102d 100644 --- a/crypto/asymmetric_keys/x509_public_key.c +++ b/crypto/asymmetric_keys/x509_public_key.c @@ -153,30 +153,29 @@ EXPORT_SYMBOL_GPL(x509_request_asymmetric_key); */ int x509_get_sig_params(struct x509_certificate *cert) { + struct public_key_signature *sig = cert->sig; struct crypto_shash *tfm; struct shash_desc *desc; - size_t digest_size, desc_size; - void *digest; + size_t desc_size; int ret; pr_devel("==>%s()\n", __func__); if (cert->unsupported_crypto) return -ENOPKG; - if (cert->sig.s) + if (sig->s) return 0; - cert->sig.s = kmemdup(cert->raw_sig, cert->raw_sig_size, - GFP_KERNEL); - if (!cert->sig.s) + sig->s = kmemdup(cert->raw_sig, cert->raw_sig_size, GFP_KERNEL); + if (!sig->s) return -ENOMEM; - cert->sig.s_size = cert->raw_sig_size; + sig->s_size = cert->raw_sig_size; /* Allocate the hashing algorithm we're going to need and find out how * big the hash operational data will be. */ - tfm = crypto_alloc_shash(cert->sig.hash_algo, 0, 0); + tfm = crypto_alloc_shash(sig->hash_algo, 0, 0); if (IS_ERR(tfm)) { if (PTR_ERR(tfm) == -ENOENT) { cert->unsupported_crypto = true; @@ -186,29 +185,28 @@ int x509_get_sig_params(struct x509_certificate *cert) } desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); - digest_size = crypto_shash_digestsize(tfm); + sig->digest_size = crypto_shash_digestsize(tfm); - /* We allocate the hash operational data storage on the end of the - * digest storage space. - */ ret = -ENOMEM; - digest = kzalloc(ALIGN(digest_size, __alignof__(*desc)) + desc_size, - GFP_KERNEL); - if (!digest) + sig->digest = kmalloc(sig->digest_size, GFP_KERNEL); + if (!sig->digest) goto error; - cert->sig.digest = digest; - cert->sig.digest_size = digest_size; + desc = kzalloc(desc_size, GFP_KERNEL); + if (!desc) + goto error; - desc = PTR_ALIGN(digest + digest_size, __alignof__(*desc)); desc->tfm = tfm; desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP; ret = crypto_shash_init(desc); if (ret < 0) - goto error; + goto error_2; might_sleep(); - ret = crypto_shash_finup(desc, cert->tbs, cert->tbs_size, digest); + ret = crypto_shash_finup(desc, cert->tbs, cert->tbs_size, sig->digest); + +error_2: + kfree(desc); error: crypto_free_shash(tfm); pr_devel("<==%s() = %d\n", __func__, ret); @@ -230,7 +228,7 @@ int x509_check_signature(const struct public_key *pub, if (ret < 0) 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); @@ -250,17 +248,18 @@ EXPORT_SYMBOL_GPL(x509_check_signature); static int x509_validate_trust(struct x509_certificate *cert, struct key *trust_keyring) { + struct public_key_signature *sig = cert->sig; struct key *key; int ret = 1; if (!trust_keyring) return -EOPNOTSUPP; - if (ca_keyid && !asymmetric_key_id_partial(cert->akid_skid, ca_keyid)) + if (ca_keyid && !asymmetric_key_id_partial(sig->auth_ids[1], ca_keyid)) return -EPERM; key = x509_request_asymmetric_key(trust_keyring, - cert->akid_id, cert->akid_skid, + sig->auth_ids[0], sig->auth_ids[1], false); if (!IS_ERR(key)) { if (!use_builtin_keys @@ -292,8 +291,8 @@ static int x509_key_preparse(struct key_preparsed_payload *prep) pr_devel("Cert Subject: %s\n", cert->subject); if (!cert->pub->pkey_algo || - !cert->sig.pkey_algo || - !cert->sig.hash_algo) { + !cert->sig->pkey_algo || + !cert->sig->hash_algo) { ret = -ENOPKG; goto error_free_cert; } @@ -301,15 +300,15 @@ static int x509_key_preparse(struct key_preparsed_payload *prep) pr_devel("Cert Key Algo: %s\n", cert->pub->pkey_algo); pr_devel("Cert Valid period: %lld-%lld\n", cert->valid_from, cert->valid_to); pr_devel("Cert Signature: %s + %s\n", - cert->sig.pkey_algo, - cert->sig.hash_algo); + cert->sig->pkey_algo, + cert->sig->hash_algo); cert->pub->id_type = "X509"; /* Check the signature on the key if it appears to be self-signed */ - if ((!cert->akid_skid && !cert->akid_id) || - asymmetric_key_id_same(cert->skid, cert->akid_skid) || - asymmetric_key_id_same(cert->id, cert->akid_id)) { + if ((!cert->sig->auth_ids[0] && !cert->sig->auth_ids[1]) || + asymmetric_key_id_same(cert->skid, cert->sig->auth_ids[1]) || + asymmetric_key_id_same(cert->id, cert->sig->auth_ids[0])) { ret = x509_check_signature(cert->pub, cert); /* self-signed */ if (ret < 0) goto error_free_cert; @@ -353,6 +352,7 @@ static int x509_key_preparse(struct key_preparsed_payload *prep) prep->payload.data[asym_subtype] = &public_key_subtype; prep->payload.data[asym_key_ids] = kids; prep->payload.data[asym_crypto] = cert->pub; + prep->payload.data[asym_auth] = cert->sig; prep->description = desc; prep->quotalen = 100; @@ -360,6 +360,7 @@ static int x509_key_preparse(struct key_preparsed_payload *prep) cert->pub = NULL; cert->id = NULL; cert->skid = NULL; + cert->sig = NULL; desc = NULL; ret = 0; -- cgit v0.10.2 From 566a117a8b24e1ae2dfa817cf0c9eec092c783b5 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 6 Apr 2016 16:13:33 +0100 Subject: PKCS#7: Make the signature a pointer rather than embedding it Point to the public_key_signature struct from the pkcs7_signed_info struct rather than embedding it. This makes the code consistent with the X.509 signature handling and makes it possible to have a common cleanup function. We also save a copy of the digest in the signature without sharing the memory with the crypto layer metadata. Signed-off-by: David Howells diff --git a/crypto/asymmetric_keys/pkcs7_parser.c b/crypto/asymmetric_keys/pkcs7_parser.c index 40de03f..8357016 100644 --- a/crypto/asymmetric_keys/pkcs7_parser.c +++ b/crypto/asymmetric_keys/pkcs7_parser.c @@ -44,9 +44,7 @@ struct pkcs7_parse_context { static void pkcs7_free_signed_info(struct pkcs7_signed_info *sinfo) { if (sinfo) { - kfree(sinfo->sig.s); - kfree(sinfo->sig.digest); - kfree(sinfo->signing_cert_id); + public_key_signature_free(sinfo->sig); kfree(sinfo); } } @@ -125,6 +123,10 @@ struct pkcs7_message *pkcs7_parse_message(const void *data, size_t datalen) ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL); if (!ctx->sinfo) goto out_no_sinfo; + ctx->sinfo->sig = kzalloc(sizeof(struct public_key_signature), + GFP_KERNEL); + if (!ctx->sinfo->sig) + goto out_no_sig; ctx->data = (unsigned long)data; ctx->ppcerts = &ctx->certs; @@ -150,6 +152,7 @@ out: ctx->certs = cert->next; x509_free_certificate(cert); } +out_no_sig: pkcs7_free_signed_info(ctx->sinfo); out_no_sinfo: pkcs7_free_message(ctx->msg); @@ -218,25 +221,26 @@ int pkcs7_sig_note_digest_algo(void *context, size_t hdrlen, switch (ctx->last_oid) { case OID_md4: - ctx->sinfo->sig.hash_algo = "md4"; + ctx->sinfo->sig->hash_algo = "md4"; break; case OID_md5: - ctx->sinfo->sig.hash_algo = "md5"; + ctx->sinfo->sig->hash_algo = "md5"; break; case OID_sha1: - ctx->sinfo->sig.hash_algo = "sha1"; + ctx->sinfo->sig->hash_algo = "sha1"; break; case OID_sha256: - ctx->sinfo->sig.hash_algo = "sha256"; + ctx->sinfo->sig->hash_algo = "sha256"; break; case OID_sha384: - ctx->sinfo->sig.hash_algo = "sha384"; + ctx->sinfo->sig->hash_algo = "sha384"; break; case OID_sha512: - ctx->sinfo->sig.hash_algo = "sha512"; + ctx->sinfo->sig->hash_algo = "sha512"; break; case OID_sha224: - ctx->sinfo->sig.hash_algo = "sha224"; + ctx->sinfo->sig->hash_algo = "sha224"; + break; default: printk("Unsupported digest algo: %u\n", ctx->last_oid); return -ENOPKG; @@ -255,7 +259,7 @@ int pkcs7_sig_note_pkey_algo(void *context, size_t hdrlen, switch (ctx->last_oid) { case OID_rsaEncryption: - ctx->sinfo->sig.pkey_algo = "rsa"; + ctx->sinfo->sig->pkey_algo = "rsa"; break; default: printk("Unsupported pkey algo: %u\n", ctx->last_oid); @@ -615,11 +619,11 @@ int pkcs7_sig_note_signature(void *context, size_t hdrlen, { struct pkcs7_parse_context *ctx = context; - ctx->sinfo->sig.s = kmemdup(value, vlen, GFP_KERNEL); - if (!ctx->sinfo->sig.s) + ctx->sinfo->sig->s = kmemdup(value, vlen, GFP_KERNEL); + if (!ctx->sinfo->sig->s) return -ENOMEM; - ctx->sinfo->sig.s_size = vlen; + ctx->sinfo->sig->s_size = vlen; return 0; } @@ -655,12 +659,16 @@ int pkcs7_note_signed_info(void *context, size_t hdrlen, pr_devel("SINFO KID: %u [%*phN]\n", kid->len, kid->len, kid->data); - sinfo->signing_cert_id = kid; + sinfo->sig->auth_ids[0] = kid; sinfo->index = ++ctx->sinfo_index; *ctx->ppsinfo = sinfo; ctx->ppsinfo = &sinfo->next; ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL); if (!ctx->sinfo) return -ENOMEM; + ctx->sinfo->sig = kzalloc(sizeof(struct public_key_signature), + GFP_KERNEL); + if (!ctx->sinfo->sig) + return -ENOMEM; return 0; } diff --git a/crypto/asymmetric_keys/pkcs7_parser.h b/crypto/asymmetric_keys/pkcs7_parser.h index a66b19e..d5eec31 100644 --- a/crypto/asymmetric_keys/pkcs7_parser.h +++ b/crypto/asymmetric_keys/pkcs7_parser.h @@ -41,19 +41,17 @@ struct pkcs7_signed_info { #define sinfo_has_ms_statement_type 5 time64_t signing_time; - /* Issuing cert serial number and issuer's name [PKCS#7 or CMS ver 1] - * or issuing cert's SKID [CMS ver 3]. - */ - struct asymmetric_key_id *signing_cert_id; - /* Message signature. * * This contains the generated digest of _either_ the Content Data or * the Authenticated Attributes [RFC2315 9.3]. If the latter, one of * the attributes contains the digest of the the Content Data within * it. + * + * THis also contains the issuing cert serial number and issuer's name + * [PKCS#7 or CMS ver 1] or issuing cert's SKID [CMS ver 3]. */ - struct public_key_signature sig; + struct public_key_signature *sig; }; struct pkcs7_message { diff --git a/crypto/asymmetric_keys/pkcs7_trust.c b/crypto/asymmetric_keys/pkcs7_trust.c index ed81282..b9a5487 100644 --- a/crypto/asymmetric_keys/pkcs7_trust.c +++ b/crypto/asymmetric_keys/pkcs7_trust.c @@ -27,7 +27,7 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, struct pkcs7_signed_info *sinfo, 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 key *key; bool trusted; @@ -105,7 +105,7 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, * the signed info directly. */ key = x509_request_asymmetric_key(trust_keyring, - sinfo->signing_cert_id, + sinfo->sig->auth_ids[0], NULL, false); if (!IS_ERR(key)) { diff --git a/crypto/asymmetric_keys/pkcs7_verify.c b/crypto/asymmetric_keys/pkcs7_verify.c index d8d8d23..1426f03 100644 --- a/crypto/asymmetric_keys/pkcs7_verify.c +++ b/crypto/asymmetric_keys/pkcs7_verify.c @@ -25,34 +25,36 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7, struct pkcs7_signed_info *sinfo) { + struct public_key_signature *sig = sinfo->sig; struct crypto_shash *tfm; struct shash_desc *desc; - size_t digest_size, desc_size; - void *digest; + size_t desc_size; int ret; - kenter(",%u,%s", sinfo->index, sinfo->sig.hash_algo); + kenter(",%u,%s", sinfo->index, sinfo->sig->hash_algo); - if (!sinfo->sig.hash_algo) + if (!sinfo->sig->hash_algo) return -ENOPKG; /* Allocate the hashing algorithm we're going to need and find out how * big the hash operational data will be. */ - tfm = crypto_alloc_shash(sinfo->sig.hash_algo, 0, 0); + tfm = crypto_alloc_shash(sinfo->sig->hash_algo, 0, 0); if (IS_ERR(tfm)) return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm); desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); - sinfo->sig.digest_size = digest_size = crypto_shash_digestsize(tfm); + sig->digest_size = crypto_shash_digestsize(tfm); ret = -ENOMEM; - digest = kzalloc(ALIGN(digest_size, __alignof__(*desc)) + desc_size, - GFP_KERNEL); - if (!digest) + sig->digest = kmalloc(sig->digest_size, GFP_KERNEL); + if (!sig->digest) + goto error_no_desc; + + desc = kzalloc(desc_size, GFP_KERNEL); + if (!desc) goto error_no_desc; - desc = PTR_ALIGN(digest + digest_size, __alignof__(*desc)); desc->tfm = tfm; desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP; @@ -60,10 +62,11 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7, ret = crypto_shash_init(desc); if (ret < 0) goto error; - ret = crypto_shash_finup(desc, pkcs7->data, pkcs7->data_len, digest); + ret = crypto_shash_finup(desc, pkcs7->data, pkcs7->data_len, + sig->digest); if (ret < 0) goto error; - pr_devel("MsgDigest = [%*ph]\n", 8, digest); + pr_devel("MsgDigest = [%*ph]\n", 8, sig->digest); /* However, if there are authenticated attributes, there must be a * message digest attribute amongst them which corresponds to the @@ -78,14 +81,15 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7, goto error; } - if (sinfo->msgdigest_len != sinfo->sig.digest_size) { + if (sinfo->msgdigest_len != sig->digest_size) { pr_debug("Sig %u: Invalid digest size (%u)\n", sinfo->index, sinfo->msgdigest_len); ret = -EBADMSG; goto error; } - if (memcmp(digest, sinfo->msgdigest, sinfo->msgdigest_len) != 0) { + if (memcmp(sig->digest, sinfo->msgdigest, + sinfo->msgdigest_len) != 0) { pr_debug("Sig %u: Message digest doesn't match\n", sinfo->index); ret = -EKEYREJECTED; @@ -97,7 +101,7 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7, * convert the attributes from a CONT.0 into a SET before we * hash it. */ - memset(digest, 0, sinfo->sig.digest_size); + memset(sig->digest, 0, sig->digest_size); ret = crypto_shash_init(desc); if (ret < 0) @@ -107,17 +111,14 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7, if (ret < 0) goto error; ret = crypto_shash_finup(desc, sinfo->authattrs, - sinfo->authattrs_len, digest); + sinfo->authattrs_len, sig->digest); if (ret < 0) goto error; - pr_devel("AADigest = [%*ph]\n", 8, digest); + pr_devel("AADigest = [%*ph]\n", 8, sig->digest); } - sinfo->sig.digest = digest; - digest = NULL; - error: - kfree(digest); + kfree(desc); error_no_desc: crypto_free_shash(tfm); kleave(" = %d", ret); @@ -144,12 +145,12 @@ static int pkcs7_find_key(struct pkcs7_message *pkcs7, * PKCS#7 message - but I can't be 100% sure of that. It's * possible this will need element-by-element comparison. */ - if (!asymmetric_key_id_same(x509->id, sinfo->signing_cert_id)) + if (!asymmetric_key_id_same(x509->id, sinfo->sig->auth_ids[0])) continue; pr_devel("Sig %u: Found cert serial match X.509[%u]\n", sinfo->index, certix); - 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", sinfo->index); continue; @@ -164,7 +165,7 @@ static int pkcs7_find_key(struct pkcs7_message *pkcs7, */ pr_debug("Sig %u: Issuing X.509 cert not found (#%*phN)\n", sinfo->index, - sinfo->signing_cert_id->len, sinfo->signing_cert_id->data); + sinfo->sig->auth_ids[0]->len, sinfo->sig->auth_ids[0]->data); return 0; } @@ -334,7 +335,7 @@ static int pkcs7_verify_one(struct pkcs7_message *pkcs7, } /* Verify the PKCS#7 binary against the key */ - ret = public_key_verify_signature(sinfo->signer->pub, &sinfo->sig); + ret = public_key_verify_signature(sinfo->signer->pub, sinfo->sig); if (ret < 0) return ret; -- cgit v0.10.2 From 6c2dc5ae4ab719a61d19e8cef082226410b04ff8 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 6 Apr 2016 16:13:34 +0100 Subject: X.509: Extract signature digest and make self-signed cert checks earlier Extract the signature digest for an X.509 certificate earlier, at the end of x509_cert_parse() rather than leaving it to the callers thereof since it has to be called anyway. Further, immediately after that, check the signature on self-signed certificates, also rather in the callers of x509_cert_parse(). We note in the x509_certificate struct the following bits of information: (1) Whether the signature is self-signed (even if we can't check the signature due to missing crypto). (2) Whether the key held in the certificate needs unsupported crypto to be used. We may get a PKCS#7 message with X.509 certs that we can't make use of - we just ignore them and give ENOPKG at the end it we couldn't verify anything if at least one of these unusable certs are in the chain of trust. (3) Whether the signature held in the certificate needs unsupported crypto to be checked. We can still use the key held in this certificate, even if we can't check the signature on it - if it is held in the system trusted keyring, for instance. We just can't add it to a ring of trusted keys or follow it further up the chain of trust. Making these checks earlier allows x509_check_signature() to be removed and replaced with direct calls to public_key_verify_signature(). Signed-off-by: David Howells diff --git a/crypto/asymmetric_keys/pkcs7_verify.c b/crypto/asymmetric_keys/pkcs7_verify.c index 1426f03..44b746e 100644 --- a/crypto/asymmetric_keys/pkcs7_verify.c +++ b/crypto/asymmetric_keys/pkcs7_verify.c @@ -190,9 +190,8 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, x509->subject, x509->raw_serial_size, x509->raw_serial); x509->seen = true; - ret = x509_get_sig_params(x509); - if (ret < 0) - goto maybe_missing_crypto_in_x509; + if (x509->unsupported_key) + goto unsupported_crypto_in_x509; pr_debug("- issuer %s\n", x509->issuer); sig = x509->sig; @@ -203,22 +202,14 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, pr_debug("- authkeyid.skid %*phN\n", sig->auth_ids[1]->len, sig->auth_ids[1]->data); - if ((!x509->sig->auth_ids[0] && !x509->sig->auth_ids[1]) || - strcmp(x509->subject, x509->issuer) == 0) { + if (x509->self_signed) { /* If there's no authority certificate specified, then * the certificate must be self-signed and is the root * of the chain. Likewise if the cert is its own * authority. */ - pr_debug("- no auth?\n"); - if (x509->raw_subject_size != x509->raw_issuer_size || - memcmp(x509->raw_subject, x509->raw_issuer, - x509->raw_issuer_size) != 0) - return 0; - - ret = x509_check_signature(x509->pub, x509); - if (ret < 0) - goto maybe_missing_crypto_in_x509; + if (x509->unsupported_sig) + goto unsupported_crypto_in_x509; x509->signer = x509; pr_debug("- self-signed\n"); return 0; @@ -270,7 +261,7 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, sinfo->index); return 0; } - ret = x509_check_signature(p->pub, x509); + ret = public_key_verify_signature(p->pub, p->sig); if (ret < 0) return ret; x509->signer = p; @@ -282,16 +273,14 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, might_sleep(); } -maybe_missing_crypto_in_x509: +unsupported_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 + * sinfo->unsupported_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; + return 0; } /* @@ -378,9 +367,8 @@ int pkcs7_verify(struct pkcs7_message *pkcs7, enum key_being_used_for usage) { struct pkcs7_signed_info *sinfo; - struct x509_certificate *x509; int enopkg = -ENOPKG; - int ret, n; + int ret; kenter(""); @@ -422,12 +410,6 @@ int pkcs7_verify(struct pkcs7_message *pkcs7, return -EINVAL; } - for (n = 0, x509 = pkcs7->certs; x509; x509 = x509->next, n++) { - ret = x509_get_sig_params(x509); - if (ret < 0) - return ret; - } - for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) { ret = pkcs7_verify_one(pkcs7, sinfo); if (ret < 0) { diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c index a2fefa7..865f46e 100644 --- a/crypto/asymmetric_keys/x509_cert_parser.c +++ b/crypto/asymmetric_keys/x509_cert_parser.c @@ -108,6 +108,11 @@ struct x509_certificate *x509_cert_parse(const void *data, size_t datalen) cert->pub->keylen = ctx->key_size; + /* Grab the signature bits */ + ret = x509_get_sig_params(cert); + if (ret < 0) + goto error_decode; + /* Generate cert issuer + serial number key ID */ kid = asymmetric_key_generate_id(cert->raw_serial, cert->raw_serial_size, @@ -119,6 +124,11 @@ struct x509_certificate *x509_cert_parse(const void *data, size_t datalen) } cert->id = kid; + /* Detect self-signed certificates */ + ret = x509_check_for_self_signed(cert); + if (ret < 0) + goto error_decode; + kfree(ctx); return cert; diff --git a/crypto/asymmetric_keys/x509_parser.h b/crypto/asymmetric_keys/x509_parser.h index 26a4d83..f24f4d8 100644 --- a/crypto/asymmetric_keys/x509_parser.h +++ b/crypto/asymmetric_keys/x509_parser.h @@ -40,7 +40,9 @@ struct x509_certificate { bool seen; /* Infinite recursion prevention */ bool verified; bool trusted; - bool unsupported_crypto; /* T if can't be verified due to missing crypto */ + bool self_signed; /* T if self-signed (check unsupported_sig too) */ + bool unsupported_key; /* T if key uses unsupported crypto */ + bool unsupported_sig; /* T if signature uses unsupported crypto */ }; /* @@ -56,5 +58,4 @@ extern int x509_decode_time(time64_t *_t, size_t hdrlen, * x509_public_key.c */ extern int x509_get_sig_params(struct x509_certificate *cert); -extern int x509_check_signature(const struct public_key *pub, - struct x509_certificate *cert); +extern int x509_check_for_self_signed(struct x509_certificate *cert); diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c index 4cd102d..752d8d5 100644 --- a/crypto/asymmetric_keys/x509_public_key.c +++ b/crypto/asymmetric_keys/x509_public_key.c @@ -161,10 +161,17 @@ int x509_get_sig_params(struct x509_certificate *cert) pr_devel("==>%s()\n", __func__); - if (cert->unsupported_crypto) - return -ENOPKG; - if (sig->s) + if (!cert->pub->pkey_algo) + cert->unsupported_key = true; + + if (!sig->pkey_algo) + cert->unsupported_sig = true; + + /* We check the hash if we can - even if we can't then verify it */ + if (!sig->hash_algo) { + cert->unsupported_sig = true; return 0; + } sig->s = kmemdup(cert->raw_sig, cert->raw_sig_size, GFP_KERNEL); if (!sig->s) @@ -178,8 +185,8 @@ int x509_get_sig_params(struct x509_certificate *cert) tfm = crypto_alloc_shash(sig->hash_algo, 0, 0); if (IS_ERR(tfm)) { if (PTR_ERR(tfm) == -ENOENT) { - cert->unsupported_crypto = true; - return -ENOPKG; + cert->unsupported_sig = true; + return 0; } return PTR_ERR(tfm); } @@ -212,29 +219,53 @@ error: pr_devel("<==%s() = %d\n", __func__, ret); return ret; } -EXPORT_SYMBOL_GPL(x509_get_sig_params); /* - * Check the signature on a certificate using the provided public key + * Check for self-signedness in an X.509 cert and if found, check the signature + * immediately if we can. */ -int x509_check_signature(const struct public_key *pub, - struct x509_certificate *cert) +int x509_check_for_self_signed(struct x509_certificate *cert) { - int ret; + int ret = 0; pr_devel("==>%s()\n", __func__); - ret = x509_get_sig_params(cert); - if (ret < 0) - return ret; + if (cert->sig->auth_ids[0] || cert->sig->auth_ids[1]) { + /* If the AKID is present it may have one or two parts. If + * both are supplied, both must match. + */ + bool a = asymmetric_key_id_same(cert->skid, cert->sig->auth_ids[1]); + bool b = asymmetric_key_id_same(cert->id, cert->sig->auth_ids[0]); + + if (!a && !b) + goto not_self_signed; + + ret = -EKEYREJECTED; + if (((a && !b) || (b && !a)) && + cert->sig->auth_ids[0] && cert->sig->auth_ids[1]) + goto out; + } + + ret = public_key_verify_signature(cert->pub, cert->sig); + if (ret < 0) { + if (ret == -ENOPKG) { + cert->unsupported_sig = true; + ret = 0; + } + goto out; + } + + pr_devel("Cert Self-signature verified"); + cert->self_signed = true; - ret = public_key_verify_signature(pub, cert->sig); - if (ret == -ENOPKG) - cert->unsupported_crypto = true; - pr_debug("Cert Verification: %d\n", ret); +out: + pr_devel("<==%s() = %d\n", __func__, ret); return ret; + +not_self_signed: + pr_devel("<==%s() = 0 [not]\n", __func__); + return 0; } -EXPORT_SYMBOL_GPL(x509_check_signature); /* * Check the new certificate against the ones in the trust keyring. If one of @@ -252,22 +283,30 @@ static int x509_validate_trust(struct x509_certificate *cert, struct key *key; int ret = 1; + if (!sig->auth_ids[0] && !sig->auth_ids[1]) + return 1; + if (!trust_keyring) return -EOPNOTSUPP; - if (ca_keyid && !asymmetric_key_id_partial(sig->auth_ids[1], ca_keyid)) return -EPERM; + if (cert->unsupported_sig) + return -ENOPKG; key = x509_request_asymmetric_key(trust_keyring, sig->auth_ids[0], sig->auth_ids[1], false); - if (!IS_ERR(key)) { - if (!use_builtin_keys - || test_bit(KEY_FLAG_BUILTIN, &key->flags)) - ret = x509_check_signature(key->payload.data[asym_crypto], - cert); - key_put(key); + if (IS_ERR(key)) + return PTR_ERR(key); + + if (!use_builtin_keys || + test_bit(KEY_FLAG_BUILTIN, &key->flags)) { + ret = public_key_verify_signature( + key->payload.data[asym_crypto], cert->sig); + if (ret == -ENOPKG) + cert->unsupported_sig = true; } + key_put(key); return ret; } @@ -290,34 +329,41 @@ static int x509_key_preparse(struct key_preparsed_payload *prep) pr_devel("Cert Issuer: %s\n", cert->issuer); pr_devel("Cert Subject: %s\n", cert->subject); - if (!cert->pub->pkey_algo || - !cert->sig->pkey_algo || - !cert->sig->hash_algo) { + if (cert->unsupported_key) { ret = -ENOPKG; goto error_free_cert; } pr_devel("Cert Key Algo: %s\n", cert->pub->pkey_algo); pr_devel("Cert Valid period: %lld-%lld\n", cert->valid_from, cert->valid_to); - pr_devel("Cert Signature: %s + %s\n", - cert->sig->pkey_algo, - cert->sig->hash_algo); cert->pub->id_type = "X509"; - /* Check the signature on the key if it appears to be self-signed */ - if ((!cert->sig->auth_ids[0] && !cert->sig->auth_ids[1]) || - asymmetric_key_id_same(cert->skid, cert->sig->auth_ids[1]) || - asymmetric_key_id_same(cert->id, cert->sig->auth_ids[0])) { - ret = x509_check_signature(cert->pub, cert); /* self-signed */ - if (ret < 0) - goto error_free_cert; - } else if (!prep->trusted) { + /* See if we can derive the trustability of this certificate. + * + * When it comes to self-signed certificates, we cannot evaluate + * trustedness except by the fact that we obtained it from a trusted + * location. So we just rely on x509_validate_trust() failing in this + * case. + * + * Note that there's a possibility of a self-signed cert matching a + * cert that we have (most likely a duplicate that we already trust) - + * in which case it will be marked trusted. + */ + if (cert->unsupported_sig || cert->self_signed) { + public_key_signature_free(cert->sig); + cert->sig = NULL; + } else { + pr_devel("Cert Signature: %s + %s\n", + cert->sig->pkey_algo, cert->sig->hash_algo); + ret = x509_validate_trust(cert, get_system_trusted_keyring()); if (ret) ret = x509_validate_trust(cert, get_ima_mok_keyring()); + if (ret == -EKEYREJECTED) + goto error_free_cert; if (!ret) - prep->trusted = 1; + prep->trusted = true; } /* Propose a description */ -- cgit v0.10.2 From ad3043fda39db0361d9601685356db4512e914be Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 6 Apr 2016 16:13:34 +0100 Subject: X.509: Fix self-signed determination There's a bug in the code determining whether a certificate is self-signed or not: if they have neither AKID nor SKID then we just assume that the cert is self-signed, which may not be true. Fix this by checking that the raw subject name matches the raw issuer name and that the public key algorithm for the key and signature are both the same in addition to requiring that the AKID bits match. Signed-off-by: David Howells diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c index 752d8d5..fc77a2b 100644 --- a/crypto/asymmetric_keys/x509_public_key.c +++ b/crypto/asymmetric_keys/x509_public_key.c @@ -230,6 +230,11 @@ int x509_check_for_self_signed(struct x509_certificate *cert) pr_devel("==>%s()\n", __func__); + if (cert->raw_subject_size != cert->raw_issuer_size || + memcmp(cert->raw_subject, cert->raw_issuer, + cert->raw_issuer_size) != 0) + goto not_self_signed; + if (cert->sig->auth_ids[0] || cert->sig->auth_ids[1]) { /* If the AKID is present it may have one or two parts. If * both are supplied, both must match. @@ -246,6 +251,10 @@ int x509_check_for_self_signed(struct x509_certificate *cert) goto out; } + ret = -EKEYREJECTED; + if (cert->pub->pkey_algo != cert->sig->pkey_algo) + goto out; + ret = public_key_verify_signature(cert->pub, cert->sig); if (ret < 0) { if (ret == -ENOPKG) { -- cgit v0.10.2 From e68503bd6836ba765dc8e0ee77ea675fedc07e41 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 6 Apr 2016 16:14:24 +0100 Subject: KEYS: Generalise system_verify_data() to provide access to internal content Generalise system_verify_data() to provide access to internal content through a callback. This allows all the PKCS#7 stuff to be hidden inside this function and removed from the PE file parser and the PKCS#7 test key. If external content is not required, NULL should be passed as data to the function. If the callback is not required, that can be set to NULL. The function is now called verify_pkcs7_signature() to contrast with verify_pefile_signature() and the definitions of both have been moved into linux/verification.h along with the key_being_used_for enum. Signed-off-by: David Howells diff --git a/arch/x86/kernel/kexec-bzimage64.c b/arch/x86/kernel/kexec-bzimage64.c index 2af478e..f2356bd 100644 --- a/arch/x86/kernel/kexec-bzimage64.c +++ b/arch/x86/kernel/kexec-bzimage64.c @@ -19,8 +19,7 @@ #include #include #include -#include -#include +#include #include #include @@ -529,18 +528,9 @@ static int bzImage64_cleanup(void *loader_data) #ifdef CONFIG_KEXEC_BZIMAGE_VERIFY_SIG static int bzImage64_verify_sig(const char *kernel, unsigned long kernel_len) { - bool trusted; - int ret; - - ret = verify_pefile_signature(kernel, kernel_len, - system_trusted_keyring, - VERIFYING_KEXEC_PE_SIGNATURE, - &trusted); - if (ret < 0) - return ret; - if (!trusted) - return -EKEYREJECTED; - return 0; + return verify_pefile_signature(kernel, kernel_len, + NULL, + VERIFYING_KEXEC_PE_SIGNATURE); } #endif diff --git a/certs/system_keyring.c b/certs/system_keyring.c index f418032..a83bffe 100644 --- a/certs/system_keyring.c +++ b/certs/system_keyring.c @@ -108,16 +108,25 @@ late_initcall(load_system_certificate_list); #ifdef CONFIG_SYSTEM_DATA_VERIFICATION /** - * Verify a PKCS#7-based signature on system data. - * @data: The data to be verified. + * verify_pkcs7_signature - Verify a PKCS#7-based signature on system data. + * @data: The data to be verified (NULL if expecting internal data). * @len: Size of @data. * @raw_pkcs7: The PKCS#7 message that is the signature. * @pkcs7_len: The size of @raw_pkcs7. + * @trusted_keys: Trusted keys to use (NULL for system_trusted_keyring). * @usage: The use to which the key is being put. + * @view_content: Callback to gain access to content. + * @ctx: Context for callback. */ -int system_verify_data(const void *data, unsigned long len, - const void *raw_pkcs7, size_t pkcs7_len, - enum key_being_used_for usage) +int verify_pkcs7_signature(const void *data, size_t len, + const void *raw_pkcs7, size_t pkcs7_len, + struct key *trusted_keys, + int untrusted_error, + enum key_being_used_for usage, + int (*view_content)(void *ctx, + const void *data, size_t len, + size_t asn1hdrlen), + void *ctx) { struct pkcs7_message *pkcs7; bool trusted; @@ -128,7 +137,7 @@ int system_verify_data(const void *data, unsigned long len, return PTR_ERR(pkcs7); /* The data should be detached - so we need to supply it. */ - if (pkcs7_supply_detached_data(pkcs7, data, len) < 0) { + if (data && pkcs7_supply_detached_data(pkcs7, data, len) < 0) { pr_err("PKCS#7 signature with non-detached data\n"); ret = -EBADMSG; goto error; @@ -138,13 +147,29 @@ int system_verify_data(const void *data, unsigned long len, if (ret < 0) goto error; - ret = pkcs7_validate_trust(pkcs7, system_trusted_keyring, &trusted); + if (!trusted_keys) + trusted_keys = system_trusted_keyring; + ret = pkcs7_validate_trust(pkcs7, trusted_keys, &trusted); if (ret < 0) goto error; - if (!trusted) { + if (!trusted && untrusted_error) { pr_err("PKCS#7 signature not signed with a trusted key\n"); - ret = -ENOKEY; + ret = untrusted_error; + goto error; + } + + if (view_content) { + size_t asn1hdrlen; + + ret = pkcs7_get_content_data(pkcs7, &data, &len, &asn1hdrlen); + if (ret < 0) { + if (ret == -ENODATA) + pr_devel("PKCS#7 message does not contain data\n"); + goto error; + } + + ret = view_content(ctx, data, len, asn1hdrlen); } error: @@ -152,6 +177,6 @@ error: pr_devel("<==%s() = %d\n", __func__, ret); return ret; } -EXPORT_SYMBOL_GPL(system_verify_data); +EXPORT_SYMBOL_GPL(verify_pkcs7_signature); #endif /* CONFIG_SYSTEM_DATA_VERIFICATION */ diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig index 91a7e04..f7d2ef9 100644 --- a/crypto/asymmetric_keys/Kconfig +++ b/crypto/asymmetric_keys/Kconfig @@ -40,8 +40,7 @@ config PKCS7_MESSAGE_PARSER config PKCS7_TEST_KEY tristate "PKCS#7 testing key type" - depends on PKCS7_MESSAGE_PARSER - select SYSTEM_TRUSTED_KEYRING + depends on SYSTEM_DATA_VERIFICATION help This option provides a type of key that can be loaded up from a PKCS#7 message - provided the message is signed by a trusted key. If @@ -54,6 +53,7 @@ config PKCS7_TEST_KEY config SIGNED_PE_FILE_VERIFICATION bool "Support for PE file signature verification" depends on PKCS7_MESSAGE_PARSER=y + depends on SYSTEM_DATA_VERIFICATION select ASN1 select OID_REGISTRY help diff --git a/crypto/asymmetric_keys/mscode_parser.c b/crypto/asymmetric_keys/mscode_parser.c index 3242cbf..6a76d5c 100644 --- a/crypto/asymmetric_keys/mscode_parser.c +++ b/crypto/asymmetric_keys/mscode_parser.c @@ -21,19 +21,13 @@ /* * Parse a Microsoft Individual Code Signing blob */ -int mscode_parse(struct pefile_context *ctx) +int mscode_parse(void *_ctx, const void *content_data, size_t data_len, + size_t asn1hdrlen) { - const void *content_data; - size_t data_len; - int ret; - - ret = pkcs7_get_content_data(ctx->pkcs7, &content_data, &data_len, 1); - - if (ret) { - pr_debug("PKCS#7 message does not contain data\n"); - return ret; - } + struct pefile_context *ctx = _ctx; + content_data -= asn1hdrlen; + data_len += asn1hdrlen; pr_devel("Data: %zu [%*ph]\n", data_len, (unsigned)(data_len), content_data); @@ -129,7 +123,6 @@ int mscode_note_digest(void *context, size_t hdrlen, { struct pefile_context *ctx = context; - ctx->digest = value; - ctx->digest_len = vlen; - return 0; + ctx->digest = kmemdup(value, vlen, GFP_KERNEL); + return ctx->digest ? 0 : -ENOMEM; } diff --git a/crypto/asymmetric_keys/pkcs7_key_type.c b/crypto/asymmetric_keys/pkcs7_key_type.c index e2d0edb..ab9bf53 100644 --- a/crypto/asymmetric_keys/pkcs7_key_type.c +++ b/crypto/asymmetric_keys/pkcs7_key_type.c @@ -13,12 +13,9 @@ #include #include #include +#include #include -#include -#include #include -#include -#include "pkcs7_parser.h" MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("PKCS#7 testing key type"); @@ -29,60 +26,47 @@ MODULE_PARM_DESC(pkcs7_usage, "Usage to specify when verifying the PKCS#7 message"); /* - * Preparse a PKCS#7 wrapped and validated data blob. + * Retrieve the PKCS#7 message content. */ -static int pkcs7_preparse(struct key_preparsed_payload *prep) +static int pkcs7_view_content(void *ctx, const void *data, size_t len, + size_t asn1hdrlen) { - enum key_being_used_for usage = pkcs7_usage; - struct pkcs7_message *pkcs7; - const void *data, *saved_prep_data; - size_t datalen, saved_prep_datalen; - bool trusted; + struct key_preparsed_payload *prep = ctx; + const void *saved_prep_data; + size_t saved_prep_datalen; int ret; - kenter(""); - - if (usage >= NR__KEY_BEING_USED_FOR) { - pr_err("Invalid usage type %d\n", usage); - return -EINVAL; - } - saved_prep_data = prep->data; saved_prep_datalen = prep->datalen; - pkcs7 = pkcs7_parse_message(saved_prep_data, saved_prep_datalen); - if (IS_ERR(pkcs7)) { - ret = PTR_ERR(pkcs7); - goto error; - } - - ret = pkcs7_verify(pkcs7, usage); - if (ret < 0) - goto error_free; - - ret = pkcs7_validate_trust(pkcs7, system_trusted_keyring, &trusted); - if (ret < 0) - goto error_free; - if (!trusted) - pr_warn("PKCS#7 message doesn't chain back to a trusted key\n"); - - ret = pkcs7_get_content_data(pkcs7, &data, &datalen, false); - if (ret < 0) - goto error_free; - prep->data = data; - prep->datalen = datalen; + prep->datalen = len; + ret = user_preparse(prep); + prep->data = saved_prep_data; prep->datalen = saved_prep_datalen; - -error_free: - pkcs7_free_message(pkcs7); -error: - kleave(" = %d", ret); return ret; } /* + * Preparse a PKCS#7 wrapped and validated data blob. + */ +static int pkcs7_preparse(struct key_preparsed_payload *prep) +{ + enum key_being_used_for usage = pkcs7_usage; + + if (usage >= NR__KEY_BEING_USED_FOR) { + pr_err("Invalid usage type %d\n", usage); + return -EINVAL; + } + + return verify_pkcs7_signature(NULL, 0, + prep->data, prep->datalen, + NULL, -ENOKEY, usage, + pkcs7_view_content, prep); +} + +/* * user defined keys take an arbitrary string as the description and an * arbitrary blob of data as the payload */ diff --git a/crypto/asymmetric_keys/pkcs7_parser.c b/crypto/asymmetric_keys/pkcs7_parser.c index 8357016..af4cd86 100644 --- a/crypto/asymmetric_keys/pkcs7_parser.c +++ b/crypto/asymmetric_keys/pkcs7_parser.c @@ -168,24 +168,25 @@ EXPORT_SYMBOL_GPL(pkcs7_parse_message); * @pkcs7: The preparsed PKCS#7 message to access * @_data: Place to return a pointer to the data * @_data_len: Place to return the data length - * @want_wrapper: True if the ASN.1 object header should be included in the data + * @_headerlen: Size of ASN.1 header not included in _data * - * Get access to the data content of the PKCS#7 message, including, optionally, - * the header of the ASN.1 object that contains it. Returns -ENODATA if the - * data object was missing from the message. + * Get access to the data content of the PKCS#7 message. The size of the + * header of the ASN.1 object that contains it is also provided and can be used + * to adjust *_data and *_data_len to get the entire object. + * + * Returns -ENODATA if the data object was missing from the message. */ int pkcs7_get_content_data(const struct pkcs7_message *pkcs7, const void **_data, size_t *_data_len, - bool want_wrapper) + size_t *_headerlen) { - size_t wrapper; - if (!pkcs7->data) return -ENODATA; - wrapper = want_wrapper ? pkcs7->data_hdrlen : 0; - *_data = pkcs7->data - wrapper; - *_data_len = pkcs7->data_len + wrapper; + *_data = pkcs7->data; + *_data_len = pkcs7->data_len; + if (_headerlen) + *_headerlen = pkcs7->data_hdrlen; return 0; } EXPORT_SYMBOL_GPL(pkcs7_get_content_data); diff --git a/crypto/asymmetric_keys/verify_pefile.c b/crypto/asymmetric_keys/verify_pefile.c index 7e8c233..2653510 100644 --- a/crypto/asymmetric_keys/verify_pefile.c +++ b/crypto/asymmetric_keys/verify_pefile.c @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #include "verify_pefile.h" @@ -392,9 +392,8 @@ error_no_desc: * verify_pefile_signature - Verify the signature on a PE binary image * @pebuf: Buffer containing the PE binary image * @pelen: Length of the binary image - * @trust_keyring: Signing certificates to use as starting points + * @trust_keys: Signing certificate(s) to use as starting points * @usage: The use to which the key is being put. - * @_trusted: Set to true if trustworth, false otherwise * * Validate that the certificate chain inside the PKCS#7 message inside the PE * binary image intersects keys we already know and trust. @@ -418,14 +417,10 @@ error_no_desc: * May also return -ENOMEM. */ int verify_pefile_signature(const void *pebuf, unsigned pelen, - struct key *trusted_keyring, - enum key_being_used_for usage, - bool *_trusted) + struct key *trusted_keys, + enum key_being_used_for usage) { - struct pkcs7_message *pkcs7; struct pefile_context ctx; - const void *data; - size_t datalen; int ret; kenter(""); @@ -439,19 +434,10 @@ int verify_pefile_signature(const void *pebuf, unsigned pelen, if (ret < 0) return ret; - pkcs7 = pkcs7_parse_message(pebuf + ctx.sig_offset, ctx.sig_len); - if (IS_ERR(pkcs7)) - return PTR_ERR(pkcs7); - ctx.pkcs7 = pkcs7; - - ret = pkcs7_get_content_data(ctx.pkcs7, &data, &datalen, false); - if (ret < 0 || datalen == 0) { - pr_devel("PKCS#7 message does not contain data\n"); - ret = -EBADMSG; - goto error; - } - - ret = mscode_parse(&ctx); + ret = verify_pkcs7_signature(NULL, 0, + pebuf + ctx.sig_offset, ctx.sig_len, + trusted_keys, -EKEYREJECTED, usage, + mscode_parse, &ctx); if (ret < 0) goto error; @@ -462,16 +448,8 @@ int verify_pefile_signature(const void *pebuf, unsigned pelen, * contents. */ ret = pefile_digest_pe(pebuf, pelen, &ctx); - if (ret < 0) - goto error; - - ret = pkcs7_verify(pkcs7, usage); - if (ret < 0) - goto error; - - ret = pkcs7_validate_trust(pkcs7, trusted_keyring, _trusted); error: - pkcs7_free_message(ctx.pkcs7); + kfree(ctx.digest); return ret; } diff --git a/crypto/asymmetric_keys/verify_pefile.h b/crypto/asymmetric_keys/verify_pefile.h index a133eb8..cd4d209 100644 --- a/crypto/asymmetric_keys/verify_pefile.h +++ b/crypto/asymmetric_keys/verify_pefile.h @@ -9,7 +9,6 @@ * 2 of the Licence, or (at your option) any later version. */ -#include #include #include @@ -23,7 +22,6 @@ struct pefile_context { unsigned sig_offset; unsigned sig_len; const struct section_header *secs; - struct pkcs7_message *pkcs7; /* PKCS#7 MS Individual Code Signing content */ const void *digest; /* Digest */ @@ -39,4 +37,5 @@ struct pefile_context { /* * mscode_parser.c */ -extern int mscode_parse(struct pefile_context *ctx); +extern int mscode_parse(void *_ctx, const void *content_data, size_t data_len, + size_t asn1hdrlen); diff --git a/include/crypto/pkcs7.h b/include/crypto/pkcs7.h index 441aff9..8323e3e 100644 --- a/include/crypto/pkcs7.h +++ b/include/crypto/pkcs7.h @@ -12,6 +12,7 @@ #ifndef _CRYPTO_PKCS7_H #define _CRYPTO_PKCS7_H +#include #include struct key; @@ -26,7 +27,7 @@ extern void pkcs7_free_message(struct pkcs7_message *pkcs7); extern int pkcs7_get_content_data(const struct pkcs7_message *pkcs7, const void **_data, size_t *_datalen, - bool want_wrapper); + size_t *_headerlen); /* * pkcs7_trust.c diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h index 2f5de5c..b3928e8 100644 --- a/include/crypto/public_key.h +++ b/include/crypto/public_key.h @@ -15,20 +15,6 @@ #define _LINUX_PUBLIC_KEY_H /* - * The use to which an asymmetric key is being put. - */ -enum key_being_used_for { - VERIFYING_MODULE_SIGNATURE, - VERIFYING_FIRMWARE_SIGNATURE, - VERIFYING_KEXEC_PE_SIGNATURE, - VERIFYING_KEY_SIGNATURE, - VERIFYING_KEY_SELF_SIGNATURE, - VERIFYING_UNSPECIFIED_SIGNATURE, - NR__KEY_BEING_USED_FOR -}; -extern const char *const key_being_used_for[NR__KEY_BEING_USED_FOR]; - -/* * Cryptographic data for the public-key subtype of the asymmetric key type. * * Note that this may include private part of the key as well as the public diff --git a/include/keys/asymmetric-type.h b/include/keys/asymmetric-type.h index 70a8775..d1e23dd 100644 --- a/include/keys/asymmetric-type.h +++ b/include/keys/asymmetric-type.h @@ -15,6 +15,7 @@ #define _KEYS_ASYMMETRIC_TYPE_H #include +#include extern struct key_type key_type_asymmetric; diff --git a/include/keys/system_keyring.h b/include/keys/system_keyring.h index 39fd38c..b2d645a 100644 --- a/include/keys/system_keyring.h +++ b/include/keys/system_keyring.h @@ -15,6 +15,7 @@ #ifdef CONFIG_SYSTEM_TRUSTED_KEYRING #include +#include #include extern struct key *system_trusted_keyring; @@ -29,12 +30,6 @@ static inline struct key *get_system_trusted_keyring(void) } #endif -#ifdef CONFIG_SYSTEM_DATA_VERIFICATION -extern int system_verify_data(const void *data, unsigned long len, - const void *raw_pkcs7, size_t pkcs7_len, - enum key_being_used_for usage); -#endif - #ifdef CONFIG_IMA_MOK_KEYRING extern struct key *ima_mok_keyring; extern struct key *ima_blacklist_keyring; diff --git a/include/linux/verification.h b/include/linux/verification.h new file mode 100644 index 0000000..bb0fcf9 --- /dev/null +++ b/include/linux/verification.h @@ -0,0 +1,50 @@ +/* Signature verification + * + * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _LINUX_VERIFICATION_H +#define _LINUX_VERIFICATION_H + +/* + * The use to which an asymmetric key is being put. + */ +enum key_being_used_for { + VERIFYING_MODULE_SIGNATURE, + VERIFYING_FIRMWARE_SIGNATURE, + VERIFYING_KEXEC_PE_SIGNATURE, + VERIFYING_KEY_SIGNATURE, + VERIFYING_KEY_SELF_SIGNATURE, + VERIFYING_UNSPECIFIED_SIGNATURE, + NR__KEY_BEING_USED_FOR +}; +extern const char *const key_being_used_for[NR__KEY_BEING_USED_FOR]; + +#ifdef CONFIG_SYSTEM_DATA_VERIFICATION + +struct key; + +extern int verify_pkcs7_signature(const void *data, size_t len, + const void *raw_pkcs7, size_t pkcs7_len, + struct key *trusted_keys, + int untrusted_error, + enum key_being_used_for usage, + int (*view_content)(void *ctx, + const void *data, size_t len, + size_t asn1hdrlen), + void *ctx); + +#ifdef CONFIG_SIGNED_PE_FILE_VERIFICATION +extern int verify_pefile_signature(const void *pebuf, unsigned pelen, + struct key *trusted_keys, + enum key_being_used_for usage); +#endif + +#endif /* CONFIG_SYSTEM_DATA_VERIFICATION */ +#endif /* _LINUX_VERIFY_PEFILE_H */ diff --git a/include/linux/verify_pefile.h b/include/linux/verify_pefile.h deleted file mode 100644 index da2049b..0000000 --- a/include/linux/verify_pefile.h +++ /dev/null @@ -1,22 +0,0 @@ -/* Signed PE file verification - * - * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.com) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public Licence - * as published by the Free Software Foundation; either version - * 2 of the Licence, or (at your option) any later version. - */ - -#ifndef _LINUX_VERIFY_PEFILE_H -#define _LINUX_VERIFY_PEFILE_H - -#include - -extern int verify_pefile_signature(const void *pebuf, unsigned pelen, - struct key *trusted_keyring, - enum key_being_used_for usage, - bool *_trusted); - -#endif /* _LINUX_VERIFY_PEFILE_H */ diff --git a/kernel/module_signing.c b/kernel/module_signing.c index 64b9dea..593aace 100644 --- a/kernel/module_signing.c +++ b/kernel/module_signing.c @@ -80,6 +80,7 @@ int mod_verify_sig(const void *mod, unsigned long *_modlen) return -EBADMSG; } - return system_verify_data(mod, modlen, mod + modlen, sig_len, - VERIFYING_MODULE_SIGNATURE); + return verify_pkcs7_signature(mod, modlen, mod + modlen, sig_len, + NULL, -ENOKEY, VERIFYING_MODULE_SIGNATURE, + NULL, NULL); } -- cgit v0.10.2 From bda850cd214e90b1be0cc25bc48c4f6ac53eb543 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 6 Apr 2016 16:14:24 +0100 Subject: PKCS#7: Make trust determination dependent on contents of trust keyring Make the determination of the trustworthiness of a key dependent on whether a key that can verify it is present in the supplied ring of trusted keys rather than whether or not the verifying key has KEY_FLAG_TRUSTED set. verify_pkcs7_signature() will return -ENOKEY if the PKCS#7 message trust chain cannot be verified. Signed-off-by: David Howells diff --git a/certs/system_keyring.c b/certs/system_keyring.c index a83bffe..dc18869 100644 --- a/certs/system_keyring.c +++ b/certs/system_keyring.c @@ -121,7 +121,6 @@ late_initcall(load_system_certificate_list); int verify_pkcs7_signature(const void *data, size_t len, const void *raw_pkcs7, size_t pkcs7_len, struct key *trusted_keys, - int untrusted_error, enum key_being_used_for usage, int (*view_content)(void *ctx, const void *data, size_t len, @@ -129,7 +128,6 @@ int verify_pkcs7_signature(const void *data, size_t len, void *ctx) { struct pkcs7_message *pkcs7; - bool trusted; int ret; pkcs7 = pkcs7_parse_message(raw_pkcs7, pkcs7_len); @@ -149,13 +147,10 @@ int verify_pkcs7_signature(const void *data, size_t len, if (!trusted_keys) trusted_keys = system_trusted_keyring; - ret = pkcs7_validate_trust(pkcs7, trusted_keys, &trusted); - if (ret < 0) - goto error; - - if (!trusted && untrusted_error) { - pr_err("PKCS#7 signature not signed with a trusted key\n"); - ret = untrusted_error; + ret = pkcs7_validate_trust(pkcs7, trusted_keys); + if (ret < 0) { + if (ret == -ENOKEY) + pr_err("PKCS#7 signature not signed with a trusted key\n"); goto error; } diff --git a/crypto/asymmetric_keys/pkcs7_key_type.c b/crypto/asymmetric_keys/pkcs7_key_type.c index ab9bf53..3b92523 100644 --- a/crypto/asymmetric_keys/pkcs7_key_type.c +++ b/crypto/asymmetric_keys/pkcs7_key_type.c @@ -62,7 +62,7 @@ static int pkcs7_preparse(struct key_preparsed_payload *prep) return verify_pkcs7_signature(NULL, 0, prep->data, prep->datalen, - NULL, -ENOKEY, usage, + NULL, usage, pkcs7_view_content, prep); } diff --git a/crypto/asymmetric_keys/pkcs7_parser.h b/crypto/asymmetric_keys/pkcs7_parser.h index d5eec31..f4e8107 100644 --- a/crypto/asymmetric_keys/pkcs7_parser.h +++ b/crypto/asymmetric_keys/pkcs7_parser.h @@ -22,7 +22,6 @@ struct pkcs7_signed_info { struct pkcs7_signed_info *next; struct x509_certificate *signer; /* Signing certificate (in msg->certs) */ unsigned index; - bool trusted; bool unsupported_crypto; /* T if not usable due to missing crypto */ /* Message digest - the digest of the Content Data (or NULL) */ diff --git a/crypto/asymmetric_keys/pkcs7_trust.c b/crypto/asymmetric_keys/pkcs7_trust.c index b9a5487..36e77cb 100644 --- a/crypto/asymmetric_keys/pkcs7_trust.c +++ b/crypto/asymmetric_keys/pkcs7_trust.c @@ -30,7 +30,6 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, struct public_key_signature *sig = sinfo->sig; struct x509_certificate *x509, *last = NULL, *p; struct key *key; - bool trusted; int ret; kenter(",%u,", sinfo->index); @@ -42,10 +41,8 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, for (x509 = sinfo->signer; x509; x509 = x509->signer) { if (x509->seen) { - if (x509->verified) { - trusted = x509->trusted; + if (x509->verified) goto verified; - } kleave(" = -ENOKEY [cached]"); return -ENOKEY; } @@ -122,7 +119,6 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, matched: ret = verify_signature(key, sig); - trusted = test_bit(KEY_FLAG_TRUSTED, &key->flags); key_put(key); if (ret < 0) { if (ret == -ENOMEM) @@ -134,12 +130,9 @@ matched: verified: if (x509) { x509->verified = true; - for (p = sinfo->signer; p != x509; p = p->signer) { + for (p = sinfo->signer; p != x509; p = p->signer) p->verified = true; - p->trusted = trusted; - } } - sinfo->trusted = trusted; kleave(" = 0"); return 0; } @@ -148,7 +141,6 @@ verified: * pkcs7_validate_trust - Validate PKCS#7 trust chain * @pkcs7: The PKCS#7 certificate to validate * @trust_keyring: Signing certificates to use as starting points - * @_trusted: Set to true if trustworth, false otherwise * * Validate that the certificate chain inside the PKCS#7 message intersects * keys we already know and trust. @@ -170,16 +162,13 @@ verified: * May also return -ENOMEM. */ int pkcs7_validate_trust(struct pkcs7_message *pkcs7, - struct key *trust_keyring, - bool *_trusted) + struct key *trust_keyring) { struct pkcs7_signed_info *sinfo; struct x509_certificate *p; int cached_ret = -ENOKEY; int ret; - *_trusted = false; - for (p = pkcs7->certs; p; p = p->next) p->seen = false; @@ -193,7 +182,6 @@ int pkcs7_validate_trust(struct pkcs7_message *pkcs7, cached_ret = -ENOPKG; continue; case 0: - *_trusted |= sinfo->trusted; cached_ret = 0; continue; default: diff --git a/crypto/asymmetric_keys/verify_pefile.c b/crypto/asymmetric_keys/verify_pefile.c index 2653510..672a94c 100644 --- a/crypto/asymmetric_keys/verify_pefile.c +++ b/crypto/asymmetric_keys/verify_pefile.c @@ -436,7 +436,7 @@ int verify_pefile_signature(const void *pebuf, unsigned pelen, ret = verify_pkcs7_signature(NULL, 0, pebuf + ctx.sig_offset, ctx.sig_len, - trusted_keys, -EKEYREJECTED, usage, + trusted_keys, usage, mscode_parse, &ctx); if (ret < 0) goto error; diff --git a/crypto/asymmetric_keys/x509_parser.h b/crypto/asymmetric_keys/x509_parser.h index f24f4d8..05eef1c 100644 --- a/crypto/asymmetric_keys/x509_parser.h +++ b/crypto/asymmetric_keys/x509_parser.h @@ -39,7 +39,6 @@ struct x509_certificate { unsigned index; bool seen; /* Infinite recursion prevention */ bool verified; - bool trusted; bool self_signed; /* T if self-signed (check unsupported_sig too) */ bool unsupported_key; /* T if key uses unsupported crypto */ bool unsupported_sig; /* T if signature uses unsupported crypto */ diff --git a/include/crypto/pkcs7.h b/include/crypto/pkcs7.h index 8323e3e..583f199 100644 --- a/include/crypto/pkcs7.h +++ b/include/crypto/pkcs7.h @@ -33,8 +33,7 @@ extern int pkcs7_get_content_data(const struct pkcs7_message *pkcs7, * pkcs7_trust.c */ extern int pkcs7_validate_trust(struct pkcs7_message *pkcs7, - struct key *trust_keyring, - bool *_trusted); + struct key *trust_keyring); /* * pkcs7_verify.c diff --git a/include/linux/verification.h b/include/linux/verification.h index bb0fcf9..a10549a 100644 --- a/include/linux/verification.h +++ b/include/linux/verification.h @@ -33,7 +33,6 @@ struct key; extern int verify_pkcs7_signature(const void *data, size_t len, const void *raw_pkcs7, size_t pkcs7_len, struct key *trusted_keys, - int untrusted_error, enum key_being_used_for usage, int (*view_content)(void *ctx, const void *data, size_t len, diff --git a/kernel/module_signing.c b/kernel/module_signing.c index 593aace..6a64e03 100644 --- a/kernel/module_signing.c +++ b/kernel/module_signing.c @@ -81,6 +81,6 @@ int mod_verify_sig(const void *mod, unsigned long *_modlen) } return verify_pkcs7_signature(mod, modlen, mod + modlen, sig_len, - NULL, -ENOKEY, VERIFYING_MODULE_SIGNATURE, + NULL, VERIFYING_MODULE_SIGNATURE, NULL, NULL); } -- cgit v0.10.2 From 3c9d6296b7aee536a96ea2b53a15d23511738c1c Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Fri, 8 Apr 2016 12:20:30 +0200 Subject: security: drop the unused hook skb_owned_by The skb_owned_by hook was added with the commit ca10b9e9a8ca ("selinux: add a skb_owned_by() hook") and later removed when said commit was reverted. Later on, when switching to list of hooks, a field named 'skb_owned_by' was included into the security_hook_head struct, but without any users nor caller. This commit removes the said left-over field. Fixes: b1d9e6b0646d ("LSM: Switch to lists of hooks") Signed-off-by: Paolo Abeni Acked-by: Casey Schaufler Acked-by: Paul Moore Signed-off-by: James Morris diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index cdee11c..ae25378 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -1804,7 +1804,6 @@ struct security_hook_heads { struct list_head tun_dev_attach_queue; struct list_head tun_dev_attach; struct list_head tun_dev_open; - struct list_head skb_owned_by; #endif /* CONFIG_SECURITY_NETWORK */ #ifdef CONFIG_SECURITY_NETWORK_XFRM struct list_head xfrm_policy_alloc_security; diff --git a/security/security.c b/security/security.c index 3644b034..554c3fb 100644 --- a/security/security.c +++ b/security/security.c @@ -1848,7 +1848,6 @@ struct security_hook_heads security_hook_heads = { .tun_dev_attach = LIST_HEAD_INIT(security_hook_heads.tun_dev_attach), .tun_dev_open = LIST_HEAD_INIT(security_hook_heads.tun_dev_open), - .skb_owned_by = LIST_HEAD_INIT(security_hook_heads.skb_owned_by), #endif /* CONFIG_SECURITY_NETWORK */ #ifdef CONFIG_SECURITY_NETWORK_XFRM .xfrm_policy_alloc_security = -- cgit v0.10.2 From 5ac7eace2d00eab5ae0e9fdee63e38aee6001f7c Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 6 Apr 2016 16:14:24 +0100 Subject: KEYS: Add a facility to restrict new links into a keyring Add a facility whereby proposed new links to be added to a keyring can be vetted, permitting them to be rejected if necessary. This can be used to block public keys from which the signature cannot be verified or for which the signature verification fails. It could also be used to provide blacklisting. This affects operations like add_key(), KEYCTL_LINK and KEYCTL_INSTANTIATE. To this end: (1) A function pointer is added to the key struct that, if set, points to the vetting function. This is called as: int (*restrict_link)(struct key *keyring, const struct key_type *key_type, unsigned long key_flags, const union key_payload *key_payload), where 'keyring' will be the keyring being added to, key_type and key_payload will describe the key being added and key_flags[*] can be AND'ed with KEY_FLAG_TRUSTED. [*] This parameter will be removed in a later patch when KEY_FLAG_TRUSTED is removed. The function should return 0 to allow the link to take place or an error (typically -ENOKEY, -ENOPKG or -EKEYREJECTED) to reject the link. The pointer should not be set directly, but rather should be set through keyring_alloc(). Note that if called during add_key(), preparse is called before this method, but a key isn't actually allocated until after this function is called. (2) KEY_ALLOC_BYPASS_RESTRICTION is added. This can be passed to key_create_or_update() or key_instantiate_and_link() to bypass the restriction check. (3) KEY_FLAG_TRUSTED_ONLY is removed. The entire contents of a keyring with this restriction emplaced can be considered 'trustworthy' by virtue of being in the keyring when that keyring is consulted. (4) key_alloc() and keyring_alloc() take an extra argument that will be used to set restrict_link in the new key. This ensures that the pointer is set before the key is published, thus preventing a window of unrestrictedness. Normally this argument will be NULL. (5) As a temporary affair, keyring_restrict_trusted_only() is added. It should be passed to keyring_alloc() as the extra argument instead of setting KEY_FLAG_TRUSTED_ONLY on a keyring. This will be replaced in a later patch with functions that look in the appropriate places for authoritative keys. Signed-off-by: David Howells Reviewed-by: Mimi Zohar diff --git a/Documentation/security/keys.txt b/Documentation/security/keys.txt index 8c18387..a6a50b3 100644 --- a/Documentation/security/keys.txt +++ b/Documentation/security/keys.txt @@ -999,6 +999,10 @@ payload contents" for more information. struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid, const struct cred *cred, key_perm_t perm, + int (*restrict_link)(struct key *, + const struct key_type *, + unsigned long, + const union key_payload *), unsigned long flags, struct key *dest); @@ -1010,6 +1014,24 @@ payload contents" for more information. KEY_ALLOC_NOT_IN_QUOTA in flags if the keyring shouldn't be accounted towards the user's quota). Error ENOMEM can also be returned. + If restrict_link not NULL, it should point to a function that will be + called each time an attempt is made to link a key into the new keyring. + This function is called to check whether a key may be added into the keying + or not. Callers of key_create_or_update() within the kernel can pass + KEY_ALLOC_BYPASS_RESTRICTION to suppress the check. An example of using + this is to manage rings of cryptographic keys that are set up when the + kernel boots where userspace is also permitted to add keys - provided they + can be verified by a key the kernel already has. + + When called, the restriction function will be passed the keyring being + added to, the key flags value and the type and payload of the key being + added. Note that when a new key is being created, this is called between + payload preparsing and actual key creation. The function should return 0 + to allow the link or an error to reject it. + + A convenience function, restrict_link_reject, exists to always return + -EPERM to in this case. + (*) To check the validity of a key, this function can be called: diff --git a/certs/system_keyring.c b/certs/system_keyring.c index dc18869..417d658 100644 --- a/certs/system_keyring.c +++ b/certs/system_keyring.c @@ -36,11 +36,10 @@ static __init int system_trusted_keyring_init(void) KUIDT_INIT(0), KGIDT_INIT(0), current_cred(), ((KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ | KEY_USR_SEARCH), - KEY_ALLOC_NOT_IN_QUOTA, NULL); + KEY_ALLOC_NOT_IN_QUOTA, + keyring_restrict_trusted_only, NULL); if (IS_ERR(system_trusted_keyring)) panic("Can't allocate system trusted keyring\n"); - - set_bit(KEY_FLAG_TRUSTED_ONLY, &system_trusted_keyring->flags); return 0; } @@ -85,7 +84,8 @@ static __init int load_system_certificate_list(void) KEY_USR_VIEW | KEY_USR_READ), KEY_ALLOC_NOT_IN_QUOTA | KEY_ALLOC_TRUSTED | - KEY_ALLOC_BUILT_IN); + KEY_ALLOC_BUILT_IN | + KEY_ALLOC_BYPASS_RESTRICTION); if (IS_ERR(key)) { pr_err("Problem loading in-kernel X.509 certificate (%ld)\n", PTR_ERR(key)); diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c index 3f93125..71e8a56 100644 --- a/fs/cifs/cifsacl.c +++ b/fs/cifs/cifsacl.c @@ -360,7 +360,7 @@ init_cifs_idmap(void) GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ, - KEY_ALLOC_NOT_IN_QUOTA, NULL); + KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL); if (IS_ERR(keyring)) { ret = PTR_ERR(keyring); goto failed_put_cred; diff --git a/fs/nfs/nfs4idmap.c b/fs/nfs/nfs4idmap.c index 5ba22c6..c444285 100644 --- a/fs/nfs/nfs4idmap.c +++ b/fs/nfs/nfs4idmap.c @@ -201,7 +201,7 @@ int nfs_idmap_init(void) GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ, - KEY_ALLOC_NOT_IN_QUOTA, NULL); + KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL); if (IS_ERR(keyring)) { ret = PTR_ERR(keyring); goto failed_put_cred; diff --git a/include/linux/key.h b/include/linux/key.h index 5f5b112..83b6036 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -174,10 +174,9 @@ struct key { #define KEY_FLAG_ROOT_CAN_CLEAR 6 /* set if key can be cleared by root without permission */ #define KEY_FLAG_INVALIDATED 7 /* set if key has been invalidated */ #define KEY_FLAG_TRUSTED 8 /* set if key is trusted */ -#define KEY_FLAG_TRUSTED_ONLY 9 /* set if keyring only accepts links to trusted keys */ -#define KEY_FLAG_BUILTIN 10 /* set if key is builtin */ -#define KEY_FLAG_ROOT_CAN_INVAL 11 /* set if key can be invalidated by root without permission */ -#define KEY_FLAG_KEEP 12 /* set if key should not be removed */ +#define KEY_FLAG_BUILTIN 9 /* set if key is built in to the kernel */ +#define KEY_FLAG_ROOT_CAN_INVAL 10 /* set if key can be invalidated by root without permission */ +#define KEY_FLAG_KEEP 11 /* set if key should not be removed */ /* the key type and key description string * - the desc is used to match a key against search criteria @@ -205,6 +204,21 @@ struct key { }; int reject_error; }; + + /* This is set on a keyring to restrict the addition of a link to a key + * to it. If this method isn't provided then it is assumed that the + * keyring is open to any addition. It is ignored for non-keyring + * keys. + * + * This is intended for use with rings of trusted keys whereby addition + * to the keyring needs to be controlled. KEY_ALLOC_BYPASS_RESTRICTION + * overrides this, allowing the kernel to add extra keys without + * restriction. + */ + int (*restrict_link)(struct key *keyring, + const struct key_type *type, + unsigned long flags, + const union key_payload *payload); }; extern struct key *key_alloc(struct key_type *type, @@ -212,14 +226,19 @@ extern struct key *key_alloc(struct key_type *type, kuid_t uid, kgid_t gid, const struct cred *cred, key_perm_t perm, - unsigned long flags); + unsigned long flags, + int (*restrict_link)(struct key *, + const struct key_type *, + unsigned long, + const union key_payload *)); -#define KEY_ALLOC_IN_QUOTA 0x0000 /* add to quota, reject if would overrun */ -#define KEY_ALLOC_QUOTA_OVERRUN 0x0001 /* add to quota, permit even if overrun */ -#define KEY_ALLOC_NOT_IN_QUOTA 0x0002 /* not in quota */ -#define KEY_ALLOC_TRUSTED 0x0004 /* Key should be flagged as trusted */ -#define KEY_ALLOC_BUILT_IN 0x0008 /* Key is built into kernel */ +#define KEY_ALLOC_IN_QUOTA 0x0000 /* add to quota, reject if would overrun */ +#define KEY_ALLOC_QUOTA_OVERRUN 0x0001 /* add to quota, permit even if overrun */ +#define KEY_ALLOC_NOT_IN_QUOTA 0x0002 /* not in quota */ +#define KEY_ALLOC_TRUSTED 0x0004 /* Key should be flagged as trusted */ +#define KEY_ALLOC_BUILT_IN 0x0008 /* Key is built into kernel */ +#define KEY_ALLOC_BYPASS_RESTRICTION 0x0010 /* Override the check on restricted keyrings */ extern void key_revoke(struct key *key); extern void key_invalidate(struct key *key); @@ -288,8 +307,22 @@ extern struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid const struct cred *cred, key_perm_t perm, unsigned long flags, + int (*restrict_link)(struct key *, + const struct key_type *, + unsigned long, + const union key_payload *), struct key *dest); +extern int keyring_restrict_trusted_only(struct key *keyring, + const struct key_type *type, + unsigned long, + const union key_payload *payload); + +extern int restrict_link_reject(struct key *keyring, + const struct key_type *type, + unsigned long flags, + const union key_payload *payload); + extern int keyring_clear(struct key *keyring); extern key_ref_t keyring_search(key_ref_t keyring, diff --git a/net/dns_resolver/dns_key.c b/net/dns_resolver/dns_key.c index c79b85e..8737412 100644 --- a/net/dns_resolver/dns_key.c +++ b/net/dns_resolver/dns_key.c @@ -281,7 +281,7 @@ static int __init init_dns_resolver(void) GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ, - KEY_ALLOC_NOT_IN_QUOTA, NULL); + KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL); if (IS_ERR(keyring)) { ret = PTR_ERR(keyring); goto failed_put_cred; diff --git a/net/rxrpc/ar-key.c b/net/rxrpc/ar-key.c index 3fb492e..1021b4c 100644 --- a/net/rxrpc/ar-key.c +++ b/net/rxrpc/ar-key.c @@ -965,7 +965,7 @@ int rxrpc_get_server_data_key(struct rxrpc_connection *conn, key = key_alloc(&key_type_rxrpc, "x", GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, 0, - KEY_ALLOC_NOT_IN_QUOTA); + KEY_ALLOC_NOT_IN_QUOTA, NULL); if (IS_ERR(key)) { _leave(" = -ENOMEM [alloc %ld]", PTR_ERR(key)); return -ENOMEM; @@ -1012,7 +1012,7 @@ struct key *rxrpc_get_null_key(const char *keyname) key = key_alloc(&key_type_rxrpc, keyname, GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, - KEY_POS_SEARCH, KEY_ALLOC_NOT_IN_QUOTA); + KEY_POS_SEARCH, KEY_ALLOC_NOT_IN_QUOTA, NULL); if (IS_ERR(key)) return key; diff --git a/security/integrity/digsig.c b/security/integrity/digsig.c index 8ef1511..659566c 100644 --- a/security/integrity/digsig.c +++ b/security/integrity/digsig.c @@ -83,10 +83,9 @@ int __init integrity_init_keyring(const unsigned int id) ((KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ | KEY_USR_WRITE | KEY_USR_SEARCH), - KEY_ALLOC_NOT_IN_QUOTA, NULL); - if (!IS_ERR(keyring[id])) - set_bit(KEY_FLAG_TRUSTED_ONLY, &keyring[id]->flags); - else { + KEY_ALLOC_NOT_IN_QUOTA, + NULL, NULL); + if (IS_ERR(keyring[id])) { err = PTR_ERR(keyring[id]); pr_info("Can't allocate %s keyring (%d)\n", keyring_name[id], err); diff --git a/security/integrity/ima/ima_mok.c b/security/integrity/ima/ima_mok.c index 676885e..ef91248 100644 --- a/security/integrity/ima/ima_mok.c +++ b/security/integrity/ima/ima_mok.c @@ -35,20 +35,20 @@ __init int ima_mok_init(void) (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ | KEY_USR_WRITE | KEY_USR_SEARCH, - KEY_ALLOC_NOT_IN_QUOTA, NULL); + KEY_ALLOC_NOT_IN_QUOTA, + keyring_restrict_trusted_only, NULL); ima_blacklist_keyring = keyring_alloc(".ima_blacklist", KUIDT_INIT(0), KGIDT_INIT(0), current_cred(), (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ | KEY_USR_WRITE | KEY_USR_SEARCH, - KEY_ALLOC_NOT_IN_QUOTA, NULL); + KEY_ALLOC_NOT_IN_QUOTA, + keyring_restrict_trusted_only, NULL); if (IS_ERR(ima_mok_keyring) || IS_ERR(ima_blacklist_keyring)) panic("Can't allocate IMA MOK or blacklist keyrings."); - set_bit(KEY_FLAG_TRUSTED_ONLY, &ima_mok_keyring->flags); - set_bit(KEY_FLAG_TRUSTED_ONLY, &ima_blacklist_keyring->flags); set_bit(KEY_FLAG_KEEP, &ima_blacklist_keyring->flags); return 0; } diff --git a/security/keys/key.c b/security/keys/key.c index b287551..deb8817 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -201,6 +201,7 @@ serial_exists: * @cred: The credentials specifying UID namespace. * @perm: The permissions mask of the new key. * @flags: Flags specifying quota properties. + * @restrict_link: Optional link restriction method for new keyrings. * * Allocate a key of the specified type with the attributes given. The key is * returned in an uninstantiated state and the caller needs to instantiate the @@ -223,7 +224,11 @@ serial_exists: */ struct key *key_alloc(struct key_type *type, const char *desc, kuid_t uid, kgid_t gid, const struct cred *cred, - key_perm_t perm, unsigned long flags) + key_perm_t perm, unsigned long flags, + int (*restrict_link)(struct key *, + const struct key_type *, + unsigned long, + const union key_payload *)) { struct key_user *user = NULL; struct key *key; @@ -291,6 +296,7 @@ struct key *key_alloc(struct key_type *type, const char *desc, key->uid = uid; key->gid = gid; key->perm = perm; + key->restrict_link = restrict_link; if (!(flags & KEY_ALLOC_NOT_IN_QUOTA)) key->flags |= 1 << KEY_FLAG_IN_QUOTA; @@ -496,6 +502,12 @@ int key_instantiate_and_link(struct key *key, } if (keyring) { + if (keyring->restrict_link) { + ret = keyring->restrict_link(keyring, key->type, + key->flags, &prep.payload); + if (ret < 0) + goto error; + } ret = __key_link_begin(keyring, &key->index_key, &edit); if (ret < 0) goto error; @@ -551,8 +563,12 @@ int key_reject_and_link(struct key *key, awaken = 0; ret = -EBUSY; - if (keyring) + if (keyring) { + if (keyring->restrict_link) + return -EPERM; + link_ret = __key_link_begin(keyring, &key->index_key, &edit); + } mutex_lock(&key_construction_mutex); @@ -793,6 +809,10 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, struct key *keyring, *key = NULL; key_ref_t key_ref; int ret; + int (*restrict_link)(struct key *, + const struct key_type *, + unsigned long, + const union key_payload *) = NULL; /* look up the key type to see if it's one of the registered kernel * types */ @@ -811,6 +831,10 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, key_check(keyring); + key_ref = ERR_PTR(-EPERM); + if (!(flags & KEY_ALLOC_BYPASS_RESTRICTION)) + restrict_link = keyring->restrict_link; + key_ref = ERR_PTR(-ENOTDIR); if (keyring->type != &key_type_keyring) goto error_put_type; @@ -835,10 +859,15 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, } index_key.desc_len = strlen(index_key.description); - key_ref = ERR_PTR(-EPERM); - if (!prep.trusted && test_bit(KEY_FLAG_TRUSTED_ONLY, &keyring->flags)) - goto error_free_prep; - flags |= prep.trusted ? KEY_ALLOC_TRUSTED : 0; + if (restrict_link) { + unsigned long kflags = prep.trusted ? KEY_FLAG_TRUSTED : 0; + ret = restrict_link(keyring, + index_key.type, kflags, &prep.payload); + if (ret < 0) { + key_ref = ERR_PTR(ret); + goto error_free_prep; + } + } ret = __key_link_begin(keyring, &index_key, &edit); if (ret < 0) { @@ -879,7 +908,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, /* allocate a new key */ key = key_alloc(index_key.type, index_key.description, - cred->fsuid, cred->fsgid, cred, perm, flags); + cred->fsuid, cred->fsgid, cred, perm, flags, NULL); if (IS_ERR(key)) { key_ref = ERR_CAST(key); goto error_link_end; diff --git a/security/keys/keyring.c b/security/keys/keyring.c index f931ccf..d2d1f33 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -491,13 +491,18 @@ static long keyring_read(const struct key *keyring, */ struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid, const struct cred *cred, key_perm_t perm, - unsigned long flags, struct key *dest) + unsigned long flags, + int (*restrict_link)(struct key *, + const struct key_type *, + unsigned long, + const union key_payload *), + struct key *dest) { struct key *keyring; int ret; keyring = key_alloc(&key_type_keyring, description, - uid, gid, cred, perm, flags); + uid, gid, cred, perm, flags, restrict_link); if (!IS_ERR(keyring)) { ret = key_instantiate_and_link(keyring, NULL, 0, dest, NULL); if (ret < 0) { @@ -510,6 +515,51 @@ struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid, } EXPORT_SYMBOL(keyring_alloc); +/** + * keyring_restrict_trusted_only - Restrict additions to a keyring to trusted keys only + * @keyring: The keyring being added to. + * @type: The type of key being added. + * @flags: The key flags. + * @payload: The payload of the key intended to be added. + * + * Reject the addition of any links to a keyring that point to keys that aren't + * marked as being trusted. It can be overridden by passing + * KEY_ALLOC_BYPASS_RESTRICTION to key_instantiate_and_link() when adding a key + * to a keyring. + * + * This is meant to be passed as the restrict_link parameter to + * keyring_alloc(). + */ +int keyring_restrict_trusted_only(struct key *keyring, + const struct key_type *type, + unsigned long flags, + const union key_payload *payload) +{ + return flags & KEY_FLAG_TRUSTED ? 0 : -EPERM; +} + +/** + * restrict_link_reject - Give -EPERM to restrict link + * @keyring: The keyring being added to. + * @type: The type of key being added. + * @flags: The key flags. + * @payload: The payload of the key intended to be added. + * + * Reject the addition of any links to a keyring. It can be overridden by + * passing KEY_ALLOC_BYPASS_RESTRICTION to key_instantiate_and_link() when + * adding a key to a keyring. + * + * This is meant to be passed as the restrict_link parameter to + * keyring_alloc(). + */ +int restrict_link_reject(struct key *keyring, + const struct key_type *type, + unsigned long flags, + const union key_payload *payload) +{ + return -EPERM; +} + /* * By default, we keys found by getting an exact match on their descriptions. */ @@ -1191,6 +1241,17 @@ void __key_link_end(struct key *keyring, up_write(&keyring->sem); } +/* + * Check addition of keys to restricted keyrings. + */ +static int __key_link_check_restriction(struct key *keyring, struct key *key) +{ + if (!keyring->restrict_link) + return 0; + return keyring->restrict_link(keyring, + key->type, key->flags, &key->payload); +} + /** * key_link - Link a key to a keyring * @keyring: The keyring to make the link in. @@ -1221,14 +1282,12 @@ int key_link(struct key *keyring, struct key *key) key_check(keyring); key_check(key); - if (test_bit(KEY_FLAG_TRUSTED_ONLY, &keyring->flags) && - !test_bit(KEY_FLAG_TRUSTED, &key->flags)) - return -EPERM; - ret = __key_link_begin(keyring, &key->index_key, &edit); if (ret == 0) { kdebug("begun {%d,%d}", keyring->serial, atomic_read(&keyring->usage)); - ret = __key_link_check_live_key(keyring, key); + ret = __key_link_check_restriction(keyring, key); + if (ret == 0) + ret = __key_link_check_live_key(keyring, key); if (ret == 0) __key_link(key, &edit); __key_link_end(keyring, &key->index_key, edit); diff --git a/security/keys/persistent.c b/security/keys/persistent.c index c9fae5e..2ef45b3 100644 --- a/security/keys/persistent.c +++ b/security/keys/persistent.c @@ -26,7 +26,7 @@ static int key_create_persistent_register(struct user_namespace *ns) current_cred(), ((KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ), - KEY_ALLOC_NOT_IN_QUOTA, NULL); + KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL); if (IS_ERR(reg)) return PTR_ERR(reg); @@ -60,7 +60,7 @@ static key_ref_t key_create_persistent(struct user_namespace *ns, kuid_t uid, uid, INVALID_GID, current_cred(), ((KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ), - KEY_ALLOC_NOT_IN_QUOTA, + KEY_ALLOC_NOT_IN_QUOTA, NULL, ns->persistent_keyring_register); if (IS_ERR(persistent)) return ERR_CAST(persistent); diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index e6d50172..40a8852 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -76,7 +76,8 @@ int install_user_keyrings(void) if (IS_ERR(uid_keyring)) { uid_keyring = keyring_alloc(buf, user->uid, INVALID_GID, cred, user_keyring_perm, - KEY_ALLOC_IN_QUOTA, NULL); + KEY_ALLOC_IN_QUOTA, + NULL, NULL); if (IS_ERR(uid_keyring)) { ret = PTR_ERR(uid_keyring); goto error; @@ -92,7 +93,8 @@ int install_user_keyrings(void) session_keyring = keyring_alloc(buf, user->uid, INVALID_GID, cred, user_keyring_perm, - KEY_ALLOC_IN_QUOTA, NULL); + KEY_ALLOC_IN_QUOTA, + NULL, NULL); if (IS_ERR(session_keyring)) { ret = PTR_ERR(session_keyring); goto error_release; @@ -134,7 +136,8 @@ int install_thread_keyring_to_cred(struct cred *new) keyring = keyring_alloc("_tid", new->uid, new->gid, new, KEY_POS_ALL | KEY_USR_VIEW, - KEY_ALLOC_QUOTA_OVERRUN, NULL); + KEY_ALLOC_QUOTA_OVERRUN, + NULL, NULL); if (IS_ERR(keyring)) return PTR_ERR(keyring); @@ -180,7 +183,8 @@ int install_process_keyring_to_cred(struct cred *new) keyring = keyring_alloc("_pid", new->uid, new->gid, new, KEY_POS_ALL | KEY_USR_VIEW, - KEY_ALLOC_QUOTA_OVERRUN, NULL); + KEY_ALLOC_QUOTA_OVERRUN, + NULL, NULL); if (IS_ERR(keyring)) return PTR_ERR(keyring); @@ -231,7 +235,7 @@ int install_session_keyring_to_cred(struct cred *cred, struct key *keyring) keyring = keyring_alloc("_ses", cred->uid, cred->gid, cred, KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ, - flags, NULL); + flags, NULL, NULL); if (IS_ERR(keyring)) return PTR_ERR(keyring); } else { @@ -785,7 +789,7 @@ long join_session_keyring(const char *name) keyring = keyring_alloc( name, old->uid, old->gid, old, KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ | KEY_USR_LINK, - KEY_ALLOC_IN_QUOTA, NULL); + KEY_ALLOC_IN_QUOTA, NULL, NULL); if (IS_ERR(keyring)) { ret = PTR_ERR(keyring); goto error2; diff --git a/security/keys/request_key.c b/security/keys/request_key.c index c7a117c..a29e355 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -116,7 +116,7 @@ static int call_sbin_request_key(struct key_construction *cons, cred = get_current_cred(); keyring = keyring_alloc(desc, cred->fsuid, cred->fsgid, cred, KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ, - KEY_ALLOC_QUOTA_OVERRUN, NULL); + KEY_ALLOC_QUOTA_OVERRUN, NULL, NULL); put_cred(cred); if (IS_ERR(keyring)) { ret = PTR_ERR(keyring); @@ -355,7 +355,7 @@ static int construct_alloc_key(struct keyring_search_context *ctx, key = key_alloc(ctx->index_key.type, ctx->index_key.description, ctx->cred->fsuid, ctx->cred->fsgid, ctx->cred, - perm, flags); + perm, flags, NULL); if (IS_ERR(key)) goto alloc_failed; diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c index 4f0f112..9db8b4a 100644 --- a/security/keys/request_key_auth.c +++ b/security/keys/request_key_auth.c @@ -202,7 +202,7 @@ struct key *request_key_auth_new(struct key *target, const void *callout_info, authkey = key_alloc(&key_type_request_key_auth, desc, cred->fsuid, cred->fsgid, cred, KEY_POS_VIEW | KEY_POS_READ | KEY_POS_SEARCH | - KEY_USR_VIEW, KEY_ALLOC_NOT_IN_QUOTA); + KEY_USR_VIEW, KEY_ALLOC_NOT_IN_QUOTA, NULL); if (IS_ERR(authkey)) { ret = PTR_ERR(authkey); goto error_alloc; -- cgit v0.10.2 From 983023f28bff62b4462fd3575a86a8947ac592d8 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 6 Apr 2016 16:14:25 +0100 Subject: KEYS: Move x509_request_asymmetric_key() to asymmetric_type.c Move x509_request_asymmetric_key() to asymmetric_type.c so that it can be generalised. Signed-off-by: David Howells diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c index a79d301..c4d66cd 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -35,6 +35,95 @@ static LIST_HEAD(asymmetric_key_parsers); static DECLARE_RWSEM(asymmetric_key_parsers_sem); /** + * x509_request_asymmetric_key - Request a key by X.509 certificate params. + * @keyring: The keys to search. + * @id: The issuer & serialNumber to look for or NULL. + * @skid: The subjectKeyIdentifier to look for or NULL. + * @partial: Use partial match if true, exact if false. + * + * Find a key in the given keyring by identifier. The preferred identifier is + * the issuer + serialNumber and the fallback identifier is the + * subjectKeyIdentifier. If both are given, the lookup is by the former, but + * the latter must also match. + */ +struct key *x509_request_asymmetric_key(struct key *keyring, + const struct asymmetric_key_id *id, + const struct asymmetric_key_id *skid, + bool partial) +{ + struct key *key; + key_ref_t ref; + const char *lookup; + char *req, *p; + int len; + + if (id) { + lookup = id->data; + len = id->len; + } else { + lookup = skid->data; + len = skid->len; + } + + /* Construct an identifier "id:". */ + p = req = kmalloc(2 + 1 + len * 2 + 1, GFP_KERNEL); + if (!req) + return ERR_PTR(-ENOMEM); + + if (partial) { + *p++ = 'i'; + *p++ = 'd'; + } else { + *p++ = 'e'; + *p++ = 'x'; + } + *p++ = ':'; + p = bin2hex(p, lookup, len); + *p = 0; + + pr_debug("Look up: \"%s\"\n", req); + + ref = keyring_search(make_key_ref(keyring, 1), + &key_type_asymmetric, req); + if (IS_ERR(ref)) + pr_debug("Request for key '%s' err %ld\n", req, PTR_ERR(ref)); + kfree(req); + + if (IS_ERR(ref)) { + switch (PTR_ERR(ref)) { + /* Hide some search errors */ + case -EACCES: + case -ENOTDIR: + case -EAGAIN: + return ERR_PTR(-ENOKEY); + default: + return ERR_CAST(ref); + } + } + + key = key_ref_to_ptr(ref); + if (id && skid) { + const struct asymmetric_key_ids *kids = asymmetric_key_ids(key); + if (!kids->id[1]) { + pr_debug("issuer+serial match, but expected SKID missing\n"); + goto reject; + } + if (!asymmetric_key_id_same(skid, kids->id[1])) { + pr_debug("issuer+serial match, but SKID does not\n"); + goto reject; + } + } + + pr_devel("<==%s() = 0 [%x]\n", __func__, key_serial(key)); + return key; + +reject: + key_put(key); + return ERR_PTR(-EKEYREJECTED); +} +EXPORT_SYMBOL_GPL(x509_request_asymmetric_key); + +/** * asymmetric_key_generate_id: Construct an asymmetric key ID * @val_1: First binary blob * @len_1: Length of first binary blob diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c index fc77a2b..2fb5941 100644 --- a/crypto/asymmetric_keys/x509_public_key.c +++ b/crypto/asymmetric_keys/x509_public_key.c @@ -58,95 +58,6 @@ static int __init ca_keys_setup(char *str) __setup("ca_keys=", ca_keys_setup); #endif -/** - * x509_request_asymmetric_key - Request a key by X.509 certificate params. - * @keyring: The keys to search. - * @id: The issuer & serialNumber to look for or NULL. - * @skid: The subjectKeyIdentifier to look for or NULL. - * @partial: Use partial match if true, exact if false. - * - * Find a key in the given keyring by identifier. The preferred identifier is - * the issuer + serialNumber and the fallback identifier is the - * subjectKeyIdentifier. If both are given, the lookup is by the former, but - * the latter must also match. - */ -struct key *x509_request_asymmetric_key(struct key *keyring, - const struct asymmetric_key_id *id, - const struct asymmetric_key_id *skid, - bool partial) -{ - struct key *key; - key_ref_t ref; - const char *lookup; - char *req, *p; - int len; - - if (id) { - lookup = id->data; - len = id->len; - } else { - lookup = skid->data; - len = skid->len; - } - - /* Construct an identifier "id:". */ - p = req = kmalloc(2 + 1 + len * 2 + 1, GFP_KERNEL); - if (!req) - return ERR_PTR(-ENOMEM); - - if (partial) { - *p++ = 'i'; - *p++ = 'd'; - } else { - *p++ = 'e'; - *p++ = 'x'; - } - *p++ = ':'; - p = bin2hex(p, lookup, len); - *p = 0; - - pr_debug("Look up: \"%s\"\n", req); - - ref = keyring_search(make_key_ref(keyring, 1), - &key_type_asymmetric, req); - if (IS_ERR(ref)) - pr_debug("Request for key '%s' err %ld\n", req, PTR_ERR(ref)); - kfree(req); - - if (IS_ERR(ref)) { - switch (PTR_ERR(ref)) { - /* Hide some search errors */ - case -EACCES: - case -ENOTDIR: - case -EAGAIN: - return ERR_PTR(-ENOKEY); - default: - return ERR_CAST(ref); - } - } - - key = key_ref_to_ptr(ref); - if (id && skid) { - const struct asymmetric_key_ids *kids = asymmetric_key_ids(key); - if (!kids->id[1]) { - pr_debug("issuer+serial match, but expected SKID missing\n"); - goto reject; - } - if (!asymmetric_key_id_same(skid, kids->id[1])) { - pr_debug("issuer+serial match, but SKID does not\n"); - goto reject; - } - } - - pr_devel("<==%s() = 0 [%x]\n", __func__, key_serial(key)); - return key; - -reject: - key_put(key); - return ERR_PTR(-EKEYREJECTED); -} -EXPORT_SYMBOL_GPL(x509_request_asymmetric_key); - /* * Set up the signature parameters in an X.509 certificate. This involves * digesting the signed data and extracting the signature. diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h index b3928e8..96ef27b 100644 --- a/include/crypto/public_key.h +++ b/include/crypto/public_key.h @@ -50,12 +50,6 @@ struct key; extern int verify_signature(const struct key *key, const struct public_key_signature *sig); -struct asymmetric_key_id; -extern struct key *x509_request_asymmetric_key(struct key *keyring, - const struct asymmetric_key_id *id, - const struct asymmetric_key_id *skid, - bool partial); - int public_key_verify_signature(const struct public_key *pkey, const struct public_key_signature *sig); diff --git a/include/keys/asymmetric-type.h b/include/keys/asymmetric-type.h index d1e23dd..735db69 100644 --- a/include/keys/asymmetric-type.h +++ b/include/keys/asymmetric-type.h @@ -76,6 +76,11 @@ const struct asymmetric_key_ids *asymmetric_key_ids(const struct key *key) return key->payload.data[asym_key_ids]; } +extern struct key *x509_request_asymmetric_key(struct key *keyring, + const struct asymmetric_key_id *id, + const struct asymmetric_key_id *skid, + bool partial); + /* * The payload is at the discretion of the subtype. */ -- cgit v0.10.2 From 9eb029893ad5bf9303ed7f145860b312cbe5f889 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 6 Apr 2016 16:14:25 +0100 Subject: KEYS: Generalise x509_request_asymmetric_key() Generalise x509_request_asymmetric_key(). It doesn't really have any dependencies on X.509 features as it uses generalised IDs and the public_key structs that contain data extracted from X.509. Signed-off-by: David Howells diff --git a/crypto/asymmetric_keys/asymmetric_keys.h b/crypto/asymmetric_keys/asymmetric_keys.h index 1d450b5..ca8e9ac 100644 --- a/crypto/asymmetric_keys/asymmetric_keys.h +++ b/crypto/asymmetric_keys/asymmetric_keys.h @@ -9,6 +9,8 @@ * 2 of the Licence, or (at your option) any later version. */ +#include + extern struct asymmetric_key_id *asymmetric_key_hex_to_key_id(const char *id); extern int __asymmetric_key_hex_to_key_id(const char *id, diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c index c4d66cd..6600181 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -35,21 +35,20 @@ static LIST_HEAD(asymmetric_key_parsers); static DECLARE_RWSEM(asymmetric_key_parsers_sem); /** - * x509_request_asymmetric_key - Request a key by X.509 certificate params. + * find_asymmetric_key - Find a key by ID. * @keyring: The keys to search. - * @id: The issuer & serialNumber to look for or NULL. - * @skid: The subjectKeyIdentifier to look for or NULL. + * @id_0: The first ID to look for or NULL. + * @id_1: The second ID to look for or NULL. * @partial: Use partial match if true, exact if false. * * Find a key in the given keyring by identifier. The preferred identifier is - * the issuer + serialNumber and the fallback identifier is the - * subjectKeyIdentifier. If both are given, the lookup is by the former, but - * the latter must also match. + * the id_0 and the fallback identifier is the id_1. If both are given, the + * lookup is by the former, but the latter must also match. */ -struct key *x509_request_asymmetric_key(struct key *keyring, - const struct asymmetric_key_id *id, - const struct asymmetric_key_id *skid, - bool partial) +struct key *find_asymmetric_key(struct key *keyring, + const struct asymmetric_key_id *id_0, + const struct asymmetric_key_id *id_1, + bool partial) { struct key *key; key_ref_t ref; @@ -57,12 +56,12 @@ struct key *x509_request_asymmetric_key(struct key *keyring, char *req, *p; int len; - if (id) { - lookup = id->data; - len = id->len; + if (id_0) { + lookup = id_0->data; + len = id_0->len; } else { - lookup = skid->data; - len = skid->len; + lookup = id_1->data; + len = id_1->len; } /* Construct an identifier "id:". */ @@ -102,14 +101,15 @@ struct key *x509_request_asymmetric_key(struct key *keyring, } key = key_ref_to_ptr(ref); - if (id && skid) { + if (id_0 && id_1) { const struct asymmetric_key_ids *kids = asymmetric_key_ids(key); - if (!kids->id[1]) { - pr_debug("issuer+serial match, but expected SKID missing\n"); + + if (!kids->id[0]) { + pr_debug("First ID matches, but second is missing\n"); goto reject; } - if (!asymmetric_key_id_same(skid, kids->id[1])) { - pr_debug("issuer+serial match, but SKID does not\n"); + if (!asymmetric_key_id_same(id_1, kids->id[1])) { + pr_debug("First ID matches, but second does not\n"); goto reject; } } @@ -121,7 +121,7 @@ reject: key_put(key); return ERR_PTR(-EKEYREJECTED); } -EXPORT_SYMBOL_GPL(x509_request_asymmetric_key); +EXPORT_SYMBOL_GPL(find_asymmetric_key); /** * asymmetric_key_generate_id: Construct an asymmetric key ID diff --git a/crypto/asymmetric_keys/pkcs7_trust.c b/crypto/asymmetric_keys/pkcs7_trust.c index 36e77cb..f6a009d 100644 --- a/crypto/asymmetric_keys/pkcs7_trust.c +++ b/crypto/asymmetric_keys/pkcs7_trust.c @@ -51,9 +51,8 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, /* Look to see if this certificate is present in the trusted * keys. */ - key = x509_request_asymmetric_key(trust_keyring, - x509->id, x509->skid, - false); + key = find_asymmetric_key(trust_keyring, + x509->id, x509->skid, false); if (!IS_ERR(key)) { /* One of the X.509 certificates in the PKCS#7 message * is apparently the same as one we already trust. @@ -84,10 +83,10 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, * trusted keys. */ if (last && (last->sig->auth_ids[0] || last->sig->auth_ids[1])) { - key = x509_request_asymmetric_key(trust_keyring, - last->sig->auth_ids[0], - last->sig->auth_ids[1], - false); + key = find_asymmetric_key(trust_keyring, + last->sig->auth_ids[0], + last->sig->auth_ids[1], + false); if (!IS_ERR(key)) { x509 = last; pr_devel("sinfo %u: Root cert %u signer is key %x\n", @@ -101,10 +100,8 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, /* As a last resort, see if we have a trusted public key that matches * the signed info directly. */ - key = x509_request_asymmetric_key(trust_keyring, - sinfo->sig->auth_ids[0], - NULL, - false); + key = find_asymmetric_key(trust_keyring, + sinfo->sig->auth_ids[0], NULL, false); if (!IS_ERR(key)) { pr_devel("sinfo %u: Direct signer is key %x\n", sinfo->index, key_serial(key)); diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c index 2fb5941..9c8483e 100644 --- a/crypto/asymmetric_keys/x509_public_key.c +++ b/crypto/asymmetric_keys/x509_public_key.c @@ -213,9 +213,8 @@ static int x509_validate_trust(struct x509_certificate *cert, if (cert->unsupported_sig) return -ENOPKG; - key = x509_request_asymmetric_key(trust_keyring, - sig->auth_ids[0], sig->auth_ids[1], - false); + key = find_asymmetric_key(trust_keyring, + sig->auth_ids[0], sig->auth_ids[1], false); if (IS_ERR(key)) return PTR_ERR(key); diff --git a/include/keys/asymmetric-type.h b/include/keys/asymmetric-type.h index 735db69..b382407 100644 --- a/include/keys/asymmetric-type.h +++ b/include/keys/asymmetric-type.h @@ -76,10 +76,10 @@ const struct asymmetric_key_ids *asymmetric_key_ids(const struct key *key) return key->payload.data[asym_key_ids]; } -extern struct key *x509_request_asymmetric_key(struct key *keyring, - const struct asymmetric_key_id *id, - const struct asymmetric_key_id *skid, - bool partial); +extern struct key *find_asymmetric_key(struct key *keyring, + const struct asymmetric_key_id *id_0, + const struct asymmetric_key_id *id_1, + bool partial); /* * The payload is at the discretion of the subtype. -- cgit v0.10.2 From 5f7f5c81e59be5ce262c5b7d0ede9565a2558d80 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 6 Apr 2016 16:14:25 +0100 Subject: X.509: Use verify_signature() if we have a struct key * to use We should call verify_signature() rather than directly calling public_key_verify_signature() if we have a struct key to use as we shouldn't be poking around in the private data of the key struct as that's subtype dependent. Signed-off-by: David Howells diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c index 9c8483e..117a6ee 100644 --- a/crypto/asymmetric_keys/x509_public_key.c +++ b/crypto/asymmetric_keys/x509_public_key.c @@ -220,8 +220,7 @@ static int x509_validate_trust(struct x509_certificate *cert, if (!use_builtin_keys || test_bit(KEY_FLAG_BUILTIN, &key->flags)) { - ret = public_key_verify_signature( - key->payload.data[asym_crypto], cert->sig); + ret = verify_signature(key, cert->sig); if (ret == -ENOPKG) cert->unsupported_sig = true; } -- cgit v0.10.2 From cfb664ff2b71fbbdc438b8e6db2a1412440432a2 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 6 Apr 2016 16:14:26 +0100 Subject: X.509: Move the trust validation code out to its own file Move the X.509 trust validation code out to its own file so that it can be generalised. Signed-off-by: David Howells diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile index f904862..6516855 100644 --- a/crypto/asymmetric_keys/Makefile +++ b/crypto/asymmetric_keys/Makefile @@ -4,7 +4,10 @@ obj-$(CONFIG_ASYMMETRIC_KEY_TYPE) += asymmetric_keys.o -asymmetric_keys-y := asymmetric_type.o signature.o +asymmetric_keys-y := \ + asymmetric_type.o \ + restrict.o \ + signature.o obj-$(CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE) += public_key.o diff --git a/crypto/asymmetric_keys/restrict.c b/crypto/asymmetric_keys/restrict.c new file mode 100644 index 0000000..b4c10f2 --- /dev/null +++ b/crypto/asymmetric_keys/restrict.c @@ -0,0 +1,106 @@ +/* Instantiate a public key crypto key from an X.509 Certificate + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#define pr_fmt(fmt) "X.509: "fmt +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "asymmetric_keys.h" +#include "x509_parser.h" + +static bool use_builtin_keys; +static struct asymmetric_key_id *ca_keyid; + +#ifndef MODULE +static struct { + struct asymmetric_key_id id; + unsigned char data[10]; +} cakey; + +static int __init ca_keys_setup(char *str) +{ + if (!str) /* default system keyring */ + return 1; + + if (strncmp(str, "id:", 3) == 0) { + struct asymmetric_key_id *p = &cakey.id; + size_t hexlen = (strlen(str) - 3) / 2; + int ret; + + if (hexlen == 0 || hexlen > sizeof(cakey.data)) { + pr_err("Missing or invalid ca_keys id\n"); + return 1; + } + + ret = __asymmetric_key_hex_to_key_id(str + 3, p, hexlen); + if (ret < 0) + pr_err("Unparsable ca_keys id hex string\n"); + else + ca_keyid = p; /* owner key 'id:xxxxxx' */ + } else if (strcmp(str, "builtin") == 0) { + use_builtin_keys = true; + } + + return 1; +} +__setup("ca_keys=", ca_keys_setup); +#endif + +/* + * Check the new certificate against the ones in the trust keyring. If one of + * those is the signing key and validates the new certificate, then mark the + * new certificate as being trusted. + * + * Return 0 if the new certificate was successfully validated, 1 if we couldn't + * find a matching parent certificate in the trusted list and an error if there + * is a matching certificate but the signature check fails. + */ +int x509_validate_trust(struct x509_certificate *cert, + struct key *trust_keyring) +{ + struct public_key_signature *sig = cert->sig; + struct key *key; + int ret = 1; + + if (!sig->auth_ids[0] && !sig->auth_ids[1]) + return 1; + + if (!trust_keyring) + return -EOPNOTSUPP; + if (ca_keyid && !asymmetric_key_id_partial(sig->auth_ids[1], ca_keyid)) + return -EPERM; + if (cert->unsupported_sig) + return -ENOPKG; + + key = find_asymmetric_key(trust_keyring, + sig->auth_ids[0], sig->auth_ids[1], + false); + if (IS_ERR(key)) + return PTR_ERR(key); + + if (!use_builtin_keys || + test_bit(KEY_FLAG_BUILTIN, &key->flags)) { + ret = verify_signature(key, cert->sig); + if (ret == -ENOPKG) + cert->unsupported_sig = true; + } + key_put(key); + return ret; +} +EXPORT_SYMBOL_GPL(x509_validate_trust); diff --git a/crypto/asymmetric_keys/x509_parser.h b/crypto/asymmetric_keys/x509_parser.h index 05eef1c..7a802b0 100644 --- a/crypto/asymmetric_keys/x509_parser.h +++ b/crypto/asymmetric_keys/x509_parser.h @@ -58,3 +58,9 @@ extern int x509_decode_time(time64_t *_t, size_t hdrlen, */ extern int x509_get_sig_params(struct x509_certificate *cert); extern int x509_check_for_self_signed(struct x509_certificate *cert); + +/* + * public_key_trust.c + */ +extern int x509_validate_trust(struct x509_certificate *cert, + struct key *trust_keyring); diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c index 117a6ee..6d7f42f 100644 --- a/crypto/asymmetric_keys/x509_public_key.c +++ b/crypto/asymmetric_keys/x509_public_key.c @@ -20,44 +20,6 @@ #include "asymmetric_keys.h" #include "x509_parser.h" -static bool use_builtin_keys; -static struct asymmetric_key_id *ca_keyid; - -#ifndef MODULE -static struct { - struct asymmetric_key_id id; - unsigned char data[10]; -} cakey; - -static int __init ca_keys_setup(char *str) -{ - if (!str) /* default system keyring */ - return 1; - - if (strncmp(str, "id:", 3) == 0) { - struct asymmetric_key_id *p = &cakey.id; - size_t hexlen = (strlen(str) - 3) / 2; - int ret; - - if (hexlen == 0 || hexlen > sizeof(cakey.data)) { - pr_err("Missing or invalid ca_keys id\n"); - return 1; - } - - ret = __asymmetric_key_hex_to_key_id(str + 3, p, hexlen); - if (ret < 0) - pr_err("Unparsable ca_keys id hex string\n"); - else - ca_keyid = p; /* owner key 'id:xxxxxx' */ - } else if (strcmp(str, "builtin") == 0) { - use_builtin_keys = true; - } - - return 1; -} -__setup("ca_keys=", ca_keys_setup); -#endif - /* * Set up the signature parameters in an X.509 certificate. This involves * digesting the signed data and extracting the signature. @@ -188,47 +150,6 @@ not_self_signed: } /* - * Check the new certificate against the ones in the trust keyring. If one of - * those is the signing key and validates the new certificate, then mark the - * new certificate as being trusted. - * - * Return 0 if the new certificate was successfully validated, 1 if we couldn't - * find a matching parent certificate in the trusted list and an error if there - * is a matching certificate but the signature check fails. - */ -static int x509_validate_trust(struct x509_certificate *cert, - struct key *trust_keyring) -{ - struct public_key_signature *sig = cert->sig; - struct key *key; - int ret = 1; - - if (!sig->auth_ids[0] && !sig->auth_ids[1]) - return 1; - - if (!trust_keyring) - return -EOPNOTSUPP; - if (ca_keyid && !asymmetric_key_id_partial(sig->auth_ids[1], ca_keyid)) - return -EPERM; - if (cert->unsupported_sig) - return -ENOPKG; - - key = find_asymmetric_key(trust_keyring, - sig->auth_ids[0], sig->auth_ids[1], false); - if (IS_ERR(key)) - return PTR_ERR(key); - - if (!use_builtin_keys || - test_bit(KEY_FLAG_BUILTIN, &key->flags)) { - ret = verify_signature(key, cert->sig); - if (ret == -ENOPKG) - cert->unsupported_sig = true; - } - key_put(key); - return ret; -} - -/* * Attempt to parse a data blob for a key as an X509 certificate. */ static int x509_key_preparse(struct key_preparsed_payload *prep) -- cgit v0.10.2 From 99716b7cae8263e1c7e7c1987e95d8f67071ab3e Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 6 Apr 2016 16:14:26 +0100 Subject: KEYS: Make the system trusted keyring depend on the asymmetric key type Make the system trusted keyring depend on the asymmetric key type as there's not a lot of point having it if you can't then load asymmetric keys onto it. This requires the ASYMMETRIC_KEY_TYPE to be made a bool, not a tristate, as the Kconfig language doesn't then correctly force ASYMMETRIC_KEY_TYPE to 'y' rather than 'm' if SYSTEM_TRUSTED_KEYRING is 'y'. Making SYSTEM_TRUSTED_KEYRING *select* ASYMMETRIC_KEY_TYPE instead doesn't work as the Kconfig interpreter then wrongly complains about dependency loops. Signed-off-by: David Howells diff --git a/certs/Kconfig b/certs/Kconfig index f0f8a44..743d480 100644 --- a/certs/Kconfig +++ b/certs/Kconfig @@ -17,6 +17,7 @@ config MODULE_SIG_KEY config SYSTEM_TRUSTED_KEYRING bool "Provide system-wide ring of trusted keys" depends on KEYS + depends on ASYMMETRIC_KEY_TYPE help Provide a system keyring to which trusted keys can be added. Keys in the keyring are considered to be trusted. Keys may be added at will diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig index f7d2ef9..e28e912 100644 --- a/crypto/asymmetric_keys/Kconfig +++ b/crypto/asymmetric_keys/Kconfig @@ -1,5 +1,5 @@ menuconfig ASYMMETRIC_KEY_TYPE - tristate "Asymmetric (public-key cryptographic) key type" + bool "Asymmetric (public-key cryptographic) key type" depends on KEYS help This option provides support for a key type that holds the data for -- cgit v0.10.2 From a511e1af8b12f44c6e55786c463c9f093c214fb6 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 6 Apr 2016 16:14:26 +0100 Subject: KEYS: Move the point of trust determination to __key_link() Move the point at which a key is determined to be trustworthy to __key_link() so that we use the contents of the keyring being linked in to to determine whether the key being linked in is trusted or not. What is 'trusted' then becomes a matter of what's in the keyring. Currently, the test is done when the key is parsed, but given that at that point we can only sensibly refer to the contents of the system trusted keyring, we can only use that as the basis for working out the trustworthiness of a new key. With this change, a trusted keyring is a set of keys that once the trusted-only flag is set cannot be added to except by verification through one of the contained keys. Further, adding a key into a trusted keyring, whilst it might grant trustworthiness in the context of that keyring, does not automatically grant trustworthiness in the context of a second keyring to which it could be secondarily linked. To accomplish this, the authentication data associated with the key source must now be retained. For an X.509 cert, this means the contents of the AuthorityKeyIdentifier and the signature data. If system keyrings are disabled then restrict_link_by_builtin_trusted() resolves to restrict_link_reject(). The integrity digital signature code still works correctly with this as it was previously using KEY_FLAG_TRUSTED_ONLY, which doesn't permit anything to be added if there is no system keyring against which trust can be determined. Signed-off-by: David Howells diff --git a/certs/system_keyring.c b/certs/system_keyring.c index 417d658..4e2fa8a 100644 --- a/certs/system_keyring.c +++ b/certs/system_keyring.c @@ -18,12 +18,26 @@ #include #include -struct key *system_trusted_keyring; -EXPORT_SYMBOL_GPL(system_trusted_keyring); +static struct key *system_trusted_keyring; extern __initconst const u8 system_certificate_list[]; extern __initconst const unsigned long system_certificate_list_size; +/** + * restrict_link_by_builtin_trusted - Restrict keyring addition by system CA + * + * Restrict the addition of keys into a keyring based on the key-to-be-added + * being vouched for by a key in the system keyring. + */ +int restrict_link_by_builtin_trusted(struct key *keyring, + const struct key_type *type, + unsigned long flags, + const union key_payload *payload) +{ + return restrict_link_by_signature(system_trusted_keyring, + type, payload); +} + /* * Load the compiled-in keys */ @@ -37,7 +51,7 @@ static __init int system_trusted_keyring_init(void) ((KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ | KEY_USR_SEARCH), KEY_ALLOC_NOT_IN_QUOTA, - keyring_restrict_trusted_only, NULL); + restrict_link_by_builtin_trusted, NULL); if (IS_ERR(system_trusted_keyring)) panic("Can't allocate system trusted keyring\n"); return 0; diff --git a/crypto/asymmetric_keys/restrict.c b/crypto/asymmetric_keys/restrict.c index b4c10f2..ac4bddf 100644 --- a/crypto/asymmetric_keys/restrict.c +++ b/crypto/asymmetric_keys/restrict.c @@ -1,6 +1,6 @@ /* Instantiate a public key crypto key from an X.509 Certificate * - * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Copyright (C) 2012, 2016 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) * * This program is free software; you can redistribute it and/or @@ -9,20 +9,12 @@ * 2 of the Licence, or (at your option) any later version. */ -#define pr_fmt(fmt) "X.509: "fmt +#define pr_fmt(fmt) "ASYM: "fmt #include #include -#include #include -#include -#include -#include -#include -#include -#include #include #include "asymmetric_keys.h" -#include "x509_parser.h" static bool use_builtin_keys; static struct asymmetric_key_id *ca_keyid; @@ -62,45 +54,55 @@ static int __init ca_keys_setup(char *str) __setup("ca_keys=", ca_keys_setup); #endif -/* +/** + * restrict_link_by_signature - Restrict additions to a ring of public keys + * @trust_keyring: A ring of keys that can be used to vouch for the new cert. + * @type: The type of key being added. + * @payload: The payload of the new key. + * * Check the new certificate against the ones in the trust keyring. If one of * those is the signing key and validates the new certificate, then mark the * new certificate as being trusted. * - * Return 0 if the new certificate was successfully validated, 1 if we couldn't - * find a matching parent certificate in the trusted list and an error if there - * is a matching certificate but the signature check fails. + * Returns 0 if the new certificate was accepted, -ENOKEY if we couldn't find a + * matching parent certificate in the trusted list, -EKEYREJECTED if the + * signature check fails or the key is blacklisted and some other error if + * there is a matching certificate but the signature check cannot be performed. */ -int x509_validate_trust(struct x509_certificate *cert, - struct key *trust_keyring) +int restrict_link_by_signature(struct key *trust_keyring, + const struct key_type *type, + const union key_payload *payload) { - struct public_key_signature *sig = cert->sig; + const struct public_key_signature *sig; struct key *key; - int ret = 1; + int ret; - if (!sig->auth_ids[0] && !sig->auth_ids[1]) - return 1; + pr_devel("==>%s()\n", __func__); if (!trust_keyring) + return -ENOKEY; + + if (type != &key_type_asymmetric) return -EOPNOTSUPP; + + sig = payload->data[asym_auth]; + if (!sig->auth_ids[0] && !sig->auth_ids[1]) + return 0; + if (ca_keyid && !asymmetric_key_id_partial(sig->auth_ids[1], ca_keyid)) return -EPERM; - if (cert->unsupported_sig) - return -ENOPKG; + /* See if we have a key that signed this one. */ key = find_asymmetric_key(trust_keyring, sig->auth_ids[0], sig->auth_ids[1], false); if (IS_ERR(key)) - return PTR_ERR(key); + return -ENOKEY; - if (!use_builtin_keys || - test_bit(KEY_FLAG_BUILTIN, &key->flags)) { - ret = verify_signature(key, cert->sig); - if (ret == -ENOPKG) - cert->unsupported_sig = true; - } + if (use_builtin_keys && !test_bit(KEY_FLAG_BUILTIN, &key->flags)) + ret = -ENOKEY; + else + ret = verify_signature(key, sig); key_put(key); return ret; } -EXPORT_SYMBOL_GPL(x509_validate_trust); diff --git a/crypto/asymmetric_keys/x509_parser.h b/crypto/asymmetric_keys/x509_parser.h index 7a802b0..05eef1c 100644 --- a/crypto/asymmetric_keys/x509_parser.h +++ b/crypto/asymmetric_keys/x509_parser.h @@ -58,9 +58,3 @@ extern int x509_decode_time(time64_t *_t, size_t hdrlen, */ extern int x509_get_sig_params(struct x509_certificate *cert); extern int x509_check_for_self_signed(struct x509_certificate *cert); - -/* - * public_key_trust.c - */ -extern int x509_validate_trust(struct x509_certificate *cert, - struct key *trust_keyring); diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c index 6d7f42f..fb73229 100644 --- a/crypto/asymmetric_keys/x509_public_key.c +++ b/crypto/asymmetric_keys/x509_public_key.c @@ -178,31 +178,12 @@ static int x509_key_preparse(struct key_preparsed_payload *prep) cert->pub->id_type = "X509"; - /* See if we can derive the trustability of this certificate. - * - * When it comes to self-signed certificates, we cannot evaluate - * trustedness except by the fact that we obtained it from a trusted - * location. So we just rely on x509_validate_trust() failing in this - * case. - * - * Note that there's a possibility of a self-signed cert matching a - * cert that we have (most likely a duplicate that we already trust) - - * in which case it will be marked trusted. - */ - if (cert->unsupported_sig || cert->self_signed) { + if (cert->unsupported_sig) { public_key_signature_free(cert->sig); cert->sig = NULL; } else { pr_devel("Cert Signature: %s + %s\n", cert->sig->pkey_algo, cert->sig->hash_algo); - - ret = x509_validate_trust(cert, get_system_trusted_keyring()); - if (ret) - ret = x509_validate_trust(cert, get_ima_mok_keyring()); - if (ret == -EKEYREJECTED) - goto error_free_cert; - if (!ret) - prep->trusted = true; } /* Propose a description */ diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h index 96ef27b..882ca0e1 100644 --- a/include/crypto/public_key.h +++ b/include/crypto/public_key.h @@ -47,6 +47,13 @@ extern void public_key_signature_free(struct public_key_signature *sig); extern struct asymmetric_key_subtype public_key_subtype; struct key; +struct key_type; +union key_payload; + +extern int restrict_link_by_signature(struct key *trust_keyring, + const struct key_type *type, + const union key_payload *payload); + extern int verify_signature(const struct key *key, const struct public_key_signature *sig); diff --git a/include/keys/system_keyring.h b/include/keys/system_keyring.h index b2d645a..9371591 100644 --- a/include/keys/system_keyring.h +++ b/include/keys/system_keyring.h @@ -12,22 +12,17 @@ #ifndef _KEYS_SYSTEM_KEYRING_H #define _KEYS_SYSTEM_KEYRING_H +#include + #ifdef CONFIG_SYSTEM_TRUSTED_KEYRING -#include -#include -#include +extern int restrict_link_by_builtin_trusted(struct key *keyring, + const struct key_type *type, + unsigned long flags, + const union key_payload *payload); -extern struct key *system_trusted_keyring; -static inline struct key *get_system_trusted_keyring(void) -{ - return system_trusted_keyring; -} #else -static inline struct key *get_system_trusted_keyring(void) -{ - return NULL; -} +#define restrict_link_by_builtin_trusted restrict_link_reject #endif #ifdef CONFIG_IMA_MOK_KEYRING diff --git a/kernel/module_signing.c b/kernel/module_signing.c index 6a64e03..937c844 100644 --- a/kernel/module_signing.c +++ b/kernel/module_signing.c @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include #include "module-internal.h" diff --git a/security/integrity/digsig.c b/security/integrity/digsig.c index 659566c..d647178 100644 --- a/security/integrity/digsig.c +++ b/security/integrity/digsig.c @@ -18,6 +18,8 @@ #include #include #include +#include +#include #include "integrity.h" @@ -40,6 +42,35 @@ static bool init_keyring __initdata = true; static bool init_keyring __initdata; #endif +#ifdef CONFIG_SYSTEM_TRUSTED_KEYRING +/* + * Restrict the addition of keys into the IMA keyring. + * + * Any key that needs to go in .ima keyring must be signed by CA in + * either .system or .ima_mok keyrings. + */ +static int restrict_link_by_ima_mok(struct key *keyring, + const struct key_type *type, + unsigned long flags, + const union key_payload *payload) +{ + int ret; + + ret = restrict_link_by_builtin_trusted(keyring, type, flags, payload); + if (ret != -ENOKEY) + return ret; + + return restrict_link_by_signature(get_ima_mok_keyring(), + type, payload); +} +#else +/* + * If there's no system trusted keyring, then keys cannot be loaded into + * .ima_mok and added keys cannot be marked trusted. + */ +#define restrict_link_by_ima_mok restrict_link_reject +#endif + int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen, const char *digest, int digestlen) { @@ -84,7 +115,7 @@ int __init integrity_init_keyring(const unsigned int id) KEY_USR_VIEW | KEY_USR_READ | KEY_USR_WRITE | KEY_USR_SEARCH), KEY_ALLOC_NOT_IN_QUOTA, - NULL, NULL); + restrict_link_by_ima_mok, NULL); if (IS_ERR(keyring[id])) { err = PTR_ERR(keyring[id]); pr_info("Can't allocate %s keyring (%d)\n", diff --git a/security/integrity/ima/ima_mok.c b/security/integrity/ima/ima_mok.c index ef91248..2988726 100644 --- a/security/integrity/ima/ima_mok.c +++ b/security/integrity/ima/ima_mok.c @@ -17,7 +17,7 @@ #include #include #include -#include +#include struct key *ima_mok_keyring; @@ -36,7 +36,7 @@ __init int ima_mok_init(void) KEY_USR_VIEW | KEY_USR_READ | KEY_USR_WRITE | KEY_USR_SEARCH, KEY_ALLOC_NOT_IN_QUOTA, - keyring_restrict_trusted_only, NULL); + restrict_link_by_builtin_trusted, NULL); ima_blacklist_keyring = keyring_alloc(".ima_blacklist", KUIDT_INIT(0), KGIDT_INIT(0), current_cred(), @@ -44,7 +44,7 @@ __init int ima_mok_init(void) KEY_USR_VIEW | KEY_USR_READ | KEY_USR_WRITE | KEY_USR_SEARCH, KEY_ALLOC_NOT_IN_QUOTA, - keyring_restrict_trusted_only, NULL); + restrict_link_by_builtin_trusted, NULL); if (IS_ERR(ima_mok_keyring) || IS_ERR(ima_blacklist_keyring)) panic("Can't allocate IMA MOK or blacklist keyrings."); -- cgit v0.10.2 From 77f68bac9481ad440f4f34dda3d28c2dce6eb87b Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 6 Apr 2016 16:14:26 +0100 Subject: KEYS: Remove KEY_FLAG_TRUSTED and KEY_ALLOC_TRUSTED Remove KEY_FLAG_TRUSTED and KEY_ALLOC_TRUSTED as they're no longer meaningful. Also we can drop the trusted flag from the preparse structure. Given this, we no longer need to pass the key flags through to restrict_link(). Further, we can now get rid of keyring_restrict_trusted_only() also. Signed-off-by: David Howells diff --git a/certs/system_keyring.c b/certs/system_keyring.c index 4e2fa8a..e460d00 100644 --- a/certs/system_keyring.c +++ b/certs/system_keyring.c @@ -31,7 +31,6 @@ extern __initconst const unsigned long system_certificate_list_size; */ int restrict_link_by_builtin_trusted(struct key *keyring, const struct key_type *type, - unsigned long flags, const union key_payload *payload) { return restrict_link_by_signature(system_trusted_keyring, @@ -97,7 +96,6 @@ static __init int load_system_certificate_list(void) ((KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ), KEY_ALLOC_NOT_IN_QUOTA | - KEY_ALLOC_TRUSTED | KEY_ALLOC_BUILT_IN | KEY_ALLOC_BYPASS_RESTRICTION); if (IS_ERR(key)) { diff --git a/include/keys/system_keyring.h b/include/keys/system_keyring.h index 9371591..c72330a 100644 --- a/include/keys/system_keyring.h +++ b/include/keys/system_keyring.h @@ -18,7 +18,6 @@ extern int restrict_link_by_builtin_trusted(struct key *keyring, const struct key_type *type, - unsigned long flags, const union key_payload *payload); #else diff --git a/include/linux/key-type.h b/include/linux/key-type.h index 7463355..eaee981 100644 --- a/include/linux/key-type.h +++ b/include/linux/key-type.h @@ -45,7 +45,6 @@ struct key_preparsed_payload { size_t datalen; /* Raw datalen */ size_t quotalen; /* Quota length for proposed payload */ time_t expiry; /* Expiry time of key */ - bool trusted; /* True if key is trusted */ }; typedef int (*request_key_actor_t)(struct key_construction *key, diff --git a/include/linux/key.h b/include/linux/key.h index 83b6036..7229147 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -173,10 +173,9 @@ struct key { #define KEY_FLAG_NEGATIVE 5 /* set if key is negative */ #define KEY_FLAG_ROOT_CAN_CLEAR 6 /* set if key can be cleared by root without permission */ #define KEY_FLAG_INVALIDATED 7 /* set if key has been invalidated */ -#define KEY_FLAG_TRUSTED 8 /* set if key is trusted */ -#define KEY_FLAG_BUILTIN 9 /* set if key is built in to the kernel */ -#define KEY_FLAG_ROOT_CAN_INVAL 10 /* set if key can be invalidated by root without permission */ -#define KEY_FLAG_KEEP 11 /* set if key should not be removed */ +#define KEY_FLAG_BUILTIN 8 /* set if key is built in to the kernel */ +#define KEY_FLAG_ROOT_CAN_INVAL 9 /* set if key can be invalidated by root without permission */ +#define KEY_FLAG_KEEP 10 /* set if key should not be removed */ /* the key type and key description string * - the desc is used to match a key against search criteria @@ -217,7 +216,6 @@ struct key { */ int (*restrict_link)(struct key *keyring, const struct key_type *type, - unsigned long flags, const union key_payload *payload); }; @@ -229,16 +227,14 @@ extern struct key *key_alloc(struct key_type *type, unsigned long flags, int (*restrict_link)(struct key *, const struct key_type *, - unsigned long, const union key_payload *)); #define KEY_ALLOC_IN_QUOTA 0x0000 /* add to quota, reject if would overrun */ #define KEY_ALLOC_QUOTA_OVERRUN 0x0001 /* add to quota, permit even if overrun */ #define KEY_ALLOC_NOT_IN_QUOTA 0x0002 /* not in quota */ -#define KEY_ALLOC_TRUSTED 0x0004 /* Key should be flagged as trusted */ -#define KEY_ALLOC_BUILT_IN 0x0008 /* Key is built into kernel */ -#define KEY_ALLOC_BYPASS_RESTRICTION 0x0010 /* Override the check on restricted keyrings */ +#define KEY_ALLOC_BUILT_IN 0x0004 /* Key is built into kernel */ +#define KEY_ALLOC_BYPASS_RESTRICTION 0x0008 /* Override the check on restricted keyrings */ extern void key_revoke(struct key *key); extern void key_invalidate(struct key *key); @@ -309,18 +305,11 @@ extern struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid unsigned long flags, int (*restrict_link)(struct key *, const struct key_type *, - unsigned long, const union key_payload *), struct key *dest); -extern int keyring_restrict_trusted_only(struct key *keyring, - const struct key_type *type, - unsigned long, - const union key_payload *payload); - extern int restrict_link_reject(struct key *keyring, const struct key_type *type, - unsigned long flags, const union key_payload *payload); extern int keyring_clear(struct key *keyring); diff --git a/security/integrity/digsig.c b/security/integrity/digsig.c index d647178..98ee4c7 100644 --- a/security/integrity/digsig.c +++ b/security/integrity/digsig.c @@ -51,12 +51,11 @@ static bool init_keyring __initdata; */ static int restrict_link_by_ima_mok(struct key *keyring, const struct key_type *type, - unsigned long flags, const union key_payload *payload) { int ret; - ret = restrict_link_by_builtin_trusted(keyring, type, flags, payload); + ret = restrict_link_by_builtin_trusted(keyring, type, payload); if (ret != -ENOKEY) return ret; diff --git a/security/keys/key.c b/security/keys/key.c index deb8817..bd5a272 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -227,7 +227,6 @@ struct key *key_alloc(struct key_type *type, const char *desc, key_perm_t perm, unsigned long flags, int (*restrict_link)(struct key *, const struct key_type *, - unsigned long, const union key_payload *)) { struct key_user *user = NULL; @@ -300,8 +299,6 @@ struct key *key_alloc(struct key_type *type, const char *desc, if (!(flags & KEY_ALLOC_NOT_IN_QUOTA)) key->flags |= 1 << KEY_FLAG_IN_QUOTA; - if (flags & KEY_ALLOC_TRUSTED) - key->flags |= 1 << KEY_FLAG_TRUSTED; if (flags & KEY_ALLOC_BUILT_IN) key->flags |= 1 << KEY_FLAG_BUILTIN; @@ -504,7 +501,7 @@ int key_instantiate_and_link(struct key *key, if (keyring) { if (keyring->restrict_link) { ret = keyring->restrict_link(keyring, key->type, - key->flags, &prep.payload); + &prep.payload); if (ret < 0) goto error; } @@ -811,7 +808,6 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, int ret; int (*restrict_link)(struct key *, const struct key_type *, - unsigned long, const union key_payload *) = NULL; /* look up the key type to see if it's one of the registered kernel @@ -843,7 +839,6 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, prep.data = payload; prep.datalen = plen; prep.quotalen = index_key.type->def_datalen; - prep.trusted = flags & KEY_ALLOC_TRUSTED; prep.expiry = TIME_T_MAX; if (index_key.type->preparse) { ret = index_key.type->preparse(&prep); @@ -860,9 +855,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, index_key.desc_len = strlen(index_key.description); if (restrict_link) { - unsigned long kflags = prep.trusted ? KEY_FLAG_TRUSTED : 0; - ret = restrict_link(keyring, - index_key.type, kflags, &prep.payload); + ret = restrict_link(keyring, index_key.type, &prep.payload); if (ret < 0) { key_ref = ERR_PTR(ret); goto error_free_prep; diff --git a/security/keys/keyring.c b/security/keys/keyring.c index d2d1f33..c91e4e0 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -494,7 +494,6 @@ struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid, unsigned long flags, int (*restrict_link)(struct key *, const struct key_type *, - unsigned long, const union key_payload *), struct key *dest) { @@ -516,33 +515,9 @@ struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid, EXPORT_SYMBOL(keyring_alloc); /** - * keyring_restrict_trusted_only - Restrict additions to a keyring to trusted keys only - * @keyring: The keyring being added to. - * @type: The type of key being added. - * @flags: The key flags. - * @payload: The payload of the key intended to be added. - * - * Reject the addition of any links to a keyring that point to keys that aren't - * marked as being trusted. It can be overridden by passing - * KEY_ALLOC_BYPASS_RESTRICTION to key_instantiate_and_link() when adding a key - * to a keyring. - * - * This is meant to be passed as the restrict_link parameter to - * keyring_alloc(). - */ -int keyring_restrict_trusted_only(struct key *keyring, - const struct key_type *type, - unsigned long flags, - const union key_payload *payload) -{ - return flags & KEY_FLAG_TRUSTED ? 0 : -EPERM; -} - -/** * restrict_link_reject - Give -EPERM to restrict link * @keyring: The keyring being added to. * @type: The type of key being added. - * @flags: The key flags. * @payload: The payload of the key intended to be added. * * Reject the addition of any links to a keyring. It can be overridden by @@ -554,7 +529,6 @@ int keyring_restrict_trusted_only(struct key *keyring, */ int restrict_link_reject(struct key *keyring, const struct key_type *type, - unsigned long flags, const union key_payload *payload) { return -EPERM; @@ -1248,8 +1222,7 @@ static int __key_link_check_restriction(struct key *keyring, struct key *key) { if (!keyring->restrict_link) return 0; - return keyring->restrict_link(keyring, - key->type, key->flags, &key->payload); + return keyring->restrict_link(keyring, key->type, &key->payload); } /** -- cgit v0.10.2 From d3bfe84129f65e0af2450743ebdab33d161d01c9 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 6 Apr 2016 16:14:27 +0100 Subject: certs: Add a secondary system keyring that can be added to dynamically Add a secondary system keyring that can be added to by root whilst the system is running - provided the key being added is vouched for by a key built into the kernel or already added to the secondary keyring. Rename .system_keyring to .builtin_trusted_keys to distinguish it more obviously from the new keyring (called .secondary_trusted_keys). The new keyring needs to be enabled with CONFIG_SECONDARY_TRUSTED_KEYRING. If the secondary keyring is enabled, a link is created from that to .builtin_trusted_keys so that the the latter will automatically be searched too if the secondary keyring is searched. Signed-off-by: David Howells diff --git a/certs/Kconfig b/certs/Kconfig index 743d480..fc5955f 100644 --- a/certs/Kconfig +++ b/certs/Kconfig @@ -56,4 +56,12 @@ config SYSTEM_EXTRA_CERTIFICATE_SIZE This is the number of bytes reserved in the kernel image for a certificate to be inserted. +config SECONDARY_TRUSTED_KEYRING + bool "Provide a keyring to which extra trustable keys may be added" + depends on SYSTEM_TRUSTED_KEYRING + help + If set, provide a keyring to which extra keys may be added, provided + those keys are not blacklisted and are vouched for by a key built + into the kernel or already in the secondary trusted keyring. + endmenu diff --git a/certs/system_keyring.c b/certs/system_keyring.c index e460d00..50979d6 100644 --- a/certs/system_keyring.c +++ b/certs/system_keyring.c @@ -18,41 +18,88 @@ #include #include -static struct key *system_trusted_keyring; +static struct key *builtin_trusted_keys; +#ifdef CONFIG_SECONDARY_TRUSTED_KEYRING +static struct key *secondary_trusted_keys; +#endif extern __initconst const u8 system_certificate_list[]; extern __initconst const unsigned long system_certificate_list_size; /** - * restrict_link_by_builtin_trusted - Restrict keyring addition by system CA + * restrict_link_to_builtin_trusted - Restrict keyring addition by built in CA * * Restrict the addition of keys into a keyring based on the key-to-be-added - * being vouched for by a key in the system keyring. + * being vouched for by a key in the built in system keyring. */ int restrict_link_by_builtin_trusted(struct key *keyring, const struct key_type *type, const union key_payload *payload) { - return restrict_link_by_signature(system_trusted_keyring, - type, payload); + return restrict_link_by_signature(builtin_trusted_keys, type, payload); } +#ifdef CONFIG_SECONDARY_TRUSTED_KEYRING +/** + * restrict_link_by_builtin_and_secondary_trusted - Restrict keyring + * addition by both builtin and secondary keyrings + * + * Restrict the addition of keys into a keyring based on the key-to-be-added + * being vouched for by a key in either the built-in or the secondary system + * keyrings. + */ +int restrict_link_by_builtin_and_secondary_trusted( + struct key *keyring, + const struct key_type *type, + const union key_payload *payload) +{ + /* If we have a secondary trusted keyring, then that contains a link + * through to the builtin keyring and the search will follow that link. + */ + if (type == &key_type_keyring && + keyring == secondary_trusted_keys && + payload == &builtin_trusted_keys->payload) + /* Allow the builtin keyring to be added to the secondary */ + return 0; + + return restrict_link_by_signature(secondary_trusted_keys, type, payload); +} +#endif + /* - * Load the compiled-in keys + * Create the trusted keyrings */ static __init int system_trusted_keyring_init(void) { - pr_notice("Initialise system trusted keyring\n"); + pr_notice("Initialise system trusted keyrings\n"); - system_trusted_keyring = - keyring_alloc(".system_keyring", + builtin_trusted_keys = + keyring_alloc(".builtin_trusted_keys", KUIDT_INIT(0), KGIDT_INIT(0), current_cred(), ((KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ | KEY_USR_SEARCH), KEY_ALLOC_NOT_IN_QUOTA, - restrict_link_by_builtin_trusted, NULL); - if (IS_ERR(system_trusted_keyring)) - panic("Can't allocate system trusted keyring\n"); + NULL, NULL); + if (IS_ERR(builtin_trusted_keys)) + panic("Can't allocate builtin trusted keyring\n"); + +#ifdef CONFIG_SECONDARY_TRUSTED_KEYRING + secondary_trusted_keys = + keyring_alloc(".secondary_trusted_keys", + KUIDT_INIT(0), KGIDT_INIT(0), current_cred(), + ((KEY_POS_ALL & ~KEY_POS_SETATTR) | + KEY_USR_VIEW | KEY_USR_READ | KEY_USR_SEARCH | + KEY_USR_WRITE), + KEY_ALLOC_NOT_IN_QUOTA, + restrict_link_by_builtin_and_secondary_trusted, + NULL); + if (IS_ERR(secondary_trusted_keys)) + panic("Can't allocate secondary trusted keyring\n"); + + if (key_link(secondary_trusted_keys, builtin_trusted_keys) < 0) + panic("Can't link trusted keyrings\n"); +#endif + return 0; } @@ -88,7 +135,7 @@ static __init int load_system_certificate_list(void) if (plen > end - p) goto dodgy_cert; - key = key_create_or_update(make_key_ref(system_trusted_keyring, 1), + key = key_create_or_update(make_key_ref(builtin_trusted_keys, 1), "asymmetric", NULL, p, @@ -125,7 +172,8 @@ late_initcall(load_system_certificate_list); * @len: Size of @data. * @raw_pkcs7: The PKCS#7 message that is the signature. * @pkcs7_len: The size of @raw_pkcs7. - * @trusted_keys: Trusted keys to use (NULL for system_trusted_keyring). + * @trusted_keys: Trusted keys to use (NULL for builtin trusted keys only, + * (void *)1UL for all trusted keys). * @usage: The use to which the key is being put. * @view_content: Callback to gain access to content. * @ctx: Context for callback. @@ -157,8 +205,15 @@ int verify_pkcs7_signature(const void *data, size_t len, if (ret < 0) goto error; - if (!trusted_keys) - trusted_keys = system_trusted_keyring; + if (!trusted_keys) { + trusted_keys = builtin_trusted_keys; + } else if (trusted_keys == (void *)1UL) { +#ifdef CONFIG_SECONDARY_TRUSTED_KEYRING + trusted_keys = secondary_trusted_keys; +#else + trusted_keys = builtin_trusted_keys; +#endif + } ret = pkcs7_validate_trust(pkcs7, trusted_keys); if (ret < 0) { if (ret == -ENOKEY) diff --git a/include/keys/system_keyring.h b/include/keys/system_keyring.h index c72330a..6144240 100644 --- a/include/keys/system_keyring.h +++ b/include/keys/system_keyring.h @@ -24,6 +24,15 @@ extern int restrict_link_by_builtin_trusted(struct key *keyring, #define restrict_link_by_builtin_trusted restrict_link_reject #endif +#ifdef CONFIG_SECONDARY_TRUSTED_KEYRING +extern int restrict_link_by_builtin_and_secondary_trusted( + struct key *keyring, + const struct key_type *type, + const union key_payload *payload); +#else +#define restrict_link_by_builtin_and_secondary_trusted restrict_link_by_builtin_trusted +#endif + #ifdef CONFIG_IMA_MOK_KEYRING extern struct key *ima_mok_keyring; extern struct key *ima_blacklist_keyring; -- cgit v0.10.2 From 56104cf2b8d20eed32c14eac8ac574c35377ab38 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 7 Apr 2016 09:45:23 +0100 Subject: IMA: Use the the system trusted keyrings instead of .ima_mok Add a config option (IMA_KEYRINGS_PERMIT_SIGNED_BY_BUILTIN_OR_SECONDARY) that, when enabled, allows keys to be added to the IMA keyrings by userspace - with the restriction that each must be signed by a key in the system trusted keyrings. EPERM will be returned if this option is disabled, ENOKEY will be returned if no authoritative key can be found and EKEYREJECTED will be returned if the signature doesn't match. Other errors such as ENOPKG may also be returned. If this new option is enabled, the builtin system keyring is searched, as is the secondary system keyring if that is also enabled. Intermediate keys between the builtin system keyring and the key being added can be added to the secondary keyring (which replaces .ima_mok) to form a trust chain - provided they are also validly signed by a key in one of the trusted keyrings. The .ima_mok keyring is then removed and the IMA blacklist keyring gets its own config option (IMA_BLACKLIST_KEYRING). Signed-off-by: David Howells Signed-off-by: Mimi Zohar diff --git a/include/keys/system_keyring.h b/include/keys/system_keyring.h index 6144240..fbd4647 100644 --- a/include/keys/system_keyring.h +++ b/include/keys/system_keyring.h @@ -33,28 +33,19 @@ extern int restrict_link_by_builtin_and_secondary_trusted( #define restrict_link_by_builtin_and_secondary_trusted restrict_link_by_builtin_trusted #endif -#ifdef CONFIG_IMA_MOK_KEYRING -extern struct key *ima_mok_keyring; +#ifdef CONFIG_IMA_BLACKLIST_KEYRING extern struct key *ima_blacklist_keyring; -static inline struct key *get_ima_mok_keyring(void) -{ - return ima_mok_keyring; -} static inline struct key *get_ima_blacklist_keyring(void) { return ima_blacklist_keyring; } #else -static inline struct key *get_ima_mok_keyring(void) -{ - return NULL; -} static inline struct key *get_ima_blacklist_keyring(void) { return NULL; } -#endif /* CONFIG_IMA_MOK_KEYRING */ +#endif /* CONFIG_IMA_BLACKLIST_KEYRING */ #endif /* _KEYS_SYSTEM_KEYRING_H */ diff --git a/security/integrity/digsig.c b/security/integrity/digsig.c index 98ee4c7..4304372 100644 --- a/security/integrity/digsig.c +++ b/security/integrity/digsig.c @@ -42,32 +42,10 @@ static bool init_keyring __initdata = true; static bool init_keyring __initdata; #endif -#ifdef CONFIG_SYSTEM_TRUSTED_KEYRING -/* - * Restrict the addition of keys into the IMA keyring. - * - * Any key that needs to go in .ima keyring must be signed by CA in - * either .system or .ima_mok keyrings. - */ -static int restrict_link_by_ima_mok(struct key *keyring, - const struct key_type *type, - const union key_payload *payload) -{ - int ret; - - ret = restrict_link_by_builtin_trusted(keyring, type, payload); - if (ret != -ENOKEY) - return ret; - - return restrict_link_by_signature(get_ima_mok_keyring(), - type, payload); -} +#ifdef CONFIG_IMA_KEYRINGS_PERMIT_SIGNED_BY_BUILTIN_OR_SECONDARY +#define restrict_link_to_ima restrict_link_by_builtin_and_secondary_trusted #else -/* - * If there's no system trusted keyring, then keys cannot be loaded into - * .ima_mok and added keys cannot be marked trusted. - */ -#define restrict_link_by_ima_mok restrict_link_reject +#define restrict_link_to_ima restrict_link_by_builtin_trusted #endif int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen, @@ -114,7 +92,7 @@ int __init integrity_init_keyring(const unsigned int id) KEY_USR_VIEW | KEY_USR_READ | KEY_USR_WRITE | KEY_USR_SEARCH), KEY_ALLOC_NOT_IN_QUOTA, - restrict_link_by_ima_mok, NULL); + restrict_link_to_ima, NULL); if (IS_ERR(keyring[id])) { err = PTR_ERR(keyring[id]); pr_info("Can't allocate %s keyring (%d)\n", diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index e54a8a8..5487827 100644 --- a/security/integrity/ima/Kconfig +++ b/security/integrity/ima/Kconfig @@ -155,23 +155,33 @@ config IMA_TRUSTED_KEYRING This option is deprecated in favor of INTEGRITY_TRUSTED_KEYRING -config IMA_MOK_KEYRING - bool "Create IMA machine owner keys (MOK) and blacklist keyrings" +config IMA_KEYRINGS_PERMIT_SIGNED_BY_BUILTIN_OR_SECONDARY + bool "Permit keys validly signed by a built-in or secondary CA cert (EXPERIMENTAL)" + depends on SYSTEM_TRUSTED_KEYRING + depends on SECONDARY_TRUSTED_KEYRING + depends on INTEGRITY_ASYMMETRIC_KEYS + select INTEGRITY_TRUSTED_KEYRING + default n + help + Keys may be added to the IMA or IMA blacklist keyrings, if the + key is validly signed by a CA cert in the system built-in or + secondary trusted keyrings. + + Intermediate keys between those the kernel has compiled in and the + IMA keys to be added may be added to the system secondary keyring, + provided they are validly signed by a key already resident in the + built-in or secondary trusted keyrings. + +config IMA_BLACKLIST_KEYRING + bool "Create IMA machine owner blacklist keyrings (EXPERIMENTAL)" depends on SYSTEM_TRUSTED_KEYRING depends on IMA_TRUSTED_KEYRING default n help - This option creates IMA MOK and blacklist keyrings. IMA MOK is an - intermediate keyring that sits between .system and .ima keyrings, - effectively forming a simple CA hierarchy. To successfully import a - key into .ima_mok it must be signed by a key which CA is in .system - keyring. On turn any key that needs to go in .ima keyring must be - signed by CA in either .system or .ima_mok keyrings. IMA MOK is empty - at kernel boot. - - IMA blacklist keyring contains all revoked IMA keys. It is consulted - before any other keyring. If the search is successful the requested - operation is rejected and error is returned to the caller. + This option creates an IMA blacklist keyring, which contains all + revoked IMA keys. It is consulted before any other keyring. If + the search is successful the requested operation is rejected and + an error is returned to the caller. config IMA_LOAD_X509 bool "Load X509 certificate onto the '.ima' trusted keyring" diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile index a8539f9..9aeaeda 100644 --- a/security/integrity/ima/Makefile +++ b/security/integrity/ima/Makefile @@ -8,4 +8,4 @@ obj-$(CONFIG_IMA) += ima.o ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \ ima_policy.o ima_template.o ima_template_lib.o ima-$(CONFIG_IMA_APPRAISE) += ima_appraise.o -obj-$(CONFIG_IMA_MOK_KEYRING) += ima_mok.o +obj-$(CONFIG_IMA_BLACKLIST_KEYRING) += ima_mok.o diff --git a/security/integrity/ima/ima_mok.c b/security/integrity/ima/ima_mok.c index 2988726..74a27995 100644 --- a/security/integrity/ima/ima_mok.c +++ b/security/integrity/ima/ima_mok.c @@ -20,23 +20,14 @@ #include -struct key *ima_mok_keyring; struct key *ima_blacklist_keyring; /* - * Allocate the IMA MOK and blacklist keyrings + * Allocate the IMA blacklist keyring */ __init int ima_mok_init(void) { - pr_notice("Allocating IMA MOK and blacklist keyrings.\n"); - - ima_mok_keyring = keyring_alloc(".ima_mok", - KUIDT_INIT(0), KGIDT_INIT(0), current_cred(), - (KEY_POS_ALL & ~KEY_POS_SETATTR) | - KEY_USR_VIEW | KEY_USR_READ | - KEY_USR_WRITE | KEY_USR_SEARCH, - KEY_ALLOC_NOT_IN_QUOTA, - restrict_link_by_builtin_trusted, NULL); + pr_notice("Allocating IMA blacklist keyring.\n"); ima_blacklist_keyring = keyring_alloc(".ima_blacklist", KUIDT_INIT(0), KGIDT_INIT(0), current_cred(), @@ -46,8 +37,8 @@ __init int ima_mok_init(void) KEY_ALLOC_NOT_IN_QUOTA, restrict_link_by_builtin_trusted, NULL); - if (IS_ERR(ima_mok_keyring) || IS_ERR(ima_blacklist_keyring)) - panic("Can't allocate IMA MOK or blacklist keyrings."); + if (IS_ERR(ima_blacklist_keyring)) + panic("Can't allocate IMA blacklist keyring."); set_bit(KEY_FLAG_KEEP, &ima_blacklist_keyring->flags); return 0; -- cgit v0.10.2 From 93da17b18539cb021f1075f8620ee8f6da9b42aa Mon Sep 17 00:00:00 2001 From: Andreas Ziegler Date: Tue, 12 Apr 2016 19:54:58 +0100 Subject: security: integrity: Remove select to deleted option PUBLIC_KEY_ALGO_RSA Commit d43de6c780a8 ("akcipher: Move the RSA DER encoding check to the crypto layer") removed the Kconfig option PUBLIC_KEY_ALGO_RSA, but forgot to remove a 'select' to this option in the definition of INTEGRITY_ASYMMETRIC_KEYS. Let's remove the select, as it's ineffective now. Signed-off-by: Andreas Ziegler Signed-off-by: David Howells diff --git a/security/integrity/Kconfig b/security/integrity/Kconfig index 979be65..da95658 100644 --- a/security/integrity/Kconfig +++ b/security/integrity/Kconfig @@ -35,7 +35,6 @@ config INTEGRITY_ASYMMETRIC_KEYS default n select ASYMMETRIC_KEY_TYPE select ASYMMETRIC_PUBLIC_KEY_SUBTYPE - select PUBLIC_KEY_ALGO_RSA select CRYPTO_RSA select X509_CERTIFICATE_PARSER help -- cgit v0.10.2 From 898de7d0f298e53568891f0ec3547b14fe8bb5d5 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 12 Apr 2016 19:54:58 +0100 Subject: KEYS: user_update should use copy of payload made during preparsing The payload preparsing routine for user keys makes a copy of the payload provided by the caller and stashes it in the key_preparsed_payload struct for ->instantiate() or ->update() to use. However, ->update() takes another copy of this to attach to the keyring. ->update() should be using this directly and clearing the pointer in the preparse data. Signed-off-by: David Howells diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c index 8705d79..66b1840 100644 --- a/security/keys/user_defined.c +++ b/security/keys/user_defined.c @@ -96,45 +96,25 @@ EXPORT_SYMBOL_GPL(user_free_preparse); */ int user_update(struct key *key, struct key_preparsed_payload *prep) { - struct user_key_payload *upayload, *zap; - size_t datalen = prep->datalen; + struct user_key_payload *zap = NULL; int ret; - ret = -EINVAL; - if (datalen <= 0 || datalen > 32767 || !prep->data) - goto error; - - /* construct a replacement payload */ - ret = -ENOMEM; - upayload = kmalloc(sizeof(*upayload) + datalen, GFP_KERNEL); - if (!upayload) - goto error; - - upayload->datalen = datalen; - memcpy(upayload->data, prep->data, datalen); - /* check the quota and attach the new data */ - zap = upayload; - - ret = key_payload_reserve(key, datalen); - - if (ret == 0) { - /* attach the new data, displacing the old */ - if (!test_bit(KEY_FLAG_NEGATIVE, &key->flags)) - zap = key->payload.data[0]; - else - zap = NULL; - rcu_assign_keypointer(key, upayload); - key->expiry = 0; - } + ret = key_payload_reserve(key, prep->datalen); + if (ret < 0) + return ret; + + /* attach the new data, displacing the old */ + key->expiry = prep->expiry; + if (!test_bit(KEY_FLAG_NEGATIVE, &key->flags)) + zap = rcu_dereference_key(key); + rcu_assign_keypointer(key, prep->payload.data[0]); + prep->payload.data[0] = NULL; if (zap) kfree_rcu(zap, rcu); - -error: return ret; } - EXPORT_SYMBOL_GPL(user_update); /* -- cgit v0.10.2 From 13100a72f40f5748a04017e0ab3df4cf27c809ef Mon Sep 17 00:00:00 2001 From: Kirill Marinushkin Date: Tue, 12 Apr 2016 19:54:58 +0100 Subject: Security: Keys: Big keys stored encrypted Solved TODO task: big keys saved to shmem file are now stored encrypted. The encryption key is randomly generated and saved to payload[big_key_data]. Signed-off-by: Kirill Marinushkin Signed-off-by: David Howells diff --git a/security/keys/Kconfig b/security/keys/Kconfig index fe4d74e..4582809 100644 --- a/security/keys/Kconfig +++ b/security/keys/Kconfig @@ -41,6 +41,10 @@ config BIG_KEYS bool "Large payload keys" depends on KEYS depends on TMPFS + select CRYPTO + select CRYPTO_AES + select CRYPTO_ECB + select CRYPTO_RNG help This option provides support for holding large keys within the kernel (for example Kerberos ticket caches). The data may be stored out to diff --git a/security/keys/big_key.c b/security/keys/big_key.c index c721e39..9e443fc 100644 --- a/security/keys/big_key.c +++ b/security/keys/big_key.c @@ -14,8 +14,10 @@ #include #include #include +#include #include #include +#include /* * Layout of key payload words. @@ -28,6 +30,14 @@ enum { }; /* + * Crypto operation with big_key data + */ +enum big_key_op { + BIG_KEY_ENC, + BIG_KEY_DEC, +}; + +/* * If the data is under this limit, there's no point creating a shm file to * hold it as the permanently resident metadata for the shmem fs will be at * least as large as the data. @@ -35,6 +45,11 @@ enum { #define BIG_KEY_FILE_THRESHOLD (sizeof(struct inode) + sizeof(struct dentry)) /* + * Key size for big_key data encryption + */ +#define ENC_KEY_SIZE 16 + +/* * big_key defined keys take an arbitrary string as the description and an * arbitrary blob of data as the payload */ @@ -50,12 +65,62 @@ struct key_type key_type_big_key = { }; /* + * Crypto names for big_key data encryption + */ +static const char big_key_rng_name[] = "stdrng"; +static const char big_key_alg_name[] = "ecb(aes)"; + +/* + * Crypto algorithms for big_key data encryption + */ +static struct crypto_rng *big_key_rng; +static struct crypto_blkcipher *big_key_blkcipher; + +/* + * Generate random key to encrypt big_key data + */ +static inline int big_key_gen_enckey(u8 *key) +{ + return crypto_rng_get_bytes(big_key_rng, key, ENC_KEY_SIZE); +} + +/* + * Encrypt/decrypt big_key data + */ +static int big_key_crypt(enum big_key_op op, u8 *data, size_t datalen, u8 *key) +{ + int ret = -EINVAL; + struct scatterlist sgio; + struct blkcipher_desc desc; + + if (crypto_blkcipher_setkey(big_key_blkcipher, key, ENC_KEY_SIZE)) { + ret = -EAGAIN; + goto error; + } + + desc.flags = 0; + desc.tfm = big_key_blkcipher; + + sg_init_one(&sgio, data, datalen); + + if (op == BIG_KEY_ENC) + ret = crypto_blkcipher_encrypt(&desc, &sgio, &sgio, datalen); + else + ret = crypto_blkcipher_decrypt(&desc, &sgio, &sgio, datalen); + +error: + return ret; +} + +/* * Preparse a big key */ int big_key_preparse(struct key_preparsed_payload *prep) { struct path *path = (struct path *)&prep->payload.data[big_key_path]; struct file *file; + u8 *enckey; + u8 *data = NULL; ssize_t written; size_t datalen = prep->datalen; int ret; @@ -73,16 +138,43 @@ int big_key_preparse(struct key_preparsed_payload *prep) /* Create a shmem file to store the data in. This will permit the data * to be swapped out if needed. * - * TODO: Encrypt the stored data with a temporary key. + * File content is stored encrypted with randomly generated key. */ - file = shmem_kernel_file_setup("", datalen, 0); + size_t enclen = ALIGN(datalen, crypto_blkcipher_blocksize(big_key_blkcipher)); + + /* prepare aligned data to encrypt */ + data = kmalloc(enclen, GFP_KERNEL); + if (!data) + return -ENOMEM; + + memcpy(data, prep->data, datalen); + memset(data + datalen, 0x00, enclen - datalen); + + /* generate random key */ + enckey = kmalloc(ENC_KEY_SIZE, GFP_KERNEL); + if (!enckey) { + ret = -ENOMEM; + goto error; + } + + ret = big_key_gen_enckey(enckey); + if (ret) + goto err_enckey; + + /* encrypt aligned data */ + ret = big_key_crypt(BIG_KEY_ENC, data, enclen, enckey); + if (ret) + goto err_enckey; + + /* save aligned data to file */ + file = shmem_kernel_file_setup("", enclen, 0); if (IS_ERR(file)) { ret = PTR_ERR(file); - goto error; + goto err_enckey; } - written = kernel_write(file, prep->data, prep->datalen, 0); - if (written != datalen) { + written = kernel_write(file, data, enclen, 0); + if (written != enclen) { ret = written; if (written >= 0) ret = -ENOMEM; @@ -92,12 +184,15 @@ int big_key_preparse(struct key_preparsed_payload *prep) /* Pin the mount and dentry to the key so that we can open it again * later */ + prep->payload.data[big_key_data] = enckey; *path = file->f_path; path_get(path); fput(file); + kfree(data); } else { /* Just store the data in a buffer */ void *data = kmalloc(datalen, GFP_KERNEL); + if (!data) return -ENOMEM; @@ -108,7 +203,10 @@ int big_key_preparse(struct key_preparsed_payload *prep) err_fput: fput(file); +err_enckey: + kfree(enckey); error: + kfree(data); return ret; } @@ -119,10 +217,10 @@ void big_key_free_preparse(struct key_preparsed_payload *prep) { if (prep->datalen > BIG_KEY_FILE_THRESHOLD) { struct path *path = (struct path *)&prep->payload.data[big_key_path]; + path_put(path); - } else { - kfree(prep->payload.data[big_key_data]); } + kfree(prep->payload.data[big_key_data]); } /* @@ -147,15 +245,15 @@ void big_key_destroy(struct key *key) { size_t datalen = (size_t)key->payload.data[big_key_len]; - if (datalen) { + if (datalen > BIG_KEY_FILE_THRESHOLD) { struct path *path = (struct path *)&key->payload.data[big_key_path]; + path_put(path); path->mnt = NULL; path->dentry = NULL; - } else { - kfree(key->payload.data[big_key_data]); - key->payload.data[big_key_data] = NULL; } + kfree(key->payload.data[big_key_data]); + key->payload.data[big_key_data] = NULL; } /* @@ -188,17 +286,41 @@ long big_key_read(const struct key *key, char __user *buffer, size_t buflen) if (datalen > BIG_KEY_FILE_THRESHOLD) { struct path *path = (struct path *)&key->payload.data[big_key_path]; struct file *file; - loff_t pos; + u8 *data; + u8 *enckey = (u8 *)key->payload.data[big_key_data]; + size_t enclen = ALIGN(datalen, crypto_blkcipher_blocksize(big_key_blkcipher)); + + data = kmalloc(enclen, GFP_KERNEL); + if (!data) + return -ENOMEM; file = dentry_open(path, O_RDONLY, current_cred()); - if (IS_ERR(file)) - return PTR_ERR(file); + if (IS_ERR(file)) { + ret = PTR_ERR(file); + goto error; + } - pos = 0; - ret = vfs_read(file, buffer, datalen, &pos); - fput(file); - if (ret >= 0 && ret != datalen) + /* read file to kernel and decrypt */ + ret = kernel_read(file, 0, data, enclen); + if (ret >= 0 && ret != enclen) { ret = -EIO; + goto err_fput; + } + + ret = big_key_crypt(BIG_KEY_DEC, data, enclen, enckey); + if (ret) + goto err_fput; + + ret = datalen; + + /* copy decrypted data to user */ + if (copy_to_user(buffer, data, datalen) != 0) + ret = -EFAULT; + +err_fput: + fput(file); +error: + kfree(data); } else { ret = datalen; if (copy_to_user(buffer, key->payload.data[big_key_data], @@ -209,8 +331,48 @@ long big_key_read(const struct key *key, char __user *buffer, size_t buflen) return ret; } +/* + * Register key type + */ static int __init big_key_init(void) { return register_key_type(&key_type_big_key); } + +/* + * Initialize big_key crypto and RNG algorithms + */ +static int __init big_key_crypto_init(void) +{ + int ret = -EINVAL; + + /* init RNG */ + big_key_rng = crypto_alloc_rng(big_key_rng_name, 0, 0); + if (IS_ERR(big_key_rng)) { + big_key_rng = NULL; + return -EFAULT; + } + + /* seed RNG */ + ret = crypto_rng_reset(big_key_rng, NULL, crypto_rng_seedsize(big_key_rng)); + if (ret) + goto error; + + /* init block cipher */ + big_key_blkcipher = crypto_alloc_blkcipher(big_key_alg_name, 0, 0); + if (IS_ERR(big_key_blkcipher)) { + big_key_blkcipher = NULL; + ret = -EFAULT; + goto error; + } + + return 0; + +error: + crypto_free_rng(big_key_rng); + big_key_rng = NULL; + return ret; +} + device_initcall(big_key_init); +late_initcall(big_key_crypto_init); -- cgit v0.10.2 From ddbb41148724367394d0880c516bfaeed127b52e Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Tue, 12 Apr 2016 19:54:58 +0100 Subject: KEYS: Add KEYCTL_DH_COMPUTE command This adds userspace access to Diffie-Hellman computations through a new keyctl() syscall command to calculate shared secrets or public keys using input parameters stored in the keyring. Input key ids are provided in a struct due to the current 5-arg limit for the keyctl syscall. Only user keys are supported in order to avoid exposing the content of logon or encrypted keys. The output is written to the provided buffer, based on the assumption that the values are only needed in userspace. Future support for other types of key derivation would involve a new command, like KEYCTL_ECDH_COMPUTE. Once Diffie-Hellman support is included in the crypto API, this code can be converted to use the crypto API to take advantage of possible hardware acceleration and reduce redundant code. Signed-off-by: Mat Martineau Signed-off-by: David Howells diff --git a/Documentation/security/keys.txt b/Documentation/security/keys.txt index 8c18387..a2f70cf 100644 --- a/Documentation/security/keys.txt +++ b/Documentation/security/keys.txt @@ -823,6 +823,36 @@ The keyctl syscall functions are: A process must have search permission on the key for this function to be successful. + (*) Compute a Diffie-Hellman shared secret or public key + + long keyctl(KEYCTL_DH_COMPUTE, struct keyctl_dh_params *params, + char *buffer, size_t buflen); + + The params struct contains serial numbers for three keys: + + - The prime, p, known to both parties + - The local private key + - The base integer, which is either a shared generator or the + remote public key + + The value computed is: + + result = base ^ private (mod prime) + + If the base is the shared generator, the result is the local + public key. If the base is the remote public key, the result is + the shared secret. + + The buffer length must be at least the length of the prime, or zero. + + If the buffer length is nonzero, the length of the result is + returned when it is successfully calculated and copied in to the + buffer. When the buffer length is zero, the minimum required + buffer length is returned. + + This function will return error EOPNOTSUPP if the key type is not + supported, error ENOKEY if the key could not be found, or error + EACCES if the key is not readable by the caller. =============== KERNEL SERVICES diff --git a/include/uapi/linux/keyctl.h b/include/uapi/linux/keyctl.h index 840cb99..86eddd6 100644 --- a/include/uapi/linux/keyctl.h +++ b/include/uapi/linux/keyctl.h @@ -12,6 +12,8 @@ #ifndef _LINUX_KEYCTL_H #define _LINUX_KEYCTL_H +#include + /* special process keyring shortcut IDs */ #define KEY_SPEC_THREAD_KEYRING -1 /* - key ID for thread-specific keyring */ #define KEY_SPEC_PROCESS_KEYRING -2 /* - key ID for process-specific keyring */ @@ -57,5 +59,13 @@ #define KEYCTL_INSTANTIATE_IOV 20 /* instantiate a partially constructed key */ #define KEYCTL_INVALIDATE 21 /* invalidate a key */ #define KEYCTL_GET_PERSISTENT 22 /* get a user's persistent keyring */ +#define KEYCTL_DH_COMPUTE 23 /* Compute Diffie-Hellman values */ + +/* keyctl structures */ +struct keyctl_dh_params { + __s32 private; + __s32 prime; + __s32 base; +}; #endif /* _LINUX_KEYCTL_H */ diff --git a/security/keys/Kconfig b/security/keys/Kconfig index 4582809..f826e87 100644 --- a/security/keys/Kconfig +++ b/security/keys/Kconfig @@ -85,3 +85,14 @@ config ENCRYPTED_KEYS Userspace only ever sees/stores encrypted blobs. If you are unsure as to whether this is required, answer N. + +config KEY_DH_OPERATIONS + bool "Diffie-Hellman operations on retained keys" + depends on KEYS + select MPILIB + help + This option provides support for calculating Diffie-Hellman + public keys and shared secrets using values stored as keys + in the kernel. + + If you are unsure as to whether this is required, answer N. diff --git a/security/keys/Makefile b/security/keys/Makefile index dfb3a7b..1fd4a16 100644 --- a/security/keys/Makefile +++ b/security/keys/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_KEYS_COMPAT) += compat.o obj-$(CONFIG_PROC_FS) += proc.o obj-$(CONFIG_SYSCTL) += sysctl.o obj-$(CONFIG_PERSISTENT_KEYRINGS) += persistent.o +obj-$(CONFIG_KEY_DH_OPERATIONS) += dh.o # # Key types diff --git a/security/keys/compat.c b/security/keys/compat.c index 25430a3..c8783b3 100644 --- a/security/keys/compat.c +++ b/security/keys/compat.c @@ -132,6 +132,10 @@ COMPAT_SYSCALL_DEFINE5(keyctl, u32, option, case KEYCTL_GET_PERSISTENT: return keyctl_get_persistent(arg2, arg3); + case KEYCTL_DH_COMPUTE: + return keyctl_dh_compute(compat_ptr(arg2), compat_ptr(arg3), + arg4); + default: return -EOPNOTSUPP; } diff --git a/security/keys/dh.c b/security/keys/dh.c new file mode 100644 index 0000000..880505a --- /dev/null +++ b/security/keys/dh.c @@ -0,0 +1,160 @@ +/* Crypto operations using stored keys + * + * Copyright (c) 2016, Intel Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include "internal.h" + +/* + * Public key or shared secret generation function [RFC2631 sec 2.1.1] + * + * ya = g^xa mod p; + * or + * ZZ = yb^xa mod p; + * + * where xa is the local private key, ya is the local public key, g is + * the generator, p is the prime, yb is the remote public key, and ZZ + * is the shared secret. + * + * Both are the same calculation, so g or yb are the "base" and ya or + * ZZ are the "result". + */ +static int do_dh(MPI result, MPI base, MPI xa, MPI p) +{ + return mpi_powm(result, base, xa, p); +} + +static ssize_t mpi_from_key(key_serial_t keyid, size_t maxlen, MPI *mpi) +{ + struct key *key; + key_ref_t key_ref; + long status; + ssize_t ret; + + key_ref = lookup_user_key(keyid, 0, KEY_NEED_READ); + if (IS_ERR(key_ref)) { + ret = -ENOKEY; + goto error; + } + + key = key_ref_to_ptr(key_ref); + + ret = -EOPNOTSUPP; + if (key->type == &key_type_user) { + down_read(&key->sem); + status = key_validate(key); + if (status == 0) { + const struct user_key_payload *payload; + + payload = user_key_payload(key); + + if (maxlen == 0) { + *mpi = NULL; + ret = payload->datalen; + } else if (payload->datalen <= maxlen) { + *mpi = mpi_read_raw_data(payload->data, + payload->datalen); + if (*mpi) + ret = payload->datalen; + } else { + ret = -EINVAL; + } + } + up_read(&key->sem); + } + + key_put(key); +error: + return ret; +} + +long keyctl_dh_compute(struct keyctl_dh_params __user *params, + char __user *buffer, size_t buflen) +{ + long ret; + MPI base, private, prime, result; + unsigned nbytes; + struct keyctl_dh_params pcopy; + uint8_t *kbuf; + ssize_t keylen; + size_t resultlen; + + if (!params || (!buffer && buflen)) { + ret = -EINVAL; + goto out; + } + if (copy_from_user(&pcopy, params, sizeof(pcopy)) != 0) { + ret = -EFAULT; + goto out; + } + + keylen = mpi_from_key(pcopy.prime, buflen, &prime); + if (keylen < 0 || !prime) { + /* buflen == 0 may be used to query the required buffer size, + * which is the prime key length. + */ + ret = keylen; + goto out; + } + + /* The result is never longer than the prime */ + resultlen = keylen; + + keylen = mpi_from_key(pcopy.base, SIZE_MAX, &base); + if (keylen < 0 || !base) { + ret = keylen; + goto error1; + } + + keylen = mpi_from_key(pcopy.private, SIZE_MAX, &private); + if (keylen < 0 || !private) { + ret = keylen; + goto error2; + } + + result = mpi_alloc(0); + if (!result) { + ret = -ENOMEM; + goto error3; + } + + kbuf = kmalloc(resultlen, GFP_KERNEL); + if (!kbuf) { + ret = -ENOMEM; + goto error4; + } + + ret = do_dh(result, base, private, prime); + if (ret) + goto error5; + + ret = mpi_read_buffer(result, kbuf, resultlen, &nbytes, NULL); + if (ret != 0) + goto error5; + + ret = nbytes; + if (copy_to_user(buffer, kbuf, nbytes) != 0) + ret = -EFAULT; + +error5: + kfree(kbuf); +error4: + mpi_free(result); +error3: + mpi_free(private); +error2: + mpi_free(base); +error1: + mpi_free(prime); +out: + return ret; +} diff --git a/security/keys/internal.h b/security/keys/internal.h index 5105c2c..8ec7a52 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -15,6 +15,7 @@ #include #include #include +#include struct iovec; @@ -257,6 +258,17 @@ static inline long keyctl_get_persistent(uid_t uid, key_serial_t destring) } #endif +#ifdef CONFIG_KEY_DH_OPERATIONS +extern long keyctl_dh_compute(struct keyctl_dh_params __user *, char __user *, + size_t); +#else +static inline long keyctl_dh_compute(struct keyctl_dh_params __user *params, + char __user *buffer, size_t buflen) +{ + return -EOPNOTSUPP; +} +#endif + /* * Debugging key validation */ diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index ed73c6c..3b135a0 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -1686,6 +1686,11 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3, case KEYCTL_GET_PERSISTENT: return keyctl_get_persistent((uid_t)arg2, (key_serial_t)arg3); + case KEYCTL_DH_COMPUTE: + return keyctl_dh_compute((struct keyctl_dh_params __user *) arg2, + (char __user *) arg3, + (size_t) arg4); + default: return -EOPNOTSUPP; } -- cgit v0.10.2 From 0fd71a620be8648486a126fccadf9f7c2a818676 Mon Sep 17 00:00:00 2001 From: Prarit Bhargava Date: Thu, 14 Apr 2016 10:40:57 -0400 Subject: selinux: Change bool variable name to index. security_get_bool_value(int bool) argument "bool" conflicts with in-kernel macros such as BUILD_BUG(). This patch changes this to index which isn't a type. Cc: Paul Moore Cc: Stephen Smalley Cc: Eric Paris Cc: James Morris Cc: "Serge E. Hallyn" Cc: Rasmus Villemoes Cc: Andrew Perepechko Cc: Jeff Vander Stoep Cc: selinux@tycho.nsa.gov Cc: Eric Paris Cc: Paul Moore Cc: David Howells Signed-off-by: Prarit Bhargava Acked-by: David Howells [PM: wrapped description for checkpatch.pl, use "selinux:..." as subj] Signed-off-by: Paul Moore diff --git a/security/selinux/include/conditional.h b/security/selinux/include/conditional.h index 67ce7a8..ff4fddc 100644 --- a/security/selinux/include/conditional.h +++ b/security/selinux/include/conditional.h @@ -17,6 +17,6 @@ int security_get_bools(int *len, char ***names, int **values); int security_set_bools(int len, int *values); -int security_get_bool_value(int bool); +int security_get_bool_value(int index); #endif diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index ebda973..89df646 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -2696,7 +2696,7 @@ out: return rc; } -int security_get_bool_value(int bool) +int security_get_bool_value(int index) { int rc; int len; @@ -2705,10 +2705,10 @@ int security_get_bool_value(int bool) rc = -EFAULT; len = policydb.p_bools.nprim; - if (bool >= len) + if (index >= len) goto out; - rc = policydb.bool_val_to_struct[bool]->state; + rc = policydb.bool_val_to_struct[index]->state; out: read_unlock(&policy_rwlock); return rc; -- cgit v0.10.2 From 2c97165befb487c0dc8b25d39f457d0d91d22a6f Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Tue, 19 Apr 2016 16:36:28 -0400 Subject: selinux: don't revalidate an inode's label when explicitly setting it There is no point in attempting to revalidate an inode's security label when we are in the process of setting it. Reported-by: Stephen Smalley Signed-off-by: Paul Moore diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index fce7dc8..f8ecc0a 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -297,6 +297,13 @@ static struct inode_security_struct *inode_security(struct inode *inode) return inode->i_security; } +static struct inode_security_struct *backing_inode_security_novalidate(struct dentry *dentry) +{ + struct inode *inode = d_backing_inode(dentry); + + return inode->i_security; +} + /* * Get the security label of a dentry's backing inode. */ @@ -686,7 +693,7 @@ static int selinux_set_mnt_opts(struct super_block *sb, struct superblock_security_struct *sbsec = sb->s_security; const char *name = sb->s_type->name; struct dentry *root = sbsec->sb->s_root; - struct inode_security_struct *root_isec = backing_inode_security(root); + struct inode_security_struct *root_isec; u32 fscontext_sid = 0, context_sid = 0, rootcontext_sid = 0; u32 defcontext_sid = 0; char **mount_options = opts->mnt_opts; @@ -729,6 +736,8 @@ static int selinux_set_mnt_opts(struct super_block *sb, && (num_opts == 0)) goto out; + root_isec = backing_inode_security_novalidate(root); + /* * parse the mount options, check if they are valid sids. * also check if someone is trying to mount the same sb more @@ -3222,7 +3231,7 @@ out_nofree: static int selinux_inode_setsecurity(struct inode *inode, const char *name, const void *value, size_t size, int flags) { - struct inode_security_struct *isec = inode_security(inode); + struct inode_security_struct *isec = inode_security_novalidate(inode); u32 newsid; int rc; -- cgit v0.10.2 From 20cdef8d57591ec8674f65ccfe555aca5fd10b64 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Mon, 4 Apr 2016 14:14:42 -0400 Subject: selinux: delay inode label lookup as long as possible Since looking up an inode's label can result in revalidation, delay the lookup as long as possible to limit the performance impact. Signed-off-by: Paul Moore diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index f8ecc0a..b09aad7 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -1790,7 +1790,6 @@ static int selinux_determine_inode_label(struct inode *dir, u32 *_new_isid) { const struct superblock_security_struct *sbsec = dir->i_sb->s_security; - const struct inode_security_struct *dsec = inode_security(dir); const struct task_security_struct *tsec = current_security(); if ((sbsec->flags & SE_SBINITIALIZED) && @@ -1800,6 +1799,7 @@ static int selinux_determine_inode_label(struct inode *dir, tsec->create_sid) { *_new_isid = tsec->create_sid; } else { + const struct inode_security_struct *dsec = inode_security(dir); return security_transition_sid(tsec->sid, dsec->sid, tclass, name, _new_isid); } @@ -2084,7 +2084,7 @@ static int selinux_binder_transfer_file(struct task_struct *from, u32 sid = task_sid(to); struct file_security_struct *fsec = file->f_security; struct dentry *dentry = file->f_path.dentry; - struct inode_security_struct *isec = backing_inode_security(dentry); + struct inode_security_struct *isec; struct common_audit_data ad; int rc; @@ -2103,6 +2103,7 @@ static int selinux_binder_transfer_file(struct task_struct *from, if (unlikely(IS_PRIVATE(d_backing_inode(dentry)))) return 0; + isec = backing_inode_security(dentry); return avc_has_perm(sid, isec->sid, isec->sclass, file_to_av(file), &ad); } @@ -3057,7 +3058,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags) { struct inode *inode = d_backing_inode(dentry); - struct inode_security_struct *isec = backing_inode_security(dentry); + struct inode_security_struct *isec; struct superblock_security_struct *sbsec; struct common_audit_data ad; u32 newsid, sid = current_sid(); @@ -3076,6 +3077,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name, ad.type = LSM_AUDIT_DATA_DENTRY; ad.u.dentry = dentry; + isec = backing_inode_security(dentry); rc = avc_has_perm(sid, isec->sid, isec->sclass, FILE__RELABELFROM, &ad); if (rc) @@ -3134,7 +3136,7 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name, int flags) { struct inode *inode = d_backing_inode(dentry); - struct inode_security_struct *isec = backing_inode_security(dentry); + struct inode_security_struct *isec; u32 newsid; int rc; @@ -3151,6 +3153,7 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name, return; } + isec = backing_inode_security(dentry); isec->sclass = inode_mode_to_security_class(inode->i_mode); isec->sid = newsid; isec->initialized = LABEL_INITIALIZED; @@ -3192,7 +3195,7 @@ static int selinux_inode_getsecurity(struct inode *inode, const char *name, void u32 size; int error; char *context = NULL; - struct inode_security_struct *isec = inode_security(inode); + struct inode_security_struct *isec; if (strcmp(name, XATTR_SELINUX_SUFFIX)) return -EOPNOTSUPP; @@ -3211,6 +3214,7 @@ static int selinux_inode_getsecurity(struct inode *inode, const char *name, void if (!error) error = cred_has_capability(current_cred(), CAP_MAC_ADMIN, SECURITY_CAP_NOAUDIT); + isec = inode_security(inode); if (!error) error = security_sid_to_context_force(isec->sid, &context, &size); @@ -3320,7 +3324,7 @@ static int ioctl_has_perm(const struct cred *cred, struct file *file, struct common_audit_data ad; struct file_security_struct *fsec = file->f_security; struct inode *inode = file_inode(file); - struct inode_security_struct *isec = inode_security(inode); + struct inode_security_struct *isec; struct lsm_ioctlop_audit ioctl; u32 ssid = cred_sid(cred); int rc; @@ -3344,6 +3348,7 @@ static int ioctl_has_perm(const struct cred *cred, struct file *file, if (unlikely(IS_PRIVATE(inode))) return 0; + isec = inode_security(inode); rc = avc_has_extended_perms(ssid, isec->sid, isec->sclass, requested, driver, xperm, &ad); out: @@ -3745,18 +3750,18 @@ static int selinux_kernel_module_from_file(struct file *file) SYSTEM__MODULE_LOAD, NULL); /* finit_module */ + ad.type = LSM_AUDIT_DATA_PATH; ad.u.path = file->f_path; - isec = inode_security(file_inode(file)); fsec = file->f_security; - if (sid != fsec->sid) { rc = avc_has_perm(sid, fsec->sid, SECCLASS_FD, FD__USE, &ad); if (rc) return rc; } + isec = inode_security(file_inode(file)); return avc_has_perm(sid, isec->sid, SECCLASS_SYSTEM, SYSTEM__MODULE_LOAD, &ad); } -- cgit v0.10.2 From 1ac42476263eec99fb2d3c31ee946cb44e80ddd5 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Mon, 18 Apr 2016 16:41:38 -0400 Subject: selinux: check ss_initialized before revalidating an inode label There is no point in trying to revalidate an inode's security label if the security server is not yet initialized. Signed-off-by: Paul Moore diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index b09aad7..474011c 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -259,7 +259,7 @@ static int __inode_security_revalidate(struct inode *inode, might_sleep_if(may_sleep); - if (isec->initialized != LABEL_INITIALIZED) { + if (ss_initialized && isec->initialized != LABEL_INITIALIZED) { if (!may_sleep) return -ECHILD; -- cgit v0.10.2 From b53f27e4fa0d0e72d897830cc4f3f83d2a25d952 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 20 Apr 2016 15:46:23 -0700 Subject: string_helpers: add kstrdup_quotable Handle allocating and escaping a string safe for logging. Signed-off-by: Kees Cook Signed-off-by: James Morris diff --git a/include/linux/string_helpers.h b/include/linux/string_helpers.h index dabe643..9de228a 100644 --- a/include/linux/string_helpers.h +++ b/include/linux/string_helpers.h @@ -68,4 +68,6 @@ static inline int string_escape_str_any_np(const char *src, char *dst, return string_escape_str(src, dst, sz, ESCAPE_ANY_NP, only); } +char *kstrdup_quotable(const char *src, gfp_t gfp); + #endif diff --git a/lib/string_helpers.c b/lib/string_helpers.c index 5c88204..aa00c9f 100644 --- a/lib/string_helpers.c +++ b/lib/string_helpers.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -534,3 +535,30 @@ int string_escape_mem(const char *src, size_t isz, char *dst, size_t osz, return p - dst; } EXPORT_SYMBOL(string_escape_mem); + +/* + * Return an allocated string that has been escaped of special characters + * and double quotes, making it safe to log in quotes. + */ +char *kstrdup_quotable(const char *src, gfp_t gfp) +{ + size_t slen, dlen; + char *dst; + const int flags = ESCAPE_HEX; + const char esc[] = "\f\n\r\t\v\a\e\\\""; + + if (!src) + return NULL; + slen = strlen(src); + + dlen = string_escape_mem(src, slen, NULL, 0, flags, esc); + dst = kmalloc(dlen + 1, gfp); + if (!dst) + return NULL; + + WARN_ON(string_escape_mem(src, slen, dst, dlen, flags, esc) != dlen); + dst[dlen] = '\0'; + + return dst; +} +EXPORT_SYMBOL_GPL(kstrdup_quotable); -- cgit v0.10.2 From 0d0443288f2244d7054796086e481ddef6abdbba Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 20 Apr 2016 15:46:24 -0700 Subject: string_helpers: add kstrdup_quotable_cmdline Provide an escaped (but readable: no inter-argument NULLs) commandline safe for logging. Signed-off-by: Kees Cook Signed-off-by: James Morris diff --git a/include/linux/string_helpers.h b/include/linux/string_helpers.h index 9de228a..684d269 100644 --- a/include/linux/string_helpers.h +++ b/include/linux/string_helpers.h @@ -69,5 +69,6 @@ static inline int string_escape_str_any_np(const char *src, char *dst, } char *kstrdup_quotable(const char *src, gfp_t gfp); +char *kstrdup_quotable_cmdline(struct task_struct *task, gfp_t gfp); #endif diff --git a/lib/string_helpers.c b/lib/string_helpers.c index aa00c9f..b16ee85 100644 --- a/lib/string_helpers.c +++ b/lib/string_helpers.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -562,3 +563,36 @@ char *kstrdup_quotable(const char *src, gfp_t gfp) return dst; } EXPORT_SYMBOL_GPL(kstrdup_quotable); + +/* + * Returns allocated NULL-terminated string containing process + * command line, with inter-argument NULLs replaced with spaces, + * and other special characters escaped. + */ +char *kstrdup_quotable_cmdline(struct task_struct *task, gfp_t gfp) +{ + char *buffer, *quoted; + int i, res; + + buffer = kmalloc(PAGE_SIZE, GFP_TEMPORARY); + if (!buffer) + return NULL; + + res = get_cmdline(task, buffer, PAGE_SIZE - 1); + buffer[res] = '\0'; + + /* Collapse trailing NULLs, leave res pointing to last non-NULL. */ + while (--res >= 0 && buffer[res] == '\0') + ; + + /* Replace inter-argument NULLs. */ + for (i = 0; i <= res; i++) + if (buffer[i] == '\0') + buffer[i] = ' '; + + /* Make sure result is printable. */ + quoted = kstrdup_quotable(buffer, gfp); + kfree(buffer); + return quoted; +} +EXPORT_SYMBOL_GPL(kstrdup_quotable_cmdline); -- cgit v0.10.2 From 21985319add60b55fc27230d9421a3e5af7e998a Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 20 Apr 2016 15:46:25 -0700 Subject: string_helpers: add kstrdup_quotable_file Allocate a NULL-terminated file path with special characters escaped, safe for logging. Signed-off-by: Kees Cook Signed-off-by: James Morris diff --git a/include/linux/string_helpers.h b/include/linux/string_helpers.h index 684d269..5ce9538 100644 --- a/include/linux/string_helpers.h +++ b/include/linux/string_helpers.h @@ -3,6 +3,8 @@ #include +struct file; + /* Descriptions of the types of units to * print in */ enum string_size_units { @@ -70,5 +72,6 @@ static inline int string_escape_str_any_np(const char *src, char *dst, char *kstrdup_quotable(const char *src, gfp_t gfp); char *kstrdup_quotable_cmdline(struct task_struct *task, gfp_t gfp); +char *kstrdup_quotable_file(struct file *file, gfp_t gfp); #endif diff --git a/lib/string_helpers.c b/lib/string_helpers.c index b16ee85..ecaac2c 100644 --- a/lib/string_helpers.c +++ b/lib/string_helpers.c @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include #include #include @@ -596,3 +598,31 @@ char *kstrdup_quotable_cmdline(struct task_struct *task, gfp_t gfp) return quoted; } EXPORT_SYMBOL_GPL(kstrdup_quotable_cmdline); + +/* + * Returns allocated NULL-terminated string containing pathname, + * with special characters escaped, able to be safely logged. If + * there is an error, the leading character will be "<". + */ +char *kstrdup_quotable_file(struct file *file, gfp_t gfp) +{ + char *temp, *pathname; + + if (!file) + return kstrdup("", gfp); + + /* We add 11 spaces for ' (deleted)' to be appended */ + temp = kmalloc(PATH_MAX + 11, GFP_TEMPORARY); + if (!temp) + return kstrdup("", gfp); + + pathname = file_path(file, temp, PATH_MAX + 11); + if (IS_ERR(pathname)) + pathname = kstrdup("", gfp); + else + pathname = kstrdup_quotable(pathname, gfp); + + kfree(temp); + return pathname; +} +EXPORT_SYMBOL_GPL(kstrdup_quotable_file); -- cgit v0.10.2 From 8a56038c2aef97a9d4b9dd608b912b9d9a4a2d68 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 20 Apr 2016 15:46:26 -0700 Subject: Yama: consolidate error reporting Use a common error reporting function for Yama violation reports, and give more detail into the process command lines. Signed-off-by: Kees Cook Signed-off-by: James Morris diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c index cb6ed10..c19f6e5 100644 --- a/security/yama/yama_lsm.c +++ b/security/yama/yama_lsm.c @@ -18,6 +18,7 @@ #include #include #include +#include #define YAMA_SCOPE_DISABLED 0 #define YAMA_SCOPE_RELATIONAL 1 @@ -41,6 +42,22 @@ static DEFINE_SPINLOCK(ptracer_relations_lock); static void yama_relation_cleanup(struct work_struct *work); static DECLARE_WORK(yama_relation_work, yama_relation_cleanup); +static void report_access(const char *access, struct task_struct *target, + struct task_struct *agent) +{ + char *target_cmd, *agent_cmd; + + target_cmd = kstrdup_quotable_cmdline(target, GFP_KERNEL); + agent_cmd = kstrdup_quotable_cmdline(agent, GFP_KERNEL); + + pr_notice_ratelimited( + "ptrace %s of \"%s\"[%d] was attempted by \"%s\"[%d]\n", + access, target_cmd, target->pid, agent_cmd, agent->pid); + + kfree(agent_cmd); + kfree(target_cmd); +} + /** * yama_relation_cleanup - remove invalid entries from the relation list * @@ -307,11 +324,8 @@ static int yama_ptrace_access_check(struct task_struct *child, } } - if (rc && (mode & PTRACE_MODE_NOAUDIT) == 0) { - printk_ratelimited(KERN_NOTICE - "ptrace of pid %d was attempted by: %s (pid %d)\n", - child->pid, current->comm, current->pid); - } + if (rc && (mode & PTRACE_MODE_NOAUDIT) == 0) + report_access("attach", child, current); return rc; } @@ -337,11 +351,8 @@ int yama_ptrace_traceme(struct task_struct *parent) break; } - if (rc) { - printk_ratelimited(KERN_NOTICE - "ptraceme of pid %d was attempted by: %s (pid %d)\n", - current->pid, parent->comm, parent->pid); - } + if (rc) + report_access("traceme", current, parent); return rc; } -- cgit v0.10.2 From 1284ab5b2dcb927d38e4f3fbc2e307f3d1af9262 Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Wed, 20 Apr 2016 15:46:27 -0700 Subject: fs: define a string representation of the kernel_read_file_id enumeration A string representation of the kernel_read_file_id enumeration is needed for displaying messages (eg. pr_info, auditing) that can be used by multiple LSMs and the integrity subsystem. To simplify keeping the list of strings up to date with the enumeration, this patch defines two new preprocessing macros named __fid_enumify and __fid_stringify to create the enumeration and an array of strings. kernel_read_file_id_str() returns a string based on the enumeration. Signed-off-by: Mimi Zohar [kees: removed removal of my old version, constified pointer values] Signed-off-by: Kees Cook Signed-off-by: James Morris diff --git a/include/linux/fs.h b/include/linux/fs.h index 14a9719..9047755 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2580,15 +2580,34 @@ static inline void i_readcount_inc(struct inode *inode) #endif extern int do_pipe_flags(int *, int); +#define __kernel_read_file_id(id) \ + id(UNKNOWN, unknown) \ + id(FIRMWARE, firmware) \ + id(MODULE, kernel-module) \ + id(KEXEC_IMAGE, kexec-image) \ + id(KEXEC_INITRAMFS, kexec-initramfs) \ + id(POLICY, security-policy) \ + id(MAX_ID, ) + +#define __fid_enumify(ENUM, dummy) READING_ ## ENUM, +#define __fid_stringify(dummy, str) #str, + enum kernel_read_file_id { - READING_FIRMWARE = 1, - READING_MODULE, - READING_KEXEC_IMAGE, - READING_KEXEC_INITRAMFS, - READING_POLICY, - READING_MAX_ID + __kernel_read_file_id(__fid_enumify) +}; + +static const char * const kernel_read_file_str[] = { + __kernel_read_file_id(__fid_stringify) }; +static inline const char * const kernel_read_file_id_str(enum kernel_read_file_id id) +{ + if (id < 0 || id >= READING_MAX_ID) + return kernel_read_file_str[READING_UNKNOWN]; + + return kernel_read_file_str[id]; +} + extern int kernel_read(struct file *, loff_t, char *, unsigned long); extern int kernel_read_file(struct file *, void **, loff_t *, loff_t, enum kernel_read_file_id); -- cgit v0.10.2 From 9b091556a073a9f5f93e2ad23d118f45c4796a84 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 20 Apr 2016 15:46:28 -0700 Subject: LSM: LoadPin for kernel file loading restrictions This LSM enforces that kernel-loaded files (modules, firmware, etc) must all come from the same filesystem, with the expectation that such a filesystem is backed by a read-only device such as dm-verity or CDROM. This allows systems that have a verified and/or unchangeable filesystem to enforce module and firmware loading restrictions without needing to sign the files individually. Signed-off-by: Kees Cook Acked-by: Serge Hallyn Signed-off-by: James Morris diff --git a/Documentation/security/LoadPin.txt b/Documentation/security/LoadPin.txt new file mode 100644 index 0000000..e11877f --- /dev/null +++ b/Documentation/security/LoadPin.txt @@ -0,0 +1,17 @@ +LoadPin is a Linux Security Module that ensures all kernel-loaded files +(modules, firmware, etc) all originate from the same filesystem, with +the expectation that such a filesystem is backed by a read-only device +such as dm-verity or CDROM. This allows systems that have a verified +and/or unchangeable filesystem to enforce module and firmware loading +restrictions without needing to sign the files individually. + +The LSM is selectable at build-time with CONFIG_SECURITY_LOADPIN, and +can be controlled at boot-time with the kernel command line option +"loadpin.enabled". By default, it is enabled, but can be disabled at +boot ("loadpin.enabled=0"). + +LoadPin starts pinning when it sees the first file loaded. If the +block device backing the filesystem is not read-only, a sysctl is +created to toggle pinning: /proc/sys/kernel/loadpin/enabled. (Having +a mutable filesystem means pinning is mutable too, but having the +sysctl allows for easy testing on systems with a mutable filesystem.) diff --git a/MAINTAINERS b/MAINTAINERS index 1c32f8a..b4b1e81 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9962,6 +9962,12 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/jj/apparmor-dev.git S: Supported F: security/apparmor/ +LOADPIN SECURITY MODULE +M: Kees Cook +T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git lsm/loadpin +S: Supported +F: security/loadpin/ + YAMA SECURITY MODULE M: Kees Cook T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git yama/tip diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index ae25378..6e466fc 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -1892,5 +1892,10 @@ extern void __init yama_add_hooks(void); #else static inline void __init yama_add_hooks(void) { } #endif +#ifdef CONFIG_SECURITY_LOADPIN +void __init loadpin_add_hooks(void); +#else +static inline void loadpin_add_hooks(void) { }; +#endif #endif /* ! __LINUX_LSM_HOOKS_H */ diff --git a/security/Kconfig b/security/Kconfig index e4523789..176758c 100644 --- a/security/Kconfig +++ b/security/Kconfig @@ -122,6 +122,7 @@ source security/selinux/Kconfig source security/smack/Kconfig source security/tomoyo/Kconfig source security/apparmor/Kconfig +source security/loadpin/Kconfig source security/yama/Kconfig source security/integrity/Kconfig diff --git a/security/Makefile b/security/Makefile index c9bfbc8..f2d71cd 100644 --- a/security/Makefile +++ b/security/Makefile @@ -8,6 +8,7 @@ subdir-$(CONFIG_SECURITY_SMACK) += smack subdir-$(CONFIG_SECURITY_TOMOYO) += tomoyo subdir-$(CONFIG_SECURITY_APPARMOR) += apparmor subdir-$(CONFIG_SECURITY_YAMA) += yama +subdir-$(CONFIG_SECURITY_LOADPIN) += loadpin # always enable default capabilities obj-y += commoncap.o @@ -22,6 +23,7 @@ obj-$(CONFIG_AUDIT) += lsm_audit.o obj-$(CONFIG_SECURITY_TOMOYO) += tomoyo/ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor/ obj-$(CONFIG_SECURITY_YAMA) += yama/ +obj-$(CONFIG_SECURITY_LOADPIN) += loadpin/ obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o # Object integrity file lists diff --git a/security/loadpin/Kconfig b/security/loadpin/Kconfig new file mode 100644 index 0000000..c668ac4 --- /dev/null +++ b/security/loadpin/Kconfig @@ -0,0 +1,10 @@ +config SECURITY_LOADPIN + bool "Pin load of kernel files (modules, fw, etc) to one filesystem" + depends on SECURITY && BLOCK + help + Any files read through the kernel file reading interface + (kernel modules, firmware, kexec images, security policy) will + be pinned to the first filesystem used for loading. Any files + that come from other filesystems will be rejected. This is best + used on systems without an initrd that have a root filesystem + backed by a read-only device such as dm-verity or a CDROM. diff --git a/security/loadpin/Makefile b/security/loadpin/Makefile new file mode 100644 index 0000000..c2d77f8 --- /dev/null +++ b/security/loadpin/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_SECURITY_LOADPIN) += loadpin.o diff --git a/security/loadpin/loadpin.c b/security/loadpin/loadpin.c new file mode 100644 index 0000000..e4debae --- /dev/null +++ b/security/loadpin/loadpin.c @@ -0,0 +1,190 @@ +/* + * Module and Firmware Pinning Security Module + * + * Copyright 2011-2016 Google Inc. + * + * Author: Kees Cook + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "LoadPin: " fmt + +#include +#include +#include +#include +#include +#include +#include /* current */ +#include + +static void report_load(const char *origin, struct file *file, char *operation) +{ + char *cmdline, *pathname; + + pathname = kstrdup_quotable_file(file, GFP_KERNEL); + cmdline = kstrdup_quotable_cmdline(current, GFP_KERNEL); + + pr_notice("%s %s obj=%s%s%s pid=%d cmdline=%s%s%s\n", + origin, operation, + (pathname && pathname[0] != '<') ? "\"" : "", + pathname, + (pathname && pathname[0] != '<') ? "\"" : "", + task_pid_nr(current), + cmdline ? "\"" : "", cmdline, cmdline ? "\"" : ""); + + kfree(cmdline); + kfree(pathname); +} + +static int enabled = 1; +static struct super_block *pinned_root; +static DEFINE_SPINLOCK(pinned_root_spinlock); + +#ifdef CONFIG_SYSCTL +static int zero; +static int one = 1; + +static struct ctl_path loadpin_sysctl_path[] = { + { .procname = "kernel", }, + { .procname = "loadpin", }, + { } +}; + +static struct ctl_table loadpin_sysctl_table[] = { + { + .procname = "enabled", + .data = &enabled, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = &zero, + .extra2 = &one, + }, + { } +}; + +/* + * This must be called after early kernel init, since then the rootdev + * is available. + */ +static void check_pinning_enforcement(struct super_block *mnt_sb) +{ + bool ro = false; + + /* + * If load pinning is not enforced via a read-only block + * device, allow sysctl to change modes for testing. + */ + if (mnt_sb->s_bdev) { + ro = bdev_read_only(mnt_sb->s_bdev); + pr_info("dev(%u,%u): %s\n", + MAJOR(mnt_sb->s_bdev->bd_dev), + MINOR(mnt_sb->s_bdev->bd_dev), + ro ? "read-only" : "writable"); + } else + pr_info("mnt_sb lacks block device, treating as: writable\n"); + + if (!ro) { + if (!register_sysctl_paths(loadpin_sysctl_path, + loadpin_sysctl_table)) + pr_notice("sysctl registration failed!\n"); + else + pr_info("load pinning can be disabled.\n"); + } else + pr_info("load pinning engaged.\n"); +} +#else +static void check_pinning_enforcement(struct super_block *mnt_sb) +{ + pr_info("load pinning engaged.\n"); +} +#endif + +static void loadpin_sb_free_security(struct super_block *mnt_sb) +{ + /* + * When unmounting the filesystem we were using for load + * pinning, we acknowledge the superblock release, but make sure + * no other modules or firmware can be loaded. + */ + if (!IS_ERR_OR_NULL(pinned_root) && mnt_sb == pinned_root) { + pinned_root = ERR_PTR(-EIO); + pr_info("umount pinned fs: refusing further loads\n"); + } +} + +static int loadpin_read_file(struct file *file, enum kernel_read_file_id id) +{ + struct super_block *load_root; + const char *origin = kernel_read_file_id_str(id); + + /* This handles the older init_module API that has a NULL file. */ + if (!file) { + if (!enabled) { + report_load(origin, NULL, "old-api-pinning-ignored"); + return 0; + } + + report_load(origin, NULL, "old-api-denied"); + return -EPERM; + } + + load_root = file->f_path.mnt->mnt_sb; + + /* First loaded module/firmware defines the root for all others. */ + spin_lock(&pinned_root_spinlock); + /* + * pinned_root is only NULL at startup. Otherwise, it is either + * a valid reference, or an ERR_PTR. + */ + if (!pinned_root) { + pinned_root = load_root; + /* + * Unlock now since it's only pinned_root we care about. + * In the worst case, we will (correctly) report pinning + * failures before we have announced that pinning is + * enabled. This would be purely cosmetic. + */ + spin_unlock(&pinned_root_spinlock); + check_pinning_enforcement(pinned_root); + report_load(origin, file, "pinned"); + } else { + spin_unlock(&pinned_root_spinlock); + } + + if (IS_ERR_OR_NULL(pinned_root) || load_root != pinned_root) { + if (unlikely(!enabled)) { + report_load(origin, file, "pinning-ignored"); + return 0; + } + + report_load(origin, file, "denied"); + return -EPERM; + } + + return 0; +} + +static struct security_hook_list loadpin_hooks[] = { + LSM_HOOK_INIT(sb_free_security, loadpin_sb_free_security), + LSM_HOOK_INIT(kernel_read_file, loadpin_read_file), +}; + +void __init loadpin_add_hooks(void) +{ + pr_info("ready to pin (currently %sabled)", enabled ? "en" : "dis"); + security_add_hooks(loadpin_hooks, ARRAY_SIZE(loadpin_hooks)); +} + +/* Should not be mutable after boot, so not listed in sysfs (perm == 0). */ +module_param(enabled, int, 0); +MODULE_PARM_DESC(enabled, "Pin module/firmware loading (default: true)"); diff --git a/security/security.c b/security/security.c index 554c3fb..e428608 100644 --- a/security/security.c +++ b/security/security.c @@ -60,6 +60,7 @@ int __init security_init(void) */ capability_add_hooks(); yama_add_hooks(); + loadpin_add_hooks(); /* * Load all the remaining security modules. -- cgit v0.10.2 From 8e4ff6f228e4722cac74db716e308d1da33d744f Mon Sep 17 00:00:00 2001 From: Stephen Smalley Date: Fri, 8 Apr 2016 13:52:00 -0400 Subject: selinux: distinguish non-init user namespace capability checks Distinguish capability checks against a target associated with the init user namespace versus capability checks against a target associated with a non-init user namespace by defining and using separate security classes for the latter. This is needed to support e.g. Chrome usage of user namespaces for the Chrome sandbox without needing to allow Chrome to also exercise capabilities on targets in the init user namespace. Suggested-by: Dan Walsh Signed-off-by: Stephen Smalley Signed-off-by: Paul Moore diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 474011c..bbff80c 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -1631,7 +1631,7 @@ static int current_has_perm(const struct task_struct *tsk, /* Check whether a task is allowed to use a capability. */ static int cred_has_capability(const struct cred *cred, - int cap, int audit) + int cap, int audit, bool initns) { struct common_audit_data ad; struct av_decision avd; @@ -1645,10 +1645,10 @@ static int cred_has_capability(const struct cred *cred, switch (CAP_TO_INDEX(cap)) { case 0: - sclass = SECCLASS_CAPABILITY; + sclass = initns ? SECCLASS_CAPABILITY : SECCLASS_CAP_USERNS; break; case 1: - sclass = SECCLASS_CAPABILITY2; + sclass = initns ? SECCLASS_CAPABILITY2 : SECCLASS_CAP2_USERNS; break; default: printk(KERN_ERR @@ -2152,7 +2152,7 @@ static int selinux_capset(struct cred *new, const struct cred *old, static int selinux_capable(const struct cred *cred, struct user_namespace *ns, int cap, int audit) { - return cred_has_capability(cred, cap, audit); + return cred_has_capability(cred, cap, audit, ns == &init_user_ns); } static int selinux_quotactl(int cmds, int type, int id, struct super_block *sb) @@ -2230,7 +2230,7 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages) int rc, cap_sys_admin = 0; rc = cred_has_capability(current_cred(), CAP_SYS_ADMIN, - SECURITY_CAP_NOAUDIT); + SECURITY_CAP_NOAUDIT, true); if (rc == 0) cap_sys_admin = 1; @@ -3213,7 +3213,7 @@ static int selinux_inode_getsecurity(struct inode *inode, const char *name, void SECURITY_CAP_NOAUDIT); if (!error) error = cred_has_capability(current_cred(), CAP_MAC_ADMIN, - SECURITY_CAP_NOAUDIT); + SECURITY_CAP_NOAUDIT, true); isec = inode_security(inode); if (!error) error = security_sid_to_context_force(isec->sid, &context, @@ -3390,7 +3390,7 @@ static int selinux_file_ioctl(struct file *file, unsigned int cmd, case KDSKBENT: case KDSKBSENT: error = cred_has_capability(cred, CAP_SYS_TTY_CONFIG, - SECURITY_CAP_AUDIT); + SECURITY_CAP_AUDIT, true); break; /* default case assumes that the command will go diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h index 8fbd138..1f1f4b2 100644 --- a/security/selinux/include/classmap.h +++ b/security/selinux/include/classmap.h @@ -12,6 +12,18 @@ #define COMMON_IPC_PERMS "create", "destroy", "getattr", "setattr", "read", \ "write", "associate", "unix_read", "unix_write" +#define COMMON_CAP_PERMS "chown", "dac_override", "dac_read_search", \ + "fowner", "fsetid", "kill", "setgid", "setuid", "setpcap", \ + "linux_immutable", "net_bind_service", "net_broadcast", \ + "net_admin", "net_raw", "ipc_lock", "ipc_owner", "sys_module", \ + "sys_rawio", "sys_chroot", "sys_ptrace", "sys_pacct", "sys_admin", \ + "sys_boot", "sys_nice", "sys_resource", "sys_time", \ + "sys_tty_config", "mknod", "lease", "audit_write", \ + "audit_control", "setfcap" + +#define COMMON_CAP2_PERMS "mac_override", "mac_admin", "syslog", \ + "wake_alarm", "block_suspend", "audit_read" + /* * Note: The name for any socket class should be suffixed by "socket", * and doesn't contain more than one substr of "socket". @@ -34,14 +46,7 @@ struct security_class_mapping secclass_map[] = { { "ipc_info", "syslog_read", "syslog_mod", "syslog_console", "module_request", "module_load", NULL } }, { "capability", - { "chown", "dac_override", "dac_read_search", - "fowner", "fsetid", "kill", "setgid", "setuid", "setpcap", - "linux_immutable", "net_bind_service", "net_broadcast", - "net_admin", "net_raw", "ipc_lock", "ipc_owner", "sys_module", - "sys_rawio", "sys_chroot", "sys_ptrace", "sys_pacct", "sys_admin", - "sys_boot", "sys_nice", "sys_resource", "sys_time", - "sys_tty_config", "mknod", "lease", "audit_write", - "audit_control", "setfcap", NULL } }, + { COMMON_CAP_PERMS, NULL } }, { "filesystem", { "mount", "remount", "unmount", "getattr", "relabelfrom", "relabelto", "associate", "quotamod", @@ -150,12 +155,15 @@ struct security_class_mapping secclass_map[] = { { "memprotect", { "mmap_zero", NULL } }, { "peer", { "recv", NULL } }, { "capability2", - { "mac_override", "mac_admin", "syslog", "wake_alarm", "block_suspend", - "audit_read", NULL } }, + { COMMON_CAP2_PERMS, NULL } }, { "kernel_service", { "use_as_override", "create_files_as", NULL } }, { "tun_socket", { COMMON_SOCK_PERMS, "attach_queue", NULL } }, { "binder", { "impersonate", "call", "set_context_mgr", "transfer", NULL } }, + { "cap_userns", + { COMMON_CAP_PERMS, NULL } }, + { "cap2_userns", + { COMMON_CAP2_PERMS, NULL } }, { NULL } }; -- cgit v0.10.2 From c2316dbf124257ae19fd2e29cb5ec51060649d38 Mon Sep 17 00:00:00 2001 From: Stephen Smalley Date: Fri, 8 Apr 2016 13:55:03 -0400 Subject: selinux: apply execstack check on thread stacks The execstack check was only being applied on the main process stack. Thread stacks allocated via mmap were only subject to the execmem permission check. Augment the check to apply to the current thread stack as well. Note that this does NOT prevent making a different thread's stack executable. Suggested-by: Nick Kralevich Acked-by: Nick Kralevich Signed-off-by: Stephen Smalley Signed-off-by: Paul Moore diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index bbff80c..a00ab81 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3479,8 +3479,9 @@ static int selinux_file_mprotect(struct vm_area_struct *vma, vma->vm_end <= vma->vm_mm->brk) { rc = cred_has_perm(cred, cred, PROCESS__EXECHEAP); } else if (!vma->vm_file && - vma->vm_start <= vma->vm_mm->start_stack && - vma->vm_end >= vma->vm_mm->start_stack) { + ((vma->vm_start <= vma->vm_mm->start_stack && + vma->vm_end >= vma->vm_mm->start_stack) || + vma_is_stack_for_task(vma, current))) { rc = current_has_perm(current, PROCESS__EXECSTACK); } else if (vma->vm_file && vma->anon_vma) { /* -- cgit v0.10.2 From da20dfe6b50ea4c1a82797b7ee8655a370535d73 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 21 Apr 2016 12:53:29 -0700 Subject: fs: fix over-zealous use of "const" When I was fixing up const recommendations from checkpatch.pl, I went overboard. This fixes the warning (during a W=1 build): include/linux/fs.h:2627:74: warning: type qualifiers ignored on function return type [-Wignored-qualifiers] static inline const char * const kernel_read_file_id_str(enum kernel_read_file_id id) Reported-by: Andy Shevchenko Signed-off-by: Kees Cook Signed-off-by: James Morris diff --git a/include/linux/fs.h b/include/linux/fs.h index 9047755..9847d5c 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2600,7 +2600,7 @@ static const char * const kernel_read_file_str[] = { __kernel_read_file_id(__fid_stringify) }; -static inline const char * const kernel_read_file_id_str(enum kernel_read_file_id id) +static inline const char *kernel_read_file_id_str(enum kernel_read_file_id id) { if (id < 0 || id >= READING_MAX_ID) return kernel_read_file_str[READING_UNKNOWN]; -- cgit v0.10.2 From 39d637af5aa7577f655c58b9e55587566c63a0af Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Sun, 26 Oct 2014 12:42:07 +0200 Subject: vfs: forbid write access when reading a file into memory This patch is based on top of the "vfs: support for a common kernel file loader" patch set. In general when the kernel is reading a file into memory it does not want anything else writing to it. The kernel currently only forbids write access to a file being executed. This patch extends this locking to files being read by the kernel. Changelog: - moved function to kernel_read_file() - Mimi - updated patch description - Mimi Signed-off-by: Dmitry Kasatkin Cc: Al Viro Signed-off-by: Mimi Zohar Reviewed-by: Luis R. Rodriguez Acked-by: Kees Cook diff --git a/fs/exec.c b/fs/exec.c index c4010b8..fdecb76 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -850,15 +850,25 @@ int kernel_read_file(struct file *file, void **buf, loff_t *size, if (ret) return ret; + ret = deny_write_access(file); + if (ret) + return ret; + i_size = i_size_read(file_inode(file)); - if (max_size > 0 && i_size > max_size) - return -EFBIG; - if (i_size <= 0) - return -EINVAL; + if (max_size > 0 && i_size > max_size) { + ret = -EFBIG; + goto out; + } + if (i_size <= 0) { + ret = -EINVAL; + goto out; + } *buf = vmalloc(i_size); - if (!*buf) - return -ENOMEM; + if (!*buf) { + ret = -ENOMEM; + goto out; + } pos = 0; while (pos < i_size) { @@ -876,18 +886,21 @@ int kernel_read_file(struct file *file, void **buf, loff_t *size, if (pos != i_size) { ret = -EIO; - goto out; + goto out_free; } ret = security_kernel_post_read_file(file, *buf, i_size, id); if (!ret) *size = pos; -out: +out_free: if (ret < 0) { vfree(*buf); *buf = NULL; } + +out: + allow_write_access(file); return ret; } EXPORT_SYMBOL_GPL(kernel_read_file); -- cgit v0.10.2 From 42a4c603198f0d45b7aa936d3ac6ba1b8bd14a1b Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Mon, 29 Feb 2016 08:30:12 -0500 Subject: ima: fix ima_inode_post_setattr Changing file metadata (eg. uid, guid) could result in having to re-appraise a file's integrity, but does not change the "new file" status nor the security.ima xattr. The IMA_PERMIT_DIRECTIO and IMA_DIGSIG_REQUIRED flags are policy rule specific. This patch only resets these flags, not the IMA_NEW_FILE or IMA_DIGSIG flags. With this patch, changing the file timestamp will not remove the file signature on new files. Reported-by: Dmitry Rozhkov Signed-off-by: Mimi Zohar Tested-by: Dmitry Rozhkov diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 6b4694a..d2f28a0 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -328,7 +328,7 @@ void ima_inode_post_setattr(struct dentry *dentry) if (iint) { iint->flags &= ~(IMA_APPRAISE | IMA_APPRAISED | IMA_APPRAISE_SUBMASK | IMA_APPRAISED_SUBMASK | - IMA_ACTION_FLAGS); + IMA_ACTION_RULE_FLAGS); if (must_appraise) iint->flags |= IMA_APPRAISE; } diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index e08935c..90bc57d 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -28,6 +28,7 @@ /* iint cache flags */ #define IMA_ACTION_FLAGS 0xff000000 +#define IMA_ACTION_RULE_FLAGS 0x06000000 #define IMA_DIGSIG 0x01000000 #define IMA_DIGSIG_REQUIRED 0x02000000 #define IMA_PERMIT_DIRECTIO 0x04000000 -- cgit v0.10.2 From 05d1a717ec0430c916a749b94eb90ab74bbfa356 Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Mon, 29 Feb 2016 19:52:05 -0500 Subject: ima: add support for creating files using the mknodat syscall Commit 3034a14 "ima: pass 'opened' flag to identify newly created files" stopped identifying empty files as new files. However new empty files can be created using the mknodat syscall. On systems with IMA-appraisal enabled, these empty files are not labeled with security.ima extended attributes properly, preventing them from subsequently being opened in order to write the file data contents. This patch defines a new hook named ima_post_path_mknod() to mark these empty files, created using mknodat, as new in order to allow the file data contents to be written. In addition, files with security.ima xattrs containing a file signature are considered "immutable" and can not be modified. The file contents need to be written, before signing the file. This patch relaxes this requirement for new files, allowing the file signature to be written before the file contents. Changelog: - defer identifying files with signatures stored as security.ima (based on Dmitry Rozhkov's comments) - removing tests (eg. dentry, dentry->d_inode, inode->i_size == 0) (based on Al's review) Signed-off-by: Mimi Zohar Cc: Al Viro < Tested-by: Dmitry Rozhkov diff --git a/fs/namei.c b/fs/namei.c index 1d9ca2d..b4bd068 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3608,6 +3608,8 @@ retry: switch (mode & S_IFMT) { case 0: case S_IFREG: error = vfs_create(path.dentry->d_inode,dentry,mode,true); + if (!error) + ima_post_path_mknod(dentry); break; case S_IFCHR: case S_IFBLK: error = vfs_mknod(path.dentry->d_inode,dentry,mode, diff --git a/include/linux/ima.h b/include/linux/ima.h index e6516cb..0eb7c2e 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -21,6 +21,7 @@ extern int ima_file_mmap(struct file *file, unsigned long prot); extern int ima_read_file(struct file *file, enum kernel_read_file_id id); extern int ima_post_read_file(struct file *file, void *buf, loff_t size, enum kernel_read_file_id id); +extern void ima_post_path_mknod(struct dentry *dentry); #else static inline int ima_bprm_check(struct linux_binprm *bprm) @@ -54,6 +55,11 @@ static inline int ima_post_read_file(struct file *file, void *buf, loff_t size, return 0; } +static inline void ima_post_path_mknod(struct dentry *dentry) +{ + return; +} + #endif /* CONFIG_IMA */ #ifdef CONFIG_IMA_APPRAISE diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index d2f28a0..1bcbc12 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -275,6 +275,11 @@ out: xattr_value->type != EVM_IMA_XATTR_DIGSIG)) { if (!ima_fix_xattr(dentry, iint)) status = INTEGRITY_PASS; + } else if ((inode->i_size == 0) && + (iint->flags & IMA_NEW_FILE) && + (xattr_value && + xattr_value->type == EVM_IMA_XATTR_DIGSIG)) { + status = INTEGRITY_PASS; } integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, filename, op, cause, rc, 0); diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 391f417..68b26c3 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -246,7 +246,8 @@ static int process_measurement(struct file *file, char *buf, loff_t size, ima_audit_measurement(iint, pathname); out_digsig: - if ((mask & MAY_WRITE) && (iint->flags & IMA_DIGSIG)) + if ((mask & MAY_WRITE) && (iint->flags & IMA_DIGSIG) && + !(iint->flags & IMA_NEW_FILE)) rc = -EACCES; kfree(xattr_value); out_free: @@ -316,6 +317,28 @@ int ima_file_check(struct file *file, int mask, int opened) EXPORT_SYMBOL_GPL(ima_file_check); /** + * ima_post_path_mknod - mark as a new inode + * @dentry: newly created dentry + * + * Mark files created via the mknodat syscall as new, so that the + * file data can be written later. + */ +void ima_post_path_mknod(struct dentry *dentry) +{ + struct integrity_iint_cache *iint; + struct inode *inode = dentry->d_inode; + int must_appraise; + + must_appraise = ima_must_appraise(inode, MAY_ACCESS, FILE_CHECK); + if (!must_appraise) + return; + + iint = integrity_inode_get(inode); + if (iint) + iint->flags |= IMA_NEW_FILE; +} + +/** * ima_read_file - pre-measure/appraise hook decision based on policy * @file: pointer to the file to be measured/appraised/audit * @read_id: caller identifier -- cgit v0.10.2 From 470bf1f27a1472264d18c84b324389509f0e30b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Sala=C3=BCn?= Date: Thu, 24 Mar 2016 02:46:33 +0100 Subject: seccomp: Fix comment typo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drop accidentally repeated word in comment. Signed-off-by: Mickaël Salaün Cc: Kees Cook Cc: Andy Lutomirski Cc: Will Drewry diff --git a/kernel/seccomp.c b/kernel/seccomp.c index e1e5a35..6c9bb62 100644 --- a/kernel/seccomp.c +++ b/kernel/seccomp.c @@ -915,7 +915,7 @@ long seccomp_get_filter(struct task_struct *task, unsigned long filter_off, fprog = filter->prog->orig_prog; if (!fprog) { - /* This must be a new non-cBPF filter, since we save every + /* This must be a new non-cBPF filter, since we save * every cBPF filter's orig_prog above when * CONFIG_CHECKPOINT_RESTORE is enabled. */ -- cgit v0.10.2 From 74f430cd0fdee1bdfb25708ee1e52fc860535a89 Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Wed, 4 May 2016 10:18:15 -0400 Subject: Yama: use atomic allocations when reporting Access reporting often happens from atomic contexes. Avoid lockups when allocating memory for command lines. Fixes: 8a56038c2ae ("Yama: consolidate error reporting") Signed-off-by: Sasha Levin diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c index c19f6e5..9b756b1 100644 --- a/security/yama/yama_lsm.c +++ b/security/yama/yama_lsm.c @@ -47,8 +47,8 @@ static void report_access(const char *access, struct task_struct *target, { char *target_cmd, *agent_cmd; - target_cmd = kstrdup_quotable_cmdline(target, GFP_KERNEL); - agent_cmd = kstrdup_quotable_cmdline(agent, GFP_KERNEL); + target_cmd = kstrdup_quotable_cmdline(target, GFP_ATOMIC); + agent_cmd = kstrdup_quotable_cmdline(agent, GFP_ATOMIC); pr_notice_ratelimited( "ptrace %s of \"%s\"[%d] was attempted by \"%s\"[%d]\n", -- cgit v0.10.2 From b937190c40de0f6f07f592042e3097b16c6b0130 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Tue, 17 May 2016 01:45:52 -0700 Subject: LSM: LoadPin: provide enablement CONFIG Instead of being enabled by default when SECURITY_LOADPIN is selected, provide an additional (default off) config to determine the boot time behavior. As before, the "loadpin.enabled=0/1" kernel parameter remains available. Suggested-by: James Morris Signed-off-by: Kees Cook Signed-off-by: James Morris diff --git a/security/loadpin/Kconfig b/security/loadpin/Kconfig index c668ac4..dd01aa9 100644 --- a/security/loadpin/Kconfig +++ b/security/loadpin/Kconfig @@ -3,8 +3,17 @@ config SECURITY_LOADPIN depends on SECURITY && BLOCK help Any files read through the kernel file reading interface - (kernel modules, firmware, kexec images, security policy) will - be pinned to the first filesystem used for loading. Any files - that come from other filesystems will be rejected. This is best - used on systems without an initrd that have a root filesystem - backed by a read-only device such as dm-verity or a CDROM. + (kernel modules, firmware, kexec images, security policy) + can be pinned to the first filesystem used for loading. When + enabled, any files that come from other filesystems will be + rejected. This is best used on systems without an initrd that + have a root filesystem backed by a read-only device such as + dm-verity or a CDROM. + +config SECURITY_LOADPIN_ENABLED + bool "Enforce LoadPin at boot" + depends on SECURITY_LOADPIN + help + If selected, LoadPin will enforce pinning at boot. If not + selected, it can be enabled at boot with the kernel parameter + "loadpin.enabled=1". diff --git a/security/loadpin/loadpin.c b/security/loadpin/loadpin.c index e4debae..89a46f1 100644 --- a/security/loadpin/loadpin.c +++ b/security/loadpin/loadpin.c @@ -45,7 +45,7 @@ static void report_load(const char *origin, struct file *file, char *operation) kfree(pathname); } -static int enabled = 1; +static int enabled = IS_ENABLED(CONFIG_SECURITY_LOADPIN_ENABLED); static struct super_block *pinned_root; static DEFINE_SPINLOCK(pinned_root_spinlock); -- cgit v0.10.2