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