/*
 *	Initialize a radclient data structure and add it to
 *	the global linked list.
 */
static int radclient_init(TALLOC_CTX *ctx, rc_file_pair_t *files)
{
	FILE *packets, *filters = NULL;

	vp_cursor_t cursor;
	VALUE_PAIR *vp;
	rc_request_t *request;
	bool packets_done = false;
	uint64_t num = 0;

	assert(files->packets != NULL);

	/*
	 *	Determine where to read the VP's from.
	 */
	if (strcmp(files->packets, "-") != 0) {
		packets = fopen(files->packets, "r");
		if (!packets) {
			ERROR("Error opening %s: %s", files->packets, strerror(errno));
			return 0;
		}

		/*
		 *	Read in the pairs representing the expected response.
		 */
		if (files->filters) {
			filters = fopen(files->filters, "r");
			if (!filters) {
				ERROR("Error opening %s: %s", files->filters, strerror(errno));
				fclose(packets);
				return 0;
			}
		}
	} else {
		packets = stdin;
	}


	/*
	 *	Loop until the file is done.
	 */
	do {
		/*
		 *	Allocate it.
		 */
		request = talloc_zero(ctx, rc_request_t);
		if (!request) {
			ERROR("Out of memory");
			goto error;
		}
		talloc_set_destructor(request, _rc_request_free);

		request->packet = rad_alloc(request, 1);
		if (!request->packet) {
			ERROR("Out of memory");
			goto error;
		}

#ifdef WITH_TCP
		request->packet->src_ipaddr = client_ipaddr;
		request->packet->src_port = client_port;
		request->packet->dst_ipaddr = server_ipaddr;
		request->packet->dst_port = server_port;
		request->packet->proto = ipproto;
#endif

		request->files = files;
		request->packet->id = -1; /* allocate when sending */
		request->num = num++;

		/*
		 *	Read the request VP's.
		 */
		if (readvp2(&request->packet->vps, request->packet, packets, &packets_done) < 0) {
			ERROR("Error parsing \"%s\"", files->packets);
			goto error;
		}

		fr_cursor_init(&cursor, &request->filter);
		vp = fr_cursor_next_by_num(&cursor, PW_PACKET_TYPE, 0, TAG_ANY);
		if (vp) {
			fr_cursor_remove(&cursor);
			request->packet_code = vp->vp_integer;
			talloc_free(vp);
		} else {
			request->packet_code = packet_code; /* Use the default set on the command line */
		}

		/*
		 *	Read in filter VP's.
		 */
		if (filters) {
			bool filters_done;

			if (readvp2(&request->filter, request, filters, &filters_done) < 0) {
				ERROR("Error parsing \"%s\"", files->filters);
				goto error;
			}

			if (!request->filter) {
				goto error;
			}

			if (filters_done && !packets_done) {
				ERROR("Differing number of packets/filters in %s:%s "
				      "(too many requests))", files->packets, files->filters);
				goto error;
			}

			if (!filters_done && packets_done) {
				ERROR("Differing number of packets/filters in %s:%s "
				      "(too many filters))", files->packets, files->filters);
				goto error;
			}

			fr_cursor_init(&cursor, &request->filter);
			vp = fr_cursor_next_by_num(&cursor, PW_PACKET_TYPE, 0, TAG_ANY);
			if (vp) {
				fr_cursor_remove(&cursor);
				request->filter_code = vp->vp_integer;
				talloc_free(vp);
			}

			/*
			 *	xlat expansions aren't supported here
			 */
			for (vp = fr_cursor_init(&cursor, &request->filter);
			     vp;
			     vp = fr_cursor_next(&cursor)) {
				if (vp->type == VT_XLAT) {
					vp->type = VT_DATA;
					vp->vp_strvalue = vp->value.xlat;
				}
			}

			/*
			 *	This allows efficient list comparisons later
			 */
			pairsort(&request->filter, attrtagcmp);
		}

		/*
		 *	Determine the response code from the request (if not already set)
		 */
		if (!request->filter_code) {
			switch (request->packet_code) {
			case PW_CODE_AUTHENTICATION_REQUEST:
				request->filter_code = PW_CODE_AUTHENTICATION_ACK;
				break;

			case PW_CODE_ACCOUNTING_REQUEST:
				request->filter_code = PW_CODE_ACCOUNTING_RESPONSE;
				break;

			case PW_CODE_COA_REQUEST:
				request->filter_code = PW_CODE_COA_ACK;
				break;

			case PW_CODE_DISCONNECT_REQUEST:
				request->filter_code = PW_CODE_DISCONNECT_ACK;
				break;

			default:
				break;
			}
		}

		/*
		 *	Keep a copy of the the User-Password attribute.
		 */
		if ((vp = pairfind(request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY)) != NULL) {
			strlcpy(request->password, vp->vp_strvalue,
				sizeof(request->password));
		/*
		 *	Otherwise keep a copy of the CHAP-Password attribute.
		 */
		} else if ((vp = pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY)) != NULL) {
			strlcpy(request->password, vp->vp_strvalue,
				sizeof(request->password));

		} else if ((vp = pairfind(request->packet->vps, PW_MSCHAP_PASSWORD, 0, TAG_ANY)) != NULL) {
			strlcpy(request->password, vp->vp_strvalue,
				sizeof(request->password));
		} else {
			request->password[0] = '\0';
		}

		/*
		 *	Fix up Digest-Attributes issues
		 */
		for (vp = fr_cursor_init(&cursor, &request->packet->vps);
		     vp;
		     vp = fr_cursor_next(&cursor)) {
			/*
			 *	Double quoted strings get marked up as xlat expansions,
			 *	but we don't support that in request.
			 */
			if (vp->type == VT_XLAT) {
				vp->vp_strvalue = vp->value.xlat;
				vp->value.xlat = NULL;
				vp->type = VT_DATA;
			}

			if (!vp->da->vendor) switch (vp->da->attr) {
			default:
				break;

				/*
				 *	Allow it to set the packet type in
				 *	the attributes read from the file.
				 */
			case PW_PACKET_TYPE:
				request->packet->code = vp->vp_integer;
				break;

			case PW_PACKET_DST_PORT:
				request->packet->dst_port = (vp->vp_integer & 0xffff);
				break;

			case PW_PACKET_DST_IP_ADDRESS:
				request->packet->dst_ipaddr.af = AF_INET;
				request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
				break;

			case PW_PACKET_DST_IPV6_ADDRESS:
				request->packet->dst_ipaddr.af = AF_INET6;
				request->packet->dst_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr;
				break;

			case PW_PACKET_SRC_PORT:
				request->packet->src_port = (vp->vp_integer & 0xffff);
				break;

			case PW_PACKET_SRC_IP_ADDRESS:
				request->packet->src_ipaddr.af = AF_INET;
				request->packet->src_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
				break;

			case PW_PACKET_SRC_IPV6_ADDRESS:
				request->packet->src_ipaddr.af = AF_INET6;
				request->packet->src_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr;
				break;

			case PW_DIGEST_REALM:
			case PW_DIGEST_NONCE:
			case PW_DIGEST_METHOD:
			case PW_DIGEST_URI:
			case PW_DIGEST_QOP:
			case PW_DIGEST_ALGORITHM:
			case PW_DIGEST_BODY_DIGEST:
			case PW_DIGEST_CNONCE:
			case PW_DIGEST_NONCE_COUNT:
			case PW_DIGEST_USER_NAME:
				/* overlapping! */
				{
					DICT_ATTR const *da;
					uint8_t *p, *q;

					p = talloc_array(vp, uint8_t, vp->length + 2);

					memcpy(p + 2, vp->vp_octets, vp->length);
					p[0] = vp->da->attr - PW_DIGEST_REALM + 1;
					vp->length += 2;
					p[1] = vp->length;

					da = dict_attrbyvalue(PW_DIGEST_ATTRIBUTES, 0);
					if (!da) {
						ERROR("Out of memory");
						goto error;
					}
					vp->da = da;

					/*
					 *	Re-do pairmemsteal ourselves,
					 *	because we play games with
					 *	vp->da, and pairmemsteal goes
					 *	to GREAT lengths to sanitize
					 *	and fix and change and
					 *	double-check the various
					 *	fields.
					 */
					memcpy(&q, &vp->vp_octets, sizeof(q));
					talloc_free(q);

					vp->vp_octets = talloc_steal(vp, p);
					vp->type = VT_DATA;

					VERIFY_VP(vp);
				}

				break;
			}
		} /* loop over the VP's we read in */

		/*
		 *	Automatically set the port if we don't have a global
		 *	or packet specific one.
		 */
		if ((server_port == 0) && (request->packet->dst_port == 0)) {
			radclient_get_port(request->packet->code, &request->packet->dst_port);
		}

		/*
		 *	Add it to the tail of the list.
		 */
		if (!request_head) {
			assert(rc_request_tail == NULL);
			request_head = request;
			request->prev = NULL;
		} else {
			assert(rc_request_tail->next == NULL);
			rc_request_tail->next = request;
			request->prev = rc_request_tail;
		}
		rc_request_tail = request;
		request->next = NULL;

	} while (!packets_done); /* loop until the file is done. */

	if (packets != stdin) fclose(packets);
	if (filters) fclose(filters);

	/*
	 *	And we're done.
	 */
	return 1;

