aboutsummaryrefslogtreecommitdiff
path: root/src/clients
diff options
context:
space:
mode:
authorIsaac Boukris <iboukris@gmail.com>2019-01-08 07:19:20 +0200
committerGreg Hudson <ghudson@mit.edu>2019-03-13 16:37:04 -0400
commit4c16e5f2040cddde2dab7c08957138c076a7e05d (patch)
treea00aa7eff731ba98d1b471ebe6c0c722cb878fe1 /src/clients
parented830223d862bb48ccc43e2c7dbbb4eaf555e679 (diff)
downloadkrb5-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/clients')
-rw-r--r--src/clients/kvno/kvno.c164
1 files changed, 143 insertions, 21 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)