Main Page | Namespace List | Class Hierarchy | Class List | Directories | File List | Namespace Members | Class Members | File Members

AccessControl.C

Go to the documentation of this file.
00001 // Copyright (C) 2001, Compaq Computer Corporation
00002 // 
00003 // This file is part of Vesta.
00004 // 
00005 // Vesta is free software; you can redistribute it and/or
00006 // modify it under the terms of the GNU Lesser General Public
00007 // License as published by the Free Software Foundation; either
00008 // version 2.1 of the License, or (at your option) any later version.
00009 // 
00010 // Vesta is distributed in the hope that it will be useful,
00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013 // Lesser General Public License for more details.
00014 // 
00015 // You should have received a copy of the GNU Lesser General Public
00016 // License along with Vesta; if not, write to the Free Software
00017 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00018 
00019 //
00020 // AccessControl.C
00021 //
00022 
00023 #include "AccessControl.H"
00024 #include "Recovery.H"
00025 #include "VestaConfig.H"
00026 #include "CharsKey.H"
00027 #include "logging.H"
00028 #include "ReadersWritersLock.H"
00029 #include <pwd.h>
00030 #include <grp.h>
00031 #include <time.h>
00032 
00033 #include "lock_timing.H"
00034 
00035 //
00036 // Types
00037 //
00038 class UIntKey {
00039   public:
00040     unsigned int i;
00041     inline Word Hash() const throw() { return i; };
00042     inline UIntKey() { this->i = 0; };
00043     inline UIntKey(unsigned int i) { this->i = i; };
00044     inline friend int operator==(const UIntKey& k1, const UIntKey& k2)
00045       throw() { return k1.i == k2.i; };
00046 };
00047 
00048 typedef Table<CharsKey, unsigned int>::Default CharsToUIntTable;
00049 typedef Table<CharsKey, unsigned int>::Iterator CharsToUIntIter;
00050 typedef Table<UIntKey, const char*>::Default UIntToCharsTable;
00051 typedef Table<UIntKey, const char*>::Iterator UIntToCharsIter;
00052 typedef Table<UIntKey, CharsSeq*>::Default UIntToCharsSeqTable;
00053 typedef Table<UIntKey, CharsSeq*>::Iterator UIntToCharsSeqIter;
00054 typedef Table<CharsKey, CharsSeq*>::Default CharsToCharsSeqTable;
00055 typedef Table<CharsKey, CharsSeq*>::Iterator CharsToCharsSeqIter;
00056 
00057 class ExportKey {
00058 public:
00059   Bit32 addr;
00060   AccessControl::IdentityRep::Flavor flavor;
00061   const char* arg;
00062   inline Word Hash() const throw()
00063     { return addr ^ (0x12345678 << flavor); };
00064   inline ExportKey()
00065     { addr = 0; flavor = AccessControl::IdentityRep::unix_flavor; arg = NULL; }
00066   inline ExportKey(Bit32 a, AccessControl::IdentityRep::Flavor f, const char*g)
00067     { addr = a; flavor = f; arg = g; };
00068   inline friend int operator==(const ExportKey& k1,const ExportKey& k2) throw()
00069     { return k1.addr == k2.addr && k1.flavor == k2.flavor &&
00070       ((k1.arg == NULL && k2.arg == NULL) ||
00071        (strcasecmp(k1.arg, k2.arg) == 0)); };
00072 };
00073 
00074 struct Export {
00075   static Export* first;
00076   Export* next;
00077   const char* pattern; // hostname wildcard, or IPaddress[/netmask]
00078   enum Level { deny, readOnly, allow };
00079   Level level;
00080   AccessControl::IdentityRep::Flavor flavor;
00081   const char* realm; // realm that global names must be from, NULL=any
00082   Export(const char* p, Level l, AccessControl::IdentityRep::Flavor f,
00083          const char* s, Export *tail)
00084     : pattern(p), level(l), flavor(f), realm(s),
00085       next(tail)
00086   { };
00087 
00088   // Test whether this key matches this export.
00089   bool match(const ExportKey &key);
00090 };
00091 
00092 typedef Table<ExportKey, Export*>::Default ExportCache;
00093 typedef Table<ExportKey, Export*>::Iterator ExportCacheIter;
00094 
00095 //
00096 // Module globals
00097 //
00098 
00099 // These are constant after initialization
00100 const char* vestaGroupFile = NULL;
00101 const char* vestaAliasFile = NULL;
00102 const char* vestaExportFile = NULL;
00103 
00104 // Used to protect the following variables.
00105 ReadersWritersLock userGroup_lock(true);
00106 
00107 static CharsToUIntTable* globalToUnixUserTable = NULL;
00108 static CharsToUIntTable* globalToUnixGroupTable = NULL;
00109 static UIntToCharsTable* unixToGlobalUserTable = NULL;
00110 static UIntToCharsSeqTable* unixToGlobalGroupsTable = NULL;
00111 static CharsToCharsSeqTable* globalUserToGroupsTable = NULL;
00112 static CharsToCharsSeqTable* globalAliasesTable = NULL;
00113 
00114 // Used to protect Export::first and exportCache.
00115 ReadersWritersLock export_lock(true);
00116 Export* Export::first = NULL;
00117 static ExportCache* exportCache = NULL;
00118 unsigned int exportEpoch = 0;
00119 
00120 // The time we last reloaded the access control information.  Used to
00121 // avoid doing it too often.
00122 static time_t last_refresh;
00123 
00124 // Minimum time since the last refresh when we will perform an
00125 // automatic refresh.
00126 static unsigned int min_auto_refresh_delay = (60*60);
00127 
00128 // Should we honor gids sent by the client with a UnixIdentityRep?
00129 static bool honor_client_gids = false;
00130 
00131 //
00132 // Forward declarations
00133 //
00134 static const char* unixToGlobalUserNL(uid_t uid) throw ();
00135 static const char* unixToGlobalGroupNL(gid_t gid) throw ();
00136 
00137 //
00138 // Methods and functions
00139 //
00140 
00141 // Like strdup, but allocate the memory with the new operator so we
00142 // can free it with the delete [] operator.  (This simplifies memory
00143 // management, because we don't have to keep track of which strings
00144 // are allocated with malloc/strdup and which ones are allocated with
00145 // the new operatotr).
00146 static char *strdup_with_new(const char *orig)
00147 {
00148   char *result = NEW_PTRFREE_ARRAY(char, strlen(orig)+1);
00149   strcpy(result, orig);
00150   return result;
00151 }
00152 
00153 // Construct a complete copy of a CharsSeq, including copying all its
00154 // elements.  (This simplifies memory management, because we don't
00155 // have to handle one pointer in multiple CharsSeqs.)
00156 static CharsSeq *full_CharSeq_copy(const CharsSeq &orig,
00157                                    unsigned int extra_space = 0)
00158 {
00159   CharsSeq *result = NEW_CONSTR(CharsSeq, (orig.size() + extra_space));
00160 
00161   for(unsigned int i = 0; i < orig.size(); i++)
00162     {
00163       result->addhi(strdup_with_new(orig.get(i)));
00164     }
00165 
00166   return result;
00167 }
00168 
00169 // Skip indicated character; error if other character seen.
00170 static void
00171 require(FILE* f, int& c, char req) throw(AccessControl::ParseError)
00172 {
00173   if (c == EOF) throw(AccessControl::ParseError("incomplete line"));
00174   if (c != req) {
00175     throw(AccessControl::ParseError(Text("expected '") + req + "'; got '" +
00176                                     (char)c + "'"));
00177   }
00178   c = getc(f);
00179 }
00180 
00181 // Whitespace character within a line?
00182 static bool
00183 white(int c)
00184 {
00185   return c == ' ' || c == '\t' || c == '\r';
00186 }
00187 
00188 // Skip whitespace within a line.
00189 static void
00190 skipWhite(FILE* f, int& c)
00191 {
00192   while (white(c)) {
00193     c = getc(f);
00194   }
00195 }
00196 
00197 // Skip the rest of the line.  Error if EOF before newline seen.
00198 static void
00199 skipLine(FILE* f, int& c) throw(AccessControl::ParseError)
00200 {
00201   while (c != '\n' && c != EOF) {
00202     c = getc(f);
00203   }
00204   if (c == EOF) throw(AccessControl::ParseError("incomplete line"));
00205   c = getc(f);
00206 }
00207 
00208 // Skip a comment, a blank line, or leading space on a line.
00209 // Return true if we skipped to the next line.
00210 static bool
00211 skipComment(FILE* f, int& c)
00212 {
00213   skipWhite(f, c);
00214   if (c == '/') {
00215     c = getc(f);
00216     require(f, c, '/');
00217     skipLine(f, c);
00218     return true;
00219   }
00220   if (c == ';' || c == '#' || c == '\n') {
00221     skipLine(f, c);
00222     return true;
00223   }
00224   return false;
00225 }
00226 
00227 // Skip whitespace, then skip a comma if present
00228 static void
00229 skipOptionalComma(FILE* f, int&c)
00230 {
00231   skipWhite(f, c);
00232   if (c == ',') c = getc(f);
00233 }
00234 
00235 // Parse out a name.  Any characters other than whitespace, newline,
00236 // colon, or comma are accepted.  Error if name ends with EOF.
00237 #define MAXNAMELEN 256
00238 static void
00239 parseName(FILE* f, int& c, char name[MAXNAMELEN])
00240   throw(AccessControl::ParseError)
00241 {
00242   int i = 0;
00243   while (i < MAXNAMELEN) {
00244     name[i++] = c;
00245     c = getc(f);
00246     if (white(c) || c == ':' || c == ',' || c == '\n') break;
00247     if (c == EOF) throw(AccessControl::ParseError("incomplete line"));
00248   }
00249   if (i >= MAXNAMELEN) throw(AccessControl::ParseError("name too long"));
00250   name[i] = '\0';
00251   if (i == 0) throw(AccessControl::ParseError("empty name"));
00252 }
00253 
00254 // Parse out a filename.  Any characters other than whitespace or
00255 // newline are accepted.  Error if name ends with EOF.
00256 #define MAXNAMELEN 256
00257 static void
00258 parseFilename(FILE* f, int& c, char name[MAXNAMELEN])
00259   throw(AccessControl::ParseError)
00260 {
00261   int i = 0;
00262   while (i < MAXNAMELEN) {
00263     name[i++] = c;
00264     c = getc(f);
00265     if (white(c) || c == '\n') break;
00266     if (c == EOF) throw(AccessControl::ParseError("incomplete line"));
00267   }
00268   if (i >= MAXNAMELEN) throw(AccessControl::ParseError("name too long"));
00269   name[i] = '\0';
00270   if (i == 0) throw(AccessControl::ParseError("empty name"));
00271 }
00272 
00273 // Add the default realm to this name if it doesn't already have a
00274 // realm specifier.
00275 static void ensure_realm(char name[MAXNAMELEN] /*IN/OUT*/)
00276   throw(AccessControl::ParseError)
00277 {
00278   // See if it has a realm
00279   if(strchr(name, '@') == 0)
00280     {
00281       // Make sure adding the realm won't overrun the buffer
00282       if((strlen(name) + AccessControl::realmlen + 2) > MAXNAMELEN)
00283         {
00284           throw(AccessControl::ParseError("name too long to add default realm"));
00285         }
00286       // Add the realm
00287       strcat(name, "@");
00288       strcat(name, AccessControl::realm);
00289     }
00290 }
00291 
00292 // Parse a membership file, in the format
00293 //
00294 //   ; optional comments, can appear anywhere
00295 //   # optional comments, can appear anywhere
00296 //   // optional comments, can appear anywhere
00297 //   . includefile
00298 //   name : [value [,]]... [value]
00299 //   ...
00300 //
00301 // If "name" is already in the table (including appearing multiple times
00302 // in the file), the values accumulate.  [,] is an optional comma.
00303 // Where whitespace is shown, zero or more spaces, tabs, or \r characters
00304 // can appear.
00305 //
00306 // The "reject" argument is not permitted as a member.
00307 
00308 static void
00309 parseMembershipFile(const char* filename, CharsToCharsSeqTable* tbl,
00310                     const char* reject ="")
00311   throw(AccessControl::ParseError)
00312 {
00313   FILE* f = fopen(filename, "r");
00314   if (f == NULL)
00315     {
00316       int saved_errno = errno;
00317       Text etxt = Basics::errno_Text(saved_errno);
00318       throw(AccessControl::ParseError(Text(filename) + ": " + etxt));
00319     }
00320   int c = getc(f);
00321   for (;;) {
00322     // Handle comments and includes
00323     while (skipComment(f, c)) ;
00324     if (c == EOF) break;
00325     if (c == '.') {
00326       c = getc(f);
00327       skipWhite(f, c);
00328       char filename2[MAXNAMELEN];
00329       parseFilename(f, c, filename2);
00330       parseMembershipFile(filename2, tbl);
00331     }
00332     while (skipComment(f, c)) ;
00333     if (c == EOF) break;
00334 
00335     // Get name
00336     char name[MAXNAMELEN];
00337     parseName(f, c, name);
00338     // Make sure that it has a realm
00339     ensure_realm(name);
00340     skipWhite(f, c);
00341     require(f, c, ':');
00342     CharsSeq* seq;
00343     if (!tbl->Get(name, seq)) {
00344       seq = NEW_CONSTR(CharsSeq, (1));
00345       tbl->Put(strdup_with_new(name), seq);
00346     }
00347     for (;;) {
00348       // Get members
00349       char value[MAXNAMELEN];
00350       if (skipComment(f, c)) break;
00351       if (c == '\n' || c == EOF) break;
00352       parseName(f, c, value);
00353       // Make sure that it has a realm
00354       ensure_realm(name);
00355       //cerr << name << ": " << value << endl;
00356       if (strcasecmp(value, reject) == 0) {
00357         throw(AccessControl::ParseError(Text(value) + " not permitted"));
00358       }
00359       seq->addhi(strdup_with_new(value));
00360       skipOptionalComma(f, c);
00361     }
00362   }
00363   fclose(f);
00364 }
00365 
00366 // Parse the export file.  Format:
00367 //
00368 //   ; optional comments, can appear anywhere
00369 //   # optional comments, can appear anywhere
00370 //   // optional comments, can appear anywhere
00371 //   . includefile
00372 //   pattern [:] [level [flavor [arg]],]... [level [flavor [arg]]]
00373 //   ...
00374 //
00375 // A pattern can be a DNS hostname, a DNS hostname pattern with ? or *
00376 // wildcards (which will not match a "."), an IP address in dotted
00377 // notation, or an IP subnet in the form address/netmask, where
00378 // netmask can be in dotted notation, or can be a single integer to
00379 // indicate how many bits are set from the left.  (E.g., a netmask of
00380 // 24 represents 255.255.255.0.)
00381 //
00382 // level ::= allow, rw, readwrite (synonyms); readonly, ro (synonyms); deny
00383 // flavor ::=  unix_flavor; global; gssapi; all, any (synonyms)
00384 //
00385 // If no "level flavor arg" tuples appear, "allow all" is supplied by
00386 // default; otherwise we start with "deny all" and then apply the
00387 // pairs that appear.  If "flavor" is omitted from a tuple, it defaults
00388 // to "all".
00389 //
00390 // arg is an argument to the flavor.  Currently only "allow global" or
00391 // "readonly global" takes an argument: a pattern for the realm
00392 // that the global name must originate from.  If this arg is omitted, any
00393 // global name is OK.
00394 //
00395 // When checking whether a client request should be admitted, we scan
00396 // the export file in reverse order, looking for the first (pattern,
00397 // flavor, arg) that matches.  If one is found, its level applies to
00398 // this request.  If not, the request is denied.
00399 
00400 
00401 static void
00402 parseExportFile(const char* filename, Export *&head)
00403   throw(AccessControl::ParseError)
00404 {
00405   char pattern[MAXNAMELEN], token[MAXNAMELEN], argbuf[MAXNAMELEN];
00406 
00407   FILE* f = fopen(filename, "r");
00408   if (f == NULL)
00409     {
00410       int saved_errno = errno;
00411       Text etxt = Basics::errno_Text(saved_errno);
00412       throw(AccessControl::ParseError(Text(filename) + ": " + etxt));
00413     }
00414   int c = getc(f);
00415 
00416   for (;;) {
00417     // Handle comments and includes
00418     while (skipComment(f, c)) ;
00419     if (c == EOF) break;
00420     if (c == '.') {
00421       c = getc(f);
00422       skipWhite(f, c);
00423       char filename2[MAXNAMELEN];
00424       parseFilename(f, c, filename2);
00425       parseExportFile(filename2, head);
00426     }
00427     while (skipComment(f, c)) ;
00428     if (c == EOF) break;
00429 
00430     // Get pattern
00431     parseName(f, c, pattern);
00432     Export::Level level;
00433     AccessControl::IdentityRep::Flavor flavor;
00434     const char* arg;
00435 
00436     if (c == ':') c = getc(f);
00437     for (;;) {
00438       // Get "level flavor arg" tuple
00439       if (skipComment(f, c)) break;
00440       if (c == EOF) throw(AccessControl::ParseError("incomplete line"));
00441       if (c == '\n') break;
00442 
00443       // Get level
00444       parseName(f, c, token);
00445       if (strcasecmp(token, "allow") == 0 ||
00446           strcasecmp(token, "readwrite") == 0 ||
00447           strcasecmp(token, "rw") == 0) {
00448         level = Export::allow;
00449 
00450       } else if (strcasecmp(token, "readonly") == 0 ||
00451                    strcasecmp(token, "ro") == 0) {
00452         level = Export::readOnly;
00453 
00454       } else if (strcasecmp(token, "deny") == 0) {
00455         level = Export::deny;
00456 
00457       } else {
00458         throw(AccessControl::ParseError(Text("unknown level ") + token));
00459       }
00460 
00461       // Get flavor
00462       flavor = AccessControl::IdentityRep::unspecified; // default
00463       skipWhite(f, c);
00464       if (c == EOF) throw(AccessControl::ParseError("incomplete line"));
00465       if (c != '\n' && c != ',') {
00466         parseName(f, c, token);
00467         if (strcasecmp(token, "unix") == 0) {
00468           flavor = AccessControl::IdentityRep::unix_flavor;
00469         } else if (strcasecmp(token, "global") == 0) {
00470           flavor = AccessControl::IdentityRep::global;
00471         } else if (strcasecmp(token, "gssapi") == 0) {
00472           flavor = AccessControl::IdentityRep::gssapi;
00473         } else if (strcasecmp(token, "any") == 0 ||
00474                    strcasecmp(token, "all") == 0) {
00475           flavor = AccessControl::IdentityRep::unspecified;
00476         } else {
00477           throw(AccessControl::ParseError(Text("unknown flavor ") + token));
00478         }
00479       }
00480 
00481       arg = NULL; // default
00482       if (flavor == AccessControl::IdentityRep::global ||
00483           flavor == AccessControl::IdentityRep::gssapi) {
00484         // Get arg
00485         skipWhite(f, c);
00486         if (c == EOF) throw(AccessControl::ParseError("incomplete line"));
00487         if (c != ',' && c != '\n') {
00488           parseName(f, c, argbuf);
00489           arg = strdup_with_new(argbuf);
00490         }
00491       }
00492 
00493       head = NEW_CONSTR(Export, (strdup_with_new(pattern), level,
00494                                  flavor, arg, head));
00495       //cerr << pattern << ": " << level << " " << flavor << " " <<
00496       //  (arg ? arg : NULL) << endl;
00497       if (skipComment(f, c)) break;
00498       if (c != '\n') require(f, c, ',');
00499     }
00500   }
00501   fclose(f);
00502 }
00503 
00504 
00505 // Like Sequence::addhi, but don't add if this name is already in the sequence
00506 static void
00507 addhi_nondup(CharsSeq* seq, const char* name)
00508 {
00509   int i;
00510   int sz = seq->size();
00511   for (i=0; i<sz; i++) {
00512     if (strcmp(seq->get(i), name) == 0) return;
00513   }
00514   seq->addhi(strdup_with_new(name));
00515 }
00516 
00517 
00518 // Free a set of access tables that are no longer needed.  These may
00519 // be either old tables replaced by new ones, or ones which could not
00520 // be constructed due to a parsing error.
00521 static void
00522 freeTables(CharsToUIntTable *dead_globalToUnixUserTable,
00523            CharsToUIntTable *dead_globalToUnixGroupTable,
00524            UIntToCharsTable *dead_unixToGlobalUserTable,
00525            UIntToCharsSeqTable *dead_unixToGlobalGroupsTable,
00526            CharsToCharsSeqTable *dead_globalUserToGroupsTable,
00527            CharsToCharsSeqTable *dead_globalAliasesTable,
00528            Export* dead_exportList,
00529            ExportCache *dead_exportCache) throw()
00530 {
00531   if(dead_globalToUnixUserTable != NULL)
00532     {
00533       // Each CharsKey has an allocated string that needs to be freed.
00534       // However, we can't free them until we delete the table.  So,
00535       // we gather them up, delete the table, then delete the key
00536       // strings.
00537       CharsSeq dead_keys;
00538       CharsToUIntIter iter(dead_globalToUnixUserTable);
00539       CharsKey key;
00540       unsigned int val;
00541       while(iter.Next(key, val))
00542         {
00543           assert(key.s != NULL);
00544           dead_keys.addhi(key.s);
00545         }
00546 
00547       // Delte the table.
00548       delete dead_globalToUnixUserTable;
00549 
00550       // Delete the key strings.
00551       for(unsigned int i = 0; i < dead_keys.size(); i++)
00552         {
00553           const char *key_str = dead_keys.get(i);
00554           assert(key_str != NULL);
00555           delete [] key_str;
00556         }
00557     }
00558   if(dead_globalToUnixGroupTable != NULL)
00559     {
00560       // Gather the key strings to be freed.
00561       CharsSeq dead_keys;
00562       CharsToUIntIter iter(dead_globalToUnixGroupTable);
00563       CharsKey key;
00564       unsigned int val;
00565       while(iter.Next(key, val))
00566         {
00567           assert(key.s != NULL);
00568           dead_keys.addhi(key.s);
00569         }
00570 
00571       // Delte the table.
00572       delete dead_globalToUnixGroupTable;
00573 
00574       // Delete the key strings.
00575       for(unsigned int i = 0; i < dead_keys.size(); i++)
00576         {
00577           const char *key_str = dead_keys.get(i);
00578           assert(key_str != NULL);
00579           delete [] key_str;
00580         }
00581     }
00582   if(dead_unixToGlobalUserTable != NULL)
00583     {
00584       // The values in this table are pointers that need to be freed.
00585       UIntToCharsIter iter(dead_unixToGlobalUserTable);
00586       UIntKey key;
00587       const char *val;
00588       while(iter.Next(key, val))
00589         {
00590           assert(val != NULL);
00591           delete [] val;
00592         }
00593 
00594       // Delete the table itself.
00595       delete dead_unixToGlobalUserTable;
00596     }
00597   if(dead_unixToGlobalGroupsTable != NULL)
00598     {
00599       // The values in this table need to be freed.
00600       UIntToCharsSeqIter iter(dead_unixToGlobalGroupsTable);
00601       UIntKey key;
00602       CharsSeq *val;
00603       while(iter.Next(key, val))
00604         {
00605           assert(val != NULL);
00606           // The sequence elements must be deleted as well.
00607           while(val->size() > 0)
00608             {
00609               const char *val_elem = val->remhi();
00610               assert(val_elem != NULL);
00611               delete [] val_elem;
00612             }
00613 
00614           // Delete the now empty sequence.
00615           delete val;
00616         }
00617 
00618       // delete the table itself.
00619       delete dead_unixToGlobalGroupsTable;
00620     }
00621 
00622   if(dead_globalUserToGroupsTable != NULL)
00623     {
00624       // The values in this table need to be freed.
00625       CharsSeq dead_keys;
00626       CharsToCharsSeqIter iter(dead_globalUserToGroupsTable);
00627       CharsKey key;
00628       CharsSeq *val;
00629       while(iter.Next(key, val))
00630         {
00631           assert(key.s != NULL);
00632           dead_keys.addhi(key.s);
00633 
00634           assert(val != NULL);
00635           // The sequence elements must be deleted as well.
00636           while(val->size() > 0)
00637             {
00638               const char *val_elem = val->remhi();
00639               assert(val_elem != NULL);
00640               delete [] val_elem;
00641             }
00642           delete val;
00643         }
00644 
00645       // Delete the table itself.
00646       delete dead_globalUserToGroupsTable;
00647 
00648       // Delete the key strings.
00649       for(unsigned int i = 0; i < dead_keys.size(); i++)
00650         {
00651           const char *key_str = dead_keys.get(i);
00652           assert(key_str != NULL);
00653           delete [] key_str;
00654         }
00655     }
00656 
00657   if(dead_globalAliasesTable != NULL)
00658     {
00659       // The values in this table need to be freed.
00660       CharsSeq dead_keys;
00661       CharsToCharsSeqIter iter(dead_globalAliasesTable);
00662       CharsKey key;
00663       CharsSeq *val;
00664       while(iter.Next(key, val))
00665         {
00666           assert(key.s != NULL);
00667           dead_keys.addhi(key.s);
00668 
00669           assert(val != NULL);
00670           // The sequence elements must be deleted as well.
00671           while(val->size() > 0)
00672             {
00673               const char *val_elem = val->remhi();
00674               assert(val_elem != NULL);
00675               delete [] val_elem;
00676             }
00677           delete val;
00678         }
00679 
00680       // delete the table itself.
00681       delete dead_globalAliasesTable;
00682 
00683       // Delete the key strings.
00684       for(unsigned int i = 0; i < dead_keys.size(); i++)
00685         {
00686           const char *key_str = dead_keys.get(i);
00687           assert(key_str != NULL);
00688           delete [] key_str;
00689         }
00690     }
00691 
00692   while(dead_exportList != NULL)
00693     {
00694       assert(dead_exportList->pattern != NULL);
00695       delete [] dead_exportList->pattern;
00696       if(dead_exportList->realm != NULL)
00697         {
00698           delete [] dead_exportList->realm;
00699         }
00700 
00701       // Save the next element in the linked list.
00702       Export *next = dead_exportList->next;
00703 
00704       // Delete this element and move on to the next (if any).
00705       delete dead_exportList;
00706       dead_exportList = next;
00707     }
00708 
00709   if(dead_exportCache != NULL)
00710     {
00711       CharsSeq dead_keys;
00712       Table<ExportKey, Export*>::Iterator iter(dead_exportCache);
00713       ExportKey key;
00714       Export *val;
00715       while(iter.Next(key, val))
00716         {
00717           // The values are just references to elements of the export
00718           // list (which we've already deleted).  However the key may
00719           // contain an allocated string that we need to delete.
00720           if(key.arg != NULL)
00721             {
00722               dead_keys.addhi(key.arg);
00723             }
00724         }
00725 
00726       // Delete the table itself.
00727       delete dead_exportCache;
00728 
00729       // Delete the realm strings.
00730       for(unsigned int i = 0; i < dead_keys.size(); i++)
00731         {
00732           const char *key_str = dead_keys.get(i);
00733           assert(key_str != NULL);
00734           delete [] key_str;
00735         }
00736     }
00737 }
00738 
00739 
00740 // Make internal tables from the passwd and group files.
00741 static void
00742 newTables(bool continue_on_error = true) throw(AccessControl::ParseError)
00743 {
00744   // Locals representing the new tables we're constructing.
00745   CharsToUIntTable *new_globalToUnixUserTable = NULL;
00746   CharsToUIntTable *new_globalToUnixGroupTable = NULL;
00747   UIntToCharsTable *new_unixToGlobalUserTable = NULL;
00748   UIntToCharsSeqTable *new_unixToGlobalGroupsTable = NULL;
00749   CharsToCharsSeqTable *new_globalUserToGroupsTable = NULL;
00750   CharsToCharsSeqTable *new_globalAliasesTable = NULL;
00751   Export* new_exportList = NULL;
00752   ExportCache *new_exportCache = NULL;
00753 
00754   try
00755     {
00756       //
00757       // 1. Build maps between local unix uids and global user names
00758       //
00759       new_globalToUnixUserTable = NEW(CharsToUIntTable);
00760       new_unixToGlobalUserTable = NEW(UIntToCharsTable);
00761 
00762       { // start of mutex allocation block
00763         OS::Passwd pw_ent;
00764         OS::PasswdIter iterator;
00765         while(iterator.Next(pw_ent)) {
00766           char* name = NEW_PTRFREE_ARRAY(char, (pw_ent.name.Length() +
00767                                          AccessControl::realmlen + 2));
00768           strcpy(name, pw_ent.name.cchars());
00769           strcat(name, "@");
00770           strcat(name, AccessControl::realm);
00771           new_globalToUnixUserTable->Put(name, pw_ent.uid);
00772           const char *dummy;
00773           if(!new_unixToGlobalUserTable->Get(UIntKey(pw_ent.uid), dummy)) {
00774             new_unixToGlobalUserTable->Put(UIntKey(pw_ent.uid),
00775                                            strdup_with_new(name));
00776           }
00777         }
00778       } // end of mutex allocation block
00779 
00780       //
00781       // 2. Build maps between local unix gids and global group names
00782       //
00783       new_globalToUnixGroupTable = NEW(CharsToUIntTable);
00784       new_unixToGlobalGroupsTable = NEW(UIntToCharsSeqTable);
00785 
00786       { // start of mutex allocation block
00787         OS::Group gr_ent;
00788         OS::GroupIter iterator;
00789         while(iterator.Next(gr_ent)) {
00790           char* gname = NEW_PTRFREE_ARRAY(char, (gr_ent.name.Length() +
00791                                                  AccessControl::realmlen + 3));
00792           strcpy(gname, "^");
00793           strcat(gname, gr_ent.name.cchars());
00794           strcat(gname, "@");
00795           strcat(gname, AccessControl::realm);
00796           new_globalToUnixGroupTable->Put(gname, gr_ent.gid);
00797           CharsSeq* seq;
00798           if(!new_unixToGlobalGroupsTable->Get(UIntKey(gr_ent.gid), seq)) {
00799             seq = NEW_CONSTR(CharsSeq, (1));
00800             new_unixToGlobalGroupsTable->Put(UIntKey(gr_ent.gid), seq);
00801           }
00802           seq->addhi(strdup_with_new(gname));
00803         }
00804       } // end of mutex allocation block
00805 
00806       //
00807       // 3. Build table of known global group memberships for global
00808       // user names
00809       //
00810       // 3a. Get each local unix user's primary group from the passwd
00811       // file
00812       //
00813       new_globalUserToGroupsTable = NEW(CharsToCharsSeqTable);
00814       { // start of mutex allocation block
00815         OS::Passwd pw_ent;
00816         OS::PasswdIter iterator;
00817         while(iterator.Next(pw_ent)) {
00818           char* name = NEW_PTRFREE_ARRAY(char, (pw_ent.name.Length() +
00819                                          AccessControl::realmlen + 2));
00820           strcpy(name, pw_ent.name.cchars());
00821           strcat(name, "@");
00822           strcat(name, AccessControl::realm);
00823           CharsSeq* seq;
00824           if(new_unixToGlobalGroupsTable->Get(pw_ent.gid, seq)) {
00825             seq = full_CharSeq_copy(*seq);
00826           } 
00827           else {
00828             // user's primary gid is not in group file
00829             // !!should we enter it as a numeric group name?
00830             seq = NEW_CONSTR(CharsSeq, (0));
00831           }
00832           new_globalUserToGroupsTable->Put(name, seq);
00833         }
00834       } // end of mutex allocation block
00835 
00836       //
00837       // 3b. Get the additional local unix users in each group from
00838       // the group file
00839       //
00840       
00841       { // start of mutex allocation block
00842         OS::Group gr_ent;
00843         OS::GroupIter iterator;
00844         while(iterator.Next(gr_ent)) {
00845           CharsSeq* gnames;
00846           bool found = new_unixToGlobalGroupsTable->Get(gr_ent.gid, gnames);
00847           if (!found)
00848             // rare but possible; group file changed since step 2
00849             continue;
00850           for(int i = 0; i < gr_ent.members.size(); i++) {
00851             Text grmem = gr_ent.members.get(i);
00852             char* name = NEW_PTRFREE_ARRAY(char, (grmem.Length() +
00853                                                   AccessControl::realmlen + 2));
00854             strcpy(name, grmem.cchars());
00855             strcat(name, "@");
00856             strcat(name, AccessControl::realm);
00857             CharsSeq* seq;
00858             if(new_globalUserToGroupsTable->Get(name, seq)) {
00859               // Add this group under all its names
00860               delete[] name;
00861               for(int i = 0; i < gnames->size(); i++) {
00862                 addhi_nondup(seq, gnames->get(i)); 
00863               }
00864             }
00865             else {
00866               // User in a group but not in passwd file.  Add anyway (?).
00867               seq = full_CharSeq_copy(*gnames);
00868               new_globalUserToGroupsTable->Put(name, seq);
00869             }
00870           }
00871         }
00872       } // end of mutex allocation block
00873       
00874       //
00875       // 3c. Add more global users and groups from the Vesta group
00876       // file
00877       //
00878       try {
00879         assert(vestaGroupFile != NULL);
00880         parseMembershipFile(vestaGroupFile, new_globalUserToGroupsTable);
00881       } catch (AccessControl::ParseError f) {
00882         Repos::dprintf(DBG_ALWAYS, "error parsing group file %s: %s\n",
00883                        vestaGroupFile, f.message.cchars());
00884         if(!continue_on_error)
00885           {
00886             f.fname = vestaGroupFile;
00887             f.fkind = "group file";
00888             throw f;
00889           }
00890       }
00891 
00892       //
00893       // 4. Build table of global user and group name aliases from
00894       // Vesta alias file
00895       //
00896       new_globalAliasesTable = NEW(CharsToCharsSeqTable);
00897       try {
00898         assert(vestaAliasFile != NULL);
00899         parseMembershipFile(vestaAliasFile, new_globalAliasesTable,
00900                             AccessControl::rootUser);
00901       } catch (AccessControl::ParseError f) {
00902         Repos::dprintf(DBG_ALWAYS, "error parsing alias file %s: %s\n",
00903                        vestaAliasFile, f.message.cchars());
00904         if(!continue_on_error)
00905           {
00906             f.fname = vestaAliasFile;
00907             f.fkind = "alias file";
00908             throw f;
00909           }
00910       }
00911 
00912       //
00913       // 4a. Add still more groups to globalUserToGroupsTable by
00914       // expanding global group aliases
00915       //
00916       CharsToCharsSeqIter iter(new_globalUserToGroupsTable);
00917       CharsKey namekey;
00918       CharsSeq* gnames;
00919       while (iter.Next(namekey, gnames)) {
00920         int i;
00921         int sz = gnames->size();
00922         for (i=0; i<sz; i++) {
00923           CharsSeq* galiases;
00924           if (new_globalAliasesTable->Get(gnames->get(i), galiases)) {
00925             int j;
00926             for (j=0; j<galiases->size(); j++) {
00927               addhi_nondup(gnames, galiases->get(j));
00928             }
00929           }
00930         }
00931       }
00932 
00933       //
00934       // 4b. Add yet more groups by giving users the groups of their
00935       // aliases
00936       //
00937       CharsToCharsSeqIter iter2(new_globalAliasesTable);
00938       CharsSeq* aliases;
00939       while (iter2.Next(namekey, aliases)) {
00940         if (namekey.s[0] == '^') continue;
00941         if (!new_globalUserToGroupsTable->Get(namekey.s, gnames)) {
00942           gnames = NEW(CharsSeq);
00943           new_globalUserToGroupsTable->Put(strdup_with_new(namekey.s),
00944                                            gnames);
00945         }
00946         int i;
00947         for (i=0; i<aliases->size(); i++) {
00948           CharsSeq* gnames2;
00949           if (new_globalUserToGroupsTable->Get(aliases->get(i), gnames2)) {
00950             int j;
00951             for (j=0; j<gnames2->size(); j++) {
00952               addhi_nondup(gnames, gnames2->get(j));
00953             }
00954           }
00955         }
00956       }
00957 
00958       //
00959       // 5. Build export table from Vesta export file
00960       //
00961       new_exportCache = NEW(ExportCache);
00962       try {
00963         parseExportFile(vestaExportFile, new_exportList);
00964       } catch (AccessControl::ParseError f) {
00965         Repos::dprintf(DBG_ALWAYS, "error parsing export file %s: %s\n",
00966                        vestaExportFile, f.message.cchars());
00967         if(!continue_on_error)
00968           {
00969             f.fname = vestaExportFile;
00970             f.fkind = "export file";
00971             throw f;
00972           }
00973       }
00974     }
00975   catch(...)
00976     {
00977       // If we fail due to a parsing error, free any partially
00978       // constructed tables.
00979       freeTables(new_globalToUnixUserTable,
00980                  new_globalToUnixGroupTable,
00981                  new_unixToGlobalUserTable,
00982                  new_unixToGlobalGroupsTable,
00983                  new_globalUserToGroupsTable,
00984                  new_globalAliasesTable,
00985                  new_exportList,
00986                  new_exportCache);
00987       throw;
00988     }
00989 
00990   // Propagate entries from exportCache.
00991   export_lock.acquireRead();
00992   if(exportCache != 0)
00993     {
00994       ExportCacheIter it(exportCache);
00995       ExportKey key; Export *val;
00996       while(it.Next(key, val))
00997         {
00998           // Search for a matching export entry from the new list.
00999           Export *exp = new_exportList;
01000           while((exp != NULL) && !exp->match(key))
01001             exp = exp->next;
01002 
01003           // Copy the argument (if any), as the old one will be freed.
01004           if (key.arg) key.arg = strdup_with_new(key.arg);
01005 
01006           // Insert an entry for this key into the new exportCache.
01007           new_exportCache->Put(key, exp);
01008         }
01009     }
01010   export_lock.releaseRead();
01011 
01012   // Now acquire the global lock on the tables so we can replace the
01013   // existing ones (if any) with our new ones).
01014   export_lock.acquireWrite();
01015   userGroup_lock.acquireWrite();
01016 
01017   RWLOCK_LOCKED_REASON(&userGroup_lock, "newTables");
01018 
01019   // Collect the old tables so we can free them after replacing them.
01020   CharsToUIntTable *old_globalToUnixUserTable = globalToUnixUserTable;
01021   CharsToUIntTable *old_globalToUnixGroupTable = globalToUnixGroupTable;
01022   UIntToCharsTable *old_unixToGlobalUserTable = unixToGlobalUserTable;
01023   UIntToCharsSeqTable *old_unixToGlobalGroupsTable = unixToGlobalGroupsTable;
01024   CharsToCharsSeqTable *old_globalUserToGroupsTable = globalUserToGroupsTable;
01025   CharsToCharsSeqTable *old_globalAliasesTable = globalAliasesTable;
01026   Export* old_exportList = Export::first;
01027   ExportCache *old_exportCache = exportCache;
01028 
01029   // Replace the tales with the new ones.
01030   globalToUnixUserTable = new_globalToUnixUserTable;
01031   globalToUnixGroupTable = new_globalToUnixGroupTable;
01032   unixToGlobalUserTable = new_unixToGlobalUserTable;
01033   unixToGlobalGroupsTable = new_unixToGlobalGroupsTable;
01034   globalUserToGroupsTable = new_globalUserToGroupsTable;
01035   globalAliasesTable = new_globalAliasesTable;
01036   Export::first = new_exportList;
01037   exportCache = new_exportCache;
01038   exportEpoch++;
01039 
01040   // Remember that we've refreshed the tables.
01041   last_refresh = time(0);
01042 
01043   userGroup_lock.releaseWrite();
01044   export_lock.releaseWrite();
01045 
01046   // If we're supposed to, display the new access control tables.
01047   if (Repos::isDebugLevel(DBG_ACCESS)) {
01048     CharsToUIntIter iter1(new_globalToUnixUserTable);
01049     Repos::dprintf(DBG_ACCESS, "\nglobalToUnixUserTable:\n");
01050     CharsKey namekey;
01051     unsigned int id;
01052     while (iter1.Next(namekey, id)) {
01053       Repos::dprintf(DBG_ACCESS, "%s\t-> %u\n", namekey.s, id);
01054     }
01055 
01056     CharsToUIntIter iter2(new_globalToUnixGroupTable);
01057     Repos::dprintf(DBG_ACCESS, "\nglobalToUnixGroupTable:\n");
01058     while (iter2.Next(namekey, id)) {
01059       Repos::dprintf(DBG_ACCESS, "%s\t-> %u\n", namekey.s, id);
01060     }
01061 
01062     UIntToCharsIter iter3(new_unixToGlobalUserTable);
01063     Repos::dprintf(DBG_ACCESS, "\nunixToGlobalUserTable:\n");
01064     UIntKey idkey;
01065     const char* name;
01066     while (iter3.Next(idkey, name)) {
01067       Repos::dprintf(DBG_ACCESS, "%u\t-> %s\n", idkey.i, name);
01068     }
01069     
01070     UIntToCharsSeqIter iter4(new_unixToGlobalGroupsTable);
01071     CharsSeq* seq;
01072     Repos::dprintf(DBG_ACCESS, "\nunixToGlobalGroupsTable:\n");
01073     while (iter4.Next(idkey, seq)) {
01074       Repos::dprintf(DBG_ACCESS, "%u\t-> %s\n", idkey.i, seq->get(0));
01075       int i;
01076       for (i=1; i<seq->size(); i++) {
01077         Repos::dprintf(DBG_ACCESS, "\t   %s\n", seq->get(i));
01078       }
01079     }
01080     
01081     CharsToCharsSeqIter iter5(new_globalUserToGroupsTable);
01082     Repos::dprintf(DBG_ACCESS, "\nglobalUserToGroupsTable:\n");
01083     while (iter5.Next(namekey, seq)) {
01084       Repos::dprintf(DBG_ACCESS, "%s\t-> %s\n",
01085               namekey.s, seq->size() ? seq->get(0) : "");
01086       int i;
01087       for (i=1; i<seq->size(); i++) {
01088         Repos::dprintf(DBG_ACCESS, "\t   %s\n", seq->get(i));
01089       }
01090     }
01091 
01092     CharsToCharsSeqIter iter6(new_globalAliasesTable);
01093     Repos::dprintf(DBG_ACCESS, "\nglobalAliasesTable:\n");
01094     while (iter6.Next(namekey, seq)) {
01095       Repos::dprintf(DBG_ACCESS, "%s\t-> %s\n", namekey.s, seq->get(0));
01096       int i;
01097       for (i=1; i<seq->size(); i++) {
01098         Repos::dprintf(DBG_ACCESS, "\t   %s\n", seq->get(i));
01099       }
01100     }
01101 
01102     Export* exp = new_exportList;
01103     Repos::dprintf(DBG_ACCESS, "\nexportTable:\n");
01104     while (exp) {
01105       Repos::dprintf(DBG_ACCESS, "%s %d %s -> %d\n",
01106                      exp->pattern, exp->flavor, exp->realm, exp->level);
01107       exp = exp->next;
01108     }
01109   }
01110 
01111   // Free all the old tables (if there were any).
01112   freeTables(old_globalToUnixUserTable,
01113              old_globalToUnixGroupTable,
01114              old_unixToGlobalUserTable,
01115              old_unixToGlobalGroupsTable,
01116              old_globalUserToGroupsTable,
01117              old_globalAliasesTable,
01118              old_exportList,
01119              old_exportCache);
01120 }
01121 
01122 // Call newTables if we haven't refreshed the access control tables
01123 // recently.
01124 static bool maybeNewTables()
01125 {
01126   bool reload_complete = false;
01127 
01128   // If it's been long enough since we last refreshed the tables...
01129   time_t now = time(0);
01130   if((now - last_refresh) > min_auto_refresh_delay)
01131     {
01132       // This function is called with the a read lock on the user and
01133       // group control tables held.  We unlock it now because
01134       // newTables will lock it when it replaces the access tables.
01135       userGroup_lock.releaseRead();
01136       try
01137         {
01138           Repos::dprintf(DBG_ALWAYS,
01139                          "Starting automatic refresh of access control information\n");
01140           newTables(/*continue_on_error=*/false);
01141           Repos::dprintf(DBG_ALWAYS,
01142                          "Completed automatic refresh of access control information\n");
01143 
01144           // If we make it here without an exception, the relaod
01145           // completed successfully.
01146           reload_complete = true;
01147         }
01148       catch(...)
01149         {
01150           Repos::dprintf(DBG_ALWAYS,
01151                          "WARNING: Automatic refresh of access control information failed\n");
01152         }
01153       // Reacquire the lock on the user and group tables.
01154       userGroup_lock.acquireRead();
01155     }
01156   return reload_complete;
01157 }
01158 
01159 const char*
01160 AccessControl::GlobalIdentityRep::user(int n) throw ()
01161 {
01162   if (n == 0) {
01163     return user_;
01164   }
01165   return this->AccessControl::IdentityRep::user(n);
01166 }
01167 
01168 const char*
01169 AccessControl::GlobalIdentityRep::group(int n) throw ()
01170 {
01171   return this->AccessControl::IdentityRep::group(n);
01172 }
01173 
01174 void AccessControl::GlobalIdentityRep::fill_caches() throw()
01175 {
01176   assert(users_cache == 0);
01177   assert(groups_cache == 0);
01178 
01179   userGroup_lock.acquireRead();
01180   CharsSeq* seq;
01181   RWLOCK_LOCKED_REASON(&userGroup_lock, "GlobalIdentityRep::fill_caches");
01182   if (globalAliasesTable->Get(user_, seq))
01183     {
01184       users_cache = full_CharSeq_copy(*seq, 1);
01185     }
01186   else
01187     {
01188       users_cache = NEW_CONSTR(CharsSeq, (1));
01189     }
01190   users_cache->addlo(strdup_with_new(user_));
01191 
01192   // A GlobalIdentity user gets exactly the global groups associated
01193   // with his global name.  There is no support for groups that the
01194   // user entered by typing a passwd to the Unix newgrp command.
01195   if (globalUserToGroupsTable->Get(user_, seq))
01196     {
01197       groups_cache = full_CharSeq_copy(*seq);
01198     }
01199   else
01200     {
01201       // User has *no* group memberships in this case
01202       groups_cache = NEW_CONSTR(CharsSeq, (1));
01203     }
01204   userGroup_lock.releaseRead();
01205 }
01206 
01207 const char*
01208 AccessControl::UnixIdentityRep::user(int n) throw ()
01209 {
01210   return this->AccessControl::IdentityRep::user(n);
01211 }
01212 
01213 const char*
01214 AccessControl::UnixIdentityRep::group(int n) throw ()
01215 {
01216   return this->AccessControl::IdentityRep::group(n);
01217 }
01218 
01219 void AccessControl::UnixIdentityRep::fill_caches() throw()
01220 {
01221   assert(users_cache == 0);
01222   assert(groups_cache == 0);
01223 
01224   userGroup_lock.acquireRead();
01225 
01226   RWLOCK_LOCKED_REASON(&userGroup_lock, "UnixIdentityRep::fill_caches (initial)");
01227 
01228   const char *user = unixToGlobalUserNL(aup_->aup_uid);
01229 
01230   RWLOCK_LOCKED_REASON(&userGroup_lock, "UnixIdentityRep::fill_caches (post-user)");
01231 
01232   CharsSeq* seq;
01233   if(globalAliasesTable->Get(user, seq))
01234     {
01235       users_cache = full_CharSeq_copy(*seq, 1);
01236     }
01237   else
01238     {
01239       users_cache = NEW_CONSTR(CharsSeq, (1));
01240     }
01241   users_cache->addlo(strdup_with_new(user));
01242 
01243   // A UnixIdentity user gets all the global groups associated with
01244   // his global name.
01245 
01246   // In addition, he may get the translation of each of the local gids
01247   // that are listed in his AuthUnix structure to a global group.
01248   // This feature has several problems, so it's turned off by default.
01249   // Here are the problems:
01250 
01251   // (1) It's normally redundant, since the gid list will normally be
01252   // exactly the gids of the global groups that the global name is a
01253   // member of.  There could be one additional gid if the user did a
01254   // newgrp to a group he was not a member of, but that is a rarely
01255   // used feature, and the user doesn't get access to that group if he
01256   // goes through SRPC and presents a GlobalIdentity, so making it
01257   // work through UnixIdentity has little value; it might even be more
01258   // confusing than useful.  The gid list could also be different if
01259   // group memberships have changed since the repository was last
01260   // restarted, but we don't handle the general case of passwd/group
01261   // changes after restart anyway, just simple cases of additions.
01262 
01263   // (2) Even if a gid has multiple names, only its first name is
01264   // returned.  This is basically a bug, but it's a pain to fix since
01265   // yielding multiple groups names per gid doesn't fit well into the
01266   // random-access group(i) style of iterating through the groups.
01267 
01268   // (3) Including these additional groups can make for slightly more
01269   // work during access checks, slowing down operations.
01270 
01271   if(globalUserToGroupsTable->Get(user, seq))
01272     {
01273       groups_cache = full_CharSeq_copy(*seq,
01274                                        (honor_client_gids
01275                                         ? aup_->aup_len+1
01276                                         : 0));
01277     }
01278   else
01279     {
01280       // Note: if honor_client_gids is false, this user has *no* group
01281       // memberships
01282       groups_cache = NEW_CONSTR(CharsSeq, (honor_client_gids
01283                                             ? aup_->aup_len+1
01284                                             : 1));
01285     }
01286 
01287   if(honor_client_gids)
01288     {
01289       groups_cache->addlo(strdup_with_new(unixToGlobalGroupNL(aup_->aup_gid)));
01290       RWLOCK_LOCKED_REASON(&userGroup_lock, "UnixIdentityRep::fill_caches (post-group)");
01291       for(unsigned int i = 0; i < aup_->aup_len; i++)
01292         {
01293           groups_cache->addhi(strdup_with_new(unixToGlobalGroupNL(aup_->aup_gids[i])));
01294           RWLOCK_LOCKED_REASON(&userGroup_lock, "UnixIdentityRep::fill_caches (post-group)");
01295         }
01296     }
01297 
01298   userGroup_lock.releaseRead();
01299 }
01300 
01301 void AccessControl::UnixIdentityRep::validate() throw()
01302 {
01303   if(users_cache == 0) fill_caches();
01304 }
01305 
01306 uid_t 
01307 AccessControl::globalToUnixUser(const char* user) throw ()
01308 {
01309   unsigned int ret = vforeignUser;
01310   if(user)
01311     {
01312       unsigned int v;
01313       userGroup_lock.acquireRead();
01314       RWLOCK_LOCKED_REASON(&userGroup_lock, "globalToUnixUser");
01315       if (globalToUnixUserTable->Get(user, v)) {
01316         ret = v;
01317       } else {
01318         char buf[MAX_MACHINE_NAME + 32];
01319         if (sscanf(user, "%u@%s", &v, buf) == 2 &&
01320             strcasecmp(buf, realm) == 0) {
01321           // Interpret 123@foo as the user with uid 123.  This is
01322           // an escape to handle users not in the local passwd file.
01323           ret = v;
01324         }
01325       }
01326       userGroup_lock.releaseRead();
01327     }
01328   return ret;
01329 }
01330 
01331 gid_t 
01332 AccessControl::globalToUnixGroup(const char* group) throw ()
01333 {
01334   unsigned int ret = vforeignGroup;
01335   if(group)
01336     {
01337       unsigned int v;
01338       userGroup_lock.acquireRead();
01339       RWLOCK_LOCKED_REASON(&userGroup_lock, "globalToUnixGroup");
01340       if (globalToUnixGroupTable->Get(group, v)) {
01341         ret = v;
01342       } else {
01343         char buf[MAX_MACHINE_NAME + 32];
01344         if (sscanf(group, "^%u@%s", &v, buf) == 2 &&
01345             strcasecmp(buf, realm) == 0) {
01346           // Interpret ^123@foo as the group with gid 123.  This is
01347           // an escape to handle groups not in the local group file.
01348           ret = v;
01349         }
01350       }
01351       userGroup_lock.releaseRead();
01352     }
01353   return ret;
01354 }
01355 
01356 static const char*
01357 unixToGlobalUserNL(uid_t uid) throw ()
01358 {
01359   const char* v;
01360   if (unixToGlobalUserTable->Get(uid, v)) {
01361     return v;
01362   }
01363 
01364   // Uncommon case (uid was not in passwd file when table was built).
01365   // Try re-loading the access control tables (which we may not do if
01366   // it's been done recently and can fail if there are parsing errors
01367   // in any of the access control files).
01368 
01369   // If we managed to complete the reload and there is now an entry
01370   // for this uid, use that.
01371   if(maybeNewTables() && unixToGlobalUserTable->Get(uid, v))
01372     {
01373       return v;
01374     }
01375 
01376   // That failed, now we're going to need to modify the tables.
01377   userGroup_lock.releaseRead();
01378 
01379   // uid was not in passwd file when table was built, and either we
01380   // couldn't reload the tables or this uid doesn't exist in the new
01381   // table.  Try to get the user from the system user table.
01382   
01383   OS::Passwd pw_ent;
01384   if(OS::getPwUid(uid, pw_ent)) {
01385     // add user to globalToUnixUserTable and unixToGlobalUserTable
01386     char* name = NEW_PTRFREE_ARRAY(char, (pw_ent.name.Length() +
01387                                           AccessControl::realmlen + 2));
01388     strcpy(name, pw_ent.name.cchars());
01389     strcat(name, "@");
01390     strcat(name, AccessControl::realm);
01391     // add user and his primary group to globalUserToGroupsTable
01392     CharsSeq* seq;
01393     if(unixToGlobalGroupsTable->Get(pw_ent.gid, seq)) {
01394       seq = full_CharSeq_copy(*seq);
01395     } 
01396     else {
01397       // user's primary gid is not in group file
01398       // !!should we enter it as a numeric group name?
01399       seq = NEW_CONSTR(CharsSeq, (0));
01400     }
01401 
01402     // Modify the tables after preparing all new entries.
01403     userGroup_lock.acquireWrite();
01404     
01405     RWLOCK_LOCKED_REASON(&userGroup_lock, "unixToGlobalUserNL: adding known");
01406 
01407     // Final check needed, since we released our lock another thread
01408     // may have already filled this in.
01409     if(unixToGlobalUserTable->Get(uid, v)) {
01410       delete name;
01411       delete seq; // @@@ Need to free sequence entries
01412       userGroup_lock.releaseWrite();
01413       userGroup_lock.acquireRead();
01414       return v;
01415     }
01416     else {
01417       globalToUnixUserTable->Put(name, pw_ent.uid);
01418       unixToGlobalUserTable->Put(UIntKey(pw_ent.uid),
01419                                  strdup_with_new(name));
01420       globalUserToGroupsTable->Put(strdup_with_new(name), seq);
01421     }
01422     userGroup_lock.releaseWrite();
01423     
01424     // Reacquire the lock on the access tables before returning.
01425     userGroup_lock.acquireRead();
01426     return name;
01427   }
01428   
01429   // Very uncommon case; uid is not in passwd file at all. Leaks
01430   // memory.  Could add to table to avoid the leak, but name might
01431   // be added to passwd file later, so maybe it's better to keep
01432   // looking there each time.
01433   char *buf = NEW_PTRFREE_ARRAY(char, (16 + strlen(AccessControl::realm)));
01434   sprintf(buf, "%u@%s", uid, AccessControl::realm);
01435 
01436   // Modify the tables
01437   userGroup_lock.acquireWrite();
01438   RWLOCK_LOCKED_REASON(&userGroup_lock, "unixToGlobalUserNL: adding unknown");
01439   globalToUnixUserTable->Put(buf, uid);
01440   unixToGlobalUserTable->Put(UIntKey(uid), strdup_with_new(buf));
01441   userGroup_lock.releaseWrite();
01442 
01443   Repos::dprintf(DBG_ALWAYS,
01444                  "WARNING: request for uid %d which has no local mapping\n",
01445                  uid);
01446 
01447   // Reacquire the lock on the access tables before returning.
01448   userGroup_lock.acquireRead();
01449   return buf;
01450 }
01451 
01452 const char*
01453 AccessControl::unixToGlobalUser(uid_t uid) throw ()
01454 {
01455   userGroup_lock.acquireRead();
01456   RWLOCK_LOCKED_REASON(&userGroup_lock, "unixToGlobalUser (pre)");
01457   const char* ret = unixToGlobalUserNL(uid);
01458   // We add a second locked reason here, because unixToGlobalUserNL
01459   // may release and re-acquire the lock if this user ID isn't already
01460   // known
01461   RWLOCK_LOCKED_REASON(&userGroup_lock, "unixToGlobalUser (post)");
01462   userGroup_lock.releaseRead();
01463   return ret;
01464 }
01465 
01466 static const char*
01467 unixToGlobalGroupNL(gid_t gid) throw ()
01468 {
01469   CharsSeq* seq;
01470   if (unixToGlobalGroupsTable->Get(gid, seq)) {
01471     return seq->get(0);
01472   }
01473 
01474   // Uncommon case (gid was not in group file when table was built).
01475   // First, try re-loading the access control tables (which we may not
01476   // do if it's been done recently and can fail if there are parsing
01477   // errors in any of the access control files).
01478 
01479   // If we managed to complete the reload and there is now an entry
01480   // for this gid, use that.
01481   if(maybeNewTables() && unixToGlobalGroupsTable->Get(gid, seq))
01482     {
01483       return seq->get(0);
01484     }
01485 
01486   // That failed, now we're going to need to modify the tables.
01487   userGroup_lock.releaseRead();
01488 
01489   // gid was not in group file when table was built, and either we
01490   // couldn't reload or this gid doesn't exist in the new tables.  Try
01491   // to get the group from the system group table.
01492   OS::Group gr_ent;
01493   if(OS::getGrGid(gid, gr_ent)) {
01494     // add group to globalToUnixGroupTable and unixToGlobalGroupsTable
01495     char* gname = NEW_PTRFREE_ARRAY(char, (gr_ent.name.Length() +
01496                                            AccessControl::realmlen + 3));
01497     strcpy(gname, "^");
01498     strcat(gname, gr_ent.name.cchars());
01499     strcat(gname, "@");
01500     strcat(gname, AccessControl::realm);
01501     seq = NEW_CONSTR(CharsSeq, (1));
01502     seq->addhi(strdup_with_new(gname));
01503 
01504     // Modify the tables
01505     userGroup_lock.acquireWrite();
01506     RWLOCK_LOCKED_REASON(&userGroup_lock, "unixToGlobalGroupNL: adding known");
01507     globalToUnixGroupTable->Put(gname, gr_ent.gid);
01508     unixToGlobalGroupsTable->Put(UIntKey(gr_ent.gid), seq);
01509 
01510     // add group to globalUserToGroupsTable entry of each member
01511     for(int i = 0; i < gr_ent.members.size(); i++) {
01512       Text grmem = gr_ent.members.get(i);
01513       char* name = NEW_PTRFREE_ARRAY(char, (grmem.Length() +
01514                                             AccessControl::realmlen + 2));
01515       strcpy(name, grmem.cchars());
01516       strcat(name, "@");
01517       strcat(name, AccessControl::realm);
01518       CharsSeq* seq;
01519       if (globalUserToGroupsTable->Get(name, seq)) {
01520         delete[] name;
01521       } 
01522       else {
01523         // User in a group but not in passwd file.  Add anyway (?).
01524         seq = NEW_CONSTR(CharsSeq, (1));
01525         seq->addhi(strdup_with_new(gname));
01526         globalUserToGroupsTable->Put(name, seq);
01527       }
01528       seq->addhi(strdup_with_new(gname));
01529     }
01530     userGroup_lock.releaseWrite();
01531     
01532     // Reacquire the lock on the access tables before returning.
01533     userGroup_lock.acquireRead();
01534     return gname;
01535   }
01536 
01537   // Very uncommon case; gid is not in groups file at all. Leaks
01538   // memory.  Could add to table to avoid the leak, but name might
01539   // be added to groups file later, so maybe it's better to keep
01540   // looking there each time.
01541   char *buf = NEW_PTRFREE_ARRAY(char, (16 + strlen(AccessControl::realm)));
01542   sprintf(buf, "^%u@%s", gid, AccessControl::realm);
01543 
01544   // Modify the tables
01545   userGroup_lock.acquireWrite();
01546   RWLOCK_LOCKED_REASON(&userGroup_lock, "unixToGlobalGroupNL: adding unknown");
01547   globalToUnixGroupTable->Put(buf, gid);
01548   seq = NEW_CONSTR(CharsSeq, (1));
01549   seq->addhi(strdup_with_new(buf));
01550   unixToGlobalGroupsTable->Put(UIntKey(gid), seq);
01551   userGroup_lock.releaseWrite();
01552 
01553   Repos::dprintf(DBG_ALWAYS,
01554                  "WARNING: request for gid %d which has no local mapping\n",
01555                  gid);
01556 
01557   // Reacquire the lock on the access tables before returning.
01558   userGroup_lock.acquireRead();
01559   return buf;
01560 }
01561 
01562 const char*
01563 AccessControl::unixToGlobalGroup(gid_t gid) throw ()
01564 {
01565   userGroup_lock.acquireRead();
01566   RWLOCK_LOCKED_REASON(&userGroup_lock, "unixToGlobalGroup (pre)");
01567   const char* ret = unixToGlobalGroupNL(gid);
01568   // We add a second locked reason here, because unixToGlobalGroupNL
01569   // may release and re-acquire the lock if this group ID isn't already
01570   // known
01571   RWLOCK_LOCKED_REASON(&userGroup_lock, "unixToGlobalGroup (post)");
01572   userGroup_lock.releaseRead();
01573   return ret;
01574 }
01575 
01576 bool
01577 toUnixUserCallback(void* closure, const char* value)
01578 {
01579   const char* at = strchr(value, '@');
01580   if (at && strcasecmp(at+1, AccessControl::realm) == 0) {
01581     *(uid_t*) closure = AccessControl::globalToUnixUser(value);
01582     return false;
01583   }
01584   return true;
01585 }
01586 
01587 // Find a user in the local realm on the owner ACL and return his
01588 // local numeric uid.  If none is found, return vforeignUser.
01589 uid_t
01590 AccessControl::toUnixUser() throw ()
01591 {
01592   uid_t ret = vforeignUser;
01593   owner.getAttrib("#owner", toUnixUserCallback, &ret);
01594   return ret;
01595 }
01596 
01597 bool
01598 toUnixGroupCallback(void* closure, const char* value)
01599 {
01600   const char* at = strchr(value, '@');
01601   if (at && strcasecmp(at+1, AccessControl::realm) == 0) {
01602     *(gid_t*) closure = AccessControl::globalToUnixGroup(value);
01603     return false;
01604   }
01605   return true;
01606 }
01607 
01608 // Find a group in the local realm on the group ACL and return its
01609 // local numeric uid.  If none is found, return vforeignGroup.
01610 gid_t
01611 AccessControl::toUnixGroup() throw ()
01612 {
01613   uid_t ret = vforeignGroup;
01614   owner.getAttrib("#group", toUnixGroupCallback, &ret);
01615   return ret;
01616 }
01617 
01618 bool
01619 AccessControl::IdentityRep::userMatch(const char* name) throw ()
01620 {
01621   int i = 0;
01622   const char* uname;
01623   while ((uname = user(i++)) != NULL) {
01624     if (strcasecmp(name, uname) == 0) return true;
01625   }
01626   return false;
01627 }
01628 
01629 bool
01630 AccessControl::IdentityRep::groupMatch(const char* name) throw ()
01631 {
01632   int i = 0;
01633   const char* gname;
01634   while ((gname = group(i++)) != NULL) {
01635     if (strcasecmp(name, gname) == 0) return true;
01636   }
01637   return false;
01638 }
01639 
01640 bool
01641 AccessControl::IdentityRep::userMatch(const char* aname,
01642                                       VestaAttribs attribs) throw ()
01643 {
01644   int i = 0;
01645   const char* uname;
01646   while ((uname = user(i++)) != NULL) {
01647     if (attribs.inAttribs(aname, uname)) return true;
01648   }
01649   return false;
01650 }
01651 
01652 bool
01653 AccessControl::IdentityRep::groupMatch(const char* aname,
01654                                        VestaAttribs attribs) throw ()
01655 {
01656   int i = 0;
01657   const char* gname;
01658   while ((gname = group(i++)) != NULL) {
01659     if (attribs.inAttribs(aname, gname)) return true;
01660   }
01661   return false;
01662 }
01663 
01664 bool
01665 AccessControl::check(AccessControl::Identity who, AccessControl::Class cls,
01666                      const char* value)
01667   throw ()
01668 {
01669   // Everything is OK for internal repository code
01670   if (who == NULL) return true;
01671 
01672   ModeBits ckbit;
01673   switch (cls) {
01674   case AccessControl::unrestricted:
01675     // Always OK.
01676     return true;
01677   case AccessControl::administrative:
01678     // OK for privileged users only.
01679     if (who->readOnly) return false;
01680     return who->userMatch(rootUser) || who->userMatch(vadminUser) ||
01681       who->userMatch(vwizardUser);
01682   case AccessControl::ownership:
01683     // OK for owner.
01684     // OK for privileged users.
01685     if (who->readOnly) return false;
01686     return who->userMatch("#owner", this->owner) ||
01687       who->userMatch(rootUser) || who->userMatch(vadminUser) ||
01688       who->userMatch(vwizardUser);
01689   case AccessControl::setuid:
01690     // OK for root.
01691     // OK for owner if value==NULL or who->userMatch(value)
01692     if (who->readOnly) return false;
01693     return ((who->userMatch("#owner", this->owner) &&
01694              (value == NULL || who->userMatch(value))) ||
01695             who->userMatch(rootUser));
01696   case AccessControl::setgid:
01697     // OK for root.
01698     // OK for owner if value==NULL or who->groupMatch(value)
01699     if (who->readOnly) return false;
01700     return ((who->userMatch("#owner", this->owner) &&
01701              (value == NULL || who->groupMatch(value))) ||
01702             who->userMatch(rootUser));
01703   case AccessControl::read:
01704     // Check further below.
01705     ckbit = 04;
01706     break;
01707   case AccessControl::write:
01708     if (who->readOnly) return false;
01709     // Check further below.
01710     ckbit = 02;
01711     break;
01712   case AccessControl::search:
01713     // Check further below.
01714     ckbit = 01;
01715     break;
01716   case AccessControl::del:
01717     if (who->readOnly) return false;
01718     // If delete is restricted, only OK for privileged users
01719     if (restrictDelete)
01720       return (who->userMatch(rootUser) || who->userMatch(vadminUser) ||
01721               who->userMatch(vwizardUser));
01722     // Check further below.
01723     ckbit = 02;
01724     break;
01725   case AccessControl::agreement:
01726     // OK only when doing wizardly repairs, when you have special
01727     // knowledge that they will not break the replica agreement invariant.
01728     // Normally agreement access is used only by internal repository code.
01729     return who->userMatch(vwizardUser);
01730   default:
01731     // Undefined
01732     assert(false);
01733   }
01734     
01735   // Note: Because checking for owner access is a bit slow, and group
01736   // access is even slower, we check world, then owner, then group,
01737   // and if any check fails, we go on to the next.  This makes us more
01738   // liberal than traditional Unix, in which -r-xr-xrwx means that the
01739   // owner and group can't write, but others can -- a fairly useless
01740   // feature.
01741 
01742   // World access OK?
01743   if ((ckbit & this->mode) != 0) return true;
01744 
01745   // Owner access OK?
01746   if (who->userMatch("#owner", this->owner) &&
01747       (((ckbit << 6) & this->mode) != 0)) return true;
01748 
01749   // Group access OK?
01750   if (who->groupMatch("#group", this->group) &&
01751       (((ckbit << 3) & this->mode) != 0)) return true;
01752 
01753   switch (cls) {
01754   case AccessControl::unrestricted:
01755   case AccessControl::administrative:
01756   case AccessControl::ownership:
01757   case AccessControl::setuid:
01758   case AccessControl::setgid:
01759   case AccessControl::agreement:
01760     // These are all covered above, so we shouln't get here.
01761     assert(false);
01762     break;
01763   case AccessControl::read:
01764     // OK for privileged users
01765     if (who->userMatch(rootUser) || who->userMatch(vadminUser) ||
01766         who->userMatch(vwizardUser)) return true;
01767     break;
01768   case AccessControl::write:
01769     // OK for privileged users
01770     if (who->userMatch(rootUser) || who->userMatch(vadminUser) ||
01771         who->userMatch(vwizardUser)) return true;
01772     break;
01773   case AccessControl::search:
01774     // OK for privileged users
01775     if (who->userMatch(rootUser) || who->userMatch(vadminUser) ||
01776         who->userMatch(vwizardUser)) return true;
01777     break;
01778   case AccessControl::del:
01779     // We shouldn't get here if delete is restricted to administrators
01780     assert(!restrictDelete);
01781     // OK for privileged users
01782     if (who->userMatch(rootUser) || who->userMatch(vadminUser) ||
01783         who->userMatch(vwizardUser)) return true;
01784     break;
01785   default:
01786     // Undefined
01787     assert(false);
01788   }
01789 
01790   return false;
01791 }
01792 
01793 // Try to interpret the pattern in exp as a numeric address or subnet.
01794 // Return -1 if it is not a numeric address or subnet, 0 if it is but
01795 // it doesn't match, 1 if it is and does match.
01797 static int
01798 numericMatch(Export* exp, Bit32 s_addr)
01799 {
01800   Bit32 addr, netmask;
01801   unsigned int a, b, c, d, e, f, g, h, n;
01802   n = sscanf(exp->pattern, "%3u.%3u.%3u.%3u/%3u.%3u.%3u.%3u",
01803              &a, &b, &c, &d, &e, &f, &g, &h);
01804   if (n >= 4) {
01805     if (a > 255 || b > 255 || c > 255 || d > 255) return -1;
01806     addr = htonl((a<<24) + (b<<16) + (c<<8) + d);
01807   }
01808   if (n == 4) {
01809     // Single IP address
01810     netmask = htonl(0xffffffff);
01811   } else if (n == 5) {
01812     // Subnet with netmask given as count of leftmost set bits
01813     if (e > 32) return -1;
01814     netmask = e ? (htonl((0xffffffff) << (32 - e))) : 0;
01815   } else if (n == 8) {
01816     // Subnet with netmask given as bitmap
01817     if (e > 255 || f > 255 || g > 255 || h > 255) return -1;
01818     netmask = htonl((e<<24) + (f<<16) + (g<<8) + h);
01819   } else {
01820     return -1;
01821   }     
01822 
01823   if ((s_addr ^ addr) & netmask) {
01824     return 0;
01825   } else {
01826     return 1;
01827   }
01828 }
01829 
01830 //
01831 // Match a hostname pattern.  
01832 // '*' matches zero or more 
01833 // '?' matches one non-'.' character.
01834 // Other characters must match literally, except that
01835 // case is ignored.
01836 //
01837 static bool
01838 hostnameMatch(const char* pat, const char* name)
01839 {
01840   for (;;) {
01841     if (*pat == '\0') {
01842       return (*name == '\0');
01843     } else if (*pat == '?') {
01844       if (*name == '\0' || *name == '.') return false;
01845       pat++;
01846       name++;
01847     } else if (*pat == '*') {
01848       if (*name == '\0' || *name == '.') {
01849         pat++;
01850       } else {
01851         name++;
01852       }
01853     } else {
01854       if (tolower(*pat) != tolower(*name)) return false;
01855       pat++;
01856       name++;
01857     }
01858   }
01859 }
01860 
01861 // @@@ Should be a config variable
01862 #define TRY_AGAIN_LIMIT 10
01863 
01864 static bool
01865 alphaMatch(Export* exp, Bit32 s_addr)
01866 {
01867   struct hostent hp, *h = &hp;
01868   unsigned int try_again_count = 0;
01869   int err, my_h_errno;
01870 #if defined(__digital__)
01871   struct hostent_data hd;
01872   memset((char *)(&hd), 0, sizeof(hd));
01873 #elif defined(__linux__)
01874   char hp_buf[1024];
01875   memset(hp_buf, 0, sizeof(hp_buf));
01876 #endif
01877 
01878   do
01879     {
01880 #if defined(__digital__)
01881       err = gethostbyaddr_r((const char*)&s_addr, 
01882                             sizeof(s_addr), AF_INET,
01883                             &hp, &hd);
01884       my_h_errno = h_errno;
01885 #elif defined(__linux__)
01886       err = gethostbyaddr_r((const char*)&s_addr, 
01887                             sizeof(s_addr), AF_INET,
01888                             &hp,
01889                             hp_buf, sizeof(hp_buf),
01890                             &h, &my_h_errno);
01891 #else
01892 #error Do not know how to do gethostbyaddr_r on this platform!
01893 #endif
01894     }
01895   // Repeast on h_errno == TRY_AGAIN, but only so many times.  (Some
01896   // broken name resolution configurations can cause infinite looping
01897   // with TRY_AGAIN).
01898   while((err != 0) &&
01899         (my_h_errno == TRY_AGAIN) &&
01900         (try_again_count++ < TRY_AGAIN_LIMIT));
01901 
01902   if (h == NULL) return false;
01903   if (hostnameMatch(exp->pattern, h->h_name)) return true;
01904   char** name = h->h_aliases;
01905   while (*name) {
01906     if (hostnameMatch(exp->pattern, *name)) return true;
01907     name++;
01908   }
01909   return false;
01910 }
01911 
01912 bool Export::match(const ExportKey &key)
01913 {
01914   // Check for flavor match
01915   if (this->flavor != AccessControl::IdentityRep::unspecified &&
01916       this->flavor != key.flavor)
01917     return false;
01918 
01919   // Check for pattern match
01920   int nm = numericMatch(this, key.addr);
01921   if (nm == 0 || (nm == -1) && !alphaMatch(this, key.addr))
01922     return false;
01923 
01924   // Check for realm match
01925   if (this->realm) {
01926     if (key.arg == NULL || !hostnameMatch(this->realm, key.arg))
01927     return false;
01928   }
01929 
01930   // All succeeded
01931   return true;
01932 }
01933 
01934 bool 
01935 AccessControl::admit(Identity who) throw ()
01936 {
01937   const char* realm = NULL;
01938   if (who->flavor == AccessControl::IdentityRep::global ||
01939       who->flavor == AccessControl::IdentityRep::gssapi) {
01940     realm = strrchr(who->user(0), '@');
01941     if (realm) realm++;
01942   }
01943   ExportKey k((unsigned int)who->origin.sin_addr.s_addr, who->flavor, realm);
01944   Export* exp;
01945 
01946   //cerr << "key " << hex << k.addr << " " << dec << k.flavor
01947   //     << " " << k.arg << endl;
01948 
01949   export_lock.acquireRead();
01950   if (!exportCache->Get(k, exp)) {
01951     //cerr << "miss ";
01952 
01953     // Save the current export epoch
01954     unsigned int curEpoch = exportEpoch;
01955 
01956     // Search for a match in the export table
01957     exp = Export::first;
01958     while((exp != NULL) && !exp->match(k))
01959       exp = exp->next;
01960 
01961     // Release our read lock and acquire a write lock to update
01962     // exportCache.
01963     export_lock.releaseRead();
01964     export_lock.acquireWrite();
01965 
01966     if(curEpoch != exportEpoch)
01967       {
01968         // Ooops, the export table changed between when we released
01969         // the read lock and acquired the write lock.  Search again
01970         // for a match before updating exportCache.
01971         exp = Export::first;
01972         while((exp != NULL) && !exp->match(k))
01973           exp = exp->next;
01974       }
01975 
01976     if (realm) k.arg = strdup_with_new(realm);
01977 
01978     exportCache->Put(k, exp);
01979     export_lock.releaseWrite();
01980   } else {
01981     //cerr << "hit ";
01982     export_lock.releaseRead();
01983   }
01984 
01985   //if (exp == NULL) {
01986   //  cerr << "(no entry)" << endl;
01987   //} else {
01988   //  cerr << exp->pattern << " " << exp->level << " " << exp->flavor
01989   //       << " " << exp->realm << endl;
01990   //}
01991 
01992 
01993   if (exp == NULL) return false;
01994 
01995   switch (exp->level) {
01996   case Export::deny:
01997     return false;
01998   case Export::readOnly:
01999     who->readOnly = true;
02000     break;
02001   case Export::allow:
02002     break;
02003   }
02004   return true;
02005 }
02006 
02007 AccessControl::ModeBits
02008 AccessControl::parseModeBits(const char* char_mode) throw ()
02009 {
02010   int mb;
02011   sscanf(char_mode, "%o", &mb);
02012   return (AccessControl::ModeBits) mb & 0777;
02013 }
02014 
02015 const char*
02016 AccessControl::formatModeBits(AccessControl::ModeBits mode) throw ()
02017 {
02018   char* ret = NEW_PTRFREE_ARRAY(char, 4);
02019   sprintf(ret, "%03o", mode & 0777);
02020   return ret;
02021 }
02022 
02023 // Disused recovery callback, needed so that old logs can be read
02024 static void
02025 AumapCallback(RecoveryReader* rr, char& c)
02026      throw(VestaLog::Error, VestaLog::Eof)
02027 {
02028     char* name;
02029     long lvalue;
02030     rr->getNewQuotedString(c, name);
02031     rr->getLong(c, lvalue);
02032     delete[] name;
02033 }
02034 
02035 static void
02036 RumapCallback(RecoveryReader* rr, char& c)
02037      throw(VestaLog::Error, VestaLog::Eof)
02038 {
02039     char* name;
02040     rr->getNewQuotedString(c, name);
02041     delete[] name;
02042 }
02043 
02044 static void
02045 AgmapCallback(RecoveryReader* rr, char& c)
02046      throw(VestaLog::Error, VestaLog::Eof)
02047 {
02048     char* name;
02049     long lvalue;
02050     rr->getNewQuotedString(c, name);
02051     rr->getLong(c, lvalue);
02052     delete[] name;
02053 }
02054 
02055 static void
02056 RgmapCallback(RecoveryReader* rr, char& c)
02057      throw(VestaLog::Error, VestaLog::Eof)
02058 {
02059     char* name;
02060     rr->getNewQuotedString(c, name);
02061     delete[] name;
02062 }
02063 
02064 // Module initialization
02065 void
02066 AccessControl::serverInit()
02067      throw(VestaConfig::failure /*, gssapi::failure*/)
02068 {
02069   static bool initialized = false;
02070   if (initialized) return;
02071   initialized = true;
02072 
02073   // If realm is unitialized, initialize it.
02074   if(realm == 0)
02075     {
02076       realm =
02077         strdup(VestaConfig::get_Text("Repository", "realm").cchars());
02078       realmlen = strlen(realm);
02079     }
02080 
02081   // If defaultFlavor is unitialized, initialize it.
02082   if(defaultFlavor == AccessControl::IdentityRep::unspecified)
02083     {
02084       Text t = VestaConfig::get_Text("Repository", "default_flavor");
02085       if (strcasecmp(t.cchars(), "unix") == 0) {
02086         defaultFlavor = IdentityRep::unix_flavor;
02087       } else if (strcasecmp(t.cchars(), "global") == 0) {
02088         defaultFlavor = IdentityRep::global;
02089       } else if (strcasecmp(t.cchars(), "gssapi") == 0) {
02090         defaultFlavor = IdentityRep::gssapi;
02091       } else {
02092         throw(VestaConfig::failure("bad value for "
02093                                    "[Repository]default_flavor"));
02094       }
02095     }
02096 
02097   AccessControl::commonInit();
02098 
02099   vestaGroupFile = strdup(VestaConfig::get_Text("Repository", "group_file")
02100                           .cchars());
02101   vestaAliasFile = strdup(VestaConfig::get_Text("Repository", "alias_file")
02102                           .cchars());
02103   vestaExportFile = strdup(VestaConfig::get_Text("Repository", "export_file")
02104                            .cchars());
02105   if(VestaConfig::is_set("Repository", "min_auto_access_refresh_delay"))
02106     {
02107       min_auto_refresh_delay = VestaConfig::get_int("Repository",
02108                                                     "min_auto_access_refresh_delay");
02109     }
02110   if(VestaConfig::is_set("Repository", "honor_client_gids"))
02111     {
02112       honor_client_gids = VestaConfig::get_bool("Repository",
02113                                                 "honor_client_gids");
02114     }
02115   newTables();
02116   RegisterRecoveryCallback("aumap", AumapCallback);
02117   RegisterRecoveryCallback("rumap", RumapCallback);
02118   RegisterRecoveryCallback("agmap", AgmapCallback);
02119   RegisterRecoveryCallback("rgmap", RgmapCallback);
02120 }    
02121 
02122 void AccessControl::refreshAccessTables() throw(AccessControl::ParseError)
02123 {
02124   // Try to load new access control tables, but don't replace the
02125   // existing ones if there are parsing errors.
02126   newTables(false);
02127 }

Generated on Mon May 8 00:48:43 2006 for Vesta by  doxygen 1.4.2