/* $CoreSDI: attr_peo.c,v 1.12 2001/11/16 22:52:59 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. */ /* * Peo attribute module. * Author: Claudio Castiglia */ #include #include #include #ifdef __linux__ #include /* in_addr_t */ #endif #include #include #include #include #include #include #include #include #include #include "sysdep.h" #include "resource.h" #include "packet.h" #include "modtypes.h" #include "iaargs.h" #include "log.h" #define ATTR_PEO "attr peo: " #define DEFAULT_KEYFILE "/var/ssyslog/.var.log.messages.key" #define DEFAULT_HASH "sha1" #define OFFSET_PREFIX "PeoOffset_" /* offset resource prefix */ typedef struct _peo_ctx { char hash[10]; /* hash method */ char keyfile[MAXPATHLEN]; /* keyfile */ char key[20]; /* last peo key */ ssize_t keysize; /* peo key size */ char macfile[MAXPATHLEN]; /* macfile */ struct stat st; /* macfile stat */ RESOURCE *roffset; /* offset resource */ off_t offset; /* macfile offset */ off_t pos; /* internal variable */ off_t prev_size; /* internal variable */ int fd; /* internal variable */ } PEO_CTX; #define PEO_l(x) (x->macfile[0] != '\0') #define GET_PEO_CTX(c) (PEO_CTX *) (c + 1) /* * _load_offset: * Search offset resource data and update offset context member. */ static void _load_offset(ATTRCON *context) { char rname[MAXPATHLEN]; PEO_CTX *pc; pc = GET_PEO_CTX(context); if (pc->offset >= 0) return; snprintf(rname, sizeof(rname), OFFSET_PREFIX "%s", pc->macfile); /* Get peo-l offset resource */ if (context->s->s_rlist != NULL) pc->roffset = res_find(context->s->s_rlist, rname); /* Convert resource to off_t */ if (pc->roffset == NULL) pc->offset = 0; else { /* Prevent coredump */ if (pc->roffset->dsize != sizeof(pc->offset)) log_warn(ATTR_PEO "Invalid resource size: %s. " "Ignoring.", rname); } } /* * _save_offset: * Save current offset into the resource list. * Return 0 on success and -1 on error and set errno. */ static int _save_offset(ATTRCON *context) { char rname[MAXPATHLEN]; int error; PEO_CTX *pc; pc = GET_PEO_CTX(context); if (!PEO_l(pc) || pc->offset < 0) return (0); error = 0; if (context->s->s_rlist != NULL) { /* If resource does not exists create one */ if (pc->roffset == NULL) { snprintf(rname, sizeof(rname), OFFSET_PREFIX "%s", pc->macfile); error = res_add(context->s->s_rlist, rname, &pc->offset, sizeof(pc->offset)); } else /* If resource exists replace current data */ error = res_replace(context->s->s_rlist, pc->roffset, &pc->offset, sizeof(pc->offset)); } return (error); } /* * _copy(): * Copy data from one fd to another or same fd. */ static int _copy(int dstfd, int srcfd, off_t dstoff, off_t srcoff, off_t size) { char buf[BUFSIZ]; ssize_t to_read, to_write; if (lseek(dstfd, dstoff, SEEK_SET) < 0 || lseek(srcfd, srcoff, SEEK_SET) < 0) return (-1); while (size) { to_read = (size > sizeof(buf)) ? sizeof(buf) : size; to_write = read(srcfd, buf, to_read); if (to_write < 0) return (-1); if (dstfd == srcfd) { if (lseek(dstfd, dstoff, SEEK_SET) < 0) return (-1); srcoff += to_write; } if (write(dstfd, buf, to_write) < 0) return (-1); if (dstfd == srcfd) { if (lseek(srcfd, srcoff, SEEK_SET) < 0) return (-1); dstoff += to_write; } size -= to_write; } return (0); } /* * _get_logset_name(): * Return logset name. */ static void _get_logset_name(ATTRCON *context, struct attrargs_ret *args) { /* Peo attribute doesnt't know about logset name */ args->size = 0; } /* * _freeze(): * Freeze peo status; return 0 on success or -1 on error * and set errno. */ static int _freeze(ATTRCON *context) { PEO_CTX *pc; int fd; pc = GET_PEO_CTX(context); if ( (fd = open(pc->keyfile, O_RDONLY, 0)) < 0) { log_err(ATTR_PEO "Can't open '%s': %s.", pc->keyfile, strerror(errno)); return (-1); } pc->keysize = read(fd, pc->key, sizeof(pc->key)); close(fd); if (pc->keysize < 0) { log_err(ATTR_PEO "Can't read '%s': %s.", pc->keyfile, strerror(errno)); return (-1); } if (PEO_l(pc)) if (stat(pc->macfile, &pc->st) < 0) { log_err(ATTR_PEO "Can't stat '%s': %s.", pc->macfile, strerror(errno)); return (-1); } return (0); } /* * _info(): * Return peo information coded as a string: * "peo * hash * keyfile * [ macfile * offset ]" * XXX: add file perms */ static void _info(ATTRCON *context, struct attrargs_ret *args) { PEO_CTX *pc; size_t len; pc = GET_PEO_CTX(context); len = snprintf(args->data, args->size, "peo\n\thash\t%s\n\tkeyfile\t%s\n", pc->hash, pc->keyfile); /* If using PEO-l load offset and add more info */ if (PEO_l(pc)) { _load_offset(context); snprintf(args->data + len, args->size - len, "\tmacfile\t%s\n\toffset\t%li\n", pc->macfile, (unsigned long int) pc->offset); } } /* * _get(): * Send keyfile and macfile; * return 0 on success or -1 on error and set errno. */ static int _get(ATTRCON *context) { PEO_CTX *pc; int fd; pc = GET_PEO_CTX(context); log_debug(ATTR_PEO "Getting %s.", pc->keyfile); /* Send freezed key */ packet_put_string(context->s->s_packet, pc->keyfile); packet_put_int32(context->s->s_packet, sizeof(pc->key)); packet_put_raw(context->s->s_packet, pc->key, sizeof(pc->key)); /* Send macfile */ if (PEO_l(pc)) { if ( (fd = open(pc->macfile, O_RDONLY, 0)) < 0) { log_err(ATTR_PEO "Can't open '%s': %s.", pc->macfile, strerror(errno)); return (-1); } packet_put_string(context->s->s_packet, pc->macfile); /* * If size < offset something wrong occurred, * autidor would detect it. */ _load_offset(context); if (pc->st.st_size < pc->offset) pc->offset = 0; /* Send macfile from current offset */ pc->offset = packet_write(context->s->s_packet, fd, pc->offset); close(fd); } return (0); } /* * _zap(): * Zap macfile and reset peo offset. */ static int _zap(ATTRCON *context, int last_call) { PEO_CTX *pc; off_t len; pc = GET_PEO_CTX(context); log_debug(ATTR_PEO "Zapping %s, pass %d.", pc->keyfile, last_call); /* First time ? */ if (pc->prev_size < 0) { /* Return 0 if user can't truncate or trucation not needed */ if (!PEO_l(pc)) return (0); _load_offset(context); if (pc->offset == 0) return (0); /* Open macfile */ if ( (pc->fd = open(pc->macfile, O_RDWR, 0)) < 0) { log_err(ATTR_PEO "Can't open '%s': %s.", pc->macfile, strerror(errno)); return (-1); } /* Actualize internal variables */ pc->prev_size = pc->offset; pc->offset = pc->pos = 0; } if (!PEO_l(pc)); /* Move new unreadaed data to the beginning */ len = pc->st.st_size - pc->prev_size; if (len >= 0) { if (_copy(pc->fd, pc->fd, pc->pos, pc->prev_size, len) < 0) { log_err(ATTR_PEO "Can't zap '%s': %s.", pc->macfile, strerror(errno)); pc->prev_size = -1; close(pc->fd); return (-1); } pc->pos += len; if (last_call) { pc->prev_size = -1; close(pc->fd); if (truncate(pc->macfile, pc->pos) < 0) { log_err(ATTR_PEO "Can't truncate '%s': %s.", pc->macfile, strerror(errno)); return (-1); } } pc->prev_size = pc->st.st_size; } /* Call me again */ return (1); //XXX } /* * _rotate(): * Rotate keyfile. */ static int _rotate(ATTRCON *context, int last_call) { PEO_CTX *pc; pc = GET_PEO_CTX(context); log_debug(ATTR_PEO "Rotating %s: pass %d, calling zap.", pc->keyfile, last_call); return (_zap(context, last_call)); } /* * _sign(): * Sign logfile. */ static int _sign(ATTRCON *context) { log_err(ATTR_PEO "SIGN: %s.", strerror(EOPNOTSUPP)); return (-1); } /* * release(): * Release peo context (ATTRCON members are not released). */ void release(ATTRCON *context) { PEO_CTX *pc; if (context != NULL) { pc = GET_PEO_CTX(context); bzero(pc->key, sizeof(pc->key)); if (_save_offset(context) < 0) log_err(ATTR_PEO "Can't release context: %s.", strerror(errno)); free(context); } } /* * init(): * Initialize peo module; * on success 0 is returned and an internal context is created; * on error -1 is returned and errno set. */ ATTRCON * init(struct attrargs_init *args) { static const char *module_name = "peo"; ATTRCON *context; PEO_CTX *pc; int i, err, peo_l; log_debug(ATTR_PEO "Initializing."); if (args->argc < 2) { errno = EINVAL; log_err(ATTR_PEO "Can't initialize %s.", strerror(errno)); return (NULL); } context = (ATTRCON *) calloc(1, sizeof(ATTRCON) + sizeof(PEO_CTX)); if (context == NULL) { log_err(ATTR_PEO "Can't create context: %s.", strerror(errno)); return (NULL); } pc = GET_PEO_CTX(context); err = 0; peo_l = 0; for (i = 1; i < args->argc; i++) { if (!strcmp(args->argv[i], "-k")) { /* keyfile */ if (++i == args->argc) { errno = EINVAL; err = 1; break; } strlcpy(pc->keyfile, args->argv[i], sizeof(pc->keyfile)); } else if (!strcmp(args->argv[i], "-l")) { peo_l = 1; /* PEO-l */ } else if (!strcmp(args->argv[i], "-m")) { if (++i == args->argc) { /* hash method */ errno = EINVAL; err = 1; break; } strlcpy(pc->hash, args->argv[i], sizeof(pc->hash)); } } if (!err && pc->keyfile[0] == '\0') strlcpy(pc->keyfile, DEFAULT_KEYFILE, sizeof(pc->keyfile)); if (!err && pc->hash[0] == '\0') strlcpy(pc->hash, DEFAULT_HASH, sizeof(pc->hash)); if (err) { log_err(ATTR_PEO "Can't initialize: %s.", strerror(errno)); release(context); return (NULL); } if (peo_l) snprintf(pc->macfile, sizeof(pc->macfile), "%s.mac", pc->keyfile); pc->offset = -1; pc->prev_size = -1; context->attr_name = module_name; return (context); } /* * proc_entry() */ int proc_entry(int opcode, ATTRCON *context, void *args) { switch(opcode) { case ATTR_GET_LOGSET_NAME: _get_logset_name(context, args); return (0); case ATTR_FREEZE: return (_freeze(context)); case ATTR_INFO: _info(context, args); return (0); case ATTR_GET: return (_get(context)); case ATTR_ZAP: return (_zap(context, (int) args)); case ATTR_ROTATE: return (_rotate(context, (int) args)); case ATTR_SIGN: return (_sign(context)); default: errno = EINVAL; } log_err(ATTR_PEO "Invalid %d command.", opcode); return (-1); }