📄 ttls.c
字号:
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) { pairadd(&request->reply->vps, reply->vps); reply->vps = NULL; } } /* * Handle the ACK, by tunneling any necessary reply * VP's back to the client. */ if (vp) { vp2diameter(tls_session, vp); pairfree(&vp); } break; case PW_AUTHENTICATION_REJECT: DEBUG2(" TTLS: 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: DEBUG2(" TTLS: 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(tls_session, vp); pairfree(&vp); } rcode = RLM_MODULE_HANDLED; break; default: DEBUG2(" TTLS: Unknown RADIUS packet type %d: rejecting tunneled user", reply->code); rcode = RLM_MODULE_REJECT; 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; DEBUG2(" TTLS: 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)) { VALUE_PAIR *vp; REQUEST *request = handler->request; /* * Terrible hacks. */ rad_assert(fake->packet == NULL); fake->packet = request->proxy; request->proxy = NULL; rad_assert(fake->reply == NULL); fake->reply = request->proxy_reply; request->proxy_reply = NULL; /* * Perform a post-auth stage for the tunneled * session. */ fake->options &= ~RAD_REQUEST_OPTION_PROXY_EAP; rcode = rad_postauth(fake); DEBUG2(" POST-AUTH %d", rcode);#ifndef NDEBUG if (debug_flag > 0) { printf(" TTLS: Final reply from tunneled session code %d\n", fake->reply->code); for (vp = fake->reply->vps; vp != NULL; vp = vp->next) { putchar('\t');vp_print(stdout, vp);putchar('\n'); } }#endif /* * 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->eap_ds, 0); return 0; break; default: /* Don't Do Anything */ DEBUG2(" TTLS: 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: DEBUG2(" TTLS: Reply was rejected"); return 0; case RLM_MODULE_HANDLED: DEBUG2(" TTLS: Reply was handled"); eaptls_request(handler->eap_ds, tls_session); return 1; case RLM_MODULE_OK: DEBUG2(" TTLS: Reply was OK"); eaptls_success(handler->eap_ds, 0); eaptls_gen_mppe_keys(&handler->request->reply->vps, tls_session->ssl, "ttls keying material"); return 1; default: DEBUG2(" TTLS: Reply was unknown."); break; } eaptls_fail(handler->eap_ds, 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 err; int rcode = PW_AUTHENTICATION_REJECT; REQUEST *fake; VALUE_PAIR *vp; ttls_tunnel_t *t; const uint8_t *data; unsigned int data_len; char buffer[1024]; REQUEST *request = handler->request; /* * Grab the dirty data, and copy it to our buffer. * * I *really* don't like these 'record_t' things... */ data_len = record_minus(&tls_session->dirty_in, buffer, sizeof(buffer)); data = buffer; /* * Write the data from the dirty buffer (i.e. packet * data) into the buffer which we will give to SSL for * decoding. * * Some of this code COULD technically go into the TLS * module, in eaptls_process(), where it returns EAPTLS_OK. * * Similarly, the writing of data to the SSL context could * go there, too... */ BIO_write(tls_session->into_ssl, buffer, data_len); record_init(&tls_session->clean_out); /* * Read (and decrypt) the tunneled data from the SSL session, * and put it into the decrypted data buffer. */ err = SSL_read(tls_session->ssl, tls_session->clean_out.data, sizeof(tls_session->clean_out.data)); if (err < 0) { /* * FIXME: Call SSL_get_error() to see what went * wrong. */ radlog(L_INFO, "rlm_eap_ttls: SSL_read Error"); return PW_AUTHENTICATION_REJECT; } t = (ttls_tunnel_t *) tls_session->opaque; /* * If there's no data, maybe this is an ACK to an * MS-CHAP2-Success. */ if (err == 0) { if (t->authenticated) { DEBUG2(" TTLS: 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. */ radlog(L_INFO, "rlm_eap_ttls: SSL_read Error"); return PW_AUTHENTICATION_REJECT; } data_len = tls_session->clean_out.used = err; data = tls_session->clean_out.data;#ifndef NDEBUG if (debug_flag > 2) { unsigned int i; for (i = 0; i < data_len; i++) { if ((i & 0x0f) == 0) printf(" TTLS tunnel data in %04x: ", i); printf("%02x ", data[i]); if ((i & 0x0f) == 0x0f) printf("\n"); } if ((data_len & 0x0f) != 0) printf("\n"); }#endif if (!diameter_verify(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(tls_session->ssl, data, data_len); if (!fake->packet->vps) { 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); }#ifndef NDEBUG if (debug_flag > 0) { printf(" TTLS: Got tunneled request\n"); for (vp = fake->packet->vps; vp != NULL; vp = vp->next) { putchar('\t');vp_print(stdout, vp);putchar('\n'); } }#endif /* * Update other items in the REQUEST data structure. */ fake->username = pairfind(fake->packet->vps, PW_USER_NAME); fake->password = pairfind(fake->packet->vps, PW_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->strvalue[0] == PW_EAP_RESPONSE) && (vp->strvalue[EAP_HEADER_LEN] == PW_EAP_IDENTITY) && (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->strvalue, vp->strvalue + 5, vp->length - 5); t->username->length = vp->length - 5; t->username->strvalue[t->username->length] = 0; DEBUG2(" TTLS: Got tunneled identity of %s", t->username->strvalue); /* * If there's a default EAP type, * set it here. */ if (t->default_eap_type != 0) { DEBUG2(" TTLS: Setting default EAP type for tunneled EAP session."); vp = paircreate(PW_EAP_TYPE, PW_TYPE_INTEGER); rad_assert(vp != NULL); vp->lvalue = 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. */ DEBUG2(" rlm_eap_ttls: 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) { DEBUG2(" TTLS: Adding old state with %02x %02x", t->state->strvalue[0], t->state->strvalue[1]); 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); } }#ifndef NDEBUG if (debug_flag > 0) { printf(" TTLS: Sending tunneled request\n"); for (vp = fake->packet->vps; vp != NULL; vp = vp->next) { putchar('\t');vp_print(stdout, vp);putchar('\n'); } }#endif /* * 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. */#ifndef NDEBUG if (debug_flag > 0) { printf(" TTLS: Got tunneled reply RADIUS code %d\n", fake->reply->code); for (vp = fake->reply->vps; vp != NULL; vp = vp->next) { putchar('\t');vp_print(stdout, vp);putchar('\n'); } }#endif /* * 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; DEBUG2(" TTLS: Tunneled authentication will be proxied to %s", 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 { DEBUG2(" TTLS: 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 + -