xref: /illumos-gate/usr/src/lib/libbsm/common/adt_token.c (revision 4f364e7c95ee7fd9d5bbeddc1940e92405bb0e72)
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  * adt_token.c
23  *
24  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
25  * Use is subject to license terms.
26  *
27  * This file does not provide any user callable functions.  See adt.c
28  */
29 
30 #include <bsm/adt.h>
31 #include <bsm/adt_event.h>
32 #include <bsm/audit.h>
33 
34 #include <adt_xlate.h>
35 #include <alloca.h>
36 #include <assert.h>
37 #include <netdb.h>
38 #include <priv.h>
39 #include <string.h>
40 #include <strings.h>
41 #include <stdlib.h>
42 #include <time.h>
43 #include <unistd.h>
44 
45 #include <sys/priv_names.h>
46 #include <sys/socket.h>
47 #include <sys/types.h>
48 #include <sys/vnode.h>
49 
50 #include <tsol/label.h>
51 
52 #ifdef	C2_DEBUG
53 #define	DPRINTF(x) { (void) printf x; }
54 #define	DFLUSH (void) fflush(stdout);
55 
56 /* 0x + Classification + Compartments + end of string */
57 #define	HEX_SIZE 2 + 2*2 + 2*32 + 1
58 
59 static char *
60 dprt_label(m_label_t *label)
61 {
62 	static char	hex[HEX_SIZE];
63 	char		*direct = NULL;
64 
65 	if (label_to_str(label, &direct, M_INTERNAL, DEF_NAMES) != 0) {
66 		adt_write_syslog("label_to_str(M_INTERNAL)", errno);
67 		return ("hex label failed");
68 	}
69 	(void) strlcpy(hex, direct, sizeof (hex));
70 	free(direct);
71 	return (hex);
72 }
73 #else	/* !C2_DEBUG */
74 #define	DPRINTF(x)
75 #define	DFLUSH
76 #endif	/* C2_DEBUG */
77 
78 static adt_token_func_t adt_getTokenFunction(char);
79 
80 static char	*empty = "";
81 
82 /*
83  * call adt_token_open() first and adt_token_close() last.
84  *
85  * au_open is sort of broken; it returns a -1 when out of memory that
86  * you're supposed to ignore; au_write and au_close return without
87  * doing anything when a -1 is passed.  This code sort of follows the
88  * au_open model except that it calls syslog to indicate underlying
89  * brokenness.  Other than that, -1 is ignored.
90  */
91 
92 void
93 adt_token_open(struct adt_event_state *event)
94 {
95 	static int	have_syslogged = 0;
96 
97 	event->ae_event_handle = au_open();
98 	if (event->ae_event_handle < 0) {
99 		if (!have_syslogged) {
100 			adt_write_syslog("au_open failed", ENOMEM);
101 			have_syslogged = 1;
102 		}
103 	} else {
104 		have_syslogged = 0;
105 	}
106 }
107 
108 /*
109  * call generate_token for each token in the order you want the tokens
110  * generated.
111  */
112 
113 void
114 adt_generate_token(struct entry *p_entry, void *p_data,
115     struct adt_event_state *event)
116 {
117 	adt_token_func_t	p_func;
118 
119 	assert((p_entry != NULL) && (p_data != NULL) && (event != NULL));
120 
121 	p_func = adt_getTokenFunction(p_entry->en_token_id);
122 	assert(p_func != NULL);
123 
124 	DPRINTF(("p_entry=%p, p_data=%p, offset=%llu, msgFmt=%s\n",
125 	    (void *)p_entry, p_data, (long long)p_entry->en_offset,
126 	    p_entry->en_msg_format));
127 	DFLUSH
128 
129 	(*p_func)(p_entry->en_type_def,
130 	    (char *)p_data + p_entry->en_offset, p_entry->en_required, event,
131 	    p_entry->en_msg_format);
132 }
133 
134 /* call this last */
135 
136 int
137 adt_token_close(struct adt_event_state *event)
138 {
139 	int	rc;
140 
141 	rc = au_close(event->ae_event_handle, AU_TO_WRITE,
142 	    event->ae_internal_id);
143 	if (rc < 0)
144 		adt_write_syslog("au_close failed", errno);
145 	return (rc);
146 }
147 
148 /*
149  * one function per token -- see the jump table at the end of file
150  */
151 
152 /* ARGSUSED */
153 static void
154 adt_to_return(datadef *def, void *p_data, int required,
155     struct adt_event_state *event, char *notUsed)
156 {
157 
158 #ifdef _LP64
159 	(void) au_write(event->ae_event_handle,
160 	    au_to_return64((int64_t)event->ae_rc, event->ae_type));
161 #else
162 	(void) au_write(event->ae_event_handle,
163 	    au_to_return32((int32_t)event->ae_rc, event->ae_type));
164 #endif
165 }
166 
167 /*
168  * AUT_CMD
169  *
170  * the command line is described with argc and argv and the environment
171  * with envp.  The envp list is NULL terminated and has no separate
172  * counter; envp will be a NULL list unless the AUDIT_ARGE policy is
173  * set.
174  */
175 
176 /* ARGSUSED */
177 static void
178 adt_to_cmd(datadef *def, void *p_data, int required,
179     struct adt_event_state *event, char *notUsed)
180 {
181 	struct adt_internal_state	*sp = event->ae_session;
182 	int				argc;
183 	char				**argv;
184 	char				**envp = NULL;
185 
186 	argc = ((union convert *)p_data)->tint;
187 	p_data = adt_adjust_address(p_data, sizeof (int), sizeof (char **));
188 	argv = ((union convert *)p_data)->tchar2star;
189 	p_data = adt_adjust_address(p_data, sizeof (char **), sizeof (char **));
190 
191 	if (sp->as_kernel_audit_policy & AUDIT_ARGE)
192 		envp = ((union convert *)p_data)->tchar2star;
193 
194 	(void) au_write(event->ae_event_handle,
195 	    au_to_cmd(argc, argv, envp));
196 }
197 
198 /*
199  * special case of AUT_CMD with 1 argument that is
200  * a string showing the whole command and no envp
201  */
202 /* ARGSUSED */
203 static void
204 adt_to_cmd1(datadef *def, void *p_data, int required,
205     struct adt_event_state *event, char *notUsed)
206 {
207 	char	*string;
208 
209 	string = ((union convert *)p_data)->tcharstar;
210 
211 	if (string == NULL) {
212 		if (required) {
213 			string = empty;
214 		} else {
215 			return;
216 		}
217 	}
218 	/* argc is hardcoded as 1 */
219 	(void) au_write(event->ae_event_handle, au_to_cmd(1, &string,
220 	    NULL));
221 }
222 
223 /*
224  * adt_to_tid	-- generic address (ip is only one defined at present)
225  *	input:
226  *		terminal type:  ADT_IPv4, ADT_IPv6...
227  *		case: ADT_IPv4 or ADT_IPv6...
228  *			ip type
229  *			remote port
230  *			local port
231  *			address
232  *		case: not defined...
233  */
234 /* ARGSUSED */
235 static void
236 adt_to_tid(datadef *def, void *p_data, int required,
237     struct adt_event_state *event, char *notUsed)
238 {
239 	au_generic_tid_t	tid;
240 	uint32_t		type;
241 	au_ip_t			*ip;
242 
243 	type = ((union convert *)p_data)->tuint32;
244 
245 	switch (type) {
246 	case ADT_IPv4:
247 	case ADT_IPv6:
248 		p_data = adt_adjust_address(p_data, sizeof (uint32_t),
249 		    sizeof (uint32_t));
250 
251 		tid.gt_type = AU_IPADR;
252 		ip = &(tid.gt_adr.at_ip);
253 
254 		ip->at_type = (type == ADT_IPv4) ?
255 		    AU_IPv4 : AU_IPv6;
256 
257 		ip->at_r_port = ((union convert *)p_data)->tuint16;
258 		p_data = adt_adjust_address(p_data, sizeof (uint16_t),
259 		    sizeof (uint16_t));
260 
261 		ip->at_l_port = ((union convert *)p_data)->tuint16;
262 
263 		/* arg3 is for the array element, not the array size */
264 		p_data = adt_adjust_address(p_data, sizeof (uint16_t),
265 		    sizeof (uint32_t));
266 
267 		(void) memcpy(ip->at_addr, p_data, ip->at_type);
268 		break;
269 	default:
270 		adt_write_syslog("Invalid terminal id type", EINVAL);
271 		return;
272 	}
273 	(void) au_write(event->ae_event_handle, au_to_tid(&tid));
274 }
275 
276 /*
277  * au_to_frmi takes a char * that is the fmri.
278  */
279 /* ARGSUSED */
280 static void
281 adt_to_frmi(datadef *def, void *p_data, int required,
282     struct adt_event_state *event, char *notUsed)
283 {
284 	char		*fmri;
285 
286 	DPRINTF(("  adt_to_fmri dd_datatype=%d\n", def->dd_datatype));
287 
288 	fmri = ((union convert *)p_data)->tcharstar;
289 
290 	if (fmri == NULL) {
291 		if (required) {
292 			fmri = empty;
293 		} else {
294 			return;
295 		}
296 	}
297 	DPRINTF(("  fmri=%s\n", fmri));
298 	(void) au_write(event->ae_event_handle, au_to_fmri(fmri));
299 }
300 
301 /*
302  * au_to_label takes an m_label_t * that is the label.
303  */
304 /* ARGSUSED */
305 static void
306 adt_to_label(datadef *def, void *p_data, int required,
307     struct adt_event_state *event, char *notUsed)
308 {
309 	m_label_t	*label;
310 
311 	DPRINTF(("  adt_to_label dd_datatype=%d\n", def->dd_datatype));
312 
313 	label = ((union convert *)p_data)->tm_label;
314 
315 	if (label != NULL) {
316 		DPRINTF(("  label=%s\n", dprt_label(label)));
317 		DFLUSH
318 		(void) au_write(event->ae_event_handle, au_to_label(label));
319 	} else {
320 		DPRINTF(("  Null label\n"));
321 		if (required)
322 			adt_write_syslog("adt_to_label no required label", 0);
323 	}
324 }
325 
326 /*
327  * au_to_newgroups takes a length and an array of gids
328  * as input.  The input to adt_to_newgroups is a length
329  * and a pointer to an array of gids.
330  */
331 
332 /* ARGSUSED */
333 static void
334 adt_to_newgroups(datadef *def, void *p_data, int required,
335     struct adt_event_state *event, char *notUsed)
336 {
337 	int	n;
338 	gid_t	*groups;
339 
340 	n = ((union convert *)p_data)->tint;
341 	if (n < 1) {
342 		if (required) {
343 			n = 0;  /* in case negative n was passed */
344 		} else {
345 			return;
346 		}
347 	}
348 	p_data = adt_adjust_address(p_data, sizeof (int), sizeof (int32_t *));
349 
350 	groups = ((union convert *)p_data)->tgidstar;
351 
352 	(void) au_write(event->ae_event_handle, au_to_newgroups(n, groups));
353 }
354 
355 /* ARGSUSED */
356 static void
357 adt_to_path(datadef *def, void *p_data, int required,
358     struct adt_event_state *event, char *notUsed)
359 {
360 	char	*path;
361 
362 	path = ((union convert *)p_data)->tcharstar;
363 
364 	if (path != NULL) {
365 		DPRINTF(("  path=%s\n", path));
366 		(void) au_write(event->ae_event_handle, au_to_path(path));
367 	} else {
368 		DPRINTF(("  Null path\n"));
369 		if (required) {
370 			(void) au_write(event->ae_event_handle,
371 			    au_to_path(empty));
372 		}
373 	}
374 }
375 
376 /*
377  * dummy token id:  AUT_PATHLIST
378  */
379 
380 /* ARGSUSED */
381 static void
382 adt_to_pathlist(datadef *def, void *p_data, int required,
383     struct adt_event_state *event, char *notUsed)
384 {
385 	char	*path;
386 	char	*working_buf;
387 	char	*pathlist;
388 	char	*last_str;
389 
390 	pathlist = ((union convert *)p_data)->tcharstar;
391 
392 	if (pathlist != NULL) {
393 		working_buf = strdup(pathlist);
394 		if (working_buf == NULL) {
395 			adt_write_syslog("audit failure", errno);
396 			if (required) {
397 				(void) au_write(event->ae_event_handle,
398 				    au_to_path(empty));
399 			}
400 			return;
401 		}
402 		for (path = strtok_r(working_buf, " ", &last_str);
403 		    path; path = strtok_r(NULL, " ", &last_str)) {
404 			DPRINTF(("  path=%s\n", path));
405 			(void) au_write(event->ae_event_handle,
406 			    au_to_path(path));
407 		}
408 	} else {
409 		DPRINTF(("  Null path list\n"));
410 		if (required)
411 			(void) au_write(event->ae_event_handle,
412 			    au_to_path(empty));
413 	}
414 }
415 
416 /*
417  * AUT_PRIV
418  */
419 
420 /* ARGSUSED */
421 static void
422 adt_to_priv(datadef *def, void *p_data, int required,
423     struct adt_event_state *event, const char *priv_type)
424 {
425 	priv_set_t	*privilege;
426 
427 	privilege = ((union convert *)p_data)->tprivstar;
428 
429 	if (privilege != NULL) {
430 		(void) au_write(event->ae_event_handle,
431 		    au_to_privset(priv_type, privilege));
432 	} else {
433 		if (required) {
434 			DPRINTF(("  Null privilege\n"));
435 			(void) au_write(event->ae_event_handle,
436 			    au_to_privset(empty, NULL));
437 		}
438 	}
439 }
440 
441 /*
442  * -AUT_PRIV_L	AUT_PRIV for a limit set
443  */
444 
445 /* ARGSUSED */
446 static void
447 adt_to_priv_limit(datadef *def, void *p_data, int required,
448     struct adt_event_state *event, char *notUsed)
449 {
450 	adt_to_priv(def, p_data, required, event, PRIV_LIMIT);
451 }
452 
453 /*
454  * -AUT_PRIV_I	AUT_PRIV for an inherit set
455  */
456 
457 /* ARGSUSED */
458 static void
459 adt_to_priv_inherit(datadef *def, void *p_data, int required,
460     struct adt_event_state *event, char *notUsed)
461 {
462 	adt_to_priv(def, p_data, required, event, PRIV_INHERITABLE);
463 }
464 
465 /* ARGSUSED */
466 static void
467 adt_to_priv_effective(datadef *def, void *p_data, int required,
468     struct adt_event_state *event, char *notUsed)
469 {
470 	adt_to_priv(def, p_data, required, event, PRIV_EFFECTIVE);
471 }
472 
473 static void
474 getCharacteristics(struct auditpinfo_addr *info, pid_t *pid)
475 {
476 	int	rc;
477 
478 	if (*pid == 0) {		/* getpinfo for this pid */
479 		info->ap_pid = getpid();
480 	} else {
481 		info->ap_pid = *pid;
482 	}
483 
484 	rc = auditon(A_GETPINFO_ADDR, (caddr_t)info,
485 	    sizeof (struct auditpinfo_addr));
486 	if (rc == -1) {
487 		info->ap_auid = AU_NOAUDITID;
488 		info->ap_asid = 0;
489 		(void) memset((void *)&(info->ap_termid), 0,
490 		    sizeof (au_tid_addr_t));
491 		info->ap_termid.at_type = AU_IPv4;
492 	}
493 }
494 
495 /*
496  * AUT_PROCESS
497  *
498  */
499 
500 /* ARGSUSED */
501 static void
502 adt_to_process(datadef *def, void *p_data, int required,
503     struct adt_event_state *event, char *notUsed)
504 {
505 	au_id_t			auid;
506 	uid_t			euid;
507 	gid_t			egid;
508 	uid_t			ruid;
509 	gid_t			rgid;
510 	pid_t			pid;
511 	au_asid_t		sid;
512 	au_tid_addr_t		*tid;
513 	struct auditpinfo_addr	info;
514 
515 	auid = ((union convert *)p_data)->tuid;
516 	p_data = adt_adjust_address(p_data, sizeof (uid_t), sizeof (uid_t));
517 	euid = ((union convert *)p_data)->tuid;
518 	p_data = adt_adjust_address(p_data, sizeof (uid_t), sizeof (gid_t));
519 	egid = ((union convert *)p_data)->tgid;
520 	p_data = adt_adjust_address(p_data, sizeof (gid_t), sizeof (uid_t));
521 	ruid = ((union convert *)p_data)->tuid;
522 	p_data = adt_adjust_address(p_data, sizeof (uid_t), sizeof (gid_t));
523 	rgid = ((union convert *)p_data)->tgid;
524 	p_data = adt_adjust_address(p_data, sizeof (gid_t), sizeof (pid_t));
525 	pid  = ((union convert *)p_data)->tpid;
526 	p_data = adt_adjust_address(p_data, sizeof (pid_t), sizeof (uint32_t));
527 	sid  = ((union convert *)p_data)->tuint32;
528 	p_data = adt_adjust_address(p_data, sizeof (uint32_t),
529 	    sizeof (au_tid_addr_t *));
530 	tid  = ((union convert *)p_data)->ttermid;
531 
532 	getCharacteristics(&info, &pid);
533 
534 	if (auid == AU_NOAUDITID)
535 		auid = info.ap_auid;
536 
537 	if (euid == AU_NOAUDITID)
538 		euid = geteuid();
539 
540 	if (egid == AU_NOAUDITID)
541 		egid = getegid();
542 
543 	if (ruid == AU_NOAUDITID)
544 		ruid = getuid();
545 
546 	if (rgid == AU_NOAUDITID)
547 		rgid = getgid();
548 
549 	if (tid == NULL)
550 		tid = &(info.ap_termid);
551 
552 	if (sid == 0)
553 		sid = info.ap_asid;
554 
555 	if (pid == 0)
556 		pid = info.ap_pid;
557 
558 	(void) au_write(event->ae_event_handle,
559 	    au_to_process_ex(auid, euid, egid, ruid, rgid, pid, sid, tid));
560 }
561 
562 /*
563  * Generate subject information.
564  * If labels are present, generate the subject label token.
565  * If the group audit policy is set, generate the subject group token.
566  *
567  * The required flag does not apply here.
568  *
569  * Non-attributable records are indicated by an auid of AU_NOAUDITID;
570  * no subject token or group token is generated for a non-attributable
571  * record.
572  */
573 
574 /* ARGSUSED */
575 static void
576 adt_to_subject(datadef *def, void *p_data, int required,
577     struct adt_event_state *event, char *notUsed)
578 {
579 	struct adt_internal_state	*sp = event->ae_session;
580 
581 	if (sp->as_info.ai_auid == AU_NOAUDITID)
582 		return;
583 
584 	assert(sp->as_have_user_data == ADT_HAVE_ALL);
585 
586 	(void) au_write(event->ae_event_handle,
587 	    au_to_subject_ex(sp->as_info.ai_auid,
588 	    sp->as_euid, sp->as_egid, sp->as_ruid, sp->as_rgid,
589 	    sp->as_pid, sp->as_info.ai_asid,
590 	    &(sp->as_info.ai_termid)));
591 	if (is_system_labeled()) {
592 		(void) au_write(event->ae_event_handle,
593 		    au_to_label(sp->as_label));
594 	}
595 	/*
596 	 * Add optional tokens if in the process model.
597 	 * In a session model, the groups list is undefined and label
598 	 * is in the state.
599 	 */
600 	if (sp->as_session_model == ADT_PROCESS_MODEL) {
601 		if (sp->as_kernel_audit_policy & AUDIT_GROUP) {
602 			int group_count;
603 			int maxgrp = getgroups(0, NULL);
604 			gid_t *grouplist = alloca(maxgrp * sizeof (gid_t));
605 
606 			if ((group_count = getgroups(maxgrp, grouplist)) > 0) {
607 				(void) au_write(event->ae_event_handle,
608 				    au_to_newgroups(group_count, grouplist));
609 			}
610 		}
611 	}
612 }
613 
614 /*
615  * adt_to_text()
616  *
617  * The format string, normally null, is sort of a wrapper around
618  * the input.  adt_write_text() is a wrapper around au_write that
619  * handles the format string
620  *
621  */
622 #define	TEXT_LENGTH 49
623 
624 static void
625 adt_write_text(int handle, char *main_text, const char *format)
626 {
627 	char	buffer[TEXT_LENGTH * 2 + 1];
628 
629 	if (format == NULL) {
630 		(void) au_write(handle, au_to_text(main_text));
631 	} else {
632 		(void) snprintf(buffer, TEXT_LENGTH * 2, format, main_text);
633 		(void) au_write(handle, au_to_text(buffer));
634 	}
635 }
636 
637 static void
638 adt_to_text(datadef *def, void *p_data, int required,
639     struct adt_event_state *event, char *format)
640 {
641 	static int	have_syslogged = 0;
642 	char		*string;
643 	char		**string_list;
644 	char		buffer[TEXT_LENGTH + 1];
645 	time_t		date;
646 	struct tm	tm;
647 	uint32_t	*int_list;
648 	int		written, available;
649 	int		i, arrayCount;
650 	struct msg_text *list;
651 	int		list_index;
652 
653 	DPRINTF(("  adt_to_text dd_datatype=%d\n", def->dd_datatype));
654 	switch (def->dd_datatype) {
655 	case ADT_DATE:
656 		/*
657 		 * Consider creating a separate token type for dates
658 		 * -- store as longs and format them in praudit.
659 		 * For now, a date is input as a time_t and output as
660 		 * a text token.  If we do this, we need to consider
661 		 * carrying timezone info so that praudit can
662 		 * represent times in an unambiguous manner.
663 		 */
664 		date = ((union convert *)p_data)->tlong;
665 		if (strftime(buffer, sizeof (buffer), "%x",
666 		    localtime_r(&date, &tm)) > TEXT_LENGTH) {
667 			if (required) {
668 				(void) strncpy(buffer, "invalid date",
669 				    TEXT_LENGTH);
670 			} else {
671 				break;
672 			}
673 		}
674 		DPRINTF(("  text=%s\n", buffer));
675 		adt_write_text(event->ae_event_handle, buffer, format);
676 		break;
677 		/*
678 		 * The "input size" is overloaded to mean the list number
679 		 * and the msg_selector indexes the desired string in
680 		 * that list
681 		 */
682 	case ADT_MSG:
683 		list = &adt_msg_text[(enum adt_msg_list)def->dd_input_size];
684 		list_index = ((union convert *)p_data)->msg_selector;
685 
686 		if ((list_index + list->ml_offset < list->ml_min_index) ||
687 		    (list_index + list->ml_offset > list->ml_max_index)) {
688 			string = "Invalid message index";
689 		} else {
690 			string = list->ml_msg_list[list_index +
691 			    list->ml_offset];
692 		}
693 
694 		if (string == NULL) {	/* null is valid; means skip */
695 			if (required) {
696 				string = empty;
697 			} else {
698 				break;
699 			}
700 		}
701 		DPRINTF(("  text=%s\n", string));
702 		adt_write_text(event->ae_event_handle, string, format);
703 		break;
704 	case ADT_UID:
705 	case ADT_GID:
706 	case ADT_UINT:
707 	case ADT_UINT32:
708 		(void) snprintf(buffer, TEXT_LENGTH, "%u",
709 		    ((union convert *)p_data)->tuint);
710 
711 		DPRINTF(("  text=%s\n", buffer));
712 		adt_write_text(event->ae_event_handle, buffer, format);
713 		break;
714 	case ADT_INT:
715 	case ADT_INT32:
716 		(void) snprintf(buffer, TEXT_LENGTH, "%d",
717 		    ((union convert *)p_data)->tint);
718 
719 		DPRINTF(("  text=%s\n", buffer));
720 		adt_write_text(event->ae_event_handle, buffer, format);
721 		break;
722 	case ADT_LONG:
723 		(void) snprintf(buffer, TEXT_LENGTH, "%ld",
724 		    ((union convert *)p_data)->tlong);
725 
726 		DPRINTF(("  text=%s\n", buffer));
727 		adt_write_text(event->ae_event_handle, buffer, format);
728 		break;
729 	case ADT_UIDSTAR:
730 	case ADT_GIDSTAR:
731 	case ADT_UINT32STAR:
732 		int_list = ((union convert *)p_data)->tuint32star;
733 		p_data = adt_adjust_address(p_data, sizeof (int *),
734 		    sizeof (int));
735 		arrayCount = ((union convert *)p_data)->tint;
736 
737 		string = buffer;
738 		available = TEXT_LENGTH;	/* space available in buffer */
739 
740 		if (arrayCount < 0)
741 			arrayCount = 0;
742 
743 		if ((arrayCount > 0) && (int_list != NULL)) {
744 			for (; arrayCount > 0; arrayCount--) {
745 				written = snprintf(string, available,
746 				    "%d ", *int_list++);
747 				if (written < 1)
748 					break;
749 				string += written;
750 				available -= written;
751 			}
752 		} else if (required) {
753 			string = empty;
754 		} else {
755 			break;
756 		}
757 
758 		adt_write_text(event->ae_event_handle, buffer, format);
759 		break;
760 	case ADT_ULONG:
761 		(void) snprintf(buffer, TEXT_LENGTH, "%lu",
762 		    ((union convert *)p_data)->tulong);
763 
764 		DPRINTF(("  text=%s\n", buffer));
765 		adt_write_text(event->ae_event_handle, buffer, format);
766 		break;
767 	case ADT_UINT64:
768 		(void) snprintf(buffer, TEXT_LENGTH, "%llu",
769 		    ((union convert *)p_data)->tuint64);
770 
771 		DPRINTF(("  text=%s\n", buffer));
772 		adt_write_text(event->ae_event_handle, buffer, format);
773 		break;
774 	case ADT_CHARSTAR:
775 		string = ((union convert *)p_data)->tcharstar;
776 
777 		if (string == NULL) {
778 			if (required) {
779 				string = empty;
780 			} else {
781 				break;
782 			}
783 		}
784 		DPRINTF(("  text=%s\n", string));
785 		adt_write_text(event->ae_event_handle, string, format);
786 		break;
787 	case ADT_CHAR2STAR:
788 		string_list = ((union convert *)p_data)->tchar2star;
789 		p_data = adt_adjust_address(p_data, sizeof (char **),
790 		    sizeof (int));
791 		arrayCount = ((union convert *)p_data)->tint;
792 
793 		if (arrayCount < 0)
794 			arrayCount = 0;
795 
796 		if ((arrayCount > 0) && (string_list != NULL)) {
797 			for (i = 0; i < arrayCount; i++) {
798 				string = string_list[i];
799 				if (string != NULL)
800 					adt_write_text(event->ae_event_handle,
801 					    string, format);
802 			}
803 		} else if (required) {
804 			adt_write_text(event->ae_event_handle, empty, format);
805 		} else {
806 			break;
807 		}
808 		break;
809 	default:
810 		if (!have_syslogged) { /* don't flood the log */
811 			adt_write_syslog("unsupported data conversion",
812 			    ENOTSUP);
813 			have_syslogged = 1;
814 		}
815 		break;
816 	}
817 	DFLUSH
818 }
819 
820 /*
821  * AUT_UAUTH
822  */
823 
824 /* ARGSUSED */
825 static void
826 adt_to_uauth(datadef *def, void *p_data, int required,
827     struct adt_event_state *event, char *format)
828 {
829 	char		*string;
830 
831 	DPRINTF(("  adt_to_uauth dd_datatype=%d\n", def->dd_datatype));
832 
833 	string = ((union convert *)p_data)->tcharstar;
834 
835 	if (string == NULL) {
836 		if (required) {
837 			string = empty;
838 		} else {
839 			return;
840 		}
841 	}
842 	DPRINTF(("  text=%s\n", string));
843 	(void) au_write(event->ae_event_handle, au_to_uauth(string));
844 }
845 
846 /*
847  * AUT_USER
848  */
849 
850 /* ARGSUSED */
851 static void
852 adt_to_user(datadef *def, void *p_data, int required,
853     struct adt_event_state *event, char *format)
854 {
855 	uid_t	uid;
856 	char	*username;
857 
858 	DPRINTF(("  adt_to_user dd_datatype=%d\n", def->dd_datatype));
859 
860 	uid = ((union convert *)p_data)->tuid;
861 	p_data = adt_adjust_address(p_data, sizeof (uid_t), sizeof (uid_t));
862 
863 	username = ((union convert *)p_data)->tcharstar;
864 
865 	if (username == NULL) {
866 		if (required) {
867 			username = empty;
868 		} else {
869 			return;
870 		}
871 	}
872 	DPRINTF(("  username=%s\n", username));
873 	(void) au_write(event->ae_event_handle, au_to_user(uid, username));
874 }
875 
876 /*
877  * AUT_ZONENAME
878  */
879 
880 /* ARGSUSED */
881 static void
882 adt_to_zonename(datadef *def, void *p_data, int required,
883     struct adt_event_state *event, char *notUsed)
884 {
885 	char	*name;
886 
887 	name = ((union convert *)p_data)->tcharstar;
888 
889 	if (name != NULL) {
890 		DPRINTF(("  name=%s\n", name));
891 		(void) au_write(event->ae_event_handle, au_to_zonename(name));
892 	} else {
893 		DPRINTF(("  Null name\n"));
894 		if (required) {
895 			(void) au_write(event->ae_event_handle,
896 			    au_to_zonename(empty));
897 		}
898 	}
899 }
900 
901 /*
902  * ADT_IN_PEER dummy token
903  */
904 
905 /* ARGSUSED */
906 static void
907 adt_to_in_peer(datadef *def, void *p_data, int required,
908     struct adt_event_state *event, char *notUsed)
909 {
910 	int	sock;
911 	struct sockaddr_in6 peer;
912 	int	peerlen = sizeof (peer);
913 
914 	DPRINTF(("    adt_to_in_peer dd_datatype=%d\n", def->dd_datatype));
915 
916 	sock = ((union convert *)p_data)->tfd;
917 
918 	if (sock < 0) {
919 		DPRINTF(("  Socket fd %d\n", sock));
920 		if (required) {
921 			adt_write_syslog("adt_to_in_peer no required socket",
922 			    0);
923 		}
924 		return;
925 	}
926 	if (getpeername(sock, (struct sockaddr *)&peer, (socklen_t *)&peerlen)
927 	    < 0) {
928 
929 		adt_write_syslog("adt_to_in_addr getpeername", errno);
930 		return;
931 	}
932 	if (peer.sin6_family == AF_INET6) {
933 		(void) au_write(event->ae_event_handle,
934 		    au_to_in_addr_ex(&(peer.sin6_addr)));
935 		(void) au_write(event->ae_event_handle,
936 		    au_to_iport((ushort_t)peer.sin6_port));
937 	} else {
938 		(void) au_write(event->ae_event_handle,
939 		    au_to_in_addr(&(((struct sockaddr_in *)&peer)->sin_addr)));
940 		(void) au_write(event->ae_event_handle,
941 		    au_to_iport(
942 		    (ushort_t)(((struct sockaddr_in *)&peer)->sin_port)));
943 	}
944 }
945 
946 /*
947  * ADT_IN_REMOTE dummy token
948  *
949  * Similar to ADT_IN_PEER except the input is
950  * an IP address type (ADT_IPv4 | ADT_IPv6) and an address V4/V6
951  */
952 
953 /* ARGSUSED */
954 static void
955 adt_to_in_remote(datadef *def, void *p_data, int required,
956     struct adt_event_state *event, char *notUsed)
957 {
958 	int32_t	type;
959 
960 	DPRINTF(("    adt_to_in_remote dd_datatype=%d\n", def->dd_datatype));
961 
962 	type = ((union convert *)p_data)->tuint32;
963 
964 	if (type ==  0) {
965 		if (required == 0) {
966 			return;
967 		}
968 		/* required and not specified */
969 		adt_write_syslog("adt_to_in_remote required address not "
970 		    "specified", 0);
971 		type = ADT_IPv4;
972 	}
973 	p_data = adt_adjust_address(p_data, sizeof (int32_t),
974 	    sizeof (uint32_t));
975 
976 	switch (type) {
977 	case ADT_IPv4:
978 		(void) au_write(event->ae_event_handle, au_to_in_addr(
979 		    (struct in_addr *)&(((union convert *)p_data)->tuint32)));
980 		break;
981 	case ADT_IPv6:
982 		(void) au_write(event->ae_event_handle, au_to_in_addr_ex(
983 		    (struct in6_addr *)&(((union convert *)p_data)->tuint32)));
984 		break;
985 	default:
986 		adt_write_syslog("adt_to_in_remote invalid type", EINVAL);
987 		return;
988 	}
989 }
990 
991 /*
992  * adt_to_iport takes a uint16_t IP port.
993  */
994 
995 /* ARGSUSED */
996 static void
997 adt_to_iport(datadef *def, void *p_data, int required,
998     struct adt_event_state *event, char *notUsed)
999 {
1000 	ushort_t port;
1001 
1002 	DPRINTF(("  adt_to_iport dd_datatype=%d\n", def->dd_datatype));
1003 
1004 	port = ((union convert *)p_data)->tuint16;
1005 
1006 	if (port == 0) {
1007 		if (required == 0) {
1008 			return;
1009 		}
1010 		/* required and not specified */
1011 		adt_write_syslog("adt_to_iport no required port", 0);
1012 	}
1013 	(void) au_write(event->ae_event_handle, au_to_iport(port));
1014 
1015 }
1016 
1017 
1018 /*
1019  *	This is a compact table that defines only the tokens that are
1020  * actually generated in the adt.xml file.  It can't be a  pure
1021  * indexed table because the adt.xml language defines internal extension
1022  * tokens for some processing.  VIZ. ADT_CMD_ALT, ADT_AUT_PRIV_* (see
1023  * adt_xlate.h), and the -AUT_PATH value.
1024  */
1025 
1026 #define	MAX_TOKEN_JMP 21
1027 
1028 static struct token_jmp token_table[MAX_TOKEN_JMP] =
1029 {
1030 	{AUT_CMD, adt_to_cmd},
1031 	{ADT_CMD_ALT, adt_to_cmd1},
1032 	{AUT_FMRI, adt_to_frmi},
1033 	{ADT_IN_PEER, adt_to_in_peer},
1034 	{ADT_IN_REMOTE, adt_to_in_remote},
1035 	{AUT_IPORT, adt_to_iport},
1036 	{AUT_LABEL, adt_to_label},
1037 	{AUT_NEWGROUPS, adt_to_newgroups},
1038 	{AUT_PATH, adt_to_path},
1039 	{-AUT_PATH, adt_to_pathlist},	/* private extension of token values */
1040 	{ADT_AUT_PRIV_L, adt_to_priv_limit},
1041 	{ADT_AUT_PRIV_I, adt_to_priv_inherit},
1042 	{ADT_AUT_PRIV_E, adt_to_priv_effective},
1043 	{AUT_PROCESS, adt_to_process},
1044 	{AUT_RETURN, adt_to_return},
1045 	{AUT_SUBJECT, adt_to_subject},
1046 	{AUT_TEXT, adt_to_text},
1047 	{AUT_TID, adt_to_tid},
1048 	{AUT_UAUTH, adt_to_uauth},
1049 	{AUT_USER, adt_to_user},
1050 	{AUT_ZONENAME, adt_to_zonename}
1051 };
1052 
1053 /*
1054  *	{AUT_ACL, adt_to_acl},			not used
1055  *	{AUT_ARBITRARY, adt_to_arbitrary},	AUT_ARBITRARY is undefined
1056  *	{AUT_ARG, adt_to_arg},			not used
1057  *	{AUT_ATTR, adt_to_attr},		not used in mountd
1058  *	{AUT_XATOM, adt_to_atom},		not used
1059  *	{AUT_EXEC_ARGS, adt_to_exec_args},	not used
1060  *	{AUT_EXEC_ENV, adt_to_exec_env},	not used
1061  *	{AUT_EXIT, adt_to_exit},		obsolete
1062  *	{AUT_FILE, adt_to_file},		AUT_FILE is undefined
1063  *	{AUT_XCOLORMAP, adt_to_colormap},	not used
1064  *	{AUT_XCURSOR, adt_to_cursor},		not used
1065  *	{AUT_XFONT, adt_to_font},		not used
1066  *	{AUT_XGC, adt_to_gc},			not used
1067  *	{AUT_GROUPS, adt_to_groups},		obsolete
1068  *	{AUT_HEADER, adt_to_header},		generated by au_close
1069  *	{AUT_IP, adt_to_ip},			not used
1070  *	{AUT_IPC, adt_to_ipc},			not used
1071  *	{AUT_IPC_PERM, adt_to_ipc_perm},	not used
1072  *	{AUT_OPAQUE, adt_to_opaque},		not used
1073  *	{AUT_XPIXMAP, adt_to_pixmap},		not used
1074  *	{AUT_XPROPERTY, adt_to_property},	not used
1075  *	{AUT_SEQ, adt_to_seq},			not used
1076  *	{AUT_SOCKET, adt_to_socket},		not used
1077  *	{AUT_SOCKET_INET, adt_to_socket_inet},  AUT_SOCKET_INET is undefined
1078  *	{AUT_TRAILER, adt_to_trailer},		generated by au_close
1079  *	{AUT_XCLIENT, adt_to_xclient}		not used
1080  */
1081 
1082 /* find function to generate token */
1083 
1084 static adt_token_func_t
1085 adt_getTokenFunction(char token_id)
1086 {
1087 	int	i;
1088 	struct token_jmp	*p_jmp = token_table;
1089 
1090 	for (i = 0; i < MAX_TOKEN_JMP; i++) {
1091 		if (token_id == p_jmp->jmp_id) {
1092 			return (p_jmp->jmp_to);
1093 		}
1094 		p_jmp++;
1095 	}
1096 	errno = EINVAL;
1097 	return (NULL);
1098 }
1099 
1100 /*
1101  * adjustAddress -- given the address of data, its size, and the type of
1102  * the next data field, calculate the offset to the next piece of data.
1103  * Depending on the caller, "current" and "next" mean the current pointer
1104  * and the next pointer or the last pointer and the current pointer.
1105  */
1106 void *
1107 adt_adjust_address(void *current_address, size_t current_size,
1108     size_t next_size)
1109 {
1110 	ptrdiff_t adjustment;
1111 	ptrdiff_t remainder;
1112 
1113 	adjustment = (size_t)current_address + current_size;
1114 
1115 	if (next_size) {
1116 		remainder = adjustment % next_size;
1117 		if (remainder != 0)
1118 			adjustment += next_size - remainder;
1119 	}
1120 	return ((char *)adjustment);
1121 }
1122