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