コード例 #1
0
ファイル: eap_tls.c プロジェクト: Gejove/freeradius-server
int eaptls_success(EAP_HANDLER *handler, int peap_flag)
{
	EAPTLS_PACKET	reply;
	REQUEST *request = handler->request;
	tls_session_t *tls_session = handler->opaque;

	handler->finished = TRUE;
	reply.code = FR_TLS_SUCCESS;
	reply.length = TLS_HEADER_LEN;
	reply.flags = peap_flag;
	reply.data = NULL;
	reply.dlen = 0;

	tls_success(tls_session, request);

	/*
	 *	Call compose AFTER checking for cached data.
	 */
	eaptls_compose(handler->eap_ds, &reply);

	/*
	 *	Automatically generate MPPE keying material.
	 */
	if (tls_session->prf_label) {
		eaptls_gen_mppe_keys(&handler->request->reply->vps,
				     tls_session->ssl, tls_session->prf_label);
	} else {
		RDEBUGW("Not adding MPPE keys because there is no PRF label");
	}

	eaptls_gen_eap_key(tls_session->ssl,
			   handler->eap_type, &handler->request->reply->vps);
	return 1;
}
コード例 #2
0
ファイル: exec.c プロジェクト: Gejove/freeradius-server
/** Start a process
 *
 * @param cmd Command to execute. This is parsed into argv[] parts,
 * 	then each individual argv part is xlat'ed.
 * @param request Current reuqest
 * @param exec_wait set to 1 if you want to read from or write to child
 * @param[in,out] input_fd pointer to int, receives the stdin file.
 * 	descriptor. Set to NULL and the child will have /dev/null on stdin
 * @param[in,out] output_fd pinter to int, receives the stdout file
 * 	descriptor. Set to NULL and child will have /dev/null on stdout.
 * @param input_pairs list of value pairs - these will be put into
 * 	the environment variables of the child.
 * @param shell_escape
 * @return PID of the child process, -1 on error.
 */
pid_t radius_start_program(const char *cmd, REQUEST *request,
			int exec_wait,
			int *input_fd,
			int *output_fd,
			VALUE_PAIR *input_pairs,
			int shell_escape)
{
#ifndef __MINGW32__
	char *p;
	VALUE_PAIR *vp;
	int n;
	int to_child[2] = {-1, -1};
	int from_child[2] = {-1, -1};
	pid_t pid;
#endif
	int argc;
	int i;
	char *argv[MAX_ARGV];
	char argv_buf[4096];
#define MAX_ENVP 1024
	char *envp[MAX_ENVP];

	argc = rad_expand_xlat(request, cmd, MAX_ARGV, (const char **) argv, 1,
				sizeof(argv_buf), argv_buf);
	if (argc <= 0) {
		RDEBUG("Exec: invalid command line '%s'.", cmd);
		return -1;
	}

#ifndef __MINGW32__
	/*
	 *	Open a pipe for child/parent communication, if necessary.
	 */
	if (exec_wait) {
		if (input_fd) {
			if (pipe(to_child) != 0) {
				RDEBUG("Exec: Couldn't open pipe to child: %s",
				       strerror(errno));
				return -1;
			}
		}
		if (output_fd) {
			if (pipe(from_child) != 0) {
				RDEBUG("Exec: Couldn't open pipe from child: %s",
				       strerror(errno));
				/* safe because these either need closing or are == -1 */
				close(to_child[0]);
				close(to_child[1]);
				return -1;
			}
		}
	}

	envp[0] = NULL;

	if (input_pairs) {
		int envlen;
		char buffer[1024];

		/*
		 *	Set up the environment variables in the
		 *	parent, so we don't call libc functions that
		 *	hold mutexes.  They might be locked when we fork,
		 *	and will remain locked in the child.
		 */
		envlen = 0;

		for (vp = input_pairs; vp != NULL; vp = vp->next) {
			/*
			 *	Hmm... maybe we shouldn't pass the
			 *	user's password in an environment
			 *	variable...
			 */
			snprintf(buffer, sizeof(buffer), "%s=", vp->da->name);
			if (shell_escape) {
				for (p = buffer; *p != '='; p++) {
					if (*p == '-') {
						*p = '_';
					} else if (isalpha((int) *p)) {
						*p = toupper(*p);
					}
				}
			}

			n = strlen(buffer);
			vp_prints_value(buffer+n, sizeof(buffer) - n, vp, shell_escape);

			envp[envlen++] = strdup(buffer);

			/*
			 *	Don't add too many attributes.
			 */
			if (envlen == (MAX_ENVP - 1)) break;
		}
		envp[envlen] = NULL;
	}

	if (exec_wait) {
		pid = rad_fork();	/* remember PID */
	} else {
		pid = fork();		/* don't wait */
	}

	if (pid == 0) {
		int devnull;

		/*
		 *	Child process.
		 *
		 *	We try to be fail-safe here. So if ANYTHING
		 *	goes wrong, we exit with status 1.
		 */

		/*
		 *	Open STDIN to /dev/null
		 */
		devnull = open("/dev/null", O_RDWR);
		if (devnull < 0) {
			RDEBUG("Exec: Failed opening /dev/null: %s\n",
			       strerror(errno));
			exit(1);
		}

		/*
		 *	Only massage the pipe handles if the parent
		 *	has created them.
		 */
		if (exec_wait) {

			if (input_fd) {
				close(to_child[1]);
				dup2(to_child[0], STDIN_FILENO);
			} else {
				dup2(devnull, STDIN_FILENO);
			}

			if (output_fd) {
				close(from_child[0]);
				dup2(from_child[1], STDOUT_FILENO);
			} else {
				dup2(devnull, STDOUT_FILENO);
			}

		} else {	/* no pipe, STDOUT should be /dev/null */
			dup2(devnull, STDIN_FILENO);
			dup2(devnull, STDOUT_FILENO);
		}

		/*
		 *	If we're not debugging, then we can't do
		 *	anything with the error messages, so we throw
		 *	them away.
		 *
		 *	If we are debugging, then we want the error
		 *	messages to go to the STDERR of the server.
		 */
		if (debug_flag == 0) {
			dup2(devnull, STDERR_FILENO);
		}
		close(devnull);

		/*
		 *	The server may have MANY FD's open.  We don't
		 *	want to leave dangling FD's for the child process
		 *	to play funky games with, so we close them.
		 */
		closefrom(3);

		execve(argv[0], argv, envp);
		RDEBUGW("Exec: failed to execute %s: %s",
		       argv[0], strerror(errno));
		exit(1);
	}

	/*
	 *	Free child environment variables
	 */
	for (i = 0; envp[i] != NULL; i++) {
		free(envp[i]);
	}

	/*
	 *	Parent process.
	 */
	if (pid < 0) {
		RDEBUG("Exec: Couldn't fork %s: %s",
		       argv[0], strerror(errno));
		if (exec_wait) {
			/* safe because these either need closing or are == -1 */
			close(to_child[0]);
			close(to_child[1]);
			close(from_child[0]);
			close(from_child[0]);
		}
		return -1;
	}

	/*
	 *	We're not waiting, exit, and ignore any child's status.
	 */
	if (exec_wait) {
		/*
		 *	Close the ends of the pipe(s) the child is using
		 *	return the ends of the pipe(s) our caller wants
		 *
		 */
		if (input_fd) {
			*input_fd = to_child[1];
			close(to_child[0]);
		}
		if (output_fd) {
			*output_fd = from_child[0];
			close(from_child[1]);
		}
	}

	return pid;
#else
	if (exec_wait) {
		RDEBUG("Exec: Wait is not supported");
		return -1;
	}
	
	{
		/*
		 *	The _spawn and _exec families of functions are
		 *	found in Windows compiler libraries for
		 *	portability from UNIX. There is a variety of
		 *	functions, including the ability to pass
		 *	either a list or array of parameters, to
		 *	search in the PATH or otherwise, and whether
		 *	or not to pass an environment (a set of
		 *	environment variables). Using _spawn, you can
		 *	also specify whether you want the new process
		 *	to close your program (_P_OVERLAY), to wait
		 *	until the new process is finished (_P_WAIT) or
		 *	for the two to run concurrently (_P_NOWAIT).
		 
		 *	_spawn and _exec are useful for instances in
		 *	which you have simple requirements for running
		 *	the program, don't want the overhead of the
		 *	Windows header file, or are interested
		 *	primarily in portability.
		 */

		/*
		 *	FIXME: check return code... what is it?
		 */
		_spawnve(_P_NOWAIT, argv[0], argv, envp);
	}

	return 0;
#endif
}
コード例 #3
0
/** 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;
}
コード例 #4
0
/*
 *	See if a user is already logged in. Sets request->simul_count to the
 *	current session count for this user and sets request->simul_mpp to 2
 *	if it looks like a multilink attempt based on the requested IP
 *	address, otherwise leaves request->simul_mpp alone.
 *
 *	Check twice. If on the first pass the user exceeds his
 *	max. number of logins, do a second pass and validate all
 *	logins by querying the terminal server (using eg. SNMP).
 */
