/* * Run a virtual server auth and postauth * */ int rad_virtual_server(REQUEST *request) { VALUE_PAIR *vp; int result; RDEBUG("server %s {", request->server); RDEBUG(" Request:"); debug_pair_list(request->packet->vps); /* * We currently only handle AUTH packets here. * This could be expanded to handle other packets as well if required. */ rad_assert(request->packet->code == PW_CODE_ACCESS_REQUEST); result = rad_authenticate(request); if (request->reply->code == PW_CODE_ACCESS_REJECT) { pairdelete(&request->config_items, PW_POST_AUTH_TYPE, 0, TAG_ANY); vp = pairmake_config("Post-Auth-Type", "Reject", T_OP_SET); if (vp) rad_postauth(request); } if (request->reply->code == PW_CODE_ACCESS_ACCEPT) { rad_postauth(request); } RDEBUG(" Reply:"); debug_pair_list(request->reply->vps); RDEBUG("} # server %s", request->server); return result; }
/* * Do authentication, by letting EAP-TLS do most of the work. */ static int mod_authenticate(void *arg, eap_handler_t *handler) { int rcode; fr_tls_status_t status; rlm_eap_peap_t *inst = (rlm_eap_peap_t *) arg; tls_session_t *tls_session = (tls_session_t *) handler->opaque; peap_tunnel_t *peap = tls_session->opaque; REQUEST *request = handler->request; /* * Session resumption requires the storage of data, so * allocate it if it doesn't already exist. */ if (!tls_session->opaque) { peap = tls_session->opaque = peap_alloc(tls_session, inst); } status = eaptls_process(handler); RDEBUG2("eaptls_process returned %d\n", status); switch (status) { /* * EAP-TLS handshake was successful, tell the * client to keep talking. * * If this was EAP-TLS, we would just return * an EAP-TLS-Success packet here. */ case FR_TLS_SUCCESS: RDEBUG2("FR_TLS_SUCCESS"); peap->status = PEAP_STATUS_TUNNEL_ESTABLISHED; break; /* * The TLS code is still working on the TLS * exchange, and it's a valid TLS request. * do nothing. */ case FR_TLS_HANDLED: /* * FIXME: If the SSL session is established, grab the state * and EAP id from the inner tunnel, and update it with * the expected EAP id! */ RDEBUG2("FR_TLS_HANDLED"); return 1; /* * Handshake is done, proceed with decoding tunneled * data. */ case FR_TLS_OK: RDEBUG2("FR_TLS_OK"); break; /* * Anything else: fail. */ default: RDEBUG2("FR_TLS_OTHERS"); return 0; } /* * Session is established, proceed with decoding * tunneled data. */ RDEBUG2("Session established. Decoding tunneled attributes"); /* * We may need PEAP data associated with the session, so * allocate it here, if it wasn't already alloacted. */ if (!tls_session->opaque) { tls_session->opaque = peap_alloc(tls_session, inst); } /* * Process the PEAP portion of the request. */ rcode = eappeap_process(handler, tls_session); switch (rcode) { case RLM_MODULE_REJECT: eaptls_fail(handler, 0); return 0; case RLM_MODULE_HANDLED: eaptls_request(handler->eap_ds, tls_session); return 1; case RLM_MODULE_OK: /* * Move the saved VP's from the Access-Accept to * our Access-Accept. */ peap = tls_session->opaque; if (peap->soh_reply_vps) { RDEBUG2("Using saved attributes from the SoH reply"); debug_pair_list(peap->soh_reply_vps); pairfilter(handler->request->reply, &handler->request->reply->vps, &peap->soh_reply_vps, 0, 0, TAG_ANY); } if (peap->accept_vps) { RDEBUG2("Using saved attributes from the original Access-Accept"); debug_pair_list(peap->accept_vps); pairfilter(handler->request->reply, &handler->request->reply->vps, &peap->accept_vps, 0, 0, TAG_ANY); } /* * Success: Automatically return MPPE keys. */ return eaptls_success(handler, 0); /* * No response packet, MUST be proxying it. * The main EAP module will take care of discovering * that the request now has a "proxy" packet, and * will proxy it, rather than returning an EAP packet. */ case RLM_MODULE_UPDATED: #ifdef WITH_PROXY rad_assert(handler->request->proxy != NULL); #endif return 1; break; default: break; } eaptls_fail(handler, 0); return 0; }
/* * Do authentication, by letting EAP-TLS do most of the work. */ static int eapttls_authenticate(void *arg, EAP_HANDLER *handler) { int rcode; fr_tls_status_t status; rlm_eap_ttls_t *inst = (rlm_eap_ttls_t *) arg; tls_session_t *tls_session = (tls_session_t *) handler->opaque; ttls_tunnel_t *t = (ttls_tunnel_t *) tls_session->opaque; REQUEST *request = handler->request; RDEBUG2("Authenticate"); tls_session->length_flag = inst->include_length; /* * Process TLS layer until done. */ status = eaptls_process(handler); RDEBUG2("eaptls_process returned %d\n", status); switch (status) { /* * EAP-TLS handshake was successful, tell the * client to keep talking. * * If this was EAP-TLS, we would just return * an EAP-TLS-Success packet here. */ case FR_TLS_SUCCESS: if (SSL_session_reused(tls_session->ssl)) { RDEBUG("Skipping Phase2 due to session resumption"); goto do_keys; } if (t && t->authenticated) { RDEBUG2("Using saved attributes from the original Access-Accept"); debug_pair_list(t->accept_vps); pairadd(&handler->request->reply->vps, t->accept_vps); t->accept_vps = NULL; do_keys: /* * Success: Automatically return MPPE keys. */ return eaptls_success(handler, 0); } else { eaptls_request(handler->eap_ds, tls_session); } return 1; /* * The TLS code is still working on the TLS * exchange, and it's a valid TLS request. * do nothing. */ case FR_TLS_HANDLED: return 1; /* * Handshake is done, proceed with decoding tunneled * data. */ case FR_TLS_OK: break; /* * Anything else: fail. */ default: return 0; } /* * Session is established, proceed with decoding * tunneled data. */ RDEBUG2("Session established. Proceeding to decode tunneled attributes."); /* * We may need TTLS data associated with the session, so * allocate it here, if it wasn't already alloacted. */ if (!tls_session->opaque) { tls_session->opaque = ttls_alloc(inst); tls_session->free_opaque = ttls_free; } /* * Process the TTLS portion of the request. */ rcode = eapttls_process(handler, tls_session); switch (rcode) { case PW_AUTHENTICATION_REJECT: eaptls_fail(handler, 0); return 0; /* * Access-Challenge, continue tunneled conversation. */ case PW_ACCESS_CHALLENGE: eaptls_request(handler->eap_ds, tls_session); return 1; /* * Success: Automatically return MPPE keys. */ case PW_AUTHENTICATION_ACK: return eaptls_success(handler, 0); /* * No response packet, MUST be proxying it. * The main EAP module will take care of discovering * that the request now has a "proxy" packet, and * will proxy it, rather than returning an EAP packet. */ case PW_STATUS_CLIENT: #ifdef WITH_PROXY rad_assert(handler->request->proxy != NULL); #endif return 1; break; default: break; } /* * Something we don't understand: Reject it. */ eaptls_fail(handler, 0); return 0; }
/** Send the challenge itself * * Challenges will come from one of three places eventually: * * 1 from attributes like PW_EAP_SIM_RANDx * (these might be retrived from a database) * * 2 from internally implemented SIM authenticators * (a simple one based upon XOR will be provided) * * 3 from some kind of SS7 interface. * * For now, they only come from attributes. * It might be that the best way to do 2/3 will be with a different * module to generate/calculate things. * */ static int eap_sim_sendchallenge(eap_handler_t *handler) { REQUEST *request = handler->request; eap_sim_state_t *ess; VALUE_PAIR **invps, **outvps, *newvp; RADIUS_PACKET *packet; uint8_t *p; ess = (eap_sim_state_t *)handler->opaque; rad_assert(handler->request != NULL); rad_assert(handler->request->reply); /* * Invps is the data from the client but this is for non-protocol data here. * We should already have consumed any client originated data. */ invps = &handler->request->packet->vps; /* * Outvps is the data to the client */ packet = handler->request->reply; outvps = &packet->vps; if (RDEBUG_ENABLED2) { RDEBUG2("EAP-SIM decoded packet:"); debug_pair_list(*invps); } /* * Okay, we got the challenges! Put them into an attribute. */ newvp = paircreate(packet, PW_EAP_SIM_RAND, 0); newvp->length = 2 + (EAPSIM_RAND_SIZE * 3); newvp->vp_octets = p = talloc_array(newvp, uint8_t, newvp->length); memset(p, 0, 2); /* clear reserved bytes */ p += 2; memcpy(p, ess->keys.rand[0], EAPSIM_RAND_SIZE); p += EAPSIM_RAND_SIZE; memcpy(p, ess->keys.rand[1], EAPSIM_RAND_SIZE); p += EAPSIM_RAND_SIZE; memcpy(p, ess->keys.rand[2], EAPSIM_RAND_SIZE); pairadd(outvps, newvp); /* * Set the EAP_ID - new value */ newvp = paircreate(packet, PW_EAP_ID, 0); newvp->vp_integer = ess->sim_id++; pairreplace(outvps, newvp); /* * Make a copy of the identity */ ess->keys.identitylen = strlen(handler->identity); memcpy(ess->keys.identity, handler->identity, ess->keys.identitylen); /* * Use the SIM identity, if available */ newvp = pairfind(*invps, PW_EAP_SIM_IDENTITY, 0, TAG_ANY); if (newvp && newvp->length > 2) { uint16_t len; memcpy(&len, newvp->vp_octets, sizeof(uint16_t)); len = ntohs(len); if (len <= newvp->length - 2 && len <= MAX_STRING_LEN) { ess->keys.identitylen = len; memcpy(ess->keys.identity, newvp->vp_octets + 2, ess->keys.identitylen); } } /* * All set, calculate keys! */ eapsim_calculate_keys(&ess->keys); #ifdef EAP_SIM_DEBUG_PRF eapsim_dump_mk(&ess->keys); #endif /* * Need to include an AT_MAC attribute so that it will get * calculated. The NONCE_MT and the MAC are both 16 bytes, so * We store the NONCE_MT in the MAC for the encoder, which * will pull it out before it does the operation. */ newvp = paircreate(packet, PW_EAP_SIM_MAC, 0); pairmemcpy(newvp, ess->keys.nonce_mt, 16); pairreplace(outvps, newvp); newvp = paircreate(packet, PW_EAP_SIM_KEY, 0); pairmemcpy(newvp, ess->keys.K_aut, 16); pairreplace(outvps, newvp); /* the SUBTYPE, set to challenge. */ newvp = paircreate(packet, PW_EAP_SIM_SUBTYPE, 0); newvp->vp_integer = EAPSIM_CHALLENGE; pairreplace(outvps, newvp); return 1; }
static int mod_authenticate (void *arg, eap_handler_t *handler) { pwd_session_t *pwd_session; pwd_hdr *hdr; pwd_id_packet *id; eap_packet_t *response; REQUEST *request, *fake; VALUE_PAIR *pw, *vp; EAP_DS *eap_ds; int len, ret = 0; eap_pwd_t *inst = (eap_pwd_t *)arg; uint16_t offset; uint8_t exch, *buf, *ptr, msk[MSK_EMSK_LEN], emsk[MSK_EMSK_LEN]; uint8_t peer_confirm[SHA256_DIGEST_LENGTH]; BIGNUM *x = NULL, *y = NULL; if ((!handler) || ((eap_ds = handler->eap_ds) == NULL) || (!inst)) { return 0; } pwd_session = (pwd_session_t *)handler->opaque; request = handler->request; response = handler->eap_ds->response; hdr = (pwd_hdr *)response->type.data; buf = hdr->data; len = response->type.length - sizeof(pwd_hdr); /* * see if we're fragmenting, if so continue until we're done */ if (pwd_session->out_buf_pos) { if (len) { RDEBUG2("pwd got something more than an ACK for a fragment"); } return send_pwd_request(pwd_session, eap_ds); } /* * the first fragment will have a total length, make a * buffer to hold all the fragments */ if (EAP_PWD_GET_LENGTH_BIT(hdr)) { if (pwd_session->in_buf) { RDEBUG2("pwd already alloced buffer for fragments"); return 0; } pwd_session->in_buf_len = ntohs(buf[0] * 256 | buf[1]); if ((pwd_session->in_buf = talloc_zero_array(pwd_session, uint8_t, pwd_session->in_buf_len)) == NULL) { RDEBUG2("pwd cannot allocate %d buffer to hold fragments", pwd_session->in_buf_len); return 0; } memset(pwd_session->in_buf, 0, pwd_session->in_buf_len); pwd_session->in_buf_pos = 0; buf += sizeof(uint16_t); len -= sizeof(uint16_t); } /* * all fragments, including the 1st will have the M(ore) bit set, * buffer those fragments! */ if (EAP_PWD_GET_MORE_BIT(hdr)) { rad_assert(pwd_session->in_buf != NULL); if ((pwd_session->in_buf_pos + len) > pwd_session->in_buf_len) { RDEBUG2("pwd will not overflow a fragment buffer. Nope, not prudent."); return 0; } memcpy(pwd_session->in_buf + pwd_session->in_buf_pos, buf, len); pwd_session->in_buf_pos += len; /* * send back an ACK for this fragment */ exch = EAP_PWD_GET_EXCHANGE(hdr); eap_ds->request->code = PW_EAP_REQUEST; eap_ds->request->type.num = PW_EAP_PWD; eap_ds->request->type.length = sizeof(pwd_hdr); if ((eap_ds->request->type.data = talloc_array(eap_ds->request, uint8_t, sizeof(pwd_hdr))) == NULL) { return 0; } hdr = (pwd_hdr *)eap_ds->request->type.data; EAP_PWD_SET_EXCHANGE(hdr, exch); return 1; } if (pwd_session->in_buf) { /* * the last fragment... */ if ((pwd_session->in_buf_pos + len) > pwd_session->in_buf_len) { RDEBUG2("pwd will not overflow a fragment buffer. Nope, not prudent."); return 0; } memcpy(pwd_session->in_buf + pwd_session->in_buf_pos, buf, len); buf = pwd_session->in_buf; len = pwd_session->in_buf_len; } switch (pwd_session->state) { case PWD_STATE_ID_REQ: if (EAP_PWD_GET_EXCHANGE(hdr) != EAP_PWD_EXCH_ID) { RDEBUG2("pwd exchange is incorrect: not ID"); return 0; } id = (pwd_id_packet *)buf; if ((id->prf != EAP_PWD_DEF_PRF) || (id->random_function != EAP_PWD_DEF_RAND_FUN) || (id->prep != EAP_PWD_PREP_NONE) || (memcmp(id->token, (char *)&pwd_session->token, 4)) || (id->group_num != ntohs(pwd_session->group_num))) { RDEBUG2("pwd id response is invalid"); return 0; } /* * we've agreed on the ciphersuite, record it... */ ptr = (uint8_t *)&pwd_session->ciphersuite; memcpy(ptr, (char *)&id->group_num, sizeof(uint16_t)); ptr += sizeof(uint16_t); *ptr = EAP_PWD_DEF_RAND_FUN; ptr += sizeof(uint8_t); *ptr = EAP_PWD_DEF_PRF; pwd_session->peer_id_len = len - sizeof(pwd_id_packet); if (pwd_session->peer_id_len >= sizeof(pwd_session->peer_id)) { RDEBUG2("pwd id response is malformed"); return 0; } memcpy(pwd_session->peer_id, id->identity, pwd_session->peer_id_len); pwd_session->peer_id[pwd_session->peer_id_len] = '\0'; /* * make fake request to get the password for the usable ID */ if ((fake = request_alloc_fake(handler->request)) == NULL) { RDEBUG("pwd unable to create fake request!"); return 0; } fake->username = pairmake_packet("User-Name", "", T_OP_EQ); if (!fake->username) { RDEBUG("pwd unanable to create value pair for username!"); request_free(&fake); return 0; } memcpy(fake->username->vp_strvalue, pwd_session->peer_id, pwd_session->peer_id_len); fake->username->length = pwd_session->peer_id_len; fake->username->vp_strvalue[fake->username->length] = 0; if ((vp = pairfind(request->config_items, PW_VIRTUAL_SERVER, 0, TAG_ANY)) != NULL) { fake->server = vp->vp_strvalue; } else if (inst->conf->virtual_server) { fake->server = inst->conf->virtual_server; } /* else fake->server == request->server */ if ((debug_flag > 0) && fr_log_fp) { RDEBUG("Sending tunneled request"); debug_pair_list(fake->packet->vps); fprintf(fr_log_fp, "server %s {\n", (!fake->server) ? "" : fake->server); } /* * Call authorization recursively, which will * get the password. */ process_authorize(0, fake); /* * Note that we don't do *anything* with the reply * attributes. */ if ((debug_flag > 0) && fr_log_fp) { fprintf(fr_log_fp, "} # server %s\n", (!fake->server) ? "" : fake->server); RDEBUG("Got tunneled reply code %d", fake->reply->code); debug_pair_list(fake->reply->vps); } if ((pw = pairfind(fake->config_items, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY)) == NULL) { DEBUG2("failed to find password for %s to do pwd authentication", pwd_session->peer_id); request_free(&fake); return 0; } if (compute_password_element(pwd_session, pwd_session->group_num, pw->data.strvalue, strlen(pw->data.strvalue), inst->conf->server_id, strlen(inst->conf->server_id), pwd_session->peer_id, strlen(pwd_session->peer_id), &pwd_session->token)) { DEBUG2("failed to obtain password element :-("); request_free(&fake); return 0; } request_free(&fake); /* * compute our scalar and element */ if (compute_scalar_element(pwd_session, inst->bnctx)) { DEBUG2("failed to compute server's scalar and element"); return 0; } if (((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) { DEBUG2("server point allocation failed"); return 0; } /* * element is a point, get both coordinates: x and y */ if (!EC_POINT_get_affine_coordinates_GFp(pwd_session->group, pwd_session->my_element, x, y, inst->bnctx)) { DEBUG2("server point assignment failed"); BN_free(x); BN_free(y); return 0; } /* * construct request */ pwd_session->out_buf_len = BN_num_bytes(pwd_session->order) + (2 * BN_num_bytes(pwd_session->prime)); if ((pwd_session->out_buf = talloc_array(pwd_session, uint8_t, pwd_session->out_buf_len)) == NULL) { return 0; } memset(pwd_session->out_buf, 0, pwd_session->out_buf_len); ptr = pwd_session->out_buf; offset = BN_num_bytes(pwd_session->prime) - BN_num_bytes(x); BN_bn2bin(x, ptr + offset); ptr += BN_num_bytes(pwd_session->prime); offset = BN_num_bytes(pwd_session->prime) - BN_num_bytes(y); BN_bn2bin(y, ptr + offset); ptr += BN_num_bytes(pwd_session->prime); offset = BN_num_bytes(pwd_session->order) - BN_num_bytes(pwd_session->my_scalar); BN_bn2bin(pwd_session->my_scalar, ptr + offset); pwd_session->state = PWD_STATE_COMMIT; ret = send_pwd_request(pwd_session, eap_ds); break; case PWD_STATE_COMMIT: if (EAP_PWD_GET_EXCHANGE(hdr) != EAP_PWD_EXCH_COMMIT) { RDEBUG2("pwd exchange is incorrect: not commit!"); return 0; } /* * process the peer's commit and generate the shared key, k */ if (process_peer_commit(pwd_session, buf, inst->bnctx)) { RDEBUG2("failed to process peer's commit"); return 0; } /* * compute our confirm blob */ if (compute_server_confirm(pwd_session, pwd_session->my_confirm, inst->bnctx)) { ERROR("rlm_eap_pwd: failed to compute confirm!"); return 0; } /* * construct a response...which is just our confirm blob */ pwd_session->out_buf_len = SHA256_DIGEST_LENGTH; if ((pwd_session->out_buf = talloc_array(pwd_session, uint8_t, pwd_session->out_buf_len)) == NULL) { return 0; } memset(pwd_session->out_buf, 0, pwd_session->out_buf_len); memcpy(pwd_session->out_buf, pwd_session->my_confirm, SHA256_DIGEST_LENGTH); pwd_session->state = PWD_STATE_CONFIRM; ret = send_pwd_request(pwd_session, eap_ds); break; case PWD_STATE_CONFIRM: if (EAP_PWD_GET_EXCHANGE(hdr) != EAP_PWD_EXCH_CONFIRM) { RDEBUG2("pwd exchange is incorrect: not commit!"); return 0; } if (compute_peer_confirm(pwd_session, peer_confirm, inst->bnctx)) { RDEBUG2("pwd exchange cannot compute peer's confirm"); return 0; } if (memcmp(peer_confirm, buf, SHA256_DIGEST_LENGTH)) { RDEBUG2("pwd exchange fails: peer confirm is incorrect!"); return 0; } if (compute_keys(pwd_session, peer_confirm, msk, emsk)) { RDEBUG2("pwd exchange cannot generate (E)MSK!"); return 0; } eap_ds->request->code = PW_EAP_SUCCESS; /* * return the MSK (in halves) */ eap_add_reply(handler->request, "MS-MPPE-Recv-Key", msk, MPPE_KEY_LEN); eap_add_reply(handler->request, "MS-MPPE-Send-Key", msk+MPPE_KEY_LEN, MPPE_KEY_LEN); ret = 1; break; default: RDEBUG2("unknown PWD state"); return 0; } /* * we processed the buffered fragments, get rid of them */ if (pwd_session->in_buf) { talloc_free(pwd_session->in_buf); pwd_session->in_buf = NULL; } return ret; }
/* * Do authentication, by letting EAP-TLS do most of the work. */ static int CC_HINT(nonnull) mod_authenticate(void *type_arg, eap_handler_t *handler) { fr_tls_status_t status; tls_session_t *tls_session = (tls_session_t *) handler->opaque; REQUEST *request = handler->request; rlm_eap_tls_t *inst; inst = type_arg; RDEBUG2("Authenticate"); status = eaptls_process(handler); RDEBUG2("eaptls_process returned %d\n", status); switch (status) { /* * EAP-TLS handshake was successful, return an * EAP-TLS-Success packet here. * * If a virtual server was configured, check that * it accepts the certificates, too. */ case FR_TLS_SUCCESS: if (inst->virtual_server) { VALUE_PAIR *vp; REQUEST *fake; /* create a fake request */ fake = request_alloc_fake(request); rad_assert(!fake->packet->vps); fake->packet->vps = paircopy(fake->packet, request->packet->vps); /* set the virtual server to use */ if ((vp = pairfind(request->config_items, PW_VIRTUAL_SERVER, 0, TAG_ANY)) != NULL) { fake->server = vp->vp_strvalue; } else { fake->server = inst->virtual_server; } RDEBUG("Processing EAP-TLS Certificate check:"); debug_pair_list(fake->packet->vps); RDEBUG("server %s {", fake->server); rad_virtual_server(fake); RDEBUG("} # server %s", fake->server); /* copy the reply vps back to our reply */ pairfilter(request->reply, &request->reply->vps, &fake->reply->vps, 0, 0, TAG_ANY); /* reject if virtual server didn't return accept */ if (fake->reply->code != PW_CODE_AUTHENTICATION_ACK) { RDEBUG2("Certificates were rejected by the virtual server"); request_free(&fake); eaptls_fail(handler, 0); return 0; } request_free(&fake); /* success */ } break; /* * The TLS code is still working on the TLS * exchange, and it's a valid TLS request. * do nothing. */ case FR_TLS_HANDLED: return 1; /* * Handshake is done, proceed with decoding tunneled * data. */ case FR_TLS_OK: RDEBUG2("Received unexpected tunneled data after successful handshake"); #ifndef NDEBUG if ((debug_flag > 2) && fr_log_fp) { unsigned int i; unsigned int data_len; unsigned char buffer[1024]; data_len = (tls_session->record_minus)(&tls_session->dirty_in, buffer, sizeof(buffer)); DEBUG(" Tunneled data (%u bytes)", data_len); for (i = 0; i < data_len; i++) { if ((i & 0x0f) == 0x00) fprintf(fr_log_fp, " %x: ", i); if ((i & 0x0f) == 0x0f) fprintf(fr_log_fp, "\n"); fprintf(fr_log_fp, "%02x ", buffer[i]); } fprintf(fr_log_fp, "\n"); } #endif eaptls_fail(handler, 0); return 0; break; /* * Anything else: fail. * * Also, remove the session from the cache so that * the client can't re-use it. */ default: tls_fail(tls_session); return 0; } /* * Success: Automatically return MPPE keys. */ return eaptls_success(handler, 0); }
/* * this code sends the challenge itself. * * Challenges will come from one of three places eventually: * * 1 from attributes like ATTRIBUTE_EAP_SIM_RANDx * (these might be retrived from a database) * * 2 from internally implemented SIM authenticators * (a simple one based upon XOR will be provided) * * 3 from some kind of SS7 interface. * * For now, they only come from attributes. * It might be that the best way to do 2/3 will be with a different * module to generate/calculate things. * */ static int eap_sim_sendchallenge(EAP_HANDLER *handler) { struct eap_sim_server_state *ess; VALUE_PAIR **invps, **outvps, *newvp; ess = (struct eap_sim_server_state *)handler->opaque; rad_assert(handler->request != NULL); rad_assert(handler->request->reply); /* invps is the data from the client. * but, this is for non-protocol data here. We should * already have consumed any client originated data. */ invps = &handler->request->packet->vps; /* outvps is the data to the client. */ outvps= &handler->request->reply->vps; if ((debug_flag > 0) && fr_log_fp) { fprintf(fr_log_fp, "+++> EAP-sim decoded packet:\n"); debug_pair_list(*invps); } /* okay, we got the challenges! Put them into an attribute */ newvp = paircreate(ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_RAND, 0, PW_TYPE_OCTETS); memset(newvp->vp_strvalue, 0, 2); /* clear reserved bytes */ memcpy(newvp->vp_strvalue+2+EAPSIM_RAND_SIZE*0, ess->keys.rand[0], EAPSIM_RAND_SIZE); memcpy(newvp->vp_strvalue+2+EAPSIM_RAND_SIZE*1, ess->keys.rand[1], EAPSIM_RAND_SIZE); memcpy(newvp->vp_strvalue+2+EAPSIM_RAND_SIZE*2, ess->keys.rand[2], EAPSIM_RAND_SIZE); newvp->length = 2+EAPSIM_RAND_SIZE*3; pairadd(outvps, newvp); /* set the EAP_ID - new value */ newvp = paircreate(ATTRIBUTE_EAP_ID, 0, PW_TYPE_INTEGER); newvp->vp_integer = ess->sim_id++; pairreplace(outvps, newvp); /* make a copy of the identity */ ess->keys.identitylen = strlen(handler->identity); memcpy(ess->keys.identity, handler->identity, ess->keys.identitylen); /* use the SIM identity, if available */ newvp = pairfind(*invps, ATTRIBUTE_EAP_SIM_BASE + PW_EAP_SIM_IDENTITY, 0, TAG_ANY); if (newvp && newvp->length > 2) { uint16_t len; memcpy(&len, newvp->vp_octets, sizeof(uint16_t)); len = ntohs(len); if (len <= newvp->length - 2 && len <= MAX_STRING_LEN) { ess->keys.identitylen = len; memcpy(ess->keys.identity, newvp->vp_octets + 2, ess->keys.identitylen); } } /* all set, calculate keys! */ eapsim_calculate_keys(&ess->keys); #ifdef EAP_SIM_DEBUG_PRF eapsim_dump_mk(&ess->keys); #endif /* * need to include an AT_MAC attribute so that it will get * calculated. The NONCE_MT and the MAC are both 16 bytes, so * we store the NONCE_MT in the MAC for the encoder, which * will pull it out before it does the operation. */ newvp = paircreate(ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_MAC, 0, PW_TYPE_OCTETS); memcpy(newvp->vp_strvalue, ess->keys.nonce_mt, 16); newvp->length = 16; pairreplace(outvps, newvp); newvp = paircreate(ATTRIBUTE_EAP_SIM_KEY, 0, PW_TYPE_OCTETS); memcpy(newvp->vp_strvalue, ess->keys.K_aut, 16); newvp->length = 16; pairreplace(outvps, newvp); /* the SUBTYPE, set to challenge. */ newvp = paircreate(ATTRIBUTE_EAP_SIM_SUBTYPE, 0, PW_TYPE_INTEGER); newvp->vp_integer = eapsim_challenge; pairreplace(outvps, newvp); return 1; }
int eaptls_success(EAP_HANDLER *handler, int peap_flag) { EAPTLS_PACKET reply; VALUE_PAIR *vp, *vps = NULL; REQUEST *request = handler->request; tls_session_t *tls_session = handler->opaque; reply.code = EAPTLS_SUCCESS; reply.length = TLS_HEADER_LEN; reply.flags = peap_flag; reply.data = NULL; reply.dlen = 0; /* * If there's no session resumption, delete the entry * from the cache. This means either it's disabled * globally for this SSL context, OR we were told to * disable it for this user. * * This also means you can't turn it on just for one * user. */ if ((!tls_session->allow_session_resumption) || (((vp = pairfind(request->config_items, 1127)) != NULL) && (vp->vp_integer == 0))) { SSL_CTX_remove_session(tls_session->ctx, tls_session->ssl->session); tls_session->allow_session_resumption = 0; /* * If we're in a resumed session and it's * not allowed, */ if (SSL_session_reused(tls_session->ssl)) { RDEBUG("FAIL: Forcibly stopping session resumption as it is not allowed."); return eaptls_fail(handler, peap_flag); } /* * Else resumption IS allowed, so we store the * user data in the cache. */ } else if (!SSL_session_reused(tls_session->ssl)) { RDEBUG2("Saving response in the cache"); vp = paircopy2(request->reply->vps, PW_USER_NAME); pairadd(&vps, vp); vp = paircopy2(request->packet->vps, PW_STRIPPED_USER_NAME); pairadd(&vps, vp); if (vps) { SSL_SESSION_set_ex_data(tls_session->ssl->session, eaptls_session_idx, vps); } else { RDEBUG2("WARNING: No information to cache: session caching will be disabled for this session."); SSL_CTX_remove_session(tls_session->ctx, tls_session->ssl->session); } /* * Else the session WAS allowed. Copy the cached * reply. */ } else { vp = SSL_SESSION_get_ex_data(tls_session->ssl->session, eaptls_session_idx); if (!vp) { RDEBUG("WARNING: No information in cached session!"); return eaptls_fail(handler, peap_flag); } else { RDEBUG("Adding cached attributes to the reply:"); debug_pair_list(vp); pairadd(&request->reply->vps, paircopy(vp)); /* * Mark the request as resumed. */ vp = pairmake("EAP-Session-Resumed", "1", T_OP_SET); if (vp) pairadd(&request->packet->vps, vp); } } /* * Call compose AFTER checking for cached data. */ eaptls_compose(handler->eap_ds, &reply); /* * Automatically generate MPPE keying material. */ if (tls_session->prf_label) { eaptls_gen_mppe_keys(&handler->request->reply->vps, tls_session->ssl, tls_session->prf_label); } else { RDEBUG("WARNING: Not adding MPPE keys because there is no PRF label"); } return 1; }
/* * Authenticate a previously sent challenge. */ static int mschapv2_authenticate(void *arg, EAP_HANDLER *handler) { int rcode, ccode; mschapv2_opaque_t *data; EAP_DS *eap_ds = handler->eap_ds; VALUE_PAIR *challenge, *response, *name; rlm_eap_mschapv2_t *inst = (rlm_eap_mschapv2_t *) arg; rad_assert(handler->request != NULL); rad_assert(handler->stage == AUTHENTICATE); data = (mschapv2_opaque_t *) handler->opaque; /* * Sanity check the response. */ if (eap_ds->response->length <= 5) { radlog(L_ERR, "rlm_eap_mschapv2: corrupted data"); return 0; } ccode = eap_ds->response->type.data[0]; switch (data->code) { case PW_EAP_MSCHAPV2_FAILURE: if (ccode == PW_EAP_MSCHAPV2_RESPONSE) { DEBUG2(" rlm_eap_mschapv2: authentication re-try from client after we sent a failure"); break; } /* * if we sent error 648 (password expired) to the client * we might get an MSCHAP-CPW packet here; turn it into a * regular MS-CHAP2-CPW packet and pass it to rlm_mschap * (or proxy it, I guess) */ if (ccode == PW_EAP_MSCHAPV2_CHGPASSWD) { VALUE_PAIR *cpw; int mschap_id = eap_ds->response->type.data[1]; int copied=0,seq=1; DEBUG2(" rlm_eap_mschapv2: password change packet received"); challenge = pairmake("MS-CHAP-Challenge", "0x00", T_OP_EQ); if (!challenge) { radlog(L_ERR, "rlm_eap_mschapv2: out of memory"); return 0; } challenge->length = MSCHAPV2_CHALLENGE_LEN; memcpy(challenge->vp_strvalue, data->challenge, MSCHAPV2_CHALLENGE_LEN); pairadd(&handler->request->packet->vps, challenge); cpw = pairmake("MS-CHAP2-CPW", "", T_OP_EQ); cpw->vp_octets[0] = 7; cpw->vp_octets[1] = mschap_id; memcpy(cpw->vp_octets+2, eap_ds->response->type.data + 520, 66); cpw->length = 68; pairadd(&handler->request->packet->vps, cpw); /* * break the encoded password into VPs (3 of them) */ while (copied < 516) { VALUE_PAIR *nt_enc; int to_copy = 516 - copied; if (to_copy > 243) to_copy = 243; nt_enc = pairmake("MS-CHAP-NT-Enc-PW", "", T_OP_ADD); nt_enc->vp_octets[0] = 6; nt_enc->vp_octets[1] = mschap_id; nt_enc->vp_octets[2] = 0; nt_enc->vp_octets[3] = seq++; memcpy(nt_enc->vp_octets + 4, eap_ds->response->type.data + 4 + copied, to_copy); copied += to_copy; nt_enc->length = 4 + to_copy; pairadd(&handler->request->packet->vps, nt_enc); } DEBUG2(" rlm_eap_mschapv2: built change password packet"); debug_pair_list(handler->request->packet->vps); /* * jump to "authentication" */ goto packet_ready; } /* * we sent a failure and are expecting a failure back */ if (ccode != PW_EAP_MSCHAPV2_FAILURE) { radlog(L_ERR, "rlm_eap_mschapv2: Sent FAILURE expecting FAILURE but got %d", ccode); return 0; } failure: handler->request->options &= ~RAD_REQUEST_OPTION_PROXY_EAP; eap_ds->request->code = PW_EAP_FAILURE; return 1; case PW_EAP_MSCHAPV2_SUCCESS: /* * we sent a success to the client; some clients send a * success back as-per the RFC, some send an ACK. Permit * both, I guess... */ switch (ccode) { case PW_EAP_MSCHAPV2_SUCCESS: eap_ds->request->code = PW_EAP_SUCCESS; pairadd(&handler->request->reply->vps, data->mppe_keys); data->mppe_keys = NULL; /* fall through... */ case PW_EAP_MSCHAPV2_ACK: #ifdef WITH_PROXY /* * It's a success. Don't proxy it. */ handler->request->options &= ~RAD_REQUEST_OPTION_PROXY_EAP; #endif pairadd(&handler->request->reply->vps, data->reply); data->reply = NULL; return 1; } radlog(L_ERR, "rlm_eap_mschapv2: Sent SUCCESS expecting SUCCESS (or ACK) but got %d", ccode); return 0; case PW_EAP_MSCHAPV2_CHALLENGE: if (ccode == PW_EAP_MSCHAPV2_FAILURE) goto failure; /* * we sent a challenge, expecting a response */ if (ccode != PW_EAP_MSCHAPV2_RESPONSE) { radlog(L_ERR, "rlm_eap_mschapv2: Sent CHALLENGE expecting RESPONSE but got %d", ccode); return 0; } /* authentication happens below */ break; default: /* should never happen */ radlog(L_ERR, "rlm_eap_mschapv2: unknown state %d", data->code); return 0; } /* * Ensure that we have at least enough data * to do the following checks. * * EAP header (4), EAP type, MS-CHAP opcode, * MS-CHAP ident, MS-CHAP data length (2), * MS-CHAP value length. */ if (eap_ds->response->length < (4 + 1 + 1 + 1 + 2 + 1)) { radlog(L_ERR, "rlm_eap_mschapv2: Response is too short"); return 0; } /* * The 'value_size' is the size of the response, * which is supposed to be the response (48 * bytes) plus 1 byte of flags at the end. */ if (eap_ds->response->type.data[4] != 49) { radlog(L_ERR, "rlm_eap_mschapv2: Response is of incorrect length %d", eap_ds->response->type.data[4]); return 0; } /* * The MS-Length field is 5 + value_size + length * of name, which is put after the response. */ if (((eap_ds->response->type.data[2] << 8) | eap_ds->response->type.data[3]) < (5 + 49)) { radlog(L_ERR, "rlm_eap_mschapv2: Response contains contradictory length %d %d", (eap_ds->response->type.data[2] << 8) | eap_ds->response->type.data[3], 5 + 49); return 0; } /* * We now know that the user has sent us a response * to the challenge. Let's try to authenticate it. * * We do this by taking the challenge from 'data', * the response from the EAP packet, and creating VALUE_PAIR's * to pass to the 'mschap' module. This is a little wonky, * but it works. */ challenge = pairmake("MS-CHAP-Challenge", "0x00", T_OP_EQ); if (!challenge) { radlog(L_ERR, "rlm_eap_mschapv2: out of memory"); return 0; } challenge->length = MSCHAPV2_CHALLENGE_LEN; memcpy(challenge->vp_strvalue, data->challenge, MSCHAPV2_CHALLENGE_LEN); response = pairmake("MS-CHAP2-Response", "0x00", T_OP_EQ); if (!response) { pairfree(&challenge); radlog(L_ERR, "rlm_eap_mschapv2: out of memory"); return 0; } response->length = MSCHAPV2_RESPONSE_LEN; memcpy(response->vp_strvalue + 2, &eap_ds->response->type.data[5], MSCHAPV2_RESPONSE_LEN - 2); response->vp_strvalue[0] = eap_ds->response->type.data[1]; response->vp_strvalue[1] = eap_ds->response->type.data[5 + MSCHAPV2_RESPONSE_LEN]; name = pairmake("NTLM-User-Name", "", T_OP_EQ); if (!name) { pairfree(&challenge); pairfree(&response); radlog(L_ERR, "rlm_eap_mschapv2: Failed creating NTLM-User-Name: %s", fr_strerror()); return 0; } /* * MS-Length - MS-Value - 5. */ name->length = (((eap_ds->response->type.data[2] << 8) | eap_ds->response->type.data[3]) - eap_ds->response->type.data[4] - 5); if (name->length >= sizeof(name->vp_strvalue)) { name->length = sizeof(name->vp_strvalue) - 1; } memcpy(name->vp_strvalue, &eap_ds->response->type.data[4 + MSCHAPV2_RESPONSE_LEN], name->length); name->vp_strvalue[name->length] = '\0'; /* * Add the pairs to the request, and call the 'mschap' * module. */ pairadd(&handler->request->packet->vps, challenge); pairadd(&handler->request->packet->vps, response); pairadd(&handler->request->packet->vps, name); packet_ready: #ifdef WITH_PROXY /* * If this options is set, then we do NOT authenticate the * user here. Instead, now that we've added the MS-CHAP * attributes to the request, we STOP, and let the outer * tunnel code handle it. * * This means that the outer tunnel code will DELETE the * EAP attributes, and proxy the MS-CHAP attributes to a * home server. */ if (handler->request->options & RAD_REQUEST_OPTION_PROXY_EAP) { char *username = NULL; eap_tunnel_data_t *tunnel; DEBUG2("rlm_eap_mschapv2: cancelling authentication and letting it be proxied"); /* * Set up the callbacks for the tunnel */ tunnel = rad_malloc(sizeof(*tunnel)); memset(tunnel, 0, sizeof(*tunnel)); tunnel->tls_session = arg; tunnel->callback = mschap_postproxy; /* * Associate the callback with the request. */ rcode = request_data_add(handler->request, handler->request->proxy, REQUEST_DATA_EAP_TUNNEL_CALLBACK, tunnel, free); rad_assert(rcode == 0); /* * The State attribute is NOT supposed to * go into the proxied packet, it will confuse * other RADIUS servers, and they will discard * the request. * * The PEAP module will take care of adding * the State attribute back, before passing * the handler & request back into the tunnel. */ pairdelete(&handler->request->packet->vps, PW_STATE, 0, TAG_ANY); /* * Fix the User-Name when proxying, to strip off * the NT Domain, if we're told to, and a User-Name * exists, and there's a \\, meaning an NT-Domain * in the user name, THEN discard the user name. */ if (inst->with_ntdomain_hack && ((challenge = pairfind(handler->request->packet->vps, PW_USER_NAME, 0, TAG_ANY)) != NULL) && ((username = strchr(challenge->vp_strvalue, '\\')) != NULL)) { /* * Wipe out the NT domain. * * FIXME: Put it into MS-CHAP-Domain? */ username++; /* skip the \\ */ memmove(challenge->vp_strvalue, username, strlen(username) + 1); /* include \0 */ challenge->length = strlen(challenge->vp_strvalue); } /* * Remember that in the post-proxy stage, we've got * to do the work below, AFTER the call to MS-CHAP * authentication... */ return 1; } #endif /* * This is a wild & crazy hack. */ rcode = module_authenticate(PW_AUTHTYPE_MS_CHAP, handler->request); /* * Delete MPPE keys & encryption policy. We don't * want these here. */ fix_mppe_keys(handler, data); /* * Take the response from the mschap module, and * return success or failure, depending on the result. */ response = NULL; if (rcode == RLM_MODULE_OK) { pairmove2(&response, &handler->request->reply->vps, PW_MSCHAP2_SUCCESS, VENDORPEC_MICROSOFT, TAG_ANY); data->code = PW_EAP_MSCHAPV2_SUCCESS; } else if (inst->send_error) { pairmove2(&response, &handler->request->reply->vps, PW_MSCHAP_ERROR, VENDORPEC_MICROSOFT, TAG_ANY); if (response) { int n,err,retry; char buf[34]; DEBUG2(" MSCHAP-Error: %s", response->vp_strvalue); /* * Pxarse the new challenge out of the * MS-CHAP-Error, so that if the client * issues a re-try, we will know which * challenge value that they used. */ n = sscanf(response->vp_strvalue, "%*cE=%d R=%d C=%32s", &err, &retry, &buf[0]); if (n == 3) { DEBUG2(" Found new challenge from MS-CHAP-Error: err=%d retry=%d challenge=%s", err, retry, buf); fr_hex2bin(buf, data->challenge, 16); } else { DEBUG2(" Could not parse new challenge from MS-CHAP-Error: %d", n); } } data->code = PW_EAP_MSCHAPV2_FAILURE; } else { eap_ds->request->code = PW_EAP_FAILURE; return 1; } /* * No response, die. */ if (!response) { radlog(L_ERR, "rlm_eap_mschapv2: No MS-CHAPv2-Success or MS-CHAP-Error was found."); return 0; } /* * Compose the response (whatever it is), * and return it to the over-lying EAP module. */ eapmschapv2_compose(handler, response); pairfree(&response); return 1; }
/* * Process the "diameter" contents of the tunneled data. */ int eapttls_process(EAP_HANDLER *handler, tls_session_t *tls_session) { int rcode = PW_AUTHENTICATION_REJECT; REQUEST *fake; VALUE_PAIR *vp; ttls_tunnel_t *t; const uint8_t *data; size_t data_len; REQUEST *request = handler->request; rad_assert(request != NULL); /* * Just look at the buffer directly, without doing * record_minus. */ data_len = tls_session->clean_out.used; tls_session->clean_out.used = 0; data = tls_session->clean_out.data; t = (ttls_tunnel_t *) tls_session->opaque; /* * If there's no data, maybe this is an ACK to an * MS-CHAP2-Success. */ if (data_len == 0) { if (t->authenticated) { RDEBUG("Got ACK, and the user was already authenticated."); return PW_AUTHENTICATION_ACK; } /* else no session, no data, die. */ /* * FIXME: Call SSL_get_error() to see what went * wrong. */ RDEBUG2("SSL_read Error"); return PW_AUTHENTICATION_REJECT; } #ifndef NDEBUG if ((debug_flag > 2) && fr_log_fp) { size_t i; for (i = 0; i < data_len; i++) { if ((i & 0x0f) == 0) fprintf(fr_log_fp, " TTLS tunnel data in %04x: ", (int) i); fprintf(fr_log_fp, "%02x ", data[i]); if ((i & 0x0f) == 0x0f) fprintf(fr_log_fp, "\n"); } if ((data_len & 0x0f) != 0) fprintf(fr_log_fp, "\n"); } #endif if (!diameter_verify(request, data, data_len)) { return PW_AUTHENTICATION_REJECT; } /* * Allocate a fake REQUEST structe. */ fake = request_alloc_fake(request); rad_assert(fake->packet->vps == NULL); /* * Add the tunneled attributes to the fake request. */ fake->packet->vps = diameter2vp(request, tls_session->ssl, data, data_len); if (!fake->packet->vps) { request_free(&fake); return PW_AUTHENTICATION_REJECT; } /* * Tell the request that it's a fake one. */ vp = pairmake("Freeradius-Proxied-To", "127.0.0.1", T_OP_EQ); if (vp) { pairadd(&fake->packet->vps, vp); } if ((debug_flag > 0) && fr_log_fp) { RDEBUG("Got tunneled request"); debug_pair_list(fake->packet->vps); } /* * Update other items in the REQUEST data structure. */ fake->username = pairfind(fake->packet->vps, PW_USER_NAME, 0, TAG_ANY); fake->password = pairfind(fake->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY); /* * No User-Name, try to create one from stored data. */ if (!fake->username) { /* * No User-Name in the stored data, look for * an EAP-Identity, and pull it out of there. */ if (!t->username) { vp = pairfind(fake->packet->vps, PW_EAP_MESSAGE, 0, TAG_ANY); if (vp && (vp->length >= EAP_HEADER_LEN + 2) && (vp->vp_strvalue[0] == PW_EAP_RESPONSE) && (vp->vp_strvalue[EAP_HEADER_LEN] == PW_EAP_IDENTITY) && (vp->vp_strvalue[EAP_HEADER_LEN + 1] != 0)) { /* * Create & remember a User-Name */ t->username = pairmake("User-Name", "", T_OP_EQ); rad_assert(t->username != NULL); memcpy(t->username->vp_strvalue, vp->vp_strvalue + 5, vp->length - 5); t->username->length = vp->length - 5; t->username->vp_strvalue[t->username->length] = 0; RDEBUG("Got tunneled identity of %s", t->username->vp_strvalue); /* * If there's a default EAP type, * set it here. */ if (t->default_eap_type != 0) { RDEBUG("Setting default EAP type for tunneled EAP session."); vp = paircreate(PW_EAP_TYPE, 0); rad_assert(vp != NULL); vp->vp_integer = t->default_eap_type; pairadd(&fake->config_items, vp); } } else { /* * Don't reject the request outright, * as it's permitted to do EAP without * user-name. */ RDEBUG2W("No EAP-Identity found to start EAP conversation."); } } /* else there WAS a t->username */ if (t->username) { vp = paircopy(t->username); pairadd(&fake->packet->vps, vp); fake->username = pairfind(fake->packet->vps, PW_USER_NAME, 0, TAG_ANY); } } /* else the request ALREADY had a User-Name */ /* * Add the State attribute, too, if it exists. */ if (t->state) { vp = paircopy(t->state); if (vp) pairadd(&fake->packet->vps, vp); } /* * If this is set, we copy SOME of the request attributes * from outside of the tunnel to inside of the tunnel. * * We copy ONLY those attributes which do NOT already * exist in the tunneled request. */ if (t->copy_request_to_tunnel) { VALUE_PAIR *copy; for (vp = request->packet->vps; vp != NULL; vp = vp->next) { /* * The attribute is a server-side thingy, * don't copy it. */ if ((vp->da->attr > 255) && (vp->da->vendor == 0)) { continue; } /* * The outside attribute is already in the * tunnel, don't copy it. * * This works for BOTH attributes which * are originally in the tunneled request, * AND attributes which are copied there * from below. */ if (pairfind(fake->packet->vps, vp->da->attr, vp->da->vendor, TAG_ANY)) { continue; } /* * Some attributes are handled specially. */ switch (vp->da->attr) { /* * NEVER copy Message-Authenticator, * EAP-Message, or State. They're * only for outside of the tunnel. */ case PW_USER_NAME: case PW_USER_PASSWORD: case PW_CHAP_PASSWORD: case PW_CHAP_CHALLENGE: case PW_PROXY_STATE: case PW_MESSAGE_AUTHENTICATOR: case PW_EAP_MESSAGE: case PW_STATE: continue; break; /* * By default, copy it over. */ default: break; } /* * Don't copy from the head, we've already * checked it. */ copy = paircopy2(vp, vp->da->attr, vp->da->vendor, TAG_ANY); pairadd(&fake->packet->vps, copy); } } if ((vp = pairfind(request->config_items, PW_VIRTUAL_SERVER, 0, TAG_ANY)) != NULL) { fake->server = vp->vp_strvalue; } else if (t->virtual_server) { fake->server = t->virtual_server; } /* else fake->server == request->server */ if ((debug_flag > 0) && fr_log_fp) { RDEBUG("Sending tunneled request"); debug_pair_list(fake->packet->vps); fprintf(fr_log_fp, "server %s {\n", (fake->server == NULL) ? "" : fake->server); } /* * Call authentication recursively, which will * do PAP, CHAP, MS-CHAP, etc. */ rad_virtual_server(fake); /* * Note that we don't do *anything* with the reply * attributes. */ if ((debug_flag > 0) && fr_log_fp) { fprintf(fr_log_fp, "} # server %s\n", (fake->server == NULL) ? "" : fake->server); RDEBUG("Got tunneled reply code %d", fake->reply->code); debug_pair_list(fake->reply->vps); } /* * Decide what to do with the reply. */ switch (fake->reply->code) { case 0: /* No reply code, must be proxied... */ #ifdef WITH_PROXY vp = pairfind(fake->config_items, PW_PROXY_TO_REALM, 0, TAG_ANY); if (vp) { eap_tunnel_data_t *tunnel; RDEBUG("Tunneled authentication will be proxied to %s", vp->vp_strvalue); /* * Tell the original request that it's going * to be proxied. */ pairmove2(&(request->config_items), &(fake->config_items), PW_PROXY_TO_REALM, 0, TAG_ANY); /* * Seed the proxy packet with the * tunneled request. */ rad_assert(request->proxy == NULL); request->proxy = fake->packet; memset(&request->proxy->src_ipaddr, 0, sizeof(request->proxy->src_ipaddr)); memset(&request->proxy->src_ipaddr, 0, sizeof(request->proxy->src_ipaddr)); request->proxy->src_port = 0; request->proxy->dst_port = 0; fake->packet = NULL; rad_free(&fake->reply); fake->reply = NULL; /* * Set up the callbacks for the tunnel */ tunnel = rad_malloc(sizeof(*tunnel)); memset(tunnel, 0, sizeof(*tunnel)); tunnel->tls_session = tls_session; tunnel->callback = eapttls_postproxy; /* * Associate the callback with the request. */ rcode = request_data_add(request, request->proxy, REQUEST_DATA_EAP_TUNNEL_CALLBACK, tunnel, free); rad_assert(rcode == 0); /* * rlm_eap.c has taken care of associating * the handler with the fake request. * * So we associate the fake request with * this request. */ rcode = request_data_add(request, request->proxy, REQUEST_DATA_EAP_MSCHAP_TUNNEL_CALLBACK, fake, my_request_free); rad_assert(rcode == 0); fake = NULL; /* * Didn't authenticate the packet, but * we're proxying it. */ rcode = PW_STATUS_CLIENT; } else #endif /* WITH_PROXY */ { RDEBUG("No tunneled reply was found for request %d , and the request was not proxied: rejecting the user.", request->number); rcode = PW_AUTHENTICATION_REJECT; } break; default: /* * Returns RLM_MODULE_FOO, and we want to return * PW_FOO */ rcode = process_reply(handler, tls_session, request, fake->reply); switch (rcode) { case RLM_MODULE_REJECT: rcode = PW_AUTHENTICATION_REJECT; break; case RLM_MODULE_HANDLED: rcode = PW_ACCESS_CHALLENGE; break; case RLM_MODULE_OK: rcode = PW_AUTHENTICATION_ACK; break; default: rcode = PW_AUTHENTICATION_REJECT; break; } break; } request_free(&fake); return rcode; }
/* * Do post-proxy processing, */ static int eapttls_postproxy(EAP_HANDLER *handler, void *data) { int rcode; tls_session_t *tls_session = (tls_session_t *) data; REQUEST *fake, *request = handler->request; rad_assert(request != NULL); RDEBUG("Passing reply from proxy back into the tunnel."); /* * If there was a fake request associated with the proxied * request, do more processing of it. */ fake = (REQUEST *) request_data_get(handler->request, handler->request->proxy, REQUEST_DATA_EAP_MSCHAP_TUNNEL_CALLBACK); /* * Do the callback, if it exists, and if it was a success. */ if (fake && handler->request->proxy_reply && (handler->request->proxy_reply->code == PW_AUTHENTICATION_ACK)) { /* * Terrible hacks. */ rad_assert(fake->packet == NULL); fake->packet = request->proxy; fake->packet->src_ipaddr = request->packet->src_ipaddr; request->proxy = NULL; rad_assert(fake->reply == NULL); fake->reply = request->proxy_reply; request->proxy_reply = NULL; if ((debug_flag > 0) && fr_log_fp) { fprintf(fr_log_fp, "server %s {\n", (fake->server == NULL) ? "" : fake->server); } /* * Perform a post-auth stage for the tunneled * session. */ fake->options &= ~RAD_REQUEST_OPTION_PROXY_EAP; rcode = rad_postauth(fake); RDEBUG2("post-auth returns %d", rcode); if ((debug_flag > 0) && fr_log_fp) { fprintf(fr_log_fp, "} # server %s\n", (fake->server == NULL) ? "" : fake->server); RDEBUG("Final reply from tunneled session code %d", fake->reply->code); debug_pair_list(fake->reply->vps); } /* * Terrible hacks. */ request->proxy = fake->packet; fake->packet = NULL; request->proxy_reply = fake->reply; fake->reply = NULL; /* * And we're done with this request. */ switch (rcode) { case RLM_MODULE_FAIL: request_free(&fake); eaptls_fail(handler, 0); return 0; break; default: /* Don't Do Anything */ RDEBUG2("Got reply %d", request->proxy_reply->code); break; } } request_free(&fake); /* robust if fake == NULL */ /* * Process the reply from the home server. */ rcode = process_reply(handler, tls_session, handler->request, handler->request->proxy_reply); /* * The proxy code uses the reply from the home server as * the basis for the reply to the NAS. We don't want that, * so we toss it, after we've had our way with it. */ pairfree(&handler->request->proxy_reply->vps); switch (rcode) { case RLM_MODULE_REJECT: RDEBUG("Reply was rejected"); break; case RLM_MODULE_HANDLED: RDEBUG("Reply was handled"); eaptls_request(handler->eap_ds, tls_session); return 1; case RLM_MODULE_OK: RDEBUG("Reply was OK"); /* * Success: Automatically return MPPE keys. */ return eaptls_success(handler, 0); default: RDEBUG("Reply was unknown."); break; } eaptls_fail(handler, 0); return 0; }