static void ap_eap_sm_sendEap(char *eap_data, unsigned short eap_len) { WpsEapHdr *fwpsEapHdr = (WpsEapHdr *)eap_data; uint8 *fwpsEapHdrLF = (uint8 *)(fwpsEapHdr + 1); unsigned short wps_len = eap_len - sizeof(WpsEapHdr); /* No fragmentation need, shift EAP_WPS_LF_OFFSET */ if (wps_len <= apEapState->eap_frag_threshold) { ap_eap_sm_sendEapol(&apEapState->msg_to_send[EAP_WPS_LF_OFFSET], eap_data, eap_len); return; } /* Modify FIRST fragmentation */ fwpsEapHdr->flags = (EAP_WPS_FLAGS_MF | EAP_WPS_FLAGS_LF); fwpsEapHdr->length = WpsHtons(sizeof(WpsEapHdr) + apEapState->eap_frag_threshold); /* Set WPS data length field */ WpsHtonsPtr((uint8*)&wps_len, (uint8*)fwpsEapHdrLF); /* Set next_frag_to_send to first WpsEapHdr to indecate first fragmentation has sent */ apEapState->next_frag_to_send = &apEapState->msg_to_send[EAPOL_HEADER_LEN]; /* frag_szie only save wps_len */ apEapState->frag_size = wps_len; ap_eap_sm_sendEapol(apEapState->msg_to_send, (char *)fwpsEapHdr, WpsNtohs((uint8 *)&fwpsEapHdr->length)); }
/* Send one fragmented packet. */ static void ap_eap_sm_sendNextFrag(WpsEapHdr *wpsEapHdr) { eapol_header_t *feapolHdr = NULL; WpsEapHdr *fwpsEapHdr = NULL; uint8 flags = 0; int max_to_send = apEapState->eap_frag_threshold; /* Locate EAPOL and EAP header */ feapolHdr = (eapol_header_t *)(apEapState->next_frag_to_send - WPS_DATA_OFFSET); fwpsEapHdr = (WpsEapHdr *)feapolHdr->body; /* How maximum length to send */ if (apEapState->frag_size < apEapState->eap_frag_threshold) max_to_send = apEapState->frag_size; /* More fragmentations check */ if (apEapState->frag_size > max_to_send) flags = EAP_WPS_FLAGS_MF; /* Create one fragmentation */ *fwpsEapHdr = *wpsEapHdr; fwpsEapHdr->id = apEapState->eap_id; fwpsEapHdr->flags = flags; fwpsEapHdr->length = WpsHtons(sizeof(WpsEapHdr) + max_to_send); ap_eap_sm_sendEapol((char *)feapolHdr, (char *)fwpsEapHdr, WpsNtohs((uint8 *)&fwpsEapHdr->length)); }
uint32 ap_eap_sm_sendMsg(char *dataBuffer, uint32 dataLen) { uint32 retVal; eapol_header_t *eapolHdr = (eapol_header_t *)apEapState->msg_to_send; WpsEapHdr *wpsEapHdr; TUTRACE((TUTRACE_INFO, "In ap_eap_sm_sendMsg buffer Length = %d\n", dataLen)); retVal = ap_eap_sm_create_pkt(dataBuffer, dataLen, EAP_CODE_REQUEST); if (retVal == WPS_SUCCESS) { /* Shift EAP_WPS_LF_OFFSET */ if (dataLen < apEapState->eap_frag_threshold) eapolHdr = (eapol_header_t *)&apEapState->msg_to_send[EAP_WPS_LF_OFFSET]; wpsEapHdr = (WpsEapHdr *)eapolHdr->body; ap_eap_sm_sendEap((char *)wpsEapHdr, WpsNtohs((uint8*)&wpsEapHdr->length)); apEapState->state = PROCESSING_PROTOCOL; } else { TUTRACE((TUTRACE_ERR, "Send EAP FAILURE to station!\n")); ap_eap_sm_Failure(apEap_wps_version2 ? 1 : 0); retVal = TREAP_ERR_SENDRECV; } return retVal; }
/* * Receive a fragmentation then send EAP_FRAG_ACK. */ static int ap_eap_sm_process_frag(char *eap_msg) { int ret; int LF_bytes = 0; WpsEapHdr *wpsEapHdr = (WpsEapHdr *)eap_msg; uint32 wps_len = WpsNtohs((uint8*)&wpsEapHdr->length) - sizeof(WpsEapHdr); int wps_data_received = apEapState->total_received - sizeof(WpsEapHdr); TUTRACE((TUTRACE_INFO, "Receive a EAP WPS fragment packet\n")); /* Total length checking */ if (apEapState->total_bytes_to_recv < wps_data_received + wps_len) { TUTRACE((TUTRACE_ERR, "Received WPS data len %d excess total bytes to " "receive %d\n", wps_data_received + wps_len, apEapState->total_bytes_to_recv)); return EAP_FAILURE; } /* Copy to frags_received */ /* First fragmentation include WpsEapHdr without length field */ if (apEapState->total_received == 0) { memcpy(apEapState->frags_received, eap_msg, sizeof(WpsEapHdr)); apEapState->total_received += sizeof(WpsEapHdr); /* Ignore length field 2 bytes copy */ LF_bytes = EAP_WPS_LF_OFFSET; } /* WPS data */ memcpy(&apEapState->frags_received[apEapState->total_received], eap_msg + sizeof(WpsEapHdr) + LF_bytes, wps_len - LF_bytes); apEapState->total_received += wps_len - LF_bytes; /* Is last framentation? */ if (wpsEapHdr->flags & EAP_WPS_FLAGS_MF) { /* Increase eap_id */ apEapState->eap_id++; /* Send WPS_FRAG_ACK */ ret = ap_eap_sm_sendFACK(); if (ret == WPS_SUCCESS) return WPS_CONT; return ret; } /* Got all fragmentations, adjust WpsEapHdr */ wpsEapHdr = (WpsEapHdr *)apEapState->frags_received; wpsEapHdr->length = WpsHtons((uint16)apEapState->total_received); TUTRACE((TUTRACE_ERR, "Received all WPS fragmentations, total bytes of WPS data " "to receive %d, received %d\n", apEapState->total_bytes_to_recv, apEapState->total_received - sizeof(WpsEapHdr))); return WPS_SUCCESS; }
static uint32 ap_eap_sm_sta_validate(char *eapol_msg) { eapol_header_t *eapolHdr = (eapol_header_t *) eapol_msg; WpsEapHdr * wpsEapHdr = (WpsEapHdr *) eapolHdr->body; unsigned char *mac; /* Ignore non 802.1x EAP BRCM packets (dont error) */ if (WpsNtohs((uint8*)&eapolHdr->eth.ether_type) != ETHER_TYPE_802_1X) return WPS_ERR_GENERIC; /* Ignore EAPOL_LOGOFF */ if (eapolHdr->type == EAPOL_LOGOFF) { return WPS_CONT; } if ((WpsNtohs((uint8*)&eapolHdr->length) <= EAP_HEADER_LEN) || (eapolHdr->type != EAP_PACKET) || ((wpsEapHdr->type != EAP_EXPANDED) && (wpsEapHdr->type != EAP_IDENTITY))) { return WPS_ERR_GENERIC; } mac = lookupSta(eapolHdr->eth.ether_shost, SEARCH_ONLY); if ((wpsEapHdr->type == EAP_IDENTITY) && mac) { if (memcmp(eapolHdr->eth.ether_shost, mac, ETHER_ADDR_LEN)) { TUTRACE((TUTRACE_ERR, "Other sta's EAP_IDENTITY comes, ignore it...\n")); return WPS_CONT; } else { TUTRACE((TUTRACE_ERR, "EAP_IDENTITY comes again...\n")); } } mac = lookupSta(eapolHdr->eth.ether_shost, SEARCH_ENTER); if (!mac) { TUTRACE((TUTRACE_ERR, "no sta...\n")); return WPS_ERR_GENERIC; } return WPS_SUCCESS; }
uint32 reg_proto_check_nonce(IN uint8 *nonce, IN BufferObj *msg, IN int nonceType) { uint16 type; if ((nonceType != WPS_ID_REGISTRAR_NONCE) && (nonceType != WPS_ID_ENROLLEE_NONCE)) { TUTRACE((TUTRACE_ERR, "RPROTO: Invalid attribute ID passed to" " CheckNonce\n")); return WPS_ERR_INVALID_PARAMETERS; } while (1) { type = buffobj_NextType(msg); if (!type) break; if (nonceType == type) { if (!(memcmp(nonce, msg->pCurrent+sizeof(WpsTlvHdr), SIZE_128_BITS))) { buffobj_Rewind(msg); return WPS_SUCCESS; } else { TUTRACE((TUTRACE_ERR, "RPROTO: Nonce mismatch\n")); buffobj_Rewind(msg); return RPROT_ERR_NONCE_MISMATCH; } } /* * advance past the TLV - the total number of bytes to advance is * the size of the TLV header + the length indicated in the header */ if (!(buffobj_Advance(msg, sizeof(WpsTlvHdr) + WpsNtohs((msg->pCurrent+sizeof(uint16)))))) { TUTRACE((TUTRACE_ERR, "RPROTO: Didn't find nonce\n")); break; } } buffobj_Rewind(msg); return RPROT_ERR_REQD_TLV_MISSING; }
static int ap_eap_sm_process_protocol(char *eap_msg) { WpsEapHdr *wpsEapHdr = (WpsEapHdr *)eap_msg; char *in_data = (char *)(wpsEapHdr + 1); uint32 in_len = WpsNtohs((uint8*)&wpsEapHdr->length) - sizeof(WpsEapHdr); /* record the in message id, ingore WPS_Start */ if (wpsEapHdr->opcode != WPS_Start) { apEapState->recv_msg_id = in_data[WPS_MSGTYPE_OFFSET]; if (apEapState->recv_msg_id == WPS_ID_MESSAGE_M1) apEapState->flags |= AP_EAP_SM_M1_RECVD; } /* reset msg_len, shift EAP_WPS_LF_OFFSET */ apEapState->msg_len = sizeof(apEapState->msg_to_send) - WPS_DATA_OFFSET - EAP_WPS_LF_OFFSET; return wps_processMsg(apEapState->mc_dev, in_data, in_len, &apEapState->msg_to_send[WPS_DATA_OFFSET + EAP_WPS_LF_OFFSET], (uint32 *)&apEapState->msg_len, TRANSPORT_TYPE_EAP); }
uint32 ap_eap_sm_process_sta_msg(char *msg, int msg_len) { eapol_header_t *eapolHdr = NULL; WpsEapHdr *wpsEapHdr = NULL; int len; int wps_data_sent_bytes = apEapState->eap_frag_threshold; int advance_bytes = apEapState->eap_frag_threshold; int wps_eap_id; char *total_bytes_to_recv; WPS_SCMODE e_mode; uint32 ret; /* Check initialization */ if (apEapState == 0) return WPS_MESSAGE_PROCESSING_ERROR; if (apEapState->parse_msg) eapolHdr = (eapol_header_t *) (*apEapState->parse_msg)(msg, msg_len, &len); if (!eapolHdr) { TUTRACE((TUTRACE_ERR, "Missing EAPOL header\n")); return WPS_ERR_GENERIC; } /* Validate eapol message */ if ((ret = ap_eap_sm_sta_validate((char*)eapolHdr)) != WPS_SUCCESS) { TUTRACE((TUTRACE_ERR, "Vaildation failed...\n")); return ret; } /* Reset resend count */ apEapState->resent_count = 0; /* * Handle same station eap identity response comes again: * 1. When AP is a enrollee, we have to take care of same station eap identity response * comes again change to "WFA-SimpleConfig-Enrollee-1-0" * 2. When AP is a registrar, we have to take care of same station eap identity response * comes again change to "WFA-SimpleConfig-Registrar-1-0" */ wpsEapHdr = (WpsEapHdr *)eapolHdr->body; if (wpsEapHdr->type == EAP_TYPE_IDENTITY) { wps_eap_id = ap_eap_sm_get_wpsid(msg); /* Ignore unrecognized WPS eap id */ if (wps_eap_id == AP_EAP_WPS_ID_NONE) return WPS_CONT; e_mode = wps_get_mode(apEapState->mc_dev); /* Case 1, AP is running as Enrollee send EAP-Request(M1) */ if (e_mode == SCMODE_AP_ENROLLEE) { /* Peer switch to Enrollee comes again */ if (wps_eap_id == AP_EAP_WPS_ID_ENROLLEE) { TUTRACE((TUTRACE_INFO, "We are running as Enrollee but comes " "again EAP Identity Response changes to WFA-SimpleConfig-" "Enrollee-1-0. Return ERROR and close session!\n")); return WPS_MESSAGE_PROCESSING_ERROR; } /* Registrar comes again */ else if (apEapState->sent_msg_id == WPS_ID_MESSAGE_M1) { /* Re-send last sent message */ ap_eap_sm_resend_last_sent(); return WPS_CONT; } } /* Case 2, AP is running as Registrar send EAP-Request(Start) */ else if (e_mode == SCMODE_AP_REGISTRAR) { /* Peer switch to Registrar comes again */ if (wps_eap_id == AP_EAP_WPS_ID_REGISTRAR) { TUTRACE((TUTRACE_INFO, "We are running as Registrar but comes " "again EAP Identity Response changes to WFA-SimpleConfig-" "Registrar-1-0. Return ERROR and close session!\n")); return WPS_MESSAGE_PROCESSING_ERROR; } /* Enrollee comes again */ else if (apEapState->state == EAP_START_SEND) { /* Re-send last sent message */ ap_eap_sm_resend_last_sent(); return WPS_CONT; } } } /* STA retransmission checking */ ret = ap_eap_sm_sta_retrans((char*)eapolHdr); if (ret != WPS_SEND_RET_MSG_CONT && ret != WPS_CONT) { if (ret == WPS_IGNORE_MSG_CONT) ret = WPS_CONT; return ret; } else if (ret == WPS_SEND_RET_MSG_CONT) { /* Re-send last sent message */ ap_eap_sm_resend_last_sent(); return WPS_CONT; } /* * For Fragmentation: Test if we are waiting for a WPS_FRAG_ACK * If so, only accept that code and send the next fragment directly * with ap_eap_sm_sendNextFrag(). Return with no error. */ if (IS_EAP_TYPE_WPS(wpsEapHdr) && FRAGMENTING(apEapState)) { /* Only accept WPS_FRAG_ACK and send next fragmentation */ if (wpsEapHdr->opcode == WPS_FRAG_ACK) { TUTRACE((TUTRACE_INFO, "Receive a WPS fragment ACK packet, send next\n")); /* Is first WPS_FRAG_ACK? */ if (apEapState->next_frag_to_send == &apEapState->msg_to_send[EAPOL_HEADER_LEN]) { wps_data_sent_bytes -= EAP_WPS_LF_OFFSET; advance_bytes += sizeof(WpsEapHdr); } /* Advance to next fragment point */ apEapState->next_frag_to_send += advance_bytes; apEapState->frag_size -= wps_data_sent_bytes; apEapState->eap_id++; /* Get msg_to_send WpsEapHdr */ eapolHdr = (eapol_header_t *)apEapState->msg_to_send; wpsEapHdr = (WpsEapHdr *)eapolHdr->body; /* Send next fragmented packet */ ap_eap_sm_sendNextFrag(wpsEapHdr); return WPS_CONT; } /* Bypass non WPS_MSG */ else if (wpsEapHdr->opcode == WPS_MSG) { if (apEapState->frag_size < apEapState->eap_frag_threshold) { /* All fragmentations sent completed, reset next_frag_to_send */ apEapState->next_frag_to_send = apEapState->msg_to_send; apEapState->frag_size = 0; /* Fall through, process this message. */ } else { TUTRACE((TUTRACE_INFO, "Not a WPS_FRAG_ACK packet, ingore it\n")); return WPS_CONT; } } } /* For reassembly: Test if this is a fragmented packet. * If it is the begining, set the expected total. * Copy new data at position (frags_received + total_received) and increase total_received. * Make sure to detect re-transmissions (same ID)?. * When receiving a packet without the "More Fragment" flags, * continue, otherwise send a WPS_FRAG_ACK with ap_eap_sm_sendFACK() and return; */ total_bytes_to_recv = (char *)(wpsEapHdr + 1); if (IS_EAP_TYPE_WPS(wpsEapHdr) && REASSEMBLING(apEapState, wpsEapHdr)) { if (apEapState->total_bytes_to_recv == 0) { /* First fragmentation must have length information */ if (!(wpsEapHdr->flags & EAP_WPS_FLAGS_LF)) { return EAP_FAILURE; } /* Save total EAP message length */ apEapState->total_bytes_to_recv = WpsNtohs((uint8*)total_bytes_to_recv); TUTRACE((TUTRACE_INFO, "Got EAP WPS total bytes to receive info %d\n", apEapState->total_bytes_to_recv)); } ret = ap_eap_sm_process_frag((char *)wpsEapHdr); if (ret != WPS_SUCCESS) return ret; /* fall through when all fragmentations are received. */ apEapState->total_received = 0; /* Reset total_received */ apEapState->total_bytes_to_recv = 0; /* Use reassembled buffer */ wpsEapHdr = (WpsEapHdr *)apEapState->frags_received; } switch (apEapState->state) { case INIT : /* Reset counter */ apEapState->eap_id = 1; /* Request Start message */ return ap_eap_sm_req_start(); case EAP_START_SEND: case PROCESSING_PROTOCOL: apEapState->eap_id++; ret = ap_eap_sm_process_protocol((char *)wpsEapHdr); /* For DTM WCN Wireless ID 463. M1-M2D Proxy Functionality */ if (ret == WPS_AP_M2D_READY_CONT) ret = ap_eap_sm_apm2d_alloc(); return ret; case REG_SUCCESSFUL: return REG_SUCCESSFUL; } return WPS_CONT; }
/* * Check if we already have requested and our peer didn't receive it. * Also check that that the id matches the response. AP dependent. */ static uint32 ap_eap_sm_sta_retrans(char *eapol_msg) { char *wps_data; eapol_header_t *eapolHdr = (eapol_header_t *) eapol_msg; WpsEapHdr * wpsEapHdr = (WpsEapHdr *) eapolHdr->body; /* Re-ransmissions detection, fragmenting /Reassembling checking */ if (IS_EAP_TYPE_WPS(wpsEapHdr) && (FRAGMENTING(apEapState) || REASSEMBLING(apEapState, wpsEapHdr))) { if (wpsEapHdr->id == apEapState->eap_id - 1) { TUTRACE((TUTRACE_INFO, "wpsEapHdr->id=%d, apEapState->eap_id=%d\n", wpsEapHdr->id, apEapState->eap_id)); TUTRACE((TUTRACE_INFO, "Peer's retransmission of previous messages(FRAG)" ", ignore it\n")); return WPS_IGNORE_MSG_CONT; } return WPS_CONT; } switch (apEapState->state) { case INIT : if (wpsEapHdr->type != EAP_TYPE_IDENTITY) return WPS_ERR_GENERIC; if (wpsEapHdr->id != apEapState->eap_id || wpsEapHdr->code != EAP_CODE_RESPONSE) { TUTRACE((TUTRACE_ERR, "bogus EAP packet id %d code %d, " "expected %d\n", wpsEapHdr->id, wpsEapHdr->code, apEapState->eap_id)); cleanUpSta(DOT11_RC_8021X_AUTH_FAIL, 1); return WPS_ERR_GENERIC; } TUTRACE((TUTRACE_ERR, "Response, Identity, code=%d, id=%d, length=%d, type=%d\n", wpsEapHdr->code, wpsEapHdr->id, WpsNtohs((uint8*)&wpsEapHdr->length), wpsEapHdr->type)); /* Store whcih if sta come from */ memcpy(&apEapState->bssid, &eapolHdr->eth.ether_dhost, ETHER_ADDR_LEN); break; case EAP_START_SEND: case PROCESSING_PROTOCOL: if (wpsEapHdr->code != EAP_CODE_RESPONSE) { TUTRACE((TUTRACE_ERR, "bogus EAP packet not response code %d\n", wpsEapHdr->code)); cleanUpSta(DOT11_RC_8021X_AUTH_FAIL, 1); return WPS_ERR_GENERIC; } if (wpsEapHdr->id != apEapState->eap_id) { /* * Ignore eap_id check in ACK message, * When there are more than 2 ER, AP may receive * ACK for M2D-1 after AP send M2D-2, in this case * the M2D-1 and M2D-2 use same eap_id because * eap_id increased by receiving peer eap packet. * One other case AP receive M2D-1 and M2-2. * Note; see WSC 2.0 spec figure 9. */ if (apEapState->sent_msg_id == WPS_ID_MESSAGE_M2 || apEapState->sent_msg_id == WPS_ID_MESSAGE_M2D) { if (wpsEapHdr->opcode == WPS_ACK) break; /* Ignore retrans */ else if (wpsEapHdr->opcode == WPS_MSG) { wps_data = (char *)(wpsEapHdr + 1); if (wps_data[WPS_MSGTYPE_OFFSET] == WPS_ID_MESSAGE_M3) break; /* Ignore retrans */ } } /* * Do retransmission when comes messages with id = eap_id -1. * They are probably retransmissions of previous messages. */ if (wpsEapHdr->id == apEapState->eap_id - 1) { TUTRACE((TUTRACE_INFO, "wpsEapHdr->id=%d, apEapState->eap_id=%d\n", wpsEapHdr->id, apEapState->eap_id)); TUTRACE((TUTRACE_INFO, "Peer's retransmission of previous messages" ",ignore it\n")); return WPS_IGNORE_MSG_CONT; } TUTRACE((TUTRACE_ERR, "bogus EAP packet id %d, expected %d\n", wpsEapHdr->id, apEapState->eap_id)); cleanUpSta(DOT11_RC_8021X_AUTH_FAIL, 1); return WPS_ERR_GENERIC; } break; } return WPS_CONT; }