Ejemplo n.º 1
0
int ssh_bind_accept(ssh_bind sshbind, ssh_session session) {
  socket_t fd = SSH_INVALID_SOCKET;
  int rc;
  if (sshbind->bindfd == SSH_INVALID_SOCKET) {
    ssh_set_error(sshbind, SSH_FATAL,
        "Can't accept new clients on a not bound socket.");
    return SSH_ERROR;
  }

  if (session == NULL){
      ssh_set_error(sshbind, SSH_FATAL,"session is null");
      return SSH_ERROR;
  }

  fd = accept(sshbind->bindfd, NULL, NULL);
  if (fd == SSH_INVALID_SOCKET) {
    ssh_set_error(sshbind, SSH_FATAL,
        "Accepting a new connection: %s",
        strerror(errno));
    return SSH_ERROR;
  }
  rc = ssh_bind_accept_fd(sshbind, session, fd);

  if(rc == SSH_ERROR){
#ifdef _WIN32
      closesocket(fd);
#else
      close(fd);
#endif
      if (session->socket)
          ssh_socket_close(session->socket);
  }
  return rc;
}
Ejemplo n.º 2
0
void agent_close(struct ssh_agent_struct *agent) {
  if (agent == NULL) {
    return;
  }

  if (getenv("SSH_AUTH_SOCK")) {
    ssh_socket_close(agent->sock);
  }
}
Ejemplo n.º 3
0
/** \internal
 * \brief starts a nonblocking flush of the output buffer
 *
 */
int ssh_socket_nonblocking_flush(ssh_socket s) {
  ssh_session session = s->session;
  uint32_t len;
  int w;

  enter_function();

  if (!ssh_socket_is_open(s)) {
    session->alive = 0;
    /* FIXME use ssh_socket_get_errno */
    ssh_set_error(session, SSH_FATAL,
        "Writing packet: error on socket (or connection closed): %s",
        strerror(s->last_errno));

    leave_function();
    return SSH_ERROR;
  }

  len = buffer_get_rest_len(s->out_buffer);
  if (!s->write_wontblock && s->poll_out && len > 0) {
      /* force the poll system to catch pollout events */
      ssh_poll_add_events(s->poll_out, POLLOUT);
      leave_function();
      return SSH_AGAIN;
  }
  if (s->write_wontblock && len > 0) {
    w = ssh_socket_unbuffered_write(s, buffer_get_rest(s->out_buffer), len);
    if (w < 0) {
      session->alive = 0;
      ssh_socket_close(s);
      /* FIXME use ssh_socket_get_errno() */
      /* FIXME use callback for errors */
      ssh_set_error(session, SSH_FATAL,
          "Writing packet: error on socket (or connection closed): %s",
          strerror(s->last_errno));
      leave_function();
      return SSH_ERROR;
    }
    buffer_pass_bytes(s->out_buffer, w);
  }

  /* Is there some data pending? */
  len = buffer_get_rest_len(s->out_buffer);
  if (s->poll_out && len > 0) {
      /* force the poll system to catch pollout events */
      ssh_poll_add_events(s->poll_out, POLLOUT);
      leave_function();
      return SSH_AGAIN;
  }

  /* all data written */
  leave_function();
  return SSH_OK;
}
Ejemplo n.º 4
0
/**
 * @brief Disconnect from a session (client or server).
 * The session can then be reused to open a new session.
 *
 * @param[in]  session  The SSH session to use.
 */
void ssh_disconnect(ssh_session session) {
  ssh_string str = NULL;
  struct ssh_iterator *it;

  if (session == NULL) {
    return;
  }

  if (session->socket != NULL && ssh_socket_is_open(session->socket)) {
    if (buffer_add_u8(session->out_buffer, SSH2_MSG_DISCONNECT) < 0) {
      goto error;
    }
    if (buffer_add_u32(session->out_buffer,
          htonl(SSH2_DISCONNECT_BY_APPLICATION)) < 0) {
      goto error;
    }

    str = ssh_string_from_char("Bye Bye");
    if (str == NULL) {
      goto error;
    }

    if (buffer_add_ssh_string(session->out_buffer,str) < 0) {
      ssh_string_free(str);
      goto error;
    }
    ssh_string_free(str);

    packet_send(session);
    ssh_socket_close(session->socket);
  }
error:
  session->alive = 0;
  if (session->socket != NULL){
    ssh_socket_reset(session->socket);
  }
  session->opts.fd = SSH_INVALID_SOCKET;
  session->session_state=SSH_SESSION_STATE_DISCONNECTED;

  while ((it=ssh_list_get_iterator(session->channels)) != NULL) {
    ssh_channel_do_free(ssh_iterator_value(ssh_channel,it));
    ssh_list_remove(session->channels, it);
  }
  if(session->current_crypto){
    crypto_free(session->current_crypto);
    session->current_crypto=NULL;
  }
  if (session->in_buffer) {
    ssh_buffer_reinit(session->in_buffer);
  }
  if (session->out_buffer) {
    ssh_buffer_reinit(session->out_buffer);
  }
  if (session->in_hashbuf) {
    ssh_buffer_reinit(session->in_hashbuf);
  }
  if (session->out_hashbuf) {
    ssh_buffer_reinit(session->out_hashbuf);
  }
  session->auth_methods = 0;
  SAFE_FREE(session->serverbanner);
  SAFE_FREE(session->clientbanner);

  if(session->ssh_message_list){
    ssh_message msg;
    while((msg=ssh_list_pop_head(ssh_message ,session->ssh_message_list))
        != NULL){
      ssh_message_free(msg);
    }
    ssh_list_free(session->ssh_message_list);
    session->ssh_message_list=NULL;
  }

  if (session->packet_callbacks){
    ssh_list_free(session->packet_callbacks);
    session->packet_callbacks=NULL;
  }
}
Ejemplo n.º 5
0
/**
 * @internal
 *
 * @brief A function to be called each time a step has been done in the
 * connection.
 */
