summaryrefslogtreecommitdiff
path: root/client
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 /client
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.
Diffstat (limited to 'client')
-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
6 files changed, 323 insertions, 113 deletions
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)