# include <kernel/kernel.h> # include <kernel/user.h> inherit conn LIB_CONN; inherit user LIB_USER; # define DEBUG(mesg) DRIVER->message("SSH: " + (mesg) + "\n") # define SSH_MSG_DISCONNECT 1 # define SSH_MSG_IGNORE 2 # define SSH_MSG_UNIMPLEMENTED 3 # define SSH_MSG_DEBUG 4 # define SSH_MSG_SERVICE_REQUEST 5 # define SSH_MSG_SERVICE_ACCEPT 6 # define SSH_MSG_KEXINIT 20 # define SSH_MSG_NEWKEYS 21 # define SSH_MSG_KEXDH_INIT 30 # define SSH_MSG_KEXDH_REPLY 31 # define SSH_MSG_USERAUTH_REQUEST 50 # define SSH_MSG_USERAUTH_FAILURE 51 # define SSH_MSG_USERAUTH_SUCCESS 52 # define SSH_MSG_USERAUTH_BANNER 53 # define SSH_MSG_GLOBAL_REQUEST 80 # define SSH_MSG_REQUEST_SUCCESS 81 # define SSH_MSG_REQUEST_FAILURE 82 # define SSH_MSG_CHANNEL_OPEN 90 # define SSH_MSG_CHANNEL_OPEN_CONFIRMATION 91 # define SSH_MSG_CHANNEL_OPEN_FAILURE 92 # define SSH_MSG_CHANNEL_WINDOW_ADJUST 93 # define SSH_MSG_CHANNEL_DATA 94 # define SSH_MSG_CHANNEL_EXTENDED_DATA 95 # define SSH_MSG_CHANNEL_EOF 96 # define SSH_MSG_CHANNEL_CLOSE 97 # define SSH_MSG_CHANNEL_REQUEST 98 # define SSH_MSG_CHANNEL_SUCCESS 99 # define SSH_MSG_CHANNEL_FAILURE 100 # define SSH_DISCONNECT_PROTOCOL_ERROR 2 # define SSH_DISCONNECT_MAC_ERROR 5 # define SSH_DISCONNECT_SERVICE_NOT_AVAILABLE 7 # define SSH_OPEN_ADMINISTRATIVELY_PROHIBITED 1 # define SSH_OPEN_CONNECT_FAILED 2 # define SSH_OPEN_UNKNOWN_CHANNEL_TYPE 3 # define SSH_OPEN_RESOURCE_SHORTAGE 4 private int receive_packet(string str); private int userauth(string str); private int client(string str); /* ========================================================================= * * Section 1: packet layer * * ========================================================================= */ private string buffer; /* received so far */ private string header; /* first 8 characters of packet */ private int length; /* length of packet to receive */ private int recv_seqno, send_seqno; /* send and receive sequence numbers */ private string dkey1, dkey2, dkey3; /* decryption keys */ private string ekey1, ekey2, ekey3; /* encryption keys */ private string dstate, estate; /* en/decryption state */ private string client_mac; /* client MAC key */ private string server_mac; /* server MAC key */ private string session_id; /* ID for this entire session */ /* * NAME: random_string() * DESCRIPTION: create a string of pseudo-random bytes */ private string random_string(int length) { string str; int n, rand; str = "................................"; while (strlen(str) < length) { str += str; } str = str[.. length - 1]; for (n = length & ~1; n != 0; ) { /* create two random bytes at a time */ rand = random(65536); str[--n] = rand >> 8; str[--n] = rand; } if (length & 1) { str[length - 1] = random(256); } return str; } /* * NAME: make_int() * DESCRIPTION: build a SSH int */ private string make_int(int i) { string str; str = "...."; str[0] = i >> 24; str[1] = i >> 16; str[2] = i >> 8; str[3] = i; return str; } /* * NAME: make_string() * DESCRIPTION: build a SSH string */ private string make_string(string str) { string header; int length; length = strlen(str); header = "\0\0.."; header[2] = length >> 8; header[3] = length; return header + str; } /* * NAME: make_mesg() * DESCRIPTION: create a message code */ private string make_mesg(int code) { string str; str = "."; str[0] = code; return str; } /* * NAME: make_packet() * DESCRIPTION: build a packet (without MAC) */ private string make_packet(string str) { int length, padding; /* minimum padding is 4 bytes, round up to multiple of 8 bytes */ length = strlen(str); padding = 12 - (length + 1) % 8; length += padding + 1; str = "\0\0..." + str + random_string(padding); str[2] = length >> 8; str[3] = length; str[4] = padding; return str; } /* * NAME: get_int() * DESCRIPTION: get an int from a buffer */ private int get_int(string b, int i) { return (b[i] << 24) + (b[i + 1] << 16) + (b[i + 2] << 8) + b[i + 3]; } /* * NAME: get_string() * DESCRIPTION: get a string from a buffer */ private string get_string(string b, int i) { return b[i + 4 .. i + (b[i] << 24) + (b[i + 1] << 16) + (b[i + 2] << 8) + b[i + 3] + 3]; } /* * NAME: encrypt_packet() * DESCRIPTION: encrypt a packet */ private string encrypt_packet(string str) { int i, n, length; string *encrypted; length = strlen(str); encrypted = allocate(length / 8); for (i = n = 0; i < length; i += 8, n++) { estate = encrypt("DES", decrypt("DES", encrypt("DES", asn_xor(str[i .. i + 7], estate), ekey1), ekey2), ekey3); encrypted[n] = estate; } return implode(encrypted, ""); } /* * NAME: decrypt_string() * DESCRIPTION: decrypt a string */ private string decrypt_string(string str) { int i, n, length; string chunk, *decrypted; length = strlen(str); decrypted = allocate(length / 8); for (i = n = 0; i < length; i += 8, n++) { chunk = str[i .. i + 7]; decrypted[n] = asn_xor(decrypt("DES", encrypt("DES", decrypt("DES", chunk, dkey3), dkey2), dkey1), dstate); dstate = chunk; } return implode(decrypted, ""); } /* * NAME: hmac() * DESCRIPTION: compute HMAC */ private string hmac(string key, string str) { string ipad, opad; ipad = "\x36\x36\x36\x36\x36\x36\x36\x36\x36\x36\x36\x36\x36\x36\x36\x36" + "\x36\x36\x36\x36\x36\x36\x36\x36\x36\x36\x36\x36\x36\x36\x36\x36" + "\x36\x36\x36\x36\x36\x36\x36\x36\x36\x36\x36\x36\x36\x36\x36\x36" + "\x36\x36\x36\x36\x36\x36\x36\x36\x36\x36\x36\x36\x36\x36\x36\x36"; opad = "\x5c\x5c\x5c\x5c\x5c\x5c\x5c\x5c\x5c\x5c\x5c\x5c\x5c\x5c\x5c\x5c" + "\x5c\x5c\x5c\x5c\x5c\x5c\x5c\x5c\x5c\x5c\x5c\x5c\x5c\x5c\x5c\x5c" + "\x5c\x5c\x5c\x5c\x5c\x5c\x5c\x5c\x5c\x5c\x5c\x5c\x5c\x5c\x5c\x5c" + "\x5c\x5c\x5c\x5c\x5c\x5c\x5c\x5c\x5c\x5c\x5c\x5c\x5c\x5c\x5c\x5c"; return hash_sha1(asn_xor(key, opad), hash_sha1(asn_xor(key, ipad), str)); } /* * NAME: send_packet() * DESCRIPTION: send a packet to the other side */ private int send_packet(string str) { str = make_packet(str); if (server_mac) { str = encrypt_packet(str) + hmac(server_mac, make_int(send_seqno) + str); } send_seqno++; return user::message(str); } /* * NAME: process_message() * DESCRIPTION: process a message */ static int process_message(string str) { if (client_mac) { string mac; /* decrypt & verify MAC */ mac = str[length ..]; str = header + decrypt_string(str[.. length - 1]); if (mac != hmac(client_mac, make_int(recv_seqno) + str)) { DEBUG("bad MAC"); send_packet(make_mesg(SSH_MSG_DISCONNECT) + make_int(SSH_DISCONNECT_MAC_ERROR) + make_string("bad MAC") + make_string("en")); return MODE_DISCONNECT; } } else { /* unencrypted */ str = header + str; } recv_seqno++; str = str[5 .. length + 7 - str[4]]; length = -1; return receive_packet(str); } /* * NAME: receive_message() * DESCRIPTION: receive a message */ int receive_message(string str) { int mode; if (previous_program() == LIB_CONN) { buffer += str; while (query_conn()) { if (length < 0) { /* * new packet */ if (strlen(buffer) < 8) { break; } header = buffer[.. 7]; buffer = buffer[8 ..]; if (client_mac) { header = decrypt_string(header); } length = get_int(header, 0); if (length <= 0 || length > 35000 - 4 || (length & 7) != 4) { DEBUG("bad packet length " + length); send_packet(make_mesg(SSH_MSG_DISCONNECT) + make_int(SSH_DISCONNECT_PROTOCOL_ERROR) + make_string("bad packet length") + make_string("en")); return MODE_DISCONNECT; } length -= 4; if (client_mac) { length += 20; } } if (strlen(buffer) < length) { break; } /* * full packet received */ str = buffer[.. length - 1]; buffer = buffer[length ..]; if (client_mac) { length -= 20; } mode = call_limited("process_message", str); if (mode == MODE_DISCONNECT) { return MODE_DISCONNECT; } if (mode >= MODE_UNBLOCK) { query_conn()->set_mode(mode); } } } return MODE_RAW; } /* * NAME: create_packet() * DESCRIPTION: initialize packet layer functions */ private void create_packet() { buffer = ""; length = -1; } /* ========================================================================= * * Section 2: transport layer * * ========================================================================= */ # define SSHD "/usr/System/sys/sshd" # define TRANSPORT_KEXINIT 0 # define TRANSPORT_SKIP 1 # define TRANSPORT_KEXDH 2 # define TRANSPORT_NEWKEYS 3 # define TRANSPORT_TRANSPORT 4 private int transport_state; /* transport state */ private string client_version; /* client protocol version string */ private string client_kexinit; /* client KEXINIT string */ private string server_kexinit; /* server KEXINIT string */ private string p, q; /* prime and group order */ private string y; /* intermediate result */ private string f, e; /* shared secrets */ private string K, H; /* crypto stuff */ /* * NAME: asn1_scan_int() * DESCRIPTION: look for the next int in an ASN.1/DER encoded string */ private int asn1_scan_int(string str, int offset) { int tag, length, size; for (;;) { tag = str[offset++] & 0x1f; if (tag == 0x1f) { /* ignore multi-octet identifier */ while (str[offset++] & 0x80) ; } length = str[offset++]; if (length & 0x80) { /* multi-octet length */ size = length & 0x7f; length = 0; while (size != 0) { length = (length << 8) + str[offset++]; --size; } } switch (tag) { case 2: /* int */ return (length << 16) + offset; case 16: /* sequence */ break; default: /* anything else */ offset += length; break; } } } /* * NAME: better_random_string() * DESCRIPTION: create a slightly more random string */ private string better_random_string(int length) { string str; str = ""; while (length >= 20) { str += hash_sha1(random_string(20)); length -= 20; } if (length >= 0) { str += hash_sha1(random_string(length))[.. length - 1]; } return str; } /* * NAME: ssh_dss_sign() * DESCRIPTION: sign m with the host key */ private string ssh_dss_sign(string m, string host_key) { int offset, length; string p, q, g, x; string k, r, s; /* retrieve params from key */ offset = asn1_scan_int(host_key, 0); length = offset >> 16; offset &= 0xffff; offset = asn1_scan_int(host_key, offset + length); length = offset >> 16; offset &= 0xffff; p = host_key[offset .. offset + length - 1]; offset = asn1_scan_int(host_key, offset + length); length = offset >> 16; offset &= 0xffff; q = host_key[offset .. offset + length - 1]; offset = asn1_scan_int(host_key, offset + length); length = offset >> 16; offset &= 0xffff; g = host_key[offset .. offset + length - 1]; offset = asn1_scan_int(host_key, offset + length); length = offset >> 16; offset &= 0xffff; offset = asn1_scan_int(host_key, offset + length); length = offset >> 16; offset &= 0xffff; x = host_key[offset .. offset + length - 1]; /* k = random 0 < k < q */ do { k = asn_mod("\1" + better_random_string(strlen(q)), q); } while (strlen(k) < strlen(q) - 1); /* r = (g ^ k mod p) mod q */ r = asn_mod(asn_pow(g, k, p), q); /* s = (k ^ -1 * (H(m) + x * r)) mod q */ s = asn_mult(asn_pow(k, asn_sub(q, "\2", q), q), asn_add("\0" + hash_sha1(m), asn_mult(x, r, q), q), q); r = ("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + r)[strlen(r) ..]; s = ("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + s)[strlen(s) ..]; return make_string("ssh-dss") + make_string(r + s); } /* * NAME: shift_key() * DESCRIPTION: shift out the lowest bit of all characters in a key string */ private string shift_key(string key) { int i, len; /* * Believe it or not, but the openssl crypto suite has the parity for * DES setkey in the <lowest> bit. */ for (i = 0, len = strlen(key); i < len; i++) { key[i] >>= 1; } return key; } /* * NAME: set_keys() * DESCRIPTION: create keys as negotiated */ private void set_keys() { string str, client_key, server_key; str = make_string(K) + H; dstate = hash_sha1(str, "A", session_id)[.. 7]; estate = hash_sha1(str, "B", session_id)[.. 7]; client_key = hash_sha1(str, "C", session_id); client_key += hash_sha1(str, client_key); server_key = hash_sha1(str, "D", session_id); server_key += hash_sha1(str, server_key); client_mac = hash_sha1(str, "E", session_id) + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; server_mac = hash_sha1(str, "F", session_id) + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; dkey1 = decrypt("DES key", shift_key(client_key[.. 7])); dkey2 = encrypt("DES key", shift_key(client_key[8 .. 15])); dkey3 = decrypt("DES key", shift_key(client_key[16 .. 23])); ekey1 = encrypt("DES key", shift_key(server_key[.. 7])); ekey2 = decrypt("DES key", shift_key(server_key[8 .. 15])); ekey3 = encrypt("DES key", shift_key(server_key[16 .. 23])); } /* * NAME: start_transport() * DESCRIPTION: start up the transport layer */ private void start_transport(string version) { DEBUG("client version is " + version); transport_state = TRANSPORT_KEXINIT; client_version = version; server_kexinit = make_mesg(SSH_MSG_KEXINIT) + better_random_string(16) + make_string("diffie-hellman-group1-sha1") + make_string("ssh-dss") + make_string("3des-cbc") + make_string("3des-cbc") + make_string("hmac-sha1") + make_string("hmac-sha1") + make_string("none") + make_string("none") + make_string("") + make_string("") + "\0" + "\0\0\0\0"; send_packet(server_kexinit); } /* * NAME: receive_packet() * DESCRIPTION: receive a packet from connection */ private int receive_packet(string str) { int offset; if (transport_state == TRANSPORT_SKIP) { transport_state = TRANSPORT_KEXDH; return MODE_NOCHANGE; } switch (str[0]) { case SSH_MSG_DISCONNECT: return MODE_DISCONNECT; case SSH_MSG_IGNORE: break; case SSH_MSG_UNIMPLEMENTED: case SSH_MSG_DEBUG: if (transport_state == TRANSPORT_TRANSPORT) { break; } break; case SSH_MSG_KEXINIT: if (transport_state == TRANSPORT_KEXINIT) { client_kexinit = str; /* generate random y (0 < y < q) */ do { y = asn_mod("\1" + better_random_string(strlen(q)), q); } while (strlen(y) < strlen(q) - 1); /* f = g ^ y mod p */ f = asn_pow("\2", y, p); offset = 17; /* type + random */ offset += 4 + get_int(str, offset); /* kex */ offset += 4 + get_int(str, offset); /* host key */ offset += 4 + get_int(str, offset); /* decrypt */ offset += 4 + get_int(str, offset); /* encrypt */ offset += 4 + get_int(str, offset); /* demac */ offset += 4 + get_int(str, offset); /* mac */ offset += 4 + get_int(str, offset); /* decompress */ offset += 4 + get_int(str, offset); /* compress */ offset += 4 + get_int(str, offset); /* de-lang */ offset += 4 + get_int(str, offset); /* lang */ if (str[offset]) { transport_state = TRANSPORT_SKIP; } else { transport_state = TRANSPORT_KEXDH; } } else if (transport_state == TRANSPORT_TRANSPORT) { server_kexinit = make_mesg(SSH_MSG_KEXINIT) + better_random_string(16) + make_string("diffie-hellman-group1-sha1") + make_string("ssh-dss") + make_string("3des-cbc") + make_string("3des-cbc") + make_string("hmac-sha1") + make_string("hmac-sha1") + make_string("none") + make_string("none") + make_string("") + make_string("") + "\0" + "\0\0\0\0"; send_packet(server_kexinit); transport_state = TRANSPORT_KEXINIT; } break; case SSH_MSG_KEXDH_INIT: if (transport_state == TRANSPORT_KEXDH) { e = get_string(str, 1); str = SSHD->query_pub_host_key(); /* K = e ^ y mod p */ K = asn_pow(e, y, p); /* H = shared secret */ H = hash_sha1(make_string(client_version), make_string(SSHD->query_version()), make_string(client_kexinit), make_string(server_kexinit), make_string(str), make_string(e), make_string(f), make_string(K)); if (!session_id) { session_id = H; } str = make_mesg(SSH_MSG_KEXDH_REPLY) + make_string(str) + make_string(f) + make_string(ssh_dss_sign(H, SSHD->query_host_key())); send_packet(str); transport_state = TRANSPORT_NEWKEYS; } break; case SSH_MSG_NEWKEYS: if (transport_state == TRANSPORT_NEWKEYS) { send_packet(make_mesg(SSH_MSG_NEWKEYS)); set_keys(); transport_state = TRANSPORT_TRANSPORT; } break; default: if (transport_state == TRANSPORT_TRANSPORT) { return userauth(str); } break; } return MODE_NOCHANGE; } /* * NAME: create_transport() * DESCRIPTION: initialize transport layer */ private void create_transport() { /* p = 2^1024 - 2^960 - 1 + 2^64 * floor( 2^894 Pi + 129093 ) */ p = "\0" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xC9\x0F\xDA\xA2\x21\x68\xC2\x34" + "\xC4\xC6\x62\x8B\x80\xDC\x1C\xD1\x29\x02\x4E\x08\x8A\x67\xCC\x74" + "\x02\x0B\xBE\xA6\x3B\x13\x9B\x22\x51\x4A\x08\x79\x8E\x34\x04\xDD" + "\xEF\x95\x19\xB3\xCD\x3A\x43\x1B\x30\x2B\x0A\x6D\xF2\x5F\x14\x37" + "\x4F\xE1\x35\x6D\x6D\x51\xC2\x45\xE4\x85\xB5\x76\x62\x5E\x7E\xC6" + "\xF4\x4C\x42\xE9\xA6\x37\xED\x6B\x0B\xFF\x5C\xB6\xF4\x06\xB7\xED" + "\xEE\x38\x6B\xFB\x5A\x89\x9F\xA5\xAE\x9F\x24\x11\x7C\x4B\x1F\xE6" + "\x49\x28\x66\x51\xEC\xE6\x53\x81\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"; /* q = (p - 1) / 2 */ q = asn_rshift(p, 1); } /* ========================================================================= * * Section 3: authentication * * ========================================================================= */ /* * NAME: userauth_banner() * DESCRIPTION: display a banner during the authentication period */ private int userauth_banner(string str) { return send_packet(make_mesg(SSH_MSG_USERAUTH_BANNER) + make_string(str) + make_string("en")); } /* * NAME: userauth() * DESCRIPTION: respond to a userauth service request */ private int userauth(string str) { string name, service, method, password; int offset; switch (str[0]) { case SSH_MSG_SERVICE_REQUEST: if (get_string(str, 1) == "ssh-userauth" && !query_user()) { send_packet(make_mesg(SSH_MSG_SERVICE_ACCEPT) + make_string("ssh-userauth")); } else { send_packet(make_mesg(SSH_MSG_DISCONNECT) + make_int(SSH_DISCONNECT_SERVICE_NOT_AVAILABLE) + make_string("service not available") + make_string("en")); return MODE_DISCONNECT; } break; case SSH_MSG_USERAUTH_REQUEST: if (!query_user()) { name = get_string(str, 1); offset = strlen(name) + 5; service = get_string(str, offset); offset += strlen(service) + 4; method = get_string(str, offset); offset += strlen(method) + 4; if (service == "ssh-connection" && method == "password" && !str[offset]) { password = get_string(str, offset + 1); if (conn::receive_message(nil, name) != MODE_DISCONNECT && query_user() && conn::receive_message(nil, password) != MODE_DISCONNECT) { send_packet(make_mesg(SSH_MSG_USERAUTH_SUCCESS)); break; } DEBUG("login failed for " + name); send_packet(make_mesg(SSH_MSG_USERAUTH_FAILURE) + make_string("login failed") + "\0"); } } send_packet(make_mesg(SSH_MSG_USERAUTH_FAILURE) + make_string("password") + "\0"); break; default: if (query_user()) { return client(str); } break; } return MODE_NOCHANGE; } /* ========================================================================= * * Section 4: connection layer * * ========================================================================= */ int channel; /* channel ID */ int window_size; /* transmit window */ int packet_size; /* maximum packet size */ int program; /* program started? */ /* * NAME: message() * DESCRIPTION: send a message to the client */ int message(string str) { if (channel >= 0 && window_size >= strlen(str)) { window_size -= strlen(str); while (strlen(str) > packet_size) { send_packet(make_mesg(SSH_MSG_CHANNEL_DATA) + make_int(channel) + make_string(str[.. packet_size - 1])); str = str[packet_size ..]; } send_packet(make_mesg(SSH_MSG_CHANNEL_DATA) + make_int(channel) + make_string(str)); return TRUE; } return FALSE; } /* * NAME: client() * DESCRIPTION: handle a message from the client */ private int client(string str) { int offset, channel_id; string type; switch (str[0]) { case SSH_MSG_GLOBAL_REQUEST: type = get_string(str, 1); offset = strlen(type) + 5; if (str[offset]) { send_packet(make_mesg(SSH_MSG_REQUEST_FAILURE)); } break; case SSH_MSG_CHANNEL_OPEN: type = get_string(str, 1); offset = 12; channel_id = get_int(str, offset); offset += 4; if (type != "session") { send_packet(make_mesg(SSH_MSG_CHANNEL_OPEN_FAILURE) + make_int(channel_id) + make_int(SSH_OPEN_UNKNOWN_CHANNEL_TYPE) + make_string("unknown channel type") + make_string("en")); break; } if (channel >= 0) { send_packet(make_mesg(SSH_MSG_CHANNEL_OPEN_FAILURE) + make_int(channel_id) + make_int(SSH_OPEN_RESOURCE_SHORTAGE) + make_string("out of channels") + make_string("en")); break; } channel = channel_id; window_size = get_int(str, offset); offset += 4; packet_size = get_int(str, offset); send_packet(make_mesg(SSH_MSG_CHANNEL_OPEN_CONFIRMATION) + make_int(channel_id) + make_int(channel_id) + make_int(0xffffffff) + make_int(2048)); break; case SSH_MSG_CHANNEL_CLOSE: if (get_int(str, 1) == channel) { send_packet(make_mesg(SSH_MSG_CHANNEL_CLOSE) + make_int(channel)); channel = -1; program = FALSE; } break; case SSH_MSG_CHANNEL_WINDOW_ADJUST: if (get_int(str, 1) == channel) { window_size += get_int(str, 5); } break; case SSH_MSG_CHANNEL_DATA: if (get_int(str, 1) == channel && program) { str = get_string(str, 5); return conn::receive_message(nil, str[.. strlen(str) - 2]); } break; case SSH_MSG_CHANNEL_REQUEST: channel_id = get_int(str, 1); type = get_string(str, 5); offset = strlen(type) + 9; if (channel_id == channel && type == "shell" && !program) { program = TRUE; if (str[offset]) { send_packet(make_mesg(SSH_MSG_CHANNEL_SUCCESS) + make_int(channel_id)); } } else if (str[offset]) { send_packet(make_mesg(SSH_MSG_CHANNEL_FAILURE) + make_int(channel_id)); } break; case SSH_MSG_CHANNEL_EXTENDED_DATA: case SSH_MSG_CHANNEL_EOF: break; /* ignore */ default: send_packet(make_mesg(SSH_MSG_UNIMPLEMENTED) + make_int(recv_seqno - 1)); break; } return MODE_NOCHANGE; } /* * NAME: create_client() * DESCRIPTION: initialize the client layer */ private void create_client() { channel = -1; } /* ========================================================================= * * Section 5: compat layer * * ========================================================================= */ /* * NAME: login() * DESCRIPTION: accept a SSH connection */ int login(string str) { if (previous_program() == LIB_CONN) { user::connection(previous_object()); previous_object()->set_mode(MODE_RAW); start_transport(str); } return MODE_RAW; } /* * NAME: logout() * DESCRIPTION: disconnect */ void logout(int quit) { if (previous_program() == LIB_CONN) { conn::close(nil, quit); if (quit) { destruct_object(this_object()); } } } /* * NAME: set_mode() * DESCRIPTION: pass on mode changes to the real connection object */ void set_mode(int mode) { if (SYSTEM() && mode >= MODE_UNBLOCK) { query_conn()->set_mode(mode); } } /* * NAME: message_done() * DESCRIPTION: ready for another message */ int message_done() { if (previous_program() == LIB_CONN) { object user; int mode; user = query_user(); if (user) { mode = user->message_done(); if (mode == MODE_DISCONNECT) { return MODE_DISCONNECT; } if (mode >= MODE_UNBLOCK) { return mode; } } return MODE_NOCHANGE; } } /* * NAME: datagram_challenge() * DESCRIPTION: don't allow a datagram channel to be opened */ void datagram_challenge(string str) { error("Datagram channel cannot be opened"); } /* * NAME: datagram() * DESCRIPTION: don't send a datagram to the client */ int datagram(string str) { return 0; } /* * NAME: disconnect() * DESCRIPTION: forward a disconnect to the connection */ void disconnect() { if (previous_program() == LIB_USER) { user::disconnect(); } } /* * NAME: create() * DESCRIPTION: initialize secure shell */ static void create(int clone) { if (clone) { conn::create("telnet"); /* pretend */ create_packet(); create_transport(); create_client(); } }