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