/*
* 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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* This is the main file for the Domain Configuration Server (DCS).
*
* The DCS is a server that runs on a domain and communicates with
* a Domain Configuration Agent (DCA) running on a remote host. The
* DCA initiates DR requests that the DCS performs by calling the
* appropriate libcfgadm(3LIB) function.
*
* This file contains functions that receive and process the messages
* received from the DCA. It also handles the initialization of the
* server and is responsible for starting a concurrent session to
* handle each DR request.
*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <syslog.h>
#include <assert.h>
#include <signal.h>
#include <netdb.h>
#include <config_admin.h>
#include <strings.h>
#include "dcs.h"
#include "remote_cfg.h"
#include "rdr_param_types.h"
#include "rdr_messages.h"
#include "rsrc_info.h"
typedef struct {
} dcs_ver_t;
/* initialization functions */
static void init_signals(void);
/* message processing functions */
/* message handling functions */
/* local callback functions */
/* utility functions */
static boolean_t dcs_global_policy(void);
/*
* Lookup table for handling different message types. This
* assumes the ordering of rdr_msg_opcode_t in remote_cfg.h.
* If this enum changes, the lookup table must be updated.
*
* The lookup table handles all _known_ opcodes >= 0. Unsupported
* opcodes, or opcodes that should not be received by the
* dispatcher are handled by the dcs_unknown_op() function.
*/
dcs_unknown_op, /* 0 is an invalid opcode */
dcs_ses_req, /* RDR_SES_REQ */
dcs_ses_estbl, /* RDR_SES_ESTBL */
dcs_ses_end, /* RDR_SES_END */
dcs_change_state, /* RDR_CONF_CHANGE_STATE */
dcs_private_func, /* RDR_CONF_PRIVATE_FUNC */
dcs_test, /* RDR_CONF_TEST */
dcs_list_ext, /* RDR_CONF_LIST_EXT */
dcs_help, /* RDR_CONF_HELP */
dcs_ap_id_cmp, /* RDR_CONF_AP_ID_CMP */
dcs_abort_cmd, /* RDR_CONF_ABORT_CMD */
dcs_unknown_op, /* RDR_CONF_CONFIRM_CALLBACK */
dcs_unknown_op, /* RDR_CONF_MSG_CALLBACK */
dcs_rsrc_info /* RDR_RSRC_INFO */
};
/*
* ver_supp[] is an array of the supported versions for the network
* transport protocol used by the DCA and DCS. Each item in the array
* is a pair: { major_version, minor_version }.
*
* The order of the array is significant. The first element should be
* the highest supported version and all successive elements should be
* strictly decreasing.
*/
{ 1, 1 },
{ 1, 0 }
};
/*
* Global Data
*/
/*
* Array of acceptable -a, -e and -u arguments.
*/
{ NULL, 0x0 }
}, esp_algs_array[] = {
{ NULL, 0x0 }
};
/*
* main:
*
* Initialize the DCS and then enter an infinite loop. This loop waits
* for connection requests to come and then establishes a connection.
* It dispatches the connection to be handled in a concurrent session.
*/
int
{
int opt;
int newfd;
/* initialize globals */
/* open log file with unique prefix */
/*
* Process command line args
*/
opterr = 0; /* disable getopt error messages */
switch (opt) {
case 'd': {
int usr_debug;
char *err_str;
/*
* The err_str parameter will be an
* empty string if successful.
*/
if (*err_str != '\0') {
optarg, "exiting");
(void) rdr_reject(dcsfd);
exit(1);
}
break;
}
case 'S':
standalone++;
break;
case 's': {
int usr_ses;
char *err_str;
if (usr_ses >= 1) {
} else {
"using default value (%d)", max_sessions);
}
break;
}
case 'a':
case 'u':
if (opt == 'a')
else /* opt == 'u' */
if (alg_ec == DCS_BAD_OPT_ARG) {
(void) rdr_reject(dcsfd);
exit(1);
}
break;
case 'e':
&alg_ec);
if (alg_ec == DCS_BAD_OPT_ARG) {
(void) rdr_reject(dcsfd);
exit(1);
}
break;
case 'l':
use_libdscp = 1;
break;
default:
else
(void) rdr_reject(dcsfd);
exit(1);
/* NOTREACHED */
break;
}
}
/*
* In the future if inetd supports per-socket IPsec dcs can be run
* under inetd.
* Daemonize if we were not started by inetd unless running standalone.
*/
closefrom(0);
(void) chdir("/");
(void) umask(0);
if (fork() != 0)
exit(0);
(void) setsid();
/* open log again after all files were closed */
}
init_signals();
/* must be root */
if (geteuid() != 0) {
(void) rdr_reject(dcsfd);
exit(1);
}
/*
* Seed the random number generator for
* generating random session identifiers.
*/
/* initialize our transport endpoint */
-1) {
(void) rdr_reject(dcsfd);
exit(1);
}
/*
* Main service loop
*/
for (;;) {
/* wait for a connection request */
}
continue;
}
/* attempt to connect */
continue;
}
/* process the session concurrently */
break;
}
}
return (1);
}
/*
* dcs_get_alg:
*
* Returns the ID of the first algorithm found in the 'algs' array
* with a name matching 'arg'. If there is no matching algorithm,
* 'error' is set to DCS_BAD_OPT_ARG, otherwise it is set to DCS_NO_ERR.
* The 'algs' array must be terminated by an entry containing a NULL
* 'arg_name' field. The 'error' argument must be a valid pointer.
*/
static uint8_t
{
*error = DCS_NO_ERR;
== 0) {
}
}
*error = DCS_BAD_OPT_ARG;
return (0);
}
/*
* dcs_log_bad_alg:
*
* Logs an appropriate message when an invalid command line argument
* was provided. 'optarg' is the invalid argument string for the
* command line option 'optopt', where 'optopt' = 'a' for the '-a'
* option. A NULL 'optarg' indicates the required option was not
* provided.
*/
static void
{
"empty string", "an argument is required, exiting");
} else {
optarg, "exiting");
}
}
/*
* init_server:
*
* Perform all the operations that are required to initialize the
* transport endpoint used by the DCS. After this routine succeeds,
* the DCS is ready to accept session requests on its well known
* port.
*/
static int
{
int req_port;
int act_port;
int init_status;
int num_sock_opts;
/*
* In standalone mode, we have to initialize the transport
* endpoint for our reserved port. In daemon mode, inetd
* starts the DCS and hands off STDIN_FILENO connected to
* our reserved port.
*/
/* in standalone mode, init fd for reserved port */
return (-1);
}
/*
* Enable per-socket IPsec if the user specified an
* AH or ESP algorithm to use and global policy is not in
* effect.
*/
if (!dcs_global_policy() &&
(ah_auth_alg != SADB_AALG_NONE ||
esp_encr_alg != SADB_EALG_NONE ||
esp_auth_alg != SADB_AALG_NONE)) {
int err;
/* Hardcoded values */
/* User defined */
if (ah_auth_alg != SADB_AALG_NONE)
if (esp_encr_alg != SADB_EALG_NONE ||
esp_auth_alg != SADB_AALG_NONE) {
}
IPV6_SEC_OPT, (void *)&ipsec_req,
sizeof (ipsec_req));
return (-1);
}
}
}
/*
* Look up our service to get the reserved port number
*/
/* use the known port if service wasn't found */
} else {
}
if (use_family == AF_INET) {
/* initialize our local address */
} else {
/* initialize our local address */
}
if (init_status != RDR_OK) {
return (-1);
}
/*
* Set the SO_LINGER socket option so that TCP aborts the connection
* when the socket is closed. This avoids encountering a TIME_WAIT
* state if the daemon ever crashes and is instantly restarted.
*/
return (-1);
}
case AF_INET:
break;
case AF_INET6:
/* sin6 already set correctly */
break;
default:
return (-1);
}
/* check that we got the requested port */
return (-1);
}
return (0);
}
/*
* init_signals:
*
* Initialize signals for the current session. All signals will be
* blocked with two possible exceptions. SIGINT is not blocked in
* standalone mode, and ses_init_signals() is called to selectively
* unblock any signals required to handle concurrent sessions.
*/
static void
init_signals(void)
{
/* block all signals */
sigfillset(&mask);
/* in standalone, allow user to abort */
if (standalone) {
}
}
/*
* dcs_dispatch_message:
*
* This function dispatches a message to the correct function. The
* correct handler is determined by the opcode field of the message
* header.
*/
int
{
/* get the current session information */
return (-1);
}
/* check the message */
if (invalid_msg(hdr)) {
return (-1);
}
/* save the current message */
/*
* hdr->message_opcode is unsigned so don't need
* to check for values less than zero
*/
return (-1);
}
/* dispatch the message */
return (-1);
}
return (0);
}
/*
* init_msg:
*
* Initialize the message header with information from the current
* session. Fields not set directly are initialized to zero.
*/
void
{
/* get the current session information */
return;
}
/* set the session information */
/* set the version being used */
}
/*
* invalid_msg:
*
* Check if the message is valid for the current session. This
* is accomplished by checking various information in the header
* against the information for the current session.
*/
static int
{
/* get the current session information */
return (-1);
}
/*
* Only perform the following checks if the message
* is not a session request. The information to check
* will not be set at the time a session request is
* received.
*/
/* check major and minor version */
return (-1);
}
/* check session identifiers */
return (-1);
}
}
return (0);
}
/*
* dcs_ses_req:
*
* Handle a session request message (RDR_SES_REQ).
*/
static int
{
int snd_status;
/* get the current session information */
return (-1);
}
/* make sure that a session hasn't been requested yet */
return (-1);
}
/* get the best matching version supported */
/* initialize session information */
/* prepare header information */
/* prepare session request specific data */
/* send the message */
if (snd_status == RDR_ABORTED) {
}
if (snd_status != RDR_OK) {
return (-1);
}
return (0);
}
/*
* dcs_ses_estbl:
*
* Handle a session establishment message (RDR_SES_ESTBL).
*/
/* ARGSUSED */
static int
{
/* get the current session information */
return (-1);
}
/*
* Make sure that a session has not been
* established yet, and that a session
* request has already been processed.
*/
return (-1);
}
/* get the best matching version supported */
/* end the session because protocol not supported */
hdr->minor_version);
return (-1);
}
return (0);
}
/*
* dcs_ses_end:
*
* Handle a session end message (RDR_SES_END).
*/
static int
{
int snd_status;
/* get the current session information */
return (-1);
}
/*
* Session end is valid from any state. However, only
* send back a reply if the error code is zero. A non-zero
* error code indicates that the session is being terminated
* under an error condition, and no acknowledgement is
* required.
*/
/* prepare header information */
/* return empty data - no information needed in reply */
if (snd_status == RDR_ABORTED) {
}
if (snd_status != RDR_OK) {
}
}
}
/*
* dcs_change_state:
*
* Handle a change state request message (RDR_CONF_CHANGE_STATE).
*/
static int
{
int cfga_status = 0;
int snd_status;
char *err_str;
unsigned int curr_attempt;
unsigned int num_attempts;
/* get the current session information */
return (-1);
}
/* make sure we have a session established */
return (-1);
}
/* initialize local confirm callback */
/* initialize local message callback */
/* verify retry value */
}
/* verify timeout value */
}
curr_attempt = 0;
while (curr_attempt < num_attempts) {
/* don't sleep the first time around */
if (curr_attempt != 0) {
/* log the error message and alert the user */
if (err_str) {
err_str);
err_str);
} else {
}
}
/* sleep with abort enabled */
/* log the retry attempt and alert the user */
}
/*
* Call into libcfgadm
*/
/*
* Retry only the operations that have a chance to
* succeed if retried. All libcfgadm errors not
* included below will always fail, regardless of
* a retry.
*/
if ((cfga_status != CFGA_BUSY) &&
(cfga_status != CFGA_SYSTEM_BUSY) &&
(cfga_status != CFGA_ERROR)) {
break;
}
/* prepare for another attempt */
++curr_attempt;
}
/* log any libcfgadm errors */
if (cfga_status != CFGA_OK) {
if (err_str) {
}
}
/* prepare header information */
/* send the message */
if (snd_status == RDR_ABORTED) {
}
if (snd_status != RDR_OK) {
}
/* clean up */
}
}
/*
* dcs_private_func:
*
* Handle a private function request message (RDR_CONF_PRIVATE_FUNC).
*/
static int
{
int cfga_status;
int snd_status;
char *err_str;
/* get the current session information */
return (-1);
}
/* make sure we have a session established */
return (-1);
}
/* initialize local confirm callback */
/* initialize local message callback */
/*
* Call into libcfgadm
*/
/* log any libcfgadm errors */
if (cfga_status != CFGA_OK) {
if (err_str) {
}
}
/* prepare header information */
/* send the message */
if (snd_status == RDR_ABORTED) {
}
if (snd_status != RDR_OK) {
}
}
}
/*
* dcs_test:
*
* Handle a test request message (RDR_CONF_TEST).
*/
static int
{
int cfga_status;
int snd_status;
char *err_str;
/* get the current session information */
return (-1);
}
/* make sure we have a session established */
return (-1);
}
/* initialize local message callback */
/*
* Call into libcfgadm
*/
/* log any libcfgadm errors */
if (cfga_status != CFGA_OK) {
if (err_str) {
}
}
/* prepare header information */
/* send the message */
if (snd_status == RDR_ABORTED) {
}
if (snd_status != RDR_OK) {
}
}
}
/*
* dcs_list_ext:
*
* Handle a list request message (RDR_CONF_LIST_EXT).
*/
static int
{
int cfga_status;
int snd_status;
char *err_str;
/* get the current session information */
return (-1);
}
/* make sure we have a session established */
return (-1);
}
/*
* Make sure that we can retrieve the data
* from libcfgadm. If not, report the error.
*/
return (-1);
}
/*
* Call into libcfgadm
*/
/*
* Log any libcfgadm errors at a low priority level.
* Since a status request does not modify the system
* in any way, we do not need to worry about these
* errors here on the host.
*/
if (cfga_status != CFGA_OK) {
if (err_str) {
}
}
/*
* Filter ap ids to return only appropriate information
*/
/* if all aps were filtered out, return an error */
}
/* calculate the sort order */
if (cfga_status == CFGA_OK) {
}
}
/* ensure that nlist is 0 for errors */
if (cfga_status != CFGA_OK) {
}
/* prepare header information */
/* send the message */
if (snd_status == RDR_ABORTED) {
}
if (snd_status != RDR_OK) {
}
}
}
}
/*
* dcs_help:
*
* Handle a help request message (RDR_CONF_HELP).
*/
static int
{
int cfga_status;
int snd_status;
char *err_str;
/* get the current session information */
return (-1);
}
/* make sure we have a session established */
return (-1);
}
/* initialize local message callback */
/*
* Call into libcfgadm
*/
/*
* Log any libcfgadm errors at a low priority level.
* Since a help request does not modify the system
* in any way, we do not need to worry about these
* errors here on the host.
*/
if (cfga_status != CFGA_OK) {
if (err_str) {
}
}
/* prepare header information */
/* send the message */
if (snd_status == RDR_ABORTED) {
}
if (snd_status != RDR_OK) {
}
}
/*
* dcs_ap_id_cmp:
*
* Handle an attachment point comparison request message (RDR_AP_ID_CMP).
*/
static int
{
int snd_status;
int cmp_result;
/* get the current session information */
return (-1);
}
/* make sure we have a session established */
return (-1);
}
/*
* Call into libcfgadm
*/
/* prepare header information */
/*
* Return result of comparison as error code.
* Since all values are valid, it is impossible
* to report an error.
*/
/* send the message */
if (snd_status == RDR_ABORTED) {
}
if (snd_status != RDR_OK) {
}
}
/*
* dcs_abort_cmd:
*
* Handle an abort request message (RDR_CONF_ABORT_CMD).
*/
/* ARGSUSED */
static int
{
int snd_status;
/* get the current session information */
return (-1);
}
if (op_status == -1) {
}
/* prepare header information */
/* send the message */
if (snd_status == RDR_ABORTED) {
}
if (snd_status != RDR_OK) {
}
}
/*
* dcs_rsrc_info:
*
* Handle a resource info request message (RDR_RSRC_INFO).
*/
static int
{
int rsrc_status;
int snd_status;
/* get the current session information */
return (-1);
}
/* make sure we have a session established */
return (-1);
}
/*
* Request resource info data.
*/
/* log errors */
if (rsrc_status != RI_SUCCESS) {
}
/* prepare header information */
/* send the message */
if (snd_status == RDR_ABORTED) {
}
if (snd_status != RDR_OK) {
}
}
/*
* dcs_unknown_op:
*
* Handle all unknown requests.
*/
/* ARGSUSED */
static int
{
/* get the current session information */
return (-1);
}
return (-1);
}
/*
* dcs_confirm_callback:
*
* Perform a confirm callback and wait for the reply. As defined
* in the config_admin(3CFGADM) man page, 1 is returned if the
* operation should be allowed to continue and 0 otherwise.
*/
static int
{
int snd_status;
int rcv_status;
/* sanity check */
if (appdata_ptr == NULL) {
return (0);
}
/* get the current session information */
return (0);
}
/* prepare header information */
/* prepare confirm callback specific data */
/* send the message */
if (snd_status == RDR_ABORTED) {
}
if (snd_status != RDR_OK) {
return (0);
}
/*
* Wait for response
*/
if (rcv_status != RDR_OK) {
return (0);
}
/*
* Perform several checks to see if we have a
* valid response to the confirm callback.
*/
if (invalid_msg(&reply_hdr)) {
return (0);
}
/* check the opcode and type */
return (0);
}
/* check for incorrect callback id */
return (0);
}
/*
* Got back valid response: return the user's answer
*/
}
/*
* dcs_message_callback:
*
* Perform a message callback to display a string to the user.
*
* Note: There is no documentation about possible return values
* for the message callback. It is assumed that the value returned
* is ignored, so 0 is returned for all cases.
*/
static int
{
int snd_status;
/* sanity check */
if (appdata_ptr == NULL) {
return (0);
}
/* get the current session information */
return (0);
}
/* prepare header information */
/* prepare message callback specific data */
/* send the message */
if (snd_status == RDR_ABORTED) {
}
if (snd_status != RDR_OK) {
}
return (0);
}
/*
* resolve_version:
*
* Consult the list of supported versions and find the highest supported
* version that is less than or equal to the version requested in the
* parameters. This assumes that the list of supported versions is ordered
* so that the highest supported version is the first element, and that
* the versions are strictly decreasing.
*/
static dcs_ver_t
{
int i;
int num_vers;
/* default to the lowest version */
for (i = 0; i < num_vers; i++) {
/*
* The major version matches and the
* minor version either matches, or
* is the best match that we have.
*/
break;
}
/*
* The requested major version is larger than
* the current version we are checking. There
* is not going to be a better match.
*/
break;
}
}
return (act_ver);
}
/*
* filter_list_data:
*
* Check a list of cfga_list_data_t structures to filter out the ones
* that don't have other-read permissions. All valid entries are placed
* at the beginning of the array and the count of entries is updated.
*/
static void
{
int num_aps;
int num_aps_ret;
int curr_ap;
int next_aval;
int end_block;
int block_size;
"RDR_PRIVILEGED" : "RDR_NOT_PRIVILEGED");
/*
* Check if the user has priviledged access
* to view all attachment points
*/
if (perm == RDR_PRIVILEGED) {
return;
}
if (*nlistp < 0) {
*nlistp = 0;
}
/*
* No priviledged access, check each attachment point to
* see if the user has access (other:read) to view it.
*/
next_aval = 0;
num_aps_ret = 0;
curr_ap = 0;
/*
* Use a simple algorithm to compact the array so that
* all attachment points that can be viewed are at the
* beginning of the array. Adjust the count of the
* attachment points accordingly.
*/
/* check for unrestricted read permission */
/*
* Check if this is the beginning of a
* block of consecutive ap ids that can
* be returned.
*/
/* search until the end of the block */
end_block++;
} else {
break;
}
}
/* make sure a copy is necessary */
/* copy the block of ap ids all at once */
block_size * sizeof (cfga_list_data_t));
}
/* move past the copied block */
next_aval += block_size;
} else {
curr_ap++;
}
}
*nlistp);
/*
* return the number of aps that have the correct
* access permissions.
*/
*nlistp = num_aps_ret;
}
/*
* generate_sort_order:
*
* Determine the sort order of an array of cfga_list_data_t structures
* and create an array of rdr_list_t structures that contain the original
* elements tagged with the sort order.
*
* This function is used to eliminate unnecessary network traffic that
* might occur if the client needs the output of config_list_ext(3CFGADM)
* sorted. Since a comparison is performed in a platform specific manner
* using config_ap_id_cmp(3CFGADM), a client must establish a new session
* for each comparison. For a long lists of attachment points, this can
* slow down a simple list_ext operation significantly. With the sort
* information included in the array of rdr_list_t structures, the client
* can perform the sort operation locally, thus eliminating a great deal
* of network traffic.
*/
static rdr_list_t *
{
int curr_ap;
if (nlist <= 0) {
return (NULL);
}
/* create our new array */
return (NULL);
}
/* copy over the elements, preserving the original order */
}
/* handle a one element list */
if (nlist == 1) {
datalp[0].sort_order = 0;
return (datalp);
}
/* sort the cfga_list_data_t array */
/* process each item in the original list */
/* look up the sort order in the sorted list */
/* found a match */
} else {
/*
* Should never get here. Since we did a
* direct copy of the array, we should always
* be able to find the ap id that we were
* looking for.
*/
"ap id in the sorted list");
}
}
return (datalp);
}
/*
* ldata_compare:
*
* Compare the two inputs to produce a strcmp(3C) style result. It uses
* config_ap_id_cmp(3CFGADM) to perform the comparison.
*
* This function is passed to qsort(3C) in generate_sort_order() to sort a
* list of attachment points.
*/
static int
{
}
/*
* basename:
*
* Find short path name of a full path name. If a short path name
* is passed in, the original pointer is returned.
*/
static char *
{
char *sp;
return (sp + 1);
}
return (cp);
}
/*
* is_socket:
*
* determine if fd represents a socket file type.
*/
static boolean_t
{
return (B_FALSE);
}
}
/*
* has_dcs_token
*
* Look for "?port [sun-dr|665]" in input buf.
* Assume only a single thread calls here.
*/
static boolean_t
{
char *token;
return (B_TRUE);
} else {
return (B_FALSE);
}
}
token++;
continue;
}
}
}
return (B_FALSE);
}
/*
* dcs_global_policy
*
* Check global policy file for dcs entry. Just covers common cases.
*/
static boolean_t
{
return (B_FALSE);
if (buf[0] == '#')
continue;
if (has_dcs_token(buf)) {
break;
}
}
return (rv);
}