1 /******************************************************************************
2 Copyright (c) 1992, 1995, 1996 Xerox Corporation. All rights reserved.
3 Portions of this code were written by Stephen White, aka ghond.
4 Use and copying of this software and preparation of derivative works based
5 upon this software are permitted. Any distribution of this software or
6 derivative works must comply with all applicable United States export
7 control laws. This software is made available AS IS, and Xerox Corporation
8 makes no warranty about the software, its performance or its conformity to
9 any specification. Any person obtaining a copy of this software is requested
10 to send their name and post office or electronic mail address to:
11 Pavel Curtis
12 Xerox PARC
13 3333 Coyote Hill Rd.
14 Palo Alto, CA 94304
15 Pavel@Xerox.Com
16 *****************************************************************************/
17
18 /* Multi-user networking protocol implementation for local clients on SysV UNIX
19
20 * The protocol for connection establishment works like this:
21 *
22 * CLIENT SERVER
23 * Create SERVER_FIFO (mode 622).
24 * Open SERVER_FIFO for reading (O_NDELAY)
25 * Wait for input on SERVER_FIFO
26 * ...
27 * Create C2S_FIFO (mode 644)
28 * and S2C_FIFO (mode 622) in
29 * some personal directory
30 * (like $HOME).
31 * Open S2C_FIFO for reading
32 * (O_NDELAY).
33 * Open SERVER_FIFO for writing
34 * (no O_NDELAY), write
35 * '\n' C2S_FIFO ' ' S2C_FIFO '\n'
36 * in a single write() call, and
37 * then close SERVER_FIFO.
38 * Open C2S_FIFO for writing Read C2S_FIFO and S2C_FIFO from
39 * (*no* O_NDELAY). SERVER_FIFO.
40 * Open S2C_FIFO for writing (O_NDELAY)
41 * and abort connection on error.
42 * Open C2S_FIFO for reading (O_NDELAY)
43 * and abort connection on error.
44 * Unlink C2S_FIFO and S2C_FIFO
45 * for privacy; their names
46 * are no longer needed.
47 * ...
48 * Close SERVER_FIFO and unlink it.
49 *
50 * It is important that the client do the above actions in the given order;
51 * no other sequence (with the sole exception of the timing of the opening
52 * and closing of SERVER_FIFO) will accomplish a successful connection.
53 *
54 * The unlinking of the client's two FIFOs makes it impossible for someone
55 * else thereafter to either spoof input or divert output in the name of the
56 * client. There is, of course, a brief window during which the names exist
57 * and a spoofer could intervene, but this appears to be unavoidable.
58 *
59 * The extra newline at the beginning of the client's connection request on
60 * SERVER_FIFO is there to make the request parsable even in the presence of
61 * random extra garbage already in the FIFO, thus thwarting a denial-of-service
62 * attack on the server.
63 *
64 * The server makes use of information about the client's FIFOs (i.e., their
65 * owner) to identify the connection for humans. For example, the user name
66 * appears in the server's log and is available to wizards via the MOO
67 * function connection_name(player). Caveat Emptor.
68 */
69
70 #include <errno.h> /* EMFILE */
71 #include "my-fcntl.h" /* open(), O_RDONLY, O_WRONLY, NONBLOCK_FLAG */
72 #include <pwd.h> /* struct passwd, getpwuid() */
73 #include "my-stat.h" /* S_IFIFO, fstat(), mkfifo() */
74 #include "my-stdio.h" /* remove() */
75 #include "my-stdlib.h" /* exit() */
76 #include "my-unistd.h" /* chmod(), close(), pipe(), read(), write() */
77
78 #include "config.h"
79 #include "exceptions.h"
80 #include "list.h"
81 #include "log.h"
82 #include "net_multi.h"
83 #include "net_proto.h"
84 #include "storage.h"
85 #include "streams.h"
86 #include "utils.h"
87
88 enum state {
89 RejectLine, GetC2S, GetS2C, Accepting
90 };
91
92 typedef struct listener {
93 struct listener *next;
94 int fifo, pseudo_client;
95 const char *filename;
96 enum state state;
97 char c2s[1001], s2c[1001];
98 int ptr; /* current index in c2s or s2c */
99 } listener;
100
101 static listener *all_listeners = 0;
102
103 const char *
proto_name(void)104 proto_name(void)
105 {
106 return "FIFO";
107 }
108
109 const char *
proto_usage_string(void)110 proto_usage_string(void)
111 {
112 return "[server-connect-file]";
113 }
114
115 int
proto_initialize(struct proto * proto,Var * desc,int argc,char ** argv)116 proto_initialize(struct proto *proto, Var * desc, int argc, char **argv)
117 {
118 const char *connect_file = DEFAULT_CONNECT_FILE;
119
120 proto->pocket_size = 2;
121 #if POSIX_NONBLOCKING_WORKS
122 /* With POSIX-style nonblocking, we'll win */
123 proto->believe_eof = 1;
124 #else
125 proto->believe_eof = 0;
126 #endif
127 proto->eol_out_string = "\n";
128
129 if (argc > 1)
130 return 0;
131 else if (argc == 1) {
132 connect_file = argv[0];
133 }
134 desc->type = TYPE_STR;
135 desc->v.str = str_dup(connect_file);
136 return 1;
137 }
138
139 enum error
proto_make_listener(Var desc,int * fd,Var * canon,const char ** name)140 proto_make_listener(Var desc, int *fd, Var * canon, const char **name)
141 {
142 char buffer[1024];
143 const char *connect_file;
144 int fifo, pseudo_client;
145 listener *l;
146
147 if (desc.type != TYPE_STR)
148 return E_TYPE;
149
150 connect_file = desc.v.str;
151 if (mkfifo(connect_file, 0600) < 0) {
152 sprintf(buffer, "Creating listening FIFO `%s'", connect_file);
153 log_perror(buffer);
154 return E_QUOTA;
155 } else if ((fifo = open(connect_file, O_RDONLY | NONBLOCK_FLAG)) < 0) {
156 log_perror("Opening listening FIFO");
157 return E_QUOTA;
158 } else if ((pseudo_client = open(connect_file, O_WRONLY)) < 0) {
159 log_perror("Opening pseudo-client");
160 return E_QUOTA;
161 } else if (!network_set_nonblocking(fifo)) {
162 log_perror("Setting listening FIFO non-blocking");
163 return E_QUOTA;
164 }
165 l = mymalloc(sizeof(listener), M_NETWORK);
166 l->next = all_listeners;
167 all_listeners = l;
168 l->filename = str_dup(connect_file);
169 l->fifo = fifo;
170 l->pseudo_client = pseudo_client;
171 l->state = GetC2S;
172 l->ptr = 0;
173
174 *fd = fifo;
175 *canon = var_ref(desc);
176 *name = l->filename;
177 return E_NONE;
178 }
179
180 static listener *
find_listener(int fd)181 find_listener(int fd)
182 {
183 listener *l;
184
185 for (l = all_listeners; l; l = l->next)
186 if (l->fifo == fd)
187 return l;
188
189 return 0;
190 }
191
192 int
proto_listen(int fd)193 proto_listen(int fd)
194 {
195 listener *l = find_listener(fd);
196
197 if (l) {
198 if (chmod(l->filename, 0622) < 0) {
199 log_perror("Making listening FIFO writable");
200 return 0;
201 }
202 return 1;
203 }
204 errlog("Can't find FIFO in PROTO_LISTEN!");
205 return 0;
206 }
207
208 enum proto_accept_error
proto_accept_connection(int listener_fd,int * read_fd,int * write_fd,const char ** name)209 proto_accept_connection(int listener_fd, int *read_fd, int *write_fd,
210 const char **name)
211 {
212 /* There is input available on listener_fd; read up to 1K of it and try
213 * to parse a line like this from it:
214 * <c2s-path-name> <space> <s2c-path-name> <newline>
215 * Because it's impossible in System V and can be difficult in POSIX to do
216 * otherwise, we assume that the maximum length of a path-name is 1000
217 * bytes. In fact, because System V and POSIX only guarantee that 512
218 * bytes can be written atomically on a pipe or FIFO, and since the client
219 * has to get two path-names, two newlines, and a space atomically into the
220 * FIFO, it really ought not use names longer than about 250 bytes each.
221 * Of course, in practice, the names will likely never exceed 100 bytes...
222 */
223 listener *l = find_listener(listener_fd);
224 struct stat st1, st2;
225 struct passwd *pw;
226
227 if (l->state != Accepting) {
228 int got_one = 0;
229
230 while (!got_one) {
231 char c;
232
233 if (read(l->fifo, &c, 1) != 1)
234 break;
235
236 switch (l->state) {
237 case RejectLine:
238 if (c == '\n') {
239 l->state = GetC2S;
240 l->ptr = 0;
241 }
242 break;
243
244 case GetC2S:
245 if (c == ' ') {
246 l->c2s[l->ptr] = '\0';
247 l->state = GetS2C;
248 l->ptr = 0;
249 } else if (c == '\n') {
250 if (l->ptr != 0)
251 errlog("Missing FIFO name(s) on listening FIFO\n");
252 l->ptr = 0;
253 } else if (l->ptr == 1000) {
254 errlog("Overlong line on server FIFO\n");
255 l->state = RejectLine;
256 } else
257 l->c2s[l->ptr++] = c;
258 break;
259
260 case GetS2C:
261 if (c == '\n') {
262 l->s2c[l->ptr] = '\0';
263 l->state = Accepting;
264 l->ptr = 0;
265 got_one = 1;
266 } else if (c == ' ' || l->ptr == 1000) {
267 errlog("Overlong or malformed line on listening FIFO\n");
268 l->state = RejectLine;
269 } else
270 l->s2c[l->ptr++] = c;
271 break;
272
273 default:
274 panic("Can't happen in proto_accept_connection()");
275 }
276 }
277
278 if (!got_one) {
279 errlog("Unproductive call to proto_accept_connection()\n");
280 return PA_OTHER;
281 }
282 }
283 if ((*write_fd = open(l->s2c, O_WRONLY | NONBLOCK_FLAG)) < 0) {
284 log_perror("Failed to open server->client FIFO");
285 if (errno == EMFILE)
286 return PA_FULL;
287 else {
288 l->state = GetC2S;
289 return PA_OTHER;
290 }
291 }
292 if ((*read_fd = open(l->c2s, O_RDONLY | NONBLOCK_FLAG)) < 0) {
293 log_perror("Failed to open server->client FIFO");
294 close(*write_fd);
295 if (errno == EMFILE)
296 return PA_FULL;
297 else {
298 l->state = GetC2S;
299 return PA_OTHER;
300 }
301 }
302 l->state = GetC2S;
303
304 if (fstat(*read_fd, &st1) < 0 || fstat(*write_fd, &st2) < 0) {
305 log_perror("Statting client FIFOs");
306 return PA_OTHER;
307 }
308 if (st1.st_mode & S_IFMT != S_IFIFO
309 || st2.st_mode & S_IFMT != S_IFIFO
310 || st1.st_uid != st2.st_uid) {
311 close(*read_fd);
312 close(*write_fd);
313 errlog("Bogus FIFO names: \"%s\" and \"%s\"\n", l->c2s, l->s2c);
314 return PA_OTHER;
315 }
316 pw = getpwuid(st1.st_uid);
317 if (pw)
318 *name = pw->pw_name;
319 else {
320 static char buffer[20];
321
322 sprintf(buffer, "User #%d", (int) st1.st_uid);
323 *name = buffer;
324 }
325
326 return PA_OKAY;
327 }
328
329 void
proto_close_connection(int read_fd,int write_fd)330 proto_close_connection(int read_fd, int write_fd)
331 {
332 write(write_fd, "\0", 1); /* Tell client we're shutting down. */
333 close(read_fd);
334 close(write_fd);
335 }
336
337 void
proto_close_listener(int fd)338 proto_close_listener(int fd)
339 {
340 listener *l, **ll;
341
342 for (l = all_listeners, ll = &all_listeners; l; ll = &(l->next),
343 l = l->next)
344 if (l->fifo == fd) {
345 remove(l->filename);
346 close(l->fifo);
347 close(l->pseudo_client);
348
349 *ll = l->next;
350 free_str(l->filename);
351 myfree(l, M_NETWORK);
352 return;
353 }
354 errlog("Can't find fd in PROTO_CLOSE_LISTENER!\n");
355 }
356
357 char rcsid_net_sysv_lcl[] = "$Id: net_sysv_lcl.c,v 1.2 1997/03/03 04:19:08 nop Exp $";
358
359 /* $Log: net_sysv_lcl.c,v $
360 /* Revision 1.2 1997/03/03 04:19:08 nop
361 /* GNU Indent normalization
362 /*
363 * Revision 1.1.1.1 1997/03/03 03:45:02 nop
364 * LambdaMOO 1.8.0p5
365 *
366 * Revision 2.4 1996/03/10 01:13:11 pavel
367 * Moved definition of DEFAULT_CONNECT_FILE to options.h. Release 1.8.0.
368 *
369 * Revision 2.3 1996/02/08 06:34:28 pavel
370 * Renamed err/logf() to errlog/oklog(). Updated copyright notice for 1996.
371 * Release 1.8.0beta1.
372 *
373 * Revision 2.2 1995/12/30 23:59:02 pavel
374 * Added support for the new multiple-listening-points interface.
375 * Release 1.8.0alpha4.
376 *
377 * Revision 2.1 1995/12/28 00:34:29 pavel
378 * Removed old support for protocol-specific input EOL conventions.
379 * Release 1.8.0alpha3.
380 *
381 * Revision 2.0 1995/11/30 04:47:19 pavel
382 * New baseline version, corresponding to release 1.8.0alpha1.
383 *
384 * Revision 1.10 1993/12/13 18:08:41 pavel
385 * -- Updated to provide new proto_listen() return value.
386 * -- Added the file name to the `creating server FIFO' message.
387 *
388 * Revision 1.9 1992/10/23 23:03:47 pavel
389 * Added copyright notice.
390 *
391 * Revision 1.8 1992/10/23 22:01:46 pavel
392 * -- #includes "my-stat.h" instead of <sys/stat.h>.
393 * -- Moved macro definition of mkfifo() to my-stat.h.
394 * -- Changed to conditionalize on POSIX_NONBLOCKING_WORKS instead of
395 * defined(O_NONBLOCK).
396 * -- Now opens its own `write'-side port on the server FIFO, so that the
397 * server FIFO won't always be input-ready (a read() would always
398 * immediately return EOF if no writers existed).
399 * -- No longer assumes that fstat() shows how much data is in the FIFO.
400 *
401 * Revision 1.7 1992/10/21 03:02:35 pavel
402 * Converted to use new automatic configuration system.
403 *
404 * Revision 1.6 1992/10/17 20:46:04 pavel
405 * Changed to use NONBLOCK_FLAG instead of O_NDELAY to allow of using
406 * POSIX-style non-blocking on systems where it is available.
407 *
408 * Revision 1.5 1992/10/06 01:35:35 pavel
409 * Moved non-blocking code to net_multi.c, replacing it with code to set
410 * proto->believe_eof appropriately.
411 *
412 * Revision 1.4 1992/10/01 16:46:56 pavel
413 * Fixed state machine in proto_accept_connection so that a PA_FULL condition
414 * leaves things in such a state that the next call will try that same
415 * connection again. Also added more logging of unsuccessful connection
416 * attempts.
417 *
418 * Revision 1.3 1992/09/27 19:34:14 pavel
419 * Added RCS Id and Log fields.
420 */
421