/* * ssheven * * Copyright (c) 2020 by cy384 * See LICENSE file for details */ // retro68 stdio/console library #include // open transport #include #include // mac os threads #include // libssh2 #include // network buffer size enum { buffer_size = 4096 }; // text input buffer size enum { input_buffer_size = 128 }; const char* libssh2_error_string(int i) { switch (i) { case LIBSSH2_ERROR_NONE: return "no error (LIBSSH2_ERROR_NONE)"; case LIBSSH2_ERROR_BANNER_RECV: return "LIBSSH2_ERROR_BANNER_RECV"; case LIBSSH2_ERROR_BANNER_SEND: return "LIBSSH2_ERROR_BANNER_SEND"; case LIBSSH2_ERROR_INVALID_MAC: return "LIBSSH2_ERROR_INVALID_MAC"; case LIBSSH2_ERROR_KEX_FAILURE: return "LIBSSH2_ERROR_KEX_FAILURE"; case LIBSSH2_ERROR_ALLOC: return "LIBSSH2_ERROR_ALLOC"; case LIBSSH2_ERROR_SOCKET_SEND: return "LIBSSH2_ERROR_SOCKET_SEND"; case LIBSSH2_ERROR_KEY_EXCHANGE_FAILURE: return "LIBSSH2_ERROR_KEY_EXCHANGE_FAILURE"; case LIBSSH2_ERROR_TIMEOUT: return "LIBSSH2_ERROR_TIMEOUT"; case LIBSSH2_ERROR_HOSTKEY_INIT: return "LIBSSH2_ERROR_HOSTKEY_INIT"; case LIBSSH2_ERROR_HOSTKEY_SIGN: return "LIBSSH2_ERROR_HOSTKEY_SIGN"; case LIBSSH2_ERROR_DECRYPT: return "LIBSSH2_ERROR_DECRYPT"; case LIBSSH2_ERROR_SOCKET_DISCONNECT: return "LIBSSH2_ERROR_SOCKET_DISCONNECT"; case LIBSSH2_ERROR_PROTO: return "LIBSSH2_ERROR_PROTO"; case LIBSSH2_ERROR_PASSWORD_EXPIRED: return "LIBSSH2_ERROR_PASSWORD_EXPIRED"; case LIBSSH2_ERROR_FILE: return "LIBSSH2_ERROR_FILE"; case LIBSSH2_ERROR_METHOD_NONE: return "LIBSSH2_ERROR_METHOD_NONE"; case LIBSSH2_ERROR_AUTHENTICATION_FAILED: return "LIBSSHLIBSSH2_ERROR_AUTHENTICATION_FAILED2_ERROR_NONE"; case LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED: return "LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED"; case LIBSSH2_ERROR_CHANNEL_OUTOFORDER: return "LIBSSH2_ERROR_CHANNEL_OUTOFORDER"; case LIBSSH2_ERROR_CHANNEL_FAILURE: return "LIBSSH2_ERROR_CHANNEL_FAILURE"; case LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED: return "LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED"; case LIBSSH2_ERROR_CHANNEL_UNKNOWN: return "LIBSSH2_ERROR_CHANNEL_UNKNOWN"; case LIBSSH2_ERROR_CHANNEL_WINDOW_EXCEEDED: return "LIBSSH2_ERROR_CHANNEL_WINDOW_EXCEEDED"; case LIBSSH2_ERROR_CHANNEL_PACKET_EXCEEDED: return "LIBSSH2_ERROR_CHANNEL_PACKET_EXCEEDED"; case LIBSSH2_ERROR_CHANNEL_CLOSED: return "LIBSSH2_ERROR_CHANNEL_CLOSED"; case LIBSSH2_ERROR_CHANNEL_EOF_SENT: return "LIBSSH2_ERROR_CHANNEL_EOF_SENT"; case LIBSSH2_ERROR_SCP_PROTOCOL: return "LIBSSH2_ERROR_SCP_PROTOCOL"; case LIBSSH2_ERROR_ZLIB: return "LIBSSH2_ERROR_ZLIB"; case LIBSSH2_ERROR_SOCKET_TIMEOUT: return "LIBSSH2_ERROR_SOCKET_TIMEOUT"; case LIBSSH2_ERROR_SFTP_PROTOCOL: return "LIBSSH2_ERROR_SFTP_PROTOCOL"; case LIBSSH2_ERROR_REQUEST_DENIED: return "LIBSSH2_ERROR_REQUEST_DENIED"; case LIBSSH2_ERROR_METHOD_NOT_SUPPORTED: return "LIBSSH2_ERROR_METHOD_NOT_SUPPORTED"; case LIBSSH2_ERROR_INVAL: return "LIBSSH2_ERROR_INVAL"; case LIBSSH2_ERROR_INVALID_POLL_TYPE: return "LIBSSH2_ERROR_INVALID_POLL_TYPE"; case LIBSSH2_ERROR_PUBLICKEY_PROTOCOL: return "LIBSSH2_ERROR_PUBLICKEY_PROTOCOL"; case LIBSSH2_ERROR_EAGAIN: return "LIBSSH2_ERROR_EAGAIN"; case LIBSSH2_ERROR_BUFFER_TOO_SMALL: return "LIBSSH2_ERROR_BUFFER_TOO_SMALL"; case LIBSSH2_ERROR_BAD_USE: return "LIBSSH2_ERROR_BAD_USE"; case LIBSSH2_ERROR_COMPRESS: return "LIBSSH2_ERROR_COMPRESS"; case LIBSSH2_ERROR_OUT_OF_BOUNDARY: return "LIBSSH2_ERROR_OUT_OF_BOUNDARY"; case LIBSSH2_ERROR_AGENT_PROTOCOL: return "LIBSSH2_ERROR_SOCKET_RECV"; case LIBSSH2_ERROR_SOCKET_RECV: return "LIBSSH2_ERROR_ENCRYPT"; case LIBSSH2_ERROR_ENCRYPT: return "LIBSSH2_ERROR_ENCRYPT"; case LIBSSH2_ERROR_BAD_SOCKET: return "LIBSSH2_ERROR_BAD_SOCKET"; case LIBSSH2_ERROR_KNOWN_HOSTS: return "LIBSSH2_ERROR_KNOWN_HOSTS"; case LIBSSH2_ERROR_CHANNEL_WINDOW_FULL: return "LIBSSH2_ERROR_CHANNEL_WINDOW_FULL"; case LIBSSH2_ERROR_KEYFILE_AUTH_FAILED: return "LIBSSH2_ERROR_KEYFILE_AUTH_FAILED"; default: return "unknown error number"; } return "should never return from here?"; } // event handler to yield whenever we're blocked static pascal void yield_notifier(void* contextPtr, OTEventCode code, OTResult result, void* cookie) { switch (code) { case kOTSyncIdleEvent: YieldToAnyThread(); break; default: break; } } void get_line(char* buffer) { int i = 0; char c; while (i < input_buffer_size - 1) { c = getc(stdin); if (c != '\n') buffer[i++] = c; else break; } buffer[i] = '\0'; return; } void do_ssh_connection(char* hostname, char* username, char* password, char* command) { // libssh2 vars LIBSSH2_CHANNEL* channel; LIBSSH2_SESSION* session; int rc; // OT vars OSStatus err = noErr; char* buffer = NULL; EndpointRef endpoint = kOTInvalidEndpointRef; TCall sndCall; DNSAddress hostDNSAddress; OSStatus result; OTFlags ot_flags; // allocate buffer buffer = OTAllocMem(buffer_size); if (buffer == NULL) { printf("failed to allocate OT buffer\n"); return; } // open TCP endpoint endpoint = OTOpenEndpoint(OTCreateConfiguration(kTCPName), 0, nil, &err); if (err != noErr) { printf("failed to open TCP endpoint\n"); if (buffer != NULL) OTFreeMem(buffer); return; } #define OT_CHECK(X) err = (X); if (err != noErr) { printf("" #X " failed: %d\n", err); goto OT_cleanup; }; OT_CHECK(OTSetSynchronous(endpoint)); OT_CHECK(OTSetBlocking(endpoint)); OT_CHECK(OTInstallNotifier(endpoint, yield_notifier, nil)); OT_CHECK(OTUseSyncIdleEvents(endpoint, true)); OT_CHECK(OTBind(endpoint, nil, nil)); // set up address struct, do the DNS lookup, and connect OTMemzero(&sndCall, sizeof(TCall)); sndCall.addr.buf = (UInt8 *) &hostDNSAddress; sndCall.addr.len = OTInitDNSAddress(&hostDNSAddress, (char *) hostname); OT_CHECK(OTConnect(endpoint, &sndCall, nil)); printf("OT setup done, endpoint: %d, (should not be %d for libssh2," " should not be %d for OT)\n", (int) endpoint, (int) LIBSSH2_INVALID_SOCKET, (int) kOTInvalidEndpointRef); #define SSH_CHECK(X) rc = (X); if (rc != LIBSSH2_ERROR_NONE) { printf("" #X "failed: %s\n", libssh2_error_string(rc)); goto libssh2_cleanup; }; // init libssh2 SSH_CHECK(libssh2_init(0)); session = libssh2_session_init(); if (session == 0) { printf("failed to open SSH session\n"); goto libssh2_cleanup; } SSH_CHECK(libssh2_session_handshake(session, endpoint)); SSH_CHECK(libssh2_userauth_password(session, username, password)); channel = libssh2_channel_open_session(session); printf("channel open: %d\n", channel); SSH_CHECK(libssh2_channel_exec(channel, command)); // read from the channel rc = libssh2_channel_read(channel, buffer, buffer_size); if (rc > 0) { printf("got %d bytes:\n", rc); for(int i = 0; i < rc; ++i) printf("%c", buffer[i]); printf("\n"); } else { printf("channel read error: %s\n", libssh2_error_string(rc)); } libssh2_cleanup: libssh2_channel_close(channel); libssh2_channel_free(channel); libssh2_session_disconnect(session, "Normal Shutdown, Thank you for playing"); libssh2_session_free(session); libssh2_exit(); // request to close the TCP connection OT_CHECK(OTSndOrderlyDisconnect(endpoint)); // get and discard remaining data so we can finish closing the connection rc = 1; while (rc != kOTLookErr) { rc = OTRcv(endpoint, buffer, 1, &ot_flags); } // finish closing the TCP connection result = OTLook(endpoint); switch (result) { case T_DISCONNECT: OTRcvDisconnect(endpoint, nil); break; default: printf("unexpected OTLook result while closing: %d\n", result); break; } OT_cleanup: // release endpoint err = OTUnbind(endpoint); if (err != noErr) printf("OTUnbind failed: %d\n", err); err = OTCloseProvider(endpoint); if (err != noErr) printf("OTCloseProvider failed: %d\n", err); // if we have a buffer, release it if (buffer != nil) OTFreeMem(buffer); return; } int main(int argc, char** argv) { char hostname[input_buffer_size] = {0}; char username[input_buffer_size] = {0}; char password[input_buffer_size] = {0}; char command[input_buffer_size] = {0}; printf("WARNING: this is a prototype with a bad RNG and no host key checks," " do not use over untrusted networks or with untrusted SSH servers!\n\n"); printf("ssheven by cy384 version 0.0.0\n\n"); printf("enter a host:port >"); fflush(stdout); get_line(hostname); printf("enter a username >"); fflush(stdout); get_line(username); printf("enter a password >"); fflush(stdout); get_line(password); printf("enter a command >"); fflush(stdout); get_line(command); if (InitOpenTransport() != noErr) { printf("failed to initialize OT\n"); return 0; } do_ssh_connection(hostname, username, password, command); CloseOpenTransport(); printf("\n(enter to exit)\n"); getchar(); return 0; }