static void ssh_client_connection_callback(ssh_session session){
	int ssh1,ssh2;

	switch(session->session_state){
		case SSH_SESSION_STATE_NONE:
		case SSH_SESSION_STATE_CONNECTING:
		case SSH_SESSION_STATE_SOCKET_CONNECTED:
			break;
		case SSH_SESSION_STATE_BANNER_RECEIVED:
		  if (session->serverbanner == NULL) {
		    goto error;
		  }
		  set_status(session, 0.4f);
		  SSH_LOG(SSH_LOG_RARE,
		      "SSH server banner: %s", session->serverbanner);

		  /* Here we analyze the different protocols the server allows. */
		  if (ssh_analyze_banner(session, 0, &ssh1, &ssh2) < 0) {
		    goto error;
		  }
		  /* Here we decide which version of the protocol to use. */
		  if (ssh2 && session->opts.ssh2) {
		    session->version = 2;
#ifdef WITH_SSH1
		    } else if(ssh1 && session->opts.ssh1) {
		    session->version = 1;
#endif
		    } else if(ssh1 && !session->opts.ssh1){
#ifdef WITH_SSH1
		    ssh_set_error(session, SSH_FATAL,
		        "SSH-1 protocol not available (configure session to allow SSH-1)");
		    goto error;
#else
		    ssh_set_error(session, SSH_FATAL,
		        "SSH-1 protocol not available (libssh compiled without SSH-1 support)");
		    goto error;
#endif
		  } else {
		    ssh_set_error(session, SSH_FATAL,
		        "No version of SSH protocol usable (banner: %s)",
		        session->serverbanner);
		    goto error;
		  }
		  /* from now, the packet layer is handling incoming packets */
		  if(session->version==2)
		    session->socket_callbacks.data=ssh_packet_socket_callback;
#ifdef WITH_SSH1
		  else
		    session->socket_callbacks.data=ssh_packet_socket_callback1;
#endif
		  ssh_packet_set_default_callbacks(session);
		  session->session_state=SSH_SESSION_STATE_INITIAL_KEX;
		  ssh_send_banner(session, 0);
		  set_status(session, 0.5f);
		  break;
		case SSH_SESSION_STATE_INITIAL_KEX:
		/* TODO: This state should disappear in favor of get_key handle */
#ifdef WITH_SSH1
			if(session->version==1){
				if (ssh_get_kex1(session) < 0)
					goto error;
				set_status(session,0.6f);
				session->connected = 1;
				break;
			}
#endif
			break;
		case SSH_SESSION_STATE_KEXINIT_RECEIVED:
			set_status(session,0.6f);
			ssh_list_kex(&session->next_crypto->server_kex);
			if (set_client_kex(session) < 0) {
				goto error;
			}
			if (ssh_kex_select_methods(session) == SSH_ERROR)
			    goto error;
			if (ssh_send_kex(session, 0) < 0) {
				goto error;
			}
			set_status(session,0.8f);
			session->session_state=SSH_SESSION_STATE_DH;
			if (dh_handshake(session) == SSH_ERROR) {
				goto error;
			}
            /* FALL THROUGH */
		case SSH_SESSION_STATE_DH:
			if(session->dh_handshake_state==DH_STATE_FINISHED){
				set_status(session,1.0f);
				session->connected = 1;
				if (session->flags & SSH_SESSION_FLAG_AUTHENTICATED)
				    session->session_state = SSH_SESSION_STATE_AUTHENTICATED;
				else
				    session->session_state=SSH_SESSION_STATE_AUTHENTICATING;
			}
			break;
		case SSH_SESSION_STATE_AUTHENTICATING:
			break;
		case SSH_SESSION_STATE_ERROR:
			goto error;
		default:
			ssh_set_error(session,SSH_FATAL,"Invalid state %d",session->session_state);
	}

	return;
error:
	ssh_socket_close(session->socket);
	session->alive = 0;
	session->session_state=SSH_SESSION_STATE_ERROR;

}
Ejemplo n.º 6
0
/**
 * @internal
 *
 * @brief A function to be called each time a step has been done in the
 * connection.
 */
