NFSMAPID(8)
NFS user and group id mapping daemon
usr/src/cmd/fs.d/nfs/nfsmapid/nfsmapid_server.cusr/src/cmd/fs.d/nfs/nfsmapid/nfsmapid_test.cusr/src/cmd/fs.d/nfs/nfsmapid/nfsmapid.c
/*
* Door server routines for nfsmapid daemon
* Translate NFSv4 users and groups between numeric and string values
*/
The idmap_kcall function takes either a door descritor as an argument, or -1
(which is never a valid file descriptor) as a signal to flush any idmap-related
caches that the kernel may be holding. This is utlimately forwarded to the
kernel via the _nfssys system call.
Command Switching
nfsmpaid_func is a server procedure which simulataneously casts the door
arguments to two different types: refd_door_args_t and mapid_arg. As a
mapid_arg, the payload's cmd field is switched among functions that map
between group names and ids or user names and ids. Only in one case does
the server use the refd_door_args_t form of the payload, this in order to
serve some kind of network statistics.
So this is an example of both a Switching Table and Payload Polymorphism (though for the latter, not in the usual sense).
Part of the validation of the user-provided payload is to ensure that the number of bytes transferred is at least the size of the struct into which we will cast that payload:
if (arg_size < sizeof (refd_door_args_t)) {
syslog(LOG_ERR,
"nfsmapid_server_netinfo failed: Invalid data\n");
door_return(NULL, 0, NULL, 0);
}
There is also an appearance of utf8 decoding here (see decode_args and
xdr_utf8string), which falls under the category (not always practiced!) of
validating user-provided input.
Expecting door_return failure
/*
* There is a chance that the door_return will fail because the
* resulting string is too large, try to indicate that if possible
*/
if (door_return((char *)resp,
MAPID_RES_LEN(resp->u_res.len), NULL, 0) == -1) {
resp->status = NFSMAPID_INTERNAL;
resp->u_res.len = 0;
(void) door_return((char *)&result, sizeof (struct mapid_res),
NULL, 0);
}
Why is it that we are so confident the second door_return call will succeed?
What is the size above which we begin to worry about door_return failures?
Here we explicitly check to see if door_return returned E2BIG to complain
about the payload being too big:
send_response:
srsz = res_size;
errno = 0;
error = door_return(res, res_size, NULL, 0);
if (errno == E2BIG) {
failed_res.res_status = EOVERFLOW;
failed_res.xdr_len = srsz;
res = (caddr_t)&failed_res;
res_size = sizeof (refd_door_res_t);
} else {
res = NULL;
res_size = 0;
}
door_return(res, res_size, NULL, 0);
Tests
The nfsmapid_test.c file contains tests which actually place door calls. This
does not seem to be a common pattern.
Instead of using a global Static File Descriptor
for the door, the function nfs_idmap_doorget() declares a local static
descriptor; this function will attempt to open the door if one has not already
been opened, but otherwise return the opened doorfd:
int
nfs_idmap_doorget()
{
static int doorfd = -1;
if (doorfd != -1)
return (doorfd);
if ((doorfd = open(NFSMAPID_DOOR, O_RDWR)) == -1) {
perror(NFSMAPID_DOOR);
exit(1);
}
return (doorfd);
}
This function will exit rather than returning -1. However, all callers check
for this return value anyhow:
/* from nfs_idmap_uid_str() */
if ((doorfd = nfs_idmap_doorget()) == -1) {
fprintf(stderr, "nfs_idmap_uid_str: Can't "
"communicate with mapping daemon nfsmapid\n");
}
Unmapping rbuf
All the tests unmap the returned buffer iff the returned buffer has a
different address than the buffer provided to the door_call:
out:
if (resp != mapresp)
munmap(door_args.rbuf, door_args.rsize);
return (error);
Anticipating door_call failure
This code anticipates that door_call itself will fail, rather than the door
server returning an error. This is good defense against the server dying, but it
does not seem to be used all over the place:
if (door_call(doorfd, &door_args) == -1) {
perror("door_call failed");
}
Application Setup
There is an attempt to create a door "jamb" with an extremely specific set of permissions, but door calls don't really honor these...
/*
* Create a file system path for the door
*/
if ((dfd = open(NFSMAPID_DOOR, O_RDWR|O_CREAT|O_TRUNC,
S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) == -1) {
syslog(LOG_ERR, "Unable to open %s: %m\n", NFSMAPID_DOOR);
(void) close(doorfd);
return (1);
}
The door for this application is created with DOOR_NO_CANCEL. Interestingly,
the door_create call is treated as fallible:
if ((doorfd = door_create(nfsmapid_func, NULL,
DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
syslog(LOG_ERR, "Unable to create door: %m\n");
return (1);
}
Why would that fail? Does the system have an upper bound on the number of file descriptors it's willing to issue?