aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRuss Allbery <rra@stanford.edu>2008-01-05 00:01:54 +0000
committerRuss Allbery <rra@stanford.edu>2008-01-05 00:01:54 +0000
commitb10beb347238b153af8aa544fb276485b34e970e (patch)
tree4105c927be0912b2fa9f479a1aaf785091ff8f64
parenta67ad3fc36765f4b948a3e9c941318ff8931a11d (diff)
The wallet client can now get the server, port, principal, and remctl
type from krb5.conf as well as from compile-time defaults and command-line options.
-rw-r--r--Makefile.am5
-rw-r--r--NEWS4
-rw-r--r--README18
-rw-r--r--TODO25
-rw-r--r--client/error.c121
-rw-r--r--client/internal.h30
-rw-r--r--client/keytab.c14
-rw-r--r--client/srvtab.c87
-rw-r--r--client/wallet.c117
-rw-r--r--client/wallet.pod67
-rw-r--r--configure.ac27
-rw-r--r--tests/client/basic-t.in165
-rw-r--r--tests/data/README7
-rw-r--r--tests/libtest.sh82
14 files changed, 525 insertions, 244 deletions
diff --git a/Makefile.am b/Makefile.am
index c5a8f6c..fdbf86d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -32,8 +32,9 @@ util_libutil_a_SOURCES = util/concat.c util/messages.c util/util.h \
bin_PROGRAMS = client/wallet
dist_sbin_SCRIPTS = server/keytab-backend server/wallet-backend
-client_wallet_SOURCES = client/file.c client/internal.h client/keytab.c \
- client/remctl.c client/srvtab.c client/wallet.c system.h
+client_wallet_SOURCES = client/error.c client/file.c client/internal.h \
+ client/keytab.c client/remctl.c client/srvtab.c client/wallet.c \
+ system.h
client_wallet_CPPFLAGS = $(REMCTL_CPPFLAGS)
client_wallet_LDFLAGS = $(REMCTL_LDFLAGS)
client_wallet_LDADD = util/libutil.a portable/libportable.a $(REMCTL_LIBS) \
diff --git a/NEWS b/NEWS
index d1242aa..49277ad 100644
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,10 @@
wallet 0.6 (unreleased)
+ The wallet client can now get the server, port, principal, and remctl
+ type from krb5.conf as well as from compile-time defaults and
+ command-line options.
+
The build system now probes for GSS-API, Kerberos v5 and v4, and AFS
libraries as necessary rather than hard-coding libraries. Building
on systems without strong shared library dependencies and building
diff --git a/README b/README
index 6b95e16..13d2b89 100644
--- a/README
+++ b/README
@@ -136,14 +136,18 @@ BUILD AND INSTALLATION
You can build and install wallet with the standard commands:
- ./configure --with-wallet-server=<server>
+ ./configure
make
make install
The last step will probably have to be done as root. Currently, this
- always installs both the client and the server. You must specify the
- default wallet server at configure time unless you're at Stanford. This
- will be replaced with real client configuration in a later release.
+ always installs both the client and the server.
+
+ You can pass the --with-wallet-server and --with-wallet-port options to
+ configure to compile in a default wallet server and port. If no port is
+ set, the remctl default port is used. If no server is set, the server
+ must be specified either in krb5.conf configuration or on the wallet
+ command line or the client will exit with an error.
By default, wallet installs itself under /usr/local except for the
server Perl modules, which are installed into whatever default site
@@ -250,9 +254,9 @@ CONFIGURATION
configuration on your KDC if you want unchanging flag support, and set
up a srvtab if you want AFS kaserver synchronization support.
- The client currently has no configuration options and hard-codes the
- wallet server and port at compile time. This will change in a future
- release.
+ The wallet client supports reading configuration settings from the
+ system krb5.conf file. For more information, see the CONFIGURATION
+ section of the wallet client man page (man wallet).
THANKS
diff --git a/TODO b/TODO
index 6d8ac9b..55f7e27 100644
--- a/TODO
+++ b/TODO
@@ -41,9 +41,6 @@ Release 1.0:
-u option similar to leland_srvtab. Needs good error messages on
Kerberos failures.
-* The wallet client should read configuration information from krb5.conf,
- and possibly from some other configuration as well.
-
* Error messages from ACL operations should refer to the ACLs by name
instead of by ID.
@@ -101,6 +98,17 @@ Future work:
* Add a comment field for objects that can be set by the owner.
+* The keytab backend currently only supports MIT Kerberos. Add support
+ for Heimdal. This should probably be done by writing a separate class
+ that handles the kadmin operations that can be subclassed and that
+ dynamically chooses its implementation based on run-time configuration.
+
+* When reading configuration from krb5.conf, we should first try to
+ determine our principal from any existing K5 ticket cache (after
+ obtaining tickets if -u was given) and extract the realm from that
+ principal, using it as the default realm when reading configuration
+ information.
+
* Implement an ssh keypair wallet object. The server can run ssh-keygen
to generate a public/private key pair and return both to the client,
which would split them apart. Used primarily for host keys. May need a
@@ -123,9 +131,6 @@ Future work:
* Add support for enforcing a naming policy through another policy
function.
-* The keytab backend currently only supports MIT Kerberos. Add support
- for Heimdal.
-
* Add readline support to the wallet client to make it easier to issue
multiple commands.
@@ -152,10 +157,6 @@ Future work:
* The Wallet::Config class is very ugly and could use some better internal
API to reference the variables in it.
-* Consider using Class::Accessor to get rid of the scaffolding code to
- access object data, and a Wallet::Base class to handle things like the
- error() method common to many classes.
-
* Use Class::DBI and Class::Trigger to handle the data access layer rather
than writing SQL directly, and implement the logging requirements with
triggers rather than explicit SQL. This may also replace
@@ -163,5 +164,9 @@ Future work:
May or may not be good ideas:
+* Consider using Class::Accessor to get rid of the scaffolding code to
+ access object data, and a Wallet::Base class to handle things like the
+ error() method common to many classes.
+
* Remove the hard-coded ADMIN ACL in the server with something more
configurable, perhaps a global ACL table or something.
diff --git a/client/error.c b/client/error.c
new file mode 100644
index 0000000..e95b284
--- /dev/null
+++ b/client/error.c
@@ -0,0 +1,121 @@
+/* $Id$
+**
+** Error handling for the wallet client.
+**
+** Provides versions of die and warn that take a Kerberos context and a
+** Kerberos error code and append the Kerberos error message to the provided
+** formatted message.
+**
+** Written by Russ Allbery <rra@stanford.edu>
+** Copyright 2006, 2007, 2008
+** Board of Trustees, Leland Stanford Jr. University
+**
+** See LICENSE for licensing terms.
+*/
+
+#include <config.h>
+
+#include <krb5.h>
+#include <stdio.h>
+#include <sys/types.h>
+
+#include <krb5.h>
+#if !defined(HAVE_KRB5_GET_ERROR_MESSAGE) && !defined(HAVE_KRB5_GET_ERR_TEXT)
+# if defined(HAVE_IBM_SVC_KRB5_SVC_H)
+# include <ibm_svc/krb5_svc.h>
+# elif defined(HAVE_ET_COM_ERR_H)
+# include <et/com_err.h>
+# else
+# include <com_err.h>
+# endif
+#endif
+
+#include <client/internal.h>
+#include <util/util.h>
+
+/* This string is returned for unknown error messages. We use a static
+ variable so that we can be sure not to free it. */
+static const char error_unknown[] = "unknown error";
+
+
+/*
+** Given a Kerberos error code, return the corresponding error. Prefer the
+** Kerberos interface if available since it will provide context-specific
+** error information, whereas the error_message() call will only provide a
+** fixed message.
+*/
+static const char *
+get_error(krb5_context ctx, krb5_error_code code)
+{
+ const char *msg = NULL;
+
+#if defined(HAVE_KRB5_GET_ERROR_MESSAGE)
+ msg = krb5_get_error_message(ctx, code);
+#elif defined(HAVE_KRB5_GET_ERR_TEXT)
+ msg = krb5_get_err_text(ctx, code);
+#elif defined(HAVE_KRB5_SVC_GET_MSG)
+ krb5_svc_get_msg(code, &msg);
+#else
+ msg = error_message(code);
+#endif
+ if (msg == NULL)
+ return error_unknown;
+ else
+ return msg;
+}
+
+
+/*
+** Free an error string if necessary.
+*/
+static void
+free_error(krb5_context ctx, const char *msg)
+{
+ if (msg == error_unknown)
+ return;
+#if defined(HAVE_KRB5_FREE_ERROR_MESSAGE)
+ krb5_free_error_message(ctx, msg);
+#elif defined(HAVE_KRB5_SVC_GET_MSG)
+ krb5_free_string((char *) msg);
+#endif
+}
+
+
+/*
+** Report a Kerberos error and exit.
+*/
+void
+die_krb5(krb5_context ctx, krb5_error_code code, const char *format, ...)
+{
+ const char *k5_msg = NULL;
+ char *message;
+ va_list args;
+
+ k5_msg = get_error(ctx, code);
+ va_start(args, format);
+ if (xasprintf(&message, format, args) < 0)
+ die("internal error: unable to format error message");
+ va_end(args);
+ die("%s: %s\n", message, k5_msg);
+}
+
+
+/*
+** Report a Kerberos error.
+*/
+void
+warn_krb5(krb5_context ctx, krb5_error_code code, const char *format, ...)
+{
+ const char *k5_msg = NULL;
+ char *message;
+ va_list args;
+
+ k5_msg = get_error(ctx, code);
+ va_start(args, format);
+ if (xasprintf(&message, format, args) < 0)
+ die("internal error: unable to format error message");
+ va_end(args);
+ warn("%s: %s\n", message, k5_msg);
+ free(message);
+ free_error(ctx, k5_msg);
+}
diff --git a/client/internal.h b/client/internal.h
index 6f4a991..1dcb608 100644
--- a/client/internal.h
+++ b/client/internal.h
@@ -3,7 +3,7 @@
** Internal support functions for the wallet client.
**
** Written by Russ Allbery <rra@stanford.edu>
-** Copyright 2007 Board of Trustees, Leland Stanford Jr. University
+** Copyright 2007, 2008 Board of Trustees, Leland Stanford Jr. University
**
** See LICENSE for licensing terms.
*/
@@ -11,7 +11,9 @@
#ifndef CLIENT_INTERNAL_H
#define CLIENT_INTERNAL_H 1
+#include <krb5.h>
#include <sys/types.h>
+
#include <util/util.h>
/* Forward declarations to avoid unnecessary includes. */
@@ -35,23 +37,31 @@ BEGIN_DECLS
int run_command(struct remctl *, const char **command, char **data,
size_t *length);
-/* Given a remctl object, the type for the wallet interface, 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. If srvtab is not NULL, write
- a srvtab based on the keytab after a successful download. */
-int get_keytab(struct remctl *, 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, 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. If srvtab
+ is not NULL, write a srvtab based on the keytab after a successful
+ download. */
+int get_keytab(struct remctl *, krb5_context, const char *type,
+ const char *name, const char *file, const char *srvtab);
/* 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. */
void write_file(const char *name, const void *data, size_t length);
-/* Given a srvtab file, the Kerberos v5 principal, and the keytab file, write
- a srvtab file for the corresponding Kerberos v4 principal. */
-void write_srvtab(const char *srvtab, const char *principal,
+/* Given a Kerberos context, a srvtab file, the Kerberos v5 principal, and the
+ keytab file, write a srvtab file for the corresponding Kerberos v4
+ principal. */
+void write_srvtab(krb5_context, const char *srvtab, const char *principal,
const char *keytab);
+/* Versions of die and warn that report Kerberos errors. */
+void die_krb5(krb5_context, krb5_error_code, const char *, ...)
+ __attribute__((__noreturn__, __format__(printf, 3, 4)));
+void warn_krb5(krb5_context, krb5_error_code, const char *, ...)
+ __attribute__((__format__(printf, 3, 4)));
+
END_DECLS
#endif /* !CLIENT_INTERNAL_H */
diff --git a/client/keytab.c b/client/keytab.c
index 1137c45..04b34c9 100644
--- a/client/keytab.c
+++ b/client/keytab.c
@@ -3,7 +3,7 @@
** Implementation of keytab handling for the wallet client.
**
** Written by Russ Allbery <rra@stanford.edu>
-** Copyright 2007 Board of Trustees, Leland Stanford Jr. University
+** Copyright 2007, 2008 Board of Trustees, Leland Stanford Jr. University
**
** See LICENSE for licensing terms.
*/
@@ -51,13 +51,13 @@ set_sync(struct remctl *r, const char *type, const char *name)
/*
-** Given a remctl object, 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.
+** 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.
*/
int
-get_keytab(struct remctl *r, const char *type, const char *name,
- const char *file, const char *srvtab)
+get_keytab(struct remctl *r, krb5_context ctx, const char *type,
+ const char *name, const char *file, const char *srvtab)
{
const char *command[5];
char *data = NULL;
@@ -81,6 +81,6 @@ get_keytab(struct remctl *r, const char *type, const char *name,
}
write_file(file, data, length);
if (srvtab != NULL)
- write_srvtab(srvtab, name, file);
+ write_srvtab(ctx, srvtab, name, file);
return 0;
}
diff --git a/client/srvtab.c b/client/srvtab.c
index dd1cd58..b454720 100644
--- a/client/srvtab.c
+++ b/client/srvtab.c
@@ -3,7 +3,7 @@
** Implementation of srvtab handling for the wallet client.
**
** Written by Russ Allbery <rra@stanford.edu>
-** Copyright 2007 Board of Trustees, Leland Stanford Jr. University
+** Copyright 2007, 2008 Board of Trustees, Leland Stanford Jr. University
**
** See LICENSE for licensing terms.
*/
@@ -22,67 +22,12 @@
# define REALM_SZ 40
#endif
-#ifdef HAVE_KRB5_GET_ERROR_MESSAGE
-static const char *
-strerror_krb5(krb5_context ctx, krb5_error_code code)
-{
- const char *msg;
-
- msg = krb5_get_error_message(ctx, code);
- if (msg == NULL)
- return "unknown error";
- else
- return msg;
-}
-#elif HAVE_KRB5_GET_ERR_TEXT
-static const char *
-strerror_krb5(krb5_context ctx, krb5_error_code code)
-{
- return krb5_get_err_text(ctx, code);
-}
-#else /* !HAVE_KRB5_GET_ERROR_MESSAGE */
-static const char *
-strerror_krb5(krb5_context ctx UNUSED, krb5_error_code code)
-{
- return error_message(code);
-}
-#endif
-
-#ifdef HAVE_KRB5_FREE_ERROR_MESSAGE
-static void
-strerror_krb5_free(krb5_context ctx, const char *msg)
-{
- krb5_free_error_message(ctx, msg);
-}
-#else /* !HAVE_KRB5_FREE_ERROR_MESSAGE */
-static void
-strerror_krb5_free(krb5_context ctx UNUSED, const char *msg UNUSED)
-{
- return;
-}
-#endif /* !HAVE_KRB5_FREE_ERROR_MESSAGE */
-
/*
-** Report a Kerberos error and exit.
-*/
-static void
-die_krb5(krb5_context ctx, const char *message, krb5_error_code code)
-{
- const char *k5_msg = NULL;
-
- k5_msg = strerror_krb5(ctx, code);
- warn("%s: %s\n", message, k5_msg);
- strerror_krb5_free(ctx, k5_msg);
- exit(1);
-}
-
-
-/*
-** Given the srvtab file name, a Kerberos principal (as a string), and a
-** keytab file name, extract the des-cbc-crc key from that keytab and write
-** it to the newly created srvtab file as a srvtab. Convert the principal
-** from Kerberos v5 form to Kerberos v4 form.
+** Given the Kerberos context, srvtab file name, a Kerberos principal (as a
+** string), and a keytab file name, extract the des-cbc-crc key from that
+** keytab and write it to the newly created srvtab file as a srvtab. Convert
+** the principal from Kerberos v5 form to Kerberos v4 form.
**
** We always force the kvno to 0 for the srvtab. This works with how the
** wallet synchronizes keys, even though it's not particularly correct.
@@ -90,9 +35,9 @@ die_krb5(krb5_context ctx, const char *message, krb5_error_code code)
** On any failure, print an error message to standard error and then exit.
*/
void
-write_srvtab(const char *srvtab, const char *principal, const char *keytab)
+write_srvtab(krb5_context ctx, const char *srvtab, const char *principal,
+ const char *keytab)
{
- krb5_context ctx = NULL;
krb5_keytab kt;
krb5_principal princ;
krb5_keytab_entry entry;
@@ -104,28 +49,24 @@ write_srvtab(const char *srvtab, const char *principal, const char *keytab)
char data[ANAME_SZ + 1 + INST_SZ + 1 + REALM_SZ + 1 + 1 + 8];
/* Open the keytab and get the DES key. */
- ret = krb5_init_context(&ctx);
- if (ret != 0)
- die_krb5(ctx, "error creating Kerberos context", ret);
ret = krb5_parse_name(ctx, principal, &princ);
if (ret != 0)
- die_krb5(ctx, "error parsing Kerberos principal", ret);
+ die_krb5(ctx, ret, "error parsing Kerberos principal %s", principal);
ret = krb5_kt_resolve(ctx, keytab, &kt);
if (ret != 0)
- die_krb5(ctx, "error opening keytab", ret);
+ die_krb5(ctx, ret, "error opening keytab %s", keytab);
ret = krb5_kt_get_entry(ctx, kt, princ, 0, ENCTYPE_DES_CBC_CRC, &entry);
if (ret != 0)
- die_krb5(ctx, "error reading DES key from keytab", ret);
- if (entry.key.length != 8) {
- fprintf(stderr, "invalid DES key length in keytab\n");
- exit(1);
- }
+ die_krb5(ctx, ret, "error reading DES key from keytab %s", keytab);
+ if (entry.key.length != 8)
+ die("invalid DES key length in keytab");
krb5_kt_close(ctx, kt);
/* Convert the principal to a Kerberos v4 principal. */
ret = krb5_524_conv_principal(ctx, princ, aname, inst, realm);
if (ret != 0)
- die_krb5(ctx, "error converting principal to Kerberos v4", ret);
+ die_krb5(ctx, ret, "error converting principal %s to Kerberos v4",
+ principal);
/* Assemble the srvtab data. */
length = 0;
diff --git a/client/wallet.c b/client/wallet.c
index 92f2984..28d27e8 100644
--- a/client/wallet.c
+++ b/client/wallet.c
@@ -3,7 +3,8 @@
** The client program for the wallet system.
**
** Written by Russ Allbery <rra@stanford.edu>
-** Copyright 2006, 2007 Board of Trustees, Leland Stanford Jr. University
+** Copyright 2006, 2007, 2008
+** Board of Trustees, Leland Stanford Jr. University
**
** See LICENSE for licensing terms.
*/
@@ -12,11 +13,31 @@
#include <system.h>
#include <errno.h>
+#include <krb5.h>
#include <remctl.h>
#include <client/internal.h>
#include <util/util.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. */
+struct options {
+ char *type;
+ char *server;
+ char *principal;
+ 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\
@@ -45,18 +66,69 @@ 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;
+
+ 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);
+}
+
+
+/*
** 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, status;
const char **command;
- const char *type = "wallet";
- const char *server = WALLET_SERVER;
- const char *principal = NULL;
- unsigned short port = WALLET_PORT;
const char *file = NULL;
const char *srvtab = NULL;
struct remctl *r;
@@ -66,16 +138,22 @@ main(int argc, char *argv[])
/* 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");
+ set_defaults(ctx, &options);
+
while ((option = getopt(argc, argv, "c:f:k:hp:S:s:v")) != EOF) {
switch (option) {
case 'c':
- type = optarg;
+ options.type = optarg;
break;
case 'f':
file = optarg;
break;
case 'k':
- principal = optarg;
+ options.principal = optarg;
break;
case 'h':
usage(0);
@@ -85,13 +163,13 @@ main(int argc, char *argv[])
tmp = strtol(optarg, &end, 10);
if (tmp <= 0 || tmp > 65535 || *end != '\0')
die("invalid port number %s", optarg);
- port = tmp;
+ options.port = tmp;
break;
case 'S':
srvtab = optarg;
break;
case 's':
- server = optarg;
+ options.server = optarg;
break;
case 'v':
printf("%s\n", PACKAGE_STRING);
@@ -117,11 +195,16 @@ main(int argc, char *argv[])
die("-S option requires -f also be used");
}
+ /* 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");
+
/* Open a remctl connection. */
r = remctl_new();
if (r == NULL)
sysdie("cannot allocate memory");
- if (!remctl_open(r, server, port, principal))
+ if (!remctl_open(r, options.server, options.port, options.principal))
die("%s", remctl_error(r));
/* Most commands, we handle ourselves, but keytab get commands with -f are
@@ -129,20 +212,16 @@ main(int argc, char *argv[])
if (strcmp(argv[0], "get") == 0 && strcmp(argv[1], "keytab") == 0) {
if (argc > 3)
die("too many arguments");
- status = get_keytab(r, type, argv[2], file, srvtab);
- remctl_close(r);
- exit(status);
+ status = get_keytab(r, ctx, options.type, argv[2], file, srvtab);
} else {
command = xmalloc(sizeof(char *) * (argc + 2));
- command[0] = type;
+ command[0] = options.type;
for (i = 0; i < argc; i++)
command[i + 1] = argv[i];
command[argc + 1] = NULL;
status = run_command(r, command, NULL, NULL);
- remctl_close(r);
- exit(status);
}
-
- /* This should never be reached. */
- die("invalid return from wallet server");
+ remctl_close(r);
+ krb5_free_context(ctx);
+ exit(status);
}
diff --git a/client/wallet.pod b/client/wallet.pod
index 8991123..c216cb7 100644
--- a/client/wallet.pod
+++ b/client/wallet.pod
@@ -62,7 +62,8 @@ protocol to talk to the wallet server.
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.
+version of the wallet code on the server. This option can also be set in
+F<krb5.conf>; see L<CONFIGURATION> below.
=item B<-f> I<output>
@@ -76,6 +77,7 @@ will be destroyed.
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>
@@ -85,7 +87,8 @@ commands are ignored.
=item B<-p> I<port>
The port to connect to on the wallet server. The default is the default
-remctl port.
+remctl port. This option can also be set in F<krb5.conf>; see
+L<CONFIGURATION> below.
=item B<-S> I<srvtab>
@@ -100,8 +103,9 @@ L<ATTRIBUTES>.
=item B<-s> I<server>
-The wallet server to connect to. The default is a hard-coded server value
-determined at configure time when compiling the wallet client.
+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<-v>
@@ -352,6 +356,61 @@ correctly.
=back
+=head1 CONFIGURATION
+
+B<wallet> 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> will look for options either at the top
+level of the [appdefaults] section or in a subsection named C<wallet>,
+inside or outside of a section for the realm. 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>. It would also set the principal
+to C<wallet/example.org@EXAMPLE.NET> only if the local default realm is
+EXAMPLE.NET:
+
+ [appdefaults]
+ wallet_port = 4373
+ wallet = {
+ wallet_server = wallet.example.org
+ EXAMPLE.NET = {
+ wallet_principal = wallet/example.org@EXAMPLE.NET
+ }
+ }
+
+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
krb5.conf(5), remctl(1), remctld(8)
diff --git a/configure.ac b/configure.ac
index dd4b502..cef61eb 100644
--- a/configure.ac
+++ b/configure.ac
@@ -29,18 +29,16 @@ AC_CHECK_FUNCS([setrlimit])
AC_REPLACE_FUNCS([asprintf])
AC_ARG_WITH([wallet-server],
- AC_HELP_STRING([--with-wallet-server=HOST], [Default wallet server]),
- [if test x"$withval" != xno ; then
- AC_DEFINE_UNQUOTED([WALLET_SERVER], ["$withval"],
- [Define to the default server host name.])
- fi])
+ [AC_HELP_STRING([--with-wallet-server=HOST], [Default wallet server])],
+ [AS_IF([test x"$withval" != xno && test x"$withval" != xyes],
+ [AC_DEFINE_UNQUOTED([WALLET_SERVER], ["$withval"],
+ [Define to the default server host name.])])])
AC_ARG_WITH([wallet-port],
- AC_HELP_STRING([--with-wallet-port=PORT],
- [Default wallet server port]),
- [if test x"$withval" != xno ; then
- AC_DEFINE_UNQUOTED([WALLET_PORT], [$withval],
- [Define to the default server port.])
- fi])
+ [AC_HELP_STRING([--with-wallet-port=PORT],
+ [Default wallet server port])],
+ [AS_IF([test x"$withval" != xno && test x"$withval" != xyes],
+ [AC_DEFINE_UNQUOTED([WALLET_PORT], [$withval],
+ [Define to the default server port.])])])
RRA_LIB_REMCTL
RRA_LIB_KRB5
@@ -54,10 +52,9 @@ AM_CONDITIONAL([AFS], [test x"$rra_afs" = xtrue])
AC_ARG_VAR([REMCTLD], [Path to the remctld binary])
AC_PATH_PROG([REMCTLD], [remctld], , [$PATH:/usr/sbin:/usr/local/sbin])
-if test x"$REMCTLD" != x ; then
- AC_DEFINE_UNQUOTED([PATH_REMCTLD], ["$REMCTLD"],
- [Define to the full path to remctld to run remctl tests.])
-fi
+AS_IF([test x"$REMCTLD" != x],
+ [AC_DEFINE_UNQUOTED([PATH_REMCTLD], ["$REMCTLD"],
+ [Define to the full path to remctld to run remctl tests.])])
dnl Needed to get prototypes for functions like asprintf on Linux.
AC_DEFINE([_GNU_SOURCE], [1], [Define to 1 on Linux to get full prototypes.])
diff --git a/tests/client/basic-t.in b/tests/client/basic-t.in
index 8f7632c..4a4a559 100644
--- a/tests/client/basic-t.in
+++ b/tests/client/basic-t.in
@@ -4,89 +4,38 @@
# Test suite for the wallet command-line client.
#
# Written by Russ Allbery <rra@stanford.edu>
-# Copyright 2006, 2007 Board of Trustees, Leland Stanford Jr. University
+# Copyright 2006, 2007, 2008 Board of Trustees, Leland Stanford Jr. University
+#
# See LICENSE for licensing terms.
-# The count starts at 1 and is updated each time ok is printed. printcount
-# takes "ok" or "not ok".
-count=1
-printcount () {
- echo "$1 $count $2"
- count=`expr $count + 1`
-}
-
-# Run a program expected to succeed, and print ok if it does and produces
-# the correct output.
-runsuccess () {
- w_output="$1"
- shift
- principal=`cat data/test.principal`
- output=`$wallet -k "$principal" -p 14444 -s localhost "$@" 2>&1`
- status=$?
- if [ $status = 0 ] && [ x"$output" = x"$w_output" ] ; then
- printcount "ok"
- else
- printcount "not ok"
- echo " saw: $output"
- echo " not: $w_output"
- fi
-}
-
-# Run a program expected to fail and make sure it fails with the correct
-# exit status and the correct failure message. Strip the second colon and
-# everything after it off the error message since it's system-specific.
-runfailure () {
- w_status="$1"
- shift
- w_output="$1"
- shift
- principal=`cat data/test.principal`
- output=`$wallet -k "$principal" -p 14444 -s localhost "$@" 2>&1`
- status=$?
- output=`echo "$output" | sed 's/\(:[^:]*\):.*/\1/'`
- if [ $status = $w_status ] && [ x"$output" = x"$w_output" ] ; then
- printcount "ok"
- else
- printcount "not ok"
- echo " saw: ($status) $output"
- echo " not: ($w_status) $w_output"
- fi
-}
+# Load the test library.
+. "@abs_top_srcdir@/tests/libtest.sh"
# Print the number of tests.
-echo 20
+total=22
+count=1
+echo "$total"
# Find the client program.
-if [ -f ../data/test.keytab ] ; then
- cd ..
-else
- if [ -f tests/data/test.keytab ] ; then
- cd tests
- fi
-fi
-if [ ! -f data/test.keytab ] || [ -z "@REMCTLD@" ] ; then
- for n in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ; do
- echo ok $n \# skip -- no Kerberos configuration
- done
+chdir_data '../client/wallet'
+if [ ! -f 'data/test.keytab' ] || [ -z '@REMCTLD@' ] ; then
+ skip 1 "$total" 'no Kerberos configuration'
exit 0
fi
-wallet=../client/wallet
-if [ ! -x "$wallet" ] ; then
- echo 'Cannot locate wallet client binary' >&2
- exit 1
-fi
+wallet='../client/wallet'
# Start the remctld daemon and wait for it to start.
+principal=`cat data/test.principal`
rm -f data/pid
-( @REMCTLD@ -m -p 14444 -s `cat data/test.principal` -P data/pid \
- -f data/wallet.conf -S -F -k data/test.keytab &)
+( @REMCTLD@ -m -p 14373 -s "$principal" -P data/pid -f data/wallet.conf \
+ -S -F -k data/test.keytab &)
KRB5CCNAME=data/test.cache; export KRB5CCNAME
-kinit -k -t data/test.keytab `cat data/test.principal` > /dev/null 2>&1
+kinit -k -t data/test.keytab "$principal" > /dev/null 2>&1
if [ $? != 0 ] ; then
- kinit -t data/test.keytab `cat data/test.principal` > /dev/null 2>&1
+ kinit -t data/test.keytab "$principal" > /dev/null 2>&1
fi
if [ $? != 0 ] ; then
- kinit -k -K data/test.keytab `cat data/test.principal` > /dev/null 2>&1
+ kinit -k -K data/test.keytab "$principal" > /dev/null 2>&1
fi
if [ $? != 0 ] ; then
echo 'Unable to obtain Kerberos tickets' >&2
@@ -100,9 +49,10 @@ fi
# We need a modified krb5.conf file for the srvtab test to work, since we need
# to add a v4_realm setting for the test-k5.stanford.edu realm that the keytab
-# is for.
+# is for. Despite all the Stanford hard-coding, this test isn't
+# Stanford-specific. It just matches the data files shipped with the package.
krb5conf=
-for p in /etc/krb5.conf /usr/local/etc/krb5.conf ; do
+for p in /etc/krb5.conf /usr/local/etc/krb5.conf data/krb5.conf ; do
if [ -r "$p" ] ; then
krb5conf="$p"
sed -e '/^ *test-k5.stanford.edu =/,/}/d' \
@@ -120,12 +70,17 @@ EOF
break
fi
done
+if [ -z "$krb5conf" ] ; then
+ echo 'No krb5.conf found -- put one in tests/data/krb5.conf' >&2
+ exit 1
+fi
# Make sure everything's clean.
rm -f keytab keytab.bak srvtab srvtab.bak sync-kaserver
-# Now, we can finally run our tests.
-runsuccess "" -c fake-wallet get keytab -f keytab service/fake-test
+# Now, we can finally run our tests. First, basic operations.
+runsuccess "" "$wallet" -k "$principal" -p 14373 -s localhost -c fake-wallet \
+ get keytab -f keytab service/fake-test
if cmp keytab data/fake-data >/dev/null 2>&1 ; then
printcount "ok"
else
@@ -136,7 +91,8 @@ if [ -f keytab.bak ] || [ -f keytab.new ] ; then
else
printcount "ok"
fi
-runsuccess "" -c fake-wallet get keytab -f keytab service/fake-test
+runsuccess "" "$wallet" -k "$principal" -p 14373 -s localhost -c fake-wallet \
+ get keytab -f keytab service/fake-test
if cmp keytab data/fake-data >/dev/null 2>&1 ; then
printcount "ok"
else
@@ -147,7 +103,28 @@ if [ -f keytab.new ] || [ ! -f keytab.bak ] ; then
else
printcount "ok"
fi
-runsuccess "" -c fake-wallet get keytab -f keytab -S srvtab service/fake-srvtab
+
+# Now, append configuration to krb5.conf and test getting configuration from
+# there.
+cat >> krb5.conf <<EOF
+
+[appdefaults]
+ wallet_server = localhost
+ wallet = {
+ wallet_port = 14373
+ wallet_type = fake-wallet
+ wallet_principal = $principal
+ }
+EOF
+runsuccess "" "$wallet" get keytab -f keytab service/fake-test
+if cmp keytab data/fake-data >/dev/null 2>&1 ; then
+ printcount "ok"
+else
+ printcount "not ok"
+fi
+
+# Test srvtab support.
+runsuccess "" "$wallet" get keytab -f keytab -S srvtab service/fake-srvtab
if cmp keytab data/fake-keytab >/dev/null 2>&1 ; then
printcount "ok"
rm keytab
@@ -165,7 +142,7 @@ if [ -f sync-kaserver ] ; then
else
printcount "not ok"
fi
-runsuccess "" -c fake-wallet get keytab -f keytab -S srvtab service/fake-srvtab
+runsuccess "" "$wallet" get keytab -f keytab -S srvtab service/fake-srvtab
if cmp keytab data/fake-keytab >/dev/null 2>&1 ; then
printcount "ok"
rm keytab
@@ -178,36 +155,32 @@ if [ -f sync-kaserver ] ; then
else
printcount "not ok"
fi
-if [ -n "$krb5conf" ] ; then
- if cmp srvtab data/fake-srvtab >/dev/null 2>&1 ; then
- printcount "ok"
- rm srvtab
- else
- printcount "not ok"
- fi
- if cmp srvtab.bak data/fake-srvtab >/dev/null 2>&1 ; then
- printcount "ok"
- rm srvtab.bak
- else
- printcount "not ok"
- fi
- KRB5_CONFIG=
- rm krb5.conf
+if cmp srvtab data/fake-srvtab >/dev/null 2>&1 ; then
+ printcount "ok"
+ rm srvtab
+else
+ printcount "not ok"
+fi
+if cmp srvtab.bak data/fake-srvtab >/dev/null 2>&1 ; then
+ printcount "ok"
+ rm srvtab.bak
else
- printcount "ok # skip cannot find krb5.conf"
+ printcount "not ok"
fi
runsuccess "Some stuff about service/fake-test" \
- -c fake-wallet show keytab service/fake-test
+ "$wallet" show keytab service/fake-test
runfailure 1 "wallet: Unknown object type srvtab" \
- -c fake-wallet get srvtab service/fake-test
+ "$wallet" get srvtab service/fake-test
runfailure 1 "wallet: Unknown keytab service/unknown" \
- -c fake-wallet show keytab service/unknown
+ "$wallet" show keytab service/unknown
runfailure 1 "wallet: Unknown keytab service/unknown" \
- -c fake-wallet get keytab service/unknown
+ "$wallet" get keytab service/unknown
runsuccess "Expiration date of service/fake-test" \
- -c fake-wallet expires keytab service/fake-test
+ "$wallet" expires keytab service/fake-test
# Clean up.
+KRB5_CONFIG=
+rm data/krb5.conf
rm -f data/test.cache
if [ -f data/pid ] ; then
kill `cat data/pid`
diff --git a/tests/data/README b/tests/data/README
index 890c4dc..0f3c88c 100644
--- a/tests/data/README
+++ b/tests/data/README
@@ -8,10 +8,15 @@ to the key in the keytab on a single line ending with a newline.
The presence of these two files will enable the tests that actually do
GSS-API authentication.
+If your krb5.conf file is not in /etc or /usr/local/etc, put a copy of
+your krb5.conf file in this directory. The tests need to generate a
+modified copy in order to test some behavior.
+
If you are building in a different directory tree than the source tree,
don't put the files in this directory. Instead, after running configure,
you will have an empty tests/data directory in your build tree. Put the
-test.keytab and test.principal files in that directory instead.
+test.keytab, test.principal, and krb5.conf (if necessary) files in that
+directory instead.
Note that to successfully run much of the test suite, you will need to have
remctld installed on the system running the tests.
diff --git a/tests/libtest.sh b/tests/libtest.sh
new file mode 100644
index 0000000..ed46d0e
--- /dev/null
+++ b/tests/libtest.sh
@@ -0,0 +1,82 @@
+# $Id$
+#
+# Shell function library for test cases.
+#
+# Written by Russ Allbery <rra@stanford.edu>
+# Copyright 2006, 2007, 2008 Board of Trustees, Leland Stanford Jr. University
+#
+# See LICENSE for licensing terms.
+
+# The count starts at 1 and is updated each time ok is printed. printcount
+# takes "ok" or "not ok".
+count=1
+printcount () {
+ echo "$1 $count $2"
+ count=`expr $count + 1`
+}
+
+# Run a program expected to succeed, and print ok if it does and produces
+# the correct output. Takes the output as the first argument, the command to
+# run as the second argument, and then all subsequent arguments are arguments
+# to the command.
+runsuccess () {
+ w_output="$1"
+ shift
+ output=`"$@" 2>&1`
+ status=$?
+ if [ $status = 0 ] && [ x"$output" = x"$w_output" ] ; then
+ printcount 'ok'
+ else
+ printcount 'not ok'
+ echo " saw: $output"
+ echo " not: $w_output"
+ fi
+}
+
+# Run a program expected to fail and make sure it fails with the correct exit
+# status and the correct failure message. Takes the expected status, the
+# expected output, and then everything else is the command and arguments.
+# Strip the second colon and everything after it off the error message since
+# it's system-specific.
+runfailure () {
+ w_status="$1"
+ shift
+ w_output="$1"
+ shift
+ output=`"$@" 2>&1`
+ status=$?
+ output=`echo "$output" | sed 's/\(:[^:]*\):.*/\1/'`
+ if [ $status = $w_status ] && [ x"$output" = x"$w_output" ] ; then
+ printcount 'ok'
+ else
+ printcount 'not ok'
+ echo " saw: ($status) $output"
+ echo " not: ($w_status) $w_output"
+ fi
+}
+
+# Skip tests from $1 to $2 inclusive with reason $3.
+skip () {
+ n="$1"
+ while [ "$n" -le "$2" ] ; do
+ echo ok "$n # skip $3"
+ n=`expr "$n" + 1`
+ done
+}
+
+# Given a file name or relative file path, try to cd to the correct directory
+# so that the relative file path is valid. Exits with an error if that isn't
+# possible.
+chdir_data () {
+ if [ -f "../$1" ] ; then
+ cd ..
+ else
+ if [ -f "tests/$1" ] ; then
+ cd tests
+ fi
+ fi
+ if [ ! -f "$1" ] ; then
+ echo "Cannot locate $1" >&2
+ exit 1
+ fi
+}