static void ssh_server_connection_callback(ssh_session session){
	int ssh1,ssh2;

	switch(session->session_state){
		case SSH_SESSION_STATE_NONE:
		case SSH_SESSION_STATE_CONNECTING:
		case SSH_SESSION_STATE_SOCKET_CONNECTED:
			break;
		case SSH_SESSION_STATE_BANNER_RECEIVED:
		  if (session->clientbanner == NULL) {
		    goto error;
		  }
		  set_status(session, 0.4f);
		  SSH_LOG(SSH_LOG_RARE,
		      "SSH client banner: %s", session->clientbanner);

		  /* Here we analyze the different protocols the server allows. */
		  if (ssh_analyze_banner(session, 1, &ssh1, &ssh2) < 0) {
		    goto error;
		  }
		  /* Here we decide which version of the protocol to use. */
		  if (ssh2 && session->opts.ssh2) {
		    session->version = 2;
		  } else if (ssh1 && session->opts.ssh1) {
		    session->version = 1;
		  } else if (ssh1 && !session->opts.ssh1) {
#ifdef WITH_SSH1
		    ssh_set_error(session, SSH_FATAL,
		        "SSH-1 protocol not available (configure session to allow SSH-1)");
		    goto error;
#else
		    ssh_set_error(session, SSH_FATAL,
		        "SSH-1 protocol not available (libssh compiled without SSH-1 support)");
		    goto error;
#endif
		  } else {
		    ssh_set_error(session, SSH_FATAL,
		        "No version of SSH protocol usable (banner: %s)",
		        session->clientbanner);
		    goto error;
		  }
		  /* from now, the packet layer is handling incoming packets */
		  if(session->version==2)
		    session->socket_callbacks.data=ssh_packet_socket_callback;
#ifdef WITH_SSH1
		  else
		    session->socket_callbacks.data=ssh_packet_socket_callback1;
#endif
		  ssh_packet_set_default_callbacks(session);
		  set_status(session, 0.5f);
		  session->session_state=SSH_SESSION_STATE_INITIAL_KEX;
          if (ssh_send_kex(session, 1) < 0) {
			goto error;
		  }
		  break;
		case SSH_SESSION_STATE_INITIAL_KEX:
		/* TODO: This state should disappear in favor of get_key handle */
#ifdef WITH_SSH1
			if(session->version==1){
				if (ssh_get_kex1(session) < 0)
					goto error;
				set_status(session,0.6f);
				session->connected = 1;
				break;
			}
#endif
			break;
		case SSH_SESSION_STATE_KEXINIT_RECEIVED:
			set_status(session,0.6f);
			if(session->next_crypto->server_kex.methods[0]==NULL){
			      if(server_set_kex(session) == SSH_ERROR)
				goto error;
			      /* We are in a rekeying, so we need to send the server kex */
			      if(ssh_send_kex(session, 1) < 0)
				goto error;
			}
			ssh_list_kex(&session->next_crypto->client_kex); // log client kex
			if (ssh_kex_select_methods(session) < 0) {
				goto error;
			}
            if (crypt_set_algorithms_server(session) == SSH_ERROR)
                goto error;
			set_status(session,0.8f);
			session->session_state=SSH_SESSION_STATE_DH;
            break;
		case SSH_SESSION_STATE_DH:
			if(session->dh_handshake_state==DH_STATE_FINISHED){
                if (generate_session_keys(session) < 0) {
                  goto error;
                }

                /*
                 * Once we got SSH2_MSG_NEWKEYS we can switch next_crypto and
                 * current_crypto
                 */
                if (session->current_crypto) {
                  crypto_free(session->current_crypto);
                }

                /* FIXME TODO later, include a function to change keys */
                session->current_crypto = session->next_crypto;
                session->next_crypto = crypto_new();
                if (session->next_crypto == NULL) {
                  goto error;
                }
			session->next_crypto->session_id = malloc(session->current_crypto->digest_len);
			if (session->next_crypto->session_id == NULL) {
			  ssh_set_error_oom(session);
			  goto error;
			}
			memcpy(session->next_crypto->session_id, session->current_crypto->session_id,
			    session->current_crypto->digest_len);

			    set_status(session,1.0f);
			    session->connected = 1;
			    session->session_state=SSH_SESSION_STATE_AUTHENTICATING;
			    if (session->flags & SSH_SESSION_FLAG_AUTHENTICATED)
				    session->session_state = SSH_SESSION_STATE_AUTHENTICATED;
		}
			break;
		case SSH_SESSION_STATE_AUTHENTICATING:
			break;
		case SSH_SESSION_STATE_ERROR:
			goto error;
		default:
			ssh_set_error(session,SSH_FATAL,"Invalid state %d",session->session_state);
	}

	return;
error:
	ssh_socket_close(session->socket);
	session->alive = 0;
	session->session_state=SSH_SESSION_STATE_ERROR;
}
Ejemplo n.º 7
0
/**
 * @internal
 *
 * @brief A function to be called each time a step has been done in the
 * connection.
 */
static void ssh_server_connection_callback(ssh_session session){
    int rc;

    switch(session->session_state){
        case SSH_SESSION_STATE_NONE:
        case SSH_SESSION_STATE_CONNECTING:
        case SSH_SESSION_STATE_SOCKET_CONNECTED:
            break;
        case SSH_SESSION_STATE_BANNER_RECEIVED:
            if (session->clientbanner == NULL) {
                goto error;
            }
            set_status(session, 0.4f);
            SSH_LOG(SSH_LOG_RARE,
                    "SSH client banner: %s", session->clientbanner);

            /* Here we analyze the different protocols the server allows. */
            rc = ssh_analyze_banner(session, 1);
            if (rc < 0) {
                ssh_set_error(session, SSH_FATAL,
                        "No version of SSH protocol usable (banner: %s)",
                        session->clientbanner);
                goto error;
            }

            /* from now, the packet layer is handling incoming packets */
            session->socket_callbacks.data=ssh_packet_socket_callback;

            ssh_packet_set_default_callbacks(session);
            set_status(session, 0.5f);
            session->session_state=SSH_SESSION_STATE_INITIAL_KEX;
            if (ssh_send_kex(session, 1) < 0) {
                goto error;
            }
            break;
        case SSH_SESSION_STATE_INITIAL_KEX:
            /* TODO: This state should disappear in favor of get_key handle */
            break;
        case SSH_SESSION_STATE_KEXINIT_RECEIVED:
            set_status(session,0.6f);
            if(session->next_crypto->server_kex.methods[0]==NULL){
                if(server_set_kex(session) == SSH_ERROR)
                    goto error;
                /* We are in a rekeying, so we need to send the server kex */
                if(ssh_send_kex(session, 1) < 0)
                    goto error;
            }
            ssh_list_kex(&session->next_crypto->client_kex); // log client kex
            if (ssh_kex_select_methods(session) < 0) {
                goto error;
            }
            if (crypt_set_algorithms_server(session) == SSH_ERROR)
                goto error;
            set_status(session,0.8f);
            session->session_state=SSH_SESSION_STATE_DH;
            break;
        case SSH_SESSION_STATE_DH:
            if(session->dh_handshake_state==DH_STATE_FINISHED){
                if (ssh_generate_session_keys(session) < 0) {
                    goto error;
                }

                /*
                 * Once we got SSH2_MSG_NEWKEYS we can switch next_crypto and
                 * current_crypto
                 */
                if (session->current_crypto) {
                    crypto_free(session->current_crypto);
                }

                /* FIXME TODO later, include a function to change keys */
                session->current_crypto = session->next_crypto;
                session->next_crypto = crypto_new();
                if (session->next_crypto == NULL) {
                    goto error;
                }
                session->next_crypto->session_id = malloc(session->current_crypto->digest_len);
                if (session->next_crypto->session_id == NULL) {
                    ssh_set_error_oom(session);
                    goto error;
                }
                memcpy(session->next_crypto->session_id, session->current_crypto->session_id,
                        session->current_crypto->digest_len);
                if (session->current_crypto->in_cipher->set_decrypt_key(session->current_crypto->in_cipher, session->current_crypto->decryptkey,
                            session->current_crypto->decryptIV) < 0) {
                    goto error;
                }
                if (session->current_crypto->out_cipher->set_encrypt_key(session->current_crypto->out_cipher, session->current_crypto->encryptkey,
                            session->current_crypto->encryptIV) < 0) {
                    goto error;
                }

                set_status(session,1.0f);
                session->connected = 1;
                session->session_state=SSH_SESSION_STATE_AUTHENTICATING;
                if (session->flags & SSH_SESSION_FLAG_AUTHENTICATED)
                    session->session_state = SSH_SESSION_STATE_AUTHENTICATED;
            }
            break;
        case SSH_SESSION_STATE_AUTHENTICATING:
            break;
        case SSH_SESSION_STATE_ERROR:
            goto error;
        default:
            ssh_set_error(session,SSH_FATAL,"Invalid state %d",session->session_state);
    }

    return;
error:
    ssh_socket_close(session->socket);
    session->alive = 0;
    session->session_state=SSH_SESSION_STATE_ERROR;
}
Ejemplo n.º 8
0
/** \internal
 * \brief starts a nonblocking flush of the output buffer
 *
 */
