/* $CoreSDI: res_local.c,v 1.20 2001/12/08 00:12:30 claudio Exp $ */ /* * Copyright (c) 2000, 2001, Core SDI S.A., Argentina * All rights reserved * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither name of the Core SDI S.A. nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * Local Resources Module * Author: Claudio Castiglia */ #include #include #include #include #include #include #include #include #include #include #include #include "log.h" #include "resource.h" #define LOCAL_RM "resmod local: " #ifndef LOCAL_RESOURCES_UMASK #define LOCAL_RESOURCES_UMASK (S_IRWXG | S_IRWXO | S_IXUSR) #endif #ifndef LOCAL_RESOURCES_PATH #define LOCAL_RESOURCES_PATH "/var/audit/resources" #endif #define WHITE_SPACE " \f\n\r\t\v" #define NAME_BEGIN '[' #define NAME_END ']' #define INCLUDE_COMMAND "include" #define INCLUDE_COMMAND_LEN 7 #define MAX_LRM_CODES 20 /* Max. codes on a resource line */ /* * _decode(): * Decode (binarize) resource line; * return no. of data bytes on the decoded line or -1 on error. */ static ssize_t _decode(char *line) { char asc[3], *pd; ssize_t datasize; for (datasize = 0, pd = line; *line != '\0' && *line != '\n' && *line !='\r'; line++, pd++, datasize++) if (*line == '\\') switch (*(++line)) { case 'n': *pd = '\n'; break; case 't': *pd = '\t'; break; case '[': case '\\': *pd = *line; break; default: /* Valid only two hex digits */ if (isxdigit(*line) && isxdigit(*(line+1))) { asc[0] = *line; asc[1] = *(++line); asc[2] = '\0'; *pd = (char) strtoul(asc, NULL, 16); break; } errno = EINVAL; return (-1); } else *pd = *line; *pd = '\0'; return (datasize); } /* * _encode(): * Encode binary data to ascii (lrm file convention, see NOTE 2 below) * and return a pointer to a encoded string. * On error NULL is returned. * NOTE 1: After use, the buffer must be deallocated using free(3). * NOTE 2: Local Resource Module (lrm) convention: * Every character between 0x20 and 0x7E are printed as is; * others are encoded as "\xx" string, where xx is the * character hex code represeted in ascii. */ static char *hexset = "0123456789ABCDEF"; #define LOWER_PRINTABLE 0x20 #define UPPER_PRINTABLE 0x7E #define UPPER_DIGIT(a) *(hexset + (a >> 4)) #define LOWER_DIGIT(a) *(hexset + (a & 0x0F)) static char * _encode(const unsigned char *data, ssize_t size) { char *encoded; int e, i; encoded = (char *) malloc(size * 3 + 1); /* byte ab -> ascii "\ab" */ if (encoded != NULL) { for (e = i = 0; i < size; i++, e++) if (data[i] < LOWER_PRINTABLE || data[i] > UPPER_PRINTABLE || data[i] == '[' || data[i] == '\\') { encoded[e++] = '\\'; encoded[e++] = UPPER_DIGIT(data[i]); encoded[e] = LOWER_DIGIT(data[i]); } else encoded[e] = data[i]; encoded[e] = '\0'; } return (encoded); } /* * _add_resource(): * Add a new resource into the resource list; * return 0 on success and -1 on error. * NOTE: The *name value is free(3)d and reset to NULL; * *size is reset to 0. */ static int _add_resource(RESLIST *list, char **name, const void *data, ssize_t *size) { int error; error = 0; if (*name != NULL && data != NULL && *size > 0) { log_debug(LOCAL_RM "Creating '%s'.", *name); error = res_add(list, *name, data, *size); free(*name); *name = NULL; *size = 0; } return (error); } /* * _get_include_name(): * Get an include file name from include command string and * return a pointer to it. * Return NULL if the string does not contain an include command. */ static char * _get_include_name(const char *command) { char *file; if (!strncmp(command, INCLUDE_COMMAND, INCLUDE_COMMAND_LEN)) { file = (char *) command + INCLUDE_COMMAND_LEN; if (isspace(*file++)) { while (*file != '\0' && isspace(*file)) file++; if (*file != '\0') return (file); } } return (NULL); } /* * _save_resource(): * Add a resource into the specified resource file. * Return 0 on success or -1 on error. */ static int _save_resource(FILE *f, RESOURCE *r) { char *encoded, line[LINE_MAX]; ssize_t count, i, j; if (r->name == NULL) return (-1); if (*r->name == '\0') return (-1); /* Write resource name */ if ( (encoded = _encode(r->name, strlen(r->name))) == NULL) return (-1); i = snprintf(line, sizeof(line), "%c %s %c\n", NAME_BEGIN, encoded, NAME_END); free(encoded); if (fwrite(line, 1, i, f) != i) return (-1); /* Write resource data */ if (r->data != NULL && r->dsize > 0) { if ( (encoded = _encode(r->data, r->dsize)) == NULL) return (-1); /* Print MAX_LRM_CODEs per line */ j = 0; line[0] = '\t'; while (encoded[j] != '\0') { for (i = 0, count = 0; count < MAX_LRM_CODES; count++, i++) { if (encoded[j + i] == '\0') break; if (encoded[j + i] == '\\') { line[i + 1] = encoded[j + i++]; line[i + 1] = encoded[j + i++]; } line[i + 1] = encoded[j + i]; } if (count) { line[i + 1] = '\n'; if (fwrite(line, 1, i + 2, f) != i + 2) { free(encoded); return (-1); } } j += i; } free(encoded); } return (0); } /* * check(): * Return true if a given resource file exists. */ int check(const char *name) { char pathname[MAXPATHLEN]; log_debug(LOCAL_RM "Checking '%s'.", name); snprintf(pathname, sizeof(pathname), "%s/%s", LOCAL_RESOURCES_PATH, name); return (access(pathname, F_OK) == 0); } /* * load(): * Parse a resource file specified on list->lname member and put * data into the list. Include files are detected and parsed. * Return 0 on success and -1 on error. * NOTE: Empty resources are not included into the list and it is * not considered an error. */ int load(RESLIST *list) { int line, status; FILE *f; char buf[LINE_MAX], pathname[MAXPATHLEN], *p, *rn, *rname, *iname; void *rdata; ssize_t rsize, bufsize, nlsize; log_debug(LOCAL_RM "Loading '%s'.", list->lname); snprintf(pathname, sizeof(pathname), "%s/%s", LOCAL_RESOURCES_PATH, list->lname); if ( (f = fopen(pathname, "r+")) == NULL) return (-1); line = 0; rname = NULL; rdata = NULL; rsize = 0; bufsize = 0; status = -1; while (1) { if (fgets(buf, sizeof(buf), f) == NULL) { status = 0; break; } line++; /* Remove beginning white spaces */ p = buf + strspn(buf, WHITE_SPACE); if (*p == '\0' || *p == '\n') continue; /* Detect resource name: [ name ] */ if (*p == NAME_BEGIN) { /* Add previously detected resource into the list */ if (_add_resource(list, &rname, rdata, &rsize) < 0) break; /* Get new resource name */ p++; rn = p + strspn(p, WHITE_SPACE); if ( (p = strrchr(p, NAME_END)) == NULL) break; do p--; while (isspace(*p) && p > rn); if (p <= rn) break; *(p + 1) = '\0'; if (_decode(rn) < 0) break; if ( (rname = strdup(rn)) == NULL) break; /* Detect include command */ if ( (iname = _get_include_name(rname)) != NULL) { list->lnext = res_open_list(iname, 0); if (list->lnext == NULL) break; if (load(list->lnext) < 0) break; free(rname); rname = NULL; continue; } continue; } /* Get resource data */ if (rname == NULL) { errno = EINVAL; break; } if ( (nlsize = _decode(p)) < 0) break; if (bufsize < nlsize + rsize) { void *np; while (bufsize < nlsize + rsize) bufsize += LINE_MAX; if ( (np = realloc(rdata, bufsize)) == NULL) break; rdata = np; } memcpy((char *) rdata + rsize, p, nlsize); rsize += nlsize; } if (ferror(f)) status = -1; else if (rname != NULL && rdata != NULL && rsize > 0) status = _add_resource(list, &rname, rdata, &rsize); fclose(f); if (rname != NULL) free(rname); if (rdata != NULL) free(rdata); return (status); } /* * save(): * Save the specified resources file list. * Return 0 on success and -1 on error. * XXX: do a backup before replacing an existing file, * then remove the backup if no errors. */ int save(const RESLIST *list) { RESOURCE *r; FILE *f; char pathname[MAXPATHLEN]; log_debug(LOCAL_RM "Saving '%s'.", list->lname); if (list == NULL) { errno = EINVAL; return (-1); } /* XXX save only if modified flag is on */ for (; list != NULL; list = list->lnext) { snprintf(pathname, sizeof(pathname), "%s/%s", LOCAL_RESOURCES_PATH, list->lname); umask(LOCAL_RESOURCES_UMASK); if ( (f = fopen(pathname, "w")) == NULL) return (-1); /* Include file */ if (list->lnext != NULL) if (list->lnext->lname[0] != '\0') { fprintf(f, "%c %s %s %c\n", NAME_BEGIN, INCLUDE_COMMAND, list->lnext->lname, NAME_END); if (save(list->lnext) < 0) { fclose(f); return (-1); } } for (r = list->first_res; r != NULL; r = r->next) if (_save_resource(f, r) < 0) break; fclose(f); if (r != NULL) /* _save_resource() error */ return (-1); } return (0); } /* * remove(): * Remove the specified resource list. * Return 0 on success and -1 on error. */ int remove(const char *lname) { char pathname[MAXPATHLEN]; log_debug(LOCAL_RM "Removing '%s'.", lname); if (lname == NULL) { errno = EINVAL; return (-1); } snprintf(pathname, sizeof(pathname), "%s/%s", LOCAL_RESOURCES_PATH, lname); return ((access(pathname, F_OK) < 0) ? -1 : unlink(pathname)); }