]>
Commit | Line | Data |
---|---|---|
6fc6879b | 1 | /* |
362bd35f | 2 | * RADIUS authentication server |
429ed54a | 3 | * Copyright (c) 2005-2009, 2011-2019, Jouni Malinen <j@w1.fi> |
6fc6879b | 4 | * |
0f3d578e JM |
5 | * This software may be distributed under the terms of the BSD license. |
6 | * See README for more details. | |
6fc6879b JM |
7 | */ |
8 | ||
9 | #include "includes.h" | |
10 | #include <net/if.h> | |
8a57da7e JM |
11 | #ifdef CONFIG_SQLITE |
12 | #include <sqlite3.h> | |
13 | #endif /* CONFIG_SQLITE */ | |
6fc6879b JM |
14 | |
15 | #include "common.h" | |
16 | #include "radius.h" | |
17 | #include "eloop.h" | |
6fc6879b | 18 | #include "eap_server/eap.h" |
d0ee16ed | 19 | #include "ap/ap_config.h" |
390b9291 | 20 | #include "crypto/tls.h" |
6fc6879b JM |
21 | #include "radius_server.h" |
22 | ||
362bd35f JM |
23 | /** |
24 | * RADIUS_SESSION_TIMEOUT - Session timeout in seconds | |
25 | */ | |
6fc6879b | 26 | #define RADIUS_SESSION_TIMEOUT 60 |
362bd35f | 27 | |
35677305 JM |
28 | /** |
29 | * RADIUS_SESSION_MAINTAIN - Completed session expiration timeout in seconds | |
30 | */ | |
31 | #define RADIUS_SESSION_MAINTAIN 5 | |
32 | ||
362bd35f JM |
33 | /** |
34 | * RADIUS_MAX_SESSION - Maximum number of active sessions | |
35 | */ | |
35677305 | 36 | #define RADIUS_MAX_SESSION 1000 |
362bd35f JM |
37 | |
38 | /** | |
39 | * RADIUS_MAX_MSG_LEN - Maximum message length for incoming RADIUS messages | |
40 | */ | |
6fc6879b JM |
41 | #define RADIUS_MAX_MSG_LEN 3000 |
42 | ||
8b423edb | 43 | static const struct eapol_callbacks radius_server_eapol_cb; |
6fc6879b JM |
44 | |
45 | struct radius_client; | |
46 | struct radius_server_data; | |
47 | ||
362bd35f JM |
48 | /** |
49 | * struct radius_server_counters - RADIUS server statistics counters | |
50 | */ | |
6fc6879b JM |
51 | struct radius_server_counters { |
52 | u32 access_requests; | |
53 | u32 invalid_requests; | |
54 | u32 dup_access_requests; | |
55 | u32 access_accepts; | |
56 | u32 access_rejects; | |
57 | u32 access_challenges; | |
58 | u32 malformed_access_requests; | |
59 | u32 bad_authenticators; | |
60 | u32 packets_dropped; | |
61 | u32 unknown_types; | |
a1dd890a JM |
62 | |
63 | u32 acct_requests; | |
64 | u32 invalid_acct_requests; | |
65 | u32 acct_responses; | |
66 | u32 malformed_acct_requests; | |
67 | u32 acct_bad_authenticators; | |
68 | u32 unknown_acct_types; | |
6fc6879b JM |
69 | }; |
70 | ||
362bd35f JM |
71 | /** |
72 | * struct radius_session - Internal RADIUS server data for a session | |
73 | */ | |
6fc6879b JM |
74 | struct radius_session { |
75 | struct radius_session *next; | |
76 | struct radius_client *client; | |
77 | struct radius_server_data *server; | |
78 | unsigned int sess_id; | |
79 | struct eap_sm *eap; | |
80 | struct eap_eapol_interface *eap_if; | |
8a57da7e JM |
81 | char *username; /* from User-Name attribute */ |
82 | char *nas_ip; | |
04ee197f | 83 | u8 mac_addr[ETH_ALEN]; /* from Calling-Station-Id attribute */ |
6fc6879b JM |
84 | |
85 | struct radius_msg *last_msg; | |
86 | char *last_from_addr; | |
87 | int last_from_port; | |
88 | struct sockaddr_storage last_from; | |
89 | socklen_t last_fromlen; | |
90 | u8 last_identifier; | |
91 | struct radius_msg *last_reply; | |
92 | u8 last_authenticator[16]; | |
8d2a9921 JM |
93 | |
94 | unsigned int remediation:1; | |
8943cc99 | 95 | unsigned int macacl:1; |
45260380 | 96 | unsigned int t_c_filtering:1; |
d0ee16ed JM |
97 | |
98 | struct hostapd_radius_attr *accept_attr; | |
45260380 JM |
99 | |
100 | u32 t_c_timestamp; /* Last read T&C timestamp from user DB */ | |
6fc6879b JM |
101 | }; |
102 | ||
362bd35f JM |
103 | /** |
104 | * struct radius_client - Internal RADIUS server data for a client | |
105 | */ | |
6fc6879b JM |
106 | struct radius_client { |
107 | struct radius_client *next; | |
108 | struct in_addr addr; | |
109 | struct in_addr mask; | |
110 | #ifdef CONFIG_IPV6 | |
111 | struct in6_addr addr6; | |
112 | struct in6_addr mask6; | |
113 | #endif /* CONFIG_IPV6 */ | |
114 | char *shared_secret; | |
115 | int shared_secret_len; | |
116 | struct radius_session *sessions; | |
117 | struct radius_server_counters counters; | |
abed6136 JM |
118 | |
119 | u8 next_dac_identifier; | |
120 | struct radius_msg *pending_dac_coa_req; | |
121 | u8 pending_dac_coa_id; | |
122 | u8 pending_dac_coa_addr[ETH_ALEN]; | |
123 | struct radius_msg *pending_dac_disconnect_req; | |
124 | u8 pending_dac_disconnect_id; | |
125 | u8 pending_dac_disconnect_addr[ETH_ALEN]; | |
6fc6879b JM |
126 | }; |
127 | ||
362bd35f JM |
128 | /** |
129 | * struct radius_server_data - Internal RADIUS server data | |
130 | */ | |
6fc6879b | 131 | struct radius_server_data { |
362bd35f JM |
132 | /** |
133 | * auth_sock - Socket for RADIUS authentication messages | |
134 | */ | |
6fc6879b | 135 | int auth_sock; |
362bd35f | 136 | |
a1dd890a JM |
137 | /** |
138 | * acct_sock - Socket for RADIUS accounting messages | |
139 | */ | |
140 | int acct_sock; | |
141 | ||
362bd35f JM |
142 | /** |
143 | * clients - List of authorized RADIUS clients | |
144 | */ | |
6fc6879b | 145 | struct radius_client *clients; |
362bd35f JM |
146 | |
147 | /** | |
148 | * next_sess_id - Next session identifier | |
149 | */ | |
6fc6879b | 150 | unsigned int next_sess_id; |
362bd35f JM |
151 | |
152 | /** | |
153 | * conf_ctx - Context pointer for callbacks | |
154 | * | |
155 | * This is used as the ctx argument in get_eap_user() calls. | |
156 | */ | |
6fc6879b | 157 | void *conf_ctx; |
362bd35f JM |
158 | |
159 | /** | |
160 | * num_sess - Number of active sessions | |
161 | */ | |
6fc6879b | 162 | int num_sess; |
362bd35f | 163 | |
d3bddd8b JM |
164 | const char *erp_domain; |
165 | ||
166 | struct dl_list erp_keys; /* struct eap_server_erp_key */ | |
167 | ||
362bd35f JM |
168 | /** |
169 | * ipv6 - Whether to enable IPv6 support in the RADIUS server | |
170 | */ | |
6fc6879b | 171 | int ipv6; |
362bd35f JM |
172 | |
173 | /** | |
174 | * start_time - Timestamp of server start | |
175 | */ | |
58707176 | 176 | struct os_reltime start_time; |
362bd35f JM |
177 | |
178 | /** | |
179 | * counters - Statistics counters for server operations | |
180 | * | |
181 | * These counters are the sum over all clients. | |
182 | */ | |
6fc6879b | 183 | struct radius_server_counters counters; |
362bd35f JM |
184 | |
185 | /** | |
186 | * get_eap_user - Callback for fetching EAP user information | |
187 | * @ctx: Context data from conf_ctx | |
188 | * @identity: User identity | |
189 | * @identity_len: identity buffer length in octets | |
190 | * @phase2: Whether this is for Phase 2 identity | |
191 | * @user: Data structure for filling in the user information | |
192 | * Returns: 0 on success, -1 on failure | |
193 | * | |
194 | * This is used to fetch information from user database. The callback | |
195 | * will fill in information about allowed EAP methods and the user | |
196 | * password. The password field will be an allocated copy of the | |
197 | * password data and RADIUS server will free it after use. | |
198 | */ | |
6fc6879b JM |
199 | int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len, |
200 | int phase2, struct eap_user *user); | |
362bd35f JM |
201 | |
202 | /** | |
203 | * eap_req_id_text - Optional data for EAP-Request/Identity | |
204 | * | |
205 | * This can be used to configure an optional, displayable message that | |
206 | * will be sent in EAP-Request/Identity. This string can contain an | |
207 | * ASCII-0 character (nul) to separate network infromation per RFC | |
208 | * 4284. The actual string length is explicit provided in | |
209 | * eap_req_id_text_len since nul character will not be used as a string | |
210 | * terminator. | |
211 | */ | |
65d50f0a | 212 | char *eap_req_id_text; |
362bd35f JM |
213 | |
214 | /** | |
215 | * eap_req_id_text_len - Length of eap_req_id_text buffer in octets | |
216 | */ | |
65d50f0a | 217 | size_t eap_req_id_text_len; |
bb437f28 | 218 | |
505a3694 JM |
219 | #ifdef CONFIG_RADIUS_TEST |
220 | char *dump_msk_file; | |
221 | #endif /* CONFIG_RADIUS_TEST */ | |
8d2a9921 JM |
222 | |
223 | char *subscr_remediation_url; | |
224 | u8 subscr_remediation_method; | |
7bd8c76a | 225 | char *hs20_sim_provisioning_url; |
8a57da7e | 226 | |
d4e39c51 JM |
227 | char *t_c_server_url; |
228 | ||
8a57da7e JM |
229 | #ifdef CONFIG_SQLITE |
230 | sqlite3 *db; | |
231 | #endif /* CONFIG_SQLITE */ | |
822e7c66 JM |
232 | |
233 | struct eap_config *eap_cfg; | |
6fc6879b JM |
234 | }; |
235 | ||
236 | ||
6fc6879b JM |
237 | #define RADIUS_DEBUG(args...) \ |
238 | wpa_printf(MSG_DEBUG, "RADIUS SRV: " args) | |
239 | #define RADIUS_ERROR(args...) \ | |
240 | wpa_printf(MSG_ERROR, "RADIUS SRV: " args) | |
241 | #define RADIUS_DUMP(args...) \ | |
242 | wpa_hexdump(MSG_MSGDUMP, "RADIUS SRV: " args) | |
243 | #define RADIUS_DUMP_ASCII(args...) \ | |
244 | wpa_hexdump_ascii(MSG_MSGDUMP, "RADIUS SRV: " args) | |
245 | ||
246 | ||
247 | static void radius_server_session_timeout(void *eloop_ctx, void *timeout_ctx); | |
f481459f JM |
248 | static void radius_server_session_remove_timeout(void *eloop_ctx, |
249 | void *timeout_ctx); | |
6fc6879b | 250 | |
7bd8c76a JM |
251 | #ifdef CONFIG_SQLITE |
252 | #ifdef CONFIG_HS20 | |
253 | ||
254 | static int db_table_exists(sqlite3 *db, const char *name) | |
255 | { | |
256 | char cmd[128]; | |
257 | ||
258 | os_snprintf(cmd, sizeof(cmd), "SELECT 1 FROM %s;", name); | |
259 | return sqlite3_exec(db, cmd, NULL, NULL, NULL) == SQLITE_OK; | |
260 | } | |
261 | ||
262 | ||
263 | static int db_table_create_sim_provisioning(sqlite3 *db) | |
264 | { | |
265 | char *err = NULL; | |
266 | const char *sql = | |
267 | "CREATE TABLE sim_provisioning(" | |
268 | " mobile_identifier_hash TEXT PRIMARY KEY," | |
269 | " imsi TEXT," | |
270 | " mac_addr TEXT," | |
271 | " eap_method TEXT," | |
272 | " timestamp TEXT" | |
273 | ");"; | |
274 | ||
275 | RADIUS_DEBUG("Adding database table for SIM provisioning information"); | |
276 | if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) { | |
277 | RADIUS_ERROR("SQLite error: %s", err); | |
278 | sqlite3_free(err); | |
279 | return -1; | |
280 | } | |
281 | ||
282 | return 0; | |
283 | } | |
284 | ||
285 | #endif /* CONFIG_HS20 */ | |
286 | #endif /* CONFIG_SQLITE */ | |
287 | ||
288 | ||
8a57da7e JM |
289 | void srv_log(struct radius_session *sess, const char *fmt, ...) |
290 | PRINTF_FORMAT(2, 3); | |
291 | ||
292 | void srv_log(struct radius_session *sess, const char *fmt, ...) | |
293 | { | |
294 | va_list ap; | |
295 | char *buf; | |
296 | int buflen; | |
297 | ||
298 | va_start(ap, fmt); | |
299 | buflen = vsnprintf(NULL, 0, fmt, ap) + 1; | |
300 | va_end(ap); | |
301 | ||
302 | buf = os_malloc(buflen); | |
303 | if (buf == NULL) | |
304 | return; | |
305 | va_start(ap, fmt); | |
306 | vsnprintf(buf, buflen, fmt, ap); | |
307 | va_end(ap); | |
308 | ||
309 | RADIUS_DEBUG("[0x%x %s] %s", sess->sess_id, sess->nas_ip, buf); | |
310 | ||
311 | #ifdef CONFIG_SQLITE | |
312 | if (sess->server->db) { | |
313 | char *sql; | |
314 | sql = sqlite3_mprintf("INSERT INTO authlog" | |
315 | "(timestamp,session,nas_ip,username,note)" | |
316 | " VALUES (" | |
317 | "strftime('%%Y-%%m-%%d %%H:%%M:%%f'," | |
318 | "'now'),%u,%Q,%Q,%Q)", | |
319 | sess->sess_id, sess->nas_ip, | |
320 | sess->username, buf); | |
321 | if (sql) { | |
322 | if (sqlite3_exec(sess->server->db, sql, NULL, NULL, | |
323 | NULL) != SQLITE_OK) { | |
324 | RADIUS_ERROR("Failed to add authlog entry into sqlite database: %s", | |
325 | sqlite3_errmsg(sess->server->db)); | |
326 | } | |
327 | sqlite3_free(sql); | |
328 | } | |
329 | } | |
330 | #endif /* CONFIG_SQLITE */ | |
331 | ||
332 | os_free(buf); | |
333 | } | |
334 | ||
6fc6879b JM |
335 | |
336 | static struct radius_client * | |
337 | radius_server_get_client(struct radius_server_data *data, struct in_addr *addr, | |
338 | int ipv6) | |
339 | { | |
340 | struct radius_client *client = data->clients; | |
341 | ||
342 | while (client) { | |
343 | #ifdef CONFIG_IPV6 | |
344 | if (ipv6) { | |
345 | struct in6_addr *addr6; | |
346 | int i; | |
347 | ||
348 | addr6 = (struct in6_addr *) addr; | |
349 | for (i = 0; i < 16; i++) { | |
350 | if ((addr6->s6_addr[i] & | |
351 | client->mask6.s6_addr[i]) != | |
352 | (client->addr6.s6_addr[i] & | |
353 | client->mask6.s6_addr[i])) { | |
354 | i = 17; | |
355 | break; | |
356 | } | |
357 | } | |
358 | if (i == 16) { | |
359 | break; | |
360 | } | |
361 | } | |
362 | #endif /* CONFIG_IPV6 */ | |
363 | if (!ipv6 && (client->addr.s_addr & client->mask.s_addr) == | |
364 | (addr->s_addr & client->mask.s_addr)) { | |
365 | break; | |
366 | } | |
367 | ||
368 | client = client->next; | |
369 | } | |
370 | ||
371 | return client; | |
372 | } | |
373 | ||
374 | ||
375 | static struct radius_session * | |
376 | radius_server_get_session(struct radius_client *client, unsigned int sess_id) | |
377 | { | |
378 | struct radius_session *sess = client->sessions; | |
379 | ||
380 | while (sess) { | |
381 | if (sess->sess_id == sess_id) { | |
382 | break; | |
383 | } | |
384 | sess = sess->next; | |
385 | } | |
386 | ||
387 | return sess; | |
388 | } | |
389 | ||
390 | ||
391 | static void radius_server_session_free(struct radius_server_data *data, | |
392 | struct radius_session *sess) | |
393 | { | |
394 | eloop_cancel_timeout(radius_server_session_timeout, data, sess); | |
f481459f | 395 | eloop_cancel_timeout(radius_server_session_remove_timeout, data, sess); |
6fc6879b | 396 | eap_server_sm_deinit(sess->eap); |
9e7245bd | 397 | radius_msg_free(sess->last_msg); |
6fc6879b | 398 | os_free(sess->last_from_addr); |
9e7245bd | 399 | radius_msg_free(sess->last_reply); |
8a57da7e JM |
400 | os_free(sess->username); |
401 | os_free(sess->nas_ip); | |
6fc6879b JM |
402 | os_free(sess); |
403 | data->num_sess--; | |
404 | } | |
405 | ||
406 | ||
6fc6879b JM |
407 | static void radius_server_session_remove(struct radius_server_data *data, |
408 | struct radius_session *sess) | |
409 | { | |
410 | struct radius_client *client = sess->client; | |
411 | struct radius_session *session, *prev; | |
412 | ||
413 | eloop_cancel_timeout(radius_server_session_remove_timeout, data, sess); | |
414 | ||
415 | prev = NULL; | |
416 | session = client->sessions; | |
417 | while (session) { | |
418 | if (session == sess) { | |
419 | if (prev == NULL) { | |
420 | client->sessions = sess->next; | |
421 | } else { | |
422 | prev->next = sess->next; | |
423 | } | |
424 | radius_server_session_free(data, sess); | |
425 | break; | |
426 | } | |
427 | prev = session; | |
428 | session = session->next; | |
429 | } | |
430 | } | |
431 | ||
432 | ||
433 | static void radius_server_session_remove_timeout(void *eloop_ctx, | |
434 | void *timeout_ctx) | |
435 | { | |
436 | struct radius_server_data *data = eloop_ctx; | |
437 | struct radius_session *sess = timeout_ctx; | |
438 | RADIUS_DEBUG("Removing completed session 0x%x", sess->sess_id); | |
439 | radius_server_session_remove(data, sess); | |
440 | } | |
441 | ||
442 | ||
443 | static void radius_server_session_timeout(void *eloop_ctx, void *timeout_ctx) | |
444 | { | |
445 | struct radius_server_data *data = eloop_ctx; | |
446 | struct radius_session *sess = timeout_ctx; | |
447 | ||
448 | RADIUS_DEBUG("Timing out authentication session 0x%x", sess->sess_id); | |
449 | radius_server_session_remove(data, sess); | |
450 | } | |
451 | ||
452 | ||
453 | static struct radius_session * | |
454 | radius_server_new_session(struct radius_server_data *data, | |
455 | struct radius_client *client) | |
456 | { | |
457 | struct radius_session *sess; | |
458 | ||
459 | if (data->num_sess >= RADIUS_MAX_SESSION) { | |
460 | RADIUS_DEBUG("Maximum number of existing session - no room " | |
461 | "for a new session"); | |
462 | return NULL; | |
463 | } | |
464 | ||
465 | sess = os_zalloc(sizeof(*sess)); | |
466 | if (sess == NULL) | |
467 | return NULL; | |
468 | ||
469 | sess->server = data; | |
470 | sess->client = client; | |
471 | sess->sess_id = data->next_sess_id++; | |
472 | sess->next = client->sessions; | |
473 | client->sessions = sess; | |
474 | eloop_register_timeout(RADIUS_SESSION_TIMEOUT, 0, | |
475 | radius_server_session_timeout, data, sess); | |
476 | data->num_sess++; | |
477 | return sess; | |
478 | } | |
479 | ||
480 | ||
390b9291 JM |
481 | #ifdef CONFIG_TESTING_OPTIONS |
482 | static void radius_server_testing_options_tls(struct radius_session *sess, | |
483 | const char *tls, | |
822e7c66 | 484 | struct eap_session_data *eap_conf) |
390b9291 JM |
485 | { |
486 | int test = atoi(tls); | |
487 | ||
488 | switch (test) { | |
489 | case 1: | |
490 | srv_log(sess, "TLS test - break VerifyData"); | |
491 | eap_conf->tls_test_flags = TLS_BREAK_VERIFY_DATA; | |
492 | break; | |
493 | case 2: | |
494 | srv_log(sess, "TLS test - break ServerKeyExchange ServerParams hash"); | |
495 | eap_conf->tls_test_flags = TLS_BREAK_SRV_KEY_X_HASH; | |
496 | break; | |
497 | case 3: | |
498 | srv_log(sess, "TLS test - break ServerKeyExchange ServerParams Signature"); | |
499 | eap_conf->tls_test_flags = TLS_BREAK_SRV_KEY_X_SIGNATURE; | |
500 | break; | |
47bd94a0 JM |
501 | case 4: |
502 | srv_log(sess, "TLS test - RSA-DHE using a short 511-bit prime"); | |
503 | eap_conf->tls_test_flags = TLS_DHE_PRIME_511B; | |
504 | break; | |
505 | case 5: | |
506 | srv_log(sess, "TLS test - RSA-DHE using a short 767-bit prime"); | |
507 | eap_conf->tls_test_flags = TLS_DHE_PRIME_767B; | |
508 | break; | |
509 | case 6: | |
510 | srv_log(sess, "TLS test - RSA-DHE using a bogus 15 \"prime\""); | |
511 | eap_conf->tls_test_flags = TLS_DHE_PRIME_15; | |
512 | break; | |
513 | case 7: | |
514 | srv_log(sess, "TLS test - RSA-DHE using a short 58-bit prime in long container"); | |
515 | eap_conf->tls_test_flags = TLS_DHE_PRIME_58B; | |
516 | break; | |
517 | case 8: | |
518 | srv_log(sess, "TLS test - RSA-DHE using a non-prime"); | |
519 | eap_conf->tls_test_flags = TLS_DHE_NON_PRIME; | |
520 | break; | |
390b9291 JM |
521 | default: |
522 | srv_log(sess, "Unrecognized TLS test"); | |
523 | break; | |
524 | } | |
525 | } | |
526 | #endif /* CONFIG_TESTING_OPTIONS */ | |
527 | ||
528 | static void radius_server_testing_options(struct radius_session *sess, | |
822e7c66 | 529 | struct eap_session_data *eap_conf) |
390b9291 JM |
530 | { |
531 | #ifdef CONFIG_TESTING_OPTIONS | |
532 | const char *pos; | |
533 | ||
534 | pos = os_strstr(sess->username, "@test-"); | |
535 | if (pos == NULL) | |
536 | return; | |
537 | pos += 6; | |
538 | if (os_strncmp(pos, "tls-", 4) == 0) | |
539 | radius_server_testing_options_tls(sess, pos + 4, eap_conf); | |
540 | else | |
541 | srv_log(sess, "Unrecognized test: %s", pos); | |
542 | #endif /* CONFIG_TESTING_OPTIONS */ | |
543 | } | |
544 | ||
545 | ||
3580ed82 JM |
546 | #ifdef CONFIG_ERP |
547 | static struct eap_server_erp_key * | |
548 | radius_server_erp_find_key(struct radius_server_data *data, const char *keyname) | |
549 | { | |
550 | struct eap_server_erp_key *erp; | |
551 | ||
552 | dl_list_for_each(erp, &data->erp_keys, struct eap_server_erp_key, | |
553 | list) { | |
554 | if (os_strcmp(erp->keyname_nai, keyname) == 0) | |
555 | return erp; | |
556 | } | |
557 | ||
558 | return NULL; | |
559 | } | |
560 | #endif /* CONFIG_ERP */ | |
561 | ||
562 | ||
6fc6879b JM |
563 | static struct radius_session * |
564 | radius_server_get_new_session(struct radius_server_data *data, | |
565 | struct radius_client *client, | |
8a57da7e | 566 | struct radius_msg *msg, const char *from_addr) |
6fc6879b | 567 | { |
04ee197f JM |
568 | u8 *user, *id; |
569 | size_t user_len, id_len; | |
6fc6879b JM |
570 | int res; |
571 | struct radius_session *sess; | |
822e7c66 | 572 | struct eap_session_data eap_sess; |
1e653daa | 573 | struct eap_user *tmp; |
6fc6879b JM |
574 | |
575 | RADIUS_DEBUG("Creating a new session"); | |
576 | ||
8a57da7e JM |
577 | if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME, &user, |
578 | &user_len, NULL) < 0) { | |
6fc6879b | 579 | RADIUS_DEBUG("Could not get User-Name"); |
6fc6879b JM |
580 | return NULL; |
581 | } | |
6fc6879b JM |
582 | RADIUS_DUMP_ASCII("User-Name", user, user_len); |
583 | ||
1e653daa MH |
584 | tmp = os_zalloc(sizeof(*tmp)); |
585 | if (!tmp) | |
586 | return NULL; | |
6fc6879b | 587 | |
1e653daa | 588 | res = data->get_eap_user(data->conf_ctx, user, user_len, 0, tmp); |
3580ed82 | 589 | #ifdef CONFIG_ERP |
822e7c66 | 590 | if (res != 0 && data->eap_cfg->erp) { |
3580ed82 JM |
591 | char *username; |
592 | ||
593 | username = os_zalloc(user_len + 1); | |
594 | if (username) { | |
595 | os_memcpy(username, user, user_len); | |
596 | if (radius_server_erp_find_key(data, username)) | |
597 | res = 0; | |
598 | os_free(username); | |
599 | } | |
600 | } | |
601 | #endif /* CONFIG_ERP */ | |
8a57da7e | 602 | if (res != 0) { |
6fc6879b | 603 | RADIUS_DEBUG("User-Name not found from user database"); |
1e653daa | 604 | eap_user_free(tmp); |
6fc6879b JM |
605 | return NULL; |
606 | } | |
607 | ||
8a57da7e JM |
608 | RADIUS_DEBUG("Matching user entry found"); |
609 | sess = radius_server_new_session(data, client); | |
610 | if (sess == NULL) { | |
611 | RADIUS_DEBUG("Failed to create a new session"); | |
1e653daa | 612 | eap_user_free(tmp); |
8a57da7e JM |
613 | return NULL; |
614 | } | |
1e653daa MH |
615 | sess->accept_attr = tmp->accept_attr; |
616 | sess->macacl = tmp->macacl; | |
617 | eap_user_free(tmp); | |
8a57da7e | 618 | |
95f6f6a4 | 619 | sess->username = os_malloc(user_len * 4 + 1); |
8a57da7e | 620 | if (sess->username == NULL) { |
de01f254 | 621 | radius_server_session_remove(data, sess); |
8a57da7e JM |
622 | return NULL; |
623 | } | |
95f6f6a4 | 624 | printf_encode(sess->username, user_len * 4 + 1, user, user_len); |
8a57da7e JM |
625 | |
626 | sess->nas_ip = os_strdup(from_addr); | |
627 | if (sess->nas_ip == NULL) { | |
de01f254 | 628 | radius_server_session_remove(data, sess); |
8a57da7e JM |
629 | return NULL; |
630 | } | |
631 | ||
04ee197f JM |
632 | if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CALLING_STATION_ID, &id, |
633 | &id_len, NULL) == 0) { | |
634 | char buf[3 * ETH_ALEN]; | |
635 | ||
636 | os_memset(buf, 0, sizeof(buf)); | |
637 | if (id_len >= sizeof(buf)) | |
638 | id_len = sizeof(buf) - 1; | |
639 | os_memcpy(buf, id, id_len); | |
640 | if (hwaddr_aton2(buf, sess->mac_addr) < 0) | |
641 | os_memset(sess->mac_addr, 0, ETH_ALEN); | |
642 | else | |
643 | RADIUS_DEBUG("Calling-Station-Id: " MACSTR, | |
644 | MAC2STR(sess->mac_addr)); | |
645 | } | |
646 | ||
8a57da7e JM |
647 | srv_log(sess, "New session created"); |
648 | ||
822e7c66 JM |
649 | os_memset(&eap_sess, 0, sizeof(eap_sess)); |
650 | radius_server_testing_options(sess, &eap_sess); | |
6fc6879b | 651 | sess->eap = eap_server_sm_init(sess, &radius_server_eapol_cb, |
822e7c66 | 652 | data->eap_cfg, &eap_sess); |
6fc6879b JM |
653 | if (sess->eap == NULL) { |
654 | RADIUS_DEBUG("Failed to initialize EAP state machine for the " | |
655 | "new session"); | |
de01f254 | 656 | radius_server_session_remove(data, sess); |
6fc6879b JM |
657 | return NULL; |
658 | } | |
659 | sess->eap_if = eap_get_interface(sess->eap); | |
660 | sess->eap_if->eapRestart = TRUE; | |
661 | sess->eap_if->portEnabled = TRUE; | |
662 | ||
663 | RADIUS_DEBUG("New session 0x%x initialized", sess->sess_id); | |
664 | ||
665 | return sess; | |
666 | } | |
667 | ||
668 | ||
04ee197f JM |
669 | #ifdef CONFIG_HS20 |
670 | static void radius_srv_hs20_t_c_pending(struct radius_session *sess) | |
671 | { | |
672 | #ifdef CONFIG_SQLITE | |
673 | char *sql; | |
674 | char addr[3 * ETH_ALEN], *id_str; | |
675 | const u8 *id; | |
676 | size_t id_len; | |
677 | ||
678 | if (!sess->server->db || !sess->eap || | |
679 | is_zero_ether_addr(sess->mac_addr)) | |
680 | return; | |
681 | ||
682 | os_snprintf(addr, sizeof(addr), MACSTR, MAC2STR(sess->mac_addr)); | |
683 | ||
684 | id = eap_get_identity(sess->eap, &id_len); | |
685 | if (!id) | |
686 | return; | |
687 | id_str = os_malloc(id_len + 1); | |
688 | if (!id_str) | |
689 | return; | |
690 | os_memcpy(id_str, id, id_len); | |
691 | id_str[id_len] = '\0'; | |
692 | ||
693 | sql = sqlite3_mprintf("INSERT OR REPLACE INTO pending_tc (mac_addr,identity) VALUES (%Q,%Q)", | |
694 | addr, id_str); | |
695 | os_free(id_str); | |
696 | if (!sql) | |
697 | return; | |
698 | ||
699 | if (sqlite3_exec(sess->server->db, sql, NULL, NULL, NULL) != | |
700 | SQLITE_OK) { | |
701 | RADIUS_ERROR("Failed to add pending_tc entry into sqlite database: %s", | |
702 | sqlite3_errmsg(sess->server->db)); | |
703 | } | |
704 | sqlite3_free(sql); | |
705 | #endif /* CONFIG_SQLITE */ | |
706 | } | |
707 | #endif /* CONFIG_HS20 */ | |
708 | ||
709 | ||
2122fc83 JM |
710 | static void radius_server_add_session(struct radius_session *sess) |
711 | { | |
712 | #ifdef CONFIG_SQLITE | |
713 | char *sql; | |
714 | char addr_txt[ETH_ALEN * 3]; | |
715 | struct os_time now; | |
716 | ||
717 | if (!sess->server->db) | |
718 | return; | |
719 | ||
720 | ||
721 | os_snprintf(addr_txt, sizeof(addr_txt), MACSTR, | |
722 | MAC2STR(sess->mac_addr)); | |
723 | ||
724 | os_get_time(&now); | |
725 | sql = sqlite3_mprintf("INSERT OR REPLACE INTO current_sessions(mac_addr,identity,start_time,nas,hs20_t_c_filtering) VALUES (%Q,%Q,%d,%Q,%u)", | |
726 | addr_txt, sess->username, now.sec, | |
727 | sess->nas_ip, sess->t_c_filtering); | |
728 | if (sql) { | |
729 | if (sqlite3_exec(sess->server->db, sql, NULL, NULL, | |
730 | NULL) != SQLITE_OK) { | |
731 | RADIUS_ERROR("Failed to add current_sessions entry into sqlite database: %s", | |
732 | sqlite3_errmsg(sess->server->db)); | |
733 | } | |
734 | sqlite3_free(sql); | |
735 | } | |
736 | #endif /* CONFIG_SQLITE */ | |
737 | } | |
738 | ||
739 | ||
ad4e4f60 JM |
740 | static void db_update_last_msk(struct radius_session *sess, const char *msk) |
741 | { | |
742 | #ifdef CONFIG_RADIUS_TEST | |
743 | #ifdef CONFIG_SQLITE | |
744 | char *sql = NULL; | |
745 | char *id_str = NULL; | |
746 | const u8 *id; | |
747 | size_t id_len; | |
7770a9dd | 748 | const char *serial_num; |
ad4e4f60 JM |
749 | |
750 | if (!sess->server->db) | |
751 | return; | |
752 | ||
7770a9dd JM |
753 | serial_num = eap_get_serial_num(sess->eap); |
754 | if (serial_num) { | |
755 | id_len = 5 + os_strlen(serial_num) + 1; | |
756 | id_str = os_malloc(id_len); | |
757 | if (!id_str) | |
758 | return; | |
759 | os_snprintf(id_str, id_len, "cert-%s", serial_num); | |
760 | } else { | |
761 | id = eap_get_identity(sess->eap, &id_len); | |
762 | if (!id) | |
763 | return; | |
764 | id_str = os_malloc(id_len + 1); | |
765 | if (!id_str) | |
766 | return; | |
767 | os_memcpy(id_str, id, id_len); | |
768 | id_str[id_len] = '\0'; | |
769 | } | |
ad4e4f60 JM |
770 | |
771 | sql = sqlite3_mprintf("UPDATE users SET last_msk=%Q WHERE identity=%Q", | |
772 | msk, id_str); | |
773 | os_free(id_str); | |
774 | if (!sql) | |
775 | return; | |
776 | ||
777 | if (sqlite3_exec(sess->server->db, sql, NULL, NULL, NULL) != | |
778 | SQLITE_OK) { | |
779 | RADIUS_DEBUG("Failed to update last_msk: %s", | |
780 | sqlite3_errmsg(sess->server->db)); | |
781 | } | |
782 | sqlite3_free(sql); | |
783 | #endif /* CONFIG_SQLITE */ | |
784 | #endif /* CONFIG_RADIUS_TEST */ | |
785 | } | |
786 | ||
787 | ||
7bd8c76a JM |
788 | #ifdef CONFIG_HS20 |
789 | ||
790 | static int radius_server_is_sim_method(struct radius_session *sess) | |
791 | { | |
792 | const char *name; | |
793 | ||
794 | name = eap_get_method(sess->eap); | |
795 | return name && | |
796 | (os_strcmp(name, "SIM") == 0 || | |
797 | os_strcmp(name, "AKA") == 0 || | |
798 | os_strcmp(name, "AKA'") == 0); | |
799 | } | |
800 | ||
801 | ||
802 | static int radius_server_hs20_missing_sim_pps(struct radius_msg *request) | |
803 | { | |
804 | u8 *buf, *pos, *end, type, sublen; | |
805 | size_t len; | |
806 | ||
807 | buf = NULL; | |
808 | for (;;) { | |
809 | if (radius_msg_get_attr_ptr(request, | |
810 | RADIUS_ATTR_VENDOR_SPECIFIC, | |
811 | &buf, &len, buf) < 0) | |
812 | return 0; | |
813 | if (len < 6) | |
814 | continue; | |
815 | pos = buf; | |
816 | end = buf + len; | |
817 | if (WPA_GET_BE32(pos) != RADIUS_VENDOR_ID_WFA) | |
818 | continue; | |
819 | pos += 4; | |
820 | ||
821 | type = *pos++; | |
822 | sublen = *pos++; | |
823 | if (sublen < 2) | |
824 | continue; /* invalid length */ | |
825 | sublen -= 2; /* skip header */ | |
826 | if (pos + sublen > end) | |
827 | continue; /* invalid WFA VSA */ | |
828 | ||
829 | if (type != RADIUS_VENDOR_ATTR_WFA_HS20_STA_VERSION) | |
830 | continue; | |
831 | ||
832 | RADIUS_DUMP("HS2.0 mobile device version", pos, sublen); | |
833 | if (sublen < 1 + 2) | |
834 | continue; | |
835 | if (pos[0] == 0) | |
836 | continue; /* Release 1 STA does not support provisioning | |
837 | ||
838 | */ | |
839 | /* UpdateIdentifier 0 indicates no PPS MO */ | |
840 | return WPA_GET_BE16(pos + 1) == 0; | |
841 | } | |
842 | } | |
843 | ||
844 | ||
845 | #define HS20_MOBILE_ID_HASH_LEN 16 | |
846 | ||
847 | static int radius_server_sim_provisioning_session(struct radius_session *sess, | |
848 | const u8 *hash) | |
849 | { | |
850 | #ifdef CONFIG_SQLITE | |
851 | char *sql; | |
852 | char addr_txt[ETH_ALEN * 3]; | |
853 | char hash_txt[2 * HS20_MOBILE_ID_HASH_LEN + 1]; | |
854 | struct os_time now; | |
855 | int res; | |
856 | const char *imsi, *eap_method; | |
857 | ||
858 | if (!sess->server->db || | |
859 | (!db_table_exists(sess->server->db, "sim_provisioning") && | |
860 | db_table_create_sim_provisioning(sess->server->db) < 0)) | |
861 | return -1; | |
862 | ||
863 | imsi = eap_get_imsi(sess->eap); | |
864 | if (!imsi) | |
865 | return -1; | |
866 | ||
867 | eap_method = eap_get_method(sess->eap); | |
868 | if (!eap_method) | |
869 | return -1; | |
870 | ||
871 | os_snprintf(addr_txt, sizeof(addr_txt), MACSTR, | |
872 | MAC2STR(sess->mac_addr)); | |
873 | wpa_snprintf_hex(hash_txt, sizeof(hash_txt), hash, | |
874 | HS20_MOBILE_ID_HASH_LEN); | |
875 | ||
876 | os_get_time(&now); | |
877 | sql = sqlite3_mprintf("INSERT INTO sim_provisioning(mobile_identifier_hash,imsi,mac_addr,eap_method,timestamp) VALUES (%Q,%Q,%Q,%Q,%u)", | |
878 | hash_txt, imsi, addr_txt, eap_method, now.sec); | |
879 | if (!sql) | |
880 | return -1; | |
881 | ||
882 | if (sqlite3_exec(sess->server->db, sql, NULL, NULL, NULL) != | |
883 | SQLITE_OK) { | |
884 | RADIUS_ERROR("Failed to add SIM provisioning entry into sqlite database: %s", | |
885 | sqlite3_errmsg(sess->server->db)); | |
886 | res = -1; | |
887 | } else { | |
888 | res = 0; | |
889 | } | |
890 | sqlite3_free(sql); | |
891 | return res; | |
892 | #endif /* CONFIG_SQLITE */ | |
893 | return -1; | |
894 | } | |
895 | ||
896 | #endif /* CONFIG_HS20 */ | |
897 | ||
898 | ||
6fc6879b JM |
899 | static struct radius_msg * |
900 | radius_server_encapsulate_eap(struct radius_server_data *data, | |
901 | struct radius_client *client, | |
902 | struct radius_session *sess, | |
903 | struct radius_msg *request) | |
904 | { | |
905 | struct radius_msg *msg; | |
906 | int code; | |
907 | unsigned int sess_id; | |
1489e11a | 908 | struct radius_hdr *hdr = radius_msg_get_hdr(request); |
f75ed556 | 909 | u16 reason = WLAN_REASON_IEEE_802_1X_AUTH_FAILED; |
6fc6879b JM |
910 | |
911 | if (sess->eap_if->eapFail) { | |
912 | sess->eap_if->eapFail = FALSE; | |
913 | code = RADIUS_CODE_ACCESS_REJECT; | |
914 | } else if (sess->eap_if->eapSuccess) { | |
915 | sess->eap_if->eapSuccess = FALSE; | |
916 | code = RADIUS_CODE_ACCESS_ACCEPT; | |
917 | } else { | |
918 | sess->eap_if->eapReq = FALSE; | |
919 | code = RADIUS_CODE_ACCESS_CHALLENGE; | |
920 | } | |
921 | ||
1489e11a | 922 | msg = radius_msg_new(code, hdr->identifier); |
6fc6879b JM |
923 | if (msg == NULL) { |
924 | RADIUS_DEBUG("Failed to allocate reply message"); | |
925 | return NULL; | |
926 | } | |
927 | ||
928 | sess_id = htonl(sess->sess_id); | |
929 | if (code == RADIUS_CODE_ACCESS_CHALLENGE && | |
930 | !radius_msg_add_attr(msg, RADIUS_ATTR_STATE, | |
931 | (u8 *) &sess_id, sizeof(sess_id))) { | |
932 | RADIUS_DEBUG("Failed to add State attribute"); | |
933 | } | |
934 | ||
935 | if (sess->eap_if->eapReqData && | |
936 | !radius_msg_add_eap(msg, wpabuf_head(sess->eap_if->eapReqData), | |
937 | wpabuf_len(sess->eap_if->eapReqData))) { | |
938 | RADIUS_DEBUG("Failed to add EAP-Message attribute"); | |
939 | } | |
940 | ||
941 | if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->eap_if->eapKeyData) { | |
942 | int len; | |
505a3694 | 943 | #ifdef CONFIG_RADIUS_TEST |
ad4e4f60 JM |
944 | char buf[2 * 64 + 1]; |
945 | ||
946 | len = sess->eap_if->eapKeyDataLen; | |
947 | if (len > 64) | |
948 | len = 64; | |
949 | len = wpa_snprintf_hex(buf, sizeof(buf), | |
950 | sess->eap_if->eapKeyData, len); | |
951 | buf[len] = '\0'; | |
952 | ||
505a3694 JM |
953 | if (data->dump_msk_file) { |
954 | FILE *f; | |
ad4e4f60 | 955 | |
505a3694 JM |
956 | f = fopen(data->dump_msk_file, "a"); |
957 | if (f) { | |
958 | len = sess->eap_if->eapKeyDataLen; | |
959 | if (len > 64) | |
960 | len = 64; | |
961 | len = wpa_snprintf_hex( | |
962 | buf, sizeof(buf), | |
963 | sess->eap_if->eapKeyData, len); | |
964 | buf[len] = '\0'; | |
965 | fprintf(f, "%s\n", buf); | |
966 | fclose(f); | |
967 | } | |
968 | } | |
ad4e4f60 JM |
969 | |
970 | db_update_last_msk(sess, buf); | |
505a3694 | 971 | #endif /* CONFIG_RADIUS_TEST */ |
6fc6879b JM |
972 | if (sess->eap_if->eapKeyDataLen > 64) { |
973 | len = 32; | |
974 | } else { | |
975 | len = sess->eap_if->eapKeyDataLen / 2; | |
976 | } | |
1489e11a | 977 | if (!radius_msg_add_mppe_keys(msg, hdr->authenticator, |
6fc6879b JM |
978 | (u8 *) client->shared_secret, |
979 | client->shared_secret_len, | |
980 | sess->eap_if->eapKeyData + len, | |
981 | len, sess->eap_if->eapKeyData, | |
982 | len)) { | |
983 | RADIUS_DEBUG("Failed to add MPPE key attributes"); | |
984 | } | |
59fcb3f0 JM |
985 | |
986 | if (sess->eap_if->eapSessionId && | |
987 | !radius_msg_add_attr(msg, RADIUS_ATTR_EAP_KEY_NAME, | |
988 | sess->eap_if->eapSessionId, | |
989 | sess->eap_if->eapSessionIdLen)) { | |
990 | RADIUS_DEBUG("Failed to add EAP-Key-Name attribute"); | |
991 | } | |
6fc6879b JM |
992 | } |
993 | ||
8d2a9921 JM |
994 | #ifdef CONFIG_HS20 |
995 | if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->remediation && | |
996 | data->subscr_remediation_url) { | |
997 | u8 *buf; | |
998 | size_t url_len = os_strlen(data->subscr_remediation_url); | |
999 | buf = os_malloc(1 + url_len); | |
1000 | if (buf == NULL) { | |
1001 | radius_msg_free(msg); | |
1002 | return NULL; | |
1003 | } | |
1004 | buf[0] = data->subscr_remediation_method; | |
1005 | os_memcpy(&buf[1], data->subscr_remediation_url, url_len); | |
1006 | if (!radius_msg_add_wfa( | |
1007 | msg, RADIUS_VENDOR_ATTR_WFA_HS20_SUBSCR_REMEDIATION, | |
1008 | buf, 1 + url_len)) { | |
1009 | RADIUS_DEBUG("Failed to add WFA-HS20-SubscrRem"); | |
1010 | } | |
1011 | os_free(buf); | |
1012 | } else if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->remediation) { | |
1013 | u8 buf[1]; | |
1014 | if (!radius_msg_add_wfa( | |
1015 | msg, RADIUS_VENDOR_ATTR_WFA_HS20_SUBSCR_REMEDIATION, | |
1016 | buf, 0)) { | |
1017 | RADIUS_DEBUG("Failed to add WFA-HS20-SubscrRem"); | |
1018 | } | |
7bd8c76a JM |
1019 | } else if (code == RADIUS_CODE_ACCESS_ACCEPT && |
1020 | data->hs20_sim_provisioning_url && | |
1021 | radius_server_is_sim_method(sess) && | |
1022 | radius_server_hs20_missing_sim_pps(request)) { | |
1023 | u8 *buf, *pos, hash[HS20_MOBILE_ID_HASH_LEN]; | |
1024 | size_t prefix_len, url_len; | |
1025 | ||
1026 | RADIUS_DEBUG("Device needs HS 2.0 SIM provisioning"); | |
1027 | ||
1028 | if (os_get_random(hash, HS20_MOBILE_ID_HASH_LEN) < 0) { | |
1029 | radius_msg_free(msg); | |
1030 | return NULL; | |
1031 | } | |
1032 | RADIUS_DUMP("hotspot2dot0-mobile-identifier-hash", | |
1033 | hash, HS20_MOBILE_ID_HASH_LEN); | |
1034 | ||
1035 | if (radius_server_sim_provisioning_session(sess, hash) < 0) { | |
1036 | radius_msg_free(msg); | |
1037 | return NULL; | |
1038 | } | |
1039 | ||
1040 | prefix_len = os_strlen(data->hs20_sim_provisioning_url); | |
1041 | url_len = prefix_len + 2 * HS20_MOBILE_ID_HASH_LEN; | |
1042 | buf = os_malloc(1 + url_len + 1); | |
1043 | if (!buf) { | |
1044 | radius_msg_free(msg); | |
1045 | return NULL; | |
1046 | } | |
1047 | pos = buf; | |
1048 | *pos++ = data->subscr_remediation_method; | |
1049 | os_memcpy(pos, data->hs20_sim_provisioning_url, prefix_len); | |
1050 | pos += prefix_len; | |
1051 | wpa_snprintf_hex((char *) pos, 2 * HS20_MOBILE_ID_HASH_LEN + 1, | |
1052 | hash, HS20_MOBILE_ID_HASH_LEN); | |
1053 | RADIUS_DEBUG("HS 2.0 subscription remediation URL: %s", | |
1054 | (char *) &buf[1]); | |
1055 | if (!radius_msg_add_wfa( | |
1056 | msg, RADIUS_VENDOR_ATTR_WFA_HS20_SUBSCR_REMEDIATION, | |
1057 | buf, 1 + url_len)) { | |
1058 | RADIUS_DEBUG("Failed to add WFA-HS20-SubscrRem"); | |
1059 | } | |
1060 | os_free(buf); | |
8d2a9921 | 1061 | } |
45260380 JM |
1062 | |
1063 | if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->t_c_filtering) { | |
1064 | u8 buf[4] = { 0x01, 0x00, 0x00, 0x00 }; /* E=1 */ | |
d4e39c51 JM |
1065 | const char *url = data->t_c_server_url, *pos; |
1066 | char *url2, *end2, *pos2; | |
1067 | size_t url_len; | |
45260380 JM |
1068 | |
1069 | if (!radius_msg_add_wfa( | |
1070 | msg, RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILTERING, | |
1071 | buf, sizeof(buf))) { | |
1072 | RADIUS_DEBUG("Failed to add WFA-HS20-T-C-Filtering"); | |
d4e39c51 JM |
1073 | radius_msg_free(msg); |
1074 | return NULL; | |
1075 | } | |
1076 | ||
1077 | if (!url) { | |
1078 | RADIUS_DEBUG("No t_c_server_url configured"); | |
1079 | radius_msg_free(msg); | |
1080 | return NULL; | |
1081 | } | |
1082 | ||
1083 | pos = os_strstr(url, "@1@"); | |
1084 | if (!pos) { | |
1085 | RADIUS_DEBUG("No @1@ macro in t_c_server_url"); | |
1086 | radius_msg_free(msg); | |
1087 | return NULL; | |
1088 | } | |
1089 | ||
1090 | url_len = os_strlen(url) + ETH_ALEN * 3 - 1 - 3; | |
5ca11965 | 1091 | url2 = os_malloc(url_len + 1); |
d4e39c51 JM |
1092 | if (!url2) { |
1093 | RADIUS_DEBUG("Failed to allocate room for T&C Server URL"); | |
1094 | os_free(url2); | |
1095 | radius_msg_free(msg); | |
1096 | return NULL; | |
45260380 | 1097 | } |
d4e39c51 | 1098 | pos2 = url2; |
5ca11965 | 1099 | end2 = url2 + url_len + 1; |
d4e39c51 JM |
1100 | os_memcpy(pos2, url, pos - url); |
1101 | pos2 += pos - url; | |
1102 | os_snprintf(pos2, end2 - pos2, MACSTR, MAC2STR(sess->mac_addr)); | |
1103 | pos2 += ETH_ALEN * 3 - 1; | |
1104 | os_memcpy(pos2, pos + 3, os_strlen(pos + 3)); | |
1105 | if (!radius_msg_add_wfa(msg, | |
1106 | RADIUS_VENDOR_ATTR_WFA_HS20_T_C_URL, | |
1107 | (const u8 *) url2, url_len)) { | |
1108 | RADIUS_DEBUG("Failed to add WFA-HS20-T-C-URL"); | |
1109 | os_free(url2); | |
1110 | radius_msg_free(msg); | |
1111 | return NULL; | |
1112 | } | |
1113 | os_free(url2); | |
1114 | ||
04ee197f | 1115 | radius_srv_hs20_t_c_pending(sess); |
45260380 | 1116 | } |
8d2a9921 JM |
1117 | #endif /* CONFIG_HS20 */ |
1118 | ||
6fc6879b JM |
1119 | if (radius_msg_copy_attr(msg, request, RADIUS_ATTR_PROXY_STATE) < 0) { |
1120 | RADIUS_DEBUG("Failed to copy Proxy-State attribute(s)"); | |
1121 | radius_msg_free(msg); | |
6fc6879b JM |
1122 | return NULL; |
1123 | } | |
1124 | ||
d0ee16ed JM |
1125 | if (code == RADIUS_CODE_ACCESS_ACCEPT) { |
1126 | struct hostapd_radius_attr *attr; | |
1127 | for (attr = sess->accept_attr; attr; attr = attr->next) { | |
1128 | if (!radius_msg_add_attr(msg, attr->type, | |
1129 | wpabuf_head(attr->val), | |
1130 | wpabuf_len(attr->val))) { | |
1131 | wpa_printf(MSG_ERROR, "Could not add RADIUS attribute"); | |
1132 | radius_msg_free(msg); | |
1133 | return NULL; | |
1134 | } | |
1135 | } | |
1136 | } | |
1137 | ||
f75ed556 JM |
1138 | if (code == RADIUS_CODE_ACCESS_REJECT) { |
1139 | if (radius_msg_add_attr_int32(msg, RADIUS_ATTR_WLAN_REASON_CODE, | |
1140 | reason) < 0) { | |
1141 | RADIUS_DEBUG("Failed to add WLAN-Reason-Code attribute"); | |
1142 | radius_msg_free(msg); | |
1143 | return NULL; | |
1144 | } | |
1145 | } | |
1146 | ||
6fc6879b JM |
1147 | if (radius_msg_finish_srv(msg, (u8 *) client->shared_secret, |
1148 | client->shared_secret_len, | |
1489e11a | 1149 | hdr->authenticator) < 0) { |
6fc6879b JM |
1150 | RADIUS_DEBUG("Failed to add Message-Authenticator attribute"); |
1151 | } | |
1152 | ||
2122fc83 JM |
1153 | if (code == RADIUS_CODE_ACCESS_ACCEPT) |
1154 | radius_server_add_session(sess); | |
1155 | ||
6fc6879b JM |
1156 | return msg; |
1157 | } | |
1158 | ||
1159 | ||
8943cc99 JM |
1160 | static struct radius_msg * |
1161 | radius_server_macacl(struct radius_server_data *data, | |
1162 | struct radius_client *client, | |
1163 | struct radius_session *sess, | |
1164 | struct radius_msg *request) | |
1165 | { | |
1166 | struct radius_msg *msg; | |
1167 | int code; | |
1168 | struct radius_hdr *hdr = radius_msg_get_hdr(request); | |
1169 | u8 *pw; | |
1170 | size_t pw_len; | |
1171 | ||
1172 | code = RADIUS_CODE_ACCESS_ACCEPT; | |
1173 | ||
1174 | if (radius_msg_get_attr_ptr(request, RADIUS_ATTR_USER_PASSWORD, &pw, | |
1175 | &pw_len, NULL) < 0) { | |
1176 | RADIUS_DEBUG("Could not get User-Password"); | |
1177 | code = RADIUS_CODE_ACCESS_REJECT; | |
1178 | } else { | |
1179 | int res; | |
1180 | struct eap_user tmp; | |
1181 | ||
1182 | os_memset(&tmp, 0, sizeof(tmp)); | |
1183 | res = data->get_eap_user(data->conf_ctx, (u8 *) sess->username, | |
1184 | os_strlen(sess->username), 0, &tmp); | |
1185 | if (res || !tmp.macacl || tmp.password == NULL) { | |
1186 | RADIUS_DEBUG("No MAC ACL user entry"); | |
b7175b4d | 1187 | bin_clear_free(tmp.password, tmp.password_len); |
8943cc99 JM |
1188 | code = RADIUS_CODE_ACCESS_REJECT; |
1189 | } else { | |
1190 | u8 buf[128]; | |
1191 | res = radius_user_password_hide( | |
1192 | request, tmp.password, tmp.password_len, | |
1193 | (u8 *) client->shared_secret, | |
1194 | client->shared_secret_len, | |
1195 | buf, sizeof(buf)); | |
b7175b4d | 1196 | bin_clear_free(tmp.password, tmp.password_len); |
8943cc99 JM |
1197 | |
1198 | if (res < 0 || pw_len != (size_t) res || | |
c2371953 | 1199 | os_memcmp_const(pw, buf, res) != 0) { |
8943cc99 JM |
1200 | RADIUS_DEBUG("Incorrect User-Password"); |
1201 | code = RADIUS_CODE_ACCESS_REJECT; | |
1202 | } | |
1203 | } | |
1204 | } | |
1205 | ||
1206 | msg = radius_msg_new(code, hdr->identifier); | |
1207 | if (msg == NULL) { | |
1208 | RADIUS_DEBUG("Failed to allocate reply message"); | |
1209 | return NULL; | |
1210 | } | |
1211 | ||
1212 | if (radius_msg_copy_attr(msg, request, RADIUS_ATTR_PROXY_STATE) < 0) { | |
1213 | RADIUS_DEBUG("Failed to copy Proxy-State attribute(s)"); | |
1214 | radius_msg_free(msg); | |
1215 | return NULL; | |
1216 | } | |
1217 | ||
1218 | if (code == RADIUS_CODE_ACCESS_ACCEPT) { | |
1219 | struct hostapd_radius_attr *attr; | |
1220 | for (attr = sess->accept_attr; attr; attr = attr->next) { | |
1221 | if (!radius_msg_add_attr(msg, attr->type, | |
1222 | wpabuf_head(attr->val), | |
1223 | wpabuf_len(attr->val))) { | |
1224 | wpa_printf(MSG_ERROR, "Could not add RADIUS attribute"); | |
1225 | radius_msg_free(msg); | |
1226 | return NULL; | |
1227 | } | |
1228 | } | |
1229 | } | |
1230 | ||
1231 | if (radius_msg_finish_srv(msg, (u8 *) client->shared_secret, | |
1232 | client->shared_secret_len, | |
1233 | hdr->authenticator) < 0) { | |
1234 | RADIUS_DEBUG("Failed to add Message-Authenticator attribute"); | |
1235 | } | |
1236 | ||
1237 | return msg; | |
1238 | } | |
1239 | ||
1240 | ||
6fc6879b JM |
1241 | static int radius_server_reject(struct radius_server_data *data, |
1242 | struct radius_client *client, | |
1243 | struct radius_msg *request, | |
1244 | struct sockaddr *from, socklen_t fromlen, | |
1245 | const char *from_addr, int from_port) | |
1246 | { | |
1247 | struct radius_msg *msg; | |
1248 | int ret = 0; | |
1249 | struct eap_hdr eapfail; | |
1489e11a JM |
1250 | struct wpabuf *buf; |
1251 | struct radius_hdr *hdr = radius_msg_get_hdr(request); | |
6fc6879b JM |
1252 | |
1253 | RADIUS_DEBUG("Reject invalid request from %s:%d", | |
1254 | from_addr, from_port); | |
1255 | ||
1489e11a | 1256 | msg = radius_msg_new(RADIUS_CODE_ACCESS_REJECT, hdr->identifier); |
6fc6879b JM |
1257 | if (msg == NULL) { |
1258 | return -1; | |
1259 | } | |
1260 | ||
1261 | os_memset(&eapfail, 0, sizeof(eapfail)); | |
1262 | eapfail.code = EAP_CODE_FAILURE; | |
1263 | eapfail.identifier = 0; | |
1264 | eapfail.length = host_to_be16(sizeof(eapfail)); | |
1265 | ||
1266 | if (!radius_msg_add_eap(msg, (u8 *) &eapfail, sizeof(eapfail))) { | |
1267 | RADIUS_DEBUG("Failed to add EAP-Message attribute"); | |
1268 | } | |
1269 | ||
1270 | if (radius_msg_copy_attr(msg, request, RADIUS_ATTR_PROXY_STATE) < 0) { | |
1271 | RADIUS_DEBUG("Failed to copy Proxy-State attribute(s)"); | |
1272 | radius_msg_free(msg); | |
6fc6879b JM |
1273 | return -1; |
1274 | } | |
1275 | ||
1276 | if (radius_msg_finish_srv(msg, (u8 *) client->shared_secret, | |
1277 | client->shared_secret_len, | |
1489e11a JM |
1278 | hdr->authenticator) < |
1279 | 0) { | |
6fc6879b JM |
1280 | RADIUS_DEBUG("Failed to add Message-Authenticator attribute"); |
1281 | } | |
1282 | ||
1283 | if (wpa_debug_level <= MSG_MSGDUMP) { | |
1284 | radius_msg_dump(msg); | |
1285 | } | |
1286 | ||
1287 | data->counters.access_rejects++; | |
1288 | client->counters.access_rejects++; | |
1489e11a JM |
1289 | buf = radius_msg_get_buf(msg); |
1290 | if (sendto(data->auth_sock, wpabuf_head(buf), wpabuf_len(buf), 0, | |
6fc6879b | 1291 | (struct sockaddr *) from, sizeof(*from)) < 0) { |
61323e70 | 1292 | wpa_printf(MSG_INFO, "sendto[RADIUS SRV]: %s", strerror(errno)); |
6fc6879b JM |
1293 | ret = -1; |
1294 | } | |
1295 | ||
1296 | radius_msg_free(msg); | |
6fc6879b JM |
1297 | |
1298 | return ret; | |
1299 | } | |
1300 | ||
1301 | ||
45260380 JM |
1302 | static void radius_server_hs20_t_c_check(struct radius_session *sess, |
1303 | struct radius_msg *msg) | |
1304 | { | |
1305 | #ifdef CONFIG_HS20 | |
1306 | u8 *buf, *pos, *end, type, sublen, *timestamp = NULL; | |
1307 | size_t len; | |
1308 | ||
1309 | buf = NULL; | |
1310 | for (;;) { | |
1311 | if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_VENDOR_SPECIFIC, | |
1312 | &buf, &len, buf) < 0) | |
1313 | break; | |
1314 | if (len < 6) | |
1315 | continue; | |
1316 | pos = buf; | |
1317 | end = buf + len; | |
1318 | if (WPA_GET_BE32(pos) != RADIUS_VENDOR_ID_WFA) | |
1319 | continue; | |
1320 | pos += 4; | |
1321 | ||
1322 | type = *pos++; | |
1323 | sublen = *pos++; | |
1324 | if (sublen < 2) | |
1325 | continue; /* invalid length */ | |
1326 | sublen -= 2; /* skip header */ | |
1327 | if (pos + sublen > end) | |
1328 | continue; /* invalid WFA VSA */ | |
1329 | ||
1330 | if (type == RADIUS_VENDOR_ATTR_WFA_HS20_TIMESTAMP && len >= 4) { | |
1331 | timestamp = pos; | |
1332 | break; | |
1333 | } | |
1334 | } | |
1335 | ||
1336 | if (!timestamp) | |
1337 | return; | |
1338 | RADIUS_DEBUG("HS20-Timestamp: %u", WPA_GET_BE32(timestamp)); | |
1339 | if (sess->t_c_timestamp != WPA_GET_BE32(timestamp)) { | |
1340 | RADIUS_DEBUG("Last read T&C timestamp does not match HS20-Timestamp --> require filtering"); | |
1341 | sess->t_c_filtering = 1; | |
1342 | } | |
1343 | #endif /* CONFIG_HS20 */ | |
1344 | } | |
1345 | ||
1346 | ||
6fc6879b JM |
1347 | static int radius_server_request(struct radius_server_data *data, |
1348 | struct radius_msg *msg, | |
1349 | struct sockaddr *from, socklen_t fromlen, | |
1350 | struct radius_client *client, | |
1351 | const char *from_addr, int from_port, | |
1352 | struct radius_session *force_sess) | |
1353 | { | |
e100828b | 1354 | struct wpabuf *eap = NULL; |
6fc6879b JM |
1355 | int res, state_included = 0; |
1356 | u8 statebuf[4]; | |
1357 | unsigned int state; | |
1358 | struct radius_session *sess; | |
1359 | struct radius_msg *reply; | |
7598210b | 1360 | int is_complete = 0; |
6fc6879b JM |
1361 | |
1362 | if (force_sess) | |
1363 | sess = force_sess; | |
1364 | else { | |
1365 | res = radius_msg_get_attr(msg, RADIUS_ATTR_STATE, statebuf, | |
1366 | sizeof(statebuf)); | |
1367 | state_included = res >= 0; | |
1368 | if (res == sizeof(statebuf)) { | |
1369 | state = WPA_GET_BE32(statebuf); | |
1370 | sess = radius_server_get_session(client, state); | |
1371 | } else { | |
1372 | sess = NULL; | |
1373 | } | |
1374 | } | |
1375 | ||
1376 | if (sess) { | |
1377 | RADIUS_DEBUG("Request for session 0x%x", sess->sess_id); | |
1378 | } else if (state_included) { | |
1379 | RADIUS_DEBUG("State attribute included but no session found"); | |
1380 | radius_server_reject(data, client, msg, from, fromlen, | |
1381 | from_addr, from_port); | |
1382 | return -1; | |
1383 | } else { | |
8a57da7e JM |
1384 | sess = radius_server_get_new_session(data, client, msg, |
1385 | from_addr); | |
6fc6879b JM |
1386 | if (sess == NULL) { |
1387 | RADIUS_DEBUG("Could not create a new session"); | |
1388 | radius_server_reject(data, client, msg, from, fromlen, | |
1389 | from_addr, from_port); | |
1390 | return -1; | |
1391 | } | |
1392 | } | |
1393 | ||
1394 | if (sess->last_from_port == from_port && | |
1489e11a JM |
1395 | sess->last_identifier == radius_msg_get_hdr(msg)->identifier && |
1396 | os_memcmp(sess->last_authenticator, | |
1397 | radius_msg_get_hdr(msg)->authenticator, 16) == 0) { | |
6fc6879b JM |
1398 | RADIUS_DEBUG("Duplicate message from %s", from_addr); |
1399 | data->counters.dup_access_requests++; | |
1400 | client->counters.dup_access_requests++; | |
1401 | ||
1402 | if (sess->last_reply) { | |
1489e11a JM |
1403 | struct wpabuf *buf; |
1404 | buf = radius_msg_get_buf(sess->last_reply); | |
1405 | res = sendto(data->auth_sock, wpabuf_head(buf), | |
1406 | wpabuf_len(buf), 0, | |
6fc6879b JM |
1407 | (struct sockaddr *) from, fromlen); |
1408 | if (res < 0) { | |
61323e70 JM |
1409 | wpa_printf(MSG_INFO, "sendto[RADIUS SRV]: %s", |
1410 | strerror(errno)); | |
6fc6879b JM |
1411 | } |
1412 | return 0; | |
1413 | } | |
1414 | ||
1415 | RADIUS_DEBUG("No previous reply available for duplicate " | |
1416 | "message"); | |
1417 | return -1; | |
1418 | } | |
95de34a1 | 1419 | |
e100828b | 1420 | eap = radius_msg_get_eap(msg); |
8943cc99 JM |
1421 | if (eap == NULL && sess->macacl) { |
1422 | reply = radius_server_macacl(data, client, sess, msg); | |
1423 | if (reply == NULL) | |
1424 | return -1; | |
1425 | goto send_reply; | |
1426 | } | |
6fc6879b JM |
1427 | if (eap == NULL) { |
1428 | RADIUS_DEBUG("No EAP-Message in RADIUS packet from %s", | |
1429 | from_addr); | |
1430 | data->counters.packets_dropped++; | |
1431 | client->counters.packets_dropped++; | |
1432 | return -1; | |
1433 | } | |
1434 | ||
e100828b | 1435 | RADIUS_DUMP("Received EAP data", wpabuf_head(eap), wpabuf_len(eap)); |
6fc6879b JM |
1436 | |
1437 | /* FIX: if Code is Request, Success, or Failure, send Access-Reject; | |
1438 | * RFC3579 Sect. 2.6.2. | |
1439 | * Include EAP-Response/Nak with no preferred method if | |
1440 | * code == request. | |
1441 | * If code is not 1-4, discard the packet silently. | |
1442 | * Or is this already done by the EAP state machine? */ | |
1443 | ||
1444 | wpabuf_free(sess->eap_if->eapRespData); | |
e100828b | 1445 | sess->eap_if->eapRespData = eap; |
6fc6879b JM |
1446 | sess->eap_if->eapResp = TRUE; |
1447 | eap_server_sm_step(sess->eap); | |
1448 | ||
1449 | if ((sess->eap_if->eapReq || sess->eap_if->eapSuccess || | |
1450 | sess->eap_if->eapFail) && sess->eap_if->eapReqData) { | |
1451 | RADIUS_DUMP("EAP data from the state machine", | |
1452 | wpabuf_head(sess->eap_if->eapReqData), | |
1453 | wpabuf_len(sess->eap_if->eapReqData)); | |
1454 | } else if (sess->eap_if->eapFail) { | |
1455 | RADIUS_DEBUG("No EAP data from the state machine, but eapFail " | |
1456 | "set"); | |
1457 | } else if (eap_sm_method_pending(sess->eap)) { | |
9e7245bd | 1458 | radius_msg_free(sess->last_msg); |
6fc6879b JM |
1459 | sess->last_msg = msg; |
1460 | sess->last_from_port = from_port; | |
1461 | os_free(sess->last_from_addr); | |
1462 | sess->last_from_addr = os_strdup(from_addr); | |
1463 | sess->last_fromlen = fromlen; | |
1464 | os_memcpy(&sess->last_from, from, fromlen); | |
1465 | return -2; | |
1466 | } else { | |
1467 | RADIUS_DEBUG("No EAP data from the state machine - ignore this" | |
1468 | " Access-Request silently (assuming it was a " | |
1469 | "duplicate)"); | |
1470 | data->counters.packets_dropped++; | |
1471 | client->counters.packets_dropped++; | |
1472 | return -1; | |
1473 | } | |
1474 | ||
7598210b AB |
1475 | if (sess->eap_if->eapSuccess || sess->eap_if->eapFail) |
1476 | is_complete = 1; | |
ad4e4f60 | 1477 | if (sess->eap_if->eapFail) { |
8a57da7e | 1478 | srv_log(sess, "EAP authentication failed"); |
ad4e4f60 JM |
1479 | db_update_last_msk(sess, "FAIL"); |
1480 | } else if (sess->eap_if->eapSuccess) { | |
8a57da7e | 1481 | srv_log(sess, "EAP authentication succeeded"); |
ad4e4f60 | 1482 | } |
7598210b | 1483 | |
45260380 JM |
1484 | if (sess->eap_if->eapSuccess) |
1485 | radius_server_hs20_t_c_check(sess, msg); | |
1486 | ||
6fc6879b JM |
1487 | reply = radius_server_encapsulate_eap(data, client, sess, msg); |
1488 | ||
8943cc99 | 1489 | send_reply: |
6fc6879b | 1490 | if (reply) { |
1489e11a JM |
1491 | struct wpabuf *buf; |
1492 | struct radius_hdr *hdr; | |
1493 | ||
6fc6879b JM |
1494 | RADIUS_DEBUG("Reply to %s:%d", from_addr, from_port); |
1495 | if (wpa_debug_level <= MSG_MSGDUMP) { | |
1496 | radius_msg_dump(reply); | |
1497 | } | |
1498 | ||
1489e11a | 1499 | switch (radius_msg_get_hdr(reply)->code) { |
6fc6879b | 1500 | case RADIUS_CODE_ACCESS_ACCEPT: |
8a57da7e | 1501 | srv_log(sess, "Sending Access-Accept"); |
6fc6879b JM |
1502 | data->counters.access_accepts++; |
1503 | client->counters.access_accepts++; | |
1504 | break; | |
1505 | case RADIUS_CODE_ACCESS_REJECT: | |
8a57da7e | 1506 | srv_log(sess, "Sending Access-Reject"); |
6fc6879b JM |
1507 | data->counters.access_rejects++; |
1508 | client->counters.access_rejects++; | |
1509 | break; | |
1510 | case RADIUS_CODE_ACCESS_CHALLENGE: | |
1511 | data->counters.access_challenges++; | |
1512 | client->counters.access_challenges++; | |
1513 | break; | |
1514 | } | |
1489e11a JM |
1515 | buf = radius_msg_get_buf(reply); |
1516 | res = sendto(data->auth_sock, wpabuf_head(buf), | |
1517 | wpabuf_len(buf), 0, | |
6fc6879b JM |
1518 | (struct sockaddr *) from, fromlen); |
1519 | if (res < 0) { | |
61323e70 JM |
1520 | wpa_printf(MSG_INFO, "sendto[RADIUS SRV]: %s", |
1521 | strerror(errno)); | |
6fc6879b | 1522 | } |
9e7245bd | 1523 | radius_msg_free(sess->last_reply); |
6fc6879b JM |
1524 | sess->last_reply = reply; |
1525 | sess->last_from_port = from_port; | |
1489e11a JM |
1526 | hdr = radius_msg_get_hdr(msg); |
1527 | sess->last_identifier = hdr->identifier; | |
1528 | os_memcpy(sess->last_authenticator, hdr->authenticator, 16); | |
6fc6879b JM |
1529 | } else { |
1530 | data->counters.packets_dropped++; | |
1531 | client->counters.packets_dropped++; | |
1532 | } | |
1533 | ||
7598210b | 1534 | if (is_complete) { |
6fc6879b JM |
1535 | RADIUS_DEBUG("Removing completed session 0x%x after timeout", |
1536 | sess->sess_id); | |
1537 | eloop_cancel_timeout(radius_server_session_remove_timeout, | |
1538 | data, sess); | |
35677305 | 1539 | eloop_register_timeout(RADIUS_SESSION_MAINTAIN, 0, |
6fc6879b JM |
1540 | radius_server_session_remove_timeout, |
1541 | data, sess); | |
1542 | } | |
1543 | ||
1544 | return 0; | |
1545 | } | |
1546 | ||
1547 | ||
abed6136 JM |
1548 | static void |
1549 | radius_server_receive_disconnect_resp(struct radius_server_data *data, | |
1550 | struct radius_client *client, | |
1551 | struct radius_msg *msg, int ack) | |
1552 | { | |
1553 | struct radius_hdr *hdr; | |
1554 | ||
1555 | if (!client->pending_dac_disconnect_req) { | |
1556 | RADIUS_DEBUG("Ignore unexpected Disconnect response"); | |
1557 | radius_msg_free(msg); | |
1558 | return; | |
1559 | } | |
1560 | ||
1561 | hdr = radius_msg_get_hdr(msg); | |
1562 | if (hdr->identifier != client->pending_dac_disconnect_id) { | |
1563 | RADIUS_DEBUG("Ignore unexpected Disconnect response with unexpected identifier %u (expected %u)", | |
1564 | hdr->identifier, | |
1565 | client->pending_dac_disconnect_id); | |
1566 | radius_msg_free(msg); | |
1567 | return; | |
1568 | } | |
1569 | ||
1570 | if (radius_msg_verify(msg, (const u8 *) client->shared_secret, | |
1571 | client->shared_secret_len, | |
1572 | client->pending_dac_disconnect_req, 0)) { | |
1573 | RADIUS_DEBUG("Ignore Disconnect response with invalid authenticator"); | |
1574 | radius_msg_free(msg); | |
1575 | return; | |
1576 | } | |
1577 | ||
1578 | RADIUS_DEBUG("Disconnect-%s received for " MACSTR, | |
1579 | ack ? "ACK" : "NAK", | |
1580 | MAC2STR(client->pending_dac_disconnect_addr)); | |
1581 | ||
1582 | radius_msg_free(msg); | |
1583 | radius_msg_free(client->pending_dac_disconnect_req); | |
1584 | client->pending_dac_disconnect_req = NULL; | |
1585 | } | |
1586 | ||
1587 | ||
1588 | static void radius_server_receive_coa_resp(struct radius_server_data *data, | |
1589 | struct radius_client *client, | |
1590 | struct radius_msg *msg, int ack) | |
1591 | { | |
1592 | struct radius_hdr *hdr; | |
1593 | #ifdef CONFIG_SQLITE | |
1594 | char addrtxt[3 * ETH_ALEN]; | |
1595 | char *sql; | |
1596 | int res; | |
1597 | #endif /* CONFIG_SQLITE */ | |
1598 | ||
1599 | if (!client->pending_dac_coa_req) { | |
1600 | RADIUS_DEBUG("Ignore unexpected CoA response"); | |
1601 | radius_msg_free(msg); | |
1602 | return; | |
1603 | } | |
1604 | ||
1605 | hdr = radius_msg_get_hdr(msg); | |
1606 | if (hdr->identifier != client->pending_dac_coa_id) { | |
1607 | RADIUS_DEBUG("Ignore unexpected CoA response with unexpected identifier %u (expected %u)", | |
1608 | hdr->identifier, | |
1609 | client->pending_dac_coa_id); | |
1610 | radius_msg_free(msg); | |
1611 | return; | |
1612 | } | |
1613 | ||
1614 | if (radius_msg_verify(msg, (const u8 *) client->shared_secret, | |
1615 | client->shared_secret_len, | |
1616 | client->pending_dac_coa_req, 0)) { | |
1617 | RADIUS_DEBUG("Ignore CoA response with invalid authenticator"); | |
1618 | radius_msg_free(msg); | |
1619 | return; | |
1620 | } | |
1621 | ||
1622 | RADIUS_DEBUG("CoA-%s received for " MACSTR, | |
1623 | ack ? "ACK" : "NAK", | |
1624 | MAC2STR(client->pending_dac_coa_addr)); | |
1625 | ||
1626 | radius_msg_free(msg); | |
1627 | radius_msg_free(client->pending_dac_coa_req); | |
1628 | client->pending_dac_coa_req = NULL; | |
1629 | ||
1630 | #ifdef CONFIG_SQLITE | |
1631 | if (!data->db) | |
1632 | return; | |
1633 | ||
1634 | os_snprintf(addrtxt, sizeof(addrtxt), MACSTR, | |
1635 | MAC2STR(client->pending_dac_coa_addr)); | |
1636 | ||
1637 | if (ack) { | |
1638 | sql = sqlite3_mprintf("UPDATE current_sessions SET hs20_t_c_filtering=0, waiting_coa_ack=0, coa_ack_received=1 WHERE mac_addr=%Q", | |
1639 | addrtxt); | |
1640 | } else { | |
1641 | sql = sqlite3_mprintf("UPDATE current_sessions SET waiting_coa_ack=0 WHERE mac_addr=%Q", | |
1642 | addrtxt); | |
1643 | } | |
1644 | if (!sql) | |
1645 | return; | |
1646 | ||
1647 | res = sqlite3_exec(data->db, sql, NULL, NULL, NULL); | |
1648 | sqlite3_free(sql); | |
1649 | if (res != SQLITE_OK) { | |
1650 | RADIUS_ERROR("Failed to update current_sessions entry: %s", | |
1651 | sqlite3_errmsg(data->db)); | |
1652 | return; | |
1653 | } | |
1654 | #endif /* CONFIG_SQLITE */ | |
1655 | } | |
1656 | ||
1657 | ||
6fc6879b JM |
1658 | static void radius_server_receive_auth(int sock, void *eloop_ctx, |
1659 | void *sock_ctx) | |
1660 | { | |
1661 | struct radius_server_data *data = eloop_ctx; | |
1662 | u8 *buf = NULL; | |
5a641ae0 JM |
1663 | union { |
1664 | struct sockaddr_storage ss; | |
1665 | struct sockaddr_in sin; | |
1666 | #ifdef CONFIG_IPV6 | |
1667 | struct sockaddr_in6 sin6; | |
1668 | #endif /* CONFIG_IPV6 */ | |
1669 | } from; | |
6fc6879b JM |
1670 | socklen_t fromlen; |
1671 | int len; | |
1672 | struct radius_client *client = NULL; | |
1673 | struct radius_msg *msg = NULL; | |
1674 | char abuf[50]; | |
1675 | int from_port = 0; | |
1676 | ||
1677 | buf = os_malloc(RADIUS_MAX_MSG_LEN); | |
1678 | if (buf == NULL) { | |
1679 | goto fail; | |
1680 | } | |
1681 | ||
1682 | fromlen = sizeof(from); | |
1683 | len = recvfrom(sock, buf, RADIUS_MAX_MSG_LEN, 0, | |
5a641ae0 | 1684 | (struct sockaddr *) &from.ss, &fromlen); |
6fc6879b | 1685 | if (len < 0) { |
61323e70 JM |
1686 | wpa_printf(MSG_INFO, "recvfrom[radius_server]: %s", |
1687 | strerror(errno)); | |
6fc6879b JM |
1688 | goto fail; |
1689 | } | |
1690 | ||
1691 | #ifdef CONFIG_IPV6 | |
1692 | if (data->ipv6) { | |
5a641ae0 JM |
1693 | if (inet_ntop(AF_INET6, &from.sin6.sin6_addr, abuf, |
1694 | sizeof(abuf)) == NULL) | |
6fc6879b | 1695 | abuf[0] = '\0'; |
5a641ae0 | 1696 | from_port = ntohs(from.sin6.sin6_port); |
6fc6879b JM |
1697 | RADIUS_DEBUG("Received %d bytes from %s:%d", |
1698 | len, abuf, from_port); | |
1699 | ||
1700 | client = radius_server_get_client(data, | |
1701 | (struct in_addr *) | |
5a641ae0 | 1702 | &from.sin6.sin6_addr, 1); |
6fc6879b JM |
1703 | } |
1704 | #endif /* CONFIG_IPV6 */ | |
1705 | ||
1706 | if (!data->ipv6) { | |
5a641ae0 JM |
1707 | os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf)); |
1708 | from_port = ntohs(from.sin.sin_port); | |
6fc6879b JM |
1709 | RADIUS_DEBUG("Received %d bytes from %s:%d", |
1710 | len, abuf, from_port); | |
1711 | ||
5a641ae0 | 1712 | client = radius_server_get_client(data, &from.sin.sin_addr, 0); |
6fc6879b JM |
1713 | } |
1714 | ||
1715 | RADIUS_DUMP("Received data", buf, len); | |
1716 | ||
1717 | if (client == NULL) { | |
1718 | RADIUS_DEBUG("Unknown client %s - packet ignored", abuf); | |
1719 | data->counters.invalid_requests++; | |
1720 | goto fail; | |
1721 | } | |
1722 | ||
1723 | msg = radius_msg_parse(buf, len); | |
1724 | if (msg == NULL) { | |
1725 | RADIUS_DEBUG("Parsing incoming RADIUS frame failed"); | |
1726 | data->counters.malformed_access_requests++; | |
1727 | client->counters.malformed_access_requests++; | |
1728 | goto fail; | |
1729 | } | |
1730 | ||
1731 | os_free(buf); | |
1732 | buf = NULL; | |
1733 | ||
1734 | if (wpa_debug_level <= MSG_MSGDUMP) { | |
1735 | radius_msg_dump(msg); | |
1736 | } | |
1737 | ||
abed6136 JM |
1738 | if (radius_msg_get_hdr(msg)->code == RADIUS_CODE_DISCONNECT_ACK) { |
1739 | radius_server_receive_disconnect_resp(data, client, msg, 1); | |
1740 | return; | |
1741 | } | |
1742 | ||
1743 | if (radius_msg_get_hdr(msg)->code == RADIUS_CODE_DISCONNECT_NAK) { | |
1744 | radius_server_receive_disconnect_resp(data, client, msg, 0); | |
1745 | return; | |
1746 | } | |
1747 | ||
1748 | if (radius_msg_get_hdr(msg)->code == RADIUS_CODE_COA_ACK) { | |
1749 | radius_server_receive_coa_resp(data, client, msg, 1); | |
1750 | return; | |
1751 | } | |
1752 | ||
1753 | if (radius_msg_get_hdr(msg)->code == RADIUS_CODE_COA_NAK) { | |
1754 | radius_server_receive_coa_resp(data, client, msg, 0); | |
1755 | return; | |
1756 | } | |
1757 | ||
1489e11a JM |
1758 | if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCESS_REQUEST) { |
1759 | RADIUS_DEBUG("Unexpected RADIUS code %d", | |
1760 | radius_msg_get_hdr(msg)->code); | |
6fc6879b JM |
1761 | data->counters.unknown_types++; |
1762 | client->counters.unknown_types++; | |
1763 | goto fail; | |
1764 | } | |
1765 | ||
1766 | data->counters.access_requests++; | |
1767 | client->counters.access_requests++; | |
1768 | ||
1769 | if (radius_msg_verify_msg_auth(msg, (u8 *) client->shared_secret, | |
1770 | client->shared_secret_len, NULL)) { | |
1771 | RADIUS_DEBUG("Invalid Message-Authenticator from %s", abuf); | |
1772 | data->counters.bad_authenticators++; | |
1773 | client->counters.bad_authenticators++; | |
1774 | goto fail; | |
1775 | } | |
1776 | ||
1777 | if (radius_server_request(data, msg, (struct sockaddr *) &from, | |
1778 | fromlen, client, abuf, from_port, NULL) == | |
1779 | -2) | |
1780 | return; /* msg was stored with the session */ | |
1781 | ||
1782 | fail: | |
9e7245bd | 1783 | radius_msg_free(msg); |
6fc6879b JM |
1784 | os_free(buf); |
1785 | } | |
1786 | ||
1787 | ||
a1dd890a JM |
1788 | static void radius_server_receive_acct(int sock, void *eloop_ctx, |
1789 | void *sock_ctx) | |
1790 | { | |
1791 | struct radius_server_data *data = eloop_ctx; | |
1792 | u8 *buf = NULL; | |
1793 | union { | |
1794 | struct sockaddr_storage ss; | |
1795 | struct sockaddr_in sin; | |
1796 | #ifdef CONFIG_IPV6 | |
1797 | struct sockaddr_in6 sin6; | |
1798 | #endif /* CONFIG_IPV6 */ | |
1799 | } from; | |
1800 | socklen_t fromlen; | |
1801 | int len, res; | |
1802 | struct radius_client *client = NULL; | |
1803 | struct radius_msg *msg = NULL, *resp = NULL; | |
1804 | char abuf[50]; | |
1805 | int from_port = 0; | |
1806 | struct radius_hdr *hdr; | |
1807 | struct wpabuf *rbuf; | |
1808 | ||
1809 | buf = os_malloc(RADIUS_MAX_MSG_LEN); | |
1810 | if (buf == NULL) { | |
1811 | goto fail; | |
1812 | } | |
1813 | ||
1814 | fromlen = sizeof(from); | |
1815 | len = recvfrom(sock, buf, RADIUS_MAX_MSG_LEN, 0, | |
1816 | (struct sockaddr *) &from.ss, &fromlen); | |
1817 | if (len < 0) { | |
1818 | wpa_printf(MSG_INFO, "recvfrom[radius_server]: %s", | |
1819 | strerror(errno)); | |
1820 | goto fail; | |
1821 | } | |
1822 | ||
1823 | #ifdef CONFIG_IPV6 | |
1824 | if (data->ipv6) { | |
1825 | if (inet_ntop(AF_INET6, &from.sin6.sin6_addr, abuf, | |
1826 | sizeof(abuf)) == NULL) | |
1827 | abuf[0] = '\0'; | |
1828 | from_port = ntohs(from.sin6.sin6_port); | |
1829 | RADIUS_DEBUG("Received %d bytes from %s:%d", | |
1830 | len, abuf, from_port); | |
1831 | ||
1832 | client = radius_server_get_client(data, | |
1833 | (struct in_addr *) | |
1834 | &from.sin6.sin6_addr, 1); | |
1835 | } | |
1836 | #endif /* CONFIG_IPV6 */ | |
1837 | ||
1838 | if (!data->ipv6) { | |
1839 | os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf)); | |
1840 | from_port = ntohs(from.sin.sin_port); | |
1841 | RADIUS_DEBUG("Received %d bytes from %s:%d", | |
1842 | len, abuf, from_port); | |
1843 | ||
1844 | client = radius_server_get_client(data, &from.sin.sin_addr, 0); | |
1845 | } | |
1846 | ||
1847 | RADIUS_DUMP("Received data", buf, len); | |
1848 | ||
1849 | if (client == NULL) { | |
1850 | RADIUS_DEBUG("Unknown client %s - packet ignored", abuf); | |
1851 | data->counters.invalid_acct_requests++; | |
1852 | goto fail; | |
1853 | } | |
1854 | ||
1855 | msg = radius_msg_parse(buf, len); | |
1856 | if (msg == NULL) { | |
1857 | RADIUS_DEBUG("Parsing incoming RADIUS frame failed"); | |
1858 | data->counters.malformed_acct_requests++; | |
1859 | client->counters.malformed_acct_requests++; | |
1860 | goto fail; | |
1861 | } | |
1862 | ||
1863 | os_free(buf); | |
1864 | buf = NULL; | |
1865 | ||
1866 | if (wpa_debug_level <= MSG_MSGDUMP) { | |
1867 | radius_msg_dump(msg); | |
1868 | } | |
1869 | ||
1870 | if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCOUNTING_REQUEST) { | |
1871 | RADIUS_DEBUG("Unexpected RADIUS code %d", | |
1872 | radius_msg_get_hdr(msg)->code); | |
1873 | data->counters.unknown_acct_types++; | |
1874 | client->counters.unknown_acct_types++; | |
1875 | goto fail; | |
1876 | } | |
1877 | ||
1878 | data->counters.acct_requests++; | |
1879 | client->counters.acct_requests++; | |
1880 | ||
1881 | if (radius_msg_verify_acct_req(msg, (u8 *) client->shared_secret, | |
1882 | client->shared_secret_len)) { | |
1883 | RADIUS_DEBUG("Invalid Authenticator from %s", abuf); | |
1884 | data->counters.acct_bad_authenticators++; | |
1885 | client->counters.acct_bad_authenticators++; | |
1886 | goto fail; | |
1887 | } | |
1888 | ||
1889 | /* TODO: Write accounting information to a file or database */ | |
1890 | ||
1891 | hdr = radius_msg_get_hdr(msg); | |
1892 | ||
1893 | resp = radius_msg_new(RADIUS_CODE_ACCOUNTING_RESPONSE, hdr->identifier); | |
1894 | if (resp == NULL) | |
1895 | goto fail; | |
1896 | ||
1897 | radius_msg_finish_acct_resp(resp, (u8 *) client->shared_secret, | |
1898 | client->shared_secret_len, | |
1899 | hdr->authenticator); | |
1900 | ||
1901 | RADIUS_DEBUG("Reply to %s:%d", abuf, from_port); | |
1902 | if (wpa_debug_level <= MSG_MSGDUMP) { | |
1903 | radius_msg_dump(resp); | |
1904 | } | |
1905 | rbuf = radius_msg_get_buf(resp); | |
1906 | data->counters.acct_responses++; | |
1907 | client->counters.acct_responses++; | |
1908 | res = sendto(data->acct_sock, wpabuf_head(rbuf), wpabuf_len(rbuf), 0, | |
1909 | (struct sockaddr *) &from.ss, fromlen); | |
1910 | if (res < 0) { | |
1911 | wpa_printf(MSG_INFO, "sendto[RADIUS SRV]: %s", | |
1912 | strerror(errno)); | |
1913 | } | |
1914 | ||
1915 | fail: | |
1916 | radius_msg_free(resp); | |
1917 | radius_msg_free(msg); | |
1918 | os_free(buf); | |
1919 | } | |
1920 | ||
1921 | ||
5cd89c26 JM |
1922 | static int radius_server_disable_pmtu_discovery(int s) |
1923 | { | |
1924 | int r = -1; | |
1925 | #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) | |
1926 | /* Turn off Path MTU discovery on IPv4/UDP sockets. */ | |
1927 | int action = IP_PMTUDISC_DONT; | |
1928 | r = setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, &action, | |
1929 | sizeof(action)); | |
1930 | if (r == -1) | |
1931 | wpa_printf(MSG_ERROR, "Failed to set IP_MTU_DISCOVER: " | |
1932 | "%s", strerror(errno)); | |
1933 | #endif | |
1934 | return r; | |
1935 | } | |
1936 | ||
1937 | ||
6fc6879b JM |
1938 | static int radius_server_open_socket(int port) |
1939 | { | |
1940 | int s; | |
1941 | struct sockaddr_in addr; | |
1942 | ||
1943 | s = socket(PF_INET, SOCK_DGRAM, 0); | |
1944 | if (s < 0) { | |
61323e70 | 1945 | wpa_printf(MSG_INFO, "RADIUS: socket: %s", strerror(errno)); |
6fc6879b JM |
1946 | return -1; |
1947 | } | |
1948 | ||
5cd89c26 JM |
1949 | radius_server_disable_pmtu_discovery(s); |
1950 | ||
6fc6879b JM |
1951 | os_memset(&addr, 0, sizeof(addr)); |
1952 | addr.sin_family = AF_INET; | |
1953 | addr.sin_port = htons(port); | |
1954 | if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { | |
61323e70 | 1955 | wpa_printf(MSG_INFO, "RADIUS: bind: %s", strerror(errno)); |
6fc6879b JM |
1956 | close(s); |
1957 | return -1; | |
1958 | } | |
1959 | ||
1960 | return s; | |
1961 | } | |
1962 | ||
1963 | ||
1964 | #ifdef CONFIG_IPV6 | |
1965 | static int radius_server_open_socket6(int port) | |
1966 | { | |
1967 | int s; | |
1968 | struct sockaddr_in6 addr; | |
1969 | ||
1970 | s = socket(PF_INET6, SOCK_DGRAM, 0); | |
1971 | if (s < 0) { | |
61323e70 JM |
1972 | wpa_printf(MSG_INFO, "RADIUS: socket[IPv6]: %s", |
1973 | strerror(errno)); | |
6fc6879b JM |
1974 | return -1; |
1975 | } | |
1976 | ||
1977 | os_memset(&addr, 0, sizeof(addr)); | |
1978 | addr.sin6_family = AF_INET6; | |
1979 | os_memcpy(&addr.sin6_addr, &in6addr_any, sizeof(in6addr_any)); | |
1980 | addr.sin6_port = htons(port); | |
1981 | if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { | |
61323e70 | 1982 | wpa_printf(MSG_INFO, "RADIUS: bind: %s", strerror(errno)); |
6fc6879b JM |
1983 | close(s); |
1984 | return -1; | |
1985 | } | |
1986 | ||
1987 | return s; | |
1988 | } | |
1989 | #endif /* CONFIG_IPV6 */ | |
1990 | ||
1991 | ||
1992 | static void radius_server_free_sessions(struct radius_server_data *data, | |
1993 | struct radius_session *sessions) | |
1994 | { | |
1995 | struct radius_session *session, *prev; | |
1996 | ||
1997 | session = sessions; | |
1998 | while (session) { | |
1999 | prev = session; | |
2000 | session = session->next; | |
2001 | radius_server_session_free(data, prev); | |
2002 | } | |
2003 | } | |
2004 | ||
2005 | ||
2006 | static void radius_server_free_clients(struct radius_server_data *data, | |
2007 | struct radius_client *clients) | |
2008 | { | |
2009 | struct radius_client *client, *prev; | |
2010 | ||
2011 | client = clients; | |
2012 | while (client) { | |
2013 | prev = client; | |
2014 | client = client->next; | |
2015 | ||
2016 | radius_server_free_sessions(data, prev->sessions); | |
2017 | os_free(prev->shared_secret); | |
abed6136 JM |
2018 | radius_msg_free(prev->pending_dac_coa_req); |
2019 | radius_msg_free(prev->pending_dac_disconnect_req); | |
6fc6879b JM |
2020 | os_free(prev); |
2021 | } | |
2022 | } | |
2023 | ||
2024 | ||
2025 | static struct radius_client * | |
2026 | radius_server_read_clients(const char *client_file, int ipv6) | |
2027 | { | |
2028 | FILE *f; | |
2029 | const int buf_size = 1024; | |
2030 | char *buf, *pos; | |
2031 | struct radius_client *clients, *tail, *entry; | |
2032 | int line = 0, mask, failed = 0, i; | |
2033 | struct in_addr addr; | |
2034 | #ifdef CONFIG_IPV6 | |
2035 | struct in6_addr addr6; | |
2036 | #endif /* CONFIG_IPV6 */ | |
2037 | unsigned int val; | |
2038 | ||
2039 | f = fopen(client_file, "r"); | |
2040 | if (f == NULL) { | |
2041 | RADIUS_ERROR("Could not open client file '%s'", client_file); | |
2042 | return NULL; | |
2043 | } | |
2044 | ||
2045 | buf = os_malloc(buf_size); | |
2046 | if (buf == NULL) { | |
2047 | fclose(f); | |
2048 | return NULL; | |
2049 | } | |
2050 | ||
2051 | clients = tail = NULL; | |
2052 | while (fgets(buf, buf_size, f)) { | |
2053 | /* Configuration file format: | |
2054 | * 192.168.1.0/24 secret | |
2055 | * 192.168.1.2 secret | |
2056 | * fe80::211:22ff:fe33:4455/64 secretipv6 | |
2057 | */ | |
2058 | line++; | |
2059 | buf[buf_size - 1] = '\0'; | |
2060 | pos = buf; | |
2061 | while (*pos != '\0' && *pos != '\n') | |
2062 | pos++; | |
2063 | if (*pos == '\n') | |
2064 | *pos = '\0'; | |
2065 | if (*buf == '\0' || *buf == '#') | |
2066 | continue; | |
2067 | ||
2068 | pos = buf; | |
2069 | while ((*pos >= '0' && *pos <= '9') || *pos == '.' || | |
2070 | (*pos >= 'a' && *pos <= 'f') || *pos == ':' || | |
2071 | (*pos >= 'A' && *pos <= 'F')) { | |
2072 | pos++; | |
2073 | } | |
2074 | ||
2075 | if (*pos == '\0') { | |
2076 | failed = 1; | |
2077 | break; | |
2078 | } | |
2079 | ||
2080 | if (*pos == '/') { | |
2081 | char *end; | |
2082 | *pos++ = '\0'; | |
2083 | mask = strtol(pos, &end, 10); | |
2084 | if ((pos == end) || | |
2085 | (mask < 0 || mask > (ipv6 ? 128 : 32))) { | |
2086 | failed = 1; | |
2087 | break; | |
2088 | } | |
2089 | pos = end; | |
2090 | } else { | |
2091 | mask = ipv6 ? 128 : 32; | |
2092 | *pos++ = '\0'; | |
2093 | } | |
2094 | ||
2095 | if (!ipv6 && inet_aton(buf, &addr) == 0) { | |
2096 | failed = 1; | |
2097 | break; | |
2098 | } | |
2099 | #ifdef CONFIG_IPV6 | |
2100 | if (ipv6 && inet_pton(AF_INET6, buf, &addr6) <= 0) { | |
2101 | if (inet_pton(AF_INET, buf, &addr) <= 0) { | |
2102 | failed = 1; | |
2103 | break; | |
2104 | } | |
2105 | /* Convert IPv4 address to IPv6 */ | |
2106 | if (mask <= 32) | |
2107 | mask += (128 - 32); | |
2108 | os_memset(addr6.s6_addr, 0, 10); | |
2109 | addr6.s6_addr[10] = 0xff; | |
2110 | addr6.s6_addr[11] = 0xff; | |
2111 | os_memcpy(addr6.s6_addr + 12, (char *) &addr.s_addr, | |
2112 | 4); | |
2113 | } | |
2114 | #endif /* CONFIG_IPV6 */ | |
2115 | ||
2116 | while (*pos == ' ' || *pos == '\t') { | |
2117 | pos++; | |
2118 | } | |
2119 | ||
2120 | if (*pos == '\0') { | |
2121 | failed = 1; | |
2122 | break; | |
2123 | } | |
2124 | ||
2125 | entry = os_zalloc(sizeof(*entry)); | |
2126 | if (entry == NULL) { | |
2127 | failed = 1; | |
2128 | break; | |
2129 | } | |
2130 | entry->shared_secret = os_strdup(pos); | |
2131 | if (entry->shared_secret == NULL) { | |
2132 | failed = 1; | |
2133 | os_free(entry); | |
2134 | break; | |
2135 | } | |
2136 | entry->shared_secret_len = os_strlen(entry->shared_secret); | |
6fc6879b | 2137 | if (!ipv6) { |
77381639 | 2138 | entry->addr.s_addr = addr.s_addr; |
6fc6879b JM |
2139 | val = 0; |
2140 | for (i = 0; i < mask; i++) | |
429ed54a | 2141 | val |= 1U << (31 - i); |
6fc6879b JM |
2142 | entry->mask.s_addr = htonl(val); |
2143 | } | |
2144 | #ifdef CONFIG_IPV6 | |
2145 | if (ipv6) { | |
2146 | int offset = mask / 8; | |
2147 | ||
2148 | os_memcpy(entry->addr6.s6_addr, addr6.s6_addr, 16); | |
2149 | os_memset(entry->mask6.s6_addr, 0xff, offset); | |
2150 | val = 0; | |
2151 | for (i = 0; i < (mask % 8); i++) | |
2152 | val |= 1 << (7 - i); | |
2153 | if (offset < 16) | |
2154 | entry->mask6.s6_addr[offset] = val; | |
2155 | } | |
2156 | #endif /* CONFIG_IPV6 */ | |
2157 | ||
2158 | if (tail == NULL) { | |
2159 | clients = tail = entry; | |
2160 | } else { | |
2161 | tail->next = entry; | |
2162 | tail = entry; | |
2163 | } | |
2164 | } | |
2165 | ||
2166 | if (failed) { | |
2167 | RADIUS_ERROR("Invalid line %d in '%s'", line, client_file); | |
2168 | radius_server_free_clients(NULL, clients); | |
2169 | clients = NULL; | |
2170 | } | |
2171 | ||
2172 | os_free(buf); | |
2173 | fclose(f); | |
2174 | ||
2175 | return clients; | |
2176 | } | |
2177 | ||
2178 | ||
362bd35f JM |
2179 | /** |
2180 | * radius_server_init - Initialize RADIUS server | |
2181 | * @conf: Configuration for the RADIUS server | |
2182 | * Returns: Pointer to private RADIUS server context or %NULL on failure | |
2183 | * | |
2184 | * This initializes a RADIUS server instance and returns a context pointer that | |
2185 | * will be used in other calls to the RADIUS server module. The server can be | |
2186 | * deinitialize by calling radius_server_deinit(). | |
2187 | */ | |
6fc6879b JM |
2188 | struct radius_server_data * |
2189 | radius_server_init(struct radius_server_conf *conf) | |
2190 | { | |
2191 | struct radius_server_data *data; | |
822e7c66 | 2192 | struct eap_config *eap_cfg; |
6fc6879b JM |
2193 | |
2194 | #ifndef CONFIG_IPV6 | |
2195 | if (conf->ipv6) { | |
61323e70 | 2196 | wpa_printf(MSG_ERROR, "RADIUS server compiled without IPv6 support"); |
6fc6879b JM |
2197 | return NULL; |
2198 | } | |
2199 | #endif /* CONFIG_IPV6 */ | |
2200 | ||
2201 | data = os_zalloc(sizeof(*data)); | |
2202 | if (data == NULL) | |
2203 | return NULL; | |
2204 | ||
822e7c66 JM |
2205 | eap_cfg = data->eap_cfg = os_zalloc(sizeof(*eap_cfg)); |
2206 | if (!eap_cfg) { | |
2207 | os_free(data); | |
2208 | return NULL; | |
2209 | } | |
baf8ab8c JM |
2210 | data->auth_sock = -1; |
2211 | data->acct_sock = -1; | |
d3bddd8b | 2212 | dl_list_init(&data->erp_keys); |
58707176 | 2213 | os_get_reltime(&data->start_time); |
6fc6879b | 2214 | data->conf_ctx = conf->conf_ctx; |
822e7c66 JM |
2215 | eap_cfg->backend_auth = TRUE; |
2216 | eap_cfg->eap_server = 1; | |
2217 | eap_cfg->eap_sim_db_priv = conf->eap_sim_db_priv; | |
2218 | eap_cfg->ssl_ctx = conf->ssl_ctx; | |
2219 | eap_cfg->msg_ctx = conf->msg_ctx; | |
6fc6879b JM |
2220 | data->ipv6 = conf->ipv6; |
2221 | if (conf->pac_opaque_encr_key) { | |
822e7c66 JM |
2222 | eap_cfg->pac_opaque_encr_key = os_malloc(16); |
2223 | if (eap_cfg->pac_opaque_encr_key) { | |
2224 | os_memcpy(eap_cfg->pac_opaque_encr_key, | |
4457f41b MJ |
2225 | conf->pac_opaque_encr_key, 16); |
2226 | } | |
6fc6879b | 2227 | } |
2d867244 | 2228 | if (conf->eap_fast_a_id) { |
822e7c66 JM |
2229 | eap_cfg->eap_fast_a_id = os_malloc(conf->eap_fast_a_id_len); |
2230 | if (eap_cfg->eap_fast_a_id) { | |
2231 | os_memcpy(eap_cfg->eap_fast_a_id, conf->eap_fast_a_id, | |
2d867244 | 2232 | conf->eap_fast_a_id_len); |
822e7c66 | 2233 | eap_cfg->eap_fast_a_id_len = conf->eap_fast_a_id_len; |
2d867244 JM |
2234 | } |
2235 | } | |
2236 | if (conf->eap_fast_a_id_info) | |
822e7c66 JM |
2237 | eap_cfg->eap_fast_a_id_info = |
2238 | os_strdup(conf->eap_fast_a_id_info); | |
2239 | eap_cfg->eap_fast_prov = conf->eap_fast_prov; | |
2240 | eap_cfg->pac_key_lifetime = conf->pac_key_lifetime; | |
2241 | eap_cfg->pac_key_refresh_time = conf->pac_key_refresh_time; | |
2242 | eap_cfg->eap_teap_auth = conf->eap_teap_auth; | |
2243 | eap_cfg->eap_teap_pac_no_inner = conf->eap_teap_pac_no_inner; | |
2244 | eap_cfg->eap_teap_separate_result = conf->eap_teap_separate_result; | |
e54cfbb5 | 2245 | eap_cfg->eap_teap_id = conf->eap_teap_id; |
6fc6879b | 2246 | data->get_eap_user = conf->get_eap_user; |
822e7c66 JM |
2247 | eap_cfg->eap_sim_aka_result_ind = conf->eap_sim_aka_result_ind; |
2248 | eap_cfg->eap_sim_id = conf->eap_sim_id; | |
2249 | eap_cfg->tnc = conf->tnc; | |
2250 | eap_cfg->wps = conf->wps; | |
2251 | eap_cfg->pwd_group = conf->pwd_group; | |
2252 | if (conf->server_id) { | |
2253 | eap_cfg->server_id = (u8 *) os_strdup(conf->server_id); | |
2254 | eap_cfg->server_id_len = os_strlen(conf->server_id); | |
2255 | } | |
65d50f0a JM |
2256 | if (conf->eap_req_id_text) { |
2257 | data->eap_req_id_text = os_malloc(conf->eap_req_id_text_len); | |
2258 | if (data->eap_req_id_text) { | |
2259 | os_memcpy(data->eap_req_id_text, conf->eap_req_id_text, | |
2260 | conf->eap_req_id_text_len); | |
2261 | data->eap_req_id_text_len = conf->eap_req_id_text_len; | |
2262 | } | |
2263 | } | |
822e7c66 | 2264 | eap_cfg->erp = conf->erp; |
d3bddd8b | 2265 | data->erp_domain = conf->erp_domain; |
822e7c66 JM |
2266 | eap_cfg->tls_session_lifetime = conf->tls_session_lifetime; |
2267 | eap_cfg->tls_flags = conf->tls_flags; | |
6fc6879b | 2268 | |
8d2a9921 JM |
2269 | if (conf->subscr_remediation_url) { |
2270 | data->subscr_remediation_url = | |
2271 | os_strdup(conf->subscr_remediation_url); | |
2272 | } | |
f6fb1926 | 2273 | data->subscr_remediation_method = conf->subscr_remediation_method; |
7bd8c76a JM |
2274 | if (conf->hs20_sim_provisioning_url) |
2275 | data->hs20_sim_provisioning_url = | |
2276 | os_strdup(conf->hs20_sim_provisioning_url); | |
8d2a9921 | 2277 | |
d4e39c51 JM |
2278 | if (conf->t_c_server_url) |
2279 | data->t_c_server_url = os_strdup(conf->t_c_server_url); | |
2280 | ||
8a57da7e JM |
2281 | #ifdef CONFIG_SQLITE |
2282 | if (conf->sqlite_file) { | |
2283 | if (sqlite3_open(conf->sqlite_file, &data->db)) { | |
2284 | RADIUS_ERROR("Could not open SQLite file '%s'", | |
2285 | conf->sqlite_file); | |
2286 | radius_server_deinit(data); | |
2287 | return NULL; | |
2288 | } | |
2289 | } | |
2290 | #endif /* CONFIG_SQLITE */ | |
2291 | ||
505a3694 JM |
2292 | #ifdef CONFIG_RADIUS_TEST |
2293 | if (conf->dump_msk_file) | |
2294 | data->dump_msk_file = os_strdup(conf->dump_msk_file); | |
2295 | #endif /* CONFIG_RADIUS_TEST */ | |
2296 | ||
6fc6879b JM |
2297 | data->clients = radius_server_read_clients(conf->client_file, |
2298 | conf->ipv6); | |
2299 | if (data->clients == NULL) { | |
61323e70 | 2300 | wpa_printf(MSG_ERROR, "No RADIUS clients configured"); |
6fc6879b JM |
2301 | radius_server_deinit(data); |
2302 | return NULL; | |
2303 | } | |
2304 | ||
2305 | #ifdef CONFIG_IPV6 | |
2306 | if (conf->ipv6) | |
2307 | data->auth_sock = radius_server_open_socket6(conf->auth_port); | |
2308 | else | |
2309 | #endif /* CONFIG_IPV6 */ | |
2310 | data->auth_sock = radius_server_open_socket(conf->auth_port); | |
2311 | if (data->auth_sock < 0) { | |
61323e70 | 2312 | wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS authentication server"); |
6fc6879b JM |
2313 | radius_server_deinit(data); |
2314 | return NULL; | |
2315 | } | |
2316 | if (eloop_register_read_sock(data->auth_sock, | |
2317 | radius_server_receive_auth, | |
2318 | data, NULL)) { | |
2319 | radius_server_deinit(data); | |
2320 | return NULL; | |
2321 | } | |
2322 | ||
a1dd890a JM |
2323 | if (conf->acct_port) { |
2324 | #ifdef CONFIG_IPV6 | |
2325 | if (conf->ipv6) | |
2326 | data->acct_sock = radius_server_open_socket6( | |
2327 | conf->acct_port); | |
2328 | else | |
2329 | #endif /* CONFIG_IPV6 */ | |
2330 | data->acct_sock = radius_server_open_socket(conf->acct_port); | |
2331 | if (data->acct_sock < 0) { | |
2332 | wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS accounting server"); | |
2333 | radius_server_deinit(data); | |
2334 | return NULL; | |
2335 | } | |
2336 | if (eloop_register_read_sock(data->acct_sock, | |
2337 | radius_server_receive_acct, | |
2338 | data, NULL)) { | |
2339 | radius_server_deinit(data); | |
2340 | return NULL; | |
2341 | } | |
2342 | } else { | |
2343 | data->acct_sock = -1; | |
2344 | } | |
2345 | ||
6fc6879b JM |
2346 | return data; |
2347 | } | |
2348 | ||
2349 | ||
362bd35f | 2350 | /** |
2c6411ed | 2351 | * radius_server_erp_flush - Flush all ERP keys |
362bd35f JM |
2352 | * @data: RADIUS server context from radius_server_init() |
2353 | */ | |
2c6411ed | 2354 | void radius_server_erp_flush(struct radius_server_data *data) |
6fc6879b | 2355 | { |
d3bddd8b JM |
2356 | struct eap_server_erp_key *erp; |
2357 | ||
2c6411ed JM |
2358 | if (data == NULL) |
2359 | return; | |
2360 | while ((erp = dl_list_first(&data->erp_keys, struct eap_server_erp_key, | |
2361 | list)) != NULL) { | |
2362 | dl_list_del(&erp->list); | |
2363 | bin_clear_free(erp, sizeof(*erp)); | |
2364 | } | |
2365 | } | |
2366 | ||
2367 | ||
2368 | /** | |
2369 | * radius_server_deinit - Deinitialize RADIUS server | |
2370 | * @data: RADIUS server context from radius_server_init() | |
2371 | */ | |
2372 | void radius_server_deinit(struct radius_server_data *data) | |
2373 | { | |
6fc6879b JM |
2374 | if (data == NULL) |
2375 | return; | |
2376 | ||
2377 | if (data->auth_sock >= 0) { | |
2378 | eloop_unregister_read_sock(data->auth_sock); | |
2379 | close(data->auth_sock); | |
2380 | } | |
2381 | ||
a1dd890a JM |
2382 | if (data->acct_sock >= 0) { |
2383 | eloop_unregister_read_sock(data->acct_sock); | |
2384 | close(data->acct_sock); | |
2385 | } | |
2386 | ||
6fc6879b JM |
2387 | radius_server_free_clients(data, data->clients); |
2388 | ||
65d50f0a | 2389 | os_free(data->eap_req_id_text); |
505a3694 JM |
2390 | #ifdef CONFIG_RADIUS_TEST |
2391 | os_free(data->dump_msk_file); | |
2392 | #endif /* CONFIG_RADIUS_TEST */ | |
8d2a9921 | 2393 | os_free(data->subscr_remediation_url); |
7bd8c76a | 2394 | os_free(data->hs20_sim_provisioning_url); |
d4e39c51 | 2395 | os_free(data->t_c_server_url); |
8a57da7e JM |
2396 | |
2397 | #ifdef CONFIG_SQLITE | |
2398 | if (data->db) | |
2399 | sqlite3_close(data->db); | |
2400 | #endif /* CONFIG_SQLITE */ | |
2401 | ||
2c6411ed | 2402 | radius_server_erp_flush(data); |
822e7c66 | 2403 | eap_server_config_free(data->eap_cfg); |
d3bddd8b | 2404 | |
6fc6879b JM |
2405 | os_free(data); |
2406 | } | |
2407 | ||
2408 | ||
362bd35f JM |
2409 | /** |
2410 | * radius_server_get_mib - Get RADIUS server MIB information | |
2411 | * @data: RADIUS server context from radius_server_init() | |
2412 | * @buf: Buffer for returning the MIB data in text format | |
2413 | * @buflen: buf length in octets | |
2414 | * Returns: Number of octets written into buf | |
2415 | */ | |
6fc6879b JM |
2416 | int radius_server_get_mib(struct radius_server_data *data, char *buf, |
2417 | size_t buflen) | |
2418 | { | |
2419 | int ret, uptime; | |
2420 | unsigned int idx; | |
2421 | char *end, *pos; | |
58707176 | 2422 | struct os_reltime now; |
6fc6879b JM |
2423 | struct radius_client *cli; |
2424 | ||
2425 | /* RFC 2619 - RADIUS Authentication Server MIB */ | |
2426 | ||
2427 | if (data == NULL || buflen == 0) | |
2428 | return 0; | |
2429 | ||
2430 | pos = buf; | |
2431 | end = buf + buflen; | |
2432 | ||
58707176 | 2433 | os_get_reltime(&now); |
6fc6879b JM |
2434 | uptime = (now.sec - data->start_time.sec) * 100 + |
2435 | ((now.usec - data->start_time.usec) / 10000) % 100; | |
2436 | ret = os_snprintf(pos, end - pos, | |
2437 | "RADIUS-AUTH-SERVER-MIB\n" | |
2438 | "radiusAuthServIdent=hostapd\n" | |
2439 | "radiusAuthServUpTime=%d\n" | |
2440 | "radiusAuthServResetTime=0\n" | |
2441 | "radiusAuthServConfigReset=4\n", | |
2442 | uptime); | |
d85e1fc8 | 2443 | if (os_snprintf_error(end - pos, ret)) { |
6fc6879b JM |
2444 | *pos = '\0'; |
2445 | return pos - buf; | |
2446 | } | |
2447 | pos += ret; | |
2448 | ||
2449 | ret = os_snprintf(pos, end - pos, | |
2450 | "radiusAuthServTotalAccessRequests=%u\n" | |
2451 | "radiusAuthServTotalInvalidRequests=%u\n" | |
2452 | "radiusAuthServTotalDupAccessRequests=%u\n" | |
2453 | "radiusAuthServTotalAccessAccepts=%u\n" | |
2454 | "radiusAuthServTotalAccessRejects=%u\n" | |
2455 | "radiusAuthServTotalAccessChallenges=%u\n" | |
2456 | "radiusAuthServTotalMalformedAccessRequests=%u\n" | |
2457 | "radiusAuthServTotalBadAuthenticators=%u\n" | |
2458 | "radiusAuthServTotalPacketsDropped=%u\n" | |
a1dd890a JM |
2459 | "radiusAuthServTotalUnknownTypes=%u\n" |
2460 | "radiusAccServTotalRequests=%u\n" | |
2461 | "radiusAccServTotalInvalidRequests=%u\n" | |
2462 | "radiusAccServTotalResponses=%u\n" | |
2463 | "radiusAccServTotalMalformedRequests=%u\n" | |
2464 | "radiusAccServTotalBadAuthenticators=%u\n" | |
2465 | "radiusAccServTotalUnknownTypes=%u\n", | |
6fc6879b JM |
2466 | data->counters.access_requests, |
2467 | data->counters.invalid_requests, | |
2468 | data->counters.dup_access_requests, | |
2469 | data->counters.access_accepts, | |
2470 | data->counters.access_rejects, | |
2471 | data->counters.access_challenges, | |
2472 | data->counters.malformed_access_requests, | |
2473 | data->counters.bad_authenticators, | |
2474 | data->counters.packets_dropped, | |
a1dd890a JM |
2475 | data->counters.unknown_types, |
2476 | data->counters.acct_requests, | |
2477 | data->counters.invalid_acct_requests, | |
2478 | data->counters.acct_responses, | |
2479 | data->counters.malformed_acct_requests, | |
2480 | data->counters.acct_bad_authenticators, | |
2481 | data->counters.unknown_acct_types); | |
d85e1fc8 | 2482 | if (os_snprintf_error(end - pos, ret)) { |
6fc6879b JM |
2483 | *pos = '\0'; |
2484 | return pos - buf; | |
2485 | } | |
2486 | pos += ret; | |
2487 | ||
2488 | for (cli = data->clients, idx = 0; cli; cli = cli->next, idx++) { | |
2489 | char abuf[50], mbuf[50]; | |
2490 | #ifdef CONFIG_IPV6 | |
2491 | if (data->ipv6) { | |
2492 | if (inet_ntop(AF_INET6, &cli->addr6, abuf, | |
2493 | sizeof(abuf)) == NULL) | |
2494 | abuf[0] = '\0'; | |
c9cd78e5 | 2495 | if (inet_ntop(AF_INET6, &cli->mask6, mbuf, |
6fc6879b JM |
2496 | sizeof(mbuf)) == NULL) |
2497 | mbuf[0] = '\0'; | |
2498 | } | |
2499 | #endif /* CONFIG_IPV6 */ | |
2500 | if (!data->ipv6) { | |
2501 | os_strlcpy(abuf, inet_ntoa(cli->addr), sizeof(abuf)); | |
2502 | os_strlcpy(mbuf, inet_ntoa(cli->mask), sizeof(mbuf)); | |
2503 | } | |
2504 | ||
2505 | ret = os_snprintf(pos, end - pos, | |
2506 | "radiusAuthClientIndex=%u\n" | |
2507 | "radiusAuthClientAddress=%s/%s\n" | |
2508 | "radiusAuthServAccessRequests=%u\n" | |
2509 | "radiusAuthServDupAccessRequests=%u\n" | |
2510 | "radiusAuthServAccessAccepts=%u\n" | |
2511 | "radiusAuthServAccessRejects=%u\n" | |
2512 | "radiusAuthServAccessChallenges=%u\n" | |
2513 | "radiusAuthServMalformedAccessRequests=%u\n" | |
2514 | "radiusAuthServBadAuthenticators=%u\n" | |
2515 | "radiusAuthServPacketsDropped=%u\n" | |
a1dd890a JM |
2516 | "radiusAuthServUnknownTypes=%u\n" |
2517 | "radiusAccServTotalRequests=%u\n" | |
2518 | "radiusAccServTotalInvalidRequests=%u\n" | |
2519 | "radiusAccServTotalResponses=%u\n" | |
2520 | "radiusAccServTotalMalformedRequests=%u\n" | |
2521 | "radiusAccServTotalBadAuthenticators=%u\n" | |
2522 | "radiusAccServTotalUnknownTypes=%u\n", | |
6fc6879b JM |
2523 | idx, |
2524 | abuf, mbuf, | |
2525 | cli->counters.access_requests, | |
2526 | cli->counters.dup_access_requests, | |
2527 | cli->counters.access_accepts, | |
2528 | cli->counters.access_rejects, | |
2529 | cli->counters.access_challenges, | |
2530 | cli->counters.malformed_access_requests, | |
2531 | cli->counters.bad_authenticators, | |
2532 | cli->counters.packets_dropped, | |
a1dd890a JM |
2533 | cli->counters.unknown_types, |
2534 | cli->counters.acct_requests, | |
2535 | cli->counters.invalid_acct_requests, | |
2536 | cli->counters.acct_responses, | |
2537 | cli->counters.malformed_acct_requests, | |
2538 | cli->counters.acct_bad_authenticators, | |
2539 | cli->counters.unknown_acct_types); | |
d85e1fc8 | 2540 | if (os_snprintf_error(end - pos, ret)) { |
6fc6879b JM |
2541 | *pos = '\0'; |
2542 | return pos - buf; | |
2543 | } | |
2544 | pos += ret; | |
2545 | } | |
2546 | ||
2547 | return pos - buf; | |
2548 | } | |
2549 | ||
2550 | ||
2551 | static int radius_server_get_eap_user(void *ctx, const u8 *identity, | |
2552 | size_t identity_len, int phase2, | |
2553 | struct eap_user *user) | |
2554 | { | |
2555 | struct radius_session *sess = ctx; | |
2556 | struct radius_server_data *data = sess->server; | |
8d2a9921 | 2557 | int ret; |
6fc6879b | 2558 | |
8d2a9921 JM |
2559 | ret = data->get_eap_user(data->conf_ctx, identity, identity_len, |
2560 | phase2, user); | |
d0ee16ed JM |
2561 | if (ret == 0 && user) { |
2562 | sess->accept_attr = user->accept_attr; | |
8d2a9921 | 2563 | sess->remediation = user->remediation; |
8943cc99 | 2564 | sess->macacl = user->macacl; |
45260380 | 2565 | sess->t_c_timestamp = user->t_c_timestamp; |
d0ee16ed | 2566 | } |
fc48d33b BG |
2567 | |
2568 | if (ret) { | |
2569 | RADIUS_DEBUG("%s: User-Name not found from user database", | |
2570 | __func__); | |
2571 | } | |
2572 | ||
8d2a9921 | 2573 | return ret; |
6fc6879b JM |
2574 | } |
2575 | ||
2576 | ||
65d50f0a JM |
2577 | static const char * radius_server_get_eap_req_id_text(void *ctx, size_t *len) |
2578 | { | |
2579 | struct radius_session *sess = ctx; | |
2580 | struct radius_server_data *data = sess->server; | |
2581 | *len = data->eap_req_id_text_len; | |
2582 | return data->eap_req_id_text; | |
2583 | } | |
2584 | ||
2585 | ||
01f7fe10 JM |
2586 | static void radius_server_log_msg(void *ctx, const char *msg) |
2587 | { | |
2588 | struct radius_session *sess = ctx; | |
2589 | srv_log(sess, "EAP: %s", msg); | |
2590 | } | |
2591 | ||
2592 | ||
d3bddd8b JM |
2593 | #ifdef CONFIG_ERP |
2594 | ||
2595 | static const char * radius_server_get_erp_domain(void *ctx) | |
2596 | { | |
2597 | struct radius_session *sess = ctx; | |
2598 | struct radius_server_data *data = sess->server; | |
2599 | ||
2600 | return data->erp_domain; | |
2601 | } | |
2602 | ||
2603 | ||
2604 | static struct eap_server_erp_key * | |
2605 | radius_server_erp_get_key(void *ctx, const char *keyname) | |
2606 | { | |
2607 | struct radius_session *sess = ctx; | |
2608 | struct radius_server_data *data = sess->server; | |
d3bddd8b | 2609 | |
3580ed82 | 2610 | return radius_server_erp_find_key(data, keyname); |
d3bddd8b JM |
2611 | } |
2612 | ||
2613 | ||
2614 | static int radius_server_erp_add_key(void *ctx, struct eap_server_erp_key *erp) | |
2615 | { | |
2616 | struct radius_session *sess = ctx; | |
2617 | struct radius_server_data *data = sess->server; | |
2618 | ||
2619 | dl_list_add(&data->erp_keys, &erp->list); | |
2620 | return 0; | |
2621 | } | |
2622 | ||
2623 | #endif /* CONFIG_ERP */ | |
2624 | ||
2625 | ||
8b423edb | 2626 | static const struct eapol_callbacks radius_server_eapol_cb = |
6fc6879b JM |
2627 | { |
2628 | .get_eap_user = radius_server_get_eap_user, | |
65d50f0a | 2629 | .get_eap_req_id_text = radius_server_get_eap_req_id_text, |
01f7fe10 | 2630 | .log_msg = radius_server_log_msg, |
d3bddd8b JM |
2631 | #ifdef CONFIG_ERP |
2632 | .get_erp_send_reauth_start = NULL, | |
2633 | .get_erp_domain = radius_server_get_erp_domain, | |
2634 | .erp_get_key = radius_server_erp_get_key, | |
2635 | .erp_add_key = radius_server_erp_add_key, | |
2636 | #endif /* CONFIG_ERP */ | |
6fc6879b JM |
2637 | }; |
2638 | ||
2639 | ||
362bd35f JM |
2640 | /** |
2641 | * radius_server_eap_pending_cb - Pending EAP data notification | |
2642 | * @data: RADIUS server context from radius_server_init() | |
2643 | * @ctx: Pending EAP context pointer | |
2644 | * | |
2645 | * This function is used to notify EAP server module that a pending operation | |
2646 | * has been completed and processing of the EAP session can proceed. | |
2647 | */ | |
6fc6879b JM |
2648 | void radius_server_eap_pending_cb(struct radius_server_data *data, void *ctx) |
2649 | { | |
2650 | struct radius_client *cli; | |
2651 | struct radius_session *s, *sess = NULL; | |
2652 | struct radius_msg *msg; | |
2653 | ||
2654 | if (data == NULL) | |
2655 | return; | |
2656 | ||
2657 | for (cli = data->clients; cli; cli = cli->next) { | |
2658 | for (s = cli->sessions; s; s = s->next) { | |
2659 | if (s->eap == ctx && s->last_msg) { | |
2660 | sess = s; | |
2661 | break; | |
2662 | } | |
6fc6879b JM |
2663 | } |
2664 | if (sess) | |
2665 | break; | |
2666 | } | |
2667 | ||
2668 | if (sess == NULL) { | |
2669 | RADIUS_DEBUG("No session matched callback ctx"); | |
2670 | return; | |
2671 | } | |
2672 | ||
2673 | msg = sess->last_msg; | |
2674 | sess->last_msg = NULL; | |
2675 | eap_sm_pending_cb(sess->eap); | |
2676 | if (radius_server_request(data, msg, | |
2677 | (struct sockaddr *) &sess->last_from, | |
2678 | sess->last_fromlen, cli, | |
2679 | sess->last_from_addr, | |
2680 | sess->last_from_port, sess) == -2) | |
2681 | return; /* msg was stored with the session */ | |
2682 | ||
2683 | radius_msg_free(msg); | |
6fc6879b | 2684 | } |
abed6136 JM |
2685 | |
2686 | ||
2687 | #ifdef CONFIG_SQLITE | |
2688 | ||
2689 | struct db_session_fields { | |
2690 | char *identity; | |
2691 | char *nas; | |
2692 | int hs20_t_c_filtering; | |
2693 | int waiting_coa_ack; | |
2694 | int coa_ack_received; | |
2695 | }; | |
2696 | ||
2697 | ||
2698 | static int get_db_session_fields(void *ctx, int argc, char *argv[], char *col[]) | |
2699 | { | |
2700 | struct db_session_fields *fields = ctx; | |
2701 | int i; | |
2702 | ||
2703 | for (i = 0; i < argc; i++) { | |
2704 | if (!argv[i]) | |
2705 | continue; | |
2706 | ||
2707 | RADIUS_DEBUG("Session DB: %s=%s", col[i], argv[i]); | |
2708 | ||
2709 | if (os_strcmp(col[i], "identity") == 0) { | |
2710 | os_free(fields->identity); | |
2711 | fields->identity = os_strdup(argv[i]); | |
2712 | } else if (os_strcmp(col[i], "nas") == 0) { | |
2713 | os_free(fields->nas); | |
2714 | fields->nas = os_strdup(argv[i]); | |
2715 | } else if (os_strcmp(col[i], "hs20_t_c_filtering") == 0) { | |
2716 | fields->hs20_t_c_filtering = atoi(argv[i]); | |
2717 | } else if (os_strcmp(col[i], "waiting_coa_ack") == 0) { | |
2718 | fields->waiting_coa_ack = atoi(argv[i]); | |
2719 | } else if (os_strcmp(col[i], "coa_ack_received") == 0) { | |
2720 | fields->coa_ack_received = atoi(argv[i]); | |
2721 | } | |
2722 | } | |
2723 | ||
2724 | return 0; | |
2725 | } | |
2726 | ||
2727 | ||
2728 | static void free_db_session_fields(struct db_session_fields *fields) | |
2729 | { | |
2730 | os_free(fields->identity); | |
2731 | fields->identity = NULL; | |
2732 | os_free(fields->nas); | |
2733 | fields->nas = NULL; | |
2734 | } | |
2735 | ||
2736 | #endif /* CONFIG_SQLITE */ | |
2737 | ||
2738 | ||
2739 | int radius_server_dac_request(struct radius_server_data *data, const char *req) | |
2740 | { | |
2741 | #ifdef CONFIG_SQLITE | |
2742 | char *sql; | |
2743 | int res; | |
2744 | int disconnect; | |
2745 | const char *pos = req; | |
2746 | u8 addr[ETH_ALEN]; | |
2747 | char addrtxt[3 * ETH_ALEN]; | |
2748 | int t_c_clear = 0; | |
2749 | struct db_session_fields fields; | |
2750 | struct sockaddr_in das; | |
2751 | struct radius_client *client; | |
2752 | struct radius_msg *msg; | |
2753 | struct wpabuf *buf; | |
2754 | u8 identifier; | |
2755 | struct os_time now; | |
2756 | ||
2757 | if (!data) | |
2758 | return -1; | |
2759 | ||
2760 | /* req: <disconnect|coa> <MAC Address> [t_c_clear] */ | |
2761 | ||
2762 | if (os_strncmp(pos, "disconnect ", 11) == 0) { | |
2763 | disconnect = 1; | |
2764 | pos += 11; | |
2765 | } else if (os_strncmp(req, "coa ", 4) == 0) { | |
2766 | disconnect = 0; | |
2767 | pos += 4; | |
2768 | } else { | |
2769 | return -1; | |
2770 | } | |
2771 | ||
2772 | if (hwaddr_aton(pos, addr)) | |
2773 | return -1; | |
2774 | pos = os_strchr(pos, ' '); | |
2775 | if (pos) { | |
2776 | if (os_strstr(pos, "t_c_clear")) | |
2777 | t_c_clear = 1; | |
2778 | } | |
2779 | ||
2780 | if (!disconnect && !t_c_clear) { | |
2781 | RADIUS_ERROR("DAC request for CoA without any authorization change"); | |
2782 | return -1; | |
2783 | } | |
2784 | ||
2785 | if (!data->db) { | |
2786 | RADIUS_ERROR("SQLite database not in use"); | |
2787 | return -1; | |
2788 | } | |
2789 | ||
2790 | os_snprintf(addrtxt, sizeof(addrtxt), MACSTR, MAC2STR(addr)); | |
2791 | ||
2792 | sql = sqlite3_mprintf("SELECT * FROM current_sessions WHERE mac_addr=%Q", | |
2793 | addrtxt); | |
2794 | if (!sql) | |
2795 | return -1; | |
2796 | ||
2797 | os_memset(&fields, 0, sizeof(fields)); | |
2798 | res = sqlite3_exec(data->db, sql, get_db_session_fields, &fields, NULL); | |
2799 | sqlite3_free(sql); | |
2800 | if (res != SQLITE_OK) { | |
2801 | RADIUS_ERROR("Failed to find matching current_sessions entry from sqlite database: %s", | |
2802 | sqlite3_errmsg(data->db)); | |
2803 | free_db_session_fields(&fields); | |
2804 | return -1; | |
2805 | } | |
2806 | ||
2807 | if (!fields.nas) { | |
2808 | RADIUS_ERROR("No NAS information found from current_sessions"); | |
2809 | free_db_session_fields(&fields); | |
2810 | return -1; | |
2811 | } | |
2812 | ||
2813 | os_memset(&das, 0, sizeof(das)); | |
2814 | das.sin_family = AF_INET; | |
2815 | das.sin_addr.s_addr = inet_addr(fields.nas); | |
2816 | das.sin_port = htons(3799); | |
2817 | ||
2818 | free_db_session_fields(&fields); | |
2819 | ||
2820 | client = radius_server_get_client(data, &das.sin_addr, 0); | |
2821 | if (!client) { | |
2822 | RADIUS_ERROR("No NAS information available to protect the packet"); | |
2823 | return -1; | |
2824 | } | |
2825 | ||
2826 | identifier = client->next_dac_identifier++; | |
2827 | ||
2828 | msg = radius_msg_new(disconnect ? RADIUS_CODE_DISCONNECT_REQUEST : | |
2829 | RADIUS_CODE_COA_REQUEST, identifier); | |
2830 | if (!msg) | |
2831 | return -1; | |
2832 | ||
2833 | os_snprintf(addrtxt, sizeof(addrtxt), RADIUS_802_1X_ADDR_FORMAT, | |
2834 | MAC2STR(addr)); | |
2835 | if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID, | |
2836 | (u8 *) addrtxt, os_strlen(addrtxt))) { | |
2837 | RADIUS_ERROR("Could not add Calling-Station-Id"); | |
2838 | radius_msg_free(msg); | |
2839 | return -1; | |
2840 | } | |
2841 | ||
2842 | if (!disconnect && t_c_clear) { | |
2843 | u8 val[4] = { 0x00, 0x00, 0x00, 0x00 }; /* E=0 */ | |
2844 | ||
2845 | if (!radius_msg_add_wfa( | |
2846 | msg, RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILTERING, | |
2847 | val, sizeof(val))) { | |
2848 | RADIUS_DEBUG("Failed to add WFA-HS20-T-C-Filtering"); | |
2849 | radius_msg_free(msg); | |
2850 | return -1; | |
2851 | } | |
2852 | } | |
2853 | ||
2854 | os_get_time(&now); | |
2855 | if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP, | |
2856 | now.sec)) { | |
2857 | RADIUS_ERROR("Failed to add Event-Timestamp attribute"); | |
2858 | radius_msg_free(msg); | |
2859 | return -1; | |
2860 | } | |
2861 | ||
2862 | radius_msg_finish_acct(msg, (u8 *) client->shared_secret, | |
2863 | client->shared_secret_len); | |
2864 | ||
2865 | if (wpa_debug_level <= MSG_MSGDUMP) | |
2866 | radius_msg_dump(msg); | |
2867 | ||
2868 | buf = radius_msg_get_buf(msg); | |
2869 | if (sendto(data->auth_sock, wpabuf_head(buf), wpabuf_len(buf), 0, | |
2870 | (struct sockaddr *) &das, sizeof(das)) < 0) { | |
2871 | RADIUS_ERROR("Failed to send packet - sendto: %s", | |
2872 | strerror(errno)); | |
2873 | radius_msg_free(msg); | |
2874 | return -1; | |
2875 | } | |
2876 | ||
2877 | if (disconnect) { | |
2878 | radius_msg_free(client->pending_dac_disconnect_req); | |
2879 | client->pending_dac_disconnect_req = msg; | |
2880 | client->pending_dac_disconnect_id = identifier; | |
2881 | os_memcpy(client->pending_dac_disconnect_addr, addr, ETH_ALEN); | |
2882 | } else { | |
2883 | radius_msg_free(client->pending_dac_coa_req); | |
2884 | client->pending_dac_coa_req = msg; | |
2885 | client->pending_dac_coa_id = identifier; | |
2886 | os_memcpy(client->pending_dac_coa_addr, addr, ETH_ALEN); | |
2887 | } | |
2888 | ||
2889 | return 0; | |
2890 | #else /* CONFIG_SQLITE */ | |
2891 | return -1; | |
2892 | #endif /* CONFIG_SQLITE */ | |
2893 | } |