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