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) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
24 */
25
26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
27 /* All Rights Reserved */
28 /* */
29
30 /*
31 * University Copyright- Copyright (c) 1982, 1986, 1988
32 * The Regents of the University of California
33 * All Rights Reserved
34 *
35 * University Acknowledgment- Portions of this document are derived from
36 * software developed by the University of California, Berkeley, and its
37 * contributors.
38 */
39
40 /*
41 * chmod option mode files
42 * where
43 * mode is [ugoa][+-=][rwxXlstugo] or an octal number
44 * mode is [<+|->A[# <number] ]<aclspec>
45 * mode is S<attrspec>
46 * option is -R, -f, and -@
47 */
48
49 /*
50 * Note that many convolutions are necessary
51 * due to the re-use of bits between locking
52 * and setgid
53 */
54
55 #include <unistd.h>
56 #include <stdlib.h>
57 #include <stdio.h>
58 #include <sys/types.h>
59 #include <sys/stat.h>
60 #include <fcntl.h>
61 #include <dirent.h>
62 #include <locale.h>
63 #include <string.h> /* strerror() */
64 #include <stdarg.h>
65 #include <limits.h>
66 #include <ctype.h>
67 #include <errno.h>
68 #include <sys/acl.h>
69 #include <aclutils.h>
70 #include <libnvpair.h>
71 #include <libcmdutils.h>
72 #include <libgen.h>
73 #include <attr.h>
74 #include "msgs.h"
75
76 static int rflag;
77 static int fflag;
78
79 extern int optind;
80 extern int errno;
81
82 static int mac; /* Alternate to argc (for parseargs) */
83 static char **mav; /* Alternate to argv (for parseargs) */
84
85 static char *ms; /* Points to the mode argument */
86
87 #define ACL_ADD 1
88 #define ACL_DELETE 2
89 #define ACL_SLOT_DELETE 3
90 #define ACL_REPLACE 4
91 #define ACL_STRIP 5
92
93 #define LEFTBRACE '{'
94 #define RIGHTBRACE '}'
95 #define A_SEP ','
96 #define A_SEP_TOK ","
97
98 #define A_COMPACT_TYPE 'c'
99 #define A_VERBOSE_TYPE 'v'
100 #define A_ALLATTRS_TYPE 'a'
101
102 #define A_SET_OP '+'
103 #define A_INVERSE_OP '-'
104 #define A_REPLACE_OP '='
105 #define A_UNDEF_OP '\0'
106
107 #define A_SET_TEXT "set"
108 #define A_INVERSE_TEXT "clear"
109
110 #define A_SET_VAL B_TRUE
111 #define A_CLEAR_VAL B_FALSE
112
113 #define ATTR_OPTS 0
114 #define ATTR_NAMES 1
115
116 #define sec_acls secptr.acls
117 #define sec_attrs secptr.attrs
118
119 typedef struct acl_args {
120 acl_t *acl_aclp;
121 int acl_slot;
122 int acl_action;
123 } acl_args_t;
124
125 typedef enum {
126 SEC_ACL,
127 SEC_ATTR
128 } chmod_sec_t;
129
130 typedef struct {
131 chmod_sec_t sec_type;
132 union {
133 acl_args_t *acls;
134 nvlist_t *attrs;
135 } secptr;
136 } sec_args_t;
137
138 typedef struct attr_name {
139 char *name;
140 struct attr_name *next;
141 } attr_name_t;
142
143
144 extern mode_t newmode_common(char *ms, mode_t new_mode, mode_t umsk,
145 char *file, char *path, o_mode_t *group_clear_bits,
146 o_mode_t *group_set_bits);
147
148 static int chmodr(char *dir, char *path, mode_t mode, mode_t umsk,
149 sec_args_t *secp, attr_name_t *attrname);
150 static int doacl(char *file, struct stat *st, acl_args_t *aclp);
151 static int dochmod(char *name, char *path, mode_t umsk, sec_args_t *secp,
152 attr_name_t *attrnames);
153 static void handle_acl(char *name, o_mode_t group_clear_bits,
154 o_mode_t group_set_bits);
155 void errmsg(int severity, int code, char *format, ...);
156 static void free_attr_names(attr_name_t *attrnames);
157 static void parseargs(int ac, char *av[]);
158 static int parse_acl_args(char *arg, sec_args_t **sec_args);
159 static int parse_attr_args(char *arg, sec_args_t **sec_args);
160 static void print_attrs(int flag);
161 static int set_attrs(char *file, attr_name_t *attrnames, nvlist_t *attr_nvlist);
162 static void usage(void);
163
164 int
main(int argc,char * argv[])165 main(int argc, char *argv[])
166 {
167 int i, c;
168 int status = 0;
169 mode_t umsk;
170 sec_args_t *sec_args = NULL;
171 attr_name_t *attrnames = NULL;
172 attr_name_t *attrend = NULL;
173 attr_name_t *tattr;
174
175 (void) setlocale(LC_ALL, "");
176 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
177 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
178 #endif
179 (void) textdomain(TEXT_DOMAIN);
180
181 parseargs(argc, argv);
182
183 while ((c = getopt(mac, mav, "Rf@:")) != EOF) {
184 switch (c) {
185 case 'R':
186 rflag++;
187 break;
188 case 'f':
189 fflag++;
190 break;
191 case '@':
192 if (((tattr = malloc(sizeof (attr_name_t))) == NULL) ||
193 ((tattr->name = strdup(optarg)) == NULL)) {
194 perror("chmod");
195 exit(2);
196 }
197 if (attrnames == NULL) {
198 attrnames = tattr;
199 attrnames->next = NULL;
200 } else {
201 attrend->next = tattr;
202 }
203 attrend = tattr;
204 break;
205 case '?':
206 usage();
207 exit(2);
208 }
209 }
210
211 /*
212 * Check for sufficient arguments
213 * or a usage error.
214 */
215
216 mac -= optind;
217 mav += optind;
218 if ((mac >= 2) && (mav[0][0] == 'A')) {
219 if (attrnames != NULL) {
220 free_attr_names(attrnames);
221 attrnames = NULL;
222 }
223 if (parse_acl_args(*mav, &sec_args)) {
224 usage();
225 exit(2);
226 }
227 } else if ((mac >= 2) && (mav[0][0] == 'S')) {
228 if (parse_attr_args(*mav, &sec_args)) {
229 usage();
230 exit(2);
231
232 /* A no-op attribute operation was specified. */
233 } else if (sec_args->sec_attrs == NULL) {
234 exit(0);
235 }
236 } else {
237 if (mac < 2) {
238 usage();
239 exit(2);
240 }
241 if (attrnames != NULL) {
242 free_attr_names(attrnames);
243 attrnames = NULL;
244 }
245 }
246
247 ms = mav[0];
248
249 umsk = umask(0);
250 (void) umask(umsk);
251
252 for (i = 1; i < mac; i++) {
253 status += dochmod(mav[i], mav[i], umsk, sec_args, attrnames);
254 }
255
256 return (fflag ? 0 : status);
257 }
258
259 static void
free_attr_names(attr_name_t * attrnames)260 free_attr_names(attr_name_t *attrnames)
261 {
262 attr_name_t *attrnamesptr = attrnames;
263 attr_name_t *tptr;
264
265 while (attrnamesptr != NULL) {
266 tptr = attrnamesptr->next;
267 if (attrnamesptr->name != NULL) {
268 free(attrnamesptr->name);
269 }
270 attrnamesptr = tptr;
271 }
272 }
273
274 static int
dochmod(char * name,char * path,mode_t umsk,sec_args_t * secp,attr_name_t * attrnames)275 dochmod(char *name, char *path, mode_t umsk, sec_args_t *secp,
276 attr_name_t *attrnames)
277 {
278 static struct stat st;
279 int linkflg = 0;
280 o_mode_t group_clear_bits, group_set_bits;
281
282 if (lstat(name, &st) < 0) {
283 errmsg(2, 0, gettext("can't access %s\n"), path);
284 return (1);
285 }
286
287 if ((st.st_mode & S_IFMT) == S_IFLNK) {
288 linkflg = 1;
289 if (stat(name, &st) < 0) {
290 errmsg(2, 0, gettext("can't access %s\n"), path);
291 return (1);
292 }
293 }
294
295 /* Do not recurse if directory is object of symbolic link */
296 if (rflag && ((st.st_mode & S_IFMT) == S_IFDIR) && !linkflg) {
297 return (chmodr(name, path, st.st_mode, umsk, secp, attrnames));
298 }
299
300 if (secp != NULL) {
301 if (secp->sec_type == SEC_ACL) {
302 return (doacl(name, &st, secp->sec_acls));
303 } else if (secp->sec_type == SEC_ATTR) {
304 return (set_attrs(name, attrnames, secp->sec_attrs));
305 } else {
306 return (1);
307 }
308 } else {
309 if (chmod(name, newmode_common(ms, st.st_mode, umsk, name, path,
310 &group_clear_bits, &group_set_bits)) == -1) {
311 errmsg(2, 0, gettext("can't change %s\n"), path);
312 return (1);
313 }
314 }
315
316 /*
317 * If the group permissions of the file are being modified,
318 * make sure that the file's ACL (if it has one) is
319 * modified also, since chmod is supposed to apply group
320 * permissions changes to both the acl mask and the
321 * general group permissions.
322 */
323 if (group_clear_bits || group_set_bits)
324 handle_acl(name, group_clear_bits, group_set_bits);
325
326 return (0);
327 }
328
329 static int
chmodr(char * dir,char * path,mode_t mode,mode_t umsk,sec_args_t * secp,attr_name_t * attrnames)330 chmodr(char *dir, char *path, mode_t mode, mode_t umsk, sec_args_t *secp,
331 attr_name_t *attrnames)
332 {
333
334 DIR *dirp;
335 struct dirent *dp;
336 char savedir[PATH_MAX]; /* dir name to restore */
337 char currdir[PATH_MAX+1]; /* current dir name + '/' */
338 char parentdir[PATH_MAX+1]; /* parent dir name + '/' */
339 int ecode;
340 struct stat st;
341 o_mode_t group_clear_bits, group_set_bits;
342
343 if (getcwd(savedir, PATH_MAX) == 0)
344 errmsg(2, 255, gettext("chmod: could not getcwd %s\n"),
345 savedir);
346
347 /*
348 * Change what we are given before doing it's contents
349 */
350 if (secp != NULL) {
351 if (lstat(dir, &st) < 0) {
352 errmsg(2, 0, gettext("can't access %s\n"), path);
353 return (1);
354 }
355 if (secp->sec_type == SEC_ACL) {
356 (void) doacl(dir, &st, secp->sec_acls);
357 } else if (secp->sec_type == SEC_ATTR) {
358 (void) set_attrs(dir, attrnames, secp->sec_attrs);
359 } else {
360 return (1);
361 }
362 } else if (chmod(dir, newmode_common(ms, mode, umsk, dir, path,
363 &group_clear_bits, &group_set_bits)) < 0) {
364 errmsg(2, 0, gettext("can't change %s\n"), path);
365 }
366
367 /*
368 * If the group permissions of the file are being modified,
369 * make sure that the file's ACL (if it has one) is
370 * modified also, since chmod is supposed to apply group
371 * permissions changes to both the acl mask and the
372 * general group permissions.
373 */
374
375 if (secp != NULL) {
376 /* only necessary when not setting ACL or system attributes */
377 if (group_clear_bits || group_set_bits)
378 handle_acl(dir, group_clear_bits, group_set_bits);
379 }
380
381 if (chdir(dir) < 0) {
382 errmsg(2, 0, "%s/%s: %s\n", savedir, dir, strerror(errno));
383 return (1);
384 }
385 if ((dirp = opendir(".")) == NULL) {
386 errmsg(2, 0, "%s\n", strerror(errno));
387 return (1);
388 }
389 ecode = 0;
390
391 /*
392 * Save parent directory path before recursive chmod.
393 * We'll need this for error printing purposes. Add
394 * a trailing '/' to the path except in the case where
395 * the path is just '/'
396 */
397
398 if (strlcpy(parentdir, path, PATH_MAX + 1) >= PATH_MAX + 1) {
399 errmsg(2, 0, gettext("directory path name too long: %s\n"),
400 path);
401 return (1);
402 }
403 if (strcmp(path, "/") != 0)
404 if (strlcat(parentdir, "/", PATH_MAX + 1) >= PATH_MAX + 1) {
405 errmsg(2, 0,
406 gettext("directory path name too long: %s/\n"),
407 parentdir);
408 return (1);
409 }
410
411
412 for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
413
414 if (strcmp(dp->d_name, ".") == 0 || /* skip . and .. */
415 strcmp(dp->d_name, "..") == 0) {
416 continue;
417 }
418 if (strlcpy(currdir, parentdir, PATH_MAX + 1) >= PATH_MAX + 1) {
419 errmsg(2, 0,
420 gettext("directory path name too long: %s\n"),
421 parentdir);
422 return (1);
423 }
424 if (strlcat(currdir, dp->d_name, PATH_MAX + 1)
425 >= PATH_MAX + 1) {
426 errmsg(2, 0,
427 gettext("directory path name too long: %s%s\n"),
428 currdir, dp->d_name);
429 return (1);
430 }
431 ecode += dochmod(dp->d_name, currdir, umsk, secp, attrnames);
432 }
433 (void) closedir(dirp);
434 if (chdir(savedir) < 0) {
435 errmsg(2, 255, gettext("can't change back to %s\n"), savedir);
436 }
437 return (ecode ? 1 : 0);
438 }
439
440 void
errmsg(int severity,int code,char * format,...)441 errmsg(int severity, int code, char *format, ...)
442 {
443 va_list ap;
444 static char *msg[] = {
445 "",
446 "ERROR",
447 "WARNING",
448 ""
449 };
450
451 va_start(ap, format);
452
453 /*
454 * Always print error message if this is a fatal error (code != 0);
455 * otherwise, print message if fflag == 0 (no -f option specified)
456 */
457 if (!fflag || (code != 0)) {
458 (void) fprintf(stderr,
459 "chmod: %s: ", gettext(msg[severity]));
460 (void) vfprintf(stderr, format, ap);
461 }
462
463 va_end(ap);
464
465 if (code != 0)
466 exit(fflag ? 0 : code);
467 }
468
469 static void
usage(void)470 usage(void)
471 {
472 (void) fprintf(stderr, gettext(
473 "usage:\tchmod [-fR] <absolute-mode> file ...\n"));
474
475 (void) fprintf(stderr, gettext(
476 "\tchmod [-fR] [-@ attribute] ... "
477 "S<attribute-operation> file ...\n"));
478
479 (void) fprintf(stderr, gettext(
480 "\tchmod [-fR] <ACL-operation> file ...\n"));
481
482 (void) fprintf(stderr, gettext(
483 "\tchmod [-fR] <symbolic-mode-list> file ...\n\n"));
484
485 (void) fprintf(stderr, gettext(
486 "where \t<symbolic-mode-list> is a comma-separated list of\n"));
487 (void) fprintf(stderr, gettext(
488 "\t[ugoa]{+|-|=}[rwxXlstugo]\n\n"));
489
490 (void) fprintf(stderr, gettext(
491 "where \t<attribute-operation> is a comma-separated list of\n"
492 "\tone or more of the following\n"));
493 (void) fprintf(stderr, gettext(
494 "\t[+|-|=]c[<compact-attribute-list>|{<compact-attribute-list>}]\n"
495 "\t[+|-|=]v[<verbose-attribute-setting>|"
496 "\'{\'<verbose-attribute-setting-list>\'}\']\n"
497 "\t[+|-|=]a\n"));
498 (void) fprintf(stderr, gettext(
499 "where \t<compact-attribute-list> is a list of zero or more of\n"));
500 print_attrs(ATTR_OPTS);
501 (void) fprintf(stderr, gettext(
502 "where \t<verbose-attribute-setting> is one of\n"));
503 print_attrs(ATTR_NAMES);
504 (void) fprintf(stderr, gettext(
505 "\tand can be, optionally, immediately preceded by \"no\"\n\n"));
506
507 (void) fprintf(stderr, gettext(
508 "where \t<ACL-operation> is one of the following\n"));
509 (void) fprintf(stderr, gettext("\tA-<acl_specification>\n"));
510 (void) fprintf(stderr, gettext("\tA[number]-\n"));
511 (void) fprintf(stderr, gettext(
512 "\tA[number]{+|=}<acl_specification>\n"));
513 (void) fprintf(stderr, gettext(
514 "where \t<acl-specification> is a comma-separated list of ACEs\n"));
515 }
516
517 /*
518 * parseargs - generate getopt-friendly argument list for backwards
519 * compatibility with earlier Solaris usage (eg, chmod -w
520 * foo).
521 *
522 * assumes the existence of a static set of alternates to argc and argv,
523 * (namely, mac, and mav[]).
524 *
525 */
526
527 static void
parseargs(int ac,char * av[])528 parseargs(int ac, char *av[])
529 {
530 int i; /* current argument */
531 int fflag; /* arg list contains "--" */
532 size_t mav_num; /* number of entries in mav[] */
533
534 /*
535 * We add an extra argument slot, in case we need to jam a "--"
536 * argument into the list.
537 */
538
539 mav_num = (size_t)ac+2;
540
541 if ((mav = calloc(mav_num, sizeof (char *))) == NULL) {
542 perror("chmod");
543 exit(2);
544 }
545
546 /* scan for the use of "--" in the argument list */
547
548 for (fflag = i = 0; i < ac; i ++) {
549 if (strcmp(av[i], "--") == 0)
550 fflag = 1;
551 }
552
553 /* process the arguments */
554
555 for (i = mac = 0;
556 (av[i] != NULL) && (av[i][0] != '\0');
557 i++) {
558 if (!fflag && av[i][0] == '-') {
559 /*
560 * If there is not already a "--" argument specified,
561 * and the argument starts with '-' but does not
562 * contain any of the official option letters, then it
563 * is probably a mode argument beginning with '-'.
564 * Force a "--" into the argument stream in front of
565 * it.
566 */
567
568 if ((strchr(av[i], 'R') == NULL &&
569 strchr(av[i], 'f') == NULL) &&
570 strchr(av[i], '@') == NULL) {
571 if ((mav[mac++] = strdup("--")) == NULL) {
572 perror("chmod");
573 exit(2);
574 }
575 }
576 }
577
578 if ((mav[mac++] = strdup(av[i])) == NULL) {
579 perror("chmod");
580 exit(2);
581 }
582 }
583
584 mav[mac] = (char *)NULL;
585 }
586
587 static int
parse_acl_args(char * arg,sec_args_t ** sec_args)588 parse_acl_args(char *arg, sec_args_t **sec_args)
589 {
590 acl_t *new_acl = NULL;
591 int slot;
592 int len;
593 int action;
594 acl_args_t *new_acl_args;
595 char *acl_spec = NULL;
596 char *end;
597
598 if (arg[0] != 'A')
599 return (1);
600
601 slot = strtol(&arg[1], &end, 10);
602
603 len = strlen(arg);
604 switch (*end) {
605 case '+':
606 action = ACL_ADD;
607 acl_spec = ++end;
608 break;
609 case '-':
610 if (len == 2 && arg[0] == 'A' && arg[1] == '-')
611 action = ACL_STRIP;
612 else
613 action = ACL_DELETE;
614 if (action != ACL_STRIP) {
615 acl_spec = ++end;
616 if (acl_spec[0] == '\0') {
617 action = ACL_SLOT_DELETE;
618 acl_spec = NULL;
619 } else if (arg[1] != '-')
620 return (1);
621 }
622 break;
623 case '=':
624 /*
625 * Was slot specified?
626 */
627 if (arg[1] == '=')
628 slot = -1;
629 action = ACL_REPLACE;
630 acl_spec = ++end;
631 break;
632 default:
633 return (1);
634 }
635
636 if ((action == ACL_REPLACE || action == ACL_ADD) && acl_spec[0] == '\0')
637 return (1);
638
639 if (acl_spec) {
640 if (acl_parse(acl_spec, &new_acl)) {
641 exit(1);
642 }
643 }
644
645 new_acl_args = malloc(sizeof (acl_args_t));
646 if (new_acl_args == NULL)
647 return (1);
648
649 new_acl_args->acl_aclp = new_acl;
650 new_acl_args->acl_slot = slot;
651 new_acl_args->acl_action = action;
652
653 if ((*sec_args = malloc(sizeof (sec_args_t))) == NULL) {
654 perror("chmod");
655 exit(2);
656 }
657 (*sec_args)->sec_type = SEC_ACL;
658 (*sec_args)->sec_acls = new_acl_args;
659
660 return (0);
661 }
662
663 /*
664 * This function is called whenever the group permissions of a file
665 * is being modified. According to the chmod(1) manpage, any
666 * change made to the group permissions must be applied to both
667 * the acl mask and the acl's GROUP_OBJ. The chmod(2) already
668 * set the mask, so this routine needs to make the same change
669 * to the GROUP_OBJ.
670 */
671 static void
handle_acl(char * name,o_mode_t group_clear_bits,o_mode_t group_set_bits)672 handle_acl(char *name, o_mode_t group_clear_bits, o_mode_t group_set_bits)
673 {
674 int aclcnt, n;
675 aclent_t *aclp, *tp;
676 o_mode_t newperm;
677 /*
678 * if this file system support ace_t acl's
679 * then simply return since we don't have an
680 * acl mask to deal with
681 */
682 if (pathconf(name, _PC_ACL_ENABLED) == _ACL_ACE_ENABLED)
683 return;
684 if ((aclcnt = acl(name, GETACLCNT, 0, NULL)) <= MIN_ACL_ENTRIES)
685 return; /* it's just a trivial acl; no need to change it */
686 if ((aclp = (aclent_t *)malloc((sizeof (aclent_t)) * aclcnt))
687 == NULL) {
688 perror("chmod");
689 exit(2);
690 }
691
692 if (acl(name, GETACL, aclcnt, aclp) < 0) {
693 free(aclp);
694 (void) fprintf(stderr, "chmod: ");
695 perror(name);
696 return;
697 }
698 for (tp = aclp, n = aclcnt; n--; tp++) {
699 if (tp->a_type == GROUP_OBJ) {
700 newperm = tp->a_perm;
701 if (group_clear_bits != 0)
702 newperm &= ~group_clear_bits;
703 if (group_set_bits != 0)
704 newperm |= group_set_bits;
705 if (newperm != tp->a_perm) {
706 tp->a_perm = newperm;
707 if (acl(name, SETACL, aclcnt, aclp)
708 < 0) {
709 (void) fprintf(stderr, "chmod: ");
710 perror(name);
711 }
712 }
713 break;
714 }
715 }
716 free(aclp);
717 }
718
719 static int
doacl(char * file,struct stat * st,acl_args_t * acl_args)720 doacl(char *file, struct stat *st, acl_args_t *acl_args)
721 {
722 acl_t *aclp;
723 acl_t *set_aclp;
724 int error = 0;
725 void *to, *from;
726 int len;
727 int isdir;
728 isdir = S_ISDIR(st->st_mode);
729
730 error = acl_get(file, 0, &aclp);
731
732 if (error != 0) {
733 errmsg(1, 0, "%s\n", acl_strerror(error));
734 return (1);
735 }
736 switch (acl_args->acl_action) {
737 case ACL_ADD:
738 if ((error = acl_addentries(aclp,
739 acl_args->acl_aclp, acl_args->acl_slot)) != 0) {
740 errmsg(1, 0, "%s\n", acl_strerror(error));
741 acl_free(aclp);
742 return (1);
743 }
744 set_aclp = aclp;
745 break;
746 case ACL_SLOT_DELETE:
747 if (acl_args->acl_slot + 1 > aclp->acl_cnt) {
748 errmsg(1, 0,
749 gettext("Invalid slot specified for removal\n"));
750 acl_free(aclp);
751 return (1);
752 }
753
754 if (acl_args->acl_slot == 0 && aclp->acl_cnt == 1) {
755 errmsg(1, 0,
756 gettext("Can't remove all ACL "
757 "entries from a file\n"));
758 acl_free(aclp);
759 return (1);
760 }
761
762 /*
763 * remove a single entry
764 *
765 * if last entry just adjust acl_cnt
766 */
767
768 if ((acl_args->acl_slot + 1) == aclp->acl_cnt)
769 aclp->acl_cnt--;
770 else {
771 to = (char *)aclp->acl_aclp +
772 (acl_args->acl_slot * aclp->acl_entry_size);
773 from = (char *)to + aclp->acl_entry_size;
774 len = (aclp->acl_cnt - acl_args->acl_slot - 1) *
775 aclp->acl_entry_size;
776 (void) memmove(to, from, len);
777 aclp->acl_cnt--;
778 }
779 set_aclp = aclp;
780 break;
781
782 case ACL_DELETE:
783 if ((error = acl_removeentries(aclp, acl_args->acl_aclp,
784 acl_args->acl_slot, ACL_REMOVE_ALL)) != 0) {
785 errmsg(1, 0, "%s\n", acl_strerror(error));
786 acl_free(aclp);
787 return (1);
788 }
789
790 if (aclp->acl_cnt == 0) {
791 errmsg(1, 0,
792 gettext("Can't remove all ACL "
793 "entries from a file\n"));
794 acl_free(aclp);
795 return (1);
796 }
797
798 set_aclp = aclp;
799 break;
800 case ACL_REPLACE:
801 if (acl_args->acl_slot >= 0) {
802 error = acl_modifyentries(aclp, acl_args->acl_aclp,
803 acl_args->acl_slot);
804 if (error) {
805 errmsg(1, 0, "%s\n", acl_strerror(error));
806 acl_free(aclp);
807 return (1);
808 }
809 set_aclp = aclp;
810 } else {
811 set_aclp = acl_args->acl_aclp;
812 }
813 break;
814 case ACL_STRIP:
815 error = acl_strip(file, st->st_uid, st->st_gid, st->st_mode);
816 if (error) {
817 errmsg(1, 0, "%s\n", acl_strerror(error));
818 acl_free(aclp);
819 return (1);
820 }
821 acl_free(aclp);
822 return (0);
823 /*NOTREACHED*/
824 default:
825 errmsg(1, 2, gettext("Unknown ACL action requested\n"));
826 /*NOTREACHED*/
827 }
828 error = acl_check(set_aclp, isdir);
829
830 if (error) {
831 errmsg(1, 2, "%s\n%s", acl_strerror(error),
832 gettext("See chmod(1) for more information on "
833 "valid ACL syntax\n"));
834 }
835 if ((error = acl_set(file, set_aclp)) != 0) {
836 errmsg(1, 0, gettext("Failed to set ACL: %s\n"),
837 acl_strerror(error));
838 acl_free(aclp);
839 return (1);
840 }
841 acl_free(aclp);
842 return (0);
843 }
844
845 /*
846 * Prints out the attributes in their verbose form:
847 * '{'[["no"]<attribute-name>][,["no"]<attribute-name>]...'}'
848 * similar to output of ls -/v.
849 */
850 static void
print_nvlist(nvlist_t * attr_nvlist)851 print_nvlist(nvlist_t *attr_nvlist)
852 {
853 int firsttime = 1;
854 boolean_t value;
855 nvlist_t *lptr = attr_nvlist;
856 nvpair_t *pair = NULL;
857
858 (void) fprintf(stderr, "\t%c", LEFTBRACE);
859 while (pair = nvlist_next_nvpair(lptr, pair)) {
860 if (nvpair_value_boolean_value(pair, &value) == 0) {
861 (void) fprintf(stderr, "%s%s%s",
862 firsttime ? "" : A_SEP_TOK,
863 (value == A_SET_VAL) ? "" : "no",
864 nvpair_name(pair));
865 firsttime = 0;
866 } else {
867 (void) fprintf(stderr, gettext(
868 "<error retrieving attributes: %s>"),
869 strerror(errno));
870 break;
871 }
872 }
873 (void) fprintf(stderr, "%c\n", RIGHTBRACE);
874 }
875
876 /*
877 * Add an attribute name and boolean value to an nvlist if an action is to be
878 * performed for that attribute. The nvlist will be used later to set all the
879 * attributes in the nvlist in one operation through a call to setattrat().
880 *
881 * If a set operation ('+') was specified, then a boolean representation of the
882 * attribute's value will be added to the nvlist for that attribute name. If an
883 * inverse operation ('-') was specified, then a boolean representation of the
884 * inverse of the attribute's value will be added to the nvlist for that
885 * attribute name.
886 *
887 * Returns an nvlist of attribute name and boolean value pairs if there are
888 * attribute actions to be performed, otherwise returns NULL.
889 */
890 static nvlist_t *
set_attrs_nvlist(char * attractptr,int numofattrs)891 set_attrs_nvlist(char *attractptr, int numofattrs)
892 {
893 int attribute_set = 0;
894 f_attr_t i;
895 nvlist_t *attr_nvlist;
896
897 if (nvlist_alloc(&attr_nvlist, NV_UNIQUE_NAME, 0) != 0) {
898 perror("chmod");
899 exit(2);
900 }
901
902 for (i = 0; i < numofattrs; i++) {
903 if (attractptr[i] != '\0') {
904 if ((nvlist_add_boolean_value(attr_nvlist,
905 attr_to_name(i),
906 (attractptr[i] == A_SET_OP))) != 0) {
907 errmsg(1, 2, gettext(
908 "unable to propagate attribute names and"
909 "values: %s\n"), strerror(errno));
910 } else {
911 attribute_set = 1;
912 }
913 }
914 }
915 return (attribute_set ? attr_nvlist : NULL);
916 }
917
918 /*
919 * Set the attributes of file, or if specified, of the named attribute file,
920 * attrname. Build an nvlist of attribute names and values and call setattrat()
921 * to set the attributes in one operation.
922 *
923 * Returns 0 if successful, otherwise returns 1.
924 */
925 static int
set_file_attrs(char * file,char * attrname,nvlist_t * attr_nvlist)926 set_file_attrs(char *file, char *attrname, nvlist_t *attr_nvlist)
927 {
928 int rc;
929 char *filename;
930
931 if (attrname != NULL) {
932 filename = attrname;
933 } else {
934 filename = basename(file);
935 }
936
937 if ((rc = setattrat(AT_FDCWD, XATTR_VIEW_READWRITE, filename,
938 attr_nvlist)) != 0) {
939 char *emsg;
940 switch (errno) {
941 case EINVAL:
942 emsg = gettext("not supported");
943 break;
944 case EPERM:
945 emsg = gettext("not privileged");
946 break;
947 default:
948 emsg = strerror(rc);
949 }
950 errmsg(1, 0, gettext(
951 "cannot set the following attributes on "
952 "%s%s%s%s: %s\n"),
953 (attrname == NULL) ? "" : gettext("attribute "),
954 (attrname == NULL) ? "" : attrname,
955 (attrname == NULL) ? "" : gettext(" of "),
956 file, emsg);
957 print_nvlist(attr_nvlist);
958 }
959
960 return (rc);
961 }
962
963 static int
save_cwd(void)964 save_cwd(void)
965 {
966 return (open(".", O_RDONLY));
967 }
968
969 static void
rest_cwd(int cwd)970 rest_cwd(int cwd)
971 {
972 if (cwd != -1) {
973 if (fchdir(cwd) != 0) {
974 errmsg(1, 1, gettext(
975 "can't change to current working directory\n"));
976 }
977 (void) close(cwd);
978 }
979 }
980
981 /*
982 * Returns 1 if filename is a system attribute file, otherwise
983 * returns 0.
984 */
985 static int
is_sattr(char * filename)986 is_sattr(char *filename)
987 {
988 return (sysattr_type(filename) != _NOT_SATTR);
989 }
990
991 /*
992 * Perform the action on the specified named attribute file for the file
993 * associated with the input file descriptor. If the named attribute file
994 * is "*", then the action is to be performed on all the named attribute files
995 * of the file associated with the input file descriptor.
996 */
997 static int
set_named_attrs(char * file,int parentfd,char * attrname,nvlist_t * attr_nvlist)998 set_named_attrs(char *file, int parentfd, char *attrname, nvlist_t *attr_nvlist)
999 {
1000 int dirfd;
1001 int error = 0;
1002 DIR *dirp = NULL;
1003 struct dirent *dp;
1004 struct stat st;
1005
1006 if ((attrname == NULL) || (strcmp(attrname, "*") != 0)) {
1007 /*
1008 * Make sure the named attribute exists and extended system
1009 * attributes are supported on the underlying file system.
1010 */
1011 if (attrname != NULL) {
1012 if (fstatat(parentfd, attrname, &st,
1013 AT_SYMLINK_NOFOLLOW) < 0) {
1014 errmsg(2, 0, gettext(
1015 "can't access attribute %s of %s\n"),
1016 attrname, file);
1017 return (1);
1018 }
1019 if (sysattr_support(attrname, _PC_SATTR_ENABLED) != 1) {
1020 errmsg(1, 0, gettext(
1021 "extended system attributes not supported "
1022 "for attribute %s of %s\n"),
1023 attrname, file);
1024 return (1);
1025 }
1026 }
1027
1028 error = set_file_attrs(file, attrname, attr_nvlist);
1029
1030 } else {
1031 if (((dirfd = dup(parentfd)) == -1) ||
1032 ((dirp = fdopendir(dirfd)) == NULL)) {
1033 errmsg(1, 0, gettext(
1034 "cannot open dir pointer of file %s\n"), file);
1035 if (dirfd > 0) {
1036 (void) close(dirfd);
1037 }
1038 return (1);
1039 }
1040
1041 while (dp = readdir(dirp)) {
1042 /*
1043 * Process all extended attribute files except
1044 * ".", "..", and extended system attribute files.
1045 */
1046 if ((strcmp(dp->d_name, ".") == 0) ||
1047 (strcmp(dp->d_name, "..") == 0) ||
1048 is_sattr(dp->d_name)) {
1049 continue;
1050 }
1051
1052 if (set_named_attrs(file, parentfd, dp->d_name,
1053 attr_nvlist) != 0) {
1054 error++;
1055 }
1056 }
1057 if (dirp != NULL) {
1058 (void) closedir(dirp);
1059 }
1060 }
1061
1062 return ((error == 0) ? 0 : 1);
1063 }
1064
1065 /*
1066 * Set the attributes of the specified file, or if specified with -@ on the
1067 * command line, the specified named attributes of the specified file.
1068 *
1069 * Returns 0 if successful, otherwise returns 1.
1070 */
1071 static int
set_attrs(char * file,attr_name_t * attrnames,nvlist_t * attr_nvlist)1072 set_attrs(char *file, attr_name_t *attrnames, nvlist_t *attr_nvlist)
1073 {
1074 char *parentd;
1075 char *tpath = NULL;
1076 int cwd;
1077 int error = 0;
1078 int parentfd;
1079 attr_name_t *tattr = attrnames;
1080
1081 if (attr_nvlist == NULL) {
1082 return (0);
1083 }
1084
1085 if (sysattr_support(file, _PC_SATTR_ENABLED) != 1) {
1086 errmsg(1, 0, gettext(
1087 "extended system attributes not supported for %s\n"), file);
1088 return (1);
1089 }
1090
1091 /*
1092 * Open the parent directory and change into it before attempting
1093 * to set the attributes of the file.
1094 */
1095 if (attrnames == NULL) {
1096 tpath = strdup(file);
1097 parentd = dirname(tpath);
1098 parentfd = open(parentd, O_RDONLY);
1099 } else {
1100 parentfd = attropen(file, ".", O_RDONLY);
1101 }
1102 if (parentfd == -1) {
1103 errmsg(1, 0, gettext(
1104 "cannot open attribute directory of %s\n"), file);
1105 if (tpath != NULL) {
1106 free(tpath);
1107 }
1108 return (1);
1109 }
1110
1111 if ((cwd = save_cwd()) < 0) {
1112 errmsg(1, 1, gettext(
1113 "can't get current working directory\n"));
1114 }
1115 if (fchdir(parentfd) != 0) {
1116 errmsg(1, 0, gettext(
1117 "can't change to parent %sdirectory of %s\n"),
1118 (attrnames == NULL) ? "" : gettext("attribute "), file);
1119 (void) close(cwd);
1120 (void) close(parentfd);
1121 if (tpath != NULL) {
1122 free(tpath);
1123 }
1124 return (1);
1125 }
1126
1127 /*
1128 * If no named attribute file names were provided on the command line
1129 * then set the attributes of the base file, otherwise, set the
1130 * attributes for each of the named attribute files specified.
1131 */
1132 if (attrnames == NULL) {
1133 error = set_named_attrs(file, parentfd, NULL, attr_nvlist);
1134 free(tpath);
1135 } else {
1136 while (tattr != NULL) {
1137 if (set_named_attrs(file, parentfd, tattr->name,
1138 attr_nvlist) != 0) {
1139 error++;
1140 }
1141 tattr = tattr->next;
1142 }
1143 }
1144 (void) close(parentfd);
1145 rest_cwd(cwd);
1146
1147 return ((error == 0) ? 0 : 1);
1148 }
1149
1150 /*
1151 * Prints the attributes in either the compact or verbose form indicated
1152 * by flag.
1153 */
1154 static void
print_attrs(int flag)1155 print_attrs(int flag)
1156 {
1157 f_attr_t i;
1158 static int numofattrs;
1159 int firsttime = 1;
1160
1161 numofattrs = attr_count();
1162
1163 (void) fprintf(stderr, gettext("\t["));
1164 for (i = 0; i < numofattrs; i++) {
1165 if ((attr_to_xattr_view(i) != XATTR_VIEW_READWRITE) ||
1166 (attr_to_data_type(i) != DATA_TYPE_BOOLEAN_VALUE)) {
1167 continue;
1168 }
1169 (void) fprintf(stderr, "%s%s",
1170 (firsttime == 1) ? "" : gettext("|"),
1171 (flag == ATTR_OPTS) ? attr_to_option(i) : attr_to_name(i));
1172 firsttime = 0;
1173 }
1174 (void) fprintf(stderr, gettext("]\n"));
1175 }
1176
1177 /*
1178 * Record what action should be taken on the specified attribute. Only boolean
1179 * read-write attributes can be manipulated.
1180 *
1181 * Returns 0 if successful, otherwise returns 1.
1182 */
1183 static int
set_attr_args(f_attr_t attr,char action,char * attractptr)1184 set_attr_args(f_attr_t attr, char action, char *attractptr)
1185 {
1186 if ((attr_to_xattr_view(attr) == XATTR_VIEW_READWRITE) &&
1187 (attr_to_data_type(attr) == DATA_TYPE_BOOLEAN_VALUE)) {
1188 attractptr[attr] = action;
1189 return (0);
1190 }
1191 return (1);
1192 }
1193
1194 /*
1195 * Parses the entry and assigns the appropriate action (either '+' or '-' in
1196 * attribute's position in the character array pointed to by attractptr, where
1197 * upon exit, attractptr is positional and the value of each character specifies
1198 * whether to set (a '+'), clear (a '-'), or leave untouched (a '\0') the
1199 * attribute value.
1200 *
1201 * If the entry is an attribute name, then the A_SET_OP action is to be
1202 * performed for this attribute. If the entry is an attribute name proceeded
1203 * with "no", then the A_INVERSE_OP action is to be performed for this
1204 * attribute. If the entry is one or more attribute option letters, then step
1205 * through each of the option letters marking the action to be performed for
1206 * each of the attributes associated with the letter as A_SET_OP.
1207 *
1208 * Returns 0 if the entry was a valid attribute(s) and the action to be
1209 * performed on that attribute(s) has been recorded, otherwise returns 1.
1210 */
1211 static int
parse_entry(char * entry,char action,char atype,int len,char * attractptr)1212 parse_entry(char *entry, char action, char atype, int len, char *attractptr)
1213 {
1214 char aopt[2] = {'\0', '\0'};
1215 char *aptr;
1216 f_attr_t attr;
1217
1218 if (atype == A_VERBOSE_TYPE) {
1219 if ((attr = name_to_attr(entry)) != F_ATTR_INVAL) {
1220 return (set_attr_args(attr,
1221 (action == A_REPLACE_OP) ? A_SET_OP : action,
1222 attractptr));
1223 } else if ((len > 2) && (strncmp(entry, "no", 2) == 0) &&
1224 ((attr = name_to_attr(entry + 2)) != F_ATTR_INVAL)) {
1225 return (set_attr_args(attr, ((action == A_REPLACE_OP) ||
1226 (action == A_SET_OP)) ? A_INVERSE_OP : A_SET_OP,
1227 attractptr));
1228 } else {
1229 return (1);
1230 }
1231 } else if (atype == A_COMPACT_TYPE) {
1232 for (aptr = entry; *aptr != '\0'; aptr++) {
1233 *aopt = *aptr;
1234 /*
1235 * The output of 'ls' can be used as the attribute mode
1236 * specification for chmod. This output can contain a
1237 * hypen ('-') for each attribute that is not set. If
1238 * so, ignore them. If a replace action is being
1239 * performed, then all attributes that don't have an
1240 * action set here, will be cleared down the line.
1241 */
1242 if (*aptr == '-') {
1243 continue;
1244 }
1245 if (set_attr_args(option_to_attr(aopt),
1246 (action == A_REPLACE_OP) ? A_SET_OP : action,
1247 attractptr) != 0) {
1248 return (1);
1249 }
1250 }
1251 return (0);
1252 }
1253 return (1);
1254 }
1255
1256 /*
1257 * Parse the attribute specification, aoptsstr. Upon completion, attr_nvlist
1258 * will point to an nvlist which contains pairs of attribute names and values
1259 * to be set; attr_nvlist will be NULL if it is a no-op.
1260 *
1261 * The attribute specification format is
1262 * S[oper]attr_type[attribute_list]
1263 * where oper is
1264 * + set operation of specified attributes in attribute list.
1265 * This is the default operation.
1266 * - inverse operation of specified attributes in attribute list
1267 * = replace operation of all attributes. All attribute operations
1268 * depend on those specified in the attribute list. Attributes
1269 * not specified in the attribute list will be cleared.
1270 * where attr_type is
1271 * c compact type. Each entry in the attribute list is a character
1272 * option representing an associated attribute name.
1273 * v verbose type. Each entry in the attribute list is an
1274 * an attribute name which can optionally be preceeded with "no"
1275 * (to imply the attribute should be cleared).
1276 * a all attributes type. The oper should be applied to all
1277 * read-write boolean system attributes. No attribute list should
1278 * be specified after an 'a' attribute type.
1279 *
1280 * Returns 0 if aoptsstr contained a valid attribute specification,
1281 * otherwise, returns 1.
1282 */
1283 static int
parse_attr_args(char * aoptsstr,sec_args_t ** sec_args)1284 parse_attr_args(char *aoptsstr, sec_args_t **sec_args)
1285 {
1286 char action;
1287 char *attractptr;
1288 char atype;
1289 char *entry;
1290 char *eptr;
1291 char *nextattr;
1292 char *nextentry;
1293 char *subentry;
1294 char *teptr;
1295 char tok[] = {'\0', '\0'};
1296 int len;
1297 f_attr_t i;
1298 int numofattrs;
1299
1300 if ((*aoptsstr != 'S') || (*(aoptsstr + 1) == '\0')) {
1301 return (1);
1302 }
1303
1304 if ((eptr = strdup(aoptsstr + 1)) == NULL) {
1305 perror("chmod");
1306 exit(2);
1307 }
1308 entry = eptr;
1309
1310 /*
1311 * Create a positional character array to determine a single attribute
1312 * operation to be performed, where each index represents the system
1313 * attribute affected, and it's value in the array represents the action
1314 * to be performed, i.e., a value of '+' means to set the attribute, a
1315 * value of '-' means to clear the attribute, and a value of '\0' means
1316 * to leave the attribute untouched. Initially, this positional
1317 * character array is all '\0's, representing a no-op.
1318 */
1319 if ((numofattrs = attr_count()) < 1) {
1320 errmsg(1, 1, gettext("system attributes not supported\n"));
1321 }
1322
1323 if ((attractptr = calloc(numofattrs, sizeof (char))) == NULL) {
1324 perror("chmod");
1325 exit(2);
1326 }
1327
1328 if ((*sec_args = malloc(sizeof (sec_args_t))) == NULL) {
1329 perror("chmod");
1330 exit(2);
1331 }
1332 (*sec_args)->sec_type = SEC_ATTR;
1333 (*sec_args)->sec_attrs = NULL;
1334
1335 /* Parse each attribute operation within the attribute specification. */
1336 while ((entry != NULL) && (*entry != '\0')) {
1337 action = A_SET_OP;
1338 atype = '\0';
1339
1340 /* Get the operator. */
1341 switch (*entry) {
1342 case A_SET_OP:
1343 case A_INVERSE_OP:
1344 case A_REPLACE_OP:
1345 action = *entry++;
1346 break;
1347 case A_COMPACT_TYPE:
1348 case A_VERBOSE_TYPE:
1349 case A_ALLATTRS_TYPE:
1350 atype = *entry++;
1351 action = A_SET_OP;
1352 break;
1353 default:
1354 break;
1355 }
1356
1357 /* An attribute type must be specified. */
1358 if (atype == '\0') {
1359 if ((*entry == A_COMPACT_TYPE) ||
1360 (*entry == A_VERBOSE_TYPE) ||
1361 (*entry == A_ALLATTRS_TYPE)) {
1362 atype = *entry++;
1363 } else {
1364 return (1);
1365 }
1366 }
1367
1368 /* Get the attribute specification separator. */
1369 if (*entry == LEFTBRACE) {
1370 *tok = RIGHTBRACE;
1371 entry++;
1372 } else {
1373 *tok = A_SEP;
1374 }
1375
1376 /* Get the attribute operation */
1377 if ((nextentry = strpbrk(entry, tok)) != NULL) {
1378 *nextentry = '\0';
1379 nextentry++;
1380 }
1381
1382 /* Check for a no-op */
1383 if ((*entry == '\0') && (atype != A_ALLATTRS_TYPE) &&
1384 (action != A_REPLACE_OP)) {
1385 entry = nextentry;
1386 continue;
1387 }
1388
1389 /*
1390 * Step through the attribute operation, setting the
1391 * appropriate values for the specified attributes in the
1392 * character array, attractptr. A value of '+' will mean the
1393 * attribute is to be set, and a value of '-' will mean the
1394 * attribute is to be cleared. If the value of an attribute
1395 * remains '\0', then no action is to be taken on that
1396 * attribute. As multiple operations specified are
1397 * accumulated, a single attribute setting operation is
1398 * represented in attractptr.
1399 */
1400 len = strlen(entry);
1401 if ((*tok == RIGHTBRACE) || (action == A_REPLACE_OP) ||
1402 (atype == A_ALLATTRS_TYPE)) {
1403
1404 if ((action == A_REPLACE_OP) ||
1405 (atype == A_ALLATTRS_TYPE)) {
1406 (void) memset(attractptr, '\0', numofattrs);
1407 }
1408
1409 if (len > 0) {
1410 if ((teptr = strdup(entry)) == NULL) {
1411 perror("chmod");
1412 exit(2);
1413 }
1414 subentry = teptr;
1415 while (subentry != NULL) {
1416 if ((nextattr = strpbrk(subentry,
1417 A_SEP_TOK)) != NULL) {
1418 *nextattr = '\0';
1419 nextattr++;
1420 }
1421 if (parse_entry(subentry, action,
1422 atype, len, attractptr) != 0) {
1423 return (1);
1424 }
1425 subentry = nextattr;
1426 }
1427 free(teptr);
1428 }
1429
1430 /*
1431 * If performing the replace action, record the
1432 * attributes and values for the rest of the
1433 * attributes that have not already been recorded,
1434 * otherwise record the specified action for all
1435 * attributes. Note: set_attr_args() will only record
1436 * the attribute and action if it is a boolean
1437 * read-write attribute so we don't need to worry
1438 * about checking it here.
1439 */
1440 if ((action == A_REPLACE_OP) ||
1441 (atype == A_ALLATTRS_TYPE)) {
1442 for (i = 0; i < numofattrs; i++) {
1443 if (attractptr[i] == A_UNDEF_OP) {
1444 (void) set_attr_args(i,
1445 (action == A_SET_OP) ?
1446 A_SET_OP : A_INVERSE_OP,
1447 attractptr);
1448 }
1449 }
1450 }
1451
1452 } else {
1453 if (parse_entry(entry, action, atype, len,
1454 attractptr) != 0) {
1455 return (1);
1456 }
1457 }
1458 entry = nextentry;
1459 }
1460
1461 /*
1462 * Populate an nvlist with attribute name and boolean value pairs
1463 * using the single attribute operation.
1464 */
1465 (*sec_args)->sec_attrs = set_attrs_nvlist(attractptr, numofattrs);
1466 free(attractptr);
1467 free(eptr);
1468
1469 return (0);
1470 }
1471