Initial commit for Tizen
[profile/extras/shadow-utils.git] / contrib / adduser.c
1 /****
2 ** 04/21/96
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).
8
9 ** Added max. username length. Used syslog() a bit for important events. 
10 ** Support to immediately expire account with passwd -e.
11
12 ** Called it version 2.0! Because I felt like it!
13
14 ** -- Chris, chris@ferret.lmh.ox.ac.uk
15
16 ** 03/17/96
17 ** hacked a bit more, removed unused code, cleaned up for gcc -Wall.
18 ** --marekm
19 **
20 ** 02/26/96
21 ** modified to call shadow utils (useradd,chage,passwd) on shadowed 
22 ** systems - Cristian Gafton, gafton@sorosis.ro
23 **
24 ** 6/27/95
25 ** shadow-adduser 1.4:
26 **
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.
30 **
31 ** Greg Gallagher
32 ** CIN.Net
33 **
34 ** 1/28/95
35 ** shadow-adduser 1.3:
36 ** 
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.
40 **
41 **                                Brandon
42 *****
43 ** 01/27/95
44 ** 
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:
49 ** 
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.
65 **
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???)
69 **
70 **                               Brandon
71 **                                  photon@usis.com
72 **
73 ***** 
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.
76 **
77 ** Craig Hagan
78 ** hagan@opine.cs.umass.edu
79 **
80 ** Modified to really work, look clean, and find unused uid by Chris Cappuccio
81 ** chris@slinky.cs.umass.edu
82 **
83 *****
84 **
85 ** 01/19/95
86 **
87 ** FURTHER modifications to enable shadow passwd support (kludged, but
88 ** no more so than the original)  by Dan Crowson - dcrowson@mo.net
89 **
90 ** Search on DAN for all changes...
91 **
92 *****
93 **
94 ** cc -O -o adduser adduser.c
95 ** Use gcc if you have it... (political reasons beyond my control) (chris)
96 **
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)
100 **
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.
105 */
106
107 #include <unistd.h>
108 #include <stdlib.h>
109 #include <pwd.h>
110 #include <grp.h>
111 #include <ctype.h>
112 #include <stdio.h>
113 #include <string.h>
114 #include <time.h>
115 #include <sys/types.h>
116 #include <sys/timeb.h>
117 #include <sys/time.h>
118 #include <sys/stat.h>
119 #include <syslog.h>
120
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) */
126
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 */
131
132
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
141
142 #define DEFAULT_MIN_PASS 0
143 #define DEFAULT_MAX_PASS 100
144 #define DEFAULT_WARN_PASS 14
145 #define DEFAULT_USER_DIE 366
146
147 void safeget (char *, int);
148
149 void 
150 main (void)
151 {
152   char foo[32];
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;
157   char cmd[255];
158   struct group *grp;
159
160   /* flags, in order:
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
165    */
166
167   /* The real program starts HERE! */
168
169   if (geteuid () != 0)
170     {
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");
173       exit (1);
174     }
175
176   /* Sanity checks
177    */
178
179 #ifdef LOGGING
180   openlog ("adduser", LOG_PID | LOG_CONS | LOG_NOWAIT, LOG_AUTH);
181   syslog (LOG_INFO, "invoked by user %s\n", getpwuid (getuid ())->pw_name);
182 #endif
183
184   if (!(grp = getgrgid (DEFAULT_GROUP)))
185     {
186       printf ("Error: the default group %d does not exist on this system!\n",
187               DEFAULT_GROUP);
188       printf ("adduser must be recompiled.\n");
189 #ifdef LOGGING
190       syslog (LOG_ERR, "warning: failed. no such default group\n");
191       closelog ();
192 #endif
193       exit (1);
194     };
195
196   while (!correct)
197     {                           /* loop until a "good" usrname is chosen */
198       while (!done)
199         {
200           printf ("\nLogin to add (^C to quit): ");
201           fflush (stdout);
202
203           safeget (usrname, sizeof (usrname));
204
205           if (!strlen (usrname))
206             {
207               printf ("Empty input.\n");
208               done = 0;
209               continue;
210             };
211
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)
215             {
216               printf ("That name is longer than the maximum of %d characters. Choose another.\n", MAX_USRNAME);
217               done = 0;
218             }
219           else if (getpwnam (usrname) != NULL)
220             {
221               printf ("That name is in use, choose another.\n");
222               done = 0;
223             }
224           else if (strchr (usrname, ' ') != NULL)
225             {
226               printf ("No spaces in username!!\n");
227               done = 0;
228             }
229           else
230             done = 1;
231         };                      /* done, we have a valid new user name */
232
233       /* all set, get the rest of the stuff */
234       printf ("\nEditing information for new user [%s]\n", usrname);
235
236       printf ("\nFull Name [%s]: ", usrname);
237       fflush (stdout);
238       safeget (person, sizeof (person));
239       if (!strlen (person))
240         {
241           bzero (person, sizeof (person));
242           strcpy (person, usrname);
243         };
244
245       if (getuid () == 0)
246         {
247           do
248             {
249               bad = 0;
250               printf ("GID [%d]: ", DEFAULT_GROUP);
251               fflush (stdout);
252               safeget (foo, sizeof (foo));
253               if (!strlen (foo))
254                 group = DEFAULT_GROUP;
255               else if (isdigit (*foo))
256                 {
257                   group = atoi (foo);
258                   if (!(grp = getgrgid (group)))
259                     {
260                       printf ("unknown gid %s\n", foo);
261                       group = DEFAULT_GROUP;
262                       bad = 1;
263                     };
264                 }
265               else if ((grp = getgrnam (foo)))
266                 group = grp->gr_gid;
267               else
268                 {
269                   printf ("unknown group %s\n", foo);
270                   group = DEFAULT_GROUP;
271                   bad = 1;
272                 }
273               if (group == 0)
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;
277                   bad = 1;
278                 };
279             }
280           while (bad);
281         }
282       else
283         {
284           printf ("Group will be default of: %d\n", DEFAULT_GROUP);
285           group = DEFAULT_GROUP;
286         }
287
288       if (getuid () == 0)
289         {
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);
292           fflush (stdout);
293           safeget (dir, sizeof (dir));
294           if (!strlen (dir))
295             {                   /* hit return */
296               sprintf (dir, "%s/%s", DEFAULT_HOME, usrname);
297             }
298           else if (dir[strlen (dir) - 1] == '/')
299             sprintf (dir+strlen(dir), "%s", usrname);
300         }
301       else
302         {
303           printf ("\nHome directory will be %s/%s\n", DEFAULT_HOME, usrname);
304           sprintf (dir, "%s/%s", DEFAULT_HOME, usrname);
305         }
306
307       printf ("\nShell [%s]: ", DEFAULT_SHELL);
308       fflush (stdout);
309       safeget (shell, sizeof (shell));
310       if (!strlen (shell))
311         sprintf (shell, "%s", DEFAULT_SHELL);
312       else
313         {
314           char *sh;
315           int ok = 0;
316 #ifdef HAVE_GETUSERSHELL
317           setusershell ();
318           while ((sh = getusershell ()) != NULL)
319             if (!strcmp (shell, sh))
320               ok = 1;
321           endusershell ();
322 #endif
323           if (!ok)
324             {
325               if (getuid () == 0)
326                 printf ("Warning: root allowed non standard shell\n");
327               else
328                 {
329                   printf ("Shell NOT in /etc/shells, DEFAULT used\n");
330                   sprintf (shell, "%s", DEFAULT_SHELL);
331                 }
332             }
333         }
334
335 #ifdef EXPIRE_VALS_SET
336       if (getuid () == 0)
337         {
338 #endif
339           printf ("\nMin. Password Change Days [%d]: ", DEFAULT_MIN_PASS);
340           fflush (stdout);
341           safeget (foo, sizeof (foo));
342           if (strlen (foo) > 1)
343             min_pass = DEFAULT_MIN_PASS;
344           else
345             min_pass = atoi (foo);
346
347           printf ("Max. Password Change Days [%d]: ", DEFAULT_MAX_PASS);
348           fflush (stdout);
349           safeget (foo, sizeof (foo));
350           if (strlen (foo) > 1)
351             max_pass = atoi (foo);
352           else
353             max_pass = DEFAULT_MAX_PASS;
354
355           printf ("Password Warning Days [%d]: ", DEFAULT_WARN_PASS);
356           fflush (stdout);
357           safeget (foo, sizeof (foo));
358           warn_pass = atoi (foo);
359           if (warn_pass == 0)
360
361             warn_pass = DEFAULT_WARN_PASS;
362
363           printf ("Days after Password Expiry for Account Locking [%d]: ", DEFAULT_USER_DIE);
364           fflush (stdout);
365           safeget (foo, sizeof (foo));
366           user_die = atoi (foo);
367           if (user_die == 0)
368             user_die = DEFAULT_USER_DIE;
369
370 #ifdef EXPIRE_VALS_SET
371         }
372       else
373         {
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;
379         }
380 #endif
381
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]: ");
388       fflush (stdout);
389       safeget (foo, sizeof (foo));
390
391       done = bad = correct = (foo[0] == 'y' || foo[0] == 'Y');
392
393       if (bad != 1)
394         printf ("\nUser [%s] not added\n", usrname);
395     }
396
397   /* Clobber the environment, I run this suid root sometimes to let 
398    * non root privileged accounts add users --chris */
399
400   *environ = NULL;
401
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);
406   if (system (cmd))
407     {
408       printf ("User add failed!\n");
409 #ifdef LOGGING
410       syslog (LOG_ERR, "could not add new user\n");
411       closelog ();
412 #endif
413       exit (errno);
414     };
415
416   olduid = getuid ();   /* chage, passwd, edquota etc. require ruid = root
417                          */
418   setuid (0);
419
420   bzero (cmd, sizeof (cmd));
421
422   /* Chage runs suid root. => we need ruid root to run it with
423    * anything other than chage -l
424    */
425
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);
429   if (system (cmd))
430     {
431       printf ("There was an error setting password expire values\n");
432 #ifdef LOGGING
433       syslog (LOG_ERR, "password expire values could not be set\n");
434 #endif
435     };
436
437   /* I want to add a user completely with one easy command --chris */
438
439 #ifdef HAVE_QUOTAS
440   bzero (cmd, sizeof (cmd));
441   sprintf (cmd, "%s -p %s -u %s", EDQUOTA_PATH, QUOTA_DEFAULT, usrname);
442   printf ("%s\n", cmd);
443   if (system (cmd))
444     {
445       printf ("\nWarning: error setting quota\n");
446 #ifdef LOGGING
447       syslog (LOG_ERR, "warning: account created but NO quotas set!\n");
448 #endif /* LOGGING */
449     }
450   else
451     printf ("\nDefault quota set.\n");
452 #endif /* HAVE_QUOTAS */
453
454   bzero (cmd, sizeof (cmd));
455   sprintf (cmd, "%s %s", PASSWD_PATH, usrname);
456   if (system (cmd))
457     {
458       printf ("\nWarning: error setting password\n");
459 #ifdef LOGGING
460       syslog (LOG_ERR, "warning: password set failed!\n");
461 #endif
462     }
463 #ifdef IMMEDIATE_CHANGE
464   bzero (cmd, sizeof (cmd));
465   sprintf (cmd, "%s -e %s", PASSWD_PATH, usrname);
466   if (system (cmd))
467     {
468       printf ("\nWarning: error expiring password\n");
469 #ifdef LOGGING
470       syslog (LOG_ERR, "warning: password expire failed!\n");
471 #endif /* LOGGING */
472     }
473 #endif /* IMMEDIATE_CHANGE */
474
475   setuid (olduid);
476
477 #ifdef LOGGING
478   closelog ();
479 #endif
480
481   printf ("\nDone.\n");
482 }
483
484 void 
485 safeget (char *buf, int maxlen)
486 {
487   int c, i = 0, bad = 0;
488   char *bstart = buf;
489   while ((c = getc (stdin)) != EOF && (c != '\n') && (++i < maxlen))
490     {
491       bad = (!isalnum (c) && (c != '_') && (c != ' '));
492       *(buf++) = (char) c;
493     }
494   *buf = '\0';
495
496   if (bad)
497     {
498       printf ("\nString contained banned character. Please stick to alphanumerics.\n");
499       *bstart = '\0';
500     }
501 }
502