Annotation of fam/fam/Listener.c++, Revision 1.1.1.1
1.1 trev 1: // Copyright (C) 1999 Silicon Graphics, Inc. All Rights Reserved.
2: //
3: // This program is free software; you can redistribute it and/or modify it
4: // under the terms of version 2 of the GNU General Public License as
5: // published by the Free Software Foundation.
6: //
7: // This program is distributed in the hope that it would be useful, but
8: // WITHOUT ANY WARRANTY; without even the implied warranty of
9: // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Further, any
10: // license provided herein, whether implied or otherwise, is limited to
11: // this program in accordance with the express provisions of the GNU
12: // General Public License. Patent licenses, if any, provided herein do not
13: // apply to combinations of this program with other product or programs, or
14: // any other product whatsoever. This program is distributed without any
15: // warranty that the program is delivered free of the rightful claim of any
16: // third person by way of infringement or the like. See the GNU General
17: // Public License for more details.
18: //
19: // You should have received a copy of the GNU General Public License along
20: // with this program; if not, write the Free Software Foundation, Inc., 59
21: // Temple Place - Suite 330, Boston MA 02111-1307, USA.
22:
23: #include "Listener.h"
24:
25: #include <assert.h>
26: #include <fcntl.h>
27: #include <stdlib.h>
28: #include <sys/types.h>
29: #include <netinet/in.h>
30: #include <arpa/inet.h> // for inet_ntoa
31: #include <rpc/rpc.h>
32: #include <rpc/pmap_clnt.h>
33: #include <rpc/clnt.h>
34: #include <sys/ioctl.h>
35: #include <sys/socket.h>
36: #include <sys/stat.h>
37: #include <sys/un.h>
38: #include <unistd.h>
39:
40: #include <errno.h>
41:
42: #include "Log.h"
43: #include "LocalClient.h"
44: #include "Scheduler.h"
45: #include "Cred.h"
46: #include "BTree.h"
47:
48: #if !(HAVE_BINDRESVPORT_PROTO)
49: extern "C" int bindresvport(int sd, struct sockaddr_in *);
50: #endif
51:
52: struct NegotiatingClient
53: {
54: NegotiatingClient(int fd, uid_t u, struct sockaddr_un *s);
55: int sock;
56: uid_t uid;
57: struct sockaddr_un sun;
58: };
59:
60: BTree<int, NegotiatingClient *> negotiating_clients;
61:
62: static void cleanup_negotiation(void *closure);
63:
64: Listener::Listener(bool sbi, bool lo, unsigned long p, unsigned long v)
65: : program(p),
66: version(v),
67: rendezvous_fd(-1),
68: started_by_inetd(sbi),
69: _ugly_sock(-1),
70: local_only(lo)
71: {
72: if (started_by_inetd)
73: {
74: // Portmapper already knows about
75: // us, so just wait for the requests to start rolling in.
76:
77: set_rendezvous_fd(RENDEZVOUS_FD);
78:
79: dirty_ugly_hack();
80: }
81: else
82: {
83: // Need to register with portmapper.
84: // Unless we're debugging, fork and close all descriptors.
85:
86: int sock = socket(PF_INET, SOCK_STREAM, 0);
87: if (sock < 0)
88: { Log::perror("can't create TCP/IP socket for rendezvous");
89: exit(1);
90: }
91: struct sockaddr_in addr;
92: memset(&addr, 0, sizeof addr);
93: addr.sin_family = AF_INET;
94: addr.sin_addr.s_addr = local_only ? htonl(INADDR_LOOPBACK) : 0;
95: addr.sin_port = htons(0);
96: if (bindresvport(sock, &addr) < 0)
97: {
98: Log::perror("can't bind to reserved port");
99: exit(1);
100: }
101: if (listen(sock, 1) < 0)
102: {
103: Log::perror("can't listen for rendezvous");
104: exit(1);
105: }
106: (void) pmap_unset(program, version);
107: if (!pmap_set(program, version, IPPROTO_TCP, ntohs(addr.sin_port)))
108: {
109: Log::error("can't register with portmapper.");
110: exit(1);
111: }
112: set_rendezvous_fd(sock);
113: }
114: }
115:
116: Listener::~Listener()
117: {
118: if (rendezvous_fd >= 0)
119: { if (!started_by_inetd)
120: pmap_unset(program, version);
121: int rc = close(rendezvous_fd);
122: if (rc < 0)
123: Log::perror("close rendezvous port(%d)", rendezvous_fd);
124: else
125: Log::debug("fam service closed");
126: (void) Scheduler::remove_read_handler(rendezvous_fd);
127: }
128: if (_ugly_sock >= 0)
129: { (void) close(_ugly_sock);
130: (void) unlink("/tmp/.fam_socket");
131: }
132: }
133:
134: void
135: Listener::set_rendezvous_fd(int newfd)
136: {
137: if (rendezvous_fd >= 0)
138: { (void) Scheduler::remove_read_handler(rendezvous_fd);
139: (void) close(rendezvous_fd);
140: }
141: rendezvous_fd = newfd;
142: if (rendezvous_fd >= 0)
143: { (void) Scheduler::install_read_handler(rendezvous_fd,
144: accept_client, this);
145: Log::debug("listening for clients on descriptor %d", rendezvous_fd);
146: }
147: }
148:
149: void
150: Listener::accept_client(int rendezvous_fd, void *closure)
151: {
152: Listener *listener = (Listener *)closure;
153:
154: // Get the new socket.
155:
156: struct sockaddr_in addr;
157: CONFIG_SOCKLEN_T addrlen = sizeof addr;
158: int client_fd = accept(rendezvous_fd, (struct sockaddr *) &addr, &addrlen);
159: if (client_fd < 0)
160: {
161: Log::perror("failed to accept new client");
162: return;
163: }
164:
165: if (ntohl(addr.sin_addr.s_addr) == INADDR_LOOPBACK)
166: {
167: Log::debug("client fd %d is local/untrusted.", client_fd);
168: // Client is local.
169:
170: Cred cred = Cred::get_cred_for_untrusted_conn(client_fd);
171: new TCP_Client(addr.sin_addr, client_fd, cred);
172: // We don't need a reference to this object. The constructor
173: // takes care of registering it with the Scheduler.
174: }
175: else
176: {
177: assert(!listener->local_only);
178:
179: // Check that client is superuser.
180: Cred cred;
181: if (ntohs(addr.sin_port) >= IPPORT_RESERVED)
182: {
183: // Client isn't connecting on a privileged port, so we will
184: // ignore who they say they are, and treat them as our
185: // untrusted-user.
186: cred = Cred::get_cred_for_untrusted_conn(client_fd);
187: }
188: Log::debug("client fd %d from %s is remote/%strusted",
189: client_fd, inet_ntoa(addr.sin_addr),
190: cred.is_valid() ? "un" : "");
191:
192: new TCP_Client(addr.sin_addr, client_fd, cred);
193: // We don't need a reference to this object. The constructor
194: // takes care of registering it with the Scheduler.
195: }
196: }
197:
198: void
199: Listener::create_local_client(TCP_Client &inet_client, uid_t uid)
200: {
201: // This TCP_Client wants to use a UNIX domain socket instead of the
202: // inet socket for communication. Create the new socket owned by the
203: // requested user and pass the name back to the client.
204:
205: // Unset TMPDIR to ensure that tempnam() works as desired
206: putenv("TMPDIR=");
207:
208: char *tmpfile = tempnam("/tmp", ".fam");
209: #if defined(__FreeBSD__)
210: sockaddr_un sun = { sizeof(sockaddr_un), AF_UNIX, "" };
211: #else
212: sockaddr_un sun = { AF_UNIX, "" };
213: #endif
214: if(strlen(tmpfile) >= (sizeof(sun.sun_path) - 1))
215: {
216: Log::error("tmpnam() too long for sun_path (%d >= %d)!",
217: strlen(tmpfile), sizeof(sun.sun_path) - 1);
218: free(tmpfile);
219: return;
220: }
221: strcpy(sun.sun_path, tmpfile);
222: free(tmpfile);
223:
224: Cred::SuperUser.become_user();
225: int client_sock = socket(PF_UNIX, SOCK_STREAM, 0);
226: if (client_sock < 0)
227: { Log::perror("localclient socket(PF_UNIX, SOCK_STREAM, 0)");
228: return;
229: }
230:
231: Log::debug("client %s said uid %d; creating %s",
232: inet_client.name(), uid, sun.sun_path);
233:
234: unlink(sun.sun_path);
235: if (bind(client_sock, (sockaddr *) &sun, sizeof sun) != 0)
236: { Log::perror("localclient bind");
237: close(client_sock);
238: return;
239: }
240:
241: if (chmod(sun.sun_path, 0600) != 0)
242: { Log::perror("localclient chmod");
243: close(client_sock);
244: return;
245: }
246:
247: if (chown(sun.sun_path, uid, (gid_t)-1) != 0)
248: { Log::perror("localclient chown");
249: close(client_sock);
250: return;
251: }
252:
253: // Since we're going to start listening on this socket, set a task
254: // to clean it up if we don't receive a connection within 60 seconds.
255: NegotiatingClient *nc = new NegotiatingClient(client_sock, uid, &sun);
256: negotiating_clients.insert(client_sock, nc);
257: timeval nto;
258: gettimeofday(&nto, NULL);
259: nto.tv_sec += 60; // XXX that should be configurable
260: Scheduler::install_onetime_task(nto, cleanup_negotiation, nc);
261:
262: if (listen(client_sock, 1) != 0)
263: { Log::perror("localclient listen");
264: close(client_sock);
265: return;
266: }
267:
268: Scheduler::install_read_handler(client_sock, accept_localclient, NULL);
269: Log::debug("listening for requests for uid %d on descriptor %d (%s)",
270: uid, client_sock, sun.sun_path);
271:
272: inet_client.send_sockaddr_un(sun);
273: }
274:
275: void
276: Listener::accept_localclient(int ofd, void *)
277: {
278: NegotiatingClient *nc = negotiating_clients.find(ofd);
279: assert(nc);
280:
281: // Get the new socket.
282:
283: #if defined(__FreeBSD__)
284: struct sockaddr_un sun = { sizeof(sockaddr_un), AF_UNIX, "" };
285: #else
286: struct sockaddr_un sun = { AF_UNIX, "" };
287: #endif
288: CONFIG_SOCKLEN_T sunlen = sizeof(sun);
289: int client_fd = accept(ofd, (struct sockaddr *) &sun, &sunlen);
290: if (client_fd < 0)
291: {
292: Log::perror("failed to accept new client");
293: return;
294: }
295:
296: // Keep the scheduler from helpfully cleaning this up.
297: Scheduler::remove_onetime_task(cleanup_negotiation, nc);
298:
299: Log::debug("client fd %d is local/trusted (socket %s, uid %d).",
300: client_fd, nc->sun.sun_path, nc->uid);
301: Cred cred(nc->uid, client_fd);
302: new LocalClient(client_fd, &(nc->sun), cred);
303: // We don't need a reference to this object. The constructor
304: // takes care of registering it with the Scheduler.
305:
306: // Stop listening for new connections on that socket.
307: Scheduler::remove_read_handler(ofd);
308: close(ofd);
309:
310: // This client is no longer negotiating, so remove it from the list
311: // and delete it.
312: negotiating_clients.remove(ofd);
313: delete nc;
314: }
315:
316: // Here's the problem. If the network is stopped and restarted,
317: // inetd and portmapper are killed and new ones are launched in their
318: // place. Since they have no idea that fam is already running, inetd
319: // opens a new TCP/IP socket and registers it with the new portmapper.
320: // Meanwhile, the old fam has /dev/imon open, and any new fam launched
321: // by the new inetd won't be able to use imon. The old fam can't just
322: // quit; it has all the state of all its existing clients to contend with.
323:
324: // So here's the dirty, ugly, hack solution. The first fam to run is
325: // the master. The master opens the UNIX domain socket,
326: // /tmp/.fam_socket, and listens for connections from slaves. Each
327: // subsequent fam is a slave. It knows it's a slave because it can
328: // connect to the master. The slave passes its rendezvous descriptor
329: // (the one that fam clients will connect to) through to the master
330: // using the access rights mechanism of Unix domain sockets. The
331: // master receives the new descriptor and starts accepting clients on
332: // it. Meanwhile, the slave blocks, waiting for the master to close
333: // the connection on /tmp/.fam_socket. That happens when the master
334: // exits.
335:
336: // This master/slave stuff has nothing to do with fam forwarding
337: // requests to fams on other hosts. That's called client/server
338: // fams.
339:
340: // Why not just kill fam when we kill the rest of the network
341: // services? Because too many programs need it. The desktop, the
342: // desktop sysadmin tools, MediaMail... None of those should go down
343: // when the network goes down, and none of them are designed to
344: // handle fam dying.
345:
346: void
347: Listener::dirty_ugly_hack()
348: {
349: #if defined(__FreeBSD__)
350: static sockaddr_un sun = { sizeof (sockaddr_un), AF_UNIX, "/tmp/.fam_socket" };
351: #else
352: static sockaddr_un sun = { AF_UNIX, "/tmp/.fam_socket" };
353: #endif
354:
355: int sock = socket(PF_UNIX, SOCK_STREAM, 0);
356: if (sock < 0)
357: { Log::perror("socket(PF_UNIX, SOCK_STREAM, 0)");
358: exit(1);
359: }
360:
361: struct stat sb;
362: if (lstat(sun.sun_path, &sb) == 0 &&
363: sb.st_uid == 0 && S_ISSOCK(sb.st_mode))
364: {
365: if (connect(sock, (sockaddr *) &sun, sizeof sun) == 0)
366: {
367: // Another fam is listening to /tmp/.fam_socket.
368: // Pass our rendezvous fd to the other fam and
369: // sleep until that fam exits.
370:
371: int rights[1] = { rendezvous_fd };
372: msghdr msg = { NULL, 0, NULL, 0, (caddr_t) rights, sizeof rights };
373: if (sendmsg(sock, &msg, 0) < 0)
374: { Log::perror("sendmsg");
375: exit(1);
376: }
377: Log::debug("fam (process %d) enslaved to master fam", getpid());
378: char data[1];
379: int nb = read(sock, &data, sizeof data);
380: assert(nb == 0);
381: exit(0);
382: }
383: else
384: Log::debug("could not enslave myself: %m");
385: }
386:
387: // We were unable to connect to another fam.
388: // We'll create our own dirty ugly hack socket and accept connections.
389:
390: (void) unlink(sun.sun_path);
391: if (bind(sock, (sockaddr *) &sun, sizeof sun) != 0)
392: { Log::perror("bind");
393: exit(1);
394: }
395:
396: if (chmod(sun.sun_path, 0700) != 0)
397: { Log::perror("chmod");
398: exit(1);
399: }
400:
401: if (listen(sock, 1) != 0)
402: { Log::perror("listen");
403: exit(1);
404: }
405:
406: (void) Scheduler::install_read_handler(sock, accept_ugly_hack, this);
407: _ugly_sock = sock;
408: Log::debug("fam (process %d) is master fam.", getpid());
409: }
410:
411: void
412: Listener::accept_ugly_hack(int ugly, void *closure)
413: {
414: Listener *listener = (Listener *) closure;
415: assert(ugly == listener->_ugly_sock);
416:
417: // Accept a new ugly connection.
418:
419: struct sockaddr_un sun;
420: CONFIG_SOCKLEN_T sunlen = sizeof sun;
421: int sock = accept(ugly, (struct sockaddr *)(&sun), &sunlen);
422: if (sock < 0)
423: { Log::perror("accept");
424: return;
425: }
426:
427: (void) Scheduler::install_read_handler(sock, read_ugly_hack, listener);
428: }
429:
430: void
431: Listener::read_ugly_hack(int sock, void *closure)
432: {
433: Listener *listener = (Listener *) closure;
434:
435: int rights[1] = { -1 };
436: msghdr msg = { NULL, 0, NULL, 0, (caddr_t) rights, sizeof rights };
437: if (recvmsg(sock, &msg, 0) >= 0)
438: {
439: Log::debug("master fam (process %d) has new slave", getpid());
440:
441: assert(rights[0] >= 0);
442: listener->set_rendezvous_fd(rights[0]);
443:
444: // Turn the inactivity timeout all the way down. We want to
445: // clean up this dirty ugly hack as soon as possible after
446: // the last Activity is destroyed.
447: Activity::timeout(0);
448:
449: // Forget socket -- it will be closed on exit.
450: // Slave is blocked waiting for socket to close, so slave
451: // will exit when master exits.
452:
453: (void) Scheduler::remove_read_handler(sock);
454: }
455: else
456: { Log::perror("recvmsg");
457: (void) Scheduler::remove_read_handler(sock);
458: (void) close(sock);
459: }
460: }
461:
462: NegotiatingClient::NegotiatingClient(int fd, uid_t u, struct sockaddr_un *sunp)
463: : sock(fd), uid(u)
464: {
465: sun.sun_family = AF_UNIX;
466: strcpy(sun.sun_path, sunp->sun_path);
467: }
468:
469: static void
470: cleanup_negotiation(void *closure)
471: {
472: NegotiatingClient *nc = (NegotiatingClient *)closure;
473:
474: Log::debug("cleaning up incomplete negotiation for client %d", nc->sock);
475:
476: // Remove client from list
477: negotiating_clients.remove(nc->sock);
478:
479: // Remove the read handler & close the socket
480: Scheduler::remove_read_handler(nc->sock);
481: close(nc->sock);
482: nc->sock = -1;
483:
484: // Remove the temp file
485: uid_t preveuid = geteuid();
486: if (preveuid) seteuid(0);
487: seteuid(nc->uid);
488: unlink(nc->sun.sun_path);
489: if (nc->uid) seteuid(0);
490: seteuid(preveuid);
491:
492: delete nc;
493: }
494:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>