📄 ttls.c
字号:
* NOT 'eap start', so we should check for that.... */ switch (reply->code) { case PW_AUTHENTICATION_ACK: RDEBUG("Got tunneled Access-Accept"); rcode = RLM_MODULE_OK; /* * MS-CHAP2-Success means that we do NOT return * an Access-Accept, but instead tunnel that * attribute to the client, and keep going with * the TTLS session. Once the client accepts * our identity, it will respond with an empty * packet, and we will send EAP-Success. */ vp = NULL; pairmove2(&vp, &reply->vps, PW_MSCHAP2_SUCCESS); if (vp) { RDEBUG("Got MS-CHAP2-Success, tunneling it to the client in a challenge."); rcode = RLM_MODULE_HANDLED; t->authenticated = TRUE; /* * Delete MPPE keys & encryption policy. We don't * want these here. */ pairdelete(&reply->vps, ((311 << 16) | 7)); pairdelete(&reply->vps, ((311 << 16) | 8)); pairdelete(&reply->vps, ((311 << 16) | 16)); pairdelete(&reply->vps, ((311 << 16) | 17)); /* * Use the tunneled reply, but not now. */ if (t->use_tunneled_reply) { t->reply = reply->vps; reply->vps = NULL; } } else { /* no MS-CHAP2-Success */ /* * Can only have EAP-Message if there's * no MS-CHAP2-Success. (FIXME: EAP-MSCHAP?) * * We also do NOT tunnel the EAP-Success * attribute back to the client, as the client * can figure it out, from the non-tunneled * EAP-Success packet. */ pairmove2(&vp, &reply->vps, PW_EAP_MESSAGE); pairfree(&vp); } /* * Handle the ACK, by tunneling any necessary reply * VP's back to the client. */ if (vp) { vp2diameter(request, tls_session, vp); pairfree(&vp); } /* * If we've been told to use the attributes from * the reply, then do so. * * WARNING: This may leak information about the * tunneled user! */ if (t->use_tunneled_reply) { pairdelete(&reply->vps, PW_PROXY_STATE); pairadd(&request->reply->vps, reply->vps); reply->vps = NULL; } break; case PW_AUTHENTICATION_REJECT: RDEBUG("Got tunneled Access-Reject"); rcode = RLM_MODULE_REJECT; break; /* * Handle Access-Challenge, but only if we * send tunneled reply data. This is because * an Access-Challenge means that we MUST tunnel * a Reply-Message to the client. */ case PW_ACCESS_CHALLENGE: RDEBUG("Got tunneled Access-Challenge"); /* * Keep the State attribute, if necessary. * * Get rid of the old State, too. */ pairfree(&t->state); pairmove2(&t->state, &reply->vps, PW_STATE); /* * We should really be a bit smarter about this, * and move over only those attributes which * are relevant to the authentication request, * but that's a lot more work, and this "dumb" * method works in 99.9% of the situations. */ vp = NULL; pairmove2(&vp, &reply->vps, PW_EAP_MESSAGE); /* * There MUST be a Reply-Message in the challenge, * which we tunnel back to the client. * * If there isn't one in the reply VP's, then * we MUST create one, with an empty string as * it's value. */ pairmove2(&vp, &reply->vps, PW_REPLY_MESSAGE); /* * Handle the ACK, by tunneling any necessary reply * VP's back to the client. */ if (vp) { vp2diameter(request, tls_session, vp); pairfree(&vp); } rcode = RLM_MODULE_HANDLED; break; default: RDEBUG("Unknown RADIUS packet type %d: rejecting tunneled user", reply->code); rcode = RLM_MODULE_INVALID; break; } 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; 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->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("sGot 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;}/* * Free a request. */static void my_request_free(void *data){ REQUEST *request = (REQUEST *)data; request_free(&request);}/* * 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; /* * 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: ", 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); fake->password = pairfind(fake->packet->vps, PW_USER_PASSWORD); /* * 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); 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, PW_TYPE_INTEGER); 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. */ RDEBUG2("WARNING! 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); } } /* 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->attribute > 255) && (((vp->attribute >> 16) & 0xffff) == 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->attribute)) { continue; } /* * Some attributes are handled specially. */ switch (vp->attribute) { /* * 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->attribute); pairadd(&fake->packet->vps, copy); } } if ((vp = pairfind(request->config_items, PW_VIRTUAL_SERVER)) != 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_authenticate(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... */ vp = pairfind(fake->config_items, PW_PROXY_TO_REALM); 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); /* * Seed the proxy packet with the * tunneled request. */ rad_assert(request->proxy == NULL); request->proxy = fake->packet; 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 { 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;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -