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