]> granicus.if.org Git - shadow/blob - src/usermod.c
* src/gpasswd.c: Remove log_gpasswd_success_gshadow(). Writing in
[shadow] / src / usermod.c
1 /*
2  * Copyright (c) 1991 - 1994, Julianne Frances Haugh
3  * Copyright (c) 1996 - 2000, Marek Michałkiewicz
4  * Copyright (c) 2000 - 2006, Tomasz Kłoczko
5  * Copyright (c) 2007 - 2011, Nicolas François
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. The name of the copyright holders or contributors may not be used to
17  *    endorse or promote products derived from this software without
18  *    specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
23  * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
24  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include <config.h>
34
35 #ident "$Id$"
36
37 #include <ctype.h>
38 #include <errno.h>
39 #include <fcntl.h>
40 #include <getopt.h>
41 #include <grp.h>
42 #include <lastlog.h>
43 #include <pwd.h>
44 #ifdef ACCT_TOOLS_SETUID
45 #ifdef USE_PAM
46 #include "pam_defs.h"
47 #endif                          /* USE_PAM */
48 #endif                          /* ACCT_TOOLS_SETUID */
49 #include <stdio.h>
50 #include <sys/stat.h>
51 #include <sys/types.h>
52 #include <time.h>
53 #include "chkname.h"
54 #include "defines.h"
55 #include "faillog.h"
56 #include "getdef.h"
57 #include "groupio.h"
58 #include "nscd.h"
59 #include "prototypes.h"
60 #include "pwauth.h"
61 #include "pwio.h"
62 #ifdef  SHADOWGRP
63 #include "sgroupio.h"
64 #endif
65 #include "shadowio.h"
66 #ifdef WITH_TCB
67 #include "tcbfuncs.h"
68 #endif
69
70 /*
71  * exit status values
72  * for E_GRP_UPDATE and E_NOSPACE (not used yet), other update requests
73  * will be implemented (as documented in the Solaris 2.x man page).
74  */
75 /*@-exitarg@*/
76 #define E_SUCCESS       0       /* success */
77 #define E_PW_UPDATE     1       /* can't update password file */
78 #define E_USAGE         2       /* invalid command syntax */
79 #define E_BAD_ARG       3       /* invalid argument to option */
80 #define E_UID_IN_USE    4       /* UID already in use (and no -o) */
81 /* #define E_BAD_PWFILE 5          passwd file contains errors */
82 #define E_NOTFOUND      6       /* specified user/group doesn't exist */
83 #define E_USER_BUSY     8       /* user to modify is logged in */
84 #define E_NAME_IN_USE   9       /* username already in use */
85 #define E_GRP_UPDATE    10      /* can't update group file */
86 /* #define E_NOSPACE    11         insufficient space to move home dir */
87 #define E_HOMEDIR       12      /* unable to complete home dir move */
88 #define VALID(s)        (strcspn (s, ":\n") == strlen (s))
89 /*
90  * Global variables
91  */
92 const char *Prog;
93
94 static char *user_name;
95 static char *user_newname;
96 static char *user_pass;
97 static uid_t user_id;
98 static uid_t user_newid;
99 static gid_t user_gid;
100 static gid_t user_newgid;
101 static char *user_comment;
102 static char *user_newcomment;
103 static char *user_home;
104 static char *user_newhome;
105 static char *user_shell;
106 #ifdef WITH_SELINUX
107 static const char *user_selinux = "";
108 #endif
109 static char *user_newshell;
110 static long user_expire;
111 static long user_newexpire;
112 static long user_inactive;
113 static long user_newinactive;
114 static long sys_ngroups;
115 static char **user_groups;      /* NULL-terminated list */
116
117 static bool
118     aflg = false,               /* append to existing secondary group set */
119     cflg = false,               /* new comment (GECOS) field */
120     dflg = false,               /* new home directory */
121     eflg = false,               /* days since 1970-01-01 when account becomes expired */
122     fflg = false,               /* days until account with expired password is locked */
123     gflg = false,               /* new primary group ID */
124     Gflg = false,               /* new secondary group set */
125     Lflg = false,               /* lock the password */
126     lflg = false,               /* new user name */
127     mflg = false,               /* create user's home directory if it doesn't exist */
128     oflg = false,               /* permit non-unique user ID to be specified with -u */
129     pflg = false,               /* new encrypted password */
130     sflg = false,               /* new shell program */
131 #ifdef WITH_SELINUX
132     Zflg = false,               /* new selinux user */
133 #endif
134     uflg = false,               /* specify new user ID */
135     Uflg = false;               /* unlock the password */
136
137 static bool is_shadow_pwd;
138
139 #ifdef SHADOWGRP
140 static bool is_shadow_grp;
141 #endif
142
143 static bool pw_locked  = false;
144 static bool spw_locked = false;
145 static bool gr_locked  = false;
146 #ifdef SHADOWGRP
147 static bool sgr_locked = false;
148 #endif
149
150
151 /* local function prototypes */
152 static void date_to_str (/*@unique@*//*@out@*/char *buf, size_t maxsize,
153                          long int date, const char *negativ);
154 static int get_groups (char *);
155 static /*@noreturn@*/void usage (int status);
156 static void new_pwent (struct passwd *);
157 #ifdef WITH_SELINUX
158 static void selinux_update_mapping (void);
159 #endif
160
161 static void new_spent (struct spwd *);
162 static /*@noreturn@*/void fail_exit (int);
163 static void update_group (void);
164
165 #ifdef SHADOWGRP
166 static void update_gshadow (void);
167 #endif
168 static void grp_update (void);
169
170 static void process_flags (int, char **);
171 static void close_files (void);
172 static void open_files (void);
173 static void usr_update (void);
174 static void move_home (void);
175 static void update_lastlog (void);
176 static void update_faillog (void);
177
178 #ifndef NO_MOVE_MAILBOX
179 static void move_mailbox (void);
180 #endif
181
182 static void date_to_str (/*@unique@*//*@out@*/char *buf, size_t maxsize,
183                          long int date, const char *negativ)
184 {
185         struct tm *tp;
186
187         if ((negativ != NULL) && (date < 0)) {
188                 strncpy (buf, negativ, maxsize);
189         } else {
190                 time_t t = (time_t) date;
191                 tp = gmtime (&t);
192 #ifdef HAVE_STRFTIME
193                 strftime (buf, maxsize, "%Y-%m-%d", tp);
194 #else
195                 (void) snprintf (buf, maxsize, "%04d-%02d-%02d",
196                                  tp->tm_year + 1900,
197                                  tp->tm_mon + 1,
198                                  tp->tm_mday);
199 #endif                          /* HAVE_STRFTIME */
200         }
201         buf[maxsize - 1] = '\0';
202 }
203
204 /*
205  * get_groups - convert a list of group names to an array of group IDs
206  *
207  *      get_groups() takes a comma-separated list of group names and
208  *      converts it to a NULL-terminated array. Any unknown group names are
209  *      reported as errors.
210  */
211 static int get_groups (char *list)
212 {
213         char *cp;
214         const struct group *grp;
215         int errors = 0;
216         int ngroups = 0;
217
218         /*
219          * Initialize the list to be empty
220          */
221         user_groups[0] = (char *) 0;
222
223         if ('\0' == *list) {
224                 return 0;
225         }
226
227         /*
228          * So long as there is some data to be converted, strip off each
229          * name and look it up. A mix of numerical and string values for
230          * group identifiers is permitted.
231          */
232         do {
233                 /*
234                  * Strip off a single name from the list
235                  */
236                 cp = strchr (list, ',');
237                 if (NULL != cp) {
238                         *cp = '\0';
239                         cp++;
240                 }
241
242                 /*
243                  * Names starting with digits are treated as numerical GID
244                  * values, otherwise the string is looked up as is.
245                  */
246                 grp = getgr_nam_gid (list);
247
248                 /*
249                  * There must be a match, either by GID value or by
250                  * string name.
251                  */
252                 if (NULL == grp) {
253                         fprintf (stderr, _("%s: group '%s' does not exist\n"),
254                                  Prog, list);
255                         errors++;
256                 }
257                 list = cp;
258
259                 /*
260                  * If the group doesn't exist, don't dump core. Instead,
261                  * try the next one.  --marekm
262                  */
263                 if (NULL == grp) {
264                         continue;
265                 }
266
267 #ifdef  USE_NIS
268                 /*
269                  * Don't add this group if they are an NIS group. Tell the
270                  * user to go to the server for this group.
271                  */
272                 if (__isgrNIS ()) {
273                         fprintf (stderr,
274                                  _("%s: group '%s' is a NIS group.\n"),
275                                  Prog, grp->gr_name);
276                         gr_free (grp);
277                         continue;
278                 }
279 #endif
280
281                 if (ngroups == sys_ngroups) {
282                         fprintf (stderr,
283                                  _("%s: too many groups specified (max %d).\n"),
284                                  Prog, ngroups);
285                         gr_free (grp);
286                         break;
287                 }
288
289                 /*
290                  * Add the group name to the user's list of groups.
291                  */
292                 user_groups[ngroups++] = xstrdup (grp->gr_name);
293                 gr_free (grp);
294         } while (NULL != list);
295
296         user_groups[ngroups] = (char *) 0;
297
298         /*
299          * Any errors in finding group names are fatal
300          */
301         if (0 != errors) {
302                 return -1;
303         }
304
305         return 0;
306 }
307
308 /*
309  * usage - display usage message and exit
310  */
311 static /*@noreturn@*/void usage (int status)
312 {
313         fprintf ((E_SUCCESS != status) ? stderr : stdout,
314                  _("Usage: usermod [options] LOGIN\n"
315                  "\n"
316                  "Options:\n"
317                  "  -c, --comment COMMENT         new value of the GECOS field\n"
318                  "  -d, --home HOME_DIR           new home directory for the user account\n"
319                  "  -e, --expiredate EXPIRE_DATE  set account expiration date to EXPIRE_DATE\n"
320                  "  -f, --inactive INACTIVE       set password inactive after expiration\n"
321                  "                                to INACTIVE\n"
322                  "  -g, --gid GROUP               force use GROUP as new primary group\n"
323                  "  -G, --groups GROUPS           new list of supplementary GROUPS\n"
324                  "  -a, --append                  append the user to the supplemental GROUPS\n"
325                  "                                mentioned by the -G option without removing\n"
326                  "                                him/her from other groups\n"
327                  "  -h, --help                    display this help message and exit\n"
328                  "  -l, --login NEW_LOGIN         new value of the login name\n"
329                  "  -L, --lock                    lock the user account\n"
330                  "  -m, --move-home               move contents of the home directory to the\n"
331                  "                                new location (use only with -d)\n"
332                  "  -o, --non-unique              allow using duplicate (non-unique) UID\n"
333                  "  -p, --password PASSWORD       use encrypted password for the new password\n"
334                  "  -s, --shell SHELL             new login shell for the user account\n"
335                  "  -u, --uid UID                 new UID for the user account\n"
336                  "  -U, --unlock                  unlock the user account\n"
337                  "%s"
338                  "\n"),
339 #ifdef WITH_SELINUX
340                  _("  -Z, --selinux-user            new SELinux user mapping for the user account\n")
341 #else
342                  ""
343 #endif
344                  );
345         exit (status);
346 }
347
348 /*
349  * update encrypted password string (for both shadow and non-shadow
350  * passwords)
351  */
352 static char *new_pw_passwd (char *pw_pass)
353 {
354         if (Lflg && ('!' != pw_pass[0])) {
355                 char *buf = xmalloc (strlen (pw_pass) + 2);
356
357 #ifdef WITH_AUDIT
358                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
359                               "updating passwd",
360                               user_newname, (unsigned int) user_newid, 0);
361 #endif
362                 SYSLOG ((LOG_INFO, "lock user '%s' password", user_newname));
363                 strcpy (buf, "!");
364                 strcat (buf, pw_pass);
365                 pw_pass = buf;
366         } else if (Uflg && pw_pass[0] == '!') {
367                 char *s;
368
369                 if (pw_pass[1] == '\0') {
370                         fprintf (stderr,
371                                  _("%s: unlocking the user's password would result in a passwordless account.\n"
372                                    "You should set a password with usermod -p to unlock this user's password.\n"),
373                                  Prog);
374                         return pw_pass;
375                 }
376
377 #ifdef WITH_AUDIT
378                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
379                               "updating password",
380                               user_newname, (unsigned int) user_newid, 0);
381 #endif
382                 SYSLOG ((LOG_INFO, "unlock user '%s' password", user_newname));
383                 s = pw_pass;
384                 while ('\0' != *s) {
385                         *s = *(s + 1);
386                         s++;
387                 }
388         } else if (pflg) {
389 #ifdef WITH_AUDIT
390                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
391                               "changing password",
392                               user_newname, (unsigned int) user_newid, 1);
393 #endif
394                 SYSLOG ((LOG_INFO, "change user '%s' password", user_newname));
395                 pw_pass = xstrdup (user_pass);
396         }
397         return pw_pass;
398 }
399
400 /*
401  * new_pwent - initialize the values in a password file entry
402  *
403  *      new_pwent() takes all of the values that have been entered and fills
404  *      in a (struct passwd) with them.
405  */
406 static void new_pwent (struct passwd *pwent)
407 {
408         if (lflg) {
409                 if (pw_locate (user_newname) != NULL) {
410                         /* This should never happen.
411                          * It was already checked that the user doesn't
412                          * exist on the system.
413                          */
414                         fprintf (stderr,
415                                  _("%s: user '%s' already exists in %s\n"),
416                                  Prog, user_newname, pw_dbname ());
417                         fail_exit (E_NAME_IN_USE);
418                 }
419 #ifdef WITH_AUDIT
420                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
421                               "changing name",
422                               user_newname, (unsigned int) user_newid, 1);
423 #endif
424                 SYSLOG ((LOG_INFO,
425                          "change user name '%s' to '%s'",
426                          pwent->pw_name, user_newname));
427                 pwent->pw_name = xstrdup (user_newname);
428         }
429         /* Update the password in passwd if there is no shadow file or if
430          * the password is currently in passwd (pw_passwd != "x").
431          * We do not force the usage of shadow passwords if they are not
432          * used for this account.
433          */
434         if (   (!is_shadow_pwd)
435             || (strcmp (pwent->pw_passwd, SHADOW_PASSWD_STRING) != 0)) {
436                 pwent->pw_passwd = new_pw_passwd (pwent->pw_passwd);
437         }
438
439         if (uflg) {
440 #ifdef WITH_AUDIT
441                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
442                               "changing uid",
443                               user_newname, (unsigned int) user_newid, 1);
444 #endif
445                 SYSLOG ((LOG_INFO,
446                          "change user '%s' UID from '%d' to '%d'",
447                          pwent->pw_name, pwent->pw_uid, user_newid));
448                 pwent->pw_uid = user_newid;
449         }
450         if (gflg) {
451 #ifdef WITH_AUDIT
452                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
453                               "changing primary group",
454                               user_newname, (unsigned int) user_newid, 1);
455 #endif
456                 SYSLOG ((LOG_INFO,
457                          "change user '%s' GID from '%d' to '%d'",
458                          pwent->pw_name, pwent->pw_gid, user_newgid));
459                 pwent->pw_gid = user_newgid;
460         }
461         if (cflg) {
462 #ifdef WITH_AUDIT
463                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
464                               "changing comment",
465                               user_newname, (unsigned int) user_newid, 1);
466 #endif
467                 pwent->pw_gecos = user_newcomment;
468         }
469
470         if (dflg) {
471 #ifdef WITH_AUDIT
472                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
473                               "changing home directory",
474                               user_newname, (unsigned int) user_newid, 1);
475 #endif
476                 SYSLOG ((LOG_INFO,
477                          "change user '%s' home from '%s' to '%s'",
478                          pwent->pw_name, pwent->pw_dir, user_newhome));
479                 pwent->pw_dir = user_newhome;
480         }
481         if (sflg) {
482 #ifdef WITH_AUDIT
483                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
484                               "changing user shell",
485                               user_newname, (unsigned int) user_newid, 1);
486 #endif
487                 SYSLOG ((LOG_INFO,
488                          "change user '%s' shell from '%s' to '%s'",
489                          pwent->pw_name, pwent->pw_shell, user_newshell));
490                 pwent->pw_shell = user_newshell;
491         }
492 }
493
494 /*
495  * new_spent - initialize the values in a shadow password file entry
496  *
497  *      new_spent() takes all of the values that have been entered and fills
498  *      in a (struct spwd) with them.
499  */
500 static void new_spent (struct spwd *spent)
501 {
502         if (lflg) {
503                 if (spw_locate (user_newname) != NULL) {
504                         fprintf (stderr,
505                                  _("%s: user '%s' already exists in %s\n"),
506                                  Prog, user_newname, spw_dbname ());
507                         fail_exit (E_NAME_IN_USE);
508                 }
509                 spent->sp_namp = xstrdup (user_newname);
510         }
511
512         if (fflg) {
513 #ifdef WITH_AUDIT
514                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
515                               "changing inactive days",
516                               user_newname, (unsigned int) user_newid, 1);
517 #endif
518                 SYSLOG ((LOG_INFO,
519                          "change user '%s' inactive from '%ld' to '%ld'",
520                          spent->sp_namp, spent->sp_inact, user_newinactive));
521                 spent->sp_inact = user_newinactive;
522         }
523         if (eflg) {
524                 /* log dates rather than numbers of days. */
525                 char new_exp[16], old_exp[16];
526                 date_to_str (new_exp, sizeof(new_exp),
527                              user_newexpire * DAY, "never");
528                 date_to_str (old_exp, sizeof(old_exp),
529                              user_expire * DAY, "never");
530 #ifdef WITH_AUDIT
531                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
532                               "changing expiration date",
533                               user_newname, (unsigned int) user_newid, 1);
534 #endif
535                 SYSLOG ((LOG_INFO,
536                          "change user '%s' expiration from '%s' to '%s'",
537                          spent->sp_namp, old_exp, new_exp));
538                 spent->sp_expire = user_newexpire;
539         }
540
541         /* Always update the shadowed password if there is a shadow entry
542          * (even if shadowed passwords might not be enabled for this
543          * account (pw_passwd != "x")).
544          * It seems better to update the password in both places in case a
545          * shadow and a non shadow entry exist.
546          * This might occur if:
547          *  + there were already both entries
548          *  + aging has been requested
549          */
550         spent->sp_pwdp = new_pw_passwd (spent->sp_pwdp);
551
552         if (pflg) {
553                 spent->sp_lstchg = (long) time ((time_t *) 0) / SCALE;
554                 if (0 == spent->sp_lstchg) {
555                         /* Better disable aging than requiring a password
556                          * change. */
557                         spent->sp_lstchg = -1;
558                 }
559         }
560 }
561
562 /*
563  * fail_exit - exit with an error code after unlocking files
564  */
565 static /*@noreturn@*/void fail_exit (int code)
566 {
567         if (gr_locked) {
568                 if (gr_unlock () == 0) {
569                         fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ());
570                         SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ()));
571                         /* continue */
572                 }
573         }
574 #ifdef  SHADOWGRP
575         if (sgr_locked) {
576                 if (sgr_unlock () == 0) {
577                         fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ());
578                         SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ()));
579                         /* continue */
580                 }
581         }
582 #endif
583         if (spw_locked) {
584                 if (spw_unlock () == 0) {
585                         fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ());
586                         SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ()));
587                         /* continue */
588                 }
589         }
590         if (pw_locked) {
591                 if (pw_unlock () == 0) {
592                         fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ());
593                         SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ()));
594                         /* continue */
595                 }
596         }
597
598 #ifdef WITH_AUDIT
599         audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
600                       "modifying account",
601                       user_name, AUDIT_NO_ID, 0);
602 #endif
603         exit (code);
604 }
605
606
607 static void update_group (void)
608 {
609         bool is_member;
610         bool was_member;
611         bool changed;
612         const struct group *grp;
613         struct group *ngrp;
614
615         changed = false;
616
617         /*
618          * Scan through the entire group file looking for the groups that
619          * the user is a member of.
620          */
621         while ((grp = gr_next ()) != NULL) {
622                 /*
623                  * See if the user specified this group as one of their
624                  * concurrent groups.
625                  */
626                 was_member = is_on_list (grp->gr_mem, user_name);
627                 is_member = Gflg && (   (was_member && aflg)
628                                      || is_on_list (user_groups, grp->gr_name));
629
630                 if (!was_member && !is_member) {
631                         continue;
632                 }
633
634                 ngrp = __gr_dup (grp);
635                 if (NULL == ngrp) {
636                         fprintf (stderr,
637                                  _("%s: Out of memory. Cannot update %s.\n"),
638                                  Prog, gr_dbname ());
639                         fail_exit (E_GRP_UPDATE);
640                 }
641
642                 if (was_member) {
643                         if ((!Gflg) || is_member) {
644                                 /* User was a member and is still a member
645                                  * of this group.
646                                  * But the user might have been renamed.
647                                  */
648                                 if (lflg) {
649                                         ngrp->gr_mem = del_list (ngrp->gr_mem,
650                                                                  user_name);
651                                         ngrp->gr_mem = add_list (ngrp->gr_mem,
652                                                                  user_newname);
653                                         changed = true;
654 #ifdef WITH_AUDIT
655                                         audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
656                                                       "changing group member",
657                                                       user_newname, AUDIT_NO_ID, 1);
658 #endif
659                                         SYSLOG ((LOG_INFO,
660                                                  "change '%s' to '%s' in group '%s'",
661                                                  user_name, user_newname,
662                                                  ngrp->gr_name));
663                                 }
664                         } else {
665                                 /* User was a member but is no more a
666                                  * member of this group.
667                                  */
668                                 ngrp->gr_mem = del_list (ngrp->gr_mem, user_name);
669                                 changed = true;
670 #ifdef WITH_AUDIT
671                                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
672                                               "removing group member",
673                                               user_name, AUDIT_NO_ID, 1);
674 #endif
675                                 SYSLOG ((LOG_INFO,
676                                          "delete '%s' from group '%s'",
677                                          user_name, ngrp->gr_name));
678                         }
679                 } else {
680                         /* User was not a member but is now a member this
681                          * group.
682                          */
683                         ngrp->gr_mem = add_list (ngrp->gr_mem, user_newname);
684                         changed = true;
685 #ifdef WITH_AUDIT
686                         audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
687                                       "adding user to group",
688                                       user_name, AUDIT_NO_ID, 1);
689 #endif
690                         SYSLOG ((LOG_INFO, "add '%s' to group '%s'",
691                                  user_newname, ngrp->gr_name));
692                 }
693                 if (!changed) {
694                         continue;
695                 }
696
697                 changed = false;
698                 if (gr_update (ngrp) == 0) {
699                         fprintf (stderr,
700                                  _("%s: failed to prepare the new %s entry '%s'\n"),
701                                  Prog, gr_dbname (), ngrp->gr_name);
702                         SYSLOG ((LOG_WARN, "failed to prepare the new %s entry '%s'", gr_dbname (), ngrp->gr_name));
703                         fail_exit (E_GRP_UPDATE);
704                 }
705         }
706 }
707
708 #ifdef SHADOWGRP
709 static void update_gshadow (void)
710 {
711         bool is_member;
712         bool was_member;
713         bool was_admin;
714         bool changed;
715         const struct sgrp *sgrp;
716         struct sgrp *nsgrp;
717
718         changed = false;
719
720         /*
721          * Scan through the entire shadow group file looking for the groups
722          * that the user is a member of.
723          */
724         while ((sgrp = sgr_next ()) != NULL) {
725
726                 /*
727                  * See if the user was a member of this group
728                  */
729                 was_member = is_on_list (sgrp->sg_mem, user_name);
730
731                 /*
732                  * See if the user was an administrator of this group
733                  */
734                 was_admin = is_on_list (sgrp->sg_adm, user_name);
735
736                 /*
737                  * See if the user specified this group as one of their
738                  * concurrent groups.
739                  */
740                 is_member = Gflg && (   (was_member && aflg)
741                                      || is_on_list (user_groups, sgrp->sg_name));
742
743                 if (!was_member && !was_admin && !is_member) {
744                         continue;
745                 }
746
747                 nsgrp = __sgr_dup (sgrp);
748                 if (NULL == nsgrp) {
749                         fprintf (stderr,
750                                  _("%s: Out of memory. Cannot update %s.\n"),
751                                  Prog, sgr_dbname ());
752                         fail_exit (E_GRP_UPDATE);
753                 }
754
755                 if (was_admin && lflg) {
756                         /* User was an admin of this group but the user
757                          * has been renamed.
758                          */
759                         nsgrp->sg_adm = del_list (nsgrp->sg_adm, user_name);
760                         nsgrp->sg_adm = add_list (nsgrp->sg_adm, user_newname);
761                         changed = true;
762 #ifdef WITH_AUDIT
763                         audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
764                                       "changing admin name in shadow group",
765                                       user_name, AUDIT_NO_ID, 1);
766 #endif
767                         SYSLOG ((LOG_INFO,
768                                  "change admin '%s' to '%s' in shadow group '%s'",
769                                  user_name, user_newname, nsgrp->sg_name));
770                 }
771
772                 if (was_member) {
773                         if ((!Gflg) || is_member) {
774                                 /* User was a member and is still a member
775                                  * of this group.
776                                  * But the user might have been renamed.
777                                  */
778                                 if (lflg) {
779                                         nsgrp->sg_mem = del_list (nsgrp->sg_mem,
780                                                                   user_name);
781                                         nsgrp->sg_mem = add_list (nsgrp->sg_mem,
782                                                                   user_newname);
783                                         changed = true;
784 #ifdef WITH_AUDIT
785                                         audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
786                                                       "changing member in shadow group",
787                                                       user_name, AUDIT_NO_ID, 1);
788 #endif
789                                         SYSLOG ((LOG_INFO,
790                                                  "change '%s' to '%s' in shadow group '%s'",
791                                                  user_name, user_newname,
792                                                  nsgrp->sg_name));
793                                 }
794                         } else {
795                                 /* User was a member but is no more a
796                                  * member of this group.
797                                  */
798                                 nsgrp->sg_mem = del_list (nsgrp->sg_mem, user_name);
799                                 changed = true;
800 #ifdef WITH_AUDIT
801                                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
802                                               "removing user from shadow group",
803                                               user_name, AUDIT_NO_ID, 1);
804 #endif
805                                 SYSLOG ((LOG_INFO,
806                                          "delete '%s' from shadow group '%s'",
807                                          user_name, nsgrp->sg_name));
808                         }
809                 } else if (is_member) {
810                         /* User was not a member but is now a member this
811                          * group.
812                          */
813                         nsgrp->sg_mem = add_list (nsgrp->sg_mem, user_newname);
814                         changed = true;
815 #ifdef WITH_AUDIT
816                         audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
817                                       "adding user to shadow group",
818                                       user_newname, AUDIT_NO_ID, 1);
819 #endif
820                         SYSLOG ((LOG_INFO, "add '%s' to shadow group '%s'",
821                                  user_newname, nsgrp->sg_name));
822                 }
823                 if (!changed) {
824                         continue;
825                 }
826
827                 changed = false;
828
829                 /* 
830                  * Update the group entry to reflect the changes.
831                  */
832                 if (sgr_update (nsgrp) == 0) {
833                         fprintf (stderr,
834                                  _("%s: failed to prepare the new %s entry '%s'\n"),
835                                  Prog, sgr_dbname (), nsgrp->sg_name);
836                         SYSLOG ((LOG_WARN, "failed to prepare the new %s entry '%s'",
837                                  sgr_dbname (), nsgrp->sg_name));
838                         fail_exit (E_GRP_UPDATE);
839                 }
840         }
841 }
842 #endif                          /* SHADOWGRP */
843
844 /*
845  * grp_update - add user to secondary group set
846  *
847  *      grp_update() takes the secondary group set given in user_groups and
848  *      adds the user to each group given by that set.
849  */
850 static void grp_update (void)
851 {
852         update_group ();
853 #ifdef SHADOWGRP
854         if (is_shadow_grp) {
855                 update_gshadow ();
856         }
857 #endif
858 }
859
860 /*
861  * process_flags - perform command line argument setting
862  *
863  *      process_flags() interprets the command line arguments and sets the
864  *      values that the user will be created with accordingly. The values
865  *      are checked for sanity.
866  */
867 static void process_flags (int argc, char **argv)
868 {
869         const struct group *grp;
870
871         bool anyflag = false;
872
873         {
874                 /*
875                  * Parse the command line options.
876                  */
877                 int c;
878                 static struct option long_options[] = {
879                         {"append", no_argument, NULL, 'a'},
880                         {"comment", required_argument, NULL, 'c'},
881                         {"home", required_argument, NULL, 'd'},
882                         {"expiredate", required_argument, NULL, 'e'},
883                         {"inactive", required_argument, NULL, 'f'},
884                         {"gid", required_argument, NULL, 'g'},
885                         {"groups", required_argument, NULL, 'G'},
886                         {"help", no_argument, NULL, 'h'},
887                         {"login", required_argument, NULL, 'l'},
888                         {"lock", no_argument, NULL, 'L'},
889                         {"move-home", no_argument, NULL, 'm'},
890                         {"non-unique", no_argument, NULL, 'o'},
891                         {"password", required_argument, NULL, 'p'},
892 #ifdef WITH_SELINUX
893                         {"selinux-user", required_argument, NULL, 'Z'},
894 #endif
895                         {"shell", required_argument, NULL, 's'},
896                         {"uid", required_argument, NULL, 'u'},
897                         {"unlock", no_argument, NULL, 'U'},
898                         {NULL, 0, NULL, '\0'}
899                 };
900                 while ((c = getopt_long (argc, argv,
901 #ifdef WITH_SELINUX
902                                          "ac:d:e:f:g:G:hl:Lmop:s:u:UZ:",
903 #else
904                                          "ac:d:e:f:g:G:hl:Lmop:s:u:U",
905 #endif
906                                          long_options, NULL)) != -1) {
907                         switch (c) {
908                         case 'a':
909                                 aflg = true;
910                                 break;
911                         case 'c':
912                                 if (!VALID (optarg)) {
913                                         fprintf (stderr,
914                                                  _("%s: invalid field '%s'\n"),
915                                                  Prog, optarg);
916                                         exit (E_BAD_ARG);
917                                 }
918                                 user_newcomment = optarg;
919                                 cflg = true;
920                                 break;
921                         case 'd':
922                                 if (!VALID (optarg)) {
923                                         fprintf (stderr,
924                                                  _("%s: invalid field '%s'\n"),
925                                                  Prog, optarg);
926                                         exit (E_BAD_ARG);
927                                 }
928                                 dflg = true;
929                                 user_newhome = optarg;
930                                 break;
931                         case 'e':
932                                 if ('\0' != *optarg) {
933                                         user_newexpire = strtoday (optarg);
934                                         if (user_newexpire < -1) {
935                                                 fprintf (stderr,
936                                                          _("%s: invalid date '%s'\n"),
937                                                          Prog, optarg);
938                                                 exit (E_BAD_ARG);
939                                         }
940                                         user_newexpire *= DAY / SCALE;
941                                 } else {
942                                         user_newexpire = -1;
943                                 }
944                                 eflg = true;
945                                 break;
946                         case 'f':
947                                 if (   (getlong (optarg, &user_newinactive) == 0)
948                                     || (user_newinactive < -1)) {
949                                         fprintf (stderr,
950                                                  _("%s: invalid numeric argument '%s'\n"),
951                                                  Prog, optarg);
952                                         exit (E_BAD_ARG);
953                                 }
954                                 fflg = true;
955                                 break;
956                         case 'g':
957                                 grp = getgr_nam_gid (optarg);
958                                 if (NULL == grp) {
959                                         fprintf (stderr,
960                                                  _("%s: group '%s' does not exist\n"),
961                                                  Prog, optarg);
962                                         exit (E_NOTFOUND);
963                                 }
964                                 user_newgid = grp->gr_gid;
965                                 gflg = true;
966                                 break;
967                         case 'G':
968                                 if (get_groups (optarg) != 0) {
969                                         exit (E_NOTFOUND);
970                                 }
971                                 Gflg = true;
972                                 break;
973                         case 'h':
974                                 usage (E_SUCCESS);
975                                 /*@notreached@*/break;
976                         case 'l':
977                                 if (!is_valid_user_name (optarg)) {
978                                         fprintf (stderr,
979                                                  _("%s: invalid user name '%s'\n"),
980                                                  Prog, optarg);
981                                         exit (E_BAD_ARG);
982                                 }
983                                 lflg = true;
984                                 user_newname = optarg;
985                                 break;
986                         case 'L':
987                                 Lflg = true;
988                                 break;
989                         case 'm':
990                                 mflg = true;
991                                 break;
992                         case 'o':
993                                 oflg = true;
994                                 break;
995                         case 'p':
996                                 user_pass = optarg;
997                                 pflg = true;
998                                 break;
999                         case 's':
1000                                 if (!VALID (optarg)) {
1001                                         fprintf (stderr,
1002                                                  _("%s: invalid field '%s'\n"),
1003                                                  Prog, optarg);
1004                                         exit (E_BAD_ARG);
1005                                 }
1006                                 user_newshell = optarg;
1007                                 sflg = true;
1008                                 break;
1009                         case 'u':
1010                                 if (   (get_uid (optarg, &user_newid) ==0)
1011                                     || (user_newid == (uid_t)-1)) {
1012                                         fprintf (stderr,
1013                                                  _("%s: invalid user ID '%s'\n"),
1014                                                  Prog, optarg);
1015                                         exit (E_BAD_ARG);
1016                                 }
1017                                 uflg = true;
1018                                 break;
1019                         case 'U':
1020                                 Uflg = true;
1021                                 break;
1022 #ifdef WITH_SELINUX
1023                         case 'Z':
1024                                 if (is_selinux_enabled () > 0) {
1025                                         user_selinux = optarg;
1026                                         Zflg = true;
1027                                 } else {
1028                                         fprintf (stderr,
1029                                                  _("%s: -Z requires SELinux enabled kernel\n"),
1030                                                  Prog);
1031                                         exit (E_BAD_ARG);
1032                                 }
1033                                 break;
1034 #endif
1035                         default:
1036                                 usage (E_USAGE);
1037                         }
1038                         anyflag = true;
1039                 }
1040         }
1041
1042         if (optind != argc - 1) {
1043                 usage (E_USAGE);
1044         }
1045
1046         user_name = argv[argc - 1];
1047
1048         {
1049                 const struct passwd *pwd;
1050                 /* local, no need for xgetpwnam */
1051                 pwd = getpwnam (user_name);
1052                 if (NULL == pwd) {
1053                         fprintf (stderr,
1054                                  _("%s: user '%s' does not exist\n"),
1055                                  Prog, user_name);
1056                         exit (E_NOTFOUND);
1057                 }
1058
1059                 user_id = pwd->pw_uid;
1060                 user_gid = pwd->pw_gid;
1061                 user_comment = xstrdup (pwd->pw_gecos);
1062                 user_home = xstrdup (pwd->pw_dir);
1063                 user_shell = xstrdup (pwd->pw_shell);
1064         }
1065
1066         /* user_newname, user_newid, user_newgid can be used even when the
1067          * options where not specified. */
1068         if (!lflg) {
1069                 user_newname = user_name;
1070         }
1071         if (!uflg) {
1072                 user_newid = user_id;
1073         }
1074         if (!gflg) {
1075                 user_newgid = user_gid;
1076         }
1077
1078 #ifdef  USE_NIS
1079         /*
1080          * Now make sure it isn't an NIS user.
1081          */
1082         if (__ispwNIS ()) {
1083                 char *nis_domain;
1084                 char *nis_master;
1085
1086                 fprintf (stderr,
1087                          _("%s: user %s is a NIS user\n"),
1088                          Prog, user_name);
1089
1090                 if (   !yp_get_default_domain (&nis_domain)
1091                     && !yp_master (nis_domain, "passwd.byname", &nis_master)) {
1092                         fprintf (stderr,
1093                                  _("%s: %s is the NIS master\n"),
1094                                  Prog, nis_master);
1095                 }
1096                 exit (E_NOTFOUND);
1097         }
1098 #endif
1099
1100         {
1101                 const struct spwd *spwd = NULL;
1102                 /* local, no need for xgetspnam */
1103                 if (is_shadow_pwd && ((spwd = getspnam (user_name)) != NULL)) {
1104                         user_expire = spwd->sp_expire;
1105                         user_inactive = spwd->sp_inact;
1106                 }
1107         }
1108
1109         if (!anyflag) {
1110                 fprintf (stderr, _("%s: no options\n"), Prog);
1111                 usage (E_USAGE);
1112         }
1113
1114         if (aflg && (!Gflg)) {
1115                 fprintf (stderr,
1116                          _("%s: %s flag is only allowed with the %s flag\n"),
1117                          Prog, "-a", "-G");
1118                 usage (E_USAGE);
1119         }
1120
1121         if ((Lflg && (pflg || Uflg)) || (pflg && Uflg)) {
1122                 fprintf (stderr,
1123                          _("%s: the -L, -p, and -U flags are exclusive\n"),
1124                          Prog);
1125                 usage (E_USAGE);
1126         }
1127
1128         if (oflg && !uflg) {
1129                 fprintf (stderr,
1130                          _("%s: %s flag is only allowed with the %s flag\n"),
1131                          Prog, "-o", "-u");
1132                 usage (E_USAGE);
1133         }
1134
1135         if (mflg && !dflg) {
1136                 fprintf (stderr,
1137                          _("%s: %s flag is only allowed with the %s flag\n"),
1138                          Prog, "-m", "-d");
1139                 usage (E_USAGE);
1140         }
1141
1142         if (user_newid == user_id) {
1143                 uflg = false;
1144                 oflg = false;
1145         }
1146         if (user_newgid == user_gid) {
1147                 gflg = false;
1148         }
1149         if (   (NULL != user_newshell)
1150             && (strcmp (user_newshell, user_shell) == 0)) {
1151                 sflg = false;
1152         }
1153         if (strcmp (user_newname, user_name) == 0) {
1154                 lflg = false;
1155         }
1156         if (user_newinactive == user_inactive) {
1157                 fflg = false;
1158         }
1159         if (user_newexpire == user_expire) {
1160                 eflg = false;
1161         }
1162         if (   (NULL != user_newhome)
1163             && (strcmp (user_newhome, user_home) == 0)) {
1164                 dflg = false;
1165                 mflg = false;
1166         }
1167         if (   (NULL != user_newcomment)
1168             && (strcmp (user_newcomment, user_comment) == 0)) {
1169                 cflg = false;
1170         }
1171
1172         if (!(Uflg || uflg || sflg || pflg || mflg || Lflg ||
1173               lflg || Gflg || gflg || fflg || eflg || dflg || cflg
1174 #ifdef WITH_SELINUX
1175               || Zflg
1176 #endif
1177         )) {
1178                 fprintf (stderr, _("%s: no changes\n"), Prog);
1179                 exit (E_SUCCESS);
1180         }
1181
1182         if (!is_shadow_pwd && (eflg || fflg)) {
1183                 fprintf (stderr,
1184                          _("%s: shadow passwords required for -e and -f\n"),
1185                          Prog);
1186                 exit (E_USAGE);
1187         }
1188
1189         /* local, no need for xgetpwnam */
1190         if (lflg && (getpwnam (user_newname) != NULL)) {
1191                 fprintf (stderr,
1192                          _("%s: user '%s' already exists\n"),
1193                          Prog, user_newname);
1194                 exit (E_NAME_IN_USE);
1195         }
1196
1197         /* local, no need for xgetpwuid */
1198         if (uflg && !oflg && (getpwuid (user_newid) != NULL)) {
1199                 fprintf (stderr,
1200                          _("%s: UID '%lu' already exists\n"),
1201                          Prog, (unsigned long) user_newid);
1202                 exit (E_UID_IN_USE);
1203         }
1204 }
1205
1206 /*
1207  * close_files - close all of the files that were opened
1208  *
1209  *      close_files() closes all of the files that were opened for this new
1210  *      user. This causes any modified entries to be written out.
1211  */
1212 static void close_files (void)
1213 {
1214         if (pw_close () == 0) {
1215                 fprintf (stderr,
1216                          _("%s: failure while writing changes to %s\n"),
1217                          Prog, pw_dbname ());
1218                 SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ()));
1219                 fail_exit (E_PW_UPDATE);
1220         }
1221         if (is_shadow_pwd && (spw_close () == 0)) {
1222                 fprintf (stderr,
1223                          _("%s: failure while writing changes to %s\n"),
1224                          Prog, spw_dbname ());
1225                 SYSLOG ((LOG_ERR,
1226                          "failure while writing changes to %s",
1227                          spw_dbname ()));
1228                 fail_exit (E_PW_UPDATE);
1229         }
1230
1231         if (Gflg || lflg) {
1232                 if (gr_close () == 0) {
1233                         fprintf (stderr,
1234                                  _("%s: failure while writing changes to %s\n"),
1235                                  Prog, gr_dbname ());
1236                         SYSLOG ((LOG_ERR,
1237                                  "failure while writing changes to %s",
1238                                  gr_dbname ()));
1239                         fail_exit (E_GRP_UPDATE);
1240                 }
1241 #ifdef SHADOWGRP
1242                 if (is_shadow_grp) {
1243                         if (sgr_close () == 0) {
1244                                 fprintf (stderr,
1245                                          _("%s: failure while writing changes to %s\n"),
1246                                          Prog, sgr_dbname ());
1247                                 SYSLOG ((LOG_ERR,
1248                                          "failure while writing changes to %s",
1249                                          sgr_dbname ()));
1250                                 fail_exit (E_GRP_UPDATE);
1251                         }
1252                         if (sgr_unlock () == 0) {
1253                                 fprintf (stderr,
1254                                          _("%s: failed to unlock %s\n"),
1255                                          Prog, sgr_dbname ());
1256                                 SYSLOG ((LOG_ERR,
1257                                          "failed to unlock %s",
1258                                          sgr_dbname ()));
1259                                 /* continue */
1260                         }
1261                 }
1262 #endif
1263                 if (gr_unlock () == 0) {
1264                         fprintf (stderr,
1265                                  _("%s: failed to unlock %s\n"),
1266                                  Prog, gr_dbname ());
1267                         SYSLOG ((LOG_ERR,
1268                                  "failed to unlock %s",
1269                                  gr_dbname ()));
1270                         /* continue */
1271                 }
1272         }
1273
1274         if (is_shadow_pwd) {
1275                 if (spw_unlock () == 0) {
1276                         fprintf (stderr,
1277                                  _("%s: failed to unlock %s\n"),
1278                                  Prog, spw_dbname ());
1279                         SYSLOG ((LOG_ERR,
1280                                  "failed to unlock %s",
1281                                  spw_dbname ()));
1282                         /* continue */
1283                 }
1284         }
1285         if (pw_unlock () == 0) {
1286                 fprintf (stderr,
1287                          _("%s: failed to unlock %s\n"),
1288                          Prog, pw_dbname ());
1289                 SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ()));
1290                 /* continue */
1291         }
1292
1293         pw_locked = false;
1294         spw_locked = false;
1295         gr_locked = false;
1296 #ifdef  SHADOWGRP
1297         sgr_locked = false;
1298 #endif
1299
1300         /*
1301          * Close the DBM and/or flat files
1302          */
1303         endpwent ();
1304         endspent ();
1305         endgrent ();
1306 #ifdef  SHADOWGRP
1307         endsgent ();
1308 #endif
1309 }
1310
1311 /*
1312  * open_files - lock and open the password files
1313  *
1314  *      open_files() opens the two password files.
1315  */
1316 static void open_files (void)
1317 {
1318         if (pw_lock () == 0) {
1319                 fprintf (stderr,
1320                          _("%s: cannot lock %s; try again later.\n"),
1321                          Prog, pw_dbname ());
1322                 fail_exit (E_PW_UPDATE);
1323         }
1324         pw_locked = true;
1325         if (pw_open (O_RDWR) == 0) {
1326                 fprintf (stderr,
1327                          _("%s: cannot open %s\n"),
1328                          Prog, pw_dbname ());
1329                 fail_exit (E_PW_UPDATE);
1330         }
1331         if (is_shadow_pwd && (spw_lock () == 0)) {
1332                 fprintf (stderr,
1333                          _("%s: cannot lock %s; try again later.\n"),
1334                          Prog, spw_dbname ());
1335                 fail_exit (E_PW_UPDATE);
1336         }
1337         spw_locked = true;
1338         if (is_shadow_pwd && (spw_open (O_RDWR) == 0)) {
1339                 fprintf (stderr,
1340                          _("%s: cannot open %s\n"),
1341                          Prog, spw_dbname ());
1342                 fail_exit (E_PW_UPDATE);
1343         }
1344
1345         if (Gflg || lflg) {
1346                 /*
1347                  * Lock and open the group file. This will load all of the
1348                  * group entries.
1349                  */
1350                 if (gr_lock () == 0) {
1351                         fprintf (stderr,
1352                                  _("%s: cannot lock %s; try again later.\n"),
1353                                  Prog, gr_dbname ());
1354                         fail_exit (E_GRP_UPDATE);
1355                 }
1356                 gr_locked = true;
1357                 if (gr_open (O_RDWR) == 0) {
1358                         fprintf (stderr,
1359                                  _("%s: cannot open %s\n"),
1360                                  Prog, gr_dbname ());
1361                         fail_exit (E_GRP_UPDATE);
1362                 }
1363 #ifdef SHADOWGRP
1364                 if (is_shadow_grp && (sgr_lock () == 0)) {
1365                         fprintf (stderr,
1366                                  _("%s: cannot lock %s; try again later.\n"),
1367                                  Prog, sgr_dbname ());
1368                         fail_exit (E_GRP_UPDATE);
1369                 }
1370                 sgr_locked = true;
1371                 if (is_shadow_grp && (sgr_open (O_RDWR) == 0)) {
1372                         fprintf (stderr,
1373                                  _("%s: cannot open %s\n"),
1374                                  Prog, sgr_dbname ());
1375                         fail_exit (E_GRP_UPDATE);
1376                 }
1377 #endif
1378         }
1379 }
1380
1381 /*
1382  * usr_update - create the user entries
1383  *
1384  *      usr_update() creates the password file entries for this user and
1385  *      will update the group entries if required.
1386  */
1387 static void usr_update (void)
1388 {
1389         struct passwd pwent;
1390         const struct passwd *pwd;
1391
1392         struct spwd spent;
1393         const struct spwd *spwd = NULL;
1394
1395         /*
1396          * Locate the entry in /etc/passwd, which MUST exist.
1397          */
1398         pwd = pw_locate (user_name);
1399         if (NULL == pwd) {
1400                 fprintf (stderr,
1401                          _("%s: user '%s' does not exist in %s\n"),
1402                          Prog, user_name, pw_dbname ());
1403                 fail_exit (E_NOTFOUND);
1404         }
1405         pwent = *pwd;
1406         new_pwent (&pwent);
1407
1408
1409         /* If the shadow file does not exist, it won't be created */
1410         if (is_shadow_pwd) {
1411                 spwd = spw_locate (user_name);
1412                 if (NULL != spwd) {
1413                         /* Update the shadow entry if it exists */
1414                         spent = *spwd;
1415                         new_spent (&spent);
1416                 } else if (   (    pflg
1417                                && (strcmp (pwent.pw_passwd, SHADOW_PASSWD_STRING) == 0))
1418                            || eflg || fflg) {
1419                         /* In some cases, we force the creation of a
1420                          * shadow entry:
1421                          *  + new password requested and passwd indicates
1422                          *    a shadowed password
1423                          *  + aging information is requested
1424                          */
1425                         memset (&spent, 0, sizeof spent);
1426                         spent.sp_namp   = user_name;
1427
1428                         /* The user explicitly asked for a shadow feature.
1429                          * Enable shadowed passwords for this new account.
1430                          */
1431                         spent.sp_pwdp   = xstrdup (pwent.pw_passwd);
1432                         pwent.pw_passwd = xstrdup (SHADOW_PASSWD_STRING);
1433
1434                         spent.sp_lstchg = (long) time ((time_t *) 0) / SCALE;
1435                         if (0 == spent.sp_lstchg) {
1436                                 /* Better disable aging than
1437                                  * requiring a password change */
1438                                 spent.sp_lstchg = -1;
1439                         }
1440                         spent.sp_min    = getdef_num ("PASS_MIN_DAYS", -1);
1441                         spent.sp_max    = getdef_num ("PASS_MAX_DAYS", -1);
1442                         spent.sp_warn   = getdef_num ("PASS_WARN_AGE", -1);
1443                         spent.sp_inact  = -1;
1444                         spent.sp_expire = -1;
1445                         spent.sp_flag   = SHADOW_SP_FLAG_UNSET;
1446                         new_spent (&spent);
1447                         spwd = &spent; /* entry needs to be committed */
1448                 }
1449         }
1450
1451         if (lflg || uflg || gflg || cflg || dflg || sflg || pflg
1452             || Lflg || Uflg) {
1453                 if (pw_update (&pwent) == 0) {
1454                         fprintf (stderr,
1455                                  _("%s: failed to prepare the new %s entry '%s'\n"),
1456                                  Prog, pw_dbname (), pwent.pw_name);
1457                         fail_exit (E_PW_UPDATE);
1458                 }
1459                 if (lflg && (pw_remove (user_name) == 0)) {
1460                         fprintf (stderr,
1461                                  _("%s: cannot remove entry '%s' from %s\n"),
1462                                  Prog, user_name, pw_dbname ());
1463                         fail_exit (E_PW_UPDATE);
1464                 }
1465         }
1466         if ((NULL != spwd) && (lflg || eflg || fflg || pflg || Lflg || Uflg)) {
1467                 if (spw_update (&spent) == 0) {
1468                         fprintf (stderr,
1469                                  _("%s: failed to prepare the new %s entry '%s'\n"),
1470                                  Prog, spw_dbname (), spent.sp_namp);
1471                         fail_exit (E_PW_UPDATE);
1472                 }
1473                 if (lflg && (spw_remove (user_name) == 0)) {
1474                         fprintf (stderr,
1475                                  _("%s: cannot remove entry '%s' from %s\n"),
1476                                  Prog, user_name, spw_dbname ());
1477                         fail_exit (E_PW_UPDATE);
1478                 }
1479         }
1480 }
1481
1482 /*
1483  * move_home - move the user's home directory
1484  *
1485  *      move_home() moves the user's home directory to a new location. The
1486  *      files will be copied if the directory cannot simply be renamed.
1487  */
1488 static void move_home (void)
1489 {
1490         struct stat sb;
1491
1492         if (access (user_newhome, F_OK) == 0) {
1493                 /*
1494                  * If the new home directory already exist, the user
1495                  * should not use -m.
1496                  */
1497                 fprintf (stderr,
1498                          _("%s: directory %s exists\n"),
1499                          Prog, user_newhome);
1500                 fail_exit (E_HOMEDIR);
1501         }
1502
1503         if (stat (user_home, &sb) == 0) {
1504                 /*
1505                  * Don't try to move it if it is not a directory
1506                  * (but /dev/null for example).  --marekm
1507                  */
1508                 if (!S_ISDIR (sb.st_mode)) {
1509                         fprintf (stderr,
1510                                  _("%s: The previous home directory (%s) was "
1511                                    "not a directory. It is not removed and no "
1512                                    "home directories are created.\n"),
1513                                  Prog, user_home);
1514                         fail_exit (E_HOMEDIR);
1515                 }
1516
1517                 if (rename (user_home, user_newhome) == 0) {
1518                         /* FIXME: rename above may have broken symlinks
1519                          *        pointing to the user's home directory
1520                          *        with an absolute path. */
1521                         if (chown_tree (user_newhome,
1522                                         user_id,  uflg ? user_newid  : (uid_t)-1,
1523                                         user_gid, gflg ? user_newgid : (gid_t)-1) != 0) {
1524                                 fprintf (stderr,
1525                                          _("%s: Failed to change ownership of the home directory"),
1526                                          Prog);
1527                                 fail_exit (E_HOMEDIR);
1528                         }
1529 #ifdef WITH_AUDIT
1530                         audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
1531                                       "moving home directory",
1532                                       user_newname, (unsigned int) user_newid,
1533                                       1);
1534 #endif
1535                         return;
1536                 } else {
1537                         if (EXDEV == errno) {
1538                                 if (copy_tree (user_home, user_newhome, true,
1539                                                true,
1540                                                user_id,
1541                                                uflg ? user_newid : (uid_t)-1,
1542                                                user_gid,
1543                                                gflg ? user_newgid : (gid_t)-1) == 0) {
1544                                         if (remove_tree (user_home, true) != 0) {
1545                                                 fprintf (stderr,
1546                                                          _("%s: warning: failed to completely remove old home directory %s"),
1547                                                          Prog, user_home);
1548                                         }
1549 #ifdef WITH_AUDIT
1550                                         audit_logger (AUDIT_USER_CHAUTHTOK,
1551                                                       Prog,
1552                                                       "moving home directory",
1553                                                       user_newname,
1554                                                       (unsigned int) user_newid,
1555                                                       1);
1556 #endif
1557                                         return;
1558                                 }
1559
1560                                 (void) remove_tree (user_newhome, true);
1561                         }
1562                         fprintf (stderr,
1563                                  _("%s: cannot rename directory %s to %s\n"),
1564                                  Prog, user_home, user_newhome);
1565                         fail_exit (E_HOMEDIR);
1566                 }
1567         }
1568 }
1569
1570 /*
1571  * update_lastlog - update the lastlog file
1572  *
1573  * Relocate the "lastlog" entries for the user. The old entry is
1574  * left alone in case the UID was shared. It doesn't hurt anything
1575  * to just leave it be.
1576  */
1577 static void update_lastlog (void)
1578 {
1579         struct lastlog ll;
1580         int fd;
1581         off_t off_uid = (off_t) user_id * sizeof ll;
1582         off_t off_newuid = (off_t) user_newid * sizeof ll;
1583
1584         if (access (LASTLOG_FILE, F_OK) != 0) {
1585                 return;
1586         }
1587
1588         fd = open (LASTLOG_FILE, O_RDWR);
1589
1590         if (-1 == fd) {
1591                 fprintf (stderr,
1592                          _("%s: failed to copy the lastlog entry of user %lu to user %lu: %s\n"),
1593                          Prog, (unsigned long) user_id, (unsigned long) user_newid, strerror (errno));
1594                 return;
1595         }
1596
1597         if (   (lseek (fd, off_uid, SEEK_SET) == off_uid)
1598             && (read (fd, &ll, sizeof ll) == (ssize_t) sizeof ll)) {
1599                 /* Copy the old entry to its new location */
1600                 if (   (lseek (fd, off_newuid, SEEK_SET) != off_newuid)
1601                     || (write (fd, &ll, sizeof ll) != (ssize_t) sizeof ll)
1602                     || (fsync (fd) != 0)
1603                     || (close (fd) != 0)) {
1604                         fprintf (stderr,
1605                                  _("%s: failed to copy the lastlog entry of user %lu to user %lu: %s\n"),
1606                                  Prog, (unsigned long) user_id, (unsigned long) user_newid, strerror (errno));
1607                 }
1608         } else {
1609                 /* Assume lseek or read failed because there is
1610                  * no entry for the old UID */
1611
1612                 /* Check if the new UID already has an entry */
1613                 if (   (lseek (fd, off_newuid, SEEK_SET) == off_newuid)
1614                     && (read (fd, &ll, sizeof ll) == (ssize_t) sizeof ll)) {
1615                         /* Reset the new uid's lastlog entry */
1616                         memzero (&ll, sizeof (ll));
1617                         if (   (lseek (fd, off_newuid, SEEK_SET) != off_newuid)
1618                             || (write (fd, &ll, sizeof ll) != (ssize_t) sizeof ll)
1619                             || (fsync (fd) != 0)
1620                             || (close (fd) != 0)) {
1621                                 fprintf (stderr,
1622                                          _("%s: failed to copy the lastlog entry of user %lu to user %lu: %s\n"),
1623                                          Prog, (unsigned long) user_id, (unsigned long) user_newid, strerror (errno));
1624                         }
1625                 } else {
1626                         (void) close (fd);
1627                 }
1628         }
1629 }
1630
1631 /*
1632  * update_faillog - update the faillog file
1633  *
1634  * Relocate the "faillog" entries for the user. The old entry is
1635  * left alone in case the UID was shared. It doesn't hurt anything
1636  * to just leave it be.
1637  */
1638 static void update_faillog (void)
1639 {
1640         struct faillog fl;
1641         int fd;
1642         off_t off_uid = (off_t) user_id * sizeof fl;
1643         off_t off_newuid = (off_t) user_newid * sizeof fl;
1644
1645         if (access (FAILLOG_FILE, F_OK) != 0) {
1646                 return;
1647         }
1648
1649         fd = open (FAILLOG_FILE, O_RDWR);
1650
1651         if (-1 == fd) {
1652                 fprintf (stderr,
1653                          _("%s: failed to copy the faillog entry of user %lu to user %lu: %s\n"),
1654                          Prog, (unsigned long) user_id, (unsigned long) user_newid, strerror (errno));
1655                 return;
1656         }
1657
1658         if (   (lseek (fd, off_uid, SEEK_SET) == off_uid)
1659             && (read (fd, (char *) &fl, sizeof fl) == (ssize_t) sizeof fl)) {
1660                 /* Copy the old entry to its new location */
1661                 if (   (lseek (fd, off_newuid, SEEK_SET) != off_newuid)
1662                     || (write (fd, &fl, sizeof fl) != (ssize_t) sizeof fl)
1663                     || (fsync (fd) != 0)
1664                     || (close (fd) != 0)) {
1665                         fprintf (stderr,
1666                                  _("%s: failed to copy the faillog entry of user %lu to user %lu: %s\n"),
1667                                  Prog, (unsigned long) user_id, (unsigned long) user_newid, strerror (errno));
1668                 }
1669         } else {
1670                 /* Assume lseek or read failed because there is
1671                  * no entry for the old UID */
1672
1673                 /* Check if the new UID already has an entry */
1674                 if (   (lseek (fd, off_newuid, SEEK_SET) == off_newuid)
1675                     && (read (fd, &fl, sizeof fl) == (ssize_t) sizeof fl)) {
1676                         /* Reset the new uid's faillog entry */
1677                         memzero (&fl, sizeof (fl));
1678                         if (   (lseek (fd, off_newuid, SEEK_SET) != off_newuid)
1679                             || (write (fd, &fl, sizeof fl) != (ssize_t) sizeof fl)
1680                             || (close (fd) != 0)) {
1681                                 fprintf (stderr,
1682                                          _("%s: failed to copy the faillog entry of user %lu to user %lu: %s\n"),
1683                                          Prog, (unsigned long) user_id, (unsigned long) user_newid, strerror (errno));
1684                         }
1685                 } else {
1686                         (void) close (fd);
1687                 }
1688         }
1689 }
1690
1691 #ifndef NO_MOVE_MAILBOX
1692 /*
1693  * This is the new and improved code to carefully chown/rename the user's
1694  * mailbox. Maybe I am too paranoid but the mail spool dir sometimes
1695  * happens to be mode 1777 (this makes mail user agents work without
1696  * being setgid mail, but is NOT recommended; they all should be fixed
1697  * to use movemail).  --marekm
1698  */
1699 static void move_mailbox (void)
1700 {
1701         const char *maildir;
1702         char mailfile[1024], newmailfile[1024];
1703         int fd;
1704         struct stat st;
1705
1706         maildir = getdef_str ("MAIL_DIR");
1707 #ifdef MAIL_SPOOL_DIR
1708         if ((NULL == maildir) && (getdef_str ("MAIL_FILE") == NULL)) {
1709                 maildir = MAIL_SPOOL_DIR;
1710         }
1711 #endif
1712         if (NULL == maildir) {
1713                 return;
1714         }
1715
1716         /*
1717          * O_NONBLOCK is to make sure open won't hang on mandatory locks.
1718          * We do fstat/fchown to make sure there are no races (someone
1719          * replacing /var/spool/mail/luser with a hard link to /etc/passwd
1720          * between stat and chown).  --marekm
1721          */
1722         (void) snprintf (mailfile, sizeof mailfile, "%s/%s",
1723                          maildir, user_name);
1724         mailfile[(sizeof mailfile) - 1] = '\0';
1725         fd = open (mailfile, O_RDONLY | O_NONBLOCK, 0);
1726         if (fd < 0) {
1727                 /* no need for warnings if the mailbox doesn't exist */
1728                 if (errno != ENOENT) {
1729                         perror (mailfile);
1730                 }
1731                 return;
1732         }
1733         if (fstat (fd, &st) < 0) {
1734                 perror ("fstat");
1735                 (void) close (fd);
1736                 return;
1737         }
1738         if (st.st_uid != user_id) {
1739                 /* better leave it alone */
1740                 fprintf (stderr, _("%s: warning: %s not owned by %s\n"),
1741                          Prog, mailfile, user_name);
1742                 (void) close (fd);
1743                 return;
1744         }
1745         if (uflg) {
1746                 if (fchown (fd, user_newid, (gid_t) -1) < 0) {
1747                         perror (_("failed to change mailbox owner"));
1748                 }
1749 #ifdef WITH_AUDIT
1750                 else {
1751                         audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
1752                                       "changing mail file owner",
1753                                       user_newname, (unsigned int) user_newid, 1);
1754                 }
1755 #endif
1756         }
1757
1758         (void) close (fd);
1759
1760         if (lflg) {
1761                 (void) snprintf (newmailfile, sizeof newmailfile, "%s/%s",
1762                                  maildir, user_newname);
1763                 newmailfile[(sizeof newmailfile) - 1] = '\0';
1764                 if (   (link (mailfile, newmailfile) != 0)
1765                     || (unlink (mailfile) != 0)) {
1766                         perror (_("failed to rename mailbox"));
1767                 }
1768 #ifdef WITH_AUDIT
1769                 else {
1770                         audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
1771                                       "changing mail file name",
1772                                       user_newname, (unsigned int) user_newid, 1);
1773                 }
1774 #endif
1775         }
1776 }
1777 #endif
1778
1779 /*
1780  * main - usermod command
1781  */
1782 int main (int argc, char **argv)
1783 {
1784 #ifdef ACCT_TOOLS_SETUID
1785 #ifdef USE_PAM
1786         pam_handle_t *pamh = NULL;
1787         int retval;
1788 #endif                          /* USE_PAM */
1789 #endif                          /* ACCT_TOOLS_SETUID */
1790
1791 #ifdef WITH_AUDIT
1792         audit_help_open ();
1793 #endif
1794
1795         /*
1796          * Get my name so that I can use it to report errors.
1797          */
1798         Prog = Basename (argv[0]);
1799
1800         (void) setlocale (LC_ALL, "");
1801         (void) bindtextdomain (PACKAGE, LOCALEDIR);
1802         (void) textdomain (PACKAGE);
1803
1804         sys_ngroups = sysconf (_SC_NGROUPS_MAX);
1805         user_groups = (char **) malloc (sizeof (char *) * (1 + sys_ngroups));
1806         user_groups[0] = (char *) 0;
1807
1808         OPENLOG ("usermod");
1809
1810         is_shadow_pwd = spw_file_present ();
1811 #ifdef SHADOWGRP
1812         is_shadow_grp = sgr_file_present ();
1813 #endif
1814
1815         process_flags (argc, argv);
1816
1817         /*
1818          * The home directory, the username and the user's UID should not
1819          * be changed while the user is logged in.
1820          */
1821         if (   (uflg || lflg || dflg)
1822             && (user_busy (user_name, user_id) != 0)) {
1823                 exit (E_USER_BUSY);
1824         }
1825
1826 #ifdef ACCT_TOOLS_SETUID
1827 #ifdef USE_PAM
1828         {
1829                 struct passwd *pampw;
1830                 pampw = getpwuid (getuid ()); /* local, no need for xgetpwuid */
1831                 if (pampw == NULL) {
1832                         fprintf (stderr,
1833                                  _("%s: Cannot determine your user name.\n"),
1834                                  Prog);
1835                         exit (1);
1836                 }
1837
1838                 retval = pam_start ("usermod", pampw->pw_name, &conv, &pamh);
1839         }
1840
1841         if (PAM_SUCCESS == retval) {
1842                 retval = pam_authenticate (pamh, 0);
1843         }
1844
1845         if (PAM_SUCCESS == retval) {
1846                 retval = pam_acct_mgmt (pamh, 0);
1847         }
1848
1849         if (NULL != pamh) {
1850                 (void) pam_end (pamh, retval);
1851         }
1852         if (PAM_SUCCESS != retval) {
1853                 fprintf (stderr, _("%s: PAM authentication failed\n"), Prog);
1854                 exit (1);
1855         }
1856 #endif                          /* USE_PAM */
1857 #endif                          /* ACCT_TOOLS_SETUID */
1858
1859 #ifdef WITH_TCB
1860         if (shadowtcb_set_user (user_name) == SHADOWTCB_FAILURE) {
1861                 exit (E_PW_UPDATE);
1862         }
1863 #endif
1864
1865         /*
1866          * Do the hard stuff - open the files, change the user entries,
1867          * change the home directory, then close and update the files.
1868          */
1869         open_files ();
1870         if (   cflg || dflg || eflg || fflg || gflg || Lflg || lflg || pflg
1871             || sflg || uflg || Uflg) {
1872                 usr_update ();
1873         }
1874         if (Gflg || lflg) {
1875                 grp_update ();
1876         }
1877         close_files ();
1878
1879 #ifdef WITH_TCB
1880         if (   (lflg || uflg)
1881             && (shadowtcb_move (user_newname, user_newid) == SHADOWTCB_FAILURE) ) {
1882                 exit (E_PW_UPDATE);
1883         }
1884 #endif
1885
1886         nscd_flush_cache ("passwd");
1887         nscd_flush_cache ("group");
1888
1889 #ifdef WITH_SELINUX
1890         if (Zflg) {
1891                 selinux_update_mapping ();
1892         }
1893 #endif
1894
1895         if (mflg) {
1896                 move_home ();
1897         }
1898
1899 #ifndef NO_MOVE_MAILBOX
1900         if (lflg || uflg) {
1901                 move_mailbox ();
1902         }
1903 #endif
1904
1905         if (uflg) {
1906                 update_lastlog ();
1907                 update_faillog ();
1908         }
1909
1910         if (!mflg && (uflg || gflg)) {
1911                 if (access (dflg ? user_newhome : user_home, F_OK) == 0) {
1912                         /*
1913                          * Change the UID on all of the files owned by
1914                          * `user_id' to `user_newid' in the user's home
1915                          * directory.
1916                          *
1917                          * move_home() already takes care of changing the
1918                          * ownership.
1919                          *
1920                          */
1921                         if (chown_tree (dflg ? user_newhome : user_home,
1922                                         user_id,
1923                                         uflg ? user_newid  : (uid_t)-1,
1924                                         user_gid,
1925                                         gflg ? user_newgid : (gid_t)-1) != 0) {
1926                                 fprintf (stderr,
1927                                          _("%s: Failed to change ownership of the home directory"),
1928                                          Prog);
1929                                 fail_exit (E_HOMEDIR);
1930                         }
1931                 }
1932         }
1933
1934         return E_SUCCESS;
1935 }
1936
1937 #ifdef WITH_SELINUX
1938 static void selinux_update_mapping (void) {
1939         const char *argv[7];
1940
1941         if (is_selinux_enabled () <= 0) {
1942                 return;
1943         }
1944
1945         if ('\0' != *user_selinux) {
1946                 argv[0] = "/usr/sbin/semanage";
1947                 argv[1] = "login";
1948                 argv[2] = "-m";
1949                 argv[3] = "-s";
1950                 argv[4] = user_selinux;
1951                 argv[5] = user_name;
1952                 argv[6] = NULL;
1953                 if (safe_system (argv[0], argv, NULL, true) != 0) {
1954                         argv[2] = "-a";
1955                         if (safe_system (argv[0], argv, NULL, false) != 0) {
1956                                 fprintf (stderr,
1957                                          _("%s: warning: the user name %s to %s SELinux user mapping failed.\n"),
1958                                          Prog, user_name, user_selinux);
1959 #ifdef WITH_AUDIT
1960                                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
1961                                               "modifying User mapping ",
1962                                               user_name, (unsigned int) user_id, 0);
1963 #endif
1964                         }
1965                 }
1966         }
1967 }
1968 #endif
1969