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

Annotation of fam/fam/Cred.c++, Revision 1.1

1.1     ! trev        1: //  Copyright (C) 1999-2002 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 "Cred.h"
        !            24:
        !            25: #include <assert.h>
        !            26: #include <stdio.h>
        !            27: #include <string.h>
        !            28: #include <unistd.h>
        !            29: #include <grp.h>
        !            30: #include <stdlib.h>
        !            31: #include <ctype.h>
        !            32: #include <pwd.h>
        !            33: #include <errno.h>
        !            34:
        !            35: #include "Log.h"
        !            36: #ifdef HAVE_MAC
        !            37: #include <sys/mac.h>
        !            38: #include <t6net.h>
        !            39: #endif
        !            40:
        !            41: static gid_t SuperUser_groups[1] = { 0 };
        !            42: const Cred Cred::SuperUser(0, 1, SuperUser_groups, -1);
        !            43: Cred Cred::untrusted;
        !            44: const Cred::Implementation *Cred::Implementation::last = NULL;
        !            45: Cred::Implementation **Cred::impllist;
        !            46: unsigned Cred::nimpl;
        !            47: unsigned Cred::nimpl_alloc;
        !            48: bool Cred::insecure_compat = false;
        !            49: #ifdef HAVE_MAC
        !            50: bool Cred::use_mac = true;
        !            51: #endif
        !            52:
        !            53: //  This is used by Listener to create a new Cred for each connection whose
        !            54: //  per-request UIDs will be ignored.  (The uid could either be the untrusted
        !            55: //  uid, or the uid which the client has been authenticated as.)  It's also
        !            56: //  used by Cred::set_untrusted_user() to create the untrusted Cred.
        !            57: Cred::Cred(uid_t u, int sockfd)
        !            58: {
        !            59:     unsigned int nAddlGroups = sysconf(_SC_NGROUPS_MAX);
        !            60:     struct passwd *pwd;
        !            61:     gid_t *addlGroups = new gid_t[nAddlGroups];
        !            62:     gid_t primary_group;
        !            63:     if ((pwd = getpwuid(u)) != NULL)
        !            64:     {
        !            65:         primary_group = pwd->pw_gid;
        !            66: #ifdef HAVE_GETGRMEMBER
        !            67:         nAddlGroups = getgrmember(pwd->pw_name, addlGroups, nAddlGroups, 0);
        !            68:         if (nAddlGroups == -1)
        !            69:         {
        !            70:             Log::info("getgrmember(%s, ...) failed: %s",
        !            71: 			pwd->pw_name, strerror(errno));
        !            72:             nAddlGroups = 0;
        !            73:         }
        !            74: #else
        !            75:         group *gbp;
        !            76:         unsigned int ii = 0;
        !            77:         setgrent();
        !            78:         while ((ii < nAddlGroups) && ((gbp = getgrent()) != NULL))
        !            79:         {
        !            80:             //  See if our user's name is in the list of group members.
        !            81:             for (int i = 0; gbp->gr_mem[i] != NULL; ++i)
        !            82:             {
        !            83:                 if (!strcmp(pwd->pw_name, gbp->gr_mem[i]))
        !            84:                 {
        !            85:                     addlGroups[ii++] = gbp->gr_gid;
        !            86:                     break;
        !            87:                 }
        !            88:             }
        !            89:         }
        !            90:         endgrent();
        !            91:         nAddlGroups = ii;
        !            92: #endif
        !            93:     }
        !            94:     else
        !            95:     {
        !            96:         nAddlGroups = 0;
        !            97: #ifdef NOGROUP
        !            98:         primary_group = untrusted.is_valid() ? untrusted.gid() : (gid_t) NOGROUP;
        !            99: #else
        !           100:         primary_group = untrusted.is_valid() ? untrusted.gid() : (gid_t) GID_NOBODY;
        !           101: #endif
        !           102:         Log::info("Warning: uid %i is unknown to this system, so "
        !           103:                   "that user will be given the gid of the untrusted user: %i",
        !           104:                   u, primary_group);
        !           105:
        !           106:     }
        !           107:     mac_t mac = NULL;
        !           108: #ifdef HAVE_MAC
        !           109:     if ((use_mac) && (sockfd != -1))
        !           110:     {
        !           111:         if(tsix_get_mac(sockfd, &mac) != 0)
        !           112:         {
        !           113:             Log::error("tsix_get_mac failed for client fd %d", sockfd);
        !           114:             exit(1);
        !           115:         }
        !           116:     }
        !           117: #endif
        !           118:     new_impl(u, primary_group, nAddlGroups, addlGroups, mac);
        !           119:     delete [] addlGroups;
        !           120: }
        !           121:
        !           122: //  This is used by TCP_Client to create a new Cred for each request from
        !           123: //  a trusted remote fam.  The MAC label is not handled correctly; the
        !           124: //  remote fam's is being used, rather than the remote client's.
        !           125: Cred::Cred(uid_t u, unsigned int ng, const gid_t *gs, int sockfd)
        !           126: {
        !           127:     mac_t mac = NULL;
        !           128: #ifdef HAVE_MAC
        !           129:     if (use_mac) tsix_get_mac(sockfd, &mac);
        !           130: #endif
        !           131:     // The first gid in the group array should be considered the
        !           132:     // primary group id, and the remaining groups are additional
        !           133:     new_impl(u, gs[0], ng-1, gs+1, mac);
        !           134: }
        !           135:
        !           136: //  This is used by Cred::get_cred_for_untrusted_conn() to create a new
        !           137: //  Cred for each unauthenticated connection.  Before the MAC stuff was
        !           138: //  added, we could just use the untrusted Cred, but now each untrusted can
        !           139: //  have their own MAC label.
        !           140: Cred::Cred(int sockfd)
        !           141: {
        !           142: #ifdef HAVE_MAC
        !           143:     if (use_mac)
        !           144:     {
        !           145:         mac_t mac = NULL;
        !           146:         if(tsix_get_mac(sockfd, &mac) != 0)
        !           147:         {
        !           148:             //  what to do in this case?  Proceed without a good MAC label?
        !           149:             Log::error("tsix_get_mac failed for client fd %d", sockfd);
        !           150:         }
        !           151:         //  this is broken: needs to test for null untrusted.p
        !           152:         else if (mac_equal(mac, untrusted.p->mac) == 0)
        !           153: 	{
        !           154:             new_impl(untrusted.p->myuid, untrusted.p->mygid,
        !           155:                      untrusted.p->nAddlGroups, untrusted.p->AddlGroups, mac);
        !           156:             return;
        !           157:         }
        !           158: 	else mac_free(mac);  // because we'll share the Implementation.
        !           159:     }
        !           160: #endif
        !           161:     //  If we're here, either we're not using MAC or the socket's MAC label
        !           162:     //  matches untrusted's, so we want to share untrusted's Implementation.
        !           163:     p = untrusted.p;
        !           164:     if (p != NULL) p->refcount++;
        !           165: }
        !           166:
        !           167: //  This builds an invalid cred which shouldn't be used until it's assigned
        !           168: //  the value of a good cred.  It's used for the uninitialized untrusted cred,
        !           169: //  and for the Creds which are created per-connection by
        !           170: //  Listener::accept_client.
        !           171: Cred::Cred()
        !           172: {
        !           173:     p = NULL;
        !           174: }
        !           175:
        !           176: Cred::Cred(const Cred& that)
        !           177: {
        !           178:     p = that.p;
        !           179:     if (p != NULL) p->refcount++;
        !           180: }
        !           181:
        !           182: Cred::~Cred()
        !           183: {
        !           184:     if (p && (!--p->refcount)) drop(p);
        !           185: }
        !           186:
        !           187: Cred&
        !           188: Cred::operator = (const Cred& that)
        !           189: {
        !           190:     if (this != &that)
        !           191:     {   if ((p != NULL) && (!--p->refcount))
        !           192: 	    drop(p);
        !           193: 	p = that.p;
        !           194: 	if (p != NULL) p->refcount++;
        !           195:     }
        !           196:     return *this;
        !           197: }
        !           198:
        !           199: void
        !           200: Cred::add(Implementation *np)
        !           201: {
        !           202:     if (nimpl >= nimpl_alloc)
        !           203:     {   nimpl_alloc = nimpl_alloc * 3 / 2 + 3;
        !           204: 	Implementation **nl = new Implementation *[nimpl_alloc];
        !           205: 	for (int i = 0; i < nimpl; i++)
        !           206: 	    nl[i] = impllist[i];
        !           207: 	delete [] impllist;
        !           208: 	impllist = nl;
        !           209:     }
        !           210:     impllist[nimpl++] = np;
        !           211: }
        !           212:
        !           213: void
        !           214: Cred::drop(Implementation *dp)
        !           215: {
        !           216:     assert(!dp->refcount);
        !           217:     for (Implementation **pp = impllist, **end = pp + nimpl; pp < end; pp++)
        !           218: 	if (*pp == dp)
        !           219: 	{   *pp = *--end;
        !           220: 	    assert(nimpl);
        !           221: 	    --nimpl;
        !           222: 	    break;
        !           223: 	}
        !           224:     delete dp;
        !           225:
        !           226:     if (!nimpl)
        !           227:     {   delete[] impllist;
        !           228: 	impllist = NULL;
        !           229: 	nimpl_alloc = 0;
        !           230:     }
        !           231: }
        !           232:
        !           233: void
        !           234: Cred::new_impl(uid_t u, gid_t g, unsigned int ng, const gid_t *gs, mac_t mac)
        !           235: {
        !           236:     for (Implementation **pp = impllist, **end = pp + nimpl; pp < end; pp++)
        !           237: 	if ((*pp)->equal(u, g, ng, gs, mac))
        !           238: 	{   (*pp)->refcount++;
        !           239: 	    p = *pp;
        !           240: #ifdef HAVE_MAC
        !           241:             if (mac != NULL) mac_free(mac);
        !           242: #endif
        !           243: 	    return;
        !           244: 	}
        !           245:
        !           246:     p = new Implementation(u, g, ng, gs, mac);
        !           247:     add(p);
        !           248: }
        !           249:
        !           250: void
        !           251: Cred::set_untrusted_user(const char *name)
        !           252: {
        !           253:     //  The only time this should get called is at startup, when we're
        !           254:     //  handling command-line or config-file options, and before any
        !           255:     //  requests are accepted.
        !           256:     assert(!untrusted.is_valid());
        !           257:
        !           258:     //  The untrusted user is never used if we're running in
        !           259:     //  insecure_compat mode.
        !           260:     if (insecure_compat) return;
        !           261:
        !           262:     //  First see if we were passed a uid.
        !           263:     const char *p = name;
        !           264:     while (isdigit(*p)) ++p;
        !           265:     if((*p == '\0') && (p != name))  // need at least one character!
        !           266:     {
        !           267:         uid_t uid = atoi(name);
        !           268:         struct passwd *pwd = getpwuid(uid);
        !           269:         if(pwd == NULL)
        !           270:         {
        !           271:             Log::error("Fatal misconfiguration: attempted to use unknown uid "
        !           272:                        "\"%i\" for untrusted-user", uid);
        !           273:             exit(1);
        !           274:         }
        !           275:         Cred tmpcred(uid, -1);
        !           276:         untrusted = tmpcred;
        !           277:         Log::debug("Setting untrusted-user to \"%s\" (uid: %d, gid: %d)",
        !           278:                    pwd->pw_name, pwd->pw_uid, pwd->pw_gid);
        !           279:         return;
        !           280:     }
        !           281:
        !           282:     //  Looks like we were passed a user name.
        !           283:     struct passwd *pwd = getpwnam(name);
        !           284:     if(pwd == NULL)
        !           285:     {
        !           286:         Log::error("Fatal misconfiguration: attempted to use unknown user "
        !           287:                    "name \"%s\" for untrusted-user", name);
        !           288:         exit(1);
        !           289:     }
        !           290:     Log::debug("Setting untrusted-user to \"%s\" (uid: %d, gid: %d)",
        !           291:                name, pwd->pw_uid, pwd->pw_gid);
        !           292:     Cred tmpcred(pwd->pw_uid, -1);
        !           293:     untrusted = tmpcred;
        !           294: }
        !           295:
        !           296: Cred
        !           297: Cred::get_cred_for_untrusted_conn(int sockfd)
        !           298: {
        !           299:     //  An invalid Cred on a connection means we'll trust the Creds supplied
        !           300:     //  by the connection.  In the case where insecure_compat is enabled, we
        !           301:     //  always want to return an invalid Cred.  As it happens, untrusted is
        !           302:     //  always invalid when insecure_compat is enabled, so we just use it.
        !           303:     //  If insecure compat isn't enabled, we want to return a valid untrusted
        !           304:     //  Cred.
        !           305:     assert(untrusted.is_valid() || insecure_compat);
        !           306:     return insecure_compat ? untrusted : Cred(sockfd);
        !           307: }
        !           308:
        !           309: void
        !           310: Cred::disable_mac()
        !           311: {
        !           312: #ifdef HAVE_MAC
        !           313:     use_mac = false;
        !           314:     Log::audit(true, "running with MAC disabled, so all client requests will "
        !           315:                      "use fam's MAC label.");
        !           316: #endif
        !           317: }
        !           318:
        !           319: void
        !           320: Cred::enable_insecure_compat()
        !           321: {
        !           322:     insecure_compat = true;
        !           323:     Log::audit(true, "running in insecure compatibility mode");
        !           324:     Log::info("running in insecure compatibility mode");
        !           325: }
        !           326:
        !           327: ///////////////////////////////////////////////////////////////////////////////
        !           328:
        !           329: Cred::Implementation::Implementation(uid_t u, gid_t g,
        !           330:                                      unsigned int ng, const gid_t *gs,
        !           331:                                      mac_t m)
        !           332:     : refcount(1), myuid(u), mygid(g), nAddlGroups(ng)
        !           333: {
        !           334: #ifdef HAVE_MAC
        !           335:     mac = NULL;
        !           336:     if (use_mac) mac = m;
        !           337:     else if (m != NULL) mac_free(m);
        !           338: #endif
        !           339:     AddlGroups = new gid_t[ng];
        !           340:
        !           341:     for (int i = 0; i < nAddlGroups; i++)
        !           342: 	AddlGroups[i] = gs[i];
        !           343:
        !           344:     if (nAddlGroups == 0) {
        !           345:         addlGroupsStr = new char[1];
        !           346:         *addlGroupsStr = '\0';
        !           347:     }
        !           348:     else {
        !           349:         // The format is: <number of addl groups> <gid1> <gid2> ... <gidn>
        !           350:
        !           351:         // Assume that the each num and accompanying space (or null
        !           352:         // character in the case of the last one) will be <= 11 chars.
        !           353:         addlGroupsStr = new char[11*(nAddlGroups + 1)];
        !           354:         char * p = addlGroupsStr;
        !           355:         p += snprintf(p, 10, "%d", nAddlGroups);
        !           356:         for (int i = 0; i < nAddlGroups; i++)
        !           357:         {
        !           358:             p += snprintf(p, 11, " %d", AddlGroups[i]);
        !           359:         }
        !           360:     }
        !           361: }
        !           362:
        !           363: Cred::Implementation::~Implementation()
        !           364: {
        !           365:     if (this == last)
        !           366: 	SuperUser.become_user();
        !           367:     delete [] AddlGroups;
        !           368:     delete [] addlGroupsStr;
        !           369: #ifdef HAVE_MAC
        !           370:     if (mac != NULL) mac_free(mac);
        !           371: #endif
        !           372: }
        !           373:
        !           374: bool
        !           375: Cred::Implementation::equal(uid_t u, gid_t g, unsigned int ng,
        !           376:                             const gid_t *gs, mac_t mac) const
        !           377: {
        !           378:     if ((u != myuid) || (g != mygid)) {
        !           379:         return false;
        !           380:     }
        !           381: #ifdef HAVE_MAC
        !           382:     if ((use_mac) && (mac_equal(this->mac, mac) == 0)) return -1;
        !           383: #endif
        !           384:     return addl_groups_equal(ng, gs);
        !           385: }
        !           386:
        !           387: bool
        !           388: Cred::Implementation::addl_groups_equal(unsigned int ng, const gid_t *gs) const
        !           389: {
        !           390:     if (ng != nAddlGroups) {
        !           391:         return false;
        !           392:     }
        !           393:     for (int i = 0; i < nAddlGroups; i++)
        !           394: 	if (AddlGroups[i] != gs[i])
        !           395: 	    return false;
        !           396:     return true;
        !           397: }
        !           398:
        !           399: // This function returns a string representation of the additional
        !           400: // groups, as required by ServerConnection::send_monitor().
        !           401: const char *
        !           402: Cred::Implementation::getAddlGroupsString() const {
        !           403:     return addlGroupsStr;
        !           404: }
        !           405:
        !           406: void
        !           407: Cred::Implementation::become_user() const
        !           408: {
        !           409:     // If we're becoming the same user we currently are, then we can
        !           410:     // just skip everything.
        !           411:     if (this == last)
        !           412: 	return;
        !           413:
        !           414:     uid_t current_uid = last ? last->myuid : 0;
        !           415:     if (current_uid != 0) {
        !           416:         /* Temporarily set the effective uid to root's uid
        !           417:          * so that we have permission to call setgroups, etc.
        !           418:          * This assumes, of course, that we were started as root,
        !           419:          * so that we have permission to do this.
        !           420:          */
        !           421:         Log::debug("Setting euid to 0");
        !           422:         if (seteuid(0) != 0)
        !           423:         {
        !           424:             Log::perror("failed to set 0 uid");
        !           425:             exit(1);
        !           426:         }
        !           427:     }
        !           428:
        !           429:     // We need to set the primary group and additional groups before
        !           430:     // setting our euid because non-root users don't have permission
        !           431:     // to change the groups.
        !           432:     if (!last || !addl_groups_equal(last->nAddlGroups, last->AddlGroups)) {
        !           433: 	if (setgroups(nAddlGroups, AddlGroups) != 0) {
        !           434:             Log::perror("failed to set groups");
        !           435:             exit(1);
        !           436: 	} else if (Log::get_level() == Log::DEBUG) {
        !           437:             if (nAddlGroups == 0) {
        !           438:                 Log::debug("Setting groups to: (none)");
        !           439:             } else {
        !           440:                 // The groupStr variable is almost what we want, but
        !           441:                 // it has the number of groups prepended.  So just
        !           442:                 // skip over that.
        !           443:                 char * p = strchr(addlGroupsStr, ' ');
        !           444:                 Log::debug("Setting groups to: %s", p+1);
        !           445:             }
        !           446:         }
        !           447:     } else {
        !           448:         Log::debug("Skipping setting groups, because they're already correct");
        !           449:     }
        !           450:
        !           451:     if (!last || (mygid != last->mygid)) {
        !           452:         if (setegid(mygid)) {
        !           453:             Log::perror("failed to set gid %d", mygid);
        !           454:             exit(1);
        !           455:         } else {
        !           456:             Log::debug("Setting egid to %i", mygid);
        !           457:         }
        !           458:     } else {
        !           459:         Log::debug("Skipping setting egid, because it's already correct");
        !           460:     }
        !           461:
        !           462:
        !           463:
        !           464:     if (myuid != 0) { // We can skip this if we're becoming root, as
        !           465:                       // we either entered this function as root, or
        !           466:                       // set our euid to root above
        !           467:         if (seteuid(myuid)) {
        !           468:             Log::perror("failed to set uid %d", myuid);
        !           469:             exit(1);
        !           470:         } else {
        !           471:             Log::debug("Setting euid to %i", myuid);
        !           472:         }
        !           473:     } else {
        !           474:         if (current_uid != 0) {
        !           475:             // We already logged a message above
        !           476:         } else {
        !           477:             Log::debug("Skipping setting euid, because it's already 0");
        !           478:         }
        !           479:     }
        !           480:
        !           481:
        !           482: #ifdef HAVE_MAC
        !           483:     if (use_mac)
        !           484:     {
        !           485:         if (mac_set_proc(mac) != 0)
        !           486:         {
        !           487:             Log::perror("become_user() failed to set MAC label for uid %d", myuid);
        !           488:         }
        !           489:     }
        !           490: #endif
        !           491:     last = this;
        !           492: }
        !           493:

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>