lintian doesn't like orphan packages with uploaders...
[debian/amanda] / common-src / ssh-security.c
index 19d60692792725905a82e3e4465555201db8fa1c..bcd1d98658a16be53147268630342881ac5facd2 100644 (file)
@@ -39,6 +39,7 @@
 #include "packet.h"
 #include "security.h"
 #include "security-util.h"
+#include "sockaddr-util.h"
 #include "stream.h"
 
 /*
 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.
@@ -65,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,
@@ -90,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
@@ -107,13 +114,14 @@ ssh_connect(
     int result;
     struct sec_handle *rh;
     char *amandad_path=NULL, *client_username=NULL, *ssh_keys=NULL;
+    char *client_port = NULL;
 
     assert(fn != NULL);
     assert(hostname != NULL);
 
     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;
@@ -146,12 +154,18 @@ 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) {
+       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;
@@ -180,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.
@@ -189,12 +296,18 @@ 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));
@@ -203,6 +316,37 @@ runssh(
        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 = newvstrallocf(rc->errmsg, _("fork: %s"), strerror(errno));
@@ -228,20 +372,8 @@ runssh(
 
     safe_fd(-1, 0);
 
-    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(!ssh_keys || strlen(ssh_keys) <= 1) {
-       execlp(SSH, SSH, SSH_OPTIONS, "-l", xclient_username,
-              rc->hostname, xamandad_path, "-auth=ssh", "amdump", "amindexd",
-              "amidxtaped", (char *)NULL);
-    }
-    else {
-       execlp(SSH, SSH, SSH_OPTIONS, "-l", xclient_username,
-              "-i", xssh_keys, rc->hostname, xamandad_path, "-auth=ssh",
-              "amdump", "amindexd", "amidxtaped", (char *)NULL);
-    }
+    execvp(SSH, (gchar **)myargs->pdata);
+
     error("error: couldn't exec %s: %s", SSH, strerror(errno));
 
     /* should never go here, shut up compiler warning */