/* {{{ libssh2_banner_send * Send the default banner, or the one set via libssh2_setopt_string * * 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. */ static int libssh2_banner_send(LIBSSH2_SESSION * session) { char *banner = (char *) LIBSSH2_SSH_DEFAULT_BANNER_WITH_CRLF; int banner_len = sizeof(LIBSSH2_SSH_DEFAULT_BANNER_WITH_CRLF) - 1; ssize_t ret; #ifdef LIBSSH2DEBUG char banner_dup[256]; #endif if (session->banner_TxRx_state == libssh2_NB_state_idle) { if (session->local.banner) { /* setopt_string will have given us our \r\n characters */ banner_len = strlen((char *) session->local.banner); banner = (char *) session->local.banner; } #ifdef LIBSSH2DEBUG /* Hack and slash to avoid sending CRLF in debug output */ if (banner_len < 256) { memcpy(banner_dup, banner, banner_len - 2); banner_dup[banner_len - 2] = '\0'; } else { memcpy(banner_dup, banner, 255); banner[255] = '\0'; } _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Sending Banner: %s", banner_dup); #endif session->banner_TxRx_state = libssh2_NB_state_created; } ret = send(session->socket_fd, banner + session->banner_TxRx_total_send, banner_len - session->banner_TxRx_total_send, LIBSSH2_SOCKET_SEND_FLAGS(session)); if (ret != (banner_len - session->banner_TxRx_total_send)) { if ((ret > 0) || ((ret == -1) && (errno == EAGAIN))) { /* the whole packet could not be sent, save the what was */ session->socket_block_directions = LIBSSH2_SESSION_BLOCK_OUTBOUND; session->banner_TxRx_total_send += ret; return PACKET_EAGAIN; } session->banner_TxRx_state = libssh2_NB_state_idle; session->banner_TxRx_total_send = 0; return PACKET_FAIL; } /* Set the state back to idle */ session->banner_TxRx_state = libssh2_NB_state_idle; session->banner_TxRx_total_send = 0; return 0; }
static libssh2pack_t send_existing(LIBSSH2_SESSION * session, unsigned char *data, unsigned long data_len, ssize_t * ret) { ssize_t rc; ssize_t length; struct transportpacket *p = &session->packet; if (!p->outbuf) { *ret = 0; return PACKET_NONE; } /* send as much as possible of the existing packet */ if ((data != p->odata) || (data_len != p->olen)) { /* When we are about to complete the sending of a packet, it is vital that the caller doesn't try to send a new/different packet since we don't add this one up until the previous one has been sent. To make the caller really notice his/hers flaw, we return error for this case */ return PACKET_BADUSE; } *ret = 1; /* set to make our parent return */ /* number of bytes left to send */ length = p->ototal_num - p->osent; rc = send(session->socket_fd, &p->outbuf[p->osent], length, LIBSSH2_SOCKET_SEND_FLAGS(session)); if (rc == length) { /* the remainder of the package was sent */ LIBSSH2_FREE(session, p->outbuf); p->outbuf = NULL; p->ototal_num = 0; } else if (rc < 0) { /* nothing was sent */ if (errno != EAGAIN) { /* send failure! */ return PACKET_FAIL; } return PACKET_EAGAIN; } debugdump(session, "libssh2_packet_write send()", &p->outbuf[p->osent], length); p->osent += length; /* we sent away this much data */ return PACKET_NONE; }
/* {{{ 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_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 */ }