From c8cd2cd7f21ce56f93532a6d5f26239e60657acb Mon Sep 17 00:00:00 2001 From: Tomas Hozza Date: Thu, 25 Jun 2015 14:53:31 +0200 Subject: [PATCH] nsupdate: Don't extract REAML from ticket, but leave it up to GSSAPI The current implementation of nsupdate does not work correctly with GSSAPI in cross realm trust scenarios. The realm is currently extracted from local kerberos ticket instead of letting GSSAPI to figure out the realm based on the remote nameserver hostname. RFC 4752 section 3.1 states that the client should use GSS_C_NT_HOSTBASED_SERVICE when calling gss_import_name(). nsupdate now leaves the realm detection up to GSSAPI, if the realm is not specified explicitly using the 'realm' option. If the option is used, the old behavior is preserved. Signed-off-by: Tomas Hozza --- bin/nsupdate/nsupdate.1 | 3 +- bin/nsupdate/nsupdate.c | 72 ++++++++----------------------------------- bin/nsupdate/nsupdate.docbook | 2 +- bin/nsupdate/nsupdate.html | 2 +- bin/tests/dst/gsstest.c | 4 +-- lib/dns/gssapictx.c | 16 +++++++--- lib/dns/include/dns/tkey.h | 24 +++++++++------ lib/dns/include/dst/gssapi.h | 8 +++-- lib/dns/tkey.c | 28 +++++++++-------- 9 files changed, 65 insertions(+), 94 deletions(-) diff --git a/bin/nsupdate/nsupdate.1 b/bin/nsupdate/nsupdate.1 index 1e2dcaf..c847fb8 100644 --- a/bin/nsupdate/nsupdate.1 +++ b/bin/nsupdate/nsupdate.1 @@ -259,8 +259,7 @@ on the commandline. .RS 4 When using GSS\-TSIG use \fIrealm_name\fR -rather than the default realm in -\fIkrb5.conf\fR. If no realm is specified the saved realm is cleared. +rather than leaving the realm detection up to GSSAPI. If no realm is specified the saved realm is cleared. .RE .PP \fB[prereq]\fR\fB nxdomain\fR {domain\-name} diff --git a/bin/nsupdate/nsupdate.c b/bin/nsupdate/nsupdate.c index b901e03..644e3d9 100644 --- a/bin/nsupdate/nsupdate.c +++ b/bin/nsupdate/nsupdate.c @@ -2489,57 +2489,6 @@ sendrequest(isc_sockaddr_t *srcaddr, isc_sockaddr_t *destaddr, #ifdef GSSAPI -/* - * Get the realm from the users kerberos ticket if possible - */ -static void -get_ticket_realm(isc_mem_t *mctx) -{ - krb5_context ctx; - krb5_error_code rc; - krb5_ccache ccache; - krb5_principal princ; - char *name, *ticket_realm; - - rc = krb5_init_context(&ctx); - if (rc != 0) - return; - - rc = krb5_cc_default(ctx, &ccache); - if (rc != 0) { - krb5_free_context(ctx); - return; - } - - rc = krb5_cc_get_principal(ctx, ccache, &princ); - if (rc != 0) { - krb5_cc_close(ctx, ccache); - krb5_free_context(ctx); - return; - } - - rc = krb5_unparse_name(ctx, princ, &name); - if (rc != 0) { - krb5_free_principal(ctx, princ); - krb5_cc_close(ctx, ccache); - krb5_free_context(ctx); - return; - } - - ticket_realm = strrchr(name, '@'); - if (ticket_realm != NULL) { - realm = isc_mem_strdup(mctx, ticket_realm); - } - - free(name); - krb5_free_principal(ctx, princ); - krb5_cc_close(ctx, ccache); - krb5_free_context(ctx); - if (realm != NULL && debugging) - fprintf(stderr, "Found realm from ticket: %s\n", realm+1); -} - - static void start_gssrequest(dns_name_t *master) { gss_ctx_id_t context; @@ -2580,11 +2529,15 @@ start_gssrequest(dns_name_t *master) { dns_fixedname_init(&fname); servname = dns_fixedname_name(&fname); - if (realm == NULL) - get_ticket_realm(mctx); - - result = isc_string_printf(servicename, sizeof(servicename), - "DNS/%s%s", namestr, realm ? realm : ""); + if (realm != NULL) { + /* Use explicit REALM passed as argument */ + result = isc_string_printf(servicename, sizeof(servicename), + "DNS/%s%s", namestr, realm); + } else { + /* Use service@host as advised in RFC4752 section 3.1 */ + result = isc_string_printf(servicename, sizeof(servicename), + "DNS@%s", namestr); + } if (result != ISC_R_SUCCESS) fatal("isc_string_printf(servicename) failed: %s", isc_result_totext(result)); @@ -2623,9 +2576,9 @@ start_gssrequest(dns_name_t *master) { /* Build first request. */ context = GSS_C_NO_CONTEXT; - result = dns_tkey_buildgssquery(rmsg, keyname, servname, NULL, 0, - &context, use_win2k_gsstsig, - mctx, &err_message); + result = dns_tkey_buildgssquery(rmsg, keyname, servname, + realm != NULL ? ISC_TRUE : ISC_FALSE, NULL, 0, + &context, use_win2k_gsstsig, mctx, &err_message); if (result == ISC_R_FAILURE) fatal("tkey query failed: %s", err_message != NULL ? err_message : "unknown error"); @@ -2765,6 +2718,7 @@ recvgss(isc_task_t *task, isc_event_t *event) { tsigkey = NULL; result = dns_tkey_gssnegotiate(tsigquery, rcvmsg, servname, + realm != NULL ? ISC_TRUE : ISC_FALSE, &context, &tsigkey, gssring, use_win2k_gsstsig, &err_message); diff --git a/bin/nsupdate/nsupdate.docbook b/bin/nsupdate/nsupdate.docbook index c54211c..bbcc681 100644 --- a/bin/nsupdate/nsupdate.docbook +++ b/bin/nsupdate/nsupdate.docbook @@ -418,7 +418,7 @@ When using GSS-TSIG use realm_name rather - than the default realm in krb5.conf. If no + than leaving the realm detection up to GSSAPI. If no realm is specified the saved realm is cleared. diff --git a/bin/nsupdate/nsupdate.html b/bin/nsupdate/nsupdate.html index 276d4af..9c0eba0 100644 --- a/bin/nsupdate/nsupdate.html +++ b/bin/nsupdate/nsupdate.html @@ -327,7 +327,7 @@

