/** Allocate a request packet
 *
 * This is done once per request with the same packet being sent to multiple realms.
 */
static rlm_rcode_t rlm_replicate_alloc(RADIUS_PACKET **out, REQUEST *request, pair_lists_t list, PW_CODE code)
{
	rlm_rcode_t rcode = RLM_MODULE_OK;
	RADIUS_PACKET *packet = NULL;
	VALUE_PAIR *vp, **vps;

	*out = NULL;

	packet = rad_alloc(request, 1);
	if (!packet) {
		return RLM_MODULE_FAIL;
	}
	packet->code = code;

	/*
	 *	Figure out which list in the request were replicating
	 */
	vps = radius_list(request, list);
	if (!vps) {
		RWDEBUG("List '%s' doesn't exist for this packet", fr_int2str(pair_lists, list, "<INVALID>"));
		rcode = RLM_MODULE_INVALID;
		goto error;
	}

	/*
	 *	Don't assume the list actually contains any attributes.
	 */
	if (*vps) {
		packet->vps = paircopy(packet, *vps);
		if (!packet->vps) {
			rcode = RLM_MODULE_FAIL;
			goto error;
		}
	}

	/*
	 *	For CHAP, create the CHAP-Challenge if it doesn't exist.
	 */
	if ((code == PW_CODE_ACCESS_REQUEST) &&
	    (pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY) != NULL) &&
	    (pairfind(request->packet->vps, PW_CHAP_CHALLENGE, 0, TAG_ANY) == NULL)) {
		vp = radius_paircreate(packet, &packet->vps, PW_CHAP_CHALLENGE, 0);
		pairmemcpy(vp, request->packet->vector, AUTH_VECTOR_LEN);
	}

	*out = packet;
	return rcode;

error:
	talloc_free(packet);
	return rcode;
}
Exemple #2
0
RADIUS_PACKET *fr_tcp_recv(int sockfd, int flags)
{
	RADIUS_PACKET *packet = rad_alloc(NULL, 0);

	if (!packet) return NULL;

	packet->sockfd = sockfd;

	if (fr_tcp_read_packet(packet, flags) != 1) {
		rad_free(&packet);
		return NULL;
	}

	return packet;
}
Exemple #3
0
REQUEST *request_alloc_coa(REQUEST *request)
{
	if (!request || request->coa) return NULL;

	/*
	 *	Originate CoA requests only when necessary.
	 */
	if ((request->packet->code != PW_AUTHENTICATION_REQUEST) &&
	    (request->packet->code != PW_ACCOUNTING_REQUEST)) return NULL;

	request->coa = request_alloc_fake(request);
	if (!request->coa) return NULL;

	request->coa->packet->code = 0; /* unknown, as of yet */
	request->coa->child_state = REQUEST_RUNNING;
	request->coa->proxy = rad_alloc(request->coa, 0);
	if (!request->coa->proxy) {
		request_free(&request->coa);
		return NULL;
	}

	return request->coa;
}
Exemple #4
0
RADIUS_PACKET *fr_tcp_accept(int sockfd)
{
	int newfd;
	socklen_t salen;
	RADIUS_PACKET *packet;
	struct sockaddr_storage src;

	salen = sizeof(src);

	newfd = accept(sockfd, (struct sockaddr *) &src, &salen);
	if (newfd < 0) {
		/*
		 *	Non-blocking sockets must handle this.
		 */
#ifdef EWOULDBLOCK
		if (errno == EWOULDBLOCK) {
			packet = rad_alloc(NULL, 0);
			if (!packet) return NULL;

			packet->sockfd = sockfd;
			packet->src_ipaddr.af = AF_UNSPEC;
			return packet;
		}
#endif

		return NULL;
	}

	packet = rad_alloc(NULL, 0);
	if (!packet) {
		close(newfd);
		return NULL;
	}

	if (src.ss_family == AF_INET) {
		struct sockaddr_in	s4;

		memcpy(&s4, &src, sizeof(s4));
		packet->src_ipaddr.af = AF_INET;
		packet->src_ipaddr.ipaddr.ip4addr = s4.sin_addr;
		packet->src_port = ntohs(s4.sin_port);

#ifdef HAVE_STRUCT_SOCKADDR_IN6
	} else if (src.ss_family == AF_INET6) {
		struct sockaddr_in6	s6;

		memcpy(&s6, &src, sizeof(s6));
		packet->src_ipaddr.af = AF_INET6;
		packet->src_ipaddr.ipaddr.ip6addr = s6.sin6_addr;
		packet->src_port = ntohs(s6.sin6_port);

#endif
	} else {
		rad_free(&packet);
		close(newfd);
		return NULL;
	}

	packet->sockfd = newfd;

	/*
	 *	Note: Caller has to set dst_ipaddr && dst_port.
	 */
	return packet;
}
Exemple #5
0
/*
 *	Initialize a radclient data structure and add it to
 *	the global linked list.
 */
static int radclient_init(const char *filename)
{
	FILE *fp;
	VALUE_PAIR *vp;
	radclient_t *radclient;
	int filedone = 0;
	int packet_number = 1;

	assert(filename != NULL);

	/*
	 *	Determine where to read the VP's from.
	 */
	if (strcmp(filename, "-") != 0) {
		fp = fopen(filename, "r");
		if (!fp) {
			fprintf(stderr, "radclient: Error opening %s: %s\n",
				filename, strerror(errno));
			return 0;
		}
	} else {
		fp = stdin;
	}

	/*
	 *	Loop until the file is done.
	 */
	do {
		/*
		 *	Allocate it.
		 */
		radclient = malloc(sizeof(*radclient));
		if (!radclient) {
			perror("radclient: X");
			if (fp != stdin) fclose(fp);
			return 0;
		}
		memset(radclient, 0, sizeof(*radclient));

		radclient->request = rad_alloc(1);
		if (!radclient->request) {
			fr_perror("radclient: Y");
			free(radclient);
			if (fp != stdin) fclose(fp);
			return 0;
		}

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

		radclient->filename = filename;
		radclient->request->id = -1; /* allocate when sending */
		radclient->packet_number = packet_number++;

		/*
		 *	Read the VP's.
		 */
		radclient->request->vps = readvp2(fp, &filedone, "radclient:");
		if (!radclient->request->vps) {
			rad_free(&radclient->request);
			free(radclient);
			if (fp != stdin) fclose(fp);
			return 1;
		}

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

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

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

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

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

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

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

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

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

			case PW_PACKET_SRC_IPV6_ADDRESS:
				radclient->request->src_ipaddr.af = AF_INET6;
				radclient->request->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! */
				memmove(&vp->vp_octets[2], &vp->vp_octets[0],
					vp->length);
				vp->vp_octets[0] = vp->attribute - PW_DIGEST_REALM + 1;
				vp->length += 2;
				vp->vp_octets[1] = vp->length;
				vp->attribute = PW_DIGEST_ATTRIBUTES;
				break;
			}
		} /* loop over the VP's we read in */

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

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

	if (fp != stdin) fclose(fp);

	/*
	 *	And we're done.
	 */
	return 1;
}
/*
 *	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;
}
int main(int argc, char **argv)
{
	RADIUS_PACKET *req;
	char *p;
	int c;
	int port = 0;
	char *filename = NULL;
	FILE *fp;
	int count = 1;
	int id;
	int force_af = AF_UNSPEC;

	id = ((int)getpid() & 0xff);
	fr_debug_flag = 0;

	radlog_dest = L_DST_STDERR;

	while ((c = getopt(argc, argv, "46c:d:f:hi:qst:r:S:xXv")) != EOF)
	{
		switch(c) {
		case '4':
			force_af = AF_INET;
			break;
		case '6':
			force_af = AF_INET6;
			break;
		case 'c':
			if (!isdigit((int) *optarg))
				usage();
			count = atoi(optarg);
			break;
		case 'd':
			radius_dir = strdup(optarg);
			break;
		case 'f':
			filename = optarg;
			break;
		case 'q':
			do_output = 0;
			break;
		case 'x':
			debug_flag++;
			fr_debug_flag++;
			break;

		case 'X':
#if 0
		  sha1_data_problems = 1; /* for debugging only */
#endif
		  break;



		case 'r':
			if (!isdigit((int) *optarg))
				usage();
			retries = atoi(optarg);
			break;
		case 'i':
			if (!isdigit((int) *optarg))
				usage();
			id = atoi(optarg);
			if ((id < 0) || (id > 255)) {
				usage();
			}
			break;
		case 's':
			do_summary = 1;
			break;
		case 't':
			if (!isdigit((int) *optarg))
				usage();
			timeout = atof(optarg);
			break;
		case 'v':
			printf("radclient: $Id: 5b775f9943b9ba7fca66ce3ed72f1e7629a2f9f3 $ built on " __DATE__ " at " __TIME__ "\n");
			exit(0);
			break;
	       case 'S':
		       fp = fopen(optarg, "r");
		       if (!fp) {
			       fprintf(stderr, "radclient: Error opening %s: %s\n",
				       optarg, strerror(errno));
			       exit(1);
		       }
		       if (fgets(filesecret, sizeof(filesecret), fp) == NULL) {
			       fprintf(stderr, "radclient: Error reading %s: %s\n",
				       optarg, strerror(errno));
			       exit(1);
		       }
		       fclose(fp);

		       /* truncate newline */
		       p = filesecret + strlen(filesecret) - 1;
		       while ((p >= filesecret) &&
			      (*p < ' ')) {
			       *p = '\0';
			       --p;
		       }

		       if (strlen(filesecret) < 2) {
			       fprintf(stderr, "radclient: Secret in %s is too short\n", optarg);
			       exit(1);
		       }
		       secret = filesecret;
		       break;
		case 'h':
		default:
			usage();
			break;
		}
	}
	argc -= (optind - 1);
	argv += (optind - 1);

	if ((argc < 3)  ||
	    ((!secret) && (argc < 4))) {
		usage();
	}

	if (!radius_dir) radius_dir = strdup(RADDBDIR);

	if (dict_init(radius_dir, RADIUS_DICTIONARY) < 0) {
		fr_perror("radclient");
		return 1;
	}

	req = rad_alloc(NULL, 1);
	if (!req) {
		fr_perror("radclient");
		exit(1);
	}

#if 0
	{
		FILE *randinit;

		if((randinit = fopen("/dev/urandom", "r")) == NULL)
		{
			perror("/dev/urandom");
		} else {
			fread(randctx.randrsl, 256, 1, randinit);
			fclose(randinit);
		}
	}
	fr_randinit(&randctx, 1);
