Deferred hostname canon using name canon rules
This commit is contained in:
@@ -1608,6 +1608,7 @@ match_rfc_san(krb5_context context,
|
|||||||
return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
|
return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
memset(&principal, 0, sizeof (principal));
|
||||||
principal.name = kn.principalName;
|
principal.name = kn.principalName;
|
||||||
principal.realm = kn.realm;
|
principal.realm = kn.realm;
|
||||||
|
|
||||||
|
@@ -293,6 +293,7 @@ change (krb5_auth_context auth_context,
|
|||||||
if (chpw.targname) {
|
if (chpw.targname) {
|
||||||
krb5_principal_data princ;
|
krb5_principal_data princ;
|
||||||
|
|
||||||
|
memset(&princ, 0, sizeof (princ));
|
||||||
princ.name = *chpw.targname;
|
princ.name = *chpw.targname;
|
||||||
princ.realm = *chpw.targrealm;
|
princ.realm = *chpw.targrealm;
|
||||||
if (princ.realm == NULL) {
|
if (princ.realm == NULL) {
|
||||||
|
@@ -85,7 +85,7 @@ main(int argc, char **argv)
|
|||||||
krb5_creds *out;
|
krb5_creds *out;
|
||||||
int optidx = 0;
|
int optidx = 0;
|
||||||
krb5_get_creds_opt opt;
|
krb5_get_creds_opt opt;
|
||||||
krb5_principal server;
|
krb5_principal server = NULL;
|
||||||
krb5_principal impersonate = NULL;
|
krb5_principal impersonate = NULL;
|
||||||
|
|
||||||
setprogname (argv[0]);
|
setprogname (argv[0]);
|
||||||
@@ -108,9 +108,6 @@ main(int argc, char **argv)
|
|||||||
argc -= optidx;
|
argc -= optidx;
|
||||||
argv += optidx;
|
argv += optidx;
|
||||||
|
|
||||||
if (argc != 1)
|
|
||||||
usage (1);
|
|
||||||
|
|
||||||
if(cache_str) {
|
if(cache_str) {
|
||||||
ret = krb5_cc_resolve(context, cache_str, &cache);
|
ret = krb5_cc_resolve(context, cache_str, &cache);
|
||||||
if (ret)
|
if (ret)
|
||||||
@@ -190,18 +187,70 @@ main(int argc, char **argv)
|
|||||||
KRB5_GC_CONSTRAINED_DELEGATION);
|
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) {
|
if (nametype_str) {
|
||||||
int32_t nametype;
|
int32_t nametype;
|
||||||
|
int do_sn2p = 1;
|
||||||
|
char *sname = NULL;
|
||||||
|
char *hname = NULL;
|
||||||
|
|
||||||
ret = krb5_parse_nametype(context, nametype_str, &nametype);
|
ret = krb5_parse_nametype(context, nametype_str, &nametype);
|
||||||
if (ret)
|
if (ret)
|
||||||
krb5_err(context, 1, ret, "krb5_parse_nametype");
|
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);
|
ret = krb5_get_creds(context, opt, cache, server, &out);
|
||||||
|
@@ -89,7 +89,8 @@ NAME-TYPE ::= INTEGER {
|
|||||||
KRB5_NT_ENT_PRINCIPAL_AND_ID(-130), -- Windows 2000 UPN and SID
|
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(-128), -- NT 4 style name
|
||||||
KRB5_NT_MS_PRINCIPAL_AND_ID(-129), -- NT style name and SID
|
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
|
-- message types
|
||||||
@@ -269,6 +270,10 @@ PrincipalName ::= SEQUENCE {
|
|||||||
Principal ::= SEQUENCE {
|
Principal ::= SEQUENCE {
|
||||||
name[0] PrincipalName,
|
name[0] PrincipalName,
|
||||||
realm[1] Realm
|
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
|
Principals ::= SEQUENCE OF Principal
|
||||||
|
@@ -53,7 +53,7 @@ _krb5_principalname2krb5_principal (krb5_context context,
|
|||||||
krb5_error_code ret;
|
krb5_error_code ret;
|
||||||
krb5_principal p;
|
krb5_principal p;
|
||||||
|
|
||||||
p = malloc(sizeof(*p));
|
p = calloc(1, sizeof(*p));
|
||||||
if (p == NULL)
|
if (p == NULL)
|
||||||
return ENOMEM;
|
return ENOMEM;
|
||||||
ret = copy_PrincipalName(&from, &p->name);
|
ret = copy_PrincipalName(&from, &p->name);
|
||||||
|
@@ -1116,6 +1116,68 @@ _krb5_get_cred_kdc_any(krb5_context context,
|
|||||||
ret_tgts);
|
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_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
|
||||||
krb5_get_credentials_with_flags(krb5_context context,
|
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_creds **out_creds)
|
||||||
{
|
{
|
||||||
krb5_error_code ret;
|
krb5_error_code ret;
|
||||||
|
krb5_name_canon_iterator name_canon_iter = NULL;
|
||||||
|
krb5_name_canon_rule_options rule_opts;
|
||||||
krb5_creds **tgts;
|
krb5_creds **tgts;
|
||||||
|
krb5_creds *try_creds;
|
||||||
krb5_creds *res_creds;
|
krb5_creds *res_creds;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
@@ -1134,6 +1199,7 @@ krb5_get_credentials_with_flags(krb5_context context,
|
|||||||
ret = krb5_enctype_valid(context, in_creds->session.keytype);
|
ret = krb5_enctype_valid(context, in_creds->session.keytype);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
options |= KRB5_TC_MATCH_KEYTYPE;
|
||||||
}
|
}
|
||||||
|
|
||||||
*out_creds = NULL;
|
*out_creds = NULL;
|
||||||
@@ -1144,46 +1210,43 @@ krb5_get_credentials_with_flags(krb5_context context,
|
|||||||
return ENOMEM;
|
return ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_creds->session.keytype)
|
if (in_creds->server->name.name_type == KRB5_NT_SRV_HST_NEEDS_CANON) {
|
||||||
options |= KRB5_TC_MATCH_KEYTYPE;
|
ret = check_cc(context, options, ccache, in_creds, res_creds);
|
||||||
|
if (ret == 0) {
|
||||||
/*
|
|
||||||
* 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) {
|
|
||||||
*out_creds = res_creds;
|
*out_creds = res_creds;
|
||||||
return 0;
|
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)
|
if(options & KRB5_GC_CACHED)
|
||||||
return not_found(context, in_creds->server, KRB5_CC_NOTFOUND);
|
goto next_rule;
|
||||||
|
|
||||||
if(options & KRB5_GC_USER_USER)
|
if(options & KRB5_GC_USER_USER)
|
||||||
flags.b.enc_tkt_in_skey = 1;
|
flags.b.enc_tkt_in_skey = 1;
|
||||||
@@ -1192,15 +1255,26 @@ krb5_get_credentials_with_flags(krb5_context context,
|
|||||||
|
|
||||||
tgts = NULL;
|
tgts = NULL;
|
||||||
ret = _krb5_get_cred_kdc_any(context, flags, ccache,
|
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++) {
|
for(i = 0; tgts && tgts[i]; i++) {
|
||||||
krb5_cc_store_cred(context, ccache, tgts[i]);
|
krb5_cc_store_cred(context, ccache, tgts[i]);
|
||||||
krb5_free_creds(context, tgts[i]);
|
krb5_free_creds(context, tgts[i]);
|
||||||
}
|
}
|
||||||
free(tgts);
|
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)
|
if(ret == 0 && (options & KRB5_GC_NO_STORE) == 0)
|
||||||
krb5_cc_store_cred(context, ccache, *out_creds);
|
store_cred(context, ccache, in_creds->server, *out_creds);
|
||||||
return ret;
|
|
||||||
|
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
|
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_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
|
||||||
krb5_get_creds(krb5_context context,
|
krb5_get_creds(krb5_context context,
|
||||||
krb5_get_creds_opt opt,
|
krb5_get_creds_opt opt,
|
||||||
@@ -1328,7 +1401,10 @@ krb5_get_creds(krb5_context context,
|
|||||||
krb5_creds in_creds;
|
krb5_creds in_creds;
|
||||||
krb5_error_code ret;
|
krb5_error_code ret;
|
||||||
krb5_creds **tgts;
|
krb5_creds **tgts;
|
||||||
|
krb5_creds *try_creds;
|
||||||
krb5_creds *res_creds;
|
krb5_creds *res_creds;
|
||||||
|
krb5_name_canon_iterator name_canon_iter = NULL;
|
||||||
|
krb5_name_canon_rule_options rule_opts;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (opt && opt->enctype) {
|
if (opt && opt->enctype) {
|
||||||
@@ -1364,48 +1440,42 @@ krb5_get_creds(krb5_context context,
|
|||||||
options |= KRB5_TC_MATCH_KEYTYPE;
|
options |= KRB5_TC_MATCH_KEYTYPE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/* Check for entry in ccache */
|
||||||
* If we got a credential, check if credential is expired before
|
if (inprinc->name.name_type == KRB5_NT_SRV_HST_NEEDS_CANON) {
|
||||||
* returning it.
|
ret = check_cc(context, options, ccache, &in_creds, res_creds);
|
||||||
*/
|
if (ret == 0) {
|
||||||
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) {
|
|
||||||
*out_creds = res_creds;
|
*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) {
|
} 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;
|
goto out;
|
||||||
}
|
}
|
||||||
|
if(options & KRB5_GC_CACHED)
|
||||||
|
goto next_rule;
|
||||||
|
|
||||||
if(options & KRB5_GC_USER_USER) {
|
if(options & KRB5_GC_USER_USER) {
|
||||||
flags.b.enc_tkt_in_skey = 1;
|
flags.b.enc_tkt_in_skey = 1;
|
||||||
options |= KRB5_GC_NO_STORE;
|
options |= KRB5_GC_NO_STORE;
|
||||||
@@ -1423,18 +1493,28 @@ krb5_get_creds(krb5_context context,
|
|||||||
|
|
||||||
tgts = NULL;
|
tgts = NULL;
|
||||||
ret = _krb5_get_cred_kdc_any(context, flags, ccache,
|
ret = _krb5_get_cred_kdc_any(context, flags, ccache,
|
||||||
&in_creds, opt->self, opt->ticket,
|
try_creds, opt->self, opt->ticket,
|
||||||
out_creds, &tgts);
|
out_creds, &tgts);
|
||||||
krb5_free_principal(context, in_creds.client);
|
|
||||||
for(i = 0; tgts && tgts[i]; i++) {
|
for(i = 0; tgts && tgts[i]; i++) {
|
||||||
krb5_cc_store_cred(context, ccache, tgts[i]);
|
krb5_cc_store_cred(context, ccache, tgts[i]);
|
||||||
krb5_free_creds(context, tgts[i]);
|
krb5_free_creds(context, tgts[i]);
|
||||||
}
|
}
|
||||||
free(tgts);
|
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);
|
_krb5_debug(context, 5, "krb5_get_creds: ret = %d", ret);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@@ -588,30 +588,13 @@ _krb5_kt_principal_not_found(krb5_context context,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static krb5_error_code
|
||||||
/**
|
krb5_kt_get_entry_wrapped(krb5_context context,
|
||||||
* Retrieve the keytab entry for `principal, kvno, enctype' into `entry'
|
krb5_keytab id,
|
||||||
* from the keytab `id'. Matching is done like krb5_kt_compare().
|
krb5_const_principal principal,
|
||||||
*
|
krb5_kvno kvno,
|
||||||
* @param context a Keberos context.
|
krb5_enctype enctype,
|
||||||
* @param id a keytab.
|
krb5_keytab_entry *entry)
|
||||||
* @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_keytab_entry tmp;
|
krb5_keytab_entry tmp;
|
||||||
krb5_error_code ret;
|
krb5_error_code ret;
|
||||||
@@ -654,6 +637,56 @@ krb5_kt_get_entry(krb5_context context,
|
|||||||
return 0;
|
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'.
|
* Copy the contents of `in' into `out'.
|
||||||
*
|
*
|
||||||
|
@@ -883,6 +883,39 @@ typedef struct {
|
|||||||
krb5int32 ks_salttype;
|
krb5int32 ks_salttype;
|
||||||
}krb5_key_salt_tuple;
|
}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)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
@@ -57,6 +57,8 @@ host/admin@H5L.ORG
|
|||||||
#include <fnmatch.h>
|
#include <fnmatch.h>
|
||||||
#include "resolve.h"
|
#include "resolve.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
#define princ_num_comp(P) ((P)->name.name_string.len)
|
#define princ_num_comp(P) ((P)->name.name_string.len)
|
||||||
#define princ_type(P) ((P)->name.name_type)
|
#define princ_type(P) ((P)->name.name_type)
|
||||||
#define princ_comp(P) ((P)->name.name_string.val)
|
#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;
|
comp[n][q - start] = 0;
|
||||||
n++;
|
n++;
|
||||||
}
|
}
|
||||||
*principal = malloc(sizeof(**principal));
|
*principal = calloc(1, sizeof(**principal));
|
||||||
if (*principal == NULL) {
|
if (*principal == NULL) {
|
||||||
ret = ENOMEM;
|
ret = ENOMEM;
|
||||||
krb5_set_error_message(context, ret,
|
krb5_set_error_message(context, ret,
|
||||||
@@ -965,6 +967,48 @@ krb5_principal_compare(krb5_context context,
|
|||||||
krb5_const_principal princ1,
|
krb5_const_principal princ1,
|
||||||
krb5_const_principal princ2)
|
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))
|
if(!krb5_realm_compare(context, princ1, princ2))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
return krb5_principal_compare_any_realm(context, princ1, princ2);
|
return krb5_principal_compare_any_realm(context, princ1, princ2);
|
||||||
@@ -1013,28 +1057,17 @@ krb5_principal_match(krb5_context context,
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Create a principal for the service running on hostname. If
|
* This is the original krb5_sname_to_principal(), renamed to be a
|
||||||
* KRB5_NT_SRV_HST is used, the hostname is canonization using DNS (or
|
* helper of the new one.
|
||||||
* 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
|
|
||||||
*/
|
*/
|
||||||
|
static krb5_error_code
|
||||||
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
|
krb5_sname_to_principal_old(krb5_context context,
|
||||||
krb5_sname_to_principal (krb5_context context,
|
const char *realm,
|
||||||
const char *hostname,
|
const char *hostname,
|
||||||
const char *sname,
|
const char *sname,
|
||||||
int32_t type,
|
int32_t type,
|
||||||
krb5_principal *ret_princ)
|
krb5_principal *ret_princ)
|
||||||
{
|
{
|
||||||
krb5_error_code ret;
|
krb5_error_code ret;
|
||||||
char localhost[MAXHOSTNAMELEN];
|
char localhost[MAXHOSTNAMELEN];
|
||||||
@@ -1060,19 +1093,25 @@ krb5_sname_to_principal (krb5_context context,
|
|||||||
if(sname == NULL)
|
if(sname == NULL)
|
||||||
sname = "host";
|
sname = "host";
|
||||||
if(type == KRB5_NT_SRV_HST) {
|
if(type == KRB5_NT_SRV_HST) {
|
||||||
ret = krb5_expand_hostname_realms (context, hostname,
|
if (realm)
|
||||||
&host, &realms);
|
ret = krb5_expand_hostname(context, hostname, &host);
|
||||||
|
else
|
||||||
|
ret = krb5_expand_hostname_realms(context, hostname,
|
||||||
|
&host, &realms);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
strlwr(host);
|
strlwr(host);
|
||||||
hostname = host;
|
hostname = host;
|
||||||
} else {
|
if (!realm)
|
||||||
|
realm = realms[0];
|
||||||
|
} else if (!realm) {
|
||||||
ret = krb5_get_host_realm(context, hostname, &realms);
|
ret = krb5_get_host_realm(context, hostname, &realms);
|
||||||
if(ret)
|
if(ret)
|
||||||
return 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);
|
hostname, NULL);
|
||||||
if(host)
|
if(host)
|
||||||
free(host);
|
free(host);
|
||||||
@@ -1096,6 +1135,7 @@ static const struct {
|
|||||||
{ "ENT_PRINCIPAL_AND_ID", KRB5_NT_ENT_PRINCIPAL_AND_ID },
|
{ "ENT_PRINCIPAL_AND_ID", KRB5_NT_ENT_PRINCIPAL_AND_ID },
|
||||||
{ "MS_PRINCIPAL", KRB5_NT_MS_PRINCIPAL },
|
{ "MS_PRINCIPAL", KRB5_NT_MS_PRINCIPAL },
|
||||||
{ "MS_PRINCIPAL_AND_ID", KRB5_NT_MS_PRINCIPAL_AND_ID },
|
{ "MS_PRINCIPAL_AND_ID", KRB5_NT_MS_PRINCIPAL_AND_ID },
|
||||||
|
{ "SRV_HST_NEEDS_CANON", KRB5_NT_SRV_HST_NEEDS_CANON },
|
||||||
{ NULL, 0 }
|
{ 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;
|
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);
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user