aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Makefile.am17
-rw-r--r--client/file.c6
-rw-r--r--client/internal.h46
-rw-r--r--client/keytab.c73
-rw-r--r--client/options.c71
-rw-r--r--client/wallet-rekey.c147
-rw-r--r--client/wallet.c85
8 files changed, 324 insertions, 122 deletions
diff --git a/.gitignore b/.gitignore
index 10cfbf8..67f4760 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,7 @@
/aclocal.m4
/build-aux/
/client/wallet
+/client/wallet-rekey
/config.h
/config.h.in
/config.h.in~
diff --git a/Makefile.am b/Makefile.am
index d5dccd9..10f47d9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -57,15 +57,22 @@ util_libutil_a_SOURCES = util/concat.c util/concat.h util/macros.h \
util/messages.h util/xmalloc.c util/xmalloc.h
util_libutil_a_CPPFLAGS = $(KRB5_CPPFLAGS)
-bin_PROGRAMS = client/wallet
+noinst_LIBRARIES += client/libwallet.a
+client_libwallet_a_SOURCES = client/file.c client/internal.h client/keytab.c \
+ client/krb5.c client/options.c client/remctl.c client/srvtab.c
+client_libwallet_a_CPPFLAGS = $(REMCTL_CPPFLAGS) $(KRB5_CPPFLAGS)
+
+bin_PROGRAMS = client/wallet client/wallet-rekey
dist_sbin_SCRIPTS = server/keytab-backend server/wallet-admin \
server/wallet-backend server/wallet-report
-client_wallet_SOURCES = client/file.c client/internal.h client/keytab.c \
- client/krb5.c client/remctl.c client/srvtab.c client/wallet.c
client_wallet_CPPFLAGS = $(REMCTL_CPPFLAGS) $(KRB5_CPPFLAGS)
client_wallet_LDFLAGS = $(REMCTL_LDFLAGS) $(KRB5_LDFLAGS)
-client_wallet_LDADD = util/libutil.a portable/libportable.a $(REMCTL_LIBS) \
- $(KRB5_LIBS)
+client_wallet_LDADD = client/libwallet.a util/libutil.a \
+ portable/libportable.a $(REMCTL_LIBS) $(KRB5_LIBS)
+client_wallet_rekey_CPPFLAGS = $(REMCTL_CPPFLAGS) $(KRB5_CPPFLAGS)
+client_wallet_rekey_LDFLAGS = $(REMCTL_LDFLAGS) $(KRB5_LDFLAGS)
+client_wallet_rekey_LDADD = client/libwallet.a util/libutil.a \
+ portable/libportable.a $(REMCTL_LIBS) $(KRB5_LIBS)
dist_man_MANS = client/wallet.1 server/keytab-backend.8 \
server/wallet-admin.8 server/wallet-backend.8 server/wallet-report.8
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. */