Do save vendor part of MAC for FTicksMac VendorHashed and VendorKeyHashed.
[libradsec.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 "list.h"
14 #include "radsecproxy.h"
15 #include "debug.h"
16
17 #include "fticks.h"
18
19 static void
20 format_hash(const uint8_t *hash, size_t out_len, uint8_t *out)
21 {
22     int i;
23
24     for (i = 0; i < out_len / 2; i++)
25         sprintf((char *) out + i*2, "%02x", hash[i % SHA256_DIGEST_SIZE]);
26 }
27
28 static void
29 hash(const uint8_t *in,
30      const uint8_t *key,
31      size_t out_len,
32      uint8_t *out)
33 {
34     if (key == NULL) {
35         struct sha256_ctx ctx;
36         uint8_t hash[SHA256_DIGEST_SIZE];
37
38         sha256_init(&ctx);
39         sha256_update(&ctx, strlen((char *) in), in);
40         sha256_digest(&ctx, sizeof(hash), hash);
41         format_hash(hash, out_len, out);
42     }
43     else {
44         struct hmac_sha256_ctx ctx;
45         uint8_t hash[SHA256_DIGEST_SIZE];
46
47         hmac_sha256_set_key(&ctx, strlen((char *) key), key);
48         hmac_sha256_update(&ctx, strlen((char *) in), in);
49         hmac_sha256_digest(&ctx, sizeof(hash), hash);
50         format_hash(hash, out_len, out);
51     }
52 }
53
54 int
55 fticks_configure(struct options *options,
56                  uint8_t **reportingp,
57                  uint8_t **macp,
58                  uint8_t **keyp)
59 {
60     int r = 0;
61     const char *reporting = (const char *) *reportingp;
62     const char *mac = (const char *) *macp;
63
64     if (reporting == NULL)
65         goto out;
66
67     if (strcasecmp(reporting, "None") == 0)
68         options->fticks_reporting = RSP_FTICKS_REPORTING_NONE;
69     else if (strcasecmp(reporting, "Basic") == 0)
70         options->fticks_reporting = RSP_FTICKS_REPORTING_BASIC;
71     else if (strcasecmp(reporting, "Full") == 0)
72         options->fticks_reporting = RSP_FTICKS_REPORTING_FULL;
73     else {
74         debugx(1, DBG_ERR, "config error: invalid FTicksReporting value: %s",
75                reporting);
76         r = 1;
77         goto out;
78     }
79
80     if (strcasecmp(mac, "Static") == 0)
81         options->fticks_mac = RSP_FTICKS_MAC_STATIC;
82     else if (strcasecmp(mac, "Original") == 0)
83         options->fticks_mac = RSP_FTICKS_MAC_ORIGINAL;
84     else if (strcasecmp(mac, "VendorHashed") == 0)
85         options->fticks_mac = RSP_FTICKS_MAC_VENDOR_HASHED;
86     else if (strcasecmp(mac, "VendorKeyHashed") == 0)
87         options->fticks_mac = RSP_FTICKS_MAC_VENDOR_KEY_HASHED;
88     else if (strcasecmp(mac, "FullyHashed") == 0)
89         options->fticks_mac = RSP_FTICKS_MAC_FULLY_HASHED;
90     else if (strcasecmp(mac, "FullyKeyHashed") == 0)
91         options->fticks_mac = RSP_FTICKS_MAC_FULLY_KEY_HASHED;
92     else {
93         debugx(1, DBG_ERR, "config error: invalid FTicksMAC value: %s", mac);
94         r = 1;
95         goto out;
96     }
97
98     if (*keyp == NULL
99         && (options->fticks_mac == RSP_FTICKS_MAC_VENDOR_KEY_HASHED
100             || options->fticks_mac == RSP_FTICKS_MAC_FULLY_KEY_HASHED)) {
101         debugx(1, DBG_ERR,
102                "config error: FTicksMAC %s requires an FTicksKey", mac);
103         options->fticks_mac = RSP_FTICKS_MAC_STATIC;
104         r = 1;
105         goto out;
106     }
107
108     if (*keyp != NULL)
109         options->fticks_key = *keyp;
110
111 out:
112     if (*reportingp != NULL) {
113         free(*reportingp);
114         *reportingp = NULL;
115     }
116     if (*macp != NULL) {
117         free(*macp);
118         *macp = NULL;
119     }
120     return r;
121 }
122
123 /** Hash the MAC in \a IN, keying with \a KEY if it's not NULL.
124
125     \a IN and \a KEY are NULL terminated strings.
126
127     \a IN is sanitised by lowercasing it, removing all but [0-9a-f]
128     and truncating it at first ';' (due to RADIUS praxis with tacking
129     on SSID to MAC in Calling-Station-Id).  */
130 void
131 fticks_hashmac(const uint8_t *in,
132                const uint8_t *key,
133                size_t out_len,
134                uint8_t *out)
135 {
136     /* TODO: lowercase */
137     /* TODO: s/[!0-9a-f]//1 */
138     /* TODO: truncate after first ';', if any */
139
140     hash(in, key, out_len, out);
141 }
142
143 void
144 fticks_log(const struct options *options,
145            const struct client *client,
146            const struct radmsg *msg,
147            const struct rqout *rqout)
148 {
149     unsigned char *username = NULL;
150     unsigned char *realm = NULL;
151     uint8_t visinst[8+40+1+1]; /* Room for 40 octets of VISINST.  */
152     uint8_t *macin = NULL;
153     uint8_t macout[2*32+1]; /* Room for ASCII representation of SHA256.  */
154
155     username = radattr2ascii(radmsg_gettype(rqout->rq->msg,
156                                             RAD_Attr_User_Name));
157     if (username != NULL) {
158         realm = (unsigned char *) strrchr((char *) username, '@');
159         if (realm != NULL)
160             realm++;
161         else
162             realm = (unsigned char *) "";
163     }
164
165     memset(visinst, 0, sizeof(visinst));
166     if (options->fticks_reporting == RSP_FTICKS_REPORTING_FULL)
167         snprintf((char *) visinst, sizeof(visinst), "VISINST=%s#",
168                  client->conf->name);
169
170 #define BOGUS_MAC "00:00:00:00:00:00" /* FIXME: Is there a standard
171                                        * for bogus MAC addresses?  */
172     memset(macout, 0, sizeof(macout));
173     strncpy((char *) macout, BOGUS_MAC, sizeof(macout) - 1);
174     if (options->fticks_mac != RSP_FTICKS_MAC_STATIC) {
175         macin = radattr2ascii(radmsg_gettype(rqout->rq->msg,
176                                              RAD_Attr_Calling_Station_Id));
177     }
178 #if RS_TESTING || 1
179     if (macin == NULL)
180         macin = (uint8_t *) strdup(BOGUS_MAC);
181 #endif  /* RS_TESTING */
182
183     switch (options->fticks_mac)
184     {
185     case RSP_FTICKS_MAC_STATIC:
186         memcpy(macout, BOGUS_MAC, sizeof(BOGUS_MAC));
187         break;
188     case RSP_FTICKS_MAC_ORIGINAL:
189         memcpy(macout, macin, sizeof(macout));
190         break;
191     case RSP_FTICKS_MAC_VENDOR_HASHED:
192         memcpy(macout, macin, 9);
193         fticks_hashmac(macin + 9, NULL, sizeof(macout) - 9, macout + 9);
194         break;
195     case RSP_FTICKS_MAC_VENDOR_KEY_HASHED:
196         memcpy(macout, macin, 9);
197         fticks_hashmac(macin + 9, options->fticks_key,
198                        sizeof(macout) - 9, macout + 9);
199         break;
200     case RSP_FTICKS_MAC_FULLY_HASHED:
201         fticks_hashmac(macin, NULL, sizeof(macout), macout);
202         break;
203     case RSP_FTICKS_MAC_FULLY_KEY_HASHED:
204         fticks_hashmac(macin, options->fticks_key, sizeof(macout), macout);
205         break;
206     default:
207         debugx(2, DBG_ERR, "invalid fticks mac configuration: %d",
208                options->fticks_mac);
209     }
210     debug(0xff,
211           "F-TICKS/eduroam/1.0#REALM=%s#VISCOUNTRY=%s#%sCSI=%s#RESULT=%s#",
212           realm,
213           client->conf->fticks_viscountry,
214           visinst,
215           macout,
216           msg->code == RAD_Access_Accept ? "OK" : "FAIL");
217     if (macin != NULL)
218         free(macin);
219     if (username != NULL)
220         free(username);
221 }
222
223 /* Local Variables: */
224 /* c-file-style: "stroustrup" */
225 /* End: */