}
memset((char *)&creds, 0, sizeof(creds));
+ printf("calling krb5_sname_to_principal\n");
if ((r = krb5_sname_to_principal(telnet_context, RemoteHostName,
"host", KRB5_NT_SRV_HST,
&creds.server))) {
printf("telnet: Kerberos V5: error while constructing service name: %s\r\n", error_message(r));
return(0);
}
+ printf("done calling krb5_sname_to_principal\n");
if (telnet_krb5_realm != NULL) {
krb5_data rdata;
#define krb5_copy_error_state(CTX, OCTX) \
krb5int_set_error(&(CTX)->errinfo, (OCTX)->errinfo.code, "%s", (OCTX)->errinfo.msg)
+/*
+ * Referral definitions, debugging hooks, and subfunctions.
+ */
+#define KRB5_REFERRAL_MAXHOPS 5
+/* #define DEBUG_REFERRALS */
+
+#ifdef DEBUG_REFERRALS
+void krb5int_dbgref_dump_principal(char *, krb5_principal);
+#endif
+
+/* Common hostname-parsing code. */
+krb5_error_code KRB5_CALLCONV krb5int_clean_hostname
+ (krb5_context,
+ const char *,
+ char *,
+ size_t);
+
#endif /* _KRB5_INT_H */
? (princ)->data + (i) \
: NULL)
+/*
+ * Constants for realm referrals.
+ */
+#define KRB5_REFERRAL_REALM ""
+
+/*
+ * Referral-specific functions.
+ */
+krb5_boolean KRB5_CALLCONV krb5_is_referral_realm(krb5_data *);
+
/*
* end "base-defs.h"
*/
#define KRB5_KEYUSAGE_PA_SAM_CHALLENGE_TRACKID 26
#define KRB5_KEYUSAGE_PA_SAM_RESPONSE 27
+/* Defined in KDC referrals draft */
+#define KRB5_KEYUSAGE_PA_REFERRAL 26 /* XXX note conflict with above */
+
krb5_boolean KRB5_CALLCONV krb5_c_valid_enctype
(krb5_enctype ktype);
krb5_boolean KRB5_CALLCONV krb5_c_valid_cksumtype
/* #define KDC_OPT_RESERVED 0x00080000 */
/* #define KDC_OPT_RESERVED 0x00040000 */
#define KDC_OPT_REQUEST_ANONYMOUS 0x00020000
-/* #define KDC_OPT_RESERVED 0x00010000 */
+#define KDC_OPT_CANONICALIZE 0x00010000
/* #define KDC_OPT_RESERVED 0x00008000 */
/* #define KDC_OPT_RESERVED 0x00004000 */
/* #define KDC_OPT_RESERVED 0x00002000 */
#define KRB5_PADATA_SAM_RESPONSE 13 /* draft challenge system response */
#define KRB5_PADATA_PK_AS_REQ 14 /* PKINIT */
#define KRB5_PADATA_PK_AS_REP 15 /* PKINIT */
-#define KRB5_PADATA_ETYPE_INFO2 19
+#define KRB5_PADATA_ETYPE_INFO2 19
+#define KRB5_PADATA_REFERRAL 25 /* draft referral system */
#define KRB5_PADATA_SAM_CHALLENGE_2 30 /* draft challenge system, updated */
#define KRB5_PADATA_SAM_RESPONSE_2 31 /* draft challenge system, updated */
(krb5_context,
const char *,
char *** );
+krb5_error_code KRB5_CALLCONV krb5_get_fallback_host_realm
+ (krb5_context,
+ krb5_data *,
+ char *** );
krb5_error_code KRB5_CALLCONV krb5_free_host_realm
(krb5_context,
char * const * );
krb5_princ_component(context, tempprinc, i)->data = 0;
}
- if (tempprinc->realm.length) {
- tempprinc->realm.data =
- malloc(tempprinc->realm.length = inprinc->realm.length);
- if (!tempprinc->realm.data) {
- for (i = 0; i < nelems; i++)
- free(krb5_princ_component(context, tempprinc, i)->data);
- free(tempprinc->data);
- free(tempprinc);
- return ENOMEM;
- }
- memcpy(tempprinc->realm.data, inprinc->realm.data,
- inprinc->realm.length);
- } else
- tempprinc->realm.data = 0;
+ tempprinc->realm.data =
+ malloc((tempprinc->realm.length = inprinc->realm.length) + 1);
+ if (!tempprinc->realm.data) {
+ for (i = 0; i < nelems; i++)
+ free(krb5_princ_component(context, tempprinc, i)->data);
+ free(tempprinc->data);
+ free(tempprinc);
+ return ENOMEM;
+ }
+ memcpy(tempprinc->realm.data, inprinc->realm.data,
+ inprinc->realm.length);
+ tempprinc->realm.data[tempprinc->realm.length] = 0;
*outprinc = tempprinc;
return 0;
* On success, OUT_CRED contains the desired credentials; the caller
* must free them.
*
+ * Beware memory management issues if you have modifications in mind.
+ * With the addition of referral support, it is now the case that *tgts,
+ * referral_tgts, tgtptr, referral_tgts, and *out_creds all may point to
+ * the same credential at different times.
+ *
* Returns errors, system errors.
*/
krb5_creds *in_cred, krb5_creds **out_cred,
krb5_creds ***tgts, int kdcopt)
{
- krb5_error_code retval;
- krb5_principal client, server;
- krb5_creds tgtq, cc_tgt, *tgtptr;
+ krb5_error_code retval, subretval;
+ krb5_principal client, server, supplied_server, out_supplied_server;
+ krb5_creds tgtq, cc_tgt, *tgtptr, *referral_tgts[KRB5_REFERRAL_MAXHOPS];
krb5_boolean old_use_conf_ktypes;
+ char **hrealms;
+ int referral_count, i;
+ /*
+ * Set up client and server pointers. Make a fresh and modifyable
+ * copy of the in_cred server and save the supplied version.
+ */
client = in_cred->client;
- server = in_cred->server;
+ if ((retval=krb5_copy_principal(context, in_cred->server, &server)))
+ return retval;
+ /* We need a second copy for the output creds. */
+ if ((retval = krb5_copy_principal(context, server, &out_supplied_server)) != 0 ) {
+ krb5_free_principal(context, server);
+ return retval;
+ }
+ supplied_server = in_cred->server;
+ in_cred->server=server;
+
+
+#ifdef DEBUG_REFERRALS
+ krb5int_dbgref_dump_principal("gc_from_kdc initial client", client);
+ krb5int_dbgref_dump_principal("gc_from_kdc initial server", server);
+#endif
memset(&cc_tgt, 0, sizeof(cc_tgt));
memset(&tgtq, 0, sizeof(tgtq));
+ memset(&referral_tgts, 0, sizeof(referral_tgts));
+
tgtptr = NULL;
*tgts = NULL;
+ *out_cred=NULL;
old_use_conf_ktypes = context->use_conf_ktypes;
+ /* Copy client realm to server if no hint. */
+ if (krb5_is_referral_realm(&server->realm)) {
+ /* Use the client realm. */
+#ifdef DEBUG_REFERRALS
+ printf("gc_from_kdc: no server realm supplied, using client realm.\n");
+#endif
+ krb5_free_data_contents(context, &server->realm);
+ if (!( server->realm.data = (char *)malloc(client->realm.length+1)))
+ return ENOMEM;
+ memcpy(server->realm.data, client->realm.data, client->realm.length);
+ server->realm.length = client->realm.length;
+ server->realm.data[server->realm.length] = 0;
+ }
/*
- * Get a TGT for the target realm.
+ * Retreive initial TGT to match the specified server, either for the
+ * local realm in the default (referral) case or for the remote
+ * realm if we're starting someplace non-local.
+ */
+ retval = tgt_mcred(context, client, server, client, &tgtq);
+ if (retval)
+ goto cleanup;
+
+ /* Fast path: Is it in the ccache? */
+ context->use_conf_ktypes = 1;
+ retval = krb5_cc_retrieve_cred(context, ccache, RETR_FLAGS,
+ &tgtq, &cc_tgt);
+ if (!retval) {
+ tgtptr = &cc_tgt;
+ } else if (!HARD_CC_ERR(retval)) {
+#ifdef DEBUG_REFERRALS
+ printf("gc_from_kdc: starting do_traversal to find initial TGT for referral\n");
+#endif
+ retval = do_traversal(context, ccache, client, server,
+ &cc_tgt, &tgtptr, tgts);
+ }
+ if (retval) {
+#ifdef DEBUG_REFERRALS
+ printf("gc_from_kdc: failed to find initial TGT for referral\n");
+#endif
+ goto cleanup;
+ }
+
+#ifdef DEBUG_REFERRALS
+ krb5int_dbgref_dump_principal("gc_from_kdc: server as requested", supplied_server);
+#endif
+
+ /*
+ * Try requesting a service ticket from our local KDC with referrals
+ * turned on. If the first referral succeeds, follow a referral-only
+ * path, otherwise fall back to old-style assumptions.
*/
+ for (referral_count=0;referral_count<KRB5_REFERRAL_MAXHOPS;referral_count++) {
+#ifdef DEBUG_REFERRALS
+#if 0
+ krb5int_dbgref_dump_principal("gc_from_kdc: referral loop: tgt in use", tgtptr->server);
+ krb5int_dbgref_dump_principal("gc_from_kdc: referral loop: request is for", server);
+#endif
+#endif
+ retval = krb5_get_cred_via_tkt(context, tgtptr,
+ KDC_OPT_CANONICALIZE |
+ FLAGS2OPTS(tgtptr->ticket_flags) |
+ kdcopt |
+ (in_cred->second_ticket.length ?
+ KDC_OPT_ENC_TKT_IN_SKEY : 0),
+ tgtptr->addresses, in_cred, out_cred);
+ if (retval) {
+#ifdef DEBUG_REFERRALS
+ printf("gc_from_kdc: referral TGS-REQ request failed: <%s>\n",error_message(retval));
+#endif
+ /* If we haven't gone anywhere yet, fail through to the
+ non-referral case. */
+ if (referral_count==0) {
+#ifdef DEBUG_REFERRALS
+ printf("gc_from_kdc: initial referral failed; punting to fallback.\n");
+#endif
+ break;
+ }
+ /* Otherwise, try the same query without canonicalization
+ set, and fail hard if that doesn't work. */
+#ifdef DEBUG_REFERRALS
+ printf("gc_from_kdc: referral #%d failed; retrying without option.\n",
+ referral_count+1);
+#endif
+ retval = krb5_get_cred_via_tkt(context, tgtptr,
+ FLAGS2OPTS(tgtptr->ticket_flags) |
+ kdcopt |
+ (in_cred->second_ticket.length ?
+ KDC_OPT_ENC_TKT_IN_SKEY : 0),
+ tgtptr->addresses,
+ in_cred, out_cred);
+ /* Whether or not that succeeded, we're done. */
+ goto cleanup;
+ }
+ else {
+ /* Referral request succeeded; let's see what it is. */
+ if (krb5_principal_compare(context, in_cred->server, (*out_cred)->server)) {
+#ifdef DEBUG_REFERRALS
+ printf("gc_from_kdc: request generated ticket for requested server principal\n");
+ krb5int_dbgref_dump_principal("gc_from_kdc final referred reply",in_cred->server);
+#endif
+ goto cleanup;
+ }
+ else {
+#ifdef DEBUG_REFERRALS
+ printf("gc_from_kdc: request generated referral tgt\n");
+ krb5int_dbgref_dump_principal("gc_from_kdc credential received", (*out_cred)->server);
+#endif
+ /* Check for referral routing loop. */
+ for (i=0;i<referral_count;i++) {
+#ifdef DEBUG_REFERRALS
+#if 0
+ krb5int_dbgref_dump_principal("gc_from_kdc: loop compare #1", (*out_cred)->server);
+ krb5int_dbgref_dump_principal("gc_from_kdc: loop compare #2", referral_tgts[i]->server);
+#endif
+#endif
+ if (krb5_principal_compare(context, (*out_cred)->server, referral_tgts[i]->server)) {
+ fprintf(stderr, "krb5_get_cred_from_kdc_opt: referral routing loop afer %d hops\n",i);
+ retval=KRB5_KDC_UNREACH;
+ goto cleanup;
+ }
+ }
+ /* Point current tgt pointer at newly-received TGT. */
+ if (tgtptr == &cc_tgt)
+ krb5_free_cred_contents(context, tgtptr);
+ tgtptr=*out_cred;
+ /* Save pointer to tgt in referral_tgts. */
+ referral_tgts[referral_count]=*out_cred;
+ /* Copy krbtgt realm to server principal. */
+ krb5_free_data_contents(context, &server->realm);
+ if ((retval=krb5int_copy_data_contents(context, &tgtptr->server->data[1], &server->realm)))
+ return retval;
+ /* Future work: rewrite server principal per any supplied padata. */
+ }
+ }
+ }
+
+#ifdef DEBUG_REFERRALS
+ krb5int_dbgref_dump_principal("gc_from_kdc client at fallback", client);
+ krb5int_dbgref_dump_principal("gc_from_kdc server at fallback", server);
+#endif
+
+ /*
+ * At this point referrals have been tried and have failed. Go back
+ * to the server principal as originally issued and try the conventional path.
+ */
+
+ /* Referrals have failed. Look up fallback realm if not originally provided. */
+ if (krb5_is_referral_realm(&supplied_server->realm)) {
+ if (server->length >= 2) {
+ retval=krb5_get_fallback_host_realm(context, &server->data[1],
+ &hrealms);
+ if (retval) goto cleanup;
+#ifdef DEBUG_REFERRALS
+#if 0
+ printf("gc_from_kdc: using fallback realm of %s\n",hrealms[0]);
+#endif
+#endif
+ krb5_free_data_contents(context,&in_cred->server->realm);
+ server->realm.data=hrealms[0];
+ server->realm.length=strlen(hrealms[0]);
+ free(hrealms);
+ }
+ else {
+ /*
+ * Problem case: Realm tagged for referral but apparently not
+ * in a <type>/<host> format that
+ * krb5_get_fallback_host_realm can deal with.
+ */
+#ifdef DEBUG_REFERRALS
+ printf("gc_from_kdc: referral specified but no fallback realm avaiable!\n");
+#endif
+ return KRB5_ERR_HOST_REALM_UNKNOWN;
+ }
+ }
+
+#ifdef DEBUG_REFERRALS
+ krb5int_dbgref_dump_principal("gc_from_kdc server at fallback after fallback rewrite", server);
+#endif
+
+ /*
+ * Get a TGT for the target realm.
+ */
+
+ krb5_free_cred_contents(context, &tgtq);
retval = tgt_mcred(context, client, server, client, &tgtq);
if (retval)
goto cleanup;
/* Fast path: Is it in the ccache? */
+ /* Free tgtptr data if reused from above. */
+ if (tgtptr == &cc_tgt)
+ krb5_free_cred_contents(context, tgtptr);
context->use_conf_ktypes = 1;
retval = krb5_cc_retrieve_cred(context, ccache, RETR_FLAGS,
&tgtq, &cc_tgt);
if (!retval) {
tgtptr = &cc_tgt;
} else if (!HARD_CC_ERR(retval)) {
- /* Not in ccache, so traverse the transit path. */
retval = do_traversal(context, ccache, client, server,
&cc_tgt, &tgtptr, tgts);
}
if (tgtptr == &cc_tgt)
krb5_free_cred_contents(context, tgtptr);
context->use_conf_ktypes = old_use_conf_ktypes;
+ /* Drop the original principal back into in_cred so that it's cached
+ in the expected format. */
+#ifdef DEBUG_REFERRALS
+ krb5int_dbgref_dump_principal("gc_from_kdc: final hacked server principal at cleanup",server);
+#endif
+ krb5_free_principal(context, server);
+ in_cred->server = supplied_server;
+ if (*out_cred && !retval) {
+ /* Success: free server, swap supplied server back in. */
+ krb5_free_principal (context, (*out_cred)->server);
+ (*out_cred)->server= out_supplied_server;
+ }
+ else {
+ /*
+ * Failure: free out_supplied_server. Don't free out_cred here
+ * since it's either null or a referral TGT that we free below,
+ * and we may need it to return.
+ */
+ krb5_free_principal (context, out_supplied_server);
+ }
+#ifdef DEBUG_REFERRALS
+ krb5int_dbgref_dump_principal("gc_from_kdc: final server after reversion",in_cred->server);
+#endif
+ /*
+ * Deal with ccache TGT management: If tgts has been set from
+ * initial non-referral TGT discovery, leave it alone. Otherwise, if
+ * referral_tgts[0] exists return it as the only entry in tgts.
+ * (Further referrals are never cached, only the referral from the
+ * local KDC.) This is part of cleanup because useful received TGTs
+ * should be cached even if the main request resulted in failure.
+ */
+ if (*tgts == NULL) {
+ if (referral_tgts[0]) {
+#if 0
+ /*
+ * This should possibly be a check on the candidate return
+ * credential against the cache, in the circumstance where we
+ * don't want to clutter the cache with near-duplicate
+ * credentials on subsequent iterations. For now, it is
+ * disabled.
+ */
+ subretval=...?;
+ if (subretval) {
+#endif
+ /* Allocate returnable TGT list. */
+ if (!(*tgts=calloc(sizeof (krb5_creds *), 2)))
+ return ENOMEM;
+ subretval=krb5_copy_creds(context, referral_tgts[0], &((*tgts)[0]));
+ if(subretval)
+ return subretval;
+ (*tgts)[1]=NULL;
+#ifdef DEBUG_REFERRALS
+ krb5int_dbgref_dump_principal("gc_from_kdc: returning referral TGT for ccache",(*tgts)[0]->server);
+#endif
+#if 0
+ }
+#endif
+ }
+ }
+
+ /* Free referral TGTs list. */
+ for (i=0;i<KRB5_REFERRAL_MAXHOPS;i++) {
+ if(referral_tgts[i]) {
+ krb5_free_creds(context, referral_tgts[i]);
+ }
+ }
+#ifdef DEBUG_REFERRALS
+ printf("gc_from_kdc finishing with %s\n", retval?error_message(retval):"no error");
+#endif
return retval;
}
goto cleanup;
if ((retval = krb5_copy_principal(context, pkdcrep->enc_part2->server,
- &(*ppcreds)->server)))
+ &(*ppcreds)->server)))
goto cleanup;
if ((retval = krb5_copy_keyblock_contents(context,
krb5_response tgsrep;
krb5_enctype *enctypes = 0;
+#ifdef DEBUG_REFERRALS
+ printf("krb5_get_cred_via_tkt starting; referral flag is %s\n", kdcoptions&KDC_OPT_CANONICALIZE?"on":"off");
+ krb5int_dbgref_dump_principal("krb5_get_cred_via_tkt requested ticket", in_cred->server);
+ krb5int_dbgref_dump_principal("krb5_get_cred_via_tkt TGT in use", tkt->server);
+#endif
+
/* tkt->client must be equal to in_cred->client */
if (!krb5_principal_compare(context, tkt->client, in_cred->client))
return KRB5_PRINC_NOMATCH;
tkt, &tgsrep);
if (enctypes)
free(enctypes);
- if (retval)
+ if (retval) {
+#ifdef DEBUG_REFERRALS
+ printf("krb5_get_cred_via_tkt ending early after send_tgs with: %s\n",
+ error_message(retval));
+#endif
return retval;
+ }
switch (tgsrep.message_type) {
case KRB5_TGS_REP:
else
retval = KRB5KRB_AP_ERR_MSG_TYPE;
- if (retval) /* neither proper reply nor error! */
+ if (retval) /* neither proper reply nor error! */
goto error_4;
retval = (krb5_error_code) err_reply->error + ERROR_TABLE_BASE_krb5;
if (!krb5_principal_compare(context, dec_rep->client, tkt->client))
retval = KRB5_KDCREP_MODIFIED;
- if (!krb5_principal_compare(context, dec_rep->enc_part2->server, in_cred->server))
- retval = KRB5_KDCREP_MODIFIED;
-
- if (!krb5_principal_compare(context, dec_rep->ticket->server, in_cred->server))
- retval = KRB5_KDCREP_MODIFIED;
+ if ((!krb5_principal_compare(context, dec_rep->enc_part2->server, in_cred->server)) ||
+ (!krb5_principal_compare(context, dec_rep->ticket->server, in_cred->server))) {
+ if (krb5_principal_compare(context, dec_rep->ticket->server, dec_rep->enc_part2->server)
+ && (kdcoptions&KDC_OPT_CANONICALIZE) ) {
+ /* in_cred server differs from ticket returned, but ticket
+ returned is consistent and we requested canonicalization. */
+#if 0
+#ifdef DEBUG_REFERRALS
+ printf("gc_via_tkt: in_cred and encoding don't match but referrals requested\n");
+ krb5int_dbgref_dump_principal("gc_via_tkt: in_cred",in_cred->server);
+ krb5int_dbgref_dump_principal("gc_via_tkt: encoded server",dec_rep->enc_part2->server);
+#endif
+#endif
+ }
+ else {
+ /* in_cred server differs from ticket returned, and ticket
+ returned is *not* consistent. */
+ retval = KRB5_KDCREP_MODIFIED;
+ }
+ }
if (dec_rep->enc_part2->nonce != tgsrep.expected_nonce)
retval = KRB5_KDCREP_MODIFIED;
error_4:;
free(tgsrep.response.data);
+#ifdef DEBUG_REFERRALS
+ printf("krb5_get_cred_via_tkt ending; %s\n", retval?error_message(retval):"no error");
+#endif
return retval;
}
* characters in the principal name.
*/
+
#include "k5-int.h"
/*
size = 0;
i++;
} else if (c == REALM_SEP) {
- if (parsed_realm || !*(cp+1))
+ if (parsed_realm)
/*
- * Multiple realm separaters or null
- * realm names are not allowed!
+ * Multiple realm separaters
+ * not allowed; zero-length realms are.
*/
return(KRB5_PARSE_MALFORMED);
parsed_realm = cp+1;
principal->magic = KV5M_PRINCIPAL;
principal->realm.magic = KV5M_DATA;
*nprincipal = principal;
+
krb5_xfree(default_realm);
return(0);
}
-
-
}
return TRUE;
}
+
+krb5_boolean KRB5_CALLCONV krb5_is_referral_realm(krb5_data *r)
+{
+ /*
+ * Check for a match with KRB5_REFERRAL_REALM. Currently this relies
+ * on that string constant being zero-length. (Unlike principal realm
+ * names, KRB5_REFERRAL_REALM is known to be a string.)
+ */
+#ifdef DEBUG_REFERRALS
+#if 0
+ printf("krb5_is_ref_realm: checking <%s> for referralness: %s\n",
+ r->data,(r->length==0)?"true":"false");
+#endif
+#endif
+ assert(strlen(KRB5_REFERRAL_REALM)==0);
+ if (r->length==0)
+ return TRUE;
+ else
+ return FALSE;
+}
* in order to get from the source realm to the destination realm. It
* takes a realm separater character (normally ., but presumably there
* for all those X.500 realms) . There are two modes it runs in: the
- * ANL krb5.confmode and the hierarchy mode. The ANL mode is
+ * ANL krb5.conf mode and the hierarchy mode. The ANL mode is
* fairly obvious. The hierarchy mode looks for common components in
* both the client and server realms. In general, the pointer scp and
* ccp are used to walk through the client and server realms. The
char **cap_nodes;
krb5_error_code cap_code;
#endif
+
+#ifdef DEBUG_REFERRALS
+ printf("krb5_walk_realm_tree starting\n");
+ printf(" client is %s\n",client->data);
+ printf(" server is %s\n",server->data);
+#endif
+
if (!(client->data &&server->data))
return KRB5_NO_TKT_IN_RLM;
#ifdef CONFIGURABLE_AUTHENTICATION_PATH
}
#endif
*tree = rettree;
+
+#ifdef DEBUG_REFERRALS
+ printf("krb5_walk_realm_tree ending; tree (length %d) is:\n",links);
+ for(i=0;i<links+2;i++) {
+ if ((*tree)[i])
+ krb5int_dbgref_dump_principal("krb5_walk_realm_tree tree",(*tree)[i]);
+ else
+ printf("tree element %i null\n");
+ }
+#endif
return 0;
}
+
+#ifdef DEBUG_REFERRALS
+void krb5int_dbgref_dump_principal(char *d, krb5_principal p)
+{
+ int n;
+
+ printf(" **%s: ",d);
+ for (n=0;n<p->length;n++)
+ printf("%s<%.*s>",(n>0)?"/":"",p->data[n].length,p->data[n].data);
+ printf("@<%.*s> (length %d, type %d)\n",p->realm.length,p->realm.data,
+ p->length, p->type);
+}
+#endif
krb5_init_secure_context
krb5_internalize_opaque
krb5_is_permitted_enctype
+krb5_is_referral_realm
krb5_is_thread_safe
krb5_kdc_rep_decrypt_proc
krb5_kt_add_entry
krb5_get_host_realm(krb5_context context, const char *host, char ***realmsp)
{
char **retrealms;
- char *default_realm, *realm, *cp, *temp_realm;
+ char *realm, *cp, *temp_realm;
krb5_error_code retval;
- int l;
char local_host[MAXDNAME+1];
- if (host) {
- /* Filter out numeric addresses if the caller utterly failed to
- convert them to names. */
- /* IPv4 - dotted quads only */
- if (strspn(host, "01234567890.") == strlen(host)) {
- /* All numbers and dots... if it's three dots, it's an
- IP address, and we reject it. But "12345" could be
- a local hostname, couldn't it? We'll just assume
- that a name with three dots is not meant to be an
- all-numeric hostname three all-numeric domains down
- from the current domain. */
- int ndots = 0;
- const char *p;
- for (p = host; *p; p++)
- if (*p == '.')
- ndots++;
- if (ndots == 3)
- return KRB5_ERR_NUMERIC_REALM;
- }
- if (strchr(host, ':'))
- /* IPv6 numeric address form? Bye bye. */
- return KRB5_ERR_NUMERIC_REALM;
-
- /* Should probably error out if strlen(host) > MAXDNAME. */
- strncpy(local_host, host, sizeof(local_host));
- local_host[sizeof(local_host) - 1] = '\0';
- } else {
- retval = krb5int_get_fq_local_hostname (local_host,
- sizeof (local_host));
- if (retval)
- return retval;
- }
+#ifdef DEBUG_REFERRALS
+ printf("get_host_realm(host:%s) called\n",host);
+#endif
- for (cp = local_host; *cp; cp++) {
- if (isupper((unsigned char) (*cp)))
- *cp = tolower((unsigned char) *cp);
- }
- l = strlen(local_host);
- /* strip off trailing dot */
- if (l && local_host[l-1] == '.')
- local_host[l-1] = 0;
+ krb5int_clean_hostname(context, host, local_host, sizeof local_host);
/*
Search for the best match for the host or domain.
*/
cp = local_host;
- realm = default_realm = (char *)NULL;
+#ifdef DEBUG_REFERRALS
+ printf(" local_host: %s\n",local_host);
+#endif
+ realm = (char *)NULL;
temp_realm = 0;
while (cp) {
+#ifdef DEBUG_REFERRALS
+ printf(" trying to look up %s in the domain_realm map\n",cp);
+#endif
retval = profile_get_string(context->profile, "domain_realm", cp,
0, (char *)NULL, &temp_realm);
if (retval)
/* Setup for another test */
if (*cp == '.') {
cp++;
- if (default_realm == (char *)NULL) {
- /* If nothing else works, use the host's domain */
- default_realm = cp;
- }
} else {
cp = strchr(cp, '.');
}
}
+#ifdef DEBUG_REFERRALS
+ printf(" done searching the domain_realm map\n");
+#endif
if (temp_realm) {
+#ifdef DEBUG_REFERRALS
+ printf(" temp_realm is %s\n",temp_realm);
+#endif
realm = malloc(strlen(temp_realm) + 1);
if (!realm) {
profile_release_string(temp_realm);
profile_release_string(temp_realm);
}
-#ifdef KRB5_DNS_LOOKUP
- if (realm == (char *)NULL) {
- int use_dns = _krb5_use_dns_realm(context);
- if ( use_dns ) {
- /*
- * Since this didn't appear in our config file, try looking
- * it up via DNS. Look for a TXT records of the form:
- *
- * _kerberos.<hostname>
- *
- */
- cp = local_host;
- do {
- retval = krb5_try_realm_txt_rr("_kerberos", cp, &realm);
- cp = strchr(cp,'.');
- if (cp)
- cp++;
- } while (retval && cp && cp[0]);
- }
- }
-#endif /* KRB5_DNS_LOOKUP */
if (realm == (char *)NULL) {
- if (default_realm != (char *)NULL) {
- /* We are defaulting to the realm of the host */
- if (!(cp = (char *)malloc(strlen(default_realm)+1)))
- return ENOMEM;
- strcpy(cp, default_realm);
- realm = cp;
-
- /* Assume the realm name is upper case */
- for (cp = realm; *cp; cp++)
- if (islower((unsigned char) (*cp)))
- *cp = toupper((unsigned char) *cp);
- } else {
- /* We are defaulting to the local realm */
- retval = krb5_get_default_realm(context, &realm);
- if (retval) {
- return retval;
- }
- }
+ if (!(cp = (char *)malloc(strlen(KRB5_REFERRAL_REALM)+1)))
+ return ENOMEM;
+ strcpy(cp, KRB5_REFERRAL_REALM);
+ realm = cp;
}
+
if (!(retrealms = (char **)calloc(2, sizeof(*retrealms)))) {
if (realm != (char *)NULL)
free(realm);
abort ();
return -1;
}
+
+
+/*
+ * Ganked from krb5_get_host_realm; handles determining a fallback realm
+ * to try in the case where referrals have failed and it's time to go
+ * look at TXT records or make a DNS-based assumption.
+ */
+
+krb5_error_code KRB5_CALLCONV
+krb5_get_fallback_host_realm(krb5_context context, krb5_data *hdata, char ***realmsp)
+{
+ char **retrealms;
+ char *default_realm, *realm, *cp, *temp_realm;
+ krb5_error_code retval;
+ char local_host[MAXDNAME+1], host[MAXDNAME+1];
+
+ /* Convert what we hope is a hostname to a string. */
+ memcpy(host, hdata->data, hdata->length);
+ host[hdata->length]=0;
+
+#ifdef DEBUG_REFERRALS
+ printf("get_fallback_host_realm(host >%s<) called\n",host);
+#endif
+
+ krb5int_clean_hostname(context, host, local_host, sizeof local_host);
+
+ /* Scan hostname for DNS realm, and save as last-ditch realm
+ assumption. */
+ cp = local_host;
+#ifdef DEBUG_REFERRALS
+ printf(" local_host: %s\n",local_host);
+#endif
+ realm = default_realm = (char *)NULL;
+ temp_realm = 0;
+ while (cp && !default_realm) {
+ if (*cp == '.') {
+ cp++;
+ if (default_realm == (char *)NULL) {
+ /* If nothing else works, use the host's domain */
+ default_realm = cp;
+ }
+ } else {
+ cp = strchr(cp, '.');
+ }
+ }
+#ifdef DEBUG_REFERRALS
+ printf(" done finding DNS-based default realm: >%s<\n",default_realm);
+#endif
+
+#ifdef KRB5_DNS_LOOKUP
+ if (realm == (char *)NULL) {
+ int use_dns = _krb5_use_dns_realm(context);
+ if ( use_dns ) {
+ /*
+ * Since this didn't appear in our config file, try looking
+ * it up via DNS. Look for a TXT records of the form:
+ *
+ * _kerberos.<hostname>
+ *
+ */
+ cp = local_host;
+ do {
+ retval = krb5_try_realm_txt_rr("_kerberos", cp, &realm);
+ cp = strchr(cp,'.');
+ if (cp)
+ cp++;
+ } while (retval && cp && cp[0]);
+ }
+ }
+#endif /* KRB5_DNS_LOOKUP */
+
+
+ if (realm == (char *)NULL) {
+ if (default_realm != (char *)NULL) {
+ /* We are defaulting to the realm of the host */
+ if (!(cp = (char *)malloc(strlen(default_realm)+1)))
+ return ENOMEM;
+ strcpy(cp, default_realm);
+ realm = cp;
+
+ /* Assume the realm name is upper case */
+ for (cp = realm; *cp; cp++)
+ if (islower((int) (*cp)))
+ *cp = toupper((int) *cp);
+ } else {
+ /* We are defaulting to the local realm */
+ retval = krb5_get_default_realm(context, &realm);
+ if (retval) {
+ return retval;
+ }
+ }
+ }
+ if (!(retrealms = (char **)calloc(2, sizeof(*retrealms)))) {
+ if (realm != (char *)NULL)
+ free(realm);
+ return ENOMEM;
+ }
+
+ retrealms[0] = realm;
+ retrealms[1] = 0;
+
+ *realmsp = retrealms;
+ return 0;
+}
+
+/*
+ * Common code for krb5_get_host_realm and krb5_get_fallback_host_realm
+ * to do basic sanity checks on supplied hostname.
+ */
+krb5_error_code KRB5_CALLCONV
+krb5int_clean_hostname(krb5_context context, const char *host, char *local_host, size_t lhsize)
+{
+ char **retrealms;
+ char *realm, *cp, *temp_realm;
+ krb5_error_code retval;
+ int l;
+
+ local_host[0]=0;
+#ifdef DEBUG_REFERRALS
+ printf("krb5int_clean_hostname called: host<%s>, local_host<%s>, size %d\n",host,local_host,lhsize);
+#endif
+ if (host) {
+ /* Filter out numeric addresses if the caller utterly failed to
+ convert them to names. */
+ /* IPv4 - dotted quads only */
+ if (strspn(host, "01234567890.") == strlen(host)) {
+ /* All numbers and dots... if it's three dots, it's an
+ IP address, and we reject it. But "12345" could be
+ a local hostname, couldn't it? We'll just assume
+ that a name with three dots is not meant to be an
+ all-numeric hostname three all-numeric domains down
+ from the current domain. */
+ int ndots = 0;
+ const char *p;
+ for (p = host; *p; p++)
+ if (*p == '.')
+ ndots++;
+ if (ndots == 3)
+ return KRB5_ERR_NUMERIC_REALM;
+ }
+ if (strchr(host, ':'))
+ /* IPv6 numeric address form? Bye bye. */
+ return KRB5_ERR_NUMERIC_REALM;
+
+ /* Should probably error out if strlen(host) > MAXDNAME. */
+ strncpy(local_host, host, lhsize);
+ local_host[lhsize - 1] = '\0';
+ } else {
+ retval = krb5int_get_fq_local_hostname (local_host, lhsize);
+ if (retval)
+ return retval;
+ }
+
+ /* fold to lowercase */
+ for (cp = local_host; *cp; cp++) {
+ if (isupper((unsigned char) (*cp)))
+ *cp = tolower((unsigned char) *cp);
+ }
+ l = strlen(local_host);
+ /* strip off trailing dot */
+ if (l && local_host[l-1] == '.')
+ local_host[l-1] = 0;
+
+#ifdef DEBUG_REFERRALS
+ printf("krb5int_clean_hostname ending: host<%s>, local_host<%s>, size %d\n",host,local_host,lhsize);
+#endif
+ return 0;
+}
register char *cp;
char localname[MAXHOSTNAMELEN];
+ FILE *log;
+
+#ifdef DEBUG_REFERRALS
+ printf("krb5_sname_to_principal(host=%s, sname=%s, type=%d)\n",hostname,sname,type);
+ printf(" name types: 0=unknown, 3=srv_host\n");
+#endif
if ((type == KRB5_NT_UNKNOWN) ||
(type == KRB5_NT_SRV_HST)) {
try_getaddrinfo_again:
err = getaddrinfo(hostname, 0, &hints, &ai);
if (err) {
+#ifdef DEBUG_REFERRALS
+ printf("sname_to_princ: probably punting due to bad hostname of %s\n",hostname);
+#endif
if (hints.ai_family == AF_INET) {
/* Just in case it's an IPv6-only name. */
hints.ai_family = 0;
}
if (!remote_host)
return ENOMEM;
+#ifdef DEBUG_REFERRALS
+ printf("sname_to_princ: hostname <%s> after rdns processing\n",remote_host);
+#endif
if (type == KRB5_NT_SRV_HST)
for (cp = remote_host; *cp; cp++)
free(remote_host);
return retval;
}
+
+#ifdef DEBUG_REFERRALS
+ printf("sname_to_princ: realm <%s> after krb5_get_host_realm\n",hrealms[0]);
+#endif
+
if (!hrealms[0]) {
free(remote_host);
krb5_xfree(hrealms);
krb5_princ_type(context, *ret_princ) = type;
+#ifdef DEBUG_REFERRALS
+ printf("krb5_sname_to_principal returning\n");
+ printf("realm: <%s>, sname: <%s>, remote_host: <%s>\n",
+ realm,sname,remote_host);
+ krb5int_dbgref_dump_principal("krb5_sname_to_principal",*ret_princ);
+#endif
+
free(remote_host);
krb5_free_host_realm(context, hrealms);
krb5_init_keyblock
krb5_init_random_key
krb5_init_secure_context
+ krb5_is_referral_realm
krb5_is_thread_safe
krb5_kt_add_entry
krb5_kt_close