nis-util
1.0.D108
|
00001 // 00002 // nis-util - NIS Administration Utilities 00003 // Copyright (C) 2001, 2003, 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/string.h> 00020 #include <algorithm> 00021 00022 #include <lib/colon/group.h> 00023 #include <lib/colon/passwd.h> // for static is_ok_user_name 00024 #include <lib/configuration.h> 00025 #include <lib/rcstring/accumulator.h> 00026 00027 00028 colon_group::~colon_group() 00029 { 00030 } 00031 00032 00033 colon_group::colon_group(const rcstring &a_filename) : 00034 colon(a_filename), 00035 last_gid(-1) 00036 { 00037 // This host's sysconf(LOGIN_NAME_MAX)-1 may be too generous for 00038 // all NIS clients. Be conservative. 00039 cfg.set_builtin_long(SECTION_GROUP, ITEM_NAME_LENGTH_MAXIMUM, 8); 00040 // there is no sysconf for this one... 00041 cfg.set_builtin_long(SECTION_GROUP, "group-id-maximum", 65534); 00042 00043 discard_comments(); 00044 discard_blank_lines(); 00045 } 00046 00047 00048 colon_group::pointer 00049 colon_group::create(const rcstring &a_filename) 00050 { 00051 return pointer(new colon_group(a_filename)); 00052 } 00053 00054 00055 colon_group::record::pointer 00056 colon_group::get(void) 00057 { 00058 source_location line_locn; 00059 line_t line; 00060 while (read_one_line(line_locn, line)) 00061 { 00062 // 00063 // Check the number of fields 00064 // 00065 if (line.size() < 4) 00066 { 00067 error 00068 ( 00069 line_locn, 00070 "too few fields (expected 4, given %d)", 00071 (int)line.size() 00072 ); 00073 continue; 00074 } 00075 if (line.size() > 4) 00076 { 00077 error 00078 ( 00079 line_locn, 00080 "too many fields (expected 4, given %ld)", 00081 (long)line.size() 00082 ); 00083 } 00084 00085 // 00086 // Transfer the fields to the structure. 00087 // 00088 record::members_t members = line[3].break_up(",", 1); 00089 record::pointer rp = 00090 record::create(line_locn, line[0], line[1], line[2], members); 00091 00092 // 00093 // If it looks halfway sensable, use it. 00094 // 00095 if (validate(rp)) 00096 return rp; 00097 } 00098 last_gid = -1; 00099 return record::pointer(); 00100 } 00101 00102 00103 colon_group::record::~record() 00104 { 00105 } 00106 00107 00108 colon_group::record::record( 00109 const source_location &a_locn, 00110 const rcstring &a_name, 00111 const rcstring &a_password, 00112 const rcstring &a_gid, 00113 const members_t &a_members 00114 ) : 00115 locn(a_locn), 00116 name(a_name), 00117 password(a_password), 00118 gid(a_gid), 00119 members(a_members), 00120 members_only(false) 00121 { 00122 // 00123 // If the first member is "MEMBERS-ONLY", the group may only be the 00124 // default group of the user of the same name or group members. 00125 // 00126 if (members.size() >= 1 && members[0] == "MEMBERS-ONLY") 00127 { 00128 members.erase(members.begin()); 00129 members_only = true; 00130 } 00131 00132 // 00133 // If the first member starts with "USE-", it means that this 00134 // group should not be used as a default group, the preferred 00135 // default group name follows the hyphen. 00136 // 00137 if 00138 ( 00139 members.size() >= 1 00140 && 00141 members[0].size() >= 5 00142 && 00143 0 == memcmp(members[0].c_str(), "USE-", 4) 00144 ) 00145 { 00146 use_instead = members[0].substr(4, 9999); 00147 members.erase(members.begin()); 00148 } 00149 } 00150 00151 00152 colon_group::record::pointer 00153 colon_group::record::create(const source_location &a_locn, 00154 const rcstring &a_name, const rcstring &a_password, const rcstring &a_gid, 00155 const members_t &a_members) 00156 { 00157 return pointer(new record(a_locn, a_name, a_password, a_gid, a_members)); 00158 } 00159 00160 00161 bool 00162 colon_group::validate(const record::pointer &rp) 00163 { 00164 // 00165 // Check the group name 00166 // 00167 if (rp->get_name().empty()) 00168 { 00169 error 00170 ( 00171 rp->get_source_location(), 00172 "may not have empty group name" 00173 ); 00174 return false; 00175 } 00176 unsigned group_name_maximum_length = 00177 cfg.get_long("group", ITEM_NAME_LENGTH_MAXIMUM); 00178 if (rp->get_name().size() > group_name_maximum_length) 00179 { 00180 rcstring suggest = rp->get_name(); 00181 suggest = suggest.substr(0, group_name_maximum_length); 00182 error 00183 ( 00184 rp->get_source_location(), 00185 "group name %s too long, by %ld, suggest %s instead", 00186 rp->get_name().quote_c().c_str(), 00187 (long)(rp->get_name().size() - group_name_maximum_length), 00188 suggest.quote_c().c_str() 00189 ); 00190 return false; 00191 } 00192 { 00193 rcstring suggest = colon_passwd::is_ok_user_name(rp->get_name()); 00194 if (suggest != rp->get_name()) 00195 { 00196 error 00197 ( 00198 rp->get_source_location(), 00199 "group name %s unacceptable, suggest %s instead", 00200 rp->get_name().quote_c().c_str(), 00201 suggest.quote_c().c_str() 00202 ); 00203 return false; 00204 } 00205 } 00206 00207 // 00208 // Check the password 00209 // 00210 if (rp->get_password() != "x" && rp->get_password() != "*") 00211 { 00212 error(rp->get_source_location(), "must have \"*\" for password"); 00213 } 00214 00215 // 00216 // Check the UID 00217 // 00218 { 00219 long gid = -1; 00220 if (!rp->get_gid().to_long(gid) || gid < 0) 00221 { 00222 rp->get_source_location(), 00223 error 00224 ( 00225 rp->get_source_location(), 00226 "group %s: group id %s is not a positive number", 00227 rp->get_name().quote_c().c_str(), 00228 rp->get_gid().quote_c().c_str() 00229 ); 00230 return false; 00231 } 00232 00233 if (gid < last_gid) 00234 { 00235 pedantic_error 00236 ( 00237 rp->get_source_location(), 00238 "group %s: group id %s is out of order", 00239 rp->get_name().quote_c().c_str(), 00240 rp->get_gid().quote_c().c_str() 00241 ); 00242 explain_sequence_errors(); 00243 } 00244 last_gid = gid; 00245 00246 #if 0 00247 if (gid >= 32768L) 00248 { 00249 error 00250 ( 00251 rp->get_source_location(), 00252 "group %s: group id %s is too large (0..32767)", 00253 rp->get_name().quote_c()->c_str(), 00254 rp->get_gid().quote_c()->c_str() 00255 ); 00256 return false; 00257 } 00258 #endif 00259 00260 rcstring s = rcstring::format("%ld", gid); 00261 if (s != rp->get_gid()) 00262 { 00263 error 00264 ( 00265 rp->get_source_location(), 00266 "group %s: group id %s not simplest decimal number, " 00267 "use %s instead", 00268 rp->get_name().quote_c().c_str(), 00269 rp->get_gid().quote_c().c_str(), 00270 s.quote_c().c_str() 00271 ); 00272 return false; 00273 } 00274 } 00275 00276 // 00277 // Make sure the group members are in alphabetical order. 00278 // This catches duplicates and some typographical errors. 00279 // 00280 const record::members_t &members = rp->get_members(); 00281 for 00282 ( 00283 record::members_t::const_iterator it = members.begin(); 00284 it != members.end(); 00285 ++it 00286 ) 00287 { 00288 if (it->empty()) 00289 { 00290 error 00291 ( 00292 rp->get_source_location(), 00293 "group %s: member list contains empty member", 00294 rp->get_name().quote_c().c_str() 00295 ); 00296 return false; 00297 } 00298 if (it != members.begin()) 00299 { 00300 if (it[-1] == it[0]) 00301 { 00302 error 00303 ( 00304 rp->get_source_location(), 00305 "group %s: duplicate %s group member", 00306 rp->get_name().quote_c().c_str(), 00307 it->c_str() 00308 ); 00309 return false; 00310 } 00311 if (it[-1] > it[0]) 00312 { 00313 error 00314 ( 00315 rp->get_source_location(), 00316 "group %s: member list not in alphabetical order", 00317 rp->get_name().quote_c().c_str() 00318 ); 00319 explain_sequence_errors(); 00320 return false; 00321 } 00322 } 00323 } 00324 00325 // 00326 // The overall length must be 511 characters. This is a 00327 // limitation of some implementations of the NIS transport 00328 // mechanism. 00329 // 00330 if (rp->representation().size() >= 512) 00331 { 00332 error 00333 ( 00334 rp->get_source_location(), 00335 "line too long (NIS limits records to 511 bytes)" 00336 ); 00337 } 00338 00339 // 00340 // Looks good so far. 00341 // 00342 return true; 00343 } 00344 00345 00346 bool 00347 colon_group::record::is_member(const rcstring &member_name) 00348 const 00349 { 00350 return (find(members.begin(), members.end(), member_name) != members.end()); 00351 } 00352 00353 00354 rcstring 00355 colon_group::record::representation(void) 00356 const 00357 { 00358 rcstring_accumulator acc; 00359 acc.push_back(name); 00360 acc.push_back(':'); 00361 acc.push_back(password); 00362 acc.push_back(':'); 00363 acc.push_back(gid); 00364 acc.push_back(':'); 00365 for 00366 ( 00367 std::vector<rcstring>::const_iterator it = members.begin(); 00368 it != members.end(); 00369 ++it 00370 ) 00371 { 00372 if (it != members.begin()) 00373 acc.push_back(','); 00374 acc.push_back(*it); 00375 } 00376 return acc.mkstr(); 00377 } 00378 00379 00380 void 00381 colon_group::record::append_member(const rcstring &user_name) 00382 { 00383 // append unique names 00384 if (std::find(members.begin(), members.end(), user_name) != members.end()) 00385 return; 00386 // try to preseve sort order 00387 for 00388 ( 00389 members_t::iterator it = members.begin(); 00390 it != members.end(); 00391 ++it 00392 ) 00393 { 00394 if (user_name < *it) 00395 { 00396 members.insert(it, user_name); 00397 return; 00398 } 00399 } 00400 // it belongs on the end 00401 members.push_back(user_name); 00402 } 00403 00404 00405 // vim: set ts=8 sw=4 et :