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