Exemple #1
0
END_TEST

START_TEST (dir_realpath_test) {
  char *res;
  const char *path;

  res = dir_realpath(NULL, NULL);
  fail_unless(res == NULL, "Failed to handle null arguments");
  fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
    strerror(errno), errno);

  res = dir_realpath(p, NULL);
  fail_unless(res == NULL, "Failed to handle null path");
  fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
    strerror(errno), errno);

  mark_point();
  path = "/foo";
  res = dir_realpath(p, path);
  fail_unless(res == NULL, "Got real path for '%s' unexpectedly", path);
  fail_unless(errno == ENOENT, "Expected ENOENT (%d), got %s (%d)", ENOENT,
    strerror(errno), errno);

  mark_point();
  path = "/";
  res = dir_realpath(p, path);
  fail_unless(res != NULL, "Failed to get real path for '%s': %s", path,
    strerror(errno));
  fail_unless(strcmp(res, path) == 0, "Expected '%s', got '%s'", path, res);
}
Exemple #2
0
/* Determine logging-in user's access table locations.  This function was
 * "borrowed" (ie plagiarized/copied/whatever) liberally from modules/
 * mod_auth.c -- the _true_ author is MacGuyver <*****@*****.**>.
 */
static char *wrap_get_user_table(cmd_rec *cmd, char *user,
    char *path) {

  char *real_path = NULL;
  struct passwd *pw = NULL;

  pw = pr_auth_getpwnam(cmd->pool, user);

  /* Handle the case where the given user does not exist. */
  if (pw == NULL) {
    return NULL;
  }

  /* For the dir_realpath() function to work, some session members need to
   * be set.
   */
  session.user = pstrdup(cmd->pool, pw->pw_name);
  session.login_uid = pw->pw_uid;

  PRIVS_USER
  real_path = dir_realpath(cmd->pool, path);
  PRIVS_RELINQUISH

  if (real_path)
    path = real_path;

  return path;
}
Exemple #3
0
static wrap2_table_t *filetab_open_cb(pool *parent_pool, const char *srcinfo) {
  struct stat st;
  wrap2_table_t *tab = NULL;
  pool *tab_pool = make_sub_pool(parent_pool);

  /* Do not allow relative paths. */
  if (*srcinfo != '/' &&
      *srcinfo != '~') {
    wrap2_log("error: table relative paths are forbidden: '%s'", srcinfo);
    destroy_pool(tab_pool);
    errno = EINVAL;
    return NULL;
  }

  /* If the path starts with a tilde, expand it out. */
  if (srcinfo[0] == '~' &&
      srcinfo[1] == '/') {
    char *path = NULL;

    PRIVS_USER
    path = dir_realpath(tab_pool, srcinfo);
    PRIVS_RELINQUISH

    if (path) {
      srcinfo = path;
      wrap2_log("resolved tilde: path now '%s'", srcinfo);
    }
  }
Exemple #4
0
MODRET wrap_handle_request(cmd_rec *cmd) {

  /* these variables are names expected to be set by the TCP wrapper code
   */
  struct request_info request;

  char *user = NULL;
  config_rec *conf = NULL, *access_conf = NULL, *syslog_conf = NULL;
  hosts_allow_table = NULL;
  hosts_deny_table = NULL;

  /* hide passwords */
  session.hide_password = TRUE;

  /* Sneaky...found in mod_auth.c's cmd_pass() function.  Need to find the
   * login UID in order to resolve the possibly-login-dependent filename.
   */
  user = (char *) get_param_ptr(cmd->server->conf, C_USER, FALSE);

  /* It's possible that a PASS command came before USER.  This is a PRE_CMD
   * handler, so it won't be protected from this case; we'll need to do
   * it manually.
   */
  if (!user)
    return DECLINED(cmd);

  /* Use mod_auth's _auth_resolve_user() [imported for use here] to get the
   * right configuration set, since the user may be logging in anonymously,
   * and the session struct hasn't yet been set for that yet (thus short-
   * circuiting the easiest way to get the right context...the macros.
   */
  conf = wrap_resolve_user(cmd->pool, &user);

  /* Search first for user-specific access files.  Multiple TCPUserAccessFiles
   * directives are allowed.
   */
  if ((access_conf = find_config(conf ? conf->subset : CURRENT_CONF, CONF_PARAM,
      "TCPUserAccessFiles", FALSE)) != NULL) {
    int matched = FALSE;
    array_header *user_array = NULL;

    while (access_conf) {

      user_array = make_array(cmd->tmp_pool, 0, sizeof(char *));
      *((char **) push_array(user_array)) = pstrdup(cmd->tmp_pool, user);

      /* Check the user expression -- don't forget the offset, to skip
       * the access file name strings in argv
       */
      if (wrap_eval_expression(((char **) access_conf->argv) + 2,
          user_array)) {
        pr_log_debug(DEBUG4, MOD_WRAP_VERSION
          ": matched TCPUserAccessFiles expression");
        matched = TRUE;
        break;
      }

      access_conf = find_config_next(access_conf, access_conf->next,
        CONF_PARAM, "TCPUserAccessFiles", FALSE);
    }

    if (!matched)
      access_conf = NULL;
  }

  /* Next, search for group-specific access files.  Multiple
   * TCPGroupAccessFiles directives are allowed.
   */ 
  if (!access_conf && (access_conf = find_config(conf ? conf->subset :
        CURRENT_CONF, CONF_PARAM, "TCPGroupAccessFiles", FALSE)) != NULL) {
    unsigned char matched = FALSE;

    /* NOTE: this gid_array is only necessary until Bug#1461 is fixed */
    array_header *gid_array = make_array(cmd->pool, 0, sizeof(gid_t));

    array_header *group_array = make_array(cmd->pool, 0, sizeof(char *));

    while (access_conf) {
      if (pr_auth_getgroups(cmd->pool, user, &gid_array, &group_array) < 1) {
        pr_log_debug(DEBUG3, MOD_WRAP_VERSION
          ": no supplemental groups found for user '%s'", user);

      } else {

        /* Check the group expression -- don't forget the offset, to skip
         * the access file names strings in argv
         */
        if (wrap_eval_expression(((char **) access_conf->argv) + 2,
            group_array)) {
          pr_log_debug(DEBUG4, MOD_WRAP_VERSION
            ": matched TCPGroupAccessFiles expression");
          matched = TRUE;
          break;
        }
      }

      access_conf = find_config_next(access_conf, access_conf->next,
        CONF_PARAM, "TCPGroupAccessFiles", FALSE);
    }

    if (!matched)
      access_conf = NULL;
  }

  /* Finally for globally-applicable access files.  Only one such directive
   * is allowed.
   */
  if (!access_conf) {
    access_conf = find_config(conf ? conf->subset : CURRENT_CONF,
      CONF_PARAM, "TCPAccessFiles", FALSE);
  }

  if (access_conf) {
    hosts_allow_table = (char *) access_conf->argv[0];
    hosts_deny_table = (char *) access_conf->argv[1];
  }

  /* Now, check the retrieved filename, and see if it requires a login-time
   * file.
   */
  if (hosts_allow_table != NULL && hosts_allow_table[0] == '~' &&
      hosts_allow_table[1] == '/') {
    char *allow_real_table = NULL;

    allow_real_table = wrap_get_user_table(cmd, user, hosts_allow_table);

    if (!wrap_is_usable_file(allow_real_table)) {
      pr_log_pri(PR_LOG_WARNING, MOD_WRAP_VERSION
        ": configured TCPAllowFile %s is unusable", hosts_allow_table);
      hosts_allow_table = NULL;

    } else
      hosts_allow_table = allow_real_table;
  }

  if (hosts_deny_table != NULL && hosts_deny_table[0] == '~' &&
      hosts_deny_table[1] == '/') {
    char *deny_real_table = NULL;

    deny_real_table = dir_realpath(cmd->pool, hosts_deny_table);

    if (!wrap_is_usable_file(deny_real_table)) {
      pr_log_pri(PR_LOG_WARNING, MOD_WRAP_VERSION
        ": configured TCPDenyFile %s is unusable", hosts_deny_table);
      hosts_deny_table = NULL;

    } else 
      hosts_deny_table = deny_real_table;
  }

  /* Make sure that _both_ allow and deny TCPAccessFiles are present.
   * If not, log the missing file, and by default allow request to succeed.
   */
  if (hosts_allow_table != NULL && hosts_deny_table != NULL) {

    /* Most common case...nothing more necessary */

  } else if (hosts_allow_table == NULL && hosts_deny_table != NULL) {

    /* Log the missing file */
    pr_log_pri(PR_LOG_INFO, MOD_WRAP_VERSION ": no usable allow access file -- "
      "allowing connection");

    return DECLINED(cmd);

  } else if (hosts_allow_table != NULL && hosts_deny_table == NULL) {

    /* log the missing file */
    pr_log_pri(PR_LOG_INFO, MOD_WRAP_VERSION ": no usable deny access file -- "
      "allowing connection");

    return DECLINED(cmd);

  } else {

    /* Neither set -- assume the admin hasn't configured these directives
     * at all.
     */
    return DECLINED(cmd);
  }

  /* Log the names of the allow/deny files being used. */
  pr_log_pri(PR_LOG_DEBUG, MOD_WRAP_VERSION ": using access files: %s, %s",
    hosts_allow_table, hosts_deny_table);

  /* retrieve the user-defined syslog priorities, if any.  Fall back to the
   * defaults as seen in tcpd.h if not defined.
   */
  syslog_conf = find_config(main_server->conf, CONF_PARAM,
    "TCPAccessSyslogLevels", FALSE);

  if (syslog_conf) {
    allow_severity = (int) syslog_conf->argv[1];
    deny_severity = (int) syslog_conf->argv[2];

  } else {

    allow_severity = PR_LOG_INFO;
    deny_severity = PR_LOG_WARNING;
  }

  pr_log_debug(DEBUG4, MOD_WRAP_VERSION ": checking under service name '%s'",
    wrap_service_name);
  request_init(&request, RQ_DAEMON, wrap_service_name, RQ_FILE,
    session.c->rfd, 0);

  fromhost(&request);

  if (STR_EQ(eval_hostname(request.client), paranoid) ||
      !hosts_access(&request)) {
    char *denymsg = NULL;

    /* log the denied connection */
    wrap_log_request_denied(deny_severity, &request);

    /* check for AccessDenyMsg */
    if ((denymsg = (char *) get_param_ptr(TOPLEVEL_CONF, "AccessDenyMsg",
        FALSE)) != NULL)
      denymsg = sreplace(cmd->tmp_pool, denymsg, "%u", user, NULL);

    if (denymsg)
      return ERROR_MSG(cmd, R_530, denymsg);
    else
      return ERROR_MSG(cmd, R_530, "Access denied.");
  }

  /* If request is allowable, return DECLINED (for engine to act as if this
   * handler was never called, else ERROR (for engine to abort processing and
   * deny request.
   */
  wrap_log_request_allowed(allow_severity, &request);

  return DECLINED(cmd);
}
Exemple #5
0
MODRET add_tcpuseraccessfiles(cmd_rec *cmd) {
  int user_argc = 1;
  char **user_argv = NULL;
  array_header *user_acl = NULL;
  config_rec *c = NULL;

  /* assume use of the standard TCP wrappers installation locations */
  char *allow_filename = NULL, *deny_filename = NULL;

  CHECK_ARGS(cmd, 3);
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);

  /* use the user-given files, checking to make sure that they exist and
   * are readable.
   */
  allow_filename = cmd->argv[2];
  deny_filename = cmd->argv[3];

  /* if the filenames begin with a '~', AND this is not immediately followed
   * by a '/' (ie '~/'), expand it out for checking and storing for later
   * lookups.  If the filenames DO begin with '~/', do the expansion later,
   * after authenication.  In other words, do checking of static filenames
   * now, and checking of dynamic (user-authentication-based) filenames
   * later.
   */
  if (allow_filename[0] == '/') {

    /* it's an absolute path, so the filename will be checked as is */
    if (!wrap_is_usable_file(allow_filename))
      return ERROR_MSG(cmd, NULL, pstrcat(cmd->tmp_pool,
        cmd->argv[0], ": '", allow_filename, "' must be a usable file", NULL));

  } else if (allow_filename[0] == '~' && allow_filename[1] != '/') {
    char *allow_real_file = NULL;

    allow_real_file = dir_realpath(cmd->pool, allow_filename);

    if (allow_real_file == NULL || !wrap_is_usable_file(allow_real_file))
      return ERROR_MSG(cmd, NULL, pstrcat(cmd->tmp_pool,
        cmd->argv[0], ": '", allow_filename, "' must be a usable file", NULL));

    allow_filename = allow_real_file;

  } else if (allow_filename[0] != '~' && allow_filename[0] != '/') {

    /* no relative paths allowed */
    return ERROR_MSG(cmd, NULL, pstrcat(cmd->tmp_pool,
      cmd->argv[0], ": '", allow_filename, "' must start with \"/\" or \"~\"",
      NULL));

  } else {

    /* it's a determine-at-login-time filename -- check it later */
    ;
  }

  if (deny_filename[0] == '/') {

    /* it's an absolute path, so the filename will be checked as is */
    if (!wrap_is_usable_file(deny_filename))
      return ERROR_MSG(cmd, NULL, pstrcat(cmd->tmp_pool,
        cmd->argv[0], ": '", deny_filename, "' must be a usable file", NULL));

  } else if (deny_filename[0] == '~' && deny_filename[1] != '/') {
    char *deny_real_file = NULL;

    deny_real_file = dir_realpath(cmd->pool, deny_filename);

    if (deny_real_file == NULL || !wrap_is_usable_file(deny_real_file))
      return ERROR_MSG(cmd, NULL, pstrcat(cmd->tmp_pool,
        cmd->argv[0], ": '", deny_filename, "' must be a usable file", NULL));

    deny_filename = deny_real_file;

  } else if (deny_filename[0] != '~' && deny_filename[0] != '/') {

    /* no relative paths allowed */
    return ERROR_MSG(cmd, NULL, pstrcat(cmd->tmp_pool,
      cmd->argv[0], ": '", deny_filename, "' must start with \"/\" or \"~\"",
      NULL));

  } else {

    /* it's a determine-at-login-time filename -- check it later */
    ;
  }

  c = add_config_param_str(cmd->argv[0], 0);

  user_acl = pr_expr_create(cmd->tmp_pool, &user_argc, &cmd->argv[0]);

  /* build the desired config_rec manually */
  c->argc = user_argc + 2;
  c->argv = pcalloc(c->pool, (user_argc + 3) * sizeof(char *));
  user_argv = (char **) c->argv;

  /* the access files are the first two arguments */
  *user_argv++ = pstrdup(c->pool, allow_filename);
  *user_argv++ = pstrdup(c->pool, deny_filename);

  /* and the user names follow */
  if (user_argc && user_acl)
    while (user_argc--) {
      *user_argv++ = pstrdup(c->pool, *((char **) user_acl->elts));
      user_acl->elts = ((char **) user_acl->elts) + 1;
    }

  /* don't forget to NULL-terminate */
  *user_argv = NULL;

  c->flags |= CF_MERGEDOWN;

  /* done */
  return HANDLED(cmd);
}
Exemple #6
0
MODRET add_tcpaccessfiles(cmd_rec *cmd) {
  config_rec *c = NULL;

  /* assume use of the standard TCP wrappers installation locations */
  char *allow_filename = "/etc/hosts.allow";
  char *deny_filename = "/etc/hosts.deny";

  CHECK_ARGS(cmd, 2);
  CHECK_CONF(cmd, CONF_ROOT|CONF_ANON|CONF_VIRTUAL|CONF_GLOBAL);

  /* use the user-given files, checking to make sure that they exist and
   * are readable.
   */
  allow_filename = cmd->argv[1];
  deny_filename = cmd->argv[2];

  /* if the filenames begin with a '~', AND this is not immediately followed
   * by a '/' (ie '~/'), expand it out for checking and storing for later
   * lookups.  If the filenames DO begin with '~/', do the expansion later,
   * after authenication.  In other words, do checking of static filenames
   * now, and checking of dynamic (user-authentication-based) filenames
   * later.
   */
  if (allow_filename[0] == '/') {

    /* it's an absolute path, so the filename will be checked as is */
    if (!wrap_is_usable_file(allow_filename))
      return ERROR_MSG(cmd, NULL, pstrcat(cmd->tmp_pool,
        cmd->argv[0], ": '", allow_filename, "' must be a usable file", NULL));

  } else if (allow_filename[0] == '~' && allow_filename[1] != '/') {
    char *allow_real_file = NULL;

    allow_real_file = dir_realpath(cmd->pool, allow_filename);

    if (allow_real_file == NULL || !wrap_is_usable_file(allow_real_file))
      return ERROR_MSG(cmd, NULL, pstrcat(cmd->tmp_pool,
        cmd->argv[0], ": '", allow_filename, "' must be a usable file", NULL));

    allow_filename = allow_real_file;

  } else if (allow_filename[0] != '~' && allow_filename[0] != '/') {

    /* no relative paths allowed */
    return ERROR_MSG(cmd, NULL, pstrcat(cmd->tmp_pool,
      cmd->argv[0], ": '", allow_filename, "' must start with \"/\" or \"~\"",
      NULL));

  } else {

    /* it's a determine-at-login-time filename -- check it later */
    ;
  }

  if (deny_filename[0] == '/') {

    /* it's an absolute path, so the filename will be checked as is */
    if (!wrap_is_usable_file(deny_filename))
      return ERROR_MSG(cmd, NULL, pstrcat(cmd->tmp_pool,
        cmd->argv[0], ": '", deny_filename, "' must be a usable file", NULL));

  } else if (deny_filename[0] == '~' && deny_filename[1] != '/') {
    char *deny_real_file = NULL;

    deny_real_file = dir_realpath(cmd->pool, deny_filename);

    if (deny_real_file == NULL || !wrap_is_usable_file(deny_real_file))
      return ERROR_MSG(cmd, NULL, pstrcat(cmd->tmp_pool,
        cmd->argv[0], ": '", deny_filename, "' must be a usable file", NULL));

    deny_filename = deny_real_file;

  } else if (deny_filename[0] != '~' && deny_filename[0] != '/') {

    /* no relative paths allowed */
    return ERROR_MSG(cmd, NULL, pstrcat(cmd->tmp_pool,
      cmd->argv[0], ": '", deny_filename, "' must start with \"/\" or \"~\"",
      NULL));

  } else {

    /* it's a determine-at-login-time filename -- check it later */
    ;
  }

  c = add_config_param_str(cmd->argv[0], 2, (void *) allow_filename,
    (void *) deny_filename);
  c->flags |= CF_MERGEDOWN;

  /* done */
  return HANDLED(cmd);
}
Exemple #7
0
MODRET site_chgrp(cmd_rec *cmd) {
  gid_t gid;
  char *path = NULL, *tmp = NULL, *arg = "";
  register unsigned int i = 0;
#if defined(HAVE_REGEX_H) && defined(HAVE_REGCOMP)
  regex_t *preg;
#endif

  if (cmd->argc < 3) {
    pr_response_add_err(R_500, _("'SITE %s' not understood"),
      _get_full_cmd(cmd));
    return NULL;
  }

  /* Construct the target file name by concatenating all the parameters after
   * the mode, separating them with spaces.
   */
  for (i = 2; i <= cmd->argc-1; i++)
    arg = pstrcat(cmd->tmp_pool, arg, *arg ? " " : "",
      pr_fs_decode_path(cmd->tmp_pool, cmd->argv[i]), NULL);

#if defined(HAVE_REGEX_H) && defined(HAVE_REGCOMP)
  preg = (regex_t *) get_param_ptr(CURRENT_CONF, "PathAllowFilter", FALSE);

  if (preg &&
      regexec(preg, arg, 0, NULL, 0) != 0) {
    pr_log_debug(DEBUG2, "'%s %s' denied by PathAllowFilter", cmd->argv[0],
      arg);
    pr_response_add_err(R_550, _("%s: Forbidden filename"), cmd->arg);
    return PR_ERROR(cmd);
  }

  preg = (regex_t *) get_param_ptr(CURRENT_CONF, "PathDenyFilter", FALSE);

  if (preg &&
      regexec(preg, arg, 0, NULL, 0) == 0) {
    pr_log_debug(DEBUG2, "'%s %s' denied by PathDenyFilter", cmd->argv[0],
      arg);
    pr_response_add_err(R_550, _("%s: Forbidden filename"), cmd->arg);
    return PR_ERROR(cmd);
  }
#endif

  path = dir_realpath(cmd->tmp_pool, arg);

  if (!path) {
    pr_response_add_err(R_550, "%s: %s", arg, strerror(errno));
    return PR_ERROR(cmd);
  }

  /* Map the given group argument, if a string, to a GID.  If already a
   * number, pass through as is.
   */
  gid = strtoul(cmd->argv[1], &tmp, 10);

  if (tmp && *tmp) {

    /* Try the parameter as a user name. */
    gid = pr_auth_name2gid(cmd->tmp_pool, cmd->argv[1]);
    if (gid == (gid_t) -1) {
      pr_response_add_err(R_550, "%s: %s", arg, strerror(EINVAL));
      return PR_ERROR(cmd);
    }
  }

  if (core_chgrp(cmd, path, (uid_t) -1, gid) == -1) {
    pr_response_add_err(R_550, "%s: %s", arg, strerror(errno));
    return PR_ERROR(cmd);

  } else
    pr_response_add(R_200, _("SITE %s command successful"), cmd->argv[0]);

  return PR_HANDLED(cmd);
}
Exemple #8
0
MODRET site_chmod(cmd_rec *cmd) {
  mode_t mode = 0;
  char *dir, *endp, *tmp, *arg = "";
  register unsigned int i = 0;
#if defined(HAVE_REGEX_H) && defined(HAVE_REGCOMP)
  regex_t *preg;
#endif

  if (cmd->argc < 3) {
    pr_response_add_err(R_500, _("'SITE %s' not understood"),
      _get_full_cmd(cmd));
    return NULL;
  }

  /* Construct the target file name by concatenating all the parameters after
   * the mode, separating them with spaces.
   */
  for (i = 2; i <= cmd->argc-1; i++)
    arg = pstrcat(cmd->tmp_pool, arg, *arg ? " " : "",
      pr_fs_decode_path(cmd->tmp_pool, cmd->argv[i]), NULL);

#if defined(HAVE_REGEX_H) && defined(HAVE_REGCOMP)
  preg = (regex_t *) get_param_ptr(CURRENT_CONF, "PathAllowFilter", FALSE);

  if (preg &&
      regexec(preg, arg, 0, NULL, 0) != 0) {
    pr_log_debug(DEBUG2, "'%s %s %s' denied by PathAllowFilter", cmd->argv[0],
      cmd->argv[1], arg);
    pr_response_add_err(R_550, _("%s: Forbidden filename"), cmd->arg);
    return PR_ERROR(cmd);
  }

  preg = (regex_t *) get_param_ptr(CURRENT_CONF, "PathDenyFilter", FALSE);

  if (preg &&
      regexec(preg, arg, 0, NULL, 0) == 0) {
    pr_log_debug(DEBUG2, "'%s %s %s' denied by PathDenyFilter", cmd->argv[0],
      cmd->argv[1], arg);
    pr_response_add_err(R_550, _("%s: Forbidden filename"), cmd->arg);
    return PR_ERROR(cmd);
  }
#endif

  dir = dir_realpath(cmd->tmp_pool, arg);

  if (!dir) {
    pr_response_add_err(R_550, "%s: %s", arg, strerror(errno));
    return PR_ERROR(cmd);
  }

  /* If the first character isn't '0', prepend it and attempt conversion.
   * This will fail if the chmod is a symbolic, but takes care of the
   * case where an octal number is sent without the leading '0'.
   */

  if (cmd->argv[1][0] != '0')
    tmp = pstrcat(cmd->tmp_pool, "0", cmd->argv[1], NULL);
  else
    tmp = cmd->argv[1];

  mode = strtol(tmp,&endp,0);
  if (endp && *endp) {
    /* It's not an absolute number, try symbolic */
    char *cp = cmd->argv[1];
    int mask = 0, mode_op = 0, curmode = 0, curumask = umask(0);
    int invalid = 0;
    char *who, *how, *what;
    struct stat st;

    umask(curumask);
    mode = 0;

    if (pr_fsio_stat(dir, &st) != -1)
      curmode = st.st_mode;

    while (TRUE) {
      who = pstrdup(cmd->tmp_pool, cp);

      tmp = strpbrk(who, "+-=");
      if (tmp != NULL) {
        how = pstrdup(cmd->tmp_pool, tmp);
        if (*how != '=')
          mode = curmode;

        *tmp = '\0';

      } else {
        invalid++;
        break;
      }

      tmp = strpbrk(how, "rwxXstugo");
      if (tmp != NULL) {
        what = pstrdup(cmd->tmp_pool, tmp);
        *tmp = '\0';

      } else {
        invalid++;
        break;
      }

      cp = what;
      while (cp) {
        switch (*who) {
          case 'u':
            mask = 0077;
            break;

          case 'g':
            mask = 0707;
            break;

          case 'o':
            mask = 0770;
            break;

          case 'a':
            mask = 0000;
            break;

          case '\0':
            mask = curumask;
            break;

          default:
            invalid++;
            break;
        }

        if (invalid)
          break;

        switch (*how) {
          case '+':
          case '-':
          case '=':
            break;

          default:
            invalid++;
        }

        if (invalid)
          break;

        switch (*cp) {
          case 'r':
            mode_op |= (S_IRUSR|S_IRGRP|S_IROTH);
            break;

          case 'w':
            mode_op |= (S_IWUSR|S_IWGRP|S_IWOTH);
            break;
          case 'x':
            mode_op |= (S_IXUSR|S_IXGRP|S_IXOTH);
            break;

          /* 'X' not implemented */
          case 's':
            /* setuid */
            mode_op |= S_ISUID;
            break;

          case 't':
            /* sticky */
            mode_op |= S_ISVTX;
            break;

          case 'o':
            mode_op |= curmode & S_IRWXO;
            mode_op |= (curmode & S_IRWXO) << 3;
            mode_op |= (curmode & S_IRWXO) << 6;
            break;

          case 'g':
            mode_op |= (curmode & S_IRWXG) >> 3;
            mode_op |= curmode & S_IRWXG;
            mode_op |= (curmode & S_IRWXG) << 3;
            break;

          case 'u':
            mode_op |= (curmode & S_IRWXO) >> 6;
            mode_op |= (curmode & S_IRWXO) >> 3;
            mode_op |= curmode & S_IRWXU;
            break;

          case '\0':
            /* Apply the mode and move on */
            switch (*how) {
              case '+':
              case '=':
                mode |= (mode_op & ~mask);
                break;

              case '-':
                mode &= ~(mode_op & ~mask);
                break;
            }

            mode_op = 0;
            if (*who && *(who+1)) {
              who++;
              cp = what;
              continue;

            } else
              cp = NULL;

            break;

          default:
            invalid++;
        }

        if (invalid)
          break;

        if (cp)
          cp++;
      }
      break;
    }

    if (invalid) {
      pr_response_add_err(R_550, _("'%s': invalid mode"), cmd->argv[1]);
      return PR_ERROR(cmd);
    }
  }
Exemple #9
0
MODRET site_chgrp(cmd_rec *cmd) {
  int res;
  gid_t gid;
  char *path = NULL, *tmp = NULL, *arg = "";
  register unsigned int i = 0;
#ifdef PR_USE_REGEX
  pr_regex_t *pre;
#endif

  if (cmd->argc < 3) {
    pr_response_add_err(R_500, _("'SITE %s' not understood"),
      _get_full_cmd(cmd));
    return NULL;
  }

  /* Construct the target file name by concatenating all the parameters after
   * the mode, separating them with spaces.
   */
  for (i = 2; i <= cmd->argc-1; i++)
    arg = pstrcat(cmd->tmp_pool, arg, *arg ? " " : "",
      pr_fs_decode_path(cmd->tmp_pool, cmd->argv[i]), NULL);

#ifdef PR_USE_REGEX
  pre = get_param_ptr(CURRENT_CONF, "PathAllowFilter", FALSE);
  if (pre != NULL &&
      pr_regexp_exec(pre, arg, 0, NULL, 0, 0, 0) != 0) {
    pr_log_debug(DEBUG2, "'%s %s' denied by PathAllowFilter", cmd->argv[0],
      arg);
    pr_response_add_err(R_550, _("%s: Forbidden filename"), cmd->arg);
    return PR_ERROR(cmd);
  }

  pre = get_param_ptr(CURRENT_CONF, "PathDenyFilter", FALSE);
  if (pre != NULL &&
      pr_regexp_exec(pre, arg, 0, NULL, 0, 0, 0) == 0) {
    pr_log_debug(DEBUG2, "'%s %s' denied by PathDenyFilter", cmd->argv[0],
      arg);
    pr_response_add_err(R_550, _("%s: Forbidden filename"), cmd->arg);
    return PR_ERROR(cmd);
  }
#endif

  path = dir_realpath(cmd->tmp_pool, arg);

  if (!path) {
    pr_response_add_err(R_550, "%s: %s", arg, strerror(errno));
    return PR_ERROR(cmd);
  }

  /* Map the given group argument, if a string, to a GID.  If already a
   * number, pass through as is.
   */
  gid = strtoul(cmd->argv[1], &tmp, 10);

  if (tmp && *tmp) {

    /* Try the parameter as a group name. */
    gid = pr_auth_name2gid(cmd->tmp_pool, cmd->argv[1]);
    if (gid == (gid_t) -1) {
      pr_log_debug(DEBUG9,
        "SITE CHGRP: Unable to resolve group name '%s' to GID", cmd->argv[1]);
      pr_response_add_err(R_550, "%s: %s", arg, strerror(EINVAL));
      return PR_ERROR(cmd);
    }
  }

  res = core_chgrp(cmd, path, (uid_t) -1, gid);
  if (res < 0) {
    int xerrno = errno;

    (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %lu, GID %lu): "
      "error chown'ing '%s' to GID %lu: %s", cmd->argv[0], session.user,
      (unsigned long) session.uid, (unsigned long) session.gid,
      path, (unsigned long) gid, strerror(xerrno));

    pr_response_add_err(R_550, "%s: %s", arg, strerror(xerrno));

    errno = xerrno;
    return PR_ERROR(cmd);

  } else {
    pr_response_add(R_200, _("SITE %s command successful"), cmd->argv[0]);
  }

  return PR_HANDLED(cmd);
}
Exemple #10
0
MODRET site_chgrp(cmd_rec *cmd) {
  int res;
  gid_t gid;
  char *path = NULL, *tmp = NULL, *arg = "";
  struct stat st;
  register unsigned int i = 0;
#ifdef PR_USE_REGEX
  pr_regex_t *pre;
#endif

  if (cmd->argc < 3) {
    pr_response_add_err(R_500, _("'SITE %s' not understood"),
      _get_full_cmd(cmd));
    return NULL;
  }

  /* Construct the target file name by concatenating all the parameters after
   * the mode, separating them with spaces.
   */
  for (i = 2; i <= cmd->argc-1; i++) {
    char *decoded_path;

    decoded_path = pr_fs_decode_path2(cmd->tmp_pool, cmd->argv[i],
      FSIO_DECODE_FL_TELL_ERRORS);
    if (decoded_path == NULL) {
      int xerrno = errno;

      pr_log_debug(DEBUG8, "'%s' failed to decode properly: %s",
        (char *) cmd->argv[i], strerror(xerrno));
      pr_response_add_err(R_550,
        _("SITE %s: Illegal character sequence in command"),
        (char *) cmd->argv[1]);

      pr_cmd_set_errno(cmd, xerrno);
      errno = xerrno;
      return PR_ERROR(cmd);
    }

    arg = pstrcat(cmd->tmp_pool, arg, *arg ? " " : "", decoded_path, NULL);
  }

#ifdef PR_USE_REGEX
  pre = get_param_ptr(CURRENT_CONF, "PathAllowFilter", FALSE);
  if (pre != NULL &&
      pr_regexp_exec(pre, arg, 0, NULL, 0, 0, 0) != 0) {
    pr_log_debug(DEBUG2, "'%s %s' denied by PathAllowFilter",
      (char *) cmd->argv[0], arg);
    pr_response_add_err(R_550, _("%s: Forbidden filename"), cmd->arg);

    pr_cmd_set_errno(cmd, EPERM);
    errno = EPERM;
    return PR_ERROR(cmd);
  }

  pre = get_param_ptr(CURRENT_CONF, "PathDenyFilter", FALSE);
  if (pre != NULL &&
      pr_regexp_exec(pre, arg, 0, NULL, 0, 0, 0) == 0) {
    pr_log_debug(DEBUG2, "'%s %s' denied by PathDenyFilter",
      (char *) cmd->argv[0], arg);
    pr_response_add_err(R_550, _("%s: Forbidden filename"), cmd->arg);

    pr_cmd_set_errno(cmd, EPERM);
    errno = EPERM;
    return PR_ERROR(cmd);
  }
#endif

  if (pr_fsio_lstat(arg, &st) == 0) {
    if (S_ISLNK(st.st_mode)) {
      char link_path[PR_TUNABLE_PATH_MAX];
      int len;

      memset(link_path, '\0', sizeof(link_path));
      len = dir_readlink(cmd->tmp_pool, arg, link_path, sizeof(link_path)-1,
        PR_DIR_READLINK_FL_HANDLE_REL_PATH);
      if (len > 0) {
        link_path[len] = '\0';
        arg = pstrdup(cmd->tmp_pool, link_path);
      }
    }
  }

  path = dir_realpath(cmd->tmp_pool, arg);
  if (path == NULL) {
    int xerrno = errno;

    pr_response_add_err(R_550, "%s: %s", arg, strerror(xerrno));

    pr_cmd_set_errno(cmd, xerrno);
    errno = xerrno;
    return PR_ERROR(cmd);
  }

  /* Map the given group argument, if a string, to a GID.  If already a
   * number, pass through as is.
   */
  gid = strtoul(cmd->argv[1], &tmp, 10);

  if (tmp && *tmp) {

    /* Try the parameter as a group name. */
    gid = pr_auth_name2gid(cmd->tmp_pool, cmd->argv[1]);
    if (gid == (gid_t) -1) {
      int xerrno = EINVAL;

      pr_log_debug(DEBUG9,
        "SITE CHGRP: Unable to resolve group name '%s' to GID",
        (char *) cmd->argv[1]);
      pr_response_add_err(R_550, "%s: %s", arg, strerror(xerrno));

      pr_cmd_set_errno(cmd, xerrno);
      errno = xerrno;
      return PR_ERROR(cmd);
    }
  }

  res = core_chgrp(cmd, path, (uid_t) -1, gid);
  if (res < 0) {
    int xerrno = errno;

    (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %s, GID %s): "
      "error chown'ing '%s' to GID %s: %s", (char *) cmd->argv[0], session.user,
      pr_uid2str(cmd->tmp_pool, session.uid),
      pr_gid2str(cmd->tmp_pool, session.gid), path,
      pr_gid2str(cmd->tmp_pool, gid), strerror(xerrno));

    pr_response_add_err(R_550, "%s: %s", arg, strerror(xerrno));

    pr_cmd_set_errno(cmd, xerrno);
    errno = xerrno;
    return PR_ERROR(cmd);
  }

  pr_response_add(R_200, _("SITE %s command successful"),
    (char *) cmd->argv[0]);
  return PR_HANDLED(cmd);
}
Exemple #11
0
MODRET site_chmod(cmd_rec *cmd) {
  int res;
  mode_t mode = 0;
  char *dir, *endp, *mode_str, *tmp, *arg = "";
  struct stat st;
  register unsigned int i = 0;
#ifdef PR_USE_REGEX
  pr_regex_t *pre;
#endif

  if (cmd->argc < 3) {
    pr_response_add_err(R_500, _("'SITE %s' not understood"),
      _get_full_cmd(cmd));
    return NULL;
  }

  /* Construct the target file name by concatenating all the parameters after
   * the mode, separating them with spaces.
   */
  for (i = 2; i <= cmd->argc-1; i++) {
    char *decoded_path;

    decoded_path = pr_fs_decode_path2(cmd->tmp_pool, cmd->argv[i],
      FSIO_DECODE_FL_TELL_ERRORS);
    if (decoded_path == NULL) {
      int xerrno = errno;

      pr_log_debug(DEBUG8, "'%s' failed to decode properly: %s",
        (char *) cmd->argv[i], strerror(xerrno));
      pr_response_add_err(R_550,
        _("SITE %s: Illegal character sequence in command"),
        (char *) cmd->argv[1]);

      pr_cmd_set_errno(cmd, xerrno);
      errno = xerrno;
      return PR_ERROR(cmd);
    }

    arg = pstrcat(cmd->tmp_pool, arg, *arg ? " " : "", decoded_path, NULL);
  }

#ifdef PR_USE_REGEX
  pre = get_param_ptr(CURRENT_CONF, "PathAllowFilter", FALSE);
  if (pre != NULL &&
      pr_regexp_exec(pre, arg, 0, NULL, 0, 0, 0) != 0) {
    pr_log_debug(DEBUG2, "'%s %s %s' denied by PathAllowFilter",
      (char *) cmd->argv[0], (char *) cmd->argv[1], arg);
    pr_response_add_err(R_550, _("%s: Forbidden filename"), cmd->arg);

    pr_cmd_set_errno(cmd, EPERM);
    errno = EPERM;
    return PR_ERROR(cmd);
  }

  pre = get_param_ptr(CURRENT_CONF, "PathDenyFilter", FALSE);
  if (pre != NULL &&
      pr_regexp_exec(pre, arg, 0, NULL, 0, 0, 0) == 0) {
    pr_log_debug(DEBUG2, "'%s %s %s' denied by PathDenyFilter",
      (char *) cmd->argv[0], (char *) cmd->argv[1], arg);
    pr_response_add_err(R_550, _("%s: Forbidden filename"), cmd->arg);

    pr_cmd_set_errno(cmd, EPERM);
    errno = EPERM;
    return PR_ERROR(cmd);
  }
#endif

  if (pr_fsio_lstat(arg, &st) == 0) {
    if (S_ISLNK(st.st_mode)) {
      char link_path[PR_TUNABLE_PATH_MAX];
      int len;

      memset(link_path, '\0', sizeof(link_path));
      len = dir_readlink(cmd->tmp_pool, arg, link_path, sizeof(link_path)-1,
        PR_DIR_READLINK_FL_HANDLE_REL_PATH);
      if (len > 0) {
        link_path[len] = '\0';
        arg = pstrdup(cmd->tmp_pool, link_path);
      }
    }
  }

  dir = dir_realpath(cmd->tmp_pool, arg);
  if (dir == NULL) {
    int xerrno = errno;

    pr_response_add_err(R_550, "%s: %s", arg, strerror(xerrno));

    pr_cmd_set_errno(cmd, xerrno);
    errno = xerrno;
    return PR_ERROR(cmd);
  }

  /* If the first character isn't '0', prepend it and attempt conversion.
   * This will fail if the chmod is a symbolic, but takes care of the
   * case where an octal number is sent without the leading '0'.
   */
  mode_str = cmd->argv[1];
  if (mode_str[0] != '0') {
    tmp = pstrcat(cmd->tmp_pool, "0", mode_str, NULL);

  } else {
    tmp = mode_str;
  }

  mode = strtol(tmp, &endp, 0);
  if (endp && *endp) {
    /* It's not an absolute number, try symbolic */
    char *cp = mode_str;
    int mask = 0, mode_op = 0, curr_mode = 0, curr_umask = umask(0);
    int invalid = 0;
    char *who, *how, *what;

    umask(curr_umask);
    mode = 0;

    if (pr_fsio_stat(dir, &st) != -1) {
      curr_mode = st.st_mode;
    }

    while (TRUE) {
      pr_signals_handle();

      who = pstrdup(cmd->tmp_pool, cp);

      tmp = strpbrk(who, "+-=");
      if (tmp != NULL) {
        how = pstrdup(cmd->tmp_pool, tmp);
        if (*how != '=') {
          mode = curr_mode;
        }

        *tmp = '\0';

      } else {
        invalid++;
        break;
      }

      tmp = strpbrk(how, "rwxXstugo");
      if (tmp != NULL) {
        what = pstrdup(cmd->tmp_pool, tmp);
        *tmp = '\0';

      } else {
        invalid++;
        break;
      }

      cp = what;
      while (cp) {
        switch (*who) {
          case 'u':
            mask = 0077;
            break;

          case 'g':
            mask = 0707;
            break;

          case 'o':
            mask = 0770;
            break;

          case 'a':
            mask = 0000;
            break;

          case '\0':
            mask = curr_umask;
            break;

          default:
            invalid++;
            break;
        }

        if (invalid)
          break;

        switch (*how) {
          case '+':
          case '-':
          case '=':
            break;

          default:
            invalid++;
        }

        if (invalid)
          break;

        switch (*cp) {
          case 'r':
            mode_op |= (S_IRUSR|S_IRGRP|S_IROTH);
            break;

          case 'w':
            mode_op |= (S_IWUSR|S_IWGRP|S_IWOTH);
            break;

          case 'x':
            mode_op |= (S_IXUSR|S_IXGRP|S_IXOTH);
            break;

          /* 'X' not implemented */
          case 's':
            /* setuid */
            mode_op |= S_ISUID;
            break;

          case 't':
            /* sticky */
            mode_op |= S_ISVTX;
            break;

          case 'o':
            mode_op |= (curr_mode & S_IRWXO);
            mode_op |= ((curr_mode & S_IRWXO) << 3);
            mode_op |= ((curr_mode & S_IRWXO) << 6);
            break;

          case 'g':
            mode_op |= ((curr_mode & S_IRWXG) >> 3);
            mode_op |= (curr_mode & S_IRWXG);
            mode_op |= ((curr_mode & S_IRWXG) << 3);
            break;

          case 'u':
            mode_op |= ((curr_mode & S_IRWXU) >> 6);
            mode_op |= ((curr_mode & S_IRWXU) >> 3);
            mode_op |= (curr_mode & S_IRWXU);
            break;

          case '\0':
            /* Apply the mode and move on */
            switch (*how) {
              case '+':
              case '=':
                mode |= (mode_op & ~mask);
                break;

              case '-':
                mode &= ~(mode_op & ~mask);
                break;
            }

            mode_op = 0;
            if (*who && *(who+1)) {
              who++;
              cp = what;
              continue;

            } else {
              cp = NULL;
            }
            break;

          default:
            invalid++;
        }

        if (invalid) {
          break;
        }

        if (cp) {
          cp++;
        }
      }
      break;
    }

    if (invalid) {
      pr_response_add_err(R_550, _("'%s': invalid mode"), (char *) cmd->argv[1]);

      pr_cmd_set_errno(cmd, EINVAL);
      errno = EINVAL;
      return PR_ERROR(cmd);
    }
  }