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