]> granicus.if.org Git - shadow/blob - src/usermod.c
[svn-upgrade] Integrating new upstream version, shadow (4.0.4)
[shadow] / src / usermod.c
1 /*
2  * Copyright 1991 - 1994, Julianne Frances Haugh
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of Julianne F. Haugh nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #include <config.h>
31
32 #include "rcsid.h"
33 RCSID (PKG_VER "$Id: usermod.c,v 1.31 2003/06/30 13:17:51 kloczek Exp $")
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <stdio.h>
37 #include <errno.h>
38 #include <pwd.h>
39 #include <grp.h>
40 #include <ctype.h>
41 #include <fcntl.h>
42 #include <time.h>
43 #ifdef USE_PAM
44 #include <security/pam_appl.h>
45 #include <security/pam_misc.h>
46 #include <pwd.h>
47 #endif                          /* USE_PAM */
48 #include "prototypes.h"
49 #include "defines.h"
50 #include "chkname.h"
51 #include "faillog.h"
52 #if HAVE_LASTLOG_H
53 #include <lastlog.h>
54 #else
55 #include "lastlog_.h"
56 #endif
57 #include "pwauth.h"
58 #include "nscd.h"
59 #include "getdef.h"
60 /*
61  * exit status values
62  * for E_GRP_UPDATE and E_NOSPACE (not used yet), other update requests
63  * will be implemented (as documented in the Solaris 2.x man page).
64  */
65 #define E_SUCCESS       0       /* success */
66 #define E_PW_UPDATE     1       /* can't update password file */
67 #define E_USAGE         2       /* invalid command syntax */
68 #define E_BAD_ARG       3       /* invalid argument to option */
69 #define E_UID_IN_USE    4       /* uid already in use (and no -o) */
70     /* #define E_BAD_PWFILE     5 *//* passwd file contains errors */
71 #define E_NOTFOUND      6       /* specified user/group doesn't exist */
72 #define E_USER_BUSY     8       /* user to modify is logged in */
73 #define E_NAME_IN_USE   9       /* username already in use */
74 #define E_GRP_UPDATE    10      /* can't update group file */
75     /* #define E_NOSPACE        11 *//* insufficient space to move home dir */
76 #define E_HOMEDIR       12      /* unable to complete home dir move */
77 #define VALID(s)        (strcspn (s, ":\n") == strlen (s))
78 static char *user_name;
79 static char *user_newname;
80 static char *user_pass;
81 static uid_t user_id;
82 static uid_t user_newid;
83 static gid_t user_gid;
84 static gid_t user_newgid;
85 static char *user_comment;
86 static char *user_home;
87 static char *user_newhome;
88 static char *user_shell;
89
90 #ifdef  SHADOWPWD
91 static long user_expire;
92 static long user_inactive;
93 #endif
94 static long sys_ngroups;
95 static char **user_groups;      /* NULL-terminated list */
96
97 static char *Prog;
98
99 static int
100  uflg = 0,                      /* specify new user ID */
101  oflg = 0,                      /* permit non-unique user ID to be specified with -u */
102  gflg = 0,                      /* new primary group ID */
103  Gflg = 0,                      /* new secondary group set */
104  dflg = 0,                      /* new home directory */
105  sflg = 0,                      /* new shell program */
106  cflg = 0,                      /* new comment (GECOS) field */
107  mflg = 0,                      /* create user's home directory if it doesn't exist */
108 #ifdef SHADOWPWD
109  fflg = 0,                      /* days until account with expired password is locked */
110  eflg = 0,                      /* days since 1970-01-01 when account becomes expired */
111 #endif
112  Lflg = 0,                      /* lock the password */
113  Uflg = 0,                      /* unlock the password */
114  pflg = 0,                      /* new encrypted password */
115  lflg = 0;                      /* new user name */
116
117 #ifdef  NDBM
118 extern int pw_dbm_mode;
119
120 #ifdef  SHADOWPWD
121 extern int sp_dbm_mode;
122 #endif
123 extern int gr_dbm_mode;
124
125 #ifdef  SHADOWGRP
126 extern int sg_dbm_mode;
127 #endif
128 #endif
129
130 #ifdef SHADOWPWD
131 static int is_shadow_pwd;
132 #endif
133 #ifdef SHADOWGRP
134 static int is_shadow_grp;
135 #endif
136
137 #include "groupio.h"
138
139 #ifdef  SHADOWGRP
140 #include "sgroupio.h"
141 #endif
142
143 #include "pwio.h"
144
145 #ifdef  SHADOWPWD
146 #include "shadowio.h"
147 #endif
148
149 /* local function prototypes */
150 static int get_groups (char *);
151 static void usage (void);
152 static void new_pwent (struct passwd *);
153
154 #ifdef SHADOWPWD
155 static void new_spent (struct spwd *);
156 #endif
157 static void fail_exit (int);
158 static int update_group (void);
159
160 #ifdef SHADOWGRP
161 static int update_gshadow (void);
162 #endif
163 static int grp_update (void);
164
165 static long get_number (const char *);
166 static uid_t get_id (const char *);
167 static void process_flags (int, char **);
168 static void close_files (void);
169 static void open_files (void);
170 static void usr_update (void);
171 static void move_home (void);
172 static void update_files (void);
173
174 #ifndef NO_MOVE_MAILBOX
175 static void move_mailbox (void);
176 #endif
177
178 /*
179  * Had to move this over from useradd.c since we have groups named
180  * "56k-family"... ergh.
181  * --Pac.
182  */
183 static struct group *getgr_nam_gid (const char *name)
184 {
185         gid_t gid;
186         char *ep;
187
188         gid = strtoul (name, &ep, 10);
189         if (*name != '\0' && *ep == '\0')       /* valid numeric gid */
190                 return getgrgid (gid);
191
192         return getgrnam (name);
193 }
194
195
196 /*
197  * get_groups - convert a list of group names to an array of group IDs
198  *
199  *      get_groups() takes a comma-separated list of group names and
200  *      converts it to a NULL-terminated array. Any unknown group names are
201  *      reported as errors.
202  */
203
204 static int get_groups (char *list)
205 {
206         char *cp;
207         const struct group *grp;
208         int errors = 0;
209         int ngroups = 0;
210
211         /*
212          * Initialize the list to be empty
213          */
214
215         user_groups[0] = (char *) 0;
216
217         if (!*list)
218                 return 0;
219
220         /*
221          * So long as there is some data to be converted, strip off each
222          * name and look it up. A mix of numerical and string values for
223          * group identifiers is permitted.
224          */
225
226         do {
227                 /*
228                  * Strip off a single name from the list
229                  */
230                 if ((cp = strchr (list, ',')))
231                         *cp++ = '\0';
232
233                 /*
234                  * Names starting with digits are treated as numerical GID
235                  * values, otherwise the string is looked up as is.
236                  */
237                 grp = getgr_nam_gid (list);
238
239                 /*
240                  * There must be a match, either by GID value or by
241                  * string name.
242                  */
243                 if (!grp) {
244                         fprintf (stderr, _("%s: unknown group %s\n"),
245                                  Prog, list);
246                         errors++;
247                 }
248                 list = cp;
249
250                 /*
251                  * If the group doesn't exist, don't dump core. Instead,
252                  * try the next one.  --marekm
253                  */
254                 if (!grp)
255                         continue;
256
257 #ifdef  USE_NIS
258                 /*
259                  * Don't add this group if they are an NIS group. Tell the
260                  * user to go to the server for this group.
261                  */
262
263                 if (__isgrNIS ()) {
264                         fprintf (stderr,
265                                  _("%s: group `%s' is a NIS group.\n"),
266                                  Prog, grp->gr_name);
267                         continue;
268                 }
269 #endif
270
271                 if (ngroups == sys_ngroups) {
272                         fprintf (stderr,
273                                  _
274                                  ("%s: too many groups specified (max %d).\n"),
275                                  Prog, ngroups);
276                         break;
277                 }
278
279                 /*
280                  * Add the group name to the user's list of groups.
281                  */
282
283                 user_groups[ngroups++] = xstrdup (grp->gr_name);
284         } while (list);
285
286         user_groups[ngroups] = (char *) 0;
287
288         /*
289          * Any errors in finding group names are fatal
290          */
291
292         if (errors)
293                 return -1;
294
295         return 0;
296 }
297
298 /*
299  * usage - display usage message and exit
300  */
301
302 static void usage (void)
303 {
304         fprintf (stderr,
305                  _
306                  ("Usage: %s\t[-u uid [-o]] [-g group] [-G group,...] \n"),
307                  Prog);
308         fprintf (stderr,
309                  _
310                  ("\t\t[-d home [-m]] [-s shell] [-c comment] [-l new_name]\n"));
311         fprintf (stderr, "\t\t");
312 #ifdef SHADOWPWD
313         fprintf (stderr, _("[-f inactive] [-e expire] "));
314 #endif
315         fprintf (stderr, _("[-p passwd] [-L|-U] name\n"));
316         exit (E_USAGE);
317 }
318
319 /*
320  * update encrypted password string (for both shadow and non-shadow
321  * passwords)
322  */
323
324 static char *new_pw_passwd (char *pw_pass, const char *pw_name)
325 {
326         if (Lflg && pw_pass[0] != '!') {
327                 char *buf = xmalloc (strlen (pw_pass) + 2);
328
329                 SYSLOG ((LOG_INFO, "lock user `%s' password", pw_name));
330                 strcpy (buf, "!");
331                 strcat (buf, pw_pass);
332                 pw_pass = buf;
333         } else if (Uflg && pw_pass[0] == '!') {
334                 char *s;
335
336                 SYSLOG ((LOG_INFO, "unlock user `%s' password", pw_name));
337                 s = pw_pass;
338                 while (*s) {
339                         *s = *(s + 1);
340                         s++;
341                 }
342         } else if (pflg) {
343                 SYSLOG ((LOG_INFO, "change user `%s' password", pw_name));
344                 pw_pass = xstrdup (user_pass);
345         }
346         return pw_pass;
347 }
348
349 /*
350  * new_pwent - initialize the values in a password file entry
351  *
352  *      new_pwent() takes all of the values that have been entered and fills
353  *      in a (struct passwd) with them.
354  */
355
356 static void new_pwent (struct passwd *pwent)
357 {
358         if (lflg) {
359                 SYSLOG ((LOG_INFO, "change user name `%s' to `%s'",
360                          pwent->pw_name, user_newname));
361                 pwent->pw_name = xstrdup (user_newname);
362         }
363 #ifdef SHADOWPWD
364         if (!is_shadow_pwd)
365 #endif
366                 pwent->pw_passwd =
367                     new_pw_passwd (pwent->pw_passwd, pwent->pw_name);
368
369         if (uflg) {
370                 SYSLOG ((LOG_INFO,
371                          "change user `%s' UID from `%d' to `%d'",
372                          pwent->pw_name, pwent->pw_uid, user_newid));
373                 pwent->pw_uid = user_newid;
374         }
375         if (gflg) {
376                 SYSLOG ((LOG_INFO,
377                          "change user `%s' GID from `%d' to `%d'",
378                          pwent->pw_name, pwent->pw_gid, user_newgid));
379                 pwent->pw_gid = user_newgid;
380         }
381         if (cflg)
382                 pwent->pw_gecos = user_comment;
383
384         if (dflg) {
385                 SYSLOG ((LOG_INFO,
386                          "change user `%s' home from `%s' to `%s'",
387                          pwent->pw_name, pwent->pw_dir, user_newhome));
388                 pwent->pw_dir = user_newhome;
389         }
390         if (sflg) {
391                 SYSLOG ((LOG_INFO,
392                          "change user `%s' shell from `%s' to `%s'",
393                          pwent->pw_name, pwent->pw_shell, user_shell));
394                 pwent->pw_shell = user_shell;
395         }
396 }
397
398 #ifdef  SHADOWPWD
399 /*
400  * new_spent - initialize the values in a shadow password file entry
401  *
402  *      new_spent() takes all of the values that have been entered and fills
403  *      in a (struct spwd) with them.
404  */
405
406 static void new_spent (struct spwd *spent)
407 {
408         if (lflg)
409                 spent->sp_namp = xstrdup (user_newname);
410
411         if (fflg) {
412                 SYSLOG ((LOG_INFO,
413                          "change user `%s' inactive from `%ld' to `%ld'",
414                          spent->sp_namp, spent->sp_inact, user_inactive));
415                 spent->sp_inact = user_inactive;
416         }
417         if (eflg) {
418                 /* XXX - dates might be better than numbers of days.  --marekm */
419                 SYSLOG ((LOG_INFO,
420                          "change user `%s' expiration from `%ld' to `%ld'",
421                          spent->sp_namp, spent->sp_expire, user_expire));
422                 spent->sp_expire = user_expire;
423         }
424         spent->sp_pwdp = new_pw_passwd (spent->sp_pwdp, spent->sp_namp);
425 }
426 #endif                          /* SHADOWPWD */
427
428 /*
429  * fail_exit - exit with an error code after unlocking files
430  */
431
432 static void fail_exit (int code)
433 {
434         (void) gr_unlock ();
435 #ifdef  SHADOWGRP
436         if (is_shadow_grp)
437                 sgr_unlock ();
438 #endif
439 #ifdef  SHADOWPWD
440         if (is_shadow_pwd)
441                 spw_unlock ();
442 #endif
443         (void) pw_unlock ();
444         exit (code);
445 }
446
447
448 static int update_group (void)
449 {
450         int is_member;
451         int was_member;
452         int changed;
453         const struct group *grp;
454         struct group *ngrp;
455
456         /*
457          * Lock and open the group file. This will load all of the group
458          * entries.
459          */
460         if (!gr_lock ()) {
461                 fprintf (stderr, _("%s: error locking group file\n"),
462                          Prog);
463                 SYSLOG ((LOG_ERR, "error locking group file"));
464                 return -1;
465         }
466         if (!gr_open (O_RDWR)) {
467                 fprintf (stderr, _("%s: error opening group file\n"),
468                          Prog);
469                 SYSLOG ((LOG_ERR, "error opening group file"));
470                 gr_unlock ();
471                 return -1;
472         }
473
474         changed = 0;
475
476         /*
477          * Scan through the entire group file looking for the groups that
478          * the user is a member of.
479          */
480         while ((grp = gr_next ())) {
481
482                 /*
483                  * See if the user specified this group as one of their
484                  * concurrent groups.
485                  */
486                 was_member = is_on_list (grp->gr_mem, user_name);
487                 is_member = Gflg && is_on_list (user_groups, grp->gr_name);
488
489                 if (!was_member && !is_member)
490                         continue;
491
492                 ngrp = __gr_dup (grp);
493                 if (!ngrp) {
494                         fprintf (stderr,
495                                  _("%s: out of memory in update_group\n"),
496                                  Prog);
497                         gr_unlock ();
498                         return -1;
499                 }
500
501                 if (was_member && (!Gflg || is_member)) {
502                         if (lflg) {
503                                 ngrp->gr_mem = del_list (ngrp->gr_mem,
504                                                          user_name);
505                                 ngrp->gr_mem = add_list (ngrp->gr_mem,
506                                                          user_newname);
507                                 changed = 1;
508                                 SYSLOG ((LOG_INFO,
509                                          "change `%s' to `%s' in group `%s'",
510                                          user_name, user_newname,
511                                          ngrp->gr_name));
512                         }
513                 } else if (was_member && Gflg && !is_member) {
514                         ngrp->gr_mem = del_list (ngrp->gr_mem, user_name);
515                         changed = 1;
516                         SYSLOG ((LOG_INFO, "delete `%s' from group `%s'",
517                                  user_name, ngrp->gr_name));
518                 } else if (!was_member && Gflg && is_member) {
519                         ngrp->gr_mem = add_list (ngrp->gr_mem,
520                                                  lflg ? user_newname :
521                                                  user_name);
522                         changed = 1;
523                         SYSLOG ((LOG_INFO, "add `%s' to group `%s'",
524                                  lflg ? user_newname : user_name,
525                                  ngrp->gr_name));
526                 }
527                 if (!changed)
528                         continue;
529
530                 changed = 0;
531                 if (!gr_update (ngrp)) {
532                         fprintf (stderr,
533                                  _("%s: error adding new group entry\n"),
534                                  Prog);
535                         SYSLOG ((LOG_ERR, "error adding group entry"));
536                         gr_unlock ();
537                         return -1;
538                 }
539 #ifdef  NDBM
540                 /*
541                  * Update the DBM group file with the new entry as well.
542                  */
543                 if (!gr_dbm_update (ngrp)) {
544                         fprintf (stderr,
545                                  _("%s: cannot add new dbm group entry\n"),
546                                  Prog);
547                         SYSLOG ((LOG_ERR, "error adding dbm group entry"));
548                         gr_unlock ();
549                         return -1;
550                 }
551 #endif                          /* NDBM */
552         }
553 #ifdef NDBM
554         endgrent ();
555 #endif                          /* NDBM */
556         if (!gr_close ()) {
557                 fprintf (stderr, _("%s: cannot rewrite group file\n"),
558                          Prog);
559                 gr_unlock ();
560                 return -1;
561         }
562         gr_unlock ();
563         return 0;
564 }
565
566 #ifdef SHADOWGRP
567 static int update_gshadow (void)
568 {
569         int is_member;
570         int was_member;
571         int was_admin;
572         int changed;
573         const struct sgrp *sgrp;
574         struct sgrp *nsgrp;
575
576         if (!sgr_lock ()) {
577                 fprintf (stderr,
578                          _("%s: error locking shadow group file\n"), Prog);
579                 SYSLOG ((LOG_ERR, "error locking shadow group file"));
580                 return -1;
581         }
582         if (!sgr_open (O_RDWR)) {
583                 fprintf (stderr,
584                          _("%s: error opening shadow group file\n"), Prog);
585                 SYSLOG ((LOG_ERR, "error opening shadow group file"));
586                 sgr_unlock ();
587                 return -1;
588         }
589
590         changed = 0;
591
592         /*
593          * Scan through the entire shadow group file looking for the groups
594          * that the user is a member of.
595          */
596         while ((sgrp = sgr_next ())) {
597
598                 /*
599                  * See if the user was a member of this group
600                  */
601                 was_member = is_on_list (sgrp->sg_mem, user_name);
602
603                 /*
604                  * See if the user was an administrator of this group
605                  */
606                 was_admin = is_on_list (sgrp->sg_adm, user_name);
607
608                 /*
609                  * See if the user specified this group as one of their
610                  * concurrent groups.
611                  */
612                 is_member = Gflg
613                     && is_on_list (user_groups, sgrp->sg_name);
614
615                 if (!was_member && !was_admin && !is_member)
616                         continue;
617
618                 nsgrp = __sgr_dup (sgrp);
619                 if (!nsgrp) {
620                         fprintf (stderr,
621                                  _
622                                  ("%s: out of memory in update_gshadow\n"),
623                                  Prog);
624                         sgr_unlock ();
625                         return -1;
626                 }
627
628                 if (was_admin && lflg) {
629                         nsgrp->sg_adm =
630                             del_list (nsgrp->sg_adm, user_name);
631                         nsgrp->sg_adm =
632                             add_list (nsgrp->sg_adm, user_newname);
633                         changed = 1;
634                         SYSLOG ((LOG_INFO,
635                                  "change admin `%s' to `%s' in shadow group `%s'",
636                                  user_name, user_newname, nsgrp->sg_name));
637                 }
638                 if (was_member && (!Gflg || is_member)) {
639                         if (lflg) {
640                                 nsgrp->sg_mem = del_list (nsgrp->sg_mem,
641                                                           user_name);
642                                 nsgrp->sg_mem = add_list (nsgrp->sg_mem,
643                                                           user_newname);
644                                 changed = 1;
645                                 SYSLOG ((LOG_INFO,
646                                          "change `%s' to `%s' in shadow group `%s'",
647                                          user_name, user_newname,
648                                          nsgrp->sg_name));
649                         }
650                 } else if (was_member && Gflg && !is_member) {
651                         nsgrp->sg_mem =
652                             del_list (nsgrp->sg_mem, user_name);
653                         changed = 1;
654                         SYSLOG ((LOG_INFO,
655                                  "delete `%s' from shadow group `%s'",
656                                  user_name, nsgrp->sg_name));
657                 } else if (!was_member && Gflg && is_member) {
658                         nsgrp->sg_mem = add_list (nsgrp->sg_mem,
659                                                   lflg ? user_newname :
660                                                   user_name);
661                         changed = 1;
662                         SYSLOG ((LOG_INFO, "add `%s' to shadow group `%s'",
663                                  lflg ? user_newname : user_name,
664                                  nsgrp->sg_name));
665                 }
666                 if (!changed)
667                         continue;
668
669                 changed = 0;
670
671                 /* 
672                  * Update the group entry to reflect the changes.
673                  */
674                 if (!sgr_update (nsgrp)) {
675                         fprintf (stderr,
676                                  _("%s: error adding new group entry\n"),
677                                  Prog);
678                         SYSLOG ((LOG_ERR,
679                                  "error adding shadow group entry"));
680                         sgr_unlock ();
681                         return -1;
682                 }
683 #ifdef  NDBM
684                 /*
685                  * Update the DBM group file with the new entry as well.
686                  */
687                 if (!sg_dbm_update (nsgrp)) {
688                         fprintf (stderr,
689                                  _("%s: cannot add new dbm group entry\n"),
690                                  Prog);
691                         SYSLOG ((LOG_ERR,
692                                  "error adding dbm shadow group entry"));
693                         sgr_unlock ();
694                         return -1;
695                 }
696 #endif                          /* NDBM */
697         }
698 #ifdef NDBM
699         endsgent ();
700 #endif                          /* NDBM */
701         if (!sgr_close ()) {
702                 fprintf (stderr,
703                          _("%s: cannot rewrite shadow group file\n"),
704                          Prog);
705                 sgr_unlock ();
706                 return -1;
707         }
708         sgr_unlock ();
709         return 0;
710 }
711 #endif                          /* SHADOWGRP */
712
713 /*
714  * grp_update - add user to secondary group set
715  *
716  *      grp_update() takes the secondary group set given in user_groups and
717  *      adds the user to each group given by that set.
718  */
719
720 static int grp_update (void)
721 {
722         int ret;
723
724         ret = update_group ();
725 #ifdef SHADOWGRP
726         if (!ret && is_shadow_grp)
727                 ret = update_gshadow ();
728 #endif
729         return ret;
730 }
731
732 static long get_number (const char *cp)
733 {
734         long val;
735         char *ep;
736
737         val = strtol (cp, &ep, 10);
738         if (*cp != '\0' && *ep == '\0') /* valid number */
739                 return val;
740
741         fprintf (stderr, _("%s: invalid numeric argument `%s'\n"), Prog,
742                  cp);
743         exit (E_BAD_ARG);
744 }
745
746 static uid_t get_id (const char *cp)
747 {
748         uid_t val;
749         char *ep;
750
751         val = strtoul (cp, &ep, 10);
752         if (*cp != '\0' && *ep == '\0') /* valid number */
753                 return val;
754
755         fprintf (stderr, _("%s: invalid numeric argument `%s'\n"), Prog,
756                  cp);
757         exit (E_BAD_ARG);
758 }
759
760 /*
761  * process_flags - perform command line argument setting
762  *
763  *      process_flags() interprets the command line arguments and sets the
764  *      values that the user will be created with accordingly. The values
765  *      are checked for sanity.
766  */
767
768 static void process_flags (int argc, char **argv)
769 {
770         const struct group *grp;
771         const struct passwd *pwd;
772
773 #ifdef SHADOWPWD
774         const struct spwd *spwd = NULL;
775 #endif
776         int anyflag = 0;
777         int arg;
778
779         if (argc == 1 || argv[argc - 1][0] == '-')
780                 usage ();
781
782         if (!(pwd = getpwnam (argv[argc - 1]))) {
783                 fprintf (stderr, _("%s: user %s does not exist\n"),
784                          Prog, argv[argc - 1]);
785                 exit (E_NOTFOUND);
786         }
787         user_name = argv[argc - 1];
788
789 #ifdef  USE_NIS
790
791         /*
792          * Now make sure it isn't an NIS user.
793          */
794
795         if (__ispwNIS ()) {
796                 char *nis_domain;
797                 char *nis_master;
798
799                 fprintf (stderr, _("%s: user %s is a NIS user\n"),
800                          Prog, user_name);
801
802                 if (!yp_get_default_domain (&nis_domain) &&
803                     !yp_master (nis_domain, "passwd.byname",
804                                 &nis_master)) {
805                         fprintf (stderr, _("%s: %s is the NIS master\n"),
806                                  Prog, nis_master);
807                 }
808                 exit (E_NOTFOUND);
809         }
810 #endif
811         user_id = pwd->pw_uid;
812         user_gid = pwd->pw_gid;
813         user_comment = xstrdup (pwd->pw_gecos);
814         user_home = xstrdup (pwd->pw_dir);
815         user_shell = xstrdup (pwd->pw_shell);
816
817 #ifdef  SHADOWPWD
818         if (is_shadow_pwd && (spwd = getspnam (user_name))) {
819                 user_expire = spwd->sp_expire;
820                 user_inactive = spwd->sp_inact;
821         }
822 #endif
823 #ifdef  SHADOWPWD
824 #define FLAGS "A:u:og:G:d:s:c:mf:e:l:p:LU"
825 #else
826 #define FLAGS "A:u:og:G:d:s:c:ml:p:LU"
827 #endif
828         while ((arg = getopt (argc, argv, FLAGS)) != EOF) {
829 #undef FLAGS
830                 switch (arg) {
831                 case 'c':
832                         if (!VALID (optarg)) {
833                                 fprintf (stderr,
834                                          _("%s: invalid field `%s'\n"),
835                                          Prog, optarg);
836                                 exit (E_BAD_ARG);
837                         }
838                         user_comment = optarg;
839                         cflg++;
840                         break;
841                 case 'd':
842                         if (!VALID (optarg)) {
843                                 fprintf (stderr,
844                                          _("%s: invalid field `%s'\n"),
845                                          Prog, optarg);
846                                 exit (E_BAD_ARG);
847                         }
848                         dflg++;
849                         user_newhome = optarg;
850                         break;
851 #ifdef SHADOWPWD
852                 case 'e':
853                         if (*optarg) {
854                                 user_expire = strtoday (optarg);
855                                 if (user_expire == -1) {
856                                         fprintf (stderr,
857                                                  _
858                                                  ("%s: invalid date `%s'\n"),
859                                                  Prog, optarg);
860                                         exit (E_BAD_ARG);
861                                 }
862                                 user_expire *= DAY / SCALE;
863                         } else
864                                 user_expire = -1;
865                         eflg++;
866                         break;
867                 case 'f':
868                         user_inactive = get_number (optarg);
869                         fflg++;
870                         break;
871 #endif
872                 case 'g':
873                         grp = getgr_nam_gid (optarg);
874                         if (!grp) {
875                                 fprintf (stderr,
876                                          _("%s: unknown group %s\n"),
877                                          Prog, optarg);
878                                 exit (E_NOTFOUND);
879                         }
880                         user_newgid = grp->gr_gid;
881                         gflg++;
882                         break;
883                 case 'G':
884                         if (get_groups (optarg))
885                                 exit (E_NOTFOUND);
886                         Gflg++;
887                         break;
888                 case 'l':
889                         if (!check_user_name (optarg)) {
890                                 fprintf (stderr,
891                                          _("%s: invalid field `%s'\n"),
892                                          Prog, optarg);
893                                 exit (E_BAD_ARG);
894                         }
895
896                         /*
897                          * If the name does not really change, we mustn't
898                          * set the flag as this will cause rather serious
899                          * problems later!
900                          */
901
902                         if (strcmp (user_name, optarg))
903                                 lflg++;
904
905                         user_newname = optarg;
906                         break;
907                 case 'L':
908                         if (Uflg || pflg)
909                                 usage ();
910
911                         Lflg++;
912                         break;
913                 case 'm':
914                         if (!dflg)
915                                 usage ();
916
917                         mflg++;
918                         break;
919                 case 'o':
920                         if (!uflg)
921                                 usage ();
922
923                         oflg++;
924                         break;
925                 case 'p':
926                         if (Lflg || Uflg)
927                                 usage ();
928
929                         user_pass = optarg;
930                         pflg++;
931                         break;
932                 case 's':
933                         if (!VALID (optarg)) {
934                                 fprintf (stderr,
935                                          _("%s: invalid field `%s'\n"),
936                                          Prog, optarg);
937                                 exit (E_BAD_ARG);
938                         }
939                         user_shell = optarg;
940                         sflg++;
941                         break;
942                 case 'u':
943                         user_newid = get_id (optarg);
944                         uflg++;
945                         break;
946                 case 'U':
947                         if (Lflg && pflg)
948                                 usage ();
949
950                         Uflg++;
951                         break;
952                 default:
953                         usage ();
954                 }
955                 anyflag++;
956         }
957         if (anyflag == 0) {
958                 fprintf (stderr, _("%s: no flags given\n"), Prog);
959                 exit (E_USAGE);
960         }
961 #ifdef SHADOWPWD
962         if (!is_shadow_pwd && (eflg || fflg)) {
963                 fprintf (stderr,
964                          _
965                          ("%s: shadow passwords required for -e and -f\n"),
966                          Prog);
967                 exit (E_USAGE);
968         }
969 #endif
970
971         if (optind != argc - 1)
972                 usage ();
973
974         if (dflg && strcmp (user_home, user_newhome) == 0)
975                 dflg = mflg = 0;
976
977         if (uflg && user_id == user_newid)
978                 uflg = oflg = 0;
979
980         if (lflg && getpwnam (user_newname)) {
981                 fprintf (stderr, _("%s: user %s exists\n"), Prog,
982                          user_newname);
983                 exit (E_NAME_IN_USE);
984         }
985
986         if (uflg && !oflg && getpwuid (user_newid)) {
987                 fprintf (stderr, _("%s: uid %lu is not unique\n"),
988                          Prog, (unsigned long) user_newid);
989                 exit (E_UID_IN_USE);
990         }
991 }
992
993 /*
994  * close_files - close all of the files that were opened
995  *
996  *      close_files() closes all of the files that were opened for this new
997  *      user. This causes any modified entries to be written out.
998  */
999
1000 static void close_files (void)
1001 {
1002         if (!pw_close ()) {
1003                 fprintf (stderr, _("%s: cannot rewrite password file\n"),
1004                          Prog);
1005                 fail_exit (E_PW_UPDATE);
1006         }
1007 #ifdef  SHADOWPWD
1008         if (is_shadow_pwd && !spw_close ()) {
1009                 fprintf (stderr,
1010                          _("%s: cannot rewrite shadow password file\n"),
1011                          Prog);
1012                 fail_exit (E_PW_UPDATE);
1013         }
1014 #endif
1015 #ifdef  SHADOWPWD
1016         if (is_shadow_pwd)
1017                 spw_unlock ();
1018 #endif
1019         (void) pw_unlock ();
1020
1021         /*
1022          * Close the DBM and/or flat files
1023          */
1024
1025         endpwent ();
1026 #ifdef  SHADOWPWD
1027         endspent ();
1028 #endif
1029         endgrent ();
1030 #ifdef  SHADOWGRP
1031         endsgent ();
1032 #endif
1033 }
1034
1035 /*
1036  * open_files - lock and open the password files
1037  *
1038  *      open_files() opens the two password files.
1039  */
1040
1041 static void open_files (void)
1042 {
1043         if (!pw_lock ()) {
1044                 fprintf (stderr, _("%s: unable to lock password file\n"),
1045                          Prog);
1046                 exit (E_PW_UPDATE);
1047         }
1048         if (!pw_open (O_RDWR)) {
1049                 fprintf (stderr, _("%s: unable to open password file\n"),
1050                          Prog);
1051                 fail_exit (E_PW_UPDATE);
1052         }
1053 #ifdef  SHADOWPWD
1054         if (is_shadow_pwd && !spw_lock ()) {
1055                 fprintf (stderr,
1056                          _("%s: cannot lock shadow password file\n"),
1057                          Prog);
1058                 fail_exit (E_PW_UPDATE);
1059         }
1060         if (is_shadow_pwd && !spw_open (O_RDWR)) {
1061                 fprintf (stderr,
1062                          _("%s: cannot open shadow password file\n"),
1063                          Prog);
1064                 fail_exit (E_PW_UPDATE);
1065         }
1066 #endif
1067 }
1068
1069 /*
1070  * usr_update - create the user entries
1071  *
1072  *      usr_update() creates the password file entries for this user and
1073  *      will update the group entries if required.
1074  */
1075
1076 static void usr_update (void)
1077 {
1078         struct passwd pwent;
1079         const struct passwd *pwd;
1080
1081 #ifdef  SHADOWPWD
1082         struct spwd spent;
1083         const struct spwd *spwd = NULL;
1084 #endif
1085
1086         /*
1087          * Locate the entry in /etc/passwd, which MUST exist.
1088          */
1089
1090         pwd = pw_locate (user_name);
1091         if (!pwd) {
1092                 fprintf (stderr, _("%s: %s not found in /etc/passwd\n"),
1093                          Prog, user_name);
1094                 fail_exit (E_NOTFOUND);
1095         }
1096         pwent = *pwd;
1097         new_pwent (&pwent);
1098
1099 #ifdef  SHADOWPWD
1100
1101         /* 
1102          * Locate the entry in /etc/shadow. It doesn't have to exist, and
1103          * won't be created if it doesn't.
1104          */
1105
1106         if (is_shadow_pwd && (spwd = spw_locate (user_name))) {
1107                 spent = *spwd;
1108                 new_spent (&spent);
1109         }
1110 #endif
1111
1112         if (lflg || uflg || gflg || cflg || dflg || sflg || pflg
1113             || Lflg || Uflg) {
1114                 if (!pw_update (&pwent)) {
1115                         fprintf (stderr,
1116                                  _("%s: error changing password entry\n"),
1117                                  Prog);
1118                         fail_exit (E_PW_UPDATE);
1119                 }
1120                 if (lflg && !pw_remove (user_name)) {
1121                         fprintf (stderr,
1122                                  _("%s: error removing password entry\n"),
1123                                  Prog);
1124                         fail_exit (E_PW_UPDATE);
1125                 }
1126 #ifdef NDBM
1127                 if (pw_dbm_present ()) {
1128                         if (!pw_dbm_update (&pwent)) {
1129                                 fprintf (stderr,
1130                                          _
1131                                          ("%s: error adding password dbm entry\n"),
1132                                          Prog);
1133                                 fail_exit (E_PW_UPDATE);
1134                         }
1135                         if (lflg && (pwd = getpwnam (user_name)) &&
1136                             !pw_dbm_remove (pwd)) {
1137                                 fprintf (stderr,
1138                                          _
1139                                          ("%s: error removing passwd dbm entry\n"),
1140                                          Prog);
1141                                 fail_exit (E_PW_UPDATE);
1142                         }
1143                 }
1144 #endif
1145         }
1146 #ifdef  SHADOWPWD
1147         if (spwd && (lflg || eflg || fflg || pflg || Lflg || Uflg)) {
1148                 if (!spw_update (&spent)) {
1149                         fprintf (stderr,
1150                                  _
1151                                  ("%s: error adding new shadow password entry\n"),
1152                                  Prog);
1153                         fail_exit (E_PW_UPDATE);
1154                 }
1155                 if (lflg && !spw_remove (user_name)) {
1156                         fprintf (stderr,
1157                                  _
1158                                  ("%s: error removing shadow password entry\n"),
1159                                  Prog);
1160                         fail_exit (E_PW_UPDATE);
1161                 }
1162         }
1163 #ifdef  NDBM
1164         if (spwd && sp_dbm_present ()) {
1165                 if (!sp_dbm_update (&spent)) {
1166                         fprintf (stderr,
1167                                  _
1168                                  ("%s: error updating shadow passwd dbm entry\n"),
1169                                  Prog);
1170                         fail_exit (E_PW_UPDATE);
1171                 }
1172                 if (lflg && !sp_dbm_remove (user_name)) {
1173                         fprintf (stderr,
1174                                  _
1175                                  ("%s: error removing shadow passwd dbm entry\n"),
1176                                  Prog);
1177                         fail_exit (E_PW_UPDATE);
1178                 }
1179         }
1180 #endif                          /* NDBM */
1181 #endif                          /* SHADOWPWD */
1182 }
1183
1184 /*
1185  * move_home - move the user's home directory
1186  *
1187  *      move_home() moves the user's home directory to a new location. The
1188  *      files will be copied if the directory cannot simply be renamed.
1189  */
1190
1191 static void move_home (void)
1192 {
1193         struct stat sb;
1194
1195         if (mflg && stat (user_home, &sb) == 0) {
1196                 /*
1197                  * Don't try to move it if it is not a directory
1198                  * (but /dev/null for example).  --marekm
1199                  */
1200                 if (!S_ISDIR (sb.st_mode))
1201                         return;
1202
1203                 if (access (user_newhome, F_OK) == 0) {
1204                         fprintf (stderr, _("%s: directory %s exists\n"),
1205                                  Prog, user_newhome);
1206                         fail_exit (E_HOMEDIR);
1207                 } else if (rename (user_home, user_newhome)) {
1208                         if (errno == EXDEV) {
1209                                 if (mkdir
1210                                     (user_newhome, sb.st_mode & 0777)) {
1211                                         fprintf (stderr,
1212                                                  _
1213                                                  ("%s: can't create %s\n"),
1214                                                  Prog, user_newhome);
1215                                 }
1216                                 if (chown (user_newhome,
1217                                            sb.st_uid, sb.st_gid)) {
1218                                         fprintf (stderr,
1219                                                  _("%s: can't chown %s\n"),
1220                                                  Prog, user_newhome);
1221                                         rmdir (user_newhome);
1222                                         fail_exit (E_HOMEDIR);
1223                                 }
1224                                 if (copy_tree (user_home, user_newhome,
1225                                                uflg ? user_newid : -1,
1226                                                gflg ? user_newgid : -1) ==
1227                                     0 && remove_tree (user_home) == 0
1228                                     && rmdir (user_home) == 0)
1229                                         return;
1230
1231                                 (void) remove_tree (user_newhome);
1232                                 (void) rmdir (user_newhome);
1233                         }
1234                         fprintf (stderr,
1235                                  _
1236                                  ("%s: cannot rename directory %s to %s\n"),
1237                                  Prog, user_home, user_newhome);
1238                         fail_exit (E_HOMEDIR);
1239                 }
1240         }
1241         if (uflg || gflg)
1242                 chown (dflg ? user_newhome : user_home,
1243                        uflg ? user_newid : user_id,
1244                        gflg ? user_newgid : user_gid);
1245 }
1246
1247 /*
1248  * update_files - update the lastlog and faillog files
1249  */
1250
1251 static void update_files (void)
1252 {
1253         struct lastlog ll;
1254         struct faillog fl;
1255         int fd;
1256
1257         /*
1258          * Relocate the "lastlog" entries for the user. The old entry is
1259          * left alone in case the UID was shared. It doesn't hurt anything
1260          * to just leave it be.
1261          */
1262
1263         if ((fd = open (LASTLOG_FILE, O_RDWR)) != -1) {
1264                 lseek (fd, (off_t) user_id * sizeof ll, SEEK_SET);
1265                 if (read (fd, (char *) &ll, sizeof ll) == sizeof ll) {
1266                         lseek (fd, (off_t) user_newid * sizeof ll,
1267                                SEEK_SET);
1268                         write (fd, (char *) &ll, sizeof ll);
1269                 }
1270                 close (fd);
1271         }
1272
1273         /*
1274          * Relocate the "faillog" entries in the same manner.
1275          */
1276
1277         if ((fd = open (FAILLOG_FILE, O_RDWR)) != -1) {
1278                 lseek (fd, (off_t) user_id * sizeof fl, SEEK_SET);
1279                 if (read (fd, (char *) &fl, sizeof fl) == sizeof fl) {
1280                         lseek (fd, (off_t) user_newid * sizeof fl,
1281                                SEEK_SET);
1282                         write (fd, (char *) &fl, sizeof fl);
1283                 }
1284                 close (fd);
1285         }
1286 }
1287
1288 #ifndef NO_MOVE_MAILBOX
1289 /*
1290  * This is the new and improved code to carefully chown/rename the user's
1291  * mailbox. Maybe I am too paranoid but the mail spool dir sometimes
1292  * happens to be mode 1777 (this makes mail user agents work without
1293  * being setgid mail, but is NOT recommended; they all should be fixed
1294  * to use movemail).  --marekm
1295  */
1296 static void move_mailbox (void)
1297 {
1298         const char *maildir;
1299         char mailfile[1024], newmailfile[1024];
1300         int fd;
1301         struct stat st;
1302
1303         maildir = getdef_str ("MAIL_DIR");
1304 #ifdef MAIL_SPOOL_DIR
1305         if (!maildir && !getdef_str ("MAIL_FILE"))
1306                 maildir = MAIL_SPOOL_DIR;
1307 #endif
1308         if (!maildir)
1309                 return;
1310
1311         /*
1312          * O_NONBLOCK is to make sure open won't hang on mandatory locks.
1313          * We do fstat/fchown to make sure there are no races (someone
1314          * replacing /var/spool/mail/luser with a hard link to /etc/passwd
1315          * between stat and chown).  --marekm
1316          */
1317
1318         snprintf (mailfile, sizeof mailfile, "%s/%s", maildir, user_name);
1319         fd = open (mailfile, O_RDONLY | O_NONBLOCK, 0);
1320         if (fd < 0) {
1321                 /* no need for warnings if the mailbox doesn't exist */
1322                 if (errno != ENOENT)
1323                         perror (mailfile);
1324                 return;
1325         }
1326         if (fstat (fd, &st) < 0) {
1327                 perror ("fstat");
1328                 close (fd);
1329                 return;
1330         }
1331         if (st.st_uid != user_id) {
1332                 /* better leave it alone */
1333                 fprintf (stderr, _("%s: warning: %s not owned by %s\n"),
1334                          Prog, mailfile, user_name);
1335                 close (fd);
1336                 return;
1337         }
1338         if (uflg && fchown (fd, user_newid, (gid_t) - 1) < 0)
1339                 perror (_("failed to change mailbox owner"));
1340
1341         close (fd);
1342
1343         if (lflg) {
1344                 snprintf (newmailfile, sizeof newmailfile, "%s/%s",
1345                           maildir, user_newname);
1346                 if (link (mailfile, newmailfile) || unlink (mailfile))
1347                         perror (_("failed to rename mailbox"));
1348         }
1349 }
1350 #endif
1351
1352 #ifdef USE_PAM
1353 static struct pam_conv conv = {
1354         misc_conv,
1355         NULL
1356 };
1357 #endif                          /* USE_PAM */
1358
1359 /*
1360  * main - usermod command
1361  */
1362
1363 int main (int argc, char **argv)
1364 {
1365         int grp_err = 0;
1366
1367 #ifdef USE_PAM
1368         pam_handle_t *pamh = NULL;
1369         struct passwd *pampw;
1370         int retval;
1371 #endif
1372
1373         sys_ngroups = sysconf (_SC_NGROUPS_MAX);
1374         user_groups = malloc ((1 + sys_ngroups) * sizeof (char *));
1375         user_groups[0] = (char *) 0;
1376         /*
1377          * Get my name so that I can use it to report errors.
1378          */
1379         Prog = Basename (argv[0]);
1380
1381         setlocale (LC_ALL, "");
1382         bindtextdomain (PACKAGE, LOCALEDIR);
1383         textdomain (PACKAGE);
1384
1385 #ifdef USE_PAM
1386         retval = PAM_SUCCESS;
1387
1388         pampw = getpwuid (getuid ());
1389         if (pampw == NULL) {
1390                 retval = PAM_USER_UNKNOWN;
1391         }
1392
1393         if (retval == PAM_SUCCESS) {
1394                 retval =
1395                     pam_start ("shadow", pampw->pw_name, &conv, &pamh);
1396         }
1397
1398         if (retval == PAM_SUCCESS) {
1399                 retval = pam_authenticate (pamh, 0);
1400                 if (retval != PAM_SUCCESS) {
1401                         pam_end (pamh, retval);
1402                 }
1403         }
1404
1405         if (retval == PAM_SUCCESS) {
1406                 retval = pam_acct_mgmt (pamh, 0);
1407                 if (retval != PAM_SUCCESS) {
1408                         pam_end (pamh, retval);
1409                 }
1410         }
1411
1412         if (retval != PAM_SUCCESS) {
1413                 fprintf (stderr, _("%s: PAM authentication failed\n"),
1414                          Prog);
1415                 exit (1);
1416         }
1417 #endif                          /* USE_PAM */
1418
1419         OPENLOG (Prog);
1420
1421 #ifdef SHADOWPWD
1422         is_shadow_pwd = spw_file_present ();
1423 #endif
1424 #ifdef SHADOWGRP
1425         is_shadow_grp = sgr_file_present ();
1426 #endif
1427
1428         /*
1429          * The open routines for the NDBM files don't use read-write as the
1430          * mode, so we have to clue them in.
1431          */
1432
1433 #ifdef  NDBM
1434         pw_dbm_mode = O_RDWR;
1435 #ifdef  SHADOWPWD
1436         sp_dbm_mode = O_RDWR;
1437 #endif
1438         gr_dbm_mode = O_RDWR;
1439 #ifdef  SHADOWGRP
1440         sg_dbm_mode = O_RDWR;
1441 #endif
1442 #endif                          /* NDBM */
1443         process_flags (argc, argv);
1444
1445         /*
1446          * Do the hard stuff - open the files, change the user entries,
1447          * change the home directory, then close and update the files.
1448          */
1449
1450         open_files ();
1451
1452         usr_update ();
1453         nscd_flush_cache ("passwd");
1454         nscd_flush_cache ("group");
1455
1456         close_files ();
1457
1458         if (Gflg || lflg)
1459                 grp_err = grp_update ();
1460
1461         if (mflg)
1462                 move_home ();
1463
1464 #ifndef NO_MOVE_MAILBOX
1465         if (lflg || uflg)
1466                 move_mailbox ();
1467 #endif
1468
1469         if (uflg) {
1470                 update_files ();
1471
1472                 /*
1473                  * Change the UID on all of the files owned by `user_id' to
1474                  * `user_newid' in the user's home directory.
1475                  */
1476
1477                 chown_tree (dflg ? user_newhome : user_home,
1478                             user_id, user_newid,
1479                             user_gid, gflg ? user_newgid : user_gid);
1480         }
1481
1482         if (grp_err)
1483                 exit (E_GRP_UPDATE);
1484
1485 #ifdef USE_PAM
1486         if (retval == PAM_SUCCESS) {
1487                 retval = pam_chauthtok (pamh, 0);
1488                 if (retval != PAM_SUCCESS) {
1489                         pam_end (pamh, retval);
1490                 }
1491         }
1492
1493         if (retval != PAM_SUCCESS) {
1494                 fprintf (stderr, _("%s: PAM chauthtok failed\n"), Prog);
1495                 exit (1);
1496         }
1497
1498         if (retval == PAM_SUCCESS)
1499                 pam_end (pamh, PAM_SUCCESS);
1500 #endif                          /* USE_PAM */
1501
1502         exit (E_SUCCESS);
1503         /* NOT REACHED */
1504 }