/*
* 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
#include
#include
#include
#include
#include
#include
#include "util.h"
#include "xml_helper.h"
#include "helpers.h"
#define BACKUP_PATH "/var/spool/poliproc"
#define ORIGINAL_BACKUP_EXTENSION "ipa_original_backup_DO_NOT_DELETE"
static int copy_file(const char *src_name, const char *dest_name) {
int src_fd=-1;
int dest_fd=-1;
int ret=0;
struct stat stat_buffer;
void *src;
void *dest;
char *tmp_file_name=NULL;
tmp_file_name=(char *) malloc(strlen(dest_name)+8);
CHECK(tmp_file_name, NULL, ("malloc failed."), return -1);
strcpy(tmp_file_name, dest_name);
strcat(tmp_file_name, ".XXXXXX");
if ( (ret=lstat(src_name, &stat_buffer)) == -1) {
if (errno != ENOENT) {
DEBUG(0,("stat on %s failed: %s\n",src_name, strerror(errno)));
goto cleanup;
}
DEBUG(0,("stat on %s failed with ENOENT, I will create an empty copy.\n",src_name, strerror(errno)));
dest_fd=open_temporary_file(tmp_file_name, "0600", "root", "root", NULL);
CHECK(dest_fd, -1, ("Failed to open temporary file %s\n", tmp_file_name), goto cleanup);
close(dest_fd);
ret=rename(tmp_file_name, dest_name);
CHECK(ret, -1, ("Cannot rename %s to %s: %s\n", tmp_file_name, dest_name, strerror(errno) ), goto cleanup);
goto cleanup;
}
ret=-1;
src_fd = open(src_name, O_RDONLY);
CHECK(src_fd, -1, ("Failed to open %s read-only.\n", src_name), goto cleanup);
dest_fd=open_temporary_file(tmp_file_name, "0600", "root", "root", NULL);
CHECK(dest_fd, -1, ("Failed to open temporary file %s\n", tmp_file_name), goto cleanup);
ret=fstat(src_fd, &stat_buffer);
CHECK(ret, -1, ("Failed to stat file %s: %s\n", src_name, strerror(errno)), goto cleanup);
ret = lseek(dest_fd, stat_buffer.st_size - 1, SEEK_SET);
CHECK(ret, -1, ("Lseek failed for file %s: %s\n", dest_name, strerror(errno)), goto cleanup);
ret = write(dest_fd, "", 1);
if ( ret != 1 ) {
DEBUG(0, ("Failed to write one byte to %s: %s.\n", dest_name, strerror(errno)));
goto cleanup;
}
src = mmap(NULL, stat_buffer.st_size, PROT_READ, MAP_SHARED, src_fd, 0);
if ( src == MAP_FAILED ) {
DEBUG(0, ("Mmap failed for file %s: %s.\n", src_name, strerror(errno)));
goto cleanup;
}
dest = mmap(NULL, stat_buffer.st_size, PROT_READ | PROT_WRITE, MAP_SHARED,
dest_fd, 0);
if ( src == MAP_FAILED ) {
DEBUG(0, ("Mmap failed for file %s: %s.\n", src_name, strerror(errno)));
munmap(src, stat_buffer.st_size);
goto cleanup;
}
memcpy(dest, src, stat_buffer.st_size);
munmap(dest, stat_buffer.st_size);
munmap(src, stat_buffer.st_size);
close(dest_fd);
close(src_fd);
ret=rename(tmp_file_name, dest_name);
CHECK(ret, -1, ("Cannot rename %s to %s: %s\n", tmp_file_name, dest_name, strerror(errno) ), goto cleanup);
cleanup:
if (dest_fd != -1) close(dest_fd);
if (src_fd != -1) close(src_fd);
free(tmp_file_name);
return ret;
}
static int check_dir_create(const char *name) {
int ret;
struct stat stat_buffer;
if( (ret=stat(name, &stat_buffer)) == -1) {
DEBUG(0, ("Directory %s does not exist, creating it.\n", name, strerror(errno)));
ret=mkdir(name, 0600);
CHECK(ret, -1, ("mkdir %s failed: %s\n", name, strerror(errno)), return -1);
} else if (!S_ISDIR(stat_buffer.st_mode)) {
DEBUG(0,("%s is not a regular file!\n",name));
return -1;
}
return 0;
}
static int safe_config_file(const char *name, const char *app_name) {
int ret;
struct stat stat_buffer;
char backup_dir[PATH_MAX];
char backup_file[PATH_MAX];
char original_backup[PATH_MAX];
char *buffer=NULL;
char *base_name=NULL;
if ( (ret=lstat(name, &stat_buffer)) == -1) {
if (errno != ENOENT) {
DEBUG(0,("stat on %s failed: %s\n",name, strerror(errno)));
goto cleanup;
}
DEBUG(0,("stat on %s failed with ENOENT, I will create an empty backup file.\n",name, strerror(errno)));
} else {
if (!S_ISREG(stat_buffer.st_mode)) {
DEBUG(0,("%s is not a regular file!\n",name));
ret=-1;
goto cleanup;
}
}
ret=check_dir_create(BACKUP_PATH);
CHECK(ret, -1, ("Failed to create main backup directory %s.\n", BACKUP_PATH), goto cleanup);
ret=snprintf(backup_dir, PATH_MAX, "%s/%s", BACKUP_PATH, app_name);
ret=snprintf(original_backup, PATH_MAX, "%s/%s.%s",backup_dir,base_name,ORIGINAL_BACKUP_EXTENSION);
if (ret<0) {
DEBUG(0,("snprintf failed.\n"));
ret=-1;
goto cleanup;
}
ret=check_dir_create(backup_dir);
CHECK(ret, -1, ("Failed to create backup directory %s for application %s.\n", backup_dir, app_name), goto cleanup);
buffer=strdup(name);
base_name=basename(buffer);
ret=snprintf(original_backup, PATH_MAX, "%s/%s.%s",backup_dir,base_name,ORIGINAL_BACKUP_EXTENSION);
if (ret<0) {
DEBUG(0,("snprintf failed.\n"));
ret=-1;
goto cleanup;
}
if ( (ret=lstat(original_backup, &stat_buffer)) == -1) {
DEBUG(0, ("There is no original backup file, I will assume that %s is the original file.\n", name));
ret=copy_file(name, original_backup);
} else {
DEBUG(3, ("Original backup file exists, I will create an ordinary backup file\n", name));
ret=snprintf(backup_file, PATH_MAX, "%s/%s.%d",backup_dir,base_name,time(NULL));
if (ret<0) {
DEBUG(0,("snprintf failed.\n"));
ret=-1;
goto cleanup;
}
ret=copy_file(name, backup_file);
}
cleanup:
free(buffer);
return ret;
}
char *get_output_handler_parameter(xmlNode *node, const char *name, const char *default_value, const int required) {
char *value;
DEBUG(3,("Search for attribute '%s'.\n",name));
value = (char *) xmlGetProp(node, (xmlChar *) name);
if (required == 1) {
CHECK_NULL_FATAL(value, ("Cannot find required attribute '%s' for output handler.\n", name));
DEBUG(3,("Found required attribute '%s' with value '%s'.\n",name, value));
} else if (required == 0 ) {
if (value == NULL) {
DEBUG(3,("Optional attribute '%s' not found, using default '%s'.\n",name, default_value));
if (default_value != NULL ) value=strdup(default_value);
} else {
DEBUG(3,("Found optional attribute '%s' with value '%s'.\n",name, value));
}
} else {
DEBUG(0,("I am not allowed to be here, aborting ...\n"));
exit(-1);
}
return value;
}
int output_handler_file(xmlNode *node, const xmlDocPtr doc, const char *xslt_file_name) {
char *name=NULL;
char *owner=NULL;
char *group=NULL;
char *permission=NULL;
char *param_name=NULL;
char *param_value=NULL;
char *app_name=NULL;
struct stat stat_buffer;
char *dir_name=NULL;
char *tmp_file_name=NULL;
char *buffer=NULL;
int ret=0;
int fd;
xsltStylesheetPtr parsed_stylesheet = NULL;
xmlDocPtr res=NULL;
name=get_output_handler_parameter(node, "name", NULL, 1);
buffer=strdup(name);
CHECK_NULL_RETURN(buffer ,("strdup failed\n"));
dir_name=dirname(buffer);
if( (ret=stat(dir_name, &stat_buffer)) == -1) {
DEBUG(0,("stat on %s failed: %s\n",dir_name, strerror(errno)));
goto cleanup;
}
if(!S_ISDIR(stat_buffer.st_mode)) {
DEBUG(0,("%s is not a directory!\n",dir_name));
ret=-1;
goto cleanup;
}
if( (ret=lstat(name, &stat_buffer)) == -1) {
if ( errno != ENOENT ) {
DEBUG(0,("stat on %s failed: %s\n",name, strerror(errno)));
goto cleanup;
}
DEBUG(0,("stat on %s failed with ENOENT, this is ok\n",name, strerror(errno)));
} else {
if(!S_ISREG(stat_buffer.st_mode)) {
DEBUG(0,("%s is not a regular file!\n",name));
ret=-1;
goto cleanup;
}
}
owner=get_output_handler_parameter(node, "owner", "root", 0);
group=get_output_handler_parameter(node, "group", "root", 0);
permission=get_output_handler_parameter(node, "permission", "0400", 0);
param_name=get_output_handler_parameter(node, "param_name", NULL, 0);
param_value=get_output_handler_parameter(node, "param_value", NULL, 0);
/* TODO: create backup copy */
app_name = find_by_xpath(doc, "//def:metadata/def:app", FIND_VALUE);
CHECK(app_name, NULL, ("Cannot find the name of the application in policy file.\n"), goto failed);
DEBUG(3, ("Found application name: %s\n", app_name));
ret=safe_config_file(name, app_name);
CHECK(ret, -1, ("Failed to safe config file %s of application %s.\n", name, app_name), goto cleanup);
tmp_file_name=(char *) malloc(strlen(name)+8);
CHECK_NULL_RETURN(tmp_file_name,("malloc failed."));
strcpy(tmp_file_name, name);
strcat(tmp_file_name, ".XXXXXX");
fd=open_temporary_file(tmp_file_name, permission, owner, group, NULL);
CHECK(fd, -1, ("Failed to open temporary file.\n"), goto failed);
parsed_stylesheet = xsltParseStylesheetFile((xmlChar *) xslt_file_name);
CHECK_NULL_FATAL(parsed_stylesheet, ("Cannot parse stylesheet!\n"));
res = xsltApplyStylesheet(parsed_stylesheet, doc, NULL);
CHECK_NULL_FATAL(res, ("Cannot apply stylesheet!\n"));
ret = xsltSaveResultToFd(fd, res, parsed_stylesheet);
if (ret == -1) {
DEBUG(0, ("Cannot save result!\n"));
close(fd);
goto cleanup;
}
close(fd);
ret=rename(tmp_file_name, name);
CHECK(ret, -1, ("Cannot rename %s to %s: %s\n", tmp_file_name, name, strerror(errno) ), goto cleanup);
cleanup:
xmlFreeDoc(res);
xsltFreeStylesheet(parsed_stylesheet);
free(buffer);
free(tmp_file_name);
free(name);
free(owner);
free(group);
free(permission);
free(param_name);
free(param_value);
free(app_name);
return ret;
failed:
free(buffer);
free(tmp_file_name);
free(name);
free(owner);
free(group);
free(permission);
free(param_name);
free(param_value);
free(app_name);
return -1;
}
int output_handler_exec_with_args(xmlNode *node, const xmlDocPtr doc, const char *xslt_file_name) {
char *command;
char *arguments;
char *user;
char *group;
char *param_name;
char *param_value;
int ret;
struct stat stat_buffer;
xsltStylesheetPtr parsed_stylesheet = NULL;
xmlDocPtr res;
xmlChar *result_string;
int result_length;
char *cur;
char *end_of_line;
command=get_output_handler_parameter(node, "command", NULL, 1);
if( (ret=stat(command, &stat_buffer)) == -1) {
DEBUG(0,("stat on %s failed: %s\n",command, strerror(errno)));
free(command);
return -1;
}
arguments=get_output_handler_parameter(node, "arguments", NULL, 0);
user=get_output_handler_parameter(node, "user", "nobody", 0);
group=get_output_handler_parameter(node, "group", "nobody", 0);
param_name=get_output_handler_parameter(node, "param_name", NULL, 0);
param_value=get_output_handler_parameter(node, "param_value", NULL, 0);
parsed_stylesheet = xsltParseStylesheetFile((xmlChar *) xslt_file_name);
CHECK_NULL_FATAL(parsed_stylesheet, ("Cannot parse stylesheet!\n"));
res = xsltApplyStylesheet(parsed_stylesheet, doc, NULL);
CHECK_NULL_FATAL(res, ("Cannot apply stylesheet!\n"));
ret = xsltSaveResultToString(&result_string, &result_length, res, parsed_stylesheet);
if (ret == -1) {
DEBUG(0, ("Cannot save result!\n"));
exit(1);
}
xmlFreeDoc(res);
xsltFreeStylesheet(parsed_stylesheet);
xsltCleanupGlobals();
cur=(char *)result_string;
while ( (end_of_line = strchr(cur, '\n'))!=NULL ) {
*end_of_line='\0';
DEBUG(3,("found argument to %s: |%s|\n",command, cur));
ret=exec_command(command, user, group, arguments, cur);
DEBUG(3,("exec_command retrun value: %d\n",ret));
cur=end_of_line+1;
};
free(result_string);
free(command);
free(arguments);
free(user);
free(group);
free(param_name);
free(param_value);
return 0;
}
int find_output_handler(const char *policy_file_name, const char *xslt_file_name) {
int i;
xmlXPathContextPtr xpath_context=NULL;
xmlXPathObjectPtr xpath_obj=NULL;
xmlDocPtr xslt_doc=NULL;
xmlDocPtr doc=NULL;
doc = xmlParseFile(policy_file_name);
CHECK(doc, NULL, ("Cannot parse file %s!\n", policy_file_name), goto failed);
xslt_doc = xmlParseFile(xslt_file_name);
CHECK(xslt_doc, NULL, ("Cannot parse file %s!\n", xslt_file_name), goto failed);
xpath_context = xmlXPathNewContext(xslt_doc);
CHECK(xpath_context, NULL, ("Error: unable to create new XPath context\n"), goto failed);
if (xmlXPathRegisterNs(xpath_context, XSLT_METADATA_NAMESPACE_PREFIX, XSLT_METADATA_NAMESPACE) != 0) {
DEBUG(0,
("Error: unable to register NS with prefix=\"%s\" and href=\"%s\"\n",
XSLT_METADATA_NAMESPACE_PREFIX, XSLT_METADATA_NAMESPACE));
goto failed;
}
xpath_obj = xmlXPathEvalExpression(XPATH_OUTPUT_HANDLER, xpath_context);
if (xpath_obj == NULL) {
DEBUG(0,
("Error: unable to evaluate xpath expression \"%s\"\n",
XPATH_OUTPUT_HANDLER));
goto failed;
}
if (xmlXPathNodeSetIsEmpty(xpath_obj->nodesetval)) {
DEBUG(0, ("Nothing found for %s\n", XPATH_OUTPUT_HANDLER));
goto failed;
}
for (i=0; inodesetval); i++) {
DEBUG(3, ("found output_handler: %s\n",(char *) xpath_obj->nodesetval->nodeTab[i]->name));
/*print_all_attributes(xpath_obj->nodesetval->nodeTab[i]);*/
if ( xmlStrEqual(xpath_obj->nodesetval->nodeTab[i]->name, (xmlChar *) "file" )) {
output_handler_file(xpath_obj->nodesetval->nodeTab[i], doc, xslt_file_name);
} else if ( xmlStrEqual(xpath_obj->nodesetval->nodeTab[i]->name, (xmlChar *) "exec_with_args" )) {
output_handler_exec_with_args(xpath_obj->nodesetval->nodeTab[i], doc, xslt_file_name);
} else {
DEBUG(0, ("Unknow outout handler '%s'.\n", xpath_obj->nodesetval->nodeTab[i]->name));
goto failed;
}
}
xmlXPathFreeObject(xpath_obj);
xmlXPathFreeContext(xpath_context);
xmlFreeDoc(xslt_doc);
xmlFreeDoc(doc);
xmlCleanupParser();
return 0;
failed:
xmlXPathFreeObject(xpath_obj);
xmlXPathFreeContext(xpath_context);
xmlFreeDoc(xslt_doc);
xmlFreeDoc(doc);
xmlCleanupParser();
return -1;
}