error:
	talloc_free(request);

	if (packets != stdin) fclose(packets);
	if (filters) fclose(filters);

	return 0;
}
Example #2
0
static REQUEST *request_setup(FILE *fp)
{
	VALUE_PAIR	*vp;
	REQUEST		*request;
	vp_cursor_t	cursor;
	struct timeval	now;

	/*
	 *	Create and initialize the new request.
	 */
	request = request_alloc(NULL);
	gettimeofday(&now, NULL);
	request->timestamp = now;

	request->packet = fr_radius_alloc(request, false);
	if (!request->packet) {
		ERROR("No memory");
		talloc_free(request);
		return NULL;
	}
	request->packet->timestamp = now;

	request->reply = fr_radius_alloc(request, false);
	if (!request->reply) {
		ERROR("No memory");
		talloc_free(request);
		return NULL;
	}

	request->listener = listen_alloc(request);
	request->client = client_alloc(request);

	request->number = 0;

	request->master_state = REQUEST_ACTIVE;
	request->child_state = REQUEST_RUNNING;
	request->handle = NULL;
	request->server = talloc_typed_strdup(request, "default");

	request->root = &main_config;

	/*
	 *	Read packet from fp
	 */
	if (fr_pair_list_afrom_file(request->packet, &request->packet->vps, fp, &filedone) < 0) {
		fr_perror("unittest");
		talloc_free(request);
		return NULL;
	}

	/*
	 *	Set the defaults for IPs, etc.
	 */
	request->packet->code = PW_CODE_ACCESS_REQUEST;

	request->packet->src_ipaddr.af = AF_INET;
	request->packet->src_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_LOOPBACK);
	request->packet->src_port = 18120;

	request->packet->dst_ipaddr.af = AF_INET;
	request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_LOOPBACK);
	request->packet->dst_port = 1812;

	/*
	 *	Copied from radclient
	 */
#if 1
	/*
	 *	Fix up Digest-Attributes issues
	 */
	for (vp = fr_cursor_init(&cursor, &request->packet->vps);
	     vp;
	     vp = fr_cursor_next(&cursor)) {
		/*
		 *	Double quoted strings get marked up as xlat expansions,
		 *	but we don't support that here.
		 */
		if (vp->type == VT_XLAT) {
			vp->vp_strvalue = vp->xlat;
			vp->xlat = NULL;
			vp->type = VT_DATA;
		}

		if (!vp->da->vendor) switch (vp->da->attr) {
		default:
			break;

			/*
			 *	Allow it to set the packet type in
			 *	the attributes read from the file.
			 */
		case PW_PACKET_TYPE:
			request->packet->code = vp->vp_integer;
			break;

		case PW_PACKET_DST_PORT:
			request->packet->dst_port = (vp->vp_integer & 0xffff);
			break;

		case PW_PACKET_DST_IP_ADDRESS:
			request->packet->dst_ipaddr.af = AF_INET;
			request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
			request->packet->dst_ipaddr.prefix = 32;
			break;

		case PW_PACKET_DST_IPV6_ADDRESS:
			request->packet->dst_ipaddr.af = AF_INET6;
			request->packet->dst_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr;
			request->packet->dst_ipaddr.prefix = 128;
			break;

		case PW_PACKET_SRC_PORT:
			request->packet->src_port = (vp->vp_integer & 0xffff);
			break;

		case PW_PACKET_SRC_IP_ADDRESS:
			request->packet->src_ipaddr.af = AF_INET;
			request->packet->src_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
			request->packet->src_ipaddr.prefix = 32;
			break;

		case PW_PACKET_SRC_IPV6_ADDRESS:
			request->packet->src_ipaddr.af = AF_INET6;
			request->packet->src_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr;
			request->packet->src_ipaddr.prefix = 128;
			break;

		case PW_CHAP_PASSWORD: {
			int i, already_hex = 0;

			/*
			 *	If it's 17 octets, it *might* be already encoded.
			 *	Or, it might just be a 17-character password (maybe UTF-8)
			 *	Check it for non-printable characters.  The odds of ALL
			 *	of the characters being 32..255 is (1-7/8)^17, or (1/8)^17,
			 *	or 1/(2^51), which is pretty much zero.
			 */
			if (vp->vp_length == 17) {
				for (i = 0; i < 17; i++) {
					if (vp->vp_octets[i] < 32) {
						already_hex = 1;
						break;
					}
				}
			}

			/*
			 *	Allow the user to specify ASCII or hex CHAP-Password
			 */
			if (!already_hex) {
				uint8_t *p;
				size_t len, len2;

				len = len2 = vp->vp_length;
				if (len2 < 17) len2 = 17;

				p = talloc_zero_array(vp, uint8_t, len2);

				memcpy(p, vp->vp_strvalue, len);

				fr_radius_encode_chap_password(p, request->packet, fr_rand() & 0xff, vp);
				vp->vp_octets = p;
				vp->vp_length = 17;
			}
		}
			break;

		case PW_DIGEST_REALM:
		case PW_DIGEST_NONCE:
		case PW_DIGEST_METHOD:
		case PW_DIGEST_URI:
		case PW_DIGEST_QOP:
		case PW_DIGEST_ALGORITHM:
		case PW_DIGEST_BODY_DIGEST:
		case PW_DIGEST_CNONCE:
		case PW_DIGEST_NONCE_COUNT:
		case PW_DIGEST_USER_NAME:
			/* overlapping! */
		{
			fr_dict_attr_t const *da;
			uint8_t *p, *q;

			p = talloc_array(vp, uint8_t, vp->vp_length + 2);

			memcpy(p + 2, vp->vp_octets, vp->vp_length);
			p[0] = vp->da->attr - PW_DIGEST_REALM + 1;
			vp->vp_length += 2;
			p[1] = vp->vp_length;

			da = fr_dict_attr_by_num(NULL, 0, PW_DIGEST_ATTRIBUTES);
			rad_assert(da != NULL);
			vp->da = da;

			/*
			 *	Re-do fr_pair_value_memsteal ourselves,
			 *	because we play games with
			 *	vp->da, and fr_pair_value_memsteal goes
			 *	to GREAT lengths to sanitize
			 *	and fix and change and
			 *	double-check the various
			 *	fields.
			 */
			memcpy(&q, &vp->vp_octets, sizeof(q));
			talloc_free(q);

			vp->vp_octets = talloc_steal(vp, p);
			vp->type = VT_DATA;

			VERIFY_VP(vp);
		}

		break;
		}
	} /* loop over the VP's we read in */
