Signed Kernel Modules
When the kernel is told to load a module, the code in the file kernel/module.c is run. In that file, the function load_module does all of the work of breaking the module into the proper sections, checking memory locations, checking symbols and all the other tasks a linker generally does. The patch modifies this function and adds the following lines of code:
if (module_check_sig(hdr, sechdrs, secstrings)) {
err = -EPERM;
goto free_hdr;
}
This new function, module_check_sig does all of the module signature-checking logic. If it returns an error, the error Improper Permission is returned to the user and module loading is aborted. If the function returns a 0, meaning no error occurred, the module load procedure continues on successfully.
The module_check_sig function is located in the file kernel/module-sig.c. The first thing the function does is check to see if a signature is located within the module. This is done with the following lines of code:
sig_index = 0;
for (i = 1; i < hdr->e_shnum; i++)
if (strcmp(secstrings+sechdrs[i].sh_name,
"module_sig") == 0) {
sig_index = i;
break;
}
if (sig_index <= 0)
return -EPERM;
This bit of code loops through all of the different ELF sections in the kernel module and looks for one called module_sig. If it does not find the signature, it returns an error and prevents this module from being loaded. If it does find the signature, the function continues.
Once the kernel has found the module signature, it needs to determine what the hash value is of the module it is being asked to load. To do this, it generates the SHA1 hash of the ELF section that contains executable code or data used by the kernel. The kernel already contains code to generate SHA1 hashes (along with other kinds of hashes, including MD5 and MD4), so most of the logic for this step is present already.
The function first allocates a crypto transformation structure by requesting the SHA1 algorithm. It then initializes this structure with the following lines of code:
sha1_tfm = crypto_alloc_tfm("sha1", 0);
if (sha1_tfm == NULL)
return -ENOMEM;
crypto_digest_init(sha1_tfm);
The sha1_tfm variable is used to create the SHA1
hash of the specific portions of the ELF file that we want, as
shown in the following code:
for (i = 1; i < hdr->e_shnum; i++) {
name = secstrings+sechdrs[i].sh_name;
/* We only care about sections with "text" or
"data" in their names */
if ((strstr(name, "text") == NULL) &&
(strstr(name, "data") == NULL))
continue;
/* avoid the ".rel.*" sections too. */
if (strstr(name, ".rel.") != NULL)
continue;
temp = (void *)sechdrs[i].sh_addr;
size = sechdrs[i].sh_size;
do {
memset(&sg, 0x00, sizeof(*sg));
sg.page = virt_to_page(temp);
sg.offset = offset_in_page(temp);
sg.length = min(size,
(PAGE_SIZE - sg.offset));
size -= sg.length;
temp += sg.length;
crypto_digest_update(sha1_tfm, &sg, 1);
} while (size > 0);
}
In this code, we care only about the ELF sections with the word text or data in their names but not ones that contain the characters .rel. After all of the sections have been found and fed to the SHA1 algorithm, the SHA1 hash is placed into the variable sha1_result with the following lines:
crypto_digest_final(sha1_tfm, sha1_result); crypto_free_tfm(sha1_tfm);
Now that the SHA1 hash is computed and the place with the signed hash has been found, all that is left to do is unencrypt the signed hash and compare it to the calculated one. This step is done in the last line of this function:
return rsa_check_sig(sig, &sha1_result[0]);
The rsa_check_sig function is located in the security/rsa/rsa.c file and uses the GnuPG code itself, which was ported to run in the kernel to unencrypt the signature and compare the values. The description of how this works is beyond the scope of this article.
Now that we have seen how the kernel determines whether a module is signed properly, how do we get a signature into a module in the first place? Two user-space programs, extract_pkey and mod, and one small script, sign (in the security/rsa/userspace/ directory), can be found in the kernel patch. The two programs can be built by running the Makefile in this directory. The extract_pkey program is used to place a public key into the kernel, and the mod program is used by the sign script to sign a kernel module.
In order to sign a module, an RSA-signing key must be generated, which can be done by using the gnupg program. To generate an RSA-signing key, pass the --gen-key option to gpg:
$ gpg --gen-key gpg (GnuPG) 1.2.1; Copyright (C) 2002 Free Software Foundation, Inc. This program comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it under certain conditions. See the file COPYING for details. Please select what kind of key you want: (1) DSA and ElGamal (default) (2) DSA (sign only) (5) RSA (sign only) Your selection?
We want to create an RSA key, so we select option 5 and then choose the default key size of 1024:
Your selection? 5 What keysize do you want? (1024) Requested keysize is 1024 bits
Continue answering the rest of the questions, and eventually your RSA key is generated. But in order to use this key, we must create an encrypting version of it. To do that, run gpg again and edit the key you just created (in the text below, I have named my key testkey):
$ gpg --edit-key testkey gpg (GnuPG) 1.2.1; Copyright (C) 2002 Free Software Foundation, Inc. This program comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it under certain conditions. See the file COPYING for details. Secret key is available. gpg: checking the trustdb gpg: checking at depth 0 signed=0 ot(-/q/n/m/f/u)=0/0/0/0/0/1 pub 1024R/77540AE9 created: 2003-10-09 expires: never trust: u/u (1). testkey Command>
We want to add a new key, so type addkey at the prompt:
Command> addkey Please select what kind of key you want: (2) DSA (sign only) (3) ElGamal (encrypt only) (5) RSA (sign only) (6) RSA (encrypt only) Your selection?
Again, we want an RSA key, so choose option 6 and answer the rest of the questions. After the key is generated, type quit at the prompt:
Command> quit Save changes? yes
Now that we have a key, we can use it to sign a kernel module.
To sign a module, use the sign script, which is a simple shell script:
#!/bin/bash
module=$1
key=$2
# strip out only the sections that we care about
./mod $module $module.out
# sha1 the sections
sha1sum $module.out | awk "{print \$1}" > \
$module.sha1
# encrypt the sections
gpg --no-greeting -e -o - -r $key $module.sha1 > \
$module.crypt
# add the encrypted data to the module
objcopy --add-section module_sig=$module.crypt \
$module
# remove the temporary files
rm $module.out $module.sha1 $module.crypt
The first thing the script does is run the program mod on the kernel module. This program strips out only the sections that we care about in the ELF file and outputs them to a temporary file. The mod program is described in more detail later.
After we have an ELF file that contains only the sections we want, we generate a SHA1 hash of the file using the sha1sum program. This SHA1 hash then is encrypted using GPG, the key is passed to it and this encrypted file is written out to a temporary file. The encrypted file is added to the original module as a new ELF section with the name module-sig. This is done with the program objcopy. And that is it. Using common programs already present on a Linux machine, it is easy to create a SHA1 hash, encrypt it and add it to an ELF file.
The mod program also is quite simple. It takes advantage of the fact that the libbfd library knows how to handle ELF files and manipulates them in different ways; it is based on the binutils program objdump. Because the libbfd library handles all of the heavy ELF logic, the mod program simply can iterate through all the sections of the ELF file it wants to with the following code:
for (section = abfd->sections;
section != NULL;
section = section->next) {
if (section->flags & SEC_HAS_CONTENTS) {
if (bfd_section_size(abfd, section) == 0)
continue;
/* We only care about sections with "text"
or "data" in their names */
name = section->name;
if ((strstr(name, "text") == NULL) &&
(strstr(name, "data") == NULL))
continue;
size = bfd_section_size(abfd, section));
data = (bfd_byte *)malloc(size);
bfd_get_section_contents(abfd, section,
(PTR)data,
0, size);
stop_offset = size / opb;
for (addr_offset = 0;
addr_offset < stop_offset;
++addr_offset) {
fprintf(out, "%c", data[addr_offset]);
}
free(data);
}
}
Now that we can sign a kernel module and the kernel knows how to detect this signature, the only remaining piece is to put our public key into the kernel so it can decrypt the signature successfully. A lot of discussion on the linux-kernel mailing list recently has centered on how to handle keys within the kernel properly. That discussion has produced some good proposals for how this aspect will be handled in the 2.7 kernel series. But for now, we do not worry about properly handling keys in flexible ways, so we compile it in directly.
First we need to get a copy of our public key. To do this, tell GPG to extract the key to a file called public_key:
$ gpg --export -o public_key
To help manipulate GPG public keys, some developers at Ericsson created a simple program called extract_pkey to help dissect the keys into their different pieces. I have modified that program to generate C code for the public key.
Run the extract_pkey program and point it at the public_key file you generated previously. Have it send the output to a file called rsa_key.c:
$ extract_pkey public_key > rsa_key.c
After this step is finished, move that rsa_key.c on top of the file in the security/rsa/ directory, replacing my public key with yours:
$ mv rsa_key.c ~/linux/linux-2.6/security/rsa/
Now you have generated a public and private RSA key pair and placed your public key into the kernel directory. Build the patched kernel, making sure to select the Module signature checking option, and then install it. If you boot in to this kernel, you will be allowed to load only the modules you have signed with your key, so be careful and test this only on a development machine.
Realizing the promise of Apache® Hadoop® requires the effective deployment of compute, memory, storage and networking to achieve optimal results. With its flexibility and multitude of options, it is easy to over or under provision the server infrastructure, resulting in poor performance and high TCO. Join us for an in depth, technical discussion with industry experts from leading Hadoop and server companies who will provide insights into the key considerations for designing and deploying an optimal Hadoop cluster.
Sponsored by AMD
Built-in forensics, incident response, and security with Red Hat Enterprise Linux 6
Every security policy provides guidance and requirements for ensuring adequate protection of information and data, as well as high-level technical and administrative security requirements for a system in a given environment. Traditionally, providing security for a system focuses on the confidentiality of the information on it. However, protecting the data integrity and system and data availability is just as important. For example, when processing United States intelligence information, there are three attributes that require protection: confidentiality, integrity, and availability.
Learn more about catching the bad guy in this free white paper.
Sponsored by DLT Solutions
Free Webinar: Hadoop
How to Build an Optimal Hadoop Cluster to Store and Maintain Unlimited Amounts of Data Using Microservers
Realizing the promise of Apache® Hadoop® requires the effective deployment of compute, memory, storage and networking to achieve optimal results. With its flexibility and multitude of options, it is easy to over or under provision the server infrastructure, resulting in poor performance and high TCO. Join us for an in depth, technical discussion with industry experts from leading Hadoop and server companies who will provide insights into the key considerations for designing and deploying an optimal Hadoop cluster.
Some of key questions to be discussed are:
- What is the “typical” Hadoop cluster and what should be installed on the different machine types?
- Why should you consider the typical workload patterns when making your hardware decisions?
- Are all microservers created equal for Hadoop deployments?
- How do I plan for expansion if I require more compute, memory, storage or networking?
| Using Salt Stack and Vagrant for Drupal Development | May 20, 2013 |
| Making Linux and Android Get Along (It's Not as Hard as It Sounds) | May 16, 2013 |
| Drupal Is a Framework: Why Everyone Needs to Understand This | May 15, 2013 |
| Home, My Backup Data Center | May 13, 2013 |
| Non-Linux FOSS: Seashore | May 10, 2013 |
| Trying to Tame the Tablet | May 08, 2013 |
- RSS Feeds
- Making Linux and Android Get Along (It's Not as Hard as It Sounds)
- Using Salt Stack and Vagrant for Drupal Development
- New Products
- Validate an E-Mail Address with PHP, the Right Way
- Drupal Is a Framework: Why Everyone Needs to Understand This
- A Topic for Discussion - Open Source Feature-Richness?
- Download the Free Red Hat White Paper "Using an Open Source Framework to Catch the Bad Guy"
- Tech Tip: Really Simple HTTP Server with Python
- New Products




2 hours 8 min ago
2 hours 37 min ago
3 hours 35 min ago
5 hours 3 min ago
6 hours 12 min ago
6 hours 58 min ago
7 hours 20 min ago
13 hours 34 min ago
19 hours 13 min ago
1 day 1 hour ago