/* {{{ libssh2_session_last_error * Returns error code and populates an error string into errmsg * If want_buf is non-zero then the string placed into errmsg must be freed by the calling program * Otherwise it is assumed to be owned by libssh2 */ LIBSSH2_API int libssh2_session_last_error(LIBSSH2_SESSION * session, char **errmsg, int *errmsg_len, int want_buf) { /* No error to report */ if (!session->err_code) { if (errmsg) { if (want_buf) { *errmsg = LIBSSH2_ALLOC(session, 1); if (*errmsg) { **errmsg = 0; } } else { *errmsg = (char *) ""; } } if (errmsg_len) { *errmsg_len = 0; } return 0; } if (errmsg) { char *serrmsg = session->err_msg ? session->err_msg : (char *) ""; int ownbuf = session->err_msg ? session->err_should_free : 0; if (want_buf) { if (ownbuf) { /* Just give the calling program the buffer */ *errmsg = serrmsg; session->err_should_free = 0; } else { /* Make a copy so the calling program can own it */ *errmsg = LIBSSH2_ALLOC(session, session->err_msglen + 1); if (*errmsg) { memcpy(*errmsg, session->err_msg, session->err_msglen); (*errmsg)[session->err_msglen] = 0; } } } else { *errmsg = serrmsg; } } if (errmsg_len) { *errmsg_len = session->err_msglen; } return session->err_code; }
static int agent_transact_pageant(LIBSSH2_AGENT *agent, agent_transaction_ctx_t transctx) { HWND hwnd; char mapname[23]; HANDLE filemap; unsigned char *p; unsigned char *p2; int id; COPYDATASTRUCT cds; if (!transctx || 4 + transctx->request_len > PAGEANT_MAX_MSGLEN) return _libssh2_error(agent->session, LIBSSH2_ERROR_INVAL, "illegal input"); hwnd = FindWindow("Pageant", "Pageant"); if (!hwnd) return _libssh2_error(agent->session, LIBSSH2_ERROR_AGENT_PROTOCOL, "found no pageant"); sprintf(mapname, "PageantRequest%08x", (unsigned)GetCurrentThreadId()); filemap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, PAGEANT_MAX_MSGLEN, mapname); if (filemap == NULL || filemap == INVALID_HANDLE_VALUE) return _libssh2_error(agent->session, LIBSSH2_ERROR_AGENT_PROTOCOL, "failed setting up pageant filemap"); p2 = p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0); _libssh2_store_str(&p2, (const char *)transctx->request, transctx->request_len); cds.dwData = PAGEANT_COPYDATA_ID; cds.cbData = 1 + strlen(mapname); cds.lpData = mapname; id = SendMessage(hwnd, WM_COPYDATA, (WPARAM) NULL, (LPARAM) &cds); if (id > 0) { transctx->response_len = _libssh2_ntohu32(p); if (transctx->response_len > PAGEANT_MAX_MSGLEN) { UnmapViewOfFile(p); CloseHandle(filemap); return _libssh2_error(agent->session, LIBSSH2_ERROR_AGENT_PROTOCOL, "agent setup fail"); } transctx->response = LIBSSH2_ALLOC(agent->session, transctx->response_len); if (!transctx->response) { UnmapViewOfFile(p); CloseHandle(filemap); return _libssh2_error(agent->session, LIBSSH2_ERROR_ALLOC, "agent malloc"); } memcpy(transctx->response, p + 4, transctx->response_len); } UnmapViewOfFile(p); CloseHandle(filemap); return 0; }
/* {{{ libssh2_banner_set * Set the local banner */ LIBSSH2_API int libssh2_banner_set(LIBSSH2_SESSION * session, const char *banner) { int banner_len = banner ? strlen(banner) : 0; if (session->local.banner) { LIBSSH2_FREE(session, session->local.banner); session->local.banner = NULL; } if (!banner_len) { return 0; } session->local.banner = LIBSSH2_ALLOC(session, banner_len + 3); if (!session->local.banner) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for local banner", 0); return -1; } memcpy(session->local.banner, banner, banner_len); session->local.banner[banner_len] = '\0'; _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Setting local Banner: %s", session->local.banner); session->local.banner[banner_len++] = '\r'; session->local.banner[banner_len++] = '\n'; session->local.banner[banner_len++] = '\0'; return 0; }
/* * hostkey_method_ssh_dss_signv * * Construct a signature from an array of vectors */ static int hostkey_method_ssh_dss_signv(LIBSSH2_SESSION * session, unsigned char **signature, unsigned long *signature_len, unsigned long veccount, const struct iovec datavec[], void **abstract) { libssh2_dsa_ctx *dsactx = (libssh2_dsa_ctx *) (*abstract); unsigned char hash[SHA_DIGEST_LENGTH]; libssh2_sha1_ctx ctx; unsigned int i; *signature = LIBSSH2_ALLOC(session, 2 * SHA_DIGEST_LENGTH); if (!*signature) { return -1; } *signature_len = 2 * SHA_DIGEST_LENGTH; memset(*signature, 0, 2 * SHA_DIGEST_LENGTH); libssh2_sha1_init(&ctx); for(i = 0; i < veccount; i++) { libssh2_sha1_update(ctx, datavec[i].iov_base, datavec[i].iov_len); } libssh2_sha1_final(ctx, hash); if (_libssh2_dsa_sha1_sign(dsactx, hash, SHA_DIGEST_LENGTH, *signature)) { LIBSSH2_FREE(session, *signature); return -1; } return 0; }
int _libssh2_rsa_sha1_sign(LIBSSH2_SESSION * session, libssh2_rsa_ctx * rsactx, const unsigned char *hash, unsigned long hash_len, unsigned char **signature, unsigned long *signature_len) { int ret; unsigned char *sig; unsigned int sig_len; sig_len = RSA_size(rsactx); sig = LIBSSH2_ALLOC(session, sig_len); if (!sig) { return -1; } ret = RSA_sign(NID_sha1, hash, hash_len, sig, &sig_len, rsactx); if (!ret) { LIBSSH2_FREE(session, sig); return -1; } *signature = sig; *signature_len = sig_len; return 0; }
static voidpf libssh2_comp_method_zlib_alloc(voidpf opaque, uInt items, uInt size) { LIBSSH2_SESSION *session = (LIBSSH2_SESSION *) opaque; return (voidpf) LIBSSH2_ALLOC(session, items * size); }
/* {{{ libssh2_comp_method_zlib_init * All your bandwidth are belong to us (so save some) */ static int libssh2_comp_method_zlib_init(LIBSSH2_SESSION * session, int compress, void **abstract) { z_stream *strm; int status; strm = LIBSSH2_ALLOC(session, sizeof(z_stream)); if (!strm) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for zlib compression/decompression", 0); return -1; } memset(strm, 0, sizeof(z_stream)); strm->opaque = (voidpf) session; strm->zalloc = (alloc_func) libssh2_comp_method_zlib_alloc; strm->zfree = (free_func) libssh2_comp_method_zlib_free; if (compress) { /* deflate */ status = deflateInit(strm, Z_DEFAULT_COMPRESSION); } else { /* inflate */ status = inflateInit(strm); } if (status != Z_OK) { LIBSSH2_FREE(session, strm); return -1; } *abstract = strm; return 0; }
/* {{{ libssh2_publickey_packet_receive * Read a packet from the subsystem */ static int libssh2_publickey_packet_receive(LIBSSH2_PUBLICKEY *pkey, unsigned char **data, unsigned long *data_len) { LIBSSH2_CHANNEL *channel = pkey->channel; LIBSSH2_SESSION *session = channel->session; unsigned char buffer[4]; unsigned long packet_len; unsigned char *packet; if (libssh2_channel_read(channel, buffer, 4) != 4) { libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, "Invalid response from publickey subsystem", 0); return -1; } packet_len = libssh2_ntohu32(buffer); packet = LIBSSH2_ALLOC(session, packet_len); if (!packet) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate publickey response buffer", 0); return -1; } if (libssh2_channel_read(channel, packet, packet_len) != packet_len) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for publickey subsystem response packet", 0); LIBSSH2_FREE(session, packet); return -1; } *data = packet; *data_len = packet_len; return 0; }
static unsigned char * gen_publickey_from_rsa(LIBSSH2_SESSION *session, RSA *rsa, size_t *key_len) { int e_bytes, n_bytes; unsigned long len; unsigned char* key; unsigned char* p; e_bytes = BN_num_bytes(rsa->e) + 1; n_bytes = BN_num_bytes(rsa->n) + 1; /* Key form is "ssh-rsa" + e + n. */ len = 4 + 7 + 4 + e_bytes + 4 + n_bytes; key = LIBSSH2_ALLOC(session, len); if (key == NULL) { return NULL; } /* Process key encoding. */ p = key; _libssh2_htonu32(p, 7); /* Key type. */ p += 4; memcpy(p, "ssh-rsa", 7); p += 7; p = write_bn(p, rsa->e, e_bytes); p = write_bn(p, rsa->n, n_bytes); *key_len = (size_t)(p - key); return key; }
static int agent_transact_unix(LIBSSH2_AGENT *agent, agent_transaction_ctx_t transctx) { unsigned char buf[4]; int rc; /* Send the length of the request */ if (transctx->state == agent_NB_state_request_created) { _libssh2_htonu32(buf, transctx->request_len); rc = send(agent->fd, buf, sizeof buf, 0); if (rc < 0) { if (errno == EAGAIN) return LIBSSH2_ERROR_EAGAIN; return -1; } transctx->state = agent_NB_state_request_length_sent; } /* Send the request body */ if (transctx->state == agent_NB_state_request_length_sent) { rc = send(agent->fd, transctx->request, transctx->request_len, 0); if (rc < 0) { if (errno == EAGAIN) return LIBSSH2_ERROR_EAGAIN; return -1; } transctx->state = agent_NB_state_request_sent; } /* Receive the length of a response */ if (transctx->state == agent_NB_state_request_sent) { rc = recv(agent->fd, buf, sizeof buf, 0); if (rc < 0) { if (errno == EAGAIN) return LIBSSH2_ERROR_EAGAIN; return -1; } transctx->response_len = _libssh2_ntohu32(buf); transctx->response = LIBSSH2_ALLOC(agent->session, transctx->response_len); if (!transctx->response) { return LIBSSH2_ERROR_ALLOC; } transctx->state = agent_NB_state_response_length_received; } /* Receive the response body */ if (transctx->state == agent_NB_state_response_length_received) { rc = recv(agent->fd, transctx->response, transctx->response_len, 0); if (rc < 0) { if (errno == EAGAIN) return LIBSSH2_ERROR_EAGAIN; return -1; } transctx->state = agent_NB_state_response_received; } return 0; }
int _libssh2_rsa_sha1_sign(LIBSSH2_SESSION * session, libssh2_rsa_ctx * rsactx, const unsigned char *hash, size_t hash_len, unsigned char **signature, size_t *signature_len) { gcry_sexp_t sig_sexp; gcry_sexp_t data; int rc; const char *tmp; size_t size; if(hash_len != SHA_DIGEST_LENGTH) { return -1; } if(gcry_sexp_build(&data, NULL, "(data (flags pkcs1) (hash sha1 %b))", hash_len, hash)) { return -1; } rc = gcry_pk_sign(&sig_sexp, data, rsactx); gcry_sexp_release(data); if(rc != 0) { return -1; } data = gcry_sexp_find_token(sig_sexp, "s", 0); if(!data) { return -1; } tmp = gcry_sexp_nth_data(data, 1, &size); if(!tmp) { return -1; } if(tmp[0] == '\0') { tmp++; size--; } *signature = LIBSSH2_ALLOC(session, size); if(!*signature) { return -1; } memcpy(*signature, tmp, size); *signature_len = size; return rc; }
static unsigned char * gen_publickey_from_dsa(LIBSSH2_SESSION* session, DSA *dsa, size_t *key_len) { int p_bytes, q_bytes, g_bytes, k_bytes; unsigned long len; unsigned char *key; unsigned char *p; const BIGNUM * p_bn; const BIGNUM * q; const BIGNUM * g; const BIGNUM * pub_key; #ifdef HAVE_OPAQUE_STRUCTS DSA_get0_pqg(dsa, &p_bn, &q, &g); #else p_bn = dsa->p; q = dsa->q; g = dsa->g; #endif #ifdef HAVE_OPAQUE_STRUCTS DSA_get0_key(dsa, &pub_key, NULL); #else pub_key = dsa->pub_key; #endif p_bytes = BN_num_bytes(p_bn) + 1; q_bytes = BN_num_bytes(q) + 1; g_bytes = BN_num_bytes(g) + 1; k_bytes = BN_num_bytes(pub_key) + 1; /* Key form is "ssh-dss" + p + q + g + pub_key. */ len = 4 + 7 + 4 + p_bytes + 4 + q_bytes + 4 + g_bytes + 4 + k_bytes; key = LIBSSH2_ALLOC(session, len); if(key == NULL) { return NULL; } /* Process key encoding. */ p = key; _libssh2_htonu32(p, 7); /* Key type. */ p += 4; memcpy(p, "ssh-dss", 7); p += 7; p = write_bn(p, p_bn, p_bytes); p = write_bn(p, q, q_bytes); p = write_bn(p, g, g_bytes); p = write_bn(p, pub_key, k_bytes); *key_len = (size_t)(p - key); return key; }
static int gen_publickey_from_dsa_evp(LIBSSH2_SESSION *session, unsigned char **method, size_t *method_len, unsigned char **pubkeydata, size_t *pubkeydata_len, EVP_PKEY *pk) { DSA* dsa = NULL; unsigned char* key; unsigned char* method_buf = NULL; size_t key_len; _libssh2_debug(session, LIBSSH2_TRACE_AUTH, "Computing public key from DSA private key envelop"); dsa = EVP_PKEY_get1_DSA(pk); if (dsa == NULL) { /* Assume memory allocation error... what else could it be ? */ goto __alloc_error; } method_buf = LIBSSH2_ALLOC(session, 7); /* ssh-dss. */ if (method_buf == NULL) { goto __alloc_error; } key = gen_publickey_from_dsa(session, dsa, &key_len); if (key == NULL) { goto __alloc_error; } DSA_free(dsa); memcpy(method_buf, "ssh-dss", 7); *method = method_buf; *method_len = 7; *pubkeydata = key; *pubkeydata_len = key_len; return 0; __alloc_error: if (dsa != NULL) { DSA_free(dsa); } if (method_buf != NULL) { LIBSSH2_FREE(session, method_buf); } _libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for private key data"); return -1; }
/* {{{ proto libssh2_userauth_list * List authentication methods * Will yield successful login if "none" happens to be allowable for this user * Not a common configuration for any SSH server though * username should be NULL, or a null terminated string */ LIBSSH2_API char *libssh2_userauth_list(LIBSSH2_SESSION *session, const char *username, unsigned int username_len) { unsigned char reply_codes[3] = { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, 0 }; unsigned long data_len = username_len + 31; /* packet_type(1) + username_len(4) + service_len(4) + service(14)"ssh-connection" + method_len(4) + method(4)"none" */ unsigned long methods_len; unsigned char *data, *s; s = data = LIBSSH2_ALLOC(session, data_len); if (!data) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for userauth_list", 0); return NULL; } *(s++) = SSH_MSG_USERAUTH_REQUEST; libssh2_htonu32(s, username_len); s += 4; if (username) { memcpy(s, username, username_len); s += username_len; } libssh2_htonu32(s, 14); s += 4; memcpy(s, "ssh-connection", 14); s += 14; libssh2_htonu32(s, 4); s += 4; memcpy(s, "none", 4); s += 4; if (libssh2_packet_write(session, data, data_len)) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send userauth-none request", 0); LIBSSH2_FREE(session, data); return NULL; } LIBSSH2_FREE(session, data); if (libssh2_packet_requirev(session, reply_codes, &data, &data_len)) { return NULL; } if (data[0] == SSH_MSG_USERAUTH_SUCCESS) { /* Wow, who'dve thought... */ LIBSSH2_FREE(session, data); session->state |= LIBSSH2_STATE_AUTHENTICATED; return NULL; } methods_len = libssh2_ntohu32(data + 1); memcpy(data, data + 5, methods_len); data[methods_len] = '\0'; #ifdef LIBSSH2_DEBUG_USERAUTH _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Permitted auth methods: %s", data); #endif return (char *)data; }
/* * libssh2_knownhost_init * * Init a collection of known hosts. Returns the pointer to a collection. * */ LIBSSH2_API LIBSSH2_KNOWNHOSTS * libssh2_knownhost_init(LIBSSH2_SESSION *session) { LIBSSH2_KNOWNHOSTS *knh = LIBSSH2_ALLOC(session, sizeof(struct _LIBSSH2_KNOWNHOSTS)); if(!knh) return NULL; knh->session = session; _libssh2_list_init(&knh->head); return knh; }
int _libssh2_wincng_rsa_sha1_sign(LIBSSH2_SESSION *session, libssh2_rsa_ctx *rsa, const unsigned char *hash, size_t hash_len, unsigned char **signature, size_t *signature_len) { BCRYPT_PKCS1_PADDING_INFO paddingInfo; unsigned char *data, *sig; unsigned long cbData, datalen, siglen; int ret; datalen = (unsigned long)hash_len; data = malloc(datalen); if (!data) { return -1; } paddingInfo.pszAlgId = BCRYPT_SHA1_ALGORITHM; memcpy(data, hash, datalen); ret = BCryptSignHash(rsa->hKey, &paddingInfo, data, datalen, NULL, 0, &cbData, BCRYPT_PAD_PKCS1); if (BCRYPT_SUCCESS(ret)) { siglen = cbData; sig = LIBSSH2_ALLOC(session, siglen); if (sig) { ret = BCryptSignHash(rsa->hKey, &paddingInfo, data, datalen, sig, siglen, &cbData, BCRYPT_PAD_PKCS1); if (BCRYPT_SUCCESS(ret)) { *signature_len = siglen; *signature = sig; } else { LIBSSH2_FREE(session, sig); } } else ret = STATUS_NO_MEMORY; } free(data); return BCRYPT_SUCCESS(ret) ? 0 : -1; }
/* * libssh2_agent_init * * Init an ssh-agent handle. Returns the pointer to the handle. * */ LIBSSH2_API LIBSSH2_AGENT * libssh2_agent_init(LIBSSH2_SESSION *session) { LIBSSH2_AGENT *agent; agent = LIBSSH2_ALLOC(session, sizeof *agent); if (!agent) { _libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate space for agent connection"); return NULL; } memset(agent, 0, sizeof *agent); agent->session = session; _libssh2_list_init(&agent->head); return agent; }
/* * libssh2_knownhost_init * * Init a collection of known hosts. Returns the pointer to a collection. * */ LIBSSH2_API LIBSSH2_KNOWNHOSTS * libssh2_knownhost_init(LIBSSH2_SESSION *session) { LIBSSH2_KNOWNHOSTS *knh = LIBSSH2_ALLOC(session, sizeof(struct _LIBSSH2_KNOWNHOSTS)); if(!knh) { _libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for known-hosts " "collection"); return NULL; } knh->session = session; _libssh2_list_init(&knh->head); return knh; }
/* {{{ libssh2_publickey_remove_ex * Remove an existing publickey so that authentication can no longer be performed using it */ LIBSSH2_API int libssh2_publickey_remove_ex(LIBSSH2_PUBLICKEY *pkey, const unsigned char *name, unsigned long name_len, const unsigned char *blob, unsigned long blob_len) { LIBSSH2_CHANNEL *channel = pkey->channel; LIBSSH2_SESSION *session = channel->session; unsigned char *s, *packet = NULL; unsigned long packet_len = 22 + name_len + blob_len; /* packet_len(4) + remove_len(4) + "remove"(6) + name_len(4) + {name} blob_len(4) + {blob} */ packet = LIBSSH2_ALLOC(session, packet_len); if (!packet) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for publickey \"remove\" packet", 0); return -1; } s = packet; libssh2_htonu32(s, packet_len - 4); s += 4; libssh2_htonu32(s, sizeof("remove") - 1); s += 4; memcpy(s, "remove", sizeof("remove") - 1); s += sizeof("remove") - 1; 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; #ifdef LIBSSH2_DEBUG_PUBLICKEY _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "Sending publickey \"remove\" packet: type=%s blob_len=%ld", name, blob_len); #endif if ((s - packet) != libssh2_channel_write(channel, packet, (s - packet))) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send publickey remove packet", 0); LIBSSH2_FREE(session, packet); return -1; } LIBSSH2_FREE(session, packet); packet = NULL; return libssh2_publickey_response_success(pkey); }
static unsigned char * gen_publickey_from_rsa(LIBSSH2_SESSION *session, RSA *rsa, size_t *key_len) { int e_bytes, n_bytes; unsigned long len; unsigned char *key; unsigned char *p; const BIGNUM * e; const BIGNUM * n; #ifdef HAVE_OPAQUE_STRUCTS RSA_get0_key(rsa, &n, &e, NULL); #else e = rsa->e; n = rsa->n; #endif e_bytes = BN_num_bytes(e) + 1; n_bytes = BN_num_bytes(n) + 1; /* Key form is "ssh-rsa" + e + n. */ len = 4 + 7 + 4 + e_bytes + 4 + n_bytes; key = LIBSSH2_ALLOC(session, len); if(key == NULL) { return NULL; } /* Process key encoding. */ p = key; _libssh2_htonu32(p, 7); /* Key type. */ p += 4; memcpy(p, "ssh-rsa", 7); p += 7; p = write_bn(p, e, e_bytes); p = write_bn(p, n, n_bytes); *key_len = (size_t)(p - key); return key; }
static void libssh2_publickey_status_error(LIBSSH2_PUBLICKEY *pkey, LIBSSH2_SESSION *session, int status, unsigned char *message, int message_len) { char *status_text; int status_text_len; char *m, *s; int m_len; /* GENERAL_FAILURE got remapped between version 1 and 2 */ if (status == 6 && pkey && pkey->version == 1) { status = 7; } if (status < 0 || status > LIBSSH2_PUBLICKEY_STATUS_CODE_MAX) { status_text = "unknown"; status_text_len = sizeof("unknown") - 1; } else { status_text = libssh2_publickey_status_codes[status].name; status_text_len = libssh2_publickey_status_codes[status].name_len; } m_len = (sizeof(LIBSSH2_PUBLICKEY_STATUS_TEXT_START) - 1) + status_text_len + (sizeof(LIBSSH2_PUBLICKEY_STATUS_TEXT_MID) - 1) + message_len + (sizeof(LIBSSH2_PUBLICKEY_STATUS_TEXT_END) - 1); m = LIBSSH2_ALLOC(session, m_len + 1); if (!m) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for status message", 0); return; } s = m; memcpy(s, LIBSSH2_PUBLICKEY_STATUS_TEXT_START, sizeof(LIBSSH2_PUBLICKEY_STATUS_TEXT_START) - 1); s += sizeof(LIBSSH2_PUBLICKEY_STATUS_TEXT_START) - 1; memcpy(s, status_text, status_text_len); s += status_text_len; memcpy(s, LIBSSH2_PUBLICKEY_STATUS_TEXT_MID, sizeof(LIBSSH2_PUBLICKEY_STATUS_TEXT_MID) - 1); s += sizeof(LIBSSH2_PUBLICKEY_STATUS_TEXT_MID) - 1; memcpy(s, message, message_len); s += message_len; memcpy(s, LIBSSH2_PUBLICKEY_STATUS_TEXT_END, sizeof(LIBSSH2_PUBLICKEY_STATUS_TEXT_END) - 1); s += sizeof(LIBSSH2_PUBLICKEY_STATUS_TEXT_END); libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, m, 1); }
static unsigned char * gen_publickey_from_dsa(LIBSSH2_SESSION* session, DSA *dsa, size_t *key_len) { int p_bytes, q_bytes, g_bytes, k_bytes; unsigned long len; unsigned char* key; unsigned char* p; p_bytes = BN_num_bytes(dsa->p) + 1; q_bytes = BN_num_bytes(dsa->q) + 1; g_bytes = BN_num_bytes(dsa->g) + 1; k_bytes = BN_num_bytes(dsa->pub_key) + 1; /* Key form is "ssh-dss" + p + q + g + pub_key. */ len = 4 + 7 + 4 + p_bytes + 4 + q_bytes + 4 + g_bytes + 4 + k_bytes; key = LIBSSH2_ALLOC(session, len); if (key == NULL) { return NULL; } /* Process key encoding. */ p = key; _libssh2_htonu32(p, 7); /* Key type. */ p += 4; memcpy(p, "ssh-dss", 7); p += 7; p = write_bn(p, dsa->p, p_bytes); p = write_bn(p, dsa->q, q_bytes); p = write_bn(p, dsa->g, g_bytes); p = write_bn(p, dsa->pub_key, k_bytes); *key_len = (size_t)(p - key); return key; }
static int _libssh2_init(LIBSSH2_SESSION * session, const LIBSSH2_CRYPT_METHOD * method, unsigned char *iv, int *free_iv, unsigned char *secret, int *free_secret, int encrypt, void **abstract) { struct crypt_ctx *ctx = LIBSSH2_ALLOC(session, sizeof(struct crypt_ctx)); if (!ctx) { return -1; } ctx->encrypt = encrypt; ctx->algo = method->algo; if (_libssh2_cipher_init(&ctx->h, ctx->algo, iv, secret, encrypt)) { LIBSSH2_FREE(session, ctx); return -1; } *abstract = ctx; *free_iv = 1; *free_secret = 1; return 0; }
/* {{{ libssh2_packet_read * Collect a packet into the input brigade * block only controls whether or not to wait for a packet to start, * Once a packet starts, libssh2 will block until it is complete * Returns packet type added to input brigade (0 if nothing added), or -1 on failure */ int libssh2_packet_read(LIBSSH2_SESSION *session, int should_block) { int packet_type = -1; if (session->socket_state == LIBSSH2_SOCKET_DISCONNECTED) { return 0; } #ifndef WIN32 fcntl(session->socket_fd, F_SETFL, O_NONBLOCK); #else { u_long non_block = TRUE; ioctlsocket(session->socket_fd, FIONBIO, &non_block); } #endif #ifdef LIBSSH2_DEBUG_TRANSPORT _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Checking for packet: will%s block", should_block ? "" : " not"); #endif if (session->state & LIBSSH2_STATE_NEWKEYS) { /* Temporary Buffer * The largest blocksize (currently) is 32, the largest MAC (currently) is 20 */ unsigned char block[2 * 32], *payload, *s, tmp[6]; long read_len; unsigned long blocksize = session->remote.crypt->blocksize; unsigned long packet_len, payload_len; int padding_len; int macstate; int free_payload = 1; /* Safely ignored in CUSTOM cipher mode */ EVP_CIPHER_CTX *ctx = (EVP_CIPHER_CTX *)session->remote.crypt_abstract; /* Note: If we add any cipher with a blocksize less than 6 we'll need to get more creative with this * For now, all blocksize sizes are 8+ */ if (should_block) { read_len = libssh2_blocking_read(session, block, blocksize); } else { read_len = recv(session->socket_fd, block, 1, LIBSSH2_SOCKET_RECV_FLAGS(session)); if (read_len <= 0) { return 0; } read_len += libssh2_blocking_read(session, block + read_len, blocksize - read_len); } if (read_len < blocksize) { return (session->socket_state == LIBSSH2_SOCKET_DISCONNECTED) ? 0 : -1; } if (session->remote.crypt->flags & LIBSSH2_CRYPT_METHOD_FLAG_EVP) { EVP_Cipher(ctx, block + blocksize, block, blocksize); memcpy(block, block + blocksize, blocksize); } else { if (session->remote.crypt->crypt(session, block, &session->remote.crypt_abstract)) { libssh2_error(session, LIBSSH2_ERROR_DECRYPT, "Error decrypting packet preamble", 0); return -1; } } packet_len = libssh2_ntohu32(block); padding_len = block[4]; #ifdef LIBSSH2_DEBUG_TRANSPORT _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Processing packet %lu bytes long (with %lu bytes padding)", packet_len, padding_len); #endif memcpy(tmp, block, 5); /* Use this for MAC later */ payload_len = packet_len - 1; /* padding_len(1) */ /* Sanity Check */ if ((payload_len > LIBSSH2_PACKET_MAXPAYLOAD) || ((packet_len + 4) % blocksize)) { /* If something goes horribly wrong during the decryption phase, just bailout and die gracefully */ session->socket_state = LIBSSH2_SOCKET_DISCONNECTED; libssh2_error(session, LIBSSH2_ERROR_PROTO, "Fatal protocol error, invalid payload size", 0); return -1; } s = payload = LIBSSH2_ALLOC(session, payload_len); memcpy(s, block + 5, blocksize - 5); s += blocksize - 5; while ((s - payload) < payload_len) { read_len = libssh2_blocking_read(session, block, blocksize); if (read_len < blocksize) { LIBSSH2_FREE(session, payload); return -1; } if (session->remote.crypt->flags & LIBSSH2_CRYPT_METHOD_FLAG_EVP) { EVP_Cipher(ctx, block + blocksize, block, blocksize); memcpy(s, block + blocksize, blocksize); } else { if (session->remote.crypt->crypt(session, block, &session->remote.crypt_abstract)) { libssh2_error(session, LIBSSH2_ERROR_DECRYPT, "Error decrypting packet preamble", 0); LIBSSH2_FREE(session, payload); return -1; } memcpy(s, block, blocksize); } s += blocksize; } read_len = libssh2_blocking_read(session, block, session->remote.mac->mac_len); if (read_len < session->remote.mac->mac_len) { LIBSSH2_FREE(session, payload); return -1; } /* Calculate MAC hash */ session->remote.mac->hash(session, block + session->remote.mac->mac_len, session->remote.seqno, tmp, 5, payload, payload_len, &session->remote.mac_abstract); macstate = (strncmp(block, block + session->remote.mac->mac_len, session->remote.mac->mac_len) == 0) ? LIBSSH2_MAC_CONFIRMED : LIBSSH2_MAC_INVALID; session->remote.seqno++; /* Ignore padding */ payload_len -= padding_len; if (session->remote.comp && strcmp(session->remote.comp->name, "none")) { /* Decompress */ unsigned char *data; unsigned long data_len; if (session->remote.comp->comp(session, 0, &data, &data_len, LIBSSH2_PACKET_MAXDECOMP, &free_payload, payload, payload_len, &session->remote.comp_abstract)) { LIBSSH2_FREE(session, payload); return -1; } #ifdef LIBSSH2_DEBUG_TRANSPORT _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Payload decompressed: %lu bytes(compressed) to %lu bytes(uncompressed)", data_len, payload_len); #endif if (free_payload) { LIBSSH2_FREE(session, payload); payload = data; payload_len = data_len; } else { if (data == payload) { /* It's not to be freed, because the compression layer reused payload, * So let's do the same! */ payload_len = data_len; } else { /* No comp_method actually lets this happen, but let's prepare for the future */ LIBSSH2_FREE(session, payload); /* We need a freeable struct otherwise the brigade won't know what to do with it */ payload = LIBSSH2_ALLOC(session, data_len); if (!payload) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for copy of uncompressed data", 0); return -1; } memcpy(payload, data, data_len); payload_len = data_len; } } } packet_type = payload[0]; libssh2_packet_add(session, payload, payload_len, macstate); } else { /* No cipher active */ unsigned char *payload; unsigned char buf[24]; unsigned long buf_len, payload_len; unsigned long packet_length; unsigned long padding_length; if (should_block) { buf_len = libssh2_blocking_read(session, buf, 5); } else { buf_len = recv(session->socket_fd, buf, 1, LIBSSH2_SOCKET_RECV_FLAGS(session)); if (buf_len <= 0) { return 0; } buf_len += libssh2_blocking_read(session, buf, 5 - buf_len); } if (buf_len < 5) { /* Something bad happened */ return -1; } packet_length = libssh2_ntohu32(buf); padding_length = buf[4]; #ifdef LIBSSH2_DEBUG_TRANSPORT _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Processing plaintext packet %lu bytes long (with %lu bytes padding)", packet_length, padding_length); #endif payload_len = packet_length - padding_length - 1; /* padding_length(1) */ payload = LIBSSH2_ALLOC(session, payload_len); if (!payload) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for copy of plaintext data", 0); return -1; } if (libssh2_blocking_read(session, payload, payload_len) < payload_len) { return (session->socket_state == LIBSSH2_SOCKET_DISCONNECTED) ? 0 : -1; } while (padding_length) { int l; /* Flush padding */ l = libssh2_blocking_read(session, buf, padding_length); if (l > 0) padding_length -= l; else break; } packet_type = payload[0]; /* MACs don't exist in non-encrypted mode */ libssh2_packet_add(session, payload, payload_len, LIBSSH2_MAC_CONFIRMED); session->remote.seqno++; } return packet_type; }
/* {{{ libssh2_packet_queue_listener * Queue a connection request for a listener */ inline int libssh2_packet_queue_listener(LIBSSH2_SESSION *session, unsigned char *data, unsigned long datalen) { /* Look for a matching listener */ unsigned char *s = data + (sizeof("forwarded-tcpip") - 1) + 5; unsigned long packet_len = 17 + (sizeof("Forward not requested") - 1); unsigned char *p, packet[17 + (sizeof("Forward not requested") - 1)]; /* packet_type(1) + channel(4) + reason(4) + descr(4) + lang(4) */ LIBSSH2_LISTENER *l = session->listeners; char failure_code = 1; /* SSH_OPEN_ADMINISTRATIVELY_PROHIBITED */ unsigned long sender_channel, initial_window_size, packet_size; unsigned char *host, *shost; unsigned long port, sport, host_len, shost_len; sender_channel = libssh2_ntohu32(s); s += 4; initial_window_size = libssh2_ntohu32(s); s += 4; packet_size = libssh2_ntohu32(s); s += 4; host_len = libssh2_ntohu32(s); s += 4; host = s; s += host_len; port = libssh2_ntohu32(s); s += 4; shost_len = libssh2_ntohu32(s); s += 4; shost = s; s += shost_len; sport = libssh2_ntohu32(s); s += 4; #ifdef LIBSSH2_DEBUG_CONNECTION _libssh2_debug(session, LIBSSH2_DBG_CONN, "Remote received connection from %s:%ld to %s:%ld", shost, sport, host, port); #endif while (l) { if ((l->port == port) && (strlen(l->host) == host_len) && (memcmp(l->host, host, host_len) == 0)) { /* This is our listener */ LIBSSH2_CHANNEL *channel, *last_queued = l->queue; if (l->queue_maxsize && (l->queue_maxsize <= l->queue_size)) { /* Queue is full */ failure_code = 4; /* SSH_OPEN_RESOURCE_SHORTAGE */ #ifdef LIBSSH2_DEBUG_CONNECTION _libssh2_debug(session, LIBSSH2_DBG_CONN, "Listener queue full, ignoring"); #endif 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 */ 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 */ break; } memcpy(channel->channel_type, "forwarded-tcpip", channel->channel_type_len + 1); channel->remote.id = 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 = initial_window_size; channel->local.window_size = initial_window_size; channel->local.packet_size = packet_size; #ifdef LIBSSH2_DEBUG_CONNECTION _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); #endif p = 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; if (libssh2_packet_write(session, packet, 17)) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send channel open confirmation", 0); return -1; } /* Link the channel into the end of the queue list */ if (!last_queued) { l->queue = channel; return 0; } while (last_queued->next) last_queued = last_queued->next; last_queued->next = channel; channel->prev = last_queued; l->queue_size++; return 0; } l = l->next; } /* We're not listening to you */ { p = packet; *(p++) = SSH_MSG_CHANNEL_OPEN_FAILURE; libssh2_htonu32(p, sender_channel); p += 4; libssh2_htonu32(p, failure_code); p += 4; libssh2_htonu32(p, sizeof("Forward not requested") - 1); p += 4; memcpy(s, "Forward not requested", sizeof("Forward not requested") - 1); p += sizeof("Forward not requested") - 1; libssh2_htonu32(p, 0); if (libssh2_packet_write(session, packet, packet_len)) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send open failure", 0); return -1; } return 0; } }
/* {{{ libssh2_packet_new * Create a new packet and attach it to the brigade */ static int libssh2_packet_add(LIBSSH2_SESSION *session, unsigned char *data, size_t datalen, int macstate) { LIBSSH2_PACKET *packet; unsigned long data_head = 0; #ifdef LIBSSH2_DEBUG_TRANSPORT _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Packet type %d received, length=%d", (int)data[0], (int)datalen); #endif if (macstate == LIBSSH2_MAC_INVALID) { if (session->macerror) { if (LIBSSH2_MACERROR(session, 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); } 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); } return -1; } } /* 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); message = data + 9; /* packet_type(1) + reason(4) + message_len(4) */ 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 = 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); } #ifdef LIBSSH2_DEBUG_TRANSPORT _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Disconnect(%d): %s(%s)", reason, message, language); #endif LIBSSH2_FREE(session, data); session->socket_state = LIBSSH2_SOCKET_DISCONNECTED; 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, data + 4, datalen - 5); } LIBSSH2_FREE(session, data); 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); message = data + 6; /* packet_type(1) + display(1) + message_len(4) */ 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 = 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); } #ifdef LIBSSH2_DEBUG_TRANSPORT /* _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); #endif LIBSSH2_FREE(session, data); return 0; } break; case SSH_MSG_CHANNEL_EXTENDED_DATA: data_head += 4; /* streamid(4) */ case SSH_MSG_CHANNEL_DATA: data_head += 9; /* packet_type(1) + channelno(4) + datalen(4) */ { LIBSSH2_CHANNEL *channel = libssh2_channel_locate(session, libssh2_ntohu32(data + 1)); if (!channel) { libssh2_error(session, LIBSSH2_ERROR_CHANNEL_UNKNOWN, "Packet received for unknown channel, ignoring", 0); LIBSSH2_FREE(session, data); return 0; } #ifdef LIBSSH2_DEBUG_CONNECTION { 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 - data_head), channel->local.id, channel->remote.id, stream_id); } #endif if ((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); #ifdef LIBSSH2_DEBUG_CONNECTION _libssh2_debug(session, LIBSSH2_DBG_CONN, "Ignoring extended data and refunding %d bytes", (int)(datalen - 13)); #endif /* Adjust the window based on the block we just freed */ libssh2_channel_receive_window_adjust(channel, datalen - 13, 0); return 0; } /* REMEMBER! remote means remote as source of data, NOT remote window! */ if (channel->remote.packet_size < (datalen - 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 = channel->remote.packet_size + data_head; } if (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); return 0; } /* Reset EOF status */ channel->remote.eof = 0; if ((datalen - data_head) > channel->remote.window_size) { libssh2_error(session, LIBSSH2_ERROR_CHANNEL_WINDOW_EXCEEDED, "Remote sent more data than current window allows, truncating", 0); datalen = channel->remote.window_size + data_head; } else { /* Now that we've received it, shrink our window */ channel->remote.window_size -= datalen - data_head; } } break; case SSH_MSG_CHANNEL_EOF: { LIBSSH2_CHANNEL *channel = libssh2_channel_locate(session, libssh2_ntohu32(data + 1)); if (!channel) { /* We may have freed already, just quietly ignore this... */ LIBSSH2_FREE(session, data); return 0; } #ifdef LIBSSH2_DEBUG_CONNECTION _libssh2_debug(session, LIBSSH2_DBG_CONN, "EOF received for channel %lu/%lu", channel->local.id, channel->remote.id); #endif channel->remote.eof = 1; LIBSSH2_FREE(session, data); 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 */ LIBSSH2_CHANNEL *channel = libssh2_channel_locate(session, libssh2_ntohu32(data+1)); if (channel) { channel->exit_status = libssh2_ntohu32(data + 9 + sizeof("exit-status")); #ifdef LIBSSH2_DEBUG_CONNECTION _libssh2_debug(session, LIBSSH2_DBG_CONN, "Exit status %lu received for channel %lu/%lu", channel->exit_status, channel->local.id, channel->remote.id); #endif } LIBSSH2_FREE(session, data); return 0; } } break; case SSH_MSG_CHANNEL_CLOSE: { LIBSSH2_CHANNEL *channel = libssh2_channel_locate(session, libssh2_ntohu32(data + 1)); if (!channel) { /* We may have freed already, just quietly ignore this... */ LIBSSH2_FREE(session, data); return 0; } #ifdef LIBSSH2_DEBUG_CONNECTION _libssh2_debug(session, LIBSSH2_DBG_CONN, "Close received for channel %lu/%lu", channel->local.id, channel->remote.id); #endif channel->remote.close = 1; /* TODO: Add a callback for this */ LIBSSH2_FREE(session, data); 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)) { int retval = libssh2_packet_queue_listener(session, data, datalen); LIBSSH2_FREE(session, data); return retval; } if ((datalen >= (sizeof("x11") + 4)) && ((sizeof("x11")-1) == libssh2_ntohu32(data + 1)) && (memcmp(data + 5, "x11", sizeof("x11") - 1) == 0)) { int retval = libssh2_packet_x11_open(session, data, datalen); LIBSSH2_FREE(session, data); return retval; } break; case SSH_MSG_CHANNEL_WINDOW_ADJUST: { LIBSSH2_CHANNEL *channel = libssh2_channel_locate(session, libssh2_ntohu32(data + 1)); unsigned long bytestoadd = libssh2_ntohu32(data + 5); if (channel && bytestoadd) { channel->local.window_size += bytestoadd; } #ifdef LIBSSH2_DEBUG_CONNECTION _libssh2_debug(session, LIBSSH2_DBG_CONN, "Window adjust received for channel %lu/%lu, adding %lu bytes, new window_size=%lu", channel->local.id, channel->remote.id, bytestoadd, channel->local.window_size); #endif LIBSSH2_FREE(session, data); return 0; } break; } packet = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_PACKET)); memset(packet, 0, sizeof(LIBSSH2_PACKET)); packet->data = data; packet->data_len = datalen; packet->data_head = data_head; packet->mac = macstate; packet->brigade = &session->packets; packet->next = NULL; if (session->packets.tail) { packet->prev = session->packets.tail; packet->prev->next = packet; session->packets.tail = packet; } else { session->packets.head = packet; session->packets.tail = packet; packet->prev = NULL; } if (data[0] == SSH_MSG_KEXINIT && !(session->state & LIBSSH2_STATE_EXCHANGING_KEYS)) { /* Remote wants new keys * Well, it's already in the brigade, * let's just call back into ourselves */ #ifdef LIBSSH2_DEBUG_TRANSPORT _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Renegotiating Keys"); #endif libssh2_kex_exchange(session, 1); /* 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 */ } return 0; }
/* {{{ libssh2_packet_x11_open * Accept a forwarded X11 connection */ inline int libssh2_packet_x11_open(LIBSSH2_SESSION *session, unsigned char *data, unsigned long datalen) { int failure_code = 2; /* SSH_OPEN_CONNECT_FAILED */ unsigned char *s = data + (sizeof("x11") - 1) + 5; unsigned long packet_len = 17 + (sizeof("X11 Forward Unavailable") - 1); unsigned char *p, packet[17 + (sizeof("X11 Forward Unavailable") - 1)]; /* packet_type(1) + channel(4) + reason(4) + descr(4) + lang(4) */ LIBSSH2_CHANNEL *channel; unsigned long sender_channel, initial_window_size, packet_size; unsigned char *shost; unsigned long sport, shost_len; sender_channel = libssh2_ntohu32(s); s += 4; initial_window_size = libssh2_ntohu32(s); s += 4; packet_size = libssh2_ntohu32(s); s += 4; shost_len = libssh2_ntohu32(s); s += 4; shost = s; s += shost_len; sport = libssh2_ntohu32(s); s += 4; #ifdef LIBSSH2_DEBUG_CONNECTION _libssh2_debug(session, LIBSSH2_DBG_CONN, "X11 Connection Received from %s:%ld on channel %lu", shost, sport, sender_channel); #endif if (session->x11) { 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 = 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 = initial_window_size; channel->local.window_size = initial_window_size; channel->local.packet_size = packet_size; #ifdef LIBSSH2_DEBUG_CONNECTION _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); #endif p = 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; if (libssh2_packet_write(session, packet, 17)) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send channel open confirmation", 0); 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, shost, sport); return 0; } else { failure_code = 4; /* SSH_OPEN_RESOURCE_SHORTAGE */ } x11_exit: p = packet; *(p++) = SSH_MSG_CHANNEL_OPEN_FAILURE; libssh2_htonu32(p, sender_channel); p += 4; libssh2_htonu32(p, failure_code); p += 4; libssh2_htonu32(p, sizeof("X11 Forward Unavailable") - 1); p += 4; memcpy(s, "X11 Forward Unavailable", sizeof("X11 Forward Unavailable") - 1); p += sizeof("X11 Forward Unavailable") - 1; libssh2_htonu32(p, 0); if (libssh2_packet_write(session, packet, packet_len)) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send open failure", 0); return -1; } return 0; }
/* {{{ libssh2_packet_write * Send a packet, encrypting it and adding a MAC code if necessary * Returns 0 on success, non-zero on failure */ int libssh2_packet_write(LIBSSH2_SESSION *session, unsigned char *data, unsigned long data_len) { unsigned long packet_length = data_len + 1; unsigned long block_size = (session->state & LIBSSH2_STATE_NEWKEYS) ? session->local.crypt->blocksize : 8; /* At this point packet_length doesn't include the packet_len field itself */ unsigned long padding_length; int free_data = 0; unsigned char buf[246]; /* 6 byte header plus max padding size(240) */ #ifdef LIBSSH2_DEBUG_TRANSPORT { /* Show a hint of what's being sent */ char excerpt[32]; int ex_len = 0, db_ofs = 0; for (; ex_len < 24 && db_ofs < data_len; ex_len += 3, db_ofs++) snprintf(excerpt + ex_len, 4, "%02X ", data[db_ofs]); _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Sending packet type %d, length=%lu, %s", (int)data[0], data_len, excerpt); } #endif if ((session->state & LIBSSH2_STATE_NEWKEYS) && strcmp(session->local.comp->name, "none")) { if (session->local.comp->comp(session, 1, &data, &data_len, LIBSSH2_PACKET_MAXCOMP, &free_data, data, data_len, &session->local.comp_abstract)) { return -1; } #ifdef LIBSSH2_DEBUG_TRANSPORT _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Compressed payload to %lu bytes", data_len); #endif } #ifndef WIN32 fcntl(session->socket_fd, F_SETFL, 0); #else { u_long non_block = FALSE; ioctlsocket(session->socket_fd, FIONBIO, &non_block); } #endif packet_length = data_len + 1; /* padding_length(1) -- MAC doesn't count -- Padding to be added soon */ padding_length = block_size - ((packet_length + 4) % block_size); if (padding_length < 4) { padding_length += block_size; } /* TODO: Maybe add 1 or 2 times block_size to padding_length randomly -- shake things up a bit... */ packet_length += padding_length; libssh2_htonu32(buf, packet_length); buf[4] = padding_length; #ifdef LIBSSH2_DEBUG_TRANSPORT _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Sending packet with total length %lu (%lu bytes padding)", packet_length, padding_length); #endif if (session->state & LIBSSH2_STATE_NEWKEYS) { /* Encryption is in effect */ unsigned char *encbuf, *s; int ret; /* Safely ignored in CUSTOM cipher mode */ EVP_CIPHER_CTX *ctx = (EVP_CIPHER_CTX *)session->local.crypt_abstract; /* include packet_length(4) itself and room for the hash at the end */ encbuf = LIBSSH2_ALLOC(session, 4 + packet_length + session->local.mac->mac_len); if (!encbuf) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate encryption buffer", 0); if (free_data) { LIBSSH2_FREE(session, data); } return -1; } /* Copy packet to encoding buffer */ memcpy(encbuf, buf, 5); memcpy(encbuf + 5, data, data_len); RAND_bytes(encbuf + 5 + data_len, padding_length); if (free_data) { LIBSSH2_FREE(session, data); } /* Calculate MAC hash */ session->local.mac->hash(session, encbuf + 4 + packet_length , session->local.seqno, encbuf, 4 + packet_length, NULL, 0, &session->local.mac_abstract); /* Encrypt data */ for(s = encbuf; (s - encbuf) < (4 + packet_length) ; s += session->local.crypt->blocksize) { if (session->local.crypt->flags & LIBSSH2_CRYPT_METHOD_FLAG_EVP) { EVP_Cipher(ctx, buf, s, session->local.crypt->blocksize); memcpy(s, buf, session->local.crypt->blocksize); } else { session->local.crypt->crypt(session, s, &session->local.crypt_abstract); } } session->local.seqno++; /* Send It */ ret = ((4 + packet_length + session->local.mac->mac_len) == send(session->socket_fd, encbuf, 4 + packet_length + session->local.mac->mac_len, LIBSSH2_SOCKET_SEND_FLAGS(session))) ? 0 : -1; /* Cleanup environment */ LIBSSH2_FREE(session, encbuf); return ret; } else { /* LIBSSH2_ENDPOINT_CRYPT_NONE */ /* Simplified write for non-encrypted mode */ struct iovec data_vector[3]; /* Using vectors means we don't have to alloc a new buffer -- a byte saved is a byte earned * No MAC during unencrypted phase */ data_vector[0].iov_base = buf; data_vector[0].iov_len = 5; data_vector[1].iov_base = (char*)data; data_vector[1].iov_len = data_len; data_vector[2].iov_base = buf + 5; data_vector[2].iov_len = padding_length; session->local.seqno++; /* Ignore this, it can't actually happen :) */ if (free_data) { LIBSSH2_FREE(session, data); } return ((packet_length + 4) == writev(session->socket_fd, data_vector, 3)) ? 0 : 1; } }
/* * libssh2_packet_queue_listener * * Queue a connection request for a listener */ static inline int 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_transport_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_transport_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_packet_add * * Create a new packet and attach it to the brigade. Called from the transport * layer when it as received a packet. */ 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 packet_add() for %lu/%lu/%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) { session->socket_block_directions = LIBSSH2_SESSION_BLOCK_OUTBOUND; 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 = packet_queue_listener(session, data, datalen, &session->packAdd_Qlstn_state); if (rc == PACKET_EAGAIN) { session->socket_block_directions = LIBSSH2_SESSION_BLOCK_OUTBOUND; 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 = packet_x11_open(session, data, datalen, &session->packAdd_x11open_state); if (rc == PACKET_EAGAIN) { session->socket_block_directions = LIBSSH2_SESSION_BLOCK_OUTBOUND; 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; } /* * The KEXINIT message has been added to the queue. The packAdd and * readPack states need to be reset because libssh2_kex_exchange * (eventually) calls upon _libssh2_transport_read to read the rest of * the key exchange conversation. */ session->readPack_state = libssh2_NB_state_idle; session->packet.total_num = 0; session->packAdd_state = libssh2_NB_state_idle; session->fullpacket_state = libssh2_NB_state_idle; /* * Also, don't use packAdd_key_state for key re-exchange, * as it will be wiped out in the middle of the exchange. * How about re-using the startup_key_state? */ memset(&session->startup_key_state, 0, sizeof(key_exchange_state_t)); /* * 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->startup_key_state); if (rc == PACKET_EAGAIN) { return PACKET_EAGAIN; } } session->packAdd_state = libssh2_NB_state_idle; return 0; }