#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
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.
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,
tcpm_stream_read_sync,
tcpm_stream_read_cancel,
tcpm_close_connection,
+ NULL,
+ NULL
};
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
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 = "22";
assert(fn != NULL);
assert(hostname != NULL);
- (void)conf_fn; /* Quiet unused parameter warning */
+ auth_debug(1, "ssh_connect: %s\n", hostname);
- sshprintf(("%s: ssh: ssh_connect: %s\n", debug_prefix_time(NULL),
- 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;
* 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;
}
(*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.
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;
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 = "22";
+
+ if(!ssh_keys || strlen(ssh_keys) <= 1) {
+ g_debug("exec: %s %s %s %s %s %s %s %s %s",
+ SSH, "SSH_OPTIONS", "-l", xclient_username, "-p", client_port,
+ rc->hostname, xamandad_path, "-auth=ssh");
+ }
+ else {
+ g_debug("exec: %s %s %s %s %s %s %s %s %s %s %s",
+ SSH, "SSH_OPTIONS", "-l", xclient_username, "-p", client_port,
+ "-i", xssh_keys, rc->hostname, xamandad_path, "-auth=ssh");
+ }
+
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]);
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);
+ execlp(SSH, SSH, SSH_OPTIONS, "-l", xclient_username, "-p", client_port,
+ rc->hostname, xamandad_path, "-auth=ssh", (char *)NULL);
}
else {
- execlp(SSH_PATH, SSH_PATH, SSH_ARGS, "-l", xclient_username,
+ execlp(SSH, SSH, SSH_OPTIONS, "-l", xclient_username, "-p", client_port,
"-i", xssh_keys, rc->hostname, xamandad_path, "-auth=ssh",
- "amdump", "amindexd", "amidxtaped", (char *)NULL);
+ (char *)NULL);
}
- error("error: couldn't exec %s: %s", SSH_PATH, strerror(errno));
+ error("error: couldn't exec %s: %s", SSH, strerror(errno));
/* should never go here, shut up compiler warning */
return(-1);
}
-
-#endif /* SSH_SECURITY */