From b10beb347238b153af8aa544fb276485b34e970e Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Sat, 5 Jan 2008 00:01:54 +0000 Subject: 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. --- Makefile.am | 5 +- NEWS | 4 ++ README | 18 ++++-- TODO | 25 +++++--- client/error.c | 121 +++++++++++++++++++++++++++++++++++ client/internal.h | 30 ++++++--- client/keytab.c | 14 ++-- client/srvtab.c | 87 ++++--------------------- client/wallet.c | 117 ++++++++++++++++++++++++++++------ client/wallet.pod | 67 ++++++++++++++++++-- configure.ac | 27 ++++---- tests/client/basic-t.in | 165 ++++++++++++++++++++---------------------------- tests/data/README | 7 +- tests/libtest.sh | 82 ++++++++++++++++++++++++ 14 files changed, 525 insertions(+), 244 deletions(-) create mode 100644 client/error.c create mode 100644 tests/libtest.sh 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= + ./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 +** Copyright 2006, 2007, 2008 +** Board of Trustees, Leland Stanford Jr. University +** +** See LICENSE for licensing terms. +*/ + +#include + +#include +#include +#include + +#include +#if !defined(HAVE_KRB5_GET_ERROR_MESSAGE) && !defined(HAVE_KRB5_GET_ERR_TEXT) +# if defined(HAVE_IBM_SVC_KRB5_SVC_H) +# include +# elif defined(HAVE_ET_COM_ERR_H) +# include +# else +# include +# endif +#endif + +#include +#include + +/* 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 -** 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 #include + #include /* 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 -** 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 -** 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 -** 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 #include +#include #include #include #include +/* 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] [ ...]\n\ @@ -44,6 +65,58 @@ 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. @@ -51,12 +124,11 @@ usage(int status) 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) 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; see L below. =item B<-f> I @@ -76,6 +77,7 @@ will be destroyed. The service principal of the wallet server. The default is to use the C principal for the wallet server. The principal chosen must match one of the keys in the keytab used by B on the wallet server. +This option can also be set in F; see L below. =item B<-h> @@ -85,7 +87,8 @@ commands are ignored. =item B<-p> I 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; see +L below. =item B<-S> I @@ -100,8 +103,9 @@ L. =item B<-s> I -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. See L below. =item B<-v> @@ -352,6 +356,61 @@ correctly. =back +=head1 CONFIGURATION + +B can optionally be configured in the system F. It +will read the default F file for the Kerberos libraries with +which it was compiled. To set an option, put the option in the +[appdefaults] section. B will look for options either at the top +level of the [appdefaults] section or in a subsection named C, +inside or outside of a section for the realm. For example, the following +fragment of a F file would set the default port to 4373 and the +default server to C. It would also set the principal +to C 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 principal for the wallet server. The principal chosen must match +one of the keys in the keytab used by B 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. + +=item wallet_type + +The command prefix (remctl type) to use. Normally this is an internal +implementation detail and the default (C) 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 -# 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 </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 +# 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 +} -- cgit v1.2.3