Annotation of fam/fam/TCP_Client.c++, Revision 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>