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