diff options
Diffstat (limited to 'key2root-rmkey.c')
-rw-r--r-- | key2root-rmkey.c | 188 |
1 files changed, 184 insertions, 4 deletions
diff --git a/key2root-rmkey.c b/key2root-rmkey.c index f2ab34d..d3dbc16 100644 --- a/key2root-rmkey.c +++ b/key2root-rmkey.c @@ -1,6 +1,10 @@ /* See LICENSE file for copyright and license details. */ +#include <errno.h> +#include <fcntl.h> #include <stdio.h> #include <stdlib.h> +#include <string.h> +#include <unistd.h> #include "arg.h" @@ -16,11 +20,118 @@ usage(void) } +static int +writeall(int fd, const char *data, size_t len) +{ + size_t off = 0; + ssize_t r; + + for (;;) { + r = write(fd, &data[off], len - off); + if (r < 0) + return -1; + off += (size_t)r; + } + + return 0; +} + + +static void +removekeys(char *data, size_t *wheadp, size_t *rheadp, size_t *rhead2p, size_t *linenop, + const char *path, const char **keys, size_t *nkeysp) +{ + int failed = 0; + size_t len, klen; + size_t i; + + while (*rhead2p < *wheadp || data[*rhead2p] != '\n') + ++*rhead2p; + + if (data[*rhead2p] != '\n') + return; + + len = *rhead2p - *rheadp; + *linenop += 1; + + if (memchr(&data[*rheadp], '\0', len)) { + fprintf(stderr, "%s: NUL byte found in %s on line %zu\n", argv0, path, *linenop); + failed = 1; + } + if (!memchr(&data[*rheadp], ' ', len)) { + fprintf(stderr, "%s: no SP byte found in %s on line %zu\n", argv0, path, *linenop); + failed = 1; + } + + if (failed) { + goto no_match; + } else { + for (i = 0; i < *nkeysp; i++) { + klen = strlen(keys[i]); + if (klen >= len || data[*rheadp + klen] != ' ' || memcpy(&data[*rheadp], keys[i], klen)) + continue; + memmove(&keys[i], &keys[i + 1], (--*nkeysp - i) * sizeof(*keys)); + goto match; + } + no_match: + *rheadp = ++*rhead2p; + return; + match: + ++*rhead2p; + memmove(&data[*rheadp], &data[*rhead2p], *wheadp - *rhead2p); + *wheadp -= *rhead2p - *rheadp; + *rhead2p = *rheadp; + } +} + + +static void +loadandremove(int fd, char **datap, size_t *lenp, size_t *sizep, const char **keys, size_t *nkeysp, const char *path) +{ + char *new; + size_t lineno = 0; + ssize_t r = 1; + size_t rhead = 0; + size_t rhead2 = 0; + + while (r) { + if (*lenp == *sizep) { + new = realloc(*datap, *sizep += 4096); + if (!new) { + fprintf(stderr, "%s: realloc: %s\n", argv0, strerror(errno)); + exit(1); + } + *datap = new; + } + + r = read(fd, &(*datap)[*lenp], *sizep - *lenp); + if (r < 0) { + fprintf(stderr, "%s: read %s: %s\n", argv0, path, strerror(errno)); + exit(1); + } + *lenp += (size_t)r; + + while (rhead2 < *lenp) + removekeys(*datap, lenp, &rhead, &rhead2, &lineno, path, keys, nkeysp); + } + + if (rhead != *lenp) + fprintf(stderr, "%s: file truncated: %s\n", argv0, path); +} + + int main(int argc, char *argv[]) { + char *path, *path2; const char *user; - int i, failed = 0; + int failed = 0; + const char **keys; + size_t i, nkeys; + int fd; + char *data = NULL; + size_t data_len = 0; + size_t data_size = 0; ARGBEGIN { default: @@ -37,7 +148,7 @@ main(int argc, char *argv[]) fprintf(stderr, "%s: bad user name specified: %s\n", argv0, user); failed = 1; } - for (i = 0; i < argc; i++) { + for (i = 0; i < (size_t)argc; i++) { if (argv[i][strcspn(argv[i], " \t\f\n\r\v")]) { fprintf(stderr, "%s: bad key name specified: %s, includes whitespace\n", argv0, argv[i]); failed = 1; @@ -46,7 +157,76 @@ main(int argc, char *argv[]) if (failed) return 1; - /* TODO */ + nkeys = (size_t)argc; + keys = calloc(nkeys, sizeof(*keys)); + if (!keys) { + fprintf(stderr, "%s: calloc: %s\n", argv0, strerror(errno)); + exit(1); + } + for (i = 0; i < (size_t)argc; i++) + keys[i] = argv[i]; - return 0; + path = malloc(sizeof("/etc/key2root/") + strlen(user)); + if (!path) { + fprintf(stderr, "%s: malloc: %s\n", argv0, strerror(errno)); + exit(1); + } + path2 = malloc(sizeof("/etc/key2root/~") + strlen(user)); + if (!path) { + fprintf(stderr, "%s: malloc: %s\n", argv0, strerror(errno)); + exit(1); + } + stpcpy(stpcpy(path, "/etc/key2root/"), user); + stpcpy(stpcpy(path2, path), "~"); + + fd = open(path, O_RDONLY); + if (fd < 0) { + if (errno == ENOENT) + goto out; + fprintf(stderr, "%s: open %s O_RDONLY: %s\n", argv0, path, strerror(errno)); + exit(1); + } + loadandremove(fd, &data, &data_len, &data_size, keys, &nkeys, path); + if (close(fd)) { + fprintf(stderr, "%s: read %s: %s\n", argv0, path, strerror(errno)); + exit(1); + } + +out: + for (i = 0; i < nkeys; i++) + fprintf(stderr, "%s: key not found for %s: %s\n", argv0, user, keys[i]); + failed |= !nkeys; + + if (nkeys != (size_t)argc) { + if (!data_len) { + if (unlink(path)) { + fprintf(stderr, "%s: unlink %s: %s\n", argv0, path, strerror(errno)); + failed = 1; + } + } else { + fd = open(path2, O_WRONLY | O_CREAT | O_EXCL, 0600); + if (fd < 0) { + fprintf(stderr, "%s: open %s O_WRONLY|O_CREAT|O_EXCL 0600: %s\n", argv0, path2, strerror(errno)); + exit(1); + } + if (writeall(fd, data, data_len) || close(fd)) { + fprintf(stderr, "%s: write %s: %s\n", argv0, path2, strerror(errno)); + if (unlink(path2)) + fprintf(stderr, "%s: unlink %s: %s\n", argv0, path2, strerror(errno)); + exit(1); + } + if (rename(path2, path)) { + fprintf(stderr, "%s: rename %s %s: %s\n", argv0, path2, path, strerror(errno)); + if (unlink(path2)) + fprintf(stderr, "%s: unlink %s: %s\n", argv0, path2, strerror(errno)); + exit(1); + } + } + } + + free(keys); + free(path); + free(path2); + free(data); + return failed; } |