/* * slush SSL remote shell * Copyright (c) 1999 Damien Miller * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #include /* SSL headers */ #include #include #include #include #include "control.h" const unsigned char magic[] = { 0x19, 0x98, 0x12, 0x17 }; const unsigned int magic_len = 4; const unsigned char version = 1; typedef struct { unsigned char msg_magic[sizeof(magic)]; unsigned short msg_length; unsigned char msg_version; unsigned char msg_type; unsigned char *msg_body; } message_t; #define MESSAGE_LENGTH 4 #define CONTROL_MSG_MAGIC 0 #define CONTROL_MSG_USERNAME 1 #define CONTROL_MSG_PASSWORD_REQUEST 2 #define CONTROL_MSG_PASSWORD 3 #define CONTROL_MSG_ENVIRONMENT 4 #define CONTROL_MSG_WINSIZE 5 #define CONTROL_MSG_TERM_NEG 6 #define CONTROL_MSG_NOOP 7 static void *check_realloc(void *p, size_t s); static void build_full_message(message_t *m, int message_type, const char *body, int len); static void build_empty_message(message_t *m, int message_type); static char *build_env(env_entry_t *e, int num_env_entries, int *length); static char *build_winsz(struct winsize *w, int *length); static void sslerror(char *fun); static int process_message(SSL *ssl, int offset, char **buffer, int *buffer_len, control_t *ctrl); static int check_partial_message(SSL *ssl, int offset, char **buffer, int *buffer_len, control_t *ctrl); /* Checks a block of data revieved from the peer for control messages */ /* if control messages are found, they are interpreted and the ctrl */ /* structure updated. The buffer and buffer_len are also modified */ /* to remove the control message itself */ /* Returns 0 on no control message */ /* or one or more CONTROL_something constants OR'd when one is recieved */ int check_for_control(SSL *ssl, char **buffer, int *buffer_len, control_t *ctrl) { int c; int d; int retval = 0; /* Check for magic in buffer */ for(c = 0; c < *buffer_len - magic_len; c++) { if (memcmp(buffer + c, magic, magic_len) == 0) retval |= process_message(ssl, c, buffer, buffer_len, ctrl); } /* Check for partial magic at end of buffer */ d = *buffer_len - magic_len; for(c = 0; c < magic_len; c++) { if (memcmp(buffer + d + c, magic, magic_len - c) == 0) retval |= check_partial_message(ssl, c, buffer, buffer_len, ctrl); } return(retval); } static int process_message(SSL *ssl, int offset, char **buffer, int *buffer_len, control_t *ctrl) { int bytes_to_read; int bytes_read; bytes_to_read = buffer_len - (offset + magic_len + MESSAGE_LENGTH); if (bytes_to_read > 0) { *buffer = check_realloc(*buffer, *buffer_len + bytes_to_read); *buffer_len += bytes_to_read; bytes_read = SSL_read(ssl, *buffer, bytes_to_read); } } static int check_partial_message(SSL *ssl, int offset, char **buffer, int *buffer_len, control_t *ctrl) { } /* Sends a control message containing one or more control types */ /* Control types are OR'd together */ void send_control(SSL *ssl, int control_types, control_t *ctrl) { int messages_len = 0; char *messages = NULL; char *env_str = NULL; char *winsz = NULL; int offset; int c; if (control_types & CONTROL_SEND_USERNAME) { /* Send username from control structure */ c = strlen(ctrl->username); offset = messages_len; messages_len += magic_len + MESSAGE_LENGTH + c; messages = check_realloc(messages, messages_len); build_full_message((message_t*)(messages + offset), CONTROL_MSG_USERNAME, ctrl->username, c); } if (control_types & CONTROL_SEND_PASSWORD_REQUEST) { /* Send password request */ offset = messages_len; messages_len += magic_len + MESSAGE_LENGTH; messages = check_realloc(messages, messages_len); build_empty_message((message_t*)(messages + offset), CONTROL_MSG_PASSWORD_REQUEST); } if (control_types & CONTROL_SEND_PASSWORD) { /* Send password from control structure */ c = strlen(ctrl->password); offset = messages_len; messages_len += magic_len + MESSAGE_LENGTH + c; messages = check_realloc(messages, messages_len); build_full_message((message_t*)(messages + offset), CONTROL_MSG_USERNAME, ctrl->password, c); } if (control_types & CONTROL_SEND_ENVIRONMENT) { /* Send environment from control structure */ env_str = build_env(ctrl->env, ctrl->num_env_entries, &c); offset = messages_len; messages_len += magic_len + MESSAGE_LENGTH + c; messages = check_realloc(messages, messages_len); build_full_message((message_t*)(messages + offset), CONTROL_MSG_USERNAME, env_str, c); } if (control_types & CONTROL_SEND_WINSIZE) { /* Send environment from control structure */ winsz = build_winsz(ctrl->w, &c); offset = messages_len; messages_len += magic_len + MESSAGE_LENGTH + c; messages = check_realloc(messages, messages_len); build_full_message((message_t*)(messages + offset), CONTROL_MSG_USERNAME, env_str, c); } if (control_types & CONTROL_SEND_TERM_NEG) { /* Send password from control structure */ c = strlen(ctrl->password); offset = messages_len; messages_len += magic_len + MESSAGE_LENGTH; messages = check_realloc(messages, messages_len); build_empty_message((message_t*)(messages + offset), CONTROL_MSG_TERM_NEG); } if (control_types & CONTROL_SEND_NOOP) { /* Send password from control structure */ c = strlen(ctrl->password); offset = messages_len; messages_len += magic_len + MESSAGE_LENGTH; messages = check_realloc(messages, messages_len); build_empty_message((message_t*)(messages + offset), CONTROL_MSG_NOOP); } if (SSL_write(ssl, messages, messages_len) != messages_len) sslerror("SSL_write"); } /* Checks through buffer escaping any control characters therein */ /* Returns new buffer containing escaped representation of original */ /* Buffer and modifies buffer_len to new length of buffer */ char *escape_buffer(char *buffer, int *buffer_len) { static char *escaped_buffer = NULL; static int escaped_buffer_len = 0; int i; int c; int d; /* Worst case expansion of data is x 2 */ if (escaped_buffer_len < (*buffer_len * 2)) { escaped_buffer_len = (*buffer_len * 2); escaped_buffer = check_realloc(escaped_buffer, escaped_buffer_len); } i = *buffer_len - (magic_len - 1); d = 0; c = 0; while (c < i) { if (memcmp(buffer + c, magic, magic_len) != 0) { escaped_buffer[d] = buffer[c]; c++; d++; } else { memcpy(escaped_buffer + d, magic, magic_len); c += magic_len; d += magic_len; escaped_buffer[d] = '\0'; escaped_buffer[d + 1] = '\0'; escaped_buffer[d + 2] = version; escaped_buffer[d + 3] = CONTROL_MSG_MAGIC; d += 4; } } /* Copy last few bytes (if there are any) */ if ((magic_len - 1) > 0) { memcpy(escaped_buffer + d, buffer + c, magic_len - 1); d += magic_len - 1; /* Check the last few bytes for a partial magic number */ /* If one is found, then append a noop message to avoid */ /* a possible magic when the packets are reassembled */ for(i = 0; i < magic_len - 1; i++) { if (memcmp(buffer + c + i, magic, (magic_len - 1 - i)) == 0) { memcpy(escaped_buffer + d, magic, magic_len); d += magic_len; escaped_buffer[d] = '\0'; escaped_buffer[d + 1] = '\0'; escaped_buffer[d + 2] = version; escaped_buffer[d + 3] = CONTROL_MSG_NOOP; d += 4; break; } } } *buffer_len = d; return(escaped_buffer); } static void *check_realloc(void *p, size_t s) { p = realloc(p, s); if (p == NULL) { syslog(LOG_ERR, "Out of memory."); exit(2); } return(p); } static void build_empty_message(message_t *m, int message_type) { memcpy(m->msg_magic, magic, magic_len); m->msg_length = 0; m->msg_version = version; m->msg_type = message_type; } static void build_full_message(message_t *m, int message_type, const char *body, int len) { memcpy(m->msg_magic, magic, magic_len); m->msg_length = htons(len); m->msg_version = version; m->msg_type = message_type; memcpy(m->msg_body, body, len); } static char *build_env(env_entry_t *e, int num_env_entries, int *length) { int c; int retval; static char buffer[2048]; if (num_env_entries == 0) { *length = 1; return("\n"); } *length = 0; for (c = 0; c < num_env_entries; c++) { retval = snprintf(buffer + *length, sizeof(buffer) - *length, "%s=%s\n", e[c].name, e[c].value); if (retval == -1) { syslog(LOG_ERR, "Environment too big."); exit(3); } *length += strlen(e[c].name) + strlen(e[c].value) + 1; } return(buffer); } static char *build_winsz(struct winsize *w, int *length) { /* Use local copy of winsize structure in case it changes */ static struct { unsigned short int rows; unsigned short int cols; unsigned short int xpixels; unsigned short int ypixels; } encoded_winsz; if (w == NULL) { memset(&encoded_winsz, '\0', sizeof(encoded_winsz)); } else { /* Mangle byte order */ encoded_winsz.rows = htons(w->ws_row); encoded_winsz.cols = htons(w->ws_col); encoded_winsz.xpixels = htons(w->ws_xpixel); encoded_winsz.ypixels = htons(w->ws_ypixel); } *length = sizeof(encoded_winsz); return((char*)&encoded_winsz); } /* Print ssl errors and exit */ static void sslerror(char *fun) { char string[120]; ERR_error_string(ERR_get_error(), string); syslog(LOG_ERR, "%s: %s", fun, string); exit(2); }