/* {{{ libssh2_kex_method_diffie_hellman_group_exchange_sha1_key_exchange * Diffie-Hellman Group Exchange Key Exchange using SHA1 * Negotiates random(ish) group for secret derivation */ static int libssh2_kex_method_diffie_hellman_group_exchange_sha1_key_exchange(LIBSSH2_SESSION *session) { unsigned char request[13], *s, *data; unsigned long data_len, p_len, g_len, request_len; _libssh2_bn *p = _libssh2_bn_init (); _libssh2_bn *g = _libssh2_bn_init (); int ret; /* Ask for a P and G pair */ #ifdef LIBSSH2_DH_GEX_NEW request[0] = SSH_MSG_KEX_DH_GEX_REQUEST; libssh2_htonu32(request + 1, LIBSSH2_DH_GEX_MINGROUP); libssh2_htonu32(request + 5, LIBSSH2_DH_GEX_OPTGROUP); libssh2_htonu32(request + 9, LIBSSH2_DH_GEX_MAXGROUP); request_len = 13; #ifdef LIBSSH2_DEBUG_KEX _libssh2_debug(session, LIBSSH2_DBG_KEX, "Initiating Diffie-Hellman Group-Exchange (New Method)"); #endif #else request[0] = SSH_MSG_KEX_DH_GEX_REQUEST_OLD; libssh2_htonu32(request + 1, LIBSSH2_DH_GEX_OPTGROUP); request_len = 5; #ifdef LIBSSH2_DEBUG_KEX _libssh2_debug(session, LIBSSH2_DBG_KEX, "Initiating Diffie-Hellman Group-Exchange (Old Method)"); #endif #endif if (libssh2_packet_write(session, request, request_len)) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send Group Exchange Request", 0); ret = -1; goto dh_gex_clean_exit; } if (libssh2_packet_require(session, SSH_MSG_KEX_DH_GEX_GROUP, &data, &data_len)) { libssh2_error(session, LIBSSH2_ERROR_TIMEOUT, "Timeout waiting for GEX_GROUP reply", 0); ret = -1; goto dh_gex_clean_exit; } s = data + 1; p_len = libssh2_ntohu32(s); s += 4; _libssh2_bn_from_bin(p, p_len, s); s += p_len; g_len = libssh2_ntohu32(s); s += 4; _libssh2_bn_from_bin(g, g_len, s); s += g_len; ret = libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(session, g, p, p_len, SSH_MSG_KEX_DH_GEX_INIT, SSH_MSG_KEX_DH_GEX_REPLY, data + 1, data_len - 1); LIBSSH2_FREE(session, data); dh_gex_clean_exit: _libssh2_bn_free(g); _libssh2_bn_free(p); return ret; }
/* {{{ 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_mac_method_hmac_ripemd160_hash * Calculate hash using ripemd160 value */ static int libssh2_mac_method_hmac_ripemd160_hash(LIBSSH2_SESSION * session, unsigned char *buf, unsigned long seqno, const unsigned char *packet, unsigned long packet_len, const unsigned char *addtl, unsigned long addtl_len, void **abstract) { libssh2_hmac_ctx ctx; unsigned char seqno_buf[4]; (void) session; libssh2_htonu32(seqno_buf, seqno); libssh2_hmac_ripemd160_init(&ctx, *abstract, 20); libssh2_hmac_update(ctx, seqno_buf, 4); libssh2_hmac_update(ctx, packet, packet_len); if (addtl && addtl_len) { libssh2_hmac_update(ctx, addtl, addtl_len); } libssh2_hmac_final(ctx, buf); libssh2_hmac_cleanup(&ctx); return 0; }
/* {{{ 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); }
/* {{{ libssh2_kex_method_list * Generate formatted preference list in buf */ static size_t libssh2_kex_method_list(unsigned char *buf, size_t list_strlen, LIBSSH2_COMMON_METHOD **method) { libssh2_htonu32(buf, list_strlen); buf += 4; if (!method || !*method) { return 4; } while (*method && (*method)->name) { int mlen = strlen((*method)->name); memcpy(buf, (*method)->name, mlen); buf += mlen; *(buf++) = ','; method++; } return list_strlen + 4; }
/* {{{ libssh2_mac_method_hmac_sha1_hash * Calculate hash using full sha1 value */ static int libssh2_mac_method_hmac_sha1_hash(LIBSSH2_SESSION *session, unsigned char *buf, unsigned long seqno, const unsigned char *packet, unsigned long packet_len, const unsigned char *addtl, unsigned long addtl_len, void **abstract) { HMAC_CTX ctx; unsigned char seqno_buf[4]; libssh2_htonu32(seqno_buf, seqno); HMAC_Init(&ctx, *abstract, 20, EVP_sha1()); HMAC_Update(&ctx, seqno_buf, 4); HMAC_Update(&ctx, packet, packet_len); if (addtl && addtl_len) { HMAC_Update(&ctx, addtl, addtl_len); } HMAC_Final(&ctx, buf, NULL); HMAC_cleanup(&ctx); return 0; }
/* {{{ libssh2_userauth_publickey_fromfile_ex * Authenticate using a keypair found in the named files */ LIBSSH2_API int libssh2_userauth_publickey_fromfile_ex(LIBSSH2_SESSION *session, const char *username, unsigned int username_len, const char *publickey, const char *privatekey, const char *passphrase) { LIBSSH2_HOSTKEY_METHOD *privkeyobj; void *abstract; unsigned char buf[5]; struct iovec datavec[4]; unsigned char *method, *pubkeydata, *packet, *s, *b, *sig, *data; unsigned char reply_codes[4] = { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, SSH_MSG_USERAUTH_PK_OK, 0 }; unsigned long method_len, pubkeydata_len, packet_len, sig_len, data_len; if (libssh2_file_read_publickey(session, &method, &method_len, &pubkeydata, &pubkeydata_len, publickey)) { return -1; } packet_len = username_len + method_len + pubkeydata_len + 45; /* packet_type(1) + username_len(4) + servicename_len(4) + service_name(14)"ssh-connection" + authmethod_len(4) + authmethod(9)"publickey" + sig_included(1)'\0' + algmethod_len(4) + publickey_len(4) */ /* Preallocate space for an overall length, method name again, * and the signature, which won't be any larger than the size of the publickeydata itself */ s = packet = LIBSSH2_ALLOC(session, packet_len + 4 + (4 + method_len) + (4 + pubkeydata_len)); *(s++) = SSH_MSG_USERAUTH_REQUEST; libssh2_htonu32(s, username_len); s += 4; memcpy(s, username, username_len); s += username_len; libssh2_htonu32(s, 14); s += 4; memcpy(s, "ssh-connection", 14); s += 14; libssh2_htonu32(s, 9); s += 4; memcpy(s, "publickey", 9); s += 9; b = s; *(s++) = 0; /* Not sending signature with *this* packet */ libssh2_htonu32(s, method_len); s += 4; memcpy(s, method, method_len); s += method_len; libssh2_htonu32(s, pubkeydata_len); s += 4; memcpy(s, pubkeydata, pubkeydata_len); s += pubkeydata_len; #ifdef LIBSSH2_DEBUG_USERAUTH _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Attempting publickey authentication"); #endif if (libssh2_packet_write(session, packet, packet_len)) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send userauth-publickey request", 0); LIBSSH2_FREE(session, packet); LIBSSH2_FREE(session, method); LIBSSH2_FREE(session, pubkeydata); return -1; } if (libssh2_packet_requirev(session, reply_codes, &data, &data_len)) { LIBSSH2_FREE(session, packet); LIBSSH2_FREE(session, method); LIBSSH2_FREE(session, pubkeydata); return -1; } if (data[0] == SSH_MSG_USERAUTH_SUCCESS) { #ifdef LIBSSH2_DEBUG_USERAUTH _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Pubkey authentication prematurely successful"); #endif /* God help any SSH server that allows an UNVERIFIED public key to validate the user */ LIBSSH2_FREE(session, data); LIBSSH2_FREE(session, packet); LIBSSH2_FREE(session, method); LIBSSH2_FREE(session, pubkeydata); session->state |= LIBSSH2_STATE_AUTHENTICATED; return 0; } if (data[0] == SSH_MSG_USERAUTH_FAILURE) { /* This public key is not allowed for this user on this server */ LIBSSH2_FREE(session, data); LIBSSH2_FREE(session, packet); LIBSSH2_FREE(session, method); LIBSSH2_FREE(session, pubkeydata); libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNRECOGNIZED, "Username/PublicKey combination invalid", 0); return -1; } /* Semi-Success! */ LIBSSH2_FREE(session, data); LIBSSH2_FREE(session, pubkeydata); if (libssh2_file_read_privatekey(session, &privkeyobj, &abstract, (const char *)method, method_len, privatekey, passphrase)) { LIBSSH2_FREE(session, method); LIBSSH2_FREE(session, packet); return -1; } *b = 0xFF; libssh2_htonu32(buf, session->session_id_len); datavec[0].iov_base = buf; datavec[0].iov_len = 4; datavec[1].iov_base = session->session_id; datavec[1].iov_len = session->session_id_len; datavec[2].iov_base = packet; datavec[2].iov_len = packet_len; if (privkeyobj->signv(session, &sig, &sig_len, 3, datavec, &abstract)) { LIBSSH2_FREE(session, method); LIBSSH2_FREE(session, packet); if (privkeyobj->dtor) { privkeyobj->dtor(session, &abstract); } return -1; } if (privkeyobj->dtor) { privkeyobj->dtor(session, &abstract); } if (sig_len > pubkeydata_len) { /* Should *NEVER* happen, but...well.. better safe than sorry */ packet = LIBSSH2_REALLOC(session, packet, packet_len + 4 + (4 + method_len) + (4 + sig_len)); /* PK sigblob */ if (!packet) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Failed allocating additional space for userauth-publickey packet", 0); LIBSSH2_FREE(session, method); return -1; } } s = packet + packet_len; libssh2_htonu32(s, 4 + method_len + 4 + sig_len); s += 4; libssh2_htonu32(s, method_len); s += 4; memcpy(s, method, method_len); s += method_len; LIBSSH2_FREE(session, method); libssh2_htonu32(s, sig_len); s += 4; memcpy(s, sig, sig_len); s += sig_len; LIBSSH2_FREE(session, sig); #ifdef LIBSSH2_DEBUG_USERAUTH _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Attempting publickey authentication -- phase 2"); #endif if (libssh2_packet_write(session, packet, s - packet)) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send userauth-publickey request", 0); LIBSSH2_FREE(session, packet); return -1; } LIBSSH2_FREE(session, packet); /* PK_OK is no longer valid */ reply_codes[2] = 0; if (libssh2_packet_requirev(session, reply_codes, &data, &data_len)) { return -1; } if (data[0] == SSH_MSG_USERAUTH_SUCCESS) { #ifdef LIBSSH2_DEBUG_USERAUTH _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Publickey authentication successful"); #endif /* We are us and we've proved it. */ LIBSSH2_FREE(session, data); session->state |= LIBSSH2_STATE_AUTHENTICATED; return 0; } /* This public key is not allowed for this user on this server */ LIBSSH2_FREE(session, data); libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED, "Invalid signature for supplied public key, or bad username/public key combination", 0); return -1; }
/* {{{ libssh2_userauth_password * Plain ol' login */ LIBSSH2_API int libssh2_userauth_password_ex(LIBSSH2_SESSION *session, const char *username, unsigned int username_len, const char *password, unsigned int password_len, LIBSSH2_PASSWD_CHANGEREQ_FUNC((*passwd_change_cb))) { unsigned char *data, *s, reply_codes[4] = { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, SSH_MSG_USERAUTH_PASSWD_CHANGEREQ, 0 }; unsigned long data_len = username_len + password_len + 40; /* packet_type(1) + username_len(4) + service_len(4) + service(14)"ssh-connection" + method_len(4) + method(8)"password" + chgpwdbool(1) + password_len(4) */ s = data = LIBSSH2_ALLOC(session, data_len); if (!data) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for userauth-password request", 0); return -1; } *(s++) = SSH_MSG_USERAUTH_REQUEST; libssh2_htonu32(s, username_len); s += 4; memcpy(s, username, username_len); s += username_len; libssh2_htonu32(s, sizeof("ssh-connection") - 1); s += 4; memcpy(s, "ssh-connection", sizeof("ssh-connection") - 1); s += sizeof("ssh-connection") - 1; libssh2_htonu32(s, sizeof("password") - 1); s += 4; memcpy(s, "password", sizeof("password") - 1); s += sizeof("password") - 1; *s = '\0'; s++; libssh2_htonu32(s, password_len); s += 4; memcpy(s, password, password_len); s += password_len; #ifdef LIBSSH2_DEBUG_USERAUTH _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Attempting to login using password authentication"); #endif if (libssh2_packet_write(session, data, data_len)) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send userauth-password request", 0); LIBSSH2_FREE(session, data); return -1; } LIBSSH2_FREE(session, data); password_response: if (libssh2_packet_requirev(session, reply_codes, &data, &data_len)) { return -1; } if (data[0] == SSH_MSG_USERAUTH_SUCCESS) { #ifdef LIBSSH2_DEBUG_USERAUTH _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Password authentication successful"); #endif LIBSSH2_FREE(session, data); session->state |= LIBSSH2_STATE_AUTHENTICATED; return 0; } if (data[0] == SSH_MSG_USERAUTH_PASSWD_CHANGEREQ) { char *newpw = NULL; int newpw_len = 0; #ifdef LIBSSH2_DEBUG_USERAUTH _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Password change required"); #endif LIBSSH2_FREE(session, data); if (passwd_change_cb) { passwd_change_cb(session, &newpw, &newpw_len, &session->abstract); if (!newpw) { libssh2_error(session, LIBSSH2_ERROR_PASSWORD_EXPIRED, "Password expired, and callback failed", 0); return -1; } data_len = username_len + password_len + 44 + newpw_len; /* basic data_len + newpw_len(4) */ s = data = LIBSSH2_ALLOC(session, data_len); if (!data) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for userauth-password-change request", 0); return -1; } *(s++) = SSH_MSG_USERAUTH_REQUEST; libssh2_htonu32(s, username_len); s += 4; memcpy(s, username, username_len); s += username_len; libssh2_htonu32(s, sizeof("ssh-connection") - 1); s += 4; memcpy(s, "ssh-connection", sizeof("ssh-connection") - 1); s += sizeof("ssh-connection") - 1; libssh2_htonu32(s, sizeof("password") - 1); s += 4; memcpy(s, "password", sizeof("password") - 1); s += sizeof("password") - 1; *s = 0xFF; s++; libssh2_htonu32(s, password_len); s += 4; memcpy(s, password, password_len); s += password_len; libssh2_htonu32(s, newpw_len); s += 4; memcpy(s, newpw, newpw_len); s += newpw_len; if (libssh2_packet_write(session, data, data_len)) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send userauth-password-change request", 0); LIBSSH2_FREE(session, data); return -1; } LIBSSH2_FREE(session, data); LIBSSH2_FREE(session, newpw); /* Ugliest use of goto ever. Blame it on the askN => requirev migration. */ goto password_response; } else { libssh2_error(session, LIBSSH2_ERROR_PASSWORD_EXPIRED, "Password Expired, and no callback specified", 0); return -1; } } /* FAILURE */ LIBSSH2_FREE(session, data); return -1; }
/* {{{ libssh2_packet_write * Send a packet, encrypting it and adding a MAC code if necessary * Returns 0 on success, non-zero on failure. * * Returns PACKET_EAGAIN if it would block - and if it does so, you should * call this function again as soon as it is likely that more data can be * sent, and this function should then be called with the same argument set * (same data pointer and same data_len) until zero or failure is returned. */ int libssh2_packet_write(LIBSSH2_SESSION * session, unsigned char *data, unsigned long data_len) { int blocksize = (session->state & LIBSSH2_STATE_NEWKEYS) ? session->local.crypt-> blocksize : 8; int padding_length; int packet_length; int total_length; int free_data = 0; #ifdef RANDOM_PADDING int rand_max; int seed = data[0]; /* FIXME: make this random */ #endif struct transportpacket *p = &session->packet; int encrypted; int i; ssize_t ret; libssh2pack_t rc; unsigned char *orgdata = data; unsigned long orgdata_len = data_len; debugdump(session, "libssh2_packet_write plain", data, data_len); /* FIRST, check if we have a pending write to complete */ rc = send_existing(session, data, data_len, &ret); if (rc || ret) { return rc; } encrypted = (session->state & LIBSSH2_STATE_NEWKEYS) ? 1 : 0; /* check if we should compress */ if (encrypted && 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 PACKET_COMPRESS; /* compression failure */ } } /* RFC4253 says: Note that the length of the concatenation of 'packet_length', 'padding_length', 'payload', and 'random padding' MUST be a multiple of the cipher block size or 8, whichever is larger. */ /* Plain math: (4 + 1 + packet_length + padding_length) % blocksize == 0 */ packet_length = data_len + 1 + 4; /* 1 is for padding_length field 4 for the packet_length field */ /* at this point we have it all except the padding */ /* first figure out our minimum padding amount to make it an even block size */ padding_length = blocksize - (packet_length % blocksize); /* if the padding becomes too small we add another blocksize worth of it (taken from the original libssh2 where it didn't have any real explanation) */ if (padding_length < 4) { padding_length += blocksize; } #ifdef RANDOM_PADDING /* FIXME: we can add padding here, but that also makes the packets bigger etc */ /* now we can add 'blocksize' to the padding_length N number of times (to "help thwart traffic analysis") but it must be less than 255 in total */ rand_max = (255 - padding_length) / blocksize + 1; padding_length += blocksize * (seed % rand_max); #endif packet_length += padding_length; /* append the MAC length to the total_length size */ total_length = packet_length + (encrypted ? session->local.mac->mac_len : 0); /* allocate memory to store the outgoing packet in, in case we can't send the whole one and thus need to keep it after this function returns. */ p->outbuf = LIBSSH2_ALLOC(session, total_length); if (!p->outbuf) { return PACKET_ENOMEM; } /* store packet_length, which is the size of the whole packet except the MAC and the packet_length field itself */ libssh2_htonu32(p->outbuf, packet_length - 4); /* store padding_length */ p->outbuf[4] = padding_length; /* copy the payload data */ memcpy(p->outbuf + 5, data, data_len); /* fill the padding area with random junk */ libssh2_random(p->outbuf + 5 + data_len, padding_length); if (free_data) { LIBSSH2_FREE(session, data); } if (encrypted) { /* Calculate MAC hash. Put the output at index packet_length, since that size includes the whole packet. The MAC is calculated on the entire unencrypted packet, including all fields except the MAC field itself. */ session->local.mac->hash(session, p->outbuf + packet_length, session->local.seqno, p->outbuf, packet_length, NULL, 0, &session->local.mac_abstract); /* Encrypt the whole packet data, one block size at a time. The MAC field is not encrypted. */ for(i = 0; i < packet_length; i += session->local.crypt->blocksize) { unsigned char *ptr = &p->outbuf[i]; if (session->local.crypt-> crypt(session, ptr, &session->local.crypt_abstract)) return PACKET_FAIL; /* encryption failure */ } } session->local.seqno++; ret = send(session->socket_fd, p->outbuf, total_length, LIBSSH2_SOCKET_SEND_FLAGS(session)); if (ret != -1) { debugdump(session, "libssh2_packet_write send()", p->outbuf, ret); } if (ret != total_length) { if ((ret > 0) || ((ret == -1) && (errno == EAGAIN))) { /* the whole packet could not be sent, save the rest */ p->odata = orgdata; p->olen = orgdata_len; p->osent = (ret == -1) ? 0 : ret; p->ototal_num = total_length; return PACKET_EAGAIN; } return PACKET_FAIL; } /* the whole thing got sent away */ p->odata = NULL; p->olen = 0; LIBSSH2_FREE(session, p->outbuf); p->outbuf = NULL; return PACKET_NONE; /* all is good */ }
/* {{{ 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; }
/* {{{ 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_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_userauth_keyboard_interactive * Authenticate using a challenge-response authentication */ LIBSSH2_API int libssh2_userauth_keyboard_interactive_ex(LIBSSH2_SESSION *session, const char *username, unsigned int username_len, LIBSSH2_USERAUTH_KBDINT_RESPONSE_FUNC((*response_callback))) { unsigned char *s, *data; /* packet */ unsigned long packet_len; packet_len = 1 /* byte SSH_MSG_USERAUTH_REQUEST */ + 4 + username_len /* string user name (ISO-10646 UTF-8, as defined in [RFC-3629]) */ + 4 + 14 /* string service name (US-ASCII) */ + 4 + 20 /* string "keyboard-interactive" (US-ASCII) */ + 4 + 0 /* string language tag (as defined in [RFC-3066]) */ + 4 + 0 /* string submethods (ISO-10646 UTF-8) */ ; if (!(data = s = LIBSSH2_ALLOC(session, packet_len))) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for keyboard-interactive authentication", 0); return -1; } *s++ = SSH_MSG_USERAUTH_REQUEST; /* user name */ libssh2_htonu32(s, username_len); s += 4; memcpy(s, username, username_len); s += username_len; /* service name */ libssh2_htonu32(s, sizeof("ssh-connection") - 1); s += 4; memcpy(s, "ssh-connection", sizeof("ssh-connection") - 1); s += sizeof("ssh-connection") - 1; /* "keyboard-interactive" */ libssh2_htonu32(s, sizeof("keyboard-interactive") - 1); s += 4; memcpy(s, "keyboard-interactive", sizeof("keyboard-interactive") - 1); s += sizeof("keyboard-interactive") - 1; /* language tag */ libssh2_htonu32(s, 0); s += 4; /* submethods */ libssh2_htonu32(s, 0); s += 4; #ifdef LIBSSH2_DEBUG_USERAUTH _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Attempting keyboard-interactive authentication"); #endif if (libssh2_packet_write(session, data, packet_len)) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send keyboard-interactive request", 0); LIBSSH2_FREE(session, data); return -1; } LIBSSH2_FREE(session, data); for (;;) { unsigned char reply_codes[4] = { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, SSH_MSG_USERAUTH_INFO_REQUEST, 0 }; unsigned int auth_name_len; char* auth_name = NULL; unsigned auth_instruction_len; char* auth_instruction = NULL; unsigned int language_tag_len; unsigned long data_len; unsigned int num_prompts = 0; unsigned int i; int auth_failure = 1; LIBSSH2_USERAUTH_KBDINT_PROMPT* prompts = NULL; LIBSSH2_USERAUTH_KBDINT_RESPONSE* responses = NULL; if (libssh2_packet_requirev(session, reply_codes, &data, &data_len)) { return -1; } if (data[0] == SSH_MSG_USERAUTH_SUCCESS) { #ifdef LIBSSH2_DEBUG_USERAUTH _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Keyboard-interactive authentication successful"); #endif LIBSSH2_FREE(session, data); session->state |= LIBSSH2_STATE_AUTHENTICATED; return 0; } if (data[0] == SSH_MSG_USERAUTH_FAILURE) { LIBSSH2_FREE(session, data); return -1; } /* server requested PAM-like conversation */ s = data + 1; /* string name (ISO-10646 UTF-8) */ auth_name_len = libssh2_ntohu32(s); s += 4; if (!(auth_name = LIBSSH2_ALLOC(session, auth_name_len))) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for keyboard-interactive 'name' request field", 0); goto cleanup; } memcpy(auth_name, s, auth_name_len); s += auth_name_len; /* string instruction (ISO-10646 UTF-8) */ auth_instruction_len = libssh2_ntohu32(s); s += 4; if (!(auth_instruction = LIBSSH2_ALLOC(session, auth_instruction_len))) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for keyboard-interactive 'instruction' request field", 0); goto cleanup; } memcpy(auth_instruction, s, auth_instruction_len); s += auth_instruction_len; /* string language tag (as defined in [RFC-3066]) */ language_tag_len = libssh2_ntohu32(s); s += 4; /* ignoring this field as deprecated */ s += language_tag_len; /* int num-prompts */ num_prompts = libssh2_ntohu32(s); s += 4; prompts = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_USERAUTH_KBDINT_PROMPT) * num_prompts); if (!prompts) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for keyboard-interactive prompts array", 0); goto cleanup; } memset(prompts, 0, sizeof(LIBSSH2_USERAUTH_KBDINT_PROMPT) * num_prompts); responses = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_USERAUTH_KBDINT_RESPONSE) * num_prompts); if (!responses) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for keyboard-interactive responses array", 0); goto cleanup; } memset(responses, 0, sizeof(LIBSSH2_USERAUTH_KBDINT_RESPONSE) * num_prompts); for(i = 0; i != num_prompts; ++i) { /* string prompt[1] (ISO-10646 UTF-8) */ prompts[i].length = libssh2_ntohu32(s); s += 4; if (!(prompts[i].text = LIBSSH2_ALLOC(session, prompts[i].length))) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for keyboard-interactive prompt message", 0); goto cleanup; } memcpy(prompts[i].text, s, prompts[i].length); s += prompts[i].length; /* boolean echo[1] */ prompts[i].echo = *s++; } response_callback(auth_name, auth_name_len, auth_instruction, auth_instruction_len, num_prompts, prompts, responses, &session->abstract); #ifdef LIBSSH2_DEBUG_USERAUTH _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Keyboard-interactive response callback function invoked"); #endif packet_len = 1 /* byte SSH_MSG_USERAUTH_INFO_RESPONSE */ + 4 /* int num-responses */ ; for (i = 0; i != num_prompts; ++i) { packet_len += 4 + responses[i].length; /* string response[1] (ISO-10646 UTF-8) */ } if (!(data = s = LIBSSH2_ALLOC(session, packet_len))) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for keyboard-interactive response packet", 0); goto cleanup; } *s = SSH_MSG_USERAUTH_INFO_RESPONSE; s++; libssh2_htonu32(s, num_prompts); s += 4; for (i = 0; i != num_prompts; ++i) { libssh2_htonu32(s, responses[i].length); s += 4; memcpy(s, responses[i].text, responses[i].length); s += responses[i].length; } if (libssh2_packet_write(session, data, packet_len)) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send userauth-keyboard-interactive request", 0); goto cleanup; } auth_failure = 0; cleanup: /* It's safe to clean all the data here, because unallocated pointers * are filled by zeroes */ LIBSSH2_FREE(session, data); if (prompts) { for (i = 0; i != num_prompts; ++i) { LIBSSH2_FREE(session, prompts[i].text); } } if (responses) { for (i = 0; i != num_prompts; ++i) { LIBSSH2_FREE(session, responses[i].text); } } LIBSSH2_FREE(session, prompts); LIBSSH2_FREE(session, responses); if (auth_failure) { return -1; } } }
/* {{{ 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_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_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_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_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_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; }
/* {{{ 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; } }
/* {{{ 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; }