Deferred hostname canon using name canon rules

This commit is contained in:
Nicolas Williams
2011-10-15 19:29:54 -05:00
parent 587cf45846
commit a5e77c578e
9 changed files with 1220 additions and 141 deletions

View File

@@ -1608,6 +1608,7 @@ match_rfc_san(krb5_context context,
return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
}
memset(&principal, 0, sizeof (principal));
principal.name = kn.principalName;
principal.realm = kn.realm;

View File

@@ -293,6 +293,7 @@ change (krb5_auth_context auth_context,
if (chpw.targname) {
krb5_principal_data princ;
memset(&princ, 0, sizeof (princ));
princ.name = *chpw.targname;
princ.realm = *chpw.targrealm;
if (princ.realm == NULL) {

View File

@@ -85,7 +85,7 @@ main(int argc, char **argv)
krb5_creds *out;
int optidx = 0;
krb5_get_creds_opt opt;
krb5_principal server;
krb5_principal server = NULL;
krb5_principal impersonate = NULL;
setprogname (argv[0]);
@@ -108,9 +108,6 @@ main(int argc, char **argv)
argc -= optidx;
argv += optidx;
if (argc != 1)
usage (1);
if(cache_str) {
ret = krb5_cc_resolve(context, cache_str, &cache);
if (ret)
@@ -190,18 +187,70 @@ main(int argc, char **argv)
KRB5_GC_CONSTRAINED_DELEGATION);
}
ret = krb5_parse_name(context, argv[0], &server);
if (ret)
krb5_err (context, 1, ret, "krb5_parse_name %s", argv[0]);
if (nametype_str) {
int32_t nametype;
int do_sn2p = 1;
char *sname = NULL;
char *hname = NULL;
ret = krb5_parse_nametype(context, nametype_str, &nametype);
if (ret)
krb5_err(context, 1, ret, "krb5_parse_nametype");
server->name.name_type = (NAME_TYPE)nametype;
if (nametype == KRB5_NT_SRV_HST) {
if (argc == 1) {
char *cp;
for (cp = sname; *cp; cp++) {
if (cp[0] == '\\') {
cp++;
} else if (cp[0] == '@' && cp[1] != '\0') {
/* If a realm is given we assume no canon is needed */
do_sn2p = 0;
break;
}
}
if (do_sn2p) {
sname = argv[0];
for (cp = sname; *cp; cp++) {
if (cp[0] == '\\') {
cp++;
} else if (cp[0] == '/') {
*cp = '\0';
hname = cp + 1;
} else if (cp[0] == '@') {
*cp = '\0';
break;
}
}
}
} else if (argc == 2) {
sname = argv[0];
hname = argv[1];
} else if (argc != 0) {
usage(1);
}
ret = krb5_sname_to_principal(context, hname, sname,
KRB5_NT_SRV_HST, &server);
if (ret)
krb5_err(context, 1, ret, "krb5_sname_to_principal %s/%s",
(sname && *sname) ? sname : "<default>",
(hname && *hname) ? hname : "<default>");
} else {
if (argc != 1)
usage(1);
ret = krb5_parse_name(context, argv[0], &server);
if (ret)
krb5_err (context, 1, ret, "krb5_parse_name %s", argv[0]);
server->name.name_type = (NAME_TYPE)nametype;
}
} else if (argc == 1) {
ret = krb5_parse_name(context, argv[0], &server);
if (ret)
krb5_err (context, 1, ret, "krb5_parse_name %s", argv[0]);
} else {
usage(1);
}
ret = krb5_get_creds(context, opt, cache, server, &out);

View File

@@ -89,7 +89,8 @@ NAME-TYPE ::= INTEGER {
KRB5_NT_ENT_PRINCIPAL_AND_ID(-130), -- Windows 2000 UPN and SID
KRB5_NT_MS_PRINCIPAL(-128), -- NT 4 style name
KRB5_NT_MS_PRINCIPAL_AND_ID(-129), -- NT style name and SID
KRB5_NT_NTLM(-1200) -- NTLM name, realm is domain
KRB5_NT_NTLM(-1200), -- NTLM name, realm is domain
KRB5_NT_SRV_HST_NEEDS_CANON (-195894762) -- -(0x0bad1dea)
}
-- message types
@@ -269,6 +270,10 @@ PrincipalName ::= SEQUENCE {
Principal ::= SEQUENCE {
name[0] PrincipalName,
realm[1] Realm
-- Note that while it'd be nice to be able to add OPTIONAL
-- fields at the end here there are issues regarding
-- applications that allocate krb5_principal_data's on the
-- stack.
}
Principals ::= SEQUENCE OF Principal

View File

@@ -53,7 +53,7 @@ _krb5_principalname2krb5_principal (krb5_context context,
krb5_error_code ret;
krb5_principal p;
p = malloc(sizeof(*p));
p = calloc(1, sizeof(*p));
if (p == NULL)
return ENOMEM;
ret = copy_PrincipalName(&from, &p->name);

View File

@@ -1116,6 +1116,68 @@ _krb5_get_cred_kdc_any(krb5_context context,
ret_tgts);
}
static krb5_error_code
check_cc(krb5_context context, krb5_flags options, krb5_ccache ccache,
krb5_creds *in_creds, krb5_creds *out_creds)
{
krb5_error_code ret;
krb5_timestamp timeret;
/*
* If we got a credential, check if credential is expired before
* returning it.
*/
ret = krb5_cc_retrieve_cred(context, ccache,
options & KRB5_TC_MATCH_KEYTYPE,
in_creds, out_creds);
if (ret != 0)
return ret; /* Caller will check for KRB5_CC_END */
/*
* If we got a credential, check if credential is expired before
* returning it, but only if KRB5_GC_EXPIRED_OK is not set.
*/
/* If expired ok, don't bother checking */
if (options & KRB5_GC_EXPIRED_OK)
return 0;
krb5_timeofday(context, &timeret);
if (out_creds->times.endtime > timeret)
return 0;
/* Expired and not ok; remove and pretend we didn't find it */
if (options & KRB5_GC_CACHED)
krb5_cc_remove_cred(context, ccache, 0, out_creds);
krb5_free_cred_contents(context, out_creds);
memset(out_creds, 0, sizeof (*out_creds));
return KRB5_CC_END;
}
static void
store_cred(krb5_context context, krb5_ccache ccache,
krb5_const_principal server_princ, krb5_creds *creds)
{
krb5_error_code ret;
krb5_principal tmp_princ = creds->server;
krb5_principal p;
krb5_cc_store_cred(context, ccache, creds);
if (strcmp(server_princ->realm, "") != 0)
return;
ret = krb5_copy_principal(context, server_princ, &p);
if (ret)
return;
if (p->name.name_type == KRB5_NT_SRV_HST_NEEDS_CANON)
p->name.name_type = KRB5_NT_SRV_HST;
creds->server = p;
krb5_cc_store_cred(context, ccache, creds);
creds->server = tmp_princ;
krb5_free_principal(context, p);
}
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_credentials_with_flags(krb5_context context,
@@ -1126,7 +1188,10 @@ krb5_get_credentials_with_flags(krb5_context context,
krb5_creds **out_creds)
{
krb5_error_code ret;
krb5_name_canon_iterator name_canon_iter = NULL;
krb5_name_canon_rule_options rule_opts;
krb5_creds **tgts;
krb5_creds *try_creds;
krb5_creds *res_creds;
int i;
@@ -1134,6 +1199,7 @@ krb5_get_credentials_with_flags(krb5_context context,
ret = krb5_enctype_valid(context, in_creds->session.keytype);
if (ret)
return ret;
options |= KRB5_TC_MATCH_KEYTYPE;
}
*out_creds = NULL;
@@ -1144,46 +1210,43 @@ krb5_get_credentials_with_flags(krb5_context context,
return ENOMEM;
}
if (in_creds->session.keytype)
options |= KRB5_TC_MATCH_KEYTYPE;
/*
* If we got a credential, check if credential is expired before
* returning it.
*/
ret = krb5_cc_retrieve_cred(context,
ccache,
in_creds->session.keytype ?
KRB5_TC_MATCH_KEYTYPE : 0,
in_creds, res_creds);
/*
* If we got a credential, check if credential is expired before
* returning it, but only if KRB5_GC_EXPIRED_OK is not set.
*/
if (ret == 0) {
krb5_timestamp timeret;
/* If expired ok, don't bother checking */
if(options & KRB5_GC_EXPIRED_OK) {
*out_creds = res_creds;
return 0;
}
krb5_timeofday(context, &timeret);
if(res_creds->times.endtime > timeret) {
if (in_creds->server->name.name_type == KRB5_NT_SRV_HST_NEEDS_CANON) {
ret = check_cc(context, options, ccache, in_creds, res_creds);
if (ret == 0) {
*out_creds = res_creds;
return 0;
}
if(options & KRB5_GC_CACHED)
krb5_cc_remove_cred(context, ccache, 0, res_creds);
} else if(ret != KRB5_CC_END) {
free(res_creds);
return ret;
}
free(res_creds);
ret = krb5_name_canon_iterator_start(context, NULL, in_creds,
&name_canon_iter);
if (ret)
return ret;
next_rule:
krb5_free_cred_contents(context, res_creds);
memset(res_creds, 0, sizeof (res_creds));
ret = krb5_name_canon_iterate_creds(context, &name_canon_iter, &try_creds,
&rule_opts);
if (ret)
goto out;
if (name_canon_iter == NULL) {
if (options & KRB5_GC_CACHED)
ret = KRB5_CC_NOTFOUND;
else
ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
goto out;
}
ret = check_cc(context, options, ccache, try_creds, res_creds);
if (ret == 0) {
*out_creds = res_creds;
goto out;
} else if(ret != KRB5_CC_END) {
goto out;
}
if(options & KRB5_GC_CACHED)
return not_found(context, in_creds->server, KRB5_CC_NOTFOUND);
goto next_rule;
if(options & KRB5_GC_USER_USER)
flags.b.enc_tkt_in_skey = 1;
@@ -1192,15 +1255,26 @@ krb5_get_credentials_with_flags(krb5_context context,
tgts = NULL;
ret = _krb5_get_cred_kdc_any(context, flags, ccache,
in_creds, NULL, NULL, out_creds, &tgts);
try_creds, NULL, NULL, out_creds, &tgts);
for(i = 0; tgts && tgts[i]; i++) {
krb5_cc_store_cred(context, ccache, tgts[i]);
krb5_free_creds(context, tgts[i]);
}
free(tgts);
if (ret == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN &&
!(rule_opts & KRB5_NCRO_SECURE))
goto next_rule;
if(ret == 0 && (options & KRB5_GC_NO_STORE) == 0)
krb5_cc_store_cred(context, ccache, *out_creds);
return ret;
store_cred(context, ccache, in_creds->server, *out_creds);
out:
krb5_free_name_canon_iterator(context, name_canon_iter);
if (!ret) {
krb5_free_creds(context, res_creds);
return not_found(context, in_creds->server, ret);
}
return 0;
}
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
@@ -1315,7 +1389,6 @@ krb5_get_creds_opt_set_ticket(krb5_context context,
}
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_creds(krb5_context context,
krb5_get_creds_opt opt,
@@ -1328,7 +1401,10 @@ krb5_get_creds(krb5_context context,
krb5_creds in_creds;
krb5_error_code ret;
krb5_creds **tgts;
krb5_creds *try_creds;
krb5_creds *res_creds;
krb5_name_canon_iterator name_canon_iter = NULL;
krb5_name_canon_rule_options rule_opts;
int i;
if (opt && opt->enctype) {
@@ -1364,48 +1440,42 @@ krb5_get_creds(krb5_context context,
options |= KRB5_TC_MATCH_KEYTYPE;
}
/*
* If we got a credential, check if credential is expired before
* returning it.
*/
ret = krb5_cc_retrieve_cred(context,
ccache,
options & KRB5_TC_MATCH_KEYTYPE,
&in_creds, res_creds);
/*
* If we got a credential, check if credential is expired before
* returning it, but only if KRB5_GC_EXPIRED_OK is not set.
*/
if (ret == 0) {
krb5_timestamp timeret;
/* If expired ok, don't bother checking */
if(options & KRB5_GC_EXPIRED_OK) {
*out_creds = res_creds;
krb5_free_principal(context, in_creds.client);
goto out;
}
krb5_timeofday(context, &timeret);
if(res_creds->times.endtime > timeret) {
/* Check for entry in ccache */
if (inprinc->name.name_type == KRB5_NT_SRV_HST_NEEDS_CANON) {
ret = check_cc(context, options, ccache, &in_creds, res_creds);
if (ret == 0) {
*out_creds = res_creds;
krb5_free_principal(context, in_creds.client);
goto out;
goto out;
}
if(options & KRB5_GC_CACHED)
krb5_cc_remove_cred(context, ccache, 0, res_creds);
}
ret = krb5_name_canon_iterator_start(context, NULL, &in_creds,
&name_canon_iter);
if (ret)
goto out;
next_rule:
ret = krb5_name_canon_iterate_creds(context, &name_canon_iter, &try_creds,
&rule_opts);
if (ret)
return ret;
if (name_canon_iter == NULL) {
if (options & KRB5_GC_CACHED)
ret = KRB5_CC_NOTFOUND;
else
ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
goto out;
}
ret = check_cc(context, options, ccache, try_creds, res_creds);
if (ret == 0) {
*out_creds = res_creds;
goto out;
} else if(ret != KRB5_CC_END) {
free(res_creds);
krb5_free_principal(context, in_creds.client);
goto out;
}
free(res_creds);
if(options & KRB5_GC_CACHED) {
krb5_free_principal(context, in_creds.client);
ret = not_found(context, in_creds.server, KRB5_CC_NOTFOUND);
goto out;
}
if(options & KRB5_GC_CACHED)
goto next_rule;
if(options & KRB5_GC_USER_USER) {
flags.b.enc_tkt_in_skey = 1;
options |= KRB5_GC_NO_STORE;
@@ -1423,18 +1493,28 @@ krb5_get_creds(krb5_context context,
tgts = NULL;
ret = _krb5_get_cred_kdc_any(context, flags, ccache,
&in_creds, opt->self, opt->ticket,
try_creds, opt->self, opt->ticket,
out_creds, &tgts);
krb5_free_principal(context, in_creds.client);
for(i = 0; tgts && tgts[i]; i++) {
krb5_cc_store_cred(context, ccache, tgts[i]);
krb5_free_creds(context, tgts[i]);
}
free(tgts);
if(ret == 0 && (options & KRB5_GC_NO_STORE) == 0)
krb5_cc_store_cred(context, ccache, *out_creds);
out:
if (ret == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN &&
!(rule_opts & KRB5_NCRO_SECURE))
goto next_rule;
if(ret == 0 && (options & KRB5_GC_NO_STORE) == 0)
store_cred(context, ccache, inprinc, *out_creds);
out:
if (ret != 0) {
krb5_free_creds(context, res_creds);
ret = not_found(context, inprinc, ret);
}
krb5_free_principal(context, in_creds.client);
krb5_free_name_canon_iterator(context, name_canon_iter);
_krb5_debug(context, 5, "krb5_get_creds: ret = %d", ret);
return ret;

View File

@@ -588,30 +588,13 @@ _krb5_kt_principal_not_found(krb5_context context,
return ret;
}
/**
* Retrieve the keytab entry for `principal, kvno, enctype' into `entry'
* from the keytab `id'. Matching is done like krb5_kt_compare().
*
* @param context a Keberos context.
* @param id a keytab.
* @param principal principal to match, NULL matches all principals.
* @param kvno key version to match, 0 matches all key version numbers.
* @param enctype encryption type to match, 0 matches all encryption types.
* @param entry the returned entry, free with krb5_kt_free_entry().
*
* @return Return an error code or 0, see krb5_get_error_message().
*
* @ingroup krb5_keytab
*/
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_kt_get_entry(krb5_context context,
krb5_keytab id,
krb5_const_principal principal,
krb5_kvno kvno,
krb5_enctype enctype,
krb5_keytab_entry *entry)
static krb5_error_code
krb5_kt_get_entry_wrapped(krb5_context context,
krb5_keytab id,
krb5_const_principal principal,
krb5_kvno kvno,
krb5_enctype enctype,
krb5_keytab_entry *entry)
{
krb5_keytab_entry tmp;
krb5_error_code ret;
@@ -654,6 +637,56 @@ krb5_kt_get_entry(krb5_context context,
return 0;
}
/**
* Retrieve the keytab entry for `principal, kvno, enctype' into `entry'
* from the keytab `id'. Matching is done like krb5_kt_compare().
*
* @param context a Keberos context.
* @param id a keytab.
* @param principal principal to match, NULL matches all principals.
* @param kvno key version to match, 0 matches all key version numbers.
* @param enctype encryption type to match, 0 matches all encryption types.
* @param entry the returned entry, free with krb5_kt_free_entry().
*
* @return Return an error code or 0, see krb5_get_error_message().
*
* @ingroup krb5_keytab
*/
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_kt_get_entry(krb5_context context,
krb5_keytab id,
krb5_const_principal principal,
krb5_kvno kvno,
krb5_enctype enctype,
krb5_keytab_entry *entry)
{
krb5_error_code ret;
krb5_principal try_princ;
krb5_name_canon_iterator name_canon_iter;
if (!principal || principal->name.name_type != KRB5_NT_SRV_HST_NEEDS_CANON)
return krb5_kt_get_entry_wrapped(context, id, principal, kvno, enctype,
entry);
ret = krb5_name_canon_iterator_start(context, principal, NULL,
&name_canon_iter);
if (ret)
return ret;
do {
ret = krb5_name_canon_iterate_princ(context, &name_canon_iter,
&try_princ, NULL);
if (ret)
break;
ret = krb5_kt_get_entry_wrapped(context, id, try_princ, kvno,
enctype, entry);
} while (ret == KRB5_KT_NOTFOUND && name_canon_iter);
krb5_free_name_canon_iterator(context, name_canon_iter);
return ret;
}
/**
* Copy the contents of `in' into `out'.
*

View File

@@ -883,6 +883,39 @@ typedef struct {
krb5int32 ks_salttype;
}krb5_key_salt_tuple;
/*
* Name canonicalization rules
*/
typedef enum krb5_name_canon_rule_type {
KRB5_NCRT_BOGUS = 0,
KRB5_NCRT_AS_IS,
KRB5_NCRT_QUALIFY,
KRB5_NCRT_RES_SEARCHLIST,
KRB5_NCRT_NSS
} krb5_name_canon_rule_type;
typedef enum krb5_name_canon_rule_options {
KRB5_NCRO_GC_ONLY = 1 << 0,
KRB5_NCRO_NO_REFERRALS = 1 << 1,
KRB5_NCRO_NO_REVLOOKUP = 1 << 2,
KRB5_NCRO_SECURE = 1 << 3
} krb5_name_canon_rule_options;
typedef struct krb5_name_canon_rule *krb5_name_canon_rule;
struct krb5_name_canon_rule {
krb5_name_canon_rule next;
krb5_name_canon_rule_type type;
krb5_name_canon_rule_options options;
char *domain;
char *realm;
unsigned int mindots;
};
typedef struct krb5_name_canon_iterator *krb5_name_canon_iterator;
#define krb5int_name_canon_rule_next(context, rule) (rule->next)
/*
*
*/

View File

@@ -57,6 +57,8 @@ host/admin@H5L.ORG
#include <fnmatch.h>
#include "resolve.h"
#include <assert.h>
#define princ_num_comp(P) ((P)->name.name_string.len)
#define princ_type(P) ((P)->name.name_type)
#define princ_comp(P) ((P)->name.name_string.val)
@@ -346,7 +348,7 @@ krb5_parse_name_flags(krb5_context context,
comp[n][q - start] = 0;
n++;
}
*principal = malloc(sizeof(**principal));
*principal = calloc(1, sizeof(**principal));
if (*principal == NULL) {
ret = ENOMEM;
krb5_set_error_message(context, ret,
@@ -965,6 +967,48 @@ krb5_principal_compare(krb5_context context,
krb5_const_principal princ1,
krb5_const_principal princ2)
{
if ((princ_type(princ1) == KRB5_NT_SRV_HST_NEEDS_CANON ||
princ_type(princ2) == KRB5_NT_SRV_HST_NEEDS_CANON) &&
princ_type(princ2) != princ_type(princ1)) {
krb5_error_code ret;
krb5_boolean princs_eq;
krb5_const_principal princ2canon;
krb5_const_principal other_princ;
krb5_principal try_princ;
krb5_name_canon_iterator nci;
if (princ_type(princ1) == KRB5_NT_SRV_HST_NEEDS_CANON) {
princ2canon = princ1;
other_princ = princ2;
} else {
princ2canon = princ2;
other_princ = princ1;
}
ret = krb5_name_canon_iterator_start(context, princ2canon, NULL, &nci);
if (ret)
return ret;
do {
ret = krb5_name_canon_iterate_princ(context, &nci, &try_princ,
NULL);
if (ret || try_princ == NULL)
break;
princs_eq = krb5_principal_compare(context, try_princ, other_princ);
if (princs_eq) {
krb5_free_name_canon_iterator(context, nci);
return TRUE;
}
} while (nci != NULL);
krb5_free_name_canon_iterator(context, nci);
}
/*
* Either neither princ requires canonicalization, both do, or
* no applicable name canonicalization rules were found and we fell
* through (chances are we'll fail here too in that last case).
* We're not going to do n^2 comparisons in the case of both princs
* requiring canonicalization.
*/
if(!krb5_realm_compare(context, princ1, princ2))
return FALSE;
return krb5_principal_compare_any_realm(context, princ1, princ2);
@@ -1013,28 +1057,17 @@ krb5_principal_match(krb5_context context,
return TRUE;
}
/**
* Create a principal for the service running on hostname. If
* KRB5_NT_SRV_HST is used, the hostname is canonization using DNS (or
* some other service), this is potentially insecure.
*
* @param context A Kerberos context.
* @param hostname hostname to use
* @param sname Service name to use
* @param type name type of pricipal, use KRB5_NT_SRV_HST or KRB5_NT_UNKNOWN.
* @param ret_princ return principal, free with krb5_free_principal().
*
* @return An krb5 error code, see krb5_get_error_message().
*
* @ingroup krb5_principal
/*
* This is the original krb5_sname_to_principal(), renamed to be a
* helper of the new one.
*/
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_sname_to_principal (krb5_context context,
const char *hostname,
const char *sname,
int32_t type,
krb5_principal *ret_princ)
static krb5_error_code
krb5_sname_to_principal_old(krb5_context context,
const char *realm,
const char *hostname,
const char *sname,
int32_t type,
krb5_principal *ret_princ)
{
krb5_error_code ret;
char localhost[MAXHOSTNAMELEN];
@@ -1060,19 +1093,25 @@ krb5_sname_to_principal (krb5_context context,
if(sname == NULL)
sname = "host";
if(type == KRB5_NT_SRV_HST) {
ret = krb5_expand_hostname_realms (context, hostname,
&host, &realms);
if (realm)
ret = krb5_expand_hostname(context, hostname, &host);
else
ret = krb5_expand_hostname_realms(context, hostname,
&host, &realms);
if (ret)
return ret;
strlwr(host);
hostname = host;
} else {
if (!realm)
realm = realms[0];
} else if (!realm) {
ret = krb5_get_host_realm(context, hostname, &realms);
if(ret)
return ret;
realm = realms[0];
}
ret = krb5_make_principal(context, ret_princ, realms[0], sname,
ret = krb5_make_principal(context, ret_princ, realm, sname,
hostname, NULL);
if(host)
free(host);
@@ -1096,6 +1135,7 @@ static const struct {
{ "ENT_PRINCIPAL_AND_ID", KRB5_NT_ENT_PRINCIPAL_AND_ID },
{ "MS_PRINCIPAL", KRB5_NT_MS_PRINCIPAL },
{ "MS_PRINCIPAL_AND_ID", KRB5_NT_MS_PRINCIPAL_AND_ID },
{ "SRV_HST_NEEDS_CANON", KRB5_NT_SRV_HST_NEEDS_CANON },
{ NULL, 0 }
};
@@ -1134,3 +1174,840 @@ krb5_principal_is_krbtgt(krb5_context context, krb5_const_principal p)
strcmp(p->name.name_string.val[0], KRB5_TGS_NAME) == 0;
}
/**
* Create a principal for the given service running on the given
* hostname. If KRB5_NT_SRV_HST is used, the hostname is canonicalized
* according the configured name canonicalization rules, with
* canonicalization delayed in some cases. One rule involves DNS, which
* is insecure unless DNSSEC is used, but we don't use DNSSEC-capable
* resolver APIs here, so that if DNSSEC is used we wouldn't know it.
*
* Canonicalization is immediate (not delayed) only when there is only
* one canonicalization rule and that rule indicates that we should do a
* host lookup by name (i.e., DNS).
*
* @param context A Kerberos context.
* @param hostname hostname to use
* @param sname Service name to use
* @param type name type of pricipal, use KRB5_NT_SRV_HST or KRB5_NT_UNKNOWN.
* @param ret_princ return principal, free with krb5_free_principal().
*
* @return An krb5 error code, see krb5_get_error_message().
*
* @ingroup krb5_principal
*/
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_sname_to_principal(krb5_context context,
const char *hostname,
const char *sname,
int32_t type,
krb5_principal *ret_princ)
{
char *realm, *remote_host;
krb5_error_code ret;
register char *cp;
char localname[MAXHOSTNAMELEN];
if ((type != KRB5_NT_UNKNOWN) &&
(type != KRB5_NT_SRV_HST))
return KRB5_SNAME_UNSUPP_NAMETYPE;
/* if hostname is NULL, use local hostname */
if (!hostname) {
if (gethostname(localname, MAXHOSTNAMELEN))
return errno;
hostname = localname;
}
/* if sname is NULL, use "host" */
if (!sname)
sname = "host";
remote_host = strdup(hostname);
if (!remote_host)
return ENOMEM;
if (type == KRB5_NT_SRV_HST) {
krb5_name_canon_rule rules;
/* Lower-case the hostname, because that's the convention */
for (cp = remote_host; *cp; cp++)
if (isupper((int) (*cp)))
*cp = tolower((int) (*cp));
ret = krb5int_get_name_canon_rules(context, &rules);
if (ret) {
_krb5_debug(context, 5, "Failed to get name canon rules: ret = %d",
ret);
return ret;
}
if (rules->type == KRB5_NCRT_NSS && rules->next == NULL) {
_krb5_debug(context, 5, "Using nss for name canon immediately "
"(without reverse lookups)");
/* For the default rule we'll just canonicalize here */
ret = krb5_sname_to_principal_old(context, NULL,
remote_host, sname,
KRB5_NT_SRV_HST,
ret_princ);
free(remote_host);
krb5int_free_name_canon_rules(context, rules);
return ret;
}
krb5int_free_name_canon_rules(context, rules);
}
/* Trailing dot(s) would be bad */
if (remote_host[0]) {
cp = remote_host + strlen(remote_host)-1;
if (*cp == '.')
*cp = '\0';
}
realm = ""; /* "Referral realm" -- borrowed from newer MIT */
ret = krb5_build_principal(context, ret_princ, strlen(realm),
realm, sname, remote_host,
(char *)0);
if (type == KRB5_NT_SRV_HST) {
/*
* Hostname canonicalization is done elsewhere (in
* krb5_get_credentials() and krb5_kt_get_entry()).
*
* We use special magic to indicate to those functions that
* this principal name requires canonicalization.
*/
(*ret_princ)->name.name_type = KRB5_NT_SRV_HST_NEEDS_CANON;
_krb5_debug(context, 5, "Building a delayed canon principal for %s/%s@",
sname, remote_host);
}
free(remote_host);
return ret;
}
/*
* Helper function to parse name canonicalization rule tokens.
*/
static
krb5_error_code
rule_parse_token(krb5_context context, krb5_name_canon_rule rule,
const char *tok)
{
long int n;
/*
* Rules consist of a sequence of tokens, some of which indicate
* what type of rule the rule is, and some of which set rule options
* or ancilliary data. First rule type token wins.
*/
/* Rule type tokens: */
if (strcmp(tok, "as-is") == 0) {
if (rule->type == KRB5_NCRT_BOGUS)
rule->type = KRB5_NCRT_AS_IS;
} else if (strcmp(tok, "qualify") == 0) {
if (rule->type == KRB5_NCRT_BOGUS)
rule->type = KRB5_NCRT_QUALIFY;
} else if (strcmp(tok, "use-resolver-searchlist") == 0) {
if (rule->type == KRB5_NCRT_BOGUS)
rule->type = KRB5_NCRT_RES_SEARCHLIST;
} else if (strcmp(tok, "nss") == 0) {
if (rule->type == KRB5_NCRT_BOGUS)
rule->type = KRB5_NCRT_NSS;
/* Rule options: */
} else if (strcmp(tok, "secure") == 0) {
rule->options |= KRB5_NCRO_SECURE;
} else if (strcmp(tok, "weak") == 0) {
rule->options &= ~KRB5_NCRO_SECURE;
} else if (strcmp(tok, "ccache_only") == 0) {
rule->options |= KRB5_NCRO_GC_ONLY;
} else if (strcmp(tok, "no_referrals") == 0) {
rule->options |= KRB5_NCRO_NO_REFERRALS;
} else if (strcmp(tok, "use_referrals") == 0) {
rule->options &= ~KRB5_NCRO_NO_REFERRALS;
} else if (strcmp(tok, "reverse_lookup") == 0) {
rule->options &= ~KRB5_NCRO_NO_REVLOOKUP;
} else if (strcmp(tok, "no_reverse_lookup") == 0) {
rule->options |= KRB5_NCRO_NO_REVLOOKUP;
/* Rule ancilliary data: */
} else if (strncmp(tok, "domain=", strlen("domain=")) == 0) {
free(rule->domain);
rule->domain = strdup(tok + strlen("domain="));
if (!rule->domain)
return ENOMEM;
} else if (strncmp(tok, "realm=", strlen("realm=")) == 0) {
free(rule->realm);
rule->realm = strdup(tok + strlen("realm="));
if (!rule->realm)
return ENOMEM;
} else if (strncmp(tok, "mindots=", strlen("mindots=")) == 0) {
errno = 0;
n = strtol(tok + strlen("mindots="), NULL, 10);
if (errno == 0 && n > 0 && n < 8)
rule->mindots = n;
}
/* ignore bogus tokens; it's not like we can print to stderr */
/* XXX Trace bogus tokens! */
return 0;
}
/*
* This helper function expands the DNS search list rule into qualify
* rules, one for each domain in the resolver search list.
*/
static
krb5_error_code
expand_search_list(krb5_context context, krb5_name_canon_rule *r, size_t *n,
size_t insert_point)
{
#if defined(HAVE_RES_NINIT) || defined(HAVE_RES_SEARCH)
#ifdef USE_RES_NINIT
struct __res_state statbuf;
#endif /* USE_RES_NINIT */
krb5_name_canon_rule_options opts;
krb5_name_canon_rule new_r;
char **dnsrch;
char **domains = NULL;
size_t srch_list_len;
size_t i;
int ret;
/* Sanitize */
assert((*n) > insert_point);
free((*r)[insert_point].domain);
free((*r)[insert_point].realm);
(*r)[insert_point].domain = NULL;
(*r)[insert_point].realm = NULL;
opts = (*r)[insert_point].options;
/*
* Would it be worthwhile to move this into context->os_context and
* krb5_os_init_context()?
*/
#ifdef USE_RES_NINIT
ret = res_ninit(&statbuf);
if (ret)
return ENOENT; /* XXX Create a better error */
dnsrch = statbuf.dnsrch;
srch_list_len = sizeof (statbuf.dnsrch) / sizeof (*statbuf.dnsrch);
#else
ret = res_init();
if (ret)
return ENOENT; /* XXX Create a better error */
dnsrch = _res.dnsrch;
srch_list_len = sizeof (_res.dnsrch) / sizeof (*_res.dnsrch);
#endif /* USE_RES_NINIT */
for (i = 0; i < srch_list_len; i++) {
if (!dnsrch || dnsrch[i] == NULL) {
srch_list_len = i;
break;
}
}
if (srch_list_len == 0) {
/* Invalidate this entry and return */
(*r)[insert_point].type = KRB5_NCRT_BOGUS;
return 0;
}
/*
* Pre-strdup() the search list so the realloc() below is the last
* point at which we can fail with ENOMEM.
*/
domains = calloc(srch_list_len, sizeof (*domains));
if (domains == NULL)
return ENOMEM;
for (i = 0; i < srch_list_len; i++) {
if ((domains[i] = strdup(dnsrch[i])) == NULL) {
for (i--; i >= 0; i--)
free(domains[i]);
return ENOMEM;
}
}
if (srch_list_len > 1) {
/* The -1 here is because we re-use this rule as one of the new rules */
new_r = realloc(*r, sizeof (**r) * ((*n) + srch_list_len - 1));
if (new_r == NULL) {
for (i = 0; i < srch_list_len; i++)
free(domains[i]);
free(domains);
return ENOMEM;
}
} else {
new_r = *r; /* srch_list_len == 1 */
}
/* Make room for the new rules */
if (insert_point < (*n) - 1) {
_krb5_debug(context, 5, "Inserting %d qualify rules in place of a "
"resolver searchlist rule", srch_list_len);
/*
* Move the rules that follow the search list rule down by
* srch_list_len - 1 rules.
*/
memmove(&new_r[insert_point + srch_list_len],
&new_r[insert_point + 1],
sizeof (new_r[0]) * ((*n) - (insert_point + 1)));
}
/*
* Clear in case the search-list rule is at the end of the rules;
* realloc() won't have done this for us.
*/
memset(&new_r[insert_point], 0, sizeof (new_r[0]) * srch_list_len);
/* Setup the new rules */
for (i = 0; i < srch_list_len; i++) {
_krb5_debug(context, 5, "Inserting qualify rule with domain=%s",
dnsrch[i]);
new_r[insert_point + i].type = KRB5_NCRT_QUALIFY;
new_r[insert_point + i].domain = domains[i];
new_r[insert_point + i].options = new_r[insert_point].options;
}
free(domains);
*r = new_r;
*n += srch_list_len - 1; /* -1 because we're replacing one rule */
#ifdef USE_RES_NINIT
res_ndestroy(&statbuf);
#endif /* USE_RES_NINIT */
#else
/* No resolver API by which to get search list -> use name service */
if ((*r)[insert_point].options & KRB5_NCRO_SECURE)
return ENOTSUP;
(*r)[insert_point].type = KRB5_NCRT_NSS;
#endif /* HAVE_RES_NINIT || HAVE_RES_SEARCH */
return 0;
}
/*
* Helper function to parse name canonicalization rules.
*/
static
krb5_error_code
parse_name_canon_rules(krb5_context context, char **rulestrs,
krb5_name_canon_rule *rules)
{
krb5_error_code ret;
char *tok;
char *cp;
char **cpp;
unsigned int n = 0;
unsigned int i, k;
krb5_name_canon_rule r;
for (cpp = rulestrs; *cpp; cpp++)
n++;
if ((r = calloc(n, sizeof (*r))) == NULL)
return ENOMEM;
/* This code is written without use of strtok_r() :( */
for (i = 0, k = 0; i < n; i++) {
cp = rulestrs[i];
do {
tok = cp;
cp = strpbrk(cp, ":");
if (cp)
*cp++ = '\0'; /* delimit token */
ret = rule_parse_token(context, &r[k], tok);
} while (cp && *cp);
/* Loosely validate parsed rule */
if (r[k].type == KRB5_NCRT_BOGUS ||
(r[k].type == KRB5_NCRT_QUALIFY && !r[k].domain) ||
(r[k].type == KRB5_NCRT_NSS && (r[k].domain || r[k].realm))) {
/* Invalid rule; mark it so and clean up */
r[k].type = KRB5_NCRT_BOGUS;
free(r[k].realm);
free(r[k].domain);
r[k].realm = NULL;
r[k].domain = NULL;
/* XXX Trace this! */
continue; /* bogus rule */
}
k++; /* good rule */
}
/* Expand search list rules */
for (i = 0; i < n; i++) {
if (r[i].type != KRB5_NCRT_RES_SEARCHLIST)
continue;
ret = expand_search_list(context, &r, &n, i);
if (ret)
return ret;
}
/* The first rule has to be valid */
k = n;
for (i = 0; i < n; i++) {
if (r[i].type != KRB5_NCRT_BOGUS) {
k = i;
break;
}
}
if (k > 0 && k < n) {
r[0] = r[k];
memset(&r[k], 0, sizeof (r[k])); /* KRB5_NCRT_BOGUS is 0 */
}
/* Setup next pointers */
for (i = 1, k = 0; i < n; i++) {
if (r[i].type == KRB5_NCRT_BOGUS)
continue;
r[k].next = &r[i];
k++;
}
*rules = r;
return 0; /* We don't communicate bad rule errors here */
}
/**
* This function returns an array of host-based service name
* canonicalization rules. The array of rules is organized as a list.
* See the definition of krb5_name_canon_rule.
*
* @param context A Kerberos context.
* @param rules Output location for array of rules.
*/
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5int_get_name_canon_rules(krb5_context context, krb5_name_canon_rule *rules)
{
krb5_error_code ret;
char **values = NULL;
char *realm = NULL;
*rules = NULL;
ret = krb5_get_default_realm(context, &realm);
if (ret == KRB5_CONFIG_NODEFREALM || ret == KRB5_CONFIG_CANTOPEN)
realm = NULL;
else if (ret)
return ret;
if (realm) {
values = krb5_config_get_strings(context, NULL,
"libdefaults",
realm,
"name_canon_rules", NULL);
free(realm);
}
if (!values) {
values = krb5_config_get_strings(context, NULL,
"libdefaults",
"name_canon_rules", NULL);
}
if (!values || !values[0]) {
/* Default rule: do the dreaded getaddrinfo()/getnameinfo() dance */
if ((*rules = calloc(1, sizeof (**rules))) == NULL)
return ENOMEM;
(*rules)->type = KRB5_NCRT_NSS;
return 0;
}
ret = parse_name_canon_rules(context, values, rules);
krb5_config_free_strings(values);
if (ret)
return ret;
{
size_t k;
krb5_name_canon_rule r;
for (k = 0, r = *rules; r; r = r->next, k++) {
_krb5_debug(context, 5,
"Name canon rule %d type=%d, options=%x, mindots=%d, "
"domain=%s, realm=%s",
k, r->type, r->options, r->mindots,
r->domain ? r->domain : "<none>",
r->realm ? r->realm : "<none>"
);
}
}
if ((*rules)[0].type != KRB5_NCRT_BOGUS)
return 0; /* success! */
free(*rules);
*rules = NULL;
/* fall through to return default rule */
_krb5_debug(context, 5, "All name canon rules are bogus!");
return 0;
}
static
krb5_error_code
get_host_realm(krb5_context context, const char *hostname, char **realm)
{
krb5_error_code ret;
char **hrealms = NULL;
*realm = NULL;
if ((ret = krb5_get_host_realm(context, hostname, &hrealms)))
return ret;
if (!hrealms)
return KRB5_ERR_HOST_REALM_UNKNOWN;
if (!hrealms[0]) {
krb5_free_host_realm(context, hrealms);
return KRB5_ERR_HOST_REALM_UNKNOWN;
}
*realm = strdup(hrealms[0]);
krb5_free_host_realm(context, hrealms);
return 0;
}
/**
* Apply a name canonicalization rule to a principal.
*
* @param context Kerberos context
* @param rule name canon rule
* @param in_princ principal name
* @param out_print resulting principal name
* @param rule_opts options for this rule
*/
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5int_apply_name_canon_rule(krb5_context context, krb5_name_canon_rule rule,
krb5_const_principal in_princ, krb5_principal *out_princ,
krb5_name_canon_rule_options *rule_opts)
{
krb5_error_code ret;
unsigned int ndots = 0;
char *realm = NULL;
const char *sname = NULL;
const char *hostname = NULL;
char *new_hostname;
const char *cp;
assert(in_princ->name.name_type == KRB5_NT_SRV_HST_NEEDS_CANON);
*out_princ = NULL;
if (rule_opts)
*rule_opts = 0;
if (rule->type == KRB5_NCRT_BOGUS)
return 0; /* rule doesn't apply */
sname = krb5_principal_get_comp_string(context, in_princ, 0);
hostname = krb5_principal_get_comp_string(context, in_princ, 1);
_krb5_debug(context, 5, "Applying a name rule (type %d) to %s", rule->type,
hostname);
if (rule_opts)
*rule_opts = rule->options;
ret = 0;
switch (rule->type) {
case KRB5_NCRT_AS_IS:
if (rule->mindots > 0) {
for (cp = strchr(hostname, '.'); cp && *cp; cp = strchr(cp, '.'))
ndots++;
if (ndots < rule->mindots)
goto out; /* *out_princ == NULL; rule doesn't apply */
}
if (rule->domain) {
cp = strstr(hostname, rule->domain);
if (cp == NULL)
goto out; /* *out_princ == NULL; rule doesn't apply */
if (cp != hostname && cp[-1] != '.')
goto out;
}
/* Rule matches, copy princ with hostname as-is, with normal magic */
realm = rule->realm;
if (!realm) {
ret = get_host_realm(context, hostname, &realm);
if (ret)
goto out;
}
_krb5_debug(context, 5, "As-is rule building a princ with realm=%s, "
"sname=%s, and hostname=%s", rule->realm, sname, hostname);
ret = krb5_build_principal(context, out_princ,
strlen(rule->realm),
rule->realm, sname, hostname,
(char *)0);
goto out;
break;
case KRB5_NCRT_QUALIFY:
assert(rule->domain != NULL);
cp = strchr(hostname, '.');
if (cp && (cp = strstr(cp, rule->domain))) {
new_hostname = strdup(hostname);
if (new_hostname == NULL) {
ret = ENOMEM;
goto out;
}
} else {
size_t len;
len = strlen(hostname) + strlen(rule->domain) + 2;
if ((new_hostname = malloc(len)) == NULL) {
ret = ENOMEM;
goto out;
}
/* We use strcpy() and strcat() for portability for now */
strcpy(new_hostname, hostname);
if (rule->domain[0] != '.')
strcat(new_hostname, ".");
strcat(new_hostname, rule->domain);
}
realm = rule->realm;
if (!realm) {
ret = get_host_realm(context, new_hostname, &realm);
if (ret)
goto out;
}
_krb5_debug(context, 5, "Building a princ with realm=%s, sname=%s, "
"and hostname=%s", rule->realm, sname, new_hostname);
ret = krb5_build_principal(context, out_princ,
strlen(realm), realm,
sname, new_hostname, (char *)0);
free(new_hostname);
goto out;
break;
case KRB5_NCRT_NSS:
_krb5_debug(context, 5, "Using name service lookups (without "
"reverse lookups)");
ret = krb5_sname_to_principal_old(context, rule->realm,
hostname, sname,
KRB5_NT_SRV_HST,
out_princ);
if (rule->next != NULL &&
(ret == KRB5_ERR_BAD_HOSTNAME ||
ret == KRB5_ERR_HOST_REALM_UNKNOWN))
/*
* Bad hostname / realm unknown -> rule inapplicable if
* there's more rules. If it's the last rule then we want
* to return all errors from krb5_sname_to_principal_old()
* here.
*/
ret = 0;
goto out;
break;
default:
/* Can't happen, but we need this to shut up gcc */
break;
}
out:
if (!ret && out_princ) {
char *unparsed;
ret = krb5_unparse_name(context, *out_princ, &unparsed);
if (ret) {
_krb5_debug(context, 5, "Couldn't unparse resulting princ! (%d)",
ret);
} else {
_krb5_debug(context, 5, "Name canon rule application yields this "
"unparsed princ: %s", unparsed);
free(unparsed);
}
} else if (!ret) {
_krb5_debug(context, 5, "Name canon rule did not apply");
} else {
_krb5_debug(context, 5, "Name canon rule application error: %d", ret);
}
if (realm != rule->realm)
free(realm);
if (*out_princ)
(*out_princ)->name.name_type = KRB5_NT_SRV_HST;
return ret;
}
/**
* Free name canonicalization rules
*/
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5int_free_name_canon_rules(krb5_context context, krb5_name_canon_rule rules)
{
krb5_name_canon_rule r;
for (r = rules; r; r = r->next) {
free(r->realm);
free(r->domain);
}
free(rules);
rules = NULL;
}
struct krb5_name_canon_iterator {
krb5_name_canon_rule rules;
krb5_name_canon_rule rule;
krb5_const_principal in_princ;
krb5_principal tmp_princ;
krb5_creds *creds;
int is_trivial;
int done;
};
/**
* Initialize name canonicalization iterator.
*
* @param context Kerberos context
* @param in_princ principal name to be canonicalized OR
* @param in_creds credentials whose server is to be canonicalized
* @param iter output iterator object
*/
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_name_canon_iterator_start(krb5_context context,
krb5_const_principal in_princ,
krb5_creds *in_creds,
krb5_name_canon_iterator *iter)
{
krb5_error_code ret;
krb5_name_canon_iterator state;
krb5_const_principal princ;
*iter = NULL;
state = calloc(1, sizeof (*state));
if (state == NULL)
return ENOMEM;
princ = in_princ ? in_princ : in_creds->server;
if (princ_type(princ) != KRB5_NT_SRV_HST_NEEDS_CANON) {
/*
* Name needs no canon -> trivial iterator; we still want an
* iterator just so as to keep callers simple.
*/
state->is_trivial = 1;
state->creds = in_creds;
} else {
ret = krb5int_get_name_canon_rules(context, &state->rules);
if (ret) goto err;
state->rule = state->rules;
}
state->in_princ = princ;
if (in_creds) {
ret = krb5_copy_creds(context, in_creds, &state->creds);
if (ret) goto err;
state->tmp_princ = state->creds->server; /* so we don't leak */
}
*iter = state;
return 0;
err:
krb5_free_name_canon_iterator(context, state);
return ENOMEM;
}
/*
* Helper for name canon iteration.
*/
static krb5_error_code
krb5_name_canon_iterate(krb5_context context,
krb5_name_canon_iterator *iter,
krb5_name_canon_rule_options *rule_opts)
{
krb5_error_code ret;
krb5_name_canon_iterator state = *iter;
if (!state)
return 0;
if (state->done) {
krb5_free_name_canon_iterator(context, state);
*iter = NULL;
return 0;
}
if (state->is_trivial && !state->done) {
state->done = 1;
return 0;
}
krb5_free_principal(context, state->tmp_princ);
do {
ret = krb5int_apply_name_canon_rule(context, state->rule,
state->in_princ, &state->tmp_princ, rule_opts);
if (ret)
return ret;
state->rule = state->rule->next;
} while (state->rule != NULL && state->tmp_princ == NULL);
if (state->tmp_princ == NULL) {
krb5_free_name_canon_iterator(context, state);
*iter = NULL;
return 0;
}
if (state->creds)
state->creds->server = state->tmp_princ;
if (state->rule == NULL)
state->done = 1;
return 0;
}
/**
* Iteratively apply name canon rules, outputing a principal and rule
* options each time. Iteration completes when the @iter is NULL on
* return or when an error is returned. Callers must free the iterator
* if they abandon it mid-way.
*
* @param context Kerberos context
* @param iter name canon rule iterator (input/output)
* @param try_princ output principal name
* @param rule_opts output rule options
*/
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_name_canon_iterate_princ(krb5_context context,
krb5_name_canon_iterator *iter,
krb5_principal *try_princ,
krb5_name_canon_rule_options *rule_opts)
{
krb5_error_code ret;
*try_princ = NULL;
ret = krb5_name_canon_iterate(context, iter, rule_opts);
if (*iter)
*try_princ = (*iter)->tmp_princ;
return ret;
}
/**
* Iteratively apply name canon rules, outputing a krb5_creds and rule
* options each time. Iteration completes when the @iter is NULL on
* return or when an error is returned. Callers must free the iterator
* if they abandon it mid-way.
*
* @param context Kerberos context
* @param iter name canon rule iterator
* @param try_creds output krb5_creds
* @param rule_opts output rule options
*/
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_name_canon_iterate_creds(krb5_context context,
krb5_name_canon_iterator *iter,
krb5_creds **try_creds,
krb5_name_canon_rule_options *rule_opts)
{
krb5_error_code ret;
*try_creds = NULL;
ret = krb5_name_canon_iterate(context, iter, rule_opts);
if (*iter)
*try_creds = (*iter)->creds;
return ret;
}
/**
* Free a name canonicalization rule iterator.
*/
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_free_name_canon_iterator(krb5_context context,
krb5_name_canon_iterator iter)
{
if (iter == NULL)
return;
if (!iter->is_trivial) {
if (iter->creds) {
krb5_free_creds(context, iter->creds);
iter->tmp_princ = NULL;
}
if (iter->tmp_princ)
krb5_free_principal(context, iter->tmp_princ);
krb5int_free_name_canon_rules(context, iter->rules);
}
free(iter);
}