Don't do priv separation if no priv connections to make.
[fwd] / privconn.c
1 /* -*-c-*-
2 *
3 * $Id: privconn.c,v 1.2 2003/11/29 20:43:01 mdw Exp $
4 *
5 * Making privileged connections
6 *
7 * (c) 2003 Straylight/Edgeware
8 */
9
10 /*----- Licensing notice --------------------------------------------------*
11 *
12 * This file is part of the `fw' port forwarder.
13 *
14 * `fw' is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
18 *
19 * `fw' is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with `fw'; if not, write to the Free Software Foundation,
26 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 */
28
29 /*----- Revision history --------------------------------------------------*
30 *
31 * $Log: privconn.c,v $
32 * Revision 1.2 2003/11/29 20:43:01 mdw
33 * Don't do priv separation if no priv connections to make.
34 *
35 * Revision 1.1 2003/11/29 20:36:07 mdw
36 * Privileged outgoing connections.
37 *
38 */
39
40 /*----- Header files ------------------------------------------------------*/
41
42 #include <assert.h>
43 #include <errno.h>
44 #include <signal.h>
45 #include <string.h>
46
47 #include <sys/types.h>
48 #include <unistd.h>
49
50 #include <sys/socket.h>
51 #include <arpa/inet.h>
52 #include <netinet/in.h>
53
54 #include <mLib/conn.h>
55 #include <mLib/darray.h>
56 #include <mLib/fdflags.h>
57 #include <mLib/fdpass.h>
58 #include <mLib/report.h>
59 #include <mLib/sel.h>
60
61 #include "privconn.h"
62
63 /*----- Data structures ---------------------------------------------------*/
64
65 typedef struct connrec {
66 struct in_addr peer;
67 unsigned port;
68 } connrec;
69
70 typedef struct connrq {
71 int i;
72 struct in_addr bind;
73 } connrq;
74
75 DA_DECL(connrec_v, connrec);
76
77 /*----- Static variables --------------------------------------------------*/
78
79 static connrec_v cv = DA_INIT;
80 static conn *qhead = 0, **qtail = &qhead;
81 static int kidfd = -1;
82 static sel_file sf;
83
84 /*----- Main code ---------------------------------------------------------*/
85
86 /* --- @doconn@ --- *
87 *
88 * Arguments: @const connrq *rq@ = index of connection record
89 *
90 * Returns: Connected file descriptor, or @-1@.
91 *
92 * Use: Main privileged connection thing.
93 */
94
95 static int doconn(const connrq *rq)
96 {
97 struct sockaddr_in sin_bind;
98 struct sockaddr_in sin_peer;
99 int fd;
100 int i;
101 connrec *c;
102
103 /* --- Check the argument --- */
104
105 if (rq->i < 0 || rq->i >= DA_LEN(&cv)) {
106 errno = EINVAL;
107 goto fail_0;
108 }
109 c = &DA(&cv)[rq->i];
110
111 /* --- Make a new socket --- */
112
113 if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
114 goto fail_0;
115
116 /* --- Bind it to a low-numbered port --- */
117
118 memset(&sin_bind, 0, sizeof(sin_bind));
119 sin_bind.sin_family = AF_INET;
120 sin_bind.sin_addr = rq->bind;
121 for (i = 1023; i >= 512; i--) {
122 sin_bind.sin_port = htons(i);
123 if (!bind(fd, (struct sockaddr *)&sin_bind, sizeof(sin_bind)))
124 goto bound;
125 if (errno != EADDRINUSE)
126 goto fail_1;
127 }
128 goto fail_1;
129
130 /* --- Connect to the peer --- *
131 *
132 * We can find out whether it's connected later, so there's no need to
133 * distinguish these cases.
134 */
135
136 bound:
137 memset(&sin_peer, 0, sizeof(sin_peer));
138 sin_peer.sin_family = AF_INET;
139 sin_peer.sin_addr = c->peer;
140 sin_peer.sin_port = c->port;
141 fdflags(fd, O_NONBLOCK, O_NONBLOCK, 0, 0);
142 if (connect(fd, (struct sockaddr *)&sin_peer, sizeof(sin_peer)) < 0 &&
143 errno != EINPROGRESS)
144 goto fail_1;
145 return (fd);
146
147 /* --- Tidy up on errors --- */
148
149 fail_1:
150 close(fd);
151 fail_0:
152 return (-1);
153 }
154
155 /* --- @dochild@ --- *
156 *
157 * Arguments: @int fd@ = my file descriptor
158 *
159 * Returns: Never.
160 *
161 * Use: Child process for making privileged connections, separated
162 * from main process after initialization.
163 */
164
165 static void dochild(int fd)
166 {
167 int i;
168 connrq rq;
169 int nfd;
170 ssize_t sz;
171 #if defined(_SC_OPEN_MAX)
172 int maxfd = sysconf(_SC_OPEN_MAX);
173 #elif defined(OPEN_MAX)
174 int maxfd = OPEN_MAX;
175 #else
176 int maxfd = -1;
177 #endif
178 struct sigaction sa;
179 struct sigaction sa_dfl;
180
181 /* --- Clear out unnecessary file descriptors --- */
182
183 if (maxfd < 0)
184 maxfd = 256;
185 for (i = 3; i < maxfd; i++)
186 if (i != fd) close(i);
187
188 /* --- Close off signal handlers --- */
189
190 sa_dfl.sa_handler = SIG_DFL;
191 sigemptyset(&sa_dfl.sa_mask);
192 sa_dfl.sa_flags = 0;
193 for (i = 0; i < 256; i++) {
194 if (sigaction(i, 0, &sa))
195 break;
196 if (sa.sa_handler != SIG_DFL && sa.sa_handler != SIG_IGN)
197 sigaction(i, &sa_dfl, 0);
198 }
199
200 /* --- Main loop --- */
201
202 for (;;) {
203 sz = read(fd, &rq, sizeof(rq));
204 if (!sz)
205 break;
206 if (sz < 0)
207 die(1, "read error in privconn child: %s", strerror(errno));
208 if ((nfd = doconn(&rq)) < 0)
209 goto err;
210 i = 0;
211 sz = fdpass_send(fd, nfd, &i, sizeof(i));
212 if (sz < 0)
213 goto err;
214 if (sz < sizeof(i))
215 die(1, "short write in privconn child");
216 continue;
217
218 err:
219 if (write(fd, &errno, sizeof(errno)) < 0)
220 die(1, "write error in privconn child: %s", strerror(errno));
221 }
222 _exit(0);
223 }
224
225 /* --- @dorecvfd@ --- *
226 *
227 * Arguments: @int fd@ = file descriptor (@== kidfd@)
228 * @unsigned mode@ = what's happening (@== SEL_READ@)
229 * @void *p@ = uninteresting (@== 0@)
230 *
231 * Returns: ---
232 *
233 * Use: Receives a file descriptor from the privileged part.
234 */
235
236 void dorecvfd(int fd, unsigned mode, void *p)
237 {
238 conn *c, *cc;
239 ssize_t n;
240 int e;
241
242 n = fdpass_recv(kidfd, &fd, &e, sizeof(e));
243 if (!n)
244 goto close;
245 assert(qhead);
246 c = qhead;
247 qhead = (conn *)c->writer.next;
248 if (!qhead) qtail = &qhead;
249 if (n < 0 || (errno = e) != 0)
250 goto fail;
251 if (fd == -1) {
252 errno = EIO;
253 goto fail;
254 }
255 conn_fd(c, c->writer.s, fd, c->func, c->p);
256 return;
257
258 fail:
259 c->func(-1, c->p);
260 return;
261
262 close:
263 close(kidfd);
264 kidfd = 0;
265 errno = EIO;
266 sel_rmfile(&sf);
267 for (c = qhead; c; c = cc) {
268 cc = (conn *)c->writer.next;
269 c->func(-1, c->p);
270 }
271 qhead = 0;
272 qtail = &qhead;
273 return;
274 }
275
276 /* --- @privconn_split@ --- *
277 *
278 * Arguments: @sel_state *s@ = select state
279 *
280 * Returns: ---
281 *
282 * Use: Splits off the privileged binding code into a separate
283 * process.
284 */
285
286 void privconn_split(sel_state *s)
287 {
288 pid_t kid;
289 int fd[2];
290
291 if (kidfd != -1 || DA_LEN(&cv) == 0)
292 return;
293 if (socketpair(PF_UNIX, SOCK_STREAM, 0, fd) < 0)
294 die(1, "couldn't create privconn socketpair: %s", strerror(errno));
295 kidfd = fd[0];
296 if ((kid = fork()) < 0)
297 die(1, "couldn't fork privconn child: %s", strerror(errno));
298 if (!kid) {
299 close(kidfd);
300 dochild(fd[1]);
301 _exit(127);
302 }
303 close(fd[1]);
304 fdflags(kidfd, 0, 0, FD_CLOEXEC, FD_CLOEXEC);
305 sel_initfile(s, &sf, kidfd, SEL_READ, dorecvfd, 0);
306 sel_addfile(&sf);
307 }
308
309 /* --- @privconn_adddest@ --- *
310 *
311 * Arguments: @struct in_addr peer@ = address to connect to
312 * @unsigned port@ = port to connect to
313 *
314 * Returns: Index for this destination address, or @-1@ if not
315 * available.
316 *
317 * Use: Adds a valid destination for a privileged connection.
318 */
319
320 int privconn_adddest(struct in_addr peer, unsigned port)
321 {
322 int i;
323 struct connrec *c;
324
325 if (kidfd != -1)
326 return (-1);
327 for (i = 0; i < DA_LEN(&cv); i++) {
328 c = &DA(&cv)[i];
329 if (peer.s_addr == c->peer.s_addr && port == c->port)
330 return (i);
331 }
332 DA_ENSURE(&cv, 1);
333 DA_EXTEND(&cv, 1);
334 c = &DA(&cv)[i];
335 c->peer = peer;
336 c->port = port;
337 return (i);
338 }
339
340 /* --- @privconn_connect@ --- *
341 *
342 * Arguments: @conn *c@ = connection structure to fill in
343 * @sel_state *s@ = pointer to select state to attach to
344 * @int i@ = address index to connect to
345 * @struct in_addr bind@ = address to bind to
346 * @void (*func)(int, void *)@ = function to call on connect
347 * @void *p@ = argument for the function
348 *
349 * Returns: Zero on success, @-1@ on failure.
350 *
351 * Use: Sets up a privileged connection job.
352 */
353
354 int privconn_connect(conn *c, sel_state *s, int i, struct in_addr bind,
355 void (*func)(int, void *), void *p)
356 {
357 int fd;
358 connrq rq;
359 ssize_t n;
360
361 rq.i = i;
362 rq.bind = bind;
363 if (kidfd == -1) {
364 if ((fd = doconn(&rq)) < 0)
365 return (-1);
366 conn_fd(c, s, fd, func, p);
367 return (0);
368 }
369
370 n = write(kidfd, &rq, sizeof(rq));
371 if (n < 0)
372 return (-1);
373 c->writer.fd = -1;
374 c->writer.s = s;
375 c->writer.next = 0;
376 c->func = func;
377 c->p = p;
378 *qtail = c;
379 qtail = (conn **)&c->writer.next;
380 return (0);
381 }
382
383 /*----- That's all, folks -------------------------------------------------*/