static rlm_rcode_t mod_checksimul(void *instance, REQUEST *request)
{
	struct radutmp	u;
	int		fd;
	VALUE_PAIR	*vp;
	uint32_t	ipno = 0;
	char		*call_num = NULL;
	int		rcode;
	rlm_radutmp_t	*inst = instance;
	char		login[256];
	char		filename[1024];

	/*
	 *	Get the filename, via xlat.
	 */
	radius_xlat(filename, sizeof(filename), inst->filename, request, NULL, NULL);

	if ((fd = open(filename, O_RDWR)) < 0) {
		/*
		 *	If the file doesn't exist, then no users
		 *	are logged in.
		 */
		if (errno == ENOENT) {
			request->simul_count=0;
			return RLM_MODULE_OK;
		}

		/*
		 *	Error accessing the file.
		 */
		radlog(L_ERR, "rlm_radumtp: Error accessing file %s: %s",
		       filename, strerror(errno));
		return RLM_MODULE_FAIL;
	}

	*login = '******';
	radius_xlat(login, sizeof(login), inst->username, request, NULL, NULL);
	if (!*login) {
		close(fd);
		return RLM_MODULE_NOOP;
	}

	/*
	 *	WTF?  This is probably wrong... we probably want to
	 *	be able to check users across multiple session accounting
	 *	methods.
	 */
	request->simul_count = 0;

	/*
	 *	Loop over utmp, counting how many people MAY be logged in.
	 */
	while (read(fd, &u, sizeof(u)) == sizeof(u)) {
		if (((strncmp(login, u.login, RUT_NAMESIZE) == 0) ||
		     (!inst->case_sensitive &&
		      (strncasecmp(login, u.login, RUT_NAMESIZE) == 0))) &&
		    (u.type == P_LOGIN)) {
			++request->simul_count;
		}
	}

	/*
	 *	The number of users logged in is OK,
	 *	OR, we've been told to not check the NAS.
	 */
	if ((request->simul_count < request->simul_max) ||
	    !inst->check_nas) {
		close(fd);
		return RLM_MODULE_OK;
	}
	lseek(fd, (off_t)0, SEEK_SET);

	/*
	 *	Setup some stuff, like for MPP detection.
	 */
	if ((vp = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS, 0, TAG_ANY)) != NULL)
		ipno = vp->vp_ipaddr;
	if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID, 0, TAG_ANY)) != NULL)
		call_num = vp->vp_strvalue;

	/*
	 *	lock the file while reading/writing.
	 */
	rad_lockfd(fd, LOCK_LEN);

	/*
	 *	FIXME: If we get a 'Start' for a user/nas/port which is
	 *	listed, but for which we did NOT get a 'Stop', then
	 *	it's not a duplicate session.  This happens with
	 *	static IP's like DSL.
	 */
	request->simul_count = 0;
	while (read(fd, &u, sizeof(u)) == sizeof(u)) {
		if (((strncmp(login, u.login, RUT_NAMESIZE) == 0) ||
		     (!inst->case_sensitive &&
		      (strncasecmp(login, u.login, RUT_NAMESIZE) == 0))) &&
		    (u.type == P_LOGIN)) {
			char session_id[sizeof(u.session_id) + 1];
			char utmp_login[sizeof(u.login) + 1];

			strlcpy(session_id, u.session_id, sizeof(session_id));

			/*
			 *	The login name MAY fill the whole field,
			 *	and thus won't be zero-filled.
			 *
			 *	Note that we take the user name from
			 *	the utmp file, as that's the canonical
			 *	form.  The 'login' variable may contain
			 *	a string which is an upper/lowercase
			 *	version of u.login.  When we call the
			 *	routine to check the terminal server,
			 *	the NAS may be case sensitive.
			 *
			 *	e.g. We ask if "bob" is using a port,
			 *	and the NAS says "no", because "BOB"
			 *	is using the port.
			 */
			strlcpy(utmp_login, u.login, sizeof(u.login));

			/*
			 *	rad_check_ts may take seconds
			 *	to return, and we don't want
			 *	to block everyone else while
			 *	that's happening.  */
			rad_unlockfd(fd, LOCK_LEN);
			rcode = rad_check_ts(u.nas_address, u.nas_port,
					     utmp_login, session_id);
			rad_lockfd(fd, LOCK_LEN);

			if (rcode == 0) {
				/*
				 *	Stale record - zap it.
				 */
				session_zap(request, u.nas_address,
					    u.nas_port, login, session_id,
					    u.framed_address, u.proto,0);
			}
			else if (rcode == 1) {
				/*
				 *	User is still logged in.
				 */
				++request->simul_count;

				/*
				 *	Does it look like a MPP attempt?
				 */
				if (strchr("SCPA", u.proto) &&
				    ipno && u.framed_address == ipno)
					request->simul_mpp = 2;
				else if (strchr("SCPA", u.proto) && call_num &&
					!strncmp(u.caller_id,call_num,16))
					request->simul_mpp = 2;
			}
			else {
				/*
				 *	Failed to check the terminal
				 *	server for duplicate logins:
				 *	Return an error.
				 */
				close(fd);
				RDEBUGW("Failed to check the terminal server for user '%s'.", utmp_login);
				return RLM_MODULE_FAIL;
			}
		}
	}
	close(fd);		/* and implicitely release the locks */

	return RLM_MODULE_OK;
}
コード例 #5
0
/*
 *	Store logins in the RADIUS utmp file.
 */
