We now have man pages
authoraland <aland>
Thu, 25 Mar 2004 21:11:57 +0000 (21:11 +0000)
committeraland <aland>
Thu, 25 Mar 2004 21:11:57 +0000 (21:11 +0000)
doc/rlm_unix [deleted file]
man/man5/rlm_unix.5

diff --git a/doc/rlm_unix b/doc/rlm_unix
deleted file mode 100644 (file)
index efba92d..0000000
+++ /dev/null
@@ -1,551 +0,0 @@
-FreeRADIUS Password Caching for Unix modules
-
-If you are using FreeBSD or NetBSD, then you do NOT want to cache the
-passwords!  See 'radiusd.conf', the comments in the 'unix' section,
-for more details.
-
-Acknowledgements
-
-Thanks to Alan DeKok for the initial idea, to Dr. Bob Pilgrim for
-implementation strategies, and to Miquel for putting it into FreeRADIUS.
-
-What does this caching do?
-
-It will add the capability to cache several of the system files that
-are used often by FreeRADIUS. These include: /etc/passwd, /etc/shadow
-(if necessary), and /etc/group.
-
-By caching the information in these files and storing it in a VERY efficient
-hash table, authentication lookups are sped up many times over. So, the
-short answer is that your authentication requests will be faster if you use
-caching, assuming you're using System authenication now. Read on for the
-long answer.
-
-How do I use it?
-
-Compile radiusd as you normally would, and then set 'cache=yes' in your
-radiusd.conf file.
-
-Then, if you want to monitor how the caching is doing (errors and such),
-'grep HASH /var/log/radius.log'.
-
-What are the advantages?
-
-Tremendous speed increase in authentication request handling. Orders of
-magnitude, literally.
-
-What are the disadvantages?
-
-As with anything else, there are a couple of trade-offs. However, these are
-quite modest, IMHO.
-
-First, memory consumption of your radiusd process will go up somewhat over
-normal operation. Depending on the size of your files, it could take several
-megabytes of RAM to build the hash table properly. However, I have gone to
-great lengths to ensure that there are no memory leaks. The size of the
-daemon process should stay constant, or grow linearly with the size of your
-system files. If you notice otherwise, send me an email with lots of detail
-about your system and I'll see what I can find.
-
-Second, you should kill -HUP the main radiusd process every so often out of
-cron. The reason for this is because the daemon will need to rebuild the
-cache to get passwords that you have changed for existing users. You will
-not have to -HUP it for new users in /etc/passwd to get authenticated, as if
-it can't find the user in the hash, it will automatically revert to the old
-(slow) lookup methods [getpwnam()]. However, if you change the password for
-a user, and the hash table contains that user but with the old password, it
-will simply think that the user is supplying the wrong password and deny
-them access. One thing I'd like to point out is that rebuilding the hash
-with the -HUP takes less than a second with our ~1MB /etc/passwd and shadow
-files. The slowest part of the -HUP is caused by the gethostby*() functions.
-All in all, a -HUP reconfig is virtually instant on our solaris boxes, and
-takes about 15 seconds on standard linux 2.x machines.
-
-Why was it written?
-
-In examining our radius servers, I noticed that there were several things
-slowing down an authentication requests. Please note that some of these
-things will depend on what you are doing in your raddb/users file, however,
-most of the things we were doing were quite common, so I feel that others
-may have similar setups. In any case, I'll go thru exactly what we were
-seeing, and how caching fixes it.
-
-Authentication Bottlenecks:
-*Group check items calling getgrnam()
-*DEFAULT users with System authentication calling getpwnam()
-*Simultaneous use check reading file /var/log/radutmp
-
-First, the auth request comes in, and radius (usually) forks a child to
-handle it. The child will then check the raddb/users file to see if the user
-is explicitly listed there. In our case, most the users were matched off of
-a DEFAULT system entry, meaning they would be looked up in the /etc/passwd
-and /etc/shadow files. However, first, we had to check for a couple of
-groups that aren't allowed access. In our case we had two DEFAULT group
-compare check items, meaning that the getgrnam() system call was called
-twice for every authentication request. Considering that the /etc/group file
-is not so large, that wasn't a huge factor. However, the /etc/group file is
-stored on disk, and was accessed through system calls, meaning it had to be
-opened and searched until a matching group was found (twice), slowing down
-authentication requestions somewhat.
-
-Ok, so we've established that there's a getgrnam() call for each DEFAULT
-user you have with a group check item. If you don't have any such DEFAULTs,
-no problem, you don't have those calls. However, nearly 99% of you probably
-have a line like the following in your raddb/users file, leading into the
-next bottleneck: DEFAULT Auth-Type = System, Simultaneous-Use = 1
-
-What does that mean? It means, for every auth request that comes in for
-which we don't have an explicit match in our raddb/users file, lookup the
-user in /etc/passwd and/or /etc/shadow using getpwnam() and getspwnam()
-system calls. So what's so bad about that? It works right? Well, yes, but
-it's inefficient because of the way getpwnam() works.
-
-Think for a second about what getpwnam() has to do. It is a linear search
-algorithm through an unsorted list stored on disk. It is an O(N) algorithm,
-for you computer science folks (barring any caching build into the function
-by particular OSs). So, basically, what it does is open the /etc/passwd
-file, start at line one, compare that user to the user we're looking for,
-and if there's no match, go to the next line and repeat. That means that on
-average, every auth request you're taking requires opening and reading half
-of your /etc/passwd file. Worst case is when a user not in the file is
-looked up, as you search the entire file without a match. Best case is when
-the user is the first one in the file, and average is in the middle
-somewhere.
-
-Are you saying, that doesn't sound so bad? Well, then perhaps caching
-isn't for you. In our case, we had a ~15,000 line passwd file, and anywhere
-from 20-40,000 logins daily. The getpwnam() call was taking by far the
-majority of the radius daemon's time, resulting in fairly poor
-authentication performance.
-
-NOTE:  radutmp caching is disabled for now until we can find a better way 
-to do it.  Still, this next paragraph indicates why it needs doing.
-
-Lastly, if you're using the wonderful simultaneous-use check item in 
-FreeRADIUS, you have another disk lookup to perform on your /var/log/radutmp
-file (actually, caching of this file just came available in b17. glad
-somebody else noticed it :) What happens is, if the user is authenticated
-properly, the daemon then opens the radutmp file, reads until it hits a line
-matching the user currently requesting authentication, and then either
-allows or denies access based on the presense or absense of a match. Thus,
-in the majority of cases, you read the entire file in for every
-authentication request (assuming that most of your users aren't trying to
-steal service from you :) .
-
-So, we've identified the problems. If you really care about how caching
-addresses those problems, keep reading.
-
-How does it work?
-
-The code will first open your /etc/passwd file and runs through it hashing
-all the usernames into a numeric index into my hash table. It then creates
-an entry into the hashtable at that index for every user in the file.
-Currently, it caches username, password, gid, and uid in the entry. Then, if
-you have shadow passwords, it will open your /etc/shadow file and put the
-passwords into the entry corresponding with the appropriate user. It is this
-hash table that allows us to eliminate the getpwnam() bottleneck. Read below
-for how to test it.
-
-Next, it will read the /etc/group file and build a singly linked list of it.
-It's not as fast as the hash, but the group file is usually small enough
-that it wouldn't make a difference. Just brining it into RAM should be good
-enough. This takes care of the getgrnam() bottleneck.
-
-NOTE:  Again, radutmp caching is disabled.  Disregard this next paragraph.
-
-Finally, it reads the /var/log/radutmp file and marks every user listed as
-logged in with a login variable stored in their hash entry. Then, every time
-a user logs in or out, that variable is updated so that we can avoid reading
-in /var/log/radutmp.
-
-So what, right? Well, understand that for every user lookup done on a
-system, you have currently to read half of your passwd file. Thus, the
-number of comparisons done per getpwnam() is the number of lines in your
-passwd file divided by two. So do 'wc -l /etc/passwd', divide that by two,
-and that's the number of comparisons done per user lookup on your system
-right now.
-
-Using the hash table method, I have achieved user lookups in RAM in near
-constant time. That is, using my passwd files (everyone's is different,
-obviously), I could find a user in the hash in 1.0673 comparisons (on
-average). Being that 1.0 is the lowest number of comparisons possible,
-that's a pretty significant thing. Again, for the computer science people,
-the hash function and table are wide enough such that 87.4% of users are
-stored singly in a bucket, 11.7% are stored with two users in a bucket, 0.8%
-are stored with 3 users in the bucket, and 0.03% are stored with more than 3
-users per bucket. That means that 87% of your lookups are done in one
-comparison, versus the number you got when you did the 'wc -l /etc/passwd' /
-2 above. This discussion has also neglected the fact that RAM searches are
-many, many times faster than disk searches. We'll let it speak for itself.
-
-But, you don't have to take my word for it. I have a test program that I've
-written that can show you the difference on your own system between the
-speed in lookups using the hash and getpwnam().  You can find the program
-cachetest.c at this end of this file.  Simply save it to cachetest.c and
-and compile it with: 
-
-gcc -o cachetest ./cachetest.c
-
-What it does is read in your passwd file, build a hash of it, and then
-perform the number of lookups you specify either with getpwnam() or using
-the hash lookup functions.
-
-Before performing tests, please note that the larger the passwd file, the
-larger the disparity between the two test methods. So you'll get a more
-accurate depiction of what the code will do for your system by running it
-on a passwd file on your radius server.
-
-I'd recommend doing the following tests with it:
-
-# Perform 1,000 lookups using getpwnam()
-./cachetest 1000 -s
-
-# Perform 1,000 lookups using the hash
-./cachetest 1000
-
-My guess, no real significant difference in the time taken to complete each
-of those. Next test.
-
-# Perform 10,000 lookups using getpwnam()
-./cachetest 10000 -s
-
-# Perform 10,000 lookups using the hash
-./cachetest 10000
-
-Unless you've got a real fast machine, you should have noticed a little bit
-of difference between the two that time. Next test
-
-# Perform 50,000 lookups using getpwnam()
-./cachetest 50000 -s
-
-# Perform 50,000 lookups using the hash
-./cachetest 50000
-
-Unless you have a cray, I'm real confident you noticed a difference that
-time. If the 50,000 user lookup didn't extraordinarily long on your system,
-try this last test.
-
-# Perform 100,000 lookups using getpwnam()
-./cachetest 100000 -s
-
-# Perform 100,000 lookups using the hash
-./cachetest 100000
-
-No doubt, you saw the difference here. Unless you want to be old and gray
-when you finish the getpwnam() tests, I'd recommend not going higher than
-100,000. Still, it's up to you. Go as high as you want on the hash test.
-1,000,000 hash lookups are done in about a second on my pentium 400 running
-linux.
-
-How will it help my radius server?
-
-Well, assuming you can live with the disadvantages listed above, it'll make
-it faster. If you haven't run the tests in the above section, I'd recommend
-doing so.
-
-Is it stable?
-
-As far as I can tell, yes. We've run it on both linux and solaris maches for
-about two weeks now, and had zero problems. If you notice a bug, by all
-means, let me know and I'll see if I can get a fix out. However, if you
-notice system degrading problems with it, you can always turn it off in
-your radiusd.conf file.
-
-Ok, that's it. If you made it this far, I'm impressed. Let me know what you
-think...
-
-jeff@apex.net
-
---------------------------------------------------------------------------
-
-/*
- * cachetest.c:  Tests performance difference between user lookups in
- * the hash table vs. getpwnam()
- *
- * All users in the passwd/shadow files are stored in a hash table.
- * the hash lookup is VERY fast,  generally 1.0673 comparisons per
- * lookup.  For the unitiated, that's blazing.  You can't have less
- * than one comparison, for example.
- *
- *     (c) 1999 Author - Jeff Carneal, Apex Internet Services, Inc.
- */    
-/* 
-       COMPILE:  gcc -o cachetest cachetest.c
-*/
-#include<stdio.h>
-#include<fcntl.h>
-#include<grp.h>
-#include<unistd.h>
-#include<stdlib.h>
-#include<errno.h>
-#include<ctype.h>
-#include<sys/stat.h>
-#include<sys/types.h>
-#include<string.h>
-
-/* Misc definitions */
-#define BUFSIZE  1024
-#define MAXUSERNAME 20
-#define HASHTABLESIZE 100000
-#define PASSWDFILE "/etc/passwd"
-
-/* Structure definitions */
-struct mypasswd {
-       char    *pw_name;       /* user name */
-       char    *pw_passwd;     /* user password */
-       uid_t   pw_uid;         /* user id */
-       gid_t   pw_gid;         /* group id */
-       int     loggedin;       /* number of logins */
-       struct mypasswd *next;  /* next */
-};
-
-/* Function prototypes */
-int buildHashTable(void);
-struct mypasswd *findHashUser(const char *user);
-int storeHashUser(struct mypasswd *new, int index);
-int hashUserName(const char *s);
-int doHashTest(int numusers, unsigned long numlookups);
-int usage(void);
-
-/* Make the tables global since so many functions rely on them */
-static struct mypasswd *hashtable[HASHTABLESIZE];
-static struct mygroup *grphead = NULL;
-
-char allusers[HASHTABLESIZE][MAXUSERNAME];
-int usegetpwnam=0;
-
-int main(int argc, char** argv) {
-       int numusers=0;
-       unsigned long numlookups=0;
-       
-       if(argc<2) {
-               usage();
-       }
-               
-       if(isdigit(argv[1][0])) {
-               numlookups = atoi(argv[1]);     
-       } else {
-               usage();
-       }
-       if((argv[2] != NULL) && !strncmp(argv[2],"-s",2)) {
-               usegetpwnam = 1;
-       }
-       
-       memset((char *)allusers, 0, MAXUSERNAME*HASHTABLESIZE);
-
-       numusers = buildHashTable();
-       if(numusers < 0) {
-               printf("HASH:  Error building hash table!  Quitting...\n");
-               exit(1);
-       }
-
-       doHashTest(numusers, numlookups);
-       printf("Done!\n\n");
-
-}
-
-/* Builds the hash table up by storing passwd/shadow fields
- * in memory.  Returns -1 on failure, 0 on success.
- */
-int buildHashTable(void) {
-       FILE *passwd, *shadow;
-       char buffer[BUFSIZE];
-       char idtmp[10];
-       char username[MAXUSERNAME];
-       char *ptr, *bufptr;
-       int len, hashindex, numread=0;
-       struct mypasswd *new, *cur, *next;
-
-       memset((char *)username, 0, MAXUSERNAME);
-
-       /* Init hash array */
-       memset((struct mypasswd *)hashtable, 0, (HASHTABLESIZE*(sizeof(struct mypasswd *))));
-
-       if( (passwd = fopen(PASSWDFILE, "r")) == NULL) {
-               printf("HASH:  Can't open file %s:  %s", PASSWDFILE, strerror(errno));
-               return -1;
-       } else {
-               while(fgets(buffer, BUFSIZE , passwd) != (char *)NULL) {
-                       numread++;
-
-                       bufptr = buffer;
-                       /* Get usernames from password file */
-                       for(ptr = bufptr; *ptr!=':'; ptr++);
-                       len = ptr - bufptr;
-                       if((len+1) > MAXUSERNAME) {
-                               printf("HASH:  Username too long in line: %s", buffer);
-                       }
-                       strncpy(username, buffer, len);
-                       username[len] = '\0';
-                       if(numread < HASHTABLESIZE) {
-                               strncpy(allusers[numread-1], username, MAXUSERNAME);
-                       }
-
-                       /* Hash the username */
-                       hashindex = hashUserName(username);     
-                       /*printf("%s:%d\n", username, hashindex);*/
-       
-                       /* Allocate space for structure to go in hashtable */
-                       if((new = (struct mypasswd *)malloc(sizeof(struct mypasswd))) == NULL) {
-                               printf("HASH:  Out of memory!");
-                               return -1;
-                       }
-                       memset((struct mypasswd *)new, 0, sizeof(struct mypasswd));
-
-                       /* Put username into new structure */
-                       if((new->pw_name = (char *)malloc(strlen(username)+1)) == NULL) {
-                               printf("HASH:  Out of memory!");
-                               return -1;
-                       }
-                       strncpy(new->pw_name, username, strlen(username)+1);
-
-                       /* Put passwords into array, if not shadowed */
-                       /* Get passwords from password file (shadow comes later) */
-                       ptr++;
-                       bufptr = ptr;
-                       while(*ptr!=':')
-                               ptr++;
-
-                       #if defined(NOSHADOW)
-                       /* Put passwords into new structure (*/
-                       len = ptr - bufptr;
-                       if((new->pw_passwd = (char *)malloc(len+1)) == NULL) {
-                               printf("HASH:  Out of memory!");
-                               return -1;
-                       }
-                       strncpy(new->pw_passwd, bufptr, len);
-                       new->pw_passwd[len] = '\0';
-                       #endif /* NOSHADOW */  
-
-                       /* 
-                   * Put uid into structure.  Not sure why, but 
-                        * at least we'll have it later if we need it
-                        */
-                       ptr++;
-                       bufptr = ptr;
-                       while(*ptr!=':')
-                               ptr++;
-                       len = ptr - bufptr;
-                       strncpy(idtmp, bufptr, len);
-                       idtmp[len] = '\0';
-                       new->pw_uid = (uid_t)atoi(idtmp);       
-
-                       /* 
-                   * Put gid into structure.  
-                        */
-                       ptr++;
-                       bufptr = ptr;
-                       while(*ptr!=':')
-                               ptr++;
-                       len = ptr - bufptr;
-                       strncpy(idtmp, bufptr, len);
-                       idtmp[len] = '\0';
-                       new->pw_gid = (gid_t)atoi(idtmp);       
-
-                       /* 
-                        * We're skipping name, home dir, and shell
-                        * as I can't think of any use for storing them
-                        */
-
-                       /*printf("User:  %s, UID:  %d, GID:  %d\n", new->pw_name, new->pw_uid, new->pw_gid);*/
-                       /* Store user in the hash */
-                       storeHashUser(new, hashindex);
-               }       /* End while(fgets(buffer, BUFSIZE , passwd) != (char *)NULL) { */
-       } /* End if */
-       fclose(passwd);
-
-       return numread;
-}
-
-/*
- * Looks up user in hashtable.  If user can't be found, returns 0.  
- * Otherwise returns a pointer to the structure for the user
- */
-struct mypasswd *findHashUser(const char *user) {
-
-       struct mypasswd *cur;
-       int index;
-
-       /* first hash the username and get the index into the hashtable */
-       index = hashUserName(user);
-
-       cur = hashtable[index];
-
-       while((cur != NULL) && (strcmp(cur->pw_name, user))) {
-               cur = cur->next;
-       }
-
-       if(cur) {
-               return cur;
-       }
-
-       return (struct mypasswd *)0;
-
-}
-
-/* Stores the username sent into the hashtable */
-int storeHashUser(struct mypasswd *new, int index) {
-
-       /* store new record at beginning of list */
-       new->next = hashtable[index];
-       hashtable[index] = new;
-
-       return 1;
-}
-
-/* Hashes the username sent to it and returns index into hashtable */
-int hashUserName(const char *s) {
-     unsigned long hash = 0;
-
-     while (*s != '\0') {
-         hash = hash * 7907 + (unsigned char)*s++;
-               }
-
-     return (hash % HASHTABLESIZE);
-}              
-
-int doHashTest(int numusers, unsigned long numlookups) {
-       int i, numlookedup = 0;
-
-       /* Use pokey getpwnam() syscall to find users */
-       if(usegetpwnam) {
-               printf("\nUsing getpwnam() lookup for %d user lookups over %d users found\n\n", numlookups, numusers);
-               while(numlookedup < numlookups) {
-                       for(i=0; i<numusers; i++) {
-                               if(numlookedup >= numlookups) 
-                                       return 0;
-
-                               if(allusers[i] != NULL)  {
-                                       getpwnam(allusers[i]);
-                                       numlookedup++;
-                               }
-                       }
-               }
-
-       /* Use blazing fast hash method to find user */
-       } else {
-               printf("\nUsing hash lookup for %d user lookups over %d users found\n\n", numlookups, numusers);
-               while(numlookedup < numlookups) {
-                       for(i=0; i<numusers; i++) {
-                               if(numlookedup >= numlookups) 
-                                       return 0;
-
-                               if(allusers[i] != NULL)  {
-                                       findHashUser(allusers[i]);
-                                       numlookedup++;
-                               }
-                       }
-               }
-       }
-       return 0;
-}
-
-int usage() {
-       printf("\nUsage:  ./cachetest #lookups [-s]\n");
-       printf("\n\tPerform 50,000 lookups using the hash method");
-       printf("\n\tEx:  ./cachetest 50000\n");
-       printf("\n\tPerform 50,000 lookups using the getpwnam() syscall");
-       printf("\n\tEx:  ./cachetest 50000 -s\n\n");
-       exit(1);
-
-}
index a2afc44..efd6675 100644 (file)
@@ -2,18 +2,22 @@
 .SH NAME
 rlm_unix \- FreeRADIUS Module
 .SH DESCRIPTION
