radius.c
Go to the documentation of this file.
00001 /*
00002  * RADIUS message processing
00003  * Copyright (c) 2002-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 "utils/includes.h"
00016 
00017 #include "utils/common.h"
00018 #include "utils/wpabuf.h"
00019 #include "crypto/md5.h"
00020 #include "crypto/crypto.h"
00021 #include "radius.h"
00022 
00023 
00027 struct radius_msg {
00031         struct wpabuf *buf;
00032 
00036         struct radius_hdr *hdr;
00037 
00044         size_t *attr_pos;
00045 
00049         size_t attr_size;
00050 
00054         size_t attr_used;
00055 };
00056 
00057 
00058 struct radius_hdr * radius_msg_get_hdr(struct radius_msg *msg)
00059 {
00060         return msg->hdr;
00061 }
00062 
00063 
00064 struct wpabuf * radius_msg_get_buf(struct radius_msg *msg)
00065 {
00066         return msg->buf;
00067 }
00068 
00069 
00070 static struct radius_attr_hdr *
00071 radius_get_attr_hdr(struct radius_msg *msg, int idx)
00072 {
00073         return (struct radius_attr_hdr *)
00074                 (wpabuf_mhead_u8(msg->buf) + msg->attr_pos[idx]);
00075 }
00076 
00077 
00078 static void radius_msg_set_hdr(struct radius_msg *msg, u8 code, u8 identifier)
00079 {
00080         msg->hdr->code = code;
00081         msg->hdr->identifier = identifier;
00082 }
00083 
00084 
00085 static int radius_msg_initialize(struct radius_msg *msg)
00086 {
00087         msg->attr_pos =
00088                 os_zalloc(RADIUS_DEFAULT_ATTR_COUNT * sizeof(*msg->attr_pos));
00089         if (msg->attr_pos == NULL)
00090                 return -1;
00091 
00092         msg->attr_size = RADIUS_DEFAULT_ATTR_COUNT;
00093         msg->attr_used = 0;
00094 
00095         return 0;
00096 }
00097 
00098 
00108 struct radius_msg * radius_msg_new(u8 code, u8 identifier)
00109 {
00110         struct radius_msg *msg;
00111 
00112         msg = os_zalloc(sizeof(*msg));
00113         if (msg == NULL)
00114                 return NULL;
00115 
00116         msg->buf = wpabuf_alloc(RADIUS_DEFAULT_MSG_SIZE);
00117         if (msg->buf == NULL || radius_msg_initialize(msg)) {
00118                 radius_msg_free(msg);
00119                 return NULL;
00120         }
00121         msg->hdr = wpabuf_put(msg->buf, sizeof(struct radius_hdr));
00122 
00123         radius_msg_set_hdr(msg, code, identifier);
00124 
00125         return msg;
00126 }
00127 
00128 
00133 void radius_msg_free(struct radius_msg *msg)
00134 {
00135         if (msg == NULL)
00136                 return;
00137 
00138         wpabuf_free(msg->buf);
00139         os_free(msg->attr_pos);
00140         os_free(msg);
00141 }
00142 
00143 
00144 static const char *radius_code_string(u8 code)
00145 {
00146         switch (code) {
00147         case RADIUS_CODE_ACCESS_REQUEST: return "Access-Request";
00148         case RADIUS_CODE_ACCESS_ACCEPT: return "Access-Accept";
00149         case RADIUS_CODE_ACCESS_REJECT: return "Access-Reject";
00150         case RADIUS_CODE_ACCOUNTING_REQUEST: return "Accounting-Request";
00151         case RADIUS_CODE_ACCOUNTING_RESPONSE: return "Accounting-Response";
00152         case RADIUS_CODE_ACCESS_CHALLENGE: return "Access-Challenge";
00153         case RADIUS_CODE_STATUS_SERVER: return "Status-Server";
00154         case RADIUS_CODE_STATUS_CLIENT: return "Status-Client";
00155         case RADIUS_CODE_RESERVED: return "Reserved";
00156         default: return "?Unknown?";
00157         }
00158 }
00159 
00160 
00161 struct radius_attr_type {
00162         u8 type;
00163         char *name;
00164         enum {
00165                 RADIUS_ATTR_UNDIST, RADIUS_ATTR_TEXT, RADIUS_ATTR_IP,
00166                 RADIUS_ATTR_HEXDUMP, RADIUS_ATTR_INT32, RADIUS_ATTR_IPV6
00167         } data_type;
00168 };
00169 
00170 static struct radius_attr_type radius_attrs[] =
00171 {
00172         { RADIUS_ATTR_USER_NAME, "User-Name", RADIUS_ATTR_TEXT },
00173         { RADIUS_ATTR_USER_PASSWORD, "User-Password", RADIUS_ATTR_UNDIST },
00174         { RADIUS_ATTR_NAS_IP_ADDRESS, "NAS-IP-Address", RADIUS_ATTR_IP },
00175         { RADIUS_ATTR_NAS_PORT, "NAS-Port", RADIUS_ATTR_INT32 },
00176         { RADIUS_ATTR_FRAMED_MTU, "Framed-MTU", RADIUS_ATTR_INT32 },
00177         { RADIUS_ATTR_REPLY_MESSAGE, "Reply-Message", RADIUS_ATTR_TEXT },
00178         { RADIUS_ATTR_STATE, "State", RADIUS_ATTR_UNDIST },
00179         { RADIUS_ATTR_CLASS, "Class", RADIUS_ATTR_UNDIST },
00180         { RADIUS_ATTR_VENDOR_SPECIFIC, "Vendor-Specific", RADIUS_ATTR_UNDIST },
00181         { RADIUS_ATTR_SESSION_TIMEOUT, "Session-Timeout", RADIUS_ATTR_INT32 },
00182         { RADIUS_ATTR_IDLE_TIMEOUT, "Idle-Timeout", RADIUS_ATTR_INT32 },
00183         { RADIUS_ATTR_TERMINATION_ACTION, "Termination-Action",
00184           RADIUS_ATTR_INT32 },
00185         { RADIUS_ATTR_CALLED_STATION_ID, "Called-Station-Id",
00186           RADIUS_ATTR_TEXT },
00187         { RADIUS_ATTR_CALLING_STATION_ID, "Calling-Station-Id",
00188           RADIUS_ATTR_TEXT },
00189         { RADIUS_ATTR_NAS_IDENTIFIER, "NAS-Identifier", RADIUS_ATTR_TEXT },
00190         { RADIUS_ATTR_PROXY_STATE, "Proxy-State", RADIUS_ATTR_UNDIST },
00191         { RADIUS_ATTR_ACCT_STATUS_TYPE, "Acct-Status-Type",
00192           RADIUS_ATTR_INT32 },
00193         { RADIUS_ATTR_ACCT_DELAY_TIME, "Acct-Delay-Time", RADIUS_ATTR_INT32 },
00194         { RADIUS_ATTR_ACCT_INPUT_OCTETS, "Acct-Input-Octets",
00195           RADIUS_ATTR_INT32 },
00196         { RADIUS_ATTR_ACCT_OUTPUT_OCTETS, "Acct-Output-Octets",
00197           RADIUS_ATTR_INT32 },
00198         { RADIUS_ATTR_ACCT_SESSION_ID, "Acct-Session-Id", RADIUS_ATTR_TEXT },
00199         { RADIUS_ATTR_ACCT_AUTHENTIC, "Acct-Authentic", RADIUS_ATTR_INT32 },
00200         { RADIUS_ATTR_ACCT_SESSION_TIME, "Acct-Session-Time",
00201           RADIUS_ATTR_INT32 },
00202         { RADIUS_ATTR_ACCT_INPUT_PACKETS, "Acct-Input-Packets",
00203           RADIUS_ATTR_INT32 },
00204         { RADIUS_ATTR_ACCT_OUTPUT_PACKETS, "Acct-Output-Packets",
00205           RADIUS_ATTR_INT32 },
00206         { RADIUS_ATTR_ACCT_TERMINATE_CAUSE, "Acct-Terminate-Cause",
00207           RADIUS_ATTR_INT32 },
00208         { RADIUS_ATTR_ACCT_MULTI_SESSION_ID, "Acct-Multi-Session-Id",
00209           RADIUS_ATTR_TEXT },
00210         { RADIUS_ATTR_ACCT_LINK_COUNT, "Acct-Link-Count", RADIUS_ATTR_INT32 },
00211         { RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, "Acct-Input-Gigawords", 
00212           RADIUS_ATTR_INT32 },
00213         { RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS, "Acct-Output-Gigawords",
00214           RADIUS_ATTR_INT32 },
00215         { RADIUS_ATTR_EVENT_TIMESTAMP, "Event-Timestamp",
00216           RADIUS_ATTR_INT32 },
00217         { RADIUS_ATTR_NAS_PORT_TYPE, "NAS-Port-Type", RADIUS_ATTR_INT32 },
00218         { RADIUS_ATTR_TUNNEL_TYPE, "Tunnel-Type", RADIUS_ATTR_HEXDUMP },
00219         { RADIUS_ATTR_TUNNEL_MEDIUM_TYPE, "Tunnel-Medium-Type",
00220           RADIUS_ATTR_HEXDUMP },
00221         { RADIUS_ATTR_CONNECT_INFO, "Connect-Info", RADIUS_ATTR_TEXT },
00222         { RADIUS_ATTR_EAP_MESSAGE, "EAP-Message", RADIUS_ATTR_UNDIST },
00223         { RADIUS_ATTR_MESSAGE_AUTHENTICATOR, "Message-Authenticator",
00224           RADIUS_ATTR_UNDIST },
00225         { RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID, "Tunnel-Private-Group-Id",
00226           RADIUS_ATTR_HEXDUMP },
00227         { RADIUS_ATTR_ACCT_INTERIM_INTERVAL, "Acct-Interim-Interval",
00228           RADIUS_ATTR_INT32 },
00229         { RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, "Chargable-User-Identity",
00230           RADIUS_ATTR_TEXT },
00231         { RADIUS_ATTR_NAS_IPV6_ADDRESS, "NAS-IPv6-Address", RADIUS_ATTR_IPV6 },
00232 };
00233 #define RADIUS_ATTRS (sizeof(radius_attrs) / sizeof(radius_attrs[0]))
00234 
00235 
00236 static struct radius_attr_type *radius_get_attr_type(u8 type)
00237 {
00238         size_t i;
00239 
00240         for (i = 0; i < RADIUS_ATTRS; i++) {
00241                 if (type == radius_attrs[i].type)
00242                         return &radius_attrs[i];
00243         }
00244 
00245         return NULL;
00246 }
00247 
00248 
00249 static void print_char(char c)
00250 {
00251         if (c >= 32 && c < 127)
00252                 printf("%c", c);
00253         else
00254                 printf("<%02x>", c);
00255 }
00256 
00257 
00258 static void radius_msg_dump_attr(struct radius_attr_hdr *hdr)
00259 {
00260         struct radius_attr_type *attr;
00261         int i, len;
00262         unsigned char *pos;
00263 
00264         attr = radius_get_attr_type(hdr->type);
00265 
00266         printf("   Attribute %d (%s) length=%d\n",
00267                hdr->type, attr ? attr->name : "?Unknown?", hdr->length);
00268 
00269         if (attr == NULL)
00270                 return;
00271 
00272         len = hdr->length - sizeof(struct radius_attr_hdr);
00273         pos = (unsigned char *) (hdr + 1);
00274 
00275         switch (attr->data_type) {
00276         case RADIUS_ATTR_TEXT:
00277                 printf("      Value: '");
00278                 for (i = 0; i < len; i++)
00279                         print_char(pos[i]);
00280                 printf("'\n");
00281                 break;
00282 
00283         case RADIUS_ATTR_IP:
00284                 if (len == 4) {
00285                         struct in_addr addr;
00286                         os_memcpy(&addr, pos, 4);
00287                         printf("      Value: %s\n", inet_ntoa(addr));
00288                 } else
00289                         printf("      Invalid IP address length %d\n", len);
00290                 break;
00291 
00292 #ifdef CONFIG_IPV6
00293         case RADIUS_ATTR_IPV6:
00294                 if (len == 16) {
00295                         char buf[128];
00296                         const char *atxt;
00297                         struct in6_addr *addr = (struct in6_addr *) pos;
00298                         atxt = inet_ntop(AF_INET6, addr, buf, sizeof(buf));
00299                         printf("      Value: %s\n", atxt ? atxt : "?");
00300                 } else
00301                         printf("      Invalid IPv6 address length %d\n", len);
00302                 break;
00303 #endif /* CONFIG_IPV6 */
00304 
00305         case RADIUS_ATTR_HEXDUMP:
00306         case RADIUS_ATTR_UNDIST:
00307                 printf("      Value:");
00308                 for (i = 0; i < len; i++)
00309                         printf(" %02x", pos[i]);
00310                 printf("\n");
00311                 break;
00312 
00313         case RADIUS_ATTR_INT32:
00314                 if (len == 4)
00315                         printf("      Value: %u\n", WPA_GET_BE32(pos));
00316                 else
00317                         printf("      Invalid INT32 length %d\n", len);
00318                 break;
00319 
00320         default:
00321                 break;
00322         }
00323 }
00324 
00325 
00326 void radius_msg_dump(struct radius_msg *msg)
00327 {
00328         size_t i;
00329 
00330         printf("RADIUS message: code=%d (%s) identifier=%d length=%d\n",
00331                msg->hdr->code, radius_code_string(msg->hdr->code),
00332                msg->hdr->identifier, ntohs(msg->hdr->length));
00333 
00334         for (i = 0; i < msg->attr_used; i++) {
00335                 struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i);
00336                 radius_msg_dump_attr(attr);
00337         }
00338 }
00339 
00340 
00341 int radius_msg_finish(struct radius_msg *msg, const u8 *secret,
00342                       size_t secret_len)
00343 {
00344         if (secret) {
00345                 u8 auth[MD5_MAC_LEN];
00346                 struct radius_attr_hdr *attr;
00347 
00348                 os_memset(auth, 0, MD5_MAC_LEN);
00349                 attr = radius_msg_add_attr(msg,
00350                                            RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
00351                                            auth, MD5_MAC_LEN);
00352                 if (attr == NULL) {
00353                         wpa_printf(MSG_WARNING, "RADIUS: Could not add "
00354                                    "Message-Authenticator");
00355                         return -1;
00356                 }
00357                 msg->hdr->length = htons(wpabuf_len(msg->buf));
00358                 hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
00359                          wpabuf_len(msg->buf), (u8 *) (attr + 1));
00360         } else
00361                 msg->hdr->length = htons(wpabuf_len(msg->buf));
00362 
00363         if (wpabuf_len(msg->buf) > 0xffff) {
00364                 wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)",
00365                            (unsigned long) wpabuf_len(msg->buf));
00366                 return -1;
00367         }
00368         return 0;
00369 }
00370 
00371 
00372 int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret,
00373                           size_t secret_len, const u8 *req_authenticator)
00374 {
00375         u8 auth[MD5_MAC_LEN];
00376         struct radius_attr_hdr *attr;
00377         const u8 *addr[4];
00378         size_t len[4];
00379 
00380         os_memset(auth, 0, MD5_MAC_LEN);
00381         attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
00382                                    auth, MD5_MAC_LEN);
00383         if (attr == NULL) {
00384                 printf("WARNING: Could not add Message-Authenticator\n");
00385                 return -1;
00386         }
00387         msg->hdr->length = htons(wpabuf_len(msg->buf));
00388         os_memcpy(msg->hdr->authenticator, req_authenticator,
00389                   sizeof(msg->hdr->authenticator));
00390         hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
00391                  wpabuf_len(msg->buf), (u8 *) (attr + 1));
00392 
00393         /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */
00394         addr[0] = (u8 *) msg->hdr;
00395         len[0] = 1 + 1 + 2;
00396         addr[1] = req_authenticator;
00397         len[1] = MD5_MAC_LEN;
00398         addr[2] = wpabuf_head_u8(msg->buf) + sizeof(struct radius_hdr);
00399         len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr);
00400         addr[3] = secret;
00401         len[3] = secret_len;
00402         md5_vector(4, addr, len, msg->hdr->authenticator);
00403 
00404         if (wpabuf_len(msg->buf) > 0xffff) {
00405                 wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)",
00406                            (unsigned long) wpabuf_len(msg->buf));
00407                 return -1;
00408         }
00409         return 0;
00410 }
00411 
00412 
00413 void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret,
00414                             size_t secret_len)
00415 {
00416         const u8 *addr[2];
00417         size_t len[2];
00418 
00419         msg->hdr->length = htons(wpabuf_len(msg->buf));
00420         os_memset(msg->hdr->authenticator, 0, MD5_MAC_LEN);
00421         addr[0] = wpabuf_head(msg->buf);
00422         len[0] = wpabuf_len(msg->buf);
00423         addr[1] = secret;
00424         len[1] = secret_len;
00425         md5_vector(2, addr, len, msg->hdr->authenticator);
00426 
00427         if (wpabuf_len(msg->buf) > 0xffff) {
00428                 wpa_printf(MSG_WARNING, "RADIUS: Too long messages (%lu)",
00429                            (unsigned long) wpabuf_len(msg->buf));
00430         }
00431 }
00432 
00433 
00434 static int radius_msg_add_attr_to_array(struct radius_msg *msg,
00435                                         struct radius_attr_hdr *attr)
00436 {
00437         if (msg->attr_used >= msg->attr_size) {
00438                 size_t *nattr_pos;
00439                 int nlen = msg->attr_size * 2;
00440 
00441                 nattr_pos = os_realloc(msg->attr_pos,
00442                                        nlen * sizeof(*msg->attr_pos));
00443                 if (nattr_pos == NULL)
00444                         return -1;
00445 
00446                 msg->attr_pos = nattr_pos;
00447                 msg->attr_size = nlen;
00448         }
00449 
00450         msg->attr_pos[msg->attr_used++] =
00451                 (unsigned char *) attr - wpabuf_head_u8(msg->buf);
00452 
00453         return 0;
00454 }
00455 
00456 
00457 struct radius_attr_hdr *radius_msg_add_attr(struct radius_msg *msg, u8 type,
00458                                             const u8 *data, size_t data_len)
00459 {
00460         size_t buf_needed;
00461         struct radius_attr_hdr *attr;
00462 
00463         if (data_len > RADIUS_MAX_ATTR_LEN) {
00464                 printf("radius_msg_add_attr: too long attribute (%lu bytes)\n",
00465                        (unsigned long) data_len);
00466                 return NULL;
00467         }
00468 
00469         buf_needed = sizeof(*attr) + data_len;
00470 
00471         if (wpabuf_tailroom(msg->buf) < buf_needed) {
00472                 /* allocate more space for message buffer */
00473                 if (wpabuf_resize(&msg->buf, buf_needed) < 0)
00474                         return NULL;
00475                 msg->hdr = wpabuf_mhead(msg->buf);
00476         }
00477 
00478         attr = wpabuf_put(msg->buf, sizeof(struct radius_attr_hdr));
00479         attr->type = type;
00480         attr->length = sizeof(*attr) + data_len;
00481         wpabuf_put_data(msg->buf, data, data_len);
00482 
00483         if (radius_msg_add_attr_to_array(msg, attr))
00484                 return NULL;
00485 
00486         return attr;
00487 }
00488 
00489 
00499 struct radius_msg * radius_msg_parse(const u8 *data, size_t len)
00500 {
00501         struct radius_msg *msg;
00502         struct radius_hdr *hdr;
00503         struct radius_attr_hdr *attr;
00504         size_t msg_len;
00505         unsigned char *pos, *end;
00506 
00507         if (data == NULL || len < sizeof(*hdr))
00508                 return NULL;
00509 
00510         hdr = (struct radius_hdr *) data;
00511 
00512         msg_len = ntohs(hdr->length);
00513         if (msg_len < sizeof(*hdr) || msg_len > len) {
00514                 wpa_printf(MSG_INFO, "RADIUS: Invalid message length");
00515                 return NULL;
00516         }
00517 
00518         if (msg_len < len) {
00519                 wpa_printf(MSG_DEBUG, "RADIUS: Ignored %lu extra bytes after "
00520                            "RADIUS message", (unsigned long) len - msg_len);
00521         }
00522 
00523         msg = os_zalloc(sizeof(*msg));
00524         if (msg == NULL)
00525                 return NULL;
00526 
00527         msg->buf = wpabuf_alloc_copy(data, msg_len);
00528         if (msg->buf == NULL || radius_msg_initialize(msg)) {
00529                 radius_msg_free(msg);
00530                 return NULL;
00531         }
00532         msg->hdr = wpabuf_mhead(msg->buf);
00533 
00534         /* parse attributes */
00535         pos = wpabuf_mhead_u8(msg->buf) + sizeof(struct radius_hdr);
00536         end = wpabuf_mhead_u8(msg->buf) + wpabuf_len(msg->buf);
00537         while (pos < end) {
00538                 if ((size_t) (end - pos) < sizeof(*attr))
00539                         goto fail;
00540 
00541                 attr = (struct radius_attr_hdr *) pos;
00542 
00543                 if (pos + attr->length > end || attr->length < sizeof(*attr))
00544                         goto fail;
00545 
00546                 /* TODO: check that attr->length is suitable for attr->type */
00547 
00548                 if (radius_msg_add_attr_to_array(msg, attr))
00549                         goto fail;
00550 
00551                 pos += attr->length;
00552         }
00553 
00554         return msg;
00555 
00556  fail:
00557         radius_msg_free(msg);
00558         return NULL;
00559 }
00560 
00561 
00562 int radius_msg_add_eap(struct radius_msg *msg, const u8 *data, size_t data_len)
00563 {
00564         const u8 *pos = data;
00565         size_t left = data_len;
00566 
00567         while (left > 0) {
00568                 int len;
00569                 if (left > RADIUS_MAX_ATTR_LEN)
00570                         len = RADIUS_MAX_ATTR_LEN;
00571                 else
00572                         len = left;
00573 
00574                 if (!radius_msg_add_attr(msg, RADIUS_ATTR_EAP_MESSAGE,
00575                                          pos, len))
00576                         return 0;
00577 
00578                 pos += len;
00579                 left -= len;
00580         }
00581 
00582         return 1;
00583 }
00584 
00585 
00586 u8 *radius_msg_get_eap(struct radius_msg *msg, size_t *eap_len)
00587 {
00588         u8 *eap, *pos;
00589         size_t len, i;
00590         struct radius_attr_hdr *attr;
00591 
00592         if (msg == NULL)
00593                 return NULL;
00594 
00595         len = 0;
00596         for (i = 0; i < msg->attr_used; i++) {
00597                 attr = radius_get_attr_hdr(msg, i);
00598                 if (attr->type == RADIUS_ATTR_EAP_MESSAGE)
00599                         len += attr->length - sizeof(struct radius_attr_hdr);
00600         }
00601 
00602         if (len == 0)
00603                 return NULL;
00604 
00605         eap = os_malloc(len);
00606         if (eap == NULL)
00607                 return NULL;
00608 
00609         pos = eap;
00610         for (i = 0; i < msg->attr_used; i++) {
00611                 attr = radius_get_attr_hdr(msg, i);
00612                 if (attr->type == RADIUS_ATTR_EAP_MESSAGE) {
00613                         int flen = attr->length - sizeof(*attr);
00614                         os_memcpy(pos, attr + 1, flen);
00615                         pos += flen;
00616                 }
00617         }
00618 
00619         if (eap_len)
00620                 *eap_len = len;
00621 
00622         return eap;
00623 }
00624 
00625 
00626 int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret,
00627                                size_t secret_len, const u8 *req_auth)
00628 {
00629         u8 auth[MD5_MAC_LEN], orig[MD5_MAC_LEN];
00630         u8 orig_authenticator[16];
00631         struct radius_attr_hdr *attr = NULL, *tmp;
00632         size_t i;
00633 
00634         for (i = 0; i < msg->attr_used; i++) {
00635                 tmp = radius_get_attr_hdr(msg, i);
00636                 if (tmp->type == RADIUS_ATTR_MESSAGE_AUTHENTICATOR) {
00637                         if (attr != NULL) {
00638                                 printf("Multiple Message-Authenticator "
00639                                        "attributes in RADIUS message\n");
00640                                 return 1;
00641                         }
00642                         attr = tmp;
00643                 }
00644         }
00645 
00646         if (attr == NULL) {
00647                 printf("No Message-Authenticator attribute found\n");
00648                 return 1;
00649         }
00650 
00651         os_memcpy(orig, attr + 1, MD5_MAC_LEN);
00652         os_memset(attr + 1, 0, MD5_MAC_LEN);
00653         if (req_auth) {
00654                 os_memcpy(orig_authenticator, msg->hdr->authenticator,
00655                           sizeof(orig_authenticator));
00656                 os_memcpy(msg->hdr->authenticator, req_auth,
00657                           sizeof(msg->hdr->authenticator));
00658         }
00659         hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
00660                  wpabuf_len(msg->buf), auth);
00661         os_memcpy(attr + 1, orig, MD5_MAC_LEN);
00662         if (req_auth) {
00663                 os_memcpy(msg->hdr->authenticator, orig_authenticator,
00664                           sizeof(orig_authenticator));
00665         }
00666 
00667         if (os_memcmp(orig, auth, MD5_MAC_LEN) != 0) {
00668                 printf("Invalid Message-Authenticator!\n");
00669                 return 1;
00670         }
00671 
00672         return 0;
00673 }
00674 
00675 
00676 int radius_msg_verify(struct radius_msg *msg, const u8 *secret,
00677                       size_t secret_len, struct radius_msg *sent_msg, int auth)
00678 {
00679         const u8 *addr[4];
00680         size_t len[4];
00681         u8 hash[MD5_MAC_LEN];
00682 
00683         if (sent_msg == NULL) {
00684                 printf("No matching Access-Request message found\n");
00685                 return 1;
00686         }
00687 
00688         if (auth &&
00689             radius_msg_verify_msg_auth(msg, secret, secret_len,
00690                                        sent_msg->hdr->authenticator)) {
00691                 return 1;
00692         }
00693 
00694         /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */
00695         addr[0] = (u8 *) msg->hdr;
00696         len[0] = 1 + 1 + 2;
00697         addr[1] = sent_msg->hdr->authenticator;
00698         len[1] = MD5_MAC_LEN;
00699         addr[2] = wpabuf_head_u8(msg->buf) + sizeof(struct radius_hdr);
00700         len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr);
00701         addr[3] = secret;
00702         len[3] = secret_len;
00703         md5_vector(4, addr, len, hash);
00704         if (os_memcmp(hash, msg->hdr->authenticator, MD5_MAC_LEN) != 0) {
00705                 printf("Response Authenticator invalid!\n");
00706                 return 1;
00707         }
00708 
00709         return 0;
00710 }
00711 
00712 
00713 int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src,
00714                          u8 type)
00715 {
00716         struct radius_attr_hdr *attr;
00717         size_t i;
00718         int count = 0;
00719 
00720         for (i = 0; i < src->attr_used; i++) {
00721                 attr = radius_get_attr_hdr(src, i);
00722                 if (attr->type == type) {
00723                         if (!radius_msg_add_attr(dst, type, (u8 *) (attr + 1),
00724                                                  attr->length - sizeof(*attr)))
00725                                 return -1;
00726                         count++;
00727                 }
00728         }
00729 
00730         return count;
00731 }
00732 
00733 
00734 /* Create Request Authenticator. The value should be unique over the lifetime
00735  * of the shared secret between authenticator and authentication server.
00736  * Use one-way MD5 hash calculated from current timestamp and some data given
00737  * by the caller. */
00738 void radius_msg_make_authenticator(struct radius_msg *msg,
00739                                    const u8 *data, size_t len)
00740 {
00741         struct os_time tv;
00742         long int l;
00743         const u8 *addr[3];
00744         size_t elen[3];
00745 
00746         os_get_time(&tv);
00747         l = os_random();
00748         addr[0] = (u8 *) &tv;
00749         elen[0] = sizeof(tv);
00750         addr[1] = data;
00751         elen[1] = len;
00752         addr[2] = (u8 *) &l;
00753         elen[2] = sizeof(l);
00754         md5_vector(3, addr, elen, msg->hdr->authenticator);
00755 }
00756 
00757 
00758 /* Get Vendor-specific RADIUS Attribute from a parsed RADIUS message.
00759  * Returns the Attribute payload and sets alen to indicate the length of the
00760  * payload if a vendor attribute with subtype is found, otherwise returns NULL.
00761  * The returned payload is allocated with os_malloc() and caller must free it
00762  * by calling os_free().
00763  */
00764 static u8 *radius_msg_get_vendor_attr(struct radius_msg *msg, u32 vendor,
00765                                       u8 subtype, size_t *alen)
00766 {
00767         u8 *data, *pos;
00768         size_t i, len;
00769 
00770         if (msg == NULL)
00771                 return NULL;
00772 
00773         for (i = 0; i < msg->attr_used; i++) {
00774                 struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i);
00775                 size_t left;
00776                 u32 vendor_id;
00777                 struct radius_attr_vendor *vhdr;
00778 
00779                 if (attr->type != RADIUS_ATTR_VENDOR_SPECIFIC)
00780                         continue;
00781 
00782                 left = attr->length - sizeof(*attr);
00783                 if (left < 4)
00784                         continue;
00785 
00786                 pos = (u8 *) (attr + 1);
00787 
00788                 os_memcpy(&vendor_id, pos, 4);
00789                 pos += 4;
00790                 left -= 4;
00791 
00792                 if (ntohl(vendor_id) != vendor)
00793                         continue;
00794 
00795                 while (left >= sizeof(*vhdr)) {
00796                         vhdr = (struct radius_attr_vendor *) pos;
00797                         if (vhdr->vendor_length > left ||
00798                             vhdr->vendor_length < sizeof(*vhdr)) {
00799                                 left = 0;
00800                                 break;
00801                         }
00802                         if (vhdr->vendor_type != subtype) {
00803                                 pos += vhdr->vendor_length;
00804                                 left -= vhdr->vendor_length;
00805                                 continue;
00806                         }
00807 
00808                         len = vhdr->vendor_length - sizeof(*vhdr);
00809                         data = os_malloc(len);
00810                         if (data == NULL)
00811                                 return NULL;
00812                         os_memcpy(data, pos + sizeof(*vhdr), len);
00813                         if (alen)
00814                                 *alen = len;
00815                         return data;
00816                 }
00817         }
00818 
00819         return NULL;
00820 }
00821 
00822 
00823 static u8 * decrypt_ms_key(const u8 *key, size_t len,
00824                            const u8 *req_authenticator,
00825                            const u8 *secret, size_t secret_len, size_t *reslen)
00826 {
00827         u8 *plain, *ppos, *res;
00828         const u8 *pos;
00829         size_t left, plen;
00830         u8 hash[MD5_MAC_LEN];
00831         int i, first = 1;
00832         const u8 *addr[3];
00833         size_t elen[3];
00834 
00835         /* key: 16-bit salt followed by encrypted key info */
00836 
00837         if (len < 2 + 16)
00838                 return NULL;
00839 
00840         pos = key + 2;
00841         left = len - 2;
00842         if (left % 16) {
00843                 printf("Invalid ms key len %lu\n", (unsigned long) left);
00844                 return NULL;
00845         }
00846 
00847         plen = left;
00848         ppos = plain = os_malloc(plen);
00849         if (plain == NULL)
00850                 return NULL;
00851         plain[0] = 0;
00852 
00853         while (left > 0) {
00854                 /* b(1) = MD5(Secret + Request-Authenticator + Salt)
00855                  * b(i) = MD5(Secret + c(i - 1)) for i > 1 */
00856 
00857                 addr[0] = secret;
00858                 elen[0] = secret_len;
00859                 if (first) {
00860                         addr[1] = req_authenticator;
00861                         elen[1] = MD5_MAC_LEN;
00862                         addr[2] = key;
00863                         elen[2] = 2; /* Salt */
00864                 } else {
00865                         addr[1] = pos - MD5_MAC_LEN;
00866                         elen[1] = MD5_MAC_LEN;
00867                 }
00868                 md5_vector(first ? 3 : 2, addr, elen, hash);
00869                 first = 0;
00870 
00871                 for (i = 0; i < MD5_MAC_LEN; i++)
00872                         *ppos++ = *pos++ ^ hash[i];
00873                 left -= MD5_MAC_LEN;
00874         }
00875 
00876         if (plain[0] == 0 || plain[0] > plen - 1) {
00877                 printf("Failed to decrypt MPPE key\n");
00878                 os_free(plain);
00879                 return NULL;
00880         }
00881 
00882         res = os_malloc(plain[0]);
00883         if (res == NULL) {
00884                 os_free(plain);
00885                 return NULL;
00886         }
00887         os_memcpy(res, plain + 1, plain[0]);
00888         if (reslen)
00889                 *reslen = plain[0];
00890         os_free(plain);
00891         return res;
00892 }
00893 
00894 
00895 static void encrypt_ms_key(const u8 *key, size_t key_len, u16 salt,
00896                            const u8 *req_authenticator,
00897                            const u8 *secret, size_t secret_len,
00898                            u8 *ebuf, size_t *elen)
00899 {
00900         int i, len, first = 1;
00901         u8 hash[MD5_MAC_LEN], saltbuf[2], *pos;
00902         const u8 *addr[3];
00903         size_t _len[3];
00904 
00905         WPA_PUT_BE16(saltbuf, salt);
00906 
00907         len = 1 + key_len;
00908         if (len & 0x0f) {
00909                 len = (len & 0xf0) + 16;
00910         }
00911         os_memset(ebuf, 0, len);
00912         ebuf[0] = key_len;
00913         os_memcpy(ebuf + 1, key, key_len);
00914 
00915         *elen = len;
00916 
00917         pos = ebuf;
00918         while (len > 0) {
00919                 /* b(1) = MD5(Secret + Request-Authenticator + Salt)
00920                  * b(i) = MD5(Secret + c(i - 1)) for i > 1 */
00921                 addr[0] = secret;
00922                 _len[0] = secret_len;
00923                 if (first) {
00924                         addr[1] = req_authenticator;
00925                         _len[1] = MD5_MAC_LEN;
00926                         addr[2] = saltbuf;
00927                         _len[2] = sizeof(saltbuf);
00928                 } else {
00929                         addr[1] = pos - MD5_MAC_LEN;
00930                         _len[1] = MD5_MAC_LEN;
00931                 }
00932                 md5_vector(first ? 3 : 2, addr, _len, hash);
00933                 first = 0;
00934 
00935                 for (i = 0; i < MD5_MAC_LEN; i++)
00936                         *pos++ ^= hash[i];
00937 
00938                 len -= MD5_MAC_LEN;
00939         }
00940 }
00941 
00942 
00943 struct radius_ms_mppe_keys *
00944 radius_msg_get_ms_keys(struct radius_msg *msg, struct radius_msg *sent_msg,
00945                        const u8 *secret, size_t secret_len)
00946 {
00947         u8 *key;
00948         size_t keylen;
00949         struct radius_ms_mppe_keys *keys;
00950 
00951         if (msg == NULL || sent_msg == NULL)
00952                 return NULL;
00953 
00954         keys = os_zalloc(sizeof(*keys));
00955         if (keys == NULL)
00956                 return NULL;
00957 
00958         key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_MICROSOFT,
00959                                          RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY,
00960                                          &keylen);
00961         if (key) {
00962                 keys->send = decrypt_ms_key(key, keylen,
00963                                             sent_msg->hdr->authenticator,
00964                                             secret, secret_len,
00965                                             &keys->send_len);
00966                 os_free(key);
00967         }
00968 
00969         key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_MICROSOFT,
00970                                          RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY,
00971                                          &keylen);
00972         if (key) {
00973                 keys->recv = decrypt_ms_key(key, keylen,
00974                                             sent_msg->hdr->authenticator,
00975                                             secret, secret_len,
00976                                             &keys->recv_len);
00977                 os_free(key);
00978         }
00979 
00980         return keys;
00981 }
00982 
00983 
00984 struct radius_ms_mppe_keys *
00985 radius_msg_get_cisco_keys(struct radius_msg *msg, struct radius_msg *sent_msg,
00986                           const u8 *secret, size_t secret_len)
00987 {
00988         u8 *key;
00989         size_t keylen;
00990         struct radius_ms_mppe_keys *keys;
00991 
00992         if (msg == NULL || sent_msg == NULL)
00993                 return NULL;
00994 
00995         keys = os_zalloc(sizeof(*keys));
00996         if (keys == NULL)
00997                 return NULL;
00998 
00999         key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_CISCO,
01000                                          RADIUS_CISCO_AV_PAIR, &keylen);
01001         if (key && keylen == 51 &&
01002             os_memcmp(key, "leap:session-key=", 17) == 0) {
01003                 keys->recv = decrypt_ms_key(key + 17, keylen - 17,
01004                                             sent_msg->hdr->authenticator,
01005                                             secret, secret_len,
01006                                             &keys->recv_len);
01007         }
01008         os_free(key);
01009 
01010         return keys;
01011 }
01012 
01013 
01014 int radius_msg_add_mppe_keys(struct radius_msg *msg,
01015                              const u8 *req_authenticator,
01016                              const u8 *secret, size_t secret_len,
01017                              const u8 *send_key, size_t send_key_len,
01018                              const u8 *recv_key, size_t recv_key_len)
01019 {
01020         struct radius_attr_hdr *attr;
01021         u32 vendor_id = htonl(RADIUS_VENDOR_ID_MICROSOFT);
01022         u8 *buf;
01023         struct radius_attr_vendor *vhdr;
01024         u8 *pos;
01025         size_t elen;
01026         int hlen;
01027         u16 salt;
01028 
01029         hlen = sizeof(vendor_id) + sizeof(*vhdr) + 2;
01030 
01031         /* MS-MPPE-Send-Key */
01032         buf = os_malloc(hlen + send_key_len + 16);
01033         if (buf == NULL) {
01034                 return 0;
01035         }
01036         pos = buf;
01037         os_memcpy(pos, &vendor_id, sizeof(vendor_id));
01038         pos += sizeof(vendor_id);
01039         vhdr = (struct radius_attr_vendor *) pos;
01040         vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY;
01041         pos = (u8 *) (vhdr + 1);
01042         salt = os_random() | 0x8000;
01043         WPA_PUT_BE16(pos, salt);
01044         pos += 2;
01045         encrypt_ms_key(send_key, send_key_len, salt, req_authenticator, secret,
01046                        secret_len, pos, &elen);
01047         vhdr->vendor_length = hlen + elen - sizeof(vendor_id);
01048 
01049         attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
01050                                    buf, hlen + elen);
01051         os_free(buf);
01052         if (attr == NULL) {
01053                 return 0;
01054         }
01055 
01056         /* MS-MPPE-Recv-Key */
01057         buf = os_malloc(hlen + send_key_len + 16);
01058         if (buf == NULL) {
01059                 return 0;
01060         }
01061         pos = buf;
01062         os_memcpy(pos, &vendor_id, sizeof(vendor_id));
01063         pos += sizeof(vendor_id);
01064         vhdr = (struct radius_attr_vendor *) pos;
01065         vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY;
01066         pos = (u8 *) (vhdr + 1);
01067         salt ^= 1;
01068         WPA_PUT_BE16(pos, salt);
01069         pos += 2;
01070         encrypt_ms_key(recv_key, recv_key_len, salt, req_authenticator, secret,
01071                        secret_len, pos, &elen);
01072         vhdr->vendor_length = hlen + elen - sizeof(vendor_id);
01073 
01074         attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
01075                                    buf, hlen + elen);
01076         os_free(buf);
01077         if (attr == NULL) {
01078                 return 0;
01079         }
01080 
01081         return 1;
01082 }
01083 
01084 
01085 /* Add User-Password attribute to a RADIUS message and encrypt it as specified
01086  * in RFC 2865, Chap. 5.2 */
01087 struct radius_attr_hdr *
01088 radius_msg_add_attr_user_password(struct radius_msg *msg,
01089                                   const u8 *data, size_t data_len,
01090                                   const u8 *secret, size_t secret_len)
01091 {
01092         u8 buf[128];
01093         int padlen, i;
01094         size_t buf_len, pos;
01095         const u8 *addr[2];
01096         size_t len[2];
01097         u8 hash[16];
01098 
01099         if (data_len > 128)
01100                 return NULL;
01101 
01102         os_memcpy(buf, data, data_len);
01103         buf_len = data_len;
01104 
01105         padlen = data_len % 16;
01106         if (padlen) {
01107                 padlen = 16 - padlen;
01108                 os_memset(buf + data_len, 0, padlen);
01109                 buf_len += padlen;
01110         }
01111 
01112         addr[0] = secret;
01113         len[0] = secret_len;
01114         addr[1] = msg->hdr->authenticator;
01115         len[1] = 16;
01116         md5_vector(2, addr, len, hash);
01117 
01118         for (i = 0; i < 16; i++)
01119                 buf[i] ^= hash[i];
01120         pos = 16;
01121 
01122         while (pos < buf_len) {
01123                 addr[0] = secret;
01124                 len[0] = secret_len;
01125                 addr[1] = &buf[pos - 16];
01126                 len[1] = 16;
01127                 md5_vector(2, addr, len, hash);
01128 
01129                 for (i = 0; i < 16; i++)
01130                         buf[pos + i] ^= hash[i];
01131 
01132                 pos += 16;
01133         }
01134 
01135         return radius_msg_add_attr(msg, RADIUS_ATTR_USER_PASSWORD,
01136                                    buf, buf_len);
01137 }
01138 
01139 
01140 int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len)
01141 {
01142         struct radius_attr_hdr *attr = NULL, *tmp;
01143         size_t i, dlen;
01144 
01145         for (i = 0; i < msg->attr_used; i++) {
01146                 tmp = radius_get_attr_hdr(msg, i);
01147                 if (tmp->type == type) {
01148                         attr = tmp;
01149                         break;
01150                 }
01151         }
01152 
01153         if (!attr)
01154                 return -1;
01155 
01156         dlen = attr->length - sizeof(*attr);
01157         if (buf)
01158                 os_memcpy(buf, (attr + 1), dlen > len ? len : dlen);
01159         return dlen;
01160 }
01161 
01162 
01163 int radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf,
01164                             size_t *len, const u8 *start)
01165 {
01166         size_t i;
01167         struct radius_attr_hdr *attr = NULL, *tmp;
01168 
01169         for (i = 0; i < msg->attr_used; i++) {
01170                 tmp = radius_get_attr_hdr(msg, i);
01171                 if (tmp->type == type &&
01172                     (start == NULL || (u8 *) tmp > start)) {
01173                         attr = tmp;
01174                         break;
01175                 }
01176         }
01177 
01178         if (!attr)
01179                 return -1;
01180 
01181         *buf = (u8 *) (attr + 1);
01182         *len = attr->length - sizeof(*attr);
01183         return 0;
01184 }
01185 
01186 
01187 int radius_msg_count_attr(struct radius_msg *msg, u8 type, int min_len)
01188 {
01189         size_t i;
01190         int count;
01191 
01192         for (count = 0, i = 0; i < msg->attr_used; i++) {
01193                 struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i);
01194                 if (attr->type == type &&
01195                     attr->length >= sizeof(struct radius_attr_hdr) + min_len)
01196                         count++;
01197         }
01198 
01199         return count;
01200 }
01201 
01202 
01203 struct radius_tunnel_attrs {
01204         int tag_used;
01205         int type; /* Tunnel-Type */
01206         int medium_type; /* Tunnel-Medium-Type */
01207         int vlanid;
01208 };
01209 
01210 
01216 int radius_msg_get_vlanid(struct radius_msg *msg)
01217 {
01218         struct radius_tunnel_attrs tunnel[RADIUS_TUNNEL_TAGS], *tun;
01219         size_t i;
01220         struct radius_attr_hdr *attr = NULL;
01221         const u8 *data;
01222         char buf[10];
01223         size_t dlen;
01224 
01225         os_memset(&tunnel, 0, sizeof(tunnel));
01226 
01227         for (i = 0; i < msg->attr_used; i++) {
01228                 attr = radius_get_attr_hdr(msg, i);
01229                 data = (const u8 *) (attr + 1);
01230                 dlen = attr->length - sizeof(*attr);
01231                 if (attr->length < 3)
01232                         continue;
01233                 if (data[0] >= RADIUS_TUNNEL_TAGS)
01234                         tun = &tunnel[0];
01235                 else
01236                         tun = &tunnel[data[0]];
01237 
01238                 switch (attr->type) {
01239                 case RADIUS_ATTR_TUNNEL_TYPE:
01240                         if (attr->length != 6)
01241                                 break;
01242                         tun->tag_used++;
01243                         tun->type = WPA_GET_BE24(data + 1);
01244                         break;
01245                 case RADIUS_ATTR_TUNNEL_MEDIUM_TYPE:
01246                         if (attr->length != 6)
01247                                 break;
01248                         tun->tag_used++;
01249                         tun->medium_type = WPA_GET_BE24(data + 1);
01250                         break;
01251                 case RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID:
01252                         if (data[0] < RADIUS_TUNNEL_TAGS) {
01253                                 data++;
01254                                 dlen--;
01255                         }
01256                         if (dlen >= sizeof(buf))
01257                                 break;
01258                         os_memcpy(buf, data, dlen);
01259                         buf[dlen] = '\0';
01260                         tun->tag_used++;
01261                         tun->vlanid = atoi(buf);
01262                         break;
01263                 }
01264         }
01265 
01266         for (i = 0; i < RADIUS_TUNNEL_TAGS; i++) {
01267                 tun = &tunnel[i];
01268                 if (tun->tag_used &&
01269                     tun->type == RADIUS_TUNNEL_TYPE_VLAN &&
01270                     tun->medium_type == RADIUS_TUNNEL_MEDIUM_TYPE_802 &&
01271                     tun->vlanid > 0)
01272                         return tun->vlanid;
01273         }
01274 
01275         return -1;
01276 }
01277 
01278 
01279 void radius_free_class(struct radius_class_data *c)
01280 {
01281         size_t i;
01282         if (c == NULL)
01283                 return;
01284         for (i = 0; i < c->count; i++)
01285                 os_free(c->attr[i].data);
01286         os_free(c->attr);
01287         c->attr = NULL;
01288         c->count = 0;
01289 }
01290 
01291 
01292 int radius_copy_class(struct radius_class_data *dst,
01293                       const struct radius_class_data *src)
01294 {
01295         size_t i;
01296 
01297         if (src->attr == NULL)
01298                 return 0;
01299 
01300         dst->attr = os_zalloc(src->count * sizeof(struct radius_attr_data));
01301         if (dst->attr == NULL)
01302                 return -1;
01303 
01304         dst->count = 0;
01305 
01306         for (i = 0; i < src->count; i++) {
01307                 dst->attr[i].data = os_malloc(src->attr[i].len);
01308                 if (dst->attr[i].data == NULL)
01309                         break;
01310                 dst->count++;
01311                 os_memcpy(dst->attr[i].data, src->attr[i].data,
01312                           src->attr[i].len);
01313                 dst->attr[i].len = src->attr[i].len;
01314         }
01315 
01316         return 0;
01317 }


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