1 /* sec/posixacl.cc: POSIX ACL functions based on Solaris ACLs.
3 This file is part of Cygwin.
5 This software is a copyrighted work licensed under the terms of the
6 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
17 #include "sec_posixacl.h"
18 #include <acl/libacl.h>
20 #define _ENTRY_SIZE(_cnt) ((_cnt) * sizeof (aclent_t))
21 #define _ACL_SIZE(_cnt) (sizeof (__acl_ext_t) + _ENTRY_SIZE (_cnt))
22 #define ACL_SIZE(_acl) ({ acl_t __acl = _acl; \
23 _ACL_SIZE((__acl)->count - (__acl)->deleted); \
25 #define ACL_PERM_MASK (ACL_READ | ACL_WRITE | ACL_EXECUTE)
32 if (count
< 0 || count
> UINT16_MAX
)
37 acl
= (acl_t
) calloc (1, sizeof (__acl_t
));
40 acl
->entry
= (aclent_t
*) calloc (count
, sizeof (aclent_t
));
46 acl
->magic
= ACL_MAGIC
;
47 acl
->max_count
= count
;
56 acl_t new_acl
= acl_init (acl
->max_count
);
61 for (uint16_t idx
= 0; idx
< acl
->count
; ++idx
)
62 if (acl
->entry
[idx
].a_type
!= ACL_DELETED_TAG
)
63 new_acl
->entry
[new_idx
++] = acl
->entry
[idx
];
64 new_acl
->magic
= ACL_MAGIC
;
65 new_acl
->count
= new_idx
;
75 acl_free (void *obj_p
)
83 if (malloc_usable_size (obj_p
) >= sizeof (__acl_t
))
86 if (acl
->magic
== ACL_MAGIC
)
100 acl_valid (acl_t acl
)
104 if (!(__aclcheck (acl
->entry
, acl
->count
, NULL
, true)))
114 acl_copy_entry (acl_entry_t dest_d
, acl_entry_t src_d
)
118 uint16_t d_idx
, s_idx
;
121 d_acl
= __from_entry (dest_d
, d_idx
);
122 s_acl
= __from_entry (src_d
, s_idx
);
125 d_acl
->entry
[d_idx
] = s_acl
->entry
[s_idx
];
136 acl_create_entry (acl_t
*acl_p
, acl_entry_t
*entry_p
)
143 if (acl
->deleted
> 0)
145 for (idx
= 0; idx
< acl
->count
; ++idx
)
146 if (acl
->entry
[idx
].a_type
== ACL_DELETED_TAG
)
148 *entry_p
= __to_entry (acl
, idx
);
153 if (acl
->count
>= acl
->max_count
)
157 new_e
= (aclent_t
*) realloc (acl
->entry
,
158 _ENTRY_SIZE (acl
->max_count
+ 1));
165 *entry_p
= __to_entry (acl
, idx
);
167 acl
->entry
[idx
].a_type
= ACL_UNDEFINED_TAG
;
168 acl
->entry
[idx
].a_id
= ACL_UNDEFINED_ID
;
169 acl
->entry
[idx
].a_perm
= 0;
178 acl_delete_entry (acl_t acl
, acl_entry_t entry_d
)
185 acl_p
= __from_entry (entry_d
, idx
);
189 acl_p
->entry
[idx
].a_type
= ACL_DELETED_TAG
;
190 acl_p
->entry
[idx
].a_id
= ACL_UNDEFINED_ID
;
191 acl_p
->entry
[idx
].a_perm
= 0;
202 acl_get_entry (acl_t acl
, int entry_id
, acl_entry_t
*entry_p
)
208 if (entry_id
== ACL_FIRST_ENTRY
)
210 else if (entry_id
!= ACL_NEXT_ENTRY
)
217 if (acl
->next
>= acl
->count
)
221 while (acl
->entry
[idx
].a_type
== ACL_DELETED_TAG
);
222 *entry_p
= __to_entry (acl
, idx
);
231 acl_calc_mask (acl_t
*acl_p
)
238 mask
= __aclcalcmask (acl
->entry
, acl
->count
);
239 /* If __aclcalcmask returns -1 we're done. Otherwise create a
241 if (mask
!= (acl_perm_t
) -1)
246 if (acl_create_entry (&acl
, &entry_d
) < 0)
248 if (!__from_entry (entry_d
, mask_idx
))
253 acl
->entry
[mask_idx
].a_type
= ACL_MASK
;
254 acl
->entry
[mask_idx
].a_id
= ACL_UNDEFINED_ID
;
255 acl
->entry
[mask_idx
].a_perm
= mask
;
266 acl_clear_perms (acl_permset_t permset_d
)
273 acl
= __from_permset (permset_d
, idx
);
276 acl
->entry
[idx
].a_perm
= 0;
287 acl_add_perm (acl_permset_t permset_d
, acl_perm_t perm
)
294 acl
= __from_permset (permset_d
, idx
);
295 if (acl
&& !(perm
& ~ACL_PERM_MASK
))
297 acl
->entry
[idx
].a_perm
|= perm
;
308 acl_delete_perm (acl_permset_t permset_d
, acl_perm_t perm
)
315 acl
= __from_permset (permset_d
, idx
);
316 if (acl
&& !(perm
& ~ACL_PERM_MASK
))
318 acl
->entry
[idx
].a_perm
&= ~perm
;
329 acl_get_permset (acl_entry_t entry_d
, acl_permset_t
*permset_p
)
336 acl
= __from_entry (entry_d
, idx
);
339 *permset_p
= (acl_permset_t
) entry_d
;
350 acl_set_permset (acl_entry_t entry_d
, acl_permset_t permset_d
)
355 uint16_t idx_e
, idx_p
;
357 acl_e
= __from_entry (entry_d
, idx_e
);
358 acl_p
= __from_permset (permset_d
, idx_p
);
359 if (acl_e
&& acl_p
&& !(acl_p
->entry
[idx_p
].a_perm
& ~ACL_PERM_MASK
))
361 acl_e
->entry
[idx_e
].a_perm
= acl_p
->entry
[idx_p
].a_perm
;
372 acl_get_qualifier (acl_entry_t entry_d
)
379 acl
= __from_entry (entry_d
, idx
);
380 if (acl
&& (acl
->entry
[idx
].a_type
& (ACL_USER
| ACL_GROUP
)))
382 id_t
*id
= (id_t
*) malloc (sizeof (id_t
));
385 *id
= acl
->entry
[idx
].a_id
;
398 acl_set_qualifier (acl_entry_t entry_d
, const void *qualifier_p
)
405 acl
= __from_entry (entry_d
, idx
);
406 if (acl
&& (acl
->entry
[idx
].a_type
& (ACL_USER
| ACL_GROUP
)))
408 acl
->entry
[idx
].a_id
= *(id_t
*) qualifier_p
;
419 acl_get_tag_type (acl_entry_t entry_d
, acl_tag_t
*tag_type_p
)
426 acl
= __from_entry (entry_d
, idx
);
429 *tag_type_p
= acl
->entry
[idx
].a_type
;
440 acl_set_tag_type (acl_entry_t entry_d
, acl_tag_t tag_type
)
447 acl
= __from_entry (entry_d
, idx
);
455 acl
->entry
[idx
].a_id
= ACL_UNDEFINED_ID
;
459 acl
->entry
[idx
].a_type
= tag_type
;
476 return (ssize_t
) ACL_SIZE (acl
);
484 acl_copy_ext (void *buf_p
, acl_t acl
, ssize_t size
)
488 ssize_t ext_size
= (ssize_t
) ACL_SIZE (acl
);
492 else if (ext_size
> size
)
496 uint16_t ext_idx
= 0;
497 __acl_ext_t
*acl_ext
= (__acl_ext_t
*) buf_p
;
499 acl_ext
->count
= acl
->count
- acl
->deleted
;
500 for (uint16_t idx
= 0; idx
< acl
->count
; ++idx
)
501 if (acl
->entry
[idx
].a_type
!= ACL_DELETED_TAG
)
502 acl_ext
->entry
[ext_idx
++] = acl
->entry
[idx
];
512 acl_copy_int (const void *buf_p
)
517 __acl_ext_t
*acl_ext
= (__acl_ext_t
*) buf_p
;
519 acl
= acl_init (acl_ext
->count
);
522 memcpy (acl
->entry
, acl_ext
->entry
, _ENTRY_SIZE (acl_ext
->count
));
523 acl
->count
= acl_ext
->count
;
533 acl_from_text (const char *buf_p
)
537 return (acl_t
) __aclfromtext (buf_p
, NULL
, true);
545 acl_to_text (acl_t acl
, ssize_t
*len_p
)
549 char *ret
= __acltotext (acl
->entry
, acl
->count
, NULL
, '\n',
551 | TEXT_SOME_EFFECTIVE
552 | TEXT_END_SEPARATOR
);
554 *len_p
= strlen (ret
);
563 fhandler_base::acl_get (acl_type_t type
)
570 fhandler_disk_file::acl_get (acl_type_t type
)
579 uint16_t cnt
, access_cnt
;
586 if (type
== ACL_TYPE_DEFAULT
&& !pc
.isdir ())
591 aclbufp
= (aclent_t
*) tp
.c_get ();
594 query_open (query_read_control
);
595 if (!(oret
= open (O_BINARY
, 0)))
598 cnt
= facl (GETACL
, MAX_ACL_ENTRIES
, aclbufp
);
601 /* Set access_cnt to number of non-default entries from file ACL. */
605 for (access_cnt
= 0; access_cnt
< cnt
; ++access_cnt
)
606 if (aclbufp
[access_cnt
].a_type
& ACL_DEFAULT
)
608 if (type
== ACL_TYPE_ACCESS
)
610 acl
= acl_init (access_cnt
);
613 memcpy (acl
->entry
, aclbufp
, _ENTRY_SIZE (access_cnt
));
614 acl
->count
= access_cnt
;
619 acl
= acl_init (cnt
);
622 memcpy (acl
->entry
, aclbufp
+ access_cnt
, _ENTRY_SIZE (cnt
));
624 for (cnt
= 0; cnt
< acl
->count
; ++cnt
)
625 acl
->entry
[cnt
].a_type
&= ~ACL_DEFAULT
;
637 fhandler_socket_local::acl_get (acl_type_t type
)
640 /* acl_get_fd on a socket. */
641 return fhandler_base::acl_get (type
);
643 /* acl_get_fd on a socket opened with O_PATH or acl_get_file on a
645 if (get_flags () & O_PATH
)
650 fhandler_disk_file
fh (pc
);
651 return fh
.acl_get (type
);
654 #ifdef __WITH_AF_UNIX
656 fhandler_socket_unix::acl_get (acl_type_t type
)
659 /* acl_get_fd on a socket. */
660 return fhandler_base::acl_get (type
);
662 /* acl_get_fd on a socket opened with O_PATH or acl_get_file on a
664 if (get_flags () & O_PATH
)
669 fhandler_disk_file
fh (pc
);
670 return fh
.acl_get (type
);
677 cygheap_fdget
cfd (fd
);
680 return cfd
->acl_get (ACL_TYPE_ACCESS
);
684 acl_get_file (const char *path_p
, acl_type_t type
)
686 if (type
!= ACL_TYPE_ACCESS
&& type
!= ACL_TYPE_DEFAULT
)
692 if (!(fh
= build_fh_name (path_p
, PC_SYM_FOLLOW
, stat_suffixes
)))
696 set_errno (fh
->error ());
699 acl_t acl
= fh
->acl_get (type
);
705 fhandler_base::acl_set (acl_t acl
, acl_type_t type
)
712 fhandler_disk_file::acl_set (acl_t acl
, acl_type_t type
)
720 aclent_t
*aclbufp
, *aclbuf_from_file
;
721 uint16_t cnt
, cnt_from_file
, access_cnt
;
728 if (type
== ACL_TYPE_DEFAULT
&& !pc
.isdir ())
733 if (acl
->count
> MAX_ACL_ENTRIES
)
738 aclbuf_from_file
= (aclent_t
*) tp
.c_get ();
741 query_open (query_write_dac
);
742 if (!(oret
= open (O_BINARY
, 0)))
745 cnt_from_file
= facl (GETACL
, MAX_ACL_ENTRIES
, aclbuf_from_file
);
746 if (cnt_from_file
< 0)
748 aclbufp
= (aclent_t
*) tp
.c_get ();
749 /* Set access_cnt to number of non-default entries from file ACL. */
751 access_cnt
= cnt_from_file
;
753 for (access_cnt
= 0; access_cnt
< cnt_from_file
; ++access_cnt
)
754 if (aclbuf_from_file
[access_cnt
].a_type
& ACL_DEFAULT
)
756 if (type
== ACL_TYPE_ACCESS
)
758 /* Check if the number of ACEs fits into the buffer. */
759 if (acl
->count
- acl
->deleted
+ cnt_from_file
- access_cnt
765 /* Copy the new ACL entries. */
767 for (uint16_t idx
= 0; idx
< acl
->count
; ++idx
)
768 if (acl
->entry
[idx
].a_type
!= ACL_DELETED_TAG
)
769 aclbufp
[cnt
++] = acl
->entry
[idx
];
770 /* Append default ACL from file, if any. */
771 if (access_cnt
< cnt_from_file
)
773 memcpy (aclbufp
+ cnt
, aclbuf_from_file
+ access_cnt
,
774 _ENTRY_SIZE (cnt_from_file
- access_cnt
));
775 cnt
+= cnt_from_file
- access_cnt
;
780 /* Check if the number of ACEs fits into the buffer. */
781 if (acl
->count
- acl
->deleted
+ access_cnt
> MAX_ACL_ENTRIES
)
786 /* Copy non-default entries from file. */
787 memcpy (aclbufp
, aclbuf_from_file
, _ENTRY_SIZE (access_cnt
));
789 /* Append new default ACL entries (and add ACL_DEFAULT flag). */
790 for (uint16_t idx
= 0; idx
< acl
->count
; ++idx
)
791 if (acl
->entry
[idx
].a_type
!= ACL_DELETED_TAG
)
793 aclbufp
[cnt
] = acl
->entry
[idx
];
794 aclbufp
[cnt
++].a_type
|= ACL_DEFAULT
;
797 ret
= facl (SETACL
, cnt
, aclbufp
);
807 fhandler_socket_local::acl_set (acl_t acl
, acl_type_t type
)
810 /* acl_set_fd on a socket. */
811 return fhandler_base::acl_set (acl
, type
);
813 /* acl_set_fd on a socket opened with O_PATH or acl_set_file on a
815 if (get_flags () & O_PATH
)
820 fhandler_disk_file
fh (pc
);
821 return fh
.acl_set (acl
, type
);
824 #ifdef __WITH_AF_UNIX
826 fhandler_socket_unix::acl_set (acl_t acl
, acl_type_t type
)
829 /* acl_set_fd on a socket. */
830 return fhandler_base::acl_set (acl
, type
);
832 /* acl_set_fd on a socket opened with O_PATH or acl_set_file on a
834 if (get_flags () & O_PATH
)
839 fhandler_disk_file
fh (pc
);
840 return fh
.acl_set (acl
, type
);
845 acl_set_fd (int fd
, acl_t acl
)
847 cygheap_fdget
cfd (fd
);
850 return cfd
->acl_set (acl
, ACL_TYPE_ACCESS
);
854 acl_set_file(const char *path_p
, acl_type_t type
, acl_t acl
)
856 if (type
!= ACL_TYPE_ACCESS
&& type
!= ACL_TYPE_DEFAULT
)
862 if (!(fh
= build_fh_name (path_p
, PC_SYM_FOLLOW
, stat_suffixes
)))
866 set_errno (fh
->error ());
869 int ret
= fh
->acl_set (acl
, type
);
875 acl_delete_def_file (const char *path_p
)
877 acl_t acl
= (acl_t
) alloca (sizeof (struct __acl_t
));
878 acl
->count
= acl
->max_count
= acl
->next
= 0;
881 return acl_set_file(path_p
, ACL_TYPE_DEFAULT
, acl
);
884 /* libacl extensions */
887 acl_check (acl_t acl
, int *last
)
896 ret
= __aclcheck (acl
->entry
, acl
->count
, last
, true);
903 ret
= ACL_MULTI_ERROR
;
917 acl_cmp (acl_t acl1
, acl_t acl2
)
925 __acl_ext_t
*acl1d
= (__acl_ext_t
*) tp
.c_get ();
926 __acl_ext_t
*acl2d
= (__acl_ext_t
*) tp
.c_get ();
927 if (acl_copy_ext (acl1d
, acl1
, NT_MAX_PATH
) < 0)
929 if (acl_copy_ext (acl2d
, acl2
, NT_MAX_PATH
) < 0)
931 if (acl1d
->count
!= acl2d
->count
)
933 if (__aclsort (acl1d
->count
, acl1d
->entry
))
935 if (__aclsort (acl2d
->count
, acl2d
->entry
))
937 for (int idx
= 0; idx
< acl1d
->count
; ++idx
)
939 if (acl1d
->entry
[idx
].a_type
!= acl2d
->entry
[idx
].a_type
)
944 if ((acl1d
->entry
[idx
].a_perm
& ACL_PERM_MASK
)
945 != (acl2d
->entry
[idx
].a_perm
& ACL_PERM_MASK
))
950 if ((acl1d
->entry
[idx
].a_type
& (ACL_USER
| ACL_GROUP
))
951 && acl1d
->entry
[idx
].a_id
!= acl2d
->entry
[idx
].a_id
)
965 acl_entries (acl_t acl
)
969 return acl
->count
- acl
->deleted
;
977 acl_equiv_mode (acl_t acl
, mode_t
*mode_p
)
986 int u_idx
= -1, g_idx
= -1, o_idx
= -1;
987 for (int idx
= 0; idx
< 3; ++idx
)
988 switch (acl
->entry
[idx
].a_type
)
1000 if (u_idx
== -1 || g_idx
== -1 || o_idx
== -1)
1006 *mode_p
= ((acl
->entry
[u_idx
].a_perm
& ACL_PERM_MASK
) << 6)
1007 | ((acl
->entry
[g_idx
].a_perm
& ACL_PERM_MASK
) << 3)
1008 | (acl
->entry
[o_idx
].a_perm
& ACL_PERM_MASK
);
1011 __except (EINVAL
) {}
1016 static const char *acl_err_txt
[] =
1019 "Duplicate entries",
1020 "Invalid entry type",
1021 "Missing or wrong entry"
1024 extern "C" const char *
1025 acl_error (int code
)
1027 if (code
< ACL_MULTI_ERROR
|| code
> ACL_MISS_ERROR
)
1029 return acl_err_txt
[code
- ACL_MULTI_ERROR
];
1033 __acl_extended_fh (fhandler_base
*fh
)
1037 if (!fh
->pc
.has_acls ())
1038 set_errno (ENOTSUP
);
1041 ret
= fh
->facl (GETACLCNT
, 0, NULL
);
1043 ret
= (ret
> MIN_ACL_ENTRIES
) ? 1 : 0;
1049 acl_extended_fd (int fd
)
1053 cygheap_fdget
cfd (fd
);
1056 return __acl_extended_fh (cfd
);
1064 __acl_extended_file (path_conv
&pc
)
1071 set_errno (pc
.error
);
1072 else if (!pc
.exists ())
1078 if (!(fh
= build_fh_pc (pc
)))
1080 ret
= __acl_extended_fh (fh
);
1084 __except (EFAULT
) {}
1090 acl_extended_file (const char *path_p
)
1092 path_conv
pc (path_p
, PC_SYM_FOLLOW
| PC_POSIX
| PC_KEEP_HANDLE
,
1094 return __acl_extended_file (pc
);
1098 acl_extended_file_nofollow (const char *path_p
)
1100 path_conv
pc (path_p
, PC_SYM_NOFOLLOW
| PC_POSIX
| PC_KEEP_HANDLE
,
1102 return __acl_extended_file (pc
);
1106 acl_from_mode (mode_t mode
)
1108 acl_t acl
= acl_init (MIN_ACL_ENTRIES
);
1112 acl
->entry
[0].a_type
= USER_OBJ
;
1113 acl
->entry
[0].a_id
= ACL_UNDEFINED_ID
;
1114 acl
->entry
[0].a_perm
= (mode
>> 6) & ACL_PERM_MASK
;
1115 acl
->entry
[1].a_type
= GROUP_OBJ
;
1116 acl
->entry
[1].a_id
= ACL_UNDEFINED_ID
;
1117 acl
->entry
[1].a_perm
= (mode
>> 3) & ACL_PERM_MASK
;
1118 acl
->entry
[2].a_type
= OTHER_OBJ
;
1119 acl
->entry
[2].a_id
= ACL_UNDEFINED_ID
;
1120 acl
->entry
[2].a_perm
= mode
& ACL_PERM_MASK
;
1125 acl_get_perm (acl_permset_t permset_d
, acl_perm_t perm
)
1132 acl
= __from_permset (permset_d
, idx
);
1133 if (acl
&& !(perm
& ~ACL_PERM_MASK
))
1134 return (~acl
->entry
[idx
].a_perm
& perm
) ? 0 : 1;
1137 __except (EINVAL
) {}
1143 acl_to_any_text (acl_t acl
, const char *prefix
, char separator
, int options
)
1147 return __acltotext (acl
->entry
, acl
->count
, prefix
, separator
,
1148 TEXT_IS_POSIX
| options
);
1150 __except (EINVAL
) {}