static int netsnmp_tcp6_accept(netsnmp_transport *t) { struct sockaddr_in6 *farend = NULL; int newsock = -1; socklen_t farendlen = sizeof(struct sockaddr_in6); char *str = NULL; farend = (struct sockaddr_in6 *) malloc(sizeof(struct sockaddr_in6)); if (farend == NULL) { /* * Indicate that the acceptance of this socket failed. */ DEBUGMSGTL(("netsnmp_tcp6", "accept: malloc failed\n")); return -1; } if (t != NULL && t->sock >= 0) { newsock = (int) accept(t->sock, (struct sockaddr *) farend, &farendlen); if (newsock < 0) { DEBUGMSGTL(("netsnmp_tcp6","accept failed rc %d errno %d \"%s\"\n", newsock, errno, strerror(errno))); free(farend); return newsock; } if (t->data != NULL) { free(t->data); } t->data = farend; t->data_length = farendlen; str = netsnmp_tcp6_fmtaddr(NULL, farend, farendlen); DEBUGMSGTL(("netsnmp_tcp6", "accept succeeded (from %s)\n", str)); free(str); /* * Try to make the new socket blocking. */ if (netsnmp_set_non_blocking_mode(newsock, FALSE) < 0) DEBUGMSGTL(("netsnmp_tcp6", "accept: couldn't f_getfl of fd %d\n", newsock)); /* * 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(newsock, SO_SNDBUF, 1, 0); netsnmp_sock_buffer_set(newsock, SO_RCVBUF, 1, 0); return newsock; } else { free(farend); return -1; } }
static int netsnmp_unix_accept(netsnmp_transport *t) { struct sockaddr *farend = NULL; int newsock = -1; socklen_t farendlen = sizeof(struct sockaddr_un); farend = (struct sockaddr *) malloc(farendlen); if (farend == NULL) { /* * Indicate that the acceptance of this socket failed. */ DEBUGMSGTL(("netsnmp_unix", "accept: malloc failed\n")); return -1; } memset(farend, 0, farendlen); if (t != NULL && t->sock >= 0) { newsock = accept(t->sock, farend, &farendlen); if (newsock < 0) { DEBUGMSGTL(("netsnmp_unix","accept failed rc %d errno %d \"%s\"\n", newsock, errno, strerror(errno))); free(farend); return newsock; } if (t->data != NULL) { free(t->data); } DEBUGMSGTL(("netsnmp_unix", "accept succeeded (farend %p len %d)\n", farend, farendlen)); t->data = farend; t->data_length = sizeof(struct sockaddr_un); netsnmp_sock_buffer_set(newsock, SO_SNDBUF, 1, 0); netsnmp_sock_buffer_set(newsock, SO_RCVBUF, 1, 0); return newsock; } else { free(farend); return -1; } }
netsnmp_transport * netsnmp_unix_transport(struct sockaddr_un *addr, int local) { netsnmp_transport *t = NULL; sockaddr_un_pair *sup = NULL; int rc = 0; char *string = NULL; if (addr == NULL || addr->sun_family != AF_UNIX) { return NULL; } t = (netsnmp_transport *) malloc(sizeof(netsnmp_transport)); if (t == NULL) { return NULL; } string = netsnmp_unix_fmtaddr(NULL, (void *)addr, sizeof(struct sockaddr_un)); DEBUGMSGTL(("netsnmp_unix", "open %s %s\n", local ? "local" : "remote", string)); free(string); memset(t, 0, sizeof(netsnmp_transport)); t->domain = netsnmp_UnixDomain; t->domain_length = sizeof(netsnmp_UnixDomain) / sizeof(netsnmp_UnixDomain[0]); t->data = malloc(sizeof(sockaddr_un_pair)); if (t->data == NULL) { netsnmp_transport_free(t); return NULL; } memset(t->data, 0, sizeof(sockaddr_un_pair)); t->data_length = sizeof(sockaddr_un_pair); sup = (sockaddr_un_pair *) t->data; t->sock = socket(PF_UNIX, SOCK_STREAM, 0); if (t->sock < 0) { netsnmp_transport_free(t); return NULL; } t->flags = NETSNMP_TRANSPORT_FLAG_STREAM; if (local) { t->local = (u_char *)malloc(strlen(addr->sun_path)); if (t->local == NULL) { netsnmp_transport_free(t); return NULL; } memcpy(t->local, addr->sun_path, strlen(addr->sun_path)); t->local_length = strlen(addr->sun_path); /* * This session is inteneded as a server, so we must bind to the given * path (unlinking it first, to avoid errors). */ t->flags |= NETSNMP_TRANSPORT_FLAG_LISTEN; unlink(addr->sun_path); rc = bind(t->sock, (struct sockaddr *) addr, SUN_LEN(addr)); if (rc != 0) { DEBUGMSGTL(("netsnmp_unix_transport", "couldn't bind \"%s\", errno %d (%s)\n", addr->sun_path, errno, strerror(errno))); netsnmp_unix_close(t); netsnmp_transport_free(t); return NULL; } /* * Save the address in the transport-specific data pointer for later * use by netsnmp_unix_close. */ sup->server.sun_family = AF_UNIX; strcpy(sup->server.sun_path, addr->sun_path); sup->local = 1; /* * Now sit here and listen for connections to arrive. */ rc = listen(t->sock, NETSNMP_STREAM_QUEUE_LEN); if (rc != 0) { DEBUGMSGTL(("netsnmp_unix_transport", "couldn't listen to \"%s\", errno %d (%s)\n", addr->sun_path, errno, strerror(errno))); netsnmp_unix_close(t); netsnmp_transport_free(t); return NULL; } } else { t->remote = (u_char *)malloc(strlen(addr->sun_path)); if (t->remote == NULL) { netsnmp_transport_free(t); return NULL; } memcpy(t->remote, addr->sun_path, strlen(addr->sun_path)); t->remote_length = strlen(addr->sun_path); rc = connect(t->sock, (struct sockaddr *) addr, sizeof(struct sockaddr_un)); if (rc != 0) { DEBUGMSGTL(("netsnmp_unix_transport", "couldn't connect to \"%s\", errno %d (%s)\n", addr->sun_path, errno, strerror(errno))); netsnmp_unix_close(t); netsnmp_transport_free(t); return NULL; } /* * Save the remote address in the transport-specific data pointer for * later use by netsnmp_unix_send. */ sup->server.sun_family = AF_UNIX; strcpy(sup->server.sun_path, addr->sun_path); sup->local = 0; netsnmp_sock_buffer_set(t->sock, SO_SNDBUF, local, 0); netsnmp_sock_buffer_set(t->sock, SO_RCVBUF, local, 0); } /* * Message size is not limited by this transport (hence msgMaxSize * is equal to the maximum legal size of an SNMP message). */ t->msgMaxSize = 0x7fffffff; t->f_recv = netsnmp_unix_recv; t->f_send = netsnmp_unix_send; t->f_close = netsnmp_unix_close; t->f_accept = netsnmp_unix_accept; t->f_fmtaddr = netsnmp_unix_fmtaddr; return t; }
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; }
static int netsnmp_ssh_accept(netsnmp_transport *t) { #ifdef SNMPSSHDOMAIN_USE_EXTERNAL_PIPE /* much of this is duplicated from snmpUnixDomain.c */ netsnmp_ssh_addr_pair *addr_pair; int newsock = -1; struct sockaddr *farend = NULL; socklen_t farendlen = sizeof(struct sockaddr_un); if (t != NULL && t->sock >= 0) { addr_pair = SNMP_MALLOC_TYPEDEF(netsnmp_ssh_addr_pair); if (addr_pair == NULL) { /* * Indicate that the acceptance of this socket failed. */ DEBUGMSGTL(("ssh", "accept: malloc failed\n")); return -1; } farend = (struct sockaddr *) &addr_pair->unix_socket_end; newsock = accept(t->sock, farend, &farendlen); if (newsock < 0) { DEBUGMSGTL(("ssh","accept failed rc %d errno %d \"%s\"\n", newsock, errno, strerror(errno))); free(addr_pair); return newsock; } /* set the SO_PASSCRED option so we can receive the remote uid */ { int one = 1; setsockopt(newsock, SOL_SOCKET, SO_PASSCRED, (void *) &one, sizeof(one)); } if (t->data != NULL) { free(t->data); } DEBUGMSGTL(("ssh", "accept succeeded (farend %p len %d)\n", farend, farendlen)); t->data = addr_pair; t->data_length = sizeof(netsnmp_ssh_addr_pair); netsnmp_sock_buffer_set(newsock, SO_SNDBUF, 1, 0); netsnmp_sock_buffer_set(newsock, SO_RCVBUF, 1, 0); return newsock; } else { return -1; } #else /* we're called directly by sshd and use stdin/out */ /* we don't need to do anything; server side uses stdin/out */ /* XXX: check that we're an ssh connection */ return STDIN_FILENO; /* return stdin */ #endif /* ! SNMPSSHDOMAIN_USE_EXTERNAL_PIPE */ }