libvscan.c revision 1bf43fcde84431aa2551e6f3debd07bdaa34f48b
/*
* 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.
*/
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <math.h>
#include <limits.h>
#include <libscf.h>
#include <errno.h>
#include <fcntl.h>
#include <door.h>
#include <pwd.h>
#include <auth_attr.h>
#include <secdb.h>
#include <libintl.h>
#include <libvscan.h>
#define VS_DOOR_CALL_RETRIES 3
#define VS_INSTANCE_FMRI "svc:/system/filesystem/vscan:icap"
/* SMF property group and property names */
#define VS_PGNAME_GENERAL "vs_general"
#define VS_PGNAME_ENGINE_PREFIX "vs_engine_"
#define VS_PNAME_MAXSIZE "maxsize"
#define VS_PNAME_MAXSIZE_ACTION "maxsize_action"
#define VS_PNAME_TYPES "types"
#define VS_PNAME_VLOG "viruslog"
#define VS_PNAME_SE_ENABLE "enable"
#define VS_PNAME_SE_HOST "host"
#define VS_PNAME_SE_PORT "port"
#define VS_PNAME_SE_MAXCONN "max_connect"
#define VS_PNAME_VAUTH "value_authorization"
/* types string processing */
#define VS_TYPES_SEP ','
#define VS_TYPES_ESCAPE '\\'
#define VS_TYPES_RULES "+-"
/*
* The SCF context enapsulating the SCF objects used in the
* repository load and store routines vs_scf_values_get()
* and vs_scf_values_set().
*
* The context is always opened before a get or set, then
* closed when finished (or on error); the open does an
* initial setup, while inside the get and set functions,
* additional objects within the context may be selectively
* initialized for use, depending on the actions needed and
* the properties being operated on.
*/
typedef struct vs_scfctx {
} vs_scfctx_t;
/*
* The vscan property definition. Maps the property id with the name
* and type used to store the property in the repository.
* A table of these definitions is defined with a single entry per
* property.
*/
typedef struct {
const char *vpd_name;
} vs_propdef_t;
typedef enum {
typedef struct vs_prop_hd {
union {
} vp_props;
} vs_prop_hd_t;
/*
* Default values - these are used to return valid data
* to the caller in cases where invalid or unexpected values
* are found in the repository.
*
* Note: These values must be kept in sync with those defined
* in the service manifest.
*/
static const char *vs_dflt_maxsize = "1GB";
static const char *vs_dflt_host = "";
static const char *vs_dflt_types = "+*";
static const char *vs_dflt_vlog = "";
/* Property definition table */
static const vs_propdef_t vs_propdefs[] = {
/* general properties */
/* scan engine properties */
};
/* Local functions */
static int vs_scf_values_get(const char *, vs_prop_hd_t *);
static int vs_scf_values_set(const char *, vs_prop_hd_t *);
static int vs_scf_pg_create(const char *, vs_prop_hd_t *);
static int vs_scf_pg_delete(const char *);
static int vs_scf_ctx_open(vs_scfctx_t *);
static void vs_scf_ctx_close(vs_scfctx_t *);
static int vs_is_valid_types(const char *);
static int vs_is_valid_host(const char *);
static int vs_checkauth(char *);
static int vs_door_call(int, door_arg_t *);
static int vs_props_get_engines(char *[], int *);
static void vs_engid_to_pgname(const char *, char [VS_PGNAME_ENGINE_LEN]);
static int vs_scf_pg_count(void);
static int vs_strtoshift(const char *);
/*
* vs_props_get_all
*
* Retrieves the general service properties and all properties
* for all scan engines from the repository.
*
* If invalid property values are found, the values are corrected to
* the default value.
*
* Return codes:
* VS_ERR_VS_ERR_NONE
* VS_ERR_SCF
* VS_ERR_SYS
*/
int
{
int i, rc, n;
!= VS_ERR_NONE)
return (rc);
n = VS_SE_MAX;
return (rc);
for (i = 0; i < n; i++) {
break;
}
/* free engids allocated in vs_props_get_engines */
for (i = 0; i < VS_SE_MAX; i++) {
}
return (rc);
}
/*
* vs_props_get
*
* Retrieves values for the specified general service properties from
* the repository.
*
* If invalid property values are found, the values are corrected to
* the default value.
*
* Return codes:
* VS_ERR_VS_ERR_NONE
* VS_ERR_INVALID_PROPERTY
* VS_ERR_SCF
* VS_ERR_SYS
*/
int
{
int rc;
return (VS_ERR_INVALID_PROPERTY);
return (rc);
}
/*
* vs_props_set
*
* Changes values for the specified general service properties
* in the repository.
*
* Return codes:
* VS_ERR_VS_ERR_NONE
* VS_ERR_INVALID_PROPERTY
* VS_ERR_INVALID_VALUE
* VS_ERR_SCF
* VS_ERR_SYS
*/
int
{
return (VS_ERR_INVALID_PROPERTY);
}
/*
* vs_props_se_get
*
* Retrieves values for the specified scan engine properties from the
* repository.
*
* If the enable property is set (true), the host property is
* checked for validity. If it is not valid, the requested values
* are returned with the enable propery set to off (false)
*
* Return codes:
* VS_ERR_VS_ERR_NONE
* VS_ERR_INVALID_PROPERTY
* VS_ERR_SCF
* VS_ERR_SYS
*/
int
{
int rc;
char pgname[VS_PGNAME_ENGINE_LEN];
/* VS_PGNAME_GENERAL is a reserved for GENERAL property group */
return (VS_ERR_INVALID_SE);
return (VS_ERR_INVALID_PROPERTY);
/* If getting enable, get the host property too */
if ((propids & VS_PROPID_SE_ENABLE))
/* Load values from the repository */
if (rc != VS_ERR_NONE)
return (rc);
/*
* If the host is invalid and the enable property is on,
* return enable property as off
*/
}
return (rc);
}
/*
* vs_props_se_set
*
* Changes the values for the specified scan engine properties in the
* repository.
*
* If the enable property is being changed to true in this operation,
* a host property must also be specified, or already exist in the
* repository.
*
* Return codes:
* VS_ERR_NONE
* VS_ERR_INVALID_PROPERTY
* VS_ERR_INVALID_VALUE
* VS_ERR_SCF
* VS_ERR_SYS
*/
int
{
int rc;
char pgname[VS_PGNAME_ENGINE_LEN];
/* VS_PGNAME_GENERAL is a reserved for GENERAL property group */
return (VS_ERR_INVALID_SE);
return (VS_ERR_INVALID_PROPERTY);
/*
* if enabling a scan engine, ensure that a valid host
* is also being set, or already exists in the repository
*/
!(propids & VS_PROPID_SE_HOST)) {
return (rc);
return (VS_ERR_INVALID_HOST);
}
}
/*
* vs_props_se_create
*/
int
{
int n;
char pgname[VS_PGNAME_ENGINE_LEN];
return (VS_ERR_INVALID_PROPERTY);
/* VS_PGNAME_GENERAL is a reserved for GENERAL property group */
return (VS_ERR_INVALID_SE);
if ((n = vs_scf_pg_count()) == -1)
return (VS_ERR_SCF);
if (n == VS_SE_MAX)
return (VS_ERR_MAX_SE);
/* if hostname not specified, default it to engid */
if ((propids & VS_PROPID_SE_HOST) == 0) {
}
}
/*
* vs_props_se_delete
*/
int
vs_props_se_delete(const char *engid)
{
char pgname[VS_PGNAME_ENGINE_LEN];
/* VS_PGNAME_GENERAL is a reserved for GENERAL property group */
return (VS_ERR_INVALID_SE);
return (vs_scf_pg_delete(pgname));
}
/*
* vs_strerror
*/
const char *
vs_strerror(int error)
{
switch (error) {
case VS_ERR_NONE:
return (gettext("no error"));
case VS_ERR_INVALID_PROPERTY:
return (gettext("invalid property id"));
case VS_ERR_INVALID_VALUE:
return (gettext("invalid property value"));
case VS_ERR_INVALID_HOST:
return (gettext("invalid host"));
case VS_ERR_INVALID_SE:
return (gettext("invalid scan engine"));
case VS_ERR_MAX_SE:
return (gettext("max scan engines exceeded"));
case VS_ERR_AUTH:
return (gettext("insufficient privileges for action"));
case VS_ERR_DAEMON_COMM:
return (gettext("unable to contact vscand"));
case VS_ERR_SCF:
return (scf_strerror(scf_error()));
case VS_ERR_SYS:
default:
return (gettext("unknown error"));
}
}
/*
* vs_get_propdef
*
* Finds and returns a property definition by property id.
*/
static const vs_propdef_t *
{
int i;
for (i = 0; i < vs_npropdefs; i++) {
return (&vs_propdefs[i]);
}
return (NULL);
}
/*
* vs_default_value
*
* Sets a property value that contains invalid data to its default value.
*
* Note that this function does not alter any values in the repository
* This is only to enable the caller to get valid data.
*/
static void
{
switch (propid) {
case VS_PROPID_MAXSIZE:
sizeof (vp->vp_maxsize));
break;
case VS_PROPID_MAXSIZE_ACTION:
break;
case VS_PROPID_TYPES:
break;
case VS_PROPID_VLOG:
break;
case VS_PROPID_SE_ENABLE:
break;
case VS_PROPID_SE_HOST:
break;
case VS_PROPID_SE_PORT:
break;
case VS_PROPID_SE_MAXCONN:
break;
default:
break;
}
}
/*
* vs_scf_values_get
*
* Gets property values for one or more properties from the repository.
* This is the single entry point for loading SMF values.
*
* While a transaction is not used for loading property values,
* the operation is parameterized by a property group. All properties
* retrieved in this function, then, must belong to the same property
* group.
*/
int
{
const vs_propdef_t *vpd;
if ((vs_scf_ctx_open(&vsc)) != 0) {
return (VS_ERR_SCF);
}
if ((rc == SCF_ERROR_NOT_FOUND) ||
(rc == SCF_ERROR_INVALID_ARGUMENT))
return (VS_ERR_INVALID_SE);
}
return (VS_ERR_SCF);
}
rc = VS_ERR_NONE;
np = 0;
continue;
break;
}
rc = VS_ERR_SCF;
break;
}
if (scf_error() == SCF_ERROR_NOT_FOUND) {
continue;
}
rc = VS_ERR_SCF;
break;
}
break;
++np;
}
return (rc);
}
/*
* vs_scf_get
*
* Loads a single values from the repository into the appropriate vscan
* property structure member.
*/
static int
{
int rc;
if (rc == SCF_ERROR_CONSTRAINT_VIOLATED ||
rc == SCF_ERROR_NOT_FOUND) {
return (VS_ERR_NONE);
}
return (VS_ERR_SCF);
}
rc = VS_ERR_NONE;
case VS_PROPID_MAXSIZE:
return (VS_ERR_SCF);
}
break;
case VS_PROPID_MAXSIZE_ACTION:
&valbool)) == -1) {
return (VS_ERR_SCF);
}
break;
case VS_PROPID_TYPES:
return (VS_ERR_SCF);
}
break;
case VS_PROPID_VLOG:
return (VS_ERR_SCF);
}
break;
case VS_PROPID_SE_ENABLE:
&valbool)) == -1) {
return (VS_ERR_SCF);
}
break;
case VS_PROPID_SE_HOST:
break;
case VS_PROPID_SE_PORT:
return (VS_ERR_SCF);
else
break;
case VS_PROPID_SE_MAXCONN:
return (VS_ERR_SCF);
}
break;
default:
break;
}
if ((rc != VS_ERR_NONE) ||
}
return (VS_ERR_NONE);
}
/*
* vs_scf_pg_create
*/
static int
{
int rc;
/* ensure that caller has authorization to refresh service */
return (rc);
if (vs_scf_ctx_open(&vsc) != 0) {
return (VS_ERR_SCF);
}
if (scf_error() == SCF_ERROR_INVALID_ARGUMENT)
return (VS_ERR_INVALID_SE);
return (VS_ERR_SCF);
}
/* set default values for those not specified */
}
if (rc != VS_ERR_NONE)
(void) vs_scf_pg_delete(pgname);
return (rc);
}
/*
* vs_scf_pg_delete
*/
static int
vs_scf_pg_delete(const char *pgname)
{
int rc;
/* ensure that caller has authorization to refresh service */
return (rc);
if (vs_scf_ctx_open(&vsc) != 0) {
return (VS_ERR_SCF);
}
if ((rc == SCF_ERROR_NOT_FOUND) ||
(rc == SCF_ERROR_INVALID_ARGUMENT))
return (VS_ERR_INVALID_SE);
else
return (VS_ERR_SCF);
}
if ((rc == SCF_ERROR_NOT_FOUND) ||
(rc == SCF_ERROR_INVALID_ARGUMENT))
return (VS_ERR_INVALID_SE);
return (VS_ERR_SCF);
}
/* Notify the daemon that things have changed */
return (VS_ERR_SCF);
}
return (VS_ERR_NONE);
}
/*
* vs_scf_values_set
*
* Sets property values in the repository. This is the single
* entry point for storing SMF values.
*
* Like loading values, this is an operation based on a single property
* group, so all property values changed in this function must belong
* to the same property group. Additionally, this operation is done in
* the context of a repository transaction; on any fatal error, the
* SCF context will be closed, destroying all SCF objects and aborting
* the transaction.
*/
static int
{
const vs_propdef_t *vpd;
/* ensure that caller has authorization to refresh service */
return (rc);
if (vs_scf_ctx_open(&vsc) != 0) {
return (VS_ERR_SCF);
}
if ((rc == SCF_ERROR_NOT_FOUND) ||
(rc == SCF_ERROR_INVALID_ARGUMENT))
return (VS_ERR_INVALID_SE);
}
return (VS_ERR_SCF);
}
return (VS_ERR_SCF);
}
/* Process the value change for each specified property */
rc = 0;
np = 0;
continue;
break;
}
rc = VS_ERR_SCF;
break;
}
}
if (rc == -1) {
rc = VS_ERR_SCF;
break;
}
break;
++np;
}
if (rc != VS_ERR_NONE) {
return (rc);
}
/* Commit the transaction */
return (VS_ERR_SCF);
}
/* Notify the daemon that things have changed */
return (VS_ERR_SCF);
return (VS_ERR_NONE);
}
/*
* vs_scf_set
*
* Stores a single value from the appropriate vscan property structure
* member into the repository.
*
* Values are set in the SCF value object, then the value object
* is added to the SCF property object.
*/
static int
{
int rc;
return (rc);
rc = VS_ERR_NONE;
case VS_PROPID_MAXSIZE:
rc = VS_ERR_SCF;
}
break;
case VS_PROPID_MAXSIZE_ACTION:
break;
case VS_PROPID_TYPES:
return (VS_ERR_SCF);
}
break;
case VS_PROPID_SE_ENABLE:
break;
case VS_PROPID_SE_HOST:
rc = VS_ERR_SCF;
}
break;
case VS_PROPID_SE_PORT:
break;
case VS_PROPID_SE_MAXCONN:
vep->vep_maxconn);
break;
case VS_PROPID_VALUE_AUTH:
VS_VALUE_AUTH)) == -1) {
return (VS_ERR_SCF);
}
break;
default:
break;
}
return (VS_ERR_SCF);
}
return (rc);
}
/*
* vs_scf_ctx_open
*
* Opens an SCF context; creates the minumum SCF objects
* vscf_property group data).
*
* Other SCF objects in the context may be initialized elsewher
* subsequent to open, but all initialized structures are destroyed
* in vs_scf_ctx_close().
*/
static int
{
return (VS_ERR_SCF);
return (VS_ERR_SCF);
return (VS_ERR_SCF);
SCF_DECODE_FMRI_EXACT) == -1) {
return (VS_ERR_SCF);
}
return (VS_ERR_SCF);
return (VS_ERR_NONE);
}
/*
* vs_scf_ctx_close
*
* Closes an SCF context; destroys all initialized SCF objects.
*/
static void
{
int i;
for (i = 0; i < VS_NUM_PROPIDS; i++) {
}
if (vsc->vscf_pgroup)
if (vsc->vscf_handle)
}
/*
* vs_validate
*
* Validate property identified in propid.
*
* Returns: VS_ERR_NONE
* VS_ERR_INVALID_VALUE
* VS_ERR_INVALID_PROPERTY
*/
static int
{
switch (propid) {
case VS_PROPID_MAXSIZE:
return (VS_ERR_INVALID_VALUE);
break;
case VS_PROPID_MAXSIZE_ACTION:
break;
case VS_PROPID_TYPES:
return (VS_ERR_INVALID_VALUE);
break;
case VS_PROPID_SE_ENABLE:
break;
case VS_PROPID_SE_PORT:
return (VS_ERR_INVALID_VALUE);
break;
case VS_PROPID_SE_HOST:
return (VS_ERR_INVALID_VALUE);
break;
case VS_PROPID_SE_MAXCONN:
return (VS_ERR_INVALID_VALUE);
break;
case VS_PROPID_VALUE_AUTH:
case VS_PROPID_VLOG:
break;
default:
return (VS_ERR_INVALID_PROPERTY);
}
return (VS_ERR_NONE);
}
/*
* vs_props_validate
*
* Validate properties identified in propids.
*
* Returns: VS_ERR_NONE
* VS_ERR_INVALID_VALUE
* VS_ERR_INVALID_PROPERTY
*/
int
{
return (VS_ERR_INVALID_PROPERTY);
continue;
return (VS_ERR_INVALID_VALUE);
}
return (VS_ERR_NONE);
}
/*
* vs_props_se_validate
*
* Validate properties identified in propids.
*
* Returns: VS_ERR_NONE
* VS_ERR_INVALID_VALUE
* VS_ERR_INVALID_PROPERTY
*/
int
{
return (VS_ERR_INVALID_PROPERTY);
continue;
return (VS_ERR_INVALID_VALUE);
}
return (VS_ERR_NONE);
}
/*
* vs_is_valid_types
*
* Checks that types property is a valid format:
* - doesn't exceed VS_VAL_TYPES_MAX
* - doesn't contain VS_VAL_TYPES_INVALID_CHARS
* - is correctly formatted - passes the parsing tests
*
* Returns 1 on success, 0 on failure
*/
static int
vs_is_valid_types(const char *types)
{
char buf[VS_VAL_TYPES_LEN];
return (0);
return (0);
return (0);
return (1);
}
/*
* vs_is_valid_host
*
* Returns 1 on success, 0 on failure
*/
static int
vs_is_valid_host(const char *host)
{
long naddr;
const char *p;
return (0);
/* ip address */
return (0);
if ((naddr & IN_CLASSA_NET) == 0)
return (0);
if ((naddr & IN_CLASSC_HOST) == 0)
return (0);
} else {
/* hostname */
p = host;
while (*p != '\0') {
if (!isascii(*p))
return (0);
if (isalnum(*p) ||
(*p == '.') || (*p == '-') || (*p == '_')) {
++p;
} else {
return (0);
}
}
}
return (1);
}
/*
* vs_parse_types
*
* Replace comma separators with '\0'.
*
* Types contains comma separated rules each beginning with +|-
* - embedded commas are escaped by backslash
* - backslash is escaped by backslash
* - a single backslash not followed by comma is illegal
*
* On entry to the function len must contain the length of
* the buffer. On sucecssful exit len will contain the length
* of the parsed data within the buffer.
*
* Returns 0 on success, -1 on failure
*/
int
{
char *p = (char *)types;
char *b = buf;
return (-1);
return (-1);
while (*p) {
switch (*p) {
case VS_TYPES_SEP:
if (*(p + 1) &&
return (-1);
*b = '\0';
break;
case VS_TYPES_ESCAPE:
++p;
if (*p == VS_TYPES_ESCAPE || *p == VS_TYPES_SEP)
*b = *p;
else
return (-1);
break;
default:
*b = *p;
}
++p;
++b;
}
return (0);
}
/*
* vs_statistics
*/
int
{
return (VS_ERR_SYS);
return (VS_ERR_SYS);
}
return (VS_ERR_DAEMON_COMM);
}
else
return (rc);
}
/*
* vs_statistics_reset
*/
int
{
/* ensure that caller has authorization to reset stats */
return (rc);
return (VS_ERR_SYS);
return (VS_ERR_DAEMON_COMM);
}
return (rc);
}
/*
* Door call with retries.
*
* Returns VS_ERR_NONE on success, otherwise VS_ERR_DAEMON_COMM.
*/
static int
{
int rc = -1;
int i;
for (i = 0; i < VS_DOOR_CALL_RETRIES; ++i) {
errno = 0;
break;
break;
}
}
/*
* vs_checkauth
*/
static int
vs_checkauth(char *auth)
{
return (VS_ERR_SYS);
return (VS_ERR_AUTH);
}
return (VS_ERR_NONE);
}
/*
* vs_props_get_engines
*
* On input, count specifies the maximum number of engine ids to
* return. engids must be an array with count entries.
* On return, count specifies the number of engine ids being
* returned in engids.
*
* Caller is responsible for free'ing the engids allocated herein.
*/
static int
{
int i, prefix_len;
char pgname[VS_PGNAME_ENGINE_LEN];
if (((vs_scf_ctx_open(&vsc)) != 0) ||
SCF_GROUP_APPLICATION) != 0)) {
return (VS_ERR_SCF);
}
for (i = 0; i < *count; i++)
i = 0;
while ((i < VS_SE_MAX) &&
VS_PGNAME_ENGINE_LEN) < 0) {
return (VS_ERR_SCF);
}
if (++i == *count)
break;
}
}
}
*count = i;
return (VS_ERR_NONE);
}
/*
* vs_scf_pg_count
*/
static int
vs_scf_pg_count(void)
{
int count = 0;
if ((vs_scf_ctx_open(&vsc) != 0) ||
SCF_GROUP_APPLICATION) != 0)) {
return (-1);
}
++count;
return (count);
}
/*
* vs_engid_to_pgname
*
* To convert an engine id (engid) to a property group name (pgname),
* the engine id is prefixed with VS_PGNAME_ENGINE_PREFIX.
*/
static void
{
}
/*
* vs_strtonum
*
* Converts a size string in the format into an integer.
*
* A size string is a numeric value followed by an optional unit
* specifier which is used as a multiplier to calculate a raw
* number.
* The size string format is: N[.N][KMGTP][B]
*
* The numeric value can contain a decimal portion. Unit specifiers
* are either a one-character or two-character string; i.e. "K" or
* "KB" for kilobytes. Unit specifiers must follow the numeric portion
* immediately, and are not case-sensitive.
*
* If either "B" is specified, or there is no unit specifier portion
* in the string, the numeric value is calculated with no multiplier
* (assumes a basic unit of "bytes").
*
* Returns:
* -1: Failure; errno set to specify the error.
* 0: Success.
*/
int
{
char *end;
int shift;
double fval;
*num = 0;
/* Check to see if this looks like a number. */
return (-1);
}
/* Rely on stroll() to process the numeric portion. */
errno = 0;
/*
* Check for ERANGE, which indicates that the value is too large to
* fit in a 64-bit value.
*/
if (errno != 0)
return (-1);
/*
* If we have a decimal value, then do the computation with floating
* point arithmetic. Otherwise, use standard arithmetic.
*/
if (*end == '.') {
return (-1); /* errno set */
if (fval > UINT64_MAX) {
return (-1);
}
} else {
return (-1); /* errno set */
/* Check for overflow */
return (-1);
}
}
return (0);
}
/*
* vs_strtoshift
*
* Converts a unit specifier string into a number of bits that
* a numeric value must be shifted.
*
* Returns:
* -1: Failure; errno set to specify the error.
* >-1: Success; the shift count.
*
*/
static int
vs_strtoshift(const char *buf)
{
const char *ends = "BKMGTPEZ";
int i;
if (buf[0] == '\0')
return (0);
break;
}
return (-1);
}
/* Allow trailing 'b' characters except in the case of 'BB'. */
return (10 * i);
}
return (-1);
}