mirror of
				git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
				synced 2025-10-31 08:44:41 +00:00 
			
		
		
		
	Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security
Pull security subsystem updates from James Morris: "In this release: - PKCS#7 parser for the key management subsystem from David Howells - appoint Kees Cook as seccomp maintainer - bugfixes and general maintenance across the subsystem" * 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security: (94 commits) X.509: Need to export x509_request_asymmetric_key() netlabel: shorter names for the NetLabel catmap funcs/structs netlabel: fix the catmap walking functions netlabel: fix the horribly broken catmap functions netlabel: fix a problem when setting bits below the previously lowest bit PKCS#7: X.509 certificate issuer and subject are mandatory fields in the ASN.1 tpm: simplify code by using %*phN specifier tpm: Provide a generic means to override the chip returned timeouts tpm: missing tpm_chip_put in tpm_get_random() tpm: Properly clean sysfs entries in error path tpm: Add missing tpm_do_selftest to ST33 I2C driver PKCS#7: Use x509_request_asymmetric_key() Revert "selinux: fix the default socket labeling in sock_graft()" X.509: x509_request_asymmetric_keys() doesn't need string length arguments PKCS#7: fix sparse non static symbol warning KEYS: revert encrypted key change ima: add support for measuring and appraising firmware firmware_class: perform new LSM checks security: introduce kernel_fw_from_file hook PKCS#7: Missing inclusion of linux/err.h ...
This commit is contained in:
		
						commit
						bb2cbf5e93
					
				
					 110 changed files with 4487 additions and 841 deletions
				
			
		|  | @ -26,6 +26,7 @@ Description: | |||
| 			option:	[[appraise_type=]] [permit_directio] | ||||
| 
 | ||||
| 		base: 	func:= [BPRM_CHECK][MMAP_CHECK][FILE_CHECK][MODULE_CHECK] | ||||
| 				[FIRMWARE_CHECK] | ||||
| 			mask:= [MAY_READ] [MAY_WRITE] [MAY_APPEND] [MAY_EXEC] | ||||
| 			fsmagic:= hex value | ||||
| 			fsuuid:= file system UUID (e.g 8bcbe394-4f13-4144-be8e-5aa9ea2ce2f6) | ||||
|  | @ -57,7 +58,8 @@ Description: | |||
| 			measure func=BPRM_CHECK | ||||
| 			measure func=FILE_MMAP mask=MAY_EXEC | ||||
| 			measure func=FILE_CHECK mask=MAY_READ uid=0 | ||||
| 			measure func=MODULE_CHECK uid=0 | ||||
| 			measure func=MODULE_CHECK | ||||
| 			measure func=FIRMWARE_CHECK | ||||
| 			appraise fowner=0 | ||||
| 
 | ||||
| 		The default policy measures all executables in bprm_check, | ||||
|  |  | |||
|  | @ -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. | ||||
| 
 | ||||
|  | @ -1319,6 +1324,23 @@ bytes respectively. Such letter suffixes can also be entirely omitted. | |||
| 			Formats: { "ima" | "ima-ng" } | ||||
| 			Default: "ima-ng" | ||||
| 
 | ||||
| 	ima.ahash_minsize= [IMA] Minimum file size for asynchronous hash usage | ||||
| 			Format: <min_file_size> | ||||
| 			Set the minimal file size for using asynchronous hash. | ||||
| 			If left unspecified, ahash usage is disabled. | ||||
| 
 | ||||
| 			ahash performance varies for different data sizes on | ||||
| 			different crypto accelerators. This option can be used | ||||
| 			to achieve the best performance for a particular HW. | ||||
| 
 | ||||
| 	ima.ahash_bufsize= [IMA] Asynchronous hash buffer size | ||||
| 			Format: <bufsize> | ||||
| 			Set hashing buffer size. Default: 4k. | ||||
| 
 | ||||
| 			ahash performance varies for different chunk sizes on | ||||
| 			different crypto accelerators. This option can be used | ||||
| 			to achieve best performance for particular HW. | ||||
| 
 | ||||
| 	init=		[KNL] | ||||
| 			Format: <full_path> | ||||
| 			Run specified binary instead of /sbin/init as init | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
							
								
								
									
										10
									
								
								MAINTAINERS
									
										
									
									
									
								
							
							
						
						
									
										10
									
								
								MAINTAINERS
									
										
									
									
									
								
							|  | @ -8002,6 +8002,16 @@ S:	Maintained | |||
| F:	drivers/mmc/host/sdhci.* | ||||
| F:	drivers/mmc/host/sdhci-pltfm.[ch] | ||||
| 
 | ||||
| SECURE COMPUTING | ||||
| M:	Kees Cook <keescook@chromium.org> | ||||
| T:	git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git seccomp | ||||
| S:	Supported | ||||
| F:	kernel/seccomp.c | ||||
| F:	include/uapi/linux/seccomp.h | ||||
| F:	include/linux/seccomp.h | ||||
| K:	\bsecure_computing | ||||
| K:	\bTIF_SECCOMP\b | ||||
| 
 | ||||
| SECURE DIGITAL HOST CONTROLLER INTERFACE, OPEN FIRMWARE BINDINGS (SDHCI-OF) | ||||
| M:	Anton Vorontsov <anton@enomsg.org> | ||||
| L:	linuxppc-dev@lists.ozlabs.org | ||||
|  |  | |||
|  | @ -321,6 +321,7 @@ config HAVE_ARCH_SECCOMP_FILTER | |||
| 	  - secure_computing is called from a ptrace_event()-safe context | ||||
| 	  - secure_computing return value is checked and a return value of -1 | ||||
| 	    results in the system call being skipped immediately. | ||||
| 	  - seccomp syscall wired up | ||||
| 
 | ||||
| config SECCOMP_FILTER | ||||
| 	def_bool y | ||||
|  |  | |||
|  | @ -409,6 +409,7 @@ | |||
| #define __NR_sched_setattr		(__NR_SYSCALL_BASE+380) | ||||
| #define __NR_sched_getattr		(__NR_SYSCALL_BASE+381) | ||||
| #define __NR_renameat2			(__NR_SYSCALL_BASE+382) | ||||
| #define __NR_seccomp			(__NR_SYSCALL_BASE+383) | ||||
| 
 | ||||
| /*
 | ||||
|  * The following SWIs are ARM private. | ||||
|  |  | |||
|  | @ -392,6 +392,7 @@ | |||
| /* 380 */	CALL(sys_sched_setattr) | ||||
| 		CALL(sys_sched_getattr) | ||||
| 		CALL(sys_renameat2) | ||||
| 		CALL(sys_seccomp) | ||||
| #ifndef syscalls_counted | ||||
| .equ syscalls_padding, ((NR_syscalls + 3) & ~3) - NR_syscalls | ||||
| #define syscalls_counted | ||||
|  |  | |||
|  | @ -372,16 +372,17 @@ | |||
| #define __NR_sched_setattr		(__NR_Linux + 349) | ||||
| #define __NR_sched_getattr		(__NR_Linux + 350) | ||||
| #define __NR_renameat2			(__NR_Linux + 351) | ||||
| #define __NR_seccomp			(__NR_Linux + 352) | ||||
| 
 | ||||
| /*
 | ||||
|  * Offset of the last Linux o32 flavoured syscall | ||||
|  */ | ||||
| #define __NR_Linux_syscalls		351 | ||||
| #define __NR_Linux_syscalls		352 | ||||
| 
 | ||||
| #endif /* _MIPS_SIM == _MIPS_SIM_ABI32 */ | ||||
| 
 | ||||
| #define __NR_O32_Linux			4000 | ||||
| #define __NR_O32_Linux_syscalls		351 | ||||
| #define __NR_O32_Linux_syscalls		352 | ||||
| 
 | ||||
| #if _MIPS_SIM == _MIPS_SIM_ABI64 | ||||
| 
 | ||||
|  | @ -701,16 +702,17 @@ | |||
| #define __NR_sched_setattr		(__NR_Linux + 309) | ||||
| #define __NR_sched_getattr		(__NR_Linux + 310) | ||||
| #define __NR_renameat2			(__NR_Linux + 311) | ||||
| #define __NR_seccomp			(__NR_Linux + 312) | ||||
| 
 | ||||
| /*
 | ||||
|  * Offset of the last Linux 64-bit flavoured syscall | ||||
|  */ | ||||
| #define __NR_Linux_syscalls		311 | ||||
| #define __NR_Linux_syscalls		312 | ||||
| 
 | ||||
| #endif /* _MIPS_SIM == _MIPS_SIM_ABI64 */ | ||||
| 
 | ||||
| #define __NR_64_Linux			5000 | ||||
| #define __NR_64_Linux_syscalls		311 | ||||
| #define __NR_64_Linux_syscalls		312 | ||||
| 
 | ||||
| #if _MIPS_SIM == _MIPS_SIM_NABI32 | ||||
| 
 | ||||
|  | @ -1034,15 +1036,16 @@ | |||
| #define __NR_sched_setattr		(__NR_Linux + 313) | ||||
| #define __NR_sched_getattr		(__NR_Linux + 314) | ||||
| #define __NR_renameat2			(__NR_Linux + 315) | ||||
| #define __NR_seccomp			(__NR_Linux + 316) | ||||
| 
 | ||||
| /*
 | ||||
|  * Offset of the last N32 flavoured syscall | ||||
|  */ | ||||
| #define __NR_Linux_syscalls		315 | ||||
| #define __NR_Linux_syscalls		316 | ||||
| 
 | ||||
| #endif /* _MIPS_SIM == _MIPS_SIM_NABI32 */ | ||||
| 
 | ||||
| #define __NR_N32_Linux			6000 | ||||
| #define __NR_N32_Linux_syscalls		315 | ||||
| #define __NR_N32_Linux_syscalls		316 | ||||
| 
 | ||||
| #endif /* _UAPI_ASM_UNISTD_H */ | ||||
|  |  | |||
|  | @ -578,3 +578,4 @@ EXPORT(sys_call_table) | |||
| 	PTR	sys_sched_setattr | ||||
| 	PTR	sys_sched_getattr		/* 4350 */ | ||||
| 	PTR	sys_renameat2 | ||||
| 	PTR	sys_seccomp | ||||
|  |  | |||
|  | @ -431,4 +431,5 @@ EXPORT(sys_call_table) | |||
| 	PTR	sys_sched_setattr | ||||
| 	PTR	sys_sched_getattr		/* 5310 */ | ||||
| 	PTR	sys_renameat2 | ||||
| 	PTR	sys_seccomp | ||||
| 	.size	sys_call_table,.-sys_call_table | ||||
|  |  | |||
|  | @ -424,4 +424,5 @@ EXPORT(sysn32_call_table) | |||
| 	PTR	sys_sched_setattr | ||||
| 	PTR	sys_sched_getattr | ||||
| 	PTR	sys_renameat2			/* 6315 */ | ||||
| 	PTR	sys_seccomp | ||||
| 	.size	sysn32_call_table,.-sysn32_call_table | ||||
|  |  | |||
|  | @ -557,4 +557,5 @@ EXPORT(sys32_call_table) | |||
| 	PTR	sys_sched_setattr | ||||
| 	PTR	sys_sched_getattr		/* 4350 */ | ||||
| 	PTR	sys_renameat2 | ||||
| 	PTR	sys_seccomp | ||||
| 	.size	sys32_call_table,.-sys32_call_table | ||||
|  |  | |||
|  | @ -360,3 +360,4 @@ | |||
| 351	i386	sched_setattr		sys_sched_setattr | ||||
| 352	i386	sched_getattr		sys_sched_getattr | ||||
| 353	i386	renameat2		sys_renameat2 | ||||
| 354	i386	seccomp			sys_seccomp | ||||
|  |  | |||
|  | @ -323,6 +323,7 @@ | |||
| 314	common	sched_setattr		sys_sched_setattr | ||||
| 315	common	sched_getattr		sys_sched_getattr | ||||
| 316	common	renameat2		sys_renameat2 | ||||
| 317	common	seccomp			sys_seccomp | ||||
| 
 | ||||
| # | ||||
| # x32-specific system call numbers start at 512 to avoid cache impact | ||||
|  |  | |||
|  | @ -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, | ||||
|  |  | |||
							
								
								
									
										28
									
								
								crypto/asymmetric_keys/mscode.asn1
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								crypto/asymmetric_keys/mscode.asn1
									
										
									
									
									
										Normal file
									
								
							|  | @ -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 | ||||
| } | ||||
							
								
								
									
										126
									
								
								crypto/asymmetric_keys/mscode_parser.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								crypto/asymmetric_keys/mscode_parser.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -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; | ||||
| } | ||||
							
								
								
									
										127
									
								
								crypto/asymmetric_keys/pkcs7.asn1
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								crypto/asymmetric_keys/pkcs7.asn1
									
										
									
									
									
										Normal file
									
								
							|  | @ -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 | ||||
| } | ||||
							
								
								
									
										100
									
								
								crypto/asymmetric_keys/pkcs7_key_type.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								crypto/asymmetric_keys/pkcs7_key_type.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,100 @@ | |||
| /* 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/err.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 | ||||
|  */ | ||||
| static 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); | ||||
							
								
								
									
										396
									
								
								crypto/asymmetric_keys/pkcs7_parser.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										396
									
								
								crypto/asymmetric_keys/pkcs7_parser.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -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; | ||||
| } | ||||
							
								
								
									
										61
									
								
								crypto/asymmetric_keys/pkcs7_parser.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								crypto/asymmetric_keys/pkcs7_parser.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -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) */ | ||||
| }; | ||||
							
								
								
									
										166
									
								
								crypto/asymmetric_keys/pkcs7_trust.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										166
									
								
								crypto/asymmetric_keys/pkcs7_trust.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,166 @@ | |||
| /* 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" | ||||
| 
 | ||||
| /**
 | ||||
|  * 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 = x509_request_asymmetric_key(trust_keyring, x509->subject, | ||||
| 						  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 = x509_request_asymmetric_key(trust_keyring, last->issuer, | ||||
| 					  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); | ||||
							
								
								
									
										321
									
								
								crypto/asymmetric_keys/pkcs7_verify.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										321
									
								
								crypto/asymmetric_keys/pkcs7_verify.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,321 @@ | |||
| /* 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; | ||||
| 
 | ||||
| 		pr_debug("- issuer %s\n", x509->issuer); | ||||
| 		if (x509->authority) | ||||
| 			pr_debug("- authkeyid %s\n", x509->authority); | ||||
| 
 | ||||
| 		if (!x509->authority || | ||||
| 		    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); | ||||
							
								
								
									
										457
									
								
								crypto/asymmetric_keys/verify_pefile.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										457
									
								
								crypto/asymmetric_keys/verify_pefile.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -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; | ||||
| } | ||||
							
								
								
									
										42
									
								
								crypto/asymmetric_keys/verify_pefile.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								crypto/asymmetric_keys/verify_pefile.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -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,86 @@ | |||
| #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 | ||||
| 
 | ||||
| /**
 | ||||
|  * x509_request_asymmetric_key - Request a key by X.509 certificate params. | ||||
|  * @keyring: The keys to search. | ||||
|  * @subject: The name of the subject to whom the key belongs. | ||||
|  * @key_id: The subject key ID as a hex string. | ||||
|  * | ||||
|  * Find a key in the given keyring by subject name and key ID.  These might, | ||||
|  * for instance, be the issuer name and the authority key ID of an X.509 | ||||
|  * certificate that needs to be verified. | ||||
|  */ | ||||
| struct key *x509_request_asymmetric_key(struct key *keyring, | ||||
| 					const char *subject, | ||||
| 					const char *key_id) | ||||
| { | ||||
| 	key_ref_t key; | ||||
| 	size_t subject_len = strlen(subject), key_id_len = strlen(key_id); | ||||
| 	char *id; | ||||
| 
 | ||||
| 	/* Construct an identifier "<subjname>:<keyid>". */ | ||||
| 	id = kmalloc(subject_len + 2 + key_id_len + 1, GFP_KERNEL); | ||||
| 	if (!id) | ||||
| 		return ERR_PTR(-ENOMEM); | ||||
| 
 | ||||
| 	memcpy(id, subject, subject_len); | ||||
| 	id[subject_len + 0] = ':'; | ||||
| 	id[subject_len + 1] = ' '; | ||||
| 	memcpy(id + subject_len + 2, key_id, key_id_len); | ||||
| 	id[subject_len + 2 + key_id_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 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); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(x509_request_asymmetric_key); | ||||
| 
 | ||||
| /*
 | ||||
|  * Set up the signature parameters in an X.509 certificate.  This involves | ||||
|  * digesting the signed data and extracting the signature. | ||||
|  | @ -102,6 +177,38 @@ 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, 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 +262,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 +288,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; | ||||
| 
 | ||||
|  |  | |||
|  | @ -28,6 +28,7 @@ | |||
| #include <linux/suspend.h> | ||||
| #include <linux/syscore_ops.h> | ||||
| #include <linux/reboot.h> | ||||
| #include <linux/security.h> | ||||
| 
 | ||||
| #include <generated/utsrelease.h> | ||||
| 
 | ||||
|  | @ -303,12 +304,17 @@ static int fw_read_file_contents(struct file *file, struct firmware_buf *fw_buf) | |||
| 	if (rc != size) { | ||||
| 		if (rc > 0) | ||||
| 			rc = -EIO; | ||||
| 		vfree(buf); | ||||
| 		return rc; | ||||
| 		goto fail; | ||||
| 	} | ||||
| 	rc = security_kernel_fw_from_file(file, buf, size); | ||||
| 	if (rc) | ||||
| 		goto fail; | ||||
| 	fw_buf->data = buf; | ||||
| 	fw_buf->size = size; | ||||
| 	return 0; | ||||
| fail: | ||||
| 	vfree(buf); | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| static int fw_get_filesystem_firmware(struct device *device, | ||||
|  | @ -612,6 +618,7 @@ static ssize_t firmware_loading_store(struct device *dev, | |||
| { | ||||
| 	struct firmware_priv *fw_priv = to_firmware_priv(dev); | ||||
| 	struct firmware_buf *fw_buf; | ||||
| 	ssize_t written = count; | ||||
| 	int loading = simple_strtol(buf, NULL, 10); | ||||
| 	int i; | ||||
| 
 | ||||
|  | @ -635,6 +642,8 @@ static ssize_t firmware_loading_store(struct device *dev, | |||
| 		break; | ||||
| 	case 0: | ||||
| 		if (test_bit(FW_STATUS_LOADING, &fw_buf->status)) { | ||||
| 			int rc; | ||||
| 
 | ||||
| 			set_bit(FW_STATUS_DONE, &fw_buf->status); | ||||
| 			clear_bit(FW_STATUS_LOADING, &fw_buf->status); | ||||
| 
 | ||||
|  | @ -644,10 +653,23 @@ static ssize_t firmware_loading_store(struct device *dev, | |||
| 			 * see the mapped 'buf->data' once the loading | ||||
| 			 * is completed. | ||||
| 			 * */ | ||||
| 			if (fw_map_pages_buf(fw_buf)) | ||||
| 			rc = fw_map_pages_buf(fw_buf); | ||||
| 			if (rc) | ||||
| 				dev_err(dev, "%s: map pages failed\n", | ||||
| 					__func__); | ||||
| 			else | ||||
| 				rc = security_kernel_fw_from_file(NULL, | ||||
| 						fw_buf->data, fw_buf->size); | ||||
| 
 | ||||
| 			/*
 | ||||
| 			 * Same logic as fw_load_abort, only the DONE bit | ||||
| 			 * is ignored and we set ABORT only on failure. | ||||
| 			 */ | ||||
| 			list_del_init(&fw_buf->pending_list); | ||||
| 			if (rc) { | ||||
| 				set_bit(FW_STATUS_ABORT, &fw_buf->status); | ||||
| 				written = rc; | ||||
| 			} | ||||
| 			complete_all(&fw_buf->completion); | ||||
| 			break; | ||||
| 		} | ||||
|  | @ -661,7 +683,7 @@ static ssize_t firmware_loading_store(struct device *dev, | |||
| 	} | ||||
| out: | ||||
| 	mutex_unlock(&fw_lock); | ||||
| 	return count; | ||||
| 	return written; | ||||
| } | ||||
| 
 | ||||
