summaryrefslogtreecommitdiffhomepage
path: root/svr-agentfwd.c
diff options
context:
space:
mode:
Diffstat (limited to 'svr-agentfwd.c')
-rw-r--r--svr-agentfwd.c266
1 files changed, 266 insertions, 0 deletions
diff --git a/svr-agentfwd.c b/svr-agentfwd.c
new file mode 100644
index 0000000..5127158
--- /dev/null
+++ b/svr-agentfwd.c
@@ -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 */
+ "auth-agent@openssh.com",
+ 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