Beispiel #1
0
/*
 * Generate a random value
 * challenge = MD5(random)
 */
int eapmd5_challenge(unsigned char *value, int len)
{
	int i;

	/*
	 *	Get real pseudo-random numbers.
	 */
	for (i = 0; i < len; i++) {
		value[i] = lrad_rand();
	}
	radlog(L_INFO, "rlm_eap_md5: Issuing Challenge");

	return 1;
}
Beispiel #2
0
static int call_modloadbalance(int component, modgroup *g, REQUEST *request,
			       int default_result)
{
	int count = 1;
	modcallable *p, *child = NULL;

	/*
	 *	Catch people who have issues.
	 */
	if (!g->children) {
		DEBUG2("  WARNING! Asked to process empty load-balance group.  Returning %s.", lrad_int2str(rcode_table, default_result, "??"));
		return default_result;
	}

	/*
	 *	Pick a random child.
	 */

	/* Loop over the children */
	for(p = g->children; p; p = p->next) {
		if (!child) {
			child = p;
			count = 1;
			continue;
		}

		/*
		 *	Keep track of how many load balancing servers
		 *	we've gone through.
		 */
		count++;

		/*
		 *	See the "camel book" for why this works.
		 *
		 *	If (rand(0..n) < 1), pick the current realm.
		 *	We add a scale factor of 65536, to avoid
		 *	floating point.
		 */
		if ((count * (lrad_rand() & 0xffff)) < (uint32_t) 0x10000) {
			child = p;
		}
	}
	rad_assert(child != NULL);

	/* Call the chosen child by recursing into modcall */
	return modcall(component, child, request);
}
Beispiel #3
0
/*
 *	Like realm find, but does load balancing, and we don't
 *	wake up any sleeping realms.  Someone should already have
 *	done that.
 *
 *	It also does NOT do fail-over to default if the realms are dead,
 *	as that decision has already been made.
 */
static REALM *proxy_realm_ldb(REQUEST *request, const char *realm_name,
			      int accounting)
{
	int		redone = 0;
	REALM		*cl, *lb;
	uint32_t	count;

 redo:
	lb = NULL;
	count = 0;
	for (cl = mainconfig.realms; cl; cl = cl->next) {
		/*
		 *	Wake up any sleeping realm.
		 *
		 *	Note that the 'realm find' function will only
		 *	wake up the FIRST realm which matches.  We've
		 *	got to wake up ALL of the matching realms.
		 */
		if (cl->wakeup <= request->timestamp) {
			cl->active = TRUE;
		}
		if (cl->acct_wakeup <= request->timestamp) {
			cl->acct_active = TRUE;
		}

		/*
		 *	Asked for auth/acct, and the auth/acct server
		 *	is not active.  Skip it.
		 */
		if ((!accounting && !cl->active) ||
		    (accounting && !cl->acct_active)) {
			continue;
		}

		/*
		 *	The realm name doesn't match, skip it.
		 */
		if (strcasecmp(cl->realm, realm_name) != 0) {
			continue;
		}

		/*
		 *	Fail-over, pick the first one that matches.
		 */
		if ((count == 0) && /* if size > 0, we have round-robin */
		    (cl->ldflag == 0)) {
			return cl;
		}

		/*
		 *	We're doing load-balancing.  Pick a random
		 *	number, which will be used to determine which
		 *	home server is chosen.
		 */
		if (!lb) {
			lb = cl;
			count = 1;
			continue;
		}

		/*
		 *	Keep track of how many load balancing servers
		 *	we've gone through.
		 */
		count++;

		/*
		 *	See the "camel book" for why this works.
		 *
		 *	If (rand(0..n) < 1), pick the current realm.
		 *	We add a scale factor of 65536, to avoid
		 *	floating point.
		 */
		if ((count * (lrad_rand() & 0xffff)) < (uint32_t) 0x10000) {
			lb = cl;
		}
	} /* loop over the realms */

	/*
	 *	All are dead, see if we have to wake
	 */
	if (!redone && !lb && mainconfig.wake_all_if_all_dead) {
		for (cl = mainconfig.realms; cl; cl = cl->next) {
			if(strcasecmp(cl->realm,realm_name) == 0) {
				if (!accounting && !cl->active) {
					cl->active = TRUE;
				}
				else if (accounting &&
					 !cl->acct_active) {
					cl->acct_active = TRUE;
				}
			}
		}
		redone = 1;
		goto redo;
	}

	/*
	 *	Return the load-balanced realm.
	 */
	return lb;
}
Beispiel #4
0
/*
 *	Like realm find, but does load balancing, and we don't
 *	wake up any sleeping realms.  Someone should already have
 *	done that.
 *
 *	It also does NOT do fail-over to default if the realms are dead,
 *	as that decision has already been made.
 */
