diff options
Diffstat (limited to 'usr/src/lib/xml')
-rw-r--r-- | usr/src/lib/xml/os_dtd.c | 238 | ||||
-rw-r--r-- | usr/src/lib/xml/os_dtd.h | 49 |
2 files changed, 287 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); +} diff --git a/usr/src/lib/xml/os_dtd.h b/usr/src/lib/xml/os_dtd.h new file mode 100644 index 0000000000..c6fe24a293 --- /dev/null +++ b/usr/src/lib/xml/os_dtd.h @@ -0,0 +1,49 @@ +/* + * 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. + */ + +#ifndef _OS_DTD_H +#define _OS_DTD_H + +/* + * Utility functions for working with XML documents that are validated against + * Document Type Definitions (DTD) shipped in the operating system. + */ + +#include <libxml/parser.h> +#include <libxml/xmlmemory.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum os_dtd_id { + OS_DTD_UNKNOWN = 0, + OS_DTD_ZONES_BRAND, + OS_DTD_ZONES_ZONE, + OS_DTD_ZONES_PLATFORM, + OS_DTD_SMF_SERVICE_BUNDLE +} os_dtd_id_t; + +typedef struct os_dtd_path os_dtd_path_t; + +extern os_dtd_id_t os_dtd_identify(xmlDocPtr); +extern int os_dtd_attach(xmlDocPtr, os_dtd_id_t); +extern int os_dtd_validate(xmlDocPtr, boolean_t, boolean_t *); + +#ifdef __cplusplus +} +#endif + +#endif /* _OS_DTD_H */ |