Move configure_fticks to fticks.c.
[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 #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         fticks_hashmac(macin + 3, NULL, sizeof(macout), macout);
193         break;
194     case RSP_FTICKS_MAC_VENDOR_KEY_HASHED:
195         fticks_hashmac(macin + 3, options->fticks_key, sizeof(macout),
196                        macout);
197         break;
198     case RSP_FTICKS_MAC_FULLY_HASHED:
199         fticks_hashmac(macin, NULL, sizeof(macout), macout);
200         break;
201     case RSP_FTICKS_MAC_FULLY_KEY_HASHED:
202         fticks_hashmac(macin, options->fticks_key, sizeof(macout), macout);
203         break;
204     default:
205         debugx(2, DBG_ERR, "invalid fticks mac configuration: %d",
206                options->fticks_mac);
207     }
208     debug(0xff,
209           "F-TICKS/eduroam/1.0#REALM=%s#VISCOUNTRY=%s#%sCSI=%s#RESULT=%s#",
210           realm,
211           client->conf->fticks_viscountry,
212           visinst,
213           macout,
214           msg->code == RAD_Access_Accept ? "OK" : "FAIL");
215     if (macin != NULL)
216         free(macin);
217     if (username != NULL)
218         free(username);
219 }
220
221 /* Local Variables: */
222 /* c-file-style: "stroustrup" */
223 /* End: */