#endif

	if (rad_debug_lvl) {
		for (vp = fr_cursor_init(&cursor, &request->packet->vps);
		     vp;
		     vp = fr_cursor_next(&cursor)) {
			/*
			 *	Take this opportunity to verify all the VALUE_PAIRs are still valid.
			 */
			if (!talloc_get_type(vp, VALUE_PAIR)) {
				ERROR("Expected VALUE_PAIR pointer got \"%s\"", talloc_get_name(vp));

				fr_log_talloc_report(vp);
				rad_assert(0);
			}

			fr_pair_fprint(fr_log_fp, vp);
		}
		fflush(fr_log_fp);
	}

	/*
	 *	FIXME: set IPs, etc.
	 */
	request->packet->code = PW_CODE_ACCESS_REQUEST;

	request->packet->src_ipaddr.af = AF_INET;
	request->packet->src_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_LOOPBACK);
	request->packet->src_port = 18120;

	request->packet->dst_ipaddr.af = AF_INET;
	request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_LOOPBACK);
	request->packet->dst_port = 1812;

	/*
	 *	Build the reply template from the request.
	 */
	request->reply->sockfd = request->packet->sockfd;
	request->reply->dst_ipaddr = request->packet->src_ipaddr;
	request->reply->src_ipaddr = request->packet->dst_ipaddr;
	request->reply->dst_port = request->packet->src_port;
	request->reply->src_port = request->packet->dst_port;
	request->reply->id = request->packet->id;
	request->reply->code = 0; /* UNKNOWN code */
	memcpy(request->reply->vector, request->packet->vector,
	       sizeof(request->reply->vector));
	request->reply->vps = NULL;
	request->reply->data = NULL;
	request->reply->data_len = 0;

	/*
	 *	Debugging
	 */
	request->log.lvl = rad_debug_lvl;
	request->log.func = vradlog_request;

	request->username = fr_pair_find_by_num(request->packet->vps, 0, PW_USER_NAME, TAG_ANY);
	request->password = fr_pair_find_by_num(request->packet->vps, 0, PW_USER_PASSWORD, TAG_ANY);

	return request;
}
static int sendrecv_eap(RADIUS_PACKET *rep)
{
	RADIUS_PACKET *req = NULL;
	VALUE_PAIR *vp, *vpnext;
	int tried_eap_md5 = 0;

	if (!rep) return -1;

	/*
	 *	Keep a copy of the the User-Password attribute.
	 */
	if ((vp = pairfind(rep->vps, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY)) != NULL) {
		strlcpy(password, vp->vp_strvalue, sizeof(password));

	} else 	if ((vp = pairfind(rep->vps, PW_USER_PASSWORD, 0, TAG_ANY)) != NULL) {
		strlcpy(password, vp->vp_strvalue, sizeof(password));
		/*
		 *	Otherwise keep a copy of the CHAP-Password attribute.
		 */
	} else if ((vp = pairfind(rep->vps, PW_CHAP_PASSWORD, 0, TAG_ANY)) != NULL) {
		strlcpy(password, vp->vp_strvalue, sizeof(password));
	} else {
		*password = '******';
	}

 again:
	rep->id++;

	/*
	 * if there are EAP types, encode them into an EAP-Message
	 *
	 */
	map_eap_methods(rep);

	/*
	 *  Fix up Digest-Attributes issues
	 */
	for (vp = rep->vps; vp != NULL; vp = vp->next) {
		switch (vp->da->attr) {
		default:
			break;

		case PW_DIGEST_REALM:
		case PW_DIGEST_NONCE:
		case PW_DIGEST_METHOD:
		case PW_DIGEST_URI:
		case PW_DIGEST_QOP:
		case PW_DIGEST_ALGORITHM:
		case PW_DIGEST_BODY_DIGEST:
		case PW_DIGEST_CNONCE:
		case PW_DIGEST_NONCE_COUNT:
		case PW_DIGEST_USER_NAME:
		/* overlapping! */
		{
			DICT_ATTR const *da;
			uint8_t *p, *q;

			p = talloc_array(vp, uint8_t, vp->length + 2);

			memcpy(p + 2, vp->vp_octets, vp->length);
			p[0] = vp->da->attr - PW_DIGEST_REALM + 1;
			vp->length += 2;
			p[1] = vp->length;

			da = dict_attrbyvalue(PW_DIGEST_ATTRIBUTES, 0);
			vp->da = da;

			/*
			 *	Re-do pairmemsteal ourselves,
			 *	because we play games with
			 *	vp->da, and pairmemsteal goes
			 *	to GREAT lengths to sanitize
			 *	and fix and change and
			 *	double-check the various
			 *	fields.
			 */
			memcpy(&q, &vp->vp_octets, sizeof(q));
			talloc_free(q);

			vp->vp_octets = talloc_steal(vp, p);
			vp->type = VT_DATA;

			VERIFY_VP(vp);
		}
			break;
		}
	}

	/*
	 *	If we've already sent a packet, free up the old
	 *	one, and ensure that the next packet has a unique
	 *	ID and authentication vector.
	 */
	if (rep->data) {
		talloc_free(rep->data);
		rep->data = NULL;
	}

	fr_md5_calc(rep->vector, rep->vector, sizeof(rep->vector));

	if (*password != '\0') {
		if ((vp = pairfind(rep->vps, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY)) != NULL) {
			pairstrcpy(vp, password);

		} else if ((vp = pairfind(rep->vps, PW_USER_PASSWORD, 0, TAG_ANY)) != NULL) {
			pairstrcpy(vp, password);

		} else if ((vp = pairfind(rep->vps, PW_CHAP_PASSWORD, 0, TAG_ANY)) != NULL) {
			pairstrcpy(vp, password);

			uint8_t *p;
			p = talloc_zero_array(vp, uint8_t, 17);
			rad_chap_encode(rep, p, rep->id, vp);
			pairmemsteal(vp, p);
		}
	} /* there WAS a password */

	/* send the response, wait for the next request */
	send_packet(rep, &req);
	if (!req) {
		ERROR("Failed getting response (EAP-Request from server)");
		return -1;
	}

	/* okay got back the packet, go and decode the EAP-Message. */
	unmap_eap_methods(req);

	debug_packet(req, R_RECV);

	/* now look for the code type. */
	for (vp = req->vps; vp != NULL; vp = vpnext) {
		vpnext = vp->next;

		switch (vp->da->attr) {
		default:
			break;

		case PW_EAP_TYPE_BASE + PW_EAP_MD5:
			if (respond_eap_md5(req, rep) && tried_eap_md5 < 3) {
				tried_eap_md5++;
				goto again;
			}
			break;

		case PW_EAP_TYPE_BASE + PW_EAP_SIM:
			if (respond_eap_sim(req, rep)) {
				goto again;
			}
			break;
		}
	}

	return 1;
}
Example #4
0
/*
 *	Initialize a radclient data structure and add it to
 *	the global linked list.
 */
