diff svr-agentfwd.c @ 391:00fcf5045160

propagate from branch 'au.asn.ucc.matt.ltc.dropbear' (head c1db4398d56c56c6d06ae1e20c1e0d04dbb598ed) to branch 'au.asn.ucc.matt.dropbear' (head d26d5eb2837f46b56a33fb0e7573aa0201abd4d5)
author Matt Johnston <matt@ucc.asn.au>
date Thu, 11 Jan 2007 04:29:08 +0000
parents 0cfba3034be5
children ca7e76d981d9 4317be8b7cf9
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/svr-agentfwd.c	Thu Jan 11 04:29:08 2007 +0000
@@ -0,0 +1,266 @@
+/*
+ * Dropbear - a SSH2 server
+ * 
+ * Copyright (c) 2002,2003 Matt Johnston
+ * All rights reserved.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE. */
+
+/* This file (agentfwd.c) handles authentication agent forwarding, for OpenSSH
+ * style agents. */
+
+#include "includes.h"
+
+#ifndef DISABLE_AGENTFWD
+
+#include "agentfwd.h"
+#include "session.h"
+#include "ssh.h"
+#include "dbutil.h"
+#include "chansession.h"
+#include "channel.h"
+#include "packet.h"
+#include "buffer.h"
+#include "random.h"
+#include "listener.h"
+
+#define AGENTDIRPREFIX "/tmp/dropbear-"
+
+static int send_msg_channel_open_agent(int fd);
+static int bindagent(int fd, struct ChanSess * chansess);
+static void agentaccept(struct Listener * listener, int sock);
+
+/* Handles client requests to start agent forwarding, sets up listening socket.
+ * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
+int agentreq(struct ChanSess * chansess) {
+
+	int fd;
+
+	if (chansess->agentlistener != NULL) {
+		return DROPBEAR_FAILURE;
+	}
+
+	/* create listening socket */
+	fd = socket(PF_UNIX, SOCK_STREAM, 0);
+	if (fd < 0) {
+		goto fail;
+	}
+
+	/* create the unix socket dir and file */
+	if (bindagent(fd, chansess) == DROPBEAR_FAILURE) {
+		goto fail;
+	}
+
+	/* listen */
+	if (listen(fd, 20) < 0) {
+		goto fail;
+	}
+
+	/* set non-blocking */
+	setnonblocking(fd);
+
+	/* pass if off to listener */
+	chansess->agentlistener = new_listener( &fd, 1, 0, chansess, 
+								agentaccept, NULL);
+
+	if (chansess->agentlistener == NULL) {
+		goto fail;
+	}
+
+	return DROPBEAR_SUCCESS;
+
+fail:
+	/* cleanup */
+	agentcleanup(chansess);
+
+	return DROPBEAR_FAILURE;
+}
+
+/* accepts a connection on the forwarded socket and opens a new channel for it
+ * back to the client */
+/* returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
+static void agentaccept(struct Listener *UNUSED(listener), int sock) {
+
+	int fd;
+
+	fd = accept(sock, NULL, NULL);
+	if (fd < 0) {
+		TRACE(("accept failed"))
+		return;
+	}
+
+	if (send_msg_channel_open_agent(fd) != DROPBEAR_SUCCESS) {
+		close(fd);
+	}
+
+}
+
+/* set up the environment variable pointing to the socket. This is called
+ * just before command/shell execution, after dropping priveleges */
+void agentset(struct ChanSess * chansess) {
+
+	char *path = NULL;
+	int len;
+
+	if (chansess->agentlistener == NULL) {
+		return;
+	}
+
+	/* 2 for "/" and "\0" */
+	len = strlen(chansess->agentdir) + strlen(chansess->agentfile) + 2;
+
+	path = m_malloc(len);
+	snprintf(path, len, "%s/%s", chansess->agentdir, chansess->agentfile);
+	addnewvar("SSH_AUTH_SOCK", path);
+	m_free(path);
+}
+
+/* close the socket, remove the socket-file */
+void agentcleanup(struct ChanSess * chansess) {
+
+	char *path = NULL;
+	uid_t uid;
+	gid_t gid;
+	int len;
+
+	if (chansess->agentlistener != NULL) {
+		remove_listener(chansess->agentlistener);
+		chansess->agentlistener = NULL;
+	}
+
+	if (chansess->agentfile != NULL && chansess->agentdir != NULL) {
+
+		/* Remove the dir as the user. That way they can't cause problems except
+		 * for themselves */
+		uid = getuid();
+		gid = getgid();
+		if ((setegid(ses.authstate.pw->pw_gid)) < 0 ||
+			(seteuid(ses.authstate.pw->pw_uid)) < 0) {
+			dropbear_exit("failed to set euid");
+		}
+
+		/* 2 for "/" and "\0" */
+		len = strlen(chansess->agentdir) + strlen(chansess->agentfile) + 2;
+
+		path = m_malloc(len);
+		snprintf(path, len, "%s/%s", chansess->agentdir, chansess->agentfile);
+		unlink(path);
+		m_free(path);
+
+		rmdir(chansess->agentdir);
+
+		if ((seteuid(uid)) < 0 ||
+			(setegid(gid)) < 0) {
+			dropbear_exit("failed to revert euid");
+		}
+
+		m_free(chansess->agentfile);
+		m_free(chansess->agentdir);
+	}
+
+}
+
+static const struct ChanType chan_agent = {
+	0, /* sepfds */
+	"[email protected]",
+	NULL,
+	NULL,
+	NULL,
+	NULL
+};
+
+
+/* helper for accepting an agent request */
+static int send_msg_channel_open_agent(int fd) {
+
+	if (send_msg_channel_open_init(fd, &chan_agent) == DROPBEAR_SUCCESS) {
+		encrypt_packet();
+		return DROPBEAR_SUCCESS;
+	} else {
+		return DROPBEAR_FAILURE;
+	}
+}
+
+/* helper for creating the agent socket-file
+   returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
+static int bindagent(int fd, struct ChanSess * chansess) {
+
+	struct sockaddr_un addr;
+	unsigned int prefix;
+	char path[sizeof(addr.sun_path)], sockfile[sizeof(addr.sun_path)];
+	mode_t mode;
+	int i;
+	uid_t uid;
+	gid_t gid;
+	int ret = DROPBEAR_FAILURE;
+
+	/* drop to user privs to make the dir/file */
+	uid = getuid();
+	gid = getgid();
+	if ((setegid(ses.authstate.pw->pw_gid)) < 0 ||
+		(seteuid(ses.authstate.pw->pw_uid)) < 0) {
+		dropbear_exit("failed to set euid");
+	}
+
+	memset((void*)&addr, 0x0, sizeof(addr));
+	addr.sun_family = AF_UNIX;
+
+	mode = S_IRWXU;
+
+	for (i = 0; i < 20; i++) {
+		genrandom((unsigned char*)&prefix, sizeof(prefix));
+		/* we want 32 bits (8 hex digits) - "/tmp/dropbear-f19c62c0" */
+		snprintf(path, sizeof(path), AGENTDIRPREFIX "%.8x", prefix);
+
+		if (mkdir(path, mode) == 0) {
+			goto bindsocket;
+		}
+		if (errno != EEXIST) {
+			break;
+		}
+	}
+	/* couldn't make a dir */
+	goto out;
+
+bindsocket:
+	/* Format is "/tmp/dropbear-0246dead/auth-d00f7654-23".
+	 * The "23" is the file desc, the random data is to avoid collisions
+	 * between subsequent user processes reusing socket fds (odds are now
+	 * 1/(2^64) */
+	genrandom((unsigned char*)&prefix, sizeof(prefix));
+	snprintf(sockfile, sizeof(sockfile), "auth-%.8x-%d", prefix, fd);
+			
+	snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s", path, sockfile);
+
+	if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) == 0) {
+		chansess->agentdir = m_strdup(path);
+		chansess->agentfile = m_strdup(sockfile);
+		ret = DROPBEAR_SUCCESS;
+	}
+
+
+out:
+	if ((seteuid(uid)) < 0 ||
+		(setegid(gid)) < 0) {
+		dropbear_exit("failed to revert euid");
+	}
+	return ret;
+}
+
+#endif