Example #1
0
/*
 * verify a module's signature
 */
int module_verify_signature(struct module_verify_data *mvdata)
{
	const Elf_Shdr *sechdrs = mvdata->sections;
	const char *secstrings = mvdata->secstrings;
	const char *sig;
	unsigned sig_size;
	int i, ret;

	for (i = 1; i < mvdata->nsects; i++) {
		switch (sechdrs[i].sh_type) {
		case SHT_PROGBITS:
			if (strcmp(mvdata->secstrings + sechdrs[i].sh_name,
				   ".module_sig") == 0) {
				mvdata->sig_index = i;
			}
			break;
		}
	}

	if (mvdata->sig_index <= 0)
		goto no_signature;

	sig = mvdata->buffer + sechdrs[mvdata->sig_index].sh_offset;
	sig_size = sechdrs[mvdata->sig_index].sh_size;

	_debug("sig in section %d (size %d)\n",
	       mvdata->sig_index, sig_size);

	/* produce a canonicalisation map for the sections */
	ret = module_verify_canonicalise(mvdata);
	if (ret < 0)
		return ret;

	/* grab an SHA1 transformation context
	 * - !!! if this tries to load the sha1.ko module, we will deadlock!!!
	 */
	mvdata->digest = crypto_alloc_tfm2("sha1", 0, 1);
	if (!mvdata->digest) {
		printk("Couldn't load module - SHA1 transform unavailable\n");
		return -EPERM;
	}

	crypto_digest_init(mvdata->digest);

#ifdef MODSIGN_DEBUG
	mvdata->xcsum = 0;
#endif

	/* load data from each relevant section into the digest */
	for (i = 1; i < mvdata->nsects; i++) {
		unsigned long sh_type = sechdrs[i].sh_type;
		unsigned long sh_info = sechdrs[i].sh_info;
		unsigned long sh_size = sechdrs[i].sh_size;
		unsigned long sh_flags = sechdrs[i].sh_flags;
		const char *sh_name = secstrings + sechdrs[i].sh_name;
		const void *data = mvdata->buffer + sechdrs[i].sh_offset;

		if (i == mvdata->sig_index)
			continue;

#ifdef MODSIGN_DEBUG
		mvdata->csum = 0;
#endif

		/* it would be nice to include relocation sections, but the act
		 * of adding a signature to the module seems changes their
		 * contents, because the symtab gets changed when sections are
		 * added or removed */
		if (sh_type == SHT_REL || sh_type == SHT_RELA) {
			if (mvdata->canonlist[sh_info]) {
				uint32_t xsh_info = mvdata->canonmap[sh_info];

				crypto_digest_update_data(mvdata, sh_name, strlen(sh_name));
				crypto_digest_update_val(mvdata, sechdrs[i].sh_type);
				crypto_digest_update_val(mvdata, sechdrs[i].sh_flags);
				crypto_digest_update_val(mvdata, sechdrs[i].sh_size);
				crypto_digest_update_val(mvdata, sechdrs[i].sh_addralign);
				crypto_digest_update_val(mvdata, xsh_info);

				if (sh_type == SHT_RELA)
					ret = extract_elf_rela(
						mvdata, i,
						data,
						sh_size / sizeof(Elf_Rela),
						sh_name);
				else
					ret = extract_elf_rel(
						mvdata, i,
						data,
						sh_size / sizeof(Elf_Rel),
						sh_name);

				if (ret < 0)
					goto format_error;
			}

			continue;
		}

		/* include allocatable loadable sections */
		if (sh_type != SHT_NOBITS && sh_flags & SHF_ALLOC)
			goto include_section;

		continue;

	include_section:
		crypto_digest_update_data(mvdata, sh_name, strlen(sh_name));
		crypto_digest_update_val(mvdata, sechdrs[i].sh_type);
		crypto_digest_update_val(mvdata, sechdrs[i].sh_flags);
		crypto_digest_update_val(mvdata, sechdrs[i].sh_size);
		crypto_digest_update_val(mvdata, sechdrs[i].sh_addralign);
		crypto_digest_update_data(mvdata, data, sh_size);

		_debug("%08zx %02x digested the %s section, size %ld\n",
		       mvdata->signed_size, mvdata->csum, sh_name, sh_size);

		mvdata->canonlist[i] = 1;
	}

	_debug("Contributed %zu bytes to the digest (csum 0x%02x)\n",
	       mvdata->signed_size, mvdata->xcsum);

	/* do the actual signature verification */
	i = ksign_verify_signature(sig, sig_size, mvdata->digest);

	_debug("verify-sig : %d\n", i);

	if (i == 0)
		i = 1;
	return i;

 format_error:
	crypto_free_tfm(mvdata->digest);
	return -ELIBBAD;

	/* deal with the case of an unsigned module */
 no_signature:
 	if (!signedonly)
		return 0;
	printk("An attempt to load unsigned module was rejected\n");
	return -EPERM;

} /* end module_verify_signature() */
/*
 * verify a module's signature
 */
