diff options
Diffstat (limited to 'usr/src/lib/xml/os_dtd.c')
| -rw-r--r-- | usr/src/lib/xml/os_dtd.c | 238 | 
1 files changed, 238 insertions, 0 deletions
| diff --git a/usr/src/lib/xml/os_dtd.c b/usr/src/lib/xml/os_dtd.c new file mode 100644 index 0000000000..579e99ba3c --- /dev/null +++ b/usr/src/lib/xml/os_dtd.c @@ -0,0 +1,238 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source.  A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2015 Joyent, Inc. + */ + +/* + * Utility functions for working with XML documents that are validated against + * Document Type Definitions (DTD) shipped in the operating system. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <strings.h> +#include <errno.h> +#include <zone.h> + +#include <libxml/parser.h> +#include <libxml/xmlmemory.h> + +#include "os_dtd.h" + +struct os_dtd_path { +	os_dtd_id_t odp_id; +	const char *odp_name; +	const char *odp_public_ident; +	const char *odp_path; +}; + +static os_dtd_path_t os_dtd_paths[] = { +	/* +	 * DTDs for Zones infrastructure: +	 */ +	{ OS_DTD_ZONES_BRAND, "brand", +	    "-//Sun Microsystems Inc//DTD Brands//EN", +	    "/usr/share/lib/xml/dtd/brand.dtd.1" }, +	{ OS_DTD_ZONES_ZONE, "zone", +	    "-//Sun Microsystems Inc//DTD Zones//EN", +	    "/usr/share/lib/xml/dtd/zonecfg.dtd.1" }, +	{ OS_DTD_ZONES_PLATFORM, "platform", +	    "-//Sun Microsystems Inc//Zones Platform//EN", +	    "/usr/share/lib/xml/dtd/zone_platform.dtd.1" }, + +	/* +	 * DTDs for smf(5): +	 */ +	{ OS_DTD_SMF_SERVICE_BUNDLE, "service_bundle", +	    NULL, +	    "/usr/share/lib/xml/dtd/service_bundle.dtd.1" }, + +	{ OS_DTD_UNKNOWN, NULL, NULL, NULL } +}; + +/* + * Check this document to see if it references the public identifier of a + * well-known DTD that we ship with the operating system.  If there is no DTD, + * or the public identifier is unknown to us, return OS_DTD_UNKNOWN. + */ +os_dtd_id_t +os_dtd_identify(xmlDocPtr doc) +{ +	xmlDtdPtr dp; +	int i; + +	if ((dp = xmlGetIntSubset(doc)) == NULL) { +		/* +		 * This document does not have an internal subset pointing +		 * to a DTD. +		 */ +		errno = EIO; +		return (OS_DTD_UNKNOWN); +	} + +	/* +	 * The use of a symbolic name via the public identifier is preferred. +	 * Check to see if the document refers to a public identifier for any +	 * well-known DTD: +	 */ +	for (i = 0; os_dtd_paths[i].odp_id != OS_DTD_UNKNOWN; i++) { +		os_dtd_path_t *odp = &os_dtd_paths[i]; +		const xmlChar *pubid = (const xmlChar *)odp->odp_public_ident; + +		if (dp->ExternalID == NULL || odp->odp_public_ident == NULL) { +			continue; +		} + +		if (xmlStrEqual(pubid, dp->ExternalID)) { +			return (odp->odp_id); +		} +	} + +	/* +	 * If a public identifier is not present, or does not match any known +	 * DTD, fall back to inspecting the system identifier. +	 */ +	for (i = 0; os_dtd_paths[i].odp_id != OS_DTD_UNKNOWN; i++) { +		os_dtd_path_t *odp = &os_dtd_paths[i]; +		char uri[sizeof ("file://") + MAXPATHLEN]; +		const xmlChar *path = (const xmlChar *)odp->odp_path; + +		if (dp->SystemID == NULL || odp->odp_path == NULL) { +			continue; +		} + +		/* +		 * The system identifier may be a regular path. +		 */ +		if (xmlStrEqual(path, dp->SystemID)) { +			return (odp->odp_id); +		} + +		/* +		 * The system identifier may also be a "file://" +		 * URI referring to a path: +		 */ +		(void) snprintf(uri, sizeof (uri), "file://%s", odp->odp_path); +		if (xmlStrEqual((const xmlChar *)uri, dp->SystemID)) { +			return (odp->odp_id); +		} +	} + +	errno = ENOENT; +	return (OS_DTD_UNKNOWN); +} + +static os_dtd_path_t * +os_dtd_lookup(os_dtd_id_t id) +{ +	int i; + +	for (i = 0; os_dtd_paths[i].odp_id != OS_DTD_UNKNOWN; i++) { +		os_dtd_path_t *odp = &os_dtd_paths[i]; + +		if (odp->odp_id == id) { +			return (odp); +		} +	} + +	errno = ENOENT; +	return (NULL); +} + +/* + * If this document references a DTD, remove that reference (the "internal + * subset").  Install a new internal subset reference to the well-known + * operating system DTD passed by the caller.  The URI in this reference will + * respect the current native system prefix (e.g. "/native") if there is one, + * such as when running in a branded zone. + */ +int +os_dtd_attach(xmlDocPtr doc, os_dtd_id_t id) +{ +	xmlDtdPtr dp; +	os_dtd_path_t *odp; +	const char *zroot = zone_get_nroot(); +	char uri[sizeof ("file://") + MAXPATHLEN]; + +	if ((odp = os_dtd_lookup(id)) == NULL) { +		return (-1); +	} + +	if ((dp = xmlGetIntSubset(doc)) != NULL) { +		/* +		 * This document already has an internal subset.  Remove it +		 * before attaching the new one. +		 */ +		xmlUnlinkNode((xmlNodePtr)dp); +		xmlFreeNode((xmlNodePtr)dp); +	} + +	/* +	 * The "system identifier" of this internal subset must refer to the +	 * path in the filesystem where the DTD file (the external subset) is +	 * stored.  If we are running in a branded zone, that file may be at a +	 * different path (e.g. under "/native"). +	 */ +	(void) snprintf(uri, sizeof (uri), "file://%s%s", zroot != NULL ? +	    zroot : "", odp->odp_path); + +	if (xmlCreateIntSubset(doc, (const xmlChar *)odp->odp_name, +	    (const xmlChar *)odp->odp_public_ident, +	    (const xmlChar *)uri) == NULL) { +		errno = EIO; +		return (-1); +	} + +	return (0); +} + +/*ARGSUSED*/ +static void +os_dtd_print_nothing(void *ctx, const char *msg, ...) +{ +} + +int +os_dtd_validate(xmlDocPtr doc, boolean_t emit_messages, boolean_t *valid) +{ +	int ret; +	xmlValidCtxtPtr cvp; +	os_dtd_id_t dtdid; + +	if ((dtdid = os_dtd_identify(doc)) != OS_DTD_UNKNOWN) { +		/* +		 * This document refers to a well-known DTD shipped with +		 * the operating system.  Ensure that it points to the +		 * correct local path for validation in the current context. +		 */ +		if (os_dtd_attach(doc, dtdid) != 0) { +			return (-1); +		} +	} + +	if ((cvp = xmlNewValidCtxt()) == NULL) { +		return (-1); +	} + +	if (!emit_messages) { +		cvp->error = os_dtd_print_nothing; +		cvp->warning = os_dtd_print_nothing; +	} + +	ret = xmlValidateDocument(cvp, doc); +	xmlFreeValidCtxt(cvp); + +	*valid = (ret == 1 ? B_TRUE : B_FALSE); +	return (0); +} | 
