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