diff options
-rw-r--r-- | client/file.c | 26 | ||||
-rw-r--r-- | client/keytab.c | 179 | ||||
-rw-r--r-- | client/wallet.c | 6 |
3 files changed, 209 insertions, 2 deletions
diff --git a/client/file.c b/client/file.c index 66d5f63..f24d3ca 100644 --- a/client/file.c +++ b/client/file.c @@ -46,6 +46,32 @@ overwrite_file(const char *name, const void *data, size_t length) sysdie("close of %s failed (file probably truncated)", name); } +/* + * Given a filename, some data, and a length, write that data to the given + * file safely, but overwrite any existing file by that name. + */ +void +append_file(const char *name, const void *data, size_t length) +{ + int fd; + ssize_t status; + + if (access(name, F_OK) == 0) + if (unlink(name) < 0) + sysdie("unable to delete existing file %s", name); + fd = open(name, O_WRONLY | O_APPEND); + if (fd < 0) + sysdie("open of %s failed", name); + if (length > 0) { + status = write(fd, data, length); + if (status < 0) + sysdie("write to %s failed", name); + else if (status != (ssize_t) length) + die("write to %s truncated", name); + } + if (close(fd) < 0) + sysdie("close of %s failed (file probably truncated)", name); +} /* * Given a filename, some data, and a length, write that data to the given diff --git a/client/keytab.c b/client/keytab.c index 5f2076f..d81079a 100644 --- a/client/keytab.c +++ b/client/keytab.c @@ -17,7 +17,85 @@ #include <util/concat.h> #include <util/messages-krb5.h> #include <util/messages.h> +#include <util/xmalloc.h> +/* List of principals we have already encountered. */ +struct principal_name { + char *princ; + struct principal_name* next; +}; + +/* + * Given a context, a keytab file, and a realm, return a list of all + * principals in that file. + */ +struct principal_name +keytab_principals(krb5_context ctx, const char *file, char *realm) +{ + char *princname = NULL, *princrealm = NULL; + bool found; + krb5_keytab keytab = NULL; + krb5_kt_cursor cursor; + krb5_keytab_entry entry; + krb5_error_code status; + struct principal_name *names_seen = NULL, *current_seen = NULL; + + memset(&entry, 0, sizeof(entry)); + status = krb5_kt_resolve(ctx, file, &keytab); + if (status != 0) + die_krb5(ctx, status, "cannot open keytab %s", file); + status = krb5_kt_start_seq_get(ctx, keytab, &cursor); + if (status != 0) + die_krb5(ctx, status, "cannot read keytab %s", file); + while ((status = krb5_kt_next_entry(ctx, keytab, &entry, &cursor)) == 0) { + status = krb5_unparse_name(ctx, entry.principal, &princname); + if (status != 0) + sysdie("error, cannot unparse name for a principal"); + + found = false; + current_seen = names_seen; + while (current_seen != NULL) { + if (strcmp(current_seen->princ, princname)) { + found = true; + break; + } + current_seen = current_seen->next; + } + + /* Add any new principals in the correct realm to the list. */ + if (found == false) { + princrealm = strchr(princname, '@'); + if (princrealm != NULL) { + *princrealm = '\0'; + princrealm++; + } + if (princrealm != NULL && strcmp(princrealm, realm) == 0) { + current_seen = xmalloc(sizeof(struct principal_name)); + current_seen->princ = xstrdup(princname); + current_seen->next = names_seen; + names_seen = current_seen; + } + } + + krb5_kt_free_entry(ctx, &entry); + free(princname); + } + + if (status != KRB5_KT_END) + die_krb5(ctx, status, "error reading keytab %s", file); + krb5_kt_end_seq_get(ctx, keytab, &cursor); + krb5_kt_close(ctx, keytab); + + /* TODO: Testing the principals correctly made, remove after. */ + warn("Exiting keytab_principals"); + current_seen = names_seen; + while (current_seen != NULL) { + warn("found principal %s", current_seen->princ); + current_seen = current_seen->next; + } + + return *names_seen; +} /* * Given keytab data as a pointer to memory and a length and the path of a @@ -61,11 +139,36 @@ merge_keytab(krb5_context ctx, const char *newfile, const char *file) krb5_kt_close(ctx, temp); } +/* + * Given a remctl object, the type and name of a keytab object, and + * references to keytab data and data length, call the correct wallet + * commands to download a keytab and return the keytab data. Returns the + * status of the remctl command. + */ +int +download_keytab(struct remctl *r, const char *type, const char *name, + char **data, size_t *length) +{ + const char *command[5]; + int status; + + command[0] = type; + command[1] = "get"; + command[2] = "keytab"; + command[3] = name; + command[4] = NULL; + status = run_command(r, command, data, length); + if (*data == NULL) { + warn("no data returned by wallet server"); + return 255; + } + return status; +} /* * Given a remctl object, the Kerberos context, the name of a keytab object, * and a file name, call the correct wallet commands to download a keytab and - * write it to that file. Returns the setatus or 255 on an internal error. + * write it to that file. Returns the status or 255 on an internal error. */ int get_keytab(struct remctl *r, krb5_context ctx, const char *type, @@ -105,3 +208,77 @@ get_keytab(struct remctl *r, krb5_context ctx, const char *type, } return 0; } + +/* + * Given a remctl object, the Kerberos context, the type and name of a keytab + * object, and a file name, iterate through every existing principal in the + * keytab, get fresh keys for those principals, and save the old and new + * keys to that file. Returns the status, or 255 on an internal error. + */ +int +rekey_keytab(struct remctl *r, krb5_context ctx, const char *type, + const char *file) +{ + char *realm = NULL; + char *data = NULL; + char *tempfile, *backupfile; + size_t length = 0; + int status; + bool error = false, rekeyed = false; + struct principal_name *names_seen, *current_seen; + + tempfile = concat(file, ".new", (char *) 0); + + krb5_get_default_realm(ctx, &realm); + *names_seen = keytab_principals(ctx, file, realm); + /* keytab_principals(ctx, file, realm); */ + + /* TODO: Testing we got back the principals correctly, delete. */ + warn("Finished keytab_principals"); + current_seen = names_seen; + while (current_seen != NULL) { + warn("found principal %s", current_seen->princ); + current_seen = current_seen->next; + } + return 0; + + current_seen = names_seen; + while (current_seen != NULL) { + status = download_keytab(r, type, current_seen->princ, &data, + &length); + if (status != 0) { + warn("error rekeying for principal %s", current_seen->princ); + error = true; + } else { + if (data != NULL) { + append_file(tempfile, data, length); + rekeyed = true; + } + } + warn("seen principal %s", current_seen->princ); + current_seen = current_seen->next; + } + + /* If no new keytab data, then leave the keytab as-is. */ + if (rekeyed == false) + sysdie("no rekeyed principals found"); + + /* Now merge the original keytab file with the one containing the new. */ + if (access(file, F_OK) == 0) { + + /* If error, first copy the keytab file to filename.old */ + if (error == true) { + data = read_file(file, &length); + backupfile = concat(file, ".old", (char *) 0); + overwrite_file(backupfile, data, length); + } + merge_keytab(ctx, tempfile, file); + } else { + data = read_file(tempfile, &length); + write_file(file, data, length); + } + if (unlink(tempfile) < 0) + sysdie("unlink of temporary keytab file %s failed", tempfile); + free(tempfile); + return 0; +} diff --git a/client/wallet.c b/client/wallet.c index e6d8eb9..9c1eb09 100644 --- a/client/wallet.c +++ b/client/wallet.c @@ -194,7 +194,7 @@ main(int argc, char *argv[]) } argc -= optind; argv += optind; - if (argc < 3) + if (argc < 3 && strcmp(argv[0], "rekey") != 0) usage(1); /* -f is only supported for get and store and -S with get keytab. */ @@ -242,6 +242,10 @@ main(int argc, char *argv[]) } else { status = get_file(r, options.type, argv[1], argv[2], file); } + } else if (strcmp(argv[0], "rekey") == 0) { + if (argc > 2) + die("too many arguments"); + status = rekey_keytab(r, ctx, "keytab", argv[1]); } else { count = argc + 1; if (strcmp(argv[0], "store") == 0) { |