[BACK]Return to TCP_Client.c++ CVS log [TXT][DIR] Up to [Development] / fam / fam

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>