2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A/*
2N/A * This library contains a set of routines that are shared amongst inetd,
2N/A * inetadm, inetconv and the formerly internal inetd services. Amongst the
2N/A * routines are ones for reading and validating the configuration of an
2N/A * inetd service, a routine for requesting inetd be refreshed, ones for
2N/A * reading, calculating and writing the hash of an inetd.conf file, and
2N/A * numerous utility routines shared amongst the formerly internal inetd
2N/A * services.
2N/A */
2N/A
2N/A
2N/A#include <string.h>
2N/A#include <rpc/rpcent.h>
2N/A#include <netdb.h>
2N/A#include <limits.h>
2N/A#include <errno.h>
2N/A#include <inetsvc.h>
2N/A#include <stdlib.h>
2N/A#include <unistd.h>
2N/A#include <nss_dbdefs.h>
2N/A#include <stdio.h>
2N/A#include <fcntl.h>
2N/A#include <pwd.h>
2N/A#include <md5.h>
2N/A#include <arpa/inet.h>
2N/A#include <netinet/in.h>
2N/A#include <signal.h>
2N/A#include <syslog.h>
2N/A#include <libintl.h>
2N/A#include <stdlib.h>
2N/A#include <assert.h>
2N/A#include <rpc/nettype.h>
2N/A#include <libuutil.h>
2N/A
2N/Astatic inetd_prop_t inetd_properties[] = {
2N/A {PR_SVC_NAME_NAME, PG_NAME_SERVICE_CONFIG, INET_TYPE_STRING,
2N/A B_FALSE, IVE_UNSET, NULL, B_FALSE},
2N/A {PR_SOCK_TYPE_NAME, PG_NAME_SERVICE_CONFIG, INET_TYPE_STRING,
2N/A B_FALSE, IVE_UNSET, NULL, B_FALSE},
2N/A {PR_PROTO_NAME, PG_NAME_SERVICE_CONFIG, INET_TYPE_STRING_LIST,
2N/A B_FALSE, IVE_UNSET, NULL, B_FALSE},
2N/A {PR_ISRPC_NAME, PG_NAME_SERVICE_CONFIG, INET_TYPE_BOOLEAN,
2N/A B_FALSE, IVE_UNSET, NULL, B_FALSE},
2N/A {PR_RPC_LW_VER_NAME, PG_NAME_SERVICE_CONFIG, INET_TYPE_INTEGER,
2N/A B_FALSE, IVE_UNSET, NULL, B_FALSE},
2N/A {PR_RPC_HI_VER_NAME, PG_NAME_SERVICE_CONFIG, INET_TYPE_INTEGER,
2N/A B_FALSE, IVE_UNSET, NULL, B_FALSE},
2N/A {PR_ISWAIT_NAME, PG_NAME_SERVICE_CONFIG, INET_TYPE_BOOLEAN,
2N/A B_FALSE, IVE_UNSET, NULL, B_FALSE},
2N/A {PR_EXEC_NAME, START_METHOD_NAME, INET_TYPE_STRING,
2N/A B_FALSE, IVE_UNSET, NULL, B_FALSE},
2N/A {PR_ARG0_NAME, START_METHOD_NAME, INET_TYPE_STRING,
2N/A B_FALSE, IVE_UNSET, NULL, B_FALSE},
2N/A {PR_USER_NAME, START_METHOD_NAME, INET_TYPE_STRING,
2N/A B_FALSE, IVE_UNSET, NULL, B_FALSE},
2N/A {PR_BIND_ADDR_NAME, PG_NAME_SERVICE_CONFIG, INET_TYPE_STRING,
2N/A B_TRUE, IVE_UNSET, NULL, B_FALSE},
2N/A {PR_BIND_FAIL_MAX_NAME, PG_NAME_SERVICE_CONFIG, INET_TYPE_INTEGER,
2N/A B_TRUE, IVE_UNSET, NULL, B_FALSE},
2N/A {PR_BIND_FAIL_INTVL_NAME, PG_NAME_SERVICE_CONFIG, INET_TYPE_INTEGER,
2N/A B_TRUE, IVE_UNSET, NULL, B_FALSE},
2N/A {PR_CON_RATE_MAX_NAME, PG_NAME_SERVICE_CONFIG, INET_TYPE_INTEGER,
2N/A B_TRUE, IVE_UNSET, NULL, B_FALSE},
2N/A {PR_MAX_COPIES_NAME, PG_NAME_SERVICE_CONFIG, INET_TYPE_INTEGER,
2N/A B_TRUE, IVE_UNSET, NULL, B_FALSE},
2N/A {PR_CON_RATE_OFFLINE_NAME, PG_NAME_SERVICE_CONFIG, INET_TYPE_INTEGER,
2N/A B_TRUE, IVE_UNSET, NULL, B_FALSE},
2N/A {PR_MAX_FAIL_RATE_CNT_NAME, PG_NAME_SERVICE_CONFIG, INET_TYPE_INTEGER,
2N/A B_TRUE, IVE_UNSET, NULL, B_FALSE},
2N/A {PR_MAX_FAIL_RATE_INTVL_NAME, PG_NAME_SERVICE_CONFIG, INET_TYPE_INTEGER,
2N/A B_TRUE, IVE_UNSET, NULL, B_FALSE},
2N/A {PR_INHERIT_ENV_NAME, PG_NAME_SERVICE_CONFIG, INET_TYPE_BOOLEAN,
2N/A B_TRUE, IVE_UNSET, NULL, B_FALSE},
2N/A {PR_DO_TCP_TRACE_NAME, PG_NAME_SERVICE_CONFIG, INET_TYPE_BOOLEAN,
2N/A B_TRUE, IVE_UNSET, NULL, B_FALSE},
2N/A {PR_DO_TCP_WRAPPERS_NAME, PG_NAME_SERVICE_CONFIG, INET_TYPE_BOOLEAN,
2N/A B_TRUE, IVE_UNSET, NULL, B_FALSE},
2N/A {PR_CONNECTION_BACKLOG_NAME, PG_NAME_SERVICE_CONFIG, INET_TYPE_INTEGER,
2N/A B_TRUE, IVE_UNSET, NULL, B_FALSE},
2N/A {PR_DO_TCP_KEEPALIVE_NAME, PG_NAME_SERVICE_CONFIG, INET_TYPE_BOOLEAN,
2N/A B_TRUE, IVE_UNSET, NULL, B_FALSE},
2N/A {NULL},
2N/A};
2N/A
2N/A#define INETSVC_SVC_BUF_MAX (NSS_BUFLEN_RPC + sizeof (struct rpcent))
2N/A
2N/A#define DIGEST_LEN 16
2N/A#define READ_BUFSIZ 8192
2N/A#define HASH_PG "hash"
2N/A#define HASH_PROP "md5sum"
2N/A
2N/A/*
2N/A * Inactivity timer used by dg_template(). After this many seconds of network
2N/A * inactivity dg_template will cease listening for new datagrams and return.
2N/A */
2N/A#define DG_INACTIVITY_TIMEOUT 60
2N/A
2N/Astatic boolean_t v6_proto(const char *);
2N/A
2N/Aboolean_t
2N/Ais_tlx_service(inetd_prop_t *props)
2N/A{
2N/A return ((strcmp(SOCKTYPE_TLI_STR,
2N/A props[PT_SOCK_TYPE_INDEX].ip_value.iv_string) == 0) ||
2N/A (strcmp(SOCKTYPE_XTI_STR,
2N/A props[PT_SOCK_TYPE_INDEX].ip_value.iv_string) == 0));
2N/A}
2N/A
2N/A/*
2N/A * Return a reference to the property table. Number of entries in table
2N/A * are returned in num_elements argument.
2N/A */
2N/Ainetd_prop_t *
2N/Aget_prop_table(size_t *num_elements)
2N/A{
2N/A *num_elements = sizeof (inetd_properties) / sizeof (inetd_prop_t);
2N/A return (&inetd_properties[0]);
2N/A}
2N/A
2N/A/*
2N/A * find_prop takes an array of inetd_prop_t's, the name of an inetd
2N/A * property, the type expected, and returns a pointer to the matching member,
2N/A * or NULL.
2N/A */
2N/Ainetd_prop_t *
2N/Afind_prop(const inetd_prop_t *prop, const char *name, inet_type_t type)
2N/A{
2N/A int i = 0;
2N/A
2N/A while (prop[i].ip_name != NULL && strcmp(name, prop[i].ip_name) != 0)
2N/A i++;
2N/A
2N/A if (prop[i].ip_name == NULL)
2N/A return (NULL);
2N/A
2N/A if (prop[i].ip_type != type)
2N/A return (NULL);
2N/A
2N/A return ((inetd_prop_t *)prop + i);
2N/A}
2N/A
2N/A/*
2N/A * get_prop_value_int takes an array of inetd_prop_t's together with the name of
2N/A * an inetd property and returns the value of the property. It's expected that
2N/A * the property exists in the searched array.
2N/A */
2N/Aint64_t
2N/Aget_prop_value_int(const inetd_prop_t *prop, const char *name)
2N/A{
2N/A inetd_prop_t *p;
2N/A
2N/A p = find_prop(prop, name, INET_TYPE_INTEGER);
2N/A return (p->ip_value.iv_int);
2N/A}
2N/A
2N/A/*
2N/A * get_prop_value_count takes an array of inetd_prop_t's together with the name
2N/A * of an inetd property and returns the value of the property. It's expected
2N/A * that the property exists in the searched array.
2N/A */
2N/Auint64_t
2N/Aget_prop_value_count(const inetd_prop_t *prop, const char *name)
2N/A{
2N/A inetd_prop_t *p;
2N/A
2N/A p = find_prop(prop, name, INET_TYPE_COUNT);
2N/A return (p->ip_value.iv_cnt);
2N/A}
2N/A
2N/A/*
2N/A * get_prop_value_boolean takes an array of inetd_prop_t's together with the
2N/A * name of an inetd property and returns the value of the property. It's
2N/A * expected that the property exists in the searched array.
2N/A */
2N/Aboolean_t
2N/Aget_prop_value_boolean(const inetd_prop_t *prop, const char *name)
2N/A{
2N/A inetd_prop_t *p;
2N/A
2N/A p = find_prop(prop, name, INET_TYPE_BOOLEAN);
2N/A return (p->ip_value.iv_boolean);
2N/A}
2N/A
2N/A/*
2N/A * get_prop_value_string takes an array of inetd_prop_t's together with
2N/A * the name of an inetd property and returns the value of the property.
2N/A * It's expected that the property exists in the searched array.
2N/A */
2N/Aconst char *
2N/Aget_prop_value_string(const inetd_prop_t *prop, const char *name)
2N/A{
2N/A inetd_prop_t *p;
2N/A
2N/A p = find_prop(prop, name, INET_TYPE_STRING);
2N/A return (p->ip_value.iv_string);
2N/A}
2N/A
2N/A/*
2N/A * get_prop_value_string_list takes an array of inetd_prop_t's together
2N/A * with the name of an inetd property and returns the value of the property.
2N/A * It's expected that the property exists in the searched array.
2N/A */
2N/Aconst char **
2N/Aget_prop_value_string_list(const inetd_prop_t *prop, const char *name)
2N/A{
2N/A inetd_prop_t *p;
2N/A
2N/A p = find_prop(prop, name, INET_TYPE_STRING_LIST);
2N/A return ((const char **)p->ip_value.iv_string_list);
2N/A}
2N/A
2N/A/*
2N/A * put_prop_value_int takes an array of inetd_prop_t's, a name of an inetd
2N/A * property, and a value. It copies the value into the property
2N/A * in the array. It's expected that the property exists in the searched array.
2N/A */
2N/Avoid
2N/Aput_prop_value_int(inetd_prop_t *prop, const char *name, int64_t value)
2N/A{
2N/A inetd_prop_t *p;
2N/A
2N/A p = find_prop(prop, name, INET_TYPE_INTEGER);
2N/A p->ip_value.iv_int = value;
2N/A p->ip_error = IVE_VALID;
2N/A}
2N/A
2N/A/*
2N/A * put_prop_value_count takes an array of inetd_prop_t's, a name of an inetd
2N/A * property, and a value. It copies the value into the property
2N/A * in the array. It's expected that the property exists in the searched array.
2N/A */
2N/Avoid
2N/Aput_prop_value_count(inetd_prop_t *prop, const char *name, uint64_t value)
2N/A{
2N/A inetd_prop_t *p;
2N/A
2N/A p = find_prop(prop, name, INET_TYPE_COUNT);
2N/A p->ip_value.iv_cnt = value;
2N/A p->ip_error = IVE_VALID;
2N/A}
2N/A
2N/A/*
2N/A * put_prop_value_boolean takes an array of inetd_prop_t's, a name of an inetd
2N/A * property, and a value. It copies the value into the property
2N/A * in the array. It's expected that the property exists in the searched array.
2N/A */
2N/Avoid
2N/Aput_prop_value_boolean(inetd_prop_t *prop, const char *name, boolean_t value)
2N/A{
2N/A inetd_prop_t *p;
2N/A
2N/A p = find_prop(prop, name, INET_TYPE_BOOLEAN);
2N/A p->ip_value.iv_boolean = value;
2N/A p->ip_error = IVE_VALID;
2N/A}
2N/A
2N/A/*
2N/A * put_prop_value_string takes an array of inetd_prop_t's, a name of an inetd
2N/A * property, and a value. It duplicates the value into the property
2N/A * in the array, and returns B_TRUE for success and B_FALSE for failure. It's
2N/A * expected that the property exists in the searched array.
2N/A */
2N/Aboolean_t
2N/Aput_prop_value_string(inetd_prop_t *prop, const char *name, const char *value)
2N/A{
2N/A inetd_prop_t *p;
2N/A
2N/A if (strlen(value) >= scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH)) {
2N/A errno = E2BIG;
2N/A return (B_FALSE);
2N/A }
2N/A p = find_prop(prop, name, INET_TYPE_STRING);
2N/A if ((p->ip_value.iv_string = strdup(value)) == NULL)
2N/A return (B_FALSE);
2N/A p->ip_error = IVE_VALID;
2N/A return (B_TRUE);
2N/A}
2N/A
2N/A/*
2N/A * put_prop_value_string_list takes an array of inetd_prop_t's, a name of an
2N/A * inetd property, and a value. It copies the value into the property
2N/A * in the array. It's expected that the property exists in the searched array.
2N/A */
2N/Avoid
2N/Aput_prop_value_string_list(inetd_prop_t *prop, const char *name, char **value)
2N/A{
2N/A inetd_prop_t *p;
2N/A
2N/A p = find_prop(prop, name, INET_TYPE_STRING_LIST);
2N/A p->ip_value.iv_string_list = value;
2N/A p->ip_error = IVE_VALID;
2N/A}
2N/A
2N/Astatic void
2N/Adestroy_rpc_info(rpc_info_t *rpc)
2N/A{
2N/A if (rpc != NULL) {
2N/A free(rpc->netbuf.buf);
2N/A free(rpc->netid);
2N/A free(rpc);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * If 'proto' is a valid netid, and no memory allocations fail, returns a
2N/A * pointer to an allocated and initialized rpc_info_t, else NULL.
2N/A */
2N/Astatic rpc_info_t *
2N/Acreate_rpc_info(const char *proto, int pnum, int low_ver, int high_ver)
2N/A{
2N/A struct netconfig *nconf;
2N/A rpc_info_t *ret;
2N/A
2N/A if ((ret = calloc(1, sizeof (rpc_info_t))) == NULL)
2N/A return (NULL);
2N/A
2N/A ret->netbuf.maxlen = sizeof (struct sockaddr_storage);
2N/A if ((ret->netbuf.buf = malloc(ret->netbuf.maxlen)) == NULL) {
2N/A free(ret);
2N/A return (NULL);
2N/A }
2N/A
2N/A ret->prognum = pnum;
2N/A ret->lowver = low_ver;
2N/A ret->highver = high_ver;
2N/A
2N/A if ((ret->netid = strdup(proto)) == NULL) {
2N/A destroy_rpc_info(ret);
2N/A return (NULL);
2N/A }
2N/A
2N/A /*
2N/A * Determine whether this is a loopback transport. If getnetconfigent()
2N/A * fails, we check to see whether it was the result of a v6 proto
2N/A * being specified and no IPv6 interface was configured on the system;
2N/A * if this holds, we know it must not be a loopback transport, else
2N/A * getnetconfigent() must be miss-behaving, so return an error.
2N/A */
2N/A if ((nconf = getnetconfigent(proto)) != NULL) {
2N/A if (strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0)
2N/A ret->is_loopback = B_TRUE;
2N/A freenetconfigent(nconf);
2N/A } else if (!v6_proto(proto)) {
2N/A destroy_rpc_info(ret);
2N/A return (NULL);
2N/A }
2N/A
2N/A return (ret);
2N/A}
2N/A
2N/Avoid
2N/Adestroy_tlx_info(tlx_info_t *tlx)
2N/A{
2N/A tlx_conn_ind_t *ci;
2N/A void *cookie = NULL;
2N/A
2N/A if (tlx == NULL)
2N/A return;
2N/A
2N/A free(tlx->dev_name);
2N/A
2N/A if (tlx->conn_ind_queue != NULL) {
2N/A /* free up conn ind queue */
2N/A while ((ci = uu_list_teardown(tlx->conn_ind_queue, &cookie)) !=
2N/A NULL) {
2N/A (void) t_free((char *)ci->call, T_CALL);
2N/A free(ci);
2N/A }
2N/A uu_list_destroy(tlx->conn_ind_queue);
2N/A }
2N/A
2N/A free(tlx->local_addr.buf);
2N/A free(tlx);
2N/A}
2N/A
2N/A/*
2N/A * Allocate, initialize and return a pointer to a tlx_info_t structure.
2N/A * On memory allocation failure NULL is returned.
2N/A */
2N/Astatic tlx_info_t *
2N/Acreate_tlx_info(const char *proto, uu_list_pool_t *conn_ind_pool)
2N/A{
2N/A size_t sz;
2N/A tlx_info_t *ret;
2N/A
2N/A if ((ret = calloc(1, sizeof (tlx_info_t))) == NULL)
2N/A return (NULL);
2N/A
2N/A ret->local_addr.maxlen = sizeof (struct sockaddr_storage);
2N/A if ((ret->local_addr.buf = calloc(1, ret->local_addr.maxlen)) == NULL)
2N/A goto fail;
2N/A
2N/A if ((ret->conn_ind_queue = uu_list_create(conn_ind_pool, NULL, 0)) ==
2N/A NULL)
2N/A goto fail;
2N/A
2N/A ret->local_addr.len = sizeof (struct sockaddr_in);
2N/A ((struct sockaddr_in *)(ret->local_addr.buf))->sin_family = AF_INET;
2N/A ((struct sockaddr_in *)(ret->local_addr.buf))->sin_addr.s_addr =
2N/A htonl(INADDR_ANY);
2N/A
2N/A /* store device name, constructing if necessary */
2N/A if (proto[0] != '/') {
2N/A sz = strlen("/dev/") + strlen(proto) + 1;
2N/A if ((ret->dev_name = malloc(sz)) == NULL)
2N/A goto fail;
2N/A (void) snprintf(ret->dev_name, sz, "/dev/%s", proto);
2N/A } else if ((ret->dev_name = strdup(proto)) == NULL) {
2N/A goto fail;
2N/A }
2N/A
2N/A return (ret);
2N/A
2N/Afail:
2N/A destroy_tlx_info(ret);
2N/A return (NULL);
2N/A}
2N/A
2N/A/*
2N/A * Returns B_TRUE if this is a v6 protocol valid for both TLI and socket
2N/A * based services, else B_FALSE.
2N/A */
2N/Astatic boolean_t
2N/Av6_proto(const char *proto)
2N/A{
2N/A return ((strcmp(proto, SOCKET_PROTO_TCP6) == 0) ||
2N/A (strcmp(proto, SOCKET_PROTO_UDP6) == 0));
2N/A}
2N/A
2N/A/*
2N/A * Returns B_TRUE if this is a valid v6 protocol for a socket based service,
2N/A * else B_FALSE.
2N/A */
2N/Astatic boolean_t
2N/Av6_socket_proto(const char *proto)
2N/A{
2N/A return ((strcmp(proto, SOCKET_PROTO_SCTP6) == 0) ||
2N/A v6_proto(proto));
2N/A
2N/A}
2N/A
2N/Astatic boolean_t
2N/Avalid_socket_proto(const char *proto)
2N/A{
2N/A return (v6_socket_proto(proto) ||
2N/A (strcmp(proto, SOCKET_PROTO_SCTP) == 0) ||
2N/A (strcmp(proto, SOCKET_PROTO_TCP) == 0) ||
2N/A (strcmp(proto, SOCKET_PROTO_UDP) == 0));
2N/A}
2N/A
2N/A/*
2N/A * Free all the memory consumed by 'pi' associated with the instance
2N/A * with configuration 'cfg'.
2N/A */
2N/Astatic void
2N/Adestroy_proto_info(basic_cfg_t *cfg, proto_info_t *pi)
2N/A{
2N/A if (pi == NULL)
2N/A return;
2N/A
2N/A assert(pi->listen_fd == -1);
2N/A
2N/A free(pi->proto);
2N/A if (pi->ri != NULL)
2N/A destroy_rpc_info(pi->ri);
2N/A if (cfg->istlx) {
2N/A destroy_tlx_info((tlx_info_t *)pi);
2N/A } else {
2N/A free(pi);
2N/A }
2N/A}
2N/A
2N/Avoid
2N/Adestroy_proto_list(basic_cfg_t *cfg)
2N/A{
2N/A void *cookie = NULL;
2N/A proto_info_t *pi;
2N/A
2N/A if (cfg->proto_list == NULL)
2N/A return;
2N/A
2N/A while ((pi = uu_list_teardown(cfg->proto_list, &cookie)) != NULL)
2N/A destroy_proto_info(cfg, pi);
2N/A uu_list_destroy(cfg->proto_list);
2N/A cfg->proto_list = NULL;
2N/A}
2N/A
2N/Avoid
2N/Adestroy_basic_cfg(basic_cfg_t *cfg)
2N/A{
2N/A if (cfg == NULL)
2N/A return;
2N/A
2N/A free(cfg->bind_addr);
2N/A destroy_proto_list(cfg);
2N/A free(cfg->svc_name);
2N/A free(cfg);
2N/A}
2N/A
2N/A/*
2N/A * Overwrite the socket address with the address specified by the
2N/A * bind_addr property.
2N/A */
2N/Astatic int
2N/Aset_bind_addr(struct sockaddr_storage *ss, char *bind_addr)
2N/A{
2N/A struct addrinfo hints, *res;
2N/A
2N/A if (bind_addr == NULL || bind_addr[0] == '\0')
2N/A return (0);
2N/A
2N/A (void) memset(&hints, 0, sizeof (hints));
2N/A hints.ai_flags = AI_DEFAULT;
2N/A hints.ai_socktype = SOCK_STREAM;
2N/A hints.ai_family = ss->ss_family;
2N/A if (getaddrinfo(bind_addr, "", &hints, &res) != 0) {
2N/A return (-1);
2N/A } else {
2N/A void *p = res->ai_addr;
2N/A struct sockaddr_storage *newss = p;
2N/A
2N/A (void) memcpy(SS_SINADDR(*ss), SS_SINADDR(*newss),
2N/A SS_ADDRLEN(*ss));
2N/A freeaddrinfo(res);
2N/A return (0);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * valid_props validates all the properties in an array of inetd_prop_t's,
2N/A * marking each property as valid or invalid. If any properties are invalid,
2N/A * it returns B_FALSE, otherwise it returns B_TRUE. Note that some properties
2N/A * are interdependent, so if one is invalid, it leaves others in an
2N/A * indeterminate state (such as ISRPC and SVC_NAME). In this case, the
2N/A * indeterminate property will be marked valid. IE, the only properties
2N/A * marked invalid are those that are KNOWN to be invalid.
2N/A *
2N/A * Piggy-backed onto this validation if 'fmri' is non-NULL is the construction
2N/A * of a structured configuration, a basic_cfg_t, which is used by inetd.
2N/A * If 'fmri' is set then the latter three parameters need to be set to
2N/A * non-NULL values, and if the configuration is valid, the storage referenced
2N/A * by cfgpp is set to point at an initialized basic_cfg_t.
2N/A */
2N/Aboolean_t
2N/Avalid_props(inetd_prop_t *prop, const char *fmri, basic_cfg_t **cfgpp,
2N/A uu_list_pool_t *proto_info_pool, uu_list_pool_t *tlx_ci_pool)
2N/A{
2N/A char *bufp, *cp;
2N/A boolean_t ret = B_TRUE;
2N/A int i;
2N/A long uidl;
2N/A boolean_t isrpc;
2N/A int sock_type_id;
2N/A int rpc_pnum;
2N/A int rpc_lv, rpc_hv;
2N/A basic_cfg_t *cfg;
2N/A char *proto = NULL;
2N/A int pi;
2N/A char **netids = NULL;
2N/A int ni = 0;
2N/A
2N/A if (fmri != NULL)
2N/A assert((cfgpp != NULL) && (proto_info_pool != NULL) &&
2N/A (tlx_ci_pool != NULL));
2N/A
2N/A /*
2N/A * Set all checkable properties to valid as a baseline. We'll be
2N/A * marking all invalid properties.
2N/A */
2N/A for (i = 0; prop[i].ip_name != NULL; i++) {
2N/A if (prop[i].ip_error != IVE_UNSET)
2N/A prop[i].ip_error = IVE_VALID;
2N/A }
2N/A
2N/A if (((cfg = calloc(1, sizeof (basic_cfg_t))) == NULL) ||
2N/A ((fmri != NULL) &&
2N/A ((cfg->proto_list = uu_list_create(proto_info_pool, NULL, 0)) ==
2N/A NULL))) {
2N/A free(cfg);
2N/A return (B_FALSE);
2N/A }
2N/A
2N/A /* Check a service name was supplied */
2N/A if ((prop[PT_SVC_NAME_INDEX].ip_error == IVE_UNSET) ||
2N/A ((cfg->svc_name =
2N/A strdup(prop[PT_SVC_NAME_INDEX].ip_value.iv_string)) == NULL))
2N/A prop[PT_SVC_NAME_INDEX].ip_error = IVE_INVALID;
2N/A
2N/A /* Check that iswait and isrpc have valid boolean values */
2N/A
2N/A if ((prop[PT_ISWAIT_INDEX].ip_error == IVE_UNSET) ||
2N/A (((cfg->iswait = prop[PT_ISWAIT_INDEX].ip_value.iv_boolean) !=
2N/A B_TRUE) && (cfg->iswait != B_FALSE)))
2N/A prop[PT_ISWAIT_INDEX].ip_error = IVE_INVALID;
2N/A
2N/A if ((prop[PT_ISRPC_INDEX].ip_error == IVE_UNSET) ||
2N/A (((isrpc = prop[PT_ISRPC_INDEX].ip_value.iv_boolean) != B_TRUE) &&
2N/A (isrpc != B_FALSE))) {
2N/A prop[PT_ISRPC_INDEX].ip_error = IVE_INVALID;
2N/A } else if (isrpc) {
2N/A /*
2N/A * This is an RPC service, so ensure that the RPC version
2N/A * numbers are zero or greater, that the low version isn't
2N/A * greater than the high version and a valid program name
2N/A * is supplied.
2N/A */
2N/A
2N/A if ((prop[PT_RPC_LW_VER_INDEX].ip_error == IVE_UNSET) ||
2N/A ((rpc_lv = prop[PT_RPC_LW_VER_INDEX].ip_value.iv_int) <
2N/A 0))
2N/A prop[PT_RPC_LW_VER_INDEX].ip_error = IVE_INVALID;
2N/A
2N/A if ((prop[PT_RPC_HI_VER_INDEX].ip_error == IVE_UNSET) ||
2N/A ((rpc_hv = prop[PT_RPC_HI_VER_INDEX].ip_value.iv_int) <
2N/A 0))
2N/A prop[PT_RPC_HI_VER_INDEX].ip_error = IVE_INVALID;
2N/A
2N/A if ((prop[PT_RPC_LW_VER_INDEX].ip_error != IVE_INVALID) &&
2N/A (prop[PT_RPC_HI_VER_INDEX].ip_error != IVE_INVALID) &&
2N/A (rpc_lv > rpc_hv)) {
2N/A prop[PT_RPC_LW_VER_INDEX].ip_error = IVE_INVALID;
2N/A prop[PT_RPC_HI_VER_INDEX].ip_error = IVE_INVALID;
2N/A }
2N/A
2N/A if ((cfg->svc_name != NULL) &&
2N/A ((rpc_pnum = get_rpc_prognum(cfg->svc_name)) == -1))
2N/A prop[PT_SVC_NAME_INDEX].ip_error = IVE_INVALID;
2N/A }
2N/A
2N/A /* Check that the socket type is one of the acceptable values. */
2N/A cfg->istlx = B_FALSE;
2N/A if ((prop[PT_SOCK_TYPE_INDEX].ip_error == IVE_UNSET) ||
2N/A ((sock_type_id = get_sock_type_id(
2N/A prop[PT_SOCK_TYPE_INDEX].ip_value.iv_string)) == -1) &&
2N/A !(cfg->istlx = is_tlx_service(prop)))
2N/A prop[PT_SOCK_TYPE_INDEX].ip_error = IVE_INVALID;
2N/A
2N/A /* Get the bind address */
2N/A if (!cfg->istlx && prop[PT_BIND_ADDR_INDEX].ip_error != IVE_UNSET &&
2N/A (cfg->bind_addr =
2N/A strdup(prop[PT_BIND_ADDR_INDEX].ip_value.iv_string)) == NULL)
2N/A prop[PT_BIND_ADDR_INDEX].ip_error = IVE_INVALID;
2N/A
2N/A /*
2N/A * Iterate through all the different protos/netids resulting from the
2N/A * proto property and check that they're valid and perform checks on
2N/A * other fields that are tied-in with the proto.
2N/A */
2N/A
2N/A pi = 0;
2N/A do {
2N/A socket_info_t *si = NULL;
2N/A tlx_info_t *ti = NULL;
2N/A proto_info_t *p_inf = NULL;
2N/A boolean_t v6only = B_FALSE;
2N/A char *only;
2N/A boolean_t invalid_proto = B_FALSE;
2N/A char **protos;
2N/A struct protoent pe;
2N/A char gpbuf[1024];
2N/A struct netconfig *nconf = NULL;
2N/A
2N/A /*
2N/A * If we don't know whether it's an rpc service or its
2N/A * endpoint type, we can't do any of the proto checks as we
2N/A * have no context; break out.
2N/A */
2N/A if ((prop[PT_ISRPC_INDEX].ip_error != IVE_VALID) ||
2N/A (prop[PT_SOCK_TYPE_INDEX].ip_error != IVE_VALID))
2N/A break;
2N/A
2N/A /* skip proto specific processing if the proto isn't set. */
2N/A if (prop[PT_PROTO_INDEX].ip_error == IVE_UNSET) {
2N/A invalid_proto = B_TRUE;
2N/A goto past_proto_processing;
2N/A }
2N/A protos = prop[PT_PROTO_INDEX].ip_value.iv_string_list;
2N/A
2N/A /*
2N/A * Get the next netid/proto.
2N/A */
2N/A
2N/A if (!cfg->istlx || !isrpc) {
2N/A proto = protos[pi++];
2N/A /*
2N/A * This is a TLI/RPC service, so get the next netid, expanding
2N/A * any supplied nettype.
2N/A */
2N/A } else if ((netids == NULL) ||
2N/A ((proto = netids[ni++]) == NULL)) {
2N/A /*
2N/A * Either this is the first time around or
2N/A * we've exhausted the last set of netids, so
2N/A * try and get the next set using the currently
2N/A * indexed proto entry.
2N/A */
2N/A
2N/A if (netids != NULL) {
2N/A destroy_strings(netids);
2N/A netids = NULL;
2N/A }
2N/A
2N/A if (protos[pi] != NULL) {
2N/A if ((netids = get_netids(protos[pi++])) ==
2N/A NULL) {
2N/A invalid_proto = B_TRUE;
2N/A proto = protos[pi - 1];
2N/A } else {
2N/A ni = 0;
2N/A proto = netids[ni++];
2N/A }
2N/A } else {
2N/A proto = NULL;
2N/A }
2N/A }
2N/A
2N/A if (proto == NULL)
2N/A break;
2N/A
2N/A if (invalid_proto)
2N/A goto past_proto_processing;
2N/A
2N/A /* strip a trailing only to simplify further processing */
2N/A only = proto + strlen(proto) - (sizeof ("6only") - 1);
2N/A if ((only > proto) && (strcmp(only, "6only") == 0)) {
2N/A *++only = '\0';
2N/A v6only = B_TRUE;
2N/A }
2N/A
2N/A /* validate the proto/netid */
2N/A
2N/A if (!cfg->istlx) {
2N/A if (!valid_socket_proto(proto))
2N/A invalid_proto = B_TRUE;
2N/A } else {
2N/A /*
2N/A * Check if we've got a valid netid. If
2N/A * getnetconfigent() fails, we check to see whether
2N/A * we've got a v6 netid that may have been rejected
2N/A * because no IPv6 interface was configured before
2N/A * flagging 'proto' as invalid. If the latter condition
2N/A * holds, we don't flag the proto as invalid, and
2N/A * leave inetd to handle the value appropriately
2N/A * when it tries to listen on behalf of the service.
2N/A */
2N/A if (((nconf = getnetconfigent(proto)) == NULL) &&
2N/A !v6_proto(proto))
2N/A invalid_proto = B_TRUE;
2N/A }
2N/A if (invalid_proto)
2N/A goto past_proto_processing;
2N/A
2N/A /*
2N/A * dissallow datagram type nowait services
2N/A */
2N/A if ((prop[PT_ISWAIT_INDEX].ip_error == IVE_VALID) &&
2N/A !cfg->iswait) {
2N/A if (strncmp(proto, SOCKET_PROTO_UDP,
2N/A sizeof (SOCKET_PROTO_UDP) - 1) == 0) {
2N/A invalid_proto = B_TRUE;
2N/A } else if (cfg->istlx && (nconf != NULL) &&
2N/A (nconf->nc_semantics == NC_TPI_CLTS)) {
2N/A invalid_proto = B_TRUE;
2N/A }
2N/A if (invalid_proto) {
2N/A prop[PT_ISWAIT_INDEX].ip_error = IVE_INVALID;
2N/A goto past_proto_processing;
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * We're running in validate only mode. Don't bother creating
2N/A * any proto structures (they don't do any further validation).
2N/A */
2N/A if (fmri == NULL)
2N/A goto past_proto_processing;
2N/A
2N/A /*
2N/A * Create the apropriate transport info structure.
2N/A */
2N/A if (cfg->istlx) {
2N/A if ((ti = create_tlx_info(proto, tlx_ci_pool)) != NULL)
2N/A p_inf = (proto_info_t *)ti;
2N/A } else {
2N/A struct sockaddr_storage *ss;
2N/A
2N/A if ((si = calloc(1, sizeof (socket_info_t))) != NULL) {
2N/A p_inf = (proto_info_t *)si;
2N/A si->type = sock_type_id;
2N/A ss = &si->local_addr;
2N/A
2N/A if (v6_socket_proto(proto)) {
2N/A ss->ss_family = AF_INET6;
2N/A /* already in network order */
2N/A ((struct sockaddr_in6 *)ss)->sin6_addr =
2N/A in6addr_any;
2N/A } else {
2N/A ss->ss_family = AF_INET;
2N/A ((struct sockaddr_in *)ss)->sin_addr.
2N/A s_addr = htonl(INADDR_ANY);
2N/A }
2N/A if (set_bind_addr(ss, cfg->bind_addr) != 0) {
2N/A prop[PT_BIND_ADDR_INDEX].ip_error =
2N/A IVE_INVALID;
2N/A }
2N/A }
2N/A }
2N/A if (p_inf == NULL) {
2N/A invalid_proto = B_TRUE;
2N/A goto past_proto_processing;
2N/A }
2N/A
2N/A p_inf->v6only = v6only;
2N/A
2N/A /*
2N/A * Store the supplied proto string for error reporting,
2N/A * re-attaching the 'only' suffix if one was taken off.
2N/A */
2N/A if ((p_inf->proto = malloc(strlen(proto) + 5)) == NULL) {
2N/A invalid_proto = B_TRUE;
2N/A goto past_proto_processing;
2N/A } else {
2N/A (void) strlcpy(p_inf->proto, proto, strlen(proto) + 5);
2N/A if (v6only)
2N/A (void) strlcat(p_inf->proto, "only",
2N/A strlen(proto) + 5);
2N/A }
2N/A
2N/A /*
2N/A * Validate and setup RPC/non-RPC specifics.
2N/A */
2N/A
2N/A if (isrpc) {
2N/A rpc_info_t *ri;
2N/A
2N/A if ((rpc_pnum != -1) && (rpc_lv != -1) &&
2N/A (rpc_hv != -1)) {
2N/A if ((ri = create_rpc_info(proto, rpc_pnum,
2N/A rpc_lv, rpc_hv)) == NULL) {
2N/A invalid_proto = B_TRUE;
2N/A } else {
2N/A p_inf->ri = ri;
2N/A }
2N/A }
2N/A }
2N/A
2N/Apast_proto_processing:
2N/A /* validate non-RPC service name */
2N/A if (!isrpc && (cfg->svc_name != NULL)) {
2N/A struct servent se;
2N/A char gsbuf[NSS_BUFLEN_SERVICES];
2N/A char *gsproto = proto;
2N/A
2N/A if (invalid_proto) {
2N/A /*
2N/A * Make getservbyname_r do its lookup without a
2N/A * proto.
2N/A */
2N/A gsproto = NULL;
2N/A } else if (gsproto != NULL) {
2N/A /*
2N/A * Since getservbyname & getprotobyname don't
2N/A * support tcp6, udp6 or sctp6 take off the 6
2N/A * digit from protocol.
2N/A */
2N/A if (v6_socket_proto(gsproto))
2N/A gsproto[strlen(gsproto) - 1] = '\0';
2N/A }
2N/A
2N/A if (getservbyname_r(cfg->svc_name, gsproto, &se, gsbuf,
2N/A sizeof (gsbuf)) == NULL) {
2N/A if (gsproto != NULL)
2N/A invalid_proto = B_TRUE;
2N/A prop[PT_SVC_NAME_INDEX].ip_error = IVE_INVALID;
2N/A } else if (cfg->istlx && (ti != NULL)) {
2N/A SS_SETPORT(*(struct sockaddr_storage *)
2N/A ti->local_addr.buf, se.s_port);
2N/A } else if (!cfg->istlx && (si != NULL)) {
2N/A if ((gsproto != NULL) &&
2N/A getprotobyname_r(gsproto, &pe, gpbuf,
2N/A sizeof (gpbuf)) == NULL) {
2N/A invalid_proto = B_TRUE;
2N/A } else {
2N/A si->protocol = pe.p_proto;
2N/A }
2N/A SS_SETPORT(si->local_addr, se.s_port);
2N/A }
2N/A
2N/A }
2N/A
2N/A if (p_inf != NULL) {
2N/A p_inf->listen_fd = -1;
2N/A
2N/A /* add new proto entry to proto_list */
2N/A uu_list_node_init(p_inf, &p_inf->link, proto_info_pool);
2N/A (void) uu_list_insert_after(cfg->proto_list, NULL,
2N/A p_inf);
2N/A }
2N/A
2N/A if (nconf != NULL)
2N/A freenetconfigent(nconf);
2N/A if (invalid_proto)
2N/A prop[PT_PROTO_INDEX].ip_error = IVE_INVALID;
2N/A } while (proto != NULL); /* while just processed a proto */
2N/A
2N/A /*
2N/A * Check that the exec string for the start method actually exists and
2N/A * that the user is either a valid username or uid. Note we don't
2N/A * mandate the setting of these fields, and don't do any checks
2N/A * for arg0, hence its absence.
2N/A */
2N/A
2N/A if (prop[PT_EXEC_INDEX].ip_error != IVE_UNSET) {
2N/A /* Don't pass any arguments to access() */
2N/A if ((bufp = strdup(
2N/A prop[PT_EXEC_INDEX].ip_value.iv_string)) == NULL) {
2N/A prop[PT_EXEC_INDEX].ip_error = IVE_INVALID;
2N/A } else {
2N/A if ((cp = strpbrk(bufp, " \t")) != NULL)
2N/A *cp = '\0';
2N/A
2N/A if ((access(bufp, F_OK) == -1) && (errno == ENOENT))
2N/A prop[PT_EXEC_INDEX].ip_error = IVE_INVALID;
2N/A free(bufp);
2N/A }
2N/A }
2N/A
2N/A if (prop[PT_USER_INDEX].ip_error != IVE_UNSET) {
2N/A char pw_buf[NSS_BUFLEN_PASSWD];
2N/A struct passwd pw;
2N/A
2N/A if (getpwnam_r(prop[PT_USER_INDEX].ip_value.iv_string, &pw,
2N/A pw_buf, NSS_BUFLEN_PASSWD) == NULL) {
2N/A errno = 0;
2N/A uidl = strtol(prop[PT_USER_INDEX].ip_value.iv_string,
2N/A &bufp, 10);
2N/A if ((errno != 0) || (*bufp != '\0') ||
2N/A (getpwuid_r(uidl, &pw, pw_buf,
2N/A NSS_BUFLEN_PASSWD) == NULL))
2N/A prop[PT_USER_INDEX].ip_error = IVE_INVALID;
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * Iterate through the properties in the array verifying that any
2N/A * default properties are valid, and setting the return boolean
2N/A * according to whether any properties were marked invalid.
2N/A */
2N/A
2N/A for (i = 0; prop[i].ip_name != NULL; i++) {
2N/A if (prop[i].ip_error == IVE_UNSET)
2N/A continue;
2N/A
2N/A if (prop[i].ip_default &&
2N/A !valid_default_prop(prop[i].ip_name, &prop[i].ip_value))
2N/A prop[i].ip_error = IVE_INVALID;
2N/A
2N/A if (prop[i].ip_error == IVE_INVALID)
2N/A ret = B_FALSE;
2N/A }
2N/A
2N/A /* pass back the basic_cfg_t if requested and it's a valid config */
2N/A if ((cfgpp != NULL) && ret) {
2N/A *cfgpp = cfg;
2N/A } else {
2N/A destroy_basic_cfg(cfg);
2N/A }
2N/A
2N/A return (ret);
2N/A}
2N/A
2N/A/*
2N/A * validate_default_prop takes the name of an inetd property, and a value
2N/A * for that property. It returns B_TRUE if the property is valid, and B_FALSE
2N/A * if the proposed value isn't valid for that property.
2N/A */
2N/A
2N/Aboolean_t
2N/Avalid_default_prop(const char *name, const void *value)
2N/A{
2N/A int i;
2N/A
2N/A for (i = 0; inetd_properties[i].ip_name != NULL; i++) {
2N/A if (strcmp(name, inetd_properties[i].ip_name) != 0)
2N/A continue;
2N/A if (!inetd_properties[i].ip_default)
2N/A return (B_FALSE);
2N/A
2N/A switch (inetd_properties[i].ip_type) {
2N/A case INET_TYPE_INTEGER:
2N/A if (*((int64_t *)value) >= -1)
2N/A return (B_TRUE);
2N/A else
2N/A return (B_FALSE);
2N/A case INET_TYPE_BOOLEAN:
2N/A if ((*((boolean_t *)value) == B_FALSE) ||
2N/A (*((boolean_t *)value) == B_TRUE))
2N/A return (B_TRUE);
2N/A else
2N/A return (B_FALSE);
2N/A case INET_TYPE_COUNT:
2N/A case INET_TYPE_STRING_LIST:
2N/A case INET_TYPE_STRING:
2N/A return (B_TRUE);
2N/A }
2N/A }
2N/A
2N/A return (B_FALSE);
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/Ascf_error_t
2N/Aread_prop(scf_handle_t *h, inetd_prop_t *iprop, int index, const char *inst,
2N/A const char *pg_name)
2N/A{
2N/A scf_simple_prop_t *sprop;
2N/A uint8_t *tmp_bool;
2N/A int64_t *tmp_int;
2N/A uint64_t *tmp_cnt;
2N/A char *tmp_char;
2N/A
2N/A if ((sprop = scf_simple_prop_get(h, inst, pg_name, iprop->ip_name)) ==
2N/A NULL)
2N/A return (scf_error());
2N/A
2N/A switch (iprop->ip_type) {
2N/A case INET_TYPE_STRING:
2N/A if ((tmp_char = scf_simple_prop_next_astring(sprop)) == NULL)
2N/A goto scf_error;
2N/A if ((iprop->ip_value.iv_string = strdup(tmp_char)) == NULL) {
2N/A scf_simple_prop_free(sprop);
2N/A return (SCF_ERROR_NO_MEMORY);
2N/A }
2N/A break;
2N/A case INET_TYPE_STRING_LIST:
2N/A {
2N/A int j = 0;
2N/A
2N/A while ((tmp_char =
2N/A scf_simple_prop_next_astring(sprop)) != NULL) {
2N/A char **cpp;
2N/A
2N/A if ((cpp = realloc(
2N/A iprop->ip_value.iv_string_list,
2N/A (j + 2) * sizeof (char *))) == NULL) {
2N/A scf_simple_prop_free(sprop);
2N/A return (SCF_ERROR_NO_MEMORY);
2N/A }
2N/A iprop->ip_value.iv_string_list = cpp;
2N/A if ((cpp[j] = strdup(tmp_char)) == NULL) {
2N/A scf_simple_prop_free(sprop);
2N/A return (SCF_ERROR_NO_MEMORY);
2N/A }
2N/A cpp[++j] = NULL;
2N/A }
2N/A if ((j == 0) || (scf_error() != SCF_ERROR_NONE))
2N/A goto scf_error;
2N/A }
2N/A break;
2N/A case INET_TYPE_BOOLEAN:
2N/A if ((tmp_bool = scf_simple_prop_next_boolean(sprop)) == NULL)
2N/A goto scf_error;
2N/A iprop->ip_value.iv_boolean =
2N/A (*tmp_bool == 0) ? B_FALSE : B_TRUE;
2N/A break;
2N/A case INET_TYPE_COUNT:
2N/A if ((tmp_cnt = scf_simple_prop_next_count(sprop)) == NULL)
2N/A goto scf_error;
2N/A iprop->ip_value.iv_cnt = *tmp_cnt;
2N/A break;
2N/A case INET_TYPE_INTEGER:
2N/A if ((tmp_int = scf_simple_prop_next_integer(sprop)) == NULL)
2N/A goto scf_error;
2N/A iprop->ip_value.iv_int = *tmp_int;
2N/A break;
2N/A default:
2N/A assert(0);
2N/A }
2N/A
2N/A iprop->ip_error = IVE_VALID;
2N/A scf_simple_prop_free(sprop);
2N/A return (0);
2N/A
2N/Ascf_error:
2N/A scf_simple_prop_free(sprop);
2N/A if (scf_error() == SCF_ERROR_NONE)
2N/A return (SCF_ERROR_NOT_FOUND);
2N/A return (scf_error());
2N/A}
2N/A
2N/A/*
2N/A * read_props reads either the full set of properties for instance 'instance'
2N/A * (including defaults - pulling them in from inetd where necessary) if
2N/A * 'instance' is non-null, else just the defaults from inetd. The properties
2N/A * are returned in an allocated inetd_prop_t array, which must be freed
2N/A * using free_instance_props(). If an error occurs NULL is returned and 'err'
2N/A * is set to indicate the cause, else a pointer to the read properties is
2N/A * returned.
2N/A */
2N/Astatic inetd_prop_t *
2N/Aread_props(scf_handle_t *h, const char *instance, size_t *num_elements,
2N/A scf_error_t *err)
2N/A{
2N/A inetd_prop_t *ret = NULL;
2N/A int i;
2N/A boolean_t defaults_only = (instance == NULL);
2N/A
2N/A if ((ret = malloc(sizeof (inetd_properties))) == NULL) {
2N/A *err = SCF_ERROR_NO_MEMORY;
2N/A return (NULL);
2N/A }
2N/A (void) memcpy(ret, &inetd_properties, sizeof (inetd_properties));
2N/A
2N/A if (defaults_only)
2N/A instance = INETD_INSTANCE_FMRI;
2N/A for (i = 0; ret[i].ip_name != NULL; i++) {
2N/A if (defaults_only && !ret[i].ip_default)
2N/A continue;
2N/A
2N/A switch (*err = read_prop(h, &ret[i], i, instance,
2N/A defaults_only ? PG_NAME_SERVICE_DEFAULTS : ret[i].ip_pg)) {
2N/A case 0:
2N/A break;
2N/A case SCF_ERROR_INVALID_ARGUMENT:
2N/A goto failure_cleanup;
2N/A case SCF_ERROR_NOT_FOUND:
2N/A /*
2N/A * In non-default-only mode where we're reading a
2N/A * default property, since the property wasn't
2N/A * found in the instance, try and read inetd's default
2N/A * value.
2N/A */
2N/A if (!ret[i].ip_default || defaults_only)
2N/A continue;
2N/A switch (*err = read_prop(h, &ret[i], i,
2N/A INETD_INSTANCE_FMRI, PG_NAME_SERVICE_DEFAULTS)) {
2N/A case 0:
2N/A ret[i].from_inetd = B_TRUE;
2N/A continue;
2N/A case SCF_ERROR_NOT_FOUND:
2N/A continue;
2N/A default:
2N/A goto failure_cleanup;
2N/A }
2N/A default:
2N/A goto failure_cleanup;
2N/A }
2N/A }
2N/A
2N/A *num_elements = i;
2N/A return (ret);
2N/A
2N/Afailure_cleanup:
2N/A free_instance_props(ret);
2N/A return (NULL);
2N/A}
2N/A
2N/A/*
2N/A * Read all properties applicable to 'instance' (including defaults).
2N/A */
2N/Ainetd_prop_t *
2N/Aread_instance_props(scf_handle_t *h, const char *instance, size_t *num_elements,
2N/A scf_error_t *err)
2N/A{
2N/A return (read_props(h, instance, num_elements, err));
2N/A}
2N/A
2N/A/*
2N/A * Read the default properties from inetd's defaults property group.
2N/A */
2N/Ainetd_prop_t *
2N/Aread_default_props(scf_handle_t *h, size_t *num_elements, scf_error_t *err)
2N/A{
2N/A return (read_props(h, NULL, num_elements, err));
2N/A}
2N/A
2N/Avoid
2N/Afree_instance_props(inetd_prop_t *prop)
2N/A{
2N/A int i;
2N/A
2N/A if (prop == NULL)
2N/A return;
2N/A
2N/A for (i = 0; prop[i].ip_name != NULL; i++) {
2N/A if (prop[i].ip_type == INET_TYPE_STRING) {
2N/A free(prop[i].ip_value.iv_string);
2N/A } else if (prop[i].ip_type == INET_TYPE_STRING_LIST) {
2N/A destroy_strings(prop[i].ip_value.iv_string_list);
2N/A }
2N/A }
2N/A free(prop);
2N/A}
2N/A
2N/Aint
2N/Aconnect_to_inetd(void)
2N/A{
2N/A struct sockaddr_un addr;
2N/A int fd;
2N/A
2N/A fd = socket(AF_UNIX, SOCK_STREAM, 0);
2N/A if (fd < 0)
2N/A return (-1);
2N/A
2N/A (void) memset(&addr, 0, sizeof (addr));
2N/A addr.sun_family = AF_UNIX;
2N/A /* CONSTCOND */
2N/A assert(sizeof (INETD_UDS_PATH) <= sizeof (addr.sun_path));
2N/A (void) strlcpy(addr.sun_path, INETD_UDS_PATH,
2N/A sizeof (addr.sun_path));
2N/A
2N/A if (connect(fd, (struct sockaddr *)&addr, sizeof (addr)) < 0) {
2N/A (void) close(fd);
2N/A return (-1);
2N/A }
2N/A
2N/A return (fd);
2N/A}
2N/A
2N/A/*
2N/A * refresh_inetd requests that inetd re-read all of the information that it's
2N/A * monitoring.
2N/A */
2N/A
2N/Aint
2N/Arefresh_inetd(void)
2N/A{
2N/A uds_request_t req;
2N/A int fd;
2N/A
2N/A if ((fd = connect_to_inetd()) < 0)
2N/A return (-1);
2N/A
2N/A req = UR_REFRESH_INETD;
2N/A if (send(fd, &req, sizeof (req), 0) < 0) {
2N/A (void) close(fd);
2N/A return (-1);
2N/A }
2N/A
2N/A (void) close(fd);
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Returns the id of the socket type 'type_str' that can be used in a call
2N/A * to socket(). If an unknown type string is passed returns -1, else the id.
2N/A */
2N/A
2N/Aint
2N/Aget_sock_type_id(const char *type_str)
2N/A{
2N/A int ret;
2N/A
2N/A if (strcmp(SOCKTYPE_STREAM_STR, type_str) == 0) {
2N/A ret = SOCK_STREAM;
2N/A } else if (strcmp(SOCKTYPE_DGRAM_STR, type_str) == 0) {
2N/A ret = SOCK_DGRAM;
2N/A } else if (strcmp(SOCKTYPE_RAW_STR, type_str) == 0) {
2N/A ret = SOCK_RAW;
2N/A } else if (strcmp(SOCKTYPE_SEQPKT_STR, type_str) == 0) {
2N/A ret = SOCK_SEQPACKET;
2N/A } else {
2N/A ret = -1;
2N/A }
2N/A return (ret);
2N/A}
2N/A
2N/A/*
2N/A * Takes either an RPC service name or number in string form as 'svc_name', and
2N/A * returns an integer format program number for the service. If the name isn't
2N/A * recognized as a valid RPC service name or isn't a valid number, -1 is
2N/A * returned, else the services program number.
2N/A */
2N/A
2N/Aint
2N/Aget_rpc_prognum(const char *svc_name)
2N/A{
2N/A struct rpcent rpc;
2N/A char buf[INETSVC_SVC_BUF_MAX];
2N/A int pnum;
2N/A char *endptr;
2N/A
2N/A if (getrpcbyname_r(svc_name, &rpc, buf, sizeof (buf)) != NULL)
2N/A return (rpc.r_number);
2N/A
2N/A pnum = strtol(svc_name, &endptr, 0);
2N/A if ((pnum == 0 && errno == EINVAL) ||
2N/A (pnum == LONG_MAX && errno == ERANGE) ||
2N/A pnum < 0 || *endptr != '\0') {
2N/A return (-1);
2N/A }
2N/A
2N/A return (pnum);
2N/A}
2N/A
2N/A/*
2N/A * calculate_hash calculates the MD5 message-digest of the file pathname.
2N/A * On success, hash is modified to point to the digest string and 0 is returned.
2N/A * Otherwise, -1 is returned and errno is set to indicate the error.
2N/A * The space for the digest string is obtained using malloc(3C) and should be
2N/A * freed by the caller.
2N/A */
2N/Aint
2N/Acalculate_hash(const char *pathname, char **hash)
2N/A{
2N/A int fd, i, serrno;
2N/A size_t len;
2N/A ssize_t n;
2N/A char *digest;
2N/A MD5_CTX md5_context;
2N/A unsigned char md5_digest[DIGEST_LEN];
2N/A unsigned char buf[READ_BUFSIZ];
2N/A
2N/A do {
2N/A fd = open(pathname, O_RDONLY);
2N/A } while (fd == -1 && errno == EINTR);
2N/A
2N/A if (fd == -1)
2N/A return (-1);
2N/A
2N/A /* allocate space for a 16-byte MD5 digest as a string of hex digits */
2N/A len = 2 * sizeof (md5_digest) + 1;
2N/A if ((digest = malloc(len)) == NULL) {
2N/A serrno = errno;
2N/A (void) close(fd);
2N/A errno = serrno;
2N/A return (-1);
2N/A }
2N/A
2N/A MD5Init(&md5_context);
2N/A
2N/A do {
2N/A if ((n = read(fd, buf, sizeof (buf))) > 0)
2N/A MD5Update(&md5_context, buf, n);
2N/A } while ((n > 0) || (n == -1 && errno == EINTR));
2N/A
2N/A serrno = errno;
2N/A MD5Final(md5_digest, &md5_context);
2N/A
2N/A (void) close(fd);
2N/A
2N/A if (n == -1) {
2N/A errno = serrno;
2N/A return (-1);
2N/A }
2N/A
2N/A for (i = 0; i < sizeof (md5_digest); i++) {
2N/A (void) snprintf(&digest[2 * i], len - (2 * i), "%02x",
2N/A md5_digest[i]);
2N/A }
2N/A *hash = digest;
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * retrieve_inetd_hash retrieves inetd's configuration file hash from the
2N/A * repository. On success, hash is modified to point to the hash string and
2N/A * SCF_ERROR_NONE is returned. Otherwise, the scf_error value is returned.
2N/A * The space for the hash string is obtained using malloc(3C) and should be
2N/A * freed by the caller.
2N/A */
2N/Ascf_error_t
2N/Aretrieve_inetd_hash(char **hash)
2N/A{
2N/A scf_simple_prop_t *sp;
2N/A char *hashstr, *s;
2N/A scf_error_t scf_err;
2N/A
2N/A if ((sp = scf_simple_prop_get(NULL, INETD_INSTANCE_FMRI, HASH_PG,
2N/A HASH_PROP)) == NULL)
2N/A return (scf_error());
2N/A
2N/A if ((hashstr = scf_simple_prop_next_astring(sp)) == NULL) {
2N/A scf_err = scf_error();
2N/A scf_simple_prop_free(sp);
2N/A return (scf_err);
2N/A }
2N/A
2N/A if ((s = strdup(hashstr)) == NULL) {
2N/A scf_simple_prop_free(sp);
2N/A return (SCF_ERROR_NO_MEMORY);
2N/A }
2N/A *hash = s;
2N/A scf_simple_prop_free(sp);
2N/A return (SCF_ERROR_NONE);
2N/A}
2N/A
2N/A/*
2N/A * store_inetd_hash stores the string hash in inetd's configuration file hash
2N/A * in the repository. On success, SCF_ERROR_NONE is returned. Otherwise, the
2N/A * scf_error value is returned.
2N/A */
2N/Ascf_error_t
2N/Astore_inetd_hash(const char *hash)
2N/A{
2N/A int ret;
2N/A scf_error_t rval = SCF_ERROR_NONE;
2N/A scf_handle_t *h;
2N/A scf_propertygroup_t *pg = NULL;
2N/A scf_instance_t *inst = NULL;
2N/A scf_transaction_t *tx = NULL;
2N/A scf_transaction_entry_t *txent = NULL;
2N/A scf_property_t *prop = NULL;
2N/A scf_value_t *val = NULL;
2N/A
2N/A if ((h = scf_handle_create(SCF_VERSION)) == NULL ||
2N/A scf_handle_bind(h) == -1)
2N/A goto error;
2N/A
2N/A if ((pg = scf_pg_create(h)) == NULL ||
2N/A (inst = scf_instance_create(h)) == NULL ||
2N/A scf_handle_decode_fmri(h, INETD_INSTANCE_FMRI, NULL, NULL, inst,
2N/A NULL, NULL, SCF_DECODE_FMRI_EXACT) == -1)
2N/A goto error;
2N/A
2N/A if (scf_instance_get_pg(inst, HASH_PG, pg) == -1) {
2N/A if (scf_error() != SCF_ERROR_NOT_FOUND ||
2N/A scf_instance_add_pg(inst, HASH_PG, SCF_GROUP_APPLICATION,
2N/A 0, pg) == -1)
2N/A goto error;
2N/A }
2N/A
2N/A if ((tx = scf_transaction_create(h)) == NULL ||
2N/A (txent = scf_entry_create(h)) == NULL ||
2N/A (prop = scf_property_create(h)) == NULL ||
2N/A (val = scf_value_create(h)) == NULL)
2N/A goto error;
2N/A
2N/A do {
2N/A if (scf_transaction_start(tx, pg) == -1)
2N/A goto error;
2N/A
2N/A if (scf_transaction_property_new(tx, txent, HASH_PROP,
2N/A SCF_TYPE_ASTRING) == -1 &&
2N/A scf_transaction_property_change_type(tx, txent, HASH_PROP,
2N/A SCF_TYPE_ASTRING) == -1)
2N/A goto error;
2N/A
2N/A if (scf_value_set_astring(val, hash) == -1 ||
2N/A scf_entry_add_value(txent, val) == -1)
2N/A goto error;
2N/A
2N/A if ((ret = scf_transaction_commit(tx)) == -1)
2N/A goto error;
2N/A
2N/A if (ret == 0) {
2N/A scf_transaction_reset(tx);
2N/A if (scf_pg_update(pg) == -1)
2N/A goto error;
2N/A }
2N/A } while (ret == 0);
2N/A
2N/A goto success;
2N/A
2N/Aerror:
2N/A rval = scf_error();
2N/A
2N/Asuccess:
2N/A scf_value_destroy(val);
2N/A scf_property_destroy(prop);
2N/A scf_entry_destroy(txent);
2N/A scf_transaction_destroy(tx);
2N/A scf_instance_destroy(inst);
2N/A scf_pg_destroy(pg);
2N/A scf_handle_destroy(h);
2N/A return (rval);
2N/A}
2N/A
2N/A/*
2N/A * This is a wrapper function for inet_ntop(). In case the af is AF_INET6
2N/A * and the address pointed by src is a IPv4-mapped IPv6 address, it returns
2N/A * a printable IPv4 address, not an IPv4-mapped IPv6 address. In other cases it
2N/A * behaves just like inet_ntop().
2N/A */
2N/Aconst char *
2N/Ainet_ntop_native(int af, const void *addr, char *dst, size_t size)
2N/A{
2N/A struct in_addr v4addr;
2N/A
2N/A if ((af == AF_INET6) && IN6_IS_ADDR_V4MAPPED((struct in6_addr *)addr)) {
2N/A IN6_V4MAPPED_TO_INADDR((struct in6_addr *)addr, &v4addr);
2N/A return (inet_ntop(AF_INET, &v4addr, dst, size));
2N/A } else {
2N/A return (inet_ntop(af, addr, dst, size));
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * inetd specific setproctitle. It sets the title so that it contains
2N/A * 'svc_name' followed by, if obtainable, the address of the remote end of
2N/A * socket 's'.
2N/A * NOTE: The argv manipulation in this function should be replaced when a
2N/A * common version of setproctitle is made available.
2N/A */
2N/Avoid
2N/Asetproctitle(const char *svc_name, int s, char *argv[])
2N/A{
2N/A socklen_t size;
2N/A struct sockaddr_storage ss;
2N/A char abuf[INET6_ADDRSTRLEN];
2N/A
2N/A static char buf[80];
2N/A
2N/A size = (socklen_t)sizeof (ss);
2N/A if (getpeername(s, (struct sockaddr *)&ss, &size) == 0) {
2N/A (void) snprintf(buf, sizeof (buf), "-%s [%s]", svc_name,
2N/A inet_ntop_native(ss.ss_family, (ss.ss_family == AF_INET6 ?
2N/A (void *)&((struct sockaddr_in6 *)(&ss))->sin6_addr :
2N/A (void *)&((struct sockaddr_in *)(&ss))->sin_addr), abuf,
2N/A sizeof (abuf)));
2N/A } else {
2N/A (void) snprintf(buf, sizeof (buf), "-%s", svc_name);
2N/A }
2N/A
2N/A /* we set argv[0] to point at our static storage. */
2N/A argv[0] = buf;
2N/A argv[1] = NULL;
2N/A}
2N/A
2N/Astatic boolean_t
2N/Ainetd_builtin_srcport(in_port_t p)
2N/A{
2N/A p = ntohs(p);
2N/A
2N/A if ((p == IPPORT_ECHO) ||
2N/A (p == IPPORT_DISCARD) ||
2N/A (p == IPPORT_DAYTIME) ||
2N/A (p == IPPORT_CHARGEN) ||
2N/A (p == IPPORT_TIMESERVER)) {
2N/A return (B_TRUE);
2N/A } else {
2N/A return (B_FALSE);
2N/A }
2N/A}
2N/A
2N/A/* ARGSUSED0 */
2N/Astatic void
2N/Aalarm_handler(int sig)
2N/A{
2N/A exit(0);
2N/A}
2N/A
2N/A/*
2N/A * This function is a datagram service template. It acts as a datagram wait
2N/A * type server, waiting for datagrams to come in, and when they do passing
2N/A * their contents, as-well as the socket they came in on and the remote
2N/A * address, in a call to the callback function 'cb'. If no datagrams are
2N/A * received for DG_INACTIVITY_TIMEOUT seconds the function exits with code 0.
2N/A */
2N/Avoid
2N/Adg_template(void (*cb)(int, const struct sockaddr *, int, const void *, size_t),
2N/A int s, void *buf, size_t buflen)
2N/A{
2N/A struct sockaddr_storage sa;
2N/A socklen_t sa_size;
2N/A ssize_t i;
2N/A char tmp[BUFSIZ];
2N/A
2N/A (void) sigset(SIGALRM, alarm_handler);
2N/A
2N/A if (buf == NULL) {
2N/A buf = tmp;
2N/A buflen = sizeof (tmp);
2N/A }
2N/A for (;;) {
2N/A (void) alarm(DG_INACTIVITY_TIMEOUT);
2N/A sa_size = sizeof (sa);
2N/A if ((i = recvfrom(s, buf, buflen, 0, (struct sockaddr *)&sa,
2N/A &sa_size)) < 0) {
2N/A continue;
2N/A } else if (inetd_builtin_srcport(
2N/A ((struct sockaddr_in *)(&sa))->sin_port)) {
2N/A /* denial-of-service attack possibility - ignore it */
2N/A syslog(LOG_WARNING,
2N/A "Incoming datagram from internal inetd service received; ignoring.");
2N/A continue;
2N/A }
2N/A (void) alarm(0);
2N/A
2N/A cb(s, (struct sockaddr *)&sa, sa_size, buf, i);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * An extension of write() or sendto() that keeps trying until either the full
2N/A * request has completed or a non-EINTR error occurs. If 'to' is set to a
2N/A * non-NULL value, sendto() is extended, else write(). Returns 0 on success
2N/A * else -1.
2N/A */
2N/Aint
2N/Asafe_sendto_write(int fd, const void *buf, size_t sz, int flags,
2N/A const struct sockaddr *to, int tolen) {
2N/A
2N/A size_t cnt = 0;
2N/A ssize_t ret;
2N/A const char *cp = buf;
2N/A
2N/A do {
2N/A if (to == NULL) {
2N/A ret = write(fd, cp + cnt, sz - cnt);
2N/A } else {
2N/A ret = sendto(fd, cp + cnt, sz - cnt, flags, to, tolen);
2N/A }
2N/A
2N/A if (ret > 0)
2N/A cnt += ret;
2N/A } while ((cnt != sz) && (errno == EINTR));
2N/A
2N/A return ((cnt == sz) ? 0 : -1);
2N/A}
2N/A
2N/Aint
2N/Asafe_sendto(int fd, const void *buf, size_t sz, int flags,
2N/A const struct sockaddr *to, int tolen) {
2N/A return (safe_sendto_write(fd, buf, sz, flags, to, tolen));
2N/A}
2N/A
2N/Aint
2N/Asafe_write(int fd, const void *buf, size_t sz)
2N/A{
2N/A return (safe_sendto_write(fd, buf, sz, 0, NULL, 0));
2N/A}
2N/A
2N/A/*
2N/A * Free up the memory occupied by string array 'strs'.
2N/A */
2N/Avoid
2N/Adestroy_strings(char **strs)
2N/A{
2N/A int i = 0;
2N/A
2N/A if (strs != NULL) {
2N/A while (strs[i] != NULL)
2N/A free(strs[i++]);
2N/A free(strs);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Parse the proto list string into an allocated array of proto strings,
2N/A * returning a pointer to this array. If one of the protos is too big
2N/A * errno is set to E2BIG and NULL is returned; if memory allocation failure
2N/A * occurs errno is set to ENOMEM and NULL is returned; else on success
2N/A * a pointer the string array is returned.
2N/A */
2N/Achar **
2N/Aget_protos(const char *pstr)
2N/A{
2N/A char *cp;
2N/A int i = 0;
2N/A char **ret = NULL;
2N/A size_t max_proto_len = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
2N/A char *str;
2N/A
2N/A /* copy the parameter as strtok modifies its parameters */
2N/A if ((str = strdup(pstr)) == NULL)
2N/A goto malloc_failure;
2N/A
2N/A for (cp = strtok(str, PROTO_DELIMITERS); cp != NULL;
2N/A cp = strtok(NULL, PROTO_DELIMITERS)) {
2N/A char **cpp;
2N/A
2N/A if (strlen(cp) >= max_proto_len) {
2N/A destroy_strings(ret);
2N/A free(str);
2N/A errno = E2BIG;
2N/A return (NULL);
2N/A }
2N/A if ((cpp = realloc(ret, (i + 2) * sizeof (char *))) == NULL)
2N/A goto malloc_failure;
2N/A ret = cpp;
2N/A if ((cpp[i] = strdup(cp)) == NULL)
2N/A goto malloc_failure;
2N/A cpp[++i] = NULL;
2N/A }
2N/A
2N/A free(str);
2N/A return (ret);
2N/A
2N/Amalloc_failure:
2N/A destroy_strings(ret);
2N/A free(str);
2N/A errno = ENOMEM;
2N/A return (NULL);
2N/A}
2N/A
2N/A/*
2N/A * Returns an allocated string array of netids corresponding with 'proto'. The
2N/A * function first tries to interpret 'proto' as a nettype to get its netids.
2N/A * If this fails it tries to interpret it as a netid. If 'proto' is neither
2N/A * a nettype or a netid or a memory allocation failures occurs NULL is
2N/A * returned, else a pointer to an array of netids associated with 'proto' is
2N/A * returned.
2N/A */
2N/Achar **
2N/Aget_netids(char *proto)
2N/A{
2N/A void *handle;
2N/A struct netconfig *nconf;
2N/A char **netids = NULL;
2N/A char **cpp;
2N/A int i = 0;
2N/A
2N/A if (strcmp(proto, "*") == 0)
2N/A proto = "visible";
2N/A
2N/A if ((handle = __rpc_setconf(proto)) != NULL) {
2N/A /* expand nettype */
2N/A while ((nconf = __rpc_getconf(handle)) != NULL) {
2N/A if ((cpp = realloc(netids,
2N/A (i + 2) * sizeof (char *))) == NULL)
2N/A goto failure_cleanup;
2N/A netids = cpp;
2N/A if ((cpp[i] = strdup(nconf->nc_netid)) == NULL)
2N/A goto failure_cleanup;
2N/A cpp[++i] = NULL;
2N/A }
2N/A __rpc_endconf(handle);
2N/A } else {
2N/A if ((netids = malloc(2 * sizeof (char *))) == NULL)
2N/A return (NULL);
2N/A if ((netids[0] = strdup(proto)) == NULL) {
2N/A free(netids);
2N/A return (NULL);
2N/A }
2N/A netids[1] = NULL;
2N/A }
2N/A
2N/A return (netids);
2N/A
2N/Afailure_cleanup:
2N/A destroy_strings(netids);
2N/A __rpc_endconf(handle);
2N/A return (NULL);
2N/A}