home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume38 / shadow / part06 / userdel.c < prev   
C/C++ Source or Header  |  1993-08-14  |  13KB  |  652 lines

  1. /*
  2.  * Copyright 1991, 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[] = "@(#)userdel.c    3.14    08:06:43    07 May 1993";
  17. #endif
  18.  
  19. #include <sys/types.h>
  20. #include <sys/stat.h>
  21. #include <stdio.h>
  22. #include <errno.h>
  23. #include "pwd.h"
  24. #include <grp.h>
  25. #include <ctype.h>
  26. #include <fcntl.h>
  27. #include <time.h>
  28. #include <utmp.h>
  29.  
  30. #ifdef    BSD
  31. #include <strings.h>
  32. #else
  33. #include <string.h>
  34. #endif
  35.  
  36. #include "config.h"
  37. #ifdef    SHADOWPWD
  38. #include "shadow.h"
  39. #endif
  40. #include "pwauth.h"
  41.  
  42. #ifdef    USE_SYSLOG
  43. #include <syslog.h>
  44.  
  45. #ifndef    LOG_WARN
  46. #define    LOG_WARN LOG_WARNING
  47. #endif
  48. #endif
  49.  
  50. #ifndef    NGROUPS_MAX
  51. #define    NGROUPS_MAX    64
  52. #endif
  53.  
  54. #if defined(DIR_XENIX) || defined(DIR_BSD) || defined(DIR_SYSV)
  55. #define    DIR_ANY
  56. #endif
  57.  
  58. char    user_name[BUFSIZ];
  59. uid_t    user_id;
  60. char    user_home[BUFSIZ];
  61.  
  62. char    *Prog;
  63. #ifdef    DIR_ANY
  64. int    rflg;
  65. #endif
  66.  
  67. #ifdef    NDBM
  68. extern    int    pw_dbm_mode;
  69. #ifdef    SHADOWPWD
  70. extern    int    sp_dbm_mode;
  71. #endif
  72. extern    int    gr_dbm_mode;
  73. #ifdef    SHADOWGRP
  74. extern    int    sg_dbm_mode;
  75. #endif
  76. #endif
  77. extern    struct    group    *getgrnam();
  78. extern    struct    group    *getgrgid();
  79. extern    struct    group    *gr_next();
  80. extern    struct    passwd    *getpwnam();
  81. extern    struct    passwd    *pw_next();
  82. extern    struct    passwd    *pw_locate();
  83.  
  84. #ifdef    SHADOWPWD
  85. extern    int    spw_lock();
  86. extern    int    spw_unlock();
  87. extern    int    spw_open();
  88. extern    int    spw_close();
  89. extern    struct    spwd    *spw_locate();
  90. #endif
  91.  
  92. #ifdef    SHADOWGRP
  93. extern    int    sgr_lock();
  94. extern    int    sgr_unlock();
  95. extern    int    sgr_open();
  96. extern    int    sgr_close();
  97. extern    struct    sgrp    *sgr_next();
  98. #endif
  99.  
  100. extern    char    *malloc();
  101.  
  102. /*
  103.  * del_list - delete a member from a list of group members
  104.  *
  105.  *    the array of member names is searched for the old member
  106.  *    name, and if present it is deleted from a freshly allocated
  107.  *    list of users.
  108.  */
  109.  
  110. char **
  111. del_list (list, member)
  112. char    **list;
  113. char    *member;
  114. {
  115.     int    i, j;
  116.     char    **tmp;
  117.  
  118.     /*
  119.      * Scan the list for the new name.  Return the original list
  120.      * pointer if it is present.
  121.      */
  122.  
  123.     for (i = j = 0;list[i] != (char *) 0;i++)
  124.         if (strcmp (list[i], member))
  125.             j++;
  126.  
  127.     if (j == i)
  128.         return list;
  129.  
  130.     /*
  131.      * Allocate a new list pointer large enough to hold all the
  132.      * old entries, and the new entries as well.
  133.      */
  134.  
  135.     if (! (tmp = (char **) malloc ((j + 2) * sizeof member)))
  136.         return 0;
  137.  
  138.     /*
  139.      * Copy the original list to the new list, then append the
  140.      * new member and NULL terminate the result.  This new list
  141.      * is returned to the invoker.
  142.      */
  143.  
  144.     for (i = j = 0;list[i] != (char *) 0;i++)
  145.         if (strcmp (list[i], member))
  146.             tmp[j++] = list[i];
  147.  
  148.     tmp[j] = (char *) 0;
  149.  
  150.     return tmp;
  151. }
  152.  
  153. /*
  154.  * usage - display usage message and exit
  155.  */
  156.  
  157. usage ()
  158. {
  159. #ifdef    DIR_ANY
  160.     fprintf (stderr, "usage: %s [-r] name\n", Prog);
  161. #else
  162.     fprintf (stderr, "usage: %s name\n", Prog);
  163. #endif
  164.     exit (2);
  165. }
  166.  
  167. /*
  168.  * update_groups - delete user from secondary group set
  169.  *
  170.  *    update_groups() takes the user name that was given and searches
  171.  *    the group files for membership in any group.
  172.  */
  173.  
  174. void
  175. update_groups ()
  176. {
  177.     int    i;
  178.     struct    group    *grp;
  179. #ifdef    SHADOWGRP
  180.     struct    sgrp    *sgrp;
  181. #endif    /* SHADOWGRP */
  182.  
  183.     /*
  184.      * Scan through the entire group file looking for the groups that
  185.      * the user is a member of.
  186.      */
  187.  
  188.     for (gr_rewind (), grp = gr_next ();grp;grp = gr_next ()) {
  189.  
  190.         /*
  191.          * See if the user specified this group as one of their
  192.          * concurrent groups.
  193.          */
  194.  
  195.         for (i = 0;grp->gr_mem[i];i++)
  196.             if (strcmp (grp->gr_mem[i], user_name) == 0)
  197.                 break;
  198.  
  199.         if (grp->gr_mem[i] == (char *) 0)
  200.             continue;
  201.  
  202.         /* 
  203.          * Delete the username from the list of group members and
  204.          * update the group entry to reflect the change.
  205.          */
  206.  
  207.         grp->gr_mem = del_list (grp->gr_mem, user_name);
  208.         if (! gr_update (grp))
  209.             fprintf (stderr, "%s: error updating group entry\n",
  210.                 Prog);
  211.  
  212.         /*
  213.          * Update the DBM group file with the new entry as well.
  214.          */
  215.  
  216. #ifdef    NDBM
  217.         if (! gr_dbm_update (grp))
  218.             fprintf (stderr, "%s: cannot update dbm group entry\n",
  219.                 Prog);
  220. #endif    /* NDBM */
  221. #ifdef    USE_SYSLOG
  222.         syslog (LOG_INFO, "delete `%s' from group `%s'\n",
  223.             user_name, grp->gr_name);
  224. #endif    /* USE_SYSLOG */
  225.     }
  226. #ifdef    NDBM
  227.     endgrent ();
  228. #endif    /* NDBM */
  229. #ifdef    SHADOWGRP
  230.     /*
  231.      * Scan through the entire shadow group file looking for the groups
  232.      * that the user is a member of.  Both the administrative list and
  233.      * the ordinary membership list is checked.
  234.      */
  235.  
  236.     for (sgr_rewind (), sgrp = sgr_next ();sgrp;sgrp = sgr_next ()) {
  237.         int    group_changed = 0;
  238.  
  239.         /*
  240.          * See if the user specified this group as one of their
  241.          * concurrent groups.
  242.          */
  243.  
  244.         for (i = 0;sgrp->sg_mem[i];i++)
  245.             if (strcmp (sgrp->sg_mem[i], user_name) == 0)
  246.                 break;
  247.  
  248.         if (sgrp->sg_mem[i]) {
  249.             sgrp->sg_mem = del_list (sgrp->sg_mem, user_name);
  250.             group_changed = 1;
  251.         }
  252.         for (i = 0;sgrp->sg_adm[i];i++) {
  253.             if (strcmp (sgrp->sg_adm[i], user_name) == 0)
  254.                 break;
  255.         }
  256.         if (sgrp->sg_adm[i]) {
  257.             sgrp->sg_adm = del_list (sgrp->sg_adm, user_name);
  258.             group_changed = 1;
  259.         }
  260.         if (! group_changed)
  261.             continue;
  262.  
  263.         if (! sgr_update (sgrp))
  264.             fprintf (stderr, "%s: error updating group entry\n",
  265.                 Prog);
  266. #ifdef    NDBM
  267.         /*
  268.          * Update the DBM group file with the new entry as well.
  269.          */
  270.  
  271.         if (! sg_dbm_update (sgrp))
  272.             fprintf (stderr, "%s: cannot update dbm group entry\n",
  273.                 Prog);
  274. #endif    /* NDBM */
  275. #ifdef    USE_SYSLOG
  276.         syslog (LOG_INFO, "delete `%s' from shadow group `%s'\n",
  277.             user_name, sgrp->sg_name);
  278. #endif    /* USE_SYSLOG */
  279.     }
  280. #ifdef    NDBM
  281.     endsgent ();
  282. #endif    /* NDBM */
  283. #endif    /* SHADOWGRP */
  284. }
  285.  
  286. /*
  287.  * close_files - close all of the files that were opened
  288.  *
  289.  *    close_files() closes all of the files that were opened for this
  290.  *    new user.  This causes any modified entries to be written out.
  291.  */
  292.  
  293. close_files ()
  294. {
  295.     if (! pw_close ())
  296.         fprintf (stderr, "%s: cannot rewrite password file\n", Prog);
  297. #ifdef    SHADOWPWD
  298.     if (! spw_close ())
  299.         fprintf (stderr, "%s: cannot rewrite shadow password file\n",    
  300.             Prog);
  301. #endif
  302.     if (! gr_close ())
  303.         fprintf (stderr, "%s: cannot rewrite group file\n",
  304.             Prog);
  305.  
  306.     (void) gr_unlock ();
  307. #ifdef    SHADOWGRP
  308.     if (! sgr_close ())
  309.         fprintf (stderr, "%s: cannot rewrite shadow group file\n",
  310.             Prog);
  311.  
  312.     (void) sgr_unlock ();
  313. #endif
  314. #ifdef    SHADOWPWD
  315.     (void) spw_unlock ();
  316. #endif
  317.     (void) pw_unlock ();
  318. }
  319.  
  320. /*
  321.  * open_files - lock and open the password files
  322.  *
  323.  *    open_files() opens the two password files.
  324.  */
  325.  
  326. open_files ()
  327. {
  328.     if (! pw_lock ()) {
  329.         fprintf (stderr, "%s: unable to lock password file\n", Prog);
  330.         exit (1);
  331.     }
  332.     if (! pw_open (O_RDWR)) {
  333.         fprintf (stderr, "%s: unable to open password file\n", Prog);
  334.         fail_exit (1);
  335.     }
  336. #ifdef    SHADOWPWD
  337.     if (! spw_lock ()) {
  338.         fprintf (stderr, "%s: cannot lock shadow password file\n", Prog);
  339.         fail_exit (1);
  340.     }
  341.     if (! spw_open (O_RDWR)) {
  342.         fprintf (stderr, "%s: cannot open shadow password file\n", Prog);
  343.         fail_exit (1);
  344.     }
  345. #endif
  346.     if (! gr_lock ()) {
  347.         fprintf (stderr, "%s: unable to lock group file\n", Prog);
  348.         fail_exit (1);
  349.     }
  350.     if (! gr_open (O_RDWR)) {
  351.         fprintf (stderr, "%s: cannot open group file\n", Prog);
  352.         fail_exit (1);
  353.     }
  354. #ifdef    SHADOWGRP
  355.     if (! sgr_lock ()) {
  356.         fprintf (stderr, "%s: unable to lock shadow group file\n", Prog);
  357.         fail_exit (1);
  358.     }
  359.     if (! sgr_open (O_RDWR)) {
  360.         fprintf (stderr, "%s: cannot open shadow group file\n", Prog);
  361.         fail_exit (1);
  362.     }
  363. #endif
  364. }
  365.  
  366. /*
  367.  * update_user - delete the user entries
  368.  *
  369.  *    update_user() deletes the password file entries for this user
  370.  *    and will update the group entries as required.
  371.  */
  372.  
  373. update_user ()
  374. {
  375.     struct    passwd    *pwd;
  376. #ifdef    SHADOWPWD
  377.     struct    spwd    *spwd;
  378.  
  379.     if ((spwd = spw_locate (user_name)) && spwd->sp_pwdp[0] == '@') {
  380.         if (pw_auth (spwd->sp_pwdp + 1, user_name, PW_DELETE)) {
  381. #ifdef    USE_SYSLOG
  382.             syslog (LOG_ERR,
  383.                 "failed deleting auth `%s' for user `%s'\n",
  384.                 spwd->sp_pwdp + 1, user_name);
  385. #endif     /* USE_SYSLOG */
  386.             fprintf (stderr,
  387.                 "%s: error deleting authentication\n",
  388.             Prog);
  389.         }
  390. #ifdef    USE_SYSLOG
  391.         else {
  392.             syslog (LOG_INFO,
  393.                 "delete auth `%s' for user `%s'\n",
  394.                 spwd->sp_pwdp + 1, user_name);
  395.         }
  396. #endif    /* USE_SYSLOG */
  397.     }
  398. #endif    /* SHADOWPWD */
  399.     if ((pwd = pw_locate (user_name)) && pwd->pw_passwd[0] == '@') {
  400.         if (pw_auth (pwd->pw_passwd + 1, user_name, PW_DELETE)) {
  401. #ifdef    USE_SYSLOG
  402.             syslog (LOG_ERR,
  403.                 "failed deleting auth `%s' for user `%s'\n",
  404.                 pwd->pw_passwd + 1, user_name);
  405. #endif     /* USE_SYSLOG */
  406.             fprintf (stderr, "%s: error deleting authentication\n",
  407.                 Prog);
  408.         }
  409. #ifdef    USE_SYSLOG
  410.         else {
  411.             syslog (LOG_INFO,
  412.                 "delete auth `%s' for user `%s'\n",
  413.                 pwd->pw_passwd + 1, user_name);
  414.         }
  415. #endif    /* USE_SYSLOG */
  416.     }
  417.     if (! pw_remove (user_name))
  418.         fprintf (stderr, "%s: error deleting password entry\n", Prog);
  419. #ifdef    SHADOWPWD
  420.     if (! spw_remove (user_name))
  421.         fprintf (stderr, "%s: error deleting shadow password entry\n",
  422.             Prog);
  423. #endif
  424. #if defined(DBM) || defined(NDBM)
  425.     if (access ("/etc/passwd.pag", 0) == 0) {
  426.         if ((pwd = getpwnam (user_name)) && ! pw_dbm_remove (pwd))
  427.             fprintf (stderr,
  428.                 "%s: error deleting password dbm entry\n",
  429.                 Prog);
  430.     }
  431.  
  432.     /*
  433.      * If the user's UID is a duplicate the duplicated entry needs
  434.      * to be updated so that a UID match can be found in the DBM
  435.      * files.
  436.      */
  437.  
  438.     for (pw_rewind (), pwd = pw_next ();pwd;pwd = pw_next ()) {
  439.         if (pwd->pw_uid == user_id) {
  440.             pw_dbm_update (pwd);
  441.             break;
  442.         }
  443.     }
  444. #endif
  445. #if defined(NDBM) && defined(SHADOWPWD)
  446.     if (access ("/etc/shadow.pag", 0) == 0 && ! sp_dbm_remove (user_name))
  447.         fprintf (stderr, "%s: error deleting shadow passwd dbm entry\n",
  448.             Prog);
  449.  
  450.     endspent ();
  451. #endif
  452. #if defined(DBM) || defined(NDBM)
  453.     endpwent ();
  454. #endif
  455. #ifdef    USE_SYSLOG
  456.     syslog (LOG_INFO, "delete user `%s'\n", user_name);
  457. #endif
  458. }
  459.  
  460. /*
  461.  * fail_exit - exit with a failure code after unlocking the files
  462.  */
  463.  
  464. fail_exit (code)
  465. int    code;
  466. {
  467.     (void) pw_unlock ();
  468.     (void) gr_unlock ();
  469. #ifdef    SHADOWPWD
  470.     (void) spw_unlock ();
  471. #endif
  472. #ifdef    SHADOWGRP
  473.     (void) sgr_unlock ();
  474. #endif
  475.     exit (code);
  476. }
  477.  
  478. /*
  479.  * user_busy - see if user is logged in.
  480.  */
  481.  
  482. user_busy (name, uid)
  483. char    *name;
  484. uid_t    uid;
  485. {
  486.     struct    utmp    *utent;
  487.  
  488.     /*
  489.      * We see if the user is logged in by looking for the user name
  490.      * in the utmp file.
  491.      */
  492.  
  493.     setutent ();
  494.  
  495.     while (utent = getutent ()) {
  496. #ifdef    USG
  497.         if (utent->ut_type != USER_PROCESS)
  498.             continue;
  499. #else
  500.         if (utent->ut_user[0] == '\0')
  501.             continue;
  502. #endif
  503.         if (strncmp (utent->ut_user, name, sizeof utent->ut_user))
  504.             continue;
  505.  
  506.         fprintf (stderr, "%s: user %s is currently logged in.\n",
  507.             Prog, name);
  508.         exit (1);
  509.     }
  510. }
  511.  
  512. /* 
  513.  * user_cancel - cancel cron and at jobs
  514.  *
  515.  *    user_cancel removes the crontab and any at jobs for a user
  516.  */
  517.  
  518. user_cancel (user)
  519. char    *user;
  520. {
  521.     char    buf[BUFSIZ];
  522.  
  523. #ifdef    HAS_CRONTAB
  524.  
  525.     /* 
  526.      * Remove the crontab if there is one.
  527.      */
  528.  
  529.     sprintf (buf, "/bin/crontab -r -u %s", user);
  530.     system (buf);
  531. #endif
  532. #ifdef    HAS_ATRM
  533.  
  534.     /*
  535.      * Remove any at jobs as well.
  536.      */
  537.  
  538.     sprintf (buf, "/bin/atrm -f %s", user);
  539.     system (buf);
  540. #endif
  541. }
  542.  
  543. /*
  544.  * main - useradd command
  545.  */
  546.  
  547. main (argc, argv)
  548. int    argc;
  549. char    **argv;
  550. {
  551.     struct    passwd    *pwd;
  552.     int    arg;
  553.     int    errors = 0;
  554.     extern    int    optind;
  555.     extern    char    *optarg;
  556.  
  557.     /*
  558.      * Get my name so that I can use it to report errors.
  559.      */
  560.  
  561.     if (Prog = strrchr (argv[0], '/'))
  562.         Prog++;
  563.     else
  564.         Prog = argv[0];
  565.  
  566. #ifdef    USE_SYSLOG
  567.     openlog (Prog, LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_AUTH);
  568. #endif
  569.  
  570.     /*
  571.      * The open routines for the DBM files don't use read-write
  572.      * as the mode, so we have to clue them in.
  573.      */
  574.  
  575. #if defined(DBM) || defined(NDBM)
  576.     pw_dbm_mode = O_RDWR;
  577. #endif
  578. #ifdef    NDBM
  579. #ifdef    SHADOWPWD
  580.     sp_dbm_mode = O_RDWR;
  581. #endif
  582.     gr_dbm_mode = O_RDWR;
  583. #ifdef    SHADOWGRP
  584.     sg_dbm_mode = O_RDWR;
  585. #endif
  586. #endif
  587.     while ((arg = getopt (argc, argv, "r")) != EOF)
  588. #ifdef    DIR_ANY
  589.         if (arg != 'r')
  590.             usage ();
  591.         else
  592.             rflg++;
  593. #else
  594.         usage ();
  595. #endif
  596.     
  597.     if (optind == argc)
  598.         usage ();
  599.  
  600.     /*
  601.      * Start with a quick check to see if the user exists.
  602.      */
  603.  
  604.     strncpy (user_name, argv[argc - 1], BUFSIZ);
  605.  
  606.     if (! (pwd = getpwnam (user_name))) {
  607.         fprintf (stderr, "%s: user %s does not exist\n",
  608.             Prog, user_name);
  609.         exit (6);
  610.     }
  611.     user_id = pwd->pw_uid;
  612.     strcpy (user_home, pwd->pw_dir);
  613.  
  614.     /*
  615.      * Check to make certain the user isn't logged in.
  616.      */
  617.  
  618.     user_busy (user_name, user_id);
  619.  
  620.     /*
  621.      * Do the hard stuff - open the files, create the user entries,
  622.      * create the home directory, then close and update the files.
  623.      */
  624.  
  625.     open_files ();
  626.  
  627.     update_user ();
  628.     update_groups ();
  629.  
  630. #ifdef    DIR_ANY
  631.     if (rflg) {
  632.         if (remove_tree (user_home) || rmdir (user_home)) {
  633.             fprintf (stderr, "%s: error removing directory %s\n",
  634.                 Prog, user_home);
  635.  
  636.             errors++;
  637.         }
  638.     }
  639. #endif
  640.     /*
  641.      * Cancel any crontabs or at jobs.  Have to do this before we
  642.      * remove the entry from /etc/passwd.
  643.      */
  644.  
  645.     user_cancel (user_name);
  646.  
  647.     close_files ();
  648.  
  649.     exit (errors ? 12:0);
  650.     /*NOTREACHED*/
  651. }
  652.