radius_server.c
Go to the documentation of this file.
00001 /*
00002  * RADIUS authentication server
00003  * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
00004  *
00005  * This program is free software; you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License version 2 as
00007  * published by the Free Software Foundation.
00008  *
00009  * Alternatively, this software may be distributed under the terms of BSD
00010  * license.
00011  *
00012  * See README and COPYING for more details.
00013  */
00014 
00015 #include "includes.h"
00016 #include <net/if.h>
00017 
00018 #include "common.h"
00019 #include "radius.h"
00020 #include "eloop.h"
00021 #include "eap_server/eap.h"
00022 #include "radius_server.h"
00023 
00027 #define RADIUS_SESSION_TIMEOUT 60
00028 
00032 #define RADIUS_MAX_SESSION 100
00033 
00037 #define RADIUS_MAX_MSG_LEN 3000
00038 
00039 static struct eapol_callbacks radius_server_eapol_cb;
00040 
00041 struct radius_client;
00042 struct radius_server_data;
00043 
00047 struct radius_server_counters {
00048         u32 access_requests;
00049         u32 invalid_requests;
00050         u32 dup_access_requests;
00051         u32 access_accepts;
00052         u32 access_rejects;
00053         u32 access_challenges;
00054         u32 malformed_access_requests;
00055         u32 bad_authenticators;
00056         u32 packets_dropped;
00057         u32 unknown_types;
00058 };
00059 
00063 struct radius_session {
00064         struct radius_session *next;
00065         struct radius_client *client;
00066         struct radius_server_data *server;
00067         unsigned int sess_id;
00068         struct eap_sm *eap;
00069         struct eap_eapol_interface *eap_if;
00070 
00071         struct radius_msg *last_msg;
00072         char *last_from_addr;
00073         int last_from_port;
00074         struct sockaddr_storage last_from;
00075         socklen_t last_fromlen;
00076         u8 last_identifier;
00077         struct radius_msg *last_reply;
00078         u8 last_authenticator[16];
00079 };
00080 
00084 struct radius_client {
00085         struct radius_client *next;
00086         struct in_addr addr;
00087         struct in_addr mask;
00088 #ifdef CONFIG_IPV6
00089         struct in6_addr addr6;
00090         struct in6_addr mask6;
00091 #endif /* CONFIG_IPV6 */
00092         char *shared_secret;
00093         int shared_secret_len;
00094         struct radius_session *sessions;
00095         struct radius_server_counters counters;
00096 };
00097 
00101 struct radius_server_data {
00105         int auth_sock;
00106 
00110         struct radius_client *clients;
00111 
00115         unsigned int next_sess_id;
00116 
00122         void *conf_ctx;
00123 
00127         int num_sess;
00128 
00135         void *eap_sim_db_priv;
00136 
00143         void *ssl_ctx;
00144 
00152         u8 *pac_opaque_encr_key;
00153 
00163         u8 *eap_fast_a_id;
00164 
00168         size_t eap_fast_a_id_len;
00169 
00178         char *eap_fast_a_id_info;
00179 
00187         int eap_fast_prov;
00188 
00195         int pac_key_lifetime;
00196 
00204         int pac_key_refresh_time;
00205 
00212         int eap_sim_aka_result_ind;
00213 
00222         int tnc;
00223 
00231         struct wps_context *wps;
00232 
00236         int ipv6;
00237 
00241         struct os_time start_time;
00242 
00248         struct radius_server_counters counters;
00249 
00264         int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len,
00265                             int phase2, struct eap_user *user);
00266 
00277         char *eap_req_id_text;
00278 
00282         size_t eap_req_id_text_len;
00283 
00284         /*
00285          * msg_ctx - Context data for wpa_msg() calls
00286          */
00287         void *msg_ctx;
00288 };
00289 
00290 
00291 extern int wpa_debug_level;
00292 
00293 #define RADIUS_DEBUG(args...) \
00294 wpa_printf(MSG_DEBUG, "RADIUS SRV: " args)
00295 #define RADIUS_ERROR(args...) \
00296 wpa_printf(MSG_ERROR, "RADIUS SRV: " args)
00297 #define RADIUS_DUMP(args...) \
00298 wpa_hexdump(MSG_MSGDUMP, "RADIUS SRV: " args)
00299 #define RADIUS_DUMP_ASCII(args...) \
00300 wpa_hexdump_ascii(MSG_MSGDUMP, "RADIUS SRV: " args)
00301 
00302 
00303 static void radius_server_session_timeout(void *eloop_ctx, void *timeout_ctx);
00304 static void radius_server_session_remove_timeout(void *eloop_ctx,
00305                                                  void *timeout_ctx);
00306 
00307 
00308 static struct radius_client *
00309 radius_server_get_client(struct radius_server_data *data, struct in_addr *addr,
00310                          int ipv6)
00311 {
00312         struct radius_client *client = data->clients;
00313 
00314         while (client) {
00315 #ifdef CONFIG_IPV6
00316                 if (ipv6) {
00317                         struct in6_addr *addr6;
00318                         int i;
00319 
00320                         addr6 = (struct in6_addr *) addr;
00321                         for (i = 0; i < 16; i++) {
00322                                 if ((addr6->s6_addr[i] &
00323                                      client->mask6.s6_addr[i]) !=
00324                                     (client->addr6.s6_addr[i] &
00325                                      client->mask6.s6_addr[i])) {
00326                                         i = 17;
00327                                         break;
00328                                 }
00329                         }
00330                         if (i == 16) {
00331                                 break;
00332                         }
00333                 }
00334 #endif /* CONFIG_IPV6 */
00335                 if (!ipv6 && (client->addr.s_addr & client->mask.s_addr) ==
00336                     (addr->s_addr & client->mask.s_addr)) {
00337                         break;
00338                 }
00339 
00340                 client = client->next;
00341         }
00342 
00343         return client;
00344 }
00345 
00346 
00347 static struct radius_session *
00348 radius_server_get_session(struct radius_client *client, unsigned int sess_id)
00349 {
00350         struct radius_session *sess = client->sessions;
00351 
00352         while (sess) {
00353                 if (sess->sess_id == sess_id) {
00354                         break;
00355                 }
00356                 sess = sess->next;
00357         }
00358 
00359         return sess;
00360 }
00361 
00362 
00363 static void radius_server_session_free(struct radius_server_data *data,
00364                                        struct radius_session *sess)
00365 {
00366         eloop_cancel_timeout(radius_server_session_timeout, data, sess);
00367         eloop_cancel_timeout(radius_server_session_remove_timeout, data, sess);
00368         eap_server_sm_deinit(sess->eap);
00369         radius_msg_free(sess->last_msg);
00370         os_free(sess->last_from_addr);
00371         radius_msg_free(sess->last_reply);
00372         os_free(sess);
00373         data->num_sess--;
00374 }
00375 
00376 
00377 static void radius_server_session_remove(struct radius_server_data *data,
00378                                          struct radius_session *sess)
00379 {
00380         struct radius_client *client = sess->client;
00381         struct radius_session *session, *prev;
00382 
00383         eloop_cancel_timeout(radius_server_session_remove_timeout, data, sess);
00384 
00385         prev = NULL;
00386         session = client->sessions;
00387         while (session) {
00388                 if (session == sess) {
00389                         if (prev == NULL) {
00390                                 client->sessions = sess->next;
00391                         } else {
00392                                 prev->next = sess->next;
00393                         }
00394                         radius_server_session_free(data, sess);
00395                         break;
00396                 }
00397                 prev = session;
00398                 session = session->next;
00399         }
00400 }
00401 
00402 
00403 static void radius_server_session_remove_timeout(void *eloop_ctx,
00404                                                  void *timeout_ctx)
00405 {
00406         struct radius_server_data *data = eloop_ctx;
00407         struct radius_session *sess = timeout_ctx;
00408         RADIUS_DEBUG("Removing completed session 0x%x", sess->sess_id);
00409         radius_server_session_remove(data, sess);
00410 }
00411 
00412 
00413 static void radius_server_session_timeout(void *eloop_ctx, void *timeout_ctx)
00414 {
00415         struct radius_server_data *data = eloop_ctx;
00416         struct radius_session *sess = timeout_ctx;
00417 
00418         RADIUS_DEBUG("Timing out authentication session 0x%x", sess->sess_id);
00419         radius_server_session_remove(data, sess);
00420 }
00421 
00422 
00423 static struct radius_session *
00424 radius_server_new_session(struct radius_server_data *data,
00425                           struct radius_client *client)
00426 {
00427         struct radius_session *sess;
00428 
00429         if (data->num_sess >= RADIUS_MAX_SESSION) {
00430                 RADIUS_DEBUG("Maximum number of existing session - no room "
00431                              "for a new session");
00432                 return NULL;
00433         }
00434 
00435         sess = os_zalloc(sizeof(*sess));
00436         if (sess == NULL)
00437                 return NULL;
00438 
00439         sess->server = data;
00440         sess->client = client;
00441         sess->sess_id = data->next_sess_id++;
00442         sess->next = client->sessions;
00443         client->sessions = sess;
00444         eloop_register_timeout(RADIUS_SESSION_TIMEOUT, 0,
00445                                radius_server_session_timeout, data, sess);
00446         data->num_sess++;
00447         return sess;
00448 }
00449 
00450 
00451 static struct radius_session *
00452 radius_server_get_new_session(struct radius_server_data *data,
00453                               struct radius_client *client,
00454                               struct radius_msg *msg)
00455 {
00456         u8 *user;
00457         size_t user_len;
00458         int res;
00459         struct radius_session *sess;
00460         struct eap_config eap_conf;
00461 
00462         RADIUS_DEBUG("Creating a new session");
00463 
00464         user = os_malloc(256);
00465         if (user == NULL) {
00466                 return NULL;
00467         }
00468         res = radius_msg_get_attr(msg, RADIUS_ATTR_USER_NAME, user, 256);
00469         if (res < 0 || res > 256) {
00470                 RADIUS_DEBUG("Could not get User-Name");
00471                 os_free(user);
00472                 return NULL;
00473         }
00474         user_len = res;
00475         RADIUS_DUMP_ASCII("User-Name", user, user_len);
00476 
00477         res = data->get_eap_user(data->conf_ctx, user, user_len, 0, NULL);
00478         os_free(user);
00479 
00480         if (res == 0) {
00481                 RADIUS_DEBUG("Matching user entry found");
00482                 sess = radius_server_new_session(data, client);
00483                 if (sess == NULL) {
00484                         RADIUS_DEBUG("Failed to create a new session");
00485                         return NULL;
00486                 }
00487         } else {
00488                 RADIUS_DEBUG("User-Name not found from user database");
00489                 return NULL;
00490         }
00491 
00492         os_memset(&eap_conf, 0, sizeof(eap_conf));
00493         eap_conf.ssl_ctx = data->ssl_ctx;
00494         eap_conf.msg_ctx = data->msg_ctx;
00495         eap_conf.eap_sim_db_priv = data->eap_sim_db_priv;
00496         eap_conf.backend_auth = TRUE;
00497         eap_conf.eap_server = 1;
00498         eap_conf.pac_opaque_encr_key = data->pac_opaque_encr_key;
00499         eap_conf.eap_fast_a_id = data->eap_fast_a_id;
00500         eap_conf.eap_fast_a_id_len = data->eap_fast_a_id_len;
00501         eap_conf.eap_fast_a_id_info = data->eap_fast_a_id_info;
00502         eap_conf.eap_fast_prov = data->eap_fast_prov;
00503         eap_conf.pac_key_lifetime = data->pac_key_lifetime;
00504         eap_conf.pac_key_refresh_time = data->pac_key_refresh_time;
00505         eap_conf.eap_sim_aka_result_ind = data->eap_sim_aka_result_ind;
00506         eap_conf.tnc = data->tnc;
00507         eap_conf.wps = data->wps;
00508         sess->eap = eap_server_sm_init(sess, &radius_server_eapol_cb,
00509                                        &eap_conf);
00510         if (sess->eap == NULL) {
00511                 RADIUS_DEBUG("Failed to initialize EAP state machine for the "
00512                              "new session");
00513                 radius_server_session_free(data, sess);
00514                 return NULL;
00515         }
00516         sess->eap_if = eap_get_interface(sess->eap);
00517         sess->eap_if->eapRestart = TRUE;
00518         sess->eap_if->portEnabled = TRUE;
00519 
00520         RADIUS_DEBUG("New session 0x%x initialized", sess->sess_id);
00521 
00522         return sess;
00523 }
00524 
00525 
00526 static struct radius_msg *
00527 radius_server_encapsulate_eap(struct radius_server_data *data,
00528                               struct radius_client *client,
00529                               struct radius_session *sess,
00530                               struct radius_msg *request)
00531 {
00532         struct radius_msg *msg;
00533         int code;
00534         unsigned int sess_id;
00535         struct radius_hdr *hdr = radius_msg_get_hdr(request);
00536 
00537         if (sess->eap_if->eapFail) {
00538                 sess->eap_if->eapFail = FALSE;
00539                 code = RADIUS_CODE_ACCESS_REJECT;
00540         } else if (sess->eap_if->eapSuccess) {
00541                 sess->eap_if->eapSuccess = FALSE;
00542                 code = RADIUS_CODE_ACCESS_ACCEPT;
00543         } else {
00544                 sess->eap_if->eapReq = FALSE;
00545                 code = RADIUS_CODE_ACCESS_CHALLENGE;
00546         }
00547 
00548         msg = radius_msg_new(code, hdr->identifier);
00549         if (msg == NULL) {
00550                 RADIUS_DEBUG("Failed to allocate reply message");
00551                 return NULL;
00552         }
00553 
00554         sess_id = htonl(sess->sess_id);
00555         if (code == RADIUS_CODE_ACCESS_CHALLENGE &&
00556             !radius_msg_add_attr(msg, RADIUS_ATTR_STATE,
00557                                  (u8 *) &sess_id, sizeof(sess_id))) {
00558                 RADIUS_DEBUG("Failed to add State attribute");
00559         }
00560 
00561         if (sess->eap_if->eapReqData &&
00562             !radius_msg_add_eap(msg, wpabuf_head(sess->eap_if->eapReqData),
00563                                 wpabuf_len(sess->eap_if->eapReqData))) {
00564                 RADIUS_DEBUG("Failed to add EAP-Message attribute");
00565         }
00566 
00567         if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->eap_if->eapKeyData) {
00568                 int len;
00569                 if (sess->eap_if->eapKeyDataLen > 64) {
00570                         len = 32;
00571                 } else {
00572                         len = sess->eap_if->eapKeyDataLen / 2;
00573                 }
00574                 if (!radius_msg_add_mppe_keys(msg, hdr->authenticator,
00575                                               (u8 *) client->shared_secret,
00576                                               client->shared_secret_len,
00577                                               sess->eap_if->eapKeyData + len,
00578                                               len, sess->eap_if->eapKeyData,
00579                                               len)) {
00580                         RADIUS_DEBUG("Failed to add MPPE key attributes");
00581                 }
00582         }
00583 
00584         if (radius_msg_copy_attr(msg, request, RADIUS_ATTR_PROXY_STATE) < 0) {
00585                 RADIUS_DEBUG("Failed to copy Proxy-State attribute(s)");
00586                 radius_msg_free(msg);
00587                 return NULL;
00588         }
00589 
00590         if (radius_msg_finish_srv(msg, (u8 *) client->shared_secret,
00591                                   client->shared_secret_len,
00592                                   hdr->authenticator) < 0) {
00593                 RADIUS_DEBUG("Failed to add Message-Authenticator attribute");
00594         }
00595 
00596         return msg;
00597 }
00598 
00599 
00600 static int radius_server_reject(struct radius_server_data *data,
00601                                 struct radius_client *client,
00602                                 struct radius_msg *request,
00603                                 struct sockaddr *from, socklen_t fromlen,
00604                                 const char *from_addr, int from_port)
00605 {
00606         struct radius_msg *msg;
00607         int ret = 0;
00608         struct eap_hdr eapfail;
00609         struct wpabuf *buf;
00610         struct radius_hdr *hdr = radius_msg_get_hdr(request);
00611 
00612         RADIUS_DEBUG("Reject invalid request from %s:%d",
00613                      from_addr, from_port);
00614 
00615         msg = radius_msg_new(RADIUS_CODE_ACCESS_REJECT, hdr->identifier);
00616         if (msg == NULL) {
00617                 return -1;
00618         }
00619 
00620         os_memset(&eapfail, 0, sizeof(eapfail));
00621         eapfail.code = EAP_CODE_FAILURE;
00622         eapfail.identifier = 0;
00623         eapfail.length = host_to_be16(sizeof(eapfail));
00624 
00625         if (!radius_msg_add_eap(msg, (u8 *) &eapfail, sizeof(eapfail))) {
00626                 RADIUS_DEBUG("Failed to add EAP-Message attribute");
00627         }
00628 
00629         if (radius_msg_copy_attr(msg, request, RADIUS_ATTR_PROXY_STATE) < 0) {
00630                 RADIUS_DEBUG("Failed to copy Proxy-State attribute(s)");
00631                 radius_msg_free(msg);
00632                 return -1;
00633         }
00634 
00635         if (radius_msg_finish_srv(msg, (u8 *) client->shared_secret,
00636                                   client->shared_secret_len,
00637                                   hdr->authenticator) <
00638             0) {
00639                 RADIUS_DEBUG("Failed to add Message-Authenticator attribute");
00640         }
00641 
00642         if (wpa_debug_level <= MSG_MSGDUMP) {
00643                 radius_msg_dump(msg);
00644         }
00645 
00646         data->counters.access_rejects++;
00647         client->counters.access_rejects++;
00648         buf = radius_msg_get_buf(msg);
00649         if (sendto(data->auth_sock, wpabuf_head(buf), wpabuf_len(buf), 0,
00650                    (struct sockaddr *) from, sizeof(*from)) < 0) {
00651                 perror("sendto[RADIUS SRV]");
00652                 ret = -1;
00653         }
00654 
00655         radius_msg_free(msg);
00656 
00657         return ret;
00658 }
00659 
00660 
00661 static int radius_server_request(struct radius_server_data *data,
00662                                  struct radius_msg *msg,
00663                                  struct sockaddr *from, socklen_t fromlen,
00664                                  struct radius_client *client,
00665                                  const char *from_addr, int from_port,
00666                                  struct radius_session *force_sess)
00667 {
00668         u8 *eap = NULL;
00669         size_t eap_len;
00670         int res, state_included = 0;
00671         u8 statebuf[4];
00672         unsigned int state;
00673         struct radius_session *sess;
00674         struct radius_msg *reply;
00675         int is_complete = 0;
00676 
00677         if (force_sess)
00678                 sess = force_sess;
00679         else {
00680                 res = radius_msg_get_attr(msg, RADIUS_ATTR_STATE, statebuf,
00681                                           sizeof(statebuf));
00682                 state_included = res >= 0;
00683                 if (res == sizeof(statebuf)) {
00684                         state = WPA_GET_BE32(statebuf);
00685                         sess = radius_server_get_session(client, state);
00686                 } else {
00687                         sess = NULL;
00688                 }
00689         }
00690 
00691         if (sess) {
00692                 RADIUS_DEBUG("Request for session 0x%x", sess->sess_id);
00693         } else if (state_included) {
00694                 RADIUS_DEBUG("State attribute included but no session found");
00695                 radius_server_reject(data, client, msg, from, fromlen,
00696                                      from_addr, from_port);
00697                 return -1;
00698         } else {
00699                 sess = radius_server_get_new_session(data, client, msg);
00700                 if (sess == NULL) {
00701                         RADIUS_DEBUG("Could not create a new session");
00702                         radius_server_reject(data, client, msg, from, fromlen,
00703                                              from_addr, from_port);
00704                         return -1;
00705                 }
00706         }
00707 
00708         if (sess->last_from_port == from_port &&
00709             sess->last_identifier == radius_msg_get_hdr(msg)->identifier &&
00710             os_memcmp(sess->last_authenticator,
00711                       radius_msg_get_hdr(msg)->authenticator, 16) == 0) {
00712                 RADIUS_DEBUG("Duplicate message from %s", from_addr);
00713                 data->counters.dup_access_requests++;
00714                 client->counters.dup_access_requests++;
00715 
00716                 if (sess->last_reply) {
00717                         struct wpabuf *buf;
00718                         buf = radius_msg_get_buf(sess->last_reply);
00719                         res = sendto(data->auth_sock, wpabuf_head(buf),
00720                                      wpabuf_len(buf), 0,
00721                                      (struct sockaddr *) from, fromlen);
00722                         if (res < 0) {
00723                                 perror("sendto[RADIUS SRV]");
00724                         }
00725                         return 0;
00726                 }
00727 
00728                 RADIUS_DEBUG("No previous reply available for duplicate "
00729                              "message");
00730                 return -1;
00731         }
00732                       
00733         eap = radius_msg_get_eap(msg, &eap_len);
00734         if (eap == NULL) {
00735                 RADIUS_DEBUG("No EAP-Message in RADIUS packet from %s",
00736                              from_addr);
00737                 data->counters.packets_dropped++;
00738                 client->counters.packets_dropped++;
00739                 return -1;
00740         }
00741 
00742         RADIUS_DUMP("Received EAP data", eap, eap_len);
00743 
00744         /* FIX: if Code is Request, Success, or Failure, send Access-Reject;
00745          * RFC3579 Sect. 2.6.2.
00746          * Include EAP-Response/Nak with no preferred method if
00747          * code == request.
00748          * If code is not 1-4, discard the packet silently.
00749          * Or is this already done by the EAP state machine? */
00750 
00751         wpabuf_free(sess->eap_if->eapRespData);
00752         sess->eap_if->eapRespData = wpabuf_alloc_ext_data(eap, eap_len);
00753         if (sess->eap_if->eapRespData == NULL)
00754                 os_free(eap);
00755         eap = NULL;
00756         sess->eap_if->eapResp = TRUE;
00757         eap_server_sm_step(sess->eap);
00758 
00759         if ((sess->eap_if->eapReq || sess->eap_if->eapSuccess ||
00760              sess->eap_if->eapFail) && sess->eap_if->eapReqData) {
00761                 RADIUS_DUMP("EAP data from the state machine",
00762                             wpabuf_head(sess->eap_if->eapReqData),
00763                             wpabuf_len(sess->eap_if->eapReqData));
00764         } else if (sess->eap_if->eapFail) {
00765                 RADIUS_DEBUG("No EAP data from the state machine, but eapFail "
00766                              "set");
00767         } else if (eap_sm_method_pending(sess->eap)) {
00768                 radius_msg_free(sess->last_msg);
00769                 sess->last_msg = msg;
00770                 sess->last_from_port = from_port;
00771                 os_free(sess->last_from_addr);
00772                 sess->last_from_addr = os_strdup(from_addr);
00773                 sess->last_fromlen = fromlen;
00774                 os_memcpy(&sess->last_from, from, fromlen);
00775                 return -2;
00776         } else {
00777                 RADIUS_DEBUG("No EAP data from the state machine - ignore this"
00778                              " Access-Request silently (assuming it was a "
00779                              "duplicate)");
00780                 data->counters.packets_dropped++;
00781                 client->counters.packets_dropped++;
00782                 return -1;
00783         }
00784 
00785         if (sess->eap_if->eapSuccess || sess->eap_if->eapFail)
00786                 is_complete = 1;
00787 
00788         reply = radius_server_encapsulate_eap(data, client, sess, msg);
00789 
00790         if (reply) {
00791                 struct wpabuf *buf;
00792                 struct radius_hdr *hdr;
00793 
00794                 RADIUS_DEBUG("Reply to %s:%d", from_addr, from_port);
00795                 if (wpa_debug_level <= MSG_MSGDUMP) {
00796                         radius_msg_dump(reply);
00797                 }
00798 
00799                 switch (radius_msg_get_hdr(reply)->code) {
00800                 case RADIUS_CODE_ACCESS_ACCEPT:
00801                         data->counters.access_accepts++;
00802                         client->counters.access_accepts++;
00803                         break;
00804                 case RADIUS_CODE_ACCESS_REJECT:
00805                         data->counters.access_rejects++;
00806                         client->counters.access_rejects++;
00807                         break;
00808                 case RADIUS_CODE_ACCESS_CHALLENGE:
00809                         data->counters.access_challenges++;
00810                         client->counters.access_challenges++;
00811                         break;
00812                 }
00813                 buf = radius_msg_get_buf(reply);
00814                 res = sendto(data->auth_sock, wpabuf_head(buf),
00815                              wpabuf_len(buf), 0,
00816                              (struct sockaddr *) from, fromlen);
00817                 if (res < 0) {
00818                         perror("sendto[RADIUS SRV]");
00819                 }
00820                 radius_msg_free(sess->last_reply);
00821                 sess->last_reply = reply;
00822                 sess->last_from_port = from_port;
00823                 hdr = radius_msg_get_hdr(msg);
00824                 sess->last_identifier = hdr->identifier;
00825                 os_memcpy(sess->last_authenticator, hdr->authenticator, 16);
00826         } else {
00827                 data->counters.packets_dropped++;
00828                 client->counters.packets_dropped++;
00829         }
00830 
00831         if (is_complete) {
00832                 RADIUS_DEBUG("Removing completed session 0x%x after timeout",
00833                              sess->sess_id);
00834                 eloop_cancel_timeout(radius_server_session_remove_timeout,
00835                                      data, sess);
00836                 eloop_register_timeout(10, 0,
00837                                        radius_server_session_remove_timeout,
00838                                        data, sess);
00839         }
00840 
00841         return 0;
00842 }
00843 
00844 
00845 static void radius_server_receive_auth(int sock, void *eloop_ctx,
00846                                        void *sock_ctx)
00847 {
00848         struct radius_server_data *data = eloop_ctx;
00849         u8 *buf = NULL;
00850         union {
00851                 struct sockaddr_storage ss;
00852                 struct sockaddr_in sin;
00853 #ifdef CONFIG_IPV6
00854                 struct sockaddr_in6 sin6;
00855 #endif /* CONFIG_IPV6 */
00856         } from;
00857         socklen_t fromlen;
00858         int len;
00859         struct radius_client *client = NULL;
00860         struct radius_msg *msg = NULL;
00861         char abuf[50];
00862         int from_port = 0;
00863 
00864         buf = os_malloc(RADIUS_MAX_MSG_LEN);
00865         if (buf == NULL) {
00866                 goto fail;
00867         }
00868 
00869         fromlen = sizeof(from);
00870         len = recvfrom(sock, buf, RADIUS_MAX_MSG_LEN, 0,
00871                        (struct sockaddr *) &from.ss, &fromlen);
00872         if (len < 0) {
00873                 perror("recvfrom[radius_server]");
00874                 goto fail;
00875         }
00876 
00877 #ifdef CONFIG_IPV6
00878         if (data->ipv6) {
00879                 if (inet_ntop(AF_INET6, &from.sin6.sin6_addr, abuf,
00880                               sizeof(abuf)) == NULL)
00881                         abuf[0] = '\0';
00882                 from_port = ntohs(from.sin6.sin6_port);
00883                 RADIUS_DEBUG("Received %d bytes from %s:%d",
00884                              len, abuf, from_port);
00885 
00886                 client = radius_server_get_client(data,
00887                                                   (struct in_addr *)
00888                                                   &from.sin6.sin6_addr, 1);
00889         }
00890 #endif /* CONFIG_IPV6 */
00891 
00892         if (!data->ipv6) {
00893                 os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf));
00894                 from_port = ntohs(from.sin.sin_port);
00895                 RADIUS_DEBUG("Received %d bytes from %s:%d",
00896                              len, abuf, from_port);
00897 
00898                 client = radius_server_get_client(data, &from.sin.sin_addr, 0);
00899         }
00900 
00901         RADIUS_DUMP("Received data", buf, len);
00902 
00903         if (client == NULL) {
00904                 RADIUS_DEBUG("Unknown client %s - packet ignored", abuf);
00905                 data->counters.invalid_requests++;
00906                 goto fail;
00907         }
00908 
00909         msg = radius_msg_parse(buf, len);
00910         if (msg == NULL) {
00911                 RADIUS_DEBUG("Parsing incoming RADIUS frame failed");
00912                 data->counters.malformed_access_requests++;
00913                 client->counters.malformed_access_requests++;
00914                 goto fail;
00915         }
00916 
00917         os_free(buf);
00918         buf = NULL;
00919 
00920         if (wpa_debug_level <= MSG_MSGDUMP) {
00921                 radius_msg_dump(msg);
00922         }
00923 
00924         if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCESS_REQUEST) {
00925                 RADIUS_DEBUG("Unexpected RADIUS code %d",
00926                              radius_msg_get_hdr(msg)->code);
00927                 data->counters.unknown_types++;
00928                 client->counters.unknown_types++;
00929                 goto fail;
00930         }
00931 
00932         data->counters.access_requests++;
00933         client->counters.access_requests++;
00934 
00935         if (radius_msg_verify_msg_auth(msg, (u8 *) client->shared_secret,
00936                                        client->shared_secret_len, NULL)) {
00937                 RADIUS_DEBUG("Invalid Message-Authenticator from %s", abuf);
00938                 data->counters.bad_authenticators++;
00939                 client->counters.bad_authenticators++;
00940                 goto fail;
00941         }
00942 
00943         if (radius_server_request(data, msg, (struct sockaddr *) &from,
00944                                   fromlen, client, abuf, from_port, NULL) ==
00945             -2)
00946                 return; /* msg was stored with the session */
00947 
00948 fail:
00949         radius_msg_free(msg);
00950         os_free(buf);
00951 }
00952 
00953 
00954 static int radius_server_disable_pmtu_discovery(int s)
00955 {
00956         int r = -1;
00957 #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
00958         /* Turn off Path MTU discovery on IPv4/UDP sockets. */
00959         int action = IP_PMTUDISC_DONT;
00960         r = setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, &action,
00961                        sizeof(action));
00962         if (r == -1)
00963                 wpa_printf(MSG_ERROR, "Failed to set IP_MTU_DISCOVER: "
00964                            "%s", strerror(errno));
00965 #endif
00966         return r;
00967 }
00968 
00969 
00970 static int radius_server_open_socket(int port)
00971 {
00972         int s;
00973         struct sockaddr_in addr;
00974 
00975         s = socket(PF_INET, SOCK_DGRAM, 0);
00976         if (s < 0) {
00977                 perror("socket");
00978                 return -1;
00979         }
00980 
00981         radius_server_disable_pmtu_discovery(s);
00982 
00983         os_memset(&addr, 0, sizeof(addr));
00984         addr.sin_family = AF_INET;
00985         addr.sin_port = htons(port);
00986         if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
00987                 perror("bind");
00988                 close(s);
00989                 return -1;
00990         }
00991 
00992         return s;
00993 }
00994 
00995 
00996 #ifdef CONFIG_IPV6
00997 static int radius_server_open_socket6(int port)
00998 {
00999         int s;
01000         struct sockaddr_in6 addr;
01001 
01002         s = socket(PF_INET6, SOCK_DGRAM, 0);
01003         if (s < 0) {
01004                 perror("socket[IPv6]");
01005                 return -1;
01006         }
01007 
01008         os_memset(&addr, 0, sizeof(addr));
01009         addr.sin6_family = AF_INET6;
01010         os_memcpy(&addr.sin6_addr, &in6addr_any, sizeof(in6addr_any));
01011         addr.sin6_port = htons(port);
01012         if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
01013                 perror("bind");
01014                 close(s);
01015                 return -1;
01016         }
01017 
01018         return s;
01019 }
01020 #endif /* CONFIG_IPV6 */
01021 
01022 
01023 static void radius_server_free_sessions(struct radius_server_data *data,
01024                                         struct radius_session *sessions)
01025 {
01026         struct radius_session *session, *prev;
01027 
01028         session = sessions;
01029         while (session) {
01030                 prev = session;
01031                 session = session->next;
01032                 radius_server_session_free(data, prev);
01033         }
01034 }
01035 
01036 
01037 static void radius_server_free_clients(struct radius_server_data *data,
01038                                        struct radius_client *clients)
01039 {
01040         struct radius_client *client, *prev;
01041 
01042         client = clients;
01043         while (client) {
01044                 prev = client;
01045                 client = client->next;
01046 
01047                 radius_server_free_sessions(data, prev->sessions);
01048                 os_free(prev->shared_secret);
01049                 os_free(prev);
01050         }
01051 }
01052 
01053 
01054 static struct radius_client *
01055 radius_server_read_clients(const char *client_file, int ipv6)
01056 {
01057         FILE *f;
01058         const int buf_size = 1024;
01059         char *buf, *pos;
01060         struct radius_client *clients, *tail, *entry;
01061         int line = 0, mask, failed = 0, i;
01062         struct in_addr addr;
01063 #ifdef CONFIG_IPV6
01064         struct in6_addr addr6;
01065 #endif /* CONFIG_IPV6 */
01066         unsigned int val;
01067 
01068         f = fopen(client_file, "r");
01069         if (f == NULL) {
01070                 RADIUS_ERROR("Could not open client file '%s'", client_file);
01071                 return NULL;
01072         }
01073 
01074         buf = os_malloc(buf_size);
01075         if (buf == NULL) {
01076                 fclose(f);
01077                 return NULL;
01078         }
01079 
01080         clients = tail = NULL;
01081         while (fgets(buf, buf_size, f)) {
01082                 /* Configuration file format:
01083                  * 192.168.1.0/24 secret
01084                  * 192.168.1.2 secret
01085                  * fe80::211:22ff:fe33:4455/64 secretipv6
01086                  */
01087                 line++;
01088                 buf[buf_size - 1] = '\0';
01089                 pos = buf;
01090                 while (*pos != '\0' && *pos != '\n')
01091                         pos++;
01092                 if (*pos == '\n')
01093                         *pos = '\0';
01094                 if (*buf == '\0' || *buf == '#')
01095                         continue;
01096 
01097                 pos = buf;
01098                 while ((*pos >= '0' && *pos <= '9') || *pos == '.' ||
01099                        (*pos >= 'a' && *pos <= 'f') || *pos == ':' ||
01100                        (*pos >= 'A' && *pos <= 'F')) {
01101                         pos++;
01102                 }
01103 
01104                 if (*pos == '\0') {
01105                         failed = 1;
01106                         break;
01107                 }
01108 
01109                 if (*pos == '/') {
01110                         char *end;
01111                         *pos++ = '\0';
01112                         mask = strtol(pos, &end, 10);
01113                         if ((pos == end) ||
01114                             (mask < 0 || mask > (ipv6 ? 128 : 32))) {
01115                                 failed = 1;
01116                                 break;
01117                         }
01118                         pos = end;
01119                 } else {
01120                         mask = ipv6 ? 128 : 32;
01121                         *pos++ = '\0';
01122                 }
01123 
01124                 if (!ipv6 && inet_aton(buf, &addr) == 0) {
01125                         failed = 1;
01126                         break;
01127                 }
01128 #ifdef CONFIG_IPV6
01129                 if (ipv6 && inet_pton(AF_INET6, buf, &addr6) <= 0) {
01130                         if (inet_pton(AF_INET, buf, &addr) <= 0) {
01131                                 failed = 1;
01132                                 break;
01133                         }
01134                         /* Convert IPv4 address to IPv6 */
01135                         if (mask <= 32)
01136                                 mask += (128 - 32);
01137                         os_memset(addr6.s6_addr, 0, 10);
01138                         addr6.s6_addr[10] = 0xff;
01139                         addr6.s6_addr[11] = 0xff;
01140                         os_memcpy(addr6.s6_addr + 12, (char *) &addr.s_addr,
01141                                   4);
01142                 }
01143 #endif /* CONFIG_IPV6 */
01144 
01145                 while (*pos == ' ' || *pos == '\t') {
01146                         pos++;
01147                 }
01148 
01149                 if (*pos == '\0') {
01150                         failed = 1;
01151                         break;
01152                 }
01153 
01154                 entry = os_zalloc(sizeof(*entry));
01155                 if (entry == NULL) {
01156                         failed = 1;
01157                         break;
01158                 }
01159                 entry->shared_secret = os_strdup(pos);
01160                 if (entry->shared_secret == NULL) {
01161                         failed = 1;
01162                         os_free(entry);
01163                         break;
01164                 }
01165                 entry->shared_secret_len = os_strlen(entry->shared_secret);
01166                 entry->addr.s_addr = addr.s_addr;
01167                 if (!ipv6) {
01168                         val = 0;
01169                         for (i = 0; i < mask; i++)
01170                                 val |= 1 << (31 - i);
01171                         entry->mask.s_addr = htonl(val);
01172                 }
01173 #ifdef CONFIG_IPV6
01174                 if (ipv6) {
01175                         int offset = mask / 8;
01176 
01177                         os_memcpy(entry->addr6.s6_addr, addr6.s6_addr, 16);
01178                         os_memset(entry->mask6.s6_addr, 0xff, offset);
01179                         val = 0;
01180                         for (i = 0; i < (mask % 8); i++)
01181                                 val |= 1 << (7 - i);
01182                         if (offset < 16)
01183                                 entry->mask6.s6_addr[offset] = val;
01184                 }
01185 #endif /* CONFIG_IPV6 */
01186 
01187                 if (tail == NULL) {
01188                         clients = tail = entry;
01189                 } else {
01190                         tail->next = entry;
01191                         tail = entry;
01192                 }
01193         }
01194 
01195         if (failed) {
01196                 RADIUS_ERROR("Invalid line %d in '%s'", line, client_file);
01197                 radius_server_free_clients(NULL, clients);
01198                 clients = NULL;
01199         }
01200 
01201         os_free(buf);
01202         fclose(f);
01203 
01204         return clients;
01205 }
01206 
01207 
01217 struct radius_server_data *
01218 radius_server_init(struct radius_server_conf *conf)
01219 {
01220         struct radius_server_data *data;
01221 
01222 #ifndef CONFIG_IPV6
01223         if (conf->ipv6) {
01224                 fprintf(stderr, "RADIUS server compiled without IPv6 "
01225                         "support.\n");
01226                 return NULL;
01227         }
01228 #endif /* CONFIG_IPV6 */
01229 
01230         data = os_zalloc(sizeof(*data));
01231         if (data == NULL)
01232                 return NULL;
01233 
01234         os_get_time(&data->start_time);
01235         data->conf_ctx = conf->conf_ctx;
01236         data->eap_sim_db_priv = conf->eap_sim_db_priv;
01237         data->ssl_ctx = conf->ssl_ctx;
01238         data->msg_ctx = conf->msg_ctx;
01239         data->ipv6 = conf->ipv6;
01240         if (conf->pac_opaque_encr_key) {
01241                 data->pac_opaque_encr_key = os_malloc(16);
01242                 os_memcpy(data->pac_opaque_encr_key, conf->pac_opaque_encr_key,
01243                           16);
01244         }
01245         if (conf->eap_fast_a_id) {
01246                 data->eap_fast_a_id = os_malloc(conf->eap_fast_a_id_len);
01247                 if (data->eap_fast_a_id) {
01248                         os_memcpy(data->eap_fast_a_id, conf->eap_fast_a_id,
01249                                   conf->eap_fast_a_id_len);
01250                         data->eap_fast_a_id_len = conf->eap_fast_a_id_len;
01251                 }
01252         }
01253         if (conf->eap_fast_a_id_info)
01254                 data->eap_fast_a_id_info = os_strdup(conf->eap_fast_a_id_info);
01255         data->eap_fast_prov = conf->eap_fast_prov;
01256         data->pac_key_lifetime = conf->pac_key_lifetime;
01257         data->pac_key_refresh_time = conf->pac_key_refresh_time;
01258         data->get_eap_user = conf->get_eap_user;
01259         data->eap_sim_aka_result_ind = conf->eap_sim_aka_result_ind;
01260         data->tnc = conf->tnc;
01261         data->wps = conf->wps;
01262         if (conf->eap_req_id_text) {
01263                 data->eap_req_id_text = os_malloc(conf->eap_req_id_text_len);
01264                 if (data->eap_req_id_text) {
01265                         os_memcpy(data->eap_req_id_text, conf->eap_req_id_text,
01266                                   conf->eap_req_id_text_len);
01267                         data->eap_req_id_text_len = conf->eap_req_id_text_len;
01268                 }
01269         }
01270 
01271         data->clients = radius_server_read_clients(conf->client_file,
01272                                                    conf->ipv6);
01273         if (data->clients == NULL) {
01274                 printf("No RADIUS clients configured.\n");
01275                 radius_server_deinit(data);
01276                 return NULL;
01277         }
01278 
01279 #ifdef CONFIG_IPV6
01280         if (conf->ipv6)
01281                 data->auth_sock = radius_server_open_socket6(conf->auth_port);
01282         else
01283 #endif /* CONFIG_IPV6 */
01284         data->auth_sock = radius_server_open_socket(conf->auth_port);
01285         if (data->auth_sock < 0) {
01286                 printf("Failed to open UDP socket for RADIUS authentication "
01287                        "server\n");
01288                 radius_server_deinit(data);
01289                 return NULL;
01290         }
01291         if (eloop_register_read_sock(data->auth_sock,
01292                                      radius_server_receive_auth,
01293                                      data, NULL)) {
01294                 radius_server_deinit(data);
01295                 return NULL;
01296         }
01297 
01298         return data;
01299 }
01300 
01301 
01306 void radius_server_deinit(struct radius_server_data *data)
01307 {
01308         if (data == NULL)
01309                 return;
01310 
01311         if (data->auth_sock >= 0) {
01312                 eloop_unregister_read_sock(data->auth_sock);
01313                 close(data->auth_sock);
01314         }
01315 
01316         radius_server_free_clients(data, data->clients);
01317 
01318         os_free(data->pac_opaque_encr_key);
01319         os_free(data->eap_fast_a_id);
01320         os_free(data->eap_fast_a_id_info);
01321         os_free(data->eap_req_id_text);
01322         os_free(data);
01323 }
01324 
01325 
01333 int radius_server_get_mib(struct radius_server_data *data, char *buf,
01334                           size_t buflen)
01335 {
01336         int ret, uptime;
01337         unsigned int idx;
01338         char *end, *pos;
01339         struct os_time now;
01340         struct radius_client *cli;
01341 
01342         /* RFC 2619 - RADIUS Authentication Server MIB */
01343 
01344         if (data == NULL || buflen == 0)
01345                 return 0;
01346 
01347         pos = buf;
01348         end = buf + buflen;
01349 
01350         os_get_time(&now);
01351         uptime = (now.sec - data->start_time.sec) * 100 +
01352                 ((now.usec - data->start_time.usec) / 10000) % 100;
01353         ret = os_snprintf(pos, end - pos,
01354                           "RADIUS-AUTH-SERVER-MIB\n"
01355                           "radiusAuthServIdent=hostapd\n"
01356                           "radiusAuthServUpTime=%d\n"
01357                           "radiusAuthServResetTime=0\n"
01358                           "radiusAuthServConfigReset=4\n",
01359                           uptime);
01360         if (ret < 0 || ret >= end - pos) {
01361                 *pos = '\0';
01362                 return pos - buf;
01363         }
01364         pos += ret;
01365 
01366         ret = os_snprintf(pos, end - pos,
01367                           "radiusAuthServTotalAccessRequests=%u\n"
01368                           "radiusAuthServTotalInvalidRequests=%u\n"
01369                           "radiusAuthServTotalDupAccessRequests=%u\n"
01370                           "radiusAuthServTotalAccessAccepts=%u\n"
01371                           "radiusAuthServTotalAccessRejects=%u\n"
01372                           "radiusAuthServTotalAccessChallenges=%u\n"
01373                           "radiusAuthServTotalMalformedAccessRequests=%u\n"
01374                           "radiusAuthServTotalBadAuthenticators=%u\n"
01375                           "radiusAuthServTotalPacketsDropped=%u\n"
01376                           "radiusAuthServTotalUnknownTypes=%u\n",
01377                           data->counters.access_requests,
01378                           data->counters.invalid_requests,
01379                           data->counters.dup_access_requests,
01380                           data->counters.access_accepts,
01381                           data->counters.access_rejects,
01382                           data->counters.access_challenges,
01383                           data->counters.malformed_access_requests,
01384                           data->counters.bad_authenticators,
01385                           data->counters.packets_dropped,
01386                           data->counters.unknown_types);
01387         if (ret < 0 || ret >= end - pos) {
01388                 *pos = '\0';
01389                 return pos - buf;
01390         }
01391         pos += ret;
01392 
01393         for (cli = data->clients, idx = 0; cli; cli = cli->next, idx++) {
01394                 char abuf[50], mbuf[50];
01395 #ifdef CONFIG_IPV6
01396                 if (data->ipv6) {
01397                         if (inet_ntop(AF_INET6, &cli->addr6, abuf,
01398                                       sizeof(abuf)) == NULL)
01399                                 abuf[0] = '\0';
01400                         if (inet_ntop(AF_INET6, &cli->mask6, abuf,
01401                                       sizeof(mbuf)) == NULL)
01402                                 mbuf[0] = '\0';
01403                 }
01404 #endif /* CONFIG_IPV6 */
01405                 if (!data->ipv6) {
01406                         os_strlcpy(abuf, inet_ntoa(cli->addr), sizeof(abuf));
01407                         os_strlcpy(mbuf, inet_ntoa(cli->mask), sizeof(mbuf));
01408                 }
01409 
01410                 ret = os_snprintf(pos, end - pos,
01411                                   "radiusAuthClientIndex=%u\n"
01412                                   "radiusAuthClientAddress=%s/%s\n"
01413                                   "radiusAuthServAccessRequests=%u\n"
01414                                   "radiusAuthServDupAccessRequests=%u\n"
01415                                   "radiusAuthServAccessAccepts=%u\n"
01416                                   "radiusAuthServAccessRejects=%u\n"
01417                                   "radiusAuthServAccessChallenges=%u\n"
01418                                   "radiusAuthServMalformedAccessRequests=%u\n"
01419                                   "radiusAuthServBadAuthenticators=%u\n"
01420                                   "radiusAuthServPacketsDropped=%u\n"
01421                                   "radiusAuthServUnknownTypes=%u\n",
01422                                   idx,
01423                                   abuf, mbuf,
01424                                   cli->counters.access_requests,
01425                                   cli->counters.dup_access_requests,
01426                                   cli->counters.access_accepts,
01427                                   cli->counters.access_rejects,
01428                                   cli->counters.access_challenges,
01429                                   cli->counters.malformed_access_requests,
01430                                   cli->counters.bad_authenticators,
01431                                   cli->counters.packets_dropped,
01432                                   cli->counters.unknown_types);
01433                 if (ret < 0 || ret >= end - pos) {
01434                         *pos = '\0';
01435                         return pos - buf;
01436                 }
01437                 pos += ret;
01438         }
01439 
01440         return pos - buf;
01441 }
01442 
01443 
01444 static int radius_server_get_eap_user(void *ctx, const u8 *identity,
01445                                       size_t identity_len, int phase2,
01446                                       struct eap_user *user)
01447 {
01448         struct radius_session *sess = ctx;
01449         struct radius_server_data *data = sess->server;
01450 
01451         return data->get_eap_user(data->conf_ctx, identity, identity_len,
01452                                   phase2, user);
01453 }
01454 
01455 
01456 static const char * radius_server_get_eap_req_id_text(void *ctx, size_t *len)
01457 {
01458         struct radius_session *sess = ctx;
01459         struct radius_server_data *data = sess->server;
01460         *len = data->eap_req_id_text_len;
01461         return data->eap_req_id_text;
01462 }
01463 
01464 
01465 static struct eapol_callbacks radius_server_eapol_cb =
01466 {
01467         .get_eap_user = radius_server_get_eap_user,
01468         .get_eap_req_id_text = radius_server_get_eap_req_id_text,
01469 };
01470 
01471 
01480 void radius_server_eap_pending_cb(struct radius_server_data *data, void *ctx)
01481 {
01482         struct radius_client *cli;
01483         struct radius_session *s, *sess = NULL;
01484         struct radius_msg *msg;
01485 
01486         if (data == NULL)
01487                 return;
01488 
01489         for (cli = data->clients; cli; cli = cli->next) {
01490                 for (s = cli->sessions; s; s = s->next) {
01491                         if (s->eap == ctx && s->last_msg) {
01492                                 sess = s;
01493                                 break;
01494                         }
01495                         if (sess)
01496                                 break;
01497                 }
01498                 if (sess)
01499                         break;
01500         }
01501 
01502         if (sess == NULL) {
01503                 RADIUS_DEBUG("No session matched callback ctx");
01504                 return;
01505         }
01506 
01507         msg = sess->last_msg;
01508         sess->last_msg = NULL;
01509         eap_sm_pending_cb(sess->eap);
01510         if (radius_server_request(data, msg,
01511                                   (struct sockaddr *) &sess->last_from,
01512                                   sess->last_fromlen, cli,
01513                                   sess->last_from_addr,
01514                                   sess->last_from_port, sess) == -2)
01515                 return; /* msg was stored with the session */
01516 
01517         radius_msg_free(msg);
01518 }


wpa_supplicant_node
Author(s): Package maintained by Blaise Gassend
autogenerated on Thu Jan 2 2014 11:25:14