diff -r 62c7eb34e283 -r 71abce159a62 usr/src/cmd/rad/mod/xport_uds/mod_xport_uds.c --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); +}