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.32 2004/10/11 06:26:40 kloczek Exp $")
34 #include <sys/types.h>
44 #include <security/pam_appl.h>
45 #include <security/pam_misc.h>
48 #include "prototypes.h"
62 * for E_GRP_UPDATE and E_NOSPACE (not used yet), other update requests
63 * will be implemented (as documented in the Solaris 2.x man page).
65 #define E_SUCCESS 0 /* success */
66 #define E_PW_UPDATE 1 /* can't update password file */
67 #define E_USAGE 2 /* invalid command syntax */
68 #define E_BAD_ARG 3 /* invalid argument to option */
69 #define E_UID_IN_USE 4 /* uid already in use (and no -o) */
70 /* #define E_BAD_PWFILE 5 *//* passwd file contains errors */
71 #define E_NOTFOUND 6 /* specified user/group doesn't exist */
72 #define E_USER_BUSY 8 /* user to modify is logged in */
73 #define E_NAME_IN_USE 9 /* username already in use */
74 #define E_GRP_UPDATE 10 /* can't update group file */
75 /* #define E_NOSPACE 11 *//* insufficient space to move home dir */
76 #define E_HOMEDIR 12 /* unable to complete home dir move */
77 #define VALID(s) (strcspn (s, ":\n") == strlen (s))
78 static char *user_name;
79 static char *user_newname;
80 static char *user_pass;
82 static uid_t user_newid;
83 static gid_t user_gid;
84 static gid_t user_newgid;
85 static char *user_comment;
86 static char *user_home;
87 static char *user_newhome;
88 static char *user_shell;
91 static long user_expire;
92 static long user_inactive;
94 static long sys_ngroups;
95 static char **user_groups; /* NULL-terminated list */
100 uflg = 0, /* specify new user ID */
101 oflg = 0, /* permit non-unique user ID to be specified with -u */
102 gflg = 0, /* new primary group ID */
103 Gflg = 0, /* new secondary group set */
104 dflg = 0, /* new home directory */
105 sflg = 0, /* new shell program */
106 cflg = 0, /* new comment (GECOS) field */
107 mflg = 0, /* create user's home directory if it doesn't exist */
109 fflg = 0, /* days until account with expired password is locked */
110 eflg = 0, /* days since 1970-01-01 when account becomes expired */
112 Lflg = 0, /* lock the password */
113 Uflg = 0, /* unlock the password */
114 pflg = 0, /* new encrypted password */
115 lflg = 0; /* new user name */
118 extern int pw_dbm_mode;
121 extern int sp_dbm_mode;
123 extern int gr_dbm_mode;
126 extern int sg_dbm_mode;
131 static int is_shadow_pwd;
134 static int is_shadow_grp;
140 #include "sgroupio.h"
146 #include "shadowio.h"
149 /* local function prototypes */
150 static int get_groups (char *);
151 static void usage (void);
152 static void new_pwent (struct passwd *);
155 static void new_spent (struct spwd *);
157 static void fail_exit (int);
158 static int update_group (void);
161 static int update_gshadow (void);
163 static int grp_update (void);
165 static long get_number (const char *);
166 static uid_t get_id (const char *);
167 static void process_flags (int, char **);
168 static void close_files (void);
169 static void open_files (void);
170 static void usr_update (void);
171 static void move_home (void);
172 static void update_files (void);
174 #ifndef NO_MOVE_MAILBOX
175 static void move_mailbox (void);
179 * Had to move this over from useradd.c since we have groups named
180 * "56k-family"... ergh.
183 static struct group *getgr_nam_gid (const char *name)
188 gid = strtoul (name, &ep, 10);
189 if (*name != '\0' && *ep == '\0') /* valid numeric gid */
190 return getgrgid (gid);
192 return getgrnam (name);
197 * get_groups - convert a list of group names to an array of group IDs
199 * get_groups() takes a comma-separated list of group names and
200 * converts it to a NULL-terminated array. Any unknown group names are
201 * reported as errors.
204 static int get_groups (char *list)
207 const struct group *grp;
212 * Initialize the list to be empty
215 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.
228 * Strip off a single name from the list
230 if ((cp = strchr (list, ',')))
234 * Names starting with digits are treated as numerical GID
235 * values, otherwise the string is looked up as is.
237 grp = getgr_nam_gid (list);
240 * There must be a match, either by GID value or by
244 fprintf (stderr, _("%s: unknown group %s\n"),
251 * If the group doesn't exist, don't dump core. Instead,
252 * try the next one. --marekm
259 * Don't add this group if they are an NIS group. Tell the
260 * user to go to the server for this group.
265 _("%s: group `%s' is a NIS group.\n"),
271 if (ngroups == sys_ngroups) {
274 ("%s: too many groups specified (max %d).\n"),
280 * Add the group name to the user's list of groups.
283 user_groups[ngroups++] = xstrdup (grp->gr_name);
286 user_groups[ngroups] = (char *) 0;
289 * Any errors in finding group names are fatal
299 * usage - display usage message and exit
302 static void usage (void)
306 ("Usage: %s\t[-u uid [-o]] [-g group] [-G group,...] \n"),
310 ("\t\t[-d home [-m]] [-s shell] [-c comment] [-l new_name]\n"));
311 fprintf (stderr, "\t\t");
313 fprintf (stderr, _("[-f inactive] [-e expire] "));
315 fprintf (stderr, _("[-p passwd] [-L|-U] name\n"));
320 * update encrypted password string (for both shadow and non-shadow
324 static char *new_pw_passwd (char *pw_pass, const char *pw_name)
326 if (Lflg && pw_pass[0] != '!') {
327 char *buf = xmalloc (strlen (pw_pass) + 2);
329 SYSLOG ((LOG_INFO, "lock user `%s' password", pw_name));
331 strcat (buf, pw_pass);
333 } else if (Uflg && pw_pass[0] == '!') {
336 SYSLOG ((LOG_INFO, "unlock user `%s' password", pw_name));
343 SYSLOG ((LOG_INFO, "change user `%s' password", pw_name));
344 pw_pass = xstrdup (user_pass);
350 * new_pwent - initialize the values in a password file entry
352 * new_pwent() takes all of the values that have been entered and fills
353 * in a (struct passwd) with them.
356 static void new_pwent (struct passwd *pwent)
359 SYSLOG ((LOG_INFO, "change user name `%s' to `%s'",
360 pwent->pw_name, user_newname));
361 pwent->pw_name = xstrdup (user_newname);
367 new_pw_passwd (pwent->pw_passwd, pwent->pw_name);
371 "change user `%s' UID from `%d' to `%d'",
372 pwent->pw_name, pwent->pw_uid, user_newid));
373 pwent->pw_uid = user_newid;
377 "change user `%s' GID from `%d' to `%d'",
378 pwent->pw_name, pwent->pw_gid, user_newgid));
379 pwent->pw_gid = user_newgid;
382 pwent->pw_gecos = user_comment;
386 "change user `%s' home from `%s' to `%s'",
387 pwent->pw_name, pwent->pw_dir, user_newhome));
388 pwent->pw_dir = user_newhome;
392 "change user `%s' shell from `%s' to `%s'",
393 pwent->pw_name, pwent->pw_shell, user_shell));
394 pwent->pw_shell = user_shell;
400 * new_spent - initialize the values in a shadow password file entry
402 * new_spent() takes all of the values that have been entered and fills
403 * in a (struct spwd) with them.
406 static void new_spent (struct spwd *spent)
409 spent->sp_namp = xstrdup (user_newname);
413 "change user `%s' inactive from `%ld' to `%ld'",
414 spent->sp_namp, spent->sp_inact, user_inactive));
415 spent->sp_inact = user_inactive;
418 /* XXX - dates might be better than numbers of days. --marekm */
420 "change user `%s' expiration from `%ld' to `%ld'",
421 spent->sp_namp, spent->sp_expire, user_expire));
422 spent->sp_expire = user_expire;
424 spent->sp_pwdp = new_pw_passwd (spent->sp_pwdp, spent->sp_namp);
426 #endif /* SHADOWPWD */
429 * fail_exit - exit with an error code after unlocking files
432 static void fail_exit (int code)
448 static int update_group (void)
453 const struct group *grp;
457 * Lock and open the group file. This will load all of the group
461 fprintf (stderr, _("%s: error locking group file\n"),
463 SYSLOG ((LOG_ERR, "error locking group file"));
466 if (!gr_open (O_RDWR)) {
467 fprintf (stderr, _("%s: error opening group file\n"),
469 SYSLOG ((LOG_ERR, "error opening group file"));
477 * Scan through the entire group file looking for the groups that
478 * the user is a member of.
480 while ((grp = gr_next ())) {
483 * See if the user specified this group as one of their
486 was_member = is_on_list (grp->gr_mem, user_name);
487 is_member = Gflg && is_on_list (user_groups, grp->gr_name);
489 if (!was_member && !is_member)
492 ngrp = __gr_dup (grp);
495 _("%s: out of memory in update_group\n"),
501 if (was_member && (!Gflg || is_member)) {
503 ngrp->gr_mem = del_list (ngrp->gr_mem,
505 ngrp->gr_mem = add_list (ngrp->gr_mem,
509 "change `%s' to `%s' in group `%s'",
510 user_name, user_newname,
513 } else if (was_member && Gflg && !is_member) {
514 ngrp->gr_mem = del_list (ngrp->gr_mem, user_name);
516 SYSLOG ((LOG_INFO, "delete `%s' from group `%s'",
517 user_name, ngrp->gr_name));
518 } else if (!was_member && Gflg && is_member) {
519 ngrp->gr_mem = add_list (ngrp->gr_mem,
520 lflg ? user_newname :
523 SYSLOG ((LOG_INFO, "add `%s' to group `%s'",
524 lflg ? user_newname : user_name,
531 if (!gr_update (ngrp)) {
533 _("%s: error adding new group entry\n"),
535 SYSLOG ((LOG_ERR, "error adding group entry"));
541 * Update the DBM group file with the new entry as well.
543 if (!gr_dbm_update (ngrp)) {
545 _("%s: cannot add new dbm group entry\n"),
547 SYSLOG ((LOG_ERR, "error adding dbm group entry"));
557 fprintf (stderr, _("%s: cannot rewrite group file\n"),
567 static int update_gshadow (void)
573 const struct sgrp *sgrp;
578 _("%s: error locking shadow group file\n"), Prog);
579 SYSLOG ((LOG_ERR, "error locking shadow group file"));
582 if (!sgr_open (O_RDWR)) {
584 _("%s: error opening shadow group file\n"), Prog);
585 SYSLOG ((LOG_ERR, "error opening shadow group file"));
593 * Scan through the entire shadow group file looking for the groups
594 * that the user is a member of.
596 while ((sgrp = sgr_next ())) {
599 * See if the user was a member of this group
601 was_member = is_on_list (sgrp->sg_mem, user_name);
604 * See if the user was an administrator of this group
606 was_admin = is_on_list (sgrp->sg_adm, user_name);
609 * See if the user specified this group as one of their
613 && is_on_list (user_groups, sgrp->sg_name);
615 if (!was_member && !was_admin && !is_member)
618 nsgrp = __sgr_dup (sgrp);
622 ("%s: out of memory in update_gshadow\n"),
628 if (was_admin && lflg) {
630 del_list (nsgrp->sg_adm, user_name);
632 add_list (nsgrp->sg_adm, user_newname);
635 "change admin `%s' to `%s' in shadow group `%s'",
636 user_name, user_newname, nsgrp->sg_name));
638 if (was_member && (!Gflg || is_member)) {
640 nsgrp->sg_mem = del_list (nsgrp->sg_mem,
642 nsgrp->sg_mem = add_list (nsgrp->sg_mem,
646 "change `%s' to `%s' in shadow group `%s'",
647 user_name, user_newname,
650 } else if (was_member && Gflg && !is_member) {
652 del_list (nsgrp->sg_mem, user_name);
655 "delete `%s' from shadow group `%s'",
656 user_name, nsgrp->sg_name));
657 } else if (!was_member && Gflg && is_member) {
658 nsgrp->sg_mem = add_list (nsgrp->sg_mem,
659 lflg ? user_newname :
662 SYSLOG ((LOG_INFO, "add `%s' to shadow group `%s'",
663 lflg ? user_newname : user_name,
672 * Update the group entry to reflect the changes.
674 if (!sgr_update (nsgrp)) {
676 _("%s: error adding new group entry\n"),
679 "error adding shadow group entry"));
685 * Update the DBM group file with the new entry as well.
687 if (!sg_dbm_update (nsgrp)) {
689 _("%s: cannot add new dbm group entry\n"),
692 "error adding dbm shadow group entry"));
703 _("%s: cannot rewrite shadow group file\n"),
711 #endif /* SHADOWGRP */
714 * grp_update - add user to secondary group set
716 * grp_update() takes the secondary group set given in user_groups and
717 * adds the user to each group given by that set.
720 static int grp_update (void)
724 ret = update_group ();
726 if (!ret && is_shadow_grp)
727 ret = update_gshadow ();
732 static long get_number (const char *cp)
737 val = strtol (cp, &ep, 10);
738 if (*cp != '\0' && *ep == '\0') /* valid number */
741 fprintf (stderr, _("%s: invalid numeric argument `%s'\n"), Prog,
746 static uid_t get_id (const char *cp)
751 val = strtoul (cp, &ep, 10);
752 if (*cp != '\0' && *ep == '\0') /* valid number */
755 fprintf (stderr, _("%s: invalid numeric argument `%s'\n"), Prog,
761 * process_flags - perform command line argument setting
763 * process_flags() interprets the command line arguments and sets the
764 * values that the user will be created with accordingly. The values
765 * are checked for sanity.
768 static void process_flags (int argc, char **argv)
770 const struct group *grp;
771 const struct passwd *pwd;
774 const struct spwd *spwd = NULL;
779 if (argc == 1 || argv[argc - 1][0] == '-')
782 if (!(pwd = getpwnam (argv[argc - 1]))) {
783 fprintf (stderr, _("%s: user %s does not exist\n"),
784 Prog, argv[argc - 1]);
787 user_name = argv[argc - 1];
792 * Now make sure it isn't an NIS user.
799 fprintf (stderr, _("%s: user %s is a NIS user\n"),
802 if (!yp_get_default_domain (&nis_domain) &&
803 !yp_master (nis_domain, "passwd.byname",
805 fprintf (stderr, _("%s: %s is the NIS master\n"),
811 user_id = pwd->pw_uid;
812 user_gid = pwd->pw_gid;
813 user_comment = xstrdup (pwd->pw_gecos);
814 user_home = xstrdup (pwd->pw_dir);
815 user_shell = xstrdup (pwd->pw_shell);
818 if (is_shadow_pwd && (spwd = getspnam (user_name))) {
819 user_expire = spwd->sp_expire;
820 user_inactive = spwd->sp_inact;
824 #define FLAGS "A:u:og:G:d:s:c:mf:e:l:p:LU"
826 #define FLAGS "A:u:og:G:d:s:c:ml:p:LU"
828 while ((arg = getopt (argc, argv, FLAGS)) != EOF) {
832 if (!VALID (optarg)) {
834 _("%s: invalid field `%s'\n"),
838 user_comment = optarg;
842 if (!VALID (optarg)) {
844 _("%s: invalid field `%s'\n"),
849 user_newhome = optarg;
854 user_expire = strtoday (optarg);
855 if (user_expire == -1) {
858 ("%s: invalid date `%s'\n"),
862 user_expire *= DAY / SCALE;
868 user_inactive = get_number (optarg);
873 grp = getgr_nam_gid (optarg);
876 _("%s: unknown group %s\n"),
880 user_newgid = grp->gr_gid;
884 if (get_groups (optarg))
889 if (!check_user_name (optarg)) {
891 _("%s: invalid field `%s'\n"),
897 * If the name does not really change, we mustn't
898 * set the flag as this will cause rather serious
902 if (strcmp (user_name, optarg))
905 user_newname = optarg;
933 if (!VALID (optarg)) {
935 _("%s: invalid field `%s'\n"),
943 user_newid = get_id (optarg);
958 fprintf (stderr, _("%s: no flags given\n"), Prog);
962 if (!is_shadow_pwd && (eflg || fflg)) {
965 ("%s: shadow passwords required for -e and -f\n"),
971 if (optind != argc - 1)
974 if (dflg && strcmp (user_home, user_newhome) == 0)
977 if (uflg && user_id == user_newid)
980 if (lflg && getpwnam (user_newname)) {
981 fprintf (stderr, _("%s: user %s exists\n"), Prog,
983 exit (E_NAME_IN_USE);
986 if (uflg && !oflg && getpwuid (user_newid)) {
987 fprintf (stderr, _("%s: uid %lu is not unique\n"),
988 Prog, (unsigned long) user_newid);
994 * close_files - close all of the files that were opened
996 * close_files() closes all of the files that were opened for this new
997 * user. This causes any modified entries to be written out.
1000 static void close_files (void)
1003 fprintf (stderr, _("%s: cannot rewrite password file\n"),
1005 fail_exit (E_PW_UPDATE);
1008 if (is_shadow_pwd && !spw_close ()) {
1010 _("%s: cannot rewrite shadow password file\n"),
1012 fail_exit (E_PW_UPDATE);
1019 (void) pw_unlock ();
1022 * Close the DBM and/or flat files
1036 * open_files - lock and open the password files
1038 * open_files() opens the two password files.
1041 static void open_files (void)
1044 fprintf (stderr, _("%s: unable to lock password file\n"),
1048 if (!pw_open (O_RDWR)) {
1049 fprintf (stderr, _("%s: unable to open password file\n"),
1051 fail_exit (E_PW_UPDATE);
1054 if (is_shadow_pwd && !spw_lock ()) {
1056 _("%s: cannot lock shadow password file\n"),
1058 fail_exit (E_PW_UPDATE);
1060 if (is_shadow_pwd && !spw_open (O_RDWR)) {
1062 _("%s: cannot open shadow password file\n"),
1064 fail_exit (E_PW_UPDATE);
1070 * usr_update - create the user entries
1072 * usr_update() creates the password file entries for this user and
1073 * will update the group entries if required.
1076 static void usr_update (void)
1078 struct passwd pwent;
1079 const struct passwd *pwd;
1083 const struct spwd *spwd = NULL;
1087 * Locate the entry in /etc/passwd, which MUST exist.
1090 pwd = pw_locate (user_name);
1092 fprintf (stderr, _("%s: %s not found in /etc/passwd\n"),
1094 fail_exit (E_NOTFOUND);
1102 * Locate the entry in /etc/shadow. It doesn't have to exist, and
1103 * won't be created if it doesn't.
1106 if (is_shadow_pwd && (spwd = spw_locate (user_name))) {
1112 if (lflg || uflg || gflg || cflg || dflg || sflg || pflg
1114 if (!pw_update (&pwent)) {
1116 _("%s: error changing password entry\n"),
1118 fail_exit (E_PW_UPDATE);
1120 if (lflg && !pw_remove (user_name)) {
1122 _("%s: error removing password entry\n"),
1124 fail_exit (E_PW_UPDATE);
1127 if (pw_dbm_present ()) {
1128 if (!pw_dbm_update (&pwent)) {
1131 ("%s: error adding password dbm entry\n"),
1133 fail_exit (E_PW_UPDATE);
1135 if (lflg && (pwd = getpwnam (user_name)) &&
1136 !pw_dbm_remove (pwd)) {
1139 ("%s: error removing passwd dbm entry\n"),
1141 fail_exit (E_PW_UPDATE);
1147 if (spwd && (lflg || eflg || fflg || pflg || Lflg || Uflg)) {
1148 if (!spw_update (&spent)) {
1151 ("%s: error adding new shadow password entry\n"),
1153 fail_exit (E_PW_UPDATE);
1155 if (lflg && !spw_remove (user_name)) {
1158 ("%s: error removing shadow password entry\n"),
1160 fail_exit (E_PW_UPDATE);
1164 if (spwd && sp_dbm_present ()) {
1165 if (!sp_dbm_update (&spent)) {
1168 ("%s: error updating shadow passwd dbm entry\n"),
1170 fail_exit (E_PW_UPDATE);
1172 if (lflg && !sp_dbm_remove (user_name)) {
1175 ("%s: error removing shadow passwd dbm entry\n"),
1177 fail_exit (E_PW_UPDATE);
1181 #endif /* SHADOWPWD */
1185 * move_home - move the user's home directory
1187 * move_home() moves the user's home directory to a new location. The
1188 * files will be copied if the directory cannot simply be renamed.
1191 static void move_home (void)
1195 if (mflg && stat (user_home, &sb) == 0) {
1197 * Don't try to move it if it is not a directory
1198 * (but /dev/null for example). --marekm
1200 if (!S_ISDIR (sb.st_mode))
1203 if (access (user_newhome, F_OK) == 0) {
1204 fprintf (stderr, _("%s: directory %s exists\n"),
1205 Prog, user_newhome);
1206 fail_exit (E_HOMEDIR);
1207 } else if (rename (user_home, user_newhome)) {
1208 if (errno == EXDEV) {
1210 (user_newhome, sb.st_mode & 0777)) {
1213 ("%s: can't create %s\n"),
1214 Prog, user_newhome);
1216 if (chown (user_newhome,
1217 sb.st_uid, sb.st_gid)) {
1219 _("%s: can't chown %s\n"),
1220 Prog, user_newhome);
1221 rmdir (user_newhome);
1222 fail_exit (E_HOMEDIR);
1224 if (copy_tree (user_home, user_newhome,
1225 uflg ? user_newid : -1,
1226 gflg ? user_newgid : -1) ==
1227 0 && remove_tree (user_home) == 0
1228 && rmdir (user_home) == 0)
1231 (void) remove_tree (user_newhome);
1232 (void) rmdir (user_newhome);
1236 ("%s: cannot rename directory %s to %s\n"),
1237 Prog, user_home, user_newhome);
1238 fail_exit (E_HOMEDIR);
1242 chown (dflg ? user_newhome : user_home,
1243 uflg ? user_newid : user_id,
1244 gflg ? user_newgid : user_gid);
1248 * update_files - update the lastlog and faillog files
1251 static void update_files (void)
1258 * Relocate the "lastlog" entries for the user. The old entry is
1259 * left alone in case the UID was shared. It doesn't hurt anything
1260 * to just leave it be.
1263 if ((fd = open (LASTLOG_FILE, O_RDWR)) != -1) {
1264 lseek (fd, (off_t) user_id * sizeof ll, SEEK_SET);
1265 if (read (fd, (char *) &ll, sizeof ll) == sizeof ll) {
1266 lseek (fd, (off_t) user_newid * sizeof ll,
1268 write (fd, (char *) &ll, sizeof ll);
1274 * Relocate the "faillog" entries in the same manner.
1277 if ((fd = open (FAILLOG_FILE, O_RDWR)) != -1) {
1278 lseek (fd, (off_t) user_id * sizeof fl, SEEK_SET);
1279 if (read (fd, (char *) &fl, sizeof fl) == sizeof fl) {
1280 lseek (fd, (off_t) user_newid * sizeof fl,
1282 write (fd, (char *) &fl, sizeof fl);
1288 #ifndef NO_MOVE_MAILBOX
1290 * This is the new and improved code to carefully chown/rename the user's
1291 * mailbox. Maybe I am too paranoid but the mail spool dir sometimes
1292 * happens to be mode 1777 (this makes mail user agents work without
1293 * being setgid mail, but is NOT recommended; they all should be fixed
1294 * to use movemail). --marekm
1296 static void move_mailbox (void)
1298 const char *maildir;
1299 char mailfile[1024], newmailfile[1024];
1303 maildir = getdef_str ("MAIL_DIR");
1304 #ifdef MAIL_SPOOL_DIR
1305 if (!maildir && !getdef_str ("MAIL_FILE"))
1306 maildir = MAIL_SPOOL_DIR;
1312 * O_NONBLOCK is to make sure open won't hang on mandatory locks.
1313 * We do fstat/fchown to make sure there are no races (someone
1314 * replacing /var/spool/mail/luser with a hard link to /etc/passwd
1315 * between stat and chown). --marekm
1318 snprintf (mailfile, sizeof mailfile, "%s/%s", maildir, user_name);
1319 fd = open (mailfile, O_RDONLY | O_NONBLOCK, 0);
1321 /* no need for warnings if the mailbox doesn't exist */
1322 if (errno != ENOENT)
1326 if (fstat (fd, &st) < 0) {
1331 if (st.st_uid != user_id) {
1332 /* better leave it alone */
1333 fprintf (stderr, _("%s: warning: %s not owned by %s\n"),
1334 Prog, mailfile, user_name);
1338 if (uflg && fchown (fd, user_newid, (gid_t) - 1) < 0)
1339 perror (_("failed to change mailbox owner"));
1344 snprintf (newmailfile, sizeof newmailfile, "%s/%s",
1345 maildir, user_newname);
1346 if (link (mailfile, newmailfile) || unlink (mailfile))
1347 perror (_("failed to rename mailbox"));
1353 static struct pam_conv conv = {
1357 #endif /* USE_PAM */
1360 * main - usermod command
1363 int main (int argc, char **argv)
1368 pam_handle_t *pamh = NULL;
1369 struct passwd *pampw;
1374 * Get my name so that I can use it to report errors.
1376 Prog = Basename (argv[0]);
1378 setlocale (LC_ALL, "");
1379 bindtextdomain (PACKAGE, LOCALEDIR);
1380 textdomain (PACKAGE);
1382 sys_ngroups = sysconf (_SC_NGROUPS_MAX);
1383 user_groups = malloc ((1 + sys_ngroups) * sizeof (char *));
1384 user_groups[0] = (char *) 0;
1389 is_shadow_pwd = spw_file_present ();
1392 is_shadow_grp = sgr_file_present ();
1395 process_flags (argc, argv);
1398 retval = PAM_SUCCESS;
1400 pampw = getpwuid (getuid ());
1401 if (pampw == NULL) {
1402 retval = PAM_USER_UNKNOWN;
1405 if (retval == PAM_SUCCESS) {
1407 pam_start ("usermod", pampw->pw_name, &conv, &pamh);
1410 if (retval == PAM_SUCCESS) {
1411 retval = pam_authenticate (pamh, 0);
1412 if (retval != PAM_SUCCESS) {
1413 pam_end (pamh, retval);
1417 if (retval == PAM_SUCCESS) {
1418 retval = pam_acct_mgmt (pamh, 0);
1419 if (retval != PAM_SUCCESS) {
1420 pam_end (pamh, retval);
1424 if (retval != PAM_SUCCESS) {
1425 fprintf (stderr, _("%s: PAM authentication failed\n"),
1430 OPENLOG ("usermod");
1431 #endif /* USE_PAM */
1434 * The open routines for the NDBM files don't use read-write as the
1435 * mode, so we have to clue them in.
1439 pw_dbm_mode = O_RDWR;
1441 sp_dbm_mode = O_RDWR;
1443 gr_dbm_mode = O_RDWR;
1445 sg_dbm_mode = O_RDWR;
1450 * Do the hard stuff - open the files, change the user entries,
1451 * change the home directory, then close and update the files.
1457 nscd_flush_cache ("passwd");
1458 nscd_flush_cache ("group");
1463 grp_err = grp_update ();
1468 #ifndef NO_MOVE_MAILBOX
1477 * Change the UID on all of the files owned by `user_id' to
1478 * `user_newid' in the user's home directory.
1481 chown_tree (dflg ? user_newhome : user_home,
1482 user_id, user_newid,
1483 user_gid, gflg ? user_newgid : user_gid);
1487 exit (E_GRP_UPDATE);
1490 if (retval == PAM_SUCCESS) {
1491 retval = pam_chauthtok (pamh, 0);
1492 if (retval != PAM_SUCCESS) {
1493 pam_end (pamh, retval);
1497 if (retval != PAM_SUCCESS) {
1498 fprintf (stderr, _("%s: PAM chauthtok failed\n"), Prog);
1502 if (retval == PAM_SUCCESS)
1503 pam_end (pamh, PAM_SUCCESS);
1504 #endif /* USE_PAM */