d88d06d155ab796efffa52e9f7a55cb53c9e5bc1
[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 typedef struct rlm_wimax_t {
33         int     delete_mppe_keys;
34 } rlm_wimax_t;
35
36 /*
37  *      A mapping of configuration file names to internal variables.
38  *
39  *      Note that the string is dynamically allocated, so it MUST
40  *      be freed.  When the configuration file parse re-reads the string,
41  *      it free's the old one, and strdup's the new one, placing the pointer
42  *      to the strdup'd string into 'config.string'.  This gets around
43  *      buffer over-flows.
44  */
45 static const CONF_PARSER module_config[] = {
46   { "delete_mppe_keys", PW_TYPE_BOOLEAN,
47     offsetof(rlm_wimax_t,delete_mppe_keys), NULL,   "no" },
48
49   { NULL, -1, 0, NULL, NULL }           /* end the list */
50 };
51
52
53 /*
54  *      Only free memory we allocated.  The strings allocated via
55  *      cf_section_parse() do not need to be freed.
56  */
57 static int wimax_detach(void *instance)
58 {
59         free(instance);
60         return 0;
61 }
62
63 /*
64  *      Do any per-module initialization that is separate to each
65  *      configured instance of the module.  e.g. set up connections
66  *      to external databases, read configuration files, set up
67  *      dictionary entries, etc.
68  *
69  *      If configuration information is given in the config section
70  *      that must be referenced in later calls, store a handle to it
71  *      in *instance otherwise put a null pointer there.
72  */
73 static int wimax_instantiate(CONF_SECTION *conf, void **instance)
74 {
75         rlm_wimax_t *inst;
76
77         /*
78          *      Set up a storage area for instance data
79          */
80         inst = rad_malloc(sizeof(*inst));
81         if (!inst) {
82                 return -1;
83         }
84         memset(inst, 0, sizeof(*inst));
85
86         /*
87          *      If the configuration parameters can't be parsed, then
88          *      fail.
89          */
90         if (cf_section_parse(conf, inst, module_config) < 0) {
91                 wimax_detach(inst);
92                 return -1;
93         }
94
95         *instance = inst;
96
97         return 0;
98 }
99
100 /*
101  *      Find the named user in this modules database.  Create the set
102  *      of attribute-value pairs to check and reply with for this user
103  *      from the database. The authentication code only needs to check
104  *      the password, the rest is done here.
105  */
106 static int wimax_authorize(void *instance, REQUEST *request)
107 {
108         VALUE_PAIR *vp;
109
110         /* quiet the compiler */
111         instance = instance;
112         request = request;
113
114         /*
115          *      Fix Calling-Station-Id.  Damn you, WiMAX!
116          */
117         vp =  pairfind(request->packet->vps, PW_CALLING_STATION_ID, 0);
118         if (vp && (vp->length == 6)) {
119                 int i;
120                 uint8_t buffer[6];
121
122                 memcpy(buffer, vp->vp_octets, 6);
123
124                 /*
125                  *      RFC 3580 Section 3.20 says this is the preferred
126                  *      format.  Everyone *SANE* is using this format,
127                  *      so we fix it here.
128                  */
129                 for (i = 0; i < 6; i++) {
130                         fr_bin2hex(&buffer[i], &vp->vp_strvalue[i * 3], 1);
131                         vp->vp_strvalue[(i * 3) + 2] = '-';
132                 }
133
134                 vp->vp_strvalue[(5*3)+2] = '\0';
135                 vp->length = (5*3)+2;
136
137                 DEBUG2("rlm_wimax: Fixing WiMAX binary Calling-Station-Id to %s",
138                        vp->vp_strvalue);
139         }
140
141         return RLM_MODULE_OK;
142 }
143
144
145 /*
146  *      Massage the request before recording it or proxying it
147  */
148 static int wimax_preacct(void *instance, REQUEST *request)
149 {
150         return wimax_authorize(instance, request);
151 }
152
153 /*
154  *      Write accounting information to this modules database.
155  */
156 static int wimax_accounting(void *instance, REQUEST *request)
157 {
158         /* quiet the compiler */
159         instance = instance;
160         request = request;
161
162         return RLM_MODULE_OK;
163 }
164
165 /*
166  *      Generate the keys after the user has been authenticated.
167  */
168 static int wimax_postauth(void *instance, REQUEST *request)
169 {
170         rlm_wimax_t *inst = instance;
171         VALUE_PAIR *msk, *emsk, *vp;
172         VALUE_PAIR *mn_nai, *ip, *fa_rk;
173         HMAC_CTX hmac;
174         unsigned int rk1_len, rk2_len, rk_len;
175         int rk_lifetime = 3600; /* ? */
176         uint32_t mip_spi;
177         uint8_t usage_data[24];
178         uint8_t mip_rk_1[EVP_MAX_MD_SIZE], mip_rk_2[EVP_MAX_MD_SIZE];
179         uint8_t mip_rk[2 * EVP_MAX_MD_SIZE];
180
181         msk = pairfind(request->reply->vps, 1129, 0);
182         emsk = pairfind(request->reply->vps, 1130, 0);
183         if (!msk || !emsk) {
184                 RDEBUG("No EAP-MSK or EAP-EMSK.  Cannot create WiMAX keys.");
185                 return RLM_MODULE_NOOP;
186         }
187
188         /*
189          *      If we delete the MS-MPPE-*-Key attributes, then add in
190          *      the WiMAX-MSK so that the client has a key available.
191          */
192         if (inst->delete_mppe_keys) {
193                 pairdelete(&request->reply->vps, 16, VENDORPEC_MICROSOFT);
194                 pairdelete(&request->reply->vps, 17, VENDORPEC_MICROSOFT);
195
196                 vp = radius_pairmake(request, &request->reply->vps, "WiMAX-MSK", "0x00", T_OP_EQ);
197                 if (vp) {
198                         memcpy(vp->vp_octets, msk->vp_octets, msk->length);
199                         vp->length = msk->length;
200                 }
201         }
202
203         /*
204          *      Initialize usage data.
205          */
206         memcpy(usage_data, "miprk@wimaxforum.org", 21); /* with trailing \0 */
207         usage_data[21] = 0x02;
208         usage_data[22] = 0x00;
209         usage_data[23] = 0x01;
210
211         /*
212          *      MIP-RK-1 = HMAC-SSHA256(EMSK, usage-data | 0x01)
213          */
214         HMAC_CTX_init(&hmac);
215         HMAC_Init_ex(&hmac, emsk->vp_octets, emsk->length, EVP_sha256(), NULL);
216         
217         HMAC_Update(&hmac, &usage_data[0], sizeof(usage_data));
218         HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len);
219
220         /*
221          *      MIP-RK-2 = HMAC-SSHA256(EMSK, MIP-RK-1 | usage-data | 0x01)
222          */
223         HMAC_CTX_init(&hmac);
224         HMAC_Init_ex(&hmac, emsk->vp_octets, emsk->length, EVP_sha256(), NULL);
225         
226         HMAC_Update(&hmac, (const uint8_t *) &mip_rk_1, rk1_len);
227         HMAC_Update(&hmac, &usage_data[0], sizeof(usage_data));
228         HMAC_Final(&hmac, &mip_rk_2[0], &rk2_len);
229
230         vp = pairfind(request->reply->vps, PW_SESSION_TIMEOUT, 0);
231         if (vp) rk_lifetime = vp->vp_integer;
232
233         memcpy(mip_rk, mip_rk_1, rk1_len);
234         memcpy(mip_rk + rk1_len, mip_rk_2, rk2_len);
235         rk_len = rk1_len + rk2_len;
236
237         /*
238          *      MIP-SPI = HMAC-SSHA256(MIP-RK, "SPI CMIP PMIP");
239          */
240         HMAC_CTX_init(&hmac);
241         HMAC_Init_ex(&hmac, mip_rk, rk_len, EVP_sha256(), NULL);
242         
243         HMAC_Update(&hmac, (const uint8_t *) "SPI CMIP PMIP", 12);
244         HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len);
245
246         /*
247          *      Take the 4 most significant octets.
248          *      If less than 256, add 256.
249          */
250         mip_spi = ((mip_rk_1[0] << 24) | (mip_rk_1[1] << 16) |
251                    (mip_rk_1[2] << 8) | mip_rk_1[3]);
252         if (mip_spi < 256) mip_spi += 256;
253
254         if (debug_flag) {
255                 int len = rk_len;
256                 char buffer[512];
257
258                 if (len > 128) len = 128; /* buffer size */
259
260                 fr_bin2hex(mip_rk, buffer, len);
261                 radlog_request(L_DBG, 0, request, "MIP-RK = 0x%s", buffer);
262                 radlog_request(L_DBG, 0, request, "MIP-SPI = %08x",
263                                ntohl(mip_spi));
264         }
265
266         /*
267          *      FIXME: Perform SPI collision prevention
268          */
269
270         /*
271          *      Calculate mobility keys
272          */
273         mn_nai = pairfind(request->packet->vps, 1900, 0);
274         if (!mn_nai) mn_nai = pairfind(request->reply->vps, 1900, 0);
275         if (!mn_nai) {
276                 RDEBUG("WARNING: WiMAX-MN-NAI was not found in the request or in the reply.");
277                 RDEBUG("WARNING: We cannot calculate MN-HA keys.");
278         }
279
280         /*
281          *      WiMAX-IP-Technology
282          */
283         vp = NULL;
284         if (mn_nai) vp = pairfind(request->reply->vps, 23, VENDORPEC_WIMAX);
285         if (!vp) {
286                 RDEBUG("WARNING: WiMAX-IP-Technology not found in reply.");
287                 RDEBUG("WARNING: Not calculating MN-HA keys");
288         }
289
290         if (vp) switch (vp->vp_integer) {
291         case 2:                 /* PMIP4 */
292                 /*
293                  *      Look for WiMAX-hHA-IP-MIP4
294                  */
295                 ip = pairfind(request->reply->vps, 6, VENDORPEC_WIMAX);
296                 if (!ip) {
297                         RDEBUG("WARNING: WiMAX-hHA-IP-MIP4 not found.  Cannot calculate MN-HA-PMIP4 key");
298                         break;
299                 }
300
301                 /*
302                  *      MN-HA-PMIP4 =
303                  *         H(MIP-RK, "PMIP4 MN HA" | HA-IPv4 | MN-NAI);
304                  */
305                 HMAC_CTX_init(&hmac);
306                 HMAC_Init_ex(&hmac, mip_rk, rk_len, EVP_sha1(), NULL);
307
308                 HMAC_Update(&hmac, (const uint8_t *) "PMIP4 MN HA", 11);
309                 HMAC_Update(&hmac, (const uint8_t *) &ip->vp_ipaddr, 4);
310                 HMAC_Update(&hmac, (const uint8_t *) &mn_nai->vp_strvalue, mn_nai->length);
311                 HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len);
312
313                 /*
314                  *      Put MN-HA-PMIP4 into WiMAX-MN-hHA-MIP4-Key
315                  */
316                 vp = pairfind(request->reply->vps, 10, VENDORPEC_WIMAX);
317                 if (!vp) {
318                         vp = radius_paircreate(request, &request->reply->vps,
319                                                10, VENDORPEC_WIMAX, PW_TYPE_OCTETS);
320                 }
321                 if (!vp) {
322                         RDEBUG("WARNING: Failed creating WiMAX-MN-hHA-MIP4-Key");
323                         break;
324                 }
325                 memcpy(vp->vp_octets, &mip_rk_1[0], rk1_len);
326                 vp->length = rk1_len;
327
328                 /*
329                  *      Put MN-HA-PMIP4-SPI into WiMAX-MN-hHA-MIP4-SPI
330                  */
331                 vp = pairfind(request->reply->vps, 11, VENDORPEC_WIMAX);
332                 if (!vp) {
333                         vp = radius_paircreate(request, &request->reply->vps,
334                                                11, VENDORPEC_WIMAX, PW_TYPE_INTEGER);
335                 }
336                 if (!vp) {
337                         RDEBUG("WARNING: Failed creating WiMAX-MN-hHA-MIP4-SPI");
338                         break;
339                 }
340                 vp->vp_integer = mip_spi + 1;
341                 break;
342
343         case 3:                 /* CMIP4 */
344                 /*
345                  *      Look for WiMAX-hHA-IP-MIP4
346                  */
347                 ip = pairfind(request->reply->vps, 6, VENDORPEC_WIMAX);
348                 if (!ip) {
349                         RDEBUG("WARNING: WiMAX-hHA-IP-MIP4 not found.  Cannot calculate MN-HA-CMIP4 key");
350                         break;
351                 }
352
353                 /*
354                  *      MN-HA-CMIP4 =
355                  *         H(MIP-RK, "CMIP4 MN HA" | HA-IPv4 | MN-NAI);
356                  */
357                 HMAC_CTX_init(&hmac);
358                 HMAC_Init_ex(&hmac, mip_rk, rk_len, EVP_sha1(), NULL);
359
360                 HMAC_Update(&hmac, (const uint8_t *) "CMIP4 MN HA", 11);
361                 HMAC_Update(&hmac, (const uint8_t *) &ip->vp_ipaddr, 4);
362                 HMAC_Update(&hmac, (const uint8_t *) &mn_nai->vp_strvalue, mn_nai->length);
363                 HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len);
364
365                 /*
366                  *      Put MN-HA-CMIP4 into WiMAX-MN-hHA-MIP4-Key
367                  */
368                 vp = pairfind(request->reply->vps, 10, VENDORPEC_WIMAX);
369                 if (!vp) {
370                         vp = radius_paircreate(request, &request->reply->vps,
371                                                10, VENDORPEC_WIMAX, PW_TYPE_OCTETS);
372                 }
373                 if (!vp) {
374                         RDEBUG("WARNING: Failed creating WiMAX-MN-hHA-MIP4-Key");
375                         break;
376                 }
377                 memcpy(vp->vp_octets, &mip_rk_1[0], rk1_len);
378                 vp->length = rk1_len;
379
380                 /*
381                  *      Put MN-HA-CMIP4-SPI into WiMAX-MN-hHA-MIP4-SPI
382                  */
383                 vp = pairfind(request->reply->vps, 11, VENDORPEC_WIMAX);
384                 if (!vp) {
385                         vp = radius_paircreate(request, &request->reply->vps,
386                                                11, VENDORPEC_WIMAX, PW_TYPE_INTEGER);
387                 }
388                 if (!vp) {
389                         RDEBUG("WARNING: Failed creating WiMAX-MN-hHA-MIP4-SPI");
390                         break;
391                 }
392                 vp->vp_integer = mip_spi;
393                 break;
394
395         case 4:                 /* CMIP6 */
396                 /*
397                  *      Look for WiMAX-hHA-IP-MIP6
398                  */
399                 ip = pairfind(request->reply->vps, 7, VENDORPEC_WIMAX);
400                 if (!ip) {
401                         RDEBUG("WARNING: WiMAX-hHA-IP-MIP6 not found.  Cannot calculate MN-HA-CMIP6 key");
402                         break;
403                 }
404
405                 /*
406                  *      MN-HA-CMIP6 =
407                  *         H(MIP-RK, "CMIP6 MN HA" | HA-IPv6 | MN-NAI);
408                  */
409                 HMAC_CTX_init(&hmac);
410                 HMAC_Init_ex(&hmac, mip_rk, rk_len, EVP_sha1(), NULL);
411
412                 HMAC_Update(&hmac, (const uint8_t *) "CMIP6 MN HA", 11);
413                 HMAC_Update(&hmac, (const uint8_t *) &ip->vp_ipv6addr, 16);
414                 HMAC_Update(&hmac, (const uint8_t *) &mn_nai->vp_strvalue, mn_nai->length);
415                 HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len);
416
417                 /*
418                  *      Put MN-HA-CMIP6 into WiMAX-MN-hHA-MIP6-Key
419                  */
420                 vp = pairfind(request->reply->vps, 12, VENDORPEC_WIMAX);
421                 if (!vp) {
422                         vp = radius_paircreate(request, &request->reply->vps,
423                                                12, VENDORPEC_WIMAX, PW_TYPE_OCTETS);
424                 }
425                 if (!vp) {
426                         RDEBUG("WARNING: Failed creating WiMAX-MN-hHA-MIP6-Key");
427                         break;
428                 }
429                 memcpy(vp->vp_octets, &mip_rk_1[0], rk1_len);
430                 vp->length = rk1_len;
431
432                 /*
433                  *      Put MN-HA-CMIP6-SPI into WiMAX-MN-hHA-MIP6-SPI
434                  */
435                 vp = pairfind(request->reply->vps, 13, VENDORPEC_WIMAX);
436                 if (!vp) {
437                         vp = radius_paircreate(request, &request->reply->vps,
438                                                13, VENDORPEC_WIMAX, PW_TYPE_INTEGER);
439                 }
440                 if (!vp) {
441                         RDEBUG("WARNING: Failed creating WiMAX-MN-hHA-MIP6-SPI");
442                         break;
443                 }
444                 vp->vp_integer = mip_spi + 2;
445                 break;
446
447         default:
448                 break;          /* do nothing */
449         }
450
451         /*
452          *      Generate FA-RK, if requested.
453          *
454          *      FA-RK= H(MIP-RK, "FA-RK")
455          */
456         fa_rk = pairfind(request->reply->vps, 14, VENDORPEC_WIMAX);
457         if (fa_rk && (fa_rk->length <= 1)) {
458                 HMAC_CTX_init(&hmac);
459                 HMAC_Init_ex(&hmac, mip_rk, rk_len, EVP_sha1(), NULL);
460                 
461                 HMAC_Update(&hmac, (const uint8_t *) "FA-RK", 5);
462
463                 HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len);
464
465                 memcpy(fa_rk->vp_octets, &mip_rk_1[0], rk1_len);
466                 fa_rk->length = rk1_len;
467         }
468
469         /*
470          *      Create FA-RK-SPI, which is really SPI-CMIP4, which is
471          *      really MIP-SPI.  Clear?  Of course.  This is WiMAX.
472          */
473         if (fa_rk) {
474                 vp = pairfind(request->reply->vps, 61, VENDORPEC_WIMAX);
475                 if (!vp) {
476                         vp = radius_paircreate(request, &request->reply->vps,
477                                                61, VENDORPEC_WIMAX, PW_TYPE_INTEGER);
478                 }
479                 if (!vp) {
480                         RDEBUG("WARNING: Failed creating WiMAX-FA-RK-SPI");
481                 } else {
482                         vp->vp_integer = mip_spi;
483                 }
484         }
485
486         /*
487          *      Generate MN-FA = H(FA-RK, "MN FA" | FA-IP | MN-NAI)
488          */
489         ip = pairfind(request->reply->vps, 1901, 0);
490         if (fa_rk && ip && mn_nai) {
491                 HMAC_CTX_init(&hmac);
492                 HMAC_Init_ex(&hmac, fa_rk->vp_octets, fa_rk->length,
493                              EVP_sha1(), NULL);
494                 
495                 HMAC_Update(&hmac, (const uint8_t *) "MN FA", 5);
496                 HMAC_Update(&hmac, (const uint8_t *) &ip->vp_ipaddr, 4);
497                 HMAC_Update(&hmac, (const uint8_t *) &mn_nai->vp_strvalue, mn_nai->length);
498
499                 HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len);
500
501                 vp = radius_paircreate(request, &request->reply->vps,
502                                        1902, 0, PW_TYPE_OCTETS);
503                 if (!vp) {
504                         RDEBUG("WARNING: Failed creating WiMAX-MN-FA");
505                 } else {
506                         memcpy(vp->vp_octets, &mip_rk_1[0], rk1_len);
507                         vp->length = rk1_len;
508                 }
509         }
510
511         /*
512          *      Give additional information about requests && responses
513          *
514          *      WiMAX-RRQ-MN-HA-SPI
515          */
516         vp = pairfind(request->packet->vps, 20, VENDORPEC_WIMAX);
517         if (vp) {
518                 RDEBUG("Client requested MN-HA key: Should use SPI to look up key from storage.");
519                 if (!mn_nai) {
520                         RDEBUG("WARNING: MN-NAI was not found!");
521                 }
522
523                 /*
524                  *      WiMAX-RRQ-HA-IP
525                  */
526                 if (!pairfind(request->packet->vps, 18, VENDORPEC_WIMAX)) {
527                         RDEBUG("WARNING: HA-IP was not found!");
528                 }
529
530
531                 /*
532                  *      WiMAX-HA-RK-Key-Requested
533                  */
534                 vp = pairfind(request->packet->vps, 58, VENDORPEC_WIMAX);
535                 if (vp && (vp->vp_integer == 1)) {
536                         RDEBUG("Client requested HA-RK: Should use IP to look it up from storage.");
537                 }
538         }
539
540         /*
541          *      Wipe the context of all sensitive information.
542          */
543         HMAC_CTX_cleanup(&hmac);
544
545         return RLM_MODULE_UPDATED;
546 }
547
548
549 /*
550  *      The module name should be the only globally exported symbol.
551  *      That is, everything else should be 'static'.
552  *
553  *      If the module needs to temporarily modify it's instantiation
554  *      data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
555  *      The server will then take care of ensuring that the module
556  *      is single-threaded.
557  */
558 module_t rlm_wimax = {
559         RLM_MODULE_INIT,
560         "wimax",
561         RLM_TYPE_THREAD_SAFE,           /* type */
562         wimax_instantiate,              /* instantiation */
563         wimax_detach,                   /* detach */
564         {
565                 NULL,                   /* authentication */
566                 wimax_authorize,        /* authorization */
567                 wimax_preacct,          /* preaccounting */
568                 wimax_accounting,       /* accounting */
569                 NULL,                   /* checksimul */
570                 NULL,                   /* pre-proxy */
571                 NULL,                   /* post-proxy */
572                 wimax_postauth          /* post-auth */
573         },
574 };