1/*
2 * Copyright (c) 2006 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29#include <sys/errno.h>
30#include <sys/types.h>
31#include <sys/malloc.h>
32#include <sys/buf.h>
33#include <sys/time.h>
34#include <sys/kauth.h>
35#include <sys/mount.h>
36#include <sys/vnode.h>
37#include <sys/syslog.h>
38#include <sys/vnode_internal.h>
39#include <sys/fslog.h>
40#include <sys/mount_internal.h>
41#include <sys/kasl.h>
42
43#include <dev/random/randomdev.h>
44
45#include <uuid/uuid.h>
46
47#include <stdarg.h>
48
49/* Log information about external modification of a process,
50 * using MessageTracer formatting. Assumes that both the caller
51 * and target are appropriately locked.
52 * Currently prints following information -
53 * 	1. Caller process name (truncated to 16 characters)
54 *	2. Caller process Mach-O UUID
55 *  3. Target process name (truncated to 16 characters)
56 *  4. Target process Mach-O UUID
57 */
58void
59fslog_extmod_msgtracer(proc_t caller, proc_t target)
60{
61	if ((caller != PROC_NULL) && (target != PROC_NULL)) {
62
63		/*
64		 * Print into buffer large enough for "ThisIsAnApplicat(BC223DD7-B314-42E0-B6B0-C5D2E6638337)",
65		 * including space for escaping, and NUL byte included in sizeof(uuid_string_t).
66		 */
67
68		uuid_string_t uuidstr;
69		char c_name[2*MAXCOMLEN + 2 /* () */ + sizeof(uuid_string_t)];
70		char t_name[2*MAXCOMLEN + 2 /* () */ + sizeof(uuid_string_t)];
71
72		strlcpy(c_name, caller->p_comm, sizeof(c_name));
73		uuid_unparse_upper(caller->p_uuid, uuidstr);
74		strlcat(c_name, "(", sizeof(c_name));
75		strlcat(c_name, uuidstr, sizeof(c_name));
76		strlcat(c_name, ")", sizeof(c_name));
77		if (0 != escape_str(c_name, strlen(c_name), sizeof(c_name))) {
78			return;
79		}
80
81		strlcpy(t_name, target->p_comm, sizeof(t_name));
82		uuid_unparse_upper(target->p_uuid, uuidstr);
83		strlcat(t_name, "(", sizeof(t_name));
84		strlcat(t_name, uuidstr, sizeof(t_name));
85		strlcat(t_name, ")", sizeof(t_name));
86		if (0 != escape_str(t_name, strlen(t_name), sizeof(t_name))) {
87			return;
88		}
89#if DEBUG
90		printf("EXTMOD: %s(%d) -> %s(%d)\n",
91			   c_name,
92			   proc_pid(caller),
93			   t_name,
94			   proc_pid(target));
95#endif
96
97		kern_asl_msg(LOG_DEBUG, "messagetracer",
98							5,
99							"com.apple.message.domain", "com.apple.kernel.external_modification", /* 0 */
100							"com.apple.message.signature", c_name, /* 1 */
101							"com.apple.message.signature2", t_name, /* 2 */
102							"com.apple.message.result", "noop", /* 3 */
103							"com.apple.message.summarize", "YES", /* 4 */
104							NULL);
105	}
106}
107
108/* Log file system related error in key-value format identified by Apple
109 * system log (ASL) facility.  The key-value pairs are string pointers
110 * (char *) and are provided as variable arguments list.  A NULL value
111 * indicates end of the list.
112 *
113 * Keys can not contain '[', ']', space, and newline.  Values can not
114 * contain '[', ']', and newline.  If any key-value contains any of the
115 * reserved characters, the behavior is undefined.  The caller of the
116 * function should escape any occurrences of '[' and ']' by prefixing
117 * it with '\'.
118 *
119 * The function takes a message ID which can be used to logically group
120 * different ASL messages.  Messages in same logical group have same message
121 * ID and have information to describe order of the message --- first,
122 * middle, or last.
123 *
124 * The following message IDs have special meaning -
125 * FSLOG_MSG_FIRST - This message is the first message in its logical
126 *	group.  This generates a unique message ID, creates two key-value
127 *	pairs with message ID and order of the message as "First".
128 * FSLOG_MSG_LAST - This is really a MASK which should be logically OR'ed
129 *	with message ID to indicate the last message for a logical group.
130 *	This also creates two key-value pairs with message ID and order of
131 *	message as "Last".
132 * FSLOG_MSG_SINGLE - This signifies that the message is the only message
133 * 	in its logical group.  Therefore no extra key-values are generated
134 *	for this option.
135 * For all other values of message IDs, it regards them as intermediate
136 * message and generates two key-value pairs with message ID and order of
137 * message as "Middle".
138 *
139 * Returns -
140 *	Message ID of the ASL message printed.  The caller should use
141 * 	this value to print intermediate messages or end the logical message
142 *	group.
143 *	For FSLOG_MSG_SINGLE option, it returns FSLOG_MSG_SINGLE.
144 */
145unsigned long fslog_err(unsigned long msg_id, ... )
146{
147	va_list ap;
148	int num_pairs;
149	char msg_id_str[21]; /* To convert 64-bit number to string with NULL char */
150	char *arg;
151	const char *msg_order_ptr;
152
153	/* Count number of arguments and key-value pairs provided by user */
154	num_pairs = 0;
155	va_start(ap, msg_id);
156	arg = va_arg(ap, char *);
157	while (arg) {
158		num_pairs++;
159		arg = va_arg(ap, char *);
160	}
161	num_pairs /= 2;
162	va_end(ap);
163
164	va_start(ap, msg_id);
165	if (msg_id == FSLOG_MSG_SINGLE) {
166		/* Single message, do not print message ID and message order */
167		(void) kern_asl_msg_va(FSLOG_VAL_LEVEL, FSLOG_VAL_FACILITY,
168		    num_pairs, ap,
169		    FSLOG_KEY_READ_UID, FSLOG_VAL_READ_UID,
170		    NULL);
171	} else {
172		if (msg_id == FSLOG_MSG_FIRST) {
173			/* First message, generate random message ID */
174			while ((msg_id == FSLOG_MSG_FIRST) ||
175			       (msg_id == FSLOG_MSG_LAST) ||
176			       (msg_id == FSLOG_MSG_SINGLE)) {
177				msg_id = RandomULong();
178				/* MSB is reserved for indicating last message
179				 * in sequence.  Clear the MSB while generating
180				 * new message ID.
181				 */
182				msg_id = msg_id >> 1;
183			}
184			msg_order_ptr = FSLOG_VAL_ORDER_FIRST;
185		} else if (msg_id & FSLOG_MSG_LAST) {
186			/* MSB set to indicate last message for this ID */
187			msg_order_ptr = FSLOG_VAL_ORDER_LAST;
188			/* MSB of message ID is set to indicate last message
189			 * in sequence.  Clear the bit to get real message ID.
190			 */
191			msg_id = msg_id & ~FSLOG_MSG_LAST;
192		} else {
193			/* Intermediate message for this ID */
194			msg_order_ptr = FSLOG_VAL_ORDER_MIDDLE;
195		}
196
197		snprintf(msg_id_str, sizeof(msg_id_str), "%lu", msg_id);
198		(void) kern_asl_msg_va(FSLOG_VAL_LEVEL, FSLOG_VAL_FACILITY,
199		    num_pairs, ap,
200		    FSLOG_KEY_READ_UID, FSLOG_VAL_READ_UID,
201		    FSLOG_KEY_MSG_ID, msg_id_str,
202		    FSLOG_KEY_MSG_ORDER, msg_order_ptr, NULL);
203	}
204	va_end(ap);
205	return msg_id;
206}
207
208/* Log information about runtime file system corruption detected by
209 * the file system.  It takes the VFS mount structure as
210 * parameter which is used to access the mount point of the
211 * corrupt volume.  If no mount structure or mount point string
212 * string exists, nothing is logged to ASL database.
213 *
214 * Currently prints following information -
215 * 	1. Mount Point
216 */
217void fslog_fs_corrupt(struct mount *mnt)
218{
219	if (mnt != NULL) {
220		fslog_err(FSLOG_MSG_SINGLE,
221			  FSLOG_KEY_ERR_TYPE, FSLOG_VAL_ERR_TYPE_FS,
222			  FSLOG_KEY_MNTPT, mnt->mnt_vfsstat.f_mntonname,
223			  NULL);
224	}
225
226	return;
227}
228
229/* Log information about IO error detected in buf_biodone()
230 * Currently prints following information -
231 * 	1. Physical block number
232 *	2. Logical block number
233 *	3. Device node
234 *	4. Mount point
235 *	5. Path for file, if any
236 *	6. Error number
237 *	7. Type of IO (read/write)
238 */
239void fslog_io_error(const buf_t bp)
240{
241	int err;
242	unsigned long msg_id;
243	char blknum_str[21];
244	char lblknum_str[21];
245	char errno_str[6];
246	const char *iotype;
247	unsigned char print_last = 0;
248	vnode_t	vp;
249
250	if (buf_error(bp) == 0) {
251		return;
252	}
253
254	/* Convert error number to string */
255	snprintf (errno_str, sizeof(errno_str), "%d", buf_error(bp));
256
257	/* Determine type of IO operation */
258	if (buf_flags(bp) & B_READ) {
259		iotype = FSLOG_VAL_IOTYPE_READ;
260	} else {
261		iotype = FSLOG_VAL_IOTYPE_WRITE;
262	}
263
264	/* Convert physical block number to string */
265	snprintf (blknum_str, sizeof(blknum_str), "%lld", buf_blkno(bp));
266
267	/* Convert logical block number to string */
268	snprintf (lblknum_str, sizeof(lblknum_str), "%lld", buf_lblkno(bp));
269
270	msg_id = fslog_err(FSLOG_MSG_FIRST,
271				FSLOG_KEY_ERR_TYPE, FSLOG_VAL_ERR_TYPE_IO,
272				FSLOG_KEY_ERRNO, errno_str,
273				FSLOG_KEY_IOTYPE, iotype,
274				FSLOG_KEY_PHYS_BLKNUM, blknum_str,
275				FSLOG_KEY_LOG_BLKNUM, lblknum_str,
276				NULL);
277
278	/* Access the vnode for this buffer */
279	vp = buf_vnode(bp);
280	if (vp) {
281		struct vfsstatfs *sp;
282		mount_t	mp;
283		char *path;
284		int len;
285		struct vfs_context context;
286
287		mp = vnode_mount(vp);
288		/* mp should be NULL only for bdevvp during boot */
289		if (mp == NULL) {
290			goto out;
291		}
292		sp = vfs_statfs(mp);
293
294		/* Access the file path */
295		MALLOC(path, char *, MAXPATHLEN, M_TEMP, M_WAITOK);
296		if (path) {
297			len = MAXPATHLEN;
298			context.vc_thread = current_thread();
299			context.vc_ucred = kauth_cred_get();
300			/* Find path without entering file system */
301			err = build_path(vp, path, len, &len, BUILDPATH_NO_FS_ENTER,
302					 &context);
303			if (!err) {
304				err = escape_str(path, len, MAXPATHLEN);
305				if (!err) {
306					/* Print device node, mount point, path */
307					msg_id = fslog_err(msg_id | FSLOG_MSG_LAST,
308						FSLOG_KEY_DEVNODE, sp->f_mntfromname,
309						FSLOG_KEY_MNTPT, sp->f_mntonname,
310						FSLOG_KEY_PATH, path,
311						NULL);
312					print_last = 1;
313				}
314			}
315			FREE(path, M_TEMP);
316		}
317
318		if (print_last == 0) {
319			/* Print device node and mount point */
320			msg_id = fslog_err(msg_id | FSLOG_MSG_LAST,
321					FSLOG_KEY_DEVNODE, sp->f_mntfromname,
322					FSLOG_KEY_MNTPT, sp->f_mntonname,
323					NULL);
324			print_last = 1;
325		}
326	}
327
328out:
329	if (print_last == 0) {
330		msg_id = fslog_err(msg_id | FSLOG_MSG_LAST, NULL);
331	}
332
333	return;
334}
335
336