nis_groups.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
*/
/*
* Copyright 1988-2003 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
*
* This file contains
* (1) Routines to test "is principal A in group B?". The
* implementation of this includes code to cache groups,
* (2) Operations on groups: add a principal, remove a principal,
* check that a group exists, create and destroy groups.
*/
#include "mt.h"
#include <syslog.h> /* ==== Is it really our place to syslog() things? */
#include <string.h>
#include <stdlib.h>
#define __NIS_PRIVATE_INTERFACES
#include <stdio.h>
#include "nis_local.h"
/*
* Results of do_ismember_2() and lookup_recursive(). ISMEM_DUNNO means
* "I couldn't look up the group (or some recursive group), so I don't know".
*/
/* === Should be defined with the rest of the hash-table routines */
typedef void (*nis_flush_func)(NIS_HASH_ITEM *);
/* Forward declarations */
nis_lookup_func, nis_error *);
/*
* The top level of the group cache. There is a single instance of this:
* it's a hash-table that maps group-names to g_entry structures (q.v.)
*/
struct g_cache {
/*
* Cache statistics, available from the server with the TAG_S_GCACHE
* tag.
*/
int ncalls;
int nhits;
int nmisses;
};
typedef struct g_cache *g_cache_ptr;
static g_cache_ptr groups_cache;
static g_cache_ptr
get_g_cache(void)
{
/* always enter with a READ LOCK and exit with one too */
if ((gc = groups_cache) != 0) {
return (gc);
}
/* write lock the cache and try again */
if ((gc = groups_cache) != 0) {
return (gc);
}
if (groups_cache == 0) {
return (0);
}
return (gc);
}
/* Interface for rpc.nisd and anyone else who wants to know */
int
int *grpcachemisses)
{
if (groups_cache == 0) {
*grpcachecall = 0;
*grpcachehits = 0;
*grpcachemisses = 0;
return (0);
} else {
return (1);
}
/* NOTREACHED */
}
/*
* Storage for the three sorts of group members:
* explicit (name of a principal),
* implicit (name of a domain, meaning all principals in that domain)
* recursive (name of a group, meaning all principals in that group )
* We expect lots of explicit members, so we use a hash table; for the other
* two we use singly-linked lists (if there are lots of implicit members
* then a hash table could be used there too, but there'll probably just be
* zero or one implicit members).
* are all assumed to be pointers into the gr_members<> vector in the
* group object, which we keep around.
*/
struct cons {
char *nom;
};
struct g_varieties {
};
typedef struct g_varieties g_varieties;
/*
* === For our purposes, most of NIS_HASH_TABLE is excess baggage. We don't
* Unless there's something that really needs stack-ordered semantics,
* we should get rid of them and reduce items from 20 bytes to 8.
*/
static bool_t
{
return (FALSE);
}
}
/* Don't use nis_insert_name() because we don't need the strdup(), */
/* so do the same sort of thing ourselves */
/* Memory is tight; can we free some that we don't need? */
/* Yup, no entries in the hash-table, so free it */
}
return (FALSE);
}
return (FALSE);
}
return (TRUE);
}
static bool_t
{
int dummy;
return (FALSE);
}
}
static void
{
}
}
/* ARGSUSED1 */
static bool_t
{
return (FALSE); /* i.e. continue */
}
static void
{
} else {
printf_hname, (void *)0);
}
}
static bool_t
{
/* Don't bother looking for duplicates. They were quite likely */
/* with the old group semantics but would be pretty weird now. */
return (FALSE);
}
return (TRUE);
}
static void
{
}
*nlp = 0;
}
static void
{
while (nl != 0) {
}
}
static bool_t
{
/* Assumes that the "*." has been stripped from 'implicit' */
}
static bool_t
{
/* ==== Using nis_dir_cmp is silly; just strcasecmp()? */
return (TRUE);
}
}
return (FALSE);
#if 0
/* Alternative semantics, where "*" means a subtree of the */
/* namespace rather than just one level wildcarded. */
return (TRUE);
}
return (FALSE);
#endif
}
static void
{
}
static void
{
} else {
}
}
static bool_t
{
/* Assumes the "@" has been stripped from 'recurs' */
}
static enum ismem
/* these recursive references */
{
case ISMEM_YES:
return (ISMEM_YES);
case ISMEM_NO:
break;
default:
break;
}
}
return (status);
}
static void
{
}
static void
{
} else {
}
}
static void
{
}
/*
* g_entry: A transformed group entry, of the sort we put in the group cache.
*/
struct g_entry {
g_varieties include; /* Semantics are "in(include) */
/* stuff above points into it */
int visiting; /* Recursion-detection for */
/* do_ismember_2(), q.v. */
};
static g_entry *
{
? 0 : ge;
}
static g_entry *
{
}
static void
{
if (el != 0) {
}
delete_explicit(&el->include);
delete_implicit(&el->include);
delete_recursive(&el->include);
}
}
}
static void
{
}
}
static void
{
}
/*
* transform_group() -- fetch a group, massage it into the form it should have
* had in the first place, and return a pointer to it.
*/
static g_entry *
{
int nm;
int i;
if (ge == 0) {
*stat = NIS_NOMEMORY;
return (0);
}
/* The calloc() set ge->visiting to zero for us */
for (i = 0; i < nm; i++) {
nis_name t;
t = ml[i];
if (*t == '-') {
t++;
}
if (*t == '*') {
} else if (*t == '@') {
/* === could check to see whether someone specified */
/* it twice (or specified it in both 'include' */
/* and 'exclude'), but probably not worth it. */
} else {
}
if (!ok) {
*stat = NIS_NOMEMORY;
"nislib:transform_group() insert failed, maybe out of memory");
return (0);
}
}
return (ge);
}
/*
* cached_group_entry(groupnam, refname, lookup)
*
* Returns a pointer to an entry in group_cache for the group called
* [group]. Adds the group to the cache if it isn't there already.
* Also checks the time-to-expire and refreshes if necessary.
* Returns NULL only if the universe is really broken.
*/
static g_entry *
{
*stat = NIS_SUCCESS;
/* always come in with a READ LOCK and exit with one too */
gc = get_g_cache();
if (gc == 0) {
*stat = NIS_NOMEMORY;
return (0);
}
/* Expire the group if necessary */
ge = 0;
}
}
if (ge != 0) {
return (ge);
}
/* write lock the cache and try again */
/* Expire the group if necessary */
ge = 0;
}
}
if (ge != 0) {
return (ge);
} else {
if (obj == 0) {
return (0); /* Couldn't read it */
}
if (ge == 0) {
return (0);
}
*stat = NIS_NOMEMORY;
return (0);
}
return (ge);
}
/* NOTREACHED */
}
static pthread_key_t visit_log_key;
struct visit_log {
};
static struct visit_log *visit_list_main;
static void
{
struct visit_log *v;
if (thr_main()) {
v->next = visit_list_main;
visit_list_main = v;
} else {
(void) pthread_setspecific(visit_log_key, v);
}
}
static void
{
(void) printf("unmark: fatal error\n");
abort();
}
static void
{
struct visit_log *v;
if (thr_main()) {
v = visit_list_main;
unmark_fatal();
visit_list_main = v->next;
} else {
unmark_fatal();
}
free(v);
}
static bool_t
{
while (v) {
return (TRUE);
v = v->next;
}
return (FALSE);
}
/*
* The main machinery for testing "is A a member of B?". The work gets done in
* do_ismember_2(), but clients won't normally call it directly; they use
* __do_ismember() or nis_ismember().
*/
static enum ismem
{
int easy_include;
/*
* return YES if
* in(princp, ge->include) AND NOT in(princp, ge->exclude)
* return NO if
* in(princp, ge->exclude) OR NOT in(princp, ge->include)
* return DUNNO if
* we can't look at some group.
*
* Checking for explicit members should be cheap, implicit members
* should be fairly cheap, and recursive members may be expensive;
* we try to order the tests accordingly.
*/
/* always enter with a READ LOCK and exit with one too */
if (ge == 0) {
/* === Should discover whether get_group() got NOTFOUND */
/* and, if so, return a definite ISMEM_NO (?) */
return (ISMEM_DUNNO);
}
return (ISMEM_NO);
}
/* Probable optimization; result will be the same with or without */
return (ISMEM_NO);
}
return (ISMEM_DUNNO);
}
mark_visit(ge);
case ISMEM_YES:
break;
case ISMEM_NO:
if (easy_include) {
} else {
}
break;
default:
if (!easy_include &&
} else {
}
}
return (answer);
}
/*
* nis_ismember(princp, group)
*
* This is the client function. It is a wrapper around an internal
* interface that is used by both the clients and servers ofNIS
* namespaces.
*/
{
nis_error x;
/* Err on the side of security: in case of doubt, return FALSE */
return (ret);
}
/*
* __do_ismember(princp, obj, lookup)
*
* Same as nis_ismember(), but calls an arbitrary lookup function rather than
* nis_lookup(). Clearly nis_ismember() could call this, but just this
* once let's save the extra function call.
*/
{
/* Err on the side of security: in case of doubt, return FALSE */
if (isit == ISMEM_DUNNO) {
if (stat != NIS_SUCCESS) {
"lookup failure on group \"%s\" from object \"%s.%s\"",
}
}
}
void
{
if (ge == 0) {
return;
}
}
/*
* nis_flushgroups()
*
* This function will free all memory associated with the group cache.
*/
void
nis_flushgroups(void)
{
if (groups_cache != 0) {
}
/* Else there's no cache, so no flushing to do */
}
/*
* nis_flushgroup(groupname) -- means "I've probably changed this group; flush
* any group_cache info that depends on it". With the old group-cache
* semantics, any group that included this one (directly or recursively)
* would have to be flushed, so it was easiest just to do a complete
* nis_flushgroups(); with the group-cache semantics here (i.e. information
* about recursive members isn't propagated), we only have to flush the one
* group.
*/
void
{
if (groups_cache != 0) {
}
/* Else there's no cache, so no flushing to do */
}
/*
* Same as __nis_flush_one_group() except that it accepts expanded group
* names i.e. with embedded "groups_dir" as part of the name.
*/
void
{
char *domainname;
/* Strip off "groups_dir" part of it */
char tname[NIS_MAXNAMELEN];
char buf[NIS_MAXNAMELEN];
} else {
}
}
{
char *p = buf;
return (0);
}
p += strlen(p);
*p++ = '.';
p += strlen(p);
*p++ = '.';
return (buf);
}
char *buf,
{
}
/*
* get_group()
*
* This function is a wrapper around the NIS code to fetch the group
* objects. The object returned is not static; it has been malloc()ed.
*/
static nis_object *
{
char namebuf[NIS_MAXNAMELEN];
/* Finally, the reason for passing that 'refname' parameter */
/* all over creation: printing a worthwhile error message */
if (refname)
"nislib:get_group() group object \"%s\", referenced by \"%s\", does not exist.",
else
"nislib:get_group() group object \"%s\" does not exist.",
name);
return (0);
if (refname)
"nislib:get_group() object \"%s\", referenced by \"%s\", lookup failed.",
else
"nislib:get_group() object \"%s\" lookup failed.",
name);
return (0);
}
if (refname)
"nislib:get_group() object \"%s\", referenced by \"%s\", is not a group.",
else
"nislib:get_group() object \"%s\" is not a group.",
name);
*stat = NIS_BADOBJECT;
return (0);
}
/*
* Steal the object before we free the whole result (cheaper than
* cloning). ==== This will leak if ever (NIS_RES_NUMOBJ(res) > 1).
*/
NIS_RES_OBJECT(res) = 0;
NIS_RES_NUMOBJ(res) = 0;
return (obj);
}
/*
* nis_addmember(princp, group)
*/
{
int nm, /* Number of members */
i;
/* Read the group object. */
if (obj == 0) {
return (result);
}
for (i = 0; i < nm; i++) {
return (NIS_NAMEEXISTS);
}
}
return (NIS_NOMEMORY);
}
for (i = 0; i < nm; i++) {
}
/* XXX overwrite problem if multiple writers ? */
return (result);
}
/*
* nis_removemember(princp, group)
*/
{
int nm, /* Number of members */
i, x;
if (!obj)
return (result);
for (i = 0; i < nm; i++) {
break;
}
/* If i == nm then we didn't find the member to remove */
if (i == nm) {
return (NIS_NOSUCHNAME);
}
return (NIS_NOMEMORY);
}
/*
* We know that ml[0..i-1] aren't the same name, and ml[i] is, so
* copy the former, skip the latter, and then check ml[i+1..nm-1]
*/
for (x = 0; x < i; x++) {
}
/* Note: Removes _all_ instances of a principal name */
while (++i < nm) {
++x;
}
}
return (result);
}
/*
* nis_verifygroup(group)
*
* Verify the existence of the named group.
*
*/
{
char namebuf[NIS_MAXNAMELEN];
else
} else
return (result);
}
/*
* __nis_creategroup_obj(name, flags, obj)
*
* This function creates an empty group of the given name.
* Using obj for setting the group object defaults.
*/
{
char namebuf[NIS_MAXNAMELEN];
char leafbuf[NIS_MAXSTRINGLEN];
/* Tease apart the name we just created in nis_map_group_r(); */
/* ==== maybe we need a more sensible interface. */
if (obj) {
} else {
}
/* If we're just creating it then it shouldn't have been in the */
/* cache so this is a no-op, but let's play safe. */
return (result);
}
/*
* nis_creategroup(name, flags)
*
* This function creates an empty group of the given name.
*/
{
}
{
char namebuf[NIS_MAXNAMELEN];
return (result);
}