xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nfs_acl.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 1991,2001-2003 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"	/* SunOS */
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 
79 #define	ACLPROC2_NULL		((unsigned long)(0))
80 #define	ACLPROC2_GETACL		((unsigned long)(1))
81 #define	ACLPROC2_SETACL		((unsigned long)(2))
82 #define	ACLPROC2_GETATTR	((unsigned long)(3))
83 #define	ACLPROC2_ACCESS		((unsigned long)(4))
84 #define	ACLPROC2_GETXATTRDIR	((unsigned long)(5))
85 
86 #define	ACLPROC3_NULL		((unsigned long)(0))
87 #define	ACLPROC3_GETACL		((unsigned long)(1))
88 #define	ACLPROC3_SETACL		((unsigned long)(2))
89 #define	ACLPROC3_GETXATTRDIR	((unsigned long)(3))
90 
91 #define	ACLPROC4_NULL		((unsigned long)(0))
92 #define	ACLPROC4_GETACL		((unsigned long)(1))
93 #define	ACLPROC4_SETACL		((unsigned long)(2))
94 
95 #define	NA_USER_OBJ	0x1
96 #define	NA_USER		0x2
97 #define	NA_GROUP_OBJ	0x4
98 #define	NA_GROUP	0x8
99 #define	NA_CLASS_OBJ	0x10
100 #define	NA_OTHER_OBJ	0x20
101 #define	NA_ACL_DEFAULT	0x1000
102 
103 #define	NA_DEF_USER_OBJ		(NA_USER_OBJ | NA_ACL_DEFAULT)
104 #define	NA_DEF_USER		(NA_USER | NA_ACL_DEFAULT)
105 #define	NA_DEF_GROUP_OBJ	(NA_GROUP_OBJ | NA_ACL_DEFAULT)
106 #define	NA_DEF_GROUP		(NA_GROUP | NA_ACL_DEFAULT)
107 #define	NA_DEF_CLASS_OBJ	(NA_CLASS_OBJ | NA_ACL_DEFAULT)
108 #define	NA_DEF_OTHER_OBJ	(NA_OTHER_OBJ | NA_ACL_DEFAULT)
109 
110 #define	NA_ACL		0x1
111 #define	NA_ACLCNT	0x2
112 #define	NA_DFACL	0x4
113 #define	NA_DFACLCNT	0x8
114 
115 #define	ACCESS2_READ	0x0001
116 #define	ACCESS2_LOOKUP	0x0002
117 #define	ACCESS2_MODIFY	0x0004
118 #define	ACCESS2_EXTEND	0x0008
119 #define	ACCESS2_DELETE	0x0010
120 #define	ACCESS2_EXECUTE	0x0020
121 
122 static char *procnames_short_v2[] = {
123 	"NULL2",	/*  0 */
124 	"GETACL2",	/*  1 */
125 	"SETACL2",	/*  2 */
126 	"GETATTR2",	/*  3 */
127 	"ACCESS2",	/*  4 */
128 	"GETXATTRDIR2",	/*  5 */
129 };
130 static char *procnames_short_v3[] = {
131 	"NULL3",	/*  0 */
132 	"GETACL3",	/*  1 */
133 	"SETACL3",	/*  2 */
134 	"GETXATTRDIR3",	/*  3 */
135 };
136 static char *procnames_short_v4[] = {
137 	"NULL4",	/*  0 */
138 	"GETACL4",	/*  1 */
139 	"SETACL4",	/*  2 */
140 };
141 
142 static char *procnames_long_v2[] = {
143 	"Null procedure",			/*  0 */
144 	"Get file access control list",		/*  1 */
145 	"Set file access control list",		/*  2 */
146 	"Get file attributes",			/*  3 */
147 	"Check access permission",		/*  4 */
148 	"Get extended attribute directory",	/*  5 */
149 };
150 static char *procnames_long_v3[] = {
151 	"Null procedure",			/*  0 */
152 	"Get file access control list",		/*  1 */
153 	"Set file access control list",		/*  2 */
154 	"Get extended attribute directory",	/*  3 */
155 };
156 static char *procnames_long_v4[] = {
157 	"Null procedure",			/*  0 */
158 	"Get file access control list",		/*  1 */
159 	"Set file access control list",		/*  2 */
160 };
161 
162 #define	MAXPROC_V2	5
163 #define	MAXPROC_V3	3
164 #define	MAXPROC_V4	2
165 
166 /* ARGSUSED */
167 void
168 interpret_nfs_acl(flags, type, xid, vers, proc, data, len)
169 	int flags, type, xid, vers, proc;
170 	char *data;
171 	int len;
172 {
173 
174 	if (vers == 2) {
175 		interpret_nfs_acl2(flags, type, xid, vers, proc, data, len);
176 		return;
177 	}
178 
179 	if (vers == 3) {
180 		interpret_nfs_acl3(flags, type, xid, vers, proc, data, len);
181 		return;
182 	}
183 
184 	if (vers == 4) {
185 		interpret_nfs_acl4(flags, type, xid, vers, proc, data, len);
186 		return;
187 	}
188 }
189 
190 interpret_nfs_acl2(flags, type, xid, vers, proc, data, len)
191 	int flags, type, xid, vers, proc;
192 	char *data;
193 	int len;
194 {
195 	char *line;
196 	char buff[2048];
197 	int off, sz;
198 	char *fh;
199 	ulong_t mask;
200 
201 	if (proc < 0 || proc > MAXPROC_V2)
202 		return;
203 
204 	if (flags & F_SUM) {
205 		line = get_sum_line();
206 
207 		if (type == CALL) {
208 			(void) sprintf(line, "NFS_ACL C %s",
209 				procnames_short_v2[proc]);
210 			line += strlen(line);
211 			switch (proc) {
212 			case ACLPROC2_GETACL:
213 				fh = sum_nfsfh();
214 				mask = getxdr_u_long();
215 				(void) sprintf(line, "%s mask=%lu", fh, mask);
216 				break;
217 			case ACLPROC2_SETACL:
218 				(void) sprintf(line, sum_nfsfh());
219 				break;
220 			case ACLPROC2_GETATTR:
221 				(void) sprintf(line, sum_nfsfh());
222 				break;
223 			case ACLPROC2_ACCESS:
224 				fh = sum_nfsfh();
225 				(void) sprintf(line, "%s (%s)", fh,
226 						sum_access2());
227 				break;
228 			case ACLPROC2_GETXATTRDIR:
229 				fh = sum_nfsfh();
230 				(void) sprintf(line, "%s create=%s", fh,
231 				    getxdr_bool() ? "true" : "false");
232 				break;
233 			default:
234 				break;
235 			}
236 
237 			check_retransmit(line, (ulong_t)xid);
238 		} else {
239 			(void) sprintf(line, "NFS_ACL R %s ",
240 				procnames_short_v2[proc]);
241 			line += strlen(line);
242 			switch (proc) {
243 			case ACLPROC2_GETACL:
244 				(void) sum_nfsstat(line);
245 				break;
246 			case ACLPROC2_SETACL:
247 				(void) sum_nfsstat(line);
248 				break;
249 			case ACLPROC2_GETATTR:
250 				(void) sum_nfsstat(line);
251 				break;
252 			case ACLPROC2_ACCESS:
253 				if (sum_nfsstat(line) == 0) {
254 					skip_fattr();
255 					line += strlen(line);
256 					(void) sprintf(line, " (%s)",
257 							sum_access2());
258 				}
259 				break;
260 			case ACLPROC2_GETXATTRDIR:
261 				if (sum_nfsstat(line) == 0) {
262 					line += strlen(line);
263 					(void) sprintf(line, sum_nfsfh());
264 				}
265 				break;
266 			default:
267 				break;
268 			}
269 		}
270 	}
271 
272 	if (flags & F_DTAIL) {
273 		show_header("NFS_ACL:  ", "Sun NFS_ACL", len);
274 		show_space();
275 		(void) sprintf(get_line(0, 0), "Proc = %d (%s)",
276 			proc, procnames_long_v2[proc]);
277 		if (type == CALL)
278 			aclcall2(proc);
279 		else
280 			aclreply2(proc);
281 		show_trailer();
282 	}
283 }
284 
285 interpret_nfs_acl3(flags, type, xid, vers, proc, data, len)
286 	int flags, type, xid, vers, proc;
287 	char *data;
288 	int len;
289 {
290 	char *line;
291 	char buff[2048];
292 	int off, sz;
293 	char *fh;
294 	ulong_t mask;
295 
296 	if (proc < 0 || proc > MAXPROC_V3)
297 		return;
298 
299 	if (flags & F_SUM) {
300 		line = get_sum_line();
301 
302 		if (type == CALL) {
303 			(void) sprintf(line, "NFS_ACL C %s",
304 				procnames_short_v3[proc]);
305 			line += strlen(line);
306 			switch (proc) {
307 			case ACLPROC3_GETACL:
308 				fh = sum_nfsfh3();
309 				mask = getxdr_u_long();
310 				(void) sprintf(line, "%s mask=%lu", fh, mask);
311 				break;
312 			case ACLPROC3_SETACL:
313 				(void) sprintf(line, sum_nfsfh3());
314 				break;
315 			case ACLPROC3_GETXATTRDIR:
316 				fh = sum_nfsfh3();
317 				(void) sprintf(line, "%s create=%s", fh,
318 				    getxdr_bool() ? "true" : "false");
319 				break;
320 			default:
321 				break;
322 			}
323 
324 			check_retransmit(line, (ulong_t)xid);
325 		} else {
326 			(void) sprintf(line, "NFS_ACL R %s ",
327 				procnames_short_v3[proc]);
328 			line += strlen(line);
329 			switch (proc) {
330 			case ACLPROC3_GETACL:
331 				(void) sum_nfsstat3(line);
332 				break;
333 			case ACLPROC3_SETACL:
334 				(void) sum_nfsstat3(line);
335 				break;
336 			case ACLPROC3_GETXATTRDIR:
337 				if (sum_nfsstat3(line) == 0) {
338 					line += strlen(line);
339 					(void) sprintf(line, sum_nfsfh3());
340 				}
341 				break;
342 			default:
343 				break;
344 			}
345 		}
346 	}
347 
348 	if (flags & F_DTAIL) {
349 		show_header("NFS_ACL:  ", "Sun NFS_ACL", len);
350 		show_space();
351 		(void) sprintf(get_line(0, 0), "Proc = %d (%s)",
352 			proc, procnames_long_v3[proc]);
353 		if (type == CALL)
354 			aclcall3(proc);
355 		else
356 			aclreply3(proc);
357 		show_trailer();
358 	}
359 }
360 
361 interpret_nfs_acl4(flags, type, xid, vers, proc, data, len)
362 	int flags, type, xid, vers, proc;
363 	char *data;
364 	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=%lu", 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