nis-util  1.0.D108
lib/colon/group.cc
Go to the documentation of this file.
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 :