freebsd-dev/bin/auditdistd/proto_uds.c

365 lines
8.3 KiB
C
Raw Normal View History

/*-
* Copyright (c) 2009-2010 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Pawel Jakub Dawidek under sponsorship from
* the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $P4: //depot/projects/trustedbsd/openbsm/bin/auditdistd/proto_uds.c#1 $
*/
/* UDS - UNIX Domain Socket */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#ifndef HAVE_STRLCPY
#include <compat/strlcpy.h>
#endif
#include "pjdlog.h"
#include "proto_impl.h"
#define UDS_CTX_MAGIC 0xd541c
struct uds_ctx {
int uc_magic;
struct sockaddr_un uc_sun;
int uc_fd;
int uc_side;
#define UDS_SIDE_CLIENT 0
#define UDS_SIDE_SERVER_LISTEN 1
#define UDS_SIDE_SERVER_WORK 2
pid_t uc_owner;
};
static void uds_close(void *ctx);
static int
uds_addr(const char *addr, struct sockaddr_un *sunp)
{
if (addr == NULL)
return (-1);
if (strncasecmp(addr, "uds://", 6) == 0)
addr += 6;
else if (strncasecmp(addr, "unix://", 7) == 0)
addr += 7;
else if (addr[0] == '/' && /* If it starts from /... */
strstr(addr, "://") == NULL)/* ...and there is no prefix... */
; /* ...we assume its us. */
else
return (-1);
sunp->sun_family = AF_UNIX;
if (strlcpy(sunp->sun_path, addr, sizeof(sunp->sun_path)) >=
sizeof(sunp->sun_path)) {
return (ENAMETOOLONG);
}
#ifdef HAVE_SOCKADDR_STORAGE_SS_LEN
sunp->sun_len = SUN_LEN(sunp);
#endif
return (0);
}
static int
uds_common_setup(const char *addr, int side, struct uds_ctx **uctxp)
{
struct uds_ctx *uctx;
int error;
uctx = malloc(sizeof(*uctx));
if (uctx == NULL)
return (errno);
/* Parse given address. */
error = uds_addr(addr, &uctx->uc_sun);
if (error != 0) {
free(uctx);
return (error);
}
uctx->uc_fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (uctx->uc_fd == -1) {
error = errno;
free(uctx);
return (error);
}
uctx->uc_side = side;
uctx->uc_owner = 0;
uctx->uc_magic = UDS_CTX_MAGIC;
*uctxp = uctx;
return (0);
}
static int
uds_connect(const char *srcaddr, const char *dstaddr, int timeout, void **ctxp)
{
struct uds_ctx *uctx;
int error;
PJDLOG_ASSERT(dstaddr != NULL);
PJDLOG_ASSERT(timeout >= -1);
error = uds_common_setup(dstaddr, UDS_SIDE_CLIENT, &uctx);
if (error != 0)
return (error);
PJDLOG_ASSERT(srcaddr == NULL);
if (connect(uctx->uc_fd, (struct sockaddr *)&uctx->uc_sun,
sizeof(uctx->uc_sun)) == -1) {
error = errno;
uds_close(uctx);
return (error);
}
*ctxp = uctx;
return (0);
}
static int
uds_connect_wait(void *ctx, int timeout)
{
struct uds_ctx *uctx = ctx;
PJDLOG_ASSERT(uctx != NULL);
PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
PJDLOG_ASSERT(uctx->uc_side == UDS_SIDE_CLIENT);
PJDLOG_ASSERT(uctx->uc_fd >= 0);
PJDLOG_ASSERT(timeout >= 0);
return (0);
}
static int
uds_server(const char *addr, void **ctxp)
{
struct uds_ctx *uctx;
int error;
error = uds_common_setup(addr, UDS_SIDE_SERVER_LISTEN, &uctx);
if (error != 0)
return (error);
(void)unlink(uctx->uc_sun.sun_path);
if (bind(uctx->uc_fd, (struct sockaddr *)&uctx->uc_sun,
sizeof(uctx->uc_sun)) == -1) {
error = errno;
uds_close(uctx);
return (error);
}
uctx->uc_owner = getpid();
if (listen(uctx->uc_fd, 8) == -1) {
error = errno;
uds_close(uctx);
return (error);
}
*ctxp = uctx;
return (0);
}
static int
uds_accept(void *ctx, void **newctxp)
{
struct uds_ctx *uctx = ctx;
struct uds_ctx *newuctx;
socklen_t fromlen;
int error;
PJDLOG_ASSERT(uctx != NULL);
PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
PJDLOG_ASSERT(uctx->uc_side == UDS_SIDE_SERVER_LISTEN);
PJDLOG_ASSERT(uctx->uc_fd >= 0);
newuctx = malloc(sizeof(*newuctx));
if (newuctx == NULL)
return (errno);
fromlen = sizeof(newuctx->uc_sun);
newuctx->uc_fd = accept(uctx->uc_fd,
(struct sockaddr *)&newuctx->uc_sun, &fromlen);
if (newuctx->uc_fd < 0) {
error = errno;
free(newuctx);
return (error);
}
newuctx->uc_side = UDS_SIDE_SERVER_WORK;
newuctx->uc_magic = UDS_CTX_MAGIC;
*newctxp = newuctx;
return (0);
}
static int
uds_send(void *ctx, const unsigned char *data, size_t size, int fd)
{
struct uds_ctx *uctx = ctx;
PJDLOG_ASSERT(uctx != NULL);
PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
PJDLOG_ASSERT(uctx->uc_fd >= 0);
return (proto_common_send(uctx->uc_fd, data, size, fd));
}
static int
uds_recv(void *ctx, unsigned char *data, size_t size, int *fdp)
{
struct uds_ctx *uctx = ctx;
PJDLOG_ASSERT(uctx != NULL);
PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
PJDLOG_ASSERT(uctx->uc_fd >= 0);
return (proto_common_recv(uctx->uc_fd, data, size, fdp));
}
static int
uds_descriptor(const void *ctx)
{
const struct uds_ctx *uctx = ctx;
PJDLOG_ASSERT(uctx != NULL);
PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
return (uctx->uc_fd);
}
static void
uds_local_address(const void *ctx, char *addr, size_t size)
{
const struct uds_ctx *uctx = ctx;
struct sockaddr_un sun;
socklen_t sunlen;
PJDLOG_ASSERT(uctx != NULL);
PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
PJDLOG_ASSERT(addr != NULL);
sunlen = sizeof(sun);
if (getsockname(uctx->uc_fd, (struct sockaddr *)&sun, &sunlen) < 0) {
PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
return;
}
PJDLOG_ASSERT(sun.sun_family == AF_UNIX);
if (sun.sun_path[0] == '\0') {
PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
return;
}
PJDLOG_VERIFY(snprintf(addr, size, "uds://%s", sun.sun_path) < (ssize_t)size);
}
static void
uds_remote_address(const void *ctx, char *addr, size_t size)
{
const struct uds_ctx *uctx = ctx;
struct sockaddr_un sun;
socklen_t sunlen;
PJDLOG_ASSERT(uctx != NULL);
PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
PJDLOG_ASSERT(addr != NULL);
sunlen = sizeof(sun);
if (getpeername(uctx->uc_fd, (struct sockaddr *)&sun, &sunlen) < 0) {
PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
return;
}
PJDLOG_ASSERT(sun.sun_family == AF_UNIX);
if (sun.sun_path[0] == '\0') {
PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
return;
}
snprintf(addr, size, "uds://%s", sun.sun_path);
}
static void
uds_close(void *ctx)
{
struct uds_ctx *uctx = ctx;
PJDLOG_ASSERT(uctx != NULL);
PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
if (uctx->uc_fd >= 0)
close(uctx->uc_fd);
/*
* Unlink the socket only if we are the owner and this is descriptor
* we listen on.
*/
if (uctx->uc_side == UDS_SIDE_SERVER_LISTEN &&
uctx->uc_owner == getpid()) {
PJDLOG_ASSERT(uctx->uc_sun.sun_path[0] != '\0');
if (unlink(uctx->uc_sun.sun_path) == -1) {
pjdlog_errno(LOG_WARNING,
"Unable to unlink socket file %s",
uctx->uc_sun.sun_path);
}
}
uctx->uc_owner = 0;
uctx->uc_magic = 0;
free(uctx);
}
static struct proto uds_proto = {
.prt_name = "uds",
.prt_connect = uds_connect,
.prt_connect_wait = uds_connect_wait,
.prt_server = uds_server,
.prt_accept = uds_accept,
.prt_send = uds_send,
.prt_recv = uds_recv,
.prt_descriptor = uds_descriptor,
.prt_local_address = uds_local_address,
.prt_remote_address = uds_remote_address,
.prt_close = uds_close
};
static __constructor void
uds_ctor(void)
{
proto_register(&uds_proto, false);
}