-The \fIrlm_unix\fP module allows authentication against the 
-system password, shadow, and group files.  It also provides
-FreeRADIUS an interface into a system wtmp file when
-added to the accounting section.
+The \fIrlm_unix\fP module allows authentication against the system
+password, shadow, and group files.  It also provides FreeRADIUS an
+interface into a radwtmp file (used by "radlast") when added to the
+accounting section.
+.PP
+The \fIrlm_unix\fP module provides the functionality for "Auth-Type =
+System", rather than "Auth-Type = Unix".  The "System" name is used
+for historical reasons.
 .PP
 The main configuration items to be aware of are:
 .IP cache
 This is a 'yes' or 'no' option.  If set to yes, FreeRADIUS will read
-the system files into memory, rather than perform a systemcall to
+the system files into memory, rather than perform a system call to
 lookup the information.  On *BSD systems, you should set this value to
 no.  On other systems, if you have a very large passwd and shadow
-files, you can try setting this to yes to increase RADIUS
+files, you can try setting this to yes, which may increase the servers
 performance.  The default is no.
 .IP cache_reload
 This is the number of seconds to wait between refreshing the cached 
@@ -38,7 +42,7 @@ attribute to be used as a check item.  Default is 'no'.
 .PP
 .DS
 modules {
-  ... stuff here ...
+  ...
 .br
   unix {
 .br
@@ -58,7 +62,7 @@ modules {
 .br
   }
 .br
-  ... stuff here ...
+  ...
 .br
 }
 .DE