/*
* 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;
}