UDS: prepare for socket file creation in bind(2)

This patch prepares for moving of the creation of socket files on the
file system from the libc bind(2) stub into the UDS service.  This
change is necessary for the socket type agnostic libc implementation.
The change is not yet activated - the code that is not yet used is
enclosed in "#if NOT_YET" blocks.  The activation needs to be atomic
with UDS's switch to libsockdriver; otherwise, user applications may
break.

As part of the change, various UDS bind(2) semantics are changed to
match the POSIX standard and other operating systems.  In
implementation terms, the service-only VFS API checkperms(2) is
renamed to socketpath(2), and extended with a new subcall which
creates a new socket file.  An extension to test56 checks the new
bind(2) semantics of UDS, although most new tests are still disabled
until activation as well.

Finally, as further preparation for a more structural redesign of the
UDS service, also return the <device,inode> number pair for the
created or checked file name, and make returning the canonized path
name optional.

Change-Id: I892d04b3301d4b911bdc571632ddde65fb747a8a
This commit is contained in:
David van Moolenbroek 2015-12-27 19:35:12 +00:00
parent 4c27a83389
commit dd96967135
13 changed files with 429 additions and 108 deletions

View File

@ -116,7 +116,7 @@
#define VFS_GCOV_FLUSH (VFS_BASE + 44) #define VFS_GCOV_FLUSH (VFS_BASE + 44)
#define VFS_MAPDRIVER (VFS_BASE + 45) #define VFS_MAPDRIVER (VFS_BASE + 45)
#define VFS_COPYFD (VFS_BASE + 46) #define VFS_COPYFD (VFS_BASE + 46)
#define VFS_CHECKPERMS (VFS_BASE + 47) #define VFS_SOCKETPATH (VFS_BASE + 47)
#define VFS_GETSYSINFO (VFS_BASE + 48) #define VFS_GETSYSINFO (VFS_BASE + 48)
#define VFS_SOCKET (VFS_BASE + 49) #define VFS_SOCKET (VFS_BASE + 49)
#define VFS_SOCKETPAIR (VFS_BASE + 50) #define VFS_SOCKETPAIR (VFS_BASE + 50)

View File

