Пример #1
0
pr_netio_t *proxy_netio_unset(int strm_type, const char *fn) {
  pr_netio_t *netio = NULL;

  if (fn == NULL) {
    errno = EINVAL;
    return NULL;
  }

  netio = pr_get_netio(strm_type);
  if (netio != NULL) {
    const char *owner_name = "core", *typestr;

    if (netio->owner_name != NULL) {
      owner_name = netio->owner_name;
    }
    typestr = netio_strm_typestr(strm_type);

    pr_trace_msg(trace_channel, 18, "(%s) found %s %s NetIO", fn, owner_name,
      typestr);
    if (pr_unregister_netio(strm_type) < 0) {
      pr_trace_msg(trace_channel, 3,
        "(%s) error unregistering %s NetIO: %s", fn, typestr, strerror(errno));
    }
  }

  /* Regardless of whether we found a previously registered NetIO, make
   * sure to use our own NetIO, if any.
   */
  switch (strm_type) {
    case PR_NETIO_STRM_CTRL:
      if (ctrl_netio != NULL) {
        if (pr_register_netio(ctrl_netio, strm_type) < 0) {
          pr_trace_msg(trace_channel, 3,
            "(%s) error registering proxy %s NetIO: %s", fn,
            netio_strm_typestr(strm_type), strerror(errno));

        } else {
          pr_trace_msg(trace_channel, 19,
            "(%s) using proxy %s NetIO", fn, netio_strm_typestr(strm_type));
        }
      }
      break;

    case PR_NETIO_STRM_DATA:
      if (data_netio != NULL) {
        if (pr_register_netio(data_netio, strm_type) < 0) {
          pr_trace_msg(trace_channel, 3,
            "(%s) error registering proxy %s NetIO: %s", fn,
            netio_strm_typestr(strm_type), strerror(errno));

        } else {
          pr_trace_msg(trace_channel, 19,
            "(%s) using proxy %s NetIO", fn, netio_strm_typestr(strm_type));
        }
      }
      break;

    default:
      break;
  }
 
  return netio;
}
Пример #2
0
const pr_netaddr_t *proxy_ftp_xfer_prepare_passive(int policy_id, cmd_rec *cmd,
    const char *error_code, struct proxy_session *proxy_sess, int flags) {
  int res, xerrno = 0;
  cmd_rec *pasv_cmd;
  const pr_netaddr_t *remote_addr = NULL;
  pr_response_t *resp;
  unsigned int resp_nlines = 0;
  unsigned short remote_port;
  char *passive_cmd, *passive_respcode = NULL;

  if (cmd == NULL ||
      error_code == NULL ||
      proxy_sess == NULL ||
      proxy_sess->backend_ctrl_conn == NULL) {
    errno = EINVAL;
    return NULL;
  }

  /* Whether we send a PASV (and expect 227) or an EPSV (and expect 229)
   * need to depend on the policy_id.
   */
  switch (policy_id) {
    case PR_CMD_PASV_ID:
      passive_cmd = C_PASV;
      break;

    case PR_CMD_EPSV_ID:
      /* If the remote host does not mention EPSV in its features, fall back
       * to using PASV.
       */
      passive_cmd = C_EPSV;
      if (pr_table_get(proxy_sess->backend_features, C_EPSV, NULL) == NULL) {
        pr_trace_msg(trace_channel, 19,
          "EPSV not supported by backend server (via FEAT), using PASV");
        if (proxy_sess->dataxfer_policy == PR_CMD_EPSV_ID) {
          proxy_sess->dataxfer_policy = PR_CMD_PASV_ID;
        }

        passive_cmd = C_PASV;
        policy_id = PR_CMD_PASV_ID;
      }
      break;

    default:
      /* In this case, the cmd we were given is the one we should send to
       * the backend server -- but only if it is either EPSV or PASV.
       */
      if (pr_cmd_cmp(cmd, PR_CMD_EPSV_ID) != 0 &&
          pr_cmd_cmp(cmd, PR_CMD_PASV_ID) != 0) {
        pr_trace_msg(trace_channel, 9,
          "illegal FTP passive transfer command '%s'", (char *) cmd->argv[0]);
        errno = EINVAL;
        return NULL;
      }

      passive_cmd = cmd->argv[0];

      if (pr_cmd_cmp(cmd, PR_CMD_EPSV_ID) == 0) {
        /* If the remote host does not mention EPSV in its features, fall back
         * to using PASV.
         */
        if (pr_table_get(proxy_sess->backend_features, C_EPSV, NULL) == NULL) {
          pr_trace_msg(trace_channel, 19,
            "EPSV not supported by backend server (via FEAT), using PASV");
          if (proxy_sess->dataxfer_policy == PR_CMD_EPSV_ID) {
            proxy_sess->dataxfer_policy = PR_CMD_PASV_ID;
          }

          passive_cmd = C_PASV;
          policy_id = PR_CMD_PASV_ID;
        }
      }

      break;
  }

  pasv_cmd = pr_cmd_alloc(cmd->tmp_pool, 1, passive_cmd);

  switch (pr_cmd_get_id(pasv_cmd->argv[0])) {
    case PR_CMD_PASV_ID:
      passive_respcode = R_227;
      break;

    case PR_CMD_EPSV_ID:
      passive_respcode = R_229;
      break;
  } 

  res = proxy_ftp_ctrl_send_cmd(cmd->tmp_pool, proxy_sess->backend_ctrl_conn,
    pasv_cmd);
  if (res < 0) {
    xerrno = errno;
    (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
      "error sending %s to backend: %s", (char *) pasv_cmd->argv[0],
      strerror(xerrno));

    pr_response_add_err(error_code, "%s: %s", (char *) cmd->argv[0],
      strerror(xerrno));
    pr_response_flush(&resp_err_list);

    errno = xerrno;
    return NULL;
  }

  resp = proxy_ftp_ctrl_recv_resp(cmd->tmp_pool, proxy_sess->backend_ctrl_conn,
    &resp_nlines, flags);
  if (resp == NULL) {
    xerrno = errno;
    (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
      "error receiving %s response from backend: %s",
      (char *) pasv_cmd->argv[0], strerror(xerrno));

    pr_response_add_err(error_code, "%s: %s", (char *) cmd->argv[0],
      strerror(xerrno));
    pr_response_flush(&resp_err_list);

    errno = xerrno;
    return NULL;
  }

  /* We specifically expect a 227 or 229 response code here; anything else is
   * an error.  Right?
   */
  if (strncmp(resp->num, passive_respcode, 4) != 0) {
    (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
      "received response code %s, but expected %s for %s command", resp->num,
      passive_respcode, (char *) pasv_cmd->argv[0]);

    if (policy_id == PR_CMD_EPSV_ID) {
      /* If using EPSV failed, try again using PASV, and switch the
       * DataTransferPolicy (if EPSV) to be PASV, for future attempts.
       */

      if (proxy_sess->dataxfer_policy == PR_CMD_EPSV_ID) {
        pr_trace_msg(trace_channel, 15,
          "falling back from EPSV to PASV DataTransferPolicy");
        proxy_sess->dataxfer_policy = PR_CMD_PASV_ID;
      }

      return proxy_ftp_xfer_prepare_passive(PR_CMD_PASV_ID, cmd,
        error_code, proxy_sess, flags);
    }

    res = proxy_ftp_ctrl_send_resp(cmd->tmp_pool,
      proxy_sess->frontend_ctrl_conn, resp, resp_nlines);

    errno = EPERM;
    return NULL;
  }

  switch (pr_cmd_get_id(pasv_cmd->argv[0])) {
    case PR_CMD_PASV_ID:
      remote_addr = proxy_ftp_msg_parse_addr(cmd->tmp_pool, resp->msg,
        pr_netaddr_get_family(session.c->local_addr));
      break;

    case PR_CMD_EPSV_ID:
      remote_addr = proxy_ftp_msg_parse_ext_addr(cmd->tmp_pool, resp->msg,
        session.c->remote_addr, PR_CMD_EPSV_ID, NULL);
      break;
  }

  if (remote_addr == NULL) {
    xerrno = errno;

    pr_trace_msg(trace_channel, 2, "error parsing %s response '%s': %s",
      (char *) pasv_cmd->argv[0], resp->msg, strerror(xerrno));

    xerrno = EPERM;
    pr_response_add_err(error_code, "%s: %s", (char *) cmd->argv[0],
      strerror(xerrno));
    pr_response_flush(&resp_err_list);

    errno = xerrno;
    return NULL;
  }

  remote_port = ntohs(pr_netaddr_get_port(remote_addr));

  /* Make sure that the given address matches the address to which we
   * originally connected.
   */

  if (pr_netaddr_cmp(remote_addr,
      proxy_sess->backend_ctrl_conn->remote_addr) != 0) {
    (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
      "Refused %s address %s (address mismatch with %s)",
      (char *) pasv_cmd->argv[0], pr_netaddr_get_ipstr(remote_addr),
      pr_netaddr_get_ipstr(proxy_sess->backend_ctrl_conn->remote_addr));
    xerrno = EPERM;

    pr_response_add_err(error_code, "%s: %s", (char *) cmd->argv[0],
      strerror(xerrno));
    pr_response_flush(&resp_err_list);

    errno = xerrno;
    return NULL;
  }

  if (remote_port < 1024) {
    (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
      "Refused %s port %hu (below 1024)",
      (char *) pasv_cmd->argv[0], remote_port);
    xerrno = EPERM;

    pr_response_add_err(error_code, "%s: %s", (char *) cmd->argv[0],
      strerror(xerrno));
    pr_response_flush(&resp_err_list);

    errno = xerrno;
    return NULL;
  }

  return remote_addr;
}
Пример #3
0
static int sftppam_converse(int nmsgs, PR_PAM_CONST struct pam_message **msgs,
    struct pam_response **resps, void *app_data) {
  register int i = 0, j = 0;
  array_header *list;
  uint32_t recvd_count = 0;
  const char **recvd_responses = NULL;
  struct pam_response *res = NULL;

  if (nmsgs <= 0 ||
      nmsgs > PAM_MAX_NUM_MSG) {
    pr_trace_msg(trace_channel, 3, "bad number of PAM messages (%d)", nmsgs);
    return PAM_CONV_ERR;
  }

  pr_trace_msg(trace_channel, 9, "handling %d PAM %s", nmsgs,
    nmsgs == 1 ? "message" : "messages");

  /* First, send these messages to the client. */

  list = make_array(sftppam_driver.driver_pool, 1,
    sizeof(sftp_kbdint_challenge_t));

  for (i = 0; i < nmsgs; i++) {
    sftp_kbdint_challenge_t *challenge;

    /* Skip PAM_ERROR_MSG messages; we don't want to send these to the client.
     */
    if (SFTP_PAM_MSG_MEMBER(msgs, i, msg_style) == PAM_TEXT_INFO) {
      if (sftppam_opts & SFTP_PAM_OPT_NO_INFO_MSGS) {
        pr_trace_msg(trace_channel, 9,
          "skipping sending of PAM_TEXT_INFO '%s' to client",
          SFTP_PAM_MSG_MEMBER(msgs, i, msg));

      } else {
        pr_trace_msg(trace_channel, 9, "sending PAM_TEXT_INFO '%s' to client",
          SFTP_PAM_MSG_MEMBER(msgs, i, msg));

        sftp_auth_send_banner(SFTP_PAM_MSG_MEMBER(msgs, i, msg));
      }

      continue;

#ifdef PAM_RADIO_TYPE
    } else if (SFTP_PAM_MSG_MEMBER(msgs, i, msg_style) == PAM_RADIO_TYPE) {
      if (sftppam_opts & SFTP_PAM_OPT_NO_RADIO_MSGS) {
        pr_trace_msg(trace_channel, 9,
          "skipping sending of PAM_RADIO_TYPE '%s' to client",
          SFTP_PAM_MSG_MEMBER(msgs, i, msg));

      } else {
        pr_trace_msg(trace_channel, 9, "sending PAM_RADIO_TYPE '%s' to client",
          SFTP_PAM_MSG_MEMBER(msgs, i, msg));

        sftp_auth_send_banner(SFTP_PAM_MSG_MEMBER(msgs, i, msg));
      }

      continue;
#endif /* PAM_RADIO_TYPE */

    } else if (SFTP_PAM_MSG_MEMBER(msgs, i, msg_style) == PAM_ERROR_MSG) {
      (void) pr_log_writefile(sftp_logfd, MOD_SFTP_PAM_VERSION,
        "received PAM_ERROR_MSG '%s'", SFTP_PAM_MSG_MEMBER(msgs, i, msg));
      continue;
    }

    challenge = push_array(list);
    challenge->challenge = pstrdup(sftppam_driver.driver_pool,
      SFTP_PAM_MSG_MEMBER(msgs, i, msg));
    challenge->display_response = FALSE;
  }

  if (list->nelts == 0) {
    /* Nothing to see here, move along. */
    return PAM_SUCCESS;
  }

  if (sftp_kbdint_send_challenge(NULL, NULL, list->nelts, list->elts) < 0) {
    pr_trace_msg(trace_channel, 3,
      "error sending keyboard-interactive challenges: %s", strerror(errno));
    return PAM_CONV_ERR;
  }

  if (sftp_kbdint_recv_response(sftppam_driver.driver_pool, list->nelts,
      &recvd_count, &recvd_responses) < 0) {
    pr_trace_msg(trace_channel, 3,
      "error receiving keyboard-interactive responses: %s", strerror(errno));
    return PAM_CONV_ERR;
  }

  res = calloc(nmsgs, sizeof(struct pam_response));
  if (res == NULL) {
    pr_log_pri(PR_LOG_ALERT, MOD_SFTP_PAM_VERSION ": Out of memory!");
    return PAM_BUF_ERR;
  }

  for (i = 0; i < nmsgs; i++) {
    res[i].resp_retcode = 0;

    switch (SFTP_PAM_MSG_MEMBER(msgs, i, msg_style)) {
      case PAM_PROMPT_ECHO_ON:
        pr_trace_msg(trace_channel, 9,
          "received PAM_PROMPT_ECHO_ON message '%s', responding with '%s'",
          SFTP_PAM_MSG_MEMBER(msgs, i, msg), recvd_responses[i]);
        res[i].resp = strdup(recvd_responses[i]); 
        break;

      case PAM_PROMPT_ECHO_OFF:
        pr_trace_msg(trace_channel, 9,
          "received PAM_PROMPT_ECHO_OFF message '%s', responding with text",
          SFTP_PAM_MSG_MEMBER(msgs, i, msg));
        res[i].resp = strdup(recvd_responses[i]); 
        break;

      case PAM_TEXT_INFO:
        pr_trace_msg(trace_channel, 9, "received PAM_TEXT_INFO message: %s",
          SFTP_PAM_MSG_MEMBER(msgs, i, msg));
        res[i].resp = NULL;
        break;

      case PAM_ERROR_MSG:
        pr_trace_msg(trace_channel, 9, "received PAM_ERROR_MSG message: %s",
          SFTP_PAM_MSG_MEMBER(msgs, i, msg));
        res[i].resp = NULL;
        break;

#ifdef PAM_RADIO_TYPE
    case PAM_RADIO_TYPE:
        pr_trace_msg(trace_channel, 9, "received PAM_RADIO_TYPE message: %s",
          SFTP_PAM_MSG_MEMBER(msgs, i, msg));
        res[i].resp = NULL;
        break;
#endif /* PAM_RADIO_TYPE */

      default:
        pr_trace_msg(trace_channel, 3,
          "received unknown PAM message style (%d), treating it as an error",
          SFTP_PAM_MSG_MEMBER(msgs, i, msg_style));
        for (j = 0; j < nmsgs; j++) {
          if (res[i].resp != NULL) {
            free(res[i].resp);
            res[i].resp = NULL;
          }
        }

        free(res);

        return PAM_CONV_ERR;
    }
  }

  *resps = res;
  return PAM_SUCCESS;
}
Пример #4
0
int proxy_ftp_sess_get_feat(pool *p, struct proxy_session *proxy_sess) {
  pool *tmp_pool;
  int res, xerrno = 0;
  cmd_rec *cmd;
  pr_response_t *resp;
  unsigned int resp_nlines = 0;
  char *feats, *token;
  size_t token_len = 0;

  tmp_pool = make_sub_pool(p);

  cmd = pr_cmd_alloc(tmp_pool, 1, C_FEAT);
  res = proxy_ftp_ctrl_send_cmd(tmp_pool, proxy_sess->backend_ctrl_conn, cmd);
  if (res < 0) {
    xerrno = errno;

    pr_trace_msg(trace_channel, 4,
      "error sending %s to backend: %s", (char *) cmd->argv[0],
      strerror(xerrno));
    destroy_pool(tmp_pool);

    errno = xerrno;
    return -1;
  }

  resp = proxy_ftp_ctrl_recv_resp(tmp_pool, proxy_sess->backend_ctrl_conn,
    &resp_nlines);
  if (resp == NULL) {
    xerrno = errno;

    pr_trace_msg(trace_channel, 4,
      "error receiving %s response from backend: %s", (char *) cmd->argv[0],
      strerror(xerrno));
    destroy_pool(tmp_pool);

    errno = xerrno;
    return -1;
  }

  if (resp->num[0] != '2') {
    pr_trace_msg(trace_channel, 4,
      "received unexpected %s response code %s from backend",
      (char *) cmd->argv[0], resp->num);

    /* Note: If the UseProxyProtocol ProxyOption is enabled, AND if the
     * response message mentions a "PROXY" command, we might read an
     * error response here that is NOT actually for the FEAT command we just
     * sent.
     *
     * A backend FTP server which does not understand the PROXY protocol
     * will treat it as a normal FTP command, and respond.  And that will
     * put us, the client, out of lockstep with the server, for how do we know
     * that we need to read that error response FIRST, then send another
     * command?
     */

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

  proxy_sess->backend_features = pr_table_nalloc(p, 0, 4);

  feats = resp->msg;
  token = pr_str_get_token2(&feats, (char *) feat_crlf, &token_len);
  while (token != NULL) {
    pr_signals_handle();

    if (token_len > 0) {
      /* The FEAT response lines in which we are interested all start with
       * a single space, per RFC spec.  Ignore any other lines.
       */
      if (token[0] == ' ') {
        char *key, *val, *ptr;

        /* Find the next space in the string, to delimit our key/value pairs. */
        ptr = strchr(token + 1, ' ');
        if (ptr != NULL) {
          key = pstrndup(p, token + 1, ptr - token - 1);
          val = pstrdup(p, ptr + 1);

        } else {
          key = pstrdup(p, token + 1);
          val = pstrdup(p, "");
        }

        pr_table_add(proxy_sess->backend_features, key, val, 0);
      }
    }

    feats = token + token_len + 1;
    token = pr_str_get_token2(&feats, (char *) feat_crlf, &token_len);
  }

  destroy_pool(tmp_pool);
  return 0;
}
Пример #5
0
char *pr_ident_lookup(pool *p, conn_t *c) {
  char *ret = "UNKNOWN";
  pool *tmp_pool = NULL;
  conn_t *ident_conn = NULL, *ident_io = NULL;
  char buf[256] = {'\0'}, *tok = NULL, *tmp = NULL;
  int timerno, i = 0;
  int ident_port = pr_inet_getservport(p, "ident", "tcp");

  tmp_pool = make_sub_pool(p);
  ident_timeout = 0;
  nstrm = NULL;

  if (ident_port == -1) {
    destroy_pool(tmp_pool);
    return pstrdup(p, ret);
  }

  /* Set up our timer before going any further. */
  timerno = pr_timer_add(PR_TUNABLE_TIMEOUTIDENT, -1, NULL,
    (callback_t) ident_timeout_cb, "ident lookup");
  if (timerno <= 0) {
    destroy_pool(tmp_pool);
    return pstrdup(p, ret);
  }

  ident_conn = pr_inet_create_connection(tmp_pool, NULL, -1, c->local_addr,
    INPORT_ANY, FALSE);
  pr_inet_set_nonblock(tmp_pool, ident_conn);

  i = pr_inet_connect_nowait(tmp_pool, ident_conn, c->remote_addr, ident_port);
  if (i < 0) {
    int xerrno = errno;

    pr_timer_remove(timerno, ANY_MODULE);
    pr_inet_close(tmp_pool, ident_conn);
    pr_trace_msg(trace_channel, 5, "connection to %s, port %d failed: %s",
      pr_netaddr_get_ipstr(c->remote_addr), ident_port, strerror(xerrno));

    destroy_pool(tmp_pool);

    errno = xerrno;
    return pstrdup(p, ret);
  }

  if (!i) {
    /* Not yet connected. */
    nstrm = pr_netio_open(p, PR_NETIO_STRM_OTHR, ident_conn->listen_fd,
      PR_NETIO_IO_RD);
    pr_netio_set_poll_interval(nstrm, 1);

    switch (pr_netio_poll(nstrm)) {

      /* Aborted, timed out */
      case 1: {
        if (ident_timeout) {
          pr_timer_remove(timerno, ANY_MODULE);
          pr_netio_close(nstrm);
          pr_inet_close(tmp_pool, ident_conn);

          pr_trace_msg(trace_channel, 5, "lookup timed out, returning '%s'",
            ret);
          destroy_pool(tmp_pool);
          return pstrdup(p, ret);
        }
        break;
      }

      /* Error. */
      case -1: {
        int xerrno = errno;

        pr_timer_remove(timerno, ANY_MODULE);
        pr_netio_close(nstrm);
        pr_inet_close(tmp_pool, ident_conn);

        pr_trace_msg(trace_channel, 6, "lookup failed (%s), returning '%s'",
          strerror(xerrno), ret);
        destroy_pool(tmp_pool);

        errno = xerrno;
        return pstrdup(p, ret);
      }

      /* Connected. */
      default: {
        ident_conn->mode = CM_OPEN;

        if (pr_inet_get_conn_info(ident_conn, ident_conn->listen_fd) < 0) {
          int xerrno = errno;

          pr_timer_remove(timerno, ANY_MODULE);
          pr_netio_close(nstrm);
          pr_inet_close(tmp_pool, ident_conn);

          pr_trace_msg(trace_channel, 2,
            "lookup timed out (%s), returning '%s'", strerror(xerrno), ret);
          destroy_pool(tmp_pool);

          errno = xerrno;
          return pstrdup(p, ret);
        }
        break;
      }
    }
  }

  ident_io = pr_inet_openrw(tmp_pool, ident_conn, NULL, PR_NETIO_STRM_OTHR,
    -1, -1, -1, FALSE);
  if (ident_io == NULL) {
    int xerrno = errno;

    pr_timer_remove(timerno, ANY_MODULE);
    pr_inet_close(tmp_pool, ident_conn);

    pr_trace_msg(trace_channel, 3, "failed opening read/write connection: %s",
      strerror(xerrno));
    destroy_pool(tmp_pool);

    errno = xerrno;
    return pstrdup(p, ret);
  }

  nstrm = ident_io->instrm;

  pr_inet_set_nonblock(tmp_pool, ident_io);
  pr_netio_set_poll_interval(ident_io->instrm, 1);
  pr_netio_set_poll_interval(ident_io->outstrm, 1);

  pr_netio_printf(ident_io->outstrm, "%d, %d\r\n", c->remote_port,
    c->local_port);

  /* If the timer fires while in netio_gets(), netio_gets() will simply return
   * either a partial string, or NULL.  This works because ident_timeout_cb
   * aborts the stream from which we are reading.  netio_set_poll_interval() is
   * used to make sure significant delays don't occur on systems that
   * automatically restart syscalls after the SIGALRM signal.
   */

  pr_trace_msg(trace_channel, 4, "reading response from remote ident server");

  if (pr_netio_gets(buf, sizeof(buf), ident_io->instrm)) {
    strip_end(buf, "\r\n");

    pr_trace_msg(trace_channel, 6, "received '%s' from remote ident server",
      buf);

    tmp = buf;
    tok = get_token(&tmp, ":");
    if (tok && (tok = get_token(&tmp, ":"))) {
      while (*tok && isspace((int) *tok)) {
        pr_signals_handle();
        tok++;
      }
      strip_end(tok, " \t");

      if (strcasecmp(tok, "ERROR") == 0) {
        if (tmp) {
          while (*tmp && isspace((int) *tmp)) {
            pr_signals_handle();
            tmp++;
          }
	  strip_end(tmp, " \t");
          if (strcasecmp(tmp, "HIDDEN-USER") == 0)
            ret = "HIDDEN-USER";
        }

      } else if (strcasecmp(tok, "USERID") == 0) {
        if (tmp && (tok = get_token(&tmp, ":"))) {
          if (tmp) {
            while (*tmp && isspace((int) *tmp)) {
              pr_signals_handle();
              tmp++;
            }
            strip_end(tmp, " \t");
            ret = tmp;
          }
        }
      }
    }
  }

  pr_timer_remove(timerno, ANY_MODULE);
  pr_inet_close(tmp_pool, ident_io);
  pr_inet_close(tmp_pool, ident_conn);
  destroy_pool(tmp_pool);

  return pstrdup(p, ret);
}
Пример #6
0
void pr_event_generate(const char *event, const void *event_data) {
  int use_cache = FALSE;
  struct event_list *evl;

  if (!event)
    return;

  /* If there are no registered callbacks, be done. */
  if (!events)
    return;

  /* If there is a cached event, see if the given event matches. */
  if (curr_event &&
      strcmp(curr_event, event) == 0)
    use_cache = TRUE;

  /* Lookup callbacks for this event. */
  for (evl = use_cache ? curr_evl : events; evl; evl = evl->next) {

    if (strcmp(evl->event, event) == 0) {  
      struct event_handler *evh;

      /* If there are no registered callbacks for this event, be done. */
      if (!evl->handlers) {
        pr_trace_msg(trace_channel, 8, "no event handlers registered for '%s'",
          event);
        return;
      }

      curr_event = event;
      curr_evl = evl;

      for (evh = use_cache ? curr_evh : evl->handlers; evh; evh = evh->next) {
        /* Make sure that if the same event is generated by the current
         * listener, the next time through we go to the next listener, rather
         * sending the same event against to the same listener (Bug#3619).
         */
        curr_evh = evh->next;

        if (evh->module) {
          pr_trace_msg(trace_channel, 8,
            "dispatching event '%s' to mod_%s (at %p)", event,
            evh->module->name, evh->cb);

        } else {
          pr_trace_msg(trace_channel, 8,
            "dispatching event '%s' to core (at %p)", event, evh->cb);
        }

        evh->cb(event_data, evh->user_data);
      }

      break;
    }
  }

  /* Clear any cached data after publishing the event to all interested
   * listeners.
   */
  curr_event = NULL;
  curr_evl = NULL;
  curr_evh = NULL;

  return;
}
Пример #7
0
int proxy_ftp_sess_send_auth_tls(pool *p, struct proxy_session *proxy_sess) {
  int uri_tls, use_tls, xerrno;
  char *auth_feat;
  array_header *auth_feats = NULL;
  pool *tmp_pool;
  cmd_rec *cmd;
  pr_response_t *resp;
  unsigned int resp_nlines = 0;

  use_tls = proxy_tls_use_tls();
  if (use_tls == PROXY_TLS_ENGINE_OFF) {
    pr_trace_msg(trace_channel, 19,
      "TLS support not enabled/desired, skipping");
    return 0;
  }

  /* Check for any per-URI scheme-based TLS requirements. */
  uri_tls = proxy_conn_get_tls(proxy_sess->dst_pconn);

  auth_feat = pr_table_get(proxy_sess->backend_features, C_AUTH, NULL);
  if (auth_feat == NULL) {
    /* Backend server does not indicate that it supports AUTH via FEAT. */

    /* If TLS is required, then fail now. */
    if (uri_tls == PROXY_TLS_ENGINE_ON ||
        use_tls == PROXY_TLS_ENGINE_ON) {
      const char *ip_str;

      ip_str = pr_netaddr_get_ipstr(proxy_sess->backend_ctrl_conn->remote_addr);

      if (uri_tls == PROXY_TLS_ENGINE_ON) {
        (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
          "backend server %s does not support AUTH TLS (see FEAT response) but "
          "URI '%.100s' requires TLS, failing connection", ip_str,
          proxy_conn_get_uri(proxy_sess->dst_pconn));

      } else if (use_tls == PROXY_TLS_ENGINE_ON) {
        (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
          "backend server %s does not support AUTH TLS (see FEAT response) but "
          "ProxyTLSEngine requires TLS, failing connection", ip_str);
      }

      errno = EPERM;
      return -1;
    }

    /* Tell the Proxy NetIO API to NOT try to use our TLS NetIO. */
    proxy_netio_use(PR_NETIO_STRM_CTRL, NULL);

    pr_trace_msg(trace_channel, 9,
      "backend server does not support AUTH TLS (via FEAT), skipping");
    return 0;
  }

  tmp_pool = make_sub_pool(p);

  /* Note: the FEAT response against IIS servers shows e.g.:
   *
   * 211-Extended features supported:
   *  LANG EN*
   *  UTF8
   *  AUTH TLS;TLS-C;SSL;TLS-P;
   *  PBSZ
   *  PROT C;P;
   *  CCC
   *  HOST
   *  SIZE
   *  MDTM
   *  REST STREAM
   * 211 END
   *
   * Note how the AUTH and PROT values are not exactly as specified
   * in RFC 4217.  This means we'll need to deal with as is.  There will
   * be other servers with other FEAT response formats, too.
   */
  if (parse_feat(tmp_pool, auth_feat, &auth_feats) > 0) {
    register unsigned int i;

    pr_trace_msg(trace_channel, 9, "parsed FEAT value '%s' into %d values",
      auth_feat, auth_feats->nelts);
    for (i = 0; i < auth_feats->nelts; i++) {
      char *val;

      val = ((char **) auth_feats->elts)[i];
      pr_trace_msg(trace_channel, 9, " %s", val);
    }
  }

  cmd = pr_cmd_alloc(tmp_pool, 2, C_AUTH, "TLS");
  cmd->arg = pstrdup(tmp_pool, "TLS");

  resp = send_recv(tmp_pool, proxy_sess->backend_ctrl_conn, cmd, &resp_nlines);
  if (resp == NULL) {
    xerrno = errno;

    proxy_netio_use(PR_NETIO_STRM_CTRL, NULL);
    destroy_pool(tmp_pool);
    errno = xerrno;
    return -1;
  }

  if (resp->num[0] != '2') {
    pr_trace_msg(trace_channel, 4,
      "received unexpected %s response code %s from backend",
      (char *) cmd->argv[0], resp->num);

    proxy_netio_use(PR_NETIO_STRM_CTRL, NULL);
    destroy_pool(tmp_pool);
    errno = EPERM;
    return -1;
  }

  destroy_pool(tmp_pool);
  return 0;
}
Пример #8
0
static int regexp_exec_pcre(pr_regex_t *pre, const char *str,
    size_t nmatches, regmatch_t *matches, int flags, unsigned long match_limit,
    unsigned long match_limit_recursion) {
  if (pre == NULL ||
      str == NULL) {
    errno = EINVAL;
    return -1;
  }

  if (pre->pcre != NULL) {
    int res;
    size_t str_len;

    str_len = strlen(str);

    /* Use the default match limits, if set and if the caller did not
     * explicitly provide limits.
     */
    if (match_limit == 0) {
      match_limit = pcre_match_limit;
    }

    if (match_limit_recursion == 0) {
      match_limit_recursion = pcre_match_limit_recursion;
    }

    if (match_limit > 0) {
      if (pre->pcre_extra == NULL) {
        pre->pcre_extra = pcalloc(pre->regex_pool, sizeof(pcre_extra));
      }

      pre->pcre_extra->flags |= PCRE_EXTRA_MATCH_LIMIT;
      pre->pcre_extra->match_limit = match_limit;
    }

    if (match_limit_recursion > 0) {
      if (pre->pcre_extra == NULL) {
        pre->pcre_extra = pcalloc(pre->regex_pool, sizeof(pcre_extra));
      }

      pre->pcre_extra->flags |= PCRE_EXTRA_MATCH_LIMIT_RECURSION;
      pre->pcre_extra->match_limit_recursion = match_limit_recursion;
    }

    pr_trace_msg(trace_channel, 9,
      "executing PCRE regex '%s' against subject '%s'",
      pr_regexp_get_pattern(pre), str);
    res = pcre_exec(pre->pcre, pre->pcre_extra, str, str_len, 0, flags,
      NULL, 0);

    if (res < 0) {
      if (pr_trace_get_level(trace_channel) >= 9) {
        const char *reason = "unknown";

        switch (res) {
          case PCRE_ERROR_NOMATCH:
            reason = "subject did not match pattern";
            break;

          case PCRE_ERROR_NULL:
            reason = "null regex or subject";
            break;

          case PCRE_ERROR_BADOPTION:
            reason = "unsupported options bit";
            break;

          case PCRE_ERROR_BADMAGIC:
            reason = "bad magic number in regex";
            break;

          case PCRE_ERROR_UNKNOWN_OPCODE:
          case PCRE_ERROR_INTERNAL:
            reason = "internal PCRE error or corrupted regex";
            break;

          case PCRE_ERROR_NOMEMORY:
            reason = "not enough memory for backreferences";
            break;

          case PCRE_ERROR_MATCHLIMIT:
            reason = "match limit reached/exceeded";
            break;

          case PCRE_ERROR_RECURSIONLIMIT:
            reason = "match limit recursion reached/exceeded";
            break;

          case PCRE_ERROR_BADUTF8:
            reason = "invalid UTF8 subject used";
            break;

          case PCRE_ERROR_PARTIAL:
            reason = "subject matched only partially; PCRE_PARTIAL flag not used";
            break;
        }

        pr_trace_msg(trace_channel, 9,
          "PCRE regex '%s' failed to match subject '%s': %s",
          pr_regexp_get_pattern(pre), str, reason);

      } else {
        pr_trace_msg(trace_channel, 9,
          "PCRE regex '%s' successfully match subject '%s'",
          pr_regexp_get_pattern(pre), str);
      }
    }

    return res;
  }

  errno = EINVAL;
  return -1;
}
Пример #9
0
const pr_netaddr_t *proxy_ftp_msg_parse_addr(pool *p, const char *msg,
    int addr_family) {
  int valid_fmt = FALSE;
  const char *ptr;
  char *addr_buf;
  unsigned int h1, h2, h3, h4, p1, p2;
  unsigned short port;
  size_t addrlen;
  pr_netaddr_t *addr;

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

  /* Have to scan the message for the encoded address/port.  Note that we may
   * see some strange formats for PASV responses from FTP servers here.
   *
   * We can't predict where the expected address/port numbers start in the
   * string, so start from the beginning.
   */
  for (ptr = msg; *ptr; ptr++) {
    if (sscanf(ptr, "%u,%u,%u,%u,%u,%u", &h1, &h2, &h3, &h4, &p1, &p2) == 6) {
      valid_fmt = TRUE;
      break;
    }
  }

  if (valid_fmt == FALSE) {
    pr_trace_msg(trace_channel, 12,
      "unable to find PORT/PASV address/port format in '%s'", msg);
    errno = EPERM;
    return NULL;
  }

  if (h1 > 255 || h2 > 255 || h3 > 255 || h4 > 255 ||
      p1 > 255 || p2 > 255 ||
      (h1|h2|h3|h4) == 0 ||
      (p1|p2) == 0) {
    pr_trace_msg(trace_channel, 9,
      "message '%s' has invalid address/port value(s)", msg);
    errno = EINVAL;
    return NULL;
  }

  /* A dotted quad address has a maximum size of 16 bytes: 4 numbers of 3 digits
   * (max), 3 periods, and 1 terminating NUL.
   */
  addrlen = 16;

#ifdef PR_USE_IPV6
  /* Allow extra room for any necessary "::ffff:" prefix, for IPv6 sessions. */
  addrlen += 7;
#endif /* PR_USE_IPV6 */

  addr_buf = pcalloc(p, addrlen); 

#ifdef PR_USE_IPV6
  if (pr_netaddr_use_ipv6()) {
    if (addr_family == AF_INET6) {
      snprintf(addr_buf, addrlen-1, "::ffff:%u.%u.%u.%u", h1, h2, h3, h4);

    } else {
      snprintf(addr_buf, addrlen-1, "%u.%u.%u.%u", h1, h2, h3, h4);
    }

  } else {
    snprintf(addr_buf, addrlen-1, "%u.%u.%u.%u", h1, h2, h3, h4);
  }
#else
  snprintf(addr_buf, addrlen-1, "%u.%u.%u.%u", h1, h2, h3, h4);
#endif /* PR_USE_IPV6 */

  /* XXX Ideally we would NOT be using session pool here, but some other
   * pool.  These objects can't be destroyed (they have no pools of their own),
   * so they will just clutter up the session pool.  Perhaps we could have
   * a pool of addrs in this API, for reusing.
   */
  addr = (pr_netaddr_t *) pr_netaddr_get_addr(session.pool, addr_buf, NULL);
  if (addr == NULL) {
    int xerrno = errno;

    pr_trace_msg(trace_channel, 7,
      "unable to resolve '%s' from message '%s': %s", addr_buf, msg,
      strerror(xerrno));

    errno = xerrno;
    return NULL;
  }

  port = (p1 << 8) + p2;
  pr_netaddr_set_port2(addr, port);

  return addr;
}
Пример #10
0
/* This function does the work of iterating through the list of registered
 * timers, checking to see if their callbacks should be invoked and whether
 * they should be removed from the registration list. Its return value is
 * the amount of time remaining on the first timer in the list.
 */
static int process_timers(int elapsed) {
  struct timer *t = NULL, *next = NULL;

  if (!recycled)
    recycled = xaset_create(timer_pool, NULL);

  if (!elapsed &&
      !recycled->xas_list) {

    if (!timers)
      return 0;

    return (timers->xas_list ? ((struct timer *) timers->xas_list)->count : 0);
  }

  /* Critical code, no interruptions please */
  if (_indispatch)
    return 0;

  pr_alarms_block();
  _indispatch++;

  if (elapsed) {
    for (t = (struct timer *) timers->xas_list; t; t = next) {
      /* If this timer has already been handled, skip */
      next = t->next;

      if (t->remove) {
        /* Move the timer onto the free_timers chain, for later reuse. */
        xaset_remove(timers, (xasetmember_t *) t);
        xaset_insert(free_timers, (xasetmember_t *) t);

      } else if ((t->count -= elapsed) <= 0) {
        /* This timer's interval has elapsed, so trigger its callback. */

        pr_trace_msg("timer", 4,
          "%ld %s for timer ID %d ('%s', for module '%s') elapsed, invoking "
          "callback (%p)", t->interval,
          t->interval != 1 ? "seconds" : "second", t->timerno,
          t->desc ? t->desc : "<unknown>",
          t->mod ? t->mod->name : "<none>", t->callback);

        if (t->callback(t->interval, t->timerno, t->interval - t->count,
            t->mod) == 0) {

          /* A return value of zero means this timer is done, and can be
           * removed.
           */
          xaset_remove(timers, (xasetmember_t *) t);
          xaset_insert(free_timers, (xasetmember_t *) t);

        } else {
          /* A non-zero return value from a timer callback signals that
           * the timer should be reused/restarted.
           */
          pr_trace_msg("timer", 6, "restarting timer ID %d ('%s'), as per "
            "callback", t->timerno, t->desc ? t->desc : "<unknown>");

          xaset_remove(timers, (xasetmember_t *) t);
          t->count = t->interval;
          xaset_insert(recycled, (xasetmember_t *) t);
        }
      }
    }
  }

  /* Put the recycled timers back into the main timer list. */
  while ((t = (struct timer *) recycled->xas_list) != NULL) {
    xaset_remove(recycled, (xasetmember_t *) t);
    xaset_insert_sort(timers, (xasetmember_t *) t, TRUE);
  }

  _indispatch--;
  pr_alarms_unblock();

  /* If no active timers remain in the list, there is no reason to set the
   * SIGALRM handle.
   */
  return (timers->xas_list ? ((struct timer *) timers->xas_list)->count : 0);
}
Пример #11
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;
}
Пример #12
0
int pr_timer_add(int seconds, int timerno, module *mod, callback_t cb,
    const char *desc) {
  struct timer *t = NULL;

  if (seconds <= 0 ||
      cb == NULL ||
      desc == NULL) {
    errno = EINVAL;
    return -1;
  }

  if (!timers)
    timers = xaset_create(timer_pool, (XASET_COMPARE) timer_cmp);

  /* Check to see that, if specified, the timerno is not already in use. */
  if (timerno >= 0) {
    for (t = (struct timer *) timers->xas_list; t; t = t->next) {
      if (t->timerno == timerno) {
        errno = EPERM;
        return -1;
      }
    }
  }

  if (!free_timers)
    free_timers = xaset_create(timer_pool, NULL);

  /* Try to use an old timer first */
  pr_alarms_block();
  t = (struct timer *) free_timers->xas_list;
  if (t != NULL) {
    xaset_remove(free_timers, (xasetmember_t *) t);

  } else {

    if (timer_pool == NULL) {
      timer_pool = make_sub_pool(permanent_pool);
      pr_pool_tag(timer_pool, "Timer Pool");
    }

    /* Must allocate a new one */
    t = palloc(timer_pool, sizeof(struct timer));
  }

  if (timerno < 0) {
    /* Dynamic timer */
    if (dynamic_timerno < PR_TIMER_DYNAMIC_TIMERNO) {
      dynamic_timerno = PR_TIMER_DYNAMIC_TIMERNO;
    }

    timerno = dynamic_timerno++;
  }

  t->timerno = timerno;
  t->count = t->interval = seconds;
  t->callback = cb;
  t->mod = mod;
  t->remove = 0;
  t->desc = desc;

  /* If called while _indispatch, add to the recycled list to prevent
   * list corruption
   */

  if (_indispatch) {
    if (!recycled)
      recycled = xaset_create(timer_pool, NULL);
    xaset_insert(recycled, (xasetmember_t *) t);

  } else {
    xaset_insert_sort(timers, (xasetmember_t *) t, TRUE);
    nalarms++;
    set_sig_alarm();

    /* The handle_alarm() function also readjusts the timers lists
     * as part of its processing, so it needs to be called when a timer
     * is added.
     */
    handle_alarm();
  }

  pr_alarms_unblock();

  pr_trace_msg("timer", 7, "added timer ID %d ('%s', for module '%s'), "
    "triggering in %ld %s", t->timerno, t->desc,
    t->mod ? t->mod->name : "[none]", t->interval,
    t->interval != 1 ? "seconds" : "second");
  return timerno;
}
Пример #13
0
int pr_timer_remove(int timerno, module *mod) {
  struct timer *t = NULL, *tnext = NULL;
  int nremoved = 0;

  /* If there are no timers currently registered, do nothing. */
  if (!timers)
    return 0;

  pr_alarms_block();

  for (t = (struct timer *) timers->xas_list; t; t = tnext) {
    tnext = t->next;

    if ((timerno < 0 || t->timerno == timerno) &&
        (mod == ANY_MODULE || t->mod == mod)) {
      nremoved++;

      if (_indispatch) {
        t->remove++;

      } else {
        xaset_remove(timers, (xasetmember_t *) t);
        xaset_insert(free_timers, (xasetmember_t *) t);
	nalarms++;

        /* The handle_alarm() function also readjusts the timers lists
         * as part of its processing, so it needs to be called when a timer
         * is removed.
         */
        handle_alarm();
      }

      pr_trace_msg("timer", 7, "removed timer ID %d ('%s', for module '%s')",
        t->timerno, t->desc, t->mod ? t->mod->name : "[none]");
    }

    /* If we are removing a specific timer, break out of the loop now.
     * Otherwise, keep removing any matching timers.
     */
    if (nremoved > 0 &&
        timerno >= 0) {
      break;
    }
  }

  pr_alarms_unblock();

  if (nremoved == 0) {
    errno = ENOENT;
    return -1;
  }

  /* If we removed a specific timer because of the given timerno, return
   * that timerno value.
   */
  if (timerno >= 0) {
    return timerno;
  }

  return nremoved;
}
Пример #14
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);
}
Пример #15
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);
}
Пример #16
0
const pr_netaddr_t *proxy_ftp_msg_parse_ext_addr(pool *p, const char *msg,
    const pr_netaddr_t *addr, int cmd_id, const char *net_proto) {
  pr_netaddr_t *res = NULL, na;
  int family = 0;
  unsigned short port = 0;
  char delim, *msg_str, *ptr;
  size_t msglen;

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

  if (cmd_id == PR_CMD_EPSV_ID) {
    /* First, find the opening '(' character. */
    ptr = strchr(msg, '(');
    if (ptr == NULL) {
      pr_trace_msg(trace_channel, 12,
        "missing starting '(' character for extended address in '%s'", msg);
      errno = EINVAL;
      return NULL;
    }

    /* Make sure that the last character is a closing ')'. */
    msglen = strlen(ptr);
    if (ptr[msglen-1] != ')') {
      pr_trace_msg(trace_channel, 12,
        "missing ending ')' character for extended address in '%s'", msg);
      errno = EINVAL;
      return NULL;
    }

    msg_str = pstrndup(p, ptr+1, msglen-2);

  } else {
    msg_str = pstrdup(p, msg);
  }

  /* Format is <d>proto<d>ip address<d>port<d> (ASCII in network order),
   * where <d> is an arbitrary delimiter character.
   */
  delim = *msg_str++;

  /* If the network protocol string (e.g. sent by client in EPSV command) is
   * null, then determine the protocol family from the address family we were
   * given.
   */

  /* XXX Hack to skip "all", e.g. "EPSV ALL" commands. */
  if (net_proto != NULL) {
    if (strncasecmp(net_proto, "all", 4) == 0) {
      net_proto = NULL;
    }
  }

  if (net_proto == NULL) {
    if (*msg_str == delim) {
      switch (pr_netaddr_get_family(addr)) {
        case AF_INET:
          family = 1;
          break;

#ifdef PR_USE_IPV6
        case AF_INET6:
          if (pr_netaddr_use_ipv6()) {
            family = 2;
            break;
          }
#endif /* PR_USE_IPV6 */
      }

    } else {
      family = atoi(msg_str);
    }

  } else {
    family = atoi(net_proto);
  }

  switch (family) {
    case 1:
      break;

#ifdef PR_USE_IPV6
    case 2:
      if (pr_netaddr_use_ipv6()) {
        break;
      }
#endif /* PR_USE_IPV6 */

    default:
      pr_trace_msg(trace_channel, 12,
        "unsupported network protocol %d", family);
      errno = EPROTOTYPE;
      return NULL;
  }

  /* Now, skip past those numeric characters that atoi() used. */
  while (PR_ISDIGIT(*msg_str)) {
    msg_str++;
  }

  /* If the next character is not the delimiter, it's a badly formatted
   * parameter.
   */
  if (*msg_str == delim) {
    msg_str++;

  } else {
    pr_trace_msg(trace_channel, 17, "rejecting badly formatted message '%s'",
      msg_str);
    errno = EPERM;
    return NULL;
  }

  pr_netaddr_clear(&na);

  /* If the next character IS the delimiter, then the address portion is
   * omitted (which is permissible).
   */
  if (*msg_str == delim) {
    pr_netaddr_set_family(&na, pr_netaddr_get_family(addr));
    pr_netaddr_set_sockaddr(&na, pr_netaddr_get_sockaddr(addr));
    msg_str++;

  } else {
    ptr = strchr(msg_str, delim);
    if (ptr == NULL) {
      /* Badly formatted message. */
      errno = EINVAL;
      return NULL;
    }

    /* Twiddle the string so that just the address portion will be processed
     * by pr_inet_pton().
     */
    *ptr = '\0';

    /* Use pr_inet_pton() to translate the address string into the address
     * value.
     */
    switch (family) {
      case 1: {
        struct sockaddr *sa = NULL;

        pr_netaddr_set_family(&na, AF_INET);
        sa = pr_netaddr_get_sockaddr(&na);
        if (sa) {
          sa->sa_family = AF_INET;
        }

        if (pr_inet_pton(AF_INET, msg_str, pr_netaddr_get_inaddr(&na)) <= 0) {
          pr_trace_msg(trace_channel, 2,
            "error converting IPv4 address '%s': %s", msg_str, strerror(errno));
          errno = EPERM;
          return NULL;
        }

        break;
      }

      case 2: {
        struct sockaddr *sa = NULL;

        pr_netaddr_set_family(&na, AF_INET6);
        sa = pr_netaddr_get_sockaddr(&na);
        if (sa) {
          sa->sa_family = AF_INET6;
        }

        if (pr_inet_pton(AF_INET6, msg_str, pr_netaddr_get_inaddr(&na)) <= 0) {
          pr_trace_msg(trace_channel, 2,
            "error converting IPv6 address '%s': %s", msg_str, strerror(errno));
          errno = EPERM;
          return NULL;
        }

        break;
      }
    }

    /* Advance past the address portion of the argument. */
    msg_str = ++ptr;
  }

  port = atoi(msg_str);

  while (PR_ISDIGIT(*msg_str)) {
    msg_str++;
  }

  /* If the next character is not the delimiter, it's a badly formatted
   * parameter.
   */
  if (*msg_str != delim) {
    pr_trace_msg(trace_channel, 17, "rejecting badly formatted message '%s'",
      msg_str);
    errno = EPERM;
    return NULL;
  }

  /* XXX Use a pool other than session.pool here, in the future. */ 
  res = pr_netaddr_dup(session.pool, &na);
  pr_netaddr_set_port(res, htons(port));

  return res;
}
Пример #17
0
int pr_event_unregister(module *m, const char *event,
    void (*cb)(const void *, void *)) {
  struct event_list *evl;
  int unregistered = FALSE;

  if (!events)
    return 0;

  pr_trace_msg(trace_channel, 3,
    "module '%s' (%p) unregistering handler for event '%s'",
    m ? m->name : "(none)", m, event ? event : "(all)");

  /* For now, simply remove the event_handler entry for this callback.  In
   * the future, add a static counter, and churn the event pool after a
   * certain number of unregistrations, so that the memory pool doesn't
   * grow unnecessarily.
   */

  for (evl = events; evl; evl = evl->next) {
    pr_signals_handle();

    if (!event || strcmp(evl->event, event) == 0) {
      struct event_handler *evh;

      /* If there are no handlers for this event, there is nothing to
       * unregister.  Skip on to the next list.
       */
      if (!evl->handlers) {
        continue;
      }

      for (evh = evl->handlers; evh;) {

        if ((m == NULL || evh->module == m) &&
            (cb == NULL || evh->cb == cb)) { 
          struct event_handler *tmp = evh->next;

          if (evh->next) {
            evh->next->prev = evh->prev;
          }

          if (evh->prev) {
            evh->prev->next = evh->next;

          } else {
            /* This is the head of the list. */
            evl->handlers = evh->next;
          }

          evh->module = NULL;
          evh = tmp;
          unregistered = TRUE;
  
        } else {
          evh = evh->next;
        }
      }
    }
  }

  /* Clear any cached data. */
  curr_event = NULL;
  curr_evl = NULL;
  curr_evh = NULL;

  if (!unregistered) {
    errno = ENOENT;
    return -1;
  }

  return 0;
}
Пример #18
0
const char *proxy_ftp_msg_fmt_ext_addr(pool *p, const pr_netaddr_t *addr,
    unsigned short port, int cmd_id, int use_masqaddr) {
  const char *addr_str;
  char delim = '|', *msg;
  int family = 0;
  size_t addr_strlen, msglen;

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

  if (use_masqaddr) {
    config_rec *c;

    /* Handle MasqueradeAddress. */
    c = find_config(main_server->conf, CONF_PARAM, "MasqueradeAddress", FALSE);
    if (c != NULL) {
      addr = c->argv[0];
    }
  }

  /* Format is <d>proto<d>ip address<d>port<d> (ASCII in network order),
   * where <d> is an arbitrary delimiter character.
   */

  switch (pr_netaddr_get_family(addr)) {
    case AF_INET:
      family = 1;
      break;

#ifdef PR_USE_IPV6
    case AF_INET6:
      family = 2;
      break;
#endif /* PR_USE_IPV6 */

    default:
      /* Unlikely to happen. */
      errno = EINVAL;
      return NULL;
  }

  addr_str = pr_netaddr_get_ipstr(addr);
  addr_strlen = strlen(addr_str);

  /* 4 delimiters, the network protocol, the IP address, the port, and a NUL. */
  msglen = (4 * 1) + addr_strlen + 6 + 1;

  msg = pcalloc(p, msglen);
  switch (cmd_id) {
    case PR_CMD_EPRT_ID:
      snprintf(msg, msglen, "%c%d%c%s%c%hu%c", delim, family, delim,
        addr_str, delim, port, delim);
      break;

    case PR_CMD_EPSV_ID:
      snprintf(msg, msglen-1, "%c%c%c%u%c", delim, delim, delim, port, delim);
      break;

    default:
      pr_trace_msg(trace_channel, 3, "invalid/unsupported command ID: %d",
        cmd_id);
      errno = EINVAL;
      return NULL;
  }

  return msg;
}
Пример #19
0
int pr_event_register(module *m, const char *event,
    void (*cb)(const void *, void *), void *user_data) {
  struct event_handler *evh;
  struct event_list *evl;
  pool *evl_pool;

  if (!event || !cb) {
    errno = EINVAL;
    return -1;
  }

  if (!event_pool) {
    event_pool = make_sub_pool(permanent_pool);
    pr_pool_tag(event_pool, "Event Pool");
  }

  pr_trace_msg(trace_channel, 3,
    "module '%s' (%p) registering handler for event '%s' (at %p)",
    m ? m->name : "(none)", m, event, cb);

  evh = pcalloc(event_pool, sizeof(struct event_handler));

  evh->module = m;
  evh->cb = cb;
  evh->user_data = user_data;

  /* Scan the currently registered lists, looking for where to add this
   * registration.
   */

  for (evl = events; evl; evl = evl->next) {
    if (strcmp(evl->event, event) == 0) {
      struct event_handler *evhi = evl->handlers;

      if (evhi) {
        /* Make sure this event handler is added to the end of the list,
         * in order to preserve module load order handling of events.
         */ 
        while (evhi) {
          if (evhi->cb == evh->cb) {
            /* Duplicate callback */
            errno = EEXIST;
            return -1;
          }

          if (evhi->next == NULL)
            break;

          evhi = evhi->next;
       }

        evh->prev = evhi;
        evhi->next = evh;

      } else
        evl->handlers = evh;

      /* All done */
      return 0;
    }
  }

  evl_pool = pr_pool_create_sz(event_pool, EVENT_POOL_SZ);
  pr_pool_tag(evl_pool, "Event listener list pool");

  evl = pcalloc(evl_pool, sizeof(struct event_list));
  evl->pool = evl_pool;
  evl->event = pstrdup(evl->pool, event);
  evl->handlers = evh; 
  evl->next = events;

  events = evl;

  /* Clear any cached data. */
  curr_event = NULL;
  curr_evl = NULL;
  curr_evh = NULL;
  
  return 0;
}
Пример #20
0
static int forward_connect(pool *p, struct proxy_session *proxy_sess,
    pr_response_t **resp, unsigned int *resp_nlines) {
  conn_t *server_conn = NULL;
  int banner_ok = TRUE, use_tls, xerrno = 0;
  pr_netaddr_t *dst_addr;
  array_header *other_addrs = NULL;

  dst_addr = proxy_sess->dst_addr;
  other_addrs = proxy_sess->other_addrs;

  server_conn = proxy_conn_get_server_conn(p, proxy_sess, dst_addr);
  if (server_conn == NULL) {
    xerrno = errno;

    if (other_addrs != NULL) {
      register unsigned int i;

      /* Try the other IP addresses for the requested name (if any) as well. */
      for (i = 0; i < other_addrs->nelts; i++) {
        dst_addr = ((pr_netaddr_t **) other_addrs->elts)[i];

        pr_trace_msg(trace_channel, 8,
          "attempting to connect to other address #%u (%s) for requested "
          "URI '%.100s'", i+1, pr_netaddr_get_ipstr(dst_addr),
          proxy_conn_get_uri(proxy_sess->dst_pconn));
        server_conn = proxy_conn_get_server_conn(p, proxy_sess, dst_addr);
        if (server_conn != NULL) {
          proxy_sess->dst_addr = dst_addr;
          break;
        }
      }
    }

    if (server_conn == NULL) {
      xerrno = errno;

      /* EINVALs lead to strange-looking error responses; change them to
       * EPERM.
       */
      if (xerrno == EINVAL) {
        xerrno = EPERM;
      }
    }

    errno = xerrno;
    return -1;
  }

  /* XXX Support/send a CLNT command of our own?  Configurable via e.g.
   * "UserAgent" string?
   */

  proxy_sess->frontend_ctrl_conn = session.c;
  proxy_sess->backend_ctrl_conn = server_conn;

  /* Read the response from the backend server. */
  *resp = proxy_ftp_ctrl_recv_resp(p, proxy_sess->backend_ctrl_conn,
    resp_nlines);
  if (*resp == NULL) {
    xerrno = errno;

    pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
      "unable to read banner from server %s:%u: %s",
      pr_netaddr_get_ipstr(proxy_sess->backend_ctrl_conn->remote_addr),
      ntohs(pr_netaddr_get_port(proxy_sess->backend_ctrl_conn->remote_addr)),
      strerror(xerrno));

    errno = EPERM;
    return -1;
  }

  if ((*resp)->num[0] != '2') {
    banner_ok = FALSE;
  }

  (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
    "received banner from backend %s:%u%s: %s %s",
    pr_netaddr_get_ipstr(proxy_sess->backend_ctrl_conn->remote_addr),
    ntohs(pr_netaddr_get_port(proxy_sess->backend_ctrl_conn->remote_addr)),
    banner_ok ? "" : ", DISCONNECTING", (*resp)->num, (*resp)->msg);

  if (banner_ok == FALSE) {
    pr_inet_close(p, proxy_sess->backend_ctrl_conn);
    proxy_sess->backend_ctrl_conn = NULL;

    errno = EPERM;
    return -1;
  }

  /* Get the features supported by the backend server */
  if (proxy_ftp_sess_get_feat(p, proxy_sess) < 0) {
    if (errno != EPERM) {
      (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
        "unable to determine features of backend server: %s", strerror(errno));
    }
  }

  use_tls = proxy_tls_use_tls();
  if (use_tls != PROXY_TLS_ENGINE_OFF) {
    if (proxy_ftp_sess_send_auth_tls(p, proxy_sess) < 0) {
      xerrno = errno;

      (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
        "error enabling TLS on control connection to backend server: %s",
        strerror(xerrno));
      pr_inet_close(p, proxy_sess->backend_ctrl_conn);
      proxy_sess->backend_ctrl_conn = NULL;

      *resp = NULL;
      errno = xerrno;
      return -1;
    }
  }

  if (proxy_netio_postopen(server_conn->instrm) < 0) {
    xerrno = errno;

    (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
      "postopen error for backend control connection input stream: %s",
      strerror(xerrno));
    proxy_inet_close(session.pool, server_conn);
    proxy_sess->backend_ctrl_conn = NULL;

    *resp = NULL;
    errno = xerrno;
    return -1;
  }

  if (proxy_netio_postopen(server_conn->outstrm) < 0) {
    xerrno = errno;

    (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
      "postopen error for backend control connection output stream: %s",
      strerror(xerrno));
    proxy_inet_close(session.pool, server_conn);
    proxy_sess->backend_ctrl_conn = NULL;

    *resp = NULL;
    errno = xerrno;
    return -1;
  }

  if (use_tls != PROXY_TLS_ENGINE_OFF) {
    if (proxy_sess_state & PROXY_SESS_STATE_BACKEND_HAS_CTRL_TLS) {
      /* NOTE: should this be a fatal error? */
      (void) proxy_ftp_sess_send_pbsz_prot(p, proxy_sess);
    }
  }

  (void) proxy_ftp_sess_send_host(p, proxy_sess);

  proxy_sess_state |= PROXY_SESS_STATE_CONNECTED;
  return 0;
}
Пример #21
0
int proxy_ftp_sess_send_pbsz_prot(pool *p, struct proxy_session *proxy_sess) {
  int use_tls;

  use_tls = proxy_tls_use_tls();
  if (use_tls == PROXY_TLS_ENGINE_OFF) {
    pr_trace_msg(trace_channel, 19,
      "TLS support not enabled/desired, skipping");
    return 0;
  }

  if (pr_table_get(proxy_sess->backend_features, C_PBSZ, NULL) != NULL) {
    int xerrno;
    pool *tmp_pool;
    cmd_rec *cmd;
    pr_response_t *resp;
    unsigned int resp_nlines = 0;

    tmp_pool = make_sub_pool(p);

    cmd = pr_cmd_alloc(tmp_pool, 2, C_PBSZ, "0");
    cmd->arg = pstrdup(tmp_pool, "0");

    resp = send_recv(tmp_pool, proxy_sess->backend_ctrl_conn, cmd,
      &resp_nlines);
    if (resp == NULL) {
      xerrno = errno;
      destroy_pool(tmp_pool);
      errno = xerrno;
      return -1;
    }

    if (resp->num[0] != '2') {
      pr_trace_msg(trace_channel, 4,
        "received unexpected %s response code %s from backend",
        (char *) cmd->argv[0], resp->num);
      destroy_pool(tmp_pool);
      errno = EPERM;
      return -1;
    }

    destroy_pool(tmp_pool);
  }

  if (pr_table_get(proxy_sess->backend_features, C_PROT, NULL) != NULL) {
    int xerrno;
    pool *tmp_pool;
    cmd_rec *cmd;
    pr_response_t *resp;
    unsigned int resp_nlines = 0;

    tmp_pool = make_sub_pool(p);

    cmd = pr_cmd_alloc(tmp_pool, 2, C_PROT, "P");
    cmd->arg = pstrdup(tmp_pool, "P");

    resp = send_recv(tmp_pool, proxy_sess->backend_ctrl_conn, cmd,
      &resp_nlines);
    if (resp == NULL) {
      xerrno = errno;
      destroy_pool(tmp_pool);
      errno = xerrno;
      return -1;
    }

    if (resp->num[0] != '2') {
      pr_trace_msg(trace_channel, 4,
        "received unexpected %s response code %s from backend",
        (char *) cmd->argv[0], resp->num);
      destroy_pool(tmp_pool);
      errno = EPERM;
      return -1;
    }

    destroy_pool(tmp_pool);
  }

  return 0;
}
Пример #22
0
static int forward_cmd_parse_dst(pool *p, const char *arg, char **name,
    struct proxy_conn **pconn) {
  const char *default_proto = NULL, *default_port = NULL, *proto = NULL,
    *port, *uri = NULL;
  char *host = NULL, *hostport = NULL, *host_ptr = NULL, *port_ptr = NULL;

  /* TODO: Revisit theses default once we start supporting other protocols. */
  default_proto = "ftp";
  default_port = "21";

  /* First, look for the optional port. */
  port_ptr = strrchr(arg, ':');
  if (port_ptr == NULL) {
    port = default_port;

  } else {
    char *tmp2 = NULL;
    long num;

    num = strtol(port_ptr+1, &tmp2, 10);

    if (tmp2 && *tmp2) {
      /* Trailing garbage found in port number. */
      (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
        "malformed port number '%s' found in USER '%s', rejecting",
        port_ptr+1, arg);
      errno = EINVAL;
      return -1;
    }

    if (num < 0 ||
        num > 65535) {
      (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
        "invalid port number %ld found in USER '%s', rejecting", num, arg);
      errno = EINVAL;
      return -1;
    }

    port = pstrdup(p, port_ptr + 1);
  }

  /* Find the required '@' delimiter. */
  host_ptr = strrchr(arg, '@');
  if (host_ptr == NULL) {
    (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
      "missing required '@' delimiter in USER '%s', rejecting", arg);
    errno = EINVAL;
    return -1;
  }

  if (port_ptr == NULL) {
    host = pstrdup(p, host_ptr + 1);

  } else {
    host = pstrndup(p, host_ptr + 1, (port_ptr - host_ptr - 1));
  }

  *name = pstrndup(p, arg, (host_ptr - arg));
  proto = default_proto;

  hostport = pstrcat(p, host, ":", port, NULL);
  if (forward_dst_filter(p, hostport) < 0) {
    return -1;
  }

  uri = pstrcat(p, proto, "://", hostport, NULL);

  /* Note: We deliberately use proxy_pool, rather than the given pool, here
   * so that the created structure (especially the pr_netaddr_t) are
   * longer-lived.
   */
  *pconn = proxy_conn_create(proxy_pool, uri);
  if (*pconn == NULL) {
    int xerrno = errno;

    pr_trace_msg(trace_channel, 1,
      "error handling URI '%.100s': %s", uri, strerror(xerrno));

    errno = xerrno;
    return -1;
  }

  return 0;
}
Пример #23
0
int erase_erase(pool *p, int fd, off_t filesz, int pattern,
    unsigned char *eraser, size_t erasersz) {
  int res;
  off_t max, pos = 0;

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

  res = erase_pattern_fill(p, pattern, eraser, erasersz);
  if (res < 0) {
    int xerrno = errno;

    pr_trace_msg(erase_channel, 6,
      "error filling buffer for '%s' pattern: %s", erase_strpattern(p, pattern),
      strerror(errno));

    errno = xerrno;
    return -1;
  }

  /* Always remember that pos is an INDEX, and filesz is a LENGTH.  Thus the
   * max position value is filesz-1.
   */
  max = filesz - 1;

  while (pos < max) {
    size_t eraser_len;
    off_t eraser_rem;

    pr_signals_handle();

    eraser_len = erasersz;
    eraser_rem = max - pos;
    if (eraser_rem < eraser_len) {
      eraser_len = (size_t) eraser_rem;
    }

    res = write_eraser(fd, eraser, eraser_len);
    if (res < 0) {
      break;
    }

    /* Note: Should this sync happen after every write, or only after
     * the entire filesz has been written?  Tunable, perhaps, for performance?
     */
    if (erase_fsync(p, fd) < 0) {
      pr_trace_msg(erase_channel, 7,
        "error flushing data to disk for fd %d: %s", fd, strerror(errno));
    }

    pos += eraser_len;

    if (pos < max) {
      res = refill_pattern(p, pattern, eraser, erasersz);
      if (res < 0) {
        break;
      }
    }
  }

  return res;
}
Пример #24
0
static void log_write(int priority, int f, char *s, int discard) {
  unsigned int max_priority = 0, *ptr = NULL;
  char serverinfo[PR_TUNABLE_BUFFER_SIZE] = {'\0'};

  memset(serverinfo, '\0', sizeof(serverinfo));

  if (main_server &&
      main_server->ServerFQDN) {
    pr_netaddr_t *remote_addr = pr_netaddr_get_sess_remote_addr();
    const char *remote_name = pr_netaddr_get_sess_remote_name();

    snprintf(serverinfo, sizeof(serverinfo)-1, "%s", main_server->ServerFQDN);
    serverinfo[sizeof(serverinfo)-1] = '\0';

    if (remote_addr && remote_name) {
      size_t serverinfo_len;

      serverinfo_len = strlen(serverinfo);

      snprintf(serverinfo + serverinfo_len,
        sizeof(serverinfo) - serverinfo_len, " (%s[%s])",
        remote_name, pr_netaddr_get_ipstr(remote_addr));

      serverinfo[sizeof(serverinfo)-1] = '\0';
    }
  }

  if (!discard &&
      (logstderr || !main_server)) {
    char buf[LOGBUFFER_SIZE] = {'\0'};
    size_t buflen, len;
    struct timeval now;
    struct tm *tm = NULL;
    unsigned long millis;

    gettimeofday(&now, NULL);
    tm = pr_localtime(NULL, (const time_t *) &(now.tv_sec));
    if (tm == NULL) {
      return;
    }

    len = strftime(buf, sizeof(buf)-1, "%Y-%m-%d %H:%M:%S", tm);
    buflen = len;
    buf[sizeof(buf)-1] = '\0';

    /* Convert microsecs to millisecs. */
    millis = now.tv_usec / 1000;

    len = snprintf(buf + buflen, sizeof(buf) - len, ",%03lu ", millis);
    buflen += len;
    buf[sizeof(buf)-1] = '\0';

    if (*serverinfo) {
      len = snprintf(buf + buflen, sizeof(buf) - buflen,
        "%s proftpd[%u] %s: %s\n", systemlog_host,
        (unsigned int) (session.pid ? session.pid : getpid()), serverinfo, s);

    } else {
      len = snprintf(buf + buflen, sizeof(buf) - buflen,
        "%s proftpd[%u]: %s\n", systemlog_host,
        (unsigned int) (session.pid ? session.pid : getpid()), s);
    }

    buflen += len;
    buf[sizeof(buf)-1] = '\0';

    pr_log_event_generate(PR_LOG_TYPE_SYSTEMLOG, STDERR_FILENO, priority,
      buf, buflen);

    fprintf(stderr, "%s", buf);
    return;
  }

  if (syslog_discard) {
    /* Only return now if we don't have any log listeners. */
    if (pr_log_event_listening(PR_LOG_TYPE_SYSLOG) <= 0 &&
        pr_log_event_listening(PR_LOG_TYPE_SYSTEMLOG) <= 0) {
      return;
    }
  }

  if (main_server != NULL) {
    ptr = get_param_ptr(main_server->conf, "SyslogLevel", FALSE);
  }

  if (ptr != NULL) {
    max_priority = *ptr;

  } else {
    /* Default SyslogLevel is NOTICE.  Note, however, that for backward
     * compatibility of debugging, if the DebugLevel is set higher
     * than DEBUG0, we will automatically ASSUME that the admin wants
     * the syslog level to be e.g. DEBUG.
     */
    max_priority = default_level;
    if (debug_level != DEBUG0) {
      max_priority = PR_LOG_DEBUG;
    }
  }

  if (priority > max_priority) {
    /* Only return now if we don't have any log listeners. */
    if (pr_log_event_listening(PR_LOG_TYPE_SYSLOG) <= 0 &&
        pr_log_event_listening(PR_LOG_TYPE_SYSTEMLOG) <= 0) {
      return;
    }
  }

  if (systemlog_fd != -1) {
    char buf[LOGBUFFER_SIZE] = {'\0'};
    size_t buflen, len;
    struct timeval now;
    struct tm *tm;
    unsigned long millis;

    gettimeofday(&now, NULL);
    tm = pr_localtime(NULL, (const time_t *) &(now.tv_sec));
    if (tm == NULL) {
      return;
    }

    len = strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", tm);
    buflen = len;
    buf[sizeof(buf) - 1] = '\0';

    /* Convert microsecs to millisecs. */
    millis = now.tv_usec / 1000;

    len = snprintf(buf + buflen, sizeof(buf) - len, ",%03lu ", millis);
    buflen += len;
    buf[sizeof(buf) - 1] = '\0';

    if (*serverinfo) {
      len = snprintf(buf + buflen, sizeof(buf) - buflen,
        "%s proftpd[%u] %s: %s\n", systemlog_host,
        (unsigned int) (session.pid ? session.pid : getpid()), serverinfo, s);

    } else {
      len = snprintf(buf + buflen, sizeof(buf) - buflen,
        "%s proftpd[%u]: %s\n", systemlog_host,
        (unsigned int) (session.pid ? session.pid : getpid()), s);
    }

    buflen += len;
    buf[sizeof(buf)-1] = '\0';

    pr_log_event_generate(PR_LOG_TYPE_SYSTEMLOG, systemlog_fd, priority,
      buf, buflen);

    /* Now we need to enforce the discard, syslog_discard and SyslogLevel
     * filtering.
     */
    if (discard) {
      return;
    }

    if (syslog_discard) {
      return;
    }

    if (priority > max_priority) {
      return;
    }

    while (write(systemlog_fd, buf, buflen) < 0) {
      if (errno == EINTR) {
        pr_signals_handle();
        continue;
      }

      break;
    }

    return;
  }

  pr_log_event_generate(PR_LOG_TYPE_SYSLOG, syslog_sockfd, priority, s,
    strlen(s));

  if (set_facility != -1) {
    f = set_facility;
  }

  if (!syslog_open) {
    syslog_sockfd = pr_openlog("proftpd", LOG_NDELAY|LOG_PID, f);
    if (syslog_sockfd < 0) {
      (void) pr_trace_msg(trace_channel, 1,
        "error opening syslog fd: %s", strerror(errno));
      return;
    }

    syslog_open = TRUE;

  } else if (f != facility) {
    /* If this message is to be sent to a different log facility than a
     * default one (or the facility configured via SyslogFacility), then
     * OR in the facility with the priority value, as per the syslog(3)
     * docs.
     */
    priority |= f;
  }

  if (*serverinfo) {
    pr_syslog(syslog_sockfd, priority, "%s - %s\n", serverinfo, s);

  } else {
    pr_syslog(syslog_sockfd, priority, "%s\n", s);
  }
}
Пример #25
0
int sftp_auth_publickey(struct ssh2_packet *pkt, cmd_rec *pass_cmd,
    const char *orig_user, const char *user, const char *service,
    unsigned char **buf, uint32_t *buflen, int *send_userauth_fail) {
  int have_signature, res;
  enum sftp_key_type_e pubkey_type;
  unsigned char *pubkey_data;
  char *pubkey_algo = NULL;
  const char *fp = NULL, *fp_algo = NULL;
  uint32_t pubkey_len;
  struct passwd *pw;

  if (pr_cmd_dispatch_phase(pass_cmd, PRE_CMD, 0) < 0) {
    (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
      "authentication request for user '%s' blocked by '%s' handler",
      orig_user, pass_cmd->argv[0]);

    pr_cmd_dispatch_phase(pass_cmd, POST_CMD_ERR, 0);
    pr_cmd_dispatch_phase(pass_cmd, LOG_CMD_ERR, 0);

    *send_userauth_fail = TRUE;
    errno = EPERM;
    return 0;
  }

  have_signature = sftp_msg_read_bool(pkt->pool, buf, buflen);

  if (sftp_interop_supports_feature(SFTP_SSH2_FEAT_HAVE_PUBKEY_ALGO)) {
    pubkey_algo = sftp_msg_read_string(pkt->pool, buf, buflen);
  }
  pubkey_len = sftp_msg_read_int(pkt->pool, buf, buflen);
  pubkey_data = sftp_msg_read_data(pkt->pool, buf, buflen, pubkey_len);

  if (pubkey_algo == NULL) {
    /* The client did not send the string identifying the public key algorithm.
     * Thus we need to extract the algorithm string from the public key data.
     */
    pubkey_algo = sftp_msg_read_string(pkt->pool, &pubkey_data, &pubkey_len);
  }

  pr_trace_msg(trace_channel, 9, "client sent '%s' public key %s",
    pubkey_algo, have_signature ? "with signature" : "without signature");

  if (strncmp(pubkey_algo, "ssh-rsa", 8) == 0) {
    pubkey_type = SFTP_KEY_RSA;

  } else if (strncmp(pubkey_algo, "ssh-dss", 8) == 0) {
    pubkey_type = SFTP_KEY_DSA;

#ifdef PR_USE_OPENSSL_ECC
  } else if (strncmp(pubkey_algo, "ecdsa-sha2-nistp256", 20) == 0) {
    pubkey_type = SFTP_KEY_ECDSA_256;

  } else if (strncmp(pubkey_algo, "ecdsa-sha2-nistp384", 20) == 0) {
    pubkey_type = SFTP_KEY_ECDSA_384;

  } else if (strncmp(pubkey_algo, "ecdsa-sha2-nistp521", 20) == 0) {
    pubkey_type = SFTP_KEY_ECDSA_521;
#endif /* PR_USE_OPENSSL_ECC */

  /* XXX This is where we would add support for X509 public keys, e.g.:
   *
   *  x509v3-ssh-dss
   *  x509v3-ssh-rsa
   *  x509v3-sign (older)
   *
   */

  } else {
    (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
      "unsupported public key algorithm '%s' requested, rejecting request",
      pubkey_algo);

    *send_userauth_fail = TRUE;
    errno = EINVAL;
    return 0;
  }

  res = sftp_keys_verify_pubkey_type(pkt->pool, pubkey_data, pubkey_len,
    pubkey_type);
  if (res != TRUE) {
    int xerrno = errno;

    (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
      "unable to verify that given public key matches given '%s' algorithm",
      pubkey_algo);

    if (res < 0) {
      (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
        "error verifying public key algorithm '%s': %s", pubkey_algo,
        strerror(xerrno));
    }

    *send_userauth_fail = TRUE;
    errno = EINVAL;
    return 0;
  }

#ifdef OPENSSL_FIPS
  if (FIPS_mode()) {
    fp = sftp_keys_get_fingerprint(pkt->pool, pubkey_data, pubkey_len,
      SFTP_KEYS_FP_DIGEST_SHA1);
    if (fp != NULL) {
      fp_algo = "SHA1";
      (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
        "public key %s fingerprint: %s", fp_algo, fp);

    } else {
      (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
        "error obtaining public key SHA1 fingerprint: %s", strerror(errno));
    }

  } else {
#endif /* OPENSSL_FIPS */
    fp = sftp_keys_get_fingerprint(pkt->pool, pubkey_data, pubkey_len,
      SFTP_KEYS_FP_DIGEST_MD5);
    if (fp != NULL) {
      fp_algo = "MD5";
      (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
        "public key %s fingerprint: %s", fp_algo, fp);

    } else {
      (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
        "error obtaining public key MD5 fingerprint: %s", strerror(errno));
    }
#ifdef OPENSSL_FIPS
  }
#endif /* OPENSSL_FIPS */

  if (fp != NULL) {
    const char *k, *v;

    /* Log the fingerprint (and fingerprinting algorithm used), for
     * debugging/auditing; make it available via environment variable as well.
     */
      
    k = pstrdup(session.pool, "SFTP_USER_PUBLICKEY_FINGERPRINT");
    v = pstrdup(session.pool, fp);
    pr_env_unset(session.pool, k);
    pr_env_set(session.pool, k, v);
      
    k = pstrdup(session.pool, "SFTP_USER_PUBLICKEY_FINGERPRINT_ALGO");
    v = pstrdup(session.pool, fp_algo);
    pr_env_unset(session.pool, k);
    pr_env_set(session.pool, k, v);
  }

  pw = pr_auth_getpwnam(pkt->pool, user);
  if (pw == NULL) {
    (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
      "no account for user '%s' found", user);

    pr_log_auth(PR_LOG_NOTICE,
      "USER %s: no such user found from %s [%s] to %s:%d", user,
      session.c->remote_name, pr_netaddr_get_ipstr(session.c->remote_addr),
      pr_netaddr_get_ipstr(session.c->local_addr), session.c->local_port);

    *send_userauth_fail = TRUE;
    errno = ENOENT;
    return 0;
  }

  if (!have_signature) {
    /* We don't perform the actual authentication just yet; we need to
     * let the client know that the pubkey algorithms are acceptable.
     */
    if (send_pubkey_ok(pubkey_algo, pubkey_data, pubkey_len) < 0) {
      return -1;
    }

    return 0;

  } else {
    const unsigned char *id;
    unsigned char *buf2, *ptr2, *signature_data;
    uint32_t buflen2, bufsz2, id_len, signature_len;

    /* XXX This should become a more generic "is this key data
     * usable/acceptable?" check (and take the pubkey_type parameter), so that
     * that is where we would check the validity/usability of an X509v3 cert
     * (if a cert), or if the key is on the blacklist (if a key).
     */

    if (sftp_blacklist_reject_key(pkt->pool, pubkey_data, pubkey_len)) {
      *send_userauth_fail = TRUE;
      errno = EPERM;
      return 0;
    }

    signature_len = sftp_msg_read_int(pkt->pool, buf, buflen);
    signature_data = sftp_msg_read_data(pkt->pool, buf, buflen, signature_len);

    /* The client signed the request as well; we need to authenticate the
     * user with the given pubkey now.  If that succeeds, we use the
     * signature to verify the request.  And if that succeeds, then we're
     * done authenticating.
     */

    /* XXX Need to pass the pubkey_type here as well, so that the
     * verification routines can handle different databases of keys/certs.
     * 
     * For X509v3 certs, we will want a way to enforce/restrict which
     * user names can be used with the provided cert.  Perhaps a database
     * mapping cert fingerprints to user names/UIDs?  Configurable callback
     * check (HOOK?), for modules to enforce.
     */

    if (sftp_keystore_verify_user_key(pkt->pool, user, pubkey_data,
        pubkey_len) < 0) {
      *send_userauth_fail = TRUE;
      errno = EACCES;
      return 0;
    }

    /* Make sure the signature matches as well. */

    id_len = sftp_session_get_id(&id);

    /* Make sure to allocate a buffer large enough to hold the publickey
     * signature and data we want to send back.
     */
    bufsz2 = buflen2 = pubkey_len + 1024;
    ptr2 = buf2 = sftp_msg_getbuf(pkt->pool, bufsz2);

    sftp_msg_write_data(&buf2, &buflen2, id, id_len, TRUE);
    sftp_msg_write_byte(&buf2, &buflen2, SFTP_SSH2_MSG_USER_AUTH_REQUEST);
    sftp_msg_write_string(&buf2, &buflen2, orig_user);

    if (sftp_interop_supports_feature(SFTP_SSH2_FEAT_SERVICE_IN_PUBKEY_SIG)) {
      sftp_msg_write_string(&buf2, &buflen2, service);

    } else {
      sftp_msg_write_string(&buf2, &buflen2, "ssh-userauth");
    }

    if (sftp_interop_supports_feature(SFTP_SSH2_FEAT_HAVE_PUBKEY_ALGO)) {
      sftp_msg_write_string(&buf2, &buflen2, "publickey");
      sftp_msg_write_bool(&buf2, &buflen2, TRUE);
      sftp_msg_write_string(&buf2, &buflen2, pubkey_algo);

    } else {
      sftp_msg_write_bool(&buf2, &buflen2, TRUE);
    }

    sftp_msg_write_data(&buf2, &buflen2, pubkey_data, pubkey_len, TRUE);

    if (sftp_keys_verify_signed_data(pkt->pool, pubkey_algo, pubkey_data,
        pubkey_len, signature_data, signature_len, (unsigned char *) ptr2,
        (bufsz2 - buflen2)) < 0) {
      (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
        "failed to verify '%s' signature on public key auth request for "
        "user '%s'", pubkey_algo, orig_user);
      *send_userauth_fail = TRUE;
      errno = EACCES;
      return 0;
    }
  }

  /* Make sure the user is authorized to login.  Normally this is checked
   * as part of the password verification process, but in the case of
   * publickey authentication, there is no password to verify.
   */

  if (pr_auth_authorize(pkt->pool, user) != PR_AUTH_OK) {
    (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
      "authentication for user '%s' failed: User not authorized", user);
    pr_log_auth(PR_LOG_NOTICE, "USER %s (Login failed): User not authorized "
      "for login", user);
    *send_userauth_fail = TRUE;
    errno = EACCES;
    return 0;
  }

  return 1;
}
Пример #26
0
int sftp_auth_hostbased(struct ssh2_packet *pkt, cmd_rec *pass_cmd,
    const char *orig_user, const char *user, const char *service, char **buf,
    uint32_t *buflen, int *send_userauth_fail) {
  struct passwd *pw;
  char *hostkey_algo, *host_fqdn, *host_user, *host_user_utf8;
  char *hostkey_data, *signature_data;
  char *buf2, *ptr2;
  const char *fp = NULL;
  const unsigned char *id;
  uint32_t buflen2, bufsz2, hostkey_datalen, id_len, signature_len;
  int pubkey_type;

  if (pr_cmd_dispatch_phase(pass_cmd, PRE_CMD, 0) < 0) {
    (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
      "authentication request for user '%s' blocked by '%s' handler",
      orig_user, pass_cmd->argv[0]);

    pr_cmd_dispatch_phase(pass_cmd, POST_CMD_ERR, 0);
    pr_cmd_dispatch_phase(pass_cmd, LOG_CMD_ERR, 0);

    *send_userauth_fail = TRUE;
    errno = EPERM;
    return 0;
  }

  hostkey_algo = sftp_msg_read_string(pkt->pool, buf, buflen);

  hostkey_datalen = sftp_msg_read_int(pkt->pool, buf, buflen);
  hostkey_data = sftp_msg_read_data(pkt->pool, buf, buflen, hostkey_datalen);

  host_fqdn = sftp_msg_read_string(pkt->pool, buf, buflen);

  host_user_utf8 = sftp_msg_read_string(pkt->pool, buf, buflen);
  host_user = sftp_utf8_decode_str(pkt->pool, host_user_utf8);

  signature_len = sftp_msg_read_int(pkt->pool, buf, buflen);
  signature_data = sftp_msg_read_data(pkt->pool, buf, buflen, signature_len);

  pr_trace_msg(trace_channel, 9,
    "client sent '%s' host key, FQDN %s, and remote user '%s'",
    hostkey_algo, host_fqdn, host_user);

  if (strncmp(hostkey_algo, "ssh-rsa", 8) == 0) {
    pubkey_type = EVP_PKEY_RSA;

  } else if (strncmp(hostkey_algo, "ssh-dss", 8) == 0) {
    pubkey_type = EVP_PKEY_DSA;

  /* XXX Need to support X509v3 certs here */

  } else {
    (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
      "unsupported host key algorithm '%s' requested, rejecting request",
      hostkey_algo);

    *send_userauth_fail = TRUE;
    errno = EINVAL;
    return 0;
  }

  if (sftp_keys_verify_pubkey_type(pkt->pool, hostkey_data, hostkey_datalen,
      pubkey_type) != TRUE) {
    (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
      "unable to verify that given host key matches given '%s' algorithm",
      hostkey_algo);

    *send_userauth_fail = TRUE;
    errno = EINVAL;
    return 0;
  }

#ifdef OPENSSL_FIPS
  if (FIPS_mode()) {
    fp = sftp_keys_get_fingerprint(pkt->pool, hostkey_data, hostkey_datalen,
      SFTP_KEYS_FP_DIGEST_SHA1);
    if (fp != NULL) {
      (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
        "public key SHA1 fingerprint: %s", fp);

    } else {
      (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
        "error obtaining public key SHA1 fingerprint: %s", strerror(errno));
    }

  } else {
#endif /* OPENSSL_FIPS */
    fp = sftp_keys_get_fingerprint(pkt->pool, hostkey_data, hostkey_datalen,
      SFTP_KEYS_FP_DIGEST_MD5);
    if (fp != NULL) {
      (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
        "public key MD5 fingerprint: %s", fp);

    } else {
      (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
        "error obtaining public key MD5 fingerprint: %s", strerror(errno));
    }
#ifdef OPENSSL_FIPS
  }
#endif /* OPENSSL_FIPS */

  pw = pr_auth_getpwnam(pkt->pool, user);
  if (pw == NULL) {
    (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
      "no account for user '%s' found", user);

    pr_log_auth(PR_LOG_NOTICE,
      "USER %s: no such user found from %s [%s] to %s:%d", user,
      session.c->remote_name, pr_netaddr_get_ipstr(session.c->remote_addr),
      pr_netaddr_get_ipstr(session.c->local_addr), session.c->local_port);

    *send_userauth_fail = TRUE;
    errno = ENOENT;
    return 0;
  }

  /* XXX Should we check the given FQDN here against the client's actual
   * DNS name and/or IP address?  Or leave that up to the keystore's
   * verify_host_key() function?
   */

  if (sftp_blacklist_reject_key(pkt->pool, hostkey_data, hostkey_datalen)) {
    *send_userauth_fail = TRUE;
    errno = EPERM;
    return 0;
  }

  /* The client signed the request as well; we need to authenticate the
   * host with the given key now.  If that succeeds, we use the signature to
   * verify the request.  And if that succeeds, then we're done authenticating.
   */

  if (sftp_keystore_verify_host_key(pkt->pool, user, host_fqdn, host_user,
      hostkey_data, hostkey_datalen) < 0) {
    *send_userauth_fail = TRUE;
    errno = EPERM;
    return 0;
  }

  /* Make sure the signature matches as well. */

  id_len = sftp_session_get_id(&id);

  /* XXX Is this buffer large enough?  Too large? */
  bufsz2 = buflen2 = 2048;
  ptr2 = buf2 = sftp_msg_getbuf(pkt->pool, bufsz2);

  sftp_msg_write_data(&buf2, &buflen2, (char *) id, id_len, TRUE);
  sftp_msg_write_byte(&buf2, &buflen2, SFTP_SSH2_MSG_USER_AUTH_REQUEST);
  sftp_msg_write_string(&buf2, &buflen2, orig_user);

  if (sftp_interop_supports_feature(SFTP_SSH2_FEAT_SERVICE_IN_HOST_SIG)) {
    sftp_msg_write_string(&buf2, &buflen2, service);

  } else {
    sftp_msg_write_string(&buf2, &buflen2, "ssh-userauth");
  }

  sftp_msg_write_string(&buf2, &buflen2, "hostbased");
  sftp_msg_write_string(&buf2, &buflen2, hostkey_algo);
  sftp_msg_write_data(&buf2, &buflen2, hostkey_data, hostkey_datalen, TRUE);
  sftp_msg_write_string(&buf2, &buflen2, host_fqdn);
  sftp_msg_write_string(&buf2, &buflen2, host_user_utf8);

  if (sftp_keys_verify_signed_data(pkt->pool, hostkey_algo, hostkey_data,
      hostkey_datalen, signature_data, signature_len, (unsigned char *) ptr2,
      (bufsz2 - buflen2)) < 0) {
    (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
      "failed to verify '%s' signature on hostbased auth request for "
      "user '%s', host %s", hostkey_algo, orig_user, host_fqdn);
    *send_userauth_fail = TRUE;
    return 0;
  }

  /* Make sure the user is authorized to login.  Normally this is checked
   * as part of the password verification process, but in the case of
   * hostbased authentication, there is no password to verify.
   */

  if (pr_auth_authorize(pkt->pool, user) != PR_AUTH_OK) {
    (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
      "authentication for user '%s' failed: User not authorized", user);
    pr_log_auth(PR_LOG_NOTICE, "USER %s (Login failed): User not authorized "
      "for login", user);
    *send_userauth_fail = TRUE;
    errno = EACCES;
    return 0;
  }

  return 1;
}
Пример #27
0
int proxy_ftp_xfer_prepare_active(int policy_id, cmd_rec *cmd,
    const char *error_code, struct proxy_session *proxy_sess, int flags) {
  int backend_family, bind_family, res, xerrno = 0;
  cmd_rec *actv_cmd;
  const pr_netaddr_t *bind_addr = NULL;
  pr_response_t *resp;
  unsigned int resp_nlines = 0;
  conn_t *data_conn = NULL;
  char *active_cmd;
  const char *resp_msg = NULL;

  if (cmd == NULL ||
      error_code == NULL ||
      proxy_sess == NULL ||
      proxy_sess->backend_ctrl_conn == NULL) {
    errno = EINVAL;
    return -1;
  }

  switch (policy_id) {
    case PR_CMD_PORT_ID:
      active_cmd = C_PORT;
      break;

    case PR_CMD_EPRT_ID:
      /* If the remote host does not mention EPRT in its features, fall back
       * to using PORT.
       */
      active_cmd = C_EPRT;
      if (pr_table_get(proxy_sess->backend_features, C_EPRT, NULL) == NULL) {
        pr_trace_msg(trace_channel, 19,
          "EPRT not supported by backend server (via FEAT), using PORT");
        if (proxy_sess->dataxfer_policy == PR_CMD_EPRT_ID) {
          proxy_sess->dataxfer_policy = PR_CMD_PORT_ID;
        }

        active_cmd = C_PORT;
        policy_id = PR_CMD_PORT_ID;
      }
      break;

    default:
      /* In this case, the cmd we were given is the one we should send to
       * the backend server -- but only if it is either EPRT or PORT.
       */
      if (pr_cmd_cmp(cmd, PR_CMD_EPRT_ID) != 0 &&
          pr_cmd_cmp(cmd, PR_CMD_PORT_ID) != 0) {
        pr_trace_msg(trace_channel, 9,
          "illegal FTP active transfer command '%s'", (char *) cmd->argv[0]);
        errno = EINVAL;
        return -1;
      }

      active_cmd = cmd->argv[0];

      if (pr_cmd_cmp(cmd, PR_CMD_EPRT_ID) == 0) {
        /* If the remote host does not mention EPRT in its features, fall back
         * to using PORT.
         */
        if (pr_table_get(proxy_sess->backend_features, C_EPRT, NULL) == NULL) {
          pr_trace_msg(trace_channel, 19,
            "EPRT not supported by backend server (via FEAT), using PORT");
          if (proxy_sess->dataxfer_policy == PR_CMD_EPRT_ID) {
            proxy_sess->dataxfer_policy = PR_CMD_PORT_ID;
          }

          active_cmd = C_PORT;
          policy_id = PR_CMD_PORT_ID;
        }
      }

      break;
  }

  bind_addr = proxy_sess->src_addr;
  if (bind_addr == NULL) {
    bind_addr = session.c->local_addr;
  }

  if (pr_netaddr_is_loopback(bind_addr) == TRUE &&
      pr_netaddr_is_loopback(proxy_sess->backend_ctrl_conn->remote_addr) != TRUE) {
    const char *local_name;
    const pr_netaddr_t *local_addr;

    local_name = pr_netaddr_get_localaddr_str(cmd->pool);
    local_addr = pr_netaddr_get_addr(cmd->pool, local_name, NULL);

    if (local_addr != NULL) {
      pr_trace_msg(trace_channel, 14,
        "%s is a loopback address, using %s instead",
        pr_netaddr_get_ipstr(bind_addr), pr_netaddr_get_ipstr(local_addr));
      bind_addr = local_addr;
    }
  }

  /* Need to check the family of the bind addr against the family of the
   * backend server.
   */
  backend_family = pr_netaddr_get_family(proxy_sess->backend_ctrl_conn->remote_addr);
  bind_family = pr_netaddr_get_family(bind_addr);

  if (bind_family == backend_family) {
#ifdef PR_USE_IPV6
    if (pr_netaddr_use_ipv6()) {
      /* Make sure that the family is NOT IPv6, even though the local and
       * remote families match.  The PORT command cannot be used for IPv6
       * addresses -- but EPRT CAN be used for IPv6 addresses.
       */
      if (bind_family == AF_INET6 &&
          policy_id == PR_CMD_PORT_ID) {
        pr_netaddr_t *mapped_addr;

        mapped_addr = pr_netaddr_v6tov4(cmd->pool, bind_addr);
        if (mapped_addr != NULL) {
          pr_trace_msg(trace_channel, 9,
            "converting local IPv6 address '%s' to IPv4 address '%s' for "
            "active transfer using PORT", pr_netaddr_get_ipstr(bind_addr),
            pr_netaddr_get_ipstr(mapped_addr));
          bind_addr = mapped_addr;
        }
      }
    }
#endif /* PR_USE_IPV6 */
  } else {
    if (backend_family == AF_INET) {
      pr_netaddr_t *mapped_addr;

      /* In this scenario, the remote peer is an IPv4 (or IPv4-mapped IPv6)
       * peer, so make sure we use an IPv4 local address.
       */
      mapped_addr = pr_netaddr_v6tov4(cmd->pool, bind_addr);
      if (mapped_addr != NULL) {
        pr_trace_msg(trace_channel, 9,
          "converting local IPv6 address '%s' to IPv4 address '%s' for "
          "active transfer with IPv4 peer", pr_netaddr_get_ipstr(bind_addr),
          pr_netaddr_get_ipstr(mapped_addr));
        bind_addr = mapped_addr;
      }
    }
  }

  if (proxy_sess->backend_data_conn != NULL) {
    /* Make sure that we only have one backend data connection. */
    proxy_inet_close(session.pool, proxy_sess->backend_data_conn);
    proxy_sess->backend_data_conn = NULL;
  }

  data_conn = proxy_ftp_conn_listen(cmd->tmp_pool, bind_addr, FALSE);
  if (data_conn == NULL) {
    xerrno = errno;

    pr_response_add_err(error_code,
      _("Unable to build data connection: Internal error"));
    pr_response_flush(&resp_err_list);

    errno = xerrno;
    return -1;
  }

  proxy_sess->backend_data_conn = data_conn;

  switch (pr_cmd_get_id(active_cmd)) {
    case PR_CMD_PORT_ID:
      resp_msg = proxy_ftp_msg_fmt_addr(cmd->tmp_pool, data_conn->local_addr,
        data_conn->local_port, FALSE);
      break;

    case PR_CMD_EPRT_ID:
      resp_msg = proxy_ftp_msg_fmt_ext_addr(cmd->tmp_pool,
        data_conn->local_addr, data_conn->local_port, PR_CMD_EPRT_ID, FALSE);
      break;
  }

  actv_cmd = pr_cmd_alloc(cmd->tmp_pool, 2, active_cmd, resp_msg);
  actv_cmd->arg = (char *) resp_msg;

  pr_cmd_clear_cache(actv_cmd);

  res = proxy_ftp_ctrl_send_cmd(cmd->tmp_pool, proxy_sess->backend_ctrl_conn,
    actv_cmd);
  if (res < 0) {
    xerrno = errno;
    (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
      "error sending %s to backend: %s", (char *) actv_cmd->argv[0],
      strerror(xerrno));

    proxy_inet_close(session.pool, proxy_sess->backend_data_conn);
    proxy_sess->backend_data_conn = NULL;

    pr_response_add_err(error_code, "%s: %s", (char *) cmd->argv[0],
      strerror(xerrno));
    pr_response_flush(&resp_err_list);

    errno = xerrno;
    return -1;
  }

  resp = proxy_ftp_ctrl_recv_resp(cmd->tmp_pool, proxy_sess->backend_ctrl_conn,
    &resp_nlines, flags);
  if (resp == NULL) {
    xerrno = errno;
    (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
      "error receiving %s response from backend: %s",
      (char *) actv_cmd->argv[0], strerror(xerrno));

    proxy_inet_close(session.pool, proxy_sess->backend_data_conn);
    proxy_sess->backend_data_conn = NULL;

    pr_response_add_err(error_code, "%s: %s", (char *) cmd->argv[0],
      strerror(xerrno));
    pr_response_flush(&resp_err_list);

    errno = xerrno;
    return -1;
  }

  if (resp->num[0] != '2') {
    (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
      "received non-2xx response from backend for %s: %s %s",
      (char *) actv_cmd->argv[0], resp->num, resp->msg);

    proxy_inet_close(session.pool, proxy_sess->backend_data_conn);
    proxy_sess->backend_data_conn = NULL;

    if (policy_id == PR_CMD_EPRT_ID) {
      /* If using EPRT failed, try again using PORT, and switch the
       * DataTransferPolicy (if EPRT) to be PORT, for future attempts.
       */

      if (proxy_sess->dataxfer_policy == PR_CMD_EPRT_ID) {
        pr_trace_msg(trace_channel, 15,
          "falling back from EPRT to PORT DataTransferPolicy");
        proxy_sess->dataxfer_policy = PR_CMD_PORT_ID;
      }

      return proxy_ftp_xfer_prepare_active(PR_CMD_PORT_ID, cmd,
        error_code, proxy_sess, flags);
    }

    pr_response_add_err(error_code, "%s", resp->msg);
    pr_response_flush(&resp_err_list);

    errno = EINVAL;
    return -1;
  }

  return 0;
}
Пример #28
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;
}
Пример #29
0
static int sftppam_driver_open(sftp_kbdint_driver_t *driver, const char *user) {
  int res;
  config_rec *c;

  /* XXX Should we pay attention to AuthOrder here?  I.e. if AuthOrder
   * does not include mod_sftp_pam or mod_auth_pam, should we fail to
   * open this driver, since the AuthOrder indicates that no PAM check is
   * desired?  For this to work, AuthOrder needs to have been processed
   * prior to this callback being invoked...
   */

  /* Figure out our default return style: whether or not PAM should allow
   * other auth modules a shot at this user or not is controlled by adding
   * '*' to a module name in the AuthOrder directive.  By default, auth
   * modules are not authoritative, and allow other auth modules a chance at
   * authenticating the user.  This is not the most secure configuration, but
   * it allows things like AuthUserFile to work "out of the box".
   */
  if (sftppam_authtab[0].auth_flags & PR_AUTH_FL_REQUIRED) {
    sftppam_authoritative = TRUE;
  }

  sftppam_userlen = strlen(user) + 1;
  if (sftppam_userlen > (PAM_MAX_MSG_SIZE + 1)) {
    sftppam_userlen = PAM_MAX_MSG_SIZE + 1;
  }

#ifdef MAXLOGNAME
  /* Some platforms' PAM libraries do not handle login strings that exceed
   * this length.
   */
  if (sftppam_userlen > MAXLOGNAME) {
    pr_log_pri(PR_LOG_NOTICE,
      "PAM(%s): Name exceeds maximum login length (%u)", user, MAXLOGNAME);
    pr_trace_msg(trace_channel, 1,
      "user name '%s' exceeds maximum login length %u, declining", user,
      MAXLOGNAME);
    errno = EPERM;
    return -1;
  }
#endif

  sftppam_user = malloc(sftppam_userlen);
  if (sftppam_user == NULL) {
    pr_log_pri(PR_LOG_ALERT, MOD_SFTP_PAM_VERSION ": Out of memory!");
    exit(1);
  }

  memset(sftppam_user, '\0', sftppam_userlen);
  sstrncpy(sftppam_user, user, sftppam_userlen);

  c = find_config(main_server->conf, CONF_PARAM, "SFTPPAMOptions", FALSE);
  while (c != NULL) {
    unsigned long opts;

    pr_signals_handle();

    opts = *((unsigned long *) c->argv[0]);
    sftppam_opts |= opts;

    c = find_config_next(c, c->next, CONF_PARAM, "SFTPPAMOptions", FALSE);
  }
 
#ifdef SOLARIS2
  /* For Solaris environments, the TTY environment will always be set,
   * in order to workaround a bug (Solaris Bug ID 4250887) where
   * pam_open_session() will crash unless both PAM_RHOST and PAM_TTY are
   * set, and the PAM_TTY setting is at least greater than the length of
   * the string "/dev/".
   */
  sftppam_opts &= ~SFTP_PAM_OPT_NO_TTY;
#endif /* SOLARIS2 */
 
  pr_signals_block();
  PRIVS_ROOT

  res = pam_start(sftppam_service, sftppam_user, &sftppam_conv, &sftppam_pamh);
  if (res != PAM_SUCCESS) {
    PRIVS_RELINQUISH
    pr_signals_unblock();

    free(sftppam_user);
    sftppam_user = NULL;
    sftppam_userlen = 0;

    switch (res) {
      case PAM_SYSTEM_ERR:
        (void) pr_log_writefile(sftp_logfd, MOD_SFTP_PAM_VERSION,
          "error starting PAM service: %s", strerror(errno));
        break;

      case PAM_BUF_ERR:
        (void) pr_log_writefile(sftp_logfd, MOD_SFTP_PAM_VERSION,
          "error starting PAM service: Memory buffer error");
        break;
    }

    return -1;
  }

  pam_set_item(sftppam_pamh, PAM_RUSER, sftppam_user);
  pam_set_item(sftppam_pamh, PAM_RHOST, session.c->remote_name);

  if (!(sftppam_opts & SFTP_PAM_OPT_NO_TTY)) {
    memset(sftppam_tty, '\0', sizeof(sftppam_tty));
    snprintf(sftppam_tty, sizeof(sftppam_tty), "/dev/ftpd%02lu",
      (unsigned long) (session.pid ? session.pid : getpid()));
    sftppam_tty[sizeof(sftppam_tty)-1] = '\0';

    pr_trace_msg(trace_channel, 9, "setting PAM_TTY to '%s'", sftppam_tty);
    pam_set_item(sftppam_pamh, PAM_TTY, sftppam_tty);
  }

  PRIVS_RELINQUISH
  pr_signals_unblock();

  /* We need to disable mod_auth_pam, since both mod_auth_pam and us want
   * to talk to the PAM API, just in different fashions.
   */

  c = add_config_param_set(&(main_server->conf), "AuthPAM", 1, NULL);
  c->argv[0] = palloc(c->pool, sizeof(unsigned char));
  *((unsigned char *) c->argv[0]) = FALSE;

  if (pr_auth_remove_auth_only_module("mod_auth_pam.c") < 0) {
    if (errno != ENOENT) {
      pr_log_pri(PR_LOG_NOTICE, MOD_SFTP_PAM_VERSION
        ": error removing 'mod_auth_pam.c' from the auth-only module list: %s",
        strerror(errno));
    }
  }

  if (pr_auth_add_auth_only_module("mod_sftp_pam.c") < 0) {
    if (errno != EEXIST) {
      pr_log_pri(PR_LOG_NOTICE, MOD_SFTP_PAM_VERSION
        ": error adding 'mod_sftp_pam.c' to the auth-only module list: %s",
        strerror(errno));
    }
  }

  sftppam_handle_auth = TRUE;

  driver->driver_pool = make_sub_pool(permanent_pool);
  pr_pool_tag(driver->driver_pool, "PAM keyboard-interactive driver pool");

  return 0;
}
Пример #30
0
int proxy_ftp_ctrl_handle_async(pool *p, conn_t *backend_conn,
                                conn_t *frontend_conn) {
    if (!(proxy_sess_state & PROXY_SESS_STATE_CONNECTED)) {
        /* Nothing to do if we're not yet connected to the backend server. */
        return 0;
    }

    while (TRUE) {
        fd_set rfds;
        struct timeval tv;
        int ctrlfd, res, xerrno = 0;

        /* By using a timeout of zero, we effect a poll on the fd. */
        tv.tv_sec = 0;
        tv.tv_usec = 0;

        pr_signals_handle();

        FD_ZERO(&rfds);

        ctrlfd = PR_NETIO_FD(backend_conn->instrm);
        FD_SET(ctrlfd, &rfds);

        res = select(ctrlfd + 1, &rfds, NULL, NULL, &tv);
        if (res < 0) {
            xerrno = errno;

            if (xerrno == EINTR) {
                pr_signals_handle();
                continue;
            }

            (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
                                    "error calling select(2) on backend control connection (fd %d): %s",
                                    ctrlfd, strerror(xerrno));
            return 0;
        }

        if (res == 0) {
            /* Nothing there. */
            break;
        }

        pr_trace_msg(trace_channel, 19,
                     "select(2) reported %d for backend %s (fd %d)", res,
                     backend_conn->remote_name, ctrlfd);

        if (FD_ISSET(ctrlfd, &rfds)) {
            unsigned int resp_nlines = 0;
            pr_response_t *resp;

            pr_timer_reset(PR_TIMER_IDLE, ANY_MODULE);

            pr_trace_msg(trace_channel, 9, "reading async response from backend %s",
                         backend_conn->remote_name);

            resp = proxy_ftp_ctrl_recv_resp(p, backend_conn, &resp_nlines);
            if (resp == NULL) {
                xerrno = errno;

                (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
                                        "error receiving response from backend control connection: %s",
                                        strerror(xerrno));

                errno = xerrno;
                return -1;
            }

            res = proxy_ftp_ctrl_send_resp(p, frontend_conn, resp, resp_nlines);
            if (res < 0) {
                xerrno = errno;

                (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
                                        "error sending response to frontend control connection: %s",
                                        strerror(xerrno));

                errno = xerrno;
                return -1;
            }
        }

        break;
    }

    return 0;
}