aboutsummaryrefslogtreecommitdiff
path: root/client
diff options
context:
space:
mode:
Diffstat (limited to 'client')
-rw-r--r--client/file.c25
-rw-r--r--client/internal.h46
-rw-r--r--client/keytab.c175
-rw-r--r--client/options.c71
-rw-r--r--client/wallet-rekey.1271
-rw-r--r--client/wallet-rekey.c147
-rw-r--r--client/wallet-rekey.pod165
-rw-r--r--client/wallet.112
-rw-r--r--client/wallet.c87
-rw-r--r--client/wallet.pod11
10 files changed, 924 insertions, 86 deletions
diff --git a/client/file.c b/client/file.c
index 66d5f63..861da6a 100644
--- a/client/file.c
+++ b/client/file.c
@@ -48,6 +48,31 @@ 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)
+{
+ int fd;
+ ssize_t status;
+
+ 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
* 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/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 5f2076f..9a7734e 100644
--- a/client/keytab.c
+++ b/client/keytab.c
@@ -17,13 +17,84 @@
#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 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 a context, a keytab file, and a realm, return a list of all
+ * principals in that file.
+ */
+static 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 = NULL, *current = NULL, *last = 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)
+ die_krb5(ctx, status, "cannot unparse name for a principal");
+
+ /* Separate into principal and realm. */
+ princrealm = strchr(princname, '@');
+ if (princrealm != NULL) {
+ *princrealm = '\0';
+ princrealm++;
+ }
+ if (princrealm == NULL || strcmp(princrealm, realm) != 0)
+ continue;
+
+ /* Check to see if the principal has already been listed. */
+ found = false;
+ for (current = names; current != NULL; current = current->next) {
+ if (strcmp(current->princ, princname) == 0) {
+ found = true;
+ break;
+ }
+ last = current;
+ }
+ if (found == false) {
+ current = xmalloc(sizeof(struct principal_name));
+ current->princ = xstrdup(princname);
+ current->next = NULL;
+ if (last == NULL)
+ names = current;
+ else
+ last->next = 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 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)
@@ -63,9 +134,36 @@ merge_keytab(krb5_context ctx, const char *newfile, const char *file)
/*
+ * 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.
+ */
+static 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 && status == 0) {
+ 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 +203,70 @@ get_keytab(struct remctl *r, krb5_context ctx, const char *type,
}
return 0;
}
+
+
+/*
+ * 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 *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, *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);
+ if (!rekeyed)
+ die("aborting, keytab unchanged");
+ 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;
+ }
+ }
+
+ /* If no new keytab data, then leave the keytab as-is. */
+ if (!rekeyed)
+ die("no rekeyable principals found");
+
+ /*
+ * 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 keytab left in %s",
+ file, backupfile);
+ free(backupfile);
+ }
+ merge_keytab(ctx, tempfile, file);
+ }
+ if (unlink(tempfile) < 0)
+ sysdie("unlink of temporary keytab file %s failed", tempfile);
+ free(tempfile);
+ 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.1 b/client/wallet-rekey.1
new file mode 100644
index 0000000..965a123
--- /dev/null
+++ b/client/wallet-rekey.1
@@ -0,0 +1,271 @@
+.\" Automatically generated by Pod::Man 2.22 (Pod::Simple 3.14)
+.\"
+.\" Standard preamble:
+.\" ========================================================================
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Vb \" Begin verbatim text
+.ft CW
+.nf
+.ne \\$1
+..
+.de Ve \" End verbatim text
+.ft R
+.fi
+..
+.\" Set up some character translations and predefined strings. \*(-- will
+.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
+.\" double quote, and \*(R" will give a right double quote. \*(C+ will
+.\" give a nicer C++. Capital omega is used to do unbreakable dashes and
+.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff,
+.\" nothing in troff, for use with C<>.
+.tr \(*W-
+.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
+.ie n \{\
+. ds -- \(*W-
+. ds PI pi
+. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
+. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
+. ds L" ""
+. ds R" ""
+. ds C` ""
+. ds C' ""
+'br\}
+.el\{\
+. ds -- \|\(em\|
+. ds PI \(*p
+. ds L" ``
+. ds R" ''
+'br\}
+.\"
+.\" Escape single quotes in literal strings from groff's Unicode transform.
+.ie \n(.g .ds Aq \(aq
+.el .ds Aq '
+.\"
+.\" If the F register is turned on, we'll generate index entries on stderr for
+.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
+.\" entries marked with X<> in POD. Of course, you'll have to process the
+.\" output yourself in some meaningful fashion.
+.ie \nF \{\
+. de IX
+. tm Index:\\$1\t\\n%\t"\\$2"
+..
+. nr % 0
+. rr F
+.\}
+.el \{\
+. de IX
+..
+.\}
+.\"
+.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
+.\" Fear. Run. Save yourself. No user-serviceable parts.
+. \" fudge factors for nroff and troff
+.if n \{\
+. ds #H 0
+. ds #V .8m
+. ds #F .3m
+. ds #[ \f1
+. ds #] \fP
+.\}
+.if t \{\
+. ds #H ((1u-(\\\\n(.fu%2u))*.13m)
+. ds #V .6m
+. ds #F 0
+. ds #[ \&
+. ds #] \&
+.\}
+. \" simple accents for nroff and troff
+.if n \{\
+. ds ' \&
+. ds ` \&
+. ds ^ \&
+. ds , \&
+. ds ~ ~
+. ds /
+.\}
+.if t \{\
+. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u"
+. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u'
+. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u'
+. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u'
+. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u'
+. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u'
+.\}
+. \" troff and (daisy-wheel) nroff accents
+.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V'
+.ds 8 \h'\*(#H'\(*b\h'-\*(#H'
+.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#]
+.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H'
+.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u'
+.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#]
+.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#]
+.ds ae a\h'-(\w'a'u*4/10)'e
+.ds Ae A\h'-(\w'A'u*4/10)'E
+. \" corrections for vroff
+.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u'
+.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u'
+. \" for low resolution devices (crt and lpr)
+.if \n(.H>23 .if \n(.V>19 \
+\{\
+. ds : e
+. ds 8 ss
+. ds o a
+. ds d- d\h'-1'\(ga
+. ds D- D\h'-1'\(hy
+. ds th \o'bp'
+. ds Th \o'LP'
+. ds ae ae
+. ds Ae AE
+.\}
+.rm #[ #] #H #V #F C
+.\" ========================================================================
+.\"
+.IX Title "WALLET-REKEY 1"
+.TH WALLET-REKEY 1 "2010-08-25" "0.12" "wallet"
+.\" For nroff, turn off justification. Always turn off hyphenation; it makes
+.\" way too many mistakes in technical documents.
+.if n .ad l
+.nh
+.SH "NAME"
+wallet\-rekey \- Client for rekeying a Kerberos keytab using wallet
+.SH "SYNOPSIS"
+.IX Header "SYNOPSIS"
+\&\fBwallet-rekey\fR [\fB\-hv\fR] [\fB\-c\fR \fIcommand\fR] [\fB\-k\fR \fIprincipal\fR]
+ [\fB\-p\fR \fIport\fR] [\fB\-s\fR \fIserver\fR] [\fB\-u\fR \fIprincipal\fR] [\fIkeytab\fR ...]
+.SH "DESCRIPTION"
+.IX Header "DESCRIPTION"
+\&\fBwallet-rekey\fR is a specialized client for the wallet system used to
+rekey a Kerberos keytab by downloading new keytab objects from wallet for
+each principal found in the keytab. For each keytab file listed on the
+command line, it walks through the principals in that keytab, finds all
+from the local default realm, requests new wallet keytab objects for each
+principal (removing the realm when naming the keytab), and merges the new
+keys into the keytab.
+.PP
+If an error occurs before any new keys were downloaded, \fBwallet-rekey\fR
+aborts. If some new keys were successfully downloaded, \fBwallet-rekey\fR
+warns about errors but continues to rekey all principals that it can. In
+this case, a copy of the existing keytab prior to the rekeying is saved in
+a file named by appending \f(CW\*(C`.old\*(C'\fR to the file name.
+.PP
+If no keytab file name is given on the command line, \fBwallet-rekey\fR
+attempts to rekey \fI/etc/krb5.keytab\fR, the system default keytab file.
+.PP
+The new keys are merged into the existing keytab file, but old keys are
+not removed. This means that, over time, the keytab will grow and
+accumulate old keys, which eventually should no longer be honored.
+Administrators may want to run:
+.PP
+.Vb 1
+\& kadmin \-q \*(Aqktremove \-k <keytab> <principal> old\*(Aq
+.Ve
+.PP
+for \s-1MIT\s0 Kerberos, where <keytab> is the path to the keytab and <principal>
+is a principal in the keytab (repeating the command for each principal)
+or:
+.PP
+.Vb 1
+\& ktutil \-k <keytab> purge
+.Ve
+.PP
+for Heimdal. This functionality will eventually be provided by
+\&\fBwallet-rekey\fR directly.
+.SH "OPTIONS"
+.IX Header "OPTIONS"
+.IP "\fB\-c\fR \fIcommand\fR" 4
+.IX Item "-c command"
+The command prefix (remctl type) to use. Normally this is an internal
+implementation detail and the default (\f(CW\*(C`wallet\*(C'\fR) should be fine. It may
+sometimes be useful to use a different prefix for testing a different
+version of the wallet code on the server. This option can also be set in
+\&\fIkrb5.conf\fR; see \s-1CONFIGURATION\s0 below.
+.IP "\fB\-k\fR \fIprincipal\fR" 4
+.IX Item "-k principal"
+The service principal of the wallet server. The default is to use the
+\&\f(CW\*(C`host\*(C'\fR principal for the wallet server. The principal chosen must match
+one of the keys in the keytab used by \fBremctld\fR on the wallet server.
+This option can also be set in \fIkrb5.conf\fR; see \s-1CONFIGURATION\s0 below.
+.IP "\fB\-h\fR" 4
+.IX Item "-h"
+Display a brief summary of options and exit. All other valid options and
+commands are ignored.
+.IP "\fB\-p\fR \fIport\fR" 4
+.IX Item "-p port"
+The port to connect to on the wallet server. The default is the default
+remctl port. This option can also be set in \fIkrb5.conf\fR; see
+\&\s-1CONFIGURATION\s0 below.
+.IP "\fB\-s\fR \fIserver\fR" 4
+.IX Item "-s server"
+The wallet server to connect to. The default may be set when compiling
+the wallet client. If it isn't, either \fB\-s\fR must be given or the server
+must be set in \fIkrb5.conf\fR. See \s-1CONFIGURATION\s0 below.
+.IP "\fB\-u\fR \fIprincipal\fR" 4
+.IX Item "-u principal"
+Rather than using the user's existing ticket cache for authentication,
+authenticate as \fIprincipal\fR first and use those credentials for
+authentication to the wallet server. \fBwallet\fR will prompt for the
+password for \fIprincipal\fR. Non-password authentication methods such as
+\&\s-1PKINIT\s0 aren't supported; to use those, run \fBkinit\fR first and use an
+existing ticket cache.
+.IP "\fB\-v\fR" 4
+.IX Item "-v"
+Display the version of the \fBwallet\fR client and exit. All other valid
+options and commands are ignored.
+.SH "CONFIGURATION"
+.IX Header "CONFIGURATION"
+The wallet system, including \fBwallet-rekey\fR, can optionally be configured
+in the system \fIkrb5.conf\fR. It will read the default \fIkrb5.conf\fR file
+for the Kerberos libraries with which it was compiled. To set an option,
+put the option in the [appdefaults] section. \fBwallet-rekey\fR will look
+for options either at the top level of the [appdefaults] section or in a
+subsection named \f(CW\*(C`wallet\*(C'\fR. For example, the following fragment of a
+\&\fIkrb5.conf\fR file would set the default port to 4373 and the default
+server to \f(CW\*(C`wallet.example.org\*(C'\fR.
+.PP
+.Vb 5
+\& [appdefaults]
+\& wallet_port = 4373
+\& wallet = {
+\& wallet_server = wallet.example.org
+\& }
+.Ve
+.PP
+The supported options are:
+.IP "wallet_principal" 4
+.IX Item "wallet_principal"
+The service principal of the wallet server. The default is to use the
+\&\f(CW\*(C`host\*(C'\fR principal for the wallet server. The principal chosen must match
+one of the keys in the keytab used by \fBremctld\fR on the wallet server.
+The \fB\-k\fR command-line option overrides this setting.
+.IP "wallet_port" 4
+.IX Item "wallet_port"
+The port to connect to on the wallet server. The default is the default
+remctl port. The \fB\-p\fR command-line option overrides this setting.
+.IP "wallet_server" 4
+.IX Item "wallet_server"
+The wallet server to connect to. The \fB\-s\fR command-line option overrides
+this setting. The default may be set when compiling the wallet client.
+If it isn't, either \fB\-s\fR must be given or this parameter must be present
+in in \fIkrb5.conf\fR.
+.IP "wallet_type" 4
+.IX Item "wallet_type"
+The command prefix (remctl type) to use. Normally this is an internal
+implementation detail and the default (\f(CW\*(C`wallet\*(C'\fR) should be fine. It may
+sometimes be useful to use a different prefix for testing a different
+version of the wallet code on the server. The \fB\-c\fR command-line option
+overrides this setting.
+.SH "SEE ALSO"
+.IX Header "SEE ALSO"
+\&\fIkadmin\fR\|(8), \fIkinit\fR\|(1), \fIkrb5.conf\fR\|(5), \fIremctl\fR\|(1), \fIremctld\fR\|(8), \fIwallet\fR\|(1)
+.PP
+This program is part of the wallet system. The current version is available
+from <http://www.eyrie.org/~eagle/software/wallet/>.
+.PP
+\&\fBwallet-rekey\fR uses the remctl protocol. For more information about
+remctl, see <http://www.eyrie.org/~eagle/software/remctl/>.
+.SH "AUTHOR"
+.IX Header "AUTHOR"
+Russ Allbery <rra@stanford.edu>
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-rekey.pod b/client/wallet-rekey.pod
new file mode 100644
index 0000000..efe9a0b
--- /dev/null
+++ b/client/wallet-rekey.pod
@@ -0,0 +1,165 @@
+=for stopwords
+wallet-rekey rekey rekeying keytab -hv Heimdal remctl remctld PKINIT kinit
+appdefaults Allbery
+
+=head1 NAME
+
+wallet-rekey - Client for rekeying a Kerberos keytab using wallet
+
+=head1 SYNOPSIS
+
+B<wallet-rekey> [B<-hv>] [B<-c> I<command>] [B<-k> I<principal>]
+ [B<-p> I<port>] [B<-s> I<server>] [B<-u> I<principal>] [I<keytab> ...]
+
+=head1 DESCRIPTION
+
+B<wallet-rekey> is a specialized client for the wallet system used to
+rekey a Kerberos keytab by downloading new keytab objects from wallet for
+each principal found in the keytab. For each keytab file listed on the
+command line, it walks through the principals in that keytab, finds all
+from the local default realm, requests new wallet keytab objects for each
+principal (removing the realm when naming the keytab), and merges the new
+keys into the keytab.
+
+If an error occurs before any new keys were downloaded, B<wallet-rekey>
+aborts. If some new keys were successfully downloaded, B<wallet-rekey>
+warns about errors but continues to rekey all principals that it can. In
+this case, a copy of the existing keytab prior to the rekeying is saved in
+a file named by appending C<.old> to the file name.
+
+If no keytab file name is given on the command line, B<wallet-rekey>
+attempts to rekey F</etc/krb5.keytab>, the system default keytab file.
+
+The new keys are merged into the existing keytab file, but old keys are
+not removed. This means that, over time, the keytab will grow and
+accumulate old keys, which eventually should no longer be honored.
+Administrators may want to run:
+
+ kadmin -q 'ktremove -k <keytab> <principal> old'
+
+for MIT Kerberos, where <keytab> is the path to the keytab and <principal>
+is a principal in the keytab (repeating the command for each principal)
+or:
+
+ ktutil -k <keytab> purge
+
+for Heimdal. This functionality will eventually be provided by
+B<wallet-rekey> directly.
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<-c> I<command>
+
+The command prefix (remctl type) to use. Normally this is an internal
+implementation detail and the default (C<wallet>) should be fine. It may
+sometimes be useful to use a different prefix for testing a different
+version of the wallet code on the server. This option can also be set in
+F<krb5.conf>; see L<CONFIGURATION> below.
+
+=item B<-k> I<principal>
+
+The service principal of the wallet server. The default is to use the
+C<host> principal for the wallet server. The principal chosen must match
+one of the keys in the keytab used by B<remctld> on the wallet server.
+This option can also be set in F<krb5.conf>; see L<CONFIGURATION> below.
+
+=item B<-h>
+
+Display a brief summary of options and exit. All other valid options and
+commands are ignored.
+
+=item B<-p> I<port>
+
+The port to connect to on the wallet server. The default is the default
+remctl port. This option can also be set in F<krb5.conf>; see
+L<CONFIGURATION> below.
+
+=item B<-s> I<server>
+
+The wallet server to connect to. The default may be set when compiling
+the wallet client. If it isn't, either B<-s> must be given or the server
+must be set in F<krb5.conf>. See L<CONFIGURATION> below.
+
+=item B<-u> I<principal>
+
+Rather than using the user's existing ticket cache for authentication,
+authenticate as I<principal> first and use those credentials for
+authentication to the wallet server. B<wallet> will prompt for the
+password for I<principal>. Non-password authentication methods such as
+PKINIT aren't supported; to use those, run B<kinit> first and use an
+existing ticket cache.
+
+=item B<-v>
+
+Display the version of the B<wallet> client and exit. All other valid
+options and commands are ignored.
+
+=back
+
+=head1 CONFIGURATION
+
+The wallet system, including B<wallet-rekey>, can optionally be configured
+in the system F<krb5.conf>. It will read the default F<krb5.conf> file
+for the Kerberos libraries with which it was compiled. To set an option,
+put the option in the [appdefaults] section. B<wallet-rekey> will look
+for options either at the top level of the [appdefaults] section or in a
+subsection named C<wallet>. For example, the following fragment of a
+F<krb5.conf> file would set the default port to 4373 and the default
+server to C<wallet.example.org>.
+
+ [appdefaults]
+ wallet_port = 4373
+ wallet = {
+ wallet_server = wallet.example.org
+ }
+
+The supported options are:
+
+=over 4
+
+=item wallet_principal
+
+The service principal of the wallet server. The default is to use the
+C<host> principal for the wallet server. The principal chosen must match
+one of the keys in the keytab used by B<remctld> on the wallet server.
+The B<-k> command-line option overrides this setting.
+
+=item wallet_port
+
+The port to connect to on the wallet server. The default is the default
+remctl port. The B<-p> command-line option overrides this setting.
+
+=item wallet_server
+
+The wallet server to connect to. The B<-s> command-line option overrides
+this setting. The default may be set when compiling the wallet client.
+If it isn't, either B<-s> must be given or this parameter must be present
+in in F<krb5.conf>.
+
+=item wallet_type
+
+The command prefix (remctl type) to use. Normally this is an internal
+implementation detail and the default (C<wallet>) should be fine. It may
+sometimes be useful to use a different prefix for testing a different
+version of the wallet code on the server. The B<-c> command-line option
+overrides this setting.
+
+=back
+
+=head1 SEE ALSO
+
+kadmin(8), kinit(1), krb5.conf(5), remctl(1), remctld(8), wallet(1)
+
+This program is part of the wallet system. The current version is available
+from L<http://www.eyrie.org/~eagle/software/wallet/>.
+
+B<wallet-rekey> uses the remctl protocol. For more information about
+remctl, see L<http://www.eyrie.org/~eagle/software/remctl/>.
+
+=head1 AUTHOR
+
+Russ Allbery <rra@stanford.edu>
+
+=cut
diff --git a/client/wallet.1 b/client/wallet.1
index 7d7004f..0e02fe9 100644
--- a/client/wallet.1
+++ b/client/wallet.1
@@ -1,4 +1,4 @@
-.\" Automatically generated by Pod::Man 2.22 (Pod::Simple 3.13)
+.\" Automatically generated by Pod::Man 2.22 (Pod::Simple 3.14)
.\"
.\" Standard preamble:
.\" ========================================================================
@@ -124,7 +124,7 @@
.\" ========================================================================
.\"
.IX Title "WALLET 1"
-.TH WALLET 1 "2010-03-08" "0.11" "wallet"
+.TH WALLET 1 "2010-08-25" "0.12" "wallet"
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
.\" way too many mistakes in technical documents.
.if n .ad l
@@ -311,6 +311,14 @@ entry in the special \s-1ACL\s0 \f(CW\*(C`ADMIN\*(C'\fR cannot be removed to pro
accidental lockout, but administrators can remove themselves from the
\&\f(CW\*(C`ADMIN\*(C'\fR \s-1ACL\s0 and can leave only a non-functioning entry on the \s-1ACL\s0. Use
caution when removing entries from the \f(CW\*(C`ADMIN\*(C'\fR \s-1ACL\s0.
+.IP "acl rename <id> <name>" 4
+.IX Item "acl rename <id> <name>"
+Renames the \s-1ACL\s0 identified by <id> to <name>. This changes the
+human-readable name, not the underlying numeric \s-1ID\s0, so the \s-1ACL\s0's
+associations with objects will be unchanged. The \f(CW\*(C`ADMIN\*(C'\fR \s-1ACL\s0 may not be
+renamed. <id> may be either the current name or the numeric \s-1ID\s0. <name>
+must not be all-numeric. To rename an \s-1ACL\s0, the current user must be
+authorized by the \f(CW\*(C`ADMIN\*(C'\fR \s-1ACL\s0.
.IP "acl show <id>" 4
.IX Item "acl show <id>"
Display the name, numeric \s-1ID\s0, and entries of the \s-1ACL\s0 <id>.
diff --git a/client/wallet.c b/client/wallet.c
index e6d8eb9..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) {
@@ -242,6 +169,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, options.type, argv[1]);
} else {
count = argc + 1;
if (strcmp(argv[0], "store") == 0) {
diff --git a/client/wallet.pod b/client/wallet.pod
index db93700..45969b2 100644
--- a/client/wallet.pod
+++ b/client/wallet.pod
@@ -5,7 +5,7 @@ wallet - Client for retrieving secure data from a central server
=for stopwords
-hv srvtab arg keytabs metadata keytab ACL PTS kinit klist remctl PKINIT
acl timestamp autocreate backend-specific setacl enctypes enctype ktadd
-KDC appdefaults remctld Allbery uuencode getacl backend
+KDC appdefaults remctld Allbery uuencode getacl backend ACL's
=head1 SYNOPSIS
@@ -210,6 +210,15 @@ accidental lockout, but administrators can remove themselves from the
C<ADMIN> ACL and can leave only a non-functioning entry on the ACL. Use
caution when removing entries from the C<ADMIN> ACL.
+=item acl rename <id> <name>
+
+Renames the ACL identified by <id> to <name>. This changes the
+human-readable name, not the underlying numeric ID, so the ACL's
+associations with objects will be unchanged. The C<ADMIN> ACL may not be
+renamed. <id> may be either the current name or the numeric ID. <name>
+must not be all-numeric. To rename an ACL, the current user must be
+authorized by the C<ADMIN> ACL.
+
=item acl show <id>
Display the name, numeric ID, and entries of the ACL <id>.