static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
{
	struct radutmp	ut, u;
	VALUE_PAIR	*vp;
	int		status = -1;
	int		protocol = -1;
	time_t		t;
	int		fd;
	int		port_seen = 0;
	int		off;
	rlm_radutmp_t	*inst = instance;
	char		buffer[256];
	char		filename[1024];
	char		ip_name[32]; /* 255.255.255.255 */
	const char	*nas;
	NAS_PORT	*cache;
	int		r;

	if (request->packet->src_ipaddr.af != AF_INET) {
		DEBUG("rlm_radutmp: IPv6 not supported!");
		return RLM_MODULE_NOOP;
	}

	/*
	 *	Which type is this.
	 */
	if ((vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE, 0, TAG_ANY)) == NULL) {
		RDEBUG("No Accounting-Status-Type record.");
		return RLM_MODULE_NOOP;
	}
	status = vp->vp_integer;

	/*
	 *	Look for weird reboot packets.
	 *
	 *	ComOS (up to and including 3.5.1b20) does not send
	 *	standard PW_STATUS_ACCOUNTING_XXX messages.
	 *
	 *	Check for:  o no Acct-Session-Time, or time of 0
	 *		    o Acct-Session-Id of "00000000".
	 *
	 *	We could also check for NAS-Port, that attribute
	 *	should NOT be present (but we don't right now).
	 */
	if ((status != PW_STATUS_ACCOUNTING_ON) &&
	    (status != PW_STATUS_ACCOUNTING_OFF)) do {
		int check1 = 0;
		int check2 = 0;

		if ((vp = pairfind(request->packet->vps, PW_ACCT_SESSION_TIME, 0, TAG_ANY))
		     == NULL || vp->vp_date == 0)
			check1 = 1;
		if ((vp = pairfind(request->packet->vps, PW_ACCT_SESSION_ID, 0, TAG_ANY))
		     != NULL && vp->length == 8 &&
		     memcmp(vp->vp_strvalue, "00000000", 8) == 0)
			check2 = 1;
		if (check1 == 0 || check2 == 0) {
			break;
		}
		radlog(L_INFO, "rlm_radutmp: converting reboot records.");
		if (status == PW_STATUS_STOP)
			status = PW_STATUS_ACCOUNTING_OFF;
		if (status == PW_STATUS_START)
			status = PW_STATUS_ACCOUNTING_ON;
	} while(0);

	time(&t);
	memset(&ut, 0, sizeof(ut));
	ut.porttype = 'A';
	ut.nas_address = htonl(INADDR_NONE);

	/*
	 *	First, find the interesting attributes.
	 */
	for (vp = request->packet->vps; vp; vp = vp->next) {
		if (!vp->da->vendor) switch (vp->da->attr) {
			case PW_LOGIN_IP_HOST:
			case PW_FRAMED_IP_ADDRESS:
				ut.framed_address = vp->vp_ipaddr;
				break;
			case PW_FRAMED_PROTOCOL:
				protocol = vp->vp_integer;
				break;
			case PW_NAS_IP_ADDRESS:
				ut.nas_address = vp->vp_ipaddr;
				break;
			case PW_NAS_PORT:
				ut.nas_port = vp->vp_integer;
				port_seen = 1;
				break;
			case PW_ACCT_DELAY_TIME:
				ut.delay = vp->vp_integer;
				break;
			case PW_ACCT_SESSION_ID:
				/*
				 *	If length > 8, only store the
				 *	last 8 bytes.
				 */
				off = vp->length - sizeof(ut.session_id);
				/*
				 * 	Ascend is br0ken - it adds a \0
				 * 	to the end of any string.
				 * 	Compensate.
				 */
				if (vp->length > 0 &&
				    vp->vp_strvalue[vp->length - 1] == 0)
					off--;
				if (off < 0) off = 0;
				memcpy(ut.session_id, vp->vp_strvalue + off,
					sizeof(ut.session_id));
				break;
			case PW_NAS_PORT_TYPE:
				if (vp->vp_integer <= 4)
					ut.porttype = porttypes[vp->vp_integer];
				break;
			case PW_CALLING_STATION_ID:
				if(inst->callerid_ok)
					strlcpy(ut.caller_id,
						(char *)vp->vp_strvalue,
						sizeof(ut.caller_id));
				break;
		}
	}

	/*
	 *	If we didn't find out the NAS address, use the
	 *	originator's IP address.
	 */
	if (ut.nas_address == htonl(INADDR_NONE)) {
		ut.nas_address = request->packet->src_ipaddr.ipaddr.ip4addr.s_addr;
		nas = request->client->shortname;

	} else if (request->packet->src_ipaddr.ipaddr.ip4addr.s_addr == ut.nas_address) {		/* might be a client, might not be. */
		nas = request->client->shortname;

	} else {
		/*
		 *	The NAS isn't a client, it's behind
		 *	a proxy server.  In that case, just
		 *	get the IP address.
		 */
		nas = ip_ntoa(ip_name, ut.nas_address);
	}

	/*
	 *	Set the protocol field.
	 */
	if (protocol == PW_PPP)
		ut.proto = 'P';
	else if (protocol == PW_SLIP)
		ut.proto = 'S';
	else
		ut.proto = 'T';
	ut.time = t - ut.delay;

	/*
	 *	Get the utmp filename, via xlat.
	 */
	radius_xlat(filename, sizeof(filename), inst->filename, request, NULL, NULL);

	/*
	 *	See if this was a reboot.
	 *
	 *	Hmm... we may not want to zap all of the users when
	 *	the NAS comes up, because of issues with receiving
	 *	UDP packets out of order.
	 */
	if (status == PW_STATUS_ACCOUNTING_ON &&
	    (ut.nas_address != htonl(INADDR_NONE))) {
		radlog(L_INFO, "rlm_radutmp: NAS %s restarted (Accounting-On packet seen)",
		       nas);
		radutmp_zap(request, filename, ut.nas_address, ut.time);
		return RLM_MODULE_OK;
	}

	if (status == PW_STATUS_ACCOUNTING_OFF &&
	    (ut.nas_address != htonl(INADDR_NONE))) {
		radlog(L_INFO, "rlm_radutmp: NAS %s rebooted (Accounting-Off packet seen)",
		       nas);
		radutmp_zap(request, filename, ut.nas_address, ut.time);
		return RLM_MODULE_OK;
	}

	/*
	 *	If we don't know this type of entry pretend we succeeded.
	 */
	if (status != PW_STATUS_START &&
	    status != PW_STATUS_STOP &&
	    status != PW_STATUS_ALIVE) {
		RDEBUGE("NAS %s port %u unknown packet type %d)",
		       nas, ut.nas_port, status);
		return RLM_MODULE_NOOP;
	}

	/*
	 *	Translate the User-Name attribute, or whatever else
	 *	they told us to use.
	 */
	*buffer = '\0';
	radius_xlat(buffer, sizeof(buffer), inst->username, request, NULL, NULL);

	/*
	 *  Copy the previous translated user name.
	 */
	strlcpy(ut.login, buffer, RUT_NAMESIZE);

	/*
	 *	Perhaps we don't want to store this record into
	 *	radutmp. We skip records:
	 *
	 *	- without a NAS-Port (telnet / tcp access)
	 *	- with the username "!root" (console admin login)
	 */
	if (!port_seen) {
		DEBUG2("  rlm_radutmp: No NAS-Port seen.  Cannot do anything.");
		DEBUG2W("checkrad will probably not work!");
		return RLM_MODULE_NOOP;
	}

	if (strncmp(ut.login, "!root", RUT_NAMESIZE) == 0) {
		DEBUG2("  rlm_radutmp: Not recording administrative user");

		return RLM_MODULE_NOOP;
	}

	/*
	 *	Enter into the radutmp file.
	 */
	fd = open(filename, O_RDWR|O_CREAT, inst->permission);
	if (fd < 0) {
		RDEBUGE("Error accessing file %s: %s",
		       filename, strerror(errno));
		return RLM_MODULE_FAIL;
	}

	/*
	 *	Lock the utmp file, prefer lockf() over flock().
	 */
	rad_lockfd(fd, LOCK_LEN);

	/*
	 *	Find the entry for this NAS / portno combination.
	 */
	if ((cache = nas_port_find(inst->nas_port_list, ut.nas_address,
				   ut.nas_port)) != NULL) {
		lseek(fd, (off_t)cache->offset, SEEK_SET);
	}

	r = 0;
	off = 0;
	while (read(fd, &u, sizeof(u)) == sizeof(u)) {
		off += sizeof(u);
		if (u.nas_address != ut.nas_address ||
		    u.nas_port	  != ut.nas_port)
			continue;

		/*
		 *	Don't compare stop records to unused entries.
		 */
		if (status == PW_STATUS_STOP &&
		    u.type == P_IDLE) {
			continue;
		}

		if (status == PW_STATUS_STOP &&
		    strncmp(ut.session_id, u.session_id,
			    sizeof(u.session_id)) != 0) {
			/*
			 *	Don't complain if this is not a
			 *	login record (some clients can
			 *	send _only_ logout records).
			 */
			if (u.type == P_LOGIN)
				RDEBUGW("Logout entry for NAS %s port %u has wrong ID",
				       nas, u.nas_port);
			r = -1;
			break;
		}

		if (status == PW_STATUS_START &&
		    strncmp(ut.session_id, u.session_id,
			    sizeof(u.session_id)) == 0  &&
		    u.time >= ut.time) {
			if (u.type == P_LOGIN) {
				radlog(L_INFO, "rlm_radutmp: Login entry for NAS %s port %u duplicate",
				       nas, u.nas_port);
				r = -1;
				break;
			}
			RDEBUGW("Login entry for NAS %s port %u wrong order",
			       nas, u.nas_port);
			r = -1;
			break;
		}

		/*
		 *	FIXME: the ALIVE record could need
		 *	some more checking, but anyway I'd
		 *	rather rewrite this mess -- miquels.
		 */
		if (status == PW_STATUS_ALIVE &&
		    strncmp(ut.session_id, u.session_id,
			    sizeof(u.session_id)) == 0  &&
		    u.type == P_LOGIN) {
			/*
			 *	Keep the original login time.
			 */
			ut.time = u.time;
		}

		if (lseek(fd, -(off_t)sizeof(u), SEEK_CUR) < 0) {
			RDEBUGW("negative lseek!");
			lseek(fd, (off_t)0, SEEK_SET);
			off = 0;
		} else
			off -= sizeof(u);
		r = 1;
		break;
	} /* read the file until we find a match */

	/*
	 *	Found the entry, do start/update it with
	 *	the information from the packet.
	 */
	if (r >= 0 &&  (status == PW_STATUS_START ||
			status == PW_STATUS_ALIVE)) {
		/*
		 *	Remember where the entry was, because it's
		 *	easier than searching through the entire file.
		 */
		if (!cache) {
			cache = talloc_zero(inst, NAS_PORT);
			if (cache) {
				cache->nasaddr = ut.nas_address;
				cache->port = ut.nas_port;
				cache->offset = off;
				cache->next = inst->nas_port_list;
				inst->nas_port_list = cache;
			}
		}

		ut.type = P_LOGIN;
		write(fd, &ut, sizeof(u));
	}

	/*
	 *	The user has logged off, delete the entry by
	 *	re-writing it in place.
	 */
	if (status == PW_STATUS_STOP) {
		if (r > 0) {
			u.type = P_IDLE;
			u.time = ut.time;
			u.delay = ut.delay;
			write(fd, &u, sizeof(u));
		} else if (r == 0) {
			RDEBUGW("Logout for NAS %s port %u, but no Login record",
			       nas, ut.nas_port);
		}
	}
	close(fd);	/* and implicitely release the locks */

	return RLM_MODULE_OK;
}
コード例 #6
0
/*
 *	Verify the response entered by the user.
 */
