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