Commit message: Walk the entire DefaultRoot path, checking for symlinks of any component,
when AllowChrootSymlinks is disabled.
CVE: CVE-2017-7418
CWE: CWE-59
Vulnerability description: ProFTPD before 1.3.5e and 1.3.6 before 1.3.6rc5 controls whether the home directory of a user could contain a symbolic link through the AllowChrootSymlinks configuration option, but checks only the last path component when enforcing AllowChrootSymlinks. Attackers with local access could bypass the AllowChrootSymlinks control by replacing a path component (other than the last one) with a symbolic link. The threat model includes an attacker who is not granted full filesystem access by a hosting provider, but can reconfigure the home directory of an FTP user.

Project: proftpd
File: mod_auth.c

static void login_succeeded(pool *p, const char *user) {
#ifdef HAVE_LOGINSUCCESS
  const char *host, *sess_ttyname;
  char *msg = NULL;
  int res, xerrno;

  host = pr_netaddr_get_dnsstr(session.c->remote_addr);
  sess_ttyname = pr_session_get_ttyname(p);

  PRIVS_ROOT
  res = loginsuccess((char *) user, (char *) host, (char *) sess_ttyname, &msg);
  xerrno = errno;
  PRIVS_RELINQUISH

  if (res == 0) {
    if (msg != NULL) {
      pr_trace_msg("auth", 14, "AIX loginsuccess() report: %s", msg);
    }

  } else {
    pr_trace_msg("auth", 3, "AIX loginsuccess() error for user '%s', "
      "host '%s', tty '%s': %s", user, host, sess_ttyname, strerror(errno));
  }

  if (msg != NULL) {
    free(msg);
  }
#endif /* HAVE_LOGINSUCCESS */
}

Project: proftpd
File: mod_auth.c

static int robots_fsio_access(pr_fs_t *fs, const char *path, int mode,
    uid_t uid, gid_t gid, array_header *suppl_gids) {
  if (mode != R_OK) {
    errno = EACCES;
    return -1;
  }

  return 0;
}

Project: proftpd
File: mod_auth.c

MODRET set_maxhostclients(cmd_rec *cmd) {
  int max;
  config_rec *c = NULL;

  if (cmd->argc < 2 || cmd->argc > 3)
    CONF_ERROR(cmd, "wrong number of parameters");

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

  if (!strcasecmp(cmd->argv[1], "none"))
    max = 0;

  else {
    char *endp = NULL;

    max = (int) strtol(cmd->argv[1], &endp, 10);

    if ((endp && *endp) || max < 1)
      CONF_ERROR(cmd, "parameter must be 'none' or a number greater than 0");
  }

  if (cmd->argc == 3) {
    c = add_config_param(cmd->argv[0], 2, NULL, NULL);
    c->argv[0] = pcalloc(c->pool, sizeof(unsigned int));
    *((unsigned int *) c->argv[0]) = max;
    c->argv[1] = pstrdup(c->pool, cmd->argv[2]);

  } else {
    c = add_config_param(cmd->argv[0], 1, NULL);
    c->argv[0] = pcalloc(c->pool, sizeof(unsigned int));
    *((unsigned int *) c->argv[0]) = max;
  }

  c->flags |= CF_MERGEDOWN;

  return PR_HANDLED(cmd);
}

Project: proftpd
File: mod_auth.c

MODRET set_maxloginattempts(cmd_rec *cmd) {
  int max;
  config_rec *c = NULL;

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

  if (strcasecmp(cmd->argv[1], "none") == 0) {
    max = 0;

  } else {
    char *endp = NULL;
    max = (int) strtol(cmd->argv[1], &endp, 10);

    if ((endp && *endp) || max < 1)
      CONF_ERROR(cmd, "parameter must be 'none' or a number greater than 0");
  }

  c = add_config_param(cmd->argv[0], 1, NULL);
  c->argv[0] = pcalloc(c->pool, sizeof(unsigned int));
  *((unsigned int *) c->argv[0]) = max;

  return PR_HANDLED(cmd);
}

Project: proftpd
File: mod_auth.c

MODRET set_wtmplog(cmd_rec *cmd) {
  int use_wtmp = -1;
  config_rec *c = NULL;

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

  if (strcasecmp(cmd->argv[1], "NONE") == 0) {
    use_wtmp = FALSE;

  } else {
    use_wtmp = get_boolean(cmd, 1);
    if (use_wtmp == -1) {
      CONF_ERROR(cmd, "expected Boolean parameter");
    }
  }

  c = add_config_param(cmd->argv[0], 1, NULL);
  c->argv[0] = pcalloc(c->pool, sizeof(unsigned char));
  *((unsigned char *) c->argv[0]) = use_wtmp;
  c->flags |= CF_MERGEDOWN;

  return PR_HANDLED(cmd);
}