static rlm_rcode_t mod_authenticate(void *instance, REQUEST *request)
{
	rlm_otp_t *inst = instance;

	const char *username;
	int rc;
	otp_pwe_t pwe;
	VALUE_PAIR *vp;
	
	char challenge[OTP_MAX_CHALLENGE_LEN];	/* cf. authorize() */
	char passcode[OTP_MAX_PASSCODE_LEN + 1];

	challenge[0] = '\0';	/* initialize for otp_pw_valid() */

	/* User-Name attribute required. */
	if (!request->username) {
		RDEBUGW("Attribute \"User-Name\" required "
			"for authentication.");
	  	
		return RLM_MODULE_INVALID;
	}
	
	username = request->username->vp_strvalue;
	
	pwe = otp_pwe_present(request);
	if (pwe == 0) {
		RDEBUGW("Attribute \"User-Password\" "
			"or equivalent required for authentication.");
		
		return RLM_MODULE_INVALID;
	}

	/*
	 *	Retrieve the challenge (from State attribute).
	 */
	vp = pairfind(request->packet->vps, PW_STATE, 0, TAG_ANY);
	if (vp) {
		char	gen_state[OTP_MAX_RADSTATE_LEN]; //!< State as hexits
		uint8_t	bin_state[OTP_MAX_RADSTATE_LEN];
	
		int32_t	then;		//!< State timestamp.
		size_t	elen;		//!< Expected State length.
		size_t	len;

		/*
		 *	Set expected State length (see otp_gen_state())
		 */
		elen = (inst->challenge_len * 2) + 8 + 8 + 32;

		if (vp->length != elen) {
			RDEBUGE("Bad radstate for [%s]: length", username);
			return RLM_MODULE_INVALID;
		}

		/*
		 *	Verify the state.
		 */

		/*
		 *	Convert vp state (ASCII encoded hexits in opaque bin
		 *	string) back to binary.
		 *
		 *	There are notes in otp_radstate as to why the state
		 *	value is encoded as hexits.
		 */
		len = fr_hex2bin(vp->vp_strvalue, bin_state, vp->length);
		if (len != (vp->length / 2)) {
			RDEBUGE("bad radstate for [%s]: not hex", username);
		
			return RLM_MODULE_INVALID;
		}

		/*
		 *	Extract data from State
		 */
		memcpy(challenge, bin_state, inst->challenge_len);

		/*
		 *	Skip flag data
		 */
		memcpy(&then, bin_state + inst->challenge_len + 4, 4);

		/*
		 *	Generate new state from returned input data
		 */
		otp_gen_state(gen_state, challenge, inst->challenge_len, 0,
			      then, inst->hmac_key);

		/*
		 *	Compare generated state (in hex form)
		 *	against generated state (in hex form)
		 *	to verify hmac.
		 */
		if (memcmp(gen_state, vp->vp_octets, vp->length)) {
			RDEBUGE("bad radstate for [%s]: hmac", username);
	
			return RLM_MODULE_REJECT;
		}

		/*
		 *	State is valid, but check expiry.
		 */
		then = ntohl(then);
		if (time(NULL) - then > inst->challenge_delay) {
			RDEBUGE("bad radstate for [%s]: expired",username);

			return RLM_MODULE_REJECT;
		}
	}
	
	/* do it */
	rc = otp_pw_valid(request, pwe, challenge, inst, passcode);

	/* Add MPPE data as needed. */
	if (rc == RLM_MODULE_OK) {
		otp_mppe(request, pwe, inst, passcode);
	}

	return rc;
}
コード例 #7
0
/*
 *	Generate a challenge to be presented to the user.
 */
