1/* 2 * Dropbear - a SSH2 server 3 * 4 * Copyright (c) 2002,2003 Matt Johnston 5 * All rights reserved. 6 * 7 * Permission is hereby granted, free of charge, to any person obtaining a copy 8 * of this software and associated documentation files (the "Software"), to deal 9 * in the Software without restriction, including without limitation the rights 10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 * copies of the Software, and to permit persons to whom the Software is 12 * furnished to do so, subject to the following conditions: 13 * 14 * The above copyright notice and this permission notice shall be included in 15 * all copies or substantial portions of the Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 * SOFTWARE. */ 24 25/* This file (agentfwd.c) handles authentication agent forwarding, for OpenSSH 26 * style agents. */ 27 28#include "includes.h" 29 30#ifndef DISABLE_AGENTFWD 31 32#include "agentfwd.h" 33#include "session.h" 34#include "ssh.h" 35#include "dbutil.h" 36#include "chansession.h" 37#include "channel.h" 38#include "packet.h" 39#include "buffer.h" 40#include "random.h" 41#include "listener.h" 42 43#define AGENTDIRPREFIX "/tmp/dropbear-" 44 45static int send_msg_channel_open_agent(int fd); 46static int bindagent(int fd, struct ChanSess * chansess); 47static void agentaccept(struct Listener * listener, int sock); 48 49/* Handles client requests to start agent forwarding, sets up listening socket. 50 * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ 51int agentreq(struct ChanSess * chansess) { 52 53 int fd; 54 55 if (chansess->agentlistener != NULL) { 56 return DROPBEAR_FAILURE; 57 } 58 59 /* create listening socket */ 60 fd = socket(PF_UNIX, SOCK_STREAM, 0); 61 if (fd < 0) { 62 goto fail; 63 } 64 65 /* create the unix socket dir and file */ 66 if (bindagent(fd, chansess) == DROPBEAR_FAILURE) { 67 goto fail; 68 } 69 70 /* listen */ 71 if (listen(fd, 20) < 0) { 72 goto fail; 73 } 74 75 /* set non-blocking */ 76 setnonblocking(fd); 77 78 /* pass if off to listener */ 79 chansess->agentlistener = new_listener( &fd, 1, 0, chansess, 80 agentaccept, NULL); 81 82 if (chansess->agentlistener == NULL) { 83 goto fail; 84 } 85 86 return DROPBEAR_SUCCESS; 87 88fail: 89 /* cleanup */ 90 agentcleanup(chansess); 91 92 return DROPBEAR_FAILURE; 93} 94 95/* accepts a connection on the forwarded socket and opens a new channel for it 96 * back to the client */ 97/* returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ 98static void agentaccept(struct Listener *UNUSED(listener), int sock) { 99 100 int fd; 101 102 fd = accept(sock, NULL, NULL); 103 if (fd < 0) { 104 TRACE(("accept failed")) 105 return; 106 } 107 108 if (send_msg_channel_open_agent(fd) != DROPBEAR_SUCCESS) { 109 close(fd); 110 } 111 112} 113 114/* set up the environment variable pointing to the socket. This is called 115 * just before command/shell execution, after dropping priveleges */ 116void agentset(struct ChanSess * chansess) { 117 118 char *path = NULL; 119 int len; 120 121 if (chansess->agentlistener == NULL) { 122 return; 123 } 124 125 /* 2 for "/" and "\0" */ 126 len = strlen(chansess->agentdir) + strlen(chansess->agentfile) + 2; 127 128 path = m_malloc(len); 129 snprintf(path, len, "%s/%s", chansess->agentdir, chansess->agentfile); 130 addnewvar("SSH_AUTH_SOCK", path); 131 m_free(path); 132} 133 134/* close the socket, remove the socket-file */ 135void agentcleanup(struct ChanSess * chansess) { 136 137 char *path = NULL; 138 uid_t uid; 139 gid_t gid; 140 int len; 141 142 if (chansess->agentlistener != NULL) { 143 remove_listener(chansess->agentlistener); 144 chansess->agentlistener = NULL; 145 } 146 147 if (chansess->agentfile != NULL && chansess->agentdir != NULL) { 148 149 /* Remove the dir as the user. That way they can't cause problems except 150 * for themselves */ 151 uid = getuid(); 152 gid = getgid(); 153 if ((setegid(ses.authstate.pw->pw_gid)) < 0 || 154 (seteuid(ses.authstate.pw->pw_uid)) < 0) { 155 dropbear_exit("failed to set euid"); 156 } 157 158 /* 2 for "/" and "\0" */ 159 len = strlen(chansess->agentdir) + strlen(chansess->agentfile) + 2; 160 161 path = m_malloc(len); 162 snprintf(path, len, "%s/%s", chansess->agentdir, chansess->agentfile); 163 unlink(path); 164 m_free(path); 165 166 rmdir(chansess->agentdir); 167 168 if ((seteuid(uid)) < 0 || 169 (setegid(gid)) < 0) { 170 dropbear_exit("failed to revert euid"); 171 } 172 173 m_free(chansess->agentfile); 174 m_free(chansess->agentdir); 175 } 176 177} 178 179static const struct ChanType chan_agent = { 180 0, /* sepfds */ 181 "auth-agent@openssh.com", 182 NULL, 183 NULL, 184 NULL, 185 NULL 186}; 187 188 189/* helper for accepting an agent request */ 190static int send_msg_channel_open_agent(int fd) { 191 192 if (send_msg_channel_open_init(fd, &chan_agent) == DROPBEAR_SUCCESS) { 193 encrypt_packet(); 194 return DROPBEAR_SUCCESS; 195 } else { 196 return DROPBEAR_FAILURE; 197 } 198} 199 200/* helper for creating the agent socket-file 201 returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ 202static int bindagent(int fd, struct ChanSess * chansess) { 203 204 struct sockaddr_un addr; 205 unsigned int prefix; 206 char path[sizeof(addr.sun_path)], sockfile[sizeof(addr.sun_path)]; 207 mode_t mode; 208 int i; 209 uid_t uid; 210 gid_t gid; 211 int ret = DROPBEAR_FAILURE; 212 213 /* drop to user privs to make the dir/file */ 214 uid = getuid(); 215 gid = getgid(); 216 if ((setegid(ses.authstate.pw->pw_gid)) < 0 || 217 (seteuid(ses.authstate.pw->pw_uid)) < 0) { 218 dropbear_exit("failed to set euid"); 219 } 220 221 memset((void*)&addr, 0x0, sizeof(addr)); 222 addr.sun_family = AF_UNIX; 223 224 mode = S_IRWXU; 225 226 for (i = 0; i < 20; i++) { 227 genrandom((unsigned char*)&prefix, sizeof(prefix)); 228 /* we want 32 bits (8 hex digits) - "/tmp/dropbear-f19c62c0" */ 229 snprintf(path, sizeof(path), AGENTDIRPREFIX "%.8x", prefix); 230 231 if (mkdir(path, mode) == 0) { 232 goto bindsocket; 233 } 234 if (errno != EEXIST) { 235 break; 236 } 237 } 238 /* couldn't make a dir */ 239 goto out; 240 241bindsocket: 242 /* Format is "/tmp/dropbear-0246dead/auth-d00f7654-23". 243 * The "23" is the file desc, the random data is to avoid collisions 244 * between subsequent user processes reusing socket fds (odds are now 245 * 1/(2^64) */ 246 genrandom((unsigned char*)&prefix, sizeof(prefix)); 247 snprintf(sockfile, sizeof(sockfile), "auth-%.8x-%d", prefix, fd); 248 249 snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s", path, sockfile); 250 251 if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) == 0) { 252 chansess->agentdir = m_strdup(path); 253 chansess->agentfile = m_strdup(sockfile); 254 ret = DROPBEAR_SUCCESS; 255 } 256 257 258out: 259 if ((seteuid(uid)) < 0 || 260 (setegid(gid)) < 0) { 261 dropbear_exit("failed to revert euid"); 262 } 263 return ret; 264} 265 266#endif 267