diff options
author | Russ Allbery <rra@stanford.edu> | 2010-07-28 22:05:05 -0700 |
---|---|---|
committer | Russ Allbery <rra@stanford.edu> | 2010-07-28 22:05:05 -0700 |
commit | 5a48a5d5f7f2af72cf84114453748fbd2a337537 (patch) | |
tree | 052cd5178f6026994b94a6ff93fdfad1b2c00aa2 /client | |
parent | a87062c0c60ba4daa3489966c85233c549a5c477 (diff) |
Break wallet-rekey out into a separate client program
Build a separate wallet-rekey client that rekeys every keytab given
on the command-line. Fix some coding style issues and add internal
prototypes. Build the shared source for both clients into an
uninstalled library to save compilation time.
Diffstat (limited to 'client')
-rw-r--r-- | client/file.c | 6 | ||||
-rw-r--r-- | client/internal.h | 46 | ||||
-rw-r--r-- | client/keytab.c | 73 | ||||
-rw-r--r-- | client/options.c | 71 | ||||
-rw-r--r-- | client/wallet-rekey.c | 147 | ||||
-rw-r--r-- | client/wallet.c | 85 |
6 files changed, 311 insertions, 117 deletions
diff --git a/client/file.c b/client/file.c index 581d4a7..861da6a 100644 --- a/client/file.c +++ b/client/file.c @@ -46,9 +46,10 @@ 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. + * Given a filename, some data, and a length, append that data to an existing + * file. Dies on any failure. */ void append_file(const char *name, const void *data, size_t length) @@ -70,6 +71,7 @@ append_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 and atomically by creating file.new, writing the data, linking diff --git a/client/internal.h b/client/internal.h index d82196c..c8e5802 100644 --- a/client/internal.h +++ b/client/internal.h @@ -15,13 +15,43 @@ #include <sys/types.h> +/* + * Allow defaults to be set for a particular site with configure options if + * people don't want to use krb5.conf for some reason. + */ +#ifndef WALLET_SERVER +# define WALLET_SERVER NULL +#endif +#ifndef WALLET_PORT +# define WALLET_PORT 0 +#endif + /* Forward declarations to avoid unnecessary includes. */ struct remctl; struct iovec; +/* + * Basic wallet behavior options set either on the command line or via + * krb5.conf. If set via krb5.conf, we allocate memory for the strings, but + * we never free them. + */ +struct options { + char *type; + char *server; + char *principal; + char *user; + int port; +}; + BEGIN_DECLS /* + * Set default options from the system krb5.conf or from compile-time + * defaults. + */ +void default_options(krb5_context ctx, struct options *options); + +/* * Given a Kerberos context and a principal name, obtain Kerberos credentials * for that principal and store them in a temporary ticket cache for use by * later operations. kdestroy() then cleans up that cache. @@ -75,12 +105,28 @@ int get_keytab(struct remctl *, krb5_context, const char *type, const char *name, const char *file, const char *srvtab); /* + * Given a remctl object, the Kerberos context, the type for the wallet + * interface, and a file name of a keytab, iterate through every existing + * principal in the keytab in the local realm, get fresh keys for those + * principals, and save the old and new keys to that file. Returns true on + * success and false on partial failure to retrieve all the keys. + */ +bool rekey_keytab(struct remctl *, krb5_context, const char *type, + const char *file); + +/* * Given a filename, some data, and a length, write that data to the given * file with error checking, overwriting any existing contents. */ void overwrite_file(const char *name, const void *data, size_t length); /* + * Given a filename, some data, and a length, append that data to an existing + * file. Dies on any failure. + */ +void append_file(const char *name, const void *data, size_t length); + +/* * Given a filename, some data, and a length, write that data to the given * file safely and atomically by creating file.new, writing the data, linking * file to file.bak, and then renaming file.new to file. diff --git a/client/keytab.c b/client/keytab.c index 94a7858..41baa73 100644 --- a/client/keytab.c +++ b/client/keytab.c @@ -25,11 +25,12 @@ struct principal_name { 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 * +static struct principal_name * keytab_principals(krb5_context ctx, const char *file, char *realm) { char *princname = NULL, *princrealm = NULL; @@ -69,31 +70,27 @@ keytab_principals(krb5_context ctx, const char *file, char *realm) break; } } - if (found == false) { current = xmalloc(sizeof(struct principal_name)); current->princ = xstrdup(princname); current->next = names; names = current; } - 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); - return names; } + /* - * Given keytab data as a pointer to memory and a length and the path of a - * second keytab, merge the keys in the memory keytab into the file keytab. - * Currently, this doesn't do any cleanup of old kvnos and doesn't handle - * duplicate kvnos correctly. Dies on any error. + * Given two files containing keytab data, second keytab, merge the keys into + * the new file. Currently, this doesn't do any cleanup of old kvnos and + * doesn't handle duplicate kvnos correctly. Dies on any error. */ static void merge_keytab(krb5_context ctx, const char *newfile, const char *file) @@ -131,13 +128,14 @@ 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 +static int download_keytab(struct remctl *r, const char *type, const char *name, char **data, size_t *length) { @@ -157,6 +155,7 @@ download_keytab(struct remctl *r, const char *type, const char *name, 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 @@ -201,13 +200,15 @@ 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. + * Given a remctl object, the Kerberos context, the type for the wallet + * interface, and a file name of a keytab, iterate through every existing + * principal in the keytab in the local realm, get fresh keys for those + * principals, and save the old and new keys to that file. Returns true on + * success and false on partial failure to retrieve all the keys. */ -int +bool rekey_keytab(struct remctl *r, krb5_context ctx, const char *type, const char *file) { @@ -220,46 +221,46 @@ rekey_keytab(struct remctl *r, krb5_context ctx, const char *type, struct principal_name *names, *current; tempfile = concat(file, ".new", (char *) 0); - krb5_get_default_realm(ctx, &realm); names = keytab_principals(ctx, file, realm); - for (current = names; current != NULL; current = current->next) { status = download_keytab(r, type, current->princ, &data, &length); if (status != 0) { warn("error rekeying for principal %s", current->princ); error = true; - } else { - if (data != NULL) { - if (access(tempfile, F_OK) == 0) - append_file(tempfile, data, length); - else - write_file(tempfile, data, length); - rekeyed = true; - } + } else if (data != NULL) { + if (access(tempfile, F_OK) == 0) + append_file(tempfile, data, length); + else + write_file(tempfile, data, length); + rekeyed = true; } } /* If no new keytab data, then leave the keytab as-is. */ - if (rekeyed == false) - sysdie("no rekeyed principals found"); + if (!rekeyed) + sysdie("no rekeyable 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) { + /* + * Now merge the original keytab file with the one containing the new + * keys. If there is an error, first make a backup of the current keytab + * file as keytab.old. + */ + if (access(file, F_OK) != 0) + link(tempfile, file); + else { + if (error) { data = read_file(file, &length); backupfile = concat(file, ".old", (char *) 0); overwrite_file(backupfile, data, length); + warn("partial failure to rekey keytab %s, old keyab left in %s", + file, backupfile); + free(backupfile); } 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; + return !error; } diff --git a/client/options.c b/client/options.c new file mode 100644 index 0000000..2f1de70 --- /dev/null +++ b/client/options.c @@ -0,0 +1,71 @@ +/* + * Set default options for wallet clients. + * + * This file provides the functions to set default options from the krb5.conf + * file for both wallet and wallet-rekey. + * + * Written by Russ Allbery <rra@stanford.edu> + * Copyright 2006, 2007, 2008, 2010 + * Board of Trustees, Leland Stanford Jr. University + * + * See LICENSE for licensing terms. + */ + +#include <config.h> +#include <portable/krb5.h> +#include <portable/system.h> + +#include <client/internal.h> + + +/* + * Load a string option from Kerberos appdefaults. This requires an annoying + * workaround because one cannot specify a default value of NULL. + */ +static void +default_string(krb5_context ctx, const char *opt, const char *defval, + char **result) +{ + if (defval == NULL) + defval = ""; + krb5_appdefault_string(ctx, "wallet", NULL, opt, defval, result); + if (*result != NULL && (*result)[0] == '\0') { + free(*result); + *result = NULL; + } +} + + +/* + * Load a number option from Kerberos appdefaults. The native interface + * doesn't support numbers, so we actually read a string and then convert. + */ +static void +default_number(krb5_context ctx, const char *opt, int defval, int *result) +{ + char *tmp = NULL; + + krb5_appdefault_string(ctx, "wallet", NULL, opt, "", &tmp); + if (tmp != NULL && tmp[0] != '\0') + *result = atoi(tmp); + else + *result = defval; + if (tmp != NULL) + free(tmp); +} + + +/* + * Set option defaults and then get krb5.conf configuration, if any, and + * override the defaults. Later, command-line options will override those + * defaults. + */ +void +default_options(krb5_context ctx, struct options *options) +{ + default_string(ctx, "wallet_type", "wallet", &options->type); + default_string(ctx, "wallet_server", WALLET_SERVER, &options->server); + default_string(ctx, "wallet_principal", NULL, &options->principal); + default_number(ctx, "wallet_port", WALLET_PORT, &options->port); + options->user = NULL; +} diff --git a/client/wallet-rekey.c b/client/wallet-rekey.c new file mode 100644 index 0000000..3a9687c --- /dev/null +++ b/client/wallet-rekey.c @@ -0,0 +1,147 @@ +/* + * A specialized wallet client for rekeying a keytab. + * + * Written by Russ Allbery <rra@stanford.edu> + * and Jon Robertson <jonrober@stanford.edu> + * Copyright 2010 Board of Trustees, Leland Stanford Jr. University + * + * See LICENSE for licensing terms. + */ + +#include <config.h> +#include <portable/krb5.h> +#include <portable/system.h> + +#include <remctl.h> +#include <errno.h> + +#include <client/internal.h> +#include <util/messages.h> +#include <util/messages-krb5.h> + +/* + * Usage message. Use as a format and pass the port number and default server + * name. + */ +static const char usage_message[] = "\ +Usage: wallet-rekey [options] [<file> ...]\n\ +\n\ +Options:\n\ + -c <command> Command prefix to use (default: wallet)\n\ + -k <principal> Kerberos principal of the server\n\ + -h Display this help\n\ + -p <port> Port of server (default: %d, if zero, remctl default)\n\ + -s <server> Server hostname (default: %s)\n\ + -u <user> Authenticate as <user> before rekeying\n\ + -v Display the version of wallet\n"; + + +/* + * Display the usage message for wallet-rekey. + */ +static void +usage(int status) +{ + fprintf((status == 0) ? stdout : stderr, usage_message, WALLET_PORT, + (WALLET_SERVER == NULL) ? "<none>" : WALLET_SERVER); + exit(status); +} + + +/* + * Main routine. Parse the arguments and then perform the desired operation. + */ +int +main(int argc, char *argv[]) +{ + krb5_context ctx; + krb5_error_code retval; + struct options options; + int option, i; + bool okay = true; + struct remctl *r; + long tmp; + char *end; + + /* Set up logging and identity. */ + message_program_name = "wallet"; + + /* Initialize default configuration. */ + retval = krb5_init_context(&ctx); + if (retval != 0) + die_krb5(ctx, retval, "cannot initialize Kerberos"); + default_options(ctx, &options); + + while ((option = getopt(argc, argv, "c:k:hp:S:s:u:v")) != EOF) { + switch (option) { + case 'c': + options.type = optarg; + break; + case 'k': + options.principal = optarg; + break; + case 'h': + usage(0); + break; + case 'p': + errno = 0; + tmp = strtol(optarg, &end, 10); + if (tmp <= 0 || tmp > 65535 || *end != '\0') + die("invalid port number %s", optarg); + options.port = tmp; + break; + case 's': + options.server = optarg; + break; + case 'u': + options.user = optarg; + break; + case 'v': + printf("%s\n", PACKAGE_STRING); + exit(0); + break; + default: + usage(1); + break; + } + } + argc -= optind; + argv += optind; + + /* + * If no server was set at configure time and none was set on the command + * line or with krb5.conf settings, we can't continue. + */ + if (options.server == NULL) + die("no server specified in krb5.conf or with -s"); + + /* If a user was specified, obtain Kerberos tickets. */ + if (options.user != NULL) + kinit(ctx, options.user); + + /* Open a remctl connection. */ + r = remctl_new(); + if (r == NULL) + sysdie("cannot allocate memory"); + if (!remctl_open(r, options.server, options.port, options.principal)) + die("%s", remctl_error(r)); + + /* + * Rekey all the keytabs given on the command line, or the system keytab + * if none were given. + */ + if (argc == 0) + okay = rekey_keytab(r, ctx, options.type, "/etc/krb5.keytab"); + else { + for (i = 0; i < argc; i++) { + okay = rekey_keytab(r, ctx, options.type, argv[i]); + if (!okay) + break; + } + } + remctl_close(r); + krb5_free_context(ctx); + if (options.user != NULL) + kdestroy(); + exit(okay ? 0 : 1); +} diff --git a/client/wallet.c b/client/wallet.c index d61fc74..dc04dcd 100644 --- a/client/wallet.c +++ b/client/wallet.c @@ -22,30 +22,9 @@ #include <util/xmalloc.h> /* - * Basic wallet behavior options set either on the command line or via - * krb5.conf. If set via krb5.conf, we allocate memory for the strings, but - * we never free them. + * Usage message. Use as a format and pass the port number and default server + * name. */ -struct options { - char *type; - char *server; - char *principal; - char *user; - int port; -}; - -/* - * Allow defaults to be set for a particular site with configure options if - * people don't want to use krb5.conf for some reason. - */ -#ifndef WALLET_SERVER -# define WALLET_SERVER NULL -#endif -#ifndef WALLET_PORT -# define WALLET_PORT 0 -#endif - -/* Usage message. Use as a format and pass the port number. */ static const char usage_message[] = "\ Usage: wallet [options] <command> <type> <name> [<arg> ...]\n\ wallet [options] acl <command> <id> [<arg> ...]\n\ @@ -58,11 +37,12 @@ Options:\n\ -p <port> Port of server (default: %d, if zero, remctl default)\n\ -S <srvtab> For the get keytab command, srvtab output file\n\ -s <server> Server hostname (default: %s)\n\ + -u <user> Authenticate as <user> before running command\n\ -v Display the version of wallet\n"; /* - * Display the usage message for remctl. + * Display the usage message for wallet. */ static void usage(int status) @@ -74,59 +54,6 @@ usage(int status) /* - * Load a string option from Kerberos appdefaults. This requires an annoying - * workaround because one cannot specify a default value of NULL. - */ -static void -default_string(krb5_context ctx, const char *opt, const char *defval, - char **result) -{ - if (defval == NULL) - defval = ""; - krb5_appdefault_string(ctx, "wallet", NULL, opt, defval, result); - if (*result != NULL && (*result)[0] == '\0') { - free(*result); - *result = NULL; - } -} - - -/* - * Load a number option from Kerberos appdefaults. The native interface - * doesn't support numbers, so we actually read a string and then convert. - */ -static void -default_number(krb5_context ctx, const char *opt, int defval, int *result) -{ - char *tmp = NULL; - - krb5_appdefault_string(ctx, "wallet", NULL, opt, "", &tmp); - if (tmp != NULL && tmp[0] != '\0') - *result = atoi(tmp); - else - *result = defval; - if (tmp != NULL) - free(tmp); -} - - -/* - * Set option defaults and then get krb5.conf configuration, if any, and - * override the defaults. Later, command-line options will override those - * defaults. - */ -static void -set_defaults(krb5_context ctx, struct options *options) -{ - default_string(ctx, "wallet_type", "wallet", &options->type); - default_string(ctx, "wallet_server", WALLET_SERVER, &options->server); - default_string(ctx, "wallet_principal", NULL, &options->principal); - default_number(ctx, "wallet_port", WALLET_PORT, &options->port); - options->user = NULL; -} - - -/* * Main routine. Parse the arguments and then perform the desired operation. */ int @@ -151,7 +78,7 @@ main(int argc, char *argv[]) retval = krb5_init_context(&ctx); if (retval != 0) die_krb5(ctx, retval, "cannot initialize Kerberos"); - set_defaults(ctx, &options); + default_options(ctx, &options); while ((option = getopt(argc, argv, "c:f:k:hp:S:s:u:v")) != EOF) { switch (option) { @@ -194,7 +121,7 @@ main(int argc, char *argv[]) } argc -= optind; argv += optind; - if (argc < 3 && strcmp(argv[0], "rekey") != 0) + if (argc < 3) usage(1); /* -f is only supported for get and store and -S with get keytab. */ |