1 /*
2 * CDDL HEADER START
3 *
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.
7 *
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.
12 *
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]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 1993, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright 2016 Nexenta Systems, Inc. All rights reserved.
24 * Copyright 2024 RackTop Systems, Inc.
25 */
26
27 /*LINTLIBRARY*/
28
29 #include <grp.h>
30 #include <pwd.h>
31 #include <string.h>
32 #include <limits.h>
33 #include <stdlib.h>
34 #include <errno.h>
35 #include <sys/param.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <sys/acl.h>
39 #include <aclutils.h>
40 #include <idmap.h>
41 #include <synch.h>
42
43 /*
44 * Unix names returned for UID and GID values can actually be longer than
45 * the traditional 8 characters, or even 32 characters, etc. When using
46 * "ad" in nsswitch.conf they can be quite long. Let's use a reasonable
47 * compromise value here (80) which is large enough to hold SIDs in the
48 * numeric form (eg. "S-1-5-21-wwwwwwwww-xxxxxxxxx-yyyyyyyyy-zzzzzzzzz")
49 * but not necessarily every crazy long user name. In addition, places
50 * below that might truncate a long name should detect truncation and
51 * fall-back to a more compact value (SID or UID/GID et).
52 */
53 #define ID_STR_MAX 80
54
55 #define APPENDED_ID_MAX ID_STR_MAX + 1 /* id + colon */
56 /*
57 * yyinteractive controls whether yyparse should print out
58 * error messages to stderr, and whether or not id's should be
59 * allowed from acl_fromtext().
60 */
61 int yyinteractive;
62 acl_t *yyacl;
63 char *yybuf;
64 mutex_t yymutex;
65
66 extern acl_t *acl_alloc(enum acl_type);
67
68 /*
69 * dynamic string that will increase in size on an
70 * as needed basis.
71 */
72 typedef struct dynaclstr {
73 size_t d_bufsize; /* current size of aclexport */
74 char *d_aclexport;
75 int d_pos;
76 } dynaclstr_t;
77
78 static int str_append(dynaclstr_t *, char *);
79 static int aclent_perm_txt(dynaclstr_t *, o_mode_t);
80
81 static void
aclent_perms(int perm,char * txt_perms)82 aclent_perms(int perm, char *txt_perms)
83 {
84 if (perm & S_IROTH)
85 txt_perms[0] = 'r';
86 else
87 txt_perms[0] = '-';
88 if (perm & S_IWOTH)
89 txt_perms[1] = 'w';
90 else
91 txt_perms[1] = '-';
92 if (perm & S_IXOTH)
93 txt_perms[2] = 'x';
94 else
95 txt_perms[2] = '-';
96 txt_perms[3] = '\0';
97 }
98
99 /*
100 * Get the user name into the passed buffer. If it can't be found,
101 * or if the noresolve flag is set, or if what we found will not fit
102 * in the buffer without truncation, just put a numeric ID.
103 */
104 static char *
pruname(uid_t uid,char * uidp,size_t buflen,int noresolve)105 pruname(uid_t uid, char *uidp, size_t buflen, int noresolve)
106 {
107 struct passwd *passwdp = NULL;
108
109 if (noresolve == 0)
110 passwdp = getpwuid(uid);
111 if (passwdp != NULL && strlen(passwdp->pw_name) < buflen) {
112 (void) strlcpy(uidp, passwdp->pw_name, buflen);
113 } else {
114 /* display uid instead */
115 (void) snprintf(uidp, buflen, "%u", uid);
116 }
117 return (uidp);
118 }
119
120 /*
121 * Get the group name into the passed buffer. If it can't be found,
122 * or if the noresolve flag is set, or if what we found will not fit
123 * in the buffer without truncation, just put a numeric ID.
124 */
125 static char *
prgname(gid_t gid,char * gidp,size_t buflen,int noresolve)126 prgname(gid_t gid, char *gidp, size_t buflen, int noresolve)
127 {
128 struct group *groupp = NULL;
129
130 if (noresolve == 0)
131 groupp = getgrgid(gid);
132 if (groupp != NULL && strlen(groupp->gr_name) < buflen) {
133 (void) strlcpy(gidp, groupp->gr_name, buflen);
134 } else {
135 /* display gid instead */
136 (void) snprintf(gidp, buflen, "%u", gid);
137 }
138 return (gidp);
139 }
140
141 static int
getsidname(uid_t who,boolean_t user,char ** sidp,boolean_t noresolve)142 getsidname(uid_t who, boolean_t user, char **sidp, boolean_t noresolve)
143 {
144 idmap_get_handle_t *get_hdl = NULL;
145 idmap_stat status;
146 idmap_rid_t rid;
147 int error = IDMAP_ERR_NORESULT;
148 int len;
149 char *domain = NULL;
150
151 *sidp = NULL;
152
153 /*
154 * First try and get windows name
155 */
156
157 if (!noresolve) {
158 if (user)
159 error = idmap_getwinnamebyuid(who,
160 IDMAP_REQ_FLG_USE_CACHE, sidp, NULL);
161 else
162 error = idmap_getwinnamebygid(who,
163 IDMAP_REQ_FLG_USE_CACHE, sidp, NULL);
164 }
165 if (error != IDMAP_SUCCESS) {
166 if (idmap_get_create(&get_hdl) == IDMAP_SUCCESS) {
167 if (user)
168 error = idmap_get_sidbyuid(get_hdl, who,
169 IDMAP_REQ_FLG_USE_CACHE, &domain, &rid,
170 &status);
171 else
172 error = idmap_get_sidbygid(get_hdl, who,
173 IDMAP_REQ_FLG_USE_CACHE, &domain, &rid,
174 &status);
175 if (error == IDMAP_SUCCESS &&
176 idmap_get_mappings(get_hdl) == 0) {
177 if (status == IDMAP_SUCCESS) {
178 len = snprintf(NULL, 0,
179 "%s-%d", domain, rid);
180 if (*sidp = malloc(len + 1)) {
181 (void) snprintf(*sidp, len + 1,
182 "%s-%d", domain, rid);
183 }
184 }
185 }
186 }
187 if (get_hdl)
188 idmap_get_destroy(get_hdl);
189 }
190
191 free(domain);
192
193 return (*sidp ? 0 : 1);
194 }
195
196 /*
197 * sid_string_by_id() is an exposed interface via -lsec
198 */
199 int
sid_string_by_id(uid_t who,boolean_t user,char ** sidp,boolean_t noresolve)200 sid_string_by_id(uid_t who, boolean_t user, char **sidp, boolean_t noresolve)
201 {
202 return (getsidname(who, user, sidp, noresolve));
203 }
204
205 static void
aclent_printacl(acl_t * aclp)206 aclent_printacl(acl_t *aclp)
207 {
208 aclent_t *tp;
209 int aclcnt;
210 int mask;
211 int slot = 0;
212 char perm[4];
213 char uidp[ID_STR_MAX];
214 char gidp[ID_STR_MAX];
215
216 /* display ACL: assume it is sorted. */
217 aclcnt = aclp->acl_cnt;
218 for (tp = aclp->acl_aclp; tp && aclcnt--; tp++) {
219 if (tp->a_type == CLASS_OBJ)
220 mask = tp->a_perm;
221 }
222 aclcnt = aclp->acl_cnt;
223 for (tp = aclp->acl_aclp; aclcnt--; tp++) {
224 (void) printf(" %d:", slot++);
225 switch (tp->a_type) {
226 case USER:
227 aclent_perms(tp->a_perm, perm);
228 (void) printf("user:%s:%s\t\t",
229 pruname(tp->a_id, uidp, sizeof (uidp), 0), perm);
230 aclent_perms((tp->a_perm & mask), perm);
231 (void) printf("#effective:%s\n", perm);
232 break;
233 case USER_OBJ:
234 /* no need to display uid */
235 aclent_perms(tp->a_perm, perm);
236 (void) printf("user::%s\n", perm);
237 break;
238 case GROUP:
239 aclent_perms(tp->a_perm, perm);
240 (void) printf("group:%s:%s\t\t",
241 prgname(tp->a_id, gidp, sizeof (gidp), 0), perm);
242 aclent_perms(tp->a_perm & mask, perm);
243 (void) printf("#effective:%s\n", perm);
244 break;
245 case GROUP_OBJ:
246 aclent_perms(tp->a_perm, perm);
247 (void) printf("group::%s\t\t", perm);
248 aclent_perms(tp->a_perm & mask, perm);
249 (void) printf("#effective:%s\n", perm);
250 break;
251 case CLASS_OBJ:
252 aclent_perms(tp->a_perm, perm);
253 (void) printf("mask:%s\n", perm);
254 break;
255 case OTHER_OBJ:
256 aclent_perms(tp->a_perm, perm);
257 (void) printf("other:%s\n", perm);
258 break;
259 case DEF_USER:
260 aclent_perms(tp->a_perm, perm);
261 (void) printf("default:user:%s:%s\n",
262 pruname(tp->a_id, uidp, sizeof (uidp), 0), perm);
263 break;
264 case DEF_USER_OBJ:
265 aclent_perms(tp->a_perm, perm);
266 (void) printf("default:user::%s\n", perm);
267 break;
268 case DEF_GROUP:
269 aclent_perms(tp->a_perm, perm);
270 (void) printf("default:group:%s:%s\n",
271 prgname(tp->a_id, gidp, sizeof (gidp), 0), perm);
272 break;
273 case DEF_GROUP_OBJ:
274 aclent_perms(tp->a_perm, perm);
275 (void) printf("default:group::%s\n", perm);
276 break;
277 case DEF_CLASS_OBJ:
278 aclent_perms(tp->a_perm, perm);
279 (void) printf("default:mask:%s\n", perm);
280 break;
281 case DEF_OTHER_OBJ:
282 aclent_perms(tp->a_perm, perm);
283 (void) printf("default:other:%s\n", perm);
284 break;
285 default:
286 (void) fprintf(stderr,
287 dgettext(TEXT_DOMAIN, "unrecognized entry\n"));
288 break;
289 }
290 }
291 }
292
293 static void
split_line(char * str,int cols)294 split_line(char *str, int cols)
295 {
296 char *ptr;
297 int len;
298 int i;
299 int last_split;
300 char *pad = "";
301 int pad_len;
302
303 len = strlen(str);
304 ptr = str;
305 pad_len = 0;
306
307 ptr = str;
308 last_split = 0;
309 for (i = 0; i != len; i++) {
310 if ((i + pad_len + 4) >= cols) {
311 (void) printf("%s%.*s\n", pad, last_split, ptr);
312 ptr = &ptr[last_split];
313 len = strlen(ptr);
314 i = 0;
315 pad_len = 4;
316 pad = " ";
317 } else {
318 if (ptr[i] == '/' || ptr[i] == ':') {
319 last_split = i;
320 }
321 }
322 }
323 if (i == len) {
324 (void) printf("%s%s\n", pad, ptr);
325 }
326 }
327
328 /*
329 * compute entry type string, such as user:joe, group:staff,...
330 */
331 static int
aclent_type_txt(dynaclstr_t * dstr,aclent_t * aclp,int flags)332 aclent_type_txt(dynaclstr_t *dstr, aclent_t *aclp, int flags)
333 {
334 char idp[ID_STR_MAX];
335 int error;
336
337 switch (aclp->a_type) {
338 case DEF_USER_OBJ:
339 case USER_OBJ:
340 if (aclp->a_type == USER_OBJ)
341 error = str_append(dstr, "user::");
342 else
343 error = str_append(dstr, "defaultuser::");
344 break;
345
346 case DEF_USER:
347 case USER:
348 if (aclp->a_type == USER)
349 error = str_append(dstr, "user:");
350 else
351 error = str_append(dstr, "defaultuser:");
352 if (error)
353 break;
354 error = str_append(dstr, pruname(aclp->a_id, idp,
355 sizeof (idp), flags & ACL_NORESOLVE));
356 if (error == 0)
357 error = str_append(dstr, ":");
358 break;
359
360 case DEF_GROUP_OBJ:
361 case GROUP_OBJ:
362 if (aclp->a_type == GROUP_OBJ)
363 error = str_append(dstr, "group::");
364 else
365 error = str_append(dstr, "defaultgroup::");
366 break;
367
368 case DEF_GROUP:
369 case GROUP:
370 if (aclp->a_type == GROUP)
371 error = str_append(dstr, "group:");
372 else
373 error = str_append(dstr, "defaultgroup:");
374 if (error)
375 break;
376 error = str_append(dstr, prgname(aclp->a_id, idp,
377 sizeof (idp), flags & ACL_NORESOLVE));
378 if (error == 0)
379 error = str_append(dstr, ":");
380 break;
381
382 case DEF_CLASS_OBJ:
383 case CLASS_OBJ:
384 if (aclp->a_type == CLASS_OBJ)
385 error = str_append(dstr, "mask:");
386 else
387 error = str_append(dstr, "defaultmask:");
388 break;
389
390 case DEF_OTHER_OBJ:
391 case OTHER_OBJ:
392 if (aclp->a_type == OTHER_OBJ)
393 error = str_append(dstr, "other:");
394 else
395 error = str_append(dstr, "defaultother:");
396 break;
397
398 default:
399 error = 1;
400 break;
401 }
402
403 return (error);
404 }
405
406 /*
407 * compute entry type string such as, owner@:, user:joe, group:staff,...
408 */
409 static int
ace_type_txt(dynaclstr_t * dynstr,ace_t * acep,int flags)410 ace_type_txt(dynaclstr_t *dynstr, ace_t *acep, int flags)
411 {
412 char idp[ID_STR_MAX];
413 int error;
414 char *sidp = NULL;
415
416 switch (acep->a_flags & ACE_TYPE_FLAGS) {
417 case ACE_OWNER:
418 error = str_append(dynstr, OWNERAT_TXT);
419 break;
420
421 case ACE_GROUP|ACE_IDENTIFIER_GROUP:
422 error = str_append(dynstr, GROUPAT_TXT);
423 break;
424
425 case ACE_IDENTIFIER_GROUP:
426 if ((flags & ACL_SID_FMT) && acep->a_who > MAXUID) {
427 if (error = str_append(dynstr,
428 GROUPSID_TXT))
429 break;
430 if (error = getsidname(acep->a_who, B_FALSE,
431 &sidp, flags & ACL_NORESOLVE))
432 break;
433 error = str_append(dynstr, sidp);
434 } else {
435 if (error = str_append(dynstr, GROUP_TXT))
436 break;
437 error = str_append(dynstr, prgname(acep->a_who, idp,
438 sizeof (idp), flags & ACL_NORESOLVE));
439 }
440 if (error == 0)
441 error = str_append(dynstr, ":");
442 break;
443
444 case ACE_EVERYONE:
445 error = str_append(dynstr, EVERYONEAT_TXT);
446 break;
447
448 case 0:
449 if ((flags & ACL_SID_FMT) && acep->a_who > MAXUID) {
450 if (error = str_append(dynstr, USERSID_TXT))
451 break;
452 if (error = getsidname(acep->a_who, B_TRUE,
453 &sidp, flags & ACL_NORESOLVE))
454 break;
455 error = str_append(dynstr, sidp);
456 } else {
457 if (error = str_append(dynstr, USER_TXT))
458 break;
459 error = str_append(dynstr, pruname(acep->a_who, idp,
460 sizeof (idp), flags & ACL_NORESOLVE));
461 }
462 if (error == 0)
463 error = str_append(dynstr, ":");
464 break;
465 default:
466 error = 0;
467 break;
468 }
469
470 if (sidp)
471 free(sidp);
472 return (error);
473 }
474
475 /*
476 * compute string of permissions, such as read_data/write_data or
477 * rwxp,...
478 * The format depends on the flags field which indicates whether the compact
479 * or verbose format should be used.
480 */
481 static int
ace_perm_txt(dynaclstr_t * dstr,uint32_t mask,uint32_t iflags,int isdir,int flags)482 ace_perm_txt(dynaclstr_t *dstr, uint32_t mask,
483 uint32_t iflags, int isdir, int flags)
484 {
485 int error = 0;
486
487 if (flags & ACL_COMPACT_FMT) {
488 char buf[16];
489
490 if (mask & ACE_READ_DATA)
491 buf[0] = 'r';
492 else
493 buf[0] = '-';
494 if (mask & ACE_WRITE_DATA)
495 buf[1] = 'w';
496 else
497 buf[1] = '-';
498 if (mask & ACE_EXECUTE)
499 buf[2] = 'x';
500 else
501 buf[2] = '-';
502 if (mask & ACE_APPEND_DATA)
503 buf[3] = 'p';
504 else
505 buf[3] = '-';
506 if (mask & ACE_DELETE)
507 buf[4] = 'd';
508 else
509 buf[4] = '-';
510 if (mask & ACE_DELETE_CHILD)
511 buf[5] = 'D';
512 else
513 buf[5] = '-';
514 if (mask & ACE_READ_ATTRIBUTES)
515 buf[6] = 'a';
516 else
517 buf[6] = '-';
518 if (mask & ACE_WRITE_ATTRIBUTES)
519 buf[7] = 'A';
520 else
521 buf[7] = '-';
522 if (mask & ACE_READ_NAMED_ATTRS)
523 buf[8] = 'R';
524 else
525 buf[8] = '-';
526 if (mask & ACE_WRITE_NAMED_ATTRS)
527 buf[9] = 'W';
528 else
529 buf[9] = '-';
530 if (mask & ACE_READ_ACL)
531 buf[10] = 'c';
532 else
533 buf[10] = '-';
534 if (mask & ACE_WRITE_ACL)
535 buf[11] = 'C';
536 else
537 buf[11] = '-';
538 if (mask & ACE_WRITE_OWNER)
539 buf[12] = 'o';
540 else
541 buf[12] = '-';
542 if (mask & ACE_SYNCHRONIZE)
543 buf[13] = 's';
544 else
545 buf[13] = '-';
546 buf[14] = ':';
547 buf[15] = '\0';
548 error = str_append(dstr, buf);
549 } else {
550 /*
551 * If ACE is a directory, but inheritance indicates its
552 * for a file then print permissions for file rather than
553 * dir.
554 */
555 if (isdir) {
556 if (mask & ACE_LIST_DIRECTORY) {
557 if (iflags == ACE_FILE_INHERIT_ACE) {
558 error = str_append(dstr,
559 READ_DATA_TXT);
560 } else {
561 error =
562 str_append(dstr, READ_DIR_TXT);
563 }
564 }
565 if (error == 0 && (mask & ACE_ADD_FILE)) {
566 if (iflags == ACE_FILE_INHERIT_ACE) {
567 error =
568 str_append(dstr, WRITE_DATA_TXT);
569 } else {
570 error =
571 str_append(dstr, ADD_FILE_TXT);
572 }
573 }
574 if (error == 0 && (mask & ACE_ADD_SUBDIRECTORY)) {
575 if (iflags == ACE_FILE_INHERIT_ACE) {
576 error = str_append(dstr,
577 APPEND_DATA_TXT);
578 } else {
579 error = str_append(dstr,
580 ADD_DIR_TXT);
581 }
582 }
583 } else {
584 if (mask & ACE_READ_DATA) {
585 error = str_append(dstr, READ_DATA_TXT);
586 }
587 if (error == 0 && (mask & ACE_WRITE_DATA)) {
588 error = str_append(dstr, WRITE_DATA_TXT);
589 }
590 if (error == 0 && (mask & ACE_APPEND_DATA)) {
591 error = str_append(dstr, APPEND_DATA_TXT);
592 }
593 }
594 if (error == 0 && (mask & ACE_READ_NAMED_ATTRS)) {
595 error = str_append(dstr, READ_XATTR_TXT);
596 }
597 if (error == 0 && (mask & ACE_WRITE_NAMED_ATTRS)) {
598 error = str_append(dstr, WRITE_XATTR_TXT);
599 }
600 if (error == 0 && (mask & ACE_EXECUTE)) {
601 error = str_append(dstr, EXECUTE_TXT);
602 }
603 if (error == 0 && (mask & ACE_DELETE_CHILD)) {
604 error = str_append(dstr, DELETE_CHILD_TXT);
605 }
606 if (error == 0 && (mask & ACE_READ_ATTRIBUTES)) {
607 error = str_append(dstr, READ_ATTRIBUTES_TXT);
608 }
609 if (error == 0 && (mask & ACE_WRITE_ATTRIBUTES)) {
610 error = str_append(dstr, WRITE_ATTRIBUTES_TXT);
611 }
612 if (error == 0 && (mask & ACE_DELETE)) {
613 error = str_append(dstr, DELETE_TXT);
614 }
615 if (error == 0 && (mask & ACE_READ_ACL)) {
616 error = str_append(dstr, READ_ACL_TXT);
617 }
618 if (error == 0 && (mask & ACE_WRITE_ACL)) {
619 error = str_append(dstr, WRITE_ACL_TXT);
620 }
621 if (error == 0 && (mask & ACE_WRITE_OWNER)) {
622 error = str_append(dstr, WRITE_OWNER_TXT);
623 }
624 if (error == 0 && (mask & ACE_SYNCHRONIZE)) {
625 error = str_append(dstr, SYNCHRONIZE_TXT);
626 }
627 if (error == 0 && dstr->d_aclexport[dstr->d_pos-1] == '/') {
628 dstr->d_aclexport[--dstr->d_pos] = '\0';
629 }
630 if (error == 0)
631 error = str_append(dstr, ":");
632 }
633 return (error);
634 }
635
636 /*
637 * compute string of access type, such as allow, deny, ...
638 */
639 static int
ace_access_txt(dynaclstr_t * dstr,int type)640 ace_access_txt(dynaclstr_t *dstr, int type)
641 {
642 int error;
643
644 if (type == ACE_ACCESS_ALLOWED_ACE_TYPE)
645 error = str_append(dstr, ALLOW_TXT);
646 else if (type == ACE_ACCESS_DENIED_ACE_TYPE)
647 error = str_append(dstr, DENY_TXT);
648 else if (type == ACE_SYSTEM_AUDIT_ACE_TYPE)
649 error = str_append(dstr, AUDIT_TXT);
650 else if (type == ACE_SYSTEM_ALARM_ACE_TYPE)
651 error = str_append(dstr, ALARM_TXT);
652 else
653 error = str_append(dstr, UNKNOWN_TXT);
654
655 return (error);
656 }
657
658 static int
ace_inherit_txt(dynaclstr_t * dstr,uint32_t iflags,int flags)659 ace_inherit_txt(dynaclstr_t *dstr, uint32_t iflags, int flags)
660 {
661 int error = 0;
662
663 if (flags & ACL_COMPACT_FMT) {
664 char buf[9];
665
666 if (iflags & ACE_FILE_INHERIT_ACE)
667 buf[0] = 'f';
668 else
669 buf[0] = '-';
670 if (iflags & ACE_DIRECTORY_INHERIT_ACE)
671 buf[1] = 'd';
672 else
673 buf[1] = '-';
674 if (iflags & ACE_INHERIT_ONLY_ACE)
675 buf[2] = 'i';
676 else
677 buf[2] = '-';
678 if (iflags & ACE_NO_PROPAGATE_INHERIT_ACE)
679 buf[3] = 'n';
680 else
681 buf[3] = '-';
682 if (iflags & ACE_SUCCESSFUL_ACCESS_ACE_FLAG)
683 buf[4] = 'S';
684 else
685 buf[4] = '-';
686 if (iflags & ACE_FAILED_ACCESS_ACE_FLAG)
687 buf[5] = 'F';
688 else
689 buf[5] = '-';
690 if (iflags & ACE_INHERITED_ACE)
691 buf[6] = 'I';
692 else
693 buf[6] = '-';
694 buf[7] = ':';
695 buf[8] = '\0';
696 error = str_append(dstr, buf);
697 } else {
698 if (iflags & ACE_FILE_INHERIT_ACE) {
699 error = str_append(dstr, FILE_INHERIT_TXT);
700 }
701 if (error == 0 && (iflags & ACE_DIRECTORY_INHERIT_ACE)) {
702 error = str_append(dstr, DIR_INHERIT_TXT);
703 }
704 if (error == 0 && (iflags & ACE_NO_PROPAGATE_INHERIT_ACE)) {
705 error = str_append(dstr, NO_PROPAGATE_TXT);
706 }
707 if (error == 0 && (iflags & ACE_INHERIT_ONLY_ACE)) {
708 error = str_append(dstr, INHERIT_ONLY_TXT);
709 }
710 if (error == 0 && (iflags & ACE_SUCCESSFUL_ACCESS_ACE_FLAG)) {
711 error = str_append(dstr, SUCCESSFUL_ACCESS_TXT);
712 }
713 if (error == 0 && (iflags & ACE_FAILED_ACCESS_ACE_FLAG)) {
714 error = str_append(dstr, FAILED_ACCESS_TXT);
715 }
716 if (error == 0 && (iflags & ACE_INHERITED_ACE)) {
717 error = str_append(dstr, INHERITED_ACE_TXT);
718 }
719 if (error == 0 && dstr->d_aclexport[dstr->d_pos-1] == '/') {
720 dstr->d_aclexport[--dstr->d_pos] = '\0';
721 error = str_append(dstr, ":");
722 }
723 }
724
725 return (error);
726 }
727
728 /*
729 * Convert internal acl representation to external representation.
730 *
731 * The length of a non-owning user name or non-owning group name ie entries
732 * of type DEF_USER, USER, DEF_GROUP or GROUP, can exceed LOGNAME_MAX. We
733 * thus check the length of these entries, and if greater than LOGNAME_MAX,
734 * we realloc() via increase_length().
735 *
736 * The LOGNAME_MAX, ENTRYTYPELEN and PERMS limits are otherwise always
737 * adhered to.
738 */
739
740 /*
741 * acltotext() converts each ACL entry to look like this:
742 *
743 * entry_type:uid^gid^name:perms[:id]
744 *
745 * The maximum length of entry_type is 14 ("defaultgroup::" and
746 * "defaultother::") hence ENTRYTYPELEN is set to 14.
747 *
748 * The max length of a uid^gid^name entry (in theory) is 8, hence we use,
749 * however the ID could be a number so we therefore use ID_STR_MAX
750 *
751 * The length of a perms entry is 4 to allow for the comma appended to each
752 * to each acl entry. Hence PERMS is set to 4.
753 */
754
755 #define ENTRYTYPELEN 14
756 #define PERMS 4
757 #define ACL_ENTRY_SIZE (ENTRYTYPELEN + ID_STR_MAX + PERMS + APPENDED_ID_MAX)
758
759 char *
aclent_acltotext(aclent_t * aclp,int aclcnt,int flags)760 aclent_acltotext(aclent_t *aclp, int aclcnt, int flags)
761 {
762 dynaclstr_t *dstr;
763 char *aclexport = NULL;
764 int i;
765 int error = 0;
766
767 if (aclp == NULL)
768 return (NULL);
769 if ((dstr = malloc(sizeof (dynaclstr_t))) == NULL)
770 return (NULL);
771 dstr->d_bufsize = aclcnt * ACL_ENTRY_SIZE;
772 if ((dstr->d_aclexport = malloc(dstr->d_bufsize)) == NULL) {
773 free(dstr);
774 return (NULL);
775 }
776 *dstr->d_aclexport = '\0';
777 dstr->d_pos = 0;
778
779 for (i = 0; i < aclcnt; i++, aclp++) {
780 if (error = aclent_type_txt(dstr, aclp, flags))
781 break;
782 if (error = aclent_perm_txt(dstr, aclp->a_perm))
783 break;
784
785 if ((flags & ACL_APPEND_ID) && ((aclp->a_type == USER) ||
786 (aclp->a_type == DEF_USER) || (aclp->a_type == GROUP) ||
787 (aclp->a_type == DEF_GROUP))) {
788 char id[ID_STR_MAX], *idstr;
789
790 if (error = str_append(dstr, ":"))
791 break;
792 id[ID_STR_MAX - 1] = '\0'; /* null terminate buffer */
793 idstr = lltostr(aclp->a_id, &id[ID_STR_MAX - 1]);
794 if (error = str_append(dstr, idstr))
795 break;
796 }
797 if (i < aclcnt - 1)
798 if (error = str_append(dstr, ","))
799 break;
800 }
801 if (error) {
802 if (dstr->d_aclexport)
803 free(dstr->d_aclexport);
804 } else {
805 aclexport = dstr->d_aclexport;
806 }
807 free(dstr);
808 return (aclexport);
809 }
810
811 char *
acltotext(aclent_t * aclp,int aclcnt)812 acltotext(aclent_t *aclp, int aclcnt)
813 {
814 return (aclent_acltotext(aclp, aclcnt, 0));
815 }
816
817
818 aclent_t *
aclfromtext(char * aclstr,int * aclcnt)819 aclfromtext(char *aclstr, int *aclcnt)
820 {
821 acl_t *aclp;
822 aclent_t *aclentp;
823 int error;
824
825 error = acl_fromtext(aclstr, &aclp);
826 if (error)
827 return (NULL);
828
829 aclentp = aclp->acl_aclp;
830 aclp->acl_aclp = NULL;
831 *aclcnt = aclp->acl_cnt;
832
833 acl_free(aclp);
834 return (aclentp);
835 }
836
837
838 /*
839 * Append string onto dynaclstr_t.
840 *
841 * Return 0 on success, 1 for failure.
842 */
843 static int
str_append(dynaclstr_t * dstr,char * newstr)844 str_append(dynaclstr_t *dstr, char *newstr)
845 {
846 size_t len = strlen(newstr);
847
848 if ((len + dstr->d_pos) >= dstr->d_bufsize) {
849 dstr->d_aclexport = realloc(dstr->d_aclexport,
850 dstr->d_bufsize + len + 1);
851 if (dstr->d_aclexport == NULL)
852 return (1);
853 dstr->d_bufsize += len;
854 }
855 (void) strcat(&dstr->d_aclexport[dstr->d_pos], newstr);
856 dstr->d_pos += len;
857 return (0);
858 }
859
860 static int
aclent_perm_txt(dynaclstr_t * dstr,o_mode_t perm)861 aclent_perm_txt(dynaclstr_t *dstr, o_mode_t perm)
862 {
863 char buf[4];
864
865 if (perm & S_IROTH)
866 buf[0] = 'r';
867 else
868 buf[0] = '-';
869 if (perm & S_IWOTH)
870 buf[1] = 'w';
871 else
872 buf[1] = '-';
873 if (perm & S_IXOTH)
874 buf[2] = 'x';
875 else
876 buf[2] = '-';
877 buf[3] = '\0';
878 return (str_append(dstr, buf));
879 }
880
881 /*
882 * ace_acltotext() convert each ace formatted acl to look like this:
883 *
884 * entry_type:uid^gid^name:perms[:flags]:<allow|deny>[:id][,]
885 *
886 * The maximum length of entry_type is 5 ("group")
887 *
888 * The max length of a uid^gid^name entry (in theory) is 8,
889 * however id could be a number so we therefore use ID_STR_MAX
890 *
891 * The length of a perms entry is 144 i.e read_data/write_data...
892 * to each acl entry.
893 *
894 * iflags: file_inherit/dir_inherit/inherit_only/no_propagate/successful_access
895 * /failed_access
896 *
897 */
898
899 #define ACE_ENTRYTYPLEN 6
900 #define IFLAGS_STR "file_inherit/dir_inherit/inherit_only/no_propagate/" \
901 "successful_access/failed_access/inherited"
902 #define IFLAGS_SIZE (sizeof (IFLAGS_STR) - 1)
903 #define ACCESS_TYPE_SIZE 7 /* if unknown */
904 #define COLON_CNT 3
905 #define PERMS_LEN 216
906 #define ACE_ENTRY_SIZE (ACE_ENTRYTYPLEN + ID_STR_MAX + PERMS_LEN + \
907 ACCESS_TYPE_SIZE + IFLAGS_SIZE + COLON_CNT + APPENDED_ID_MAX)
908
909 static char *
ace_acltotext(acl_t * aceaclp,int flags)910 ace_acltotext(acl_t *aceaclp, int flags)
911 {
912 ace_t *aclp = aceaclp->acl_aclp;
913 int aclcnt = aceaclp->acl_cnt;
914 int i;
915 int error = 0;
916 int isdir = (aceaclp->acl_flags & ACL_IS_DIR);
917 dynaclstr_t *dstr;
918 char *aclexport = NULL;
919 char *rawsidp = NULL;
920
921 if (aclp == NULL)
922 return (NULL);
923
924 if ((dstr = malloc(sizeof (dynaclstr_t))) == NULL)
925 return (NULL);
926 dstr->d_bufsize = aclcnt * ACL_ENTRY_SIZE;
927 if ((dstr->d_aclexport = malloc(dstr->d_bufsize)) == NULL) {
928 free(dstr);
929 return (NULL);
930 }
931 *dstr->d_aclexport = '\0';
932 dstr->d_pos = 0;
933
934 for (i = 0; i < aclcnt; i++, aclp++) {
935
936 if (error = ace_type_txt(dstr, aclp, flags))
937 break;
938 if (error = ace_perm_txt(dstr, aclp->a_access_mask,
939 aclp->a_flags, isdir, flags))
940 break;
941 if (error = ace_inherit_txt(dstr, aclp->a_flags, flags))
942 break;
943 if (error = ace_access_txt(dstr, aclp->a_type))
944 break;
945
946 if ((flags & ACL_APPEND_ID) &&
947 (((aclp->a_flags & ACE_TYPE_FLAGS) == 0) ||
948 ((aclp->a_flags & ACE_TYPE_FLAGS) ==
949 ACE_IDENTIFIER_GROUP))) {
950 char id[ID_STR_MAX], *idstr;
951
952 if (error = str_append(dstr, ":"))
953 break;
954
955 rawsidp = NULL;
956 id[ID_STR_MAX -1] = '\0'; /* null terminate */
957 if (aclp->a_who > MAXUID && (flags & ACL_SID_FMT)) {
958
959 error = getsidname(aclp->a_who,
960 ((aclp->a_flags & ACE_TYPE_FLAGS) == 0) ?
961 B_TRUE : B_FALSE, &idstr, 1);
962 rawsidp = idstr;
963 if (error)
964 break;
965 } else if (aclp->a_who > MAXUID &&
966 !(flags & ACL_NORESOLVE)) {
967 idstr = lltostr(UID_NOBODY,
968 &id[ID_STR_MAX - 1]);
969 } else {
970 idstr = lltostr(aclp->a_who,
971 &id[ID_STR_MAX - 1]);
972 }
973 if (error = str_append(dstr, idstr))
974 break;
975 if (rawsidp) {
976 free(rawsidp);
977 rawsidp = NULL;
978 }
979 }
980 if (i < aclcnt - 1) {
981 if (error = str_append(dstr, ","))
982 break;
983 }
984 }
985
986 if (rawsidp)
987 free(rawsidp);
988 if (error) {
989 if (dstr->d_aclexport)
990 free(dstr->d_aclexport);
991 } else {
992 aclexport = dstr->d_aclexport;
993 }
994 free(dstr);
995 return (aclexport);
996 }
997
998 char *
acl_totext(acl_t * aclp,int flags)999 acl_totext(acl_t *aclp, int flags)
1000 {
1001 char *txtp;
1002
1003 if (aclp == NULL)
1004 return (NULL);
1005
1006 switch (aclp->acl_type) {
1007 case ACE_T:
1008 txtp = ace_acltotext(aclp, flags);
1009 break;
1010 case ACLENT_T:
1011 txtp = aclent_acltotext(aclp->acl_aclp, aclp->acl_cnt, flags);
1012 break;
1013 }
1014
1015 return (txtp);
1016 }
1017
1018 int
acl_fromtext(const char * acltextp,acl_t ** ret_aclp)1019 acl_fromtext(const char *acltextp, acl_t **ret_aclp)
1020 {
1021 int error;
1022 char *buf;
1023
1024 buf = malloc(strlen(acltextp) + 2);
1025 if (buf == NULL)
1026 return (EACL_MEM_ERROR);
1027 strcpy(buf, acltextp);
1028 strcat(buf, "\n");
1029
1030 (void) mutex_lock(&yymutex);
1031 yybuf = buf;
1032 yyreset();
1033 error = yyparse();
1034 free(buf);
1035
1036 if (yyacl) {
1037 if (error == 0)
1038 *ret_aclp = yyacl;
1039 else {
1040 acl_free(yyacl);
1041 }
1042 yyacl = NULL;
1043 }
1044 (void) mutex_unlock(&yymutex);
1045
1046 return (error);
1047 }
1048
1049 int
acl_parse(const char * acltextp,acl_t ** aclp)1050 acl_parse(const char *acltextp, acl_t **aclp)
1051 {
1052 int error;
1053
1054 yyinteractive = 1;
1055 error = acl_fromtext(acltextp, aclp);
1056 yyinteractive = 0;
1057 return (error);
1058 }
1059
1060 static void
ace_compact_printacl(acl_t * aclp,int flgs)1061 ace_compact_printacl(acl_t *aclp, int flgs)
1062 {
1063 int cnt;
1064 ace_t *acep;
1065 dynaclstr_t *dstr;
1066 int len;
1067
1068 if ((dstr = malloc(sizeof (dynaclstr_t))) == NULL)
1069 return;
1070 dstr->d_bufsize = ACE_ENTRY_SIZE;
1071 if ((dstr->d_aclexport = malloc(dstr->d_bufsize)) == NULL) {
1072 free(dstr);
1073 return;
1074 }
1075 *dstr->d_aclexport = '\0';
1076
1077 dstr->d_pos = 0;
1078 for (cnt = 0, acep = aclp->acl_aclp;
1079 cnt != aclp->acl_cnt; cnt++, acep++) {
1080 dstr->d_aclexport[0] = '\0';
1081 dstr->d_pos = 0;
1082
1083 if (ace_type_txt(dstr, acep, flgs))
1084 break;
1085 len = strlen(&dstr->d_aclexport[0]);
1086 if (ace_perm_txt(dstr, acep->a_access_mask, acep->a_flags,
1087 aclp->acl_flags & ACL_IS_DIR, ACL_COMPACT_FMT))
1088 break;
1089 if (ace_inherit_txt(dstr, acep->a_flags, ACL_COMPACT_FMT))
1090 break;
1091 if (ace_access_txt(dstr, acep->a_type) == -1)
1092 break;
1093 (void) printf(" %20.*s%s\n", len, dstr->d_aclexport,
1094 &dstr->d_aclexport[len]);
1095 }
1096
1097 if (dstr->d_aclexport)
1098 free(dstr->d_aclexport);
1099 free(dstr);
1100 }
1101
1102 static void
ace_printacl(acl_t * aclp,int cols,int flgs)1103 ace_printacl(acl_t *aclp, int cols, int flgs)
1104 {
1105 int slot = 0;
1106 char *token;
1107 char *acltext;
1108
1109 if (flgs & ACL_COMPACT_FMT) {
1110 ace_compact_printacl(aclp, flgs);
1111 return;
1112 }
1113
1114 acltext = acl_totext(aclp, flgs);
1115
1116 if (acltext == NULL)
1117 return;
1118
1119 token = strtok(acltext, ",");
1120 if (token == NULL) {
1121 free(acltext);
1122 return;
1123 }
1124
1125 do {
1126 (void) printf(" %d:", slot++);
1127 split_line(token, cols - 5);
1128 } while (token = strtok(NULL, ","));
1129 free(acltext);
1130 }
1131
1132 /*
1133 * pretty print an ACL.
1134 * For aclent_t ACL's the format is
1135 * similar to the old format used by getfacl,
1136 * with the addition of adding a "slot" number
1137 * before each entry.
1138 *
1139 * for ace_t ACL's the cols variable will break up
1140 * the long lines into multiple lines and will also
1141 * print a "slot" number.
1142 */
1143 void
acl_printacl2(acl_t * aclp,int cols,int flgs)1144 acl_printacl2(acl_t *aclp, int cols, int flgs)
1145 {
1146
1147 switch (aclp->acl_type) {
1148 case ACLENT_T:
1149 aclent_printacl(aclp);
1150 break;
1151 case ACE_T:
1152 ace_printacl(aclp, cols, flgs);
1153 break;
1154 }
1155 }
1156
1157 /*
1158 * Historical, compatibility version of the above.
1159 */
1160 void
acl_printacl(acl_t * aclp,int cols,int compact)1161 acl_printacl(acl_t *aclp, int cols, int compact)
1162 {
1163 int flgs = compact ? ACL_COMPACT_FMT : 0;
1164
1165 switch (aclp->acl_type) {
1166 case ACLENT_T:
1167 aclent_printacl(aclp);
1168 break;
1169 case ACE_T:
1170 ace_printacl(aclp, cols, flgs);
1171 break;
1172 }
1173 }
1174
1175 typedef struct value_table {
1176 char p_letter; /* perm letter such as 'r' */
1177 uint32_t p_value; /* value for perm when pletter found */
1178 } value_table_t;
1179
1180 /*
1181 * The permission tables are laid out in positional order
1182 * a '-' character will indicate a permission at a given
1183 * position is not specified. The '-' is not part of the
1184 * table, but will be checked for in the permission computation
1185 * routine.
1186 */
1187 value_table_t ace_perm_table[] = {
1188 { 'r', ACE_READ_DATA},
1189 { 'w', ACE_WRITE_DATA},
1190 { 'x', ACE_EXECUTE},
1191 { 'p', ACE_APPEND_DATA},
1192 { 'd', ACE_DELETE},
1193 { 'D', ACE_DELETE_CHILD},
1194 { 'a', ACE_READ_ATTRIBUTES},
1195 { 'A', ACE_WRITE_ATTRIBUTES},
1196 { 'R', ACE_READ_NAMED_ATTRS},
1197 { 'W', ACE_WRITE_NAMED_ATTRS},
1198 { 'c', ACE_READ_ACL},
1199 { 'C', ACE_WRITE_ACL},
1200 { 'o', ACE_WRITE_OWNER},
1201 { 's', ACE_SYNCHRONIZE}
1202 };
1203
1204 #define ACE_PERM_COUNT (sizeof (ace_perm_table) / sizeof (value_table_t))
1205
1206 value_table_t aclent_perm_table[] = {
1207 { 'r', S_IROTH},
1208 { 'w', S_IWOTH},
1209 { 'x', S_IXOTH}
1210 };
1211
1212 #define ACLENT_PERM_COUNT (sizeof (aclent_perm_table) / sizeof (value_table_t))
1213
1214 value_table_t inherit_table[] = {
1215 {'f', ACE_FILE_INHERIT_ACE},
1216 {'d', ACE_DIRECTORY_INHERIT_ACE},
1217 {'i', ACE_INHERIT_ONLY_ACE},
1218 {'n', ACE_NO_PROPAGATE_INHERIT_ACE},
1219 {'S', ACE_SUCCESSFUL_ACCESS_ACE_FLAG},
1220 {'F', ACE_FAILED_ACCESS_ACE_FLAG},
1221 {'I', ACE_INHERITED_ACE}
1222 };
1223
1224 #define IFLAG_COUNT (sizeof (inherit_table) / sizeof (value_table_t))
1225 #define IFLAG_COUNT_V1 6 /* Older version compatibility */
1226
1227 /*
1228 * compute value from a permission table or inheritance table
1229 * based on string passed in. If positional is set then
1230 * string must match order in permtab, otherwise any order
1231 * is allowed.
1232 */
1233 int
compute_values(value_table_t * permtab,int count,char * permstr,int positional,uint32_t * mask)1234 compute_values(value_table_t *permtab, int count,
1235 char *permstr, int positional, uint32_t *mask)
1236 {
1237 uint32_t perm_val = 0;
1238 char *pstr;
1239 int i, found;
1240
1241 if (count < 0)
1242 return (1);
1243
1244 if (positional) {
1245 for (i = 0, pstr = permstr; i != count && pstr &&
1246 *pstr; i++, pstr++) {
1247 if (*pstr == permtab[i].p_letter) {
1248 perm_val |= permtab[i].p_value;
1249 } else if (*pstr != '-') {
1250 return (1);
1251 }
1252 }
1253 } else { /* random order single letters with no '-' */
1254 for (pstr = permstr; pstr && *pstr; pstr++) {
1255 for (found = 0, i = 0; i != count; i++) {
1256 if (*pstr == permtab[i].p_letter) {
1257 perm_val |= permtab[i].p_value;
1258 found = 1;
1259 break;
1260 }
1261 }
1262 if (found == 0)
1263 return (1);
1264 }
1265 }
1266
1267 *mask = perm_val;
1268 return (0);
1269 }
1270
1271
1272 int
ace_inherit_helper(char * str,uint32_t * imask,int table_length)1273 ace_inherit_helper(char *str, uint32_t *imask, int table_length)
1274 {
1275 int rc = 0;
1276
1277 if (strlen(str) == table_length) {
1278 /*
1279 * If the string == table_length then first check to see it's
1280 * in positional format. If that fails then see if it's in
1281 * non-positional format.
1282 */
1283 if (compute_values(inherit_table, table_length, str,
1284 1, imask) && compute_values(inherit_table,
1285 table_length, str, 0, imask)) {
1286 rc = 1;
1287 }
1288 } else {
1289 rc = compute_values(inherit_table, table_length, str, 0, imask);
1290 }
1291
1292 return (rc ? EACL_INHERIT_ERROR : 0);
1293 }
1294
1295 /*
1296 * compute value for inheritance flags.
1297 */
1298 int
compute_ace_inherit(char * str,uint32_t * imask)1299 compute_ace_inherit(char *str, uint32_t *imask)
1300 {
1301 int rc = 0;
1302
1303 rc = ace_inherit_helper(str, imask, IFLAG_COUNT);
1304
1305 if (rc && strlen(str) != IFLAG_COUNT) {
1306
1307 /* is it an old formatted inherit string? */
1308 rc = ace_inherit_helper(str, imask, IFLAG_COUNT_V1);
1309 }
1310
1311 return (rc);
1312 }
1313
1314
1315 /*
1316 * compute value for ACE permissions.
1317 */
1318 int
compute_ace_perms(char * str,uint32_t * mask)1319 compute_ace_perms(char *str, uint32_t *mask)
1320 {
1321 int positional = 0;
1322 int error;
1323
1324 if (strlen(str) == ACE_PERM_COUNT)
1325 positional = 1;
1326
1327 error = compute_values(ace_perm_table, ACE_PERM_COUNT,
1328 str, positional, mask);
1329
1330 if (error && positional) {
1331 /*
1332 * If positional was set, then make sure permissions
1333 * aren't actually valid in non positional case where
1334 * all permissions are specified, just in random order.
1335 */
1336 error = compute_values(ace_perm_table,
1337 ACE_PERM_COUNT, str, 0, mask);
1338 }
1339 if (error)
1340 error = EACL_PERM_MASK_ERROR;
1341
1342 return (error);
1343 }
1344
1345
1346
1347 /*
1348 * compute values for aclent permissions.
1349 */
1350 int
compute_aclent_perms(char * str,o_mode_t * mask)1351 compute_aclent_perms(char *str, o_mode_t *mask)
1352 {
1353 int error;
1354 uint32_t pmask;
1355
1356 if (strlen(str) != ACLENT_PERM_COUNT)
1357 return (EACL_PERM_MASK_ERROR);
1358
1359 *mask = 0;
1360 error = compute_values(aclent_perm_table, ACLENT_PERM_COUNT,
1361 str, 1, &pmask);
1362 if (error == 0) {
1363 *mask = (o_mode_t)pmask;
1364 } else
1365 error = EACL_PERM_MASK_ERROR;
1366 return (error);
1367 }
1368
1369 /*
1370 * determine ACE permissions.
1371 */
1372 int
ace_perm_mask(struct acl_perm_type * aclperm,uint32_t * mask)1373 ace_perm_mask(struct acl_perm_type *aclperm, uint32_t *mask)
1374 {
1375 int error;
1376
1377 if (aclperm->perm_style == PERM_TYPE_EMPTY) {
1378 *mask = 0;
1379 return (0);
1380 }
1381
1382 if (aclperm->perm_style == PERM_TYPE_ACE) {
1383 *mask = aclperm->perm_val;
1384 return (0);
1385 }
1386
1387 error = compute_ace_perms(aclperm->perm_str, mask);
1388 if (error) {
1389 acl_error(dgettext(TEXT_DOMAIN,
1390 "Invalid permission(s) '%s' specified\n"),
1391 aclperm->perm_str);
1392 return (EACL_PERM_MASK_ERROR);
1393 }
1394
1395 return (0);
1396 }
1397