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 */
24
25 /*
26 * setfacl [-r] -f aclfile file ...
27 * setfacl [-r] -d acl_entries file ...
28 * setfacl [-r] -m acl_entries file ...
29 * setfacl [-r] -s acl_entries file ...
30 * This command deletes/adds/modifies/sets discretionary information for a file
31 * or files.
32 */
33
34 #include <stdlib.h>
35 #include <stdio.h>
36 #include <pwd.h>
37 #include <grp.h>
38 #include <string.h>
39 #include <locale.h>
40 #include <sys/acl.h>
41 #include <sys/types.h>
42 #include <unistd.h>
43 #include <errno.h>
44
45 #define ADD 1
46 #define MODIFY 2
47 #define DELETE 3
48 #define SET 4
49
50 static int get_acl_info(char *filep, aclent_t **aclpp);
51 static int mod_entries(aclent_t *, int, char *, char *, char *, int);
52 static int set_file_entries(char *, char *, int);
53 static int set_online_entries(char *, char *, int);
54 static void usage();
55 static int parse_entry_list(aclent_t **, int *, char *, int);
56 static int convert_to_aclent_t(char *, int *, aclent_t **, int);
57 static int parse_entry(char *, aclent_t *, int);
58 static void err_handle(int, aclent_t *);
59 static int conv_id(char *);
60
61 int
62 main(int argc, char *argv[])
63 {
64 int c;
65 int dflag = 0;
66 int mflag = 0;
67 int rflag = 0;
68 int sflag = 0;
69 int fflag = 0;
70 int errflag = 0;
71 int aclcnt; /* used by -m -d */
72 aclent_t *aclp; /* used by -m -d */
73 char *aclfilep; /* acl file argument */
74 char *d_entryp = NULL; /* ptr to del entry list */
75 char *m_entryp = NULL; /* ptr to mod entry list */
76 char *s_entryp = NULL; /* ptr to set entry list */
77 char *work_dp = NULL; /* working ptrs for the above */
78 char *work_mp = NULL;
79 char *work_sp = NULL;
80
81 (void) setlocale(LC_ALL, "");
82 (void) textdomain(TEXT_DOMAIN);
83
84 if (argc < 3)
85 usage();
86
87 while ((c = getopt(argc, argv, "rm:d:s:f:")) != EOF) {
88 switch (c) {
89 case 'r':
90 rflag++;
91 break;
92 case 'd':
93 if (dflag || fflag || sflag)
182 exit(2);
183 }
184 }
185 return (0);
186 }
187
188 /*
189 * For add, modify, and delete, we need to get the ACL of the file first.
190 */
191 static int
192 get_acl_info(char *filep, aclent_t **aclpp)
193 {
194 int aclcnt;
195
196 if ((aclcnt = acl(filep, GETACLCNT, 0, NULL)) < 0) {
197 if (errno == ENOSYS) {
198 (void) fprintf(stderr,
199 gettext("File system doesn't support aclent_t "
200 "style ACL's.\n"
201 "See acl(5) for more information on"
202 " ACL styles support by Solaris.\n"));
203 return (-1);
204 }
205 (void) fprintf(stderr,
206 gettext("%s: failed to get acl count\n"), filep);
207 perror("get acl count error");
208 return (-1);
209 }
210 if (aclcnt < MIN_ACL_ENTRIES) {
211 (void) fprintf(stderr,
212 gettext("%d: acl count is too small from %s\n"),
213 aclcnt, filep);
214 return (-1);
215 }
216
217 if ((*aclpp = (aclent_t *)malloc(sizeof (aclent_t) * aclcnt)) == NULL) {
218 (void) fprintf(stderr, gettext("out of memory\n"));
219 return (-1);
220 }
221 if (acl(filep, GETACL, aclcnt, *aclpp) < 0) {
222 (void) fprintf(stderr,
223 gettext("%s: failed to get acl entries\n"), filep);
224 perror("getacl error");
225 return (-1);
226 }
227 return (aclcnt);
228 }
229
230 /*
231 * mod_entries() handles add, delete, and modify ACL entries of a file.
232 * The real action is in convert_to_aclent_t() called by parse_entry_list().
233 * aclp: points ACL of a file and may be changed by lower level routine.
234 * modp: modify entry list in ascii format
235 * delp: delete entry list in ascii format
236 * fnamep: file of interest
237 */
238 static int
239 mod_entries(aclent_t *aclp, int cnt, char *modp, char *delp,
240 char *fnamep, int rfg)
241 {
242 int rc; /* return code */
243
244 /* modify and add: from -m option */
245 if (parse_entry_list(&aclp, &cnt, modp, MODIFY) == -1)
246 return (-1);
247
248 /* deletion: from -d option */
249 if (parse_entry_list(&aclp, &cnt, delp, DELETE) == -1)
250 return (-1);
251
252 if (aclsort(cnt, rfg, aclp) == -1) {
253 (void) err_handle(cnt, aclp);
254 (void) fprintf(stderr,
255 gettext("aclcnt %d, file %s\n"), cnt, fnamep);
256 return (-1);
257 }
258
259 if (acl(fnamep, SETACL, cnt, aclp) < 0) {
260 fprintf(stderr,
261 gettext("%s: failed to set acl entries\n"), fnamep);
262 perror("setacl error");
263 return (-1);
325 }
326
327 if (acl(fnamep, SETACL, aclcnt, aclp) < 0) {
328 fprintf(stderr,
329 gettext("%s: failed to set acl entries\n"), fnamep);
330 perror("setacl error");
331 return (-1);
332 }
333 return (0);
334 }
335
336 /*
337 * set_online_entries() parses the acl entries from command line (setp).
338 * It converts the comma separated acl entries into aclent_t format.
339 * It then recalculates the mask according to rflag.
340 * Finally it sets ACL to the file (fnamep).
341 */
342 static int
343 set_online_entries(char *setp, char *fnamep, int rflag)
344 {
345 char *commap;
346 aclent_t *aclp;
347 int aclcnt = 0;
348
349 if (parse_entry_list(&aclp, &aclcnt, setp, SET) == -1)
350 return (-1);
351
352 if (aclsort(aclcnt, rflag, aclp) == -1) {
353 (void) err_handle(aclcnt, aclp);
354 (void) fprintf(stderr,
355 gettext("aclcnt %d, file %s\n"), aclcnt, fnamep);
356 return (-1);
357 }
358
359 if (acl(fnamep, SETACL, aclcnt, aclp) < 0) {
360 fprintf(stderr,
361 gettext("%s: failed to set acl entries\n"), fnamep);
362 perror("setacl error");
363 return (-1);
364 }
365 return (0);
397 /*
398 * convert_to_aclent_t() converts an acl entry in ascii format (fields separated
399 * by colon) into aclent_t and appends it to the current ACL. It also handles
400 * memory allocation/deallocation for acl entries in aclent_t format.
401 * aclpp that contains acl entries in acl format will be returned.
402 * We don't check duplicates.
403 */
404 static int
405 convert_to_aclent_t(char *entryp, int *cntp, aclent_t **aclpp, int mode)
406 {
407 aclent_t *new_aclp;
408 aclent_t tmpacl;
409 aclent_t *taclp, *centry = NULL, *gentry = NULL;
410 int cur_cnt;
411 int found = 0;
412 int is_obj;
413
414 if (entryp == NULL)
415 return (0);
416
417 if (*cntp > 1)
418 new_aclp = (aclent_t *)realloc(*aclpp,
419 sizeof (aclent_t) * (*cntp));
420 else
421 new_aclp = (aclent_t *) malloc(sizeof (aclent_t) * (*cntp));
422 if (new_aclp == NULL) {
423 fprintf(stderr,
424 gettext("Insufficient memory for acl %d\n"), *cntp);
425 return (-1);
426 }
427
428 tmpacl.a_id = 0; /* id field needs to be initialized */
429 if (entryp[0] == 'u')
430 tmpacl.a_id = getuid(); /* id field for user */
431 if (entryp[0] == 'g')
432 tmpacl.a_id = getgid(); /* id field for group */
433
434 tmpacl.a_type = 0;
435 if (parse_entry(entryp, &tmpacl, mode) == -1)
436 return (-1);
437
438 is_obj = ((tmpacl.a_type == USER_OBJ) ||
439 (tmpacl.a_type == GROUP_OBJ) ||
440 (tmpacl.a_type == CLASS_OBJ) ||
441 (tmpacl.a_type == DEF_USER_OBJ) ||
442 (tmpacl.a_type == DEF_GROUP_OBJ) ||
443 (tmpacl.a_type == DEF_OTHER_OBJ));
444
445 cur_cnt = *cntp - 1;
446 switch (mode) {
447 case MODIFY: /* and add */
448 for (taclp = new_aclp; cur_cnt-- > 0; taclp++) {
449 if (taclp->a_type == tmpacl.a_type &&
450 ((taclp->a_id == tmpacl.a_id) || is_obj)) {
451 found++;
452 /* cnt is added before it's called */
453 *cntp -= 1;
454 taclp->a_perm = tmpacl.a_perm;
455 break;
456 }
457 }
458 if (!found) /* Add it to the end: no need to change cntp */
459 memcpy(new_aclp + *cntp -1, &tmpacl, sizeof (aclent_t));
460 break;
461
462 case DELETE:
463 for (taclp = new_aclp; cur_cnt-- > 0; taclp++) {
464 if (taclp->a_type == tmpacl.a_type &&
|
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 2020 Peter Tribble.
24 */
25
26 /*
27 * setfacl [-r] -f aclfile file ...
28 * setfacl [-r] -d acl_entries file ...
29 * setfacl [-r] -m acl_entries file ...
30 * setfacl [-r] -s acl_entries file ...
31 * This command deletes/adds/modifies/sets discretionary information for a file
32 * or files.
33 */
34
35 #include <stdlib.h>
36 #include <stdio.h>
37 #include <pwd.h>
38 #include <grp.h>
39 #include <string.h>
40 #include <locale.h>
41 #include <sys/acl.h>
42 #include <sys/types.h>
43 #include <unistd.h>
44 #include <errno.h>
45 #include <ctype.h>
46
47 #define ADD 1
48 #define MODIFY 2
49 #define DELETE 3
50 #define SET 4
51
52 static int get_acl_info(char *filep, aclent_t **aclpp);
53 static int mod_entries(aclent_t *, int, char *, char *, char *, int);
54 static int set_file_entries(char *, char *, int);
55 static int set_online_entries(char *, char *, int);
56 static void usage();
57 static int parse_entry_list(aclent_t **, int *, char *, int);
58 static int convert_to_aclent_t(char *, int *, aclent_t **, int);
59 static int parse_entry(char *, aclent_t *, int);
60 static void err_handle(int, aclent_t *);
61 static int conv_id(char *);
62
63 int
64 main(int argc, char *argv[])
65 {
66 int c;
67 int dflag = 0;
68 int mflag = 0;
69 int rflag = 0;
70 int sflag = 0;
71 int fflag = 0;
72 int errflag = 0;
73 int aclcnt; /* used by -m -d */
74 aclent_t *aclp; /* used by -m -d */
75 char *aclfilep = NULL; /* acl file argument */
76 char *d_entryp = NULL; /* ptr to del entry list */
77 char *m_entryp = NULL; /* ptr to mod entry list */
78 char *s_entryp = NULL; /* ptr to set entry list */
79 char *work_dp = NULL; /* working ptrs for the above */
80 char *work_mp = NULL;
81 char *work_sp = NULL;
82
83 (void) setlocale(LC_ALL, "");
84 (void) textdomain(TEXT_DOMAIN);
85
86 if (argc < 3)
87 usage();
88
89 while ((c = getopt(argc, argv, "rm:d:s:f:")) != EOF) {
90 switch (c) {
91 case 'r':
92 rflag++;
93 break;
94 case 'd':
95 if (dflag || fflag || sflag)
184 exit(2);
185 }
186 }
187 return (0);
188 }
189
190 /*
191 * For add, modify, and delete, we need to get the ACL of the file first.
192 */
193 static int
194 get_acl_info(char *filep, aclent_t **aclpp)
195 {
196 int aclcnt;
197
198 if ((aclcnt = acl(filep, GETACLCNT, 0, NULL)) < 0) {
199 if (errno == ENOSYS) {
200 (void) fprintf(stderr,
201 gettext("File system doesn't support aclent_t "
202 "style ACL's.\n"
203 "See acl(5) for more information on"
204 "POSIX-draft ACL support.\n"));
205 return (-1);
206 }
207 (void) fprintf(stderr,
208 gettext("%s: failed to get acl count\n"), filep);
209 perror("get acl count error");
210 return (-1);
211 }
212 if (aclcnt < MIN_ACL_ENTRIES) {
213 (void) fprintf(stderr,
214 gettext("%d: acl count is too small from %s\n"),
215 aclcnt, filep);
216 return (-1);
217 }
218
219 if ((*aclpp = (aclent_t *)malloc(sizeof (aclent_t) * aclcnt)) == NULL) {
220 (void) fprintf(stderr, gettext("out of memory\n"));
221 return (-1);
222 }
223 if (acl(filep, GETACL, aclcnt, *aclpp) < 0) {
224 (void) fprintf(stderr,
225 gettext("%s: failed to get acl entries\n"), filep);
226 perror("getacl error");
227 return (-1);
228 }
229 return (aclcnt);
230 }
231
232 /*
233 * mod_entries() handles add, delete, and modify ACL entries of a file.
234 * The real action is in convert_to_aclent_t() called by parse_entry_list().
235 * aclp: points ACL of a file and may be changed by lower level routine.
236 * modp: modify entry list in ascii format
237 * delp: delete entry list in ascii format
238 * fnamep: file of interest
239 */
240 static int
241 mod_entries(aclent_t *aclp, int cnt, char *modp, char *delp,
242 char *fnamep, int rfg)
243 {
244 /* modify and add: from -m option */
245 if (parse_entry_list(&aclp, &cnt, modp, MODIFY) == -1)
246 return (-1);
247
248 /* deletion: from -d option */
249 if (parse_entry_list(&aclp, &cnt, delp, DELETE) == -1)
250 return (-1);
251
252 if (aclsort(cnt, rfg, aclp) == -1) {
253 (void) err_handle(cnt, aclp);
254 (void) fprintf(stderr,
255 gettext("aclcnt %d, file %s\n"), cnt, fnamep);
256 return (-1);
257 }
258
259 if (acl(fnamep, SETACL, cnt, aclp) < 0) {
260 fprintf(stderr,
261 gettext("%s: failed to set acl entries\n"), fnamep);
262 perror("setacl error");
263 return (-1);
325 }
326
327 if (acl(fnamep, SETACL, aclcnt, aclp) < 0) {
328 fprintf(stderr,
329 gettext("%s: failed to set acl entries\n"), fnamep);
330 perror("setacl error");
331 return (-1);
332 }
333 return (0);
334 }
335
336 /*
337 * set_online_entries() parses the acl entries from command line (setp).
338 * It converts the comma separated acl entries into aclent_t format.
339 * It then recalculates the mask according to rflag.
340 * Finally it sets ACL to the file (fnamep).
341 */
342 static int
343 set_online_entries(char *setp, char *fnamep, int rflag)
344 {
345 aclent_t *aclp;
346 int aclcnt = 0;
347
348 if (parse_entry_list(&aclp, &aclcnt, setp, SET) == -1)
349 return (-1);
350
351 if (aclsort(aclcnt, rflag, aclp) == -1) {
352 (void) err_handle(aclcnt, aclp);
353 (void) fprintf(stderr,
354 gettext("aclcnt %d, file %s\n"), aclcnt, fnamep);
355 return (-1);
356 }
357
358 if (acl(fnamep, SETACL, aclcnt, aclp) < 0) {
359 fprintf(stderr,
360 gettext("%s: failed to set acl entries\n"), fnamep);
361 perror("setacl error");
362 return (-1);
363 }
364 return (0);
396 /*
397 * convert_to_aclent_t() converts an acl entry in ascii format (fields separated
398 * by colon) into aclent_t and appends it to the current ACL. It also handles
399 * memory allocation/deallocation for acl entries in aclent_t format.
400 * aclpp that contains acl entries in acl format will be returned.
401 * We don't check duplicates.
402 */
403 static int
404 convert_to_aclent_t(char *entryp, int *cntp, aclent_t **aclpp, int mode)
405 {
406 aclent_t *new_aclp;
407 aclent_t tmpacl;
408 aclent_t *taclp, *centry = NULL, *gentry = NULL;
409 int cur_cnt;
410 int found = 0;
411 int is_obj;
412
413 if (entryp == NULL)
414 return (0);
415
416 tmpacl.a_id = 0; /* id field needs to be initialized */
417 if (entryp[0] == 'u')
418 tmpacl.a_id = getuid(); /* id field for user */
419 if (entryp[0] == 'g')
420 tmpacl.a_id = getgid(); /* id field for group */
421
422 tmpacl.a_type = 0;
423 if (parse_entry(entryp, &tmpacl, mode) == -1)
424 return (-1);
425
426 is_obj = ((tmpacl.a_type == USER_OBJ) ||
427 (tmpacl.a_type == GROUP_OBJ) ||
428 (tmpacl.a_type == CLASS_OBJ) ||
429 (tmpacl.a_type == DEF_USER_OBJ) ||
430 (tmpacl.a_type == DEF_GROUP_OBJ) ||
431 (tmpacl.a_type == DEF_OTHER_OBJ));
432
433 if (*cntp > 1)
434 new_aclp = (aclent_t *)realloc(*aclpp,
435 sizeof (aclent_t) * (*cntp));
436 else
437 new_aclp = (aclent_t *) malloc(sizeof (aclent_t) * (*cntp));
438 if (new_aclp == NULL) {
439 fprintf(stderr,
440 gettext("Insufficient memory for acl %d\n"), *cntp);
441 return (-1);
442 }
443
444 cur_cnt = *cntp - 1;
445 switch (mode) {
446 case MODIFY: /* and add */
447 for (taclp = new_aclp; cur_cnt-- > 0; taclp++) {
448 if (taclp->a_type == tmpacl.a_type &&
449 ((taclp->a_id == tmpacl.a_id) || is_obj)) {
450 found++;
451 /* cnt is added before it's called */
452 *cntp -= 1;
453 taclp->a_perm = tmpacl.a_perm;
454 break;
455 }
456 }
457 if (!found) /* Add it to the end: no need to change cntp */
458 memcpy(new_aclp + *cntp -1, &tmpacl, sizeof (aclent_t));
459 break;
460
461 case DELETE:
462 for (taclp = new_aclp; cur_cnt-- > 0; taclp++) {
463 if (taclp->a_type == tmpacl.a_type &&
|