nis-util
1.0.D108
|
00001 // 00002 // nis-util - NIS Administration Utilities 00003 // Copyright (C) 2002, 2008, 2009, 2011, 2012 Peter Miller 00004 // 00005 // This program is free software; you can redistribute it and/or modify 00006 // it under the terms of the GNU General Public License as published by 00007 // the Free Software Foundation; either version 2 of the License, or (at 00008 // your option) any later version. 00009 // 00010 // This program 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 // General Public License for more details. 00014 // 00015 // You should have received a copy of the GNU General Public License 00016 // along with this program. If not, see <http://www.gnu.org/licenses/>. 00017 // 00018 00019 #include <lib/ac/assert.h> 00020 #include <algorithm> 00021 #include <lib/space/netgroup/slurp.h> 00022 #include <lib/colon/passwd/slurp.h> 00023 00024 00025 space_netgroup_slurp::~space_netgroup_slurp() 00026 { 00027 } 00028 00029 00030 space_netgroup_slurp::space_netgroup_slurp( 00031 const rcstring &a_filename, 00032 const space_hosts_slurp::pointer &a_hosts, 00033 const colon_passwd_slurp::pointer &a_passwd 00034 ) : 00035 space_netgroup(a_filename), 00036 hosts(a_hosts), 00037 passwd(a_passwd) 00038 { 00039 } 00040 00041 00042 space_netgroup_slurp::pointer 00043 space_netgroup_slurp::create(const rcstring &a_filename, 00044 const space_hosts_slurp::pointer &a_hosts, 00045 const colon_passwd_slurp::pointer &a_passwd) 00046 { 00047 return pointer(new space_netgroup_slurp(a_filename, a_hosts, a_passwd)); 00048 } 00049 00050 00051 space_netgroup_slurp::pointer 00052 space_netgroup_slurp::create(const rcstring &a_filename) 00053 { 00054 space_hosts_slurp::pointer a_hosts; 00055 colon_passwd_slurp::pointer a_passwd; 00056 return create(a_filename, a_hosts, a_passwd); 00057 } 00058 00059 00060 void 00061 space_netgroup_slurp::process(const record::pointer &rp) 00062 { 00063 // 00064 // Stash the record. 00065 // 00066 by_name_t::iterator it1 = by_name.find(rp->get_name()); 00067 if (it1 != by_name.end()) 00068 { 00069 error 00070 ( 00071 rp->get_source_location(), 00072 "duplicate %s netgroup...", 00073 rp->get_name().quote_c().c_str() 00074 ); 00075 error 00076 ( 00077 it1->second->get_source_location(), 00078 "...here is the first definition of the %s netgroup", 00079 it1->second->get_name().quote_c().c_str() 00080 ); 00081 } 00082 else 00083 { 00084 by_name.insert(by_name_t::value_type(rp->get_name(), rp)); 00085 } 00086 00087 // 00088 // Check the members. 00089 // 00090 const record::members_t &members = rp->get_members(); 00091 for 00092 ( 00093 record::members_t::const_iterator it = members.begin(); 00094 it != members.end(); 00095 ++it 00096 ) 00097 { 00098 // 00099 // Can't check references until all the groups have been 00100 // read in. 00101 // 00102 if (!it->reference_to_netgroup.empty()) 00103 continue; 00104 00105 // 00106 // Check the user name. 00107 // 00108 if 00109 ( 00110 passwd 00111 && 00112 !it->user_any() 00113 && 00114 !it->user_ignore() 00115 && 00116 !passwd->query_by_name(it->user) 00117 ) 00118 { 00119 error 00120 ( 00121 rp->get_source_location(), 00122 "user %s unknown", 00123 it->user.quote_c().c_str() 00124 ); 00125 } 00126 00127 // 00128 // Check the host name. 00129 // 00130 if 00131 ( 00132 hosts 00133 && 00134 !it->host_any() 00135 && 00136 !it->host_ignore() 00137 && 00138 !hosts->query_by_name(it->host) 00139 ) 00140 { 00141 error 00142 ( 00143 rp->get_source_location(), 00144 "host %s unknown", 00145 it->host.quote_c().c_str() 00146 ); 00147 } 00148 } 00149 00150 // 00151 // Check the line length. 00152 // 00153 if (rp->representation().size() >= 512) 00154 { 00155 error 00156 ( 00157 rp->get_source_location(), 00158 "line too long (NIS limits records to 511 bytes, " 00159 "and NIS+ limits record to 1024 bytes)" 00160 ); 00161 } 00162 } 00163 00164 00165 static bool 00166 append_unique(space_netgroup::record::members_t &elp, 00167 const space_netgroup::entry_t &e) 00168 { 00169 for 00170 ( 00171 space_netgroup::record::members_t::const_iterator it = elp.begin(); 00172 it != elp.end(); 00173 ++it 00174 ) 00175 { 00176 if (*it == e) 00177 { 00178 return false; 00179 } 00180 } 00181 elp.push_back(e); 00182 return true; 00183 } 00184 00185 00186 static bool 00187 append_unique(space_netgroup::record::members_t &elp, 00188 const space_netgroup::record::members_t &elp2) 00189 { 00190 bool result = false; 00191 for 00192 ( 00193 space_netgroup::record::members_t::const_iterator it2 = elp2.begin(); 00194 it2 != elp2.end(); 00195 ++it2 00196 ) 00197 { 00198 if (append_unique(elp, *it2)) 00199 result = true; 00200 } 00201 return result; 00202 } 00203 00204 00205 void 00206 space_netgroup_slurp::read_and_process(void) 00207 { 00208 // 00209 // Read the file contents. 00210 // 00211 for (;;) 00212 { 00213 record::pointer rp = get(); 00214 if (!rp) 00215 { 00216 break; 00217 } 00218 process(rp); 00219 } 00220 00221 // 00222 // Check that cross references are OK. 00223 // (Sorting the keys gives more consistent output, which reduces 00224 // the false negatives when testing. Also, std::map does it for free.) 00225 // 00226 name_list_t keys = keys_by_name(); 00227 // std::sort(keys.begin(), keys.end()); 00228 for (name_list_t::iterator it = keys.begin(); it != keys.end(); ++it) 00229 { 00230 rcstring key = *it; 00231 record::pointer rp = query_by_name(key); 00232 assert(rp); 00233 if (!rp) 00234 continue; 00235 const record::members_t &members = rp->get_members(); 00236 for 00237 ( 00238 record::members_t::const_iterator mit = members.begin(); 00239 mit != members.end(); 00240 ++mit 00241 ) 00242 { 00243 // 00244 // Ignore group members which are not references to other groups. 00245 // 00246 rcstring name2 = mit->reference_to_netgroup; 00247 if (name2.empty()) 00248 continue; 00249 00250 // 00251 // Make sure referenced groups exist. 00252 // 00253 record::pointer rp2 = query_by_name(name2); 00254 if (!rp2) 00255 { 00256 error 00257 ( 00258 rp->get_source_location(), 00259 "netgroup %s references netgroup %s that does not exist", 00260 rp->get_name().quote_c().c_str(), 00261 name2.quote_c().c_str() 00262 ); 00263 } 00264 } 00265 } 00266 00267 // 00268 // Compute the closure of by_name. 00269 // (Sorting the keys gives more consistent output, which reduces the false 00270 // negatives when testing. By using std::map, we get that sort for free.) 00271 // 00272 // FIXME: Unfortunately, this has O(n**3) behaviour. Sites with 00273 // many large netgroups may need for this to use the algorithm in 00274 // Knuth to speed things up. 00275 // 00276 typedef std::map<rcstring, record::members_t> closure_t; 00277 closure_t closure; 00278 for 00279 ( 00280 by_name_t::const_iterator bnit = by_name.begin(); 00281 bnit != by_name.end(); 00282 ++bnit 00283 ) 00284 { 00285 closure.insert 00286 ( 00287 closure_t::value_type(bnit->first, bnit->second->get_members()) 00288 ); 00289 } 00290 for (;;) 00291 { 00292 // 00293 // We keep iterating until there are no more changes. 00294 // 00295 bool changed = false; 00296 00297 // 00298 // For each netgroup, make sure we have everything covered. 00299 // 00300 for 00301 ( 00302 name_list_t::const_iterator kit1 = keys.begin(); 00303 kit1 != keys.end(); 00304 ++kit1 00305 ) 00306 { 00307 // 00308 // Look at each netgroup entry, and if it references another 00309 // netgroup, add all its entries to this one. 00310 // 00311 rcstring key1 = *kit1; 00312 closure_t::iterator elp1 = closure.find(key1); 00313 if (elp1 == closure.end()) 00314 continue; 00315 const record::members_t &members = elp1->second; 00316 record::members_t new_members = members; 00317 for 00318 ( 00319 record::members_t::const_iterator eit = members.begin(); 00320 eit != members.end(); 00321 ++eit 00322 ) 00323 { 00324 rcstring key2 = eit->reference_to_netgroup; 00325 if (key2.empty()) 00326 continue; 00327 00328 // short circuit reference to self 00329 if (key2 == key1) 00330 continue; 00331 closure_t::const_iterator elp2 = closure.find(key2); 00332 00333 // 00334 // Ignore netgroups that don't exist. 00335 // 00336 if (elp2 == closure.end()) 00337 continue; 00338 00339 // 00340 // If there is stuff in *elp2 that isn't in *elp1, 00341 // then something changed, and we need to iterate. 00342 // 00343 if (append_unique(new_members, elp2->second)) 00344 { 00345 changed = true; 00346 } 00347 } 00348 elp1->second = new_members; 00349 } 00350 00351 // 00352 // If this iteration found nothing different, we are done. 00353 // 00354 if (!changed) 00355 break; 00356 } 00357 00358 // 00359 // Iterate over the closure, checking for self references, and also 00360 // filling in the by_user and by_host instance variables. 00361 // 00362 for 00363 ( 00364 closure_t::const_iterator cit = closure.begin(); 00365 cit != closure.end(); 00366 ++cit 00367 ) 00368 { 00369 for 00370 ( 00371 record::members_t::const_iterator mit = cit->second.begin(); 00372 mit != cit->second.end(); 00373 ++mit 00374 ) 00375 { 00376 // 00377 // Check for self recursion. We do this once, rather than 00378 // risk the same error being issued multiple times by the 00379 // loop that computes the closure of by_name. 00380 // 00381 if (cit->first == mit->reference_to_netgroup) 00382 { 00383 record::pointer rp = query_by_name(cit->first); 00384 assert(rp); 00385 if (rp) 00386 { 00387 error 00388 ( 00389 rp->get_source_location(), 00390 "netgroup %s contains a recursive reference to itself", 00391 rp->get_name().quote_c().c_str() 00392 ); 00393 break; 00394 } 00395 } 00396 00397 // 00398 // Ignore cross references, we have resolved them all, now. 00399 // 00400 if (!mit->reference_to_netgroup.empty()) 00401 continue; 00402 00403 // 00404 // Stash the by_host information. 00405 // 00406 if (mit->host_indexing()) 00407 { 00408 rcstring hostname(mit->host_any() ? "*" : mit->host); 00409 by_host_t::iterator nlp = by_host.find(hostname); 00410 if (nlp == by_host.end()) 00411 { 00412 name_list_t names; 00413 names.push_back(cit->first); 00414 by_host.insert(by_host_t::value_type(hostname, names)); 00415 } 00416 else 00417 nlp->second.push_back(cit->first); 00418 } 00419 00420 // 00421 // Stash the by_user information. 00422 // 00423 if (mit->user_indexing()) 00424 { 00425 rcstring username(mit->user_any() ? "*" : mit->user); 00426 by_user_t::iterator nlp = by_user.find(username); 00427 if (nlp == by_user.end()) 00428 { 00429 name_list_t names; 00430 names.push_back(cit->first); 00431 by_user.insert(by_user_t::value_type(username, names)); 00432 } 00433 else 00434 nlp->second.push_back(cit->first); 00435 } 00436 } 00437 } 00438 00439 // 00440 // Close the file. 00441 // 00442 close(); 00443 } 00444 00445 00446 space_netgroup_slurp::name_list_t 00447 space_netgroup_slurp::keys_by_name(void) 00448 const 00449 { 00450 name_list_t result; 00451 for 00452 ( 00453 by_name_t::const_iterator it = by_name.begin(); 00454 it != by_name.end(); 00455 ++it 00456 ) 00457 result.push_back(it->first); 00458 return result; 00459 } 00460 00461 00462 space_netgroup::record::pointer 00463 space_netgroup_slurp::query_by_name(const rcstring &key) 00464 const 00465 { 00466 by_name_t::const_iterator it = by_name.find(key); 00467 if (it == by_name.end()) 00468 return record::pointer(); 00469 return it->second; 00470 } 00471 00472 00473 space_netgroup_slurp::name_list_t 00474 space_netgroup_slurp::keys_by_host(void) 00475 const 00476 { 00477 name_list_t result; 00478 for 00479 ( 00480 by_host_t::const_iterator it = by_host.begin(); 00481 it != by_host.end(); 00482 ++it 00483 ) 00484 result.push_back(it->first); 00485 return result; 00486 } 00487 00488 00489 space_netgroup_slurp::name_list_t 00490 space_netgroup_slurp::query_by_host(const rcstring &key) 00491 const 00492 { 00493 by_host_t::const_iterator it = by_host.find(key); 00494 if (it == by_host.end()) 00495 return name_list_t(); 00496 return it->second; 00497 } 00498 00499 00500 space_netgroup_slurp::name_list_t 00501 space_netgroup_slurp::keys_by_user(void) 00502 const 00503 { 00504 name_list_t result; 00505 for 00506 ( 00507 by_user_t::const_iterator it = by_user.begin(); 00508 it != by_user.end(); 00509 ++it 00510 ) 00511 result.push_back(it->first); 00512 return result; 00513 } 00514 00515 00516 space_netgroup_slurp::name_list_t 00517 space_netgroup_slurp::query_by_user(const rcstring &key) 00518 const 00519 { 00520 by_user_t::const_iterator it = by_user.find(key); 00521 if (it == by_user.end()) 00522 return name_list_t(); 00523 return it->second; 00524 } 00525 00526 00527 // vim: set ts=8 sw=4 et :