int ssh_socket_nonblocking_flush(ssh_socket s) {
  ssh_session session = s->session;
  uint32_t len;
  int w;

  if (!ssh_socket_is_open(s)) {
    session->alive = 0;
    if(s->callbacks && s->callbacks->exception){
      s->callbacks->exception(
          SSH_SOCKET_EXCEPTION_ERROR,
          s->last_errno,s->callbacks->userdata);
    }else{
      ssh_set_error(session, SSH_FATAL,
          "Writing packet: error on socket (or connection closed): %s",
          strerror(s->last_errno));
    }

    return SSH_ERROR;
  }

  len = ssh_buffer_get_len(s->out_buffer);
  if (!s->write_wontblock && s->poll_out && len > 0) {
      /* force the poll system to catch pollout events */
      ssh_poll_add_events(s->poll_out, POLLOUT);

      return SSH_AGAIN;
  }
  if (s->write_wontblock && len > 0) {
    w = ssh_socket_unbuffered_write(s, ssh_buffer_get(s->out_buffer), len);
    if (w < 0) {
      session->alive = 0;
      ssh_socket_close(s);

      if(s->callbacks && s->callbacks->exception){
        s->callbacks->exception(
                SSH_SOCKET_EXCEPTION_ERROR,
                s->last_errno,s->callbacks->userdata);
      }else{
        ssh_set_error(session, SSH_FATAL,
                "Writing packet: error on socket (or connection closed): %s",
                strerror(s->last_errno));
      }
      return SSH_ERROR;
    }
    ssh_buffer_pass_bytes(s->out_buffer, w);
    if (s->session->socket_counter != NULL) {
        s->session->socket_counter->out_bytes += w;
    }
  }

  /* Is there some data pending? */
  len = ssh_buffer_get_len(s->out_buffer);
  if (s->poll_out && len > 0) {
      /* force the poll system to catch pollout events */
      ssh_poll_add_events(s->poll_out, POLLOUT);

      return SSH_AGAIN;
  }

  /* all data written */
  return SSH_OK;
}
Ejemplo n.º 9
0
/**
 * @brief               SSH poll callback. This callback will be used when an event
 *                      caught on the socket.
 *
 * @param p             Poll object this callback belongs to.
 * @param fd            The raw socket.
 * @param revents       The current poll events on the socket.
 * @param userdata      Userdata to be passed to the callback function,
 *                      in this case the socket object.
 *
 * @return              0 on success, < 0 when the poll object has been removed
 *                      from its poll context.
 */
