/*
 *	This hack strips out Cisco's VSA duplicities in lines
 *	(Cisco not implemented VSA's in standard way.
 *
 *	Cisco sends it's VSA attributes with the attribute name *again*
 *	in the string, like:  H323-Attribute = "h323-attribute=value".
 *	This sort of behaviour is nonsense.
 */
static void cisco_vsa_hack(REQUEST *request)
{
	int		vendorcode;
	char		*ptr;
	char		newattr[MAX_STRING_LEN];
	VALUE_PAIR	*vp;
	vp_cursor_t	cursor;
	for (vp = fr_cursor_init(&cursor, &request->packet->vps);
	     vp;
	     vp = fr_cursor_next(&cursor)) {
		vendorcode = vp->da->vendor;
		if (!((vendorcode == 9) || (vendorcode == 6618))) {
			continue; /* not a Cisco or Quintum VSA, continue */
		}

		if (vp->da->type != PW_TYPE_STRING) {
			continue;
		}

		/*
		 *  No weird packing.  Ignore it.
		 */
		ptr = strchr(vp->vp_strvalue, '='); /* find an '=' */
		if (!ptr) {
			continue;
		}

		/*
		 *	Cisco-AVPair's get packed as:
		 *
		 *	Cisco-AVPair = "h323-foo-bar = baz"
		 *	Cisco-AVPair = "h323-foo-bar=baz"
		 *
		 *	which makes sense only if you're a lunatic.
		 *	This code looks for the attribute named inside
		 *	of the string, and if it exists, adds it as a new
		 *	attribute.
		 */
		if (vp->da->attr == 1) {
			char const *p;

			p = vp->vp_strvalue;
			gettoken(&p, newattr, sizeof(newattr), false);

			if (dict_attrbyname(newattr) != NULL) {
				pair_make_request(newattr, ptr + 1, T_OP_EQ);
			}
		} else {	/* h322-foo-bar = "h323-foo-bar = baz" */
			/*
			 *	We strip out the duplicity from the
			 *	value field, we use only the value on
			 *	the right side of the '=' character.
			 */
			fr_pair_value_strcpy(vp, ptr + 1);
		}
	}
}
Example #2
0
/** Parse the MS-SOH response in data and update sohvp
 *
 * Note that sohvp might still have been updated in event of a failure.
 *
 * @param request Current request
 * @param data MS-SOH blob
 * @param data_len length of MS-SOH blob
 *
 * @return
 *	- 0 on success.
 *	- -1 on failure.
 */
