libssh2
1/* Copyright (C) The libssh2 project and its contributors.
2*
3* Sample showing how to do SSH2 connect.
4*
5* The sample code has default values for host name, user name, password
6* and path to copy, but you can specify them on the command line like:
7*
8* $ ./ssh2 hostip user password [[-p|-i|-k] [command]]
9*
10* -p authenticate using password
11* -i authenticate using keyboard-interactive
12* -k authenticate using public key (password argument decrypts keyfile)
13* command executes on the remote machine
14*
15* SPDX-License-Identifier: BSD-3-Clause
16*/
17
18#include "libssh2_setup.h"19#include <libssh2.h>20
21#ifdef HAVE_SYS_SOCKET_H22#include <sys/socket.h>23#endif24#ifdef HAVE_UNISTD_H25#include <unistd.h>26#endif27#ifdef HAVE_NETINET_IN_H28#include <netinet/in.h>29#endif30#ifdef HAVE_ARPA_INET_H31#include <arpa/inet.h>32#endif33
34#include <stdio.h>35#include <stdlib.h>36#include <string.h>37
38static const char *pubkey = ".ssh/id_rsa.pub";39static const char *privkey = ".ssh/id_rsa";40static const char *username = "username";41static const char *password = "password";42
43static void kbd_callback(const char *name, int name_len,44const char *instruction, int instruction_len,45int num_prompts,46const LIBSSH2_USERAUTH_KBDINT_PROMPT *prompts,47LIBSSH2_USERAUTH_KBDINT_RESPONSE *responses,48void **abstract)49{
50(void)name;51(void)name_len;52(void)instruction;53(void)instruction_len;54if(num_prompts == 1) {55responses[0].text = strdup(password);56responses[0].length = (unsigned int)strlen(password);57}58(void)prompts;59(void)abstract;60}
61
62int main(int argc, char *argv[])63{
64uint32_t hostaddr;65libssh2_socket_t sock;66int i, auth_pw = 0;67struct sockaddr_in sin;68const char *fingerprint;69char *userauthlist;70int rc;71LIBSSH2_SESSION *session = NULL;72LIBSSH2_CHANNEL *channel;73
74#ifdef _WIN3275WSADATA wsadata;76
77rc = WSAStartup(MAKEWORD(2, 0), &wsadata);78if(rc) {79fprintf(stderr, "WSAStartup failed with error: %d\n", rc);80return 1;81}82#endif83
84if(argc > 1) {85hostaddr = inet_addr(argv[1]);86}87else {88hostaddr = htonl(0x7F000001);89}90if(argc > 2) {91username = argv[2];92}93if(argc > 3) {94password = argv[3];95}96
97rc = libssh2_init(0);98if(rc) {99fprintf(stderr, "libssh2 initialization failed (%d)\n", rc);100return 1;101}102
103/* Ultra basic "connect to port 22 on localhost". Your code is104* responsible for creating the socket establishing the connection
105*/
106sock = socket(AF_INET, SOCK_STREAM, 0);107if(sock == LIBSSH2_INVALID_SOCKET) {108fprintf(stderr, "failed to create socket.\n");109rc = 1;110goto shutdown;111}112
113sin.sin_family = AF_INET;114sin.sin_port = htons(22);115sin.sin_addr.s_addr = hostaddr;116
117fprintf(stderr, "Connecting to %s:%d as user %s\n",118inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), username);119
120if(connect(sock, (struct sockaddr*)(&sin), sizeof(struct sockaddr_in))) {121fprintf(stderr, "failed to connect.\n");122goto shutdown;123}124
125/* Create a session instance and start it up. This will trade welcome126* banners, exchange keys, and setup crypto, compression, and MAC layers
127*/
128session = libssh2_session_init();129if(!session) {130fprintf(stderr, "Could not initialize SSH session.\n");131goto shutdown;132}133
134/* Enable all debugging when libssh2 was built with debugging enabled */135libssh2_trace(session, ~0);136
137rc = libssh2_session_handshake(session, sock);138if(rc) {139fprintf(stderr, "Failure establishing SSH session: %d\n", rc);140goto shutdown;141}142
143rc = 1;144
145/* At this point we have not yet authenticated. The first thing to do146* is check the hostkey's fingerprint against our known hosts Your app
147* may have it hard coded, may go to a file, may present it to the
148* user, that's your call
149*/
150fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1);151fprintf(stderr, "Fingerprint: ");152for(i = 0; i < 20; i++) {153fprintf(stderr, "%02X ", (unsigned char)fingerprint[i]);154}155fprintf(stderr, "\n");156
157/* check what authentication methods are available */158userauthlist = libssh2_userauth_list(session, username,159(unsigned int)strlen(username));160if(userauthlist) {161fprintf(stderr, "Authentication methods: %s\n", userauthlist);162if(strstr(userauthlist, "password")) {163auth_pw |= 1;164}165if(strstr(userauthlist, "keyboard-interactive")) {166auth_pw |= 2;167}168if(strstr(userauthlist, "publickey")) {169auth_pw |= 4;170}171
172/* check for options */173if(argc > 4) {174if((auth_pw & 1) && !strcmp(argv[4], "-p")) {175auth_pw = 1;176}177if((auth_pw & 2) && !strcmp(argv[4], "-i")) {178auth_pw = 2;179}180if((auth_pw & 4) && !strcmp(argv[4], "-k")) {181auth_pw = 4;182}183}184
185if(auth_pw & 1) {186/* We could authenticate via password */187if(libssh2_userauth_password(session, username, password)) {188fprintf(stderr, "Authentication by password failed.\n");189goto shutdown;190}191else {192fprintf(stderr, "Authentication by password succeeded.\n");193}194}195else if(auth_pw & 2) {196/* Or via keyboard-interactive */197if(libssh2_userauth_keyboard_interactive(session, username,198&kbd_callback) ) {199fprintf(stderr,200"Authentication by keyboard-interactive failed.\n");201goto shutdown;202}203else {204fprintf(stderr,205"Authentication by keyboard-interactive succeeded.\n");206}207}208else if(auth_pw & 4) {209/* Or by public key */210size_t fn1sz, fn2sz;211char *fn1, *fn2;212char const *h = getenv("HOME");213if(!h || !*h)214h = ".";215fn1sz = strlen(h) + strlen(pubkey) + 2;216fn2sz = strlen(h) + strlen(privkey) + 2;217fn1 = malloc(fn1sz);218fn2 = malloc(fn2sz);219if(!fn1 || !fn2) {220free(fn2);221free(fn1);222fprintf(stderr, "out of memory\n");223goto shutdown;224}225/* Avoid false positives */226#if defined(__GNUC__) && __GNUC__ >= 7227#pragma GCC diagnostic push228#pragma GCC diagnostic warning "-Wformat-truncation=1"229#endif230/* Using asprintf() here would be much cleaner,231but less portable */
232snprintf(fn1, fn1sz, "%s/%s", h, pubkey);233snprintf(fn2, fn2sz, "%s/%s", h, privkey);234#if defined(__GNUC__) && __GNUC__ >= 7235#pragma GCC diagnostic pop236#endif237
238if(libssh2_userauth_publickey_fromfile(session, username,239fn1, fn2,240password)) {241fprintf(stderr, "Authentication by public key failed.\n");242free(fn2);243free(fn1);244goto shutdown;245}246else {247fprintf(stderr, "Authentication by public key succeeded.\n");248}249free(fn2);250free(fn1);251}252else {253fprintf(stderr, "No supported authentication methods found.\n");254goto shutdown;255}256}257
258/* Request a session channel on which to run a shell */259channel = libssh2_channel_open_session(session);260if(!channel) {261fprintf(stderr, "Unable to open a session\n");262goto shutdown;263}264
265/* Some environment variables may be set,266* It's up to the server which ones it'll allow though
267*/
268libssh2_channel_setenv(channel, "FOO", "bar");269
270/* Request a terminal with 'vanilla' terminal emulation271* See /etc/termcap for more options. This is useful when opening
272* an interactive shell.
273*/
274#if 0275if(libssh2_channel_request_pty(channel, "vanilla")) {276fprintf(stderr, "Failed requesting pty\n");277}278#endif279
280if(argc > 5) {281if(libssh2_channel_exec(channel, argv[5])) {282fprintf(stderr, "Unable to request command on channel\n");283goto shutdown;284}285/* Instead of just running a single command with libssh2_channel_exec,286* a shell can be opened on the channel instead, for interactive use.
287* You usually want a pty allocated first in that case (see above). */
288#if 0289if(libssh2_channel_shell(channel)) {290fprintf(stderr, "Unable to request shell on allocated pty\n");291goto shutdown;292}293#endif294
295/* At this point the shell can be interacted with using296* libssh2_channel_read()
297* libssh2_channel_read_stderr()
298* libssh2_channel_write()
299* libssh2_channel_write_stderr()
300*
301* Blocking mode may be (en|dis)abled with:
302* libssh2_channel_set_blocking()
303* If the server send EOF, libssh2_channel_eof() will return non-0
304* To send EOF to the server use: libssh2_channel_send_eof()
305* A channel can be closed with: libssh2_channel_close()
306* A channel can be freed with: libssh2_channel_free()
307*/
308
309/* Read and display all the data received on stdout (ignoring stderr)310* until the channel closes. This will eventually block if the command
311* produces too much data on stderr; the loop must be rewritten to use
312* non-blocking mode and include interspersed calls to
313* libssh2_channel_read_stderr() to avoid this. See ssh2_echo.c for
314* an idea of how such a loop might look.
315*/
316while(!libssh2_channel_eof(channel)) {317char buf[1024];318ssize_t err = libssh2_channel_read(channel, buf, sizeof(buf));319if(err < 0)320fprintf(stderr, "Unable to read response: %ld\n", (long)err);321else {322fwrite(buf, 1, (size_t)err, stdout);323}324}325}326
327rc = libssh2_channel_get_exit_status(channel);328
329if(libssh2_channel_close(channel))330fprintf(stderr, "Unable to close channel\n");331
332if(channel) {333libssh2_channel_free(channel);334channel = NULL;335}336
337/* Other channel types are supported via:338* libssh2_scp_send()
339* libssh2_scp_recv2()
340* libssh2_channel_direct_tcpip()
341*/
342
343shutdown:344
345if(session) {346libssh2_session_disconnect(session, "Normal Shutdown");347libssh2_session_free(session);348}349
350if(sock != LIBSSH2_INVALID_SOCKET) {351shutdown(sock, 2);352LIBSSH2_SOCKET_CLOSE(sock);353}354
355fprintf(stderr, "all done\n");356
357libssh2_exit();358
359#ifdef _WIN32360WSACleanup();361#endif362
363return rc;364}
365