📄 ttls.c
字号:
/* * rlm_eap_ttls.c contains the interfaces that are called from eap * * Version: $Id: ttls.c,v 1.17 2004/04/19 20:21:19 aland Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Copyright 2003 Alan DeKok <aland@freeradius.org> */#include "eap_tls.h"#include "eap_ttls.h"/* * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | AVP Code | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * |V M r r r r r r| AVP Length | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Vendor-ID (opt) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Data ... * +-+-+-+-+-+-+-+-+ *//* * Verify that the diameter packet is valid. */static int diameter_verify(const uint8_t *data, unsigned int data_len){ uint32_t attr; uint32_t length; unsigned int offset; unsigned int data_left = data_len; while (data_left > 0) { rad_assert(data_left <= data_len); memcpy(&attr, data, sizeof(attr)); data += 4; attr = ntohl(attr); if (attr > 255) { DEBUG2(" rlm_eap_ttls: Non-RADIUS attribute in tunneled authentication is not supported"); return 0; } memcpy(&length, data , sizeof(length)); data += 4; length = ntohl(length); /* * A "vendor" flag, with a vendor ID of zero, * is equivalent to no vendor. This is stupid. */ offset = 8; if ((length & (1 << 31)) != 0) { int attribute; uint32_t vendor; DICT_ATTR *da; memcpy(&vendor, data, sizeof(vendor)); vendor = ntohl(vendor); if (vendor > 65535) { DEBUG2(" rlm_eap_ttls: Vendor codes larger than 65535 are not supported"); return 0; } attribute = (vendor << 16) | attr; da = dict_attrbyvalue(attribute); /* * SHOULD check ((length & (1 << 30)) != 0) * for the mandatory bit. */ if (!da) { DEBUG2(" rlm_eap_ttls: Fatal! Vendor %u, Attribute %u was not found in our dictionary. ", vendor, attr); return 0; } data += 4; /* skip the vendor field */ offset += 4; /* offset to value field */ } /* * Ignore the M bit. We support all RADIUS attributes... */ /* * Get the length. If it's too big, die. */ length &= 0x00ffffff; /* * Too short or too long is bad. * * FIXME: EAP-Message */ if (length < offset) { DEBUG2(" rlm_eap_ttls: Tunneled attribute %d is too short (%d)to contain anything useful.", attr, length); return 0; } if (length > (MAX_STRING_LEN + 8)) { DEBUG2(" rlm_eap_ttls: Tunneled attribute %d is too long (%d) to pack into a RADIUS attribute.", attr, length); return 0; } if (length > data_left) { DEBUG2(" rlm_eap_ttls: Tunneled attribute %d is longer than room left in the packet (%d > %d).", attr, length, data_left); return 0; } /* * Check for broken implementations, which don't * pad the AVP to a 4-octet boundary. */ if (data_left == length) break; /* * The length does NOT include the padding, so * we've got to account for it here by rounding up * to the nearest 4-byte boundary. */ length += 0x03; length &= ~0x03; /* * If the rest of the diameter packet is larger than * this attribute, continue. * * Otherwise, if the attribute over-flows the end * of the packet, die. */ if (data_left < length) { DEBUG2(" rlm_eap_ttls: ERROR! Diameter attribute overflows packet!"); return 0; } /* * Check again for equality, now that we're padded * length to a multiple of 4 octets. */ if (data_left == length) break; /* * data_left > length, continue. */ data_left -= length; data += length - offset; } /* * We got this far. It looks OK. */ return 1;}/* * Convert diameter attributes to our VALUE_PAIR's */static VALUE_PAIR *diameter2vp(SSL *ssl, const uint8_t *data, unsigned int data_len){ uint32_t attr; uint32_t length; unsigned int offset; int size; unsigned int data_left = data_len; VALUE_PAIR *first = NULL; VALUE_PAIR **last = &first; VALUE_PAIR *vp; while (data_left > 0) { rad_assert(data_left <= data_len); memcpy(&attr, data, sizeof(attr)); data += 4; attr = ntohl(attr); memcpy(&length, data, sizeof(length)); data += 4; length = ntohl(length); /* * Ignore the M bit. We support all RADIUS attributes... */ /* * A "vendor" flag, with a vendor ID of zero, * is equivalent to no vendor. This is stupid. */ offset = 8; if ((length & (1 << 31)) != 0) { uint32_t vendor; memcpy(&vendor, data, sizeof(vendor)); vendor = ntohl(vendor); attr |= (vendor << 16); data += 4; /* skip the vendor field, it's zero */ offset += 4; /* offset to value field */ } /* * Get the length. */ length &= 0x00ffffff; /* * diameter code + length, and it must fit in * a VALUE_PAIR. */ rad_assert(length <= (offset + MAX_STRING_LEN)); /* * Get the size of the value portion of the * attribute. */ size = length - offset; /* * Create it. */ vp = paircreate(attr, PW_TYPE_OCTETS); if (!vp) { DEBUG2(" rlm_eap_ttls: Failure in creating VP"); pairfree(&first); return NULL; } /* * If it's a type from our dictionary, then * we need to put the data in a relevant place. */ switch (vp->type) { case PW_TYPE_INTEGER: case PW_TYPE_DATE: if (size != vp->length) { DEBUG2(" rlm_eap_ttls: Invalid length attribute %d", attr); pairfree(&first); return NULL; } memcpy(&vp->lvalue, data, vp->length); /* * Stored in host byte order: change it. */ vp->lvalue = ntohl(vp->lvalue); break; case PW_TYPE_IPADDR: if (size != vp->length) { DEBUG2(" rlm_eap_ttls: Invalid length attribute %d", attr); pairfree(&first); return NULL; } memcpy(&vp->lvalue, data, vp->length); /* * Stored in network byte order: don't change it. */ break; /* * String, octet, etc. Copy the data from the * value field over verbatim. * * FIXME: Ipv6 attributes ? * */ default: vp->length = size; memcpy(vp->strvalue, data, vp->length); break; } /* * User-Password is NUL padded to a multiple * of 16 bytes. Let's chop it to something * more reasonable. * * NOTE: This means that the User-Password * attribute CANNOT EVER have embedded zeros in it! */ switch (vp->attribute) { case PW_USER_PASSWORD: rad_assert(vp->length <= 128); /* RFC requirements */ /* * If the password is exactly 16 octets, * it won't be zero-terminated. */ vp->strvalue[vp->length] = '\0'; vp->length = strlen(vp->strvalue); break; /* * Ensure that the client is using the * correct challenge. This weirdness is * to protect against against replay * attacks, where anyone observing the * CHAP exchange could pose as that user, * by simply choosing to use the same * challenge. * * By using a challenge based on * information from the current session, * we can guarantee that the client is * not *choosing* a challenge. * * We're a little forgiving in that we * have loose checks on the length, and * we do NOT check the Id (first octet of * the response to the challenge) * * But if the client gets the challenge correct, * we're not too worried about the Id. */ case PW_CHAP_CHALLENGE: case PW_MSCHAP_CHALLENGE: if ((vp->length < 8) || (vp->length > 16)) { DEBUG2(" TTLS: Tunneled challenge has invalid length"); pairfree(&first); return NULL; } else { int i; uint8_t challenge[16]; eapttls_gen_challenge(ssl, challenge, sizeof(challenge)); for (i = 0; i < vp->length; i++) { if (challenge[i] != vp->strvalue[i]) { DEBUG2(" TTLS: Tunneled challenge is incorrect"); pairfree(&first); return NULL; } } } break; default: break; } /* switch over checking/re-writing of attributes. */ /* * Update the list. */ *last = vp; last = &(vp->next); /* * Catch non-aligned attributes. */ if (data_left == length) break; /* * The length does NOT include the padding, so * we've got to account for it here by rounding up * to the nearest 4-byte boundary. */ length += 0x03; length &= ~0x03; rad_assert(data_left >= length); data_left -= length; data += length - offset; /* already updated */ } /* * We got this far. It looks OK. */ return first;}/* * Convert VALUE_PAIR's to diameter attributes, and write them * to an SSL session. * * The ONLY VALUE_PAIR's which may be passed to this function * are ones which can go inside of a RADIUS (i.e. diameter) * packet. So no server-configuration attributes, or the like. */static int vp2diameter(tls_session_t *tls_session, VALUE_PAIR *first){ /* * RADIUS packets are no more than 4k in size, so if * we've got more than 4k of data to write, it's very * bad. */ uint8_t buffer[4096]; uint8_t *p; uint32_t attr; uint32_t length; uint32_t vendor; size_t total; VALUE_PAIR *vp; p = buffer; total = 0; for (vp = first; vp != NULL; vp = vp->next) { /* * Too much data: die. */ if ((total + vp->length + 12) >= sizeof(buffer)) { DEBUG2(" TTLS output buffer is full!"); return 0; } /* * Hmm... we don't group multiple EAP-Messages * together. Maybe we should... */ /* * Length is no more than 253, due to RADIUS * issues. */ length = vp->length; vendor = (vp->attribute >> 16) & 0xffff; if (vendor != 0) { attr = vp->attribute & 0xffff; length |= (1 << 31); } else { attr = vp->attribute; } /* * Hmm... set the M bit for all attributes? */ length |= (1 << 30); attr = ntohl(attr); memcpy(p, &attr, sizeof(attr)); p += 4; total += 4; length += 8; /* includes 8 bytes of attr & length */ if (vendor != 0) { length += 4; /* include 4 bytes of vendor */ length = ntohl(length); memcpy(p, &length, sizeof(length)); p += 4; total += 4; vendor = ntohl(vendor); memcpy(p, &vendor, sizeof(vendor)); p += 4; total += 4; } else { length = ntohl(length); memcpy(p, &length, sizeof(length)); p += 4; total += 4; } switch (vp->type) { case PW_TYPE_INTEGER: case PW_TYPE_DATE: attr = ntohl(vp->lvalue); /* stored in host order */ memcpy(p, &attr, sizeof(attr)); length = 4; break; case PW_TYPE_IPADDR: attr = vp->lvalue; /* stored in network order */ memcpy(p, &attr, sizeof(attr)); length = 4; break; case PW_TYPE_STRING: case PW_TYPE_OCTETS: default: memcpy(p, vp->strvalue, vp->length); length = vp->length; break; } /* * Skip to the end of the data. */ p += length; total += length; /* * Align the data to a multiple of 4 bytes. */ if ((total & 0x03) != 0) { unsigned int i; length = 4 - (total & 0x03); for (i = 0; i < length; i++) { *p = '\0'; p++; total++; } } } /* loop over the VP's to write. */ /* * Write the data in the buffer to the SSL session. */ if (total > 0) {#ifndef NDEBUG unsigned int i; if (debug_flag > 2) { for (i = 0; i < total; i++) { if ((i & 0x0f) == 0) printf(" TTLS tunnel data out %04x: ", i); printf("%02x ", buffer[i]); if ((i & 0x0f) == 0x0f) printf("\n"); } if ((total & 0x0f) != 0) printf("\n"); }#endif record_plus(&tls_session->clean_in, buffer, total); /* * FIXME: Check the return code. */ tls_handshake_send(tls_session); record_init(&tls_session->clean_in); } /* * Everything's OK. */ return 1;}/* * Use a reply packet to determine what to do. */static int process_reply(EAP_HANDLER *handler, tls_session_t *tls_session, REQUEST *request, RADIUS_PACKET *reply){ int rcode = RLM_MODULE_REJECT; VALUE_PAIR *vp; ttls_tunnel_t *t = tls_session->opaque; handler = handler; /* -Wunused */ /* * If the response packet was Access-Accept, then * we're OK. If not, die horribly. * * FIXME: Take MS-CHAP2-Success attribute, and * tunnel it back to the client, to authenticate * ourselves to the client. * * FIXME: If we have an Access-Challenge, then * the Reply-Message is tunneled back to the client. * * FIXME: If we have an EAP-Message, then that message * must be tunneled back to the client. * * FIXME: If we have an Access-Challenge with a State * attribute, then do we tunnel that to the client, or * keep track of it ourselves? * * FIXME: EAP-Messages can only start with 'identity', * NOT 'eap start', so we should check for that.... */ switch (reply->code) { case PW_AUTHENTICATION_ACK: DEBUG2(" TTLS: 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) {#if 1 /* * FIXME: Tunneling MS-CHAP2-Success causes * the only client we have access to, to die. * * We don't want that... */ pairfree(&vp);#else DEBUG2(" TTLS: Got MS-CHAP2-Success, tunneling it to the client in a challenge."); rcode = RLM_MODULE_HANDLED; t->authenticated = TRUE;#endif } 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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -