Merge tag 'keys-next-20140722' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs into next
This commit is contained in:
commit
4ca332e11d
|
@ -566,6 +566,11 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
|
|||
possible to determine what the correct size should be.
|
||||
This option provides an override for these situations.
|
||||
|
||||
ca_keys= [KEYS] This parameter identifies a specific key(s) on
|
||||
the system trusted keyring to be used for certificate
|
||||
trust validation.
|
||||
format: { id:<keyid> | builtin }
|
||||
|
||||
ccw_timeout_log [S390]
|
||||
See Documentation/s390/CommonIO for details.
|
||||
|
||||
|
|
|
@ -1150,20 +1150,24 @@ The structure has a number of fields, some of which are mandatory:
|
|||
const void *data;
|
||||
size_t datalen;
|
||||
size_t quotalen;
|
||||
time_t expiry;
|
||||
};
|
||||
|
||||
Before calling the method, the caller will fill in data and datalen with
|
||||
the payload blob parameters; quotalen will be filled in with the default
|
||||
quota size from the key type and the rest will be cleared.
|
||||
quota size from the key type; expiry will be set to TIME_T_MAX and the
|
||||
rest will be cleared.
|
||||
|
||||
If a description can be proposed from the payload contents, that should be
|
||||
attached as a string to the description field. This will be used for the
|
||||
key description if the caller of add_key() passes NULL or "".
|
||||
|
||||
The method can attach anything it likes to type_data[] and payload. These
|
||||
are merely passed along to the instantiate() or update() operations.
|
||||
are merely passed along to the instantiate() or update() operations. If
|
||||
set, the expiry time will be applied to the key if it is instantiated from
|
||||
this data.
|
||||
|
||||
The method should return 0 if success ful or a negative error code
|
||||
The method should return 0 if successful or a negative error code
|
||||
otherwise.
|
||||
|
||||
|
||||
|
@ -1172,7 +1176,9 @@ The structure has a number of fields, some of which are mandatory:
|
|||
This method is only required if the preparse() method is provided,
|
||||
otherwise it is unused. It cleans up anything attached to the
|
||||
description, type_data and payload fields of the key_preparsed_payload
|
||||
struct as filled in by the preparse() method.
|
||||
struct as filled in by the preparse() method. It will always be called
|
||||
after preparse() returns successfully, even if instantiate() or update()
|
||||
succeed.
|
||||
|
||||
|
||||
(*) int (*instantiate)(struct key *key, struct key_preparsed_payload *prep);
|
||||
|
|
|
@ -22,7 +22,6 @@ config ASYMMETRIC_PUBLIC_KEY_SUBTYPE
|
|||
|
||||
config PUBLIC_KEY_ALGO_RSA
|
||||
tristate "RSA public-key algorithm"
|
||||
select MPILIB_EXTRA
|
||||
select MPILIB
|
||||
help
|
||||
This option enables support for the RSA algorithm (PKCS#1, RFC3447).
|
||||
|
@ -33,8 +32,39 @@ config X509_CERTIFICATE_PARSER
|
|||
select ASN1
|
||||
select OID_REGISTRY
|
||||
help
|
||||
This option procides support for parsing X.509 format blobs for key
|
||||
This option provides support for parsing X.509 format blobs for key
|
||||
data and provides the ability to instantiate a crypto key from a
|
||||
public key packet found inside the certificate.
|
||||
|
||||
config PKCS7_MESSAGE_PARSER
|
||||
tristate "PKCS#7 message parser"
|
||||
depends on X509_CERTIFICATE_PARSER
|
||||
select ASN1
|
||||
select OID_REGISTRY
|
||||
help
|
||||
This option provides support for parsing PKCS#7 format messages for
|
||||
signature data and provides the ability to verify the signature.
|
||||
|
||||
config PKCS7_TEST_KEY
|
||||
tristate "PKCS#7 testing key type"
|
||||
depends on PKCS7_MESSAGE_PARSER
|
||||
select SYSTEM_TRUSTED_KEYRING
|
||||
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
|
||||
it is, the PKCS#7 wrapper is discarded and reading the key returns
|
||||
just the payload. If it isn't, adding the key will fail with an
|
||||
error.
|
||||
|
||||
This is intended for testing the PKCS#7 parser.
|
||||
|
||||
config SIGNED_PE_FILE_VERIFICATION
|
||||
bool "Support for PE file signature verification"
|
||||
depends on PKCS7_MESSAGE_PARSER=y
|
||||
select ASN1
|
||||
select OID_REGISTRY
|
||||
help
|
||||
This option provides support for verifying the signature(s) on a
|
||||
signed PE binary.
|
||||
|
||||
endif # ASYMMETRIC_KEY_TYPE
|
||||
|
|
|
@ -25,3 +25,40 @@ $(obj)/x509_rsakey-asn1.o: $(obj)/x509_rsakey-asn1.c $(obj)/x509_rsakey-asn1.h
|
|||
|
||||
clean-files += x509-asn1.c x509-asn1.h
|
||||
clean-files += x509_rsakey-asn1.c x509_rsakey-asn1.h
|
||||
|
||||
#
|
||||
# PKCS#7 message handling
|
||||
#
|
||||
obj-$(CONFIG_PKCS7_MESSAGE_PARSER) += pkcs7_message.o
|
||||
pkcs7_message-y := \
|
||||
pkcs7-asn1.o \
|
||||
pkcs7_parser.o \
|
||||
pkcs7_trust.o \
|
||||
pkcs7_verify.o
|
||||
|
||||
$(obj)/pkcs7_parser.o: $(obj)/pkcs7-asn1.h
|
||||
$(obj)/pkcs7-asn1.o: $(obj)/pkcs7-asn1.c $(obj)/pkcs7-asn1.h
|
||||
|
||||
clean-files += pkcs7-asn1.c pkcs7-asn1.h
|
||||
|
||||
#
|
||||
# PKCS#7 parser testing key
|
||||
#
|
||||
obj-$(CONFIG_PKCS7_TEST_KEY) += pkcs7_test_key.o
|
||||
pkcs7_test_key-y := \
|
||||
pkcs7_key_type.o
|
||||
|
||||
#
|
||||
# Signed PE binary-wrapped key handling
|
||||
#
|
||||
obj-$(CONFIG_SIGNED_PE_FILE_VERIFICATION) += verify_signed_pefile.o
|
||||
|
||||
verify_signed_pefile-y := \
|
||||
verify_pefile.o \
|
||||
mscode_parser.o \
|
||||
mscode-asn1.o
|
||||
|
||||
$(obj)/mscode_parser.o: $(obj)/mscode-asn1.h $(obj)/mscode-asn1.h
|
||||
$(obj)/mscode-asn1.o: $(obj)/mscode-asn1.c $(obj)/mscode-asn1.h
|
||||
|
||||
clean-files += mscode-asn1.c mscode-asn1.h
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
* 2 of the Licence, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
int asymmetric_keyid_match(const char *kid, const char *id);
|
||||
|
||||
static inline const char *asymmetric_key_id(const struct key *key)
|
||||
{
|
||||
return key->type_data.p[1];
|
||||
|
|
|
@ -22,6 +22,35 @@ MODULE_LICENSE("GPL");
|
|||
static LIST_HEAD(asymmetric_key_parsers);
|
||||
static DECLARE_RWSEM(asymmetric_key_parsers_sem);
|
||||
|
||||
/*
|
||||
* Match asymmetric key id with partial match
|
||||
* @id: key id to match in a form "id:<id>"
|
||||
*/
|
||||
int asymmetric_keyid_match(const char *kid, const char *id)
|
||||
{
|
||||
size_t idlen, kidlen;
|
||||
|
||||
if (!kid || !id)
|
||||
return 0;
|
||||
|
||||
/* make it possible to use id as in the request: "id:<id>" */
|
||||
if (strncmp(id, "id:", 3) == 0)
|
||||
id += 3;
|
||||
|
||||
/* Anything after here requires a partial match on the ID string */
|
||||
idlen = strlen(id);
|
||||
kidlen = strlen(kid);
|
||||
if (idlen > kidlen)
|
||||
return 0;
|
||||
|
||||
kid += kidlen - idlen;
|
||||
if (strcasecmp(id, kid) != 0)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(asymmetric_keyid_match);
|
||||
|
||||
/*
|
||||
* Match asymmetric keys on (part of) their name
|
||||
* We have some shorthand methods for matching keys. We allow:
|
||||
|
@ -34,9 +63,8 @@ static int asymmetric_key_match(const struct key *key, const void *description)
|
|||
{
|
||||
const struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key);
|
||||
const char *spec = description;
|
||||
const char *id, *kid;
|
||||
const char *id;
|
||||
ptrdiff_t speclen;
|
||||
size_t idlen, kidlen;
|
||||
|
||||
if (!subtype || !spec || !*spec)
|
||||
return 0;
|
||||
|
@ -55,23 +83,8 @@ static int asymmetric_key_match(const struct key *key, const void *description)
|
|||
speclen = id - spec;
|
||||
id++;
|
||||
|
||||
/* Anything after here requires a partial match on the ID string */
|
||||
kid = asymmetric_key_id(key);
|
||||
if (!kid)
|
||||
return 0;
|
||||
|
||||
idlen = strlen(id);
|
||||
kidlen = strlen(kid);
|
||||
if (idlen > kidlen)
|
||||
return 0;
|
||||
|
||||
kid += kidlen - idlen;
|
||||
if (strcasecmp(id, kid) != 0)
|
||||
return 0;
|
||||
|
||||
if (speclen == 2 &&
|
||||
memcmp(spec, "id", 2) == 0)
|
||||
return 1;
|
||||
if (speclen == 2 && memcmp(spec, "id", 2) == 0)
|
||||
return asymmetric_keyid_match(asymmetric_key_id(key), id);
|
||||
|
||||
if (speclen == subtype->name_len &&
|
||||
memcmp(spec, subtype->name, speclen) == 0)
|
||||
|
@ -156,36 +169,13 @@ static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep)
|
|||
pr_devel("==>%s()\n", __func__);
|
||||
|
||||
if (subtype) {
|
||||
subtype->destroy(prep->payload);
|
||||
subtype->destroy(prep->payload[0]);
|
||||
module_put(subtype->owner);
|
||||
}
|
||||
kfree(prep->type_data[1]);
|
||||
kfree(prep->description);
|
||||
}
|
||||
|
||||
/*
|
||||
* Instantiate a asymmetric_key defined key. The key was preparsed, so we just
|
||||
* have to transfer the data here.
|
||||
*/
|
||||
static int asymmetric_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
|
||||
{
|
||||
int ret;
|
||||
|
||||
pr_devel("==>%s()\n", __func__);
|
||||
|
||||
ret = key_payload_reserve(key, prep->quotalen);
|
||||
if (ret == 0) {
|
||||
key->type_data.p[0] = prep->type_data[0];
|
||||
key->type_data.p[1] = prep->type_data[1];
|
||||
key->payload.data = prep->payload;
|
||||
prep->type_data[0] = NULL;
|
||||
prep->type_data[1] = NULL;
|
||||
prep->payload = NULL;
|
||||
}
|
||||
pr_devel("<==%s() = %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* dispose of the data dangling from the corpse of a asymmetric key
|
||||
*/
|
||||
|
@ -205,7 +195,7 @@ struct key_type key_type_asymmetric = {
|
|||
.name = "asymmetric",
|
||||
.preparse = asymmetric_key_preparse,
|
||||
.free_preparse = asymmetric_key_free_preparse,
|
||||
.instantiate = asymmetric_key_instantiate,
|
||||
.instantiate = generic_key_instantiate,
|
||||
.match = asymmetric_key_match,
|
||||
.destroy = asymmetric_key_destroy,
|
||||
.describe = asymmetric_key_describe,
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
--- Microsoft individual code signing data blob parser
|
||||
---
|
||||
--- 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.
|
||||
---
|
||||
|
||||
MSCode ::= SEQUENCE {
|
||||
type SEQUENCE {
|
||||
contentType ContentType,
|
||||
parameters ANY
|
||||
},
|
||||
content SEQUENCE {
|
||||
digestAlgorithm DigestAlgorithmIdentifier,
|
||||
digest OCTET STRING ({ mscode_note_digest })
|
||||
}
|
||||
}
|
||||
|
||||
ContentType ::= OBJECT IDENTIFIER ({ mscode_note_content_type })
|
||||
|
||||
DigestAlgorithmIdentifier ::= SEQUENCE {
|
||||
algorithm OBJECT IDENTIFIER ({ mscode_note_digest_algo }),
|
||||
parameters ANY OPTIONAL
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
/* Parse a Microsoft Individual Code Signing blob
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "MSCODE: "fmt
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/oid_registry.h>
|
||||
#include <crypto/pkcs7.h>
|
||||
#include "verify_pefile.h"
|
||||
#include "mscode-asn1.h"
|
||||
|
||||
/*
|
||||
* Parse a Microsoft Individual Code Signing blob
|
||||
*/
|
||||
int mscode_parse(struct pefile_context *ctx)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
pr_devel("Data: %zu [%*ph]\n", data_len, (unsigned)(data_len),
|
||||
content_data);
|
||||
|
||||
return asn1_ber_decoder(&mscode_decoder, ctx, content_data, data_len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check the content type OID
|
||||
*/
|
||||
int mscode_note_content_type(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
enum OID oid;
|
||||
|
||||
oid = look_up_OID(value, vlen);
|
||||
if (oid == OID__NR) {
|
||||
char buffer[50];
|
||||
|
||||
sprint_oid(value, vlen, buffer, sizeof(buffer));
|
||||
pr_err("Unknown OID: %s\n", buffer);
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
/*
|
||||
* pesign utility had a bug where it was putting
|
||||
* OID_msIndividualSPKeyPurpose instead of OID_msPeImageDataObjId
|
||||
* So allow both OIDs.
|
||||
*/
|
||||
if (oid != OID_msPeImageDataObjId &&
|
||||
oid != OID_msIndividualSPKeyPurpose) {
|
||||
pr_err("Unexpected content type OID %u\n", oid);
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note the digest algorithm OID
|
||||
*/
|
||||
int mscode_note_digest_algo(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pefile_context *ctx = context;
|
||||
char buffer[50];
|
||||
enum OID oid;
|
||||
|
||||
oid = look_up_OID(value, vlen);
|
||||
switch (oid) {
|
||||
case OID_md4:
|
||||
ctx->digest_algo = HASH_ALGO_MD4;
|
||||
break;
|
||||
case OID_md5:
|
||||
ctx->digest_algo = HASH_ALGO_MD5;
|
||||
break;
|
||||
case OID_sha1:
|
||||
ctx->digest_algo = HASH_ALGO_SHA1;
|
||||
break;
|
||||
case OID_sha256:
|
||||
ctx->digest_algo = HASH_ALGO_SHA256;
|
||||
break;
|
||||
|
||||
case OID__NR:
|
||||
sprint_oid(value, vlen, buffer, sizeof(buffer));
|
||||
pr_err("Unknown OID: %s\n", buffer);
|
||||
return -EBADMSG;
|
||||
|
||||
default:
|
||||
pr_err("Unsupported content type: %u\n", oid);
|
||||
return -ENOPKG;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note the digest we're guaranteeing with this certificate
|
||||
*/
|
||||
int mscode_note_digest(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pefile_context *ctx = context;
|
||||
|
||||
ctx->digest = value;
|
||||
ctx->digest_len = vlen;
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
PKCS7ContentInfo ::= SEQUENCE {
|
||||
contentType ContentType,
|
||||
content [0] EXPLICIT SignedData OPTIONAL
|
||||
}
|
||||
|
||||
ContentType ::= OBJECT IDENTIFIER ({ pkcs7_note_OID })
|
||||
|
||||
SignedData ::= SEQUENCE {
|
||||
version INTEGER,
|
||||
digestAlgorithms DigestAlgorithmIdentifiers,
|
||||
contentInfo ContentInfo,
|
||||
certificates CHOICE {
|
||||
certSet [0] IMPLICIT ExtendedCertificatesAndCertificates,
|
||||
certSequence [2] IMPLICIT Certificates
|
||||
} OPTIONAL ({ pkcs7_note_certificate_list }),
|
||||
crls CHOICE {
|
||||
crlSet [1] IMPLICIT CertificateRevocationLists,
|
||||
crlSequence [3] IMPLICIT CRLSequence
|
||||
} OPTIONAL,
|
||||
signerInfos SignerInfos
|
||||
}
|
||||
|
||||
ContentInfo ::= SEQUENCE {
|
||||
contentType ContentType,
|
||||
content [0] EXPLICIT Data OPTIONAL
|
||||
}
|
||||
|
||||
Data ::= ANY ({ pkcs7_note_data })
|
||||
|
||||
DigestAlgorithmIdentifiers ::= CHOICE {
|
||||
daSet SET OF DigestAlgorithmIdentifier,
|
||||
daSequence SEQUENCE OF DigestAlgorithmIdentifier
|
||||
}
|
||||
|
||||
DigestAlgorithmIdentifier ::= SEQUENCE {
|
||||
algorithm OBJECT IDENTIFIER ({ pkcs7_note_OID }),
|
||||
parameters ANY OPTIONAL
|
||||
}
|
||||
|
||||
--
|
||||
-- Certificates and certificate lists
|
||||
--
|
||||
ExtendedCertificatesAndCertificates ::= SET OF ExtendedCertificateOrCertificate
|
||||
|
||||
ExtendedCertificateOrCertificate ::= CHOICE {
|
||||
certificate Certificate, -- X.509
|
||||
extendedCertificate [0] IMPLICIT ExtendedCertificate -- PKCS#6
|
||||
}
|
||||
|
||||
ExtendedCertificate ::= Certificate -- cheating
|
||||
|
||||
Certificates ::= SEQUENCE OF Certificate
|
||||
|
||||
CertificateRevocationLists ::= SET OF CertificateList
|
||||
|
||||
CertificateList ::= SEQUENCE OF Certificate -- This may be defined incorrectly
|
||||
|
||||
CRLSequence ::= SEQUENCE OF CertificateList
|
||||
|
||||
Certificate ::= ANY ({ pkcs7_extract_cert }) -- X.509
|
||||
|
||||
--
|
||||
-- Signer information
|
||||
--
|
||||
SignerInfos ::= CHOICE {
|
||||
siSet SET OF SignerInfo,
|
||||
siSequence SEQUENCE OF SignerInfo
|
||||
}
|
||||
|
||||
SignerInfo ::= SEQUENCE {
|
||||
version INTEGER,
|
||||
issuerAndSerialNumber IssuerAndSerialNumber,
|
||||
digestAlgorithm DigestAlgorithmIdentifier ({ pkcs7_sig_note_digest_algo }),
|
||||
authenticatedAttributes CHOICE {
|
||||
aaSet [0] IMPLICIT SetOfAuthenticatedAttribute
|
||||
({ pkcs7_sig_note_set_of_authattrs }),
|
||||
aaSequence [2] EXPLICIT SEQUENCE OF AuthenticatedAttribute
|
||||
-- Explicit because easier to compute digest on
|
||||
-- sequence of attributes and then reuse encoded
|
||||
-- sequence in aaSequence.
|
||||
} OPTIONAL,
|
||||
digestEncryptionAlgorithm
|
||||
DigestEncryptionAlgorithmIdentifier ({ pkcs7_sig_note_pkey_algo }),
|
||||
encryptedDigest EncryptedDigest,
|
||||
unauthenticatedAttributes CHOICE {
|
||||
uaSet [1] IMPLICIT SET OF UnauthenticatedAttribute,
|
||||
uaSequence [3] IMPLICIT SEQUENCE OF UnauthenticatedAttribute
|
||||
} OPTIONAL
|
||||
} ({ pkcs7_note_signed_info })
|
||||
|
||||
IssuerAndSerialNumber ::= SEQUENCE {
|
||||
issuer Name ({ pkcs7_sig_note_issuer }),
|
||||
serialNumber CertificateSerialNumber ({ pkcs7_sig_note_serial })
|
||||
}
|
||||
|
||||
CertificateSerialNumber ::= INTEGER
|
||||
|
||||
SetOfAuthenticatedAttribute ::= SET OF AuthenticatedAttribute
|
||||
|
||||
AuthenticatedAttribute ::= SEQUENCE {
|
||||
type OBJECT IDENTIFIER ({ pkcs7_note_OID }),
|
||||
values SET OF ANY ({ pkcs7_sig_note_authenticated_attr })
|
||||
}
|
||||
|
||||
UnauthenticatedAttribute ::= SEQUENCE {
|
||||
type OBJECT IDENTIFIER ({ pkcs7_note_OID }),
|
||||
values SET OF ANY
|
||||
}
|
||||
|
||||
DigestEncryptionAlgorithmIdentifier ::= SEQUENCE {
|
||||
algorithm OBJECT IDENTIFIER ({ pkcs7_note_OID }),
|
||||
parameters ANY OPTIONAL
|
||||
}
|
||||
|
||||
EncryptedDigest ::= OCTET STRING ({ pkcs7_sig_note_signature })
|
||||
|
||||
---
|
||||
--- X.500 Name
|
||||
---
|
||||
Name ::= SEQUENCE OF RelativeDistinguishedName
|
||||
|
||||
RelativeDistinguishedName ::= SET OF AttributeValueAssertion
|
||||
|
||||
AttributeValueAssertion ::= SEQUENCE {
|
||||
attributeType OBJECT IDENTIFIER ({ pkcs7_note_OID }),
|
||||
attributeValue ANY
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
/* Testing module to load key from trusted PKCS#7 message
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "PKCS7key: "fmt
|
||||
#include <linux/key.h>
|
||||
#include <linux/key-type.h>
|
||||
#include <crypto/pkcs7.h>
|
||||
#include <keys/user-type.h>
|
||||
#include <keys/system_keyring.h>
|
||||
#include "pkcs7_parser.h"
|
||||
|
||||
/*
|
||||
* Preparse a PKCS#7 wrapped and validated data blob.
|
||||
*/
|
||||
static int pkcs7_preparse(struct key_preparsed_payload *prep)
|
||||
{
|
||||
struct pkcs7_message *pkcs7;
|
||||
const void *data, *saved_prep_data;
|
||||
size_t datalen, saved_prep_datalen;
|
||||
bool trusted;
|
||||
int ret;
|
||||
|
||||
kenter("");
|
||||
|
||||
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);
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* user defined keys take an arbitrary string as the description and an
|
||||
* arbitrary blob of data as the payload
|
||||
*/
|
||||
struct key_type key_type_pkcs7 = {
|
||||
.name = "pkcs7_test",
|
||||
.def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
|
||||
.preparse = pkcs7_preparse,
|
||||
.free_preparse = user_free_preparse,
|
||||
.instantiate = generic_key_instantiate,
|
||||
.match = user_match,
|
||||
.revoke = user_revoke,
|
||||
.destroy = user_destroy,
|
||||
.describe = user_describe,
|
||||
.read = user_read,
|
||||
};
|
||||
|
||||
/*
|
||||
* Module stuff
|
||||
*/
|
||||
static int __init pkcs7_key_init(void)
|
||||
{
|
||||
return register_key_type(&key_type_pkcs7);
|
||||
}
|
||||
|
||||
static void __exit pkcs7_key_cleanup(void)
|
||||
{
|
||||
unregister_key_type(&key_type_pkcs7);
|
||||
}
|
||||
|
||||
module_init(pkcs7_key_init);
|
||||
module_exit(pkcs7_key_cleanup);
|
|
@ -0,0 +1,396 @@
|
|||
/* PKCS#7 parser
|
||||
*
|
||||
* 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) "PKCS7: "fmt
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/oid_registry.h>
|
||||
#include "public_key.h"
|
||||
#include "pkcs7_parser.h"
|
||||
#include "pkcs7-asn1.h"
|
||||
|
||||
struct pkcs7_parse_context {
|
||||
struct pkcs7_message *msg; /* Message being constructed */
|
||||
struct pkcs7_signed_info *sinfo; /* SignedInfo being constructed */
|
||||
struct pkcs7_signed_info **ppsinfo;
|
||||
struct x509_certificate *certs; /* Certificate cache */
|
||||
struct x509_certificate **ppcerts;
|
||||
unsigned long data; /* Start of data */
|
||||
enum OID last_oid; /* Last OID encountered */
|
||||
unsigned x509_index;
|
||||
unsigned sinfo_index;
|
||||
};
|
||||
|
||||
/**
|
||||
* pkcs7_free_message - Free a PKCS#7 message
|
||||
* @pkcs7: The PKCS#7 message to free
|
||||
*/
|
||||
void pkcs7_free_message(struct pkcs7_message *pkcs7)
|
||||
{
|
||||
struct x509_certificate *cert;
|
||||
struct pkcs7_signed_info *sinfo;
|
||||
|
||||
if (pkcs7) {
|
||||
while (pkcs7->certs) {
|
||||
cert = pkcs7->certs;
|
||||
pkcs7->certs = cert->next;
|
||||
x509_free_certificate(cert);
|
||||
}
|
||||
while (pkcs7->crl) {
|
||||
cert = pkcs7->crl;
|
||||
pkcs7->crl = cert->next;
|
||||
x509_free_certificate(cert);
|
||||
}
|
||||
while (pkcs7->signed_infos) {
|
||||
sinfo = pkcs7->signed_infos;
|
||||
pkcs7->signed_infos = sinfo->next;
|
||||
mpi_free(sinfo->sig.mpi[0]);
|
||||
kfree(sinfo->sig.digest);
|
||||
kfree(sinfo);
|
||||
}
|
||||
kfree(pkcs7);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pkcs7_free_message);
|
||||
|
||||
/**
|
||||
* pkcs7_parse_message - Parse a PKCS#7 message
|
||||
* @data: The raw binary ASN.1 encoded message to be parsed
|
||||
* @datalen: The size of the encoded message
|
||||
*/
|
||||
struct pkcs7_message *pkcs7_parse_message(const void *data, size_t datalen)
|
||||
{
|
||||
struct pkcs7_parse_context *ctx;
|
||||
struct pkcs7_message *msg;
|
||||
long ret;
|
||||
|
||||
ret = -ENOMEM;
|
||||
msg = kzalloc(sizeof(struct pkcs7_message), GFP_KERNEL);
|
||||
if (!msg)
|
||||
goto error_no_sig;
|
||||
ctx = kzalloc(sizeof(struct pkcs7_parse_context), GFP_KERNEL);
|
||||
if (!ctx)
|
||||
goto error_no_ctx;
|
||||
ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL);
|
||||
if (!ctx->sinfo)
|
||||
goto error_no_sinfo;
|
||||
|
||||
ctx->msg = msg;
|
||||
ctx->data = (unsigned long)data;
|
||||
ctx->ppcerts = &ctx->certs;
|
||||
ctx->ppsinfo = &ctx->msg->signed_infos;
|
||||
|
||||
/* Attempt to decode the signature */
|
||||
ret = asn1_ber_decoder(&pkcs7_decoder, ctx, data, datalen);
|
||||
if (ret < 0)
|
||||
goto error_decode;
|
||||
|
||||
while (ctx->certs) {
|
||||
struct x509_certificate *cert = ctx->certs;
|
||||
ctx->certs = cert->next;
|
||||
x509_free_certificate(cert);
|
||||
}
|
||||
mpi_free(ctx->sinfo->sig.mpi[0]);
|
||||
kfree(ctx->sinfo->sig.digest);
|
||||
kfree(ctx->sinfo);
|
||||
kfree(ctx);
|
||||
return msg;
|
||||
|
||||
error_decode:
|
||||
mpi_free(ctx->sinfo->sig.mpi[0]);
|
||||
kfree(ctx->sinfo->sig.digest);
|
||||
kfree(ctx->sinfo);
|
||||
error_no_sinfo:
|
||||
kfree(ctx);
|
||||
error_no_ctx:
|
||||
pkcs7_free_message(msg);
|
||||
error_no_sig:
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pkcs7_parse_message);
|
||||
|
||||
/**
|
||||
* pkcs7_get_content_data - Get access to the PKCS#7 content
|
||||
* @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
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
int pkcs7_get_content_data(const struct pkcs7_message *pkcs7,
|
||||
const void **_data, size_t *_data_len,
|
||||
bool want_wrapper)
|
||||
{
|
||||
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;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pkcs7_get_content_data);
|
||||
|
||||
/*
|
||||
* Note an OID when we find one for later processing when we know how
|
||||
* to interpret it.
|
||||
*/
|
||||
int pkcs7_note_OID(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pkcs7_parse_context *ctx = context;
|
||||
|
||||
ctx->last_oid = look_up_OID(value, vlen);
|
||||
if (ctx->last_oid == OID__NR) {
|
||||
char buffer[50];
|
||||
sprint_oid(value, vlen, buffer, sizeof(buffer));
|
||||
printk("PKCS7: Unknown OID: [%lu] %s\n",
|
||||
(unsigned long)value - ctx->data, buffer);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note the digest algorithm for the signature.
|
||||
*/
|
||||
int pkcs7_sig_note_digest_algo(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pkcs7_parse_context *ctx = context;
|
||||
|
||||
switch (ctx->last_oid) {
|
||||
case OID_md4:
|
||||
ctx->sinfo->sig.pkey_hash_algo = HASH_ALGO_MD4;
|
||||
break;
|
||||
case OID_md5:
|
||||
ctx->sinfo->sig.pkey_hash_algo = HASH_ALGO_MD5;
|
||||
break;
|
||||
case OID_sha1:
|
||||
ctx->sinfo->sig.pkey_hash_algo = HASH_ALGO_SHA1;
|
||||
break;
|
||||
case OID_sha256:
|
||||
ctx->sinfo->sig.pkey_hash_algo = HASH_ALGO_SHA256;
|
||||
break;
|
||||
default:
|
||||
printk("Unsupported digest algo: %u\n", ctx->last_oid);
|
||||
return -ENOPKG;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note the public key algorithm for the signature.
|
||||
*/
|
||||
int pkcs7_sig_note_pkey_algo(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pkcs7_parse_context *ctx = context;
|
||||
|
||||
switch (ctx->last_oid) {
|
||||
case OID_rsaEncryption:
|
||||
ctx->sinfo->sig.pkey_algo = PKEY_ALGO_RSA;
|
||||
break;
|
||||
default:
|
||||
printk("Unsupported pkey algo: %u\n", ctx->last_oid);
|
||||
return -ENOPKG;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Extract a certificate and store it in the context.
|
||||
*/
|
||||
int pkcs7_extract_cert(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pkcs7_parse_context *ctx = context;
|
||||
struct x509_certificate *x509;
|
||||
|
||||
if (tag != ((ASN1_UNIV << 6) | ASN1_CONS_BIT | ASN1_SEQ)) {
|
||||
pr_debug("Cert began with tag %02x at %lu\n",
|
||||
tag, (unsigned long)ctx - ctx->data);
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
/* We have to correct for the header so that the X.509 parser can start
|
||||
* from the beginning. Note that since X.509 stipulates DER, there
|
||||
* probably shouldn't be an EOC trailer - but it is in PKCS#7 (which
|
||||
* stipulates BER).
|
||||
*/
|
||||
value -= hdrlen;
|
||||
vlen += hdrlen;
|
||||
|
||||
if (((u8*)value)[1] == 0x80)
|
||||
vlen += 2; /* Indefinite length - there should be an EOC */
|
||||
|
||||
x509 = x509_cert_parse(value, vlen);
|
||||
if (IS_ERR(x509))
|
||||
return PTR_ERR(x509);
|
||||
|
||||
pr_debug("Got cert for %s\n", x509->subject);
|
||||
pr_debug("- fingerprint %s\n", x509->fingerprint);
|
||||
|
||||
x509->index = ++ctx->x509_index;
|
||||
*ctx->ppcerts = x509;
|
||||
ctx->ppcerts = &x509->next;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Save the certificate list
|
||||
*/
|
||||
int pkcs7_note_certificate_list(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pkcs7_parse_context *ctx = context;
|
||||
|
||||
pr_devel("Got cert list (%02x)\n", tag);
|
||||
|
||||
*ctx->ppcerts = ctx->msg->certs;
|
||||
ctx->msg->certs = ctx->certs;
|
||||
ctx->certs = NULL;
|
||||
ctx->ppcerts = &ctx->certs;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Extract the data from the message and store that and its content type OID in
|
||||
* the context.
|
||||
*/
|
||||
int pkcs7_note_data(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pkcs7_parse_context *ctx = context;
|
||||
|
||||
pr_debug("Got data\n");
|
||||
|
||||
ctx->msg->data = value;
|
||||
ctx->msg->data_len = vlen;
|
||||
ctx->msg->data_hdrlen = hdrlen;
|
||||
ctx->msg->data_type = ctx->last_oid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse authenticated attributes
|
||||
*/
|
||||
int pkcs7_sig_note_authenticated_attr(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pkcs7_parse_context *ctx = context;
|
||||
|
||||
pr_devel("AuthAttr: %02x %zu [%*ph]\n", tag, vlen, (unsigned)vlen, value);
|
||||
|
||||
switch (ctx->last_oid) {
|
||||
case OID_messageDigest:
|
||||
if (tag != ASN1_OTS)
|
||||
return -EBADMSG;
|
||||
ctx->sinfo->msgdigest = value;
|
||||
ctx->sinfo->msgdigest_len = vlen;
|
||||
return 0;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Note the set of auth attributes for digestion purposes [RFC2315 9.3]
|
||||
*/
|
||||
int pkcs7_sig_note_set_of_authattrs(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pkcs7_parse_context *ctx = context;
|
||||
|
||||
/* We need to switch the 'CONT 0' to a 'SET OF' when we digest */
|
||||
ctx->sinfo->authattrs = value - (hdrlen - 1);
|
||||
ctx->sinfo->authattrs_len = vlen + (hdrlen - 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note the issuing certificate serial number
|
||||
*/
|
||||
int pkcs7_sig_note_serial(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pkcs7_parse_context *ctx = context;
|
||||
ctx->sinfo->raw_serial = value;
|
||||
ctx->sinfo->raw_serial_size = vlen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note the issuer's name
|
||||
*/
|
||||
int pkcs7_sig_note_issuer(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pkcs7_parse_context *ctx = context;
|
||||
ctx->sinfo->raw_issuer = value;
|
||||
ctx->sinfo->raw_issuer_size = vlen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note the signature data
|
||||
*/
|
||||
int pkcs7_sig_note_signature(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pkcs7_parse_context *ctx = context;
|
||||
MPI mpi;
|
||||
|
||||
BUG_ON(ctx->sinfo->sig.pkey_algo != PKEY_ALGO_RSA);
|
||||
|
||||
mpi = mpi_read_raw_data(value, vlen);
|
||||
if (!mpi)
|
||||
return -ENOMEM;
|
||||
|
||||
ctx->sinfo->sig.mpi[0] = mpi;
|
||||
ctx->sinfo->sig.nr_mpi = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note a signature information block
|
||||
*/
|
||||
int pkcs7_note_signed_info(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct pkcs7_parse_context *ctx = context;
|
||||
|
||||
ctx->sinfo->index = ++ctx->sinfo_index;
|
||||
*ctx->ppsinfo = ctx->sinfo;
|
||||
ctx->ppsinfo = &ctx->sinfo->next;
|
||||
ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL);
|
||||
if (!ctx->sinfo)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/* PKCS#7 crypto data parser internal definitions
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/oid_registry.h>
|
||||
#include <crypto/pkcs7.h>
|
||||
#include "x509_parser.h"
|
||||
|
||||
#define kenter(FMT, ...) \
|
||||
pr_devel("==> %s("FMT")\n", __func__, ##__VA_ARGS__)
|
||||
#define kleave(FMT, ...) \
|
||||
pr_devel("<== %s()"FMT"\n", __func__, ##__VA_ARGS__)
|
||||
|
||||
struct pkcs7_signed_info {
|
||||
struct pkcs7_signed_info *next;
|
||||
struct x509_certificate *signer; /* Signing certificate (in msg->certs) */
|
||||
unsigned index;
|
||||
bool trusted;
|
||||
|
||||
/* Message digest - the digest of the Content Data (or NULL) */
|
||||
const void *msgdigest;
|
||||
unsigned msgdigest_len;
|
||||
|
||||
/* Authenticated Attribute data (or NULL) */
|
||||
unsigned authattrs_len;
|
||||
const void *authattrs;
|
||||
|
||||
/* Issuing cert serial number and issuer's name */
|
||||
const void *raw_serial;
|
||||
unsigned raw_serial_size;
|
||||
unsigned raw_issuer_size;
|
||||
const void *raw_issuer;
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
struct public_key_signature sig;
|
||||
};
|
||||
|
||||
struct pkcs7_message {
|
||||
struct x509_certificate *certs; /* Certificate list */
|
||||
struct x509_certificate *crl; /* Revocation list */
|
||||
struct pkcs7_signed_info *signed_infos;
|
||||
|
||||
/* Content Data (or NULL) */
|
||||
enum OID data_type; /* Type of Data */
|
||||
size_t data_len; /* Length of Data */
|
||||
size_t data_hdrlen; /* Length of Data ASN.1 header */
|
||||
const void *data; /* Content Data (or 0) */
|
||||
};
|
|
@ -0,0 +1,219 @@
|
|||
/* Validate the trust chain of a PKCS#7 message.
|
||||
*
|
||||
* 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) "PKCS7: "fmt
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/asn1.h>
|
||||
#include <linux/key.h>
|
||||
#include <keys/asymmetric-type.h>
|
||||
#include "public_key.h"
|
||||
#include "pkcs7_parser.h"
|
||||
|
||||
/*
|
||||
* Request an asymmetric key.
|
||||
*/
|
||||
static struct key *pkcs7_request_asymmetric_key(
|
||||
struct key *keyring,
|
||||
const char *signer, size_t signer_len,
|
||||
const char *authority, size_t auth_len)
|
||||
{
|
||||
key_ref_t key;
|
||||
char *id;
|
||||
|
||||
kenter(",%zu,,%zu", signer_len, auth_len);
|
||||
|
||||
/* Construct an identifier. */
|
||||
id = kmalloc(signer_len + 2 + auth_len + 1, GFP_KERNEL);
|
||||
if (!id)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
memcpy(id, signer, signer_len);
|
||||
id[signer_len + 0] = ':';
|
||||
id[signer_len + 1] = ' ';
|
||||
memcpy(id + signer_len + 2, authority, auth_len);
|
||||
id[signer_len + 2 + auth_len] = 0;
|
||||
|
||||
pr_debug("Look up: \"%s\"\n", id);
|
||||
|
||||
key = keyring_search(make_key_ref(keyring, 1),
|
||||
&key_type_asymmetric, id);
|
||||
if (IS_ERR(key))
|
||||
pr_debug("Request for module key '%s' err %ld\n",
|
||||
id, PTR_ERR(key));
|
||||
kfree(id);
|
||||
|
||||
if (IS_ERR(key)) {
|
||||
switch (PTR_ERR(key)) {
|
||||
/* Hide some search errors */
|
||||
case -EACCES:
|
||||
case -ENOTDIR:
|
||||
case -EAGAIN:
|
||||
return ERR_PTR(-ENOKEY);
|
||||
default:
|
||||
return ERR_CAST(key);
|
||||
}
|
||||
}
|
||||
|
||||
pr_devel("<==%s() = 0 [%x]\n", __func__, key_serial(key_ref_to_ptr(key)));
|
||||
return key_ref_to_ptr(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the trust on one PKCS#7 SignedInfo block.
|
||||
*/
|
||||
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 x509_certificate *x509, *last = NULL, *p;
|
||||
struct key *key;
|
||||
bool trusted;
|
||||
int ret;
|
||||
|
||||
kenter(",%u,", sinfo->index);
|
||||
|
||||
for (x509 = sinfo->signer; x509; x509 = x509->signer) {
|
||||
if (x509->seen) {
|
||||
if (x509->verified) {
|
||||
trusted = x509->trusted;
|
||||
goto verified;
|
||||
}
|
||||
kleave(" = -ENOKEY [cached]");
|
||||
return -ENOKEY;
|
||||
}
|
||||
x509->seen = true;
|
||||
|
||||
/* Look to see if this certificate is present in the trusted
|
||||
* keys.
|
||||
*/
|
||||
key = pkcs7_request_asymmetric_key(
|
||||
trust_keyring,
|
||||
x509->subject, strlen(x509->subject),
|
||||
x509->fingerprint, strlen(x509->fingerprint));
|
||||
if (!IS_ERR(key))
|
||||
/* One of the X.509 certificates in the PKCS#7 message
|
||||
* is apparently the same as one we already trust.
|
||||
* Verify that the trusted variant can also validate
|
||||
* the signature on the descendant.
|
||||
*/
|
||||
goto matched;
|
||||
if (key == ERR_PTR(-ENOMEM))
|
||||
return -ENOMEM;
|
||||
|
||||
/* Self-signed certificates form roots of their own, and if we
|
||||
* don't know them, then we can't accept them.
|
||||
*/
|
||||
if (x509->next == x509) {
|
||||
kleave(" = -ENOKEY [unknown self-signed]");
|
||||
return -ENOKEY;
|
||||
}
|
||||
|
||||
might_sleep();
|
||||
last = x509;
|
||||
sig = &last->sig;
|
||||
}
|
||||
|
||||
/* No match - see if the root certificate has a signer amongst the
|
||||
* trusted keys.
|
||||
*/
|
||||
if (!last || !last->issuer || !last->authority) {
|
||||
kleave(" = -ENOKEY [no backref]");
|
||||
return -ENOKEY;
|
||||
}
|
||||
|
||||
key = pkcs7_request_asymmetric_key(
|
||||
trust_keyring,
|
||||
last->issuer, strlen(last->issuer),
|
||||
last->authority, strlen(last->authority));
|
||||
if (IS_ERR(key))
|
||||
return PTR_ERR(key) == -ENOMEM ? -ENOMEM : -ENOKEY;
|
||||
x509 = last;
|
||||
|
||||
matched:
|
||||
ret = verify_signature(key, sig);
|
||||
trusted = test_bit(KEY_FLAG_TRUSTED, &key->flags);
|
||||
key_put(key);
|
||||
if (ret < 0) {
|
||||
if (ret == -ENOMEM)
|
||||
return ret;
|
||||
kleave(" = -EKEYREJECTED [verify %d]", ret);
|
||||
return -EKEYREJECTED;
|
||||
}
|
||||
|
||||
verified:
|
||||
x509->verified = true;
|
||||
for (p = sinfo->signer; p != x509; p = p->signer) {
|
||||
p->verified = true;
|
||||
p->trusted = trusted;
|
||||
}
|
||||
sinfo->trusted = trusted;
|
||||
kleave(" = 0");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* Returns, in order of descending priority:
|
||||
*
|
||||
* (*) -EKEYREJECTED if a signature failed to match for which we have a valid
|
||||
* key, or:
|
||||
*
|
||||
* (*) 0 if at least one signature chain intersects with the keys in the trust
|
||||
* keyring, or:
|
||||
*
|
||||
* (*) -ENOPKG if a suitable crypto module couldn't be found for a check on a
|
||||
* chain.
|
||||
*
|
||||
* (*) -ENOKEY if we couldn't find a match for any of the signature chains in
|
||||
* the message.
|
||||
*
|
||||
* May also return -ENOMEM.
|
||||
*/
|
||||
int pkcs7_validate_trust(struct pkcs7_message *pkcs7,
|
||||
struct key *trust_keyring,
|
||||
bool *_trusted)
|
||||
{
|
||||
struct pkcs7_signed_info *sinfo;
|
||||
struct x509_certificate *p;
|
||||
int cached_ret = 0, ret;
|
||||
|
||||
for (p = pkcs7->certs; p; p = p->next)
|
||||
p->seen = false;
|
||||
|
||||
for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) {
|
||||
ret = pkcs7_validate_trust_one(pkcs7, sinfo, trust_keyring);
|
||||
if (ret < 0) {
|
||||
if (ret == -ENOPKG) {
|
||||
cached_ret = -ENOPKG;
|
||||
} else if (ret == -ENOKEY) {
|
||||
if (cached_ret == 0)
|
||||
cached_ret = -ENOKEY;
|
||||
} else {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
*_trusted |= sinfo->trusted;
|
||||
}
|
||||
|
||||
return cached_ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pkcs7_validate_trust);
|
|
@ -0,0 +1,323 @@
|
|||
/* Verify the signature on a PKCS#7 message.
|
||||
*
|
||||
* 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) "PKCS7: "fmt
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/asn1.h>
|
||||
#include <crypto/hash.h>
|
||||
#include "public_key.h"
|
||||
#include "pkcs7_parser.h"
|
||||
|
||||
/*
|
||||
* Digest the relevant parts of the PKCS#7 data
|
||||
*/
|
||||
static int pkcs7_digest(struct pkcs7_message *pkcs7,
|
||||
struct pkcs7_signed_info *sinfo)
|
||||
{
|
||||
struct crypto_shash *tfm;
|
||||
struct shash_desc *desc;
|
||||
size_t digest_size, desc_size;
|
||||
void *digest;
|
||||
int ret;
|
||||
|
||||
kenter(",%u,%u", sinfo->index, sinfo->sig.pkey_hash_algo);
|
||||
|
||||
if (sinfo->sig.pkey_hash_algo >= PKEY_HASH__LAST ||
|
||||
!hash_algo_name[sinfo->sig.pkey_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(hash_algo_name[sinfo->sig.pkey_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);
|
||||
|
||||
ret = -ENOMEM;
|
||||
digest = kzalloc(digest_size + desc_size, GFP_KERNEL);
|
||||
if (!digest)
|
||||
goto error_no_desc;
|
||||
|
||||
desc = digest + digest_size;
|
||||
desc->tfm = tfm;
|
||||
desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
|
||||
|
||||
/* Digest the message [RFC2315 9.3] */
|
||||
ret = crypto_shash_init(desc);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
ret = crypto_shash_finup(desc, pkcs7->data, pkcs7->data_len, digest);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
pr_devel("MsgDigest = [%*ph]\n", 8, digest);
|
||||
|
||||
/* However, if there are authenticated attributes, there must be a
|
||||
* message digest attribute amongst them which corresponds to the
|
||||
* digest we just calculated.
|
||||
*/
|
||||
if (sinfo->msgdigest) {
|
||||
u8 tag;
|
||||
|
||||
if (sinfo->msgdigest_len != sinfo->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) {
|
||||
pr_debug("Sig %u: Message digest doesn't match\n",
|
||||
sinfo->index);
|
||||
ret = -EKEYREJECTED;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* We then calculate anew, using the authenticated attributes
|
||||
* as the contents of the digest instead. Note that we need to
|
||||
* convert the attributes from a CONT.0 into a SET before we
|
||||
* hash it.
|
||||
*/
|
||||
memset(digest, 0, sinfo->sig.digest_size);
|
||||
|
||||
ret = crypto_shash_init(desc);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
tag = ASN1_CONS_BIT | ASN1_SET;
|
||||
ret = crypto_shash_update(desc, &tag, 1);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
ret = crypto_shash_finup(desc, sinfo->authattrs,
|
||||
sinfo->authattrs_len, digest);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
pr_devel("AADigest = [%*ph]\n", 8, digest);
|
||||
}
|
||||
|
||||
sinfo->sig.digest = digest;
|
||||
digest = NULL;
|
||||
|
||||
error:
|
||||
kfree(digest);
|
||||
error_no_desc:
|
||||
crypto_free_shash(tfm);
|
||||
kleave(" = %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the key (X.509 certificate) to use to verify a PKCS#7 message. PKCS#7
|
||||
* uses the issuer's name and the issuing certificate serial number for
|
||||
* matching purposes. These must match the certificate issuer's name (not
|
||||
* subject's name) and the certificate serial number [RFC 2315 6.7].
|
||||
*/
|
||||
static int pkcs7_find_key(struct pkcs7_message *pkcs7,
|
||||
struct pkcs7_signed_info *sinfo)
|
||||
{
|
||||
struct x509_certificate *x509;
|
||||
unsigned certix = 1;
|
||||
|
||||
kenter("%u,%u,%u",
|
||||
sinfo->index, sinfo->raw_serial_size, sinfo->raw_issuer_size);
|
||||
|
||||
for (x509 = pkcs7->certs; x509; x509 = x509->next, certix++) {
|
||||
/* I'm _assuming_ that the generator of the PKCS#7 message will
|
||||
* encode the fields from the X.509 cert in the same way in the
|
||||
* PKCS#7 message - but I can't be 100% sure of that. It's
|
||||
* possible this will need element-by-element comparison.
|
||||
*/
|
||||
if (x509->raw_serial_size != sinfo->raw_serial_size ||
|
||||
memcmp(x509->raw_serial, sinfo->raw_serial,
|
||||
sinfo->raw_serial_size) != 0)
|
||||
continue;
|
||||
pr_devel("Sig %u: Found cert serial match X.509[%u]\n",
|
||||
sinfo->index, certix);
|
||||
|
||||
if (x509->raw_issuer_size != sinfo->raw_issuer_size ||
|
||||
memcmp(x509->raw_issuer, sinfo->raw_issuer,
|
||||
sinfo->raw_issuer_size) != 0) {
|
||||
pr_warn("Sig %u: X.509 subject and PKCS#7 issuer don't match\n",
|
||||
sinfo->index);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (x509->pub->pkey_algo != sinfo->sig.pkey_algo) {
|
||||
pr_warn("Sig %u: X.509 algo and PKCS#7 sig algo don't match\n",
|
||||
sinfo->index);
|
||||
continue;
|
||||
}
|
||||
|
||||
sinfo->signer = x509;
|
||||
return 0;
|
||||
}
|
||||
pr_warn("Sig %u: Issuing X.509 cert not found (#%*ph)\n",
|
||||
sinfo->index, sinfo->raw_serial_size, sinfo->raw_serial);
|
||||
return -ENOKEY;
|
||||
}
|
||||
|
||||
/*
|
||||
* Verify the internal certificate chain as best we can.
|
||||
*/
|
||||
static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
|
||||
struct pkcs7_signed_info *sinfo)
|
||||
{
|
||||
struct x509_certificate *x509 = sinfo->signer, *p;
|
||||
int ret;
|
||||
|
||||
kenter("");
|
||||
|
||||
for (p = pkcs7->certs; p; p = p->next)
|
||||
p->seen = false;
|
||||
|
||||
for (;;) {
|
||||
pr_debug("verify %s: %s\n", x509->subject, x509->fingerprint);
|
||||
x509->seen = true;
|
||||
ret = x509_get_sig_params(x509);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (x509->issuer)
|
||||
pr_debug("- issuer %s\n", x509->issuer);
|
||||
if (x509->authority)
|
||||
pr_debug("- authkeyid %s\n", x509->authority);
|
||||
|
||||
if (!x509->authority ||
|
||||
(x509->subject &&
|
||||
strcmp(x509->subject, x509->issuer) == 0)) {
|
||||
/* 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)
|
||||
return ret;
|
||||
x509->signer = x509;
|
||||
pr_debug("- self-signed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Look through the X.509 certificates in the PKCS#7 message's
|
||||
* list to see if the next one is there.
|
||||
*/
|
||||
pr_debug("- want %s\n", x509->authority);
|
||||
for (p = pkcs7->certs; p; p = p->next) {
|
||||
pr_debug("- cmp [%u] %s\n", p->index, p->fingerprint);
|
||||
if (p->raw_subject_size == x509->raw_issuer_size &&
|
||||
strcmp(p->fingerprint, x509->authority) == 0 &&
|
||||
memcmp(p->raw_subject, x509->raw_issuer,
|
||||
x509->raw_issuer_size) == 0)
|
||||
goto found_issuer;
|
||||
}
|
||||
|
||||
/* We didn't find the root of this chain */
|
||||
pr_debug("- top\n");
|
||||
return 0;
|
||||
|
||||
found_issuer:
|
||||
pr_debug("- issuer %s\n", p->subject);
|
||||
if (p->seen) {
|
||||
pr_warn("Sig %u: X.509 chain contains loop\n",
|
||||
sinfo->index);
|
||||
return 0;
|
||||
}
|
||||
ret = x509_check_signature(p->pub, x509);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
x509->signer = p;
|
||||
if (x509 == p) {
|
||||
pr_debug("- self-signed\n");
|
||||
return 0;
|
||||
}
|
||||
x509 = p;
|
||||
might_sleep();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Verify one signed information block from a PKCS#7 message.
|
||||
*/
|
||||
static int pkcs7_verify_one(struct pkcs7_message *pkcs7,
|
||||
struct pkcs7_signed_info *sinfo)
|
||||
{
|
||||
int ret;
|
||||
|
||||
kenter(",%u", sinfo->index);
|
||||
|
||||
/* First of all, digest the data in the PKCS#7 message and the
|
||||
* signed information block
|
||||
*/
|
||||
ret = pkcs7_digest(pkcs7, sinfo);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Find the key for the signature */
|
||||
ret = pkcs7_find_key(pkcs7, sinfo);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
pr_devel("Using X.509[%u] for sig %u\n",
|
||||
sinfo->signer->index, sinfo->index);
|
||||
|
||||
/* Verify the PKCS#7 binary against the key */
|
||||
ret = public_key_verify_signature(sinfo->signer->pub, &sinfo->sig);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
pr_devel("Verified signature %u\n", sinfo->index);
|
||||
|
||||
/* Verify the internal certificate chain */
|
||||
return pkcs7_verify_sig_chain(pkcs7, sinfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* pkcs7_verify - Verify a PKCS#7 message
|
||||
* @pkcs7: The PKCS#7 message to be verified
|
||||
*/
|
||||
int pkcs7_verify(struct pkcs7_message *pkcs7)
|
||||
{
|
||||
struct pkcs7_signed_info *sinfo;
|
||||
struct x509_certificate *x509;
|
||||
int ret, n;
|
||||
|
||||
kenter("");
|
||||
|
||||
for (n = 0, x509 = pkcs7->certs; x509; x509 = x509->next, n++) {
|
||||
ret = x509_get_sig_params(x509);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
pr_debug("X.509[%u] %s\n", n, x509->authority);
|
||||
}
|
||||
|
||||
for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) {
|
||||
ret = pkcs7_verify_one(pkcs7, sinfo);
|
||||
if (ret < 0) {
|
||||
kleave(" = %d", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
kleave(" = 0");
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pkcs7_verify);
|
|
@ -0,0 +1,457 @@
|
|||
/* Parse a signed PE binary
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "PEFILE: "fmt
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/pe.h>
|
||||
#include <linux/asn1.h>
|
||||
#include <crypto/pkcs7.h>
|
||||
#include <crypto/hash.h>
|
||||
#include "verify_pefile.h"
|
||||
|
||||
/*
|
||||
* Parse a PE binary.
|
||||
*/
|
||||
static int pefile_parse_binary(const void *pebuf, unsigned int pelen,
|
||||
struct pefile_context *ctx)
|
||||
{
|
||||
const struct mz_hdr *mz = pebuf;
|
||||
const struct pe_hdr *pe;
|
||||
const struct pe32_opt_hdr *pe32;
|
||||
const struct pe32plus_opt_hdr *pe64;
|
||||
const struct data_directory *ddir;
|
||||
const struct data_dirent *dde;
|
||||
const struct section_header *secs, *sec;
|
||||
size_t cursor, datalen = pelen;
|
||||
|
||||
kenter("");
|
||||
|
||||
#define chkaddr(base, x, s) \
|
||||
do { \
|
||||
if ((x) < base || (s) >= datalen || (x) > datalen - (s)) \
|
||||
return -ELIBBAD; \
|
||||
} while (0)
|
||||
|
||||
chkaddr(0, 0, sizeof(*mz));
|
||||
if (mz->magic != MZ_MAGIC)
|
||||
return -ELIBBAD;
|
||||
cursor = sizeof(*mz);
|
||||
|
||||
chkaddr(cursor, mz->peaddr, sizeof(*pe));
|
||||
pe = pebuf + mz->peaddr;
|
||||
if (pe->magic != PE_MAGIC)
|
||||
return -ELIBBAD;
|
||||
cursor = mz->peaddr + sizeof(*pe);
|
||||
|
||||
chkaddr(0, cursor, sizeof(pe32->magic));
|
||||
pe32 = pebuf + cursor;
|
||||
pe64 = pebuf + cursor;
|
||||
|
||||
switch (pe32->magic) {
|
||||
case PE_OPT_MAGIC_PE32:
|
||||
chkaddr(0, cursor, sizeof(*pe32));
|
||||
ctx->image_checksum_offset =
|
||||
(unsigned long)&pe32->csum - (unsigned long)pebuf;
|
||||
ctx->header_size = pe32->header_size;
|
||||
cursor += sizeof(*pe32);
|
||||
ctx->n_data_dirents = pe32->data_dirs;
|
||||
break;
|
||||
|
||||
case PE_OPT_MAGIC_PE32PLUS:
|
||||
chkaddr(0, cursor, sizeof(*pe64));
|
||||
ctx->image_checksum_offset =
|
||||
(unsigned long)&pe64->csum - (unsigned long)pebuf;
|
||||
ctx->header_size = pe64->header_size;
|
||||
cursor += sizeof(*pe64);
|
||||
ctx->n_data_dirents = pe64->data_dirs;
|
||||
break;
|
||||
|
||||
default:
|
||||
pr_debug("Unknown PEOPT magic = %04hx\n", pe32->magic);
|
||||
return -ELIBBAD;
|
||||
}
|
||||
|
||||
pr_debug("checksum @ %x\n", ctx->image_checksum_offset);
|
||||
pr_debug("header size = %x\n", ctx->header_size);
|
||||
|
||||
if (cursor >= ctx->header_size || ctx->header_size >= datalen)
|
||||
return -ELIBBAD;
|
||||
|
||||
if (ctx->n_data_dirents > (ctx->header_size - cursor) / sizeof(*dde))
|
||||
return -ELIBBAD;
|
||||
|
||||
ddir = pebuf + cursor;
|
||||
cursor += sizeof(*dde) * ctx->n_data_dirents;
|
||||
|
||||
ctx->cert_dirent_offset =
|
||||
(unsigned long)&ddir->certs - (unsigned long)pebuf;
|
||||
ctx->certs_size = ddir->certs.size;
|
||||
|
||||
if (!ddir->certs.virtual_address || !ddir->certs.size) {
|
||||
pr_debug("Unsigned PE binary\n");
|
||||
return -EKEYREJECTED;
|
||||
}
|
||||
|
||||
chkaddr(ctx->header_size, ddir->certs.virtual_address,
|
||||
ddir->certs.size);
|
||||
ctx->sig_offset = ddir->certs.virtual_address;
|
||||
ctx->sig_len = ddir->certs.size;
|
||||
pr_debug("cert = %x @%x [%*ph]\n",
|
||||
ctx->sig_len, ctx->sig_offset,
|
||||
ctx->sig_len, pebuf + ctx->sig_offset);
|
||||
|
||||
ctx->n_sections = pe->sections;
|
||||
if (ctx->n_sections > (ctx->header_size - cursor) / sizeof(*sec))
|
||||
return -ELIBBAD;
|
||||
ctx->secs = secs = pebuf + cursor;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check and strip the PE wrapper from around the signature and check that the
|
||||
* remnant looks something like PKCS#7.
|
||||
*/
|
||||
static int pefile_strip_sig_wrapper(const void *pebuf,
|
||||
struct pefile_context *ctx)
|
||||
{
|
||||
struct win_certificate wrapper;
|
||||
const u8 *pkcs7;
|
||||
|
||||
if (ctx->sig_len < sizeof(wrapper)) {
|
||||
pr_debug("Signature wrapper too short\n");
|
||||
return -ELIBBAD;
|
||||
}
|
||||
|
||||
memcpy(&wrapper, pebuf + ctx->sig_offset, sizeof(wrapper));
|
||||
pr_debug("sig wrapper = { %x, %x, %x }\n",
|
||||
wrapper.length, wrapper.revision, wrapper.cert_type);
|
||||
|
||||
/* Both pesign and sbsign round up the length of certificate table
|
||||
* (in optional header data directories) to 8 byte alignment.
|
||||
*/
|
||||
if (round_up(wrapper.length, 8) != ctx->sig_len) {
|
||||
pr_debug("Signature wrapper len wrong\n");
|
||||
return -ELIBBAD;
|
||||
}
|
||||
if (wrapper.revision != WIN_CERT_REVISION_2_0) {
|
||||
pr_debug("Signature is not revision 2.0\n");
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
if (wrapper.cert_type != WIN_CERT_TYPE_PKCS_SIGNED_DATA) {
|
||||
pr_debug("Signature certificate type is not PKCS\n");
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
/* Looks like actual pkcs signature length is in wrapper->length.
|
||||
* size obtained from data dir entries lists the total size of
|
||||
* certificate table which is also aligned to octawrod boundary.
|
||||
*
|
||||
* So set signature length field appropriately.
|
||||
*/
|
||||
ctx->sig_len = wrapper.length;
|
||||
ctx->sig_offset += sizeof(wrapper);
|
||||
ctx->sig_len -= sizeof(wrapper);
|
||||
if (ctx->sig_len == 0) {
|
||||
pr_debug("Signature data missing\n");
|
||||
return -EKEYREJECTED;
|
||||
}
|
||||
|
||||
/* What's left should a PKCS#7 cert */
|
||||
pkcs7 = pebuf + ctx->sig_offset;
|
||||
if (pkcs7[0] == (ASN1_CONS_BIT | ASN1_SEQ)) {
|
||||
if (pkcs7[1] == 0x82 &&
|
||||
pkcs7[2] == (((ctx->sig_len - 4) >> 8) & 0xff) &&
|
||||
pkcs7[3] == ((ctx->sig_len - 4) & 0xff))
|
||||
return 0;
|
||||
if (pkcs7[1] == 0x80)
|
||||
return 0;
|
||||
if (pkcs7[1] > 0x82)
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
pr_debug("Signature data not PKCS#7\n");
|
||||
return -ELIBBAD;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compare two sections for canonicalisation.
|
||||
*/
|
||||
static int pefile_compare_shdrs(const void *a, const void *b)
|
||||
{
|
||||
const struct section_header *shdra = a;
|
||||
const struct section_header *shdrb = b;
|
||||
int rc;
|
||||
|
||||
if (shdra->data_addr > shdrb->data_addr)
|
||||
return 1;
|
||||
if (shdrb->data_addr > shdra->data_addr)
|
||||
return -1;
|
||||
|
||||
if (shdra->virtual_address > shdrb->virtual_address)
|
||||
return 1;
|
||||
if (shdrb->virtual_address > shdra->virtual_address)
|
||||
return -1;
|
||||
|
||||
rc = strcmp(shdra->name, shdrb->name);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
if (shdra->virtual_size > shdrb->virtual_size)
|
||||
return 1;
|
||||
if (shdrb->virtual_size > shdra->virtual_size)
|
||||
return -1;
|
||||
|
||||
if (shdra->raw_data_size > shdrb->raw_data_size)
|
||||
return 1;
|
||||
if (shdrb->raw_data_size > shdra->raw_data_size)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Load the contents of the PE binary into the digest, leaving out the image
|
||||
* checksum and the certificate data block.
|
||||
*/
|
||||
static int pefile_digest_pe_contents(const void *pebuf, unsigned int pelen,
|
||||
struct pefile_context *ctx,
|
||||
struct shash_desc *desc)
|
||||
{
|
||||
unsigned *canon, tmp, loop, i, hashed_bytes;
|
||||
int ret;
|
||||
|
||||
/* Digest the header and data directory, but leave out the image
|
||||
* checksum and the data dirent for the signature.
|
||||
*/
|
||||
ret = crypto_shash_update(desc, pebuf, ctx->image_checksum_offset);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
tmp = ctx->image_checksum_offset + sizeof(uint32_t);
|
||||
ret = crypto_shash_update(desc, pebuf + tmp,
|
||||
ctx->cert_dirent_offset - tmp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
tmp = ctx->cert_dirent_offset + sizeof(struct data_dirent);
|
||||
ret = crypto_shash_update(desc, pebuf + tmp, ctx->header_size - tmp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
canon = kcalloc(ctx->n_sections, sizeof(unsigned), GFP_KERNEL);
|
||||
if (!canon)
|
||||
return -ENOMEM;
|
||||
|
||||
/* We have to canonicalise the section table, so we perform an
|
||||
* insertion sort.
|
||||
*/
|
||||
canon[0] = 0;
|
||||
for (loop = 1; loop < ctx->n_sections; loop++) {
|
||||
for (i = 0; i < loop; i++) {
|
||||
if (pefile_compare_shdrs(&ctx->secs[canon[i]],
|
||||
&ctx->secs[loop]) > 0) {
|
||||
memmove(&canon[i + 1], &canon[i],
|
||||
(loop - i) * sizeof(canon[0]));
|
||||
break;
|
||||
}
|
||||
}
|
||||
canon[i] = loop;
|
||||
}
|
||||
|
||||
hashed_bytes = ctx->header_size;
|
||||
for (loop = 0; loop < ctx->n_sections; loop++) {
|
||||
i = canon[loop];
|
||||
if (ctx->secs[i].raw_data_size == 0)
|
||||
continue;
|
||||
ret = crypto_shash_update(desc,
|
||||
pebuf + ctx->secs[i].data_addr,
|
||||
ctx->secs[i].raw_data_size);
|
||||
if (ret < 0) {
|
||||
kfree(canon);
|
||||
return ret;
|
||||
}
|
||||
hashed_bytes += ctx->secs[i].raw_data_size;
|
||||
}
|
||||
kfree(canon);
|
||||
|
||||
if (pelen > hashed_bytes) {
|
||||
tmp = hashed_bytes + ctx->certs_size;
|
||||
ret = crypto_shash_update(desc,
|
||||
pebuf + hashed_bytes,
|
||||
pelen - tmp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Digest the contents of the PE binary, leaving out the image checksum and the
|
||||
* certificate data block.
|
||||
*/
|
||||
static int pefile_digest_pe(const void *pebuf, unsigned int pelen,
|
||||
struct pefile_context *ctx)
|
||||
{
|
||||
struct crypto_shash *tfm;
|
||||
struct shash_desc *desc;
|
||||
size_t digest_size, desc_size;
|
||||
void *digest;
|
||||
int ret;
|
||||
|
||||
kenter(",%u", ctx->digest_algo);
|
||||
|
||||
/* Allocate the hashing algorithm we're going to need and find out how
|
||||
* big the hash operational data will be.
|
||||
*/
|
||||
tfm = crypto_alloc_shash(hash_algo_name[ctx->digest_algo], 0, 0);
|
||||
if (IS_ERR(tfm))
|
||||
return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm);
|
||||
|
||||
desc_size = crypto_shash_descsize(tfm) + sizeof(*desc);
|
||||
digest_size = crypto_shash_digestsize(tfm);
|
||||
|
||||
if (digest_size != ctx->digest_len) {
|
||||
pr_debug("Digest size mismatch (%zx != %x)\n",
|
||||
digest_size, ctx->digest_len);
|
||||
ret = -EBADMSG;
|
||||
goto error_no_desc;
|
||||
}
|
||||
pr_debug("Digest: desc=%zu size=%zu\n", desc_size, digest_size);
|
||||
|
||||
ret = -ENOMEM;
|
||||
desc = kzalloc(desc_size + digest_size, GFP_KERNEL);
|
||||
if (!desc)
|
||||
goto error_no_desc;
|
||||
|
||||
desc->tfm = tfm;
|
||||
desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
|
||||
ret = crypto_shash_init(desc);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
ret = pefile_digest_pe_contents(pebuf, pelen, ctx, desc);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
digest = (void *)desc + desc_size;
|
||||
ret = crypto_shash_final(desc, digest);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
pr_debug("Digest calc = [%*ph]\n", ctx->digest_len, digest);
|
||||
|
||||
/* Check that the PE file digest matches that in the MSCODE part of the
|
||||
* PKCS#7 certificate.
|
||||
*/
|
||||
if (memcmp(digest, ctx->digest, ctx->digest_len) != 0) {
|
||||
pr_debug("Digest mismatch\n");
|
||||
ret = -EKEYREJECTED;
|
||||
} else {
|
||||
pr_debug("The digests match!\n");
|
||||
}
|
||||
|
||||
error:
|
||||
kfree(desc);
|
||||
error_no_desc:
|
||||
crypto_free_shash(tfm);
|
||||
kleave(" = %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @_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.
|
||||
*
|
||||
* Returns, in order of descending priority:
|
||||
*
|
||||
* (*) -ELIBBAD if the image cannot be parsed, or:
|
||||
*
|
||||
* (*) -EKEYREJECTED if a signature failed to match for which we have a valid
|
||||
* key, or:
|
||||
*
|
||||
* (*) 0 if at least one signature chain intersects with the keys in the trust
|
||||
* keyring, or:
|
||||
*
|
||||
* (*) -ENOPKG if a suitable crypto module couldn't be found for a check on a
|
||||
* chain.
|
||||
*
|
||||
* (*) -ENOKEY if we couldn't find a match for any of the signature chains in
|
||||
* the message.
|
||||
*
|
||||
* May also return -ENOMEM.
|
||||
*/
|
||||
int verify_pefile_signature(const void *pebuf, unsigned pelen,
|
||||
struct key *trusted_keyring, bool *_trusted)
|
||||
{
|
||||
struct pkcs7_message *pkcs7;
|
||||
struct pefile_context ctx;
|
||||
const void *data;
|
||||
size_t datalen;
|
||||
int ret;
|
||||
|
||||
kenter("");
|
||||
|
||||
memset(&ctx, 0, sizeof(ctx));
|
||||
ret = pefile_parse_binary(pebuf, pelen, &ctx);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = pefile_strip_sig_wrapper(pebuf, &ctx);
|
||||
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);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
pr_debug("Digest: %u [%*ph]\n",
|
||||
ctx.digest_len, ctx.digest_len, ctx.digest);
|
||||
|
||||
/* Generate the digest and check against the PKCS7 certificate
|
||||
* contents.
|
||||
*/
|
||||
ret = pefile_digest_pe(pebuf, pelen, &ctx);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
ret = pkcs7_verify(pkcs7);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
ret = pkcs7_validate_trust(pkcs7, trusted_keyring, _trusted);
|
||||
|
||||
error:
|
||||
pkcs7_free_message(ctx.pkcs7);
|
||||
return ret;
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/* PE Binary parser bits
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/verify_pefile.h>
|
||||
#include <crypto/pkcs7.h>
|
||||
#include <crypto/hash_info.h>
|
||||
|
||||
struct pefile_context {
|
||||
unsigned header_size;
|
||||
unsigned image_checksum_offset;
|
||||
unsigned cert_dirent_offset;
|
||||
unsigned n_data_dirents;
|
||||
unsigned n_sections;
|
||||
unsigned certs_size;
|
||||
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 */
|
||||
unsigned digest_len; /* Digest length */
|
||||
enum hash_algo digest_algo; /* Digest algorithm */
|
||||
};
|
||||
|
||||
#define kenter(FMT, ...) \
|
||||
pr_devel("==> %s("FMT")\n", __func__, ##__VA_ARGS__)
|
||||
#define kleave(FMT, ...) \
|
||||
pr_devel("<== %s()"FMT"\n", __func__, ##__VA_ARGS__)
|
||||
|
||||
/*
|
||||
* mscode_parser.c
|
||||
*/
|
||||
extern int mscode_parse(struct pefile_context *ctx);
|
|
@ -6,7 +6,7 @@ Certificate ::= SEQUENCE {
|
|||
|
||||
TBSCertificate ::= SEQUENCE {
|
||||
version [ 0 ] Version DEFAULT,
|
||||
serialNumber CertificateSerialNumber,
|
||||
serialNumber CertificateSerialNumber ({ x509_note_serial }),
|
||||
signature AlgorithmIdentifier ({ x509_note_pkey_algo }),
|
||||
issuer Name ({ x509_note_issuer }),
|
||||
validity Validity,
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#define pr_fmt(fmt) "X.509: "fmt
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/oid_registry.h>
|
||||
|
@ -52,6 +53,7 @@ void x509_free_certificate(struct x509_certificate *cert)
|
|||
kfree(cert);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(x509_free_certificate);
|
||||
|
||||
/*
|
||||
* Parse an X.509 certificate
|
||||
|
@ -97,6 +99,7 @@ error_no_ctx:
|
|||
error_no_cert:
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(x509_cert_parse);
|
||||
|
||||
/*
|
||||
* Note an OID when we find one for later processing when we know how
|
||||
|
@ -210,6 +213,19 @@ int x509_note_signature(void *context, size_t hdrlen,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note the certificate serial number
|
||||
*/
|
||||
int x509_note_serial(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct x509_parse_context *ctx = context;
|
||||
ctx->cert->raw_serial = value;
|
||||
ctx->cert->raw_serial_size = vlen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note some of the name segments from which we'll fabricate a name.
|
||||
*/
|
||||
|
@ -322,6 +338,8 @@ int x509_note_issuer(void *context, size_t hdrlen,
|
|||
const void *value, size_t vlen)
|
||||
{
|
||||
struct x509_parse_context *ctx = context;
|
||||
ctx->cert->raw_issuer = value;
|
||||
ctx->cert->raw_issuer_size = vlen;
|
||||
return x509_fabricate_name(ctx, hdrlen, tag, &ctx->cert->issuer, vlen);
|
||||
}
|
||||
|
||||
|
@ -330,6 +348,8 @@ int x509_note_subject(void *context, size_t hdrlen,
|
|||
const void *value, size_t vlen)
|
||||
{
|
||||
struct x509_parse_context *ctx = context;
|
||||
ctx->cert->raw_subject = value;
|
||||
ctx->cert->raw_subject_size = vlen;
|
||||
return x509_fabricate_name(ctx, hdrlen, tag, &ctx->cert->subject, vlen);
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,9 @@
|
|||
|
||||
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 */
|
||||
char *issuer; /* Name of certificate issuer */
|
||||
char *subject; /* Name of certificate subject */
|
||||
char *fingerprint; /* Key fingerprint as hex */
|
||||
|
@ -25,7 +27,16 @@ struct x509_certificate {
|
|||
unsigned tbs_size; /* Size of signed data */
|
||||
unsigned raw_sig_size; /* Size of sigature */
|
||||
const void *raw_sig; /* Signature data */
|
||||
struct public_key_signature sig; /* Signature parameters */
|
||||
const void *raw_serial; /* Raw serial number in ASN.1 */
|
||||
unsigned raw_serial_size;
|
||||
unsigned raw_issuer_size;
|
||||
const void *raw_issuer; /* Raw issuer name in ASN.1 */
|
||||
const void *raw_subject; /* Raw subject name in ASN.1 */
|
||||
unsigned raw_subject_size;
|
||||
unsigned index;
|
||||
bool seen; /* Infinite recursion prevention */
|
||||
bool verified;
|
||||
bool trusted;
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -18,11 +18,80 @@
|
|||
#include <linux/asn1_decoder.h>
|
||||
#include <keys/asymmetric-subtype.h>
|
||||
#include <keys/asymmetric-parser.h>
|
||||
#include <keys/system_keyring.h>
|
||||
#include <crypto/hash.h>
|
||||
#include "asymmetric_keys.h"
|
||||
#include "public_key.h"
|
||||
#include "x509_parser.h"
|
||||
|
||||
static bool use_builtin_keys;
|
||||
static char *ca_keyid;
|
||||
|
||||
#ifndef MODULE
|
||||
static int __init ca_keys_setup(char *str)
|
||||
{
|
||||
if (!str) /* default system keyring */
|
||||
return 1;
|
||||
|
||||
if (strncmp(str, "id:", 3) == 0)
|
||||
ca_keyid = str; /* owner key 'id:xxxxxx' */
|
||||
else if (strcmp(str, "builtin") == 0)
|
||||
use_builtin_keys = true;
|
||||
|
||||
return 1;
|
||||
}
|
||||
__setup("ca_keys=", ca_keys_setup);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Find a key in the given keyring by issuer and authority.
|
||||
*/
|
||||
static struct key *x509_request_asymmetric_key(struct key *keyring,
|
||||
const char *signer,
|
||||
size_t signer_len,
|
||||
const char *authority,
|
||||
size_t auth_len)
|
||||
{
|
||||
key_ref_t key;
|
||||
char *id;
|
||||
|
||||
/* Construct an identifier. */
|
||||
id = kmalloc(signer_len + 2 + auth_len + 1, GFP_KERNEL);
|
||||
if (!id)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
memcpy(id, signer, signer_len);
|
||||
id[signer_len + 0] = ':';
|
||||
id[signer_len + 1] = ' ';
|
||||
memcpy(id + signer_len + 2, authority, auth_len);
|
||||
id[signer_len + 2 + auth_len] = 0;
|
||||
|
||||
pr_debug("Look up: \"%s\"\n", id);
|
||||
|
||||
key = keyring_search(make_key_ref(keyring, 1),
|
||||
&key_type_asymmetric, id);
|
||||
if (IS_ERR(key))
|
||||
pr_debug("Request for module key '%s' err %ld\n",
|
||||
id, PTR_ERR(key));
|
||||
kfree(id);
|
||||
|
||||
if (IS_ERR(key)) {
|
||||
switch (PTR_ERR(key)) {
|
||||
/* Hide some search errors */
|
||||
case -EACCES:
|
||||
case -ENOTDIR:
|
||||
case -EAGAIN:
|
||||
return ERR_PTR(-ENOKEY);
|
||||
default:
|
||||
return ERR_CAST(key);
|
||||
}
|
||||
}
|
||||
|
||||
pr_devel("<==%s() = 0 [%x]\n", __func__,
|
||||
key_serial(key_ref_to_ptr(key)));
|
||||
return key_ref_to_ptr(key);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up the signature parameters in an X.509 certificate. This involves
|
||||
* digesting the signed data and extracting the signature.
|
||||
|
@ -102,6 +171,40 @@ int x509_check_signature(const struct public_key *pub,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(x509_check_signature);
|
||||
|
||||
/*
|
||||
* 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 key *key;
|
||||
int ret = 1;
|
||||
|
||||
if (!trust_keyring)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (ca_keyid && !asymmetric_keyid_match(cert->authority, ca_keyid))
|
||||
return -EPERM;
|
||||
|
||||
key = x509_request_asymmetric_key(trust_keyring,
|
||||
cert->issuer, strlen(cert->issuer),
|
||||
cert->authority,
|
||||
strlen(cert->authority));
|
||||
if (!IS_ERR(key)) {
|
||||
if (!use_builtin_keys
|
||||
|| test_bit(KEY_FLAG_BUILTIN, &key->flags))
|
||||
ret = x509_check_signature(key->payload.data, cert);
|
||||
key_put(key);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempt to parse a data blob for a key as an X509 certificate.
|
||||
*/
|
||||
|
@ -155,9 +258,13 @@ static int x509_key_preparse(struct key_preparsed_payload *prep)
|
|||
/* Check the signature on the key if it appears to be self-signed */
|
||||
if (!cert->authority ||
|
||||
strcmp(cert->fingerprint, cert->authority) == 0) {
|
||||
ret = x509_check_signature(cert->pub, cert);
|
||||
ret = x509_check_signature(cert->pub, cert); /* self-signed */
|
||||
if (ret < 0)
|
||||
goto error_free_cert;
|
||||
} else if (!prep->trusted) {
|
||||
ret = x509_validate_trust(cert, get_system_trusted_keyring());
|
||||
if (!ret)
|
||||
prep->trusted = 1;
|
||||
}
|
||||
|
||||
/* Propose a description */
|
||||
|
@ -177,7 +284,7 @@ static int x509_key_preparse(struct key_preparsed_payload *prep)
|
|||
__module_get(public_key_subtype.owner);
|
||||
prep->type_data[0] = &public_key_subtype;
|
||||
prep->type_data[1] = cert->fingerprint;
|
||||
prep->payload = cert->pub;
|
||||
prep->payload[0] = cert->pub;
|
||||
prep->description = desc;
|
||||
prep->quotalen = 100;
|
||||
|
||||
|
|
|
@ -174,7 +174,9 @@ static int nfs_map_numeric_to_string(__u32 id, char *buf, size_t buflen)
|
|||
|
||||
static struct key_type key_type_id_resolver = {
|
||||
.name = "id_resolver",
|
||||
.instantiate = user_instantiate,
|
||||
.preparse = user_preparse,
|
||||
.free_preparse = user_free_preparse,
|
||||
.instantiate = generic_key_instantiate,
|
||||
.match = user_match,
|
||||
.revoke = user_revoke,
|
||||
.destroy = user_destroy,
|
||||
|
@ -282,6 +284,8 @@ static struct key *nfs_idmap_request_key(const char *name, size_t namelen,
|
|||
desc, "", 0, idmap);
|
||||
mutex_unlock(&idmap->idmap_mutex);
|
||||
}
|
||||
if (!IS_ERR(rkey))
|
||||
set_bit(KEY_FLAG_ROOT_CAN_INVAL, &rkey->flags);
|
||||
|
||||
kfree(desc);
|
||||
return rkey;
|
||||
|
@ -394,7 +398,9 @@ static const struct rpc_pipe_ops idmap_upcall_ops = {
|
|||
|
||||
static struct key_type key_type_id_resolver_legacy = {
|
||||
.name = "id_legacy",
|
||||
.instantiate = user_instantiate,
|
||||
.preparse = user_preparse,
|
||||
.free_preparse = user_free_preparse,
|
||||
.instantiate = generic_key_instantiate,
|
||||
.match = user_match,
|
||||
.revoke = user_revoke,
|
||||
.destroy = user_destroy,
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
/* PKCS#7 crypto data parser
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
struct key;
|
||||
struct pkcs7_message;
|
||||
|
||||
/*
|
||||
* pkcs7_parser.c
|
||||
*/
|
||||
extern struct pkcs7_message *pkcs7_parse_message(const void *data,
|
||||
size_t datalen);
|
||||
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);
|
||||
|
||||
/*
|
||||
* pkcs7_trust.c
|
||||
*/
|
||||
extern int pkcs7_validate_trust(struct pkcs7_message *pkcs7,
|
||||
struct key *trust_keyring,
|
||||
bool *_trusted);
|
||||
|
||||
/*
|
||||
* pkcs7_verify.c
|
||||
*/
|
||||
extern int pkcs7_verify(struct pkcs7_message *pkcs7);
|
|
@ -16,7 +16,8 @@
|
|||
|
||||
extern struct key_type key_type_big_key;
|
||||
|
||||
extern int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep);
|
||||
extern int big_key_preparse(struct key_preparsed_payload *prep);
|
||||
extern void big_key_free_preparse(struct key_preparsed_payload *prep);
|
||||
extern void big_key_revoke(struct key *key);
|
||||
extern void big_key_destroy(struct key *key);
|
||||
extern void big_key_describe(const struct key *big_key, struct seq_file *m);
|
||||
|
|
|
@ -17,7 +17,15 @@
|
|||
#include <linux/key.h>
|
||||
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _KEYS_SYSTEM_KEYRING_H */
|
||||
|
|
|
@ -37,7 +37,8 @@ extern struct key_type key_type_logon;
|
|||
|
||||
struct key_preparsed_payload;
|
||||
|
||||
extern int user_instantiate(struct key *key, struct key_preparsed_payload *prep);
|
||||
extern int user_preparse(struct key_preparsed_payload *prep);
|
||||
extern void user_free_preparse(struct key_preparsed_payload *prep);
|
||||
extern int user_update(struct key *key, struct key_preparsed_payload *prep);
|
||||
extern int user_match(const struct key *key, const void *criterion);
|
||||
extern void user_revoke(struct key *key);
|
||||
|
|
|
@ -41,10 +41,11 @@ struct key_construction {
|
|||
struct key_preparsed_payload {
|
||||
char *description; /* Proposed key description (or NULL) */
|
||||
void *type_data[2]; /* Private key-type data */
|
||||
void *payload; /* Proposed payload */
|
||||
void *payload[2]; /* Proposed payload */
|
||||
const void *data; /* Raw data */
|
||||
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 */
|
||||
};
|
||||
|
||||
|
@ -159,5 +160,7 @@ static inline int key_negate_and_link(struct key *key,
|
|||
return key_reject_and_link(key, timeout, ENOKEY, keyring, instkey);
|
||||
}
|
||||
|
||||
extern int generic_key_instantiate(struct key *key, struct key_preparsed_payload *prep);
|
||||
|
||||
#endif /* CONFIG_KEYS */
|
||||
#endif /* _LINUX_KEY_TYPE_H */
|
||||
|
|
|
@ -170,6 +170,8 @@ struct key {
|
|||
#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 */
|
||||
|
||||
/* the key type and key description string
|
||||
* - the desc is used to match a key against search criteria
|
||||
|
|
|
@ -52,9 +52,15 @@ enum OID {
|
|||
OID_md4, /* 1.2.840.113549.2.4 */
|
||||
OID_md5, /* 1.2.840.113549.2.5 */
|
||||
|
||||
OID_certAuthInfoAccess, /* 1.3.6.1.5.5.7.1.1 */
|
||||
/* Microsoft Authenticode & Software Publishing */
|
||||
OID_msIndirectData, /* 1.3.6.1.4.1.311.2.1.4 */
|
||||
OID_msPeImageDataObjId, /* 1.3.6.1.4.1.311.2.1.15 */
|
||||
OID_msIndividualSPKeyPurpose, /* 1.3.6.1.4.1.311.2.1.21 */
|
||||
OID_msOutlookExpress, /* 1.3.6.1.4.1.311.16.4 */
|
||||
|
||||
OID_certAuthInfoAccess, /* 1.3.6.1.5.5.7.1.1 */
|
||||
OID_sha1, /* 1.3.14.3.2.26 */
|
||||
OID_sha256, /* 2.16.840.1.101.3.4.2.1 */
|
||||
|
||||
/* Distinguished Name attribute IDs [RFC 2256] */
|
||||
OID_commonName, /* 2.5.4.3 */
|
||||
|
|
|
@ -0,0 +1,448 @@
|
|||
/*
|
||||
* Copyright 2011 Red Hat, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author(s): Peter Jones <pjones@redhat.com>
|
||||
*/
|
||||
#ifndef __LINUX_PE_H
|
||||
#define __LINUX_PE_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#define MZ_MAGIC 0x5a4d /* "MZ" */
|
||||
|
||||
struct mz_hdr {
|
||||
uint16_t magic; /* MZ_MAGIC */
|
||||
uint16_t lbsize; /* size of last used block */
|
||||
uint16_t blocks; /* pages in file, 0x3 */
|
||||
uint16_t relocs; /* relocations */
|
||||
uint16_t hdrsize; /* header size in "paragraphs" */
|
||||
uint16_t min_extra_pps; /* .bss */
|
||||
uint16_t max_extra_pps; /* runtime limit for the arena size */
|
||||
uint16_t ss; /* relative stack segment */
|
||||
uint16_t sp; /* initial %sp register */
|
||||
uint16_t checksum; /* word checksum */
|
||||
uint16_t ip; /* initial %ip register */
|
||||
uint16_t cs; /* initial %cs relative to load segment */
|
||||
uint16_t reloc_table_offset; /* offset of the first relocation */
|
||||
uint16_t overlay_num; /* overlay number. set to 0. */
|
||||
uint16_t reserved0[4]; /* reserved */
|
||||
uint16_t oem_id; /* oem identifier */
|
||||
uint16_t oem_info; /* oem specific */
|
||||
uint16_t reserved1[10]; /* reserved */
|
||||
uint32_t peaddr; /* address of pe header */
|
||||
char message[64]; /* message to print */
|
||||
};
|
||||
|
||||
struct mz_reloc {
|
||||
uint16_t offset;
|
||||
uint16_t segment;
|
||||
};
|
||||
|
||||
#define PE_MAGIC 0x00004550 /* "PE\0\0" */
|
||||
#define PE_OPT_MAGIC_PE32 0x010b
|
||||
#define PE_OPT_MAGIC_PE32_ROM 0x0107
|
||||
#define PE_OPT_MAGIC_PE32PLUS 0x020b
|
||||
|
||||
/* machine type */
|
||||
#define IMAGE_FILE_MACHINE_UNKNOWN 0x0000
|
||||
#define IMAGE_FILE_MACHINE_AM33 0x01d3
|
||||
#define IMAGE_FILE_MACHINE_AMD64 0x8664
|
||||
#define IMAGE_FILE_MACHINE_ARM 0x01c0
|
||||
#define IMAGE_FILE_MACHINE_ARMV7 0x01c4
|
||||
#define IMAGE_FILE_MACHINE_EBC 0x0ebc
|
||||
#define IMAGE_FILE_MACHINE_I386 0x014c
|
||||
#define IMAGE_FILE_MACHINE_IA64 0x0200
|
||||
#define IMAGE_FILE_MACHINE_M32R 0x9041
|
||||
#define IMAGE_FILE_MACHINE_MIPS16 0x0266
|
||||
#define IMAGE_FILE_MACHINE_MIPSFPU 0x0366
|
||||
#define IMAGE_FILE_MACHINE_MIPSFPU16 0x0466
|
||||
#define IMAGE_FILE_MACHINE_POWERPC 0x01f0
|
||||
#define IMAGE_FILE_MACHINE_POWERPCFP 0x01f1
|
||||
#define IMAGE_FILE_MACHINE_R4000 0x0166
|
||||
#define IMAGE_FILE_MACHINE_SH3 0x01a2
|
||||
#define IMAGE_FILE_MACHINE_SH3DSP 0x01a3
|
||||
#define IMAGE_FILE_MACHINE_SH3E 0x01a4
|
||||
#define IMAGE_FILE_MACHINE_SH4 0x01a6
|
||||
#define IMAGE_FILE_MACHINE_SH5 0x01a8
|
||||
#define IMAGE_FILE_MACHINE_THUMB 0x01c2
|
||||
#define IMAGE_FILE_MACHINE_WCEMIPSV2 0x0169
|
||||
|
||||
/* flags */
|
||||
#define IMAGE_FILE_RELOCS_STRIPPED 0x0001
|
||||
#define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002
|
||||
#define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004
|
||||
#define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008
|
||||
#define IMAGE_FILE_AGGRESSIVE_WS_TRIM 0x0010
|
||||
#define IMAGE_FILE_LARGE_ADDRESS_AWARE 0x0020
|
||||
#define IMAGE_FILE_16BIT_MACHINE 0x0040
|
||||
#define IMAGE_FILE_BYTES_REVERSED_LO 0x0080
|
||||
#define IMAGE_FILE_32BIT_MACHINE 0x0100
|
||||
#define IMAGE_FILE_DEBUG_STRIPPED 0x0200
|
||||
#define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP 0x0400
|
||||
#define IMAGE_FILE_NET_RUN_FROM_SWAP 0x0800
|
||||
#define IMAGE_FILE_SYSTEM 0x1000
|
||||
#define IMAGE_FILE_DLL 0x2000
|
||||
#define IMAGE_FILE_UP_SYSTEM_ONLY 0x4000
|
||||
#define IMAGE_FILE_BYTES_REVERSED_HI 0x8000
|
||||
|
||||
struct pe_hdr {
|
||||
uint32_t magic; /* PE magic */
|
||||
uint16_t machine; /* machine type */
|
||||
uint16_t sections; /* number of sections */
|
||||
uint32_t timestamp; /* time_t */
|
||||
uint32_t symbol_table; /* symbol table offset */
|
||||
uint32_t symbols; /* number of symbols */
|
||||
uint16_t opt_hdr_size; /* size of optional header */
|
||||
uint16_t flags; /* flags */
|
||||
};
|
||||
|
||||
#define IMAGE_FILE_OPT_ROM_MAGIC 0x107
|
||||
#define IMAGE_FILE_OPT_PE32_MAGIC 0x10b
|
||||
#define IMAGE_FILE_OPT_PE32_PLUS_MAGIC 0x20b
|
||||
|
||||
#define IMAGE_SUBSYSTEM_UNKNOWN 0
|
||||
#define IMAGE_SUBSYSTEM_NATIVE 1
|
||||
#define IMAGE_SUBSYSTEM_WINDOWS_GUI 2
|
||||
#define IMAGE_SUBSYSTEM_WINDOWS_CUI 3
|
||||
#define IMAGE_SUBSYSTEM_POSIX_CUI 7
|
||||
#define IMAGE_SUBSYSTEM_WINDOWS_CE_GUI 9
|
||||
#define IMAGE_SUBSYSTEM_EFI_APPLICATION 10
|
||||
#define IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER 11
|
||||
#define IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER 12
|
||||
#define IMAGE_SUBSYSTEM_EFI_ROM_IMAGE 13
|
||||
#define IMAGE_SUBSYSTEM_XBOX 14
|
||||
|
||||
#define IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE 0x0040
|
||||
#define IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY 0x0080
|
||||
#define IMAGE_DLL_CHARACTERISTICS_NX_COMPAT 0x0100
|
||||
#define IMAGE_DLLCHARACTERISTICS_NO_ISOLATION 0x0200
|
||||
#define IMAGE_DLLCHARACTERISTICS_NO_SEH 0x0400
|
||||
#define IMAGE_DLLCHARACTERISTICS_NO_BIND 0x0800
|
||||
#define IMAGE_DLLCHARACTERISTICS_WDM_DRIVER 0x2000
|
||||
#define IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE 0x8000
|
||||
|
||||
/* the fact that pe32 isn't padded where pe32+ is 64-bit means union won't
|
||||
* work right. vomit. */
|
||||
struct pe32_opt_hdr {
|
||||
/* "standard" header */
|
||||
uint16_t magic; /* file type */
|
||||
uint8_t ld_major; /* linker major version */
|
||||
uint8_t ld_minor; /* linker minor version */
|
||||
uint32_t text_size; /* size of text section(s) */
|
||||
uint32_t data_size; /* size of data section(s) */
|
||||
uint32_t bss_size; /* size of bss section(s) */
|
||||
uint32_t entry_point; /* file offset of entry point */
|
||||
uint32_t code_base; /* relative code addr in ram */
|
||||
uint32_t data_base; /* relative data addr in ram */
|
||||
/* "windows" header */
|
||||
uint32_t image_base; /* preferred load address */
|
||||
uint32_t section_align; /* alignment in bytes */
|
||||
uint32_t file_align; /* file alignment in bytes */
|
||||
uint16_t os_major; /* major OS version */
|
||||
uint16_t os_minor; /* minor OS version */
|
||||
uint16_t image_major; /* major image version */
|
||||
uint16_t image_minor; /* minor image version */
|
||||
uint16_t subsys_major; /* major subsystem version */
|
||||
uint16_t subsys_minor; /* minor subsystem version */
|
||||
uint32_t win32_version; /* reserved, must be 0 */
|
||||
uint32_t image_size; /* image size */
|
||||
uint32_t header_size; /* header size rounded up to
|
||||
file_align */
|
||||
uint32_t csum; /* checksum */
|
||||
uint16_t subsys; /* subsystem */
|
||||
uint16_t dll_flags; /* more flags! */
|
||||
uint32_t stack_size_req;/* amt of stack requested */
|
||||
uint32_t stack_size; /* amt of stack required */
|
||||
uint32_t heap_size_req; /* amt of heap requested */
|
||||
uint32_t heap_size; /* amt of heap required */
|
||||
uint32_t loader_flags; /* reserved, must be 0 */
|
||||
uint32_t data_dirs; /* number of data dir entries */
|
||||
};
|
||||
|
||||
struct pe32plus_opt_hdr {
|
||||
uint16_t magic; /* file type */
|
||||
uint8_t ld_major; /* linker major version */
|
||||
uint8_t ld_minor; /* linker minor version */
|
||||
uint32_t text_size; /* size of text section(s) */
|
||||
uint32_t data_size; /* size of data section(s) */
|
||||
uint32_t bss_size; /* size of bss section(s) */
|
||||
uint32_t entry_point; /* file offset of entry point */
|
||||
uint32_t code_base; /* relative code addr in ram */
|
||||
/* "windows" header */
|
||||
uint64_t image_base; /* preferred load address */
|
||||
uint32_t section_align; /* alignment in bytes */
|
||||
uint32_t file_align; /* file alignment in bytes */
|
||||
uint16_t os_major; /* major OS version */
|
||||
uint16_t os_minor; /* minor OS version */
|
||||
uint16_t image_major; /* major image version */
|
||||
uint16_t image_minor; /* minor image version */
|
||||
uint16_t subsys_major; /* major subsystem version */
|
||||
uint16_t subsys_minor; /* minor subsystem version */
|
||||
uint32_t win32_version; /* reserved, must be 0 */
|
||||
uint32_t image_size; /* image size */
|
||||
uint32_t header_size; /* header size rounded up to
|
||||
file_align */
|
||||
uint32_t csum; /* checksum */
|
||||
uint16_t subsys; /* subsystem */
|
||||
uint16_t dll_flags; /* more flags! */
|
||||
uint64_t stack_size_req;/* amt of stack requested */
|
||||
uint64_t stack_size; /* amt of stack required */
|
||||
uint64_t heap_size_req; /* amt of heap requested */
|
||||
uint64_t heap_size; /* amt of heap required */
|
||||
uint32_t loader_flags; /* reserved, must be 0 */
|
||||
uint32_t data_dirs; /* number of data dir entries */
|
||||
};
|
||||
|
||||
struct data_dirent {
|
||||
uint32_t virtual_address; /* relative to load address */
|
||||
uint32_t size;
|
||||
};
|
||||
|
||||
struct data_directory {
|
||||
struct data_dirent exports; /* .edata */
|
||||
struct data_dirent imports; /* .idata */
|
||||
struct data_dirent resources; /* .rsrc */
|
||||
struct data_dirent exceptions; /* .pdata */
|
||||
struct data_dirent certs; /* certs */
|
||||
struct data_dirent base_relocations; /* .reloc */
|
||||
struct data_dirent debug; /* .debug */
|
||||
struct data_dirent arch; /* reservered */
|
||||
struct data_dirent global_ptr; /* global pointer reg. Size=0 */
|
||||
struct data_dirent tls; /* .tls */
|
||||
struct data_dirent load_config; /* load configuration structure */
|
||||
struct data_dirent bound_imports; /* no idea */
|
||||
struct data_dirent import_addrs; /* import address table */
|
||||
struct data_dirent delay_imports; /* delay-load import table */
|
||||
struct data_dirent clr_runtime_hdr; /* .cor (object only) */
|
||||
struct data_dirent reserved;
|
||||
};
|
||||
|
||||
struct section_header {
|
||||
char name[8]; /* name or "/12\0" string tbl offset */
|
||||
uint32_t virtual_size; /* size of loaded section in ram */
|
||||
uint32_t virtual_address; /* relative virtual address */
|
||||
uint32_t raw_data_size; /* size of the section */
|
||||
uint32_t data_addr; /* file pointer to first page of sec */
|
||||
uint32_t relocs; /* file pointer to relocation entries */
|
||||
uint32_t line_numbers; /* line numbers! */
|
||||
uint16_t num_relocs; /* number of relocations */
|
||||
uint16_t num_lin_numbers; /* srsly. */
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
/* they actually defined 0x00000000 as well, but I think we'll skip that one. */
|
||||
#define IMAGE_SCN_RESERVED_0 0x00000001
|
||||
#define IMAGE_SCN_RESERVED_1 0x00000002
|
||||
#define IMAGE_SCN_RESERVED_2 0x00000004
|
||||
#define IMAGE_SCN_TYPE_NO_PAD 0x00000008 /* don't pad - obsolete */
|
||||
#define IMAGE_SCN_RESERVED_3 0x00000010
|
||||
#define IMAGE_SCN_CNT_CODE 0x00000020 /* .text */
|
||||
#define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040 /* .data */
|
||||
#define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080 /* .bss */
|
||||
#define IMAGE_SCN_LNK_OTHER 0x00000100 /* reserved */
|
||||
#define IMAGE_SCN_LNK_INFO 0x00000200 /* .drectve comments */
|
||||
#define IMAGE_SCN_RESERVED_4 0x00000400
|
||||
#define IMAGE_SCN_LNK_REMOVE 0x00000800 /* .o only - scn to be rm'd*/
|
||||
#define IMAGE_SCN_LNK_COMDAT 0x00001000 /* .o only - COMDAT data */
|
||||
#define IMAGE_SCN_RESERVED_5 0x00002000 /* spec omits this */
|
||||
#define IMAGE_SCN_RESERVED_6 0x00004000 /* spec omits this */
|
||||
#define IMAGE_SCN_GPREL 0x00008000 /* global pointer referenced data */
|
||||
/* spec lists 0x20000 twice, I suspect they meant 0x10000 for one of them */
|
||||
#define IMAGE_SCN_MEM_PURGEABLE 0x00010000 /* reserved for "future" use */
|
||||
#define IMAGE_SCN_16BIT 0x00020000 /* reserved for "future" use */
|
||||
#define IMAGE_SCN_LOCKED 0x00040000 /* reserved for "future" use */
|
||||
#define IMAGE_SCN_PRELOAD 0x00080000 /* reserved for "future" use */
|
||||
/* and here they just stuck a 1-byte integer in the middle of a bitfield */
|
||||
#define IMAGE_SCN_ALIGN_1BYTES 0x00100000 /* it does what it says on the box */
|
||||
#define IMAGE_SCN_ALIGN_2BYTES 0x00200000
|
||||
#define IMAGE_SCN_ALIGN_4BYTES 0x00300000
|
||||
#define IMAGE_SCN_ALIGN_8BYTES 0x00400000
|
||||
#define IMAGE_SCN_ALIGN_16BYTES 0x00500000
|
||||
#define IMAGE_SCN_ALIGN_32BYTES 0x00600000
|
||||
#define IMAGE_SCN_ALIGN_64BYTES 0x00700000
|
||||
#define IMAGE_SCN_ALIGN_128BYTES 0x00800000
|
||||
#define IMAGE_SCN_ALIGN_256BYTES 0x00900000
|
||||
#define IMAGE_SCN_ALIGN_512BYTES 0x00a00000
|
||||
#define IMAGE_SCN_ALIGN_1024BYTES 0x00b00000
|
||||
#define IMAGE_SCN_ALIGN_2048BYTES 0x00c00000
|
||||
#define IMAGE_SCN_ALIGN_4096BYTES 0x00d00000
|
||||
#define IMAGE_SCN_ALIGN_8192BYTES 0x00e00000
|
||||
#define IMAGE_SCN_LNK_NRELOC_OVFL 0x01000000 /* extended relocations */
|
||||
#define IMAGE_SCN_MEM_DISCARDABLE 0x02000000 /* scn can be discarded */
|
||||
#define IMAGE_SCN_MEM_NOT_CACHED 0x04000000 /* cannot be cached */
|
||||
#define IMAGE_SCN_MEM_NOT_PAGED 0x08000000 /* not pageable */
|
||||
#define IMAGE_SCN_MEM_SHARED 0x10000000 /* can be shared */
|
||||
#define IMAGE_SCN_MEM_EXECUTE 0x20000000 /* can be executed as code */
|
||||
#define IMAGE_SCN_MEM_READ 0x40000000 /* readable */
|
||||
#define IMAGE_SCN_MEM_WRITE 0x80000000 /* writeable */
|
||||
|
||||
enum x64_coff_reloc_type {
|
||||
IMAGE_REL_AMD64_ABSOLUTE = 0,
|
||||
IMAGE_REL_AMD64_ADDR64,
|
||||
IMAGE_REL_AMD64_ADDR32,
|
||||
IMAGE_REL_AMD64_ADDR32N,
|
||||
IMAGE_REL_AMD64_REL32,
|
||||
IMAGE_REL_AMD64_REL32_1,
|
||||
IMAGE_REL_AMD64_REL32_2,
|
||||
IMAGE_REL_AMD64_REL32_3,
|
||||
IMAGE_REL_AMD64_REL32_4,
|
||||
IMAGE_REL_AMD64_REL32_5,
|
||||
IMAGE_REL_AMD64_SECTION,
|
||||
IMAGE_REL_AMD64_SECREL,
|
||||
IMAGE_REL_AMD64_SECREL7,
|
||||
IMAGE_REL_AMD64_TOKEN,
|
||||
IMAGE_REL_AMD64_SREL32,
|
||||
IMAGE_REL_AMD64_PAIR,
|
||||
IMAGE_REL_AMD64_SSPAN32,
|
||||
};
|
||||
|
||||
enum arm_coff_reloc_type {
|
||||
IMAGE_REL_ARM_ABSOLUTE,
|
||||
IMAGE_REL_ARM_ADDR32,
|
||||
IMAGE_REL_ARM_ADDR32N,
|
||||
IMAGE_REL_ARM_BRANCH2,
|
||||
IMAGE_REL_ARM_BRANCH1,
|
||||
IMAGE_REL_ARM_SECTION,
|
||||
IMAGE_REL_ARM_SECREL,
|
||||
};
|
||||
|
||||
enum sh_coff_reloc_type {
|
||||
IMAGE_REL_SH3_ABSOLUTE,
|
||||
IMAGE_REL_SH3_DIRECT16,
|
||||
IMAGE_REL_SH3_DIRECT32,
|
||||
IMAGE_REL_SH3_DIRECT8,
|
||||
IMAGE_REL_SH3_DIRECT8_WORD,
|
||||
IMAGE_REL_SH3_DIRECT8_LONG,
|
||||
IMAGE_REL_SH3_DIRECT4,
|
||||
IMAGE_REL_SH3_DIRECT4_WORD,
|
||||
IMAGE_REL_SH3_DIRECT4_LONG,
|
||||
IMAGE_REL_SH3_PCREL8_WORD,
|
||||
IMAGE_REL_SH3_PCREL8_LONG,
|
||||
IMAGE_REL_SH3_PCREL12_WORD,
|
||||
IMAGE_REL_SH3_STARTOF_SECTION,
|
||||
IMAGE_REL_SH3_SIZEOF_SECTION,
|
||||
IMAGE_REL_SH3_SECTION,
|
||||
IMAGE_REL_SH3_SECREL,
|
||||
IMAGE_REL_SH3_DIRECT32_NB,
|
||||
IMAGE_REL_SH3_GPREL4_LONG,
|
||||
IMAGE_REL_SH3_TOKEN,
|
||||
IMAGE_REL_SHM_PCRELPT,
|
||||
IMAGE_REL_SHM_REFLO,
|
||||
IMAGE_REL_SHM_REFHALF,
|
||||
IMAGE_REL_SHM_RELLO,
|
||||
IMAGE_REL_SHM_RELHALF,
|
||||
IMAGE_REL_SHM_PAIR,
|
||||
IMAGE_REL_SHM_NOMODE,
|
||||
};
|
||||
|
||||
enum ppc_coff_reloc_type {
|
||||
IMAGE_REL_PPC_ABSOLUTE,
|
||||
IMAGE_REL_PPC_ADDR64,
|
||||
IMAGE_REL_PPC_ADDR32,
|
||||
IMAGE_REL_PPC_ADDR24,
|
||||
IMAGE_REL_PPC_ADDR16,
|
||||
IMAGE_REL_PPC_ADDR14,
|
||||
IMAGE_REL_PPC_REL24,
|
||||
IMAGE_REL_PPC_REL14,
|
||||
IMAGE_REL_PPC_ADDR32N,
|
||||
IMAGE_REL_PPC_SECREL,
|
||||
IMAGE_REL_PPC_SECTION,
|
||||
IMAGE_REL_PPC_SECREL16,
|
||||
IMAGE_REL_PPC_REFHI,
|
||||
IMAGE_REL_PPC_REFLO,
|
||||
IMAGE_REL_PPC_PAIR,
|
||||
IMAGE_REL_PPC_SECRELLO,
|
||||
IMAGE_REL_PPC_GPREL,
|
||||
IMAGE_REL_PPC_TOKEN,
|
||||
};
|
||||
|
||||
enum x86_coff_reloc_type {
|
||||
IMAGE_REL_I386_ABSOLUTE,
|
||||
IMAGE_REL_I386_DIR16,
|
||||
IMAGE_REL_I386_REL16,
|
||||
IMAGE_REL_I386_DIR32,
|
||||
IMAGE_REL_I386_DIR32NB,
|
||||
IMAGE_REL_I386_SEG12,
|
||||
IMAGE_REL_I386_SECTION,
|
||||
IMAGE_REL_I386_SECREL,
|
||||
IMAGE_REL_I386_TOKEN,
|
||||
IMAGE_REL_I386_SECREL7,
|
||||
IMAGE_REL_I386_REL32,
|
||||
};
|
||||
|
||||
enum ia64_coff_reloc_type {
|
||||
IMAGE_REL_IA64_ABSOLUTE,
|
||||
IMAGE_REL_IA64_IMM14,
|
||||
IMAGE_REL_IA64_IMM22,
|
||||
IMAGE_REL_IA64_IMM64,
|
||||
IMAGE_REL_IA64_DIR32,
|
||||
IMAGE_REL_IA64_DIR64,
|
||||
IMAGE_REL_IA64_PCREL21B,
|
||||
IMAGE_REL_IA64_PCREL21M,
|
||||
IMAGE_REL_IA64_PCREL21F,
|
||||
IMAGE_REL_IA64_GPREL22,
|
||||
IMAGE_REL_IA64_LTOFF22,
|
||||
IMAGE_REL_IA64_SECTION,
|
||||
IMAGE_REL_IA64_SECREL22,
|
||||
IMAGE_REL_IA64_SECREL64I,
|
||||
IMAGE_REL_IA64_SECREL32,
|
||||
IMAGE_REL_IA64_DIR32NB,
|
||||
IMAGE_REL_IA64_SREL14,
|
||||
IMAGE_REL_IA64_SREL22,
|
||||
IMAGE_REL_IA64_SREL32,
|
||||
IMAGE_REL_IA64_UREL32,
|
||||
IMAGE_REL_IA64_PCREL60X,
|
||||
IMAGE_REL_IA64_PCREL60B,
|
||||
IMAGE_REL_IA64_PCREL60F,
|
||||
IMAGE_REL_IA64_PCREL60I,
|
||||
IMAGE_REL_IA64_PCREL60M,
|
||||
IMAGE_REL_IA64_IMMGPREL6,
|
||||
IMAGE_REL_IA64_TOKEN,
|
||||
IMAGE_REL_IA64_GPREL32,
|
||||
IMAGE_REL_IA64_ADDEND,
|
||||
};
|
||||
|
||||
struct coff_reloc {
|
||||
uint32_t virtual_address;
|
||||
uint32_t symbol_table_index;
|
||||
union {
|
||||
enum x64_coff_reloc_type x64_type;
|
||||
enum arm_coff_reloc_type arm_type;
|
||||
enum sh_coff_reloc_type sh_type;
|
||||
enum ppc_coff_reloc_type ppc_type;
|
||||
enum x86_coff_reloc_type x86_type;
|
||||
enum ia64_coff_reloc_type ia64_type;
|
||||
uint16_t data;
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
* Definitions for the contents of the certs data block
|
||||
*/
|
||||
#define WIN_CERT_TYPE_PKCS_SIGNED_DATA 0x0002
|
||||
#define WIN_CERT_TYPE_EFI_OKCS115 0x0EF0
|
||||
#define WIN_CERT_TYPE_EFI_GUID 0x0EF1
|
||||
|
||||
#define WIN_CERT_REVISION_1_0 0x0100
|
||||
#define WIN_CERT_REVISION_2_0 0x0200
|
||||
|
||||
struct win_certificate {
|
||||
uint32_t length;
|
||||
uint16_t revision;
|
||||
uint16_t cert_type;
|
||||
};
|
||||
|
||||
#endif /* __LINUX_PE_H */
|
|
@ -0,0 +1,18 @@
|
|||
/* 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
|
||||
|
||||
extern int verify_pefile_signature(const void *pebuf, unsigned pelen,
|
||||
struct key *trusted_keyring, bool *_trusted);
|
||||
|
||||
#endif /* _LINUX_VERIFY_PEFILE_H */
|
|
@ -89,6 +89,7 @@ static __init int load_system_certificate_list(void)
|
|||
pr_err("Problem loading in-kernel X.509 certificate (%ld)\n",
|
||||
PTR_ERR(key));
|
||||
} else {
|
||||
set_bit(KEY_FLAG_BUILTIN, &key_ref_to_ptr(key)->flags);
|
||||
pr_notice("Loaded X.509 cert '%s'\n",
|
||||
key_ref_to_ptr(key)->description);
|
||||
key_ref_put(key);
|
||||
|
|
|
@ -451,7 +451,8 @@ config MPILIB
|
|||
|
||||
config SIGNATURE
|
||||
tristate
|
||||
depends on KEYS && CRYPTO
|
||||
depends on KEYS
|
||||
select CRYPTO
|
||||
select CRYPTO_SHA1
|
||||
select MPILIB
|
||||
help
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <linux/key-type.h>
|
||||
|
||||
#include <keys/ceph-type.h>
|
||||
#include <keys/user-type.h>
|
||||
#include <linux/ceph/decode.h>
|
||||
#include "crypto.h"
|
||||
|
||||
|
@ -423,8 +424,7 @@ int ceph_encrypt2(struct ceph_crypto_key *secret, void *dst, size_t *dst_len,
|
|||
}
|
||||
}
|
||||
|
||||
static int ceph_key_instantiate(struct key *key,
|
||||
struct key_preparsed_payload *prep)
|
||||
static int ceph_key_preparse(struct key_preparsed_payload *prep)
|
||||
{
|
||||
struct ceph_crypto_key *ckey;
|
||||
size_t datalen = prep->datalen;
|
||||
|
@ -435,10 +435,6 @@ static int ceph_key_instantiate(struct key *key,
|
|||
if (datalen <= 0 || datalen > 32767 || !prep->data)
|
||||
goto err;
|
||||
|
||||
ret = key_payload_reserve(key, datalen);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
ret = -ENOMEM;
|
||||
ckey = kmalloc(sizeof(*ckey), GFP_KERNEL);
|
||||
if (!ckey)
|
||||
|
@ -450,7 +446,8 @@ static int ceph_key_instantiate(struct key *key,
|
|||
if (ret < 0)
|
||||
goto err_ckey;
|
||||
|
||||
key->payload.data = ckey;
|
||||
prep->payload[0] = ckey;
|
||||
prep->quotalen = datalen;
|
||||
return 0;
|
||||
|
||||
err_ckey:
|
||||
|
@ -459,12 +456,15 @@ err:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int ceph_key_match(const struct key *key, const void *description)
|
||||
static void ceph_key_free_preparse(struct key_preparsed_payload *prep)
|
||||
{
|
||||
return strcmp(key->description, description) == 0;
|
||||
struct ceph_crypto_key *ckey = prep->payload[0];
|
||||
ceph_crypto_key_destroy(ckey);
|
||||
kfree(ckey);
|
||||
}
|
||||
|
||||
static void ceph_key_destroy(struct key *key) {
|
||||
static void ceph_key_destroy(struct key *key)
|
||||
{
|
||||
struct ceph_crypto_key *ckey = key->payload.data;
|
||||
|
||||
ceph_crypto_key_destroy(ckey);
|
||||
|
@ -473,8 +473,10 @@ static void ceph_key_destroy(struct key *key) {
|
|||
|
||||
struct key_type key_type_ceph = {
|
||||
.name = "ceph",
|
||||
.instantiate = ceph_key_instantiate,
|
||||
.match = ceph_key_match,
|
||||
.preparse = ceph_key_preparse,
|
||||
.free_preparse = ceph_key_free_preparse,
|
||||
.instantiate = generic_key_instantiate,
|
||||
.match = user_match,
|
||||
.destroy = ceph_key_destroy,
|
||||
};
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ const struct cred *dns_resolver_cache;
|
|||
#define DNS_ERRORNO_OPTION "dnserror"
|
||||
|
||||
/*
|
||||
* Instantiate a user defined key for dns_resolver.
|
||||
* Preparse instantiation data for a dns_resolver key.
|
||||
*
|
||||
* The data must be a NUL-terminated string, with the NUL char accounted in
|
||||
* datalen.
|
||||
|
@ -58,17 +58,15 @@ const struct cred *dns_resolver_cache;
|
|||
* "ip1,ip2,...#foo=bar"
|
||||
*/
|
||||
static int
|
||||
dns_resolver_instantiate(struct key *key, struct key_preparsed_payload *prep)
|
||||
dns_resolver_preparse(struct key_preparsed_payload *prep)
|
||||
{
|
||||
struct user_key_payload *upayload;
|
||||
unsigned long derrno;
|
||||
int ret;
|
||||
size_t datalen = prep->datalen, result_len = 0;
|
||||
int datalen = prep->datalen, result_len = 0;
|
||||
const char *data = prep->data, *end, *opt;
|
||||
|
||||
kenter("%%%d,%s,'%*.*s',%zu",
|
||||
key->serial, key->description,
|
||||
(int)datalen, (int)datalen, data, datalen);
|
||||
kenter("'%*.*s',%u", datalen, datalen, data, datalen);
|
||||
|
||||
if (datalen <= 1 || !data || data[datalen - 1] != '\0')
|
||||
return -EINVAL;
|
||||
|
@ -95,8 +93,7 @@ dns_resolver_instantiate(struct key *key, struct key_preparsed_payload *prep)
|
|||
opt_len = next_opt - opt;
|
||||
if (!opt_len) {
|
||||
printk(KERN_WARNING
|
||||
"Empty option to dns_resolver key %d\n",
|
||||
key->serial);
|
||||
"Empty option to dns_resolver key\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -125,30 +122,28 @@ dns_resolver_instantiate(struct key *key, struct key_preparsed_payload *prep)
|
|||
goto bad_option_value;
|
||||
|
||||
kdebug("dns error no. = %lu", derrno);
|
||||
key->type_data.x[0] = -derrno;
|
||||
prep->type_data[0] = ERR_PTR(-derrno);
|
||||
continue;
|
||||
}
|
||||
|
||||
bad_option_value:
|
||||
printk(KERN_WARNING
|
||||
"Option '%*.*s' to dns_resolver key %d:"
|
||||
"Option '%*.*s' to dns_resolver key:"
|
||||
" bad/missing value\n",
|
||||
opt_nlen, opt_nlen, opt, key->serial);
|
||||
opt_nlen, opt_nlen, opt);
|
||||
return -EINVAL;
|
||||
} while (opt = next_opt + 1, opt < end);
|
||||
}
|
||||
|
||||
/* don't cache the result if we're caching an error saying there's no
|
||||
* result */
|
||||
if (key->type_data.x[0]) {
|
||||
kleave(" = 0 [h_error %ld]", key->type_data.x[0]);
|
||||
if (prep->type_data[0]) {
|
||||
kleave(" = 0 [h_error %ld]", PTR_ERR(prep->type_data[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
kdebug("store result");
|
||||
ret = key_payload_reserve(key, result_len);
|
||||
if (ret < 0)
|
||||
return -EINVAL;
|
||||
prep->quotalen = result_len;
|
||||
|
||||
upayload = kmalloc(sizeof(*upayload) + result_len + 1, GFP_KERNEL);
|
||||
if (!upayload) {
|
||||
|
@ -159,12 +154,22 @@ dns_resolver_instantiate(struct key *key, struct key_preparsed_payload *prep)
|
|||
upayload->datalen = result_len;
|
||||
memcpy(upayload->data, data, result_len);
|
||||
upayload->data[result_len] = '\0';
|
||||
rcu_assign_pointer(key->payload.data, upayload);
|
||||
|
||||
prep->payload[0] = upayload;
|
||||
kleave(" = 0");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clean up the preparse data
|
||||
*/
|
||||
static void dns_resolver_free_preparse(struct key_preparsed_payload *prep)
|
||||
{
|
||||
pr_devel("==>%s()\n", __func__);
|
||||
|
||||
kfree(prep->payload[0]);
|
||||
}
|
||||
|
||||
/*
|
||||
* The description is of the form "[<type>:]<domain_name>"
|
||||
*
|
||||
|
@ -234,7 +239,9 @@ static long dns_resolver_read(const struct key *key,
|
|||
|
||||
struct key_type key_type_dns_resolver = {
|
||||
.name = "dns_resolver",
|
||||
.instantiate = dns_resolver_instantiate,
|
||||
.preparse = dns_resolver_preparse,
|
||||
.free_preparse = dns_resolver_free_preparse,
|
||||
.instantiate = generic_key_instantiate,
|
||||
.match = dns_resolver_match,
|
||||
.revoke = user_revoke,
|
||||
.destroy = user_destroy,
|
||||
|
|
|
@ -129,6 +129,7 @@ int dns_query(const char *type, const char *name, size_t namelen,
|
|||
}
|
||||
|
||||
down_read(&rkey->sem);
|
||||
set_bit(KEY_FLAG_ROOT_CAN_INVAL, &rkey->flags);
|
||||
rkey->perm |= KEY_USR_VIEW;
|
||||
|
||||
ret = key_validate(rkey);
|
||||
|
|
|
@ -26,8 +26,10 @@
|
|||
#include "ar-internal.h"
|
||||
|
||||
static int rxrpc_vet_description_s(const char *);
|
||||
static int rxrpc_instantiate(struct key *, struct key_preparsed_payload *);
|
||||
static int rxrpc_instantiate_s(struct key *, struct key_preparsed_payload *);
|
||||
static int rxrpc_preparse(struct key_preparsed_payload *);
|
||||
static int rxrpc_preparse_s(struct key_preparsed_payload *);
|
||||
static void rxrpc_free_preparse(struct key_preparsed_payload *);
|
||||
static void rxrpc_free_preparse_s(struct key_preparsed_payload *);
|
||||
static void rxrpc_destroy(struct key *);
|
||||
static void rxrpc_destroy_s(struct key *);
|
||||
static void rxrpc_describe(const struct key *, struct seq_file *);
|
||||
|
@ -39,7 +41,9 @@ static long rxrpc_read(const struct key *, char __user *, size_t);
|
|||
*/
|
||||
struct key_type key_type_rxrpc = {
|
||||
.name = "rxrpc",
|
||||
.instantiate = rxrpc_instantiate,
|
||||
.preparse = rxrpc_preparse,
|
||||
.free_preparse = rxrpc_free_preparse,
|
||||
.instantiate = generic_key_instantiate,
|
||||
.match = user_match,
|
||||
.destroy = rxrpc_destroy,
|
||||
.describe = rxrpc_describe,
|
||||
|
@ -54,7 +58,9 @@ EXPORT_SYMBOL(key_type_rxrpc);
|
|||
struct key_type key_type_rxrpc_s = {
|
||||
.name = "rxrpc_s",
|
||||
.vet_description = rxrpc_vet_description_s,
|
||||
.instantiate = rxrpc_instantiate_s,
|
||||
.preparse = rxrpc_preparse_s,
|
||||
.free_preparse = rxrpc_free_preparse_s,
|
||||
.instantiate = generic_key_instantiate,
|
||||
.match = user_match,
|
||||
.destroy = rxrpc_destroy_s,
|
||||
.describe = rxrpc_describe,
|
||||
|
@ -81,13 +87,13 @@ static int rxrpc_vet_description_s(const char *desc)
|
|||
* parse an RxKAD type XDR format token
|
||||
* - the caller guarantees we have at least 4 words
|
||||
*/
|
||||
static int rxrpc_instantiate_xdr_rxkad(struct key *key, const __be32 *xdr,
|
||||
unsigned int toklen)
|
||||
static int rxrpc_preparse_xdr_rxkad(struct key_preparsed_payload *prep,
|
||||
size_t datalen,
|
||||
const __be32 *xdr, unsigned int toklen)
|
||||
{
|
||||
struct rxrpc_key_token *token, **pptoken;
|
||||
size_t plen;
|
||||
u32 tktlen;
|
||||
int ret;
|
||||
|
||||
_enter(",{%x,%x,%x,%x},%u",
|
||||
ntohl(xdr[0]), ntohl(xdr[1]), ntohl(xdr[2]), ntohl(xdr[3]),
|
||||
|
@ -103,9 +109,7 @@ static int rxrpc_instantiate_xdr_rxkad(struct key *key, const __be32 *xdr,
|
|||
return -EKEYREJECTED;
|
||||
|
||||
plen = sizeof(*token) + sizeof(*token->kad) + tktlen;
|
||||
ret = key_payload_reserve(key, key->datalen + plen);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
prep->quotalen = datalen + plen;
|
||||
|
||||
plen -= sizeof(*token);
|
||||
token = kzalloc(sizeof(*token), GFP_KERNEL);
|
||||
|
@ -146,16 +150,16 @@ static int rxrpc_instantiate_xdr_rxkad(struct key *key, const __be32 *xdr,
|
|||
token->kad->ticket[6], token->kad->ticket[7]);
|
||||
|
||||
/* count the number of tokens attached */
|
||||
key->type_data.x[0]++;
|
||||
prep->type_data[0] = (void *)((unsigned long)prep->type_data[0] + 1);
|
||||
|
||||
/* attach the data */
|
||||
for (pptoken = (struct rxrpc_key_token **)&key->payload.data;
|
||||
for (pptoken = (struct rxrpc_key_token **)&prep->payload[0];
|
||||
*pptoken;
|
||||
pptoken = &(*pptoken)->next)
|
||||
continue;
|
||||
*pptoken = token;
|
||||
if (token->kad->expiry < key->expiry)
|
||||
key->expiry = token->kad->expiry;
|
||||
if (token->kad->expiry < prep->expiry)
|
||||
prep->expiry = token->kad->expiry;
|
||||
|
||||
_leave(" = 0");
|
||||
return 0;
|
||||
|
@ -418,8 +422,9 @@ static int rxrpc_krb5_decode_ticket(u8 **_ticket, u16 *_tktlen,
|
|||
* parse an RxK5 type XDR format token
|
||||
* - the caller guarantees we have at least 4 words
|
||||
*/
|
||||
static int rxrpc_instantiate_xdr_rxk5(struct key *key, const __be32 *xdr,
|
||||
unsigned int toklen)
|
||||
static int rxrpc_preparse_xdr_rxk5(struct key_preparsed_payload *prep,
|
||||
size_t datalen,
|
||||
const __be32 *xdr, unsigned int toklen)
|
||||
{
|
||||
struct rxrpc_key_token *token, **pptoken;
|
||||
struct rxk5_key *rxk5;
|
||||
|
@ -432,9 +437,7 @@ static int rxrpc_instantiate_xdr_rxk5(struct key *key, const __be32 *xdr,
|
|||
|
||||
/* reserve some payload space for this subkey - the length of the token
|
||||
* is a reasonable approximation */
|
||||
ret = key_payload_reserve(key, key->datalen + toklen);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
prep->quotalen = datalen + toklen;
|
||||
|
||||
token = kzalloc(sizeof(*token), GFP_KERNEL);
|
||||
if (!token)
|
||||
|
@ -520,14 +523,14 @@ static int rxrpc_instantiate_xdr_rxk5(struct key *key, const __be32 *xdr,
|
|||
if (toklen != 0)
|
||||
goto inval;
|
||||
|
||||
/* attach the payload to the key */
|
||||
for (pptoken = (struct rxrpc_key_token **)&key->payload.data;
|
||||
/* attach the payload */
|
||||
for (pptoken = (struct rxrpc_key_token **)&prep->payload[0];
|
||||
*pptoken;
|
||||
pptoken = &(*pptoken)->next)
|
||||
continue;
|
||||
*pptoken = token;
|
||||
if (token->kad->expiry < key->expiry)
|
||||
key->expiry = token->kad->expiry;
|
||||
if (token->kad->expiry < prep->expiry)
|
||||
prep->expiry = token->kad->expiry;
|
||||
|
||||
_leave(" = 0");
|
||||
return 0;
|
||||
|
@ -545,16 +548,17 @@ error:
|
|||
* attempt to parse the data as the XDR format
|
||||
* - the caller guarantees we have more than 7 words
|
||||
*/
|
||||
static int rxrpc_instantiate_xdr(struct key *key, const void *data, size_t datalen)
|
||||
static int rxrpc_preparse_xdr(struct key_preparsed_payload *prep)
|
||||
{
|
||||
const __be32 *xdr = data, *token;
|
||||
const __be32 *xdr = prep->data, *token;
|
||||
const char *cp;
|
||||
unsigned int len, tmp, loop, ntoken, toklen, sec_ix;
|
||||
size_t datalen = prep->datalen;
|
||||
int ret;
|
||||
|
||||
_enter(",{%x,%x,%x,%x},%zu",
|
||||
ntohl(xdr[0]), ntohl(xdr[1]), ntohl(xdr[2]), ntohl(xdr[3]),
|
||||
datalen);
|
||||
prep->datalen);
|
||||
|
||||
if (datalen > AFSTOKEN_LENGTH_MAX)
|
||||
goto not_xdr;
|
||||
|
@ -635,13 +639,13 @@ static int rxrpc_instantiate_xdr(struct key *key, const void *data, size_t datal
|
|||
|
||||
switch (sec_ix) {
|
||||
case RXRPC_SECURITY_RXKAD:
|
||||
ret = rxrpc_instantiate_xdr_rxkad(key, xdr, toklen);
|
||||
ret = rxrpc_preparse_xdr_rxkad(prep, datalen, xdr, toklen);
|
||||
if (ret != 0)
|
||||
goto error;
|
||||
break;
|
||||
|
||||
case RXRPC_SECURITY_RXK5:
|
||||
ret = rxrpc_instantiate_xdr_rxk5(key, xdr, toklen);
|
||||
ret = rxrpc_preparse_xdr_rxk5(prep, datalen, xdr, toklen);
|
||||
if (ret != 0)
|
||||
goto error;
|
||||
break;
|
||||
|
@ -665,8 +669,9 @@ error:
|
|||
}
|
||||
|
||||
/*
|
||||
* instantiate an rxrpc defined key
|
||||
* data should be of the form:
|
||||
* Preparse an rxrpc defined key.
|
||||
*
|
||||
* Data should be of the form:
|
||||
* OFFSET LEN CONTENT
|
||||
* 0 4 key interface version number
|
||||
* 4 2 security index (type)
|
||||
|
@ -678,7 +683,7 @@ error:
|
|||
*
|
||||
* if no data is provided, then a no-security key is made
|
||||
*/
|
||||
static int rxrpc_instantiate(struct key *key, struct key_preparsed_payload *prep)
|
||||
static int rxrpc_preparse(struct key_preparsed_payload *prep)
|
||||
{
|
||||
const struct rxrpc_key_data_v1 *v1;
|
||||
struct rxrpc_key_token *token, **pp;
|
||||
|
@ -686,7 +691,7 @@ static int rxrpc_instantiate(struct key *key, struct key_preparsed_payload *prep
|
|||
u32 kver;
|
||||
int ret;
|
||||
|
||||
_enter("{%x},,%zu", key_serial(key), prep->datalen);
|
||||
_enter("%zu", prep->datalen);
|
||||
|
||||
/* handle a no-security key */
|
||||
if (!prep->data && prep->datalen == 0)
|
||||
|
@ -694,7 +699,7 @@ static int rxrpc_instantiate(struct key *key, struct key_preparsed_payload *prep
|
|||
|
||||
/* determine if the XDR payload format is being used */
|
||||
if (prep->datalen > 7 * 4) {
|
||||
ret = rxrpc_instantiate_xdr(key, prep->data, prep->datalen);
|
||||
ret = rxrpc_preparse_xdr(prep);
|
||||
if (ret != -EPROTO)
|
||||
return ret;
|
||||
}
|
||||
|
@ -743,9 +748,7 @@ static int rxrpc_instantiate(struct key *key, struct key_preparsed_payload *prep
|
|||
goto error;
|
||||
|
||||
plen = sizeof(*token->kad) + v1->ticket_length;
|
||||
ret = key_payload_reserve(key, plen + sizeof(*token));
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
prep->quotalen = plen + sizeof(*token);
|
||||
|
||||
ret = -ENOMEM;
|
||||
token = kzalloc(sizeof(*token), GFP_KERNEL);
|
||||
|
@ -762,15 +765,16 @@ static int rxrpc_instantiate(struct key *key, struct key_preparsed_payload *prep
|
|||
memcpy(&token->kad->session_key, &v1->session_key, 8);
|
||||
memcpy(&token->kad->ticket, v1->ticket, v1->ticket_length);
|
||||
|
||||
/* attach the data */
|
||||
key->type_data.x[0]++;
|
||||
/* count the number of tokens attached */
|
||||
prep->type_data[0] = (void *)((unsigned long)prep->type_data[0] + 1);
|
||||
|
||||
pp = (struct rxrpc_key_token **)&key->payload.data;
|
||||
/* attach the data */
|
||||
pp = (struct rxrpc_key_token **)&prep->payload[0];
|
||||
while (*pp)
|
||||
pp = &(*pp)->next;
|
||||
*pp = token;
|
||||
if (token->kad->expiry < key->expiry)
|
||||
key->expiry = token->kad->expiry;
|
||||
if (token->kad->expiry < prep->expiry)
|
||||
prep->expiry = token->kad->expiry;
|
||||
token = NULL;
|
||||
ret = 0;
|
||||
|
||||
|
@ -781,44 +785,14 @@ error:
|
|||
}
|
||||
|
||||
/*
|
||||
* instantiate a server secret key
|
||||
* data should be a pointer to the 8-byte secret key
|
||||
* Free token list.
|
||||
*/
|
||||
static int rxrpc_instantiate_s(struct key *key,
|
||||
struct key_preparsed_payload *prep)
|
||||
static void rxrpc_free_token_list(struct rxrpc_key_token *token)
|
||||
{
|
||||
struct crypto_blkcipher *ci;
|
||||
struct rxrpc_key_token *next;
|
||||
|
||||
_enter("{%x},,%zu", key_serial(key), prep->datalen);
|
||||
|
||||
if (prep->datalen != 8)
|
||||
return -EINVAL;
|
||||
|
||||
memcpy(&key->type_data, prep->data, 8);
|
||||
|
||||
ci = crypto_alloc_blkcipher("pcbc(des)", 0, CRYPTO_ALG_ASYNC);
|
||||
if (IS_ERR(ci)) {
|
||||
_leave(" = %ld", PTR_ERR(ci));
|
||||
return PTR_ERR(ci);
|
||||
}
|
||||
|
||||
if (crypto_blkcipher_setkey(ci, prep->data, 8) < 0)
|
||||
BUG();
|
||||
|
||||
key->payload.data = ci;
|
||||
_leave(" = 0");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* dispose of the data dangling from the corpse of a rxrpc key
|
||||
*/
|
||||
static void rxrpc_destroy(struct key *key)
|
||||
{
|
||||
struct rxrpc_key_token *token;
|
||||
|
||||
while ((token = key->payload.data)) {
|
||||
key->payload.data = token->next;
|
||||
for (; token; token = next) {
|
||||
next = token->next;
|
||||
switch (token->security_index) {
|
||||
case RXRPC_SECURITY_RXKAD:
|
||||
kfree(token->kad);
|
||||
|
@ -837,6 +811,61 @@ static void rxrpc_destroy(struct key *key)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Clean up preparse data.
|
||||
*/
|
||||
static void rxrpc_free_preparse(struct key_preparsed_payload *prep)
|
||||
{
|
||||
rxrpc_free_token_list(prep->payload[0]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Preparse a server secret key.
|
||||
*
|
||||
* The data should be the 8-byte secret key.
|
||||
*/
|
||||
static int rxrpc_preparse_s(struct key_preparsed_payload *prep)
|
||||
{
|
||||
struct crypto_blkcipher *ci;
|
||||
|
||||
_enter("%zu", prep->datalen);
|
||||
|
||||
if (prep->datalen != 8)
|
||||
return -EINVAL;
|
||||
|
||||
memcpy(&prep->type_data, prep->data, 8);
|
||||
|
||||
ci = crypto_alloc_blkcipher("pcbc(des)", 0, CRYPTO_ALG_ASYNC);
|
||||
if (IS_ERR(ci)) {
|
||||
_leave(" = %ld", PTR_ERR(ci));
|
||||
return PTR_ERR(ci);
|
||||
}
|
||||
|
||||
if (crypto_blkcipher_setkey(ci, prep->data, 8) < 0)
|
||||
BUG();
|
||||
|
||||
prep->payload[0] = ci;
|
||||
_leave(" = 0");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clean up preparse data.
|
||||
*/
|
||||
static void rxrpc_free_preparse_s(struct key_preparsed_payload *prep)
|
||||
{
|
||||
if (prep->payload[0])
|
||||
crypto_free_blkcipher(prep->payload[0]);
|
||||
}
|
||||
|
||||
/*
|
||||
* dispose of the data dangling from the corpse of a rxrpc key
|
||||
*/
|
||||
static void rxrpc_destroy(struct key *key)
|
||||
{
|
||||
rxrpc_free_token_list(key->payload.data);
|
||||
}
|
||||
|
||||
/*
|
||||
* dispose of the data dangling from the corpse of a rxrpc key
|
||||
*/
|
||||
|
|
|
@ -13,7 +13,9 @@
|
|||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/rbtree.h>
|
||||
#include <linux/cred.h>
|
||||
#include <linux/key-type.h>
|
||||
#include <linux/digsig.h>
|
||||
|
||||
|
@ -24,7 +26,11 @@ static struct key *keyring[INTEGRITY_KEYRING_MAX];
|
|||
static const char *keyring_name[INTEGRITY_KEYRING_MAX] = {
|
||||
"_evm",
|
||||
"_module",
|
||||
#ifndef CONFIG_IMA_TRUSTED_KEYRING
|
||||
"_ima",
|
||||
#else
|
||||
".ima",
|
||||
#endif
|
||||
};
|
||||
|
||||
int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
|
||||
|
@ -56,3 +62,25 @@ int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
|
|||
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int integrity_init_keyring(const unsigned int id)
|
||||
{
|
||||
const struct cred *cred = current_cred();
|
||||
int err = 0;
|
||||
|
||||
keyring[id] = keyring_alloc(keyring_name[id], KUIDT_INIT(0),
|
||||
KGIDT_INIT(0), 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);
|
||||
if (!IS_ERR(keyring[id]))
|
||||
set_bit(KEY_FLAG_TRUSTED_ONLY, &keyring[id]->flags);
|
||||
else {
|
||||
err = PTR_ERR(keyring[id]);
|
||||
pr_info("Can't allocate %s keyring (%d)\n",
|
||||
keyring_name[id], err);
|
||||
keyring[id] = NULL;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
|
|
@ -123,3 +123,13 @@ config IMA_APPRAISE
|
|||
For more information on integrity appraisal refer to:
|
||||
<http://linux-ima.sourceforge.net>
|
||||
If unsure, say N.
|
||||
|
||||
config IMA_TRUSTED_KEYRING
|
||||
bool "Require all keys on the .ima keyring be signed"
|
||||
depends on IMA_APPRAISE && SYSTEM_TRUSTED_KEYRING
|
||||
depends on INTEGRITY_ASYMMETRIC_KEYS
|
||||
select KEYS_DEBUG_PROC_KEYS
|
||||
default y
|
||||
help
|
||||
This option requires that all keys added to the .ima
|
||||
keyring be signed by a key on the system trusted keyring.
|
||||
|
|
|
@ -249,4 +249,16 @@ static inline int security_filter_rule_match(u32 secid, u32 field, u32 op,
|
|||
return -EINVAL;
|
||||
}
|
||||
#endif /* CONFIG_IMA_LSM_RULES */
|
||||
|
||||
#ifdef CONFIG_IMA_TRUSTED_KEYRING
|
||||
static inline int ima_init_keyring(const unsigned int id)
|
||||
{
|
||||
return integrity_init_keyring(id);
|
||||
}
|
||||
#else
|
||||
static inline int ima_init_keyring(const unsigned int id)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_IMA_TRUSTED_KEYRING */
|
||||
#endif
|
||||
|
|
|
@ -325,8 +325,14 @@ static int __init init_ima(void)
|
|||
|
||||
hash_setup(CONFIG_IMA_DEFAULT_HASH);
|
||||
error = ima_init();
|
||||
if (!error)
|
||||
ima_initialized = 1;
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
error = ima_init_keyring(INTEGRITY_KEYRING_IMA);
|
||||
if (error)
|
||||
goto out;
|
||||
ima_initialized = 1;
|
||||
out:
|
||||
return error;
|
||||
}
|
||||
|
||||
|
|
|
@ -124,6 +124,7 @@ struct integrity_iint_cache *integrity_iint_find(struct inode *inode);
|
|||
int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
|
||||
const char *digest, int digestlen);
|
||||
|
||||
int integrity_init_keyring(const unsigned int id);
|
||||
#else
|
||||
|
||||
static inline int integrity_digsig_verify(const unsigned int id,
|
||||
|
@ -133,6 +134,10 @@ static inline int integrity_digsig_verify(const unsigned int id,
|
|||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int integrity_init_keyring(const unsigned int id)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_INTEGRITY_SIGNATURE */
|
||||
|
||||
#ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS
|
||||
|
|
|
@ -34,7 +34,9 @@ MODULE_LICENSE("GPL");
|
|||
struct key_type key_type_big_key = {
|
||||
.name = "big_key",
|
||||
.def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
|
||||
.instantiate = big_key_instantiate,
|
||||
.preparse = big_key_preparse,
|
||||
.free_preparse = big_key_free_preparse,
|
||||
.instantiate = generic_key_instantiate,
|
||||
.match = user_match,
|
||||
.revoke = big_key_revoke,
|
||||
.destroy = big_key_destroy,
|
||||
|
@ -43,11 +45,11 @@ struct key_type key_type_big_key = {
|
|||
};
|
||||
|
||||
/*
|
||||
* Instantiate a big key
|
||||
* Preparse a big key
|
||||
*/
|
||||
int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
|
||||
int big_key_preparse(struct key_preparsed_payload *prep)
|
||||
{
|
||||
struct path *path = (struct path *)&key->payload.data2;
|
||||
struct path *path = (struct path *)&prep->payload;
|
||||
struct file *file;
|
||||
ssize_t written;
|
||||
size_t datalen = prep->datalen;
|
||||
|
@ -58,11 +60,9 @@ int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
|
|||
goto error;
|
||||
|
||||
/* Set an arbitrary quota */
|
||||
ret = key_payload_reserve(key, 16);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
prep->quotalen = 16;
|
||||
|
||||
key->type_data.x[1] = datalen;
|
||||
prep->type_data[1] = (void *)(unsigned long)datalen;
|
||||
|
||||
if (datalen > BIG_KEY_FILE_THRESHOLD) {
|
||||
/* Create a shmem file to store the data in. This will permit the data
|
||||
|
@ -73,7 +73,7 @@ int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
|
|||
file = shmem_kernel_file_setup("", datalen, 0);
|
||||
if (IS_ERR(file)) {
|
||||
ret = PTR_ERR(file);
|
||||
goto err_quota;
|
||||
goto error;
|
||||
}
|
||||
|
||||
written = kernel_write(file, prep->data, prep->datalen, 0);
|
||||
|
@ -93,23 +93,32 @@ int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
|
|||
} else {
|
||||
/* Just store the data in a buffer */
|
||||
void *data = kmalloc(datalen, GFP_KERNEL);
|
||||
if (!data) {
|
||||
ret = -ENOMEM;
|
||||
goto err_quota;
|
||||
}
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
key->payload.data = memcpy(data, prep->data, prep->datalen);
|
||||
prep->payload[0] = memcpy(data, prep->data, prep->datalen);
|
||||
}
|
||||
return 0;
|
||||
|
||||
err_fput:
|
||||
fput(file);
|
||||
err_quota:
|
||||
key_payload_reserve(key, 0);
|
||||
error:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear preparsement.
|
||||
*/
|
||||
void big_key_free_preparse(struct key_preparsed_payload *prep)
|
||||
{
|
||||
if (prep->datalen > BIG_KEY_FILE_THRESHOLD) {
|
||||
struct path *path = (struct path *)&prep->payload;
|
||||
path_put(path);
|
||||
} else {
|
||||
kfree(prep->payload[0]);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* dispose of the links from a revoked keyring
|
||||
* - called with the key sem write-locked
|
||||
|
|
|
@ -811,7 +811,7 @@ static int encrypted_instantiate(struct key *key,
|
|||
goto out;
|
||||
}
|
||||
|
||||
rcu_assign_keypointer(key, epayload);
|
||||
prep->payload[0] = epayload;
|
||||
out:
|
||||
kfree(datablob);
|
||||
return ret;
|
||||
|
|
|
@ -437,6 +437,11 @@ static int __key_instantiate_and_link(struct key *key,
|
|||
/* disable the authorisation key */
|
||||
if (authkey)
|
||||
key_revoke(authkey);
|
||||
|
||||
if (prep->expiry != TIME_T_MAX) {
|
||||
key->expiry = prep->expiry;
|
||||
key_schedule_gc(prep->expiry + key_gc_delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -479,6 +484,7 @@ int key_instantiate_and_link(struct key *key,
|
|||
prep.data = data;
|
||||
prep.datalen = datalen;
|
||||
prep.quotalen = key->type->def_datalen;
|
||||
prep.expiry = TIME_T_MAX;
|
||||
if (key->type->preparse) {
|
||||
ret = key->type->preparse(&prep);
|
||||
if (ret < 0)
|
||||
|
@ -488,7 +494,7 @@ int key_instantiate_and_link(struct key *key,
|
|||
if (keyring) {
|
||||
ret = __key_link_begin(keyring, &key->index_key, &edit);
|
||||
if (ret < 0)
|
||||
goto error_free_preparse;
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = __key_instantiate_and_link(key, &prep, keyring, authkey, &edit);
|
||||
|
@ -496,10 +502,9 @@ int key_instantiate_and_link(struct key *key,
|
|||
if (keyring)
|
||||
__key_link_end(keyring, &key->index_key, edit);
|
||||
|
||||
error_free_preparse:
|
||||
error:
|
||||
if (key->type->preparse)
|
||||
key->type->free_preparse(&prep);
|
||||
error:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -811,11 +816,12 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
|
|||
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);
|
||||
if (ret < 0) {
|
||||
key_ref = ERR_PTR(ret);
|
||||
goto error_put_type;
|
||||
goto error_free_prep;
|
||||
}
|
||||
if (!index_key.description)
|
||||
index_key.description = prep.description;
|
||||
|
@ -941,6 +947,7 @@ int key_update(key_ref_t key_ref, const void *payload, size_t plen)
|
|||
prep.data = payload;
|
||||
prep.datalen = plen;
|
||||
prep.quotalen = key->type->def_datalen;
|
||||
prep.expiry = TIME_T_MAX;
|
||||
if (key->type->preparse) {
|
||||
ret = key->type->preparse(&prep);
|
||||
if (ret < 0)
|
||||
|
@ -956,9 +963,9 @@ int key_update(key_ref_t key_ref, const void *payload, size_t plen)
|
|||
|
||||
up_write(&key->sem);
|
||||
|
||||
error:
|
||||
if (key->type->preparse)
|
||||
key->type->free_preparse(&prep);
|
||||
error:
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(key_update);
|
||||
|
@ -1023,6 +1030,38 @@ void key_invalidate(struct key *key)
|
|||
}
|
||||
EXPORT_SYMBOL(key_invalidate);
|
||||
|
||||
/**
|
||||
* generic_key_instantiate - Simple instantiation of a key from preparsed data
|
||||
* @key: The key to be instantiated
|
||||
* @prep: The preparsed data to load.
|
||||
*
|
||||
* Instantiate a key from preparsed data. We assume we can just copy the data
|
||||
* in directly and clear the old pointers.
|
||||
*
|
||||
* This can be pointed to directly by the key type instantiate op pointer.
|
||||
*/
|
||||
int generic_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
|
||||
{
|
||||
int ret;
|
||||
|
||||
pr_devel("==>%s()\n", __func__);
|
||||
|
||||
ret = key_payload_reserve(key, prep->quotalen);
|
||||
if (ret == 0) {
|
||||
key->type_data.p[0] = prep->type_data[0];
|
||||
key->type_data.p[1] = prep->type_data[1];
|
||||
rcu_assign_keypointer(key, prep->payload[0]);
|
||||
key->payload.data2[1] = prep->payload[1];
|
||||
prep->type_data[0] = NULL;
|
||||
prep->type_data[1] = NULL;
|
||||
prep->payload[0] = NULL;
|
||||
prep->payload[1] = NULL;
|
||||
}
|
||||
pr_devel("<==%s() = %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(generic_key_instantiate);
|
||||
|
||||
/**
|
||||
* register_key_type - Register a type of key.
|
||||
* @ktype: The new key type.
|
||||
|
|
|
@ -37,8 +37,6 @@ static int key_get_type_from_user(char *type,
|
|||
return ret;
|
||||
if (ret == 0 || ret >= len)
|
||||
return -EINVAL;
|
||||
if (type[0] == '.')
|
||||
return -EPERM;
|
||||
type[len - 1] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
@ -86,6 +84,10 @@ SYSCALL_DEFINE5(add_key, const char __user *, _type,
|
|||
if (!*description) {
|
||||
kfree(description);
|
||||
description = NULL;
|
||||
} else if ((description[0] == '.') &&
|
||||
(strncmp(type, "keyring", 7) == 0)) {
|
||||
ret = -EPERM;
|
||||
goto error2;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -404,12 +406,25 @@ long keyctl_invalidate_key(key_serial_t id)
|
|||
key_ref = lookup_user_key(id, 0, KEY_NEED_SEARCH);
|
||||
if (IS_ERR(key_ref)) {
|
||||
ret = PTR_ERR(key_ref);
|
||||
|
||||
/* Root is permitted to invalidate certain special keys */
|
||||
if (capable(CAP_SYS_ADMIN)) {
|
||||
key_ref = lookup_user_key(id, 0, 0);
|
||||
if (IS_ERR(key_ref))
|
||||
goto error;
|
||||
if (test_bit(KEY_FLAG_ROOT_CAN_INVAL,
|
||||
&key_ref_to_ptr(key_ref)->flags))
|
||||
goto invalidate;
|
||||
goto error_put;
|
||||
}
|
||||
|
||||
goto error;
|
||||
}
|
||||
|
||||
invalidate:
|
||||
key_invalidate(key_ref_to_ptr(key_ref));
|
||||
ret = 0;
|
||||
|
||||
error_put:
|
||||
key_ref_put(key_ref);
|
||||
error:
|
||||
kleave(" = %ld", ret);
|
||||
|
|
|
@ -73,6 +73,8 @@ static inline unsigned keyring_hash(const char *desc)
|
|||
* can be treated as ordinary keys in addition to having their own special
|
||||
* operations.
|
||||
*/
|
||||
static int keyring_preparse(struct key_preparsed_payload *prep);
|
||||
static void keyring_free_preparse(struct key_preparsed_payload *prep);
|
||||
static int keyring_instantiate(struct key *keyring,
|
||||
struct key_preparsed_payload *prep);
|
||||
static void keyring_revoke(struct key *keyring);
|
||||
|
@ -84,6 +86,8 @@ static long keyring_read(const struct key *keyring,
|
|||
struct key_type key_type_keyring = {
|
||||
.name = "keyring",
|
||||
.def_datalen = 0,
|
||||
.preparse = keyring_preparse,
|
||||
.free_preparse = keyring_free_preparse,
|
||||
.instantiate = keyring_instantiate,
|
||||
.match = user_match,
|
||||
.revoke = keyring_revoke,
|
||||
|
@ -122,6 +126,21 @@ static void keyring_publish_name(struct key *keyring)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Preparse a keyring payload
|
||||
*/
|
||||
static int keyring_preparse(struct key_preparsed_payload *prep)
|
||||
{
|
||||
return prep->datalen != 0 ? -EINVAL : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Free a preparse of a user defined key payload
|
||||
*/
|
||||
static void keyring_free_preparse(struct key_preparsed_payload *prep)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialise a keyring.
|
||||
*
|
||||
|
@ -130,17 +149,10 @@ static void keyring_publish_name(struct key *keyring)
|
|||
static int keyring_instantiate(struct key *keyring,
|
||||
struct key_preparsed_payload *prep)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = -EINVAL;
|
||||
if (prep->datalen == 0) {
|
||||
assoc_array_init(&keyring->keys);
|
||||
/* make the keyring available by name if it has one */
|
||||
keyring_publish_name(keyring);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
assoc_array_init(&keyring->keys);
|
||||
/* make the keyring available by name if it has one */
|
||||
keyring_publish_name(keyring);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
#include "internal.h"
|
||||
#include <keys/user-type.h>
|
||||
|
||||
static int request_key_auth_preparse(struct key_preparsed_payload *);
|
||||
static void request_key_auth_free_preparse(struct key_preparsed_payload *);
|
||||
static int request_key_auth_instantiate(struct key *,
|
||||
struct key_preparsed_payload *);
|
||||
static void request_key_auth_describe(const struct key *, struct seq_file *);
|
||||
|
@ -33,6 +35,8 @@ static long request_key_auth_read(const struct key *, char __user *, size_t);
|
|||
struct key_type key_type_request_key_auth = {
|
||||
.name = ".request_key_auth",
|
||||
.def_datalen = sizeof(struct request_key_auth),
|
||||
.preparse = request_key_auth_preparse,
|
||||
.free_preparse = request_key_auth_free_preparse,
|
||||
.instantiate = request_key_auth_instantiate,
|
||||
.describe = request_key_auth_describe,
|
||||
.revoke = request_key_auth_revoke,
|
||||
|
@ -40,6 +44,15 @@ struct key_type key_type_request_key_auth = {
|
|||
.read = request_key_auth_read,
|
||||
};
|
||||
|
||||
int request_key_auth_preparse(struct key_preparsed_payload *prep)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void request_key_auth_free_preparse(struct key_preparsed_payload *prep)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* Instantiate a request-key authorisation key.
|
||||
*/
|
||||
|
|
|
@ -27,7 +27,9 @@ static int logon_vet_description(const char *desc);
|
|||
struct key_type key_type_user = {
|
||||
.name = "user",
|
||||
.def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
|
||||
.instantiate = user_instantiate,
|
||||
.preparse = user_preparse,
|
||||
.free_preparse = user_free_preparse,
|
||||
.instantiate = generic_key_instantiate,
|
||||
.update = user_update,
|
||||
.match = user_match,
|
||||
.revoke = user_revoke,
|
||||
|
@ -47,7 +49,9 @@ EXPORT_SYMBOL_GPL(key_type_user);
|
|||
struct key_type key_type_logon = {
|
||||
.name = "logon",
|
||||
.def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
|
||||
.instantiate = user_instantiate,
|
||||
.preparse = user_preparse,
|
||||
.free_preparse = user_free_preparse,
|
||||
.instantiate = generic_key_instantiate,
|
||||
.update = user_update,
|
||||
.match = user_match,
|
||||
.revoke = user_revoke,
|
||||
|
@ -58,38 +62,37 @@ struct key_type key_type_logon = {
|
|||
EXPORT_SYMBOL_GPL(key_type_logon);
|
||||
|
||||
/*
|
||||
* instantiate a user defined key
|
||||
* Preparse a user defined key payload
|
||||
*/
|
||||
int user_instantiate(struct key *key, struct key_preparsed_payload *prep)
|
||||
int user_preparse(struct key_preparsed_payload *prep)
|
||||
{
|
||||
struct user_key_payload *upayload;
|
||||
size_t datalen = prep->datalen;
|
||||
int ret;
|
||||
|
||||
ret = -EINVAL;
|
||||
if (datalen <= 0 || datalen > 32767 || !prep->data)
|
||||
goto error;
|
||||
return -EINVAL;
|
||||
|
||||
ret = key_payload_reserve(key, datalen);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
ret = -ENOMEM;
|
||||
upayload = kmalloc(sizeof(*upayload) + datalen, GFP_KERNEL);
|
||||
if (!upayload)
|
||||
goto error;
|
||||
return -ENOMEM;
|
||||
|
||||
/* attach the data */
|
||||
prep->quotalen = datalen;
|
||||
prep->payload[0] = upayload;
|
||||
upayload->datalen = datalen;
|
||||
memcpy(upayload->data, prep->data, datalen);
|
||||
rcu_assign_keypointer(key, upayload);
|
||||
ret = 0;
|
||||
|
||||
error:
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(user_preparse);
|
||||
|
||||
EXPORT_SYMBOL_GPL(user_instantiate);
|
||||
/*
|
||||
* Free a preparse of a user defined key payload
|
||||
*/
|
||||
void user_free_preparse(struct key_preparsed_payload *prep)
|
||||
{
|
||||
kfree(prep->payload[0]);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(user_free_preparse);
|
||||
|
||||
/*
|
||||
* update a user defined key
|
||||
|
|
Loading…
Reference in New Issue