#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(src_name)+7); CHECK(tmp_file_name, NULL, ("malloc failed."), return -1); strcpy(tmp_file_name, src_name); strcat(tmp_file_name, ".XXXXXX"); 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) { DEBUG(0,("stat on %s failed: %s\n",name, strerror(errno))); goto cleanup; } 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) { DEBUG(0,("stat on %s failed: %s\n",name, strerror(errno))); goto cleanup; } 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)+7); 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; }