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