Make radsecproxy.h self-sustaining.
[radsecproxy.git] / fticks.c
1 /* Copyright (C) 2011 NORDUnet A/S
2  * See LICENSE for information about licensing.
3  */
4
5 #include <stdio.h>              /* For sprintf().  */
6 #include <string.h>
7 #include <nettle/sha.h>
8 #include <nettle/hmac.h>
9
10 #include <regex.h>
11 #include <pthread.h>
12 #include <sys/time.h>
13 #include "radsecproxy.h"
14 #include "debug.h"
15
16 #include "fticks.h"
17
18 static void
19 _format_hash(const uint8_t *hash, size_t out_len, uint8_t *out)
20 {
21     int ir, iw;
22
23     for (ir = 0, iw = 0; iw <= out_len - 3; ir++, iw += 2)
24         sprintf((char *) out + iw, "%02x", hash[ir % SHA256_DIGEST_SIZE]);
25 }
26
27 static void
28 _hash(const uint8_t *in,
29       const uint8_t *key,
30       size_t out_len,
31       uint8_t *out)
32 {
33     if (key == NULL) {
34         struct sha256_ctx ctx;
35         uint8_t hash[SHA256_DIGEST_SIZE];
36
37         sha256_init(&ctx);
38         sha256_update(&ctx, strlen((char *) in), in);
39         sha256_digest(&ctx, sizeof(hash), hash);
40         _format_hash(hash, out_len, out);
41     }
42     else {
43         struct hmac_sha256_ctx ctx;
44         uint8_t hash[SHA256_DIGEST_SIZE];
45
46         hmac_sha256_set_key(&ctx, strlen((char *) key), key);
47         hmac_sha256_update(&ctx, strlen((char *) in), in);
48         hmac_sha256_digest(&ctx, sizeof(hash), hash);
49         _format_hash(hash, out_len, out);
50     }
51 }
52
53 int
54 fticks_configure(struct options *options,
55                  uint8_t **reportingp,
56                  uint8_t **macp,
57                  uint8_t **keyp)
58 {
59     int r = 0;
60     const char *reporting = (const char *) *reportingp;
61     const char *mac = (const char *) *macp;
62
63     if (reporting == NULL)
64         goto out;
65
66     if (strcasecmp(reporting, "None") == 0)
67         options->fticks_reporting = RSP_FTICKS_REPORTING_NONE;
68     else if (strcasecmp(reporting, "Basic") == 0)
69         options->fticks_reporting = RSP_FTICKS_REPORTING_BASIC;
70     else if (strcasecmp(reporting, "Full") == 0)
71         options->fticks_reporting = RSP_FTICKS_REPORTING_FULL;
72     else {
73         debugx(1, DBG_ERR, "config error: invalid FTicksReporting value: %s",
74                reporting);
75         r = 1;
76         goto out;
77     }
78
79     if (strcasecmp(mac, "Static") == 0)
80         options->fticks_mac = RSP_FTICKS_MAC_STATIC;
81     else if (strcasecmp(mac, "Original") == 0)
82         options->fticks_mac = RSP_FTICKS_MAC_ORIGINAL;
83     else if (strcasecmp(mac, "VendorHashed") == 0)
84         options->fticks_mac = RSP_FTICKS_MAC_VENDOR_HASHED;
85     else if (strcasecmp(mac, "VendorKeyHashed") == 0)
86         options->fticks_mac = RSP_FTICKS_MAC_VENDOR_KEY_HASHED;
87     else if (strcasecmp(mac, "FullyHashed") == 0)
88         options->fticks_mac = RSP_FTICKS_MAC_FULLY_HASHED;
89     else if (strcasecmp(mac, "FullyKeyHashed") == 0)
90         options->fticks_mac = RSP_FTICKS_MAC_FULLY_KEY_HASHED;
91     else {
92         debugx(1, DBG_ERR, "config error: invalid FTicksMAC value: %s", mac);
93         r = 1;
94         goto out;
95     }
96
97     if (*keyp == NULL
98         && (options->fticks_mac == RSP_FTICKS_MAC_VENDOR_KEY_HASHED
99             || options->fticks_mac == RSP_FTICKS_MAC_FULLY_KEY_HASHED)) {
100         debugx(1, DBG_ERR,
101                "config error: FTicksMAC %s requires an FTicksKey", mac);
102         options->fticks_mac = RSP_FTICKS_MAC_STATIC;
103         r = 1;
104         goto out;
105     }
106
107     if (*keyp != NULL)
108         options->fticks_key = *keyp;
109
110 out:
111     if (*reportingp != NULL) {
112         free(*reportingp);
113         *reportingp = NULL;
114     }
115     if (*macp != NULL) {
116         free(*macp);
117         *macp = NULL;
118     }
119     return r;
120 }
121
122 /** Hash the Ethernet MAC address in \a IN, keying a HMAC with \a KEY
123     unless \a KEY is NULL.  If \a KEY is null \a IN is hashed with an
124     ordinary cryptographic hash function such as SHA-2.
125
126     \a IN and \a KEY are NULL terminated strings.
127
128     \a IN is supposed to be an Ethernet MAC address and is sanitised
129     by lowercasing it, removing all but [0-9a-f] and truncating it at
130     the first ';' found.  The truncation is done because RADIUS
131     supposedly has a praxis of tacking on SSID to the MAC address in
132     Calling-Station-Id.  */
133 void
134 fticks_hashmac(const uint8_t *in,
135                const uint8_t *key,
136                size_t out_len,
137                uint8_t *out)
138 {
139     /* TODO: lowercase */
140     /* TODO: s/[!0-9a-f]//1 */
141     /* TODO: truncate after first ';', if any */
142
143     _hash(in, key, out_len, out);
144 }
145
146 void
147 fticks_log(const struct options *options,
148            const struct client *client,
149            const struct radmsg *msg,
150            const struct rqout *rqout)
151 {
152     uint8_t *username = NULL;
153     uint8_t *realm = NULL;
154     uint8_t visinst[8+40+1+1]; /* Room for 40 octets of VISINST.  */
155     uint8_t *macin = NULL;
156     uint8_t macout[2*32+1]; /* Room for ASCII representation of SHA256.  */
157
158     username = radattr2ascii(radmsg_gettype(rqout->rq->msg,
159                                             RAD_Attr_User_Name));
160     if (username != NULL) {
161         realm = (uint8_t *) strrchr((char *) username, '@');
162         if (realm != NULL)
163             realm++;
164     }
165     if (realm == NULL)
166         realm = (uint8_t *) "";
167
168     memset(visinst, 0, sizeof(visinst));
169     if (options->fticks_reporting == RSP_FTICKS_REPORTING_FULL) {
170         snprintf((char *) visinst, sizeof(visinst), "VISINST=%s#",
171                  client->conf->name);
172     }
173
174     memset(macout, 0, sizeof(macout));
175     if (options->fticks_mac == RSP_FTICKS_MAC_STATIC) {
176         strncpy((char *) macout, "undisclosed", sizeof(macout) - 1);
177     }
178     else {
179         macin = radattr2ascii(radmsg_gettype(rqout->rq->msg,
180                                              RAD_Attr_Calling_Station_Id));
181         if (macin) {
182             switch (options->fticks_mac)
183             {
184             case RSP_FTICKS_MAC_ORIGINAL:
185                 memcpy(macout, macin, sizeof(macout));
186                 break;
187             case RSP_FTICKS_MAC_VENDOR_HASHED:
188                 memcpy(macout, macin, 9);
189                 fticks_hashmac(macin, NULL, sizeof(macout) - 9, macout + 9);
190                 break;
191             case RSP_FTICKS_MAC_VENDOR_KEY_HASHED:
192                 memcpy(macout, macin, 9);
193                 /* We are hashing the first nine octets too for easier
194                  * correlation between vendor-key-hashed and
195                  * fully-key-hashed log records.  This opens up for a
196                  * known plaintext attack on the key but the
197                  * consequences of that is considered outweighed by
198                  * the convenience gained.  */
199                 fticks_hashmac(macin, options->fticks_key,
200                                sizeof(macout) - 9, macout + 9);
201                 break;
202             case RSP_FTICKS_MAC_FULLY_HASHED:
203                 fticks_hashmac(macin, NULL, sizeof(macout), macout);
204                 break;
205             case RSP_FTICKS_MAC_FULLY_KEY_HASHED:
206                 fticks_hashmac(macin, options->fticks_key, sizeof(macout),
207                                macout);
208                 break;
209             default:
210                 debugx(2, DBG_ERR, "invalid fticks mac configuration: %d",
211                        options->fticks_mac);
212             }
213         }
214     }
215     debug(0xff,
216           "F-TICKS/eduroam/1.0#REALM=%s#VISCOUNTRY=%s#%sCSI=%s#RESULT=%s#",
217           realm,
218           client->conf->fticks_viscountry,
219           visinst,
220           macout,
221           msg->code == RAD_Access_Accept ? "OK" : "FAIL");
222     if (macin != NULL)
223         free(macin);
224     if (username != NULL)
225         free(username);
226 }
227
228 /* Local Variables: */
229 /* c-file-style: "stroustrup" */
230 /* End: */