| static DEVICE_ATTR(loading, 0644, firmware_loading_show, firmware_loading_store); | ||||
|  |  | |||
|  | @ -491,11 +491,10 @@ static int tpm_startup(struct tpm_chip *chip, __be16 startup_type) | |||
| int tpm_get_timeouts(struct tpm_chip *chip) | ||||
| { | ||||
| 	struct tpm_cmd_t tpm_cmd; | ||||
| 	struct timeout_t *timeout_cap; | ||||
| 	unsigned long new_timeout[4]; | ||||
| 	unsigned long old_timeout[4]; | ||||
| 	struct duration_t *duration_cap; | ||||
| 	ssize_t rc; | ||||
| 	u32 timeout; | ||||
| 	unsigned int scale = 1; | ||||
| 
 | ||||
| 	tpm_cmd.header.in = tpm_getcap_header; | ||||
| 	tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP; | ||||
|  | @ -529,25 +528,46 @@ int tpm_get_timeouts(struct tpm_chip *chip) | |||
| 	    != sizeof(tpm_cmd.header.out) + sizeof(u32) + 4 * sizeof(u32)) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	timeout_cap = &tpm_cmd.params.getcap_out.cap.timeout; | ||||
| 	/* Don't overwrite default if value is 0 */ | ||||
| 	timeout = be32_to_cpu(timeout_cap->a); | ||||
| 	if (timeout && timeout < 1000) { | ||||
| 		/* timeouts in msec rather usec */ | ||||
| 		scale = 1000; | ||||
| 		chip->vendor.timeout_adjusted = true; | ||||
| 	old_timeout[0] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.a); | ||||
| 	old_timeout[1] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.b); | ||||
| 	old_timeout[2] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.c); | ||||
| 	old_timeout[3] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.d); | ||||
| 	memcpy(new_timeout, old_timeout, sizeof(new_timeout)); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Provide ability for vendor overrides of timeout values in case | ||||
| 	 * of misreporting. | ||||
| 	 */ | ||||
| 	if (chip->ops->update_timeouts != NULL) | ||||
| 		chip->vendor.timeout_adjusted = | ||||
| 			chip->ops->update_timeouts(chip, new_timeout); | ||||
| 
 | ||||
| 	if (!chip->vendor.timeout_adjusted) { | ||||
| 		/* Don't overwrite default if value is 0 */ | ||||
| 		if (new_timeout[0] != 0 && new_timeout[0] < 1000) { | ||||
| 			int i; | ||||
| 
 | ||||
| 			/* timeouts in msec rather usec */ | ||||
| 			for (i = 0; i != ARRAY_SIZE(new_timeout); i++) | ||||
| 				new_timeout[i] *= 1000; | ||||
| 			chip->vendor.timeout_adjusted = true; | ||||
| 		} | ||||
| 	} | ||||
| 	if (timeout) | ||||
| 		chip->vendor.timeout_a = usecs_to_jiffies(timeout * scale); | ||||
| 	timeout = be32_to_cpu(timeout_cap->b); | ||||
| 	if (timeout) | ||||
| 		chip->vendor.timeout_b = usecs_to_jiffies(timeout * scale); | ||||
| 	timeout = be32_to_cpu(timeout_cap->c); | ||||
| 	if (timeout) | ||||
| 		chip->vendor.timeout_c = usecs_to_jiffies(timeout * scale); | ||||
| 	timeout = be32_to_cpu(timeout_cap->d); | ||||
| 	if (timeout) | ||||
| 		chip->vendor.timeout_d = usecs_to_jiffies(timeout * scale); | ||||
| 
 | ||||
| 	/* Report adjusted timeouts */ | ||||
| 	if (chip->vendor.timeout_adjusted) { | ||||
| 		dev_info(chip->dev, | ||||
| 			 HW_ERR "Adjusting reported timeouts: A %lu->%luus B %lu->%luus C %lu->%luus D %lu->%luus\n", | ||||
| 			 old_timeout[0], new_timeout[0], | ||||
| 			 old_timeout[1], new_timeout[1], | ||||
| 			 old_timeout[2], new_timeout[2], | ||||
| 			 old_timeout[3], new_timeout[3]); | ||||
| 	} | ||||
| 
 | ||||
| 	chip->vendor.timeout_a = usecs_to_jiffies(new_timeout[0]); | ||||
| 	chip->vendor.timeout_b = usecs_to_jiffies(new_timeout[1]); | ||||
| 	chip->vendor.timeout_c = usecs_to_jiffies(new_timeout[2]); | ||||
| 	chip->vendor.timeout_d = usecs_to_jiffies(new_timeout[3]); | ||||
| 
 | ||||
| duration: | ||||
| 	tpm_cmd.header.in = tpm_getcap_header; | ||||
|  | @ -991,13 +1011,13 @@ int tpm_get_random(u32 chip_num, u8 *out, size_t max) | |||
| 	int err, total = 0, retries = 5; | ||||
| 	u8 *dest = out; | ||||
| 
 | ||||
| 	if (!out || !num_bytes || max > TPM_MAX_RNG_DATA) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	chip = tpm_chip_find_get(chip_num); | ||||
| 	if (chip == NULL) | ||||
| 		return -ENODEV; | ||||
| 
 | ||||
| 	if (!out || !num_bytes || max > TPM_MAX_RNG_DATA) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	do { | ||||
| 		tpm_cmd.header.in = tpm_getrandom_header; | ||||
| 		tpm_cmd.params.getrandom_in.num_bytes = cpu_to_be32(num_bytes); | ||||
|  | @ -1016,6 +1036,7 @@ int tpm_get_random(u32 chip_num, u8 *out, size_t max) | |||
| 		num_bytes -= recd; | ||||
| 	} while (retries-- && total < max); | ||||
| 
 | ||||