static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
{
	rlm_otp_t *inst = (rlm_otp_t *) instance;

	char challenge[OTP_MAX_CHALLENGE_LEN + 1];	/* +1 for '\0' terminator */
	int auth_type_found;

	/* Early exit if Auth-Type != inst->name */
	{
		VALUE_PAIR *vp;

		auth_type_found = 0;
		vp = pairfind(request->config_items, PW_AUTHTYPE, 0, TAG_ANY);
		if (vp) {
      			auth_type_found = 1;
      			if (strcmp(vp->vp_strvalue, inst->name)) {
				return RLM_MODULE_NOOP;
    			}	
  		}
  	}

	/* The State attribute will be present if this is a response. */
	if (pairfind(request->packet->vps, PW_STATE, 0, TAG_ANY) != NULL) {
		DEBUG("rlm_otp: autz: Found response to Access-Challenge");
		
		return RLM_MODULE_OK;
	}

	/* User-Name attribute required. */
	if (!request->username) {
		RDEBUGW("Attribute \"User-Name\" "
		       "required for authentication.");
		
		return RLM_MODULE_INVALID;
	}

	if (otp_pwe_present(request) == 0) {
		RDEBUGW("Attribute "
			"\"User-Password\" or equivalent required "
			"for authentication.");
		
		return RLM_MODULE_INVALID;
	}

	/*
	 * 	We used to check for special "challenge" and "resync" passcodes
	 * 	here, but these are complicated to explain and application is
	 * 	limited.  More importantly, since we've removed all actual OTP
	 * 	code (now we ask otpd), it's awkward for us to support them.
	 * 	Should the need arise to reinstate these options, the most
	 *	likely choice is to duplicate some otpd code here.
	 */
	if (inst->allow_sync && !inst->allow_async) {
		/* This is the token sync response. */
		if (!auth_type_found) {
			pairmake_config("Auth-Type", inst->name, T_OP_EQ);
		}

		return RLM_MODULE_OK;
	}

	/*
	 *	Generate a random challenge.
	 */
	otp_async_challenge(challenge, inst->challenge_len);

	/*
	 *	Create the State attribute, which will be returned to
	 *	us along with the response.
	 *
	 *	We will need this to verify the response.
	 *
	 *	It must be hmac protected to prevent insertion of arbitrary
	 *	State by an inside attacker.
	 *
	 *	If we won't actually use the State (server config doesn't
	 *	allow async), we just use a trivial State.
	 *
	 *	We always create at least a trivial State, so mod_authorize()
	 *	can quickly pass on to mod_authenticate().
	 */
	{
		int32_t now = htonl(time(NULL)); //!< Low-order 32 bits on LP64.

		char gen_state[OTP_MAX_RADSTATE_LEN];
		size_t len;
		VALUE_PAIR *vp;

		len = otp_gen_state(gen_state, challenge, inst->challenge_len,
				    0, now, inst->hmac_key);
		
		vp = paircreate(request->reply, PW_STATE, 0);
		if (!vp) {
			return RLM_MODULE_FAIL;
		}

		memcpy(vp->vp_octets, gen_state, len);	
		vp->length = len;
		
		pairadd(&request->reply->vps, vp);
	}

	/*
	 *	Add the challenge to the reply.
	 */
	{
		VALUE_PAIR *vp;
		char buff[1024];
		size_t len;

		/*
		 *	First add the internal OTP challenge attribute to
		 *	the reply list.
		 */
		vp = paircreate(request->reply, PW_OTP_CHALLENGE, 0);
		if (!vp) {
			return RLM_MODULE_FAIL;
		}
		
		memcpy(vp->vp_strvalue, challenge, inst->challenge_len);	
		vp->length = inst->challenge_len;
		vp->op = T_OP_SET;
	
		pairadd(&request->reply->vps, vp);
		
		/*
		 *	Then add the message to the user to they known
		 *	what the challenge value is.
		 */
		
		len = radius_xlat(buff, sizeof(buff), inst->chal_prompt,
				  request, NULL, NULL);
		if (len == 0) {
			return RLM_MODULE_FAIL;
		}
		
		vp = paircreate(request->reply, PW_REPLY_MESSAGE, 0);
		if (!vp) {
			return RLM_MODULE_FAIL;
		}
		
		memcpy(vp->vp_strvalue, challenge, len);	
		vp->length = inst->challenge_len;
		vp->op = T_OP_SET;
		
		pairadd(&request->reply->vps, vp);
	}

	/*
	 *	Mark the packet as an Access-Challenge packet.
	 * 	The server will take care of sending it to the user.
	 */
	request->reply->code = PW_ACCESS_CHALLENGE;
	
	DEBUG("rlm_otp: Sending Access-Challenge.");

	if (!auth_type_found) {
		pairmake_config("Auth-Type", inst->name, T_OP_EQ);
	}
	
	return RLM_MODULE_HANDLED;
}
コード例 #8
0
/*
 *	Authenticate the user with the given password.
 */
