/*% * Verify. */ static isc_result_t gssapi_verify(dst_context_t *dctx, const isc_region_t *sig) { dst_gssapi_signverifyctx_t *ctx = dctx->ctxdata.gssctx; isc_region_t message, r; gss_buffer_desc gmessage, gsig; OM_uint32 minor, gret; gss_ctx_id_t gssctx = dctx->key->keydata.gssctx; unsigned char *buf; char err[1024]; /* * Convert the data we wish to sign into a structure gssapi can * understand. */ isc_buffer_usedregion(ctx->buffer, &message); REGION_TO_GBUFFER(message, gmessage); /* * XXXMLG * It seem that gss_verify_mic() modifies the signature buffer, * at least on Heimdal's implementation. Copy it here to an allocated * buffer. */ buf = isc_mem_allocate(dst__memory_pool, sig->length); if (buf == NULL) return (ISC_R_FAILURE); memmove(buf, sig->base, sig->length); r.base = buf; r.length = sig->length; REGION_TO_GBUFFER(r, gsig); /* * Verify the data. */ gret = gss_verify_mic(&minor, gssctx, &gmessage, &gsig, NULL); isc_mem_free(dst__memory_pool, buf); /* * Convert return codes into something useful to us. */ if (gret != GSS_S_COMPLETE) { gss_log(3, "GSS verify error: %s", gss_error_tostring(gret, minor, err, sizeof(err))); if (gret == GSS_S_DEFECTIVE_TOKEN || gret == GSS_S_BAD_SIG || gret == GSS_S_DUPLICATE_TOKEN || gret == GSS_S_OLD_TOKEN || gret == GSS_S_UNSEQ_TOKEN || gret == GSS_S_GAP_TOKEN || gret == GSS_S_CONTEXT_EXPIRED || gret == GSS_S_NO_CONTEXT || gret == GSS_S_FAILURE) return(DST_R_VERIFYFAILURE); else return (ISC_R_FAILURE); } return (ISC_R_SUCCESS); }
static isc_result_t gssapi_sign(dst_context_t *dctx, isc_buffer_t *sig) { gssapi_ctx_t *ctx = dctx->opaque; isc_region_t message; gss_buffer_desc gmessage, gsig; OM_uint32 minor, gret; isc_buffer_usedregion(ctx->buffer, &message); REGION_TO_GBUFFER(message, gmessage); gret = gss_get_mic(&minor, ctx->context_id, GSS_C_QOP_DEFAULT, &gmessage, &gsig); if (gret != 0) return (ISC_R_FAILURE); if (gsig.length > isc_buffer_availablelength(sig)) { gss_release_buffer(&minor, &gsig); return (ISC_R_NOSPACE); } isc_buffer_putmem(sig, gsig.value, gsig.length); gss_release_buffer(&minor, &gsig); return (ISC_R_SUCCESS); }
static inline void name_to_gbuffer(dns_name_t *name, isc_buffer_t *buffer, gss_buffer_desc *gbuffer) { dns_name_t tname, *namep; isc_region_t r; isc_result_t result; if (!dns_name_isabsolute(name)) namep = name; else { unsigned int labels; dns_name_init(&tname, NULL); labels = dns_name_countlabels(name); dns_name_getlabelsequence(name, 0, labels - 1, &tname); namep = &tname; } result = dns_name_toprincipal(namep, buffer); RUNTIME_CHECK(result == ISC_R_SUCCESS); isc_buffer_putuint8(buffer, 0); isc_buffer_usedregion(buffer, &r); REGION_TO_GBUFFER(r, *gbuffer); }
static isc_result_t gssapi_verify(dst_context_t *dctx, const isc_region_t *sig) { gssapi_ctx_t *ctx = dctx->opaque; isc_region_t message; gss_buffer_desc gmessage, gsig; OM_uint32 minor, gret; isc_buffer_usedregion(ctx->buffer, &message); REGION_TO_GBUFFER(message, gmessage); REGION_TO_GBUFFER(*sig, gsig); gret = gss_verify_mic(&minor, ctx->context_id, &gmessage, &gsig, NULL); if (gret != 0) return (ISC_R_FAILURE); return (ISC_R_SUCCESS); }
/*% * Sign. */ static isc_result_t gssapi_sign(dst_context_t *dctx, isc_buffer_t *sig) { dst_gssapi_signverifyctx_t *ctx = dctx->ctxdata.gssctx; isc_region_t message; gss_buffer_desc gmessage, gsig; OM_uint32 minor, gret; gss_ctx_id_t gssctx = dctx->key->keydata.gssctx; char buf[1024]; /* * Convert the data we wish to sign into a structure gssapi can * understand. */ isc_buffer_usedregion(ctx->buffer, &message); REGION_TO_GBUFFER(message, gmessage); /* * Generate the signature. */ gret = gss_get_mic(&minor, gssctx, GSS_C_QOP_DEFAULT, &gmessage, &gsig); /* * If it did not complete, we log the result and return a generic * failure code. */ if (gret != GSS_S_COMPLETE) { gss_log(3, "GSS sign error: %s", gss_error_tostring(gret, minor, buf, sizeof(buf))); return (ISC_R_FAILURE); } /* * If it will not fit in our allocated buffer, return that we need * more space. */ if (gsig.length > isc_buffer_availablelength(sig)) { gss_release_buffer(&minor, &gsig); return (ISC_R_NOSPACE); } /* * Copy the output into our buffer space, and release the gssapi * allocated space. */ isc_buffer_putmem(sig, gsig.value, (unsigned int)gsig.length); if (gsig.length != 0U) gss_release_buffer(&minor, &gsig); return (ISC_R_SUCCESS); }
static isc_result_t gssapi_restore(dst_key_t *key, const char *keystr) { OM_uint32 major, minor; unsigned int len; isc_buffer_t *b = NULL; isc_region_t r; gss_buffer_desc gssbuffer; isc_result_t result; len = strlen(keystr); if ((len % 4) != 0U) return (ISC_R_BADBASE64); len = (len / 4) * 3; result = isc_buffer_allocate(key->mctx, &b, len); if (result != ISC_R_SUCCESS) return (result); result = isc_base64_decodestring(keystr, b); if (result != ISC_R_SUCCESS) { isc_buffer_free(&b); return (result); } isc_buffer_remainingregion(b, &r); REGION_TO_GBUFFER(r, gssbuffer); major = gss_import_sec_context(&minor, &gssbuffer, &key->keydata.gssctx); if (major != GSS_S_COMPLETE) { isc_buffer_free(&b); return (ISC_R_FAILURE); } isc_buffer_free(&b); return (ISC_R_SUCCESS); }
isc_result_t dst_gssapi_acceptctx(gss_cred_id_t cred, const char *gssapi_keytab, isc_region_t *intoken, isc_buffer_t **outtoken, gss_ctx_id_t *ctxout, dns_name_t *principal, isc_mem_t *mctx) { #ifdef GSSAPI isc_region_t r; isc_buffer_t namebuf; gss_buffer_desc gnamebuf = GSS_C_EMPTY_BUFFER, gintoken, gouttoken = GSS_C_EMPTY_BUFFER; OM_uint32 gret, minor; gss_ctx_id_t context = GSS_C_NO_CONTEXT; gss_name_t gname = NULL; isc_result_t result; char buf[1024]; REQUIRE(outtoken != NULL && *outtoken == NULL); log_cred(cred); REGION_TO_GBUFFER(*intoken, gintoken); if (*ctxout == NULL) context = GSS_C_NO_CONTEXT; else context = *ctxout; if (gssapi_keytab != NULL) { #ifdef ISC_PLATFORM_GSSAPI_KRB5_HEADER gret = gsskrb5_register_acceptor_identity(gssapi_keytab); if (gret != GSS_S_COMPLETE) { gss_log(3, "failed " "gsskrb5_register_acceptor_identity(%s): %s", gssapi_keytab, gss_error_tostring(gret, minor, buf, sizeof(buf))); return (DNS_R_INVALIDTKEY); } #else /* * Minimize memory leakage by only setting KRB5_KTNAME * if it needs to change. */ const char *old = getenv("KRB5_KTNAME"); if (old == NULL || strcmp(old, gssapi_keytab) != 0) { char *kt = malloc(strlen(gssapi_keytab) + 13); if (kt == NULL) return (ISC_R_NOMEMORY); sprintf(kt, "KRB5_KTNAME=%s", gssapi_keytab); if (putenv(kt) != 0) return (ISC_R_NOMEMORY); } #endif } gret = gss_accept_sec_context(&minor, &context, cred, &gintoken, GSS_C_NO_CHANNEL_BINDINGS, &gname, NULL, &gouttoken, NULL, NULL, NULL); result = ISC_R_FAILURE; switch (gret) { case GSS_S_COMPLETE: result = ISC_R_SUCCESS; break; case GSS_S_CONTINUE_NEEDED: result = DNS_R_CONTINUE; break; case GSS_S_DEFECTIVE_TOKEN: case GSS_S_DEFECTIVE_CREDENTIAL: case GSS_S_BAD_SIG: case GSS_S_DUPLICATE_TOKEN: case GSS_S_OLD_TOKEN: case GSS_S_NO_CRED: case GSS_S_CREDENTIALS_EXPIRED: case GSS_S_BAD_BINDINGS: case GSS_S_NO_CONTEXT: case GSS_S_BAD_MECH: case GSS_S_FAILURE: result = DNS_R_INVALIDTKEY; /* fall through */ default: gss_log(3, "failed gss_accept_sec_context: %s", gss_error_tostring(gret, minor, buf, sizeof(buf))); return (result); } if (gouttoken.length > 0) { RETERR(isc_buffer_allocate(mctx, outtoken, gouttoken.length)); GBUFFER_TO_REGION(gouttoken, r); RETERR(isc_buffer_copyregion(*outtoken, &r)); (void)gss_release_buffer(&minor, &gouttoken); } if (gret == GSS_S_COMPLETE) { gret = gss_display_name(&minor, gname, &gnamebuf, NULL); if (gret != GSS_S_COMPLETE) { gss_log(3, "failed gss_display_name: %s", gss_error_tostring(gret, minor, buf, sizeof(buf))); RETERR(ISC_R_FAILURE); } /* * Compensate for a bug in Solaris8's implementation * of gss_display_name(). Should be harmless in any * case, since principal names really should not * contain null characters. */ if (gnamebuf.length > 0 && ((char *)gnamebuf.value)[gnamebuf.length - 1] == '\0') gnamebuf.length--; gss_log(3, "gss-api source name (accept) is %.*s", (int)gnamebuf.length, (char *)gnamebuf.value); GBUFFER_TO_REGION(gnamebuf, r); isc_buffer_init(&namebuf, r.base, r.length); isc_buffer_add(&namebuf, r.length); RETERR(dns_name_fromtext(principal, &namebuf, dns_rootname, 0, NULL)); if (gnamebuf.length != 0) { gret = gss_release_buffer(&minor, &gnamebuf); if (gret != GSS_S_COMPLETE) gss_log(3, "failed gss_release_buffer: %s", gss_error_tostring(gret, minor, buf, sizeof(buf))); } } *ctxout = context; out: if (gname != NULL) { gret = gss_release_name(&minor, &gname); if (gret != GSS_S_COMPLETE) gss_log(3, "failed gss_release_name: %s", gss_error_tostring(gret, minor, buf, sizeof(buf))); } return (result); #else UNUSED(cred); UNUSED(gssapi_keytab); UNUSED(intoken); UNUSED(outtoken); UNUSED(ctxout); UNUSED(principal); UNUSED(mctx); return (ISC_R_NOTIMPLEMENTED); #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) { #ifdef GSSAPI isc_region_t r; isc_buffer_t namebuf; gss_name_t gname; OM_uint32 gret, minor, ret_flags, flags; gss_buffer_desc gintoken, *gintokenp, gouttoken = GSS_C_EMPTY_BUFFER; isc_result_t result; gss_buffer_desc gnamebuf; unsigned char array[DNS_NAME_MAXTEXT + 1]; /* Client must pass us a valid gss_ctx_id_t here */ REQUIRE(gssctx != NULL); REQUIRE(mctx != NULL); isc_buffer_init(&namebuf, array, sizeof(array)); 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 (gret != GSS_S_COMPLETE) { gss_err_message(mctx, gret, minor, err_message); result = ISC_R_FAILURE; goto out; } if (intoken != NULL) { /* Don't call gss_release_buffer for gintoken! */ REGION_TO_GBUFFER(*intoken, gintoken); gintokenp = &gintoken; } else { gintokenp = NULL; } /* * Note that we don't set GSS_C_SEQUENCE_FLAG as Windows DNS * servers don't like it. */ flags = GSS_C_REPLAY_FLAG | GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG; gret = gss_init_sec_context(&minor, GSS_C_NO_CREDENTIAL, gssctx, gname, GSS_SPNEGO_MECHANISM, flags, 0, NULL, gintokenp, NULL, &gouttoken, &ret_flags, NULL); if (gret != GSS_S_COMPLETE && gret != GSS_S_CONTINUE_NEEDED) { gss_err_message(mctx, gret, minor, err_message); gss_log(3, "Failure initiating security context: %s", *err_message); result = ISC_R_FAILURE; goto out; } /* * XXXSRA Not handled yet: RFC 3645 3.1.1: check ret_flags * MUTUAL and INTEG flags, fail if either not set. */ /* * RFC 2744 states the a valid output token has a non-zero length. */ if (gouttoken.length != 0) { GBUFFER_TO_REGION(gouttoken, r); RETERR(isc_buffer_copyregion(outtoken, &r)); (void)gss_release_buffer(&minor, &gouttoken); } (void)gss_release_name(&minor, &gname); if (gret == GSS_S_COMPLETE) result = ISC_R_SUCCESS; else result = DNS_R_CONTINUE; out: return (result); #else UNUSED(name); UNUSED(intoken); UNUSED(outtoken); UNUSED(gssctx); UNUSED(mctx); UNUSED(err_message); return (ISC_R_NOTIMPLEMENTED); #endif }