1/* set-mode-acl.c - set access control list equivalent to a mode 2 3 Copyright (C) 2002-2003, 2005-2010 Free Software Foundation, Inc. 4 5 This program is free software: you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation; either version 3 of the License, or 8 (at your option) any later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 Written by Paul Eggert and Andreas Gruenbacher, and Bruno Haible. */ 19 20#include <config.h> 21 22#include "acl.h" 23 24#include "acl-internal.h" 25 26#include "gettext.h" 27#define _(msgid) gettext (msgid) 28 29 30/* If DESC is a valid file descriptor use fchmod to change the 31 file's mode to MODE on systems that have fchown. On systems 32 that don't have fchown and if DESC is invalid, use chown on 33 NAME instead. 34 Return 0 if successful. Return -1 and set errno upon failure. */ 35 36int 37chmod_or_fchmod (const char *name, int desc, mode_t mode) 38{ 39 if (HAVE_FCHMOD && desc != -1) 40 return fchmod (desc, mode); 41 else 42 return chmod (name, mode); 43} 44 45/* Set the access control lists of a file. If DESC is a valid file 46 descriptor, use file descriptor operations where available, else use 47 filename based operations on NAME. If access control lists are not 48 available, fchmod the target file to MODE. Also sets the 49 non-permission bits of the destination file (S_ISUID, S_ISGID, S_ISVTX) 50 to those from MODE if any are set. 51 Return 0 if successful. Return -1 and set errno upon failure. */ 52 53int 54qset_acl (char const *name, int desc, mode_t mode) 55{ 56#if USE_ACL 57# if HAVE_ACL_GET_FILE 58 /* POSIX 1003.1e draft 17 (abandoned) specific version. */ 59 /* Linux, FreeBSD, MacOS X, IRIX, Tru64 */ 60# if MODE_INSIDE_ACL 61 /* Linux, FreeBSD, IRIX, Tru64 */ 62 63 /* We must also have acl_from_text and acl_delete_def_file. 64 (acl_delete_def_file could be emulated with acl_init followed 65 by acl_set_file, but acl_set_file with an empty acl is 66 unspecified.) */ 67 68# ifndef HAVE_ACL_FROM_TEXT 69# error Must have acl_from_text (see POSIX 1003.1e draft 17). 70# endif 71# ifndef HAVE_ACL_DELETE_DEF_FILE 72# error Must have acl_delete_def_file (see POSIX 1003.1e draft 17). 73# endif 74 75 acl_t acl; 76 int ret; 77 78 if (HAVE_ACL_FROM_MODE) /* Linux */ 79 { 80 acl = acl_from_mode (mode); 81 if (!acl) 82 return -1; 83 } 84 else /* FreeBSD, IRIX, Tru64 */ 85 { 86 /* If we were to create the ACL using the functions acl_init(), 87 acl_create_entry(), acl_set_tag_type(), acl_set_qualifier(), 88 acl_get_permset(), acl_clear_perm[s](), acl_add_perm(), we 89 would need to create a qualifier. I don't know how to do this. 90 So create it using acl_from_text(). */ 91 92# if HAVE_ACL_FREE_TEXT /* Tru64 */ 93 char acl_text[] = "u::---,g::---,o::---,"; 94# else /* FreeBSD, IRIX */ 95 char acl_text[] = "u::---,g::---,o::---"; 96# endif 97 98 if (mode & S_IRUSR) acl_text[ 3] = 'r'; 99 if (mode & S_IWUSR) acl_text[ 4] = 'w'; 100 if (mode & S_IXUSR) acl_text[ 5] = 'x'; 101 if (mode & S_IRGRP) acl_text[10] = 'r'; 102 if (mode & S_IWGRP) acl_text[11] = 'w'; 103 if (mode & S_IXGRP) acl_text[12] = 'x'; 104 if (mode & S_IROTH) acl_text[17] = 'r'; 105 if (mode & S_IWOTH) acl_text[18] = 'w'; 106 if (mode & S_IXOTH) acl_text[19] = 'x'; 107 108 acl = acl_from_text (acl_text); 109 if (!acl) 110 return -1; 111 } 112 if (HAVE_ACL_SET_FD && desc != -1) 113 ret = acl_set_fd (desc, acl); 114 else 115 ret = acl_set_file (name, ACL_TYPE_ACCESS, acl); 116 if (ret != 0) 117 { 118 int saved_errno = errno; 119 acl_free (acl); 120 121 if (ACL_NOT_WELL_SUPPORTED (errno)) 122 return chmod_or_fchmod (name, desc, mode); 123 else 124 { 125 errno = saved_errno; 126 return -1; 127 } 128 } 129 else 130 acl_free (acl); 131 132 if (S_ISDIR (mode) && acl_delete_def_file (name)) 133 return -1; 134 135 if (mode & (S_ISUID | S_ISGID | S_ISVTX)) 136 { 137 /* We did not call chmod so far, so the special bits have not yet 138 been set. */ 139 return chmod_or_fchmod (name, desc, mode); 140 } 141 return 0; 142 143# else /* !MODE_INSIDE_ACL */ 144 /* MacOS X */ 145 146# if !HAVE_ACL_TYPE_EXTENDED 147# error Must have ACL_TYPE_EXTENDED 148# endif 149 150 /* On MacOS X, acl_get_file (name, ACL_TYPE_ACCESS) 151 and acl_get_file (name, ACL_TYPE_DEFAULT) 152 always return NULL / EINVAL. You have to use 153 acl_get_file (name, ACL_TYPE_EXTENDED) 154 or acl_get_fd (open (name, ...)) 155 to retrieve an ACL. 156 On the other hand, 157 acl_set_file (name, ACL_TYPE_ACCESS, acl) 158 and acl_set_file (name, ACL_TYPE_DEFAULT, acl) 159 have the same effect as 160 acl_set_file (name, ACL_TYPE_EXTENDED, acl): 161 Each of these calls sets the file's ACL. */ 162 163 acl_t acl; 164 int ret; 165 166 /* Remove the ACL if the file has ACLs. */ 167 if (HAVE_ACL_GET_FD && desc != -1) 168 acl = acl_get_fd (desc); 169 else 170 acl = acl_get_file (name, ACL_TYPE_EXTENDED); 171 if (acl) 172 { 173 acl_free (acl); 174 175 acl = acl_init (0); 176 if (acl) 177 { 178 if (HAVE_ACL_SET_FD && desc != -1) 179 ret = acl_set_fd (desc, acl); 180 else 181 ret = acl_set_file (name, ACL_TYPE_EXTENDED, acl); 182 if (ret != 0) 183 { 184 int saved_errno = errno; 185 186 acl_free (acl); 187 188 if (ACL_NOT_WELL_SUPPORTED (saved_errno)) 189 return chmod_or_fchmod (name, desc, mode); 190 else 191 { 192 errno = saved_errno; 193 return -1; 194 } 195 } 196 acl_free (acl); 197 } 198 } 199 200 /* Since !MODE_INSIDE_ACL, we have to call chmod explicitly. */ 201 return chmod_or_fchmod (name, desc, mode); 202# endif 203 204# elif HAVE_ACL && defined GETACLCNT /* Solaris, Cygwin, not HP-UX */ 205 206# if defined ACL_NO_TRIVIAL 207 /* Solaris 10 (newer version), which has additional API declared in 208 <sys/acl.h> (acl_t) and implemented in libsec (acl_set, acl_trivial, 209 acl_fromtext, ...). */ 210 211 acl_t *aclp; 212 char acl_text[] = "user::---,group::---,mask:---,other:---"; 213 int ret; 214 int saved_errno; 215 216 if (mode & S_IRUSR) acl_text[ 6] = 'r'; 217 if (mode & S_IWUSR) acl_text[ 7] = 'w'; 218 if (mode & S_IXUSR) acl_text[ 8] = 'x'; 219 if (mode & S_IRGRP) acl_text[17] = acl_text[26] = 'r'; 220 if (mode & S_IWGRP) acl_text[18] = acl_text[27] = 'w'; 221 if (mode & S_IXGRP) acl_text[19] = acl_text[28] = 'x'; 222 if (mode & S_IROTH) acl_text[36] = 'r'; 223 if (mode & S_IWOTH) acl_text[37] = 'w'; 224 if (mode & S_IXOTH) acl_text[38] = 'x'; 225 226 if (acl_fromtext (acl_text, &aclp) != 0) 227 { 228 errno = ENOMEM; 229 return -1; 230 } 231 232 ret = (desc < 0 ? acl_set (name, aclp) : facl_set (desc, aclp)); 233 saved_errno = errno; 234 acl_free (aclp); 235 if (ret < 0) 236 { 237 if (saved_errno == ENOSYS || saved_errno == EOPNOTSUPP) 238 return chmod_or_fchmod (name, desc, mode); 239 errno = saved_errno; 240 return -1; 241 } 242 243 if (mode & (S_ISUID | S_ISGID | S_ISVTX)) 244 { 245 /* We did not call chmod so far, so the special bits have not yet 246 been set. */ 247 return chmod_or_fchmod (name, desc, mode); 248 } 249 return 0; 250 251# else /* Solaris, Cygwin, general case */ 252 253# ifdef ACE_GETACL 254 /* Solaris also has a different variant of ACLs, used in ZFS and NFSv4 255 file systems (whereas the other ones are used in UFS file systems). */ 256 257 /* The flags in the ace_t structure changed in a binary incompatible way 258 when ACL_NO_TRIVIAL etc. were introduced in <sys/acl.h> version 1.15. 259 How to distinguish the two conventions at runtime? 260 We fetch the existing ACL. In the old convention, usually three ACEs have 261 a_flags = ACE_OWNER / ACE_GROUP / ACE_OTHER, in the range 0x0100..0x0400. 262 In the new convention, these values are not used. */ 263 int convention; 264 265 { 266 int count; 267 ace_t *entries; 268 269 for (;;) 270 { 271 if (desc != -1) 272 count = facl (desc, ACE_GETACLCNT, 0, NULL); 273 else 274 count = acl (name, ACE_GETACLCNT, 0, NULL); 275 if (count <= 0) 276 { 277 convention = -1; 278 break; 279 } 280 entries = (ace_t *) malloc (count * sizeof (ace_t)); 281 if (entries == NULL) 282 { 283 errno = ENOMEM; 284 return -1; 285 } 286 if ((desc != -1 287 ? facl (desc, ACE_GETACL, count, entries) 288 : acl (name, ACE_GETACL, count, entries)) 289 == count) 290 { 291 int i; 292 293 convention = 0; 294 for (i = 0; i < count; i++) 295 if (entries[i].a_flags & (ACE_OWNER | ACE_GROUP | ACE_OTHER)) 296 { 297 convention = 1; 298 break; 299 } 300 free (entries); 301 break; 302 } 303 /* Huh? The number of ACL entries changed since the last call. 304 Repeat. */ 305 free (entries); 306 } 307 } 308 309 if (convention >= 0) 310 { 311 ace_t entries[3]; 312 int ret; 313 314 if (convention) 315 { 316 /* Running on Solaris 10. */ 317 entries[0].a_type = ALLOW; 318 entries[0].a_flags = ACE_OWNER; 319 entries[0].a_who = 0; /* irrelevant */ 320 entries[0].a_access_mask = (mode >> 6) & 7; 321 entries[1].a_type = ALLOW; 322 entries[1].a_flags = ACE_GROUP; 323 entries[1].a_who = 0; /* irrelevant */ 324 entries[1].a_access_mask = (mode >> 3) & 7; 325 entries[2].a_type = ALLOW; 326 entries[2].a_flags = ACE_OTHER; 327 entries[2].a_who = 0; 328 entries[2].a_access_mask = mode & 7; 329 } 330 else 331 { 332 /* Running on Solaris 10 (newer version) or Solaris 11. */ 333 entries[0].a_type = ACE_ACCESS_ALLOWED_ACE_TYPE; 334 entries[0].a_flags = NEW_ACE_OWNER; 335 entries[0].a_who = 0; /* irrelevant */ 336 entries[0].a_access_mask = 337 (mode & 0400 ? NEW_ACE_READ_DATA : 0) 338 | (mode & 0200 ? NEW_ACE_WRITE_DATA : 0) 339 | (mode & 0100 ? NEW_ACE_EXECUTE : 0); 340 entries[1].a_type = ACE_ACCESS_ALLOWED_ACE_TYPE; 341 entries[1].a_flags = NEW_ACE_GROUP | NEW_ACE_IDENTIFIER_GROUP; 342 entries[1].a_who = 0; /* irrelevant */ 343 entries[1].a_access_mask = 344 (mode & 0040 ? NEW_ACE_READ_DATA : 0) 345 | (mode & 0020 ? NEW_ACE_WRITE_DATA : 0) 346 | (mode & 0010 ? NEW_ACE_EXECUTE : 0); 347 entries[2].a_type = ACE_ACCESS_ALLOWED_ACE_TYPE; 348 entries[2].a_flags = ACE_EVERYONE; 349 entries[2].a_who = 0; 350 entries[2].a_access_mask = 351 (mode & 0004 ? NEW_ACE_READ_DATA : 0) 352 | (mode & 0002 ? NEW_ACE_WRITE_DATA : 0) 353 | (mode & 0001 ? NEW_ACE_EXECUTE : 0); 354 } 355 if (desc != -1) 356 ret = facl (desc, ACE_SETACL, 357 sizeof (entries) / sizeof (ace_t), entries); 358 else 359 ret = acl (name, ACE_SETACL, 360 sizeof (entries) / sizeof (ace_t), entries); 361 if (ret < 0 && errno != EINVAL && errno != ENOTSUP) 362 { 363 if (errno == ENOSYS) 364 return chmod_or_fchmod (name, desc, mode); 365 return -1; 366 } 367 } 368# endif 369 370 { 371 aclent_t entries[3]; 372 int ret; 373 374 entries[0].a_type = USER_OBJ; 375 entries[0].a_id = 0; /* irrelevant */ 376 entries[0].a_perm = (mode >> 6) & 7; 377 entries[1].a_type = GROUP_OBJ; 378 entries[1].a_id = 0; /* irrelevant */ 379 entries[1].a_perm = (mode >> 3) & 7; 380 entries[2].a_type = OTHER_OBJ; 381 entries[2].a_id = 0; 382 entries[2].a_perm = mode & 7; 383 384 if (desc != -1) 385 ret = facl (desc, SETACL, sizeof (entries) / sizeof (aclent_t), entries); 386 else 387 ret = acl (name, SETACL, sizeof (entries) / sizeof (aclent_t), entries); 388 if (ret < 0) 389 { 390 if (errno == ENOSYS) 391 return chmod_or_fchmod (name, desc, mode); 392 return -1; 393 } 394 } 395 396 if (!MODE_INSIDE_ACL || (mode & (S_ISUID | S_ISGID | S_ISVTX))) 397 { 398 /* We did not call chmod so far, so the special bits have not yet 399 been set. */ 400 return chmod_or_fchmod (name, desc, mode); 401 } 402 return 0; 403 404# endif 405 406# elif HAVE_GETACL /* HP-UX */ 407 408 struct stat statbuf; 409 struct acl_entry entries[3]; 410 int ret; 411 412 if (desc != -1) 413 ret = fstat (desc, &statbuf); 414 else 415 ret = stat (name, &statbuf); 416 if (ret < 0) 417 return -1; 418 419 entries[0].uid = statbuf.st_uid; 420 entries[0].gid = ACL_NSGROUP; 421 entries[0].mode = (mode >> 6) & 7; 422 entries[1].uid = ACL_NSUSER; 423 entries[1].gid = statbuf.st_gid; 424 entries[1].mode = (mode >> 3) & 7; 425 entries[2].uid = ACL_NSUSER; 426 entries[2].gid = ACL_NSGROUP; 427 entries[2].mode = mode & 7; 428 429 if (desc != -1) 430 ret = fsetacl (desc, sizeof (entries) / sizeof (struct acl_entry), entries); 431 else 432 ret = setacl (name, sizeof (entries) / sizeof (struct acl_entry), entries); 433 if (ret < 0) 434 { 435 if (errno == ENOSYS || errno == EOPNOTSUPP) 436 return chmod_or_fchmod (name, desc, mode); 437 return -1; 438 } 439 440 if (mode & (S_ISUID | S_ISGID | S_ISVTX)) 441 { 442 /* We did not call chmod so far, so the special bits have not yet 443 been set. */ 444 return chmod_or_fchmod (name, desc, mode); 445 } 446 return 0; 447 448# elif HAVE_ACLX_GET && defined ACL_AIX_WIP /* AIX */ 449 450 acl_type_list_t types; 451 size_t types_size = sizeof (types); 452 acl_type_t type; 453 454 if (aclx_gettypes (name, &types, &types_size) < 0 455 || types.num_entries == 0) 456 return chmod_or_fchmod (name, desc, mode); 457 458 /* XXX Do we need to clear all types of ACLs for the given file, or is it 459 sufficient to clear the first one? */ 460 type = types.entries[0]; 461 if (type.u64 == ACL_AIXC) 462 { 463 union { struct acl a; char room[128]; } u; 464 int ret; 465 466 u.a.acl_len = (char *) &u.a.acl_ext[0] - (char *) &u.a; /* no entries */ 467 u.a.acl_mode = mode & ~(S_IXACL | 0777); 468 u.a.u_access = (mode >> 6) & 7; 469 u.a.g_access = (mode >> 3) & 7; 470 u.a.o_access = mode & 7; 471 472 if (desc != -1) 473 ret = aclx_fput (desc, SET_ACL | SET_MODE_S_BITS, 474 type, &u.a, u.a.acl_len, mode); 475 else 476 ret = aclx_put (name, SET_ACL | SET_MODE_S_BITS, 477 type, &u.a, u.a.acl_len, mode); 478 if (!(ret < 0 && errno == ENOSYS)) 479 return ret; 480 } 481 else if (type.u64 == ACL_NFS4) 482 { 483 union { nfs4_acl_int_t a; char room[128]; } u; 484 nfs4_ace_int_t *ace; 485 int ret; 486 487 u.a.aclVersion = NFS4_ACL_INT_STRUCT_VERSION; 488 u.a.aclEntryN = 0; 489 ace = &u.a.aclEntry[0]; 490 { 491 ace->flags = ACE4_ID_SPECIAL; 492 ace->aceWho.special_whoid = ACE4_WHO_OWNER; 493 ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE; 494 ace->aceFlags = 0; 495 ace->aceMask = 496 (mode & 0400 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0) 497 | (mode & 0200 498 ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA 499 | ACE4_ADD_SUBDIRECTORY 500 : 0) 501 | (mode & 0100 ? ACE4_EXECUTE : 0); 502 ace->aceWhoString[0] = '\0'; 503 ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace; 504 ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4]; 505 u.a.aclEntryN++; 506 } 507 { 508 ace->flags = ACE4_ID_SPECIAL; 509 ace->aceWho.special_whoid = ACE4_WHO_GROUP; 510 ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE; 511 ace->aceFlags = 0; 512 ace->aceMask = 513 (mode & 0040 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0) 514 | (mode & 0020 515 ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA 516 | ACE4_ADD_SUBDIRECTORY 517 : 0) 518 | (mode & 0010 ? ACE4_EXECUTE : 0); 519 ace->aceWhoString[0] = '\0'; 520 ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace; 521 ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4]; 522 u.a.aclEntryN++; 523 } 524 { 525 ace->flags = ACE4_ID_SPECIAL; 526 ace->aceWho.special_whoid = ACE4_WHO_EVERYONE; 527 ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE; 528 ace->aceFlags = 0; 529 ace->aceMask = 530 (mode & 0004 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0) 531 | (mode & 0002 532 ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA 533 | ACE4_ADD_SUBDIRECTORY 534 : 0) 535 | (mode & 0001 ? ACE4_EXECUTE : 0); 536 ace->aceWhoString[0] = '\0'; 537 ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace; 538 ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4]; 539 u.a.aclEntryN++; 540 } 541 u.a.aclLength = (char *) ace - (char *) &u.a; 542 543 if (desc != -1) 544 ret = aclx_fput (desc, SET_ACL | SET_MODE_S_BITS, 545 type, &u.a, u.a.aclLength, mode); 546 else 547 ret = aclx_put (name, SET_ACL | SET_MODE_S_BITS, 548 type, &u.a, u.a.aclLength, mode); 549 if (!(ret < 0 && errno == ENOSYS)) 550 return ret; 551 } 552 553 return chmod_or_fchmod (name, desc, mode); 554 555# elif HAVE_STATACL /* older AIX */ 556 557 union { struct acl a; char room[128]; } u; 558 int ret; 559 560 u.a.acl_len = (char *) &u.a.acl_ext[0] - (char *) &u.a; /* no entries */ 561 u.a.acl_mode = mode & ~(S_IXACL | 0777); 562 u.a.u_access = (mode >> 6) & 7; 563 u.a.g_access = (mode >> 3) & 7; 564 u.a.o_access = mode & 7; 565 566 if (desc != -1) 567 ret = fchacl (desc, &u.a, u.a.acl_len); 568 else 569 ret = chacl (name, &u.a, u.a.acl_len); 570 571 if (ret < 0 && errno == ENOSYS) 572 return chmod_or_fchmod (name, desc, mode); 573 574 return ret; 575 576# else /* Unknown flavor of ACLs */ 577 return chmod_or_fchmod (name, desc, mode); 578# endif 579#else /* !USE_ACL */ 580 return chmod_or_fchmod (name, desc, mode); 581#endif 582} 583 584/* As with qset_acl, but also output a diagnostic on failure. */ 585 586int 587set_acl (char const *name, int desc, mode_t mode) 588{ 589 int r = qset_acl (name, desc, mode); 590 if (r != 0) 591 error (0, errno, _("setting permissions for %s"), quote (name)); 592 return r; 593} 594