xref: /illumos-gate/usr/src/cmd/setfacl/setfacl.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
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 
46 #define	ADD	1
47 #define	MODIFY	2
48 #define	DELETE	3
49 #define	SET	4
50 
51 static int get_acl_info(char *filep, aclent_t **aclpp);
52 static int mod_entries(aclent_t *, int, char *, char *, char *, int);
53 static int set_file_entries(char *, char *, int);
54 static int set_online_entries(char *, char *, int);
55 static void usage();
56 static int parse_entry_list(aclent_t **, int *, char *, int);
57 static int convert_to_aclent_t(char *, int *, aclent_t **, int);
58 static int parse_entry(char *, aclent_t *, int);
59 static void err_handle(int, aclent_t *);
60 static int conv_id(char *);
61 
62 int
63 main(int argc, char *argv[])
64 {
65 	int		c;
66 	int		dflag = 0;
67 	int		mflag = 0;
68 	int		rflag = 0;
69 	int		sflag = 0;
70 	int		fflag = 0;
71 	int		errflag = 0;
72 	int		aclcnt;			/* used by -m -d */
73 	aclent_t	*aclp;			/* used by -m -d */
74 	char		*aclfilep;		/* acl file argument */
75 	char		*d_entryp = NULL;	/* ptr to del entry list */
76 	char		*m_entryp = NULL;	/* ptr to mod entry list */
77 	char		*s_entryp = NULL;	/* ptr to set entry list */
78 	char		*work_dp = NULL;	/* working ptrs for the above */
79 	char		*work_mp = NULL;
80 	char		*work_sp = NULL;
81 
82 	(void) setlocale(LC_ALL, "");
83 	(void) textdomain(TEXT_DOMAIN);
84 
85 	if (argc < 3)
86 		usage();
87 
88 	while ((c = getopt(argc, argv, "rm:d:s:f:")) != EOF) {
89 		switch (c) {
90 		case 'r':
91 			rflag++;
92 			break;
93 		case 'd':
94 			if (dflag || fflag || sflag)
95 				usage();
96 			dflag++;
97 			d_entryp = optarg;
98 			break;
99 		case 'm':
100 			if (mflag || fflag || sflag)
101 				usage();
102 			mflag++;
103 			m_entryp = optarg;
104 			break;
105 		case 's':
106 			if (fflag || sflag || mflag || dflag)
107 				usage();
108 			sflag++;
109 			s_entryp = optarg;
110 			break;
111 		case 'f':
112 			if (fflag || sflag || mflag || dflag)
113 				usage();
114 			fflag++;
115 			aclfilep = optarg;
116 			break;
117 		case '?':
118 			errflag++;
119 			break;
120 		}
121 	}
122 	if (errflag)
123 		usage();
124 
125 	/* one of these flags should be set */
126 	if (!fflag && !sflag && !mflag && !dflag)
127 		usage();
128 
129 	/* no file arguments */
130 	if (optind >= argc)
131 		usage();
132 
133 	for (; optind < argc; optind++) {
134 		register char *filep;
135 
136 		filep = argv[optind];
137 
138 		/* modify and delete: we need to get the ACL first */
139 		if (mflag || dflag) {
140 			if (m_entryp != NULL) {
141 				free(work_mp);
142 				work_mp = strdup(m_entryp);
143 				if (work_mp == NULL) {
144 					fprintf(stderr,
145 					    gettext("out of memory %s\n"),
146 					    m_entryp);
147 					exit(1);
148 				}
149 			}
150 
151 			if (d_entryp != NULL) {
152 				free(work_dp);
153 				work_dp = strdup(d_entryp);
154 				if (work_dp == NULL) {
155 					fprintf(stderr,
156 					    gettext("out of memory %s\n"),
157 					    d_entryp);
158 					exit(1);
159 				}
160 			}
161 
162 			aclcnt = get_acl_info(filep, &aclp);
163 			if (aclcnt == -1)
164 				exit(2);
165 			if (mod_entries(aclp, aclcnt, work_mp,
166 			    work_dp, filep, rflag) == -1)
167 				exit(2);
168 		} else if (fflag) {
169 			if (set_file_entries(aclfilep, filep, rflag) == -1)
170 				exit(2);
171 		} else if (sflag) {
172 			if (s_entryp != NULL) {
173 				free(work_sp);
174 				work_sp = strdup(s_entryp);
175 				if (work_sp == NULL) {
176 					fprintf(stderr,
177 					    gettext("out of memory %s\n"),
178 					    s_entryp);
179 					exit(1);
180 				}
181 			}
182 			if (set_online_entries(work_sp, filep, rflag) == -1)
183 				exit(2);
184 		}
185 	}
186 	return (0);
187 }
188 
189 /*
190  * For add, modify, and delete, we need to get the ACL of the file first.
191  */
192 static int
193 get_acl_info(char *filep, aclent_t **aclpp)
194 {
195 	int	aclcnt;
196 
197 	if ((aclcnt = acl(filep, GETACLCNT, 0, NULL)) < 0) {
198 		if (errno == ENOSYS) {
199 			(void) fprintf(stderr,
200 			    gettext("File system doesn't support aclent_t "
201 			    "style ACL's.\n"
202 			    "See acl(5) for more information on"
203 			    " ACL styles support by Solaris.\n"));
204 			return (-1);
205 		}
206 		(void) fprintf(stderr,
207 		    gettext("%s: failed to get acl count\n"), filep);
208 		perror("get acl count error");
209 		return (-1);
210 	}
211 	if (aclcnt < MIN_ACL_ENTRIES) {
212 		(void) fprintf(stderr,
213 		    gettext("%d: acl count is too small from %s\n"),
214 		    aclcnt, filep);
215 		return (-1);
216 	}
217 
218 	if ((*aclpp = (aclent_t *)malloc(sizeof (aclent_t) * aclcnt)) == NULL) {
219 		(void) fprintf(stderr, gettext("out of memory\n"));
220 		return (-1);
221 	}
222 	if (acl(filep, GETACL, aclcnt, *aclpp) < 0) {
223 		(void) fprintf(stderr,
224 		    gettext("%s: failed to get acl entries\n"), filep);
225 		perror("getacl error");
226 		return (-1);
227 	}
228 	return (aclcnt);
229 }
230 
231 /*
232  * mod_entries() handles add, delete, and modify ACL entries of a file.
233  * The real action is in convert_to_aclent_t() called by parse_entry_list().
234  * aclp: points ACL of a file and may be changed by lower level routine.
235  * modp: modify entry list in ascii format
236  * delp: delete entry list in ascii format
237  * fnamep: file of interest
238  */
239 static int
240 mod_entries(aclent_t *aclp, int cnt, char *modp, char *delp,
241 	char *fnamep, int rfg)
242 {
243 	int	rc;		/* return code */
244 
245 	/* modify and add: from -m option */
246 	if (parse_entry_list(&aclp, &cnt, modp, MODIFY) == -1)
247 		return (-1);
248 
249 	/* deletion: from -d option */
250 	if (parse_entry_list(&aclp, &cnt, delp, DELETE) == -1)
251 		return (-1);
252 
253 	if (aclsort(cnt, rfg, aclp) == -1) {
254 		(void) err_handle(cnt, aclp);
255 		(void) fprintf(stderr,
256 		    gettext("aclcnt %d, file %s\n"), cnt, fnamep);
257 		return (-1);
258 	}
259 
260 	if (acl(fnamep, SETACL, cnt, aclp) < 0) {
261 		fprintf(stderr,
262 		    gettext("%s: failed to set acl entries\n"), fnamep);
263 		perror("setacl error");
264 		return (-1);
265 	}
266 	return (0);
267 }
268 
269 /*
270  * set_file_entries() creates ACL entries from ACL file (acl_fnamep).
271  * It opens the file and converts every line (one line per acl entry)
272  * into aclent_t format. It then recalculates the mask according to rflag.
273  * Finally it sets ACL to the file (fnamep).
274  */
275 static int
276 set_file_entries(char *acl_fnamep, char *fnamep, int rflag)
277 {
278 	int		aclcnt = 0;
279 	FILE		*acl_fp;
280 	aclent_t	*aclp;
281 	char		buf[BUFSIZ];
282 	char		*tp;
283 
284 	if (strcmp(acl_fnamep, "-") == 0)
285 		acl_fp = stdin;
286 	else {
287 		if ((acl_fp = fopen(acl_fnamep, "r")) == NULL) {
288 			fprintf(stderr, gettext("Can't open acl file %s\n"),
289 			    acl_fnamep);
290 			return (-1);
291 		}
292 	}
293 	while (fgets(buf, BUFSIZ, acl_fp) != NULL) {
294 		if (buf[0] == '#' || buf[0] == '\n')
295 			continue;
296 
297 		/* check effective permission: add a null after real perm */
298 		if ((tp = (char *)strchr(buf, '#')) != NULL) {
299 			tp--;
300 			while (*tp == ' ' || *tp == '\t') {
301 				if (tp != buf)
302 					tp--;
303 				else {
304 					fprintf(stderr,
305 					    gettext("entry format error %s\n"),
306 					    buf);
307 					exit(1);
308 				}
309 			}
310 			*(tp+1) = '\0';
311 		}
312 
313 		/* remove <nl> at the end if there is one */
314 		if ((tp = (char *)strchr(buf, '\n')) != NULL)
315 			*tp = '\0';
316 		aclcnt++;
317 		if (convert_to_aclent_t(buf, &aclcnt, &aclp, SET) == -1)
318 			return (-1);
319 	}
320 
321 	if (aclsort(aclcnt, rflag, aclp) == -1) {
322 		(void) err_handle(aclcnt, aclp);
323 		(void) fprintf(stderr, gettext("aclcnt %d, aclfile %s\n"),
324 		    aclcnt, acl_fnamep);
325 		return (-1);
326 	}
327 
328 	if (acl(fnamep, SETACL, aclcnt, aclp) < 0) {
329 		fprintf(stderr,
330 		    gettext("%s: failed to set acl entries\n"), fnamep);
331 		perror("setacl error");
332 		return (-1);
333 	}
334 	return (0);
335 }
336 
337 /*
338  * set_online_entries() parses the acl entries from command line (setp).
339  * It converts the comma separated acl entries into aclent_t format.
340  * It then recalculates the mask according to rflag.
341  * Finally it sets ACL to the file (fnamep).
342  */
343 static int
344 set_online_entries(char *setp, char *fnamep, int rflag)
345 {
346 	char		*commap;
347 	aclent_t	*aclp;
348 	int		aclcnt = 0;
349 
350 	if (parse_entry_list(&aclp, &aclcnt, setp, SET) == -1)
351 		return (-1);
352 
353 	if (aclsort(aclcnt, rflag, aclp) == -1) {
354 		(void) err_handle(aclcnt, aclp);
355 		(void) fprintf(stderr,
356 		    gettext("aclcnt %d, file %s\n"), aclcnt, fnamep);
357 		return (-1);
358 	}
359 
360 	if (acl(fnamep, SETACL, aclcnt, aclp) < 0) {
361 		fprintf(stderr,
362 		    gettext("%s: failed to set acl entries\n"), fnamep);
363 		perror("setacl error");
364 		return (-1);
365 	}
366 	return (0);
367 }
368 
369 /*
370  * parse_entry_list() parses entry list (listp) separated by commas.
371  * Once it gets an ACL entry, it calls convert_to_aclent_t() to convert
372  * to internal format.
373  */
374 static int
375 parse_entry_list(aclent_t **aclpp, int *aclcntp, char *listp, int mode)
376 {
377 	char	*commap;
378 
379 	if (listp == NULL)
380 		return (0);
381 	while ((commap = (char *)strchr(listp, ',')) != NULL) {
382 		*commap = '\0';
383 		*aclcntp += 1;
384 		/* aclcnt may be updated after the call: add or modify */
385 		if (convert_to_aclent_t(listp, aclcntp, aclpp, mode) == -1)
386 			return (-1);
387 		listp = ++commap;
388 	}
389 	/* this is for only one entry or last entry */
390 	if (*listp != '\0') {
391 		*aclcntp += 1;
392 		if (convert_to_aclent_t(listp, aclcntp, aclpp, mode) == -1)
393 			return (-1);
394 	}
395 	return (0);
396 }
397 
398 /*
399  * convert_to_aclent_t() converts an acl entry in ascii format (fields separated
400  * by colon) into aclent_t and appends it to the current ACL. It also handles
401  * memory allocation/deallocation for acl entries in aclent_t format.
402  * aclpp that contains acl entries in acl format will be returned.
403  * We don't check duplicates.
404  */
405 static int
406 convert_to_aclent_t(char *entryp, int *cntp, aclent_t **aclpp, int mode)
407 {
408 	aclent_t	*new_aclp;
409 	aclent_t	tmpacl;
410 	aclent_t	*taclp, *centry, *gentry;
411 	int		cur_cnt;
412 	int		found = 0;
413 	int		is_obj;
414 
415 	if (entryp == NULL)
416 		return (0);
417 
418 	if (*cntp > 1)
419 		new_aclp = (aclent_t *)realloc(*aclpp,
420 		    sizeof (aclent_t) * (*cntp));
421 	else
422 		new_aclp = (aclent_t *) malloc(sizeof (aclent_t) * (*cntp));
423 	if (new_aclp == NULL) {
424 		fprintf(stderr,
425 		    gettext("Insufficient memory for acl %d\n"), *cntp);
426 		return (-1);
427 	}
428 
429 	tmpacl.a_id = 0;	/* id field needs to be initialized */
430 	if (entryp[0] == 'u')
431 		tmpacl.a_id = getuid();	/* id field for user */
432 	if (entryp[0] == 'g')
433 		tmpacl.a_id = getgid();	/* id field for group */
434 
435 	tmpacl.a_type = 0;
436 	if (parse_entry(entryp, &tmpacl, mode) == -1)
437 		return (-1);
438 
439 	is_obj = ((tmpacl.a_type == USER_OBJ) ||
440 	    (tmpacl.a_type == GROUP_OBJ) ||
441 	    (tmpacl.a_type == CLASS_OBJ) ||
442 	    (tmpacl.a_type == DEF_USER_OBJ) ||
443 	    (tmpacl.a_type == DEF_GROUP_OBJ) ||
444 	    (tmpacl.a_type == DEF_OTHER_OBJ));
445 
446 	cur_cnt = *cntp - 1;
447 	switch (mode) {
448 	case MODIFY:	/* and add */
449 		for (taclp = new_aclp; cur_cnt-- > 0; taclp++) {
450 			if (taclp->a_type == tmpacl.a_type &&
451 			    ((taclp->a_id == tmpacl.a_id) || is_obj)) {
452 				found++;
453 				/* cnt is added before it's called */
454 				*cntp -= 1;
455 				taclp->a_perm = tmpacl.a_perm;
456 				break;
457 			}
458 		}
459 		if (!found)	/* Add it to the end: no need to change cntp */
460 			memcpy(new_aclp + *cntp -1, &tmpacl, sizeof (aclent_t));
461 		break;
462 
463 	case DELETE:
464 		for (taclp = new_aclp; cur_cnt-- > 0; taclp++) {
465 			if (taclp->a_type == tmpacl.a_type &&
466 			    ((taclp->a_id == tmpacl.a_id) || is_obj)) {
467 				found++;
468 				/* move up the rest */
469 				while (cur_cnt-- > 0) {
470 					memcpy(taclp, taclp+1,
471 					    sizeof (aclent_t));
472 					taclp++;
473 				}
474 				*cntp = *cntp - 2;
475 				break;
476 			}
477 		}
478 		if (!found)
479 			*cntp -= 1;
480 		break;
481 
482 	case SET:
483 		/* we may check duplicate before copying over?? */
484 		memcpy(new_aclp + *cntp -1, &tmpacl, sizeof (aclent_t));
485 		break;
486 
487 	default:
488 		fprintf(stderr,
489 		    gettext("Unrecognized mode: internal error\n"));
490 		break;
491 	}
492 
493 	/*
494 	 * If converting from non-trivial acl entry to trivial one,
495 	 * reset CLASS_OBJ's permission with that of GROUP_OBJ.
496 	 */
497 
498 	if (mode == DELETE) {
499 		boolean_t	trivial = B_TRUE;	/* assumption */
500 		cur_cnt = *cntp;
501 		for (taclp = new_aclp; cur_cnt-- > 0; taclp++) {
502 			switch (taclp->a_type) {
503 				case USER_OBJ:
504 				case OTHER_OBJ:
505 					break;
506 				case CLASS_OBJ:
507 					centry = taclp;
508 					break;
509 				case GROUP_OBJ:
510 					gentry = taclp;
511 					break;
512 				default:
513 					/*
514 					 * Confirmed that the new acl set is
515 					 * still a non-trivial acl.
516 					 * Skip reset.
517 					 */
518 					trivial = B_FALSE;
519 			}
520 		}
521 		if (centry != NULL && gentry != NULL && trivial == B_TRUE)
522 			centry->a_perm = gentry->a_perm;
523 	}
524 	*aclpp = new_aclp; 	/* return new acl entries */
525 	return (0);
526 }
527 
528 static void
529 usage()
530 {
531 	(void) fprintf(stderr, gettext("usage:\n"));
532 	(void) fprintf(stderr,
533 	    gettext("\tsetfacl [-r] -f aclfile file ...\n"));
534 	(void) fprintf(stderr,
535 	    gettext("\tsetfacl [-r] -d acl_entries file ...\n"));
536 	(void) fprintf(stderr,
537 	    gettext("\tsetfacl [-r] -m acl_entries file ...\n"));
538 	(void) fprintf(stderr,
539 	    gettext("\tsetfacl [-r] -s acl_entries file ...\n"));
540 	exit(1);
541 }
542 
543 static void
544 err_handle(int cnt, aclent_t *aclentp)
545 {
546 	int	rc;
547 	int	which;
548 
549 	rc = aclcheck(aclentp, cnt, &which);
550 	switch (rc) {
551 	case USER_ERROR:
552 		fprintf(stderr,
553 		    gettext("There is more than one user owner entry"));
554 		fprintf(stderr,
555 		    gettext(" -- error found at entry index %d\n"), which);
556 		break;
557 	case GRP_ERROR:
558 		fprintf(stderr,
559 		    gettext("There is more than one group owner entry"));
560 		fprintf(stderr,
561 		    gettext(" -- error found at entry index %d\n"), which);
562 		break;
563 	case CLASS_ERROR:
564 		fprintf(stderr,
565 		    gettext("There is more than one mask entry"));
566 		fprintf(stderr,
567 		    gettext(" -- error found at entry index %d\n"), which);
568 		break;
569 	case OTHER_ERROR:
570 		fprintf(stderr,
571 		    gettext("There is more than one other entry"));
572 		fprintf(stderr,
573 		    gettext(" -- error found at entry index %d\n"), which);
574 		break;
575 	case DUPLICATE_ERROR:
576 		fprintf(stderr,
577 		    gettext("Duplicate user or group entries"));
578 		fprintf(stderr,
579 		    gettext(" -- error found at entry index %d\n"), which);
580 		break;
581 	case MISS_ERROR:
582 		fprintf(stderr,
583 		    gettext("Missing user/group owner, other, mask entry\n"));
584 		break;
585 	case MEM_ERROR:
586 		fprintf(stderr,
587 		    gettext("Insufficient memory\n"));
588 		break;
589 	case ENTRY_ERROR:
590 		fprintf(stderr,
591 		    gettext("Unrecognized entry type"));
592 		fprintf(stderr,
593 		    gettext(" -- error found at entry index %d\n"), which);
594 		break;
595 	default:
596 		/* error is not from aclcheck */
597 		fprintf(stderr,
598 		    gettext("aclsort error\n"));
599 		break;
600 	}
601 }
602 
603 static int
604 parse_entry(char *fieldp, aclent_t *aclentp, int mode)
605 {
606 	char		*colonp;
607 	int		def_flag = 0, mo_flag = 0;
608 	int		id;
609 	struct passwd	*pwp;
610 	struct group	*grp;
611 
612 	colonp = (char *)strchr(fieldp, ':');
613 	if (colonp == NULL) {
614 		fprintf(stderr,
615 		    gettext("Can't find colon delimiter %s\n"), fieldp);
616 		return (-1);
617 	}
618 	*colonp = '\0';
619 	if ((strcmp(fieldp, "default") == 0) || (strcmp(fieldp, "d") == 0)) {
620 		def_flag++;
621 		fieldp = ++colonp;
622 		colonp = (char *)strchr(fieldp, ':');
623 		if (colonp == NULL) {
624 			fprintf(stderr,
625 			    gettext("Can't find colon delimiter %s\n"), fieldp);
626 			return (-1);
627 		}
628 		*colonp = '\0';
629 	}
630 
631 	/* process entry type */
632 	if ((strcmp(fieldp, "user") == 0) || (strcmp(fieldp, "u") == 0)) {
633 		if (def_flag)
634 			aclentp->a_type = DEF_USER;
635 		else
636 			aclentp->a_type = USER;
637 	}
638 	if ((strcmp(fieldp, "group") == 0) || (strcmp(fieldp, "g") == 0)) {
639 		if (def_flag)
640 			aclentp->a_type = DEF_GROUP;
641 		else
642 			aclentp->a_type = GROUP;
643 	}
644 	if ((strcmp(fieldp, "mask") == 0) || (strcmp(fieldp, "m") == 0)) {
645 		if (def_flag)
646 			aclentp->a_type = DEF_CLASS_OBJ;
647 		else
648 			aclentp->a_type = CLASS_OBJ;
649 	}
650 	if ((strcmp(fieldp, "other") == 0) || (strcmp(fieldp, "o") == 0)) {
651 		if (def_flag)
652 			aclentp->a_type = DEF_OTHER_OBJ;
653 		else
654 			aclentp->a_type = OTHER_OBJ;
655 	}
656 
657 	/* still can't determine entry type */
658 	if (aclentp->a_type == 0) {
659 		fprintf(stderr,
660 		    gettext("Unrecognized entry type %s \n"), fieldp);
661 		return (-1);
662 	}
663 
664 	/* mask and other entries dont have id field */
665 	if (aclentp->a_type != CLASS_OBJ && aclentp->a_type != OTHER_OBJ &&
666 	    aclentp->a_type != DEF_CLASS_OBJ &&
667 	    aclentp->a_type != DEF_OTHER_OBJ) {
668 		/* process id: */
669 		fieldp = ++colonp;
670 		colonp = (char *)strchr(fieldp, ':');
671 		if (colonp == NULL) {
672 			if (mode != DELETE) {
673 				fprintf(stderr,
674 				    gettext("Can't find colon delimiter %s\n"),
675 				    fieldp);
676 				return (-1);
677 			}
678 		} else
679 			*colonp = '\0';
680 
681 		if (*fieldp == '\0') {
682 			/* empty uid */
683 			if (aclentp->a_type == USER)
684 				aclentp->a_type = USER_OBJ;
685 			if (aclentp->a_type == DEF_USER)
686 				aclentp->a_type = DEF_USER_OBJ;
687 			if (aclentp->a_type == GROUP)
688 				aclentp->a_type = GROUP_OBJ;
689 			if (aclentp->a_type == DEF_GROUP)
690 				aclentp->a_type = DEF_GROUP_OBJ;
691 		} else {
692 			/* see if it's a user/group name */
693 			if (aclentp->a_type == USER ||
694 			    aclentp->a_type == USER_OBJ ||
695 			    aclentp->a_type == DEF_USER ||
696 			    aclentp->a_type == DEF_USER_OBJ) {
697 				if ((pwp = getpwnam(fieldp)) != NULL)
698 					aclentp->a_id = pwp->pw_uid;
699 				else {
700 					/* treat it as numeric id */
701 					id = conv_id(fieldp);
702 					if (id == -1)
703 						return (-1);
704 					aclentp->a_id = id;
705 				}
706 			} else {
707 				/* group name */
708 				if ((grp = getgrnam(fieldp)) != NULL)
709 					aclentp->a_id = grp->gr_gid;
710 				else {
711 					id = conv_id(fieldp);
712 					if (id == -1)
713 						return (-1);
714 					aclentp->a_id = id;
715 				}
716 			}
717 		}
718 	} else {
719 		/* it is mask/other entry */
720 		mo_flag = 1;
721 	}
722 
723 	/* process permission: rwx and [0]n  format */
724 	if (mode == DELETE)
725 		/* delete format: no permission field */
726 		return (0);
727 	fieldp = ++colonp;
728 	colonp = (char *)strchr(fieldp, ':');
729 	if (colonp != NULL) {
730 		if (mo_flag == 1) {
731 			/* Use only single : on mask/other entry */
732 			(void) fprintf(stderr, gettext("use only 1 colon for "
733 			    "mask and other entries.\n"));
734 			return (-1);
735 		} else {
736 			/* it's ok to have extra colon */
737 			*colonp = '\0';
738 		}
739 	}
740 
741 	if ((int)strlen(fieldp) > 3) {
742 		fprintf(stderr,
743 		    gettext("only rwx or [0]n format is allowed\n"));
744 		return (-1);
745 	}
746 	if (strlen(fieldp) == 3) {
747 		aclentp->a_perm = 0;
748 		/* treat it as rwx */
749 		if (*fieldp == 'r')
750 			aclentp->a_perm += 4;
751 		else
752 			if (*fieldp != '-') {
753 				fprintf(stderr,
754 				    gettext("Unrecognized character "));
755 				fprintf(stderr,
756 				    gettext("found in mode field\n"));
757 				return (-1);
758 			}
759 		fieldp++;
760 		if (*fieldp == 'w')
761 			aclentp->a_perm += 2;
762 		else
763 			if (*fieldp != '-') {
764 				fprintf(stderr,
765 				    gettext("Unrecognized character "));
766 				fprintf(stderr,
767 				    gettext("found in mode field\n"));
768 				return (-1);
769 			}
770 		fieldp++;
771 		if (*fieldp == 'x')
772 			aclentp->a_perm += 1;
773 		else
774 			if (*fieldp != '-') {
775 				fprintf(stderr,
776 				    gettext("Unrecognized character "));
777 				fprintf(stderr,
778 				    gettext("found in mode field\n"));
779 				return (-1);
780 			}
781 		return (0);
782 	}
783 
784 	if (*fieldp == '\0')
785 		return (0);
786 
787 	if (*fieldp >= '0' && *fieldp <= '7')
788 		aclentp->a_perm = *fieldp - '0';
789 	else {
790 		fprintf(stderr, gettext("Unrecognized character "));
791 		fprintf(stderr, gettext("found in mode field\n"));
792 		return (-1);
793 	}
794 	if (aclentp->a_perm == 0 && *++fieldp != '\0') {
795 		/* look at next char */
796 		if (*fieldp >= '0' && *fieldp <= '7')
797 			aclentp->a_perm = *fieldp - '0';
798 		else {
799 			fprintf(stderr, gettext("Unrecognized character "));
800 			fprintf(stderr, gettext("found in mode field\n"));
801 			fprintf(stderr,
802 			    gettext("Check also the number of fields "));
803 			fprintf(stderr,
804 			    gettext("(default) mask and other entries\n"));
805 			return (-1);
806 		}
807 	}
808 	/* check for junk at the end ??? */
809 	return (0);
810 }
811 
812 /*
813  * This function is different from atoi() in that it checks for
814  * valid digit in the id field whereas atoi() won't report any
815  * error.
816  */
817 static int
818 conv_id(char *fieldp)
819 {
820 	int	a_id = 0;
821 
822 	for (; *fieldp != '\0'; fieldp++) {
823 		if (!isdigit(*fieldp)) {
824 			fprintf(stderr, gettext("non-digit in id field\n"));
825 			return (-1);
826 		}
827 		a_id = a_id * 10 + (*fieldp - '0');
828 	}
829 	return (a_id);
830 }
831