+++ /dev/null
-
-User Collision in FreeRadius
-----------------------------
-
-0. INTRODUCTION
-
- A. What is it?
-
- User collision is the ability to uniquely authenticate duplicate
- usernames in radius. In addition, it provides resources to
- correctly identify each duplicate user in the accounting logs.
-
- B. Who needs it and why?
-
- Many ISPs are acquiring other ISPs in local or remote areas. This
- causes problems with centralizing services such as radius because
- of the overlap in usernames between ISPs.
-
- For example:
-
- o ISP A (with 10,000 users) acquires ISP B (with 1,000 users).
- o ISP A determines that 375 of ISP B's 1,000 usernames conflict
- with usernames already in use at ISP A (eg 'foo' is valid
- username on both ISPs)
- o ISP A, therefore, cannot merge its user files with ISP B and
- centralize radius
-
- Now, about now, many of you are thinking, "what about realms?"
- Well, realms are great, but, in general, it will require the end
- user to add "@domain.com", which is a pain. It means ISP A has to
- call 375 people and tell them to add that to their login name.
-
- And now some of you are thinking, "why couldn't you add the realm
- in the server based on some other attribute (such as NAS-IP or
- Calling-Station)?" The answer is, you could, but both of those
- solutions are a pill to maintain. For example, what if you want
- to merge the huntgroups (and NASs) of ISP A and ISP B? Then the
- NAS-IP method falls apart. And god forbid the user call from a
- different number or change their line for Calling-Station.
-
- So how to solve it? Enter user collision code in radius...
-
- C. How does it work?
-
- Currently, it only works when authenticating users via the
- following methods in FreeRadius:
-
- o 'users' file ( Auth-Types 'Local' and 'Crypt-Local' _ONLY_ )
- o cached password (and shadow) file users
- o rlm_fastusers module (see README.rlm_fastusers)
-
- That's it so far. Perhaps other module authors will include it in
- their code later, but that is for them to decide.
-
- Btw, the reason user collision cannot be implemented (efficiently)
- on a non-cached /etc/passwd file is because getpwnam() will always
- return the first user it finds with a matching username. Thus, if
- you're going to use the /etc/passwd file, you MUST set 'cache =
- yes' in your radiusd.conf. Or, you could always just put
- duplicate usernames in the 'users' files, but you should probably
- be caching anyway if you want a speedy server :)
-
-1. Authentication
-
- It currently works by using the password of the user to uniquely
- identify and auth- enticate them.
-
- Example:
-
- ISP A user 'foo' has password 'bar'
- ISP B user 'foo' has password 'bleah'
-
- If the user logs in with 'bar' as their password, their login will be
- accepted and they will get the reply attributes associated with what
- you've entered for the user 'foo' password 'bar' entry.
-
- If the user logs in with 'bleah' as the password, again, they'll be
- authenticated, but they will get the reply attributes associated with
- user 'foo' password 'bleah'.
-
- So, what happens if two users have the same password? IT
- BREAKS. Well, ok, it doesn't "break", but every user with the
- associated pass will get the same reply attributes, causing grief for
- you. THE CODE DOES NOT CHECK FOR THIS, THAT IS YOUR JOB!
-
- NOTE: Again, user collision authentication *depends* on the passwords
- being distinct!
-
- So the security minded among us are now agast. I'm not much for this
- method myself, but trust me, the corporate folks are gonna love it.
- And it's not inherently insecure as long as the passwords remain
- different.
-
-2. Accounting
-
- Ok, so now, how can we tell in the accounting logs which user 'foo'
- logged in? Well, again, you have a little work to do. The code
- identifies the user via a unique value assigned to the 'Class' reply
- attribute in the users file.
-
- For example:
-
- foo Auth-Type := Local, User-Password == "bar"
- Class = 0x0001
-
- foo Auth-Type := Local, User-Password == "bleah"
- Class = 0x0002
-
- Now, you'd add other attributes as well, but let's just start with
- 'Class' by itself as a reply. When a user 'foo' logs in with password
- 'bar', the 'Class=0x0001' reply item gets sent back to the NAS they
- dialed into. The NAS then adds 'Class=0x0001' to the Accounting
- 'Start' packet it sends for that user, thus uniquely identifying *this*
- 'foo' in the your accounting logs.
-
- If the user 'foo' logs in with password 'bleah', you will get
- 'Class=0x0002' in your accounting logs.
-
- Now, again, you should note that it is *your job* to make sure the
- 'Class' values are different for each user. If you don't, I can assure
- you the phones will ring off the hook when bitchy users get their
- bills.
-
- Obviously, this method works only for users in your 'users' files.
-
- If you are using a cached passwd file, then the "Full name" field in
- the passwd file you cached will be passed back to the NAS as the value
- of 'Class'.
-
- Example:
-
- /etc/passwd -> test:x:500:500:0x1001:/dev/null:/dev/null
-
- In this case, "0x1001" will be passed as the value for the 'Class'
- attribute when the Auth-Accept packet gets sent back to the NAS, and
- then you should see 'Class=0x1001' for that user in your accounting
- logs.
-
- Once again, it is your job to ensure that these Class values are unique
- for each user.
-
-D. *Does* it work?
-
- As of 10-01-2000, it is *extremely alpha*. If you find bugs, by all
- means let met know at jeff@apex.net, but make sure you include
- relavent sections of your 'users' file and debug output from the
- server (radiusd -X).
-
-1. USAGE
-
- Set 'usercollide=yes' in your radiusd.conf and either restart or
- kill -HUP radiusd.
-
-2. CAVEATS
-
- Currently does not work with all modules (ie, sql, ldap, etc).
-
-3. ACKNOWLEDGEMENT
-
- Jeff Carneal - Author
- Alan DeKok - for telling me about the 'Class' attribute :)
-
static PAIR_LIST *fastuser_find(REQUEST *request, PAIR_LIST *user,
const char *username);
static void fastuser_tablestats(PAIR_LIST **hashtable, int size);
-static int fastuser_passcheck(REQUEST *request, PAIR_LIST *user, const char *name);
static const CONF_PARSER module_config[] = {
{ "usersfile", PW_TYPE_STRING_PTR,
while((cur) && (!userfound)) {
if((strcmp(cur->name, username)==0) &&
paircmp(request, request->packet->vps, cur->check, &request->reply->vps) == 0) {
- /*
- * Usercollide means we have to compare check pairs
- * AND the password
- */
- if(mainconfig.do_usercollide) {
- if((userfound = fastuser_passcheck(request, cur, username))==0) {
- cur = cur->next;
- }
-
- } else {
userfound = 1;
DEBUG2(" fastusers: Matched %s at %d", cur->name, cur->lineno);
- }
} else {
cur = cur->next;
}
}
}
-static int fastuser_passcheck(REQUEST *request, PAIR_LIST *user,
- const char *name UNUSED)
-{
- int found=0;
- VALUE_PAIR *check_save;
-
- /*
- * We check for REJECT specially here or a REJECT
- * user will never match
- */
- check_save = pairfind(user->check, PW_AUTHTYPE);
- if((check_save) && check_save->lvalue == PW_AUTHTYPE_REJECT) {
- DEBUG2(" fastusers(uc): User '%s' line %d is Auth-Type Reject, but usercollide match",
- user->name, user->lineno);
- return 1;
- }
-
- /* Save the orginal config items */
- check_save = request->config_items;
- request->config_items = NULL;
-
- DEBUG2(" fastusers(uc): Checking %s at %d", user->name, user->lineno);
-
- /* Copy this users check pairs to the request */
- request->config_items = paircopy(user->check);
-
- /* Check the req to see if we matched */
- if(rad_check_password(request)==0) {
- DEBUG2(" fastusers(uc): Matched %s at %d", user->name, user->lineno);
- found = 1;
- }
-
- /* Restore check items */
- pairfree(&request->config_items);
- request->config_items = check_save;
-
- return found;
-}
-
/*
* (Re-)read the "users" file into memory.
*/
* entry to the current list of reply pairs.
*/
if ((paircmp(request, request_pairs, pl->check, reply_pairs) == 0)) {
- if ((mainconfig.do_usercollide) &&
- (strcmp(pl->name, "DEFAULT"))) {
-
- /*
- * We have to make sure the password
- * matches as well
- */
-
- /* Save the orginal config items */
- check_save = paircopy(request->config_items);
-
- /* Copy this users check pairs to the request */
- check_tmp = paircopy(pl->check);
- pairmove(check_pairs, &check_tmp);
- pairfree(&check_tmp);
-
- DEBUG2(" users: Checking entry %s at line %d", pl->name, pl->lineno);
- /* Check the req to see if we matched */
- if (rad_check_password(request)==0) {
- DEBUG2(" users: Matched entry %s at line %d", pl->name, pl->lineno);
-
- found = 1;
-
- /* Free our saved config items */
- pairfree(&check_save);
-
- /*
- * Already copied check items, so
- * just copy reply here
- */
- reply_tmp = paircopy(pl->reply);
- pairxlatmove(request, reply_pairs, &reply_tmp);
- pairfree(&reply_tmp);
-
- /* We didn't match here */
- } else {
- /* Restore check items */
- pairfree(&request->config_items);
- request->config_items = paircopy(check_save);
- check_pairs = &request->config_items;
- continue;
- }
+ DEBUG2(" users: Matched entry %s at line %d", pl->name, pl->lineno);
+ found = 1;
+ check_tmp = paircopy(pl->check);
+ reply_tmp = paircopy(pl->reply);
+ pairxlatmove(request, reply_pairs, &reply_tmp);
+ pairmove(check_pairs, &check_tmp);
+ pairfree(&reply_tmp);
+ pairfree(&check_tmp); /* should be NULL */
- /* No usercollide */
- } else {
-
- DEBUG2(" users: Matched entry %s at line %d", pl->name, pl->lineno);
- found = 1;
- check_tmp = paircopy(pl->check);
- reply_tmp = paircopy(pl->reply);
- pairxlatmove(request, reply_pairs, &reply_tmp);
- pairmove(check_pairs, &check_tmp);
- pairfree(&reply_tmp);
- pairfree(&check_tmp); /* should be NULL */
- }
/*
* Fallthrough?
*/