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) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
24 *
25 * Copyright 2022 RackTop Systems, Inc.
26 */
27
28
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <limits.h>
33 #include <grp.h>
34 #include <pwd.h>
35 #include <strings.h>
36 #include <sys/types.h>
37 #include <errno.h>
38 #include <sys/stat.h>
39 #include <sys/varargs.h>
40 #include <locale.h>
41 #include <aclutils.h>
42 #include <sys/avl.h>
43 #include <acl_common.h>
44 #include <idmap.h>
45
46 #define ACL_PATH 0
47 #define ACL_FD 1
48
49
50 typedef union {
51 const char *file;
52 int fd;
53 } acl_inp;
54
55
56 /*
57 * Determine whether a file has a trivial ACL
58 * returns: 0 = trivial
59 * 1 = nontrivial
60 * <0 some other system failure, such as ENOENT or EPERM
61 */
62 int
acl_trivial(const char * filename)63 acl_trivial(const char *filename)
64 {
65 int acl_flavor;
66 int aclcnt;
67 int cntcmd;
68 int val = 0;
69 ace_t *acep;
70
71 acl_flavor = pathconf(filename, _PC_ACL_ENABLED);
72
73 if (acl_flavor == _ACL_ACE_ENABLED)
74 cntcmd = ACE_GETACLCNT;
75 else
76 cntcmd = GETACLCNT;
77
78 aclcnt = acl(filename, cntcmd, 0, NULL);
79 if (aclcnt > 0) {
80 if (acl_flavor == _ACL_ACE_ENABLED) {
81 acep = malloc(sizeof (ace_t) * aclcnt);
82 if (acep == NULL)
83 return (-1);
84 if (acl(filename, ACE_GETACL,
85 aclcnt, acep) < 0) {
86 free(acep);
87 return (-1);
88 }
89
90 val = ace_trivial(acep, aclcnt);
91 free(acep);
92
93 } else if (aclcnt > MIN_ACL_ENTRIES)
94 val = 1;
95 }
96 return (val);
97 }
98
99
100 static int
cacl_get(acl_inp inp,int get_flag,int type,acl_t ** aclp)101 cacl_get(acl_inp inp, int get_flag, int type, acl_t **aclp)
102 {
103 const char *fname;
104 int fd;
105 int ace_acl = 0;
106 int error;
107 int getcmd, cntcmd;
108 acl_t *acl_info;
109 int save_errno;
110 int stat_error;
111 struct stat64 statbuf;
112
113 *aclp = NULL;
114 if (type == ACL_PATH) {
115 fname = inp.file;
116 ace_acl = pathconf(fname, _PC_ACL_ENABLED);
117 } else {
118 fd = inp.fd;
119 ace_acl = fpathconf(fd, _PC_ACL_ENABLED);
120 }
121
122 /*
123 * if acl's aren't supported then
124 * send it through the old GETACL interface
125 */
126 if (ace_acl == 0 || ace_acl == -1) {
127 ace_acl = _ACL_ACLENT_ENABLED;
128 }
129
130 if (ace_acl & _ACL_ACE_ENABLED) {
131 cntcmd = ACE_GETACLCNT;
132 getcmd = ACE_GETACL;
133 acl_info = acl_alloc(ACE_T);
134 } else {
135 cntcmd = GETACLCNT;
136 getcmd = GETACL;
137 acl_info = acl_alloc(ACLENT_T);
138 }
139
140 if (acl_info == NULL)
141 return (-1);
142
143 if (type == ACL_PATH) {
144 acl_info->acl_cnt = acl(fname, cntcmd, 0, NULL);
145 } else {
146 acl_info->acl_cnt = facl(fd, cntcmd, 0, NULL);
147 }
148
149 save_errno = errno;
150 if (acl_info->acl_cnt < 0) {
151 acl_free(acl_info);
152 errno = save_errno;
153 return (-1);
154 }
155
156 if (acl_info->acl_cnt == 0) {
157 acl_free(acl_info);
158 errno = save_errno;
159 return (0);
160 }
161
162 acl_info->acl_aclp =
163 malloc(acl_info->acl_cnt * acl_info->acl_entry_size);
164 save_errno = errno;
165
166 if (acl_info->acl_aclp == NULL) {
167 acl_free(acl_info);
168 errno = save_errno;
169 return (-1);
170 }
171
172 if (type == ACL_PATH) {
173 stat_error = stat64(fname, &statbuf);
174 error = acl(fname, getcmd, acl_info->acl_cnt,
175 acl_info->acl_aclp);
176 } else {
177 stat_error = fstat64(fd, &statbuf);
178 error = facl(fd, getcmd, acl_info->acl_cnt,
179 acl_info->acl_aclp);
180 }
181
182 save_errno = errno;
183 if (error == -1) {
184 acl_free(acl_info);
185 errno = save_errno;
186 return (-1);
187 }
188
189
190 if (stat_error == 0) {
191 acl_info->acl_flags =
192 (S_ISDIR(statbuf.st_mode) ? ACL_IS_DIR : 0);
193 } else
194 acl_info->acl_flags = 0;
195
196 switch (acl_info->acl_type) {
197 case ACLENT_T:
198 if (acl_info->acl_cnt <= MIN_ACL_ENTRIES)
199 acl_info->acl_flags |= ACL_IS_TRIVIAL;
200 break;
201 case ACE_T:
202 if (ace_trivial(acl_info->acl_aclp, acl_info->acl_cnt) == 0)
203 acl_info->acl_flags |= ACL_IS_TRIVIAL;
204 break;
205 default:
206 errno = EINVAL;
207 acl_free(acl_info);
208 return (-1);
209 }
210
211 if ((acl_info->acl_flags & ACL_IS_TRIVIAL) &&
212 (get_flag & ACL_NO_TRIVIAL)) {
213 acl_free(acl_info);
214 errno = 0;
215 return (0);
216 }
217
218 *aclp = acl_info;
219 return (0);
220 }
221
222 /*
223 * return -1 on failure, otherwise the number of acl
224 * entries is returned
225 */
226 int
acl_get(const char * path,int get_flag,acl_t ** aclp)227 acl_get(const char *path, int get_flag, acl_t **aclp)
228 {
229 acl_inp acl_inp;
230 acl_inp.file = path;
231
232 return (cacl_get(acl_inp, get_flag, ACL_PATH, aclp));
233 }
234
235 int
facl_get(int fd,int get_flag,acl_t ** aclp)236 facl_get(int fd, int get_flag, acl_t **aclp)
237 {
238
239 acl_inp acl_inp;
240 acl_inp.fd = fd;
241
242 return (cacl_get(acl_inp, get_flag, ACL_FD, aclp));
243 }
244
245 /*
246 * Set an ACL, translates acl to ace_t when appropriate.
247 */
248 static int
cacl_set(acl_inp * acl_inp,acl_t * aclp,int type)249 cacl_set(acl_inp *acl_inp, acl_t *aclp, int type)
250 {
251 int error = 0;
252 int acl_flavor_target;
253 struct stat64 statbuf;
254 int stat_error;
255 int isdir;
256
257
258 if (type == ACL_PATH) {
259 stat_error = stat64(acl_inp->file, &statbuf);
260 if (stat_error)
261 return (-1);
262 acl_flavor_target = pathconf(acl_inp->file, _PC_ACL_ENABLED);
263 } else {
264 stat_error = fstat64(acl_inp->fd, &statbuf);
265 if (stat_error)
266 return (-1);
267 acl_flavor_target = fpathconf(acl_inp->fd, _PC_ACL_ENABLED);
268 }
269
270 /*
271 * If target returns an error or 0 from pathconf call then
272 * fall back to UFS/POSIX Draft interface.
273 * In the case of 0 we will then fail in either acl(2) or
274 * acl_translate(). We could erroneously get 0 back from
275 * a file system that is using fs_pathconf() and not answering
276 * the _PC_ACL_ENABLED question itself.
277 */
278 if (acl_flavor_target == 0 || acl_flavor_target == -1)
279 acl_flavor_target = _ACL_ACLENT_ENABLED;
280
281 isdir = S_ISDIR(statbuf.st_mode);
282
283 if ((error = acl_translate(aclp, acl_flavor_target, isdir,
284 statbuf.st_uid, statbuf.st_gid)) != 0) {
285 return (error);
286 }
287
288 if (type == ACL_PATH) {
289 error = acl(acl_inp->file,
290 (aclp->acl_type == ACE_T) ? ACE_SETACL : SETACL,
291 aclp->acl_cnt, aclp->acl_aclp);
292 } else {
293 error = facl(acl_inp->fd,
294 (aclp->acl_type == ACE_T) ? ACE_SETACL : SETACL,
295 aclp->acl_cnt, aclp->acl_aclp);
296 }
297
298 return (error);
299 }
300
301 int
acl_set(const char * path,acl_t * aclp)302 acl_set(const char *path, acl_t *aclp)
303 {
304 acl_inp acl_inp;
305
306 acl_inp.file = path;
307
308 return (cacl_set(&acl_inp, aclp, ACL_PATH));
309 }
310
311 int
facl_set(int fd,acl_t * aclp)312 facl_set(int fd, acl_t *aclp)
313 {
314 acl_inp acl_inp;
315
316 acl_inp.fd = fd;
317
318 return (cacl_set(&acl_inp, aclp, ACL_FD));
319 }
320
321 int
acl_cnt(acl_t * aclp)322 acl_cnt(acl_t *aclp)
323 {
324 return (aclp->acl_cnt);
325 }
326
327 int
acl_type(acl_t * aclp)328 acl_type(acl_t *aclp)
329 {
330 return (aclp->acl_type);
331 }
332
333 acl_t *
acl_dup(acl_t * aclp)334 acl_dup(acl_t *aclp)
335 {
336 acl_t *newaclp;
337
338 newaclp = acl_alloc(aclp->acl_type);
339 if (newaclp == NULL)
340 return (NULL);
341
342 newaclp->acl_aclp = malloc(aclp->acl_entry_size * aclp->acl_cnt);
343 if (newaclp->acl_aclp == NULL) {
344 acl_free(newaclp);
345 return (NULL);
346 }
347
348 (void) memcpy(newaclp->acl_aclp,
349 aclp->acl_aclp, aclp->acl_entry_size * aclp->acl_cnt);
350 newaclp->acl_cnt = aclp->acl_cnt;
351
352 return (newaclp);
353 }
354
355 int
acl_flags(acl_t * aclp)356 acl_flags(acl_t *aclp)
357 {
358 return (aclp->acl_flags);
359 }
360
361 void *
acl_data(acl_t * aclp)362 acl_data(acl_t *aclp)
363 {
364 return (aclp->acl_aclp);
365 }
366
367 /*
368 * Take an acl array and build an acl_t.
369 */
370 acl_t *
acl_to_aclp(enum acl_type type,void * acl,int count)371 acl_to_aclp(enum acl_type type, void *acl, int count)
372 {
373 acl_t *aclp;
374
375
376 aclp = acl_alloc(type);
377 if (aclp == NULL)
378 return (aclp);
379
380 aclp->acl_aclp = acl;
381 aclp->acl_cnt = count;
382
383 return (aclp);
384 }
385
386 /*
387 * Remove an ACL from a file and create a trivial ACL based
388 * off of the mode argument. After acl has been set owner/group
389 * are updated to match owner,group arguments
390 */
391 int
acl_strip(const char * file,uid_t owner,gid_t group,mode_t mode)392 acl_strip(const char *file, uid_t owner, gid_t group, mode_t mode)
393 {
394 int error = 0;
395 aclent_t min_acl[MIN_ACL_ENTRIES];
396 ace_t *min_ace_acl;
397 int acl_flavor;
398 int aclcnt;
399 struct stat64 statbuf;
400
401 acl_flavor = pathconf(file, _PC_ACL_ENABLED);
402
403 if (stat64(file, &statbuf) != 0) {
404 error = 1;
405 return (error);
406 }
407
408 /*
409 * force it through aclent flavor when file system doesn't
410 * understand question
411 */
412 if (acl_flavor == 0 || acl_flavor == -1)
413 acl_flavor = _ACL_ACLENT_ENABLED;
414
415 if (acl_flavor & _ACL_ACLENT_ENABLED) {
416 min_acl[0].a_type = USER_OBJ;
417 min_acl[0].a_id = owner;
418 min_acl[0].a_perm = ((mode & 0700) >> 6);
419 min_acl[1].a_type = GROUP_OBJ;
420 min_acl[1].a_id = group;
421 min_acl[1].a_perm = ((mode & 0070) >> 3);
422 min_acl[2].a_type = CLASS_OBJ;
423 min_acl[2].a_id = (uid_t)-1;
424 min_acl[2].a_perm = ((mode & 0070) >> 3);
425 min_acl[3].a_type = OTHER_OBJ;
426 min_acl[3].a_id = (uid_t)-1;
427 min_acl[3].a_perm = (mode & 0007);
428 aclcnt = 4;
429 error = acl(file, SETACL, aclcnt, min_acl);
430 } else if (acl_flavor & _ACL_ACE_ENABLED) {
431 if ((error = acl_trivial_create(mode, S_ISDIR(statbuf.st_mode),
432 &min_ace_acl, &aclcnt)) != 0)
433 return (error);
434 error = acl(file, ACE_SETACL, aclcnt, min_ace_acl);
435 free(min_ace_acl);
436 } else {
437 errno = EINVAL;
438 error = 1;
439 }
440
441 if (error == 0)
442 error = chown(file, owner, group);
443 return (error);
444 }
445
446 static int
ace_match(void * entry1,void * entry2)447 ace_match(void *entry1, void *entry2)
448 {
449 ace_t *p1 = (ace_t *)entry1;
450 ace_t *p2 = (ace_t *)entry2;
451 ace_t ace1, ace2;
452
453 ace1 = *p1;
454 ace2 = *p2;
455
456 /*
457 * Need to fixup who field for abstrations for
458 * accurate comparison, since field is undefined.
459 */
460 if (ace1.a_flags & (ACE_OWNER|ACE_GROUP|ACE_EVERYONE))
461 ace1.a_who = (uid_t)-1;
462 if (ace2.a_flags & (ACE_OWNER|ACE_GROUP|ACE_EVERYONE))
463 ace2.a_who = (uid_t)-1;
464 return (memcmp(&ace1, &ace2, sizeof (ace_t)));
465 }
466
467 static int
aclent_match(void * entry1,void * entry2)468 aclent_match(void *entry1, void *entry2)
469 {
470 aclent_t *aclent1 = (aclent_t *)entry1;
471 aclent_t *aclent2 = (aclent_t *)entry2;
472
473 return (memcmp(aclent1, aclent2, sizeof (aclent_t)));
474 }
475
476 /*
477 * Find acl entries in acl that correspond to removeacl. Search
478 * is started from slot. The flag argument indicates whether to
479 * remove all matches or just the first match.
480 */
481 int
acl_removeentries(acl_t * acl,acl_t * removeacl,int start_slot,int flag)482 acl_removeentries(acl_t *acl, acl_t *removeacl, int start_slot, int flag)
483 {
484 int i, j;
485 int match;
486 int (*acl_match)(void *acl1, void *acl2);
487 void *acl_entry, *remove_entry;
488 void *start;
489 int found = 0;
490
491 if (flag != ACL_REMOVE_ALL && flag != ACL_REMOVE_FIRST)
492 flag = ACL_REMOVE_FIRST;
493
494 if (acl == NULL || removeacl == NULL)
495 return (EACL_NO_ACL_ENTRY);
496
497 if (acl->acl_type != removeacl->acl_type)
498 return (EACL_DIFF_TYPE);
499
500 if (acl->acl_type == ACLENT_T)
501 acl_match = aclent_match;
502 else
503 acl_match = ace_match;
504
505 for (i = 0, remove_entry = removeacl->acl_aclp;
506 i != removeacl->acl_cnt; i++) {
507
508 j = 0;
509 acl_entry = (char *)acl->acl_aclp +
510 (acl->acl_entry_size * start_slot);
511 for (;;) {
512 match = acl_match(acl_entry, remove_entry);
513 if (match == 0) {
514 found++;
515
516 /* avoid memmove if last entry */
517 if (acl->acl_cnt == (j + 1)) {
518 acl->acl_cnt--;
519 break;
520 }
521
522 start = (char *)acl_entry +
523 acl->acl_entry_size;
524 (void) memmove(acl_entry, start,
525 acl->acl_entry_size *
526 (acl->acl_cnt-- - (j + 1)));
527
528 if (flag == ACL_REMOVE_FIRST)
529 break;
530 /*
531 * List has changed, just continue so this
532 * slot gets checked with it's new contents.
533 */
534 continue;
535 }
536 acl_entry = ((char *)acl_entry + acl->acl_entry_size);
537 if (++j >= acl->acl_cnt) {
538 break;
539 }
540 }
541 remove_entry = (char *)remove_entry + removeacl->acl_entry_size;
542 }
543
544 return ((found == 0) ? EACL_NO_ACL_ENTRY : 0);
545 }
546
547 /*
548 * Replace entires entries in acl1 with the corresponding entries
549 * in newentries. The where argument specifies where to begin
550 * the replacement. If the where argument is 1 greater than the
551 * number of acl entries in acl1 then they are appended. If the
552 * where argument is 2+ greater than the number of acl entries then
553 * EACL_INVALID_SLOT is returned.
554 */
555 int
acl_modifyentries(acl_t * acl1,acl_t * newentries,int where)556 acl_modifyentries(acl_t *acl1, acl_t *newentries, int where)
557 {
558
559 int slot;
560 int slots_needed;
561 int slots_left;
562 int newsize;
563
564 if (acl1 == NULL || newentries == NULL)
565 return (EACL_NO_ACL_ENTRY);
566
567 if (where < 0 || where >= acl1->acl_cnt)
568 return (EACL_INVALID_SLOT);
569
570 if (acl1->acl_type != newentries->acl_type)
571 return (EACL_DIFF_TYPE);
572
573 slot = where;
574
575 slots_left = acl1->acl_cnt - slot + 1;
576 if (slots_left < newentries->acl_cnt) {
577 slots_needed = newentries->acl_cnt - slots_left;
578 newsize = (acl1->acl_entry_size * acl1->acl_cnt) +
579 (acl1->acl_entry_size * slots_needed);
580 acl1->acl_aclp = realloc(acl1->acl_aclp, newsize);
581 if (acl1->acl_aclp == NULL)
582 return (-1);
583 }
584 (void) memcpy((char *)acl1->acl_aclp + (acl1->acl_entry_size * slot),
585 newentries->acl_aclp,
586 newentries->acl_entry_size * newentries->acl_cnt);
587
588 /*
589 * Did ACL grow?
590 */
591
592 if ((slot + newentries->acl_cnt) > acl1->acl_cnt) {
593 acl1->acl_cnt = slot + newentries->acl_cnt;
594 }
595
596 return (0);
597 }
598
599 /*
600 * Add acl2 entries into acl1. The where argument specifies where
601 * to add the entries.
602 */
603 int
acl_addentries(acl_t * acl1,acl_t * acl2,int where)604 acl_addentries(acl_t *acl1, acl_t *acl2, int where)
605 {
606
607 int newsize;
608 int len;
609 void *start;
610 void *to;
611
612 if (acl1 == NULL || acl2 == NULL)
613 return (EACL_NO_ACL_ENTRY);
614
615 if (acl1->acl_type != acl2->acl_type)
616 return (EACL_DIFF_TYPE);
617
618 /*
619 * allow where to specify 1 past last slot for an append operation
620 * but anything greater is an error.
621 */
622 if (where < 0 || where > acl1->acl_cnt)
623 return (EACL_INVALID_SLOT);
624
625 newsize = (acl2->acl_entry_size * acl2->acl_cnt) +
626 (acl1->acl_entry_size * acl1->acl_cnt);
627 acl1->acl_aclp = realloc(acl1->acl_aclp, newsize);
628 if (acl1->acl_aclp == NULL)
629 return (-1);
630
631 /*
632 * first push down entries where new ones will be inserted
633 */
634
635 to = (void *)((char *)acl1->acl_aclp +
636 ((where + acl2->acl_cnt) * acl1->acl_entry_size));
637
638 start = (void *)((char *)acl1->acl_aclp +
639 where * acl1->acl_entry_size);
640
641 if (where < acl1->acl_cnt) {
642 len = (acl1->acl_cnt - where) * acl1->acl_entry_size;
643 (void) memmove(to, start, len);
644 }
645
646 /*
647 * now stick in new entries.
648 */
649
650 (void) memmove(start, acl2->acl_aclp,
651 acl2->acl_cnt * acl2->acl_entry_size);
652
653 acl1->acl_cnt += acl2->acl_cnt;
654 return (0);
655 }
656
657 /*
658 * return text for an ACL error.
659 */
660 char *
acl_strerror(int errnum)661 acl_strerror(int errnum)
662 {
663 switch (errnum) {
664 case EACL_GRP_ERROR:
665 return (dgettext(TEXT_DOMAIN,
666 "There is more than one group or default group entry"));
667 case EACL_USER_ERROR:
668 return (dgettext(TEXT_DOMAIN,
669 "There is more than one user or default user entry"));
670 case EACL_OTHER_ERROR:
671 return (dgettext(TEXT_DOMAIN,
672 "There is more than one other entry"));
673 case EACL_CLASS_ERROR:
674 return (dgettext(TEXT_DOMAIN,
675 "There is more than one mask entry"));
676 case EACL_DUPLICATE_ERROR:
677 return (dgettext(TEXT_DOMAIN,
678 "Duplicate user or group entries"));
679 case EACL_MISS_ERROR:
680 return (dgettext(TEXT_DOMAIN,
681 "Missing user/group owner, other, mask entry"));
682 case EACL_MEM_ERROR:
683 return (dgettext(TEXT_DOMAIN,
684 "Memory error"));
685 case EACL_ENTRY_ERROR:
686 return (dgettext(TEXT_DOMAIN,
687 "Unrecognized entry type"));
688 case EACL_INHERIT_ERROR:
689 return (dgettext(TEXT_DOMAIN,
690 "Invalid inheritance flags"));
691 case EACL_FLAGS_ERROR:
692 return (dgettext(TEXT_DOMAIN,
693 "Unrecognized entry flags"));
694 case EACL_PERM_MASK_ERROR:
695 return (dgettext(TEXT_DOMAIN,
696 "Invalid ACL permissions"));
697 case EACL_COUNT_ERROR:
698 return (dgettext(TEXT_DOMAIN,
699 "Invalid ACL count"));
700 case EACL_INVALID_SLOT:
701 return (dgettext(TEXT_DOMAIN,
702 "Invalid ACL entry number specified"));
703 case EACL_NO_ACL_ENTRY:
704 return (dgettext(TEXT_DOMAIN,
705 "ACL entry doesn't exist"));
706 case EACL_DIFF_TYPE:
707 return (dgettext(TEXT_DOMAIN,
708 "Different file system ACL types cannot be merged"));
709 case EACL_INVALID_USER_GROUP:
710 return (dgettext(TEXT_DOMAIN, "Invalid user or group"));
711 case EACL_INVALID_STR:
712 return (dgettext(TEXT_DOMAIN, "ACL string is invalid"));
713 case EACL_FIELD_NOT_BLANK:
714 return (dgettext(TEXT_DOMAIN, "Field expected to be blank"));
715 case EACL_INVALID_ACCESS_TYPE:
716 return (dgettext(TEXT_DOMAIN, "Invalid access type"));
717 case EACL_UNKNOWN_DATA:
718 return (dgettext(TEXT_DOMAIN, "Unrecognized entry"));
719 case EACL_MISSING_FIELDS:
720 return (dgettext(TEXT_DOMAIN,
721 "ACL specification missing required fields"));
722 case EACL_INHERIT_NOTDIR:
723 return (dgettext(TEXT_DOMAIN,
724 "Inheritance flags are only allowed on directories"));
725 case -1:
726 return (strerror(errno));
727 default:
728 errno = EINVAL;
729 return (dgettext(TEXT_DOMAIN, "Unknown error"));
730 }
731 }
732
733 extern int yyinteractive;
734
735 /* PRINTFLIKE1 */
736 void
acl_error(const char * fmt,...)737 acl_error(const char *fmt, ...)
738 {
739 va_list va;
740
741 if (yyinteractive == 0)
742 return;
743
744 va_start(va, fmt);
745 (void) vfprintf(stderr, fmt, va);
746 va_end(va);
747 }
748
749 typedef enum id_type {
750 UID_TYPE,
751 GID_TYPE,
752 PID_TYPE
753 } id_type_t;
754
755 static int
sid_to_id_impl(char * sid,id_type_t type,int * is_user,uid_t * id)756 sid_to_id_impl(char *sid, id_type_t type, int *is_user, uid_t *id)
757 {
758 idmap_get_handle_t *get_hdl;
759 char *rid_start;
760 idmap_stat rv;
761 idmap_rid_t rid;
762 const char *errstr;
763 idmap_stat status;
764 int error = 1;
765
766 rid_start = strrchr(sid, '-');
767 if (rid_start == NULL)
768 return (error);
769
770 rid = strtonum(rid_start + 1, 0, UINT32_MAX, &errstr);
771 if (errstr != NULL)
772 return (error);
773
774 if (idmap_get_create(&get_hdl) != IDMAP_SUCCESS)
775 return (error);
776
777 /*
778 * When these functions return success, the &status output is
779 * indeterminate. We only care about rv==success in this caller,
780 * so just ignore &status.
781 */
782 /* We need sid prefix. Insert NUL on '-', restore it later. */
783 *rid_start = '\0';
784 switch (type) {
785 case UID_TYPE:
786 rv = idmap_get_uidbysid(get_hdl,
787 sid, rid, IDMAP_REQ_FLG_USE_CACHE,
788 id, &status);
789 break;
790
791 case GID_TYPE:
792 rv = idmap_get_gidbysid(get_hdl,
793 sid, rid, IDMAP_REQ_FLG_USE_CACHE,
794 id, &status);
795 break;
796
797 case PID_TYPE:
798 rv = idmap_get_pidbysid(get_hdl, sid, rid,
799 IDMAP_REQ_FLG_USE_CACHE, id, is_user,
800 &status);
801 break;
802 }
803
804 *rid_start = '-'; /* putback character removed earlier */
805 if (rv == IDMAP_SUCCESS &&
806 idmap_get_mappings(get_hdl) == IDMAP_SUCCESS) {
807 error = 0;
808 }
809 idmap_get_destroy(get_hdl);
810
811 return (error);
812 }
813
814 int
sid_to_id(char * sid,boolean_t user,uid_t * id)815 sid_to_id(char *sid, boolean_t user, uid_t *id)
816 {
817 char *domain_start;
818 int error = 1;
819
820 if ((domain_start = strchr(sid, '@')) == NULL) {
821 error = sid_to_id_impl(sid, user ? UID_TYPE : GID_TYPE,
822 NULL, id);
823 } else {
824 char *name = sid;
825 idmap_stat rv;
826
827 *domain_start++ = '\0';
828
829 if (user)
830 rv = idmap_getuidbywinname(name, domain_start,
831 IDMAP_REQ_FLG_USE_CACHE, id);
832 else
833 rv = idmap_getgidbywinname(name, domain_start,
834 IDMAP_REQ_FLG_USE_CACHE, id);
835 *--domain_start = '@';
836 if (rv == IDMAP_SUCCESS)
837 error = 0;
838 }
839
840 return (error);
841 }
842
843 /*
844 * Variant of sid_to_id() called when we don't know whether the SID
845 * is a user or group. 2nd arg gets the type (0:group, 1:user)
846 * Returns zero for success, 1 for errors.
847 */
848 int
sid_to_xid(char * sid,int * is_user,uid_t * id)849 sid_to_xid(char *sid, int *is_user, uid_t *id)
850 {
851 if ((strchr(sid, '@')) != NULL)
852 return (1);
853
854 return (sid_to_id_impl(sid, PID_TYPE, is_user, id));
855 }
856