static REALM *proxy_realm_ldb(REQUEST *request, const char *realm_name,
			      int accounting)
{
	REALM	*cl;
	REALM	*array[32];	/* maximum number of load balancing realms */
	int	size;

	size = 0;
	for (cl = mainconfig.realms; cl; cl = cl->next) {
		/*
		 *	Wake up any sleeping realm.
		 *
		 *	Note that the 'realm find' function will only
		 *	wake up the FIRST realm which matches.  We've
		 *	got to wake up ALL of the matching realms.
		 */
		if (cl->wakeup <= request->timestamp) {
			cl->active = TRUE;
		}
		if (cl->acct_wakeup <= request->timestamp) {
			cl->acct_active = TRUE;
		}

		/*
		 *	Asked for auth/acct, and the auth/acct server
		 *	is not active.  Skip it.
		 */
		if ((!accounting && !cl->active) ||
		    (accounting && !cl->acct_active)) {
			continue;
		}

		/*
		 *	The realm name doesn't match, skip it.
		 */
		if (strcasecmp(cl->realm, realm_name) != 0) {
			continue;
		}

		/*
		 *	Fail-over, pick the first one that matches.
		 */
		if ((size == 0) && /* if size > 0, we have round-robin */
		    (cl->ldflag == 0)) {
			return cl;
		}

		/*
		 *	Else we're doing round-robin.
		 */
		array[size] = cl;
		size++;

		/*
		 *	If too many realms are found, then limit
		 *	the number to 32.
		 */
		if (size >= 32) {
			break;
		}
	}

	/*
	 *	WTF?  We didn't find a live realm!
	 *
	 *	This means that 'rlm_realm' found a live realm for
	 *	us, and then it was marked dead before code execution
	 *	got here.  The only thing to do is to dump the request.
	 */
	if (size == 0) {
		return NULL;
	}

	/*
	 *	Pick a random realm.  It's not true round-robin,
	 *	but over a large number of requests, it statistically
	 *	distributes the requests evenly among the realms.
	 */
	return array[(int) (lrad_rand() % size)];
}
Beispiel #5
0
/*
 *	For more than 2 modules with redundancy + load balancing
 *	across all of them, layering the "redundant" and
 *	"load-balance" groups gets too complicated.  As a result, we
 *	implement a special function to do this.
 */