static int radclient_init(TALLOC_CTX *ctx, rc_file_pair_t *files)
{
	FILE *packets, *filters = NULL;

	vp_cursor_t cursor;
	VALUE_PAIR *vp;
	rc_request_t *request;
	bool packets_done = false;
	uint64_t num = 0;

	assert(files->packets != NULL);

	/*
	 *	Determine where to read the VP's from.
	 */
	if (strcmp(files->packets, "-") != 0) {
		packets = fopen(files->packets, "r");
		if (!packets) {
			ERROR("Error opening %s: %s", files->packets, strerror(errno));
			return 0;
		}

		/*
		 *	Read in the pairs representing the expected response.
		 */
		if (files->filters) {
			filters = fopen(files->filters, "r");
			if (!filters) {
				ERROR("Error opening %s: %s", files->filters, strerror(errno));
				fclose(packets);
				return 0;
			}
		}
	} else {
		packets = stdin;
	}


	/*
	 *	Loop until the file is done.
	 */
	do {
		/*
		 *	Allocate it.
		 */
		request = talloc_zero(ctx, rc_request_t);
		if (!request) {
			ERROR("Out of memory");
			goto error;
		}

		request->packet = fr_radius_alloc(request, true);
		if (!request->packet) {
			ERROR("Out of memory");
			goto error;
		}

#ifdef WITH_TCP
		request->packet->src_ipaddr = client_ipaddr;
		request->packet->src_port = client_port;
		request->packet->dst_ipaddr = server_ipaddr;
		request->packet->dst_port = server_port;
		request->packet->proto = ipproto;
#endif

		request->files = files;
		request->packet->id = -1; /* allocate when sending */
		request->num = num++;

		/*
		 *	Read the request VP's.
		 */
		if (fr_pair_list_afrom_file(request->packet, &request->packet->vps, packets, &packets_done) < 0) {
			char const *input;

			if ((files->packets[0] == '-') && (files->packets[1] == '\0')) {
				input = "stdin";
			} else {
				input = files->packets;
			}

			REDEBUG("Error parsing \"%s\"", input);
			goto error;
		}

		/*
		 *	Skip empty entries
		 */
		if (!request->packet->vps) {
			talloc_free(request);
			continue;
		}

		/*
		 *	Read in filter VP's.
		 */
		if (filters) {
			bool filters_done;

			if (fr_pair_list_afrom_file(request, &request->filter, filters, &filters_done) < 0) {
				REDEBUG("Error parsing \"%s\"", files->filters);
				goto error;
			}

			if (filters_done && !packets_done) {
				REDEBUG("Differing number of packets/filters in %s:%s "
				        "(too many requests))", files->packets, files->filters);
				goto error;
			}

			if (!filters_done && packets_done) {
				REDEBUG("Differing number of packets/filters in %s:%s "
				        "(too many filters))", files->packets, files->filters);
				goto error;
			}

			/*
			 *	xlat expansions aren't supported here
			 */
			for (vp = fr_cursor_init(&cursor, &request->filter);
			     vp;
			     vp = fr_cursor_next(&cursor)) {
				if (vp->type == VT_XLAT) {
					vp->type = VT_DATA;
					vp->vp_strvalue = vp->xlat;
					vp->vp_length = talloc_array_length(vp->vp_strvalue) - 1;
				}

				if (vp->da->vendor == 0 ) switch (vp->da->attr) {
				case PW_RESPONSE_PACKET_TYPE:
				case PW_PACKET_TYPE:
					fr_cursor_remove(&cursor);	/* so we don't break the filter */
					request->filter_code = vp->vp_integer;
					talloc_free(vp);

				default:
					break;
				}
			}

			/*
			 *	This allows efficient list comparisons later
			 */
			fr_pair_list_sort(&request->filter, fr_pair_cmp_by_da_tag);
		}

		/*
		 *	Process special attributes
		 */
		for (vp = fr_cursor_init(&cursor, &request->packet->vps);
		     vp;
		     vp = fr_cursor_next(&cursor)) {
			/*
			 *	Double quoted strings get marked up as xlat expansions,
			 *	but we don't support that in request.
			 */
			if (vp->type == VT_XLAT) {
				vp->type = VT_DATA;
				vp->vp_strvalue = vp->xlat;
				vp->vp_length = talloc_array_length(vp->vp_strvalue) - 1;
			}

			if (!vp->da->vendor) switch (vp->da->attr) {
			default:
				break;

			/*
			 *	Allow it to set the packet type in
			 *	the attributes read from the file.
			 */
			case PW_PACKET_TYPE:
				request->packet->code = vp->vp_integer;
				break;

			case PW_RESPONSE_PACKET_TYPE:
				request->filter_code = vp->vp_integer;
				break;

			case PW_PACKET_DST_PORT:
				request->packet->dst_port = (vp->vp_integer & 0xffff);
				break;

			case PW_PACKET_DST_IP_ADDRESS:
				request->packet->dst_ipaddr.af = AF_INET;
				request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
				request->packet->dst_ipaddr.prefix = 32;
				break;

			case PW_PACKET_DST_IPV6_ADDRESS:
				request->packet->dst_ipaddr.af = AF_INET6;
				request->packet->dst_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr;
				request->packet->dst_ipaddr.prefix = 128;
				break;

			case PW_PACKET_SRC_PORT:
				if ((vp->vp_integer < 1024) ||
				    (vp->vp_integer > 65535)) {
					ERROR("Invalid value '%u' for Packet-Src-Port", vp->vp_integer);
					goto error;
				}
				request->packet->src_port = (vp->vp_integer & 0xffff);
				break;

			case PW_PACKET_SRC_IP_ADDRESS:
				request->packet->src_ipaddr.af = AF_INET;
				request->packet->src_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
				request->packet->src_ipaddr.prefix = 32;
				break;

			case PW_PACKET_SRC_IPV6_ADDRESS:
				request->packet->src_ipaddr.af = AF_INET6;
				request->packet->src_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr;
				request->packet->src_ipaddr.prefix = 128;
				break;

			case PW_DIGEST_REALM:
			case PW_DIGEST_NONCE:
			case PW_DIGEST_METHOD:
			case PW_DIGEST_URI:
			case PW_DIGEST_QOP:
			case PW_DIGEST_ALGORITHM:
			case PW_DIGEST_BODY_DIGEST:
			case PW_DIGEST_CNONCE:
			case PW_DIGEST_NONCE_COUNT:
			case PW_DIGEST_USER_NAME:
			/* overlapping! */
			{
				fr_dict_attr_t const *da;
				uint8_t *p, *q;

				p = talloc_array(vp, uint8_t, vp->vp_length + 2);

				memcpy(p + 2, vp->vp_octets, vp->vp_length);
				p[0] = vp->da->attr - PW_DIGEST_REALM + 1;
				vp->vp_length += 2;
				p[1] = vp->vp_length;

				da = fr_dict_attr_by_num(NULL, 0, PW_DIGEST_ATTRIBUTES);
				if (!da) {
					ERROR("Out of memory");
					goto error;
				}
				vp->da = da;

				/*
				 *	Re-do fr_pair_value_memsteal ourselves,
				 *	because we play games with
				 *	vp->da, and fr_pair_value_memsteal goes
				 *	to GREAT lengths to sanitize
				 *	and fix and change and
				 *	double-check the various
				 *	fields.
				 */
				memcpy(&q, &vp->vp_octets, sizeof(q));
				talloc_free(q);

				vp->vp_octets = talloc_steal(vp, p);
				vp->type = VT_DATA;

				VERIFY_VP(vp);
			}
				break;

				/*
				 *	Cache this for later.
				 */
			case PW_CLEARTEXT_PASSWORD:
				request->password = vp;
				break;

			/*
			 *	Keep a copy of the the password attribute.
			 */
			case PW_CHAP_PASSWORD:
				/*
				 *	If it's already hex, do nothing.
				 */
				if ((vp->vp_length == 17) &&
				    (already_hex(vp))) break;

				/*
				 *	CHAP-Password is octets, so it may not be zero terminated.
				 */
				request->password = fr_pair_make(request->packet, &request->packet->vps, "Cleartext-Password",
							     "", T_OP_EQ);
				fr_pair_value_bstrncpy(request->password, vp->vp_strvalue, vp->vp_length);
				break;

			case PW_USER_PASSWORD:
			case PW_MS_CHAP_PASSWORD:
				request->password = fr_pair_make(request->packet, &request->packet->vps, "Cleartext-Password",
							     vp->vp_strvalue, T_OP_EQ);
				break;

			case PW_RADCLIENT_TEST_NAME:
				request->name = vp->vp_strvalue;
				break;
			}
		} /* loop over the VP's we read in */

		/*
		 *	Use the default set on the command line
		 */
		if (request->packet->code == PW_CODE_UNDEFINED) request->packet->code = packet_code;

		/*
		 *	Default to the filename
		 */
		if (!request->name) request->name = request->files->packets;

		/*
		 *	Automatically set the response code from the request code
		 *	(if one wasn't already set).
		 */
		if (request->filter_code == PW_CODE_UNDEFINED) {
			switch (request->packet->code) {
			case PW_CODE_ACCESS_REQUEST:
				request->filter_code = PW_CODE_ACCESS_ACCEPT;
				break;

			case PW_CODE_ACCOUNTING_REQUEST:
				request->filter_code = PW_CODE_ACCOUNTING_RESPONSE;
				break;

			case PW_CODE_COA_REQUEST:
				request->filter_code = PW_CODE_COA_ACK;
				break;

			case PW_CODE_DISCONNECT_REQUEST:
				request->filter_code = PW_CODE_DISCONNECT_ACK;
				break;

			case PW_CODE_STATUS_SERVER:
				switch (radclient_get_code(request->packet->dst_port)) {
				case PW_CODE_ACCESS_REQUEST:
					request->filter_code = PW_CODE_ACCESS_ACCEPT;
					break;

				case PW_CODE_ACCOUNTING_REQUEST:
					request->filter_code = PW_CODE_ACCOUNTING_RESPONSE;
					break;

				default:
					request->filter_code = PW_CODE_UNDEFINED;
					break;
				}
				break;

			case PW_CODE_UNDEFINED:
				REDEBUG("Both Packet-Type and Response-Packet-Type undefined, specify at least one, "
					"or a well known RADIUS port");
				goto error;

			default:
				REDEBUG("Can't determine expected Response-Packet-Type for Packet-Type %i",
					request->packet->code);
				goto error;
			}
		/*
		 *	Automatically set the request code from the response code
		 *	(if one wasn't already set).
		 */
		} else if (request->packet->code == PW_CODE_UNDEFINED) {
			switch (request->filter_code) {
			case PW_CODE_ACCESS_ACCEPT:
			case PW_CODE_ACCESS_REJECT:
				request->packet->code = PW_CODE_ACCESS_REQUEST;
				break;

			case PW_CODE_ACCOUNTING_RESPONSE:
				request->packet->code = PW_CODE_ACCOUNTING_REQUEST;
				break;

			case PW_CODE_DISCONNECT_ACK:
			case PW_CODE_DISCONNECT_NAK:
				request->packet->code = PW_CODE_DISCONNECT_REQUEST;
				break;

			case PW_CODE_COA_ACK:
			case PW_CODE_COA_NAK:
				request->packet->code = PW_CODE_COA_REQUEST;
				break;

			default:
				REDEBUG("Can't determine expected Packet-Type for Response-Packet-Type %i",
					request->filter_code);
				goto error;
			}
		}

		/*
		 *	Automatically set the dst port (if one wasn't already set).
		 */
		if (request->packet->dst_port == 0) {
			radclient_get_port(request->packet->code, &request->packet->dst_port);
			if (request->packet->dst_port == 0) {
				REDEBUG("Can't determine destination port");
				goto error;
			}
		}

		/*
		 *	Add it to the tail of the list.
		 */
		if (!request_head) {
			assert(rc_request_tail == NULL);
			request_head = request;
			request->prev = NULL;
		} else {
			assert(rc_request_tail->next == NULL);
			rc_request_tail->next = request;
			request->prev = rc_request_tail;
		}
		rc_request_tail = request;
		request->next = NULL;

		/*
		 *	Set the destructor so it removes itself from the
		 *	request list when freed. We don't set this until
		 *	the packet is actually in the list, else we trigger
		 *	the asserts in the free callback.
		 */
		talloc_set_destructor(request, _rc_request_free);
	} while (!packets_done); /* loop until the file is done. */

	if (packets != stdin) fclose(packets);
	if (filters) fclose(filters);

	/*
	 *	And we're done.
	 */
	return 1;

error:
	talloc_free(request);

	if (packets != stdin) fclose(packets);
	if (filters) fclose(filters);

	return 0;
}
Example #5
0
/** Convert value_pair_map_t to VALUE_PAIR(s) and add them to a REQUEST.
 *
 * Takes a single value_pair_map_t, resolves request and list identifiers
 * to pointers in the current request, then attempts to retrieve module
 * specific value(s) using callback, and adds the resulting values to the
 * correct request/list.
 *
 * @param request The current request.
 * @param map specifying destination attribute and location and src identifier.
 * @param func to retrieve module specific values and convert them to
 *	VALUE_PAIRS.
 * @param ctx to be passed to func.
 * @param src name to be used in debugging if different from map value.
 * @return -1 if the operation failed, -2 in the source attribute wasn't valid, 0 on success.
 */
