usr/src/cmd/rad/mod/xport_uds/mod_xport_uds.c
changeset 391 71abce159a62
child 426 2cc50564cd5f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/rad/mod/xport_uds/mod_xport_uds.c	Mon Dec 14 23:43:39 2009 -0800
@@ -0,0 +1,247 @@
+/*
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.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_object.h"
+#include "rad_modapi.h"
+#include "rad_modapi_xport.h"
+#include "rad_connection.h"
+#include "rad_util.h"
+#include "rad_xport.h"
+#include "rad_log.h"
+
+#include "api_uds.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
+listen_on_name(const char *name)
+{
+	int fd;
+	struct sockaddr_un addr;
+
+	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);
+}
+
+static radmod_transport_t transport = {
+	rm_fd_read,
+	rm_fd_write,
+	rm_fd_close,
+	rm_fd_free
+};
+
+/*
+ * 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);
+	}
+	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
+uds_run(void *arg)
+{
+	radmod_connection_t *conn = arg;
+	rad_proto_handle(conn);
+	free(conn);
+}
+
+static rad_moderr_t
+uds_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 ? d->d_data.string : "rad";
+	d = struct_get(data, "control");
+	boolean_t control = d != NULL ? d->d_data.boolean : B_FALSE;
+
+	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(path->d_data.string)) < 0) {
+		rad_log(RL_ERROR, "Error starting uds 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");
+
+		rad_subject_t *subject = rad_subject_create_fd(afd, B_TRUE);
+		if (subject == NULL) {
+			rad_log(RL_ERROR, "unable to allocate subject");
+			(void) close(afd);
+			continue;
+		}
+
+		if (control) {
+			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;
+		}
+
+		rm_xport_fd_t *fddata = zalloc(sizeof (rm_xport_fd_t));
+		fddata->infd = afd;
+		fddata->outfd = afd;
+		radmod_connection_t *conn = rad_conn_create();
+		conn->rm_conn_xport_ops = &transport;
+		conn->rm_conn_xport_data = fddata;
+		conn->rm_conn_proto_ops = proto;
+		conn->rm_conn_subject = subject;
+
+		if (rad_thread_create_async(uds_run, conn) != rm_ok) {
+			rad_conn_close(conn);
+			free(conn);
+		}
+	}
+}
+
+static pthread_t server_thread;
+
+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(uds_listen, data));
+}
+
+static rad_modinfo_t modinfo = {
+	"xport_uds", "unix domain socket transport module",
+};
+
+int
+_rad_init(void *handle)
+{
+	if (rad_module_register(handle, RAD_MODVERSION, &modinfo) == -1)
+		return (-1);
+
+	rad_xport_register("uds", &t__uds, starter);
+	return (0);
+}