X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=common-src%2Fssh-security.c;h=bcd1d98658a16be53147268630342881ac5facd2;hb=HEAD;hp=c0b023c86aa79a4819163fcb89a1a9ae752e52de;hpb=12179dea039515c06168c0037d048566a3f623de;p=debian%2Famanda diff --git a/common-src/ssh-security.c b/common-src/ssh-security.c index c0b023c..bcd1d98 100644 --- a/common-src/ssh-security.c +++ b/common-src/ssh-security.c @@ -37,38 +37,10 @@ #include "util.h" #include "event.h" #include "packet.h" -#include "queue.h" #include "security.h" #include "security-util.h" +#include "sockaddr-util.h" #include "stream.h" -#include "version.h" - -#ifdef SSH_SECURITY - -/*#define SSH_DEBUG*/ - -#ifdef SSH_DEBUG -int ssh_debug = 1; -#else -int ssh_debug = 0; -#endif - -#define sshprintf(x) \ - do { \ - if (ssh_debug) { \ - dbprintf(x); \ - } \ - } while (0) - -/* - * Path to the ssh binary. This should be configurable. - */ -#define SSH_PATH "/usr/bin/ssh" - -/* - * Arguments to ssh. This should also be configurable - */ -#define SSH_ARGS "-x", "-o", "BatchMode=yes", "-o", "PreferredAuthentications=publickey" /* * Number of seconds ssh has to start up @@ -87,6 +59,10 @@ int ssh_debug = 0; static void ssh_connect(const char *, char *(*)(char *, void *), void (*)(void *, security_handle_t *, security_status_t), void *, void *); +static void ssh_accept(const security_driver_t *driver, char *(*conf_fn)(char *, void *), + int in, int out, + void (*fn)(security_handle_t *, pkt_t *), + void *datap); /* * This is our interface to the outside world. @@ -94,7 +70,8 @@ static void ssh_connect(const char *, char *(*)(char *, void *), const security_driver_t ssh_security_driver = { "SSH", ssh_connect, - sec_accept, + ssh_accept, + sec_get_authenticated_peer_name_hostname, sec_close, stream_sendpkt, stream_recvpkt, @@ -110,6 +87,8 @@ const security_driver_t ssh_security_driver = { tcpm_stream_read_sync, tcpm_stream_read_cancel, tcpm_close_connection, + NULL, + NULL }; static int newhandle = 1; @@ -117,7 +96,8 @@ static int newhandle = 1; /* * Local functions */ -static int runssh(struct tcp_conn *, const char *, const char *, const char *); +static int runssh(struct tcp_conn *, const char *, const char *, const char *, + const char *); /* * ssh version of a security handle allocator. Logically sets @@ -131,33 +111,36 @@ ssh_connect( void * arg, void * datap) { + int result; struct sec_handle *rh; - struct hostent *he; char *amandad_path=NULL, *client_username=NULL, *ssh_keys=NULL; + char *client_port = NULL; assert(fn != NULL); assert(hostname != NULL); - (void)conf_fn; /* Quiet unused parameter warning */ - - sshprintf(("%s: ssh: ssh_connect: %s\n", debug_prefix_time(NULL), - hostname)); + auth_debug(1, "ssh_connect: %s\n", hostname); - rh = alloc(SIZEOF(*rh)); + rh = g_new0(struct sec_handle, 1); security_handleinit(&rh->sech, &ssh_security_driver); rh->hostname = NULL; rh->rs = NULL; rh->ev_timeout = NULL; rh->rc = NULL; - if ((he = gethostbyname(hostname)) == NULL) { + /* get the canonical hostname */ + rh->hostname = NULL; + if ((result = resolve_hostname(hostname, 0, NULL, &rh->hostname)) != 0 + || rh->hostname == NULL) { security_seterror(&rh->sech, - "%s: ssh could not resolve hostname", hostname); + _("ssh_security could not find canonical name for '%s': %s"), + hostname, gai_strerror(result)); (*fn)(arg, &rh->sech, S_ERROR); return; } - rh->hostname = stralloc(he->h_name); /* will be replaced */ rh->rs = tcpma_stream_client(rh, newhandle++); + rh->rc->conf_fn = conf_fn; + rh->rc->datap = datap; if (rh->rs == NULL) goto error; @@ -171,13 +154,19 @@ ssh_connect( * XXX need to eventually limit number of outgoing connections here. */ if(conf_fn) { + char *port_str; amandad_path = conf_fn("amandad_path", datap); client_username = conf_fn("client_username", datap); ssh_keys = conf_fn("ssh_keys", datap); + port_str = conf_fn("client_port", datap); + if (port_str && strlen(port_str) >= 1) { + client_port = port_str; + } } if(rh->rc->read == -1) { - if (runssh(rh->rs->rc, amandad_path, client_username, ssh_keys) < 0) { - security_seterror(&rh->sech, "can't connect to %s: %s", + if (runssh(rh->rs->rc, amandad_path, client_username, ssh_keys, + client_port) < 0) { + security_seterror(&rh->sech, _("can't connect to %s: %s"), hostname, rh->rs->rc->errmsg); goto error; } @@ -205,6 +194,99 @@ error: (*fn)(arg, &rh->sech, S_ERROR); } +/* like sec_accept, but first it gets the remote system's hostname */ +static void +ssh_accept( + const security_driver_t *driver, + char *(*conf_fn)(char *, void *), + int in, + int out, + void (*fn)(security_handle_t *, pkt_t *), + void *datap) +{ + struct sec_handle *rh; + struct tcp_conn *rc = sec_tcp_conn_get("", 0); + char *ssh_connection, *p; + char *errmsg = NULL; + sockaddr_union addr; + int result; + + /* "Accepting" an SSH connection means that amandad was invoked via sshd, so + * we should have anSSH_CONNECTION env var. If not, then this probably isn't + * a real SSH connection and we should bail out. */ + ssh_connection = getenv("SSH_CONNECTION"); + if (!ssh_connection) { + errmsg = g_strdup("$SSH_CONNECTION not set - was amandad started by sshd?"); + goto error; + } + + /* make a local copy, to munge */ + ssh_connection = g_strdup(ssh_connection); + + /* strip off the first component - the ASCII IP address */ + if ((p = strchr(ssh_connection, ' ')) == NULL) { + errmsg = g_strdup("$SSH_CONNECTION malformed"); + goto error; + } + *p = '\0'; + + /* ---- everything from here on is just a warning, leaving hostname at "" */ + + SU_INIT(&addr, AF_INET); + + /* turn the string address into a sockaddr */ + if ((result = str_to_sockaddr(ssh_connection, &addr)) != 1) { + if (result == 0) { + g_warning("Could not parse peer address %s", ssh_connection); + } else { + g_warning("Parsing peer address %s: %s", ssh_connection, gai_strerror(result)); + } + goto done; + } + + /* find the hostname */ + result = getnameinfo((struct sockaddr *)&addr, SS_LEN(&addr), + rc->hostname, sizeof(rc->hostname), NULL, 0, 0); + if (result != 0) { + g_warning("Could not get hostname for SSH client %s: %s", ssh_connection, + gai_strerror(result)); + goto done; + } + + /* and verify it */ + if (check_name_give_sockaddr(rc->hostname, + (struct sockaddr *)&addr, &errmsg) < 0) { + rc->hostname[0] = '\0'; /* null out the bad hostname */ + g_warning("Checking SSH client DNS: %s", errmsg); + amfree(errmsg); + goto done; + } + +done: + if (ssh_connection) + g_free(ssh_connection); + + rc->read = in; + rc->write = out; + rc->accept_fn = fn; + rc->driver = driver; + rc->conf_fn = conf_fn; + rc->datap = datap; + sec_tcp_conn_read(rc); + return; + +error: + if (ssh_connection) + g_free(ssh_connection); + + /* make up a fake handle for the error */ + rh = g_new0(struct sec_handle, 1); + security_handleinit(&rh->sech, driver); + security_seterror((security_handle_t*)rh, "ssh_accept: %s", errmsg); + amfree(errmsg); + (*fn)(&rh->sech, NULL); +} + /* * Forks a ssh to the host listed in rc->hostname * Returns negative on error, with an errmsg in rc->errmsg. @@ -214,23 +296,60 @@ runssh( struct tcp_conn * rc, const char * amandad_path, const char * client_username, - const char * ssh_keys) + const char * ssh_keys, + const char * client_port) { int rpipe[2], wpipe[2]; char *xamandad_path = (char *)amandad_path; char *xclient_username = (char *)client_username; char *xssh_keys = (char *)ssh_keys; + char *xclient_port = (char *)client_port; + GPtrArray *myargs; + gchar *ssh_options[100] = {SSH_OPTIONS, NULL}; + gchar **ssh_option; + gchar *cmd; memset(rpipe, -1, SIZEOF(rpipe)); memset(wpipe, -1, SIZEOF(wpipe)); if (pipe(rpipe) < 0 || pipe(wpipe) < 0) { - rc->errmsg = newvstralloc(rc->errmsg, "pipe: ", strerror(errno), NULL); + rc->errmsg = newvstrallocf(rc->errmsg, _("pipe: %s"), strerror(errno)); return (-1); } + if(!xamandad_path || strlen(xamandad_path) <= 1) + xamandad_path = vstralloc(amlibexecdir, "/", "amandad", NULL); + if(!xclient_username || strlen(xclient_username) <= 1) + xclient_username = CLIENT_LOGIN; + if(!xclient_port || strlen(xclient_port) <= 1) + xclient_port = NULL; + + myargs = g_ptr_array_sized_new(20); + g_ptr_array_add(myargs, SSH); + for (ssh_option = ssh_options; *ssh_option != NULL; ssh_option++) { + g_ptr_array_add(myargs, *ssh_option); + } + g_ptr_array_add(myargs, "-l"); + g_ptr_array_add(myargs, xclient_username); + if (xclient_port) { + g_ptr_array_add(myargs, "-p"); + g_ptr_array_add(myargs, xclient_port); + } + if (ssh_keys && strlen(ssh_keys) > 1) { + g_ptr_array_add(myargs, "-i"); + g_ptr_array_add(myargs, xssh_keys); + } + g_ptr_array_add(myargs, rc->hostname); + g_ptr_array_add(myargs, xamandad_path); + g_ptr_array_add(myargs, "-auth=ssh"); + g_ptr_array_add(myargs, NULL); + + cmd = g_strjoinv(" ", (gchar **)myargs->pdata); + g_debug("exec: %s", cmd); + g_free(cmd); + switch (rc->pid = fork()) { case -1: - rc->errmsg = newvstralloc(rc->errmsg, "fork: ", strerror(errno), NULL); + rc->errmsg = newvstrallocf(rc->errmsg, _("fork: %s"), strerror(errno)); aclose(rpipe[0]); aclose(rpipe[1]); aclose(wpipe[0]); @@ -248,27 +367,15 @@ runssh( return (0); } + /* drop root privs for good */ + set_root_privs(-1); + safe_fd(-1, 0); - if(!xamandad_path || strlen(xamandad_path) <= 1) - xamandad_path = vstralloc(libexecdir, "/", "amandad", - versionsuffix(), NULL); - if(!xclient_username || strlen(xclient_username) <= 1) - xclient_username = CLIENT_LOGIN; - if(!ssh_keys || strlen(ssh_keys) <= 1) { - execlp(SSH_PATH, SSH_PATH, SSH_ARGS, "-l", xclient_username, - rc->hostname, xamandad_path, "-auth=ssh", "amdump", "amindexd", - "amidxtaped", (char *)NULL); - } - else { - execlp(SSH_PATH, SSH_PATH, SSH_ARGS, "-l", xclient_username, - "-i", xssh_keys, rc->hostname, xamandad_path, "-auth=ssh", - "amdump", "amindexd", "amidxtaped", (char *)NULL); - } - error("error: couldn't exec %s: %s", SSH_PATH, strerror(errno)); + execvp(SSH, (gchar **)myargs->pdata); + + error("error: couldn't exec %s: %s", SSH, strerror(errno)); /* should never go here, shut up compiler warning */ return(-1); } - -#endif /* SSH_SECURITY */