#endif

	req->id = id;

	/*
	 *	Resolve hostname.
	 */
	if (force_af == AF_UNSPEC) force_af = AF_INET;
	req->dst_ipaddr.af = force_af;
	if (strcmp(argv[1], "-") != 0) {
		char const *hostname = argv[1];
		char const *portname = argv[1];
		char buffer[256];

		if (*argv[1] == '[') { /* IPv6 URL encoded */
			p = strchr(argv[1], ']');
			if ((size_t) (p - argv[1]) >= sizeof(buffer)) {
				usage();
			}

			memcpy(buffer, argv[1] + 1, p - argv[1] - 1);
			buffer[p - argv[1] - 1] = '\0';

			hostname = buffer;
			portname = p + 1;

		}
		p = strchr(portname, ':');
		if (p && (strchr(p + 1, ':') == NULL)) {
			*p = '\0';
			portname = p + 1;
		} else {
			portname = NULL;
		}

		if (ip_hton(hostname, force_af, &req->dst_ipaddr) < 0) {
			fprintf(stderr, "radclient: Failed to find IP address for host %s: %s\n", hostname, strerror(errno));
			exit(1);
		}

		/*
		 *	Strip port from hostname if needed.
		 */
		if (portname) port = atoi(portname);
	}

	/*
	 *	See what kind of request we want to send.
	 */
	if (strcmp(argv[2], "auth") == 0) {
		if (port == 0) port = getport("radius");
		if (port == 0) port = PW_AUTH_UDP_PORT;
		req->code = PW_AUTHENTICATION_REQUEST;

	} else if (strcmp(argv[2], "acct") == 0) {
		if (port == 0) port = getport("radacct");
		if (port == 0) port = PW_ACCT_UDP_PORT;
		req->code = PW_ACCOUNTING_REQUEST;
		do_summary = 0;

	} else if (strcmp(argv[2], "status") == 0) {
		if (port == 0) port = getport("radius");
		if (port == 0) port = PW_AUTH_UDP_PORT;
		req->code = PW_STATUS_SERVER;

	} else if (strcmp(argv[2], "disconnect") == 0) {
		if (port == 0) port = PW_POD_UDP_PORT;
		req->code = PW_DISCONNECT_REQUEST;

	} else if (isdigit((int) argv[2][0])) {
		if (port == 0) port = getport("radius");
		if (port == 0) port = PW_AUTH_UDP_PORT;
		req->code = atoi(argv[2]);
	} else {
		usage();
	}
	req->dst_port = port;

	/*
	 *	Add the secret.
	 */
	if (argv[3]) secret = argv[3];

	/*
	 *	Read valuepairs.
	 *	Maybe read them, from stdin, if there's no
	 *	filename, or if the filename is '-'.
	 */
	if (filename && (strcmp(filename, "-") != 0)) {
		fp = fopen(filename, "r");
		if (!fp) {
			fprintf(stderr, "radclient: Error opening %s: %s\n",
				filename, strerror(errno));
			exit(1);
		}
	} else {
		fp = stdin;
	}

	/*
	 *	Send request.
	 */
	if ((req->sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
		perror("radclient: socket: ");
		exit(1);
	}

	while(!filedone) {
		if(req->vps) pairfree(&req->vps);

		if ((req->vps = readvp2(fp, &filedone, "radeapclient:"))
		    == NULL) {
			break;
		}

		sendrecv_eap(req);
	}

	free(radius_dir);
	if(do_summary) {
		printf("\n\t   Total approved auths:  %d\n", totalapp);
		printf("\t     Total denied auths:  %d\n", totaldeny);
	}
	return 0;
}
static void got_packet(UNUSED uint8_t *args, const struct pcap_pkthdr *header, const uint8_t *data)
{

	static int count = 1;			/* Packets seen */
	
	/*
	 *  Define pointers for packet's attributes
	 */
	const struct ip_header *ip;		/* The IP header */
	const struct udp_header *udp;		/* The UDP header */
	const uint8_t *payload;			/* Packet payload */
	
	/*
	 *  And define the size of the structures we're using
	 */
	int size_ethernet = sizeof(struct ethernet_header);
	int size_ip = sizeof(struct ip_header);
	int size_udp = sizeof(struct udp_header);
	
	/*
	 *  For FreeRADIUS
	 */
	RADIUS_PACKET *packet, *original;
	struct timeval elapsed;

	/*
	 * Define our packet's attributes
	 */

	if ((data[0] == 2) && (data[1] == 0) &&
	    (data[2] == 0) && (data[3] == 0)) {
		ip = (const struct ip_header*) (data + 4);

	} else {
		ip = (const struct ip_header*)(data + size_ethernet);
	}
	
	udp = (const struct udp_header*)(((const uint8_t *) ip) + size_ip);
	payload = (const uint8_t *)(((const uint8_t *) udp) + size_udp);

	packet = rad_alloc(NULL, 0);
	if (!packet) {
		fprintf(stderr, "Out of memory\n");
		return;
	}

	packet->src_ipaddr.af = AF_INET;
	packet->src_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_src.s_addr;
	packet->src_port = ntohs(udp->udp_sport);
	packet->dst_ipaddr.af = AF_INET;
	packet->dst_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_dst.s_addr;
	packet->dst_port = ntohs(udp->udp_dport);

	memcpy(&packet->data, &payload, sizeof(packet->data));
	packet->data_len = header->len - (payload - data);

	if (!rad_packet_ok(packet, 0)) {
		DEBUG(log_dst, "Packet: %s\n", fr_strerror());
		
		DEBUG(log_dst, "  From     %s:%d\n", inet_ntoa(ip->ip_src), ntohs(udp->udp_sport));
		DEBUG(log_dst, "  To:      %s:%d\n", inet_ntoa(ip->ip_dst), ntohs(udp->udp_dport));
		DEBUG(log_dst, "  Type:    %s\n", fr_packet_codes[packet->code]);

		rad_free(&packet);
		return;
	}
	
	switch (packet->code) {
	case PW_COA_REQUEST:
		/* we need a 16 x 0 byte vector for decrypting encrypted VSAs */
		original = nullpacket;
		break;
	case PW_AUTHENTICATION_ACK:
		/* look for a matching request and use it for decoding */
		original = rbtree_finddata(request_tree, packet);
		break;
	case PW_AUTHENTICATION_REQUEST:
		/* save the request for later matching */
		original = rad_alloc_reply(NULL, packet);
		if (original) { /* just ignore allocation failures */
			rbtree_deletebydata(request_tree, original);
			rbtree_insert(request_tree, original);
		}
		/* fallthrough */
	default:
		/* don't attempt to decode any encrypted attributes */
		original = NULL;
	}

	/*
	 *  Decode the data without bothering to check the signatures.
	 */
	if (rad_decode(packet, original, radius_secret) != 0) {
		rad_free(&packet);
		fr_perror("decode");
		return;
	}

	/*
	 *  We've seen a successfull reply to this, so delete it now
	 */
	if (original)
		rbtree_deletebydata(request_tree, original);

	if (filter_vps && filter_packet(packet)) {
		rad_free(&packet);
		DEBUG(log_dst, "Packet number %d doesn't match\n", count++);
		return;
	}

	if (out) {
		pcap_dump((void *) out, header, data);
		goto check_filter;
	}

	INFO(log_dst, "%s Id %d\t", fr_packet_codes[packet->code], packet->id);

	/*
	 *  Print the RADIUS packet
	 */
	INFO(log_dst, "%s:%d -> ", inet_ntoa(ip->ip_src), ntohs(udp->udp_sport));
	INFO(log_dst, "%s:%d", inet_ntoa(ip->ip_dst), ntohs(udp->udp_dport));
	
	DEBUG1(log_dst, "\t(%d packets)", count++);
	
	if (!start_pcap.tv_sec) {
		start_pcap = header->ts;
	}

	tv_sub(&header->ts, &start_pcap, &elapsed);

	INFO(log_dst, "\t+%u.%03u", (unsigned int) elapsed.tv_sec,
	       (unsigned int) elapsed.tv_usec / 1000);
	
	if (fr_debug_flag > 1) {
		DEBUG(log_dst, "\n");
		if (packet->vps) {
			if (do_sort) sort(packet);
	
			vp_printlist(log_dst, packet->vps);
			pairfree(&packet->vps);
		}
	}
	
	INFO(log_dst, "\n");
	
	if (!to_stdout && (fr_debug_flag > 4)) {
		rad_print_hex(packet);
	}
	
	fflush(log_dst);

 check_filter:
	/*
	 *  If we're doing filtering, Access-Requests are cached in the
	 *  filter tree.
	 */
	if (!filter_vps ||
	    ((packet->code != PW_AUTHENTICATION_REQUEST) &&
	     (packet->code != PW_ACCOUNTING_REQUEST))) {
		rad_free(&packet);
	}
}
/*
 *	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 = rad_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! */
			{
				DICT_ATTR 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 = dict_attrbyvalue(PW_DIGEST_ATTRIBUTES, 0);
				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:
					REDEBUG("Can't determine expected response to Status-Server request, specify "
					        "a well known RADIUS port, or add a Response-Packet-Type attribute "
					        "to the request of filter");
					goto error;
				}
				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;
}
int main(int argc, char **argv)
{
	RADIUS_PACKET *req;
	char *p;
	int c;
	int port = 0;
	char *filename = NULL;
	FILE *fp;
	int id;
	int force_af = AF_UNSPEC;

	/*
	 *	We probably don't want to free the talloc autofree context
	 *	directly, so we'll allocate a new context beneath it, and
	 *	free that before any leak reports.
	 */
	TALLOC_CTX *autofree = talloc_init("main");

	id = ((int)getpid() & 0xff);
	fr_debug_flag = 0;

	radlog_dest = L_DST_STDERR;

	set_radius_dir(autofree, RADIUS_DIR);

	while ((c = getopt(argc, argv, "46c:d:D:f:hi:qst:r:S:xXv")) != EOF)
	{
		switch(c) {
		case '4':
			force_af = AF_INET;
			break;
		case '6':
			force_af = AF_INET6;
			break;
		case 'd':
			set_radius_dir(autofree, optarg);
			break;
		case 'D':
			mainconfig.dictionary_dir = talloc_typed_strdup(NULL, optarg);
			break;
		case 'f':
			filename = optarg;
			break;
		case 'q':
			do_output = 0;
			break;
		case 'x':
			debug_flag++;
			fr_debug_flag++;
			break;

		case 'X':
#if 0
		  sha1_data_problems = 1; /* for debugging only */
#endif
		  break;



		case 'r':
			if (!isdigit((int) *optarg))
				usage();
			retries = atoi(optarg);
			break;
		case 'i':
			if (!isdigit((int) *optarg))
				usage();
			id = atoi(optarg);
			if ((id < 0) || (id > 255)) {
				usage();
			}
			break;
		case 's':
			do_summary = 1;
			break;
		case 't':
			if (!isdigit((int) *optarg))
				usage();
			timeout = atof(optarg);
			break;
		case 'v':
			printf("radeapclient: $Id: 20c518ef414933fae3cdf564852c12e483f7c8c9 $ built on " __DATE__ " at " __TIME__ "\n");
			exit(0);
			break;
	       case 'S':
		       fp = fopen(optarg, "r");
		       if (!fp) {
			       fprintf(stderr, "radclient: Error opening %s: %s\n",
				       optarg, fr_syserror(errno));
			       exit(1);
		       }
		       if (fgets(filesecret, sizeof(filesecret), fp) == NULL) {
			       fprintf(stderr, "radclient: Error reading %s: %s\n",
				       optarg, fr_syserror(errno));
			       exit(1);
		       }
		       fclose(fp);

		       /* truncate newline */
		       p = filesecret + strlen(filesecret) - 1;
		       while ((p >= filesecret) &&
			      (*p < ' ')) {
			       *p = '\0';
			       --p;
		       }

		       if (strlen(filesecret) < 2) {
			       fprintf(stderr, "radclient: Secret in %s is too short\n", optarg);
			       exit(1);
		       }
		       secret = filesecret;
		       break;
		case 'h':
		default:
			usage();
			break;
		}
	}
	argc -= (optind - 1);
	argv += (optind - 1);

	if ((argc < 3)  ||
	    ((!secret) && (argc < 4))) {
		usage();
	}

	if (!mainconfig.dictionary_dir) {
		mainconfig.dictionary_dir = DICTDIR;
	}

	/*
	 *	Read the distribution dictionaries first, then
	 *	the ones in raddb.
	 */
	DEBUG2("including dictionary file %s/%s", mainconfig.dictionary_dir, RADIUS_DICTIONARY);
	if (dict_init(mainconfig.dictionary_dir, RADIUS_DICTIONARY) != 0) {
		ERROR("Errors reading dictionary: %s",
		      fr_strerror());
		exit(1);
	}

	/*
	 *	It's OK if this one doesn't exist.
	 */
	int rcode = dict_read(radius_dir, RADIUS_DICTIONARY);
	if (rcode == -1) {
		ERROR("Errors reading %s/%s: %s", radius_dir, RADIUS_DICTIONARY,
		      fr_strerror());
		exit(1);
	}

	/*
	 *	We print this after reading it.  That way if
	 *	it doesn't exist, it's OK, and we don't print
	 *	anything.
	 */
	if (rcode == 0) {
		DEBUG2("including dictionary file %s/%s", radius_dir, RADIUS_DICTIONARY);
	}

	req = rad_alloc(NULL, 1);
	if (!req) {
		fr_perror("radclient");
		exit(1);
	}

#if 0
	{
		FILE *randinit;

		if((randinit = fopen("/dev/urandom", "r")) == NULL)
		{
			perror("/dev/urandom");
		} else {
			fread(randctx.randrsl, 256, 1, randinit);
			fclose(randinit);
		}
	}
	fr_randinit(&randctx, 1);
#endif

	req->id = id;

	/*
	 *	Resolve hostname.
	 */
	if (force_af == AF_UNSPEC) force_af = AF_INET;
	req->dst_ipaddr.af = force_af;
	if (strcmp(argv[1], "-") != 0) {
		char const *hostname = argv[1];
		char const *portname = argv[1];
		char buffer[256];

		if (*argv[1] == '[') { /* IPv6 URL encoded */
			p = strchr(argv[1], ']');
			if ((size_t) (p - argv[1]) >= sizeof(buffer)) {
				usage();
			}

			memcpy(buffer, argv[1] + 1, p - argv[1] - 1);
			buffer[p - argv[1] - 1] = '\0';

			hostname = buffer;
			portname = p + 1;

		}
		p = strchr(portname, ':');
		if (p && (strchr(p + 1, ':') == NULL)) {
			*p = '\0';
			portname = p + 1;
		} else {
			portname = NULL;
		}

		if (ip_hton(hostname, force_af, &req->dst_ipaddr) < 0) {
			fprintf(stderr, "radclient: Failed to find IP address for host %s: %s\n", hostname, fr_syserror(errno));
			exit(1);
		}

		/*
		 *	Strip port from hostname if needed.
		 */
		if (portname) port = atoi(portname);
	}

	/*
	 *	See what kind of request we want to send.
	 */
	if (strcmp(argv[2], "auth") == 0) {
		if (port == 0) port = getport("radius");
		if (port == 0) port = PW_AUTH_UDP_PORT;
		req->code = PW_CODE_AUTHENTICATION_REQUEST;

	} else if (strcmp(argv[2], "acct") == 0) {
		if (port == 0) port = getport("radacct");
		if (port == 0) port = PW_ACCT_UDP_PORT;
		req->code = PW_CODE_ACCOUNTING_REQUEST;
		do_summary = 0;

	} else if (strcmp(argv[2], "status") == 0) {
		if (port == 0) port = getport("radius");
		if (port == 0) port = PW_AUTH_UDP_PORT;
		req->code = PW_CODE_STATUS_SERVER;

	} else if (strcmp(argv[2], "disconnect") == 0) {
		if (port == 0) port = PW_POD_UDP_PORT;
		req->code = PW_CODE_DISCONNECT_REQUEST;

	} else if (isdigit((int) argv[2][0])) {
		if (port == 0) port = getport("radius");
		if (port == 0) port = PW_AUTH_UDP_PORT;
		req->code = atoi(argv[2]);
	} else {
		usage();
	}
	req->dst_port = port;

	/*
	 *	Add the secret.
	 */
	if (argv[3]) secret = argv[3];

	/*
	 *	Read valuepairs.
	 *	Maybe read them, from stdin, if there's no
	 *	filename, or if the filename is '-'.
	 */
	if (filename && (strcmp(filename, "-") != 0)) {
		fp = fopen(filename, "r");
		if (!fp) {
			fprintf(stderr, "radclient: Error opening %s: %s\n",
				filename, fr_syserror(errno));
			exit(1);
		}
	} else {
		fp = stdin;
	}

	/*
	 *	Send request.
	 */
	if ((req->sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
		perror("radclient: socket: ");
		exit(1);
	}

	while(!filedone) {
		if(req->vps) pairfree(&req->vps);

		if ((req->vps = readvp2(NULL, fp, &filedone, "radeapclient:")) == NULL) {
			break;
		}

		sendrecv_eap(req);
	}

	if(do_summary) {
		printf("\n\t   Total approved auths:  %d\n", totalapp);
		printf("\t     Total denied auths:  %d\n", totaldeny);
	}

	talloc_free(autofree);

	return 0;
}
static void rs_process_packet(rs_event_t *event, struct pcap_pkthdr const *header, uint8_t const *data)
{

	static int count = 0;			/* Packets seen */
	rs_stats_t *stats = event->stats;
	decode_fail_t reason;

	/*
	 *	Pointers into the packet data we just received
	 */
	size_t len;
	uint8_t const		*p = data;
	struct ip_header const	*ip = NULL;	/* The IP header */
	struct ip_header6 const	*ip6 = NULL;	/* The IPv6 header */
	struct udp_header const	*udp;		/* The UDP header */
	uint8_t			version;	/* IP header version */
	bool			response = false;	/* Was it a response code */

	RADIUS_PACKET *current, *original;
	struct timeval elapsed;
	struct timeval latency;

	count++;

	if (header->caplen <= 5) {
		INFO("Packet too small, captured %i bytes", header->caplen);
		return;
	}

	/*
	 *	Loopback header
	 */
	if ((p[0] == 2) && (p[1] == 0) && (p[2] == 0) && (p[3] == 0)) {
		p += 4;
	/*
	 *	Ethernet header
	 */
	} else {
		p += sizeof(struct ethernet_header);
	}

	version = (p[0] & 0xf0) >> 4;
	switch (version) {
	case 4:
		ip = (struct ip_header const *)p;
		len = (0x0f & ip->ip_vhl) * 4;	/* ip_hl specifies length in 32bit words */
		p += len;
		break;

	case 6:
		ip6 = (struct ip_header6 const *)p;
		p += sizeof(struct ip_header6);

		break;

	default:
		DEBUG("IP version invalid %i", version);
		return;
	}

	/*
	 *	End of variable length bits, do basic check now to see if packet looks long enough
	 */
	len = (p - data) + sizeof(struct udp_header) + (sizeof(radius_packet_t) - 1);	/* length value */
	if (len > header->caplen) {
		DEBUG("Packet too small, we require at least %zu bytes, captured %i bytes",
		      (size_t) len, header->caplen);
		return;
	}

	udp = (struct udp_header const *)p;
	p += sizeof(struct udp_header);

	/*
	 *	With artificial talloc memory limits there's a good chance we can
	 *	recover once some requests timeout, so make an effort to deal
	 *	with allocation failures gracefully.
	 */
	current = rad_alloc(NULL, 0);
	if (!current) {
		ERROR("Failed allocating memory to hold decoded packet");
		return;
	}
	current->timestamp = header->ts;
	current->data_len = header->caplen - (data - p);
	memcpy(&current->data, &p, sizeof(current->data));

	/*
	 *	Populate IP/UDP fields from PCAP data
	 */
	if (ip) {
		current->src_ipaddr.af = AF_INET;
		current->src_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_src.s_addr;

		current->dst_ipaddr.af = AF_INET;
		current->dst_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_dst.s_addr;
	} else {
		current->src_ipaddr.af = AF_INET6;
		memcpy(&current->src_ipaddr.ipaddr.ip6addr.s6_addr, &ip6->ip_src.s6_addr,
		       sizeof(current->src_ipaddr.ipaddr.ip6addr.s6_addr));

		current->dst_ipaddr.af = AF_INET6;
		memcpy(&current->dst_ipaddr.ipaddr.ip6addr.s6_addr, &ip6->ip_dst.s6_addr,
		       sizeof(current->dst_ipaddr.ipaddr.ip6addr.s6_addr));
	}

	current->src_port = ntohs(udp->udp_sport);
	current->dst_port = ntohs(udp->udp_dport);

	if (!rad_packet_ok(current, 0, &reason)) {
		DEBUG("(%i) ** %s **", count, fr_strerror());

		DEBUG("(%i) %s Id %i %s:%s:%d -> %s:%d\t+%u.%03u", count,
		      fr_packet_codes[current->code], current->id,
		      event->in->name,
		      fr_inet_ntop(current->src_ipaddr.af, &current->src_ipaddr.ipaddr), current->src_port,
		      fr_inet_ntop(current->dst_ipaddr.af, &current->dst_ipaddr.ipaddr), current->dst_port,
		      (unsigned int) elapsed.tv_sec, ((unsigned int) elapsed.tv_usec / 1000));

		rad_free(&current);
		return;
	}

	switch (current->code) {
	case PW_CODE_COA_REQUEST:
		/* we need a 16 x 0 byte vector for decrypting encrypted VSAs */
		original = nullpacket;
		break;
	case PW_CODE_ACCOUNTING_RESPONSE:
	case PW_CODE_AUTHENTICATION_REJECT:
	case PW_CODE_AUTHENTICATION_ACK:
		response = true;
		/* look for a matching request and use it for decoding */
		original = rbtree_finddata(request_tree, current);
		break;
	case PW_CODE_ACCOUNTING_REQUEST:
	case PW_CODE_AUTHENTICATION_REQUEST:
		/* save the request for later matching */
		original = rad_alloc_reply(event->conf, current);
		original->timestamp = header->ts;
		if (original) { /* just ignore allocation failures */
			rbtree_deletebydata(request_tree, original);
			rbtree_insert(request_tree, original);
		}
		/* fallthrough */
	default:
		/* don't attempt to decode any encrypted attributes */
		original = NULL;
	}

	/*
	 *  Decode the data without bothering to check the signatures.
	 */
	if (rad_decode(current, original, event->conf->radius_secret) != 0) {
		rad_free(&current);
		fr_perror("decode");
		return;
	}

	if (filter_vps && rs_filter_packet(current)) {
		rad_free(&current);
		DEBUG("Packet number %d doesn't match", count++);
		return;
	}

	if (event->out) {
		pcap_dump((void *) (event->out->dumper), header, data);
		goto check_filter;
	}

	rs_tv_sub(&header->ts, &start_pcap, &elapsed);

	rs_stats_update_count(&stats->gauge, current);
	if (original) {
		rs_tv_sub(&current->timestamp, &original->timestamp, &latency);

		/*
		 *	Update stats for both the request and response types.
		 *
		 *	This isn't useful for things like Access-Requests, but will be useful for
		 *	CoA and Disconnect Messages, as we get the average latency across both
		 *	response types.
		 *
		 *	It also justifies allocating 255 instances rs_latency_t.
		 */
		rs_stats_update_latency(&stats->exchange[current->code], &latency);
		rs_stats_update_latency(&stats->exchange[original->code], &latency);

		/*
		 *	Print info about the response.
		 */
		DEBUG("(%i) %s Id %i %s:%s:%d %s %s:%d\t+%u.%03u\t+%u.%03u", count,
		      fr_packet_codes[current->code], current->id,
		      event->in->name,
		      fr_inet_ntop(current->src_ipaddr.af, &current->src_ipaddr.ipaddr), current->src_port,
		      response ? "<-" : "->",
		      fr_inet_ntop(current->dst_ipaddr.af, &current->dst_ipaddr.ipaddr), current->dst_port,
		      (unsigned int) elapsed.tv_sec, ((unsigned int) elapsed.tv_usec / 1000),
		      (unsigned int) latency.tv_sec, ((unsigned int) latency.tv_usec / 1000));
	/*
	 *	It's the original request
	 */
	} else {
		/*
		 *	Print info about the request
		 */
		DEBUG("(%i) %s Id %i %s:%s:%d %s %s:%d\t+%u.%03u", count,
		      fr_packet_codes[current->code], current->id,
		      event->in->name,
		      fr_inet_ntop(current->src_ipaddr.af, &current->src_ipaddr.ipaddr), current->src_port,
		      response ? "<-" : "->",
		      fr_inet_ntop(current->dst_ipaddr.af, &current->dst_ipaddr.ipaddr), current->dst_port,
		      (unsigned int) elapsed.tv_sec, ((unsigned int) elapsed.tv_usec / 1000));
	}

	if (fr_debug_flag > 1) {
		if (current->vps) {
			if (event->conf->do_sort) {
				pairsort(&current->vps, true);
			}
			vp_printlist(log_dst, current->vps);
			pairfree(&current->vps);
		}
	}

	/*
	 *  We've seen a successful reply to this, so delete it now
	 */
	if (original) {
		rbtree_deletebydata(request_tree, original);
	}

	if (!event->conf->to_stdout && (fr_debug_flag > 4)) {
		rad_print_hex(current);
	}

	fflush(log_dst);

check_filter:
	/*
	 *  If we're doing filtering, Access-Requests are cached in the
	 *  filter tree.
	 */
	if (!filter_vps ||
	    ((current->code != PW_CODE_AUTHENTICATION_REQUEST) && (current->code != PW_CODE_ACCOUNTING_REQUEST))) {
		rad_free(&current);
	}
}
/** Copy packet to multiple servers
 *
 * Create a duplicate of the packet and send it to a list of realms
 * defined by the presence of the Replicate-To-Realm VP in the control
 * list of the current request.
 *
 * This is pretty hacky and is 100% fire and forget. If you're looking
 * to forward authentication requests to multiple realms and process
 * the responses, this function will not allow you to do that.
 *
 * @param[in] instance 	of this module.
 * @param[in] request 	The current request.
 * @param[in] list	of attributes to copy to the duplicate packet.
 * @param[in] code	to write into the code field of the duplicate packet.
 * @return RCODE fail on error, invalid if list does not exist, noop if no
 * 	   replications succeeded, else ok.
 */
static int replicate_packet(UNUSED void *instance, REQUEST *request,
			    pair_lists_t list, unsigned int code)
{
	int rcode = RLM_MODULE_NOOP;
	VALUE_PAIR *vp, **vps, *last;
	home_server *home;
	REALM *realm;
	home_pool_t *pool;
	RADIUS_PACKET *packet = NULL;

	last = request->config_items;

	/*
	 *	Send as many packets as necessary to different
	 *	destinations.
	 */
	while (1) {
		vp = pairfind(last, PW_REPLICATE_TO_REALM, 0, TAG_ANY);
		if (!vp) break;

		last = vp->next;

		realm = realm_find2(vp->vp_strvalue);
		if (!realm) {
			RDEBUG2E("Cannot Replicate to unknown realm %s", realm);
			continue;
		}
		
		/*
		 *	We shouldn't really do this on every loop.
		 */
		switch (request->packet->code) {
		default:
			RDEBUG2E("Cannot replicate unknown packet code %d",
				request->packet->code);
			cleanup(packet);
			return RLM_MODULE_FAIL;
		
		case PW_AUTHENTICATION_REQUEST:
			pool = realm->auth_pool;
			break;
			
#ifdef WITH_ACCOUNTING
			
		case PW_ACCOUNTING_REQUEST:
			pool = realm->acct_pool;
			break;
#endif
			
#ifdef WITH_COA
		case PW_COA_REQUEST:
		case PW_DISCONNECT_REQUEST:
			pool = realm->acct_pool;
			break;
#endif
		}
		
		if (!pool) {
			RDEBUG2W("Cancelling replication to Realm %s, as the realm is local.", realm->name);
			continue;
		}
		
		home = home_server_ldb(realm->name, pool, request);
		if (!home) {
			RDEBUG2E("Failed to find live home server for realm %s",
				realm->name);
			continue;
		}
		
		/*
		 *	For replication to multiple servers we re-use the packet
		 *	we built here.
		 */
		if (!packet) {
			packet = rad_alloc(NULL, 1);
			if (!packet) return RLM_MODULE_FAIL;
			packet->sockfd = -1;
			packet->code = code;
			packet->id = fr_rand() & 0xff;

			packet->sockfd = fr_socket(&home->src_ipaddr, 0);
			if (packet->sockfd < 0) {
				RDEBUGE("Failed opening socket: %s", fr_strerror());
				rcode = RLM_MODULE_FAIL;
				goto done;
			}
			
			vps = radius_list(request, list);
			if (!vps) {
				RDEBUGW("List '%s' doesn't exist for "
				       "this packet", fr_int2str(pair_lists,
				       list, "?unknown?"));
				rcode = RLM_MODULE_INVALID;
				goto done;
			}
			
			/*
			 *	Don't assume the list actually contains any
			 *	attributes.
			 */
			if (*vps) {
				packet->vps = paircopy(packet, *vps);
				if (!packet->vps) {
					rcode = RLM_MODULE_FAIL;
					goto done;
				}
			}
			


			/*
			 *	For CHAP, create the CHAP-Challenge if
			 *	it doesn't exist.
			 */
			if ((code == PW_AUTHENTICATION_REQUEST) &&
			    (pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY) != NULL) &&
			    (pairfind(request->packet->vps, PW_CHAP_CHALLENGE, 0, TAG_ANY) == NULL)) {
				vp = radius_paircreate(request, &packet->vps,
						       PW_CHAP_CHALLENGE, 0);
				vp->length = AUTH_VECTOR_LEN;
				memcpy(vp->vp_strvalue, request->packet->vector,
				       AUTH_VECTOR_LEN);
			}
		} else {
			size_t i;

			for (i = 0; i < sizeof(packet->vector); i++) {
				packet->vector[i] = fr_rand() & 0xff;
			}

			packet->id++;
			free(packet->data);
			packet->data = NULL;
			packet->data_len = 0;
		}

		/*
		 *	(Re)-Write these.
		 */
		packet->dst_ipaddr = home->ipaddr;
		packet->dst_port = home->port;
		memset(&packet->src_ipaddr, 0, sizeof(packet->src_ipaddr));
		packet->src_port = 0;
		
		/*
		 *	Encode, sign and then send the packet.
		 */
		RDEBUG("Replicating list '%s' to Realm '%s'",
		       fr_int2str(pair_lists, list, "¿unknown?"),realm->name);
		if (rad_send(packet, NULL, home->secret) < 0) {
			RDEBUGE("Failed replicating packet: %s",
			       fr_strerror());
			rcode = RLM_MODULE_FAIL;
			goto done;
		}

		/*
		 *	We've sent it to at least one destination.
		 */
		rcode = RLM_MODULE_OK;
	}
	
	done:
	
	cleanup(packet);
	return rcode;
}
/*
 *	Write accounting information to this modules database.
 */
