Annotation of fam/fam/TCP_Client.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 "TCP_Client.h"
24:
25: #include <assert.h>
26: #include <ctype.h>
27: #include <stdio.h>
28: #include <stdlib.h>
29: #include <string.h>
30: #include <unistd.h>
31:
32: #include "Cred.h"
33: #include "Event.h"
34: #include "Interest.h"
35: #include "Log.h"
36: #include "Scanner.h"
37: #include "Listener.h"
38:
39: //////////////////////////////////////////////////////////////////////////////
40: // Construction/destruction
41:
42: TCP_Client::TCP_Client(in_addr host, int fd, Cred &cr)
43: : MxClient(host), cred(cr), my_scanner(NULL),
44: conn(fd, input_handler, unblock_handler, this),
45: insecure_compat_suggested(false)
46: {
47: assert(fd >= 0);
48:
49: // Set client's name.
50:
51: char namebuf[20];
52: sprintf(namebuf, "client %d", fd);
53: name(namebuf);
54: Log::debug("new connection from %s", name());
55: }
56:
57: TCP_Client::~TCP_Client()
58: {
59: }
60:
61: //////////////////////////////////////////////////////////////////////////////
62: // Input
63:
64: bool
65: TCP_Client::input_handler(const char *msg, unsigned nbytes, void *closure)
66: {
67: TCP_Client *client = (TCP_Client *) closure;
68: if (msg) {
69: return client->input_msg(msg, nbytes);
70: }
71: else
72: { Log::debug("lost connection from %s", client->name());
73: delete client; // NULL msg means connection closed.
74: return true;
75: }
76: }
77:
78: bool
79: TCP_Client::input_msg(const char *msg, int size)
80: {
81: // Parse the first message.
82: // The first message is:
83: // opcode, request number, uid, gid, file name.
84:
85: // If there's an error parsing the message, return false to cause
86: // the connection to the misbehaving client to be closed.
87:
88: bool got_N_with_groups = false;
89: char *p = (char *) msg, *q;
90: char opcode = *p++;
91: Request reqnum = strtol(p, &q, 10);
92: if (p == q)
93: {
94: Log::error("bad message (no request id)");
95: return false;
96: }
97: p = q;
98: uid_t uid = strtol(p, &q, 10);
99: if (p == q)
100: {
101: Log::error("bad message (no uid)");
102: return false;
103: }
104: p = q;
105: gid_t gid = strtol(p, &q, 10);
106: if (p == q)
107: {
108: Log::error("bad message (no gid)");
109: return false;
110: }
111: p = q;
112:
113: // Advance past the space
114: p++;
115:
116: char filename[PATH_MAX + 1];
117: int i;
118: for (i = 0; *p; i++)
119: { if (i >= PATH_MAX)
120: { Log::error("%s path name too long (%d chars)", name(), i);
121: return false;
122: }
123: filename[i] = *p++;
124: }
125: filename[i] = '\0';
126: if (i > 0 && filename[i - 1] == '\n')
127: filename[i - 1] = '\0'; // strip the trailing newline
128: p++;
129:
130: // Parse the second message, if any.
131: // The second message is:
132: // ngroups, group, group, group ...
133:
134: // This is a raging kludge. got_N_with_groups is how a client says
135: // "create a UNIX domain socket for local communication with me."
136: // You guessed that, right?
137: //
138: // The reason it's done this way, rather than by having fam listen
139: // for connections from local clients on a unix domain socket in addition
140: // to the internet domain socket, is that when fam is started by inetd,
141: // the first client would have to connect on the internet domain socket
142: // anyway to make inetd exec fam.
143: if ((opcode == 'N') && (p < msg + size - 1)) got_N_with_groups = true;
144:
145: // If no Cred is set on the connection, that means we trust the uid
146: // & gids supplied in the message.
147: Cred msg_cred = cred;
148: if (!msg_cred.is_valid())
149: {
150: gid_t *grouplist = &gid;
151: int ngroups = 1;
152: if (p < msg + size - 1)
153: {
154: ngroups += strtol(p, &p, 10);
155: int maxgroups = sysconf(_SC_NGROUPS_MAX);
156:
157: if (ngroups > maxgroups)
158: {
159: Log::info("message contained %i groups, but group list was"
160: " truncated to %i because of _SC_NGROUPS_MAX",
161: ngroups, maxgroups);
162: ngroups = maxgroups;
163: }
164: grouplist = new gid_t[ngroups];
165: grouplist[0] = gid;
166: for (int i = 1; i < ngroups; i++)
167: {
168: grouplist[i] = strtol(p, &q, 10);
169: if (p == q)
170: {
171: Log::error("bad message (%d additional groups expected, %d found)", ngroups - 1, i);
172: ngroups = i;
173: break;
174: }
175: p = q;
176: }
177: }
178:
179: Cred c(uid, ngroups, grouplist, conn.get_fd());
180: msg_cred = c;
181: if (grouplist != &gid) delete [] grouplist;
182: }
183:
184: // Process the message.
185:
186: switch (opcode)
187: {
188: case 'W': // Monitor File
189: {
190: Log::debug("%s said: request %d monitor file \"%s\"",
191: name(), reqnum, filename);
192: monitor_file(reqnum, filename, msg_cred);
193: break;
194: }
195: case 'M': // Monitor Directory
196: Log::debug("%s said: request %d monitor dir \"%s\"",
197: name(), reqnum, filename);
198: monitor_dir(reqnum, filename, msg_cred);
199: break;
200:
201: case 'C': // Cancel
202: Log::debug("%s said: cancel request %d", name(), reqnum);
203: cancel(reqnum);
204: break;
205:
206: case 'S': // Suspend
207: Log::debug("%s said: suspend request %d", name(), reqnum);
208: MxClient::suspend(reqnum);
209: break;
210:
211: case 'U': // Resume
212: Log::debug("%s said: resume request %d", name(), reqnum);
213: MxClient::resume(reqnum);
214: break;
215:
216: case 'N': // Client Name
217: Log::debug("%s said: %s is %s, and %s a unix domain socket",
218: name(), name(), filename,
219: (got_N_with_groups ? "wants" : "doesn't want"));
220: if (*filename && strcmp(filename, "test"))
221: name(filename); // set this client's name
222:
223: // Check to see whether this client wants to switch to a unix
224: // domain socket. Create it owned by the uid the client says
225: // they're running as.
226: if (got_N_with_groups) Listener::create_local_client(*this, uid);
227:
228: break;
229:
230: //
231: // Ignore these obsolete messages.
232: //
233: case 'D':
234: case 'V':
235: case 'E':
236: break;
237:
238: default:
239: Log::error("%s said unknown request '%c' ('\\%3o')",
240: name(), opcode, opcode & 0377);
241: return false;
242: }
243:
244: return true;
245: }
246:
247: //////////////////////////////////////////////////////////////////////////////
248: // Output
249:
250: void
251: TCP_Client::unblock_handler(void *closure)
252: {
253: TCP_Client *client = (TCP_Client *) closure;
254:
255: // Continue scanner, if any.
256:
257: Scanner *scanner = client->my_scanner;
258: if (scanner)
259: { if (scanner->done())
260: client->my_scanner = NULL;
261: else
262: return;
263: }
264:
265: // After scanner has run, scan more interests.
266:
267: Interest *ip;
268: while (client->ready_for_events() && (ip = client->to_be_scanned.first()))
269: { client->to_be_scanned.remove(ip);
270: ip->scan();
271: }
272:
273: // Enable input if all enqueued work is done.
274:
275: if (client->ready_for_events())
276: client->conn.ready_for_input(true);
277: }
278:
279: bool
280: TCP_Client::ready_for_events()
281: {
282: return conn.ready_for_output();
283: }
284:
285: void
286: TCP_Client::enqueue_for_scan(Interest *ip)
287: {
288: if (!to_be_scanned.size())
289: conn.ready_for_input(false);
290: to_be_scanned.insert(ip);
291: }
292:
293: void
294: TCP_Client::dequeue_from_scan(Interest *ip)
295: {
296: to_be_scanned.remove(ip);
297: if (!to_be_scanned.size())
298: conn.ready_for_input(true);
299: }
300:
301: void
302: TCP_Client::enqueue_scanner(Scanner *sp)
303: {
304: assert(!my_scanner);
305: my_scanner = sp;
306: conn.ready_for_input(false);
307: }
308:
309: void
310: TCP_Client::post_event(const Event& event, Request request, const char *path)
311: {
312: conn.send_event(event, request, path);
313: Log::debug("sent event to %s: request %d \"%s\" %s",
314: name(), request, path, event.name());
315: }
316:
317: //////////////////////////////////////////////////////////////////////////////
318: // Random kludges
319:
320: // If we're not running in insecure_compatibility mode,
321: // and we haven't already logged a message for this connection,
322: // put a message in the syslog saying "this client was denied...
323: // try insecure_compat mode."
324: void
325: TCP_Client::suggest_insecure_compat(const char *path)
326: {
327: if (insecure_compat_suggested) return;
328:
329: // if cred isn't valid it could be a remote connection.
330: if ((cred.is_valid()) &&
331: (!Cred::insecure_compat_enabled()) &&
332: (cred.uid() == Cred::get_untrusted_uid()))
333: {
334: // XXX add a config option to turn off this message!
335: Log::log(Log::INFO, "Client \"%s\", whose requests are being serviced "
336: "as uid %d, was denied access on %s. If this client might "
337: "be running as uid %d because it failed authentication, you "
338: "might disable authentication. See the fam(1M) man page.",
339: name(), cred.uid(), path, cred.uid());
340: insecure_compat_suggested = true;
341: }
342: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>