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 */
24
25 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
26 /* All Rights Reserved */
27 /* */
28
29 /*
30 * University Copyright- Copyright (c) 1982, 1986, 1988
31 * The Regents of the University of California
32 * All Rights Reserved
33 *
34 * University Acknowledgment- Portions of this document are derived from
35 * software developed by the University of California, Berkeley, and its
36 * contributors.
37 */
38
39 /*
40 * chmod option mode files
41 * where
42 * mode is [ugoa][+-=][rwxXlstugo] or an octal number
43 * mode is [<+|->A[# <number] ]<aclspec>
44 * mode is S<attrspec>
45 * option is -R, -f, and -@
46 */
47
48 /*
49 * Note that many convolutions are necessary
50 * due to the re-use of bits between locking
51 * and setgid
52 */
53
54 #include <unistd.h>
55 #include <stdlib.h>
56 #include <stdio.h>
57 #include <sys/types.h>
58 #include <sys/stat.h>
59 #include <fcntl.h>
60 #include <dirent.h>
61 #include <locale.h>
62 #include <string.h> /* strerror() */
63 #include <stdarg.h>
64 #include <limits.h>
65 #include <ctype.h>
66 #include <errno.h>
67 #include <sys/acl.h>
68 #include <aclutils.h>
69 #include <libnvpair.h>
70 #include <libcmdutils.h>
71 #include <libgen.h>
72 #include <attr.h>
73
74 static int rflag;
75 static int fflag;
76
77 extern int optind;
78 extern int errno;
79
80 static int mac; /* Alternate to argc (for parseargs) */
81 static char **mav; /* Alternate to argv (for parseargs) */
82
83 static char *ms; /* Points to the mode argument */
84
85 #define ACL_ADD 1
86 #define ACL_DELETE 2
87 #define ACL_SLOT_DELETE 3
88 #define ACL_REPLACE 4
89 #define ACL_STRIP 5
90
91 #define LEFTBRACE '{'
92 #define RIGHTBRACE '}'
93 #define A_SEP ','
94 #define A_SEP_TOK ","
95
96 #define A_COMPACT_TYPE 'c'
97 #define A_VERBOSE_TYPE 'v'
98 #define A_ALLATTRS_TYPE 'a'
99
100 #define A_SET_OP '+'
101 #define A_INVERSE_OP '-'
102 #define A_REPLACE_OP '='
103 #define A_UNDEF_OP '\0'
104
105 #define A_SET_TEXT "set"
106 #define A_INVERSE_TEXT "clear"
107
108 #define A_SET_VAL B_TRUE
109 #define A_CLEAR_VAL B_FALSE
110
111 #define ATTR_OPTS 0
112 #define ATTR_NAMES 1
113
114 #define sec_acls secptr.acls
115 #define sec_attrs secptr.attrs
116
117 typedef struct acl_args {
118 acl_t *acl_aclp;
119 int acl_slot;
120 int acl_action;
121 } acl_args_t;
122
123 typedef enum {
124 SEC_ACL,
125 SEC_ATTR
126 } chmod_sec_t;
127
128 typedef struct {
129 chmod_sec_t sec_type;
130 union {
131 acl_args_t *acls;
132 nvlist_t *attrs;
133 } secptr;
134 } sec_args_t;
135
136 typedef struct attr_name {
137 char *name;
138 struct attr_name *next;
139 } attr_name_t;
140
141
142 extern mode_t newmode_common(char *ms, mode_t new_mode, mode_t umsk,
143 char *file, char *path, o_mode_t *group_clear_bits,
144 o_mode_t *group_set_bits);
145
146 static int chmodr(char *dir, char *path, mode_t mode, mode_t umsk,
147 sec_args_t *secp, attr_name_t *attrname);
148 static int doacl(char *file, struct stat *st, acl_args_t *aclp);
149 static int dochmod(char *name, char *path, mode_t umsk, sec_args_t *secp,
150 attr_name_t *attrnames);
151 static void handle_acl(char *name, o_mode_t group_clear_bits,
152 o_mode_t group_set_bits);
153 void errmsg(int severity, int code, char *format, ...);
154 static void free_attr_names(attr_name_t *attrnames);
155 static void parseargs(int ac, char *av[]);
156 static int parse_acl_args(char *arg, sec_args_t **sec_args);
157 static int parse_attr_args(char *arg, sec_args_t **sec_args);
158 static void print_attrs(int flag);
159 static int set_attrs(char *file, attr_name_t *attrnames, nvlist_t *attr_nvlist);
160 static void usage(void);
161
162 int
main(int argc,char * argv[])163 main(int argc, char *argv[])
164 {
165 int i, c;
166 int status = 0;
167 mode_t umsk;
168 sec_args_t *sec_args = NULL;
169 attr_name_t *attrnames = NULL;
170 attr_name_t *attrend = NULL;
171 attr_name_t *tattr;
172
173 (void) setlocale(LC_ALL, "");
174 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
175 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
176 #endif
177 (void) textdomain(TEXT_DOMAIN);
178
179 parseargs(argc, argv);
180
181 while ((c = getopt(mac, mav, "Rf@:")) != EOF) {
182 switch (c) {
183 case 'R':
184 rflag++;
185 break;
186 case 'f':
187 fflag++;
188 break;
189 case '@':
190 if (((tattr = malloc(sizeof (attr_name_t))) == NULL) ||
191 ((tattr->name = strdup(optarg)) == NULL)) {
192 perror("chmod");
193 exit(2);
194 }
195 if (attrnames == NULL) {
196 attrnames = tattr;
197 attrnames->next = NULL;
198 } else {
199 attrend->next = tattr;
200 }
201 attrend = tattr;
202 break;
203 case '?':
204 usage();
205 exit(2);
206 }
207 }
208
209 /*
210 * Check for sufficient arguments
211 * or a usage error.
212 */
213
214 mac -= optind;
215 mav += optind;
216 if ((mac >= 2) && (mav[0][0] == 'A')) {
217 if (attrnames != NULL) {
218 free_attr_names(attrnames);
219 attrnames = NULL;
220 }
221 if (parse_acl_args(*mav, &sec_args)) {
222 usage();
223 exit(2);
224 }
225 } else if ((mac >= 2) && (mav[0][0] == 'S')) {
226 if (parse_attr_args(*mav, &sec_args)) {
227 usage();
228 exit(2);
229
230 /* A no-op attribute operation was specified. */
231 } else if (sec_args->sec_attrs == NULL) {
232 exit(0);
233 }
234 } else {
235 if (mac < 2) {
236 usage();
237 exit(2);
238 }
239 if (attrnames != NULL) {
240 free_attr_names(attrnames);
241 attrnames = NULL;
242 }
243 }
244
245 ms = mav[0];
246
247 umsk = umask(0);
248 (void) umask(umsk);
249
250 for (i = 1; i < mac; i++) {
251 status += dochmod(mav[i], mav[i], umsk, sec_args, attrnames);
252 }
253
254 return (fflag ? 0 : status);
255 }
256
257 static void
free_attr_names(attr_name_t * attrnames)258 free_attr_names(attr_name_t *attrnames)
259 {
260 attr_name_t *attrnamesptr = attrnames;
261 attr_name_t *tptr;
262
263 while (attrnamesptr != NULL) {
264 tptr = attrnamesptr->next;
265 if (attrnamesptr->name != NULL) {
266 free(attrnamesptr->name);
267 }
268 attrnamesptr = tptr;
269 }
270 }
271
272 static int
dochmod(char * name,char * path,mode_t umsk,sec_args_t * secp,attr_name_t * attrnames)273 dochmod(char *name, char *path, mode_t umsk, sec_args_t *secp,
274 attr_name_t *attrnames)
275 {
276 static struct stat st;
277 int linkflg = 0;
278 o_mode_t group_clear_bits, group_set_bits;
279
280 if (lstat(name, &st) < 0) {
281 errmsg(2, 0, gettext("can't access %s\n"), path);
282 return (1);
283 }
284
285 if ((st.st_mode & S_IFMT) == S_IFLNK) {
286 linkflg = 1;
287 if (stat(name, &st) < 0) {
288 errmsg(2, 0, gettext("can't access %s\n"), path);
289 return (1);
290 }
291 }
292
293 /* Do not recurse if directory is object of symbolic link */
294 if (rflag && ((st.st_mode & S_IFMT) == S_IFDIR) && !linkflg) {
295 return (chmodr(name, path, st.st_mode, umsk, secp, attrnames));
296 }
297
298 if (secp != NULL) {
299 if (secp->sec_type == SEC_ACL) {
300 return (doacl(name, &st, secp->sec_acls));
301 } else if (secp->sec_type == SEC_ATTR) {
302 return (set_attrs(name, attrnames, secp->sec_attrs));
303 } else {
304 return (1);
305 }
306 } else {
307 if (chmod(name, newmode_common(ms, st.st_mode, umsk, name, path,
308 &group_clear_bits, &group_set_bits)) == -1) {
309 errmsg(2, 0, gettext("can't change %s\n"), path);
310 return (1);
311 }
312 }
313
314 /*
315 * If the group permissions of the file are being modified,
316 * make sure that the file's ACL (if it has one) is
317 * modified also, since chmod is supposed to apply group
318 * permissions changes to both the acl mask and the
319 * general group permissions.
320 */
321 if (group_clear_bits || group_set_bits)
322 handle_acl(name, group_clear_bits, group_set_bits);
323
324 return (0);
325 }
326
327 static int
chmodr(char * dir,char * path,mode_t mode,mode_t umsk,sec_args_t * secp,attr_name_t * attrnames)328 chmodr(char *dir, char *path, mode_t mode, mode_t umsk, sec_args_t *secp,
329 attr_name_t *attrnames)
330 {
331
332 DIR *dirp;
333 struct dirent *dp;
334 char savedir[PATH_MAX]; /* dir name to restore */
335 char currdir[PATH_MAX+1]; /* current dir name + '/' */
336 char parentdir[PATH_MAX+1]; /* parent dir name + '/' */
337 int ecode;
338 struct stat st;
339 o_mode_t group_clear_bits, group_set_bits;
340
341 if (getcwd(savedir, PATH_MAX) == 0)
342 errmsg(2, 255, gettext("chmod: could not getcwd %s\n"),
343 savedir);
344
345 /*
346 * Change what we are given before doing it's contents
347 */
348 if (secp != NULL) {
349 if (lstat(dir, &st) < 0) {
350 errmsg(2, 0, gettext("can't access %s\n"), path);
351 return (1);
352 }
353 if (secp->sec_type == SEC_ACL) {
354 (void) doacl(dir, &st, secp->sec_acls);
355 } else if (secp->sec_type == SEC_ATTR) {
356 (void) set_attrs(dir, attrnames, secp->sec_attrs);
357 } else {
358 return (1);
359 }
360 } else if (chmod(dir, newmode_common(ms, mode, umsk, dir, path,
361 &group_clear_bits, &group_set_bits)) < 0) {
362 errmsg(2, 0, gettext("can't change %s\n"), path);
363 }
364
365 /*
366 * If the group permissions of the file are being modified,
367 * make sure that the file's ACL (if it has one) is
368 * modified also, since chmod is supposed to apply group
369 * permissions changes to both the acl mask and the
370 * general group permissions.
371 */
372
373 if (secp != NULL) {
374 /* only necessary when not setting ACL or system attributes */
375 if (group_clear_bits || group_set_bits)
376 handle_acl(dir, group_clear_bits, group_set_bits);
377 }
378
379 if (chdir(dir) < 0) {
380 errmsg(2, 0, "%s/%s: %s\n", savedir, dir, strerror(errno));
381 return (1);
382 }
383 if ((dirp = opendir(".")) == NULL) {
384 errmsg(2, 0, "%s\n", strerror(errno));
385 return (1);
386 }
387 ecode = 0;
388
389 /*
390 * Save parent directory path before recursive chmod.
391 * We'll need this for error printing purposes. Add
392 * a trailing '/' to the path except in the case where
393 * the path is just '/'
394 */
395
396 if (strlcpy(parentdir, path, PATH_MAX + 1) >= PATH_MAX + 1) {
397 errmsg(2, 0, gettext("directory path name too long: %s\n"),
398 path);
399 return (1);
400 }
401 if (strcmp(path, "/") != 0)
402 if (strlcat(parentdir, "/", PATH_MAX + 1) >= PATH_MAX + 1) {
403 errmsg(2, 0,
404 gettext("directory path name too long: %s/\n"),
405 parentdir);
406 return (1);
407 }
408
409
410 for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
411
412 if (strcmp(dp->d_name, ".") == 0 || /* skip . and .. */
413 strcmp(dp->d_name, "..") == 0) {
414 continue;
415 }
416 if (strlcpy(currdir, parentdir, PATH_MAX + 1) >= PATH_MAX + 1) {
417 errmsg(2, 0,
418 gettext("directory path name too long: %s\n"),
419 parentdir);
420 return (1);
421 }
422 if (strlcat(currdir, dp->d_name, PATH_MAX + 1)
423 >= PATH_MAX + 1) {
424 errmsg(2, 0,
425 gettext("directory path name too long: %s%s\n"),
426 currdir, dp->d_name);
427 return (1);
428 }
429 ecode += dochmod(dp->d_name, currdir, umsk, secp, attrnames);
430 }
431 (void) closedir(dirp);
432 if (chdir(savedir) < 0) {
433 errmsg(2, 255, gettext("can't change back to %s\n"), savedir);
434 }
435 return (ecode ? 1 : 0);
436 }
437
438 /* PRINTFLIKE3 */
439 void
errmsg(int severity,int code,char * format,...)440 errmsg(int severity, int code, char *format, ...)
441 {
442 va_list ap;
443 static char *msg[] = {
444 "",
445 "ERROR",
446 "WARNING",
447 ""
448 };
449
450 va_start(ap, format);
451
452 /*
453 * Always print error message if this is a fatal error (code == 0);
454 * otherwise, print message if fflag == 0 (no -f option specified)
455 */
456 if (!fflag || (code != 0)) {
457 (void) fprintf(stderr,
458 "chmod: %s: ", gettext(msg[severity]));
459 (void) vfprintf(stderr, format, ap);
460 }
461
462 va_end(ap);
463
464 if (code != 0)
465 exit(fflag ? 0 : code);
466 }
467
468 static void
usage(void)469 usage(void)
470 {
471 (void) fprintf(stderr, gettext(
472 "usage:\tchmod [-fR] <absolute-mode> file ...\n"));
473
474 (void) fprintf(stderr, gettext(
475 "\tchmod [-fR] [-@ attribute] ... "
476 "S<attribute-operation> file ...\n"));
477
478 (void) fprintf(stderr, gettext(
479 "\tchmod [-fR] <ACL-operation> file ...\n"));
480
481 (void) fprintf(stderr, gettext(
482 "\tchmod [-fR] <symbolic-mode-list> file ...\n\n"));
483
484 (void) fprintf(stderr, gettext(
485 "where \t<symbolic-mode-list> is a comma-separated list of\n"));
486 (void) fprintf(stderr, gettext(
487 "\t[ugoa]{+|-|=}[rwxXlstugo]\n\n"));
488
489 (void) fprintf(stderr, gettext(
490 "where \t<attribute-operation> is a comma-separated list of\n"
491 "\tone or more of the following\n"));
492 (void) fprintf(stderr, gettext(
493 "\t[+|-|=]c[<compact-attribute-list>|{<compact-attribute-list>}]\n"
494 "\t[+|-|=]v[<verbose-attribute-setting>|"
495 "\'{\'<verbose-attribute-setting-list>\'}\']\n"
496 "\t[+|-|=]a\n"));
497 (void) fprintf(stderr, gettext(
498 "where \t<compact-attribute-list> is a list of zero or more of\n"));
499 print_attrs(ATTR_OPTS);
500 (void) fprintf(stderr, gettext(
501 "where \t<verbose-attribute-setting> is one of\n"));
502 print_attrs(ATTR_NAMES);
503 (void) fprintf(stderr, gettext(
504 "\tand can be, optionally, immediately preceded by \"no\"\n\n"));
505
506 (void) fprintf(stderr, gettext(
507 "where \t<ACL-operation> is one of the following\n"));
508 (void) fprintf(stderr, gettext("\tA-<acl_specification>\n"));
509 (void) fprintf(stderr, gettext("\tA[number]-\n"));
510 (void) fprintf(stderr, gettext(
511 "\tA[number]{+|=}<acl_specification>\n"));
512 (void) fprintf(stderr, gettext(
513 "where \t<acl-specification> is a comma-separated list of ACEs\n"));
514 }
515
516 /*
517 * parseargs - generate getopt-friendly argument list for backwards
518 * compatibility with earlier Solaris usage (eg, chmod -w
519 * foo).
520 *
521 * assumes the existence of a static set of alternates to argc and argv,
522 * (namely, mac, and mav[]).
523 *
524 */
525
526 static void
parseargs(int ac,char * av[])527 parseargs(int ac, char *av[])
528 {
529 int i; /* current argument */
530 int fflag; /* arg list contains "--" */
531 size_t mav_num; /* number of entries in mav[] */
532
533 /*
534 * We add an extra argument slot, in case we need to jam a "--"
535 * argument into the list.
536 */
537
538 mav_num = (size_t)ac+2;
539
540 if ((mav = calloc(mav_num, sizeof (char *))) == NULL) {
541 perror("chmod");
542 exit(2);
543 }
544
545 /* scan for the use of "--" in the argument list */
546
547 for (fflag = i = 0; i < ac; i ++) {
548 if (strcmp(av[i], "--") == 0)
549 fflag = 1;
550 }
551
552 /* process the arguments */
553
554 for (i = mac = 0;
555 (av[i] != (char *)NULL) && (av[i][0] != (char)NULL);
556 i++) {
557 if (!fflag && av[i][0] == '-') {
558 /*
559 * If there is not already a "--" argument specified,
560 * and the argument starts with '-' but does not
561 * contain any of the official option letters, then it
562 * is probably a mode argument beginning with '-'.
563 * Force a "--" into the argument stream in front of
564 * it.
565 */
566
567 if ((strchr(av[i], 'R') == NULL &&
568 strchr(av[i], 'f') == NULL) &&
569 strchr(av[i], '@') == NULL) {
570 if ((mav[mac++] = strdup("--")) == NULL) {
571 perror("chmod");
572 exit(2);
573 }
574 }
575 }
576
577 if ((mav[mac++] = strdup(av[i])) == NULL) {
578 perror("chmod");
579 exit(2);
580 }
581 }
582
583 mav[mac] = (char *)NULL;
584 }
585
586 static int
parse_acl_args(char * arg,sec_args_t ** sec_args)587 parse_acl_args(char *arg, sec_args_t **sec_args)
588 {
589 acl_t *new_acl = NULL;
590 int slot;
591 int len;
592 int action;
593 acl_args_t *new_acl_args;
594 char *acl_spec = NULL;
595 char *end;
596
597 if (arg[0] != 'A')
598 return (1);
599
600 slot = strtol(&arg[1], &end, 10);
601
602 len = strlen(arg);
603 switch (*end) {
604 case '+':
605 action = ACL_ADD;
606 acl_spec = ++end;
607 break;
608 case '-':
609 if (len == 2 && arg[0] == 'A' && arg[1] == '-')
610 action = ACL_STRIP;
611 else
612 action = ACL_DELETE;
613 if (action != ACL_STRIP) {
614 acl_spec = ++end;
615 if (acl_spec[0] == '\0') {
616 action = ACL_SLOT_DELETE;
617 acl_spec = NULL;
618 } else if (arg[1] != '-')
619 return (1);
620 }
621 break;
622 case '=':
623 /*
624 * Was slot specified?
625 */
626 if (arg[1] == '=')
627 slot = -1;
628 action = ACL_REPLACE;
629 acl_spec = ++end;
630 break;
631 default:
632 return (1);
633 }
634
635 if ((action == ACL_REPLACE || action == ACL_ADD) && acl_spec[0] == '\0')
636 return (1);
637
638 if (acl_spec) {
639 if (acl_parse(acl_spec, &new_acl)) {
640 exit(1);
641 }
642 }
643
644 new_acl_args = malloc(sizeof (acl_args_t));
645 if (new_acl_args == NULL)
646 return (1);
647
648 new_acl_args->acl_aclp = new_acl;
649 new_acl_args->acl_slot = slot;
650 new_acl_args->acl_action = action;
651
652 if ((*sec_args = malloc(sizeof (sec_args_t))) == NULL) {
653 perror("chmod");
654 exit(2);
655 }
656 (*sec_args)->sec_type = SEC_ACL;
657 (*sec_args)->sec_acls = new_acl_args;
658
659 return (0);
660 }
661
662 /*
663 * This function is called whenever the group permissions of a file
664 * is being modified. According to the chmod(1) manpage, any
665 * change made to the group permissions must be applied to both
666 * the acl mask and the acl's GROUP_OBJ. The chmod(2) already
667 * set the mask, so this routine needs to make the same change
668 * to the GROUP_OBJ.
669 */
670 static void
handle_acl(char * name,o_mode_t group_clear_bits,o_mode_t group_set_bits)671 handle_acl(char *name, o_mode_t group_clear_bits, o_mode_t group_set_bits)
672 {
673 int aclcnt, n;
674 aclent_t *aclp, *tp;
675 o_mode_t newperm;
676 /*
677 * if this file system support ace_t acl's
678 * then simply return since we don't have an
679 * acl mask to deal with
680 */
681 if (pathconf(name, _PC_ACL_ENABLED) == _ACL_ACE_ENABLED)
682 return;
683 if ((aclcnt = acl(name, GETACLCNT, 0, NULL)) <= MIN_ACL_ENTRIES)
684 return; /* it's just a trivial acl; no need to change it */
685 if ((aclp = (aclent_t *)malloc((sizeof (aclent_t)) * aclcnt))
686 == NULL) {
687 perror("chmod");
688 exit(2);
689 }
690
691 if (acl(name, GETACL, aclcnt, aclp) < 0) {
692 free(aclp);
693 (void) fprintf(stderr, "chmod: ");
694 perror(name);
695 return;
696 }
697 for (tp = aclp, n = aclcnt; n--; tp++) {
698 if (tp->a_type == GROUP_OBJ) {
699 newperm = tp->a_perm;
700 if (group_clear_bits != 0)
701 newperm &= ~group_clear_bits;
702 if (group_set_bits != 0)
703 newperm |= group_set_bits;
704 if (newperm != tp->a_perm) {
705 tp->a_perm = newperm;
706 if (acl(name, SETACL, aclcnt, aclp)
707 < 0) {
708 (void) fprintf(stderr, "chmod: ");
709 perror(name);
710 }
711 }
712 break;
713 }
714 }
715 free(aclp);
716 }
717
718 static int
doacl(char * file,struct stat * st,acl_args_t * acl_args)719 doacl(char *file, struct stat *st, acl_args_t *acl_args)
720 {
721 acl_t *aclp;
722 acl_t *set_aclp;
723 int error = 0;
724 void *to, *from;
725 int len;
726 int isdir;
727 isdir = S_ISDIR(st->st_mode);
728
729 error = acl_get(file, 0, &aclp);
730
731 if (error != 0) {
732 errmsg(1, 1, "%s\n", acl_strerror(error));
733 return (1);
734 }
735 switch (acl_args->acl_action) {
736 case ACL_ADD:
737 if ((error = acl_addentries(aclp,
738 acl_args->acl_aclp, acl_args->acl_slot)) != 0) {
739 errmsg(1, 1, "%s\n", acl_strerror(error));
740 acl_free(aclp);
741 return (1);
742 }
743 set_aclp = aclp;
744 break;
745 case ACL_SLOT_DELETE:
746 if (acl_args->acl_slot + 1 > aclp->acl_cnt) {
747 errmsg(1, 1,
748 gettext("Invalid slot specified for removal\n"));
749 acl_free(aclp);
750 return (1);
751 }
752
753 if (acl_args->acl_slot == 0 && aclp->acl_cnt == 1) {
754 errmsg(1, 1,
755 gettext("Can't remove all ACL "
756 "entries from a file\n"));
757 acl_free(aclp);
758 return (1);
759 }
760
761 /*
762 * remove a single entry
763 *
764 * if last entry just adjust acl_cnt
765 */
766
767 if ((acl_args->acl_slot + 1) == aclp->acl_cnt)
768 aclp->acl_cnt--;
769 else {
770 to = (char *)aclp->acl_aclp +
771 (acl_args->acl_slot * aclp->acl_entry_size);
772 from = (char *)to + aclp->acl_entry_size;
773 len = (aclp->acl_cnt - acl_args->acl_slot - 1) *
774 aclp->acl_entry_size;
775 (void) memmove(to, from, len);
776 aclp->acl_cnt--;
777 }
778 set_aclp = aclp;
779 break;
780
781 case ACL_DELETE:
782 if ((error = acl_removeentries(aclp, acl_args->acl_aclp,
783 acl_args->acl_slot, ACL_REMOVE_ALL)) != 0) {
784 errmsg(1, 1, "%s\n", acl_strerror(error));
785 acl_free(aclp);
786 return (1);
787 }
788
789 if (aclp->acl_cnt == 0) {
790 errmsg(1, 1,
791 gettext("Can't remove all ACL "
792 "entries from a file\n"));
793 acl_free(aclp);
794 return (1);
795 }
796
797 set_aclp = aclp;
798 break;
799 case ACL_REPLACE:
800 if (acl_args->acl_slot >= 0) {
801 error = acl_modifyentries(aclp, acl_args->acl_aclp,
802 acl_args->acl_slot);
803 if (error) {
804 errmsg(1, 1, "%s\n", acl_strerror(error));
805 acl_free(aclp);
806 return (1);
807 }
808 set_aclp = aclp;
809 } else {
810 set_aclp = acl_args->acl_aclp;
811 }
812 break;
813 case ACL_STRIP:
814 error = acl_strip(file, st->st_uid, st->st_gid, st->st_mode);
815 if (error) {
816 errmsg(1, 1, "%s\n", acl_strerror(error));
817 return (1);
818 }
819 acl_free(aclp);
820 return (0);
821 /*NOTREACHED*/
822 default:
823 errmsg(1, 0, gettext("Unknown ACL action requested\n"));
824 return (1);
825 break;
826 }
827 error = acl_check(set_aclp, isdir);
828
829 if (error) {
830 errmsg(1, 0, "%s\n%s", acl_strerror(error),
831 gettext("See chmod(1) for more information on "
832 "valid ACL syntax\n"));
833 return (1);
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