2 * Copyright 1991 - 1994, Julianne Frances Haugh
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of Julianne F. Haugh nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 RCSID(PKG_VER "$Id: usermod.c,v 1.18 2000/08/26 18:27:18 marekm Exp $")
35 #include <sys/types.h>
45 #include "prototypes.h"
59 * for E_GRP_UPDATE and E_NOSPACE (not used yet), other update requests
60 * will be implemented (as documented in the Solaris 2.x man page).
62 #define E_SUCCESS 0 /* success */
63 #define E_PW_UPDATE 1 /* can't update password file */
64 #define E_USAGE 2 /* invalid command syntax */
65 #define E_BAD_ARG 3 /* invalid argument to option */
66 #define E_UID_IN_USE 4 /* uid already in use (and no -o) */
67 /* #define E_BAD_PWFILE 5 */ /* passwd file contains errors */
68 #define E_NOTFOUND 6 /* specified user/group doesn't exist */
69 #define E_USER_BUSY 8 /* user to modify is logged in */
70 #define E_NAME_IN_USE 9 /* username already in use */
71 #define E_GRP_UPDATE 10 /* can't update group file */
72 /* #define E_NOSPACE 11 */ /* insufficient space to move home dir */
73 #define E_HOMEDIR 12 /* unable to complete home dir move */
75 #define VALID(s) (strcspn (s, ":\n") == strlen (s))
77 static char *user_name;
78 static char *user_newname;
79 static char *user_pass;
81 static uid_t user_newid;
82 static gid_t user_gid;
83 static gid_t user_newgid;
84 static char *user_comment;
85 static char *user_home;
86 static char *user_newhome;
87 static char *user_shell;
89 static long user_expire;
90 static long user_inactive;
92 static char *user_groups[NGROUPS_MAX+1]; /* NULL-terminated list */
97 static char *auth_arg;
98 static char user_auth[BUFSIZ];
99 static int Aflg = 0; /* specify user defined authentication method */
105 uflg = 0, /* specify new user ID */
106 oflg = 0, /* permit non-unique user ID to be specified with -u */
107 gflg = 0, /* new primary group ID */
108 Gflg = 0, /* new secondary group set */
109 dflg = 0, /* new home directory */
110 sflg = 0, /* new shell program */
111 cflg = 0, /* new comment (GECOS) field */
112 mflg = 0, /* create user's home directory if it doesn't exist */
114 fflg = 0, /* days until account with expired password is locked */
115 eflg = 0, /* days since 1970-01-01 when account becomes expired */
117 pflg = 0, /* new encrypted password */
118 lflg = 0; /* new user name */
121 extern int pw_dbm_mode;
123 extern int sp_dbm_mode;
125 extern int gr_dbm_mode;
127 extern int sg_dbm_mode;
132 static int is_shadow_pwd;
135 static int is_shadow_grp;
141 #include "sgroupio.h"
147 #include "shadowio.h"
153 /* local function prototypes */
154 static int get_groups(char *);
155 static void usage(void);
156 static void new_pwent(struct passwd *);
158 static void new_spent(struct spwd *);
160 static void fail_exit(int);
161 static int update_group(void);
163 static int update_gshadow(void);
165 static int grp_update(void);
167 static char *get_password(const char *);
168 static void split_auths(char *, char **);
169 static void update_auths(const char *, const char *, char *);
170 static void add_auths(const char *, const char *, char *);
171 static void delete_auths(const char *, const char *, char *);
172 static void convert_auth(char *, const char *, const char *);
173 static int valid_auth(const char *);
175 static long get_number(const char *);
176 static void process_flags(int, char **);
177 static void close_files(void);
178 static void open_files(void);
179 static void usr_update(void);
180 static void move_home(void);
181 static void update_files(void);
182 #ifndef NO_MOVE_MAILBOX
183 static void move_mailbox(void);
186 /* Had to move this over from useradd.c since we have groups named
187 * "56k-family"... ergh.
189 static struct group *
190 getgr_nam_gid(const char *name)
195 gid = strtol(name, &ep, 10);
196 if (*name != '\0' && *ep == '\0') /* valid numeric gid */
197 return getgrgid(gid);
199 return getgrnam(name);
204 * get_groups - convert a list of group names to an array of group IDs
206 * get_groups() takes a comma-separated list of group names and
207 * converts it to a NULL-terminated array. Any unknown group
208 * names are reported as errors.
212 get_groups(char *list)
215 const struct group *grp;
220 * Initialize the list to be empty
223 user_groups[0] = (char *) 0;
229 * So long as there is some data to be converted, strip off
230 * each name and look it up. A mix of numerical and string
231 * values for group identifiers is permitted.
236 * Strip off a single name from the list
238 if ((cp = strchr (list, ',')))
242 * Names starting with digits are treated as numerical
243 * GID values, otherwise the string is looked up as is.
245 grp = getgr_nam_gid(list);
248 * There must be a match, either by GID value or by
252 fprintf(stderr, _("%s: unknown group %s\n"),
259 * If the group doesn't exist, don't dump core...
260 * Instead, try the next one. --marekm
267 * Don't add this group if they are an NIS group. Tell
268 * the user to go to the server for this group.
272 fprintf(stderr, _("%s: group `%s' is a NIS group.\n"),
278 if (ngroups == NGROUPS_MAX) {
280 _("%s: too many groups specified (max %d).\n"),
286 * Add the group name to the user's list of groups.
289 user_groups[ngroups++] = xstrdup(grp->gr_name);
292 user_groups[ngroups] = (char *) 0;
295 * Any errors in finding group names are fatal
305 * usage - display usage message and exit
312 _("usage: %s\t[-u uid [-o]] [-g group] [-G group,...] \n"),
315 _("\t\t[-d home [-m]] [-s shell] [-c comment] [-l new_name]\n"));
316 fprintf(stderr, "\t\t");
318 fprintf(stderr, _("[-f inactive] [-e expire ] "));
321 fprintf(stderr, _("[-A {DEFAULT|program},... ] "));
323 fprintf(stderr, _("[-p passwd] name\n"));
328 * new_pwent - initialize the values in a password file entry
330 * new_pwent() takes all of the values that have been entered and
331 * fills in a (struct passwd) with them.
335 new_pwent(struct passwd *pwent)
338 SYSLOG((LOG_INFO, "change user name `%s' to `%s'\n",
339 pwent->pw_name, user_newname));
340 pwent->pw_name = xstrdup (user_newname);
346 SYSLOG((LOG_INFO, "change user `%s' password\n",
348 pwent->pw_passwd = xstrdup(user_pass);
351 SYSLOG((LOG_INFO, "change user `%s' UID from `%d' to `%d'\n",
352 pwent->pw_name, pwent->pw_uid, user_newid));
353 pwent->pw_uid = user_newid;
356 SYSLOG((LOG_INFO, "change user `%s' GID from `%d' to `%d'\n",
357 pwent->pw_name, pwent->pw_gid, user_newgid));
358 pwent->pw_gid = user_newgid;
361 pwent->pw_gecos = user_comment;
364 SYSLOG((LOG_INFO, "change user `%s' home from `%s' to `%s'\n",
365 pwent->pw_name, pwent->pw_dir, user_newhome));
366 pwent->pw_dir = user_newhome;
369 SYSLOG((LOG_INFO, "change user `%s' shell from `%s' to `%s'\n",
370 pwent->pw_name, pwent->pw_shell, user_shell));
371 pwent->pw_shell = user_shell;
377 * new_spent - initialize the values in a shadow password file entry
379 * new_spent() takes all of the values that have been entered and
380 * fills in a (struct spwd) with them.
384 new_spent(struct spwd *spent)
387 spent->sp_namp = xstrdup (user_newname);
391 "change user `%s' inactive from `%ld' to `%ld'\n",
392 spent->sp_namp, spent->sp_inact, user_inactive));
393 spent->sp_inact = user_inactive;
396 /* XXX - dates might be better than numbers of days. --marekm */
398 "change user `%s' expiration from `%ld' to `%ld'\n",
399 spent->sp_namp, spent->sp_expire, user_expire));
400 spent->sp_expire = user_expire;
403 SYSLOG((LOG_INFO, "change user `%s' password\n",
405 spent->sp_pwdp = xstrdup(user_pass);
408 #endif /* SHADOWPWD */
411 * fail_exit - exit with an error code after unlocking files
437 const struct group *grp;
441 * Lock and open the group file. This will load all of the group
445 fprintf(stderr, _("%s: error locking group file\n"), Prog);
446 SYSLOG((LOG_ERR, "error locking group file"));
449 if (! gr_open (O_RDWR)) {
450 fprintf(stderr, _("%s: error opening group file\n"), Prog);
451 SYSLOG((LOG_ERR, "error opening group file"));
459 * Scan through the entire group file looking for the groups that
460 * the user is a member of.
462 while ((grp = gr_next())) {
465 * See if the user specified this group as one of their
468 was_member = is_on_list(grp->gr_mem, user_name);
469 is_member = Gflg && is_on_list(user_groups, grp->gr_name);
471 if (!was_member && !is_member)
474 ngrp = __gr_dup(grp);
477 _("%s: out of memory in update_group\n"),
483 if (was_member && (!Gflg || is_member)) {
485 ngrp->gr_mem = del_list(ngrp->gr_mem,
487 ngrp->gr_mem = add_list(ngrp->gr_mem,
491 "change `%s' to `%s' in group `%s'\n",
492 user_name, user_newname,
495 } else if (was_member && Gflg && !is_member) {
496 ngrp->gr_mem = del_list (ngrp->gr_mem, user_name);
498 SYSLOG((LOG_INFO, "delete `%s' from group `%s'\n",
499 user_name, ngrp->gr_name));
500 } else if (!was_member && Gflg && is_member) {
501 ngrp->gr_mem = add_list (ngrp->gr_mem,
502 lflg ? user_newname:user_name);
504 SYSLOG((LOG_INFO, "add `%s' to group `%s'\n",
505 lflg ? user_newname:user_name, ngrp->gr_name));
511 if (! gr_update (ngrp)) {
512 fprintf(stderr, _("%s: error adding new group entry\n"),
514 SYSLOG((LOG_ERR, "error adding group entry"));
520 * Update the DBM group file with the new entry as well.
522 if (! gr_dbm_update (ngrp)) {
524 _("%s: cannot add new dbm group entry\n"),
526 SYSLOG((LOG_ERR, "error adding dbm group entry"));
536 fprintf(stderr, _("%s: cannot rewrite group file\n"),
553 const struct sgrp *sgrp;
557 fprintf(stderr, _("%s: error locking shadow group file\n"),
559 SYSLOG((LOG_ERR, "error locking shadow group file"));
562 if (!sgr_open(O_RDWR)) {
563 fprintf(stderr, _("%s: error opening shadow group file\n"),
565 SYSLOG((LOG_ERR, "error opening shadow group file"));
573 * Scan through the entire shadow group file looking for the groups
574 * that the user is a member of.
576 while ((sgrp = sgr_next())) {
579 * See if the user was a member of this group
581 was_member = is_on_list(sgrp->sg_mem, user_name);
584 * See if the user was an administrator of this group
586 was_admin = is_on_list(sgrp->sg_adm, user_name);
589 * See if the user specified this group as one of their
592 is_member = Gflg && is_on_list(user_groups, sgrp->sg_name);
594 if (!was_member && !was_admin && !is_member)
597 nsgrp = __sgr_dup(sgrp);
600 _("%s: out of memory in update_gshadow\n"),
606 if (was_admin && lflg) {
607 nsgrp->sg_adm = del_list (nsgrp->sg_adm, user_name);
608 nsgrp->sg_adm = add_list (nsgrp->sg_adm, user_newname);
611 "change admin `%s' to `%s' in shadow group `%s'\n",
612 user_name, user_newname, nsgrp->sg_name));
614 if (was_member && (!Gflg || is_member)) {
616 nsgrp->sg_mem = del_list (nsgrp->sg_mem,
618 nsgrp->sg_mem = add_list (nsgrp->sg_mem,
622 "change `%s' to `%s' in shadow group `%s'\n",
623 user_name, user_newname, nsgrp->sg_name));
625 } else if (was_member && Gflg && !is_member) {
626 nsgrp->sg_mem = del_list (nsgrp->sg_mem, user_name);
629 "delete `%s' from shadow group `%s'\n",
630 user_name, nsgrp->sg_name));
631 } else if (!was_member && Gflg && is_member) {
632 nsgrp->sg_mem = add_list (nsgrp->sg_mem,
633 lflg ? user_newname:user_name);
635 SYSLOG((LOG_INFO, "add `%s' to shadow group `%s'\n",
636 lflg ? user_newname:user_name,nsgrp->sg_name));
644 * Update the group entry to reflect the changes.
646 if (! sgr_update (nsgrp)) {
648 _("%s: error adding new group entry\n"),
650 SYSLOG((LOG_ERR, "error adding shadow group entry\n"));
656 * Update the DBM group file with the new entry as well.
658 if (! sg_dbm_update (nsgrp)) {
660 _("%s: cannot add new dbm group entry\n"),
663 "error adding dbm shadow group entry\n"));
673 fprintf(stderr, _("%s: cannot rewrite shadow group file\n"),
681 #endif /* SHADOWGRP */
684 * grp_update - add user to secondary group set
686 * grp_update() takes the secondary group set given in user_groups
687 * and adds the user to each group given by that set.
695 ret = update_group();
697 if (!ret && is_shadow_grp)
698 ret = update_gshadow();
705 * get_password - locate encrypted password in authentication list
709 get_password(const char *list)
712 static char buf[257];
715 for (cp = buf;cp;cp = end) {
716 if ((end = strchr (cp, ';')))
728 * split_auths - break up comma list into (char *) array
732 split_auths(char *list, char **array)
737 for (cp = list;cp;cp = end) {
738 if ((end = strchr (cp, ';')))
747 * update_auths - find list of methods to update
751 update_auths(const char *old, const char *new, char *update)
753 char oldbuf[257], newbuf[257];
754 char *oldv[32], *newv[32], *updatev[32];
757 strcpy (oldbuf, old);
758 split_auths (oldbuf, oldv);
760 strcpy (newbuf, new);
761 split_auths (newbuf, newv);
763 for (i = j = k = 0;oldv[i];i++) {
764 for (j = 0;newv[j];j++)
765 if (strcmp (oldv[i], newv[j]) != 0)
768 if (newv[j] != (char *) 0)
769 updatev[k++] = oldv[i];
774 for (i = 0;updatev[i];i++) {
776 strcat (update, ";");
778 strcat (update, updatev[i]);
783 * add_auths - find list of methods to add
787 add_auths(const char *old, const char *new, char *add)
789 char oldbuf[257], newbuf[257];
790 char *oldv[32], *newv[32], *addv[32];
793 strcpy (oldbuf, old);
794 split_auths (oldbuf, oldv);
796 strcpy (newbuf, new);
797 split_auths (newbuf, newv);
799 for (i = j = k = 0;newv[i];i++) {
800 for (j = 0;oldv[j];j++)
801 if (strcmp (oldv[i], newv[j]) == 0)
804 if (oldv[j] == (char *) 0)
810 for (i = 0;addv[i];i++) {
814 strcat (add, addv[i]);
819 * delete_auths - find list of methods to delete
823 delete_auths(const char *old, const char *new, char *remove)
825 char oldbuf[257], newbuf[257];
826 char *oldv[32], *newv[32], *removev[32];
829 strcpy (oldbuf, old);
830 split_auths (oldbuf, oldv);
832 strcpy (newbuf, new);
833 split_auths (newbuf, newv);
835 for (i = j = k = 0;oldv[i];i++) {
836 for (j = 0;newv[j];j++)
837 if (strcmp (oldv[i], newv[j]) == 0)
840 if (newv[j] == (char *) 0)
841 removev[k++] = oldv[i];
846 for (i = 0;removev[i];i++) {
848 strcat (remove, ";");
850 strcat (remove, removev[i]);
855 * convert_auth - convert the argument list to a authentication list
859 convert_auth(char *auths, const char *oldauths, const char *list)
866 * Copy each method. DEFAULT is replaced by an encrypted string
867 * if one can be found in the current authentication list.
872 for (cp = buf;cp;cp = end) {
876 if ((end = strchr (cp, ',')))
879 if (strcmp (cp, "DEFAULT") == 0) {
880 if ((old = get_password (oldauths)))
892 * valid_auth - check authentication list for validity
896 valid_auth(const char *methods)
903 * Cursory checks, length and illegal characters
906 if ((int) strlen (methods) > 256)
909 if (! VALID (methods))
913 * Pick each method apart and check it.
916 strcpy (buf, methods);
917 for (cp = buf;cp;cp = end) {
918 if ((end = strchr (cp, ',')))
921 if (strcmp (cp, "DEFAULT") == 0) {
922 if (default_cnt++ > 0)
931 get_number(const char *cp)
936 val = strtol(cp, &ep, 10);
937 if (*cp != '\0' && *ep == '\0') /* valid number */
940 fprintf(stderr, _("%s: invalid numeric argument `%s'\n"), Prog, cp);
945 * process_flags - perform command line argument setting
947 * process_flags() interprets the command line arguments and sets
948 * the values that the user will be created with accordingly. The
949 * values are checked for sanity.
953 process_flags(int argc, char **argv)
955 const struct group *grp;
956 const struct passwd *pwd;
958 const struct spwd *spwd = NULL;
963 if (argc == 1 || argv[argc - 1][0] == '-')
966 if (! (pwd = getpwnam (argv[argc - 1]))) {
967 fprintf(stderr, _("%s: user %s does not exist\n"),
968 Prog, argv[argc - 1]);
971 user_name = argv[argc - 1];
976 * Now make sure it isn't an NIS user.
983 fprintf(stderr, _("%s: user %s is a NIS user\n"),
986 if (! yp_get_default_domain (&nis_domain) &&
987 ! yp_master (nis_domain, "passwd.byname",
989 fprintf(stderr, _("%s: %s is the NIS master\n"),
995 user_id = pwd->pw_uid;
996 user_gid = pwd->pw_gid;
997 user_comment = xstrdup(pwd->pw_gecos);
998 user_home = xstrdup(pwd->pw_dir);
999 user_shell = xstrdup(pwd->pw_shell);
1002 if (is_shadow_pwd && (spwd = getspnam (user_name))) {
1003 user_expire = spwd->sp_expire;
1004 user_inactive = spwd->sp_inact;
1008 #define FLAGS "A:u:og:G:d:s:c:mf:e:l:p:"
1010 #define FLAGS "A:u:og:G:d:s:c:ml:p:"
1012 while ((arg = getopt(argc, argv, FLAGS)) != EOF) {
1017 if (! valid_auth (optarg)) {
1019 _("%s: invalid field `%s'\n"),
1028 if (! VALID (optarg)) {
1030 _("%s: invalid field `%s'\n"),
1034 user_comment = optarg;
1038 if (! VALID (optarg)) {
1040 _("%s: invalid field `%s'\n"),
1045 user_newhome = optarg;
1050 user_expire = strtoday(optarg);
1051 if (user_expire == -1) {
1053 _("%s: invalid date `%s'\n"),
1057 user_expire *= DAY/SCALE;
1063 user_inactive = get_number(optarg);
1068 grp = getgr_nam_gid(optarg);
1071 _("%s: unknown group %s\n"),
1075 user_newgid = grp->gr_gid;
1079 if (get_groups(optarg))
1084 if (!check_user_name(optarg)) {
1086 _("%s: invalid field `%s'\n"),
1092 * If the name does not really change, we
1093 * mustn't set the flag as this will cause
1094 * rather serious problems later!
1097 if (strcmp (user_name, optarg))
1100 user_newname = optarg;
1119 if (! VALID (optarg)) {
1121 _("%s: invalid field `%s'\n"),
1125 user_shell = optarg;
1129 user_newid = get_number(optarg);
1138 fprintf(stderr, _("%s: no flags given\n"), Prog);
1143 if (!is_shadow_pwd && (eflg || fflg)) {
1145 _("%s: shadow passwords required for -e and -f\n"),
1151 if (optind != argc - 1)
1154 if (dflg && strcmp (user_home, user_newhome) == 0)
1157 if (uflg && user_id == user_newid)
1160 if (lflg && getpwnam (user_newname)) {
1161 fprintf(stderr, _("%s: user %s exists\n"), Prog, user_newname);
1162 exit(E_NAME_IN_USE);
1165 if (uflg && !oflg && getpwuid(user_newid)) {
1166 fprintf(stderr, _("%s: uid %ld is not unique\n"),
1167 Prog, (long) user_newid);
1173 * close_files - close all of the files that were opened
1175 * close_files() closes all of the files that were opened for this
1176 * new user. This causes any modified entries to be written out.
1182 if (! pw_close ()) {
1183 fprintf(stderr, _("%s: cannot rewrite password file\n"), Prog);
1184 fail_exit(E_PW_UPDATE);
1187 if (is_shadow_pwd && ! spw_close ()) {
1188 fprintf(stderr, _("%s: cannot rewrite shadow password file\n"),
1190 fail_exit(E_PW_UPDATE);
1197 (void) pw_unlock ();
1200 * Close the DBM and/or flat files
1214 * open_files - lock and open the password files
1216 * open_files() opens the two password files.
1223 fprintf(stderr, _("%s: unable to lock password file\n"), Prog);
1226 if (! pw_open (O_RDWR)) {
1227 fprintf(stderr, _("%s: unable to open password file\n"), Prog);
1228 fail_exit(E_PW_UPDATE);
1231 if (is_shadow_pwd && ! spw_lock ()) {
1232 fprintf(stderr, _("%s: cannot lock shadow password file\n"),
1234 fail_exit(E_PW_UPDATE);
1236 if (is_shadow_pwd && ! spw_open (O_RDWR)) {
1237 fprintf(stderr, _("%s: cannot open shadow password file\n"),
1239 fail_exit(E_PW_UPDATE);
1245 * usr_update - create the user entries
1247 * usr_update() creates the password file entries for this user
1248 * and will update the group entries if required.
1254 struct passwd pwent;
1255 const struct passwd *pwd;
1258 const struct spwd *spwd = NULL;
1261 char old_auth[BUFSIZ];
1262 char auth_buf[BUFSIZ];
1266 * Locate the entry in /etc/passwd, which MUST exist.
1269 pwd = pw_locate(user_name);
1271 fprintf(stderr, _("%s: %s not found in /etc/passwd\n"),
1273 fail_exit(E_NOTFOUND);
1281 * Locate the entry in /etc/shadow. It doesn't have to
1282 * exist, and won't be created if it doesn't.
1285 if (is_shadow_pwd && (spwd = spw_locate(user_name))) {
1294 strcpy (old_auth, spwd ? spent.sp_pwdp : pwent.pw_passwd);
1296 strcpy (old_auth, pwent.pw_passwd);
1300 convert_auth (user_auth, old_auth, auth_arg);
1303 * XXX - this code needs some checking, changing the user name with
1304 * "usermod -l new old" clears the password for this user :-(.
1305 * For now, just don't define AUTH_METHODS and all will be well.
1306 * Most programs don't support "administrator defined authentication
1307 * methods" and PAM (when done) will be better anyway :-). --marekm
1309 if (lflg || (Aflg && strcmp (old_auth, user_auth) != 0)) {
1310 delete_auths (old_auth, user_auth, auth_buf);
1311 if (auth_buf[0] && pw_auth (auth_buf, user_name,
1312 PW_DELETE, (char *) 0)) {
1314 _("%s: error deleting authentication method\n"),
1316 SYSLOG((LOG_ERR, "error deleting auth for `%s'\n",
1318 fail_exit(E_PW_UPDATE);
1320 add_auths (old_auth, user_auth, auth_buf);
1321 if (auth_buf[0] == '@' && pw_auth (auth_buf,
1322 lflg ? user_newname:user_name, PW_ADD, (char *) 0)) {
1324 _("%s: error adding authentication method\n"),
1326 SYSLOG((LOG_ERR, "error adding auth for `%s'\n",
1327 lflg ? user_newname:user_name));
1328 fail_exit(E_PW_UPDATE);
1330 update_auths (old_auth, user_auth, auth_buf);
1331 if (lflg && auth_buf[0] == '@' && pw_auth (auth_buf,
1332 user_newname, PW_CHANGE, user_name)) {
1334 _("%s: error changing authentication method\n"),
1336 SYSLOG((LOG_ERR, "error changing auth for `%s'\n",
1337 lflg ? user_newname:user_name));
1338 fail_exit(E_PW_UPDATE);
1342 spent.sp_pwdp = user_auth;
1345 pwent.pw_passwd = user_auth;
1347 #endif /* AUTH_METHODS */
1348 if (lflg || uflg || gflg || cflg || dflg || sflg || Aflg || pflg) {
1349 if (! pw_update (&pwent)) {
1351 _("%s: error changing password entry\n"),
1353 fail_exit(E_PW_UPDATE);
1355 if (lflg && ! pw_remove (user_name)) {
1357 _("%s: error removing password entry\n"),
1359 fail_exit(E_PW_UPDATE);
1362 if (pw_dbm_present()) {
1363 if (! pw_dbm_update (&pwent)) {
1365 _("%s: error adding password dbm entry\n"),
1367 fail_exit(E_PW_UPDATE);
1369 if (lflg && (pwd = getpwnam (user_name)) &&
1370 ! pw_dbm_remove (pwd)) {
1372 _("%s: error removing passwd dbm entry\n"),
1374 fail_exit(E_PW_UPDATE);
1380 if (spwd && (lflg || eflg || fflg || Aflg || pflg)) {
1381 if (! spw_update (&spent)) {
1383 _("%s: error adding new shadow password entry\n"),
1385 fail_exit(E_PW_UPDATE);
1387 if (lflg && ! spw_remove (user_name)) {
1389 _("%s: error removing shadow password entry\n"),
1391 fail_exit(E_PW_UPDATE);
1395 if (spwd && sp_dbm_present()) {
1396 if (! sp_dbm_update (&spent)) {
1398 _("%s: error updating shadow passwd dbm entry\n"),
1400 fail_exit(E_PW_UPDATE);
1402 if (lflg && ! sp_dbm_remove (user_name)) {
1404 _("%s: error removing shadow passwd dbm entry\n"),
1406 fail_exit(E_PW_UPDATE);
1410 #endif /* SHADOWPWD */
1414 * move_home - move the user's home directory
1416 * move_home() moves the user's home directory to a new location.
1417 * The files will be copied if the directory cannot simply be
1426 if (mflg && stat (user_home, &sb) == 0) {
1428 * Don't try to move it if it is not a directory
1429 * (but /dev/null for example). --marekm
1431 if (!S_ISDIR(sb.st_mode))
1434 if (access(user_newhome, F_OK) == 0) {
1435 fprintf(stderr, _("%s: directory %s exists\n"),
1436 Prog, user_newhome);
1437 fail_exit(E_HOMEDIR);
1438 } else if (rename (user_home, user_newhome)) {
1439 if (errno == EXDEV) {
1440 if (mkdir (user_newhome, sb.st_mode & 0777)) {
1442 _("%s: can't create %s\n"),
1443 Prog, user_newhome);
1445 if (chown (user_newhome,
1446 sb.st_uid, sb.st_gid)) {
1448 _("%s: can't chown %s\n"),
1449 Prog, user_newhome);
1450 rmdir (user_newhome);
1451 fail_exit(E_HOMEDIR);
1453 if (copy_tree (user_home, user_newhome,
1454 uflg ? user_newid:-1,
1455 gflg ? user_newgid:-1) == 0 &&
1456 remove_tree (user_home) == 0 &&
1457 rmdir (user_home) == 0)
1460 (void) remove_tree (user_newhome);
1461 (void) rmdir (user_newhome);
1464 _("%s: cannot rename directory %s to %s\n"),
1465 Prog, user_home, user_newhome);
1466 fail_exit(E_HOMEDIR);
1470 chown (dflg ? user_newhome:user_home,
1471 uflg ? user_newid:user_id,
1472 gflg ? user_newgid:user_gid);
1476 * update_files - update the lastlog and faillog files
1487 * Relocate the "lastlog" entries for the user. The old entry
1488 * is left alone in case the UID was shared. It doesn't hurt
1489 * anything to just leave it be.
1492 if ((fd = open(LASTLOG_FILE, O_RDWR)) != -1) {
1493 lseek(fd, (off_t) user_id * sizeof ll, SEEK_SET);
1494 if (read(fd, (char *) &ll, sizeof ll) == sizeof ll) {
1495 lseek(fd, (off_t) user_newid * sizeof ll, SEEK_SET);
1496 write(fd, (char *) &ll, sizeof ll);
1502 * Relocate the "faillog" entries in the same manner.
1505 if ((fd = open(FAILLOG_FILE, O_RDWR)) != -1) {
1506 lseek(fd, (off_t) user_id * sizeof fl, SEEK_SET);
1507 if (read(fd, (char *) &fl, sizeof fl) == sizeof fl) {
1508 lseek(fd, (off_t) user_newid * sizeof fl, SEEK_SET);
1509 write(fd, (char *) &fl, sizeof fl);
1515 #ifndef NO_MOVE_MAILBOX
1517 * This is the new and improved code to carefully chown/rename the user's
1518 * mailbox. Maybe I am too paranoid but the mail spool dir sometimes
1519 * happens to be mode 1777 (this makes mail user agents work without
1520 * being setgid mail, but is NOT recommended; they all should be fixed
1521 * to use movemail). --marekm
1526 const char *maildir;
1527 char mailfile[1024], newmailfile[1024];
1531 maildir = getdef_str("MAIL_DIR");
1532 #ifdef MAIL_SPOOL_DIR
1533 if (!maildir && !getdef_str("MAIL_FILE"))
1534 maildir = MAIL_SPOOL_DIR;
1540 * O_NONBLOCK is to make sure open won't hang on mandatory locks.
1541 * We do fstat/fchown to make sure there are no races (someone
1542 * replacing /var/spool/mail/luser with a hard link to /etc/passwd
1543 * between stat and chown). --marekm
1546 snprintf(mailfile, sizeof mailfile, "%s/%s", maildir, user_name);
1547 fd = open(mailfile, O_RDONLY | O_NONBLOCK, 0);
1549 /* no need for warnings if the mailbox doesn't exist */
1550 if (errno != ENOENT)
1554 if (fstat(fd, &st) < 0) {
1559 if (st.st_uid != user_id) {
1560 /* better leave it alone */
1561 fprintf(stderr, _("%s: warning: %s not owned by %s\n"),
1562 Prog, mailfile, user_name);
1566 if (uflg && fchown(fd, user_newid, (gid_t) -1) < 0)
1567 perror(_("failed to change mailbox owner"));
1572 snprintf(newmailfile, sizeof newmailfile, "%s/%s", maildir, user_newname);
1573 if (link(mailfile, newmailfile) || unlink(mailfile))
1574 perror(_("failed to rename mailbox"));
1580 * main - usermod command
1584 main(int argc, char **argv)
1589 * Get my name so that I can use it to report errors.
1591 Prog = Basename(argv[0]);
1593 setlocale(LC_ALL, "");
1594 bindtextdomain(PACKAGE, LOCALEDIR);
1595 textdomain(PACKAGE);
1597 openlog(Prog, LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_AUTH);
1600 is_shadow_pwd = spw_file_present();
1603 is_shadow_grp = sgr_file_present();
1607 * The open routines for the NDBM files don't use read-write
1608 * as the mode, so we have to clue them in.
1612 pw_dbm_mode = O_RDWR;
1614 sp_dbm_mode = O_RDWR;
1616 gr_dbm_mode = O_RDWR;
1618 sg_dbm_mode = O_RDWR;
1621 process_flags (argc, argv);
1624 * Do the hard stuff - open the files, change the user entries,
1625 * change the home directory, then close and update the files.
1635 grp_err = grp_update();
1640 #ifndef NO_MOVE_MAILBOX
1649 * Change the UID on all of the files owned by `user_id'
1650 * to `user_newid' in the user's home directory.
1653 chown_tree(dflg ? user_newhome:user_home,
1654 user_id, user_newid,
1655 user_gid, gflg ? user_newgid:user_gid);