diff options
author | Russ Allbery <rra@stanford.edu> | 2007-09-27 03:22:46 +0000 |
---|---|---|
committer | Russ Allbery <rra@stanford.edu> | 2007-09-27 03:22:46 +0000 |
commit | e0f6e1222ede4a7545ca995a8aacaae0b591cb9c (patch) | |
tree | 2af9d140d13ee321c6fdb81c4444ca2e0e93c4a3 /client | |
parent | 7ec47028dbfe6df70d4c07e9546ae1680cf4e91f (diff) |
Initial cut at srvtab support in the wallet client. This still requires
additional work and cleanup, particularly support for the sync attribute.
Diffstat (limited to 'client')
-rw-r--r-- | client/internal.h | 55 | ||||
-rw-r--r-- | client/srvtab.c | 169 | ||||
-rw-r--r-- | client/wallet.c | 18 | ||||
-rw-r--r-- | client/wallet.pod | 85 |
4 files changed, 303 insertions, 24 deletions
diff --git a/client/internal.h b/client/internal.h new file mode 100644 index 0000000..834ec57 --- /dev/null +++ b/client/internal.h @@ -0,0 +1,55 @@ +/* $Id$ +** +** Internal support functions for the wallet client. +** +** Written by Russ Allbery <rra@stanford.edu> +** Copyright 2007 Board of Trustees, Leland Stanford Jr. University +** +** See README for licensing terms. +*/ + +#ifndef CLIENT_INTERNAL_H +#define CLIENT_INTERNAL_H 1 + +/* __attribute__ is available in gcc 2.5 and later, but only with gcc 2.7 + could you use the __format__ form of the attributes, which is what we use + (to avoid confusion with other macros). */ +#ifndef __attribute__ +# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7) +# define __attribute__(spec) /* empty */ +# endif +#endif + +/* Used for unused parameters to silence gcc warnings. */ +#define UNUSED __attribute__((__unused__)) + +/* BEGIN_DECLS is used at the beginning of declarations so that C++ + compilers don't mangle their names. END_DECLS is used at the end. */ +#undef BEGIN_DECLS +#undef END_DECLS +#ifdef __cplusplus +# define BEGIN_DECLS extern "C" { +# define END_DECLS } +#else +# define BEGIN_DECLS /* empty */ +# define END_DECLS /* empty */ +#endif + +/* Temporary until we have some real configuration. */ +#ifndef SERVER +# define SERVER "wallet.stanford.edu" +#endif +#ifndef PORT +# define PORT 4444 +#endif + +BEGIN_DECLS + +/* 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, + const char *keytab); + +END_DECLS + +#endif /* !CLIENT_INTERNAL_H */ diff --git a/client/srvtab.c b/client/srvtab.c new file mode 100644 index 0000000..573840a --- /dev/null +++ b/client/srvtab.c @@ -0,0 +1,169 @@ +/* $Id$ +** +** Implementation of srvtab handling for the wallet client. +** +** Written by Russ Allbery <rra@stanford.edu> +** Copyright 2007 Board of Trustees, Leland Stanford Jr. University +** +** See README for licensing terms. +*/ + +#include <config.h> +#include <system.h> + +#include <errno.h> +#include <fcntl.h> +#include <krb5.h> +#include <string.h> +#include <unistd.h> + +#include <client/internal.h> + +#ifndef KRB5_KRB4_COMPAT +# define ANAME_SZ 40 +# define INST_SZ 40 +# 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); + fprintf(stderr, "%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. +** +** 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. +** +** 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) +{ + krb5_context ctx = NULL; + krb5_keytab kt; + krb5_principal princ; + krb5_keytab_entry entry; + krb5_error_code ret; + size_t length; + int fd; + ssize_t status; + char aname[ANAME_SZ + 1] = ""; + char inst[INST_SZ + 1] = ""; + char realm[REALM_SZ + 1] = ""; + 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); + ret = krb5_kt_resolve(ctx, keytab, &kt); + if (ret != 0) + die_krb5(ctx, "error opening keytab", ret); + 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); + } + 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); + + /* Assemble the srvtab data. */ + length = 0; + strcpy(data + length, aname); + length += strlen(aname); + data[length++] = '\0'; + strcpy(data + length, inst); + length += strlen(inst); + data[length++] = '\0'; + strcpy(data + length, realm); + length += strlen(realm); + data[length++] = '\0'; + data[length++] = '\0'; + memcpy(data + length, entry.key.contents, 8); + length += 8; + krb5_free_keytab_entry_contents(ctx, &entry); + + /* Write out the srvtab file. */ + fd = open(srvtab, O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (fd < 0) { + fprintf(stderr, "open of %s failed: %s", srvtab, strerror(errno)); + exit(1); + } + status = write(fd, data, length); + if (status < 0) { + fprintf(stderr, "write to %s failed: %s", srvtab, strerror(errno)); + exit(1); + } else if (status != (ssize_t) length) { + fprintf(stderr, "write to %s truncated", srvtab); + exit(1); + } + if (close(fd) < 0) { + fprintf(stderr, "close of %s failed: %s", srvtab, strerror(errno)); + exit(1); + } +} diff --git a/client/wallet.c b/client/wallet.c index 304856c..8d8bb58 100644 --- a/client/wallet.c +++ b/client/wallet.c @@ -13,16 +13,9 @@ #include <errno.h> #include <fcntl.h> - #include <remctl.h> -/* Temporary until we have some real configuration. */ -#ifndef SERVER -# define SERVER "wallet.stanford.edu" -#endif -#ifndef PORT -# define PORT 4444 -#endif +#include <client/internal.h> /* Usage message. */ static const char usage_message[] = "\ @@ -120,11 +113,16 @@ main(int argc, char *argv[]) fprintf(stderr, "wallet: -f only supported for get\n"); exit(1); } - if (srvtab != NULL) + if (srvtab != NULL) { if (strcmp(argv[0], "get") != 0 || strcmp(argv[1], "keytab") != 0) { fprintf(stderr, "wallet: -S only supported for get keytab\n"); exit(1); } + if (file == NULL) { + fprintf(stderr, "wallet: -S requires -f\n"); + exit(1); + } + } /* Allocate space for the command to send to the server. */ command = malloc(sizeof(char *) * (argc + 2)); @@ -169,6 +167,8 @@ main(int argc, char *argv[]) fprintf(stderr, "close of %s failed: %s", file, strerror(errno)); exit(1); } + if (srvtab != NULL) + write_srvtab(srvtab, command[3], file); } else { fwrite(result->stdout_buf, 1, result->stdout_len, stdout); } diff --git a/client/wallet.pod b/client/wallet.pod index 3f7c60b..e7ea4a0 100644 --- a/client/wallet.pod +++ b/client/wallet.pod @@ -5,8 +5,8 @@ wallet - Client for retrieving secure data from a central server =head1 SYNOPSIS B<wallet> [B<-hv>] [B<-c> I<command>] [B<-f> I<output>] -[B<-k> I<principal>] [B<-p> I<port>] [B<-s> I<server>] I<command> -[I<arg> ...] +[B<-k> I<principal>] [B<-p> I<port>] [B<-s> I<server>] [B<-S> I<srvtab>] +I<command> [I<arg> ...] =head1 DESCRIPTION @@ -36,16 +36,17 @@ C<keytab> and a name of C<host/example.com>. The meaning of the name is specific to each type of object. Most other wallet commands besides those three are only available to -wallet administrators. The other commands allow setting ownership and -ACLs on objects, creating and destroying objects, creating and destroying -ACLs, and adding and removing entries from ACLs. An ACL consists of one -or more entries, each of which is a scheme and an identifier. A scheme -specifies a way of checking whether a user is authorized. An identifier -is some data specific to the scheme that specifies which users are -authorized. For example, for the C<krb5> scheme, the identifier is a -principal name and only that principal is authorized by that ACL entry. -For the C<pts> scheme, the identifier is a PTS group name, and all members -of that PTS group are authorized by that ACL entry. +wallet administrators. The exception is attribute commands; see +L<ATTRIBUTES>. The other commands allow setting ownership and ACLs on +objects, creating and destroying objects, creating and destroying ACLs, +and adding and removing entries from ACLs. An ACL consists of one or more +entries, each of which is a scheme and an identifier. A scheme specifies +a way of checking whether a user is authorized. An identifier is some +data specific to the scheme that specifies which users are authorized. +For example, for the C<krb5> scheme, the identifier is a principal name +and only that principal is authorized by that ACL entry. For the C<pts> +scheme, the identifier is a PTS group name, and all members of that PTS +group are authorized by that ACL entry. To run the wallet command-line client, you must already have a Kerberos ticket. You can obtain a Kerberos ticket with B<kinit> and see your @@ -86,6 +87,17 @@ commands are ignored. The port to connect to on the wallet server. The default is the default remctl port (4444). +=item B<-S> I<srvtab> + +This flag is only used in combination with the C<get> command on a +C<keytab> object, and must be used in conjunction with the B<-f> flag. +After the keytab is saved to the file specified by B<-f>, the DES key for +that principal will be extracted and written as a Kerberos v4 srvtab to +the file I<srvtab>. Any existing contents of I<srvtab> will be +destroyed. For more information on how the principal is converted to +Kerberos v4, see the description of the B<sync> attribute under +L<ATTRIBUTES>. + =item B<-s> I<server> The wallet server to connect to. The default is a hard-coded server value @@ -118,6 +130,8 @@ object that change data except the C<flags> commands, nor can the C<get> command be used on that object. C<show>, C<getacl>, and C<owner> or C<expires> without an argument can still be used on that object. +For more information on attributes, see L<ATTRIBUTES>. + =over 4 =item acl add <id> <scheme> <identifier> @@ -240,8 +254,6 @@ particular object type, and <attr> must be an attribute type known to the underlying object implementation. To clear the attribute for this object, pass in a <value> of the empty string (C<''>). -Currently, no object attributes are implemented. - =item show <type> <name> Displays the current object metadata for the object identified by <type> @@ -262,9 +274,52 @@ will be lifted in the future. =back +=head1 ATTRIBUTES + +Object attributes store additional properties and configuration +information for objects stored in the wallet. They are displayed as part +of the object data with C<show>, retrieved with C<getattr>, and set with +C<setattr>. + +=head1 Keytab Attributes + +Keytab objects support the following attributes: + +=over 4 + +=item sync + +Sets the external systems to which the key of a given principal is +synchronized. The only supported value for this attribute is C<kaserver>, +which says to synchronize the key with an AFS Kerberos v4 kaserver. + +If this attribute is set on a keytab, whenever the C<get> command is run +for that keytab, the DES key will be extracted from that keytab and set in +the configured AFS kaserver. If the B<-S> option is given to the +B<wallet> client, the srvtab corresponding to the keytab will be written +to the file specified with that option. The Kerberos v4 principal name +will be the same as the Kerberos v5 principal name except that the +components are separated by C<.> instead of C</>; the second component is +truncated after the first C<.> if the first component is one of C<host>, +C<ident>, C<imap>, C<pop>, or C<smtp>; and the first component is C<rcmd> +if the Kerberos v5 principal component is C<host>. The principal name +must not contain more than two components. + +If this attribute is set, calling C<destroy> will also destroy the +principal from the AFS kaserver, with a principal mapping determined as +above. + +The realm of the srvtab defaults to the same realm as the keytab. You can +change this by setting the v4_realm configuration option in the [realms] +section of krb5.conf for the local realm. The keytab must be for a +principal in the default local realm for the B<-S> option to work +correctly. + +=back + =head1 SEE ALSO -remctl(1), remctld(8) +krb5.conf(5), remctl(1), remctld(8) This program is part of the wallet system. The current version is available from L<http://www.eyrie.org/~eagle/software/wallet/>. |