4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
34 #include <sys/types.h>
37 #include <sys/varargs.h>
41 #include <acl_common.h>
55 * Determine whether a file has a trivial ACL
56 * returns: 0 = trivial
58 * <0 some other system failure, such as ENOENT or EPERM
61 acl_trivial(const char *filename
)
69 acl_flavor
= pathconf(filename
, _PC_ACL_ENABLED
);
71 if (acl_flavor
== _ACL_ACE_ENABLED
)
72 cntcmd
= ACE_GETACLCNT
;
76 aclcnt
= acl(filename
, cntcmd
, 0, NULL
);
78 if (acl_flavor
== _ACL_ACE_ENABLED
) {
79 acep
= malloc(sizeof (ace_t
) * aclcnt
);
82 if (acl(filename
, ACE_GETACL
,
88 val
= ace_trivial(acep
, aclcnt
);
91 } else if (aclcnt
> MIN_ACL_ENTRIES
)
99 cacl_get(acl_inp inp
, int get_flag
, int type
, acl_t
**aclp
)
109 struct stat64 statbuf
;
112 if (type
== ACL_PATH
) {
114 ace_acl
= pathconf(fname
, _PC_ACL_ENABLED
);
117 ace_acl
= fpathconf(fd
, _PC_ACL_ENABLED
);
121 * if acl's aren't supported then
122 * send it through the old GETACL interface
124 if (ace_acl
== 0 || ace_acl
== -1) {
125 ace_acl
= _ACL_ACLENT_ENABLED
;
128 if (ace_acl
& _ACL_ACE_ENABLED
) {
129 cntcmd
= ACE_GETACLCNT
;
131 acl_info
= acl_alloc(ACE_T
);
135 acl_info
= acl_alloc(ACLENT_T
);
138 if (acl_info
== NULL
)
141 if (type
== ACL_PATH
) {
142 acl_info
->acl_cnt
= acl(fname
, cntcmd
, 0, NULL
);
144 acl_info
->acl_cnt
= facl(fd
, cntcmd
, 0, NULL
);
148 if (acl_info
->acl_cnt
< 0) {
154 if (acl_info
->acl_cnt
== 0) {
161 malloc(acl_info
->acl_cnt
* acl_info
->acl_entry_size
);
164 if (acl_info
->acl_aclp
== NULL
) {
170 if (type
== ACL_PATH
) {
171 stat_error
= stat64(fname
, &statbuf
);
172 error
= acl(fname
, getcmd
, acl_info
->acl_cnt
,
175 stat_error
= fstat64(fd
, &statbuf
);
176 error
= facl(fd
, getcmd
, acl_info
->acl_cnt
,
188 if (stat_error
== 0) {
189 acl_info
->acl_flags
=
190 (S_ISDIR(statbuf
.st_mode
) ? ACL_IS_DIR
: 0);
192 acl_info
->acl_flags
= 0;
194 switch (acl_info
->acl_type
) {
196 if (acl_info
->acl_cnt
<= MIN_ACL_ENTRIES
)
197 acl_info
->acl_flags
|= ACL_IS_TRIVIAL
;
200 if (ace_trivial(acl_info
->acl_aclp
, acl_info
->acl_cnt
) == 0)
201 acl_info
->acl_flags
|= ACL_IS_TRIVIAL
;
209 if ((acl_info
->acl_flags
& ACL_IS_TRIVIAL
) &&
210 (get_flag
& ACL_NO_TRIVIAL
)) {
221 * return -1 on failure, otherwise the number of acl
222 * entries is returned
225 acl_get(const char *path
, int get_flag
, acl_t
**aclp
)
230 return (cacl_get(acl_inp
, get_flag
, ACL_PATH
, aclp
));
234 facl_get(int fd
, int get_flag
, acl_t
**aclp
)
240 return (cacl_get(acl_inp
, get_flag
, ACL_FD
, aclp
));
244 * Set an ACL, translates acl to ace_t when appropriate.
247 cacl_set(acl_inp
*acl_inp
, acl_t
*aclp
, int type
)
250 int acl_flavor_target
;
251 struct stat64 statbuf
;
256 if (type
== ACL_PATH
) {
257 stat_error
= stat64(acl_inp
->file
, &statbuf
);
260 acl_flavor_target
= pathconf(acl_inp
->file
, _PC_ACL_ENABLED
);
262 stat_error
= fstat64(acl_inp
->fd
, &statbuf
);
265 acl_flavor_target
= fpathconf(acl_inp
->fd
, _PC_ACL_ENABLED
);
269 * If target returns an error or 0 from pathconf call then
270 * fall back to UFS/POSIX Draft interface.
271 * In the case of 0 we will then fail in either acl(2) or
272 * acl_translate(). We could erroneously get 0 back from
273 * a file system that is using fs_pathconf() and not answering
274 * the _PC_ACL_ENABLED question itself.
276 if (acl_flavor_target
== 0 || acl_flavor_target
== -1)
277 acl_flavor_target
= _ACL_ACLENT_ENABLED
;
279 isdir
= S_ISDIR(statbuf
.st_mode
);
281 if ((error
= acl_translate(aclp
, acl_flavor_target
, isdir
,
282 statbuf
.st_uid
, statbuf
.st_gid
)) != 0) {
286 if (type
== ACL_PATH
) {
287 error
= acl(acl_inp
->file
,
288 (aclp
->acl_type
== ACE_T
) ? ACE_SETACL
: SETACL
,
289 aclp
->acl_cnt
, aclp
->acl_aclp
);
291 error
= facl(acl_inp
->fd
,
292 (aclp
->acl_type
== ACE_T
) ? ACE_SETACL
: SETACL
,
293 aclp
->acl_cnt
, aclp
->acl_aclp
);
300 acl_set(const char *path
, acl_t
*aclp
)
306 return (cacl_set(&acl_inp
, aclp
, ACL_PATH
));
310 facl_set(int fd
, acl_t
*aclp
)
316 return (cacl_set(&acl_inp
, aclp
, ACL_FD
));
322 return (aclp
->acl_cnt
);
326 acl_type(acl_t
*aclp
)
328 return (aclp
->acl_type
);
336 newaclp
= acl_alloc(aclp
->acl_type
);
340 newaclp
->acl_aclp
= malloc(aclp
->acl_entry_size
* aclp
->acl_cnt
);
341 if (newaclp
->acl_aclp
== NULL
) {
346 (void) memcpy(newaclp
->acl_aclp
,
347 aclp
->acl_aclp
, aclp
->acl_entry_size
* aclp
->acl_cnt
);
348 newaclp
->acl_cnt
= aclp
->acl_cnt
;
354 acl_flags(acl_t
*aclp
)
356 return (aclp
->acl_flags
);
360 acl_data(acl_t
*aclp
)
362 return (aclp
->acl_aclp
);
366 * Take an acl array and build an acl_t.
369 acl_to_aclp(enum acl_type type
, void *acl
, int count
)
374 aclp
= acl_alloc(type
);
378 aclp
->acl_aclp
= acl
;
379 aclp
->acl_cnt
= count
;
385 * Remove an ACL from a file and create a trivial ACL based
386 * off of the mode argument. After acl has been set owner/group
387 * are updated to match owner,group arguments
390 acl_strip(const char *file
, uid_t owner
, gid_t group
, mode_t mode
)
393 aclent_t min_acl
[MIN_ACL_ENTRIES
];
397 struct stat64 statbuf
;
399 acl_flavor
= pathconf(file
, _PC_ACL_ENABLED
);
401 if (stat64(file
, &statbuf
) != 0) {
407 * force it through aclent flavor when file system doesn't
408 * understand question
410 if (acl_flavor
== 0 || acl_flavor
== -1)
411 acl_flavor
= _ACL_ACLENT_ENABLED
;
413 if (acl_flavor
& _ACL_ACLENT_ENABLED
) {
414 min_acl
[0].a_type
= USER_OBJ
;
415 min_acl
[0].a_id
= owner
;
416 min_acl
[0].a_perm
= ((mode
& 0700) >> 6);
417 min_acl
[1].a_type
= GROUP_OBJ
;
418 min_acl
[1].a_id
= group
;
419 min_acl
[1].a_perm
= ((mode
& 0070) >> 3);
420 min_acl
[2].a_type
= CLASS_OBJ
;
421 min_acl
[2].a_id
= (uid_t
)-1;
422 min_acl
[2].a_perm
= ((mode
& 0070) >> 3);
423 min_acl
[3].a_type
= OTHER_OBJ
;
424 min_acl
[3].a_id
= (uid_t
)-1;
425 min_acl
[3].a_perm
= (mode
& 0007);
427 error
= acl(file
, SETACL
, aclcnt
, min_acl
);
428 } else if (acl_flavor
& _ACL_ACE_ENABLED
) {
429 if ((error
= acl_trivial_create(mode
, S_ISDIR(statbuf
.st_mode
),
430 &min_ace_acl
, &aclcnt
)) != 0)
432 error
= acl(file
, ACE_SETACL
, aclcnt
, min_ace_acl
);
440 error
= chown(file
, owner
, group
);
445 ace_match(void *entry1
, void *entry2
)
447 ace_t
*p1
= (ace_t
*)entry1
;
448 ace_t
*p2
= (ace_t
*)entry2
;
455 * Need to fixup who field for abstrations for
456 * accurate comparison, since field is undefined.
458 if (ace1
.a_flags
& (ACE_OWNER
|ACE_GROUP
|ACE_EVERYONE
))
459 ace1
.a_who
= (uid_t
)-1;
460 if (ace2
.a_flags
& (ACE_OWNER
|ACE_GROUP
|ACE_EVERYONE
))
461 ace2
.a_who
= (uid_t
)-1;
462 return (memcmp(&ace1
, &ace2
, sizeof (ace_t
)));
466 aclent_match(void *entry1
, void *entry2
)
468 aclent_t
*aclent1
= (aclent_t
*)entry1
;
469 aclent_t
*aclent2
= (aclent_t
*)entry2
;
471 return (memcmp(aclent1
, aclent2
, sizeof (aclent_t
)));
475 * Find acl entries in acl that correspond to removeacl. Search
476 * is started from slot. The flag argument indicates whether to
477 * remove all matches or just the first match.
480 acl_removeentries(acl_t
*acl
, acl_t
*removeacl
, int start_slot
, int flag
)
484 int (*acl_match
)(void *acl1
, void *acl2
);
485 void *acl_entry
, *remove_entry
;
489 if (flag
!= ACL_REMOVE_ALL
&& flag
!= ACL_REMOVE_FIRST
)
490 flag
= ACL_REMOVE_FIRST
;
492 if (acl
== NULL
|| removeacl
== NULL
)
493 return (EACL_NO_ACL_ENTRY
);
495 if (acl
->acl_type
!= removeacl
->acl_type
)
496 return (EACL_DIFF_TYPE
);
498 if (acl
->acl_type
== ACLENT_T
)
499 acl_match
= aclent_match
;
501 acl_match
= ace_match
;
503 for (i
= 0, remove_entry
= removeacl
->acl_aclp
;
504 i
!= removeacl
->acl_cnt
; i
++) {
507 acl_entry
= (char *)acl
->acl_aclp
+
508 (acl
->acl_entry_size
* start_slot
);
510 match
= acl_match(acl_entry
, remove_entry
);
514 /* avoid memmove if last entry */
515 if (acl
->acl_cnt
== (j
+ 1)) {
520 start
= (char *)acl_entry
+
522 (void) memmove(acl_entry
, start
,
523 acl
->acl_entry_size
*
524 (acl
->acl_cnt
-- - (j
+ 1)));
526 if (flag
== ACL_REMOVE_FIRST
)
529 * List has changed, just continue so this
530 * slot gets checked with it's new contents.
534 acl_entry
= ((char *)acl_entry
+ acl
->acl_entry_size
);
535 if (++j
>= acl
->acl_cnt
) {
539 remove_entry
= (char *)remove_entry
+ removeacl
->acl_entry_size
;
542 return ((found
== 0) ? EACL_NO_ACL_ENTRY
: 0);
546 * Replace entires entries in acl1 with the corresponding entries
547 * in newentries. The where argument specifies where to begin
548 * the replacement. If the where argument is 1 greater than the
549 * number of acl entries in acl1 then they are appended. If the
550 * where argument is 2+ greater than the number of acl entries then
551 * EACL_INVALID_SLOT is returned.
554 acl_modifyentries(acl_t
*acl1
, acl_t
*newentries
, int where
)
562 if (acl1
== NULL
|| newentries
== NULL
)
563 return (EACL_NO_ACL_ENTRY
);
565 if (where
< 0 || where
>= acl1
->acl_cnt
)
566 return (EACL_INVALID_SLOT
);
568 if (acl1
->acl_type
!= newentries
->acl_type
)
569 return (EACL_DIFF_TYPE
);
573 slots_left
= acl1
->acl_cnt
- slot
+ 1;
574 if (slots_left
< newentries
->acl_cnt
) {
575 slots_needed
= newentries
->acl_cnt
- slots_left
;
576 newsize
= (acl1
->acl_entry_size
* acl1
->acl_cnt
) +
577 (acl1
->acl_entry_size
* slots_needed
);
578 acl1
->acl_aclp
= realloc(acl1
->acl_aclp
, newsize
);
579 if (acl1
->acl_aclp
== NULL
)
582 (void) memcpy((char *)acl1
->acl_aclp
+ (acl1
->acl_entry_size
* slot
),
583 newentries
->acl_aclp
,
584 newentries
->acl_entry_size
* newentries
->acl_cnt
);
590 if ((slot
+ newentries
->acl_cnt
) > acl1
->acl_cnt
) {
591 acl1
->acl_cnt
= slot
+ newentries
->acl_cnt
;
598 * Add acl2 entries into acl1. The where argument specifies where
599 * to add the entries.
602 acl_addentries(acl_t
*acl1
, acl_t
*acl2
, int where
)
610 if (acl1
== NULL
|| acl2
== NULL
)
611 return (EACL_NO_ACL_ENTRY
);
613 if (acl1
->acl_type
!= acl2
->acl_type
)
614 return (EACL_DIFF_TYPE
);
617 * allow where to specify 1 past last slot for an append operation
618 * but anything greater is an error.
620 if (where
< 0 || where
> acl1
->acl_cnt
)
621 return (EACL_INVALID_SLOT
);
623 newsize
= (acl2
->acl_entry_size
* acl2
->acl_cnt
) +
624 (acl1
->acl_entry_size
* acl1
->acl_cnt
);
625 acl1
->acl_aclp
= realloc(acl1
->acl_aclp
, newsize
);
626 if (acl1
->acl_aclp
== NULL
)
630 * first push down entries where new ones will be inserted
633 to
= (void *)((char *)acl1
->acl_aclp
+
634 ((where
+ acl2
->acl_cnt
) * acl1
->acl_entry_size
));
636 start
= (void *)((char *)acl1
->acl_aclp
+
637 where
* acl1
->acl_entry_size
);
639 if (where
< acl1
->acl_cnt
) {
640 len
= (acl1
->acl_cnt
- where
) * acl1
->acl_entry_size
;
641 (void) memmove(to
, start
, len
);
645 * now stick in new entries.
648 (void) memmove(start
, acl2
->acl_aclp
,
649 acl2
->acl_cnt
* acl2
->acl_entry_size
);
651 acl1
->acl_cnt
+= acl2
->acl_cnt
;
656 * return text for an ACL error.
659 acl_strerror(int errnum
)
663 return (dgettext(TEXT_DOMAIN
,
664 "There is more than one group or default group entry"));
665 case EACL_USER_ERROR
:
666 return (dgettext(TEXT_DOMAIN
,
667 "There is more than one user or default user entry"));
668 case EACL_OTHER_ERROR
:
669 return (dgettext(TEXT_DOMAIN
,
670 "There is more than one other entry"));
671 case EACL_CLASS_ERROR
:
672 return (dgettext(TEXT_DOMAIN
,
673 "There is more than one mask entry"));
674 case EACL_DUPLICATE_ERROR
:
675 return (dgettext(TEXT_DOMAIN
,
676 "Duplicate user or group entries"));
677 case EACL_MISS_ERROR
:
678 return (dgettext(TEXT_DOMAIN
,
679 "Missing user/group owner, other, mask entry"));
681 return (dgettext(TEXT_DOMAIN
,
683 case EACL_ENTRY_ERROR
:
684 return (dgettext(TEXT_DOMAIN
,
685 "Unrecognized entry type"));
686 case EACL_INHERIT_ERROR
:
687 return (dgettext(TEXT_DOMAIN
,
688 "Invalid inheritance flags"));
689 case EACL_FLAGS_ERROR
:
690 return (dgettext(TEXT_DOMAIN
,
691 "Unrecognized entry flags"));
692 case EACL_PERM_MASK_ERROR
:
693 return (dgettext(TEXT_DOMAIN
,
694 "Invalid ACL permissions"));
695 case EACL_COUNT_ERROR
:
696 return (dgettext(TEXT_DOMAIN
,
697 "Invalid ACL count"));
698 case EACL_INVALID_SLOT
:
699 return (dgettext(TEXT_DOMAIN
,
700 "Invalid ACL entry number specified"));
701 case EACL_NO_ACL_ENTRY
:
702 return (dgettext(TEXT_DOMAIN
,
703 "ACL entry doesn't exist"));
705 return (dgettext(TEXT_DOMAIN
,
706 "Different file system ACL types cannot be merged"));
707 case EACL_INVALID_USER_GROUP
:
708 return (dgettext(TEXT_DOMAIN
, "Invalid user or group"));
709 case EACL_INVALID_STR
:
710 return (dgettext(TEXT_DOMAIN
, "ACL string is invalid"));
711 case EACL_FIELD_NOT_BLANK
:
712 return (dgettext(TEXT_DOMAIN
, "Field expected to be blank"));
713 case EACL_INVALID_ACCESS_TYPE
:
714 return (dgettext(TEXT_DOMAIN
, "Invalid access type"));
715 case EACL_UNKNOWN_DATA
:
716 return (dgettext(TEXT_DOMAIN
, "Unrecognized entry"));
717 case EACL_MISSING_FIELDS
:
718 return (dgettext(TEXT_DOMAIN
,
719 "ACL specification missing required fields"));
720 case EACL_INHERIT_NOTDIR
:
721 return (dgettext(TEXT_DOMAIN
,
722 "Inheritance flags are only allowed on directories"));
724 return (strerror(errno
));
727 return (dgettext(TEXT_DOMAIN
, "Unknown error"));
731 extern int yyinteractive
;
735 acl_error(const char *fmt
, ...)
739 if (yyinteractive
== 0)
743 (void) vfprintf(stderr
, fmt
, va
);
748 sid_to_id(char *sid
, boolean_t user
, uid_t
*id
)
750 idmap_get_handle_t
*get_hdl
= NULL
;
751 char *rid_start
= NULL
;
757 if ((domain_start
= strchr(sid
, '@')) == NULL
) {
760 if ((rid_start
= strrchr(sid
, '-')) == NULL
)
764 rid
= strtoul(rid_start
--, &end
, 10);
765 if (errno
== 0 && *end
== '\0') {
766 if (idmap_get_create(&get_hdl
) ==
769 error
= idmap_get_uidbysid(get_hdl
,
770 sid
, rid
, IDMAP_REQ_FLG_USE_CACHE
,
773 error
= idmap_get_gidbysid(get_hdl
,
774 sid
, rid
, IDMAP_REQ_FLG_USE_CACHE
,
776 if (error
== IDMAP_SUCCESS
) {
777 error
= idmap_get_mappings(get_hdl
);
778 if (error
== IDMAP_SUCCESS
&&
779 status
!= IDMAP_SUCCESS
)
788 idmap_get_destroy(get_hdl
);
792 *rid_start
= '-'; /* putback character removed earlier */
795 *domain_start
++ = '\0';
798 error
= idmap_getuidbywinname(name
, domain_start
,
799 IDMAP_REQ_FLG_USE_CACHE
, id
);
801 error
= idmap_getgidbywinname(name
, domain_start
,
802 IDMAP_REQ_FLG_USE_CACHE
, id
);
803 *--domain_start
= '@';
804 error
= (error
== IDMAP_SUCCESS
) ? 0 : 1;