/* * Copyright (C) Sumit Bose 2009 * * 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 3 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include "util.h" #include "xml_helper.h" #include "ipaaction.h" #include "output_handler.h" /* If a default namespace is defined * * IMPORTANT: XPath 1.0 has no concept of a default namespace. Unprefixed * names in XPath only match names which have no namespace. So, if the * document uses a default namespace, it is required to associate a non-empty * prefix with the default namespace via register-namespace and add that * prefix to names in XPath expressions intended to match nodes in the default * namespace. */ xmlChar *default_namespace_prefix = (xmlChar *) "def"; /** * \brief Validate a XML document with the help of an RELAX NG file * * An already parsed XML ducument can be validated against a RELAX NG schema * file. * * \param doc pointer to the already parsed XML document * \param rng_file_name the name of the RELAX NG shcema file * * \return 0 if the XML document is valid with respect to the RELAX NG schema * file, -1 if an error occured or the document is not valid. * */ int validate_with_rng(xmlDocPtr doc, const char *rng_file_name) { int ret=-1; xmlRelaxNGParserCtxtPtr rng_parser_context=NULL; xmlRelaxNGPtr rng_schema=NULL; xmlRelaxNGValidCtxtPtr rng_context=NULL; rng_parser_context = xmlRelaxNGNewParserCtxt(rng_file_name); CHECK(rng_parser_context, NULL, ("Failed to parse RNG file\n"), goto cleanup); rng_schema = xmlRelaxNGParse(rng_parser_context); CHECK(rng_schema, NULL, ("Failed to create RNG schema\n"), goto cleanup); rng_context = xmlRelaxNGNewValidCtxt(rng_schema); CHECK(rng_context, NULL, ("Failed to create RNG context\n"), goto cleanup); if (xmlRelaxNGValidateDoc(rng_context, doc) == 0) { DEBUG(3, ("The document is valid.\n")); ret=0; } else { DEBUG(0, ("Error during validation.\n")); goto cleanup; } cleanup: xmlRelaxNGFreeValidCtxt(rng_context); xmlRelaxNGFree(rng_schema); xmlRelaxNGFreeParserCtxt(rng_parser_context); return ret; } /** * \brief Validate a XML policy file * * Call this function before any further processing of a XML policy file. It * will extract the name of the RELAX NG schema file from the metadata section * together with other information and validate the file accordingly. * * \param policy_file_name name of the XML policy file * \param ipa_policy_type will contain the IPA policy type, i.e. action, * config or role, if the function returns successfully * \param xslt_file_name will contain the name of the XSLT file if the IPA * policy is either config or role (action do not need an XSLT) and if the * function returns successfully * * \return 0 on success, -1 if an error occured * */ int validate_policy(const char *policy_file_name, char **ipa_policy_type, char **xslt_file_name) { int ret; xmlDocPtr doc=NULL; char *rng_file_name=NULL; xmlChar xpath_expr[XMLCHARLEN]; doc = xmlParseFile(policy_file_name); CHECK(doc, NULL, ("Cannot parse document %s!\n", policy_file_name), return -1); xmlStrPrintf(xpath_expr, XMLCHARLEN, (xmlChar *) "//%s:RNGfile", default_namespace_prefix); rng_file_name = find_by_xpath(doc, xpath_expr, FIND_VALUE); CHECK(rng_file_name, NULL, ("Name of RELANX NG schema file not found.\n"), goto failed); DEBUG(3, ("Found name of RELAX NG schema file: %s\n", rng_file_name)); ret=validate_with_rng(doc, rng_file_name); CHECK(ret, -1, ("Validation failed for %s\n", policy_file_name), goto failed); xmlStrPrintf(xpath_expr, XMLCHARLEN, (xmlChar *) "//%s:ipa/*[2]", default_namespace_prefix); *ipa_policy_type = find_by_xpath(doc, xpath_expr, FIND_NAME); CHECK(*ipa_policy_type, NULL, ("Type of IPA policy not found.\n"), goto failed); DEBUG(3, ("Found IPA policy type: %s\n", *ipa_policy_type)); if ( strncmp(*ipa_policy_type, "ipaconfig",9) != 0 && strncmp(*ipa_policy_type, "iparole",7) != 0 && strncmp(*ipa_policy_type, "ipaaction",9) != 0) { DEBUG(0,("unknown IPA ploicy type\n")); exit(1); } if (strncmp(*ipa_policy_type, "ipaaction", 9)!=0) { xmlStrPrintf(xpath_expr, XMLCHARLEN, (xmlChar *) "//%s:XSLTfile", default_namespace_prefix); *xslt_file_name = find_by_xpath(doc, xpath_expr, FIND_VALUE); CHECK(*xslt_file_name, NULL, ("Name of XSLT file not found.\n"), goto failed); DEBUG(3, ("Found name of XSLT file: %s\n", *xslt_file_name)); } free(rng_file_name); xmlFreeDoc(doc); xmlCleanupParser(); return 0; failed: free(rng_file_name); xmlFreeDoc(doc); xmlCleanupParser(); return -1; } /** * \brief Show all attributes of an XML node * * This is a debung function to print all attribute of a XML node. * * \param node pointer to the XML node * * \return 0 * */ int print_all_attributes(const xmlNode *node) { xmlAttr *cur; cur=node->properties; while(cur!=NULL) { DEBUG(3, ("found attribute '%s' with value '%s'.\n", cur->name, XML_GET_CONTENT(cur->children))); cur=cur->next; } return 0; } xmlChar *get_default_namespace(xmlDocPtr doc) { xmlNodePtr root_node; xmlChar *default_namespace; root_node = xmlDocGetRootElement(doc); CHECK(root_node, NULL, ("Cannot find root node of the current document!\n"), return NULL); if (xmlStrncasecmp(root_node->name, (xmlChar *) "IPA", XMLCHARLEN) != 0) { DEBUG(0, ("Name of root node of the current document has to be 'ipa'!\n")); exit(1); } CHECK(root_node->ns->href, NULL, ("Root node of the current document must define a namespace!\n"), return NULL); default_namespace = xmlStrdup(root_node->ns->href); CHECK(default_namespace, NULL, ("Cannot copy namespace!\n"), return NULL); DEBUG(3, ("Default namespace is %s\n", default_namespace)); return default_namespace; } /** * \brief find a single name or value defined by a XPath expression * * This function will return the name or the value of a XML node which is * selected by a XPath expression. if more than one value is found it is * considered as an error and NULL is returned. * * \param doc pointer to the already parsed XML document * \param xpath_expr a XPath expression describing the node to search for * \param type use FIND_NAME to return the name and FIND_VALUE to return the * value of the node * * \return pointer to the found string or NULL in case of an error * */ char *find_by_xpath(const xmlDocPtr doc, const xmlChar * xpath_expr, const int type) { int ret; xmlXPathContextPtr xpath_context=NULL; xmlXPathObjectPtr xpath_obj=NULL; char *result = NULL; xmlChar *namespace=NULL; namespace = get_default_namespace(doc); CHECK(namespace, NULL, ("No default namespace found.\n"), return NULL); /* Create xpath evaluation context */ xpath_context = xmlXPathNewContext(doc); CHECK(xpath_context, NULL, ("Error: unable to create new XPath context\n"), goto failed); /* Register a namespace */ ret=xmlXPathRegisterNs(xpath_context, default_namespace_prefix, namespace); CHECK(ret, -1, ("Error: unable to register NS with prefix=\"%s\" and href=\"%s\"\n", default_namespace_prefix , namespace), goto failed); /* Evaluate xpath expression */ xpath_obj = xmlXPathEvalExpression(xpath_expr, xpath_context); CHECK(xpath_obj, NULL, ("Error: unable to evaluate xpath expression \"%s\"\n", xpath_expr), goto failed); if (xmlXPathNodeSetIsEmpty(xpath_obj->nodesetval)) { DEBUG(0, ("Nothing found for %s\n", xpath_expr)); goto failed; } else if (xmlXPathNodeSetGetLength(xpath_obj->nodesetval) != 1) { DEBUG(0, ("More than one node found for %s!", xpath_expr)); goto failed; } else { switch (type) { case FIND_NAME: result = strdup((char *) xpath_obj->nodesetval->nodeTab[0]->name); break; case FIND_VALUE: result = (char *) xmlNodeListGetString(doc, xpath_obj->nodesetval->nodeTab[0]-> xmlChildrenNode, 1); break; default: DEBUG(0,("Unknow search type %d\n")); } } xmlFree(namespace); xmlXPathFreeObject(xpath_obj); xmlXPathFreeContext(xpath_context); return result; failed: xmlFree(namespace); xmlXPathFreeObject(xpath_obj); xmlXPathFreeContext(xpath_context); return NULL; } struct search_list { char *path; struct search_list *next; }; static struct search_list *search_list_head = NULL; static xmlExternalEntityLoader default_entity_loader = NULL; xmlParserInputPtr xml_external_entity_loader(const char *url, const char *id, xmlParserCtxtPtr ctxt) { const char *filename; char new_path[PATH_MAX]; xmlParserInputPtr input_ptr; warningSAXFunc saved_warning = NULL; errorSAXFunc saved_error = NULL; struct search_list *path_ptr; CHECK(default_entity_loader, NULL, ("No default entity loader, have you called setup_xml_search_path?\n"), return NULL); filename=strrchr(url, '/'); if (filename == NULL) filename=url; DEBUG(3,("looking for file %s.\n", filename)); if (ctxt != NULL && ctxt->sax != NULL) { saved_warning = ctxt->sax->warning; ctxt->sax->warning = NULL; saved_error = ctxt->sax->error; ctxt->sax->error = NULL; } input_ptr=default_entity_loader(url, id, ctxt); if (input_ptr != NULL) { DEBUG(3, ("found: %s %s\n",url, id)); if (saved_warning != NULL) ctxt->sax->warning = saved_warning; if (saved_error != NULL) ctxt->sax->error = saved_error; return input_ptr; } path_ptr = search_list_head; while ( path_ptr != NULL ) { new_path[0]='\0'; strncat(new_path, path_ptr->path, PATH_MAX-1); strncat(new_path, filename, PATH_MAX-strlen(new_path)-1); input_ptr=default_entity_loader(new_path, id, ctxt); if (input_ptr != NULL) { DEBUG(3, ("found: %s %s\n",new_path, id)); if (saved_warning != NULL) ctxt->sax->warning = saved_warning; if (saved_error != NULL) ctxt->sax->error = saved_error; return input_ptr; } path_ptr = path_ptr->next; } DEBUG(0, ("entity NOT found: %s %s\n",new_path, id)); if (saved_warning != NULL) ctxt->sax->warning = saved_warning; if (saved_error != NULL) ctxt->sax->error = saved_error; return NULL; } int setup_xml_search_path(const char *path) { struct search_list *path_ptr; if (default_entity_loader==NULL) { default_entity_loader = xmlGetExternalEntityLoader(); CHECK(default_entity_loader, NULL, ("Cannot find default entity loader.\n"), return -1); xmlSetExternalEntityLoader(xml_external_entity_loader); } if (path != NULL ) { DEBUG(3,("Adding %s to xml search path.\n", path)); path_ptr = search_list_head; while ( path_ptr != NULL ) { path_ptr = path_ptr->next; } path_ptr = malloc(sizeof(struct search_list)); CHECK(path_ptr, NULL, ("malloc failed.\n"), exit(1)); path_ptr->path=strdup(path); CHECK(path_ptr->path, NULL, ("strdup failed.\n"), exit(1)); path_ptr->next=search_list_head; search_list_head=path_ptr; } return 0; } int print_xml_search_path(void) { struct search_list *path_ptr; path_ptr = search_list_head; while ( path_ptr != NULL ) { DEBUG(0,("search path element: %s\n",path_ptr->path)); path_ptr = path_ptr->next; } return 0; } int free_xml_search_path(void) { struct search_list *path_ptr; struct search_list *next_path_ptr; path_ptr = search_list_head; while ( path_ptr != NULL ) { DEBUG(0,("freeing search path element: %s\n",path_ptr->path)); next_path_ptr = path_ptr->next; free(path_ptr->path); free(path_ptr); path_ptr=next_path_ptr; } return 0; } int process_policy(const char *policy_file_name) { int ret=0; char *ipa_policy_type=NULL; char *xslt_file_name=NULL; ret=validate_policy(policy_file_name, &ipa_policy_type, &xslt_file_name); if ( ret == -1 ) { DEBUG(0,("Invalid policy %s.\n", policy_file_name)); goto cleanup; } if ( strncmp( ipa_policy_type, "ipaaction", 9)==0) { handle_ipaaction(policy_file_name); } else { find_output_handler(policy_file_name, xslt_file_name); } cleanup: xmlCleanupParser(); free(xslt_file_name); free(ipa_policy_type); return ret; }