Fixed debug message. Noted by Dave Anderson.
[freeradius.git] / src / modules / rlm_wimax / rlm_wimax.c
1 /*
2  * rlm_wimax.c
3  *
4  * Version:     $Id$
5  *
6  * Copyright (C) 2008 Alan DeKok <aland@networkradius.com>
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
9  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
10  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
11  * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
12  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
13  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
14  * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
15  * SOFTWARE.
16  */
17
18 #include <freeradius-devel/ident.h>
19 RCSID("$Id$")
20
21 #include <freeradius-devel/radiusd.h>
22 #include <freeradius-devel/modules.h>
23
24 /*
25  *      FIXME: Add check for this header to configure.in
26  */
27 #include <openssl/hmac.h>
28
29 /*
30  *      FIXME: Fix the build system to create definitions from names.
31  */
32 #define WIMAX2ATTR(x) ((24757 << 16) | (x))
33
34 /*
35  *      Find the named user in this modules database.  Create the set
36  *      of attribute-value pairs to check and reply with for this user
37  *      from the database. The authentication code only needs to check
38  *      the password, the rest is done here.
39  */
40 static int wimax_authorize(void *instance, REQUEST *request)
41 {
42         VALUE_PAIR *vp;
43
44         /* quiet the compiler */
45         instance = instance;
46         request = request;
47
48         /*
49          *      Fix Calling-Station-Id.  Damn you, WiMAX!
50          */
51         vp =  pairfind(request->packet->vps, PW_CALLING_STATION_ID);
52         if (vp && (vp->length == 6)) {
53                 int i;
54                 uint8_t buffer[6];
55
56                 memcpy(buffer, vp->vp_octets, 6);
57
58                 /*
59                  *      RFC 3580 Section 3.20 says this is the preferred
60                  *      format.  Everyone *SANE* is using this format,
61                  *      so we fix it here.
62                  */
63                 for (i = 0; i < 6; i++) {
64                         fr_bin2hex(&buffer[i], &vp->vp_strvalue[i * 3], 1);
65                         vp->vp_strvalue[(i * 3) + 2] = '-';
66                 }
67
68                 vp->vp_strvalue[(5*3)+2] = '\0';
69                 vp->length = (5*3)+2;
70
71                 DEBUG2("rlm_wimax: Fixing WiMAX binary Calling-Station-Id to %s",
72                        vp->vp_strvalue);
73         }
74
75         return RLM_MODULE_OK;
76 }
77
78
79 /*
80  *      Massage the request before recording it or proxying it
81  */
82 static int wimax_preacct(void *instance, REQUEST *request)
83 {
84         return wimax_authorize(instance, request);
85 }
86
87 /*
88  *      Write accounting information to this modules database.
89  */
90 static int wimax_accounting(void *instance, REQUEST *request)
91 {
92         /* quiet the compiler */
93         instance = instance;
94         request = request;
95
96         return RLM_MODULE_OK;
97 }
98
99 /*
100  *      Generate the keys after the user has been authenticated.
101  */
102 static int wimax_postauth(UNUSED void *instance, REQUEST *request)
103 {
104         VALUE_PAIR *msk, *emsk, *vp;
105         VALUE_PAIR *mn_nai, *ip, *fa_rk;
106         HMAC_CTX hmac;
107         unsigned int rk1_len, rk2_len, rk_len;
108         int rk_lifetime = 3600; /* ? */
109         uint32_t mip_spi;
110         uint8_t usage_data[24];
111         uint8_t mip_rk_1[EVP_MAX_MD_SIZE], mip_rk_2[EVP_MAX_MD_SIZE];
112         uint8_t mip_rk[2 * EVP_MAX_MD_SIZE];
113
114         msk = pairfind(request->reply->vps, 1129);
115         emsk = pairfind(request->reply->vps, 1130);
116         if (!msk || !emsk) {
117                 RDEBUG("No EAP-MSK or EAP-EMSK.  Cannot create WiMAX keys.");
118                 return RLM_MODULE_NOOP;
119         }
120
121         /*
122          *      Initialize usage data.
123          */
124         memcpy(usage_data, "miprk@wimaxforum.org", 21); /* with trailing \0 */
125         usage_data[21] = 0x02;
126         usage_data[22] = 0x00;
127         usage_data[23] = 0x01;
128
129         /*
130          *      MIP-RK-1 = HMAC-SSHA256(EMSK, usage-data | 0x01)
131          */
132         HMAC_CTX_init(&hmac);
133         HMAC_Init_ex(&hmac, emsk->vp_octets, emsk->length, EVP_sha256(), NULL);
134         
135         HMAC_Update(&hmac, &usage_data[0], sizeof(usage_data));
136         HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len);
137
138         /*
139          *      MIP-RK-2 = HMAC-SSHA256(EMSK, MIP-RK-1 | usage-data | 0x01)
140          */
141         HMAC_CTX_init(&hmac);
142         HMAC_Init_ex(&hmac, emsk->vp_octets, emsk->length, EVP_sha256(), NULL);
143         
144         HMAC_Update(&hmac, (const uint8_t *) &mip_rk_1, rk1_len);
145         HMAC_Update(&hmac, &usage_data[0], sizeof(usage_data));
146         HMAC_Final(&hmac, &mip_rk_2[0], &rk2_len);
147
148         vp = pairfind(request->reply->vps, PW_SESSION_TIMEOUT);
149         if (vp) rk_lifetime = vp->vp_integer;
150
151         memcpy(mip_rk, mip_rk_1, rk1_len);
152         memcpy(mip_rk + rk1_len, mip_rk_2, rk2_len);
153         rk_len = rk1_len + rk2_len;
154
155         /*
156          *      MIP-SPI = HMAC-SSHA256(MIP-RK, "SPI CMIP PMIP");
157          */
158         HMAC_CTX_init(&hmac);
159         HMAC_Init_ex(&hmac, mip_rk, rk_len, EVP_sha256(), NULL);
160         
161         HMAC_Update(&hmac, (const uint8_t *) "SPI CMIP PMIP", 12);
162         HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len);
163
164         /*
165          *      Take the 4 most significant octets.
166          *      If less than 256, add 256.
167          */
168         mip_spi = ((mip_rk_1[0] << 24) | (mip_rk_1[1] << 16) |
169                    (mip_rk_1[2] << 8) | mip_rk_1[3]);
170         if (mip_spi < 256) mip_spi += 256;
171
172         if (debug_flag) {
173                 int len = rk_len;
174                 char buffer[512];
175
176                 if (len > 128) len = 128; /* buffer size */
177
178                 fr_bin2hex(mip_rk, buffer, len);
179                 radlog_request(L_DBG, 0, request, "MIP-RK = 0x%s", buffer);
180                 radlog_request(L_DBG, 0, request, "MIP-SPI = %08x",
181                                ntohl(mip_spi));
182         }
183
184         /*
185          *      FIXME: Perform SPI collision prevention
186          */
187
188         /*
189          *      Calculate mobility keys
190          */
191         mn_nai = pairfind(request->packet->vps, 1900);
192         if (!mn_nai) mn_nai = pairfind(request->reply->vps, 1900);
193         if (!mn_nai) {
194                 RDEBUG("WARNING: WiMAX-MN-NAI was not found in the request or in the reply.");
195                 RDEBUG("WARNING: We cannot calculate MN-HA keys.");
196         }
197
198         /*
199          *      WiMAX-IP-Technology
200          */
201         vp = NULL;
202         if (mn_nai) vp = pairfind(request->reply->vps, WIMAX2ATTR(23));
203         if (!vp) {
204                 RDEBUG("WARNING: WiMAX-IP-Technology not found in reply.");
205                 RDEBUG("WARNING: Not calculating MN-HA keys");
206         }
207
208         if (vp) switch (vp->vp_integer) {
209         case 2:                 /* PMIP4 */
210                 /*
211                  *      Look for WiMAX-hHA-IP-MIP4
212                  */
213                 ip = pairfind(request->reply->vps, WIMAX2ATTR(6));
214                 if (!ip) {
215                         RDEBUG("WARNING: WiMAX-hHA-IP-MIP4 not found.  Cannot calculate MN-HA-PMIP4 key");
216                         break;
217                 }
218
219                 /*
220                  *      MN-HA-PMIP4 =
221                  *         H(MIP-RK, "PMIP4 MN HA" | HA-IPv4 | MN-NAI);
222                  */
223                 HMAC_CTX_init(&hmac);
224                 HMAC_Init_ex(&hmac, mip_rk, rk_len, EVP_sha1(), NULL);
225
226                 HMAC_Update(&hmac, (const uint8_t *) "PMIP4 MN HA", 11);
227                 HMAC_Update(&hmac, (const uint8_t *) &ip->vp_ipaddr, 4);
228                 HMAC_Update(&hmac, (const uint8_t *) &mn_nai->vp_strvalue, mn_nai->length);
229                 HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len);
230
231                 /*
232                  *      Put MN-HA-PMIP4 into WiMAX-MN-hHA-MIP4-Key
233                  */
234                 vp = pairfind(request->reply->vps, WIMAX2ATTR(10));
235                 if (!vp) {
236                         vp = radius_paircreate(request, &request->reply->vps,
237                                                WIMAX2ATTR(10), PW_TYPE_OCTETS);
238                 }
239                 if (!vp) {
240                         RDEBUG("WARNING: Failed creating WiMAX-MN-hHA-MIP4-Key");
241                         break;
242                 }
243                 memcpy(vp->vp_octets, &mip_rk_1[0], rk1_len);
244                 vp->length = rk1_len;
245
246                 /*
247                  *      Put MN-HA-PMIP4-SPI into WiMAX-MN-hHA-MIP4-SPI
248                  */
249                 vp = pairfind(request->reply->vps, WIMAX2ATTR(11));
250                 if (!vp) {
251                         vp = radius_paircreate(request, &request->reply->vps,
252                                                WIMAX2ATTR(11), PW_TYPE_INTEGER);
253                 }
254                 if (!vp) {
255                         RDEBUG("WARNING: Failed creating WiMAX-MN-hHA-MIP4-SPI");
256                         break;
257                 }
258                 vp->vp_integer = mip_spi + 1;
259                 break;
260
261         case 3:                 /* CMIP4 */
262                 /*
263                  *      Look for WiMAX-hHA-IP-MIP4
264                  */
265                 ip = pairfind(request->reply->vps, WIMAX2ATTR(6));
266                 if (!ip) {
267                         RDEBUG("WARNING: WiMAX-hHA-IP-MIP4 not found.  Cannot calculate MN-HA-CMIP4 key");
268                         break;
269                 }
270
271                 /*
272                  *      MN-HA-CMIP4 =
273                  *         H(MIP-RK, "CMIP4 MN HA" | HA-IPv4 | MN-NAI);
274                  */
275                 HMAC_CTX_init(&hmac);
276                 HMAC_Init_ex(&hmac, mip_rk, rk_len, EVP_sha1(), NULL);
277
278                 HMAC_Update(&hmac, (const uint8_t *) "CMIP4 MN HA", 11);
279                 HMAC_Update(&hmac, (const uint8_t *) &ip->vp_ipaddr, 4);
280                 HMAC_Update(&hmac, (const uint8_t *) &mn_nai->vp_strvalue, mn_nai->length);
281                 HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len);
282
283                 /*
284                  *      Put MN-HA-CMIP4 into WiMAX-MN-hHA-MIP4-Key
285                  */
286                 vp = pairfind(request->reply->vps, WIMAX2ATTR(10));
287                 if (!vp) {
288                         vp = radius_paircreate(request, &request->reply->vps,
289                                                WIMAX2ATTR(10), PW_TYPE_OCTETS);
290                 }
291                 if (!vp) {
292                         RDEBUG("WARNING: Failed creating WiMAX-MN-hHA-MIP4-Key");
293                         break;
294                 }
295                 memcpy(vp->vp_octets, &mip_rk_1[0], rk1_len);
296                 vp->length = rk1_len;
297
298                 /*
299                  *      Put MN-HA-CMIP4-SPI into WiMAX-MN-hHA-MIP4-SPI
300                  */
301                 vp = pairfind(request->reply->vps, WIMAX2ATTR(11));
302                 if (!vp) {
303                         vp = radius_paircreate(request, &request->reply->vps,
304                                                WIMAX2ATTR(11), PW_TYPE_INTEGER);
305                 }
306                 if (!vp) {
307                         RDEBUG("WARNING: Failed creating WiMAX-MN-hHA-MIP4-SPI");
308                         break;
309                 }
310                 vp->vp_integer = mip_spi;
311                 break;
312
313         case 4:                 /* CMIP6 */
314                 /*
315                  *      Look for WiMAX-hHA-IP-MIP6
316                  */
317                 ip = pairfind(request->reply->vps, WIMAX2ATTR(7));
318                 if (!ip) {
319                         RDEBUG("WARNING: WiMAX-hHA-IP-MIP6 not found.  Cannot calculate MN-HA-CMIP6 key");
320                         break;
321                 }
322
323                 /*
324                  *      MN-HA-CMIP6 =
325                  *         H(MIP-RK, "CMIP6 MN HA" | HA-IPv6 | MN-NAI);
326                  */
327                 HMAC_CTX_init(&hmac);
328                 HMAC_Init_ex(&hmac, mip_rk, rk_len, EVP_sha1(), NULL);
329
330                 HMAC_Update(&hmac, (const uint8_t *) "CMIP6 MN HA", 11);
331                 HMAC_Update(&hmac, (const uint8_t *) &ip->vp_ipv6addr, 16);
332                 HMAC_Update(&hmac, (const uint8_t *) &mn_nai->vp_strvalue, mn_nai->length);
333                 HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len);
334
335                 /*
336                  *      Put MN-HA-CMIP6 into WiMAX-MN-hHA-MIP6-Key
337                  */
338                 vp = pairfind(request->reply->vps, WIMAX2ATTR(12));
339                 if (!vp) {
340                         vp = radius_paircreate(request, &request->reply->vps,
341                                                WIMAX2ATTR(12), PW_TYPE_OCTETS);
342                 }
343                 if (!vp) {
344                         RDEBUG("WARNING: Failed creating WiMAX-MN-hHA-MIP6-Key");
345                         break;
346                 }
347                 memcpy(vp->vp_octets, &mip_rk_1[0], rk1_len);
348                 vp->length = rk1_len;
349
350                 /*
351                  *      Put MN-HA-CMIP6-SPI into WiMAX-MN-hHA-MIP6-SPI
352                  */
353                 vp = pairfind(request->reply->vps, WIMAX2ATTR(13));
354                 if (!vp) {
355                         vp = radius_paircreate(request, &request->reply->vps,
356                                                WIMAX2ATTR(13), PW_TYPE_INTEGER);
357                 }
358                 if (!vp) {
359                         RDEBUG("WARNING: Failed creating WiMAX-MN-hHA-MIP6-SPI");
360                         break;
361                 }
362                 vp->vp_integer = mip_spi + 2;
363                 break;
364
365         default:
366                 break;          /* do nothing */
367         }
368
369         /*
370          *      Generate FA-RK, if requested.
371          *
372          *      FA-RK= H(MIP-RK, "FA-RK")
373          */
374         fa_rk = pairfind(request->reply->vps, WIMAX2ATTR(14));
375         if (fa_rk && (fa_rk->length == 0)) {
376                 HMAC_CTX_init(&hmac);
377                 HMAC_Init_ex(&hmac, mip_rk, rk_len, EVP_sha1(), NULL);
378                 
379                 HMAC_Update(&hmac, (const uint8_t *) "FA-RK", 5);
380
381                 HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len);
382
383                 memcpy(fa_rk->vp_octets, &mip_rk_1[0], rk1_len);
384                 fa_rk->length = rk1_len;
385         }
386
387         /*
388          *      Create FA-RK-SPI, which is really SPI-CMIP4, which is
389          *      really MIP-SPI.  Clear?  Of course.  This is WiMAX.
390          */
391         if (fa_rk) {
392                 vp = pairfind(request->reply->vps, WIMAX2ATTR(61));
393                 if (!vp) {
394                         vp = radius_paircreate(request, &request->reply->vps,
395                                                WIMAX2ATTR(61), PW_TYPE_INTEGER);
396                 }
397                 if (!vp) {
398                         RDEBUG("WARNING: Failed creating WiMAX-FA-RK-SPI");
399                 } else {
400                         vp->vp_integer = mip_spi;
401                 }
402         }
403
404         /*
405          *      Generate MN-FA = H(FA-RK, "MN FA" | FA-IP | MN-NAI)
406          */
407         ip = pairfind(request->reply->vps, 1901);
408         if (fa_rk && ip && mn_nai) {
409                 HMAC_CTX_init(&hmac);
410                 HMAC_Init_ex(&hmac, fa_rk->vp_octets, fa_rk->length,
411                              EVP_sha1(), NULL);
412                 
413                 HMAC_Update(&hmac, (const uint8_t *) "MN FA", 5);
414                 HMAC_Update(&hmac, (const uint8_t *) &ip->vp_ipaddr, 4);
415                 HMAC_Update(&hmac, (const uint8_t *) &mn_nai->vp_strvalue, mn_nai->length);
416
417                 HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len);
418
419                 vp = radius_paircreate(request, &request->reply->vps,
420                                        1902, PW_TYPE_OCTETS);
421                 if (!vp) {
422                         RDEBUG("WARNING: Failed creating WiMAX-MN-FA");
423                 } else {
424                         memcpy(vp->vp_octets, &mip_rk_1[0], rk1_len);
425                         vp->length = rk1_len;
426                 }
427         }
428
429         /*
430          *      Give additional information about requests && responses
431          *
432          *      WiMAX-RRQ-MN-HA-SPI
433          */
434         vp = pairfind(request->packet->vps, WIMAX2ATTR(20));
435         if (vp) {
436                 RDEBUG("Client requested MN-HA key: Should use SPI to look up key from storage.");
437                 if (!mn_nai) {
438                         RDEBUG("WARNING: MN-NAI was not found!");
439                 }
440
441                 /*
442                  *      WiMAX-RRQ-HA-IP
443                  */
444                 if (!pairfind(request->packet->vps, WIMAX2ATTR(18))) {
445                         RDEBUG("WARNING: HA-IP was not found!");
446                 }
447
448
449                 /*
450                  *      WiMAX-HA-RK-Key-Requested
451                  */
452                 vp = pairfind(request->packet->vps, WIMAX2ATTR(58));
453                 if (vp && (vp->vp_integer == 1)) {
454                         RDEBUG("Client requested HA-RK: Should use IP to look it up from storage.");
455                 }
456         }
457
458         /*
459          *      Wipe the context of all sensitive information.
460          */
461         HMAC_CTX_cleanup(&hmac);
462
463         return RLM_MODULE_UPDATED;
464 }
465
466
467 /*
468  *      The module name should be the only globally exported symbol.
469  *      That is, everything else should be 'static'.
470  *
471  *      If the module needs to temporarily modify it's instantiation
472  *      data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
473  *      The server will then take care of ensuring that the module
474  *      is single-threaded.
475  */
476 module_t rlm_wimax = {
477         RLM_MODULE_INIT,
478         "wimax",
479         RLM_TYPE_THREAD_SAFE,           /* type */
480         NULL,                           /* instantiation */
481         NULL,                           /* detach */
482         {
483                 NULL,                   /* authentication */
484                 wimax_authorize,        /* authorization */
485                 wimax_preacct,          /* preaccounting */
486                 wimax_accounting,       /* accounting */
487                 NULL,                   /* checksimul */
488                 NULL,                   /* pre-proxy */
489                 NULL,                   /* post-proxy */
490                 wimax_postauth          /* post-auth */
491         },
492 };