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, 2010, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include <assert.h>
2N/A#include <repcache_protocol.h>
2N/A#include "scf_type.h"
2N/A#include <errno.h>
2N/A#include <libgen.h>
2N/A#include <libscf_priv.h>
2N/A#include <stdlib.h>
2N/A#include <string.h>
2N/A#include <sys/types.h>
2N/A#include <sys/socket.h>
2N/A#include <arpa/inet.h>
2N/A#include <netinet/in.h>
2N/A
2N/A#define UTF8_TOP_N(n) \
2N/A (0xff ^ (0xff >> (n))) /* top N bits set */
2N/A
2N/A#define UTF8_BOTTOM_N(n) \
2N/A ((1 << (n)) - 1) /* bottom N bits set */
2N/A
2N/A/*
2N/A * The first byte of an n-byte UTF8 encoded character looks like:
2N/A *
2N/A * n bits
2N/A *
2N/A * 1 0xxxxxxx
2N/A * 2 110xxxxx
2N/A * 3 1110xxxx
2N/A * 4 11110xxx
2N/A * 5 111110xx
2N/A * 6 1111110x
2N/A *
2N/A * Continuation bytes are 01xxxxxx.
2N/A */
2N/A
2N/A#define UTF8_MAX_BYTES 6
2N/A
2N/A/*
2N/A * number of bits in an n-byte UTF-8 encoding. for multi-byte encodings,
2N/A * You get (7 - n) bits in the first byte, and 6 bits for each additional byte.
2N/A */
2N/A#define UTF8_BITS(n) /* 1 <= n <= 6 */ \
2N/A ((n) == 1)? 7 : \
2N/A (7 - (n) + 6 * ((n) - 1))
2N/A
2N/A#define UTF8_SINGLE_BYTE(c) \
2N/A (((c) & UTF8_TOP_N(1)) == 0) /* 0xxxxxxx */
2N/A
2N/A#define UTF8_HEAD_CHECK(c, n) /* 2 <= n <= 6 */ \
2N/A (((c) & UTF8_TOP_N((n) + 1)) == UTF8_TOP_N(n))
2N/A
2N/A#define UTF8_HEAD_VALUE(c, n) /* 2 <= n <= 6 */ \
2N/A ((c) & UTF8_BOTTOM_N(7 - (n))) /* 'x' mask */
2N/A
2N/A#define UTF8_CONT_CHECK(c) \
2N/A (((c) & UTF8_TOP_N(2)) == UTF8_TOP_N(1)) /* 10xxxxxx */
2N/A
2N/A/*
2N/A * adds in the 6 new bits from a continuation byte
2N/A */
2N/A#define UTF8_VALUE_UPDATE(v, c) \
2N/A (((v) << 6) | ((c) & UTF8_BOTTOM_N(6)))
2N/A
2N/A/*
2N/A * URI components
2N/A */
2N/A
2N/A#define URI_COMPONENT_COUNT 5
2N/A
2N/Aenum {
2N/A URI_SCHEME = 0x0, /* URI scheme */
2N/A URI_AUTHORITY, /* URI authority */
2N/A URI_PATH, /* URI path */
2N/A URI_QUERY, /* URI query */
2N/A URI_FRAGMENT /* URI fragment */
2N/A};
2N/A
2N/Astatic int
2N/Avalid_utf8(const char *str_arg)
2N/A{
2N/A const char *str = str_arg;
2N/A uint_t c;
2N/A uint32_t v;
2N/A int i, n;
2N/A
2N/A while ((c = *str++) != 0) {
2N/A if (UTF8_SINGLE_BYTE(c))
2N/A continue; /* ascii */
2N/A
2N/A for (n = 2; n <= UTF8_MAX_BYTES; n++)
2N/A if (UTF8_HEAD_CHECK(c, n))
2N/A break;
2N/A
2N/A if (n > UTF8_MAX_BYTES)
2N/A return (0); /* invalid head byte */
2N/A
2N/A v = UTF8_HEAD_VALUE(c, n);
2N/A
2N/A for (i = 1; i < n; i++) {
2N/A c = *str++;
2N/A if (!UTF8_CONT_CHECK(c))
2N/A return (0); /* invalid byte */
2N/A
2N/A v = UTF8_VALUE_UPDATE(v, c);
2N/A }
2N/A
2N/A /*
2N/A * if v could have been encoded in the next smallest
2N/A * encoding, the string is not well-formed UTF-8.
2N/A */
2N/A if ((v >> (UTF8_BITS(n - 1))) == 0)
2N/A return (0);
2N/A }
2N/A
2N/A /*
2N/A * we've reached the end of the string -- make sure it is short enough
2N/A */
2N/A return ((str - str_arg) < REP_PROTOCOL_VALUE_LEN);
2N/A}
2N/A
2N/Astatic int
2N/Avalid_string(const char *str)
2N/A{
2N/A return (strlen(str) < REP_PROTOCOL_VALUE_LEN);
2N/A}
2N/A
2N/Astatic int
2N/Avalid_opaque(const char *str_arg)
2N/A{
2N/A const char *str = str_arg;
2N/A uint_t c;
2N/A ptrdiff_t len;
2N/A
2N/A while ((c = *str++) != 0)
2N/A if ((c < '0' || c > '9') && (c < 'a' || c > 'f') &&
2N/A (c < 'A' || c > 'F'))
2N/A return (0); /* not hex digit */
2N/A
2N/A len = (str - str_arg) - 1; /* not counting NIL byte */
2N/A return ((len % 2) == 0 && len / 2 < REP_PROTOCOL_VALUE_LEN);
2N/A}
2N/A
2N/A/*
2N/A * Return 1 if the supplied parameter is a conformant URI (as defined
2N/A * by RFC 2396), 0 otherwise.
2N/A */
2N/Astatic int
2N/Avalid_uri(const char *str)
2N/A{
2N/A /*
2N/A * URI Regular Expression. Compiled with regcmp(1).
2N/A *
2N/A * ^(([^:/?#]+:){0,1})$0(//([^/?#]*)$1){0,1}([^?#]*)$2
2N/A * (?([^#]*)$3){0,1}(#(.*)$4){0,1}
2N/A */
2N/A char exp[] = {
2N/A 040, 074, 00, 060, 012, 0126, 05, 072, 057, 077, 043, 024,
2N/A 072, 057, 00, 00, 01, 014, 00, 00, 060, 020, 024, 057,
2N/A 024, 057, 074, 01, 0125, 04, 057, 077, 043, 014, 01, 01,
2N/A 057, 01, 00, 01, 074, 02, 0125, 03, 077, 043, 014, 02,
2N/A 02, 060, 014, 024, 077, 074, 03, 0125, 02, 043, 014, 03,
2N/A 03, 057, 02, 00, 01, 060, 012, 024, 043, 074, 04, 021,
2N/A 014, 04, 04, 057, 03, 00, 01, 064, 00,
2N/A 0};
2N/A char uri[URI_COMPONENT_COUNT][REP_PROTOCOL_VALUE_LEN];
2N/A
2N/A /*
2N/A * If the string is too long, then the URI cannot be valid. Also,
2N/A * this protects against buffer overflow attacks on the uri array.
2N/A */
2N/A if (strlen(str) >= REP_PROTOCOL_VALUE_LEN)
2N/A return (0);
2N/A
2N/A if (regex(exp, str, uri[URI_SCHEME], uri[URI_AUTHORITY], uri[URI_PATH],
2N/A uri[URI_QUERY], uri[URI_FRAGMENT]) == NULL) {
2N/A return (0);
2N/A }
2N/A /*
2N/A * To be a valid URI, the length of the URI_PATH must not be zero
2N/A */
2N/A if (strlen(uri[URI_PATH]) == 0) {
2N/A return (0);
2N/A }
2N/A return (1);
2N/A}
2N/A
2N/A/*
2N/A * Return 1 if the supplied parameter is a conformant fmri, 0
2N/A * otherwise.
2N/A */
2N/Astatic int
2N/Avalid_fmri(const char *str)
2N/A{
2N/A int ret;
2N/A char fmri[REP_PROTOCOL_VALUE_LEN] = { 0 };
2N/A
2N/A /*
2N/A * Try to parse the fmri, if we can parse it then it
2N/A * must be syntactically correct. Work on a copy of
2N/A * the fmri since the parsing process can modify the
2N/A * supplied string.
2N/A */
2N/A if (strlcpy(fmri, str, sizeof (fmri)) >= sizeof (fmri))
2N/A return (0);
2N/A
2N/A ret = ! scf_parse_fmri(fmri, NULL, NULL, NULL, NULL, NULL, NULL);
2N/A
2N/A return (ret);
2N/A}
2N/A
2N/A/*
2N/A * check_prefix()
2N/A * Return 1 if the prefix is a valid IPv4 or IPv6 network prefix, 0 otherwise
2N/A */
2N/Astatic int
2N/Acheck_net_prefix(const char *p, int max_len)
2N/A{
2N/A char *end;
2N/A int len;
2N/A
2N/A len = strtol(p, &end, 10);
2N/A if (p == end || len < 0 || len > max_len)
2N/A return (0);
2N/A
2N/A return (1);
2N/A}
2N/A
2N/A/*
2N/A * Return 1 if the supplied IP address is valid, 0 otherwise.
2N/A */
2N/Astatic int
2N/Avalid_ip(int af, const char *str)
2N/A{
2N/A void *unused[4];
2N/A const char *addr = str;
2N/A char buf[INET6_ADDRSTRLEN]; /* enough for both IPv4 and IPv6 */
2N/A char *net_prefix;
2N/A int buf_sz;
2N/A int plen;
2N/A
2N/A switch (af) {
2N/A case AF_INET:
2N/A buf_sz = INET_ADDRSTRLEN;
2N/A plen = 32; /* bit size of an IPv4 */
2N/A break;
2N/A
2N/A case AF_INET6:
2N/A buf_sz = INET6_ADDRSTRLEN;
2N/A plen = 128; /* bit size of an IPv6 */
2N/A break;
2N/A
2N/A default:
2N/A assert(0);
2N/A abort();
2N/A }
2N/A
2N/A /* check network prefix for the IP address */
2N/A if ((net_prefix = strchr(str, '/')) != NULL) {
2N/A if (check_net_prefix(++net_prefix, plen) == 0)
2N/A return (0);
2N/A
2N/A (void) strlcpy(buf, str, buf_sz);
2N/A if ((net_prefix = strchr(buf, '/')) != NULL)
2N/A *net_prefix = '\0';
2N/A
2N/A addr = buf;
2N/A }
2N/A
2N/A return (inet_pton(af, addr, unused));
2N/A}
2N/A
2N/Arep_protocol_value_type_t
2N/Ascf_proto_underlying_type(rep_protocol_value_type_t t)
2N/A{
2N/A switch (t) {
2N/A case REP_PROTOCOL_TYPE_BOOLEAN:
2N/A case REP_PROTOCOL_TYPE_COUNT:
2N/A case REP_PROTOCOL_TYPE_INTEGER:
2N/A case REP_PROTOCOL_TYPE_TIME:
2N/A case REP_PROTOCOL_TYPE_STRING:
2N/A case REP_PROTOCOL_TYPE_OPAQUE:
2N/A return (t);
2N/A
2N/A case REP_PROTOCOL_SUBTYPE_USTRING:
2N/A return (REP_PROTOCOL_TYPE_STRING);
2N/A
2N/A case REP_PROTOCOL_SUBTYPE_URI:
2N/A return (REP_PROTOCOL_SUBTYPE_USTRING);
2N/A case REP_PROTOCOL_SUBTYPE_FMRI:
2N/A return (REP_PROTOCOL_SUBTYPE_URI);
2N/A
2N/A case REP_PROTOCOL_SUBTYPE_HOST:
2N/A return (REP_PROTOCOL_SUBTYPE_USTRING);
2N/A case REP_PROTOCOL_SUBTYPE_HOSTNAME:
2N/A return (REP_PROTOCOL_SUBTYPE_HOST);
2N/A case REP_PROTOCOL_SUBTYPE_NETADDR:
2N/A return (REP_PROTOCOL_SUBTYPE_HOST);
2N/A case REP_PROTOCOL_SUBTYPE_NETADDR_V4:
2N/A return (REP_PROTOCOL_SUBTYPE_NETADDR);
2N/A case REP_PROTOCOL_SUBTYPE_NETADDR_V6:
2N/A return (REP_PROTOCOL_SUBTYPE_NETADDR);
2N/A
2N/A case REP_PROTOCOL_TYPE_INVALID:
2N/A default:
2N/A return (REP_PROTOCOL_TYPE_INVALID);
2N/A }
2N/A}
2N/A
2N/Aint
2N/Ascf_is_compatible_protocol_type(rep_protocol_value_type_t base,
2N/A rep_protocol_value_type_t new)
2N/A{
2N/A rep_protocol_value_type_t t, cur;
2N/A
2N/A if (base == REP_PROTOCOL_TYPE_INVALID)
2N/A return (0);
2N/A
2N/A if (base == new)
2N/A return (1);
2N/A
2N/A for (t = new; t != (cur = scf_proto_underlying_type(t)); t = cur) {
2N/A if (cur == REP_PROTOCOL_TYPE_INVALID)
2N/A return (0);
2N/A if (cur == base)
2N/A return (1); /* base is parent of new */
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/Astatic int
2N/Avalid_encoded_value(rep_protocol_value_type_t t, const char *v)
2N/A{
2N/A char *p;
2N/A ulong_t ns;
2N/A
2N/A switch (t) {
2N/A case REP_PROTOCOL_TYPE_BOOLEAN:
2N/A return ((*v == '0' || *v == '1') && v[1] == 0);
2N/A
2N/A case REP_PROTOCOL_TYPE_COUNT:
2N/A errno = 0;
2N/A if (strtoull(v, &p, 10) != 0 && *v == '0')
2N/A return (0);
2N/A return (errno == 0 && p != v && *p == 0);
2N/A
2N/A case REP_PROTOCOL_TYPE_INTEGER:
2N/A errno = 0;
2N/A if (strtoll(v, &p, 10) != 0 && *v == '0')
2N/A return (0);
2N/A return (errno == 0 && p != v && *p == 0);
2N/A
2N/A case REP_PROTOCOL_TYPE_TIME:
2N/A errno = 0;
2N/A (void) strtoll(v, &p, 10);
2N/A if (errno != 0 || p == v || (*p != 0 && *p != '.'))
2N/A return (0);
2N/A if (*p == '.') {
2N/A v = p + 1;
2N/A errno = 0;
2N/A ns = strtoul(v, &p, 10);
2N/A
2N/A /* must be exactly 9 digits */
2N/A if ((p - v) != 9 || errno != 0 || *p != 0)
2N/A return (0);
2N/A if (ns >= NANOSEC)
2N/A return (0);
2N/A }
2N/A return (1);
2N/A
2N/A case REP_PROTOCOL_TYPE_STRING:
2N/A return (valid_string(v));
2N/A
2N/A case REP_PROTOCOL_TYPE_OPAQUE:
2N/A return (valid_opaque(v));
2N/A
2N/A /*
2N/A * The remaining types are subtypes -- because of the way
2N/A * scf_validate_encoded_value() works, we can rely on the fact
2N/A * that v is a valid example of our base type. We only have to
2N/A * check our own additional restrictions.
2N/A */
2N/A case REP_PROTOCOL_SUBTYPE_USTRING:
2N/A return (valid_utf8(v));
2N/A
2N/A case REP_PROTOCOL_SUBTYPE_URI:
2N/A return (valid_uri(v));
2N/A
2N/A case REP_PROTOCOL_SUBTYPE_FMRI:
2N/A return (valid_fmri(v));
2N/A
2N/A case REP_PROTOCOL_SUBTYPE_HOST:
2N/A return (valid_encoded_value(REP_PROTOCOL_SUBTYPE_HOSTNAME, v) ||
2N/A valid_encoded_value(REP_PROTOCOL_SUBTYPE_NETADDR, v));
2N/A
2N/A case REP_PROTOCOL_SUBTYPE_HOSTNAME:
2N/A /* XXX check for valid hostname */
2N/A return (valid_utf8(v));
2N/A
2N/A case REP_PROTOCOL_SUBTYPE_NETADDR:
2N/A return (valid_ip(AF_INET, v) || valid_ip(AF_INET6, v));
2N/A
2N/A case REP_PROTOCOL_SUBTYPE_NETADDR_V4:
2N/A return (valid_ip(AF_INET, v));
2N/A
2N/A case REP_PROTOCOL_SUBTYPE_NETADDR_V6:
2N/A return (valid_ip(AF_INET6, v));
2N/A
2N/A case REP_PROTOCOL_TYPE_INVALID:
2N/A default:
2N/A return (0);
2N/A }
2N/A}
2N/A
2N/Aint
2N/Ascf_validate_encoded_value(rep_protocol_value_type_t t, const char *v)
2N/A{
2N/A rep_protocol_value_type_t base, cur;
2N/A
2N/A base = scf_proto_underlying_type(t);
2N/A while ((cur = scf_proto_underlying_type(base)) != base)
2N/A base = cur;
2N/A
2N/A if (base != t && !valid_encoded_value(base, v))
2N/A return (0);
2N/A
2N/A return (valid_encoded_value(t, v));
2N/A}