libexplain  1.4.D001
libexplain/buffer/errno/chown.c
Go to the documentation of this file.
00001 /*
00002  * libexplain - Explain errno values returned by libc functions
00003  * Copyright (C) 2008-2010, 2013 Peter Miller
00004  *
00005  * This program is free software; you can redistribute it and/or modify it
00006  * under the terms of the GNU Lesser General Public License as published by
00007  * the Free Software Foundation; either version 3 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  * Lesser General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU Lesser General Public License
00016  * along with this program. If not, see <http://www.gnu.org/licenses/>.
00017  */
00018 
00019 #include <libexplain/ac/errno.h>
00020 #include <libexplain/ac/sys/stat.h>
00021 #include <libexplain/ac/unistd.h>
00022 
00023 #include <libexplain/buffer/dac.h>
00024 #include <libexplain/buffer/does_not_have_inode_modify_permission.h>
00025 #include <libexplain/buffer/eacces.h>
00026 #include <libexplain/buffer/ebadf.h>
00027 #include <libexplain/buffer/efault.h>
00028 #include <libexplain/buffer/eio.h>
00029 #include <libexplain/buffer/eloop.h>
00030 #include <libexplain/buffer/enametoolong.h>
00031 #include <libexplain/buffer/enoent.h>
00032 #include <libexplain/buffer/enomem.h>
00033 #include <libexplain/buffer/enosys.h>
00034 #include <libexplain/buffer/enotdir.h>
00035 #include <libexplain/buffer/eperm.h>
00036 #include <libexplain/buffer/erofs.h>
00037 #include <libexplain/buffer/errno/chown.h>
00038 #include <libexplain/buffer/errno/generic.h>
00039 #include <libexplain/buffer/errno/path_resolution.h>
00040 #include <libexplain/buffer/file_type.h>
00041 #include <libexplain/buffer/gettext.h>
00042 #include <libexplain/buffer/gid.h>
00043 #include <libexplain/buffer/pointer.h>
00044 #include <libexplain/buffer/uid.h>
00045 #include <libexplain/capability.h>
00046 #include <libexplain/explanation.h>
00047 #include <libexplain/group_in_groups.h>
00048 #include <libexplain/option.h>
00049 
00050 
00051 static void
00052 explain_buffer_errno_chown_system_call(explain_string_buffer_t *sb,
00053     int errnum, const char *pathname, int owner, int group)
00054 {
00055     explain_string_buffer_puts(sb, "chown(pathname = ");
00056     if (errnum == EFAULT)
00057         explain_buffer_pointer(sb, pathname);
00058     else
00059         explain_string_buffer_puts_quoted(sb, pathname);
00060     explain_string_buffer_puts(sb, ", owner = ");
00061     explain_buffer_uid(sb, owner);
00062     explain_string_buffer_puts(sb, ", group = ");
00063     explain_buffer_gid(sb, group);
00064     explain_string_buffer_putc(sb, ')');
00065 }
00066 
00067 
00068 static int
00069 explain_buffer_eperm_chown_st(explain_string_buffer_t *sb,
00070     const char *pathname, const struct stat *st, int chown_restricted,
00071     int owner, int group, const char *caption,
00072     const explain_final_t *final_component)
00073 {
00074     uid_t           uid;
00075 
00076     uid = final_component->id.uid;
00077     if (owner >= 0 && (uid_t)owner != st->st_uid)
00078     {
00079         int             may_change_file_owner;
00080 
00081         may_change_file_owner =
00082             (
00083                 (!chown_restricted && uid == st->st_uid)
00084             ||
00085                 explain_capability_chown()
00086             );
00087         if (!may_change_file_owner)
00088         {
00089             if (chown_restricted)
00090             {
00091                 explain_string_buffer_t euid_sb;
00092                 char            euid[40];
00093 
00094                 explain_string_buffer_init(&euid_sb, euid, sizeof(euid));
00095                 explain_buffer_uid(&euid_sb, uid);
00096                 explain_string_buffer_printf_gettext
00097                 (
00098                     sb,
00099                     /*
00100                      * xgettext: This message is used to explain an
00101                      * EPERM error reported by a chown (or similar)
00102                      * system call, in the case where chown is
00103                      * restricted, i.e. when it is not sufficent to be
00104                      * the owner of the file to change its ownership.
00105                      *
00106                      * %1$s => the process effictive UID number and name,
00107                      *         already quoted
00108                      * %2$s => the name of the offending syscall argument
00109                      */
00110                     i18n("the process effective UID %s is the same as "
00111                         "the owner UID of %s but this is not sufficient "
00112                         "privilege to change the owner UID"),
00113                     euid,
00114                     caption
00115                 );
00116                 explain_buffer_dac_chown(sb);
00117             }
00118             else
00119             {
00120                 if (!pathname)
00121                 {
00122                     explain_buffer_does_not_have_inode_modify_permission_fd_st
00123                     (
00124                         sb,
00125                         st,
00126                         caption,
00127                         &final_component->id
00128                     );
00129                 }
00130                 else
00131                 {
00132                     explain_buffer_does_not_have_inode_modify_permission1
00133                     (
00134                         sb,
00135                         pathname,
00136                         st,
00137                         caption,
00138                         &final_component->id
00139                     );
00140                 }
00141             }
00142             return 0;
00143         }
00144     }
00145 
00146     if (group >= 0 && (gid_t)group != st->st_gid)
00147     {
00148         int             may_change_file_group;
00149 
00150         /*
00151          * The owner of a file may change the group of the file
00152          * to any group of which that owner is a member.  A
00153          * privileged process (root, or CAP_CHOWN) may change
00154          * the group arbitrarily.
00155          */
00156         may_change_file_group =
00157             (
00158                 (
00159                     uid == st->st_uid
00160                 &&
00161                     /* FIXME: watch out for NFS 16 group limit */
00162                     explain_group_in_groups
00163                     (
00164                         group,
00165                         &final_component->id
00166                     )
00167                 )
00168             ||
00169                 explain_capability_chown()
00170             );
00171         if (!may_change_file_group)
00172         {
00173             if (uid != st->st_uid)
00174             {
00175                 explain_string_buffer_t nuid_sb;
00176                 explain_string_buffer_t euid_sb;
00177                 char            nuid[40];
00178                 char            euid[40];
00179 
00180                 explain_string_buffer_init(&euid_sb, euid, sizeof(euid));
00181                 explain_buffer_uid(&euid_sb, uid);
00182                 explain_string_buffer_init(&nuid_sb, nuid, sizeof(nuid));
00183                 explain_buffer_uid(&nuid_sb, st->st_uid);
00184 
00185                 explain_string_buffer_puts(sb, ", ");
00186                 explain_string_buffer_printf_gettext
00187                 (
00188                     sb,
00189                     /*
00190                      * xgettext:  This error message is used to explain
00191                      * an EPERM error reported by the chown(2) system
00192                      * call, in the case where the process euid does not
00193                      * match the file's owner.
00194                      *
00195                      * %1$s => the process effective UID, already quoted
00196                      * %2$s => the name of the offenting syscall argument
00197                      * %3$s => the file's UID, already quoted
00198                      */
00199                     i18n("the process effective UID is %s but the %s owner "
00200                         "UID is %s"),
00201                     euid,
00202                     caption,
00203                     nuid
00204                 );
00205             }
00206             else /*if (!explain_group_in_groups(group, &final_component->id)) */
00207             {
00208                 explain_string_buffer_t egid_sb;
00209                 explain_string_buffer_t ngid_sb;
00210                 explain_string_buffer_t list_sb;
00211                 char            ngid[40];
00212                 char            egid[40];
00213                 char            list[1000];
00214 
00215                 explain_string_buffer_init(&ngid_sb, ngid, sizeof(ngid));
00216                 explain_buffer_gid(&ngid_sb, group);
00217                 explain_string_buffer_init(&egid_sb, egid, sizeof(egid));
00218                 explain_buffer_gid(&egid_sb, getegid());
00219                 explain_string_buffer_init(&list_sb, list, sizeof(list));
00220                 explain_buffer_gid_supplementary(&list_sb);
00221 
00222                 /* FIXME: watch out for NFS 16 group limit */
00223                 explain_string_buffer_printf_gettext
00224                 (
00225                     sb,
00226                     /*
00227                      * xgettext:  This error message is used when the
00228                      * chown(2) system call returns an EPERM error, is
00229                      * the case where the GID is inappropriate, and the
00230                      * process is not priviliged.
00231                      *
00232                      * %1$s => the name and number of the requested GID,
00233                      *         already quoted.
00234                      * %2$s => the name and number of the process effective
00235                      *         GID, already quoted.
00236                      * %3$s => the names and numbers of the supplementary GID
00237                      *         list, already quoted.
00238                      */
00239                     i18n("the requested group GID %s is not the process "
00240                         "effective group GID %s and is not in the "
00241                         "supplementary GID list %s"),
00242                     ngid,
00243                     egid,
00244                     list
00245                 );
00246             }
00247             explain_buffer_dac_chown(sb);
00248             return 0;
00249         }
00250     }
00251 
00252     /*
00253      * No error, yay.  Except, we were looking for an error, but none
00254      * was found, so we report failure.
00255      */
00256     return -1;
00257 }
00258 
00259 
00260 static void
00261 explain_buffer_eperm_chown_vague(explain_string_buffer_t *sb, int owner,
00262     int group)
00263 {
00264     if (owner >= 0)
00265     {
00266         if (group >= 0)
00267         {
00268             explain_buffer_gettext
00269             (
00270                 sb,
00271                 /*
00272                  * xgettext: The message is used when explaining an EPERM
00273                  * error reported by the chown(2) system call, in the case
00274                  * where no more specific explanation is available,
00275                  * but the call attempted to change both the UID and the GID.
00276                  */
00277                 i18n("the process did not have the required permissions "
00278                 "to change the owner UID and group GID")
00279             );
00280         }
00281         else
00282         {
00283             explain_buffer_gettext
00284             (
00285                 sb,
00286                 /*
00287                  * xgettext: The message is used when explaining an EPERM
00288                  * error reported by the chown(2) system call, in the case
00289                  * where no more specific explanation is available,
00290                  * but the call attempted to change only the UID.
00291                  */
00292                 i18n("the process did not have the required permissions "
00293                 "to change the owner UID")
00294             );
00295         }
00296     }
00297     else
00298     {
00299         if (group >= 0)
00300         {
00301             explain_buffer_eperm_setgid(sb);
00302         }
00303         else
00304         {
00305             explain_buffer_gettext
00306             (
00307                 sb,
00308                 /*
00309                  * xgettext: The message is used when explaining an EPERM
00310                  * error reported by the chown(2) system call, in the case
00311                  * where no more specific explanation is available.
00312                  */
00313                 i18n("the process did not have the required permissions "
00314                 "to change the owner UID or group GID")
00315             );
00316         }
00317     }
00318     explain_buffer_dac_chown(sb);
00319 }
00320 
00321 
00322 static void
00323 explain_buffer_eperm_chown(explain_string_buffer_t *sb,
00324     const char *pathname, int owner, int group, const char *pathname_caption,
00325     explain_final_t *final_component)
00326 {
00327     int             chown_restricted;
00328 
00329     /*
00330      * We have to ask whether or not the chown(2) system call
00331      * is restricted or not.  If the system has never heard of
00332      * _PC_CHOWN_RESTRICTED, we will assume that is is.
00333      *
00334      *  1 restricted
00335      *  0 not restricted
00336      * -1 assume restricted
00337      *
00338      *  If chown_restricted, it means only root can chown the file;
00339      *  if chown not restricted, it means owner can chown the file.
00340      */
00341     chown_restricted = !!pathconf(pathname, _PC_CHOWN_RESTRICTED);
00342     if (owner >= 0 && chown_restricted && group < 0)
00343         final_component->want_to_modify_inode = 0;
00344 
00345     /*
00346      * First we check to see if there is a problem with the pathname itself.
00347      */
00348     if
00349     (
00350         explain_buffer_errno_path_resolution
00351         (
00352             sb,
00353             EPERM,
00354             pathname,
00355             pathname_caption,
00356             final_component
00357         )
00358     >=
00359         0
00360     )
00361         return;
00362 
00363     /*
00364      * we get to here if the process DOES have inode modify permission,
00365      * or permissions could not be determined
00366      */
00367     {
00368         struct stat     st;
00369 
00370         if (stat(pathname, &st) >= 0)
00371         {
00372             if
00373             (
00374                 explain_buffer_eperm_chown_st
00375                 (
00376                     sb,
00377                     pathname,
00378                     &st,
00379                     chown_restricted,
00380                     owner,
00381                     group,
00382                     pathname_caption,
00383                     final_component
00384                 )
00385             >=
00386                 0
00387             )
00388                 return;
00389         }
00390     }
00391 
00392     explain_buffer_eperm_chown_vague(sb, owner, group);
00393 }
00394 
00395 
00396 static void
00397 explain_buffer_eperm_chown_fd(explain_string_buffer_t *sb, int fildes,
00398     int owner, int group, const char *fildes_caption)
00399 {
00400     int             chown_restricted;
00401     explain_final_t final_component;
00402 
00403     /*
00404      * We have to ask whether or not the chown(2) system call
00405      * is restricted or not.  If the system has never heard of
00406      * _PC_CHOWN_RESTRICTED, we will assume that is is.
00407      *
00408      *  1 restricted
00409      *  0 not restricted
00410      * -1 assume restricted
00411      *
00412      *  If chown_restricted, it means only root can chown the file;
00413      *  if chown not restricted, it means owner can chown the file.
00414      */
00415     chown_restricted = !!fpathconf(fildes, _PC_CHOWN_RESTRICTED);
00416     explain_final_init(&final_component);
00417     if (owner >= 0 && chown_restricted && group < 0)
00418         final_component.want_to_modify_inode = 0;
00419     else
00420         final_component.want_to_modify_inode = 1;
00421 
00422     /*
00423      * we get to here if the process DOES have inode modify permission,
00424      * or permissions could not be determined
00425      */
00426     {
00427         struct stat     st;
00428 
00429         if (fstat(fildes, &st) >= 0)
00430         {
00431             if
00432             (
00433                 explain_buffer_eperm_chown_st
00434                 (
00435                     sb,
00436                     0,
00437                     &st,
00438                     chown_restricted,
00439                     owner,
00440                     group,
00441                     fildes_caption,
00442                     &final_component
00443                 )
00444             >=
00445                 0
00446             )
00447                 return;
00448         }
00449     }
00450     explain_buffer_eperm_chown_vague(sb, owner, group);
00451 }
00452 
00453 
00454 void
00455 explain_buffer_errno_chown_explanation(explain_string_buffer_t *sb,
00456     int errnum, const char *syscall_name, const char *pathname, int owner,
00457     int group, const char *pathname_caption)
00458 {
00459     explain_final_t final_component;
00460 
00461     explain_final_init(&final_component);
00462     final_component.want_to_modify_inode = 1;
00463 
00464     explain_buffer_errno_chown_explanation_fc
00465     (
00466         sb,
00467         errnum,
00468         syscall_name,
00469         pathname,
00470         owner,
00471         group,
00472         pathname_caption,
00473         &final_component
00474     );
00475 }
00476 
00477 
00478 static void
00479 explain_buffer_einval_chown(explain_string_buffer_t *sb)
00480 {
00481     explain_buffer_gettext
00482     (
00483         sb,
00484         /*
00485          * xgettext: This error message is issued to explain an EINVAL error
00486          * reported by a chown (or similar) system call, in the case where
00487          * either the UID is invalid, the GID is invalid, or both.
00488          */
00489         i18n("the owner UID or group GID is not a value supported by the "
00490         "system")
00491     );
00492 }
00493 
00494 
00495 void
00496 explain_buffer_errno_chown_explanation_fc(explain_string_buffer_t *sb,
00497     int errnum, const char *syscall_name, const char *pathname, int owner,
00498     int group, const char *pathname_caption, explain_final_t *final_component)
00499 {
00500     final_component->want_to_modify_inode = 1;
00501 
00502     switch (errnum)
00503     {
00504     case EACCES:
00505         explain_buffer_eacces(sb, pathname, pathname_caption, final_component);
00506         break;
00507 
00508     case EFAULT:
00509         explain_buffer_efault(sb, pathname_caption);
00510         break;
00511 
00512     case EIO:
00513         explain_buffer_eio_path(sb, pathname);
00514         break;
00515 
00516     case EINVAL:
00517         explain_buffer_einval_chown(sb);
00518         break;
00519 
00520     case ELOOP:
00521         explain_buffer_eloop(sb, pathname, pathname_caption, final_component);
00522         break;
00523 
00524     case ENAMETOOLONG:
00525         explain_buffer_enametoolong
00526         (
00527             sb,
00528             pathname,
00529             pathname_caption,
00530             final_component
00531         );
00532         break;
00533 
00534     case ENOENT:
00535         explain_buffer_enoent(sb, pathname, pathname_caption, final_component);
00536         break;
00537 
00538     case ENOMEM:
00539         explain_buffer_enomem_kernel(sb);
00540         break;
00541 
00542     case ENOTDIR:
00543         explain_buffer_enotdir(sb, pathname, pathname_caption, final_component);
00544         break;
00545 
00546     case EPERM:
00547         explain_buffer_eperm_chown
00548         (
00549             sb,
00550             pathname,
00551             owner,
00552             group,
00553             pathname_caption,
00554             final_component
00555         );
00556         break;
00557 
00558     case EROFS:
00559         explain_buffer_erofs(sb, pathname, pathname_caption);
00560         break;
00561 
00562     default:
00563         explain_buffer_errno_generic(sb, errnum, syscall_name);
00564         break;
00565     }
00566 }
00567 
00568 
00569 void
00570 explain_buffer_errno_fchown_explanation(explain_string_buffer_t *sb,
00571     int errnum, const char *syscall_name, int fildes, int owner, int group,
00572     const char *fildes_caption)
00573 {
00574     switch (errnum)
00575     {
00576     case EBADF:
00577         explain_buffer_ebadf(sb, fildes, fildes_caption);
00578         break;
00579 
00580     case EIO:
00581         explain_buffer_eio_fildes(sb, fildes);
00582         break;
00583 
00584     case EINVAL:
00585         {
00586             struct stat     st;
00587 
00588             if (fstat(fildes, &st) >= 0)
00589             {
00590                 switch (st.st_mode & S_IFMT)
00591                 {
00592 #if S_IFREG != 0
00593                 case 0:
00594 #endif
00595                 case S_IFSOCK:
00596                 case S_IFIFO:
00597                     explain_buffer_enosys_fildes
00598                     (
00599                         sb,
00600                         fildes,
00601                         fildes_caption,
00602                         syscall_name
00603                     );
00604                     return;
00605 
00606                 default:
00607                     break;
00608                 }
00609             }
00610         }
00611         explain_buffer_einval_chown(sb);
00612         break;
00613 
00614     case ENOMEM:
00615         explain_buffer_enomem_kernel(sb);
00616         break;
00617 
00618     case EPERM:
00619         explain_buffer_eperm_chown_fd(sb, fildes, owner, group, fildes_caption);
00620         break;
00621 
00622     case EROFS:
00623         explain_buffer_erofs_fildes(sb, fildes, fildes_caption);
00624         break;
00625 
00626     default:
00627         explain_buffer_errno_generic(sb, errnum, syscall_name);
00628         break;
00629     }
00630 }
00631 
00632 
00633 void
00634 explain_buffer_errno_chown(explain_string_buffer_t *sb, int errnum,
00635     const char *pathname, int owner, int group)
00636 {
00637     explain_explanation_t exp;
00638 
00639     explain_explanation_init(&exp, errnum);
00640     explain_buffer_errno_chown_system_call
00641     (
00642         &exp.system_call_sb,
00643         errnum,
00644         pathname,
00645         owner,
00646         group
00647     );
00648     explain_buffer_errno_chown_explanation
00649     (
00650         &exp.explanation_sb,
00651         errnum,
00652         "chown",
00653         pathname,
00654         owner,
00655         group,
00656         "pathname"
00657     );
00658     explain_explanation_assemble(&exp, sb);
00659 }
00660 
00661 
00662 /* vim: set ts=8 sw=4 et : */