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>