@ -1451,15 +1451,6 @@ typedef struct {
} mess_lsys_tty_fkey_ctl; } mess_lsys_tty_fkey_ctl;
_ASSERT_MSG_SIZE(mess_lsys_tty_fkey_ctl); _ASSERT_MSG_SIZE(mess_lsys_tty_fkey_ctl);
typedef struct {
endpoint_t endpt;
cp_grant_id_t grant;
size_t count;
uint8_t padding[44];
} mess_lsys_vfs_checkperms;
_ASSERT_MSG_SIZE(mess_lsys_vfs_checkperms);
typedef struct { typedef struct {
endpoint_t endpt; endpoint_t endpt;
int fd; int fd;
@ -1480,6 +1471,16 @@ typedef struct {
} mess_lsys_vfs_mapdriver; } mess_lsys_vfs_mapdriver;
_ASSERT_MSG_SIZE(mess_lsys_vfs_mapdriver); _ASSERT_MSG_SIZE(mess_lsys_vfs_mapdriver);
typedef struct {
endpoint_t endpt;
cp_grant_id_t grant;
size_t count;
int what;
uint8_t padding[40];
} mess_lsys_vfs_socketpath;
_ASSERT_MSG_SIZE(mess_lsys_vfs_socketpath);
typedef struct { typedef struct {
endpoint_t endpt; endpoint_t endpt;
void *addr; void *addr;
@ -2266,6 +2267,14 @@ typedef struct {
} mess_vfs_lsys_gcov; } mess_vfs_lsys_gcov;
_ASSERT_MSG_SIZE(mess_vfs_lsys_gcov); _ASSERT_MSG_SIZE(mess_vfs_lsys_gcov);
typedef struct {
dev_t device;
ino_t inode;
uint8_t padding[40];
} mess_vfs_lsys_socketpath;
_ASSERT_MSG_SIZE(mess_vfs_lsys_socketpath);
typedef struct { typedef struct {
time_t atime; time_t atime;
time_t mtime; time_t mtime;
@ -2481,9 +2490,9 @@ typedef struct noxfer_message {
mess_lsys_sched_scheduling_start m_lsys_sched_scheduling_start; mess_lsys_sched_scheduling_start m_lsys_sched_scheduling_start;
mess_lsys_sched_scheduling_stop m_lsys_sched_scheduling_stop; mess_lsys_sched_scheduling_stop m_lsys_sched_scheduling_stop;
mess_lsys_tty_fkey_ctl m_lsys_tty_fkey_ctl; mess_lsys_tty_fkey_ctl m_lsys_tty_fkey_ctl;
mess_lsys_vfs_checkperms m_lsys_vfs_checkperms;
mess_lsys_vfs_copyfd m_lsys_vfs_copyfd; mess_lsys_vfs_copyfd m_lsys_vfs_copyfd;
mess_lsys_vfs_mapdriver m_lsys_vfs_mapdriver; mess_lsys_vfs_mapdriver m_lsys_vfs_mapdriver;
mess_lsys_vfs_socketpath m_lsys_vfs_socketpath;
mess_lsys_vm_getref m_lsys_vm_getref; mess_lsys_vm_getref m_lsys_vm_getref;
mess_lsys_vm_info m_lsys_vm_info; mess_lsys_vm_info m_lsys_vm_info;
mess_lsys_vm_map_phys m_lsys_vm_map_phys; mess_lsys_vm_map_phys m_lsys_vm_map_phys;
@ -2567,6 +2576,7 @@ typedef struct noxfer_message {
mess_vfs_lsockdriver_simple m_vfs_lsockdriver_simple; mess_vfs_lsockdriver_simple m_vfs_lsockdriver_simple;
mess_vfs_lsockdriver_socket m_vfs_lsockdriver_socket; mess_vfs_lsockdriver_socket m_vfs_lsockdriver_socket;
mess_vfs_lsys_gcov m_vfs_lsys_gcov; mess_vfs_lsys_gcov m_vfs_lsys_gcov;
mess_vfs_lsys_socketpath m_vfs_lsys_socketpath;
mess_vfs_utimens m_vfs_utimens; mess_vfs_utimens m_vfs_utimens;
mess_vm_vfs_mmap m_vm_vfs_mmap; mess_vm_vfs_mmap m_vm_vfs_mmap;
mess_vmmcp m_vmmcp; mess_vmmcp m_vmmcp;

View File

@ -269,7 +269,11 @@ int mapdriver(const char *label, devmajor_t major, const int *domains,
pid_t getnpid(endpoint_t proc_ep); pid_t getnpid(endpoint_t proc_ep);
uid_t getnuid(endpoint_t proc_ep); uid_t getnuid(endpoint_t proc_ep);
gid_t getngid(endpoint_t proc_ep); gid_t getngid(endpoint_t proc_ep);
int checkperms(endpoint_t endpt, char *path, size_t size); int socketpath(endpoint_t endpt, char *path, size_t size, int what, dev_t *dev,
ino_t *ino);
#define SPATH_CHECK 0 /* check user permissions on socket path */
#define SPATH_CREATE 1 /* create socket file at given path */
#define SPATH_CANONIZE 0x8000 /* copy back canonized path (legacy support) */
int copyfd(endpoint_t endpt, int fd, int what); int copyfd(endpoint_t endpt, int fd, int what);
#define COPYFD_FROM 0 /* copy file descriptor from remote process */ #define COPYFD_FROM 0 /* copy file descriptor from remote process */
#define COPYFD_TO 1 /* copy file descriptor to remote process */ #define COPYFD_TO 1 /* copy file descriptor to remote process */

View File

@ -13,7 +13,6 @@ SRCS+= \
alloc_util.c \ alloc_util.c \
assert.c \ assert.c \
asynsend.c \ asynsend.c \
checkperms.c \
clock_time.c \ clock_time.c \
closenb.c \ closenb.c \
copyfd.c \ copyfd.c \
@ -49,6 +48,7 @@ SRCS+= \
sef_ping.c \ sef_ping.c \
sef_signal.c \ sef_signal.c \
sef_st.c \ sef_st.c \
socketpath.c \
sqrt_approx.c \ sqrt_approx.c \
srv_fork.c \ srv_fork.c \
srv_kill.c \ srv_kill.c \

View File

@ -1,28 +0,0 @@
#include "syslib.h"
#include <unistd.h>
#include <string.h>
#include <minix/safecopies.h>
int
checkperms(endpoint_t endpt, char *path, size_t size)
{
cp_grant_id_t grant;
message m;
int r;
if ((grant = cpf_grant_direct(VFS_PROC_NR, (vir_bytes) path, size,
CPF_READ | CPF_WRITE)) == GRANT_INVALID)
return ENOMEM;
memset(&m, 0, sizeof(m));
m.m_lsys_vfs_checkperms.endpt = endpt;
m.m_lsys_vfs_checkperms.grant = grant;
m.m_lsys_vfs_checkperms.count = size;
r = _taskcall(VFS_PROC_NR, VFS_CHECKPERMS, &m);
cpf_revoke(grant);
return r;
}

View File

@ -0,0 +1,35 @@
#include "syslib.h"
#include <unistd.h>
#include <string.h>
#include <minix/safecopies.h>
int
socketpath(endpoint_t endpt, char * path, size_t size, int what, dev_t * dev,
ino_t * ino)
{
cp_grant_id_t grant;
message m;
int r;
if ((grant = cpf_grant_direct(VFS_PROC_NR, (vir_bytes)path, size,
CPF_READ | CPF_WRITE)) == GRANT_INVALID)
return ENOMEM;
memset(&m, 0, sizeof(m));
m.m_lsys_vfs_socketpath.endpt = endpt;
m.m_lsys_vfs_socketpath.grant = grant;
m.m_lsys_vfs_socketpath.count = size;
m.m_lsys_vfs_socketpath.what = what | SPATH_CANONIZE;
r = _taskcall(VFS_PROC_NR, VFS_SOCKETPATH, &m);
cpf_revoke(grant);
if (r == OK) {
*dev = m.m_vfs_lsys_socketpath.device;
*ino = m.m_vfs_lsys_socketpath.inode;
}
return r;
}

View File

@ -67,10 +67,11 @@ do_accept(devminor_t minor, endpoint_t endpt, cp_grant_id_t grant)
/* Locate the server socket. */ /* Locate the server socket. */
for (i = 0; i < NR_FDS; i++) { for (i = 0; i < NR_FDS; i++) {
if (uds_fd_table[i].addr.sun_family == AF_UNIX && if (uds_fd_table[i].stale == FALSE &&
uds_fd_table[i].listening == TRUE &&
uds_fd_table[i].addr.sun_family == AF_UNIX &&
!strncmp(addr.sun_path, uds_fd_table[i].addr.sun_path, !strncmp(addr.sun_path, uds_fd_table[i].addr.sun_path,
sizeof(uds_fd_table[i].addr.sun_path)) && sizeof(uds_fd_table[i].addr.sun_path)))
uds_fd_table[i].listening == 1)
break; break;
} }
@ -146,6 +147,8 @@ do_connect(devminor_t minor, endpoint_t endpt, cp_grant_id_t grant)
int child, peer; int child, peer;
struct sockaddr_un addr; struct sockaddr_un addr;
int rc, i, j; int rc, i, j;
dev_t dev;
ino_t ino;
dprintf(("UDS: do_connect(%d)\n", minor)); dprintf(("UDS: do_connect(%d)\n", minor));
@ -167,8 +170,8 @@ do_connect(devminor_t minor, endpoint_t endpt, cp_grant_id_t grant)
sizeof(struct sockaddr_un))) != OK) sizeof(struct sockaddr_un))) != OK)
return rc; return rc;
if ((rc = checkperms(uds_fd_table[minor].owner, addr.sun_path, if ((rc = socketpath(uds_fd_table[minor].owner, addr.sun_path,
sizeof(addr.sun_path))) != OK) sizeof(addr.sun_path), SPATH_CHECK, &dev, &ino)) != OK)
return rc; return rc;
/* /*
@ -178,7 +181,9 @@ do_connect(devminor_t minor, endpoint_t endpt, cp_grant_id_t grant)
for (i = 0; i < NR_FDS; i++) { for (i = 0; i < NR_FDS; i++) {
if (uds_fd_table[minor].type != uds_fd_table[i].type) if (uds_fd_table[minor].type != uds_fd_table[i].type)
continue; continue;
if (!uds_fd_table[i].listening) if (uds_fd_table[i].listening == FALSE)
continue;
if (uds_fd_table[i].stale == TRUE)
continue; continue;
if (uds_fd_table[i].addr.sun_family != AF_UNIX) if (uds_fd_table[i].addr.sun_family != AF_UNIX)
continue; continue;
@ -276,7 +281,7 @@ do_listen(devminor_t minor, endpoint_t endpt, cp_grant_id_t grant)
sizeof(backlog_size))) != OK) sizeof(backlog_size))) != OK)
return rc; return rc;
if (uds_fd_table[minor].listening == 0) { if (uds_fd_table[minor].listening == FALSE) {
/* Set the backlog size to a reasonable value. */ /* Set the backlog size to a reasonable value. */
if (backlog_size <= 0 || backlog_size > UDS_SOMAXCONN) if (backlog_size <= 0 || backlog_size > UDS_SOMAXCONN)
backlog_size = UDS_SOMAXCONN; backlog_size = UDS_SOMAXCONN;
@ -295,7 +300,7 @@ do_listen(devminor_t minor, endpoint_t endpt, cp_grant_id_t grant)
} }
/* This socket is now listening. */ /* This socket is now listening. */
uds_fd_table[minor].listening = 1; uds_fd_table[minor].listening = TRUE;
return OK; return OK;
} }
@ -334,6 +339,8 @@ do_bind(devminor_t minor, endpoint_t endpt, cp_grant_id_t grant)
{ {
struct sockaddr_un addr; struct sockaddr_un addr;
int rc, i; int rc, i;
dev_t dev;
ino_t ino;
dprintf(("UDS: do_bind(%d)\n", minor)); dprintf(("UDS: do_bind(%d)\n", minor));
@ -356,21 +363,41 @@ do_bind(devminor_t minor, endpoint_t endpt, cp_grant_id_t grant)
if (addr.sun_path[0] == '\0') if (addr.sun_path[0] == '\0')
return ENOENT; return ENOENT;
if ((rc = checkperms(uds_fd_table[minor].owner, addr.sun_path, /* Attempt to create the socket file. */
sizeof(addr.sun_path))) != OK) if ((rc = socketpath(uds_fd_table[minor].owner, addr.sun_path,
#if NOT_YET
sizeof(addr.sun_path), SPATH_CREATE, &dev, &ino)) != OK)
#else
sizeof(addr.sun_path), SPATH_CHECK, &dev, &ino)) != OK)
#endif
return rc; return rc;
/* Make sure the address isn't already in use by another socket. */ /*
* It is possible that the socket path name was already in use as
* address by another socket. This means that the socket file was
* prematurely unlinked. In that case, mark the old socket as stale,
* so that its path name will not be matched and only the newly bound
* socket will be found in address-based searches. For now, we leave
* the old socket marked as stale for as long as it is bound to the
* same address. A more advanced implementation could establish an
* order between the sockets so that the most recently bound socket is
* found at any time, but it is doubtful whether that would be useful.
*/
for (i = 0; i < NR_FDS; i++) { for (i = 0; i < NR_FDS; i++) {
if (uds_fd_table[i].addr.sun_family == AF_UNIX && if (uds_fd_table[i].stale == FALSE &&
uds_fd_table[i].addr.sun_family == AF_UNIX &&
!strncmp(addr.sun_path, uds_fd_table[i].addr.sun_path, !strncmp(addr.sun_path, uds_fd_table[i].addr.sun_path,
sizeof(uds_fd_table[i].addr.sun_path))) { sizeof(uds_fd_table[i].addr.sun_path))) {
/* Another socket is bound to this sun_path. */ #if NOT_YET
uds_fd_table[i].stale = TRUE;
#else
return EADDRINUSE; return EADDRINUSE;
#endif
} }
} }
/* Looks good, perform the bind(). */ /* Looks good, perform the bind(). */
uds_fd_table[minor].stale = FALSE;
memcpy(&uds_fd_table[minor].addr, &addr, sizeof(struct sockaddr_un)); memcpy(&uds_fd_table[minor].addr, &addr, sizeof(struct sockaddr_un));
return OK; return OK;
@ -597,6 +624,8 @@ do_sendto(devminor_t minor, endpoint_t endpt, cp_grant_id_t grant)
{ {
int rc; int rc;
struct sockaddr_un addr; struct sockaddr_un addr;
dev_t dev;
ino_t ino;
dprintf(("UDS: do_sendto(%d)\n", minor)); dprintf(("UDS: do_sendto(%d)\n", minor));
@ -612,8 +641,8 @@ do_sendto(devminor_t minor, endpoint_t endpt, cp_grant_id_t grant)
if (addr.sun_family != AF_UNIX || addr.sun_path[0] == '\0') if (addr.sun_family != AF_UNIX || addr.sun_path[0] == '\0')
return EINVAL; return EINVAL;
if ((rc = checkperms(uds_fd_table[minor].owner, addr.sun_path, if ((rc = socketpath(uds_fd_table[minor].owner, addr.sun_path,
sizeof(addr.sun_path))) != OK) sizeof(addr.sun_path), SPATH_CHECK, &dev, &ino)) != OK)
return rc; return rc;
memcpy(&uds_fd_table[minor].target, &addr, sizeof(struct sockaddr_un)); memcpy(&uds_fd_table[minor].target, &addr, sizeof(struct sockaddr_un));
@ -825,6 +854,7 @@ do_sendmsg(devminor_t minor, endpoint_t endpt, cp_grant_id_t grant)
* target address. * target address.
*/ */
if (uds_fd_table[i].type == SOCK_DGRAM && if (uds_fd_table[i].type == SOCK_DGRAM &&
uds_fd_table[i].stale == FALSE &&
uds_fd_table[i].addr.sun_family == AF_UNIX && uds_fd_table[i].addr.sun_family == AF_UNIX &&
!strncmp(uds_fd_table[minor].target.sun_path, !strncmp(uds_fd_table[minor].target.sun_path,
uds_fd_table[i].addr.sun_path, uds_fd_table[i].addr.sun_path,

View File

@ -94,7 +94,8 @@ uds_open(devminor_t UNUSED(orig_minor), int access,
for (i = 0; i < OPEN_MAX; i++) for (i = 0; i < OPEN_MAX; i++)
uds_fd_table[minor].ancillary_data.fds[i] = -1; uds_fd_table[minor].ancillary_data.fds[i] = -1;
uds_fd_table[minor].listening = 0; uds_fd_table[minor].stale = FALSE;
uds_fd_table[minor].listening = FALSE;
uds_fd_table[minor].peer = -1; uds_fd_table[minor].peer = -1;
uds_fd_table[minor].child = -1; uds_fd_table[minor].child = -1;
@ -203,7 +204,7 @@ uds_select(devminor_t minor, unsigned int ops, endpoint_t endpt)
bytes = uds_perform_read(minor, NONE, GRANT_INVALID, 1, 1); bytes = uds_perform_read(minor, NONE, GRANT_INVALID, 1, 1);
if (bytes > 0) { if (bytes > 0) {
ready_ops |= CDEV_OP_RD; /* data available */ ready_ops |= CDEV_OP_RD; /* data available */
} else if (uds_fd_table[minor].listening == 1) { } else if (uds_fd_table[minor].listening == TRUE) {
/* Check for pending connections. */ /* Check for pending connections. */
for (i = 0; i < uds_fd_table[minor].backlog_size; i++) for (i = 0; i < uds_fd_table[minor].backlog_size; i++)
{ {
@ -389,6 +390,7 @@ uds_perform_write(devminor_t minor, endpoint_t endpt, cp_grant_id_t grant,
* the target address. * the target address.
*/ */
if (uds_fd_table[i].type == SOCK_DGRAM && if (uds_fd_table[i].type == SOCK_DGRAM &&
uds_fd_table[i].stale == FALSE &&
uds_fd_table[i].addr.sun_family == AF_UNIX && uds_fd_table[i].addr.sun_family == AF_UNIX &&
!strncmp(uds_fd_table[minor].target.sun_path, !strncmp(uds_fd_table[minor].target.sun_path,
uds_fd_table[i].addr.sun_path, uds_fd_table[i].addr.sun_path,

View File

@ -121,8 +121,14 @@ struct uds_fd {
*/ */
struct sockaddr_un source; struct sockaddr_un source;
/* Flag (1 or 0) - listening for incoming connections. /* Flag (TRUE or FALSE) - address overridden by newer socket.
* Default to 0. Set to 1 by do_listen() * Default to FALSE. Set to TRUE by do_bind() on another socket with
* the same path but its on-disk socket file removed in the meantime.
*/
int stale;
/* Flag (TRUE or FALSE) - listening for incoming connections.
* Default to FALSE. Set to TRUE by do_listen().
*/ */
int listening; int listening;

View File

@ -33,8 +33,6 @@
static int lookup(struct vnode *dirp, struct lookup *resolve, static int lookup(struct vnode *dirp, struct lookup *resolve,
node_details_t *node, struct fproc *rfp); node_details_t *node, struct fproc *rfp);
static int check_perms(endpoint_t ep, cp_grant_id_t io_gr, size_t
pathlen);
/*===========================================================================* /*===========================================================================*
* advance * * advance *
@ -801,64 +799,145 @@ canonical_path(char orig_path[PATH_MAX], struct fproc *rfp)
} }
/*===========================================================================* /*===========================================================================*
* check_perms * * do_socketpath *
*===========================================================================*/ *===========================================================================*/
static int check_perms(ep, io_gr, pathlen) int do_socketpath(void)
endpoint_t ep;
cp_grant_id_t io_gr;
size_t pathlen;
{ {
int r, slot; /*
struct vnode *vp; * Perform a path action on an on-disk socket file. This call may be performed
struct vmnt *vmp; * by the UDS service only. The action is always on behalf of a user process
* that is currently making a socket call to the UDS service, and thus, VFS may
* rely on the fact that the user process is blocked. TODO: there should be
* checks in place to prevent (even accidental) abuse of this function, though.
*/
int r, what, slot;
endpoint_t ep;
cp_grant_id_t io_gr;
size_t pathlen;
struct vnode *dirp, *vp;
struct vmnt *vmp, *vmp2;
struct fproc *rfp; struct fproc *rfp;
char canon_path[PATH_MAX]; char path[PATH_MAX];
struct lookup resolve; struct lookup resolve, resolve2;
struct sockaddr_un sun; struct sockaddr_un sun;
mode_t bits;
/* This should be replaced by an ACL check. */
if (!super_user) return EPERM;
ep = job_m_in.m_lsys_vfs_socketpath.endpt;
io_gr = job_m_in.m_lsys_vfs_socketpath.grant;
pathlen = job_m_in.m_lsys_vfs_socketpath.count;
what = job_m_in.m_lsys_vfs_socketpath.what;
if (isokendpt(ep, &slot) != OK) return(EINVAL); if (isokendpt(ep, &slot) != OK) return(EINVAL);
if (pathlen < sizeof(sun.sun_path) || pathlen >= PATH_MAX) return(EINVAL); if (pathlen < sizeof(sun.sun_path) || pathlen >= PATH_MAX) return(EINVAL);
rfp = &(fproc[slot]); rfp = &(fproc[slot]);
r = sys_safecopyfrom(who_e, io_gr, (vir_bytes) 0, (vir_bytes) canon_path, r = sys_safecopyfrom(who_e, io_gr, (vir_bytes)0, (vir_bytes)path, pathlen);
pathlen);
if (r != OK) return(r); if (r != OK) return(r);
canon_path[pathlen] = '\0'; path[pathlen] = '\0';
/* Turn path into canonical path to the socket file */ /* If requested, turn path into canonical path to the socket file */
if ((r = canonical_path(canon_path, rfp)) != OK) return(r); if (what & SPATH_CANONIZE) {
if (strlen(canon_path) >= pathlen) return(ENAMETOOLONG); if ((r = canonical_path(path, rfp)) != OK) return(r);
if (strlen(path) >= pathlen) return(ENAMETOOLONG);
/* copy canon_path back to the caller */ /* copy path back to the caller */
r = sys_safecopyto(who_e, (cp_grant_id_t) io_gr, (vir_bytes) 0, r = sys_safecopyto(who_e, (cp_grant_id_t)io_gr, (vir_bytes)0,
(vir_bytes) canon_path, pathlen); (vir_bytes)path, pathlen);
if (r != OK) return(r); if (r != OK) return(r);
}
/* Now do permissions checking */ /* Now perform the requested action. For the SPATH_CHECK action, a socket
lookup_init(&resolve, canon_path, PATH_NOFLAGS, &vmp, &vp); * file is expected to exist already, and we should check whether the given
resolve.l_vmnt_lock = VMNT_READ; * user process has access to it. For the SPATH_CREATE action, no file is
resolve.l_vnode_lock = VNODE_READ; * expected to exist yet, and a socket file should be created on behalf of
if ((vp = eat_path(&resolve, rfp)) == NULL) return(err_code); * the user process. In both cases, on success, return the socket file's
* device and inode numbers to the caller.
*
* Since the above canonicalization releases all locks once done, we need to
* recheck absolutely everything now. TODO: do not release locks in between.
*/
switch (what & ~SPATH_CANONIZE) {
case SPATH_CHECK:
lookup_init(&resolve, path, PATH_NOFLAGS, &vmp, &vp);
resolve.l_vmnt_lock = VMNT_READ;
resolve.l_vnode_lock = VNODE_READ;
if ((vp = eat_path(&resolve, rfp)) == NULL) return(err_code);
/* check permissions */ /* Check file type and permissions. */
r = forbidden(rfp, vp, (R_BIT | W_BIT)); if (!S_ISSOCK(vp->v_mode))
r = ENOTSOCK; /* not in POSIX spec; this is what NetBSD does */
else
r = forbidden(rfp, vp, R_BIT | W_BIT);
unlock_vnode(vp); if (r == OK) {
unlock_vmnt(vmp); job_m_out.m_vfs_lsys_socketpath.device = vp->v_dev;
job_m_out.m_vfs_lsys_socketpath.inode = vp->v_inode_nr;
}
unlock_vnode(vp);
unlock_vmnt(vmp);
put_vnode(vp);
break;
case SPATH_CREATE:
/* This is effectively simulating a mknod(2) call by the user process,
* including the application of its umask to the file permissions.
*/
lookup_init(&resolve, path, PATH_RET_SYMLINK, &vmp, &dirp);
resolve.l_vmnt_lock = VMNT_WRITE;
resolve.l_vnode_lock = VNODE_WRITE;
if ((dirp = last_dir(&resolve, rfp)) == NULL) return(err_code);
bits = S_IFSOCK | (ACCESSPERMS & rfp->fp_umask);
if (!S_ISDIR(dirp->v_mode))
r = ENOTDIR;
else if ((r = forbidden(rfp, dirp, W_BIT | X_BIT)) == OK) {
r = req_mknod(dirp->v_fs_e, dirp->v_inode_nr, path,
rfp->fp_effuid, rfp->fp_effgid, bits, NO_DEV);
if (r == OK) {
/* Now we need to find out the device and inode number
* of the socket file we just created. The vmnt lock
* should prevent any trouble here.
*/
lookup_init(&resolve2, resolve.l_path,
PATH_RET_SYMLINK, &vmp2, &vp);
resolve2.l_vmnt_lock = VMNT_READ;
resolve2.l_vnode_lock = VNODE_READ;
vp = advance(dirp, &resolve2, rfp);
assert(vmp2 == NULL);
if (vp != NULL) {
job_m_out.m_vfs_lsys_socketpath.device =
vp->v_dev;
job_m_out.m_vfs_lsys_socketpath.inode =
vp->v_inode_nr;
unlock_vnode(vp);
put_vnode(vp);
} else {
/* Huh. This should never happen. If it does,
* we assume the socket file has somehow been
* lost, so we do not try to unlink it.
*/
printf("VFS: socketpath did not find created "
"node at %s (%d)\n", path, err_code);
r = err_code;
}
} else if (r == EEXIST)
r = EADDRINUSE;
}
unlock_vnode(dirp);
unlock_vmnt(vmp);
put_vnode(dirp);
break;
default:
r = ENOSYS;
}
put_vnode(vp);
return(r); return(r);
} }
/*===========================================================================*
* do_checkperms *
*===========================================================================*/
int do_checkperms(void)
{
/* This should be replaced by an ACL check. */
if (!super_user) return EPERM;
return check_perms(job_m_in.m_lsys_vfs_checkperms.endpt,
job_m_in.m_lsys_vfs_checkperms.grant,
job_m_in.m_lsys_vfs_checkperms.count);
}

View File

@ -170,7 +170,7 @@ void lookup_init(struct lookup *resolve, char *path, int flags, struct
vmnt **vmp, struct vnode **vp); vmnt **vmp, struct vnode **vp);
int get_name(struct vnode *dirp, struct vnode *entry, char *_name); int get_name(struct vnode *dirp, struct vnode *entry, char *_name);
int canonical_path(char *orig_path, struct fproc *rfp); int canonical_path(char *orig_path, struct fproc *rfp);
int do_checkperms(void); int do_socketpath(void);
/* pipe.c */ /* pipe.c */
int do_pipe2(void); int do_pipe2(void);

View File

@ -62,7 +62,7 @@ int (* const call_vec[NR_VFS_CALLS])(void) = {
CALL(VFS_GCOV_FLUSH) = do_gcov_flush, /* gcov_flush(2) */ CALL(VFS_GCOV_FLUSH) = do_gcov_flush, /* gcov_flush(2) */
CALL(VFS_MAPDRIVER) = do_mapdriver, /* mapdriver(2) */ CALL(VFS_MAPDRIVER) = do_mapdriver, /* mapdriver(2) */
CALL(VFS_COPYFD) = do_copyfd, /* copyfd(2) */ CALL(VFS_COPYFD) = do_copyfd, /* copyfd(2) */
CALL(VFS_CHECKPERMS) = do_checkperms, /* checkperms(2) */ CALL(VFS_SOCKETPATH) = do_socketpath, /* socketpath(2) */
CALL(VFS_GETSYSINFO) = do_getsysinfo, /* getsysinfo(2) */ CALL(VFS_GETSYSINFO) = do_getsysinfo, /* getsysinfo(2) */
CALL(VFS_SOCKET) = do_socket, /* socket(2) */ CALL(VFS_SOCKET) = do_socket, /* socket(2) */
CALL(VFS_SOCKETPAIR) = do_socketpair, /* socketpair(2) */ CALL(VFS_SOCKETPAIR) = do_socketpair, /* socketpair(2) */

View File

@ -1390,14 +1390,197 @@ static void
test_file(void) test_file(void)
{ {
struct sockaddr_un addr; struct sockaddr_un addr;
int sd; #if NOT_YET
struct sockaddr_un saddr, saddr2;
char buf[1];
socklen_t len;
struct stat st;
mode_t omask;
int, csd, fd;
#endif
int sd, sd2;
/*
* If the provided socket path exists on the file system, the bind(2)
* call must fail, regardless of whether there is a socket that is
* bound to that address.
*/
UNLINK(TEST_SUN_PATH); UNLINK(TEST_SUN_PATH);
memset(&addr, 0, sizeof(addr)); memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX; addr.sun_family = AF_UNIX;
strlcpy(addr.sun_path, TEST_SUN_PATH, sizeof(addr.sun_path)); strlcpy(addr.sun_path, TEST_SUN_PATH, sizeof(addr.sun_path));
if ((sd = socket(PF_UNIX, SOCK_DGRAM, 0)) == -1)
test_fail("Can't open socket");
if (bind(sd, (struct sockaddr *)&addr, sizeof(addr)) != 0)
test_fail("Can't bind socket");
if ((sd2 = socket(PF_UNIX, SOCK_DGRAM, 0)) == -1)
test_fail("Can't open socket");
if (bind(sd2, (struct sockaddr *)&addr, sizeof(addr)) != -1)
test_fail("Binding socket unexpectedly succeeded");
if (errno != EADDRINUSE)
test_fail("Binding socket failed with wrong error");
CLOSE(sd);
#if NOT_YET
if (bind(sd2, (struct sockaddr *)&addr, sizeof(addr)) != -1)
test_fail("Binding socket unexpectedly succeeded");
if (errno != EADDRINUSE)
test_fail("Binding socket failed with wrong error");
CLOSE(sd2);
UNLINK(TEST_SUN_PATH);
/*
* If the socket is removed from the file system while there is still a
* socket bound to it, it should be possible to bind a new socket to
* the address. The old socket should then become unreachable in terms
* of connections and data directed to the address, even though it
* should still appear to be bound to the same address.
*/
if ((sd = socket(PF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0)) == -1)
test_fail("Can't open socket");
if (bind(sd, (struct sockaddr *)&addr, sizeof(addr)) != 0)
test_fail("Can't bind socket");
memset(&saddr, 0, sizeof(saddr));
len = sizeof(saddr);
if (getsockname(sd, (struct sockaddr *)&saddr, &len) != 0)
test_fail("Can't get socket address");
if ((csd = socket(PF_UNIX, SOCK_DGRAM, 0)) == -1)
test_fail("Can't open client socket");
memset(buf, 'X', sizeof(buf));
if (sendto(csd, buf, sizeof(buf), 0, (struct sockaddr *)&addr,
sizeof(addr)) != sizeof(buf))
test_fail("Can't send to socket");
if (recvfrom(sd, buf, sizeof(buf), 0, NULL, 0) != sizeof(buf))
test_fail("Can't receive from socket");
if (buf[0] != 'X')
test_fail("Transmission failure");
if (unlink(TEST_SUN_PATH) != 0)
test_fail("Can't unlink socket");
if ((sd2 = socket(PF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0)) == -1)
test_fail("Can't open socket");
if (bind(sd2, (struct sockaddr *)&addr, sizeof(addr)) != 0)
test_fail("Can't bind socket");
memset(&saddr2, 0, sizeof(saddr2));
len = sizeof(saddr2);
if (getsockname(sd2, (struct sockaddr *)&saddr2, &len) != 0)
test_fail("Can't get socket address");
if (memcmp(&saddr, &saddr2, sizeof(saddr)))
test_fail("Unexpected socket address");
memset(buf, 'Y', sizeof(buf));
if (sendto(csd, buf, sizeof(buf), 0, (struct sockaddr *)&addr,
sizeof(addr)) != sizeof(buf))
test_fail("Can't send to socket");
if (recvfrom(sd, buf, sizeof(buf), 0, NULL, 0) != -1)
test_fail("Unexpectedly received from old socket");
if (errno != EWOULDBLOCK)
test_fail("Wrong receive failure from old socket");
if (recvfrom(sd2, buf, sizeof(buf), 0, NULL, 0) != sizeof(buf))
test_fail("Can't receive from new socket");
if (buf[0] != 'Y')
test_fail("Transmission failure");
len = sizeof(saddr2);
if (getsockname(sd, (struct sockaddr *)&saddr2, &len) != 0)
test_fail("Can't get old socket address");
if (memcmp(&saddr, &saddr2, sizeof(saddr)))
test_fail("Unexpected old socket address");
/*
* Currently, our implementation "hides" the old socket even if the new
* socket is closed, but since this is not standard behavior and may be
* changed later, we do not test for it. However, in any case,
* rebinding the hidden socket should make it "visible" again.
*/
strlcpy(saddr2.sun_path, TEST_SUN_PATHB, sizeof(saddr2.sun_path));
if (bind(sd, (struct sockaddr *)&saddr2, sizeof(saddr2)) != 0)
test_fail("Can't rebind socket");
memset(buf, 'Z', sizeof(buf));
if (sendto(csd, buf, sizeof(buf), 0, (struct sockaddr *)&saddr2,
sizeof(saddr2)) != sizeof(buf))
test_fail("Can't send to socket");
if (recvfrom(sd, buf, sizeof(buf), 0, NULL, 0) != sizeof(buf))
test_fail("Can't receive from socket");
if (buf[0] != 'Z')
test_fail("Transmission failure");
if (unlink(TEST_SUN_PATH) != 0)
test_fail("Can't unlink socket");
if (unlink(TEST_SUN_PATHB) != 0)
test_fail("Can't unlink other socket");
CLOSE(sd);
CLOSE(sd2);
CLOSE(csd);
/*
* If the socket path identifies a file that is not a socket, bind(2)
* should still fail with EADDRINUSE, but connect(2) and sendto(2)
* should fail with ENOTSOCK (the latter is not specified by POSIX, so
* we follow NetBSD here).
*/
if ((fd = open(TEST_SUN_PATH, O_WRONLY | O_CREAT | O_EXCL, 0700)) < 0)
test_fail("Can't create regular file");
CLOSE(fd);
if ((sd = socket(PF_UNIX, SOCK_DGRAM, 0)) == -1)
test_fail("Can't open socket");
if (bind(sd, (struct sockaddr *)&addr, sizeof(addr)) != -1)
test_fail("Binding socket unexpectedly succeeded");
if (errno != EADDRINUSE)
test_fail("Binding socket failed with wrong error");
if (sendto(sd, buf, sizeof(buf), 0, (struct sockaddr *)&addr,
sizeof(addr)) != -1)
test_fail("Sending to socket unexpectedly succeeded");
if (errno != ENOTSOCK)
test_fail("Sending to socket failed with wrong error");
CLOSE(sd);
if ((sd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1)
test_fail("Can't open socket");
if (connect(sd, (struct sockaddr *)&addr, sizeof(addr)) != -1)
test_fail("Connecting socket unexpectedly succeeded");
if (errno != ENOTSOCK)
test_fail("Connecting socket failed with wrong error");
CLOSE(sd);
UNLINK(TEST_SUN_PATH);
/*
* The created socket file should have an access mode of 0777 corrected
* with the calling process's file mode creation mask.
*/
omask = umask(045123);
if ((sd = socket(PF_UNIX, SOCK_DGRAM, 0)) == -1)
test_fail("Can't open socket");
if (bind(sd, (struct sockaddr *)&addr, sizeof(addr)) != 0)
test_fail("Can't bind socket");
if (stat(TEST_SUN_PATH, &st) != 0)
test_fail("Can't stat socket");
if (!S_ISSOCK(st.st_mode))
test_fail("File is not a socket");
if ((st.st_mode & ALLPERMS) != (ACCESSPERMS & ~0123))
test_fail("Unexpected file permission mask");
CLOSE(sd);
UNLINK(TEST_SUN_PATH);
umask(omask);
#endif
/* /*
* Only socket(2), socketpair(2), and accept(2) may be used to obtain * Only socket(2), socketpair(2), and accept(2) may be used to obtain
* new file descriptors to sockets (or "sockets"); open(2) on a socket * new file descriptors to sockets (or "sockets"); open(2) on a socket