/* * 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; }