home *** CD-ROM | disk | FTP | other *** search
/ The CDPD Public Domain Collection for CDTV 4 / CDPD_IV.bin / networking / tcpip / amitcp-support / wustl-ftpdaemon / src / access.c next >
Encoding:
C/C++ Source or Header  |  1994-06-29  |  24.5 KB  |  842 lines

  1. /* Copyright (c) 1993, 1994  Washington University in Saint Louis
  2.  * All rights reserved.
  3.  *
  4.  * Redistribution and use in source and binary forms, with or without
  5.  * modification, are permitted provided that the following conditions are
  6.  * met: 1. Redistributions of source code must retain the above copyright
  7.  * notice, this list of conditions and the following disclaimer. 2.
  8.  * Redistributions in binary form must reproduce the above copyright notice,
  9.  * this list of conditions and the following disclaimer in the documentation
  10.  * and/or other materials provided with the distribution. 3. All advertising
  11.  * materials mentioning features or use of this software must display the
  12.  * following acknowledgement: This product includes software developed by the
  13.  * Washington University in Saint Louis and its contributors. 4. Neither the
  14.  * name of the University nor the names of its contributors may be used to
  15.  * endorse or promote products derived from this software without specific
  16.  * prior written permission.
  17.  *
  18.  * THIS SOFTWARE IS PROVIDED BY WASHINGTON UNIVERSITY AND CONTRIBUTORS
  19.  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  20.  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  21.  * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASHINGTON
  22.  * UNIVERSITY OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  23.  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  24.  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  25.  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  26.  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  27.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  28.  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  29.  * POSSIBILITY OF SUCH DAMAGE.
  30.  */
  31.  
  32. #include "config.h"
  33.  
  34. #include <stdio.h>
  35. #include <errno.h>
  36. #include <string.h>
  37. #ifdef SYSSYSLOG
  38. #include <sys/syslog.h>
  39. #else
  40. #include <syslog.h>
  41. #endif
  42. #include <time.h>
  43. #include <ctype.h>
  44. #include <pwd.h>
  45. #include <grp.h>
  46.  
  47. #include <sys/types.h>
  48. #include <sys/stat.h>
  49. #ifndef AMIGA
  50. #include <sys/file.h>
  51. #endif
  52. #include <sys/param.h>
  53.  
  54. #include "pathnames.h"
  55. #include "extensions.h"
  56.  
  57. #if defined(SVR4) || defined(ISC) || defined(AMIGA)
  58. #include <fcntl.h>
  59. #endif
  60.  
  61. extern char remotehost[],
  62.   remoteaddr[],
  63.  *aclbuf;
  64. extern int nameserved,
  65.   anonymous,
  66.   guest,
  67.   use_accessfile;
  68. char Shutdown[MAXPATHLEN];
  69. #define MAXLINE    80
  70. static  char  incline[MAXLINE];
  71. int pidfd = -1;
  72.  
  73. extern int fnmatch();
  74.  
  75. /*************************************************************************/
  76. /* FUNCTION  : parse_time                                                */
  77. /* PURPOSE   : Check a single valid-time-string against the current time */
  78. /*             and return whether or not a match occurs.                 */
  79. /* ARGUMENTS : a pointer to the time-string                              */
  80. /*************************************************************************/
  81.  
  82. int
  83. parsetime(char *whattime)
  84. {
  85.     static char *days[] = {"Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Wk"};
  86.     time_t clock;
  87.     struct tm *curtime;
  88.     int wday,
  89.       start,
  90.       stop,
  91.       ltime,
  92.       validday,
  93.       loop,
  94.       match;
  95.  
  96.     (void) time(&clock);
  97.     curtime = localtime(&clock);
  98.     wday = curtime->tm_wday;
  99.     validday = 0;
  100.     match = 1;
  101.  
  102.     while (match && isalpha(*whattime) && isupper(*whattime)) {
  103.         match = 0;
  104.         for (loop = 0; loop < 8; loop++) {
  105.             if (strncmp(days[loop], whattime, 2) == NULL) {
  106.                 whattime += 2;
  107.                 match = 1;
  108.                 if ((wday == loop) | ((loop == 7) && wday && (wday < 6))) {
  109.                     validday = 1;
  110.                 }
  111.             }
  112.         }
  113.     }
  114.  
  115.     if (strncmp(whattime, "Any", 3) == NULL) {
  116.         validday = 1;
  117.         whattime += 3;
  118.     }
  119.     if (!validday)
  120.         return (0);
  121.  
  122.     if (sscanf(whattime, "%d-%d", &start, &stop) == 2) {
  123.         ltime = curtime->tm_min + 100 * curtime->tm_hour;
  124.         if ((start < stop) && ((ltime > start) && ltime < stop))
  125.             return (1);
  126.         if ((start > stop) && ((ltime > start) || ltime < stop))
  127.             return (1);
  128.     } else
  129.         return (1);
  130.  
  131.     return (0);
  132. }
  133.  
  134. /*************************************************************************/
  135. /* FUNCTION  : validtime                                                 */
  136. /* PURPOSE   : Break apart a set of valid time-strings and pass them to  */
  137. /*             parse_time, returning whether or not ANY matches occurred */
  138. /* ARGUMENTS : a pointer to the time-string                              */
  139. /*************************************************************************/
  140.  
  141. int
  142. validtime(char *ptr)
  143. {
  144.     char *nextptr;
  145.     int good;
  146.  
  147.     while (1) {
  148.         nextptr = strchr(ptr, '|');
  149.         if (strchr(ptr, '|') == NULL)
  150.             return (parsetime(ptr));
  151.         *nextptr = '\0';
  152.         good = parsetime(ptr);
  153.         /* gotta restore the | or things get skipped! */
  154.         *nextptr++ = '|';
  155.         if (good)
  156.             return (1);
  157.         ptr = nextptr;
  158.     }
  159. }
  160.  
  161. /*************************************************************************/
  162. /* FUNCTION  : hostmatch                                                 */
  163. /* PURPOSE   : Match remote hostname or address against a glob string    */
  164. /* ARGUMENTS : The string to match                                       */
  165. /* RETURNS   : 0 if no match, 1 if a match occurs                        */
  166. /*************************************************************************/
  167.  
  168. hostmatch(char *addr)
  169. {
  170.     FILE  *incfile;
  171.     char  *ptr;
  172.     int   found = 0;
  173.  
  174.     if (addr == NULL) return(0);
  175.  
  176.     if (isdigit(*addr))
  177.         return(fnmatch(addr, remoteaddr, NULL));
  178.     else if (*addr == '/') {
  179.         /*
  180.          * read addrglobs from named path using similar format as addrglobs
  181.          * in access file
  182.          */
  183.         if ((incfile = fopen(addr, "r")) == NULL) {
  184.             if (errno != ENOENT) syslog(LOG_ERR,
  185.                 "cannot open addrglob file %s: %s", addr, strerror(errno));
  186.             return(0);
  187.         }
  188.         
  189.         while (!found && (fgets(incline, MAXLINE, incfile) != NULL)) {
  190.             ptr = strtok(incline, " \t\n");
  191.             if (ptr && hostmatch(ptr))
  192.                 found = 1;
  193.             while (!found && ((ptr = strtok(NULL, " \t\n")) != NULL)) {
  194.                 if (ptr && hostmatch(ptr))
  195.                     found = 1;
  196.             }
  197.         }
  198.         fclose(incfile);
  199.         return(found);
  200.     }
  201.     else
  202.         return(fnmatch(addr, remotehost, FNM_NOCASE));
  203. }
  204.  
  205. /*************************************************************************/
  206. /* FUNCTION  : acl_guestgroup                                            */
  207. /* PURPOSE   : If the real user is a member of any of the listed groups, */
  208. /*             return 1.  Otherwise return 0.                            */
  209. /* ARGUMENTS : pw, a pointer to the passwd struct for the user           */
  210. /*************************************************************************/
  211.  
  212. int
  213. acl_guestgroup(struct passwd *pw)
  214. {
  215.     struct aclmember *entry = NULL;
  216.     struct group *grp;
  217.     int which;
  218.     char **member;
  219.  
  220.     entry = (struct aclmember *) NULL;
  221.     /* guestgroup <group> [<group> ...] */
  222.  
  223.     while (getaclentry("guestgroup", &entry)) {
  224.         for (which = 0; (which < MAXARGS) && ARG[which]; which++) {
  225.             if (!(grp = getgrnam(ARG[which])))
  226.                 continue;
  227.             if (pw->pw_gid == grp->gr_gid)
  228.                 return (1);
  229.             for (member = grp->gr_mem; *member; member++) {
  230.                 if (!strcmp(*member, pw->pw_name))
  231.                     return (1);
  232.             }
  233.         }
  234.     }
  235.     return (0);
  236. }
  237.  
  238. /*************************************************************************/
  239. /* FUNCTION  : acl_autogroup                                             */
  240. /* PURPOSE   : If the guest user is a member of any of the classes in    */
  241. /*             the autogroup comment, cause a setegid() to the specified */
  242. /*             group.                                                    */
  243. /* ARGUMENTS : pw, a pointer to the passwd struct for the user           */
  244. /*************************************************************************/
  245.  
  246. void
  247. acl_autogroup(struct passwd *pw)
  248. {
  249.     char class[1024];
  250.  
  251.     struct aclmember *entry = NULL;
  252.     struct group *grp;
  253.     gid_t gid;
  254.     int which;
  255.  
  256.     (void) acl_getclass(class);
  257.  
  258.     entry = (struct aclmember *) NULL;
  259.     /* autogroup <group> <class> [<class> ...] */
  260.  
  261.     while (getaclentry("autogroup", &entry)) {
  262.         if (!ARG0 || !ARG1)
  263.             return;
  264.  
  265.         grp = getgrnam(ARG0);
  266.         if (grp) {
  267.             gid = grp->gr_gid;
  268.         } else {
  269.             syslog(LOG_ERR, "autogroup: set group %s not found", ARG0);
  270.             continue;
  271.         }
  272.  
  273.         for (which = 1; (which < MAXARGS) && ARG[which]; which++) {
  274.             if (!strcmp(ARG[which], class)) {
  275.                 pw->pw_gid = gid;
  276.                 return;
  277.             }
  278.         }
  279.     }
  280. }
  281.  
  282. /*************************************************************************/
  283. /* FUNCTION  : acl_setfunctions                                          */
  284. /* PURPOSE   : Scan the ACL buffer and determine what logging to perform */
  285. /*             for this user, and whether or not user is allowed to use  */
  286. /*             the automatic TAR and COMPRESS functions.                 */
  287. /* ARGUMENTS : pointer to buffer to class name, pointer to ACL buffer    */
  288. /*************************************************************************/
  289.  
  290. void
  291. acl_setfunctions(void)
  292. {
  293.     char class[1024];
  294.  
  295.     extern int log_incoming_xfers,
  296.       log_outbound_xfers,
  297.       mangleopts,
  298.       log_commands,
  299.       lgi_failure_threshold;
  300.  
  301.     struct aclmember *entry = NULL;
  302.  
  303.     int l_compress = 0,
  304.       l_tar = 0,
  305.       inbound = 0,
  306.       outbound = 0,
  307.       which,
  308.       set;
  309.  
  310.     log_incoming_xfers = 0;
  311.     log_outbound_xfers = 0;
  312.     log_commands = 0;
  313.  
  314.     (void) acl_getclass(class);
  315.  
  316.     entry = (struct aclmember *) NULL;
  317.     if (getaclentry("loginfails", &entry) && ARG0 != NULL) {
  318.         lgi_failure_threshold = atoi(ARG0);
  319.     }
  320. #ifndef NO_PRIVATE
  321.     entry = (struct aclmember *) NULL;
  322.     if (getaclentry("private", &entry) && !strcmp(ARG0, "yes"))
  323.         priv_setup(_PATH_PRIVATE);
  324. #endif /* !NO_PRIVATE */
  325.  
  326.     entry = (struct aclmember *) NULL;
  327.     set = 0;
  328.     while (!set && getaclentry("compress", &entry)) {
  329.         if (!strcasecmp(ARG0, "yes"))
  330.             l_compress = 1;
  331.         for (which = 1; (which < MAXARGS) && ARG[which]; which++) {
  332.             if (fnmatch(ARG[which], class, NULL)) {
  333.                 mangleopts |= l_compress * (O_COMPRESS | O_UNCOMPRESS);
  334.                 set = 1;
  335.             }
  336.         }
  337.     }
  338.  
  339.     entry = (struct aclmember *) NULL;
  340.     set = 0;
  341.     while (!set && getaclentry("tar", &entry)) {
  342.         if (!strcasecmp(ARG0, "yes"))
  343.             l_tar = 1;
  344.         for (which = 1; (which < MAXARGS) && ARG[which]; which++) {
  345.             if (fnmatch(ARG[which], class, NULL)) {
  346.                 mangleopts |= l_tar * O_TAR;
  347.                 set = 1;
  348.             }
  349.         }
  350.     }
  351.  
  352.     /* plan on expanding command syntax to include classes for each of these */
  353.  
  354.     entry = (struct aclmember *) NULL;
  355.     while (getaclentry("log", &entry)) {
  356.         if (!strcasecmp(ARG0, "commands")) {
  357.             if (anonymous && strcasestr(ARG1, "anonymous"))
  358.                 log_commands = 1;
  359.             if (guest && strcasestr(ARG1, "guest"))
  360.                 log_commands = 1;
  361.             if (!guest && !anonymous && strcasestr(ARG1, "real"))
  362.                 log_commands = 1;
  363.         }
  364.         if (!strcasecmp(ARG0, "transfers")) {
  365.             set = 0;
  366.             if (strcasestr(ARG1, "anonymous") && anonymous)
  367.                 set = 1;
  368.             if (strcasestr(ARG1, "guest") && guest)
  369.                 set = 1;
  370.             if (strcasestr(ARG1, "real") && !guest && !anonymous)
  371.                 set = 1;
  372.             if (strcasestr(ARG2, "inbound"))
  373.                 inbound = 1;
  374.             if (strcasestr(ARG2, "outbound"))
  375.                 outbound = 1;
  376.             if (set)
  377.                 log_incoming_xfers = inbound;
  378.             if (set)
  379.                 log_outbound_xfers = outbound;
  380.         }
  381.     }
  382. }
  383.  
  384. /*************************************************************************/
  385. /* FUNCTION  : acl_getclass                                              */
  386. /* PURPOSE   : Scan the ACL buffer and determine what class user is in   */
  387. /* ARGUMENTS : pointer to buffer to class name, pointer to ACL buffer    */
  388. /*************************************************************************/
  389.  
  390. int
  391. acl_getclass(char *classbuf)
  392. {
  393.     int which;
  394.     struct aclmember *entry = NULL;
  395.  
  396.     while (getaclentry("class", &entry)) {
  397.         if (ARG0)
  398.             strcpy(classbuf, ARG0);
  399.  
  400.         for (which = 2; (which < MAXARGS) && ARG[which]; which++) {
  401.             if (anonymous && strcasestr(ARG1, "anonymous") &&
  402.                 hostmatch(ARG[which]))
  403.                 return (1);
  404.  
  405.             if (guest && strcasestr(ARG1, "guest") && hostmatch(ARG[which]))
  406.                 return (1);
  407.  
  408.             if (!guest && !anonymous && strcasestr(ARG1, "real") &&
  409.                 hostmatch(ARG[which]))
  410.                 return (1);
  411.         }
  412.     }
  413.  
  414.     *classbuf = (char) NULL;
  415.     return (0);
  416.  
  417. }
  418.  
  419. /*************************************************************************/
  420. /* FUNCTION  : acl_getlimit                                              */
  421. /* PURPOSE   : Scan the ACL buffer and determine what limit applies to   */
  422. /*             the user                                                  */
  423. /* ARGUMENTS : pointer class name, pointer to ACL buffer                 */
  424. /*************************************************************************/
  425.  
  426. int
  427. acl_getlimit(char *class, char *msgpathbuf)
  428. {
  429.     int limit;
  430.     struct aclmember *entry = NULL;
  431.  
  432.     if (msgpathbuf)
  433.         *msgpathbuf = NULL;
  434.  
  435.     /* limit <class> <n> <times> [<message_file>] */
  436.     while (getaclentry("limit", &entry)) {
  437.         if (!ARG0 || !ARG1 || !ARG2)
  438.             continue;
  439.         if (!strcmp(class, ARG0)) {
  440.             limit = atoi(ARG1);
  441.             if (validtime(ARG2)) {
  442.                 if (ARG3 && msgpathbuf)
  443.                     strcpy(msgpathbuf, ARG3);
  444.                 return (limit);
  445.             }
  446.         }
  447.     }
  448.     return (-1);
  449. }
  450.  
  451. /*************************************************************************/
  452. /* FUNCTION  : acl_deny                                                  */
  453. /* PURPOSE   : Scan the ACL buffer and determine a deny command applies  */
  454. /* ARGUMENTS : pointer class name, pointer to ACL buffer                 */
  455. /*************************************************************************/
  456.  
  457. int
  458. acl_deny(char *msgpathbuf)
  459. {
  460.     struct aclmember *entry = NULL;
  461.  
  462.     if (msgpathbuf)
  463.         *msgpathbuf = (char) NULL;
  464.  
  465.     /* deny <addrglob> [<message_file>] */
  466.     while (getaclentry("deny", &entry)) {
  467.         if (!ARG0)
  468.             continue;
  469.         if (!nameserved && !strcmp(ARG0, "!nameserved")) {
  470.             if (ARG1)
  471.                 strcpy(msgpathbuf, entry->arg[1]);
  472.             return (1);
  473.         }
  474.         if (hostmatch(ARG0)) {
  475.             if (ARG1)
  476.                 strcpy(msgpathbuf, entry->arg[1]);
  477.             return (1);
  478.         }
  479.     }
  480.     return (0);
  481. }
  482.  
  483. /*************************************************************************/
  484. /* FUNCTION  : acl_countusers                                            */
  485. /* PURPOSE   : Check the anonymous FTP access lists to see if this       */
  486. /*             access is permitted.                                      */
  487. /* ARGUMENTS : none                                                      */
  488. /*************************************************************************/
  489.  
  490. int
  491. acl_countusers(char *class)
  492. {
  493.     int count,
  494.       which;
  495.     char pidfile[MAXPATHLEN];
  496.     pid_t buf[MAXUSERS];
  497. #if !defined(HAVE_FLOCK) && !defined(AMIGA)
  498. struct flock arg;
  499. #endif
  500.  
  501.     /* 
  502.      * if pidfd was not opened previously... 
  503.      * pidfd must stay open after the chroot(~ftp)  
  504.      */
  505.  
  506.     sprintf(pidfile, _PATH_PIDNAMES, class);
  507.  
  508.     if (pidfd < 0) {
  509.         pidfd = open(pidfile, O_RDWR | O_CREAT, 0644);
  510.     }
  511.  
  512.     if (pidfd < 0) {
  513.         syslog(LOG_ERR, "open of pid file failed: %s", strerror(errno));
  514.         return -1;
  515.     }
  516.  
  517. #ifdef AMIGA
  518. #else
  519. #ifdef HAVE_FLOCK
  520.     while (flock(pidfd, LOCK_EX)) {
  521.         syslog(LOG_ERR, "sleeping: flock of pid file failed: %s",
  522. #else 
  523.     arg.l_type = F_WRLCK;
  524.     arg.l_whence = arg.l_start = arg.l_len = 0;
  525.     while ( -1 == fcntl( pidfd, F_SETLK, &arg) ) {
  526.         syslog(LOG_ERR, "sleeping: fcntl lock of pid file failed: %s",
  527. #endif
  528.                strerror(errno));
  529.         sleep(1);
  530.     }
  531. #endif
  532.     lseek(pidfd, 0, L_SET);
  533.  
  534.     count = 0;
  535.  
  536.     if (read(pidfd, buf, sizeof(buf)) == sizeof(buf)) {
  537.         for (which = 0; which < MAXUSERS; which++)
  538.             if (buf[which]
  539. #ifndef AMIGA
  540.  && !kill(buf[which], 0)
  541. #endif
  542.               )
  543.                 count++;
  544.     }
  545. #ifndef AMIGA
  546. #ifdef HAVE_FLOCK
  547.     flock(pidfd, LOCK_UN);
  548. #else
  549.     arg.l_type = F_UNLCK; arg.l_whence = arg.l_start = arg.l_len = 0;
  550.     fcntl(pidfd, F_SETLK, &arg);
  551. #endif
  552. #endif
  553.     return (count);
  554. }
  555.  
  556. /*************************************************************************/
  557. /* FUNCTION  : acl_join                                                  */
  558. /* PURPOSE   : Add the current process to the list of processes in the   */
  559. /*             specified class.                                          */
  560. /* ARGUMENTS : The name of the class to join                             */
  561. /*************************************************************************/
  562.  
  563. void
  564. acl_join(char *class)
  565. {
  566.     int which,
  567.       avail;
  568.     pid_t buf[MAXUSERS];
  569.     char pidfile[MAXPATHLEN];
  570.     pid_t procid;
  571. #if !defined(HAVE_FLOCK) && !defined(AMIGA)
  572.     struct flock arg;
  573. #endif
  574.  
  575.     /* 
  576.      * if pidfd was not opened previously... 
  577.      * pidfd must stay open after the chroot(~ftp)  
  578.      */
  579.  
  580.     sprintf(pidfile, _PATH_PIDNAMES, class);
  581.  
  582.     if (pidfd < 0) {
  583.         pidfd = open(pidfile, O_RDWR | O_CREAT, 0644);
  584.     }
  585.  
  586.     if (pidfd < 0) {
  587.         syslog(LOG_ERR, "open of pid file failed: %s", strerror(errno));
  588.         return;
  589.     }
  590.  
  591. #ifndef AMIGA
  592. #ifdef HAVE_FLOCK
  593.     while (flock(pidfd, LOCK_EX)) {
  594.         syslog(LOG_ERR, "sleeping: flock of pid file failed: %s",
  595. #else 
  596.     arg.l_type = F_WRLCK;
  597.     arg.l_whence = arg.l_start = arg.l_len = 0;
  598.     while ( -1 == fcntl( pidfd, F_SETLK, &arg) ) {
  599.         syslog(LOG_ERR, "sleeping: fcntl lock of pid file failed: %s",
  600. #endif
  601.                strerror(errno));
  602.         sleep(1);
  603.     }
  604. #endif
  605.  
  606.     procid = getpid();
  607.  
  608.     lseek(pidfd, 0, L_SET);
  609.     if (read(pidfd, buf, sizeof(buf)) < sizeof(buf))
  610.         for (which = 0; which < MAXUSERS; buf[which++] = 0)
  611.             continue;
  612.  
  613.     avail = 0;
  614.     for (which = 0; which < MAXUSERS; which++) {
  615.         if ((buf[which] == 0)
  616. #ifndef AMIGA
  617.  || (kill(buf[which], 0) == -1)
  618. #endif
  619.            ) {
  620.             avail = which;
  621.             buf[which] = 0;
  622.         } else if (buf[which] == procid) {
  623.             /* already exists in pid file... */
  624. #ifndef AMIGA
  625. #ifdef HAVE_FLOCK
  626.             flock(pidfd, LOCK_UN);
  627. #else
  628.             arg.l_type = F_UNLCK; arg.l_whence = arg.l_start = arg.l_len = 0; 
  629.             fcntl(pidfd, F_SETLK, &arg);
  630. #endif
  631. #endif
  632.             return;
  633.         }
  634.     }
  635.  
  636.     buf[avail] = procid;
  637.  
  638.     lseek(pidfd, 0, L_SET);
  639.     write(pidfd, buf, sizeof(buf));
  640. #ifndef AMIGA
  641. #ifdef HAVE_FLOCK
  642.     flock(pidfd, LOCK_UN);
  643. #else
  644.     arg.l_type = F_UNLCK; arg.l_whence = arg.l_start = arg.l_len = 0;
  645.     fcntl(pidfd, F_SETLK, &arg);
  646. #endif
  647. #endif
  648.  
  649. }
  650.  
  651. /*************************************************************************/
  652. /* FUNCTION  : acl_remove                                                */
  653. /* PURPOSE   : remove the current process to the list of processes in    */
  654. /*             the specified class.                                      */
  655. /* ARGUMENTS : The name of the class to remove                           */
  656. /*************************************************************************/
  657.  
  658. void
  659. acl_remove()
  660. {
  661.     char class[1024];
  662.     int which,
  663.       avail;
  664.     pid_t buf[MAXUSERS];
  665.     char pidfile[MAXPATHLEN];
  666.     pid_t procid;
  667. #if !defined(HAVE_FLOCK) && !defined(AMIGA)
  668.     struct flock arg;
  669. #endif
  670.  
  671.     if (!acl_getclass(class)) {
  672.         return;
  673.     }
  674.  
  675.     /* 
  676.      * if pidfd was not opened previously... 
  677.      * pidfd must stay open after the chroot(~ftp)  
  678.      */
  679.  
  680.     sprintf(pidfile, _PATH_PIDNAMES, class);
  681.  
  682.     if (pidfd < 0) {
  683.         pidfd = open(pidfile, O_RDWR | O_CREAT, 0644);
  684.     }
  685.  
  686.     if (pidfd < 0) {
  687.         syslog(LOG_ERR, "open of pid file failed: %s", strerror(errno));
  688.         return;
  689.     }
  690.  
  691. #ifndef AMIGA
  692. #ifdef HAVE_FLOCK
  693.     while (flock(pidfd, LOCK_EX)) {
  694.         syslog(LOG_ERR, "sleeping: flock of pid file failed: %s",
  695. #else 
  696.     arg.l_type = F_WRLCK;
  697.     arg.l_whence = arg.l_start = arg.l_len = 0;
  698.     while ( -1 == fcntl( pidfd, F_SETLK, &arg) ) {
  699.         syslog(LOG_ERR, "sleeping: fcntl lock of pid file failed: %s",
  700. #endif
  701.                strerror(errno));
  702.         sleep(1);
  703.     }
  704. #endif
  705.  
  706.     procid = getpid();
  707.  
  708.     lseek(pidfd, 0, L_SET);
  709.     if (read(pidfd, buf, sizeof(buf)) < sizeof(buf))
  710.         for (which = 0; which < MAXUSERS; buf[which++] = 0)
  711.             continue;
  712.  
  713.     avail = 0;
  714.     for (which = 0; which < MAXUSERS; which++) {
  715.         if ((buf[which] == 0)
  716. #ifndef AMIGA
  717.  || (kill(buf[which], 0) == -1)
  718. #endif
  719.            ) {
  720.             avail = which;
  721.             buf[which] = 0;
  722.         } else if (buf[which] == procid) {
  723.             buf[which] = 0;
  724.         }
  725.     }
  726.  
  727.     lseek(pidfd, 0, L_SET);
  728.     write(pidfd, buf, sizeof(buf));
  729. #ifndef AMIGA
  730. #ifdef HAVE_FLOCK
  731.     flock(pidfd, LOCK_UN);
  732. #else
  733.     arg.l_type = F_UNLCK; arg.l_whence = arg.l_start = arg.l_len = 0;
  734.     fcntl(pidfd, F_SETLK, &arg);
  735. #endif
  736. #endif
  737.  
  738.     close(pidfd);
  739.     pidfd = -1;
  740. }
  741.  
  742. /*************************************************************************/
  743. /* FUNCTION  : pr_mesg                                                   */
  744. /* PURPOSE   : Display a message to the user                             */
  745. /* ARGUMENTS : message code, name of file to display                     */
  746. /*************************************************************************/
  747.  
  748. int
  749. pr_mesg(int msgcode, char *msgfile)
  750. {
  751.     FILE *infile;
  752.     char inbuf[1024],
  753.       outbuf[1024],
  754.      *cr;
  755.  
  756.     if (msgfile && strlen(msgfile) > 0) {
  757.         infile = fopen(msgfile, "r");
  758.         if (infile) {
  759.             while (fgets(inbuf, 255, infile) != NULL) {
  760.                 if ((cr = strchr(inbuf, '\n')) != NULL)
  761.                     *cr = '\0';
  762.                 msg_massage(inbuf, outbuf);
  763.                 lreply(msgcode, "%s", outbuf);
  764.             }
  765.             fclose(infile);
  766.         }
  767.     }
  768. }
  769.  
  770. /*************************************************************************/
  771. /* FUNCTION  : access_init                                               */
  772. /* PURPOSE   : Read and parse the access lists to set things up          */
  773. /* ARGUMENTS : none                                                      */
  774. /*************************************************************************/
  775.  
  776. void
  777. access_init(void)
  778. {
  779.     struct aclmember *entry;
  780.  
  781.     if (!readacl(_PATH_FTPACCESS))
  782.         return;
  783.     (void) parseacl();
  784.  
  785.     Shutdown[0] = '\0';
  786.     entry = (struct aclmember *) NULL;
  787.     if (getaclentry("shutdown", &entry) && ARG0 != NULL)
  788.         (void) strncpy(Shutdown, ARG0, sizeof(Shutdown));
  789.  
  790. }
  791.  
  792. /*************************************************************************/
  793. /* FUNCTION  : access_ok                                                 */
  794. /* PURPOSE   : Check the anonymous FTP access lists to see if this       */
  795. /*             access is permitted.                                      */
  796. /* ARGUMENTS : none                                                      */
  797. /*************************************************************************/
  798.  
  799. int
  800. access_ok(int msgcode)
  801. {
  802.     char class[1024],
  803.       msgfile[MAXPATHLEN];
  804.     int limit;
  805.  
  806.     if (!use_accessfile)
  807.         return (1);
  808.  
  809.     if (aclbuf == NULL) {
  810.         syslog(LOG_NOTICE, 
  811.         "ACCESS DENIED (error reading access file) TO %s [%s]",
  812.                  remotehost, remoteaddr);
  813.         return (0);
  814.     }
  815.     if (acl_deny(msgfile)) {
  816.         pr_mesg(msgcode, msgfile);
  817.         syslog(LOG_NOTICE, "ACCESS DENIED (deny command) TO %s [%s]",
  818.                remotehost, remoteaddr);
  819.         return (0);
  820.     }
  821.     /* if user is not in any class, deny access */
  822.     if (!acl_getclass(class)) {
  823.         syslog(LOG_NOTICE, "ACCESS DENIED (not in any class) TO %s [%s]",
  824.                remotehost, remoteaddr);
  825.         return (0);
  826.     }
  827.     /* if no limits defined, no limits apply -- access OK */
  828.     limit = acl_getlimit(class, msgfile);
  829.  
  830.     if ((limit == -1) || (acl_countusers(class) < limit)) {
  831.         acl_join(class);
  832.         return (1);
  833.     } else {
  834.         syslog(LOG_NOTICE, "ACCESS DENIED (user limit %d; class %s) TO %s [%s]",
  835.                limit, class, remotehost, remoteaddr);
  836.         pr_mesg(msgcode, msgfile);
  837.         return (-1);
  838.     }
  839.  
  840.     /* NOTREACHED */
  841. }
  842.