nis-util  1.0.D108
lib/colon/passwd/slurp/check.cc
Go to the documentation of this file.
00001 //
00002 // nis-util - NIS Administration Utilities
00003 // Copyright (C) 2001, 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/errno.h>
00020 #include <lib/ac/string.h>
00021 #include <lib/ac/sys/types.h>
00022 #include <lib/ac/sys/stat.h>
00023 #include <lib/ac/unistd.h>
00024 #include <libexplain/lstat.h>
00025 
00026 #include <lib/colon/group/slurp.h>
00027 #include <lib/colon/passwd/slurp/check.h>
00028 #include <lib/path_join.h>
00029 
00030 
00031 colon_passwd_slurp_check::~colon_passwd_slurp_check()
00032 {
00033 }
00034 
00035 
00036 colon_passwd_slurp_check::colon_passwd_slurp_check(
00037     const rcstring &a_filename,
00038     const colon_group_slurp::pointer &a_group
00039 ) :
00040     colon_passwd_slurp(a_filename),
00041     group(a_group)
00042 {
00043 }
00044 
00045 
00046 colon_passwd_slurp_check::pointer
00047 colon_passwd_slurp_check::create(const rcstring &a_filename,
00048     const colon_group_slurp::pointer &a_group)
00049 {
00050     return pointer(new colon_passwd_slurp_check(a_filename, a_group));
00051 }
00052 
00053 
00054 bool
00055 colon_passwd_slurp_check::process(const record::pointer &rp)
00056 {
00057     // Do all the base class's processing first.
00058     if (!colon_passwd_slurp::process(rp))
00059         return false;
00060 
00061     //
00062     // Check the GID field
00063     //
00064     {
00065         const colon_group::record::pointer gp =
00066             group->query_by_gid(rp->get_gid());
00067         if (!gp)
00068         {
00069             error
00070             (
00071                 rp->get_source_location(),
00072                 "user %s: group id %s unknown",
00073                 rp->get_name().quote_c().c_str(),
00074                 rp->get_gid().quote_c().c_str()
00075             );
00076             return false;
00077         }
00078 
00079         if (!gp->get_use_instead().empty())
00080         {
00081             const colon_group::record::pointer gp2 =
00082                 group->query_by_name(gp->get_use_instead());
00083             if (gp2)
00084             {
00085                 error
00086                 (
00087                     rp->get_source_location(),
00088                     "user %s: default group %s (gid %s) is not suitable, "
00089                         "use group %s (gid %s) instead",
00090                     rp->get_name().quote_c().c_str(),
00091                     gp->get_name().quote_c().c_str(),
00092                     rp->get_gid().c_str(),
00093                     gp2->get_name().quote_c().c_str(),
00094                     gp2->get_gid().c_str()
00095                 );
00096             }
00097             else
00098             {
00099                 error
00100                 (
00101                     rp->get_source_location(),
00102                     "user %s: default group %s (gid %s) is not suitable, "
00103                         "use group %s instead",
00104                     rp->get_name().quote_c().c_str(),
00105                     gp->get_name().quote_c().c_str(),
00106                     rp->get_gid().c_str(),
00107                     gp->get_use_instead().quote_c().c_str()
00108                 );
00109             }
00110             return false;
00111         }
00112         else if (gp->is_members_only())
00113         {
00114             //
00115             // Groups marked "MEMBERS-ONLY" may only be used by the user
00116             // of the same name, or group members.
00117             //
00118             if
00119             (
00120                 gp->get_name() != rp->get_name()
00121             &&
00122                 !gp->is_member(rp->get_name())
00123             )
00124             {
00125                 error
00126                 (
00127                     rp->get_source_location(),
00128                     "user %s: default group %s (gid %s) is reserved, and may "
00129                         "not be used as this user's default group",
00130                     rp->get_name().quote_c().c_str(),
00131                     gp->get_name().quote_c().c_str(),
00132                     rp->get_gid().c_str()
00133                 );
00134                 return false;
00135             }
00136         }
00137     }
00138 
00139     //
00140     // Check full name field.
00141     //
00142 
00143     //
00144     // Check home directory
00145     //
00146     // FIXME: make sure the home directory is in an automount map.
00147     //
00148     if (!check_home(rp))
00149         return false;
00150 
00151     //
00152     // Check shell.
00153     //
00154     // FIXME: check against the /etc/shell file (or some similar
00155     // command line argument).
00156 
00157     //
00158     // Looks good so far.
00159     //
00160     return true;
00161 }
00162 
00163 
00164 bool
00165 colon_passwd_slurp_check::check_home(const record::pointer &rp)
00166 {
00167     // Note: this makes the bold assumption that the server building
00168     // the NIS maps is itself using those same NIS maps.
00169     // Otherwise this check is always going to fail.
00170 
00171     struct stat st;
00172     if (lstat(rp->get_home().c_str(), &st) < 0)
00173     {
00174         if (errno != ENOENT)
00175         {
00176             error
00177             (
00178                 rp->get_source_location(),
00179                 "%s",
00180                 explain_lstat(rp->get_home().c_str(), &st)
00181             );
00182         }
00183 #if 1
00184         return false;
00185 #else
00186         // make a fake ok status
00187         memset(&st, 0, sizeof(st));
00188         st.st_mode = S_IFDIR | 02755;
00189         st.st_uid = rp->get_uid_binary();
00190         st.st_gid = rp->get_gid_binary();
00191 #endif
00192     }
00193 
00194     if (S_ISLNK(st.st_mode))
00195     {
00196         // Note: The sun automounter uses symbolic links when it automounts
00197         // a file system, fromthe use point to the actual mount point.
00198         // This is especially true when the auto mount is actually
00199         // local to the NIS client using the passwd map.
00200         // Thus, symbolic links usually mean a mistake.
00201 
00202         rcstring link_destination;
00203         {
00204             char buffer[2000];
00205             int n = readlink(rp->get_home().c_str(), buffer, sizeof(buffer));
00206             if (n <= 0)
00207             {
00208                 error
00209                 (
00210                     rp->get_source_location(),
00211                     "user %s home directory %s may not be a symbolic link",
00212                     rp->get_name().quote_c().c_str(),
00213                     rp->get_home().quote_c().c_str()
00214                 );
00215                 return false;
00216             }
00217             link_destination = rcstring(buffer, n);
00218         }
00219 
00220         rcstring suggest;
00221         if (link_destination.front() == '/')
00222             suggest = link_destination;
00223         else
00224         {
00225             rcstring dir = rp->get_home().dirname();
00226             suggest = path_join(dir, link_destination);
00227         }
00228         error
00229         (
00230             rp->get_source_location(),
00231             "user %s home directory %s may not be a symbolic link, "
00232             "suggest %s instead",
00233             rp->get_name().quote_c().c_str(),
00234             rp->get_home().quote_c().c_str(),
00235             suggest.quote_c().c_str()
00236         );
00237         return false;
00238     }
00239     if (!S_ISDIR(st.st_mode))
00240     {
00241         error
00242         (
00243             rp->get_source_location(),
00244             "user %s home directory %s must be a directory",
00245             rp->get_name().quote_c().c_str(),
00246             rp->get_home().c_str()
00247         );
00248         return false;
00249     }
00250 
00251     if ((st.st_mode & 05733) != 00711)
00252     {
00253         error
00254         (
00255             rp->get_source_location(),
00256             "user %s home directory %s is mode %#o, it should be 02755, "
00257                 "suggest...",
00258             rp->get_name().quote_c().c_str(),
00259             rp->get_home().c_str(),
00260             (st.st_mode & 07777)
00261         );
00262         error
00263         (
00264             rp->get_source_location(),
00265             "sudo chmod %#o %s",
00266             ((st.st_mode & 02755) | 00711),
00267             rp->get_home().quote_shell().c_str()
00268         );
00269     }
00270 
00271     {
00272         long uid = rp->get_uid_binary();
00273         if (uid >= 0)
00274         {
00275             if ((long)st.st_uid != uid)
00276             {
00277                 rcstring old_uid;
00278                 record::pointer oup = query_by_uid(st.st_uid);
00279                 if (oup)
00280                     old_uid = oup->get_uid();
00281                 else
00282                     old_uid = rcstring::format("%ld", (long)st.st_uid);
00283 
00284                 rcstring new_owner = rp->get_name();
00285                 long gid = rp->get_gid_binary();
00286                 if (gid >= 0)
00287                 {
00288                     if (gid != (long)st.st_gid)
00289                     {
00290                         colon_group::record::pointer grp =
00291                             group->query_by_gid(rp->get_gid());
00292                         if (grp)
00293                             new_owner += "." + grp->get_name();
00294                         else
00295                             new_owner += rcstring::format(".%ld", gid);
00296                     }
00297                 }
00298 
00299                 warning
00300                 (
00301                     rp->get_source_location(),
00302                     "user %s home directory %s is owned by %s, it "
00303                         "should be %s, suggest...",
00304                     rp->get_name().quote_c().c_str(),
00305                     rp->get_home().quote_c().c_str(),
00306                     old_uid.quote_c().c_str(),
00307                     rp->get_name().quote_c().c_str()
00308                 );
00309                 warning
00310                 (
00311                     rp->get_source_location(),
00312                     "sudo chown %s %s",
00313                     new_owner.quote_shell().c_str(),
00314                     rp->get_home().quote_shell().c_str()
00315                 );
00316                 return false;
00317             }
00318         }
00319     }
00320 
00321     {
00322         long gid = rp->get_gid_binary();
00323         if (gid >= 0)
00324         {
00325             if ((long)st.st_gid != gid)
00326             {
00327                 // determine the name of the old group
00328                 rcstring old_group;
00329                 {
00330                     colon_group::record::pointer grp =
00331                         group->query_by_gid(st.st_gid);
00332                     if (grp)
00333                         old_group = grp->get_name();
00334                     else
00335                         old_group = rcstring::format("%ld", (long)st.st_gid);
00336                 }
00337 
00338                 // determine the name of the new group
00339                 rcstring new_group;
00340                 {
00341                     colon_group::record::pointer grp =
00342                         group->query_by_gid(rp->get_gid());
00343                     if (grp)
00344                         new_group = grp->get_name();
00345                     else
00346                         new_group = rp->get_gid();
00347                 }
00348 
00349                 warning
00350                 (
00351                     rp->get_source_location(),
00352                     "user %s home directory \"%s\" is group %s, it "
00353                         "should be %s, suggest...",
00354                     rp->get_name().quote_c().c_str(),
00355                     rp->get_home().quote_c().c_str(),
00356                     old_group.quote_c().c_str(),
00357                     new_group.quote_c().c_str()
00358                 );
00359                 warning
00360                 (
00361                     rp->get_source_location(),
00362                     "sudo chgrp %s %s",
00363                     new_group.quote_shell().c_str(),
00364                     rp->get_home().quote_shell().c_str()
00365                 );
00366             }
00367         }
00368     }
00369 
00370     return true;
00371 }
00372 
00373 
00374 // vim: set ts=8 sw=4 et :