diff options
author | Russ Allbery <rra@stanford.edu> | 2010-08-25 18:01:37 -0700 |
---|---|---|
committer | Russ Allbery <rra@stanford.edu> | 2010-08-25 18:01:37 -0700 |
commit | e91c0b93355b28617f7c0d756026856762ece242 (patch) | |
tree | 35c4fb8ab35ec5b8d140a0b7e869848a1fac39a9 /client | |
parent | 602ff7584d3668c36b1bf5fd43988e6f45eceb48 (diff) |
Imported Upstream version 0.12
Diffstat (limited to 'client')
-rw-r--r-- | client/file.c | 25 | ||||
-rw-r--r-- | client/internal.h | 46 | ||||
-rw-r--r-- | client/keytab.c | 175 | ||||
-rw-r--r-- | client/options.c | 71 | ||||
-rw-r--r-- | client/wallet-rekey.1 | 271 | ||||
-rw-r--r-- | client/wallet-rekey.c | 147 | ||||
-rw-r--r-- | client/wallet-rekey.pod | 165 | ||||
-rw-r--r-- | client/wallet.1 | 12 | ||||
-rw-r--r-- | client/wallet.c | 87 | ||||
-rw-r--r-- | client/wallet.pod | 11 |
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>. |