static int call_modredundantloadbalance(int component, modgroup *g, REQUEST *request,
					int default_result)
{
	int count = 1;
	int myresult = default_result;
	int priority = 0;	/* default result has lowest priority  */
	modcallable *p, *child = NULL;

	/*
	 *	Catch people who have issues.
	 */
	if (!g->children) {
		DEBUG2("  WARNING! Asked to process empty redundant-load-balance group.  Returning %s.", lrad_int2str(rcode_table, default_result, "??"));
		return default_result;
	}

	/*
	 *	Pick a random child.
	 */

	/* Loop over the children */
	for(p = g->children; p; p = p->next) {
		if (!child) {
			child = p;
			count = 1;
			continue;
		}

		/*
		 *	Keep track of how many load balancing servers
		 *	we've gone through.
		 */
		count++;

		/*
		 *	See the "camel book" for why this works.
		 *
		 *	If (rand(0..n) < 1), pick the current realm.
		 *	We add a scale factor of 65536, to avoid
		 *	floating point.
		 */
		if ((count * (lrad_rand() & 0xffff)) < (uint32_t) 0x10000) {
			child = p;
		}
	}
	rad_assert(child != NULL);

	/*
	 *	Call the chosen child, with fail-over to the next one
	 *	if it is down.
	 */
	p = child;
	do {
		/*
		 *	Call the chosen entry.  If we're done, then
		 *	stop.
		 */
		if (!call_one(component, p, request, &priority, &myresult)) {
			break;
		}
		
		/*
		 *	Go to the next one, and wrap around to the beginning if
		 *	we reach the end.
		 */
		p = p->next;
		if (!p) p = g->children;
	} while (p != child);

	/*
	 *	And return whatever was decided.
	 */
	return myresult;
}
Beispiel #6
0
/*
 *	Initiate the EAP-MD5 session by sending a challenge to the peer.
 */
static int md5_initiate(void *type_data, EAP_HANDLER *handler)
{
	int		i;
	MD5_PACKET	*reply;

	type_data = type_data;	/* -Wunused */

	/*
	 *	Allocate an EAP-MD5 packet.
	 */
	reply = eapmd5_alloc();
	if (reply == NULL)  {
		radlog(L_ERR, "rlm_eap_md5: out of memory");
		return 0;
	}

	/*
	 *	Fill it with data.
	 */
	reply->code = PW_MD5_CHALLENGE;
	reply->length = 1 + MD5_CHALLENGE_LEN; /* one byte of value size */
	reply->value_size = MD5_CHALLENGE_LEN;

	/*
	 *	Allocate user data.
	 */
	reply->value = malloc(reply->value_size);
	if (reply->value == NULL) {
		radlog(L_ERR, "rlm_eap_md5: out of memory");
		eapmd5_free(&reply);
		return 0;
	}

	/*
	 *	Get a random challenge.
	 */
	for (i = 0; i < reply->value_size; i++) {
		reply->value[i] = lrad_rand();
	}
	radlog(L_INFO, "rlm_eap_md5: Issuing Challenge");

	/*
	 *	Keep track of the challenge.
	 */
	handler->opaque = malloc(reply->value_size);
	rad_assert(handler->opaque != NULL);
	memcpy(handler->opaque, reply->value, reply->value_size);
	handler->free_opaque = free;

	/*
	 *	Compose the EAP-MD5 packet out of the data structure,
	 *	and free it.
	 */
	eapmd5_compose(handler->eap_ds, reply);

	/*
	 *	We don't need to authorize the user at this point.
	 *
	 *	We also don't need to keep the challenge, as it's
	 *	stored in 'handler->eap_ds', which will be given back
	 *	to us...
	 */
	handler->stage = AUTHENTICATE;

	return 1;
}
Beispiel #7
0
/*
 * we got an EAP-Request/Sim/Start message in a legal state.
 *
 * pick a supported version, put it into the reply, and insert a nonce.
 */