int ssh_socket_pollcallback(struct ssh_poll_handle_struct *p, socket_t fd,
                            int revents, void *v_s) {
    ssh_socket s = (ssh_socket)v_s;
    char buffer[MAX_BUF_SIZE];
    int r;
    int err = 0;
    socklen_t errlen = sizeof(err);
    /* Do not do anything if this socket was already closed */
    if (!ssh_socket_is_open(s)) {
        return -1;
    }
    SSH_LOG(SSH_LOG_TRACE, "Poll callback on socket %d (%s%s%s), out buffer %d",fd,
            (revents & POLLIN) ? "POLLIN ":"",
            (revents & POLLOUT) ? "POLLOUT ":"",
            (revents & POLLERR) ? "POLLERR":"",
            ssh_buffer_get_len(s->out_buffer));
    if (revents & POLLERR || revents & POLLHUP) {
        /* Check if we are in a connecting state */
        if (s->state == SSH_SOCKET_CONNECTING) {
            s->state = SSH_SOCKET_ERROR;
            r = getsockopt(fd, SOL_SOCKET, SO_ERROR, (char *)&err, &errlen);
            if (r < 0) {
                err = errno;
            }
            s->last_errno = err;
            ssh_socket_close(s);
            if (s->callbacks && s->callbacks->connected) {
                s->callbacks->connected(SSH_SOCKET_CONNECTED_ERROR, err,
                                        s->callbacks->userdata);
            }
            return -1;
        }
        /* Then we are in a more standard kind of error */
        /* force a read to get an explanation */
        revents |= POLLIN;
    }
    if ((revents & POLLIN) && s->state == SSH_SOCKET_CONNECTED) {
        s->read_wontblock = 1;
        r = ssh_socket_unbuffered_read(s, buffer, sizeof(buffer));
        if (r < 0) {
            if (p != NULL) {
                ssh_poll_remove_events(p, POLLIN);
            }
            if (s->callbacks && s->callbacks->exception) {
                s->callbacks->exception(SSH_SOCKET_EXCEPTION_ERROR,
                                        s->last_errno, s->callbacks->userdata);
                /* p may have been freed, so don't use it
                 * anymore in this function */
                p = NULL;
                return -2;
            }
        }
        if (r == 0) {
            if (p != NULL) {
                ssh_poll_remove_events(p, POLLIN);
            }
            if (p != NULL) {
                ssh_poll_remove_events(p, POLLIN);
            }
            if (s->callbacks && s->callbacks->exception) {
                s->callbacks->exception(SSH_SOCKET_EXCEPTION_EOF,
                                        0, s->callbacks->userdata);
                /* p may have been freed, so don't use it
                 * anymore in this function */
                p = NULL;
                return -2;
            }
        }
        if (r > 0) {
            if (s->session->socket_counter != NULL) {
                s->session->socket_counter->in_bytes += r;
            }
            /* Bufferize the data and then call the callback */
            r = ssh_buffer_add_data(s->in_buffer, buffer, r);
            if (r < 0) {
                return -1;
            }
            if (s->callbacks && s->callbacks->data) {
                do {
                    r = s->callbacks->data(ssh_buffer_get(s->in_buffer),
                                           ssh_buffer_get_len(s->in_buffer),
                                           s->callbacks->userdata);
                    ssh_buffer_pass_bytes(s->in_buffer, r);
                } while ((r > 0) && (s->state == SSH_SOCKET_CONNECTED));
                /* p may have been freed, so don't use it
                 * anymore in this function */
                p = NULL;
            }
        }
    }
#ifdef _WIN32
    if (revents & POLLOUT || revents & POLLWRNORM) {
#else
    if (revents & POLLOUT) {
#endif
        /* First, POLLOUT is a sign we may be connected */
        if (s->state == SSH_SOCKET_CONNECTING) {
            SSH_LOG(SSH_LOG_PACKET, "Received POLLOUT in connecting state");
            s->state = SSH_SOCKET_CONNECTED;
            if (p != NULL) {
                ssh_poll_set_events(p, POLLOUT | POLLIN);
            }
            r = ssh_socket_set_blocking(ssh_socket_get_fd_in(s));
            if (r < 0) {
                return -1;
            }
            if (s->callbacks && s->callbacks->connected) {
                s->callbacks->connected(SSH_SOCKET_CONNECTED_OK, 0,
                                        s->callbacks->userdata);
            }
            return 0;
        }
        /* So, we can write data */
        s->write_wontblock=1;
        if (p != NULL) {
            ssh_poll_remove_events(p, POLLOUT);
        }

        /* If buffered data is pending, write it */
        if (ssh_buffer_get_len(s->out_buffer) > 0) {
            ssh_socket_nonblocking_flush(s);
        } else if (s->callbacks && s->callbacks->controlflow) {
            /* Otherwise advertise the upper level that write can be done */
            SSH_LOG(SSH_LOG_TRACE,"sending control flow event");
            s->callbacks->controlflow(SSH_SOCKET_FLOW_WRITEWONTBLOCK,
                                      s->callbacks->userdata);
        }
        /* TODO: Find a way to put back POLLOUT when buffering occurs */
    }
    /* Return -1 if one of the poll handlers disappeared */
    return (s->poll_in == NULL || s->poll_out == NULL) ? -1 : 0;
}

/** @internal
 * @brief returns the input poll handle corresponding to the socket,
 * creates it if it does not exist.
 * @returns allocated and initialized ssh_poll_handle object
 */
ssh_poll_handle ssh_socket_get_poll_handle_in(ssh_socket s){
	if(s->poll_in)
		return s->poll_in;
	s->poll_in=ssh_poll_new(s->fd_in,0,ssh_socket_pollcallback,s);
	if(s->fd_in == s->fd_out && s->poll_out == NULL)
    s->poll_out=s->poll_in;
	return s->poll_in;
}

/** @internal
 * @brief returns the output poll handle corresponding to the socket,
 * creates it if it does not exist.
 * @returns allocated and initialized ssh_poll_handle object
 */
ssh_poll_handle ssh_socket_get_poll_handle_out(ssh_socket s){
  if(s->poll_out)
    return s->poll_out;
  s->poll_out=ssh_poll_new(s->fd_out,0,ssh_socket_pollcallback,s);
  if(s->fd_in == s->fd_out && s->poll_in == NULL)
    s->poll_in=s->poll_out;
  return s->poll_out;
}

/** \internal
 * \brief Deletes a socket object
 */
void ssh_socket_free(ssh_socket s){
  if (s == NULL) {
    return;
  }
  ssh_socket_close(s);
  ssh_buffer_free(s->in_buffer);
  ssh_buffer_free(s->out_buffer);
  SAFE_FREE(s);
}

#ifndef _WIN32
int ssh_socket_unix(ssh_socket s, const char *path) {
  struct sockaddr_un sunaddr;
  socket_t fd;
  sunaddr.sun_family = AF_UNIX;
  snprintf(sunaddr.sun_path, sizeof(sunaddr.sun_path), "%s", path);

  fd = socket(AF_UNIX, SOCK_STREAM, 0);
  if (fd == SSH_INVALID_SOCKET) {
    ssh_set_error(s->session, SSH_FATAL,
		    "Error from socket(AF_UNIX, SOCK_STREAM, 0): %s",
		    strerror(errno));
    return -1;
  }

  if (fcntl(fd, F_SETFD, 1) == -1) {
    ssh_set_error(s->session, SSH_FATAL,
		    "Error from fcntl(fd, F_SETFD, 1): %s",
		    strerror(errno));
    close(fd);
    return -1;
  }

  if (connect(fd, (struct sockaddr *) &sunaddr,
        sizeof(sunaddr)) < 0) {
    ssh_set_error(s->session, SSH_FATAL, "Error from connect(): %s",
		    strerror(errno));
    close(fd);
    return -1;
  }
  ssh_socket_set_fd(s,fd);
  return 0;
}
#endif

/** \internal
 * \brief closes a socket
 */
void ssh_socket_close(ssh_socket s){
  if (ssh_socket_is_open(s)) {
#ifdef _WIN32
    CLOSE_SOCKET(s->fd_in);
    /* fd_in = fd_out under win32 */
    s->last_errno = WSAGetLastError();
#else
    if (s->fd_out != s->fd_in && s->fd_out != -1) {
        CLOSE_SOCKET(s->fd_out);
    }
    CLOSE_SOCKET(s->fd_in);
    s->last_errno = errno;
#endif
  }

  if(s->poll_in != NULL){
    if(s->poll_out == s->poll_in)
      s->poll_out = NULL;
    ssh_poll_free(s->poll_in);
    s->poll_in=NULL;
  }
  if(s->poll_out != NULL){
    ssh_poll_free(s->poll_out);
    s->poll_out=NULL;
  }

  s->state = SSH_SOCKET_CLOSED;
}

/**
 * @internal
 * @brief sets the file descriptor of the socket.
 * @param[out] s ssh_socket to update
 * @param[in] fd file descriptor to set
 * @warning this function updates boths the input and output
 * file descriptors
 */
void ssh_socket_set_fd(ssh_socket s, socket_t fd) {
    s->fd_in = s->fd_out = fd;

    if (s->poll_in) {
        ssh_poll_set_fd(s->poll_in,fd);
    } else {
        s->state = SSH_SOCKET_CONNECTING;

        /* POLLOUT is the event to wait for in a nonblocking connect */
        ssh_poll_set_events(ssh_socket_get_poll_handle_in(s), POLLOUT);
#ifdef _WIN32
        ssh_poll_add_events(ssh_socket_get_poll_handle_in(s), POLLWRNORM);
#endif
    }
}
Ejemplo n.º 10
0
void packet_parse(SSH_SESSION *session) {
  STRING *error_s = NULL;
  char *error = NULL;
  int type = session->in_packet.type;
  u32 tmp;

#ifdef HAVE_SSH1
  if (session->version == 1) {
    /* SSH-1 */
    switch(type) {
      case SSH_MSG_DISCONNECT:
        ssh_log(session, SSH_LOG_PACKET, "Received SSH_MSG_DISCONNECT");
        ssh_set_error(session, SSH_FATAL, "Received SSH_MSG_DISCONNECT");

        ssh_socket_close(session->socket);
        session->alive = 0;
        return;
      case SSH_SMSG_STDOUT_DATA:
      case SSH_SMSG_STDERR_DATA:
      case SSH_SMSG_EXITSTATUS:
        channel_handle1(session,type);
        return;
      case SSH_MSG_DEBUG:
      case SSH_MSG_IGNORE:
        break;
      default:
        ssh_log(session, SSH_LOG_PACKET,
            "Unexpected message code %d", type);
    }
    return;
  } else {
#endif /* HAVE_SSH1 */
    switch(type) {
      case SSH2_MSG_DISCONNECT:
        buffer_get_u32(session->in_buffer, &tmp);
        error_s = buffer_get_ssh_string(session->in_buffer);
        if (error_s == NULL) {
          return;
        }
        error = string_to_char(error_s);
        string_free(error_s);
        if (error == NULL) {
          return;
        }
        ssh_log(session, SSH_LOG_PACKET, "Received SSH_MSG_DISCONNECT\n");
        ssh_set_error(session, SSH_FATAL,
            "Received SSH_MSG_DISCONNECT: %s",error);

        SAFE_FREE(error);

        ssh_socket_close(session->socket);
        session->alive = 0;

        return;
      case SSH2_MSG_CHANNEL_WINDOW_ADJUST:
      case SSH2_MSG_CHANNEL_DATA:
      case SSH2_MSG_CHANNEL_EXTENDED_DATA:
      case SSH2_MSG_CHANNEL_REQUEST:
      case SSH2_MSG_CHANNEL_EOF:
      case SSH2_MSG_CHANNEL_CLOSE:
        channel_handle(session,type);
      case SSH2_MSG_IGNORE:
      case SSH2_MSG_DEBUG:
        return;
      default:
        ssh_log(session, SSH_LOG_RARE, "Received unhandled packet %d", type);
    }
#ifdef HAVE_SSH1
  }
#endif
}
Ejemplo n.º 11
0
/**
 * @internal
 *
 * @brief A function to be called each time a step has been done in the
 * connection.
 */
static void ssh_server_connection_callback(ssh_session session){
    int rc;

    switch(session->session_state){
        case SSH_SESSION_STATE_NONE:
        case SSH_SESSION_STATE_CONNECTING:
        case SSH_SESSION_STATE_SOCKET_CONNECTED:
            break;
        case SSH_SESSION_STATE_BANNER_RECEIVED:
            if (session->clientbanner == NULL) {
                goto error;
            }
            set_status(session, 0.4f);
            SSH_LOG(SSH_LOG_RARE,
                    "SSH client banner: %s", session->clientbanner);

            /* Here we analyze the different protocols the server allows. */
            rc = ssh_analyze_banner(session, 1);
            if (rc < 0) {
                ssh_set_error(session, SSH_FATAL,
                        "No version of SSH protocol usable (banner: %s)",
                        session->clientbanner);
                goto error;
            }

            /* from now, the packet layer is handling incoming packets */
            session->socket_callbacks.data=ssh_packet_socket_callback;
            ssh_packet_register_socket_callback(session, session->socket);

            ssh_packet_set_default_callbacks(session);
            set_status(session, 0.5f);
            session->session_state=SSH_SESSION_STATE_INITIAL_KEX;
            if (ssh_send_kex(session, 1) < 0) {
                goto error;
            }
            break;
        case SSH_SESSION_STATE_INITIAL_KEX:
            /* TODO: This state should disappear in favor of get_key handle */
            break;
        case SSH_SESSION_STATE_KEXINIT_RECEIVED:
            set_status(session,0.6f);
            if(session->next_crypto->server_kex.methods[0]==NULL){
                if(server_set_kex(session) == SSH_ERROR)
                    goto error;
                /* We are in a rekeying, so we need to send the server kex */
                if(ssh_send_kex(session, 1) < 0)
                    goto error;
            }
            ssh_list_kex(&session->next_crypto->client_kex); // log client kex
            if (ssh_kex_select_methods(session) < 0) {
                goto error;
            }
            if (crypt_set_algorithms_server(session) == SSH_ERROR)
                goto error;
            set_status(session,0.8f);
            session->session_state=SSH_SESSION_STATE_DH;
            break;
        case SSH_SESSION_STATE_DH:
            if(session->dh_handshake_state==DH_STATE_FINISHED){

                rc = ssh_packet_set_newkeys(session, SSH_DIRECTION_IN);
                if (rc != SSH_OK) {
                    goto error;
                }

                /*
                 * If the client supports extension negotiation, we will send
                 * our supported extensions now. This is the first message after
                 * sending NEWKEYS message and after turning on crypto.
                 */
                if (session->extensions & SSH_EXT_NEGOTIATION &&
                    session->session_state != SSH_SESSION_STATE_AUTHENTICATED) {

                    /*
                     * Only send an SSH_MSG_EXT_INFO message the first time the client
                     * undergoes NEWKEYS.  It is unexpected for this message to be sent
                     * upon rekey, and may cause clients to log error messages.
                     *
                     * The session_state can not be used for this purpose because it is
                     * re-set to SSH_SESSION_STATE_KEXINIT_RECEIVED during rekey.  So,
                     * use the connected flag which transitions from non-zero below.
                     *
                     * See also:
                     * - https://bugzilla.mindrot.org/show_bug.cgi?id=2929
                     */
                    if (session->connected == 0) {
                        ssh_server_send_extensions(session);
                    }
                }

                set_status(session,1.0f);
                session->connected = 1;
                session->session_state=SSH_SESSION_STATE_AUTHENTICATING;
                if (session->flags & SSH_SESSION_FLAG_AUTHENTICATED)
                    session->session_state = SSH_SESSION_STATE_AUTHENTICATED;

            }
            break;
        case SSH_SESSION_STATE_AUTHENTICATING:
            break;
        case SSH_SESSION_STATE_ERROR:
            goto error;
        default:
            ssh_set_error(session,SSH_FATAL,"Invalid state %d",session->session_state);
    }

    return;
error:
    ssh_socket_close(session->socket);
    session->alive = 0;
    session->session_state=SSH_SESSION_STATE_ERROR;
}
Ejemplo n.º 12
0
/**
 * @brief Disconnect from a session (client or server).
 * The session can then be reused to open a new session.
 *
 * @param[in]  session  The SSH session to use.
 */
void ssh_disconnect(ssh_session session) {
  ssh_string str = NULL;
  int i;

  if (session == NULL) {
    return;
  }

  enter_function();

  if (ssh_socket_is_open(session->socket)) {
    if (buffer_add_u8(session->out_buffer, SSH2_MSG_DISCONNECT) < 0) {
      goto error;
    }
    if (buffer_add_u32(session->out_buffer,
          htonl(SSH2_DISCONNECT_BY_APPLICATION)) < 0) {
      goto error;
    }

    str = ssh_string_from_char("Bye Bye");
    if (str == NULL) {
      goto error;
    }

    if (buffer_add_ssh_string(session->out_buffer,str) < 0) {
      ssh_string_free(str);
      goto error;
    }
    ssh_string_free(str);

    packet_send(session);
    ssh_socket_close(session->socket);
  }
error:
  session->alive = 0;
  if(session->socket){
    ssh_socket_reset(session->socket);
  }
  session->fd = SSH_INVALID_SOCKET;
  session->session_state=SSH_SESSION_STATE_DISCONNECTED;
  while (session->channels) {
    ssh_channel_free(session->channels);
  }
  if(session->current_crypto){
    crypto_free(session->current_crypto);
    session->current_crypto=NULL;
  }
  if(session->in_buffer)
    buffer_reinit(session->in_buffer);
  if(session->out_buffer)
    buffer_reinit(session->out_buffer);
  if(session->in_hashbuf)
    buffer_reinit(session->in_hashbuf);
  if(session->out_hashbuf)
    buffer_reinit(session->out_hashbuf);
  session->auth_methods = 0;
  SAFE_FREE(session->serverbanner);
  SAFE_FREE(session->clientbanner);
  if (session->client_kex.methods) {
    for (i = 0; i < 10; i++) {
      SAFE_FREE(session->client_kex.methods[i]);
    }
  }

  if (session->server_kex.methods) {
    for (i = 0; i < 10; i++) {
      SAFE_FREE(session->server_kex.methods[i]);
    }
  }
  SAFE_FREE(session->client_kex.methods);
  SAFE_FREE(session->server_kex.methods);
  if(session->ssh_message_list){
    ssh_message msg;
    while((msg=ssh_list_pop_head(ssh_message ,session->ssh_message_list))
        != NULL){
      ssh_message_free(msg);
    }
    ssh_list_free(session->ssh_message_list);
    session->ssh_message_list=NULL;
  }

  if (session->packet_callbacks){
    ssh_list_free(session->packet_callbacks);
    session->packet_callbacks=NULL;
  }

  leave_function();
}
Ejemplo n.º 13
0
/**
 * @brief Disconnect from a session (client or server).
 * The session can then be reused to open a new session.
 *
 * @param[in]  session  The SSH session to use.
 */
void ssh_disconnect(ssh_session session) {
  struct ssh_iterator *it;
  int rc;

  if (session == NULL) {
    return;
  }

  if (session->socket != NULL && ssh_socket_is_open(session->socket)) {
    rc = ssh_buffer_pack(session->out_buffer,
                         "bdss",
                         SSH2_MSG_DISCONNECT,
                         SSH2_DISCONNECT_BY_APPLICATION,
                         "Bye Bye",
                         ""); /* language tag */
    if (rc != SSH_OK){
      ssh_set_error_oom(session);
      goto error;
    }

    ssh_packet_send(session);
    ssh_socket_close(session->socket);
  }
error:
  session->recv_seq = 0;
  session->send_seq = 0;
  session->alive = 0;
  if (session->socket != NULL){
    ssh_socket_reset(session->socket);
  }
  session->opts.fd = SSH_INVALID_SOCKET;
  session->session_state=SSH_SESSION_STATE_DISCONNECTED;

  while ((it=ssh_list_get_iterator(session->channels)) != NULL) {
    ssh_channel_do_free(ssh_iterator_value(ssh_channel,it));
    ssh_list_remove(session->channels, it);
  }
  if(session->current_crypto){
    crypto_free(session->current_crypto);
    session->current_crypto=NULL;
  }
  if (session->next_crypto) {
    crypto_free(session->next_crypto);
    session->next_crypto = crypto_new();
    if (session->next_crypto == NULL) {
      ssh_set_error_oom(session);
    }
  }
  if (session->in_buffer) {
    ssh_buffer_reinit(session->in_buffer);
  }
  if (session->out_buffer) {
    ssh_buffer_reinit(session->out_buffer);
  }
  if (session->in_hashbuf) {
    ssh_buffer_reinit(session->in_hashbuf);
  }
  if (session->out_hashbuf) {
    ssh_buffer_reinit(session->out_hashbuf);
  }
  session->auth.supported_methods = 0;
  SAFE_FREE(session->serverbanner);
  SAFE_FREE(session->clientbanner);

  if(session->ssh_message_list){
    ssh_message msg;
    while((msg=ssh_list_pop_head(ssh_message ,session->ssh_message_list))
        != NULL){
      ssh_message_free(msg);
    }
    ssh_list_free(session->ssh_message_list);
    session->ssh_message_list=NULL;
  }

  if (session->packet_callbacks){
    ssh_list_free(session->packet_callbacks);
    session->packet_callbacks=NULL;
  }
}
Ejemplo n.º 14
0
/**
 * @internal
 *
 * @brief A function to be called each time a step has been done in the
 * connection.
 */
static void ssh_client_connection_callback(ssh_session session)
{
    int rc;

    switch(session->session_state) {
        case SSH_SESSION_STATE_NONE:
        case SSH_SESSION_STATE_CONNECTING:
            break;
        case SSH_SESSION_STATE_SOCKET_CONNECTED:
            ssh_set_fd_towrite(session);
            ssh_send_banner(session, 0);

            break;
        case SSH_SESSION_STATE_BANNER_RECEIVED:
            if (session->serverbanner == NULL) {
                goto error;
            }
            set_status(session, 0.4f);
            SSH_LOG(SSH_LOG_RARE,
                    "SSH server banner: %s", session->serverbanner);

            /* Here we analyze the different protocols the server allows. */
            rc = ssh_analyze_banner(session, 0);
            if (rc < 0) {
                ssh_set_error(session, SSH_FATAL,
                        "No version of SSH protocol usable (banner: %s)",
                        session->serverbanner);
                goto error;
            }

            ssh_packet_register_socket_callback(session, session->socket);

            ssh_packet_set_default_callbacks(session);
            session->session_state = SSH_SESSION_STATE_INITIAL_KEX;
            rc = ssh_set_client_kex(session);
            if (rc != SSH_OK) {
                goto error;
            }
            rc = ssh_send_kex(session, 0);
            if (rc < 0) {
                goto error;
            }
            set_status(session, 0.5f);

            break;
        case SSH_SESSION_STATE_INITIAL_KEX:
            /* TODO: This state should disappear in favor of get_key handle */
            break;
        case SSH_SESSION_STATE_KEXINIT_RECEIVED:
            set_status(session,0.6f);
            ssh_list_kex(&session->next_crypto->server_kex);
            if (session->next_crypto->client_kex.methods[0] == NULL) {
                /* in rekeying state if next_crypto client_kex is empty */
                rc = ssh_set_client_kex(session);
                if (rc != SSH_OK) {
                    goto error;
                }
                rc = ssh_send_kex(session, 0);
                if (rc < 0) {
                    goto error;
                }
            }
            if (ssh_kex_select_methods(session) == SSH_ERROR)
                goto error;
            set_status(session,0.8f);
            session->session_state=SSH_SESSION_STATE_DH;
            if (dh_handshake(session) == SSH_ERROR) {
                goto error;
            }
            /* FALL THROUGH */
        case SSH_SESSION_STATE_DH:
            if(session->dh_handshake_state==DH_STATE_FINISHED){
                set_status(session,1.0f);
                session->connected = 1;
                if (session->flags & SSH_SESSION_FLAG_AUTHENTICATED)
                    session->session_state = SSH_SESSION_STATE_AUTHENTICATED;
                else
                    session->session_state=SSH_SESSION_STATE_AUTHENTICATING;
            }
            break;
        case SSH_SESSION_STATE_AUTHENTICATING:
            break;
        case SSH_SESSION_STATE_ERROR:
            goto error;
        default:
            ssh_set_error(session,SSH_FATAL,"Invalid state %d",session->session_state);
    }

    return;
error:
    ssh_socket_close(session->socket);
    session->alive = 0;
    session->session_state=SSH_SESSION_STATE_ERROR;

}