コード例 #1
0
ファイル: mod_auth_file.c プロジェクト: jmaggard10/proftpd
static int af_setpwent(pool *p) {

  if (af_user_file != NULL) {
    if (af_user_file->af_file != NULL) {
      /* If already opened, rewind */
      rewind(af_user_file->af_file);
      return 0;

    } else {
      int xerrno;

      PRIVS_ROOT
      af_user_file->af_file = fopen(af_user_file->af_path, "r");
      xerrno = errno;
      PRIVS_RELINQUISH

      if (af_user_file->af_file == NULL) {
        struct stat st;

        if (pr_fsio_stat(af_user_file->af_path, &st) == 0) {
          pr_log_pri(PR_LOG_WARNING,
            "error: unable to open AuthUserFile file '%s' (file owned by "
            "UID %s, GID %s, perms %04o, accessed by UID %s, GID %s): %s",
            af_user_file->af_path, pr_uid2str(p, st.st_uid),
            pr_gid2str(p, st.st_gid), st.st_mode & ~S_IFMT,
            pr_uid2str(p, geteuid()), pr_gid2str(p, getegid()),
            strerror(xerrno));

        } else {
          pr_log_pri(PR_LOG_WARNING,
            "error: unable to open AuthUserFile file '%s': %s",
            af_user_file->af_path, strerror(xerrno));
        }

        errno = xerrno;
        return -1;
      }

      /* As the file may contain sensitive data, we do not want it lingering
       * around in stdio buffers.
       */
      (void) setvbuf(af_user_file->af_file, NULL, _IONBF, 0);

      if (fcntl(fileno(af_user_file->af_file), F_SETFD, FD_CLOEXEC) < 0) {
        pr_log_pri(PR_LOG_WARNING, MOD_AUTH_FILE_VERSION
          ": unable to set CLOEXEC on AuthUserFile %s (fd %d): %s",
          af_user_file->af_path, fileno(af_user_file->af_file),
          strerror(errno));
      }

      pr_log_debug(DEBUG7, MOD_AUTH_FILE_VERSION ": using passwd file '%s'",
        af_user_file->af_path);
      return 0;
    }
  }

  pr_trace_msg(trace_channel, 8, "no AuthUserFile configured");
  errno = EPERM;
  return -1;
}
コード例 #2
0
ファイル: mod_auth_file.c プロジェクト: jmaggard10/proftpd
static int af_allow_grent(pool *p, struct group *grp) {
  if (af_group_file == NULL) {
    errno = EPERM;
    return -1;
  }

  /* Check that the grent is within the ID restrictions (if present). */
  if (af_group_file->af_restricted_ids) {

    if (grp->gr_gid < af_group_file->af_min_id.gid) {
      pr_log_debug(DEBUG3, MOD_AUTH_FILE_VERSION ": skipping group '%s': "
        "GID %s below the minimum allowed (%s)", grp->gr_name,
        pr_gid2str(p, grp->gr_gid),
        pr_gid2str(p, af_group_file->af_min_id.gid));
      errno = EINVAL;
      return -1;
    }

    if (grp->gr_gid > af_group_file->af_max_id.gid) {
      pr_log_debug(DEBUG3, MOD_AUTH_FILE_VERSION ": skipping group '%s': "
        "GID %s above the maximum allowed (%s)", grp->gr_name,
        pr_gid2str(p, grp->gr_gid),
        pr_gid2str(p, af_group_file->af_max_id.gid));
      errno = EINVAL;
      return -1;
    }
  }

#ifdef PR_USE_REGEX
  /* Check if the grent has an acceptable name. */
  if (af_group_file->af_restricted_names) {
    int res;

    res = pr_regexp_exec(af_group_file->af_name_regex, grp->gr_name, 0,
      NULL, 0, 0, 0);

    if ((res != 0 && !af_group_file->af_name_regex_inverted) ||
        (res == 0 && af_group_file->af_name_regex_inverted)) {
      pr_log_debug(DEBUG3, MOD_AUTH_FILE_VERSION ": skipping group '%s': "
        "name '%s' does not meet allowed filter '%s'", grp->gr_name,
        grp->gr_name, af_group_file->af_name_filter);
      errno = EINVAL;
      return -1;
    }
  }
#endif /* regex support */

  return 0;
}
コード例 #3
0
ファイル: mkhome.c プロジェクト: proftpd/proftpd
static int copy_symlink(pool *p, const char *src_dir, const char *src_path,
    const char *dst_dir, const char *dst_path, uid_t uid, gid_t gid) {
  char *link_path = pcalloc(p, PR_TUNABLE_BUFFER_SIZE);
  int len;

  len = pr_fsio_readlink(src_path, link_path, PR_TUNABLE_BUFFER_SIZE-1);
  if (len < 0) {
    int xerrno = errno;

    pr_log_pri(PR_LOG_WARNING, "CreateHome: error reading link '%s': %s",
      src_path, strerror(xerrno));

    errno = xerrno;
    return -1;
  }
  link_path[len] = '\0';

  /* If the target of the link lies within the src path, rename that portion
   * of the link to be the corresponding part of the dst path.
   */
  if (strncmp(link_path, src_dir, strlen(src_dir)) == 0) {
    link_path = pdircat(p, dst_dir, link_path + strlen(src_dir), NULL);
  }

  if (pr_fsio_symlink(link_path, dst_path) < 0) {
    int xerrno = errno;

    pr_log_pri(PR_LOG_WARNING, "CreateHome: error symlinking '%s' to '%s': %s",
      link_path, dst_path, strerror(xerrno));

    errno = xerrno;
    return -1;
  }

  /* Make sure the new symlink has the proper ownership. */
  if (pr_fsio_chown(dst_path, uid, gid) < 0) {
    pr_log_pri(PR_LOG_WARNING, "CreateHome: error chown'ing '%s' to %s/%s: %s",
      dst_path, pr_uid2str(p, uid), pr_gid2str(p, gid), strerror(errno));
  }

  return 0; 
}
コード例 #4
0
ファイル: path.c プロジェクト: Castaglia/proftpd-mod_explain
const char *explain_path_error(pool *p, int err_errno, const char *full_path,
    int flags, mode_t mode) {
  register unsigned int i;
  array_header *components = NULL;
  const char *explained = NULL, *path = NULL, *prev_path = NULL;
  unsigned long name_max, no_trunc;

  if (p == NULL ||
      full_path == NULL) {
    errno = EINVAL;
    return NULL;
  }

  /* Try to get some of the easy cases out of the way first. */

  if (err_errno == ENAMETOOLONG) {
    unsigned long path_max;

    path_max = explain_platform_path_max(p, full_path);
    if (path_max > 0) {
      size_t path_len;

      path_len = strlen(full_path);
      if (path_len > path_max) {
        explained = describe_enametoolong_path(p, full_path, path_len,
          path_max, flags);
        return explained;
      }
    }
  }

  /* Now we need to walk the path.  Whee.
   *
   * To do this, we split the path into an array, one element per component.
   * This SHOULD make it easier to progressively construct the longer and
   * longer paths, leading up to the final component/full path, for checking
   * each fully qualified component along the way.
   */

  components = path_split(p, full_path);

  name_max = explain_platform_name_max(p, full_path);
  no_trunc = explain_platform_no_trunc(p, full_path);

  for (i = 0; i < components->nelts; i++) {
    const char **elts, *component;
    int final_component = FALSE, res, xerrno = 0;
    struct stat st;

    pr_signals_handle();

    elts = components->elts;
    component = elts[i];

    if (err_errno == ENAMETOOLONG) {
      size_t component_len;

      component_len = strlen(component);

      /* Component names can be too long only if the filesystem on which
       * the path resides does not silently truncate long names.
       */
      if (component_len > name_max &&
          no_trunc == 1) {
        explained = describe_enametoolong_name(p, component, component_len,
          name_max, flags);
        return explained;
      }
    }

    if (path == NULL) {
      path = component;

    } else {
      path = pdircat(p, path, component, NULL);
    }

    /* The following are the checks on the non-final components.  The final
     * component has different constraints.
     */

    final_component = (i == (components->nelts-1));

    res = pr_fsio_lstat(path, &st);
    xerrno = errno;

    if (res < 0) {
      pr_trace_msg(trace_channel, 3,
        "error checking component #%u (of %u), path '%s': %s", i+1,
        components->nelts, path, strerror(xerrno));
    }

    if (final_component == FALSE) {
      if (res < 0) {
        switch (xerrno) {
          case ENOENT:
            explained = describe_enoent_dir(p, path, flags);
            break;

          case EACCES:
            explained = describe_eacces_dir(p, path, flags);
            break;

          default:
            pr_trace_msg(trace_channel, 3,
              "unexplained error [%s (%d)] for directory '%s'",
              strerror(xerrno), errno, path);
        }

        break;
      }

      /* XXX What if this is a symlink? */

      if (!S_ISLNK(st.st_mode) &&
          !S_ISDIR(st.st_mode)) {
        /* Explains ENOTDIR */
        explained = pstrcat(p, "path '", path,
          "' does not refer to a directory", NULL);
        break;
      }

      /* if WANT_SEARCH and no search, EACCES */

      /* if found, and a directory, set current lookup directory, and go to
       * next component.
       */

    } else {
      /* Last component, full path, leaf file. */
      if (res < 0) {
        switch (xerrno) {
          case ENOENT:
            explained = describe_enoent_file(p, path, flags);
            break;

          case EACCES:
            explained = describe_eacces_file(p, path, flags);
            break;

          default:
            pr_trace_msg(trace_channel, 3,
              "unexplained error [%s (%d)] for file '%s'",
              strerror(xerrno), errno, path);
        }

        break;
      }

      switch (err_errno) {
        case EACCES:
          explained = describe_eacces_file(p, path, flags);
          if (explained != NULL) {
            if (pr_fsio_lstat(prev_path, &st) == 0) {
              explained = pstrcat(p, explained, "; parent directory '",
                prev_path, "' has perms ", mode2s(p, st.st_mode),
                ", and is owned by UID ", pr_uid2str(p, st.st_uid),
                ", GID ", pr_gid2str(p, st.st_gid), NULL);
            }
          }

          break;

        default:
          pr_trace_msg(trace_channel, 3,
            "unexplained error [%s (%d)] for file '%s'",
            strerror(xerrno), errno, path);
      }
    }

    prev_path = path;
  }

  return explained;
}
コード例 #5
0
ファイル: mkhome.c プロジェクト: proftpd/proftpd
/* srcdir is to be considered a "skeleton" directory, in the manner of
 * /etc/skel, and destdir is a user's newly created home directory that needs
 * to be populated with the files in srcdir.
 */