int soh_verify(REQUEST *request, uint8_t const *data, unsigned int data_len) {

	VALUE_PAIR *vp;
	eap_soh hdr;
	soh_response resp;
	soh_mode_subheader mode;
	soh_tlv tlv;
	int curr_shid=-1, curr_shid_c=-1, curr_hc=-1;

	rad_assert(request->packet != NULL);

	hdr.tlv_type = soh_pull_be_16(data); data += 2;
	hdr.tlv_len = soh_pull_be_16(data); data += 2;
	hdr.tlv_vendor = soh_pull_be_32(data); data += 4;

	if (hdr.tlv_type != 7 || hdr.tlv_vendor != 0x137) {
		RDEBUG("SoH payload is %i %08x not a ms-vendor packet", hdr.tlv_type, hdr.tlv_vendor);
		return -1;
	}

	hdr.soh_type = soh_pull_be_16(data); data += 2;
	hdr.soh_len = soh_pull_be_16(data); data += 2;
	if (hdr.soh_type != 1) {
		RDEBUG("SoH tlv %04x is not a response", hdr.soh_type);
		return -1;
	}

	/* FIXME: check for sufficient data */
	resp.outer_type = soh_pull_be_16(data); data += 2;
	resp.outer_len = soh_pull_be_16(data); data += 2;
	resp.vendor = soh_pull_be_32(data); data += 4;
	resp.inner_type = soh_pull_be_16(data); data += 2;
	resp.inner_len = soh_pull_be_16(data); data += 2;


	if (resp.outer_type!=7 || resp.vendor != 0x137) {
		RDEBUG("SoH response outer type %i/vendor %08x not recognised", resp.outer_type, resp.vendor);
		return -1;
	}
	switch (resp.inner_type) {
	case 1:
		/* no mode sub-header */
		RDEBUG("SoH without mode subheader");
		break;

	case 2:
		mode.outer_type = soh_pull_be_16(data); data += 2;
		mode.outer_len = soh_pull_be_16(data); data += 2;
		mode.vendor = soh_pull_be_32(data); data += 4;
		memcpy(mode.corrid, data, 24); data += 24;
		mode.intent = data[0];
		mode.content_type = data[1];
		data += 2;

		if (mode.outer_type != 7 || mode.vendor != 0x137 || mode.content_type != 0) {
			RDEBUG3("SoH mode subheader outer type %i/vendor %08x/content type %i invalid", mode.outer_type, mode.vendor, mode.content_type);
			return -1;
		}
		RDEBUG3("SoH with mode subheader");
		break;

	default:
		RDEBUG("SoH invalid inner type %i", resp.inner_type);
		return -1;
	}

	/* subtract off the relevant amount of data */
	if (resp.inner_type==2) {
		data_len = resp.inner_len - 34;
	} else {
		data_len = resp.inner_len;
	}

	/* TLV
	 * MS-SOH 2.2.1
	 * See also 2.2.3
	 *
	 * 1 bit mandatory
	 * 1 bit reserved
	 * 14 bits tlv type
	 * 2 bytes tlv length
	 * N bytes payload
	 *
	 */
	while (data_len >= 4) {
		tlv.tlv_type = soh_pull_be_16(data); data += 2;
		tlv.tlv_len = soh_pull_be_16(data); data += 2;

		data_len -= 4;

		switch (tlv.tlv_type) {
		case 2:
			/* System-Health-Id TLV
			 * MS-SOH 2.2.3.1
			 *
			 * 3 bytes IANA/SMI vendor code
			 * 1 byte component (i.e. within vendor, which SoH component
			 */
			curr_shid = soh_pull_be_24(data);
			curr_shid_c = data[3];
			RDEBUG2("SoH System-Health-ID vendor %08x component=%i", curr_shid, curr_shid_c);
			break;

		case 7:
			/* Vendor-Specific packet
			 * MS-SOH 2.2.3.3
			 *
			 * 4 bytes vendor, supposedly ignored by NAP
			 * N bytes payload; for Microsoft component#0 this is the MS TV stuff
			 */
			if (curr_shid==0x137 && curr_shid_c==0) {
				RDEBUG2("SoH MS type-value payload");
				eapsoh_mstlv(request, data + 4, tlv.tlv_len - 4);
			} else {
				RDEBUG2("SoH unhandled vendor-specific TLV %08x/component=%i %i bytes payload",
					curr_shid, curr_shid_c, tlv.tlv_len);
			}
			break;

		case 8:
			/* Health-Class
			 * MS-SOH 2.2.3.5.6
			 *
			 * 1 byte integer
			 */
			RDEBUG2("SoH Health-Class %i", data[0]);
			curr_hc = data[0];
			break;

		case 9:
			/* Software-Version
			 * MS-SOH 2.2.3.5.7
			 *
			 * 1 byte integer
			 */
			RDEBUG2("SoH Software-Version %i", data[0]);
			break;

		case 11:
			/* Health-Class status
			 * MS-SOH 2.2.3.5.9
			 *
			 * variable data; for the MS System Health vendor, these are 4-byte
			 * integers which are a really, really dumb format:
			 *
			 *  28 bits ignore
			 *  1 bit - 1==product snoozed
			 *  1 bit - 1==microsoft product
			 *  1 bit - 1==product up-to-date
			 *  1 bit - 1==product enabled
			 */
			RDEBUG2("SoH Health-Class-Status - current shid=%08x component=%i", curr_shid, curr_shid_c);

			if (curr_shid == 0x137 && curr_shid_c == 128) {
				char const *s, *t;
				uint32_t hcstatus = soh_pull_be_32(data);

				RDEBUG2("SoH Health-Class-Status microsoft DWORD=%08x", hcstatus);

				vp = pair_make_request("SoH-MS-Windows-Health-Status", NULL, T_OP_EQ);
				if (!vp) return 0;

				switch (curr_hc) {
				case 4:
					/* security updates */
					s = "security-updates";
					switch (hcstatus) {
					case 0xff0005:
						fr_pair_value_sprintf(vp, "%s ok all-installed", s);
						break;

					case 0xff0006:
						fr_pair_value_sprintf(vp, "%s warn some-missing", s);
						break;

					case 0xff0008:
						fr_pair_value_sprintf(vp, "%s warn never-started", s);
						break;

					case 0xc0ff000c:
						fr_pair_value_sprintf(vp, "%s error no-wsus-srv", s);
						break;

					case 0xc0ff000d:
						fr_pair_value_sprintf(vp, "%s error no-wsus-clid", s);
						break;

					case 0xc0ff000e:
						fr_pair_value_sprintf(vp, "%s warn wsus-disabled", s);
						break;

					case 0xc0ff000f:
						fr_pair_value_sprintf(vp, "%s error comm-failure", s);
						break;

					case 0xc0ff0010:
						fr_pair_value_sprintf(vp, "%s warn needs-reboot", s);
						break;

					default:
						fr_pair_value_sprintf(vp, "%s error %08x", s, hcstatus);
						break;
					}
					break;

				case 3:
					/* auto updates */
					s = "auto-updates";
					switch (hcstatus) {
					case 1:
						fr_pair_value_sprintf(vp, "%s warn disabled", s);
						break;

					case 2:
						fr_pair_value_sprintf(vp, "%s ok action=check-only", s);
						break;

					case 3:
						fr_pair_value_sprintf(vp, "%s ok action=download", s);
						break;

					case 4:
						fr_pair_value_sprintf(vp, "%s ok action=install", s);
						break;

					case 5:
						fr_pair_value_sprintf(vp, "%s warn unconfigured", s);
						break;

					case 0xc0ff0003:
						fr_pair_value_sprintf(vp, "%s warn service-down", s);
						break;

					case 0xc0ff0018:
						fr_pair_value_sprintf(vp, "%s warn never-started", s);
						break;

					default:
						fr_pair_value_sprintf(vp, "%s error %08x", s, hcstatus);
						break;
					}
					break;

				default:
					/* other - firewall, antivirus, antispyware */
					s = healthclass2str(curr_hc);
					if (s) {
						/* bah. this is vile. stupid microsoft
						 */
						if (hcstatus & 0xff000000) {
							/* top octet non-zero means an error
							 * FIXME: is this always correct? MS-WSH 2.2.8 is unclear
							 */
							t = clientstatus2str(hcstatus);
							if (t) {
								fr_pair_value_sprintf(vp, "%s error %s", s, t);
							} else {
								fr_pair_value_sprintf(vp, "%s error %08x", s, hcstatus);
							}
						} else {
							fr_pair_value_sprintf(vp,
									"%s ok snoozed=%i microsoft=%i up2date=%i enabled=%i",
									s,
									hcstatus & 0x8 ? 1 : 0,
									hcstatus & 0x4 ? 1 : 0,
									hcstatus & 0x2 ? 1 : 0,
									hcstatus & 0x1 ? 1 : 0
									);
						}
					} else {
						fr_pair_value_sprintf(vp, "%i unknown %08x", curr_hc, hcstatus);
					}
					break;
				}
			} else {
				vp = pair_make_request("SoH-MS-Health-Other", NULL, T_OP_EQ);
				if (!vp) return 0;

				/* FIXME: what to do with the payload? */
				fr_pair_value_sprintf(vp, "%08x/%i ?", curr_shid, curr_shid_c);
			}
			break;

		default:
			RDEBUG("SoH Unknown TLV %i len=%i", tlv.tlv_type, tlv.tlv_len);
			break;
		}

		data += tlv.tlv_len;
		data_len -= tlv.tlv_len;
	}

	return 0;
}
Example #3
0
/** Parses the MS-SOH type/value (note: NOT type/length/value) data and update the sohvp list
 *
 * See section 2.2.4 of MS-SOH. Because there's no "length" field we CANNOT just skip
 * unknown types; we need to know their length ahead of time. Therefore, we abort
 * if we find an unknown type. Note that sohvp may still have been modified in the
 * failure case.
 *
 * @param request Current request
 * @param p binary blob
 * @param data_len length of blob
 * @return
 *	- 0 on success.
 *	- -1 on failure.
 */
