/* {{{ proto libssh2_session_startup * session: LIBSSH2_SESSION struct allocated and owned by the calling program * Returns: 0 on success, or non-zero on failure * Any memory allocated by libssh2 will use alloc/realloc/free * callbacks in session * socket *must* be populated with an opened and connected socket. */ LIBSSH2_API int libssh2_session_startup(LIBSSH2_SESSION * session, int sock) { int rc; if (session->startup_state == libssh2_NB_state_idle) { _libssh2_debug(session, LIBSSH2_DBG_TRANS, "session_startup for socket %d", sock); /* FIXME: on some platforms (like win32) sockets are unsigned */ if (sock < 0) { /* Did we forget something? */ libssh2_error(session, LIBSSH2_ERROR_SOCKET_NONE, "Bad socket provided", 0); return LIBSSH2_ERROR_SOCKET_NONE; } session->socket_fd = sock; session->socket_block = !_libssh2_get_socket_nonblocking(session->socket_fd); if (session->socket_block) { /* * Since we can't be sure that we are in blocking or there * was an error detecting the state, so set to blocking to * be sure */ _libssh2_nonblock(session->socket_fd, 0); } session->startup_state = libssh2_NB_state_created; } /* TODO: Liveness check */ if (session->startup_state == libssh2_NB_state_created) { rc = libssh2_banner_send(session); if (rc == PACKET_EAGAIN) { libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block sending banner to remote host", 0); return LIBSSH2_ERROR_EAGAIN; } else if (rc) { /* Unable to send banner? */ libssh2_error(session, LIBSSH2_ERROR_BANNER_SEND, "Error sending banner to remote host", 0); return LIBSSH2_ERROR_BANNER_SEND; } session->startup_state = libssh2_NB_state_sent; } if (session->startup_state == libssh2_NB_state_sent) { rc = libssh2_banner_receive(session); if (rc == PACKET_EAGAIN) { libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block waiting for banner", 0); return LIBSSH2_ERROR_EAGAIN; } else if (rc) { /* Unable to receive banner from remote */ libssh2_error(session, LIBSSH2_ERROR_BANNER_NONE, "Timeout waiting for banner", 0); return LIBSSH2_ERROR_BANNER_NONE; } session->startup_state = libssh2_NB_state_sent1; } if (session->startup_state == libssh2_NB_state_sent1) { rc = libssh2_kex_exchange(session, 0, &session->startup_key_state); if (rc == PACKET_EAGAIN) { libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block exchanging encryption keys", 0); return LIBSSH2_ERROR_EAGAIN; } else if (rc) { libssh2_error(session, LIBSSH2_ERROR_KEX_FAILURE, "Unable to exchange encryption keys", 0); return LIBSSH2_ERROR_KEX_FAILURE; } session->startup_state = libssh2_NB_state_sent2; } if (session->startup_state == libssh2_NB_state_sent2) { _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Requesting userauth service"); /* Request the userauth service */ session->startup_service[0] = SSH_MSG_SERVICE_REQUEST; libssh2_htonu32(session->startup_service + 1, sizeof("ssh-userauth") - 1); memcpy(session->startup_service + 5, "ssh-userauth", sizeof("ssh-userauth") - 1); session->startup_state = libssh2_NB_state_sent3; } if (session->startup_state == libssh2_NB_state_sent3) { rc = libssh2_packet_write(session, session->startup_service, sizeof("ssh-userauth") + 5 - 1); if (rc == PACKET_EAGAIN) { libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block asking for ssh-userauth service", 0); return LIBSSH2_ERROR_EAGAIN; } else if (rc) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to ask for ssh-userauth service", 0); return LIBSSH2_ERROR_SOCKET_SEND; } session->startup_state = libssh2_NB_state_sent4; } if (session->startup_state == libssh2_NB_state_sent4) { rc = libssh2_packet_require_ex(session, SSH_MSG_SERVICE_ACCEPT, &session->startup_data, &session->startup_data_len, 0, NULL, 0, &session->startup_req_state); if (rc == PACKET_EAGAIN) { return LIBSSH2_ERROR_EAGAIN; } else if (rc) { return LIBSSH2_ERROR_SOCKET_DISCONNECT; } session->startup_service_length = libssh2_ntohu32(session->startup_data + 1); if ((session->startup_service_length != (sizeof("ssh-userauth") - 1)) || strncmp("ssh-userauth", (char *) session->startup_data + 5, session->startup_service_length)) { LIBSSH2_FREE(session, session->startup_data); session->startup_data = NULL; libssh2_error(session, LIBSSH2_ERROR_PROTO, "Invalid response received from server", 0); return LIBSSH2_ERROR_PROTO; } LIBSSH2_FREE(session, session->startup_data); session->startup_data = NULL; session->startup_state = libssh2_NB_state_idle; return 0; } /* just for safety return some error */ return LIBSSH2_ERROR_INVAL; }
/* {{{ libssh2_publickey_add_ex * Add a new public key entry */ LIBSSH2_API int libssh2_publickey_add_ex(LIBSSH2_PUBLICKEY *pkey, const unsigned char *name, unsigned long name_len, const unsigned char *blob, unsigned long blob_len, char overwrite, unsigned long num_attrs, libssh2_publickey_attribute attrs[]) { LIBSSH2_CHANNEL *channel = pkey->channel; LIBSSH2_SESSION *session = channel->session; unsigned char *packet = NULL, *s; unsigned long i, packet_len = 19 + name_len + blob_len; unsigned char *comment = NULL; unsigned long comment_len = 0; /* packet_len(4) + add_len(4) + "add"(3) + name_len(4) + {name} blob_len(4) + {blob} */ #ifdef LIBSSH2_DEBUG_PUBLICKEY _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "Adding %s pubickey", name); #endif if (pkey->version == 1) { for(i = 0; i < num_attrs; i++) { /* Search for a comment attribute */ if (attrs[i].name_len == (sizeof("comment") - 1) && strncmp(attrs[i].name, "comment", sizeof("comment") - 1) == 0) { comment = attrs[i].value; comment_len = attrs[i].value_len; break; } } packet_len += 4 + comment_len; } else { packet_len += 5; /* overwrite(1) + attribute_count(4) */ for(i = 0; i < num_attrs; i++) { packet_len += 9 + attrs[i].name_len + attrs[i].value_len; /* name_len(4) + value_len(4) + mandatory(1) */ } } packet = LIBSSH2_ALLOC(session, packet_len); if (!packet) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for publickey \"add\" packet", 0); return -1; } s = packet; libssh2_htonu32(s, packet_len - 4); s += 4; libssh2_htonu32(s, sizeof("add") - 1); s += 4; memcpy(s, "add", sizeof("add") - 1); s += sizeof("add") - 1; if (pkey->version == 1) { libssh2_htonu32(s, comment_len); s += 4; if (comment) { memcpy(s, comment, comment_len); s += comment_len; } libssh2_htonu32(s, name_len); s += 4; memcpy(s, name, name_len); s += name_len; libssh2_htonu32(s, blob_len); s += 4; memcpy(s, blob, blob_len); s += blob_len; } else { /* Version == 2 */ libssh2_htonu32(s, name_len); s += 4; memcpy(s, name, name_len); s += name_len; libssh2_htonu32(s, blob_len); s += 4; memcpy(s, blob, blob_len); s += blob_len; *(s++) = overwrite ? 0xFF : 0; libssh2_htonu32(s, num_attrs); s += 4; for(i = 0; i < num_attrs; i++) { libssh2_htonu32(s, attrs[i].name_len); s += 4; memcpy(s, attrs[i].name, attrs[i].name_len); s += attrs[i].name_len; libssh2_htonu32(s, attrs[i].value_len); s += 4; memcpy(s, attrs[i].value, attrs[i].value_len); s += attrs[i].value_len; *(s++) = attrs[i].mandatory ? 0xFF : 0; } } #ifdef LIBSSH2_DEBUG_PUBLICKEY _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "Sending publickey \"add\" packet: type=%s blob_len=%ld num_attrs=%ld", name, blob_len, num_attrs); #endif if ((s - packet) != libssh2_channel_write(channel, packet, (s - packet))) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send publickey add packet", 0); LIBSSH2_FREE(session, packet); return -1; } LIBSSH2_FREE(session, packet); packet = NULL; return libssh2_publickey_response_success(pkey); }
/* {{{ libssh2_publickey_list_fetch * Fetch a list of supported public key from a server */ LIBSSH2_API int libssh2_publickey_list_fetch(LIBSSH2_PUBLICKEY *pkey, unsigned long *num_keys, libssh2_publickey_list **pkey_list) { LIBSSH2_CHANNEL *channel = pkey->channel; LIBSSH2_SESSION *session = channel->session; libssh2_publickey_list *list = NULL; unsigned char *s, buffer[12], *data = NULL; unsigned long buffer_len = 12, keys = 0, max_keys = 0, data_len, i, response; /* packet_len(4) + list_len(4) + "list"(4) */ s = buffer; libssh2_htonu32(s, buffer_len - 4); s += 4; libssh2_htonu32(s, sizeof("list") - 1); s += 4; memcpy(s, "list", sizeof("list") - 1); s += sizeof("list") - 1; #ifdef LIBSSH2_DEBUG_PUBLICKEY _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "Sending publickey \"list\" packet"); #endif if ((s - buffer) != libssh2_channel_write(channel, buffer, (s - buffer))) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send publickey list packet", 0); return -1; } while (1) { if (libssh2_publickey_packet_receive(pkey, &data, &data_len)) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for response from publickey subsystem", 0); goto err_exit; } s = data; if ((response = libssh2_publickey_response_id(&s, data_len)) < 0) { libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, "Invalid publickey subsystem response code", 0); goto err_exit; } switch (response) { case LIBSSH2_PUBLICKEY_RESPONSE_STATUS: /* Error, or processing complete */ { unsigned long status, descr_len, lang_len; unsigned char *descr, *lang; status = libssh2_ntohu32(s); s += 4; descr_len = libssh2_ntohu32(s); s += 4; descr = s; s += descr_len; lang_len = libssh2_ntohu32(s); s += 4; lang = s; s += lang_len; if (s > data + data_len) { libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, "Malformed publickey subsystem packet", 0); goto err_exit; } if (status == LIBSSH2_PUBLICKEY_SUCCESS) { LIBSSH2_FREE(session, data); *pkey_list = list; *num_keys = keys; return 0; } libssh2_publickey_status_error(pkey, session, status, descr, descr_len); goto err_exit; } case LIBSSH2_PUBLICKEY_RESPONSE_PUBLICKEY: /* What we want */ if (keys >= max_keys) { /* Grow the key list if necessary */ max_keys += 8; list = LIBSSH2_REALLOC(session, list, (max_keys + 1) * sizeof(libssh2_publickey_list)); if (!list) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for publickey list", 0); goto err_exit; } } if (pkey->version == 1) { unsigned long comment_len; comment_len = libssh2_ntohu32(s); s += 4; if (comment_len) { list[keys].num_attrs = 1; list[keys].attrs = LIBSSH2_ALLOC(session, sizeof(libssh2_publickey_attribute)); if (!list[keys].attrs) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for publickey attributes", 0); goto err_exit; } list[keys].attrs[0].name = "comment"; list[keys].attrs[0].name_len = sizeof("comment") - 1; list[keys].attrs[0].value = s; list[keys].attrs[0].value_len = comment_len; list[keys].attrs[0].mandatory = 0; s += comment_len; } else { list[keys].num_attrs = 0; list[keys].attrs = NULL; } list[keys].name_len = libssh2_ntohu32(s); s += 4; list[keys].name = s; s += list[keys].name_len; list[keys].blob_len = libssh2_ntohu32(s); s += 4; list[keys].blob = s; s += list[keys].blob_len; } else { /* Version == 2 */ list[keys].name_len = libssh2_ntohu32(s); s += 4; list[keys].name = s; s += list[keys].name_len; list[keys].blob_len = libssh2_ntohu32(s); s += 4; list[keys].blob = s; s += list[keys].blob_len; list[keys].num_attrs = libssh2_ntohu32(s); s += 4; if (list[keys].num_attrs) { list[keys].attrs = LIBSSH2_ALLOC(session, list[keys].num_attrs * sizeof(libssh2_publickey_attribute)); if (!list[keys].attrs) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for publickey attributes", 0); goto err_exit; } for(i = 0; i < list[keys].num_attrs; i++) { list[keys].attrs[i].name_len = libssh2_ntohu32(s); s += 4; list[keys].attrs[i].name = s; s += list[keys].attrs[i].name_len; list[keys].attrs[i].value_len = libssh2_ntohu32(s); s += 4; list[keys].attrs[i].value = s; s += list[keys].attrs[i].value_len; list[keys].attrs[i].mandatory = 0; /* actually an ignored value */ } } else { list[keys].attrs = NULL; } } list[keys].packet = data; /* To be FREEd in libssh2_publickey_list_free() */ keys++; list[keys].packet = NULL; /* Terminate the list */ data = NULL; break; default: /* Unknown/Unexpected */ libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, "Unexpected publickey subsystem response, ignoring", 0); LIBSSH2_FREE(session, data); } } /* Only reached via explicit goto */ err_exit: if (data) { LIBSSH2_FREE(session, data); } if (list) { libssh2_publickey_list_free(pkey, list); } return -1; }
int _libssh2_pub_priv_keyfilememory(LIBSSH2_SESSION *session, unsigned char **method, size_t *method_len, unsigned char **pubkeydata, size_t *pubkeydata_len, const char *privatekeydata, size_t privatekeydata_len, const char *passphrase) { int st; BIO* bp; EVP_PKEY* pk; _libssh2_debug(session, LIBSSH2_TRACE_AUTH, "Computing public key from private key."); bp = BIO_new_mem_buf((char *)privatekeydata, privatekeydata_len); if (!bp) { return -1; } if (!EVP_get_cipherbyname("des")) { /* If this cipher isn't loaded it's a pretty good indication that none * are. I have *NO DOUBT* that there's a better way to deal with this * ($#&%#$(%$#( Someone buy me an OpenSSL manual and I'll read up on * it. */ OpenSSL_add_all_ciphers(); } BIO_reset(bp); pk = PEM_read_bio_PrivateKey(bp, NULL, NULL, (void*)passphrase); BIO_free(bp); if (pk == NULL) { return _libssh2_error(session, LIBSSH2_ERROR_FILE, "Unable to extract public key " "from private key file: " "Wrong passphrase or invalid/unrecognized " "private key file format"); } switch (pk->type) { case EVP_PKEY_RSA : st = gen_publickey_from_rsa_evp(session, method, method_len, pubkeydata, pubkeydata_len, pk); break; case EVP_PKEY_DSA : st = gen_publickey_from_dsa_evp(session, method, method_len, pubkeydata, pubkeydata_len, pk); break; default : st = _libssh2_error(session, LIBSSH2_ERROR_FILE, "Unable to extract public key " "from private key file: " "Unsupported private key file format"); break; } EVP_PKEY_free(pk); return st; }
/* {{{ libssh2_publickey_init * Startup the publickey subsystem */ LIBSSH2_API LIBSSH2_PUBLICKEY *libssh2_publickey_init(LIBSSH2_SESSION *session) { LIBSSH2_PUBLICKEY *pkey = NULL; LIBSSH2_CHANNEL *channel = NULL; unsigned char buffer[19]; /* packet_len(4) + version_len(4) + "version"(7) + version_num(4) */ unsigned char *s, *data = NULL; unsigned long data_len; int response; #ifdef LIBSSH2_DEBUG_PUBLICKEY _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "Initializing publickey subsystem"); #endif channel = libssh2_channel_open_session(session); if (!channel) { libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE, "Unable to startup channel", 0); goto err_exit; } if (libssh2_channel_subsystem(channel, "publickey")) { libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE, "Unable to request publickey subsystem", 0); goto err_exit; } libssh2_channel_set_blocking(channel, 1); libssh2_channel_handle_extended_data(channel, LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE); pkey = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_PUBLICKEY)); if (!pkey) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate a new publickey structure", 0); goto err_exit; } pkey->channel = channel; pkey->version = 0; s = buffer; libssh2_htonu32(s, 4 + (sizeof("version") - 1) + 4); s += 4; libssh2_htonu32(s, sizeof("version") - 1); s += 4; memcpy(s, "version", sizeof("version") - 1); s += sizeof("version") - 1; libssh2_htonu32(s, LIBSSH2_PUBLICKEY_VERSION); s += 4; #ifdef LIBSSH2_DEBUG_PUBLICKEY _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "Sending publickey version packet advertising version %d support", (int)LIBSSH2_PUBLICKEY_VERSION); #endif if ((s - buffer) != libssh2_channel_write(channel, buffer, (s - buffer))) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send publickey version packet", 0); goto err_exit; } while (1) { if (libssh2_publickey_packet_receive(pkey, &data, &data_len)) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for response from publickey subsystem", 0); goto err_exit; } s = data; if ((response = libssh2_publickey_response_id(&s, data_len)) < 0) { libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, "Invalid publickey subsystem response code", 0); goto err_exit; } switch (response) { case LIBSSH2_PUBLICKEY_RESPONSE_STATUS: /* Error */ { unsigned long status, descr_len, lang_len; unsigned char *descr, *lang; status = libssh2_ntohu32(s); s += 4; descr_len = libssh2_ntohu32(s); s += 4; descr = s; s += descr_len; lang_len = libssh2_ntohu32(s); s += 4; lang = s; s += lang_len; if (s > data + data_len) { libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, "Malformed publickey subsystem packet", 0); goto err_exit; } libssh2_publickey_status_error(NULL, session, status, descr, descr_len); goto err_exit; } case LIBSSH2_PUBLICKEY_RESPONSE_VERSION: /* What we want */ pkey->version = libssh2_ntohu32(s); if (pkey->version > LIBSSH2_PUBLICKEY_VERSION) { #ifdef LIBSSH2_DEBUG_PUBLICKEY _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "Truncating remote publickey version from %lu", pkey->version); #endif pkey->version = LIBSSH2_PUBLICKEY_VERSION; } #ifdef LIBSSH2_DEBUG_PUBLICKEY _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "Enabling publickey subsystem version %lu", pkey->version); #endif LIBSSH2_FREE(session, data); return pkey; default: /* Unknown/Unexpected */ libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, "Unexpected publickey subsystem response, ignoring", 0); LIBSSH2_FREE(session, data); data = NULL; } } /* Never reached except by direct goto */ err_exit: if (channel) { libssh2_channel_close(channel); } if (pkey) { LIBSSH2_FREE(session, pkey); } if (data) { LIBSSH2_FREE(session, data); } return NULL; }
/* {{{ libssh2_session_disconnect_ex */ LIBSSH2_API int libssh2_session_disconnect_ex(LIBSSH2_SESSION * session, int reason, const char *description, const char *lang) { unsigned char *s; unsigned long descr_len = 0, lang_len = 0; int rc; if (session->disconnect_state == libssh2_NB_state_idle) { _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Disconnecting: reason=%d, desc=%s, lang=%s", reason, description, lang); if (description) { descr_len = strlen(description); } if (lang) { lang_len = strlen(lang); } /* 13 = packet_type(1) + reason code(4) + descr_len(4) + lang_len(4) */ session->disconnect_data_len = descr_len + lang_len + 13; s = session->disconnect_data = LIBSSH2_ALLOC(session, session->disconnect_data_len); if (!session->disconnect_data) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for disconnect packet", 0); session->disconnect_state = libssh2_NB_state_idle; return -1; } *(s++) = SSH_MSG_DISCONNECT; libssh2_htonu32(s, reason); s += 4; libssh2_htonu32(s, descr_len); s += 4; if (description) { memcpy(s, description, descr_len); s += descr_len; } libssh2_htonu32(s, lang_len); s += 4; if (lang) { memcpy(s, lang, lang_len); s += lang_len; } session->disconnect_state = libssh2_NB_state_created; } rc = libssh2_packet_write(session, session->disconnect_data, session->disconnect_data_len); if (rc == PACKET_EAGAIN) { return PACKET_EAGAIN; } LIBSSH2_FREE(session, session->disconnect_data); session->disconnect_data = NULL; session->disconnect_state = libssh2_NB_state_idle; return 0; }
int _libssh2_pub_priv_keyfilememory(LIBSSH2_SESSION *session, unsigned char **method, size_t *method_len, unsigned char **pubkeydata, size_t *pubkeydata_len, const char *privatekeydata, size_t privatekeydata_len, const char *passphrase) { int st; BIO* bp; EVP_PKEY* pk; int pktype; _libssh2_debug(session, LIBSSH2_TRACE_AUTH, "Computing public key from private key."); bp = BIO_new_mem_buf((char *)privatekeydata, privatekeydata_len); if(!bp) { return -1; } BIO_reset(bp); pk = PEM_read_bio_PrivateKey(bp, NULL, NULL, (void *)passphrase); BIO_free(bp); if(pk == NULL) { return _libssh2_error(session, LIBSSH2_ERROR_FILE, "Unable to extract public key " "from private key file: " "Wrong passphrase or invalid/unrecognized " "private key file format"); } #ifdef HAVE_OPAQUE_STRUCTS pktype = EVP_PKEY_id(pk); #else pktype = pk->type; #endif switch(pktype) { case EVP_PKEY_RSA : st = gen_publickey_from_rsa_evp(session, method, method_len, pubkeydata, pubkeydata_len, pk); break; #if LIBSSH2_DSA case EVP_PKEY_DSA : st = gen_publickey_from_dsa_evp(session, method, method_len, pubkeydata, pubkeydata_len, pk); break; #endif /* LIBSSH_DSA */ default : st = _libssh2_error(session, LIBSSH2_ERROR_FILE, "Unable to extract public key " "from private key file: " "Unsupported private key file format"); break; } EVP_PKEY_free(pk); return st; }
/* {{{ libssh2_packet_new * Create a new packet and attach it to the brigade */ int libssh2_packet_add(LIBSSH2_SESSION * session, unsigned char *data, size_t datalen, int macstate) { int rc; if (session->packAdd_state == libssh2_NB_state_idle) { session->packAdd_data_head = 0; /* Zero the whole thing out */ memset(&session->packAdd_key_state, 0, sizeof(session->packAdd_key_state)); /* Zero the whole thing out */ memset(&session->packAdd_Qlstn_state, 0, sizeof(session->packAdd_Qlstn_state)); /* Zero the whole thing out */ memset(&session->packAdd_x11open_state, 0, sizeof(session->packAdd_x11open_state)); _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Packet type %d received, length=%d", (int) data[0], (int) datalen); if (macstate == LIBSSH2_MAC_INVALID) { if (session->macerror) { if (LIBSSH2_MACERROR(session, (char *) data, datalen) == 0) { /* Calling app has given the OK, Process it anyway */ macstate = LIBSSH2_MAC_CONFIRMED; } else { libssh2_error(session, LIBSSH2_ERROR_INVALID_MAC, "Invalid Message Authentication Code received", 0); if (session->ssh_msg_disconnect) { LIBSSH2_DISCONNECT(session, SSH_DISCONNECT_MAC_ERROR, "Invalid MAC received", sizeof("Invalid MAC received") - 1, "", 0); } LIBSSH2_FREE(session, data); return -1; } } else { libssh2_error(session, LIBSSH2_ERROR_INVALID_MAC, "Invalid Message Authentication Code received", 0); if (session->ssh_msg_disconnect) { LIBSSH2_DISCONNECT(session, SSH_DISCONNECT_MAC_ERROR, "Invalid MAC received", sizeof("Invalid MAC received") - 1, "", 0); } LIBSSH2_FREE(session, data); return -1; } } session->packAdd_state = libssh2_NB_state_allocated; } /* * =============================== NOTE =============================== * I know this is very ugly and not a really good use of "goto", but * this case statement would be even uglier to do it any other way */ if (session->packAdd_state == libssh2_NB_state_jump1) { goto libssh2_packet_add_jump_point1; } else if (session->packAdd_state == libssh2_NB_state_jump2) { goto libssh2_packet_add_jump_point2; } else if (session->packAdd_state == libssh2_NB_state_jump3) { goto libssh2_packet_add_jump_point3; } if (session->packAdd_state == libssh2_NB_state_allocated) { /* A couple exceptions to the packet adding rule: */ switch (data[0]) { case SSH_MSG_DISCONNECT: { char *message, *language; int reason, message_len, language_len; reason = libssh2_ntohu32(data + 1); message_len = libssh2_ntohu32(data + 5); /* 9 = packet_type(1) + reason(4) + message_len(4) */ message = (char *) data + 9; language_len = libssh2_ntohu32(data + 9 + message_len); /* * This is where we hack on the data a little, * Use the MSB of language_len to to a terminating NULL * (In all liklihood it is already) * Shift the language tag back a byte (In all likelihood * it's zero length anyway) * Store a NULL in the last byte of the packet to terminate * the language string * With the lengths passed this isn't *REALLY* necessary, * but it's "kind" */ message[message_len] = '\0'; language = (char *) data + 9 + message_len + 3; if (language_len) { memcpy(language, language + 1, language_len); } language[language_len] = '\0'; if (session->ssh_msg_disconnect) { LIBSSH2_DISCONNECT(session, reason, message, message_len, language, language_len); } _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Disconnect(%d): %s(%s)", reason, message, language); LIBSSH2_FREE(session, data); session->socket_state = LIBSSH2_SOCKET_DISCONNECTED; session->packAdd_state = libssh2_NB_state_idle; return -1; } break; case SSH_MSG_IGNORE: /* As with disconnect, back it up one and add a trailing NULL */ memcpy(data + 4, data + 5, datalen - 5); data[datalen] = '\0'; if (session->ssh_msg_ignore) { LIBSSH2_IGNORE(session, (char *) data + 4, datalen - 5); } LIBSSH2_FREE(session, data); session->packAdd_state = libssh2_NB_state_idle; return 0; break; case SSH_MSG_DEBUG: { int always_display = data[0]; char *message, *language; int message_len, language_len; message_len = libssh2_ntohu32(data + 2); /* 6 = packet_type(1) + display(1) + message_len(4) */ message = (char *) data + 6; language_len = libssh2_ntohu32(data + 6 + message_len); /* * This is where we hack on the data a little, * Use the MSB of language_len to to a terminating NULL * (In all liklihood it is already) * Shift the language tag back a byte (In all likelihood * it's zero length anyway) * Store a NULL in the last byte of the packet to terminate * the language string * With the lengths passed this isn't *REALLY* necessary, * but it's "kind" */ message[message_len] = '\0'; language = (char *) data + 6 + message_len + 3; if (language_len) { memcpy(language, language + 1, language_len); } language[language_len] = '\0'; if (session->ssh_msg_debug) { LIBSSH2_DEBUG(session, always_display, message, message_len, language, language_len); } /* * _libssh2_debug will actually truncate this for us so * that it's not an inordinate about of data */ _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Debug Packet: %s", message); LIBSSH2_FREE(session, data); session->packAdd_state = libssh2_NB_state_idle; return 0; } break; case SSH_MSG_CHANNEL_EXTENDED_DATA: /* streamid(4) */ session->packAdd_data_head += 4; case SSH_MSG_CHANNEL_DATA: /* packet_type(1) + channelno(4) + datalen(4) */ session->packAdd_data_head += 9; { session->packAdd_channel = libssh2_channel_locate(session, libssh2_ntohu32 (data + 1)); if (!session->packAdd_channel) { libssh2_error(session, LIBSSH2_ERROR_CHANNEL_UNKNOWN, "Packet received for unknown channel, ignoring", 0); LIBSSH2_FREE(session, data); session->packAdd_state = libssh2_NB_state_idle; return 0; } #ifdef LIBSSH2DEBUG { unsigned long stream_id = 0; if (data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA) { stream_id = libssh2_ntohu32(data + 5); } _libssh2_debug(session, LIBSSH2_DBG_CONN, "%d bytes received for channel %lu/%lu stream #%lu", (int) (datalen - session->packAdd_data_head), session->packAdd_channel->local.id, session->packAdd_channel->remote.id, stream_id); } #endif if ((session->packAdd_channel->remote. extended_data_ignore_mode == LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE) && (data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA)) { /* Pretend we didn't receive this */ LIBSSH2_FREE(session, data); _libssh2_debug(session, LIBSSH2_DBG_CONN, "Ignoring extended data and refunding %d bytes", (int) (datalen - 13)); /* Adjust the window based on the block we just freed */ libssh2_packet_add_jump_point1: session->packAdd_state = libssh2_NB_state_jump1; rc = libssh2_channel_receive_window_adjust(session-> packAdd_channel, datalen - 13, 0); if (rc == PACKET_EAGAIN) { return PACKET_EAGAIN; } session->packAdd_state = libssh2_NB_state_idle; return 0; } /* * REMEMBER! remote means remote as source of data, * NOT remote window! */ if (session->packAdd_channel->remote.packet_size < (datalen - session->packAdd_data_head)) { /* * Spec says we MAY ignore bytes sent beyond * packet_size */ libssh2_error(session, LIBSSH2_ERROR_CHANNEL_PACKET_EXCEEDED, "Packet contains more data than we offered to receive, truncating", 0); datalen = session->packAdd_channel->remote.packet_size + session->packAdd_data_head; } if (session->packAdd_channel->remote.window_size <= 0) { /* * Spec says we MAY ignore bytes sent beyond * window_size */ libssh2_error(session, LIBSSH2_ERROR_CHANNEL_WINDOW_EXCEEDED, "The current receive window is full, data ignored", 0); LIBSSH2_FREE(session, data); session->packAdd_state = libssh2_NB_state_idle; return 0; } /* Reset EOF status */ session->packAdd_channel->remote.eof = 0; if ((datalen - session->packAdd_data_head) > session->packAdd_channel->remote.window_size) { libssh2_error(session, LIBSSH2_ERROR_CHANNEL_WINDOW_EXCEEDED, "Remote sent more data than current window allows, truncating", 0); datalen = session->packAdd_channel->remote.window_size + session->packAdd_data_head; } else { /* Now that we've received it, shrink our window */ session->packAdd_channel->remote.window_size -= datalen - session->packAdd_data_head; } } break; case SSH_MSG_CHANNEL_EOF: { session->packAdd_channel = libssh2_channel_locate(session, libssh2_ntohu32 (data + 1)); if (!session->packAdd_channel) { /* We may have freed already, just quietly ignore this... */ LIBSSH2_FREE(session, data); session->packAdd_state = libssh2_NB_state_idle; return 0; } _libssh2_debug(session, LIBSSH2_DBG_CONN, "EOF received for channel %lu/%lu", session->packAdd_channel->local.id, session->packAdd_channel->remote.id); session->packAdd_channel->remote.eof = 1; LIBSSH2_FREE(session, data); session->packAdd_state = libssh2_NB_state_idle; return 0; } break; case SSH_MSG_CHANNEL_REQUEST: { if (libssh2_ntohu32(data + 5) == sizeof("exit-status") - 1 && !memcmp("exit-status", data + 9, sizeof("exit-status") - 1)) { /* we've got "exit-status" packet. Set the session value */ session->packAdd_channel = libssh2_channel_locate(session, libssh2_ntohu32(data + 1)); if (session->packAdd_channel) { session->packAdd_channel->exit_status = libssh2_ntohu32(data + 9 + sizeof("exit-status")); _libssh2_debug(session, LIBSSH2_DBG_CONN, "Exit status %lu received for channel %lu/%lu", session->packAdd_channel->exit_status, session->packAdd_channel->local.id, session->packAdd_channel->remote.id); } LIBSSH2_FREE(session, data); session->packAdd_state = libssh2_NB_state_idle; return 0; } } break; case SSH_MSG_CHANNEL_CLOSE: { session->packAdd_channel = libssh2_channel_locate(session, libssh2_ntohu32 (data + 1)); if (!session->packAdd_channel) { /* We may have freed already, just quietly ignore this... */ LIBSSH2_FREE(session, data); session->packAdd_state = libssh2_NB_state_idle; return 0; } _libssh2_debug(session, LIBSSH2_DBG_CONN, "Close received for channel %lu/%lu", session->packAdd_channel->local.id, session->packAdd_channel->remote.id); session->packAdd_channel->remote.close = 1; session->packAdd_channel->remote.eof = 1; /* TODO: Add a callback for this */ LIBSSH2_FREE(session, data); session->packAdd_state = libssh2_NB_state_idle; return 0; } break; case SSH_MSG_CHANNEL_OPEN: if ((datalen >= (sizeof("forwarded-tcpip") + 4)) && ((sizeof("forwarded-tcpip") - 1) == libssh2_ntohu32(data + 1)) && (memcmp (data + 5, "forwarded-tcpip", sizeof("forwarded-tcpip") - 1) == 0)) { libssh2_packet_add_jump_point2: session->packAdd_state = libssh2_NB_state_jump2; rc = libssh2_packet_queue_listener(session, data, datalen, &session-> packAdd_Qlstn_state); if (rc == PACKET_EAGAIN) { return PACKET_EAGAIN; } LIBSSH2_FREE(session, data); session->packAdd_state = libssh2_NB_state_idle; return rc; } if ((datalen >= (sizeof("x11") + 4)) && ((sizeof("x11") - 1) == libssh2_ntohu32(data + 1)) && (memcmp(data + 5, "x11", sizeof("x11") - 1) == 0)) { libssh2_packet_add_jump_point3: session->packAdd_state = libssh2_NB_state_jump3; rc = libssh2_packet_x11_open(session, data, datalen, &session->packAdd_x11open_state); if (rc == PACKET_EAGAIN) { return PACKET_EAGAIN; } LIBSSH2_FREE(session, data); session->packAdd_state = libssh2_NB_state_idle; return rc; } break; case SSH_MSG_CHANNEL_WINDOW_ADJUST: { unsigned long bytestoadd = libssh2_ntohu32(data + 5); session->packAdd_channel = libssh2_channel_locate(session, libssh2_ntohu32 (data + 1)); if (session->packAdd_channel && bytestoadd) { session->packAdd_channel->local.window_size += bytestoadd; } _libssh2_debug(session, LIBSSH2_DBG_CONN, "Window adjust received for channel %lu/%lu, adding %lu bytes, new window_size=%lu", session->packAdd_channel->local.id, session->packAdd_channel->remote.id, bytestoadd, session->packAdd_channel->local.window_size); LIBSSH2_FREE(session, data); session->packAdd_state = libssh2_NB_state_idle; return 0; } break; } session->packAdd_state = libssh2_NB_state_sent; } if (session->packAdd_state == libssh2_NB_state_sent) { session->packAdd_packet = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_PACKET)); if (!session->packAdd_packet) { _libssh2_debug(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for LIBSSH2_PACKET"); LIBSSH2_FREE(session, data); session->packAdd_state = libssh2_NB_state_idle; return -1; } memset(session->packAdd_packet, 0, sizeof(LIBSSH2_PACKET)); session->packAdd_packet->data = data; session->packAdd_packet->data_len = datalen; session->packAdd_packet->data_head = session->packAdd_data_head; session->packAdd_packet->mac = macstate; session->packAdd_packet->brigade = &session->packets; session->packAdd_packet->next = NULL; if (session->packets.tail) { session->packAdd_packet->prev = session->packets.tail; session->packAdd_packet->prev->next = session->packAdd_packet; session->packets.tail = session->packAdd_packet; } else { session->packets.head = session->packAdd_packet; session->packets.tail = session->packAdd_packet; session->packAdd_packet->prev = NULL; } session->packAdd_state = libssh2_NB_state_sent1; } if ((data[0] == SSH_MSG_KEXINIT && !(session->state & LIBSSH2_STATE_EXCHANGING_KEYS)) || (session->packAdd_state == libssh2_NB_state_sent2)) { if (session->packAdd_state == libssh2_NB_state_sent1) { /* * Remote wants new keys * Well, it's already in the brigade, * let's just call back into ourselves */ _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Renegotiating Keys"); session->packAdd_state = libssh2_NB_state_sent2; } /* * If there was a key reexchange failure, let's just hope we didn't * send NEWKEYS yet, otherwise remote will drop us like a rock */ rc = libssh2_kex_exchange(session, 1, &session->packAdd_key_state); if (rc == PACKET_EAGAIN) { return PACKET_EAGAIN; } } session->packAdd_state = libssh2_NB_state_idle; return 0; }
/* {{{ libssh2_packet_queue_listener * Queue a connection request for a listener */ static inline int libssh2_packet_queue_listener(LIBSSH2_SESSION * session, unsigned char *data, unsigned long datalen, packet_queue_listener_state_t * listen_state) { /* * Look for a matching listener */ unsigned char *s = data + (sizeof("forwarded-tcpip") - 1) + 5; /* 17 = packet_type(1) + channel(4) + reason(4) + descr(4) + lang(4) */ unsigned long packet_len = 17 + (sizeof(FwdNotReq) - 1); unsigned char *p; LIBSSH2_LISTENER *listen = session->listeners; char failure_code = 1; /* SSH_OPEN_ADMINISTRATIVELY_PROHIBITED */ int rc; (void) datalen; if (listen_state->state == libssh2_NB_state_idle) { listen_state->sender_channel = libssh2_ntohu32(s); s += 4; listen_state->initial_window_size = libssh2_ntohu32(s); s += 4; listen_state->packet_size = libssh2_ntohu32(s); s += 4; listen_state->host_len = libssh2_ntohu32(s); s += 4; listen_state->host = s; s += listen_state->host_len; listen_state->port = libssh2_ntohu32(s); s += 4; listen_state->shost_len = libssh2_ntohu32(s); s += 4; listen_state->shost = s; s += listen_state->shost_len; listen_state->sport = libssh2_ntohu32(s); s += 4; _libssh2_debug(session, LIBSSH2_DBG_CONN, "Remote received connection from %s:%ld to %s:%ld", listen_state->shost, listen_state->sport, listen_state->host, listen_state->port); listen_state->state = libssh2_NB_state_allocated; } if (listen_state->state != libssh2_NB_state_sent) { while (listen) { if ((listen->port == (int) listen_state->port) && (strlen(listen->host) == listen_state->host_len) && (memcmp (listen->host, listen_state->host, listen_state->host_len) == 0)) { /* This is our listener */ LIBSSH2_CHANNEL *channel, *last_queued = listen->queue; last_queued = listen->queue; if (listen_state->state == libssh2_NB_state_allocated) { if (listen->queue_maxsize && (listen->queue_maxsize <= listen->queue_size)) { /* Queue is full */ failure_code = 4; /* SSH_OPEN_RESOURCE_SHORTAGE */ _libssh2_debug(session, LIBSSH2_DBG_CONN, "Listener queue full, ignoring"); listen_state->state = libssh2_NB_state_sent; break; } channel = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_CHANNEL)); if (!channel) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate a channel for new connection", 0); failure_code = 4; /* SSH_OPEN_RESOURCE_SHORTAGE */ listen_state->state = libssh2_NB_state_sent; break; } memset(channel, 0, sizeof(LIBSSH2_CHANNEL)); channel->session = session; channel->channel_type_len = sizeof("forwarded-tcpip") - 1; channel->channel_type = LIBSSH2_ALLOC(session, channel-> channel_type_len + 1); if (!channel->channel_type) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate a channel for new connection", 0); LIBSSH2_FREE(session, channel); failure_code = 4; /* SSH_OPEN_RESOURCE_SHORTAGE */ listen_state->state = libssh2_NB_state_sent; break; } memcpy(channel->channel_type, "forwarded-tcpip", channel->channel_type_len + 1); channel->remote.id = listen_state->sender_channel; channel->remote.window_size_initial = LIBSSH2_CHANNEL_WINDOW_DEFAULT; channel->remote.window_size = LIBSSH2_CHANNEL_WINDOW_DEFAULT; channel->remote.packet_size = LIBSSH2_CHANNEL_PACKET_DEFAULT; channel->local.id = libssh2_channel_nextid(session); channel->local.window_size_initial = listen_state->initial_window_size; channel->local.window_size = listen_state->initial_window_size; channel->local.packet_size = listen_state->packet_size; _libssh2_debug(session, LIBSSH2_DBG_CONN, "Connection queued: channel %lu/%lu win %lu/%lu packet %lu/%lu", channel->local.id, channel->remote.id, channel->local.window_size, channel->remote.window_size, channel->local.packet_size, channel->remote.packet_size); p = listen_state->packet; *(p++) = SSH_MSG_CHANNEL_OPEN_CONFIRMATION; libssh2_htonu32(p, channel->remote.id); p += 4; libssh2_htonu32(p, channel->local.id); p += 4; libssh2_htonu32(p, channel->remote.window_size_initial); p += 4; libssh2_htonu32(p, channel->remote.packet_size); p += 4; listen_state->state = libssh2_NB_state_created; } if (listen_state->state == libssh2_NB_state_created) { rc = libssh2_packet_write(session, listen_state->packet, 17); if (rc == PACKET_EAGAIN) { return PACKET_EAGAIN; } else if (rc) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send channel open confirmation", 0); listen_state->state = libssh2_NB_state_idle; return -1; } /* Link the channel into the end of the queue list */ if (!last_queued) { listen->queue = channel; listen_state->state = libssh2_NB_state_idle; return 0; } while (last_queued->next) { last_queued = last_queued->next; } last_queued->next = channel; channel->prev = last_queued; listen->queue_size++; listen_state->state = libssh2_NB_state_idle; return 0; } } listen = listen->next; } listen_state->state = libssh2_NB_state_sent; } /* We're not listening to you */ { p = listen_state->packet; *(p++) = SSH_MSG_CHANNEL_OPEN_FAILURE; libssh2_htonu32(p, listen_state->sender_channel); p += 4; libssh2_htonu32(p, failure_code); p += 4; libssh2_htonu32(p, sizeof(FwdNotReq) - 1); p += 4; memcpy(s, FwdNotReq, sizeof(FwdNotReq) - 1); p += sizeof(FwdNotReq) - 1; libssh2_htonu32(p, 0); rc = libssh2_packet_write(session, listen_state->packet, packet_len); if (rc == PACKET_EAGAIN) { return PACKET_EAGAIN; } else if (rc) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send open failure", 0); listen_state->state = libssh2_NB_state_idle; return -1; } listen_state->state = libssh2_NB_state_idle; return 0; } }
/* * libssh2_scp_send_ex * * Send a file using SCP * */ LIBSSH2_API LIBSSH2_CHANNEL * libssh2_scp_send_ex(LIBSSH2_SESSION * session, const char *path, int mode, size_t size, long mtime, long atime) { int cmd_len; unsigned const char *base; int rc; if (session->scpSend_state == libssh2_NB_state_idle) { session->scpSend_command_len = libssh2_shell_quotedsize(path) + sizeof("scp -t ") + ((mtime || atime)?1:0); session->scpSend_command = LIBSSH2_ALLOC(session, session->scpSend_command_len); if (!session->scpSend_command) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate a command buffer for scp session", 0); return NULL; } sprintf((char *)session->scpSend_command, "scp -%st ", (mtime || atime)?"p":""); cmd_len = strlen((char *)session->scpSend_command); (void)libssh2_shell_quotearg(path, &session->scpSend_command[cmd_len], session->scpSend_command_len - cmd_len); session->scpSend_command[session->scpSend_command_len - 1] = '\0'; _libssh2_debug(session, LIBSSH2_DBG_SCP, "Opening channel for SCP send"); /* Allocate a channel */ session->scpSend_state = libssh2_NB_state_created; } if (session->scpSend_state == libssh2_NB_state_created) { session->scpSend_channel = libssh2_channel_open_ex(session, "session", sizeof("session") - 1, LIBSSH2_CHANNEL_WINDOW_DEFAULT, LIBSSH2_CHANNEL_PACKET_DEFAULT, NULL, 0); if (!session->scpSend_channel) { if (libssh2_session_last_errno(session) != LIBSSH2_ERROR_EAGAIN) { /* previous call set libssh2_session_last_error(), pass it through */ LIBSSH2_FREE(session, session->scpSend_command); session->scpSend_command = NULL; session->scpSend_state = libssh2_NB_state_idle; } else { libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block starting up channel", 0); } return NULL; } session->scpSend_state = libssh2_NB_state_sent; } if (session->scpSend_state == libssh2_NB_state_sent) { /* Request SCP for the desired file */ rc = libssh2_channel_process_startup(session->scpSend_channel, "exec", sizeof("exec") - 1, (char *) session->scpSend_command, session->scpSend_command_len); if (rc == PACKET_EAGAIN) { libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block requesting SCP startup", 0); return NULL; } else if (rc) { /* previous call set libssh2_session_last_error(), pass it through */ LIBSSH2_FREE(session, session->scpSend_command); session->scpSend_command = NULL; libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Unknown error while getting error string", 0); goto scp_send_error; } LIBSSH2_FREE(session, session->scpSend_command); session->scpSend_command = NULL; session->scpSend_state = libssh2_NB_state_sent1; } if (session->scpSend_state == libssh2_NB_state_sent1) { /* Wait for ACK */ rc = libssh2_channel_read_ex(session->scpSend_channel, 0, (char *) session->scpSend_response, 1); if (rc == PACKET_EAGAIN) { libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block waiting for response from remote", 0); return NULL; } else if ((rc <= 0) || (session->scpSend_response[0] != 0)) { libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid ACK response from remote", 0); goto scp_send_error; } if (mtime || atime) { /* Send mtime and atime to be used for file */ session->scpSend_response_len = snprintf((char *) session->scpSend_response, LIBSSH2_SCP_RESPONSE_BUFLEN, "T%ld 0 %ld 0\n", mtime, atime); _libssh2_debug(session, LIBSSH2_DBG_SCP, "Sent %s", session->scpSend_response); } session->scpSend_state = libssh2_NB_state_sent2; } /* Send mtime and atime to be used for file */ if (mtime || atime) { if (session->scpSend_state == libssh2_NB_state_sent2) { rc = libssh2_channel_write_ex(session->scpSend_channel, 0, (char *) session->scpSend_response, session->scpSend_response_len); if (rc == PACKET_EAGAIN) { libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block sending time data for SCP file", 0); return NULL; } else if (rc != (int)session->scpSend_response_len) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send time data for SCP file", 0); goto scp_send_error; } session->scpSend_state = libssh2_NB_state_sent3; } if (session->scpSend_state == libssh2_NB_state_sent3) { /* Wait for ACK */ rc = libssh2_channel_read_ex(session->scpSend_channel, 0, (char *) session->scpSend_response, 1); if (rc == PACKET_EAGAIN) { libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block waiting for response", 0); return NULL; } else if ((rc <= 0) || (session->scpSend_response[0] != 0)) { libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid ACK response from remote", 0); goto scp_send_error; } session->scpSend_state = libssh2_NB_state_sent4; } } else { if (session->scpSend_state == libssh2_NB_state_sent2) { session->scpSend_state = libssh2_NB_state_sent4; } } if (session->scpSend_state == libssh2_NB_state_sent4) { /* Send mode, size, and basename */ base = (unsigned char *) strrchr(path, '/'); if (base) { base++; } else { base = (unsigned char *) path; } session->scpSend_response_len = snprintf((char *) session->scpSend_response, LIBSSH2_SCP_RESPONSE_BUFLEN, "C0%o %lu %s\n", mode, (unsigned long) size, base); _libssh2_debug(session, LIBSSH2_DBG_SCP, "Sent %s", session->scpSend_response); session->scpSend_state = libssh2_NB_state_sent5; } if (session->scpSend_state == libssh2_NB_state_sent5) { rc = libssh2_channel_write_ex(session->scpSend_channel, 0, (char *) session->scpSend_response, session->scpSend_response_len); if (rc == PACKET_EAGAIN) { libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block send core file data for SCP file", 0); return NULL; } else if (rc != (int)session->scpSend_response_len) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send core file data for SCP file", 0); goto scp_send_error; } session->scpSend_state = libssh2_NB_state_sent6; } if (session->scpSend_state == libssh2_NB_state_sent6) { /* Wait for ACK */ rc = libssh2_channel_read_ex(session->scpSend_channel, 0, (char *) session->scpSend_response, 1); if (rc == PACKET_EAGAIN) { libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block waiting for response", 0); return NULL; } else if (rc <= 0) { libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid ACK response from remote", 0); goto scp_send_error; } else if (session->scpSend_response[0] != 0) { /* * Set this as the default error for here, if * we are successful it will be replaced */ libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid ACK response from remote", 0); session->scpSend_err_len = _libssh2_channel_packet_data_len(session->scpSend_channel, 0); session->scpSend_err_msg = LIBSSH2_ALLOC(session, session->scpSend_err_len + 1); if (!session->scpSend_err_msg) { goto scp_send_error; } memset(session->scpSend_err_msg, 0, session->scpSend_err_len + 1); /* Read the remote error message */ rc = libssh2_channel_read_ex(session->scpSend_channel, 0, session->scpSend_err_msg, session->scpSend_err_len); if (rc <= 0) { /* * Since we have alread started reading this packet, it is * already in the systems so it can't return PACKET_EAGAIN */ LIBSSH2_FREE(session, session->scpSend_err_msg); session->scpSend_err_msg = NULL; goto scp_send_error; } libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, session->scpSend_err_msg, 1); session->scpSend_err_msg = NULL; goto scp_send_error; } } session->scpSend_state = libssh2_NB_state_idle; return session->scpSend_channel; scp_send_error: while (libssh2_channel_free(session->scpSend_channel) == PACKET_EAGAIN); session->scpSend_channel = NULL; session->scpSend_state = libssh2_NB_state_idle; return NULL; }
/* {{{ libssh2_packet_x11_open * Accept a forwarded X11 connection */ static inline int libssh2_packet_x11_open(LIBSSH2_SESSION * session, unsigned char *data, unsigned long datalen, packet_x11_open_state_t * x11open_state) { int failure_code = 2; /* SSH_OPEN_CONNECT_FAILED */ unsigned char *s = data + (sizeof("x11") - 1) + 5; /* 17 = packet_type(1) + channel(4) + reason(4) + descr(4) + lang(4) */ unsigned long packet_len = 17 + (sizeof(X11FwdUnAvil) - 1); unsigned char *p; LIBSSH2_CHANNEL *channel; int rc; (void) datalen; if (x11open_state->state == libssh2_NB_state_idle) { x11open_state->sender_channel = libssh2_ntohu32(s); s += 4; x11open_state->initial_window_size = libssh2_ntohu32(s); s += 4; x11open_state->packet_size = libssh2_ntohu32(s); s += 4; x11open_state->shost_len = libssh2_ntohu32(s); s += 4; x11open_state->shost = s; s += x11open_state->shost_len; x11open_state->sport = libssh2_ntohu32(s); s += 4; _libssh2_debug(session, LIBSSH2_DBG_CONN, "X11 Connection Received from %s:%ld on channel %lu", x11open_state->shost, x11open_state->sport, x11open_state->sender_channel); x11open_state->state = libssh2_NB_state_allocated; } if (session->x11) { if (x11open_state->state == libssh2_NB_state_allocated) { channel = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_CHANNEL)); if (!channel) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate a channel for new connection", 0); failure_code = 4; /* SSH_OPEN_RESOURCE_SHORTAGE */ goto x11_exit; } memset(channel, 0, sizeof(LIBSSH2_CHANNEL)); channel->session = session; channel->channel_type_len = sizeof("x11") - 1; channel->channel_type = LIBSSH2_ALLOC(session, channel->channel_type_len + 1); if (!channel->channel_type) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate a channel for new connection", 0); LIBSSH2_FREE(session, channel); failure_code = 4; /* SSH_OPEN_RESOURCE_SHORTAGE */ goto x11_exit; } memcpy(channel->channel_type, "x11", channel->channel_type_len + 1); channel->remote.id = x11open_state->sender_channel; channel->remote.window_size_initial = LIBSSH2_CHANNEL_WINDOW_DEFAULT; channel->remote.window_size = LIBSSH2_CHANNEL_WINDOW_DEFAULT; channel->remote.packet_size = LIBSSH2_CHANNEL_PACKET_DEFAULT; channel->local.id = libssh2_channel_nextid(session); channel->local.window_size_initial = x11open_state->initial_window_size; channel->local.window_size = x11open_state->initial_window_size; channel->local.packet_size = x11open_state->packet_size; _libssh2_debug(session, LIBSSH2_DBG_CONN, "X11 Connection established: channel %lu/%lu win %lu/%lu packet %lu/%lu", channel->local.id, channel->remote.id, channel->local.window_size, channel->remote.window_size, channel->local.packet_size, channel->remote.packet_size); p = x11open_state->packet; *(p++) = SSH_MSG_CHANNEL_OPEN_CONFIRMATION; libssh2_htonu32(p, channel->remote.id); p += 4; libssh2_htonu32(p, channel->local.id); p += 4; libssh2_htonu32(p, channel->remote.window_size_initial); p += 4; libssh2_htonu32(p, channel->remote.packet_size); p += 4; x11open_state->state = libssh2_NB_state_created; } if (x11open_state->state == libssh2_NB_state_created) { rc = libssh2_packet_write(session, x11open_state->packet, 17); if (rc == PACKET_EAGAIN) { return PACKET_EAGAIN; } else if (rc) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send channel open confirmation", 0); x11open_state->state = libssh2_NB_state_idle; return -1; } /* Link the channel into the session */ if (session->channels.tail) { session->channels.tail->next = channel; channel->prev = session->channels.tail; } else { session->channels.head = channel; channel->prev = NULL; } channel->next = NULL; session->channels.tail = channel; /* * Pass control to the callback, they may turn right around and * free the channel, or actually use it */ LIBSSH2_X11_OPEN(channel, (char *) x11open_state->shost, x11open_state->sport); x11open_state->state = libssh2_NB_state_idle; return 0; } } else { failure_code = 4; /* SSH_OPEN_RESOURCE_SHORTAGE */ } x11_exit: p = x11open_state->packet; *(p++) = SSH_MSG_CHANNEL_OPEN_FAILURE; libssh2_htonu32(p, x11open_state->sender_channel); p += 4; libssh2_htonu32(p, failure_code); p += 4; libssh2_htonu32(p, sizeof(X11FwdUnAvil) - 1); p += 4; memcpy(s, X11FwdUnAvil, sizeof(X11FwdUnAvil) - 1); p += sizeof(X11FwdUnAvil) - 1; libssh2_htonu32(p, 0); rc = libssh2_packet_write(session, x11open_state->packet, packet_len); if (rc == PACKET_EAGAIN) { return PACKET_EAGAIN; } else if (rc) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send open failure", 0); x11open_state->state = libssh2_NB_state_idle; return -1; } x11open_state->state = libssh2_NB_state_idle; return 0; }
/* {{{ libssh2_kexinit * Send SSH_MSG_KEXINIT packet */ static int libssh2_kexinit(LIBSSH2_SESSION *session) { size_t data_len = 62; /* packet_type(1) + cookie(16) + first_packet_follows(1) + reserved(4) + length longs(40) */ size_t kex_len, hostkey_len = 0; size_t crypt_cs_len, crypt_sc_len; size_t comp_cs_len, comp_sc_len; size_t mac_cs_len, mac_sc_len; size_t lang_cs_len, lang_sc_len; unsigned char *data, *s; kex_len = LIBSSH2_METHOD_PREFS_LEN(session->kex_prefs, libssh2_kex_methods); hostkey_len = LIBSSH2_METHOD_PREFS_LEN(session->hostkey_prefs, libssh2_hostkey_methods()); crypt_cs_len = LIBSSH2_METHOD_PREFS_LEN(session->local.crypt_prefs, libssh2_crypt_methods()); crypt_sc_len = LIBSSH2_METHOD_PREFS_LEN(session->remote.crypt_prefs, libssh2_crypt_methods()); mac_cs_len = LIBSSH2_METHOD_PREFS_LEN(session->local.mac_prefs, libssh2_mac_methods()); mac_sc_len = LIBSSH2_METHOD_PREFS_LEN(session->remote.mac_prefs, libssh2_mac_methods()); comp_cs_len = LIBSSH2_METHOD_PREFS_LEN(session->local.comp_prefs, libssh2_comp_methods()); comp_sc_len = LIBSSH2_METHOD_PREFS_LEN(session->remote.comp_prefs, libssh2_comp_methods()); lang_cs_len = LIBSSH2_METHOD_PREFS_LEN(session->local.lang_prefs, NULL); lang_sc_len = LIBSSH2_METHOD_PREFS_LEN(session->remote.lang_prefs, NULL); data_len += kex_len + hostkey_len + \ crypt_cs_len + crypt_sc_len + \ comp_cs_len + comp_sc_len + \ mac_cs_len + mac_sc_len + \ lang_cs_len + lang_sc_len; s = data = LIBSSH2_ALLOC(session, data_len); if (!data) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory", 0); return -1; } *(s++) = SSH_MSG_KEXINIT; libssh2_random(s, 16); s += 16; /* Ennumerating through these lists twice is probably (certainly?) inefficient from a CPU standpoint, but it saves multiple malloc/realloc calls */ LIBSSH2_METHOD_PREFS_STR(s, kex_len, session->kex_prefs, libssh2_kex_methods); LIBSSH2_METHOD_PREFS_STR(s, hostkey_len, session->hostkey_prefs, libssh2_hostkey_methods()); LIBSSH2_METHOD_PREFS_STR(s, crypt_cs_len, session->local.crypt_prefs, libssh2_crypt_methods()); LIBSSH2_METHOD_PREFS_STR(s, crypt_sc_len, session->remote.crypt_prefs, libssh2_crypt_methods()); LIBSSH2_METHOD_PREFS_STR(s, mac_cs_len, session->local.mac_prefs, libssh2_mac_methods()); LIBSSH2_METHOD_PREFS_STR(s, mac_sc_len, session->remote.mac_prefs, libssh2_mac_methods()); LIBSSH2_METHOD_PREFS_STR(s, comp_cs_len, session->local.comp_prefs, libssh2_comp_methods()); LIBSSH2_METHOD_PREFS_STR(s, comp_sc_len, session->remote.comp_prefs, libssh2_comp_methods()); LIBSSH2_METHOD_PREFS_STR(s, lang_cs_len, session->local.lang_prefs, NULL); LIBSSH2_METHOD_PREFS_STR(s, lang_sc_len, session->remote.lang_prefs, NULL); /* No optimistic KEX packet follows */ /* Deal with optimistic packets * session->flags |= KEXINIT_OPTIMISTIC * session->flags |= KEXINIT_METHODSMATCH */ *(s++) = 0; /* Reserved == 0 */ *(s++) = 0; *(s++) = 0; *(s++) = 0; *(s++) = 0; #ifdef LIBSSH2_DEBUG_KEX { /* Funnily enough, they'll all "appear" to be '\0' terminated */ char *p = data + 21; /* type(1) + cookie(16) + len(4) */ _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent KEX: %s", p); p += kex_len + 4; _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent HOSTKEY: %s", p); p += hostkey_len + 4; _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent CRYPT_CS: %s", p); p += crypt_cs_len + 4; _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent CRYPT_SC: %s", p); p += crypt_sc_len + 4; _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent MAC_CS: %s", p); p += mac_cs_len + 4; _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent MAC_SC: %s", p); p += mac_sc_len + 4; _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent COMP_CS: %s", p); p += comp_cs_len + 4; _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent COMP_SC: %s", p); p += comp_sc_len + 4; _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent LANG_CS: %s", p); p += lang_cs_len + 4; _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent LANG_SC: %s", p); p += lang_sc_len + 4; } #endif /* LIBSSH2_DEBUG_KEX */ if (libssh2_packet_write(session, data, data_len)) { LIBSSH2_FREE(session, data); libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send KEXINIT packet to remote host", 0); return -1; } if (session->local.kexinit) { LIBSSH2_FREE(session, session->local.kexinit); } session->local.kexinit = data; session->local.kexinit_len = data_len; return 0; }
/* {{{ libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange * Diffie Hellman Key Exchange, Group Agnostic */ static int libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(LIBSSH2_SESSION *session, _libssh2_bn *g, _libssh2_bn *p, int group_order, unsigned char packet_type_init, unsigned char packet_type_reply, unsigned char *midhash, unsigned long midhash_len) { unsigned char *e_packet = NULL, *s_packet = NULL, *tmp, h_sig_comp[SHA_DIGEST_LENGTH], c; unsigned long e_packet_len, s_packet_len, tmp_len; int ret = 0; _libssh2_bn_ctx *ctx = _libssh2_bn_ctx_new(); _libssh2_bn *x = _libssh2_bn_init(); /* Random from client */ _libssh2_bn *e = _libssh2_bn_init(); /* g^x mod p */ _libssh2_bn *f = _libssh2_bn_init(); /* g^(Random from server) mod p */ _libssh2_bn *k = _libssh2_bn_init(); /* The shared secret: f^x mod p */ unsigned char *s, *f_value, *k_value = NULL, *h_sig; unsigned long f_value_len, k_value_len, h_sig_len; libssh2_sha1_ctx exchange_hash; /* Generate x and e */ _libssh2_bn_rand(x, group_order, 0, -1); _libssh2_bn_mod_exp(e, g, x, p, ctx); /* Send KEX init */ e_packet_len = _libssh2_bn_bytes(e) + 6; /* packet_type(1) + String Length(4) + leading 0(1) */ if (_libssh2_bn_bits(e) % 8) { /* Leading 00 not needed */ e_packet_len--; } e_packet = LIBSSH2_ALLOC(session, e_packet_len); if (!e_packet) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Out of memory error", 0); ret = -1; goto clean_exit; } e_packet[0] = packet_type_init; libssh2_htonu32(e_packet + 1, e_packet_len - 5); if (_libssh2_bn_bits(e) % 8) { _libssh2_bn_to_bin(e, e_packet + 5); } else { e_packet[5] = 0; _libssh2_bn_to_bin(e, e_packet + 6); } #ifdef LIBSSH2_DEBUG_KEX _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sending KEX packet %d", (int)packet_type_init); #endif if (libssh2_packet_write(session, e_packet, e_packet_len)) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send KEX init message", 0); ret = -11; goto clean_exit; } if (session->burn_optimistic_kexinit) { /* The first KEX packet to come along will be the guess initially sent by the server * That guess turned out to be wrong so we need to silently ignore it */ int burn_type; #ifdef LIBSSH2_DEBUG_KEX _libssh2_debug(session, LIBSSH2_DBG_KEX, "Waiting for badly guessed KEX packet (to be ignored)"); #endif burn_type = libssh2_packet_burn(session); if (burn_type <= 0) { /* Failed to receive a packet */ ret = -1; goto clean_exit; } session->burn_optimistic_kexinit = 0; #ifdef LIBSSH2_DEBUG_KEX _libssh2_debug(session, LIBSSH2_DBG_KEX, "Burnt packet of type: %02x", (unsigned int)burn_type); #endif } /* Wait for KEX reply */ if (libssh2_packet_require(session, packet_type_reply, &s_packet, &s_packet_len)) { libssh2_error(session, LIBSSH2_ERROR_TIMEOUT, "Timed out waiting for KEX reply", 0); ret = -1; goto clean_exit; } /* Parse KEXDH_REPLY */ s = s_packet + 1; session->server_hostkey_len = libssh2_ntohu32(s); s += 4; session->server_hostkey = LIBSSH2_ALLOC(session, session->server_hostkey_len); if (!session->server_hostkey) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for a copy of the host key", 0); ret = -1; goto clean_exit; } memcpy(session->server_hostkey, s, session->server_hostkey_len); s += session->server_hostkey_len; #if LIBSSH2_MD5 { libssh2_md5_ctx fingerprint_ctx; libssh2_md5_init(&fingerprint_ctx); libssh2_md5_update(fingerprint_ctx, session->server_hostkey, session->server_hostkey_len); libssh2_md5_final(fingerprint_ctx, session->server_hostkey_md5); } #ifdef LIBSSH2_DEBUG_KEX { char fingerprint[50], *fprint = fingerprint; int i; for(i = 0; i < 16; i++, fprint += 3) { snprintf(fprint, 4, "%02x:", session->server_hostkey_md5[i]); } *(--fprint) = '\0'; _libssh2_debug(session, LIBSSH2_DBG_KEX, "Server's MD5 Fingerprint: %s", fingerprint); } #endif /* LIBSSH2_DEBUG_KEX */ #endif /* ! LIBSSH2_MD5 */ { libssh2_sha1_ctx fingerprint_ctx; libssh2_sha1_init(&fingerprint_ctx); libssh2_sha1_update (fingerprint_ctx, session->server_hostkey, session->server_hostkey_len); libssh2_sha1_final(fingerprint_ctx, session->server_hostkey_sha1); } #ifdef LIBSSH2_DEBUG_KEX { char fingerprint[64], *fprint = fingerprint; int i; for(i = 0; i < 20; i++, fprint += 3) { snprintf(fprint, 4, "%02x:", session->server_hostkey_sha1[i]); } *(--fprint) = '\0'; _libssh2_debug(session, LIBSSH2_DBG_KEX, "Server's SHA1 Fingerprint: %s", fingerprint); } #endif /* LIBSSH2_DEBUG_KEX */ if (session->hostkey->init(session, session->server_hostkey, session->server_hostkey_len, &session->server_hostkey_abstract)) { libssh2_error(session, LIBSSH2_ERROR_HOSTKEY_INIT, "Unable to initialize hostkey importer", 0); ret = -1; goto clean_exit; } f_value_len = libssh2_ntohu32(s); s += 4; f_value = s; s += f_value_len; _libssh2_bn_from_bin(f, f_value_len, f_value); h_sig_len = libssh2_ntohu32(s); s += 4; h_sig = s; /* Compute the shared secret */ _libssh2_bn_mod_exp(k, f, x, p, ctx); k_value_len = _libssh2_bn_bytes(k) + 5; if (_libssh2_bn_bits(k) % 8) { /* don't need leading 00 */ k_value_len--; } k_value = LIBSSH2_ALLOC(session, k_value_len); if (!k_value) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate buffer for K", 0); ret = -1; goto clean_exit; } libssh2_htonu32(k_value, k_value_len - 4); if (_libssh2_bn_bits(k) % 8) { _libssh2_bn_to_bin(k, k_value + 4); } else { k_value[4] = 0; _libssh2_bn_to_bin(k, k_value + 5); } libssh2_sha1_init(&exchange_hash); if (session->local.banner) { libssh2_htonu32(h_sig_comp, strlen((char *)session->local.banner) - 2); libssh2_sha1_update(exchange_hash, h_sig_comp, 4); libssh2_sha1_update(exchange_hash, (char *)session->local.banner, strlen((char *)session->local.banner) - 2); } else { libssh2_htonu32(h_sig_comp, sizeof(LIBSSH2_SSH_DEFAULT_BANNER) - 1); libssh2_sha1_update(exchange_hash, h_sig_comp, 4); libssh2_sha1_update(exchange_hash, LIBSSH2_SSH_DEFAULT_BANNER, sizeof(LIBSSH2_SSH_DEFAULT_BANNER) - 1); } libssh2_htonu32(h_sig_comp, strlen((char *)session->remote.banner)); libssh2_sha1_update(exchange_hash, h_sig_comp, 4); libssh2_sha1_update(exchange_hash, session->remote.banner, strlen((char *)session->remote.banner)); libssh2_htonu32(h_sig_comp, session->local.kexinit_len); libssh2_sha1_update(exchange_hash, h_sig_comp, 4); libssh2_sha1_update(exchange_hash, session->local.kexinit, session->local.kexinit_len); libssh2_htonu32(h_sig_comp, session->remote.kexinit_len); libssh2_sha1_update(exchange_hash, h_sig_comp, 4); libssh2_sha1_update(exchange_hash, session->remote.kexinit, session->remote.kexinit_len); libssh2_htonu32(h_sig_comp, session->server_hostkey_len); libssh2_sha1_update(exchange_hash, h_sig_comp, 4); libssh2_sha1_update(exchange_hash, session->server_hostkey, session->server_hostkey_len); if (packet_type_init == SSH_MSG_KEX_DH_GEX_INIT) { /* diffie-hellman-group-exchange hashes additional fields */ #ifdef LIBSSH2_DH_GEX_NEW libssh2_htonu32(h_sig_comp, LIBSSH2_DH_GEX_MINGROUP); libssh2_htonu32(h_sig_comp + 4, LIBSSH2_DH_GEX_OPTGROUP); libssh2_htonu32(h_sig_comp + 8, LIBSSH2_DH_GEX_MAXGROUP); libssh2_sha1_update(exchange_hash, h_sig_comp, 12); #else libssh2_htonu32(h_sig_comp, LIBSSH2_DH_GEX_OPTGROUP); libssh2_sha1_update(exchange_hash, h_sig_comp, 4); #endif } if (midhash) { libssh2_sha1_update(exchange_hash, midhash, midhash_len); } libssh2_sha1_update(exchange_hash, e_packet + 1, e_packet_len - 1); libssh2_htonu32(h_sig_comp, f_value_len); libssh2_sha1_update(exchange_hash, h_sig_comp, 4); libssh2_sha1_update(exchange_hash, f_value, f_value_len); libssh2_sha1_update(exchange_hash, k_value, k_value_len); libssh2_sha1_final(exchange_hash, h_sig_comp); if (session->hostkey->sig_verify(session, h_sig, h_sig_len, h_sig_comp, 20, &session->server_hostkey_abstract)) { libssh2_error(session, LIBSSH2_ERROR_HOSTKEY_SIGN, "Unable to verify hostkey signature", 0); ret = -1; goto clean_exit; } #ifdef LIBSSH2_DEBUG_KEX _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sending NEWKEYS message"); #endif c = SSH_MSG_NEWKEYS; if (libssh2_packet_write(session, &c, 1)) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send NEWKEYS message", 0); ret = -1; goto clean_exit; } if (libssh2_packet_require(session, SSH_MSG_NEWKEYS, &tmp, &tmp_len)) { libssh2_error(session, LIBSSH2_ERROR_TIMEOUT, "Timed out waiting for NEWKEYS", 0); ret = -1; goto clean_exit; } /* The first key exchange has been performed, switch to active crypt/comp/mac mode */ session->state |= LIBSSH2_STATE_NEWKEYS; #ifdef LIBSSH2_DEBUG_KEX _libssh2_debug(session, LIBSSH2_DBG_KEX, "Received NEWKEYS message"); #endif /* This will actually end up being just packet_type(1) for this packet type anyway */ LIBSSH2_FREE(session, tmp); if (!session->session_id) { session->session_id = LIBSSH2_ALLOC(session, SHA_DIGEST_LENGTH); if (!session->session_id) { ret = -1; goto clean_exit; } memcpy(session->session_id, h_sig_comp, SHA_DIGEST_LENGTH); session->session_id_len = SHA_DIGEST_LENGTH; #ifdef LIBSSH2_DEBUG_KEX _libssh2_debug(session, LIBSSH2_DBG_KEX, "session_id calculated"); #endif } /* Cleanup any existing cipher */ if (session->local.crypt->dtor) { session->local.crypt->dtor(session, &session->local.crypt_abstract); } /* Calculate IV/Secret/Key for each direction */ if (session->local.crypt->init) { unsigned char *iv = NULL, *secret = NULL; int free_iv = 0, free_secret = 0; LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA1_HASH(iv, session->local.crypt->iv_len, "A"); LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA1_HASH(secret, session->local.crypt->secret_len, "C"); if (session->local.crypt->init(session, session->local.crypt, iv, &free_iv, secret, &free_secret, 1, &session->local.crypt_abstract)) { LIBSSH2_FREE(session, iv); LIBSSH2_FREE(session, secret); ret = -1; goto clean_exit; } if (free_iv) { memset(iv, 0, session->local.crypt->iv_len); LIBSSH2_FREE(session, iv); } if (free_secret) { memset(secret, 0, session->local.crypt->secret_len); LIBSSH2_FREE(session, secret); } } #ifdef LIBSSH2_DEBUG_KEX _libssh2_debug(session, LIBSSH2_DBG_KEX, "Client to Server IV and Key calculated"); #endif if (session->remote.crypt->dtor) { /* Cleanup any existing cipher */ session->remote.crypt->dtor(session, &session->remote.crypt_abstract); } if (session->remote.crypt->init) { unsigned char *iv = NULL, *secret = NULL; int free_iv = 0, free_secret = 0; LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA1_HASH(iv, session->remote.crypt->iv_len, "B"); LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA1_HASH(secret, session->remote.crypt->secret_len, "D"); if (session->remote.crypt->init(session, session->remote.crypt, iv, &free_iv, secret, &free_secret, 0, &session->remote.crypt_abstract)) { LIBSSH2_FREE(session, iv); LIBSSH2_FREE(session, secret); ret = -1; goto clean_exit; } if (free_iv) { memset(iv, 0, session->remote.crypt->iv_len); LIBSSH2_FREE(session, iv); } if (free_secret) { memset(secret, 0, session->remote.crypt->secret_len); LIBSSH2_FREE(session, secret); } } #ifdef LIBSSH2_DEBUG_KEX _libssh2_debug(session, LIBSSH2_DBG_KEX, "Server to Client IV and Key calculated"); #endif if (session->local.mac->dtor) { session->local.mac->dtor(session, &session->local.mac_abstract); } if (session->local.mac->init) { unsigned char *key = NULL; int free_key = 0; LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA1_HASH(key, session->local.mac->key_len, "E"); session->local.mac->init(session, key, &free_key, &session->local.mac_abstract); if (free_key) { memset(key, 0, session->local.mac->key_len); LIBSSH2_FREE(session, key); } } #ifdef LIBSSH2_DEBUG_KEX _libssh2_debug(session, LIBSSH2_DBG_KEX, "Client to Server HMAC Key calculated"); #endif if (session->remote.mac->dtor) { session->remote.mac->dtor(session, &session->remote.mac_abstract); } if (session->remote.mac->init) { unsigned char *key = NULL; int free_key = 0; LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA1_HASH(key, session->remote.mac->key_len, "F"); session->remote.mac->init(session, key, &free_key, &session->remote.mac_abstract); if (free_key) { memset(key, 0, session->remote.mac->key_len); LIBSSH2_FREE(session, key); } } #ifdef LIBSSH2_DEBUG_KEX _libssh2_debug(session, LIBSSH2_DBG_KEX, "Server to Client HMAC Key calculated"); #endif clean_exit: _libssh2_bn_free(x); _libssh2_bn_free(e); _libssh2_bn_free(f); _libssh2_bn_free(k); _libssh2_bn_ctx_free(ctx); if (e_packet) { LIBSSH2_FREE(session, e_packet); } if (s_packet) { LIBSSH2_FREE(session, s_packet); } if (k_value) { LIBSSH2_FREE(session, k_value); } if (session->server_hostkey) { LIBSSH2_FREE(session, session->server_hostkey); session->server_hostkey = NULL; } return ret; }
/* {{{ libssh2_kex_agree_methods * Decide which specific method to use of the methods offered by each party */ static int libssh2_kex_agree_methods(LIBSSH2_SESSION *session, unsigned char *data, unsigned data_len) { unsigned char *kex, *hostkey, *crypt_cs, *crypt_sc, *comp_cs, *comp_sc, *mac_cs, *mac_sc, *lang_cs, *lang_sc; size_t kex_len, hostkey_len, crypt_cs_len, crypt_sc_len, comp_cs_len, comp_sc_len, mac_cs_len, mac_sc_len, lang_cs_len, lang_sc_len; unsigned char *s = data; /* Skip packet_type, we know it already */ s++; /* Skip cookie, don't worry, it's preserved in the kexinit field */ s += 16; /* Locate each string */ kex_len = libssh2_ntohu32(s); kex = s + 4; s += 4 + kex_len; hostkey_len = libssh2_ntohu32(s); hostkey = s + 4; s += 4 + hostkey_len; crypt_cs_len = libssh2_ntohu32(s); crypt_cs = s + 4; s += 4 + crypt_cs_len; crypt_sc_len = libssh2_ntohu32(s); crypt_sc = s + 4; s += 4 + crypt_sc_len; mac_cs_len = libssh2_ntohu32(s); mac_cs = s + 4; s += 4 + mac_cs_len; mac_sc_len = libssh2_ntohu32(s); mac_sc = s + 4; s += 4 + mac_sc_len; comp_cs_len = libssh2_ntohu32(s); comp_cs = s + 4; s += 4 + comp_cs_len; comp_sc_len = libssh2_ntohu32(s); comp_sc = s + 4; s += 4 + comp_sc_len; lang_cs_len = libssh2_ntohu32(s); lang_cs = s + 4; s += 4 + lang_cs_len; lang_sc_len = libssh2_ntohu32(s); lang_sc = s + 4; s += 4 + lang_sc_len; /* If the server sent an optimistic packet, assume that it guessed wrong. * If the guess is determined to be right (by libssh2_kex_agree_kex_hostkey) * This flag will be reset to zero so that it's not ignored */ session->burn_optimistic_kexinit = *(s++); /* Next uint32 in packet is all zeros (reserved) */ if (libssh2_kex_agree_kex_hostkey(session, kex, kex_len, hostkey, hostkey_len)) { return -1; } if (libssh2_kex_agree_crypt(session, &session->local, crypt_cs, crypt_cs_len) || libssh2_kex_agree_crypt(session, &session->remote, crypt_sc, crypt_sc_len)) { return -1; } if (libssh2_kex_agree_mac(session, &session->local, mac_cs, mac_cs_len) || libssh2_kex_agree_mac(session, &session->remote, mac_sc, mac_sc_len)) { return -1; } if (libssh2_kex_agree_comp(session, &session->local, comp_cs, comp_cs_len) || libssh2_kex_agree_comp(session, &session->remote, comp_sc, comp_sc_len)) { return -1; } if (libssh2_kex_agree_lang(session, &session->local, lang_cs, lang_cs_len) || libssh2_kex_agree_lang(session, &session->remote, lang_sc, lang_sc_len)) { return -1; } #ifdef LIBSSH2_DEBUG_KEX _libssh2_debug(session, LIBSSH2_DBG_KEX, "Agreed on KEX method: %s", session->kex->name); _libssh2_debug(session, LIBSSH2_DBG_KEX, "Agreed on HOSTKEY method: %s", session->hostkey->name); _libssh2_debug(session, LIBSSH2_DBG_KEX, "Agreed on CRYPT_CS method: %s", session->local.crypt->name); _libssh2_debug(session, LIBSSH2_DBG_KEX, "Agreed on CRYPT_SC method: %s", session->remote.crypt->name); _libssh2_debug(session, LIBSSH2_DBG_KEX, "Agreed on MAC_CS method: %s", session->local.mac->name); _libssh2_debug(session, LIBSSH2_DBG_KEX, "Agreed on MAC_SC method: %s", session->remote.mac->name); _libssh2_debug(session, LIBSSH2_DBG_KEX, "Agreed on COMP_CS method: %s", session->local.comp->name); _libssh2_debug(session, LIBSSH2_DBG_KEX, "Agreed on COMP_SC method: %s", session->remote.comp->name); _libssh2_debug(session, LIBSSH2_DBG_KEX, "Agreed on LANG_CS method:"); /* None yet */ _libssh2_debug(session, LIBSSH2_DBG_KEX, "Agreed on LANG_SC method:"); /* None yet */ #endif /* Initialize compression layer */ if (session->local.comp && session->local.comp->init && session->local.comp->init(session, 1, &session->local.comp_abstract)) { return -1; } if (session->remote.comp && session->remote.comp->init && session->remote.comp->init(session, 0, &session->remote.comp_abstract)) { return -1; } return 0; }
/* {{{ proto libssh2_session_free * Frees the memory allocated to the session * Also closes and frees any channels attached to this session */ LIBSSH2_API int libssh2_session_free(LIBSSH2_SESSION * session) { int rc; if (session->free_state == libssh2_NB_state_idle) { _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Freeing session resource", session->remote.banner); session->state = libssh2_NB_state_created; } if (session->free_state == libssh2_NB_state_created) { while (session->channels.head) { LIBSSH2_CHANNEL *tmp = session->channels.head; rc = libssh2_channel_free(session->channels.head); if (rc == PACKET_EAGAIN) { return PACKET_EAGAIN; } if (tmp == session->channels.head) { /* channel_free couldn't do it's job, perform a messy cleanup */ tmp = session->channels.head; /* unlink */ session->channels.head = tmp->next; /* free */ LIBSSH2_FREE(session, tmp); /* reverse linking isn't important here, we're killing the structure */ } } session->state = libssh2_NB_state_sent; } if (session->state == libssh2_NB_state_sent) { while (session->listeners) { rc = libssh2_channel_forward_cancel(session->listeners); if (rc == PACKET_EAGAIN) { return PACKET_EAGAIN; } } session->state = libssh2_NB_state_sent1; } if (session->state & LIBSSH2_STATE_NEWKEYS) { /* hostkey */ if (session->hostkey && session->hostkey->dtor) { session->hostkey->dtor(session, &session->server_hostkey_abstract); } /* Client to Server */ /* crypt */ if (session->local.crypt && session->local.crypt->dtor) { session->local.crypt->dtor(session, &session->local.crypt_abstract); } /* comp */ if (session->local.comp && session->local.comp->dtor) { session->local.comp->dtor(session, 1, &session->local.comp_abstract); } /* mac */ if (session->local.mac && session->local.mac->dtor) { session->local.mac->dtor(session, &session->local.mac_abstract); } /* Server to Client */ /* crypt */ if (session->remote.crypt && session->remote.crypt->dtor) { session->remote.crypt->dtor(session, &session->remote.crypt_abstract); } /* comp */ if (session->remote.comp && session->remote.comp->dtor) { session->remote.comp->dtor(session, 0, &session->remote.comp_abstract); } /* mac */ if (session->remote.mac && session->remote.mac->dtor) { session->remote.mac->dtor(session, &session->remote.mac_abstract); } /* session_id */ if (session->session_id) { LIBSSH2_FREE(session, session->session_id); } } /* Free banner(s) */ if (session->remote.banner) { LIBSSH2_FREE(session, session->remote.banner); } if (session->local.banner) { LIBSSH2_FREE(session, session->local.banner); } /* Free preference(s) */ if (session->kex_prefs) { LIBSSH2_FREE(session, session->kex_prefs); } if (session->hostkey_prefs) { LIBSSH2_FREE(session, session->hostkey_prefs); } if (session->local.crypt_prefs) { LIBSSH2_FREE(session, session->local.crypt_prefs); } if (session->local.mac_prefs) { LIBSSH2_FREE(session, session->local.mac_prefs); } if (session->local.comp_prefs) { LIBSSH2_FREE(session, session->local.comp_prefs); } if (session->local.lang_prefs) { LIBSSH2_FREE(session, session->local.lang_prefs); } if (session->remote.crypt_prefs) { LIBSSH2_FREE(session, session->remote.crypt_prefs); } if (session->remote.mac_prefs) { LIBSSH2_FREE(session, session->remote.mac_prefs); } if (session->remote.comp_prefs) { LIBSSH2_FREE(session, session->remote.comp_prefs); } if (session->remote.lang_prefs) { LIBSSH2_FREE(session, session->remote.lang_prefs); } /* * Make sure all memory used in the state variables are free */ if (session->startup_data) { LIBSSH2_FREE(session, session->startup_data); } if (session->disconnect_data) { LIBSSH2_FREE(session, session->disconnect_data); } if (session->userauth_list_data) { LIBSSH2_FREE(session, session->userauth_list_data); } if (session->userauth_pswd_data) { LIBSSH2_FREE(session, session->userauth_pswd_data); } if (session->userauth_pswd_newpw) { LIBSSH2_FREE(session, session->userauth_pswd_newpw); } if (session->userauth_host_packet) { LIBSSH2_FREE(session, session->userauth_host_packet); } if (session->userauth_host_method) { LIBSSH2_FREE(session, session->userauth_host_method); } if (session->userauth_host_data) { LIBSSH2_FREE(session, session->userauth_host_data); } if (session->userauth_pblc_data) { LIBSSH2_FREE(session, session->userauth_pblc_data); } if (session->userauth_pblc_packet) { LIBSSH2_FREE(session, session->userauth_pblc_packet); } if (session->userauth_pblc_method) { LIBSSH2_FREE(session, session->userauth_pblc_method); } if (session->userauth_kybd_data) { LIBSSH2_FREE(session, session->userauth_kybd_data); } if (session->userauth_kybd_packet) { LIBSSH2_FREE(session, session->userauth_kybd_packet); } if (session->userauth_kybd_auth_instruction) { LIBSSH2_FREE(session, session->userauth_kybd_auth_instruction); } if (session->open_packet) { LIBSSH2_FREE(session, session->open_packet); } if (session->open_data) { LIBSSH2_FREE(session, session->open_data); } if (session->direct_message) { LIBSSH2_FREE(session, session->direct_message); } if (session->fwdLstn_packet) { LIBSSH2_FREE(session, session->fwdLstn_packet); } if (session->pkeyInit_data) { LIBSSH2_FREE(session, session->pkeyInit_data); } if (session->scpRecv_command) { LIBSSH2_FREE(session, session->scpRecv_command); } if (session->scpSend_command) { LIBSSH2_FREE(session, session->scpSend_command); } if (session->scpRecv_err_msg) { LIBSSH2_FREE(session, session->scpRecv_err_msg); } if (session->scpSend_err_msg) { LIBSSH2_FREE(session, session->scpSend_err_msg); } /* Free the error message, if we ar supposed to */ if (session->err_msg && session->err_should_free) { LIBSSH2_FREE(session, session->err_msg); } /* Cleanup any remaining packets */ while (session->packets.head) { LIBSSH2_PACKET *tmp = session->packets.head; /* unlink */ session->packets.head = tmp->next; /* free */ LIBSSH2_FREE(session, tmp->data); LIBSSH2_FREE(session, tmp); } LIBSSH2_FREE(session, session); return 0; }
/* * scp_recv * * Open a channel and request a remote file via SCP * */ static LIBSSH2_CHANNEL * scp_recv(LIBSSH2_SESSION * session, const char *path, struct stat * sb) { int cmd_len; int rc; int tmp_err_code; const char *tmp_err_msg; if (session->scpRecv_state == libssh2_NB_state_idle) { session->scpRecv_mode = 0; session->scpRecv_size = 0; session->scpRecv_mtime = 0; session->scpRecv_atime = 0; session->scpRecv_command_len = _libssh2_shell_quotedsize(path) + sizeof("scp -f ") + (sb?1:0); session->scpRecv_command = LIBSSH2_ALLOC(session, session->scpRecv_command_len); if (!session->scpRecv_command) { _libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate a command buffer for " "SCP session"); return NULL; } snprintf((char *)session->scpRecv_command, session->scpRecv_command_len, "scp -%sf ", sb?"p":""); cmd_len = strlen((char *)session->scpRecv_command); (void) shell_quotearg(path, &session->scpRecv_command[cmd_len], session->scpRecv_command_len - cmd_len); _libssh2_debug(session, LIBSSH2_TRACE_SCP, "Opening channel for SCP receive"); session->scpRecv_state = libssh2_NB_state_created; } if (session->scpRecv_state == libssh2_NB_state_created) { /* Allocate a channel */ session->scpRecv_channel = _libssh2_channel_open(session, "session", sizeof("session") - 1, LIBSSH2_CHANNEL_WINDOW_DEFAULT, LIBSSH2_CHANNEL_PACKET_DEFAULT, NULL, 0); if (!session->scpRecv_channel) { if (libssh2_session_last_errno(session) != LIBSSH2_ERROR_EAGAIN) { LIBSSH2_FREE(session, session->scpRecv_command); session->scpRecv_command = NULL; session->scpRecv_state = libssh2_NB_state_idle; } else { _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block starting up channel"); } return NULL; } session->scpRecv_state = libssh2_NB_state_sent; } if (session->scpRecv_state == libssh2_NB_state_sent) { /* Request SCP for the desired file */ rc = _libssh2_channel_process_startup(session->scpRecv_channel, "exec", sizeof("exec") - 1, (char *) session->scpRecv_command, session->scpRecv_command_len); if (rc == LIBSSH2_ERROR_EAGAIN) { _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block requesting SCP startup"); return NULL; } else if (rc) { LIBSSH2_FREE(session, session->scpRecv_command); session->scpRecv_command = NULL; goto scp_recv_error; } LIBSSH2_FREE(session, session->scpRecv_command); session->scpRecv_command = NULL; _libssh2_debug(session, LIBSSH2_TRACE_SCP, "Sending initial wakeup"); /* SCP ACK */ session->scpRecv_response[0] = '\0'; session->scpRecv_state = libssh2_NB_state_sent1; } if (session->scpRecv_state == libssh2_NB_state_sent1) { rc = _libssh2_channel_write(session->scpRecv_channel, 0, session->scpRecv_response, 1); if (rc == LIBSSH2_ERROR_EAGAIN) { _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block sending initial wakeup"); return NULL; } else if (rc != 1) { goto scp_recv_error; } /* Parse SCP response */ session->scpRecv_response_len = 0; session->scpRecv_state = libssh2_NB_state_sent2; } if ((session->scpRecv_state == libssh2_NB_state_sent2) || (session->scpRecv_state == libssh2_NB_state_sent3)) { while (sb && (session->scpRecv_response_len < LIBSSH2_SCP_RESPONSE_BUFLEN)) { unsigned char *s, *p; if (session->scpRecv_state == libssh2_NB_state_sent2) { rc = _libssh2_channel_read(session->scpRecv_channel, 0, (char *) session-> scpRecv_response + session->scpRecv_response_len, 1); if (rc == LIBSSH2_ERROR_EAGAIN) { _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block waiting for SCP response"); return NULL; } else if (rc < 0) { /* error, give up */ _libssh2_error(session, rc, "Failed reading SCP response"); goto scp_recv_error; } else if(rc == 0) goto scp_recv_empty_channel; session->scpRecv_response_len++; if (session->scpRecv_response[0] != 'T') { size_t err_len; char *err_msg; /* there can be 01 for warnings 02 for errors The following string MUST be newline terminated */ err_len = _libssh2_channel_packet_data_len(session-> scpRecv_channel, 0); err_msg = LIBSSH2_ALLOC(session, err_len + 1); if (!err_msg) { _libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Failed to get memory "); goto scp_recv_error; } /* Read the remote error message */ (void)_libssh2_channel_read(session->scpRecv_channel, 0, err_msg, err_len); /* If it failed for any reason, we ignore it anyway. */ /* zero terminate the error */ err_msg[err_len]=0; _libssh2_debug(session, LIBSSH2_TRACE_SCP, "got %02x %s", session->scpRecv_response[0], err_msg); _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Failed to recv file"); LIBSSH2_FREE(session, err_msg); goto scp_recv_error; } if ((session->scpRecv_response_len > 1) && ((session-> scpRecv_response[session->scpRecv_response_len - 1] < '0') || (session-> scpRecv_response[session->scpRecv_response_len - 1] > '9')) && (session-> scpRecv_response[session->scpRecv_response_len - 1] != ' ') && (session-> scpRecv_response[session->scpRecv_response_len - 1] != '\r') && (session-> scpRecv_response[session->scpRecv_response_len - 1] != '\n')) { _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid data in SCP response"); goto scp_recv_error; } if ((session->scpRecv_response_len < 9) || (session-> scpRecv_response[session->scpRecv_response_len - 1] != '\n')) { if (session->scpRecv_response_len == LIBSSH2_SCP_RESPONSE_BUFLEN) { /* You had your chance */ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Unterminated response from SCP server"); goto scp_recv_error; } /* Way too short to be an SCP response, or not done yet, short circuit */ continue; } /* We're guaranteed not to go under response_len == 0 by the logic above */ while ((session-> scpRecv_response[session->scpRecv_response_len - 1] == '\r') || (session-> scpRecv_response[session->scpRecv_response_len - 1] == '\n')) session->scpRecv_response_len--; session->scpRecv_response[session->scpRecv_response_len] = '\0'; if (session->scpRecv_response_len < 8) { /* EOL came too soon */ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, " "too short" ); goto scp_recv_error; } s = session->scpRecv_response + 1; p = (unsigned char *) strchr((char *) s, ' '); if (!p || ((p - s) <= 0)) { /* No spaces or space in the wrong spot */ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, " "malformed mtime"); goto scp_recv_error; } *(p++) = '\0'; /* Make sure we don't get fooled by leftover values */ session->scpRecv_mtime = strtol((char *) s, NULL, 10); s = (unsigned char *) strchr((char *) p, ' '); if (!s || ((s - p) <= 0)) { /* No spaces or space in the wrong spot */ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, malformed mtime.usec"); goto scp_recv_error; } /* Ignore mtime.usec */ s++; p = (unsigned char *) strchr((char *) s, ' '); if (!p || ((p - s) <= 0)) { /* No spaces or space in the wrong spot */ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, too short or malformed"); goto scp_recv_error; } *p = '\0'; /* Make sure we don't get fooled by leftover values */ session->scpRecv_atime = strtol((char *) s, NULL, 10); /* SCP ACK */ session->scpRecv_response[0] = '\0'; session->scpRecv_state = libssh2_NB_state_sent3; } if (session->scpRecv_state == libssh2_NB_state_sent3) { rc = _libssh2_channel_write(session->scpRecv_channel, 0, session->scpRecv_response, 1); if (rc == LIBSSH2_ERROR_EAGAIN) { _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block waiting to send SCP ACK"); return NULL; } else if (rc != 1) { goto scp_recv_error; } _libssh2_debug(session, LIBSSH2_TRACE_SCP, "mtime = %ld, atime = %ld", session->scpRecv_mtime, session->scpRecv_atime); /* We *should* check that atime.usec is valid, but why let that stop use? */ break; } } session->scpRecv_state = libssh2_NB_state_sent4; } if (session->scpRecv_state == libssh2_NB_state_sent4) { session->scpRecv_response_len = 0; session->scpRecv_state = libssh2_NB_state_sent5; } if ((session->scpRecv_state == libssh2_NB_state_sent5) || (session->scpRecv_state == libssh2_NB_state_sent6)) { while (session->scpRecv_response_len < LIBSSH2_SCP_RESPONSE_BUFLEN) { char *s, *p, *e = NULL; if (session->scpRecv_state == libssh2_NB_state_sent5) { rc = _libssh2_channel_read(session->scpRecv_channel, 0, (char *) session-> scpRecv_response + session->scpRecv_response_len, 1); if (rc == LIBSSH2_ERROR_EAGAIN) { _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block waiting for SCP response"); return NULL; } else if (rc < 0) { /* error, bail out*/ _libssh2_error(session, rc, "Failed reading SCP response"); goto scp_recv_error; } else if(rc == 0) goto scp_recv_empty_channel; session->scpRecv_response_len++; if (session->scpRecv_response[0] != 'C') { _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server"); goto scp_recv_error; } if ((session->scpRecv_response_len > 1) && (session-> scpRecv_response[session->scpRecv_response_len - 1] != '\r') && (session-> scpRecv_response[session->scpRecv_response_len - 1] != '\n') && (session-> scpRecv_response[session->scpRecv_response_len - 1] < 32)) { _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid data in SCP response"); goto scp_recv_error; } if ((session->scpRecv_response_len < 7) || (session-> scpRecv_response[session->scpRecv_response_len - 1] != '\n')) { if (session->scpRecv_response_len == LIBSSH2_SCP_RESPONSE_BUFLEN) { /* You had your chance */ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Unterminated response from SCP server"); goto scp_recv_error; } /* Way too short to be an SCP response, or not done yet, short circuit */ continue; } /* We're guaranteed not to go under response_len == 0 by the logic above */ while ((session-> scpRecv_response[session->scpRecv_response_len - 1] == '\r') || (session-> scpRecv_response[session->scpRecv_response_len - 1] == '\n')) { session->scpRecv_response_len--; } session->scpRecv_response[session->scpRecv_response_len] = '\0'; if (session->scpRecv_response_len < 6) { /* EOL came too soon */ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, too short"); goto scp_recv_error; } s = (char *) session->scpRecv_response + 1; p = strchr(s, ' '); if (!p || ((p - s) <= 0)) { /* No spaces or space in the wrong spot */ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, malformed mode"); goto scp_recv_error; } *(p++) = '\0'; /* Make sure we don't get fooled by leftover values */ session->scpRecv_mode = strtol(s, &e, 8); if (e && *e) { _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, invalid mode"); goto scp_recv_error; } s = strchr(p, ' '); if (!s || ((s - p) <= 0)) { /* No spaces or space in the wrong spot */ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, too short or malformed"); goto scp_recv_error; } *s = '\0'; /* Make sure we don't get fooled by leftover values */ session->scpRecv_size = scpsize_strtol(p, &e, 10); if (e && *e) { _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, invalid size"); goto scp_recv_error; } /* SCP ACK */ session->scpRecv_response[0] = '\0'; session->scpRecv_state = libssh2_NB_state_sent6; } if (session->scpRecv_state == libssh2_NB_state_sent6) { rc = _libssh2_channel_write(session->scpRecv_channel, 0, session->scpRecv_response, 1); if (rc == LIBSSH2_ERROR_EAGAIN) { _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block sending SCP ACK"); return NULL; } else if (rc != 1) { goto scp_recv_error; } _libssh2_debug(session, LIBSSH2_TRACE_SCP, "mode = 0%lo size = %ld", session->scpRecv_mode, session->scpRecv_size); /* We *should* check that basename is valid, but why let that stop us? */ break; } } session->scpRecv_state = libssh2_NB_state_sent7; } if (sb) { memset(sb, 0, sizeof(struct stat)); sb->st_mtime = session->scpRecv_mtime; sb->st_atime = session->scpRecv_atime; sb->st_size = session->scpRecv_size; sb->st_mode = session->scpRecv_mode; } session->scpRecv_state = libssh2_NB_state_idle; return session->scpRecv_channel; scp_recv_empty_channel: /* the code only jumps here as a result of a zero read from channel_read() so we check EOF status to avoid getting stuck in a loop */ if(libssh2_channel_eof(session->scpRecv_channel)) _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Unexpected channel close"); else return session->scpRecv_channel; /* fall-through */ scp_recv_error: tmp_err_code = session->err_code; tmp_err_msg = session->err_msg; while (libssh2_channel_free(session->scpRecv_channel) == LIBSSH2_ERROR_EAGAIN); session->err_code = tmp_err_code; session->err_msg = tmp_err_msg; session->scpRecv_channel = NULL; session->scpRecv_state = libssh2_NB_state_idle; return NULL; }
/* {{{ libssh2_banner_receive * Wait for a hello from the remote host * Allocate a buffer and store the banner in session->remote.banner * Returns: 0 on success, PACKET_EAGAIN if read would block, 1 on failure */ static int libssh2_banner_receive(LIBSSH2_SESSION * session) { int ret; int banner_len; if (session->banner_TxRx_state == libssh2_NB_state_idle) { banner_len = 0; session->banner_TxRx_state = libssh2_NB_state_created; } else { banner_len = session->banner_TxRx_total_send; } while ((banner_len < (int) sizeof(session->banner_TxRx_banner)) && ((banner_len == 0) || (session->banner_TxRx_banner[banner_len - 1] != '\n'))) { char c = '\0'; ret = recv(session->socket_fd, &c, 1, LIBSSH2_SOCKET_RECV_FLAGS(session)); if (ret < 0) { #ifdef WIN32 switch (WSAGetLastError()) { case WSAEWOULDBLOCK: errno = EAGAIN; break; case WSAENOTSOCK: errno = EBADF; break; case WSAENOTCONN: case WSAECONNABORTED: errno = WSAENOTCONN; break; case WSAEINTR: errno = EINTR; break; } #endif /* WIN32 */ if (errno == EAGAIN) { session->banner_TxRx_total_send = banner_len; return PACKET_EAGAIN; } /* Some kinda error */ session->banner_TxRx_state = libssh2_NB_state_idle; session->banner_TxRx_total_send = 0; return 1; } if (ret == 0) { session->socket_state = LIBSSH2_SOCKET_DISCONNECTED; return PACKET_FAIL; } if (c == '\0') { /* NULLs are not allowed in SSH banners */ session->banner_TxRx_state = libssh2_NB_state_idle; session->banner_TxRx_total_send = 0; return 1; } session->banner_TxRx_banner[banner_len++] = c; } while (banner_len && ((session->banner_TxRx_banner[banner_len - 1] == '\n') || (session->banner_TxRx_banner[banner_len - 1] == '\r'))) { banner_len--; } /* From this point on, we are done here */ session->banner_TxRx_state = libssh2_NB_state_idle; session->banner_TxRx_total_send = 0; if (!banner_len) return 1; session->remote.banner = LIBSSH2_ALLOC(session, banner_len + 1); if (!session->remote.banner) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Error allocating space for remote banner", 0); return 1; } memcpy(session->remote.banner, session->banner_TxRx_banner, banner_len); session->remote.banner[banner_len] = '\0'; _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Received Banner: %s", session->remote.banner); return 0; }
/* * scp_send() * * Send a file using SCP * */ static LIBSSH2_CHANNEL * scp_send(LIBSSH2_SESSION * session, const char *path, int mode, libssh2_int64_t size, time_t mtime, time_t atime) { int cmd_len; int rc; int tmp_err_code; const char *tmp_err_msg; if (session->scpSend_state == libssh2_NB_state_idle) { session->scpSend_command_len = _libssh2_shell_quotedsize(path) + sizeof("scp -t ") + ((mtime || atime)?1:0); session->scpSend_command = LIBSSH2_ALLOC(session, session->scpSend_command_len); if (!session->scpSend_command) { _libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate a command buffer for scp session"); return NULL; } snprintf((char *)session->scpSend_command, session->scpSend_command_len, "scp -%st ", (mtime || atime)?"p":""); cmd_len = strlen((char *)session->scpSend_command); (void)shell_quotearg(path, &session->scpSend_command[cmd_len], session->scpSend_command_len - cmd_len); session->scpSend_command[session->scpSend_command_len - 1] = '\0'; _libssh2_debug(session, LIBSSH2_TRACE_SCP, "Opening channel for SCP send"); /* Allocate a channel */ session->scpSend_state = libssh2_NB_state_created; } if (session->scpSend_state == libssh2_NB_state_created) { session->scpSend_channel = _libssh2_channel_open(session, "session", sizeof("session") - 1, LIBSSH2_CHANNEL_WINDOW_DEFAULT, LIBSSH2_CHANNEL_PACKET_DEFAULT, NULL, 0); if (!session->scpSend_channel) { if (libssh2_session_last_errno(session) != LIBSSH2_ERROR_EAGAIN) { /* previous call set libssh2_session_last_error(), pass it through */ LIBSSH2_FREE(session, session->scpSend_command); session->scpSend_command = NULL; session->scpSend_state = libssh2_NB_state_idle; } else { _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block starting up channel"); } return NULL; } session->scpSend_state = libssh2_NB_state_sent; } if (session->scpSend_state == libssh2_NB_state_sent) { /* Request SCP for the desired file */ rc = _libssh2_channel_process_startup(session->scpSend_channel, "exec", sizeof("exec") - 1, (char *) session->scpSend_command, session->scpSend_command_len); if (rc == LIBSSH2_ERROR_EAGAIN) { _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block requesting SCP startup"); return NULL; } else if (rc) { /* previous call set libssh2_session_last_error(), pass it through */ LIBSSH2_FREE(session, session->scpSend_command); session->scpSend_command = NULL; _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Unknown error while getting error string"); goto scp_send_error; } LIBSSH2_FREE(session, session->scpSend_command); session->scpSend_command = NULL; session->scpSend_state = libssh2_NB_state_sent1; } if (session->scpSend_state == libssh2_NB_state_sent1) { /* Wait for ACK */ rc = _libssh2_channel_read(session->scpSend_channel, 0, (char *) session->scpSend_response, 1); if (rc == LIBSSH2_ERROR_EAGAIN) { _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block waiting for response from remote"); return NULL; } else if (rc < 0) { _libssh2_error(session, rc, "SCP failure"); goto scp_send_error; } else if(!rc) /* remain in the same state */ goto scp_send_empty_channel; else if (session->scpSend_response[0] != 0) { _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid ACK response from remote"); goto scp_send_error; } if (mtime || atime) { /* Send mtime and atime to be used for file */ session->scpSend_response_len = snprintf((char *) session->scpSend_response, LIBSSH2_SCP_RESPONSE_BUFLEN, "T%ld 0 %ld 0\n", (long)mtime, (long)atime); _libssh2_debug(session, LIBSSH2_TRACE_SCP, "Sent %s", session->scpSend_response); } session->scpSend_state = libssh2_NB_state_sent2; } /* Send mtime and atime to be used for file */ if (mtime || atime) { if (session->scpSend_state == libssh2_NB_state_sent2) { rc = _libssh2_channel_write(session->scpSend_channel, 0, session->scpSend_response, session->scpSend_response_len); if (rc == LIBSSH2_ERROR_EAGAIN) { _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block sending time data for SCP file"); return NULL; } else if (rc != (int)session->scpSend_response_len) { _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send time data for SCP file"); goto scp_send_error; } session->scpSend_state = libssh2_NB_state_sent3; } if (session->scpSend_state == libssh2_NB_state_sent3) { /* Wait for ACK */ rc = _libssh2_channel_read(session->scpSend_channel, 0, (char *) session->scpSend_response, 1); if (rc == LIBSSH2_ERROR_EAGAIN) { _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block waiting for response"); return NULL; } else if (rc < 0) { _libssh2_error(session, rc, "SCP failure"); goto scp_send_error; } else if(!rc) /* remain in the same state */ goto scp_send_empty_channel; else if (session->scpSend_response[0] != 0) { _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid SCP ACK response"); goto scp_send_error; } session->scpSend_state = libssh2_NB_state_sent4; } } else { if (session->scpSend_state == libssh2_NB_state_sent2) { session->scpSend_state = libssh2_NB_state_sent4; } } if (session->scpSend_state == libssh2_NB_state_sent4) { /* Send mode, size, and basename */ const char *base = strrchr(path, '/'); if (base) base++; else base = path; session->scpSend_response_len = snprintf((char *) session->scpSend_response, LIBSSH2_SCP_RESPONSE_BUFLEN, "C0%o %" LIBSSH2_INT64_T_FORMAT " %s\n", mode, size, base); _libssh2_debug(session, LIBSSH2_TRACE_SCP, "Sent %s", session->scpSend_response); session->scpSend_state = libssh2_NB_state_sent5; } if (session->scpSend_state == libssh2_NB_state_sent5) { rc = _libssh2_channel_write(session->scpSend_channel, 0, session->scpSend_response, session->scpSend_response_len); if (rc == LIBSSH2_ERROR_EAGAIN) { _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block send core file data for SCP file"); return NULL; } else if (rc != (int)session->scpSend_response_len) { _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send core file data for SCP file"); goto scp_send_error; } session->scpSend_state = libssh2_NB_state_sent6; } if (session->scpSend_state == libssh2_NB_state_sent6) { /* Wait for ACK */ rc = _libssh2_channel_read(session->scpSend_channel, 0, (char *) session->scpSend_response, 1); if (rc == LIBSSH2_ERROR_EAGAIN) { _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block waiting for response"); return NULL; } else if (rc < 0) { _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid ACK response from remote"); goto scp_send_error; } else if (rc == 0) goto scp_send_empty_channel; else if (session->scpSend_response[0] != 0) { size_t err_len; char *err_msg; err_len = _libssh2_channel_packet_data_len(session->scpSend_channel, 0); err_msg = LIBSSH2_ALLOC(session, err_len + 1); if (!err_msg) { _libssh2_error(session, LIBSSH2_ERROR_ALLOC, "failed to get memory"); goto scp_send_error; } /* Read the remote error message */ rc = _libssh2_channel_read(session->scpSend_channel, 0, err_msg, err_len); if (rc > 0) { err_msg[err_len]=0; _libssh2_debug(session, LIBSSH2_TRACE_SCP, "got %02x %s", session->scpSend_response[0], err_msg); } LIBSSH2_FREE(session, err_msg); _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "failed to send file"); goto scp_send_error; } } session->scpSend_state = libssh2_NB_state_idle; return session->scpSend_channel; scp_send_empty_channel: /* the code only jumps here as a result of a zero read from channel_read() so we check EOF status to avoid getting stuck in a loop */ if(libssh2_channel_eof(session->scpSend_channel)) { _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Unexpected channel close"); } else return session->scpSend_channel; /* fall-through */ scp_send_error: tmp_err_code = session->err_code; tmp_err_msg = session->err_msg; while (libssh2_channel_free(session->scpSend_channel) == LIBSSH2_ERROR_EAGAIN); session->err_code = tmp_err_code; session->err_msg = tmp_err_msg; session->scpSend_channel = NULL; session->scpSend_state = libssh2_NB_state_idle; return NULL; }
/* {{{ libssh2_scp_recv * Open a channel and request a remote file via SCP * * NOTE: Will block in a busy loop on error. This has to be done, * otherwise the blocking error code would erase the true * cause of the error. */ LIBSSH2_API LIBSSH2_CHANNEL * libssh2_scp_recv(LIBSSH2_SESSION * session, const char *path, struct stat * sb) { int path_len = strlen(path); int rc; if (session->scpRecv_state == libssh2_NB_state_idle) { session->scpRecv_mode = 0; session->scpRecv_size = 0; session->scpRecv_mtime = 0; session->scpRecv_atime = 0; session->scpRecv_command_len = path_len + sizeof("scp -f "); if (sb) { session->scpRecv_command_len++; } session->scpRecv_command = LIBSSH2_ALLOC(session, session->scpRecv_command_len); if (!session->scpRecv_command) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate a command buffer for SCP session", 0); return NULL; } if (sb) { memcpy(session->scpRecv_command, "scp -pf ", sizeof("scp -pf ") - 1); memcpy(session->scpRecv_command + sizeof("scp -pf ") - 1, path, path_len); } else { memcpy(session->scpRecv_command, "scp -f ", sizeof("scp -f ") - 1); memcpy(session->scpRecv_command + sizeof("scp -f ") - 1, path, path_len); } session->scpRecv_command[session->scpRecv_command_len - 1] = '\0'; _libssh2_debug(session, LIBSSH2_DBG_SCP, "Opening channel for SCP receive"); session->scpRecv_state = libssh2_NB_state_created; } if (session->scpRecv_state == libssh2_NB_state_created) { /* Allocate a channel */ do { session->scpRecv_channel = libssh2_channel_open_ex(session, "session", sizeof("session") - 1, LIBSSH2_CHANNEL_WINDOW_DEFAULT, LIBSSH2_CHANNEL_PACKET_DEFAULT, NULL, 0); if (!session->scpRecv_channel) { if (libssh2_session_last_errno(session) != LIBSSH2_ERROR_EAGAIN) { LIBSSH2_FREE(session, session->scpRecv_command); session->scpRecv_command = NULL; session->scpRecv_state = libssh2_NB_state_idle; return NULL; } else if (libssh2_session_last_errno(session) == LIBSSH2_ERROR_EAGAIN) { libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block starting up channel", 0); return NULL; } } } while (!session->scpRecv_channel); session->scpRecv_state = libssh2_NB_state_sent; } if (session->scpRecv_state == libssh2_NB_state_sent) { /* Request SCP for the desired file */ rc = libssh2_channel_process_startup(session->scpRecv_channel, "exec", sizeof("exec") - 1, (char *) session->scpRecv_command, session->scpRecv_command_len); if (rc == PACKET_EAGAIN) { libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block requesting SCP startup", 0); return NULL; } else if (rc) { LIBSSH2_FREE(session, session->scpRecv_command); session->scpRecv_command = NULL; goto scp_recv_error; } LIBSSH2_FREE(session, session->scpRecv_command); session->scpRecv_command = NULL; _libssh2_debug(session, LIBSSH2_DBG_SCP, "Sending initial wakeup"); /* SCP ACK */ session->scpRecv_response[0] = '\0'; session->scpRecv_state = libssh2_NB_state_sent1; } if (session->scpRecv_state == libssh2_NB_state_sent1) { rc = libssh2_channel_write_ex(session->scpRecv_channel, 0, (char *) session->scpRecv_response, 1); if (rc == PACKET_EAGAIN) { libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block sending initial wakeup", 0); return NULL; } else if (rc != 1) { goto scp_recv_error; } /* Parse SCP response */ session->scpRecv_response_len = 0; session->scpRecv_state = libssh2_NB_state_sent2; } if ((session->scpRecv_state == libssh2_NB_state_sent2) || (session->scpRecv_state == libssh2_NB_state_sent3)) { while (sb && (session->scpRecv_response_len < LIBSSH2_SCP_RESPONSE_BUFLEN)) { unsigned char *s, *p; if (session->scpRecv_state == libssh2_NB_state_sent2) { rc = libssh2_channel_read_ex(session->scpRecv_channel, 0, (char *) session-> scpRecv_response + session->scpRecv_response_len, 1); if (rc == PACKET_EAGAIN) { libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block waiting for SCP response", 0); return NULL; } else if (rc <= 0) { /* Timeout, give up */ libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Timed out waiting for SCP response", 0); goto scp_recv_error; } session->scpRecv_response_len++; if (session->scpRecv_response[0] != 'T') { /* * Set this as the default error for here, if * we are successful it will be replaced */ libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid data in SCP response, missing Time data", 0); session->scpRecv_err_len = libssh2_channel_packet_data_len(session-> scpRecv_channel, 0); session->scpRecv_err_msg = LIBSSH2_ALLOC(session, session->scpRecv_err_len + 1); if (!session->scpRecv_err_msg) { goto scp_recv_error; } memset(session->scpRecv_err_msg, 0, session->scpRecv_err_len + 1); /* Read the remote error message */ rc = libssh2_channel_read_ex(session->scpRecv_channel, 0, session->scpRecv_err_msg, session->scpRecv_err_len); if (rc <= 0) { /* * Since we have alread started reading this packet, it is * already in the systems so it can't return PACKET_EAGAIN */ LIBSSH2_FREE(session, session->scpRecv_err_msg); session->scpRecv_err_msg = NULL; libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Unknown error while getting error string", 0); goto scp_recv_error; } libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, session->scpRecv_err_msg, 1); session->scpRecv_err_msg = NULL; goto scp_recv_error; } if ((session->scpRecv_response_len > 1) && ((session-> scpRecv_response[session->scpRecv_response_len - 1] < '0') || (session-> scpRecv_response[session->scpRecv_response_len - 1] > '9')) && (session-> scpRecv_response[session->scpRecv_response_len - 1] != ' ') && (session-> scpRecv_response[session->scpRecv_response_len - 1] != '\r') && (session-> scpRecv_response[session->scpRecv_response_len - 1] != '\n')) { libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid data in SCP response", 0); goto scp_recv_error; } if ((session->scpRecv_response_len < 9) || (session-> scpRecv_response[session->scpRecv_response_len - 1] != '\n')) { if (session->scpRecv_response_len == LIBSSH2_SCP_RESPONSE_BUFLEN) { /* You had your chance */ libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Unterminated response from SCP server", 0); goto scp_recv_error; } /* Way too short to be an SCP response, or not done yet, short circuit */ continue; } /* We're guaranteed not to go under response_len == 0 by the logic above */ while ((session-> scpRecv_response[session->scpRecv_response_len - 1] == '\r') || (session-> scpRecv_response[session->scpRecv_response_len - 1] == '\n')) session->scpRecv_response_len--; session->scpRecv_response[session->scpRecv_response_len] = '\0'; if (session->scpRecv_response_len < 8) { /* EOL came too soon */ libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, too short", 0); goto scp_recv_error; } s = session->scpRecv_response + 1; p = (unsigned char *) strchr((char *) s, ' '); if (!p || ((p - s) <= 0)) { /* No spaces or space in the wrong spot */ libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, malformed mtime", 0); goto scp_recv_error; } *(p++) = '\0'; /* Make sure we don't get fooled by leftover values */ errno = 0; session->scpRecv_mtime = strtol((char *) s, NULL, 10); if (errno) { libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, invalid mtime", 0); goto scp_recv_error; } s = (unsigned char *) strchr((char *) p, ' '); if (!s || ((s - p) <= 0)) { /* No spaces or space in the wrong spot */ libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, malformed mtime.usec", 0); goto scp_recv_error; } /* Ignore mtime.usec */ s++; p = (unsigned char *) strchr((char *) s, ' '); if (!p || ((p - s) <= 0)) { /* No spaces or space in the wrong spot */ libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, too short or malformed", 0); goto scp_recv_error; } *(p++) = '\0'; /* Make sure we don't get fooled by leftover values */ errno = 0; session->scpRecv_atime = strtol((char *) s, NULL, 10); if (errno) { libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, invalid atime", 0); goto scp_recv_error; } /* SCP ACK */ session->scpRecv_response[0] = '\0'; session->scpRecv_state = libssh2_NB_state_sent3; } if (session->scpRecv_state == libssh2_NB_state_sent3) { rc = libssh2_channel_write_ex(session->scpRecv_channel, 0, (char *) session-> scpRecv_response, 1); if (rc == PACKET_EAGAIN) { libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block waiting to send SCP ACK", 0); return NULL; } else if (rc != 1) { goto scp_recv_error; } _libssh2_debug(session, LIBSSH2_DBG_SCP, "mtime = %ld, atime = %ld", session->scpRecv_mtime, session->scpRecv_atime); /* We *should* check that atime.usec is valid, but why let that stop use? */ break; } } session->scpRecv_state = libssh2_NB_state_sent4; } if (session->scpRecv_state == libssh2_NB_state_sent4) { session->scpRecv_response_len = 0; session->scpRecv_state = libssh2_NB_state_sent5; } if ((session->scpRecv_state == libssh2_NB_state_sent5) || (session->scpRecv_state == libssh2_NB_state_sent6)) { while (session->scpRecv_response_len < LIBSSH2_SCP_RESPONSE_BUFLEN) { char *s, *p, *e = NULL; if (session->scpRecv_state == libssh2_NB_state_sent5) { rc = libssh2_channel_read_ex(session->scpRecv_channel, 0, (char *) session-> scpRecv_response + session->scpRecv_response_len, 1); if (rc == PACKET_EAGAIN) { libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block waiting for SCP response", 0); return NULL; } else if (rc <= 0) { /* Timeout, give up */ libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Timed out waiting for SCP response", 0); goto scp_recv_error; } session->scpRecv_response_len++; if (session->scpRecv_response[0] != 'C') { libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server", 0); goto scp_recv_error; } if ((session->scpRecv_response_len > 1) && (session-> scpRecv_response[session->scpRecv_response_len - 1] != '\r') && (session-> scpRecv_response[session->scpRecv_response_len - 1] != '\n') && ((session-> scpRecv_response[session->scpRecv_response_len - 1] < 32) || (session-> scpRecv_response[session->scpRecv_response_len - 1] > 126))) { libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid data in SCP response", 0); goto scp_recv_error; } if ((session->scpRecv_response_len < 7) || (session-> scpRecv_response[session->scpRecv_response_len - 1] != '\n')) { if (session->scpRecv_response_len == LIBSSH2_SCP_RESPONSE_BUFLEN) { /* You had your chance */ libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Unterminated response from SCP server", 0); goto scp_recv_error; } /* Way too short to be an SCP response, or not done yet, short circuit */ continue; } /* We're guaranteed not to go under response_len == 0 by the logic above */ while ((session-> scpRecv_response[session->scpRecv_response_len - 1] == '\r') || (session-> scpRecv_response[session->scpRecv_response_len - 1] == '\n')) { session->scpRecv_response_len--; } session->scpRecv_response[session->scpRecv_response_len] = '\0'; if (session->scpRecv_response_len < 6) { /* EOL came too soon */ libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, too short", 0); goto scp_recv_error; } s = (char *) session->scpRecv_response + 1; p = strchr(s, ' '); if (!p || ((p - s) <= 0)) { /* No spaces or space in the wrong spot */ libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, malformed mode", 0); goto scp_recv_error; } *(p++) = '\0'; /* Make sure we don't get fooled by leftover values */ errno = 0; session->scpRecv_mode = strtol(s, &e, 8); if ((e && *e) || errno) { libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, invalid mode", 0); goto scp_recv_error; } s = strchr(p, ' '); if (!s || ((s - p) <= 0)) { /* No spaces or space in the wrong spot */ libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, too short or malformed", 0); goto scp_recv_error; } *(s++) = '\0'; /* Make sure we don't get fooled by leftover values */ errno = 0; session->scpRecv_size = scpsize_strtol(p, &e, 10); if ((e && *e) || errno) { libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, invalid size", 0); goto scp_recv_error; } /* SCP ACK */ session->scpRecv_response[0] = '\0'; session->scpRecv_state = libssh2_NB_state_sent6; } if (session->scpRecv_state == libssh2_NB_state_sent6) { rc = libssh2_channel_write_ex(session->scpRecv_channel, 0, (char *) session-> scpRecv_response, 1); if (rc == PACKET_EAGAIN) { libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block sending SCP ACK", 0); return NULL; } else if (rc != 1) { goto scp_recv_error; } _libssh2_debug(session, LIBSSH2_DBG_SCP, "mode = 0%lo size = %ld", session->scpRecv_mode, session->scpRecv_size); /* We *should* check that basename is valid, but why let that stop us? */ break; } } session->scpRecv_state = libssh2_NB_state_sent7; } if (sb) { memset(sb, 0, sizeof(struct stat)); sb->st_mtime = session->scpRecv_mtime; sb->st_atime = session->scpRecv_atime; sb->st_size = session->scpRecv_size; sb->st_mode = session->scpRecv_mode; } session->scpRecv_state = libssh2_NB_state_idle; return session->scpRecv_channel; scp_recv_error: while (libssh2_channel_free(session->scpRecv_channel) == PACKET_EAGAIN); session->scpRecv_channel = NULL; session->scpRecv_state = libssh2_NB_state_idle; return NULL; }
static int gen_publickey_from_ec_evp(LIBSSH2_SESSION *session, unsigned char **method, size_t *method_len, unsigned char **pubkeydata, size_t *pubkeydata_len, EVP_PKEY *pk) { int rc = 0; EC_KEY *ec = NULL; unsigned char *p; unsigned char *method_buf = NULL; unsigned char *key; size_t key_len = 0; unsigned char *octal_value = NULL; size_t octal_len; const EC_POINT *public_key; const EC_GROUP *group; BN_CTX *bn_ctx; libssh2_curve_type type; _libssh2_debug(session, LIBSSH2_TRACE_AUTH, "Computing public key from EC private key envelop"); bn_ctx = BN_CTX_new(); if(bn_ctx == NULL) return -1; ec = EVP_PKEY_get1_EC_KEY(pk); if(ec == NULL) { rc = -1; goto clean_exit; } public_key = EC_KEY_get0_public_key(ec); group = EC_KEY_get0_group(ec); type = _libssh2_ecdsa_key_get_curve_type(ec); method_buf = LIBSSH2_ALLOC(session, 19); if(method_buf == NULL) { return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, "out of memory"); } if(type == LIBSSH2_EC_CURVE_NISTP256) memcpy(method_buf, "ecdsa-sha2-nistp256", 19); else if(type == LIBSSH2_EC_CURVE_NISTP384) memcpy(method_buf, "ecdsa-sha2-nistp384", 19); else if(type == LIBSSH2_EC_CURVE_NISTP521) memcpy(method_buf, "ecdsa-sha2-nistp521", 19); else { _libssh2_debug(session, LIBSSH2_TRACE_ERROR, "Unsupported EC private key type"); rc = -1; goto clean_exit; } /* get length */ octal_len = EC_POINT_point2oct(group, public_key, POINT_CONVERSION_UNCOMPRESSED, NULL, 0, bn_ctx); if(octal_len > EC_MAX_POINT_LEN) { rc = -1; goto clean_exit; } octal_value = malloc(octal_len); if(octal_value == NULL) { rc = -1; goto clean_exit; } /* convert to octal */ if(EC_POINT_point2oct(group, public_key, POINT_CONVERSION_UNCOMPRESSED, octal_value, octal_len, bn_ctx) != octal_len) { rc = -1; goto clean_exit; } /* Key form is: type_len(4) + type(19) + domain_len(4) + domain(8) + pub_key_len(4) + pub_key(~65). */ key_len = 4 + 19 + 4 + 8 + 4 + octal_len; key = LIBSSH2_ALLOC(session, key_len); if(key == NULL) { rc = -1; goto clean_exit; } /* Process key encoding. */ p = key; /* Key type */ _libssh2_store_str(&p, (const char *)method_buf, 19); /* Name domain */ _libssh2_store_str(&p, (const char *)method_buf + 11, 8); /* Public key */ _libssh2_store_str(&p, (const char *)octal_value, octal_len); *method = method_buf; *method_len = 19; *pubkeydata = key; *pubkeydata_len = key_len; clean_exit: if(ec != NULL) EC_KEY_free(ec); if(bn_ctx != NULL) { BN_CTX_free(bn_ctx); } if(octal_value != NULL) free(octal_value); if(rc == 0) return 0; if(method_buf != NULL) LIBSSH2_FREE(session, method_buf); return -1; }