All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
rlm_wimax.c
Go to the documentation of this file.
1 /*
2  * This program is is free software; you can redistribute it and/or modify
3  * it under the terms of the GNU General Public License as published by
4  * the Free Software Foundation; either version 2 of the License, or (at
5  * your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15  */
16 
17 /**
18  * $Id: b22da1a839361bb8cfe2d8b5866cabda8d55b37f $
19  * @file rlm_wimax.c
20  * @brief Supports various WiMax functionality.
21  *
22  * @copyright 2008 Alan DeKok <aland@networkradius.com>
23  */
24 RCSID("$Id: b22da1a839361bb8cfe2d8b5866cabda8d55b37f $")
25 USES_APPLE_DEPRECATED_API /* OpenSSL API has been deprecated by Apple */
26 
27 #include <freeradius-devel/radiusd.h>
28 #include <freeradius-devel/modules.h>
29 
30 /*
31  * FIXME: Add check for this header to configure.ac
32  */
33 #include <openssl/hmac.h>
34 
35 /*
36  * FIXME: Fix the build system to create definitions from names.
37  */
38 typedef struct rlm_wimax_t {
40 } rlm_wimax_t;
41 
42 /*
43  * A mapping of configuration file names to internal variables.
44  *
45  * Note that the string is dynamically allocated, so it MUST
46  * be freed. When the configuration file parse re-reads the string,
47  * it free's the old one, and strdup's the new one, placing the pointer
48  * to the strdup'd string into 'config.string'. This gets around
49  * buffer over-flows.
50  */
51 static const CONF_PARSER module_config[] = {
52  { FR_CONF_OFFSET("delete_mppe_keys", PW_TYPE_BOOLEAN, rlm_wimax_t, delete_mppe_keys), .dflt = "no" },
54 };
55 
56 /*
57  * Find the named user in this modules database. Create the set
58  * of attribute-value pairs to check and reply with for this user
59  * from the database. The authentication code only needs to check
60  * the password, the rest is done here.
61  */
62 static rlm_rcode_t CC_HINT(nonnull) mod_authorize(UNUSED void *instance, REQUEST *request)
63 {
64  VALUE_PAIR *vp;
65 
66  /*
67  * Fix Calling-Station-Id. Damn you, WiMAX!
68  */
69  vp = fr_pair_find_by_num(request->packet->vps, 0, PW_CALLING_STATION_ID, TAG_ANY);
70  if (vp && (vp->vp_length == 6)) {
71  int i;
72  char *p;
73  uint8_t buffer[6];
74 
75  memcpy(buffer, vp->vp_strvalue, 6);
76 
77  p = talloc_array(vp, char, (5 * 3) + 2 + 1);
78  /*
79  * RFC 3580 Section 3.20 says this is the preferred
80  * format. Everyone *SANE* is using this format,
81  * so we fix it here.
82  */
83  for (i = 0; i < 6; i++) {
84  fr_bin2hex(&p[i * 3], &buffer[i], 1);
85  p[(i * 3) + 2] = '-';
86  }
87 
88  p[(5*3)+2] = '\0';
90 
91  DEBUG2("rlm_wimax: Fixing WiMAX binary Calling-Station-Id to %s",
92  vp->vp_strvalue);
93  return RLM_MODULE_OK;
94  }
95 
96  return RLM_MODULE_NOOP;
97 }
98 
99 
100 /*
101  * Massage the request before recording it or proxying it
102  */
103 static rlm_rcode_t CC_HINT(nonnull) mod_preacct(void *instance, REQUEST *request)
104 {
105  return mod_authorize(instance, request);
106 }
107 
108 /*
109  * Write accounting information to this modules database.
110  */
111 static rlm_rcode_t CC_HINT(nonnull) mod_accounting(UNUSED void *instance, UNUSED REQUEST *request)
112 {
113  return RLM_MODULE_OK;
114 }
115 
116 /*
117  * Generate the keys after the user has been authenticated.
118  */
119 static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *request)
120 {
121  rlm_wimax_t *inst = instance;
122  VALUE_PAIR *msk, *emsk, *vp;
123  VALUE_PAIR *mn_nai, *ip, *fa_rk;
124  HMAC_CTX hmac;
125  unsigned int rk1_len, rk2_len, rk_len;
126  uint32_t mip_spi;
127  uint8_t usage_data[24];
128  uint8_t mip_rk_1[EVP_MAX_MD_SIZE], mip_rk_2[EVP_MAX_MD_SIZE];
129  uint8_t mip_rk[2 * EVP_MAX_MD_SIZE];
130 
131  msk = fr_pair_find_by_num(request->reply->vps, 0, PW_EAP_MSK, TAG_ANY);
132  emsk = fr_pair_find_by_num(request->reply->vps, 0, PW_EAP_EMSK, TAG_ANY);
133  if (!msk || !emsk) {
134  RDEBUG("No EAP-MSK or EAP-EMSK. Cannot create WiMAX keys");
135  return RLM_MODULE_NOOP;
136  }
137 
138  /*
139  * If we delete the MS-MPPE-*-Key attributes, then add in
140  * the WiMAX-MSK so that the client has a key available.
141  */
142  if (inst->delete_mppe_keys) {
143  fr_pair_delete_by_num(&request->reply->vps, VENDORPEC_MICROSOFT, 16, TAG_ANY);
144  fr_pair_delete_by_num(&request->reply->vps, VENDORPEC_MICROSOFT, 17, TAG_ANY);
145 
146  vp = pair_make_reply("WiMAX-MSK", NULL, T_OP_EQ);
147  if (vp) {
148  fr_pair_value_memcpy(vp, msk->vp_octets, msk->vp_length);
149  }
150  }
151 
152  /*
153  * Initialize usage data.
154  */
155  memcpy(usage_data, "miprk@wimaxforum.org", 21); /* with trailing \0 */
156  usage_data[21] = 0x02;
157  usage_data[22] = 0x00;
158  usage_data[23] = 0x01;
159 
160  /*
161  * MIP-RK-1 = HMAC-SSHA256(EMSK, usage-data | 0x01)
162  */
163  HMAC_CTX_init(&hmac);
164  HMAC_Init_ex(&hmac, emsk->vp_octets, emsk->vp_length, EVP_sha256(), NULL);
165 
166  HMAC_Update(&hmac, &usage_data[0], sizeof(usage_data));
167  HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len);
168 
169  /*
170  * MIP-RK-2 = HMAC-SSHA256(EMSK, MIP-RK-1 | usage-data | 0x01)
171  */
172  HMAC_Init_ex(&hmac, emsk->vp_octets, emsk->vp_length, EVP_sha256(), NULL);
173 
174  HMAC_Update(&hmac, (uint8_t const *) &mip_rk_1, rk1_len);
175  HMAC_Update(&hmac, &usage_data[0], sizeof(usage_data));
176  HMAC_Final(&hmac, &mip_rk_2[0], &rk2_len);
177 
178  memcpy(mip_rk, mip_rk_1, rk1_len);
179  memcpy(mip_rk + rk1_len, mip_rk_2, rk2_len);
180  rk_len = rk1_len + rk2_len;
181 
182  /*
183  * MIP-SPI = HMAC-SSHA256(MIP-RK, "SPI CMIP PMIP");
184  */
185  HMAC_Init_ex(&hmac, mip_rk, rk_len, EVP_sha256(), NULL);
186 
187  HMAC_Update(&hmac, (uint8_t const *) "SPI CMIP PMIP", 12);
188  HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len);
189 
190  /*
191  * Take the 4 most significant octets.
192  * If less than 256, add 256.
193  */
194  mip_spi = ((mip_rk_1[0] << 24) | (mip_rk_1[1] << 16) |
195  (mip_rk_1[2] << 8) | mip_rk_1[3]);
196  if (mip_spi < 256) mip_spi += 256;
197 
198  if (rad_debug_lvl) {
199  int len = rk_len;
200  char buffer[512];
201 
202  if (len > 128) len = 128; /* buffer size */
203 
204  fr_bin2hex(buffer, mip_rk, len);
205  RDEBUG("MIP-RK = 0x%s", buffer);
206  RDEBUG("MIP-SPI = %08x", ntohl(mip_spi));
207  }
208 
209  /*
210  * FIXME: Perform SPI collision prevention
211  */
212 
213  /*
214  * Calculate mobility keys
215  */
216  mn_nai = fr_pair_find_by_num(request->packet->vps, 0, PW_WIMAX_MN_NAI, TAG_ANY);
217  if (!mn_nai) mn_nai = fr_pair_find_by_num(request->reply->vps, 0, PW_WIMAX_MN_NAI, TAG_ANY);
218  if (!mn_nai) {
219  RWDEBUG("WiMAX-MN-NAI was not found in the request or in the reply");
220  RWDEBUG("We cannot calculate MN-HA keys");
221  }
222 
223  /*
224  * WiMAX-IP-Technology
225  */
226  vp = NULL;
227  if (mn_nai) vp = fr_pair_find_by_num(request->reply->vps, VENDORPEC_WIMAX, 23, TAG_ANY);
228  if (!vp) {
229  RWDEBUG("WiMAX-IP-Technology not found in reply");
230  RWDEBUG("Not calculating MN-HA keys");
231  }
232 
233  if (vp) switch (vp->vp_integer) {
234  case 2: /* PMIP4 */
235  /*
236  * Look for WiMAX-hHA-IP-MIP4
237  */
238  ip = fr_pair_find_by_num(request->reply->vps, VENDORPEC_WIMAX, 6, TAG_ANY);
239  if (!ip) {
240  RWDEBUG("WiMAX-hHA-IP-MIP4 not found. Cannot calculate MN-HA-PMIP4 key");
241  break;
242  }
243 
244  /*
245  * MN-HA-PMIP4 =
246  * H(MIP-RK, "PMIP4 MN HA" | HA-IPv4 | MN-NAI);
247  */
248  HMAC_Init_ex(&hmac, mip_rk, rk_len, EVP_sha1(), NULL);
249 
250  HMAC_Update(&hmac, (uint8_t const *) "PMIP4 MN HA", 11);
251  HMAC_Update(&hmac, (uint8_t const *) &ip->vp_ipaddr, 4);
252  HMAC_Update(&hmac, (uint8_t const *) &mn_nai->vp_strvalue, mn_nai->vp_length);
253  HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len);
254 
255  /*
256  * Put MN-HA-PMIP4 into WiMAX-MN-hHA-MIP4-Key
257  */
258  vp = fr_pair_find_by_num(request->reply->vps, VENDORPEC_WIMAX, 10, TAG_ANY);
259  if (!vp) {
260  vp = radius_pair_create(request->reply, &request->reply->vps,
261  10, VENDORPEC_WIMAX);
262  }
263  if (!vp) {
264  RWDEBUG("Failed creating WiMAX-MN-hHA-MIP4-Key");
265  break;
266  }
267  fr_pair_value_memcpy(vp, &mip_rk_1[0], rk1_len);
268 
269  /*
270  * Put MN-HA-PMIP4-SPI into WiMAX-MN-hHA-MIP4-SPI
271  */
272  vp = fr_pair_find_by_num(request->reply->vps, VENDORPEC_WIMAX, 11, TAG_ANY);
273  if (!vp) {
274  vp = radius_pair_create(request->reply, &request->reply->vps,
275  11, VENDORPEC_WIMAX);
276  }
277  if (!vp) {
278  RWDEBUG("Failed creating WiMAX-MN-hHA-MIP4-SPI");
279  break;
280  }
281  vp->vp_integer = mip_spi + 1;
282  break;
283 
284  case 3: /* CMIP4 */
285  /*
286  * Look for WiMAX-hHA-IP-MIP4
287  */
288  ip = fr_pair_find_by_num(request->reply->vps, VENDORPEC_WIMAX, 6, TAG_ANY);
289  if (!ip) {
290  RWDEBUG("WiMAX-hHA-IP-MIP4 not found. Cannot calculate MN-HA-CMIP4 key");
291  break;
292  }
293 
294  /*
295  * MN-HA-CMIP4 =
296  * H(MIP-RK, "CMIP4 MN HA" | HA-IPv4 | MN-NAI);
297  */
298  HMAC_Init_ex(&hmac, mip_rk, rk_len, EVP_sha1(), NULL);
299 
300  HMAC_Update(&hmac, (uint8_t const *) "CMIP4 MN HA", 11);
301  HMAC_Update(&hmac, (uint8_t const *) &ip->vp_ipaddr, 4);
302  HMAC_Update(&hmac, (uint8_t const *) &mn_nai->vp_strvalue, mn_nai->vp_length);
303  HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len);
304 
305  /*
306  * Put MN-HA-CMIP4 into WiMAX-MN-hHA-MIP4-Key
307  */
308  vp = fr_pair_find_by_num(request->reply->vps, VENDORPEC_WIMAX, 10, TAG_ANY);
309  if (!vp) {
310  vp = radius_pair_create(request->reply, &request->reply->vps,
311  10, VENDORPEC_WIMAX);
312  }
313  if (!vp) {
314  RWDEBUG("Failed creating WiMAX-MN-hHA-MIP4-Key");
315  break;
316  }
317  fr_pair_value_memcpy(vp, &mip_rk_1[0], rk1_len);
318 
319  /*
320  * Put MN-HA-CMIP4-SPI into WiMAX-MN-hHA-MIP4-SPI
321  */
322  vp = fr_pair_find_by_num(request->reply->vps, VENDORPEC_WIMAX, 11, TAG_ANY);
323  if (!vp) {
324  vp = radius_pair_create(request->reply, &request->reply->vps,
325  11, VENDORPEC_WIMAX);
326  }
327  if (!vp) {
328  RWDEBUG("Failed creating WiMAX-MN-hHA-MIP4-SPI");
329  break;
330  }
331  vp->vp_integer = mip_spi;
332  break;
333 
334  case 4: /* CMIP6 */
335  /*
336  * Look for WiMAX-hHA-IP-MIP6
337  */
338  ip = fr_pair_find_by_num(request->reply->vps, VENDORPEC_WIMAX, 7, TAG_ANY);
339  if (!ip) {
340  RWDEBUG("WiMAX-hHA-IP-MIP6 not found. Cannot calculate MN-HA-CMIP6 key");
341  break;
342  }
343 
344  /*
345  * MN-HA-CMIP6 =
346  * H(MIP-RK, "CMIP6 MN HA" | HA-IPv6 | MN-NAI);
347  */
348  HMAC_Init_ex(&hmac, mip_rk, rk_len, EVP_sha1(), NULL);
349 
350  HMAC_Update(&hmac, (uint8_t const *) "CMIP6 MN HA", 11);
351  HMAC_Update(&hmac, (uint8_t const *) &ip->vp_ipv6addr, 16);
352  HMAC_Update(&hmac, (uint8_t const *) &mn_nai->vp_strvalue, mn_nai->vp_length);
353  HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len);
354 
355  /*
356  * Put MN-HA-CMIP6 into WiMAX-MN-hHA-MIP6-Key
357  */
358  vp = fr_pair_find_by_num(request->reply->vps, VENDORPEC_WIMAX, 12, TAG_ANY);
359  if (!vp) {
360  vp = radius_pair_create(request->reply, &request->reply->vps,
361  12, VENDORPEC_WIMAX);
362  }
363  if (!vp) {
364  RWDEBUG("Failed creating WiMAX-MN-hHA-MIP6-Key");
365  break;
366  }
367  fr_pair_value_memcpy(vp, &mip_rk_1[0], rk1_len);
368 
369  /*
370  * Put MN-HA-CMIP6-SPI into WiMAX-MN-hHA-MIP6-SPI
371  */
372  vp = fr_pair_find_by_num(request->reply->vps, VENDORPEC_WIMAX, 13, TAG_ANY);
373  if (!vp) {
374  vp = radius_pair_create(request->reply, &request->reply->vps,
375  13, VENDORPEC_WIMAX);
376  }
377  if (!vp) {
378  RWDEBUG("Failed creating WiMAX-MN-hHA-MIP6-SPI");
379  break;
380  }
381  vp->vp_integer = mip_spi + 2;
382  break;
383 
384  default:
385  break; /* do nothing */
386  }
387 
388  /*
389  * Generate FA-RK, if requested.
390  *
391  * FA-RK= H(MIP-RK, "FA-RK")
392  */
393  fa_rk = fr_pair_find_by_num(request->reply->vps, VENDORPEC_WIMAX, 14, TAG_ANY);
394  if (fa_rk && (fa_rk->vp_length <= 1)) {
395  HMAC_Init_ex(&hmac, mip_rk, rk_len, EVP_sha1(), NULL);
396 
397  HMAC_Update(&hmac, (uint8_t const *) "FA-RK", 5);
398 
399  HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len);
400 
401  fr_pair_value_memcpy(fa_rk, &mip_rk_1[0], rk1_len);
402  }
403 
404  /*
405  * Create FA-RK-SPI, which is really SPI-CMIP4, which is
406  * really MIP-SPI. Clear? Of course. This is WiMAX.
407  */
408  if (fa_rk) {
409  vp = fr_pair_find_by_num(request->reply->vps, VENDORPEC_WIMAX, 61, TAG_ANY);
410  if (!vp) {
411  vp = radius_pair_create(request->reply, &request->reply->vps,
412  61, VENDORPEC_WIMAX);
413  }
414  if (!vp) {
415  RWDEBUG("Failed creating WiMAX-FA-RK-SPI");
416  } else {
417  vp->vp_integer = mip_spi;
418  }
419  }
420 
421  /*
422  * Give additional information about requests && responses
423  *
424  * WiMAX-RRQ-MN-HA-SPI
425  */
426  vp = fr_pair_find_by_num(request->packet->vps, VENDORPEC_WIMAX, 20, TAG_ANY);
427  if (vp) {
428  RDEBUG("Client requested MN-HA key: Should use SPI to look up key from storage");
429  if (!mn_nai) {
430  RWDEBUG("MN-NAI was not found!");
431  }
432 
433  /*
434  * WiMAX-RRQ-HA-IP
435  */
436  if (!fr_pair_find_by_num(request->packet->vps, VENDORPEC_WIMAX, 18, TAG_ANY)) {
437  RWDEBUG("HA-IP was not found!");
438  }
439 
440 
441  /*
442  * WiMAX-HA-RK-Key-Requested
443  */
444  vp = fr_pair_find_by_num(request->packet->vps, VENDORPEC_WIMAX, 58, TAG_ANY);
445  if (vp && (vp->vp_integer == 1)) {
446  RDEBUG("Client requested HA-RK: Should use IP to look it up from storage");
447  }
448  }
449 
450  /*
451  * Wipe the context of all sensitive information.
452  */
453  HMAC_CTX_cleanup(&hmac);
454 
455  return RLM_MODULE_UPDATED;
456 }
457 
458 
459 /*
460  * The module name should be the only globally exported symbol.
461  * That is, everything else should be 'static'.
462  *
463  * If the module needs to temporarily modify it's instantiation
464  * data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
465  * The server will then take care of ensuring that the module
466  * is single-threaded.
467  */
468 extern module_t rlm_wimax;
469 module_t rlm_wimax = {
471  .name = "wimax",
472  .type = RLM_TYPE_THREAD_SAFE,
473  .inst_size = sizeof(rlm_wimax_t),
474  .config = module_config,
475  .methods = {
477  [MOD_PREACCT] = mod_preacct,
480  },
481 };
static const CONF_PARSER module_config[]
Definition: rlm_wimax.c:51
static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
Write accounting data to Couchbase documents.
module_t rlm_wimax
Definition: rlm_wimax.c:469
The module is OK, continue.
Definition: radiusd.h:91
static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request) CC_HINT(nonnull)
Metadata exported by the module.
Definition: modules.h:134
7 methods index for postauth section.
Definition: modules.h:48
VALUE_PAIR * radius_pair_create(TALLOC_CTX *ctx, VALUE_PAIR **vps, unsigned int attribute, unsigned int vendor)
Create a VALUE_PAIR and add it to a list of VALUE_PAIR s.
Definition: pair.c:704
static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
Handle authorization requests using Couchbase document data.
#define RLM_TYPE_THREAD_SAFE
Module is threadsafe.
Definition: modules.h:75
static rlm_rcode_t CC_HINT(nonnull)
Definition: rlm_wimax.c:62
#define UNUSED
Definition: libradius.h:134
#define RLM_MODULE_INIT
Definition: modules.h:86
#define CONF_PARSER_TERMINATOR
Definition: conffile.h:289
bool delete_mppe_keys
Definition: rlm_wimax.c:39
#define inst
Definition: token.h:46
Defines a CONF_PAIR to C data type mapping.
Definition: conffile.h:267
#define VENDORPEC_MICROSOFT
Definition: radius.h:200
void fr_pair_value_strsteal(VALUE_PAIR *vp, char const *src)
Reparent an allocated char buffer to a VALUE_PAIR.
Definition: pair.c:1955
USES_APPLE_DEPRECATED_API struct rlm_wimax_t rlm_wimax_t
#define DEBUG2(fmt,...)
Definition: log.h:176
3 methods index for accounting section.
Definition: modules.h:44
Stores an attribute, a value and various bits of other data.
Definition: pair.h:112
A truth value.
Definition: radius.h:56
enum rlm_rcodes rlm_rcode_t
Return codes indicating the result of the module call.
void fr_pair_delete_by_num(VALUE_PAIR **head, unsigned int vendor, unsigned int attr, int8_t tag)
Delete matching pairs.
Definition: pair.c:797
Module succeeded without doing anything.
Definition: radiusd.h:96
uint64_t magic
Used to validate module struct.
Definition: modules.h:135
#define TAG_ANY
Definition: pair.h:191
#define FR_CONF_OFFSET(_n, _t, _s, _f)
Definition: conffile.h:168
log_lvl_t rad_debug_lvl
Global debugging level.
Definition: log.c:49
#define pair_make_reply(_a, _b, _c)
Definition: radiusd.h:546
2 methods index for preacct section.
Definition: modules.h:43
VALUE_PAIR * fr_pair_find_by_num(VALUE_PAIR *head, unsigned int vendor, unsigned int attr, int8_t tag)
Find the pair with the matching attribute.
Definition: pair.c:639
#define RWDEBUG(fmt,...)
Definition: log.h:251
1 methods index for authorize section.
Definition: modules.h:42
#define RCSID(id)
Definition: build.h:135
OK (pairs modified).
Definition: radiusd.h:97
#define RDEBUG(fmt,...)
Definition: log.h:243
size_t fr_bin2hex(char *hex, uint8_t const *bin, size_t inlen)
Convert binary data to a hex string.
Definition: misc.c:254
void fr_pair_value_memcpy(VALUE_PAIR *vp, uint8_t const *src, size_t len)
Copy data into an "octets" data type.
Definition: pair.c:1905
#define USES_APPLE_DEPRECATED_API
Definition: build.h:122
#define VENDORPEC_WIMAX
Definition: radius.h:202