static int copy_dir(pool *p, const char *src_dir, const char *dst_dir,
    uid_t uid, gid_t gid) {
  DIR *dh = NULL;
  struct dirent *dent = NULL;

  dh = opendir(src_dir);
  if (dh == NULL) {
    int xerrno = errno;

    pr_log_pri(PR_LOG_WARNING, "CreateHome: error copying '%s' skel files: %s",
      src_dir, strerror(xerrno));

    errno = xerrno;
    return -1;
  }

  while ((dent = readdir(dh)) != NULL) {
    struct stat st;
    char *src_path, *dst_path;

    pr_signals_handle();

    /* Skip "." and ".." */
    if (strncmp(dent->d_name, ".", 2) == 0 ||
        strncmp(dent->d_name, "..", 3) == 0) {
      continue;
    }

    src_path = pdircat(p, src_dir, dent->d_name, NULL);
    dst_path = pdircat(p, dst_dir, dent->d_name, NULL);

    if (pr_fsio_lstat(src_path, &st) < 0) {
      pr_log_debug(DEBUG3, "CreateHome: unable to stat '%s' (%s), skipping",
        src_path, strerror(errno));
      continue;
    }

    /* Is this path to a directory? */
    if (S_ISDIR(st.st_mode)) {
      create_dir(dst_path, uid, gid, st.st_mode);
      copy_dir(p, src_path, dst_path, uid, gid);
      continue;

    /* Is this path to a regular file? */
    } else if (S_ISREG(st.st_mode)) {
      mode_t dst_mode = st.st_mode;

      /* Make sure to prevent S{U,G}ID permissions on target files. */

      if (dst_mode & S_ISUID) {
        dst_mode &= ~S_ISUID;
      }

      if (dst_mode & S_ISGID) {
        dst_mode &= ~S_ISGID;
      }

      (void) pr_fs_copy_file(src_path, dst_path);

      /* Make sure the destination file has the proper ownership and mode. */
      if (pr_fsio_chown(dst_path, uid, gid) < 0) {
        pr_log_pri(PR_LOG_WARNING, "CreateHome: error chown'ing '%s' "
          "to %s/%s: %s", dst_path, pr_uid2str(p, uid), pr_gid2str(p, gid),
          strerror(errno));
      }

      if (pr_fsio_chmod(dst_path, dst_mode) < 0) {
        pr_log_pri(PR_LOG_WARNING, "CreateHome: error chmod'ing '%s' to "
          "%04o: %s", dst_path, (unsigned int) dst_mode, strerror(errno));
      }

      continue;

    /* Is this path a symlink? */
    } else if (S_ISLNK(st.st_mode)) {
      copy_symlink(p, src_dir, src_path, dst_dir, dst_path, uid, gid);
      continue;

    /* All other file types are skipped */
    } else {
      pr_log_debug(DEBUG3, "CreateHome: skipping skel file '%s'", src_path);
      continue;
    }
  }

  closedir(dh);
  return 0;
}
コード例 #6
0
ファイル: mod_site.c プロジェクト: Nakor78/proftpd
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);
}