Commit 98696688 authored by Daniel Stenberg's avatar Daniel Stenberg
Browse files

SSH: move knownhost logic to separate function

parent b903186f
Loading
Loading
Loading
Loading
+126 −112
Original line number Diff line number Diff line
@@ -504,100 +504,16 @@ static int sshkeycallback(CURL *easy,
#define libssh2_session_startup(x,y) libssh2_session_handshake(x,y)
#endif

/*
 * ssh_statemach_act() runs the SSH state machine as far as it can without
 * blocking and without reaching the end.  The data the pointer 'block' points
 * to will be set to TRUE if the libssh2 function returns LIBSSH2_ERROR_EAGAIN
 * meaning it wants to be called again when the socket is ready
 */

static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
static CURLcode ssh_knownhost(struct connectdata *conn)
{
  CURLcode result = CURLE_OK;
  struct SessionHandle *data = conn->data;
  struct SSHPROTO *sftp_scp = data->state.proto.ssh;
  struct ssh_conn *sshc = &conn->proto.sshc;
  curl_socket_t sock = conn->sock[FIRSTSOCKET];
#ifdef CURL_LIBSSH2_DEBUG
  const char *fingerprint;
#endif /* CURL_LIBSSH2_DEBUG */
  const char *host_public_key_md5;
  int rc = LIBSSH2_ERROR_NONE, i;
  int err;
  int seekerr = CURL_SEEKFUNC_OK;
  *block = 0; /* we're not blocking by default */

  do {

    switch(sshc->state) {
    case SSH_S_STARTUP:
      sshc->secondCreateDirs = 0;
      sshc->nextstate = SSH_NO_STATE;
      sshc->actualcode = CURLE_OK;

      rc = libssh2_session_startup(sshc->ssh_session, sock);
      if(rc == LIBSSH2_ERROR_EAGAIN) {
        break;
      }
      else if(rc) {
        failf(data, "Failure establishing ssh session");
        state(conn, SSH_SESSION_FREE);
        sshc->actualcode = CURLE_FAILED_INIT;
        break;
      }

      /* Set libssh2 to non-blocking, since everything internally is
         non-blocking */
      libssh2_session_set_blocking(sshc->ssh_session, 0);

      state(conn, SSH_HOSTKEY);

      /* fall-through */
    case SSH_HOSTKEY:

#ifdef CURL_LIBSSH2_DEBUG
      /*
       * Before we authenticate we should check the hostkey's fingerprint
       * against our known hosts. How that is handled (reading from file,
       * whatever) is up to us. As for know not much is implemented, besides
       * showing how to get the fingerprint.
       */
      fingerprint = libssh2_hostkey_hash(sshc->ssh_session,
                                         LIBSSH2_HOSTKEY_HASH_MD5);

      /* The fingerprint points to static storage (!), don't free() it. */
      infof(data, "Fingerprint: ");
      for(rc = 0; rc < 16; rc++)
        infof(data, "%02X ", (unsigned char) fingerprint[rc]);
      infof(data, "\n");
#endif /* CURL_LIBSSH2_DEBUG */

      /* Before we authenticate we check the hostkey's MD5 fingerprint
       * against a known fingerprint, if available.  This implementation pulls
       * it from the curl option.
       */
      if(data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5] &&
         strlen(data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]) == 32) {
        char buf[33];
        host_public_key_md5 = libssh2_hostkey_hash(sshc->ssh_session,
                                                   LIBSSH2_HOSTKEY_HASH_MD5);
        for(i = 0; i < 16; i++)
          snprintf(&buf[i*2], 3, "%02x",
                   (unsigned char) host_public_key_md5[i]);
        if(!strequal(buf, data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5])) {
          failf(data,
                "Denied establishing ssh session: mismatch md5 fingerprint. "
                "Remote %s is not equal to %s",
                buf, data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]);
          state(conn, SSH_SESSION_FREE);
          sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
          break;
        }
      }
  CURLcode result = CURLE_OK;

