summaryrefslogtreecommitdiffstats
path: root/src/lib/krb5/krb
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/krb5/krb')
-rw-r--r--src/lib/krb5/krb/Makefile.in18
-rw-r--r--src/lib/krb5/krb/addr_srch.c22
-rw-r--r--src/lib/krb5/krb/auth_con.c3
-rw-r--r--src/lib/krb5/krb/auth_con.h5
-rw-r--r--src/lib/krb5/krb/bld_princ.c2
-rw-r--r--src/lib/krb5/krb/copy_auth.c82
-rw-r--r--src/lib/krb5/krb/gc_frm_kdc.c10
-rw-r--r--src/lib/krb5/krb/gc_via_tkt.c6
-rw-r--r--src/lib/krb5/krb/gen_subkey.c13
-rw-r--r--src/lib/krb5/krb/get_creds.c8
-rw-r--r--src/lib/krb5/krb/get_in_tkt.c107
-rw-r--r--src/lib/krb5/krb/gic_opt.c9
-rw-r--r--src/lib/krb5/krb/int-proto.h11
-rw-r--r--src/lib/krb5/krb/kfree.c96
-rw-r--r--src/lib/krb5/krb/mk_cred.c6
-rw-r--r--src/lib/krb5/krb/mk_rep.c69
-rw-r--r--src/lib/krb5/krb/mk_req_ext.c160
-rw-r--r--src/lib/krb5/krb/pac.c869
-rw-r--r--src/lib/krb5/krb/parse.c123
-rw-r--r--src/lib/krb5/krb/princ_comp.c118
-rw-r--r--src/lib/krb5/krb/rd_priv.c4
-rw-r--r--src/lib/krb5/krb/rd_rep.c86
-rw-r--r--src/lib/krb5/krb/rd_req.c13
-rw-r--r--src/lib/krb5/krb/rd_req_dec.c455
-rw-r--r--src/lib/krb5/krb/serialize.c3
-rw-r--r--src/lib/krb5/krb/unparse.c133
-rw-r--r--src/lib/krb5/krb/valid_times.c2
27 files changed, 2152 insertions, 281 deletions
diff --git a/src/lib/krb5/krb/Makefile.in b/src/lib/krb5/krb/Makefile.in
index 1a204dfd27..359a5e8d2f 100644
--- a/src/lib/krb5/krb/Makefile.in
+++ b/src/lib/krb5/krb/Makefile.in
@@ -64,6 +64,7 @@ STLIBOBJS= \
mk_req.o \
mk_req_ext.o \
mk_safe.o \
+ pac.o \
parse.o \
pr_to_salt.o \
preauth.o \
@@ -150,7 +151,8 @@ OBJS= $(OUTPRE)addr_comp.$(OBJEXT) \
$(OUTPRE)mk_req.$(OBJEXT) \
$(OUTPRE)mk_req_ext.$(OBJEXT) \
$(OUTPRE)mk_safe.$(OBJEXT) \
- $(OUTPRE)parse.$(OBJEXT) \
+ $(OUTPRE)pac.$(OBJEXT) \
+ $(OUTPRE)parse.$(OBJEXT) \
$(OUTPRE)pr_to_salt.$(OBJEXT) \
$(OUTPRE)preauth.$(OBJEXT) \
$(OUTPRE)preauth2.$(OBJEXT) \
@@ -237,6 +239,7 @@ SRCS= $(srcdir)/addr_comp.c \
$(srcdir)/mk_req.c \
$(srcdir)/mk_req_ext.c \
$(srcdir)/mk_safe.c \
+ $(srcdir)/pac.c \
$(srcdir)/parse.c \
$(srcdir)/pr_to_salt.c \
$(srcdir)/preauth.c \
@@ -709,7 +712,7 @@ get_creds.so get_creds.po $(OUTPRE)get_creds.$(OBJEXT): \
$(SRCTOP)/include/k5-plugin.h $(SRCTOP)/include/k5-thread.h \
$(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \
$(SRCTOP)/include/krb5/preauth_plugin.h $(SRCTOP)/include/port-sockets.h \
- $(SRCTOP)/include/socket-utils.h get_creds.c
+ $(SRCTOP)/include/socket-utils.h get_creds.c int-proto.h
get_in_tkt.so get_in_tkt.po $(OUTPRE)get_in_tkt.$(OBJEXT): \
$(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
$(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
@@ -882,6 +885,16 @@ mk_safe.so mk_safe.po $(OUTPRE)mk_safe.$(OBJEXT): $(BUILDTOP)/include/autoconf.h
$(SRCTOP)/include/krb5/locate_plugin.h $(SRCTOP)/include/krb5/preauth_plugin.h \
$(SRCTOP)/include/port-sockets.h $(SRCTOP)/include/socket-utils.h \
auth_con.h cleanup.h mk_safe.c
+pac.so pac.po $(OUTPRE)pac.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(SRCTOP)/include/k5-buf.h \
+ $(SRCTOP)/include/k5-err.h $(SRCTOP)/include/k5-gmt_mktime.h \
+ $(SRCTOP)/include/k5-int-pkinit.h $(SRCTOP)/include/k5-int.h \
+ $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-plugin.h \
+ $(SRCTOP)/include/k5-thread.h $(SRCTOP)/include/k5-utf8.h \
+ $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \
+ $(SRCTOP)/include/krb5/preauth_plugin.h $(SRCTOP)/include/port-sockets.h \
+ $(SRCTOP)/include/socket-utils.h pac.c
parse.so parse.po $(OUTPRE)parse.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
$(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(SRCTOP)/include/k5-buf.h \
@@ -929,6 +942,7 @@ princ_comp.so princ_comp.po $(OUTPRE)princ_comp.$(OBJEXT): \
$(SRCTOP)/include/k5-gmt_mktime.h $(SRCTOP)/include/k5-int-pkinit.h \
$(SRCTOP)/include/k5-int.h $(SRCTOP)/include/k5-platform.h \
$(SRCTOP)/include/k5-plugin.h $(SRCTOP)/include/k5-thread.h \
+ $(SRCTOP)/include/k5-unicode.h $(SRCTOP)/include/k5-utf8.h \
$(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \
$(SRCTOP)/include/krb5/preauth_plugin.h $(SRCTOP)/include/port-sockets.h \
$(SRCTOP)/include/socket-utils.h princ_comp.c
diff --git a/src/lib/krb5/krb/addr_srch.c b/src/lib/krb5/krb/addr_srch.c
index efab59f8fd..11a3ce0bb1 100644
--- a/src/lib/krb5/krb/addr_srch.c
+++ b/src/lib/krb5/krb/addr_srch.c
@@ -29,6 +29,20 @@
#include "k5-int.h"
+static unsigned int
+address_count(krb5_address *const *addrlist)
+{
+ unsigned int i;
+
+ if (addrlist == NULL)
+ return 0;
+
+ for (i = 0; addrlist[i]; i++)
+ ;
+
+ return i;
+}
+
/*
* if addr is listed in addrlist, or addrlist is null, return TRUE.
* if not listed, return FALSE
@@ -36,6 +50,14 @@
krb5_boolean
krb5_address_search(krb5_context context, const krb5_address *addr, krb5_address *const *addrlist)
{
+ /*
+ * Treat an address list containing only a NetBIOS address
+ * as empty, because we presently have no way of associating
+ * a client with its NetBIOS address.
+ */
+ if (address_count(addrlist) == 1 &&
+ addrlist[0]->addrtype == ADDRTYPE_NETBIOS)
+ return TRUE;
if (!addrlist)
return TRUE;
for (; *addrlist; addrlist++) {
diff --git a/src/lib/krb5/krb/auth_con.c b/src/lib/krb5/krb/auth_con.c
index 8ac121a6e9..7c1858553d 100644
--- a/src/lib/krb5/krb/auth_con.c
+++ b/src/lib/krb5/krb/auth_con.c
@@ -34,8 +34,9 @@ krb5_auth_con_init(krb5_context context, krb5_auth_context *auth_context)
(*auth_context)->req_cksumtype = context->default_ap_req_sumtype;
(*auth_context)->safe_cksumtype = context->default_safe_sumtype;
- (*auth_context) -> checksum_func = NULL;
+ (*auth_context)->checksum_func = NULL;
(*auth_context)->checksum_func_data = NULL;
+ (*auth_context)->negotiated_etype = ENCTYPE_NULL;
(*auth_context)->magic = KV5M_AUTH_CONTEXT;
return 0;
}
diff --git a/src/lib/krb5/krb/auth_con.h b/src/lib/krb5/krb/auth_con.h
index 9543de355e..be63bedbf4 100644
--- a/src/lib/krb5/krb/auth_con.h
+++ b/src/lib/krb5/krb/auth_con.h
@@ -21,8 +21,9 @@ struct _krb5_auth_context {
krb5_pointer i_vector; /* mk_priv, rd_priv only */
krb5_rcache rcache;
krb5_enctype * permitted_etypes; /* rd_req */
- krb5_mk_req_checksum_func checksum_func;
- void *checksum_func_data;
+ krb5_mk_req_checksum_func checksum_func;
+ void *checksum_func_data;
+ krb5_enctype negotiated_etype;
};
diff --git a/src/lib/krb5/krb/bld_princ.c b/src/lib/krb5/krb/bld_princ.c
index 1b9a894477..c7e996374c 100644
--- a/src/lib/krb5/krb/bld_princ.c
+++ b/src/lib/krb5/krb/bld_princ.c
@@ -88,7 +88,7 @@ krb5int_build_principal_va(krb5_context context,
princ->type = KRB5_NT_UNKNOWN;
princ->magic = KV5M_PRINCIPAL;
krb5_princ_set_realm_data(context, princ, r);
- krb5_princ_set_realm_length(context, princ, strlen(r));
+ krb5_princ_set_realm_length(context, princ, rlen);
princ->data = data;
princ->length = count;
r = NULL; /* take ownership */
diff --git a/src/lib/krb5/krb/copy_auth.c b/src/lib/krb5/krb/copy_auth.c
index 9c978cb6bf..cd27f72b52 100644
--- a/src/lib/krb5/krb/copy_auth.c
+++ b/src/lib/krb5/krb/copy_auth.c
@@ -26,6 +26,33 @@
*
* krb5_copy_authdata()
*/
+/*
+ * Copyright (c) 2006-2008, Novell, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * The copyright holder's name is not used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
#include "k5-int.h"
@@ -80,3 +107,58 @@ krb5_copy_authdata(krb5_context context, krb5_authdata *const *inauthdat, krb5_a
*outauthdat = tempauthdat;
return 0;
}
+
+krb5_error_code KRB5_CALLCONV
+krb5_decode_authdata_container(krb5_context context,
+ krb5_authdatatype type,
+ const krb5_authdata *container,
+ krb5_authdata ***authdata)
+{
+ krb5_error_code code;
+ krb5_data data;
+
+ *authdata = NULL;
+
+ if ((container->ad_type & AD_TYPE_FIELD_TYPE_MASK) != type)
+ return EINVAL;
+
+ data.length = container->length;
+ data.data = (char *)container->contents;
+
+ code = decode_krb5_authdata(&data, authdata);
+ if (code)
+ return code;
+
+ return 0;
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_encode_authdata_container(krb5_context context,
+ krb5_authdatatype type,
+ krb5_authdata *const*authdata,
+ krb5_authdata ***container)
+{
+ krb5_error_code code;
+ krb5_data *data;
+ krb5_authdata ad_datum;
+ krb5_authdata *ad_data[2];
+
+ *container = NULL;
+
+ code = encode_krb5_authdata((krb5_authdata * const *)authdata, &data);
+ if (code)
+ return code;
+
+ ad_datum.ad_type = type & AD_TYPE_FIELD_TYPE_MASK;
+ ad_datum.length = data->length;
+ ad_datum.contents = (unsigned char *)data->data;
+
+ ad_data[0] = &ad_datum;
+ ad_data[1] = NULL;
+
+ code = krb5_copy_authdata(context, ad_data, container);
+
+ krb5_free_data(context, data);
+
+ return code;
+}
diff --git a/src/lib/krb5/krb/gc_frm_kdc.c b/src/lib/krb5/krb/gc_frm_kdc.c
index 90a49d6a6e..77b1a4232c 100644
--- a/src/lib/krb5/krb/gc_frm_kdc.c
+++ b/src/lib/krb5/krb/gc_frm_kdc.c
@@ -139,10 +139,6 @@ static void tr_dbg_rtree(struct tr_state *, const char *, krb5_principal);
#define HARD_CC_ERR(r) ((r) && (r) != KRB5_CC_NOTFOUND && \
(r) != KRB5_CC_NOT_KTYPE)
-#define IS_TGS_PRINC(c, p) \
- (krb5_princ_size((c), (p)) == 2 && \
- data_eq_string(*krb5_princ_component((c), (p), 0), KRB5_TGS_NAME))
-
/*
* Flags for ccache lookups of cross-realm TGTs.
*
@@ -169,8 +165,6 @@ static krb5_error_code do_traversal(krb5_context ctx, krb5_ccache,
krb5_principal client, krb5_principal server,
krb5_creds *out_cc_tgt, krb5_creds **out_tgt,
krb5_creds ***out_kdc_tgts);
-static krb5_error_code krb5_get_cred_from_kdc_opt(krb5_context, krb5_ccache,
- krb5_creds *, krb5_creds **, krb5_creds ***, int);
/*
* init_cc_tgts()
@@ -778,7 +772,7 @@ cleanup:
* Returns errors, system errors.
*/
-static krb5_error_code
+krb5_error_code
krb5_get_cred_from_kdc_opt(krb5_context context, krb5_ccache ccache,
krb5_creds *in_cred, krb5_creds **out_cred,
krb5_creds ***tgts, int kdcopt)
@@ -915,7 +909,7 @@ krb5_get_cred_from_kdc_opt(krb5_context context, krb5_ccache ccache,
*/
if (old_use_conf_ktypes || context->tgs_ktype_count == 0)
goto cleanup;
- for (i = 0; i < context->tgs_ktype_count; i++) {
+ for (i = 0; i < (signed)context->tgs_ktype_count; i++) {
if ((*out_cred)->keyblock.enctype == context->tgs_ktypes[i]) {
/* Found an allowable etype, so we're done */
goto cleanup;
diff --git a/src/lib/krb5/krb/gc_via_tkt.c b/src/lib/krb5/krb/gc_via_tkt.c
index 22ac7f9db0..c73c6d5296 100644
--- a/src/lib/krb5/krb/gc_via_tkt.c
+++ b/src/lib/krb5/krb/gc_via_tkt.c
@@ -31,12 +31,6 @@
#include "k5-int.h"
#include "int-proto.h"
-#define in_clock_skew(date, now) (labs((date)-(now)) < context->clockskew)
-
-#define IS_TGS_PRINC(c, p) \
- (krb5_princ_size((c), (p)) == 2 && \
- data_eq_string(*krb5_princ_component((c), (p), 0), KRB5_TGS_NAME))
-
static krb5_error_code
krb5_kdcrep2creds(krb5_context context, krb5_kdc_rep *pkdcrep, krb5_address *const *address, krb5_data *psectkt, krb5_creds **ppcreds)
{
diff --git a/src/lib/krb5/krb/gen_subkey.c b/src/lib/krb5/krb/gen_subkey.c
index ad8d4bba39..4d4e7be681 100644
--- a/src/lib/krb5/krb/gen_subkey.c
+++ b/src/lib/krb5/krb/gen_subkey.c
@@ -40,7 +40,10 @@ key2data (krb5_keyblock k)
}
krb5_error_code
-krb5_generate_subkey(krb5_context context, const krb5_keyblock *key, krb5_keyblock **subkey)
+krb5_generate_subkey_extended(krb5_context context,
+ const krb5_keyblock *key,
+ krb5_enctype enctype,
+ krb5_keyblock **subkey)
{
krb5_error_code retval;
krb5_data seed;
@@ -52,10 +55,16 @@ krb5_generate_subkey(krb5_context context, const krb5_keyblock *key, krb5_keyblo
if ((*subkey = (krb5_keyblock *) malloc(sizeof(krb5_keyblock))) == NULL)
return(ENOMEM);
- if ((retval = krb5_c_make_random_key(context, key->enctype, *subkey))) {
+ if ((retval = krb5_c_make_random_key(context, enctype, *subkey))) {
krb5_xfree(*subkey);
return(retval);
}
return(0);
}
+
+krb5_error_code
+krb5_generate_subkey(krb5_context context, const krb5_keyblock *key, krb5_keyblock **subkey)
+{
+ return krb5_generate_subkey_extended(context, key, key->enctype, subkey);
+}
diff --git a/src/lib/krb5/krb/get_creds.c b/src/lib/krb5/krb/get_creds.c
index 38c3383170..6824a74b22 100644
--- a/src/lib/krb5/krb/get_creds.c
+++ b/src/lib/krb5/krb/get_creds.c
@@ -44,6 +44,7 @@
*/
#include "k5-int.h"
+#include "int-proto.h"
static krb5_error_code
krb5_get_credentials_core(krb5_context context, krb5_flags options,
@@ -110,6 +111,7 @@ krb5_get_credentials(krb5_context context, krb5_flags options,
krb5_creds **tgts;
krb5_flags fields;
int not_ktype;
+ int kdcopt = 0;
retval = krb5_get_credentials_core(context, options,
in_creds,
@@ -141,7 +143,11 @@ krb5_get_credentials(krb5_context context, krb5_flags options,
else
not_ktype = 0;
- retval = krb5_get_cred_from_kdc(context, ccache, ncreds, out_creds, &tgts);
+ if (options & KRB5_GC_CANONICALIZE)
+ kdcopt |= KDC_OPT_CANONICALIZE;
+
+ retval = krb5_get_cred_from_kdc_opt(context, ccache, ncreds,
+ out_creds, &tgts, kdcopt);
if (tgts) {
register int i = 0;
krb5_error_code rv2;
diff --git a/src/lib/krb5/krb/get_in_tkt.c b/src/lib/krb5/krb/get_in_tkt.c
index a1c29a46aa..f30ae21227 100644
--- a/src/lib/krb5/krb/get_in_tkt.c
+++ b/src/lib/krb5/krb/get_in_tkt.c
@@ -293,15 +293,31 @@ verify_as_reply(krb5_context context,
krb5_kdc_rep *as_reply)
{
krb5_error_code retval;
-
+ int canon_req;
+ int canon_ok;
+
/* check the contents for sanity: */
if (!as_reply->enc_part2->times.starttime)
as_reply->enc_part2->times.starttime =
as_reply->enc_part2->times.authtime;
-
- if (!krb5_principal_compare(context, as_reply->client, request->client)
- || !krb5_principal_compare(context, as_reply->enc_part2->server, request->server)
- || !krb5_principal_compare(context, as_reply->ticket->server, request->server)
+
+ /*
+ * We only allow the AS-REP server name to be changed if the
+ * caller set the canonicalize flag (or requested an enterprise
+ * principal) and we requested (and received) a TGT.
+ */
+ canon_req = ((request->kdc_options & KDC_OPT_CANONICALIZE) != 0) ||
+ (krb5_princ_type(context, request->client) == KRB5_NT_ENTERPRISE_PRINCIPAL);
+ if (canon_req) {
+ canon_ok = IS_TGS_PRINC(context, request->server) &&
+ IS_TGS_PRINC(context, as_reply->enc_part2->server);
+ } else
+ canon_ok = 0;
+
+ if ((!canon_ok &&
+ (!krb5_principal_compare(context, as_reply->client, request->client) ||
+ !krb5_principal_compare(context, as_reply->enc_part2->server, request->server)))
+ || !krb5_principal_compare(context, as_reply->enc_part2->server, as_reply->ticket->server)
|| (request->nonce != as_reply->enc_part2->nonce)
/* XXX check for extraneous flags */
/* XXX || (!krb5_addresses_compare(context, addrs, as_reply->enc_part2->caddrs)) */
@@ -507,7 +523,10 @@ krb5_get_in_tkt(krb5_context context,
krb5_pa_data ** preauth_to_use = 0;
int loopcount = 0;
krb5_int32 do_more = 0;
+ int canon_flag;
int use_master = 0;
+ int referral_count = 0;
+ krb5_principal_data referred_client;
#if APPLE_PKINIT
inTktDebug("krb5_get_in_tkt top\n");
@@ -518,7 +537,15 @@ krb5_get_in_tkt(krb5_context context,
if (ret_as_reply)
*ret_as_reply = 0;
-
+
+ referred_client = *(creds->client);
+ referred_client.realm.data = NULL;
+ referred_client.realm.length = 0;
+
+ /* per referrals draft, enterprise principals imply canonicalization */
+ canon_flag = ((options & KDC_OPT_CANONICALIZE) != 0) ||
+ creds->client->type == KRB5_NT_ENTERPRISE_PRINCIPAL;
+
/*
* Set up the basic request structure
*/
@@ -641,6 +668,27 @@ krb5_get_in_tkt(krb5_context context,
if (retval)
goto cleanup;
continue;
+ } else if (canon_flag && err_reply->error == KDC_ERR_WRONG_REALM) {
+ if (++referral_count > KRB5_REFERRAL_MAXHOPS ||
+ err_reply->client == NULL ||
+ err_reply->client->realm.length == 0) {
+ retval = KRB5KDC_ERR_WRONG_REALM;
+ krb5_free_error(context, err_reply);
+ goto cleanup;
+ }
+ /* Rewrite request.client with realm from error reply */
+ if (referred_client.realm.data) {
+ krb5_free_data_contents(context, &referred_client.realm);
+ referred_client.realm.data = NULL;
+ }
+ retval = krb5int_copy_data_contents(context,
+ &err_reply->client->realm,
+ &referred_client.realm);
+ krb5_free_error(context, err_reply);
+ if (retval)
+ goto cleanup;
+ request.client = &referred_client;
+ continue;
} else {
retval = (krb5_error_code) err_reply->error
+ ERROR_TABLE_BASE_krb5;
@@ -692,6 +740,8 @@ cleanup:
else
krb5_free_kdc_rep(context, as_reply);
}
+ if (referred_client.realm.data)
+ krb5_free_data_contents(context, &referred_client.realm);
return (retval);
}
@@ -923,6 +973,8 @@ krb5_get_init_creds(krb5_context context,
krb5_timestamp time_now;
krb5_enctype etype = 0;
krb5_preauth_client_rock get_data_rock;
+ int canon_flag = 0;
+ krb5_principal_data referred_client;
/* initialize everything which will be freed at cleanup */
@@ -947,6 +999,11 @@ krb5_get_init_creds(krb5_context context,
err_reply = NULL;
+ /* referred_client is used to rewrite the client realm for referrals */
+ referred_client = *client;
+ referred_client.realm.data = NULL;
+ referred_client.realm.length = 0;
+
/*
* Set up the basic request structure
*/
@@ -984,6 +1041,17 @@ krb5_get_init_creds(krb5_context context,
if (tempint)
request.kdc_options |= KDC_OPT_PROXIABLE;
+ /* canonicalize */
+ if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_CANONICALIZE))
+ tempint = 1;
+ else if ((ret = krb5_libdefault_boolean(context, &client->realm,
+ "canonicalize", &tempint)) == 0)
+ ;
+ else
+ tempint = 0;
+ if (tempint)
+ request.kdc_options |= KDC_OPT_CANONICALIZE;
+
/* allow_postdate */
if (start_time > 0)
@@ -1045,6 +1113,10 @@ krb5_get_init_creds(krb5_context context,
request.client = client;
+ /* per referrals draft, enterprise principals imply canonicalization */
+ canon_flag = ((request.kdc_options & KDC_OPT_CANONICALIZE) != 0) ||
+ client->type == KRB5_NT_ENTERPRISE_PRINCIPAL;
+
/* service */
if (in_tkt_service) {
@@ -1151,7 +1223,7 @@ krb5_get_init_creds(krb5_context context,
krb5_data random_data;
random_data.length = 4;
- random_data.data = random_buf;
+ random_data.data = (char *)random_buf;
if (krb5_c_random_make_octets(context, &random_data) == 0)
/* See RT ticket 3196 at MIT. If we set the high bit, we
may have compatibility problems with Heimdal, because
@@ -1253,6 +1325,25 @@ krb5_get_init_creds(krb5_context context,
if (ret)
goto cleanup;
/* continue to next iteration */
+ } else if (canon_flag && err_reply->error == KDC_ERR_WRONG_REALM) {
+ if (err_reply->client == NULL ||
+ err_reply->client->realm.length == 0) {
+ ret = KRB5KDC_ERR_WRONG_REALM;
+ krb5_free_error(context, err_reply);
+ goto cleanup;
+ }
+ /* Rewrite request.client with realm from error reply */
+ if (referred_client.realm.data) {
+ krb5_free_data_contents(context, &referred_client.realm);
+ referred_client.realm.data = NULL;
+ }
+ ret = krb5int_copy_data_contents(context,
+ &err_reply->client->realm,
+ &referred_client.realm);
+ krb5_free_error(context, err_reply);
+ if (ret)
+ goto cleanup;
+ request.client = &referred_client;
} else {
if (err_reply->e_data.length > 0) {
/* continue to next iteration */
@@ -1403,6 +1494,8 @@ cleanup:
*as_reply = local_as_reply;
else if (local_as_reply)
krb5_free_kdc_rep(context, local_as_reply);
+ if (referred_client.realm.data)
+ krb5_free_data_contents(context, &referred_client.realm);
return(ret);
}
diff --git a/src/lib/krb5/krb/gic_opt.c b/src/lib/krb5/krb/gic_opt.c
index 1ba1877bf9..348637ca3c 100644
--- a/src/lib/krb5/krb/gic_opt.c
+++ b/src/lib/krb5/krb/gic_opt.c
@@ -43,6 +43,15 @@ krb5_get_init_creds_opt_set_proxiable(krb5_get_init_creds_opt *opt, int proxiabl
}
void KRB5_CALLCONV
+krb5_get_init_creds_opt_set_canonicalize(krb5_get_init_creds_opt *opt, int canonicalize)
+{
+ if (canonicalize)
+ opt->flags |= KRB5_GET_INIT_CREDS_OPT_CANONICALIZE;
+ else
+ opt->flags &= ~(KRB5_GET_INIT_CREDS_OPT_CANONICALIZE);
+}
+
+void KRB5_CALLCONV
krb5_get_init_creds_opt_set_etype_list(krb5_get_init_creds_opt *opt, krb5_enctype *etype_list, int etype_list_length)
{
opt->flags |= KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST;
diff --git a/src/lib/krb5/krb/int-proto.h b/src/lib/krb5/krb/int-proto.h
index 5c576c3fd6..b81fe2566b 100644
--- a/src/lib/krb5/krb/int-proto.h
+++ b/src/lib/krb5/krb/int-proto.h
@@ -54,5 +54,16 @@ krb5_preauth_supply_preauth_data(krb5_context context,
const char *attr,
const char *value);
+krb5_error_code
+krb5_get_cred_from_kdc_opt(krb5_context context, krb5_ccache ccache,
+ krb5_creds *in_cred, krb5_creds **out_cred,
+ krb5_creds ***tgts, int kdcopt);
+
+#define in_clock_skew(date, now) (labs((date)-(now)) < context->clockskew)
+
+#define IS_TGS_PRINC(c, p) \
+ (krb5_princ_size((c), (p)) == 2 && \
+ data_eq_string(*krb5_princ_component((c), (p), 0), KRB5_TGS_NAME))
+
#endif /* KRB5_INT_FUNC_PROTO__ */
diff --git a/src/lib/krb5/krb/kfree.c b/src/lib/krb5/krb/kfree.c
index 39bb1f2128..9e5e192748 100644
--- a/src/lib/krb5/krb/kfree.c
+++ b/src/lib/krb5/krb/kfree.c
@@ -25,6 +25,33 @@
*
* krb5_free_address()
*/
+/*
+ * Copyright (c) 2006-2008, Novell, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * The copyright holder's name is not used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
#include "k5-int.h"
@@ -713,3 +740,72 @@ krb5_free_pa_enc_ts(krb5_context ctx, krb5_pa_enc_ts *pa_enc_ts)
return;
krb5_xfree(pa_enc_ts);
}
+
+void KRB5_CALLCONV
+krb5_free_pa_for_user(krb5_context context, krb5_pa_for_user *req)
+{
+ if (req == NULL)
+ return;
+ if (req->user != NULL) {
+ krb5_free_principal(context, req->user);
+ req->user = NULL;
+ }
+ krb5_free_checksum_contents(context, &req->cksum);
+ krb5_free_data_contents(context, &req->auth_package);
+ krb5_xfree(req);
+}
+
+void KRB5_CALLCONV
+krb5_free_pa_server_referral_data(krb5_context context,
+ krb5_pa_server_referral_data *ref)
+{
+ if (ref == NULL)
+ return;
+ if (ref->referred_realm) {
+ krb5_free_data(context, ref->referred_realm);
+ ref->referred_realm = NULL;
+ }
+ if (ref->true_principal_name != NULL) {
+ krb5_free_principal(context, ref->true_principal_name);
+ ref->true_principal_name = NULL;
+ }
+ if (ref->requested_principal_name != NULL) {
+ krb5_free_principal(context, ref->requested_principal_name);
+ ref->requested_principal_name = NULL;
+ }
+ krb5_free_checksum_contents(context, &ref->rep_cksum);
+ krb5_xfree(ref);
+}
+
+void KRB5_CALLCONV
+krb5_free_pa_svr_referral_data(krb5_context context,
+ krb5_pa_svr_referral_data *ref)
+{
+ if (ref == NULL)
+ return;
+ if (ref->principal != NULL) {
+ krb5_free_principal(context, ref->principal);
+ ref->principal = NULL;
+ }
+ krb5_xfree(ref);
+}
+
+void KRB5_CALLCONV
+krb5_free_pa_pac_req(krb5_context context,
+ krb5_pa_pac_req *req)
+{
+ if (req == NULL)
+ return;
+ krb5_xfree(req);
+}
+
+void KRB5_CALLCONV
+krb5_free_etype_list(krb5_context context,
+ krb5_etype_list *etypes)
+{
+ if (etypes != NULL) {
+ if (etypes->etypes != NULL)
+ krb5_xfree(etypes->etypes);
+ krb5_xfree(etypes);
+ }
+}
diff --git a/src/lib/krb5/krb/mk_cred.c b/src/lib/krb5/krb/mk_cred.c
index e9ed3850bd..8611e14097 100644
--- a/src/lib/krb5/krb/mk_cred.c
+++ b/src/lib/krb5/krb/mk_cred.c
@@ -174,13 +174,15 @@ krb5_mk_ncred(krb5_context context, krb5_auth_context auth_context,
/*
* Allocate memory for a NULL terminated list of tickets.
*/
- for (ncred = 0; ppcreds[ncred]; ncred++);
+ for (ncred = 0; ppcreds[ncred]; ncred++)
+ ;
if ((pcred = (krb5_cred *)calloc(1, sizeof(krb5_cred))) == NULL)
return ENOMEM;
if ((pcred->tickets
- = (krb5_ticket **)calloc(ncred+1, sizeof(krb5_ticket *))) == NULL) {
+ = (krb5_ticket **)calloc((size_t)ncred+1,
+ sizeof(krb5_ticket *))) == NULL) {
free(pcred);
return ENOMEM;
}
diff --git a/src/lib/krb5/krb/mk_rep.c b/src/lib/krb5/krb/mk_rep.c
index b512f09752..ee4f34ed2f 100644
--- a/src/lib/krb5/krb/mk_rep.c
+++ b/src/lib/krb5/krb/mk_rep.c
@@ -26,6 +26,33 @@
*
* krb5_mk_rep()
*/
+/*
+ * Copyright (c) 2006-2008, Novell, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * The copyright holder's name is not used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
#include "k5-int.h"
#include "auth_con.h"
@@ -39,9 +66,9 @@
returns system errors
*/
-krb5_error_code KRB5_CALLCONV
-krb5_mk_rep(krb5_context context, krb5_auth_context auth_context,
- krb5_data *outbuf)
+static krb5_error_code
+k5_mk_rep(krb5_context context, krb5_auth_context auth_context,
+ krb5_data *outbuf, int dce_style)
{
krb5_error_code retval;
krb5_ap_rep_enc_part repl;
@@ -58,17 +85,31 @@ krb5_mk_rep(krb5_context context, krb5_auth_context auth_context,
return(retval);
}
- repl.ctime = auth_context->authentp->ctime;
- repl.cusec = auth_context->authentp->cusec;
- if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_USE_SUBKEY) {
+ if (dce_style) {
+ krb5_us_timeofday(context, &repl.ctime, &repl.cusec);
+ } else {
+ repl.ctime = auth_context->authentp->ctime;
+ repl.cusec = auth_context->authentp->cusec;
+ }
+
+ if (dce_style)
+ repl.subkey = NULL;
+ else if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_USE_SUBKEY) {
+ assert(auth_context->negotiated_etype != ENCTYPE_NULL);
+
retval = krb5int_generate_and_save_subkey (context, auth_context,
- auth_context->keyblock);
+ auth_context->keyblock,
+ auth_context->negotiated_etype);
if (retval)
return retval;
repl.subkey = auth_context->send_subkey;
} else
repl.subkey = auth_context->authentp->subkey;
- repl.seq_number = auth_context->local_seq_number;
+
+ if (dce_style)
+ repl.seq_number = auth_context->remote_seq_number;
+ else
+ repl.seq_number = auth_context->local_seq_number;
/* encode it before encrypting */
if ((retval = encode_krb5_ap_rep_enc_part(&repl, &scratch)))
@@ -95,3 +136,15 @@ cleanup_scratch:
return retval;
}
+
+krb5_error_code KRB5_CALLCONV
+krb5_mk_rep(krb5_context context, krb5_auth_context auth_context, krb5_data *outbuf)
+{
+ return k5_mk_rep(context, auth_context, outbuf, 0);
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_mk_rep_dce(krb5_context context, krb5_auth_context auth_context, krb5_data *outbuf)
+{
+ return k5_mk_rep(context, auth_context, outbuf, 1);
+}
diff --git a/src/lib/krb5/krb/mk_req_ext.c b/src/lib/krb5/krb/mk_req_ext.c
index 6d40e5de7a..4a9d03551f 100644
--- a/src/lib/krb5/krb/mk_req_ext.c
+++ b/src/lib/krb5/krb/mk_req_ext.c
@@ -64,16 +64,25 @@
returns system errors
*/
+static krb5_error_code
+make_etype_list(krb5_context context,
+ krb5_enctype *desired_etypes,
+ krb5_enctype tkt_enctype,
+ krb5_authdata ***authdata);
+
static krb5_error_code
krb5_generate_authenticator (krb5_context,
krb5_authenticator *, krb5_principal,
krb5_checksum *, krb5_keyblock *,
- krb5_ui_4, krb5_authdata ** );
+ krb5_ui_4, krb5_authdata **,
+ krb5_enctype *desired_etypes,
+ krb5_enctype tkt_enctype);
krb5_error_code
krb5int_generate_and_save_subkey (krb5_context context,
krb5_auth_context auth_context,
- krb5_keyblock *keyblock)
+ krb5_keyblock *keyblock,
+ krb5_enctype enctype)
{
/* Provide some more fodder for random number code.
This isn't strong cryptographically; the point here is not
@@ -92,7 +101,8 @@ krb5int_generate_and_save_subkey (krb5_context context,
if (auth_context->send_subkey)
krb5_free_keyblock(context, auth_context->send_subkey);
- if ((retval = krb5_generate_subkey(context, keyblock, &auth_context->send_subkey)))
+ if ((retval = krb5_generate_subkey_extended(context, keyblock, enctype,
+ &auth_context->send_subkey)))
return retval;
if (auth_context->recv_subkey)
@@ -116,18 +126,23 @@ krb5_mk_req_extended(krb5_context context, krb5_auth_context *auth_context,
krb5_checksum checksum;
krb5_checksum *checksump = 0;
krb5_auth_context new_auth_context;
+ krb5_enctype *desired_etypes = NULL;
krb5_ap_req request;
krb5_data *scratch = 0;
krb5_data *toutbuf;
request.ap_options = ap_req_options & AP_OPTS_WIRE_MASK;
- request.authenticator.ciphertext.data = 0;
+ request.authenticator.ciphertext.data = NULL;
request.ticket = 0;
if (!in_creds->ticket.length)
return(KRB5_NO_TKT_SUPPLIED);
+ if ((ap_req_options & AP_OPTS_ETYPE_NEGOTIATION) &&
+ !(ap_req_options & AP_OPTS_MUTUAL_REQUIRED))
+ return(EINVAL);
+
/* we need a native ticket */
if ((retval = decode_krb5_ticket(&(in_creds)->ticket, &request.ticket)))
return(retval);
@@ -174,7 +189,8 @@ krb5_mk_req_extended(krb5_context context, krb5_auth_context *auth_context,
if ((ap_req_options & AP_OPTS_USE_SUBKEY)&&(!(*auth_context)->send_subkey)) {
retval = krb5int_generate_and_save_subkey (context, *auth_context,
- &in_creds->keyblock);
+ &in_creds->keyblock,
+ in_creds->keyblock.enctype);
if (retval)
goto cleanup;
}
@@ -205,12 +221,23 @@ krb5_mk_req_extended(krb5_context context, krb5_auth_context *auth_context,
goto cleanup_cksum;
}
+ if (ap_req_options & AP_OPTS_ETYPE_NEGOTIATION) {
+ if ((*auth_context)->permitted_etypes == NULL) {
+ retval = krb5_get_tgs_ktypes(context, in_creds->server, &desired_etypes);
+ if (retval)
+ goto cleanup_cksum;
+ } else
+ desired_etypes = (*auth_context)->permitted_etypes;
+ }
+
if ((retval = krb5_generate_authenticator(context,
(*auth_context)->authentp,
- (in_creds)->client, checksump,
+ in_creds->client, checksump,
(*auth_context)->send_subkey,
(*auth_context)->local_seq_number,
- (in_creds)->authdata)))
+ in_creds->authdata,
+ desired_etypes,
+ in_creds->keyblock.enctype)))
goto cleanup_cksum;
/* encode the authenticator */
@@ -223,7 +250,6 @@ krb5_mk_req_extended(krb5_context context, krb5_auth_context *auth_context,
*/
(*auth_context)->authentp->client = NULL;
(*auth_context)->authentp->checksum = NULL;
- (*auth_context)->authentp->authorization_data = NULL;
/* call the encryption routine */
if ((retval = krb5_encrypt_helper(context, &in_creds->keyblock,
@@ -242,6 +268,9 @@ cleanup_cksum:
free(checksump->contents);
cleanup:
+ if (desired_etypes &&
+ desired_etypes != (*auth_context)->permitted_etypes)
+ krb5_xfree(desired_etypes);
if (request.ticket)
krb5_free_ticket(context, request.ticket);
if (request.authenticator.ciphertext.data) {
@@ -261,7 +290,9 @@ static krb5_error_code
krb5_generate_authenticator(krb5_context context, krb5_authenticator *authent,
krb5_principal client, krb5_checksum *cksum,
krb5_keyblock *key, krb5_ui_4 seq_number,
- krb5_authdata **authorization)
+ krb5_authdata **authorization,
+ krb5_enctype *desired_etypes,
+ krb5_enctype tkt_enctype)
{
krb5_error_code retval;
@@ -274,7 +305,116 @@ krb5_generate_authenticator(krb5_context context, krb5_authenticator *authent,
} else
authent->subkey = 0;
authent->seq_number = seq_number;
- authent->authorization_data = authorization;
+ authent->authorization_data = NULL;
+
+ if (authorization != NULL) {
+ retval = krb5_copy_authdata(context, authorization,
+ &authent->authorization_data);
+ if (retval)
+ return retval;
+ }
+ /* Only send EtypeList if we prefer another enctype to tkt_enctype */
+ if (desired_etypes != NULL && desired_etypes[0] != tkt_enctype) {
+ retval = make_etype_list(context, desired_etypes, tkt_enctype,
+ &authent->authorization_data);
+ if (retval)
+ return retval;
+ }
return(krb5_us_timeofday(context, &authent->ctime, &authent->cusec));
}
+
+/* RFC 4537 */
+static krb5_error_code
+make_etype_list(krb5_context context,
+ krb5_enctype *desired_etypes,
+ krb5_enctype tkt_enctype,
+ krb5_authdata ***authdata)
+{
+ krb5_error_code code;
+ krb5_etype_list etypes;
+ krb5_data *enc_etype_list;
+ krb5_data *ad_if_relevant;
+ krb5_authdata *etype_adata[2], etype_adatum, **adata;
+ int i;
+
+ etypes.etypes = desired_etypes;
+
+ for (etypes.length = 0;
+ etypes.etypes[etypes.length] != ENCTYPE_NULL;
+ etypes.length++)
+ ;
+
+ /*
+ * RFC 4537:
+ *
+ * If the enctype of the ticket session key is included in the enctype
+ * list sent by the client, it SHOULD be the last on the list;
+ */
+ for (i = 0; i < etypes.length; i++) {
+ if (etypes.etypes[i] == tkt_enctype) {
+ krb5_enctype etype;
+
+ etype = etypes.etypes[etypes.length - 1];
+ etypes.etypes[etypes.length - 1] = tkt_enctype;
+ etypes.etypes[i] = etype;
+ break;
+ }
+ }
+
+ code = encode_krb5_etype_list(&etypes, &enc_etype_list);
+ if (code) {
+ return code;
+ }
+
+ etype_adatum.magic = KV5M_AUTHDATA;
+ etype_adatum.ad_type = KRB5_AUTHDATA_ETYPE_NEGOTIATION;
+ etype_adatum.length = enc_etype_list->length;
+ etype_adatum.contents = (krb5_octet *)enc_etype_list->data;
+
+ etype_adata[0] = &etype_adatum;
+ etype_adata[1] = NULL;
+
+ /* Wrap in AD-IF-RELEVANT container */
+ code = encode_krb5_authdata(etype_adata, &ad_if_relevant);
+ if (code) {
+ krb5_free_data(context, enc_etype_list);
+ return code;
+ }
+
+ krb5_free_data(context, enc_etype_list);
+
+ adata = *authdata;
+ if (adata == NULL) {
+ adata = (krb5_authdata **)calloc(2, sizeof(krb5_authdata *));
+ i = 0;
+ } else {
+ for (i = 0; adata[i] != NULL; i++)
+ ;
+
+ adata = (krb5_authdata **)realloc(*authdata,
+ (i + 2) * sizeof(krb5_authdata *));
+ }
+ if (adata == NULL) {
+ krb5_free_data(context, ad_if_relevant);
+ return ENOMEM;
+ }
+
+ adata[i] = (krb5_authdata *)malloc(sizeof(krb5_authdata));
+ if (adata[i] == NULL) {
+ krb5_free_data(context, ad_if_relevant);
+ return ENOMEM;
+ }
+ adata[i]->magic = KV5M_AUTHDATA;
+ adata[i]->ad_type = KRB5_AUTHDATA_IF_RELEVANT;
+ adata[i]->length = ad_if_relevant->length;
+ adata[i]->contents = (krb5_octet *)ad_if_relevant->data;
+ krb5_xfree(ad_if_relevant); /* contents owned by adata[i] */
+
+ adata[i + 1] = NULL;
+
+ *authdata = adata;
+
+ return 0;
+}
+
diff --git a/src/lib/krb5/krb/pac.c b/src/lib/krb5/krb/pac.c
new file mode 100644
index 0000000000..9aaff954f3
--- /dev/null
+++ b/src/lib/krb5/krb/pac.c
@@ -0,0 +1,869 @@
+/*
+ * lib/krb5/krb/pac.c
+ *
+ * Copyright 2008 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ *
+ */
+
+#include "k5-int.h"
+#include "k5-utf8.h"
+
+/* draft-brezak-win2k-krb-authz-00 */
+
+/*
+ * A PAC consists of a sequence of PAC_INFO_BUFFERs, preceeded by
+ * a PACTYPE header. Decoding the contents of the buffers is left
+ * to the application (notwithstanding signature verification).
+ */
+
+typedef struct _PAC_INFO_BUFFER {
+ krb5_ui_4 ulType;
+ krb5_ui_4 cbBufferSize;
+ krb5_ui_8 Offset;
+} PAC_INFO_BUFFER;
+
+#define PAC_INFO_BUFFER_LENGTH 16
+
+/* ulType */
+#define PAC_LOGON_INFO 1
+#define PAC_SERVER_CHECKSUM 6
+#define PAC_PRIVSVR_CHECKSUM 7
+#define PAC_CLIENT_INFO 10
+
+typedef struct _PACTYPE {
+ krb5_ui_4 cBuffers;
+ krb5_ui_4 Version;
+ PAC_INFO_BUFFER Buffers[1];
+} PACTYPE;
+
+#define PAC_ALIGNMENT 8
+#define PACTYPE_LENGTH 8U
+#define PAC_SIGNATURE_DATA_LENGTH 4U
+#define PAC_CLIENT_INFO_LENGTH 10U
+
+#define NT_TIME_EPOCH 11644473600LL
+
+struct krb5_pac_data {
+ PACTYPE *pac; /* PAC header + info buffer array */
+ krb5_data data; /* PAC data (including uninitialised header) */
+};
+
+static krb5_error_code
+k5_pac_locate_buffer(krb5_context context,
+ const krb5_pac pac,
+ krb5_ui_4 type,
+ krb5_data *data);
+
+/*
+ * Add a buffer to the provided PAC and update header.
+ */
+static krb5_error_code
+k5_pac_add_buffer(krb5_context context,
+ krb5_pac pac,
+ krb5_ui_4 type,
+ const krb5_data *data,
+ krb5_boolean zerofill,
+ krb5_data *out_data)
+{
+ PACTYPE *header;
+ size_t header_len, i, pad = 0;
+ char *pac_data;
+
+ assert((data->data == NULL) == zerofill);
+
+ /* Check there isn't already a buffer of this type */
+ if (k5_pac_locate_buffer(context, pac, type, NULL) == 0) {
+ return EINVAL;
+ }
+
+ header = (PACTYPE *)realloc(pac->pac,
+ sizeof(PACTYPE) +
+ (pac->pac->cBuffers * sizeof(PAC_INFO_BUFFER)));
+ if (header == NULL) {
+ return ENOMEM;
+ }
+ pac->pac = header;
+
+ header_len = PACTYPE_LENGTH + (pac->pac->cBuffers * PAC_INFO_BUFFER_LENGTH);
+
+ if (data->length % PAC_ALIGNMENT)
+ pad = PAC_ALIGNMENT - (data->length % PAC_ALIGNMENT);
+
+ pac_data = realloc(pac->data.data,
+ pac->data.length + PAC_INFO_BUFFER_LENGTH + data->length + pad);
+ if (pac_data == NULL) {
+ return ENOMEM;
+ }
+ pac->data.data = pac_data;
+
+ /* Update offsets of existing buffers */
+ for (i = 0; i < pac->pac->cBuffers; i++)
+ pac->pac->Buffers[i].Offset += PAC_INFO_BUFFER_LENGTH;
+
+ /* Make room for new PAC_INFO_BUFFER */
+ memmove(pac->data.data + header_len + PAC_INFO_BUFFER_LENGTH,
+ pac->data.data + header_len,
+ pac->data.length - header_len);
+ memset(pac->data.data + header_len, 0, PAC_INFO_BUFFER_LENGTH);
+
+ /* Initialise new PAC_INFO_BUFFER */
+ pac->pac->Buffers[i].ulType = type;
+ pac->pac->Buffers[i].cbBufferSize = data->length;
+ pac->pac->Buffers[i].Offset = pac->data.length + PAC_INFO_BUFFER_LENGTH;
+ assert((pac->pac->Buffers[i].Offset % PAC_ALIGNMENT) == 0);
+
+ /* Copy in new PAC data and zero padding bytes */
+ if (zerofill)
+ memset(pac->data.data + pac->pac->Buffers[i].Offset, 0, data->length);
+ else
+ memcpy(pac->data.data + pac->pac->Buffers[i].Offset, data->data, data->length);
+
+ memset(pac->data.data + pac->pac->Buffers[i].Offset + data->length, 0, pad);
+
+ pac->pac->cBuffers++;
+ pac->data.length += PAC_INFO_BUFFER_LENGTH + data->length + pad;
+
+ if (out_data != NULL) {
+ out_data->data = pac->data.data + pac->pac->Buffers[i].Offset;
+ out_data->length = data->length;
+ }
+
+ return 0;
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_pac_add_buffer(krb5_context context,
+ krb5_pac pac,
+ krb5_ui_4 type,
+ const krb5_data *data)
+{
+ return k5_pac_add_buffer(context, pac, type, data, FALSE, NULL);
+}
+
+/*
+ * Free a PAC
+ */
+void KRB5_CALLCONV
+krb5_pac_free(krb5_context context,
+ krb5_pac pac)
+{
+ if (pac != NULL) {
+ if (pac->data.data != NULL) {
+ memset(pac->data.data, 0, pac->data.length);
+ free(pac->data.data);
+ }
+ if (pac->pac != NULL)
+ free(pac->pac);
+ memset(pac, 0, sizeof(*pac));
+ free(pac);
+ }
+}
+
+static krb5_error_code
+k5_pac_locate_buffer(krb5_context context,
+ const krb5_pac pac,
+ krb5_ui_4 type,
+ krb5_data *data)
+{
+ PAC_INFO_BUFFER *buffer = NULL;
+ size_t i;
+
+ if (pac == NULL)
+ return EINVAL;
+
+ for (i = 0; i < pac->pac->cBuffers; i++) {
+ if (pac->pac->Buffers[i].ulType == type) {
+ if (buffer == NULL)
+ buffer = &pac->pac->Buffers[i];
+ else
+ return EINVAL;
+ }
+ }
+
+ if (buffer == NULL)
+ return ENOENT;
+
+ assert(buffer->Offset + buffer->cbBufferSize <= pac->data.length);
+
+ if (data != NULL) {
+ data->length = buffer->cbBufferSize;
+ data->data = pac->data.data + buffer->Offset;
+ }
+
+ return 0;
+}
+
+/*
+ * Find a buffer and copy data into output
+ */
+krb5_error_code KRB5_CALLCONV
+krb5_pac_get_buffer(krb5_context context,
+ krb5_pac pac,
+ krb5_ui_4 type,
+ krb5_data *data)
+{
+ krb5_data d;
+ krb5_error_code ret;
+
+ ret = k5_pac_locate_buffer(context, pac, type, &d);
+ if (ret != 0)
+ return ret;
+
+ data->data = malloc(d.length);
+ if (data == NULL)
+ return ENOMEM;
+
+ data->length = d.length;
+ memcpy(data->data, d.data, d.length);
+
+ return 0;
+}
+
+/*
+ * Return an array of the types of data in the PAC
+ */
+krb5_error_code KRB5_CALLCONV
+krb5_pac_get_types(krb5_context context,
+ krb5_pac pac,
+ size_t *len,
+ krb5_ui_4 **types)
+{
+ size_t i;
+
+ *types = (krb5_ui_4 *)malloc(pac->pac->cBuffers * sizeof(krb5_ui_4));
+ if (*types == NULL)
+ return ENOMEM;
+
+ *len = pac->pac->cBuffers;
+
+ for (i = 0; i < pac->pac->cBuffers; i++)
+ (*types)[i] = pac->pac->Buffers[i].ulType;
+
+ return 0;
+}
+
+/*
+ * Initialize PAC
+ */
+krb5_error_code KRB5_CALLCONV
+krb5_pac_init(krb5_context context,
+ krb5_pac *ppac)
+{
+ krb5_pac pac;
+
+ pac = (krb5_pac)malloc(sizeof(*pac));
+ if (pac == NULL)
+ return ENOMEM;
+
+ pac->pac = (PACTYPE *)malloc(sizeof(PACTYPE));
+ if (pac->pac == NULL) {
+ free( pac);
+ return ENOMEM;
+ }
+
+ pac->pac->cBuffers = 0;
+ pac->pac->Version = 0;
+
+ pac->data.length = PACTYPE_LENGTH;
+ pac->data.data = calloc(1, pac->data.length);
+ if (pac->data.data == NULL) {
+ krb5_pac_free(context, pac);
+ return ENOMEM;
+ }
+
+ *ppac = pac;
+
+ return 0;
+}
+
+/*
+ * Parse the supplied data into the PAC allocated by this function
+ */
+krb5_error_code KRB5_CALLCONV
+krb5_pac_parse(krb5_context context,
+ const void *ptr,
+ size_t len,
+ krb5_pac *ppac)
+{
+ krb5_error_code ret;
+ size_t i;
+ PACTYPE header;
+ const unsigned char *p = (const unsigned char *)ptr;
+ krb5_pac pac;
+ size_t header_len;
+
+ *ppac = NULL;
+
+ if (len < PACTYPE_LENGTH)
+ return ERANGE;
+
+ header.cBuffers = load_32_le(p);
+ p += 4;
+ header.Version = load_32_le(p);
+ p += 4;
+
+ if (header.Version != 0)
+ return EINVAL;
+
+ header_len = PACTYPE_LENGTH + (header.cBuffers * PAC_INFO_BUFFER_LENGTH);
+ if (len < header_len)
+ return ERANGE;
+
+ ret = krb5_pac_init(context, &pac);
+ if (ret != 0)
+ return ret;
+
+ pac->pac = (PACTYPE *)realloc(pac->pac,
+ sizeof(PACTYPE) + ((header.cBuffers - 1) * sizeof(PAC_INFO_BUFFER)));
+ if (pac->pac == NULL) {
+ krb5_pac_free(context, pac);
+ return ENOMEM;
+ }
+
+ memcpy(pac->pac, &header, sizeof(header));
+
+ for (i = 0; i < pac->pac->cBuffers; i++) {
+ PAC_INFO_BUFFER *buffer = &pac->pac->Buffers[i];
+
+ buffer->ulType = load_32_le(p);
+ p += 4;
+ buffer->cbBufferSize = load_32_le(p);
+ p += 4;
+ buffer->Offset = load_64_le(p);
+ p += 8;
+
+ if (buffer->Offset % PAC_ALIGNMENT) {
+ krb5_pac_free(context, pac);
+ return EINVAL;
+ }
+ if (buffer->Offset < header_len ||
+ buffer->Offset + buffer->cbBufferSize > len) {
+ krb5_pac_free(context, pac);
+ return ERANGE;
+ }
+ }
+
+ pac->data.data = realloc(pac->data.data, len);
+ if (pac->data.data == NULL) {
+ krb5_pac_free(context, pac);
+ return ENOMEM;
+ }
+ memcpy(pac->data.data, ptr, len);
+
+ pac->data.length = len;
+
+ *ppac = pac;
+
+ return 0;
+}
+
+static krb5_error_code
+k5_time_to_seconds_since_1970(krb5_ui_8 ntTime, krb5_timestamp *elapsedSeconds)
+{
+ krb5_ui_8 abstime;
+
+ ntTime /= 10000000;
+
+ abstime = time > 0 ? ntTime - NT_TIME_EPOCH : -ntTime;
+
+ if (abstime > KRB5_INT32_MAX)
+ return ERANGE;
+
+ *elapsedSeconds = abstime;
+
+ return 0;
+}
+
+static krb5_error_code
+k5_seconds_since_1970_to_time(krb5_timestamp elapsedSeconds, krb5_ui_8 *ntTime)
+{
+ *ntTime = elapsedSeconds;
+
+ if (elapsedSeconds > 0)
+ *ntTime += NT_TIME_EPOCH;
+
+ *ntTime *= 10000000;
+
+ return 0;
+}
+
+static krb5_error_code
+k5_pac_validate_client(krb5_context context,
+ const krb5_pac pac,
+ krb5_timestamp authtime,
+ krb5_const_principal principal)
+{
+ krb5_error_code ret;
+ krb5_data client_info;
+ char *pac_princname;
+ unsigned char *p;
+ krb5_timestamp pac_authtime;
+ krb5_ui_2 pac_princname_length;
+ krb5_ui_8 pac_nt_authtime;
+ krb5_principal pac_principal;
+
+ ret = k5_pac_locate_buffer(context, pac, PAC_CLIENT_INFO, &client_info);
+ if (ret != 0)
+ return ret;
+
+ if (client_info.length < PAC_CLIENT_INFO_LENGTH)
+ return ERANGE;
+
+ p = (unsigned char *)client_info.data;
+ pac_nt_authtime = load_64_le(p);
+ p += 8;
+ pac_princname_length = load_16_le(p);
+ p += 2;
+
+ ret = k5_time_to_seconds_since_1970(pac_nt_authtime, &pac_authtime);
+ if (ret != 0)
+ return ret;
+
+ if (client_info.length < PAC_CLIENT_INFO_LENGTH + pac_princname_length ||
+ pac_princname_length % 2)
+ return ERANGE;
+
+ ret = krb5int_ucs2lecs_to_utf8s(p, (size_t)pac_princname_length / 2, &pac_princname, NULL);
+ if (ret != 0)
+ return ret;
+
+ ret = krb5_parse_name_flags(context, pac_princname, 0, &pac_principal);
+ if (ret != 0) {
+ free(pac_princname);
+ return ret;
+ }
+
+ free(pac_princname);
+
+ if (pac_authtime != authtime ||
+ krb5_principal_compare(context, pac_principal, principal) == FALSE)
+ ret = KRB5KRB_AP_WRONG_PRINC;
+
+ krb5_free_principal(context, pac_principal);
+
+ return ret;
+}
+
+static krb5_error_code
+k5_pac_zero_signature(krb5_context context,
+ const krb5_pac pac,
+ krb5_ui_4 type,
+ krb5_data *data)
+{
+ PAC_INFO_BUFFER *buffer = NULL;
+ size_t i;
+
+ assert(type == PAC_SERVER_CHECKSUM || type == PAC_PRIVSVR_CHECKSUM);
+ assert(data->length >= pac->data.length);
+
+ for (i = 0; i < pac->pac->cBuffers; i++) {
+ if (pac->pac->Buffers[i].ulType == type) {
+ buffer = &pac->pac->Buffers[i];
+ break;
+ }
+ }
+
+ if (buffer == NULL)
+ return ENOENT;
+
+ if (buffer->Offset + buffer->cbBufferSize > pac->data.length)
+ return ERANGE;
+
+ if (buffer->cbBufferSize < PAC_SIGNATURE_DATA_LENGTH)
+ return KRB5_BAD_MSIZE;
+
+ /* Zero out the data portion of the checksum only */
+ memset(data->data + buffer->Offset + PAC_SIGNATURE_DATA_LENGTH,
+ 0,
+ buffer->cbBufferSize - PAC_SIGNATURE_DATA_LENGTH);
+
+ return 0;
+}
+
+static krb5_error_code
+k5_pac_verify_server_checksum(krb5_context context,
+ const krb5_pac pac,
+ const krb5_keyblock *server)
+{
+ krb5_error_code ret;
+ krb5_data pac_data; /* PAC with zeroed checksums */
+ krb5_checksum checksum;
+ krb5_data checksum_data;
+ krb5_boolean valid;
+ krb5_octet *p;
+
+ ret = k5_pac_locate_buffer(context, pac, PAC_SERVER_CHECKSUM, &checksum_data);
+ if (ret != 0)
+ return ret;
+
+ if (checksum_data.length < PAC_SIGNATURE_DATA_LENGTH)
+ return KRB5_BAD_MSIZE;
+
+ p = (krb5_octet *)checksum_data.data;
+ checksum.checksum_type = load_32_le(p);
+ checksum.length = checksum_data.length - PAC_SIGNATURE_DATA_LENGTH;
+ checksum.contents = p + PAC_SIGNATURE_DATA_LENGTH;
+
+ pac_data.length = pac->data.length;
+ pac_data.data = malloc(pac->data.length);
+ if (pac_data.data == NULL)
+ return ENOMEM;
+
+ memcpy(pac_data.data, pac->data.data, pac->data.length);
+
+ /* Zero out both checksum buffers */
+ ret = k5_pac_zero_signature(context, pac, PAC_SERVER_CHECKSUM, &pac_data);
+ if (ret != 0) {
+ free(pac_data.data);
+ return ret;
+ }
+
+ ret = k5_pac_zero_signature(context, pac, PAC_PRIVSVR_CHECKSUM, &pac_data);
+ if (ret != 0) {
+ free(pac_data.data);
+ return ret;
+ }
+
+ ret = krb5_c_verify_checksum(context, server, KRB5_KEYUSAGE_APP_DATA_CKSUM,
+ &pac_data, &checksum, &valid);
+ if (ret != 0) {
+ free(pac_data.data);
+ return ret;
+ }
+
+ if (valid == FALSE)
+ ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
+
+ return ret;
+}
+
+static krb5_error_code
+k5_pac_verify_kdc_checksum(krb5_context context,
+ const krb5_pac pac,
+ const krb5_keyblock *privsvr)
+{
+ krb5_error_code ret;
+ krb5_data server_checksum, privsvr_checksum;
+ krb5_checksum checksum;
+ krb5_boolean valid;
+ krb5_octet *p;
+
+ ret = k5_pac_locate_buffer(context, pac, PAC_PRIVSVR_CHECKSUM, &privsvr_checksum);
+ if (ret != 0)
+ return ret;
+
+ if (privsvr_checksum.length < PAC_SIGNATURE_DATA_LENGTH)
+ return KRB5_BAD_MSIZE;
+
+ ret = k5_pac_locate_buffer(context, pac, PAC_SERVER_CHECKSUM, &server_checksum);
+ if (ret != 0)
+ return ret;
+
+ if (server_checksum.length < PAC_SIGNATURE_DATA_LENGTH)
+ return KRB5_BAD_MSIZE;
+
+ p = (krb5_octet *)privsvr_checksum.data;
+ checksum.checksum_type = load_32_le(p);
+ checksum.length = privsvr_checksum.length - PAC_SIGNATURE_DATA_LENGTH;
+ checksum.contents = p + PAC_SIGNATURE_DATA_LENGTH;
+
+ server_checksum.data += PAC_SIGNATURE_DATA_LENGTH;
+ server_checksum.length -= PAC_SIGNATURE_DATA_LENGTH;
+
+ ret = krb5_c_verify_checksum(context, privsvr, KRB5_KEYUSAGE_APP_DATA_CKSUM,
+ &server_checksum, &checksum, &valid);
+ if (ret != 0)
+ return ret;
+
+ if (valid == FALSE)
+ ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
+
+ return ret;
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_pac_verify(krb5_context context,
+ const krb5_pac pac,
+ krb5_timestamp authtime,
+ krb5_const_principal principal,
+ const krb5_keyblock *server,
+ const krb5_keyblock *privsvr)
+{
+ krb5_error_code ret;
+
+ if (server == NULL)
+ return EINVAL;
+
+ ret = k5_pac_verify_server_checksum(context, pac, server);
+ if (ret != 0)
+ return ret;
+
+ if (privsvr != NULL) {
+ ret = k5_pac_verify_kdc_checksum(context, pac, privsvr);
+ if (ret != 0)
+ return ret;
+ }
+
+ if (principal != NULL) {
+ ret = k5_pac_validate_client(context, pac, authtime, principal);
+ if (ret != 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static krb5_error_code
+k5_insert_client_info(krb5_context context,
+ krb5_pac pac,
+ krb5_timestamp authtime,
+ krb5_const_principal principal)
+{
+ krb5_error_code ret;
+ krb5_data client_info;
+ char *princ_name_utf8 = NULL;
+ unsigned char *princ_name_ucs2 = NULL, *p;
+ size_t princ_name_ucs2_len = 0;
+ krb5_ui_8 nt_authtime;
+
+ /* If we already have a CLIENT_INFO buffer, then just validate it */
+ if (k5_pac_locate_buffer(context, pac, PAC_CLIENT_INFO, &client_info) == 0) {
+ return k5_pac_validate_client(context, pac, authtime, principal);
+ }
+
+ ret = krb5_unparse_name_flags(context, principal,
+ KRB5_PRINCIPAL_UNPARSE_NO_REALM, &princ_name_utf8);
+ if (ret != 0)
+ goto cleanup;
+
+ ret = krb5int_utf8s_to_ucs2les(princ_name_utf8,
+ &princ_name_ucs2,
+ &princ_name_ucs2_len);
+ if (ret != 0)
+ goto cleanup;
+
+ client_info.length = PAC_CLIENT_INFO_LENGTH + princ_name_ucs2_len;
+ client_info.data = NULL;
+
+ ret = k5_pac_add_buffer(context, pac, PAC_CLIENT_INFO, &client_info, TRUE, &client_info);
+ if (ret != 0)
+ goto cleanup;
+
+ p = (unsigned char *)client_info.data;
+
+ /* copy in authtime converted to a 64-bit NT time */
+ k5_seconds_since_1970_to_time(authtime, &nt_authtime);
+ store_64_le(nt_authtime, p);
+ p += 8;
+
+ /* copy in number of UCS-2 characters in principal name */
+ store_16_le(princ_name_ucs2_len, p);
+ p += 2;
+
+ /* copy in principal name */
+ memcpy(p, princ_name_ucs2, princ_name_ucs2_len);
+
+cleanup:
+ if (princ_name_utf8 != NULL)
+ free(princ_name_utf8);
+ if (princ_name_ucs2 != NULL)
+ free(princ_name_ucs2);
+
+ return ret;
+}
+
+static krb5_error_code
+k5_insert_checksum(krb5_context context,
+ krb5_pac pac,
+ krb5_ui_4 type,
+ const krb5_keyblock *key,
+ krb5_cksumtype *cksumtype)
+{
+ krb5_error_code ret;
+ size_t len;
+ krb5_data cksumdata;
+
+ ret = krb5int_c_mandatory_cksumtype(context, key->enctype, cksumtype);
+ if (ret != 0)
+ return ret;
+
+ ret = krb5_c_checksum_length(context, *cksumtype, &len);
+ if (ret != 0)
+ return ret;
+
+ ret = k5_pac_locate_buffer(context, pac, type, &cksumdata);
+ if (ret == 0) {
+ /* If we're resigning PAC, make sure we can fit checksum into existing buffer */
+ if (cksumdata.length != PAC_SIGNATURE_DATA_LENGTH + len)
+ return ERANGE;
+
+ memset(cksumdata.data, 0, cksumdata.length);
+ } else {
+ /* Add a zero filled buffer */
+ cksumdata.length = PAC_SIGNATURE_DATA_LENGTH + len;
+ cksumdata.data = NULL;
+
+ ret = k5_pac_add_buffer(context, pac, type, &cksumdata, TRUE, &cksumdata);
+ if (ret != 0)
+ return ret;
+ }
+
+ /* Encode checksum type into buffer */
+ store_32_le((krb5_ui_4)*cksumtype, (unsigned char *)cksumdata.data);
+
+ return 0;
+}
+
+/* in-place encoding of PAC header */
+static krb5_error_code
+k5_pac_encode_header(krb5_context context, krb5_pac pac)
+{
+ size_t i;
+ unsigned char *p;
+ size_t header_len;
+
+ header_len = PACTYPE_LENGTH + (pac->pac->cBuffers * PAC_INFO_BUFFER_LENGTH);
+ assert(pac->data.length >= header_len);
+
+ p = (unsigned char *)pac->data.data;
+
+ store_32_le(pac->pac->cBuffers, p);
+ p += 4;
+ store_32_le(pac->pac->Version, p);
+ p += 4;
+
+ for (i = 0; i < pac->pac->cBuffers; i++) {
+ PAC_INFO_BUFFER *buffer = &pac->pac->Buffers[i];
+
+ store_32_le(buffer->ulType, p);
+ p += 4;
+ store_32_le(buffer->cbBufferSize, p);
+ p += 4;
+ store_64_le(buffer->Offset, p);
+ p += 8;
+
+ assert((buffer->Offset % PAC_ALIGNMENT) == 0);
+ assert(buffer->Offset + buffer->cbBufferSize <= pac->data.length);
+ assert(buffer->Offset >= header_len);
+
+ if (buffer->Offset % PAC_ALIGNMENT ||
+ buffer->Offset + buffer->cbBufferSize > pac->data.length ||
+ buffer->Offset < header_len)
+ return ERANGE;
+ }
+
+ return 0;
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5int_pac_sign(krb5_context context,
+ krb5_pac pac,
+ krb5_timestamp authtime,
+ krb5_const_principal principal,
+ const krb5_keyblock *server_key,
+ const krb5_keyblock *privsvr_key,
+ krb5_data *data)
+{
+ krb5_error_code ret;
+ krb5_data server_cksum, privsvr_cksum;
+ krb5_cksumtype server_cksumtype, privsvr_cksumtype;
+ krb5_crypto_iov iov[2];
+
+ data->length = 0;
+ data->data = NULL;
+
+ if (principal != NULL) {
+ ret = k5_insert_client_info(context, pac, authtime, principal);
+ if (ret != 0)
+ return ret;
+ }
+
+ /* Create zeroed buffers for both checksums */
+ ret = k5_insert_checksum(context, pac, PAC_SERVER_CHECKSUM,
+ server_key, &server_cksumtype);
+ if (ret != 0)
+ return ret;
+
+ ret = k5_insert_checksum(context, pac, PAC_PRIVSVR_CHECKSUM,
+ privsvr_key, &privsvr_cksumtype);
+ if (ret != 0)
+ return ret;
+
+ /* Now, encode the PAC header so that the checksums will include it */
+ ret = k5_pac_encode_header(context, pac);
+ if (ret != 0)
+ return ret;
+
+ /* Generate the server checksum over the entire PAC */
+ ret = k5_pac_locate_buffer(context, pac, PAC_SERVER_CHECKSUM, &server_cksum);
+ if (ret != 0)
+ return ret;
+
+ assert(server_cksum.length > PAC_SIGNATURE_DATA_LENGTH);
+
+ iov[0].flags = KRB5_CRYPTO_TYPE_DATA;
+ iov[0].data = pac->data;
+
+ iov[1].flags = KRB5_CRYPTO_TYPE_CHECKSUM;
+ iov[1].data.data = server_cksum.data + PAC_SIGNATURE_DATA_LENGTH;
+ iov[1].data.length = server_cksum.length - PAC_SIGNATURE_DATA_LENGTH;
+
+ ret = krb5_c_make_checksum_iov(context, server_cksumtype,
+ server_key, KRB5_KEYUSAGE_APP_DATA_CKSUM,
+ iov, sizeof(iov)/sizeof(iov[0]));
+ if (ret != 0)
+ return ret;
+
+ /* Generate the privsvr checksum over the server checksum buffer */
+ ret = k5_pac_locate_buffer(context, pac, PAC_PRIVSVR_CHECKSUM, &privsvr_cksum);
+ if (ret != 0)
+ return ret;
+
+ assert(privsvr_cksum.length > PAC_SIGNATURE_DATA_LENGTH);
+
+ iov[0].flags = KRB5_CRYPTO_TYPE_DATA;
+ iov[0].data.data = server_cksum.data + PAC_SIGNATURE_DATA_LENGTH;
+ iov[0].data.length = server_cksum.length - PAC_SIGNATURE_DATA_LENGTH;
+
+ iov[1].flags = KRB5_CRYPTO_TYPE_CHECKSUM;
+ iov[1].data.data = privsvr_cksum.data + PAC_SIGNATURE_DATA_LENGTH;
+ iov[1].data.length = privsvr_cksum.length - PAC_SIGNATURE_DATA_LENGTH;
+
+ ret = krb5_c_make_checksum_iov(context, privsvr_cksumtype,
+ privsvr_key, KRB5_KEYUSAGE_APP_DATA_CKSUM,
+ iov, sizeof(iov)/sizeof(iov[0]));
+ if (ret != 0)
+ return ret;
+
+ data->data = malloc(pac->data.length);
+ if (data->data == NULL)
+ return ENOMEM;
+
+ data->length = pac->data.length;
+
+ memcpy(data->data, pac->data.data, pac->data.length);
+ memset(pac->data.data, 0, PACTYPE_LENGTH + (pac->pac->cBuffers * PAC_INFO_BUFFER_LENGTH));
+
+ return 0;
+}
+
diff --git a/src/lib/krb5/krb/parse.c b/src/lib/krb5/krb/parse.c
index c6b1f6ebe6..5c705490d3 100644
--- a/src/lib/krb5/krb/parse.c
+++ b/src/lib/krb5/krb/parse.c
@@ -1,7 +1,7 @@
/*
* lib/krb5/krb/parse.c
*
- * Copyright 1990,1991 by the Massachusetts Institute of Technology.
+ * Copyright 1990,1991,2008 by the Massachusetts Institute of Technology.
* All Rights Reserved.
*
* Export of this software from the United States of America may
@@ -59,17 +59,17 @@
#define FCOMPNUM 10
-
/*
* May the fleas of a thousand camels infest the ISO, they who think
* that arbitrarily large multi-component names are a Good Thing.....
*/
-krb5_error_code KRB5_CALLCONV
-krb5_parse_name(krb5_context context, const char *name, krb5_principal *nprincipal)
+static krb5_error_code
+k5_parse_name(krb5_context context, const char *name,
+ int flags, krb5_principal *nprincipal)
{
register const char *cp;
register char *q;
- register int i,c,size;
+ register int i,c,size;
int components = 0;
const char *parsed_realm = NULL;
int fcompsize[FCOMPNUM];
@@ -79,24 +79,28 @@ krb5_parse_name(krb5_context context, const char *name, krb5_principal *nprincip
char *tmpdata;
krb5_principal principal;
krb5_error_code retval;
-
+ unsigned int enterprise = (flags & KRB5_PRINCIPAL_PARSE_ENTERPRISE);
+ int first_at;
+
/*
* Pass 1. Find out how many components there are to the name,
- * and get string sizes for the first FCOMPNUM components.
+ * and get string sizes for the first FCOMPNUM components. For
+ * enterprise principal names (UPNs), there is only a single
+ * component.
*/
size = 0;
- for (i=0,cp = name; (c = *cp); cp++) {
+ for (i=0,cp = name, first_at = 1; (c = *cp); cp++) {
if (c == QUOTECHAR) {
cp++;
if (!(c = *cp))
/*
- * QUOTECHAR can't be at the last
- * character of the name!
- */
+ * QUOTECHAR can't be at the last
+ * character of the name!
+ */
return(KRB5_PARSE_MALFORMED);
size++;
continue;
- } else if (c == COMPONENT_SEP) {
+ } else if (c == COMPONENT_SEP && !enterprise) {
if (parsed_realm)
/*
* Shouldn't see a component separator
@@ -108,22 +112,26 @@ krb5_parse_name(krb5_context context, const char *name, krb5_principal *nprincip
}
size = 0;
i++;
- } else if (c == REALM_SEP) {
+ } else if (c == REALM_SEP && (!enterprise || !first_at)) {
if (parsed_realm)
/*
* Multiple realm separaters
* not allowed; zero-length realms are.
*/
return(KRB5_PARSE_MALFORMED);
- parsed_realm = cp+1;
+ parsed_realm = cp + 1;
if (i < FCOMPNUM) {
fcompsize[i] = size;
}
size = 0;
- } else
+ } else {
+ if (c == REALM_SEP && enterprise && first_at)
+ first_at = 0;
+
size++;
+ }
}
- if (parsed_realm)
+ if (parsed_realm != NULL)
realmsize = size;
else if (i < FCOMPNUM)
fcompsize[i] = size;
@@ -133,20 +141,30 @@ krb5_parse_name(krb5_context context, const char *name, krb5_principal *nprincip
* component pieces
*/
principal = (krb5_principal)malloc(sizeof(krb5_principal_data));
- if (!principal) {
- return(ENOMEM);
+ if (principal == NULL) {
+ return(ENOMEM);
}
principal->data = (krb5_data *) malloc(sizeof(krb5_data) * components);
- if (!principal->data) {
- free((char *)principal);
+ if (principal->data == NULL) {
+ krb5_xfree((char *)principal);
return ENOMEM;
}
principal->length = components;
+
/*
- * If a realm was not found, then use the defualt realm....
+ * If a realm was not found, then use the default realm, unless
+ * KRB5_PRINCIPAL_PARSE_NO_REALM was specified in which case the
+ * realm will be empty.
*/
if (!parsed_realm) {
- if (!default_realm) {
+ if (flags & KRB5_PRINCIPAL_PARSE_REQUIRE_REALM) {
+ krb5_set_error_message(context, KRB5_PARSE_MALFORMED,
+ "Principal %s is missing required realm", name);
+ krb5_xfree(principal->data);
+ krb5_xfree(principal);
+ return KRB5_PARSE_MALFORMED;
+ }
+ if (!default_realm && (flags & KRB5_PRINCIPAL_PARSE_NO_REALM) == 0) {
retval = krb5_get_default_realm(context, &default_realm);
if (retval) {
krb5_xfree(principal->data);
@@ -156,7 +174,14 @@ krb5_parse_name(krb5_context context, const char *name, krb5_principal *nprincip
default_realm_size = strlen(default_realm);
}
realmsize = default_realm_size;
+ } else if (flags & KRB5_PRINCIPAL_PARSE_NO_REALM) {
+ krb5_set_error_message(context, KRB5_PARSE_MALFORMED,
+ "Principal %s has realm present", name);
+ krb5_xfree(principal->data);
+ krb5_xfree(principal);
+ return KRB5_PARSE_MALFORMED;
}
+
/*
* Pass 2. Happens only if there were more than FCOMPNUM
* component; if this happens, someone should be shot
@@ -208,7 +233,7 @@ krb5_parse_name(krb5_context context, const char *name, krb5_principal *nprincip
/*
* Now, we need to allocate the space for the strings themselves.....
*/
- tmpdata = malloc(realmsize+1);
+ tmpdata = malloc(realmsize + 1);
if (tmpdata == 0) {
krb5_xfree(principal->data);
krb5_xfree(principal);
@@ -220,7 +245,7 @@ krb5_parse_name(krb5_context context, const char *name, krb5_principal *nprincip
for (i=0; i < components; i++) {
char *tmpdata2 =
malloc(krb5_princ_component(context, principal, i)->length + 1);
- if (!tmpdata2) {
+ if (tmpdata2 == NULL) {
for (i--; i >= 0; i--)
krb5_xfree(krb5_princ_component(context, principal, i)->data);
krb5_xfree(krb5_princ_realm(context, principal)->data);
@@ -239,7 +264,7 @@ krb5_parse_name(krb5_context context, const char *name, krb5_principal *nprincip
* allocated.
*/
q = krb5_princ_component(context, principal, 0)->data;
- for (i=0,cp = name; (c = *cp); cp++) {
+ for (i=0,cp = name, first_at = 1; (c = *cp); cp++) {
if (c == QUOTECHAR) {
cp++;
switch (c = *cp) {
@@ -257,29 +282,57 @@ krb5_parse_name(krb5_context context, const char *name, krb5_principal *nprincip
break;
default:
*q++ = c;
+ break;
}
- } else if ((c == COMPONENT_SEP) || (c == REALM_SEP)) {
+ } else if (c == COMPONENT_SEP && !enterprise) {
+ i++;
+ *q++ = '\0';
+ q = krb5_princ_component(context, principal, i)->data;
+ } else if (c == REALM_SEP && (!enterprise || !first_at)) {
i++;
*q++ = '\0';
- if (c == COMPONENT_SEP)
- q = krb5_princ_component(context, principal, i)->data;
- else
- q = krb5_princ_realm(context, principal)->data;
- } else
+ q = krb5_princ_realm(context, principal)->data;
+ } else {
+ if (c == REALM_SEP && enterprise && first_at)
+ first_at = 0;
+
*q++ = c;
+ }
}
*q++ = '\0';
- if (!parsed_realm)
- strlcpy(krb5_princ_realm(context, principal)->data, default_realm, realmsize + 1);
+ if (!parsed_realm) {
+ if (flags & KRB5_PRINCIPAL_PARSE_NO_REALM)
+ (krb5_princ_realm(context, principal)->data)[0] = '\0';
+ else
+ strlcpy(krb5_princ_realm(context, principal)->data, default_realm, realmsize+1);
+ }
/*
* Alright, we're done. Now stuff a pointer to this monstrosity
* into the return variable, and let's get out of here.
*/
- krb5_princ_type(context, principal) = KRB5_NT_PRINCIPAL;
+ if (enterprise)
+ krb5_princ_type(context, principal) = KRB5_NT_ENTERPRISE_PRINCIPAL;
+ else
+ krb5_princ_type(context, principal) = KRB5_NT_PRINCIPAL;
principal->magic = KV5M_PRINCIPAL;
principal->realm.magic = KV5M_DATA;
*nprincipal = principal;
- krb5_xfree(default_realm);
+ if (default_realm != NULL)
+ krb5_xfree(default_realm);
+
return(0);
}
+
+krb5_error_code KRB5_CALLCONV
+krb5_parse_name(krb5_context context, const char *name, krb5_principal *nprincipal)
+{
+ return k5_parse_name(context, name, 0, nprincipal);
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_parse_name_flags(krb5_context context, const char *name,
+ int flags, krb5_principal *nprincipal)
+{
+ return k5_parse_name(context, name, flags, nprincipal);
+}
diff --git a/src/lib/krb5/krb/princ_comp.c b/src/lib/krb5/krb/princ_comp.c
index 6e033ad918..46e00e48d1 100644
--- a/src/lib/krb5/krb/princ_comp.c
+++ b/src/lib/krb5/krb/princ_comp.c
@@ -29,37 +29,116 @@
*/
#include "k5-int.h"
+#include "k5-unicode.h"
+
+static krb5_boolean
+realm_compare_flags(krb5_context context,
+ krb5_const_principal princ1,
+ krb5_const_principal princ2,
+ int flags)
+{
+ const krb5_data *realm1 = krb5_princ_realm(context, princ1);
+ const krb5_data *realm2 = krb5_princ_realm(context, princ2);
+
+ if (realm1->length != realm2->length)
+ return FALSE;
+
+ return (flags & KRB5_PRINCIPAL_COMPARE_CASEFOLD) ?
+ (strncasecmp(realm1->data, realm2->data, realm2->length) == 0) :
+ (memcmp(realm1->data, realm2->data, realm2->length) == 0);
+}
krb5_boolean KRB5_CALLCONV
krb5_realm_compare(krb5_context context, krb5_const_principal princ1, krb5_const_principal princ2)
{
- if (!data_eq(*krb5_princ_realm(context, princ1),
- *krb5_princ_realm(context, princ2)))
- return FALSE;
+ return realm_compare_flags(context, princ1, princ2, 0);
+}
- return TRUE;
+static krb5_error_code
+upn_to_principal(krb5_context context,
+ krb5_const_principal princ,
+ krb5_principal *upn)
+{
+ char *unparsed_name;
+ krb5_error_code code;
+
+ code = krb5_unparse_name_flags(context, princ,
+ KRB5_PRINCIPAL_UNPARSE_NO_REALM,
+ &unparsed_name);
+ if (code) {
+ *upn = NULL;
+ return code;
+ }
+
+ code = krb5_parse_name(context, unparsed_name, upn);
+
+ free(unparsed_name);
+
+ return code;
}
krb5_boolean KRB5_CALLCONV
-krb5_principal_compare(krb5_context context, krb5_const_principal princ1, krb5_const_principal princ2)
+krb5_principal_compare_flags(krb5_context context,
+ krb5_const_principal princ1,
+ krb5_const_principal princ2,
+ int flags)
{
register int i;
krb5_int32 nelem;
+ unsigned int utf8 = (flags & KRB5_PRINCIPAL_COMPARE_UTF8) != 0;
+ unsigned int casefold = (flags & KRB5_PRINCIPAL_COMPARE_CASEFOLD) != 0;
+ krb5_principal upn1 = NULL;
+ krb5_principal upn2 = NULL;
+ krb5_boolean ret = FALSE;
+
+ if (flags & KRB5_PRINCIPAL_COMPARE_ENTERPRISE) {
+ /* Treat UPNs as if they were real principals */
+ if (krb5_princ_type(context, princ1) == KRB5_NT_ENTERPRISE_PRINCIPAL) {
+ if (upn_to_principal(context, princ1, &upn1) == 0)
+ princ1 = upn1;
+ }
+ if (krb5_princ_type(context, princ2) == KRB5_NT_ENTERPRISE_PRINCIPAL) {
+ if (upn_to_principal(context, princ2, &upn2) == 0)
+ princ2 = upn2;
+ }
+ }
nelem = krb5_princ_size(context, princ1);
if (nelem != krb5_princ_size(context, princ2))
- return FALSE;
+ goto out;
- if (! krb5_realm_compare(context, princ1, princ2))
- return FALSE;
+ if ((flags & KRB5_PRINCIPAL_COMPARE_IGNORE_REALM) == 0 &&
+ !realm_compare_flags(context, princ1, princ2, flags))
+ goto out;
for (i = 0; i < (int) nelem; i++) {
register const krb5_data *p1 = krb5_princ_component(context, princ1, i);
register const krb5_data *p2 = krb5_princ_component(context, princ2, i);
- if (!data_eq(*p1, *p2))
- return FALSE;
+ int cmp;
+
+ if (casefold) {
+ if (utf8)
+ cmp = krb5int_utf8_normcmp(p1, p2, KRB5_UTF8_CASEFOLD);
+ else
+ cmp = p1->length == p2->length ?
+ strncasecmp(p1->data, p2->data, p2->length) :
+ p1->length - p2->length;
+ } else
+ cmp = !data_eq(*p1, *p2);
+
+ if (cmp != 0)
+ goto out;
}
- return TRUE;
+
+ ret = TRUE;
+
+out:
+ if (upn1 != NULL)
+ krb5_free_principal(context, upn1);
+ if (upn2 != NULL)
+ krb5_free_principal(context, upn2);
+
+ return ret;
}
krb5_boolean KRB5_CALLCONV krb5_is_referral_realm(const krb5_data *r)
@@ -81,3 +160,20 @@ krb5_boolean KRB5_CALLCONV krb5_is_referral_realm(const krb5_data *r)
else
return FALSE;
}
+
+krb5_boolean KRB5_CALLCONV
+krb5_principal_compare(krb5_context context,
+ krb5_const_principal princ1,
+ krb5_const_principal princ2)
+{
+ return krb5_principal_compare_flags(context, princ1, princ2, 0);
+}
+
+krb5_boolean KRB5_CALLCONV
+krb5_principal_compare_any_realm(krb5_context context,
+ krb5_const_principal princ1,
+ krb5_const_principal princ2)
+{
+ return krb5_principal_compare_flags(context, princ1, princ2, KRB5_PRINCIPAL_COMPARE_IGNORE_REALM);
+}
+
diff --git a/src/lib/krb5/krb/rd_priv.c b/src/lib/krb5/krb/rd_priv.c
index 66cee85381..618726efe2 100644
--- a/src/lib/krb5/krb/rd_priv.c
+++ b/src/lib/krb5/krb/rd_priv.c
@@ -265,7 +265,9 @@ krb5_rd_priv(krb5_context context, krb5_auth_context auth_context,
error:;
krb5_xfree(outbuf->data);
- return retval;
+ outbuf->length = 0;
+ outbuf->data = NULL;
+ return retval;
}
diff --git a/src/lib/krb5/krb/rd_rep.c b/src/lib/krb5/krb/rd_rep.c
index 901de4338a..1e6e0e1e82 100644
--- a/src/lib/krb5/krb/rd_rep.c
+++ b/src/lib/krb5/krb/rd_rep.c
@@ -26,6 +26,33 @@
*
* krb5_rd_rep()
*/
+/*
+ * Copyright (c) 2006-2008, Novell, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * The copyright holder's name is not used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
#include "k5-int.h"
#include "auth_con.h"
@@ -102,6 +129,8 @@ krb5_rd_rep(krb5_context context, krb5_auth_context auth_context,
krb5_free_keyblock(context, auth_context->send_subkey);
auth_context->send_subkey = NULL;
}
+ /* not used for anything yet */
+ auth_context->negotiated_etype = (*repl)->subkey->enctype;
}
/* Get remote sequence number */
@@ -114,3 +143,60 @@ clean_scratch:
free(scratch.data);
return retval;
}
+
+krb5_error_code KRB5_CALLCONV
+krb5_rd_rep_dce(krb5_context context, krb5_auth_context auth_context,
+ const krb5_data *inbuf, krb5_ui_4 *nonce)
+{
+ krb5_error_code retval;
+ krb5_ap_rep * reply;
+ krb5_data scratch;
+ krb5_ap_rep_enc_part *repl;
+
+ if (!krb5_is_ap_rep(inbuf))
+ return KRB5KRB_AP_ERR_MSG_TYPE;
+
+ /* decode it */
+
+ if ((retval = decode_krb5_ap_rep(inbuf, &reply)))
+ return retval;
+
+ /* put together an eblock for this encryption */
+
+ scratch.length = reply->enc_part.ciphertext.length;
+ if (!(scratch.data = malloc(scratch.length))) {
+ krb5_free_ap_rep(context, reply);
+ return(ENOMEM);
+ }
+
+ if ((retval = krb5_c_decrypt(context, auth_context->keyblock,
+ KRB5_KEYUSAGE_AP_REP_ENCPART, 0,
+ &reply->enc_part, &scratch)))
+ goto clean_scratch;
+
+ /* now decode the decrypted stuff */
+ retval = decode_krb5_ap_rep_enc_part(&scratch, &repl);
+ if (retval)
+ goto clean_scratch;
+
+ *nonce = repl->seq_number;
+ if (*nonce != auth_context->local_seq_number) {
+ retval = KRB5_MUTUAL_FAILED;
+ goto clean_scratch;
+ }
+
+ /* Must be NULL to prevent echoing for client AP-REP */
+ if (repl->subkey != NULL) {
+ retval = KRB5_MUTUAL_FAILED;
+ goto clean_scratch;
+ }
+
+clean_scratch:
+ memset(scratch.data, 0, scratch.length);
+
+ if (repl != NULL)
+ krb5_free_ap_rep_enc_part(context, repl);
+ krb5_free_ap_rep(context, reply);
+ free(scratch.data);
+ return retval;
+}
diff --git a/src/lib/krb5/krb/rd_req.c b/src/lib/krb5/krb/rd_req.c
index 6a479496fa..5848aa776f 100644
--- a/src/lib/krb5/krb/rd_req.c
+++ b/src/lib/krb5/krb/rd_req.c
@@ -77,19 +77,6 @@ krb5_rd_req(krb5_context context, krb5_auth_context *auth_context,
*auth_context = new_auth_context;
}
- if (!server) {
- server = request->ticket->server;
- }
- /* Get an rcache if necessary. */
- if (((*auth_context)->rcache == NULL)
- && ((*auth_context)->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME)
- && server) {
- if ((retval = krb5_get_server_rcache(context,
- krb5_princ_component(context,
- server,0),
- &(*auth_context)->rcache)))
- goto cleanup_auth_context;
- }
#ifndef LEAN_CLIENT
/* Get a keytab if necessary. */
diff --git a/src/lib/krb5/krb/rd_req_dec.c b/src/lib/krb5/krb/rd_req_dec.c
index b81266d264..bbf7ed6a72 100644
--- a/src/lib/krb5/krb/rd_req_dec.c
+++ b/src/lib/krb5/krb/rd_req_dec.c
@@ -62,6 +62,19 @@
static krb5_error_code decrypt_authenticator
(krb5_context, const krb5_ap_req *, krb5_authenticator **,
int);
+static krb5_error_code
+decode_etype_list(krb5_context context,
+ const krb5_authenticator *authp,
+ krb5_enctype **desired_etypes,
+ int *desired_etypes_len);
+static krb5_error_code
+negotiate_etype(krb5_context context,
+ const krb5_enctype *desired_etypes,
+ int desired_etypes_len,
+ int mandatory_etypes_index,
+ const krb5_enctype *permitted_etypes,
+ int permitted_etypes_len,
+ krb5_enctype *negotiated_etype);
krb5_error_code
krb5int_check_clockskew(krb5_context context, krb5_timestamp date)
@@ -79,27 +92,83 @@ krb5int_check_clockskew(krb5_context context, krb5_timestamp date)
static krb5_error_code
krb5_rd_req_decrypt_tkt_part(krb5_context context, const krb5_ap_req *req,
- krb5_keytab keytab)
+ krb5_const_principal server, krb5_keytab keytab)
{
krb5_error_code retval;
- krb5_enctype enctype;
krb5_keytab_entry ktent;
- enctype = req->ticket->enc_part.enctype;
+ retval = KRB5_KT_NOTFOUND;
#ifndef LEAN_CLIENT
- if ((retval = krb5_kt_get_entry(context, keytab, req->ticket->server,
- req->ticket->enc_part.kvno,
- enctype, &ktent)))
- return retval;
+ if (server != NULL || keytab->ops->start_seq_get == NULL) {
+ retval = krb5_kt_get_entry(context, keytab,
+ server != NULL ? server : req->ticket->server,
+ req->ticket->enc_part.kvno,
+ req->ticket->enc_part.enctype, &ktent);
+ if (retval == 0) {
+ retval = krb5_decrypt_tkt_part(context, &ktent.key, req->ticket);
+
+ (void) krb5_free_keytab_entry_contents(context, &ktent);
+ }
+ } else {
+ krb5_error_code code;
+ krb5_kt_cursor cursor;
+
+ retval = krb5_kt_start_seq_get(context, keytab, &cursor);
+ if (retval != 0)
+ goto map_error;
+
+ while ((code = krb5_kt_next_entry(context, keytab,
+ &ktent, &cursor)) == 0) {
+ if (ktent.key.enctype != req->ticket->enc_part.enctype)
+ continue;
+
+ retval = krb5_decrypt_tkt_part(context, &ktent.key,
+ req->ticket);
+
+ if (retval == 0) {
+ krb5_principal tmp;
+
+ /*
+ * We overwrite ticket->server to be the principal
+ * that we match in the keytab. The reason for doing
+ * this is that GSS-API and other consumers look at
+ * that principal to make authorization decisions
+ * about whether the appropriate server is contacted.
+ * It might be cleaner to create a new API and store
+ * the server in the auth_context, but doing so would
+ * probably miss existing uses of the server. Instead,
+ * perhaps an API should be created to retrieve the
+ * server as it appeared in the ticket.
+ */
+ retval = krb5_copy_principal(context, ktent.principal, &tmp);
+ if (retval == 0) {
+ krb5_free_principal(context, req->ticket->server);
+ req->ticket->server = tmp;
+ }
+ (void) krb5_free_keytab_entry_contents(context, &ktent);
+ break;
+ }
+ (void) krb5_free_keytab_entry_contents(context, &ktent);
+ }
+
+ code = krb5_kt_end_seq_get(context, keytab, &cursor);
+ if (code != 0)
+ retval = code;
+ }
#endif /* LEAN_CLIENT */
- retval = krb5_decrypt_tkt_part(context, &ktent.key, req->ticket);
- /* Upon error, Free keytab entry first, then return */
+map_error:
+ switch (retval) {
+ case KRB5_KT_KVNONOTFOUND:
+ case KRB5_KT_NOTFOUND:
+ case KRB5KRB_AP_ERR_BAD_INTEGRITY:
+ retval = KRB5KRB_AP_WRONG_PRINC;
+ break;
+ default:
+ break;
+ }
-#ifndef LEAN_CLIENT
- (void) krb5_kt_free_entry(context, &ktent);
-#endif /* LEAN_CLIENT */
return retval;
}
@@ -134,8 +203,13 @@ krb5_rd_req_decoded_opt(krb5_context context, krb5_auth_context *auth_context,
krb5_ticket **ticket, int check_valid_flag)
{
krb5_error_code retval = 0;
- krb5_principal_data princ_data;
-
+ krb5_principal_data princ_data;
+ krb5_enctype *desired_etypes = NULL;
+ int desired_etypes_len = 0;
+ int rfc4537_etypes_len = 0;
+ krb5_enctype *permitted_etypes = NULL;
+ int permitted_etypes_len = 0;
+
req->ticket->enc_part2 = NULL;
if (server && krb5_is_referral_realm(&server->realm)) {
char *realm;
@@ -147,18 +221,7 @@ krb5_rd_req_decoded_opt(krb5_context context, krb5_auth_context *auth_context,
princ_data.realm.data = realm;
princ_data.realm.length = strlen(realm);
}
- if (server && !krb5_principal_compare(context, server, req->ticket->server)) {
- char *found_name = 0, *wanted_name = 0;
- if (krb5_unparse_name(context, server, &wanted_name) == 0
- && krb5_unparse_name(context, req->ticket->server, &found_name) == 0)
- krb5_set_error_message(context, KRB5KRB_AP_WRONG_PRINC,
- "Wrong principal in request (found %s, wanted %s)",
- found_name, wanted_name);
- krb5_free_unparsed_name(context, wanted_name);
- krb5_free_unparsed_name(context, found_name);
- retval = KRB5KRB_AP_WRONG_PRINC;
- goto cleanup;
- }
+
/* if (req->ap_options & AP_OPTS_USE_SESSION_KEY)
do we need special processing here ? */
@@ -171,7 +234,7 @@ krb5_rd_req_decoded_opt(krb5_context context, krb5_auth_context *auth_context,
krb5_free_keyblock(context, (*auth_context)->keyblock);
(*auth_context)->keyblock = NULL;
} else {
- if ((retval = krb5_rd_req_decrypt_tkt_part(context, req, keytab)))
+ if ((retval = krb5_rd_req_decrypt_tkt_part(context, req, server, keytab)))
goto cleanup;
}
@@ -197,6 +260,19 @@ krb5_rd_req_decoded_opt(krb5_context context, krb5_auth_context *auth_context,
goto cleanup;
}
+ if (!server) {
+ server = req->ticket->server;
+ }
+ /* Get an rcache if necessary. */
+ if (((*auth_context)->rcache == NULL)
+ && ((*auth_context)->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME)
+ && server) {
+ if ((retval = krb5_get_server_rcache(context,
+ krb5_princ_component(context,
+ server,0),
+ &(*auth_context)->rcache)))
+ goto cleanup;
+ }
/* okay, now check cross-realm policy */
#if defined(_SINGLE_HOP_ONLY)
@@ -296,126 +372,86 @@ krb5_rd_req_decoded_opt(krb5_context context, krb5_auth_context *auth_context,
}
}
- /* check if the various etypes are permitted */
-
- if ((*auth_context)->auth_context_flags & KRB5_AUTH_CONTEXT_PERMIT_ALL) {
- /* no etype check needed */;
- } else if ((*auth_context)->permitted_etypes == NULL) {
- int etype;
- size_t size_etype_enc = 3 * sizeof(krb5_enctype); /* upto three types */
- size_t size_etype_bool = 3 * sizeof(krb5_boolean);
- krb5_etypes_permitted etypes;
- memset(&etypes, 0, sizeof etypes);
-
- etypes.etype = (krb5_enctype*) malloc(size_etype_enc);
- if (etypes.etype == NULL) {
- retval = ENOMEM;
- goto cleanup;
- }
- etypes.etype_ok = (krb5_boolean*) malloc(size_etype_bool);
- if (etypes.etype_ok == NULL) {
- retval = ENOMEM;
- free(etypes.etype);
- goto cleanup;
- }
- memset(etypes.etype, 0, size_etype_enc);
- memset(etypes.etype_ok, 0, size_etype_bool);
+ /* read RFC 4537 etype list from sender */
+ retval = decode_etype_list(context,
+ (*auth_context)->authentp,
+ &desired_etypes,
+ &rfc4537_etypes_len);
+ if (retval != 0)
+ goto cleanup;
- etypes.etype[etypes.etype_count++] = req->ticket->enc_part.enctype;
- etypes.etype[etypes.etype_count++] = req->ticket->enc_part2->session->enctype;
- if ((*auth_context)->authentp->subkey) {
- etypes.etype[etypes.etype_count++] = (*auth_context)->authentp->subkey->enctype;
- }
+ if (desired_etypes == NULL)
+ desired_etypes = (krb5_enctype *)calloc(4, sizeof(krb5_enctype));
+ else
+ desired_etypes = (krb5_enctype *)realloc(desired_etypes,
+ (rfc4537_etypes_len + 4) *
+ sizeof(krb5_enctype));
+ if (desired_etypes == NULL) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
- retval = krb5_is_permitted_enctype_ext(context, &etypes);
+ desired_etypes_len = rfc4537_etypes_len;
-#if 0
- /* check against the default set */
- if ((!krb5_is_permitted_enctype(context,
- etype = req->ticket->enc_part.enctype)) ||
- (!krb5_is_permitted_enctype(context,
- etype = req->ticket->enc_part2->session->enctype)) ||
- (((*auth_context)->authentp->subkey) &&
- !krb5_is_permitted_enctype(context,
- etype = (*auth_context)->authentp->subkey->enctype))) {
-#endif
- if (retval == 0 /* all etypes are not permitted */ ||
- (!etypes.etype_ok[0] || !etypes.etype_ok[1] ||
- (((*auth_context)->authentp->subkey) && !etypes.etype_ok[etypes.etype_count-1]))) {
- char enctype_name[30];
- retval = KRB5_NOPERM_ETYPE;
-
- if (!etypes.etype_ok[0]) {
- etype = etypes.etype[1];
- } else if (!etypes.etype_ok[1]) {
- etype = etypes.etype[1];
- } else {
- etype = etypes.etype[2];
- }
- free(etypes.etype);
- free(etypes.etype_ok);
-
- if (krb5_enctype_to_string(etype, enctype_name, sizeof(enctype_name)) == 0)
- krb5_set_error_message(context, retval,
- "Encryption type %s not permitted",
- enctype_name);
- goto cleanup;
- }
- free(etypes.etype);
- free(etypes.etype_ok);
- } else {
- /* check against the set in the auth_context */
- int i;
+ /*
+ * RFC 4537:
+ *
+ * If the EtypeList is present and the server prefers an enctype from
+ * the client's enctype list over that of the AP-REQ authenticator
+ * subkey (if that is present) or the service ticket session key, the
+ * server MUST create a subkey using that enctype. This negotiated
+ * subkey is sent in the subkey field of AP-REP message, and it is then
+ * used as the protocol key or base key [RFC3961] for subsequent
+ * communication.
+ *
+ * If the enctype of the ticket session key is included in the enctype
+ * list sent by the client, it SHOULD be the last on the list;
+ * otherwise, this enctype MUST NOT be negotiated if it was not included
+ * in the list.
+ *
+ * The second paragraph does appear to contradict the first with respect
+ * to whether it is legal to negotiate the ticket session key type if it
+ * is absent in the EtypeList. A literal reading suggests that we can use
+ * the AP-REQ subkey enctype. Also a client has no way of distinguishing
+ * a server that does not RFC 4537 from one that has chosen the same
+ * enctype as the ticket session key for the acceptor subkey, surely.
+ */
- for (i=0; (*auth_context)->permitted_etypes[i]; i++)
- if ((*auth_context)->permitted_etypes[i] ==
- req->ticket->enc_part.enctype)
- break;
- if (!(*auth_context)->permitted_etypes[i]) {
- char enctype_name[30];
- retval = KRB5_NOPERM_ETYPE;
- if (krb5_enctype_to_string(req->ticket->enc_part.enctype,
- enctype_name, sizeof(enctype_name)) == 0)
- krb5_set_error_message(context, retval,
- "Encryption type %s not permitted",
- enctype_name);
- goto cleanup;
- }
-
- for (i=0; (*auth_context)->permitted_etypes[i]; i++)
- if ((*auth_context)->permitted_etypes[i] ==
- req->ticket->enc_part2->session->enctype)
- break;
- if (!(*auth_context)->permitted_etypes[i]) {
- char enctype_name[30];
- retval = KRB5_NOPERM_ETYPE;
- if (krb5_enctype_to_string(req->ticket->enc_part2->session->enctype,
- enctype_name, sizeof(enctype_name)) == 0)
- krb5_set_error_message(context, retval,
- "Encryption type %s not permitted",
- enctype_name);
- goto cleanup;
- }
-
- if ((*auth_context)->authentp->subkey) {
- for (i=0; (*auth_context)->permitted_etypes[i]; i++)
- if ((*auth_context)->permitted_etypes[i] ==
- (*auth_context)->authentp->subkey->enctype)
- break;
- if (!(*auth_context)->permitted_etypes[i]) {
- char enctype_name[30];
- retval = KRB5_NOPERM_ETYPE;
- if (krb5_enctype_to_string((*auth_context)->authentp->subkey->enctype,
- enctype_name,
- sizeof(enctype_name)) == 0)
- krb5_set_error_message(context, retval,
- "Encryption type %s not permitted",
- enctype_name);
+ if ((*auth_context)->authentp->subkey != NULL) {
+ desired_etypes[desired_etypes_len++] = (*auth_context)->authentp->subkey->enctype;
+ }
+ desired_etypes[desired_etypes_len++] = req->ticket->enc_part2->session->enctype;
+ desired_etypes[desired_etypes_len++] = req->ticket->enc_part.enctype;
+ desired_etypes[desired_etypes_len] = ENCTYPE_NULL;
+
+ if (((*auth_context)->auth_context_flags & KRB5_AUTH_CONTEXT_PERMIT_ALL) == 0) {
+ if ((*auth_context)->permitted_etypes != NULL) {
+ permitted_etypes = (*auth_context)->permitted_etypes;
+ } else {
+ retval = krb5_get_permitted_enctypes(context, &permitted_etypes);
+ if (retval != 0)
goto cleanup;
- }
}
+ for (permitted_etypes_len = 0;
+ permitted_etypes[permitted_etypes_len] != ENCTYPE_NULL;
+ permitted_etypes_len++)
+ ;
+ } else {
+ permitted_etypes = NULL;
+ permitted_etypes_len = 0;
}
+ /* check if the various etypes are permitted */
+ retval = negotiate_etype(context,
+ desired_etypes, desired_etypes_len,
+ rfc4537_etypes_len,
+ permitted_etypes, permitted_etypes_len,
+ &(*auth_context)->negotiated_etype);
+ if (retval != 0)
+ goto cleanup;
+
+ assert((*auth_context)->negotiated_etype != ENCTYPE_NULL);
+
(*auth_context)->remote_seq_number = (*auth_context)->authentp->seq_number;
if ((*auth_context)->authentp->subkey) {
if ((retval = krb5_copy_keyblock(context,
@@ -454,11 +490,22 @@ krb5_rd_req_decoded_opt(krb5_context context, krb5_auth_context *auth_context,
if (ticket)
if ((retval = krb5_copy_ticket(context, req->ticket, ticket)))
goto cleanup;
- if (ap_req_options)
- *ap_req_options = req->ap_options;
+ if (ap_req_options) {
+ *ap_req_options = req->ap_options & AP_OPTS_WIRE_MASK;
+ if (rfc4537_etypes_len != 0)
+ *ap_req_options |= AP_OPTS_ETYPE_NEGOTIATION;
+ if ((*auth_context)->negotiated_etype != (*auth_context)->keyblock->enctype)
+ *ap_req_options |= AP_OPTS_USE_SUBKEY;
+ }
+
retval = 0;
cleanup:
+ if (desired_etypes != NULL)
+ krb5_xfree(desired_etypes);
+ if (permitted_etypes != NULL &&
+ permitted_etypes != (*auth_context)->permitted_etypes)
+ krb5_xfree(permitted_etypes);
if (server == &princ_data)
krb5_free_default_realm(context, princ_data.realm.data);
if (retval) {
@@ -537,3 +584,129 @@ free(scratch.data);}
}
#endif
+static krb5_error_code
+negotiate_etype(krb5_context context,
+ const krb5_enctype *desired_etypes,
+ int desired_etypes_len,
+ int mandatory_etypes_index,
+ const krb5_enctype *permitted_etypes,
+ int permitted_etypes_len,
+ krb5_enctype *negotiated_etype)
+{
+ int i, j;
+
+ *negotiated_etype = ENCTYPE_NULL;
+
+ /* mandatory segment of desired_etypes must be permitted */
+ for (i = mandatory_etypes_index; i < desired_etypes_len; i++) {
+ krb5_boolean permitted = FALSE;
+
+ for (j = 0; j < permitted_etypes_len; j++) {
+ if (desired_etypes[i] == permitted_etypes[j]) {
+ permitted = TRUE;
+ break;
+ }
+ }
+
+ if (permitted == FALSE) {
+ char enctype_name[30];
+
+ if (krb5_enctype_to_string(desired_etypes[i],
+ enctype_name,
+ sizeof(enctype_name)) == 0)
+ krb5_set_error_message(context, KRB5_NOPERM_ETYPE,
+ "Encryption type %s not permitted",
+ enctype_name);
+ return KRB5_NOPERM_ETYPE;
+ }
+ }
+
+ /*
+ * permitted_etypes is ordered from most to least preferred;
+ * find first desired_etype that matches.
+ */
+ for (j = 0; j < permitted_etypes_len; j++) {
+ for (i = 0; i < desired_etypes_len; i++) {
+ if (desired_etypes[i] == permitted_etypes[j]) {
+ *negotiated_etype = permitted_etypes[j];
+ return 0;
+ }
+ }
+ }
+
+ /*NOTREACHED*/
+ return KRB5_NOPERM_ETYPE;
+}
+
+static krb5_error_code
+decode_etype_list(krb5_context context,
+ const krb5_authenticator *authp,
+ krb5_enctype **desired_etypes,
+ int *desired_etypes_len)
+{
+ krb5_error_code code;
+ krb5_authdata **ad_if_relevant = NULL;
+ krb5_authdata *etype_adata = NULL;
+ krb5_etype_list *etype_list = NULL;
+ int i, j;
+ krb5_data data;
+
+ *desired_etypes = NULL;
+
+ if (authp->authorization_data == NULL)
+ return 0;
+
+ /*
+ * RFC 4537 says that ETYPE_NEGOTIATION auth data should be wrapped
+ * in AD_IF_RELEVANT, but we handle the case where it is mandatory.
+ */
+ for (i = 0; authp->authorization_data[i] != NULL; i++) {
+ switch (authp->authorization_data[i]->ad_type) {
+ case KRB5_AUTHDATA_IF_RELEVANT:
+ code = krb5_decode_authdata_container(context,
+ KRB5_AUTHDATA_IF_RELEVANT,
+ authp->authorization_data[i],
+ &ad_if_relevant);
+ if (code != 0)
+ continue;
+
+ for (j = 0; ad_if_relevant[j] != NULL; j++) {
+ if (ad_if_relevant[j]->ad_type == KRB5_AUTHDATA_ETYPE_NEGOTIATION) {
+ etype_adata = ad_if_relevant[j];
+ break;
+ }
+ }
+ if (etype_adata == NULL) {
+ krb5_free_authdata(context, ad_if_relevant);
+ ad_if_relevant = NULL;
+ }
+ break;
+ case KRB5_AUTHDATA_ETYPE_NEGOTIATION:
+ etype_adata = authp->authorization_data[i];
+ break;
+ default:
+ break;
+ }
+ if (etype_adata != NULL)
+ break;
+ }
+
+ if (etype_adata == NULL)
+ return 0;
+
+ data.data = (char *)etype_adata->contents;
+ data.length = etype_adata->length;
+
+ code = decode_krb5_etype_list(&data, &etype_list);
+ if (code == 0) {
+ *desired_etypes = etype_list->etypes;
+ *desired_etypes_len = etype_list->length;
+ krb5_xfree(etype_list);
+ }
+
+ if (ad_if_relevant != NULL)
+ krb5_free_authdata(context, ad_if_relevant);
+
+ return code;
+}
+
diff --git a/src/lib/krb5/krb/serialize.c b/src/lib/krb5/krb/serialize.c
index fc20fb1928..9152dba0a7 100644
--- a/src/lib/krb5/krb/serialize.c
+++ b/src/lib/krb5/krb/serialize.c
@@ -62,7 +62,8 @@ krb5_register_serializer(krb5_context kcontext, const krb5_ser_entry *entry)
kret = 0;
/* See if it's already there, if so, we're good to go. */
- if (!(stable = krb5_find_serializer(kcontext, entry->odtype))) {
+ if (!(stable = (krb5_ser_entry *)krb5_find_serializer(kcontext,
+ entry->odtype))) {
/*
* Can't find our type. Create a new entry.
*/
diff --git a/src/lib/krb5/krb/unparse.c b/src/lib/krb5/krb/unparse.c
index e24dccf097..ec0976fb22 100644
--- a/src/lib/krb5/krb/unparse.c
+++ b/src/lib/krb5/krb/unparse.c
@@ -1,7 +1,7 @@
/*
* lib/krb5/krb/unparse.c
*
- * Copyright 1990 by the Massachusetts Institute of Technology.
+ * Copyright 1990, 2008 by the Massachusetts Institute of Technology.
* All Rights Reserved.
*
* Export of this software from the United States of America may
@@ -58,33 +58,52 @@
#define COMPONENT_SEP '/'
static int
-component_length_quoted(const krb5_data *src)
+component_length_quoted(const krb5_data *src, int flags)
{
const char *cp = src->data;
int length = src->length;
int j;
int size = length;
- for (j = 0; j < length; j++,cp++)
- if (*cp == REALM_SEP || *cp == COMPONENT_SEP ||
- *cp == '\0' || *cp == '\\' || *cp == '\t' ||
- *cp == '\n' || *cp == '\b')
- size++;
+ if ((flags & KRB5_PRINCIPAL_UNPARSE_DISPLAY) == 0) {
+ int no_realm = (flags & KRB5_PRINCIPAL_UNPARSE_NO_REALM) &&
+ !(flags & KRB5_PRINCIPAL_UNPARSE_SHORT);
+
+ for (j = 0; j < length; j++,cp++)
+ if ((!no_realm && *cp == REALM_SEP) ||
+ *cp == COMPONENT_SEP ||
+ *cp == '\0' || *cp == '\\' || *cp == '\t' ||
+ *cp == '\n' || *cp == '\b')
+ size++;
+ }
+
return size;
}
static int
-copy_component_quoting(char *dest, const krb5_data *src)
+copy_component_quoting(char *dest, const krb5_data *src, int flags)
{
int j;
const char *cp = src->data;
char *q = dest;
int length = src->length;
+ if (flags & KRB5_PRINCIPAL_UNPARSE_DISPLAY) {
+ memcpy(dest, src->data, src->length);
+ return src->length;
+ }
+
for (j=0; j < length; j++,cp++) {
+ int no_realm = (flags & KRB5_PRINCIPAL_UNPARSE_NO_REALM) &&
+ !(flags & KRB5_PRINCIPAL_UNPARSE_SHORT);
+
switch (*cp) {
- case COMPONENT_SEP:
case REALM_SEP:
+ if (no_realm) {
+ *q++ = *cp;
+ break;
+ }
+ case COMPONENT_SEP:
case '\\':
*q++ = '\\';
*q++ = *cp;
@@ -101,6 +120,13 @@ copy_component_quoting(char *dest, const krb5_data *src)
*q++ = '\\';
*q++ = 'b';
break;
+#if 0
+ /* Heimdal escapes spaces in principal names upon unparsing */
+ case ' ':
+ *q++ = '\\';
+ *q++ = ' ';
+ break;
+#endif
case '\0':
*q++ = '\\';
*q++ = '0';
@@ -112,27 +138,47 @@ copy_component_quoting(char *dest, const krb5_data *src)
return q - dest;
}
-krb5_error_code KRB5_CALLCONV
-krb5_unparse_name_ext(krb5_context context, krb5_const_principal principal,
- char **name, unsigned int *size)
+static krb5_error_code
+k5_unparse_name(krb5_context context, krb5_const_principal principal,
+ int flags, char **name, unsigned int *size)
{
char *cp, *q;
int i;
int length;
krb5_int32 nelem;
unsigned int totalsize = 0;
+ char *default_realm = NULL;
+ krb5_error_code ret = 0;
if (!principal || !name)
return KRB5_PARSE_MALFORMED;
- totalsize += component_length_quoted(krb5_princ_realm(context,
- principal));
- totalsize++; /* This is for the separator */
+ if (flags & KRB5_PRINCIPAL_UNPARSE_SHORT) {
+ /* omit realm if local realm */
+ krb5_principal_data p;
+
+ ret = krb5_get_default_realm(context, &default_realm);
+ if (ret != 0)
+ goto cleanup;
+
+ krb5_princ_realm(context, &p)->length = strlen(default_realm);
+ krb5_princ_realm(context, &p)->data = default_realm;
+
+ if (krb5_realm_compare(context, &p, principal))
+ flags |= KRB5_PRINCIPAL_UNPARSE_NO_REALM;
+ }
+
+ if ((flags & KRB5_PRINCIPAL_UNPARSE_NO_REALM) == 0) {
+ totalsize += component_length_quoted(krb5_princ_realm(context,
+ principal),
+ flags);
+ totalsize++; /* This is for the separator */
+ }
nelem = krb5_princ_size(context, principal);
for (i = 0; i < (int) nelem; i++) {
cp = krb5_princ_component(context, principal, i)->data;
- totalsize += component_length_quoted(krb5_princ_component(context, principal, i));
+ totalsize += component_length_quoted(krb5_princ_component(context, principal, i), flags);
totalsize++; /* This is for the separator */
}
if (nelem == 0)
@@ -143,7 +189,7 @@ krb5_unparse_name_ext(krb5_context context, krb5_const_principal principal,
* provided, use it, realloc'ing it if necessary.
*
* We need only n-1 seperators for n components, but we need
- * an extra byte for the NULL at the end.
+ * an extra byte for the NUL at the end.
*/
if (size) {
if (*name && (*size < totalsize)) {
@@ -156,8 +202,10 @@ krb5_unparse_name_ext(krb5_context context, krb5_const_principal principal,
*name = malloc(totalsize);
}
- if (!*name)
- return ENOMEM;
+ if (!*name) {
+ ret = ENOMEM;
+ goto cleanup;
+ }
q = *name;
@@ -167,24 +215,55 @@ krb5_unparse_name_ext(krb5_context context, krb5_const_principal principal,
q += copy_component_quoting(q,
krb5_princ_component(context,
principal,
- i));
+ i),
+ flags);
*q++ = COMPONENT_SEP;
}
if (i > 0)
q--; /* Back up last component separator */
- *q++ = REALM_SEP;
- q += copy_component_quoting(q, krb5_princ_realm(context, principal));
+ if ((flags & KRB5_PRINCIPAL_UNPARSE_NO_REALM) == 0) {
+ *q++ = REALM_SEP;
+ q += copy_component_quoting(q, krb5_princ_realm(context, principal), flags);
+ }
*q++ = '\0';
-
- return 0;
+
+cleanup:
+ if (default_realm != NULL)
+ krb5_free_default_realm(context, default_realm);
+
+ return ret;
}
krb5_error_code KRB5_CALLCONV
krb5_unparse_name(krb5_context context, krb5_const_principal principal, register char **name)
{
- if (name) /* name == NULL will return error from _ext */
- *name = NULL;
- return(krb5_unparse_name_ext(context, principal, name, NULL));
+ if (name != NULL) /* name == NULL will return error from _ext */
+ *name = NULL;
+
+ return k5_unparse_name(context, principal, 0, name, NULL);
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_unparse_name_ext(krb5_context context, krb5_const_principal principal,
+ char **name, unsigned int *size)
+{
+ return k5_unparse_name(context, principal, 0, name, size);
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_unparse_name_flags(krb5_context context, krb5_const_principal principal,
+ int flags, char **name)
+{
+ if (name != NULL)
+ *name = NULL;
+ return k5_unparse_name(context, principal, flags, name, NULL);
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_unparse_name_flags_ext(krb5_context context, krb5_const_principal principal,
+ int flags, char **name, unsigned int *size)
+{
+ return k5_unparse_name(context, principal, flags, name, size);
}
diff --git a/src/lib/krb5/krb/valid_times.c b/src/lib/krb5/krb/valid_times.c
index 9c53d7d919..febbc369ff 100644
--- a/src/lib/krb5/krb/valid_times.c
+++ b/src/lib/krb5/krb/valid_times.c
@@ -29,8 +29,6 @@
#include "k5-int.h"
-#define in_clock_skew(date) (labs((date)-currenttime) < context->clockskew)
-
/*
* This is an internal routine which validates the krb5_timestamps
* field in a krb5_ticket.