xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nfs_acl.c (revision 2209d3c850d80c0681948f966816f28f767575cb)
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  * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
27  */
28 
29 #include <sys/types.h>
30 #include <sys/errno.h>
31 #include <sys/tiuser.h>
32 #include <setjmp.h>
33 #include <pwd.h>
34 #include <grp.h>
35 
36 #include <rpc/types.h>
37 #include <rpc/xdr.h>
38 #include <rpc/auth.h>
39 #include <rpc/clnt.h>
40 #include <rpc/rpc_msg.h>
41 #include <string.h>
42 #include "snoop.h"
43 
44 #include <sys/stat.h>
45 
46 extern char *get_sum_line();
47 extern void check_retransmit();
48 extern char *sum_nfsfh();
49 extern int sum_nfsstat();
50 extern int detail_nfsstat();
51 extern void detail_nfsfh();
52 extern void detail_fattr();
53 extern void skip_fattr();
54 extern char *sum_nfsfh3();
55 extern int sum_nfsstat3();
56 extern int detail_nfsstat3();
57 extern void detail_post_op_attr();
58 extern void detail_nfsfh3();
59 extern int sum_nfsstat4();
60 extern int detail_nfsstat4();
61 
62 extern jmp_buf xdr_err;
63 
64 static void aclcall2();
65 static void aclreply2();
66 static void aclcall3();
67 static void aclreply3();
68 static void aclcall4();
69 static void aclreply4();
70 static void detail_access2();
71 static char *sum_access2();
72 static void detail_mask();
73 static void detail_secattr();
74 static void detail_aclent();
75 static char *detail_uname();
76 static char *detail_gname();
77 static char *detail_perm(ushort_t);
78 static void interpret_nfs_acl2(int, int, int, int, int, char *, int);
79 static void interpret_nfs_acl3(int, int, int, int, int, char *, int);
80 static void interpret_nfs_acl4(int, int, int, int, int, char *, int);
81 
82 #define	ACLPROC2_NULL		((unsigned long)(0))
83 #define	ACLPROC2_GETACL		((unsigned long)(1))
84 #define	ACLPROC2_SETACL		((unsigned long)(2))
85 #define	ACLPROC2_GETATTR	((unsigned long)(3))
86 #define	ACLPROC2_ACCESS		((unsigned long)(4))
87 #define	ACLPROC2_GETXATTRDIR	((unsigned long)(5))
88 
89 #define	ACLPROC3_NULL		((unsigned long)(0))
90 #define	ACLPROC3_GETACL		((unsigned long)(1))
91 #define	ACLPROC3_SETACL		((unsigned long)(2))
92 #define	ACLPROC3_GETXATTRDIR	((unsigned long)(3))
93 
94 #define	ACLPROC4_NULL		((unsigned long)(0))
95 #define	ACLPROC4_GETACL		((unsigned long)(1))
96 #define	ACLPROC4_SETACL		((unsigned long)(2))
97 
98 #define	NA_USER_OBJ	0x1
99 #define	NA_USER		0x2
100 #define	NA_GROUP_OBJ	0x4
101 #define	NA_GROUP	0x8
102 #define	NA_CLASS_OBJ	0x10
103 #define	NA_OTHER_OBJ	0x20
104 #define	NA_ACL_DEFAULT	0x1000
105 
106 #define	NA_DEF_USER_OBJ		(NA_USER_OBJ | NA_ACL_DEFAULT)
107 #define	NA_DEF_USER		(NA_USER | NA_ACL_DEFAULT)
108 #define	NA_DEF_GROUP_OBJ	(NA_GROUP_OBJ | NA_ACL_DEFAULT)
109 #define	NA_DEF_GROUP		(NA_GROUP | NA_ACL_DEFAULT)
110 #define	NA_DEF_CLASS_OBJ	(NA_CLASS_OBJ | NA_ACL_DEFAULT)
111 #define	NA_DEF_OTHER_OBJ	(NA_OTHER_OBJ | NA_ACL_DEFAULT)
112 
113 #define	NA_ACL		0x1
114 #define	NA_ACLCNT	0x2
115 #define	NA_DFACL	0x4
116 #define	NA_DFACLCNT	0x8
117 
118 #define	ACCESS2_READ	0x0001
119 #define	ACCESS2_LOOKUP	0x0002
120 #define	ACCESS2_MODIFY	0x0004
121 #define	ACCESS2_EXTEND	0x0008
122 #define	ACCESS2_DELETE	0x0010
123 #define	ACCESS2_EXECUTE	0x0020
124 
125 static char *procnames_short_v2[] = {
126 	"NULL2",	/*  0 */
127 	"GETACL2",	/*  1 */
128 	"SETACL2",	/*  2 */
129 	"GETATTR2",	/*  3 */
130 	"ACCESS2",	/*  4 */
131 	"GETXATTRDIR2",	/*  5 */
132 };
133 static char *procnames_short_v3[] = {
134 	"NULL3",	/*  0 */
135 	"GETACL3",	/*  1 */
136 	"SETACL3",	/*  2 */
137 	"GETXATTRDIR3",	/*  3 */
138 };
139 static char *procnames_short_v4[] = {
140 	"NULL4",	/*  0 */
141 	"GETACL4",	/*  1 */
142 	"SETACL4",	/*  2 */
143 };
144 
145 static char *procnames_long_v2[] = {
146 	"Null procedure",			/*  0 */
147 	"Get file access control list",		/*  1 */
148 	"Set file access control list",		/*  2 */
149 	"Get file attributes",			/*  3 */
150 	"Check access permission",		/*  4 */
151 	"Get extended attribute directory",	/*  5 */
152 };
153 static char *procnames_long_v3[] = {
154 	"Null procedure",			/*  0 */
155 	"Get file access control list",		/*  1 */
156 	"Set file access control list",		/*  2 */
157 	"Get extended attribute directory",	/*  3 */
158 };
159 static char *procnames_long_v4[] = {
160 	"Null procedure",			/*  0 */
161 	"Get file access control list",		/*  1 */
162 	"Set file access control list",		/*  2 */
163 };
164 
165 #define	MAXPROC_V2	5
166 #define	MAXPROC_V3	3
167 #define	MAXPROC_V4	2
168 
169 /* ARGSUSED */
170 void
171 interpret_nfs_acl(flags, type, xid, vers, proc, data, len)
172 	int flags, type, xid, vers, proc;
173 	char *data;
174 	int len;
175 {
176 
177 	if (vers == 2) {
178 		interpret_nfs_acl2(flags, type, xid, vers, proc, data, len);
179 		return;
180 	}
181 
182 	if (vers == 3) {
183 		interpret_nfs_acl3(flags, type, xid, vers, proc, data, len);
184 		return;
185 	}
186 
187 	if (vers == 4) {
188 		interpret_nfs_acl4(flags, type, xid, vers, proc, data, len);
189 		return;
190 	}
191 }
192 
193 static void
194 interpret_nfs_acl2(int flags, int type, int xid, int vers, int proc,
195     char *data, int len)
196 {
197 	char *line;
198 	char buff[2048];
199 	int off, sz;
200 	char *fh;
201 	ulong_t mask;
202 
203 	if (proc < 0 || proc > MAXPROC_V2)
204 		return;
205 
206 	if (flags & F_SUM) {
207 		line = get_sum_line();
208 
209 		if (type == CALL) {
210 			(void) sprintf(line, "NFS_ACL C %s",
211 			    procnames_short_v2[proc]);
212 			line += strlen(line);
213 			switch (proc) {
214 			case ACLPROC2_GETACL:
215 				fh = sum_nfsfh();
216 				mask = getxdr_u_long();
217 				(void) sprintf(line, "%s mask=0x%lx", fh, mask);
218 				break;
219 			case ACLPROC2_SETACL:
220 				(void) sprintf(line, sum_nfsfh());
221 				break;
222 			case ACLPROC2_GETATTR:
223 				(void) sprintf(line, sum_nfsfh());
224 				break;
225 			case ACLPROC2_ACCESS:
226 				fh = sum_nfsfh();
227 				(void) sprintf(line, "%s (%s)", fh,
228 				    sum_access2());
229 				break;
230 			case ACLPROC2_GETXATTRDIR:
231 				fh = sum_nfsfh();
232 				(void) sprintf(line, "%s create=%s", fh,
233 				    getxdr_bool() ? "true" : "false");
234 				break;
235 			default:
236 				break;
237 			}
238 
239 			check_retransmit(line, (ulong_t)xid);
240 		} else {
241 			(void) sprintf(line, "NFS_ACL R %s ",
242 			    procnames_short_v2[proc]);
243 			line += strlen(line);
244 			switch (proc) {
245 			case ACLPROC2_GETACL:
246 				(void) sum_nfsstat(line);
247 				break;
248 			case ACLPROC2_SETACL:
249 				(void) sum_nfsstat(line);
250 				break;
251 			case ACLPROC2_GETATTR:
252 				(void) sum_nfsstat(line);
253 				break;
254 			case ACLPROC2_ACCESS:
255 				if (sum_nfsstat(line) == 0) {
256 					skip_fattr();
257 					line += strlen(line);
258 					(void) sprintf(line, " (%s)",
259 					    sum_access2());
260 				}
261 				break;
262 			case ACLPROC2_GETXATTRDIR:
263 				if (sum_nfsstat(line) == 0) {
264 					line += strlen(line);
265 					(void) sprintf(line, sum_nfsfh());
266 				}
267 				break;
268 			default:
269 				break;
270 			}
271 		}
272 	}
273 
274 	if (flags & F_DTAIL) {
275 		show_header("NFS_ACL:  ", "Sun NFS_ACL", len);
276 		show_space();
277 		(void) sprintf(get_line(0, 0), "Proc = %d (%s)",
278 		    proc, procnames_long_v2[proc]);
279 		if (type == CALL)
280 			aclcall2(proc);
281 		else
282 			aclreply2(proc);
283 		show_trailer();
284 	}
285 }
286 
287 static void
288 interpret_nfs_acl3(int flags, int type, int xid, int vers, int proc,
289     char *data, int len)
290 {
291 	char *line;
292 	char buff[2048];
293 	int off, sz;
294 	char *fh;
295 	ulong_t mask;
296 
297 	if (proc < 0 || proc > MAXPROC_V3)
298 		return;
299 
300 	if (flags & F_SUM) {
301 		line = get_sum_line();
302 
303 		if (type == CALL) {
304 			(void) sprintf(line, "NFS_ACL C %s",
305 			    procnames_short_v3[proc]);
306 			line += strlen(line);
307 			switch (proc) {
308 			case ACLPROC3_GETACL:
309 				fh = sum_nfsfh3();
310 				mask = getxdr_u_long();
311 				(void) sprintf(line, "%s mask=0x%lx", fh, mask);
312 				break;
313 			case ACLPROC3_SETACL:
314 				(void) sprintf(line, sum_nfsfh3());
315 				break;
316 			case ACLPROC3_GETXATTRDIR:
317 				fh = sum_nfsfh3();
318 				(void) sprintf(line, "%s create=%s", fh,
319 				    getxdr_bool() ? "true" : "false");
320 				break;
321 			default:
322 				break;
323 			}
324 
325 			check_retransmit(line, (ulong_t)xid);
326 		} else {
327 			(void) sprintf(line, "NFS_ACL R %s ",
328 			    procnames_short_v3[proc]);
329 			line += strlen(line);
330 			switch (proc) {
331 			case ACLPROC3_GETACL:
332 				(void) sum_nfsstat3(line);
333 				break;
334 			case ACLPROC3_SETACL:
335 				(void) sum_nfsstat3(line);
336 				break;
337 			case ACLPROC3_GETXATTRDIR:
338 				if (sum_nfsstat3(line) == 0) {
339 					line += strlen(line);
340 					(void) sprintf(line, sum_nfsfh3());
341 				}
342 				break;
343 			default:
344 				break;
345 			}
346 		}
347 	}
348 
349 	if (flags & F_DTAIL) {
350 		show_header("NFS_ACL:  ", "Sun NFS_ACL", len);
351 		show_space();
352 		(void) sprintf(get_line(0, 0), "Proc = %d (%s)",
353 		    proc, procnames_long_v3[proc]);
354 		if (type == CALL)
355 			aclcall3(proc);
356 		else
357 			aclreply3(proc);
358 		show_trailer();
359 	}
360 }
361 
362 static void
363 interpret_nfs_acl4(int flags, int type, int xid, int vers, int proc,
364     char *data, int len)
365 {
366 	char *line;
367 	char buff[2048];
368 	int off, sz;
369 	char *fh;
370 	ulong_t mask;
371 
372 	if (proc < 0 || proc > MAXPROC_V4)
373 		return;
374 
375 	if (flags & F_SUM) {
376 		line = get_sum_line();
377 
378 		if (type == CALL) {
379 			(void) sprintf(line, "NFS_ACL C %s",
380 			    procnames_short_v4[proc]);
381 			line += strlen(line);
382 			switch (proc) {
383 			case ACLPROC4_GETACL:
384 				fh = sum_nfsfh3();
385 				mask = getxdr_u_long();
386 				(void) sprintf(line, "%s mask=0x%lx", fh, mask);
387 				break;
388 			case ACLPROC4_SETACL:
389 				(void) sprintf(line, sum_nfsfh3());
390 				break;
391 			default:
392 				break;
393 			}
394 
395 			check_retransmit(line, (ulong_t)xid);
396 		} else {
397 			(void) sprintf(line, "NFS_ACL R %s ",
398 			    procnames_short_v4[proc]);
399 			line += strlen(line);
400 			switch (proc) {
401 			case ACLPROC4_GETACL:
402 				(void) sum_nfsstat4(line);
403 				break;
404 			case ACLPROC4_SETACL:
405 				(void) sum_nfsstat4(line);
406 				break;
407 			default:
408 				break;
409 			}
410 		}
411 	}
412 
413 	if (flags & F_DTAIL) {
414 		show_header("NFS_ACL:  ", "Sun NFS_ACL", len);
415 		show_space();
416 		(void) sprintf(get_line(0, 0), "Proc = %d (%s)",
417 		    proc, procnames_long_v4[proc]);
418 		if (type == CALL)
419 			aclcall4(proc);
420 		else
421 			aclreply4(proc);
422 		show_trailer();
423 	}
424 }
425 
426 int
427 sum_nfsstat4(char *line)
428 {
429 	ulong_t status;
430 	char *p, *nfsstat4_to_name(int);
431 
432 	status = getxdr_long();
433 	p = nfsstat4_to_name(status);
434 	(void) strcpy(line, p);
435 	return (status);
436 }
437 
438 int
439 detail_nfsstat4()
440 {
441 	ulong_t status;
442 	char buff[64];
443 	int pos;
444 
445 	pos = getxdr_pos();
446 	status = sum_nfsstat4(buff);
447 
448 	(void) sprintf(get_line(pos, getxdr_pos()), "Status = %d (%s)",
449 	    status, buff);
450 
451 	return ((int)status);
452 }
453 
454 /*
455  * Print out version 2 NFS_ACL call packets
456  */
457 static void
458 aclcall2(proc)
459 	int proc;
460 {
461 
462 	switch (proc) {
463 	case ACLPROC2_GETACL:
464 		detail_nfsfh();
465 		detail_mask();
466 		break;
467 	case ACLPROC2_SETACL:
468 		detail_nfsfh();
469 		detail_secattr();
470 		break;
471 	case ACLPROC2_GETATTR:
472 		detail_nfsfh();
473 		break;
474 	case ACLPROC2_ACCESS:
475 		detail_nfsfh();
476 		detail_access2();
477 		break;
478 	default:
479 		break;
480 	}
481 }
482 
483 /*
484  * Print out version 2 NFS_ACL reply packets
485  */
486 static void
487 aclreply2(proc)
488 	int proc;
489 {
490 
491 	switch (proc) {
492 	case ACLPROC2_GETACL:
493 		if (detail_nfsstat() == 0) {
494 			detail_fattr();
495 			detail_secattr();
496 		}
497 		break;
498 	case ACLPROC2_SETACL:
499 		if (detail_nfsstat() == 0)
500 			detail_fattr();
501 		break;
502 	case ACLPROC2_GETATTR:
503 		if (detail_nfsstat() == 0)
504 			detail_fattr();
505 		break;
506 	case ACLPROC2_ACCESS:
507 		if (detail_nfsstat() == 0) {
508 			detail_fattr();
509 			detail_access2();
510 		}
511 		break;
512 	default:
513 		break;
514 	}
515 }
516 
517 /*
518  * Print out version 3 NFS_ACL call packets
519  */
520 static void
521 aclcall3(proc)
522 	int proc;
523 {
524 
525 	switch (proc) {
526 	case ACLPROC3_GETACL:
527 		detail_nfsfh3();
528 		detail_mask();
529 		break;
530 	case ACLPROC3_SETACL:
531 		detail_nfsfh3();
532 		detail_secattr();
533 		break;
534 	default:
535 		break;
536 	}
537 }
538 
539 /*
540  * Print out version 3 NFS_ACL reply packets
541  */
542 static void
543 aclreply3(proc)
544 	int proc;
545 {
546 
547 	switch (proc) {
548 	case ACLPROC3_GETACL:
549 		if (detail_nfsstat3() == 0) {
550 			detail_post_op_attr("");
551 			detail_secattr();
552 		}
553 		break;
554 	case ACLPROC3_SETACL:
555 		if (detail_nfsstat3() == 0)
556 			detail_post_op_attr("");
557 		break;
558 	default:
559 		break;
560 	}
561 }
562 
563 /*
564  * Print out version 4 NFS_ACL call packets
565  */
566 static void
567 aclcall4(proc)
568 	int proc;
569 {
570 
571 	switch (proc) {
572 	case ACLPROC4_GETACL:
573 		detail_nfsfh3();
574 		detail_mask();
575 		break;
576 	case ACLPROC4_SETACL:
577 		detail_nfsfh3();
578 		detail_secattr();
579 		break;
580 	default:
581 		break;
582 	}
583 }
584 
585 /*
586  * Print out version 4 NFS_ACL reply packets
587  */
588 static void
589 aclreply4(proc)
590 	int proc;
591 {
592 
593 	switch (proc) {
594 	case ACLPROC4_GETACL:
595 		if (detail_nfsstat4() == 0) {
596 			detail_post_op_attr("");
597 			detail_secattr();
598 		}
599 		break;
600 	case ACLPROC4_SETACL:
601 		if (detail_nfsstat4() == 0)
602 			detail_post_op_attr("");
603 		break;
604 	default:
605 		break;
606 	}
607 }
608 
609 static void
610 detail_access2()
611 {
612 	uint_t bits;
613 
614 	bits = showxdr_u_long("Access bits = 0x%08x");
615 	(void) sprintf(get_line(0, 0), "	%s",
616 	    getflag(bits, ACCESS2_READ, "Read", "(no read)"));
617 	(void) sprintf(get_line(0, 0), "	%s",
618 	    getflag(bits, ACCESS2_LOOKUP, "Lookup", "(no lookup)"));
619 	(void) sprintf(get_line(0, 0), "	%s",
620 	    getflag(bits, ACCESS2_MODIFY, "Modify", "(no modify)"));
621 	(void) sprintf(get_line(0, 0), "	%s",
622 	    getflag(bits, ACCESS2_EXTEND, "Extend", "(no extend)"));
623 	(void) sprintf(get_line(0, 0), "	%s",
624 	    getflag(bits, ACCESS2_DELETE, "Delete", "(no delete)"));
625 	(void) sprintf(get_line(0, 0), "	%s",
626 	    getflag(bits, ACCESS2_EXECUTE, "Execute", "(no execute)"));
627 }
628 
629 static char *
630 sum_access2()
631 {
632 	int bits;
633 	static char buff[22];
634 
635 	bits = getxdr_u_long();
636 	buff[0] = '\0';
637 
638 	if (bits & ACCESS2_READ)
639 		(void) strcat(buff, "read,");
640 	if (bits & ACCESS2_LOOKUP)
641 		(void) strcat(buff, "lookup,");
642 	if (bits & ACCESS2_MODIFY)
643 		(void) strcat(buff, "modify,");
644 	if (bits & ACCESS2_EXTEND)
645 		(void) strcat(buff, "extend,");
646 	if (bits & ACCESS2_DELETE)
647 		(void) strcat(buff, "delete,");
648 	if (bits & ACCESS2_EXECUTE)
649 		(void) strcat(buff, "execute,");
650 	if (buff[0] != '\0')
651 		buff[strlen(buff) - 1] = '\0';
652 
653 	return (buff);
654 }
655 
656 static void
657 detail_mask()
658 {
659 	ulong_t mask;
660 
661 	mask = showxdr_u_long("Mask = 0x%lx");
662 	(void) sprintf(get_line(0, 0), "	%s",
663 	    getflag(mask, NA_ACL, "aclent", "(no aclent)"));
664 	(void) sprintf(get_line(0, 0), "	%s",
665 	    getflag(mask, NA_ACLCNT, "aclcnt", "(no aclcnt)"));
666 	(void) sprintf(get_line(0, 0), "	%s",
667 	    getflag(mask, NA_DFACL, "dfaclent", "(no dfaclent)"));
668 	(void) sprintf(get_line(0, 0), "	%s",
669 	    getflag(mask, NA_DFACLCNT, "dfaclcnt", "(no dfaclcnt)"));
670 }
671 
672 static void
673 detail_secattr()
674 {
675 
676 	detail_mask();
677 	showxdr_long("Aclcnt = %d");
678 	detail_aclent();
679 	showxdr_long("Dfaclcnt = %d");
680 	detail_aclent();
681 }
682 
683 static void
684 detail_aclent()
685 {
686 	int count;
687 	int type;
688 	int id;
689 	ushort_t perm;
690 
691 	count = getxdr_long();
692 	while (count-- > 0) {
693 		type = getxdr_long();
694 		id = getxdr_long();
695 		perm = getxdr_u_short();
696 		switch (type) {
697 		case NA_USER:
698 			(void) sprintf(get_line(0, 0), "\tuser:%s:%s",
699 			    detail_uname(id), detail_perm(perm));
700 			break;
701 		case NA_USER_OBJ:
702 			(void) sprintf(get_line(0, 0), "\tuser::%s",
703 			    detail_perm(perm));
704 			break;
705 		case NA_GROUP:
706 			(void) sprintf(get_line(0, 0), "\tgroup:%s:%s",
707 			    detail_gname(id), detail_perm(perm));
708 			break;
709 		case NA_GROUP_OBJ:
710 			(void) sprintf(get_line(0, 0), "\tgroup::%s",
711 			    detail_perm(perm));
712 			break;
713 		case NA_CLASS_OBJ:
714 			(void) sprintf(get_line(0, 0), "\tmask:%s",
715 			    detail_perm(perm));
716 			break;
717 		case NA_OTHER_OBJ:
718 			(void) sprintf(get_line(0, 0), "\tother:%s",
719 			    detail_perm(perm));
720 			break;
721 		case NA_DEF_USER:
722 			(void) sprintf(get_line(0, 0), "\tdefault:user:%s:%s",
723 			    detail_uname(id), detail_perm(perm));
724 			break;
725 		case NA_DEF_USER_OBJ:
726 			(void) sprintf(get_line(0, 0), "\tdefault:user::%s",
727 			    detail_perm(perm));
728 			break;
729 		case NA_DEF_GROUP:
730 			(void) sprintf(get_line(0, 0), "\tdefault:group:%s:%s",
731 			    detail_gname(id), detail_perm(perm));
732 			break;
733 		case NA_DEF_GROUP_OBJ:
734 			(void) sprintf(get_line(0, 0), "\tdefault:group::%s",
735 			    detail_perm(perm));
736 			break;
737 		case NA_DEF_CLASS_OBJ:
738 			(void) sprintf(get_line(0, 0), "\tdefault:mask:%s",
739 			    detail_perm(perm));
740 			break;
741 		case NA_DEF_OTHER_OBJ:
742 			(void) sprintf(get_line(0, 0), "\tdefault:other:%s",
743 			    detail_perm(perm));
744 			break;
745 		default:
746 			(void) sprintf(get_line(0, 0), "\tunrecognized entry");
747 			break;
748 		}
749 	}
750 }
751 
752 static char *
753 detail_uname(uid_t uid)
754 {
755 	struct passwd *pwd;
756 	static char uidp[10];
757 
758 	pwd = getpwuid(uid);
759 	if (pwd == NULL) {
760 		sprintf(uidp, "%d", uid);
761 		return (uidp);
762 	}
763 	return (pwd->pw_name);
764 }
765 
766 static char *
767 detail_gname(gid_t gid)
768 {
769 	struct group *grp;
770 	static char gidp[10];
771 
772 	grp = getgrgid(gid);
773 	if (grp == NULL) {
774 		sprintf(gidp, "%d", gid);
775 		return (gidp);
776 	}
777 	return (grp->gr_name);
778 }
779 
780 static char *perms[] = {
781 	"---",
782 	"--x",
783 	"-w-",
784 	"-wx",
785 	"r--",
786 	"r-x",
787 	"rw-",
788 	"rwx"
789 };
790 static char *
791 detail_perm(ushort_t perm)
792 {
793 
794 	if (perm >= sizeof (perms) / sizeof (perms[0]))
795 		return ("?");
796 	return (perms[perm]);
797 }
798