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