#ifdef HAVE_LIBSSH2_KNOWNHOST_API
  if(data->set.str[STRING_SSH_KNOWNHOSTS]) {
    /* we're asked to verify the host against a file */
    struct ssh_conn *sshc = &conn->proto.sshc;
    int rc;
    int keytype;
    size_t keylen;
    const char *remotekey = libssh2_session_hostkey(sshc->ssh_session,
@@ -704,8 +620,106 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
      break;
    }
  }
#endif /* HAVE_LIBSSH2_KNOWNHOST_API */
#else /* HAVE_LIBSSH2_KNOWNHOST_API */
  (void)conn;
#endif
  return result;
}


/*
 * ssh_statemach_act() runs the SSH state machine as far as it can without
 * blocking and without reaching the end.  The data the pointer 'block' points
 * to will be set to TRUE if the libssh2 function returns LIBSSH2_ERROR_EAGAIN
 * meaning it wants to be called again when the socket is ready
 */

static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
{
  CURLcode result = CURLE_OK;
  struct SessionHandle *data = conn->data;
  struct SSHPROTO *sftp_scp = data->state.proto.ssh;
  struct ssh_conn *sshc = &conn->proto.sshc;
  curl_socket_t sock = conn->sock[FIRSTSOCKET];
#ifdef CURL_LIBSSH2_DEBUG
  const char *fingerprint;
#endif /* CURL_LIBSSH2_DEBUG */
  const char *host_public_key_md5;
  int rc = LIBSSH2_ERROR_NONE, i;
  int err;
  int seekerr = CURL_SEEKFUNC_OK;
  *block = 0; /* we're not blocking by default */

  do {

    switch(sshc->state) {
    case SSH_S_STARTUP:
      sshc->secondCreateDirs = 0;
      sshc->nextstate = SSH_NO_STATE;
      sshc->actualcode = CURLE_OK;

      rc = libssh2_session_startup(sshc->ssh_session, sock);
      if(rc == LIBSSH2_ERROR_EAGAIN) {
        break;
      }
      else if(rc) {
        failf(data, "Failure establishing ssh session");
        state(conn, SSH_SESSION_FREE);
        sshc->actualcode = CURLE_FAILED_INIT;
        break;
      }

      /* Set libssh2 to non-blocking, since everything internally is
         non-blocking */
      libssh2_session_set_blocking(sshc->ssh_session, 0);

      state(conn, SSH_HOSTKEY);

      /* fall-through */
    case SSH_HOSTKEY:

#ifdef CURL_LIBSSH2_DEBUG
      /*
       * Before we authenticate we should check the hostkey's fingerprint
       * against our known hosts. How that is handled (reading from file,
       * whatever) is up to us. As for know not much is implemented, besides
       * showing how to get the fingerprint.
       */
      fingerprint = libssh2_hostkey_hash(sshc->ssh_session,
                                         LIBSSH2_HOSTKEY_HASH_MD5);

      /* The fingerprint points to static storage (!), don't free() it. */
      infof(data, "Fingerprint: ");
      for(rc = 0; rc < 16; rc++)
        infof(data, "%02X ", (unsigned char) fingerprint[rc]);
      infof(data, "\n");
#endif /* CURL_LIBSSH2_DEBUG */

      /* Before we authenticate we check the hostkey's MD5 fingerprint
       * against a known fingerprint, if available.  This implementation pulls
       * it from the curl option.
       */
      if(data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5] &&
         strlen(data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]) == 32) {
        char buf[33];
        host_public_key_md5 = libssh2_hostkey_hash(sshc->ssh_session,
                                                   LIBSSH2_HOSTKEY_HASH_MD5);
        for(i = 0; i < 16; i++)
          snprintf(&buf[i*2], 3, "%02x",
                   (unsigned char) host_public_key_md5[i]);
        if(!strequal(buf, data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5])) {
          failf(data,
                "Denied establishing ssh session: mismatch md5 fingerprint. "
                "Remote %s is not equal to %s",
                buf, data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]);
          state(conn, SSH_SESSION_FREE);
          sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
          break;
        }
      }

      result = ssh_knownhost(conn);
      if(!result)
        state(conn, SSH_AUTHLIST);
      break;