audit_bsm_klib.c revision 173142
1155192Srwatson/*
2155192Srwatson * Copyright (c) 1999-2005 Apple Computer, Inc.
3155192Srwatson * Copyright (c) 2005 Robert N. M. Watson
4155192Srwatson * All rights reserved.
5155192Srwatson *
6155192Srwatson * Redistribution and use in source and binary forms, with or without
7155192Srwatson * modification, are permitted provided that the following conditions
8155192Srwatson * are met:
9155192Srwatson * 1.  Redistributions of source code must retain the above copyright
10155192Srwatson *     notice, this list of conditions and the following disclaimer.
11155192Srwatson * 2.  Redistributions in binary form must reproduce the above copyright
12155192Srwatson *     notice, this list of conditions and the following disclaimer in the
13155192Srwatson *     documentation and/or other materials provided with the distribution.
14155192Srwatson * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
15155192Srwatson *     its contributors may be used to endorse or promote products derived
16155192Srwatson *     from this software without specific prior written permission.
17155192Srwatson *
18155192Srwatson * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND
19155192Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20155192Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21155192Srwatson * ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR
22155192Srwatson * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23155192Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24155192Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25155192Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26155192Srwatson * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
27155192Srwatson * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28155192Srwatson * POSSIBILITY OF SUCH DAMAGE.
29155192Srwatson *
30155192Srwatson * $FreeBSD: head/sys/security/audit/audit_bsm_klib.c 173142 2007-10-29 18:07:48Z rwatson $
31155192Srwatson */
32155192Srwatson
33155192Srwatson#include <sys/param.h>
34155192Srwatson#include <sys/fcntl.h>
35155192Srwatson#include <sys/filedesc.h>
36155192Srwatson#include <sys/libkern.h>
37155192Srwatson#include <sys/malloc.h>
38155192Srwatson#include <sys/mount.h>
39155192Srwatson#include <sys/proc.h>
40155192Srwatson#include <sys/sem.h>
41155192Srwatson#include <sys/syscall.h>
42155192Srwatson#include <sys/sysctl.h>
43155192Srwatson#include <sys/sysent.h>
44155192Srwatson#include <sys/vnode.h>
45155192Srwatson
46155192Srwatson#include <bsm/audit.h>
47155192Srwatson#include <bsm/audit_kevents.h>
48155192Srwatson#include <security/audit/audit.h>
49155192Srwatson#include <security/audit/audit_private.h>
50155192Srwatson
51155192Srwatson/*
52155192Srwatson * Hash table functions for the audit event number to event class mask
53155192Srwatson * mapping.
54155192Srwatson */
55155192Srwatson#define EVCLASSMAP_HASH_TABLE_SIZE 251
56155192Srwatsonstruct evclass_elem {
57155192Srwatson	au_event_t event;
58155192Srwatson	au_class_t class;
59155192Srwatson	LIST_ENTRY(evclass_elem) entry;
60155192Srwatson};
61155192Srwatsonstruct evclass_list {
62155192Srwatson	LIST_HEAD(, evclass_elem) head;
63155192Srwatson};
64155192Srwatson
65155192Srwatsonstatic MALLOC_DEFINE(M_AUDITEVCLASS, "audit_evclass", "Audit event class");
66156889Srwatsonstatic struct mtx		evclass_mtx;
67156889Srwatsonstatic struct evclass_list	evclass_hash[EVCLASSMAP_HASH_TABLE_SIZE];
68155192Srwatson
69155192Srwatson/*
70155192Srwatson * Look up the class for an audit event in the class mapping table.
71155192Srwatson */
72155192Srwatsonau_class_t
73155192Srwatsonau_event_class(au_event_t event)
74155192Srwatson{
75155192Srwatson	struct evclass_list *evcl;
76155192Srwatson	struct evclass_elem *evc;
77155192Srwatson	au_class_t class;
78155192Srwatson
79155192Srwatson	mtx_lock(&evclass_mtx);
80155192Srwatson	evcl = &evclass_hash[event % EVCLASSMAP_HASH_TABLE_SIZE];
81173142Srwatson	class = 0;
82155192Srwatson	LIST_FOREACH(evc, &evcl->head, entry) {
83155192Srwatson		if (evc->event == event) {
84155192Srwatson			class = evc->class;
85155192Srwatson			goto out;
86155192Srwatson		}
87155192Srwatson	}
88155192Srwatsonout:
89155192Srwatson	mtx_unlock(&evclass_mtx);
90155192Srwatson	return (class);
91155192Srwatson}
92155192Srwatson
93156889Srwatson/*
94155192Srwatson * Insert a event to class mapping. If the event already exists in the
95155192Srwatson * mapping, then replace the mapping with the new one.
96156889Srwatson *
97155192Srwatson * XXX There is currently no constraints placed on the number of mappings.
98156889Srwatson * May want to either limit to a number, or in terms of memory usage.
99155192Srwatson */
100155192Srwatsonvoid
101156889Srwatsonau_evclassmap_insert(au_event_t event, au_class_t class)
102155192Srwatson{
103155192Srwatson	struct evclass_list *evcl;
104155192Srwatson	struct evclass_elem *evc, *evc_new;
105155192Srwatson
106155192Srwatson	/*
107155192Srwatson	 * Pessimistically, always allocate storage before acquiring mutex.
108155192Srwatson	 * Free if there is already a mapping for this event.
109155192Srwatson	 */
110155192Srwatson	evc_new = malloc(sizeof(*evc), M_AUDITEVCLASS, M_WAITOK);
111155192Srwatson
112155192Srwatson	mtx_lock(&evclass_mtx);
113155192Srwatson	evcl = &evclass_hash[event % EVCLASSMAP_HASH_TABLE_SIZE];
114155192Srwatson	LIST_FOREACH(evc, &evcl->head, entry) {
115155192Srwatson		if (evc->event == event) {
116155192Srwatson			evc->class = class;
117155192Srwatson			mtx_unlock(&evclass_mtx);
118155192Srwatson			free(evc_new, M_AUDITEVCLASS);
119155192Srwatson			return;
120155192Srwatson		}
121155192Srwatson	}
122155192Srwatson	evc = evc_new;
123155192Srwatson	evc->event = event;
124155192Srwatson	evc->class = class;
125155192Srwatson	LIST_INSERT_HEAD(&evcl->head, evc, entry);
126155192Srwatson	mtx_unlock(&evclass_mtx);
127155192Srwatson}
128155192Srwatson
129155192Srwatsonvoid
130156889Srwatsonau_evclassmap_init(void)
131155192Srwatson{
132155192Srwatson	int i;
133155192Srwatson
134155192Srwatson	mtx_init(&evclass_mtx, "evclass_mtx", NULL, MTX_DEF);
135155192Srwatson	for (i = 0; i < EVCLASSMAP_HASH_TABLE_SIZE; i++)
136155192Srwatson		LIST_INIT(&evclass_hash[i].head);
137155192Srwatson
138155192Srwatson	/*
139155192Srwatson	 * Set up the initial event to class mapping for system calls.
140155192Srwatson	 *
141155192Srwatson	 * XXXRW: Really, this should walk all possible audit events, not all
142155192Srwatson	 * native ABI system calls, as there may be audit events reachable
143155192Srwatson	 * only through non-native system calls.  It also seems a shame to
144155192Srwatson	 * frob the mutex this early.
145156889Srwatson	 */
146155192Srwatson	for (i = 0; i < SYS_MAXSYSCALL; i++) {
147155192Srwatson		if (sysent[i].sy_auevent != AUE_NULL)
148173142Srwatson			au_evclassmap_insert(sysent[i].sy_auevent, 0);
149155192Srwatson	}
150155192Srwatson}
151155192Srwatson
152155192Srwatson/*
153155192Srwatson * Check whether an event is aditable by comparing the mask of classes this
154155192Srwatson * event is part of against the given mask.
155155192Srwatson */
156155192Srwatsonint
157159269Srwatsonau_preselect(au_event_t event, au_class_t class, au_mask_t *mask_p, int sorf)
158155192Srwatson{
159155192Srwatson	au_class_t effmask = 0;
160155192Srwatson
161155192Srwatson	if (mask_p == NULL)
162155192Srwatson		return (-1);
163155192Srwatson
164156889Srwatson	/*
165155192Srwatson	 * Perform the actual check of the masks against the event.
166155192Srwatson	 */
167155192Srwatson	if (sorf & AU_PRS_SUCCESS)
168159269Srwatson		effmask |= (mask_p->am_success & class);
169156889Srwatson
170155192Srwatson	if (sorf & AU_PRS_FAILURE)
171159269Srwatson		effmask |= (mask_p->am_failure & class);
172156889Srwatson
173155192Srwatson	if (effmask)
174155192Srwatson		return (1);
175156889Srwatson	else
176155192Srwatson		return (0);
177155192Srwatson}
178155192Srwatson
179155192Srwatson/*
180156889Srwatson * Convert sysctl names and present arguments to events.
181155192Srwatson */
182155192Srwatsonau_event_t
183155192Srwatsonctlname_to_sysctlevent(int name[], uint64_t valid_arg)
184155192Srwatson{
185155192Srwatson
186155192Srwatson	/* can't parse it - so return the worst case */
187156889Srwatson	if ((valid_arg & (ARG_CTLNAME | ARG_LEN)) != (ARG_CTLNAME | ARG_LEN))
188155192Srwatson		return (AUE_SYSCTL);
189155192Srwatson
190155192Srwatson	switch (name[0]) {
191155192Srwatson	/* non-admin "lookups" treat them special */
192155192Srwatson	case KERN_OSTYPE:
193155192Srwatson	case KERN_OSRELEASE:
194155192Srwatson	case KERN_OSREV:
195155192Srwatson	case KERN_VERSION:
196155192Srwatson	case KERN_ARGMAX:
197155192Srwatson	case KERN_CLOCKRATE:
198155192Srwatson	case KERN_BOOTTIME:
199155192Srwatson	case KERN_POSIX1:
200155192Srwatson	case KERN_NGROUPS:
201155192Srwatson	case KERN_JOB_CONTROL:
202155192Srwatson	case KERN_SAVED_IDS:
203155192Srwatson	case KERN_OSRELDATE:
204155192Srwatson	case KERN_DUMMY:
205155192Srwatson		return (AUE_SYSCTL_NONADMIN);
206155192Srwatson
207155192Srwatson	/* only treat the changeable controls as admin */
208155192Srwatson	case KERN_MAXVNODES:
209155192Srwatson	case KERN_MAXPROC:
210155192Srwatson	case KERN_MAXFILES:
211155192Srwatson	case KERN_MAXPROCPERUID:
212155192Srwatson	case KERN_MAXFILESPERPROC:
213155192Srwatson	case KERN_HOSTID:
214155192Srwatson	case KERN_SECURELVL:
215155192Srwatson	case KERN_HOSTNAME:
216155192Srwatson	case KERN_VNODE:
217155192Srwatson	case KERN_PROC:
218155192Srwatson	case KERN_FILE:
219155192Srwatson	case KERN_PROF:
220155192Srwatson	case KERN_NISDOMAINNAME:
221155192Srwatson	case KERN_UPDATEINTERVAL:
222155192Srwatson	case KERN_NTP_PLL:
223155192Srwatson	case KERN_BOOTFILE:
224155192Srwatson	case KERN_DUMPDEV:
225155192Srwatson	case KERN_IPC:
226155192Srwatson	case KERN_PS_STRINGS:
227155192Srwatson	case KERN_USRSTACK:
228155192Srwatson	case KERN_LOGSIGEXIT:
229155192Srwatson	case KERN_IOV_MAX:
230155192Srwatson	case KERN_MAXID:
231155192Srwatson		return ((valid_arg & ARG_VALUE) ?
232155192Srwatson			AUE_SYSCTL : AUE_SYSCTL_NONADMIN);
233155192Srwatson
234155192Srwatson	default:
235155192Srwatson		return (AUE_SYSCTL);
236155192Srwatson	}
237155192Srwatson	/* NOTREACHED */
238155192Srwatson}
239155192Srwatson
240155192Srwatson/*
241156889Srwatson * Convert an open flags specifier into a specific type of open event for
242155192Srwatson * auditing purposes.
243155192Srwatson */
244155192Srwatsonau_event_t
245156889Srwatsonflags_and_error_to_openevent(int oflags, int error)
246156889Srwatson{
247155192Srwatson	au_event_t aevent;
248155192Srwatson
249156889Srwatson	/*
250156889Srwatson	 * Need to check only those flags we care about.
251156889Srwatson	 */
252155192Srwatson	oflags = oflags & (O_RDONLY | O_CREAT | O_TRUNC | O_RDWR | O_WRONLY);
253155192Srwatson
254155192Srwatson	/*
255155192Srwatson	 * These checks determine what flags are on with the condition that
256155192Srwatson	 * ONLY that combination is on, and no other flags are on.
257155192Srwatson	 */
258155192Srwatson	switch (oflags) {
259155192Srwatson	case O_RDONLY:
260155192Srwatson		aevent = AUE_OPEN_R;
261155192Srwatson		break;
262155192Srwatson
263155192Srwatson	case (O_RDONLY | O_CREAT):
264155192Srwatson		aevent = AUE_OPEN_RC;
265155192Srwatson		break;
266155192Srwatson
267155192Srwatson	case (O_RDONLY | O_CREAT | O_TRUNC):
268155192Srwatson		aevent = AUE_OPEN_RTC;
269155192Srwatson		break;
270155192Srwatson
271155192Srwatson	case (O_RDONLY | O_TRUNC):
272155192Srwatson		aevent = AUE_OPEN_RT;
273155192Srwatson		break;
274155192Srwatson
275155192Srwatson	case O_RDWR:
276155192Srwatson		aevent = AUE_OPEN_RW;
277155192Srwatson		break;
278155192Srwatson
279155192Srwatson	case (O_RDWR | O_CREAT):
280155192Srwatson		aevent = AUE_OPEN_RWC;
281155192Srwatson		break;
282155192Srwatson
283155192Srwatson	case (O_RDWR | O_CREAT | O_TRUNC):
284155192Srwatson		aevent = AUE_OPEN_RWTC;
285155192Srwatson		break;
286155192Srwatson
287155192Srwatson	case (O_RDWR | O_TRUNC):
288155192Srwatson		aevent = AUE_OPEN_RWT;
289155192Srwatson		break;
290155192Srwatson
291155192Srwatson	case O_WRONLY:
292155192Srwatson		aevent = AUE_OPEN_W;
293155192Srwatson		break;
294155192Srwatson
295155192Srwatson	case (O_WRONLY | O_CREAT):
296155192Srwatson		aevent = AUE_OPEN_WC;
297155192Srwatson		break;
298155192Srwatson
299155192Srwatson	case (O_WRONLY | O_CREAT | O_TRUNC):
300155192Srwatson		aevent = AUE_OPEN_WTC;
301155192Srwatson		break;
302155192Srwatson
303155192Srwatson	case (O_WRONLY | O_TRUNC):
304155192Srwatson		aevent = AUE_OPEN_WT;
305155192Srwatson		break;
306155192Srwatson
307155192Srwatson	default:
308155192Srwatson		aevent = AUE_OPEN;
309155192Srwatson		break;
310155192Srwatson	}
311155192Srwatson
312155192Srwatson#if 0
313156889Srwatson	/*
314170196Srwatson	 * Convert chatty errors to better matching events.  Failures to
315170196Srwatson	 * find a file are really just attribute events -- so recast them as
316170196Srwatson	 * such.
317155192Srwatson	 *
318155192Srwatson	 * XXXAUDIT: Solaris defines that AUE_OPEN will never be returned, it
319155192Srwatson	 * is just a placeholder.  However, in Darwin we return that in
320155192Srwatson	 * preference to other events.  For now, comment this out as we don't
321155192Srwatson	 * have a BSM conversion routine for AUE_OPEN.
322155192Srwatson	 */
323155192Srwatson	switch (aevent) {
324155192Srwatson	case AUE_OPEN_R:
325155192Srwatson	case AUE_OPEN_RT:
326155192Srwatson	case AUE_OPEN_RW:
327155192Srwatson	case AUE_OPEN_RWT:
328155192Srwatson	case AUE_OPEN_W:
329155192Srwatson	case AUE_OPEN_WT:
330155192Srwatson		if (error == ENOENT)
331155192Srwatson			aevent = AUE_OPEN;
332155192Srwatson	}
333155192Srwatson#endif
334155192Srwatson	return (aevent);
335155192Srwatson}
336155192Srwatson
337155192Srwatson/*
338155192Srwatson * Convert a MSGCTL command to a specific event.
339155192Srwatson */
340155192Srwatsonint
341155192Srwatsonmsgctl_to_event(int cmd)
342155192Srwatson{
343155192Srwatson
344155192Srwatson	switch (cmd) {
345155192Srwatson	case IPC_RMID:
346155192Srwatson		return (AUE_MSGCTL_RMID);
347155192Srwatson
348155192Srwatson	case IPC_SET:
349155192Srwatson		return (AUE_MSGCTL_SET);
350155192Srwatson
351155192Srwatson	case IPC_STAT:
352155192Srwatson		return (AUE_MSGCTL_STAT);
353155192Srwatson
354155192Srwatson	default:
355170196Srwatson		/* We will audit a bad command. */
356155192Srwatson		return (AUE_MSGCTL);
357155192Srwatson	}
358155192Srwatson}
359155192Srwatson
360155192Srwatson/*
361155192Srwatson * Convert a SEMCTL command to a specific event.
362155192Srwatson */
363155192Srwatsonint
364155192Srwatsonsemctl_to_event(int cmd)
365155192Srwatson{
366155192Srwatson
367155192Srwatson	switch (cmd) {
368155192Srwatson	case GETALL:
369155192Srwatson		return (AUE_SEMCTL_GETALL);
370155192Srwatson
371155192Srwatson	case GETNCNT:
372155192Srwatson		return (AUE_SEMCTL_GETNCNT);
373155192Srwatson
374155192Srwatson	case GETPID:
375155192Srwatson		return (AUE_SEMCTL_GETPID);
376155192Srwatson
377155192Srwatson	case GETVAL:
378155192Srwatson		return (AUE_SEMCTL_GETVAL);
379155192Srwatson
380155192Srwatson	case GETZCNT:
381155192Srwatson		return (AUE_SEMCTL_GETZCNT);
382155192Srwatson
383155192Srwatson	case IPC_RMID:
384155192Srwatson		return (AUE_SEMCTL_RMID);
385155192Srwatson
386155192Srwatson	case IPC_SET:
387155192Srwatson		return (AUE_SEMCTL_SET);
388155192Srwatson
389155192Srwatson	case SETALL:
390155192Srwatson		return (AUE_SEMCTL_SETALL);
391155192Srwatson
392155192Srwatson	case SETVAL:
393155192Srwatson		return (AUE_SEMCTL_SETVAL);
394155192Srwatson
395155192Srwatson	case IPC_STAT:
396155192Srwatson		return (AUE_SEMCTL_STAT);
397155192Srwatson
398155192Srwatson	default:
399155192Srwatson		/* We will audit a bad command */
400155192Srwatson		return (AUE_SEMCTL);
401155192Srwatson	}
402155192Srwatson}
403155192Srwatson
404155192Srwatson/*
405155192Srwatson * Convert a command for the auditon() system call to a audit event.
406155192Srwatson */
407155192Srwatsonint
408155192Srwatsonauditon_command_event(int cmd)
409155192Srwatson{
410155192Srwatson
411155192Srwatson	switch(cmd) {
412155192Srwatson	case A_GETPOLICY:
413155192Srwatson		return (AUE_AUDITON_GPOLICY);
414155192Srwatson
415155192Srwatson	case A_SETPOLICY:
416155192Srwatson		return (AUE_AUDITON_SPOLICY);
417155192Srwatson
418155192Srwatson	case A_GETKMASK:
419155192Srwatson		return (AUE_AUDITON_GETKMASK);
420155192Srwatson
421155192Srwatson	case A_SETKMASK:
422155192Srwatson		return (AUE_AUDITON_SETKMASK);
423155192Srwatson
424155192Srwatson	case A_GETQCTRL:
425155192Srwatson		return (AUE_AUDITON_GQCTRL);
426155192Srwatson
427155192Srwatson	case A_SETQCTRL:
428155192Srwatson		return (AUE_AUDITON_SQCTRL);
429155192Srwatson
430155192Srwatson	case A_GETCWD:
431155192Srwatson		return (AUE_AUDITON_GETCWD);
432155192Srwatson
433155192Srwatson	case A_GETCAR:
434155192Srwatson		return (AUE_AUDITON_GETCAR);
435155192Srwatson
436155192Srwatson	case A_GETSTAT:
437155192Srwatson		return (AUE_AUDITON_GETSTAT);
438155192Srwatson
439155192Srwatson	case A_SETSTAT:
440155192Srwatson		return (AUE_AUDITON_SETSTAT);
441155192Srwatson
442155192Srwatson	case A_SETUMASK:
443155192Srwatson		return (AUE_AUDITON_SETUMASK);
444155192Srwatson
445155192Srwatson	case A_SETSMASK:
446155192Srwatson		return (AUE_AUDITON_SETSMASK);
447155192Srwatson
448155192Srwatson	case A_GETCOND:
449155192Srwatson		return (AUE_AUDITON_GETCOND);
450155192Srwatson
451155192Srwatson	case A_SETCOND:
452155192Srwatson		return (AUE_AUDITON_SETCOND);
453155192Srwatson
454155192Srwatson	case A_GETCLASS:
455155192Srwatson		return (AUE_AUDITON_GETCLASS);
456155192Srwatson
457155192Srwatson	case A_SETCLASS:
458155192Srwatson		return (AUE_AUDITON_SETCLASS);
459155192Srwatson
460155192Srwatson	case A_GETPINFO:
461155192Srwatson	case A_SETPMASK:
462155192Srwatson	case A_SETFSIZE:
463155192Srwatson	case A_GETFSIZE:
464155192Srwatson	case A_GETPINFO_ADDR:
465155192Srwatson	case A_GETKAUDIT:
466155192Srwatson	case A_SETKAUDIT:
467155192Srwatson	default:
468155192Srwatson		return (AUE_AUDITON);	/* No special record */
469155192Srwatson	}
470155192Srwatson}
471155192Srwatson
472156889Srwatson/*
473156889Srwatson * Create a canonical path from given path by prefixing either the root
474156889Srwatson * directory, or the current working directory.  If the process working
475170196Srwatson * directory is NULL, we could use 'rootvnode' to obtain the root directory,
476156889Srwatson * but this results in a volfs name written to the audit log. So we will
477156889Srwatson * leave the filename starting with '/' in the audit log in this case.
478155192Srwatson *
479155192Srwatson * XXXRW: Since we combine two paths here, ideally a buffer of size
480155192Srwatson * MAXPATHLEN * 2 would be passed in.
481155192Srwatson */
482155192Srwatsonvoid
483155192Srwatsoncanon_path(struct thread *td, char *path, char *cpath)
484155192Srwatson{
485155192Srwatson	char *bufp;
486155192Srwatson	char *retbuf, *freebuf;
487155192Srwatson	struct vnode *vnp;
488155192Srwatson	struct filedesc *fdp;
489159143Scsjp	int cisr, error, vfslocked;
490155192Srwatson
491165621Srwatson	WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL,
492165621Srwatson	    "canon_path() at %s:%d", __FILE__, __LINE__);
493165621Srwatson
494155192Srwatson	fdp = td->td_proc->p_fd;
495155192Srwatson	bufp = path;
496159143Scsjp	cisr = 0;
497168355Srwatson	FILEDESC_SLOCK(fdp);
498155192Srwatson	if (*(path) == '/') {
499156889Srwatson		while (*(bufp) == '/')
500156889Srwatson			bufp++;			/* Skip leading '/'s. */
501156889Srwatson		/*
502156889Srwatson		 * If no process root, or it is the same as the system root,
503155192Srwatson		 * audit the path as passed in with a single '/'.
504155192Srwatson		 */
505155192Srwatson		if ((fdp->fd_rdir == NULL) ||
506156889Srwatson		    (fdp->fd_rdir == rootvnode)) {
507155192Srwatson			vnp = NULL;
508156889Srwatson			bufp--;			/* Restore one '/'. */
509155192Srwatson		} else {
510156889Srwatson			vnp = fdp->fd_rdir;	/* Use process root. */
511155192Srwatson			vref(vnp);
512155192Srwatson		}
513155192Srwatson	} else {
514156889Srwatson		vnp = fdp->fd_cdir;	/* Prepend the current dir. */
515159143Scsjp		cisr = (fdp->fd_rdir == fdp->fd_cdir);
516155192Srwatson		vref(vnp);
517155192Srwatson		bufp = path;
518155192Srwatson	}
519168355Srwatson	FILEDESC_SUNLOCK(fdp);
520155192Srwatson	if (vnp != NULL) {
521155192Srwatson		/*
522156889Srwatson		 * XXX: vn_fullpath() on FreeBSD is "less reliable" than
523156889Srwatson		 * vn_getpath() on Darwin, so this will need more attention
524156889Srwatson		 * in the future.  Also, the question and string bounding
525156889Srwatson		 * here seems a bit questionable and will also require
526156889Srwatson		 * attention.
527155192Srwatson		 */
528155192Srwatson		vfslocked = VFS_LOCK_GIANT(vnp->v_mount);
529155192Srwatson		vn_lock(vnp, LK_EXCLUSIVE | LK_RETRY, td);
530155192Srwatson		error = vn_fullpath(td, vnp, &retbuf, &freebuf);
531155192Srwatson		if (error == 0) {
532159143Scsjp			/* Copy and free buffer allocated by vn_fullpath().
533159143Scsjp			 * If the current working directory was the same as
534159143Scsjp			 * the root directory, and the path was a relative
535159143Scsjp			 * pathname, do not separate the two components with
536159143Scsjp			 * the '/' character.
537159143Scsjp			 */
538159143Scsjp			snprintf(cpath, MAXPATHLEN, "%s%s%s", retbuf,
539159143Scsjp			    cisr ? "" : "/", bufp);
540156889Srwatson			free(freebuf, M_TEMP);
541156889Srwatson		} else
542155192Srwatson			cpath[0] = '\0';
543155192Srwatson		vput(vnp);
544155192Srwatson		VFS_UNLOCK_GIANT(vfslocked);
545170196Srwatson	} else
546155192Srwatson		strlcpy(cpath, bufp, MAXPATHLEN);
547155192Srwatson}
548