Creating User Accounts
Steven Bellenot
The code for generating user accounts is often one of the most site-specific pieces of software that a system administrator grows. Every system has its quirks, and the account generation program must cover them all. Every account generator does the same basic tasks: creates the user account in a password database, creates the user's home directory, copies the possibly customized dot files into this new directory, and performs whatever else your particular site requires. The generator described in this article was intended for use on undergraduate student accounts, and the quirks there included: disk space quotas, interfacing with a home grown printer accounting program, and of course the first email that specifies the rules of the road. A less obvious requirement was adding all the students to same netgroup and using that netgroup to limit which computers they could use. The account generator described here has been in production use for a couple of years.
The source for this generator is available at:
ftp://ftp.mfi.com/pub/sysadmin/1998/nov98.tar.Z
The source contains more than a dozen files, mostly C code and Bourne shell scripts and a Tcl/Tk file to glue it together. A mock database and NIS setup is included, so you can easily build a working demo. The demo has been tested on a Linux system, as well as the Sun environment.
Seemingly, one of the constants in any computer environment is that there is always an account generator in place and it always breaks. The old generator at our site was a batch system. Classroom instructors would send a list of names by email and get back a hard copy list of accounts with new random passwords assigned by the program. The old system had several spectacular failures. The list of names was assumed to be in the form firstName lastName, and "John Doe" would get the account name "jdoe". If the instructor listed the names backwards, like Doe, John, the account name became "djohn". If the instructor added a comma, like "John Doe," the account name became "jdoe," with the comma. If the instructor listed a double first name, like "Mary Jo Doe," and the next name was "John Deer", it generated accounts "mjo", "djohn", and crashed because it couldn't find a match for the first name "Deer". But the really spectacular failure came when I ran the batch system on a list that still included the mail header.
The New Requirements There were three goals for the new account generator. First, we needed to get the instructors out of the loop. We especially didn't like the need to hand out slips of paper with passwords on them. Second, we wanted the computer lab assistants to do all the work. This was particularly a problem because the account NIS database was maintained on a machine that only system people could log onto. The lab assistant had an X terminal. And finally, we were able to acquire the enrollment data from the registrar, so any typos were part of the student official record and not part of the chaos added by us. This enrollment information was modified until there were flat ASCII files for every math class section, which contained a line like:
lastName firstName perhapsOtherNames socialSecurityNumber otherJunk
for every student in that section.
One of the problems with automatic generation of account names by rules such as these is name collusions. In our case, both Jane Doe and John Doe would map to the user name "jdoe". Even the old generator was smart enough to detect a duplicate hit and abort generation of the duplicate. We added another file to the enrollment database that allowed us to fudge duplicates. The above collusion could be avoid by adding "John Doe2" to this special file, so his user name became "jdoe2". Or we could add "J ohnDoe" to the file to make his user name "johndoe". Currently, this name collusion problem is the only case where a system person is involved with the undergraduate account generation. Collusions are rather rare; forgotten passwords are much more common.
An additional requirement was self-imposed. I used this opportunity to learn and use Tcl/Tk to generate a GUI interface for the lab assistant. I used example routines from an earlier addition of Welch's excellent book on Tcl/Tk. Since then, my students and I have used Tcl/Tk in a number of other programs with good success. I am not a graphic designer; the GUI is merely functional and not a work of art.
NIS and Netgroups We use Netgroups to decide which group of people can log on to which machines. Our typical /etc/passwd file has the "standard users", such as root, bin, and nobody then a few lines at the bottom like:
+@uaccount:x::::::/etc/uaccount-shell
+@ulab:x::::::/etc/not-here-try-there
+@sysadmin:::::::
+:x::::::/etc/nicer-not-here-shell
This particular machine accepts only people in the sysadm netgroup, for all other uses, it accepts their login but runs a different shell program listed in /etc/passwd. The "not-here" shells are compiled c programs, which just print a message and exit. The "uaccount-shell" is describe below. The order in which these occur is important: login takes your first match, and the last + matches everyone (in particular it matches the boss, hence the "nicer"). Warning: On Solaris 2.x this is not the default behavior for passwd. The passwd line in /etc/nsswitch.conf needs to be "passwd: compat".
In NIS, the netgroup database is created from the netgroup file. For example, netgroup has lines like:
sysadmin (,dude1,) (,dude2,) (,dude3,)
uaccount (,labasst1,) (,labasst2,)
eqhosts (host1,,) (host2,,) (host3,,)
ulab0 (,name1,) (,name2,)
ulab1 (,name21,) (,name22,)
ulab2 (,name41,) (,name42,)
ulab ulab0 ulab1 ulab2.
The format is described in detail in netgroup(4), however note that the first field is for hosts, the second for user names, and the last for NIS domains. The multiple "ulab" lines are a way of overcoming the line limit for /etc/netgroup of around 1000 characters. The netgroup "ulab" will contain all the user names on any of the ulab[012] lines.
Solution Architecture Here is a run down of the sequence of events for starting the account generator program. The lab assistant sits in the lab in front of an X terminal called hostA, and starts the program by running a program which just does rlogin nis-master. Since the assistant is in the netgroup "uaccount", this starts the program /etc/uaccount-shell as the assistant's shell. This is a compiled c program, which sets the DISPLAY environment variable to hostA:0.0, and executes the main GUI program account.tcl. This account.tcl is now running on the "nis-master", but displaying on the lab assistant's X terminal. (In some environments, the initial program might have to do an xhost nis-master so that the "nis-master" would have permission to draw on the X terminal hostA. That is not the case here, because the lab assistant has the same home directory on both machines.)
The GUI program is now running, and the lab assistant and student can use it to add a user. The GUI allows them to browse the database of students allowed to add accounts and select one. The student is authenticated both by the lab assistant and by the student's ID number. The GUI can only add accounts of the form <first initial><last name or the first seven letters of the last name>; if there is already such an account, the session fails. Failure is benign; the GUI merely returns to the browsing window. The GUI also checks that the password is good. Once the user password combination has been approved, the GUI calls a setuid c program to do all the work.
The C program updates both the passwd and netgroup database, and calls ypmake to update the database. It calls an even older account-generating script, which creates the home directory, copies the initial dot files, sets up the users quota, turns on printing privileges, and mails the standard "greeting and rules letter". The C program keeps both the passwd and netgroup file in alphabetical order.
The Tcl/Tk Code The GUI code in src/account.tcl uses wish4.2 (Tcl7.6/Tk4.2) and borrows a couple of slightly modified procedures, GetValue and ScrolledListBox2 from the example code to the first edition of Welch's Practical Programming in Tcl and Tk (http://www.beedub.com/book/). GetValue is a generic dialog box that gets one value. GetValue is set to non-echo mode when asking for a password. The class roll database is displayed in two scrolled list boxes, each generated via ScrolledListBox2. The files in the db directory are in the list box to the left, and the students in the selected class are displayed in the listbox to the left. Selecting a class in the class list evokes the procedure, FileSelect, which parses the class file and enters the data in the second listbox.
Selecting a student in the second list box evokes the procedure StudentSelect. StudentSelect is the heart of the system. First, it constructs the user name via the LoginName procedure. It makes sure there is not already a user account with this name via AlreadyAccounted. If the user name is new, then we use the GetValue to get their student number. The student number usually is the social security number. Some folks enter this number as a string of nine digits, while others use hyphens to give the form xxx-xx-xxxx. StudentSelect handles both forms, and if this number matches the one in the database, we continue with the password. As usual, the password must be typed twice and run though the procedure pwok to make sure the password is "difficult" enough. Finally, we call on some utility C code to finish job. The crypt program encrypts the password, which is passed to the addone program to finish adding the user. Finally, our action is logged via logit.sh.
A more-experienced Tcl/Tk programmer likely would not design the program this way. The script is used to glue existing programs together, and some of these programs could also be easily done in Tcl/Tk.
The C Code The crypt.c code is very straightforward. It picks a random salt and encrypts the given password using the crypt(3c) call. The random salt means the same password is encrypted differently for different calls.
The addone.c code (and its helpers, the other files in src) is not very transparent. This code has been abused; basically picks a uid for the user and then updates the system files for the passwd and netgroup. (In the demo case, these files are in the directory ../nis.) Finally, it calls the oldest add user script and mails the welcome notice. The code locks certain files to ensure sequential access with other password utilities.
The netgroup code (newnetgp.c) is perhaps the most interesting helper. The new login user name is added via AddItem, then the old netgroup file is parsed for all names in this group via MergeNetgroups. Each ulab netgroup line is parsed with ProcessUlabLine adding the items with AddItem. The whole list is sorted with qsort via SortItems and then output to the "netout" file. This last task is done via OutputUlabGroups. Apparently, there is a limit in this code; it will break if around 10,000 students are in this one netgroup.
Another interesting item is the way the passwd file is updated. The file temp.pw contains the new line for the passwd file. (This would need modification if your NIS system used both passwd and shadow files.) The shell script vipw.kludge uses vi to read and sort the passwd file. The vipw.kludge file is a HERE document, and the lines between the two "HERE" are almost exactly as they would be typed during a vi editing session. We move to the end of the file, read in the temp.pw file, then the file is sorted and written out. (The original vipw.kludge used vipw to lock the /etc/passwd file.)
When these programs were updated to Solaris 2.x, it was noticed that shell scripts did not work the same. The addone.c code contained several system calls that didn't work anymore. The suidsystem.c and cp.c programs were essentially attempts to get around the system call not working. We discovered that all that was needed was to add the option -p to the shell scripts.
The file locking code was necessary because other users could be updating their passwords (via yppasswd). The newlock.c does the file locking so that yppasswd and addone correctly take turns updating passwords and accounts.
The uid.c does a straightforward search to find the next unused uid in the passwd file. Our passwd file is sorted by user name, so an inspection of the whole file is necessary. The last two acts of addone are to mail root a message and to create the home directory and other user files via the copyfiles.sh.
The Shell Script The shell script copyfiles.sh is really the old adduser script. It creates the home directory and copies configuration files such as .login. For .login, it changes the default printer. The group and owner of these new files are changed to the new user. The initial disk quota is set. Our printer accounting software checks for a file which is created next, then quotas for files on a remote machine are set. Finally, the welcome message is mailed.
Final Words This system just grew. In that, it does show how a hodge podge of techniques can be used, but it certainly is not an example of clear design or implementation. Note that in this implementation, passwords are sent over the wire in clear text. It is also possible to use X Windows and NIS unsecurely.
About the Author
Steve Bellenot has a Ph.D. in Mathematics from Claremont Graduate University. He is currently a member of the sys admin faculty at Florida State University. His interests include Scientific Visualization and cooperative computing. Steve can be reached at: bellenot@math.fsu.edu.
|