/* Copyright (C) 2005 Tresys Technology, LLC * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* * Author: jmowery@tresys.com * */ #include "sechecker.h" #include "sechk_parse.h" #include #include #include #include /* xml parser keywords */ #define SECHK_PARSE_SECHECKER_TAG "sechecker" #define SECHK_PARSE_PROFILE_TAG "profile" #define SECHK_PARSE_MODULE_TAG "module" #define SECHK_PARSE_OPTION_TAG "option" #define SECHK_PARSE_ITEM_TAG "item" #define SECHK_PARSE_OUTPUT_TAG "output" #define SECHK_PARSE_VALUE_ATTRIB "value" #define SECHK_PARSE_NAME_ATTRIB "name" #define SECHK_PARSE_VERSION_ATTRIB "version" #define SECHK_PARSE_OUTPUT_NONE "none" #define SECHK_PARSE_OUTPUT_QUIET "quiet" #define SECHK_PARSE_OUTPUT_SHORT "short" #define SECHK_PARSE_OUTPUT_VERBOSE "verbose" static char *build_dtd_path(void) { char *path = NULL; size_t path_sz = 0; #ifdef PROFILE_INSTALL_DIR if (apol_str_append(&path, &path_sz, "file://localhost/") == -1) return NULL; if (apol_str_append(&path, &path_sz, PROFILE_INSTALL_DIR) == -1) return NULL; if (apol_str_append(&path, &path_sz, "/sechecker.dtd") == -1) return NULL; return path; #endif return NULL; } /* Parsing functions */ /* Parse the configuration file. */ int sechk_lib_parse_xml_file(const char *filename, sechk_lib_t * lib) { xmlTextReaderPtr reader = NULL; xmlDtdPtr dtd = NULL; xmlDocPtr xml = NULL; xmlValidCtxtPtr ctxt = NULL; int tmp, ret = 0; char *dtd_path = NULL; /* this initializes the XML library and checks potential ABI mismatches * between the version it was compiled for and the actual shared * library used. */ LIBXML_TEST_VERSION; reader = xmlReaderForFile(filename, NULL, 0); if (!reader) { ret = errno; if (ret != ENOENT) fprintf(stderr, "Error: Could not create xmlReader.\n"); goto exit_err; } dtd_path = build_dtd_path(); if (!dtd_path) { fprintf(stderr, "Error: getting DTD path\n"); goto exit_err; } dtd = xmlParseDTD(NULL, (const xmlChar *)dtd_path); free(dtd_path); if (!dtd) { fprintf(stderr, "Error: parsing DTD\n"); goto exit_err; } xml = xmlParseFile(filename); if (!xml) { fprintf(stderr, "Error: parsing sechecker profile\n"); goto exit_err; } ctxt = xmlNewValidCtxt(); if (!ctxt) { fprintf(stderr, "Error: out of memory\n"); goto exit_err; } /* validate profile against the DTD */ if (xmlValidateDtd(ctxt, xml, dtd) == 0) { fprintf(stderr, "Error: SEChecker profile contains invalid XML. Aborting.\n"); goto exit_err; } xmlFreeValidCtxt(ctxt); xmlFreeDoc(xml); xmlFreeDtd(dtd); while (1) { ret = xmlTextReaderRead(reader); if (ret == -1) { ret = errno; fprintf(stderr, "Error: Error reading xml.\n"); goto exit_err; } if (ret == 0) /* no more nodes to read */ break; tmp = sechk_lib_process_xml_node(reader, lib); if (tmp == -1) goto exit_err; if (tmp == 1) { ret = xmlTextReaderNext(reader); if (ret == 0) break; if (ret == -1) { ret = errno; fprintf(stderr, "Error in xmlTextReaderNext()\n"); goto exit_err; } } } /* cleanup function for the XML library */ xmlCleanupParser(); xmlFreeTextReader(reader); return 0; exit_err: xmlCleanupParser(); if (reader) xmlFreeTextReader(reader); if (ret) errno = ret; return -1; } /* process a single node in the xml file */ int sechk_lib_process_xml_node(xmlTextReaderPtr reader, sechk_lib_t * lib) { xmlChar *attrib = NULL; int idx; sechk_name_value_t *nv = NULL; static xmlChar *option = NULL; static xmlChar *value = NULL; static sechk_module_t *current_module = NULL; switch (xmlTextReaderNodeType(reader)) { case XML_ELEMENT_DECL: /* closing tags */ if (xmlStrEqual(xmlTextReaderConstName(reader), (xmlChar *) SECHK_PARSE_MODULE_TAG) == 1) { current_module = NULL; } else if (xmlStrEqual(xmlTextReaderConstName(reader), (xmlChar *) SECHK_PARSE_OPTION_TAG) == 1) { free(option); option = NULL; } break; case XML_ELEMENT_NODE: /* opening tags */ if (xmlStrEqual(xmlTextReaderConstName(reader), (xmlChar *) SECHK_PARSE_SECHECKER_TAG) == 1) { /* parsing the tag */ attrib = xmlTextReaderGetAttribute(reader, (xmlChar *) SECHK_PARSE_VERSION_ATTRIB); if (attrib) { #ifdef SECHECKER_VERSION if (atof((const char *)attrib) > atof(SECHECKER_VERSION)) { fprintf(stderr, "Error: sechecker version in profile is incorrect: expected %1.1f got %1.1f\n", atof(SECHECKER_VERSION), atof((const char *)attrib)); goto exit_err; } #endif free(attrib); attrib = NULL; } else { fprintf(stderr, "Warning: sechecker version is not specified.\n"); } } else if (xmlStrEqual(xmlTextReaderConstName(reader), (xmlChar *) SECHK_PARSE_MODULE_TAG) == 1) { /* parsing the tag */ attrib = xmlTextReaderGetAttribute(reader, (xmlChar *) SECHK_PARSE_NAME_ATTRIB); if (attrib) { if ((idx = sechk_lib_get_module_idx((const char *)attrib, lib)) == -1) { fprintf(stderr, "Warning: module %s not found.\n", (const char *)attrib); return 1; /* not a fatal error */ } else { /* set the values on the existing module */ current_module = apol_vector_get_element(lib->modules, idx); current_module->selected = true; } free(attrib); attrib = NULL; } else { fprintf(stderr, "Error: module name is not specified.\n"); goto exit_err; } } else if (xmlStrEqual(xmlTextReaderConstName(reader), (xmlChar *) SECHK_PARSE_OPTION_TAG) == 1) { /* parsing the