dirtree.c
上传用户:pycemail
上传日期:2007-01-04
资源大小:329k
文件大小:49k
源码类别:

Ftp客户端

开发平台:

Unix_Linux

  1. /*
  2.  * ProFTPD - FTP server daemon
  3.  * Copyright (c) 1997, 1998 Public Flood Software
  4.  *  
  5.  * This program is free software; you can redistribute it and/or modify
  6.  * it under the terms of the GNU General Public License as published by
  7.  * the Free Software Foundation; either version 2 of the License, or
  8.  * (at your option) any later version.
  9.  *
  10.  * This program is distributed in the hope that it will be useful,
  11.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  * GNU General Public License for more details.
  14.  *
  15.  * You should have received a copy of the GNU General Public License
  16.  * along with this program; if not, write to the Free Software
  17.  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
  18.  */
  19. /* Read configuration file(s), and manage server/configuration
  20.  * structures.
  21.  * $Id: dirtree.c,v 1.10 1999/10/01 03:32:17 macgyver Exp $
  22.  */
  23. /* History:
  24.  * 5/1/97 0.99.0pl2
  25.  *  Used to be named "config.c", renamed to dirtree.c (directive
  26.  *  tree) so as not to conflict with GNU autoconf's top-level config.h.
  27.  */
  28. #include "conf.h"
  29. #include <sys/stat.h>
  30. #include <stdarg.h>
  31. #ifdef HAVE_ARPA_INET_H
  32. # include <arpa/inet.h>
  33. #endif
  34. xaset_t *servers = NULL;
  35. server_rec *main_server = NULL;
  36. int tcpBackLog = TUNABLE_DEFAULT_BACKLOG;
  37. int SocketBindTight = FALSE;
  38. char ServerType = SERVER_STANDALONE;
  39. int ServerMaxInstances = 0;
  40. int ServerUseReverseDNS = TRUE;
  41. int TimeoutLogin = TUNABLE_TIMEOUTLOGIN;
  42. int TimeoutIdle = TUNABLE_TIMEOUTIDLE;
  43. int TimeoutNoXfer = TUNABLE_TIMEOUTNOXFER;
  44. int TimeoutStalled = TUNABLE_TIMEOUTSTALLED;
  45. char MultilineRFC2228 = 0;
  46. /* Used by find_config_* */
  47. xaset_t *find_config_top = NULL;
  48. static void _mergedown(xaset_t*,int);
  49. /* Used by get_param_int_next & get_param_ptr_next as "placeholders" */
  50. static config_rec *_last_param_int = NULL;
  51. static config_rec *_last_param_ptr = NULL;
  52. static int _kludge_disable_umask = 0;
  53. /* Used only while reading configuration files */
  54. struct {
  55.   pool *tpool;
  56.   array_header *sstack,*cstack;
  57.   server_rec **curserver;
  58.   config_rec **curconfig;
  59. } conf;
  60. void kludge_disable_umask()
  61. {
  62.   _kludge_disable_umask = 1;
  63. }
  64. void kludge_enable_umask()
  65. {
  66.   _kludge_disable_umask = 0;
  67. }
  68. char *get_word(char **cp)
  69. {
  70.   char *ret,*dst;
  71.   char quote_mode = 0;
  72.   if(!cp || !*cp || !**cp)
  73.     return NULL;
  74.   while(**cp && isspace((UCHAR)**cp)) (*cp)++;
  75.   if(!**cp)
  76.     return NULL;
  77.   ret = dst = *cp;
  78.   
  79.   if(**cp == '"') {
  80.     quote_mode++;
  81.     (*cp)++;
  82.   }
  83.   while(**cp && (quote_mode ? (**cp != '"') : !isspace((UCHAR)**cp))) {
  84.     if(**cp == '\' && quote_mode) {
  85.       /* escaped char */
  86.       if(*((*cp)+1))
  87.         *dst = *(++(*cp));
  88.     }
  89.     *dst++ = **cp;
  90.     ++(*cp);
  91.   }
  92.   if(**cp) (*cp)++;
  93.   *dst = '';
  94.   return ret;
  95. }
  96. cmd_rec *get_config_cmd(pool *ppool, FILE *fp)
  97. {
  98.   char buf[256],*cp,*wrd;
  99.   cmd_rec *newcmd;
  100.   pool *newpool;
  101.   array_header *tarr;
  102.   int i;
  103.   while(fgets(buf,sizeof(buf)-1,fp)) {
  104.     i = strlen(buf);
  105.     if(i && buf[i-1] == 'n')
  106.       buf[i-1] = '';
  107.     for(cp = buf; *cp && isspace((UCHAR)*cp); cp++) ;
  108.     if(*cp == '#' || !*cp) /* Comment or blank line */
  109.       continue;
  110.     /* Build a new pool for the command structure and array */
  111.     newpool = make_sub_pool(ppool);
  112.     newcmd = (cmd_rec*)pcalloc(newpool,sizeof(cmd_rec));
  113.     newcmd->pool = newpool;
  114.     tarr = make_array(newpool,4,sizeof(char**));
  115.     /* Add each word to the array */
  116.     while((wrd = get_word(&cp)) != NULL) {
  117.       char *tmp = pstrdup(newpool, wrd);
  118.       
  119.       *((char**)push_array(tarr)) = tmp; /* pstrdup(newpool,wrd); */
  120.       newcmd->argc++;
  121.     }
  122.     *((char**)push_array(tarr)) = NULL;
  123.     
  124.     /* The array header's job is done, we can forget about it and
  125.      * it will get purged when the command's pool is cleared
  126.      */
  127.     newcmd->argv = (char**)tarr->elts;
  128.     /* Perform a fixup on configuration directives so that:
  129.      * -argv[0]--  -argv[1]-- ----argv[2]-----
  130.      * <Option     /etc/adir  /etc/anotherdir>
  131.      *   .. becomes ..
  132.      * -argv[0]--  -argv[1]-  ----argv[2]----
  133.      * <Option>    /etc/adir  /etc/anotherdir
  134.      */
  135.     if(newcmd->argc && *(newcmd->argv[0]) == '<') {
  136.       char *cp = newcmd->argv[newcmd->argc-1];
  137.       if(*(cp + strlen(cp)-1) == '>' && newcmd->argc > 1) {
  138.         if(!strcmp(cp,">")) {
  139.           newcmd->argv[newcmd->argc-1] = NULL;
  140.           newcmd->argc--;
  141.         } else
  142.           *(cp + strlen(cp)-1) = '';
  143.         cp = newcmd->argv[0];
  144.         if(*(cp + strlen(cp)-1) != '>')
  145.           newcmd->argv[0] = pstrcat(newcmd->pool,cp,">",NULL);
  146.       }
  147.     }
  148.         
  149.     return newcmd;
  150.   }
  151.   return NULL;
  152. }
  153. void init_dyn_stacks(pool *p,config_rec *top)
  154. {
  155.   conf.sstack = make_array(p,1,sizeof(server_rec*));
  156.   conf.curserver = (server_rec**)push_array(conf.sstack);
  157.   *conf.curserver = main_server;
  158.   conf.cstack = make_array(p,3,sizeof(config_rec*));
  159.   conf.curconfig = (config_rec**)push_array(conf.cstack);
  160.   *conf.curconfig = NULL;
  161.   conf.curconfig = (config_rec**)push_array(conf.cstack);
  162.   *conf.curconfig = top;
  163. }
  164. void init_conf_stacks()
  165. {
  166.   pool *pool = make_sub_pool(permanent_pool);
  167.   conf.tpool = pool;
  168.   conf.sstack = make_array(pool,1,sizeof(server_rec*));
  169.   conf.curserver = (server_rec**)push_array(conf.sstack);
  170.   *conf.curserver = main_server;
  171.   conf.cstack = make_array(pool,10,sizeof(config_rec*));
  172.   conf.curconfig = (config_rec**)push_array(conf.cstack);
  173.   *conf.curconfig = NULL;
  174. }
  175. void free_dyn_stacks()
  176. {
  177.   bzero(&conf,sizeof(conf));
  178. }
  179. void free_conf_stacks()
  180. {
  181.   destroy_pool(conf.tpool);
  182.   bzero(&conf,sizeof(conf));
  183. }
  184. /* Used by modules to start/end configuration sections */
  185. server_rec *start_new_server(const char *addr)
  186. {
  187.   server_rec *s;
  188.   pool *p;
  189.   p = make_sub_pool(permanent_pool);
  190.   s = (server_rec*)pcalloc(p,sizeof(server_rec));
  191.   s->pool = p;
  192.   
  193.   /* Have to make sure it ends up on the end of the chain,
  194.    * otherwise main_server becomes useless.
  195.    */
  196.   xaset_insert_end(servers,(xasetmember_t*)s);
  197.   s->set = servers;
  198.   if(addr)
  199.     s->ServerAddress = pstrdup(s->pool,addr);
  200.   s->ServerPort = -1;
  201.   conf.curserver = (server_rec**)push_array(conf.sstack);
  202.   *conf.curserver = s;
  203.   return s;
  204. }
  205. server_rec *end_new_server()
  206. {
  207.   if(!*conf.curserver)
  208.     return NULL;
  209.   if(conf.curserver == (server_rec**)conf.sstack->elts)
  210.     return NULL; /* Disallow underflows */
  211.   
  212.   conf.curserver--;
  213.   conf.sstack->nelts--;
  214.   return *conf.curserver;
  215. }
  216. /* Starts a sub-configuration */
  217.   
  218. config_rec *start_sub_config(const char *name)
  219. {
  220.   config_rec *c,*parent = *conf.curconfig;
  221.   pool *p;
  222.   xaset_t **set;
  223.   if(parent) {
  224.     p = make_sub_pool(parent->pool);
  225.     set = &parent->subset;
  226.   } else {
  227.     p = make_sub_pool((*conf.curserver)->pool);
  228.     set = &(*conf.curserver)->conf;
  229.   }
  230.   c = (config_rec*)pcalloc(p,sizeof(config_rec));
  231.   if(!*set)
  232.     *set = xaset_create(p,NULL);
  233.   xaset_insert(*set,(xasetmember_t*)c);
  234.   
  235.   c->pool = p;
  236.   c->set = *set;
  237.   c->parent = parent;
  238.   if(name)
  239.     c->name = pstrdup(p,name);
  240.   if(parent && (parent->config_type == CONF_DYNDIR))
  241.     c->flags |= CF_DYNAMIC;
  242.   /* Now insert another level onto the stack */
  243.   if(!*conf.curconfig)
  244.     *conf.curconfig = c;
  245.   else {
  246.     conf.curconfig = (config_rec**)push_array(conf.cstack);
  247.     *conf.curconfig = c;
  248.   }
  249.   return c;
  250. }
  251. /* Pop one level off the stack */
  252. config_rec *end_sub_config()
  253. {
  254.   if(conf.curconfig == (config_rec**)conf.cstack->elts) {
  255.     if(*conf.curconfig)
  256.       *conf.curconfig = NULL;
  257.     return NULL;
  258.   }
  259.   conf.curconfig--;
  260.   conf.cstack->nelts--;
  261.   return *conf.curconfig;
  262. }
  263. /* Adds a config_rec to the specified set */
  264. config_rec *add_config_set(xaset_t **set,const char *name)
  265. {
  266.   pool *p;
  267.   config_rec *c,*parent = NULL;
  268.   if(!*set) {
  269.     p = make_sub_pool(permanent_pool);
  270.     *set = xaset_create(p,NULL);
  271.   } else {
  272.     if((*set)->xas_list)
  273.       parent = ((config_rec*)((*set)->xas_list))->parent;
  274.     p = (*set)->mempool;
  275.   }
  276.   c = (config_rec*)pcalloc(p,sizeof(config_rec));
  277.   
  278.   c->pool = p;
  279.   c->set = *set;
  280.   c->parent = parent;
  281.   if(name)
  282.     c->name = pstrdup(p,name);
  283.   xaset_insert_end(*set,(xasetmember_t*)c);
  284.   return c;
  285. }
  286. /* Adds a config_rec on the current "level" */
  287. config_rec *add_config(const char *name)
  288. {
  289.   server_rec *s = *conf.curserver;
  290.   config_rec *parent,*c = *conf.curconfig;
  291.   pool *p;
  292.   xaset_t **set;
  293.   if(c) {
  294.     parent = c;
  295.     p = c->pool;
  296.     set = &c->subset;
  297.   } else {
  298.     parent = NULL;
  299.     
  300.     if(!s->conf || !s->conf->xas_list)
  301.       p = make_sub_pool(s->pool);
  302.     else
  303.       p = ((config_rec*)s->conf->xas_list)->pool;
  304.     set = &s->conf;
  305.   }
  306.   if(!*set)
  307.     *set = xaset_create(p,NULL);
  308.   c = add_config_set(set,name);
  309.   c->parent = parent;
  310.   return c;
  311. }
  312. array_header *parse_group_expression(pool *p, int *argc, char **argv)
  313. {
  314.   array_header *acl = NULL;
  315.   int cnt = *argc;
  316.   char *s,*ent;
  317.   if(cnt) {
  318.     acl = make_array(p,cnt,(sizeof(char*)));
  319.     while(cnt-- && *(++argv)) {
  320.       s = pstrdup(p,*argv);
  321.       while((ent = get_token(&s,",")) != NULL)
  322.         if(*ent)
  323.           *((char**)push_array(acl)) = ent;
  324.     }
  325.     *argc = acl->nelts;
  326.   } else
  327.     *argc = 0;
  328.   return acl;
  329. }
  330. array_header *parse_user_expression(pool *p, int *argc, char **argv)
  331. {
  332.   return parse_group_expression(p,argc,argv);
  333. }
  334. /* boolean "group-expression" matching, returns 1 if the expression
  335.  * matches
  336.  */
  337. int group_expression(char **expr)
  338. {
  339.   int cnt,found;
  340.   char *grp;
  341.   for(; *expr; expr++) {
  342.     grp = *expr;
  343.     found = FALSE;
  344.     if(*grp == '!') {
  345.       found = !found;
  346.       grp++;
  347.     }
  348.     for(cnt = session.groups->nelts-1; cnt >= 0; cnt--)
  349.       if(strcmp(*(((char**)session.groups->elts)+cnt),grp) == 0) {
  350.         found = !found; break;
  351.       }
  352.     if(!found) {
  353.       expr = NULL;
  354.       break;
  355.     } 
  356.   }
  357.   if(expr)
  358.     return TRUE;
  359.   return FALSE;
  360. }
  361.   
  362. /* boolean "user-expression" matching, returns 1 if the expression
  363.  * matches
  364.  */
  365. int user_expression(char **expr)
  366. {
  367.   int found;
  368.   char *user;
  369.   for(; *expr; expr++) {
  370.     user = *expr;
  371.     found = FALSE;
  372.     if(*user == '!') {
  373.       found = !found;
  374.       user++;
  375.     }
  376.     if(strcmp(session.user,user) == 0)
  377.       found = !found;
  378.     if(!found) {
  379.       expr = NULL;
  380.       break;
  381.     }
  382.   }
  383.   if(expr)
  384.     return TRUE;
  385.   return FALSE;
  386. }
  387. /* Per-directory configuration */
  388. static int _strmatch(register char *s1, register char *s2)
  389. {
  390.   register int len = 0;
  391.   while(*s1 && *s2 && *s1++ == *s2++)
  392.     len++;
  393.   return len;
  394. }
  395. static config_rec *_recur_match_path(pool *p,xaset_t *s, char *path)
  396. {
  397.   config_rec *c,*res;
  398.   char *tmp;
  399.   if(!s)
  400.     return NULL;
  401.   for(c = (config_rec*)s->xas_list; c; c=c->next)
  402.     if(c->config_type == CONF_DIR) {
  403.       tmp = c->name;
  404.       if(c->argv[1]) {
  405.         if(*(char*)(c->argv[1]) == '~')
  406.           c->argv[1] = dir_canonical_path(c->pool,(char*)c->argv[1]);
  407.         tmp = pdircat(p,(char*)c->argv[1],tmp,NULL);
  408.       }
  409.       if(!strcmp(tmp,path))
  410.         return c; /* Exact match */
  411.       if(!strstr(tmp,"/*")) {
  412.         if(*tmp && *(tmp+(strlen(tmp)-1)) == '/') {
  413.           *(tmp+(strlen(tmp)-1)) = '';
  414.           if(!strcmp(tmp,path))
  415.             return c;
  416.         }
  417.         tmp = pstrcat(p,tmp,"/*",NULL);
  418.       }
  419.       /* Temporary measure until we figure what's going on with
  420.        * gnu fnmatch
  421.        */
  422. #if 0
  423.       if(fnmatch(tmp,path,FNM_PATHNAME) == 0) {
  424. #else
  425.       if(fnmatch(tmp,path,0) == 0) {
  426. #endif
  427.         if(c->subset) {
  428.           res = _recur_match_path(p,c->subset,path);
  429.           if(res)
  430.             return res;
  431.         }
  432.         return c;
  433.       }
  434.     }
  435.   return NULL;
  436. }
  437. config_rec *dir_match_path(pool *p, char *path)
  438. {
  439.   char *tmp;
  440.   config_rec *res = NULL;
  441.   tmp = pstrdup(p,path);
  442.   if(*(tmp+strlen(tmp)-1) == '*')
  443.     *(tmp+strlen(tmp)-1) = '';
  444.   if(*(tmp+strlen(tmp)-1) == '/')
  445.     *(tmp+strlen(tmp)-1) = '';
  446.   if(session.anon_config) {
  447.     res = _recur_match_path(p,session.anon_config->subset,tmp);
  448.     if(!res) {
  449.       if(session.anon_root && !strncmp(session.anon_root,tmp,
  450.                                        strlen(session.anon_root)))
  451.         return NULL;
  452.     }
  453.   }
  454.   if(!res)
  455.     res = _recur_match_path(p,main_server->conf,tmp);
  456. /*
  457.   if(!res)
  458.     res = ((session.anon_config) ? session.anon_config : (config_rec*)main_server->conf->xas_list);
  459. */
  460.   return res;
  461. }
  462. int dir_get_param(pool *pp,char *path,char *param)
  463. {
  464.   char *fullpath,*tmp;
  465.   pool *p;
  466.   config_rec *c;
  467.   p = make_sub_pool(pp);
  468.   if(*path != '/')
  469.     fullpath = pstrcat(p,session.cwd,"/",path,NULL);
  470.   else
  471.     fullpath = path;
  472.   if((tmp = dir_realpath(p,fullpath)) != NULL)
  473.     fullpath = tmp;
  474.   if(session.anon_root)
  475.     fullpath = pdircat(p,session.anon_root,fullpath,NULL);
  476.   c = dir_match_path(p,fullpath);
  477.   destroy_pool(p);
  478.   if(c)
  479.     return get_param_int(c->subset,param,FALSE);
  480.   return -1;
  481. }
  482. static int _dir_check_op(pool *p,xaset_t *c,int op,
  483.                          int uid,int gid,int mode)
  484. {
  485.   int i,res = 1,pcheck = 0;
  486.   int *gidp = NULL,u,g;
  487.   if(!c)
  488.     return 1; /* Default is to allow */
  489.   if(uid == session.uid)
  490.     pcheck |= (mode & S_IRWXU);
  491.   else if(session.gid == gid)
  492. isgroup:
  493.     pcheck |= (mode & S_IRWXG);
  494.   else {
  495.     for(i = session.gids->nelts, gidp = (int*)session.gids->elts; i;
  496.         i--, gidp++)
  497.           if(*gidp == gid)
  498.             goto isgroup;
  499.     pcheck |= (mode & S_IRWXO);
  500.   }
  501.   switch(op) {
  502.   case OP_HIDE:
  503.     u = get_param_int(c,"HideUser",FALSE);
  504.     while(u != -1 && (u != uid || u == session.uid))
  505.       u = get_param_int_next("HideUser",FALSE);
  506.     if(u == uid) {
  507.       res = 0;
  508.       break;
  509.     }
  510.     g = get_param_int(c,"HideGroup",FALSE);
  511.     while(g != -1 && (g != gid || g == session.gid))
  512.       g = get_param_int_next("HideGroup",FALSE);
  513.     if(g == gid) {
  514.       res = 0;
  515.       break;
  516.     }
  517.     if(get_param_int(c,"HideNoAccess",FALSE) == 1) {
  518.       if(S_ISDIR(mode))
  519.         res = pcheck &= (S_IXUSR|S_IXGRP|S_IXOTH);
  520.       else
  521.         res = pcheck &= (S_IRUSR|S_IRGRP|S_IROTH);
  522.     }
  523.     break;
  524.   case OP_COMMAND:
  525.     if(get_param_int(c,"AllowAll",FALSE) == 1)
  526.       /* nop */;  
  527.     else if(get_param_int(c,"DenyAll",FALSE) == 1)
  528.       res = 0;
  529.     break;
  530.   }
  531.   return res;
  532. }
  533. int dir_check_op_mode(pool *p,char *path,int op,
  534.                        int uid,int gid,int mode)
  535. {
  536.   char *fullpath;
  537.   xaset_t *c;
  538.   config_rec *sc;
  539.   int res;
  540.   if(*path != '/')
  541.     fullpath = pdircat(p,session.cwd,path,NULL);
  542.   else
  543.     fullpath = path;
  544.   if(session.anon_root)
  545.     fullpath = pdircat(p,session.anon_root,fullpath,NULL);
  546.   
  547.   c = CURRENT_CONF;
  548.   sc = _recur_match_path(p,c,fullpath);
  549.   if(sc)
  550.     res = _dir_check_op(p,sc->subset,op,uid,gid,mode);
  551.   else
  552.     res = _dir_check_op(p,c,op,uid,gid,mode);
  553.   return res;  
  554. }
  555. int dir_check_op(pool *pp,char *path,int op)
  556. {
  557.   struct stat sbuf;
  558.   if(fs_stat(path,&sbuf) == -1)
  559.     return 1;
  560.   return dir_check_op_mode(pp,path,op,sbuf.st_uid,sbuf.st_gid,sbuf.st_mode);
  561. }
  562. static int _check_user_access(xaset_t *conf, char *name)
  563. {
  564.   int ret = 0;
  565.   config_rec *c = find_config(conf,CONF_PARAM,name,FALSE);
  566.   while(c) {
  567.     ret = user_expression((char**)c->argv);
  568.     if(ret)
  569.       break;
  570.     c = find_config_next(c,c->next,CONF_PARAM,name,FALSE);
  571.   }
  572.   return ret;
  573. }
  574. static int _check_group_access(xaset_t *conf, char *name)
  575. {
  576.   int ret = 0;
  577.   config_rec *c = find_config(conf,CONF_PARAM,name,FALSE);
  578.   while(c) {
  579.     ret = group_expression((char**)c->argv);
  580.     if(ret)
  581.       break;
  582.     c = find_config_next(c,c->next,CONF_PARAM,name,FALSE);
  583.   }
  584.   return ret;
  585. }
  586. /* returns 1 if explicit match
  587.  * returns -1 if explicit mismatch (i.e. "NONE")
  588.  * returns 0 if no match
  589.  */
  590. int match_ip(p_in_addr_t *addr, char *name, const char *match)
  591. {
  592.   char buf[1024];
  593.   char *mask,*cp;
  594.   int cidr_mode = 0, cidr_bits;
  595.   p_in_addr_t cidr_addr;
  596.   u_int_32 cidr_mask = 0;
  597.   if(!strcasecmp(match,"ALL"))
  598.     return 1;
  599.   if(!strcasecmp(match,"NONE"))
  600.     return -1;
  601.   memset(buf,0,sizeof(buf));
  602.   mask = buf;
  603.   if(*match == '.') {
  604.     *mask++ = '*';
  605.     *mask = '';
  606.     sstrcat(buf, match, sizeof(buf));
  607.   } else if(*(match + strlen(match) - 1) == '.') {
  608.     sstrcat(buf, match, sizeof(buf));
  609.     sstrcat(buf, "*", sizeof(buf));
  610.   } else if((cp = strchr(match,'/')) != NULL) { /* check for CIDR notation */
  611.     /* first portion of CIDR should be dotted quad, second portion
  612.      * is netmask
  613.      */
  614.     sstrncpy(buf, match, (cp-match)+1 <= sizeof(buf) ?
  615.                          (cp-match)+1 :  sizeof(buf));    
  616.     cidr_bits = atoi(cp+1);
  617.     
  618.     if(cidr_bits > 0 && cidr_bits < 33) {
  619.       int shift = 32 - cidr_bits;
  620.       
  621.       cidr_mode = 1;
  622.       while(cidr_bits--)
  623. cidr_mask = (cidr_mask << 1) | 1;
  624.       cidr_mask = cidr_mask << shift;
  625. #ifdef HAVE_INET_ATON
  626.       if(inet_aton(mask,&cidr_addr) == 0)
  627. return 0;
  628. #else
  629.       cidr_addr.s_addr = inet_addr(mask);
  630. #endif
  631.       cidr_addr.s_addr &= htonl(cidr_mask);
  632.     } else {
  633.       return 0;
  634.     }
  635.   } else {
  636.     sstrcat(buf, match, sizeof(buf));
  637.   }
  638.   
  639.   if(cidr_mode) {
  640.     if((addr->s_addr & htonl(cidr_mask)) == cidr_addr.s_addr)
  641.       return 1;
  642.   } else {
  643.     if(fnmatch(buf, name, FNM_NOESCAPE) == 0 ||
  644.        fnmatch(buf, inet_ntoa(*addr), FNM_NOESCAPE) == 0)
  645.       return 1;
  646.   }
  647.   
  648.   return 0;
  649. }
  650. static int _check_ip_access(xaset_t *conf, char *name)
  651. {
  652.   char **argv;
  653.   int argc; 
  654.   config_rec *c = find_config(conf,CONF_PARAM,name,FALSE);
  655.   while(c) {
  656.     for(argc = c->argc, argv = (char**)c->argv; argc; argc--, argv++) {
  657.       switch(match_ip(session.c->remote_ipaddr,session.c->remote_name,*argv)) {
  658.       case 1: return TRUE;
  659.       case -1: return FALSE;
  660.       default: break; /* avoid compiler warning */
  661.       }
  662.     }
  663.     c = find_config_next(c,c->next,CONF_PARAM,name,FALSE);
  664.   }
  665.   return FALSE;
  666. }
  667. /* 1 if allowed, 0 otherwise */
  668. static int _check_limit_allow(config_rec *c)
  669. {
  670.   /* if session.groups is null, this means no authentication
  671.    * attempt has been made, so we simply check for the
  672.    * very existance of an AllowGroup, and assume (for now) it's
  673.    * allowed.  This works because later calls to _check_limit_allow
  674.    * WILL have filled in the group members and we can truely check
  675.    * group membership at that time.  Same goes for AllowUser.
  676.    */
  677.   if(!session.user) {
  678.     if(find_config(c->subset,CONF_PARAM,"AllowUser", FALSE))
  679.       return 1;
  680.   } else if(_check_user_access(c->subset,"AllowUser"))
  681.     return 1;
  682.   if(!session.groups) {
  683.     if(find_config(c->subset,CONF_PARAM,"AllowGroup",FALSE))
  684.       return 1;
  685.   } else if(_check_group_access(c->subset,"AllowGroup"))
  686.     return 1;
  687.   if(_check_ip_access(c->subset,"Allow"))
  688.     return 1;
  689.   if(get_param_int(c->subset,"AllowAll",FALSE) == 1)
  690.     return 1;
  691.   return 0;
  692. }
  693. static int _check_limit_deny(config_rec *c)
  694. {
  695.   if(get_param_int(c->subset,"DenyAll",FALSE) == 1)
  696.     return 1;
  697.   if(session.user && _check_user_access(c->subset,"DenyUser"))
  698.     return 1;
  699.   if(session.groups && _check_group_access(c->subset,"DenyGroup"))
  700.     return 1;
  701.   if(_check_ip_access(c->subset,"Deny"))
  702.     return 1;
  703.   return 0;
  704. }
  705. /* _check_limit returns 1 if allowed, 0 if implicitly allowed,
  706.  * and -1 if implicitly denied and -2 if explicitly denied.
  707.  */
  708.    
  709. static int _check_limit(config_rec *c)
  710. {
  711.   int order;
  712.   if((order = get_param_int(c->subset,"Order",FALSE)) == -1)
  713.     order = ORDER_ALLOWDENY;
  714.   if(order == ORDER_DENYALLOW) {
  715.     /* check deny first */
  716.     if(_check_limit_deny(c))
  717.       return -2; /* explicit deny */
  718.     if(_check_limit_allow(c))
  719.       return 1; /* explicit allow */
  720.     return -1;     /* implicit deny */
  721.   }
  722.   /* check allow first */
  723.   if(_check_limit_allow(c))
  724.     return 1; /* explicit allow */
  725.   if(_check_limit_deny(c))
  726.     return -2; /* explicit deny */
  727.   return 0; /* implicit allow */
  728. }
  729. /* Note: if and == 1, the logic is short circuited so that the first
  730.  * failure results in a FALSE return from the entire function, if and
  731.  * == 0, an ORing operation is assumed and the function will return
  732.  * TRUE if any <limit LOGIN> allows access.
  733.  */
  734. int login_check_limits(xaset_t *conf, int recurse, 
  735.                        int and, int *found)
  736. {
  737.   int res = and;
  738.   int rfound;
  739.   config_rec *c;
  740.   int argc;
  741.   char **argv;
  742.   *found = 0;
  743.   if(!conf || !conf->xas_list)
  744.     return TRUE; /* default is to allow */
  745.   /* First check top level */
  746.   for(c = (config_rec*)conf->xas_list; c; c=c->next)
  747.     if(c->config_type == CONF_LIMIT) {
  748.       for(argc = c->argc, argv = (char**)c->argv; argc; argc--, argv++)
  749.         if(!strcasecmp("LOGIN",*argv))
  750.           break;
  751.       if(argc) {
  752.         if(and) {
  753.           switch(_check_limit(c)) {
  754.           case 1: res = (res && TRUE); (*found)++; break;
  755.   case -1:
  756.           case -2: res = (res && FALSE); (*found)++; break;
  757.           }
  758.           if(!res)
  759.             break;
  760.         } else
  761.           switch(_check_limit(c)) {
  762.           case 1: res = TRUE;
  763.   case -1:
  764.           case -2: (*found)++; break;
  765.           }
  766.       }
  767.     }
  768.   if( ((res && and) || (!res && !and && *found)) && recurse ) {
  769.     for(c = (config_rec*)conf->xas_list; c; c=c->next)
  770.       if(c->config_type == CONF_ANON && c->subset && c->subset->xas_list) {
  771.        if(and) {
  772.          res = (res && login_check_limits(c->subset,recurse,
  773.                                           and,&rfound));
  774.          (*found) += rfound;
  775.          if(!res)
  776.            break;
  777.        } else {
  778.          int rres;
  779.          rres = login_check_limits(c->subset,
  780.                                   recurse,and,&rfound);
  781.          if(rfound)
  782.            res = (res || rres);
  783.          (*found) += rfound;
  784.          if(res)
  785.            break;
  786.        }
  787.      }
  788.   }
  789.   if(!*found && !and)
  790.     return TRUE; /* Default is to allow */
  791.   return res;
  792. }
  793. int dir_check_limits(config_rec *c, char *cmd, int hidden)
  794. {
  795.   /* Check limit directives */
  796.   int res = 1;
  797.   config_rec *lc;
  798.   int i;
  799.   errno = 0;
  800.   while(c && (res == 1)) {
  801.     if(c->subset)
  802.       for(lc = (config_rec*)c->subset->xas_list; lc && (res == 1); lc=lc->next)
  803.         if(lc->config_type == CONF_LIMIT) {
  804.           for(i = 0; i < lc->argc; i++)
  805.             if(!strcmp(cmd,(char*)(lc->argv[i])))
  806.               break;
  807. /*
  808.           log_debug(DEBUG5,"cmd=%s i=%d lc->argc=%d lc->argv[0]=%sn",
  809.                     cmd, i, lc->argc, (char*)(lc->argv[i]));
  810. */
  811.           if(i == lc->argc)
  812.             continue;
  813.           /* Found a limit directive associated with the current
  814.            * command
  815.            */
  816.           if(hidden && get_param_int(lc->subset,"IgnoreHidden",FALSE) == 1)
  817.             { res = 0; errno = ENOENT; break; }
  818.           switch(_check_limit(lc)) {
  819.           case 1: res++; break;
  820.           case -1:
  821.           case -2: res = 0; break;
  822.           default: continue;
  823.           }
  824.           break;
  825.         }
  826.     c = c->parent;
  827.   }
  828.   if(!res && !errno)
  829.     errno = EACCES;
  830.   return res;
  831. }
  832. void build_dyn_config(pool *p,char *_path, struct stat *_sbuf, int recurse)
  833. {
  834.   char *fullpath,*path,*dynpath,*cp;
  835.   struct stat sbuf;
  836.   config_rec *d;
  837.   FILE *fp;
  838.   cmd_rec *cmd;
  839.   xaset_t **set = NULL;
  840.   int isfile,removed = 0;
  841.   /* Switch through each directory, from "deepest" up looking for
  842.    * new or updated .ftpaccess files
  843.    */
  844.   if(!_path)
  845.     return;
  846.   path = pstrdup(p,_path);
  847.   memcpy(&sbuf,_sbuf,sizeof(sbuf));
  848.   if(S_ISDIR(sbuf.st_mode))
  849.     dynpath = pdircat(p,path,"/.ftpaccess",NULL);
  850.   else
  851.     dynpath = NULL;
  852.   while(path) {
  853.     if(session.anon_root) {
  854.       fullpath = pdircat(p,session.anon_root,path,NULL);
  855.       if(strcmp(fullpath,"/") && *(fullpath + strlen(fullpath) - 1) == '/')
  856.         *(fullpath + strlen(fullpath) - 1) = '';
  857.     } else
  858.       fullpath = path;
  859.     if(dynpath)
  860.       isfile = fs_stat(dynpath,&sbuf);
  861.     else
  862.       isfile = -1;
  863.     d = dir_match_path(p,fullpath);
  864.     if(!d && isfile != -1) {
  865.       set = (session.anon_config ? &session.anon_config->subset :
  866.              &main_server->conf);
  867.       d = add_config_set(set,fullpath);
  868.       d->config_type = CONF_DIR;
  869.       d->argc = 1;
  870.       d->argv = pcalloc(d->pool,2*sizeof(void*));
  871.     } else if(d) {
  872.       config_rec *newd,*dnext;
  873.       if(isfile != -1 && strcmp(d->name,fullpath) != 0) {
  874.         set = &d->subset;
  875.         newd = add_config_set(set,fullpath);
  876.         newd->config_type = CONF_DIR;
  877.         newd->argc = 1;
  878.         newd->argv = pcalloc(newd->pool,2*sizeof(void*));
  879. newd->parent = d;
  880.         d = newd;
  881.       } else if(d->subset && d->subset->xas_list &&
  882.                 strcmp(d->name,fullpath) == 0 && 
  883.                 (isfile == -1 || sbuf.st_mtime > (time_t)d->argv[0])) {
  884.         set = (d->parent ? &d->parent->subset :
  885.                &main_server->conf);
  886.         /* remove all old dynamic entries */
  887.         for(newd = (config_rec*)d->subset->xas_list; newd; newd=dnext) {
  888.           dnext = newd->next;
  889.           if(newd->flags & CF_DYNAMIC) {
  890.             xaset_remove(d->subset,(xasetmember_t*)newd);
  891.             removed++;
  892.           }
  893.         }
  894.         if(!d->subset->xas_list) {
  895.           destroy_pool(d->subset->mempool);
  896.           d->subset = NULL;
  897.           d->argv[0] = NULL;
  898.   /* If the file has been removed and no entries exist in this
  899.            * dynamic entry, remove it completely
  900.            */
  901.           if(isfile == -1)
  902.             xaset_remove(*set,(xasetmember_t*)d);
  903.         }
  904.       }
  905.     }
  906.     if(isfile != -1 && d && sbuf.st_mtime > (time_t)d->argv[0]) {
  907.       /* File has been modified or not loaded yet */
  908.       d->argv[0] = (void*)sbuf.st_mtime;
  909.       fp = fopen(dynpath,"r");
  910.       if(fp) {
  911.         removed = 0;
  912.         init_dyn_stacks(p,d);
  913.         d->config_type = CONF_DYNDIR;
  914.         while((cmd = get_config_cmd(p,fp)) != NULL) {
  915.           if(cmd->argc) {
  916.             conftable *c;
  917.             char found = 0;
  918.             modret_t *mr;
  919.             cmd->server = *conf.curserver;
  920.             cmd->config = *conf.curconfig;
  921.               
  922.             for(c = m_conftable; c->directive; c++)
  923.               if(!strcasecmp(c->directive,cmd->argv[0])) {
  924.                 ++found;
  925.                 if((mr = call_module(c->m,c->handler,cmd)) != NULL) 
  926.                 {
  927.                   if(MODRET_ERRMSG(mr))
  928.                   log_pri(LOG_WARNING,"warning: %s",MODRET_ERRMSG(mr));
  929.                 }
  930. if(MODRET_ISDECLINED(mr))
  931. found--;
  932. destroy_pool(cmd->tmp_pool);
  933.               }
  934.             if(!found)
  935.               log_pri(LOG_WARNING,"warning: unknown configuration directive '%s'.",
  936.                       cmd->argv[0]);
  937.           }
  938.  
  939.           destroy_pool(cmd->pool);
  940.         }
  941. log_debug(DEBUG5,"dynamic configuration added/updated for %s.",
  942.                          fullpath);
  943.         d->config_type = CONF_DIR;
  944.         free_dyn_stacks();
  945.         _mergedown(*set,TRUE);
  946.         fclose(fp);
  947.       }
  948.     }
  949.     if(isfile == -1 && removed && d && set) {
  950.       log_debug(DEBUG5,"dynamic configuration removed for %s.",
  951.                        fullpath);
  952.       _mergedown(*set,FALSE);
  953.     }
  954.     if(!recurse)
  955.       break;
  956.     cp = rindex(path,'/');
  957.     if(cp && strcmp(path,"/") != 0)
  958.       *cp = '';
  959.     else
  960.       path = NULL;
  961.     if(path) {
  962.       if(*(path+strlen(path)-1) == '*')
  963.         *(path+strlen(path)-1) = '';
  964.       dynpath = pdircat(p,path,"/.ftpaccess",NULL);
  965.     }
  966.   }
  967. }
  968. /* dir_check_full() fully recurses the path passed
  969.  * returns 1 if operation is allowed on current path,
  970.  * or 0 if not.
  971.  */
  972. int dir_check_full(pool *pp, char *cmd, char *group, char *path, int *hidden)
  973. {
  974.   char *fullpath,*grpowner;
  975.   config_rec *c;
  976.   struct stat sbuf;
  977.   pool *p;
  978.   int res = 1,hid,_umask = 0,isfile;
  979.   if(!hidden)
  980.     hidden = &hid;
  981.   *hidden = 0;
  982.   p = make_sub_pool(pp);
  983.   /* flood -- this is no longer needed, as all paths passed to 
  984.    * dir_check should have gone through either dir_canonical or
  985.    * dir_real first (depending on if they are supposed to pre-exist
  986.   fullpath = dir_realpath(p,path);
  987.   if(!fullpath)
  988.     fullpath = pdircat(p,session.cwd,path,NULL);
  989.   else 
  990.     path = fullpath;
  991.   */
  992.   fullpath = path;
  993.   if(session.anon_root)
  994.     fullpath = pdircat(p,session.anon_root,fullpath,NULL);
  995.   log_debug(DEBUG5,"in dir_check(): path = '%s', fullpath = '%s'.",
  996.             path,fullpath);
  997.   /* Check and build all appropriate dynamic configuration entries */
  998.   if((isfile = fs_stat(path,&sbuf)) == -1)
  999.     bzero(&sbuf,sizeof(sbuf));
  1000.   build_dyn_config(p,path,&sbuf,1);
  1001.   session.dir_config = c = dir_match_path(p,fullpath);
  1002.   if(!c && session.anon_config)
  1003.     c = session.anon_config;
  1004.   if(_kludge_disable_umask || 
  1005.       (_umask = get_param_int(CURRENT_CONF,"Umask",FALSE)) == -1)
  1006.     _umask = 0;
  1007.   if((grpowner = get_param_ptr(CURRENT_CONF,"GroupOwner",FALSE)) != NULL) {
  1008.     /* attempt chgrp on all new files */
  1009.     struct group *gr;
  1010.     if((gr = auth_getgrnam(p,grpowner)) != NULL)
  1011.       session.fsgid = gr->gr_gid;
  1012.   }
  1013.   if(isfile != -1) {
  1014.     *hidden = !_dir_check_op(p,CURRENT_CONF,OP_HIDE,sbuf.st_uid,sbuf.st_gid,sbuf.st_mode);
  1015.     res = _dir_check_op(p,CURRENT_CONF,OP_COMMAND,sbuf.st_uid,sbuf.st_gid,sbuf.st_mode);
  1016.   }
  1017.   if(res) {
  1018.     res = dir_check_limits(c,cmd,*hidden);
  1019.     /* If specifically allowed, res will be > 1 and we don't want to
  1020.      * check the command group limit
  1021.      */
  1022.     if(res == 1 && group)
  1023.       res = dir_check_limits(c,group,*hidden);
  1024.     /* if still == 1, no explicit allow so check lowest priority "ALL" group */
  1025.     if(res == 1)
  1026.       res = dir_check_limits(c,"ALL",*hidden);
  1027.   }
  1028.   if(res && _umask)
  1029.     umask(_umask);
  1030.   destroy_pool(p);
  1031.   return res;
  1032. }
  1033. /* dir_check() checks the current dir configuration against the path,
  1034.  * if it matches (partially), a search is done only in the subconfig,
  1035.  * otherwise handed off to dir_check_full
  1036.  */
  1037. int dir_check(pool *pp, char *cmd, char *group, char *path, int *hidden)
  1038. {
  1039.   char *fullpath,*grpowner;
  1040.   config_rec *c;
  1041.   struct stat sbuf;
  1042.   pool *p;
  1043.   int res = 1,hid,_umask = 0,isfile;
  1044.   p = make_sub_pool(pp);
  1045.   fullpath = path;
  1046.   if(session.anon_root)
  1047.     fullpath = pdircat(p,session.anon_root,fullpath,NULL);
  1048.   c = (session.dir_config ? session.dir_config :
  1049.         (session.anon_config ? session.anon_config : NULL));
  1050.   if(!c || strncmp(c->name,fullpath,strlen(c->name)) != 0) {
  1051.     destroy_pool(p);
  1052.     return dir_check_full(pp,cmd,group,path,hidden);
  1053.   }
  1054.   if(!hidden)
  1055.     hidden = &hid;
  1056.   *hidden = 0;
  1057.   /* Check and build all appropriate dynamic configuration entries */
  1058.   if((isfile = fs_stat(path,&sbuf)) == -1)
  1059.     bzero(&sbuf,sizeof(sbuf));
  1060.   build_dyn_config(p,path,&sbuf,0);
  1061.   session.dir_config = c = dir_match_path(p,fullpath);
  1062.   if(!c && session.anon_config)
  1063.     c = session.anon_config;
  1064.   if(_kludge_disable_umask ||
  1065.        (_umask = get_param_int(CURRENT_CONF,"Umask",FALSE)) == -1)
  1066.     _umask = 0;
  1067.   if((grpowner = get_param_ptr(CURRENT_CONF,"GroupOwner",FALSE)) != NULL) {
  1068.     /* attempt chgrp on all new files */
  1069.     struct group *gr;
  1070.     if((gr = auth_getgrnam(p,grpowner)) != NULL)
  1071.       session.fsgid = gr->gr_gid;
  1072.   }
  1073.   if(isfile != -1) {
  1074.     *hidden = !_dir_check_op(p,CURRENT_CONF,OP_HIDE,sbuf.st_uid,sbuf.st_gid,sbuf.st_mode);
  1075.     res = _dir_check_op(p,CURRENT_CONF,OP_COMMAND,sbuf.st_uid,sbuf.st_gid,sbuf.st_mode);
  1076.   }
  1077.   if(res) {
  1078.     res = dir_check_limits(c,cmd,*hidden);
  1079.     /* If specifically allowed, res will be > 1 and we don't want to
  1080.      * check the command group limit
  1081.      */
  1082.     if(res == 1 && group)
  1083.       res = dir_check_limits(c,group,*hidden);
  1084.     /* if still == 1, no explicit allow so check lowest priority "ALL" group */
  1085.     if(res == 1)
  1086.       res = dir_check_limits(c,"ALL",*hidden);
  1087.   }
  1088.   if(res && _umask)
  1089.     umask(_umask);
  1090.   destroy_pool(p);
  1091.   return res;
  1092. }
  1093. /* dir_check_canon() canonocalizes as much of the path as possible
  1094.  * (which may not be all of it, as the target may not yet exist
  1095.  * then we hand off to dir_check()
  1096.  */
  1097. int dir_check_canon(pool *pp, char *cmd, char *group, char *path, int *hidden)
  1098. {
  1099.   return dir_check(pp,cmd,group,dir_best_path(pp,path),hidden);
  1100. }
  1101. /*
  1102.  * Move all the members (i.e. a "branch") of one config set to
  1103.  * a different parent.
  1104.  */
  1105. static void _reparent_all(config_rec *newparent,xaset_t *set)
  1106. {
  1107.   config_rec *c,*cnext;
  1108.   if(!newparent->subset)
  1109.     newparent->subset = xaset_create(newparent->pool,NULL);
  1110.   for(c = (config_rec*)set->xas_list; c; c = cnext) {
  1111.     cnext = c->next;
  1112.     xaset_remove(set,(xasetmember_t*)c);
  1113.     xaset_insert(newparent->subset,(xasetmember_t*)c);
  1114.     c->set = newparent->subset;
  1115.     c->parent = newparent;
  1116.   }
  1117. }
  1118. /* Recursively find the most appropriate place to move a CONF_DIR
  1119.  * directive to.
  1120.  */
  1121. static config_rec *_find_best_dir(xaset_t *set,char *path,int *matchlen)
  1122. {
  1123.   config_rec *c,*res = NULL,*rres;
  1124.   int len,imatchlen,tmatchlen;
  1125.   *matchlen = 0;
  1126.   if(!set || !set->xas_list)
  1127.     return NULL;
  1128.   for(c = (config_rec*)set->xas_list; c; c=c->next) {
  1129.     if(c->config_type == CONF_DIR) {
  1130.       if(!strcmp(c->name,path))
  1131.         continue; /* Don't examine the current */
  1132.       len = strlen(c->name);
  1133.       while(len > 0 && (*(c->name+len-1) == '*' ||
  1134.                         *(c->name+len-1) == '/'))
  1135.         len--;
  1136.       if(!strncmp(c->name,path,len) &&
  1137.          len < strlen(path)) {
  1138.            rres = _find_best_dir(c->subset,path,&imatchlen);
  1139.            tmatchlen = _strmatch(path,c->name);
  1140.            if(!rres && tmatchlen > *matchlen) {
  1141.              res = c;
  1142.              *matchlen = tmatchlen;
  1143.            } else if(imatchlen > *matchlen) {
  1144.              res = rres;
  1145.              *matchlen = imatchlen;
  1146.            }     
  1147.          }
  1148.     }
  1149.   }
  1150.   return res;
  1151. }
  1152. /* Reorder all the CONF_DIR configuration sections, so that they are
  1153.  * in directory tree order
  1154.  */
  1155. static void _reorder_dirs(xaset_t *set, int mask)
  1156. {
  1157.   config_rec *c,*cnext,*newparent;
  1158.   int tmp,defer = 0;
  1159.   if(!set || !set->xas_list)
  1160.     return;
  1161.   if(!(mask & CF_DEFER))
  1162.     defer = 1;
  1163.   for(c = (config_rec*)set->xas_list; c; c=cnext) {
  1164.     cnext = c->next;
  1165.     if(c->config_type == CONF_DIR) {
  1166.       if(mask && !(c->flags & mask))
  1167.         continue;
  1168.       if(defer && (c->flags & CF_DEFER))
  1169.         continue;
  1170.       /* If <Directory *> is used inside <Anonymous>, move all
  1171.        * the directives from '*' into the higher level
  1172.        */
  1173.       if(!strcmp(c->name,"*") && c->parent &&
  1174.          c->parent->config_type == CONF_ANON) {
  1175.         if(c->subset)
  1176.           _reparent_all(c->parent,c->subset);
  1177.         xaset_remove(c->parent->subset,(xasetmember_t*)c);
  1178.       } else {
  1179.         newparent = _find_best_dir(set,c->name,&tmp);
  1180.         if(newparent) {
  1181.           if(!newparent->subset)
  1182.             newparent->subset = xaset_create(newparent->pool,NULL);
  1183.           xaset_remove(c->set,(xasetmember_t*)c);
  1184.           xaset_insert(newparent->subset,(xasetmember_t*)c);
  1185.           c->set = newparent->subset;
  1186.           c->parent = newparent;
  1187.         }
  1188.       }
  1189.     }
  1190.   }
  1191.   /* Top level is now sorted, now we recursively sort all the sublevels
  1192.    */
  1193.   for(c = (config_rec*)set->xas_list; c; c=c->next)
  1194.     if(c->config_type == CONF_DIR || c->config_type == CONF_ANON)
  1195.       _reorder_dirs(c->subset,mask);
  1196. }
  1197. void debug_dump_config(xaset_t *s,char *indent)
  1198. {
  1199.   config_rec *c;
  1200.   if(!indent)
  1201.     indent = "";
  1202.   for(c = (config_rec*)s->xas_list; c; c=c->next) {
  1203.     log_debug(DEBUG5,"%s%s",indent,c->name);
  1204.     if(c->subset)
  1205.       debug_dump_config(c->subset,pstrcat(permanent_pool,indent," ",NULL));
  1206.   }
  1207. }
  1208. static void _mergedown(xaset_t *s,int dynamic)
  1209. {
  1210.   config_rec *c,*dest,*newconf;
  1211.   int argc;
  1212.   void **argv,**sargv;
  1213.   
  1214.   if(!s || !s->xas_list)
  1215.     return;
  1216.   for(c = (config_rec*)s->xas_list; c; c=c->next)
  1217.     if(c->flags & CF_MERGEDOWN)
  1218.       for(dest = (config_rec*)s->xas_list; dest; dest=dest->next)
  1219.         if(dest->config_type == CONF_ANON ||
  1220.            dest->config_type == CONF_DIR) {
  1221.           /* If an option of the same name/type is found in the
  1222.            * next level down, it overrides, so we don't merge.
  1223.            */
  1224.           if(find_config(dest->subset,c->config_type,
  1225.                          c->name,FALSE))
  1226.             continue;
  1227.           if(!dest->subset)
  1228.             dest->subset = xaset_create(dest->pool,NULL);
  1229.           newconf = add_config_set(&dest->subset,c->name);
  1230.           newconf->config_type = c->config_type;
  1231.           newconf->flags = c->flags | (dynamic ? CF_DYNAMIC : 0);
  1232.           newconf->argc = c->argc;
  1233.           newconf->argv = palloc(newconf->pool,(c->argc+1)*sizeof(void*));
  1234.           argv = newconf->argv; sargv = c->argv;
  1235.           argc = newconf->argc;
  1236.           while(argc--)
  1237.             *argv++ = *sargv++;
  1238.           *argv++ = NULL;
  1239.         }
  1240.           
  1241.   /* Top level merged, recursively merge lower levels */
  1242.   for(c = (config_rec*)s->xas_list; c; c=c->next)
  1243.     if(c->subset && (c->config_type == CONF_ANON ||
  1244.                      c->config_type == CONF_DIR))
  1245.       _mergedown(c->subset,dynamic);
  1246. }
  1247. /* iterate through <Directory> blocks inside of anonymous and
  1248.  * resolve each one.
  1249.  */
  1250. void resolve_anonymous_dirs(xaset_t *clist)
  1251. {
  1252.   config_rec *c;
  1253.   char *realdir;
  1254.   if(!clist)
  1255.     return;
  1256.   for(c = (config_rec*)clist->xas_list; c; c=c->next) {
  1257.     if(c->config_type == CONF_DIR) {
  1258.       if(c->argv[1]) {
  1259.         realdir = dir_best_path(c->pool,c->argv[1]);
  1260.         if(realdir)
  1261.           c->argv[1] = realdir;
  1262.         else {
  1263.           realdir = dir_canonical_path(c->pool,c->argv[1]);
  1264.           if(realdir)
  1265.             c->argv[1] = realdir;
  1266.         }
  1267.       }
  1268.       if(c->subset)
  1269.         resolve_anonymous_dirs(c->subset);
  1270.     }
  1271.   }
  1272. }
  1273. /* iterate through directory configuration items and resolve
  1274.  * ~ references
  1275.  */
  1276. void resolve_defered_dirs(server_rec *s)
  1277. {
  1278.   config_rec *c;
  1279.   char *realdir;
  1280.   if(!s || !s->conf)
  1281.     return;
  1282.   for(c = (config_rec*)s->conf->xas_list; c; c=c->next) {
  1283.     if(c->config_type == CONF_DIR && (c->flags & CF_DEFER)) {
  1284.       realdir = dir_best_path(c->pool,c->name);
  1285.       if(realdir)
  1286.         c->name = realdir;
  1287.       else {
  1288.         realdir = dir_canonical_path(c->pool,c->name);
  1289.         if(realdir)
  1290.           c->name = realdir;
  1291.       }
  1292.     }
  1293.   }
  1294. }
  1295. static
  1296. void _copy_recur(xaset_t **set, pool *p, config_rec *c, config_rec *new_parent)
  1297. {
  1298.   config_rec *newconf;
  1299.   int argc;
  1300.   void **argv,**sargv;
  1301.   if(!*set)
  1302.     *set = xaset_create(p,NULL);
  1303.   newconf = add_config_set(set,c->name);
  1304.   newconf->config_type = c->config_type;
  1305.   newconf->flags = c->flags;
  1306.   newconf->parent = new_parent;
  1307.   newconf->argc = c->argc;
  1308.   if(c->argc) {
  1309.     newconf->argv = palloc(newconf->pool,(c->argc+1)*sizeof(void*));
  1310.     argv = newconf->argv; sargv = c->argv;
  1311.     argc = newconf->argc;
  1312.     while(argc--)
  1313.       *argv++ = *sargv++;
  1314.     if(argv)
  1315.     *argv++ = NULL;
  1316.   }
  1317.   if(c->subset && c->subset->xas_list)
  1318.     _copy_recur(&newconf->subset,p,
  1319.                 (config_rec*)c->subset->xas_list,newconf);
  1320. }
  1321. static 
  1322. void _copy_global_to_all(xaset_t *set)
  1323. {
  1324.   server_rec *s;
  1325.   config_rec *c;
  1326.   if(!set || !set->xas_list)
  1327.     return;
  1328.   for(c = (config_rec*)set->xas_list; c; c=c->next)
  1329.     for(s = (server_rec*)servers->xas_list; s; s=s->next)
  1330.       _copy_recur(&s->conf,s->pool,c,NULL);
  1331. }
  1332. void fixup_globals()
  1333. {
  1334.   server_rec *s,*smain;
  1335.   config_rec *c,*cnext;
  1336.   smain = (server_rec*)servers->xas_list;
  1337.   for(s = smain; s; s=s->next) {
  1338.     /* loop through each top level directive looking for a CONF_GLOBAL
  1339.      * context
  1340.      */
  1341.     if(!s->conf || !s->conf->xas_list)
  1342.       continue;
  1343.     for(c = (config_rec*)s->conf->xas_list; c; c=cnext) {
  1344.       cnext = c->next;
  1345.       if(c->config_type == CONF_GLOBAL) {
  1346.         /* copy the contents of the block to all other servers
  1347.          * (including this one), then pull the block "out of play".
  1348.          */
  1349.         if(c->subset && c->subset->xas_list)
  1350.           _copy_global_to_all(c->subset);
  1351.         xaset_remove(s->conf,(xasetmember_t*)c);
  1352.         if(!s->conf->xas_list) {
  1353.           destroy_pool(s->conf->mempool);
  1354.           s->conf = NULL;
  1355.         }
  1356.       }
  1357.     }
  1358.   }
  1359. }
  1360. void fixup_dirs(server_rec *s, int mask)
  1361. {
  1362.   if(!s || !s->conf)
  1363.     return;
  1364.   _reorder_dirs(s->conf,mask);
  1365.   /* Merge mergeable configuration items down
  1366.    */
  1367.   _mergedown(s->conf,FALSE);
  1368. /*
  1369.   for(c = (config_rec*)s->conf->xas_list; c; c=c->next)
  1370.     if(c->config_type == CONF_ANON)
  1371.       _reorder_dirs(c->subset);
  1372. */
  1373.   log_debug(DEBUG5,"");
  1374.   log_debug(DEBUG5,"Config for %s:",s->ServerName);
  1375.   debug_dump_config(s->conf,NULL);
  1376. }
  1377. config_rec *find_config_next(config_rec *prev, config_rec *c, int type,
  1378.                              const char *name, int recurse)
  1379. {
  1380.   config_rec *top = c;
  1381.   /* We do two searches (if recursing) so that we find the "deepest"
  1382.    * level first.
  1383.    */
  1384.   if(!c && !prev)
  1385.     return NULL;
  1386.   if(!prev)
  1387.     prev = top;
  1388.   if(recurse) {
  1389.     do {
  1390.       config_rec *res = NULL;
  1391.       for(c = top; c; c=c->next) {
  1392.         if(c->subset && c->subset->xas_list)
  1393.           res = find_config_next(NULL,(config_rec*)c->subset->xas_list,
  1394.                                  type,name,recurse+1);
  1395.           if(res)
  1396.             return res;
  1397.         }
  1398.       /* If deep recursion yielded no match try the current subset */
  1399.       for(c = top; c; c=c->next)
  1400.         if((type == -1 || type == c->config_type) &&
  1401.             (!name || !strcmp(name,c->name)))
  1402.           return c;
  1403.               
  1404.       /* Restart the search at the previous level if required */
  1405.       if(prev->parent && recurse == 1 &&
  1406.          prev->parent->next &&
  1407.          prev->parent->set != find_config_top) {
  1408.         prev = top = prev->parent->next; c = top;
  1409.         continue;
  1410.       }
  1411.       break;
  1412.     } while(1);
  1413.   } else {
  1414.     for(c = top; c; c=c->next)
  1415.       if((type == -1 || type == c->config_type) &&
  1416.          (!name || !strcmp(name,c->name)))
  1417.         return c;
  1418.   }
  1419.   return NULL;
  1420. }
  1421.     
  1422. void find_config_set_top(config_rec *c)
  1423. {
  1424.   if(c && c->parent)
  1425.     find_config_top = c->parent->set;
  1426.   else
  1427.     find_config_top = NULL;
  1428. }
  1429. config_rec *find_config(xaset_t *set, int type, const char *name, int recurse)
  1430. {
  1431.   if(!set || !set->xas_list)
  1432.     return NULL;
  1433.   find_config_set_top((config_rec*)set->xas_list);
  1434.   return find_config_next(NULL,(config_rec*)set->xas_list,type,name,recurse);
  1435. }
  1436. /* These next two functions return the first argument in a
  1437.  * CONF_PARAM configuration entry.  If more than one or all
  1438.  * parameters are needed, the caller will need to use find_config,
  1439.  * and iterate through the argv themselves.
  1440.  * _int returns -1 if the config name is not found, _ptr returns
  1441.  * NULL.
  1442.  */
  1443. long get_param_int(xaset_t *set,const char *name,int recurse)
  1444. {
  1445.   config_rec *c;
  1446.   if(!set) {
  1447.     _last_param_int = NULL;
  1448.     return -1;
  1449.   }
  1450.   c = find_config(set,CONF_PARAM,name,recurse);
  1451.   if(c && c->argc) {
  1452.     _last_param_int = c;
  1453.     return (long)c->argv[0];
  1454.   }
  1455.   _last_param_int = NULL;
  1456.   return -1;  /* Parameters aren't allowed to contain neg. integers anyway */
  1457. }
  1458. long get_param_int_next(const char *name,int recurse)
  1459. {
  1460.   config_rec *c;
  1461.   if(!_last_param_int || !_last_param_int->next) {
  1462.     _last_param_int = NULL;
  1463.     return -1;
  1464.   }
  1465.   c = find_config_next(_last_param_int,_last_param_int->next,
  1466.                        CONF_PARAM,name,recurse);
  1467.   if(c && c->argc) {
  1468.     _last_param_int = c;
  1469.     return (long)c->argv[0];
  1470.   }
  1471.   _last_param_int = NULL;
  1472.   return -1;
  1473. }
  1474. void *get_param_ptr(xaset_t *set,const char *name,int recurse)
  1475. {
  1476.   config_rec *c;
  1477.   if(!set) {
  1478.     _last_param_ptr = NULL;
  1479.     return NULL;
  1480.   }
  1481.   c = find_config(set,CONF_PARAM,name,recurse);
  1482.   if(c && c->argc) {
  1483.     _last_param_ptr = c;
  1484.     return c->argv[0];
  1485.   }
  1486.   _last_param_ptr = NULL;
  1487.   return NULL;
  1488. }
  1489. void *get_param_ptr_next(const char *name,int recurse)
  1490. {
  1491.   config_rec *c;
  1492.   if(!_last_param_ptr || !_last_param_ptr->next) {
  1493.     _last_param_ptr = NULL;
  1494.     return NULL;
  1495.   }
  1496.   c = find_config_next(_last_param_ptr,_last_param_ptr->next,
  1497.                        CONF_PARAM,name,recurse);
  1498.   if(c && c->argv) {
  1499.     _last_param_ptr = c;
  1500.     return c->argv[0];
  1501.   }
  1502.   _last_param_ptr = NULL;
  1503.   return NULL;
  1504. }
  1505. int remove_config(xaset_t *set, const char *name,int recurse)
  1506. {
  1507.   server_rec *s = (conf.curserver ? *conf.curserver : main_server);
  1508.   config_rec *c;
  1509.   int found = 0;
  1510.   xaset_t *fset;
  1511.   while((c = find_config(set,-1,name,recurse)) != NULL) {
  1512.     found++;
  1513.     fset = c->set;
  1514.     xaset_remove(fset,(xasetmember_t*)c);
  1515.     if(!fset->xas_list) {
  1516.       if(c->parent && c->parent->subset == fset) {
  1517.         c->parent->subset = NULL;
  1518.         destroy_pool(fset->mempool);
  1519.       } else if(s->conf == fset) {
  1520.         s->conf = NULL;
  1521.         destroy_pool(fset->mempool);
  1522.       }
  1523.     }
  1524.   }
  1525.   return found;
  1526. }
  1527.         
  1528. config_rec *add_config_param_set(xaset_t **set,const char *name,int num,...)
  1529. {
  1530.   config_rec *c = add_config_set(set,name);
  1531.   void **argv;
  1532.   va_list ap;
  1533.   if(c) {
  1534.     c->config_type = CONF_PARAM;
  1535.     c->argc = num;
  1536.     c->argv = pcalloc(c->pool,(num+1)*sizeof(void*));
  1537.     argv = c->argv;
  1538.     va_start(ap,num);
  1539.     while(num-- > 0)
  1540.       *argv++ = va_arg(ap,void*);
  1541.     va_end(ap);
  1542.   }
  1543.   return c;
  1544. }
  1545. config_rec *add_config_param_str(const char *name, int num, ...)
  1546. {
  1547.   config_rec *c = add_config(name);
  1548.   char *arg;
  1549.   void **argv;
  1550.   va_list ap;
  1551.   if(c) {
  1552.     c->config_type = CONF_PARAM;
  1553.     c->argc = num;
  1554.     c->argv = pcalloc(c->pool,(num+1) * sizeof(char*));
  1555.     argv = c->argv;
  1556.     va_start(ap,num);
  1557.     while(num-- > 0) {
  1558.       arg = va_arg(ap,char*);
  1559.       if(arg)
  1560.         *argv++ = pstrdup(permanent_pool,arg);
  1561.       else
  1562.         *argv++ = NULL;
  1563.     }
  1564.     va_end(ap);
  1565.   }
  1566.   return c;
  1567. }
  1568. config_rec *add_config_param(const char *name,int num,...)
  1569. {
  1570.   config_rec *c = add_config(name);
  1571.   void **argv;
  1572.   va_list ap;
  1573.   if(c) {
  1574.     c->config_type = CONF_PARAM;
  1575.     c->argc = num;
  1576.     c->argv = pcalloc(c->pool,(num+1) * sizeof(void*));
  1577.     
  1578.     argv = c->argv;
  1579.     va_start(ap,num);
  1580.     while(num-- > 0)
  1581.       *argv++ = va_arg(ap,void*);
  1582.     va_end(ap);
  1583.   }
  1584.   return c;
  1585. }
  1586. int parse_config_file(const char *fname)
  1587. {
  1588.   FILE *fp;
  1589.   cmd_rec *cmd;
  1590.   pool *tmp_pool = make_sub_pool(permanent_pool);
  1591.   modret_t *mr;
  1592.  
  1593.   fp = pfopen(tmp_pool,fname,"r");
  1594.   if(!fp) { destroy_pool(tmp_pool); return -1; }
  1595.   
  1596.   while((cmd = get_config_cmd(tmp_pool,fp)) != NULL) {
  1597.     if(cmd->argc) {
  1598.       conftable *c;
  1599.       char found = 0;
  1600.       cmd->server = *conf.curserver;
  1601.       cmd->config = *conf.curconfig;
  1602.       for(c = m_conftable; c->directive; c++)
  1603.         if(!strcasecmp(c->directive,cmd->argv[0])) {
  1604.           ++found;
  1605.           if((mr = call_module(c->m,c->handler,cmd)) != NULL) {
  1606.             if(MODRET_ISERROR(mr)) {
  1607.             log_pri(LOG_ERR,"Fatal: %s",MODRET_ERRMSG(mr));
  1608.             exit(1);
  1609.     }
  1610.           }
  1611.   if(MODRET_ISDECLINED(mr))
  1612.     found--;
  1613.           destroy_pool(cmd->tmp_pool);
  1614.         }
  1615.        if(!found) {
  1616.          log_pri(LOG_ERR,"Fatal: unknown configuration directive '%s'.",
  1617.                  cmd->argv[0]);
  1618.          exit(1);
  1619.        }
  1620.     }
  1621.     destroy_pool(cmd->pool);
  1622.   }
  1623.   pfclose(tmp_pool,fp);
  1624.   destroy_pool(tmp_pool);
  1625.   return 0;
  1626. }
  1627. /* Go through each server configuration and complain if important
  1628.  * information is missing (post reading configuration files).
  1629.  * otherwise fill in defaults where applicable
  1630.  */
  1631. void fixup_servers()
  1632. {
  1633.   server_rec *s;
  1634.   fixup_globals();
  1635.   s = (server_rec*)servers->xas_list;
  1636.   if(s && !s->ServerName)
  1637.     s->ServerName = pstrdup(s->pool,"ProFTPD");
  1638.   for(; s; s=s->next) {
  1639.     if(s->ServerPort == -1)
  1640.       s->ServerPort = inet_getservport(s->pool,"ftp","tcp");
  1641.     if(!s->ServerAddress)
  1642.       s->ServerFQDN = s->ServerAddress = inet_gethostname(s->pool);
  1643.     else
  1644.       s->ServerFQDN = inet_fqdn(s->pool,s->ServerAddress);
  1645.     if(!s->ServerFQDN)
  1646.       s->ServerFQDN = s->ServerAddress;
  1647.     if(!s->ServerAdmin)
  1648.       s->ServerAdmin = pstrcat(s->pool,"root@",s->ServerFQDN,NULL);
  1649.     if(!s->ServerName) {
  1650.       server_rec *m = (server_rec*)servers->xas_list;
  1651.       s->ServerName = pstrdup(s->pool,m->ServerName);
  1652.     }
  1653.     if(!s->tcp_rwin)
  1654.       s->tcp_rwin = TUNABLE_DEFAULT_RWIN;
  1655.     if(!s->tcp_swin)
  1656.       s->tcp_swin = TUNABLE_DEFAULT_SWIN;
  1657.     s->ipaddr = inet_getaddr(s->pool,s->ServerAddress);
  1658.     if(!s->ipaddr) {
  1659.       log_pri(LOG_ERR,"Fatal: unable to determine IP address of `%s'.",
  1660.                       s->ServerAddress);
  1661.       exit(1);
  1662.     }
  1663.     if(get_param_int(s->conf,"DefaultServer",FALSE) == 1)
  1664.       s->ipaddr->s_addr = 0;
  1665.     fixup_dirs(s,0);
  1666.   }
  1667.   clear_inet_pool();
  1668. }
  1669. void init_config()
  1670. {
  1671.   pool *pool = make_sub_pool(permanent_pool);
  1672.   servers = xaset_create(pool,NULL);
  1673.   pool = make_sub_pool(permanent_pool);
  1674.   main_server = (server_rec*)pcalloc(pool,sizeof(server_rec));
  1675.   xaset_insert(servers,(xasetmember_t*)main_server);
  1676.   main_server->pool = pool;
  1677.   main_server->set = servers;
  1678. }
  1679. /* These functions are used by modules to help parse configuration.
  1680.  */
  1681. int check_conf(cmd_rec *cmd, int allowed)
  1682. {
  1683.   int x;
  1684.   x = (cmd->config && cmd->config->config_type != CONF_PARAM ?
  1685.        cmd->config->config_type : CONF_ROOT);
  1686.   return (x & allowed);
  1687. }
  1688. char *get_section_name(cmd_rec *cmd)
  1689. {
  1690.   if(!cmd->config || cmd->config->config_type == CONF_PARAM)
  1691.     return "top level";
  1692.   switch(cmd->config->config_type) {
  1693.   case CONF_DIR: return "<Directory>";
  1694.   case CONF_ANON: return "<Anonymous>";
  1695.   case CONF_LIMIT: return "<Limit>";
  1696.   case CONF_GLOBAL: return "<Global>";
  1697.   };
  1698.   return "(null)";
  1699. }
  1700. int get_boolean(cmd_rec *cmd, int av)
  1701. {
  1702.   char *cp = cmd->argv[av];
  1703.   /* Boolean string can be "on","off","yes","no",
  1704.    * "true","false","1" or "0"
  1705.    */
  1706.   if(!strcasecmp(cp,"on"))
  1707.     return 1;
  1708.   if(!strcasecmp(cp,"off"))
  1709.     return 0;
  1710.   if(!strcasecmp(cp,"yes"))
  1711.     return 1;
  1712.   if(!strcasecmp(cp,"no"))
  1713.     return 0;
  1714.   if(!strcasecmp(cp,"true"))
  1715.     return 1;
  1716.   if(!strcasecmp(cp,"false"))
  1717.     return 0;
  1718.   if(!strcasecmp(cp,"1"))
  1719.     return 1;
  1720.   if(!strcasecmp(cp,"0"))
  1721.     return 0;
  1722.   return -1;
  1723. }
  1724. char *get_full_cmd(cmd_rec *cmd)
  1725. {
  1726.   pool *p = cmd->tmp_pool;
  1727.   char *res = "";
  1728.   int i;
  1729.   if(cmd->arg)
  1730.     res = pstrcat(p,cmd->argv[0]," ",cmd->arg,NULL);
  1731.   else {
  1732.     for(i = 0; i < cmd->argc; i++)
  1733.       res = pstrcat(p,res,cmd->argv[i]," ",NULL);
  1734.     while(res[strlen(res)-1] == ' ' && *res)
  1735.       res[strlen(res)-1] = '';
  1736.   }
  1737.   return res;
  1738. }