Log MAC as "undisclosed" for config FTicksMac Static.
[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 "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
171     memset(macout, 0, sizeof(macout));
172     if (options->fticks_mac == RSP_FTICKS_MAC_STATIC) {
173         strncpy((char *) macout, "undisclosed", sizeof(macout) - 1);
174     }
175     else {
176         macin = radattr2ascii(radmsg_gettype(rqout->rq->msg,
177                                              RAD_Attr_Calling_Station_Id));
178         if (macin) {
179             switch (options->fticks_mac)
180             {
181             case RSP_FTICKS_MAC_ORIGINAL:
182                 memcpy(macout, macin, sizeof(macout));
183                 break;
184             case RSP_FTICKS_MAC_VENDOR_HASHED:
185                 memcpy(macout, macin, 9);
186                 fticks_hashmac(macin + 9, NULL, sizeof(macout) - 9, macout + 9);
187                 break;
188             case RSP_FTICKS_MAC_VENDOR_KEY_HASHED:
189                 memcpy(macout, macin, 9);
190                 fticks_hashmac(macin + 9, options->fticks_key,
191                                sizeof(macout) - 9, macout + 9);
192                 break;
193             case RSP_FTICKS_MAC_FULLY_HASHED:
194                 fticks_hashmac(macin, NULL, sizeof(macout), macout);
195                 break;
196             case RSP_FTICKS_MAC_FULLY_KEY_HASHED:
197                 fticks_hashmac(macin, options->fticks_key, sizeof(macout),
198                                macout);
199                 break;
200             default:
201                 debugx(2, DBG_ERR, "invalid fticks mac configuration: %d",
202                        options->fticks_mac);
203             }
204         }
205     }
206     debug(0xff,
207           "F-TICKS/eduroam/1.0#REALM=%s#VISCOUNTRY=%s#%sCSI=%s#RESULT=%s#",
208           realm,
209           client->conf->fticks_viscountry,
210           visinst,
211           macout,
212           msg->code == RAD_Access_Accept ? "OK" : "FAIL");
213     if (macin != NULL)
214         free(macin);
215     if (username != NULL)
216         free(username);
217 }
218
219 /* Local Variables: */
220 /* c-file-style: "stroustrup" */
221 /* End: */