diff options
Diffstat (limited to 'source3/lib/unix_msg')
-rw-r--r-- | source3/lib/unix_msg/unix_msg.c | 173 |
1 files changed, 161 insertions, 12 deletions
diff --git a/source3/lib/unix_msg/unix_msg.c b/source3/lib/unix_msg/unix_msg.c index bdd95a6b61..3a9ce09282 100644 --- a/source3/lib/unix_msg/unix_msg.c +++ b/source3/lib/unix_msg/unix_msg.c @@ -42,6 +42,8 @@ struct unix_dgram_msg { int sock; ssize_t sent; int sys_errno; + size_t num_fds; + int *fds; size_t buflen; uint8_t buf[]; }; @@ -421,6 +423,7 @@ static void unix_dgram_send_queue_free(struct unix_dgram_send_queue *q) struct unix_dgram_msg *msg; msg = q->msgs; DLIST_REMOVE(q->msgs, msg); + close_fd_array(msg->fds, msg->num_fds); free(msg); } close(q->sock); @@ -442,28 +445,73 @@ static struct unix_dgram_send_queue *find_send_queue( } static int queue_msg(struct unix_dgram_send_queue *q, - const struct iovec *iov, int iovlen) + const struct iovec *iov, int iovlen, + const int *fds, size_t num_fds) { struct unix_dgram_msg *msg; ssize_t buflen; size_t msglen; + size_t fds_size = sizeof(int) * num_fds; + int fds_copy[MIN(num_fds, INT8_MAX)]; + size_t fds_padding = 0; int i; + size_t tmp; + int ret = -1; + + if (num_fds > INT8_MAX) { + return EINVAL; + } + + for (i = 0; i < num_fds; i++) { + fds_copy[i] = -1; + } + + for (i = 0; i < num_fds; i++) { + fds_copy[i] = dup(fds[i]); + if (fds_copy[i] == -1) { + ret = errno; + goto fail; + } + } buflen = iov_buflen(iov, iovlen); if (buflen == -1) { - return EINVAL; + goto invalid; } - msglen = offsetof(struct unix_dgram_msg, buf) + buflen; - if ((msglen < buflen) || - (msglen < offsetof(struct unix_dgram_msg, buf))) { + msglen = offsetof(struct unix_dgram_msg, buf); + tmp = msglen + buflen; + if ((tmp < msglen) || (tmp < buflen)) { /* overflow */ - return EINVAL; + goto invalid; + } + msglen = tmp; + + if (num_fds > 0) { + const size_t fds_align = sizeof(int) - 1; + + tmp = msglen + fds_align; + if ((tmp < msglen) || (tmp < fds_align)) { + /* overflow */ + goto invalid; + } + tmp &= ~fds_align; + + fds_padding = tmp - msglen; + msglen = tmp; + + tmp = msglen + fds_size; + if ((tmp < msglen) || (tmp < fds_size)) { + /* overflow */ + goto invalid; + } + msglen = tmp; } msg = malloc(msglen); if (msg == NULL) { - return ENOMEM; + ret = ENOMEM; + goto fail; } msg->buflen = buflen; msg->sock = q->sock; @@ -474,8 +522,23 @@ static int queue_msg(struct unix_dgram_send_queue *q, buflen += iov[i].iov_len; } + msg->num_fds = num_fds; + if (msg->num_fds > 0) { + void *fds_ptr = (void *)&msg->buf[buflen+fds_padding]; + memcpy(fds_ptr, fds_copy, fds_size); + msg->fds = (int *)fds_ptr; + } else { + msg->fds = NULL; + } + DLIST_ADD_END(q->msgs, msg, struct unix_dgram_msg); return 0; + +invalid: + ret = EINVAL; +fail: + close_fd_array(fds_copy, num_fds); + return ret; } static void unix_dgram_send_job(void *private_data) @@ -489,10 +552,35 @@ static void unix_dgram_send_job(void *private_data) .msg_iov = &iov, .msg_iovlen = 1, }; +#ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL + struct cmsghdr *cmsg; + size_t fds_size = sizeof(int) * dmsg->num_fds; + size_t cmsg_len = CMSG_LEN(fds_size); + size_t cmsg_space = CMSG_SPACE(fds_size); + char cmsg_buf[cmsg_space]; + + if (dmsg->num_fds > 0) { + void *fdptr; + + memset(cmsg_buf, 0, cmsg_space); + + msg.msg_control = cmsg_buf; + msg.msg_controllen = cmsg_space; + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = cmsg_len; + fdptr = CMSG_DATA(cmsg); + memcpy(fdptr, dmsg->fds, fds_size); + msg.msg_controllen = cmsg->cmsg_len; + } +#endif /* HAVE_STRUCT_MSGHDR_MSG_CONTROL */ do { dmsg->sent = sendmsg(dmsg->sock, &msg, 0); } while ((dmsg->sent == -1) && (errno == EINTR)); + + close_fd_array(dmsg->fds, dmsg->num_fds); } static void unix_dgram_job_finished(struct poll_watch *w, int fd, short events, @@ -521,6 +609,7 @@ static void unix_dgram_job_finished(struct poll_watch *w, int fd, short events, msg = q->msgs; DLIST_REMOVE(q->msgs, msg); + close_fd_array(msg->fds, msg->num_fds); free(msg); if (q->msgs != NULL) { @@ -536,11 +625,52 @@ static void unix_dgram_job_finished(struct poll_watch *w, int fd, short events, static int unix_dgram_send(struct unix_dgram_ctx *ctx, const struct sockaddr_un *dst, - const struct iovec *iov, int iovlen) + const struct iovec *iov, int iovlen, + const int *fds, size_t num_fds) { struct unix_dgram_send_queue *q; struct msghdr msg; +#ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL + struct cmsghdr *cmsg; + size_t fds_size = sizeof(int) * num_fds; + size_t cmsg_len = CMSG_LEN(fds_size); + size_t cmsg_space = CMSG_SPACE(fds_size); + char cmsg_buf[cmsg_space]; +#endif /* HAVE_STRUCT_MSGHDR_MSG_CONTROL */ int ret; + int i; + + if (num_fds > INT8_MAX) { + return EINVAL; + } + +#ifndef HAVE_STRUCT_MSGHDR_MSG_CONTROL + if (num_fds > 0) { + return ENOSYS; + } +#endif /* ! HAVE_STRUCT_MSGHDR_MSG_CONTROL */ + + for (i = 0; i < num_fds; i++) { + /* + * Make sure we only allow fd passing + * for communication channels, + * e.g. sockets, pipes, fifos, ... + */ + ret = lseek(fds[i], 0, SEEK_CUR); + if (ret == -1 && errno == ESPIPE) { + /* ok */ + continue; + } + + /* + * Reject the message as we may need to call dup(), + * if we queue the message. + * + * That might result in unexpected behavior for the caller + * for files and broken posix locking. + */ + return EINVAL; + } /* * To preserve message ordering, we have to queue a message when @@ -548,7 +678,7 @@ static int unix_dgram_send(struct unix_dgram_ctx *ctx, */ q = find_send_queue(ctx, dst->sun_path); if (q != NULL) { - return queue_msg(q, iov, iovlen); + return queue_msg(q, iov, iovlen, fds, num_fds); } /* @@ -561,6 +691,23 @@ static int unix_dgram_send(struct unix_dgram_ctx *ctx, .msg_iov = discard_const_p(struct iovec, iov), .msg_iovlen = iovlen }; +#ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL + if (num_fds > 0) { + void *fdptr; + + memset(cmsg_buf, 0, cmsg_space); + + msg.msg_control = cmsg_buf; + msg.msg_controllen = cmsg_space; + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = cmsg_len; + fdptr = CMSG_DATA(cmsg); + memcpy(fdptr, fds, fds_size); + msg.msg_controllen = cmsg->cmsg_len; + } +#endif /* HAVE_STRUCT_MSGHDR_MSG_CONTROL */ ret = sendmsg(ctx->sock, &msg, 0); if (ret >= 0) { @@ -574,7 +721,7 @@ static int unix_dgram_send(struct unix_dgram_ctx *ctx, if (ret != 0) { return ret; } - ret = queue_msg(q, iov, iovlen); + ret = queue_msg(q, iov, iovlen, fds, num_fds); if (ret != 0) { unix_dgram_send_queue_free(q); return ret; @@ -730,7 +877,8 @@ int unix_msg_send(struct unix_msg_ctx *ctx, const struct sockaddr_un *dst, sizeof(struct iovec) * iovlen); } - return unix_dgram_send(ctx->dgram, dst, iov_copy, iovlen+1); + return unix_dgram_send(ctx->dgram, dst, iov_copy, iovlen+1, + NULL, 0); } hdr = (struct unix_msg_hdr) { @@ -786,7 +934,8 @@ int unix_msg_send(struct unix_msg_ctx *ctx, const struct sockaddr_un *dst, } sent += (fragment_len - sizeof(ctx->cookie) - sizeof(hdr)); - ret = unix_dgram_send(ctx->dgram, dst, iov_copy, iov_index); + ret = unix_dgram_send(ctx->dgram, dst, iov_copy, iov_index, + NULL, 0); if (ret != 0) { break; } |