int radius_map2request(REQUEST *request, value_pair_map_t const *map,
		       UNUSED char const *src, radius_tmpl_getvalue_t func, void *ctx)
{
	int rcode, num;
	VALUE_PAIR **list, *vp, *head = NULL;
	REQUEST *context;
	TALLOC_CTX *parent;
	vp_cursor_t cursor;

	/*
	 *	Sanity check inputs.  We can have a list or attribute
	 *	as a destination.
	 */
	if ((map->dst->type != VPT_TYPE_LIST) &&
	    (map->dst->type != VPT_TYPE_ATTR)) {
		REDEBUG("Invalid mapping destination");
		return -2;
	}

	context = request;
	if (radius_request(&context, map->dst->request) < 0) {
		REDEBUG("Mapping \"%s\" -> \"%s\" invalid in this context", map->src->name, map->dst->name);
		return -2;
	}

	/*
	 *	If there's no CoA packet and we're updating it,
	 *	auto-allocate it.
	 */
	if (((map->dst->list == PAIR_LIST_COA) ||
	     (map->dst->list == PAIR_LIST_DM)) &&
	    !request->coa) {
		request_alloc_coa(context);
		if (map->dst->list == PAIR_LIST_COA) {
			context->coa->proxy->code = PW_CODE_COA_REQUEST;
		} else {
			context->coa->proxy->code = PW_CODE_DISCONNECT_REQUEST;
		}
	}

	list = radius_list(context, map->dst->list);
	if (!list) {
		REDEBUG("Mapping \"%s\" -> \"%s\" invalid in this context", map->src->name, map->dst->name);

		return -2;
	}

	parent = radius_list_ctx(context, map->dst->list);

	/*
	 *	The callback should either return -1 to signify operations error, -2 when it can't find the
	 *	attribute or list being referenced, or 0 to signify success.
	 *	It may return "sucess", but still have no VPs to work with.
	 */
	rcode = func(&head, request, map, ctx);
	if (rcode < 0) {
		rad_assert(!head);
		return rcode;
	}

	if (!head) return 0;

	/*
	 *	Reparent the VP
	 */
	for (vp = fr_cursor_init(&cursor, &head); vp; vp = fr_cursor_next(&cursor)) {

		VERIFY_VP(vp);
		if (debug_flag) debug_map(request, map, vp);

		(void) talloc_steal(parent, vp);
	}

	/*
	 *	List to list copies.
	 */
	if (map->dst->type == VPT_TYPE_LIST) {
		switch (map->op) {
		case T_OP_CMP_FALSE:
			rad_assert(head == NULL);
			pairfree(list);

			if (map->dst->list == PAIR_LIST_REQUEST) {
				context->username = NULL;
				context->password = NULL;
			}
			break;

		case T_OP_SET:
			if (map->src->type == VPT_TYPE_LIST) {
				pairfree(list);
				*list = head;
			} else {
		case T_OP_EQ:

				rad_assert(map->src->type == VPT_TYPE_EXEC);
				pairmove(parent, list, &head);
				pairfree(&head);
			}

			if (map->dst->list == PAIR_LIST_REQUEST) {
				context->username = pairfind(head, PW_USER_NAME, 0, TAG_ANY);
				context->password = pairfind(head, PW_USER_PASSWORD, 0, TAG_ANY);
			}
			break;

		case T_OP_ADD:
			pairadd(list, head);
			break;

		default:
			pairfree(&head);
			return -1;
		}

		return 0;
	}

	/*
	 *	We now should have only one destination attribute, and
	 *	only one source attribute.
	 */
	rad_assert(head->next == NULL);

	/*
	 *	Find the destination attribute.  We leave with either
	 *	the cursor and vp pointing to the attribute, or vp is
	 *	NULL.
	 */
	num = map->dst->num;
	for (vp = fr_cursor_init(&cursor, list);
	     vp != NULL;
	     vp = fr_cursor_next(&cursor)) {
		VERIFY_VP(vp);
		if ((vp->da == map->dst->da) && (!vp->da->flags.has_tag || (map->dst->tag == TAG_ANY) || (vp->tag == map->dst->tag))) {
			if (num == 0) break;
			num--;
		}
	}

	/*
	 *	Figure out what to do with the source attribute.
	 */
	switch (map->op) {
	case T_OP_CMP_FALSE:	/* remove matching attributes */
		pairfree(&head);
		if (!vp) return 0;

		/*
		 *	Wildcard: delete all of the matching ones,
		 *	based on tag.
		 */
		if (!map->dst->num) {
			pairdelete(list, map->dst->da->attr, map->dst->da->vendor,
				   map->dst->tag);
			vp = NULL;
		} else {
			/*
			 *	We've found the Nth one.  Delete it, and only
			 *	it.
			 */
			vp = fr_cursor_remove(&cursor);
		}

		/*
		 *	Check that the User-Name and User-Password
		 *	caches point to the correct attribute.
		 */
	fixup:
		if (map->dst->list == PAIR_LIST_REQUEST) {
			context->username = pairfind(*list, PW_USER_NAME, 0, TAG_ANY);
			context->password = pairfind(*list, PW_USER_PASSWORD, 0, TAG_ANY);
		}
		pairfree(&vp);
		return 0;

	case T_OP_EQ:		/* set only if not already set */
		if (vp) {
			pairfree(&head);
			return 0;
		}
		fr_cursor_insert(&cursor, head);
		goto fixup;

	case T_OP_SET:		/* over-write if existing, or else add */
		if (vp) vp = fr_cursor_remove(&cursor);
		fr_cursor_insert(&cursor, head);
		goto fixup;

	case T_OP_ADD:		/* append no matter what */
		vp = NULL;
		pairadd(list, head);
		goto fixup;

	case T_OP_SUB:		/* delete if it matches */
		head->op = T_OP_CMP_EQ;
		rcode = radius_compare_vps(NULL, head, vp);
		pairfree(&head);

		if (rcode == 0) {
			vp = fr_cursor_remove(&cursor);
			goto fixup;
		}
		return 0;

	default:		/* filtering operators */
		/*
		 *	If the VP doesn't exist, the filters will add
		 *	it with the given value.
		 */
		if (!vp) {
			fr_cursor_insert(&cursor, head);
			goto fixup;
		}
		break;
	}

	/*
	 *	The LHS exists.  We need to limit it's value based on
	 *	the operator, and the value of the RHS.
	 */
	head->op = map->op;
	rcode = radius_compare_vps(NULL, head, vp);
	head->op = T_OP_SET;

	switch (map->op) {
	case T_OP_CMP_EQ:
		if (rcode == 0) {
	leave:
			pairfree(&head);
			break;
		}
	replace:
		vp = fr_cursor_remove(&cursor);
		fr_cursor_insert(&cursor, head);
		goto fixup;

	case T_OP_LE:
		if (rcode <= 0) goto leave;
		goto replace;

	case T_OP_GE:
		if (rcode >= 0) goto leave;
		goto replace;

	default:
		pairfree(&head);
		return -1;
	}

	return 0;
}
Example #6
0
/** Return a VP from a value_pair_tmpl_t
 *
 * @param out where to write the retrieved vp.
 * @param request current request.
 * @param vpt the value pair template
 * @return -1 if VP could not be found, -2 if list could not be found, -3 if context could not be found.
 */
