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 - 2008, 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;
101 static char *user_newshell;
102 static long user_expire;
103 static long user_newexpire;
104 static long user_inactive;
105 static long user_newinactive;
106 static long sys_ngroups;
107 static char **user_groups; /* NULL-terminated list */
110 aflg = false, /* append to existing secondary group set */
111 cflg = false, /* new comment (GECOS) field */
112 dflg = false, /* new home directory */
113 eflg = false, /* days since 1970-01-01 when account becomes expired */
114 fflg = false, /* days until account with expired password is locked */
115 gflg = false, /* new primary group ID */
116 Gflg = false, /* new secondary group set */
117 Lflg = false, /* lock the password */
118 lflg = false, /* new user name */
119 mflg = false, /* create user's home directory if it doesn't exist */
120 oflg = false, /* permit non-unique user ID to be specified with -u */
121 pflg = false, /* new encrypted password */
122 sflg = false, /* new shell program */
123 uflg = false, /* specify new user ID */
124 Uflg = false; /* unlock the password */
126 static bool is_shadow_pwd;
129 static bool is_shadow_grp;
132 static bool pw_locked = false;
133 static bool spw_locked = false;
134 static bool gr_locked = false;
136 static bool sgr_locked = false;
140 /* local function prototypes */
141 static void date_to_str (char *buf, size_t maxsize,
142 long int date, const char *negativ);
143 static int get_groups (char *);
144 static void usage (void);
145 static void new_pwent (struct passwd *);
147 static void new_spent (struct spwd *);
148 static void fail_exit (int);
149 static void update_group (void);
152 static void update_gshadow (void);
154 static void grp_update (void);
156 static long get_number (const char *);
157 static uid_t get_id (const char *);
158 static void process_flags (int, char **);
159 static void close_files (void);
160 static void open_files (void);
161 static void usr_update (void);
162 static void move_home (void);
163 static void update_lastlog (void);
164 static void update_faillog (void);
166 #ifndef NO_MOVE_MAILBOX
167 static void move_mailbox (void);
170 static void date_to_str (char *buf, size_t maxsize,
171 long int date, const char *negativ)
175 if ((negativ != NULL) && (date < 0)) {
176 strncpy (buf, negativ, maxsize);
178 time_t t = (time_t) date;
181 strftime (buf, maxsize, "%Y-%m-%d", tp);
183 snprintf (buf, maxsize, "%04d-%02d-%02d",
184 tp->tm_year + 1900, tp->tm_mon + 1, tp->tm_mday);
185 #endif /* HAVE_STRFTIME */
187 buf[maxsize - 1] = '\0';
190 * Had to move this over from useradd.c since we have groups named
191 * "56k-family"... ergh.
194 static struct group *getgr_nam_gid (const char *grname)
199 val = strtol (grname, &errptr, 10);
200 if (*grname != '\0' && *errptr == '\0' && errno != ERANGE && val >= 0) {
201 return xgetgrgid ((gid_t) val);
203 return xgetgrnam (grname);
207 * get_groups - convert a list of group names to an array of group IDs
209 * get_groups() takes a comma-separated list of group names and
210 * converts it to a NULL-terminated array. Any unknown group names are
211 * reported as errors.
213 static int get_groups (char *list)
216 const struct group *grp;
221 * Initialize the list to be empty
223 user_groups[0] = (char *) 0;
230 * So long as there is some data to be converted, strip off each
231 * name and look it up. A mix of numerical and string values for
232 * group identifiers is permitted.
236 * Strip off a single name from the list
238 cp = strchr (list, ',');
245 * Names starting with digits are treated as numerical GID
246 * values, otherwise the string is looked up as is.
248 grp = getgr_nam_gid (list);
251 * There must be a match, either by GID value or by
255 fprintf (stderr, _("%s: group '%s' does not exist\n"),
262 * If the group doesn't exist, don't dump core. Instead,
263 * try the next one. --marekm
271 * Don't add this group if they are an NIS group. Tell the
272 * user to go to the server for this group.
276 _("%s: group '%s' is a NIS group.\n"),
282 if (ngroups == sys_ngroups) {
284 _("%s: too many groups specified (max %d).\n"),
290 * Add the group name to the user's list of groups.
292 user_groups[ngroups++] = xstrdup (grp->gr_name);
293 } while (NULL != list);
295 user_groups[ngroups] = (char *) 0;
298 * Any errors in finding group names are fatal
308 * usage - display usage message and exit
310 static void usage (void)
312 fputs (_("Usage: usermod [options] LOGIN\n"
315 " -c, --comment COMMENT new value of the GECOS field\n"
316 " -d, --home HOME_DIR new home directory for the user account\n"
317 " -e, --expiredate EXPIRE_DATE set account expiration date to EXPIRE_DATE\n"
318 " -f, --inactive INACTIVE set password inactive after expiration\n"
320 " -g, --gid GROUP force use GROUP as new primary group\n"
321 " -G, --groups GROUPS new list of supplementary GROUPS\n"
322 " -a, --append append the user to the supplemental GROUPS\n"
323 " mentioned by the -G option without removing\n"
324 " him/her from other groups\n"
325 " -h, --help display this help message and exit\n"
326 " -l, --login NEW_LOGIN new value of the login name\n"
327 " -L, --lock lock the user account\n"
328 " -m, --move-home move contents of the home directory to the\n"
329 " new location (use only with -d)\n"
330 " -o, --non-unique allow using duplicate (non-unique) UID\n"
331 " -p, --password PASSWORD use encrypted password for the new password\n"
332 " -s, --shell SHELL new login shell for the user account\n"
333 " -u, --uid UID new UID for the user account\n"
334 " -U, --unlock unlock the user account\n"
340 * update encrypted password string (for both shadow and non-shadow
343 static char *new_pw_passwd (char *pw_pass)
345 if (Lflg && ('!' != pw_pass[0])) {
346 char *buf = xmalloc (strlen (pw_pass) + 2);
349 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
351 user_newname, (unsigned int) user_newid, 0);
353 SYSLOG ((LOG_INFO, "lock user '%s' password", user_newname));
355 strcat (buf, pw_pass);
357 } else if (Uflg && pw_pass[0] == '!') {
360 if (pw_pass[1] == '\0') {
362 _("%s: unlocking the user's password would result in a passwordless account.\n"
363 "You should set a password with usermod -p to unlock this user's password.\n"),
369 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
371 user_newname, (unsigned int) user_newid, 0);
373 SYSLOG ((LOG_INFO, "unlock user '%s' password", user_newname));
381 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
383 user_newname, (unsigned int) user_newid, 1);
385 SYSLOG ((LOG_INFO, "change user '%s' password", user_newname));
386 pw_pass = xstrdup (user_pass);
392 * new_pwent - initialize the values in a password file entry
394 * new_pwent() takes all of the values that have been entered and fills
395 * in a (struct passwd) with them.
397 static void new_pwent (struct passwd *pwent)
401 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
403 user_newname, (unsigned int) user_newid, 1);
405 SYSLOG ((LOG_INFO, "change user name '%s' to '%s'",
406 pwent->pw_name, user_newname));
407 pwent->pw_name = xstrdup (user_newname);
409 if (!is_shadow_pwd) {
411 new_pw_passwd (pwent->pw_passwd);
416 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
418 user_newname, (unsigned int) user_newid, 1);
421 "change user '%s' UID from '%d' to '%d'",
422 pwent->pw_name, pwent->pw_uid, user_newid));
423 pwent->pw_uid = user_newid;
427 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
428 "changing primary group",
429 user_newname, (unsigned int) user_newid, 1);
432 "change user '%s' GID from '%d' to '%d'",
433 pwent->pw_name, pwent->pw_gid, user_newgid));
434 pwent->pw_gid = user_newgid;
438 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
440 user_newname, (unsigned int) user_newid, 1);
442 pwent->pw_gecos = user_newcomment;
447 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
448 "changing home directory",
449 user_newname, (unsigned int) user_newid, 1);
452 "change user '%s' home from '%s' to '%s'",
453 pwent->pw_name, pwent->pw_dir, user_newhome));
454 pwent->pw_dir = user_newhome;
458 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
459 "changing user shell",
460 user_newname, (unsigned int) user_newid, 1);
462 SYSLOG ((LOG_INFO, "change user '%s' shell from '%s' to '%s'",
463 pwent->pw_name, pwent->pw_shell, user_newshell));
464 pwent->pw_shell = user_newshell;
469 * new_spent - initialize the values in a shadow password file entry
471 * new_spent() takes all of the values that have been entered and fills
472 * in a (struct spwd) with them.
474 static void new_spent (struct spwd *spent)
477 spent->sp_namp = xstrdup (user_newname);
482 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
483 "changing inactive days",
484 user_newname, (unsigned int) user_newid, 1);
487 "change user '%s' inactive from '%ld' to '%ld'",
488 spent->sp_namp, spent->sp_inact, user_newinactive));
489 spent->sp_inact = user_newinactive;
492 /* log dates rather than numbers of days. */
493 char new_exp[16], old_exp[16];
494 date_to_str (new_exp, sizeof(new_exp),
495 user_newexpire * DAY, "never");
496 date_to_str (old_exp, sizeof(old_exp),
497 user_expire * DAY, "never");
499 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
500 "changing expiration date",
501 user_newname, (unsigned int) user_newid, 1);
504 "change user '%s' expiration from '%s' to '%s'",
505 spent->sp_namp, old_exp, new_exp));
506 spent->sp_expire = user_newexpire;
508 spent->sp_pwdp = new_pw_passwd (spent->sp_pwdp);
510 spent->sp_lstchg = (long) time ((time_t *) 0) / SCALE;
515 * fail_exit - exit with an error code after unlocking files
517 static void fail_exit (int code)
520 if (gr_unlock () == 0) {
521 fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ());
522 SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ()));
528 if (sgr_unlock () == 0) {
529 fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ());
530 SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ()));
536 if (spw_unlock () == 0) {
537 fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ());
538 SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ()));
543 if (pw_unlock () == 0) {
544 fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ());
545 SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ()));
551 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
553 user_name, AUDIT_NO_ID, 0);
559 static void update_group (void)
564 const struct group *grp;
570 * Scan through the entire group file looking for the groups that
571 * the user is a member of.
573 while ((grp = gr_next ()) != NULL) {
575 * See if the user specified this group as one of their
578 was_member = is_on_list (grp->gr_mem, user_name);
579 is_member = Gflg && ( (was_member && aflg)
580 || is_on_list (user_groups, grp->gr_name));
582 if (!was_member && !is_member) {
586 ngrp = __gr_dup (grp);
589 _("%s: Out of memory. Cannot update %s.\n"),
591 fail_exit (E_GRP_UPDATE);
594 if (was_member && (!Gflg || is_member)) {
596 ngrp->gr_mem = del_list (ngrp->gr_mem,
598 ngrp->gr_mem = add_list (ngrp->gr_mem,
602 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
603 "changing group member",
604 user_newname, AUDIT_NO_ID, 1);
607 "change '%s' to '%s' in group '%s'",
608 user_name, user_newname,
611 } else if (was_member && !aflg && Gflg && !is_member) {
612 ngrp->gr_mem = del_list (ngrp->gr_mem, user_name);
615 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
616 "removing group member",
617 user_name, AUDIT_NO_ID, 1);
619 SYSLOG ((LOG_INFO, "delete '%s' from group '%s'",
620 user_name, ngrp->gr_name));
621 } else if (!was_member && Gflg && is_member) {
622 ngrp->gr_mem = add_list (ngrp->gr_mem, user_newname);
625 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
626 "adding user to group",
627 user_name, AUDIT_NO_ID, 1);
629 SYSLOG ((LOG_INFO, "add '%s' to group '%s'",
630 user_newname, ngrp->gr_name));
637 if (gr_update (ngrp) == 0) {
639 _("%s: failed to prepare the new %s entry '%s'\n"),
640 Prog, gr_dbname (), ngrp->gr_name);
641 SYSLOG ((LOG_WARN, "failed to prepare the new %s entry '%s'", gr_dbname (), ngrp->gr_name));
642 fail_exit (E_GRP_UPDATE);
648 static void update_gshadow (void)
654 const struct sgrp *sgrp;
660 * Scan through the entire shadow group file looking for the groups
661 * that the user is a member of.
663 while ((sgrp = sgr_next ()) != NULL) {
666 * See if the user was a member of this group
668 was_member = is_on_list (sgrp->sg_mem, user_name);
671 * See if the user was an administrator of this group
673 was_admin = is_on_list (sgrp->sg_adm, user_name);
676 * See if the user specified this group as one of their
679 is_member = Gflg && is_on_list (user_groups, sgrp->sg_name);
680 is_member = Gflg && ( (was_member && aflg)
681 || is_on_list (user_groups, sgrp->sg_name));
683 if (!was_member && !was_admin && !is_member) {
687 nsgrp = __sgr_dup (sgrp);
690 _("%s: Out of memory. Cannot update %s.\n"),
691 Prog, sgr_dbname ());
692 fail_exit (E_GRP_UPDATE);
695 if (was_admin && lflg) {
696 nsgrp->sg_adm = del_list (nsgrp->sg_adm, user_name);
697 nsgrp->sg_adm = add_list (nsgrp->sg_adm, user_newname);
700 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
701 "changing admin name in shadow group",
702 user_name, AUDIT_NO_ID, 1);
705 "change admin '%s' to '%s' in shadow group '%s'",
706 user_name, user_newname, nsgrp->sg_name));
708 if (was_member && (!Gflg || is_member)) {
710 nsgrp->sg_mem = del_list (nsgrp->sg_mem,
712 nsgrp->sg_mem = add_list (nsgrp->sg_mem,
716 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
717 "changing member in shadow group",
718 user_name, AUDIT_NO_ID, 1);
721 "change '%s' to '%s' in shadow group '%s'",
722 user_name, user_newname,
725 } else if (was_member && !aflg && Gflg && !is_member) {
726 nsgrp->sg_mem = del_list (nsgrp->sg_mem, user_name);
729 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
730 "removing user from shadow group",
731 user_name, AUDIT_NO_ID, 1);
734 "delete '%s' from shadow group '%s'",
735 user_name, nsgrp->sg_name));
736 } else if (!was_member && Gflg && is_member) {
737 nsgrp->sg_mem = add_list (nsgrp->sg_mem, user_newname);
740 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
741 "adding user to shadow group",
742 user_newname, AUDIT_NO_ID, 1);
744 SYSLOG ((LOG_INFO, "add '%s' to shadow group '%s'",
745 user_newname, nsgrp->sg_name));
754 * Update the group entry to reflect the changes.
756 if (sgr_update (nsgrp) == 0) {
758 _("%s: failed to prepare the new %s entry '%s'\n"),
759 Prog, sgr_dbname (), nsgrp->sg_name);
760 SYSLOG ((LOG_WARN, "failed to prepare the new %s entry '%s'",
761 sgr_dbname (), nsgrp->sg_name));
762 fail_exit (E_GRP_UPDATE);
766 #endif /* SHADOWGRP */
769 * grp_update - add user to secondary group set
771 * grp_update() takes the secondary group set given in user_groups and
772 * adds the user to each group given by that set.
774 static void grp_update (void)
784 static long get_number (const char *numstr)
789 val = strtol (numstr, &errptr, 10);
790 if (('\0' != *errptr) || (ERANGE == errno)) {
791 fprintf (stderr, _("%s: invalid numeric argument '%s'\n"), Prog,
798 static uid_t get_id (const char *uidstr)
803 val = strtol (uidstr, &errptr, 10);
804 if (('\0' != *errptr) || (ERANGE == errno) || (val < 0)) {
805 fprintf (stderr, _("%s: invalid numeric argument '%s'\n"), Prog,
813 * process_flags - perform command line argument setting
815 * process_flags() interprets the command line arguments and sets the
816 * values that the user will be created with accordingly. The values
817 * are checked for sanity.
819 static void process_flags (int argc, char **argv)
821 const struct group *grp;
823 bool anyflag = false;
825 if ((1 == argc) || ('-' == argv[argc - 1][0])) {
830 const struct passwd *pwd;
831 /* local, no need for xgetpwnam */
832 pwd = getpwnam (argv[argc - 1]);
834 fprintf (stderr, _("%s: user '%s' does not exist\n"),
835 Prog, argv[argc - 1]);
839 user_name = argv[argc - 1];
840 user_id = pwd->pw_uid;
841 user_gid = pwd->pw_gid;
842 user_comment = xstrdup (pwd->pw_gecos);
843 user_home = xstrdup (pwd->pw_dir);
844 user_shell = xstrdup (pwd->pw_shell);
846 user_newname = user_name;
847 user_newid = user_id;
848 user_newgid = user_gid;
849 user_newcomment = user_comment;
850 user_newhome = user_home;
851 user_newshell = user_shell;
855 * Now make sure it isn't an NIS user.
861 fprintf (stderr, _("%s: user %s is a NIS user\n"),
864 if ( !yp_get_default_domain (&nis_domain)
865 && !yp_master (nis_domain, "passwd.byname", &nis_master)) {
866 fprintf (stderr, _("%s: %s is the NIS master\n"),
874 const struct spwd *spwd = NULL;
875 /* local, no need for xgetspnam */
876 if (is_shadow_pwd && ((spwd = getspnam (user_name)) != NULL)) {
877 user_expire = spwd->sp_expire;
878 user_inactive = spwd->sp_inact;
879 user_newexpire = user_expire;
880 user_newinactive = user_inactive;
886 * Parse the command line options.
889 static struct option long_options[] = {
890 {"append", no_argument, NULL, 'a'},
891 {"comment", required_argument, NULL, 'c'},
892 {"home", required_argument, NULL, 'd'},
893 {"expiredate", required_argument, NULL, 'e'},
894 {"inactive", required_argument, NULL, 'f'},
895 {"gid", required_argument, NULL, 'g'},
896 {"groups", required_argument, NULL, 'G'},
897 {"help", no_argument, NULL, 'h'},
898 {"login", required_argument, NULL, 'l'},
899 {"lock", no_argument, NULL, 'L'},
900 {"move-home", no_argument, NULL, 'm'},
901 {"non-unique", no_argument, NULL, 'o'},
902 {"password", required_argument, NULL, 'p'},
903 {"shell", required_argument, NULL, 's'},
904 {"uid", required_argument, NULL, 'u'},
905 {"unlock", no_argument, NULL, 'U'},
906 {NULL, 0, NULL, '\0'}
909 getopt_long (argc, argv, "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) {
941 ("%s: invalid date '%s'\n"),
945 user_newexpire *= DAY / SCALE;
952 user_newinactive = get_number (optarg);
956 grp = getgr_nam_gid (optarg);
959 _("%s: group '%s' does not exist\n"),
963 user_newgid = grp->gr_gid;
967 if (get_groups (optarg) != 0) {
973 if (!is_valid_user_name (optarg)) {
975 _("%s: invalid field '%s'\n"),
980 user_newname = optarg;
996 if (!VALID (optarg)) {
998 _("%s: invalid field '%s'\n"),
1002 user_newshell = optarg;
1006 user_newid = get_id (optarg);
1020 fprintf (stderr, _("%s: no flags given\n"), Prog);
1024 if (user_newid == user_id) {
1028 if (user_newgid == user_gid) {
1031 if (strcmp (user_newshell, user_shell) == 0) {
1034 if (strcmp (user_newname, user_name) == 0) {
1037 if (user_newinactive == user_inactive) {
1040 if (user_newexpire == user_expire) {
1043 if (strcmp (user_newhome, user_home) == 0) {
1047 if (strcmp (user_newcomment, user_comment) == 0) {
1051 if (!(Uflg || uflg || sflg || pflg || oflg || mflg || Lflg ||
1052 lflg || Gflg || gflg || fflg || eflg || dflg || cflg)) {
1053 fprintf (stderr, _("%s: no changes\n"), Prog);
1057 if (!is_shadow_pwd && (eflg || fflg)) {
1060 ("%s: shadow passwords required for -e and -f\n"),
1065 if (optind != argc - 1) {
1069 if (aflg && (!Gflg)) {
1071 _("%s: %s flag is only allowed with the %s flag\n"),
1077 if ((Lflg && (pflg || Uflg)) || (pflg && Uflg)) {
1079 _("%s: the -L, -p, and -U flags are exclusive\n"),
1085 if (oflg && !uflg) {
1087 _("%s: %s flag is only allowed with the %s flag\n"),
1093 if (mflg && !dflg) {
1095 _("%s: %s flag is only allowed with the %s flag\n"),
1101 /* local, no need for xgetpwnam */
1102 if (lflg && (getpwnam (user_newname) != NULL)) {
1103 fprintf (stderr, _("%s: user '%s' already exists\n"), Prog, user_newname);
1104 exit (E_NAME_IN_USE);
1107 /* local, no need for xgetpwuid */
1108 if (uflg && !oflg && (getpwuid (user_newid) != NULL)) {
1109 fprintf (stderr, _("%s: UID '%lu' already exists\n"),
1110 Prog, (unsigned long) user_newid);
1111 exit (E_UID_IN_USE);
1116 * close_files - close all of the files that were opened
1118 * close_files() closes all of the files that were opened for this new
1119 * user. This causes any modified entries to be written out.
1121 static void close_files (void)
1123 if (pw_close () == 0) {
1125 _("%s: failure while writing changes to %s\n"),
1126 Prog, pw_dbname ());
1127 SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ()));
1128 fail_exit (E_PW_UPDATE);
1130 if (is_shadow_pwd && (spw_close () == 0)) {
1132 _("%s: failure while writing changes to %s\n"),
1133 Prog, spw_dbname ());
1134 SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ()));
1135 fail_exit (E_PW_UPDATE);
1139 if (gr_close () == 0) {
1141 _("%s: failure while writing changes to %s\n"),
1142 Prog, gr_dbname ());
1143 SYSLOG ((LOG_ERR, "failure while writing changes to %s", gr_dbname ()));
1144 fail_exit (E_GRP_UPDATE);
1147 if (is_shadow_grp) {
1148 if (sgr_close () == 0) {
1150 _("%s: failure while writing changes to %s\n"),
1151 Prog, sgr_dbname ());
1152 SYSLOG ((LOG_ERR, "failure while writing changes to %s", sgr_dbname ()));
1153 fail_exit (E_GRP_UPDATE);
1155 if (sgr_unlock () == 0) {
1156 fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ());
1157 SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ()));
1162 if (gr_unlock () == 0) {
1163 fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ());
1164 SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ()));
1169 if (is_shadow_pwd) {
1170 if (spw_unlock () == 0) {
1171 fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ());
1172 SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ()));
1176 if (pw_unlock () == 0) {
1177 fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ());
1178 SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ()));
1190 * Close the DBM and/or flat files
1201 * open_files - lock and open the password files
1203 * open_files() opens the two password files.
1205 static void open_files (void)
1207 if (pw_lock () == 0) {
1209 _("%s: cannot lock %s; try again later.\n"),
1210 Prog, pw_dbname ());
1211 fail_exit (E_PW_UPDATE);
1214 if (pw_open (O_RDWR) == 0) {
1216 _("%s: cannot open %s\n"), Prog, pw_dbname ());
1217 fail_exit (E_PW_UPDATE);
1219 if (is_shadow_pwd && (spw_lock () == 0)) {
1221 _("%s: cannot lock %s; try again later.\n"),
1222 Prog, spw_dbname ());
1223 fail_exit (E_PW_UPDATE);
1226 if (is_shadow_pwd && (spw_open (O_RDWR) == 0)) {
1228 _("%s: cannot open %s\n"), Prog, spw_dbname ());
1229 fail_exit (E_PW_UPDATE);
1234 * Lock and open the group file. This will load all of the
1237 if (gr_lock () == 0) {
1239 _("%s: cannot lock %s; try again later.\n"),
1240 Prog, gr_dbname ());
1241 fail_exit (E_GRP_UPDATE);
1244 if (gr_open (O_RDWR) == 0) {
1246 _("%s: cannot open %s\n"), Prog, gr_dbname ());
1247 fail_exit (E_GRP_UPDATE);
1250 if (is_shadow_grp && (sgr_lock () == 0)) {
1252 _("%s: cannot lock %s; try again later.\n"),
1253 Prog, sgr_dbname ());
1254 fail_exit (E_GRP_UPDATE);
1257 if (is_shadow_grp && (sgr_open (O_RDWR) == 0)) {
1259 _("%s: cannot open %s\n"), Prog, sgr_dbname ());
1260 fail_exit (E_GRP_UPDATE);
1267 * usr_update - create the user entries
1269 * usr_update() creates the password file entries for this user and
1270 * will update the group entries if required.
1272 static void usr_update (void)
1274 struct passwd pwent;
1275 const struct passwd *pwd;
1278 const struct spwd *spwd = NULL;
1281 * Locate the entry in /etc/passwd, which MUST exist.
1283 pwd = pw_locate (user_name);
1285 fprintf (stderr, _("%s: user '%s' does not exist in %s\n"),
1286 Prog, user_name, pw_dbname ());
1287 fail_exit (E_NOTFOUND);
1294 * Locate the entry in /etc/shadow. It doesn't have to exist, and
1295 * won't be created if it doesn't.
1297 if (is_shadow_pwd && ((spwd = spw_locate (user_name)) != NULL)) {
1302 if (lflg || uflg || gflg || cflg || dflg || sflg || pflg
1304 if (pw_update (&pwent) == 0) {
1306 _("%s: failed to prepare the new %s entry '%s'\n"),
1307 Prog, pw_dbname (), pwent.pw_name);
1308 fail_exit (E_PW_UPDATE);
1310 if (lflg && (pw_remove (user_name) == 0)) {
1312 _("%s: cannot remove entry '%s' from %s\n"),
1313 Prog, user_name, pw_dbname ());
1314 fail_exit (E_PW_UPDATE);
1317 if ((NULL != spwd) && (lflg || eflg || fflg || pflg || Lflg || Uflg)) {
1318 if (spw_update (&spent) == 0) {
1320 _("%s: failed to prepare the new %s entry '%s'\n"),
1321 Prog, spw_dbname (), spent.sp_namp);
1322 fail_exit (E_PW_UPDATE);
1324 if (lflg && (spw_remove (user_name) == 0)) {
1326 _("%s: cannot remove entry '%s' from %s\n"),
1327 Prog, user_name, spw_dbname ());
1328 fail_exit (E_PW_UPDATE);
1334 * move_home - move the user's home directory
1336 * move_home() moves the user's home directory to a new location. The
1337 * files will be copied if the directory cannot simply be renamed.
1339 static void move_home (void)
1343 if (mflg && (stat (user_home, &sb) == 0)) {
1345 * Don't try to move it if it is not a directory
1346 * (but /dev/null for example). --marekm
1348 if (!S_ISDIR (sb.st_mode)) {
1352 if (access (user_newhome, F_OK) == 0) {
1353 fprintf (stderr, _("%s: directory %s exists\n"),
1354 Prog, user_newhome);
1355 fail_exit (E_HOMEDIR);
1356 } else if (rename (user_home, user_newhome) != 0) {
1357 if (errno == EXDEV) {
1358 if (mkdir (user_newhome, sb.st_mode & 0777) != 0) {
1360 _("%s: can't create %s\n"),
1361 Prog, user_newhome);
1363 if (chown (user_newhome, sb.st_uid, sb.st_gid) != 0) {
1365 _("%s: can't chown %s\n"),
1366 Prog, user_newhome);
1367 rmdir (user_newhome);
1368 fail_exit (E_HOMEDIR);
1370 if (copy_tree (user_home, user_newhome,
1371 uflg ? (long int)user_newid : -1,
1372 gflg ? (long int)user_newgid : -1) == 0) {
1373 if (remove_tree (user_home) != 0) {
1376 ("%s: warning: failed to completely remove old home directory %s"),
1380 audit_logger (AUDIT_USER_CHAUTHTOK,
1382 "moving home directory",
1384 (unsigned int) user_newid,
1390 /* TODO: do some cleanup if the copy
1392 (void) remove_tree (user_newhome);
1395 _("%s: cannot rename directory %s to %s\n"),
1396 Prog, user_home, user_newhome);
1397 fail_exit (E_HOMEDIR);
1400 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
1401 "moving home directory",
1402 user_newname, (unsigned int) user_newid, 1);
1407 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
1408 "changing home directory owner",
1409 user_newname, (unsigned int) user_newid, 1);
1411 chown (dflg ? user_newhome : user_home,
1412 uflg ? user_newid : user_id,
1413 gflg ? user_newgid : user_gid);
1418 * update_lastlog - update the lastlog file
1420 * Relocate the "lastlog" entries for the user. The old entry is
1421 * left alone in case the UID was shared. It doesn't hurt anything
1422 * to just leave it be.
1424 static void update_lastlog (void)
1428 off_t off_uid = (off_t) user_id * sizeof ll;
1429 off_t off_newuid = (off_t) user_newid * sizeof ll;
1431 if (access (LASTLOG_FILE, F_OK) != 0) {
1435 fd = open (LASTLOG_FILE, O_RDWR);
1439 _("%s: failed to copy the lastlog entry of user %lu to user %lu: %s\n"),
1440 Prog, (unsigned long) user_id, (unsigned long) user_newid, strerror (errno));
1444 if ( (lseek (fd, off_uid, SEEK_SET) == off_uid)
1445 && (read (fd, &ll, sizeof ll) == (ssize_t) sizeof ll)) {
1446 /* Copy the old entry to its new location */
1447 if ( (lseek (fd, off_newuid, SEEK_SET) != off_newuid)
1448 || (write (fd, &ll, sizeof ll) != (ssize_t) sizeof ll)
1449 || (close (fd) != 0)) {
1451 _("%s: failed to copy the lastlog entry of user %lu to user %lu: %s\n"),
1452 Prog, (unsigned long) user_id, (unsigned long) user_newid, strerror (errno));
1455 /* Assume lseek or read failed because there is
1456 * no entry for the old UID */
1458 /* Check if the new UID already has an entry */
1459 if ( (lseek (fd, off_newuid, SEEK_SET) == off_newuid)
1460 && (read (fd, &ll, sizeof ll) == (ssize_t) sizeof ll)) {
1461 /* Reset the new uid's lastlog entry */
1462 memzero (&ll, sizeof (ll));
1463 if ( (lseek (fd, off_newuid, SEEK_SET) != off_newuid)
1464 || (write (fd, &ll, sizeof ll) != (ssize_t) sizeof ll)
1465 || (close (fd) != 0)) {
1467 _("%s: failed to copy the lastlog entry of user %lu to user %lu: %s\n"),
1468 Prog, (unsigned long) user_id, (unsigned long) user_newid, strerror (errno));
1477 * update_faillog - update the faillog file
1479 * Relocate the "faillog" entries for the user. The old entry is
1480 * left alone in case the UID was shared. It doesn't hurt anything
1481 * to just leave it be.
1483 static void update_faillog (void)
1487 off_t off_uid = (off_t) user_id * sizeof fl;
1488 off_t off_newuid = (off_t) user_newid * sizeof fl;
1490 if (access (FAILLOG_FILE, F_OK) != 0) {
1494 fd = open (FAILLOG_FILE, O_RDWR);
1498 _("%s: failed to copy the faillog entry of user %lu to user %lu: %s\n"),
1499 Prog, (unsigned long) user_id, (unsigned long) user_newid, strerror (errno));
1503 if ( (lseek (fd, off_uid, SEEK_SET) == off_uid)
1504 && (read (fd, (char *) &fl, sizeof fl) == (ssize_t) sizeof fl)) {
1505 /* Copy the old entry to its new location */
1506 if ( (lseek (fd, off_newuid, SEEK_SET) != off_newuid)
1507 || (write (fd, &fl, sizeof fl) != (ssize_t) sizeof fl)
1508 || (close (fd) != 0)) {
1510 _("%s: failed to copy the faillog entry of user %lu to user %lu: %s\n"),
1511 Prog, (unsigned long) user_id, (unsigned long) user_newid, strerror (errno));
1514 /* Assume lseek or read failed because there is
1515 * no entry for the old UID */
1517 /* Check if the new UID already has an entry */
1518 if ( (lseek (fd, off_newuid, SEEK_SET) == off_newuid)
1519 && (read (fd, &fl, sizeof fl) == (ssize_t) sizeof fl)) {
1520 /* Reset the new uid's lastlog entry */
1521 memzero (&fl, sizeof (fl));
1522 if ( (lseek (fd, off_newuid, SEEK_SET) != off_newuid)
1523 || (write (fd, &fl, sizeof fl) != (ssize_t) sizeof fl)
1524 || (close (fd) != 0)) {
1526 _("%s: failed to copy the faillog entry of user %lu to user %lu: %s\n"),
1527 Prog, (unsigned long) user_id, (unsigned long) user_newid, strerror (errno));
1535 #ifndef NO_MOVE_MAILBOX
1537 * This is the new and improved code to carefully chown/rename the user's
1538 * mailbox. Maybe I am too paranoid but the mail spool dir sometimes
1539 * happens to be mode 1777 (this makes mail user agents work without
1540 * being setgid mail, but is NOT recommended; they all should be fixed
1541 * to use movemail). --marekm
1543 static void move_mailbox (void)
1545 const char *maildir;
1546 char mailfile[1024], newmailfile[1024];
1550 maildir = getdef_str ("MAIL_DIR");
1551 #ifdef MAIL_SPOOL_DIR
1552 if ((NULL == maildir) && (getdef_str ("MAIL_FILE") == NULL)) {
1553 maildir = MAIL_SPOOL_DIR;
1556 if (NULL == maildir) {
1561 * O_NONBLOCK is to make sure open won't hang on mandatory locks.
1562 * We do fstat/fchown to make sure there are no races (someone
1563 * replacing /var/spool/mail/luser with a hard link to /etc/passwd
1564 * between stat and chown). --marekm
1566 snprintf (mailfile, sizeof mailfile, "%s/%s", maildir, user_name);
1567 fd = open (mailfile, O_RDONLY | O_NONBLOCK, 0);
1569 /* no need for warnings if the mailbox doesn't exist */
1570 if (errno != ENOENT) {
1575 if (fstat (fd, &st) < 0) {
1580 if (st.st_uid != user_id) {
1581 /* better leave it alone */
1582 fprintf (stderr, _("%s: warning: %s not owned by %s\n"),
1583 Prog, mailfile, user_name);
1588 if (fchown (fd, user_newid, (gid_t) - 1) < 0) {
1589 perror (_("failed to change mailbox owner"));
1593 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
1594 "changing mail file owner",
1595 user_newname, (unsigned int) user_newid, 1);
1603 snprintf (newmailfile, sizeof newmailfile, "%s/%s",
1604 maildir, user_newname);
1605 if ( (link (mailfile, newmailfile) != 0)
1606 || (unlink (mailfile) != 0)) {
1607 perror (_("failed to rename mailbox"));
1611 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
1612 "changing mail file name",
1613 user_newname, (unsigned int) user_newid, 1);
1621 * main - usermod command
1623 int main (int argc, char **argv)
1625 #ifdef ACCT_TOOLS_SETUID
1627 pam_handle_t *pamh = NULL;
1629 #endif /* USE_PAM */
1630 #endif /* ACCT_TOOLS_SETUID */
1637 * Get my name so that I can use it to report errors.
1639 Prog = Basename (argv[0]);
1641 (void) setlocale (LC_ALL, "");
1642 (void) bindtextdomain (PACKAGE, LOCALEDIR);
1643 (void) textdomain (PACKAGE);
1645 sys_ngroups = sysconf (_SC_NGROUPS_MAX);
1646 user_groups = (char **) malloc (sizeof (char *) * (1 + sys_ngroups));
1647 user_groups[0] = (char *) 0;
1649 OPENLOG ("usermod");
1651 is_shadow_pwd = spw_file_present ();
1653 is_shadow_grp = sgr_file_present ();
1656 process_flags (argc, argv);
1658 #ifdef ACCT_TOOLS_SETUID
1661 struct passwd *pampw;
1662 pampw = getpwuid (getuid ()); /* local, no need for xgetpwuid */
1663 if (pampw == NULL) {
1665 _("%s: Cannot determine your user name.\n"),
1670 retval = pam_start ("usermod", pampw->pw_name, &conv, &pamh);
1673 if (PAM_SUCCESS == retval) {
1674 retval = pam_authenticate (pamh, 0);
1677 if (PAM_SUCCESS == retval) {
1678 retval = pam_acct_mgmt (pamh, 0);
1682 (void) pam_end (pamh, retval);
1684 if (PAM_SUCCESS != retval) {
1685 fprintf (stderr, _("%s: PAM authentication failed\n"), Prog);
1688 #endif /* USE_PAM */
1689 #endif /* ACCT_TOOLS_SETUID */
1692 * Do the hard stuff - open the files, change the user entries,
1693 * change the home directory, then close and update the files.
1696 if ( cflg || dflg || eflg || fflg || gflg || Lflg || lflg || pflg
1697 || sflg || uflg || Uflg) {
1705 nscd_flush_cache ("passwd");
1706 nscd_flush_cache ("group");
1712 #ifndef NO_MOVE_MAILBOX
1723 * Change the UID on all of the files owned by `user_id' to
1724 * `user_newid' in the user's home directory.
1726 chown_tree (dflg ? user_newhome : user_home,
1727 user_id, user_newid,
1728 user_gid, gflg ? user_newgid : user_gid);