diff options
author | Russ Allbery <rra@stanford.edu> | 2008-01-05 00:01:54 +0000 |
---|---|---|
committer | Russ Allbery <rra@stanford.edu> | 2008-01-05 00:01:54 +0000 |
commit | b10beb347238b153af8aa544fb276485b34e970e (patch) | |
tree | 4105c927be0912b2fa9f479a1aaf785091ff8f64 /client | |
parent | a67ad3fc36765f4b948a3e9c941318ff8931a11d (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.c | 121 | ||||
-rw-r--r-- | client/internal.h | 30 | ||||
-rw-r--r-- | client/keytab.c | 14 | ||||
-rw-r--r-- | client/srvtab.c | 87 | ||||
-rw-r--r-- | client/wallet.c | 117 | ||||
-rw-r--r-- | client/wallet.pod | 67 |
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) |