static struct wpabuf * eap_pwd_build_req(struct eap_sm *sm, void *priv, u8 id) { struct eap_pwd_data *data = priv; struct wpabuf *req; u8 lm_exch; const u8 *buf; u16 totlen = 0; size_t len; /* * if we're buffering response fragments then just ACK */ if (data->in_frag_pos) { wpa_printf(MSG_DEBUG, "EAP-pwd: ACKing a fragment!!"); req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, EAP_PWD_HDR_SIZE, EAP_CODE_REQUEST, id); if (req == NULL) { eap_pwd_state(data, FAILURE); return NULL; } switch (data->state) { case PWD_ID_Req: wpabuf_put_u8(req, EAP_PWD_OPCODE_ID_EXCH); break; case PWD_Commit_Req: wpabuf_put_u8(req, EAP_PWD_OPCODE_COMMIT_EXCH); break; case PWD_Confirm_Req: wpabuf_put_u8(req, EAP_PWD_OPCODE_CONFIRM_EXCH); break; default: eap_pwd_state(data, FAILURE); /* just to be sure */ wpabuf_free(req); return NULL; } return req; } /* * build the data portion of a request */ switch (data->state) { case PWD_ID_Req: eap_pwd_build_id_req(sm, data, id); lm_exch = EAP_PWD_OPCODE_ID_EXCH; break; case PWD_Commit_Req: eap_pwd_build_commit_req(sm, data, id); lm_exch = EAP_PWD_OPCODE_COMMIT_EXCH; break; case PWD_Confirm_Req: eap_pwd_build_confirm_req(sm, data, id); lm_exch = EAP_PWD_OPCODE_CONFIRM_EXCH; break; default: wpa_printf(MSG_INFO, "EAP-pwd: Unknown state %d in build_req", data->state); eap_pwd_state(data, FAILURE); lm_exch = 0; /* hush now, sweet compiler */ break; } if (data->state == FAILURE) return NULL; /* * determine whether that data needs to be fragmented */ len = wpabuf_len(data->outbuf) - data->out_frag_pos; if ((len + EAP_PWD_HDR_SIZE) > data->mtu) { len = data->mtu - EAP_PWD_HDR_SIZE; EAP_PWD_SET_MORE_BIT(lm_exch); /* * if this is the first fragment, need to set the M bit * and add the total length to the eap_pwd_hdr */ if (data->out_frag_pos == 0) { EAP_PWD_SET_LENGTH_BIT(lm_exch); totlen = wpabuf_len(data->outbuf) + EAP_PWD_HDR_SIZE + sizeof(u16); len -= sizeof(u16); wpa_printf(MSG_DEBUG, "EAP-pwd: Fragmenting output, " "total length = %d", totlen); } wpa_printf(MSG_DEBUG, "EAP-pwd: Send a %d byte fragment", (int) len); } /* * alloc an eap request and populate it with the data */ req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, EAP_PWD_HDR_SIZE + len + (totlen ? sizeof(u16) : 0), EAP_CODE_REQUEST, id); if (req == NULL) { eap_pwd_state(data, FAILURE); return NULL; } wpabuf_put_u8(req, lm_exch); if (EAP_PWD_GET_LENGTH_BIT(lm_exch)) wpabuf_put_be16(req, totlen); buf = wpabuf_head_u8(data->outbuf); wpabuf_put_data(req, buf + data->out_frag_pos, len); data->out_frag_pos += len; /* * either not fragged or last fragment, either way free up the data */ if (data->out_frag_pos >= wpabuf_len(data->outbuf)) { wpabuf_free(data->outbuf); data->outbuf = NULL; data->out_frag_pos = 0; } return req; }
static struct wpabuf * eap_pwd_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, const struct wpabuf *reqData) { struct eap_pwd_data *data = priv; struct wpabuf *resp = NULL; const u8 *pos, *buf; size_t len; u16 tot_len = 0; u8 lm_exch; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PWD, reqData, &len); if ((pos == NULL) || (len < 1)) { wpa_printf(MSG_DEBUG, "EAP-pwd: Got a frame but pos is %s and " "len is %d", pos == NULL ? "NULL" : "not NULL", (int) len); ret->ignore = TRUE; return NULL; } ret->ignore = FALSE; ret->methodState = METHOD_MAY_CONT; ret->decision = DECISION_FAIL; ret->allowNotifications = FALSE; lm_exch = *pos; pos++; /* skip over the bits and the exch */ len--; /* * we're fragmenting so send out the next fragment */ if (data->out_frag_pos) { /* * this should be an ACK */ if (len) wpa_printf(MSG_INFO, "Bad Response! Fragmenting but " "not an ACK"); wpa_printf(MSG_DEBUG, "EAP-pwd: Got an ACK for a fragment"); /* * check if there are going to be more fragments */ len = wpabuf_len(data->outbuf) - data->out_frag_pos; if ((len + EAP_PWD_HDR_SIZE) > data->mtu) { len = data->mtu - EAP_PWD_HDR_SIZE; EAP_PWD_SET_MORE_BIT(lm_exch); } resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, EAP_PWD_HDR_SIZE + len, EAP_CODE_RESPONSE, eap_get_id(reqData)); if (resp == NULL) { wpa_printf(MSG_INFO, "Unable to allocate memory for " "next fragment!"); return NULL; } wpabuf_put_u8(resp, lm_exch); buf = wpabuf_head_u8(data->outbuf); wpabuf_put_data(resp, buf + data->out_frag_pos, len); data->out_frag_pos += len; /* * this is the last fragment so get rid of the out buffer */ if (data->out_frag_pos >= wpabuf_len(data->outbuf)) { wpabuf_free(data->outbuf); data->outbuf = NULL; data->out_frag_pos = 0; } wpa_printf(MSG_DEBUG, "EAP-pwd: Send %s fragment of %d bytes", data->out_frag_pos == 0 ? "last" : "next", (int) len); return resp; } /* * see if this is a fragment that needs buffering * * if it's the first fragment there'll be a length field */ if (EAP_PWD_GET_LENGTH_BIT(lm_exch)) { tot_len = WPA_GET_BE16(pos); wpa_printf(MSG_DEBUG, "EAP-pwd: Incoming fragments whose " "total length = %d", tot_len); data->inbuf = wpabuf_alloc(tot_len); if (data->inbuf == NULL) { wpa_printf(MSG_INFO, "Out of memory to buffer " "fragments!"); return NULL; } pos += sizeof(u16); len -= sizeof(u16); } /* * buffer and ACK the fragment */ if (EAP_PWD_GET_MORE_BIT(lm_exch)) { data->in_frag_pos += len; if (data->in_frag_pos > wpabuf_size(data->inbuf)) { wpa_printf(MSG_INFO, "EAP-pwd: Buffer overflow attack " "detected (%d vs. %d)!", (int) data->in_frag_pos, (int) wpabuf_len(data->inbuf)); wpabuf_free(data->inbuf); data->in_frag_pos = 0; return NULL; } wpabuf_put_data(data->inbuf, pos, len); resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, EAP_PWD_HDR_SIZE, EAP_CODE_RESPONSE, eap_get_id(reqData)); if (resp != NULL) wpabuf_put_u8(resp, (EAP_PWD_GET_EXCHANGE(lm_exch))); wpa_printf(MSG_DEBUG, "EAP-pwd: ACKing a %d byte fragment", (int) len); return resp; } /* * we're buffering and this is the last fragment */ if (data->in_frag_pos) { wpabuf_put_data(data->inbuf, pos, len); wpa_printf(MSG_DEBUG, "EAP-pwd: Last fragment, %d bytes", (int) len); data->in_frag_pos += len; pos = wpabuf_head_u8(data->inbuf); len = data->in_frag_pos; } wpa_printf(MSG_DEBUG, "EAP-pwd: processing frame: exch %d, len %d", EAP_PWD_GET_EXCHANGE(lm_exch), (int) len); switch (EAP_PWD_GET_EXCHANGE(lm_exch)) { case EAP_PWD_OPCODE_ID_EXCH: eap_pwd_perform_id_exchange(sm, data, ret, reqData, pos, len); break; case EAP_PWD_OPCODE_COMMIT_EXCH: eap_pwd_perform_commit_exchange(sm, data, ret, reqData, pos, len); break; case EAP_PWD_OPCODE_CONFIRM_EXCH: eap_pwd_perform_confirm_exchange(sm, data, ret, reqData, pos, len); break; default: wpa_printf(MSG_INFO, "EAP-pwd: Ignoring message with unknown " "opcode %d", lm_exch); break; } /* * if we buffered the just processed input now's the time to free it */ if (data->in_frag_pos) { wpabuf_free(data->inbuf); data->in_frag_pos = 0; } if (data->outbuf == NULL) return NULL; /* generic failure */ /* * we have output! Do we need to fragment it? */ len = wpabuf_len(data->outbuf); if ((len + EAP_PWD_HDR_SIZE) > data->mtu) { resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, data->mtu, EAP_CODE_RESPONSE, eap_get_id(reqData)); /* * if so it's the first so include a length field */ EAP_PWD_SET_LENGTH_BIT(lm_exch); EAP_PWD_SET_MORE_BIT(lm_exch); tot_len = len; /* * keep the packet at the MTU */ len = data->mtu - EAP_PWD_HDR_SIZE - sizeof(u16); wpa_printf(MSG_DEBUG, "EAP-pwd: Fragmenting output, total " "length = %d", tot_len); } else { resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, EAP_PWD_HDR_SIZE + len, EAP_CODE_RESPONSE, eap_get_id(reqData)); } if (resp == NULL) return NULL; wpabuf_put_u8(resp, lm_exch); if (EAP_PWD_GET_LENGTH_BIT(lm_exch)) { wpabuf_put_be16(resp, tot_len); data->out_frag_pos += len; } buf = wpabuf_head_u8(data->outbuf); wpabuf_put_data(resp, buf, len); /* * if we're not fragmenting then there's no need to carry this around */ if (data->out_frag_pos == 0) { wpabuf_free(data->outbuf); data->outbuf = NULL; data->out_frag_pos = 0; } return resp; }
static int send_pwd_request (pwd_session_t *sess, EAP_DS *eap_ds) { int len; uint16_t totlen; pwd_hdr *hdr; len = (sess->out_buf_len - sess->out_buf_pos) + sizeof(pwd_hdr); rad_assert(len > 0); eap_ds->request->code = PW_EAP_REQUEST; eap_ds->request->type.num = PW_EAP_PWD; eap_ds->request->type.length = (len > sess->mtu) ? sess->mtu : len; eap_ds->request->type.data = talloc_zero_array(eap_ds->request, uint8_t, eap_ds->request->type.length); hdr = (pwd_hdr *)eap_ds->request->type.data; switch (sess->state) { case PWD_STATE_ID_REQ: EAP_PWD_SET_EXCHANGE(hdr, EAP_PWD_EXCH_ID); break; case PWD_STATE_COMMIT: EAP_PWD_SET_EXCHANGE(hdr, EAP_PWD_EXCH_COMMIT); break; case PWD_STATE_CONFIRM: EAP_PWD_SET_EXCHANGE(hdr, EAP_PWD_EXCH_CONFIRM); break; default: DEBUG2("pwd state is messed up! Cannot send request"); return 0; } /* * are we fragmenting? */ if ((int)((sess->out_buf_len - sess->out_buf_pos) + sizeof(pwd_hdr)) > sess->mtu) { EAP_PWD_SET_MORE_BIT(hdr); if (sess->out_buf_pos == 0) { /* * the first fragment, add the total length */ EAP_PWD_SET_LENGTH_BIT(hdr); totlen = ntohs(sess->out_buf_len); memcpy(hdr->data, (char *)&totlen, sizeof(totlen)); memcpy(hdr->data + sizeof(uint16_t), sess->out_buf, sess->mtu - sizeof(pwd_hdr) - sizeof(uint16_t)); sess->out_buf_pos += (sess->mtu - sizeof(pwd_hdr) - sizeof(uint16_t)); } else { /* * an intermediate fragment */ memcpy(hdr->data, sess->out_buf + sess->out_buf_pos, (sess->mtu - sizeof(pwd_hdr))); sess->out_buf_pos += (sess->mtu - sizeof(pwd_hdr)); } } else { /* * either it's not a fragment or it's the last fragment. * The out buffer isn't needed anymore though so get rid of it. */ memcpy(hdr->data, sess->out_buf + sess->out_buf_pos, (sess->out_buf_len - sess->out_buf_pos)); talloc_free(sess->out_buf); sess->out_buf = NULL; sess->out_buf_pos = sess->out_buf_len = 0; } return 1; }