| 	tpm_chip_put(chip); | ||||
| 	return total ? total : -EIO; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(tpm_get_random); | ||||
|  | @ -1095,7 +1116,7 @@ struct tpm_chip *tpm_register_hardware(struct device *dev, | |||
| 		goto del_misc; | ||||
| 
 | ||||
| 	if (tpm_add_ppi(&dev->kobj)) | ||||
| 		goto del_misc; | ||||
| 		goto del_sysfs; | ||||
| 
 | ||||
| 	chip->bios_dir = tpm_bios_log_setup(chip->devname); | ||||
| 
 | ||||
|  | @ -1106,6 +1127,8 @@ struct tpm_chip *tpm_register_hardware(struct device *dev, | |||
| 
 | ||||
| 	return chip; | ||||
| 
 | ||||
| del_sysfs: | ||||
| 	tpm_sysfs_del_device(chip); | ||||
| del_misc: | ||||
| 	tpm_dev_del_device(chip); | ||||
| put_device: | ||||
|  |  | |||
|  | @ -235,7 +235,6 @@ static int tpm_bios_measurements_release(struct inode *inode, | |||
| static int tpm_ascii_bios_measurements_show(struct seq_file *m, void *v) | ||||
| { | ||||
| 	int len = 0; | ||||
| 	int i; | ||||
| 	char *eventname; | ||||
| 	struct tcpa_event *event = v; | ||||
| 	unsigned char *event_entry = | ||||
|  | @ -251,8 +250,7 @@ static int tpm_ascii_bios_measurements_show(struct seq_file *m, void *v) | |||
| 	seq_printf(m, "%2d ", event->pcr_index); | ||||
| 
 | ||||
| 	/* 2nd: SHA1 */ | ||||
| 	for (i = 0; i < 20; i++) | ||||
| 		seq_printf(m, "%02x", event->pcr_value[i]); | ||||
| 	seq_printf(m, "%20phN", event->pcr_value); | ||||
| 
 | ||||
| 	/* 3rd: event type identifier */ | ||||
| 	seq_printf(m, " %02x", event->event_type); | ||||
|  |  | |||
|  | @ -714,6 +714,7 @@ tpm_st33_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) | |||
| 	} | ||||
| 
 | ||||
| 	tpm_get_timeouts(chip); | ||||
| 	tpm_do_selftest(chip); | ||||
| 
 | ||||
| 	dev_info(chip->dev, "TPM I2C Initialized\n"); | ||||
| 	return 0; | ||||
|  |  | |||
|  | @ -373,6 +373,36 @@ out_err: | |||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| struct tis_vendor_timeout_override { | ||||
| 	u32 did_vid; | ||||
| 	unsigned long timeout_us[4]; | ||||
| }; | ||||
| 
 | ||||
| static const struct tis_vendor_timeout_override vendor_timeout_overrides[] = { | ||||
| 	/* Atmel 3204 */ | ||||
| 	{ 0x32041114, { (TIS_SHORT_TIMEOUT*1000), (TIS_LONG_TIMEOUT*1000), | ||||
| 			(TIS_SHORT_TIMEOUT*1000), (TIS_SHORT_TIMEOUT*1000) } }, | ||||
| }; | ||||
| 
 | ||||
| static bool tpm_tis_update_timeouts(struct tpm_chip *chip, | ||||
| 				    unsigned long *timeout_cap) | ||||
| { | ||||
| 	int i; | ||||
| 	u32 did_vid; | ||||
| 
 | ||||
| 	did_vid = ioread32(chip->vendor.iobase + TPM_DID_VID(0)); | ||||
| 
 | ||||
| 	for (i = 0; i != ARRAY_SIZE(vendor_timeout_overrides); i++) { | ||||
| 		if (vendor_timeout_overrides[i].did_vid != did_vid) | ||||
| 			continue; | ||||
| 		memcpy(timeout_cap, vendor_timeout_overrides[i].timeout_us, | ||||
| 		       sizeof(vendor_timeout_overrides[i].timeout_us)); | ||||
| 		return true; | ||||
| 	} | ||||
| 
 | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Early probing for iTPM with STS_DATA_EXPECT flaw. | ||||
|  * Try sending command without itpm flag set and if that | ||||
|  | @ -437,6 +467,7 @@ static const struct tpm_class_ops tpm_tis = { | |||
| 	.recv = tpm_tis_recv, | ||||
| 	.send = tpm_tis_send, | ||||
| 	.cancel = tpm_tis_ready, | ||||
| 	.update_timeouts = tpm_tis_update_timeouts, | ||||
| 	.req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID, | ||||
| 	.req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID, | ||||
| 	.req_canceled = tpm_tis_req_canceled, | ||||
|  |  | |||
|  | @ -1216,7 +1216,7 @@ EXPORT_SYMBOL(install_exec_creds); | |||
| /*
 | ||||
|  * determine how safe it is to execute the proposed program | ||||
|  * - the caller must hold ->cred_guard_mutex to protect against | ||||
|  *   PTRACE_ATTACH | ||||
|  *   PTRACE_ATTACH or seccomp thread-sync | ||||
|  */ | ||||
| static void check_unsafe_exec(struct linux_binprm *bprm) | ||||
| { | ||||
|  | @ -1234,7 +1234,7 @@ static void check_unsafe_exec(struct linux_binprm *bprm) | |||
| 	 * This isn't strictly necessary, but it makes it harder for LSMs to | ||||
| 	 * mess up. | ||||
| 	 */ | ||||
| 	if (current->no_new_privs) | ||||
| 	if (task_no_new_privs(current)) | ||||
| 		bprm->unsafe |= LSM_UNSAFE_NO_NEW_PRIVS; | ||||
| 
 | ||||
| 	t = p; | ||||
|  | @ -1272,7 +1272,7 @@ int prepare_binprm(struct linux_binprm *bprm) | |||
| 	bprm->cred->egid = current_egid(); | ||||
| 
 | ||||
| 	if (!(bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) && | ||||
| 	    !current->no_new_privs && | ||||
| 	    !task_no_new_privs(current) && | ||||
| 	    kuid_has_mapping(bprm->cred->user_ns, inode->i_uid) && | ||||
| 	    kgid_has_mapping(bprm->cred->user_ns, inode->i_gid)) { | ||||
| 		/* Set-uid? */ | ||||
|  |  | |||
|  | @ -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, | ||||
|  |  | |||
|  | @ -297,15 +297,11 @@ static void render_cap_t(struct seq_file *m, const char *header, | |||
| 	seq_puts(m, header); | ||||
| 	CAP_FOR_EACH_U32(__capi) { | ||||
| 		seq_printf(m, "%08x", | ||||
| 			   a->cap[(_KERNEL_CAPABILITY_U32S-1) - __capi]); | ||||
| 			   a->cap[CAP_LAST_U32 - __capi]); | ||||
| 	} | ||||
| 	seq_putc(m, '\n'); | ||||
| } | ||||
| 
 | ||||
| /* Remove non-existent capabilities */ | ||||
| #define NORM_CAPS(v) (v.cap[CAP_TO_INDEX(CAP_LAST_CAP)] &= \ | ||||
| 				CAP_TO_MASK(CAP_LAST_CAP + 1) - 1) | ||||
| 
 | ||||
| static inline void task_cap(struct seq_file *m, struct task_struct *p) | ||||
| { | ||||
| 	const struct cred *cred; | ||||
|  | @ -319,11 +315,6 @@ static inline void task_cap(struct seq_file *m, struct task_struct *p) | |||
| 	cap_bset	= cred->cap_bset; | ||||
| 	rcu_read_unlock(); | ||||
| 
 | ||||
| 	NORM_CAPS(cap_inheritable); | ||||
| 	NORM_CAPS(cap_permitted); | ||||
| 	NORM_CAPS(cap_effective); | ||||
| 	NORM_CAPS(cap_bset); | ||||
| 
 | ||||
| 	render_cap_t(m, "CapInh:\t", &cap_inheritable); | ||||
| 	render_cap_t(m, "CapPrm:\t", &cap_permitted); | ||||
| 	render_cap_t(m, "CapEff:\t", &cap_effective); | ||||
|  |  | |||
							
								
								
									
										36
									
								
								include/crypto/pkcs7.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								include/crypto/pkcs7.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -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); | ||||
|  | @ -98,4 +98,8 @@ struct key; | |||
| extern int verify_signature(const struct key *key, | ||||
| 			    const struct public_key_signature *sig); | ||||
| 
 | ||||
| extern struct key *x509_request_asymmetric_key(struct key *keyring, | ||||
| 					       const char *issuer, | ||||
| 					       const char *key_id); | ||||
| 
 | ||||
| #endif /* _LINUX_PUBLIC_KEY_H */ | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -78,8 +78,11 @@ extern const kernel_cap_t __cap_init_eff_set; | |||
| # error Fix up hand-coded capability macro initializers | ||||
| #else /* HAND-CODED capability initializers */ | ||||
| 
 | ||||
| #define CAP_LAST_U32			((_KERNEL_CAPABILITY_U32S) - 1) | ||||
| #define CAP_LAST_U32_VALID_MASK		(CAP_TO_MASK(CAP_LAST_CAP + 1) -1) | ||||
| 
 | ||||
| # define CAP_EMPTY_SET    ((kernel_cap_t){{ 0, 0 }}) | ||||
| # define CAP_FULL_SET     ((kernel_cap_t){{ ~0, ~0 }}) | ||||
| # define CAP_FULL_SET     ((kernel_cap_t){{ ~0, CAP_LAST_U32_VALID_MASK }}) | ||||
| # define CAP_FS_SET       ((kernel_cap_t){{ CAP_FS_MASK_B0 \ | ||||
| 				    | CAP_TO_MASK(CAP_LINUX_IMMUTABLE), \ | ||||
| 				    CAP_FS_MASK_B1 } }) | ||||
|  |  | |||
|  | @ -19,6 +19,7 @@ extern int ima_file_check(struct file *file, int mask); | |||
| extern void ima_file_free(struct file *file); | ||||
| extern int ima_file_mmap(struct file *file, unsigned long prot); | ||||
| extern int ima_module_check(struct file *file); | ||||
| extern int ima_fw_from_file(struct file *file, char *buf, size_t size); | ||||
| 
 | ||||
| #else | ||||
| static inline int ima_bprm_check(struct linux_binprm *bprm) | ||||
|  | @ -46,6 +47,11 @@ static inline int ima_module_check(struct file *file) | |||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static inline int ima_fw_from_file(struct file *file, char *buf, size_t size) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| #endif /* CONFIG_IMA */ | ||||
| 
 | ||||
| #ifdef CONFIG_IMA_APPRAISE | ||||
|  |  | |||
|  | @ -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 */ | ||||
|  |  | |||
							
								
								
									
										448
									
								
								include/linux/pe.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										448
									
								
								include/linux/pe.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -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 */ | ||||
|  | @ -1304,13 +1304,12 @@ struct task_struct { | |||
| 				 * execve */ | ||||
| 	unsigned in_iowait:1; | ||||
| 
 | ||||
| 	/* task may not gain privileges */ | ||||
| 	unsigned no_new_privs:1; | ||||
| 
 | ||||
| 	/* Revert to default priority/policy when forking */ | ||||
| 	unsigned sched_reset_on_fork:1; | ||||
| 	unsigned sched_contributes_to_load:1; | ||||
| 
 | ||||
| 	unsigned long atomic_flags; /* Flags needing atomic access. */ | ||||
| 
 | ||||
| 	pid_t pid; | ||||
| 	pid_t tgid; | ||||
| 
 | ||||
|  | @ -1962,6 +1961,19 @@ static inline void memalloc_noio_restore(unsigned int flags) | |||
| 	current->flags = (current->flags & ~PF_MEMALLOC_NOIO) | flags; | ||||
| } | ||||
| 
 | ||||
| /* Per-process atomic flags. */ | ||||
| #define PFA_NO_NEW_PRIVS 0x00000001	/* May not gain new privileges. */ | ||||
| 
 | ||||
| static inline bool task_no_new_privs(struct task_struct *p) | ||||
| { | ||||
| 	return test_bit(PFA_NO_NEW_PRIVS, &p->atomic_flags); | ||||
| } | ||||
| 
 | ||||
| static inline void task_set_no_new_privs(struct task_struct *p) | ||||
| { | ||||
| 	set_bit(PFA_NO_NEW_PRIVS, &p->atomic_flags); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * task->jobctl flags | ||||
|  */ | ||||
|  |  | |||
|  | @ -3,6 +3,8 @@ | |||
| 
 | ||||
| #include <uapi/linux/seccomp.h> | ||||
| 
 | ||||
| #define SECCOMP_FILTER_FLAG_MASK	(SECCOMP_FILTER_FLAG_TSYNC) | ||||
| 
 | ||||
| #ifdef CONFIG_SECCOMP | ||||
| 
 | ||||
| #include <linux/thread_info.h> | ||||
|  | @ -14,11 +16,11 @@ struct seccomp_filter; | |||
|  * | ||||
|  * @mode:  indicates one of the valid values above for controlled | ||||
|  *         system calls available to a process. | ||||
|  * @filter: The metadata and ruleset for determining what system calls | ||||
|  *          are allowed for a task. | ||||
|  * @filter: must always point to a valid seccomp-filter or NULL as it is | ||||
|  *          accessed without locking during system call entry. | ||||
|  * | ||||
|  *          @filter must only be accessed from the context of current as there | ||||
|  *          is no locking. | ||||
|  *          is no read locking. | ||||
|  */ | ||||
| struct seccomp { | ||||
| 	int mode; | ||||
|  |  | |||
|  | @ -702,6 +702,15 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) | |||
|  *	@inode points to the inode to use as a reference. | ||||
|  *	The current task must be the one that nominated @inode. | ||||
|  *	Return 0 if successful. | ||||
|  * @kernel_fw_from_file: | ||||
|  *	Load firmware from userspace (not called for built-in firmware). | ||||
|  *	@file contains the file structure pointing to the file containing | ||||
|  *	the firmware to load. This argument will be NULL if the firmware | ||||
|  *	was loaded via the uevent-triggered blob-based interface exposed | ||||
|  *	by CONFIG_FW_LOADER_USER_HELPER. | ||||
|  *	@buf pointer to buffer containing firmware contents. | ||||
|  *	@size length of the firmware contents. | ||||
|  *	Return 0 if permission is granted. | ||||
|  * @kernel_module_request: | ||||
|  *	Ability to trigger the kernel to automatically upcall to userspace for | ||||
|  *	userspace to load a kernel module with the given name. | ||||
|  | @ -1565,6 +1574,7 @@ struct security_operations { | |||
| 	void (*cred_transfer)(struct cred *new, const struct cred *old); | ||||
| 	int (*kernel_act_as)(struct cred *new, u32 secid); | ||||
| 	int (*kernel_create_files_as)(struct cred *new, struct inode *inode); | ||||
| 	int (*kernel_fw_from_file)(struct file *file, char *buf, size_t size); | ||||
| 	int (*kernel_module_request)(char *kmod_name); | ||||
| 	int (*kernel_module_from_file)(struct file *file); | ||||
| 	int (*task_fix_setuid) (struct cred *new, const struct cred *old, | ||||
|  | @ -1837,6 +1847,7 @@ int security_prepare_creds(struct cred *new, const struct cred *old, gfp_t gfp); | |||
| void security_transfer_creds(struct cred *new, const struct cred *old); | ||||
| int security_kernel_act_as(struct cred *new, u32 secid); | ||||
| int security_kernel_create_files_as(struct cred *new, struct inode *inode); | ||||
| int security_kernel_fw_from_file(struct file *file, char *buf, size_t size); | ||||
| int security_kernel_module_request(char *kmod_name); | ||||
| int security_kernel_module_from_file(struct file *file); | ||||
| int security_task_fix_setuid(struct cred *new, const struct cred *old, | ||||
|  | @ -2363,6 +2374,12 @@ static inline int security_kernel_create_files_as(struct cred *cred, | |||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static inline int security_kernel_fw_from_file(struct file *file, | ||||
| 					       char *buf, size_t size) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static inline int security_kernel_module_request(char *kmod_name) | ||||
| { | ||||
| 	return 0; | ||||
|  |  | |||
|  | @ -866,4 +866,6 @@ asmlinkage long sys_process_vm_writev(pid_t pid, | |||
| asmlinkage long sys_kcmp(pid_t pid1, pid_t pid2, int type, | ||||
| 			 unsigned long idx1, unsigned long idx2); | ||||
| asmlinkage long sys_finit_module(int fd, const char __user *uargs, int flags); | ||||
| asmlinkage long sys_seccomp(unsigned int op, unsigned int flags, | ||||
| 			    const char __user *uargs); | ||||
| #endif | ||||
|  |  | |||
|  | @ -39,6 +39,9 @@ struct tpm_class_ops { | |||
| 	int (*send) (struct tpm_chip *chip, u8 *buf, size_t len); | ||||
| 	void (*cancel) (struct tpm_chip *chip); | ||||
| 	u8 (*status) (struct tpm_chip *chip); | ||||
| 	bool (*update_timeouts)(struct tpm_chip *chip, | ||||
| 				unsigned long *timeout_cap); | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
| #if defined(CONFIG_TCG_TPM) || defined(CONFIG_TCG_TPM_MODULE) | ||||
|  |  | |||
							
								
								
									
										18
									
								
								include/linux/verify_pefile.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								include/linux/verify_pefile.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -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 */ | ||||
|  | @ -139,7 +139,7 @@ struct netlbl_lsm_cache { | |||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * struct netlbl_lsm_secattr_catmap - NetLabel LSM secattr category bitmap | ||||
|  * struct netlbl_lsm_catmap - NetLabel LSM secattr category bitmap | ||||
|  * @startbit: the value of the lowest order bit in the bitmap | ||||
|  * @bitmap: the category bitmap | ||||
|  * @next: pointer to the next bitmap "node" or NULL | ||||
|  | @ -162,10 +162,10 @@ struct netlbl_lsm_cache { | |||
| #define NETLBL_CATMAP_SIZE              (NETLBL_CATMAP_MAPSIZE * \ | ||||
| 					 NETLBL_CATMAP_MAPCNT) | ||||
| #define NETLBL_CATMAP_BIT               (NETLBL_CATMAP_MAPTYPE)0x01 | ||||
| struct netlbl_lsm_secattr_catmap { | ||||
| struct netlbl_lsm_catmap { | ||||
| 	u32 startbit; | ||||
| 	NETLBL_CATMAP_MAPTYPE bitmap[NETLBL_CATMAP_MAPCNT]; | ||||
| 	struct netlbl_lsm_secattr_catmap *next; | ||||
| 	struct netlbl_lsm_catmap *next; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  | @ -209,7 +209,7 @@ struct netlbl_lsm_secattr { | |||
| 	struct netlbl_lsm_cache *cache; | ||||
| 	struct { | ||||
| 		struct { | ||||
| 			struct netlbl_lsm_secattr_catmap *cat; | ||||
| 			struct netlbl_lsm_catmap *cat; | ||||
| 			u32 lvl; | ||||
| 		} mls; | ||||
| 		u32 secid; | ||||
|  | @ -258,7 +258,7 @@ static inline void netlbl_secattr_cache_free(struct netlbl_lsm_cache *cache) | |||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * netlbl_secattr_catmap_alloc - Allocate a LSM secattr catmap | ||||
|  * netlbl_catmap_alloc - Allocate a LSM secattr catmap | ||||
|  * @flags: memory allocation flags | ||||
|  * | ||||
|  * Description: | ||||
|  | @ -266,30 +266,28 @@ static inline void netlbl_secattr_cache_free(struct netlbl_lsm_cache *cache) | |||
|  * on failure. | ||||
|  * | ||||
|  */ | ||||
| static inline struct netlbl_lsm_secattr_catmap *netlbl_secattr_catmap_alloc( | ||||
| 	                                                           gfp_t flags) | ||||
| static inline struct netlbl_lsm_catmap *netlbl_catmap_alloc(gfp_t flags) | ||||
| { | ||||
| 	return kzalloc(sizeof(struct netlbl_lsm_secattr_catmap), flags); | ||||
| 	return kzalloc(sizeof(struct netlbl_lsm_catmap), flags); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * netlbl_secattr_catmap_free - Free a LSM secattr catmap | ||||
|  * netlbl_catmap_free - Free a LSM secattr catmap | ||||
|  * @catmap: the category bitmap | ||||
|  * | ||||
|  * Description: | ||||
|  * Free a LSM secattr catmap. | ||||
|  * | ||||
|  */ | ||||
| static inline void netlbl_secattr_catmap_free( | ||||
| 	                              struct netlbl_lsm_secattr_catmap *catmap) | ||||
| static inline void netlbl_catmap_free(struct netlbl_lsm_catmap *catmap) | ||||
| { | ||||
| 	struct netlbl_lsm_secattr_catmap *iter; | ||||
| 	struct netlbl_lsm_catmap *iter; | ||||
| 
 | ||||
| 	do { | ||||
| 	while (catmap) { | ||||
| 		iter = catmap; | ||||
| 		catmap = catmap->next; | ||||
| 		kfree(iter); | ||||
| 	} while (catmap); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  | @ -321,7 +319,7 @@ static inline void netlbl_secattr_destroy(struct netlbl_lsm_secattr *secattr) | |||
| 	if (secattr->flags & NETLBL_SECATTR_CACHE) | ||||
| 		netlbl_secattr_cache_free(secattr->cache); | ||||
| 	if (secattr->flags & NETLBL_SECATTR_MLS_CAT) | ||||
| 		netlbl_secattr_catmap_free(secattr->attr.mls.cat); | ||||
| 		netlbl_catmap_free(secattr->attr.mls.cat); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  | @ -390,17 +388,22 @@ int netlbl_cfg_cipsov4_map_add(u32 doi, | |||
| /*
 | ||||
|  * LSM security attribute operations | ||||
|  */ | ||||
| int netlbl_secattr_catmap_walk(struct netlbl_lsm_secattr_catmap *catmap, | ||||
| 			       u32 offset); | ||||
| int netlbl_secattr_catmap_walk_rng(struct netlbl_lsm_secattr_catmap *catmap, | ||||
| 				   u32 offset); | ||||
| int netlbl_secattr_catmap_setbit(struct netlbl_lsm_secattr_catmap *catmap, | ||||
| 				 u32 bit, | ||||
| 				 gfp_t flags); | ||||
| int netlbl_secattr_catmap_setrng(struct netlbl_lsm_secattr_catmap *catmap, | ||||
| 				 u32 start, | ||||
| 				 u32 end, | ||||
| 				 gfp_t flags); | ||||
| int netlbl_catmap_walk(struct netlbl_lsm_catmap *catmap, u32 offset); | ||||
| int netlbl_catmap_walkrng(struct netlbl_lsm_catmap *catmap, u32 offset); | ||||
| int netlbl_catmap_getlong(struct netlbl_lsm_catmap *catmap, | ||||
| 			  u32 *offset, | ||||
| 			  unsigned long *bitmap); | ||||
| int netlbl_catmap_setbit(struct netlbl_lsm_catmap **catmap, | ||||
| 			 u32 bit, | ||||
| 			 gfp_t flags); | ||||
| int netlbl_catmap_setrng(struct netlbl_lsm_catmap **catmap, | ||||
| 			 u32 start, | ||||
| 			 u32 end, | ||||
| 			 gfp_t flags); | ||||
| int netlbl_catmap_setlong(struct netlbl_lsm_catmap **catmap, | ||||
| 			  u32 offset, | ||||
| 			  unsigned long bitmap, | ||||
| 			  gfp_t flags); | ||||
| 
 | ||||
| /*
 | ||||
|  * LSM protocol operations (NetLabel LSM/kernel API) | ||||
|  | @ -492,30 +495,39 @@ static inline int netlbl_cfg_cipsov4_map_add(u32 doi, | |||
| { | ||||
| 	return -ENOSYS; | ||||
| } | ||||
| static inline int netlbl_secattr_catmap_walk( | ||||
| 	                              struct netlbl_lsm_secattr_catmap *catmap, | ||||
| 				      u32 offset) | ||||
| static inline int netlbl_catmap_walk(struct netlbl_lsm_catmap *catmap, | ||||
| 				     u32 offset) | ||||
| { | ||||
| 	return -ENOENT; | ||||
| } | ||||
| static inline int netlbl_secattr_catmap_walk_rng( | ||||
| 				      struct netlbl_lsm_secattr_catmap *catmap, | ||||
| 				      u32 offset) | ||||
| static inline int netlbl_catmap_walkrng(struct netlbl_lsm_catmap *catmap, | ||||
| 					u32 offset) | ||||
| { | ||||
| 	return -ENOENT; | ||||
| } | ||||
| static inline int netlbl_secattr_catmap_setbit( | ||||
| 	                              struct netlbl_lsm_secattr_catmap *catmap, | ||||
| 				      u32 bit, | ||||
| 				      gfp_t flags) | ||||
| static inline int netlbl_catmap_getlong(struct netlbl_lsm_catmap *catmap, | ||||
| 					u32 *offset, | ||||
| 					unsigned long *bitmap) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| static inline int netlbl_secattr_catmap_setrng( | ||||
| 	                              struct netlbl_lsm_secattr_catmap *catmap, | ||||
| 				      u32 start, | ||||
| 				      u32 end, | ||||
| 				      gfp_t flags) | ||||
| static inline int netlbl_catmap_setbit(struct netlbl_lsm_catmap **catmap, | ||||
| 				       u32 bit, | ||||
| 				       gfp_t flags) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| static inline int netlbl_catmap_setrng(struct netlbl_lsm_catmap **catmap, | ||||
| 				       u32 start, | ||||
| 				       u32 end, | ||||
| 				       gfp_t flags) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| static int netlbl_catmap_setlong(struct netlbl_lsm_catmap **catmap, | ||||
| 				 u32 offset, | ||||
| 				 unsigned long bitmap, | ||||
| 				 gfp_t flags) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
|  |  | |||
|  | @ -699,9 +699,11 @@ __SYSCALL(__NR_sched_setattr, sys_sched_setattr) | |||
| __SYSCALL(__NR_sched_getattr, sys_sched_getattr) | ||||
| #define __NR_renameat2 276 | ||||
| __SYSCALL(__NR_renameat2, sys_renameat2) | ||||
| #define __NR_seccomp 277 | ||||
| __SYSCALL(__NR_seccomp, sys_seccomp) | ||||
| 
 | ||||
| #undef __NR_syscalls | ||||
| #define __NR_syscalls 277 | ||||
| #define __NR_syscalls 278 | ||||
| 
 | ||||
| /*
 | ||||
|  * All syscalls below here should go away really, | ||||
|  |  | |||
|  | @ -10,6 +10,13 @@ | |||
| #define SECCOMP_MODE_STRICT	1 /* uses hard-coded filter. */ | ||||
| #define SECCOMP_MODE_FILTER	2 /* uses user-supplied filter. */ | ||||
| 
 | ||||
| /* Valid operations for seccomp syscall. */ | ||||
| #define SECCOMP_SET_MODE_STRICT	0 | ||||
| #define SECCOMP_SET_MODE_FILTER	1 | ||||
| 
 | ||||
| /* Valid flags for SECCOMP_SET_MODE_FILTER */ | ||||
| #define SECCOMP_FILTER_FLAG_TSYNC	1 | ||||
| 
 | ||||
| /*
 | ||||
|  * All BPF programs must return a 32-bit value. | ||||
|  * The bottom 16-bits are for optional return data. | ||||
|  |  | |||
|  | @ -1677,7 +1677,7 @@ void audit_log_cap(struct audit_buffer *ab, char *prefix, kernel_cap_t *cap) | |||
| 	audit_log_format(ab, " %s=", prefix); | ||||
| 	CAP_FOR_EACH_U32(i) { | ||||
| 		audit_log_format(ab, "%08x", | ||||
| 				 cap->cap[(_KERNEL_CAPABILITY_U32S-1) - i]); | ||||
| 				 cap->cap[CAP_LAST_U32 - i]); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -258,6 +258,10 @@ SYSCALL_DEFINE2(capset, cap_user_header_t, header, const cap_user_data_t, data) | |||
| 		i++; | ||||
| 	} | ||||
| 
 | ||||
| 	effective.cap[CAP_LAST_U32] &= CAP_LAST_U32_VALID_MASK; | ||||
| 	permitted.cap[CAP_LAST_U32] &= CAP_LAST_U32_VALID_MASK; | ||||
| 	inheritable.cap[CAP_LAST_U32] &= CAP_LAST_U32_VALID_MASK; | ||||
| 
 | ||||
| 	new = prepare_creds(); | ||||
| 	if (!new) | ||||
| 		return -ENOMEM; | ||||
|  |  | |||
|  | @ -315,6 +315,15 @@ static struct task_struct *dup_task_struct(struct task_struct *orig) | |||
| 		goto free_ti; | ||||
| 
 | ||||
| 	tsk->stack = ti; | ||||
| #ifdef CONFIG_SECCOMP | ||||
| 	/*
 | ||||
| 	 * We must handle setting up seccomp filters once we're under | ||||
| 	 * the sighand lock in case orig has changed between now and | ||||
| 	 * then. Until then, filter must be NULL to avoid messing up | ||||
| 	 * the usage counts on the error path calling free_task. | ||||
| 	 */ | ||||
| 	tsk->seccomp.filter = NULL; | ||||
| #endif | ||||
| 
 | ||||
| 	setup_thread_stack(tsk, orig); | ||||
| 	clear_user_return_notifier(tsk); | ||||
|  | @ -1081,6 +1090,39 @@ static int copy_signal(unsigned long clone_flags, struct task_struct *tsk) | |||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void copy_seccomp(struct task_struct *p) | ||||
| { | ||||
| #ifdef CONFIG_SECCOMP | ||||
| 	/*
 | ||||
| 	 * Must be called with sighand->lock held, which is common to | ||||
| 	 * all threads in the group. Holding cred_guard_mutex is not | ||||
| 	 * needed because this new task is not yet running and cannot | ||||
| 	 * be racing exec. | ||||
| 	 */ | ||||
| 	BUG_ON(!spin_is_locked(¤t->sighand->siglock)); | ||||
| 
 | ||||
| 	/* Ref-count the new filter user, and assign it. */ | ||||
| 	get_seccomp_filter(current); | ||||
| 	p->seccomp = current->seccomp; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Explicitly enable no_new_privs here in case it got set | ||||
| 	 * between the task_struct being duplicated and holding the | ||||
| 	 * sighand lock. The seccomp state and nnp must be in sync. | ||||
| 	 */ | ||||
| 	if (task_no_new_privs(current)) | ||||
| 		task_set_no_new_privs(p); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * If the parent gained a seccomp mode after copying thread | ||||
| 	 * flags and between before we held the sighand lock, we have | ||||
| 	 * to manually enable the seccomp thread flag here. | ||||
| 	 */ | ||||
| 	if (p->seccomp.mode != SECCOMP_MODE_DISABLED) | ||||
| 		set_tsk_thread_flag(p, TIF_SECCOMP); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| SYSCALL_DEFINE1(set_tid_address, int __user *, tidptr) | ||||
| { | ||||
| 	current->clear_child_tid = tidptr; | ||||
|  | @ -1195,7 +1237,6 @@ static struct task_struct *copy_process(unsigned long clone_flags, | |||
| 		goto fork_out; | ||||
| 
 | ||||
| 	ftrace_graph_init_task(p); | ||||
| 	get_seccomp_filter(p); | ||||
| 
 | ||||
| 	rt_mutex_init_task(p); | ||||
| 
 | ||||
|  | @ -1434,6 +1475,12 @@ static struct task_struct *copy_process(unsigned long clone_flags, | |||
| 
 | ||||
| 	spin_lock(¤t->sighand->siglock); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Copy seccomp details explicitly here, in case they were changed | ||||
| 	 * before holding sighand lock. | ||||
| 	 */ | ||||
| 	copy_seccomp(p); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Process group and session signals need to be delivered to just the | ||||
| 	 * parent before the fork or both the parent and the child after the | ||||
|  |  | |||
							
								
								
									
										414
									
								
								kernel/seccomp.c
									
										
									
									
									
								
							
							
						
						
									
										414
									
								
								kernel/seccomp.c
									
										
									
									
									
								
							|  | @ -18,15 +18,17 @@ | |||
| #include <linux/compat.h> | ||||
| #include <linux/sched.h> | ||||
| #include <linux/seccomp.h> | ||||
| #include <linux/slab.h> | ||||
| #include <linux/syscalls.h> | ||||
| 
 | ||||
| /* #define SECCOMP_DEBUG 1 */ | ||||
| 
 | ||||
| #ifdef CONFIG_SECCOMP_FILTER | ||||
| #include <asm/syscall.h> | ||||
| #include <linux/filter.h> | ||||
| #include <linux/pid.h> | ||||
| #include <linux/ptrace.h> | ||||
| #include <linux/security.h> | ||||
| #include <linux/slab.h> | ||||
| #include <linux/tracehook.h> | ||||
| #include <linux/uaccess.h> | ||||
| 
 | ||||
|  | @ -172,21 +174,24 @@ static int seccomp_check_filter(struct sock_filter *filter, unsigned int flen) | |||
|  */ | ||||
| static u32 seccomp_run_filters(int syscall) | ||||
| { | ||||
| 	struct seccomp_filter *f; | ||||
| 	struct seccomp_filter *f = ACCESS_ONCE(current->seccomp.filter); | ||||
| 	struct seccomp_data sd; | ||||
| 	u32 ret = SECCOMP_RET_ALLOW; | ||||
| 
 | ||||
| 	/* Ensure unexpected behavior doesn't result in failing open. */ | ||||
| 	if (WARN_ON(current->seccomp.filter == NULL)) | ||||
| 	if (unlikely(WARN_ON(f == NULL))) | ||||
| 		return SECCOMP_RET_KILL; | ||||
| 
 | ||||
| 	/* Make sure cross-thread synced filter points somewhere sane. */ | ||||
| 	smp_read_barrier_depends(); | ||||
| 
 | ||||
| 	populate_seccomp_data(&sd); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * All filters in the list are evaluated and the lowest BPF return | ||||
| 	 * value always takes priority (ignoring the DATA). | ||||
| 	 */ | ||||
| 	for (f = current->seccomp.filter; f; f = f->prev) { | ||||
| 	for (; f; f = f->prev) { | ||||
| 		u32 cur_ret = SK_RUN_FILTER(f->prog, (void *)&sd); | ||||
| 
 | ||||
| 		if ((cur_ret & SECCOMP_RET_ACTION) < (ret & SECCOMP_RET_ACTION)) | ||||
|  | @ -194,29 +199,159 @@ static u32 seccomp_run_filters(int syscall) | |||
| 	} | ||||
| 	return ret; | ||||
| } | ||||
| #endif /* CONFIG_SECCOMP_FILTER */ | ||||
| 
 | ||||
| static inline bool seccomp_may_assign_mode(unsigned long seccomp_mode) | ||||
| { | ||||
| 	BUG_ON(!spin_is_locked(¤t->sighand->siglock)); | ||||
| 
 | ||||
| 	if (current->seccomp.mode && current->seccomp.mode != seccomp_mode) | ||||
| 		return false; | ||||
| 
 | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| static inline void seccomp_assign_mode(struct task_struct *task, | ||||
| 				       unsigned long seccomp_mode) | ||||
| { | ||||
| 	BUG_ON(!spin_is_locked(&task->sighand->siglock)); | ||||
| 
 | ||||
| 	task->seccomp.mode = seccomp_mode; | ||||
| 	/*
 | ||||
| 	 * Make sure TIF_SECCOMP cannot be set before the mode (and | ||||
| 	 * filter) is set. | ||||
| 	 */ | ||||
| 	smp_mb__before_atomic(); | ||||
| 	set_tsk_thread_flag(task, TIF_SECCOMP); | ||||
| } | ||||
| 
 | ||||
| #ifdef CONFIG_SECCOMP_FILTER | ||||
| /* Returns 1 if the parent is an ancestor of the child. */ | ||||
| static int is_ancestor(struct seccomp_filter *parent, | ||||
| 		       struct seccomp_filter *child) | ||||
| { | ||||
| 	/* NULL is the root ancestor. */ | ||||
| 	if (parent == NULL) | ||||
| 		return 1; | ||||
| 	for (; child; child = child->prev) | ||||
| 		if (child == parent) | ||||
| 			return 1; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * seccomp_attach_filter: Attaches a seccomp filter to current. | ||||
|  * seccomp_can_sync_threads: checks if all threads can be synchronized | ||||
|  * | ||||
|  * Expects sighand and cred_guard_mutex locks to be held. | ||||
|  * | ||||
|  * Returns 0 on success, -ve on error, or the pid of a thread which was | ||||
|  * either not in the correct seccomp mode or it did not have an ancestral | ||||
|  * seccomp filter. | ||||
|  */ | ||||
| static inline pid_t seccomp_can_sync_threads(void) | ||||
| { | ||||
| 	struct task_struct *thread, *caller; | ||||
| 
 | ||||
| 	BUG_ON(!mutex_is_locked(¤t->signal->cred_guard_mutex)); | ||||
| 	BUG_ON(!spin_is_locked(¤t->sighand->siglock)); | ||||
| 
 | ||||
| 	/* Validate all threads being eligible for synchronization. */ | ||||
| 	caller = current; | ||||
| 	for_each_thread(caller, thread) { | ||||
| 		pid_t failed; | ||||
| 
 | ||||
| 		/* Skip current, since it is initiating the sync. */ | ||||
| 		if (thread == caller) | ||||
| 			continue; | ||||
| 
 | ||||
| 		if (thread->seccomp.mode == SECCOMP_MODE_DISABLED || | ||||
| 		    (thread->seccomp.mode == SECCOMP_MODE_FILTER && | ||||
| 		     is_ancestor(thread->seccomp.filter, | ||||
| 				 caller->seccomp.filter))) | ||||
| 			continue; | ||||
| 
 | ||||
| 		/* Return the first thread that cannot be synchronized. */ | ||||
| 		failed = task_pid_vnr(thread); | ||||
| 		/* If the pid cannot be resolved, then return -ESRCH */ | ||||
| 		if (unlikely(WARN_ON(failed == 0))) | ||||
| 			failed = -ESRCH; | ||||
| 		return failed; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * seccomp_sync_threads: sets all threads to use current's filter | ||||
|  * | ||||
|  * Expects sighand and cred_guard_mutex locks to be held, and for | ||||
|  * seccomp_can_sync_threads() to have returned success already | ||||
|  * without dropping the locks. | ||||
|  * | ||||
|  */ | ||||
| static inline void seccomp_sync_threads(void) | ||||
| { | ||||
| 	struct task_struct *thread, *caller; | ||||
| 
 | ||||
| 	BUG_ON(!mutex_is_locked(¤t->signal->cred_guard_mutex)); | ||||
| 	BUG_ON(!spin_is_locked(¤t->sighand->siglock)); | ||||
| 
 | ||||
| 	/* Synchronize all threads. */ | ||||
| 	caller = current; | ||||
| 	for_each_thread(caller, thread) { | ||||
| 		/* Skip current, since it needs no changes. */ | ||||
| 		if (thread == caller) | ||||
| 			continue; | ||||
| 
 | ||||
| 		/* Get a task reference for the new leaf node. */ | ||||
| 		get_seccomp_filter(caller); | ||||
| 		/*
 | ||||
| 		 * Drop the task reference to the shared ancestor since | ||||
| 		 * current's path will hold a reference.  (This also | ||||
| 		 * allows a put before the assignment.) | ||||
| 		 */ | ||||
| 		put_seccomp_filter(thread); | ||||
| 		smp_store_release(&thread->seccomp.filter, | ||||
| 				  caller->seccomp.filter); | ||||
| 		/*
 | ||||
| 		 * Opt the other thread into seccomp if needed. | ||||
| 		 * As threads are considered to be trust-realm | ||||
| 		 * equivalent (see ptrace_may_access), it is safe to | ||||
| 		 * allow one thread to transition the other. | ||||
| 		 */ | ||||
| 		if (thread->seccomp.mode == SECCOMP_MODE_DISABLED) { | ||||
| 			/*
 | ||||
| 			 * Don't let an unprivileged task work around | ||||
| 			 * the no_new_privs restriction by creating | ||||
| 			 * a thread that sets it up, enters seccomp, | ||||
| 			 * then dies. | ||||
| 			 */ | ||||
| 			if (task_no_new_privs(caller)) | ||||
| 				task_set_no_new_privs(thread); | ||||
| 
 | ||||
| 			seccomp_assign_mode(thread, SECCOMP_MODE_FILTER); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * seccomp_prepare_filter: Prepares a seccomp filter for use. | ||||
|  * @fprog: BPF program to install | ||||
|  * | ||||
|  * Returns 0 on success or an errno on failure. | ||||
|  * Returns filter on success or an ERR_PTR on failure. | ||||
|  */ | ||||
| static long seccomp_attach_filter(struct sock_fprog *fprog) | ||||
| static struct seccomp_filter *seccomp_prepare_filter(struct sock_fprog *fprog) | ||||
| { | ||||
| 	struct seccomp_filter *filter; | ||||
| 	unsigned long fp_size = fprog->len * sizeof(struct sock_filter); | ||||
| 	unsigned long total_insns = fprog->len; | ||||
| 	unsigned long fp_size; | ||||
| 	struct sock_filter *fp; | ||||
| 	int new_len; | ||||
| 	long ret; | ||||
| 
 | ||||
| 	if (fprog->len == 0 || fprog->len > BPF_MAXINSNS) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	for (filter = current->seccomp.filter; filter; filter = filter->prev) | ||||
| 		total_insns += filter->prog->len + 4;  /* include a 4 instr penalty */ | ||||
| 	if (total_insns > MAX_INSNS_PER_PATH) | ||||
| 		return -ENOMEM; | ||||
| 		return ERR_PTR(-EINVAL); | ||||
| 	BUG_ON(INT_MAX / fprog->len < sizeof(struct sock_filter)); | ||||
| 	fp_size = fprog->len * sizeof(struct sock_filter); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Installing a seccomp filter requires that the task has | ||||
|  | @ -224,14 +359,14 @@ static long seccomp_attach_filter(struct sock_fprog *fprog) | |||
| 	 * This avoids scenarios where unprivileged tasks can affect the | ||||
| 	 * behavior of privileged children. | ||||
| 	 */ | ||||
| 	if (!current->no_new_privs && | ||||
| 	if (!task_no_new_privs(current) && | ||||
| 	    security_capable_noaudit(current_cred(), current_user_ns(), | ||||
| 				     CAP_SYS_ADMIN) != 0) | ||||
| 		return -EACCES; | ||||
| 		return ERR_PTR(-EACCES); | ||||
| 
 | ||||
| 	fp = kzalloc(fp_size, GFP_KERNEL|__GFP_NOWARN); | ||||
| 	if (!fp) | ||||
| 		return -ENOMEM; | ||||
| 		return ERR_PTR(-ENOMEM); | ||||
| 
 | ||||
| 	/* Copy the instructions from fprog. */ | ||||
| 	ret = -EFAULT; | ||||
|  | @ -275,13 +410,7 @@ static long seccomp_attach_filter(struct sock_fprog *fprog) | |||
| 
 | ||||
| 	sk_filter_select_runtime(filter->prog); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * If there is an existing filter, make it the prev and don't drop its | ||||
| 	 * task reference. | ||||
| 	 */ | ||||
| 	filter->prev = current->seccomp.filter; | ||||
| 	current->seccomp.filter = filter; | ||||
| 	return 0; | ||||
| 	return filter; | ||||
| 
 | ||||
| free_filter_prog: | ||||
| 	kfree(filter->prog); | ||||
|  | @ -289,19 +418,20 @@ free_filter: | |||
| 	kfree(filter); | ||||
| free_prog: | ||||
| 	kfree(fp); | ||||
| 	return ret; | ||||
| 	return ERR_PTR(ret); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * seccomp_attach_user_filter - attaches a user-supplied sock_fprog | ||||
|  * seccomp_prepare_user_filter - prepares a user-supplied sock_fprog | ||||
|  * @user_filter: pointer to the user data containing a sock_fprog. | ||||
|  * | ||||
|  * Returns 0 on success and non-zero otherwise. | ||||
|  */ | ||||
| static long seccomp_attach_user_filter(char __user *user_filter) | ||||
| static struct seccomp_filter * | ||||
| seccomp_prepare_user_filter(const char __user *user_filter) | ||||
| { | ||||
| 	struct sock_fprog fprog; | ||||
| 	long ret = -EFAULT; | ||||
| 	struct seccomp_filter *filter = ERR_PTR(-EFAULT); | ||||
| 
 | ||||
| #ifdef CONFIG_COMPAT | ||||
| 	if (is_compat_task()) { | ||||
|  | @ -314,9 +444,56 @@ static long seccomp_attach_user_filter(char __user *user_filter) | |||
| #endif | ||||
| 	if (copy_from_user(&fprog, user_filter, sizeof(fprog))) | ||||
| 		goto out; | ||||
| 	ret = seccomp_attach_filter(&fprog); | ||||
| 	filter = seccomp_prepare_filter(&fprog); | ||||
| out: | ||||
| 	return ret; | ||||
| 	return filter; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * seccomp_attach_filter: validate and attach filter | ||||
|  * @flags:  flags to change filter behavior | ||||
|  * @filter: seccomp filter to add to the current process | ||||
|  * | ||||
|  * Caller must be holding current->sighand->siglock lock. | ||||
|  * | ||||
|  * Returns 0 on success, -ve on error. | ||||
|  */ | ||||
| static long seccomp_attach_filter(unsigned int flags, | ||||
| 				  struct seccomp_filter *filter) | ||||
| { | ||||
| 	unsigned long total_insns; | ||||
| 	struct seccomp_filter *walker; | ||||
| 
 | ||||
| 	BUG_ON(!spin_is_locked(¤t->sighand->siglock)); | ||||
| 
 | ||||
| 	/* Validate resulting filter length. */ | ||||
| 	total_insns = filter->prog->len; | ||||
| 	for (walker = current->seccomp.filter; walker; walker = walker->prev) | ||||
| 		total_insns += walker->prog->len + 4;  /* 4 instr penalty */ | ||||
| 	if (total_insns > MAX_INSNS_PER_PATH) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	/* If thread sync has been requested, check that it is possible. */ | ||||
| 	if (flags & SECCOMP_FILTER_FLAG_TSYNC) { | ||||
| 		int ret; | ||||
| 
 | ||||
| 		ret = seccomp_can_sync_threads(); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * If there is an existing filter, make it the prev and don't drop its | ||||
| 	 * task reference. | ||||
| 	 */ | ||||
| 	filter->prev = current->seccomp.filter; | ||||
| 	current->seccomp.filter = filter; | ||||
| 
 | ||||
| 	/* Now that the new filter is in place, synchronize to all threads. */ | ||||
| 	if (flags & SECCOMP_FILTER_FLAG_TSYNC) | ||||
| 		seccomp_sync_threads(); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /* get_seccomp_filter - increments the reference count of the filter on @tsk */ | ||||
|  | @ -329,6 +506,14 @@ void get_seccomp_filter(struct task_struct *tsk) | |||
| 	atomic_inc(&orig->usage); | ||||
| } | ||||
| 
 | ||||
| static inline void seccomp_filter_free(struct seccomp_filter *filter) | ||||
| { | ||||
| 	if (filter) { | ||||
| 		sk_filter_free(filter->prog); | ||||
| 		kfree(filter); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /* put_seccomp_filter - decrements the ref count of tsk->seccomp.filter */ | ||||
| void put_seccomp_filter(struct task_struct *tsk) | ||||
| { | ||||
|  | @ -337,8 +522,7 @@ void put_seccomp_filter(struct task_struct *tsk) | |||
| 	while (orig && atomic_dec_and_test(&orig->usage)) { | ||||
| 		struct seccomp_filter *freeme = orig; | ||||
| 		orig = orig->prev; | ||||
| 		sk_filter_free(freeme->prog); | ||||
| 		kfree(freeme); | ||||
| 		seccomp_filter_free(freeme); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -382,12 +566,17 @@ static int mode1_syscalls_32[] = { | |||
| 
 | ||||
| int __secure_computing(int this_syscall) | ||||
| { | ||||
| 	int mode = current->seccomp.mode; | ||||
| 	int exit_sig = 0; | ||||
| 	int *syscall; | ||||
| 	u32 ret; | ||||
| 
 | ||||
| 	switch (mode) { | ||||
| 	/*
 | ||||
| 	 * Make sure that any changes to mode from another thread have | ||||
| 	 * been seen after TIF_SECCOMP was seen. | ||||
| 	 */ | ||||
| 	rmb(); | ||||
| 
 | ||||
| 	switch (current->seccomp.mode) { | ||||
| 	case SECCOMP_MODE_STRICT: | ||||
| 		syscall = mode1_syscalls; | ||||
| #ifdef CONFIG_COMPAT | ||||
|  | @ -473,47 +662,152 @@ long prctl_get_seccomp(void) | |||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * prctl_set_seccomp: configures current->seccomp.mode | ||||
|  * @seccomp_mode: requested mode to use | ||||
|  * @filter: optional struct sock_fprog for use with SECCOMP_MODE_FILTER | ||||
|  * | ||||
|  * This function may be called repeatedly with a @seccomp_mode of | ||||
|  * SECCOMP_MODE_FILTER to install additional filters.  Every filter | ||||
|  * successfully installed will be evaluated (in reverse order) for each system | ||||
|  * call the task makes. | ||||
|  * seccomp_set_mode_strict: internal function for setting strict seccomp | ||||
|  * | ||||
|  * Once current->seccomp.mode is non-zero, it may not be changed. | ||||
|  * | ||||
|  * Returns 0 on success or -EINVAL on failure. | ||||
|  */ | ||||
| long prctl_set_seccomp(unsigned long seccomp_mode, char __user *filter) | ||||
| static long seccomp_set_mode_strict(void) | ||||
| { | ||||
| 	const unsigned long seccomp_mode = SECCOMP_MODE_STRICT; | ||||
| 	long ret = -EINVAL; | ||||
| 
 | ||||
| 	if (current->seccomp.mode && | ||||
| 	    current->seccomp.mode != seccomp_mode) | ||||
| 	spin_lock_irq(¤t->sighand->siglock); | ||||
| 
 | ||||
| 	if (!seccomp_may_assign_mode(seccomp_mode)) | ||||
| 		goto out; | ||||
| 
 | ||||
| #ifdef TIF_NOTSC | ||||
| 	disable_TSC(); | ||||
| #endif | ||||
| 	seccomp_assign_mode(current, seccomp_mode); | ||||
| 	ret = 0; | ||||
| 
 | ||||
| out: | ||||
| 	spin_unlock_irq(¤t->sighand->siglock); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| #ifdef CONFIG_SECCOMP_FILTER | ||||
| /**
 | ||||
|  * seccomp_set_mode_filter: internal function for setting seccomp filter | ||||
|  * @flags:  flags to change filter behavior | ||||
|  * @filter: struct sock_fprog containing filter | ||||
|  * | ||||
|  * This function may be called repeatedly to install additional filters. | ||||
|  * Every filter successfully installed will be evaluated (in reverse order) | ||||
|  * for each system call the task makes. | ||||
|  * | ||||
|  * Once current->seccomp.mode is non-zero, it may not be changed. | ||||
|  * | ||||
|  * Returns 0 on success or -EINVAL on failure. | ||||
|  */ | ||||
| static long seccomp_set_mode_filter(unsigned int flags, | ||||
| 				    const char __user *filter) | ||||
| { | ||||
| 	const unsigned long seccomp_mode = SECCOMP_MODE_FILTER; | ||||
| 	struct seccomp_filter *prepared = NULL; | ||||
| 	long ret = -EINVAL; | ||||
| 
 | ||||
| 	/* Validate flags. */ | ||||
| 	if (flags & ~SECCOMP_FILTER_FLAG_MASK) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	/* Prepare the new filter before holding any locks. */ | ||||
| 	prepared = seccomp_prepare_user_filter(filter); | ||||
| 	if (IS_ERR(prepared)) | ||||
| 		return PTR_ERR(prepared); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Make sure we cannot change seccomp or nnp state via TSYNC | ||||
| 	 * while another thread is in the middle of calling exec. | ||||
| 	 */ | ||||
| 	if (flags & SECCOMP_FILTER_FLAG_TSYNC && | ||||
| 	    mutex_lock_killable(¤t->signal->cred_guard_mutex)) | ||||
| 		goto out_free; | ||||
| 
 | ||||
| 	spin_lock_irq(¤t->sighand->siglock); | ||||
| 
 | ||||
| 	if (!seccomp_may_assign_mode(seccomp_mode)) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	ret = seccomp_attach_filter(flags, prepared); | ||||
| 	if (ret) | ||||
| 		goto out; | ||||
| 	/* Do not free the successfully attached filter. */ | ||||
| 	prepared = NULL; | ||||
| 
 | ||||
| 	seccomp_assign_mode(current, seccomp_mode); | ||||
| out: | ||||
| 	spin_unlock_irq(¤t->sighand->siglock); | ||||
| 	if (flags & SECCOMP_FILTER_FLAG_TSYNC) | ||||
| 		mutex_unlock(¤t->signal->cred_guard_mutex); | ||||
| out_free: | ||||
| 	seccomp_filter_free(prepared); | ||||
| 	return ret; | ||||
| } | ||||
| #else | ||||
| static inline long seccomp_set_mode_filter(unsigned int flags, | ||||
| 					   const char __user *filter) | ||||
| { | ||||
| 	return -EINVAL; | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| /* Common entry point for both prctl and syscall. */ | ||||
| static long do_seccomp(unsigned int op, unsigned int flags, | ||||
| 		       const char __user *uargs) | ||||
| { | ||||
| 	switch (op) { | ||||
| 	case SECCOMP_SET_MODE_STRICT: | ||||
| 		if (flags != 0 || uargs != NULL) | ||||
| 			return -EINVAL; | ||||
| 		return seccomp_set_mode_strict(); | ||||
| 	case SECCOMP_SET_MODE_FILTER: | ||||
| 		return seccomp_set_mode_filter(flags, uargs); | ||||
| 	default: | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| SYSCALL_DEFINE3(seccomp, unsigned int, op, unsigned int, flags, | ||||
| 			 const char __user *, uargs) | ||||
| { | ||||
| 	return do_seccomp(op, flags, uargs); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * prctl_set_seccomp: configures current->seccomp.mode | ||||
|  * @seccomp_mode: requested mode to use | ||||
|  * @filter: optional struct sock_fprog for use with SECCOMP_MODE_FILTER | ||||
|  * | ||||
|  * Returns 0 on success or -EINVAL on failure. | ||||
|  */ | ||||
| long prctl_set_seccomp(unsigned long seccomp_mode, char __user *filter) | ||||
| { | ||||
| 	unsigned int op; | ||||
| 	char __user *uargs; | ||||
| 
 | ||||
| 	switch (seccomp_mode) { | ||||
| 	case SECCOMP_MODE_STRICT: | ||||
| 		ret = 0; | ||||
| #ifdef TIF_NOTSC | ||||
| 		disable_TSC(); | ||||
| #endif | ||||
| 		op = SECCOMP_SET_MODE_STRICT; | ||||
| 		/*
 | ||||
| 		 * Setting strict mode through prctl always ignored filter, | ||||
| 		 * so make sure it is always NULL here to pass the internal | ||||
| 		 * check in do_seccomp(). | ||||
| 		 */ | ||||
| 		uargs = NULL; | ||||
| 		break; | ||||
| #ifdef CONFIG_SECCOMP_FILTER | ||||
| 	case SECCOMP_MODE_FILTER: | ||||
| 		ret = seccomp_attach_user_filter(filter); | ||||
| 		if (ret) | ||||
| 			goto out; | ||||
| 		op = SECCOMP_SET_MODE_FILTER; | ||||
| 		uargs = filter; | ||||
| 		break; | ||||
| #endif | ||||
| 	default: | ||||
| 		goto out; | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	current->seccomp.mode = seccomp_mode; | ||||
| 	set_thread_flag(TIF_SECCOMP); | ||||
| out: | ||||
| 	return ret; | ||||
| 	/* prctl interface doesn't have flags, so they are always zero. */ | ||||
| 	return do_seccomp(op, 0, uargs); | ||||
| } | ||||
|  |  | |||
|  | @ -1990,12 +1990,12 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3, | |||
| 		if (arg2 != 1 || arg3 || arg4 || arg5) | ||||
| 			return -EINVAL; | ||||
| 
 | ||||
| 		current->no_new_privs = 1; | ||||
| 		task_set_no_new_privs(current); | ||||
| 		break; | ||||
| 	case PR_GET_NO_NEW_PRIVS: | ||||
| 		if (arg2 || arg3 || arg4 || arg5) | ||||
| 			return -EINVAL; | ||||
| 		return current->no_new_privs ? 1 : 0; | ||||
| 		return task_no_new_privs(current) ? 1 : 0; | ||||
| 	case PR_GET_THP_DISABLE: | ||||
| 		if (arg2 || arg3 || arg4 || arg5) | ||||
| 			return -EINVAL; | ||||
|  |  | |||
|  | @ -213,3 +213,6 @@ cond_syscall(compat_sys_open_by_handle_at); | |||
| 
 | ||||
| /* compare kernel pointers */ | ||||
| cond_syscall(sys_kcmp); | ||||
| 
 | ||||
| /* operate on Secure Computing state */ | ||||
| cond_syscall(sys_seccomp); | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -890,8 +890,8 @@ static int cipso_v4_map_cat_rbm_hton(const struct cipso_v4_doi *doi_def, | |||
| 	} | ||||
| 
 | ||||
| 	for (;;) { | ||||
| 		host_spot = netlbl_secattr_catmap_walk(secattr->attr.mls.cat, | ||||
| 						       host_spot + 1); | ||||
| 		host_spot = netlbl_catmap_walk(secattr->attr.mls.cat, | ||||
| 					       host_spot + 1); | ||||
| 		if (host_spot < 0) | ||||
| 			break; | ||||
| 
 | ||||
|  | @ -973,7 +973,7 @@ static int cipso_v4_map_cat_rbm_ntoh(const struct cipso_v4_doi *doi_def, | |||
| 				return -EPERM; | ||||
| 			break; | ||||
| 		} | ||||
| 		ret_val = netlbl_secattr_catmap_setbit(secattr->attr.mls.cat, | ||||
| 		ret_val = netlbl_catmap_setbit(&secattr->attr.mls.cat, | ||||
| 						       host_spot, | ||||
| 						       GFP_ATOMIC); | ||||
| 		if (ret_val != 0) | ||||
|  | @ -1039,8 +1039,7 @@ static int cipso_v4_map_cat_enum_hton(const struct cipso_v4_doi *doi_def, | |||
| 	u32 cat_iter = 0; | ||||
| 
 | ||||
| 	for (;;) { | ||||
| 		cat = netlbl_secattr_catmap_walk(secattr->attr.mls.cat, | ||||
| 						 cat + 1); | ||||
| 		cat = netlbl_catmap_walk(secattr->attr.mls.cat, cat + 1); | ||||
| 		if (cat < 0) | ||||
| 			break; | ||||
| 		if ((cat_iter + 2) > net_cat_len) | ||||
|  | @ -1075,9 +1074,9 @@ static int cipso_v4_map_cat_enum_ntoh(const struct cipso_v4_doi *doi_def, | |||
| 	u32 iter; | ||||
| 
 | ||||
| 	for (iter = 0; iter < net_cat_len; iter += 2) { | ||||
| 		ret_val = netlbl_secattr_catmap_setbit(secattr->attr.mls.cat, | ||||
| 				get_unaligned_be16(&net_cat[iter]), | ||||
| 				GFP_ATOMIC); | ||||
| 		ret_val = netlbl_catmap_setbit(&secattr->attr.mls.cat, | ||||
| 					     get_unaligned_be16(&net_cat[iter]), | ||||
| 					     GFP_ATOMIC); | ||||
| 		if (ret_val != 0) | ||||
| 			return ret_val; | ||||
| 	} | ||||
|  | @ -1155,8 +1154,7 @@ static int cipso_v4_map_cat_rng_hton(const struct cipso_v4_doi *doi_def, | |||
| 		return -ENOSPC; | ||||
| 
 | ||||
| 	for (;;) { | ||||
| 		iter = netlbl_secattr_catmap_walk(secattr->attr.mls.cat, | ||||
| 						  iter + 1); | ||||
| 		iter = netlbl_catmap_walk(secattr->attr.mls.cat, iter + 1); | ||||
| 		if (iter < 0) | ||||
| 			break; | ||||
| 		cat_size += (iter == 0 ? 0 : sizeof(u16)); | ||||
|  | @ -1164,8 +1162,7 @@ static int cipso_v4_map_cat_rng_hton(const struct cipso_v4_doi *doi_def, | |||
| 			return -ENOSPC; | ||||
| 		array[array_cnt++] = iter; | ||||
| 
 | ||||
| 		iter = netlbl_secattr_catmap_walk_rng(secattr->attr.mls.cat, | ||||
| 						      iter); | ||||
| 		iter = netlbl_catmap_walkrng(secattr->attr.mls.cat, iter); | ||||
| 		if (iter < 0) | ||||
| 			return -EFAULT; | ||||
| 		cat_size += sizeof(u16); | ||||
|  | @ -1217,10 +1214,10 @@ static int cipso_v4_map_cat_rng_ntoh(const struct cipso_v4_doi *doi_def, | |||
| 		else | ||||
| 			cat_low = 0; | ||||
| 
 | ||||
| 		ret_val = netlbl_secattr_catmap_setrng(secattr->attr.mls.cat, | ||||
| 						       cat_low, | ||||
| 						       cat_high, | ||||
| 						       GFP_ATOMIC); | ||||
| 		ret_val = netlbl_catmap_setrng(&secattr->attr.mls.cat, | ||||
| 					       cat_low, | ||||
| 					       cat_high, | ||||
| 					       GFP_ATOMIC); | ||||
| 		if (ret_val != 0) | ||||
| 			return ret_val; | ||||
| 	} | ||||
|  | @ -1335,16 +1332,12 @@ static int cipso_v4_parsetag_rbm(const struct cipso_v4_doi *doi_def, | |||
| 	secattr->flags |= NETLBL_SECATTR_MLS_LVL; | ||||
| 
 | ||||
| 	if (tag_len > 4) { | ||||
| 		secattr->attr.mls.cat = netlbl_secattr_catmap_alloc(GFP_ATOMIC); | ||||
| 		if (secattr->attr.mls.cat == NULL) | ||||
| 			return -ENOMEM; | ||||
| 
 | ||||
| 		ret_val = cipso_v4_map_cat_rbm_ntoh(doi_def, | ||||
| 						    &tag[4], | ||||
| 						    tag_len - 4, | ||||
| 						    secattr); | ||||
| 		if (ret_val != 0) { | ||||
| 			netlbl_secattr_catmap_free(secattr->attr.mls.cat); | ||||
| 			netlbl_catmap_free(secattr->attr.mls.cat); | ||||
| 			return ret_val; | ||||
| 		} | ||||
| 
 | ||||
|  | @ -1430,16 +1423,12 @@ static int cipso_v4_parsetag_enum(const struct cipso_v4_doi *doi_def, | |||
| 	secattr->flags |= NETLBL_SECATTR_MLS_LVL; | ||||
| 
 | ||||
| 	if (tag_len > 4) { | ||||
| 		secattr->attr.mls.cat = netlbl_secattr_catmap_alloc(GFP_ATOMIC); | ||||
| 		if (secattr->attr.mls.cat == NULL) | ||||
| 			return -ENOMEM; | ||||
| 
 | ||||
| 		ret_val = cipso_v4_map_cat_enum_ntoh(doi_def, | ||||
| 						     &tag[4], | ||||
| 						     tag_len - 4, | ||||
| 						     secattr); | ||||
| 		if (ret_val != 0) { | ||||
| 			netlbl_secattr_catmap_free(secattr->attr.mls.cat); | ||||
| 			netlbl_catmap_free(secattr->attr.mls.cat); | ||||
| 			return ret_val; | ||||
| 		} | ||||
| 
 | ||||
|  | @ -1524,16 +1513,12 @@ static int cipso_v4_parsetag_rng(const struct cipso_v4_doi *doi_def, | |||
| 	secattr->flags |= NETLBL_SECATTR_MLS_LVL; | ||||
| 
 | ||||
| 	if (tag_len > 4) { | ||||
| 		secattr->attr.mls.cat = netlbl_secattr_catmap_alloc(GFP_ATOMIC); | ||||
| 		if (secattr->attr.mls.cat == NULL) | ||||
| 			return -ENOMEM; | ||||
| 
 | ||||
| 		ret_val = cipso_v4_map_cat_rng_ntoh(doi_def, | ||||
| 						    &tag[4], | ||||
| 						    tag_len - 4, | ||||
| 						    secattr); | ||||
| 		if (ret_val != 0) { | ||||
| 			netlbl_secattr_catmap_free(secattr->attr.mls.cat); | ||||
| 			netlbl_catmap_free(secattr->attr.mls.cat); | ||||
| 			return ret_val; | ||||
| 		} | ||||
| 
 | ||||
|  |  | |||
|  | @ -405,8 +405,72 @@ out_entry: | |||
|  * Security Attribute Functions | ||||
|  */ | ||||
| 
 | ||||
| #define _CM_F_NONE	0x00000000 | ||||
| #define _CM_F_ALLOC	0x00000001 | ||||
| #define _CM_F_WALK	0x00000002 | ||||
| 
 | ||||
| /**
 | ||||
|  * netlbl_secattr_catmap_walk - Walk a LSM secattr catmap looking for a bit | ||||
|  * _netlbl_catmap_getnode - Get a individual node from a catmap | ||||
|  * @catmap: pointer to the category bitmap | ||||
|  * @offset: the requested offset | ||||
|  * @cm_flags: catmap flags, see _CM_F_* | ||||
|  * @gfp_flags: memory allocation flags | ||||
|  * | ||||
|  * Description: | ||||
|  * Iterate through the catmap looking for the node associated with @offset. | ||||
|  * If the _CM_F_ALLOC flag is set in @cm_flags and there is no associated node, | ||||
|  * one will be created and inserted into the catmap.  If the _CM_F_WALK flag is | ||||
|  * set in @cm_flags and there is no associated node, the next highest node will | ||||
|  * be returned.  Returns a pointer to the node on success, NULL on failure. | ||||
|  * | ||||
|  */ | ||||
| static struct netlbl_lsm_catmap *_netlbl_catmap_getnode( | ||||
| 					     struct netlbl_lsm_catmap **catmap, | ||||
| 					     u32 offset, | ||||
| 					     unsigned int cm_flags, | ||||
| 					     gfp_t gfp_flags) | ||||
| { | ||||
| 	struct netlbl_lsm_catmap *iter = *catmap; | ||||
| 	struct netlbl_lsm_catmap *prev = NULL; | ||||
| 
 | ||||
| 	if (iter == NULL) | ||||
| 		goto catmap_getnode_alloc; | ||||
| 	if (offset < iter->startbit) | ||||
| 		goto catmap_getnode_walk; | ||||
| 	while (iter && offset >= (iter->startbit + NETLBL_CATMAP_SIZE)) { | ||||
| 		prev = iter; | ||||
| 		iter = iter->next; | ||||
| 	} | ||||
| 	if (iter == NULL || offset < iter->startbit) | ||||
| 		goto catmap_getnode_walk; | ||||
| 
 | ||||
| 	return iter; | ||||
| 
 | ||||
| catmap_getnode_walk: | ||||
| 	if (cm_flags & _CM_F_WALK) | ||||
| 		return iter; | ||||
| catmap_getnode_alloc: | ||||
| 	if (!(cm_flags & _CM_F_ALLOC)) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	iter = netlbl_catmap_alloc(gfp_flags); | ||||
| 	if (iter == NULL) | ||||
| 		return NULL; | ||||
| 	iter->startbit = offset & ~(NETLBL_CATMAP_SIZE - 1); | ||||
| 
 | ||||
| 	if (prev == NULL) { | ||||
| 		iter->next = *catmap; | ||||
| 		*catmap = iter; | ||||
| 	} else { | ||||
| 		iter->next = prev->next; | ||||
| 		prev->next = iter; | ||||
| 	} | ||||
| 
 | ||||
| 	return iter; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * netlbl_catmap_walk - Walk a LSM secattr catmap looking for a bit | ||||
|  * @catmap: the category bitmap | ||||
|  * @offset: the offset to start searching at, in bits | ||||
|  * | ||||
|  | @ -415,54 +479,51 @@ out_entry: | |||
|  * returns the spot of the first set bit or -ENOENT if no bits are set. | ||||
|  * | ||||
|  */ | ||||
| int netlbl_secattr_catmap_walk(struct netlbl_lsm_secattr_catmap *catmap, | ||||
| 			       u32 offset) | ||||
| int netlbl_catmap_walk(struct netlbl_lsm_catmap *catmap, u32 offset) | ||||
| { | ||||
| 	struct netlbl_lsm_secattr_catmap *iter = catmap; | ||||
| 	u32 node_idx; | ||||
| 	u32 node_bit; | ||||
| 	struct netlbl_lsm_catmap *iter = catmap; | ||||
| 	u32 idx; | ||||
| 	u32 bit; | ||||
| 	NETLBL_CATMAP_MAPTYPE bitmap; | ||||
| 
 | ||||
| 	iter = _netlbl_catmap_getnode(&catmap, offset, _CM_F_WALK, 0); | ||||
| 	if (iter == NULL) | ||||
| 		return -ENOENT; | ||||
| 	if (offset > iter->startbit) { | ||||
| 		while (offset >= (iter->startbit + NETLBL_CATMAP_SIZE)) { | ||||
| 			iter = iter->next; | ||||
| 			if (iter == NULL) | ||||
| 				return -ENOENT; | ||||
| 		} | ||||
| 		node_idx = (offset - iter->startbit) / NETLBL_CATMAP_MAPSIZE; | ||||
| 		node_bit = offset - iter->startbit - | ||||
| 			   (NETLBL_CATMAP_MAPSIZE * node_idx); | ||||
| 		offset -= iter->startbit; | ||||
| 		idx = offset / NETLBL_CATMAP_MAPSIZE; | ||||
| 		bit = offset % NETLBL_CATMAP_MAPSIZE; | ||||
| 	} else { | ||||
| 		node_idx = 0; | ||||
| 		node_bit = 0; | ||||
| 		idx = 0; | ||||
| 		bit = 0; | ||||
| 	} | ||||
| 	bitmap = iter->bitmap[node_idx] >> node_bit; | ||||
| 	bitmap = iter->bitmap[idx] >> bit; | ||||
| 
 | ||||
| 	for (;;) { | ||||
| 		if (bitmap != 0) { | ||||
| 			while ((bitmap & NETLBL_CATMAP_BIT) == 0) { | ||||
| 				bitmap >>= 1; | ||||
| 				node_bit++; | ||||
| 				bit++; | ||||
| 			} | ||||
| 			return iter->startbit + | ||||
| 				(NETLBL_CATMAP_MAPSIZE * node_idx) + node_bit; | ||||
| 			       (NETLBL_CATMAP_MAPSIZE * idx) + bit; | ||||
| 		} | ||||
| 		if (++node_idx >= NETLBL_CATMAP_MAPCNT) { | ||||
| 		if (++idx >= NETLBL_CATMAP_MAPCNT) { | ||||
| 			if (iter->next != NULL) { | ||||
| 				iter = iter->next; | ||||
| 				node_idx = 0; | ||||
| 				idx = 0; | ||||
| 			} else | ||||
| 				return -ENOENT; | ||||
| 		} | ||||
| 		bitmap = iter->bitmap[node_idx]; | ||||
| 		node_bit = 0; | ||||
| 		bitmap = iter->bitmap[idx]; | ||||
| 		bit = 0; | ||||
| 	} | ||||
| 
 | ||||
| 	return -ENOENT; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * netlbl_secattr_catmap_walk_rng - Find the end of a string of set bits | ||||
|  * netlbl_catmap_walkrng - Find the end of a string of set bits | ||||
|  * @catmap: the category bitmap | ||||
|  * @offset: the offset to start searching at, in bits | ||||
|  * | ||||
|  | @ -472,57 +533,105 @@ int netlbl_secattr_catmap_walk(struct netlbl_lsm_secattr_catmap *catmap, | |||
|  * the end of the bitmap. | ||||
|  * | ||||
|  */ | ||||
| int netlbl_secattr_catmap_walk_rng(struct netlbl_lsm_secattr_catmap *catmap, | ||||
| 				   u32 offset) | ||||
| int netlbl_catmap_walkrng(struct netlbl_lsm_catmap *catmap, u32 offset) | ||||
| { | ||||
| 	struct netlbl_lsm_secattr_catmap *iter = catmap; | ||||
| 	u32 node_idx; | ||||
| 	u32 node_bit; | ||||
| 	struct netlbl_lsm_catmap *iter; | ||||
| 	struct netlbl_lsm_catmap *prev = NULL; | ||||
| 	u32 idx; | ||||
| 	u32 bit; | ||||
| 	NETLBL_CATMAP_MAPTYPE bitmask; | ||||
| 	NETLBL_CATMAP_MAPTYPE bitmap; | ||||
| 
 | ||||
| 	iter = _netlbl_catmap_getnode(&catmap, offset, _CM_F_WALK, 0); | ||||
| 	if (iter == NULL) | ||||
| 		return -ENOENT; | ||||
| 	if (offset > iter->startbit) { | ||||
| 		while (offset >= (iter->startbit + NETLBL_CATMAP_SIZE)) { | ||||
| 			iter = iter->next; | ||||
| 			if (iter == NULL) | ||||
| 				return -ENOENT; | ||||
| 		} | ||||
| 		node_idx = (offset - iter->startbit) / NETLBL_CATMAP_MAPSIZE; | ||||
| 		node_bit = offset - iter->startbit - | ||||
| 			   (NETLBL_CATMAP_MAPSIZE * node_idx); | ||||
| 		offset -= iter->startbit; | ||||
| 		idx = offset / NETLBL_CATMAP_MAPSIZE; | ||||
| 		bit = offset % NETLBL_CATMAP_MAPSIZE; | ||||
| 	} else { | ||||
| 		node_idx = 0; | ||||
| 		node_bit = 0; | ||||
| 		idx = 0; | ||||
| 		bit = 0; | ||||
| 	} | ||||
| 	bitmask = NETLBL_CATMAP_BIT << node_bit; | ||||
| 	bitmask = NETLBL_CATMAP_BIT << bit; | ||||
| 
 | ||||
| 	for (;;) { | ||||
| 		bitmap = iter->bitmap[node_idx]; | ||||
| 		bitmap = iter->bitmap[idx]; | ||||
| 		while (bitmask != 0 && (bitmap & bitmask) != 0) { | ||||
| 			bitmask <<= 1; | ||||
| 			node_bit++; | ||||
| 			bit++; | ||||
| 		} | ||||
| 
 | ||||
| 		if (bitmask != 0) | ||||
| 		if (prev && idx == 0 && bit == 0) | ||||
| 			return prev->startbit + NETLBL_CATMAP_SIZE - 1; | ||||
| 		else if (bitmask != 0) | ||||
| 			return iter->startbit + | ||||
| 				(NETLBL_CATMAP_MAPSIZE * node_idx) + | ||||
| 				node_bit - 1; | ||||
| 		else if (++node_idx >= NETLBL_CATMAP_MAPCNT) { | ||||
| 				(NETLBL_CATMAP_MAPSIZE * idx) + bit - 1; | ||||
| 		else if (++idx >= NETLBL_CATMAP_MAPCNT) { | ||||
| 			if (iter->next == NULL) | ||||
| 				return iter->startbit +	NETLBL_CATMAP_SIZE - 1; | ||||
| 				return iter->startbit + NETLBL_CATMAP_SIZE - 1; | ||||
| 			prev = iter; | ||||
| 			iter = iter->next; | ||||
| 			node_idx = 0; | ||||
| 			idx = 0; | ||||
| 		} | ||||
| 		bitmask = NETLBL_CATMAP_BIT; | ||||
| 		node_bit = 0; | ||||
| 		bit = 0; | ||||
| 	} | ||||
| 
 | ||||
| 	return -ENOENT; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * netlbl_secattr_catmap_setbit - Set a bit in a LSM secattr catmap | ||||
|  * @catmap: the category bitmap | ||||
|  * netlbl_catmap_getlong - Export an unsigned long bitmap | ||||
|  * @catmap: pointer to the category bitmap | ||||
|  * @offset: pointer to the requested offset | ||||
|  * @bitmap: the exported bitmap | ||||
|  * | ||||
|  * Description: | ||||
|  * Export a bitmap with an offset greater than or equal to @offset and return | ||||
|  * it in @bitmap.  The @offset must be aligned to an unsigned long and will be | ||||
|  * updated on return if different from what was requested; if the catmap is | ||||
|  * empty at the requested offset and beyond, the @offset is set to (u32)-1. | ||||
|  * Returns zero on sucess, negative values on failure. | ||||
|  * | ||||
|  */ | ||||
| int netlbl_catmap_getlong(struct netlbl_lsm_catmap *catmap, | ||||
| 			  u32 *offset, | ||||
| 			  unsigned long *bitmap) | ||||
| { | ||||
| 	struct netlbl_lsm_catmap *iter; | ||||
| 	u32 off = *offset; | ||||
| 	u32 idx; | ||||
| 
 | ||||
| 	/* only allow aligned offsets */ | ||||
| 	if ((off & (BITS_PER_LONG - 1)) != 0) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	if (off < catmap->startbit) { | ||||
| 		off = catmap->startbit; | ||||
| 		*offset = off; | ||||
| 	} | ||||
| 	iter = _netlbl_catmap_getnode(&catmap, off, _CM_F_NONE, 0); | ||||
| 	if (iter == NULL) { | ||||
| 		*offset = (u32)-1; | ||||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	if (off < iter->startbit) { | ||||
| 		off = iter->startbit; | ||||
| 		*offset = off; | ||||
| 	} else | ||||
| 		off -= iter->startbit; | ||||
| 
 | ||||
| 	idx = off / NETLBL_CATMAP_MAPSIZE; | ||||
| 	*bitmap = iter->bitmap[idx] >> (off % NETLBL_CATMAP_SIZE); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * netlbl_catmap_setbit - Set a bit in a LSM secattr catmap | ||||
|  * @catmap: pointer to the category bitmap | ||||
|  * @bit: the bit to set | ||||
|  * @flags: memory allocation flags | ||||
|  * | ||||
|  | @ -531,36 +640,27 @@ int netlbl_secattr_catmap_walk_rng(struct netlbl_lsm_secattr_catmap *catmap, | |||
|  * negative values on failure. | ||||
|  * | ||||
|  */ | ||||
| int netlbl_secattr_catmap_setbit(struct netlbl_lsm_secattr_catmap *catmap, | ||||
| 				 u32 bit, | ||||
| 				 gfp_t flags) | ||||
| int netlbl_catmap_setbit(struct netlbl_lsm_catmap **catmap, | ||||
| 			 u32 bit, | ||||
| 			 gfp_t flags) | ||||
| { | ||||
| 	struct netlbl_lsm_secattr_catmap *iter = catmap; | ||||
| 	u32 node_bit; | ||||
| 	u32 node_idx; | ||||
| 	struct netlbl_lsm_catmap *iter; | ||||
| 	u32 idx; | ||||
| 
 | ||||
| 	while (iter->next != NULL && | ||||
| 	       bit >= (iter->startbit + NETLBL_CATMAP_SIZE)) | ||||
| 		iter = iter->next; | ||||
| 	if (bit >= (iter->startbit + NETLBL_CATMAP_SIZE)) { | ||||
| 		iter->next = netlbl_secattr_catmap_alloc(flags); | ||||
| 		if (iter->next == NULL) | ||||
| 			return -ENOMEM; | ||||
| 		iter = iter->next; | ||||
| 		iter->startbit = bit & ~(NETLBL_CATMAP_SIZE - 1); | ||||
| 	} | ||||
| 	iter = _netlbl_catmap_getnode(catmap, bit, _CM_F_ALLOC, flags); | ||||
| 	if (iter == NULL) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	/* gcc always rounds to zero when doing integer division */ | ||||
| 	node_idx = (bit - iter->startbit) / NETLBL_CATMAP_MAPSIZE; | ||||
| 	node_bit = bit - iter->startbit - (NETLBL_CATMAP_MAPSIZE * node_idx); | ||||
| 	iter->bitmap[node_idx] |= NETLBL_CATMAP_BIT << node_bit; | ||||
| 	bit -= iter->startbit; | ||||
| 	idx = bit / NETLBL_CATMAP_MAPSIZE; | ||||
| 	iter->bitmap[idx] |= NETLBL_CATMAP_BIT << (bit % NETLBL_CATMAP_MAPSIZE); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * netlbl_secattr_catmap_setrng - Set a range of bits in a LSM secattr catmap | ||||
|  * @catmap: the category bitmap | ||||
|  * netlbl_catmap_setrng - Set a range of bits in a LSM secattr catmap | ||||
|  * @catmap: pointer to the category bitmap | ||||
|  * @start: the starting bit | ||||
|  * @end: the last bit in the string | ||||
|  * @flags: memory allocation flags | ||||
|  | @ -570,36 +670,63 @@ int netlbl_secattr_catmap_setbit(struct netlbl_lsm_secattr_catmap *catmap, | |||
|  * on success, negative values on failure. | ||||
|  * | ||||
|  */ | ||||
| int netlbl_secattr_catmap_setrng(struct netlbl_lsm_secattr_catmap *catmap, | ||||
| 				 u32 start, | ||||
| 				 u32 end, | ||||
| 				 gfp_t flags) | ||||
| int netlbl_catmap_setrng(struct netlbl_lsm_catmap **catmap, | ||||
| 			 u32 start, | ||||
| 			 u32 end, | ||||
| 			 gfp_t flags) | ||||
| { | ||||
| 	int ret_val = 0; | ||||
| 	struct netlbl_lsm_secattr_catmap *iter = catmap; | ||||
| 	u32 iter_max_spot; | ||||
| 	u32 spot; | ||||
| 	int rc = 0; | ||||
| 	u32 spot = start; | ||||
| 
 | ||||
| 	/* XXX - This could probably be made a bit faster by combining writes
 | ||||
| 	 * to the catmap instead of setting a single bit each time, but for | ||||
| 	 * right now skipping to the start of the range in the catmap should | ||||
| 	 * be a nice improvement over calling the individual setbit function | ||||
| 	 * repeatedly from a loop. */ | ||||
| 
 | ||||
| 	while (iter->next != NULL && | ||||
| 	       start >= (iter->startbit + NETLBL_CATMAP_SIZE)) | ||||
| 		iter = iter->next; | ||||
| 	iter_max_spot = iter->startbit + NETLBL_CATMAP_SIZE; | ||||
| 
 | ||||
| 	for (spot = start; spot <= end && ret_val == 0; spot++) { | ||||
| 		if (spot >= iter_max_spot && iter->next != NULL) { | ||||
| 			iter = iter->next; | ||||
| 			iter_max_spot = iter->startbit + NETLBL_CATMAP_SIZE; | ||||
| 		} | ||||
| 		ret_val = netlbl_secattr_catmap_setbit(iter, spot, flags); | ||||
| 	while (rc == 0 && spot <= end) { | ||||
| 		if (((spot & (BITS_PER_LONG - 1)) != 0) && | ||||
| 		    ((end - spot) > BITS_PER_LONG)) { | ||||
| 			rc = netlbl_catmap_setlong(catmap, | ||||
| 						   spot, | ||||
| 						   (unsigned long)-1, | ||||
| 						   flags); | ||||
| 			spot += BITS_PER_LONG; | ||||
| 		} else | ||||
| 			rc = netlbl_catmap_setbit(catmap, spot++, flags); | ||||
| 	} | ||||
| 
 | ||||
| 	return ret_val; | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * netlbl_catmap_setlong - Import an unsigned long bitmap | ||||
|  * @catmap: pointer to the category bitmap | ||||
|  * @offset: offset to the start of the imported bitmap | ||||
|  * @bitmap: the bitmap to import | ||||
|  * @flags: memory allocation flags | ||||
|  * | ||||
|  * Description: | ||||
|  * Import the bitmap specified in @bitmap into @catmap, using the offset | ||||
|  * in @offset.  The offset must be aligned to an unsigned long.  Returns zero | ||||
|  * on success, negative values on failure. | ||||
|  * | ||||
|  */ | ||||
| int netlbl_catmap_setlong(struct netlbl_lsm_catmap **catmap, | ||||
| 			  u32 offset, | ||||
| 			  unsigned long bitmap, | ||||
| 			  gfp_t flags) | ||||
| { | ||||
| 	struct netlbl_lsm_catmap *iter; | ||||
| 	u32 idx; | ||||
| 
 | ||||
| 	/* only allow aligned offsets */ | ||||
| 	if ((offset & (BITS_PER_LONG - 1)) != 0) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	iter = _netlbl_catmap_getnode(catmap, offset, _CM_F_ALLOC, flags); | ||||
| 	if (iter == NULL) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	offset -= iter->startbit; | ||||
| 	idx = offset / NETLBL_CATMAP_MAPSIZE; | ||||
| 	iter->bitmap[idx] |= bitmap << (offset % NETLBL_CATMAP_MAPSIZE); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  |  | |||
|  | @ -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 | ||||
|  */ | ||||
|  |  | |||
|  | @ -2,4 +2,3 @@ hostprogs-y	:= genheaders | |||
| HOST_EXTRACFLAGS += -Isecurity/selinux/include | ||||
| 
 | ||||
| always		:= $(hostprogs-y) | ||||
| clean-files	:= $(hostprogs-y) | ||||
|  |  | |||
|  | @ -2,4 +2,4 @@ hostprogs-y	:= mdp | |||
| HOST_EXTRACFLAGS += -Isecurity/selinux/include | ||||
| 
 | ||||
| always		:= $(hostprogs-y) | ||||
| clean-files	:= $(hostprogs-y) policy.* file_contexts | ||||
| clean-files	:= policy.* file_contexts | ||||
|  |  | |||
|  | @ -621,7 +621,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest) | |||
| 	 * There is no exception for unconfined as change_hat is not | ||||
| 	 * available. | ||||
| 	 */ | ||||
| 	if (current->no_new_privs) | ||||
| 	if (task_no_new_privs(current)) | ||||
| 		return -EPERM; | ||||
| 
 | ||||
| 	/* released below */ | ||||
|  | @ -776,7 +776,7 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec, | |||
| 	 * no_new_privs is set because this aways results in a reduction | ||||
| 	 * of permissions. | ||||
| 	 */ | ||||
| 	if (current->no_new_privs && !unconfined(profile)) { | ||||
| 	if (task_no_new_privs(current) && !unconfined(profile)) { | ||||
| 		put_cred(cred); | ||||
| 		return -EPERM; | ||||
| 	} | ||||
|  |  | |||
|  | @ -401,6 +401,11 @@ static int cap_kernel_create_files_as(struct cred *new, struct inode *inode) | |||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int cap_kernel_fw_from_file(struct file *file, char *buf, size_t size) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int cap_kernel_module_request(char *kmod_name) | ||||
| { | ||||
| 	return 0; | ||||
|  | @ -1015,6 +1020,7 @@ void __init security_fixup_ops(struct security_operations *ops) | |||
| 	set_to_cap_if_null(ops, cred_transfer); | ||||
| 	set_to_cap_if_null(ops, kernel_act_as); | ||||
| 	set_to_cap_if_null(ops, kernel_create_files_as); | ||||
| 	set_to_cap_if_null(ops, kernel_fw_from_file); | ||||
| 	set_to_cap_if_null(ops, kernel_module_request); | ||||
| 	set_to_cap_if_null(ops, kernel_module_from_file); | ||||
| 	set_to_cap_if_null(ops, task_fix_setuid); | ||||
|  |  | |||
|  | @ -421,6 +421,9 @@ int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data | |||
| 		cpu_caps->inheritable.cap[i] = le32_to_cpu(caps.data[i].inheritable); | ||||
| 	} | ||||
| 
 | ||||
| 	cpu_caps->permitted.cap[CAP_LAST_U32] &= CAP_LAST_U32_VALID_MASK; | ||||
| 	cpu_caps->inheritable.cap[CAP_LAST_U32] &= CAP_LAST_U32_VALID_MASK; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
|  | @ -822,15 +825,20 @@ int cap_task_setnice(struct task_struct *p, int nice) | |||
|  * Implement PR_CAPBSET_DROP.  Attempt to remove the specified capability from | ||||
|  * the current task's bounding set.  Returns 0 on success, -ve on error. | ||||
|  */ | ||||
| static long cap_prctl_drop(struct cred *new, unsigned long cap) | ||||
| static int cap_prctl_drop(unsigned long cap) | ||||
| { | ||||
| 	struct cred *new; | ||||
| 
 | ||||
| 	if (!ns_capable(current_user_ns(), CAP_SETPCAP)) | ||||
| 		return -EPERM; | ||||
| 	if (!cap_valid(cap)) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	new = prepare_creds(); | ||||
| 	if (!new) | ||||
| 		return -ENOMEM; | ||||
| 	cap_lower(new->cap_bset, cap); | ||||
| 	return 0; | ||||
| 	return commit_creds(new); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  | @ -848,26 +856,17 @@ static long cap_prctl_drop(struct cred *new, unsigned long cap) | |||
| int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3, | ||||
| 		   unsigned long arg4, unsigned long arg5) | ||||
| { | ||||
| 	const struct cred *old = current_cred(); | ||||
| 	struct cred *new; | ||||
| 	long error = 0; | ||||
| 
 | ||||
| 	new = prepare_creds(); | ||||
| 	if (!new) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	switch (option) { | ||||
| 	case PR_CAPBSET_READ: | ||||
| 		error = -EINVAL; | ||||
| 		if (!cap_valid(arg2)) | ||||
| 			goto error; | ||||
| 		error = !!cap_raised(new->cap_bset, arg2); | ||||
| 		goto no_change; | ||||
| 			return -EINVAL; | ||||
| 		return !!cap_raised(old->cap_bset, arg2); | ||||
| 
 | ||||
| 	case PR_CAPBSET_DROP: | ||||
| 		error = cap_prctl_drop(new, arg2); | ||||
| 		if (error < 0) | ||||
| 			goto error; | ||||
| 		goto changed; | ||||
| 		return cap_prctl_drop(arg2); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * The next four prctl's remain to assist with transitioning a | ||||
|  | @ -889,10 +888,9 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3, | |||
| 	 * capability-based-privilege environment. | ||||
| 	 */ | ||||
| 	case PR_SET_SECUREBITS: | ||||
| 		error = -EPERM; | ||||
| 		if ((((new->securebits & SECURE_ALL_LOCKS) >> 1) | ||||
| 		     & (new->securebits ^ arg2))			/*[1]*/ | ||||
| 		    || ((new->securebits & SECURE_ALL_LOCKS & ~arg2))	/*[2]*/ | ||||
| 		if ((((old->securebits & SECURE_ALL_LOCKS) >> 1) | ||||
| 		     & (old->securebits ^ arg2))			/*[1]*/ | ||||
| 		    || ((old->securebits & SECURE_ALL_LOCKS & ~arg2))	/*[2]*/ | ||||
| 		    || (arg2 & ~(SECURE_ALL_LOCKS | SECURE_ALL_BITS))	/*[3]*/ | ||||
| 		    || (cap_capable(current_cred(), | ||||
| 				    current_cred()->user_ns, CAP_SETPCAP, | ||||
|  | @ -906,46 +904,39 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3, | |||
| 			 */ | ||||
| 		    ) | ||||
| 			/* cannot change a locked bit */ | ||||
| 			goto error; | ||||
| 			return -EPERM; | ||||
| 
 | ||||
| 		new = prepare_creds(); | ||||
| 		if (!new) | ||||
| 			return -ENOMEM; | ||||
| 		new->securebits = arg2; | ||||
| 		goto changed; | ||||
| 		return commit_creds(new); | ||||
| 
 | ||||
| 	case PR_GET_SECUREBITS: | ||||
| 		error = new->securebits; | ||||
| 		goto no_change; | ||||
| 		return old->securebits; | ||||
| 
 | ||||
| 	case PR_GET_KEEPCAPS: | ||||
| 		if (issecure(SECURE_KEEP_CAPS)) | ||||
| 			error = 1; | ||||
| 		goto no_change; | ||||
| 		return !!issecure(SECURE_KEEP_CAPS); | ||||
| 
 | ||||
| 	case PR_SET_KEEPCAPS: | ||||
| 		error = -EINVAL; | ||||
| 		if (arg2 > 1) /* Note, we rely on arg2 being unsigned here */ | ||||
| 			goto error; | ||||
| 		error = -EPERM; | ||||
| 			return -EINVAL; | ||||
| 		if (issecure(SECURE_KEEP_CAPS_LOCKED)) | ||||
| 			goto error; | ||||
| 			return -EPERM; | ||||
| 
 | ||||
| 		new = prepare_creds(); | ||||
| 		if (!new) | ||||
| 			return -ENOMEM; | ||||
| 		if (arg2) | ||||
| 			new->securebits |= issecure_mask(SECURE_KEEP_CAPS); | ||||
| 		else | ||||
| 			new->securebits &= ~issecure_mask(SECURE_KEEP_CAPS); | ||||
| 		goto changed; | ||||
| 		return commit_creds(new); | ||||
| 
 | ||||
| 	default: | ||||
| 		/* No functionality available - continue with default */ | ||||
| 		error = -ENOSYS; | ||||
| 		goto error; | ||||
| 		return -ENOSYS; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Functionality provided */ | ||||
| changed: | ||||
| 	return commit_creds(new); | ||||
| 
 | ||||
| no_change: | ||||
| error: | ||||
| 	abort_creds(new); | ||||
| 	return error; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  |  | |||
|  | @ -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. | ||||
|  |  | |||
|  | @ -158,7 +158,7 @@ struct integrity_iint_cache *integrity_iint_insert(struct inode *inode); | |||
| struct integrity_iint_cache *integrity_iint_find(struct inode *inode); | ||||
| 
 | ||||
| /* IMA policy related functions */ | ||||
| enum ima_hooks { FILE_CHECK = 1, MMAP_CHECK, BPRM_CHECK, MODULE_CHECK, POST_SETATTR }; | ||||
| enum ima_hooks { FILE_CHECK = 1, MMAP_CHECK, BPRM_CHECK, MODULE_CHECK, FIRMWARE_CHECK, POST_SETATTR }; | ||||
| 
 | ||||
| int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask, | ||||
| 		     int flags); | ||||
|  | @ -171,6 +171,7 @@ void ima_delete_rules(void); | |||
| #define IMA_APPRAISE_ENFORCE	0x01 | ||||
| #define IMA_APPRAISE_FIX	0x02 | ||||
| #define IMA_APPRAISE_MODULES	0x04 | ||||
| #define IMA_APPRAISE_FIRMWARE	0x08 | ||||
| 
 | ||||
| #ifdef CONFIG_IMA_APPRAISE | ||||
| int ima_appraise_measurement(int func, struct integrity_iint_cache *iint, | ||||
|  | @ -249,4 +250,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 | ||||
|  |  | |||
|  | @ -75,6 +75,8 @@ enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint, | |||
| 		return iint->ima_bprm_status; | ||||
| 	case MODULE_CHECK: | ||||
| 		return iint->ima_module_status; | ||||
| 	case FIRMWARE_CHECK: | ||||
| 		return iint->ima_firmware_status; | ||||
| 	case FILE_CHECK: | ||||
| 	default: | ||||
| 		return iint->ima_file_status; | ||||
|  | @ -94,6 +96,9 @@ static void ima_set_cache_status(struct integrity_iint_cache *iint, | |||
| 	case MODULE_CHECK: | ||||
| 		iint->ima_module_status = status; | ||||
| 		break; | ||||
| 	case FIRMWARE_CHECK: | ||||
| 		iint->ima_firmware_status = status; | ||||
| 		break; | ||||
| 	case FILE_CHECK: | ||||
| 	default: | ||||
| 		iint->ima_file_status = status; | ||||
|  | @ -113,6 +118,9 @@ static void ima_cache_flags(struct integrity_iint_cache *iint, int func) | |||
| 	case MODULE_CHECK: | ||||
| 		iint->flags |= (IMA_MODULE_APPRAISED | IMA_APPRAISED); | ||||
| 		break; | ||||
| 	case FIRMWARE_CHECK: | ||||
| 		iint->flags |= (IMA_FIRMWARE_APPRAISED | IMA_APPRAISED); | ||||
| 		break; | ||||
| 	case FILE_CHECK: | ||||
| 	default: | ||||
| 		iint->flags |= (IMA_FILE_APPRAISED | IMA_APPRAISED); | ||||
|  | @ -214,7 +222,7 @@ int ima_appraise_measurement(int func, struct integrity_iint_cache *iint, | |||
| 		hash_start = 1; | ||||
| 	case IMA_XATTR_DIGEST: | ||||
| 		if (iint->flags & IMA_DIGSIG_REQUIRED) { | ||||
| 			cause = "IMA signature required"; | ||||
| 			cause = "IMA-signature-required"; | ||||
| 			status = INTEGRITY_FAIL; | ||||
| 			break; | ||||
| 		} | ||||
|  |  | |||
|  | @ -16,6 +16,8 @@ | |||
| #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||||
| 
 | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/moduleparam.h> | ||||
| #include <linux/ratelimit.h> | ||||
| #include <linux/file.h> | ||||
| #include <linux/crypto.h> | ||||
| #include <linux/scatterlist.h> | ||||
|  | @ -25,7 +27,45 @@ | |||
| #include <crypto/hash_info.h> | ||||
| #include "ima.h" | ||||
| 
 | ||||
| struct ahash_completion { | ||||
| 	struct completion completion; | ||||
| 	int err; | ||||
| }; | ||||
| 
 | ||||
| /* minimum file size for ahash use */ | ||||
| static unsigned long ima_ahash_minsize; | ||||
| module_param_named(ahash_minsize, ima_ahash_minsize, ulong, 0644); | ||||
| MODULE_PARM_DESC(ahash_minsize, "Minimum file size for ahash use"); | ||||
| 
 | ||||
| /* default is 0 - 1 page. */ | ||||
| static int ima_maxorder; | ||||
| static unsigned int ima_bufsize = PAGE_SIZE; | ||||
| 
 | ||||
| static int param_set_bufsize(const char *val, const struct kernel_param *kp) | ||||
| { | ||||
| 	unsigned long long size; | ||||
| 	int order; | ||||
| 
 | ||||
| 	size = memparse(val, NULL); | ||||
| 	order = get_order(size); | ||||
| 	if (order >= MAX_ORDER) | ||||
| 		return -EINVAL; | ||||
| 	ima_maxorder = order; | ||||
| 	ima_bufsize = PAGE_SIZE << order; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static struct kernel_param_ops param_ops_bufsize = { | ||||
| 	.set = param_set_bufsize, | ||||
| 	.get = param_get_uint, | ||||
| }; | ||||
| #define param_check_bufsize(name, p) __param_check(name, p, unsigned int) | ||||
| 
 | ||||
| module_param_named(ahash_bufsize, ima_bufsize, bufsize, 0644); | ||||
| MODULE_PARM_DESC(ahash_bufsize, "Maximum ahash buffer size"); | ||||
| 
 | ||||
| static struct crypto_shash *ima_shash_tfm; | ||||
| static struct crypto_ahash *ima_ahash_tfm; | ||||
| 
 | ||||
| /**
 | ||||
|  * ima_kernel_read - read file content | ||||
|  | @ -93,9 +133,246 @@ static void ima_free_tfm(struct crypto_shash *tfm) | |||
| 		crypto_free_shash(tfm); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Calculate the MD5/SHA1 file digest | ||||
| /**
 | ||||
|  * ima_alloc_pages() - Allocate contiguous pages. | ||||
|  * @max_size:       Maximum amount of memory to allocate. | ||||
|  * @allocated_size: Returned size of actual allocation. | ||||
|  * @last_warn:      Should the min_size allocation warn or not. | ||||
|  * | ||||
|  * Tries to do opportunistic allocation for memory first trying to allocate | ||||
|  * max_size amount of memory and then splitting that until zero order is | ||||
|  * reached. Allocation is tried without generating allocation warnings unless | ||||
|  * last_warn is set. Last_warn set affects only last allocation of zero order. | ||||
|  * | ||||
|  * By default, ima_maxorder is 0 and it is equivalent to kmalloc(GFP_KERNEL) | ||||
|  * | ||||
|  * Return pointer to allocated memory, or NULL on failure. | ||||
|  */ | ||||
| static void *ima_alloc_pages(loff_t max_size, size_t *allocated_size, | ||||
| 			     int last_warn) | ||||
| { | ||||
| 	void *ptr; | ||||
| 	int order = ima_maxorder; | ||||
| 	gfp_t gfp_mask = __GFP_WAIT | __GFP_NOWARN | __GFP_NORETRY; | ||||
| 
 | ||||
| 	if (order) | ||||
| 		order = min(get_order(max_size), order); | ||||
| 
 | ||||
| 	for (; order; order--) { | ||||
| 		ptr = (void *)__get_free_pages(gfp_mask, order); | ||||
| 		if (ptr) { | ||||
| 			*allocated_size = PAGE_SIZE << order; | ||||
| 			return ptr; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/* order is zero - one page */ | ||||
| 
 | ||||
| 	gfp_mask = GFP_KERNEL; | ||||
| 
 | ||||
| 	if (!last_warn) | ||||
| 		gfp_mask |= __GFP_NOWARN; | ||||
| 
 | ||||
| 	ptr = (void *)__get_free_pages(gfp_mask, 0); | ||||
| 	if (ptr) { | ||||
| 		*allocated_size = PAGE_SIZE; | ||||
| 		return ptr; | ||||
| 	} | ||||
| 
 | ||||
| 	*allocated_size = 0; | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * ima_free_pages() - Free pages allocated by ima_alloc_pages(). | ||||
|  * @ptr:  Pointer to allocated pages. | ||||
|  * @size: Size of allocated buffer. | ||||
|  */ | ||||
| static void ima_free_pages(void *ptr, size_t size) | ||||
| { | ||||
| 	if (!ptr) | ||||
| 		return; | ||||
| 	free_pages((unsigned long)ptr, get_order(size)); | ||||
| } | ||||
| 
 | ||||
| static struct crypto_ahash *ima_alloc_atfm(enum hash_algo algo) | ||||
| { | ||||
| 	struct crypto_ahash *tfm = ima_ahash_tfm; | ||||
| 	int rc; | ||||
| 
 | ||||
| 	if ((algo != ima_hash_algo && algo < HASH_ALGO__LAST) || !tfm) { | ||||
| 		tfm = crypto_alloc_ahash(hash_algo_name[algo], 0, 0); | ||||
| 		if (!IS_ERR(tfm)) { | ||||
| 			if (algo == ima_hash_algo) | ||||
| 				ima_ahash_tfm = tfm; | ||||
| 		} else { | ||||
| 			rc = PTR_ERR(tfm); | ||||
| 			pr_err("Can not allocate %s (reason: %d)\n", | ||||
| 			       hash_algo_name[algo], rc); | ||||
| 		} | ||||
| 	} | ||||
| 	return tfm; | ||||
| } | ||||
| 
 | ||||
| static void ima_free_atfm(struct crypto_ahash *tfm) | ||||
| { | ||||
| 	if (tfm != ima_ahash_tfm) | ||||
| 		crypto_free_ahash(tfm); | ||||
| } | ||||
| 
 | ||||
| static void ahash_complete(struct crypto_async_request *req, int err) | ||||
| { | ||||
| 	struct ahash_completion *res = req->data; | ||||
| 
 | ||||
| 	if (err == -EINPROGRESS) | ||||
| 		return; | ||||
| 	res->err = err; | ||||
| 	complete(&res->completion); | ||||
| } | ||||
| 
 | ||||
| static int ahash_wait(int err, struct ahash_completion *res) | ||||
| { | ||||
| 	switch (err) { | ||||
| 	case 0: | ||||
| 		break; | ||||
| 	case -EINPROGRESS: | ||||
| 	case -EBUSY: | ||||
| 		wait_for_completion(&res->completion); | ||||
| 		reinit_completion(&res->completion); | ||||
| 		err = res->err; | ||||
| 		/* fall through */ | ||||
| 	default: | ||||
| 		pr_crit_ratelimited("ahash calculation failed: err: %d\n", err); | ||||
| 	} | ||||
| 
 | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| static int ima_calc_file_hash_atfm(struct file *file, | ||||
| 				   struct ima_digest_data *hash, | ||||
| 				   struct crypto_ahash *tfm) | ||||
| { | ||||
| 	loff_t i_size, offset; | ||||
| 	char *rbuf[2] = { NULL, }; | ||||
| 	int rc, read = 0, rbuf_len, active = 0, ahash_rc = 0; | ||||
| 	struct ahash_request *req; | ||||
| 	struct scatterlist sg[1]; | ||||
| 	struct ahash_completion res; | ||||
| 	size_t rbuf_size[2]; | ||||
| 
 | ||||
| 	hash->length = crypto_ahash_digestsize(tfm); | ||||
| 
 | ||||
| 	req = ahash_request_alloc(tfm, GFP_KERNEL); | ||||
| 	if (!req) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	init_completion(&res.completion); | ||||
| 	ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG | | ||||
| 				   CRYPTO_TFM_REQ_MAY_SLEEP, | ||||
| 				   ahash_complete, &res); | ||||
| 
 | ||||
| 	rc = ahash_wait(crypto_ahash_init(req), &res); | ||||
| 	if (rc) | ||||
| 		goto out1; | ||||
| 
 | ||||
| 	i_size = i_size_read(file_inode(file)); | ||||
| 
 | ||||
| 	if (i_size == 0) | ||||
| 		goto out2; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Try to allocate maximum size of memory. | ||||
| 	 * Fail if even a single page cannot be allocated. | ||||
| 	 */ | ||||
| 	rbuf[0] = ima_alloc_pages(i_size, &rbuf_size[0], 1); | ||||
| 	if (!rbuf[0]) { | ||||
| 		rc = -ENOMEM; | ||||
| 		goto out1; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Only allocate one buffer if that is enough. */ | ||||
| 	if (i_size > rbuf_size[0]) { | ||||
| 		/*
 | ||||
| 		 * Try to allocate secondary buffer. If that fails fallback to | ||||
| 		 * using single buffering. Use previous memory allocation size | ||||
| 		 * as baseline for possible allocation size. | ||||
| 		 */ | ||||
| 		rbuf[1] = ima_alloc_pages(i_size - rbuf_size[0], | ||||
| 					  &rbuf_size[1], 0); | ||||
| 	} | ||||
| 
 | ||||
| 	if (!(file->f_mode & FMODE_READ)) { | ||||
| 		file->f_mode |= FMODE_READ; | ||||
| 		read = 1; | ||||
| 	} | ||||
| 
 | ||||
| 	for (offset = 0; offset < i_size; offset += rbuf_len) { | ||||
| 		if (!rbuf[1] && offset) { | ||||
| 			/* Not using two buffers, and it is not the first
 | ||||
| 			 * read/request, wait for the completion of the | ||||
| 			 * previous ahash_update() request. | ||||
| 			 */ | ||||
| 			rc = ahash_wait(ahash_rc, &res); | ||||
| 			if (rc) | ||||
| 				goto out3; | ||||
| 		} | ||||
| 		/* read buffer */ | ||||
| 		rbuf_len = min_t(loff_t, i_size - offset, rbuf_size[active]); | ||||
| 		rc = ima_kernel_read(file, offset, rbuf[active], rbuf_len); | ||||
| 		if (rc != rbuf_len) | ||||
| 			goto out3; | ||||
| 
 | ||||
| 		if (rbuf[1] && offset) { | ||||
| 			/* Using two buffers, and it is not the first
 | ||||
| 			 * read/request, wait for the completion of the | ||||
| 			 * previous ahash_update() request. | ||||
| 			 */ | ||||
| 			rc = ahash_wait(ahash_rc, &res); | ||||
| 			if (rc) | ||||
| 				goto out3; | ||||
| 		} | ||||
| 
 | ||||
| 		sg_init_one(&sg[0], rbuf[active], rbuf_len); | ||||
| 		ahash_request_set_crypt(req, sg, NULL, rbuf_len); | ||||
| 
 | ||||
| 		ahash_rc = crypto_ahash_update(req); | ||||
| 
 | ||||
| 		if (rbuf[1]) | ||||
| 			active = !active; /* swap buffers, if we use two */ | ||||
| 	} | ||||
| 	/* wait for the last update request to complete */ | ||||
| 	rc = ahash_wait(ahash_rc, &res); | ||||
| out3: | ||||
| 	if (read) | ||||
| 		file->f_mode &= ~FMODE_READ; | ||||
| 	ima_free_pages(rbuf[0], rbuf_size[0]); | ||||
| 	ima_free_pages(rbuf[1], rbuf_size[1]); | ||||
| out2: | ||||
| 	if (!rc) { | ||||
| 		ahash_request_set_crypt(req, NULL, hash->digest, 0); | ||||
| 		rc = ahash_wait(crypto_ahash_final(req), &res); | ||||
| 	} | ||||
| out1: | ||||
| 	ahash_request_free(req); | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| static int ima_calc_file_ahash(struct file *file, struct ima_digest_data *hash) | ||||
| { | ||||
| 	struct crypto_ahash *tfm; | ||||
| 	int rc; | ||||
| 
 | ||||
| 	tfm = ima_alloc_atfm(hash->algo); | ||||
| 	if (IS_ERR(tfm)) | ||||
| 		return PTR_ERR(tfm); | ||||
| 
 | ||||
| 	rc = ima_calc_file_hash_atfm(file, hash, tfm); | ||||
| 
 | ||||
| 	ima_free_atfm(tfm); | ||||
| 
 | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| static int ima_calc_file_hash_tfm(struct file *file, | ||||
| 				  struct ima_digest_data *hash, | ||||
| 				  struct crypto_shash *tfm) | ||||
|  | @ -156,7 +433,7 @@ out: | |||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash) | ||||
| static int ima_calc_file_shash(struct file *file, struct ima_digest_data *hash) | ||||
| { | ||||
| 	struct crypto_shash *tfm; | ||||
| 	int rc; | ||||
|  | @ -172,6 +449,35 @@ int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash) | |||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * ima_calc_file_hash - calculate file hash | ||||
|  * | ||||
|  * Asynchronous hash (ahash) allows using HW acceleration for calculating | ||||
|  * a hash. ahash performance varies for different data sizes on different | ||||
|  * crypto accelerators. shash performance might be better for smaller files. | ||||
|  * The 'ima.ahash_minsize' module parameter allows specifying the best | ||||
|  * minimum file size for using ahash on the system. | ||||
|  * | ||||
|  * If the ima.ahash_minsize parameter is not specified, this function uses | ||||
|  * shash for the hash calculation.  If ahash fails, it falls back to using | ||||
|  * shash. | ||||
|  */ | ||||
| int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash) | ||||
| { | ||||
| 	loff_t i_size; | ||||
| 	int rc; | ||||
| 
 | ||||
| 	i_size = i_size_read(file_inode(file)); | ||||
| 
 | ||||
| 	if (ima_ahash_minsize && i_size >= ima_ahash_minsize) { | ||||
| 		rc = ima_calc_file_ahash(file, hash); | ||||
| 		if (!rc) | ||||
| 			return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	return ima_calc_file_shash(file, hash); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Calculate the hash of template data | ||||
|  */ | ||||
|  |  | |||
|  | @ -88,8 +88,6 @@ static void ima_rdwr_violation_check(struct file *file) | |||
| 	if (!S_ISREG(inode->i_mode) || !ima_initialized) | ||||
| 		return; | ||||
| 
 | ||||
| 	mutex_lock(&inode->i_mutex);	/* file metadata: permissions, xattr */ | ||||
| 
 | ||||
| 	if (mode & FMODE_WRITE) { | ||||
| 		if (atomic_read(&inode->i_readcount) && IS_IMA(inode)) { | ||||
| 			struct integrity_iint_cache *iint; | ||||
|  | @ -104,8 +102,6 @@ static void ima_rdwr_violation_check(struct file *file) | |||
| 			send_writers = true; | ||||
| 	} | ||||
| 
 | ||||
| 	mutex_unlock(&inode->i_mutex); | ||||
| 
 | ||||
| 	if (!send_tomtou && !send_writers) | ||||
| 		return; | ||||
| 
 | ||||
|  | @ -163,7 +159,7 @@ static int process_measurement(struct file *file, const char *filename, | |||
| { | ||||
| 	struct inode *inode = file_inode(file); | ||||
| 	struct integrity_iint_cache *iint; | ||||
| 	struct ima_template_desc *template_desc = ima_template_desc_current(); | ||||
| 	struct ima_template_desc *template_desc; | ||||
| 	char *pathbuf = NULL; | ||||
| 	const char *pathname = NULL; | ||||
| 	int rc = -ENOMEM, action, must_appraise, _func; | ||||
|  | @ -207,6 +203,7 @@ static int process_measurement(struct file *file, const char *filename, | |||
| 		goto out_digsig; | ||||
| 	} | ||||
| 
 | ||||
| 	template_desc = ima_template_desc_current(); | ||||
| 	if (strcmp(template_desc->name, IMA_TEMPLATE_IMA_NAME) == 0) { | ||||
| 		if (action & IMA_APPRAISE_SUBMASK) | ||||
| 			xattr_ptr = &xattr_value; | ||||
|  | @ -322,14 +319,31 @@ int ima_module_check(struct file *file) | |||
| 	return process_measurement(file, NULL, MAY_EXEC, MODULE_CHECK); | ||||
| } | ||||
| 
 | ||||
| int ima_fw_from_file(struct file *file, char *buf, size_t size) | ||||
| { | ||||
| 	if (!file) { | ||||
| 		if ((ima_appraise & IMA_APPRAISE_FIRMWARE) && | ||||
| 		    (ima_appraise & IMA_APPRAISE_ENFORCE)) | ||||
| 			return -EACCES;	/* INTEGRITY_UNKNOWN */ | ||||
| 		return 0; | ||||
| 	} | ||||
| 	return process_measurement(file, NULL, MAY_EXEC, FIRMWARE_CHECK); | ||||
| } | ||||
| 
 | ||||
| static int __init init_ima(void) | ||||
| { | ||||
| 	int error; | ||||
| 
 | ||||
| 	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; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -84,6 +84,7 @@ static struct ima_rule_entry default_rules[] = { | |||
| 	{.action = MEASURE, .func = FILE_CHECK, .mask = MAY_READ, .uid = GLOBAL_ROOT_UID, | ||||
| 	 .flags = IMA_FUNC | IMA_MASK | IMA_UID}, | ||||
| 	{.action = MEASURE, .func = MODULE_CHECK, .flags = IMA_FUNC}, | ||||
| 	{.action = MEASURE, .func = FIRMWARE_CHECK, .flags = IMA_FUNC}, | ||||
| }; | ||||
| 
 | ||||
| static struct ima_rule_entry default_appraise_rules[] = { | ||||
|  | @ -241,6 +242,8 @@ static int get_subaction(struct ima_rule_entry *rule, int func) | |||
| 		return IMA_BPRM_APPRAISE; | ||||
| 	case MODULE_CHECK: | ||||
| 		return IMA_MODULE_APPRAISE; | ||||
| 	case FIRMWARE_CHECK: | ||||
| 		return IMA_FIRMWARE_APPRAISE; | ||||
| 	case FILE_CHECK: | ||||
| 	default: | ||||
| 		return IMA_FILE_APPRAISE; | ||||
|  | @ -332,7 +335,7 @@ void __init ima_init_policy(void) | |||
| void ima_update_policy(void) | ||||
| { | ||||
| 	static const char op[] = "policy_update"; | ||||
| 	const char *cause = "already exists"; | ||||
| 	const char *cause = "already-exists"; | ||||
| 	int result = 1; | ||||
| 	int audit_info = 0; | ||||
| 
 | ||||
|  | @ -486,6 +489,8 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) | |||
| 				entry->func = FILE_CHECK; | ||||
| 			else if (strcmp(args[0].from, "MODULE_CHECK") == 0) | ||||
| 				entry->func = MODULE_CHECK; | ||||
| 			else if (strcmp(args[0].from, "FIRMWARE_CHECK") == 0) | ||||
| 				entry->func = FIRMWARE_CHECK; | ||||
| 			else if ((strcmp(args[0].from, "FILE_MMAP") == 0) | ||||
| 				|| (strcmp(args[0].from, "MMAP_CHECK") == 0)) | ||||
| 				entry->func = MMAP_CHECK; | ||||
|  | @ -636,6 +641,8 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) | |||
| 		result = -EINVAL; | ||||
| 	else if (entry->func == MODULE_CHECK) | ||||
| 		ima_appraise |= IMA_APPRAISE_MODULES; | ||||
| 	else if (entry->func == FIRMWARE_CHECK) | ||||
| 		ima_appraise |= IMA_APPRAISE_FIRMWARE; | ||||
| 	audit_log_format(ab, "res=%d", !result); | ||||
| 	audit_log_end(ab); | ||||
| 	return result; | ||||
|  | @ -659,7 +666,7 @@ ssize_t ima_parse_add_rule(char *rule) | |||
| 	/* Prevent installed policy from changing */ | ||||
| 	if (ima_rules != &ima_default_rules) { | ||||
| 		integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, | ||||
| 				    NULL, op, "already exists", | ||||
| 				    NULL, op, "already-exists", | ||||
| 				    -EACCES, audit_info); | ||||
| 		return -EACCES; | ||||
| 	} | ||||
|  | @ -685,7 +692,7 @@ ssize_t ima_parse_add_rule(char *rule) | |||
| 	if (result) { | ||||
| 		kfree(entry); | ||||
| 		integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, | ||||
| 				    NULL, op, "invalid policy", result, | ||||
| 				    NULL, op, "invalid-policy", result, | ||||
| 				    audit_info); | ||||
| 		return result; | ||||
| 	} | ||||
|  |  | |||
|  | @ -46,10 +46,14 @@ | |||
| #define IMA_BPRM_APPRAISED	0x00002000 | ||||
| #define IMA_MODULE_APPRAISE	0x00004000 | ||||
| #define IMA_MODULE_APPRAISED	0x00008000 | ||||
| #define IMA_FIRMWARE_APPRAISE	0x00010000 | ||||
| #define IMA_FIRMWARE_APPRAISED	0x00020000 | ||||
| #define IMA_APPRAISE_SUBMASK	(IMA_FILE_APPRAISE | IMA_MMAP_APPRAISE | \ | ||||
| 				 IMA_BPRM_APPRAISE | IMA_MODULE_APPRAISE) | ||||
| 				 IMA_BPRM_APPRAISE | IMA_MODULE_APPRAISE | \ | ||||
| 				 IMA_FIRMWARE_APPRAISE) | ||||
| #define IMA_APPRAISED_SUBMASK	(IMA_FILE_APPRAISED | IMA_MMAP_APPRAISED | \ | ||||
| 				 IMA_BPRM_APPRAISED | IMA_MODULE_APPRAISED) | ||||
| 				 IMA_BPRM_APPRAISED | IMA_MODULE_APPRAISED | \ | ||||
| 				 IMA_FIRMWARE_APPRAISED) | ||||
| 
 | ||||
| enum evm_ima_xattr_type { | ||||
| 	IMA_XATTR_DIGEST = 0x01, | ||||
|  | @ -104,6 +108,7 @@ struct integrity_iint_cache { | |||
| 	enum integrity_status ima_mmap_status:4; | ||||
| 	enum integrity_status ima_bprm_status:4; | ||||
| 	enum integrity_status ima_module_status:4; | ||||
| 	enum integrity_status ima_firmware_status:4; | ||||
| 	enum integrity_status evm_status:4; | ||||
| 	struct ima_digest_data *ima_hash; | ||||
| }; | ||||
|  | @ -124,6 +129,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 +139,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 | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -845,6 +845,17 @@ int security_kernel_create_files_as(struct cred *new, struct inode *inode) | |||
| 	return security_ops->kernel_create_files_as(new, inode); | ||||
| } | ||||
| 
 | ||||
| int security_kernel_fw_from_file(struct file *file, char *buf, size_t size) | ||||
| { | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = security_ops->kernel_fw_from_file(file, buf, size); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 	return ima_fw_from_file(file, buf, size); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(security_kernel_fw_from_file); | ||||
| 
 | ||||
| int security_kernel_module_request(char *kmod_name) | ||||
| { | ||||
| 	return security_ops->kernel_module_request(kmod_name); | ||||
|  |  | |||
|  | @ -161,6 +161,17 @@ static int selinux_peerlbl_enabled(void) | |||
| 	return (selinux_policycap_alwaysnetwork || netlbl_enabled() || selinux_xfrm_enabled()); | ||||
| } | ||||
| 
 | ||||
| static int selinux_netcache_avc_callback(u32 event) | ||||
| { | ||||
| 	if (event == AVC_CALLBACK_RESET) { | ||||
| 		sel_netif_flush(); | ||||
| 		sel_netnode_flush(); | ||||
| 		sel_netport_flush(); | ||||
| 		synchronize_net(); | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * initialise the security for the init task | ||||
|  */ | ||||
|  | @ -5993,6 +6004,9 @@ static __init int selinux_init(void) | |||
| 	if (register_security(&selinux_ops)) | ||||
| 		panic("SELinux: Unable to register with kernel.\n"); | ||||
| 
 | ||||
| 	if (avc_add_callback(selinux_netcache_avc_callback, AVC_CALLBACK_RESET)) | ||||
| 		panic("SELinux: Unable to register AVC netcache callback\n"); | ||||
| 
 | ||||
| 	if (selinux_enforcing) | ||||
| 		printk(KERN_DEBUG "SELinux:  Starting in enforcing mode\n"); | ||||
| 	else | ||||
|  |  | |||
|  | @ -17,6 +17,8 @@ | |||
| #ifndef _SELINUX_NETIF_H_ | ||||
| #define _SELINUX_NETIF_H_ | ||||
| 
 | ||||
| void sel_netif_flush(void); | ||||
| 
 | ||||
| int sel_netif_sid(int ifindex, u32 *sid); | ||||
| 
 | ||||
| #endif	/* _SELINUX_NETIF_H_ */ | ||||
|  |  | |||
|  | @ -27,6 +27,8 @@ | |||
| #ifndef _SELINUX_NETNODE_H | ||||
| #define _SELINUX_NETNODE_H | ||||
| 
 | ||||
| void sel_netnode_flush(void); | ||||
| 
 | ||||
| int sel_netnode_sid(void *addr, u16 family, u32 *sid); | ||||
| 
 | ||||
| #endif | ||||
|  |  | |||
|  | @ -26,6 +26,8 @@ | |||
| #ifndef _SELINUX_NETPORT_H | ||||
| #define _SELINUX_NETPORT_H | ||||
| 
 | ||||
| void sel_netport_flush(void); | ||||
| 
 | ||||
| int sel_netport_sid(u8 protocol, u16 pnum, u32 *sid); | ||||
| 
 | ||||
| #endif | ||||
|  |  | |||
|  | @ -8,6 +8,7 @@ | |||
| #ifndef _SELINUX_SECURITY_H_ | ||||
| #define _SELINUX_SECURITY_H_ | ||||
| 
 | ||||
| #include <linux/compiler.h> | ||||
| #include <linux/dcache.h> | ||||
| #include <linux/magic.h> | ||||
| #include <linux/types.h> | ||||
|  | @ -220,7 +221,7 @@ struct selinux_kernel_status { | |||
| 	/*
 | ||||
| 	 * The version > 0 supports above members. | ||||
| 	 */ | ||||
| } __attribute__((packed)); | ||||
| } __packed; | ||||
| 
 | ||||
| extern void selinux_status_update_setenforce(int enforcing); | ||||
| extern void selinux_status_update_policyload(int seqno); | ||||
|  |  | |||
|  | @ -240,7 +240,7 @@ static void sel_netif_kill(int ifindex) | |||
|  * Remove all entries from the network interface table. | ||||
|  * | ||||
|  */ | ||||
| static void sel_netif_flush(void) | ||||
| void sel_netif_flush(void) | ||||
| { | ||||
| 	int idx; | ||||
| 	struct sel_netif *netif; | ||||
|  | @ -252,15 +252,6 @@ static void sel_netif_flush(void) | |||
| 	spin_unlock_bh(&sel_netif_lock); | ||||
| } | ||||
| 
 | ||||
| static int sel_netif_avc_callback(u32 event) | ||||
| { | ||||
| 	if (event == AVC_CALLBACK_RESET) { | ||||
| 		sel_netif_flush(); | ||||
| 		synchronize_net(); | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int sel_netif_netdev_notifier_handler(struct notifier_block *this, | ||||
| 					     unsigned long event, void *ptr) | ||||
| { | ||||
|  | @ -291,10 +282,6 @@ static __init int sel_netif_init(void) | |||
| 
 | ||||
| 	register_netdevice_notifier(&sel_netif_netdev_notifier); | ||||
| 
 | ||||
| 	err = avc_add_callback(sel_netif_avc_callback, AVC_CALLBACK_RESET); | ||||
| 	if (err) | ||||
| 		panic("avc_add_callback() failed, error %d\n", err); | ||||
| 
 | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
Some files were not shown because too many files have changed in this diff Show more
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 Linus Torvalds
						Linus Torvalds