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