static rlm_rcode_t mod_authenticate(void *instance, REQUEST *request)
{
	rlm_yubikey_t *inst = instance;
	
	char *passcode;
	size_t i, len;
	uint32_t counter;

	const DICT_ATTR *da;	
	VALUE_PAIR *key, *vp;
	yubikey_token_st token;
	
	char private_id[(YUBIKEY_UID_SIZE * 2) + 1];
	
	/*
	 *	Can't do yubikey auth if there's no password.
	 */
	if (!request->password || (request->password->da->attr != PW_USER_PASSWORD)) {
		RDEBUGE("No Clear-Text password in the request. Can't do Yubikey authentication.");
		return RLM_MODULE_FAIL;
	}
	
	passcode = request->password->vp_strvalue;
	len = request->password->length;
	/*
	 *	Verify the passcode is the correct length (in its raw
	 *	modhex encoded form).
	 *
	 *	<public_id (6-16 bytes)> + <aes-block (32 bytes)>
	 */
	if (len != (inst->id_len + 32)) {
		RDEBUGE("User-Password value is not the correct length, expected %u, got %zu", inst->id_len + 32, len);
		return RLM_MODULE_FAIL;	
	}

	for (i = inst->id_len; i < len; i++) {
		if (!is_modhex(*passcode)) {
			RDEBUG2("User-Password (aes-block) value contains non modhex chars");
			return RLM_MODULE_FAIL;	
		}
	}
	
	da = dict_attrbyname("Yubikey-Key");
	key = pairfind(request->config_items, da->attr, da->vendor, TAG_ANY);
	if (!key) {
		RDEBUGE("Yubikey-Key attribute not found in control list, can't decrypt OTP data");
		return RLM_MODULE_FAIL;
	}

	if (key->length != YUBIKEY_KEY_SIZE) {
		RDEBUGE("Yubikey-Key length incorrect, expected %u got %zu", YUBIKEY_KEY_SIZE, key->length);
		return RLM_MODULE_FAIL;	
	}
	
	yubikey_parse(request->password->vp_octets + inst->id_len,
		      key->vp_octets, &token);

	/*
	 *	Apparently this just uses byte offsets...
	 */
	if (!yubikey_crc_ok_p((uint8_t *) &token)) {
		RDEBUGE("Decrypting OTP token data failed, rejecting");	
		return RLM_MODULE_REJECT;
	}
	
	RDEBUG("Token data decrypted successfully");
	
	if (request->options && request->radlog) {
		(void) fr_bin2hex((uint8_t*) &token.uid,
				  (char *) &private_id, YUBIKEY_UID_SIZE);
		RDEBUG2("Private ID	: 0x%s", private_id);
		RDEBUG2("Session counter   : %u", yubikey_counter(token.ctr));
		RDEBUG2("# used in session : %u", token.use);
		RDEBUG2("Token timetamp    : %u",
			(token.tstph << 16) | token.tstpl);
		RDEBUG2("Random data       : %u", token.rnd);
		RDEBUG2("CRC data          : 0x%x", token.crc);
	}

	/*
	 *	Private ID used for validation purposes
	 */
	vp = pairmake(request, &request->packet->vps, "Yubikey-Private-ID", NULL, T_OP_SET);	
	memcpy(vp->vp_octets, token.uid, YUBIKEY_UID_SIZE);
	vp->length = YUBIKEY_UID_SIZE;
	
	/*
	 *	Token timestamp
	 */
	vp = pairmake(request, &request->packet->vps, "Yubikey-Timestamp", NULL, T_OP_SET);
	vp->vp_integer = (token.tstph << 16) | token.tstpl;
	vp->length = 4;
	
	/*
	 *	Token random
	 */
	vp = pairmake(request, &request->packet->vps, "Yubikey-Random", NULL, T_OP_SET);
	vp->vp_integer = token.rnd;
	vp->length = 4;
	
	/*
	 *	Combine the two counter fields together so we can do
	 *	replay attack checks.
	 */
	counter = (yubikey_counter(token.ctr) << 16) | token.use;
	
	vp = pairmake(request, &request->packet->vps, "Yubikey-Counter", NULL, T_OP_SET);
	vp->vp_integer = counter;
	vp->length = 4;
	
	/*
	 *	Now we check for replay attacks
	 */
	vp = pairfind(request->config_items, vp->da->attr, vp->da->vendor, TAG_ANY);
	if (!vp) {
		RDEBUGW("Yubikey-Counter not found in control list, skipping replay attack checks");
		return RLM_MODULE_OK;
	}

	if (counter <= vp->vp_integer) {
		RDEBUGE("Replay attack detected! Counter value %u, is lt or eq to last known counter value %u",
			counter, vp->vp_integer);
		return RLM_MODULE_REJECT;	
	}

	return RLM_MODULE_OK;
}
コード例 #9
0
ファイル: rlm_wimax.c プロジェクト: Gejove/freeradius-server
/*
 *	Generate the keys after the user has been authenticated.
 */
