home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume38 / shadow / part08 / pwck.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-08-14  |  10.6 KB  |  601 lines

  1. /*
  2.  * Copyright 1992, 1993, John F. Haugh II
  3.  * All rights reserved.
  4.  *
  5.  * Permission is granted to copy and create derivative works for any
  6.  * non-commercial purpose, provided this copyright notice is preserved
  7.  * in all copies of source code, or included in human readable form
  8.  * and conspicuously displayed on all copies of object code or
  9.  * distribution media.
  10.  *
  11.  * This software is provided on an AS-IS basis and the author makes
  12.  * no warrantee of any kind.
  13.  */
  14.  
  15. #ifndef    lint
  16. static    char    sccsid[] = "@(#)pwck.c    3.2    17:42:44    01 May 1993";
  17. #endif
  18.  
  19. #include <stdio.h>
  20. #include <fcntl.h>
  21. #include <grp.h>
  22. #ifdef    BSD
  23. #include <strings.h>
  24. #else
  25. #include <string.h>
  26. #endif
  27.  
  28. #include "config.h"
  29. #include "pwd.h"
  30. #ifdef    SHADOWPWD
  31. #include "shadow.h"
  32. #endif
  33.  
  34. #ifdef    USE_SYSLOG
  35. #include <syslog.h>
  36.  
  37. #ifndef    LOG_WARN
  38. #define    LOG_WARN LOG_WARNING
  39. #endif
  40. #endif
  41.  
  42. struct    pw_file_entry {
  43.     char    *pwf_line;
  44.     int    pwf_changed;
  45.     struct    passwd    *pwf_entry;
  46.     struct    pw_file_entry *pwf_next;
  47. };
  48.  
  49. #ifdef    SHADOWPWD
  50. struct    spw_file_entry {
  51.     char    *spwf_line;
  52.     int    spwf_changed;
  53.     struct    spwd    *spwf_entry;
  54.     struct    spw_file_entry *spwf_next;
  55. };
  56. #endif
  57.  
  58. /*
  59.  * Exit codes
  60.  */
  61.  
  62. #define    E_OKAY        0
  63. #define    E_USAGE        1
  64. #define    E_BADENTRY    2
  65. #define    E_CANTOPEN    3
  66. #define    E_CANTLOCK    4
  67. #define    E_CANTUPDATE    5
  68.  
  69. /*
  70.  * Message strings
  71.  */
  72.  
  73. char    *CANTOPEN = "%s: cannot open file %s\n";
  74. char    *CANTLOCK = "%s: cannot lock file %s\n";
  75. char    *CANTUPDATE = "%s: cannot update file %s\n";
  76. char    *CHANGES = "%s: the files have been updated; run mkpasswd\n";
  77. char    *NOCHANGES = "%s: no changes\n";
  78. char    *NOGROUP = "user %s: no group %d\n";
  79. char    *NOHOME = "user %s: directory %s does not exist\n";
  80. char    *NOSHELL = "user %s: program %s does not exist\n";
  81. char    *BADENTRY = "invalid password file entry\n";
  82. char    *PWDUP = "duplicate password entry\n";
  83. char    *DELETE = "delete line `%s'? ";
  84. char    *NO = "No";
  85. #ifdef    SHADOWPWD
  86. char    *BADSENTRY = "invalid shadow password file entry\n";
  87. char    *SPWDUP = "duplicate shadow password entry\n";
  88. #endif
  89.  
  90. /*
  91.  * Global variables
  92.  */
  93.  
  94. extern    int    optind;
  95. extern    char    *optarg;
  96. extern    struct    pw_file_entry    *__pwf_head;
  97. extern    int    __pw_changed;
  98. #ifdef    SHADOWPWD
  99. extern    struct    spw_file_entry    *__spwf_head;
  100. extern    int    __sp_changed;
  101. #endif
  102.  
  103. /*
  104.  * Local variables
  105.  */
  106.  
  107. char    *Prog;
  108. char    *pwd_file = PWDFILE;
  109. #ifdef    SHADOWPWD
  110. char    *spw_file = SHADOW;
  111. #endif
  112. char    read_only;
  113.  
  114. /*
  115.  * usage - print syntax message and exit
  116.  */
  117.  
  118. usage ()
  119. {
  120. #ifdef    SHADOWPWD
  121.     fprintf (stderr, "Usage: %s [ -r ] [ passwd shadow ]\n", Prog);
  122. #else
  123.     fprintf (stderr, "Usage: %s [ -r ] [ passwd ]\n", Prog);
  124. #endif
  125.     exit (E_USAGE);
  126. }
  127.  
  128. /*
  129.  * yes_or_no - get answer to question from the user
  130.  */
  131.  
  132. int
  133. yes_or_no ()
  134. {
  135.     char    buf[BUFSIZ];
  136.  
  137.     /*
  138.      * In read-only mode all questions are answered "no".
  139.      */
  140.  
  141.     if (read_only) {
  142.         puts (NO);
  143.         return 0;
  144.     }
  145.  
  146.     /*
  147.      * Get a line and see what the first character is.
  148.      */
  149.  
  150.     if (fgets (buf, BUFSIZ, stdin))
  151.         return buf[0] == 'y' || buf[0] == 'Y';
  152.  
  153.     return 0;
  154. }
  155.  
  156. /*
  157.  * pwck - verify password file integrity
  158.  */
  159.  
  160. main (argc, argv)
  161. int    argc;
  162. char    **argv;
  163. {
  164.     int    arg;
  165.     int    errors = 0;
  166.     int    deleted = 0;
  167.     struct    pw_file_entry    *pfe, *tpfe;
  168.     struct    passwd    *pwd;
  169. #ifdef    SHADOWPWD
  170.     struct    spw_file_entry    *spe, *tspe;
  171.     struct    spwd    *spw;
  172. #endif
  173.  
  174.     /*
  175.      * Get my name so that I can use it to report errors.
  176.      */
  177.  
  178.     if (Prog = strrchr (argv[0], '/'))
  179.         Prog++;
  180.     else
  181.         Prog = argv[0];
  182.  
  183. #ifdef    USE_SYSLOG
  184.     openlog (Prog, LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_AUTH);
  185. #endif
  186.  
  187.     /*
  188.      * Parse the command line arguments
  189.      */
  190.  
  191.     while ((arg = getopt (argc, argv, "r")) != EOF) {
  192.         if (arg == 'r')
  193.             read_only = 1;
  194.         else if (arg != EOF)
  195.             usage ();
  196.     }
  197.  
  198.     /*
  199.      * Make certain we have the right number of arguments
  200.      */
  201.  
  202. #ifdef    SHADOWPWD
  203.     if (optind != argc && optind + 2 != argc)
  204. #else
  205.     if (optind != argc && optind + 1 != argc)
  206. #endif
  207.         usage ();
  208.  
  209.     /*
  210.      * If there are two left over filenames, use those as the
  211.      * password and shadow password filenames.
  212.      */
  213.  
  214.     if (optind != argc) {
  215.         pwd_file = argv[optind];
  216.         pw_name (pwd_file);
  217. #ifdef    SHADOWPWD
  218.         spw_file = argv[optind + 1];
  219.         spw_name (spw_file);
  220. #endif
  221.     }
  222.  
  223.     /*
  224.      * Lock the files if we aren't in "read-only" mode
  225.      */
  226.  
  227.     if (! read_only) {
  228.         if (! pw_lock ()) {
  229.             fprintf (stderr, CANTLOCK, Prog, pwd_file);
  230. #ifdef    USE_SYSLOG
  231.             if (optind == argc)
  232.                 syslog (LOG_WARN, "cannot lock %s\n", pwd_file);
  233.  
  234.             closelog ();
  235. #endif
  236.             exit (E_CANTLOCK);
  237.         }
  238. #ifdef    SHADOWPWD
  239.         if (! spw_lock ()) {
  240.             fprintf (stderr, CANTLOCK, Prog, spw_file);
  241. #ifdef    USE_SYSLOG
  242.             if (optind == argc)
  243.                 syslog (LOG_WARN, "cannot lock %s\n", spw_file);
  244.  
  245.             closelog ();
  246. #endif
  247.             exit (E_CANTLOCK);
  248.         }
  249. #endif
  250.     }
  251.  
  252.     /*
  253.      * Open the files.  Use O_RDONLY if we are in read_only mode,
  254.      * O_RDWR otherwise.
  255.      */
  256.  
  257.     if (! pw_open (read_only ? O_RDONLY:O_RDWR)) {
  258.         fprintf (stderr, CANTOPEN, Prog, pwd_file);
  259. #ifdef    USE_SYSLOG
  260.         if (optind == argc)
  261.             syslog (LOG_WARN, "cannot open %s\n", pwd_file);
  262.  
  263.         closelog ();
  264. #endif
  265.         exit (E_CANTOPEN);
  266.     }
  267. #ifdef    SHADOWPWD
  268.     if (! spw_open (read_only ? O_RDONLY:O_RDWR)) {
  269.         fprintf (stderr, CANTOPEN, Prog, spw_file);
  270. #ifdef    USE_SYSLOG
  271.         if (optind == argc)
  272.             syslog (LOG_WARN, "cannot open %s\n", spw_file);
  273.  
  274.         closelog ();
  275. #endif
  276.         exit (E_CANTOPEN);
  277.     }
  278. #endif
  279.  
  280.     /*
  281.      * Loop through the entire password file.
  282.      */
  283.  
  284.     for (pfe = __pwf_head;pfe;pfe = pfe->pwf_next) {
  285.  
  286.         /*
  287.          * Start with the entries that are completely corrupt.
  288.          * They have no (struct passwd) entry because they couldn't
  289.          * be parsed properly.
  290.          */
  291.  
  292.         if (pfe->pwf_entry == (struct passwd *) 0) {
  293.  
  294.             /*
  295.              * Tell the user this entire line is bogus and
  296.              * ask them to delete it.
  297.              */
  298.  
  299.             printf (BADENTRY);
  300.             printf (DELETE, pfe->pwf_line);
  301.             errors++;
  302.  
  303.             /*
  304.              * prompt the user to delete the entry or not
  305.              */
  306.  
  307.             if (! yes_or_no ())
  308.                 continue;
  309.  
  310.             /*
  311.              * All password file deletions wind up here.  This
  312.              * code removes the current entry from the linked
  313.              * list.  When done, it skips back to the top of
  314.              * the loop to try out the next list element.
  315.              */
  316.  
  317. delete_pw:
  318. #ifdef    USE_SYSLOG
  319.             syslog (LOG_INFO,
  320.                 "delete passwd line `%s'\n", pfe->pwf_line);
  321. #endif
  322.             deleted++;
  323.             __pw_changed = 1;
  324.  
  325.             /*
  326.              * Simple case - delete from the head of the
  327.              * list.
  328.              */
  329.  
  330.             if (pfe == __pwf_head) {
  331.                 __pwf_head = pfe->pwf_next;
  332.                 continue;
  333.             }
  334.  
  335.             /*
  336.              * Hard case - find entry where pwf_next is
  337.              * the current entry.
  338.              */
  339.  
  340.             for (tpfe = __pwf_head;tpfe->pwf_next != pfe;
  341.                     tpfe = pfe->pwf_next)
  342.                 ;
  343.  
  344.             tpfe->pwf_next = pfe->pwf_next;
  345.             continue;
  346.         }
  347.  
  348.         /*
  349.          * Password structure is good, start using it.
  350.          */
  351.  
  352.         pwd = pfe->pwf_entry;
  353.  
  354.         /*
  355.          * Make sure this entry has a unique name.
  356.          */
  357.  
  358.         for (tpfe = __pwf_head;tpfe;tpfe = tpfe->pwf_next) {
  359.  
  360.             /*
  361.              * Don't check this entry
  362.              */
  363.  
  364.             if (tpfe == pfe)
  365.                 continue;
  366.  
  367.             /*
  368.              * Don't check invalid entries.
  369.              */
  370.  
  371.             if (tpfe->pwf_entry == (struct passwd *) 0)
  372.                 continue;
  373.  
  374.             if (strcmp (pwd->pw_name, tpfe->pwf_entry->pw_name))
  375.                 continue;
  376.  
  377.             /*
  378.              * Tell the user this entry is a duplicate of
  379.              * another and ask them to delete it.
  380.              */
  381.  
  382.             puts (PWDUP);
  383.             printf (DELETE, pfe->pwf_line);
  384.             errors++;
  385.  
  386.             /*
  387.              * prompt the user to delete the entry or not
  388.              */
  389.  
  390.             if (yes_or_no ())
  391.                 goto delete_pw;
  392.         }
  393.  
  394.         /*
  395.          * Make sure the primary group exists
  396.          */
  397.  
  398.         if (! getgrgid (pwd->pw_gid)) {
  399.  
  400.             /*
  401.              * No primary group, just give a warning
  402.              */
  403.  
  404.             printf (NOGROUP, pwd->pw_name, pwd->pw_gid);
  405.             errors++;
  406.         }
  407.  
  408.         /*
  409.          * Make sure the home directory exists
  410.          */
  411.  
  412.         if (access (pwd->pw_dir, 0)) {
  413.  
  414.             /*
  415.              * Home directory doesn't exist, give a warning
  416.              */
  417.  
  418.             printf (NOHOME, pwd->pw_name, pwd->pw_dir);
  419.             errors++;
  420.         }
  421.  
  422.         /*
  423.          * Make sure the login shell is executable
  424.          */
  425.  
  426.         if (pwd->pw_shell[0] && access (pwd->pw_shell, 0)) {
  427.  
  428.             /*
  429.              * Login shell doesn't exist, give a warning
  430.              */
  431.             
  432.             printf (NOSHELL, pwd->pw_name, pwd->pw_shell);
  433.             errors++;
  434.         }
  435.     }
  436.  
  437. #ifdef    SHADOWPWD
  438.     /*
  439.      * Loop through the entire shadow password file.
  440.      */
  441.  
  442.     for (spe = __spwf_head;spe;spe = spe->spwf_next) {
  443.  
  444.         /*
  445.          * Start with the entries that are completely corrupt.
  446.          * They have no (struct spwd) entry because they couldn't
  447.          * be parsed properly.
  448.          */
  449.  
  450.         if (spe->spwf_entry == (struct spwd *) 0) {
  451.  
  452.             /*
  453.              * Tell the user this entire line is bogus and
  454.              * ask them to delete it.
  455.              */
  456.  
  457.             printf (BADSENTRY);
  458.             printf (DELETE, spe->spwf_line);
  459.             errors++;
  460.  
  461.             /*
  462.              * prompt the user to delete the entry or not
  463.              */
  464.  
  465.             if (! yes_or_no ())
  466.                 continue;
  467.  
  468.             /*
  469.              * All shadow file deletions wind up here.  This
  470.              * code removes the current entry from the linked
  471.              * list.  When done, it skips back to the top of
  472.              * the loop to try out the next list element.
  473.              */
  474.  
  475. delete_spw:
  476. #ifdef    USE_SYSLOG
  477.             syslog (LOG_INFO,
  478.                 "delete shadow line `%s'\n", spe->spwf_line);
  479. #endif
  480.             deleted++;
  481.             __sp_changed = 1;
  482.  
  483.             /*
  484.              * Simple case - delete from the head of the
  485.              * list.
  486.              */
  487.  
  488.             if (spe == __spwf_head) {
  489.                 __spwf_head = spe->spwf_next;
  490.                 continue;
  491.             }
  492.  
  493.             /*
  494.              * Hard case - find entry where spwf_next is
  495.              * the current entry.
  496.              */
  497.  
  498.             for (tspe = __spwf_head;tspe->spwf_next != spe;
  499.                     tspe = spe->spwf_next)
  500.                 ;
  501.  
  502.             tspe->spwf_next = spe->spwf_next;
  503.             continue;
  504.         }
  505.  
  506.         /*
  507.          * Shadow password structure is good, start using it.
  508.          */
  509.  
  510.         spw = spe->spwf_entry;
  511.  
  512.         /*
  513.          * Make sure this entry has a unique name.
  514.          */
  515.  
  516.         for (tspe = __spwf_head;tspe;tspe = tspe->spwf_next) {
  517.  
  518.             /*
  519.              * Don't check this entry
  520.              */
  521.  
  522.             if (tspe == spe)
  523.                 continue;
  524.  
  525.             /*
  526.              * Don't check invalid entries.
  527.              */
  528.  
  529.             if (tspe->spwf_entry == (struct spwd *) 0)
  530.                 continue;
  531.  
  532.             if (strcmp (spw->sp_namp, tspe->spwf_entry->sp_namp))
  533.                 continue;
  534.  
  535.             /*
  536.              * Tell the user this entry is a duplicate of
  537.              * another and ask them to delete it.
  538.              */
  539.  
  540.             puts (SPWDUP);
  541.             printf (DELETE, spe->spwf_line);
  542.             errors++;
  543.  
  544.             /*
  545.              * prompt the user to delete the entry or not
  546.              */
  547.  
  548.             if (yes_or_no ())
  549.                 goto delete_spw;
  550.         }
  551.     }
  552. #endif
  553.  
  554.     /*
  555.      * All done.  If there were no deletions we can just abandon any
  556.      * changes to the files.
  557.      */
  558.  
  559.     if (deleted) {
  560.         if (! pw_close ()) {
  561.             fprintf (stderr, CANTUPDATE, Prog, pwd_file);
  562. #ifdef    USE_SYLOG
  563.             syslog (LOG_WARN, "cannot update %s\n", pwd_file);
  564.             closelog ();
  565. #endif
  566.             exit (E_CANTUPDATE);
  567.         }
  568. #ifdef    SHADOWPWD
  569.         if (! spw_close ()) {
  570.             fprintf (stderr, CANTUPDATE, Prog, spw_file);
  571. #ifdef    USE_SYLOG
  572.             syslog (LOG_WARN, "cannot update %s\n", spw_file);
  573.             closelog ();
  574. #endif
  575.             exit (E_CANTUPDATE);
  576.         }
  577. #endif
  578.     }
  579.  
  580.     /*
  581.      * Don't be anti-social - unlock the files when you're done.
  582.      */
  583.  
  584. #ifdef    SHADOWPWD
  585.     (void) spw_unlock ();
  586. #endif
  587.     (void) pw_unlock ();
  588.  
  589.     /*
  590.      * Tell the user what we did and exit.
  591.      */
  592.  
  593.     if (errors)
  594.         printf (deleted ? CHANGES:NOCHANGES, Prog);
  595.  
  596. #ifdef    USE_SYSLOG
  597.     closelog ();
  598. #endif
  599.     exit (errors ? E_BADENTRY:E_OKAY);
  600. }
  601.