aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--client/file.c26
-rw-r--r--client/keytab.c179
-rw-r--r--client/wallet.c6
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) {