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