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