libexplain
1.4.D001
|
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 : */