hostapd: add experimental radius server
[openwrt/openwrt.git] / package / network / services / hostapd / src / hostapd / radius.c
1 #include "utils/includes.h"
2 #include "utils/common.h"
3 #include "utils/eloop.h"
4 #include "crypto/crypto.h"
5 #include "crypto/tls.h"
6
7 #include "ap/ap_config.h"
8 #include "eap_server/eap.h"
9 #include "radius/radius.h"
10 #include "radius/radius_server.h"
11 #include "eap_register.h"
12
13 #include <libubox/blobmsg_json.h>
14 #include <libubox/blobmsg.h>
15 #include <libubox/avl.h>
16 #include <libubox/avl-cmp.h>
17 #include <libubox/kvlist.h>
18
19 #include <sys/stat.h>
20 #include <fnmatch.h>
21
22 #define VENDOR_ID_WISPR 14122
23 #define VENDOR_ATTR_SIZE 6
24
25 struct radius_parse_attr_data {
26 unsigned int vendor;
27 u8 type;
28 int size;
29 char format;
30 const char *data;
31 };
32
33 struct radius_parse_attr_state {
34 struct hostapd_radius_attr *prev;
35 struct hostapd_radius_attr *attr;
36 struct wpabuf *buf;
37 void *attrdata;
38 };
39
40 struct radius_user_state {
41 struct avl_node node;
42 struct eap_user data;
43 };
44
45 struct radius_user_data {
46 struct kvlist users;
47 struct avl_tree user_state;
48 struct blob_attr *wildcard;
49 };
50
51 struct radius_state {
52 struct radius_server_data *radius;
53 struct eap_config eap;
54
55 struct radius_user_data phase1, phase2;
56 const char *user_file;
57 time_t user_file_ts;
58
59 int n_attrs;
60 struct hostapd_radius_attr *attrs;
61 };
62
63 struct radius_config {
64 struct tls_connection_params tls;
65 struct radius_server_conf radius;
66 };
67
68 enum {
69 USER_ATTR_PASSWORD,
70 USER_ATTR_HASH,
71 USER_ATTR_SALT,
72 USER_ATTR_METHODS,
73 USER_ATTR_RADIUS,
74 USER_ATTR_VLAN,
75 USER_ATTR_MAX_RATE_UP,
76 USER_ATTR_MAX_RATE_DOWN,
77 __USER_ATTR_MAX
78 };
79
80 static void radius_tls_event(void *ctx, enum tls_event ev,
81 union tls_event_data *data)
82 {
83 switch (ev) {
84 case TLS_CERT_CHAIN_SUCCESS:
85 wpa_printf(MSG_DEBUG, "radius: remote certificate verification success");
86 break;
87 case TLS_CERT_CHAIN_FAILURE:
88 wpa_printf(MSG_INFO, "radius: certificate chain failure: reason=%d depth=%d subject='%s' err='%s'",
89 data->cert_fail.reason,
90 data->cert_fail.depth,
91 data->cert_fail.subject,
92 data->cert_fail.reason_txt);
93 break;
94 case TLS_PEER_CERTIFICATE:
95 wpa_printf(MSG_DEBUG, "radius: peer certificate: depth=%d serial_num=%s subject=%s",
96 data->peer_cert.depth,
97 data->peer_cert.serial_num ? data->peer_cert.serial_num : "N/A",
98 data->peer_cert.subject);
99 break;
100 case TLS_ALERT:
101 if (data->alert.is_local)
102 wpa_printf(MSG_DEBUG, "radius: local TLS alert: %s",
103 data->alert.description);
104 else
105 wpa_printf(MSG_DEBUG, "radius: remote TLS alert: %s",
106 data->alert.description);
107 break;
108 case TLS_UNSAFE_RENEGOTIATION_DISABLED:
109 /* Not applicable to TLS server */
110 break;
111 }
112 }
113
114 static void radius_userdata_init(struct radius_user_data *u)
115 {
116 kvlist_init(&u->users, kvlist_blob_len);
117 avl_init(&u->user_state, avl_strcmp, false, NULL);
118 }
119
120 static void radius_userdata_free(struct radius_user_data *u)
121 {
122 struct radius_user_state *s, *tmp;
123
124 kvlist_free(&u->users);
125 free(u->wildcard);
126 u->wildcard = NULL;
127 avl_remove_all_elements(&u->user_state, s, node, tmp)
128 free(s);
129 }
130
131 static void
132 radius_userdata_load(struct radius_user_data *u, struct blob_attr *data)
133 {
134 enum {
135 USERSTATE_USERS,
136 USERSTATE_WILDCARD,
137 __USERSTATE_MAX,
138 };
139 static const struct blobmsg_policy policy[__USERSTATE_MAX] = {
140 [USERSTATE_USERS] = { "users", BLOBMSG_TYPE_TABLE },
141 [USERSTATE_WILDCARD] = { "wildcard", BLOBMSG_TYPE_ARRAY },
142 };
143 struct blob_attr *tb[__USERSTATE_MAX], *cur;
144 int rem;
145
146 if (!data)
147 return;
148
149 blobmsg_parse(policy, __USERSTATE_MAX, tb, blobmsg_data(data), blobmsg_len(data));
150
151 blobmsg_for_each_attr(cur, tb[USERSTATE_USERS], rem)
152 kvlist_set(&u->users, blobmsg_name(cur), cur);
153
154 if (tb[USERSTATE_WILDCARD])
155 u->wildcard = blob_memdup(tb[USERSTATE_WILDCARD]);
156 }
157
158 static void
159 load_userfile(struct radius_state *s)
160 {
161 enum {
162 USERDATA_PHASE1,
163 USERDATA_PHASE2,
164 __USERDATA_MAX
165 };
166 static const struct blobmsg_policy policy[__USERDATA_MAX] = {
167 [USERDATA_PHASE1] = { "phase1", BLOBMSG_TYPE_TABLE },
168 [USERDATA_PHASE2] = { "phase2", BLOBMSG_TYPE_TABLE },
169 };
170 struct blob_attr *tb[__USERDATA_MAX], *cur;
171 static struct blob_buf b;
172 struct stat st;
173 int rem;
174
175 if (stat(s->user_file, &st))
176 return;
177
178 if (s->user_file_ts == st.st_mtime)
179 return;
180
181 s->user_file_ts = st.st_mtime;
182 radius_userdata_free(&s->phase1);
183 radius_userdata_free(&s->phase2);
184
185 blob_buf_init(&b, 0);
186 blobmsg_add_json_from_file(&b, s->user_file);
187 blobmsg_parse(policy, __USERDATA_MAX, tb, blob_data(b.head), blob_len(b.head));
188 radius_userdata_load(&s->phase1, tb[USERDATA_PHASE1]);
189 radius_userdata_load(&s->phase2, tb[USERDATA_PHASE2]);
190
191 blob_buf_free(&b);
192 }
193
194 static struct blob_attr *
195 radius_user_get(struct radius_user_data *s, const char *name)
196 {
197 struct blob_attr *cur;
198 int rem;
199
200 cur = kvlist_get(&s->users, name);
201 if (cur)
202 return cur;
203
204 blobmsg_for_each_attr(cur, s->wildcard, rem) {
205 static const struct blobmsg_policy policy = {
206 "name", BLOBMSG_TYPE_STRING
207 };
208 struct blob_attr *pattern;
209
210 if (blobmsg_type(cur) != BLOBMSG_TYPE_TABLE)
211 continue;
212
213 blobmsg_parse(&policy, 1, &pattern, blobmsg_data(cur), blobmsg_len(cur));
214 if (!name)
215 continue;
216
217 if (!fnmatch(blobmsg_get_string(pattern), name, 0))
218 return cur;
219 }
220
221 return NULL;
222 }
223
224 static struct radius_parse_attr_data *
225 radius_parse_attr(struct blob_attr *attr)
226 {
227 static const struct blobmsg_policy policy[4] = {
228 { .type = BLOBMSG_TYPE_INT32 },
229 { .type = BLOBMSG_TYPE_INT32 },
230 { .type = BLOBMSG_TYPE_STRING },
231 { .type = BLOBMSG_TYPE_STRING },
232 };
233 static struct radius_parse_attr_data data;
234 struct blob_attr *tb[4];
235 const char *format;
236
237 blobmsg_parse_array(policy, ARRAY_SIZE(policy), tb, blobmsg_data(attr), blobmsg_len(attr));
238
239 if (!tb[0] || !tb[1] || !tb[2] || !tb[3])
240 return NULL;
241
242 format = blobmsg_get_string(tb[2]);
243 if (strlen(format) != 1)
244 return NULL;
245
246 data.vendor = blobmsg_get_u32(tb[0]);
247 data.type = blobmsg_get_u32(tb[1]);
248 data.format = format[0];
249 data.data = blobmsg_get_string(tb[3]);
250 data.size = strlen(data.data);
251
252 switch (data.format) {
253 case 's':
254 break;
255 case 'x':
256 if (data.size & 1)
257 return NULL;
258 data.size /= 2;
259 break;
260 case 'd':
261 data.size = 4;
262 break;
263 default:
264 return NULL;
265 }
266
267 return &data;
268 }
269
270 static void
271 radius_count_attrs(struct blob_attr **tb, int *n_attr, size_t *attr_size)
272 {
273 struct blob_attr *data = tb[USER_ATTR_RADIUS];
274 struct blob_attr *cur;
275 int rem;
276
277 blobmsg_for_each_attr(cur, data, rem) {
278 struct radius_parse_attr_data *data;
279 size_t prev = *attr_size;
280
281 data = radius_parse_attr(cur);
282 if (!data)
283 continue;
284
285 *attr_size += data->size;
286 if (data->vendor)
287 *attr_size += VENDOR_ATTR_SIZE;
288
289 (*n_attr)++;
290 }
291
292 *n_attr += !!tb[USER_ATTR_VLAN] * 3 +
293 !!tb[USER_ATTR_MAX_RATE_UP] +
294 !!tb[USER_ATTR_MAX_RATE_DOWN];
295 *attr_size += !!tb[USER_ATTR_VLAN] * (4 + 4 + 5) +
296 !!tb[USER_ATTR_MAX_RATE_UP] * (4 + VENDOR_ATTR_SIZE) +
297 !!tb[USER_ATTR_MAX_RATE_DOWN] * (4 + VENDOR_ATTR_SIZE);
298 }
299
300 static void *
301 radius_add_attr(struct radius_parse_attr_state *state,
302 u32 vendor, u8 type, u8 len)
303 {
304 struct hostapd_radius_attr *attr;
305 struct wpabuf *buf;
306 void *val;
307
308 val = state->attrdata;
309
310 buf = state->buf++;
311 buf->buf = val;
312
313 attr = state->attr++;
314 attr->val = buf;
315 attr->type = type;
316
317 if (state->prev)
318 state->prev->next = attr;
319 state->prev = attr;
320
321 if (vendor) {
322 u8 *vendor_hdr = val + 4;
323
324 WPA_PUT_BE32(val, vendor);
325 vendor_hdr[0] = type;
326 vendor_hdr[1] = len + 2;
327
328 len += VENDOR_ATTR_SIZE;
329 val += VENDOR_ATTR_SIZE;
330 attr->type = RADIUS_ATTR_VENDOR_SPECIFIC;
331 }
332
333 buf->size = buf->used = len;
334 state->attrdata += len;
335
336 return val;
337 }
338
339 static void
340 radius_parse_attrs(struct blob_attr **tb, struct radius_parse_attr_state *state)
341 {
342 struct blob_attr *data = tb[USER_ATTR_RADIUS];
343 struct hostapd_radius_attr *prev = NULL;
344 struct blob_attr *cur;
345 int len, rem;
346 void *val;
347
348 if ((cur = tb[USER_ATTR_VLAN]) != NULL && blobmsg_get_u32(cur) < 4096) {
349 char buf[5];
350
351 val = radius_add_attr(state, 0, RADIUS_ATTR_TUNNEL_TYPE, 4);
352 WPA_PUT_BE32(val, RADIUS_TUNNEL_TYPE_VLAN);
353
354 val = radius_add_attr(state, 0, RADIUS_ATTR_TUNNEL_MEDIUM_TYPE, 4);
355 WPA_PUT_BE32(val, RADIUS_TUNNEL_MEDIUM_TYPE_802);
356
357 len = snprintf(buf, sizeof(buf), "%d", blobmsg_get_u32(cur));
358 val = radius_add_attr(state, 0, RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID, len);
359 memcpy(val, buf, len);
360 }
361
362 if ((cur = tb[USER_ATTR_MAX_RATE_UP]) != NULL) {
363 val = radius_add_attr(state, VENDOR_ID_WISPR, 7, 4);
364 WPA_PUT_BE32(val, blobmsg_get_u32(cur));
365 }
366
367 if ((cur = tb[USER_ATTR_MAX_RATE_DOWN]) != NULL) {
368 val = radius_add_attr(state, VENDOR_ID_WISPR, 8, 4);
369 WPA_PUT_BE32(val, blobmsg_get_u32(cur));
370 }
371
372 blobmsg_for_each_attr(cur, data, rem) {
373 struct radius_parse_attr_data *data;
374 void *val;
375 int size;
376
377 data = radius_parse_attr(cur);
378 if (!data)
379 continue;
380
381 val = radius_add_attr(state, data->vendor, data->type, data->size);
382 switch (data->format) {
383 case 's':
384 memcpy(val, data->data, data->size);
385 break;
386 case 'x':
387 hexstr2bin(data->data, val, data->size);
388 break;
389 case 'd':
390 WPA_PUT_BE32(val, atoi(data->data));
391 break;
392 }
393 }
394 }
395
396 static void
397 radius_user_parse_methods(struct eap_user *eap, struct blob_attr *data)
398 {
399 struct blob_attr *cur;
400 int rem, n = 0;
401
402 if (!data)
403 return;
404
405 blobmsg_for_each_attr(cur, data, rem) {
406 const char *method;
407
408 if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
409 continue;
410
411 if (n == EAP_MAX_METHODS)
412 break;
413
414 method = blobmsg_get_string(cur);
415 eap->methods[n].method = eap_server_get_type(method, &eap->methods[n].vendor);
416 if (eap->methods[n].vendor == EAP_VENDOR_IETF &&
417 eap->methods[n].method == EAP_TYPE_NONE) {
418 if (!strcmp(method, "TTLS-PAP")) {
419 eap->ttls_auth |= EAP_TTLS_AUTH_PAP;
420 continue;
421 }
422 if (!strcmp(method, "TTLS-CHAP")) {
423 eap->ttls_auth |= EAP_TTLS_AUTH_CHAP;
424 continue;
425 }
426 if (!strcmp(method, "TTLS-MSCHAP")) {
427 eap->ttls_auth |= EAP_TTLS_AUTH_MSCHAP;
428 continue;
429 }
430 if (!strcmp(method, "TTLS-MSCHAPV2")) {
431 eap->ttls_auth |= EAP_TTLS_AUTH_MSCHAPV2;
432 continue;
433 }
434 }
435 n++;
436 }
437 }
438
439 static struct eap_user *
440 radius_user_get_state(struct radius_user_data *u, struct blob_attr *data,
441 const char *id)
442 {
443 static const struct blobmsg_policy policy[__USER_ATTR_MAX] = {
444 [USER_ATTR_PASSWORD] = { "password", BLOBMSG_TYPE_STRING },
445 [USER_ATTR_HASH] = { "hash", BLOBMSG_TYPE_STRING },
446 [USER_ATTR_SALT] = { "salt", BLOBMSG_TYPE_STRING },
447 [USER_ATTR_METHODS] = { "methods", BLOBMSG_TYPE_ARRAY },
448 [USER_ATTR_RADIUS] = { "radius", BLOBMSG_TYPE_ARRAY },
449 [USER_ATTR_VLAN] = { "vlan-id", BLOBMSG_TYPE_INT32 },
450 [USER_ATTR_MAX_RATE_UP] = { "max-rate-up", BLOBMSG_TYPE_INT32 },
451 [USER_ATTR_MAX_RATE_DOWN] = { "max-rate-down", BLOBMSG_TYPE_INT32 },
452 };
453 struct blob_attr *tb[__USER_ATTR_MAX], *cur;
454 char *password_buf, *salt_buf, *name_buf;
455 struct radius_parse_attr_state astate = {};
456 struct hostapd_radius_attr *attr;
457 struct radius_user_state *state;
458 int pw_len = 0, salt_len = 0;
459 struct eap_user *eap;
460 struct wpabuf *val;
461 size_t attrsize = 0;
462 void *attrdata;
463 int n_attr = 0;
464
465 state = avl_find_element(&u->user_state, id, state, node);
466 if (state)
467 return &state->data;
468
469 blobmsg_parse(policy, __USER_ATTR_MAX, tb, blobmsg_data(data), blobmsg_len(data));
470
471 if ((cur = tb[USER_ATTR_SALT]) != NULL)
472 salt_len = strlen(blobmsg_get_string(cur)) / 2;
473 if ((cur = tb[USER_ATTR_HASH]) != NULL)
474 pw_len = strlen(blobmsg_get_string(cur)) / 2;
475 else if ((cur = tb[USER_ATTR_PASSWORD]) != NULL)
476 pw_len = blobmsg_len(cur) - 1;
477 radius_count_attrs(tb, &n_attr, &attrsize);
478
479 state = calloc_a(sizeof(*state), &name_buf, strlen(id) + 1,
480 &password_buf, pw_len,
481 &salt_buf, salt_len,
482 &astate.attr, n_attr * sizeof(*astate.attr),
483 &astate.buf, n_attr * sizeof(*astate.buf),
484 &astate.attrdata, attrsize);
485 eap = &state->data;
486 eap->salt = salt_len ? salt_buf : NULL;
487 eap->salt_len = salt_len;
488 eap->password = pw_len ? password_buf : NULL;
489 eap->password_len = pw_len;
490 eap->force_version = -1;
491
492 if ((cur = tb[USER_ATTR_SALT]) != NULL)
493 hexstr2bin(blobmsg_get_string(cur), salt_buf, salt_len);
494 if ((cur = tb[USER_ATTR_PASSWORD]) != NULL)
495 memcpy(password_buf, blobmsg_get_string(cur), pw_len);
496 else if ((cur = tb[USER_ATTR_HASH]) != NULL) {
497 hexstr2bin(blobmsg_get_string(cur), password_buf, pw_len);
498 eap->password_hash = 1;
499 }
500 radius_user_parse_methods(eap, tb[USER_ATTR_METHODS]);
501
502 if (n_attr > 0) {
503 cur = tb[USER_ATTR_RADIUS];
504 eap->accept_attr = astate.attr;
505 radius_parse_attrs(tb, &astate);
506 }
507
508 state->node.key = strcpy(name_buf, id);
509 avl_insert(&u->user_state, &state->node);
510
511 return &state->data;
512
513 free:
514 free(state);
515 return NULL;
516 }
517
518 static int radius_get_eap_user(void *ctx, const u8 *identity,
519 size_t identity_len, int phase2,
520 struct eap_user *user)
521 {
522 struct radius_state *s = ctx;
523 struct radius_user_data *u = phase2 ? &s->phase2 : &s->phase1;
524 struct blob_attr *entry;
525 struct eap_user *data;
526 char *id;
527
528 if (identity_len > 512)
529 return -1;
530
531 load_userfile(s);
532
533 id = alloca(identity_len + 1);
534 memcpy(id, identity, identity_len);
535 id[identity_len] = 0;
536
537 entry = radius_user_get(u, id);
538 if (!entry)
539 return -1;
540
541 if (!user)
542 return 0;
543
544 data = radius_user_get_state(u, entry, id);
545 if (!data)
546 return -1;
547
548 *user = *data;
549 if (user->password_len > 0)
550 user->password = os_memdup(user->password, user->password_len);
551 if (user->salt_len > 0)
552 user->salt = os_memdup(user->salt, user->salt_len);
553 user->phase2 = phase2;
554
555 return 0;
556 }
557
558 static int radius_setup(struct radius_state *s, struct radius_config *c)
559 {
560 struct eap_config *eap = &s->eap;
561 struct tls_config conf = {
562 .event_cb = radius_tls_event,
563 .tls_flags = TLS_CONN_DISABLE_TLSv1_3,
564 .cb_ctx = s,
565 };
566
567 eap->eap_server = 1;
568 eap->max_auth_rounds = 100;
569 eap->max_auth_rounds_short = 50;
570 eap->ssl_ctx = tls_init(&conf);
571 if (!eap->ssl_ctx) {
572 wpa_printf(MSG_INFO, "TLS init failed\n");
573 return 1;
574 }
575
576 if (tls_global_set_params(eap->ssl_ctx, &c->tls)) {
577 wpa_printf(MSG_INFO, "failed to set TLS parameters\n");
578 return 1;
579 }
580
581 c->radius.eap_cfg = eap;
582 c->radius.conf_ctx = s;
583 c->radius.get_eap_user = radius_get_eap_user;
584 s->radius = radius_server_init(&c->radius);
585 if (!s->radius) {
586 wpa_printf(MSG_INFO, "failed to initialize radius server\n");
587 return 1;
588 }
589
590 return 0;
591 }
592
593 static int radius_init(struct radius_state *s)
594 {
595 memset(s, 0, sizeof(*s));
596 radius_userdata_init(&s->phase1);
597 radius_userdata_init(&s->phase2);
598 }
599
600 static void radius_deinit(struct radius_state *s)
601 {
602 if (s->radius)
603 radius_server_deinit(s->radius);
604
605 if (s->eap.ssl_ctx)
606 tls_deinit(s->eap.ssl_ctx);
607
608 radius_userdata_free(&s->phase1);
609 radius_userdata_free(&s->phase2);
610 }
611
612 static int usage(const char *progname)
613 {
614 fprintf(stderr, "Usage: %s <options>\n",
615 progname);
616 }
617
618 int radius_main(int argc, char **argv)
619 {
620 static struct radius_state state = {};
621 static struct radius_config config = {};
622 const char *progname = argv[0];
623 int ret = 0;
624 int ch;
625
626 wpa_debug_setup_stdout();
627 wpa_debug_level = 0;
628
629 if (eloop_init()) {
630 wpa_printf(MSG_ERROR, "Failed to initialize event loop");
631 return 1;
632 }
633
634 eap_server_register_methods();
635 radius_init(&state);
636
637 while ((ch = getopt(argc, argv, "6C:c:d:i:k:K:p:P:s:u:")) != -1) {
638 switch (ch) {
639 case '6':
640 config.radius.ipv6 = 1;
641 break;
642 case 'C':
643 config.tls.ca_cert = optarg;
644 break;
645 case 'c':
646 if (config.tls.client_cert2)
647 return usage(progname);
648
649 if (config.tls.client_cert)
650 config.tls.client_cert2 = optarg;
651 else
652 config.tls.client_cert = optarg;
653 break;
654 case 'd':
655 config.tls.dh_file = optarg;
656 break;
657 case 'i':
658 state.eap.server_id = optarg;
659 state.eap.server_id_len = strlen(optarg);
660 break;
661 case 'k':
662 if (config.tls.private_key2)
663 return usage(progname);
664
665 if (config.tls.private_key)
666 config.tls.private_key2 = optarg;
667 else
668 config.tls.private_key = optarg;
669 break;
670 case 'K':
671 if (config.tls.private_key_passwd2)
672 return usage(progname);
673
674 if (config.tls.private_key_passwd)
675 config.tls.private_key_passwd2 = optarg;
676 else
677 config.tls.private_key_passwd = optarg;
678 break;
679 case 'p':
680 config.radius.auth_port = atoi(optarg);
681 break;
682 case 'P':
683 config.radius.acct_port = atoi(optarg);
684 break;
685 case 's':
686 config.radius.client_file = optarg;
687 break;
688 case 'u':
689 state.user_file = optarg;
690 break;
691 default:
692 return usage(progname);
693 }
694 }
695
696 if (!config.tls.client_cert || !config.tls.private_key ||
697 !config.radius.client_file || !state.eap.server_id ||
698 !state.user_file) {
699 wpa_printf(MSG_INFO, "missing options\n");
700 goto out;
701 }
702
703 ret = radius_setup(&state, &config);
704 if (ret)
705 goto out;
706
707 load_userfile(&state);
708 eloop_run();
709
710 out:
711 radius_deinit(&state);
712 os_program_deinit();
713
714 return ret;
715 }