/*
* 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
* 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.
*/
/*
* This library contains a set of routines that are shared amongst inetd,
* inetadm, inetconv and the formerly internal inetd services. Amongst the
* routines are ones for reading and validating the configuration of an
* inetd service, a routine for requesting inetd be refreshed, ones for
* reading, calculating and writing the hash of an inetd.conf file, and
* numerous utility routines shared amongst the formerly internal inetd
* services.
*/
#include <string.h>
#include <netdb.h>
#include <limits.h>
#include <errno.h>
#include <inetsvc.h>
#include <stdlib.h>
#include <unistd.h>
#include <nss_dbdefs.h>
#include <stdio.h>
#include <fcntl.h>
#include <pwd.h>
#include <md5.h>
#include <signal.h>
#include <syslog.h>
#include <libintl.h>
#include <stdlib.h>
#include <assert.h>
#include <libuutil.h>
{NULL},
};
/*
* Inactivity timer used by dg_template(). After this many seconds of network
* inactivity dg_template will cease listening for new datagrams and return.
*/
{
return ((strcmp(SOCKTYPE_TLI_STR,
}
/*
* Return a reference to the property table. Number of entries in table
* are returned in num_elements argument.
*/
{
return (&inetd_properties[0]);
}
/*
* find_prop takes an array of inetd_prop_t's, the name of an inetd
* property, the type expected, and returns a pointer to the matching member,
* or NULL.
*/
{
int i = 0;
i++;
return (NULL);
return (NULL);
return ((inetd_prop_t *)prop + i);
}
/*
* get_prop_value_int takes an array of inetd_prop_t's together with the name of
* an inetd property and returns the value of the property. It's expected that
* the property exists in the searched array.
*/
{
inetd_prop_t *p;
}
/*
* get_prop_value_count takes an array of inetd_prop_t's together with the name
* of an inetd property and returns the value of the property. It's expected
* that the property exists in the searched array.
*/
{
inetd_prop_t *p;
}
/*
* get_prop_value_boolean takes an array of inetd_prop_t's together with the
* name of an inetd property and returns the value of the property. It's
* expected that the property exists in the searched array.
*/
{
inetd_prop_t *p;
return (p->ip_value.iv_boolean);
}
/*
* get_prop_value_string takes an array of inetd_prop_t's together with
* the name of an inetd property and returns the value of the property.
* It's expected that the property exists in the searched array.
*/
const char *
{
inetd_prop_t *p;
}
/*
* get_prop_value_string_list takes an array of inetd_prop_t's together
* with the name of an inetd property and returns the value of the property.
* It's expected that the property exists in the searched array.
*/
const char **
{
inetd_prop_t *p;
return ((const char **)p->ip_value.iv_string_list);
}
/*
* put_prop_value_int takes an array of inetd_prop_t's, a name of an inetd
* property, and a value. It copies the value into the property
* in the array. It's expected that the property exists in the searched array.
*/
void
{
inetd_prop_t *p;
}
/*
* put_prop_value_count takes an array of inetd_prop_t's, a name of an inetd
* property, and a value. It copies the value into the property
* in the array. It's expected that the property exists in the searched array.
*/
void
{
inetd_prop_t *p;
}
/*
* put_prop_value_boolean takes an array of inetd_prop_t's, a name of an inetd
* property, and a value. It copies the value into the property
* in the array. It's expected that the property exists in the searched array.
*/
void
{
inetd_prop_t *p;
}
/*
* put_prop_value_string takes an array of inetd_prop_t's, a name of an inetd
* property, and a value. It duplicates the value into the property
* in the array, and returns B_TRUE for success and B_FALSE for failure. It's
* expected that the property exists in the searched array.
*/
{
inetd_prop_t *p;
return (B_FALSE);
}
return (B_FALSE);
return (B_TRUE);
}
/*
* put_prop_value_string_list takes an array of inetd_prop_t's, a name of an
* inetd property, and a value. It copies the value into the property
* in the array. It's expected that the property exists in the searched array.
*/
void
{
inetd_prop_t *p;
}
static void
{
}
}
/*
* If 'proto' is a valid netid, and no memory allocations fail, returns a
* pointer to an allocated and initialized rpc_info_t, else NULL.
*/
static rpc_info_t *
{
return (NULL);
return (NULL);
}
return (NULL);
}
/*
* Determine whether this is a loopback transport. If getnetconfigent()
* fails, we check to see whether it was the result of a v6 proto
* being specified and no IPv6 interface was configured on the system;
* if this holds, we know it must not be a loopback transport, else
* getnetconfigent() must be miss-behaving, so return an error.
*/
return (NULL);
}
return (ret);
}
void
{
return;
/* free up conn ind queue */
NULL) {
}
}
}
/*
* Allocate, initialize and return a pointer to a tlx_info_t structure.
* On memory allocation failure NULL is returned.
*/
static tlx_info_t *
{
return (NULL);
goto fail;
NULL)
goto fail;
/* LINTED E_BAD_PTR_CAST_ALIGN */
/* LINTED E_BAD_PTR_CAST_ALIGN */
/* store device name, constructing if necessary */
if (proto[0] != '/') {
goto fail;
goto fail;
}
return (ret);
fail:
return (NULL);
}
/*
* Returns B_TRUE if this is a v6 protocol valid for both TLI and socket
* based services, else B_FALSE.
*/
static boolean_t
{
}
/*
* Returns B_TRUE if this is a valid v6 protocol for a socket based service,
* else B_FALSE.
*/
static boolean_t
{
}
static boolean_t
{
return (v6_socket_proto(proto) ||
}
/*
* Free all the memory consumed by 'pi' associated with the instance
* with configuration 'cfg'.
*/
static void
{
return;
} else {
}
}
void
{
return;
}
void
{
return;
}
/*
* Overwrite the socket address with the address specified by the
* bind_addr property.
*/
static int
{
return (0);
return (-1);
} else {
SS_ADDRLEN(*ss));
return (0);
}
}
/*
* valid_props validates all the properties in an array of inetd_prop_t's,
* marking each property as valid or invalid. If any properties are invalid,
* it returns B_FALSE, otherwise it returns B_TRUE. Note that some properties
* are interdependent, so if one is invalid, it leaves others in an
* indeterminate state (such as ISRPC and SVC_NAME). In this case, the
* indeterminate property will be marked valid. IE, the only properties
* marked invalid are those that are KNOWN to be invalid.
*
* Piggy-backed onto this validation if 'fmri' is non-NULL is the construction
* of a structured configuration, a basic_cfg_t, which is used by inetd.
* If 'fmri' is set then the latter three parameters need to be set to
* non-NULL values, and if the configuration is valid, the storage referenced
* by cfgpp is set to point at an initialized basic_cfg_t.
*/
{
int i;
long uidl;
int sock_type_id;
int rpc_pnum;
int pi;
int ni = 0;
(tlx_ci_pool != NULL));
/*
* Set all checkable properties to valid as a baseline. We'll be
* marking all invalid properties.
*/
}
NULL))) {
return (B_FALSE);
}
/* Check a service name was supplied */
/* Check that iswait and isrpc have valid boolean values */
} else if (isrpc) {
/*
* This is an RPC service, so ensure that the RPC version
* numbers are zero or greater, that the low version isn't
* greater than the high version and a valid program name
* is supplied.
*/
0))
0))
}
}
/* Check that the socket type is one of the acceptable values. */
/* Get the bind address */
/*
* proto property and check that they're valid and perform checks on
* other fields that are tied-in with the proto.
*/
pi = 0;
do {
char *only;
char **protos;
/*
* If we don't know whether it's an rpc service or its
* endpoint type, we can't do any of the proto checks as we
* have no context; break out.
*/
break;
/* skip proto specific processing if the proto isn't set. */
goto past_proto_processing;
}
/*
*/
/*
* any supplied nettype.
*/
/*
* Either this is the first time around or
* we've exhausted the last set of netids, so
* try and get the next set using the currently
* indexed proto entry.
*/
}
NULL) {
} else {
ni = 0;
}
} else {
}
}
break;
if (invalid_proto)
goto past_proto_processing;
/* strip a trailing only to simplify further processing */
*++only = '\0';
}
if (!valid_socket_proto(proto))
} else {
/*
* Check if we've got a valid netid. If
* getnetconfigent() fails, we check to see whether
* we've got a v6 netid that may have been rejected
* because no IPv6 interface was configured before
* flagging 'proto' as invalid. If the latter condition
* holds, we don't flag the proto as invalid, and
* leave inetd to handle the value appropriately
* when it tries to listen on behalf of the service.
*/
}
if (invalid_proto)
goto past_proto_processing;
/*
* dissallow datagram type nowait services
*/
sizeof (SOCKET_PROTO_UDP) - 1) == 0) {
}
if (invalid_proto) {
goto past_proto_processing;
}
}
/*
* We're running in validate only mode. Don't bother creating
* any proto structures (they don't do any further validation).
*/
goto past_proto_processing;
/*
* Create the apropriate transport info structure.
*/
} else {
if (v6_socket_proto(proto)) {
/* already in network order */
} else {
}
}
}
}
goto past_proto_processing;
}
/*
* Store the supplied proto string for error reporting,
* re-attaching the 'only' suffix if one was taken off.
*/
goto past_proto_processing;
} else {
if (v6only)
}
/*
*/
if (isrpc) {
(rpc_hv != -1)) {
} else {
}
}
}
/* validate non-RPC service name */
if (invalid_proto) {
/*
* Make getservbyname_r do its lookup without a
* proto.
*/
/*
* Since getservbyname & getprotobyname don't
* support tcp6, udp6 or sctp6 take off the 6
* digit from protocol.
*/
if (v6_socket_proto(gsproto))
}
/* LINTED E_BAD_PTR_CAST_ALIGN */
SS_SETPORT(*(struct sockaddr_storage *)
} else {
}
}
}
/* add new proto entry to proto_list */
p_inf);
}
if (invalid_proto)
/*
* Check that the exec string for the start method actually exists and
* that the user is either a valid username or uid. Note we don't
* mandate the setting of these fields, and don't do any checks
* for arg0, hence its absence.
*/
/* Don't pass any arguments to access() */
} else {
*cp = '\0';
}
}
errno = 0;
&bufp, 10);
NSS_BUFLEN_PASSWD) == NULL))
}
}
/*
* Iterate through the properties in the array verifying that any
* default properties are valid, and setting the return boolean
* according to whether any properties were marked invalid.
*/
continue;
if (prop[i].ip_default &&
}
/* pass back the basic_cfg_t if requested and it's a valid config */
} else {
}
return (ret);
}
/*
* validate_default_prop takes the name of an inetd property, and a value
* for that property. It returns B_TRUE if the property is valid, and B_FALSE
* if the proposed value isn't valid for that property.
*/
{
int i;
continue;
if (!inetd_properties[i].ip_default)
return (B_FALSE);
switch (inetd_properties[i].ip_type) {
case INET_TYPE_INTEGER:
return (B_TRUE);
else
return (B_FALSE);
case INET_TYPE_BOOLEAN:
return (B_TRUE);
else
return (B_FALSE);
case INET_TYPE_COUNT:
case INET_TYPE_STRING_LIST:
case INET_TYPE_STRING:
return (B_TRUE);
}
}
return (B_FALSE);
}
/*ARGSUSED*/
const char *pg_name)
{
char *tmp_char;
NULL)
return (scf_error());
case INET_TYPE_STRING:
goto scf_error;
return (SCF_ERROR_NO_MEMORY);
}
break;
case INET_TYPE_STRING_LIST:
{
int j = 0;
while ((tmp_char =
char **cpp;
(j + 2) * sizeof (char *))) == NULL) {
return (SCF_ERROR_NO_MEMORY);
}
return (SCF_ERROR_NO_MEMORY);
}
}
if ((j == 0) || (scf_error() != SCF_ERROR_NONE))
goto scf_error;
}
break;
case INET_TYPE_BOOLEAN:
goto scf_error;
break;
case INET_TYPE_COUNT:
goto scf_error;
break;
case INET_TYPE_INTEGER:
goto scf_error;
break;
default:
assert(0);
}
return (0);
if (scf_error() == SCF_ERROR_NONE)
return (SCF_ERROR_NOT_FOUND);
return (scf_error());
}
/*
* read_props reads either the full set of properties for instance 'instance'
* (including defaults - pulling them in from inetd where necessary) if
* 'instance' is non-null, else just the defaults from inetd. The properties
* are returned in an allocated inetd_prop_t array, which must be freed
* using free_instance_props(). If an error occurs NULL is returned and 'err'
* is set to indicate the cause, else a pointer to the read properties is
* returned.
*/
static inetd_prop_t *
{
int i;
return (NULL);
}
if (defaults_only)
continue;
case 0:
break;
goto failure_cleanup;
case SCF_ERROR_NOT_FOUND:
/*
* In non-default-only mode where we're reading a
* default property, since the property wasn't
* found in the instance, try and read inetd's default
* value.
*/
continue;
case 0:
continue;
case SCF_ERROR_NOT_FOUND:
continue;
default:
goto failure_cleanup;
}
default:
goto failure_cleanup;
}
}
*num_elements = i;
return (ret);
return (NULL);
}
/*
* Read all properties applicable to 'instance' (including defaults).
*/
{
}
/*
* Read the default properties from inetd's defaults property group.
*/
{
}
void
{
int i;
return;
}
}
}
int
connect_to_inetd(void)
{
int fd;
if (fd < 0)
return (-1);
/* CONSTCOND */
return (-1);
}
return (fd);
}
/*
* refresh_inetd requests that inetd re-read all of the information that it's
* monitoring.
*/
int
refresh_inetd(void)
{
int fd;
if ((fd = connect_to_inetd()) < 0)
return (-1);
return (-1);
}
return (0);
}
/*
* Returns the id of the socket type 'type_str' that can be used in a call
* to socket(). If an unknown type string is passed returns -1, else the id.
*/
int
{
int ret;
ret = SOCK_STREAM;
ret = SOCK_DGRAM;
} else {
ret = -1;
}
return (ret);
}
/*
* Takes either an RPC service name or number in string form as 'svc_name', and
* returns an integer format program number for the service. If the name isn't
* recognized as a valid RPC service name or isn't a valid number, -1 is
* returned, else the services program number.
*/
int
{
int pnum;
char *endptr;
return (-1);
}
return (pnum);
}
/*
* calculate_hash calculates the MD5 message-digest of the file pathname.
* On success, hash is modified to point to the digest string and 0 is returned.
* Otherwise, -1 is returned and errno is set to indicate the error.
* The space for the digest string is obtained using malloc(3C) and should be
* freed by the caller.
*/
int
{
ssize_t n;
char *digest;
do {
if (fd == -1)
return (-1);
/* allocate space for a 16-byte MD5 digest as a string of hex digits */
return (-1);
}
do {
if (n == -1) {
return (-1);
}
for (i = 0; i < sizeof (md5_digest); i++) {
md5_digest[i]);
}
return (0);
}
/*
* retrieve_inetd_hash retrieves inetd's configuration file hash from the
* repository. On success, hash is modified to point to the hash string and
* SCF_ERROR_NONE is returned. Otherwise, the scf_error value is returned.
* The space for the hash string is obtained using malloc(3C) and should be
* freed by the caller.
*/
{
char *hashstr, *s;
return (scf_error());
return (scf_err);
}
return (SCF_ERROR_NO_MEMORY);
}
*hash = s;
return (SCF_ERROR_NONE);
}
/*
* store_inetd_hash stores the string hash in inetd's configuration file hash
* in the repository. On success, SCF_ERROR_NONE is returned. Otherwise, the
* scf_error value is returned.
*/
{
int ret;
scf_handle_t *h;
scf_handle_bind(h) == -1)
goto error;
goto error;
if (scf_error() != SCF_ERROR_NOT_FOUND ||
0, pg) == -1)
goto error;
}
goto error;
do {
goto error;
SCF_TYPE_ASTRING) == -1 &&
SCF_TYPE_ASTRING) == -1)
goto error;
goto error;
goto error;
if (ret == 0) {
goto error;
}
} while (ret == 0);
goto success;
return (rval);
}
/*
* This is a wrapper function for inet_ntop(). In case the af is AF_INET6
* and the address pointed by src is a IPv4-mapped IPv6 address, it returns
* a printable IPv4 address, not an IPv4-mapped IPv6 address. In other cases it
* behaves just like inet_ntop().
*/
const char *
{
} else {
}
}
/*
* inetd specific setproctitle. It sets the title so that it contains
* 'svc_name' followed by, if obtainable, the address of the remote end of
* socket 's'.
* NOTE: The argv manipulation in this function should be replaced when a
* common version of setproctitle is made available.
*/
void
{
sizeof (abuf)));
} else {
}
/* we set argv[0] to point at our static storage. */
}
static boolean_t
{
p = ntohs(p);
if ((p == IPPORT_ECHO) ||
(p == IPPORT_DISCARD) ||
(p == IPPORT_DAYTIME) ||
(p == IPPORT_CHARGEN) ||
(p == IPPORT_TIMESERVER)) {
return (B_TRUE);
} else {
return (B_FALSE);
}
}
/* ARGSUSED0 */
static void
{
exit(0);
}
/*
* This function is a datagram service template. It acts as a datagram wait
* type server, waiting for datagrams to come in, and when they do passing
* their contents, as-well as the socket they came in on and the remote
* address, in a call to the callback function 'cb'. If no datagrams are
* received for DG_INACTIVITY_TIMEOUT seconds the function exits with code 0.
*/
void
{
ssize_t i;
}
for (;;) {
(void) alarm(DG_INACTIVITY_TIMEOUT);
&sa_size)) < 0) {
continue;
} else if (inetd_builtin_srcport(
/* denial-of-service attack possibility - ignore it */
"Incoming datagram from internal inetd service received; ignoring.");
continue;
}
(void) alarm(0);
}
}
/*
* An extension of write() or sendto() that keeps trying until either the full
* request has completed or a non-EINTR error occurs. If 'to' is set to a
* non-NULL value, sendto() is extended, else write(). Returns 0 on success
* else -1.
*/
int
do {
} else {
}
if (ret > 0)
}
int
}
int
{
}
/*
* Free up the memory occupied by string array 'strs'.
*/
void
{
int i = 0;
}
}
/*
* Parse the proto list string into an allocated array of proto strings,
* returning a pointer to this array. If one of the protos is too big
* errno is set to E2BIG and NULL is returned; if memory allocation failure
* occurs errno is set to ENOMEM and NULL is returned; else on success
* a pointer the string array is returned.
*/
char **
{
char *cp;
int i = 0;
char *str;
/* copy the parameter as strtok modifies its parameters */
goto malloc_failure;
char **cpp;
return (NULL);
}
goto malloc_failure;
goto malloc_failure;
}
return (ret);
return (NULL);
}
/*
* Returns an allocated string array of netids corresponding with 'proto'. The
* function first tries to interpret 'proto' as a nettype to get its netids.
* If this fails it tries to interpret it as a netid. If 'proto' is neither
* a nettype or a netid or a memory allocation failures occurs NULL is
* returned, else a pointer to an array of netids associated with 'proto' is
* returned.
*/
char **
{
void *handle;
char **cpp;
int i = 0;
proto = "visible";
/* expand nettype */
(i + 2) * sizeof (char *))) == NULL)
goto failure_cleanup;
goto failure_cleanup;
}
} else {
return (NULL);
return (NULL);
}
}
return (netids);
return (NULL);
}