int SSH2Channel::subsystem(const char *command, int timeout_ms, ExceptionSink *xsink) { AutoLocker al(parent->m); if (check_open(xsink)) return -1; BlockingHelper bh(parent); int rc; while (true) { rc = libssh2_channel_subsystem(channel, command); //printd(5, "SSH2Channel::subsystem() cmd=%s rc=%d\n", command, rc); if (rc == LIBSSH2_ERROR_EAGAIN) { if ((rc = parent->waitSocketUnlocked(xsink, SSH2CHANNEL_TIMEOUT, "SSH2CHANNEL-SUBSYSTEM-ERROR", "SSH2Channel::subsystem", timeout_ms))) break; continue; } if (rc) parent->doSessionErrUnlocked(xsink); break; } return rc; }
/* {{{ libssh2_publickey_init * Startup the publickey subsystem */ LIBSSH2_API LIBSSH2_PUBLICKEY *libssh2_publickey_init(LIBSSH2_SESSION *session) { LIBSSH2_PUBLICKEY *pkey = NULL; LIBSSH2_CHANNEL *channel = NULL; unsigned char buffer[19]; /* packet_len(4) + version_len(4) + "version"(7) + version_num(4) */ unsigned char *s, *data = NULL; unsigned long data_len; int response; #ifdef LIBSSH2_DEBUG_PUBLICKEY _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "Initializing publickey subsystem"); #endif channel = libssh2_channel_open_session(session); if (!channel) { libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE, "Unable to startup channel", 0); goto err_exit; } if (libssh2_channel_subsystem(channel, "publickey")) { libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE, "Unable to request publickey subsystem", 0); goto err_exit; } libssh2_channel_set_blocking(channel, 1); libssh2_channel_handle_extended_data(channel, LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE); pkey = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_PUBLICKEY)); if (!pkey) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate a new publickey structure", 0); goto err_exit; } pkey->channel = channel; pkey->version = 0; s = buffer; libssh2_htonu32(s, 4 + (sizeof("version") - 1) + 4); s += 4; libssh2_htonu32(s, sizeof("version") - 1); s += 4; memcpy(s, "version", sizeof("version") - 1); s += sizeof("version") - 1; libssh2_htonu32(s, LIBSSH2_PUBLICKEY_VERSION); s += 4; #ifdef LIBSSH2_DEBUG_PUBLICKEY _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "Sending publickey version packet advertising version %d support", (int)LIBSSH2_PUBLICKEY_VERSION); #endif if ((s - buffer) != libssh2_channel_write(channel, buffer, (s - buffer))) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send publickey version packet", 0); goto err_exit; } while (1) { if (libssh2_publickey_packet_receive(pkey, &data, &data_len)) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for response from publickey subsystem", 0); goto err_exit; } s = data; if ((response = libssh2_publickey_response_id(&s, data_len)) < 0) { libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, "Invalid publickey subsystem response code", 0); goto err_exit; } switch (response) { case LIBSSH2_PUBLICKEY_RESPONSE_STATUS: /* Error */ { unsigned long status, descr_len, lang_len; unsigned char *descr, *lang; status = libssh2_ntohu32(s); s += 4; descr_len = libssh2_ntohu32(s); s += 4; descr = s; s += descr_len; lang_len = libssh2_ntohu32(s); s += 4; lang = s; s += lang_len; if (s > data + data_len) { libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, "Malformed publickey subsystem packet", 0); goto err_exit; } libssh2_publickey_status_error(NULL, session, status, descr, descr_len); goto err_exit; } case LIBSSH2_PUBLICKEY_RESPONSE_VERSION: /* What we want */ pkey->version = libssh2_ntohu32(s); if (pkey->version > LIBSSH2_PUBLICKEY_VERSION) { #ifdef LIBSSH2_DEBUG_PUBLICKEY _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "Truncating remote publickey version from %lu", pkey->version); #endif pkey->version = LIBSSH2_PUBLICKEY_VERSION; } #ifdef LIBSSH2_DEBUG_PUBLICKEY _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "Enabling publickey subsystem version %lu", pkey->version); #endif LIBSSH2_FREE(session, data); return pkey; default: /* Unknown/Unexpected */ libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, "Unexpected publickey subsystem response, ignoring", 0); LIBSSH2_FREE(session, data); data = NULL; } } /* Never reached except by direct goto */ err_exit: if (channel) { libssh2_channel_close(channel); } if (pkey) { LIBSSH2_FREE(session, pkey); } if (data) { LIBSSH2_FREE(session, data); } return NULL; }
mx_channel_t * mx_channel_netconf (mx_sock_session_t *mssp, mx_sock_t *client, int xml_mode) { LIBSSH2_CHANNEL *channel; mx_channel_t *mcp; mcp = TAILQ_FIRST(&mssp->mss_released); if (mcp) { mx_log("S%u reusing channel C%u for client S%u", mssp->mss_base.ms_id, mcp->mc_id, client->ms_id); TAILQ_REMOVE(&mssp->mss_released, mcp, mc_link); TAILQ_INSERT_HEAD(&mssp->mss_channels, mcp, mc_link); mcp->mc_state = MSS_RPC_INITIAL; mcp->mc_client = client; if (mx_mti(client)->mti_set_channel) mx_mti(client)->mti_set_channel(client, mcp->mc_session, mcp); return mcp; } /* Must use blocking IO for channel creation */ libssh2_session_set_blocking(mssp->mss_session, 1); channel = libssh2_channel_open_session(mssp->mss_session); if (channel == NULL) { mx_log("S%u could not open netconf channel", mssp->mss_base.ms_id); return NULL; } if (!xml_mode) { if (libssh2_channel_subsystem(channel, "netconf") != 0) { mx_log("S%u could not open netconf subsystem", mssp->mss_base.ms_id); goto try_xml_mode; } mx_log("S%u opened netconf subsystem channel to %s", mssp->mss_base.ms_id, mssp->mss_target); } else { static const char command[] = "xml-mode netconf need-trailer"; try_xml_mode: if (libssh2_channel_process_startup(channel, "exec", sizeof("exec") - 1, command, strlen(command)) != 0) { mx_log("S%u could not open netconf xml-mode", mssp->mss_base.ms_id); libssh2_channel_free(channel); channel = NULL; } else { mx_log("S%u opened netconf xml-mode channel to %s", mssp->mss_base.ms_id, mssp->mss_target); } } libssh2_session_set_blocking(mssp->mss_session, 0); if (channel == NULL) { mx_log("S%u could not open netconf channel", mssp->mss_base.ms_id); return NULL; } mcp = mx_channel_create(mssp, client, channel); if (mcp == NULL) { /* XXX fail */ return NULL; } mx_channel_netconf_send_hello(mcp); mx_channel_netconf_read_hello(mcp); return mcp; }
netsnmp_transport * netsnmp_ssh_transport(const struct sockaddr_in *addr, int local) { netsnmp_transport *t = NULL; netsnmp_ssh_addr_pair *addr_pair = NULL; int rc = 0; int i, auth_pw = 0; const char *fingerprint; char *userauthlist; struct sockaddr_un *unaddr; const char *sockpath = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_SSHTOSNMP_SOCKET); char tmpsockpath[MAXPATHLEN]; #ifdef NETSNMP_NO_LISTEN_SUPPORT if (local) return NULL; #endif /* NETSNMP_NO_LISTEN_SUPPORT */ if (addr == NULL || addr->sin_family != AF_INET) { return NULL; } t = SNMP_MALLOC_TYPEDEF(netsnmp_transport); if (t == NULL) { return NULL; } t->domain = netsnmp_snmpSSHDomain; t->domain_length = netsnmp_snmpSSHDomain_len; t->flags = NETSNMP_TRANSPORT_FLAG_STREAM | NETSNMP_TRANSPORT_FLAG_TUNNELED; addr_pair = SNMP_MALLOC_TYPEDEF(netsnmp_ssh_addr_pair); if (addr_pair == NULL) { netsnmp_transport_free(t); return NULL; } t->data = addr_pair; t->data_length = sizeof(netsnmp_ssh_addr_pair); if (local) { #ifndef NETSNMP_NO_LISTEN_SUPPORT #ifdef SNMPSSHDOMAIN_USE_EXTERNAL_PIPE /* XXX: set t->local and t->local_length */ t->flags |= NETSNMP_TRANSPORT_FLAG_LISTEN; unaddr = &addr_pair->unix_socket_end; /* open a unix domain socket */ /* XXX: get data from the transport def for it's location */ unaddr->sun_family = AF_UNIX; if (NULL == sockpath) { sprintf(tmpsockpath, "%s/%s", get_persistent_directory(), DEFAULT_SOCK_NAME); sockpath = tmpsockpath; } snprintf(unaddr->sun_path, sizeof(unaddr->sun_path), "%s", sockpath); snprintf(addr_pair->socket_path, sizeof(addr_pair->socket_path), "%s", sockpath); t->sock = socket(PF_UNIX, SOCK_STREAM, 0); if (t->sock < 0) { netsnmp_transport_free(t); return NULL; } /* set the SO_PASSCRED option so we can receive the remote uid */ { int one = 1; setsockopt(t->sock, SOL_SOCKET, SO_PASSCRED, (void *) &one, sizeof(one)); } unlink(unaddr->sun_path); rc = bind(t->sock, unaddr, SUN_LEN(unaddr)); if (rc != 0) { DEBUGMSGTL(("netsnmp_ssh_transport", "couldn't bind \"%s\", errno %d (%s)\n", unaddr->sun_path, errno, strerror(errno))); netsnmp_ssh_close(t); netsnmp_transport_free(t); return NULL; } /* set the socket permissions */ { /* * Apply any settings to the ownership/permissions of the * Sshdomain socket */ int sshdomain_sock_perm = netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_SSHDOMAIN_SOCK_PERM); int sshdomain_sock_user = netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_SSHDOMAIN_SOCK_USER); int sshdomain_sock_group = netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_SSHDOMAIN_SOCK_GROUP); DEBUGMSGTL(("ssh", "here: %s, %d, %d, %d\n", unaddr->sun_path, sshdomain_sock_perm, sshdomain_sock_user, sshdomain_sock_group)); if (sshdomain_sock_perm != 0) { DEBUGMSGTL(("ssh", "Setting socket perms to %d\n", sshdomain_sock_perm)); chmod(unaddr->sun_path, sshdomain_sock_perm); } if (sshdomain_sock_user || sshdomain_sock_group) { /* * If either of user or group haven't been set, * then leave them unchanged. */ if (sshdomain_sock_user == 0 ) sshdomain_sock_user = -1; if (sshdomain_sock_group == 0 ) sshdomain_sock_group = -1; DEBUGMSGTL(("ssh", "Setting socket user/group to %d/%d\n", sshdomain_sock_user, sshdomain_sock_group)); chown(unaddr->sun_path, sshdomain_sock_user, sshdomain_sock_group); } } rc = listen(t->sock, NETSNMP_STREAM_QUEUE_LEN); if (rc != 0) { DEBUGMSGTL(("netsnmp_ssh_transport", "couldn't listen to \"%s\", errno %d (%s)\n", unaddr->sun_path, errno, strerror(errno))); netsnmp_ssh_close(t); netsnmp_transport_free(t); return NULL; } #else /* we're called directly by sshd and use stdin/out */ /* for ssh on the server side we've been launched so bind to stdin/out */ /* nothing to do */ /* XXX: verify we're inside ssh */ t->sock = STDIN_FILENO; #endif /* ! SNMPSSHDOMAIN_USE_EXTERNAL_PIPE */ #else /* NETSNMP_NO_LISTEN_SUPPORT */ netsnmp_transport_free(t); return NULL; #endif /* NETSNMP_NO_LISTEN_SUPPORT */ } else { char *username; char *keyfilepub; char *keyfilepriv; /* use the requested user name */ /* XXX: default to the current user name on the system like ssh does */ username = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_SSH_USERNAME); if (!username || 0 == *username) { snmp_log(LOG_ERR, "You must specify a ssh username to use. See the snmp.conf manual page\n"); netsnmp_transport_free(t); return NULL; } /* use the requested public key file */ keyfilepub = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_SSH_PUBKEY); if (!keyfilepub || 0 == *keyfilepub) { /* XXX: default to ~/.ssh/id_rsa.pub */ snmp_log(LOG_ERR, "You must specify a ssh public key file to use. See the snmp.conf manual page\n"); netsnmp_transport_free(t); return NULL; } /* use the requested private key file */ keyfilepriv = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_SSH_PRIVKEY); if (!keyfilepriv || 0 == *keyfilepriv) { /* XXX: default to keyfilepub without the .pub suffix */ snmp_log(LOG_ERR, "You must specify a ssh private key file to use. See the snmp.conf manual page\n"); netsnmp_transport_free(t); return NULL; } /* xxx: need an ipv6 friendly one too (sigh) */ /* XXX: not ideal when structs don't actually match size wise */ memcpy(&(addr_pair->remote_addr), addr, sizeof(struct sockaddr_in)); t->sock = socket(PF_INET, SOCK_STREAM, 0); if (t->sock < 0) { netsnmp_transport_free(t); return NULL; } t->remote_length = sizeof(*addr); t->remote = netsnmp_memdup(addr, sizeof(*addr)); if (!t->remote) { netsnmp_ssh_close(t); netsnmp_transport_free(t); return NULL; } /* * This is a client-type session, so attempt to connect to the far * end. We don't go non-blocking here because it's not obvious what * you'd then do if you tried to do snmp_sends before the connection * had completed. So this can block. */ rc = connect(t->sock, addr, sizeof(struct sockaddr)); if (rc < 0) { netsnmp_ssh_close(t); netsnmp_transport_free(t); return NULL; } /* * Allow user to override the send and receive buffers. Default is * to use os default. Don't worry too much about errors -- * just plough on regardless. */ netsnmp_sock_buffer_set(t->sock, SO_SNDBUF, local, 0); netsnmp_sock_buffer_set(t->sock, SO_RCVBUF, local, 0); /* open the SSH session and channel */ addr_pair->session = libssh2_session_init(); if (libssh2_session_startup(addr_pair->session, t->sock)) { shutdown: snmp_log(LOG_ERR, "Failed to establish an SSH session\n"); netsnmp_ssh_close(t); netsnmp_transport_free(t); return NULL; } /* At this point we havn't authenticated, The first thing to do is check the hostkey's fingerprint against our known hosts Your app may have it hard coded, may go to a file, may present it to the user, that's your call */ fingerprint = libssh2_hostkey_hash(addr_pair->session, LIBSSH2_HOSTKEY_HASH_MD5); DEBUGMSGTL(("ssh", "Fingerprint: ")); for(i = 0; i < 16; i++) { DEBUGMSG(("ssh", "%02x", (unsigned char)fingerprint[i])); } DEBUGMSG(("ssh", "\n")); /* check what authentication methods are available */ userauthlist = libssh2_userauth_list(addr_pair->session, username, strlen(username)); DEBUGMSG(("ssh", "Authentication methods: %s\n", userauthlist)); /* XXX: allow other types */ /* XXX: 4 seems magic to me... */ if (strstr(userauthlist, "publickey") != NULL) { auth_pw |= 4; } /* XXX: hard coded paths and users */ if (auth_pw & 4) { /* public key */ if (libssh2_userauth_publickey_fromfile(addr_pair->session, username, keyfilepub, keyfilepriv, NULL)) { snmp_log(LOG_ERR,"Authentication by public key failed!\n"); goto shutdown; } else { DEBUGMSG(("ssh", "\tAuthentication by public key succeeded.\n")); } } else { snmp_log(LOG_ERR,"Authentication by public key failed!\n"); goto shutdown; } /* we've now authenticated both sides; contining onward ... */ /* Request a channel */ if (!(addr_pair->channel = libssh2_channel_open_session(addr_pair->session))) { snmp_log(LOG_ERR, "Unable to open a session\n"); goto shutdown; } /* Request a terminal with 'vanilla' terminal emulation * See /etc/termcap for more options */ /* XXX: needed? doubt it */ /* if (libssh2_channel_request_pty(addr_pair->channel, "vanilla")) { */ /* snmp_log(LOG_ERR, "Failed requesting pty\n"); */ /* goto shutdown; */ /* } */ if (libssh2_channel_subsystem(addr_pair->channel, "snmp")) { snmp_log(LOG_ERR, "Failed to request the ssh 'snmp' subsystem\n"); goto shutdown; } } DEBUGMSG(("ssh","Opened connection.\n")); /* * Message size is not limited by this transport (hence msgMaxSize * is equal to the maximum legal size of an SNMP message). */ t->msgMaxSize = SNMP_MAX_PACKET_LEN; t->f_recv = netsnmp_ssh_recv; t->f_send = netsnmp_ssh_send; t->f_close = netsnmp_ssh_close; t->f_accept = netsnmp_ssh_accept; t->f_fmtaddr = netsnmp_ssh_fmtaddr; return t; }
int main(int argc, char *argv[]) { int rc, i, auth = AUTH_NONE; struct sockaddr_in sin; const char *fingerprint; char *userauthlist; LIBSSH2_SESSION *session; LIBSSH2_CHANNEL *channel = NULL; char buf[1048576]; /* avoid any buffer reallocation for simplicity */ ssize_t len; #ifdef WIN32 SOCKET sock = INVALID_SOCKET; WSADATA wsadata; int err; err = WSAStartup(MAKEWORD(2,0), &wsadata); if (err != 0) { fprintf(stderr, "WSAStartup failed with error: %d\n", err); return 1; } #else int sock = -1; #endif if (argc > 1) server_ip = argv[1]; if (argc > 2) username = argv[2]; if (argc > 3) password = argv[3]; rc = libssh2_init (0); if (rc != 0) { fprintf (stderr, "libssh2 initialization failed (%d)\n", rc); return 1; } /* Connect to SSH server */ sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); #ifdef WIN32 if (sock == INVALID_SOCKET) { fprintf(stderr, "failed to open socket!\n"); return -1; } #else if (sock == -1) { perror("socket"); return -1; } #endif sin.sin_family = AF_INET; if (INADDR_NONE == (sin.sin_addr.s_addr = inet_addr(server_ip))) { fprintf(stderr, "inet_addr: Invalid IP address \"%s\"\n", server_ip); return -1; } sin.sin_port = htons(830); if (connect(sock, (struct sockaddr*)(&sin), sizeof(struct sockaddr_in)) != 0) { fprintf(stderr, "Failed to connect to %s!\n", inet_ntoa(sin.sin_addr)); return -1; } /* Create a session instance */ session = libssh2_session_init(); if(!session) { fprintf(stderr, "Could not initialize SSH session!\n"); return -1; } /* ... start it up. This will trade welcome banners, exchange keys, * and setup crypto, compression, and MAC layers */ rc = libssh2_session_handshake(session, sock); if(rc) { fprintf(stderr, "Error when starting up SSH session: %d\n", rc); return -1; } /* At this point we havn't yet authenticated. The first thing to do * is check the hostkey's fingerprint against our known hosts Your app * may have it hard coded, may go to a file, may present it to the * user, that's your call */ fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1); fprintf(stderr, "Fingerprint: "); for(i = 0; i < 20; i++) fprintf(stderr, "%02X ", (unsigned char)fingerprint[i]); fprintf(stderr, "\n"); /* check what authentication methods are available */ userauthlist = libssh2_userauth_list(session, username, strlen(username)); fprintf(stderr, "Authentication methods: %s\n", userauthlist); if (strstr(userauthlist, "password")) auth |= AUTH_PASSWORD; if (strstr(userauthlist, "publickey")) auth |= AUTH_PUBLICKEY; /* check for options */ if(argc > 4) { if ((auth & AUTH_PASSWORD) && !strcasecmp(argv[4], "-p")) auth = AUTH_PASSWORD; if ((auth & AUTH_PUBLICKEY) && !strcasecmp(argv[4], "-k")) auth = AUTH_PUBLICKEY; } if (auth & AUTH_PASSWORD) { if (libssh2_userauth_password(session, username, password)) { fprintf(stderr, "Authentication by password failed.\n"); goto shutdown; } } else if (auth & AUTH_PUBLICKEY) { if (libssh2_userauth_publickey_fromfile(session, username, keyfile1, keyfile2, password)) { fprintf(stderr, "Authentication by public key failed!\n"); goto shutdown; } fprintf(stderr, "Authentication by public key succeeded.\n"); } else { fprintf(stderr, "No supported authentication methods found!\n"); goto shutdown; } /* open a channel */ channel = libssh2_channel_open_session(session); if (!channel) { fprintf(stderr, "Could not open the channel!\n" "(Note that this can be a problem at the server!" " Please review the server logs.)\n"); goto shutdown; } /* execute the subsystem on our channel */ if (libssh2_channel_subsystem(channel, "netconf")) { fprintf(stderr, "Could not execute the \"netconf\" subsystem!\n" "(Note that this can be a problem at the server!" " Please review the server logs.)\n"); goto shutdown; } /* NETCONF: http://tools.ietf.org/html/draft-ietf-netconf-ssh-06 */ fprintf(stderr, "Sending NETCONF client <hello>\n"); snprintf(buf, sizeof(buf), "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" "<hello>" "<capabilities>" "<capability>urn:ietf:params:xml:ns:netconf:base:1.0</capability>" "</capabilities>" "</hello>\n" "]]>]]>\n%n", (int *)&len); if (-1 == netconf_write(channel, buf, len)) goto shutdown; fprintf(stderr, "Reading NETCONF server <hello>\n"); len = netconf_read_until(channel, "</hello>", buf, sizeof(buf)); if (-1 == len) goto shutdown; fprintf(stderr, "Got %d bytes:\n----------------------\n%s", (int)len, buf); fprintf(stderr, "Sending NETCONF <rpc>\n"); snprintf(buf, sizeof(buf), "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" "<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" "<get-interface-information><terse/></get-interface-information>" "</rpc>\n" "]]>]]>\n%n", (int *)&len); if (-1 == netconf_write(channel, buf, len)) goto shutdown; fprintf(stderr, "Reading NETCONF <rpc-reply>\n"); len = netconf_read_until(channel, "</rpc-reply>", buf, sizeof(buf)); if (-1 == len) goto shutdown; fprintf(stderr, "Got %d bytes:\n----------------------\n%s", (int)len, buf); shutdown: if (channel) libssh2_channel_free(channel); libssh2_session_disconnect(session, "Client disconnecting normally"); libssh2_session_free(session); #ifdef WIN32 closesocket(sock); #else close(sock); #endif libssh2_exit(); return 0; }