Home | History | Annotate | Download | only in dropbear
      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 
     45 static int send_msg_channel_open_agent(int fd);
     46 static int bindagent(int fd, struct ChanSess * chansess);
     47 static 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 */
     51 int 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 
     88 fail:
     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 */
     98 static 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 */
    116 void 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 */
    135 void 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 
    179 static const struct ChanType chan_agent = {
    180 	0, /* sepfds */
    181 	"auth-agent (at) openssh.com",
    182 	NULL,
    183 	NULL,
    184 	NULL,
    185 	NULL
    186 };
    187 
    188 
    189 /* helper for accepting an agent request */
    190 static 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 */
    202 static 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 
    241 bindsocket:
    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 
    258 out:
    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