CR 7121230 Rad unix transport paths should be created under /system/volatile/rad/
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <bsm/adt_event.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <pthread.h>
#include <ucred.h>
#include <zone.h>
#include <rad/adr_stream.h>
#include "rad_object.h"
#include "rad_modapi.h"
#include "rad_modapi_xport.h"
#include "rad_connection.h"
#include "rad_xport.h"
#include "api_unix.h"
static boolean_t
sockaddr_init(struct sockaddr_un *addr, const char *name)
{
size_t namelen;
size_t addrlen;
(void) memset(addr, 0, sizeof (*addr));
addr->sun_family = AF_UNIX;
namelen = strlen(name);
addrlen = sizeof (addr->sun_path);
if (namelen >= addrlen)
return (B_FALSE);
(void) strlcpy(addr->sun_path, name, sizeof (addr->sun_path));
return (B_TRUE);
}
static int
create_tmpdir(const char *name)
{
int retval = 0;
struct stat st;
mode_t um;
int i = strncmp(name, RAD_TMPDIR "/", strlen(RAD_TMPDIR "/"));
if (i == 0) { /* Default path specified */
if (stat(RAD_TMPDIR, &st) == 0) {
if (!S_ISDIR(st.st_mode)) {
rad_log(RL_ERROR, "file '%s' exists.",
RAD_TMPDIR);
retval = -1;
}
} else if (errno == ENOENT) { /* Create it */
um = umask(0);
i = mkdir(RAD_TMPDIR, S_IRWXU | S_IRWXG | S_IRWXO);
umask(um);
if (i != 0) {
rad_log(RL_ERROR, "error creating '%s': %s."
RAD_TMPDIR, strerror(errno));
retval = -1;
}
} else {
rad_log(RL_ERROR, "error creating '%s': %s."
RAD_TMPDIR, strerror(errno));
retval = -1;
}
}
return (retval);
}
static int
listen_on_name(const char *name)
{
int fd;
struct sockaddr_un addr;
if (create_tmpdir(name) != 0)
return (-1);
if (unlink(name) == -1 && errno != ENOENT) {
rad_log(RL_ERROR, "unlink of '%s' failed: %s", name,
strerror(errno));
return (-1);
}
if (!sockaddr_init(&addr, name)) {
rad_log(RL_ERROR, "socket name '%s' too long", name);
return (-1);
}
if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
rad_log(RL_ERROR, "socket failed: %s", strerror(errno));
return (-1);
}
if (bind(fd, (struct sockaddr *)&addr, sizeof (addr)) == -1) {
rad_log(RL_ERROR, "bind to '%s' failed: %s", name,
strerror(errno));
(void) close(fd);
return (-1);
}
if (listen(fd, 15) == -1) {
rad_log(RL_ERROR, "listen on '%s' failed: %s", name,
strerror(errno));
return (-1);
}
return (fd);
}
/*
* Determines if the ucred represents someone who is effectively us.
*/
static boolean_t
sent_by_joe(ucred_t *uc)
{
const priv_set_t *theirprivs;
priv_set_t *myprivs = priv_allocset();
if (myprivs == NULL) {
rad_log(RL_ERROR, "failed to allocate privilege set");
return (B_FALSE);
}
/* Could handle this "gracefully", but it isn't supposed to fail */
if (getppriv(PRIV_PERMITTED, myprivs) == -1)
rad_log(RL_FATAL, "getppriv(PRIV_PERMITTED) failed: %s",
strerror(errno));
if (uc == NULL ||
ucred_geteuid(uc) != getuid() ||
ucred_getzoneid(uc) != getzoneid() ||
(theirprivs = ucred_getprivset(uc, PRIV_EFFECTIVE)) == NULL ||
!priv_issubset(myprivs, theirprivs)) {
priv_freeset(myprivs);
rad_log(RL_WARN, "unprivileged client (uid=%d) "
"attempted connection to control port", ucred_geteuid(uc));
return (B_FALSE);
}
priv_freeset(myprivs);
return (B_TRUE);
}
static void
unix_run(void *arg)
{
radmod_connection_t *conn = arg;
rad_proto_handle(conn);
rad_conn_free(conn);
}
static rad_moderr_t
unix_listen(rad_thread_t *arg)
{
data_t *data = rad_thread_arg(arg);
int fd;
data_t *d, *path = struct_get(data, "path");
d = struct_get(data, "proto");
const char *protostr = d != NULL ? data_to_string(d) : "rad";
d = struct_get(data, "control");
boolean_t control = d != NULL ? data_to_boolean(d) : B_FALSE;
d = struct_get(data, "peercred");
boolean_t peercred = d != NULL ? data_to_boolean(d) : B_TRUE;
rad_protocol_t *proto = rad_proto_find(protostr);
if (proto == NULL) {
rad_log(RL_ERROR, "Unable to find protocol \"%s\".", protostr);
return (rm_config);
}
if ((fd = listen_on_name(data_to_string(path))) < 0) {
rad_log(RL_ERROR, "Error starting AF_UNIX server: %s",
strerror(errno));
return (rm_system);
}
rad_thread_ack(arg, rm_ok);
for (;;) {
int afd;
rad_log(RL_DEBUG, "Waiting for connection.\n");
if ((afd = accept(fd, 0, 0)) == -1) {
rad_log(RL_WARN, "Error in accept(): %s\n",
strerror(errno));
continue;
}
rad_log(RL_DEBUG, "Connection accepted.\n");
/* subject allocation failure and missing ucred are conflated */
rad_subject_t *subject = peercred ? rad_subject_create_fd(afd) :
NULL;
if (control) {
if (subject == NULL) {
(void) close(afd);
continue;
}
if (!sent_by_joe(subject->rs_ucred)) {
rad_subject_unref(subject);
(void) close(afd);
continue;
}
rad_log(RL_DEBUG,
"accepting connection on control port");
subject->rs_control = B_TRUE;
}
adr_stream_t *stream = adr_stream_create_fd(afd);
if (stream == NULL)
continue;
radmod_connection_t *conn = rad_conn_create_fd(afd, B_TRUE);
if (conn == NULL) {
adr_stream_close(stream);
adr_stream_free(stream);
rad_log(RL_WARN, "failed to allocate connection");
continue;
}
conn->rm_conn_xport = stream;
conn->rm_conn_proto_ops = proto;
if (subject != NULL &&
!rad_conn_setsubject(conn, subject, ADT_login)) {
rad_conn_close(conn);
rad_conn_free(conn);
rad_log(RL_WARN, "failed to set connection subject");
continue;
}
if (rad_thread_create_async(unix_run, conn) != rm_ok) {
rad_conn_close(conn);
rad_conn_free(conn);
}
}
}
static rad_moderr_t
starter(data_t *data)
{
data_t *path = struct_get(data, "path");
if (path == NULL) {
rad_log(RL_ERROR, "Unix domain socket requires path\n");
return (rm_config);
}
return (rad_thread_create(unix_listen, data));
}
static rad_modinfo_t modinfo = {
"xport_unix", "unix domain socket transport module",
};
int
_rad_init(void *handle)
{
if (rad_module_register(handle, RAD_MODVERSION, &modinfo) == -1)
return (-1);
rad_xport_register("unix", &t__unix, starter);
return (0);
}