diff options
-rw-r--r-- | include/applets.h | 3 | ||||
-rw-r--r-- | include/usage.h | 7 | ||||
-rw-r--r-- | networking/Config.in | 7 | ||||
-rw-r--r-- | networking/Makefile.in | 1 | ||||
-rw-r--r-- | networking/fakeidentd.c | 441 |
5 files changed, 459 insertions, 0 deletions
diff --git a/include/applets.h b/include/applets.h index 90a081550..7771c17a9 100644 --- a/include/applets.h +++ b/include/applets.h | |||
@@ -185,6 +185,9 @@ | |||
185 | #ifdef CONFIG_EXPR | 185 | #ifdef CONFIG_EXPR |
186 | APPLET(expr, expr_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) | 186 | APPLET(expr, expr_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) |
187 | #endif | 187 | #endif |
188 | #ifdef CONFIG_FAKEIDENTD | ||
189 | APPLET(fakeidentd, fakeidentd_main, _BB_DIR_USR_SBIN, _BB_SUID_NEVER) | ||
190 | #endif | ||
188 | #ifdef CONFIG_FALSE | 191 | #ifdef CONFIG_FALSE |
189 | APPLET(false, false_main, _BB_DIR_BIN, _BB_SUID_NEVER) | 192 | APPLET(false, false_main, _BB_DIR_BIN, _BB_SUID_NEVER) |
190 | #endif | 193 | #endif |
diff --git a/include/usage.h b/include/usage.h index 6d594dc01..128aee2f5 100644 --- a/include/usage.h +++ b/include/usage.h | |||
@@ -575,6 +575,13 @@ | |||
575 | "\\( and \\) or null; if \\( and \\) are not used, they return the number \n" \ | 575 | "\\( and \\) or null; if \\( and \\) are not used, they return the number \n" \ |
576 | "of characters matched or 0." | 576 | "of characters matched or 0." |
577 | 577 | ||
578 | #define fakeidentd_trivial_usage \ | ||
579 | "[-b ip] [STRING]" | ||
580 | #define fakeidentd_full_usage \ | ||
581 | "Returns a set string to auth requests\n\n"\ | ||
582 | "\t-b\tBind to ip address\n"\ | ||
583 | "\tSTRING\tThe ident answer string (default is nobody)" | ||
584 | |||
578 | #define false_trivial_usage \ | 585 | #define false_trivial_usage \ |
579 | "" | 586 | "" |
580 | #define false_full_usage \ | 587 | #define false_full_usage \ |
diff --git a/networking/Config.in b/networking/Config.in index 42176f050..cf2f1dcc1 100644 --- a/networking/Config.in +++ b/networking/Config.in | |||
@@ -18,6 +18,13 @@ config CONFIG_ARPING | |||
18 | help | 18 | help |
19 | Ping hosts by ARP packets | 19 | Ping hosts by ARP packets |
20 | 20 | ||
21 | config CONFIG_FAKEIDENTD | ||
22 | bool "fakeidentd" | ||
23 | default n | ||
24 | help | ||
25 | fakeidentd listens to the ident port and returns a set fake | ||
26 | value whatever it gets. | ||
27 | |||
21 | config CONFIG_FTPGET | 28 | config CONFIG_FTPGET |
22 | bool "ftpget" | 29 | bool "ftpget" |
23 | default n | 30 | default n |
diff --git a/networking/Makefile.in b/networking/Makefile.in index 9bfe90176..4e27dbc67 100644 --- a/networking/Makefile.in +++ b/networking/Makefile.in | |||
@@ -24,6 +24,7 @@ endif | |||
24 | srcdir=$(top_srcdir)/networking | 24 | srcdir=$(top_srcdir)/networking |
25 | NETWORKING-y:= | 25 | NETWORKING-y:= |
26 | NETWORKING-$(CONFIG_ARPING) += arping.o | 26 | NETWORKING-$(CONFIG_ARPING) += arping.o |
27 | NETWORKING-$(CONFIG_FAKEIDENTD) += fakeidentd.o | ||
27 | NETWORKING-$(CONFIG_FTPGET) += ftpgetput.o | 28 | NETWORKING-$(CONFIG_FTPGET) += ftpgetput.o |
28 | NETWORKING-$(CONFIG_FTPPUT) += ftpgetput.o | 29 | NETWORKING-$(CONFIG_FTPPUT) += ftpgetput.o |
29 | NETWORKING-$(CONFIG_HOSTNAME) += hostname.o | 30 | NETWORKING-$(CONFIG_HOSTNAME) += hostname.o |
diff --git a/networking/fakeidentd.c b/networking/fakeidentd.c new file mode 100644 index 000000000..560f6bab0 --- /dev/null +++ b/networking/fakeidentd.c | |||
@@ -0,0 +1,441 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * A fake identd server | ||
4 | * | ||
5 | * Adapted to busybox by Thomas Lundquist <thomasez@zelow.no> | ||
6 | * Original Author: Tomi Ollila <too@iki.fi> | ||
7 | * http://www.guru-group.fi/~too/sw/ | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License as published by | ||
11 | * the Free Software Foundation; either version 2 of the License, or | ||
12 | * (at your option) any later version. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
17 | * General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software | ||
21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
22 | * | ||
23 | */ | ||
24 | |||
25 | #include <unistd.h> | ||
26 | #include <stdio.h> | ||
27 | #include <stdlib.h> | ||
28 | #include <stdarg.h> | ||
29 | #include <string.h> | ||
30 | #include <fcntl.h> | ||
31 | #include <signal.h> | ||
32 | #include <sys/syslog.h> | ||
33 | |||
34 | #include <pwd.h> | ||
35 | #include <netdb.h> | ||
36 | |||
37 | #include <sys/syslog.h> | ||
38 | #include <sys/types.h> | ||
39 | #include <sys/time.h> | ||
40 | #include <time.h> | ||
41 | #include <sys/socket.h> | ||
42 | #include <netinet/in.h> | ||
43 | #include <errno.h> | ||
44 | #include <arpa/inet.h> | ||
45 | #include <sys/uio.h> | ||
46 | |||
47 | #include "busybox.h" | ||
48 | |||
49 | #define IDENT_PORT 113 | ||
50 | #define MAXCONNS 20 | ||
51 | #define MAXIDLETIME 45 | ||
52 | |||
53 | static const char ident_substr[] = " : USERID : UNIX : "; | ||
54 | static const int ident_substr_len = sizeof(ident_substr) - 1; | ||
55 | #define PIDFILE "/var/run/identd.pid" | ||
56 | |||
57 | /* | ||
58 | * We have to track the 'first connection socket' so that we | ||
59 | * don't go around closing file descriptors for non-clients. | ||
60 | * | ||
61 | * descriptor setup normally | ||
62 | * 0 = server socket | ||
63 | * 1 = syslog fd (hopefully -- otherwise this won't work) | ||
64 | * 2 = connection socket after detached from tty. standard error before that | ||
65 | * 3 - 2 + MAXCONNS = rest connection sockets | ||
66 | * | ||
67 | * To try to make sure that syslog fd is what is "requested", the that fd | ||
68 | * is closed before openlog() call. It can only severely fail if fd 0 | ||
69 | * is initially closed. | ||
70 | */ | ||
71 | #define FCS 2 | ||
72 | |||
73 | /* | ||
74 | * FD of the connection is always the index of the connection structure | ||
75 | * in `conns' array + FCS | ||
76 | */ | ||
77 | struct { | ||
78 | char buf[20]; | ||
79 | int len; | ||
80 | time_t lasttime; | ||
81 | } conns[MAXCONNS]; | ||
82 | |||
83 | /* When using global variables, bind those at least to a structure. */ | ||
84 | struct { | ||
85 | const char *identuser; | ||
86 | fd_set readfds; | ||
87 | int conncnt; | ||
88 | } G; | ||
89 | |||
90 | /* | ||
91 | * Prototypes | ||
92 | */ | ||
93 | static void reply(int s, char *buf); | ||
94 | static void replyError(int s, char *buf); | ||
95 | |||
96 | static const char *nobodystr = "nobody"; /* this needs to be declared like this */ | ||
97 | static char *bind_ip_address = "0.0.0.0"; | ||
98 | |||
99 | static inline void movefd(int from, int to) | ||
100 | { | ||
101 | if (from != to) { | ||
102 | dup2(from, to); | ||
103 | close(from); | ||
104 | } | ||
105 | } | ||
106 | |||
107 | static void inetbind(void) | ||
108 | { | ||
109 | int s, port; | ||
110 | struct sockaddr_in addr; | ||
111 | int len = sizeof(addr); | ||
112 | int one = 1; | ||
113 | struct servent *se; | ||
114 | |||
115 | if ((se = getservbyname("identd", "tcp")) == NULL) | ||
116 | port = IDENT_PORT; | ||
117 | else | ||
118 | port = se->s_port; | ||
119 | |||
120 | if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) | ||
121 | bb_perror_msg_and_die("Cannot create server socket"); | ||
122 | |||
123 | setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); | ||
124 | |||
125 | memset(&addr, 0, sizeof(addr)); | ||
126 | addr.sin_addr.s_addr = inet_addr(bind_ip_address); | ||
127 | addr.sin_family = AF_INET; | ||
128 | addr.sin_port = htons(port); | ||
129 | |||
130 | if (bind(s, (struct sockaddr *)&addr, len) < 0) | ||
131 | bb_perror_msg_and_die("Cannot bind() port %i", IDENT_PORT); | ||
132 | |||
133 | if (listen(s, 5) < 0) | ||
134 | bb_perror_msg_and_die("Cannot listen() on port %i", IDENT_PORT); | ||
135 | |||
136 | movefd(s, 0); | ||
137 | } | ||
138 | |||
139 | static void delpidfile(void) | ||
140 | { | ||
141 | /* | ||
142 | * Usually nobody has no write/delete access to directory /var/run/ | ||
143 | * therefore if file cannot be deleted, it is truncated | ||
144 | */ | ||
145 | if (unlink(PIDFILE) < 0) | ||
146 | close(open(PIDFILE, O_WRONLY|O_CREAT|O_TRUNC, 0644)); | ||
147 | } | ||
148 | |||
149 | static void handlexitsigs(int signum) | ||
150 | { | ||
151 | delpidfile(); | ||
152 | exit(0); | ||
153 | } | ||
154 | |||
155 | /* May succeed. If not, won't care. */ | ||
156 | static void writepid(uid_t nobody, uid_t nogrp) | ||
157 | { | ||
158 | char buf[24]; | ||
159 | int fd = open(PIDFILE, O_WRONLY|O_CREAT|O_TRUNC, 0664); | ||
160 | |||
161 | if (fd < 0) | ||
162 | return; | ||
163 | |||
164 | snprintf(buf, 23, "%d\n", getpid()); | ||
165 | write(fd, buf, strlen(buf)); | ||
166 | fchown(fd, nobody, nogrp); | ||
167 | close(fd); | ||
168 | |||
169 | /* should this handle ILL, ... (see signal(7)) */ | ||
170 | signal(SIGTERM, handlexitsigs); | ||
171 | signal(SIGINT, handlexitsigs); | ||
172 | signal(SIGQUIT, handlexitsigs); | ||
173 | } | ||
174 | |||
175 | /* return 0 as parent, 1 as child */ | ||
176 | static int godaemon(void) | ||
177 | { | ||
178 | uid_t nobody, nogrp; | ||
179 | struct passwd *pw; | ||
180 | |||
181 | switch (fork()) { | ||
182 | case -1: | ||
183 | bb_perror_msg_and_die("Could not fork"); | ||
184 | |||
185 | case 0: | ||
186 | pw = getpwnam(nobodystr); | ||
187 | if (pw == NULL) | ||
188 | bb_error_msg_and_die("Cannot find uid/gid of user '%s'", nobodystr); | ||
189 | nobody = pw->pw_uid; | ||
190 | nogrp = pw->pw_gid; | ||
191 | writepid(nobody, nogrp); | ||
192 | |||
193 | close(0); | ||
194 | inetbind(); | ||
195 | if (setgid(nogrp)) bb_error_msg_and_die("Could not setgid()"); | ||
196 | if (setegid(nogrp)) bb_error_msg_and_die("Could not setegid()"); | ||
197 | if (setuid(nobody)) bb_error_msg_and_die("Could not setuid()"); | ||
198 | if (seteuid(nobody)) bb_error_msg_and_die("Could not seteuid()"); | ||
199 | close(1); | ||
200 | close(2); | ||
201 | |||
202 | signal(SIGHUP, SIG_IGN); | ||
203 | signal(SIGPIPE, SIG_IGN); /* connection closed when writing (raises ???) */ | ||
204 | |||
205 | setsid(); | ||
206 | |||
207 | openlog(bb_applet_name, 0, LOG_DAEMON); | ||
208 | return 1; | ||
209 | } | ||
210 | |||
211 | return 0; | ||
212 | } | ||
213 | |||
214 | static void deleteConn(int s) | ||
215 | { | ||
216 | int i = s - FCS; | ||
217 | |||
218 | close(s); | ||
219 | |||
220 | G.conncnt--; | ||
221 | |||
222 | /* | ||
223 | * Most of the time there is 0 connections. Most often that there | ||
224 | * is connections, there is just one connection. When this one connection | ||
225 | * closes, i == G.conncnt = 0 -> no copying. | ||
226 | * When there is more than one connection, the oldest connections closes | ||
227 | * earlier on average. When this happens, the code below starts copying | ||
228 | * the connection structure w/ highest index to the place which which is | ||
229 | * just deleted. This means that the connection structures are no longer | ||
230 | * in chronological order. I'd quess this means that when there is more | ||
231 | * than 1 connection, on average every other connection structure needs | ||
232 | * to be copied over the time all these connections are deleted. | ||
233 | */ | ||
234 | if (i != G.conncnt) { | ||
235 | memcpy(&conns[i], &conns[G.conncnt], sizeof(conns[0])); | ||
236 | movefd(G.conncnt + FCS, s); | ||
237 | } | ||
238 | |||
239 | FD_CLR(G.conncnt + FCS, &G.readfds); | ||
240 | } | ||
241 | |||
242 | static int closeOldest(void) | ||
243 | { | ||
244 | time_t min = conns[0].lasttime; | ||
245 | int idx = 0; | ||
246 | int i; | ||
247 | |||
248 | for (i = 1; i < MAXCONNS; i++) | ||
249 | if (conns[i].lasttime < min) | ||
250 | idx = i; | ||
251 | |||
252 | replyError(idx + FCS, "X-SERVER-TOO-BUSY"); | ||
253 | close(idx + FCS); | ||
254 | |||
255 | return idx; | ||
256 | } | ||
257 | |||
258 | static int checkInput(char *buf, int len, int l) | ||
259 | { | ||
260 | int i; | ||
261 | for (i = len; i < len + l; ++i) | ||
262 | if (buf[i] == '\n') | ||
263 | return 1; | ||
264 | return 0; | ||
265 | } | ||
266 | |||
267 | int fakeidentd_main(int argc, char **argv) | ||
268 | { | ||
269 | int flag; | ||
270 | |||
271 | memset(conns, 0, sizeof(conns)); | ||
272 | memset(&G, 0, sizeof(G)); | ||
273 | FD_ZERO(&G.readfds); | ||
274 | FD_SET(0, &G.readfds); | ||
275 | |||
276 | /* handle -b <ip> parameter */ | ||
277 | while ((flag = getopt(argc, argv, "b:")) != EOF) { | ||
278 | switch (flag) { | ||
279 | case 'b': | ||
280 | bind_ip_address = optarg; | ||
281 | break; | ||
282 | default: | ||
283 | bb_show_usage(); | ||
284 | } | ||
285 | } | ||
286 | /* handle optional REPLY STRING */ | ||
287 | if (optind < argc) | ||
288 | G.identuser = argv[optind]; | ||
289 | else | ||
290 | G.identuser = nobodystr; | ||
291 | |||
292 | /* daemonize and have the parent return */ | ||
293 | if (godaemon() == 0) | ||
294 | return 0; | ||
295 | |||
296 | while (1) { | ||
297 | fd_set rfds = G.readfds; | ||
298 | struct timeval tv = { 15, 0 }; | ||
299 | int i; | ||
300 | int tim = time(NULL); | ||
301 | |||
302 | select(G.conncnt + FCS, &rfds, NULL, NULL, G.conncnt? &tv: NULL); | ||
303 | |||
304 | for (i = G.conncnt - 1; i >= 0; i--) { | ||
305 | int s = i + FCS; | ||
306 | |||
307 | if (FD_ISSET(s, &rfds)) { | ||
308 | char *buf = conns[i].buf; | ||
309 | unsigned int len = conns[i].len; | ||
310 | unsigned int l; | ||
311 | |||
312 | if ((l = read(s, buf + len, sizeof(conns[0].buf) - len)) > 0) { | ||
313 | if (checkInput(buf, len, l)) { | ||
314 | reply(s, buf); | ||
315 | goto deleteconn; | ||
316 | } else if (len + l >= sizeof(conns[0].buf)) { | ||
317 | replyError(s, "X-INVALID-REQUEST"); | ||
318 | goto deleteconn; | ||
319 | } else { | ||
320 | conns[i].len += l; | ||
321 | } | ||
322 | } else { | ||
323 | goto deleteconn; | ||
324 | } | ||
325 | |||
326 | conns[i].lasttime = tim; | ||
327 | continue; | ||
328 | |||
329 | deleteconn: | ||
330 | deleteConn(s); | ||
331 | } else { | ||
332 | /* implement as time_after() in linux kernel sources ... */ | ||
333 | if (conns[i].lasttime + MAXIDLETIME <= tim) { | ||
334 | replyError(s, "X-TIMEOUT"); | ||
335 | deleteConn(s); | ||
336 | } | ||
337 | } | ||
338 | } | ||
339 | |||
340 | if (FD_ISSET(0, &rfds)) { | ||
341 | int s = accept(0, NULL, 0); | ||
342 | |||
343 | if (s < 0) { | ||
344 | if (errno != EINTR) /* EINTR */ | ||
345 | syslog(LOG_ERR, "accept: %s", strerror(errno)); | ||
346 | } else { | ||
347 | if (G.conncnt == MAXCONNS) | ||
348 | i = closeOldest(); | ||
349 | else | ||
350 | i = G.conncnt++; | ||
351 | |||
352 | movefd(s, i + FCS); /* move if not already there */ | ||
353 | FD_SET(i + FCS, &G.readfds); | ||
354 | |||
355 | conns[i].len = 0; | ||
356 | conns[i].lasttime = time(NULL); | ||
357 | } | ||
358 | } | ||
359 | } /* end of while(1) */ | ||
360 | |||
361 | return 0; | ||
362 | } | ||
363 | |||
364 | static int parseAddrs(char *ptr, char **myaddr, char **heraddr); | ||
365 | static void reply(int s, char *buf) | ||
366 | { | ||
367 | char *myaddr, *heraddr; | ||
368 | |||
369 | myaddr = heraddr = NULL; | ||
370 | |||
371 | if (parseAddrs(buf, &myaddr, &heraddr)) | ||
372 | replyError(s, "X-INVALID-REQUEST"); | ||
373 | else { | ||
374 | struct iovec iv[6]; | ||
375 | iv[0].iov_base = myaddr; iv[0].iov_len = strlen(myaddr); | ||
376 | iv[1].iov_base = ", "; iv[1].iov_len = 2; | ||
377 | iv[2].iov_base = heraddr; iv[2].iov_len = strlen(heraddr); | ||
378 | iv[3].iov_base = (void *)ident_substr; iv[3].iov_len = ident_substr_len; | ||
379 | iv[4].iov_base = (void *)G.identuser; iv[4].iov_len = strlen(G.identuser); | ||
380 | iv[5].iov_base = "\r\n"; iv[5].iov_len = 2; | ||
381 | writev(s, iv, 6); | ||
382 | } | ||
383 | } | ||
384 | |||
385 | static void replyError(int s, char *buf) | ||
386 | { | ||
387 | struct iovec iv[3]; | ||
388 | iv[0].iov_base = "0, 0 : ERROR : "; iv[0].iov_len = 15; | ||
389 | iv[1].iov_base = buf; iv[1].iov_len = strlen(buf); | ||
390 | iv[2].iov_base = "\r\n"; iv[2].iov_len = 2; | ||
391 | writev(s, iv, 3); | ||
392 | } | ||
393 | |||
394 | static int chmatch(char c, char *chars) | ||
395 | { | ||
396 | for (; *chars; chars++) | ||
397 | if (c == *chars) | ||
398 | return 1; | ||
399 | return 0; | ||
400 | } | ||
401 | |||
402 | static int skipchars(char **p, char *chars) | ||
403 | { | ||
404 | while (chmatch(**p, chars)) | ||
405 | (*p)++; | ||
406 | if (**p == '\r' || **p == '\n') | ||
407 | return 0; | ||
408 | return 1; | ||
409 | } | ||
410 | |||
411 | static int parseAddrs(char *ptr, char **myaddr, char **heraddr) | ||
412 | { | ||
413 | /* parse <port-on-server> , <port-on-client> */ | ||
414 | |||
415 | if (!skipchars(&ptr, " \t")) | ||
416 | return -1; | ||
417 | |||
418 | *myaddr = ptr; | ||
419 | |||
420 | if (!skipchars(&ptr, "1234567890")) | ||
421 | return -1; | ||
422 | |||
423 | if (!chmatch(*ptr, " \t,")) | ||
424 | return -1; | ||
425 | |||
426 | *ptr++ = '\0'; | ||
427 | |||
428 | if (!skipchars(&ptr, " \t,") ) | ||
429 | return -1; | ||
430 | |||
431 | *heraddr = ptr; | ||
432 | |||
433 | skipchars(&ptr, "1234567890"); | ||
434 | |||
435 | if (!chmatch(*ptr, " \n\r")) | ||
436 | return -1; | ||
437 | |||
438 | *ptr = '\0'; | ||
439 | |||
440 | return 0; | ||
441 | } | ||