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) { | 