static int eapsoh_mstlv(REQUEST *request, uint8_t const *p, unsigned int data_len)
{
	VALUE_PAIR *vp;
	uint8_t c;
	int t;
	char *q;

	while (data_len > 0) {
		c = *p++;
		data_len--;

		switch (c) {
		case 1:
			/* MS-Machine-Inventory-Packet
			 * MS-SOH section 2.2.4.1
			 */
			if (data_len < 18) {
				RDEBUG("insufficient data for MS-Machine-Inventory-Packet");
				return 0;
			}
			data_len -= 18;

			vp = pair_make_request("SoH-MS-Machine-OS-vendor", "Microsoft", T_OP_EQ);
			if (!vp) return 0;

			vp = pair_make_request("SoH-MS-Machine-OS-version", NULL, T_OP_EQ);
			if (!vp) return 0;

			vp->vp_integer = soh_pull_be_32(p); p+=4;

			vp = pair_make_request("SoH-MS-Machine-OS-release", NULL, T_OP_EQ);
			if (!vp) return 0;

			vp->vp_integer = soh_pull_be_32(p); p+=4;

			vp = pair_make_request("SoH-MS-Machine-OS-build", NULL, T_OP_EQ);
			if (!vp) return 0;

			vp->vp_integer = soh_pull_be_32(p); p+=4;

			vp = pair_make_request("SoH-MS-Machine-SP-version", NULL, T_OP_EQ);
			if (!vp) return 0;

			vp->vp_integer = soh_pull_be_16(p); p+=2;

			vp = pair_make_request("SoH-MS-Machine-SP-release", NULL, T_OP_EQ);
			if (!vp) return 0;

			vp->vp_integer = soh_pull_be_16(p); p+=2;

			vp = pair_make_request("SoH-MS-Machine-Processor", NULL, T_OP_EQ);
			if (!vp) return 0;

			vp->vp_integer = soh_pull_be_16(p); p+=2;
			break;

		case 2:
			/* MS-Quarantine-State - FIXME: currently unhandled
			 * MS-SOH 2.2.4.1
			 *
			 * 1 byte reserved
			 * 1 byte flags
			 * 8 bytes NT Time field (100-nanosec since 1 Jan 1601)
			 * 2 byte urilen
			 * N bytes uri
			 */
			p += 10;
			t = soh_pull_be_16(p);	/* t == uri len */
			p += 2;
			p += t;
			data_len -= 12 + t;
			break;

		case 3:
			/* MS-Packet-Info
			 * MS-SOH 2.2.4.3
			 */
			RDEBUG3("SoH MS-Packet-Info %s vers=%i", *p & 0x10 ? "request" : "response", *p & 0xf);
			p++;
			data_len--;
			break;

		case 4:
			/* MS-SystemGenerated-Ids - FIXME: currently unhandled
			 * MS-SOH 2.2.4.4
			 *
			 * 2 byte length
			 * N bytes (3 bytes IANA enterprise# + 1 byte component id#)
			 */
			t = soh_pull_be_16(p);
			p += 2;
			p += t;
			data_len -= 2 + t;
			break;

		case 5:
			/* MS-MachineName
			 * MS-SOH 2.2.4.5
			 *
			 * 1 byte namelen
			 * N bytes name
			 */
			t = soh_pull_be_16(p);
			p += 2;

			vp = pair_make_request("SoH-MS-Machine-Name", NULL, T_OP_EQ);
			if (!vp) return 0;

			vp->vp_strvalue = q = talloc_array(vp, char, t);
			vp->type = VT_DATA;

			memcpy(q, p, t);
			q[t] = 0;

			p += t;
			data_len -= 2 + t;
			break;

		case 6:
			/* MS-CorrelationId
			 * MS-SOH 2.2.4.6
			 *
			 * 24 bytes opaque binary which we might, in future, have
			 * to echo back to the client in a final SoHR
			 */
			vp = pair_make_request("SoH-MS-Correlation-Id", NULL, T_OP_EQ);
			if (!vp) return 0;

			fr_pair_value_memcpy(vp, p, 24);
			p += 24;
			data_len -= 24;
			break;

		case 7:
			/* MS-Installed-Shvs - FIXME: currently unhandled
			 * MS-SOH 2.2.4.7
			 *
			 * 2 bytes length
			 * N bytes (3 bytes IANA enterprise# + 1 byte component id#)
			 */
			t = soh_pull_be_16(p);
			p += 2;
			p += t;
			data_len -= 2 + t;
			break;

		case 8:
			/* MS-Machine-Inventory-Ex
			 * MS-SOH 2.2.4.8
			 *
			 * 4 bytes reserved
			 * 1 byte product type (client=1 domain_controller=2 server=3)
			 */
			p += 4;
			vp = pair_make_request("SoH-MS-Machine-Role", NULL, T_OP_EQ);
			if (!vp) return 0;

			vp->vp_integer = *p;
			p++;
			data_len -= 5;
			break;

		default:
			RDEBUG("SoH Unknown MS TV %i stopping", c);
			return 0;
		}
	}
	return 1;
}
Example #4
0
/*
 *	Process the "diameter" contents of the tunneled data.
 */