When using GSS-TSIG use realm_name rather - than the default realm in krb5.conf. If no + than leaving the realm detection up to GSSAPI. If no realm is specified the saved realm is cleared.

diff --git a/bin/tests/dst/gsstest.c b/bin/tests/dst/gsstest.c index c1296f7..7c85d0b 100755 --- a/bin/tests/dst/gsstest.c +++ b/bin/tests/dst/gsstest.c @@ -309,7 +309,7 @@ initctx2(isc_task_t *task, isc_event_t *event) { printf("Received token from server, calling gss_init_sec_context()\n"); isc_buffer_init(&outtoken, array, DNS_NAME_MAXTEXT + 1); result = dns_tkey_processgssresponse(query, response, - dns_fixedname_name(&gssname), + dns_fixedname_name(&gssname), ISC_FALSE, &gssctx, &outtoken, &tsigkey, ring, NULL); gssctx = *gssctxp; @@ -396,7 +396,7 @@ initctx1(isc_task_t *task, isc_event_t *event) { printf("Calling gss_init_sec_context()\n"); gssctx = GSS_C_NO_CONTEXT; result = dns_tkey_buildgssquery(query, dns_fixedname_name(&servername), - dns_fixedname_name(&gssname), + dns_fixedname_name(&gssname), ISC_FALSE, NULL, 36000, &gssctx, ISC_TRUE, mctx, NULL); CHECK("dns_tkey_buildgssquery", result); diff --git a/lib/dns/gssapictx.c b/lib/dns/gssapictx.c index aeaeb85..21222e0 100644 --- a/lib/dns/gssapictx.c +++ b/lib/dns/gssapictx.c @@ -558,14 +558,15 @@ gss_err_message(isc_mem_t *mctx, isc_uint32_t major, isc_uint32_t minor, #endif isc_result_t -dst_gssapi_initctx(dns_name_t *name, isc_buffer_t *intoken, - isc_buffer_t *outtoken, gss_ctx_id_t *gssctx, - isc_mem_t *mctx, char **err_message) +dst_gssapi_initctx(dns_name_t *name, isc_boolean_t explicit_realm, + isc_buffer_t *intoken, isc_buffer_t *outtoken, + gss_ctx_id_t *gssctx, isc_mem_t *mctx, char **err_message) { #ifdef GSSAPI isc_region_t r; isc_buffer_t namebuf; gss_name_t gname; + gss_OID gname_type; OM_uint32 gret, minor, ret_flags, flags; gss_buffer_desc gintoken, *gintokenp, gouttoken = GSS_C_EMPTY_BUFFER; isc_result_t result; @@ -580,7 +581,13 @@ dst_gssapi_initctx(dns_name_t *name, isc_buffer_t *intoken, name_to_gbuffer(name, &namebuf, &gnamebuf); /* Get the name as a GSS name */ - gret = gss_import_name(&minor, &gnamebuf, GSS_C_NO_OID, &gname); + if (explicit_realm == ISC_TRUE) { + gname_type = GSS_C_NO_OID; + } else { + gname_type = GSS_C_NT_HOSTBASED_SERVICE; + } + + gret = gss_import_name(&minor, &gnamebuf, gname_type, &gname); if (gret != GSS_S_COMPLETE) { gss_err_message(mctx, gret, minor, err_message); result = ISC_R_FAILURE; @@ -642,6 +649,7 @@ dst_gssapi_initctx(dns_name_t *name, isc_buffer_t *intoken, return (result); #else UNUSED(name); + UNUSED(explicit_realm); UNUSED(intoken); UNUSED(outtoken); UNUSED(gssctx); diff --git a/lib/dns/include/dns/tkey.h b/lib/dns/include/dns/tkey.h index 0dcec1e..a0e6c2a 100644 --- a/lib/dns/include/dns/tkey.h +++ b/lib/dns/include/dns/tkey.h @@ -123,9 +123,9 @@ dns_tkey_builddhquery(dns_message_t *msg, dst_key_t *key, dns_name_t *name, isc_result_t dns_tkey_buildgssquery(dns_message_t *msg, dns_name_t *name, dns_name_t *gname, - isc_buffer_t *intoken, isc_uint32_t lifetime, - gss_ctx_id_t *context, isc_boolean_t win2k, - isc_mem_t *mctx, char **err_message); + isc_boolean_t explicit_realm, isc_buffer_t *intoken, + isc_uint32_t lifetime, gss_ctx_id_t *context, + isc_boolean_t win2k, isc_mem_t *mctx, char **err_message); /*%< * Builds a query containing a TKEY that will generate a GSSAPI context. * The key is requested to have the specified lifetime (in seconds). @@ -134,6 +134,8 @@ dns_tkey_buildgssquery(dns_message_t *msg, dns_name_t *name, dns_name_t *gname, *\li 'msg' is a valid message *\li 'name' is a valid name *\li 'gname' is a valid name + *\li 'explicit_realm' ISC_TRUE if an explicit realm is used, + * ISC_FALSE if the realm detection is left up to GSSAPI. *\li 'context' is a pointer to a valid gss_ctx_id_t * (which may have the value GSS_C_NO_CONTEXT) *\li 'win2k' when true says to turn on some hacks to work @@ -188,9 +190,10 @@ dns_tkey_processdhresponse(dns_message_t *qmsg, dns_message_t *rmsg, isc_result_t dns_tkey_processgssresponse(dns_message_t *qmsg, dns_message_t *rmsg, - dns_name_t *gname, gss_ctx_id_t *context, - isc_buffer_t *outtoken, dns_tsigkey_t **outkey, - dns_tsig_keyring_t *ring, char **err_message); + dns_name_t *gname, isc_boolean_t explicit_realm, + gss_ctx_id_t *context, isc_buffer_t *outtoken, + dns_tsigkey_t **outkey, dns_tsig_keyring_t *ring, + char **err_message); /*%< * XXX */ @@ -216,9 +219,10 @@ dns_tkey_processdeleteresponse(dns_message_t *qmsg, dns_message_t *rmsg, isc_result_t dns_tkey_gssnegotiate(dns_message_t *qmsg, dns_message_t *rmsg, - dns_name_t *server, gss_ctx_id_t *context, - dns_tsigkey_t **outkey, dns_tsig_keyring_t *ring, - isc_boolean_t win2k, char **err_message); + dns_name_t *server, isc_boolean_t explicit_realm, + gss_ctx_id_t *context, dns_tsigkey_t **outkey, + dns_tsig_keyring_t *ring, isc_boolean_t win2k, + char **err_message); /* * Client side negotiation of GSS-TSIG. Process the response @@ -231,6 +235,8 @@ dns_tkey_gssnegotiate(dns_message_t *qmsg, dns_message_t *rmsg, * it will be filled with the new message to send * 'rmsg' is a valid message, the incoming TKEY message * 'server' is the server name + * 'explicit_realm' ISC_TRUE if an explicit realm is used, + * ISC_FALSE if the realm detection is left up to GSSAPI. * 'context' is the input context handle * 'outkey' receives the established key, if non-NULL; * if non-NULL must point to NULL diff --git a/lib/dns/include/dst/gssapi.h b/lib/dns/include/dst/gssapi.h index 1e81a55..d093fa3 100644 --- a/lib/dns/include/dst/gssapi.h +++ b/lib/dns/include/dst/gssapi.h @@ -93,15 +93,17 @@ dst_gssapi_releasecred(gss_cred_id_t *cred); */ isc_result_t -dst_gssapi_initctx(dns_name_t *name, isc_buffer_t *intoken, - isc_buffer_t *outtoken, gss_ctx_id_t *gssctx, - isc_mem_t *mctx, char **err_message); +dst_gssapi_initctx(dns_name_t *name, isc_boolean_t explicit_realm, + isc_buffer_t *intoken, isc_buffer_t *outtoken, + gss_ctx_id_t *gssctx, isc_mem_t *mctx, char **err_message); /* * Initiates a GSS context. * * Requires: * 'name' is a valid name, preferably one known by the GSS * provider + * 'explicit_realm' True if the REALM is explicitly included in the 'name', + * otherwise leave the REALM detection up to GSSAPI * 'intoken' is a token received from the acceptor, or NULL if * there isn't one * 'outtoken' is a buffer to receive the token generated by diff --git a/lib/dns/tkey.c b/lib/dns/tkey.c index 20c98e5..3463d3a 100644 --- a/lib/dns/tkey.c +++ b/lib/dns/tkey.c @@ -1016,9 +1016,9 @@ dns_tkey_builddhquery(dns_message_t *msg, dst_key_t *key, dns_name_t *name, isc_result_t dns_tkey_buildgssquery(dns_message_t *msg, dns_name_t *name, dns_name_t *gname, - isc_buffer_t *intoken, isc_uint32_t lifetime, - gss_ctx_id_t *context, isc_boolean_t win2k, - isc_mem_t *mctx, char **err_message) + isc_boolean_t explicit_realm, isc_buffer_t *intoken, + isc_uint32_t lifetime, gss_ctx_id_t *context, + isc_boolean_t win2k, isc_mem_t *mctx, char **err_message) { dns_rdata_tkey_t tkey; isc_result_t result; @@ -1035,7 +1035,7 @@ dns_tkey_buildgssquery(dns_message_t *msg, dns_name_t *name, dns_name_t *gname, REQUIRE(mctx != NULL); isc_buffer_init(&token, array, sizeof(array)); - result = dst_gssapi_initctx(gname, NULL, &token, context, + result = dst_gssapi_initctx(gname, explicit_realm, NULL, &token, context, mctx, err_message); if (result != DNS_R_CONTINUE && result != ISC_R_SUCCESS) return (result); @@ -1251,9 +1251,10 @@ dns_tkey_processdhresponse(dns_message_t *qmsg, dns_message_t *rmsg, isc_result_t dns_tkey_processgssresponse(dns_message_t *qmsg, dns_message_t *rmsg, - dns_name_t *gname, gss_ctx_id_t *context, - isc_buffer_t *outtoken, dns_tsigkey_t **outkey, - dns_tsig_keyring_t *ring, char **err_message) + dns_name_t *gname, isc_boolean_t explicit_realm, + gss_ctx_id_t *context, isc_buffer_t *outtoken, + dns_tsigkey_t **outkey, dns_tsig_keyring_t *ring, + char **err_message) { dns_rdata_t rtkeyrdata = DNS_RDATA_INIT, qtkeyrdata = DNS_RDATA_INIT; dns_name_t *tkeyname; @@ -1304,7 +1305,7 @@ dns_tkey_processgssresponse(dns_message_t *qmsg, dns_message_t *rmsg, isc_buffer_init(outtoken, array, sizeof(array)); isc_buffer_init(&intoken, rtkey.key, rtkey.keylen); - RETERR(dst_gssapi_initctx(gname, &intoken, outtoken, context, + RETERR(dst_gssapi_initctx(gname, explicit_realm, &intoken, outtoken, context, ring->mctx, err_message)); RETERR(dst_key_fromgssapi(dns_rootname, *context, rmsg->mctx, @@ -1384,9 +1385,10 @@ dns_tkey_processdeleteresponse(dns_message_t *qmsg, dns_message_t *rmsg, isc_result_t dns_tkey_gssnegotiate(dns_message_t *qmsg, dns_message_t *rmsg, - dns_name_t *server, gss_ctx_id_t *context, - dns_tsigkey_t **outkey, dns_tsig_keyring_t *ring, - isc_boolean_t win2k, char **err_message) + dns_name_t *server, isc_boolean_t explicit_realm, + gss_ctx_id_t *context, dns_tsigkey_t **outkey, + dns_tsig_keyring_t *ring, isc_boolean_t win2k, + char **err_message) { dns_rdata_t rtkeyrdata = DNS_RDATA_INIT, qtkeyrdata = DNS_RDATA_INIT; dns_name_t *tkeyname; @@ -1430,8 +1432,8 @@ dns_tkey_gssnegotiate(dns_message_t *qmsg, dns_message_t *rmsg, isc_buffer_init(&intoken, rtkey.key, rtkey.keylen); isc_buffer_init(&outtoken, array, sizeof(array)); - result = dst_gssapi_initctx(server, &intoken, &outtoken, context, - ring->mctx, err_message); + result = dst_gssapi_initctx(server, explicit_realm, &intoken, &outtoken, + context, ring->mctx, err_message); if (result != DNS_R_CONTINUE && result != ISC_R_SUCCESS) return (result); -- 2.4.3