static void toss_sta(nas_t *nas, nas_sta_t *sta, int reason, int driver_signal) { nas_sta_t *sta_list; uint hash; #ifdef BCMDBG char eabuf[ETHER_ADDR_STR_LEN]; #endif if (sta == NULL) { dbg(nas, "called with NULL STA ponter"); return; } /* If this is because of a driver message, telling the driver again * is unnecessary (and could loop). */ if (!driver_signal) { dbg(nas, "deauthenticating %s", ether_etoa((uchar *)&sta->ea, eabuf)); nas_deauthenticate(nas, &sta->ea, reason); } /* Be careful not to leak timer descriptors. */ wpa_stop_retx(sta); dbg(nas, "cleanup STA %s", ether_etoa((uchar *)&sta->ea, eabuf)); /* Remove this one from its hashed list. */ hash = pae_hash(&sta->ea); sta_list = nas->sta_hashed[hash]; if (sta_list == sta) { /* It was the head, so its next is the new head. */ nas->sta_hashed[hash] = sta->next; } else { /* Find the one that points to it and change the pointer. */ while ((sta_list != NULL) && (sta_list->next != sta)) sta_list = sta_list->next; if (sta_list == NULL) { dbg(nas, "sta %s not in hash list", ether_etoa((uchar *)&sta->ea, eabuf)); } else { sta_list->next = sta->next; } } sta->used = FALSE; return; }
/* Proxy EAP packet from PAE to RADIUS server */ void radius_forward(nas_t *nas, nas_sta_t *sta, eap_header_t *eap) { radius_header_t *request; unsigned char buf[16], *ptr; long val; int left; /* Allocate packet */ if (!(request = malloc(RADIUS_MAX_LEN))) { perror("malloc"); return; } /* Fill header */ request->code = RADIUS_ACCESS_REQUEST; request->id = sta - nas->sta; request->length = htons(RADIUS_HEADER_LEN); /* Fill Request Authenticator */ nas_rand128(request->vector); /* Fill attributes */ /* User Name */ if (sta->pae.radius.username.data && sta->pae.radius.username.length) radius_add(request, RD_TP_USER_NAME, sta->pae.radius.username.data, sta->pae.radius.username.length); /* NAS IP Address */ radius_add(request, RD_TP_NAS_IP_ADDRESS, (unsigned char *) &nas->client.sin_addr, sizeof(nas->client.sin_addr)); /* Called Station Id */ snprintf((char *)buf, sizeof(buf), "%02x%02x%02x%02x%02x%02x", ((unsigned char *) &nas->ea)[0], ((unsigned char *) &nas->ea)[1], ((unsigned char *) &nas->ea)[2], ((unsigned char *) &nas->ea)[3], ((unsigned char *) &nas->ea)[4], ((unsigned char *) &nas->ea)[5]); radius_add(request, RD_TP_CALLED_STATION_ID, buf, strlen((char *)buf)); /* Calling Station Id */ snprintf((char *)buf, sizeof(buf), "%02x%02x%02x%02x%02x%02x", ((unsigned char *) &sta->ea)[0], ((unsigned char *) &sta->ea)[1], ((unsigned char *) &sta->ea)[2], ((unsigned char *) &sta->ea)[3], ((unsigned char *) &sta->ea)[4], ((unsigned char *) &sta->ea)[5]); radius_add(request, RD_TP_CALLING_STATION_ID, buf, strlen((char *)buf)); /* NAS identifier */ if (strlen(nas->nas_id)) radius_add(request, RD_TP_NAS_IDENTIFIER, (unsigned char *)nas->nas_id, strlen(nas->nas_id)); else { snprintf((char *)buf, sizeof(buf), "%02x%02x%02x%02x%02x%02x", ((unsigned char *) &nas->ea)[0], ((unsigned char *) &nas->ea)[1], ((unsigned char *) &nas->ea)[2], ((unsigned char *) &nas->ea)[3], ((unsigned char *) &nas->ea)[4], ((unsigned char *) &nas->ea)[5]); radius_add(request, RD_TP_NAS_IDENTIFIER, buf, strlen((char *)buf)); } /* NAS Port */ val = htonl((long) pae_hash(&sta->ea)); radius_add(request, RD_TP_NAS_PORT, (unsigned char *) &val, sizeof(val)); val = htonl(1400); radius_add(request, RD_TP_FRAMED_MTU, (unsigned char *) &val, sizeof(val)); /* State */ if (sta->pae.radius.state.data && sta->pae.radius.state.length) { radius_add(request, RD_TP_STATE, sta->pae.radius.state.data, sta->pae.radius.state.length); free(sta->pae.radius.state.data); sta->pae.radius.state.data = NULL; sta->pae.radius.state.length = 0; } /* NAS Port Type */ val = htonl((long) nas->type); radius_add(request, RD_TP_NAS_PORT_TYPE, (unsigned char *) &val, 4); /* EAP Message(s) */ if (eap) { for (left = ntohs(eap->length); left > 0; left -= 253) { radius_add(request, RD_TP_EAP_MESSAGE, (unsigned char *) eap + ntohs(eap->length) - left, left <= 253 ? left : 253); } } /* Message Authenticator */ memset(buf, 0, 16); radius_add(request, RD_TP_MESSAGE_AUTHENTICATOR, buf, 16); ptr = (unsigned char *) request + ntohs(request->length) - 16; /* Calculate HMAC-MD5 checksum with null signature */ hmac_md5((unsigned char *) request, ntohs(request->length), nas->secret.data, nas->secret.length, ptr); /* Send packet */ if (NAS_RADIUS_SEND_PACKET(nas, request, ntohs(request->length)) < 0) { perror(inet_ntoa(nas->server.sin_addr)); free(request); request = NULL; } /* Save original request packet */ if (sta->pae.radius.request) free(sta->pae.radius.request); sta->pae.radius.request = request; }
/* * Search for or create a STA struct. * If `enter' is not set, do not create it when one is not found. */ nas_sta_t * lookup_sta(nas_t *nas, struct ether_addr *ea, sta_lookup_mode_t mode) { unsigned int hash; nas_sta_t *sta; time_t now, oldest; hash = pae_hash(ea); /* Search for entry in the hash table */ for (sta = nas->sta_hashed[hash]; sta && memcmp(&sta->ea, ea, ETHER_ADDR_LEN); sta = sta->next); /* One second resolution is probably good enough. */ (void) time(&now); /* Allocate a new entry */ if (!sta) { int i, old_idx = -1; /* Don't make an unwanted entry. */ if (mode == SEARCH_ONLY) return NULL; oldest = now; for (i = 0; i < MAX_SUPPLICANTS; i++) { if (!nas->sta[i].used) break; else if (nas->sta[i].last_use < oldest) { oldest = nas->sta[i].last_use; old_idx = i; } } if (i < MAX_SUPPLICANTS) { sta = &nas->sta[i]; } else if (old_idx == -1) { /* Full up with all the same timestamp?! Can * this really happen? */ return NULL; } else { /* Didn't find one unused, so age out LRU entry. */ sta = &nas->sta[old_idx]; toss_sta(nas, sta, DOT11_RC_BUSY, 0); } if ((sta != NULL) && (sta->pae.radius.request != NULL)) { free(sta->pae.radius.request); } /* Initialize entry */ memset(sta, 0, (sizeof(nas_sta_t))); memcpy(&sta->ea, ea, ETHER_ADDR_LEN); sta->used = TRUE; sta->nas = nas; /* initialize EAPOL version */ sta->eapol_version = WPA_EAPOL_VERSION; /* Initial STA state: * Cheaper and harmless not to distiguish NAS mode. */ if (nas->flags & NAS_FLAG_AUTHENTICATOR) sta->suppl.state = sta->suppl.retry_state = WPA_AUTHENTICATION2; #ifdef BCMSUPPL if (nas->flags & NAS_FLAG_SUPPLICANT) sta->suppl.state = sta->suppl.retry_state = WPA_SUP_AUTHENTICATION; sta->suppl.pk_state = EAPOL_SUP_PK_UNKNOWN; #endif sta->pae.state = INITIALIZE; /* initial mode/wsec/algom. assoc proc will override them */ if ((nas->mode & RADIUS) && (nas->wsec & WEP_ENABLED)) { sta->mode = RADIUS; sta->wsec = WEP_ENABLED; sta->algo = CRYPTO_ALGO_WEP128; } else { sta->mode = 0; sta->wsec = 0; sta->algo = 0; } dbg(nas, "mode %d wsec %d algo %d", sta->mode, sta->wsec, sta->algo); /* Add entry to the cache */ sta->next = nas->sta_hashed[hash]; nas->sta_hashed[hash] = sta; sta->flags = 0; } sta->last_use = now; return sta; }