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

Ftp客户端

开发平台:

Unix_Linux

  1. /*
  2.  * ProFTPD - FTP server daemon
  3.  * Copyright (c) 1997, 1998 Public Flood Software
  4.  * Copyright (C) 1999, MacGyver aka Habeeb J. Dihu <macgyver@tos.net>
  5.  *  
  6.  * This program is free software; you can redistribute it and/or modify
  7.  * it under the terms of the GNU General Public License as published by
  8.  * the Free Software Foundation; either version 2 of the License, or
  9.  * (at your option) any later version.
  10.  *
  11.  * This program is distributed in the hope that it will be useful,
  12.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  * GNU General Public License for more details.
  15.  *
  16.  * You should have received a copy of the GNU General Public License
  17.  * along with this program; if not, write to the Free Software
  18.  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
  19.  */
  20. /*
  21.  * Authentication module for ProFTPD
  22.  * $Id: mod_auth.c,v 1.12 1999/10/01 07:57:31 macgyver Exp $
  23.  */
  24. #include "conf.h"
  25. #include "privs.h"
  26. /* From the core module */
  27. extern int core_display_file(const char *,const char *);
  28. extern pid_t mpid;
  29. module auth_module;
  30. static int logged_in = 0;
  31. static int auth_tries = 0;
  32. /* check_auth is hooked into the main server's auth_hook function,
  33.  * so that we can deny all commands until authentication is complete.
  34.  */
  35. int check_auth(cmd_rec *cmd)
  36. {
  37.   if(get_param_int(cmd->server->conf,"authenticated",FALSE) != 1) {
  38.     send_response(R_530,"Please login with USER and PASS.");
  39.     return FALSE;
  40.   }
  41.   return TRUE;
  42. }
  43. int _auth_shutdown(CALLBACK_FRAME)
  44. {
  45.   log_pri(LOG_ERR,"scheduled main_exit() never ran [from auth:_login_timeout], terminating.");
  46.   end_login(1);
  47.   return 0; /* Avoid compiler warning */
  48. }
  49. /* As for 1.2.0, timer callbacks are now non-reentrant, so it's
  50.  * safe to call main_exit()
  51.  */
  52. int _login_timeout(CALLBACK_FRAME)
  53. {
  54.   /* Is this the proper behavior when timing out? */
  55.   send_response_async(R_421,"Login Timeout (%d seconds): closing control connection.",
  56.                       TimeoutLogin);
  57.   main_exit((void*)LOG_NOTICE,"FTP login timed out, disconnected.",
  58.   (void*)0,NULL);
  59. /*
  60.   schedule(main_exit,0,(void*)LOG_NOTICE,"FTP login timed out, disconnected.",
  61.            (void*)0,NULL);
  62.   remove_timer(TIMER_IDLE,ANY_MODULE);
  63.   remove_timer(TIMER_NOXFER,ANY_MODULE);
  64.   add_timer(10,-1,&auth_module,_auth_shutdown);
  65. */
  66. /* should never be reached */
  67.   return 0; /* Don't restart the timer */
  68. }
  69. int auth_init_child()
  70. {
  71.   /* Start the login timer */
  72.   if(TimeoutLogin)
  73.     add_timer(TimeoutLogin,TIMER_LOGIN,&auth_module,_login_timeout);
  74.   return 0;
  75. }
  76. int auth_init()
  77. {
  78.   /* By default, enable auth checking */
  79.   set_auth_check(check_auth);
  80.   return 0;
  81. }
  82. static int _do_auth(pool *p, xaset_t *conf, char *u, char *pw)
  83. {
  84.   char *cpw = NULL;
  85.   config_rec *c;
  86.   if(conf) {
  87.     c = find_config(conf,CONF_PARAM,"UserPassword",FALSE);
  88.     while(c) {
  89.       if(!strcmp(c->argv[0],u)) {
  90.         cpw = (char*)c->argv[1];
  91.         break;
  92.       }
  93.       c = find_config_next(c,c->next,CONF_PARAM,"UserPassword",FALSE);
  94.     }
  95.   }
  96.   if(cpw) {
  97.     if(!auth_getpwnam(p,u))
  98.       return AUTH_NOPWD;
  99.     return auth_check(p,cpw,u,pw);
  100.   }
  101.   return auth_authenticate(p,u,pw);
  102. }
  103. /* Handle group based authentication, only checked if pw
  104.  * based fails
  105.  */
  106. static config_rec *_auth_group(pool *p, char *user, char **group,
  107.                                char **ournamep, char **anonnamep, char *pass)
  108. {
  109.   config_rec *c;
  110.   char *ourname = NULL,*anonname = NULL;
  111.   char **grmem;
  112.   struct group *grp;
  113.   ourname = (char*)get_param_ptr(main_server->conf,"UserName",FALSE);
  114.   if(ournamep && ourname)
  115.     *ournamep = ourname;
  116.   c = find_config(main_server->conf,CONF_PARAM,"GroupPassword",TRUE);
  117.   if(c) do {
  118.     grp = auth_getgrnam(p,c->argv[0]);
  119.     if(!grp)
  120.       continue;
  121.     for(grmem = grp->gr_mem; *grmem; grmem++)
  122.       if(!strcmp(*grmem,user)) {
  123.         if(auth_check(p,c->argv[1],user,pass) == 0)
  124.           break;
  125.       }
  126.     if(*grmem) {
  127.       if(group)
  128.         *group = c->argv[0];
  129.       if(c->parent)
  130.         c = c->parent;
  131.       if(c->config_type == CONF_ANON)
  132.         anonname = (char*)get_param_ptr(c->subset,"UserName",FALSE);
  133.       if(anonnamep)
  134.         *anonnamep = anonname;
  135.       if(anonnamep && !anonname && ourname)
  136.         *anonnamep = ourname;
  137.       
  138.       break;
  139.     }
  140.   } while((c = find_config_next(c,c->next,CONF_PARAM,"GroupPassword",TRUE)) != NULL);
  141.   return c;
  142. }
  143. static void build_group_arrays(pool *p, struct passwd *xpw, char *name,
  144.                             array_header **gids, array_header **groups)
  145. {
  146.   struct group *gr;
  147.   struct passwd *pw = xpw;
  148.   array_header *xgids,*xgroups;
  149.   char **gr_mem;
  150.   xgids = make_array(p,2,sizeof(int));
  151.   xgroups = make_array(p,2,sizeof(char*));
  152.   if(!pw && !name)
  153.     return;
  154.   if(!pw) {
  155.     pw = auth_getpwnam(p,name);
  156.     if(!pw)
  157.       return;
  158.   }
  159.   if((gr = auth_getgrgid(p,pw->pw_gid)) != NULL)
  160.     *((char**)push_array(xgroups)) =
  161.                          pstrdup(p,gr->gr_name);
  162.   auth_setgrent(p);
  163.   while((gr = auth_getgrent(p)) != NULL && gr->gr_mem)
  164.     for(gr_mem = gr->gr_mem; *gr_mem; gr_mem++) {
  165.       if(!strcmp(*gr_mem,pw->pw_name)) {
  166.         *((int*)push_array(xgids)) = (int)gr->gr_gid;
  167.         if(pw->pw_gid != gr->gr_gid)
  168.           *((char**)push_array(xgroups)) =
  169.                          pstrdup(p,gr->gr_name);
  170.         break;
  171.       }
  172.     }
  173.   *gids = xgids;
  174.   *groups = xgroups;
  175. }
  176. static int _init_groups(pool *p, gid_t addl_group)
  177. {
  178.   gid_t *gid_arr;
  179.   int i,*session_gids;
  180.   size_t ngids = session.gids->nelts;
  181.   session_gids = session.gids->elts;
  182.   gid_arr = palloc(p,sizeof(gid_t) * (ngids+1));
  183.   for(i = 0; i < ngids; i++)
  184.     gid_arr[i] = (gid_t)session_gids[i];
  185.   
  186.   gid_arr[i] = addl_group;
  187.   return setgroups(ngids+1,gid_arr);
  188. }
  189. static config_rec *_auth_anonymous_group(pool *p, char *user)
  190. {
  191.   config_rec *c;
  192.   int ret = 0;
  193.   build_group_arrays(p,NULL,user,&session.gids,&session.groups);
  194.   c = find_config(main_server->conf,CONF_PARAM,"AnonymousGroup",FALSE);
  195.   if(c) do {
  196.     ret = group_expression((char**)c->argv);
  197.   } while(!ret && (c = find_config_next(c,c->next,CONF_PARAM,"AnonymousGroup",FALSE)) != NULL);
  198.  
  199.   return ret ? c : NULL;
  200. }
  201. static config_rec *_auth_resolve_user(pool *p,char **user,
  202.                                       char **ournamep,
  203.                                       char **anonnamep)
  204. {
  205.   config_rec *c,*topc;
  206.   char *ourname,*anonname = NULL;
  207.   int is_alias = 0, force_anon = 0;
  208.   /* Precendence rules:
  209.    *   1. Search for UserAlias directive.
  210.    *   2. Search for Anonymous directive.
  211.    *   3. Normal user login
  212.    */
  213.   ourname = (char*)get_param_ptr(main_server->conf,"UserName",FALSE);
  214.   if(ournamep && ourname)
  215.     *ournamep = ourname; 
  216.   c = find_config(main_server->conf,CONF_PARAM,"UserAlias",TRUE);
  217.   if(c) do {
  218.     if(!strcmp(c->argv[0], "*") || !strcmp(c->argv[0],*user)) {
  219.       is_alias = 1;
  220.       break;
  221.     }  
  222.   } while((c = find_config_next(c,c->next,CONF_PARAM,"UserAlias",TRUE)) != NULL);
  223.   /* if AuthAliasOnly is set, ignore this one and continue */
  224.   topc = c;
  225.   while(c && c->parent &&
  226.              find_config(c->parent->set,CONF_PARAM,"AuthAliasOnly",FALSE)) {
  227.     is_alias = 0;
  228.     find_config_set_top(topc);
  229.     c = find_config_next(c,c->next,CONF_PARAM,"UserAlias",TRUE);
  230.     if(c && (!strcmp(c->argv[0],"*") || !strcmp(c->argv[0],*user)))
  231.       is_alias = 1;
  232.   }
  233.   if(c) {
  234.     *user = c->argv[1];
  235.     /* If the alias is applied inside an <Anonymous> context, we have found
  236.      * our anon block
  237.      */
  238.     if(c->parent && c->parent->config_type == CONF_ANON)
  239.       c = c->parent;
  240.     else
  241.       c = NULL;
  242.   }
  243.   /* Next, search for an anonymous entry */
  244.   if(!c)
  245.     c = find_config(main_server->conf,CONF_ANON,NULL,FALSE);
  246.   else
  247.     find_config_set_top(c);
  248.   if(c) do {
  249.     anonname = (char*)get_param_ptr(c->subset,"UserName",FALSE);
  250.     if(!anonname)
  251.       anonname = ourname;
  252.     if(anonname && !strcmp(anonname,*user)) {
  253.        if(anonnamep)
  254.          *anonnamep = anonname;
  255.        break;
  256.     }
  257.   } while((c = find_config_next(c,c->next,CONF_ANON,NULL,FALSE)) != NULL);
  258.   if(!c) {
  259.     c = _auth_anonymous_group(p,*user);
  260.     if(c)
  261.       force_anon = 1;
  262.   }
  263.   if(!is_alias && !force_anon) {
  264.     if(find_config((c ? c->subset :
  265.                    main_server->conf),CONF_PARAM,"AuthAliasOnly",FALSE)) {
  266.       
  267.       if(c && c->config_type == CONF_ANON)
  268.         c = NULL;
  269.       else
  270.         *user = NULL;
  271.       if(*user && find_config(main_server->conf,CONF_PARAM,"AuthAliasOnly",FALSE))
  272.         *user = NULL;
  273.       if((!user || !c) && anonnamep)
  274.         *anonnamep = NULL;
  275.     }
  276.   }
  277.   return c;
  278. }
  279. static int _auth_check_ftpusers(xaset_t *s, const char *user)
  280. {
  281.   int res = 1;
  282.   FILE *fp;
  283.   char *u,buf[256];
  284.   if(get_param_int(s,"UseFtpUsers",FALSE) != 0) {
  285.     PRIVS_ROOT
  286.     fp = fopen(FTPUSERS_PATH,"r");
  287.     PRIVS_RELINQUISH
  288.     if(!fp)
  289.       return res;
  290.     while(fgets(buf,sizeof(buf)-1,fp)) {
  291.       buf[sizeof(buf)-1] = ''; CHOP(buf);
  292.       u = buf; while(isspace((UCHAR)*u) && *u) u++;
  293.       if(!*u || *u == '#')
  294.         continue;
  295.       if(!strcmp(u,user)) {
  296.         res = 0;
  297.         break;
  298.       }
  299.     }
  300.     fclose(fp);
  301.   }
  302.   return res;
  303. }
  304. static int _auth_check_shell(xaset_t *s, const char *shell)
  305. {
  306.   int res = 1;
  307.   FILE *shellf;
  308.   char buf[256];
  309.   if(get_param_int(s,"RequireValidShell",FALSE) != 0 &&
  310.      (shellf = fopen(VALID_SHELL_PATH,"r")) != NULL) {
  311.     res = 0;
  312.     while(fgets(buf,sizeof(buf)-1,shellf)) {
  313.       buf[sizeof(buf)-1] = ''; CHOP(buf);
  314.       if(!strcmp(shell,buf)) {
  315.         res = 1;
  316.         break;
  317.       }
  318.     }
  319.     fclose(shellf);
  320.   }
  321.   return res;
  322. }
  323. /* Determine any applicable chdirs
  324.  */
  325. static char *_get_default_chdir(pool *p, xaset_t *conf)
  326. {
  327.   config_rec *c;
  328.   char *dir = NULL;
  329.   int ret;
  330.   c = find_config(conf,CONF_PARAM,"DefaultChdir",FALSE);
  331.   while(c) {
  332.     /* Check the groups acl */
  333.     if(c->argc < 2) {
  334.       dir = c->argv[0];
  335.       break;
  336.     }
  337.     ret = group_expression(((char**)c->argv)+1);
  338.     if(ret) {
  339.       dir = c->argv[0];
  340.       break;
  341.     }
  342.     c = find_config_next(c,c->next,CONF_PARAM,"DefaultChdir",FALSE);
  343.   }
  344.   /* if the directory is relative, concatenate w/ session.cwd
  345.    */
  346.   if(dir && *dir != '/' && *dir != '~')
  347.     dir = pdircat(p,session.cwd,dir,NULL);
  348.   return dir;
  349. }
  350. /* Determine if the user (non-anon) needs a default root dir
  351.  * other than /
  352.  */
  353. static char *_get_default_root(pool *p)
  354. {
  355.   config_rec *c;
  356.   char *dir = NULL;
  357.   int ret;
  358.   c = find_config(main_server->conf,CONF_PARAM,"DefaultRoot",FALSE);
  359.   while(c) {
  360.     /* Check the groups acl */
  361.     if(c->argc < 2) {
  362.       dir = c->argv[0];
  363.       break;
  364.     }
  365.     ret = group_expression(((char**)c->argv)+1);
  366.     if(ret) {
  367.       dir = c->argv[0];
  368.       break;
  369.     }
  370.     c = find_config_next(c,c->next,CONF_PARAM,"DefaultRoot",FALSE);
  371.   }
  372.   if(dir) {
  373.     if(!strcmp(dir,"/"))
  374.       dir = NULL;
  375.     else {
  376.       char *realdir;
  377.       realdir = dir_realpath(p,dir);
  378.       if(realdir)
  379.         dir = realdir;
  380.     }
  381.   }
  382.   return dir;
  383. }
  384. static struct passwd *passwd_dup(pool *p, struct passwd *pw)
  385. {
  386.   struct passwd *npw;
  387.       
  388.   npw = pcalloc(p,sizeof(struct passwd));
  389.    
  390.   npw->pw_name = pstrdup(p,pw->pw_name);
  391.   npw->pw_passwd = pstrdup(p,pw->pw_passwd);
  392.   npw->pw_uid = pw->pw_uid;
  393.   npw->pw_gid = pw->pw_gid;
  394.   npw->pw_gecos = pstrdup(p,pw->pw_gecos);
  395.   npw->pw_dir = pstrdup(p,pw->pw_dir);
  396.   npw->pw_shell = pstrdup(p,pw->pw_shell);
  397.       
  398.   return npw;
  399. }
  400. /* Next function (the biggie) handles all authentication, setting
  401.  * up chroot() jail, etc.
  402.  */
  403. static int _setup_environment(pool *p, char *user, char *pass)
  404. {
  405.   struct passwd *pw;
  406.   struct stat sbuf;
  407.   config_rec *c;
  408.   char *origuser,*ourname,*anonname = NULL,*anongroup = NULL,*ugroup = NULL;
  409.   char ttyname[20], *defaulttransfermode;
  410.   char *defroot = NULL,*defchdir = NULL,*xferlog = NULL;
  411.   int aclp,i,force_anon = 0,wtmp_log = -1,showsymlinks;
  412.   /********************* Authenticate the user here *********************/
  413.   session.hide_password = TRUE;
  414.   origuser = user;
  415.   c = _auth_resolve_user(p,&user,&ourname,&anonname);
  416.   if(!user) {
  417.     log_pri(LOG_NOTICE,"failed login from %s, '%s' is not a UserAlias.",inet_ascii(p,session.c->remote_ipaddr),origuser);
  418.     goto auth_failure;
  419.   }
  420.   /* If c != NULL from this point on, we have an anonymous login */
  421.   aclp = login_check_limits(main_server->conf,FALSE,TRUE,&i);
  422.   if((pw = auth_getpwnam(p,user)) == NULL) {
  423.     log_pri(LOG_NOTICE,"failed login from %s, can't find user '%s'",inet_ascii(p,session.c->remote_ipaddr),user);
  424.     goto auth_failure;
  425.   }
  426.   /* security: other functions perform pw lookups, thus we need to make
  427.    * a local copy of the user just looked up
  428.    */
  429.   pw = passwd_dup(p,pw);
  430.   if(pw->pw_uid == 0) {
  431.     /* If RootLogin is set to true, we allow this... even though we
  432.      * still log a warning. :)
  433.      */
  434.     if(get_param_int((c ? c->subset : main_server->conf),"RootLogin",FALSE) != 1) {
  435.       log_auth(LOG_CRIT,"SECURITY VIOLATION: root login attempted from %s [%s] to %s:%i",
  436.        session.c->remote_name,
  437.        inet_ascii(p,session.c->remote_ipaddr),
  438.        inet_ascii(p,session.c->local_ipaddr),
  439.        session.c->local_port);
  440.       return 0;
  441.     } else
  442.       log_auth(LOG_WARNING,"root login from %s [%s] to %s:%i",
  443.        session.c->remote_name,
  444.        inet_ascii(p,session.c->remote_ipaddr),
  445.        inet_ascii(p,session.c->local_ipaddr),
  446.        session.c->local_port);
  447.   }
  448.   session.user = pstrdup(p,pw->pw_name);
  449.   session.group = pstrdup(p,auth_gid_name(p,pw->pw_gid));
  450.   /* set force_anon (for AnonymousGroup) and build a custom
  451.    * anonymous config for this session.
  452.    */
  453.   if(c && c->config_type != CONF_ANON) {
  454.     force_anon = 1;
  455.     defroot = _get_default_root(session.pool);
  456.     if(!defroot)
  457.       defroot = pstrdup(session.pool,pw->pw_dir);
  458.     c = (config_rec*)pcalloc(session.pool,sizeof(config_rec));
  459.     c->config_type = CONF_ANON;
  460.     c->name = defroot;
  461.     anonname = pw->pw_name;
  462.     /* hackery, we trick everything else by pointing the subset
  463.      * at the main server's configuration.  tricky, eh?
  464.      */
  465.      c->subset = main_server->conf;
  466.   }
  467.   if(c) {
  468.     if(!force_anon) {
  469.         anongroup = (char*)get_param_ptr(c->subset,"GroupName",FALSE);
  470.       if(!anongroup)
  471.         anongroup = (char*)get_param_ptr(main_server->conf,"GroupName",FALSE);
  472.     }
  473.     if(!login_check_limits(c->subset,FALSE,TRUE,&i) || (!aclp && !i) ){
  474.       log_auth(LOG_NOTICE,"ANON %s: LIMIT access denies login from %s [%s] to %s:%i",
  475.                           origuser,
  476.                           session.c->remote_name,
  477.                           inet_ascii(p,session.c->remote_ipaddr),
  478.   inet_ascii(p,session.c->local_ipaddr),
  479.   session.c->local_port);
  480.       goto auth_failure;
  481.     }
  482.   }
  483.   if(!c && !aclp) {
  484.     log_auth(LOG_NOTICE,"USER %s: LIMIT access denies login from %s [%s] to %s:%i",
  485.              origuser,session.c->remote_name,
  486.              inet_ascii(p,session.c->remote_ipaddr),
  487.      inet_ascii(p,session.c->local_ipaddr),
  488.      session.c->local_port);
  489.     goto auth_failure;
  490.   }
  491.   if(!c || get_param_int(c->subset,"AnonRequirePassword",FALSE) == 1) {
  492.     int authcode;
  493.     if(c)
  494.       authcode = _do_auth(p,c->subset,user,pass);
  495.     else
  496.       authcode = _do_auth(p,main_server->conf,user,pass);
  497.     if(authcode) {
  498.       /* Normal authentication has failed, see if group authentication
  499.        * passes
  500.        */
  501.       if((c = _auth_group(p,user,&anongroup,&ourname,&anonname,pass)) != NULL) {
  502.         if(c->config_type != CONF_ANON) {
  503.           c = NULL;
  504.           ugroup = anongroup; anongroup = NULL;
  505.         }
  506.         authcode = 0;
  507.       }
  508.     }
  509.       
  510.     bzero(pass,strlen(pass));
  511.     switch(authcode) {
  512.     case AUTH_NOPWD:
  513.       log_auth(LOG_NOTICE,"USER %s: no such user found from %s [%s] to %s:%i",
  514.                user,session.c->remote_name,
  515.        inet_ascii(p,session.c->remote_ipaddr),
  516.        inet_ascii(p,session.c->local_ipaddr),
  517.        session.c->local_port);
  518.       break;
  519.     case AUTH_BADPWD:
  520.       log_auth(LOG_NOTICE,"USER %s: incorrect password from %s [%s] to %s:%i",
  521.                origuser,session.c->remote_name,
  522.        inet_ascii(p,session.c->remote_ipaddr),
  523.        inet_ascii(p,session.c->local_ipaddr),
  524.        session.c->local_port);
  525.       break;
  526.     case AUTH_AGEPWD:
  527.       log_auth(LOG_NOTICE,"USER %s: password expired from %s [%s] to %s:%i",
  528.        user,session.c->remote_name,
  529.        inet_ascii(p,session.c->remote_ipaddr),
  530.        inet_ascii(p,session.c->local_ipaddr),
  531.        session.c->local_port);
  532.       break;
  533.     case AUTH_DISABLEDPWD:
  534.       log_auth(LOG_NOTICE,"USER %s: account disabled from %s [%s] to %s:%i",
  535.        user,session.c->remote_name,
  536.        inet_ascii(p,session.c->remote_ipaddr),
  537.        inet_ascii(p,session.c->local_ipaddr),
  538.        session.c->local_port);
  539.       break;
  540.     };
  541.     if(authcode != 0)
  542.       goto auth_failure;
  543.   } else if(c)
  544.     session.hide_password = FALSE;
  545. /* Flood - 7/10/97, not sure what setutent() was used for, but it
  546.  * certainly looks unnecessary now.
  547.  */
  548.  /* setutent(); */
  549.   auth_setgrent(p);
  550.   if(!_auth_check_shell((c ? c->subset : main_server->conf),pw->pw_shell)) {
  551.     log_auth(LOG_NOTICE,"failed login '%s' from %s [%s] to %s:%i (invalid shell)",
  552.                user,session.c->remote_name,
  553.        inet_ascii(p,session.c->remote_ipaddr),
  554.        inet_ascii(p,session.c->local_ipaddr),
  555.        session.c->local_port);
  556.     goto auth_failure;
  557.   }
  558.   if(!_auth_check_ftpusers((c ? c->subset : main_server->conf),pw->pw_name)) {
  559.     log_auth(LOG_NOTICE,"failed login '%s' from %s [%s] to %s:%i (user in %s)",
  560.              pw->pw_name,session.c->remote_name,
  561.              inet_ascii(p,session.c->remote_ipaddr),
  562.      inet_ascii(p,session.c->local_ipaddr),
  563.      session.c->local_port,
  564.              FTPUSERS_PATH);
  565.     goto auth_failure;
  566.   }
  567.   if(c) {
  568.     struct group *grp;
  569.     int add_userdir;
  570.     char *u;
  571.     
  572.     u = (char *) get_param_int(main_server->conf,C_USER,FALSE);               
  573.                                                                               
  574.     add_userdir = get_param_int((c ? c->subset : main_server->conf),
  575. "UserDirRoot",FALSE);
  576.     if(add_userdir > 0 && strcmp(u, user)) {
  577.       session.anon_root = dir_realpath(session.pool, pdircat(session.pool,
  578.      c->name, u, NULL));
  579.     } else {
  580.       session.anon_root = dir_realpath(session.pool,c->name);
  581.     }
  582.     
  583.     session.anon_user = pstrdup(session.pool,pass);
  584.     if(!session.anon_root) {
  585.       log_pri(LOG_ERR,"%s: Directory %s is not accessible.",
  586.               session.user,c->name);
  587.       add_response_err(R_530,"Unable to set anonymous privileges.");
  588.       goto auth_failure;
  589.     }
  590.   
  591.     sstrncpy(session.cwd, "/", sizeof(session.cwd));
  592.     xferlog = get_param_ptr(c->subset,"TransferLog",FALSE);
  593.     if(anongroup) {
  594.       grp = auth_getgrnam(p,anongroup);
  595.       if(grp) {
  596.         pw->pw_gid = grp->gr_gid;
  597.         session.group = pstrdup(p,grp->gr_name);
  598.       }
  599.     }
  600.   } else {
  601.     struct group *grp;
  602.     if(ugroup) {
  603.       grp = auth_getgrnam(p,ugroup);
  604.       if(grp) {
  605.         pw->pw_gid = grp->gr_gid;
  606.         session.group = pstrdup(p,grp->gr_name);
  607.       }
  608.     }
  609.     sstrncpy(session.cwd, pw->pw_dir, MAX_PATH_LEN);
  610.   }
  611.   /* Get default chdir (if any) */
  612.   defchdir = _get_default_chdir(p,(c ? c->subset : main_server->conf));
  613.   
  614.   if(defchdir)
  615.     sstrncpy(session.cwd, defchdir, MAX_PATH_LEN);
  616.   build_group_arrays(session.pool,pw,NULL,
  617.                      &session.gids,&session.groups);
  618.   /* check limits again to make sure deny/allow directives still permit
  619.    * access.
  620.    */
  621.   if(!login_check_limits((c ? c->subset : main_server->conf),FALSE,TRUE,&i))
  622.   {
  623.     log_auth(LOG_NOTICE,"%s: LIMIT access denies login (DenyGroup) from %s [%s] to %s:%i",
  624.              origuser,session.c->remote_name,
  625.              inet_ascii(p,session.c->remote_ipaddr),
  626.      inet_ascii(p,session.c->local_ipaddr),
  627.      session.c->local_port);
  628.     goto auth_failure;
  629.   }
  630.   
  631.   /* perform a dir fixup */
  632.   resolve_defered_dirs(main_server);
  633.   fixup_dirs(main_server,CF_DEFER);
  634.   /* If running under an anonymous context, resolve all <Directory>
  635.    * blocks inside it
  636.    */
  637.   if(c && c->subset)
  638.     resolve_anonymous_dirs(c->subset);
  639.   if(c)
  640.     log_auth(LOG_NOTICE,"ANONYMOUS FTP login as '%s' from %s [%s] to %s:%i",
  641.       origuser,session.c->remote_name,
  642.       inet_ascii(p,session.c->remote_ipaddr),
  643.       inet_ascii(p,session.c->local_ipaddr),
  644.       session.c->local_port);
  645.   else
  646.     log_auth(LOG_NOTICE,"FTP login as '%s' from %s [%s] to %s:%i",
  647.       origuser,session.c->remote_name,
  648.       inet_ascii(p,session.c->remote_ipaddr),
  649.       inet_ascii(p,session.c->local_ipaddr),
  650.       session.c->local_port);
  651.   /* Write the login to wtmp.  This must be done here because we won't
  652.    * have access after we give up root.  This can result in falsified
  653.    * wtmp entries if an error kicks the user out before we get
  654.    * through with the login process.  Oh well.
  655.    */
  656. #if (defined(BSD) && (BSD >= 199103))
  657.   snprintf(ttyname, sizeof(ttyname), "ftp%ld",(long)getpid());
  658. #else
  659.   snprintf(ttyname, sizeof(ttyname), "ftpd%d",(int)getpid());
  660. #endif
  661.   /* Perform wtmp logging only if not turned off in <Anonymous>
  662.    * or the current server
  663.    */
  664.   if(c)
  665.     wtmp_log = get_param_int(c->subset,"WtmpLog",FALSE);
  666.   if(wtmp_log == -1)
  667.     wtmp_log = get_param_int(main_server->conf,"WtmpLog",FALSE);
  668.   PRIVS_ROOT
  669.   if(wtmp_log != 0) {
  670.     log_wtmp(ttyname,session.user,session.c->remote_name,
  671.              session.c->remote_ipaddr);
  672.     session.wtmp_log = TRUE;
  673.   }
  674.   /* Open the /var/run log for later writing */
  675.   log_open_run(mpid,FALSE,TRUE);
  676.   /* Open /var/log/ files */
  677.   if(!xferlog) {
  678.     if(c)
  679.       xferlog = get_param_ptr(c->subset,"TransferLog",FALSE);
  680.     if(!xferlog)
  681.       xferlog = get_param_ptr(main_server->conf,"TransferLog",FALSE);
  682.     if(!xferlog)
  683.       xferlog = XFERLOG_PATH;
  684.   }
  685.   if(strcasecmp(xferlog,"NONE") == 0)
  686.     log_open_xfer(NULL);
  687.   else
  688.     log_open_xfer(xferlog);
  689.   _init_groups(p,pw->pw_gid);
  690.   PRIVS_RELINQUISH
  691.   /* Now check to see if the user has an applicable DefaultRoot */
  692.   if(!c && (defroot = _get_default_root(session.pool))) {
  693.     /* Make sure pass/group is open */
  694.     auth_setpwent(p);
  695.     auth_setgrent(p);
  696.     /* On some unices the following is necessary to ensure the files
  697.      * are open.  (BSDI 3.1)
  698.      */
  699.     auth_getpwent(p);
  700.     auth_getgrent(p);
  701.     PRIVS_ROOT
  702.     if(chroot(defroot) == -1) {
  703.       PRIVS_RELINQUISH
  704.       add_response_err(R_530,"Unable to set default root directory.");
  705.       log_pri(LOG_ERR,"%s chroot("%s"): %s",session.user,
  706.               defroot,strerror(errno));
  707.       end_login(1);
  708.     }
  709.     PRIVS_RELINQUISH
  710.     session.anon_root = defroot;
  711.     /* Re-calc the new cwd based on this root dir.  If not applicable
  712.      * place the user in / (of defroot)
  713.      */
  714.     if(strncmp(session.cwd,defroot,strlen(defroot)) == 0) {
  715.       char *newcwd = &session.cwd[strlen(defroot)];
  716.       if(*newcwd == '/')
  717.         newcwd++;
  718.       session.cwd[0] = '/';
  719.       sstrncpy(&session.cwd[1], newcwd, sizeof(session.cwd));
  720.     } else
  721.       sstrncpy(session.cwd, "/", sizeof(session.cwd));
  722.   }
  723.   if(c) {
  724.     /* Make sure pass/group is open */
  725.     auth_setpwent(p);
  726.     auth_setgrent(p);
  727.     /* On some unices the following is necessary to ensure the files
  728.      * are open.  (BSDI 3.1)
  729.      */
  730.     auth_getpwent(p);
  731.     auth_getgrent(p);
  732.   }
  733.   PRIVS_ROOT
  734.   if(c && chroot(session.anon_root) == -1) { 
  735.     if(session.uid)
  736.       _init_groups(p,session.gid);
  737.     PRIVS_RELINQUISH
  738.     add_response_err(R_530,"Unable to set anonymous privileges.");
  739.     log_pri(LOG_ERR,"%s chroot(): %s",session.user,strerror(errno));
  740.     
  741.     end_login(1);
  742.   }
  743.   /* new in 1.1.x, I gave in and we don't give up root permanently..
  744.    * sigh.
  745.    */
  746. #ifndef __hpux
  747.   block_signals();
  748.   PRIVS_ROOT
  749.   setuid(0);
  750.   setgid(0);
  751.   PRIVS_SETUP(pw->pw_uid,pw->pw_gid)
  752.   unblock_signals();
  753. #else
  754.   session.uid = session.ouid = pw->pw_uid;
  755.   session.gid = pw->pw_gid;
  756.   PRIVS_RELINQUISH
  757. #endif
  758. #ifdef HAVE_GETEUID
  759.   if(getegid() != pw->pw_gid ||
  760.      geteuid() != pw->pw_uid) {
  761.     PRIVS_RELINQUISH
  762.     add_response_err(R_530,"Unable to set user privileges.");
  763.     log_pri(LOG_ERR,"%s setregid() or setreuid(): %s",
  764.             session.user,strerror(errno));
  765.     end_login(1);
  766.   }
  767. #endif
  768.   /*
  769.    *  session.uid = pw->pw_uid;
  770.    */
  771.   /* Overwrite original uid, so PRIVS_ macros no longer 
  772.    * try to do anything 
  773.    */
  774.   /*
  775.    * session.ouid = pw->pw_uid;
  776.    * session.gid = pw->pw_gid;
  777.    */
  778.   /* chdir to the proper directory, do this even if anonymous
  779.    * to make sure we aren't outside our chrooted space.
  780.    */
  781.   showsymlinks = get_param_int((c ? c->subset : main_server->conf),
  782.                                "ShowSymlinks",FALSE);
  783.   if(showsymlinks == -1)
  784.     showsymlinks = 1;
  785.   if(fs_chdir_canon(session.cwd,!showsymlinks) == -1) {
  786.     add_response_err(R_530,"Unable to chdir.");
  787.     log_pri(LOG_ERR,"%s chdir("%s"): %s",session.user,
  788.             session.cwd,strerror(errno));
  789.     end_login(1);
  790.   }
  791.   sstrncpy(session.cwd, fs_getcwd(), sizeof(session.cwd));
  792.   sstrncpy(session.vwd, fs_getvwd(), sizeof(session.vwd));
  793.   /* check dynamic configuration */
  794.   if(fs_stat("/",&sbuf) != -1)
  795.     build_dyn_config(p,"/",&sbuf,1);
  796.   if(c) {
  797.     if(!session.hide_password)
  798.       session.proc_prefix =
  799.       pstrcat(permanent_pool,session.c->remote_name,
  800.               ": anonymous/",pass,NULL);
  801.     else
  802.       session.proc_prefix =
  803.       pstrcat(permanent_pool,session.c->remote_name,
  804.               ": anonymous",NULL);
  805.     session.anon_config = c;
  806.     session.flags = SF_ANON;
  807.   } else {
  808.     session.proc_prefix = pstrdup(permanent_pool,session.c->remote_name);
  809.             
  810.     session.flags = 0;
  811.   }
  812.   /* Make sure passwd file is closed after login to avoid leaking information */
  813.   auth_endpwent(p);
  814.   /* Default transfer mode is ASCII */
  815.   defaulttransfermode = (char*)get_param_ptr(CURRENT_CONF, "DefaultTransferMode", FALSE);
  816.   if (defaulttransfermode && strcasecmp(defaulttransfermode, "binary") == 0)
  817. session.flags &= (SF_ALL^SF_ASCII);
  818.   else
  819. session.flags |= SF_ASCII;
  820.   /* Authentication complete, user logged in, now kill the login
  821.    * timer.
  822.    */
  823.   log_run_address(session.c->remote_name,session.c->remote_ipaddr);
  824.   log_run_cwd(session.cwd);
  825.   main_set_idle();
  826.   remove_timer(TIMER_LOGIN,&auth_module);
  827.   session.user = pstrdup(permanent_pool,session.user);
  828.   session.group = pstrdup(permanent_pool,session.group);
  829.   return 1;
  830. auth_failure:
  831.   session.user = session.group = NULL;
  832.   session.gids = session.groups = NULL;
  833.   session.wtmp_log = 0;
  834.   return 0;
  835. }
  836.   
  837. MODRET cmd_user(cmd_rec *cmd)
  838. {
  839.   int nopass = 0, cur = 0,hcur = 0, ccur = 0;
  840.   logrun_t *l;
  841.   config_rec *c,*maxc;
  842.   char *user,*origuser, config_class_users[128];
  843.   int failnopwprompt = 0, aclp,i, classes_enabled;
  844.   if(logged_in)
  845.     return ERROR_MSG(cmd,R_503,"You are already logged in!");
  846.   if(cmd->argc != 2)
  847.     return ERROR_MSG(cmd,R_500,"'USER': command requires a parameter.");
  848.   user = cmd->argv[1];
  849.   remove_config(cmd->server->conf,C_USER,FALSE);
  850.   add_config_param_set(&cmd->server->conf,C_USER,1,
  851.                        pstrdup(cmd->server->pool,user));
  852.   origuser = user;
  853.   c = _auth_resolve_user(cmd->tmp_pool,&user,NULL,NULL);
  854.   switch(get_param_int((c && c->config_type == CONF_ANON ? c->subset :
  855.                      main_server->conf),"LoginPasswordPrompt",FALSE))
  856.   {
  857.     case 0: failnopwprompt = 1; break;
  858.     default: failnopwprompt = 0; break;
  859.   }
  860.   if(failnopwprompt) {
  861.     if(!user) {
  862.       log_pri(LOG_NOTICE,"failed login from %s, '%s' is not a UserAlias.",inet_ascii(cmd->tmp_pool,session.c->remote_ipaddr),origuser);
  863.       send_response(R_530,"Login incorrect.");
  864.       end_login(0);
  865.     }
  866.     aclp = login_check_limits(main_server->conf,FALSE,TRUE,&i);
  867.     if(c && c->config_type != CONF_ANON) {
  868.       c = (config_rec*)pcalloc(session.pool,sizeof(config_rec));
  869.       c->config_type = CONF_ANON;
  870.       c->name = ""; /* don't really need this yet */
  871.       c->subset = main_server->conf;
  872.     }
  873.     if(c) {
  874.       if(!login_check_limits(c->subset,FALSE,TRUE,&i) || (!aclp && !i) ) {
  875.               log_auth(LOG_NOTICE,"ANON %s: LIMIT access denies login from %s [%s] to %s:%i",
  876.                           origuser,
  877.                           session.c->remote_name,
  878.                           inet_ascii(cmd->tmp_pool,session.c->remote_ipaddr),
  879.   inet_ascii(cmd->tmp_pool,session.c->local_ipaddr),
  880.   session.c->local_port);
  881.               send_response(R_530,"Login incorrect.");
  882.               end_login(0);
  883.       }
  884.     }
  885.     if(!c && !aclp) {
  886.       log_auth(LOG_NOTICE,"USER %s: LIMIT access denies login from %s [%s] to %s:%i",
  887.                origuser,session.c->remote_name,
  888.                inet_ascii(cmd->tmp_pool,session.c->remote_ipaddr),
  889.        inet_ascii(cmd->tmp_pool,session.c->local_ipaddr),
  890.        session.c->local_port);
  891.       send_response(R_530,"Login incorrect.");
  892.       end_login(0);
  893.     }
  894.   }
  895.   
  896.   if((classes_enabled = get_param_int(main_server->conf,"Classes",FALSE)) < 0)
  897.     classes_enabled = 0;
  898.   
  899.   if(classes_enabled)
  900.     session.class = (class_t *) find_class(session.c->remote_ipaddr,
  901.    session.c->remote_name);
  902.   
  903.   /* Determine how many users are currently connected */
  904.   if(user) {
  905.     PRIVS_ROOT
  906.     while((l = log_read_run(NULL)) != NULL)
  907.       /* Make sure it matches our current server */
  908.       if(l->server_ip.s_addr == main_server->ipaddr->s_addr &&
  909.          l->server_port == main_server->ServerPort) {
  910.         if((c && c->config_type == CONF_ANON && !strcmp(l->user,user)) || !c) {
  911.           char *s, *d, ip[32];
  912.           cur++;
  913.           s = strchr (l->address, '[');
  914.           d = ip;
  915.           if (s != NULL) s++;
  916.           while (*s && *s != ']') *d++ = *s++;
  917.           *d = '';
  918.           if(!strcmp(ip, inet_ntoa(*session.c->remote_ipaddr)))
  919.             hcur++;
  920.         }
  921.         if(classes_enabled && strcmp(l->class, session.class->name) == 0)
  922.          ccur++;
  923.       }
  924.     PRIVS_RELINQUISH
  925.   }
  926.   remove_config(cmd->server->conf,"CURRENT-CLIENTS",FALSE);
  927.   add_config_param_set(&cmd->server->conf,"CURRENT-CLIENTS",1,(void*)cur);
  928.   if (classes_enabled) {
  929.     remove_config(cmd->server->conf,"CURRENT-CLASS",FALSE);
  930.     add_config_param_set(&cmd->server->conf,"CURRENT-CLASS",1,session.class->name);
  931.     snprintf(config_class_users, sizeof(config_class_users), "%s-%s", "CURRENT-CLIENTS-CLASS", session.class->name);
  932.     remove_config(cmd->server->conf,config_class_users,FALSE);
  933.     add_config_param_set(&cmd->server->conf,config_class_users,1,ccur);
  934.     /* too many users in this class ? */
  935.     if (ccur == session.class->max_connections) {
  936. char *display = NULL;
  937. if(session.flags & SF_ANON)
  938.   display = (char*) get_param_ptr(session.anon_config->subset,
  939.   "DisplayGoAway",FALSE);
  940.            
  941. if(!display)
  942.   display = (char*) get_param_ptr(cmd->server->conf,
  943.   "DisplayGoAway",FALSE);
  944. if (display)
  945.   core_display_file(R_530, display);
  946. else
  947.   send_response(R_530,
  948. "Too many users in your class, please try again later.");
  949. log_auth(LOG_NOTICE,"connection refused (max clients for class %s)",session.class->name);
  950. end_login(0);
  951.     }
  952.   }
  953.   
  954.   /* Try to determine what MaxClients applies to the user
  955.    * (if any) and count through the runtime file to see
  956.    * if this would exceed the max.
  957.    */
  958.   if(c && user && get_param_int(c->subset,"AnonRequirePassword",FALSE) != 1)
  959.       nopass++;
  960.   maxc = find_config((c ? c->subset : cmd->server->conf),
  961.                   CONF_PARAM,"MaxClientsPerHost",FALSE);
  962.   if(maxc) {
  963.     int max = (int)maxc->argv[0];
  964.     char *maxstr = "Sorry, maximum number clients (%m) from your host already connected.";
  965.     char maxn[10];
  966.     snprintf(maxn, sizeof(maxn), "%d",max);
  967.     if(maxc->argc > 1)
  968.       maxstr = maxc->argv[1];
  969.     
  970.     if(hcur >= max) {
  971.       send_response(R_530,"%s",
  972.                     sreplace(cmd->tmp_pool,maxstr,"%m",maxn,NULL));
  973.       log_auth(LOG_NOTICE,"connection refused (max clients per host %d)",
  974.                max);
  975.       end_login(0);
  976.     }
  977.   }
  978.   maxc = find_config((c ? c->subset : cmd->server->conf),
  979.                   CONF_PARAM,"MaxClients",FALSE);
  980.   if(maxc) {
  981.     int max = (int)maxc->argv[0];
  982.     char *maxstr = "Sorry, maximum number of allowed clients (%m) already connected.";
  983.     char maxn[10];
  984.     snprintf(maxn, sizeof(maxn), "%d",max);
  985.     if(maxc->argc > 1)
  986.       maxstr = maxc->argv[1];
  987.     
  988.     if(cur >= max) {
  989.       send_response(R_530,"%s",
  990.                     sreplace(cmd->tmp_pool,maxstr,"%m",maxn,NULL));
  991.       log_auth(LOG_NOTICE,"connection refused (max clients %d)",
  992.                max);
  993.       end_login(0);
  994.     }
  995.   }
  996.   /* Make sure the passwd file is open in case we chroot right away,
  997.    * so we can still access it.
  998.    */
  999.   auth_setpwent(cmd->tmp_pool);
  1000.   auth_setgrent(cmd->tmp_pool);
  1001.   if(nopass)
  1002.     add_response(R_331,"Anonymous login ok, send your complete e-mail address as password.");
  1003.   else
  1004.     add_response(R_331,"Password required for %s.",cmd->argv[1]);
  1005.   session.gids = NULL;
  1006.   session.groups = NULL;
  1007.   session.user = NULL;
  1008.   session.group = NULL;
  1009.   return HANDLED(cmd);
  1010. }
  1011. MODRET cmd_pass(cmd_rec *cmd)
  1012. {
  1013.   char *display = NULL;
  1014.   char *user,*grantmsg;
  1015.   int res = 0;
  1016. #if 0
  1017.   if(cmd->argc < 2)
  1018.     return ERROR_MSG(cmd,R_500,"'PASS': command requires a parameter.");
  1019. #endif
  1020.   if(logged_in)
  1021.     return ERROR_MSG(cmd,R_503,"You are already logged in!");
  1022.   user = (char*)get_param_ptr(cmd->server->conf,C_USER,FALSE);
  1023.   if(!user)
  1024.     return ERROR_MSG(cmd,R_503,"Login with USER first.");
  1025.   
  1026.   if((res = _setup_environment(cmd->tmp_pool,user,cmd->arg)) == 1) {
  1027.     add_config_param_set(&cmd->server->conf,"authenticated",1,(void*)1);
  1028.     set_auth_check(NULL);
  1029.     if(session.flags & SF_ANON)
  1030.       display = (char*)get_param_ptr(session.anon_config->subset,
  1031.                                      "DisplayLogin",FALSE);
  1032.     if(!display)
  1033.       display = (char*)get_param_ptr(cmd->server->conf,
  1034.                                      "DisplayLogin",FALSE);
  1035.     if(display)
  1036.       core_display_file(R_230,display);
  1037.     if((grantmsg = 
  1038.         (char*)get_param_ptr((session.anon_config ? session.anon_config->subset :
  1039.                               cmd->server->conf),"AccessGrantMsg",FALSE)) != NULL) {
  1040.       grantmsg = sreplace(cmd->tmp_pool,grantmsg,"%u",user,NULL);
  1041.       add_response(R_230,"%s",grantmsg,NULL);
  1042.     } else {
  1043.       if(session.flags & SF_ANON)
  1044.         add_response(R_230,"Anonymous access granted, restrictions apply.");
  1045.       else
  1046.         add_response(R_230,"User %s logged in.",user);
  1047.     }
  1048.     logged_in = 1;
  1049.     return HANDLED(cmd);
  1050.   }
  1051.   remove_config(cmd->server->conf,C_USER,FALSE);
  1052.   if(res == 0) {
  1053.     int max;
  1054.     max = get_param_int(main_server->conf,"MaxLoginAttempts",FALSE);
  1055.     if(max == -1)
  1056.       max = 3;
  1057.     if(++auth_tries >= max) {
  1058.       send_response(R_530,"Login incorrect");
  1059.       log_auth(LOG_NOTICE,"Maximum login attempts exceeded from %s [%s] to %s:%i",
  1060.                session.c->remote_name,
  1061.                inet_ascii(cmd->tmp_pool,session.c->remote_ipaddr),
  1062.        inet_ascii(cmd->tmp_pool,session.c->local_ipaddr),
  1063.        session.c->local_port);
  1064.       end_login(0);
  1065.     }
  1066.     return ERROR_MSG(cmd,R_530,"Login incorrect.");
  1067.   }
  1068.   return HANDLED(cmd);
  1069. }
  1070. MODRET set_rootlogin(cmd_rec *cmd)
  1071. {
  1072.   CHECK_ARGS(cmd,1);
  1073.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL|CONF_ANON|CONF_GLOBAL);
  1074.   add_config_param("RootLogin",1,(void*)get_boolean(cmd,1));
  1075.   return HANDLED(cmd);
  1076. }
  1077. MODRET set_loginpasswordprompt(cmd_rec *cmd)
  1078. {
  1079.   config_rec *c;
  1080.   CHECK_ARGS(cmd,1);
  1081.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL|CONF_ANON|CONF_GLOBAL);
  1082.   c = add_config_param("LoginPasswordPrompt",1,(void*)get_boolean(cmd,1));
  1083.   c->flags |= CF_MERGEDOWN;
  1084.   return HANDLED(cmd);
  1085. }
  1086. MODRET add_defaultroot(cmd_rec *cmd)
  1087. {
  1088.   config_rec *c;
  1089.   char *dir,**argv;
  1090.   int argc;
  1091.   array_header *acl = NULL;
  1092.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
  1093.   if(cmd->argc < 2)
  1094.     CONF_ERROR(cmd,"syntax: DefaultRoot <directory> [<group-expression>]");
  1095.   argv = cmd->argv;
  1096.   argc = cmd->argc-2;
  1097.   dir = *++argv;
  1098.   /* dir must be / or ~
  1099.    */
  1100.   if(*dir != '/' && *dir != '~')
  1101.     CONF_ERROR(cmd,pstrcat(cmd->tmp_pool,"(",dir,") absolute pathname "
  1102.               "required.",NULL));
  1103.   if(strchr(dir,'*'))
  1104.     CONF_ERROR(cmd,pstrcat(cmd->tmp_pool,"(",dir,") wildcards not allowed "
  1105.                "in pathname.",NULL));
  1106.   if(*(dir+strlen(dir)-1) != '/')
  1107.     dir = pstrcat(cmd->tmp_pool,dir,"/",NULL);
  1108.   acl = parse_group_expression(cmd->tmp_pool,&argc,argv);
  1109.   c = add_config_param("DefaultRoot",0);
  1110.   c->argc = argc+1;
  1111.   c->argv = pcalloc(c->pool,(argc+2) * sizeof(char*));
  1112.   argv = (char**)c->argv;
  1113.   *argv++ = pstrdup(permanent_pool,dir);
  1114.   if(argc && acl)
  1115.     while(argc--) {
  1116.       *argv++ = pstrdup(permanent_pool,*((char**)acl->elts));
  1117.       acl->elts = ((char**)acl->elts) + 1;
  1118.     }
  1119.   *argv = NULL;
  1120.   return HANDLED(cmd);
  1121. }
  1122. MODRET add_defaultchdir(cmd_rec *cmd)
  1123. {
  1124.   config_rec *c;
  1125.   char *dir,**argv;
  1126.   int argc;
  1127.   array_header *acl = NULL;
  1128.   CHECK_CONF(cmd,CONF_ROOT|CONF_VIRTUAL|CONF_ANON|CONF_GLOBAL);
  1129.   if(cmd->argc < 2)
  1130.     CONF_ERROR(cmd,"syntax: DefaultChdir <directory> [<group-expression>]");
  1131.   argv = cmd->argv;
  1132.   argc = cmd->argc-2;
  1133.   dir = *++argv;
  1134.   if(strchr(dir,'*'))
  1135.     CONF_ERROR(cmd,pstrcat(cmd->tmp_pool,"(",dir,") wildcards not allowed "
  1136.                "in pathname.",NULL));
  1137.   if(*(dir+strlen(dir)-1) != '/')
  1138.     dir = pstrcat(cmd->tmp_pool,dir,"/",NULL);
  1139.   acl = parse_group_expression(cmd->tmp_pool,&argc,argv);
  1140.   c = add_config_param("DefaultChdir",0);
  1141.   c->argc = argc+1;
  1142.   c->argv = pcalloc(c->pool,(argc+2) * sizeof(char*));
  1143.   argv = (char**)c->argv;
  1144.   *argv++ = pstrdup(permanent_pool,dir);
  1145.   if(argc && acl)
  1146.     while(argc--) {
  1147.       *argv++ = pstrdup(permanent_pool,*((char**)acl->elts));
  1148.       acl->elts = ((char**)acl->elts) + 1;
  1149.     }
  1150.   *argv = NULL;
  1151.   return HANDLED(cmd);
  1152. }
  1153. MODRET add_userdirroot (cmd_rec *cmd)
  1154. {
  1155.   CHECK_ARGS(cmd,1);
  1156.   CHECK_CONF (cmd, CONF_ANON);
  1157.   add_config_param("UserDirRoot",1,(void*)get_boolean(cmd,1));
  1158.   return HANDLED(cmd);
  1159. }
  1160. static conftable auth_config[] = {
  1161.   { "RootLogin", set_rootlogin, NULL },
  1162.   { "LoginPasswordPrompt", set_loginpasswordprompt, NULL },
  1163.   { "DefaultRoot", add_defaultroot, NULL },
  1164.   { "DefaultChdir", add_defaultchdir, NULL },
  1165.   { "UserDirRoot", add_userdirroot, NULL },
  1166.   { NULL, NULL, NULL }
  1167. };
  1168. cmdtable auth_commands[] = {
  1169.   { CMD, C_USER, G_NONE, cmd_user, FALSE, FALSE, CL_AUTH },
  1170.   { CMD, C_PASS, G_NONE, cmd_pass, FALSE,  FALSE, CL_AUTH },
  1171.   { 0, NULL }
  1172. };
  1173. /* Module interface */
  1174. module auth_module = {
  1175.   NULL,NULL, /* Always NULL */
  1176.   0x20, /* API Version 2.0 */
  1177.   "auth",
  1178.   auth_config,
  1179.   auth_commands,
  1180.   NULL,
  1181.   auth_init,auth_init_child
  1182. };