- Add support for file descriptor passing to PFS.

- For security reasons move some libc code to PFS.
- Fix a few bugs in PFS.
Contributed by Thomas Cort.
This commit is contained in:
Thomas Veerman 2010-08-30 13:46:44 +00:00
parent 13ef7f1f38
commit e8ddc0f46e
10 changed files with 808 additions and 367 deletions

View file

@ -207,14 +207,7 @@ static int in_group(uid_t uid, gid_t gid)
static int _uds_bind(int socket, const struct sockaddr *address,
socklen_t address_len, struct sockaddr_un *uds_addr)
{
mode_t bits, perm_bits, access_desired;
struct stat buf;
uid_t euid;
gid_t egid;
char real_sun_path[PATH_MAX+1];
char *realpath_result;
int i, r, shift;
int null_found;
int r;
int did_mknod;
if (address == NULL) {
@ -222,120 +215,14 @@ static int _uds_bind(int socket, const struct sockaddr *address,
return -1;
}
/* sun_family is always supposed to be AF_UNIX */
if (((struct sockaddr_un *) address)->sun_family != AF_UNIX) {
errno = EAFNOSUPPORT;
return -1;
}
/* an empty path is not supported */
if (((struct sockaddr_un *) address)->sun_path[0] == '\0') {
errno = ENOENT;
return -1;
}
/* the path must be a null terminated string for realpath to work */
for (null_found = i = 0;
i < sizeof(((struct sockaddr_un *) address)->sun_path); i++) {
if (((struct sockaddr_un *) address)->sun_path[i] == '\0') {
null_found = 1;
break;
}
}
if (!null_found) {
errno = EINVAL;
return -1;
}
/*
* Get the realpath(3) of the socket file.
*/
realpath_result = realpath(((struct sockaddr_un *) address)->sun_path,
real_sun_path);
if (realpath_result == NULL) {
return -1;
}
if (strlen(real_sun_path) >= UNIX_PATH_MAX) {
errno = ENAMETOOLONG;
return -1;
}
strcpy(((struct sockaddr_un *) address)->sun_path, real_sun_path);
/*
* input parameters look good -- create the socket file on the
* file system
*/
did_mknod = 0;
r = mknod(((struct sockaddr_un *) address)->sun_path,
S_IFSOCK|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH, 0);
if (r == -1) {
if (errno == EEXIST) {
/* file already exists, verify that it is a socket */
r = stat(((struct sockaddr_un *) address)->sun_path,
&buf);
if (r == -1) {
return -1;
}
if (!S_ISSOCK(buf.st_mode)) {
errno = EADDRINUSE;
return -1;
}
/* check permissions the permissions of the
* socket file.
*/
/* read + write access */
access_desired = R_BIT | W_BIT;
euid = geteuid();
egid = getegid();
if (euid == -1 || egid == -1) {
errno = EACCES;
return -1;
}
bits = buf.st_mode;
if (euid == ((uid_t) 0)) {
perm_bits = R_BIT | W_BIT;
} else {
if (euid == buf.st_uid) {
shift = 6; /* owner */
} else if (egid == buf.st_gid) {
shift = 3; /* group */
} else if (in_group(euid, buf.st_gid)) {
shift = 3; /* suppl. groups */
} else {
shift = 0; /* other */
}
perm_bits =
(bits >> shift) & (R_BIT | W_BIT | X_BIT);
}
if ((perm_bits | access_desired) != perm_bits) {
errno = EACCES;
return -1;
}
/* if we get here permissions are OK */
} else {
return -1;
}
} else {
if (r == -1 && errno != EEXIST) {
return -1;
} else if (r == 0) {
did_mknod = 1;
}

View file

@ -160,147 +160,15 @@ static int _udp_connect(int socket, const struct sockaddr *address,
return r;
}
static int in_group(uid_t uid, gid_t gid)
{
int r, i;
int size;
gid_t *list;
size = sysconf(_SC_NGROUPS_MAX);
list = malloc(size * sizeof(gid_t));
if (list == NULL) {
return 0;
}
r= getgroups(size, list);
if (r == -1) {
free(list);
return 0;
}
for (i = 0; i < r; i++) {
if (gid == list[i]) {
free(list);
return 1;
}
}
free(list);
return 0;
}
static int _uds_connect(int socket, const struct sockaddr *address,
socklen_t address_len)
{
mode_t bits, perm_bits, access_desired;
struct stat buf;
uid_t euid;
gid_t egid;
char real_sun_path[PATH_MAX+1];
char *realpath_result;
int i, r, shift;
int null_found;
if (address == NULL) {
errno = EFAULT;
return -1;
}
/* sun_family is always supposed to be AF_UNIX */
if (((struct sockaddr_un *) address)->sun_family != AF_UNIX) {
errno = EAFNOSUPPORT;
return -1;
}
/* an empty path is not supported */
if (((struct sockaddr_un *) address)->sun_path[0] == '\0') {
errno = EINVAL;
return -1;
}
/* the path must be a null terminated string for realpath to work */
for (null_found = i = 0;
i < sizeof(((struct sockaddr_un *) address)->sun_path); i++) {
if (((struct sockaddr_un *) address)->sun_path[i] == '\0') {
null_found = 1;
break;
}
}
if (!null_found) {
errno = EINVAL;
return -1;
}
/*
* Get the realpath(3) of the socket file.
*/
realpath_result = realpath(((struct sockaddr_un *) address)->sun_path,
real_sun_path);
if (realpath_result == NULL) {
return -1;
}
if (strlen(real_sun_path) >= UNIX_PATH_MAX) {
errno = ENAMETOOLONG;
return -1;
}
strcpy(((struct sockaddr_un *) address)->sun_path, real_sun_path);
/*
* input parameters look good -- check the permissions of the
* socket file. emulate eaccess() (i.e. the access(2) function
* with effective UID/GID).
*/
access_desired = R_BIT | W_BIT; /* read + write access */
euid = geteuid();
egid = getegid();
if (euid == -1 || egid == -1) {
errno = EACCES;
return -1;
}
r= stat(((struct sockaddr_un *) address)->sun_path, &buf);
if (r == -1) {
return -1;
}
if (!S_ISSOCK(buf.st_mode)) {
errno = EINVAL;
return -1;
}
bits = buf.st_mode;
if (euid == ((uid_t) 0)) {
perm_bits = R_BIT | W_BIT;
} else {
if (euid == buf.st_uid) {
shift = 6; /* owner */
} else if (egid == buf.st_gid) {
shift = 3; /* group */
} else if (in_group(euid, buf.st_gid)) {
shift = 3; /* suppl. groups */
} else {
shift = 0; /* other */
}
perm_bits = (bits >> shift) & (R_BIT | W_BIT | X_BIT);
}
if ((perm_bits | access_desired) != perm_bits) {
errno = EACCES;
return -1;
}
/* perform the connect */
r= ioctl(socket, NWIOSUDSCONN, (void *) address);
return r;
return ioctl(socket, NWIOSUDSCONN, (void *) address);
}

View file

@ -23,8 +23,7 @@ ssize_t recvmsg(int socket, struct msghdr *msg, int flags)
}
r= ioctl(socket, NWIOGUDSSOTYPE, &uds_sotype);
if (r != -1 || (errno != ENOTTY && errno != EBADIOCTL))
{
if (r != -1 || (errno != ENOTTY && errno != EBADIOCTL)) {
if (r == -1) {
return r;
}
@ -46,7 +45,7 @@ ssize_t recvmsg(int socket, struct msghdr *msg, int flags)
static ssize_t _uds_recvmsg_conn(int socket, struct msghdr *msg, int flags)
{
int r;
int r, rc;
if (flags != 0) {
#if DEBUG
@ -56,13 +55,30 @@ static ssize_t _uds_recvmsg_conn(int socket, struct msghdr *msg, int flags)
return -1;
}
r = readv(socket, msg->msg_iov, msg->msg_iovlen);
r= readv(socket, msg->msg_iov, msg->msg_iovlen);
if (r >= 0 && msg->msg_name && msg->msg_namelen > 0)
{
if (r >= 0 && msg->msg_name && msg->msg_namelen > 0) {
getpeername(socket, msg->msg_name, &msg->msg_namelen);
}
/* get control data */
if (r >= 0 && msg->msg_control && msg->msg_controllen > 0) {
struct msg_control msg_ctrl;
memset(&msg_ctrl, '\0', sizeof(struct msg_control));
msg_ctrl.msg_controllen = msg->msg_controllen;
rc = ioctl(socket, NWIOGUDSCTRL, &msg_ctrl);
if (rc == -1) {
return rc;
}
if (msg_ctrl.msg_controllen <= msg->msg_controllen) {
memcpy(msg->msg_control, msg_ctrl.msg_control,
msg_ctrl.msg_controllen);
msg->msg_controllen = msg_ctrl.msg_controllen;
}
}
msg->msg_flags = 0;
return r;
@ -70,7 +86,7 @@ static ssize_t _uds_recvmsg_conn(int socket, struct msghdr *msg, int flags)
static ssize_t _uds_recvmsg_dgram(int socket, struct msghdr *msg, int flags)
{
int r;
int r, rc;
if (flags != 0) {
#if DEBUG
@ -80,13 +96,34 @@ static ssize_t _uds_recvmsg_dgram(int socket, struct msghdr *msg, int flags)
return -1;
}
r = readv(socket, msg->msg_iov, msg->msg_iovlen);
r= readv(socket, msg->msg_iov, msg->msg_iovlen);
if (r >= 0 && msg->msg_name && msg->msg_namelen > 0)
if (r >= 0 && msg->msg_name &&
msg->msg_namelen >= sizeof(struct sockaddr_un))
{
ioctl(socket, NWIOGUDSFADDR, msg->msg_name);
rc= ioctl(socket, NWIOGUDSFADDR, msg->msg_name);
if (rc == -1) {
return rc;
}
msg->msg_namelen= sizeof(struct sockaddr_un);
}
/* get control data */
if (r >= 0 && msg->msg_control && msg->msg_controllen > 0) {
struct msg_control msg_ctrl;
memset(&msg_ctrl, '\0', sizeof(struct msg_control));
msg_ctrl.msg_controllen = msg->msg_controllen;
rc = ioctl(socket, NWIOGUDSCTRL, &msg_ctrl);
if (rc == -1) {
return rc;
}
if (msg_ctrl.msg_controllen <= msg->msg_controllen) {
memcpy(msg->msg_control, msg_ctrl.msg_control,
msg_ctrl.msg_controllen);
msg->msg_controllen = msg_ctrl.msg_controllen;
}
}
msg->msg_flags = 0;

View file

@ -26,8 +26,7 @@ ssize_t sendmsg(int socket, const struct msghdr *msg, int flags)
}
r= ioctl(socket, NWIOGUDSSOTYPE, &uds_sotype);
if (r != -1 || (errno != ENOTTY && errno != EBADIOCTL))
{
if (r != -1 || (errno != ENOTTY && errno != EBADIOCTL)) {
if (r == -1) {
return r;
}
@ -51,6 +50,8 @@ ssize_t sendmsg(int socket, const struct msghdr *msg, int flags)
static ssize_t _uds_sendmsg_conn(int socket, const struct msghdr *msg,
int flags)
{
struct msg_control msg_ctrl;
int r;
if (flags != 0) {
#if DEBUG
@ -61,6 +62,23 @@ static ssize_t _uds_sendmsg_conn(int socket, const struct msghdr *msg,
}
/* grab the control data */
memset(&msg_ctrl, '\0', sizeof(struct msg_control));
if (msg->msg_controllen > MSG_CONTROL_MAX) {
errno = ENOMEM;
return -1;
} else if (msg->msg_controllen > 0) {
memcpy(&msg_ctrl.msg_control, msg->msg_control,
msg->msg_controllen);
}
msg_ctrl.msg_controllen = msg->msg_controllen;
/* send the control data to PFS */
r= ioctl(socket, NWIOSUDSCTRL, (void *) &msg_ctrl);
if (r == -1) {
return r;
}
/* Silently ignore destination, if given. */
return writev(socket, msg->msg_iov, msg->msg_iovlen);
@ -69,10 +87,8 @@ static ssize_t _uds_sendmsg_conn(int socket, const struct msghdr *msg,
static ssize_t _uds_sendmsg_dgram(int socket, const struct msghdr *msg,
int flags)
{
char real_sun_path[PATH_MAX+1];
char *realpath_result;
char *dest_addr;
int null_found;
struct msg_control msg_ctrl;
struct sockaddr_un *dest_addr;
int i, r;
if (flags != 0) {
@ -90,50 +106,29 @@ static ssize_t _uds_sendmsg_dgram(int socket, const struct msghdr *msg,
return -1;
}
/* sun_family is always supposed to be AF_UNIX */
if (((struct sockaddr_un *) dest_addr)->sun_family != AF_UNIX) {
errno = EAFNOSUPPORT;
return -1;
}
/* an empty path is not supported */
if (((struct sockaddr_un *) dest_addr)->sun_path[0] == '\0') {
errno = ENOENT;
return -1;
}
/* the path must be a null terminated string for realpath to work */
for (null_found = i = 0;
i < sizeof(((struct sockaddr_un *) dest_addr)->sun_path); i++) {
if (((struct sockaddr_un *) dest_addr)->sun_path[i] == '\0') {
null_found = 1;
break;
}
}
if (!null_found) {
errno = EINVAL;
return -1;
}
realpath_result = realpath(
((struct sockaddr_un *) dest_addr)->sun_path, real_sun_path);
if (realpath_result == NULL) {
return -1;
}
if (strlen(real_sun_path) >= UNIX_PATH_MAX) {
errno = ENAMETOOLONG;
return -1;
}
/* set the target address */
r= ioctl(socket, NWIOSUDSTADDR, (void *) dest_addr);
if (r == -1) {
return r;
}
/* grab the control data */
memset(&msg_ctrl, '\0', sizeof(struct msg_control));
if (msg->msg_controllen > MSG_CONTROL_MAX) {
errno = ENOMEM;
return -1;
} else if (msg->msg_controllen > 0) {
memcpy(&msg_ctrl.msg_control, msg->msg_control,
msg->msg_controllen);
}
msg_ctrl.msg_controllen = msg->msg_controllen;
/* send the control data to PFS */
r= ioctl(socket, NWIOSUDSCTRL, (void *) &msg_ctrl);
if (r == -1) {
return r;
}
/* do the send */
return writev(socket, msg->msg_iov, msg->msg_iovlen);
}

View file

@ -206,10 +206,7 @@ static ssize_t _uds_sendto_conn(int socket, const void *message, size_t length,
static ssize_t _uds_sendto_dgram(int socket, const void *message, size_t length,
int flags, const struct sockaddr *dest_addr, socklen_t dest_len)
{
char real_sun_path[PATH_MAX+1];
char *realpath_result;
int null_found;
int i, r;
int r;
/* for connectionless unix domain sockets (SOCK_DGRAM) */
@ -226,46 +223,6 @@ static ssize_t _uds_sendto_dgram(int socket, const void *message, size_t length,
return -1;
}
/* sun_family is always supposed to be AF_UNIX */
if (((struct sockaddr_un *) dest_addr)->sun_family != AF_UNIX) {
errno = EAFNOSUPPORT;
return -1;
}
/* an empty path is not supported */
if (((struct sockaddr_un *) dest_addr)->sun_path[0] == '\0') {
errno = ENOENT;
return -1;
}
/* the path must be a null terminated string for realpath to work */
for (null_found = i = 0;
i < sizeof(((struct sockaddr_un *) dest_addr)->sun_path); i++) {
if (((struct sockaddr_un *) dest_addr)->sun_path[i] == '\0') {
null_found = 1;
break;
}
}
if (!null_found) {
errno = EINVAL;
return -1;
}
realpath_result = realpath(
((struct sockaddr_un *) dest_addr)->sun_path, real_sun_path);
if (realpath_result == NULL) {
return -1;
}
if (strlen(real_sun_path) >= UNIX_PATH_MAX) {
errno = ENAMETOOLONG;
return -1;
}
strcpy(((struct sockaddr_un *) dest_addr)->sun_path, real_sun_path);
/* set the target address */
r= ioctl(socket, NWIOSUDSTADDR, (void *) dest_addr);
if (r == -1) {

View file

@ -55,12 +55,13 @@ PUBLIC int uds_open(message *dev_m_in, message *dev_m_out)
* Find a slot in the descriptor table for the new descriptor.
* The index of the descriptor in the table will be returned.
* Subsequent calls to read/write/close/ioctl/etc will use this
* minor number.
* minor number. The minor number must be different from the
* the /dev/uds device's minor number (currently 0).
*/
minor = -1; /* to trap error */
for (i = 0; i < NR_FDS; i++) {
for (i = 1; i < NR_FDS; i++) {
if (uds_fd_table[i].state == UDS_FREE) {
minor = i;
break;
@ -122,6 +123,12 @@ PUBLIC int uds_open(message *dev_m_in, message *dev_m_out)
uds_fd_table[minor].backlog[i] = -1;
}
memset(&uds_fd_table[minor].ancillary_data, '\0', sizeof(struct
ancillary));
for (i = 0; i < OPEN_MAX; i++) {
uds_fd_table[minor].ancillary_data.fds[i] = -1;
}
/* default the size to UDS_SOMAXCONN */
uds_fd_table[minor].backlog_size = UDS_SOMAXCONN;
@ -138,6 +145,8 @@ PUBLIC int uds_open(message *dev_m_in, message *dev_m_out)
/* initially the socket is not bound or listening on an address */
memset(&(uds_fd_table[minor].addr), '\0', sizeof(struct sockaddr_un));
memset(&(uds_fd_table[minor].source), '\0', sizeof(struct sockaddr_un));
memset(&(uds_fd_table[minor].target), '\0', sizeof(struct sockaddr_un));
/* Initially the socket isn't suspended. */
uds_fd_table[minor].suspended = UDS_NOT_SUSPENDED;
@ -245,6 +254,10 @@ PUBLIC int uds_close(message *dev_m_in, message *dev_m_out)
}
}
if (uds_fd_table[minor].ancillary_data.nfiledes > 0) {
clear_fds(minor, &(uds_fd_table[minor].ancillary_data));
}
/* Prepare Request to the FS side of PFS */
fs_m_in.m_type = REQ_PUTNODE;
@ -325,8 +338,8 @@ PUBLIC int uds_select(message *dev_m_in, message *dev_m_out)
}
}
/* check if we can write at least 1 byte */
bytes = uds_perform_write(minor, dev_m_in->m_source, 1, 1);
/* check if we can write without blocking */
bytes = uds_perform_write(minor, dev_m_in->m_source, PIPE_BUF, 1);
if (bytes > 0) {
uds_fd_table[minor].sel_ops_out |= SEL_WR;
}
@ -547,6 +560,11 @@ PRIVATE int uds_perform_write(int minor, endpoint_t m_source,
break;
}
}
if (peer == -1) {
errno = ENOENT;
return -1;
}
}
/* check if write would overrun buffer. check if message
@ -651,7 +669,8 @@ PUBLIC int uds_read(message *dev_m_in, message *dev_m_out)
static int call_count = 0;
printf("(uds) [%d] uds_read() call_count=%d\n", uds_minor(dev_m_in),
++call_count);
printf("Endpoint: 0x%x | Position 0x%x\n", dev_m_in->IO_ENDPT, dev_m_in->POSITION);
printf("Endpoint: 0x%x | Position 0x%x\n", dev_m_in->IO_ENDPT,
dev_m_in->POSITION);
#endif
minor = uds_minor(dev_m_in);
@ -713,7 +732,8 @@ PUBLIC int uds_write(message *dev_m_in, message *dev_m_out)
static int call_count = 0;
printf("(uds) [%d] uds_write() call_count=%d\n", uds_minor(dev_m_in),
++call_count);
printf("Endpoint: 0x%x | Position 0x%x\n", dev_m_in->IO_ENDPT, dev_m_in->POSITION);
printf("Endpoint: 0x%x | Position 0x%x\n", dev_m_in->IO_ENDPT,
dev_m_in->POSITION);
#endif
minor = uds_minor(dev_m_in);
@ -775,7 +795,8 @@ PUBLIC int uds_ioctl(message *dev_m_in, message *dev_m_out)
static int call_count = 0;
printf("(uds) [%d] uds_ioctl() call_count=%d\n", uds_minor(dev_m_in),
++call_count);
printf("Endpoint: 0x%x | Position 0x%x\n", dev_m_in->IO_ENDPT, dev_m_in->POSITION);
printf("Endpoint: 0x%x | Position 0x%x\n", dev_m_in->IO_ENDPT,
dev_m_in->POSITION);
#endif
minor = uds_minor(dev_m_in);
@ -895,6 +916,16 @@ PUBLIC int uds_ioctl(message *dev_m_in, message *dev_m_out)
/* set the send buffer size -- setsockopt(SO_SNDBUF) */
return do_setsockopt_rcvbuf(dev_m_in, dev_m_out);
case NWIOSUDSCTRL:
/* set the control data -- sendmsg() */
return do_sendmsg(dev_m_in, dev_m_out);
case NWIOGUDSCTRL:
/* set the control data -- recvmsg() */
return do_recvmsg(dev_m_in, dev_m_out);
default:
/* the IOCTL command is not valid for /dev/uds --

View file

@ -8,6 +8,7 @@
#include <ansi.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/stat.h>

View file

@ -7,6 +7,7 @@
struct buf;
struct inode;
struct sockaddr_un;
struct ancillary;
/* buffer.c */
_PROTOTYPE( struct buf *get_block, (dev_t dev, ino_t inum) );
@ -90,9 +91,11 @@ _PROTOTYPE( int do_setsockopt_rcvbuf,
(message *dev_m_in, message *dev_m_out) );
_PROTOTYPE( int do_sendto, (message *dev_m_in, message *dev_m_out) );
_PROTOTYPE( int do_recvfrom, (message *dev_m_in, message *dev_m_out) );
_PROTOTYPE( int do_sendmsg, (message *dev_m_in, message *dev_m_out) );
_PROTOTYPE( int do_recvmsg, (message *dev_m_in, message *dev_m_out) );
_PROTOTYPE( int perform_connection,
(message *dev_m_in, message *dev_m_out,
struct sockaddr_un *addr, int minorx,
int minory) );
_PROTOTYPE( int clear_fds, (int minor, struct ancillary *data) );
#endif

View file

@ -23,7 +23,10 @@
* do_setsockopt_rcvbuf: handles the setsockopt(2) syscall.
* do_sendto: handles the sendto(2) syscall.
* do_recvfrom: handles the recvfrom(2) syscall.
* do_sendmsg: handles the sendmsg(2) syscall.
* do_recvmsg: handles the recvmsg(2) syscall.
* perform_connection: performs the connection of two descriptors.
* clear_fds: calls put_filp for undelivered FDs.
*
* Also see...
*
@ -50,6 +53,187 @@ PUBLIC void uds_init(void)
memset(uds_fd_table, '\0', sizeof(uds_fd_t) * NR_FDS);
}
/* check the permissions of a socket file */
PRIVATE int check_perms(int minor, struct sockaddr_un *addr)
{
int rc;
message vfs_m;
cp_grant_id_t grant_id;
grant_id = cpf_grant_direct(VFS_PROC_NR, (vir_bytes) addr->sun_path,
UNIX_PATH_MAX, CPF_READ | CPF_WRITE);
/* ask the VFS to verify the permissions */
memset(&vfs_m, '\0', sizeof(message));
vfs_m.m_type = PFS_REQ_CHECK_PERMS;
vfs_m.IO_ENDPT = uds_fd_table[minor].owner;
vfs_m.IO_GRANT = (char *) grant_id;
vfs_m.COUNT = UNIX_PATH_MAX;
rc = sendrec(VFS_PROC_NR, &vfs_m);
cpf_revoke(grant_id);
if (OK != rc) {
printf("(uds) sendrec error... req_nr: %d err: %d\n",
vfs_m.m_type, rc);
return EIO;
}
#if DEBUG == 1
printf("(uds) VFS reply => %d\n", vfs_m.m_type);
printf("(uds) Canonical Path => %s\n", addr->sun_path);
#endif
return vfs_m.m_type; /* return reply code OK, ELOOP, etc. */
}
PRIVATE filp_id_t verify_fd(endpoint_t ep, int fd)
{
int rc;
message vfs_m;
#if DEBUG == 1
static int call_count = 0;
printf("(uds) verify_fd(%d,%d) call_count=%d\n", ep, fd,
++call_count);
#endif
memset(&vfs_m, '\0', sizeof(message));
vfs_m.m_type = PFS_REQ_VERIFY_FD;
vfs_m.IO_ENDPT = ep;
vfs_m.COUNT = fd;
rc = sendrec(VFS_PROC_NR, &vfs_m);
if (OK != rc) {
printf("(uds) sendrec error... req_nr: %d err: %d\n",
vfs_m.m_type, rc);
return NULL;;
}
#if DEBUG == 1
printf("(uds) VFS reply => %d\n", vfs_m.m_type);
#endif
return vfs_m.ADDRESS;
}
PRIVATE int set_filp(filp_id_t sfilp)
{
int rc;
message vfs_m;
#if DEBUG == 1
static int call_count = 0;
printf("(uds) set_filp(0x%x) call_count=%d\n", sfilp, ++call_count);
#endif
memset(&vfs_m, '\0', sizeof(message));
vfs_m.m_type = PFS_REQ_SET_FILP;
vfs_m.ADDRESS = sfilp;
rc = sendrec(VFS_PROC_NR, &vfs_m);
if (OK != rc) {
printf("(uds) sendrec error... req_nr: %d err: %d\n",
vfs_m.m_type, rc);
return EIO;
}
#if DEBUG == 1
printf("(uds) VFS reply => %d\n", vfs_m.m_type);
#endif
return vfs_m.m_type; /* return reply code OK, ELOOP, etc. */
}
PRIVATE int copy_filp(endpoint_t to_ep, filp_id_t cfilp)
{
int rc;
message vfs_m;
#if DEBUG == 1
static int call_count = 0;
printf("(uds) copy_filp(%d, 0x%x) call_count=%d\n",to_ep, cfilp,
++call_count);
#endif
memset(&vfs_m, '\0', sizeof(message));
vfs_m.m_type = PFS_REQ_COPY_FILP;
vfs_m.IO_ENDPT = to_ep;
vfs_m.ADDRESS = cfilp;
rc = sendrec(VFS_PROC_NR, &vfs_m);
if (OK != rc) {
printf("(uds) sendrec error... req_nr: %d err: %d\n",
vfs_m.m_type, rc);
return EIO;
}
#if DEBUG == 1
printf("(uds) VFS reply => %d\n", vfs_m.m_type);
#endif
return vfs_m.m_type;
}
PRIVATE int put_filp(filp_id_t pfilp)
{
int rc;
message vfs_m;
#if DEBUG == 1
static int call_count = 0;
printf("(uds) put_filp(0x%x) call_count=%d\n", pfilp, ++call_count);
#endif
memset(&vfs_m, '\0', sizeof(message));
vfs_m.m_type = PFS_REQ_PUT_FILP;
vfs_m.ADDRESS = pfilp;
rc = sendrec(VFS_PROC_NR, &vfs_m);
if (OK != rc) {
printf("(uds) sendrec error... req_nr: %d err: %d\n",
vfs_m.m_type, rc);
return EIO;
}
#if DEBUG == 1
printf("(uds) VFS reply => %d\n", vfs_m.m_type);
#endif
return vfs_m.m_type; /* return reply code OK, ELOOP, etc. */
}
PRIVATE int cancel_fd(endpoint_t ep, int fd)
{
int rc;
message vfs_m;
#if DEBUG == 1
static int call_count = 0;
printf("(uds) cancel_fd(%d,%d) call_count=%d\n", ep, fd, ++call_count);
#endif
memset(&vfs_m, '\0', sizeof(message));
vfs_m.m_type = PFS_REQ_CANCEL_FD;
vfs_m.IO_ENDPT = ep;
vfs_m.COUNT = fd;
rc = sendrec(VFS_PROC_NR, &vfs_m);
if (OK != rc) {
printf("(uds) sendrec error... req_nr: %d err: %d\n",
vfs_m.m_type, rc);
return EIO;
}
#if DEBUG == 1
printf("(uds) VFS reply => %d\n", vfs_m.m_type);
#endif
return vfs_m.m_type; /* return reply code OK, ELOOP, etc. */
}
PUBLIC int perform_connection(message *dev_m_in, message *dev_m_out,
struct sockaddr_un *addr, int minorx, int minory)
{
@ -152,7 +336,8 @@ PUBLIC int do_accept(message *dev_m_in, message *dev_m_out)
if (uds_fd_table[i].addr.sun_family == AF_UNIX &&
!strncmp(addr.sun_path,
uds_fd_table[i].addr.sun_path,
UNIX_PATH_MAX)) {
UNIX_PATH_MAX) &&
uds_fd_table[i].listening == 1) {
rc = 0;
break;
@ -233,7 +418,8 @@ PUBLIC int do_accept(message *dev_m_in, message *dev_m_out)
/* if peer is blocked on connect() revive peer */
if (uds_fd_table[minorpeer].suspended) {
#if DEBUG == 1
printf("(uds) [%d] {do_accept} revive %d", minor, minorpeer);
printf("(uds) [%d] {do_accept} revive %d\n", minor,
minorpeer);
#endif
uds_fd_table[minorpeer].ready_to_revive = 1;
notify(dev_m_in->m_source);
@ -298,6 +484,18 @@ PUBLIC int do_connect(message *dev_m_in, message *dev_m_out)
return EIO;
}
rc = check_perms(minor, &addr);
if (rc != OK) {
/* permission denied, socket file doesn't exist, etc. */
uds_fd_table[minor].syscall_done = 1;
uds_set_reply(dev_m_out, TASK_REPLY, dev_m_in->IO_ENDPT,
(cp_grant_id_t) dev_m_in->IO_GRANT, rc);
return rc;
}
/* look for a socket of the same type that is listening on the
* address we want to connect to
*/
@ -323,7 +521,7 @@ PUBLIC int do_connect(message *dev_m_in, message *dev_m_out)
uds_fd_table[i].child = -1;
#if DEBUG == 1
printf("(uds) [%d] {do_connect} revive %d", minor, i);
printf("(uds) [%d] {do_connect} revive %d\n", minor, i);
#endif
/* wake the parent (server) */
@ -432,7 +630,7 @@ PUBLIC int do_connect(message *dev_m_in, message *dev_m_out)
}
#if DEBUG == 1
printf("(uds) [%d] {do_connect} suspend", minor);
printf("(uds) [%d] {do_connect} suspend\n", minor);
#endif
/* suspend until the server side completes the connection with accept()
@ -625,7 +823,7 @@ PUBLIC int do_socket(message *dev_m_in, message *dev_m_out)
PUBLIC int do_bind(message *dev_m_in, message *dev_m_out)
{
int minor;
int minor, strlen;
struct sockaddr_un addr;
int rc, i;
@ -667,15 +865,39 @@ PUBLIC int do_bind(message *dev_m_in, message *dev_m_out)
}
/* do some basic sanity checks on the address */
if (addr.sun_family != AF_UNIX || addr.sun_path[0] == '\0') {
if (addr.sun_family != AF_UNIX) {
/* bad family */
uds_fd_table[minor].syscall_done = 1;
uds_set_reply(dev_m_out, TASK_REPLY, dev_m_in->IO_ENDPT,
(cp_grant_id_t) dev_m_in->IO_GRANT,
EAFNOSUPPORT);
return EAFNOSUPPORT;
}
if (addr.sun_path[0] == '\0') {
/* bad address */
uds_fd_table[minor].syscall_done = 1;
uds_set_reply(dev_m_out, TASK_REPLY, dev_m_in->IO_ENDPT,
(cp_grant_id_t) dev_m_in->IO_GRANT, EINVAL);
(cp_grant_id_t) dev_m_in->IO_GRANT, ENOENT);
return EINVAL;
return ENOENT;
}
rc = check_perms(minor, &addr);
if (rc != OK) {
/* permission denied, socket file doesn't exist, etc. */
uds_fd_table[minor].syscall_done = 1;
uds_set_reply(dev_m_out, TASK_REPLY, dev_m_in->IO_ENDPT,
(cp_grant_id_t) dev_m_in->IO_GRANT, rc);
return rc;
}
/* make sure the address isn't already in use by another socket. */
@ -939,8 +1161,12 @@ PUBLIC int do_socketpair(message *dev_m_in, message *dev_m_out)
minory = (minor(minorin) & BYTE);
#if DEBUG == 1
printf("socketpair() %d - %d\n", minorx, minory);
#endif
/* security check - both sockets must have the same endpoint (owner) */
if (uds_fd_table[minorx].endpoint != uds_fd_table[minory].endpoint) {
if (uds_fd_table[minorx].owner != uds_fd_table[minory].owner) {
/* we won't allow you to magically connect your socket to
* someone elses socket
@ -1307,6 +1533,17 @@ PUBLIC int do_sendto(message *dev_m_in, message *dev_m_out)
return EINVAL;
}
rc = check_perms(minor, &addr);
if (rc != OK) {
uds_fd_table[minor].syscall_done = 1;
uds_set_reply(dev_m_out, TASK_REPLY, dev_m_in->IO_ENDPT,
(cp_grant_id_t) dev_m_in->IO_GRANT, rc);
return rc;
}
memcpy(&(uds_fd_table[minor].target), &addr,
sizeof(struct sockaddr_un));
@ -1352,3 +1589,414 @@ PUBLIC int do_recvfrom(message *dev_m_in, message *dev_m_out)
return OK;
}
int msg_control_read(struct msg_control *msg_ctrl, struct ancillary *data,
int minor)
{
int rc;
struct msghdr msghdr;
struct cmsghdr *cmsg = NULL;
#if DEBUG == 1
static int call_count = 0;
printf("(uds) [%d] msg_control_read() call_count=%d\n", minor,
++call_count);
#endif
data->nfiledes = 0;
memset(&msghdr, '\0', sizeof(struct msghdr));
msghdr.msg_control = msg_ctrl->msg_control;
msghdr.msg_controllen = msg_ctrl->msg_controllen;
for(cmsg = CMSG_FIRSTHDR(&msghdr); cmsg != NULL;
cmsg = CMSG_NXTHDR(&msghdr, cmsg)) {
if (cmsg->cmsg_level == SOL_SOCKET &&
cmsg->cmsg_type == SCM_RIGHTS) {
int i;
int nfds =
MIN((cmsg->cmsg_len-CMSG_LEN(0))/sizeof(int),
OPEN_MAX);
for (i = 0; i < nfds; i++) {
if (data->nfiledes == OPEN_MAX) {
return EOVERFLOW;
}
data->fds[data->nfiledes] =
((int *) CMSG_DATA(cmsg))[i];
#if DEBUG == 1
printf("(uds) [%d] fd[%d]=%d\n", minor,
data->nfiledes, data->fds[data->nfiledes]);
#endif
data->nfiledes++;
}
}
}
/* obtain this socket's credentials */
rc = getnucred(uds_fd_table[minor].owner, &(data->cred));
if (rc == -1) {
return errno;
}
#if DEBUG == 1
printf("(uds) [%d] cred={%d,%d,%d}\n", minor,
data->cred.pid, data->cred.uid,
data->cred.gid);
#endif
return OK;
}
PRIVATE int send_fds(int minor, struct ancillary *data)
{
int rc, i, j;
#if DEBUG == 1
static int call_count = 0;
printf("(uds) [%d] send_fds() call_count=%d\n", minor, ++call_count);
#endif
/* verify the file descriptors and get their filps. */
for (i = 0; i < data->nfiledes; i++) {
data->filps[i] = verify_fd(uds_fd_table[minor].owner,
data->fds[i]);
if (data->filps[i] == NULL) {
return EINVAL;
}
}
/* set them as in-flight */
for (i = 0; i < data->nfiledes; i++) {
rc = set_filp(data->filps[i]);
if (rc != OK) {
/* revert set_filp() calls */
for (j = i; j >= 0; j--) {
put_filp(data->filps[j]);
}
return rc;
}
}
return OK;
}
PUBLIC int clear_fds(int minor, struct ancillary *data)
{
/* This function calls put_filp() for all of the FDs in data.
* This is used when a Unix Domain Socket is closed and there
* exists references to file descriptors that haven't been received
* with recvmsg().
*/
int i;
#if DEBUG == 1
static int call_count = 0;
printf("(uds) [%d] recv_fds() call_count=%d\n", minor,
++call_count);
#endif
for (i = 0; i < data->nfiledes; i++) {
put_filp(data->filps[i]);
#if DEBUG == 1
printf("(uds) clear_fds() => %d\n", data->fds[i]);
#endif
data->fds[i] = -1;
data->filps[i] = NULL;
}
data->nfiledes = 0;
return OK;
}
PRIVATE int recv_fds(int minor, struct ancillary *data,
struct msg_control *msg_ctrl)
{
int rc, i, j;
struct msghdr msghdr;
struct cmsghdr *cmsg;
endpoint_t to_ep;
#if DEBUG == 1
static int call_count = 0;
printf("(uds) [%d] recv_fds() call_count=%d\n", minor,
++call_count);
#endif
msghdr.msg_control = msg_ctrl->msg_control;
msghdr.msg_controllen = msg_ctrl->msg_controllen;
cmsg = CMSG_FIRSTHDR(&msghdr);
cmsg->cmsg_len = CMSG_LEN(sizeof(int) * data->nfiledes);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
to_ep = uds_fd_table[minor].owner;
/* copy to the target endpoint */
for (i = 0; i < data->nfiledes; i++) {
rc = copy_filp(to_ep, data->filps[i]);
if (rc < 0) {
/* revert set_filp() calls */
for (j = 0; j < data->nfiledes; j++) {
put_filp(data->filps[j]);
}
/* revert copy_filp() calls */
for (j = i; j >= 0; j--) {
cancel_fd(to_ep, data->fds[j]);
}
return rc;
}
data->fds[i] = rc; /* data->fds[i] now has the new FD */
}
for (i = 0; i < data->nfiledes; i++) {
put_filp(data->filps[i]);
#if DEBUG == 1
printf("(uds) recv_fds() => %d\n", data->fds[i]);
#endif
((int *)CMSG_DATA(cmsg))[i] = data->fds[i];
data->fds[i] = -1;
data->filps[i] = NULL;
}
data->nfiledes = 0;
return OK;
}
PRIVATE int recv_cred(int minor, struct ancillary *data,
struct msg_control *msg_ctrl)
{
int rc, i;
struct msghdr msghdr;
struct cmsghdr *cmsg;
#if DEBUG == 1
static int call_count = 0;
printf("(uds) [%d] recv_cred() call_count=%d\n", minor,
++call_count);
#endif
msghdr.msg_control = msg_ctrl->msg_control;
msghdr.msg_controllen = msg_ctrl->msg_controllen;
cmsg = CMSG_FIRSTHDR(&msghdr);
if (cmsg->cmsg_len > 0) {
cmsg = CMSG_NXTHDR(&msghdr, cmsg);
}
cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_CREDENTIALS;
memcpy(CMSG_DATA(cmsg), &(data->cred), sizeof(struct ucred));
return OK;
}
PUBLIC int do_sendmsg(message *dev_m_in, message *dev_m_out)
{
int minor, peer, rc, i;
struct msg_control msg_ctrl;
#if DEBUG == 1
static int call_count = 0;
printf("(uds) [%d] do_sendmsg() call_count=%d\n",
uds_minor(dev_m_in), ++call_count);
#endif
minor = uds_minor(dev_m_in);
memset(&msg_ctrl, '\0', sizeof(struct msg_control));
rc = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) dev_m_in->IO_GRANT,
(vir_bytes) 0, (vir_bytes) &msg_ctrl,
sizeof(struct msg_control), D);
if (rc != OK) {
uds_fd_table[minor].syscall_done = 1;
uds_set_reply(dev_m_out, TASK_REPLY, dev_m_in->IO_ENDPT,
(cp_grant_id_t) dev_m_in->IO_GRANT, EIO);
return EIO;
}
/* locate peer */
peer = -1;
if (uds_fd_table[minor].type == SOCK_DGRAM) {
if (uds_fd_table[minor].target.sun_path[0] == '\0' ||
uds_fd_table[minor].target.sun_family != AF_UNIX) {
uds_fd_table[minor].syscall_done = 1;
uds_set_reply(dev_m_out, TASK_REPLY,
dev_m_in->IO_ENDPT,
(cp_grant_id_t) dev_m_in->IO_GRANT,
EDESTADDRREQ);
return EDESTADDRREQ;
}
for (i = 0; i < NR_FDS; i++) {
/* look for a SOCK_DGRAM socket that is bound on
* the target address
*/
if (uds_fd_table[i].type == SOCK_DGRAM &&
uds_fd_table[i].addr.sun_family == AF_UNIX &&
!strncmp(uds_fd_table[minor].target.sun_path,
uds_fd_table[i].addr.sun_path, UNIX_PATH_MAX)){
peer = i;
break;
}
}
if (peer == -1) {
uds_fd_table[minor].syscall_done = 1;
uds_set_reply(dev_m_out, TASK_REPLY,
dev_m_in->IO_ENDPT,
(cp_grant_id_t) dev_m_in->IO_GRANT, ENOENT);
return ENOENT;
}
} else {
peer = uds_fd_table[minor].peer;
if (peer == -1) {
uds_fd_table[minor].syscall_done = 1;
uds_set_reply(dev_m_out, TASK_REPLY,
dev_m_in->IO_ENDPT,
(cp_grant_id_t) dev_m_in->IO_GRANT, ENOTCONN);
return ENOTCONN;
}
}
#if DEBUG == 1
printf("(uds) [%d] sendmsg() -- peer=%d\n", minor, peer);
#endif
/* note: it's possible that there is already some file
* descriptors in ancillary_data if the peer didn't call
* recvmsg() yet. That's okay. The receiver will
* get the current file descriptors plus the new ones.
*/
rc = msg_control_read(&msg_ctrl, &uds_fd_table[peer].ancillary_data,
minor);
if (rc != OK) {
uds_fd_table[minor].syscall_done = 1;
uds_set_reply(dev_m_out, TASK_REPLY, dev_m_in->IO_ENDPT,
(cp_grant_id_t) dev_m_in->IO_GRANT, rc);
return rc;
}
rc = send_fds(minor, &uds_fd_table[peer].ancillary_data);
if (rc != OK) {
uds_fd_table[minor].syscall_done = 1;
uds_set_reply(dev_m_out, TASK_REPLY, dev_m_in->IO_ENDPT,
(cp_grant_id_t) dev_m_in->IO_GRANT, rc);
return rc;
}
uds_fd_table[minor].syscall_done = 1;
uds_set_reply(dev_m_out, TASK_REPLY, dev_m_in->IO_ENDPT,
(cp_grant_id_t) dev_m_in->IO_GRANT, OK);
return OK;
}
PUBLIC int do_recvmsg(message *dev_m_in, message *dev_m_out)
{
int minor;
int rc;
struct msg_control msg_ctrl;
socklen_t controllen_avail = 0;
socklen_t controllen_needed = 0;
socklen_t controllen_desired = 0;
#if DEBUG == 1
static int call_count = 0;
printf("(uds) [%d] do_sendmsg() call_count=%d\n",
uds_minor(dev_m_in), ++call_count);
#endif
minor = uds_minor(dev_m_in);
#if DEBUG == 1
printf("(uds) [%d] CREDENTIALS {pid:%d,uid:%d,gid:%d}\n", minor,
uds_fd_table[minor].ancillary_data.cred.pid,
uds_fd_table[minor].ancillary_data.cred.uid,
uds_fd_table[minor].ancillary_data.cred.gid);
#endif
memset(&msg_ctrl, '\0', sizeof(struct msg_control));
/* get the msg_control from the user, it will include the
* amount of space the user has allocated for control data.
*/
rc = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) dev_m_in->IO_GRANT,
(vir_bytes) 0, (vir_bytes) &msg_ctrl,
sizeof(struct msg_control), D);
if (rc != OK) {
uds_fd_table[minor].syscall_done = 1;
uds_set_reply(dev_m_out, TASK_REPLY, dev_m_in->IO_ENDPT,
(cp_grant_id_t) dev_m_in->IO_GRANT, EIO);
return EIO;
}
controllen_avail = MIN(msg_ctrl.msg_controllen, MSG_CONTROL_MAX);
if (uds_fd_table[minor].ancillary_data.nfiledes > 0) {
controllen_needed = CMSG_LEN(sizeof(int) *
(uds_fd_table[minor].ancillary_data.nfiledes));
}
/* if there is room we also include credentials */
controllen_desired = controllen_needed +
CMSG_LEN(sizeof(struct ucred));
if (controllen_needed > controllen_avail) {
uds_fd_table[minor].syscall_done = 1;
uds_set_reply(dev_m_out, TASK_REPLY, dev_m_in->IO_ENDPT,
(cp_grant_id_t) dev_m_in->IO_GRANT, EOVERFLOW);
return EOVERFLOW;
}
rc = recv_fds(minor, &uds_fd_table[minor].ancillary_data, &msg_ctrl);
if (rc != OK) {
uds_fd_table[minor].syscall_done = 1;
uds_set_reply(dev_m_out, TASK_REPLY, dev_m_in->IO_ENDPT,
(cp_grant_id_t) dev_m_in->IO_GRANT, rc);
return rc;
}
if (controllen_desired <= controllen_avail) {
rc = recv_cred(minor, &uds_fd_table[minor].ancillary_data,
&msg_ctrl);
if (rc != OK) {
uds_fd_table[minor].syscall_done = 1;
uds_set_reply(dev_m_out, TASK_REPLY,
dev_m_in->IO_ENDPT,
(cp_grant_id_t) dev_m_in->IO_GRANT, rc);
return rc;
}
}
/* send the user the control data */
rc = sys_safecopyto(VFS_PROC_NR, (cp_grant_id_t) dev_m_in->IO_GRANT,
(vir_bytes) 0, (vir_bytes) &msg_ctrl,
sizeof(struct msg_control), D);
if (rc != OK) {
uds_fd_table[minor].syscall_done = 1;
uds_set_reply(dev_m_out, TASK_REPLY, dev_m_in->IO_ENDPT,
(cp_grant_id_t) dev_m_in->IO_GRANT, EIO);
return EIO;
}
uds_fd_table[minor].syscall_done = 1;
uds_set_reply(dev_m_out, TASK_REPLY, dev_m_in->IO_ENDPT,
(cp_grant_id_t) dev_m_in->IO_GRANT, OK);
return OK;
}

View file

@ -9,7 +9,9 @@
* dev_uds.c, table.c, uds.c
*/
#include <limits.h>
#include <sys/types.h>
#include <sys/ucred.h>
#include <sys/un.h>
#include <minix/endpoint.h>
@ -17,8 +19,15 @@
/* max connection backlog for incoming connections */
#define UDS_SOMAXCONN 64
/* UDS FD state Flags */
#define UDS_CONNECTING 0x10
typedef void* filp_id_t;
/* ancillary data to be sent */
struct ancillary {
filp_id_t filps[OPEN_MAX];
int fds[OPEN_MAX];
int nfiledes;
struct ucred cred;
};
/*
* Internal State Information for a socket descriptor.
@ -122,6 +131,11 @@ struct uds_fd {
*/
int listening;
/* stores file pointers and credentials being sent between
* processes with sendmsg(2) and recvmsg(2).
*/
struct ancillary ancillary_data;
/* Holds an errno. This is set when a connected socket is
* closed and we need to pass ECONNRESET on to a suspended
* peer.