3 ** hacked even more, replaced gets() with something slightly harder to buffer
4 ** overflow. Added support for setting a default quota on new account, with
5 ** edquota -p. Other cleanups for security, I let some users run adduser suid
6 ** root to add new accounts. (overflow checks, clobber environment, valid
7 ** shell checks, restrictions on gid + home dir settings).
9 ** Added max. username length. Used syslog() a bit for important events.
10 ** Support to immediately expire account with passwd -e.
12 ** Called it version 2.0! Because I felt like it!
14 ** -- Chris, chris@ferret.lmh.ox.ac.uk
17 ** hacked a bit more, removed unused code, cleaned up for gcc -Wall.
21 ** modified to call shadow utils (useradd,chage,passwd) on shadowed
22 ** systems - Cristian Gafton, gafton@sorosis.ro
25 ** shadow-adduser 1.4:
27 ** now it copies the /etc/skel dir into the person's dir,
28 ** makes the mail folders, changed some defaults and made a 'make
29 ** install' just for the hell of it.
35 ** shadow-adduser 1.3:
37 ** Basically a bug-fix on my additions in 1.2. Thanx to Terry Stewart
38 ** (stew@texas.net) for pointing out one of the many idiotic bugs I introduced.
39 ** It was such a stupid bug that I would have never seen it myself.
45 ** shadow-adduser 1.2:
46 ** I took the C source from adduser-shadow (credits are below) and made
47 ** it a little more worthwhile. Many small changes... Here's
48 ** the ones I can remember:
50 ** Removed support for non-shadowed systems (if you don't have shadow,
51 ** use the original adduser, don't get this shadow version!)
52 ** Added support for the correct /etc/shadow fields (Min days before
53 ** password change, max days before password change, Warning days,
54 ** and how many days from expiry date does the account go invalid)
55 ** The previous version just left all of those fields blank.
56 ** There is still one field left (expiry date for the account, period)
57 ** which I have left blank because I do not use it and didn't want to
58 ** spend any more time on this. I'm sure someone will put it in and
59 ** tack another plethora of credits on here. :)
60 ** Added in the password date field, which should always reflect the last
61 ** date the password was changed, for expiry purposes. "passwd" always
62 ** updates this field, so the adduser program should set it up right
63 ** initially (or a user could keep thier initial password forever ;)
64 ** The number is in days since Jan 1st, 1970.
66 ** Have fun with it, and someone please make
67 ** a real version(this is still just a hack)
68 ** for us all to use (and Email it to me???)
74 ** adduser 1.0: add a new user account (For systems not using shadow)
75 ** With a nice little interface and a will to do all the work for you.
78 ** hagan@opine.cs.umass.edu
80 ** Modified to really work, look clean, and find unused uid by Chris Cappuccio
81 ** chris@slinky.cs.umass.edu
87 ** FURTHER modifications to enable shadow passwd support (kludged, but
88 ** no more so than the original) by Dan Crowson - dcrowson@mo.net
90 ** Search on DAN for all changes...
94 ** cc -O -o adduser adduser.c
95 ** Use gcc if you have it... (political reasons beyond my control) (chris)
97 ** I've gotten this program to work with success under Linux (without
98 ** shadow) and SunOS 4.1.3. I would assume it should work pretty well
99 ** on any system that uses no shadow. (chris)
101 ** If you have no crypt() then try
102 ** cc -DNO_CRYPT -O -o adduser adduser.c xfdes.c
103 ** I'm not sure how login operates with no crypt()... I guess
104 ** the same way we're doing it here.
115 #include <sys/types.h>
116 #include <sys/timeb.h>
117 #include <sys/time.h>
118 #include <sys/stat.h>
121 #define IMMEDIATE_CHANGE /* Expire newly created password, must be changed
122 * immediately upon next login */
123 #define HAVE_QUOTAS /* Obvious */
124 #define EXPIRE_VALS_SET /* If defined, 'normal' users can't change
125 * password expiry values (if running suid root) */
127 #define HAVE_GETUSERSHELL /* FIXME: Isn't this defined in config.h too? */
128 #define LOGGING /* If we want to log various things to syslog */
129 #define MAX_USRNAME 8 /* Longer usernames seem to work on my system....
130 * But they're probably a poor idea */
133 #define DEFAULT_SHELL "/bin/bash" /* because BASH is your friend */
134 #define DEFAULT_HOME "/home"
135 #define USERADD_PATH "/usr/sbin/useradd"
136 #define CHAGE_PATH "/usr/bin/chage"
137 #define PASSWD_PATH "/usr/bin/passwd"
138 #define EDQUOTA_PATH "/usr/sbin/edquota"
139 #define QUOTA_DEFAULT "defuser"
140 #define DEFAULT_GROUP 100
142 #define DEFAULT_MIN_PASS 0
143 #define DEFAULT_MAX_PASS 100
144 #define DEFAULT_WARN_PASS 14
145 #define DEFAULT_USER_DIE 366
147 void safeget (char *, int);
153 char usrname[32], person[32], dir[32], shell[32];
154 unsigned int group, min_pass, max_pass, warn_pass, user_die;
155 /* the group and uid of the new user */
156 int bad = 0, done = 0, correct = 0, olduid;
161 * bad to see if the username is in /etc/passwd, or if strange stuff has
162 * been typed if the user might be put in group 0
163 * done allows the program to exit when a user has been added
164 * correct loops until a username is found that isn't in /etc/passwd
167 /* The real program starts HERE! */
171 printf ("It seems you don't have access to add a new user. Try\n");
172 printf ("logging in as root or su root to gain superuser access.\n");
180 openlog ("adduser", LOG_PID | LOG_CONS | LOG_NOWAIT, LOG_AUTH);
181 syslog (LOG_INFO, "invoked by user %s\n", getpwuid (getuid ())->pw_name);
184 if (!(grp = getgrgid (DEFAULT_GROUP)))
186 printf ("Error: the default group %d does not exist on this system!\n",
188 printf ("adduser must be recompiled.\n");
190 syslog (LOG_ERR, "warning: failed. no such default group\n");
197 { /* loop until a "good" usrname is chosen */
200 printf ("\nLogin to add (^C to quit): ");
203 safeget (usrname, sizeof (usrname));
205 if (!strlen (usrname))
207 printf ("Empty input.\n");
212 /* what I saw here before made me think maybe I was running DOS */
213 /* might this be a solution? (chris) */
214 if (strlen (usrname) > MAX_USRNAME)
216 printf ("That name is longer than the maximum of %d characters. Choose another.\n", MAX_USRNAME);
219 else if (getpwnam (usrname) != NULL)
221 printf ("That name is in use, choose another.\n");
224 else if (strchr (usrname, ' ') != NULL)
226 printf ("No spaces in username!!\n");
231 }; /* done, we have a valid new user name */
233 /* all set, get the rest of the stuff */
234 printf ("\nEditing information for new user [%s]\n", usrname);
236 printf ("\nFull Name [%s]: ", usrname);
238 safeget (person, sizeof (person));
239 if (!strlen (person))
241 bzero (person, sizeof (person));
242 strcpy (person, usrname);
250 printf ("GID [%d]: ", DEFAULT_GROUP);
252 safeget (foo, sizeof (foo));
254 group = DEFAULT_GROUP;
255 else if (isdigit (*foo))
258 if (!(grp = getgrgid (group)))
260 printf ("unknown gid %s\n", foo);
261 group = DEFAULT_GROUP;
265 else if ((grp = getgrnam (foo)))
269 printf ("unknown group %s\n", foo);
270 group = DEFAULT_GROUP;
274 { /* You're not allowed to make root group users! */
275 printf ("Creation of root group users not allowed (must be done by hand)\n");
276 group = DEFAULT_GROUP;
284 printf ("Group will be default of: %d\n", DEFAULT_GROUP);
285 group = DEFAULT_GROUP;
290 printf ("\nIf home dir ends with a / then '%s' will be appended to it\n", usrname);
291 printf ("Home Directory [%s/%s]: ", DEFAULT_HOME, usrname);
293 safeget (dir, sizeof (dir));
296 sprintf (dir, "%s/%s", DEFAULT_HOME, usrname);
298 else if (dir[strlen (dir) - 1] == '/')
299 sprintf (dir+strlen(dir), "%s", usrname);
303 printf ("\nHome directory will be %s/%s\n", DEFAULT_HOME, usrname);
304 sprintf (dir, "%s/%s", DEFAULT_HOME, usrname);
307 printf ("\nShell [%s]: ", DEFAULT_SHELL);
309 safeget (shell, sizeof (shell));
311 sprintf (shell, "%s", DEFAULT_SHELL);
316 #ifdef HAVE_GETUSERSHELL
318 while ((sh = getusershell ()) != NULL)
319 if (!strcmp (shell, sh))
326 printf ("Warning: root allowed non standard shell\n");
329 printf ("Shell NOT in /etc/shells, DEFAULT used\n");
330 sprintf (shell, "%s", DEFAULT_SHELL);
335 #ifdef EXPIRE_VALS_SET
339 printf ("\nMin. Password Change Days [%d]: ", DEFAULT_MIN_PASS);
341 safeget (foo, sizeof (foo));
342 if (strlen (foo) > 1)
343 min_pass = DEFAULT_MIN_PASS;
345 min_pass = atoi (foo);
347 printf ("Max. Password Change Days [%d]: ", DEFAULT_MAX_PASS);
349 safeget (foo, sizeof (foo));
350 if (strlen (foo) > 1)
351 max_pass = atoi (foo);
353 max_pass = DEFAULT_MAX_PASS;
355 printf ("Password Warning Days [%d]: ", DEFAULT_WARN_PASS);
357 safeget (foo, sizeof (foo));
358 warn_pass = atoi (foo);
361 warn_pass = DEFAULT_WARN_PASS;
363 printf ("Days after Password Expiry for Account Locking [%d]: ", DEFAULT_USER_DIE);
365 safeget (foo, sizeof (foo));
366 user_die = atoi (foo);
368 user_die = DEFAULT_USER_DIE;
370 #ifdef EXPIRE_VALS_SET
374 printf ("\nSorry, account expiry values are set.\n");
375 user_die = DEFAULT_USER_DIE;
376 warn_pass = DEFAULT_WARN_PASS;
377 max_pass = DEFAULT_MAX_PASS;
378 min_pass = DEFAULT_MIN_PASS;
382 printf ("\nInformation for new user [%s] [%s]:\n", usrname, person);
383 printf ("Home directory: [%s] Shell: [%s]\n", dir, shell);
384 printf ("GID: [%d]\n", group);
385 printf ("MinPass: [%d] MaxPass: [%d] WarnPass: [%d] UserExpire: [%d]\n",
386 min_pass, max_pass, warn_pass, user_die);
387 printf ("\nIs this correct? [y/N]: ");
389 safeget (foo, sizeof (foo));
391 done = bad = correct = (foo[0] == 'y' || foo[0] == 'Y');
394 printf ("\nUser [%s] not added\n", usrname);
397 /* Clobber the environment, I run this suid root sometimes to let
398 * non root privileged accounts add users --chris */
402 bzero (cmd, sizeof (cmd));
403 sprintf (cmd, "%s -g %d -d %s -s %s -c \"%s\" -m -k /etc/skel %s",
404 USERADD_PATH, group, dir, shell, person, usrname);
405 printf ("Calling useradd to add new user:\n%s\n", cmd);
408 printf ("User add failed!\n");
410 syslog (LOG_ERR, "could not add new user\n");
416 olduid = getuid (); /* chage, passwd, edquota etc. require ruid = root
420 bzero (cmd, sizeof (cmd));
422 /* Chage runs suid root. => we need ruid root to run it with
423 * anything other than chage -l
426 sprintf (cmd, "%s -m %d -M %d -W %d -I %d %s", CHAGE_PATH,
427 min_pass, max_pass, warn_pass, user_die, usrname);
428 printf ("%s\n", cmd);
431 printf ("There was an error setting password expire values\n");
433 syslog (LOG_ERR, "password expire values could not be set\n");
437 /* I want to add a user completely with one easy command --chris */
440 bzero (cmd, sizeof (cmd));
441 sprintf (cmd, "%s -p %s -u %s", EDQUOTA_PATH, QUOTA_DEFAULT, usrname);
442 printf ("%s\n", cmd);
445 printf ("\nWarning: error setting quota\n");
447 syslog (LOG_ERR, "warning: account created but NO quotas set!\n");
451 printf ("\nDefault quota set.\n");
452 #endif /* HAVE_QUOTAS */
454 bzero (cmd, sizeof (cmd));
455 sprintf (cmd, "%s %s", PASSWD_PATH, usrname);
458 printf ("\nWarning: error setting password\n");
460 syslog (LOG_ERR, "warning: password set failed!\n");
463 #ifdef IMMEDIATE_CHANGE
464 bzero (cmd, sizeof (cmd));
465 sprintf (cmd, "%s -e %s", PASSWD_PATH, usrname);
468 printf ("\nWarning: error expiring password\n");
470 syslog (LOG_ERR, "warning: password expire failed!\n");
473 #endif /* IMMEDIATE_CHANGE */
481 printf ("\nDone.\n");
485 safeget (char *buf, int maxlen)
487 int c, i = 0, bad = 0;
489 while ((c = getc (stdin)) != EOF && (c != '\n') && (++i < maxlen))
491 bad = (!isalnum (c) && (c != '_') && (c != ' '));
498 printf ("\nString contained banned character. Please stick to alphanumerics.\n");