static rlm_rcode_t wimax_postauth(void *instance, REQUEST *request)
{
	rlm_wimax_t *inst = instance;
	VALUE_PAIR *msk, *emsk, *vp;
	VALUE_PAIR *mn_nai, *ip, *fa_rk;
	HMAC_CTX hmac;
	unsigned int rk1_len, rk2_len, rk_len;
	uint32_t mip_spi;
	uint8_t usage_data[24];
	uint8_t mip_rk_1[EVP_MAX_MD_SIZE], mip_rk_2[EVP_MAX_MD_SIZE];
	uint8_t mip_rk[2 * EVP_MAX_MD_SIZE];

	msk = pairfind(request->reply->vps, 1129, 0, TAG_ANY);
	emsk = pairfind(request->reply->vps, 1130, 0, TAG_ANY);
	if (!msk || !emsk) {
		RDEBUG("No EAP-MSK or EAP-EMSK.  Cannot create WiMAX keys.");
		return RLM_MODULE_NOOP;
	}

	/*
	 *	If we delete the MS-MPPE-*-Key attributes, then add in
	 *	the WiMAX-MSK so that the client has a key available.
	 */
	if (inst->delete_mppe_keys) {
		pairdelete(&request->reply->vps, 16, VENDORPEC_MICROSOFT, TAG_ANY);
		pairdelete(&request->reply->vps, 17, VENDORPEC_MICROSOFT, TAG_ANY);

		vp = radius_pairmake(request, &request->reply->vps, "WiMAX-MSK", "0x00", T_OP_EQ);
		if (vp) {
			memcpy(vp->vp_octets, msk->vp_octets, msk->length);
			vp->length = msk->length;
		}
	}

	/*
	 *	Initialize usage data.
	 */
	memcpy(usage_data, "*****@*****.**", 21);	/* with trailing \0 */
	usage_data[21] = 0x02;
	usage_data[22] = 0x00;
	usage_data[23] = 0x01;

	/*
	 *	MIP-RK-1 = HMAC-SSHA256(EMSK, usage-data | 0x01)
	 */
	HMAC_CTX_init(&hmac);
	HMAC_Init_ex(&hmac, emsk->vp_octets, emsk->length, EVP_sha256(), NULL);
	
	HMAC_Update(&hmac, &usage_data[0], sizeof(usage_data));
	HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len);

	/*
	 *	MIP-RK-2 = HMAC-SSHA256(EMSK, MIP-RK-1 | usage-data | 0x01)
	 */
	HMAC_Init_ex(&hmac, emsk->vp_octets, emsk->length, EVP_sha256(), NULL);
	
	HMAC_Update(&hmac, (const uint8_t *) &mip_rk_1, rk1_len);
	HMAC_Update(&hmac, &usage_data[0], sizeof(usage_data));
	HMAC_Final(&hmac, &mip_rk_2[0], &rk2_len);

	memcpy(mip_rk, mip_rk_1, rk1_len);
	memcpy(mip_rk + rk1_len, mip_rk_2, rk2_len);
	rk_len = rk1_len + rk2_len;

	/*
	 *	MIP-SPI = HMAC-SSHA256(MIP-RK, "SPI CMIP PMIP");
	 */
	HMAC_Init_ex(&hmac, mip_rk, rk_len, EVP_sha256(), NULL);
	
	HMAC_Update(&hmac, (const uint8_t *) "SPI CMIP PMIP", 12);
	HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len);

	/*
	 *	Take the 4 most significant octets.
	 *	If less than 256, add 256.
	 */
	mip_spi = ((mip_rk_1[0] << 24) | (mip_rk_1[1] << 16) |
		   (mip_rk_1[2] << 8) | mip_rk_1[3]);
	if (mip_spi < 256) mip_spi += 256;

	if (debug_flag) {
		int len = rk_len;
		char buffer[512];

		if (len > 128) len = 128; /* buffer size */

		fr_bin2hex(mip_rk, buffer, len);
		radlog_request(L_DBG, 0, request, "MIP-RK = 0x%s", buffer);
		radlog_request(L_DBG, 0, request, "MIP-SPI = %08x",
			       ntohl(mip_spi));
	}

	/*
	 *	FIXME: Perform SPI collision prevention
	 */

	/*
	 *	Calculate mobility keys
	 */
	mn_nai = pairfind(request->packet->vps, 1900, 0, TAG_ANY);
	if (!mn_nai) mn_nai = pairfind(request->reply->vps, 1900, 0, TAG_ANY);
	if (!mn_nai) {
		RDEBUGW("WiMAX-MN-NAI was not found in the request or in the reply.");
		RDEBUGW("We cannot calculate MN-HA keys.");
	}

	/*
	 *	WiMAX-IP-Technology
	 */
	vp = NULL;
	if (mn_nai) vp = pairfind(request->reply->vps, 23, VENDORPEC_WIMAX, TAG_ANY);
	if (!vp) {
		RDEBUGW("WiMAX-IP-Technology not found in reply.");
		RDEBUGW("Not calculating MN-HA keys");
	}

	if (vp) switch (vp->vp_integer) {
	case 2:			/* PMIP4 */
		/*
		 *	Look for WiMAX-hHA-IP-MIP4
		 */
		ip = pairfind(request->reply->vps, 6, VENDORPEC_WIMAX, TAG_ANY);
		if (!ip) {
			RDEBUGW("WiMAX-hHA-IP-MIP4 not found.  Cannot calculate MN-HA-PMIP4 key");
			break;
		}

		/*
		 *	MN-HA-PMIP4 =
		 *	   H(MIP-RK, "PMIP4 MN HA" | HA-IPv4 | MN-NAI);
		 */
		HMAC_Init_ex(&hmac, mip_rk, rk_len, EVP_sha1(), NULL);

		HMAC_Update(&hmac, (const uint8_t *) "PMIP4 MN HA", 11);
		HMAC_Update(&hmac, (const uint8_t *) &ip->vp_ipaddr, 4);
		HMAC_Update(&hmac, (const uint8_t *) &mn_nai->vp_strvalue, mn_nai->length);
		HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len);

		/*
		 *	Put MN-HA-PMIP4 into WiMAX-MN-hHA-MIP4-Key
		 */
		vp = pairfind(request->reply->vps, 10, VENDORPEC_WIMAX, TAG_ANY);
		if (!vp) {
			vp = radius_paircreate(request, &request->reply->vps,
					       10, VENDORPEC_WIMAX);
		}
		if (!vp) {
			RDEBUGW("Failed creating WiMAX-MN-hHA-MIP4-Key");
			break;
		}
		memcpy(vp->vp_octets, &mip_rk_1[0], rk1_len);
		vp->length = rk1_len;

		/*
		 *	Put MN-HA-PMIP4-SPI into WiMAX-MN-hHA-MIP4-SPI
		 */
		vp = pairfind(request->reply->vps, 11, VENDORPEC_WIMAX, TAG_ANY);
		if (!vp) {
			vp = radius_paircreate(request, &request->reply->vps,
					       11, VENDORPEC_WIMAX);
		}
		if (!vp) {
			RDEBUGW("Failed creating WiMAX-MN-hHA-MIP4-SPI");
			break;
		}
		vp->vp_integer = mip_spi + 1;
		break;

	case 3:			/* CMIP4 */
		/*
		 *	Look for WiMAX-hHA-IP-MIP4
		 */
		ip = pairfind(request->reply->vps, 6, VENDORPEC_WIMAX, TAG_ANY);
		if (!ip) {
			RDEBUGW("WiMAX-hHA-IP-MIP4 not found.  Cannot calculate MN-HA-CMIP4 key");
			break;
		}

		/*
		 *	MN-HA-CMIP4 =
		 *	   H(MIP-RK, "CMIP4 MN HA" | HA-IPv4 | MN-NAI);
		 */
		HMAC_Init_ex(&hmac, mip_rk, rk_len, EVP_sha1(), NULL);

		HMAC_Update(&hmac, (const uint8_t *) "CMIP4 MN HA", 11);
		HMAC_Update(&hmac, (const uint8_t *) &ip->vp_ipaddr, 4);
		HMAC_Update(&hmac, (const uint8_t *) &mn_nai->vp_strvalue, mn_nai->length);
		HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len);

		/*
		 *	Put MN-HA-CMIP4 into WiMAX-MN-hHA-MIP4-Key
		 */
		vp = pairfind(request->reply->vps, 10, VENDORPEC_WIMAX, TAG_ANY);
		if (!vp) {
			vp = radius_paircreate(request, &request->reply->vps,
					       10, VENDORPEC_WIMAX);
		}
		if (!vp) {
			RDEBUGW("Failed creating WiMAX-MN-hHA-MIP4-Key");
			break;
		}
		memcpy(vp->vp_octets, &mip_rk_1[0], rk1_len);
		vp->length = rk1_len;

		/*
		 *	Put MN-HA-CMIP4-SPI into WiMAX-MN-hHA-MIP4-SPI
		 */
		vp = pairfind(request->reply->vps, 11, VENDORPEC_WIMAX, TAG_ANY);
		if (!vp) {
			vp = radius_paircreate(request, &request->reply->vps,
					       11, VENDORPEC_WIMAX);
		}
		if (!vp) {
			RDEBUGW("Failed creating WiMAX-MN-hHA-MIP4-SPI");
			break;
		}
		vp->vp_integer = mip_spi;
		break;

	case 4:			/* CMIP6 */
		/*
		 *	Look for WiMAX-hHA-IP-MIP6
		 */
		ip = pairfind(request->reply->vps, 7, VENDORPEC_WIMAX, TAG_ANY);
		if (!ip) {
			RDEBUGW("WiMAX-hHA-IP-MIP6 not found.  Cannot calculate MN-HA-CMIP6 key");
			break;
		}

		/*
		 *	MN-HA-CMIP6 =
		 *	   H(MIP-RK, "CMIP6 MN HA" | HA-IPv6 | MN-NAI);
		 */
		HMAC_Init_ex(&hmac, mip_rk, rk_len, EVP_sha1(), NULL);

		HMAC_Update(&hmac, (const uint8_t *) "CMIP6 MN HA", 11);
		HMAC_Update(&hmac, (const uint8_t *) &ip->vp_ipv6addr, 16);
		HMAC_Update(&hmac, (const uint8_t *) &mn_nai->vp_strvalue, mn_nai->length);
		HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len);

		/*
		 *	Put MN-HA-CMIP6 into WiMAX-MN-hHA-MIP6-Key
		 */
		vp = pairfind(request->reply->vps, 12, VENDORPEC_WIMAX, TAG_ANY);
		if (!vp) {
			vp = radius_paircreate(request, &request->reply->vps,
					       12, VENDORPEC_WIMAX);
		}
		if (!vp) {
			RDEBUGW("Failed creating WiMAX-MN-hHA-MIP6-Key");
			break;
		}
		memcpy(vp->vp_octets, &mip_rk_1[0], rk1_len);
		vp->length = rk1_len;

		/*
		 *	Put MN-HA-CMIP6-SPI into WiMAX-MN-hHA-MIP6-SPI
		 */
		vp = pairfind(request->reply->vps, 13, VENDORPEC_WIMAX, TAG_ANY);
		if (!vp) {
			vp = radius_paircreate(request, &request->reply->vps,
					       13, VENDORPEC_WIMAX);
		}
		if (!vp) {
			RDEBUGW("Failed creating WiMAX-MN-hHA-MIP6-SPI");
			break;
		}
		vp->vp_integer = mip_spi + 2;
		break;

	default:
		break;		/* do nothing */
	}

	/*
	 *	Generate FA-RK, if requested.
	 *
	 *	FA-RK= H(MIP-RK, "FA-RK")
	 */
	fa_rk = pairfind(request->reply->vps, 14, VENDORPEC_WIMAX, TAG_ANY);
	if (fa_rk && (fa_rk->length <= 1)) {
		HMAC_Init_ex(&hmac, mip_rk, rk_len, EVP_sha1(), NULL);
		
		HMAC_Update(&hmac, (const uint8_t *) "FA-RK", 5);

		HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len);

		memcpy(fa_rk->vp_octets, &mip_rk_1[0], rk1_len);
		fa_rk->length = rk1_len;
	}

	/*
	 *	Create FA-RK-SPI, which is really SPI-CMIP4, which is
	 *	really MIP-SPI.  Clear?  Of course.  This is WiMAX.
	 */
	if (fa_rk) {
		vp = pairfind(request->reply->vps, 61, VENDORPEC_WIMAX, TAG_ANY);
		if (!vp) {
			vp = radius_paircreate(request, &request->reply->vps,
					       61, VENDORPEC_WIMAX);
		}
		if (!vp) {
			RDEBUGW("Failed creating WiMAX-FA-RK-SPI");
		} else {
			vp->vp_integer = mip_spi;
		}
	}

	/*
	 *	Give additional information about requests && responses
	 *
	 *	WiMAX-RRQ-MN-HA-SPI
	 */
	vp = pairfind(request->packet->vps, 20, VENDORPEC_WIMAX, TAG_ANY);
	if (vp) {
		RDEBUG("Client requested MN-HA key: Should use SPI to look up key from storage.");
		if (!mn_nai) {
			RDEBUGW("MN-NAI was not found!");
		}

		/*
		 *	WiMAX-RRQ-HA-IP
		 */
		if (!pairfind(request->packet->vps, 18, VENDORPEC_WIMAX, TAG_ANY)) {
			RDEBUGW("HA-IP was not found!");
		}


		/*
		 *	WiMAX-HA-RK-Key-Requested
		 */
		vp = pairfind(request->packet->vps, 58, VENDORPEC_WIMAX, TAG_ANY);
		if (vp && (vp->vp_integer == 1)) {
			RDEBUG("Client requested HA-RK: Should use IP to look it up from storage.");
		}
	}

	/*
	 *	Wipe the context of all sensitive information.
	 */
	HMAC_CTX_cleanup(&hmac);

	return RLM_MODULE_UPDATED;
}