radius.c

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

Generated on Sat Nov 21 23:16:52 2009 for hostapd by  doxygen 1.6.1