diff options
author | Matt Johnston <matt@ucc.asn.au> | 2007-01-11 03:14:55 +0000 |
---|---|---|
committer | Matt Johnston <matt@ucc.asn.au> | 2007-01-11 03:14:55 +0000 |
commit | 9d5ed350a749368c84254c11e7616ce3c891193a (patch) | |
tree | 6dacbff2e9f5c60a1568382db55c72dd6d2ce925 /svr-agentfwd.c | |
parent | ca52f070aecf91e75f6ae6c87d4ae1a2189ccb14 (diff) | |
parent | 5ea605d8de5b4438deb4fa86c5231710dd09f934 (diff) |
propagate from branch 'au.asn.ucc.matt.ltm.dropbear' (head 2af95f00ebd5bb7a28b3817db1218442c935388e)
to branch 'au.asn.ucc.matt.dropbear' (head ecd779509ef23a8cdf64888904fc9b31d78aa933)
--HG--
extra : convert_revision : d26d5eb2837f46b56a33fb0e7573aa0201abd4d5
Diffstat (limited to 'svr-agentfwd.c')
-rw-r--r-- | svr-agentfwd.c | 266 |
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 |