]> granicus.if.org Git - shadow/blob - src/pwck.c
use_system_pw_file and use_system_spw_file were not reset.
[shadow] / src / pwck.c
1 /*
2  * Copyright 1992 - 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 #ident "$Id$"
33
34 #include <fcntl.h>
35 #include <grp.h>
36 #include <pwd.h>
37 #include <stdio.h>
38 #include "chkname.h"
39 #include "commonio.h"
40 #include "defines.h"
41 #include "prototypes.h"
42 #include "pwio.h"
43 #include "shadowio.h"
44 #include "getdef.h"
45 #include "nscd.h"
46 extern void __pw_del_entry (const struct commonio_entry *);
47 extern struct commonio_entry *__pw_get_head (void);
48
49 extern void __spw_del_entry (const struct commonio_entry *);
50 extern struct commonio_entry *__spw_get_head (void);
51
52 /*
53  * Exit codes
54  */
55
56 #define E_OKAY          0
57 #define E_USAGE         1
58 #define E_BADENTRY      2
59 #define E_CANTOPEN      3
60 #define E_CANTLOCK      4
61 #define E_CANTUPDATE    5
62
63 /*
64  * Global variables
65  */
66
67 static char *Prog;
68 static const char *pwd_file = PASSWD_FILE;
69 static int use_system_pw_file = 1;
70 static const char *spw_file = SHADOW_FILE;
71 static int use_system_spw_file = 1;
72
73 static int is_shadow = 0;
74
75 /* Options */
76 static int read_only = 0;
77 static int sort_mode = 0;
78 static int quiet = 0;           /* don't report warnings, only errors */
79
80 /* local function prototypes */
81 static void usage (void);
82 static void process_flags (int argc, char **argv);
83 static void open_files (void);
84 static void close_files (int changed);
85 static void check_pw_file (int *errors, int *changed);
86 static void check_spw_file (int *errors, int *changed);
87
88 /*
89  * usage - print syntax message and exit
90  */
91 static void usage (void)
92 {
93         fprintf (stderr, _("Usage: %s [-q] [-r] [-s] [passwd [shadow]]\n"),
94                  Prog);
95         exit (E_USAGE);
96 }
97
98 /*
99  * process_flags - parse the command line options
100  *
101  *      It will not return if an error is encountered.
102  */
103 static void process_flags (int argc, char **argv)
104 {
105         int arg;
106
107         /*
108          * Parse the command line arguments
109          */
110         while ((arg = getopt (argc, argv, "eqrs")) != EOF) {
111                 switch (arg) {
112                 case 'e':       /* added for Debian shadow-961025-2 compatibility */
113                 case 'q':
114                         quiet = 1;
115                         break;
116                 case 'r':
117                         read_only = 1;
118                         break;
119                 case 's':
120                         sort_mode = 1;
121                         break;
122                 default:
123                         usage ();
124                 }
125         }
126
127         if (sort_mode && read_only) {
128                 fprintf (stderr, _("%s: -s and -r are incompatibile\n"), Prog);
129                 exit (E_USAGE);
130         }
131
132         /*
133          * Make certain we have the right number of arguments
134          */
135         if (optind != argc && optind + 1 != argc && optind + 2 != argc)
136                 usage ();
137
138         /*
139          * If there are two left over filenames, use those as the password
140          * and shadow password filenames.
141          */
142         if (optind != argc) {
143                 pwd_file = argv[optind];
144                 pw_name (pwd_file);
145                 use_system_pw_file = 0;
146         }
147         if (optind + 2 == argc) {
148                 spw_file = argv[optind + 1];
149                 spw_name (spw_file);
150                 is_shadow = 1;
151                 use_system_spw_file = 0;
152         } else if (optind == argc)
153                 is_shadow = spw_file_present ();
154 }
155
156 /*
157  * open_files - open the shadow database
158  *
159  *      In read-only mode, the databases are not locked and are opened
160  *      only for reading.
161  */
162 static void open_files (void)
163 {
164         /*
165          * Lock the files if we aren't in "read-only" mode
166          */
167         if (!read_only) {
168                 if (!pw_lock ()) {
169                         fprintf (stderr, _("%s: cannot lock file %s\n"),
170                                  Prog, pwd_file);
171                         if (use_system_pw_file)
172                                 SYSLOG ((LOG_WARN, "cannot lock %s", pwd_file));
173                         closelog ();
174                         exit (E_CANTLOCK);
175                 }
176                 if (is_shadow && !spw_lock ()) {
177                         fprintf (stderr, _("%s: cannot lock file %s\n"),
178                                  Prog, spw_file);
179                         if (use_system_spw_file)
180                                 SYSLOG ((LOG_WARN, "cannot lock %s", spw_file));
181                         closelog ();
182                         exit (E_CANTLOCK);
183                 }
184         }
185
186         /*
187          * Open the files. Use O_RDONLY if we are in read_only mode, O_RDWR
188          * otherwise.
189          */
190         if (!pw_open (read_only ? O_RDONLY : O_RDWR)) {
191                 fprintf (stderr, _("%s: cannot open file %s\n"),
192                          Prog, pwd_file);
193                 if (use_system_pw_file)
194                         SYSLOG ((LOG_WARN, "cannot open %s", pwd_file));
195                 closelog ();
196                 exit (E_CANTOPEN);
197         }
198         if (is_shadow && !spw_open (read_only ? O_RDONLY : O_RDWR)) {
199                 fprintf (stderr, _("%s: cannot open file %s\n"),
200                          Prog, spw_file);
201                 if (use_system_spw_file)
202                         SYSLOG ((LOG_WARN, "cannot open %s", spw_file));
203                 closelog ();
204                 exit (E_CANTOPEN);
205         }
206 }
207
208 /*
209  * close_files - close and unlock the password/shadow databases
210  *
211  *      If changed is not set, the databases are not closed, and no
212  *      changes are committed in the databases. The databases are
213  *      unlocked anyway.
214  */
215 static void close_files (int changed)
216 {
217         /*
218          * All done. If there were no change we can just abandon any
219          * changes to the files.
220          */
221         if (changed) {
222                 if (!pw_close ()) {
223                         fprintf (stderr, _("%s: cannot update file %s\n"),
224                                  Prog, pwd_file);
225                         SYSLOG ((LOG_WARN, "cannot update %s", pwd_file));
226                         closelog ();
227                         exit (E_CANTUPDATE);
228                 }
229                 if (is_shadow && !spw_close ()) {
230                         fprintf (stderr, _("%s: cannot update file %s\n"),
231                                  Prog, spw_file);
232                         SYSLOG ((LOG_WARN, "cannot update %s", spw_file));
233                         closelog ();
234                         exit (E_CANTUPDATE);
235                 }
236         }
237
238         /*
239          * Don't be anti-social - unlock the files when you're done.
240          */
241         if (is_shadow)
242                 spw_unlock ();
243         (void) pw_unlock ();
244 }
245
246 /*
247  * check_pw_file - check the content of the passwd file
248  */
249 static void check_pw_file (int *errors, int *changed)
250 {
251         struct commonio_entry *pfe, *tpfe;
252         struct passwd *pwd;
253         struct spwd *spw;
254
255         /*
256          * Loop through the entire password file.
257          */
258         for (pfe = __pw_get_head (); pfe; pfe = pfe->next) {
259                 /*
260                  * If this is a NIS line, skip it. You can't "know" what NIS
261                  * is going to do without directly asking NIS ...
262                  */
263                 if (pfe->line[0] == '+' || pfe->line[0] == '-')
264                         continue;
265
266                 /*
267                  * Start with the entries that are completely corrupt.  They
268                  * have no (struct passwd) entry because they couldn't be
269                  * parsed properly.
270                  */
271                 if (!pfe->eptr) {
272                         /*
273                          * Tell the user this entire line is bogus and ask
274                          * them to delete it.
275                          */
276                         printf (_("invalid password file entry\n"));
277                         printf (_("delete line '%s'? "), pfe->line);
278                         *errors += 1;
279
280                         /*
281                          * prompt the user to delete the entry or not
282                          */
283                         if (!yes_or_no (read_only))
284                                 continue;
285
286                         /*
287                          * All password file deletions wind up here. This
288                          * code removes the current entry from the linked
289                          * list. When done, it skips back to the top of the
290                          * loop to try out the next list element.
291                          */
292                       delete_pw:
293                         SYSLOG ((LOG_INFO, "delete passwd line `%s'",
294                                  pfe->line));
295                         *changed = 1;
296
297                         __pw_del_entry (pfe);
298                         continue;
299                 }
300
301                 /*
302                  * Password structure is good, start using it.
303                  */
304                 pwd = pfe->eptr;
305
306                 /*
307                  * Make sure this entry has a unique name.
308                  */
309                 for (tpfe = __pw_get_head (); tpfe; tpfe = tpfe->next) {
310                         const struct passwd *ent = tpfe->eptr;
311
312                         /*
313                          * Don't check this entry
314                          */
315                         if (tpfe == pfe)
316                                 continue;
317
318                         /*
319                          * Don't check invalid entries.
320                          */
321                         if (!ent)
322                                 continue;
323
324                         if (strcmp (pwd->pw_name, ent->pw_name) != 0)
325                                 continue;
326
327                         /*
328                          * Tell the user this entry is a duplicate of
329                          * another and ask them to delete it.
330                          */
331                         printf (_("duplicate password entry\n"));
332                         printf (_("delete line '%s'? "), pfe->line);
333                         *errors += 1;
334
335                         /*
336                          * prompt the user to delete the entry or not
337                          */
338                         if (yes_or_no (read_only))
339                                 goto delete_pw;
340                 }
341
342                 /*
343                  * Check for invalid usernames.  --marekm
344                  */
345                 if (!check_user_name (pwd->pw_name)) {
346                         printf (_("invalid user name '%s'\n"), pwd->pw_name);
347                         *errors += 1;
348                 }
349
350                 /*
351                  * Make sure the primary group exists
352                  */
353                 /* local, no need for xgetgrgid */
354                 if (!quiet && !getgrgid (pwd->pw_gid)) {
355
356                         /*
357                          * No primary group, just give a warning
358                          */
359
360                         printf (_("user %s: no group %u\n"),
361                                 pwd->pw_name, pwd->pw_gid);
362                         *errors += 1;
363                 }
364
365                 /*
366                  * Make sure the home directory exists
367                  */
368                 if (!quiet && access (pwd->pw_dir, F_OK)) {
369                         /*
370                          * Home directory doesn't exist, give a warning
371                          */
372                         printf (_
373                                 ("user %s: directory %s does not exist\n"),
374                                 pwd->pw_name, pwd->pw_dir);
375                         *errors += 1;
376                 }
377
378                 /*
379                  * Make sure the login shell is executable
380                  */
381                 if (!quiet && pwd->pw_shell[0]
382                     && access (pwd->pw_shell, F_OK)) {
383
384                         /*
385                          * Login shell doesn't exist, give a warning
386                          */
387                         printf (_("user %s: program %s does not exist\n"),
388                                 pwd->pw_name, pwd->pw_shell);
389                         *errors += 1;
390                 }
391
392                 /*
393                  * Make sure this entry exists in the /etc/shadow file.
394                  */
395
396                 if (is_shadow) {
397                         spw = (struct spwd *) spw_locate (pwd->pw_name);
398                         if (spw == NULL) {
399                                 printf (_
400                                         ("no matching password file entry in %s\n"),
401                                         spw_file);
402                                 printf (_("add user '%s' in %s? "),
403                                         pwd->pw_name, spw_file);
404                                 *errors += 1;
405                                 if (yes_or_no (read_only)) {
406                                         struct spwd sp;
407                                         struct passwd pw;
408
409                                         sp.sp_namp = pwd->pw_name;
410                                         sp.sp_pwdp = pwd->pw_passwd;
411                                         sp.sp_min =
412                                             getdef_num ("PASS_MIN_DAYS", -1);
413                                         sp.sp_max =
414                                             getdef_num ("PASS_MAX_DAYS", -1);
415                                         sp.sp_warn =
416                                             getdef_num ("PASS_WARN_AGE", -1);
417                                         sp.sp_inact = -1;
418                                         sp.sp_expire = -1;
419                                         sp.sp_flag = -1;
420                                         sp.sp_lstchg =
421                                             time ((time_t *) 0) / (24L * 3600L);
422                                         *changed = 1;
423
424                                         if (!spw_update (&sp)) {
425                                                 fprintf (stderr,
426                                                          _
427                                                          ("%s: can't update shadow entry for %s\n"),
428                                                          Prog, sp.sp_namp);
429                                                 exit (E_CANTUPDATE);
430                                         }
431                                         /* remove password from /etc/passwd */
432                                         pw = *pwd;
433                                         pw.pw_passwd = SHADOW_PASSWD_STRING;    /* XXX warning: const */
434                                         if (!pw_update (&pw)) {
435                                                 fprintf (stderr,
436                                                          _
437                                                          ("%s: can't update passwd entry for %s\n"),
438                                                          Prog, pw.pw_name);
439                                                 exit (E_CANTUPDATE);
440                                         }
441                                 }
442                         }
443                 }
444         }
445 }
446
447 /*
448  * check_spw_file - check the content of the shadowed password file (shadow)
449  */
450 static void check_spw_file (int *errors, int *changed)
451 {
452         struct commonio_entry *spe, *tspe;
453         struct spwd *spw;
454
455         /*
456          * Loop through the entire shadow password file.
457          */
458         for (spe = __spw_get_head (); spe; spe = spe->next) {
459                 /*
460                  * Do not treat lines which were missing in shadow
461                  * and were added earlier.
462                  */
463                 if (spe->line == NULL)
464                         continue;
465
466                 /*
467                  * If this is a NIS line, skip it. You can't "know" what NIS
468                  * is going to do without directly asking NIS ...
469                  */
470                 if (spe->line[0] == '+' || spe->line[0] == '-')
471                         continue;
472
473                 /*
474                  * Start with the entries that are completely corrupt. They
475                  * have no (struct spwd) entry because they couldn't be
476                  * parsed properly.
477                  */
478                 if (!spe->eptr) {
479                         /*
480                          * Tell the user this entire line is bogus and ask
481                          * them to delete it.
482                          */
483                         printf (_("invalid shadow password file entry\n"));
484                         printf (_("delete line '%s'? "), spe->line);
485                         *errors += 1;
486
487                         /*
488                          * prompt the user to delete the entry or not
489                          */
490                         if (!yes_or_no (read_only))
491                                 continue;
492
493                         /*
494                          * All shadow file deletions wind up here. This code
495                          * removes the current entry from the linked list.
496                          * When done, it skips back to the top of the loop
497                          * to try out the next list element.
498                          */
499                       delete_spw:
500                         SYSLOG ((LOG_INFO, "delete shadow line `%s'",
501                                  spe->line));
502                         *changed = 1;
503
504                         __spw_del_entry (spe);
505                         continue;
506                 }
507
508                 /*
509                  * Shadow password structure is good, start using it.
510                  */
511                 spw = spe->eptr;
512
513                 /*
514                  * Make sure this entry has a unique name.
515                  */
516                 for (tspe = __spw_get_head (); tspe; tspe = tspe->next) {
517                         const struct spwd *ent = tspe->eptr;
518
519                         /*
520                          * Don't check this entry
521                          */
522                         if (tspe == spe)
523                                 continue;
524
525                         /*
526                          * Don't check invalid entries.
527                          */
528                         if (!ent)
529                                 continue;
530
531                         if (strcmp (spw->sp_namp, ent->sp_namp) != 0)
532                                 continue;
533
534                         /*
535                          * Tell the user this entry is a duplicate of
536                          * another and ask them to delete it.
537                          */
538                         printf (_("duplicate shadow password entry\n"));
539                         printf (_("delete line '%s'? "), spe->line);
540                         *errors += 1;
541
542                         /*
543                          * prompt the user to delete the entry or not
544                          */
545                         if (yes_or_no (read_only))
546                                 goto delete_spw;
547                 }
548
549                 /*
550                  * Make sure this entry exists in the /etc/passwd
551                  * file.
552                  */
553                 if (!pw_locate (spw->sp_namp)) {
554                         /*
555                          * Tell the user this entry has no matching
556                          * /etc/passwd entry and ask them to delete it.
557                          */
558                         printf (_("no matching password file entry in %s\n"),
559                                 pwd_file);
560                         printf (_("delete line '%s'? "), spe->line);
561                         *errors += 1;
562
563                         /*
564                          * prompt the user to delete the entry or not
565                          */
566                         if (yes_or_no (read_only))
567                                 goto delete_spw;
568                 }
569
570                 /*
571                  * Warn if last password change in the future.  --marekm
572                  */
573                 if (!quiet && spw->sp_lstchg > time ((time_t *) 0) / SCALE) {
574                         printf (_
575                                 ("user %s: last password change in the future\n"),
576                                 spw->sp_namp);
577                         *errors += 1;
578                 }
579         }
580 }
581
582 /*
583  * pwck - verify password file integrity
584  */
585 int main (int argc, char **argv)
586 {
587         int errors = 0;
588         int changed = 0;
589
590         /*
591          * Get my name so that I can use it to report errors.
592          */
593         Prog = Basename (argv[0]);
594
595         setlocale (LC_ALL, "");
596         bindtextdomain (PACKAGE, LOCALEDIR);
597         textdomain (PACKAGE);
598
599         OPENLOG ("pwck");
600
601         /* Parse the command line arguments */
602         process_flags (argc, argv);
603
604         open_files ();
605
606         if (sort_mode) {
607                 pw_sort ();
608                 if (is_shadow)
609                         spw_sort ();
610                 changed = 1;
611         } else {
612                 check_pw_file (&errors, &changed);
613
614                 if (is_shadow) {
615                         check_spw_file (&errors, &changed);
616                 }
617         }
618
619         close_files (changed);
620
621         nscd_flush_cache ("passwd");
622
623         /*
624          * Tell the user what we did and exit.
625          */
626         if (errors)
627                 printf (changed ?
628                         _("%s: the files have been updated\n") :
629                         _("%s: no changes\n"), Prog);
630
631         closelog ();
632         exit (errors ? E_BADENTRY : E_OKAY);
633 }