Computing a HMAC inside the Linux kernel.
Recently I’ve been experimenting with Hash-based Message Authentication Code (HMAC) and I got to the point where I had to implement it inside a kernel module. Not having any experience with how I could go about doing it, I went searching for the Crypto API on the Internet. Surprise surprise, there isn’t much to search on. I really can’t understand why there’s so much obfuscation when it comes to kernel APIs. Oh well…
All that was left for me to do was to start digging through the kernel source files and to try to piece the puzzle together. Without further ado, here is what I managed to create.
The hmac_sha1 function is responsible for generating the hmac (which in this case uses SHA1). It takes a given string and key (pass-phrase) as input, and it copies the resulting hexa representation inside a string.
I hope this example could come in handy for anyone looking to implement a quick HMAC generator for strings.
/*
* HMAC generator for strings.
*
* Author: andrei.sambra@telecom-sudparis.eu
* GPLv3 License applies to this code.
*
* */
#include <linux/module.h> /* Needed by all modules */
#include <linux/kernel.h> /* Needed for KERN_INFO */
#include <linux/init.h> /* Needed for the macros */
#include <linux/crypto.h>
#include <linux/err.h>
#include <linux/scatterlist.h>
#include <linux/gfp.h>
#include <crypto/hash.h>
/*
* Desired output: 694c7f0b84c0a8f0043c20aea9355a7eb51647f3
*/
static char hmac[40];
static char *key = "passphrase";
static char *mystring = "22";
struct hmac_sha1_result {
struct completion completion;
int err;
};
static void hmac_sha1_complete(struct crypto_async_request *req, int err) {
struct hmac_sha1_result *r=req->data;
if(err==-EINPROGRESS)
return;
r->err=err;
complete(&r->completion);
}
static int hmac_sha1(char *key, size_t klen, // key and key length
char *data_in, size_t dlen, // data in and length
char *hash_out, size_t outlen) { // hash buffer and length
int rc=0;
struct crypto_ahash *tfm;
struct scatterlist sg;
struct ahash_request *req;
struct hmac_sha1_result tresult;
void *hash_buf;
int len = 20;
char hash_tmp[20];
char *hash_res = hash_tmp;
/* Set hash output to 0 initially */
memset(hash_out, 0, outlen);
init_completion(&tresult.completion);
tfm=crypto_alloc_ahash("hmac(sha1)",0,0);
if(IS_ERR(tfm)) {
printk(KERN_ERR "hmac_sha1: crypto_alloc_ahash failed.\n");
rc=PTR_ERR(tfm);
goto err_tfm;
}
if(!(req=ahash_request_alloc(tfm,GFP_KERNEL))) {
printk(KERN_ERR "hmac_sha1: failed to allocate request for hmac(sha1)\n");
rc=-ENOMEM;
goto err_req;
}
if(crypto_ahash_digestsize(tfm)>len) {
printk(KERN_ERR "hmac_sha1: tfm size > result buffer.\n");
rc=-EINVAL;
goto err_req;
}
ahash_request_set_callback(req,CRYPTO_TFM_REQ_MAY_BACKLOG,
hmac_sha1_complete,&tresult);
if(!(hash_buf=kzalloc(dlen,GFP_KERNEL))) {
printk(KERN_ERR "hmac_sha1: failed to kzalloc hash_buf");
rc=-ENOMEM;
goto err_hash_buf;
}
memcpy(hash_buf,data_in,dlen);
sg_init_one(&sg,hash_buf,dlen);
crypto_ahash_clear_flags(tfm,-0);
if((rc=crypto_ahash_setkey(tfm,key,klen))){
printk(KERN_ERR "hmac_sha1: crypto_ahash_setkey failed\n");
goto err_setkey;
}
ahash_request_set_crypt(req,&sg,hash_res,dlen);
rc=crypto_ahash_digest(req);
switch(rc) {
case 0:
while (len--) {
snprintf(hash_out, outlen, "%02x", (*hash_res++ & 0x0FF));
hash_out += 2;
}
break;
case -EINPROGRESS:
case -EBUSY:
rc=wait_for_completion_interruptible(&tresult.completion);
if(!rc && !(rc=tresult.err)) {
INIT_COMPLETION(tresult.completion);
break;
} else {
printk(KERN_ERR "hmac_sha1: wait_for_completion_interruptible failed\n");
goto out;
}
default:
goto out;
}
out:
err_setkey:
kfree(hash_buf);
err_hash_buf:
ahash_request_free(req);
err_req:
crypto_free_ahash(tfm);
err_tfm:
return rc;
}
static int __init init_main(void)
{
printk(KERN_INFO "[CRYPTO] -> Successfully loaded crypto module.\n");
hmac_sha1(key, strlen(key), mystring, strlen(mystring), hmac, sizeof(hmac));
printk(KERN_INFO "FINAL MAC:%s", hmac);
return 0;
}
static void __exit cleanup_main(void)
{
#if DEBUG > 0
printk(KERN_INFO "[CRYPTO] <- Successfully unloaded crypto module.\n");
#endif
}
module_init(init_main);
module_exit(cleanup_main);
/*
* Declaring code as GPL.
*/
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Andrei Sambra - andrei.sambra@telecom-sudparis.eu"); /* Who wrote this module? */
MODULE_DESCRIPTION("SHA1-HMAC"); /* What does this module do */
This is the content of the Makefile needed to compile the module.
obj-m += crypto.o all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
To insert the module, type: sudo insmod crypto.ko
To remove the module, type: sudo rmmod crypto
Enjoy!
~Andrei
Eduroam on n900 – TELECOM SudParis
One of the WiFi networks available for students and staff at TELECOM SudParis (France) is Eduroam. Since I’m not always in the office, I need a WiFi connection for occasional email checking/sending. So, I decided to give Eduroam a go on my Nokia n900.
Before the firmware update (version 3.2010.02-8) released on Feb 16th, it was impossible to connect to this network. However, things have changed now.
Prior to today, the connection settings I was using were WPA-EAP with EAP mode: TTLS and EAP method: MSCHAPv2. The output from dmsg on the n900 showed that the authentication was always successful, but I was always getting disconnected by ‘local choice’: wlan0: deauthenticating by local choice (reason=3)
So…I decided to test other EAP methods. For TTLS, the n900 only supports 3 methods: EAP GTC, EAP MSCHAPv2 and MSCHAPv2. Since 2 out of 3 use MSCHAP, I tried EAP GTC. What do you know, it actually worked! I suspect some of the issues were partially caused by the lack of proper certificates. I fixed it by downloading the CA cert of TELECOM SudParis from here, transforming it into standard PEM format, and then importing it into the list of certificates on the n900.
Note: Unless the certificate is in PEM format and has the CA bit set, the certificate manager on the n900 will not provide you with the option to install it! The certificate you download is in standard x509 DER format. In order to export it into PEM, you need to use openssl. Here are the two commands you can use:
openssl x509 -in input.crt -out input.der -outform DER
openssl x509 -in input.der -inform DER -out output.pem -outform PEM
I hope this helps, since it took me a while to get a working connection.
Andrei~
Writing Loadable Kernel Modules using netfilter hooks (in-depth HOWTO) – Part 1
Note: This article was inspired by the lack of updated documentation on how to write proper netfilter kernel modules. At the time I’m writing this article, the latest stable release was 2.6.32.8. I am also assuming you are familiar with how LKMs (Loadable Kernel Modules) work. If you are not, then you might want to check this article first: tldp.org/HOWTO/Module-HOWTO/
In this article (Part 1) I will present how to create a simple Linux kernel module that implements a netfilter hook for a generic transport protocol (not one of the usual ones).
In Part 2, I plan to connect the module to the iptables rules generated on the userspace side.
What is netfilter?
For those of you who are not familiar with netfilter, all I can say is that it is actually a framework for packet mangling, outside the normal Berkeley socket interface. It’s the engine behind iptables – the popular firewall solution for Linux. It has four parts. Firstly, each protocol defines “hooks” (IPv4 defines 5) which are well-defined points in a packet’s traversal of that protocol stack. At each of these points, the protocol will call the netfilter framework with the packet and the hook number.
Secondly, parts of the kernel can register to listen to the different hooks for each protocol. So when a packet is passed to the netfilter framework, it checks to see if anyone has registered for that protocol and hook; if so, they each get a chance to examine (and possibly alter) the packet in order, then discard the packet (NF_DROP), allow it to pass (NF_ACCEPT), tell netfilter to forget about the packet (NF_STOLEN), or ask netfilter to queue the packet for userspace (NF_QUEUE).
The third part is that packets that have been queued are collected (by the ip_queue driver) for sending to userspace; these packets are handled asynchronously.
Netfilter Hooks in the Linux Kernel.
Netfilter modules can be loaded into the Linux kernel at runtime, so we need hooks in the actual routing code to enable dynamic hooking of functions. An integer identifier is allocated to each of these netfilter hooks. The identifiers of all hooks for each supported protocol are defined in the protocol-specific header file (<linux/netfilter_ipv4.h> or <linux/netfilter_ipv6.h>). The following five hooks are defined for IP Version 4 in <linux/netfilter_ipv4.h>:
- NF_IP_PRE_ROUTING (default value is 0): incoming packets pass this hook in the ip_rcv() (linux/net/ipv4/ip_input.c) function before they are processed by the routing code
- NF_IP_LOCAL_IN (default value is 1): all incoming packets addressed to the local computer pass this hook in the function ip_local_deliver()
- NF_IP_FORWARD (default value is 2): all incoming packets not addressed to the local computer pass this hook in the function ip_forward()
- NF_IP_LOCAL_OUT (default value is 3): all outgoing packets created in the local computer pass this hook in the function ip_build_and_send_pkt()
- NF_IP_POST_ROUTING (default value is 4): this hook in the ip_finish_output() function represents the last chance to access all outgoing (forwarded or locally created) packets before they leave the computer over a network device
Calling the NF_HOOK macro causes the routing code to process the filter functions hooked into a netfilter hook. More specifically, the NF_HOOK macro has the following arguments:
- pf (protocol family): This is the identifier of the protocol family: PF_INET for IP Version 4, PF_INET6 for IP Version 6.
- hook: This is the hook identifier. All valid identifiers for each protocol family are defined in a header file (e.g., <linux/netfilter_ipv4.h>).
- skb: This is a pointer to the sk_buff structure with the packet to be handled.
- indev (input device): This is a pointer to the net_device structure of the network device that received the packet. It is set to NULL in the above example, because the packet is an outgoing packet.
- outdev (output device): This is a pointer to the net_device structure of the network device that should be used by the packet to leave the local computer. In the above example, the device used has to be determined first by use of the routing table (rt).
- okfn() (okay function): This function is invoked when all filter functions registered with this hook returned NF_ACCEPT, thereby okaying the packet’s transit.
Registering and Unregistering Packet-Filter Functions.
The packet-filter functions that are actually hooked into the netfilter hooks are so-called hook functions of the type nf_hookfn. The signature of a hook function is defined in <linux/netfilter.h> as follows:
typedef unsigned int nf_hookfn(unsigned int hooknum,
struct sk_buff **skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn) (struct sk_buff *));
The return value of a packet-filter function specifies what should happen to the packet. It is of the type unsigned int and can take any of the following values, defined in <linux/netfilter.h>:
- NF_DROP (default value is 0): The active rules list processing is stopped, and the packet is dropped
- NF_ACCEPT (default value is 1): The packet is passed to the next packet filter function in the rules list. Once the end of the list has been reached, the packet is released by okfn() for further processing
- NF_STOLEN (default value is 2): The packet filter function withholds the packet for further processing, so that the active rules list processing is stopped. In contrast to NF_DROP, however, the packet does not have to be explicitly dropped
- NF_QUEUE (default value is 3): The function nf_queue() (net/core/netfilter.c) puts the packet in a queue from which it can be removed and processed (e.g., by a user space program). Subsequently, nf_reinject() has to be invoked to return the packet to the Linux kernel for further processing by netfilter
- NF_REPEAT (default value is 4): In contrast to NF_ACCEPT, rather than a continuation of processing at the next packet-filter function, the current filter function is invoked again
nf_register_hook(), nf_unregister_hook() registers and unregisters a packet-filter function with the Linux kernel. The parameter passed is a nf_hook_ops structure, which includes all information required.
To register a new packet-filter function with the Linux kernel, we first have to initialize a structure of the type nf_hook_ops (linux/netfilter.h) with all of the management information required:
struct nf_hook_ops
{
struct list_head list;
/* User fills in from here down. */
nf_hookfn *hook;
int pf;
int hooknum;
/* Hooks are ordered in ascending priority. */
int priority;
};
The fields of this structure have the following meaning:
- list: the nf_hook_ops structures are maintained in a linked list within the Linux kernel
- hook(): this is a pointer to the actual packet-filter function of the type nf_hookfn
- pf: the protocol family identifier (e.g., PF_INET or PF_INET6)
- hooknum: the hook identifier (e.g., NF_IP_INPUT) are used to determine the hook for this packet-filter function
- priority: packet-filter functions within the rules list of a hook are sorted by the priority field in ascending order, so that they will be invoked in this order when a packet transits
Now that you’ve got the main idea on how netfilter hooks work, I suggest we check an example. The following code belongs to a personal project involving the design of a little protocol. The source code for the .c file can be found here, while the code for the .h file is here.
The basic idea behind this generic protocol is that it uses a header made of a port number and a type of message, both defined in the header file. My module is supposed to identify the protocol (based on the protocol field of the IP header) and process only packets arriving for this protocol. Once the match is made, the next thing to do is to recover the port number and type of message from our protocol’s header. Once we have all data, we can call our callback function to do whatever we want next.
There have been a couple of important changes since 2.6.22 (which most guides are written for):
- the params of the hook function have changed slightly: struct sk_buff *skb, instead of struct sk_buff **skb
- skb_network_header(const struct sk_buff *skb), skb_transport_header(const struct sk_buff *skb) and skb_mac_header(const struct sk_buff *skb)accessors – instead of nh, h and mac header fields of the sk_buff structure (/include/linux/skbuff.h)
One more important aspect you need to take into consideration is that both skb_network_header and skb_transport_header point to the same memory address. This means that if you want to access the transport header information, you will need to add (skip) to the location of the header in the memory by adding the IP header length (e.g.. rpmp_header = (struct rpmphdr *)(skb_transport_header(sock_buff)+sizeof(struct iphdr));). I don’t know why this happens, but it happens.
Anyway, here is the code:
Source code for my_module.h:
#include <linux/types.h>
#define DRIVER_AUTHOR "Andrei SAMBRA "
#define DRIVER_DESC "Remote Port Management Protocol"
#define IPPROTO_RPMP 150
struct rpmphdr {
__be16 dport;
__u16 type;
};
Source code for my_module.c:
/*
* Author: andrei.sambra@telecom-sudparis.eu
* GPLv3 License applies to this code.
*
* */
#include <linux/module.h> /* Needed by all modules */
#include <linux/kernel.h> /* Needed for KERN_INFO */
#include <linux/init.h> /* Needed for the macros */
#include <linux/skbuff.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/ip.h>
#include <linux/udp.h>
#include "rpmp.h"
#define DEBUG 0
struct sk_buff *sock_buff;
struct iphdr *ip_header;
struct udphdr *udp_header;
struct rpmphdr *rpmp_header;
static struct nf_hook_ops nfho;
static unsigned int hook_func(unsigned int hooknum,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
sock_buff = skb;
if (!sock_buff) {
return NF_ACCEPT;
} else {
ip_header = (struct iphdr *)skb_network_header(sock_buff);
if (!ip_header) {
return NF_ACCEPT;
} else {
if (ip_header->protocol == IPPROTO_RPMP) {
rpmp_header = (struct rpmphdr *)(skb_transport_header(sock_buff)+sizeof(struct iphdr));
#if DEBUG > 0
printk(KERN_INFO "[RPMP] DEBUG: th: 0p%p\n", rpmp_header);
printk(KERN_INFO "[RPMP] DEBUG: nh: 0p%p\n", skb_network_header(sock_buff));
printk(KERN_INFO "[RPMP] DEBUG: mh: 0p%p\n", skb_mac_header(sock_buff));
printk(KERN_INFO "[RPMP] DEBUG: Length: rpmp_header=%d | dport=%d | type=%d.\n",
sizeof(rpmp_header),
sizeof(rpmp_header->dport),
sizeof(rpmp_header->type));
printk(KERN_INFO "[RPMP] DEBUG: From IP address: %d.%d.%d.%dn",
ip_header-saddr & 0x000000FF,
(ip_header->saddr & 0x0000FF00) >> 8,
(ip_header->saddr & 0x00FF0000) >> 16,
(ip_header->saddr & 0xFF000000) >> 24);
#endif
printk(KERN_INFO "[RPMP] Got a RPMP packet for port=%d (type:%d).\n",
ntohs(rpmp_header->dport),
ntohs(rpmp_header->type));
/* Callback function here*/
return NF_DROP;
} else {
return NF_ACCEPT;
}
}
}
}
/*
static int process_pkt()
{
#if DEBUG > 0
printk(KERN_INFO "[RPMP] DEBUG: Inside the callback!\n");
#endif
return 0;
}
*/
static int __init init_main(void)
{
nfho.hook = hook_func;
nfho.hooknum = 1;
nfho.pf = PF_INET;
nfho.priority = NF_IP_PRI_FIRST;
nf_register_hook(&nfho);
#if DEBUG > 0
printk(KERN_INFO "[RPMP] Successfully inserted protocol module into kernel.\n");
#endif
return 0;
}
static void __exit cleanup_main(void)
{
nf_unregister_hook(&nfho);
#if DEBUG > 0
printk(KERN_INFO "[RPMP] Successfully unloaded protocol module.\n");
#endif
}
module_init(init_main);
module_exit(cleanup_main);
/*
* Declaring code as GPL.
*/
MODULE_LICENSE("GPLv3");
MODULE_AUTHOR(DRIVER_AUTHOR); /* Who wrote this module? */
MODULE_DESCRIPTION(DRIVER_DESC); /* What does this module do */
Google Wave
Google Wave is finally out! I was lucky enough to receive an invite from day 1 so for the past days I’ve been taking it for a ride. What I can say so far is that this application shows amazing potential!
As you log in for the first time, you start with a pretty clean interface. You get a list containing whichever friends from your contacts that have joined Google Wave (probably not many for the time being). You also get a list with a few gadgets that can be used right away in your waves. While they are not many, at least it’s a start. They include a map gadget, a poll, a trip organiser, a weather gadget and some kind of video conferencing for your waves. You can also share pictures and other resources during a wave.
On the other hand, the user can improve the functionality of Google Wave by adding “robots”. These robots act as additional users which can invited to a wave, where they perform several actions. To be able to use these robots, the user just needs to add them as if they are adding a contact. Note that these robots are 3rd party applications, based on the Google Wave API. Here is a link to a sample gallery of robots.
So far I’ve tested only a handful of these robots and some of them have proven to be particularly useful. Here is a short list:
- Tweety -it fetches tweets from your home page (based on your username and password). You should probably keep it in a separate wave.
- XMPP – It can send wave messages to your favourite IM client. The best thing about this robot is that you don’t need to be logged into Google Wave to receive updates!
- CodeBot – code formatting. You paste code and it gets properly formatted.
- buglinky – automatically links “issue #NN” to a bug tracker of your choice, and replaces raw bug URLs with the equivalent text. All the annotations and replacements happen while typing.
- Bloggy – posts your wave on Blogspot. You can eve see replies from Blogspot in your Google Wave interface.
As you can see, Google Wave can be customised to offer you exactly the functionality you desire. I am really looking forward to see what else people can do with Google Wave!
~Andrei
Installing GeForce GT220 drivers under Linux
I’ve recently upgraded my old desktop. For this occasion I decided to go for an Nvidia video card (the GeForce GT220) since it is better supported than ATI cards; and while at it, I also decided to go for a clean install of Ubuntu 9.04 (64-bit, of course). Everything went smoothly. The install time took less than 15 minutes and I was really pleased to see how well the new machine performs.
However, I noticed that large windows lagged when I dragged them around, and the general visual experience on my 25.5″ monitor was far from good. As I suspected, there was no video acceleration enabled. Easy, all I have to do is just to install the Nvidia drivers, right? Wrong.
Searching for the correct drivers on the ‘Download’ section of the Nvidia website returned no results. This was weird, since I knew the card has been out for a while. They couldn’t even provide test/beta drivers. Maybe they didn’t update the search database for this model yet? I don’t know. What I do know is that I went and looked on their FTP server and found something! Apparently the drivers do exist!
Here is the link to the latest Linux drivers for the GeForce GT220 (64-bit): ftp://download.nvidia.com/XFree86/Linux-x86_64/190.42/
Next I downloaded all the packages and proceeded to install them. This is the order in which you should proceed for a painless and correct installation process:
- Make sure you have updated to the latest stable release of the Linux kernel.
- Download the driver packages from the link provided above.
- Terminate X server, by following these steps:
- press ALT+CTRL+F2(or 3, or 4…)
- login with your username/password
- stop the X server and GDM by typing this command: `
sudo /etc/init.d/gdm stop`.
- Install the drivers by going to the directory where you downloaded the files to and executing the 1st package: `
sudo sh NVIDIA-Linux-x86_64-190.18-pkg0.run`. Just ‘accept’ the proposed configuration and click on ‘OK’. Hopefully you should not get any errors. - Once the installation has successfully completed, it’s time to restart the X server / GDM: `
sudo /etc/init.d/gdm start`. - The welcome screen should be up and running now and you can login.
This is it! It’s that simple!
I decided to post this since there was no guide for the GeForce GT220 install under Linux. I believe you can safely use this method to install any GT200 series. Please let me know if this guide helped you at all.
Andrei~
Bad Free.fr!
When testing a little program that I’ve made, called ptool (more info here), I have found out that the LAN interface of my ADSL router was operating in promiscuous mode. The router is a standard issue from Free.fr.
I have no reason why this would happen, especially on the LAN port. If this is common practice from Free, then I might even be forced to change ISP. I’m curious how many other providers snoop on their users’ LANs.
If you’re curious whether or not your ISP is listening to your LAN traffic, you could try downloading and running the program that I’ve written. I think it currently supports most linux distributions and might work on Solaris and Mac OS too (not sure if that’s the case for the latest build though).
Please let me know what you find. I really want to get to the bottom of this!
Andrei~
Triplifying Bugzilla
I have recently joined the Computer Science department of TELECOM SudParis as an intern. My job is to integrate Triplify and Bugzilla in order to create semantic data. In other words, I have to export data from the Bugzilla database to an RDF file. All this is possible due to Triplify, a php script that can be configured to query given tables from the specified database.
The main part is the definition of a number of SQL queries selecting information that is meant for public use. In order for Triplify to be able to convert the results of your SQL queries into RDF, the query results are required to have a certain structure.
This is the classmap for the database tables queried by Triplify (class->table):
-
bom:Issue->bugs -
bom:product-> products -
bom:component-> components -
bom:milestone-> milestones -
bom:computersystem-> op_sys + rep_platform -
sioc:User-> profiles -
foaf:Person-> profiles
The main vocabulary used for the Bugzilla triplification is EvoOntBom, with occasional Bugzilla specific classes found in heliosBtOntology.
If you want to see the RDF+n3 output, please click here.
If you find this stuff interesting, don’t hesitate to contact me for more info.
Andrei~
Welcome!
Welcome to my den!
I am starting this personal blog so I can keep track of the recent and future events from my personal life. Please enjoy yourselves and have a great time!
Andrei~