static int replicate_packet(void *instance, REQUEST *request)
{
    int rcode = RLM_MODULE_NOOP;
    VALUE_PAIR *vp, *last;
    home_server *home;
    REALM *realm;
    home_pool_t *pool;
    RADIUS_PACKET *packet = NULL;

    instance = instance;	/* -Wunused */
    last = request->config_items;

    /*
     *	Send as many packets as necessary to different
     *	destinations.
     */
    while (1) {
        vp = pairfind(last, PW_REPLICATE_TO_REALM, 0);
        if (!vp) break;

        last = vp->next;

        realm = realm_find2(vp->vp_strvalue);
        if (!realm) {
            RDEBUG2("ERROR: Cannot Replicate to unknown realm %s", realm);
            continue;
        }

        /*
         *	We shouldn't really do this on every loop.
         */
        switch (request->packet->code) {
        default:
            RDEBUG2("ERROR: Cannot replicate unknown packet code %d",
                    request->packet->code);
            cleanup(packet);
            return RLM_MODULE_FAIL;

        case PW_AUTHENTICATION_REQUEST:
            pool = realm->auth_pool;
            break;

#ifdef WITH_ACCOUNTING

        case PW_ACCOUNTING_REQUEST:
            pool = realm->acct_pool;
            break;
#endif

#ifdef WITH_COA
        case PW_COA_REQUEST:
        case PW_DISCONNECT_REQUEST:
            pool = realm->acct_pool;
            break;
#endif
        }

        if (!pool) {
            RDEBUG2(" WARNING: Cancelling replication to Realm %s, as the realm is local.", realm->name);
            continue;
        }

        home = home_server_ldb(realm->name, pool, request);
        if (!home) {
            RDEBUG2("ERROR: Failed to find live home server for realm %s",
                    realm->name);
            continue;
        }

        if (!packet) {
            packet = rad_alloc(1);
            if (!packet) return RLM_MODULE_FAIL;
            packet->sockfd = -1;
            packet->code = request->packet->code;
            packet->id = fr_rand() & 0xff;

            packet->sockfd = fr_socket(&home->src_ipaddr, 0);
            if (packet->sockfd < 0) {
                RDEBUG("ERROR: Failed opening socket: %s", fr_strerror());
                cleanup(packet);
                return RLM_MODULE_FAIL;
            }

            packet->vps = paircopy(request->packet->vps);
            if (!packet->vps) {
                RDEBUG("ERROR: Out of memory!");
                cleanup(packet);
                return RLM_MODULE_FAIL;
            }

            /*
             *	For CHAP, create the CHAP-Challenge if
             *	it doesn't exist.
             */
            if ((request->packet->code == PW_AUTHENTICATION_REQUEST) &&
                    (pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0) != NULL) &&
                    (pairfind(request->packet->vps, PW_CHAP_CHALLENGE, 0) == NULL)) {
                vp = radius_paircreate(request, &packet->vps,
                                       PW_CHAP_CHALLENGE, 0,
                                       PW_TYPE_OCTETS);
                vp->length = AUTH_VECTOR_LEN;
                memcpy(vp->vp_strvalue, request->packet->vector,
                       AUTH_VECTOR_LEN);
            }
        } else {
            size_t i;

            for (i = 0; i < sizeof(packet->vector); i++) {
                packet->vector[i] = fr_rand() & 0xff;
            }

            packet->id++;
            free(packet->data);
            packet->data = NULL;
            packet->data_len = 0;
        }

        /*
         *	(Re)-Write these.
         */
        packet->dst_ipaddr = home->ipaddr;
        packet->dst_port = home->port;
        memset(&packet->src_ipaddr, 0, sizeof(packet->src_ipaddr));
        packet->src_port = 0;

        /*
         *	Encode, sign and then send the packet.
         */
        RDEBUG("Replicating packet to Realm %s", realm->name);
        if (rad_send(packet, NULL, home->secret) < 0) {
            RDEBUG("ERROR: Failed replicating packet: %s",
                   fr_strerror());
            cleanup(packet);
            return RLM_MODULE_FAIL;
        }

        /*
         *	We've sent it to at least one destination.
         */
        rcode = RLM_MODULE_OK;
    }

    cleanup(packet);
    return rcode;
}
static int tls_socket_recv(rad_listen_t *listener)
{
	bool doing_init = false;
	ssize_t rcode;
	RADIUS_PACKET *packet;
	REQUEST *request;
	listen_socket_t *sock = listener->data;
	fr_tls_status_t status;
	RADCLIENT *client = sock->client;

	if (!sock->packet) {
		sock->packet = rad_alloc(sock, false);
		if (!sock->packet) return 0;

		sock->packet->sockfd = listener->fd;
		sock->packet->src_ipaddr = sock->other_ipaddr;
		sock->packet->src_port = sock->other_port;
		sock->packet->dst_ipaddr = sock->my_ipaddr;
		sock->packet->dst_port = sock->my_port;

		if (sock->request) sock->request->packet = talloc_steal(sock->request, sock->packet);
	}

	/*
	 *	Allocate a REQUEST for debugging, and initialize the TLS session.
	 */
	if (!sock->request) {
		sock->request = request = request_alloc(sock);
		if (!sock->request) {
			ERROR("Out of memory");
			return 0;
		}

		rad_assert(request->packet == NULL);
		rad_assert(sock->packet != NULL);
		request->packet = talloc_steal(request, sock->packet);

		request->component = "<core>";
		request->component = "<tls-connect>";

		request->reply = rad_alloc(request, false);
		if (!request->reply) return 0;

		rad_assert(sock->ssn == NULL);

		sock->ssn = tls_new_session(sock, listener->tls, sock->request,
					    listener->tls->require_client_cert);
		if (!sock->ssn) {
			TALLOC_FREE(sock->request);
			sock->packet = NULL;
			return 0;
		}

		SSL_set_ex_data(sock->ssn->ssl, FR_TLS_EX_INDEX_REQUEST, (void *)request);
		SSL_set_ex_data(sock->ssn->ssl, fr_tls_ex_index_certs, (void *) &sock->certs);
		SSL_set_ex_data(sock->ssn->ssl, FR_TLS_EX_INDEX_TALLOC, sock->parent);

		doing_init = true;
	}

	rad_assert(sock->request != NULL);
	rad_assert(sock->request->packet != NULL);
	rad_assert(sock->packet != NULL);
	rad_assert(sock->ssn != NULL);

	request = sock->request;

	RDEBUG3("Reading from socket %d", request->packet->sockfd);
	PTHREAD_MUTEX_LOCK(&sock->mutex);
	rcode = read(request->packet->sockfd,
		     sock->ssn->dirty_in.data,
		     sizeof(sock->ssn->dirty_in.data));
	if ((rcode < 0) && (errno == ECONNRESET)) {
	do_close:
		PTHREAD_MUTEX_UNLOCK(&sock->mutex);
		DEBUG("Closing TLS socket from client port %u", sock->other_port);
		tls_socket_close(listener);
		PTHREAD_MUTEX_UNLOCK(&sock->mutex);
		return 0;
	}

	if (rcode < 0) {
		RDEBUG("Error reading TLS socket: %s", fr_syserror(errno));
		goto do_close;
	}

	/*
	 *	Normal socket close.
	 */
	if (rcode == 0) goto do_close;

	sock->ssn->dirty_in.used = rcode;

	dump_hex("READ FROM SSL", sock->ssn->dirty_in.data, sock->ssn->dirty_in.used);

	/*
	 *	Catch attempts to use non-SSL.
	 */
	if (doing_init && (sock->ssn->dirty_in.data[0] != handshake)) {
		RDEBUG("Non-TLS data sent to TLS socket: closing");
		goto do_close;
	}

	/*
	 *	If we need to do more initialization, do that here.
	 */
	if (!SSL_is_init_finished(sock->ssn->ssl)) {
		if (!tls_handshake_recv(request, sock->ssn)) {
			RDEBUG("FAILED in TLS handshake receive");
			goto do_close;
		}

		/*
		 *	More ACK data to send.  Do so.
		 */
		if (sock->ssn->dirty_out.used > 0) {
			tls_socket_write(listener, request);
			PTHREAD_MUTEX_UNLOCK(&sock->mutex);
			return 0;
		}

		/*
		 *	FIXME: Run the request through a virtual
		 *	server in order to see if we like the
		 *	certificate presented by the client.
		 */
	}

	/*
	 *	Try to get application data.
	 */
	status = tls_application_data(sock->ssn, request);
	RDEBUG("Application data status %d", status);

	if (status == FR_TLS_MORE_FRAGMENTS) {
		PTHREAD_MUTEX_UNLOCK(&sock->mutex);
		return 0;
	}

	if (sock->ssn->clean_out.used == 0) {
		PTHREAD_MUTEX_UNLOCK(&sock->mutex);
		return 0;
	}

	/*
	 *	We now have a bunch of application data.
	 */
	dump_hex("TUNNELED DATA > ", sock->ssn->clean_out.data, sock->ssn->clean_out.used);

	/*
	 *	If the packet is a complete RADIUS packet, return it to
	 *	the caller.  Otherwise...
	 */
	if ((sock->ssn->clean_out.used < 20) ||
	    (((sock->ssn->clean_out.data[2] << 8) | sock->ssn->clean_out.data[3]) != (int) sock->ssn->clean_out.used)) {
		RDEBUG("Received bad packet: Length %zd contents %d",
		       sock->ssn->clean_out.used,
		       (sock->ssn->clean_out.data[2] << 8) | sock->ssn->clean_out.data[3]);
		goto do_close;
	}

	packet = sock->packet;
	packet->data = talloc_array(packet, uint8_t, sock->ssn->clean_out.used);
	packet->data_len = sock->ssn->clean_out.used;
	sock->ssn->record_minus(&sock->ssn->clean_out, packet->data, packet->data_len);
	packet->vps = NULL;
	PTHREAD_MUTEX_UNLOCK(&sock->mutex);

	if (!rad_packet_ok(packet, 0, NULL)) {
		ERROR("Receive - %s", fr_strerror());
		DEBUG("Closing TLS socket from client");
		PTHREAD_MUTEX_LOCK(&sock->mutex);
		tls_socket_close(listener);
		PTHREAD_MUTEX_UNLOCK(&sock->mutex);
		return 0;	/* do_close unlocks the mutex */
	}

	/*
	 *	Copied from src/lib/radius.c, rad_recv();
	 */
	if (fr_debug_lvl) {
		char host_ipaddr[128];

		if (is_radius_code(packet->code)) {
			RDEBUG("tls_recv: %s packet from host %s port %d, id=%d, length=%d",
			       fr_packet_codes[packet->code],
			       inet_ntop(packet->src_ipaddr.af,
					 &packet->src_ipaddr.ipaddr,
					 host_ipaddr, sizeof(host_ipaddr)),
			       packet->src_port,
			       packet->id, (int) packet->data_len);
		} else {
			RDEBUG("tls_recv: Packet from host %s port %d code=%d, id=%d, length=%d",
			       inet_ntop(packet->src_ipaddr.af,
					 &packet->src_ipaddr.ipaddr,
					 host_ipaddr, sizeof(host_ipaddr)),
			       packet->src_port,
			       packet->code,
			       packet->id, (int) packet->data_len);
		}
	}

	FR_STATS_INC(auth, total_requests);

	return 1;
}
Exemple #15
0
/*
 *	Relay the request to a remote server.
 *	Returns:
 *
 *      RLM_MODULE_FAIL: we don't reply, caller returns without replying
 *      RLM_MODULE_NOOP: caller falls through to normal processing
 *      RLM_MODULE_HANDLED  : we reply, caller returns without replying
 */