int radius_vpt_get_vp(VALUE_PAIR **out, REQUEST *request, value_pair_tmpl_t const *vpt)
{
	VALUE_PAIR **vps, *vp;

	if (out) *out = NULL;

	if (radius_request(&request, vpt->request) < 0) {
		return -3;
	}

	vps = radius_list(request, vpt->list);
	if (!vps) {
		return -2;
	}

	switch (vpt->type) {
		/*
		 *	May not may not be found, but it *is* a known
		 *	name.
		 */
	case VPT_TYPE_ATTR:
		if (vpt->num == 0) {
			vp = pairfind(*vps, vpt->da->attr, vpt->da->vendor, vpt->tag);
			if (!vp) return -1;

		} else {
			int num;
			vp_cursor_t cursor;

			/*
			 *	It's faster to just repeat the 3-4 lines of pairfind here.
			 */
			num = vpt->num;
			for (vp = fr_cursor_init(&cursor, vps);
			     vp != NULL;
			     vp = fr_cursor_next(&cursor)) {
				VERIFY_VP(vp);
				if ((vp->da == vpt->da) && (!vp->da->flags.has_tag || (vpt->tag == TAG_ANY) || (vp->tag == vpt->tag))) {
					if (num == 0) {
						*out = vp;
						return 0;
					}
					num--;
				}
			}
			return -1;
		}
		break;

	case VPT_TYPE_LIST:
		vp = *vps;
		break;

	default:
		/*
		 *	literal, xlat, regex, exec, data.
		 *	no attribute.
		 */
		return -1;
	}

	if (out) {
		*out = vp;
	}

	return 0;
}
/** Create and insert a cache entry.
 *
 * @return RLM_MODULE_OK on success, RLM_MODULE_UPDATED if we merged the cache entry and RLM_MODULE_FAIL on failure.
 */
static rlm_rcode_t cache_insert(rlm_cache_t *inst, REQUEST *request, rlm_cache_handle_t **handle,
				char const *key, int ttl)
{
	VALUE_PAIR *vp, *to_cache;
	vp_cursor_t src_list, cached_request, cached_reply, cached_control;

	value_pair_map_t const *map;

	bool merge = false;
	rlm_cache_entry_t *c;

	if ((inst->max_entries > 0) && inst->module->count &&
	    (inst->module->count(inst, request, handle) > inst->max_entries)) {
		RWDEBUG("Cache is full: %d entries", inst->max_entries);
		return RLM_MODULE_FAIL;
	}

	c = cache_alloc(inst, request);
	if (!c) return RLM_MODULE_FAIL;

	c->key = talloc_typed_strdup(c, key);
	c->created = c->expires = request->timestamp;
	c->expires += ttl;

	RDEBUG("Creating new cache entry");

	fr_cursor_init(&cached_request, &c->packet);
	fr_cursor_init(&cached_reply, &c->reply);
	fr_cursor_init(&cached_control, &c->control);

	for (map = inst->maps; map != NULL; map = map->next) {
		rad_assert(map->lhs && map->rhs);

		if (map_to_vp(&to_cache, request, map, NULL) < 0) {
			RDEBUG("Skipping %s", map->rhs->name);
			continue;
		}

		/*
		 *	Reparent the VPs map_to_vp may return multiple.
		 */
		for (vp = fr_cursor_init(&src_list, &to_cache);
		     vp;
		     vp = fr_cursor_next(&src_list)) {
			VERIFY_VP(vp);

			/*
			 *	Prevent people from accidentally caching
			 *	cache control attributes.
			 */
			if (map->rhs->type == TMPL_TYPE_LIST) switch (vp->da->attr) {
			case PW_CACHE_TTL:
			case PW_CACHE_STATUS_ONLY:
			case PW_CACHE_READ_ONLY:
			case PW_CACHE_MERGE:
			case PW_CACHE_ENTRY_HITS:
				RDEBUG2("Skipping %s", vp->da->name);
				continue;

			default:
				break;
			}

			RINDENT();
			if (RDEBUG_ENABLED2) map_debug_log(request, map, vp);
			REXDENT();
			(void) talloc_steal(c, vp);

			vp->op = map->op;

			switch (map->lhs->tmpl_list) {
			case PAIR_LIST_REQUEST:
				fr_cursor_insert(&cached_request, vp);
				break;

			case PAIR_LIST_REPLY:
				fr_cursor_insert(&cached_reply, vp);
				break;

			case PAIR_LIST_CONTROL:
				fr_cursor_insert(&cached_control, vp);
				break;

			default:
				rad_assert(0);	/* should have been caught by validation */
			}
		}
	}

	/*
	 *	Check to see if we need to merge the entry into the request
	 */
	vp = pairfind(request->config_items, PW_CACHE_MERGE, 0, TAG_ANY);
	if (vp && (vp->vp_integer > 0)) merge = true;

	if (merge) cache_merge(inst, request, c);

	for (;;) {
		cache_status_t ret;

		ret = inst->module->insert(inst, request, handle, c);
		switch (ret) {
		case CACHE_RECONNECT:
			if (cache_reconnect(inst, request, handle) == 0) continue;
			return RLM_MODULE_FAIL;

		case CACHE_OK:
			RDEBUG("Commited entry, TTL %d seconds", ttl);
			cache_free(inst, &c);
			return merge ? RLM_MODULE_UPDATED :
				       RLM_MODULE_OK;

		default:
			talloc_free(c);	/* Failed insertion - use talloc_free not the driver free */
			return RLM_MODULE_FAIL;
		}
	}
}
Example #8
0
/*
 *	Process and reply to an authentication request
 *
 *	The return value of this function isn't actually used right now, so
 *	it's not entirely clear if it is returning the right things. --Pac.
 */
