diff options
author | Isaac Boukris <iboukris@gmail.com> | 2019-01-08 07:19:20 +0200 |
---|---|---|
committer | Greg Hudson <ghudson@mit.edu> | 2019-03-13 16:37:04 -0400 |
commit | 4c16e5f2040cddde2dab7c08957138c076a7e05d (patch) | |
tree | a00aa7eff731ba98d1b471ebe6c0c722cb878fe1 /src | |
parent | ed830223d862bb48ccc43e2c7dbbb4eaf555e679 (diff) | |
download | krb5-4c16e5f2040cddde2dab7c08957138c076a7e05d.zip krb5-4c16e5f2040cddde2dab7c08957138c076a7e05d.tar.gz krb5-4c16e5f2040cddde2dab7c08957138c076a7e05d.tar.bz2 |
Add new kvno protocol transition options
Add kvno -I to do S4U2Self without treating the principal as an
enterprise name (as -U does). Add kvno -F to do S4U2Self with an
X.509 certificate.
[ghudson@mit.edu: changed code to read cert in PEM format; updated RST
man page instead of generated nroff file, and regenerated nroff file]
ticket: 8778 (new)
Diffstat (limited to 'src')
-rw-r--r-- | src/clients/kvno/kvno.c | 164 | ||||
-rw-r--r-- | src/man/kvno.man | 16 |
2 files changed, 156 insertions, 24 deletions
diff --git a/src/clients/kvno/kvno.c b/src/clients/kvno/kvno.c index f4fa048..66ef491 100644 --- a/src/clients/kvno/kvno.c +++ b/src/clients/kvno/kvno.c @@ -26,11 +26,14 @@ */ #include "k5-platform.h" +#include "k5-buf.h" +#include "k5-base64.h" #include <locale.h> #ifdef HAVE_UNISTD_H #include <unistd.h> #endif #include <string.h> +#include <ctype.h> static char *prog; static int quiet = 0; @@ -39,14 +42,17 @@ static void xusage() { fprintf(stderr, _("usage: %s [-C] [-u] [-c ccache] [-e etype]\n"), prog); - fprintf(stderr, _("\t[-k keytab] [-S sname] [-U for_user [-P]]\n")); + fprintf(stderr, _("\t[-k keytab] [-S sname] [{-I | -U} for_user | " + "[-F cert_file] [-P]]\n")); fprintf(stderr, _("\t[--u2u ccache] service1 service2 ...\n")); exit(1); } static void do_v5_kvno(int argc, char *argv[], char *ccachestr, char *etypestr, char *keytab_name, char *sname, int canon, int unknown, - char *for_user, int proxy, const char *u2u_ccname); + char *for_user, int for_user_enterprise, + char *for_user_cert_file, int proxy, + const char *u2u_ccname); #include <com_err.h> static void extended_com_err_fn(const char *myprog, errcode_t code, @@ -60,11 +66,13 @@ main(int argc, char *argv[]) { "u2u", 1, NULL, OPTION_U2U }, { NULL, 0, NULL, 0 } }; - const char *shopts = "uCc:e:hk:qPS:U:"; + const char *shopts = "uCc:e:hk:qPS:I:U:F:"; int option; char *etypestr = NULL, *ccachestr = NULL, *keytab_name = NULL; char *sname = NULL, *for_user = NULL, *u2u_ccname = NULL; - int canon = 0, unknown = 0, proxy = 0; + char *for_user_cert_file = NULL; + int canon = 0, unknown = 0, proxy = 0, for_user_enterprise = 0; + int impersonate = 0; setlocale(LC_ALL, ""); set_com_err_hook(extended_com_err_fn); @@ -111,8 +119,18 @@ main(int argc, char *argv[]) xusage(); } break; + case 'I': + impersonate = 1; + for_user = optarg; + break; case 'U': - for_user = optarg; /* S4U2Self - protocol transition */ + impersonate = 1; + for_user_enterprise = 1; + for_user = optarg; + break; + case 'F': + impersonate = 1; + for_user_cert_file = optarg; break; case OPTION_U2U: u2u_ccname = optarg; @@ -123,8 +141,9 @@ main(int argc, char *argv[]) } } - if (u2u_ccname != NULL && for_user != NULL) { - fprintf(stderr, _("Options --u2u and -P are mutually exclusive\n")); + if (u2u_ccname != NULL && impersonate) { + fprintf(stderr, + _("Options --u2u and -I|-U|-F are mutually exclusive\n")); xusage(); } @@ -133,9 +152,9 @@ main(int argc, char *argv[]) fprintf(stderr, _("Option -P (constrained delegation) " "requires keytab to be specified\n")); xusage(); - } else if (for_user == NULL) { + } else if (!impersonate) { fprintf(stderr, _("Option -P (constrained delegation) requires " - "option -U (protocol transition)\n")); + "option -I|-U|-F (protocol transition)\n")); xusage(); } } @@ -144,7 +163,8 @@ main(int argc, char *argv[]) xusage(); do_v5_kvno(argc - optind, argv + optind, ccachestr, etypestr, keytab_name, - sname, canon, unknown, for_user, proxy, u2u_ccname); + sname, canon, unknown, for_user, for_user_enterprise, + for_user_cert_file, proxy, u2u_ccname); return 0; } @@ -162,13 +182,103 @@ static void extended_com_err_fn(const char *myprog, errcode_t code, fprintf(stderr, "\n"); } +/* Read a line from fp into buf. Trim any trailing whitespace, and return a + * pointer to the first non-whitespace character. */ +static const char * +read_line(FILE *fp, char *buf, size_t bufsize) +{ + char *end, *begin; + + if (fgets(buf, bufsize, fp) == NULL) + return NULL; + + end = buf + strlen(buf); + while (end > buf && isspace((uint8_t)end[-1])) + *--end = '\0'; + + begin = buf; + while (isspace((uint8_t)*begin)) + begin++; + + return begin; +} + +/* Read a certificate from file_name in PEM format, placing the DER + * representation of the certificate in *der_out. */ +static krb5_error_code +read_pem_file(char *file_name, krb5_data *der_out) +{ + krb5_error_code ret = 0; + FILE *fp = NULL; + const char *begin_line = "-----BEGIN CERTIFICATE-----"; + const char *end_line = "-----END ", *line; + char linebuf[256]; + struct k5buf buf = EMPTY_K5BUF; + uint8_t *der_cert; + size_t dlen; + + *der_out = empty_data(); + + fp = fopen(file_name, "r"); + if (fp == NULL) + return errno; + + for (;;) { + line = read_line(fp, linebuf, sizeof(linebuf)); + if (line == NULL) { + ret = EINVAL; + k5_setmsg(context, ret, _("No begin line not found")); + goto cleanup; + } + if (strncmp(line, begin_line, strlen(begin_line)) == 0) + break; + } + + k5_buf_init_dynamic(&buf); + for (;;) { + line = read_line(fp, linebuf, sizeof(linebuf)); + if (line == NULL) { + ret = EINVAL; + k5_setmsg(context, ret, _("No end line found")); + goto cleanup; + } + + if (strncmp(line, end_line, strlen(end_line)) == 0) + break; + + /* Header lines would be expected for an actual privacy-enhanced mail + * message, but not for a certificate. */ + if (*line == '\0' || strchr(line, ':') != NULL) { + ret = EINVAL; + k5_setmsg(context, ret, _("Unexpected header line")); + goto cleanup; + } + + k5_buf_add(&buf, line); + } + + der_cert = k5_base64_decode(buf.data, &dlen); + if (der_cert == NULL) { + ret = EINVAL; + k5_setmsg(context, ret, _("Invalid base64")); + goto cleanup; + } + + *der_out = make_data(der_cert, dlen); + +cleanup: + fclose(fp); + k5_buf_free(&buf); + return ret; +} + /* Request a single service ticket and display its status (unless quiet is * set). On failure, display an error message and return non-zero. */ static krb5_error_code kvno(const char *name, krb5_ccache ccache, krb5_principal me, krb5_enctype etype, krb5_keytab keytab, const char *sname, - krb5_flags options, int unknown, krb5_principal for_user_princ, int proxy, - krb5_data *u2u_ticket) + krb5_flags options, int unknown, krb5_principal for_user_princ, + krb5_data *for_user_cert, int proxy, krb5_data *u2u_ticket) { krb5_error_code ret; krb5_principal server = NULL; @@ -204,7 +314,7 @@ kvno(const char *name, krb5_ccache ccache, krb5_principal me, if (u2u_ticket != NULL) in_creds.second_ticket = *u2u_ticket; - if (for_user_princ != NULL) { + if (for_user_princ != NULL || for_user_cert != NULL) { if (!proxy && !krb5_principal_compare(context, me, server)) { ret = EINVAL; com_err(prog, ret, @@ -215,7 +325,8 @@ kvno(const char *name, krb5_ccache ccache, krb5_principal me, in_creds.client = for_user_princ; in_creds.server = me; ret = krb5_get_credentials_for_user(context, options, ccache, - &in_creds, NULL, &out_creds); + &in_creds, for_user_cert, + &out_creds); } else { in_creds.client = me; in_creds.server = server; @@ -320,17 +431,18 @@ cleanup: static void do_v5_kvno(int count, char *names[], char * ccachestr, char *etypestr, char *keytab_name, char *sname, int canon, int unknown, - char *for_user, int proxy, const char *u2u_ccname) + char *for_user, int for_user_enterprise, + char *for_user_cert_file, int proxy, const char *u2u_ccname) { krb5_error_code ret; - int i, errors; + int i, errors, flags; krb5_enctype etype; krb5_ccache ccache; krb5_principal me; krb5_keytab keytab = NULL; krb5_principal for_user_princ = NULL; krb5_flags options = canon ? KRB5_GC_CANONICALIZE : 0; - krb5_data *u2u_ticket = NULL; + krb5_data cert_data = empty_data(), *user_cert = NULL, *u2u_ticket = NULL; ret = krb5_init_context(&context); if (ret) { @@ -366,15 +478,24 @@ do_v5_kvno(int count, char *names[], char * ccachestr, char *etypestr, } if (for_user) { - ret = krb5_parse_name_flags(context, for_user, - KRB5_PRINCIPAL_PARSE_ENTERPRISE, - &for_user_princ); + flags = for_user_enterprise ? KRB5_PRINCIPAL_PARSE_ENTERPRISE : 0; + ret = krb5_parse_name_flags(context, for_user, flags, &for_user_princ); if (ret) { com_err(prog, ret, _("while parsing principal name %s"), for_user); exit(1); } } + if (for_user_cert_file != NULL) { + ret = read_pem_file(for_user_cert_file, &cert_data); + if (ret) { + com_err(prog, ret, _("while reading certificate file %s"), + for_user_cert_file); + exit(1); + } + user_cert = &cert_data; + } + if (u2u_ccname != NULL) { ret = get_u2u_ticket(u2u_ccname, &u2u_ticket); if (ret) { @@ -394,7 +515,7 @@ do_v5_kvno(int count, char *names[], char * ccachestr, char *etypestr, errors = 0; for (i = 0; i < count; i++) { if (kvno(names[i], ccache, me, etype, keytab, sname, options, unknown, - for_user_princ, proxy, u2u_ticket) != 0) + for_user_princ, user_cert, proxy, u2u_ticket) != 0) errors++; } @@ -404,6 +525,7 @@ do_v5_kvno(int count, char *names[], char * ccachestr, char *etypestr, krb5_free_principal(context, for_user_princ); krb5_cc_close(context, ccache); krb5_free_data(context, u2u_ticket); + krb5_free_data_contents(context, &cert_data); krb5_free_context(context); if (errors) diff --git a/src/man/kvno.man b/src/man/kvno.man index ad4bfd5..129c9f2 100644 --- a/src/man/kvno.man +++ b/src/man/kvno.man @@ -1,6 +1,6 @@ .\" Man page generated from reStructuredText. . -.TH "KVNO" "1" " " "1.17" "MIT Kerberos" +.TH "KVNO" "1" " " "1.18" "MIT Kerberos" .SH NAME kvno \- print key version numbers of Kerberos principals . @@ -39,7 +39,9 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] [\fB\-h\fP] [\fB\-P\fP] [\fB\-S\fP \fIsname\fP] +[\fB\-I\fP \fIfor_user\fP] [\fB\-U\fP \fIfor_user\fP] +[\fB\-F\fP \fIcert_file\fP] [\fB\-\-u2u\fP \fIccache\fP] \fIservice1 service2\fP ... .SH DESCRIPTION @@ -79,12 +81,20 @@ constructed from those hostnames and the service name \fIsname\fP\&. The service hostnames will be canonicalized according to the usual rules for constructing service principals. .TP -\fB\-U\fP \fIfor_user\fP +\fB\-I\fP \fIfor_user\fP Specifies that protocol transition (S4U2Self) is to be used to acquire a ticket on behalf of \fIfor_user\fP\&. If constrained delegation is not requested, the service name must match the credentials cache client principal. .TP +\fB\-U\fP \fIfor_user\fP +Same as \-I, but treats \fIfor_user\fP as an enterprise name. +.TP +\fB\-F\fP \fIcert_file\fP +Specifies that protocol transition is to be used, identifying the +client principal with the X.509 certificate in \fIcert_file\fP\&. The +certificate file must be in PEM format. +.TP \fB\-\-u2u\fP \fIccache\fP Requests a user\-to\-user ticket. \fIccache\fP must contain a local krbtgt ticket for the server principal. The reported version @@ -107,6 +117,6 @@ kinit(1), kdestroy(1), kerberos(7) .SH AUTHOR MIT .SH COPYRIGHT -1985-2018, MIT +1985-2019, MIT .\" Generated by docutils manpage writer. . |