• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 */
agentreq(struct ChanSess * chansess)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 */
agentaccept(struct Listener * UNUSED (listener),int sock)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 */
agentset(struct ChanSess * chansess)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 */
agentcleanup(struct ChanSess * chansess)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@openssh.com",
182 	NULL,
183 	NULL,
184 	NULL,
185 	NULL
186 };
187 
188 
189 /* helper for accepting an agent request */
send_msg_channel_open_agent(int fd)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 */
bindagent(int fd,struct ChanSess * chansess)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