int rad_authenticate(REQUEST *request)
{
#ifdef WITH_SESSION_MGMT
    VALUE_PAIR	*check_item;
#endif
    VALUE_PAIR	*module_msg;
    VALUE_PAIR	*tmp = NULL;
    int		result;
    char		autz_retry = 0;
    int		autz_type = 0;

#ifdef WITH_PROXY
    /*
     *	If this request got proxied to another server, we need
     *	to check whether it authenticated the request or not.
     */
    if (request->proxy_reply) {
        switch (request->proxy_reply->code) {
        /*
         *	Reply of ACCEPT means accept, thus set Auth-Type
         *	accordingly.
         */
        case PW_CODE_AUTHENTICATION_ACK:
            tmp = radius_paircreate(request,
                                    &request->config_items,
                                    PW_AUTH_TYPE, 0);
            if (tmp) tmp->vp_integer = PW_AUTHTYPE_ACCEPT;
            goto authenticate;

        /*
         *	Challenges are punted back to the NAS without any
         *	further processing.
         */
        case PW_CODE_ACCESS_CHALLENGE:
            request->reply->code = PW_CODE_ACCESS_CHALLENGE;
            return RLM_MODULE_OK;
        /*
         *	ALL other replies mean reject. (this is fail-safe)
         *
         *	Do NOT do any authorization or authentication. They
         *	are being rejected, so we minimize the amount of work
         *	done by the server, by rejecting them here.
         */
        case PW_CODE_AUTHENTICATION_REJECT:
            rad_authlog("Login incorrect (Home Server says so)",
                        request, 0);
            request->reply->code = PW_CODE_AUTHENTICATION_REJECT;
            return RLM_MODULE_REJECT;

        default:
            rad_authlog("Login incorrect (Home Server failed to respond)",
                        request, 0);
            return RLM_MODULE_REJECT;
        }
    }
#endif
    /*
     *	Look for, and cache, passwords.
     */
    if (!request->password) {
        request->password = pairfind(request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY);
    }
    if (!request->password) {
        request->password = pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY);
    }

    /*
     *	Get the user's authorization information from the database
     */
autz_redo:
    result = process_authorize(autz_type, request);
    switch (result) {
    case RLM_MODULE_NOOP:
    case RLM_MODULE_NOTFOUND:
    case RLM_MODULE_OK:
    case RLM_MODULE_UPDATED:
        break;
    case RLM_MODULE_HANDLED:
        return result;
    case RLM_MODULE_FAIL:
    case RLM_MODULE_INVALID:
    case RLM_MODULE_REJECT:
    case RLM_MODULE_USERLOCK:
    default:
        if ((module_msg = pairfind(request->packet->vps, PW_MODULE_FAILURE_MESSAGE, 0, TAG_ANY)) != NULL) {
            char msg[MAX_STRING_LEN + 16];
            snprintf(msg, sizeof(msg), "Invalid user (%s)",
                     module_msg->vp_strvalue);
            rad_authlog(msg,request,0);
        } else {
            rad_authlog("Invalid user", request, 0);
        }
        request->reply->code = PW_CODE_AUTHENTICATION_REJECT;
        return result;
    }
    if (!autz_retry) {
        tmp = pairfind(request->config_items, PW_AUTZ_TYPE, 0, TAG_ANY);
        if (tmp) {
            autz_type = tmp->vp_integer;
            RDEBUG2("Using Autz-Type %s",
                    dict_valnamebyattr(PW_AUTZ_TYPE, 0, autz_type));
            autz_retry = 1;
            goto autz_redo;
        }
    }

    /*
     *	If we haven't already proxied the packet, then check
     *	to see if we should.  Maybe one of the authorize
     *	modules has decided that a proxy should be used. If
     *	so, get out of here and send the packet.
     */
    if (
#ifdef WITH_PROXY
        (request->proxy == NULL) &&
#endif
        ((tmp = pairfind(request->config_items, PW_PROXY_TO_REALM, 0, TAG_ANY)) != NULL)) {
        REALM *realm;

        realm = realm_find2(tmp->vp_strvalue);

        /*
         *	Don't authenticate, as the request is going to
         *	be proxied.
         */
        if (realm && realm->auth_pool) {
            return RLM_MODULE_OK;
        }

        /*
         *	Catch users who set Proxy-To-Realm to a LOCAL
         *	realm (sigh).  But don't complain if it is
         *	*the* LOCAL realm.
         */
        if (realm &&(strcmp(realm->name, "LOCAL") != 0)) {
            RWDEBUG2("You set Proxy-To-Realm = %s, but it is a LOCAL realm!  Cancelling proxy request.", realm->name);
        }

        if (!realm) {
            RWDEBUG2("You set Proxy-To-Realm = %s, but the realm does not exist!  Cancelling invalid proxy request.", tmp->vp_strvalue);
        }
    }

#ifdef WITH_PROXY
authenticate:
#endif

    /*
     *	Validate the user
     */
    do {
        result = rad_check_password(request);
        if (result > 0) {
            /* don't reply! */
            return RLM_MODULE_HANDLED;
        }
    } while(0);

    /*
     *	Failed to validate the user.
     *
     *	We PRESUME that the code which failed will clean up
     *	request->reply->vps, to be ONLY the reply items it
     *	wants to send back.
     */
    if (result < 0) {
        RDEBUG2("Failed to authenticate the user");
        request->reply->code = PW_CODE_AUTHENTICATION_REJECT;

        if ((module_msg = pairfind(request->packet->vps, PW_MODULE_FAILURE_MESSAGE, 0, TAG_ANY)) != NULL) {
            char msg[MAX_STRING_LEN+19];

            snprintf(msg, sizeof(msg), "Login incorrect (%s)",
                     module_msg->vp_strvalue);
            rad_authlog(msg, request, 0);
        } else {
            rad_authlog("Login incorrect", request, 0);
        }

        if (request->password) {
            VERIFY_VP(request->password);
            /* double check: maybe the secret is wrong? */
            if ((debug_flag > 1) && (request->password->da->attr == PW_USER_PASSWORD)) {
                uint8_t const *p;

                p = (uint8_t const *) request->password->vp_strvalue;
                while (*p) {
                    int size;

                    size = fr_utf8_char(p);
                    if (!size) {
                        RWDEBUG("Unprintable characters in the password.  Double-check the "
                                "shared secret on the server and the NAS!");
                        break;
                    }
                    p += size;
                }
            }
        }
    }

#ifdef WITH_SESSION_MGMT
    if (result >= 0 &&
            (check_item = pairfind(request->config_items, PW_SIMULTANEOUS_USE, 0, TAG_ANY)) != NULL) {
        int r, session_type = 0;
        char		logstr[1024];
        char		umsg[MAX_STRING_LEN + 1];
        char const	*user_msg = NULL;

        tmp = pairfind(request->config_items, PW_SESSION_TYPE, 0, TAG_ANY);
        if (tmp) {
            session_type = tmp->vp_integer;
            RDEBUG2("Using Session-Type %s",
                    dict_valnamebyattr(PW_SESSION_TYPE, 0, session_type));
        }

        /*
         *	User authenticated O.K. Now we have to check
         *	for the Simultaneous-Use parameter.
         */
        if (request->username &&
                (r = process_checksimul(session_type, request, check_item->vp_integer)) != 0) {
            char mpp_ok = 0;

            if (r == 2) {
                /* Multilink attempt. Check if port-limit > simultaneous-use */
                VALUE_PAIR *port_limit;

                if ((port_limit = pairfind(request->reply->vps, PW_PORT_LIMIT, 0, TAG_ANY)) != NULL &&
                        port_limit->vp_integer > check_item->vp_integer) {
                    RDEBUG2("MPP is OK");
                    mpp_ok = 1;
                }
            }
            if (!mpp_ok) {
                if (check_item->vp_integer > 1) {
                    snprintf(umsg, sizeof(umsg),
                             "\r\nYou are already logged in %d times  - access denied\r\n\n",
                             (int)check_item->vp_integer);
                    user_msg = umsg;
                } else {
                    user_msg = "\r\nYou are already logged in - access denied\r\n\n";
                }

                request->reply->code = PW_CODE_AUTHENTICATION_REJECT;

                /*
                 *	They're trying to log in too many times.
                 *	Remove ALL reply attributes.
                 */
                pairfree(&request->reply->vps);
                pairmake_reply("Reply-Message",
                               user_msg, T_OP_SET);

                snprintf(logstr, sizeof(logstr), "Multiple logins (max %d) %s",
                         check_item->vp_integer,
                         r == 2 ? "[MPP attempt]" : "");
                rad_authlog(logstr, request, 1);

                result = -1;
            }
        }
    }
