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 #if DROPBEAR_SVR_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 "dbrandom.h"
41 #include "listener.h"
42 #include "auth.h"
43 
44 #define AGENTDIRPREFIX "/tmp/dropbear-"
45 
46 static int send_msg_channel_open_agent(int fd);
47 static int bindagent(int fd, struct ChanSess * chansess);
48 static void agentaccept(const struct Listener * listener, int sock);
49 
50 /* Handles client requests to start agent forwarding, sets up listening socket.
51  * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
svr_agentreq(struct ChanSess * chansess)52 int svr_agentreq(struct ChanSess * chansess) {
53 	int fd = -1;
54 
55 	if (!svr_pubkey_allows_agentfwd()) {
56 		return DROPBEAR_FAILURE;
57 	}
58 
59 	if (chansess->agentlistener != NULL) {
60 		return DROPBEAR_FAILURE;
61 	}
62 
63 	/* create listening socket */
64 	fd = socket(PF_UNIX, SOCK_STREAM, 0);
65 	if (fd < 0) {
66 		goto fail;
67 	}
68 
69 	/* create the unix socket dir and file */
70 	if (bindagent(fd, chansess) == DROPBEAR_FAILURE) {
71 		goto fail;
72 	}
73 
74 	/* listen */
75 	if (listen(fd, 20) < 0) {
76 		goto fail;
77 	}
78 
79 	/* set non-blocking */
80 	setnonblocking(fd);
81 
82 	/* pass if off to listener */
83 	chansess->agentlistener = new_listener( &fd, 1, 0, chansess,
84 								agentaccept, NULL);
85 
86 	if (chansess->agentlistener == NULL) {
87 		goto fail;
88 	}
89 
90 	return DROPBEAR_SUCCESS;
91 
92 fail:
93 	m_close(fd);
94 	/* cleanup */
95 	svr_agentcleanup(chansess);
96 
97 	return DROPBEAR_FAILURE;
98 }
99 
100 /* accepts a connection on the forwarded socket and opens a new channel for it
101  * back to the client */
102 /* returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
agentaccept(const struct Listener * UNUSED (listener),int sock)103 static void agentaccept(const struct Listener *UNUSED(listener), int sock) {
104 
105 	int fd;
106 
107 	fd = accept(sock, NULL, NULL);
108 	if (fd < 0) {
109 		TRACE(("accept failed"))
110 		return;
111 	}
112 
113 	if (send_msg_channel_open_agent(fd) != DROPBEAR_SUCCESS) {
114 		close(fd);
115 	}
116 
117 }
118 
119 /* set up the environment variable pointing to the socket. This is called
120  * just before command/shell execution, after dropping privileges */
svr_agentset(const struct ChanSess * chansess)121 void svr_agentset(const struct ChanSess * chansess) {
122 
123 	char *path = NULL;
124 	int len;
125 
126 	if (chansess->agentlistener == NULL) {
127 		return;
128 	}
129 
130 	/* 2 for "/" and "\0" */
131 	len = strlen(chansess->agentdir) + strlen(chansess->agentfile) + 2;
132 
133 	path = m_malloc(len);
134 	snprintf(path, len, "%s/%s", chansess->agentdir, chansess->agentfile);
135 	addnewvar("SSH_AUTH_SOCK", path);
136 	m_free(path);
137 }
138 
139 /* close the socket, remove the socket-file */
svr_agentcleanup(struct ChanSess * chansess)140 void svr_agentcleanup(struct ChanSess * chansess) {
141 
142 	char *path = NULL;
143 	uid_t uid;
144 	gid_t gid;
145 	int len;
146 
147 	if (chansess->agentlistener != NULL) {
148 		remove_listener(chansess->agentlistener);
149 		chansess->agentlistener = NULL;
150 	}
151 
152 	if (chansess->agentfile != NULL && chansess->agentdir != NULL) {
153 
154 #if DROPBEAR_SVR_MULTIUSER
155 		/* Remove the dir as the user. That way they can't cause problems except
156 		 * for themselves */
157 		uid = getuid();
158 		gid = getgid();
159 		if ((setegid(ses.authstate.pw_gid)) < 0 ||
160 			(seteuid(ses.authstate.pw_uid)) < 0) {
161 			dropbear_exit("Failed to set euid");
162 		}
163 #endif
164 
165 		/* 2 for "/" and "\0" */
166 		len = strlen(chansess->agentdir) + strlen(chansess->agentfile) + 2;
167 
168 		path = m_malloc(len);
169 		snprintf(path, len, "%s/%s", chansess->agentdir, chansess->agentfile);
170 		unlink(path);
171 		m_free(path);
172 
173 		rmdir(chansess->agentdir);
174 
175 #if DROPBEAR_SVR_MULTIUSER
176 		if ((seteuid(uid)) < 0 ||
177 			(setegid(gid)) < 0) {
178 			dropbear_exit("Failed to revert euid");
179 		}
180 #endif
181 
182 		m_free(chansess->agentfile);
183 		m_free(chansess->agentdir);
184 	}
185 
186 }
187 
188 static const struct ChanType chan_svr_agent = {
189 	0, /* sepfds */
190 	"auth-agent@openssh.com",
191 	NULL,
192 	NULL,
193 	NULL,
194 	NULL,
195 	NULL
196 };
197 
198 
199 /* helper for accepting an agent request */
send_msg_channel_open_agent(int fd)200 static int send_msg_channel_open_agent(int fd) {
201 
202 	if (send_msg_channel_open_init(fd, &chan_svr_agent) == DROPBEAR_SUCCESS) {
203 		encrypt_packet();
204 		return DROPBEAR_SUCCESS;
205 	} else {
206 		return DROPBEAR_FAILURE;
207 	}
208 }
209 
210 /* helper for creating the agent socket-file
211    returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
bindagent(int fd,struct ChanSess * chansess)212 static int bindagent(int fd, struct ChanSess * chansess) {
213 
214 	struct sockaddr_un addr;
215 	unsigned int prefix;
216 	char path[(sizeof(addr.sun_path)-1)/2], sockfile[(sizeof(addr.sun_path)-1)/2];
217 	mode_t mode;
218 	int i;
219 	uid_t uid;
220 	gid_t gid;
221 	int ret = DROPBEAR_FAILURE;
222 
223 #if DROPBEAR_SVR_MULTIUSER
224 	/* drop to user privs to make the dir/file */
225 	uid = getuid();
226 	gid = getgid();
227 	if ((setegid(ses.authstate.pw_gid)) < 0 ||
228 		(seteuid(ses.authstate.pw_uid)) < 0) {
229 		dropbear_exit("Failed to set euid");
230 	}
231 #endif
232 
233 	memset((void*)&addr, 0x0, sizeof(addr));
234 	addr.sun_family = AF_UNIX;
235 
236 	mode = S_IRWXU;
237 
238 	for (i = 0; i < 20; i++) {
239 		genrandom((unsigned char*)&prefix, sizeof(prefix));
240 		/* we want 32 bits (8 hex digits) - "/tmp/dropbear-f19c62c0" */
241 		snprintf(path, sizeof(path), AGENTDIRPREFIX "%.8x", prefix);
242 
243 		if (mkdir(path, mode) == 0) {
244 			goto bindsocket;
245 		}
246 		if (errno != EEXIST) {
247 			break;
248 		}
249 	}
250 	/* couldn't make a dir */
251 	goto out;
252 
253 bindsocket:
254 	/* Format is "/tmp/dropbear-0246dead/auth-d00f7654-23".
255 	 * The "23" is the file desc, the random data is to avoid collisions
256 	 * between subsequent user processes reusing socket fds (odds are now
257 	 * 1/(2^64) */
258 	genrandom((unsigned char*)&prefix, sizeof(prefix));
259 	snprintf(sockfile, sizeof(sockfile), "auth-%.8x-%d", prefix, fd);
260 
261 	snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s", path, sockfile);
262 
263 	if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) == 0) {
264 		chansess->agentdir = m_strdup(path);
265 		chansess->agentfile = m_strdup(sockfile);
266 		ret = DROPBEAR_SUCCESS;
267 	}
268 
269 
270 out:
271 #if DROPBEAR_SVR_MULTIUSER
272 	if ((seteuid(uid)) < 0 ||
273 		(setegid(gid)) < 0) {
274 		dropbear_exit("Failed to revert euid");
275 	}
276 #endif
277 	return ret;
278 }
279 
280 #endif
281