X-Git-Url: https://git.distorted.org.uk/u/mdw/catacomb/blobdiff_plain/6f51228e8b99c2f0cbec5cb9d77925e0031eacd8..d11a0bf77a5230387d222ec727865a898767ff3e:/key-file.c diff --git a/key-file.c b/key-file.c new file mode 100644 index 0000000..ab620e7 --- /dev/null +++ b/key-file.c @@ -0,0 +1,327 @@ +/* -*-c-*- + * + * $Id: key-file.c,v 1.1 1999/12/22 15:47:48 mdw Exp $ + * + * System-dependent key filing operations + * + * (c) 1999 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb 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 Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: key-file.c,v $ + * Revision 1.1 1999/12/22 15:47:48 mdw + * Major key-management revision. + * + */ + +/*----- Header files ------------------------------------------------------*/ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "key.h" + +/*----- Main code ---------------------------------------------------------*/ + +/* --- @fdcopy@ --- * + * + * Arguments: @int source@ = source file descriptor + * @int dest@ = destination file descriptor + * + * Returns: Zero if OK, nonzero otherwise. + * + * Use: Copies data from one file descriptor to another. + */ + +static int fdcopy(int source, int dest) +{ + char buf[4096]; + + if (lseek(source, 0, SEEK_SET) < 0|| + lseek(dest, 0, SEEK_SET) < 0 || + ftruncate(dest, 0) < 0) + return (-1); + for (;;) { + int n = read(source, buf, sizeof(buf)); + if (n < 0) + return (-1); + else if (n == 0) + break; + else if (write(dest, buf, n) < 0) + return (-1); + } + return (0); +} + +/* --- @key_save@ --- * + * + * Arguments: @key_file *f@ = pointer to key file block + * + * Returns: A @KWRITE_@ code indicating how well it worked. + * + * Use: Writes a key file's data back to the actual file. This code + * is extremely careful about error handling. It should usually + * be able to back out somewhere sensible, but it can tell when + * it's got itself into a real pickle and starts leaving well + * alone. + * + * Callers, please make sure that you ring alarm bells when this + * function returns @KWRITE_BROKEN@. + */ + +int key_save(key_file *f) +{ + dstr n_older = DSTR_INIT, n_old = DSTR_INIT, n_new = DSTR_INIT; + int rc = KWRITE_FAIL; + + if (!(f->f & KF_MODIFIED)) + return (KWRITE_OK); + + /* --- Write a new key file out --- * + * + * Check for an error after each key line. This ought to be enough. + * Checking after each individual byte write and @fprintf@ isn't much fun. + */ + + dstr_putf(&n_new, "%s.new", f->name); + + { + key *k; + key_iter i; + FILE *fp; + + if ((fp = fopen(n_new.buf, "w")) == 0) + goto fail_open; + + for (key_mkiter(&i, f); (k = key_next(&i)) != 0; ) { + if (key_extract(f, k, fp, 0)) { + fclose(fp); + goto fail_write; + } + } + + if (fclose(fp)) + goto fail_write; + } + + /* --- Set up the other filenames --- */ + + dstr_putf(&n_older, "%s.older", f->name); + dstr_putf(&n_old, "%s.old", f->name); + + /* --- Move the current backup on one --- * + * + * If the `older' file exists, then we're in need of attention. + */ + + { + struct stat st; + if (stat(n_older.buf, &st) == 0 || errno != ENOENT) { + errno = EEXIST; + rc = KWRITE_BROKEN; + goto fail_shift; + } + if (rename(n_old.buf, n_older.buf) && errno != ENOENT) + goto fail_shift; + } + + /* --- Copy the current file to the backup --- */ + + { + int fd; + if ((fd = open(n_old.buf, O_WRONLY | O_CREAT | O_EXCL, 0600)) < 0) + goto fail_backup; + if (fdcopy(fileno(f->fp), fd)) { + close(fd); + goto fail_backup; + } + if (close(fd)) + goto fail_backup; + } + + /* --- Copy the newly created file to the current one --- * + * + * This is the dangerous bit. + */ + + { + int fd; + if ((fd = open(n_new.buf, O_RDONLY)) < 0) + goto fail_update; + if (fdcopy(fd, fileno(f->fp))) { + close(fd); + goto fail_update; + } + close(fd); + if (fsync(fileno(f->fp))) + goto fail_update; + } + + /* --- Clean up --- * + * + * Remove the `new' file and the `older' backup. Then we're done. + */ + + unlink(n_new.buf); + unlink(n_older.buf); + return (KWRITE_OK); + + /* --- Failure while writing the new key file --- * + * + * I need to copy the backup back. If that fails then I'm really stuffed. + * If not, then I might as well try to get the backups sorted back out + * again. + */ + +fail_update: + { + int fd; + int e = errno; + + if ((fd = open(n_old.buf, O_RDONLY)) < 0) + rc = KWRITE_BROKEN; + else if (fdcopy(fd, fileno(f->fp))) { + close(fd); + rc = KWRITE_BROKEN; + } else { + close(fd); + if (fsync(fileno(f->fp))) + rc = KWRITE_BROKEN; + } + + errno = e; + if (rc == KWRITE_BROKEN) + goto fail_shift; + } + /* Now drop through */ + + /* --- Failure while writing the new backup --- * + * + * The new backup isn't any use. Try to recover the old one. + */ + +fail_backup: + { + int e = errno; + unlink(n_old.buf); + if (rename(n_older.buf, n_old.buf) && errno != ENOENT) + rc = KWRITE_BROKEN; + errno = e; + } + /* Now drop through */ + + /* --- Failure while demoting the current backup --- * + * + * Leave the completed output file there for the operator in case he wants + * to clean up. + */ + +fail_shift: + dstr_destroy(&n_new); + dstr_destroy(&n_old); + dstr_destroy(&n_older); + return (rc); + +/* --- Failure during write of new data --- * + * + * Clean up the new file and return. These errors can never cause + * breakage. + */ + +fail_write: + unlink(n_new.buf); + fail_open: + dstr_destroy(&n_new); + return (rc); +} + +/* --- @key_lockfile@ --- * + * + * Arguments: @key_file *f@ = pointer to file structure to initialize + * @const char *file@ = pointer to the file name + * @int how@ = opening options (@KOPEN_*@). + * + * Returns: Zero if it worked, nonzero otherwise. + * + * Use: Opens a keyfile and stores the information needed for + * continued access in the structure. + * + * If the file is opened with @KOPEN_WRITE@, it's created if + * necessary with read and write permissions for owner only, and + * locked for update while it's open. + * + * This is a system-dependent routine, and only really intended + * for the private use of @key_open@. + */ + +int key_lockfile(key_file *f, const char *file, int how) +{ + int of, lf; + const char *ff; + int fd; + + /* --- Lots of things depend on whether we're writing --- */ + + switch (how) { + case KOPEN_READ: + of = O_RDONLY; + lf = LOCK_NONEXCL; + ff = "r"; + break; + case KOPEN_WRITE: + of = O_RDWR | O_CREAT; + lf = LOCK_EXCL; + ff = "r+"; + break; + default: + errno = EINVAL; + return (-1); + } + + /* --- Open and lock the file --- */ + + if ((fd = open(file, of, 0600)) < 0) + return (-1); + if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0 || + lock_file(fd, lf) < 0 || + (f->fp = fdopen(fd, ff)) == 0) { + close(fd); + return (-1); + } + + return (0); +} + +/*----- That's all, folks -------------------------------------------------*/