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"
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).
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))
89 static char *user_name;
90 static char *user_newname;
91 static char *user_pass;
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;
102 static const char *user_selinux = "";
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 */
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 */
127 Zflg = false, /* new selinux user */
129 uflg = false, /* specify new user ID */
130 Uflg = false; /* unlock the password */
132 static bool is_shadow_pwd;
135 static bool is_shadow_grp;
138 static bool pw_locked = false;
139 static bool spw_locked = false;
140 static bool gr_locked = false;
142 static bool sgr_locked = false;
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 *);
153 static void selinux_update_mapping (void);
156 static void new_spent (struct spwd *);
157 static void fail_exit (int);
158 static void update_group (void);
161 static void update_gshadow (void);
163 static void grp_update (void);
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);
173 #ifndef NO_MOVE_MAILBOX
174 static void move_mailbox (void);
177 static void date_to_str (char *buf, size_t maxsize,
178 long int date, const char *negativ)
182 if ((negativ != NULL) && (date < 0)) {
183 strncpy (buf, negativ, maxsize);
185 time_t t = (time_t) date;
188 strftime (buf, maxsize, "%Y-%m-%d", tp);
190 snprintf (buf, maxsize, "%04d-%02d-%02d",
191 tp->tm_year + 1900, tp->tm_mon + 1, tp->tm_mday);
192 #endif /* HAVE_STRFTIME */
194 buf[maxsize - 1] = '\0';
198 * get_groups - convert a list of group names to an array of group IDs
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.
204 static int get_groups (char *list)
207 const struct group *grp;
212 * Initialize the list to be empty
214 user_groups[0] = (char *) 0;
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.
227 * Strip off a single name from the list
229 cp = strchr (list, ',');
236 * Names starting with digits are treated as numerical GID
237 * values, otherwise the string is looked up as is.
239 grp = getgr_nam_gid (list);
242 * There must be a match, either by GID value or by
246 fprintf (stderr, _("%s: group '%s' does not exist\n"),
253 * If the group doesn't exist, don't dump core. Instead,
254 * try the next one. --marekm
262 * Don't add this group if they are an NIS group. Tell the
263 * user to go to the server for this group.
267 _("%s: group '%s' is a NIS group.\n"),
273 if (ngroups == sys_ngroups) {
275 _("%s: too many groups specified (max %d).\n"),
281 * Add the group name to the user's list of groups.
283 user_groups[ngroups++] = xstrdup (grp->gr_name);
284 } while (NULL != list);
286 user_groups[ngroups] = (char *) 0;
289 * Any errors in finding group names are fatal
299 * usage - display usage message and exit
301 static void usage (void)
304 _("Usage: usermod [options] LOGIN\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"
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"
330 _(" -Z, --selinux-user new SELinux user mapping for the user account\n")
339 * update encrypted password string (for both shadow and non-shadow
342 static char *new_pw_passwd (char *pw_pass)
344 if (Lflg && ('!' != pw_pass[0])) {
345 char *buf = xmalloc (strlen (pw_pass) + 2);
348 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
350 user_newname, (unsigned int) user_newid, 0);
352 SYSLOG ((LOG_INFO, "lock user '%s' password", user_newname));
354 strcat (buf, pw_pass);
356 } else if (Uflg && pw_pass[0] == '!') {
359 if (pw_pass[1] == '\0') {
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"),
368 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
370 user_newname, (unsigned int) user_newid, 0);
372 SYSLOG ((LOG_INFO, "unlock user '%s' password", user_newname));
380 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
382 user_newname, (unsigned int) user_newid, 1);
384 SYSLOG ((LOG_INFO, "change user '%s' password", user_newname));
385 pw_pass = xstrdup (user_pass);
391 * new_pwent - initialize the values in a password file entry
393 * new_pwent() takes all of the values that have been entered and fills
394 * in a (struct passwd) with them.
396 static void new_pwent (struct passwd *pwent)
399 if (pw_locate (user_newname) != NULL) {
401 _("%s: user '%s' already exists in %s\n"),
402 Prog, user_newname, pw_dbname ());
403 fail_exit (E_NAME_IN_USE);
406 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
408 user_newname, (unsigned int) user_newid, 1);
411 "change user name '%s' to '%s'",
412 pwent->pw_name, user_newname));
413 pwent->pw_name = xstrdup (user_newname);
415 if (!is_shadow_pwd) {
416 pwent->pw_passwd = new_pw_passwd (pwent->pw_passwd);
421 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
423 user_newname, (unsigned int) user_newid, 1);
426 "change user '%s' UID from '%d' to '%d'",
427 pwent->pw_name, pwent->pw_uid, user_newid));
428 pwent->pw_uid = user_newid;
432 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
433 "changing primary group",
434 user_newname, (unsigned int) user_newid, 1);
437 "change user '%s' GID from '%d' to '%d'",
438 pwent->pw_name, pwent->pw_gid, user_newgid));
439 pwent->pw_gid = user_newgid;
443 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
445 user_newname, (unsigned int) user_newid, 1);
447 pwent->pw_gecos = user_newcomment;
452 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
453 "changing home directory",
454 user_newname, (unsigned int) user_newid, 1);
457 "change user '%s' home from '%s' to '%s'",
458 pwent->pw_name, pwent->pw_dir, user_newhome));
459 pwent->pw_dir = user_newhome;
463 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
464 "changing user shell",
465 user_newname, (unsigned int) user_newid, 1);
468 "change user '%s' shell from '%s' to '%s'",
469 pwent->pw_name, pwent->pw_shell, user_newshell));
470 pwent->pw_shell = user_newshell;
475 * new_spent - initialize the values in a shadow password file entry
477 * new_spent() takes all of the values that have been entered and fills
478 * in a (struct spwd) with them.
480 static void new_spent (struct spwd *spent)
483 if (spw_locate (user_newname) != NULL) {
485 _("%s: user '%s' already exists in %s\n"),
486 Prog, user_newname, spw_dbname ());
487 fail_exit (E_NAME_IN_USE);
489 spent->sp_namp = xstrdup (user_newname);
494 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
495 "changing inactive days",
496 user_newname, (unsigned int) user_newid, 1);
499 "change user '%s' inactive from '%ld' to '%ld'",
500 spent->sp_namp, spent->sp_inact, user_newinactive));
501 spent->sp_inact = user_newinactive;
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");
511 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
512 "changing expiration date",
513 user_newname, (unsigned int) user_newid, 1);
516 "change user '%s' expiration from '%s' to '%s'",
517 spent->sp_namp, old_exp, new_exp));
518 spent->sp_expire = user_newexpire;
520 spent->sp_pwdp = new_pw_passwd (spent->sp_pwdp);
522 spent->sp_lstchg = (long) time ((time_t *) 0) / SCALE;
523 if (0 == spent->sp_lstchg) {
524 /* Better disable aging than requiring a password
526 spent->sp_lstchg = -1;
532 * fail_exit - exit with an error code after unlocking files
534 static void fail_exit (int code)
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 ()));
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 ()));
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 ()));
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 ()));
568 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
570 user_name, AUDIT_NO_ID, 0);
576 static void update_group (void)
581 const struct group *grp;
587 * Scan through the entire group file looking for the groups that
588 * the user is a member of.
590 while ((grp = gr_next ()) != NULL) {
592 * See if the user specified this group as one of their
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));
599 if (!was_member && !is_member) {
603 ngrp = __gr_dup (grp);
606 _("%s: Out of memory. Cannot update %s.\n"),
608 fail_exit (E_GRP_UPDATE);
611 if (was_member && (!Gflg || is_member)) {
613 ngrp->gr_mem = del_list (ngrp->gr_mem,
615 ngrp->gr_mem = add_list (ngrp->gr_mem,
619 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
620 "changing group member",
621 user_newname, AUDIT_NO_ID, 1);
624 "change '%s' to '%s' in group '%s'",
625 user_name, user_newname,
628 } else if (was_member && !aflg && Gflg && !is_member) {
629 ngrp->gr_mem = del_list (ngrp->gr_mem, user_name);
632 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
633 "removing group member",
634 user_name, AUDIT_NO_ID, 1);
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);
643 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
644 "adding user to group",
645 user_name, AUDIT_NO_ID, 1);
647 SYSLOG ((LOG_INFO, "add '%s' to group '%s'",
648 user_newname, ngrp->gr_name));
655 if (gr_update (ngrp) == 0) {
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);
666 static void update_gshadow (void)
672 const struct sgrp *sgrp;
678 * Scan through the entire shadow group file looking for the groups
679 * that the user is a member of.
681 while ((sgrp = sgr_next ()) != NULL) {
684 * See if the user was a member of this group
686 was_member = is_on_list (sgrp->sg_mem, user_name);
689 * See if the user was an administrator of this group
691 was_admin = is_on_list (sgrp->sg_adm, user_name);
694 * See if the user specified this group as one of their
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));
701 if (!was_member && !was_admin && !is_member) {
705 nsgrp = __sgr_dup (sgrp);
708 _("%s: Out of memory. Cannot update %s.\n"),
709 Prog, sgr_dbname ());
710 fail_exit (E_GRP_UPDATE);
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);
718 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
719 "changing admin name in shadow group",
720 user_name, AUDIT_NO_ID, 1);
723 "change admin '%s' to '%s' in shadow group '%s'",
724 user_name, user_newname, nsgrp->sg_name));
726 if (was_member && (!Gflg || is_member)) {
728 nsgrp->sg_mem = del_list (nsgrp->sg_mem,
730 nsgrp->sg_mem = add_list (nsgrp->sg_mem,
734 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
735 "changing member in shadow group",
736 user_name, AUDIT_NO_ID, 1);
739 "change '%s' to '%s' in shadow group '%s'",
740 user_name, user_newname,
743 } else if (was_member && !aflg && Gflg && !is_member) {
744 nsgrp->sg_mem = del_list (nsgrp->sg_mem, user_name);
747 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
748 "removing user from shadow group",
749 user_name, AUDIT_NO_ID, 1);
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);
758 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
759 "adding user to shadow group",
760 user_newname, AUDIT_NO_ID, 1);
762 SYSLOG ((LOG_INFO, "add '%s' to shadow group '%s'",
763 user_newname, nsgrp->sg_name));
772 * Update the group entry to reflect the changes.
774 if (sgr_update (nsgrp) == 0) {
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);
784 #endif /* SHADOWGRP */
787 * grp_update - add user to secondary group set
789 * grp_update() takes the secondary group set given in user_groups and
790 * adds the user to each group given by that set.
792 static void grp_update (void)
803 * process_flags - perform command line argument setting
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.
809 static void process_flags (int argc, char **argv)
811 const struct group *grp;
813 bool anyflag = false;
815 if ((1 == argc) || ('-' == argv[argc - 1][0])) {
820 const struct passwd *pwd;
821 /* local, no need for xgetpwnam */
822 pwd = getpwnam (argv[argc - 1]);
825 _("%s: user '%s' does not exist\n"),
826 Prog, argv[argc - 1]);
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);
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;
846 * Now make sure it isn't an NIS user.
853 _("%s: user %s is a NIS user\n"),
856 if ( !yp_get_default_domain (&nis_domain)
857 && !yp_master (nis_domain, "passwd.byname", &nis_master)) {
859 _("%s: %s is the NIS master\n"),
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;
879 * Parse the command line options.
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'},
897 {"selinux-user", required_argument, NULL, 'Z'},
899 {"shell", required_argument, NULL, 's'},
900 {"uid", required_argument, NULL, 'u'},
901 {"unlock", no_argument, NULL, 'U'},
902 {NULL, 0, NULL, '\0'}
904 while ((c = getopt_long (argc, argv,
906 "ac:d:e:f:g:G:hl:Lmop:s:u:UZ:",
908 "ac:d:e:f:g:G:hl:Lmop:s:u:U",
910 long_options, NULL)) != -1) {
916 if (!VALID (optarg)) {
918 _("%s: invalid field '%s'\n"),
922 user_newcomment = optarg;
926 if (!VALID (optarg)) {
928 _("%s: invalid field '%s'\n"),
933 user_newhome = optarg;
936 if ('\0' != *optarg) {
937 user_newexpire = strtoday (optarg);
938 if (user_newexpire == -1) {
940 _("%s: invalid date '%s'\n"),
944 user_newexpire *= DAY / SCALE;
951 if ( (getlong (optarg, &user_newinactive) == 0)
952 || (user_newinactive < -1)) {
954 _("%s: invalid numeric argument '%s'\n"),
961 grp = getgr_nam_gid (optarg);
964 _("%s: group '%s' does not exist\n"),
968 user_newgid = grp->gr_gid;
972 if (get_groups (optarg) != 0) {
978 if (!is_valid_user_name (optarg)) {
980 _("%s: invalid field '%s'\n"),
985 user_newname = optarg;
1001 if (!VALID (optarg)) {
1003 _("%s: invalid field '%s'\n"),
1007 user_newshell = optarg;
1011 if ( (get_uid (optarg, &user_newid) ==0)
1012 || (user_newid == (uid_t)-1)) {
1014 _("%s: invalid user ID '%s'\n"),
1025 if (is_selinux_enabled () > 0) {
1026 user_selinux = optarg;
1030 _("%s: -Z requires SELinux enabled kernel\n"),
1044 fprintf (stderr, _("%s: no flags given\n"), Prog);
1048 if (user_newid == user_id) {
1052 if (user_newgid == user_gid) {
1055 if (strcmp (user_newshell, user_shell) == 0) {
1058 if (strcmp (user_newname, user_name) == 0) {
1061 if (user_newinactive == user_inactive) {
1064 if (user_newexpire == user_expire) {
1067 if (strcmp (user_newhome, user_home) == 0) {
1071 if (strcmp (user_newcomment, user_comment) == 0) {
1075 if (!(Uflg || uflg || sflg || pflg || oflg || mflg || Lflg ||
1076 lflg || Gflg || gflg || fflg || eflg || dflg || cflg
1081 fprintf (stderr, _("%s: no changes\n"), Prog);
1085 if (!is_shadow_pwd && (eflg || fflg)) {
1087 _("%s: shadow passwords required for -e and -f\n"),
1092 if (optind != argc - 1) {
1096 if (aflg && (!Gflg)) {
1098 _("%s: %s flag is only allowed with the %s flag\n"),
1104 if ((Lflg && (pflg || Uflg)) || (pflg && Uflg)) {
1106 _("%s: the -L, -p, and -U flags are exclusive\n"),
1112 if (oflg && !uflg) {
1114 _("%s: %s flag is only allowed with the %s flag\n"),
1120 if (mflg && !dflg) {
1122 _("%s: %s flag is only allowed with the %s flag\n"),
1128 /* local, no need for xgetpwnam */
1129 if (lflg && (getpwnam (user_newname) != NULL)) {
1131 _("%s: user '%s' already exists\n"),
1132 Prog, user_newname);
1133 exit (E_NAME_IN_USE);
1136 /* local, no need for xgetpwuid */
1137 if (uflg && !oflg && (getpwuid (user_newid) != NULL)) {
1139 _("%s: UID '%lu' already exists\n"),
1140 Prog, (unsigned long) user_newid);
1141 exit (E_UID_IN_USE);
1146 * close_files - close all of the files that were opened
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.
1151 static void close_files (void)
1153 if (pw_close () == 0) {
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);
1160 if (is_shadow_pwd && (spw_close () == 0)) {
1162 _("%s: failure while writing changes to %s\n"),
1163 Prog, spw_dbname ());
1165 "failure while writing changes to %s",
1167 fail_exit (E_PW_UPDATE);
1171 if (gr_close () == 0) {
1173 _("%s: failure while writing changes to %s\n"),
1174 Prog, gr_dbname ());
1176 "failure while writing changes to %s",
1178 fail_exit (E_GRP_UPDATE);
1181 if (is_shadow_grp) {
1182 if (sgr_close () == 0) {
1184 _("%s: failure while writing changes to %s\n"),
1185 Prog, sgr_dbname ());
1187 "failure while writing changes to %s",
1189 fail_exit (E_GRP_UPDATE);
1191 if (sgr_unlock () == 0) {
1193 _("%s: failed to unlock %s\n"),
1194 Prog, sgr_dbname ());
1196 "failed to unlock %s",
1202 if (gr_unlock () == 0) {
1204 _("%s: failed to unlock %s\n"),
1205 Prog, gr_dbname ());
1207 "failed to unlock %s",
1213 if (is_shadow_pwd) {
1214 if (spw_unlock () == 0) {
1216 _("%s: failed to unlock %s\n"),
1217 Prog, spw_dbname ());
1219 "failed to unlock %s",
1224 if (pw_unlock () == 0) {
1226 _("%s: failed to unlock %s\n"),
1227 Prog, pw_dbname ());
1228 SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ()));
1240 * Close the DBM and/or flat files
1251 * open_files - lock and open the password files
1253 * open_files() opens the two password files.
1255 static void open_files (void)
1257 if (pw_lock () == 0) {
1259 _("%s: cannot lock %s; try again later.\n"),
1260 Prog, pw_dbname ());
1261 fail_exit (E_PW_UPDATE);
1264 if (pw_open (O_RDWR) == 0) {
1266 _("%s: cannot open %s\n"),
1267 Prog, pw_dbname ());
1268 fail_exit (E_PW_UPDATE);
1270 if (is_shadow_pwd && (spw_lock () == 0)) {
1272 _("%s: cannot lock %s; try again later.\n"),
1273 Prog, spw_dbname ());
1274 fail_exit (E_PW_UPDATE);
1277 if (is_shadow_pwd && (spw_open (O_RDWR) == 0)) {
1279 _("%s: cannot open %s\n"),
1280 Prog, spw_dbname ());
1281 fail_exit (E_PW_UPDATE);
1286 * Lock and open the group file. This will load all of the
1289 if (gr_lock () == 0) {
1291 _("%s: cannot lock %s; try again later.\n"),
1292 Prog, gr_dbname ());
1293 fail_exit (E_GRP_UPDATE);
1296 if (gr_open (O_RDWR) == 0) {
1298 _("%s: cannot open %s\n"),
1299 Prog, gr_dbname ());
1300 fail_exit (E_GRP_UPDATE);
1303 if (is_shadow_grp && (sgr_lock () == 0)) {
1305 _("%s: cannot lock %s; try again later.\n"),
1306 Prog, sgr_dbname ());
1307 fail_exit (E_GRP_UPDATE);
1310 if (is_shadow_grp && (sgr_open (O_RDWR) == 0)) {
1312 _("%s: cannot open %s\n"),
1313 Prog, sgr_dbname ());
1314 fail_exit (E_GRP_UPDATE);
1321 * usr_update - create the user entries
1323 * usr_update() creates the password file entries for this user and
1324 * will update the group entries if required.
1326 static void usr_update (void)
1328 struct passwd pwent;
1329 const struct passwd *pwd;
1332 const struct spwd *spwd = NULL;
1335 * Locate the entry in /etc/passwd, which MUST exist.
1337 pwd = pw_locate (user_name);
1340 _("%s: user '%s' does not exist in %s\n"),
1341 Prog, user_name, pw_dbname ());
1342 fail_exit (E_NOTFOUND);
1349 * Locate the entry in /etc/shadow. It doesn't have to exist, and
1350 * won't be created if it doesn't.
1352 if (is_shadow_pwd && ((spwd = spw_locate (user_name)) != NULL)) {
1357 if (lflg || uflg || gflg || cflg || dflg || sflg || pflg
1359 if (pw_update (&pwent) == 0) {
1361 _("%s: failed to prepare the new %s entry '%s'\n"),
1362 Prog, pw_dbname (), pwent.pw_name);
1363 fail_exit (E_PW_UPDATE);
1365 if (lflg && (pw_remove (user_name) == 0)) {
1367 _("%s: cannot remove entry '%s' from %s\n"),
1368 Prog, user_name, pw_dbname ());
1369 fail_exit (E_PW_UPDATE);
1372 if ((NULL != spwd) && (lflg || eflg || fflg || pflg || Lflg || Uflg)) {
1373 if (spw_update (&spent) == 0) {
1375 _("%s: failed to prepare the new %s entry '%s'\n"),
1376 Prog, spw_dbname (), spent.sp_namp);
1377 fail_exit (E_PW_UPDATE);
1379 if (lflg && (spw_remove (user_name) == 0)) {
1381 _("%s: cannot remove entry '%s' from %s\n"),
1382 Prog, user_name, spw_dbname ());
1383 fail_exit (E_PW_UPDATE);
1389 * move_home - move the user's home directory
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.
1394 static void move_home (void)
1398 if (mflg && (stat (user_home, &sb) == 0)) {
1400 * Don't try to move it if it is not a directory
1401 * (but /dev/null for example). --marekm
1403 if (!S_ISDIR (sb.st_mode)) {
1407 if (access (user_newhome, F_OK) == 0) {
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) {
1416 _("%s: can't create %s\n"),
1417 Prog, user_newhome);
1419 if (chown (user_newhome, sb.st_uid, sb.st_gid) != 0) {
1421 _("%s: can't chown %s\n"),
1422 Prog, user_newhome);
1423 rmdir (user_newhome);
1424 fail_exit (E_HOMEDIR);
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) {
1431 _("%s: warning: failed to completely remove old home directory %s"),
1435 audit_logger (AUDIT_USER_CHAUTHTOK,
1437 "moving home directory",
1439 (unsigned int) user_newid,
1445 /* TODO: do some cleanup if the copy
1447 (void) remove_tree (user_newhome);
1450 _("%s: cannot rename directory %s to %s\n"),
1451 Prog, user_home, user_newhome);
1452 fail_exit (E_HOMEDIR);
1455 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
1456 "moving home directory",
1457 user_newname, (unsigned int) user_newid, 1);
1462 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
1463 "changing home directory owner",
1464 user_newname, (unsigned int) user_newid, 1);
1466 chown (dflg ? user_newhome : user_home,
1467 uflg ? user_newid : user_id,
1468 gflg ? user_newgid : user_gid);
1473 * update_lastlog - update the lastlog file
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.
1479 static void update_lastlog (void)
1483 off_t off_uid = (off_t) user_id * sizeof ll;
1484 off_t off_newuid = (off_t) user_newid * sizeof ll;
1486 if (access (LASTLOG_FILE, F_OK) != 0) {
1490 fd = open (LASTLOG_FILE, O_RDWR);
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));
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)) {
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));
1511 /* Assume lseek or read failed because there is
1512 * no entry for the old UID */
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)) {
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));
1534 * update_faillog - update the faillog file
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.
1540 static void update_faillog (void)
1544 off_t off_uid = (off_t) user_id * sizeof fl;
1545 off_t off_newuid = (off_t) user_newid * sizeof fl;
1547 if (access (FAILLOG_FILE, F_OK) != 0) {
1551 fd = open (FAILLOG_FILE, O_RDWR);
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));
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)) {
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));
1572 /* Assume lseek or read failed because there is
1573 * no entry for the old UID */
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)) {
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));
1593 #ifndef NO_MOVE_MAILBOX
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
1601 static void move_mailbox (void)
1603 const char *maildir;
1604 char mailfile[1024], newmailfile[1024];
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;
1614 if (NULL == maildir) {
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
1624 snprintf (mailfile, sizeof mailfile, "%s/%s", maildir, user_name);
1625 fd = open (mailfile, O_RDONLY | O_NONBLOCK, 0);
1627 /* no need for warnings if the mailbox doesn't exist */
1628 if (errno != ENOENT) {
1633 if (fstat (fd, &st) < 0) {
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);
1646 if (fchown (fd, user_newid, (gid_t) - 1) < 0) {
1647 perror (_("failed to change mailbox owner"));
1651 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
1652 "changing mail file owner",
1653 user_newname, (unsigned int) user_newid, 1);
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"));
1669 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
1670 "changing mail file name",
1671 user_newname, (unsigned int) user_newid, 1);
1679 * main - usermod command
1681 int main (int argc, char **argv)
1683 #ifdef ACCT_TOOLS_SETUID
1685 pam_handle_t *pamh = NULL;
1687 #endif /* USE_PAM */
1688 #endif /* ACCT_TOOLS_SETUID */
1695 * Get my name so that I can use it to report errors.
1697 Prog = Basename (argv[0]);
1699 (void) setlocale (LC_ALL, "");
1700 (void) bindtextdomain (PACKAGE, LOCALEDIR);
1701 (void) textdomain (PACKAGE);
1703 sys_ngroups = sysconf (_SC_NGROUPS_MAX);
1704 user_groups = (char **) malloc (sizeof (char *) * (1 + sys_ngroups));
1705 user_groups[0] = (char *) 0;
1707 OPENLOG ("usermod");
1709 is_shadow_pwd = spw_file_present ();
1711 is_shadow_grp = sgr_file_present ();
1714 process_flags (argc, argv);
1716 #ifdef ACCT_TOOLS_SETUID
1719 struct passwd *pampw;
1720 pampw = getpwuid (getuid ()); /* local, no need for xgetpwuid */
1721 if (pampw == NULL) {
1723 _("%s: Cannot determine your user name.\n"),
1728 retval = pam_start ("usermod", pampw->pw_name, &conv, &pamh);
1731 if (PAM_SUCCESS == retval) {
1732 retval = pam_authenticate (pamh, 0);
1735 if (PAM_SUCCESS == retval) {
1736 retval = pam_acct_mgmt (pamh, 0);
1740 (void) pam_end (pamh, retval);
1742 if (PAM_SUCCESS != retval) {
1743 fprintf (stderr, _("%s: PAM authentication failed\n"), Prog);
1746 #endif /* USE_PAM */
1747 #endif /* ACCT_TOOLS_SETUID */
1750 * Do the hard stuff - open the files, change the user entries,
1751 * change the home directory, then close and update the files.
1754 if ( cflg || dflg || eflg || fflg || gflg || Lflg || lflg || pflg
1755 || sflg || uflg || Uflg) {
1763 nscd_flush_cache ("passwd");
1764 nscd_flush_cache ("group");
1768 selinux_update_mapping ();
1776 #ifndef NO_MOVE_MAILBOX
1787 * Change the UID on all of the files owned by `user_id' to
1788 * `user_newid' in the user's home directory.
1790 chown_tree (dflg ? user_newhome : user_home,
1791 user_id, user_newid,
1792 user_gid, gflg ? user_newgid : user_gid);
1800 static void selinux_update_mapping (void) {
1801 const char *argv[7];
1803 if (is_selinux_enabled () <= 0) return;
1805 if (*user_selinux) {
1806 argv[0] = "/usr/sbin/semanage";
1810 argv[4] = user_selinux;
1811 argv[5] = user_name;
1813 if (safe_system (argv[0], argv, NULL, 1)) {
1815 if (safe_system (argv[0], argv, NULL, 0)) {
1817 _("%s: warning: the user name %s to %s SELinux user mapping failed.\n"),
1818 Prog, user_name, user_selinux);
1820 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
1821 "modifying User mapping ",
1822 user_name, (unsigned int) user_id, 0);