int module_verify_signature(struct module_verify_data *mvdata,
			    int *_gpgsig_ok)
{
	const struct elf_note *note;
	struct crypto_shash *tfm;
	const Elf_Shdr *sechdrs = mvdata->sections;
	const char *secstrings = mvdata->secstrings;
	const char *sig;
	unsigned note_size, sig_size, note_namesz;
	int loop, ret;

	_debug("looking for sig section '%s'\n", modsign_note_section);

	for (loop = 1; loop < mvdata->nsects; loop++) {
		switch (sechdrs[loop].sh_type) {
		case SHT_NOTE:
			if (strcmp(mvdata->secstrings + sechdrs[loop].sh_name,
				   modsign_note_section) == 0)
				mvdata->sig_index = loop;
			break;
		}
	}

	if (mvdata->sig_index <= 0)
		goto no_signature;

	note = mvdata->buffer + sechdrs[mvdata->sig_index].sh_offset;
	note_size = sechdrs[mvdata->sig_index].sh_size;

	/* there should be one note of the appropriate type */
	if (note_size < sizeof(*note) + 2 * 4)
		goto format_error_no_free;
	note_namesz = note->n_namesz;
	sig_size = note->n_descsz;
	if (note_namesz != sizeof(modsign_note_name))
		goto format_error_no_free;
	if (note->n_type != MODSIGN_NOTE_TYPE)
		goto format_error_no_free;
	if (memcmp(note + 1, modsign_note_name, note_namesz) != 0)
		goto format_error_no_free;
	sig = (void *)(note + 1) + roundup(note_namesz, 4);

	_debug("sig in section %d (size %d)\n",
	       mvdata->sig_index, sig_size);
	_debug("%02x%02x%02x%02x%02x%02x%02x%02x\n",
	       sig[0], sig[1], sig[2], sig[3],
	       sig[4], sig[5], sig[6], sig[7]);

	/* produce a canonicalisation map for the sections */
	ret = module_verify_canonicalise(mvdata);
	if (ret < 0)
		return ret;

	/* grab an SHA1 transformation context
	 * - !!! if this tries to load the sha1.ko module, we will deadlock!!!
	 */
	tfm = crypto_alloc_shash("sha1", 0, 0);
	if (IS_ERR(tfm)) {
		printk(KERN_ERR
		       "Couldn't load module - SHA1 transform unavailable\n");
		return -EPERM;
	}

	mvdata->hash = kmalloc(sizeof(*mvdata->hash) +
			       crypto_shash_descsize(tfm),
			       GFP_KERNEL);
	if (!mvdata->hash) {
		crypto_free_shash(tfm);
		return -ENOMEM;
	}

	mvdata->hash->tfm = tfm;
	mvdata->hash->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
	ret = crypto_shash_init(mvdata->hash);
	if (ret < 0) {
		crypto_free_shash(mvdata->hash->tfm);
		kfree(mvdata->hash);
		return -ENOMEM;
	}

#ifdef MODSIGN_DEBUG
	mvdata->xcsum = 0;
#endif

	/* load data from each relevant section into the digest */
	for (loop = 0; loop < mvdata->ncanon; loop++) {
		int sect = mvdata->canonlist[loop];
		unsigned long sh_type = sechdrs[sect].sh_type;
		unsigned long sh_info = sechdrs[sect].sh_info;
		unsigned long sh_size = sechdrs[sect].sh_size;
		unsigned long sh_flags = sechdrs[sect].sh_flags;
		const char *sh_name = secstrings + sechdrs[sect].sh_name;
		const void *data = mvdata->buffer + sechdrs[sect].sh_offset;

#ifdef MODSIGN_DEBUG
		mvdata->csum = 0;
#endif

		/* it would be nice to include relocation sections, but the act
		 * of adding a signature to the module seems changes their
		 * contents, because the symtab gets changed when sections are
		 * added or removed */
		if (sh_type == SHT_REL || sh_type == SHT_RELA) {
			uint32_t xsh_info = mvdata->canonmap[sh_info];

			crypto_digest_update_data(mvdata, sh_name, strlen(sh_name));
			crypto_digest_update_val(mvdata, sechdrs[sect].sh_type);
			crypto_digest_update_val(mvdata, sechdrs[sect].sh_flags);
			crypto_digest_update_val(mvdata, sechdrs[sect].sh_size);
			crypto_digest_update_val(mvdata, sechdrs[sect].sh_addralign);
			crypto_digest_update_val(mvdata, xsh_info);

			if (sh_type == SHT_RELA)
				ret = extract_elf_rela(
					mvdata, sect,
					data,
					sh_size / sizeof(Elf_Rela),
					sh_name);
			else
				ret = extract_elf_rel(
					mvdata, sect,
					data,
					sh_size / sizeof(Elf_Rel),
					sh_name);

			if (ret < 0)
				goto format_error;
			continue;
		}

		/* include the headers of BSS sections */
		if (sh_type == SHT_NOBITS && sh_flags & SHF_ALLOC) {
			crypto_digest_update_data(mvdata, sh_name, strlen(sh_name));
			crypto_digest_update_val(mvdata, sechdrs[sect].sh_type);
			crypto_digest_update_val(mvdata, sechdrs[sect].sh_flags);
			crypto_digest_update_val(mvdata, sechdrs[sect].sh_size);
			crypto_digest_update_val(mvdata, sechdrs[sect].sh_addralign);
			goto digested;
		}

		/* include allocatable loadable sections */
		if (sh_type != SHT_NOBITS && sh_flags & SHF_ALLOC)
			goto include_section;

		continue;

	include_section:
		crypto_digest_update_data(mvdata, sh_name, strlen(sh_name));
		crypto_digest_update_val(mvdata, sechdrs[sect].sh_type);
		crypto_digest_update_val(mvdata, sechdrs[sect].sh_flags);
		crypto_digest_update_val(mvdata, sechdrs[sect].sh_size);
		crypto_digest_update_val(mvdata, sechdrs[sect].sh_addralign);

		crypto_digest_update_data(mvdata, data, sh_size);

	digested:
		_debug("%08zx %02x digested the %s section, size %ld\n",
		       mvdata->signed_size, mvdata->csum, sh_name, sh_size);
	}

	_debug("Contributed %zu bytes to the digest (csum 0x%02x)\n",
	       mvdata->signed_size, mvdata->xcsum);

	/* do the actual signature verification */
	ret = ksign_verify_signature(sig, sig_size, mvdata->hash);

	crypto_free_shash(mvdata->hash->tfm);
	kfree(mvdata->hash);

	_debug("verify-sig : %d\n", ret);

	switch (ret) {
	case 0:			/* good signature */
		*_gpgsig_ok = 1;
		break;
	case -EKEYREJECTED:	/* signature mismatch or number format error */
		printk(KERN_ERR "Module signature verification failed\n");
		break;
	case -ENOKEY:		/* signed, but we don't have the public key */
		printk(KERN_ERR "Module signed with unknown public key\n");
		break;
	default:		/* other error (probably ENOMEM) */
		break;
	}

	if (ret && badsigok) {
		printk(KERN_ERR "Bad signature ignored by cmdline\n");
		ret = 0;
	}

	return ret;

format_error:
	crypto_free_shash(mvdata->hash->tfm);
	kfree(mvdata->hash);
format_error_no_free:
	printk(KERN_ERR "Module format error encountered\n");
	return -ELIBBAD;

	/* deal with the case of an unsigned module */
no_signature:
	_debug("no signature found\n");
	if (!signedonly)
		return 0;
	printk(KERN_ERR "An attempt to load unsigned module was rejected\n");
	return -EKEYREJECTED;
}