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