PW_CODE eapttls_process(eap_handler_t *handler, tls_session_t *tls_session)
{
	PW_CODE code = PW_CODE_ACCESS_REJECT;
	rlm_rcode_t rcode;
	REQUEST *fake;
	VALUE_PAIR *vp;
	ttls_tunnel_t *t;
	uint8_t const *data;
	size_t data_len;
	REQUEST *request = handler->request;
	chbind_packet_t *chbind;

	/*
	 *	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_CODE_ACCESS_ACCEPT;
		} /* else no session, no data, die. */

		/*
		 *	FIXME: Call SSL_get_error() to see what went
		 *	wrong.
		 */
		RDEBUG2("SSL_read Error");
		return PW_CODE_ACCESS_REJECT;
	}

#ifndef NDEBUG
	if ((rad_debug_lvl > 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_CODE_ACCESS_REJECT;
	}

	/*
	 *	Allocate a fake REQUEST structure.
	 */
	fake = request_alloc_fake(request);

	rad_assert(!fake->packet->vps);

	/*
	 *	Add the tunneled attributes to the fake request.
	 */
	fake->packet->vps = diameter2vp(request, fake, tls_session->ssl, data, data_len);
	if (!fake->packet->vps) {
		talloc_free(fake);
		return PW_CODE_ACCESS_REJECT;
	}

	/*
	 *	Tell the request that it's a fake one.
	 */
	pair_make_request("Freeradius-Proxied-To", "127.0.0.1", T_OP_EQ);

	RDEBUG("Got tunneled request");
	rdebug_pair_list(L_DBG_LVL_1, request, fake->packet->vps, NULL);

	/*
	 *	Update other items in the REQUEST data structure.
	 */
	fake->username = fr_pair_find_by_num(fake->packet->vps, PW_USER_NAME, 0, TAG_ANY);
	fake->password = fr_pair_find_by_num(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 = fr_pair_find_by_num(fake->packet->vps, PW_EAP_MESSAGE, 0, TAG_ANY);
			if (vp &&
			    (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 = fr_pair_make(t, NULL, "User-Name", NULL, T_OP_EQ);
				rad_assert(t->username != NULL);

				fr_pair_value_bstrncpy(t->username, vp->vp_octets + 5, vp->vp_length - 5);

				RDEBUG("Got tunneled identity of %s",
				       t->username->vp_strvalue);

				/*
				 *	If there's a default EAP type,
				 *	set it here.
				 */
				if (t->default_method != 0) {
					RDEBUG("Setting default EAP type for tunneled EAP session");
					vp = fr_pair_afrom_num(fake, PW_EAP_TYPE, 0);
					rad_assert(vp != NULL);
					vp->vp_integer = t->default_method;
					fr_pair_add(&fake->config, vp);
				}

			} else {
				/*
				 *	Don't reject the request outright,
				 *	as it's permitted to do EAP without
				 *	user-name.
				 */
				RWDEBUG2("No EAP-Identity found to start EAP conversation");
			}
		} /* else there WAS a t->username */

		if (t->username) {
			vp = fr_pair_list_copy(fake->packet, t->username);
			fr_pair_add(&fake->packet->vps, vp);
			fake->username = fr_pair_find_by_num(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 = fr_pair_list_copy(fake->packet, t->state);
		if (vp) fr_pair_add(&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;
		vp_cursor_t cursor;

		for (vp = fr_cursor_init(&cursor, &request->packet->vps); vp; vp = fr_cursor_next(&cursor)) {
			/*
			 *	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 (fr_pair_find_by_da(fake->packet->vps, vp->da, 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;

			/*
			 *	By default, copy it over.
			 */
			default:
				break;
			}

			/*
			 *	Don't copy from the head, we've already
			 *	checked it.
			 */
			copy = fr_pair_list_copy_by_num(fake->packet, vp, vp->da->attr, vp->da->vendor, TAG_ANY);
			fr_pair_add(&fake->packet->vps, copy);
		}
	}

	if ((vp = fr_pair_find_by_num(request->config, 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 ((rad_debug_lvl > 0) && fr_log_fp) {
		RDEBUG("Sending tunneled request");
	}

	/*
	 *	Process channel binding.
	 */
	chbind = eap_chbind_vp2packet(fake, fake->packet->vps);
	if (chbind) {
		PW_CODE chbind_code;
		CHBIND_REQ *req = talloc_zero(fake, CHBIND_REQ);

		RDEBUG("received chbind request");
		req->request = chbind;
		if (fake->username) {
			req->username = fake->username;
		} else {
			req->username = NULL;
		}
		chbind_code = chbind_process(request, req);

		/* encapsulate response here */
		if (req->response) {
			RDEBUG("sending chbind response");
			fr_pair_add(&fake->reply->vps,
				eap_chbind_packet2vp(fake, req->response));
		} else {
			RDEBUG("no chbind response");
		}

		/* clean up chbind req */
		talloc_free(req);

		if (chbind_code != PW_CODE_ACCESS_ACCEPT) {
			return chbind_code;
		}
	}

	/*
	 *	Call authentication recursively, which will
	 *	do PAP, CHAP, MS-CHAP, etc.
	 */
	rad_virtual_server(fake);

	/*
	 *	Decide what to do with the reply.
	 */
	switch (fake->reply->code) {
	case 0:			/* No reply code, must be proxied... */
#ifdef WITH_PROXY
		vp = fr_pair_find_by_num(fake->config, 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.
			 */
			fr_pair_list_move_by_num(request, &request->config,
				  &fake->config,
				  PW_PROXY_TO_REALM, 0, TAG_ANY);

			/*
			 *	Seed the proxy packet with the
			 *	tunneled request.
			 */
			rad_assert(!request->proxy);
			request->proxy = talloc_steal(request, 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 = talloc_zero(request, eap_tunnel_data_t);
			tunnel->tls_session = tls_session;
			tunnel->callback = eapttls_postproxy;

			/*
			 *	Associate the callback with the request.
			 */
			code = request_data_add(request, request->proxy, REQUEST_DATA_EAP_TUNNEL_CALLBACK,
						tunnel, false);
			rad_assert(code == 0);

			/*
			 *	rlm_eap.c has taken care of associating
			 *	the handler with the fake request.
			 *
			 *	So we associate the fake request with
			 *	this request.
			 */
			code = request_data_add(request, request->proxy, REQUEST_DATA_EAP_MSCHAP_TUNNEL_CALLBACK,
						fake, true);
			rad_assert(code == 0);
			fake = NULL;

			/*
			 *	Didn't authenticate the packet, but
			 *	we're proxying it.
			 */
			code = PW_CODE_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);
			code = PW_CODE_ACCESS_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:
			code = PW_CODE_ACCESS_REJECT;
			break;

		case RLM_MODULE_HANDLED:
			code = PW_CODE_ACCESS_CHALLENGE;
			break;

		case RLM_MODULE_OK:
			code = PW_CODE_ACCESS_ACCEPT;
			break;

		default:
			code = PW_CODE_ACCESS_REJECT;
			break;
		}
		break;
	}

	talloc_free(fake);

	return code;
}
Example #5
0
static rlm_rcode_t rlm_sql_process_groups(rlm_sql_t *inst, REQUEST *request, rlm_sql_handle_t **handle,
					  sql_fall_through_t *do_fall_through)
{
	rlm_rcode_t		rcode = RLM_MODULE_NOOP;
	VALUE_PAIR		*check_tmp = NULL, *reply_tmp = NULL, *sql_group = NULL;
	rlm_sql_grouplist_t	*head = NULL, *entry = NULL;

	char			*expanded = NULL;
	int			rows;

	rad_assert(request->packet != NULL);

	if (!inst->config->groupmemb_query) {
		RWARN("Cannot do check groups when group_membership_query is not set");

	do_nothing:
		*do_fall_through = FALL_THROUGH_DEFAULT;

		/*
		 *	Didn't add group attributes or allocate
		 *	memory, so don't do anything else.
		 */
		return RLM_MODULE_NOTFOUND;
	}

	/*
	 *	Get the list of groups this user is a member of
	 */
	rows = sql_get_grouplist(inst, handle, request, &head);
	if (rows < 0) {
		REDEBUG("Error retrieving group list");

		return RLM_MODULE_FAIL;
	}
	if (rows == 0) {
		RDEBUG2("User not found in any groups");
		goto do_nothing;
	}
	rad_assert(head);

	RDEBUG2("User found in the group table");

	/*
	 *	Add the Sql-Group attribute to the request list so we know
	 *	which group we're retrieving attributes for
	 */
	sql_group = pair_make_request(inst->group_da->name, NULL, T_OP_EQ);
	if (!sql_group) {
		REDEBUG("Error creating %s attribute", inst->group_da->name);
		rcode = RLM_MODULE_FAIL;
		goto finish;
	}

	entry = head;
	do {
	next:
		rad_assert(entry != NULL);
		fr_pair_value_strcpy(sql_group, entry->name);

		if (inst->config->authorize_group_check_query) {
			vp_cursor_t cursor;
			VALUE_PAIR *vp;

			/*
			 *	Expand the group query
			 */
			if (radius_axlat(&expanded, request, inst->config->authorize_group_check_query,
					 inst->sql_escape_func, *handle) < 0) {
				REDEBUG("Error generating query");
				rcode = RLM_MODULE_FAIL;
				goto finish;
			}

			rows = sql_getvpdata(request, inst, request, handle, &check_tmp, expanded);
			TALLOC_FREE(expanded);
			if (rows < 0) {
				REDEBUG("Error retrieving check pairs for group %s", entry->name);
				rcode = RLM_MODULE_FAIL;
				goto finish;
			}

			/*
			 *	If we got check rows we need to process them before we decide to
			 *	process the reply rows
			 */
			if ((rows > 0) &&
			    (paircompare(request, request->packet->vps, check_tmp, &request->reply->vps) != 0)) {
				fr_pair_list_free(&check_tmp);
				entry = entry->next;

				if (!entry) break;

				goto next;	/* != continue */
			}

			RDEBUG2("Group \"%s\": Conditional check items matched", entry->name);
			rcode = RLM_MODULE_OK;

			RDEBUG2("Group \"%s\": Merging assignment check items", entry->name);
			RINDENT();
			for (vp = fr_cursor_init(&cursor, &check_tmp);
			     vp;
			     vp = fr_cursor_next(&cursor)) {
			 	if (!fr_assignment_op[vp->op]) continue;

			 	rdebug_pair(L_DBG_LVL_2, request, vp, NULL);
			}
			REXDENT();
			radius_pairmove(request, &request->config, check_tmp, true);
			check_tmp = NULL;
		}

		if (inst->config->authorize_group_reply_query) {
			/*
			 *	Now get the reply pairs since the paircompare matched
			 */
			if (radius_axlat(&expanded, request, inst->config->authorize_group_reply_query,
					 inst->sql_escape_func, *handle) < 0) {
				REDEBUG("Error generating query");
				rcode = RLM_MODULE_FAIL;
				goto finish;
			}

			rows = sql_getvpdata(request->reply, inst, request, handle, &reply_tmp, expanded);
			TALLOC_FREE(expanded);
			if (rows < 0) {
				REDEBUG("Error retrieving reply pairs for group %s", entry->name);
				rcode = RLM_MODULE_FAIL;
				goto finish;
			}
			*do_fall_through = fall_through(reply_tmp);

			RDEBUG2("Group \"%s\": Merging reply items", entry->name);
			rcode = RLM_MODULE_OK;

			rdebug_pair_list(L_DBG_LVL_2, request, reply_tmp, NULL);

			radius_pairmove(request, &request->reply->vps, reply_tmp, true);
			reply_tmp = NULL;
		/*
		 *	If there's no reply query configured, then we assume
		 *	FALL_THROUGH_NO, which is the same as the users file if you
		 *	had no reply attributes.
		 */
		} else {
			*do_fall_through = FALL_THROUGH_DEFAULT;
		}

		entry = entry->next;
	} while (entry != NULL && (*do_fall_through == FALL_THROUGH_YES));

finish:
	talloc_free(head);
	fr_pair_delete_by_num(&request->packet->vps, 0, inst->group_da->attr, TAG_ANY);

	return rcode;
}