static int process_eap_start(RADIUS_PACKET *req,
			     RADIUS_PACKET *rep)
{
	VALUE_PAIR *vp, *newvp;
	VALUE_PAIR *anyidreq_vp, *fullauthidreq_vp, *permanentidreq_vp;
	uint16_t *versions, selectedversion;
	unsigned int i,versioncount;

	/* form new response clear of any EAP stuff */
	cleanresp(rep);

	if((vp = pairfind(req->vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_VERSION_LIST)) == NULL) {
		fprintf(stderr, "illegal start message has no VERSION_LIST\n");
		return 0;
	}

	versions = (uint16_t *)vp->strvalue;

	/* verify that the attribute length is big enough for a length field */
	if(vp->length < 4)
	{
		fprintf(stderr, "start message has illegal VERSION_LIST. Too short: %d\n", vp->length);
		return 0;
	}

	versioncount = ntohs(versions[0])/2;
	/* verify that the attribute length is big enough for the given number
	 * of versions present.
	 */
	if((unsigned)vp->length <= (versioncount*2 + 2))
	{
		fprintf(stderr, "start message is too short. Claimed %d versions does not fit in %d bytes\n", versioncount, vp->length);
		return 0;
	}

	/*
	 * record the versionlist for the MK calculation.
	 */
	eapsim_mk.versionlistlen = versioncount*2;
	memcpy(eapsim_mk.versionlist, (unsigned char *)(versions+1),
	       eapsim_mk.versionlistlen);

	/* walk the version list, and pick the one we support, which
	 * at present, is 1, EAP_SIM_VERSION.
	 */
	selectedversion=0;
	for(i=0; i < versioncount; i++)
	{
		if(ntohs(versions[i+1]) == EAP_SIM_VERSION)
		{
			selectedversion=EAP_SIM_VERSION;
			break;
		}
	}
	if(selectedversion == 0)
	{
		fprintf(stderr, "eap-sim start message. No compatible version found. We need %d\n", EAP_SIM_VERSION);
		for(i=0; i < versioncount; i++)
		{
			fprintf(stderr, "\tfound version %d\n",
				ntohs(versions[i+1]));
		}
	}

	/*
	 * now make sure that we have only FULLAUTH_ID_REQ.
	 * I think that it actually might not matter - we can answer in
	 * anyway we like, but it is illegal to have more than one
	 * present.
	 */
	anyidreq_vp = pairfind(req->vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_ANY_ID_REQ);
	fullauthidreq_vp = pairfind(req->vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_FULLAUTH_ID_REQ);
	permanentidreq_vp = pairfind(req->vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_PERMANENT_ID_REQ);

	if(fullauthidreq_vp == NULL ||
	   anyidreq_vp != NULL ||
	   permanentidreq_vp != NULL) {
		fprintf(stderr, "start message has %sanyidreq, %sfullauthid and %spermanentid. Illegal combination.\n",
			(anyidreq_vp != NULL ? "a " : "no "),
			(fullauthidreq_vp != NULL ? "a " : "no "),
			(permanentidreq_vp != NULL ? "a " : "no "));
		return 0;
	}

	/* okay, we have just any_id_req there, so fill in response */

	/* mark the subtype as being EAP-SIM/Response/Start */
	newvp = paircreate(ATTRIBUTE_EAP_SIM_SUBTYPE, PW_TYPE_INTEGER);
	newvp->lvalue = eapsim_start;
	pairreplace(&(rep->vps), newvp);

	/* insert selected version into response. */
	newvp = paircreate(ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_SELECTED_VERSION,
			   PW_TYPE_OCTETS);
	versions = (uint16_t *)newvp->strvalue;
	versions[0] = htons(selectedversion);
	newvp->length = 2;
	pairreplace(&(rep->vps), newvp);

	/* record the selected version */
	memcpy(eapsim_mk.versionselect, (unsigned char *)versions, 2);

	vp = newvp = NULL;

	{
		uint32_t nonce[4];
		/*
		 * insert a nonce_mt that we make up.
		 */
		newvp = paircreate(ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_NONCE_MT,
				   PW_TYPE_OCTETS);
		newvp->strvalue[0]=0;
		newvp->strvalue[1]=0;
		newvp->length = 18;  /* 16 bytes of nonce + padding */

		nonce[0]=lrad_rand();
		nonce[1]=lrad_rand();
		nonce[2]=lrad_rand();
		nonce[3]=lrad_rand();
		memcpy(&newvp->strvalue[2], nonce, 16);
		pairreplace(&(rep->vps), newvp);

		/* also keep a copy of the nonce! */
		memcpy(eapsim_mk.nonce_mt, nonce, 16);
	}

	{
		uint16_t *pidlen, idlen;

		/*
		 * insert the identity here.
		 */
		vp = pairfind(rep->vps, PW_USER_NAME);
		if(vp == NULL)
		{
			fprintf(stderr, "eap-sim: We need to have a User-Name attribute!\n");
			return 0;
		}
		newvp = paircreate(ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_IDENTITY,
				   PW_TYPE_OCTETS);
		idlen = strlen(vp->strvalue);
		pidlen = (uint16_t *)newvp->strvalue;
		*pidlen = htons(idlen);
		newvp->length = idlen + 2;

		memcpy(&newvp->strvalue[2], vp->strvalue, idlen);
		pairreplace(&(rep->vps), newvp);

		/* record it */
		memcpy(eapsim_mk.identity, vp->strvalue, idlen);
		eapsim_mk.identitylen = idlen;
	}

	return 1;
}
Beispiel #8
0
/*
 *	Add an entry to the proxy tree.
 *
 *	This is the ONLY function in this source file which may be called
 *	from a child thread.  It therefore needs mutexes...
 */
