]> granicus.if.org Git - shadow/blob - src/pwck.c
* src/chfn.c, man/chfn.1.xml: Add support for long options.
[shadow] / src / pwck.c
1 /*
2  * Copyright (c) 1992 - 1994, Julianne Frances Haugh
3  * Copyright (c) 1996 - 2000, Marek Michałkiewicz
4  * Copyright (c) 2001       , Michał Moskal
5  * Copyright (c) 2001 - 2006, Tomasz Kłoczko
6  * Copyright (c) 2007 - 2010, Nicolas François
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. The name of the copyright holders or contributors may not be used to
18  *    endorse or promote products derived from this software without
19  *    specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
24  * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
25  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33
34 #include <config.h>
35
36 #ident "$Id$"
37
38 #include <fcntl.h>
39 #include <grp.h>
40 #include <pwd.h>
41 #include <stdio.h>
42 #include "chkname.h"
43 #include "commonio.h"
44 #include "defines.h"
45 #include "prototypes.h"
46 #include "pwio.h"
47 #include "shadowio.h"
48 #include "getdef.h"
49 #include "nscd.h"
50 #ifdef WITH_TCB
51 #include "tcbfuncs.h"
52 #endif                          /* WITH_TCB */
53
54 /*
55  * Exit codes
56  */
57 /*@-exitarg@*/
58 #define E_OKAY          0
59 #define E_USAGE         1
60 #define E_BADENTRY      2
61 #define E_CANTOPEN      3
62 #define E_CANTLOCK      4
63 #define E_CANTUPDATE    5
64 #define E_CANTSORT      6
65
66 /*
67  * Global variables
68  */
69 const char *Prog;
70
71 static bool use_system_pw_file = true;
72 static bool use_system_spw_file = true;
73
74 static bool is_shadow = false;
75
76 static bool pw_opened  = false;
77 static bool spw_opened = false;
78
79 static bool pw_locked  = false;
80 static bool spw_locked = false;
81
82 /* Options */
83 static bool read_only = false;
84 static bool sort_mode = false;
85 static bool quiet = false;              /* don't report warnings, only errors */
86
87 /* local function prototypes */
88 static void fail_exit (int code);
89 static void usage (void);
90 static void process_flags (int argc, char **argv);
91 static void open_files (void);
92 static void close_files (bool changed);
93 static void check_pw_file (int *errors, bool *changed);
94 static void check_spw_file (int *errors, bool *changed);
95
96 /*
97  * fail_exit - do some cleanup and exit with the given error code
98  */
99 static void fail_exit (int code)
100 {
101         if (spw_locked) {
102                 if (spw_unlock () == 0) {
103                         fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ());
104                         if (use_system_spw_file) {
105                                 SYSLOG ((LOG_ERR, "failed to unlock %s",
106                                          spw_dbname ()));
107                         }
108                         /* continue */
109                 }
110         }
111
112         if (pw_locked) {
113                 if (pw_unlock () == 0) {
114                         fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ());
115                         if (use_system_pw_file) {
116                                 SYSLOG ((LOG_ERR, "failed to unlock %s",
117                                          pw_dbname ()));
118                         }
119                         /* continue */
120                 }
121         }
122
123         closelog ();
124
125         exit (code);
126 }
127 /*
128  * usage - print syntax message and exit
129  */
130 static void usage (void)
131 {
132 #ifdef WITH_TCB
133         if (getdef_bool ("USE_TCB")) {
134                 fprintf (stderr, _("Usage: %s [-q] [-r] [passwd]\n"),
135                          Prog);
136         } else
137 #endif                          /* WITH_TCB */
138         {
139                 fprintf (stderr,
140                          _("Usage: %s [-q] [-r] [-s] [passwd [shadow]]\n"),
141                          Prog);
142         }
143         exit (E_USAGE);
144 }
145
146 /*
147  * process_flags - parse the command line options
148  *
149  *      It will not return if an error is encountered.
150  */
151 static void process_flags (int argc, char **argv)
152 {
153         int arg;
154
155         /*
156          * Parse the command line arguments
157          */
158         while ((arg = getopt (argc, argv, "eqrs")) != EOF) {
159                 switch (arg) {
160                 case 'e':       /* added for Debian shadow-961025-2 compatibility */
161                 case 'q':
162                         quiet = true;
163                         break;
164                 case 'r':
165                         read_only = true;
166                         break;
167                 case 's':
168                         sort_mode = true;
169                         break;
170                 default:
171                         usage ();
172                 }
173         }
174
175         if (sort_mode && read_only) {
176                 fprintf (stderr, _("%s: -s and -r are incompatible\n"), Prog);
177                 exit (E_USAGE);
178         }
179
180         /*
181          * Make certain we have the right number of arguments
182          */
183         if ((argc < optind) || (argc > (optind + 2))) {
184                 usage ();
185         }
186
187         /*
188          * If there are two left over filenames, use those as the password
189          * and shadow password filenames.
190          */
191         if (optind != argc) {
192                 pw_setdbname (argv[optind]);
193                 use_system_pw_file = false;
194         }
195         if ((optind + 2) == argc) {
196 #ifdef WITH_TCB
197                 if (getdef_bool ("USE_TCB")) {
198                         fprintf (stderr,
199                                  _("%s: no alternative shadow file allowed when USE_TCB is enabled.\n"),
200                                  Prog);
201                         usage ();
202                 }
203 #endif                          /* WITH_TCB */
204                 spw_setdbname (argv[optind + 1]);
205                 is_shadow = true;
206                 use_system_spw_file = false;
207         } else if (optind == argc) {
208                 is_shadow = spw_file_present ();
209         }
210 }
211
212 /*
213  * open_files - open the shadow database
214  *
215  *      In read-only mode, the databases are not locked and are opened
216  *      only for reading.
217  */
218 static void open_files (void)
219 {
220         bool use_tcb = false;
221 #ifdef WITH_TCB
222         use_tcb = getdef_bool ("USE_TCB");
223 #endif                          /* WITH_TCB */
224
225         /*
226          * Lock the files if we aren't in "read-only" mode
227          */
228         if (!read_only) {
229                 if (pw_lock () == 0) {
230                         fprintf (stderr,
231                                  _("%s: cannot lock %s; try again later.\n"),
232                                  Prog, pw_dbname ());
233                         fail_exit (E_CANTLOCK);
234                 }
235                 pw_locked = true;
236                 if (is_shadow && !use_tcb) {
237                         if (spw_lock () == 0) {
238                                 fprintf (stderr,
239                                          _("%s: cannot lock %s; try again later.\n"),
240                                          Prog, spw_dbname ());
241                                 fail_exit (E_CANTLOCK);
242                         }
243                         spw_locked = true;
244                 }
245         }
246
247         /*
248          * Open the files. Use O_RDONLY if we are in read_only mode, O_RDWR
249          * otherwise.
250          */
251         if (pw_open (read_only ? O_RDONLY : O_RDWR) == 0) {
252                 fprintf (stderr, _("%s: cannot open %s\n"),
253                          Prog, pw_dbname ());
254                 if (use_system_pw_file) {
255                         SYSLOG ((LOG_WARN, "cannot open %s", pw_dbname ()));
256                 }
257                 fail_exit (E_CANTOPEN);
258         }
259         pw_opened = true;
260         if (is_shadow && !use_tcb) {
261                 if (spw_open (read_only ? O_RDONLY : O_RDWR) == 0) {
262                         fprintf (stderr, _("%s: cannot open %s\n"),
263                                  Prog, spw_dbname ());
264                         if (use_system_spw_file) {
265                                 SYSLOG ((LOG_WARN, "cannot open %s",
266                                          spw_dbname ()));
267                         }
268                         fail_exit (E_CANTOPEN);
269                 }
270                 spw_opened = true;
271         }
272 }
273
274 /*
275  * close_files - close and unlock the password/shadow databases
276  *
277  *      If changed is not set, the databases are not closed, and no
278  *      changes are committed in the databases. The databases are
279  *      unlocked anyway.
280  */
281 static void close_files (bool changed)
282 {
283         /*
284          * All done. If there were no change we can just abandon any
285          * changes to the files.
286          */
287         if (changed) {
288                 if (pw_opened && pw_close () == 0) {
289                         fprintf (stderr,
290                                  _("%s: failure while writing changes to %s\n"),
291                                  Prog, pw_dbname ());
292                         if (use_system_pw_file) {
293                                 SYSLOG ((LOG_ERR,
294                                          "failure while writing changes to %s",
295                                          pw_dbname ()));
296                         }
297                         fail_exit (E_CANTUPDATE);
298                 }
299                 pw_opened = false;
300                 if (is_shadow && spw_opened && (spw_close () == 0)) {
301                         fprintf (stderr,
302                                  _("%s: failure while writing changes to %s\n"),
303                                  Prog, spw_dbname ());
304                         if (use_system_spw_file) {
305                                 SYSLOG ((LOG_ERR,
306                                          "failure while writing changes to %s",
307                                          spw_dbname ()));
308                         }
309                         fail_exit (E_CANTUPDATE);
310                 }
311                 spw_opened = false;
312         }
313
314         /*
315          * Don't be anti-social - unlock the files when you're done.
316          */
317         if (spw_locked) {
318                 if (spw_unlock () == 0) {
319                         fprintf (stderr,
320                                  _("%s: failed to unlock %s\n"),
321                                  Prog, spw_dbname ());
322                         if (use_system_spw_file) {
323                                 SYSLOG ((LOG_ERR, "failed to unlock %s",
324                                          spw_dbname ()));
325                         }
326                         /* continue */
327                 }
328         }
329         spw_locked = false;
330         if (pw_locked) {
331                 if (pw_unlock () == 0) {
332                         fprintf (stderr,
333                                  _("%s: failed to unlock %s\n"),
334                                  Prog, pw_dbname ());
335                         if (use_system_pw_file) {
336                                 SYSLOG ((LOG_ERR, "failed to unlock %s",
337                                          pw_dbname ()));
338                         }
339                         /* continue */
340                 }
341         }
342         pw_locked = false;
343 }
344
345 /*
346  * check_pw_file - check the content of the passwd file
347  */
348 static void check_pw_file (int *errors, bool *changed)
349 {
350         struct commonio_entry *pfe, *tpfe;
351         struct passwd *pwd;
352         struct spwd *spw;
353
354         /*
355          * Loop through the entire password file.
356          */
357         for (pfe = __pw_get_head (); NULL != pfe; pfe = pfe->next) {
358                 /*
359                  * If this is a NIS line, skip it. You can't "know" what NIS
360                  * is going to do without directly asking NIS ...
361                  */
362                 if (('+' == pfe->line[0]) || ('-' == pfe->line[0])) {
363                         continue;
364                 }
365
366                 /*
367                  * Start with the entries that are completely corrupt.  They
368                  * have no (struct passwd) entry because they couldn't be
369                  * parsed properly.
370                  */
371                 if (NULL == pfe->eptr) {
372                         /*
373                          * Tell the user this entire line is bogus and ask
374                          * them to delete it.
375                          */
376                         puts (_("invalid password file entry"));
377                         printf (_("delete line '%s'? "), pfe->line);
378                         *errors += 1;
379
380                         /*
381                          * prompt the user to delete the entry or not
382                          */
383                         if (!yes_or_no (read_only)) {
384                                 continue;
385                         }
386
387                         /*
388                          * All password file deletions wind up here. This
389                          * code removes the current entry from the linked
390                          * list. When done, it skips back to the top of the
391                          * loop to try out the next list element.
392                          */
393                       delete_pw:
394                         if (use_system_pw_file) {
395                                 SYSLOG ((LOG_INFO, "delete passwd line '%s'",
396                                          pfe->line));
397                         }
398                         *changed = true;
399
400                         __pw_del_entry (pfe);
401                         continue;
402                 }
403
404                 /*
405                  * Password structure is good, start using it.
406                  */
407                 pwd = pfe->eptr;
408
409                 /*
410                  * Make sure this entry has a unique name.
411                  */
412                 for (tpfe = __pw_get_head (); NULL != tpfe; tpfe = tpfe->next) {
413                         const struct passwd *ent = tpfe->eptr;
414
415                         /*
416                          * Don't check this entry
417                          */
418                         if (tpfe == pfe) {
419                                 continue;
420                         }
421
422                         /*
423                          * Don't check invalid entries.
424                          */
425                         if (NULL == ent) {
426                                 continue;
427                         }
428
429                         if (strcmp (pwd->pw_name, ent->pw_name) != 0) {
430                                 continue;
431                         }
432
433                         /*
434                          * Tell the user this entry is a duplicate of
435                          * another and ask them to delete it.
436                          */
437                         puts (_("duplicate password entry"));
438                         printf (_("delete line '%s'? "), pfe->line);
439                         *errors += 1;
440
441                         /*
442                          * prompt the user to delete the entry or not
443                          */
444                         if (yes_or_no (read_only)) {
445                                 goto delete_pw;
446                         }
447                 }
448
449                 /*
450                  * Check for invalid usernames.  --marekm
451                  */
452                 if (!is_valid_user_name (pwd->pw_name)) {
453                         printf (_("invalid user name '%s'\n"), pwd->pw_name);
454                         *errors += 1;
455                 }
456
457                 /*
458                  * Check for invalid user ID.
459                  */
460                 if (pwd->pw_uid == (uid_t)-1) {
461                         printf (_("invalid user ID '%lu'\n"), (long unsigned int)pwd->pw_uid);
462                         *errors += 1;
463                 }
464
465                 /*
466                  * Make sure the primary group exists
467                  */
468                 /* local, no need for xgetgrgid */
469                 if (!quiet && (NULL == getgrgid (pwd->pw_gid))) {
470
471                         /*
472                          * No primary group, just give a warning
473                          */
474
475                         printf (_("user '%s': no group %lu\n"),
476                                 pwd->pw_name, (unsigned long) pwd->pw_gid);
477                         *errors += 1;
478                 }
479
480                 /*
481                  * Make sure the home directory exists
482                  */
483                 if (!quiet && (access (pwd->pw_dir, F_OK) != 0)) {
484                         /*
485                          * Home directory doesn't exist, give a warning
486                          */
487                         printf (_("user '%s': directory '%s' does not exist\n"),
488                                 pwd->pw_name, pwd->pw_dir);
489                         *errors += 1;
490                 }
491
492                 /*
493                  * Make sure the login shell is executable
494                  */
495                 if (   !quiet
496                     && ('\0' != pwd->pw_shell[0])
497                     && (access (pwd->pw_shell, F_OK) != 0)) {
498
499                         /*
500                          * Login shell doesn't exist, give a warning
501                          */
502                         printf (_("user '%s': program '%s' does not exist\n"),
503                                 pwd->pw_name, pwd->pw_shell);
504                         *errors += 1;
505                 }
506
507                 /*
508                  * Make sure this entry exists in the /etc/shadow file.
509                  */
510
511                 if (is_shadow) {
512 #ifdef WITH_TCB
513                         if (getdef_bool ("USE_TCB")) {
514                                 if (shadowtcb_set_user (pwd->pw_name) == SHADOWTCB_FAILURE) {
515                                         printf (_("no tcb directory for %s\n"),
516                                                 pwd->pw_name);
517                                         printf (_("create tcb directory for %s?"),
518                                                 pwd->pw_name);
519                                         *errors += 1;
520                                         if (yes_or_no (read_only)) {
521                                                 if (shadowtcb_create (pwd->pw_name, pwd->pw_uid) == SHADOWTCB_FAILURE) {
522                                                         *errors += 1;
523                                                         printf (_("failed to create tcb directory for %s\n"), pwd->pw_name);
524                                                         continue;
525                                                 }
526                                         } else {
527                                                 continue;
528                                         }
529                                 }
530                                 if (spw_lock () == 0) {
531                                         *errors += 1;
532                                         fprintf (stderr,
533                                                  _("%s: cannot lock %s.\n"),
534                                                  Prog, spw_dbname ());
535                                         continue;
536                                 }
537                                 spw_locked = true;
538                                 if (spw_open (read_only ? O_RDONLY : O_RDWR) == 0) {
539                                         fprintf (stderr,
540                                                  _("%s: cannot open %s\n"),
541                                                  Prog, spw_dbname ());
542                                         *errors += 1;
543                                         if (spw_unlock () == 0) {
544                                                 fprintf (stderr,
545                                                          _("%s: failed to unlock %s\n"),
546                                                          Prog, spw_dbname ());
547                                                 if (use_system_spw_file) {
548                                                         SYSLOG ((LOG_ERR,
549                                                                  "failed to unlock %s",
550                                                                  spw_dbname ()));
551                                                 }
552                                         }
553                                         continue;
554                                 }
555                                 spw_opened = true;
556                         }
557 #endif                          /* WITH_TCB */
558                         spw = (struct spwd *) spw_locate (pwd->pw_name);
559                         if (NULL == spw) {
560                                 printf (_("no matching password file entry in %s\n"),
561                                         spw_dbname ());
562                                 printf (_("add user '%s' in %s? "),
563                                         pwd->pw_name, spw_dbname ());
564                                 *errors += 1;
565                                 if (yes_or_no (read_only)) {
566                                         struct spwd sp;
567                                         struct passwd pw;
568
569                                         sp.sp_namp   = pwd->pw_name;
570                                         sp.sp_pwdp   = pwd->pw_passwd;
571                                         sp.sp_min    =
572                                             getdef_num ("PASS_MIN_DAYS", -1);
573                                         sp.sp_max    =
574                                             getdef_num ("PASS_MAX_DAYS", -1);
575                                         sp.sp_warn   =
576                                             getdef_num ("PASS_WARN_AGE", -1);
577                                         sp.sp_inact  = -1;
578                                         sp.sp_expire = -1;
579                                         sp.sp_flag   = SHADOW_SP_FLAG_UNSET;
580                                         sp.sp_lstchg = (long) time ((time_t *) 0) / SCALE;
581                                         if (0 == sp.sp_lstchg) {
582                                                 /* Better disable aging than
583                                                  * requiring a password change
584                                                  */
585                                                 sp.sp_lstchg = -1;
586                                         }
587                                         *changed = true;
588
589                                         if (spw_update (&sp) == 0) {
590                                                 fprintf (stderr,
591                                                          _("%s: failed to prepare the new %s entry '%s'\n"),
592                                                          Prog, spw_dbname (), sp.sp_namp);
593                                                 fail_exit (E_CANTUPDATE);
594                                         }
595                                         /* remove password from /etc/passwd */
596                                         pw = *pwd;
597                                         pw.pw_passwd = SHADOW_PASSWD_STRING;    /* XXX warning: const */
598                                         if (pw_update (&pw) == 0) {
599                                                 fprintf (stderr,
600                                                          _("%s: failed to prepare the new %s entry '%s'\n"),
601                                                          Prog, pw_dbname (), pw.pw_name);
602                                                 fail_exit (E_CANTUPDATE);
603                                         }
604                                 }
605                         } else {
606                                 /* The passwd entry has a shadow counterpart.
607                                  * Make sure no passwords are in passwd.
608                                  */
609                                 if (strcmp (pwd->pw_passwd, SHADOW_PASSWD_STRING) != 0) {
610                                         printf (_("user %s has an entry in %s, but its password field in %s is not set to 'x'\n"),
611                                                 pwd->pw_name, spw_dbname (), pw_dbname ());
612                                         *errors += 1;
613                                 }
614                         }
615                 }
616 #ifdef WITH_TCB
617                 if (getdef_bool ("USE_TCB") && spw_locked) {
618                         if (spw_opened && (spw_close () == 0)) {
619                                 fprintf (stderr,
620                                          _("%s: failure while writing changes to %s\n"),
621                                          Prog, spw_dbname ());
622                                 if (use_system_spw_file) {
623                                         SYSLOG ((LOG_ERR,
624                                                  "failure while writing changes to %s",
625                                                  spw_dbname ()));
626                                 }
627                         } else {
628                                 spw_opened = false;
629                         }
630                         if (spw_unlock () == 0) {
631                                 fprintf (stderr,
632                                          _("%s: failed to unlock %s\n"),
633                                          Prog, spw_dbname ());
634                                 if (use_system_spw_file) {
635                                         SYSLOG ((LOG_ERR, "failed to unlock %s",
636                                                  spw_dbname ()));
637                                 }
638                         } else {
639                                 spw_locked = false;
640                         }
641                 }
642 #endif                          /* WITH_TCB */
643         }
644 }
645
646 /*
647  * check_spw_file - check the content of the shadowed password file (shadow)
648  */
649 static void check_spw_file (int *errors, bool *changed)
650 {
651         struct commonio_entry *spe, *tspe;
652         struct spwd *spw;
653
654         /*
655          * Loop through the entire shadow password file.
656          */
657         for (spe = __spw_get_head (); NULL != spe; spe = spe->next) {
658                 /*
659                  * Do not treat lines which were missing in shadow
660                  * and were added earlier.
661                  */
662                 if (NULL == spe->line) {
663                         continue;
664                 }
665
666                 /*
667                  * If this is a NIS line, skip it. You can't "know" what NIS
668                  * is going to do without directly asking NIS ...
669                  */
670                 if (('+' == spe->line[0]) || ('-' == spe->line[0])) {
671                         continue;
672                 }
673
674                 /*
675                  * Start with the entries that are completely corrupt. They
676                  * have no (struct spwd) entry because they couldn't be
677                  * parsed properly.
678                  */
679                 if (NULL == spe->eptr) {
680                         /*
681                          * Tell the user this entire line is bogus and ask
682                          * them to delete it.
683                          */
684                         puts (_("invalid shadow password file entry"));
685                         printf (_("delete line '%s'? "), spe->line);
686                         *errors += 1;
687
688                         /*
689                          * prompt the user to delete the entry or not
690                          */
691                         if (!yes_or_no (read_only)) {
692                                 continue;
693                         }
694
695                         /*
696                          * All shadow file deletions wind up here. This code
697                          * removes the current entry from the linked list.
698                          * When done, it skips back to the top of the loop
699                          * to try out the next list element.
700                          */
701                       delete_spw:
702                         if (use_system_spw_file) {
703                                 SYSLOG ((LOG_INFO, "delete shadow line '%s'",
704                                          spe->line));
705                         }
706                         *changed = true;
707
708                         __spw_del_entry (spe);
709                         continue;
710                 }
711
712                 /*
713                  * Shadow password structure is good, start using it.
714                  */
715                 spw = spe->eptr;
716
717                 /*
718                  * Make sure this entry has a unique name.
719                  */
720                 for (tspe = __spw_get_head (); NULL != tspe; tspe = tspe->next) {
721                         const struct spwd *ent = tspe->eptr;
722
723                         /*
724                          * Don't check this entry
725                          */
726                         if (tspe == spe) {
727                                 continue;
728                         }
729
730                         /*
731                          * Don't check invalid entries.
732                          */
733                         if (NULL == ent) {
734                                 continue;
735                         }
736
737                         if (strcmp (spw->sp_namp, ent->sp_namp) != 0) {
738                                 continue;
739                         }
740
741                         /*
742                          * Tell the user this entry is a duplicate of
743                          * another and ask them to delete it.
744                          */
745                         puts (_("duplicate shadow password entry"));
746                         printf (_("delete line '%s'? "), spe->line);
747                         *errors += 1;
748
749                         /*
750                          * prompt the user to delete the entry or not
751                          */
752                         if (yes_or_no (read_only)) {
753                                 goto delete_spw;
754                         }
755                 }
756
757                 /*
758                  * Make sure this entry exists in the /etc/passwd
759                  * file.
760                  */
761                 if (pw_locate (spw->sp_namp) == NULL) {
762                         /*
763                          * Tell the user this entry has no matching
764                          * /etc/passwd entry and ask them to delete it.
765                          */
766                         printf (_("no matching password file entry in %s\n"),
767                                 pw_dbname ());
768                         printf (_("delete line '%s'? "), spe->line);
769                         *errors += 1;
770
771                         /*
772                          * prompt the user to delete the entry or not
773                          */
774                         if (yes_or_no (read_only)) {
775                                 goto delete_spw;
776                         }
777                 }
778
779                 /*
780                  * Warn if last password change in the future.  --marekm
781                  */
782                 if (   !quiet
783                     && (spw->sp_lstchg > (long) time ((time_t *) 0) / SCALE)) {
784                         printf (_("user %s: last password change in the future\n"),
785                                 spw->sp_namp);
786                         *errors += 1;
787                 }
788         }
789 }
790
791 /*
792  * pwck - verify password file integrity
793  */
794 int main (int argc, char **argv)
795 {
796         int errors = 0;
797         bool changed = false;
798
799         /*
800          * Get my name so that I can use it to report errors.
801          */
802         Prog = Basename (argv[0]);
803
804         (void) setlocale (LC_ALL, "");
805         (void) bindtextdomain (PACKAGE, LOCALEDIR);
806         (void) textdomain (PACKAGE);
807
808         OPENLOG ("pwck");
809
810         /* Parse the command line arguments */
811         process_flags (argc, argv);
812
813         open_files ();
814
815         if (sort_mode) {
816                 if (pw_sort () != 0) {
817                         fprintf (stderr,
818                                  _("%s: cannot sort entries in %s\n"),
819                                  Prog, pw_dbname ());
820                         fail_exit (E_CANTSORT);
821                 }
822                 if (is_shadow) {
823                         if (spw_sort () != 0) {
824                                 fprintf (stderr,
825                                          _("%s: cannot sort entries in %s\n"),
826                                          Prog, spw_dbname ());
827                                 fail_exit (E_CANTSORT);
828                         }
829                 }
830                 changed = true;
831         } else {
832                 check_pw_file (&errors, &changed);
833
834                 if (is_shadow) {
835                         check_spw_file (&errors, &changed);
836                 }
837         }
838
839         close_files (changed);
840
841         nscd_flush_cache ("passwd");
842
843         /*
844          * Tell the user what we did and exit.
845          */
846         if (0 != errors) {
847                 printf (changed ?
848                         _("%s: the files have been updated\n") :
849                         _("%s: no changes\n"), Prog);
850         }
851
852         closelog ();
853         return ((0 != errors) ? E_BADENTRY : E_OKAY);
854 }
855