Re: argument defs for vnode scope callbacks
Re: argument defs for vnode scope callbacks
- Subject: Re: argument defs for vnode scope callbacks
- From: Quinn <email@hidden>
- Date: Wed, 11 May 2005 10:26:55 +0100
At 22:49 +0200 10/5/05, Nicolas Berloquin wrote:
Is it documented ?
We don't yet have any formal documentation. However, I cooked up a
quick summary during the Tiger pre-release cycle, and I've included
it at the end of this email. I plan to convert this to formal
documentation eventually.
S+E
--
Quinn "The Eskimo!" <http://www.apple.com/developer/>
Apple Developer Technical Support * Networking, Communications, Hardware
----------------------------------------------------------------------
Kauth Notes
===========
Kauth Fundamentals
------------------
The Kauth system is new in Tiger. It was implemented primarily to
simplify the implementation of access control lists (ACLs), a major
new feature of the Tiger kernel. Because evaluating an ACL is a
complex business, the code for doing so has been abstracted out of
each file system plug-in and moved into the kernel proper. Kauth
does this in a general and flexible way.
Kauth defines a number of core concepts.
o scopes -- A scope is an area of interest for authorisation within
the kernel. For example, the scope KAUTH_SCOPE_VNODE is used for all
authorisation within the VFS layer. Scopes allow you to register an
interest in some subset of kernel authorisation decisions, without
being involved in /all/ authorisation decisions.
Scopes are strings formatted using reverse DNS notation (for example,
KAUTH_SCOPE_VNODE is "com.apple.kauth.vnode"), so you define your own
if you like.
o actions -- An action is an operation within a scope. For example,
the VFS layer defines an action, KAUTH_VNODE_READ_DATA, which
determines whether you're allowed to read data. Actions are
specified by integer constants (the actual type is kauth_action_t)
and each scope has its own action namespace.
The combination of a scope and an action defines an operation that
whose authorisation is to be checked.
o actors -- An actor is an entity that's performing an operation.
o credentials -- Credentials are the information that identifies an
actor. Credentials are specified by an opaque type, kauth_cred_t.
There are numerous accessor functions that let you operate on this
type. For example, kauth_cred_getuid returns the effective user ID
(EUID) from the credentials.
o request -- A request by an actor to perform an action (within a
particular scope).
o listener -- A listener is a callback that makes an authorisation
decision for a request. Fundamentally the listener authorises the
request based on the credentials of the actor. The listener is free
to use whatever scope- and action-dependent information necessary to
make that decision.
There is a default listener for every built-in scope. This listener
implements the standard BSD authorisation model for all actions
within that scope. In addition, you can register your own listeners
for a scope.
Implementing a Listener
-----------------------
The prototype for a listener is shown below.
static int MyListener(
kauth_cred_t credential,
void * idata,
kauth_action_t action,
uintptr_t arg0,
uintptr_t arg1,
uintptr_t arg2,
uintptr_t arg3
);
The meaning of the first three arguments is the same for all scopes.
o credential is a reference to the actor's credentials.
o idata is the cookie (or refCon) data you passed in when you
registered the listener (see below).
o action is the requested action (for example, KAUTH_VNODE_READ_DATA).
The meaning of the remaining parameters is scope dependent. I'll
discuss these in detail below. However, in most cases these
parameters give you extra information that allow you to make your
authorisation decision. For example, for the VFS scope
(KAUTH_SCOPE_VNODE), arg1 is a reference to the vnode (vnode_t)
that's being operated on.
A listener callback must return one of the following values.
o KAUTH_RESULT_DEFER -- This value indicates that the listener defers
the decision about this request to the other listeners (and
ultimately to the default listener).
o KAUTH_RESULT_ALLOW -- This value indicates that, as far as /this/
listener is concerned, the request is allowed.
o KAUTH_RESULT_DENY -- This value indicates that the request should be denied.
For request to be allowed, at least one listener must return
KAUTH_RESULT_ALLOW and no listeners can return KAUTH_RESULT_DENY.
This has a number of consequences.
- All listeners are called for all requests (because the last
listener just might return KAUTH_RESULT_DENY).
- Because a listener can't allow a request that's denied by any other
listener, a non-default listener can only /tighten/ security.
Registering a Listener
----------------------
You can register a listener for an existing scope using kauth_listen_scope.
extern kauth_listener_t kauth_listen_scope(
const char * identifier,
kauth_scope_callback_t callback,
void * idata
);
The parameters are as follows:
o identifier is the name of the scope.
o callback is the address of your listener callback function, with
the prototype shown above.
o idata is the cookie (or refCon) for your listener callback.
On error, the result is NULL. On success the result is a reference
to the listener; you can use this to deregister your listener.
It is not an error to register a listener before the corresponding
scope is registered. The system will remember your listener and
apply it once the scope appears.
Deregistering a Listener
------------------------
You can deregister a listener using kauth_unlisten_scope.
extern void kauth_unlisten_scope(kauth_listener_t listener);
The parameters are as follows:
o listener is a reference to the listener you got from kauth_listen_scope.
Registering a New Scope
-----------------------
IMPORTANT
You should only do this if you want to create your own custom
scope. In most cases third party developers should register
a listener for an existing scope.
You can register a new scope using kauth_register_scope.
extern kauth_scope_t kauth_register_scope(
const char * identifier,
kauth_scope_callback_t callback,
void * idata
);
The parameters are as follows:
o identifier is the name of the scope. It is an error to register a
scope that already exists.
o callback is the address of the listener callback function for this
scope; this becomes the scope's default listener. This parameter may
be NULL, in which case a callback that always returns
KAUTH_RESULT_DEFER is assumed.
o idata is the cookie (or refCon) for the listener callback.
On error, the result is NULL. On success the result is a reference
to the scope; you can use this to deregister your scope.
Deregistering a Scope
-----------------------
You can deregister a scope using kauth_deregister_scope:
extern void kauth_deregister_scope(kauth_scope_t scope);
The parameters are as follows:
o scope is a reference to the scope you got from kauth_register_scope.
Any listeners registers on the scope will go dormant; they'll be
reactivated if the scope is reregistered.
Authorising an Action
---------------------
You can make an authorisation request using kauth_authorize_action.
extern int kauth_authorize_action(
kauth_scope_t scope,
kauth_cred_t credential,
kauth_action_t action,
uintptr_t arg0,
uintptr_t arg1,
uintptr_t arg2,
uintptr_t arg3
);
The parameters are as follows:
o scope is a reference to the scope of in which the action is
defined. You get this value from the result of kauth_register_scope.
o credential is a reference to the actor's credentials. Normally you
would either already have this information, or you'd call
kauth_cred_get to get the credentials for the current thread.
o action is the requested action.
o arg0 through arg3 are passed unmodified to the listener callbacks.
If your kext supports plug-ins and those plug-ins call
kauth_authorize_action, you must have a way for your plug-ins to
discover the scope value (kauth_scope_t). The easiest way to do this
is to export a wrapper function that's tailored to your kext's
requirements.
IMPORTANT
The kernel already provides kauth_authorize_action wrappers for all
of the scopes that it defines. If you're authorising within a
kernel-defined scope you should call these wrappers rather than
calling kauth_authorize_action directly. These wrappers are
described below, along with corresponding scopes.
Process Scope
-------------
The process scope (KAUTH_SCOPE_PROCESS, "com.apple.kauth.process") is
one of the easiest to understand. The scope defines two actions:
o KAUTH_PROCESS_CANSIGNAL -- Authorises whether the current process
can signal the target process. arg0 (struct proc *) is the process
to be signalled. arg1 (int) is the signal that's being sent.
IMPORTANT
This action is not implemented in current Tiger builds
<rdar://problem/3931697>.
o KAUTH_PROCESS_CANTRACE -- Authorises whether the current process
can trace the target process. arg0 (struct proc *) is the process
being traced. arg1 (int *) is a pointer to an integer; if the
listener denies the request, it must set this value to a non-zero
error code.
The kernel also exports a kauth_authorize_action wrapper for this
scope, namely kauth_authorize_process.
extern int kauth_authorize_process(
kauth_cred_t credential,
kauth_action_t action,
struct proc * process,
uintptr_t arg1,
uintptr_t arg2,
uintptr_t arg3
);
This wrapper around kauth_authorize_action does two useful things:
1. It supplies the appropriate scope parameter.
2. It eliminates the need to cast process (struct proc *) to arg0 (uintptr_t).
File Operation Scope
--------------------
The file operation scope (KAUTH_SCOPE_FILEOP,
"com.apple.kauth.fileop") is different from other scopes in that it's
not used to actually authorise an operation; rather, the system uses
this scope to notify listeners of significant file operations. It
defines four actions:
o KAUTH_FILEOP_OPEN -- Notifies that a file has been opened. arg0
(vnode_t) is a vnode reference. arg1 (const char *) is a pointer to
the file's full path.
IMPORTANT
As of Tiger8A323, KAUTH_FILEOP_OPEN is not called when you exec a
Mach-O binary <rdar://problem/3932944>.
o KAUTH_FILEOP_CLOSE -- Notifies that a file is about to be closed.
arg0 (vnode_t) is a vnode reference. arg1 (const char *) is a
pointer to the file's full path.
o KAUTH_FILEOP_RENAME -- Notifies that a file is has been renamed.
arg0 (const char *) is a pointer to the file's previous full path.
arg1 (const char *) is a pointer to the file's new full path.
o KAUTH_FILEOP_EXCHANGE -- Notifies that two files have been
exchanged (via <x-man-page://2/exchangedata>. arg0 (const char *) is
a pointer to the file file's full path. arg1 (const char *) is a
pointer to the second file's full path.
If you install a listener in this scope, it will be called to notify
you of these events. The kernel ignores the return value of your
listener, although we recommend taht you return KAUTH_RESULT_DEFER.
Your listener is called by the thread that's performing the
operation, so you can block for extended periods of time. One
possible use for this facility is to implement a file checking
facility. Your listener can pass the path of the file to a daemon
and then block waiting for the reply. That process can then check
the file, repairing it if necessary. Finally, when it's done the
daemon process can reply to the kernel, which unblocks the listener.
The kernel also exports a kauth_authorize_action wrapper for this
scope, namely kauth_authorize_fileop.
extern int kauth_authorize_fileop(
kauth_cred_t credential,
kauth_action_t action,
uintptr_t arg0,
uintptr_t arg1
);
This is typically not useful for third party developers.
Generic Scope
-------------
The generic scope (KAUTH_SCOPE_GENERIC, "com.apple.kauth.generic")
has a single action, KAUTH_GENERIC_ISSUSER, which the kernel reques
to test whether a credential has superuser privileges. None of the
generic arguments (arg0 through arg3) are significant.
IMPORTANT:
The kernel does not currently use this request for all superuser
tests; in many cases the kernel continues to directly compare the
credential's effective user ID to 0.
The kernel also exports a kauth_authorize_action wrapper for this
scope, namely kauth_authorize_generic.
extern int kauth_authorize_generic(
kauth_cred_t credential,
kauth_action_t action
);
Vnode Scope
-----------
The vnode scope (KAUTH_SCOPE_VNODE, "com.apple.kauth.vnode") is the
most complex scope currently defined. The first thing to note is
that, within the vnode scope, actions are not enumerations but rather
bitfields. Thus, it's perfectly reasonable to combine actions by
ORing them together. For example, an action of KAUTH_VNODE_READ_DATA
| KAUTH_VNODE_EXECUTE indicates that actor wishes to both read and
execute the file.
To authorise an action within the vnode scope, you would call vnode_authorize
extern int vnode_authorize(
vnode_t vp,
vnode_t dvp,
kauth_action_t action,
vfs_context_t context
);
The parameters are as follows:
o vp is the vnode on which the action is being performed.
o dvp is the parent directory's vnode. In many cases this is NULL,
indicating that the parent is unknown or irrelevant.
o action is the operation being performed; this is discussed in detail below.
o context is the VFS context associated with the actor. This is an
opaque data structure that's intimately tied into the VFS
implementation. Most VFS entry points are passed this context. In
addition, there are numerous VFS context routines defined in
<sys/vnode.h>.
IMPORTANT:
It is relatively unusual for a VFS plug-in to call vnode_authorize.
In most cases the VFS layer has authorised all actions before calling
your plug-in. However, there are some circumstances where a VFS
plug-in should call vnode_authorize. For example, HFS [Plus]'s
implementation of searchfs uses vnode_authorized to ensure that the
caller has access to the file system objects that it's returning.
vnode_authorize is a fairly simple wrapper around
kauth_authorize_action. It performs two useful functions:
1. It assembles the correct scope listener arguments (arg0 through
arg3). These are discussed below.
2. It ensures that the error code is correct. Specifically, it
translates the EPERM error returned by kauth_authorize_action into
EACCES (which is the appropriate error for file system functions).
Also, if the listener denies a request and provides a specific error
code (via arg3, see below), it returns that error.
The scope listener arguments for the vnode scope are as follows:
o arg0 -- context (vfs_context_t) -- The VFS context, described above.
o arg1 -- vp (vnode_t) -- The vnode itself.
o arg2 -- dvp (vnode_t) -- The parent vnode, if available. This may be NULL.
o arg3 -- errPtr (int *) -- A pointer to an error. If your callback
denies the request, it can set this value to indicate the error to
return to the client. If you don't set this, the client gets EACCES.
Within the vnode scope, the following standard actions are defined:
o KAUTH_VNODE_READ_DATA (also KAUTH_VNODE_LIST_DIRECTORY) -- If the
vnode is a directory, authorises whether the actor can enumerate the
contents of that directory. Otherwise, authorises whether the actor
can read the contents of a file.
o KAUTH_VNODE_WRITE_DATA (also KAUTH_VNODE_ADD_FILE) -- If the vnode
is a directory, authorises whether the actor can add a file to that
directory. vp is the directory to which the file is being added; dvp
is NULL. Otherwise, authorises whether the actor can write the
contents of a file.
o KAUTH_VNODE_EXECUTE (also KAUTH_VNODE_SEARCH) -- If the vnode is a
directory, authorises whether the actor can probe for the existance
of an item within the directory as part of a path lookup. Otherwise,
authorises whether the actor can execute the contents of a file.
o KAUTH_VNODE_DELETE -- Authorises whether the actor can delete an
item from a directory. vp is the item to be deleted and dvp is the
directory it's being deleted from.
o KAUTH_VNODE_APPEND_DATA (also KAUTH_VNODE_ADD_SUBDIRECTORY) -- If
the vnode is a directory, authorises whether the actor can add a
directory to it. *** AFAICT the previous isn't true. *** Otherwise,
authorises whether the actor can append data to the contents of a
file.
o KAUTH_VNODE_DELETE_CHILD -- *** I'm unsure what this does; AFAICT
it's only used internally by the default listener for the vnode
scope. ***
o KAUTH_VNODE_READ_ATTRIBUTES -- Authorises whether the actor can
read standard attributes of the vnode (such as the time stamps).
o KAUTH_VNODE_WRITE_ATTRIBUTES -- Authorises whether the actor can
change standard attributes of the vnode (such as the time stamps).
o KAUTH_VNODE_READ_EXTATTRIBUTES -- Authorises whether the actor can
read extended attributes of the vnode (those accessed via getxattr,
but also including the resource fork).
o KAUTH_VNODE_WRITE_EXTATTRIBUTES -- Authorises whether the actor can
change (or add) extended attributes of the vnode (those accessed via
getxattr, but also including the resource fork).
o KAUTH_VNODE_READ_SECURITY -- Authorises whether the actor can read
the vnode's ACL.
o KAUTH_VNODE_WRITE_SECURITY -- Authorises whether the actor can
write the vnode's ACL.
o KAUTH_VNODE_TAKE_OWNERSHIP -- Authorises whether the actor can
change ownership of the vnode.
o KAUTH_VNODE_SYNCHRONIZE -- *** for Windows compatibility; not
currently used ***
o KAUTH_VNODE_LINKTARGET -- Authorises whether the actor can make a
new hard link to the vnode.
o KAUTH_VNODE_CHECKIMMUTABLE -- Authorises whether the actor is
allowed to modify the file (in the SF_IMMUTABLE sense). This flag is
set if other checks have already been made to check that the file can
by modified, but the modification should still fail for immutable
files.
o KAUTH_VNODE_ACCESS -- This is a special flag. If this flag is set
the authorisation request is advisory (for example, to satisfy an
access <x-man-page://2/access> systme call) rathehr than
authoritative. A listener can use this to avoid doing extra work in
advisory case.
o KAUTH_VNODE_NOIMMUTABLE -- This is a special flag. It is passed to
the listener along with the KAUTH_VNODE_WRITE_SECURITY bit (and no
others) to indicate that the actor wishes to change one or more of
the immutable flags, and the state of these flags should not be
considered when authorizing the request.
----------------------------------------------------------------------
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Darwin-kernel mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden