]> granicus.if.org Git - shadow/blob - src/usermod.c
09d1172d872695446aac41846dcb2cb67a3b5755
[shadow] / src / usermod.c
1 /*
2  * Copyright 1991 - 1994, Julianne Frances Haugh
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
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.
16  *
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
27  * SUCH DAMAGE.
28  */
29
30 #include <config.h>
31
32 #include "rcsid.h"
33 RCSID(PKG_VER "$Id: usermod.c,v 1.18 2000/08/26 18:27:18 marekm Exp $")
34
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <stdio.h>
38 #include <errno.h>
39 #include <pwd.h>
40 #include <grp.h>
41 #include <ctype.h>
42 #include <fcntl.h>
43 #include <time.h>
44
45 #include "prototypes.h"
46 #include "defines.h"
47 #include "chkname.h"
48 #include "faillog.h"
49 #if HAVE_LASTLOG_H
50 #include <lastlog.h>
51 #else
52 #include "lastlog_.h"
53 #endif
54 #include "pwauth.h"
55 #include "getdef.h"
56
57 /*
58  * exit status values
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).
61  */
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 */
74
75 #define VALID(s)        (strcspn (s, ":\n") == strlen (s))
76
77 static char *user_name;
78 static char *user_newname;
79 static char *user_pass;
80 static uid_t user_id;
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;
88 #ifdef  SHADOWPWD
89 static long user_expire;
90 static long user_inactive;
91 #endif
92 static char *user_groups[NGROUPS_MAX+1];  /* NULL-terminated list */
93
94 static char *Prog;
95
96 #ifdef AUTH_METHODS
97 static char *auth_arg;
98 static char user_auth[BUFSIZ];
99 static int Aflg = 0; /* specify user defined authentication method */
100 #else
101 #define Aflg 0
102 #endif
103
104 static int
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 */
113 #ifdef SHADOWPWD
114         fflg = 0, /* days until account with expired password is locked */
115         eflg = 0, /* days since 1970-01-01 when account becomes expired */
116 #endif
117         pflg = 0, /* new encrypted password */
118         lflg = 0; /* new user name */
119
120 #ifdef  NDBM
121 extern  int     pw_dbm_mode;
122 #ifdef  SHADOWPWD
123 extern  int     sp_dbm_mode;
124 #endif
125 extern  int     gr_dbm_mode;
126 #ifdef  SHADOWGRP
127 extern  int     sg_dbm_mode;
128 #endif
129 #endif
130
131 #ifdef SHADOWPWD
132 static int is_shadow_pwd;
133 #endif
134 #ifdef SHADOWGRP
135 static int is_shadow_grp;
136 #endif
137
138 #include "groupio.h"
139
140 #ifdef  SHADOWGRP
141 #include "sgroupio.h"
142 #endif
143
144 #include "pwio.h"
145
146 #ifdef  SHADOWPWD
147 #include "shadowio.h"
148 #endif
149
150 extern char *optarg;
151 extern int optind;
152
153 /* local function prototypes */
154 static int get_groups(char *);
155 static void usage(void);
156 static void new_pwent(struct passwd *);
157 #ifdef SHADOWPWD
158 static void new_spent(struct spwd *);
159 #endif
160 static void fail_exit(int);
161 static int update_group(void);
162 #ifdef SHADOWGRP
163 static int update_gshadow(void);
164 #endif
165 static int grp_update(void);
166 #ifdef AUTH_METHODS
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 *);
174 #endif
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);
184 #endif
185
186 /* Had to move this over from useradd.c since we have groups named
187  * "56k-family"... ergh.
188  * --Pac. */
189 static struct group *
190 getgr_nam_gid(const char *name)
191 {
192         gid_t gid;
193         char *ep;
194
195         gid = strtol(name, &ep, 10);
196         if (*name != '\0' && *ep == '\0')  /* valid numeric gid */
197                 return getgrgid(gid);
198
199         return getgrnam(name);
200 }
201
202
203 /*
204  * get_groups - convert a list of group names to an array of group IDs
205  *
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.
209  */
210
211 static int
212 get_groups(char *list)
213 {
214         char *cp;
215         const struct group *grp;
216         int errors = 0;
217         int ngroups = 0;
218
219         /*
220          * Initialize the list to be empty
221          */
222
223         user_groups[0] = (char *) 0;
224
225         if (! *list)
226                 return 0;
227
228         /*
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.
232          */
233
234         do {
235                 /*
236                  * Strip off a single name from the list
237                  */
238                 if ((cp = strchr (list, ',')))
239                         *cp++ = '\0';
240
241                 /*
242                  * Names starting with digits are treated as numerical
243                  * GID values, otherwise the string is looked up as is.
244                  */
245                 grp = getgr_nam_gid(list);
246
247                 /*
248                  * There must be a match, either by GID value or by
249                  * string name.
250                  */
251                 if (!grp) {
252                         fprintf(stderr, _("%s: unknown group %s\n"),
253                                 Prog, list);
254                         errors++;
255                 }
256                 list = cp;
257
258                 /*
259                  * If the group doesn't exist, don't dump core...
260                  * Instead, try the next one.  --marekm
261                  */
262                 if (! grp)
263                         continue;
264
265 #ifdef  USE_NIS
266                 /*
267                  * Don't add this group if they are an NIS group.  Tell
268                  * the user to go to the server for this group.
269                  */
270
271                 if (__isgrNIS ()) {
272                         fprintf(stderr, _("%s: group `%s' is a NIS group.\n"),
273                                 Prog, grp->gr_name);
274                         continue;
275                 }
276 #endif
277
278                 if (ngroups == NGROUPS_MAX) {
279                         fprintf(stderr,
280                                 _("%s: too many groups specified (max %d).\n"),
281                                 Prog, ngroups);
282                         break;
283                 }
284
285                 /*
286                  * Add the group name to the user's list of groups.
287                  */
288
289                 user_groups[ngroups++] = xstrdup(grp->gr_name);
290         } while (list);
291
292         user_groups[ngroups] = (char *) 0;
293
294         /*
295          * Any errors in finding group names are fatal
296          */
297
298         if (errors)
299                 return -1;
300
301         return 0;
302 }
303
304 /*
305  * usage - display usage message and exit
306  */
307
308 static void
309 usage(void)
310 {
311         fprintf(stderr,
312                 _("usage: %s\t[-u uid [-o]] [-g group] [-G group,...] \n"),
313                 Prog);
314         fprintf(stderr,
315                 _("\t\t[-d home [-m]] [-s shell] [-c comment] [-l new_name]\n"));
316         fprintf(stderr, "\t\t");
317 #ifdef SHADOWPWD
318         fprintf(stderr, _("[-f inactive] [-e expire ] "));
319 #endif
320 #ifdef AUTH_METHODS
321         fprintf(stderr, _("[-A {DEFAULT|program},... ] "));
322 #endif
323         fprintf(stderr, _("[-p passwd] name\n"));
324         exit(E_USAGE);
325 }
326
327 /*
328  * new_pwent - initialize the values in a password file entry
329  *
330  *      new_pwent() takes all of the values that have been entered and
331  *      fills in a (struct passwd) with them.
332  */
333
334 static void
335 new_pwent(struct passwd *pwent)
336 {
337         if (lflg) {
338                 SYSLOG((LOG_INFO, "change user name `%s' to `%s'\n",
339                         pwent->pw_name, user_newname));
340                 pwent->pw_name = xstrdup (user_newname);
341         }
342 #ifdef SHADOWPWD
343         if (!is_shadow_pwd)
344 #endif
345         if (pflg) {
346                 SYSLOG((LOG_INFO, "change user `%s' password\n",
347                         pwent->pw_name));
348                 pwent->pw_passwd = xstrdup(user_pass);
349         }
350         if (uflg) {
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;
354         }
355         if (gflg) {
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;
359         }
360         if (cflg)
361                 pwent->pw_gecos = user_comment;
362
363         if (dflg) {
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;
367         }
368         if (sflg) {
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;
372         }
373 }
374
375 #ifdef  SHADOWPWD
376 /*
377  * new_spent - initialize the values in a shadow password file entry
378  *
379  *      new_spent() takes all of the values that have been entered and
380  *      fills in a (struct spwd) with them.
381  */
382
383 static void
384 new_spent(struct spwd *spent)
385 {
386         if (lflg)
387                 spent->sp_namp = xstrdup (user_newname);
388
389         if (fflg) {
390                 SYSLOG((LOG_INFO,
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;
394         }
395         if (eflg) {
396                 /* XXX - dates might be better than numbers of days.  --marekm */
397                 SYSLOG((LOG_INFO,
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;
401         }
402         if (pflg) {
403                 SYSLOG((LOG_INFO, "change user `%s' password\n",
404                         spent->sp_namp));
405                 spent->sp_pwdp = xstrdup(user_pass);
406         }
407 }
408 #endif  /* SHADOWPWD */
409
410 /*
411  * fail_exit - exit with an error code after unlocking files
412  */
413
414 static void
415 fail_exit(int code)
416 {
417         (void) gr_unlock ();
418 #ifdef  SHADOWGRP
419         if (is_shadow_grp)
420                 sgr_unlock ();
421 #endif
422 #ifdef  SHADOWPWD
423         if (is_shadow_pwd)
424                 spw_unlock ();
425 #endif
426         (void) pw_unlock ();
427         exit(code);
428 }
429
430
431 static int
432 update_group(void)
433 {
434         int is_member;
435         int was_member;
436         int changed;
437         const struct group *grp;
438         struct group *ngrp;
439
440         /*
441          * Lock and open the group file.  This will load all of the group
442          * entries.
443          */
444         if (! gr_lock ()) {
445                 fprintf(stderr, _("%s: error locking group file\n"), Prog);
446                 SYSLOG((LOG_ERR, "error locking group file"));
447                 return -1;
448         }
449         if (! gr_open (O_RDWR)) {
450                 fprintf(stderr, _("%s: error opening group file\n"), Prog);
451                 SYSLOG((LOG_ERR, "error opening group file"));
452                 gr_unlock();
453                 return -1;
454         }
455
456         changed = 0;
457
458         /*
459          * Scan through the entire group file looking for the groups that
460          * the user is a member of.
461          */
462         while ((grp = gr_next())) {
463
464                 /*
465                  * See if the user specified this group as one of their
466                  * concurrent groups.
467                  */
468                 was_member = is_on_list(grp->gr_mem, user_name);
469                 is_member = Gflg && is_on_list(user_groups, grp->gr_name);
470
471                 if (!was_member && !is_member)
472                         continue;
473
474                 ngrp = __gr_dup(grp);
475                 if (!ngrp) {
476                         fprintf(stderr,
477                                 _("%s: out of memory in update_group\n"),
478                                 Prog);
479                         gr_unlock();
480                         return -1;
481                 }
482
483                 if (was_member && (!Gflg || is_member)) {
484                         if (lflg) {
485                                 ngrp->gr_mem = del_list(ngrp->gr_mem,
486                                                         user_name);
487                                 ngrp->gr_mem = add_list(ngrp->gr_mem,
488                                                         user_newname);
489                                 changed = 1;
490                                 SYSLOG((LOG_INFO,
491                                         "change `%s' to `%s' in group `%s'\n",
492                                         user_name, user_newname,
493                                         ngrp->gr_name));
494                         }
495                 } else if (was_member && Gflg && !is_member) {
496                         ngrp->gr_mem = del_list (ngrp->gr_mem, user_name);
497                         changed = 1;
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);
503                         changed = 1;
504                         SYSLOG((LOG_INFO, "add `%s' to group `%s'\n",
505                                 lflg ? user_newname:user_name, ngrp->gr_name));
506                 }
507                 if (!changed)
508                         continue;
509
510                 changed = 0;
511                 if (! gr_update (ngrp)) {
512                         fprintf(stderr, _("%s: error adding new group entry\n"),
513                                 Prog);
514                         SYSLOG((LOG_ERR, "error adding group entry"));
515                         gr_unlock();
516                         return -1;
517                 }
518 #ifdef  NDBM
519                 /*
520                  * Update the DBM group file with the new entry as well.
521                  */
522                 if (! gr_dbm_update (ngrp)) {
523                         fprintf(stderr,
524                                 _("%s: cannot add new dbm group entry\n"),
525                                 Prog);
526                         SYSLOG((LOG_ERR, "error adding dbm group entry"));
527                         gr_unlock();
528                         return -1;
529                 }
530 #endif  /* NDBM */
531         }
532 #ifdef NDBM
533         endgrent ();
534 #endif  /* NDBM */
535         if (!gr_close()) {
536                 fprintf(stderr, _("%s: cannot rewrite group file\n"),
537                         Prog);
538                 gr_unlock();
539                 return -1;
540         }
541         gr_unlock();
542         return 0;
543 }
544
545 #ifdef SHADOWGRP
546 static int
547 update_gshadow(void)
548 {
549         int is_member;
550         int was_member;
551         int was_admin;
552         int changed;
553         const struct sgrp *sgrp;
554         struct sgrp *nsgrp;
555
556         if (!sgr_lock()) {
557                 fprintf(stderr, _("%s: error locking shadow group file\n"),
558                         Prog);
559                 SYSLOG((LOG_ERR, "error locking shadow group file"));
560                 return -1;
561         }
562         if (!sgr_open(O_RDWR)) {
563                 fprintf(stderr, _("%s: error opening shadow group file\n"),
564                         Prog);
565                 SYSLOG((LOG_ERR, "error opening shadow group file"));
566                 sgr_unlock();
567                 return -1;
568         }
569
570         changed = 0;
571
572         /*
573          * Scan through the entire shadow group file looking for the groups
574          * that the user is a member of.
575          */
576         while ((sgrp = sgr_next())) {
577
578                 /*
579                  * See if the user was a member of this group
580                  */
581                 was_member = is_on_list(sgrp->sg_mem, user_name);
582
583                 /*
584                  * See if the user was an administrator of this group
585                  */
586                 was_admin = is_on_list(sgrp->sg_adm, user_name);
587
588                 /*
589                  * See if the user specified this group as one of their
590                  * concurrent groups.
591                  */
592                 is_member = Gflg && is_on_list(user_groups, sgrp->sg_name);
593
594                 if (!was_member && !was_admin && !is_member)
595                         continue;
596
597                 nsgrp = __sgr_dup(sgrp);
598                 if (!nsgrp) {
599                         fprintf(stderr,
600                                 _("%s: out of memory in update_gshadow\n"),
601                                 Prog);
602                         sgr_unlock();
603                         return -1;
604                 }
605
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);
609                         changed = 1;
610                         SYSLOG((LOG_INFO,
611                             "change admin `%s' to `%s' in shadow group `%s'\n",
612                             user_name, user_newname, nsgrp->sg_name));
613                 }
614                 if (was_member && (!Gflg || is_member)) {
615                         if (lflg) {
616                                 nsgrp->sg_mem = del_list (nsgrp->sg_mem,
617                                         user_name);
618                                 nsgrp->sg_mem = add_list (nsgrp->sg_mem,
619                                         user_newname);
620                                 changed = 1;
621                                 SYSLOG((LOG_INFO,
622                                   "change `%s' to `%s' in shadow group `%s'\n",
623                                   user_name, user_newname, nsgrp->sg_name));
624                         }
625                 } else if (was_member && Gflg && !is_member) {
626                         nsgrp->sg_mem = del_list (nsgrp->sg_mem, user_name);
627                         changed = 1;
628                         SYSLOG((LOG_INFO,
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);
634                         changed = 1;
635                         SYSLOG((LOG_INFO, "add `%s' to shadow group `%s'\n",
636                                 lflg ? user_newname:user_name,nsgrp->sg_name));
637                 }
638                 if (!changed)
639                         continue;
640
641                 changed = 0;
642
643                 /* 
644                  * Update the group entry to reflect the changes.
645                  */
646                 if (! sgr_update (nsgrp)) {
647                         fprintf(stderr,
648                                 _("%s: error adding new group entry\n"),
649                                 Prog);
650                         SYSLOG((LOG_ERR, "error adding shadow group entry\n"));
651                         sgr_unlock();
652                         return -1;
653                 }
654 #ifdef  NDBM
655                 /*
656                  * Update the DBM group file with the new entry as well.
657                  */
658                 if (! sg_dbm_update (nsgrp)) {
659                         fprintf(stderr,
660                                 _("%s: cannot add new dbm group entry\n"),
661                                 Prog);
662                         SYSLOG((LOG_ERR,
663                                 "error adding dbm shadow group entry\n"));
664                         sgr_unlock();
665                         return -1;
666                 }
667 #endif  /* NDBM */
668         }
669 #ifdef NDBM
670         endsgent ();
671 #endif  /* NDBM */
672         if (!sgr_close()) {
673                 fprintf(stderr, _("%s: cannot rewrite shadow group file\n"),
674                         Prog);
675                 sgr_unlock();
676                 return -1;
677         }
678         sgr_unlock();
679         return 0;
680 }
681 #endif /* SHADOWGRP */
682
683 /*
684  * grp_update - add user to secondary group set
685  *
686  *      grp_update() takes the secondary group set given in user_groups
687  *      and adds the user to each group given by that set.
688  */
689
690 static int
691 grp_update(void)
692 {
693         int ret;
694
695         ret = update_group();
696 #ifdef SHADOWGRP
697         if (!ret && is_shadow_grp)
698                 ret = update_gshadow();
699 #endif
700         return ret;
701 }
702
703 #ifdef AUTH_METHODS
704 /*
705  * get_password - locate encrypted password in authentication list
706  */
707
708 static char *
709 get_password(const char *list)
710 {
711         char    *cp, *end;
712         static  char    buf[257];
713
714         strcpy (buf, list);
715         for (cp = buf;cp;cp = end) {
716                 if ((end = strchr (cp, ';')))
717                         *end++ = 0;
718
719                 if (cp[0] == '@')
720                         continue;
721
722                 return cp;
723         }
724         return (char *) 0;
725 }
726
727 /*
728  * split_auths - break up comma list into (char *) array
729  */
730
731 static void
732 split_auths(char *list, char **array)
733 {
734         char    *cp, *end;
735         int     i = 0;
736
737         for (cp = list;cp;cp = end) {
738                 if ((end = strchr (cp, ';')))
739                         *end++ = '\0';
740
741                 array[i++] = cp;
742         }
743         array[i] = 0;
744 }
745
746 /*
747  * update_auths - find list of methods to update
748  */
749
750 static void
751 update_auths(const char *old, const char *new, char *update)
752 {
753         char    oldbuf[257], newbuf[257];
754         char    *oldv[32], *newv[32], *updatev[32];
755         int     i, j, k;
756
757         strcpy (oldbuf, old);
758         split_auths (oldbuf, oldv);
759
760         strcpy (newbuf, new);
761         split_auths (newbuf, newv);
762
763         for (i = j = k = 0;oldv[i];i++) {
764                 for (j = 0;newv[j];j++)
765                         if (strcmp (oldv[i], newv[j]) != 0)
766                                 break;
767
768                 if (newv[j] != (char *) 0)
769                         updatev[k++] = oldv[i];
770         }
771         updatev[k] = 0;
772
773         update[0] = '\0';
774         for (i = 0;updatev[i];i++) {
775                 if (i)
776                         strcat (update, ";");
777
778                 strcat (update, updatev[i]);
779         }
780 }
781
782 /*
783  * add_auths - find list of methods to add
784  */
785
786 static void
787 add_auths(const char *old, const char *new, char *add)
788 {
789         char    oldbuf[257], newbuf[257];
790         char    *oldv[32], *newv[32], *addv[32];
791         int     i, j, k;
792
793         strcpy (oldbuf, old);
794         split_auths (oldbuf, oldv);
795
796         strcpy (newbuf, new);
797         split_auths (newbuf, newv);
798
799         for (i = j = k = 0;newv[i];i++) {
800                 for (j = 0;oldv[j];j++)
801                         if (strcmp (oldv[i], newv[j]) == 0)
802                                 break;
803
804                 if (oldv[j] == (char *) 0)
805                         addv[k++] = newv[i];
806         }
807         addv[k] = 0;
808
809         add[0] = '\0';
810         for (i = 0;addv[i];i++) {
811                 if (i)
812                         strcat (add, ";");
813
814                 strcat (add, addv[i]);
815         }
816 }
817
818 /*
819  * delete_auths - find list of methods to delete
820  */
821
822 static void
823 delete_auths(const char *old, const char *new, char *remove)
824 {
825         char    oldbuf[257], newbuf[257];
826         char    *oldv[32], *newv[32], *removev[32];
827         int     i, j, k;
828
829         strcpy (oldbuf, old);
830         split_auths (oldbuf, oldv);
831
832         strcpy (newbuf, new);
833         split_auths (newbuf, newv);
834
835         for (i = j = k = 0;oldv[i];i++) {
836                 for (j = 0;newv[j];j++)
837                         if (strcmp (oldv[i], newv[j]) == 0)
838                                 break;
839
840                 if (newv[j] == (char *) 0)
841                         removev[k++] = oldv[i];
842         }
843         removev[k] = 0;
844
845         remove[0] = '\0';
846         for (i = 0;removev[i];i++) {
847                 if (i)
848                         strcat (remove, ";");
849
850                 strcat (remove, removev[i]);
851         }
852 }
853
854 /*
855  * convert_auth - convert the argument list to a authentication list
856  */
857
858 static void
859 convert_auth(char *auths, const char *oldauths, const char *list)
860 {
861         char    *cp, *end;
862         char    *old;
863         char    buf[257];
864
865         /*
866          * Copy each method.  DEFAULT is replaced by an encrypted string
867          * if one can be found in the current authentication list.
868          */
869
870         strcpy (buf, list);
871         auths[0] = '\0';
872         for (cp = buf;cp;cp = end) {
873                 if (auths[0])
874                         strcat (auths, ";");
875
876                 if ((end = strchr (cp, ',')))
877                         *end++ = '\0';
878
879                 if (strcmp (cp, "DEFAULT") == 0) {
880                         if ((old = get_password (oldauths)))
881                                 strcat (auths, old);
882                         else
883                                 strcat (auths, "!");
884                 } else {
885                         strcat (auths, "@");
886                         strcat (auths, cp);
887                 }
888         }
889 }
890
891 /*
892  * valid_auth - check authentication list for validity
893  */
894
895 static int
896 valid_auth(const char *methods)
897 {
898         char    *cp, *end;
899         char    buf[257];
900         int     default_cnt = 0;
901
902         /*
903          * Cursory checks, length and illegal characters
904          */
905
906         if ((int) strlen (methods) > 256)
907                 return 0;
908
909         if (! VALID (methods))
910                 return 0;
911
912         /*
913          * Pick each method apart and check it.
914          */
915
916         strcpy (buf, methods);
917         for (cp = buf;cp;cp = end) {
918                 if ((end = strchr (cp, ',')))
919                         *end++ = '\0';
920
921                 if (strcmp (cp, "DEFAULT") == 0) {
922                         if (default_cnt++ > 0)
923                                 return 0;
924                 }
925         }
926         return 1;
927 }
928 #endif
929
930 static long
931 get_number(const char *cp)
932 {
933         long val;
934         char *ep;
935
936         val = strtol(cp, &ep, 10);
937         if (*cp != '\0' && *ep == '\0')  /* valid number */
938                 return val;
939
940         fprintf(stderr, _("%s: invalid numeric argument `%s'\n"), Prog, cp);
941         exit(E_BAD_ARG);
942 }
943
944 /*
945  * process_flags - perform command line argument setting
946  *
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.
950  */
951
952 static void
953 process_flags(int argc, char **argv)
954 {
955         const struct group *grp;
956         const struct passwd *pwd;
957 #ifdef SHADOWPWD
958         const struct spwd *spwd = NULL;
959 #endif
960         int     anyflag = 0;
961         int     arg;
962
963         if (argc == 1 || argv[argc - 1][0] == '-')
964                 usage ();
965
966         if (! (pwd = getpwnam (argv[argc - 1]))) {
967                 fprintf(stderr, _("%s: user %s does not exist\n"),
968                         Prog, argv[argc - 1]);
969                 exit(E_NOTFOUND);
970         }
971         user_name = argv[argc - 1];
972
973 #ifdef  USE_NIS
974
975         /*
976          * Now make sure it isn't an NIS user.
977          */
978
979         if (__ispwNIS ()) {
980                 char    *nis_domain;
981                 char    *nis_master;
982
983                 fprintf(stderr, _("%s: user %s is a NIS user\n"),
984                         Prog, user_name);
985
986                 if (! yp_get_default_domain (&nis_domain) &&
987                                 ! yp_master (nis_domain, "passwd.byname",
988                                 &nis_master)) {
989                         fprintf(stderr, _("%s: %s is the NIS master\n"),
990                                 Prog, nis_master);
991                 }
992                 exit(E_NOTFOUND);
993         }
994 #endif
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);
1000
1001 #ifdef  SHADOWPWD
1002         if (is_shadow_pwd && (spwd = getspnam (user_name))) {
1003                 user_expire = spwd->sp_expire;
1004                 user_inactive = spwd->sp_inact;
1005         }
1006 #endif
1007 #ifdef  SHADOWPWD
1008 #define FLAGS "A:u:og:G:d:s:c:mf:e:l:p:"
1009 #else
1010 #define FLAGS "A:u:og:G:d:s:c:ml:p:"
1011 #endif
1012         while ((arg = getopt(argc, argv, FLAGS)) != EOF) {
1013 #undef FLAGS
1014                 switch (arg) {
1015 #ifdef AUTH_METHODS
1016                 case 'A':
1017                         if (! valid_auth (optarg)) {
1018                                 fprintf(stderr,
1019                                         _("%s: invalid field `%s'\n"),
1020                                         Prog, optarg);
1021                                 exit(E_BAD_ARG);
1022                         }
1023                         auth_arg = optarg;
1024                         Aflg++;
1025                         break;
1026 #endif
1027                 case 'c':
1028                         if (! VALID (optarg)) {
1029                                 fprintf(stderr,
1030                                         _("%s: invalid field `%s'\n"),
1031                                         Prog, optarg);
1032                                 exit(E_BAD_ARG);
1033                         }
1034                         user_comment = optarg;
1035                         cflg++;
1036                         break;
1037                 case 'd':
1038                         if (! VALID (optarg)) {
1039                                 fprintf(stderr,
1040                                         _("%s: invalid field `%s'\n"),
1041                                         Prog, optarg);
1042                                 exit(E_BAD_ARG);
1043                         }
1044                         dflg++;
1045                         user_newhome = optarg;
1046                         break;
1047 #ifdef SHADOWPWD
1048                 case 'e':
1049                         if (*optarg) {
1050                                 user_expire = strtoday(optarg);
1051                                 if (user_expire == -1) {
1052                                         fprintf(stderr,
1053                                                 _("%s: invalid date `%s'\n"),
1054                                                 Prog, optarg);
1055                                         exit(E_BAD_ARG);
1056                                 }
1057                                 user_expire *= DAY/SCALE;
1058                         } else
1059                                 user_expire = -1;
1060                         eflg++;
1061                         break;
1062                 case 'f':
1063                         user_inactive = get_number(optarg);
1064                         fflg++;
1065                         break;
1066 #endif
1067                 case 'g':
1068                         grp = getgr_nam_gid(optarg);
1069                         if (!grp) {
1070                                 fprintf(stderr,
1071                                         _("%s: unknown group %s\n"),
1072                                         Prog, optarg);
1073                                 exit(E_NOTFOUND);
1074                         }
1075                         user_newgid = grp->gr_gid;
1076                         gflg++;
1077                         break;
1078                 case 'G':
1079                         if (get_groups(optarg))
1080                                 exit(E_NOTFOUND);
1081                         Gflg++;
1082                         break;
1083                 case 'l':
1084                         if (!check_user_name(optarg)) {
1085                                 fprintf(stderr,
1086                                         _("%s: invalid field `%s'\n"),
1087                                         Prog, optarg);
1088                                 exit(E_BAD_ARG);
1089                         }
1090
1091                         /*
1092                          * If the name does not really change, we
1093                          * mustn't set the flag as this will cause
1094                          * rather serious problems later!
1095                          */
1096
1097                         if (strcmp (user_name, optarg))
1098                                 lflg++;
1099
1100                         user_newname = optarg;
1101                         break;
1102                 case 'm':
1103                         if (! dflg)
1104                                 usage ();
1105
1106                         mflg++;
1107                         break;
1108                 case 'o':
1109                         if (! uflg)
1110                                 usage ();
1111
1112                         oflg++;
1113                         break;
1114                 case 'p':
1115                         user_pass = optarg;
1116                         pflg++;
1117                         break;
1118                 case 's':
1119                         if (! VALID (optarg)) {
1120                                 fprintf(stderr,
1121                                         _("%s: invalid field `%s'\n"),
1122                                         Prog, optarg);
1123                                 exit(E_BAD_ARG);
1124                         }
1125                         user_shell = optarg;
1126                         sflg++;
1127                         break;
1128                 case 'u':
1129                         user_newid = get_number(optarg);
1130                         uflg++;
1131                         break;
1132                 default:
1133                         usage ();
1134                 }
1135                 anyflag++;
1136         }
1137         if (anyflag == 0) {
1138                 fprintf(stderr, _("%s: no flags given\n"), Prog);
1139                 exit(E_USAGE);
1140         }
1141
1142 #ifdef SHADOWPWD
1143         if (!is_shadow_pwd && (eflg || fflg)) {
1144                 fprintf(stderr,
1145                         _("%s: shadow passwords required for -e and -f\n"),
1146                         Prog);
1147                 exit(E_USAGE);
1148         }
1149 #endif
1150
1151         if (optind != argc - 1)
1152                 usage ();
1153
1154         if (dflg && strcmp (user_home, user_newhome) == 0)
1155                 dflg = mflg = 0;
1156
1157         if (uflg && user_id == user_newid)
1158                 uflg = oflg = 0;
1159
1160         if (lflg && getpwnam (user_newname)) {
1161                 fprintf(stderr, _("%s: user %s exists\n"), Prog, user_newname);
1162                 exit(E_NAME_IN_USE);
1163         }
1164
1165         if (uflg && !oflg && getpwuid(user_newid)) {
1166                 fprintf(stderr, _("%s: uid %ld is not unique\n"),
1167                         Prog, (long) user_newid);
1168                 exit(E_UID_IN_USE);
1169         }
1170 }
1171
1172 /*
1173  * close_files - close all of the files that were opened
1174  *
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.
1177  */
1178
1179 static void
1180 close_files(void)
1181 {
1182         if (! pw_close ()) {
1183                 fprintf(stderr, _("%s: cannot rewrite password file\n"), Prog);
1184                 fail_exit(E_PW_UPDATE);
1185         }
1186 #ifdef  SHADOWPWD
1187         if (is_shadow_pwd && ! spw_close ()) {
1188                 fprintf(stderr, _("%s: cannot rewrite shadow password file\n"), 
1189                         Prog);
1190                 fail_exit(E_PW_UPDATE);
1191         }
1192 #endif
1193 #ifdef  SHADOWPWD
1194         if (is_shadow_pwd)
1195                 spw_unlock ();
1196 #endif
1197         (void) pw_unlock ();
1198
1199         /*
1200          * Close the DBM and/or flat files
1201          */
1202
1203         endpwent ();
1204 #ifdef  SHADOWPWD
1205         endspent ();
1206 #endif
1207         endgrent ();
1208 #ifdef  SHADOWGRP
1209         endsgent ();
1210 #endif
1211 }
1212
1213 /*
1214  * open_files - lock and open the password files
1215  *
1216  *      open_files() opens the two password files.
1217  */
1218
1219 static void
1220 open_files(void)
1221 {
1222         if (!pw_lock()) {
1223                 fprintf(stderr, _("%s: unable to lock password file\n"), Prog);
1224                 exit(E_PW_UPDATE);
1225         }
1226         if (! pw_open (O_RDWR)) {
1227                 fprintf(stderr, _("%s: unable to open password file\n"), Prog);
1228                 fail_exit(E_PW_UPDATE);
1229         }
1230 #ifdef  SHADOWPWD
1231         if (is_shadow_pwd && ! spw_lock ()) {
1232                 fprintf(stderr, _("%s: cannot lock shadow password file\n"),
1233                         Prog);
1234                 fail_exit(E_PW_UPDATE);
1235         }
1236         if (is_shadow_pwd && ! spw_open (O_RDWR)) {
1237                 fprintf(stderr, _("%s: cannot open shadow password file\n"),
1238                         Prog);
1239                 fail_exit(E_PW_UPDATE);
1240         }
1241 #endif
1242 }
1243
1244 /*
1245  * usr_update - create the user entries
1246  *
1247  *      usr_update() creates the password file entries for this user
1248  *      and will update the group entries if required.
1249  */
1250
1251 static void
1252 usr_update(void)
1253 {
1254         struct passwd pwent;
1255         const struct passwd *pwd;
1256 #ifdef  SHADOWPWD
1257         struct spwd spent;
1258         const struct spwd *spwd = NULL;
1259 #endif
1260 #ifdef AUTH_METHODS
1261         char    old_auth[BUFSIZ];
1262         char    auth_buf[BUFSIZ];
1263 #endif
1264
1265         /*
1266          * Locate the entry in /etc/passwd, which MUST exist.
1267          */
1268
1269         pwd = pw_locate(user_name);
1270         if (!pwd) {
1271                 fprintf(stderr, _("%s: %s not found in /etc/passwd\n"),
1272                         Prog, user_name);
1273                 fail_exit(E_NOTFOUND);
1274         }
1275         pwent = *pwd;
1276         new_pwent (&pwent);
1277
1278 #ifdef  SHADOWPWD
1279
1280         /* 
1281          * Locate the entry in /etc/shadow.  It doesn't have to
1282          * exist, and won't be created if it doesn't.
1283          */
1284
1285         if (is_shadow_pwd && (spwd = spw_locate(user_name))) {
1286                 spent = *spwd;
1287                 new_spent (&spent);
1288         }
1289 #endif
1290
1291 #ifdef AUTH_METHODS
1292
1293 #ifdef  SHADOWPWD
1294         strcpy (old_auth, spwd ? spent.sp_pwdp : pwent.pw_passwd);
1295 #else
1296         strcpy (old_auth, pwent.pw_passwd);
1297 #endif
1298
1299         if (Aflg)
1300                 convert_auth (user_auth, old_auth, auth_arg);
1301
1302         /*
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
1308          */
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)) {
1313                         fprintf(stderr,
1314                                 _("%s: error deleting authentication method\n"),
1315                                 Prog);
1316                         SYSLOG((LOG_ERR, "error deleting auth for `%s'\n",
1317                                 user_name));
1318                         fail_exit(E_PW_UPDATE);
1319                 }
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)) {
1323                         fprintf(stderr,
1324                                 _("%s: error adding authentication method\n"),
1325                                 Prog);
1326                         SYSLOG((LOG_ERR, "error adding auth for `%s'\n",
1327                                 lflg ? user_newname:user_name));
1328                         fail_exit(E_PW_UPDATE);
1329                 }
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)) {
1333                         fprintf(stderr,
1334                                 _("%s: error changing authentication method\n"),
1335                                 Prog);
1336                         SYSLOG((LOG_ERR, "error changing auth for `%s'\n",
1337                                 lflg ? user_newname:user_name));
1338                         fail_exit(E_PW_UPDATE);
1339                 }
1340 #ifdef  SHADOWPWD
1341                 if (spwd)
1342                         spent.sp_pwdp = user_auth;
1343                 else
1344 #endif
1345                         pwent.pw_passwd = user_auth;
1346         }
1347 #endif  /* AUTH_METHODS */
1348         if (lflg || uflg || gflg || cflg || dflg || sflg || Aflg || pflg) {
1349                 if (! pw_update (&pwent)) {
1350                         fprintf(stderr,
1351                                 _("%s: error changing password entry\n"),
1352                                 Prog);
1353                         fail_exit(E_PW_UPDATE);
1354                 }
1355                 if (lflg && ! pw_remove (user_name)) {
1356                         fprintf(stderr,
1357                                 _("%s: error removing password entry\n"),
1358                                 Prog);
1359                         fail_exit(E_PW_UPDATE);
1360                 }
1361 #ifdef NDBM
1362                 if (pw_dbm_present()) {
1363                         if (! pw_dbm_update (&pwent)) {
1364                                 fprintf(stderr,
1365                                         _("%s: error adding password dbm entry\n"),
1366                                         Prog);
1367                                 fail_exit(E_PW_UPDATE);
1368                         }
1369                         if (lflg && (pwd = getpwnam (user_name)) &&
1370                                         ! pw_dbm_remove (pwd)) {
1371                                 fprintf(stderr,
1372                                         _("%s: error removing passwd dbm entry\n"),
1373                                         Prog);
1374                                 fail_exit(E_PW_UPDATE);
1375                         }
1376                 }
1377 #endif
1378         }
1379 #ifdef  SHADOWPWD
1380         if (spwd && (lflg || eflg || fflg || Aflg || pflg)) {
1381                 if (! spw_update (&spent)) {
1382                         fprintf(stderr,
1383                                 _("%s: error adding new shadow password entry\n"),
1384                                 Prog);
1385                         fail_exit(E_PW_UPDATE);
1386                 }
1387                 if (lflg && ! spw_remove (user_name)) {
1388                         fprintf(stderr,
1389                                 _("%s: error removing shadow password entry\n"),
1390                                 Prog);
1391                         fail_exit(E_PW_UPDATE);
1392                 }
1393         }
1394 #ifdef  NDBM
1395         if (spwd && sp_dbm_present()) {
1396                 if (! sp_dbm_update (&spent)) {
1397                         fprintf(stderr,
1398                                 _("%s: error updating shadow passwd dbm entry\n"),
1399                                 Prog);
1400                         fail_exit(E_PW_UPDATE);
1401                 }
1402                 if (lflg && ! sp_dbm_remove (user_name)) {
1403                         fprintf(stderr,
1404                                 _("%s: error removing shadow passwd dbm entry\n"),
1405                                 Prog);
1406                         fail_exit(E_PW_UPDATE);
1407                 }
1408         }
1409 #endif  /* NDBM */
1410 #endif  /* SHADOWPWD */
1411 }
1412
1413 /*
1414  * move_home - move the user's home directory
1415  *
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
1418  *      renamed.
1419  */
1420
1421 static void
1422 move_home(void)
1423 {
1424         struct  stat    sb;
1425
1426         if (mflg && stat (user_home, &sb) == 0) {
1427                 /*
1428                  * Don't try to move it if it is not a directory
1429                  * (but /dev/null for example).  --marekm
1430                  */
1431                 if (!S_ISDIR(sb.st_mode))
1432                         return;
1433
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)) {
1441                                         fprintf(stderr,
1442                                                 _("%s: can't create %s\n"),
1443                                                 Prog, user_newhome);
1444                                 }
1445                                 if (chown (user_newhome,
1446                                                 sb.st_uid, sb.st_gid)) {
1447                                         fprintf(stderr,
1448                                                 _("%s: can't chown %s\n"),
1449                                                 Prog, user_newhome);
1450                                         rmdir (user_newhome);
1451                                         fail_exit(E_HOMEDIR);
1452                                 }
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)
1458                                         return;
1459
1460                                 (void) remove_tree (user_newhome);
1461                                 (void) rmdir (user_newhome);
1462                         }
1463                         fprintf(stderr,
1464                                 _("%s: cannot rename directory %s to %s\n"),
1465                                 Prog, user_home, user_newhome);
1466                         fail_exit(E_HOMEDIR);
1467                 }
1468         }
1469         if (uflg || gflg)
1470                 chown (dflg ? user_newhome:user_home,
1471                         uflg ? user_newid:user_id,
1472                         gflg ? user_newgid:user_gid);
1473 }
1474
1475 /*
1476  * update_files - update the lastlog and faillog files
1477  */
1478
1479 static void
1480 update_files(void)
1481 {
1482         struct  lastlog ll;
1483         struct  faillog fl;
1484         int     fd;
1485
1486         /*
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.
1490          */
1491
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);
1497                 }
1498                 close(fd);
1499         }
1500
1501         /*
1502          * Relocate the "faillog" entries in the same manner.
1503          */
1504
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);
1510                 }
1511                 close(fd);
1512         }
1513 }
1514
1515 #ifndef NO_MOVE_MAILBOX
1516 /*
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
1522  */
1523 static void
1524 move_mailbox(void)
1525 {
1526         const char *maildir;
1527         char mailfile[1024], newmailfile[1024];
1528         int fd;
1529         struct stat st;
1530
1531         maildir = getdef_str("MAIL_DIR");
1532 #ifdef MAIL_SPOOL_DIR
1533         if (!maildir && !getdef_str("MAIL_FILE"))
1534                 maildir = MAIL_SPOOL_DIR;
1535 #endif
1536         if (!maildir)
1537                 return;
1538
1539         /*
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
1544          */
1545
1546         snprintf(mailfile, sizeof mailfile, "%s/%s", maildir, user_name);
1547         fd = open(mailfile, O_RDONLY | O_NONBLOCK, 0);
1548         if (fd < 0) {
1549                 /* no need for warnings if the mailbox doesn't exist */
1550                 if (errno != ENOENT)
1551                         perror(mailfile);
1552                 return;
1553         }
1554         if (fstat(fd, &st) < 0) {
1555                 perror("fstat");
1556                 close(fd);
1557                 return;
1558         }
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);
1563                 close(fd);
1564                 return;
1565         }
1566         if (uflg && fchown(fd, user_newid, (gid_t) -1) < 0)
1567                 perror(_("failed to change mailbox owner"));
1568
1569         close(fd);
1570
1571         if (lflg) {
1572                 snprintf(newmailfile, sizeof newmailfile, "%s/%s", maildir, user_newname);
1573                 if (link(mailfile, newmailfile) || unlink(mailfile))
1574                         perror(_("failed to rename mailbox"));
1575         }
1576 }
1577 #endif
1578
1579 /*
1580  * main - usermod command
1581  */
1582
1583 int
1584 main(int argc, char **argv)
1585 {
1586         int grp_err = 0;
1587
1588         /*
1589          * Get my name so that I can use it to report errors.
1590          */
1591         Prog = Basename(argv[0]);
1592
1593         setlocale(LC_ALL, "");
1594         bindtextdomain(PACKAGE, LOCALEDIR);
1595         textdomain(PACKAGE);
1596
1597         openlog(Prog, LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_AUTH);
1598
1599 #ifdef SHADOWPWD
1600         is_shadow_pwd = spw_file_present();
1601 #endif
1602 #ifdef SHADOWGRP
1603         is_shadow_grp = sgr_file_present();
1604 #endif
1605
1606         /*
1607          * The open routines for the NDBM files don't use read-write
1608          * as the mode, so we have to clue them in.
1609          */
1610
1611 #ifdef  NDBM
1612         pw_dbm_mode = O_RDWR;
1613 #ifdef  SHADOWPWD
1614         sp_dbm_mode = O_RDWR;
1615 #endif
1616         gr_dbm_mode = O_RDWR;
1617 #ifdef  SHADOWGRP
1618         sg_dbm_mode = O_RDWR;
1619 #endif
1620 #endif  /* NDBM */
1621         process_flags (argc, argv);
1622
1623         /*
1624          * Do the hard stuff - open the files, change the user entries,
1625          * change the home directory, then close and update the files.
1626          */
1627
1628         open_files();
1629
1630         usr_update();
1631
1632         close_files();
1633
1634         if (Gflg || lflg)
1635                 grp_err = grp_update();
1636
1637         if (mflg)
1638                 move_home();
1639
1640 #ifndef NO_MOVE_MAILBOX
1641         if (lflg || uflg)
1642                 move_mailbox();
1643 #endif
1644
1645         if (uflg) {
1646                 update_files();
1647
1648                 /*
1649                  * Change the UID on all of the files owned by `user_id'
1650                  * to `user_newid' in the user's home directory.
1651                  */
1652
1653                 chown_tree(dflg ? user_newhome:user_home,
1654                         user_id, user_newid,
1655                         user_gid, gflg ? user_newgid:user_gid);
1656         }
1657
1658         if (grp_err)
1659                 exit(E_GRP_UPDATE);
1660
1661         exit(E_SUCCESS);
1662         /*NOTREACHED*/
1663 }