1 /*-
2  * Copyright (c) 2008, 2009 Edward Tomasz Napierała <trasz@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29 
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <errno.h>
34 #include <assert.h>
35 #include <string.h>
36 #include <pwd.h>
37 #include <grp.h>
38 #include <ctype.h>
39 #include <err.h>
40 #include <sys/syscall.h>
41 #include <sys/types.h>
42 #include <sys/acl.h>
43 
44 #include "acl_support.h"
45 
46 #define MAX_ENTRY_LENGTH 512
47 
48 /*
49  * Parse the tag field of ACL entry passed as "str".  If qualifier
50  * needs to follow, then the variable referenced by "need_qualifier"
51  * is set to 1, otherwise it's set to 0.
52  */
53 static int
54 parse_tag(const char *str, acl_entry_t entry, int *need_qualifier)
55 {
56 
57 	assert(need_qualifier != NULL);
58 	*need_qualifier = 0;
59 
60 	if (strcmp(str, "owner@") == 0)
61 		return (acl_set_tag_type(entry, ACL_USER_OBJ));
62 	if (strcmp(str, "group@") == 0)
63 		return (acl_set_tag_type(entry, ACL_GROUP_OBJ));
64 	if (strcmp(str, "everyone@") == 0)
65 		return (acl_set_tag_type(entry, ACL_EVERYONE));
66 
67 	*need_qualifier = 1;
68 
69 	if (strcmp(str, "user") == 0 || strcmp(str, "u") == 0)
70 		return (acl_set_tag_type(entry, ACL_USER));
71 	if (strcmp(str, "group") == 0 || strcmp(str, "g") == 0)
72 		return (acl_set_tag_type(entry, ACL_GROUP));
73 
74 	warnx("malformed ACL: invalid \"tag\" field");
75 
76 	return (-1);
77 }
78 
79 /*
80  * Parse the qualifier field of ACL entry passed as "str".
81  * If user or group name cannot be resolved, then the variable
82  * referenced by "need_qualifier" is set to 1.
83  */
84 static int
85 parse_qualifier(char *str, acl_entry_t entry, int *need_qualifier)
86 {
87 	int qualifier_length, error;
88 	id_t id;
89 	char *end;
90 	struct passwd *pwd;
91 	struct group *grp;
92 	acl_tag_t tag;
93 
94 	assert(need_qualifier != NULL);
95 	*need_qualifier = 0;
96 
97 	qualifier_length = strlen(str);
98 
99 	if (qualifier_length == 0) {
100 		warnx("malformed ACL: empty \"qualifier\" field");
101 		return (-1);
102 	}
103 
104 	/* XXX: Can we assume that valid username never begins with a digit? */
105 	if (isdigit(str[0])) {
106 		id = strtod(str, &end);
107 
108 		if (end - str != qualifier_length) {
109 			warnx("malformed ACL: trailing characters "
110 			    "after numerical id");
111 			return (-1);
112 		}
113 
114 		return (acl_set_qualifier(entry, &id));
115 	}
116 
117 	error = acl_get_tag_type(entry, &tag);
118 	if (error)
119 		return (error);
120 
121 	assert(tag == ACL_USER || tag == ACL_GROUP);
122 
123 	if (tag == ACL_USER) {
124 		/* XXX: Thread-unsafe. */
125 		pwd = getpwnam(str);
126 		if (pwd == NULL) {
127 			*need_qualifier = 1;
128 			return (0);
129 		}
130 
131 		return (acl_set_qualifier(entry, &(pwd->pw_uid)));
132 	}
133 
134 	/* XXX: Thread-unsafe. */
135 	grp = getgrnam(str);
136 	if (grp == NULL) {
137 		*need_qualifier = 1;
138 		return (0);
139 	}
140 
141 	return (acl_set_qualifier(entry, &(grp->gr_gid)));
142 }
143 
144 static int
145 parse_access_mask(char *str, acl_entry_t entry)
146 {
147 	int error;
148 	acl_perm_t perm;
149 
150 	error = _nfs4_parse_access_mask(str, &perm);
151 	if (error)
152 		return (error);
153 
154 	error = acl_set_permset(entry, &perm);
155 
156 	return (error);
157 }
158 
159 static int
160 parse_flags(char *str, acl_entry_t entry)
161 {
162 	int error;
163 	acl_flag_t flags;
164 
165 	error = _nfs4_parse_flags(str, &flags);
166 	if (error)
167 		return (error);
168 
169 	error = acl_set_flagset_np(entry, &flags);
170 
171 	return (error);
172 }
173 
174 static int
175 parse_entry_type(const char *str, acl_entry_t entry)
176 {
177 
178 	if (strcmp(str, "allow") == 0)
179 		return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_ALLOW));
180 	if (strcmp(str, "deny") == 0)
181 		return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_DENY));
182 	if (strcmp(str, "audit") == 0)
183 		return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_AUDIT));
184 	if (strcmp(str, "alarm") == 0)
185 		return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_ALARM));
186 
187 	warnx("malformed ACL: invalid \"type\" field");
188 
189 	return (-1);
190 }
191 
192 static int
193 parse_appended_id(char *str, acl_entry_t entry)
194 {
195 	int qualifier_length;
196 	char *end;
197 	id_t id;
198 
199 	qualifier_length = strlen(str);
200 	if (qualifier_length == 0) {
201 		warnx("malformed ACL: \"appended id\" field present, "
202 	           "but empty");
203 		return (-1);
204 	}
205 
206 	id = strtod(str, &end);
207 	if (end - str != qualifier_length) {
208 		warnx("malformed ACL: appended id is not a number");
209 		return (-1);
210 	}
211 
212 	return (acl_set_qualifier(entry, &id));
213 }
214 
215 static int
216 number_of_colons(const char *str)
217 {
218 	int count = 0;
219 
220 	while (*str != '\0') {
221 		if (*str == ':')
222 			count++;
223 
224 		str++;
225 	}
226 
227 	return (count);
228 }
229 
230 int
231 _nfs4_acl_entry_from_text(acl_t aclp, char *str)
232 {
233 	int error, need_qualifier;
234 	acl_entry_t entry;
235 	char *field, *qualifier_field;
236 
237 	error = acl_create_entry(&aclp, &entry);
238 	if (error)
239 		return (error);
240 
241 	assert(_entry_brand(entry) == ACL_BRAND_NFS4);
242 
243 	if (str == NULL)
244 		goto truncated_entry;
245 	field = strsep(&str, ":");
246 
247 	field = string_skip_whitespace(field);
248 	if ((*field == '\0') && (!str)) {
249 		/*
250 		 * Is an entirely comment line, skip to next
251 		 * comma.
252 		 */
253 		return (0);
254 	}
255 
256 	error = parse_tag(field, entry, &need_qualifier);
257 	if (error)
258 		goto malformed_field;
259 
260 	if (need_qualifier) {
261 		if (str == NULL)
262 			goto truncated_entry;
263 		qualifier_field = field = strsep(&str, ":");
264 		error = parse_qualifier(field, entry, &need_qualifier);
265 		if (error)
266 			goto malformed_field;
267 	}
268 
269 	if (str == NULL)
270 		goto truncated_entry;
271 	field = strsep(&str, ":");
272 	error = parse_access_mask(field, entry);
273 	if (error)
274 		goto malformed_field;
275 
276 	if (str == NULL)
277 		goto truncated_entry;
278 	/* Do we have "flags" field? */
279 	if (number_of_colons(str) > 0) {
280 		field = strsep(&str, ":");
281 		error = parse_flags(field, entry);
282 		if (error)
283 			goto malformed_field;
284 	}
285 
286 	if (str == NULL)
287 		goto truncated_entry;
288 	field = strsep(&str, ":");
289 	error = parse_entry_type(field, entry);
290 	if (error)
291 		goto malformed_field;
292 
293 	if (need_qualifier) {
294 		if (str == NULL) {
295 			warnx("malformed ACL: unknown user or group name "
296 			    "\"%s\"", qualifier_field);
297 			goto truncated_entry;
298 		}
299 
300 		error = parse_appended_id(str, entry);
301 		if (error)
302 			goto malformed_field;
303 	}
304 
305 	return (0);
306 
307 truncated_entry:
308 malformed_field:
309 	acl_delete_entry(aclp, entry);
310 	errno = EINVAL;
311 	return (-1);
312 }
313 /*-
314  * Copyright (c) 2008, 2009 Edward Tomasz Napierała <trasz@FreeBSD.org>
315  * All rights reserved.
316  *
317  * Redistribution and use in source and binary forms, with or without
318  * modification, are permitted provided that the following conditions
319  * are met:
320  * 1. Redistributions of source code must retain the above copyright
321  *    notice, this list of conditions and the following disclaimer.
322  * 2. Redistributions in binary form must reproduce the above copyright
323  *    notice, this list of conditions and the following disclaimer in the
324  *    documentation and/or other materials provided with the distribution.
325  *
326  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
327  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
328  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
329  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
330  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
331  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
332  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
333  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
334  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
335  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
336  * SUCH DAMAGE.
337  */
338 
339 #include <sys/cdefs.h>
340 __FBSDID("$FreeBSD$");
341 
342 #include <stdio.h>
343 #include <stdlib.h>
344 #include <unistd.h>
345 #include <errno.h>
346 #include <assert.h>
347 #include <string.h>
348 #include <pwd.h>
349 #include <grp.h>
350 #include <ctype.h>
351 #include <err.h>
352 #include <sys/syscall.h>
353 #include <sys/types.h>
354 #include <sys/acl.h>
355 
356 #include "acl_support.h"
357 
358 #define MAX_ENTRY_LENGTH 512
359 
360 /*
361  * Parse the tag field of ACL entry passed as "str".  If qualifier
362  * needs to follow, then the variable referenced by "need_qualifier"
363  * is set to 1, otherwise it's set to 0.
364  */
365 static int
366 parse_tag(const char *str, acl_entry_t entry, int *need_qualifier)
367 {
368 
369 	assert(need_qualifier != NULL);
370 	*need_qualifier = 0;
371 
372 	if (strcmp(str, "owner@") == 0)
373 		return (acl_set_tag_type(entry, ACL_USER_OBJ));
374 	if (strcmp(str, "group@") == 0)
375 		return (acl_set_tag_type(entry, ACL_GROUP_OBJ));
376 	if (strcmp(str, "everyone@") == 0)
377 		return (acl_set_tag_type(entry, ACL_EVERYONE));
378 
379 	*need_qualifier = 1;
380 
381 	if (strcmp(str, "user") == 0 || strcmp(str, "u") == 0)
382 		return (acl_set_tag_type(entry, ACL_USER));
383 	if (strcmp(str, "group") == 0 || strcmp(str, "g") == 0)
384 		return (acl_set_tag_type(entry, ACL_GROUP));
385 
386 	warnx("malformed ACL: invalid \"tag\" field");
387 
388 	return (-1);
389 }
390 
391 /*
392  * Parse the qualifier field of ACL entry passed as "str".
393  * If user or group name cannot be resolved, then the variable
394  * referenced by "need_qualifier" is set to 1.
395  */
396 static int
397 parse_qualifier(char *str, acl_entry_t entry, int *need_qualifier)
398 {
399 	int qualifier_length, error;
400 	id_t id;
401 	char *end;
402 	struct passwd *pwd;
403 	struct group *grp;
404 	acl_tag_t tag;
405 
406 	assert(need_qualifier != NULL);
407 	*need_qualifier = 0;
408 
409 	qualifier_length = strlen(str);
410 
411 	if (qualifier_length == 0) {
412 		warnx("malformed ACL: empty \"qualifier\" field");
413 		return (-1);
414 	}
415 
416 	/* XXX: Can we assume that valid username never begins with a digit? */
417 	if (isdigit(str[0])) {
418 		id = strtod(str, &end);
419 
420 		if (end - str != qualifier_length) {
421 			warnx("malformed ACL: trailing characters "
422 			    "after numerical id");
423 			return (-1);
424 		}
425 
426 		return (acl_set_qualifier(entry, &id));
427 	}
428 
429 	error = acl_get_tag_type(entry, &tag);
430 	if (error)
431 		return (error);
432 
433 	assert(tag == ACL_USER || tag == ACL_GROUP);
434 
435 	if (tag == ACL_USER) {
436 		/* XXX: Thread-unsafe. */
437 		pwd = getpwnam(str);
438 		if (pwd == NULL) {
439 			*need_qualifier = 1;
440 			return (0);
441 		}
442 
443 		return (acl_set_qualifier(entry, &(pwd->pw_uid)));
444 	}
445 
446 	/* XXX: Thread-unsafe. */
447 	grp = getgrnam(str);
448 	if (grp == NULL) {
449 		*need_qualifier = 1;
450 		return (0);
451 	}
452 
453 	return (acl_set_qualifier(entry, &(grp->gr_gid)));
454 }
455 
456 static int
457 parse_access_mask(char *str, acl_entry_t entry)
458 {
459 	int error;
460 	acl_perm_t perm;
461 
462 	error = _nfs4_parse_access_mask(str, &perm);
463 	if (error)
464 		return (error);
465 
466 	error = acl_set_permset(entry, &perm);
467 
468 	return (error);
469 }
470 
471 static int
472 parse_flags(char *str, acl_entry_t entry)
473 {
474 	int error;
475 	acl_flag_t flags;
476 
477 	error = _nfs4_parse_flags(str, &flags);
478 	if (error)
479 		return (error);
480 
481 	error = acl_set_flagset_np(entry, &flags);
482 
483 	return (error);
484 }
485 
486 static int
487 parse_entry_type(const char *str, acl_entry_t entry)
488 {
489 
490 	if (strcmp(str, "allow") == 0)
491 		return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_ALLOW));
492 	if (strcmp(str, "deny") == 0)
493 		return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_DENY));
494 	if (strcmp(str, "audit") == 0)
495 		return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_AUDIT));
496 	if (strcmp(str, "alarm") == 0)
497 		return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_ALARM));
498 
499 	warnx("malformed ACL: invalid \"type\" field");
500 
501 	return (-1);
502 }
503 
504 static int
505 parse_appended_id(char *str, acl_entry_t entry)
506 {
507 	int qualifier_length;
508 	char *end;
509 	id_t id;
510 
511 	qualifier_length = strlen(str);
512 	if (qualifier_length == 0) {
513 		warnx("malformed ACL: \"appended id\" field present, "
514 	           "but empty");
515 		return (-1);
516 	}
517 
518 	id = strtod(str, &end);
519 	if (end - str != qualifier_length) {
520 		warnx("malformed ACL: appended id is not a number");
521 		return (-1);
522 	}
523 
524 	return (acl_set_qualifier(entry, &id));
525 }
526 
527 static int
528 number_of_colons(const char *str)
529 {
530 	int count = 0;
531 
532 	while (*str != '\0') {
533 		if (*str == ':')
534 			count++;
535 
536 		str++;
537 	}
538 
539 	return (count);
540 }
541 
542 int
543 _nfs4_acl_entry_from_text(acl_t aclp, char *str)
544 {
545 	int error, need_qualifier;
546 	acl_entry_t entry;
547 	char *field, *qualifier_field;
548 
549 	error = acl_create_entry(&aclp, &entry);
550 	if (error)
551 		return (error);
552 
553 	assert(_entry_brand(entry) == ACL_BRAND_NFS4);
554 
555 	if (str == NULL)
556 		goto truncated_entry;
557 	field = strsep(&str, ":");
558 
559 	field = string_skip_whitespace(field);
560 	if ((*field == '\0') && (!str)) {
561 		/*
562 		 * Is an entirely comment line, skip to next
563 		 * comma.
564 		 */
565 		return (0);
566 	}
567 
568 	error = parse_tag(field, entry, &need_qualifier);
569 	if (error)
570 		goto malformed_field;
571 
572 	if (need_qualifier) {
573 		if (str == NULL)
574 			goto truncated_entry;
575 		qualifier_field = field = strsep(&str, ":");
576 		error = parse_qualifier(field, entry, &need_qualifier);
577 		if (error)
578 			goto malformed_field;
579 	}
580 
581 	if (str == NULL)
582 		goto truncated_entry;
583 	field = strsep(&str, ":");
584 	error = parse_access_mask(field, entry);
585 	if (error)
586 		goto malformed_field;
587 
588 	if (str == NULL)
589 		goto truncated_entry;
590 	/* Do we have "flags" field? */
591 	if (number_of_colons(str) > 0) {
592 		field = strsep(&str, ":");
593 		error = parse_flags(field, entry);
594 		if (error)
595 			goto malformed_field;
596 	}
597 
598 	if (str == NULL)
599 		goto truncated_entry;
600 	field = strsep(&str, ":");
601 	error = parse_entry_type(field, entry);
602 	if (error)
603 		goto malformed_field;
604 
605 	if (need_qualifier) {
606 		if (str == NULL) {
607 			warnx("malformed ACL: unknown user or group name "
608 			    "\"%s\"", qualifier_field);
609 			goto truncated_entry;
610 		}
611 
612 		error = parse_appended_id(str, entry);
613 		if (error)
614 			goto malformed_field;
615 	}
616 
617 	return (0);
618 
619 truncated_entry:
620 malformed_field:
621 	acl_delete_entry(aclp, entry);
622 	errno = EINVAL;
623 	return (-1);
624 }
625 /*-
626  * Copyright (c) 2008, 2009 Edward Tomasz Napierała <trasz@FreeBSD.org>
627  * All rights reserved.
628  *
629  * Redistribution and use in source and binary forms, with or without
630  * modification, are permitted provided that the following conditions
631  * are met:
632  * 1. Redistributions of source code must retain the above copyright
633  *    notice, this list of conditions and the following disclaimer.
634  * 2. Redistributions in binary form must reproduce the above copyright
635  *    notice, this list of conditions and the following disclaimer in the
636  *    documentation and/or other materials provided with the distribution.
637  *
638  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
639  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
640  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
641  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
642  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
643  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
644  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
645  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
646  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
647  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
648  * SUCH DAMAGE.
649  */
650 
651 #include <sys/cdefs.h>
652 __FBSDID("$FreeBSD$");
653 
654 #include <stdio.h>
655 #include <stdlib.h>
656 #include <unistd.h>
657 #include <errno.h>
658 #include <assert.h>
659 #include <string.h>
660 #include <pwd.h>
661 #include <grp.h>
662 #include <ctype.h>
663 #include <err.h>
664 #include <sys/syscall.h>
665 #include <sys/types.h>
666 #include <sys/acl.h>
667 
668 #include "acl_support.h"
669 
670 #define MAX_ENTRY_LENGTH 512
671 
672 /*
673  * Parse the tag field of ACL entry passed as "str".  If qualifier
674  * needs to follow, then the variable referenced by "need_qualifier"
675  * is set to 1, otherwise it's set to 0.
676  */
677 static int
678 parse_tag(const char *str, acl_entry_t entry, int *need_qualifier)
679 {
680 
681 	assert(need_qualifier != NULL);
682 	*need_qualifier = 0;
683 
684 	if (strcmp(str, "owner@") == 0)
685 		return (acl_set_tag_type(entry, ACL_USER_OBJ));
686 	if (strcmp(str, "group@") == 0)
687 		return (acl_set_tag_type(entry, ACL_GROUP_OBJ));
688 	if (strcmp(str, "everyone@") == 0)
689 		return (acl_set_tag_type(entry, ACL_EVERYONE));
690 
691 	*need_qualifier = 1;
692 
693 	if (strcmp(str, "user") == 0 || strcmp(str, "u") == 0)
694 		return (acl_set_tag_type(entry, ACL_USER));
695 	if (strcmp(str, "group") == 0 || strcmp(str, "g") == 0)
696 		return (acl_set_tag_type(entry, ACL_GROUP));
697 
698 	warnx("malformed ACL: invalid \"tag\" field");
699 
700 	return (-1);
701 }
702 
703 /*
704  * Parse the qualifier field of ACL entry passed as "str".
705  * If user or group name cannot be resolved, then the variable
706  * referenced by "need_qualifier" is set to 1.
707  */
708 static int
709 parse_qualifier(char *str, acl_entry_t entry, int *need_qualifier)
710 {
711 	int qualifier_length, error;
712 	id_t id;
713 	char *end;
714 	struct passwd *pwd;
715 	struct group *grp;
716 	acl_tag_t tag;
717 
718 	assert(need_qualifier != NULL);
719 	*need_qualifier = 0;
720 
721 	qualifier_length = strlen(str);
722 
723 	if (qualifier_length == 0) {
724 		warnx("malformed ACL: empty \"qualifier\" field");
725 		return (-1);
726 	}
727 
728 	/* XXX: Can we assume that valid username never begins with a digit? */
729 	if (isdigit(str[0])) {
730 		id = strtod(str, &end);
731 
732 		if (end - str != qualifier_length) {
733 			warnx("malformed ACL: trailing characters "
734 			    "after numerical id");
735 			return (-1);
736 		}
737 
738 		return (acl_set_qualifier(entry, &id));
739 	}
740 
741 	error = acl_get_tag_type(entry, &tag);
742 	if (error)
743 		return (error);
744 
745 	assert(tag == ACL_USER || tag == ACL_GROUP);
746 
747 	if (tag == ACL_USER) {
748 		/* XXX: Thread-unsafe. */
749 		pwd = getpwnam(str);
750 		if (pwd == NULL) {
751 			*need_qualifier = 1;
752 			return (0);
753 		}
754 
755 		return (acl_set_qualifier(entry, &(pwd->pw_uid)));
756 	}
757 
758 	/* XXX: Thread-unsafe. */
759 	grp = getgrnam(str);
760 	if (grp == NULL) {
761 		*need_qualifier = 1;
762 		return (0);
763 	}
764 
765 	return (acl_set_qualifier(entry, &(grp->gr_gid)));
766 }
767 
768 static int
769 parse_access_mask(char *str, acl_entry_t entry)
770 {
771 	int error;
772 	acl_perm_t perm;
773 
774 	error = _nfs4_parse_access_mask(str, &perm);
775 	if (error)
776 		return (error);
777 
778 	error = acl_set_permset(entry, &perm);
779 
780 	return (error);
781 }
782 
783 static int
784 parse_flags(char *str, acl_entry_t entry)
785 {
786 	int error;
787 	acl_flag_t flags;
788 
789 	error = _nfs4_parse_flags(str, &flags);
790 	if (error)
791 		return (error);
792 
793 	error = acl_set_flagset_np(entry, &flags);
794 
795 	return (error);
796 }
797 
798 static int
799 parse_entry_type(const char *str, acl_entry_t entry)
800 {
801 
802 	if (strcmp(str, "allow") == 0)
803 		return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_ALLOW));
804 	if (strcmp(str, "deny") == 0)
805 		return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_DENY));
806 	if (strcmp(str, "audit") == 0)
807 		return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_AUDIT));
808 	if (strcmp(str, "alarm") == 0)
809 		return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_ALARM));
810 
811 	warnx("malformed ACL: invalid \"type\" field");
812 
813 	return (-1);
814 }
815 
816 static int
817 parse_appended_id(char *str, acl_entry_t entry)
818 {
819 	int qualifier_length;
820 	char *end;
821 	id_t id;
822 
823 	qualifier_length = strlen(str);
824 	if (qualifier_length == 0) {
825 		warnx("malformed ACL: \"appended id\" field present, "
826 	           "but empty");
827 		return (-1);
828 	}
829 
830 	id = strtod(str, &end);
831 	if (end - str != qualifier_length) {
832 		warnx("malformed ACL: appended id is not a number");
833 		return (-1);
834 	}
835 
836 	return (acl_set_qualifier(entry, &id));
837 }
838 
839 static int
840 number_of_colons(const char *str)
841 {
842 	int count = 0;
843 
844 	while (*str != '\0') {
845 		if (*str == ':')
846 			count++;
847 
848 		str++;
849 	}
850 
851 	return (count);
852 }
853 
854 int
855 _nfs4_acl_entry_from_text(acl_t aclp, char *str)
856 {
857 	int error, need_qualifier;
858 	acl_entry_t entry;
859 	char *field, *qualifier_field;
860 
861 	error = acl_create_entry(&aclp, &entry);
862 	if (error)
863 		return (error);
864 
865 	assert(_entry_brand(entry) == ACL_BRAND_NFS4);
866 
867 	if (str == NULL)
868 		goto truncated_entry;
869 	field = strsep(&str, ":");
870 
871 	field = string_skip_whitespace(field);
872 	if ((*field == '\0') && (!str)) {
873 		/*
874 		 * Is an entirely comment line, skip to next
875 		 * comma.
876 		 */
877 		return (0);
878 	}
879 
880 	error = parse_tag(field, entry, &need_qualifier);
881 	if (error)
882 		goto malformed_field;
883 
884 	if (need_qualifier) {
885 		if (str == NULL)
886 			goto truncated_entry;
887 		qualifier_field = field = strsep(&str, ":");
888 		error = parse_qualifier(field, entry, &need_qualifier);
889 		if (error)
890 			goto malformed_field;
891 	}
892 
893 	if (str == NULL)
894 		goto truncated_entry;
895 	field = strsep(&str, ":");
896 	error = parse_access_mask(field, entry);
897 	if (error)
898 		goto malformed_field;
899 
900 	if (str == NULL)
901 		goto truncated_entry;
902 	/* Do we have "flags" field? */
903 	if (number_of_colons(str) > 0) {
904 		field = strsep(&str, ":");
905 		error = parse_flags(field, entry);
906 		if (error)
907 			goto malformed_field;
908 	}
909 
910 	if (str == NULL)
911 		goto truncated_entry;
912 	field = strsep(&str, ":");
913 	error = parse_entry_type(field, entry);
914 	if (error)
915 		goto malformed_field;
916 
917 	if (need_qualifier) {
918 		if (str == NULL) {
919 			warnx("malformed ACL: unknown user or group name "
920 			    "\"%s\"", qualifier_field);
921 			goto truncated_entry;
922 		}
923 
924 		error = parse_appended_id(str, entry);
925 		if (error)
926 			goto malformed_field;
927 	}
928 
929 	return (0);
930 
931 truncated_entry:
932 malformed_field:
933 	acl_delete_entry(aclp, entry);
934 	errno = EINVAL;
935 	return (-1);
936 }
937