{
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, _("\tservice1 service2 ...\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);
+ char *for_user, int proxy, const char *u2u_ccname);
#include <com_err.h>
static void extended_com_err_fn(const char *myprog, errcode_t code,
int
main(int argc, char *argv[])
{
+ enum { OPTION_U2U = 256 };
+ struct option lopts[] = {
+ { "u2u", 1, NULL, OPTION_U2U },
+ { NULL, 0, NULL, 0 }
+ };
+ const char *shopts = "uCc:e:hk:qPS:U:";
int option;
char *etypestr = NULL, *ccachestr = NULL, *keytab_name = NULL;
- char *sname = NULL, *for_user = NULL;
+ char *sname = NULL, *for_user = NULL, *u2u_ccname = NULL;
int canon = 0, unknown = 0, proxy = 0;
setlocale(LC_ALL, "");
prog = strrchr(argv[0], '/');
prog = prog ? (prog + 1) : argv[0];
- while ((option = getopt(argc, argv, "uCc:e:hk:qPS:U:")) != -1) {
+ while ((option = getopt_long(argc, argv, shopts, lopts, NULL)) != -1) {
switch (option) {
case 'C':
canon = 1;
case 'U':
for_user = optarg; /* S4U2Self - protocol transition */
break;
+ case OPTION_U2U:
+ u2u_ccname = optarg;
+ break;
default:
xusage();
break;
}
}
+ if (u2u_ccname != NULL && for_user != NULL) {
+ fprintf(stderr, _("Options --u2u and -P are mutually exclusive\n"));
+ xusage();
+ }
+
if (proxy) {
if (keytab_name == NULL) {
fprintf(stderr, _("Option -P (constrained delegation) "
xusage();
do_v5_kvno(argc - optind, argv + optind, ccachestr, etypestr, keytab_name,
- sname, canon, unknown, for_user, proxy);
+ sname, canon, unknown, for_user, proxy, u2u_ccname);
return 0;
}
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_flags options, int unknown, krb5_principal for_user_princ, int proxy,
+ krb5_data *u2u_ticket)
{
krb5_error_code ret;
krb5_principal server = NULL;
in_creds.keyblock.enctype = etype;
+ if (u2u_ticket != NULL)
+ in_creds.second_ticket = *u2u_ticket;
+
if (for_user_princ != NULL) {
if (!proxy && !krb5_principal_compare(context, me, server)) {
ret = EINVAL;
return ret;
}
+/* Fetch the encoded local TGT for ccname's default client principal. */
+static krb5_error_code
+get_u2u_ticket(const char *ccname, krb5_data **ticket_out)
+{
+ krb5_error_code ret;
+ krb5_ccache cc = NULL;
+ krb5_creds mcred, *creds = NULL;
+
+ *ticket_out = NULL;
+ memset(&mcred, 0, sizeof(mcred));
+
+ ret = krb5_cc_resolve(context, ccname, &cc);
+ if (ret)
+ goto cleanup;
+ ret = krb5_cc_get_principal(context, cc, &mcred.client);
+ if (ret)
+ goto cleanup;
+ ret = krb5_build_principal_ext(context, &mcred.server,
+ mcred.client->realm.length,
+ mcred.client->realm.data,
+ KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME,
+ mcred.client->realm.length,
+ mcred.client->realm.data, 0);
+ if (ret)
+ goto cleanup;
+ ret = krb5_get_credentials(context, KRB5_GC_CACHED, cc, &mcred, &creds);
+ if (ret)
+ goto cleanup;
+
+ ret = krb5_copy_data(context, &creds->ticket, ticket_out);
+
+cleanup:
+ if (cc != NULL)
+ krb5_cc_close(context, cc);
+ krb5_free_cred_contents(context, &mcred);
+ krb5_free_creds(context, creds);
+ return ret;
+}
+
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)
+ char *for_user, int proxy, const char *u2u_ccname)
{
krb5_error_code ret;
int i, errors;
krb5_principal me;
krb5_keytab keytab = NULL;
krb5_principal for_user_princ = NULL;
- krb5_flags options;
+ krb5_flags options = canon ? KRB5_GC_CANONICALIZE : 0;
+ krb5_data *u2u_ticket = NULL;
ret = krb5_init_context(&context);
if (ret) {
}
}
+ if (u2u_ccname != NULL) {
+ ret = get_u2u_ticket(u2u_ccname, &u2u_ticket);
+ if (ret) {
+ com_err(prog, ret, _("while getting user-to-user ticket from %s"),
+ u2u_ccname);
+ exit(1);
+ }
+ options |= KRB5_GC_USER_USER;
+ }
+
ret = krb5_cc_get_principal(context, ccache, &me);
if (ret) {
com_err(prog, ret, _("while getting client principal name"));
exit(1);
}
- options = canon ? KRB5_GC_CANONICALIZE : 0;
-
errors = 0;
for (i = 0; i < count; i++) {
if (kvno(names[i], ccache, me, etype, keytab, sname, options, unknown,
- for_user_princ, proxy) != 0)
+ for_user_princ, proxy, u2u_ticket) != 0)
errors++;
}
krb5_free_principal(context, me);
krb5_free_principal(context, for_user_princ);
krb5_cc_close(context, ccache);
+ krb5_free_data(context, u2u_ticket);
krb5_free_context(context);
if (errors)
--- /dev/null
+from k5test import *
+
+realm = K5Realm(create_host=False)
+
+# Create a second user principal and get tickets for it.
+u2u_ccache = 'FILE:' + os.path.join(realm.testdir, 'ccu2u')
+realm.addprinc('alice', password('alice'))
+realm.kinit('alice', password('alice'), ['-c', u2u_ccache])
+
+# Verify that -allow_dup_skey denies u2u requests.
+realm.run([kadminl, 'modprinc', '-allow_dup_skey', 'alice'])
+realm.run([kvno, '--u2u', u2u_ccache, 'alice'], expected_code=1,
+ expected_msg='KDC policy rejects request')
+realm.run([kadminl, 'modprinc', '+allow_dup_skey', 'alice'])
+
+# Verify that -allow_svr denies regular TGS requests, but allows
+# user-to-user TGS requests.
+realm.run([kadminl, 'modprinc', '-allow_svr', 'alice'])
+realm.run([kvno, 'alice'], expected_code=1,
+ expected_msg='Server principal valid for user2user only')
+realm.run([kvno, '--u2u', u2u_ccache, 'alice'], expected_msg='kvno = 0')
+realm.run([kadminl, 'modprinc', '+allow_svr', 'alice'])
+
+# Try u2u against the client user.
+realm.run([kvno, '--u2u', realm.ccache, realm.user_princ])
+
+realm.run([klist])