diff options
author | Jon Robertson <jonrober@stanford.edu> | 2010-07-27 12:40:12 -0700 |
---|---|---|
committer | Jon Robertson <jonrober@stanford.edu> | 2010-07-27 12:40:12 -0700 |
commit | 534f2111ab41ed63024d811a3d8f5b81256d83a9 (patch) | |
tree | 7419aed995a22abee9a4418c8287b1551b693051 /client | |
parent | c75eb196a37ce8ca1acd791267cfb36ee30fdcdb (diff) |
Adding wallet rekey capability -- work in progress, testing
First, testing version of wallet rekey code, committed in order to get
feedback from Russ. This code will eventually take an existing keytab
file, and for every principal belonging to our default realm in it, get
new versions of that keytab and merge them into the file. This allows
for quietly rekeying principals automatically.
Diffstat (limited to 'client')
-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) { |