int rl_add_proxy(REQUEST *request)
{
	int		i, found, proxy;
	uint32_t	mask;
	proxy_id_t	myid, *entry;

	myid.dst_ipaddr = request->proxy->dst_ipaddr;
	myid.dst_port = request->proxy->dst_port;

	/*
	 *	Proxied requests get sent out the proxy FD ONLY.
	 *
	 *	FIXME: Once we allocate multiple proxy FD's, move this
	 *	code to below, so we can have more than 256 requests
	 *	outstanding.
	 */
	request->proxy_outstanding = 1;

	pthread_mutex_lock(&proxy_mutex);

	/*
	 *	Assign a proxy ID.
	 */
	entry = rbtree_finddata(proxy_id_tree, &myid);
	if (!entry) {	/* allocate it */
		entry = rad_malloc(sizeof(*entry) + sizeof(entry->id) * 255);
		
		entry->dst_ipaddr = request->proxy->dst_ipaddr;
		entry->dst_port = request->proxy->dst_port;
		entry->index = 0;

		DEBUG3(" proxy: creating %08x:%d",
		       entry->dst_ipaddr,
		       entry->dst_port);
		
		/*
		 *	Insert the new home server entry into
		 *	the tree.
		 *
		 *	FIXME: We don't (currently) delete the
		 *	entries, so this is technically a
		 *	memory leak.
		 */
		if (rbtree_insert(proxy_id_tree, entry) == 0) {
			DEBUG2("ERROR: Failed to insert entry into proxy Id tree");
			free(entry);
			return 0;
		}

		/*
		 *	Clear out bits in the array which DO have
		 *	proxy Fd's associated with them.  We do this
		 *	by getting the mask of bits which have proxy
		 *	fd's...  */
		mask = 0;
		for (i = 0; i < 32; i++) {
			if (proxy_fds[i] != -1) {
				mask |= (1 << i);
			}
		}
		rad_assert(mask != 0);

		/*
		 *	Set bits here indicate that the Fd is in use.
		 */
		entry->mask = mask;

		mask = ~mask;

		/*
		 *	Set the bits which are unused (and therefore
		 *	allocated).  The clear bits indicate that the Id
		 *	for that FD is unused.
		 */
		for (i = 0; i < 256; i++) {
			entry->id[i] = mask;
		}
	} /* else the entry already existed in the proxy Id tree */
	
 retry:
	/*
	 *	Try to find a free Id.
	 */
	found = -1;
	for (i = 0; i < 256; i++) {
		/*
		 *	Some bits are still zero..
		 */
		if (entry->id[(i + entry->index) & 0xff] != (uint32_t) ~0) {
			found = (i + entry->index) & 0xff;
			break;
		}

		/*
		 *	Hmm... do we want to re-use Id's, when we
		 *	haven't seen all of the responses?
		 */
	}
	
	/*
	 *	No free Id, try to get a new FD.
	 */
	if (found < 0) {
		/*
		 *	First, see if there were FD's recently allocated,
		 *	which we don't know about.
		 */
		mask = 0;
		for (i = 0; i < 32; i++) {
			if (proxy_fds[i] < 0) continue;

			mask |= (1 << i);
		}

		/*
		 *	There ARE more FD's than we know about.
		 *	Update the masks for Id's, and re-try.
		 */
		if (entry->mask != mask) {
			/*
			 *	New mask always has more bits than
			 *	the old one, but never fewer bits.
			 */
			rad_assert((entry->mask & mask) == entry->mask);

			/*
			 *	Clear the bits we already know about,
			 *	and then or in those bits into the
			 *	global mask.
			 */
			mask ^= entry->mask;
			entry->mask |= mask;
			mask = ~mask;
			
			/*
			 *	Clear the bits in the Id's for the new
			 *	FD's.
			 */
			for (i = 0; i < 256; i++) {
				entry->id[i] &= mask;
			}
			
			/*
			 *	And try again to allocate an Id.
			 */
			goto retry;
		} /* else no new Fd's were allocated. */

		/*
		 *	If all Fd's are allocated, die.
		 */
		if (~mask == 0) {
			radlog(L_ERR|L_CONS, "ERROR: More than 8000 proxied requests outstanding for home server %08x:%d",
			       ntohs(entry->dst_ipaddr), entry->dst_port);
			return 0;
		}
		
		/*
		 *	Allocate a new proxy Fd.  This function adds it
		 *	into the list of listeners.
		 */
		proxy = proxy_new_listener();
		if (proxy < 0) {
			DEBUG2("ERROR: Failed to create a new socket for proxying requests.");
			return 0;
		}

		/*
		 *
		 */
		found = -1;
		for (i = 0; i < 32; i++) {
			/*
			 *	Found a free entry.  Save the socket,
			 *	and remember where we saved it.
			 */
			if (proxy_fds[(proxy + i) & 0x1f] == -1) {
				proxy_fds[(proxy + i) & 0x1f] = proxy;
				found = (proxy + i) & 0x1f;
				break;
			}
		}
		rad_assert(found >= 0);	/* i.e. the mask had free bits. */

		mask = 1 << found;
		entry->mask |= mask;
		mask = ~mask;

		/*
		 *	Clear the relevant bits in the mask.
		 */
		for (i = 0; i < 256; i++) {
			entry->id[i] &= mask;
		}

		/*
		 *	Pick a random Id to start from, as we've
		 *	just guaranteed that it's free.
		 */
		found = lrad_rand() & 0xff;
	}
	
	/*
	 *	Mark next (hopefully unused) entry.
	 */
	entry->index = (found + 1) & 0xff;
	
	/*
	 *	We now have to find WHICH proxy fd to use.
	 */
	proxy = -1;
	for (i = 0; i < 32; i++) {
		/*
		 *	FIXME: pick a random socket to use?
		 */
		if ((entry->id[found] & (1 << i)) == 0) {
			proxy = i;
			break;
		}
	}

	/*
	 *	There was no bit clear, which we had just checked above...
	 */
	rad_assert(proxy != -1);

	/*
	 *	Mark the Id as allocated, for thei Fd.
	 */
	entry->id[found] |= (1 << proxy);
	request->proxy->id = found;
	request->proxy->src_ipaddr = proxy_ipaddr;

	rad_assert(proxy_fds[proxy] != -1);
	request->proxy->sockfd = proxy_fds[proxy];

	DEBUG3(" proxy: allocating %08x:%d %d",
	       entry->dst_ipaddr,
	       entry->dst_port,
	       request->proxy->id);
	
	if (!rbtree_insert(proxy_tree, request)) {
		DEBUG2("ERROR: Failed to insert entry into proxy tree");
		return 0;
	}
	
	pthread_mutex_unlock(&proxy_mutex);

	return 1;
}