/* * 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) { return DROPBEAR_FAILURE; } /* listen */ if (listen(fd, 20) < 0) { goto fail; } /* set non-blocking */ if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) { goto fail; } /* 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 * 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 && chansess->agentdir) { /* 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