diff options
Diffstat (limited to 'remote/ssh-conn.c')
-rw-r--r-- | remote/ssh-conn.c | 338 |
1 files changed, 338 insertions, 0 deletions
diff --git a/remote/ssh-conn.c b/remote/ssh-conn.c new file mode 100644 index 00000000..7f099d35 --- /dev/null +++ b/remote/ssh-conn.c @@ -0,0 +1,338 @@ +/* + Remote Client + HardInfo - Displays System Information + Copyright (C) 2003-2009 Leandro A. F. Pereira <leandro@hardinfo.org> + + Based on ssh-method.c from GnomeVFS + Copyright (C) 1999 Free Software Foundation + Original author: Ian McKellar <yakk@yakk.net> + + ssh-conn.c and ssh-conn.h are free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + ssh-conn.c and ssh-con.h are distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with ssh-conn.c and ssh-conn.h; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include "config.h" +#ifdef HAS_LIBSOUP +#include <glib/gstdio.h> +#include <glib.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/wait.h> + +#include "ssh-conn.h" + +const char *ssh_conn_errors[] = { + "OK", + "No URI specified", + "Unknown protocol", + "Unknown error", + "Cannot spawn SSH", + "Bad parameters", + "Permission denied (invalid credentials)", + "Host key verification failed", + "Connection refused", + "Invalid username or password" +}; + +int ssh_write(SSHConn * conn, + gconstpointer buffer, gint num_bytes, gint * bytes_written) +{ + int written; + int count = 0; + + do { + written = write(conn->fd_write, buffer, (size_t) num_bytes); + if (written == -1 && errno == EINTR) { + count++; + usleep(10); + } + } while (written == -1 && errno == EINTR && count < 5); + + if (written == -1) { + return -1; + } + + *bytes_written = written; + + return 1; +} + +int ssh_read(gint fd, gpointer buffer, gint num_bytes, gint * bytes_read) +{ + int retval; + fd_set fds; + struct timeval tv = { 5, 0 }; + + FD_ZERO(&fds); + FD_SET(fd, &fds); + + *bytes_read = 0; + + if ((retval = select(fd + 1, &fds, NULL, NULL, &tv)) < 0) { + return retval; + } + + if ((retval = read(fd, buffer, (size_t) num_bytes)) > 0) { + *bytes_read = retval; + return 1; + } + + return retval; +} + +void ssh_close(SSHConn * conn) +{ + if (!conn) { + return; + } + + close(conn->fd_read); + close(conn->fd_write); + close(conn->fd_error); + + if (conn->uri) { + soup_uri_free(conn->uri); + } + + kill(conn->pid, SIGINT); + + if (conn->askpass_path) { + DEBUG("unlinking %s", conn->askpass_path); + g_remove(conn->askpass_path); + g_free(conn->askpass_path); + } + + g_free(conn); +} + +static void ssh_conn_setup(gpointer user_data) +{ + gchar *askpass_path = (gchar *) user_data; + int fd; + + if ((fd = open("/dev/tty", O_RDWR)) != -1) { + ioctl(fd, TIOCNOTTY, NULL); + close(fd); + } + + if (askpass_path) { + g_setenv("DISPLAY", "none:0.", TRUE); + g_setenv("SSH_ASKPASS", askpass_path, TRUE); + } else { + g_setenv("SSH_ASKPASS", "/bin/false", TRUE); + } +} + +SSHConnResponse ssh_new(SoupURI * uri, + SSHConn ** conn_return, gchar * command) +{ + int argc, res, bytes_read; + char **argv, *askpass_path = NULL; + GString *cmd_line; + SSHConnResponse response; + SSHConn *connection; + gchar buffer[512]; + + if (!conn_return) { + return SSH_CONN_BAD_PARAMS; + } + + if (!uri) { + return SSH_CONN_NO_URI; + } + + if (!g_str_equal(uri->scheme, "ssh")) { + return SSH_CONN_UNKNOWN_PROTOCOL; + } + + if (uri->password) { + int tmp_askpass; + + askpass_path = + g_build_filename(g_get_home_dir(), ".hardinfo", + "ssh-askpass-XXXXXX", NULL); + tmp_askpass = g_mkstemp(askpass_path); + if (tmp_askpass > 0) { + gchar *tmp; + + g_chmod(askpass_path, 0700); + + tmp = g_strdup_printf("#!/bin/sh\n" + "echo '%s'\n" + "rm -f \"$0\"", uri->password); + write(tmp_askpass, tmp, strlen(tmp)); + close(tmp_askpass); + g_free(tmp); + + DEBUG("using [%s] as ssh-askpass", askpass_path); + } + } + + + cmd_line = g_string_new("ssh -x"); + + if (uri->query && strlen(uri->query)) { + GHashTable *query; + GList *keys, *key; + + query = soup_form_decode(uri->query); + keys = g_hash_table_get_keys(query); + + for (key = keys; key; key = key->next) { + gchar *param; + + g_string_append_printf(cmd_line, " -%s", (gchar *) key->data); + + if ((param = (gchar *) g_hash_table_lookup(query, key->data))) { + g_string_append_printf(cmd_line, "'%s'", param); + } + } + + g_list_free(keys); + g_hash_table_destroy(query); + } + + if (uri->user) { + g_string_append_printf(cmd_line, " -l '%s'", uri->user); + } + + if (uri->port) { + g_string_append_printf(cmd_line, " -p %d", uri->port); + } + + g_string_append_printf(cmd_line, + " %s \"LC_ALL=C %s\"", uri->host, command); + + DEBUG("cmd_line = [%s]", cmd_line->str); + + if (!g_shell_parse_argv(cmd_line->str, &argc, &argv, NULL)) { + response = SSH_CONN_BAD_PARAMS; + goto end; + } + + connection = g_new0(SSHConn, 1); + connection->exit_status = -1; + + DEBUG("spawning SSH"); + + if (!g_spawn_async_with_pipes(NULL, argv, NULL, + G_SPAWN_SEARCH_PATH, + ssh_conn_setup, askpass_path, + &connection->pid, + &connection->fd_write, + &connection->fd_read, + &connection->fd_error, NULL)) { + response = SSH_CONN_CANNOT_SPAWN_SSH; + goto end; + } + + memset(buffer, 0, sizeof(buffer)); + res = ssh_read(connection->fd_error, &buffer, sizeof(buffer), + &bytes_read); + DEBUG("bytes read: %d, result = %d", bytes_read, res); + if (bytes_read > 0 && res == 1) { + DEBUG("Received (error channel): [%s]", buffer); + + if (strstr(buffer, "Permission denied")) { + response = SSH_CONN_PERMISSION_DENIED; + goto end; + } else if (strstr(buffer, "Host key verification failed")) { + response = SSH_CONN_HOST_KEY_CHECK_FAIL; + goto end; + } else if (strstr(buffer, "Connection refused")) { + response = SSH_CONN_REFUSED; + goto end; + } + } + + DEBUG("no error detected; ssh conn established"); + + connection->uri = soup_uri_copy(uri); + response = SSH_CONN_OK; + + end: + g_strfreev(argv); + g_string_free(cmd_line, TRUE); + + if (askpass_path) { + if (connection) { + connection->askpass_path = askpass_path; + } else { + g_free(askpass_path); + } + } + + if (response != SSH_CONN_OK) { + if (connection->uri) { + soup_uri_free(connection->uri); + } + + g_free(connection); + + *conn_return = NULL; + } else { + *conn_return = connection; + } + + DEBUG("response = %d (%s)", response, ssh_conn_errors[response]); + + return response; +} + +#ifdef SSH_TEST +int main(int argc, char **argv) +{ + SSHConn *c; + SSHConnResponse r; + SoupURI *u; + char buffer[256]; + + if (argc < 2) { + g_print("Usage: %s URI command\n", argv[0]); + g_print("Example: %s ssh://user:password@host 'ls -la /'\n", + argv[0]); + return 1; + } + + u = soup_uri_new(argv[1]); + r = ssh_new(u, &c, argv[2]); + g_print("Connection result: %s\n", ssh_conn_errors[r]); + + if (r == SSH_CONN_OK) { + int bytes_read; + + while (ssh_read(c->fd_read, &buffer, sizeof(buffer), + &bytes_read) > 0) { + g_print("Bytes read: %d\n", bytes_read); + g_print("Contents: %s", buffer); + } + + g_print("Finished running remote command\n"); + } + + g_print("Closing SSH [ptr = %p]", c); + ssh_close(c); + + return 0; +} +#endif /* SSH_TEST */ +#endif /* HAS_LIBSOUP */ |