]> granicus.if.org Git - shadow/blob - src/usermod.c
916483d2906045c214f5168c95ef15832495474a
[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                                 if (Uflg || pflg)
985                                         usage ();
986
987                                 Lflg++;
988                                 break;
989                         case 'm':
990                                 if (!dflg)
991                                         usage ();
992
993                                 mflg++;
994                                 break;
995                         case 'o':
996                                 if (!uflg)
997                                         usage ();
998
999                                 oflg++;
1000                                 break;
1001                         case 'p':
1002                                 if (Lflg || Uflg)
1003                                         usage ();
1004
1005                                 user_pass = optarg;
1006                                 pflg++;
1007                                 break;
1008                         case 's':
1009                                 if (!VALID (optarg)) {
1010                                         fprintf (stderr,
1011                                                  _("%s: invalid field '%s'\n"),
1012                                                  Prog, optarg);
1013                                         exit (E_BAD_ARG);
1014                                 }
1015 #ifdef WITH_AUDIT
1016                                 user_newshell = optarg;
1017 #else
1018                                 user_shell = optarg;
1019 #endif
1020                                 sflg++;
1021                                 break;
1022                         case 'u':
1023                                 user_newid = get_id (optarg);
1024                                 uflg++;
1025                                 break;
1026                         case 'U':
1027                                 if (Lflg && pflg)
1028                                         usage ();
1029
1030                                 Uflg++;
1031                                 break;
1032                         default:
1033                                 usage ();
1034                         }
1035                         anyflag++;
1036                 }
1037         }
1038
1039         if (anyflag == 0) {
1040                 fprintf (stderr, _("%s: no flags given\n"), Prog);
1041                 exit (E_USAGE);
1042         }
1043         if (!is_shadow_pwd && (eflg || fflg)) {
1044                 fprintf (stderr,
1045                          _
1046                          ("%s: shadow passwords required for -e and -f\n"),
1047                          Prog);
1048                 exit (E_USAGE);
1049         }
1050
1051         if (optind != argc - 1)
1052                 usage ();
1053
1054         if (aflg && (!Gflg)) {
1055                 fprintf (stderr,
1056                          _("%s: -a flag is ONLY allowed with the -G flag\n"),
1057                          Prog);
1058                 usage ();
1059                 exit (E_USAGE);
1060         }
1061
1062         if (dflg && strcmp (user_home, user_newhome) == 0)
1063                 dflg = mflg = 0;
1064
1065         if (uflg && user_id == user_newid)
1066                 uflg = oflg = 0;
1067
1068         if (lflg && getpwnam (user_newname)) {
1069                 fprintf (stderr, _("%s: user %s exists\n"), Prog, user_newname);
1070                 exit (E_NAME_IN_USE);
1071         }
1072
1073         if (uflg && !oflg && getpwuid (user_newid)) {
1074                 fprintf (stderr, _("%s: uid %lu is not unique\n"),
1075                          Prog, (unsigned long) user_newid);
1076                 exit (E_UID_IN_USE);
1077         }
1078 }
1079
1080 /*
1081  * close_files - close all of the files that were opened
1082  *
1083  *      close_files() closes all of the files that were opened for this new
1084  *      user. This causes any modified entries to be written out.
1085  */
1086 static void close_files (void)
1087 {
1088         if (!pw_close ()) {
1089                 fprintf (stderr, _("%s: cannot rewrite password file\n"), Prog);
1090                 fail_exit (E_PW_UPDATE);
1091         }
1092         if (is_shadow_pwd && !spw_close ()) {
1093                 fprintf (stderr,
1094                          _("%s: cannot rewrite shadow password file\n"), Prog);
1095                 fail_exit (E_PW_UPDATE);
1096         }
1097
1098         if (Gflg || lflg) {
1099                 if (!gr_close ()) {
1100                         fprintf (stderr, _("%s: cannot rewrite group file\n"),
1101                                  Prog);
1102                         fail_exit (E_GRP_UPDATE);
1103                 }
1104 #ifdef SHADOWGRP
1105                 if (is_shadow_grp && !sgr_close ()) {
1106                         fprintf (stderr,
1107                                  _("%s: cannot rewrite shadow group file\n"),
1108                                  Prog);
1109                         fail_exit (E_GRP_UPDATE);
1110                 }
1111                 if (is_shadow_grp)
1112                         sgr_unlock ();
1113 #endif
1114                 gr_unlock ();
1115         }
1116
1117         if (is_shadow_pwd)
1118                 spw_unlock ();
1119         pw_unlock ();
1120
1121         pw_locked = 0;
1122         spw_locked = 0;
1123         gr_locked = 0;
1124         sgr_locked = 0;
1125
1126         /*
1127          * Close the DBM and/or flat files
1128          */
1129         endpwent ();
1130         endspent ();
1131         endgrent ();
1132 #ifdef  SHADOWGRP
1133         endsgent ();
1134 #endif
1135 }
1136
1137 /*
1138  * open_files - lock and open the password files
1139  *
1140  *      open_files() opens the two password files.
1141  */
1142 static void open_files (void)
1143 {
1144         if (!pw_lock ()) {
1145                 fprintf (stderr, _("%s: unable to lock password file\n"), Prog);
1146                 fail_exit (E_PW_UPDATE);
1147         }
1148         pw_locked = 1;
1149         if (!pw_open (O_RDWR)) {
1150                 fprintf (stderr, _("%s: unable to open password file\n"), Prog);
1151                 fail_exit (E_PW_UPDATE);
1152         }
1153         if (is_shadow_pwd && !spw_lock ()) {
1154                 fprintf (stderr,
1155                          _("%s: cannot lock shadow password file\n"), Prog);
1156                 fail_exit (E_PW_UPDATE);
1157         }
1158         spw_locked = 1;
1159         if (is_shadow_pwd && !spw_open (O_RDWR)) {
1160                 fprintf (stderr,
1161                          _("%s: cannot open shadow password file\n"), Prog);
1162                 fail_exit (E_PW_UPDATE);
1163         }
1164
1165         if (Gflg || lflg) {
1166                 /*
1167                  * Lock and open the group file. This will load all of the
1168                  * group entries.
1169                  */
1170                 if (!gr_lock ()) {
1171                         fprintf (stderr, _("%s: error locking group file\n"),
1172                                  Prog);
1173                         fail_exit (E_GRP_UPDATE);
1174                 }
1175                 gr_locked = 1;
1176                 if (!gr_open (O_RDWR)) {
1177                         fprintf (stderr, _("%s: error opening group file\n"),
1178                                  Prog);
1179                         fail_exit (E_GRP_UPDATE);
1180                 }
1181 #ifdef SHADOWGRP
1182                 if (is_shadow_grp && !sgr_lock ()) {
1183                         fprintf (stderr,
1184                                  _("%s: error locking shadow group file\n"),
1185                                  Prog);
1186                         fail_exit (E_GRP_UPDATE);
1187                 }
1188                 sgr_locked = 1;
1189                 if (is_shadow_grp && !sgr_open (O_RDWR)) {
1190                         fprintf (stderr,
1191                                  _("%s: error opening shadow group file\n"),
1192                                  Prog);
1193                         fail_exit (E_GRP_UPDATE);
1194                 }
1195 #endif
1196         }
1197
1198
1199
1200 }
1201
1202 /*
1203  * usr_update - create the user entries
1204  *
1205  *      usr_update() creates the password file entries for this user and
1206  *      will update the group entries if required.
1207  */
1208 static void usr_update (void)
1209 {
1210         struct passwd pwent;
1211         const struct passwd *pwd;
1212
1213         struct spwd spent;
1214         const struct spwd *spwd = NULL;
1215
1216         /*
1217          * Locate the entry in /etc/passwd, which MUST exist.
1218          */
1219         pwd = pw_locate (user_name);
1220         if (!pwd) {
1221                 fprintf (stderr, _("%s: %s not found in /etc/passwd\n"),
1222                          Prog, user_name);
1223                 fail_exit (E_NOTFOUND);
1224         }
1225         pwent = *pwd;
1226         new_pwent (&pwent);
1227
1228
1229         /* 
1230          * Locate the entry in /etc/shadow. It doesn't have to exist, and
1231          * won't be created if it doesn't.
1232          */
1233         if (is_shadow_pwd && (spwd = spw_locate (user_name))) {
1234                 spent = *spwd;
1235                 new_spent (&spent);
1236         }
1237
1238         if (lflg || uflg || gflg || cflg || dflg || sflg || pflg
1239             || Lflg || Uflg) {
1240                 if (!pw_update (&pwent)) {
1241                         fprintf (stderr,
1242                                  _("%s: error changing password entry\n"),
1243                                  Prog);
1244                         fail_exit (E_PW_UPDATE);
1245                 }
1246                 if (lflg && !pw_remove (user_name)) {
1247                         fprintf (stderr,
1248                                  _("%s: error removing password entry\n"),
1249                                  Prog);
1250                         fail_exit (E_PW_UPDATE);
1251                 }
1252         }
1253         if (spwd && (lflg || eflg || fflg || pflg || Lflg || Uflg)) {
1254                 if (!spw_update (&spent)) {
1255                         fprintf (stderr,
1256                                  _
1257                                  ("%s: error adding new shadow password entry\n"),
1258                                  Prog);
1259                         fail_exit (E_PW_UPDATE);
1260                 }
1261                 if (lflg && !spw_remove (user_name)) {
1262                         fprintf (stderr,
1263                                  _
1264                                  ("%s: error removing shadow password entry\n"),
1265                                  Prog);
1266                         fail_exit (E_PW_UPDATE);
1267                 }
1268         }
1269 }
1270
1271 /*
1272  * move_home - move the user's home directory
1273  *
1274  *      move_home() moves the user's home directory to a new location. The
1275  *      files will be copied if the directory cannot simply be renamed.
1276  */
1277 static void move_home (void)
1278 {
1279         struct stat sb;
1280
1281         if (mflg && stat (user_home, &sb) == 0) {
1282                 /*
1283                  * Don't try to move it if it is not a directory
1284                  * (but /dev/null for example).  --marekm
1285                  */
1286                 if (!S_ISDIR (sb.st_mode))
1287                         return;
1288
1289                 if (access (user_newhome, F_OK) == 0) {
1290                         fprintf (stderr, _("%s: directory %s exists\n"),
1291                                  Prog, user_newhome);
1292                         fail_exit (E_HOMEDIR);
1293                 } else if (rename (user_home, user_newhome)) {
1294                         if (errno == EXDEV) {
1295                                 if (mkdir (user_newhome, sb.st_mode & 0777)) {
1296                                         fprintf (stderr,
1297                                                  _
1298                                                  ("%s: can't create %s\n"),
1299                                                  Prog, user_newhome);
1300                                 }
1301                                 if (chown (user_newhome, sb.st_uid, sb.st_gid)) {
1302                                         fprintf (stderr,
1303                                                  _("%s: can't chown %s\n"),
1304                                                  Prog, user_newhome);
1305                                         rmdir (user_newhome);
1306                                         fail_exit (E_HOMEDIR);
1307                                 }
1308                                 if (copy_tree (user_home, user_newhome,
1309                                                uflg ? user_newid : -1,
1310                                                gflg ? user_newgid : -1) == 0) {
1311                                         if (remove_tree (user_home) != 0 ||
1312                                             rmdir (user_home) != 0)
1313                                                 fprintf (stderr,
1314                                                          _
1315                                                          ("%s: warning: failed to completely remove old home directory %s"),
1316                                                          Prog, user_home);
1317 #ifdef WITH_AUDIT
1318                                         audit_logger (AUDIT_USER_CHAUTHTOK,
1319                                                       Prog,
1320                                                       "moving home directory",
1321                                                       user_newname, user_newid,
1322                                                       1);
1323 #endif
1324                                         return;
1325                                 }
1326
1327                                 (void) remove_tree (user_newhome);
1328                                 (void) rmdir (user_newhome);
1329                         }
1330                         fprintf (stderr,
1331                                  _
1332                                  ("%s: cannot rename directory %s to %s\n"),
1333                                  Prog, user_home, user_newhome);
1334                         fail_exit (E_HOMEDIR);
1335                 }
1336 #ifdef WITH_AUDIT
1337                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
1338                               "moving home directory", user_newname, user_newid,
1339                               1);
1340 #endif
1341         }
1342         if (uflg || gflg) {
1343 #ifdef WITH_AUDIT
1344                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
1345                               "changing home directory owner", user_newname,
1346                               user_newid, 1);
1347 #endif
1348                 chown (dflg ? user_newhome : user_home,
1349                        uflg ? user_newid : user_id,
1350                        gflg ? user_newgid : user_gid);
1351         }
1352 }
1353
1354 /*
1355  * update_files - update the lastlog and faillog files
1356  */
1357 static void update_files (void)
1358 {
1359         struct lastlog ll;
1360         struct faillog fl;
1361         int fd;
1362
1363         /*
1364          * Relocate the "lastlog" entries for the user. The old entry is
1365          * left alone in case the UID was shared. It doesn't hurt anything
1366          * to just leave it be.
1367          */
1368         if ((fd = open (LASTLOG_FILE, O_RDWR)) != -1) {
1369                 lseek (fd, (off_t) user_id * sizeof ll, SEEK_SET);
1370                 if (read (fd, (char *) &ll, sizeof ll) == sizeof ll) {
1371                         lseek (fd, (off_t) user_newid * sizeof ll, SEEK_SET);
1372                         write (fd, (char *) &ll, sizeof ll);
1373                 }
1374                 close (fd);
1375         }
1376
1377         /*
1378          * Relocate the "faillog" entries in the same manner.
1379          */
1380         if ((fd = open (FAILLOG_FILE, O_RDWR)) != -1) {
1381                 lseek (fd, (off_t) user_id * sizeof fl, SEEK_SET);
1382                 if (read (fd, (char *) &fl, sizeof fl) == sizeof fl) {
1383                         lseek (fd, (off_t) user_newid * sizeof fl, SEEK_SET);
1384                         write (fd, (char *) &fl, sizeof fl);
1385                 }
1386                 close (fd);
1387         }
1388 }
1389
1390 #ifndef NO_MOVE_MAILBOX
1391 /*
1392  * This is the new and improved code to carefully chown/rename the user's
1393  * mailbox. Maybe I am too paranoid but the mail spool dir sometimes
1394  * happens to be mode 1777 (this makes mail user agents work without
1395  * being setgid mail, but is NOT recommended; they all should be fixed
1396  * to use movemail).  --marekm
1397  */
1398 static void move_mailbox (void)
1399 {
1400         const char *maildir;
1401         char mailfile[1024], newmailfile[1024];
1402         int fd;
1403         struct stat st;
1404
1405         maildir = getdef_str ("MAIL_DIR");
1406 #ifdef MAIL_SPOOL_DIR
1407         if (!maildir && !getdef_str ("MAIL_FILE"))
1408                 maildir = MAIL_SPOOL_DIR;
1409 #endif
1410         if (!maildir)
1411                 return;
1412
1413         /*
1414          * O_NONBLOCK is to make sure open won't hang on mandatory locks.
1415          * We do fstat/fchown to make sure there are no races (someone
1416          * replacing /var/spool/mail/luser with a hard link to /etc/passwd
1417          * between stat and chown).  --marekm
1418          */
1419         snprintf (mailfile, sizeof mailfile, "%s/%s", maildir, user_name);
1420         fd = open (mailfile, O_RDONLY | O_NONBLOCK, 0);
1421         if (fd < 0) {
1422                 /* no need for warnings if the mailbox doesn't exist */
1423                 if (errno != ENOENT)
1424                         perror (mailfile);
1425                 return;
1426         }
1427         if (fstat (fd, &st) < 0) {
1428                 perror ("fstat");
1429                 close (fd);
1430                 return;
1431         }
1432         if (st.st_uid != user_id) {
1433                 /* better leave it alone */
1434                 fprintf (stderr, _("%s: warning: %s not owned by %s\n"),
1435                          Prog, mailfile, user_name);
1436                 close (fd);
1437                 return;
1438         }
1439         if (uflg) {
1440                 if (fchown (fd, user_newid, (gid_t) - 1) < 0) {
1441                         perror (_("failed to change mailbox owner"));
1442                 }
1443 #ifdef WITH_AUDIT
1444                 else {
1445                         audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
1446                                       "changing mail file owner", user_newname,
1447                                       user_newid, 1);
1448                 }
1449 #endif
1450         }
1451
1452         close (fd);
1453
1454         if (lflg) {
1455                 snprintf (newmailfile, sizeof newmailfile, "%s/%s",
1456                           maildir, user_newname);
1457                 if (link (mailfile, newmailfile) || unlink (mailfile)) {
1458                         perror (_("failed to rename mailbox"));
1459                 }
1460 #ifdef WITH_AUDIT
1461                 else {
1462                         audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
1463                                       "changing mail file name", user_newname,
1464                                       user_newid, 1);
1465                 }
1466 #endif
1467         }
1468 }
1469 #endif
1470
1471 /*
1472  * main - usermod command
1473  */
1474 int main (int argc, char **argv)
1475 {
1476 #ifdef USE_PAM
1477         pam_handle_t *pamh = NULL;
1478         struct passwd *pampw;
1479         int retval;
1480 #endif
1481
1482 #ifdef WITH_AUDIT
1483         audit_help_open ();
1484 #endif
1485
1486         /*
1487          * Get my name so that I can use it to report errors.
1488          */
1489         Prog = Basename (argv[0]);
1490
1491         setlocale (LC_ALL, "");
1492         bindtextdomain (PACKAGE, LOCALEDIR);
1493         textdomain (PACKAGE);
1494
1495         sys_ngroups = sysconf (_SC_NGROUPS_MAX);
1496         user_groups = malloc ((1 + sys_ngroups) * sizeof (char *));
1497         user_groups[0] = (char *) 0;
1498
1499         OPENLOG ("usermod");
1500
1501         is_shadow_pwd = spw_file_present ();
1502 #ifdef SHADOWGRP
1503         is_shadow_grp = sgr_file_present ();
1504 #endif
1505
1506         process_flags (argc, argv);
1507
1508 #ifdef USE_PAM
1509         retval = PAM_SUCCESS;
1510
1511         pampw = getpwuid (getuid ());
1512         if (pampw == NULL) {
1513                 retval = PAM_USER_UNKNOWN;
1514         }
1515
1516         if (retval == PAM_SUCCESS) {
1517                 retval = pam_start ("usermod", pampw->pw_name, &conv, &pamh);
1518         }
1519
1520         if (retval == PAM_SUCCESS) {
1521                 retval = pam_authenticate (pamh, 0);
1522                 if (retval != PAM_SUCCESS) {
1523                         pam_end (pamh, retval);
1524                 }
1525         }
1526
1527         if (retval == PAM_SUCCESS) {
1528                 retval = pam_acct_mgmt (pamh, 0);
1529                 if (retval != PAM_SUCCESS) {
1530                         pam_end (pamh, retval);
1531                 }
1532         }
1533
1534         if (retval != PAM_SUCCESS) {
1535                 fprintf (stderr, _("%s: PAM authentication failed\n"), Prog);
1536                 exit (1);
1537         }
1538 #endif                          /* USE_PAM */
1539
1540         /*
1541          * Do the hard stuff - open the files, change the user entries,
1542          * change the home directory, then close and update the files.
1543          */
1544         open_files ();
1545         usr_update ();
1546         if (Gflg || lflg)
1547                 grp_update ();
1548         close_files ();
1549
1550         nscd_flush_cache ("passwd");
1551         nscd_flush_cache ("group");
1552
1553         if (mflg)
1554                 move_home ();
1555
1556 #ifndef NO_MOVE_MAILBOX
1557         if (lflg || uflg)
1558                 move_mailbox ();
1559 #endif
1560
1561         if (uflg) {
1562                 update_files ();
1563
1564                 /*
1565                  * Change the UID on all of the files owned by `user_id' to
1566                  * `user_newid' in the user's home directory.
1567                  */
1568                 chown_tree (dflg ? user_newhome : user_home,
1569                             user_id, user_newid,
1570                             user_gid, gflg ? user_newgid : user_gid);
1571         }
1572
1573 #ifdef USE_PAM
1574         if (retval == PAM_SUCCESS)
1575                 pam_end (pamh, PAM_SUCCESS);
1576 #endif                          /* USE_PAM */
1577
1578         exit (E_SUCCESS);
1579         /* NOT REACHED */
1580 }