lv_libssh2_status_t lv_libssh2_knownhosts_add( lv_libssh2_knownhosts_t* handle, const char* name, const char* salt, const char* key, const size_t key_len, const char* comment, const size_t comment_len, const int type_mask, lv_libssh2_knownhost_t* knownhost ) { if (handle == NULL) { return LV_LIBSSH2_STATUS_ERROR_NULL_VALUE; } if (knownhost == NULL) { return LV_LIBSSH2_STATUS_ERROR_NULL_VALUE; } int result = libssh2_knownhost_addc( handle->inner, name, salt, key, key_len, comment, comment_len, type_mask, &knownhost->inner ); return lv_libssh2_status_from_result(result); }
/** * seashell_tunnel_connect_password (const char* host, const char* user, const char* password, int* error) * Connects to the host via SSH on port 22, and launches a Seashell backend instance for that user * on the host. * * Consults /etc/seashell_hosts for host's SSH public keys. If this file does not exist, * this function will fail for security reasons. /etc/seashell_hosts is a standard * OpenSSH known_hosts file. * * Arguments: * host - Host to connect to. * user - User to run as. * password - User's password. * error - [optional] denotes error on failure. * remote_addr - Address to which the remote IP address will * be written. Reserve 128 bytes. * family - Address family. * target - Target to execute. * * Returns: * Handle to connection object on success, NULL otherwise. * If error is NOT null, error will hold more detailed error information. */ struct seashell_connection* seashell_tunnel_connect_password (const char* host, const char* user, const char* password, int* error, char * remote_addr, int* family, char* target) { struct addrinfo hints; struct addrinfo *results, *rp; int sockfd; int i, e; struct seashell_connection* result = NULL; /* Resolve the host's address. * See getaddrinfo(3) for how this works. */ memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = 0; hints.ai_protocol = 0; e = getaddrinfo(host, "22", &hints, &results); if (e != 0) { SET_ERROR(TUNNEL_ERROR_RESOLV); return NULL; } for (rp = results; rp != NULL; rp = rp->ai_next) { sockfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (sockfd == -1) continue; if (connect(sockfd, rp->ai_addr, rp->ai_addrlen) != -1) break; close(sockfd); } /* Write address that we're connecting to into * remote_addr. */ if(rp != NULL) { *family = rp->ai_family; switch(rp->ai_family) { case AF_INET: if(inet_ntop(rp->ai_family, &((struct sockaddr_in *)rp->ai_addr)->sin_addr, remote_addr, 128) == NULL) { SET_ERROR(TUNNEL_ERROR_RESOLV); return NULL; } break; case AF_INET6: if(inet_ntop(rp->ai_family, &((struct sockaddr_in6 *)rp->ai_addr)->sin6_addr, remote_addr, 128) == NULL) { SET_ERROR(TUNNEL_ERROR_RESOLV); return NULL; } break; default: SET_ERROR(TUNNEL_ERROR_RESOLV); return NULL; } } freeaddrinfo(results); /* Either rp == NULL, in which case we failed at connecting, * or sockfd holds our socket. */ if (rp == NULL) { SET_ERROR(TUNNEL_ERROR_CONNECT); return NULL; } /** Set up the session */ LIBSSH2_SESSION* session; LIBSSH2_CHANNEL* channel; LIBSSH2_KNOWNHOSTS* hosts; size_t len; int type; session = libssh2_session_init(); if (!session) { SET_ERROR(TUNNEL_ERROR_SESSION_START); goto session_teardown; } e = libssh2_session_handshake(session, sockfd); if (e) { SET_ERROR(TUNNEL_ERROR_SESSION_HANDSHAKE); goto session_teardown; } hosts = libssh2_knownhost_init(session); if (!hosts) { SET_ERROR(TUNNEL_ERROR_HOSTS_FILE); goto session_teardown; } if (!IS_INSTALLED() && access(DEBUG_HOSTS_FILE, F_OK) != -1) { libssh2_knownhost_readfile(hosts, DEBUG_HOSTS_FILE, LIBSSH2_KNOWNHOST_FILE_OPENSSH); } else { libssh2_knownhost_readfile(hosts, HOSTS_FILE, LIBSSH2_KNOWNHOST_FILE_OPENSSH); } const char* fingerprint = libssh2_session_hostkey(session, &len, &type); if (!fingerprint || type == LIBSSH2_HOSTKEY_TYPE_UNKNOWN) { libssh2_knownhost_free(hosts); SET_ERROR(TUNNEL_ERROR_HOST); goto session_teardown; } struct libssh2_knownhost *hostkey; /** NOTE: Documentation is buggy. hostkey MUST be passed. */ int check = libssh2_knownhost_check(hosts, host, fingerprint, len, LIBSSH2_KNOWNHOST_TYPE_PLAIN | LIBSSH2_KNOWNHOST_KEYENC_RAW, &hostkey); if (check != LIBSSH2_KNOWNHOST_CHECK_MATCH) { int keytype = 0; switch (type) { case LIBSSH2_HOSTKEY_TYPE_RSA: keytype = LIBSSH2_KNOWNHOST_KEY_SSHRSA; break; case LIBSSH2_HOSTKEY_TYPE_DSS: keytype = LIBSSH2_KNOWNHOST_KEY_SSHRSA; break; } if (keytype) { libssh2_knownhost_addc(hosts, host, NULL, fingerprint, len, "Generated from Seashell Tunnel", strlen("Generated from Seashell Tunnel"), LIBSSH2_KNOWNHOST_TYPE_PLAIN | LIBSSH2_KNOWNHOST_KEYENC_RAW | (type == LIBSSH2_HOSTKEY_TYPE_RSA ? LIBSSH2_KNOWNHOST_KEY_SSHRSA : LIBSSH2_KNOWNHOST_KEY_SSHDSS), NULL); libssh2_knownhost_writefile(hosts, DUMP_FILE, LIBSSH2_KNOWNHOST_FILE_OPENSSH); fprintf(stderr, "%s: Check SSH key for %s! Keys written to %s\n", user, host, DUMP_FILE); } else { fprintf(stderr, "%s: Check SSH key for %s!\n", user, host, DUMP_FILE); fprintf(stderr, "%s: Keys not written to file - contact Seashell Maintainers to add support for the LibSSH2 key format %d\n", user, type); } libssh2_knownhost_free(hosts); SET_ERROR(TUNNEL_ERROR_HOST); goto session_teardown; } libssh2_knownhost_free(hosts); FPRINTF_IF_DEBUG(stderr, "%s: Host check passed for %s (fingerprint type %d) - ", user, host, type); for(i = 0; i < 20; i++) { FPRINTF_IF_DEBUG(stderr, "%02X ", (unsigned char)fingerprint[i]); } FPRINTF_IF_DEBUG(stderr, "\n"); e = libssh2_userauth_password(session, user, password); if (e) { FPRINTF_IF_DEBUG(stderr, "%s: Error authenticating: %d\n", user, e); SET_ERROR(TUNNEL_ERROR_CREDS); goto session_teardown; } channel = libssh2_channel_open_session(session); if (!channel) { SET_ERROR(TUNNEL_ERROR_CHANNEL_OPEN); goto session_teardown; } /** * Ideally we'd have a subsystem configured, * as I don't see a good way of pulling out of ssh2 * if the target does not exist. */ e = libssh2_channel_exec(channel, target); if (e) { SET_ERROR(TUNNEL_ERROR_LAUNCH_SEASHELL); goto channel_teardown; } result = malloc(sizeof(struct seashell_connection)); if (!result) { SET_ERROR(TUNNEL_ERROR_SESSION_START); goto channel_teardown; } result->sockfd = sockfd; result->session = session; result->channel = channel; goto end; channel_teardown: libssh2_channel_free(channel); session_teardown: libssh2_session_free(session); close(sockfd); end: return result; }