#endif

    /*
     *	Result should be >= 0 here - if not, it means the user
     *	is rejected, so we just process post-auth and return.
     */
    if (result < 0) {
        return RLM_MODULE_REJECT;
    }

    /*
     *	Set the reply to Access-Accept, if it hasn't already
     *	been set to something.  (i.e. Access-Challenge)
     */
    if (request->reply->code == 0)
        request->reply->code = PW_CODE_AUTHENTICATION_ACK;

    if ((module_msg = pairfind(request->packet->vps, PW_MODULE_SUCCESS_MESSAGE, 0, TAG_ANY)) != NULL) {
        char msg[MAX_STRING_LEN+12];

        snprintf(msg, sizeof(msg), "Login OK (%s)",
                 module_msg->vp_strvalue);
        rad_authlog(msg, request, 1);
    } else {
        rad_authlog("Login OK", request, 1);
    }

    return result;
}
Example #9
0
/** Convert value_pair_map_t to VALUE_PAIR(s) and add them to a REQUEST.
 *
 * Takes a single value_pair_map_t, resolves request and list identifiers
 * to pointers in the current request, then attempts to retrieve module
 * specific value(s) using callback, and adds the resulting values to the
 * correct request/list.
 *
 * @param request The current request.
 * @param map specifying destination attribute and location and src identifier.
 * @param func to retrieve module specific values and convert them to
 *	VALUE_PAIRS.
 * @param ctx to be passed to func.
 * @param src name to be used in debugging if different from map value.
 * @return -1 if the operation failed, -2 in the source attribute wasn't valid, 0 on success.
 */
int radius_map2request(REQUEST *request, value_pair_map_t const *map,
		       UNUSED char const *src, radius_tmpl_getvalue_t func, void *ctx)
{
	int rcode;
	vp_cursor_t cursor;
	VALUE_PAIR **list, *vp, *head = NULL;
	char buffer[1024];

	if (radius_request(&request, map->dst->request) < 0) {
		REDEBUG("Mapping \"%s\" -> \"%s\" invalid in this context", map->src->name, map->dst->name);

		return -2;
	}

	list = radius_list(request, map->dst->list);
	if (!list) {
		REDEBUG("Mapping \"%s\" -> \"%s\" invalid in this context", map->src->name, map->dst->name);

		return -2;
	}


	/*
	 *	The callback should either return -1 to signify operations error, -2 when it can't find the
	 *	attribute or list being referenced, or 0 to signify success.
	 *	It may return "sucess", but still have no VPs to work with.
	 *	Only if it returned an error code should it not write anything to the head pointer.
	 */
	rcode = func(&head, request, map, ctx);
	if (rcode < 0) {
		rad_assert(!head);

		return rcode;
	}

	if (!head) return 0;

	VERIFY_VP(head);

	if (debug_flag) for (vp = paircursor(&cursor, &head); vp; vp = pairnext(&cursor)) {
		char *value;

		switch (map->src->type) {
			/*
			 *	Just print the value being assigned
			 */
			default:

			case VPT_TYPE_LITERAL:
				vp_prints_value(buffer, sizeof(buffer), vp, '\'');
				value = buffer;
				break;
			case VPT_TYPE_XLAT:
				vp_prints_value(buffer, sizeof(buffer), vp, '"');
				value = buffer;
				break;
			case VPT_TYPE_DATA:
				vp_prints_value(buffer, sizeof(buffer), vp, 0);
				value = buffer;
				break;
			/*
			 *	Just printing the value doesn't make sense, but we still
			 *	want to know what it was...
			 */
			case VPT_TYPE_LIST:
				vp_prints_value(buffer, sizeof(buffer), vp, '\'');
				value = talloc_asprintf(request, "&%s%s -> %s", map->src->name, vp->da->name, buffer);
				break;
			case VPT_TYPE_ATTR:
				vp_prints_value(buffer, sizeof(buffer), vp, '\'');
				value = talloc_asprintf(request, "&%s -> %s", map->src->name, buffer);
				break;
		}


		RDEBUG("\t\t%s %s %s", map->dst->name, fr_int2str(fr_tokens, vp->op, "<INVALID>"), value);

		if (value != buffer) talloc_free(value);
	}

	/*
	 *	Use pairmove so the operator is respected
	 */
	radius_pairmove(request, list, head);
	return 0;
}
/** Insert a single VP
 *
 * @todo don't use with pairdelete
 */
void fr_cursor_insert(vp_cursor_t *cursor, VALUE_PAIR *add)
{
	VALUE_PAIR *i;

	if (!add) {
		return;
	}

	VERIFY_VP(add);

	/*
	 *	Only allow one VP to by inserted at a time
	 */
	add->next = NULL;

	/*
	 *	Cursor was initialised with a pointer to a NULL value_pair
	 */
	if (!*cursor->first) {
		*cursor->first = add;
		cursor->current = add;

		return;
	}

	/*
	 *	We don't yet know where the last VALUE_PAIR is
	 *
	 *	Assume current is closer to the end of the list and use that if available.
	 */
	if (!cursor->last) {
		cursor->last = cursor->current ? cursor->current : *cursor->first;
	}

	VERIFY_VP(cursor->last);

	/*
	 *	Something outside of the cursor added another VALUE_PAIR
	 */
	if (cursor->last->next) {
		for (i = cursor->last; i; i = i->next) {
			VERIFY_VP(i);
			cursor->last = i;
		}
	}

	/*
	 *	Either current was never set, or something iterated to the end of the
	 *	attribute list.
	 */
	if (!cursor->current) {
		cursor->current = add;
	}

	/*
	 *	If there's no next cursor, and the pair we just inserted has additional
	 *	linked pairs, we need to set next to be the next VP in the list.
	 */
	if (!cursor->next) {
		cursor->next = add->next;
	}

	cursor->last->next = add;
}
Example #11
0
/** Copy pairs matching a VPT in the current request
 *
 * @param ctx to allocate new VALUE_PAIRs under.
 * @param out where to write the copied vps.
 * @param request current request.
 * @param vpt the value pair template
 * @return -1 if VP could not be found, -2 if list could not be found, -3 if context could not be found.
 */
int tmpl_copy_vps(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, value_pair_tmpl_t const *vpt)
{
	VALUE_PAIR **vps, *vp;
	REQUEST *current = request;
	vp_cursor_t from, to;

	rad_assert((vpt->type == TMPL_TYPE_ATTR) || (vpt->type == TMPL_TYPE_LIST));

	if (out) *out = NULL;

	if (radius_request(&current, vpt->tmpl_request) < 0) {
		return -3;
	}

	vps = radius_list(request, vpt->tmpl_list);
	if (!vps) {
		return -2;
	}

	switch (vpt->type) {
	/*
	 *	May not be found, but it *is* a known name.
	 */
	case TMPL_TYPE_ATTR:
	{
		int num;

		(void) fr_cursor_init(&to, out);
		(void) fr_cursor_init(&from, vps);

		vp = fr_cursor_next_by_da(&from, vpt->tmpl_da, vpt->tmpl_tag);
		if (!vp) return -1;

		switch (vpt->tmpl_num) {
		/* Copy all pairs of this type (and tag) */
		case NUM_ALL:
			do {
				VERIFY_VP(vp);
				vp = paircopyvp(ctx, vp);
				if (!vp) {
					pairfree(out);
					return -4;
				}
				fr_cursor_insert(&to, vp);
			} while ((vp = fr_cursor_next_by_da(&from, vpt->tmpl_da, vpt->tmpl_tag)));
			break;

		/* Specific attribute number */
		default:
			for (num = vpt->tmpl_num;
			     num && vp;
			     num--, vp = fr_cursor_next_by_da(&from, vpt->tmpl_da, vpt->tmpl_tag)) {
			     VERIFY_VP(vp);
			}
			if (!vp) return -1;
			/* FALL-THROUGH */

		/* Just copy the first pair */
		case NUM_ANY:
			vp = paircopyvp(ctx, vp);
			if (!vp) {
				pairfree(out);
				return -4;
			}
			fr_cursor_insert(&to, vp);
		}
	}
		break;

	case TMPL_TYPE_LIST:
		vp = paircopy(ctx, *vps);
		if (!vp) return 0;

		fr_cursor_merge(&to, vp);
		break;

	default:
		rad_assert(0);
	}

	return 0;
}