int proxy_send(REQUEST *request)
{
	int rcode;
	VALUE_PAIR *proxypair;
	VALUE_PAIR *replicatepair;
	VALUE_PAIR *realmpair;
	VALUE_PAIR *namepair;
	VALUE_PAIR *strippednamepair;
	VALUE_PAIR *delaypair;
	VALUE_PAIR *vp, *vps;
	REALM *realm;
	char *realmname;
	int replicating;

	/*
	 *	Not authentication or accounting.  Stop it.
	 */
	if ((request->packet->code != PW_AUTHENTICATION_REQUEST) &&
	    (request->packet->code != PW_ACCOUNTING_REQUEST)) {
		return RLM_MODULE_FAIL;
	}

	/*
	 *	The timestamp is used below to figure the
	 *	next_try. The request needs to "hang around" until
	 *	either the other server sends a reply or the retry
	 *	count has been exceeded.  Until then, it should not
	 *	be eligible for the time-based cleanup.  --Pac. */

	/* Look for proxy/replicate signs */
	/* FIXME - What to do if multiple Proxy-To/Replicate-To attrs are
	 * set...  Log an error? Actually replicate to multiple places? That
	 * would be cool. For now though, I'll just take the first one and
	 * ignore the rest. */
	proxypair = pairfind(request->config_items, PW_PROXY_TO_REALM);
	replicatepair = pairfind(request->config_items, PW_REPLICATE_TO_REALM);
	if (proxypair) {
		realmpair = proxypair;
		replicating = 0;
	} else if (replicatepair) {
		realmpair = replicatepair;
		replicating = 1;
	} else {
		/*
		 *	Neither proxy or replicate attributes are set,
		 *	so we can exit from the proxy code.
		 */
		return RLM_MODULE_NOOP;
	}

	/*
	 *	If the server has already decided to reject the request,
	 *	then don't try to proxy it.
	 */
	if (request->reply->code == PW_AUTHENTICATION_REJECT) {
		DEBUG2("Cancelling proxy as request was already rejected");
		return RLM_MODULE_REJECT;
	}
	if (((vp = pairfind(request->config_items, PW_AUTH_TYPE)) != NULL) &&
	    (vp->lvalue == PW_AUTHTYPE_REJECT)) {
		DEBUG2("Cancelling proxy as request was already rejected");
		return RLM_MODULE_REJECT;
	}
	/*
	 *	Length == 0 means it exists, but there's no realm.
	 *	Don't proxy it.
	 */
	if (realmpair->length == 0) {
		return RLM_MODULE_NOOP;
	}

	realmname = (char *)realmpair->strvalue;

	/*
	 *	Look for the realm, using the load balancing
	 *	version of realm find.
	 */
	realm = proxy_realm_ldb(request, realmname,
				(request->packet->code == PW_ACCOUNTING_REQUEST));
	if (realm == NULL) {
		return RLM_MODULE_FAIL;
	}

	/*
	 *	Remember that we sent the request to a Realm.
	 */
	pairadd(&request->packet->vps,
		pairmake("Realm", realm->realm, T_OP_EQ));
	
	/*
	 *	Access-Request: look for LOCAL realm.
	 *	Accounting-Request: look for LOCAL realm.
	 */
	if (((request->packet->code == PW_AUTHENTICATION_REQUEST) &&
	     (realm->ipaddr == htonl(INADDR_NONE))) ||
	    ((request->packet->code == PW_ACCOUNTING_REQUEST) &&	    
	     (realm->acct_ipaddr == htonl(INADDR_NONE)))) {
		return RLM_MODULE_NOOP;
	}
	
	/*
	 *	Copy the request, then look up
	 *	name and plain-text password in the copy.
	 *
	 *	Note that the User-Name attribute is the *original*
	 *	as sent over by the client.  The Stripped-User-Name
	 *	attribute is the one hacked through the 'hints' file.
	 */
	vps = paircopy(request->packet->vps);
	namepair = pairfind(vps, PW_USER_NAME);
	strippednamepair = pairfind(vps, PW_STRIPPED_USER_NAME);

	/*
	 *	If there's a Stripped-User-Name attribute in the
	 *	request, then use THAT as the User-Name for the
	 *	proxied request, instead of the original name.
	 *
	 *	This is done by making a copy of the Stripped-User-Name
	 *	attribute, turning it into a User-Name attribute,
	 *	deleting the Stripped-User-Name and User-Name attributes
	 *	from the vps list, and making the new User-Name
	 *	the head of the vps list.
	 */
	if (strippednamepair) {
		vp = paircreate(PW_USER_NAME, PW_TYPE_STRING);
		if (!vp) {
			radlog(L_ERR|L_CONS, "no memory");
			exit(1);
		}
		memcpy(vp->strvalue, strippednamepair->strvalue,
				sizeof(vp->strvalue));
		vp->length = strippednamepair->length;
		pairdelete(&vps, PW_USER_NAME);
		pairdelete(&vps, PW_STRIPPED_USER_NAME);
		vp->next = vps;
		namepair = vp;
		vps = vp;
	}

	/*
	 *	Now build a new RADIUS_PACKET and send it.
	 *
	 *	FIXME: it could be that the id wraps around too fast if
	 *	we have a lot of requests, it might be better to keep
	 *	a seperate ID value per remote server.
	 *
	 *	OTOH the remote radius server should be smart enough to
	 *	compare _both_ ID and vector. Right ?
	 */
	if ((request->proxy = rad_alloc(TRUE)) == NULL) {
		radlog(L_ERR|L_CONS, "no memory");
		exit(1);
	}

	/*
	 *	Proxied requests get sent out the proxy FD ONLY.
	 */
	request->proxy->sockfd = proxyfd;

	request->proxy->code = request->packet->code;
	if (request->packet->code == PW_AUTHENTICATION_REQUEST) {
		request->proxy->dst_port = realm->auth_port;
		request->proxy->dst_ipaddr = realm->ipaddr;
	} else if (request->packet->code == PW_ACCOUNTING_REQUEST) {
		request->proxy->dst_port = realm->acct_port;
		request->proxy->dst_ipaddr = realm->acct_ipaddr;
	}
	rad_assert(request->proxy->vps == NULL);
	request->proxy->vps = vps;

	/*
	 *	Add the request to the list of outstanding requests.
	 *	Note that request->proxy->id is a 16 bits value,
	 *	while rad_send sends only the 8 least significant
	 *	bits of that same value.
	 */
	request->proxy->id = (proxy_id++) & 0xff;
	proxy_id &= 0xffff;

	/*
	 *	Add PROXY_STATE attribute.
	 */
	proxy_addinfo(request);

	/*
	 *	If there is no PW_CHAP_CHALLENGE attribute but there
	 *	is a PW_CHAP_PASSWORD we need to add it since we can't
	 *	use the request authenticator anymore - we changed it.
	 */
	if (pairfind(vps, PW_CHAP_PASSWORD) &&
	    pairfind(vps, PW_CHAP_CHALLENGE) == NULL) {
		vp = paircreate(PW_CHAP_CHALLENGE, PW_TYPE_STRING);
		if (!vp) {
			radlog(L_ERR|L_CONS, "no memory");
			exit(1);
		}
		vp->length = AUTH_VECTOR_LEN;
		memcpy(vp->strvalue, request->packet->vector, AUTH_VECTOR_LEN);
		pairadd(&vps, vp);
	}

	/*
	 *	Set up for sending the request.
	 */
	memcpy(request->proxysecret, realm->secret, sizeof(request->proxysecret));
	request->proxy_is_replicate = replicating;
	request->proxy_try_count = mainconfig.proxy_retry_count - 1;
	request->proxy_next_try = request->timestamp + mainconfig.proxy_retry_delay;
	delaypair = pairfind(vps, PW_ACCT_DELAY_TIME);
	request->proxy->timestamp = request->timestamp - (delaypair ? delaypair->lvalue : 0);

	/*
	 *  Do pre-proxying
	 */
	rcode = module_pre_proxy(request);

	/*
	 *	Do NOT free proxy->vps, the pairs are needed for the
	 *	retries! --Pac.
	 */

	/*
	 *	Delay sending the proxy packet until after we've
	 *	done the work above, playing with the request.
	 *
	 *	After this point, it becomes dangerous to play
	 *	with the request data structure, as the reply MAY
	 *	come in and get processed before we're done with it here.
	 *
	 *	Only proxy the packet if the pre-proxy code succeeded.
	 */
	if ((rcode == RLM_MODULE_OK) ||
	    (rcode == RLM_MODULE_NOOP) ||
	    (rcode == RLM_MODULE_UPDATED)) {
		rad_send(request->proxy, NULL, (char *)request->proxysecret);
		if (!replicating) {
			rcode = RLM_MODULE_HANDLED; /* caller doesn't reply */
		} else {
			rcode = RLM_MODULE_NOOP; /* caller replies */
		}
	} else {
		rcode = RLM_MODULE_FAIL; /* caller doesn't reply */
	}

	return rcode;
}
Exemple #16
0
static int do_packet(int allports, uint32_t nasaddr, const struct radutmp *u)
{
	int i, retries=5, timeout=3;
	struct timeval tv;
	RADIUS_PACKET *req, *rep = NULL;
	VALUE_PAIR *vp;
	const char *secret;

	if ((req = rad_alloc(1)) == NULL) {
		librad_perror("radzap");
		exit(1);
	}
	req->id = getpid() & 0xFF;
	req->code = PW_ACCOUNTING_REQUEST;
	req->dst_port = acct_port;
	if(req->dst_port == 0) 
		req->dst_port = getport("radacct");
	if(req->dst_port == 0) 
		req->dst_port = PW_ACCT_UDP_PORT;
	if (radiusip == INADDR_NONE) {
		req->dst_ipaddr = ip_getaddr("localhost");
	}
	else {
		req->dst_ipaddr = radiusip;
	}
	if(!req->dst_ipaddr) 
		req->dst_ipaddr = 0x7f000001;
	req->vps = NULL;
	secret = getsecret(req->dst_ipaddr);

	if(allports != 0) {
		INTPAIR(PW_ACCT_STATUS_TYPE, PW_STATUS_ACCOUNTING_OFF);
		IPPAIR(PW_NAS_IP_ADDRESS, nasaddr);
		INTPAIR(PW_ACCT_DELAY_TIME, 0);
	} else {
		char login[sizeof u->login+1];
		char session_id[sizeof u->session_id+1];
		strNcpy(login, u->login, sizeof login);
		strNcpy(session_id, u->session_id, sizeof session_id);
		INTPAIR(PW_ACCT_STATUS_TYPE, PW_STATUS_STOP);
		IPPAIR(PW_NAS_IP_ADDRESS, u->nas_address);
		INTPAIR(PW_ACCT_DELAY_TIME, 0);
		STRINGPAIR(PW_USER_NAME, login);
		INTPAIR(PW_NAS_PORT, u->nas_port);
		STRINGPAIR(PW_ACCT_SESSION_ID, session_id);
		if(u->proto=='P') {
			INTPAIR(PW_SERVICE_TYPE, PW_FRAMED_USER);
			INTPAIR(PW_FRAMED_PROTOCOL, PW_PPP);
		} else if(u->proto=='S') {
			INTPAIR(PW_SERVICE_TYPE, PW_FRAMED_USER);
			INTPAIR(PW_FRAMED_PROTOCOL, PW_SLIP);
		} else {
			INTPAIR(PW_SERVICE_TYPE, PW_LOGIN_USER); /* A guess, really */
		}
		IPPAIR(PW_FRAMED_IP_ADDRESS, u->framed_address);
		INTPAIR(PW_ACCT_SESSION_TIME, 0);
		INTPAIR(PW_ACCT_INPUT_OCTETS, 0);
		INTPAIR(PW_ACCT_OUTPUT_OCTETS, 0);
		INTPAIR(PW_ACCT_INPUT_PACKETS, 0);
		INTPAIR(PW_ACCT_OUTPUT_PACKETS, 0);
	}
	if ((req->sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
		perror("radzap: socket: ");
		exit(1);
	}

	for (i = 0; i < retries; i++) {
		fd_set rdfdesc;

		rad_send(req, NULL, secret);

		/* And wait for reply, timing out as necessary */
		FD_ZERO(&rdfdesc);
		FD_SET(req->sockfd, &rdfdesc);

		tv.tv_sec = (int)timeout;
		tv.tv_usec = 1000000 * (timeout - (int)timeout);

		/* Something's wrong if we don't get exactly one fd. */
		if (select(req->sockfd + 1, &rdfdesc, NULL, NULL, &tv) != 1) {
			continue;
		}

		rep = rad_recv(req->sockfd);
		if (rep != NULL) {
			break;
		} else {	/* NULL: couldn't receive the packet */
			librad_perror("radzap:");
			exit(1);
		}
	}

	/* No response or no data read (?) */
	if (i == retries) {
		fprintf(stderr, "%s: no response from server\n", progname);
		exit(1);
	}

	if (rad_decode(rep, req, secret) != 0) {
		librad_perror("rad_decode");
		exit(1);
	}

	vp_printlist(stdout, rep->vps);
	return 0;
}
static REQUEST *request_setup(FILE *fp)
{	
	REQUEST *request;

	/*
	 *	Create and initialize the new request.
	 */
	request = request_alloc(NULL);

	request->packet = rad_alloc(request, 0);
	if (!request->packet) {
		ERROR("No memory");
		request_free(&request);
		return NULL;
	}

	request->reply = rad_alloc(request, 0);
	if (!request->reply) {
		ERROR("No memory");
		request_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_ACTIVE;
	request->handle = NULL;
	request->server = talloc_strdup(request, "default");

	request->root = &mainconfig;

	/*
	 *	Read packet from fp
	 */
	request->packet->vps = readvp2(request->packet, fp, &filedone, "radiusd:");
	if (!request->packet->vps) {
		talloc_free(request);
		return NULL;
	}

	/*
	 *	FIXME: set IPs, etc.
	 */
	request->packet->code = PW_CODE_AUTHENTICATION_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->options = debug_flag;
	request->radlog = radlog_request;

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

	return request;
}
Exemple #18
0
/*
 *	Relay the request to a remote server.
 *	Returns:
 *
 *      RLM_MODULE_FAIL: we don't reply, caller returns without replying
 *      RLM_MODULE_NOOP: caller falls through to normal processing
 *      RLM_MODULE_HANDLED  : we reply, caller returns without replying
 */
int proxy_send(REQUEST *request)
{
	int rcode;
	int pre_proxy_type = 0;
	VALUE_PAIR *realmpair;
	VALUE_PAIR *strippedname;
	VALUE_PAIR *delaypair;
	VALUE_PAIR *vp;
	REALM *realm;
	char *realmname;

	/*
	 *	Not authentication or accounting.  Stop it.
	 */
	if ((request->packet->code != PW_AUTHENTICATION_REQUEST) &&
	    (request->packet->code != PW_ACCOUNTING_REQUEST)) {
		DEBUG2("  ERROR: Cannot proxy packets of type %d",
		       request->packet->code);
		return RLM_MODULE_FAIL;
	}

	/*
	 *	The timestamp is used below to figure the
	 *	next_try. The request needs to "hang around" until
	 *	either the other server sends a reply or the retry
	 *	count has been exceeded.  Until then, it should not
	 *	be eligible for the time-based cleanup.  --Pac. */

	realmpair = pairfind(request->config_items, PW_PROXY_TO_REALM);
	if (!realmpair) {
		/*
		 *	Not proxying, so we can exit from the proxy
		 *	code.
		 */
		return RLM_MODULE_NOOP;
	}

	/*
	 *	If the server has already decided to reject the request,
	 *	then don't try to proxy it.
	 */
	if (request->reply->code == PW_AUTHENTICATION_REJECT) {
		DEBUG2("Cancelling proxy as request was already rejected");
		return RLM_MODULE_REJECT;
	}
	if (((vp = pairfind(request->config_items, PW_AUTH_TYPE)) != NULL) &&
	    (vp->lvalue == PW_AUTHTYPE_REJECT)) {
		DEBUG2("Cancelling proxy as request was already rejected");
		return RLM_MODULE_REJECT;
	}
	/*
	 *	Length == 0 means it exists, but there's no realm.
	 *	Don't proxy it.
	 */
	if (realmpair->length == 0) {
		return RLM_MODULE_NOOP;
	}

	realmname = (char *)realmpair->strvalue;

	/*
	 *	Look for the realm, using the load balancing
	 *	version of realm find.
	 */
	realm = proxy_realm_ldb(request, realmname,
				(request->packet->code == PW_ACCOUNTING_REQUEST));
	if (realm == NULL) {
		DEBUG2("  ERROR: Failed to find live home server for realm %s",
		       realmname);
		return RLM_MODULE_FAIL;
	}

	/*
	 *	Remember that we sent the request to a Realm.
	 */
	pairadd(&request->packet->vps,
		pairmake("Realm", realm->realm, T_OP_EQ));

	/*
	 *	Access-Request: look for LOCAL realm.
	 *	Accounting-Request: look for LOCAL realm.
	 */
	if (((request->packet->code == PW_AUTHENTICATION_REQUEST) &&
	     (realm->ipaddr == htonl(INADDR_NONE))) ||
	    ((request->packet->code == PW_ACCOUNTING_REQUEST) &&
	     (realm->acct_ipaddr == htonl(INADDR_NONE)))) {
		DEBUG2(" WARNING: Cancelling proxy to Realm %s, as the realm is local.",
		       realm->realm);
		return RLM_MODULE_NOOP;
	}

	/*
	 *	Allocate the proxy packet, only if it wasn't already
	 *	allocated by a module.  This check is mainly to support
	 *	the proxying of EAP-TTLS and EAP-PEAP tunneled requests.
	 *
	 *	In those cases, the EAP module creates a "fake"
	 *	request, and recursively passes it through the
	 *	authentication stage of the server.  The module then
	 *	checks if the request was supposed to be proxied, and
	 *	if so, creates a proxy packet from the TUNNELED request,
	 *	and not from the EAP request outside of the tunnel.
	 *
	 *	The proxy then works like normal, except that the response
	 *	packet is "eaten" by the EAP module, and encapsulated into
	 *	an EAP packet.
	 */
	if (!request->proxy) {
		/*
		 *	Now build a new RADIUS_PACKET.
		 *
		 *	FIXME: it could be that the id wraps around
		 *	too fast if we have a lot of requests, it
		 *	might be better to keep a seperate ID value
		 *	per remote server.
		 *
		 *	OTOH the remote radius server should be smart
		 *	enough to compare _both_ ID and vector.
		 *	Right?
		 */
		if ((request->proxy = rad_alloc(TRUE)) == NULL) {
			radlog(L_ERR|L_CONS, "no memory");
			exit(1);
		}

		/*
		 *	We now massage the attributes to be proxied...
		 */

		/*
		 *	Copy the request, then look up name and
		 *	plain-text password in the copy.
		 *
		 *	Note that the User-Name attribute is the
		 *	*original* as sent over by the client.  The
		 *	Stripped-User-Name attribute is the one hacked
		 *	through the 'hints' file.
		 */
		request->proxy->vps =  paircopy(request->packet->vps);
	}

	/*
	 *	Strip the name, if told to.
	 *
	 *	Doing it here catches the case of proxied tunneled
	 *	requests.
	 */
	if (realm->striprealm == TRUE &&
	   (strippedname = pairfind(request->proxy->vps, PW_STRIPPED_USER_NAME)) != NULL) {
		/*
		 *	If there's a Stripped-User-Name attribute in
		 *	the request, then use THAT as the User-Name
		 *	for the proxied request, instead of the
		 *	original name.
		 *
		 *	This is done by making a copy of the
		 *	Stripped-User-Name attribute, turning it into
		 *	a User-Name attribute, deleting the
		 *	Stripped-User-Name and User-Name attributes
		 *	from the vps list, and making the new
		 *	User-Name the head of the vps list.
		 */
		vp = pairfind(request->proxy->vps, PW_USER_NAME);
		if (!vp) {
			vp = paircreate(PW_USER_NAME, PW_TYPE_STRING);
			if (!vp) {
				radlog(L_ERR|L_CONS, "no memory");
				exit(1);
			}
			vp->next = request->proxy->vps;
			request->proxy->vps = vp;
		}
		memcpy(vp->strvalue, strippedname->strvalue,
		       sizeof(vp->strvalue));
		vp->length = strippedname->length;

		/*
		 *	Do NOT delete Stripped-User-Name.
		 */
	}
	
	/*
	 *	If there is no PW_CHAP_CHALLENGE attribute but
	 *	there is a PW_CHAP_PASSWORD we need to add it
	 *	since we can't use the request authenticator
	 *	anymore - we changed it.
	 */
	if (pairfind(request->proxy->vps, PW_CHAP_PASSWORD) &&
	    pairfind(request->proxy->vps, PW_CHAP_CHALLENGE) == NULL) {
		vp = paircreate(PW_CHAP_CHALLENGE, PW_TYPE_STRING);
		if (!vp) {
			radlog(L_ERR|L_CONS, "no memory");
			exit(1);
		}
		vp->length = AUTH_VECTOR_LEN;
		memcpy(vp->strvalue, request->packet->vector, AUTH_VECTOR_LEN);
		pairadd(&(request->proxy->vps), vp);
	}

	request->proxy->code = request->packet->code;
	if (request->packet->code == PW_AUTHENTICATION_REQUEST) {
		request->proxy->dst_port = realm->auth_port;
		request->proxy->dst_ipaddr = realm->ipaddr;
	} else if (request->packet->code == PW_ACCOUNTING_REQUEST) {
		request->proxy->dst_port = realm->acct_port;
		request->proxy->dst_ipaddr = realm->acct_ipaddr;
	}

	/*
	 *	Add PROXY_STATE attribute, before pre-proxy stage,
	 *	so the pre-proxy modules have access to it.
	 *
	 *	Note that, at this point, the proxied request HAS NOT
	 *	been assigned a RADIUS Id.
	 */
	proxy_addinfo(request);

	/*
	 *	Set up for sending the request.
	 */
	memcpy(request->proxysecret, realm->secret, sizeof(request->proxysecret));
	request->proxy_try_count = mainconfig.proxy_retry_count - 1;
	request->proxy_next_try = request->timestamp + mainconfig.proxy_retry_delay;
	delaypair = pairfind(request->proxy->vps, PW_ACCT_DELAY_TIME);
	request->proxy->timestamp = request->timestamp - (delaypair ? delaypair->lvalue : 0);

	/*
	 *  Do pre-proxying
	 */
	vp = pairfind(request->config_items, PW_PRE_PROXY_TYPE);
	if (vp) {
		DEBUG2("  Found Pre-Proxy-Type %s", vp->strvalue);
		pre_proxy_type = vp->lvalue;
	}
	rcode = module_pre_proxy(pre_proxy_type, request);

	/*
	 *	Do NOT free request->proxy->vps, the pairs are needed
	 *	for the retries! --Pac.
	 */

	/*
	 *	Delay sending the proxy packet until after we've
	 *	done the work above, playing with the request.
	 *
	 *	After this point, it becomes dangerous to play
	 *	with the request data structure, as the reply MAY
	 *	come in and get processed before we're done with it here.
	 *
	 *	Only proxy the packet if the pre-proxy code succeeded.
	 */
	if ((rcode == RLM_MODULE_OK) ||
	    (rcode == RLM_MODULE_NOOP) ||
	    (rcode == RLM_MODULE_UPDATED)) {
		request->options |= RAD_REQUEST_OPTION_PROXIED;

		/*
		 *	IF it's a fake request, don't send the proxy
		 *	packet.  The outer tunnel session will take
		 *	care of doing that.
		 */
		if ((request->options & RAD_REQUEST_OPTION_FAKE_REQUEST) == 0) {
			/*
			 *	Add the proxied request to the
			 *	list of outstanding proxied
			 *	requests, BEFORE we send it, so
			 *	we have fewer problems with race
			 *	conditions when the responses come
			 *	back very quickly.
			 */
			if (!rl_add_proxy(request)) {
				DEBUG("ERROR: Failed to proxy request %d",
				      request->number);
				return RLM_MODULE_FAIL; /* caller doesn't reply */
			}

			rad_send(request->proxy, NULL,
				 (char *)request->proxysecret);
		}
		rcode = RLM_MODULE_HANDLED; /* caller doesn't reply */
	} else {
		rcode = RLM_MODULE_FAIL; /* caller doesn't reply */
	}

	return rcode;
}
Exemple #19
0
main(int argc, char *argv[])
{
	int filedone;
	RADIUS_PACKET *req,*req2;
	VALUE_PAIR *vp, *vpkey, *vpextra;
	extern unsigned int sha1_data_problems;

	req = NULL;
	req2 = NULL;
	filedone = 0;

	if(argc>1) {
	  sha1_data_problems = 1;
	}

	if (dict_init(radius_dir, RADIUS_DICTIONARY) < 0) {
		librad_perror("radclient");
		return 1;
	}

	if ((req = rad_alloc(1)) == NULL) {
		librad_perror("radclient");
		exit(1);
	}

	if ((req2 = rad_alloc(1)) == NULL) {
		librad_perror("radclient");
		exit(1);
	}

	while(!filedone) {
		if(req->vps) pairfree(&req->vps);
		if(req2->vps) pairfree(&req2->vps);

		if ((req->vps = readvp2(stdin, &filedone, "eapsimlib:")) == NULL) {
			break;
		}

		printf("\nRead:\n");
		vp_printlist(stdout, req->vps);

		map_eapsim_types(req);
		map_eap_types(req);
		printf("Mapped to:\n");
		vp_printlist(stdout, req->vps);

		/* find the EAP-Message, copy it to req2 */
		vp = paircopy2(req->vps, PW_EAP_MESSAGE);

		if(vp == NULL) continue;

		pairadd(&req2->vps, vp);

		/* only call unmap for sim types here */
		unmap_eap_types(req2);
		unmap_eapsim_types(req2);

		printf("Unmapped to:\n");
		vp_printlist(stdout, req2->vps);

		vp = pairfind(req2->vps,
			      ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_MAC);
		vpkey   = pairfind(req->vps, ATTRIBUTE_EAP_SIM_KEY);
		vpextra = pairfind(req->vps, ATTRIBUTE_EAP_SIM_EXTRA);

		if(vp != NULL && vpkey != NULL && vpextra!=NULL) {
			uint8_t calcmac[16];

			/* find the EAP-Message, copy it to req2 */

			memset(calcmac, 0, sizeof(calcmac));
			printf("Confirming MAC...");
			if(eapsim_checkmac(req2->vps, vpkey->strvalue,
					   vpextra->strvalue, vpextra->length,
					   calcmac)) {
				printf("succeed\n");
			} else {
				int i, j;

				printf("calculated MAC (");
				for (i = 0; i < 20; i++) {
					if(j==4) {
						printf("_");
						j=0;
					}
					j++;

					printf("%02x", calcmac[i]);
				}
				printf(" did not match\n");
			}
		}

		fflush(stdout);
	}
}
Exemple #20
0
/*
 *	Create a new REQUEST, based on an old one.
 *
 *	This function allows modules to inject fake requests
 *	into the server, for tunneled protocols like TTLS & PEAP.
 */
REQUEST *request_alloc_fake(REQUEST *request)
{
  REQUEST *fake;

  fake = request_alloc();

  fake->number = request->number;
#ifdef HAVE_PTHREAD_H
  fake->child_pid = request->child_pid;
#endif
  fake->parent = request;
  fake->root = request->root;
  fake->client = request->client;

  /*
   *	For new server support.
   *
   *	FIXME: Key instead off of a "virtual server" data structure.
   *
   *	FIXME: Permit different servers for inner && outer sessions?
   */
  fake->server = request->server;

  fake->packet = rad_alloc(request, 1);
  if (!fake->packet) {
	  request_free(&fake);
	  return NULL;
  }

  fake->reply = rad_alloc(request, 0);
  if (!fake->reply) {
	  request_free(&fake);
	  return NULL;
  }

  fake->master_state = REQUEST_ACTIVE;
  fake->child_state = REQUEST_RUNNING;

  /*
   *	Fill in the fake request.
   */
  fake->packet->sockfd = -1;
  fake->packet->src_ipaddr = request->packet->src_ipaddr;
  fake->packet->src_port = request->packet->src_port;
  fake->packet->dst_ipaddr = request->packet->dst_ipaddr;
  fake->packet->dst_port = 0;

  /*
   *	This isn't STRICTLY required, as the fake request MUST NEVER
   *	be put into the request list.  However, it's still reasonable
   *	practice.
   */
  fake->packet->id = fake->number & 0xff;
  fake->packet->code = request->packet->code;
  fake->timestamp = request->timestamp;
 
  /*
   *	Required for new identity support
   */
  fake->listener = request->listener;

  /*
   *	Fill in the fake reply, based on the fake request.
   */
  fake->reply->sockfd = fake->packet->sockfd;
  fake->reply->src_ipaddr = fake->packet->dst_ipaddr;
  fake->reply->src_port = fake->packet->dst_port;
  fake->reply->dst_ipaddr = fake->packet->src_ipaddr;
  fake->reply->dst_port = fake->packet->src_port;
  fake->reply->id = fake->packet->id;
  fake->reply->code = 0; /* UNKNOWN code */

  /*
   *	Copy debug information.
   */
  fake->options = request->options;
  fake->radlog = request->radlog;

  return fake;
}
int main(int argc, char *argv[])
{
	rs_t *conf;

	fr_pcap_t *in = NULL, *in_p;
	fr_pcap_t **in_head = &in;
	fr_pcap_t *out = NULL;

	int ret = 1;					/* Exit status */
	int limit = -1;					/* How many packets to sniff */

	char errbuf[PCAP_ERRBUF_SIZE];			/* Error buffer */
	int port = 1812;

	char buffer[1024];

	int opt;
	FR_TOKEN parsecode;
	char const *radius_dir = RADIUS_DIR;

	rs_stats_t stats;

	fr_debug_flag = 2;
	log_dst = stdout;

	talloc_set_log_stderr();

	conf = talloc_zero(NULL, rs_t);
	if (!fr_assert(conf)) {
		exit (1);
	}

	/*
	 *  We don't really want probes taking down machines
	 */
#ifdef HAVE_TALLOC_SET_MEMLIMIT
	talloc_set_memlimit(conf, 52428800);		/* 50 MB */
#endif

	/*
	 *  Get options
	 */
	while ((opt = getopt(argc, argv, "c:d:DFf:hi:I:p:qr:s:Svw:xXW:P:O:")) != EOF) {
		switch (opt) {
		case 'c':
			limit = atoi(optarg);
			if (limit <= 0) {
				fprintf(stderr, "radsniff: Invalid number of packets \"%s\"", optarg);
				exit(1);
			}
			break;

		case 'd':
			radius_dir = optarg;
			break;

		case 'D':
			{
				pcap_if_t *all_devices = NULL;
				pcap_if_t *dev_p;

				if (pcap_findalldevs(&all_devices, errbuf) < 0) {
					ERROR("Error getting available capture devices: %s", errbuf);
					goto finish;
				}

				int i = 1;
				for (dev_p = all_devices;
				     dev_p;
				     dev_p = dev_p->next) {
					INFO("%i.%s", i++, dev_p->name);
				}
				ret = 0;
				goto finish;
			}

		case 'F':
			conf->from_stdin = true;
			conf->to_stdout = true;
			break;

		case 'f':
			conf->pcap_filter = optarg;
			break;

		case 'h':
			usage(0);
			break;

		case 'i':
			*in_head = fr_pcap_init(conf, optarg, PCAP_INTERFACE_IN);
			if (!*in_head) {
				goto finish;
			}
			in_head = &(*in_head)->next;
			conf->from_dev = true;
			break;

		case 'I':
			*in_head = fr_pcap_init(conf, optarg, PCAP_FILE_IN);
			if (!*in_head) {
				goto finish;
			}
			in_head = &(*in_head)->next;
			conf->from_file = true;
			break;

		case 'p':
			port = atoi(optarg);
			break;

		case 'q':
			if (fr_debug_flag > 0) {
				fr_debug_flag--;
			}
			break;

		case 'r':
			conf->radius_filter = optarg;
			break;

		case 's':
			conf->radius_secret = optarg;
			break;

		case 'S':
			conf->do_sort = true;
			break;

		case 'v':
#ifdef HAVE_COLLECTDC_H
			INFO("%s, %s, collectdclient version %s", radsniff_version, pcap_lib_version(),
			     lcc_version_string());
#else
			INFO("%s %s", radsniff_version, pcap_lib_version());
#endif
			exit(0);
			break;

		case 'w':
			out = fr_pcap_init(conf, optarg, PCAP_FILE_OUT);
			conf->to_file = true;
			break;

		case 'x':
		case 'X':
		  	fr_debug_flag++;
			break;

		case 'W':
			conf->stats.interval = atoi(optarg);
			if (conf->stats.interval <= 0) {
				ERROR("Stats interval must be > 0");
				usage(64);
			}
			break;

		case 'T':
			conf->stats.timeout = atoi(optarg);
			if (conf->stats.timeout <= 0) {
				ERROR("Timeout value must be > 0");
				usage(64);
			}
			break;

#ifdef HAVE_COLLECTDC_H
		case 'P':
			conf->stats.prefix = optarg;
			break;

		case 'O':
			conf->stats.collectd = optarg;
			conf->stats.out = RS_STATS_OUT_COLLECTD;
			break;
#endif
		default:
			usage(64);
		}
	}

	/* What's the point in specifying -F ?! */
	if (conf->from_stdin && conf->from_file && conf->to_file) {
		usage(64);
	}

	/* Can't read from both... */
	if (conf->from_file && conf->from_dev) {
		usage(64);
	}

	/* Reading from file overrides stdin */
	if (conf->from_stdin && (conf->from_file || conf->from_dev)) {
		conf->from_stdin = false;
	}

	/* Writing to file overrides stdout */
	if (conf->to_file && conf->to_stdout) {
		conf->to_stdout = false;
	}

	if (conf->to_stdout) {
		out = fr_pcap_init(conf, "stdout", PCAP_STDIO_OUT);
		if (!out) {
			goto finish;
		}
	}

	if (conf->from_stdin) {
		*in_head = fr_pcap_init(conf, "stdin", PCAP_STDIO_IN);
		if (!*in_head) {
			goto finish;
		}
		in_head = &(*in_head)->next;
	}

	if (!conf->radius_secret) {
		conf->radius_secret = RS_DEFAULT_SECRET;
	}

	if (conf->stats.interval && !conf->stats.out) {
		conf->stats.out = RS_STATS_OUT_STDIO;
	}

	if (conf->stats.timeout == 0) {
		conf->stats.timeout = RS_DEFAULT_TIMEOUT;
	}

	/*
	 *	If were writing pcap data stdout we *really* don't want to send
	 *	logging there as well.
	 */
 	log_dst = conf->to_stdout ? stderr : stdout;

#if !defined(HAVE_PCAP_FOPEN_OFFLINE) || !defined(HAVE_PCAP_DUMP_FOPEN)
	if (conf->from_stdin || conf->to_stdout) {
		ERROR("PCAP streams not supported");
		goto finish;
	}
#endif

	if (!conf->pcap_filter) {
		snprintf(buffer, sizeof(buffer), "udp port %d or %d or %d",
			 port, port + 1, 3799);
		conf->pcap_filter = buffer;
	}

	if (dict_init(radius_dir, RADIUS_DICTIONARY) < 0) {
		fr_perror("radsniff");
		ret = 64;
		goto finish;
	}
	fr_strerror();	/* Clear out any non-fatal errors */

	if (conf->radius_filter) {
		parsecode = userparse(NULL, conf->radius_filter, &filter_vps);
		if (parsecode == T_OP_INVALID) {
			ERROR("Invalid RADIUS filter \"%s\" (%s)", conf->radius_filter, fr_strerror());
			ret = 64;
			goto finish;
		}

		if (!filter_vps) {
			ERROR("Empty RADIUS filter \"%s\"", conf->radius_filter);
			ret = 64;
			goto finish;
		}

		filter_tree = rbtree_create((rbcmp) fr_packet_cmp, _rb_rad_free, 0);
		if (!filter_tree) {
			ERROR("Failed creating filter tree");
			ret = 64;
			goto finish;
		}
	}

	/*
	 *	Setup the request tree
	 */
	request_tree = rbtree_create((rbcmp) fr_packet_cmp, _rb_rad_free, 0);
	if (!request_tree) {
		ERROR("Failed creating request tree");
		goto finish;
	}

	/*
	 *	Allocate a null packet for decrypting attributes in CoA requests
	 */
	nullpacket = rad_alloc(conf, 0);
	if (!nullpacket) {
		ERROR("Out of memory");
		goto finish;
	}

	/*
	 *	Get the default capture device
	 */
	if (!conf->from_stdin && !conf->from_file && !conf->from_dev) {
		pcap_if_t *all_devices;			/* List of all devices libpcap can listen on */
		pcap_if_t *dev_p;

		if (pcap_findalldevs(&all_devices, errbuf) < 0) {
			ERROR("Error getting available capture devices: %s", errbuf);
			goto finish;
		}

		if (!all_devices) {
			ERROR("No capture files specified and no live interfaces available");
			ret = 64;
			goto finish;
		}

		for (dev_p = all_devices;
		     dev_p;
		     dev_p = dev_p->next) {
		     	/* Don't use the any devices, it's horribly broken */
		     	if (!strcmp(dev_p->name, "any")) continue;
			*in_head = fr_pcap_init(conf, dev_p->name, PCAP_INTERFACE_IN);
			in_head = &(*in_head)->next;
		}
		conf->from_auto = true;
		conf->from_dev = true;
		INFO("Defaulting to capture on all interfaces");
	}

	/*
	 *	Print captures values which will be used
	 */
	if (fr_debug_flag > 2) {
			DEBUG1("Sniffing with options:");
		if (conf->from_dev)	{
			char *buff = fr_pcap_device_names(conf, in, ' ');
			DEBUG1("  Device(s)                : [%s]", buff);
			talloc_free(buff);
		}
		if (conf->to_file || conf->to_stdout) {
			DEBUG1("  Writing to               : [%s]", out->name);
		}
		if (limit > 0)	{
			DEBUG1("  Capture limit (packets)  : [%d]", limit);
		}
			DEBUG1("  PCAP filter              : [%s]", conf->pcap_filter);
			DEBUG1("  RADIUS secret            : [%s]", conf->radius_secret);
		if (filter_vps){
			DEBUG1("  RADIUS filter            :");
			vp_printlist(log_dst, filter_vps);
		}
	}

	/*
	 *	Open our interface to collectd
	 */
#ifdef HAVE_COLLECTDC_H
	if (conf->stats.out == RS_STATS_OUT_COLLECTD) {
		size_t i;
		rs_stats_tmpl_t *tmpl, **next;

		if (rs_stats_collectd_open(conf) < 0) {
			exit(1);
		}

		next = &conf->stats.tmpl;

		for (i = 0; i < (sizeof(rs_useful_codes) / sizeof(*rs_useful_codes)); i++) {
			tmpl = rs_stats_collectd_init_latency(conf, next, conf, "radius_pkt_ex",
							      &stats.exchange[rs_useful_codes[i]],
							      rs_useful_codes[i]);
			if (!tmpl) {
				goto tmpl_error;
			}
			next = &(tmpl->next);
			tmpl = rs_stats_collectd_init_counter(conf, next, conf, "radius_pkt",
							      &stats.gauge.type[rs_useful_codes[i]],
							      rs_useful_codes[i]);
			if (!tmpl) {
				tmpl_error:
				ERROR("Error allocating memory for stats template");
				goto finish;
			}
			next = &(tmpl->next);
		}
	}
#endif

	/*
	 *	This actually opens the capture interfaces/files (we just allocated the memory earlier)
	 */
	{
		fr_pcap_t *prev = NULL;

		for (in_p = in;
		     in_p;
		     in_p = in_p->next) {
			if (fr_pcap_open(in_p) < 0) {
				if (!conf->from_auto) {
					ERROR("Failed opening pcap handle for %s", in_p->name);
					goto finish;
				}

				DEBUG("Failed opening pcap handle: %s", fr_strerror());
				/* Unlink it from the list */
				if (prev) {
					prev->next = in_p->next;
					talloc_free(in_p);
					in_p = prev;
				} else {
					in = in_p->next;
					talloc_free(in_p);
					in_p = in;
				}

				goto next;
			}

			if (conf->pcap_filter) {
				if (fr_pcap_apply_filter(in_p, conf->pcap_filter) < 0) {
					ERROR("Failed applying filter");
					goto finish;
				}
			}

			next:
			prev = in_p;
		}
	}

	/*
	 *	Open our output interface (if we have one);
	 */
	if (out) {
		if (fr_pcap_open(out) < 0) {
			ERROR("Failed opening pcap output");
			goto finish;
		}
	}

	/*
	 *	Setup and enter the main event loop. Who needs libev when you can roll your own...
	 */
	 {
	 	struct timeval now;
	 	fr_event_list_t		*events;
	 	rs_update_t		update;

	 	char *buff;

		memset(&stats, 0, sizeof(stats));
		memset(&update, 0, sizeof(update));

	 	events = fr_event_list_create(conf, _rs_event_status);
	 	if (!events) {
	 		ERROR();
	 		goto finish;
	 	}

		for (in_p = in;
	     	     in_p;
	     	     in_p = in_p->next) {
	     	     	rs_event_t *event;

	     	     	event = talloc_zero(events, rs_event_t);
	     	     	event->conf = conf;
	     	     	event->in = in_p;
	     	     	event->out = out;
	     	     	event->stats = &stats;

			if (!fr_event_fd_insert(events, 0, in_p->fd, rs_got_packet, event)) {
				ERROR("Failed inserting file descriptor");
				goto finish;
			}
		}

		buff = fr_pcap_device_names(conf, in, ' ');
		INFO("Sniffing on (%s)", buff);
		talloc_free(buff);

		gettimeofday(&now, NULL);
		start_pcap = now;

		/*
		 *	Insert our stats processor
		 */
		if (conf->stats.interval) {
			update.list = events;
			update.conf = conf;
			update.stats = &stats;
			update.in    = in;

			now.tv_sec += conf->stats.interval;
			now.tv_usec = 0;
			fr_event_insert(events, rs_stats_process, (void *) &update, &now, NULL);
		}

		ret = fr_event_loop(events);	/* Enter the main event loop */
	 }

	INFO("Done sniffing");

	finish:

	if (filter_tree) {
		rbtree_free(filter_tree);
	}

	INFO("Exiting...");
	/*
	 *	Free all the things! This also closes all the sockets and file descriptors
	 */
	talloc_free(conf);

	return ret;
}
int proxy_tls_recv(rad_listen_t *listener)
{
	int rcode;
	size_t length;
	listen_socket_t *sock = listener->data;
	char buffer[256];
	RADIUS_PACKET *packet;
	RAD_REQUEST_FUNP fun = NULL;
	uint8_t *data;

	if (!sock->data) sock->data = rad_malloc(sock->ssn->offset);
	data = sock->data;

	DEBUG3("Proxy SSL socket has data to read");
	PTHREAD_MUTEX_LOCK(&sock->mutex);
redo:
	rcode = SSL_read(sock->ssn->ssl, data, 4);
	if (rcode <= 0) {
		int err = SSL_get_error(sock->ssn->ssl, rcode);
		switch (err) {
		case SSL_ERROR_WANT_READ:
		case SSL_ERROR_WANT_WRITE:
			rcode = 0;
			goto redo;
		case SSL_ERROR_ZERO_RETURN:
			/* remote end sent close_notify, send one back */
			SSL_shutdown(sock->ssn->ssl);

		case SSL_ERROR_SYSCALL:
		do_close:
			PTHREAD_MUTEX_UNLOCK(&sock->mutex);
			tls_socket_close(listener);
			return 0;

		default:
			while ((err = ERR_get_error())) {
				DEBUG("proxy recv says %s",
				      ERR_error_string(err, NULL));
			}
			
			goto do_close;
		}
	}

	length = (data[2] << 8) | data[3];
	DEBUG3("Proxy received header saying we have a packet of %u bytes",
	       (unsigned int) length);

	if (length > sock->ssn->offset) {
		radlog(L_INFO,
		       "Received packet will be too large! Set \"fragment_size=%u\"",
		       (data[2] << 8) | data[3]);
		goto do_close;
	}
	
	rcode = SSL_read(sock->ssn->ssl, data + 4, length);
	if (rcode <= 0) {
		switch (SSL_get_error(sock->ssn->ssl, rcode)) {
		case SSL_ERROR_WANT_READ:
		case SSL_ERROR_WANT_WRITE:
			rcode = 0;
			break;

		case SSL_ERROR_ZERO_RETURN:
			/* remote end sent close_notify, send one back */
			SSL_shutdown(sock->ssn->ssl);
			goto do_close;
		default:
			goto do_close;
		}
	}
	PTHREAD_MUTEX_UNLOCK(&sock->mutex);

	packet = rad_alloc(0);
	packet->sockfd = listener->fd;
	packet->src_ipaddr = sock->other_ipaddr;
	packet->src_port = sock->other_port;
	packet->dst_ipaddr = sock->my_ipaddr;
	packet->dst_port = sock->my_port;
	packet->code = data[0];
	packet->id = data[1];
	packet->data_len = length;
	packet->data = rad_malloc(packet->data_len);
	memcpy(packet->data, data, packet->data_len);
	memcpy(packet->vector, packet->data + 4, 16);

	/*
	 *	FIXME: Client MIB updates?
	 */
	switch(packet->code) {
	case PW_AUTHENTICATION_ACK:
	case PW_ACCESS_CHALLENGE:
	case PW_AUTHENTICATION_REJECT:
		fun = rad_authenticate;
		break;

#ifdef WITH_ACCOUNTING
	case PW_ACCOUNTING_RESPONSE:
		fun = rad_accounting;
		break;
#endif

	default:
		/*
		 *	FIXME: Update MIB for packet types?
		 */
		radlog(L_ERR, "Invalid packet code %d sent to a proxy port "
		       "from home server %s port %d - ID %d : IGNORED",
		       packet->code,
		       ip_ntoh(&packet->src_ipaddr, buffer, sizeof(buffer)),
		       packet->src_port, packet->id);
		rad_free(&packet);
		return 0;
	}

	if (!request_proxy_reply(packet)) {
		rad_free(&packet);
		return 0;
	}

	return 1;
}
int main(int argc, char *argv[])
{
	const char *from_dev = NULL;			/* Capture from device */
	const char *from_file = NULL;			/* Read from pcap file */
	int from_stdin = 0;				/* Read from stdin */
	
	pcap_t *in;					/* PCAP input handle */
	
	int limit = -1;					/* How many packets to sniff */
	
	char errbuf[PCAP_ERRBUF_SIZE];			/* Error buffer */

	char *to_file = NULL;				/* PCAP output file */
	
	char *pcap_filter = NULL;			/* PCAP filter string */
	char *radius_filter = NULL;
	int port = 1812;
	
	struct bpf_program fp;				/* Holds compiled filter */
	bpf_u_int32 ip_mask = PCAP_NETMASK_UNKNOWN;	/* Device Subnet mask */
	bpf_u_int32 ip_addr = 0;			/* Device IP */
	
	char buffer[1024];

	int opt;
	FR_TOKEN parsecode;
	const char *radius_dir = RADIUS_DIR;
	
	fr_debug_flag = 2;
	log_dst = stdout;

	/*
	 *  Get options
	 */
	while ((opt = getopt(argc, argv, "c:d:Ff:hi:I:p:qr:s:Svw:xX")) != EOF) {
		switch (opt)
		{
		case 'c':
			limit = atoi(optarg);
			if (limit <= 0) {
				fprintf(stderr, "radsniff: Invalid number of packets \"%s\"\n", optarg);
				exit(1);
			}
			break;
		case 'd':
			radius_dir = optarg;
			break;
		case 'F':
			from_stdin = 1;
			to_stdout = 1;
			break;
		case 'f':
			pcap_filter = optarg;
			break;
		case 'h':
			usage(0);
			break;
		case 'i':
			from_dev = optarg;
			break;
		case 'I':
			from_file = optarg;
			break;
		case 'p':
			port = atoi(optarg);
			break;
		case 'q':
			if (fr_debug_flag > 0) {
				fr_debug_flag--;
			}
			break;
		case 'r':
			radius_filter = optarg;
			break;
		case 's':
			radius_secret = optarg;
			break;
		case 'S':
			do_sort = 1;
			break;
		case 'v':
			INFO(log_dst, "%s %s\n", radsniff_version, pcap_lib_version());
			exit(0);
			break;
		case 'w':
			to_file = optarg;
			break;
		case 'x':
		case 'X':
		  	fr_debug_flag++;
			break;
		default:
			usage(64);
		}
	}
	
	/* What's the point in specifying -F ?! */
	if (from_stdin && from_file && to_file) {
		usage(64);
	}
	
	/* Can't read from both... */
	if (from_file && from_dev) {
		usage(64);
	}
	
	/* Reading from file overrides stdin */
	if (from_stdin && (from_file || from_dev)) {
		from_stdin = 0;
	}
	
	/* Writing to file overrides stdout */
	if (to_file && to_stdout) {
		to_stdout = 0;
	}
	
	/*
	 *  If were writing pcap data stdout we *really* don't want to send
	 *  logging there as well.
	 */
 	log_dst = to_stdout ? stderr : stdout;

#if !defined(HAVE_PCAP_FOPEN_OFFLINE) || !defined(HAVE_PCAP_DUMP_FOPEN)
	if (from_stdin || to_stdout) {
		fprintf(stderr, "radsniff: PCAP streams not supported.\n");
		exit(64);
	}
#endif

	if (!pcap_filter) {
		pcap_filter = buffer;
		snprintf(buffer, sizeof(buffer), "udp port %d or %d or %d",
			 port, port + 1, 3799);
	}
	
	/*
	 *  There are times when we don't need the dictionaries.
	 */
	if (!to_stdout) {
		if (dict_init(radius_dir, RADIUS_DICTIONARY) < 0) {
			fr_perror("radsniff");
			exit(64);
		}
	}

	if (radius_filter) {
		parsecode = userparse(radius_filter, &filter_vps);
		if (parsecode == T_OP_INVALID) {
			fprintf(stderr, "radsniff: Invalid RADIUS filter \"%s\" (%s)\n", radius_filter, fr_strerror());
			exit(64);
		}
		
		if (!filter_vps) {
			fprintf(stderr, "radsniff: Empty RADIUS filter \"%s\"\n", radius_filter);
			exit(64);
		}

		filter_tree = rbtree_create((rbcmp) fr_packet_cmp, free, 0);
		if (!filter_tree) {
			fprintf(stderr, "radsniff: Failed creating filter tree\n");
			exit(1);
		}
	}

	/*
	 *  Setup the request tree
	 */
	request_tree = rbtree_create((rbcmp) fr_packet_cmp, free, 0);
	if (!request_tree) {
		fprintf(stderr, "radsniff: Failed creating request tree\n");
		exit(1);
	}

	/*
	 *  Allocate a null packet for decrypting attributes in CoA requests
	 */
	nullpacket = rad_alloc(NULL, 0);
	if (!nullpacket) {
		fprintf(stderr, "radsniff: Out of memory\n");
		exit(1);
	}

	/*
	 *  Get the default capture device
	 */
	if (!from_stdin && !from_file && !from_dev) {
		from_dev = pcap_lookupdev(errbuf);
		if (!from_dev) {
			fprintf(stderr, "radsniff: Failed discovering default interface (%s)\n", errbuf);
			exit(1);
		}

		INFO(log_dst, "Capturing from interface \"%s\"\n", from_dev);
	}
	
	/*
	 *  Print captures values which will be used
	 */
	if (fr_debug_flag > 2) {
				DEBUG1(log_dst, "Sniffing with options:\n");
		if (from_dev)	DEBUG1(log_dst, "  Device		   : [%s]\n", from_dev);
		if (limit > 0)	DEBUG1(log_dst, "  Capture limit (packets)  : [%d]\n", limit);
				DEBUG1(log_dst, "  PCAP filter	      : [%s]\n", pcap_filter);
				DEBUG1(log_dst, "  RADIUS secret	    : [%s]\n", radius_secret);
		if (filter_vps){DEBUG1(log_dst, "  RADIUS filter	    :\n");
			vp_printlist(log_dst, filter_vps);
		}
	}

	/*
	 *  Figure out whether were doing a reading from a file, doing a live
	 *  capture or reading from stdin.
	 */
	if (from_file) {
		in = pcap_open_offline(from_file, errbuf);
#ifdef HAVE_PCAP_FOPEN_OFFLINE
	} else if (from_stdin) {
		in = pcap_fopen_offline(stdin, errbuf);
#endif
	} else if (from_dev) {
		pcap_lookupnet(from_dev, &ip_addr, &ip_mask, errbuf);
		in = pcap_open_live(from_dev, 65536, 1, 1, errbuf);
	} else {
		fprintf(stderr, "radsniff: No capture devices available\n");
	}
	
	if (!in) {
		fprintf(stderr, "radsniff: Failed opening input (%s)\n", errbuf);
		exit(1);
	}

	if (to_file) {
		out = pcap_dump_open(in, to_file);
		if (!out) {
			fprintf(stderr, "radsniff: Failed opening output file (%s)\n", pcap_geterr(in));
			exit(1);
		}
#ifdef HAVE_PCAP_DUMP_FOPEN
	} else if (to_stdout) {
		out = pcap_dump_fopen(in, stdout);
		if (!out) {
			fprintf(stderr, "radsniff: Failed opening stdout (%s)\n", pcap_geterr(in));
			exit(1);
		}
#endif
	}

	/*
	 *  Apply the rules
	 */
	if (pcap_compile(in, &fp, pcap_filter, 0, ip_mask) < 0) {
		fprintf(stderr, "radsniff: Failed compiling PCAP filter (%s)\n", pcap_geterr(in));
		exit(1);
	}
	
	if (pcap_setfilter(in, &fp) < 0) {
		fprintf(stderr, "radsniff: Failed applying PCAP filter (%s)\n", pcap_geterr(in));
		exit(1);
	}

	/*
	 *  Enter the main capture loop...
	 */
	pcap_loop(in, limit, got_packet, NULL);
	
	/*
	 *  ...were done capturing.
	 */
	pcap_close(in);
	if (out) {
		pcap_dump_close(out);
	}
	
	if (filter_tree) {
		rbtree_free(filter_tree);
	}
	
	INFO(log_dst, "Done sniffing\n");
	
	return 0;
}
/*
 *	Initialize the request.
 */
static int request_init(char const *filename)
{
	FILE *fp;
	vp_cursor_t cursor;
	VALUE_PAIR *vp;
	bool filedone = false;

	/*
	 *	Determine where to read the VP's from.
	 */
	if (filename) {
		fp = fopen(filename, "r");
		if (!fp) {
			fprintf(stderr, "dhcpclient: Error opening %s: %s\n",
				filename, fr_syserror(errno));
			return 0;
		}
	} else {
		fp = stdin;
	}

	request = rad_alloc(NULL, false);

	/*
	 *	Read the VP's.
	 */
	if (fr_pair_list_afrom_file(NULL, &request->vps, fp, &filedone) < 0) {
		fr_perror("dhcpclient");
		rad_free(&request);
		if (fp != stdin) fclose(fp);
		return 1;
	}

	/*
	 *	Fix / set various options
	 */
	for (vp = fr_cursor_init(&cursor, &request->vps); vp; vp = fr_cursor_next(&cursor)) {

		if (vp->da->vendor == DHCP_MAGIC_VENDOR && vp->da->attr == PW_DHCP_MESSAGE_TYPE) {
			/*	Allow to set packet type using DHCP-Message-Type. */
			request->code = vp->vp_integer + PW_DHCP_OFFSET;

		} else if (!vp->da->vendor) switch (vp->da->attr) {

		default:
			break;

		/*
		 *	Allow it to set the packet type in
		 *	the attributes read from the file.
		 *	(this takes precedence over the command argument.)
		 */
		case PW_PACKET_TYPE:
			request->code = vp->vp_integer;
			break;

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

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

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

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

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

		case PW_PACKET_SRC_IPV6_ADDRESS:
			request->src_ipaddr.af = AF_INET6;
			request->src_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr;
			request->src_ipaddr.prefix = 128;
			break;
		} /* switch over the attribute */

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

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

	if (fp != stdin) fclose(fp);

	/*
	 *	And we're done.
	 */
	return 1;
}
static int tls_socket_recv(rad_listen_t *listener)
{
	int doing_init = FALSE;
	ssize_t rcode;
	RADIUS_PACKET *packet;
	REQUEST *request;
	listen_socket_t *sock = listener->data;
	fr_tls_status_t status;
	RADCLIENT *client = sock->client;

	if (!sock->packet) {
		sock->packet = rad_alloc(0);
		if (!sock->packet) return 0;

		sock->packet->sockfd = listener->fd;
		sock->packet->src_ipaddr = sock->other_ipaddr;
		sock->packet->src_port = sock->other_port;
		sock->packet->dst_ipaddr = sock->my_ipaddr;
		sock->packet->dst_port = sock->my_port;

		if (sock->request) sock->request->packet = sock->packet;
	}

	/*
	 *	Allocate a REQUEST for debugging.
	 */
	if (!sock->request) {
		sock->request = request = request_alloc();
		if (!sock->request) {
			radlog(L_ERR, "Out of memory");
			return 0;
		}

		rad_assert(request->packet == NULL);
		rad_assert(sock->packet != NULL);
		request->packet = sock->packet;

		request->component = "<core>";
		request->component = "<tls-connect>";

		/*
		 *	Not sure if we should do this on every packet...
		 */
		request->reply = rad_alloc(0);
		if (!request->reply) return 0;

		request->options = RAD_REQUEST_OPTION_DEBUG2;

		rad_assert(sock->ssn == NULL);

		sock->ssn = tls_new_session(listener->tls, sock->request,
					    listener->tls->require_client_cert);
		if (!sock->ssn) {
			request_free(&sock->request);
			sock->packet = NULL;
			return 0;
		}

		SSL_set_ex_data(sock->ssn->ssl, FR_TLS_EX_INDEX_REQUEST, (void *)request);
		SSL_set_ex_data(sock->ssn->ssl, FR_TLS_EX_INDEX_CERTS, (void *)&request->packet->vps);

		doing_init = TRUE;
	}

	rad_assert(sock->request != NULL);
	rad_assert(sock->request->packet != NULL);
	rad_assert(sock->packet != NULL);
	rad_assert(sock->ssn != NULL);

	request = sock->request;

	RDEBUG3("Reading from socket %d", request->packet->sockfd);
	PTHREAD_MUTEX_LOCK(&sock->mutex);
	rcode = read(request->packet->sockfd,
		     sock->ssn->dirty_in.data,
		     sizeof(sock->ssn->dirty_in.data));
	if ((rcode < 0) && (errno == ECONNRESET)) {
	do_close:
		PTHREAD_MUTEX_UNLOCK(&sock->mutex);
		tls_socket_close(listener);
		return 0;
	}
	
	if (rcode < 0) {
		RDEBUG("Error reading TLS socket: %s", strerror(errno));
		goto do_close;
	}

	/*
	 *	Normal socket close.
	 */
	if (rcode == 0) goto do_close;
	
	sock->ssn->dirty_in.used = rcode;
	memset(sock->ssn->dirty_in.data + sock->ssn->dirty_in.used,
	       0, 16);

	dump_hex("READ FROM SSL", sock->ssn->dirty_in.data, sock->ssn->dirty_in.used);

	/*
	 *	Catch attempts to use non-SSL.
	 */
	if (doing_init && (sock->ssn->dirty_in.data[0] != handshake)) {
		RDEBUG("Non-TLS data sent to TLS socket: closing");
		goto do_close;
	}
	
	/*
	 *	Skip ahead to reading application data.
	 */
	if (SSL_is_init_finished(sock->ssn->ssl)) goto app;

	if (!tls_handshake_recv(request, sock->ssn)) {
		RDEBUG("FAILED in TLS handshake receive");
		goto do_close;
	}
	
	if (sock->ssn->dirty_out.used > 0) {
		tls_socket_write(listener, request);
		PTHREAD_MUTEX_UNLOCK(&sock->mutex);
		return 0;
	}

app:
	/*
	 *	FIXME: Run the packet through a virtual server in
	 *	order to see if we like the certificate presented by
	 *	the client.
	 */

	status = tls_application_data(sock->ssn, request);
	RDEBUG("Application data status %d", status);

	if (status == FR_TLS_MORE_FRAGMENTS) {
		PTHREAD_MUTEX_UNLOCK(&sock->mutex);
		return 0;
	}

	if (sock->ssn->clean_out.used == 0) {
		PTHREAD_MUTEX_UNLOCK(&sock->mutex);
		return 0;
	}

	dump_hex("TUNNELED DATA", sock->ssn->clean_out.data, sock->ssn->clean_out.used);

	/*
	 *	If the packet is a complete RADIUS packet, return it to
	 *	the caller.  Otherwise...
	 */
	if ((sock->ssn->clean_out.used < 20) ||
	    (((sock->ssn->clean_out.data[2] << 8) | sock->ssn->clean_out.data[3]) != (int) sock->ssn->clean_out.used)) {
		RDEBUG("Received bad packet: Length %d contents %d",
		       sock->ssn->clean_out.used,
		       (sock->ssn->clean_out.data[2] << 8) | sock->ssn->clean_out.data[3]);
		goto do_close;
	}

	packet = sock->packet;
	packet->data = rad_malloc(sock->ssn->clean_out.used);
	packet->data_len = sock->ssn->clean_out.used;
	sock->ssn->record_minus(&sock->ssn->clean_out, packet->data, packet->data_len);
	packet->vps = NULL;
	PTHREAD_MUTEX_UNLOCK(&sock->mutex);

	if (!rad_packet_ok(packet, 0)) {
		RDEBUG("Received bad packet: %s", fr_strerror());
		tls_socket_close(listener);
		return 0;	/* do_close unlocks the mutex */
	}

	/*
	 *	Copied from src/lib/radius.c, rad_recv();
	 */
	if (fr_debug_flag) {
		char host_ipaddr[128];

		if ((packet->code > 0) && (packet->code < FR_MAX_PACKET_CODE)) {
			RDEBUG("tls_recv: %s packet from host %s port %d, id=%d, length=%d",
			       fr_packet_codes[packet->code],
			       inet_ntop(packet->src_ipaddr.af,
					 &packet->src_ipaddr.ipaddr,
					 host_ipaddr, sizeof(host_ipaddr)),
			       packet->src_port,
			       packet->id, (int) packet->data_len);
		} else {
			RDEBUG("tls_recv: Packet from host %s port %d code=%d, id=%d, length=%d",
			       inet_ntop(packet->src_ipaddr.af,
					 &packet->src_ipaddr.ipaddr,
					 host_ipaddr, sizeof(host_ipaddr)),
			       packet->src_port,
			       packet->code,
			       packet->id, (int) packet->data_len);
		}
	}

	FR_STATS_INC(auth, total_requests);

	return 1;
}
int proxy_tls_recv(rad_listen_t *listener)
{
	listen_socket_t *sock = listener->data;
	char buffer[256];
	RADIUS_PACKET *packet;
	uint8_t *data;
	ssize_t data_len;

	if (listener->status != RAD_LISTEN_STATUS_KNOWN) return 0;

	DEBUG3("Proxy SSL socket has data to read");
	PTHREAD_MUTEX_LOCK(&sock->mutex);
	data_len = proxy_tls_read(listener);
	PTHREAD_MUTEX_UNLOCK(&sock->mutex);

	if (data_len < 0) {
		DEBUG("Closing TLS socket to home server");
		PTHREAD_MUTEX_LOCK(&sock->mutex);
		tls_socket_close(listener);
		PTHREAD_MUTEX_UNLOCK(&sock->mutex);
		return 0;
	}

	if (data_len == 0) return 0; /* not done yet */

	data = sock->data;

	packet = rad_alloc(sock, false);
	packet->sockfd = listener->fd;
	packet->src_ipaddr = sock->other_ipaddr;
	packet->src_port = sock->other_port;
	packet->dst_ipaddr = sock->my_ipaddr;
	packet->dst_port = sock->my_port;
	packet->code = data[0];
	packet->id = data[1];
	packet->data_len = data_len;
	packet->data = talloc_array(packet, uint8_t, packet->data_len);
	memcpy(packet->data, data, packet->data_len);
	memcpy(packet->vector, packet->data + 4, 16);

	/*
	 *	FIXME: Client MIB updates?
	 */
	switch (packet->code) {
	case PW_CODE_ACCESS_ACCEPT:
	case PW_CODE_ACCESS_CHALLENGE:
	case PW_CODE_ACCESS_REJECT:
		break;

#ifdef WITH_ACCOUNTING
	case PW_CODE_ACCOUNTING_RESPONSE:
		break;
#endif

	default:
		/*
		 *	FIXME: Update MIB for packet types?
		 */
		ERROR("Invalid packet code %d sent to a proxy port "
		       "from home server %s port %d - ID %d : IGNORED",
		       packet->code,
		       ip_ntoh(&packet->src_ipaddr, buffer, sizeof(buffer)),
		       packet->src_port, packet->id);
		rad_free(&packet);
		return 0;
	}

	if (!request_proxy_reply(packet)) {
		rad_free(&packet);
		return 0;
	}

	return 1;
}
static REQUEST *request_setup(FILE *fp)
{
	VALUE_PAIR *vp;
	REQUEST *request;
	vp_cursor_t cursor;

	/*
	 *	Create and initialize the new request.
	 */
	request = request_alloc(NULL);

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

	request->reply = rad_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 (readvp2(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->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_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->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->length;
				if (len2 < 17) len2 = 17;

				p = talloc_zero_array(vp, uint8_t, len2);

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

				rad_chap_encode(request->packet,
						p,
						fr_rand() & 0xff, vp);
				vp->vp_octets = p;
				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! */
		{
			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);
			rad_assert(da != NULL);
			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 */
#endif

	if (debug_flag) {
		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);
			}

			vp_print(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 = debug_flag;
	request->log.func = vradlog_request;

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

	return request;
}
int main(int argc, char **argv)
{
	RADIUS_PACKET *req;
	char *p;
	int c;
	uint16_t port = 0;
	char *filename = NULL;
	FILE *fp;
	int id;
	int force_af = AF_UNSPEC;

	static fr_log_t radclient_log = {
		.colourise = true,
		.fd = STDOUT_FILENO,
		.dst = L_DST_STDOUT,
		.file = NULL,
		.debug_file = NULL,
	};

	radlog_init(&radclient_log, false);

	/*
	 *	We probably don't want to free the talloc autofree context
	 *	directly, so we'll allocate a new context beneath it, and
	 *	free that before any leak reports.
	 */
	TALLOC_CTX *autofree = talloc_init("main");

	id = ((int)getpid() & 0xff);
	fr_debug_flag = 0;

	set_radius_dir(autofree, RADIUS_DIR);

	while ((c = getopt(argc, argv, "46c:d:D:f:hi:qst:r:S:xXv")) != EOF)
	{
		switch(c) {
		case '4':
			force_af = AF_INET;
			break;
		case '6':
			force_af = AF_INET6;
			break;
		case 'd':
			set_radius_dir(autofree, optarg);
			break;
		case 'D':
			main_config.dictionary_dir = talloc_typed_strdup(NULL, optarg);
			break;
		case 'f':
			filename = optarg;
			break;
		case 'q':
			do_output = 0;
			break;
		case 'x':
			debug_flag++;
			fr_debug_flag++;
			break;

		case 'X':
#if 0
		  sha1_data_problems = 1; /* for debugging only */
#endif
		  break;



		case 'r':
			if (!isdigit((int) *optarg))
				usage();
			retries = atoi(optarg);
			break;
		case 'i':
			if (!isdigit((int) *optarg))
				usage();
			id = atoi(optarg);
			if ((id < 0) || (id > 255)) {
				usage();
			}
			break;
		case 's':
			do_summary = 1;
			break;
		case 't':
			if (!isdigit((int) *optarg))
				usage();
			timeout = atof(optarg);
			break;
		case 'v':
			printf("radeapclient: $Id: 69643e042c5a7c9053977756a5623e3c07598f2e $ built on " __DATE__ " at " __TIME__ "\n");
			exit(0);
			break;
	       case 'S':
		       fp = fopen(optarg, "r");
		       if (!fp) {
			       fprintf(stderr, "radclient: Error opening %s: %s\n",
				       optarg, fr_syserror(errno));
			       exit(1);
		       }
		       if (fgets(filesecret, sizeof(filesecret), fp) == NULL) {
			       fprintf(stderr, "radclient: Error reading %s: %s\n",
				       optarg, fr_syserror(errno));
			       exit(1);
		       }
		       fclose(fp);

		       /* truncate newline */
		       p = filesecret + strlen(filesecret) - 1;
		       while ((p >= filesecret) &&
			      (*p < ' ')) {
			       *p = '\0';
			       --p;
		       }

		       if (strlen(filesecret) < 2) {
			       fprintf(stderr, "radclient: Secret in %s is too short\n", optarg);
			       exit(1);
		       }
		       secret = filesecret;
		       break;
		case 'h':
		default:
			usage();
			break;
		}
	}
	argc -= (optind - 1);
	argv += (optind - 1);

	if ((argc < 3)  ||
	    ((!secret) && (argc < 4))) {
		usage();
	}

	if (!main_config.dictionary_dir) {
		main_config.dictionary_dir = DICTDIR;
	}

	/*
	 *	Read the distribution dictionaries first, then
	 *	the ones in raddb.
	 */
	DEBUG2("including dictionary file %s/%s", main_config.dictionary_dir, RADIUS_DICTIONARY);
	if (dict_init(main_config.dictionary_dir, RADIUS_DICTIONARY) != 0) {
		ERROR("Errors reading dictionary: %s",
		      fr_strerror());
		exit(1);
	}

	/*
	 *	It's OK if this one doesn't exist.
	 */
	int rcode = dict_read(radius_dir, RADIUS_DICTIONARY);
	if (rcode == -1) {
		ERROR("Errors reading %s/%s: %s", radius_dir, RADIUS_DICTIONARY,
		      fr_strerror());
		exit(1);
	}

	/*
	 *	We print this after reading it.  That way if
	 *	it doesn't exist, it's OK, and we don't print
	 *	anything.
	 */
	if (rcode == 0) {
		DEBUG2("including dictionary file %s/%s", radius_dir, RADIUS_DICTIONARY);
	}

	req = rad_alloc(NULL, 1);
	if (!req) {
		fr_perror("radclient");
		exit(1);
	}

#if 0
	{
		FILE *randinit;

		if((randinit = fopen("/dev/urandom", "r")) == NULL)
		{
			perror("/dev/urandom");
		} else {
			fread(randctx.randrsl, 256, 1, randinit);
			fclose(randinit);
		}
	}
	fr_randinit(&randctx, 1);
#endif

	req->id = id;

	/*
	 *	Resolve hostname.
	 */
	if (force_af == AF_UNSPEC) force_af = AF_INET;
	req->dst_ipaddr.af = force_af;
	if (strcmp(argv[1], "-") != 0) {
		char const *hostname = argv[1];
		char const *portname = argv[1];
		char buffer[256];

		if (*argv[1] == '[') { /* IPv6 URL encoded */
			p = strchr(argv[1], ']');
			if ((size_t) (p - argv[1]) >= sizeof(buffer)) {
				usage();
			}

			memcpy(buffer, argv[1] + 1, p - argv[1] - 1);
			buffer[p - argv[1] - 1] = '\0';

			hostname = buffer;
			portname = p + 1;

		}
		p = strchr(portname, ':');
		if (p && (strchr(p + 1, ':') == NULL)) {
			*p = '\0';
			portname = p + 1;
		} else {
			portname = NULL;
		}

		if (ip_hton(&req->dst_ipaddr, force_af, hostname, false) < 0) {
			fprintf(stderr, "radclient: Failed to find IP address for host %s: %s\n", hostname, fr_syserror(errno));
			exit(1);
		}

		/*
		 *	Strip port from hostname if needed.
		 */
		if (portname) port = atoi(portname);
	}

	/*
	 *	See what kind of request we want to send.
	 */
	if (strcmp(argv[2], "auth") == 0) {
		if (port == 0) port = getport("radius");
		if (port == 0) port = PW_AUTH_UDP_PORT;
		req->code = PW_CODE_AUTHENTICATION_REQUEST;

	} else if (strcmp(argv[2], "acct") == 0) {
		if (port == 0) port = getport("radacct");
		if (port == 0) port = PW_ACCT_UDP_PORT;
		req->code = PW_CODE_ACCOUNTING_REQUEST;
		do_summary = 0;

	} else if (strcmp(argv[2], "status") == 0) {
		if (port == 0) port = getport("radius");
		if (port == 0) port = PW_AUTH_UDP_PORT;
		req->code = PW_CODE_STATUS_SERVER;

	} else if (strcmp(argv[2], "disconnect") == 0) {
		if (port == 0) port = PW_POD_UDP_PORT;
		req->code = PW_CODE_DISCONNECT_REQUEST;

	} else if (isdigit((int) argv[2][0])) {
		if (port == 0) port = getport("radius");
		if (port == 0) port = PW_AUTH_UDP_PORT;
		req->code = atoi(argv[2]);
	} else {
		usage();
	}
	req->dst_port = port;

	/*
	 *	Add the secret.
	 */
	if (argv[3]) secret = argv[3];

	/*
	 *	Read valuepairs.
	 *	Maybe read them, from stdin, if there's no
	 *	filename, or if the filename is '-'.
	 */
	if (filename && (strcmp(filename, "-") != 0)) {
		fp = fopen(filename, "r");
		if (!fp) {
			fprintf(stderr, "radclient: Error opening %s: %s\n",
				filename, fr_syserror(errno));
			exit(1);
		}
	} else {
		fp = stdin;
	}

	/*
	 *	Send request.
	 */
	if ((req->sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
		perror("radclient: socket: ");
		exit(1);
	}

	while(!filedone) {
		if (req->vps) pairfree(&req->vps);
		if (readvp2(&req->vps, NULL, fp, &filedone) < 0) {
			fr_perror("radeapclient");
			break;
		}

		sendrecv_eap(req);
	}

	if(do_summary) {
		printf("\n\t   Total approved auths:  %d\n", totalapp);
		printf("\t     Total denied auths:  %d\n", totaldeny);
	}

	talloc_free(autofree);

	return 0;
}

/*
 * given a radius request with some attributes in the EAP range, build
 * them all into a single EAP-Message body.
 *
 * Note that this function will build multiple EAP-Message bodies
 * if there are multiple eligible EAP-types. This is incorrect, as the
 * recipient will in fact concatenate them.
 *
 * XXX - we could break the loop once we process one type. Maybe this
 *       just deserves an assert?
 *
 */
static void map_eap_methods(RADIUS_PACKET *req)
{
	VALUE_PAIR *vp, *vpnext;
	int id, eapcode;
	int eap_method;

	eap_packet_t *pt_ep = talloc_zero(req, eap_packet_t);

	vp = pairfind(req->vps, ATTRIBUTE_EAP_ID, 0, TAG_ANY);
	if(!vp) {
		id = ((int)getpid() & 0xff);
	} else {
		id = vp->vp_integer;
	}

	vp = pairfind(req->vps, ATTRIBUTE_EAP_CODE, 0, TAG_ANY);
	if(!vp) {
		eapcode = PW_EAP_REQUEST;
	} else {
		eapcode = vp->vp_integer;
	}

	for(vp = req->vps; vp != NULL; vp = vpnext) {
		/* save it in case it changes! */
		vpnext = vp->next;

		if(vp->da->attr >= ATTRIBUTE_EAP_BASE &&
		   vp->da->attr < ATTRIBUTE_EAP_BASE+256) {
			break;
		}
	}

	if(!vp) {
		return;
	}

	eap_method = vp->da->attr - ATTRIBUTE_EAP_BASE;

	switch(eap_method) {
	case PW_EAP_IDENTITY:
	case PW_EAP_NOTIFICATION:
	case PW_EAP_NAK:
	case PW_EAP_MD5:
	case PW_EAP_OTP:
	case PW_EAP_GTC:
	case PW_EAP_TLS:
	case PW_EAP_LEAP:
	case PW_EAP_TTLS:
	case PW_EAP_PEAP:
	default:
		/*
		 * no known special handling, it is just encoded as an
		 * EAP-message with the given type.
		 */

		/* nuke any existing EAP-Messages */
		pairdelete(&req->vps, PW_EAP_MESSAGE, 0, TAG_ANY);

		pt_ep->code = eapcode;
		pt_ep->id = id;
		pt_ep->type.num = eap_method;
		pt_ep->type.length = vp->length;

		pt_ep->type.data = talloc_memdup(vp, vp->vp_octets, vp->length);
		talloc_set_type(pt_ep->type.data, uint8_t);

		eap_basic_compose(req, pt_ep);
	}
}
/*
 *	Initialize the request.
 */
static int request_init(char const *filename)
{
	FILE *fp;
	vp_cursor_t cursor;
	VALUE_PAIR *vp;
	int filedone = 0;

	/*
	 *	Determine where to read the VP's from.
	 */
	if (filename) {
		fp = fopen(filename, "r");
		if (!fp) {
			fprintf(stderr, "dhcpclient: Error opening %s: %s\n",
				filename, fr_syserror(errno));
			return 0;
		}
	} else {
		fp = stdin;
	}

	request = rad_alloc(NULL, 0);

	/*
	 *	Read the VP's.
	 */
	request->vps = readvp2(NULL, fp, &filedone, "dhcpclient:");
	if (!request->vps) {
		rad_free(&request);
		if (fp != stdin) fclose(fp);
		return 1;
	}

	/*
	 *	Fix / set various options
	 */
	for (vp = paircursor(&cursor, &request->vps); vp; vp = pairnext(&cursor)) {
		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->code = vp->vp_integer;
			break;

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

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

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

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

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

		case PW_PACKET_SRC_IPV6_ADDRESS:
			request->src_ipaddr.af = AF_INET6;
			request->src_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr;
			break;
		} /* switch over the attribute */

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

	if (fp != stdin) fclose(fp);

	/*
	 *	And we're done.
	 */
	return 1;
}
main(int argc, char *argv[])
{
	int filedone;
	RADIUS_PACKET *req,*req2;
	VALUE_PAIR *vp, *vpkey, *vpextra;
	extern unsigned int sha1_data_problems;

	req = NULL;
	req2 = NULL;
	filedone = 0;

	if(argc>1) {
	  sha1_data_problems = 1;
	}

	if (dict_init(radius_dir, RADIUS_DICTIONARY) < 0) {
		ERROR("%s", fr_strerror());
		return 1;
	}

	req = rad_alloc(NULL, true)
	if (!req) {
		ERROR("%s", fr_strerror());
		exit(1);
	}

	req2 = rad_alloc(NULL, true);
	if (!req2) {
		ERROR("%s", fr_strerror());
		exit(1);
	}

	while(!filedone) {
		if (req->vps) pairfree(&req->vps);
		if (req2->vps) pairfree(&req2->vps);

		if (readvp2(&req->vps, NULL, stdin, &filedone) < 0) {
			ERROR("%s", fr_strerror());
			break;
		}

		if (fr_debug_flag > 1) {
			DEBUG("Read:");
			vp_printlist(stdout, req->vps);
		}

		map_eapsim_types(req);
		map_eap_methods(req);

		if (fr_debug_flag > 1) {
			DEBUG("Mapped to:");
			vp_printlist(stdout, req->vps);
		}

		/* find the EAP-Message, copy it to req2 */
		vp = paircopy2(NULL, req->vps, PW_EAP_MESSAGE, 0, TAG_ANY);

		if(!vp) continue;

		pairadd(&req2->vps, vp);

		/* only call unmap for sim types here */
		unmap_eap_methods(req2);
		unmap_eapsim_types(req2);

		if (fr_debug_flag > 1) {
			DEBUG("Unmapped to:");
			vp_printlist(stdout, req2->vps);
		}

		vp = pairfind(req2->vps, PW_EAP_SIM_MAC, 0, TAG_ANY);
		vpkey   = pairfind(req->vps, PW_EAP_SIM_KEY, 0, TAG_ANY);
		vpextra = pairfind(req->vps, PW_EAP_SIM_EXTRA, 0, TAG_ANY);

		if(vp != NULL && vpkey != NULL && vpextra!=NULL) {
			uint8_t calcmac[16];

			/* find the EAP-Message, copy it to req2 */

			memset(calcmac, 0, sizeof(calcmac));
			DEBUG("Confirming MAC...");
			if(eapsim_checkmac(req2->vps, vpkey->vp_strvalue,
					   vpextra->vp_strvalue, vpextra->length,
					   calcmac)) {
				DEBUG("succeed");
			} else {
				int i, j;

				DEBUG("calculated MAC (");
				for (i = 0; i < 20; i++) {
					if(j==4) {
						DEBUG("_");
						j=0;
					}
					j++;

					DEBUG("%02x", calcmac[i]);
				}
				DEBUG("did not match");
			}
		}

		fflush(stdout);
	}
}