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
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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.
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.
44 #ifdef ACCT_TOOLS_SETUID
48 #endif /* ACCT_TOOLS_SETUID */
51 #include <sys/types.h>
59 #include "prototypes.h"
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).
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))
91 static char *user_name;
92 static char *user_newname;
93 static char *user_pass;
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;
104 static const char *user_selinux = "";
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 */
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 */
129 Zflg = false, /* new selinux user */
131 uflg = false, /* specify new user ID */
132 Uflg = false; /* unlock the password */
134 static bool is_shadow_pwd;
137 static bool is_shadow_grp;
140 static bool pw_locked = false;
141 static bool spw_locked = false;
142 static bool gr_locked = false;
144 static bool sgr_locked = false;
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 *);
155 static void selinux_update_mapping (void);
158 static void new_spent (struct spwd *);
159 static void fail_exit (int);
160 static void update_group (void);
163 static void update_gshadow (void);
165 static void grp_update (void);
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);
175 #ifndef NO_MOVE_MAILBOX
176 static void move_mailbox (void);
179 static void date_to_str (char *buf, size_t maxsize,
180 long int date, const char *negativ)
184 if ((negativ != NULL) && (date < 0)) {
185 strncpy (buf, negativ, maxsize);
187 time_t t = (time_t) date;
190 strftime (buf, maxsize, "%Y-%m-%d", tp);
192 snprintf (buf, maxsize, "%04d-%02d-%02d",
193 tp->tm_year + 1900, tp->tm_mon + 1, tp->tm_mday);
194 #endif /* HAVE_STRFTIME */
196 buf[maxsize - 1] = '\0';
200 * get_groups - convert a list of group names to an array of group IDs
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.
206 static int get_groups (char *list)
209 const struct group *grp;
214 * Initialize the list to be empty
216 user_groups[0] = (char *) 0;
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.
229 * Strip off a single name from the list
231 cp = strchr (list, ',');
238 * Names starting with digits are treated as numerical GID
239 * values, otherwise the string is looked up as is.
241 grp = getgr_nam_gid (list);
244 * There must be a match, either by GID value or by
248 fprintf (stderr, _("%s: group '%s' does not exist\n"),
255 * If the group doesn't exist, don't dump core. Instead,
256 * try the next one. --marekm
264 * Don't add this group if they are an NIS group. Tell the
265 * user to go to the server for this group.
269 _("%s: group '%s' is a NIS group.\n"),
275 if (ngroups == sys_ngroups) {
277 _("%s: too many groups specified (max %d).\n"),
283 * Add the group name to the user's list of groups.
285 user_groups[ngroups++] = xstrdup (grp->gr_name);
286 } while (NULL != list);
288 user_groups[ngroups] = (char *) 0;
291 * Any errors in finding group names are fatal
301 * usage - display usage message and exit
303 static void usage (int status)
305 fprintf ((E_SUCCESS != status) ? stderr : stdout,
306 _("Usage: usermod [options] LOGIN\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"
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"
332 _(" -Z, --selinux-user new SELinux user mapping for the user account\n")
341 * update encrypted password string (for both shadow and non-shadow
344 static char *new_pw_passwd (char *pw_pass)
346 if (Lflg && ('!' != pw_pass[0])) {
347 char *buf = xmalloc (strlen (pw_pass) + 2);
350 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
352 user_newname, (unsigned int) user_newid, 0);
354 SYSLOG ((LOG_INFO, "lock user '%s' password", user_newname));
356 strcat (buf, pw_pass);
358 } else if (Uflg && pw_pass[0] == '!') {
361 if (pw_pass[1] == '\0') {
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"),
370 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
372 user_newname, (unsigned int) user_newid, 0);
374 SYSLOG ((LOG_INFO, "unlock user '%s' password", user_newname));
382 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
384 user_newname, (unsigned int) user_newid, 1);
386 SYSLOG ((LOG_INFO, "change user '%s' password", user_newname));
387 pw_pass = xstrdup (user_pass);
393 * new_pwent - initialize the values in a password file entry
395 * new_pwent() takes all of the values that have been entered and fills
396 * in a (struct passwd) with them.
398 static void new_pwent (struct passwd *pwent)
401 if (pw_locate (user_newname) != NULL) {
403 _("%s: user '%s' already exists in %s\n"),
404 Prog, user_newname, pw_dbname ());
405 fail_exit (E_NAME_IN_USE);
408 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
410 user_newname, (unsigned int) user_newid, 1);
413 "change user name '%s' to '%s'",
414 pwent->pw_name, user_newname));
415 pwent->pw_name = xstrdup (user_newname);
417 if (!is_shadow_pwd) {
418 pwent->pw_passwd = new_pw_passwd (pwent->pw_passwd);
423 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
425 user_newname, (unsigned int) user_newid, 1);
428 "change user '%s' UID from '%d' to '%d'",
429 pwent->pw_name, pwent->pw_uid, user_newid));
430 pwent->pw_uid = user_newid;
434 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
435 "changing primary group",
436 user_newname, (unsigned int) user_newid, 1);
439 "change user '%s' GID from '%d' to '%d'",
440 pwent->pw_name, pwent->pw_gid, user_newgid));
441 pwent->pw_gid = user_newgid;
445 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
447 user_newname, (unsigned int) user_newid, 1);
449 pwent->pw_gecos = user_newcomment;
454 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
455 "changing home directory",
456 user_newname, (unsigned int) user_newid, 1);
459 "change user '%s' home from '%s' to '%s'",
460 pwent->pw_name, pwent->pw_dir, user_newhome));
461 pwent->pw_dir = user_newhome;
465 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
466 "changing user shell",
467 user_newname, (unsigned int) user_newid, 1);
470 "change user '%s' shell from '%s' to '%s'",
471 pwent->pw_name, pwent->pw_shell, user_newshell));
472 pwent->pw_shell = user_newshell;
477 * new_spent - initialize the values in a shadow password file entry
479 * new_spent() takes all of the values that have been entered and fills
480 * in a (struct spwd) with them.
482 static void new_spent (struct spwd *spent)
485 if (spw_locate (user_newname) != NULL) {
487 _("%s: user '%s' already exists in %s\n"),
488 Prog, user_newname, spw_dbname ());
489 fail_exit (E_NAME_IN_USE);
491 spent->sp_namp = xstrdup (user_newname);
496 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
497 "changing inactive days",
498 user_newname, (unsigned int) user_newid, 1);
501 "change user '%s' inactive from '%ld' to '%ld'",
502 spent->sp_namp, spent->sp_inact, user_newinactive));
503 spent->sp_inact = user_newinactive;
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");
513 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
514 "changing expiration date",
515 user_newname, (unsigned int) user_newid, 1);
518 "change user '%s' expiration from '%s' to '%s'",
519 spent->sp_namp, old_exp, new_exp));
520 spent->sp_expire = user_newexpire;
522 spent->sp_pwdp = new_pw_passwd (spent->sp_pwdp);
524 spent->sp_lstchg = (long) time ((time_t *) 0) / SCALE;
525 if (0 == spent->sp_lstchg) {
526 /* Better disable aging than requiring a password
528 spent->sp_lstchg = -1;
534 * fail_exit - exit with an error code after unlocking files
536 static void fail_exit (int code)
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 ()));
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 ()));
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 ()));
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 ()));
570 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
572 user_name, AUDIT_NO_ID, 0);
578 static void update_group (void)
583 const struct group *grp;
589 * Scan through the entire group file looking for the groups that
590 * the user is a member of.
592 while ((grp = gr_next ()) != NULL) {
594 * See if the user specified this group as one of their
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));
601 if (!was_member && !is_member) {
605 ngrp = __gr_dup (grp);
608 _("%s: Out of memory. Cannot update %s.\n"),
610 fail_exit (E_GRP_UPDATE);
613 if (was_member && (!Gflg || is_member)) {
615 ngrp->gr_mem = del_list (ngrp->gr_mem,
617 ngrp->gr_mem = add_list (ngrp->gr_mem,
621 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
622 "changing group member",
623 user_newname, AUDIT_NO_ID, 1);
626 "change '%s' to '%s' in group '%s'",
627 user_name, user_newname,
630 } else if (was_member && !aflg && Gflg && !is_member) {
631 ngrp->gr_mem = del_list (ngrp->gr_mem, user_name);
634 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
635 "removing group member",
636 user_name, AUDIT_NO_ID, 1);
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);
645 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
646 "adding user to group",
647 user_name, AUDIT_NO_ID, 1);
649 SYSLOG ((LOG_INFO, "add '%s' to group '%s'",
650 user_newname, ngrp->gr_name));
657 if (gr_update (ngrp) == 0) {
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);
668 static void update_gshadow (void)
674 const struct sgrp *sgrp;
680 * Scan through the entire shadow group file looking for the groups
681 * that the user is a member of.
683 while ((sgrp = sgr_next ()) != NULL) {
686 * See if the user was a member of this group
688 was_member = is_on_list (sgrp->sg_mem, user_name);
691 * See if the user was an administrator of this group
693 was_admin = is_on_list (sgrp->sg_adm, user_name);
696 * See if the user specified this group as one of their
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));
703 if (!was_member && !was_admin && !is_member) {
707 nsgrp = __sgr_dup (sgrp);
710 _("%s: Out of memory. Cannot update %s.\n"),
711 Prog, sgr_dbname ());
712 fail_exit (E_GRP_UPDATE);
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);
720 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
721 "changing admin name in shadow group",
722 user_name, AUDIT_NO_ID, 1);
725 "change admin '%s' to '%s' in shadow group '%s'",
726 user_name, user_newname, nsgrp->sg_name));
728 if (was_member && (!Gflg || is_member)) {
730 nsgrp->sg_mem = del_list (nsgrp->sg_mem,
732 nsgrp->sg_mem = add_list (nsgrp->sg_mem,
736 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
737 "changing member in shadow group",
738 user_name, AUDIT_NO_ID, 1);
741 "change '%s' to '%s' in shadow group '%s'",
742 user_name, user_newname,
745 } else if (was_member && !aflg && Gflg && !is_member) {
746 nsgrp->sg_mem = del_list (nsgrp->sg_mem, user_name);
749 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
750 "removing user from shadow group",
751 user_name, AUDIT_NO_ID, 1);
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);
760 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
761 "adding user to shadow group",
762 user_newname, AUDIT_NO_ID, 1);
764 SYSLOG ((LOG_INFO, "add '%s' to shadow group '%s'",
765 user_newname, nsgrp->sg_name));
774 * Update the group entry to reflect the changes.
776 if (sgr_update (nsgrp) == 0) {
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);
786 #endif /* SHADOWGRP */
789 * grp_update - add user to secondary group set
791 * grp_update() takes the secondary group set given in user_groups and
792 * adds the user to each group given by that set.
794 static void grp_update (void)
805 * process_flags - perform command line argument setting
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.
811 static void process_flags (int argc, char **argv)
813 const struct group *grp;
815 bool anyflag = false;
817 if ((1 == argc) || ('-' == argv[argc - 1][0])) {
822 const struct passwd *pwd;
823 /* local, no need for xgetpwnam */
824 pwd = getpwnam (argv[argc - 1]);
827 _("%s: user '%s' does not exist\n"),
828 Prog, argv[argc - 1]);
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);
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;
848 * Now make sure it isn't an NIS user.
855 _("%s: user %s is a NIS user\n"),
858 if ( !yp_get_default_domain (&nis_domain)
859 && !yp_master (nis_domain, "passwd.byname", &nis_master)) {
861 _("%s: %s is the NIS master\n"),
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;
881 * Parse the command line options.
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'},
899 {"selinux-user", required_argument, NULL, 'Z'},
901 {"shell", required_argument, NULL, 's'},
902 {"uid", required_argument, NULL, 'u'},
903 {"unlock", no_argument, NULL, 'U'},
904 {NULL, 0, NULL, '\0'}
906 while ((c = getopt_long (argc, argv,
908 "ac:d:e:f:g:G:hl:Lmop:s:u:UZ:",
910 "ac:d:e:f:g:G:hl:Lmop:s:u:U",
912 long_options, NULL)) != -1) {
918 if (!VALID (optarg)) {
920 _("%s: invalid field '%s'\n"),
924 user_newcomment = optarg;
928 if (!VALID (optarg)) {
930 _("%s: invalid field '%s'\n"),
935 user_newhome = optarg;
938 if ('\0' != *optarg) {
939 user_newexpire = strtoday (optarg);
940 if (user_newexpire == -1) {
942 _("%s: invalid date '%s'\n"),
946 user_newexpire *= DAY / SCALE;
953 if ( (getlong (optarg, &user_newinactive) == 0)
954 || (user_newinactive < -1)) {
956 _("%s: invalid numeric argument '%s'\n"),
963 grp = getgr_nam_gid (optarg);
966 _("%s: group '%s' does not exist\n"),
970 user_newgid = grp->gr_gid;
974 if (get_groups (optarg) != 0) {
983 if (!is_valid_user_name (optarg)) {
985 _("%s: invalid field '%s'\n"),
990 user_newname = optarg;
1006 if (!VALID (optarg)) {
1008 _("%s: invalid field '%s'\n"),
1012 user_newshell = optarg;
1016 if ( (get_uid (optarg, &user_newid) ==0)
1017 || (user_newid == (uid_t)-1)) {
1019 _("%s: invalid user ID '%s'\n"),
1030 if (is_selinux_enabled () > 0) {
1031 user_selinux = optarg;
1035 _("%s: -Z requires SELinux enabled kernel\n"),
1049 fprintf (stderr, _("%s: no flags given\n"), Prog);
1053 if (user_newid == user_id) {
1057 if (user_newgid == user_gid) {
1060 if (strcmp (user_newshell, user_shell) == 0) {
1063 if (strcmp (user_newname, user_name) == 0) {
1066 if (user_newinactive == user_inactive) {
1069 if (user_newexpire == user_expire) {
1072 if (strcmp (user_newhome, user_home) == 0) {
1076 if (strcmp (user_newcomment, user_comment) == 0) {
1080 if (!(Uflg || uflg || sflg || pflg || oflg || mflg || Lflg ||
1081 lflg || Gflg || gflg || fflg || eflg || dflg || cflg
1086 fprintf (stderr, _("%s: no changes\n"), Prog);
1090 if (!is_shadow_pwd && (eflg || fflg)) {
1092 _("%s: shadow passwords required for -e and -f\n"),
1097 if (optind != argc - 1) {
1101 if (aflg && (!Gflg)) {
1103 _("%s: %s flag is only allowed with the %s flag\n"),
1109 if ((Lflg && (pflg || Uflg)) || (pflg && Uflg)) {
1111 _("%s: the -L, -p, and -U flags are exclusive\n"),
1117 if (oflg && !uflg) {
1119 _("%s: %s flag is only allowed with the %s flag\n"),
1125 if (mflg && !dflg) {
1127 _("%s: %s flag is only allowed with the %s flag\n"),
1133 /* local, no need for xgetpwnam */
1134 if (lflg && (getpwnam (user_newname) != NULL)) {
1136 _("%s: user '%s' already exists\n"),
1137 Prog, user_newname);
1138 exit (E_NAME_IN_USE);
1141 /* local, no need for xgetpwuid */
1142 if (uflg && !oflg && (getpwuid (user_newid) != NULL)) {
1144 _("%s: UID '%lu' already exists\n"),
1145 Prog, (unsigned long) user_newid);
1146 exit (E_UID_IN_USE);
1151 * close_files - close all of the files that were opened
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.
1156 static void close_files (void)
1158 if (pw_close () == 0) {
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);
1165 if (is_shadow_pwd && (spw_close () == 0)) {
1167 _("%s: failure while writing changes to %s\n"),
1168 Prog, spw_dbname ());
1170 "failure while writing changes to %s",
1172 fail_exit (E_PW_UPDATE);
1176 if (gr_close () == 0) {
1178 _("%s: failure while writing changes to %s\n"),
1179 Prog, gr_dbname ());
1181 "failure while writing changes to %s",
1183 fail_exit (E_GRP_UPDATE);
1186 if (is_shadow_grp) {
1187 if (sgr_close () == 0) {
1189 _("%s: failure while writing changes to %s\n"),
1190 Prog, sgr_dbname ());
1192 "failure while writing changes to %s",
1194 fail_exit (E_GRP_UPDATE);
1196 if (sgr_unlock () == 0) {
1198 _("%s: failed to unlock %s\n"),
1199 Prog, sgr_dbname ());
1201 "failed to unlock %s",
1207 if (gr_unlock () == 0) {
1209 _("%s: failed to unlock %s\n"),
1210 Prog, gr_dbname ());
1212 "failed to unlock %s",
1218 if (is_shadow_pwd) {
1219 if (spw_unlock () == 0) {
1221 _("%s: failed to unlock %s\n"),
1222 Prog, spw_dbname ());
1224 "failed to unlock %s",
1229 if (pw_unlock () == 0) {
1231 _("%s: failed to unlock %s\n"),
1232 Prog, pw_dbname ());
1233 SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ()));
1245 * Close the DBM and/or flat files
1256 * open_files - lock and open the password files
1258 * open_files() opens the two password files.
1260 static void open_files (void)
1262 if (pw_lock () == 0) {
1264 _("%s: cannot lock %s; try again later.\n"),
1265 Prog, pw_dbname ());
1266 fail_exit (E_PW_UPDATE);
1269 if (pw_open (O_RDWR) == 0) {
1271 _("%s: cannot open %s\n"),
1272 Prog, pw_dbname ());
1273 fail_exit (E_PW_UPDATE);
1275 if (is_shadow_pwd && (spw_lock () == 0)) {
1277 _("%s: cannot lock %s; try again later.\n"),
1278 Prog, spw_dbname ());
1279 fail_exit (E_PW_UPDATE);
1282 if (is_shadow_pwd && (spw_open (O_RDWR) == 0)) {
1284 _("%s: cannot open %s\n"),
1285 Prog, spw_dbname ());
1286 fail_exit (E_PW_UPDATE);
1291 * Lock and open the group file. This will load all of the
1294 if (gr_lock () == 0) {
1296 _("%s: cannot lock %s; try again later.\n"),
1297 Prog, gr_dbname ());
1298 fail_exit (E_GRP_UPDATE);
1301 if (gr_open (O_RDWR) == 0) {
1303 _("%s: cannot open %s\n"),
1304 Prog, gr_dbname ());
1305 fail_exit (E_GRP_UPDATE);
1308 if (is_shadow_grp && (sgr_lock () == 0)) {
1310 _("%s: cannot lock %s; try again later.\n"),
1311 Prog, sgr_dbname ());
1312 fail_exit (E_GRP_UPDATE);
1315 if (is_shadow_grp && (sgr_open (O_RDWR) == 0)) {
1317 _("%s: cannot open %s\n"),
1318 Prog, sgr_dbname ());
1319 fail_exit (E_GRP_UPDATE);
1326 * usr_update - create the user entries
1328 * usr_update() creates the password file entries for this user and
1329 * will update the group entries if required.
1331 static void usr_update (void)
1333 struct passwd pwent;
1334 const struct passwd *pwd;
1337 const struct spwd *spwd = NULL;
1340 * Locate the entry in /etc/passwd, which MUST exist.
1342 pwd = pw_locate (user_name);
1345 _("%s: user '%s' does not exist in %s\n"),
1346 Prog, user_name, pw_dbname ());
1347 fail_exit (E_NOTFOUND);
1354 * Locate the entry in /etc/shadow. It doesn't have to exist, and
1355 * won't be created if it doesn't.
1357 if (is_shadow_pwd && ((spwd = spw_locate (user_name)) != NULL)) {
1362 if (lflg || uflg || gflg || cflg || dflg || sflg || pflg
1364 if (pw_update (&pwent) == 0) {
1366 _("%s: failed to prepare the new %s entry '%s'\n"),
1367 Prog, pw_dbname (), pwent.pw_name);
1368 fail_exit (E_PW_UPDATE);
1370 if (lflg && (pw_remove (user_name) == 0)) {
1372 _("%s: cannot remove entry '%s' from %s\n"),
1373 Prog, user_name, pw_dbname ());
1374 fail_exit (E_PW_UPDATE);
1377 if ((NULL != spwd) && (lflg || eflg || fflg || pflg || Lflg || Uflg)) {
1378 if (spw_update (&spent) == 0) {
1380 _("%s: failed to prepare the new %s entry '%s'\n"),
1381 Prog, spw_dbname (), spent.sp_namp);
1382 fail_exit (E_PW_UPDATE);
1384 if (lflg && (spw_remove (user_name) == 0)) {
1386 _("%s: cannot remove entry '%s' from %s\n"),
1387 Prog, user_name, spw_dbname ());
1388 fail_exit (E_PW_UPDATE);
1394 * move_home - move the user's home directory
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.
1399 static void move_home (void)
1403 if (mflg && (stat (user_home, &sb) == 0)) {
1405 * Don't try to move it if it is not a directory
1406 * (but /dev/null for example). --marekm
1408 if (!S_ISDIR (sb.st_mode)) {
1412 if (access (user_newhome, F_OK) == 0) {
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) {
1424 _("%s: can't create %s\n"),
1425 Prog, user_newhome);
1427 if (chown (user_newhome, sb.st_uid, sb.st_gid) != 0) {
1429 _("%s: can't chown %s\n"),
1430 Prog, user_newhome);
1431 rmdir (user_newhome);
1432 fail_exit (E_HOMEDIR);
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) {
1443 _("%s: warning: failed to completely remove old home directory %s"),
1447 audit_logger (AUDIT_USER_CHAUTHTOK,
1449 "moving home directory",
1451 (unsigned int) user_newid,
1457 /* TODO: do some cleanup if the copy
1459 (void) remove_tree (user_newhome);
1462 _("%s: cannot rename directory %s to %s\n"),
1463 Prog, user_home, user_newhome);
1464 fail_exit (E_HOMEDIR);
1467 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
1468 "moving home directory",
1469 user_newname, (unsigned int) user_newid, 1);
1474 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
1475 "changing home directory owner",
1476 user_newname, (unsigned int) user_newid, 1);
1478 chown (dflg ? user_newhome : user_home,
1479 uflg ? user_newid : user_id,
1480 gflg ? user_newgid : user_gid);
1485 * update_lastlog - update the lastlog file
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.
1491 static void update_lastlog (void)
1495 off_t off_uid = (off_t) user_id * sizeof ll;
1496 off_t off_newuid = (off_t) user_newid * sizeof ll;
1498 if (access (LASTLOG_FILE, F_OK) != 0) {
1502 fd = open (LASTLOG_FILE, O_RDWR);
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));
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)) {
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));
1523 /* Assume lseek or read failed because there is
1524 * no entry for the old UID */
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)) {
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));
1546 * update_faillog - update the faillog file
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.
1552 static void update_faillog (void)
1556 off_t off_uid = (off_t) user_id * sizeof fl;
1557 off_t off_newuid = (off_t) user_newid * sizeof fl;
1559 if (access (FAILLOG_FILE, F_OK) != 0) {
1563 fd = open (FAILLOG_FILE, O_RDWR);
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));
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)) {
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));
1584 /* Assume lseek or read failed because there is
1585 * no entry for the old UID */
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)) {
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));
1605 #ifndef NO_MOVE_MAILBOX
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
1613 static void move_mailbox (void)
1615 const char *maildir;
1616 char mailfile[1024], newmailfile[1024];
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;
1626 if (NULL == maildir) {
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
1636 snprintf (mailfile, sizeof mailfile, "%s/%s", maildir, user_name);
1637 fd = open (mailfile, O_RDONLY | O_NONBLOCK, 0);
1639 /* no need for warnings if the mailbox doesn't exist */
1640 if (errno != ENOENT) {
1645 if (fstat (fd, &st) < 0) {
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);
1658 if (fchown (fd, user_newid, (gid_t) - 1) < 0) {
1659 perror (_("failed to change mailbox owner"));
1663 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
1664 "changing mail file owner",
1665 user_newname, (unsigned int) user_newid, 1);
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"));
1681 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
1682 "changing mail file name",
1683 user_newname, (unsigned int) user_newid, 1);
1691 * main - usermod command
1693 int main (int argc, char **argv)
1695 #ifdef ACCT_TOOLS_SETUID
1697 pam_handle_t *pamh = NULL;
1699 #endif /* USE_PAM */
1700 #endif /* ACCT_TOOLS_SETUID */
1707 * Get my name so that I can use it to report errors.
1709 Prog = Basename (argv[0]);
1711 (void) setlocale (LC_ALL, "");
1712 (void) bindtextdomain (PACKAGE, LOCALEDIR);
1713 (void) textdomain (PACKAGE);
1715 sys_ngroups = sysconf (_SC_NGROUPS_MAX);
1716 user_groups = (char **) malloc (sizeof (char *) * (1 + sys_ngroups));
1717 user_groups[0] = (char *) 0;
1719 OPENLOG ("usermod");
1721 is_shadow_pwd = spw_file_present ();
1723 is_shadow_grp = sgr_file_present ();
1726 process_flags (argc, argv);
1729 * The home directory, the username and the user's UID should not
1730 * be changed while the user is logged in.
1732 if ( (uflg || lflg || dflg)
1733 && (user_busy (user_name, user_id) != 0)) {
1735 _("%s: user %s is currently logged in\n"),
1740 #ifdef ACCT_TOOLS_SETUID
1743 struct passwd *pampw;
1744 pampw = getpwuid (getuid ()); /* local, no need for xgetpwuid */
1745 if (pampw == NULL) {
1747 _("%s: Cannot determine your user name.\n"),
1752 retval = pam_start ("usermod", pampw->pw_name, &conv, &pamh);
1755 if (PAM_SUCCESS == retval) {
1756 retval = pam_authenticate (pamh, 0);
1759 if (PAM_SUCCESS == retval) {
1760 retval = pam_acct_mgmt (pamh, 0);
1764 (void) pam_end (pamh, retval);
1766 if (PAM_SUCCESS != retval) {
1767 fprintf (stderr, _("%s: PAM authentication failed\n"), Prog);
1770 #endif /* USE_PAM */
1771 #endif /* ACCT_TOOLS_SETUID */
1774 * Do the hard stuff - open the files, change the user entries,
1775 * change the home directory, then close and update the files.
1778 if ( cflg || dflg || eflg || fflg || gflg || Lflg || lflg || pflg
1779 || sflg || uflg || Uflg) {
1787 nscd_flush_cache ("passwd");
1788 nscd_flush_cache ("group");
1792 selinux_update_mapping ();
1800 #ifndef NO_MOVE_MAILBOX
1806 if (uflg) { // FIXME: gflg also, except for faillog/lastlog
1811 * Change the UID on all of the files owned by `user_id' to
1812 * `user_newid' in the user's home directory.
1814 chown_tree (dflg ? user_newhome : user_home,
1815 user_id, user_newid,
1816 user_gid, gflg ? user_newgid : user_gid);
1823 static void selinux_update_mapping (void) {
1824 const char *argv[7];
1826 if (is_selinux_enabled () <= 0) return;
1828 if (*user_selinux) {
1829 argv[0] = "/usr/sbin/semanage";
1833 argv[4] = user_selinux;
1834 argv[5] = user_name;
1836 if (safe_system (argv[0], argv, NULL, 1)) {
1838 if (safe_system (argv[0], argv, NULL, 0)) {
1840 _("%s: warning: the user name %s to %s SELinux user mapping failed.\n"),
1841 Prog, user_name, user_selinux);
1843 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
1844 "modifying User mapping ",
1845 user_name, (unsigned int) user_id, 0);