OM_uint32 _gss_spnego_verify_mechtypes_mic(OM_uint32 *minor_status, gssspnego_ctx ctx, heim_octet_string *mic) { gss_buffer_desc mic_buf; OM_uint32 major_status; if (ctx->flags.verified_mic) { /* This doesn't make sense, we've already verified it? */ *minor_status = 0; return GSS_S_DUPLICATE_TOKEN; } mic_buf.length = mic->length; mic_buf.value = mic->data; major_status = gss_verify_mic(minor_status, ctx->negotiated_ctx_id, &ctx->NegTokenInit_mech_types, &mic_buf, NULL); if (major_status == GSS_S_UNAVAILABLE) { _gss_mg_log(10, "mech doesn't support MIC, allowing anyway"); } else if (major_status) { return gss_mg_set_error_string(GSS_SPNEGO_MECHANISM, GSS_S_DEFECTIVE_TOKEN, *minor_status, "SPNEGO peer sent invalid mechListMIC"); } ctx->flags.verified_mic = 1; *minor_status = 0; return GSS_S_COMPLETE; }
OM_uint32 GSSAPI_CALLCONV _gss_spnego_verify_mic (OM_uint32 * minor_status, gss_const_ctx_id_t context_handle, const gss_buffer_t message_buffer, const gss_buffer_t token_buffer, gss_qop_t * qop_state ) { gssspnego_ctx ctx; *minor_status = 0; if (context_handle == GSS_C_NO_CONTEXT) { return GSS_S_NO_CONTEXT; } ctx = (gssspnego_ctx)context_handle; if (ctx->negotiated_ctx_id == GSS_C_NO_CONTEXT) { return GSS_S_NO_CONTEXT; } return gss_verify_mic(minor_status, ctx->negotiated_ctx_id, message_buffer, token_buffer, qop_state); }
/*% * 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); }
/* Privileged */ OM_uint32 ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) { ctx->major = gss_verify_mic(&ctx->minor, ctx->context, gssbuf, gssmic, NULL); return (ctx->major); }
static bool_t svcauth_gss_validate(struct svc_req *rqst, struct svc_rpc_gss_data *gd, struct rpc_msg *msg) { struct opaque_auth *oa; gss_buffer_desc rpcbuf, checksum; OM_uint32 maj_stat, min_stat, qop_state; u_char rpchdr[128]; int32_t *buf; log_debug("in svcauth_gss_validate()"); memset(rpchdr, 0, sizeof(rpchdr)); /* XXX - Reconstruct RPC header for signing (from xdr_callmsg). */ oa = &msg->rm_call.cb_cred; if (oa->oa_length > MAX_AUTH_BYTES) return (FALSE); /* 8 XDR units from the IXDR macro calls. */ if (sizeof(rpchdr) < (8 * BYTES_PER_XDR_UNIT + RNDUP(oa->oa_length))) return (FALSE); buf = (int32_t *)(void *)rpchdr; IXDR_PUT_LONG(buf, msg->rm_xid); IXDR_PUT_ENUM(buf, msg->rm_direction); IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers); IXDR_PUT_LONG(buf, msg->rm_call.cb_prog); IXDR_PUT_LONG(buf, msg->rm_call.cb_vers); IXDR_PUT_LONG(buf, msg->rm_call.cb_proc); IXDR_PUT_ENUM(buf, oa->oa_flavor); IXDR_PUT_LONG(buf, oa->oa_length); if (oa->oa_length) { memcpy((caddr_t)buf, oa->oa_base, oa->oa_length); buf += RNDUP(oa->oa_length) / sizeof(int32_t); } rpcbuf.value = rpchdr; rpcbuf.length = (u_char *)buf - rpchdr; checksum.value = msg->rm_call.cb_verf.oa_base; checksum.length = msg->rm_call.cb_verf.oa_length; maj_stat = gss_verify_mic(&min_stat, gd->ctx, &rpcbuf, &checksum, &qop_state); if (maj_stat != GSS_S_COMPLETE) { log_status("gss_verify_mic", maj_stat, min_stat); if (log_badverf != NULL) (*log_badverf)(gd->client_name, svcauth_gss_name, rqst, msg, log_badverf_data); return (FALSE); } return (TRUE); }
uint32_t sapgss_verify_mic( uint32_t *minor_status, gss_ctx_id_t context_handle, gss_buffer_t message_buffer, gss_buffer_t message_token, gss_qop_t *qop_state) { return gss_verify_mic(minor_status, context_handle, message_buffer, message_token, qop_state); }
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL gss_verify(OM_uint32 *minor_status, gss_ctx_id_t context_handle, gss_buffer_t message_buffer, gss_buffer_t token_buffer, int *qop_state) { return (gss_verify_mic(minor_status, context_handle, message_buffer, token_buffer, (gss_qop_t *)qop_state)); }
static bool_t authgss_validate(AUTH *auth, struct opaque_auth *verf) { struct rpc_gss_data *gd; u_int num, qop_state; gss_buffer_desc signbuf, checksum; OM_uint32 maj_stat, min_stat; log_debug("in authgss_validate()"); gd = AUTH_PRIVATE(auth); if (gd->established == FALSE) { /* would like to do this only on NULL rpc -- * gc->established is good enough. * save the on the wire verifier to validate last * INIT phase packet after decode if the major * status is GSS_S_COMPLETE */ if ((gd->gc_wire_verf.value = mem_alloc(verf->oa_length)) == NULL) { fprintf(stderr, "gss_validate: out of memory\n"); return (FALSE); } memcpy(gd->gc_wire_verf.value, verf->oa_base, verf->oa_length); gd->gc_wire_verf.length = verf->oa_length; return (TRUE); } if (gd->gc.gc_proc == RPCSEC_GSS_INIT || gd->gc.gc_proc == RPCSEC_GSS_CONTINUE_INIT) { num = htonl(gd->win); } else num = htonl(gd->gc.gc_seq); signbuf.value = # signbuf.length = sizeof(num); checksum.value = verf->oa_base; checksum.length = verf->oa_length; maj_stat = gss_verify_mic(&min_stat, gd->ctx, &signbuf, &checksum, &qop_state); if (maj_stat != GSS_S_COMPLETE || qop_state != gd->sec.qop) { log_status("gss_verify_mic", maj_stat, min_stat); if (maj_stat == GSS_S_CONTEXT_EXPIRED) { gd->established = FALSE; authgss_destroy_context(auth); } return (FALSE); } return (TRUE); }
static bool_t rpc_gss_validate(AUTH *auth, struct opaque_auth *verf) { struct rpc_gss_data *gd; gss_qop_t qop_state; uint32_t num; gss_buffer_desc signbuf, checksum; OM_uint32 maj_stat, min_stat; log_debug("in rpc_gss_validate()"); gd = AUTH_PRIVATE(auth); if (gd->gd_state == RPCSEC_GSS_CONTEXT) { /* * Save the on the wire verifier to validate last INIT * phase packet after decode if the major status is * GSS_S_COMPLETE. */ if (gd->gd_verf.value) xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &gd->gd_verf); gd->gd_verf.value = mem_alloc(verf->oa_length); if (gd->gd_verf.value == NULL) { fprintf(stderr, "gss_validate: out of memory\n"); _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM); return (FALSE); } memcpy(gd->gd_verf.value, verf->oa_base, verf->oa_length); gd->gd_verf.length = verf->oa_length; return (TRUE); } num = htonl(gd->gd_cred.gc_seq); signbuf.value = # signbuf.length = sizeof(num); checksum.value = verf->oa_base; checksum.length = verf->oa_length; maj_stat = gss_verify_mic(&min_stat, gd->gd_ctx, &signbuf, &checksum, &qop_state); if (maj_stat != GSS_S_COMPLETE || qop_state != gd->gd_qop) { log_status("gss_verify_mic", gd->gd_mech, maj_stat, min_stat); if (maj_stat == GSS_S_CONTEXT_EXPIRED) { rpc_gss_destroy_context(auth, TRUE); } _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM); return (FALSE); } return (TRUE); }
static int svcauth_gss_validate(struct svc_req *req, struct svc_rpc_gss_data *gd, struct rpc_msg *msg) { struct opaque_auth *oa; gss_buffer_desc rpcbuf, checksum; OM_uint32 maj_stat, min_stat, qop_state; u_char rpchdr[RPCHDR_LEN]; int32_t *buf; memset(rpchdr, 0, RPCHDR_LEN); /* XXX - Reconstruct RPC header for signing (from xdr_callmsg). */ oa = &msg->rm_call.cb_cred; if (oa->oa_length > MAX_AUTH_BYTES) return GSS_S_CALL_BAD_STRUCTURE; /* XXX since MAX_AUTH_BYTES is 400, the following code trivially * overruns (up to 431 per Coverity, but compare RPCHDR_LEN with * what is marshalled below). */ buf = (int32_t *) rpchdr; IXDR_PUT_LONG(buf, msg->rm_xid); IXDR_PUT_ENUM(buf, msg->rm_direction); IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers); IXDR_PUT_LONG(buf, msg->rm_call.cb_prog); IXDR_PUT_LONG(buf, msg->rm_call.cb_vers); IXDR_PUT_LONG(buf, msg->rm_call.cb_proc); IXDR_PUT_ENUM(buf, oa->oa_flavor); IXDR_PUT_LONG(buf, oa->oa_length); if (oa->oa_length) { memcpy((caddr_t) buf, oa->oa_base, oa->oa_length); buf += RNDUP(oa->oa_length) / sizeof(int32_t); } rpcbuf.value = rpchdr; rpcbuf.length = (u_char *) buf - rpchdr; checksum.value = msg->rm_call.cb_verf.oa_base; checksum.length = msg->rm_call.cb_verf.oa_length; maj_stat = gss_verify_mic(&min_stat, gd->ctx, &rpcbuf, &checksum, &qop_state); if (maj_stat != GSS_S_COMPLETE) { __warnx(TIRPC_DEBUG_FLAG_AUTH, "%s: %d %d", __func__, maj_stat, min_stat); return (maj_stat); } return GSS_S_COMPLETE; }
OM_uint32 ntlm_gss_verify_mic( OM_uint32 *minor_status, const gss_ctx_id_t context_handle, const gss_buffer_t msg_buffer, const gss_buffer_t token_buffer, gss_qop_t *qop_state) { OM_uint32 ret; ret = gss_verify_mic(minor_status, context_handle, msg_buffer, token_buffer, qop_state); return (ret); }
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); }
static int HandleOP(Verify) { OM_uint32 maj_stat, min_stat; int32_t hContext, flags, seqno; krb5_data msg, mic; gss_ctx_id_t ctx; gss_buffer_desc msg_token, mic_token; gss_qop_t qop; ret32(c, hContext); ctx = find_handle(c->handles, hContext, handle_context); if (ctx == NULL) errx(1, "verify: reference to unknown context"); ret32(c, flags); ret32(c, seqno); retdata(c, msg); msg_token.length = msg.length; msg_token.value = msg.data; retdata(c, mic); mic_token.length = mic.length; mic_token.value = mic.data; maj_stat = gss_verify_mic(&min_stat, ctx, &msg_token, &mic_token, &qop); if (maj_stat != GSS_S_COMPLETE) errx(1, "gss_verify_mic failed"); krb5_data_free(&mic); krb5_data_free(&msg); put32(c, 0); /* XXX fix gsm_error */ return 0; }
static void getverifymic(gss_ctx_id_t cctx, gss_ctx_id_t sctx, gss_OID mechoid) { gss_buffer_desc input_token, output_token; OM_uint32 min_stat, maj_stat; gss_qop_t qop_state; input_token.value = "bar"; input_token.length = 3; maj_stat = gss_get_mic(&min_stat, cctx, 0, &input_token, &output_token); if (maj_stat != GSS_S_COMPLETE) errx(1, "gss_get_mic failed: %s", gssapi_err(maj_stat, min_stat, mechoid)); maj_stat = gss_verify_mic(&min_stat, sctx, &input_token, &output_token, &qop_state); if (maj_stat != GSS_S_COMPLETE) errx(1, "gss_verify_mic failed: %s", gssapi_err(maj_stat, min_stat, mechoid)); gss_release_buffer(&min_stat, &output_token); }
static OM_uint32 verify_mechlist_mic (OM_uint32 *minor_status, gssspnego_ctx context_handle, gss_buffer_t mech_buf, heim_octet_string *mechListMIC ) { OM_uint32 ret; gss_buffer_desc mic_buf; if (context_handle->verified_mic) { /* This doesn't make sense, we've already verified it? */ *minor_status = 0; return GSS_S_DUPLICATE_TOKEN; } if (mechListMIC == NULL) { *minor_status = 0; return GSS_S_DEFECTIVE_TOKEN; } mic_buf.length = mechListMIC->length; mic_buf.value = mechListMIC->data; ret = gss_verify_mic(minor_status, context_handle->negotiated_ctx_id, mech_buf, &mic_buf, NULL); if (ret != GSS_S_COMPLETE) ret = GSS_S_DEFECTIVE_TOKEN; return ret; }
static bool_t authgss_refresh(AUTH *auth, struct rpc_msg *msg) { struct rpc_gss_data *gd; struct rpc_gss_init_res gr; gss_buffer_desc *recv_tokenp, send_token; OM_uint32 maj_stat, min_stat, call_stat, ret_flags; log_debug("in authgss_refresh()"); gd = AUTH_PRIVATE(auth); if (gd->established || gd->inprogress) return (TRUE); /* GSS context establishment loop. */ memset(&gr, 0, sizeof(gr)); recv_tokenp = GSS_C_NO_BUFFER; #ifdef DEBUG print_rpc_gss_sec(&gd->sec); #endif /*DEBUG*/ for (;;) { gd->inprogress = TRUE; maj_stat = gss_init_sec_context(&min_stat, gd->sec.cred, &gd->ctx, gd->name, gd->sec.mech, gd->sec.req_flags, 0, /* time req */ GSS_C_NO_CHANNEL_BINDINGS, recv_tokenp, NULL, /* used mech */ &send_token, &ret_flags, NULL); /* time rec */ log_status("gss_init_sec_context", maj_stat, min_stat); if (recv_tokenp != GSS_C_NO_BUFFER) { gss_release_buffer(&min_stat, &gr.gr_token); recv_tokenp = GSS_C_NO_BUFFER; } if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) { log_status("gss_init_sec_context (error)", maj_stat, min_stat); break; } if (send_token.length != 0) { memset(&gr, 0, sizeof(gr)); call_stat = clnt_call(gd->clnt, NULLPROC, xdr_rpc_gss_init_args, &send_token, xdr_rpc_gss_init_res, (caddr_t)&gr, AUTH_TIMEOUT); gss_release_buffer(&min_stat, &send_token); log_debug("authgss_refresh: call_stat=%d", call_stat); log_debug("%s", clnt_sperror(gd->clnt, "authgss_refresh")); if (call_stat != RPC_SUCCESS || (gr.gr_major != GSS_S_COMPLETE && gr.gr_major != GSS_S_CONTINUE_NEEDED)) break; if (gr.gr_ctx.length != 0) { if (gd->gc.gc_ctx.value) gss_release_buffer(&min_stat, &gd->gc.gc_ctx); gd->gc.gc_ctx = gr.gr_ctx; } if (gr.gr_token.length != 0) { if (maj_stat != GSS_S_CONTINUE_NEEDED) break; recv_tokenp = &gr.gr_token; } gd->gc.gc_proc = RPCSEC_GSS_CONTINUE_INIT; } /* GSS_S_COMPLETE => check gss header verifier, usually checked in * gss_validate */ if (maj_stat == GSS_S_COMPLETE) { gss_buffer_desc bufin; gss_buffer_desc bufout; uint32_t seq; gss_qop_t qop_state = 0; seq = htonl(gr.gr_win); bufin.value = (u_char *)&seq; bufin.length = sizeof(seq); bufout.value = (u_char *)gd->gc_wire_verf.value; bufout.length = gd->gc_wire_verf.length; log_debug("authgss_refresh: GSS_S_COMPLETE: calling verify_mic"); maj_stat = gss_verify_mic(&min_stat,gd->ctx, &bufin, &bufout, &qop_state); if (maj_stat != GSS_S_COMPLETE || qop_state != gd->sec.qop) { log_status("gss_verify_mic", maj_stat, min_stat); gss_release_buffer(&min_stat, &gd->gc_wire_verf); if (maj_stat == GSS_S_CONTEXT_EXPIRED) { gd->established = FALSE; authgss_destroy_context(auth); } return (FALSE); } gss_release_buffer(&min_stat, &gd->gc_wire_verf); gd->established = TRUE; gd->inprogress = FALSE; gd->gc.gc_proc = RPCSEC_GSS_DATA; gd->gc.gc_seq = 0; gd->win = gr.gr_win; break; } } log_status("authgss_refresh: at end of context negotiation", maj_stat, min_stat); /* End context negotiation loop. */ if (gd->gc.gc_proc != RPCSEC_GSS_DATA) { log_debug("authgss_refresh: returning ERROR (gc_proc %d)", gd->gc.gc_proc); if (gr.gr_token.length != 0) gss_release_buffer(&min_stat, &gr.gr_token); authgss_destroy(auth); auth = NULL; rpc_createerr.cf_stat = RPC_AUTHERROR; return (FALSE); } log_debug("authgss_refresh: returning SUCCESS"); return (TRUE); }
static bool_t rpc_gss_init(AUTH *auth, rpc_gss_options_ret_t *options_ret) { struct rpc_gss_data *gd; struct rpc_gss_init_res gr; gss_buffer_desc *recv_tokenp, recv_token, send_token; OM_uint32 maj_stat, min_stat, call_stat; const char *mech; log_debug("in rpc_gss_refresh()"); gd = AUTH_PRIVATE(auth); if (gd->gd_state != RPCSEC_GSS_START) return (TRUE); /* GSS context establishment loop. */ gd->gd_state = RPCSEC_GSS_CONTEXT; gd->gd_cred.gc_proc = RPCSEC_GSS_INIT; gd->gd_cred.gc_seq = 0; memset(&recv_token, 0, sizeof(recv_token)); memset(&gr, 0, sizeof(gr)); recv_tokenp = GSS_C_NO_BUFFER; for (;;) { maj_stat = gss_init_sec_context(&min_stat, gd->gd_options.my_cred, &gd->gd_ctx, gd->gd_name, gd->gd_mech, gd->gd_options.req_flags, gd->gd_options.time_req, gd->gd_options.input_channel_bindings, recv_tokenp, &gd->gd_mech, /* used mech */ &send_token, &options_ret->ret_flags, &options_ret->time_req); /* * Free the token which we got from the server (if * any). Remember that this was allocated by XDR, not * GSS-API. */ if (recv_tokenp != GSS_C_NO_BUFFER) { xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_token); recv_tokenp = GSS_C_NO_BUFFER; } if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) { log_status("gss_init_sec_context", gd->gd_mech, maj_stat, min_stat); options_ret->major_status = maj_stat; options_ret->minor_status = min_stat; break; } if (send_token.length != 0) { memset(&gr, 0, sizeof(gr)); call_stat = clnt_call(gd->gd_clnt, NULLPROC, (xdrproc_t)xdr_gss_buffer_desc, &send_token, (xdrproc_t)xdr_rpc_gss_init_res, (caddr_t)&gr, AUTH_TIMEOUT); gss_release_buffer(&min_stat, &send_token); if (call_stat != RPC_SUCCESS) break; if (gr.gr_major != GSS_S_COMPLETE && gr.gr_major != GSS_S_CONTINUE_NEEDED) { log_status("server reply", gd->gd_mech, gr.gr_major, gr.gr_minor); options_ret->major_status = gr.gr_major; options_ret->minor_status = gr.gr_minor; break; } /* * Save the server's gr_handle value, freeing * what we have already (remember that this * was allocated by XDR, not GSS-API). */ if (gr.gr_handle.length != 0) { xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &gd->gd_cred.gc_handle); gd->gd_cred.gc_handle = gr.gr_handle; } /* * Save the server's token as well. */ if (gr.gr_token.length != 0) { recv_token = gr.gr_token; recv_tokenp = &recv_token; } /* * Since we have copied out all the bits of gr * which XDR allocated for us, we don't need * to free it. */ gd->gd_cred.gc_proc = RPCSEC_GSS_CONTINUE_INIT; } if (maj_stat == GSS_S_COMPLETE) { gss_buffer_desc bufin; u_int seq, qop_state = 0; /* * gss header verifier, * usually checked in gss_validate */ seq = htonl(gr.gr_win); bufin.value = (unsigned char *)&seq; bufin.length = sizeof(seq); maj_stat = gss_verify_mic(&min_stat, gd->gd_ctx, &bufin, &gd->gd_verf, &qop_state); if (maj_stat != GSS_S_COMPLETE || qop_state != gd->gd_qop) { log_status("gss_verify_mic", gd->gd_mech, maj_stat, min_stat); if (maj_stat == GSS_S_CONTEXT_EXPIRED) { rpc_gss_destroy_context(auth, TRUE); } _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM); options_ret->major_status = maj_stat; options_ret->minor_status = min_stat; return (FALSE); } options_ret->major_status = GSS_S_COMPLETE; options_ret->minor_status = 0; options_ret->rpcsec_version = gd->gd_cred.gc_version; options_ret->gss_context = gd->gd_ctx; if (rpc_gss_oid_to_mech(gd->gd_mech, &mech)) { strlcpy(options_ret->actual_mechanism, mech, sizeof(options_ret->actual_mechanism)); } gd->gd_state = RPCSEC_GSS_ESTABLISHED; gd->gd_cred.gc_proc = RPCSEC_GSS_DATA; gd->gd_cred.gc_seq = 0; gd->gd_win = gr.gr_win; break; } } xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &gd->gd_verf); /* End context negotiation loop. */ if (gd->gd_cred.gc_proc != RPCSEC_GSS_DATA) { rpc_createerr.cf_stat = RPC_AUTHERROR; _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM); return (FALSE); } return (TRUE); }
static OM_uint32 spnego_reply (OM_uint32 * minor_status, const gssspnego_cred cred, gss_ctx_id_t * context_handle, const gss_name_t target_name, const gss_OID mech_type, OM_uint32 req_flags, OM_uint32 time_req, const gss_channel_bindings_t input_chan_bindings, const gss_buffer_t input_token, gss_OID * actual_mech_type, gss_buffer_t output_token, OM_uint32 * ret_flags, OM_uint32 * time_rec ) { OM_uint32 ret, minor; NegTokenResp resp; u_char oidbuf[17]; size_t oidlen; size_t len, taglen; gss_OID_desc mech; int require_mic; size_t buf_len; gss_buffer_desc mic_buf, mech_buf; gss_buffer_desc mech_output_token; gssspnego_ctx ctx; *minor_status = 0; ctx = (gssspnego_ctx)*context_handle; output_token->length = 0; output_token->value = NULL; mech_output_token.length = 0; mech_output_token.value = NULL; mech_buf.value = NULL; mech_buf.length = 0; ret = der_match_tag_and_length(input_token->value, input_token->length, ASN1_C_CONTEXT, CONS, 1, &len, &taglen); if (ret) return ret; if (len > input_token->length - taglen) return ASN1_OVERRUN; ret = decode_NegTokenResp((const unsigned char *)input_token->value+taglen, len, &resp, NULL); if (ret) { *minor_status = ENOMEM; return GSS_S_FAILURE; } if (resp.negResult == NULL || *(resp.negResult) == reject || resp.supportedMech == NULL) { free_NegTokenResp(&resp); return GSS_S_BAD_MECH; } ret = der_put_oid(oidbuf + sizeof(oidbuf) - 1, sizeof(oidbuf), resp.supportedMech, &oidlen); if (ret || (oidlen == GSS_SPNEGO_MECHANISM->length && memcmp(oidbuf + sizeof(oidbuf) - oidlen, GSS_SPNEGO_MECHANISM->elements, oidlen) == 0)) { /* Avoid recursively embedded SPNEGO */ free_NegTokenResp(&resp); return GSS_S_BAD_MECH; } HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); if (resp.responseToken != NULL) { gss_buffer_desc mech_input_token; mech_input_token.length = resp.responseToken->length; mech_input_token.value = resp.responseToken->data; mech.length = oidlen; mech.elements = oidbuf + sizeof(oidbuf) - oidlen; /* Fall through as if the negotiated mechanism was requested explicitly */ ret = gss_init_sec_context(&minor, (cred != NULL) ? cred->negotiated_cred_id : GSS_C_NO_CREDENTIAL, &ctx->negotiated_ctx_id, target_name, &mech, req_flags, time_req, input_chan_bindings, &mech_input_token, &ctx->negotiated_mech_type, &mech_output_token, &ctx->mech_flags, &ctx->mech_time_rec); if (GSS_ERROR(ret)) { HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); free_NegTokenResp(&resp); *minor_status = minor; return ret; } if (ret == GSS_S_COMPLETE) { ctx->open = 1; } } if (*(resp.negResult) == request_mic) { ctx->require_mic = 1; } if (ctx->open) { /* * Verify the mechListMIC if one was provided or CFX was * used and a non-preferred mechanism was selected */ if (resp.mechListMIC != NULL) { require_mic = 1; } else { ret = _gss_spnego_require_mechlist_mic(minor_status, ctx, &require_mic); if (ret) { HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); free_NegTokenResp(&resp); gss_release_buffer(&minor, &mech_output_token); return ret; } } } else { require_mic = 0; } if (require_mic) { ASN1_MALLOC_ENCODE(MechTypeList, mech_buf.value, mech_buf.length, &ctx->initiator_mech_types, &buf_len, ret); if (ret) { HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); free_NegTokenResp(&resp); gss_release_buffer(&minor, &mech_output_token); *minor_status = ret; return GSS_S_FAILURE; } if (mech_buf.length != buf_len) abort(); if (resp.mechListMIC == NULL) { HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); free(mech_buf.value); free_NegTokenResp(&resp); *minor_status = 0; return GSS_S_DEFECTIVE_TOKEN; } mic_buf.length = resp.mechListMIC->length; mic_buf.value = resp.mechListMIC->data; if (mech_output_token.length == 0) { ret = gss_verify_mic(minor_status, ctx->negotiated_ctx_id, &mech_buf, &mic_buf, NULL); if (ret) { HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); free(mech_buf.value); gss_release_buffer(&minor, &mech_output_token); free_NegTokenResp(&resp); return GSS_S_DEFECTIVE_TOKEN; } ctx->verified_mic = 1; } } ret = spnego_reply_internal(minor_status, ctx, require_mic ? &mech_buf : NULL, &mech_output_token, output_token); if (mech_buf.value != NULL) free(mech_buf.value); free_NegTokenResp(&resp); gss_release_buffer(&minor, &mech_output_token); if (actual_mech_type) *actual_mech_type = ctx->negotiated_mech_type; if (ret_flags) *ret_flags = ctx->mech_flags; if (time_rec) *time_rec = ctx->mech_time_rec; HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); return ret; }
bool_t xdr_rpc_gss_unwrap_data(XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr, gss_ctx_id_t ctx, gss_qop_t qop, rpc_gss_svc_t svc, u_int seq) { XDR tmpxdrs; gss_buffer_desc databuf, wrapbuf; OM_uint32 maj_stat, min_stat; u_int seq_num, qop_state; int conf_state; bool_t xdr_stat; if (xdr_func == (xdrproc_t)xdr_void || xdr_ptr == NULL) return (TRUE); memset(&databuf, 0, sizeof(databuf)); memset(&wrapbuf, 0, sizeof(wrapbuf)); if (svc == RPCSEC_GSS_SVC_INTEGRITY) { /* Decode databody_integ. */ if (!xdr_rpc_gss_buf(xdrs, &databuf, (u_int)-1)) { log_debug("xdr decode databody_integ failed"); return (FALSE); } /* Decode checksum. */ if (!xdr_rpc_gss_buf(xdrs, &wrapbuf, (u_int)-1)) { gss_release_buffer(&min_stat, &databuf); log_debug("xdr decode checksum failed"); return (FALSE); } /* Verify checksum and QOP. */ maj_stat = gss_verify_mic(&min_stat, ctx, &databuf, &wrapbuf, &qop_state); gss_release_buffer(&min_stat, &wrapbuf); if (maj_stat != GSS_S_COMPLETE || qop_state != qop) { gss_release_buffer(&min_stat, &databuf); log_status("gss_verify_mic", maj_stat, min_stat); return (FALSE); } } else if (svc == RPCSEC_GSS_SVC_PRIVACY) { /* Decode databody_priv. */ if (!xdr_rpc_gss_buf(xdrs, &wrapbuf, (u_int)-1)) { log_debug("xdr decode databody_priv failed"); return (FALSE); } /* Decrypt databody. */ maj_stat = gss_unwrap(&min_stat, ctx, &wrapbuf, &databuf, &conf_state, &qop_state); gss_release_buffer(&min_stat, &wrapbuf); /* Verify encryption and QOP. */ if (maj_stat != GSS_S_COMPLETE || qop_state != qop || conf_state != TRUE) { gss_release_buffer(&min_stat, &databuf); log_status("gss_unwrap", maj_stat, min_stat); return (FALSE); } } /* Decode rpc_gss_data_t (sequence number + arguments). */ xdrmem_create(&tmpxdrs, databuf.value, databuf.length, XDR_DECODE); xdr_stat = (xdr_u_int(&tmpxdrs, &seq_num) && (*xdr_func)(&tmpxdrs, xdr_ptr)); XDR_DESTROY(&tmpxdrs); gss_release_buffer(&min_stat, &databuf); /* Verify sequence number. */ if (xdr_stat == TRUE && seq_num != seq) { log_debug("wrong sequence number in databody"); return (FALSE); } return (xdr_stat); }
int main(int argc, char **argv) { struct module_stat stat; int mod; int syscall_num; stat.version = sizeof(stat); mod = modfind("gsstest_syscall"); if (mod < 0) { fprintf(stderr, "%s: kernel support not present\n", argv[0]); exit(1); } modstat(mod, &stat); syscall_num = stat.data.intval; switch (atoi(argv[1])) { case 1: syscall(syscall_num, 1, NULL, NULL); break; case 2: { struct gsstest_2_args args; struct gsstest_2_res res; char hostname[512]; char token_buffer[8192]; OM_uint32 maj_stat, min_stat; gss_ctx_id_t client_context = GSS_C_NO_CONTEXT; gss_cred_id_t client_cred; gss_OID mech_type = GSS_C_NO_OID; gss_buffer_desc name_buf, message_buf; gss_name_t name; int32_t enctypes[] = { ETYPE_DES_CBC_CRC, ETYPE_ARCFOUR_HMAC_MD5, ETYPE_ARCFOUR_HMAC_MD5_56, ETYPE_AES256_CTS_HMAC_SHA1_96, ETYPE_AES128_CTS_HMAC_SHA1_96, ETYPE_DES3_CBC_SHA1, }; int num_enctypes = sizeof(enctypes) / sizeof(enctypes[0]); int established; int i; for (i = 0; i < num_enctypes; i++) { printf("testing etype %d\n", enctypes[i]); args.output_token.length = sizeof(token_buffer); args.output_token.value = token_buffer; gethostname(hostname, sizeof(hostname)); snprintf(token_buffer, sizeof(token_buffer), "nfs@%s", hostname); name_buf.length = strlen(token_buffer); name_buf.value = token_buffer; maj_stat = gss_import_name(&min_stat, &name_buf, GSS_C_NT_HOSTBASED_SERVICE, &name); if (GSS_ERROR(maj_stat)) { printf("gss_import_name failed\n"); report_error(mech_type, maj_stat, min_stat); goto out; } maj_stat = gss_acquire_cred(&min_stat, GSS_C_NO_NAME, 0, GSS_C_NO_OID_SET, GSS_C_INITIATE, &client_cred, NULL, NULL); if (GSS_ERROR(maj_stat)) { printf("gss_acquire_cred (client) failed\n"); report_error(mech_type, maj_stat, min_stat); goto out; } maj_stat = gss_krb5_set_allowable_enctypes(&min_stat, client_cred, 1, &enctypes[i]); if (GSS_ERROR(maj_stat)) { printf("gss_krb5_set_allowable_enctypes failed\n"); report_error(mech_type, maj_stat, min_stat); goto out; } res.output_token.length = 0; res.output_token.value = 0; established = 0; while (!established) { maj_stat = gss_init_sec_context(&min_stat, client_cred, &client_context, name, GSS_C_NO_OID, (GSS_C_MUTUAL_FLAG |GSS_C_CONF_FLAG |GSS_C_INTEG_FLAG |GSS_C_SEQUENCE_FLAG |GSS_C_REPLAY_FLAG), 0, GSS_C_NO_CHANNEL_BINDINGS, &res.output_token, &mech_type, &args.input_token, NULL, NULL); if (GSS_ERROR(maj_stat)) { printf("gss_init_sec_context failed\n"); report_error(mech_type, maj_stat, min_stat); goto out; } if (args.input_token.length) { args.step = 1; syscall(syscall_num, 2, &args, &res); gss_release_buffer(&min_stat, &args.input_token); if (res.maj_stat != GSS_S_COMPLETE && res.maj_stat != GSS_S_CONTINUE_NEEDED) { printf("gss_accept_sec_context (kernel) failed\n"); report_error(mech_type, res.maj_stat, res.min_stat); goto out; } } if (maj_stat == GSS_S_COMPLETE) established = 1; } message_buf.value = "Hello world"; message_buf.length = strlen((char *) message_buf.value); maj_stat = gss_get_mic(&min_stat, client_context, GSS_C_QOP_DEFAULT, &message_buf, &args.input_token); if (GSS_ERROR(maj_stat)) { printf("gss_get_mic failed\n"); report_error(mech_type, maj_stat, min_stat); goto out; } args.step = 2; syscall(syscall_num, 2, &args, &res); gss_release_buffer(&min_stat, &args.input_token); if (GSS_ERROR(res.maj_stat)) { printf("kernel gss_verify_mic failed\n"); report_error(mech_type, res.maj_stat, res.min_stat); goto out; } maj_stat = gss_verify_mic(&min_stat, client_context, &message_buf, &res.output_token, NULL); if (GSS_ERROR(maj_stat)) { printf("gss_verify_mic failed\n"); report_error(mech_type, maj_stat, min_stat); goto out; } maj_stat = gss_wrap(&min_stat, client_context, TRUE, GSS_C_QOP_DEFAULT, &message_buf, NULL, &args.input_token); if (GSS_ERROR(maj_stat)) { printf("gss_wrap failed\n"); report_error(mech_type, maj_stat, min_stat); goto out; } args.step = 3; syscall(syscall_num, 2, &args, &res); gss_release_buffer(&min_stat, &args.input_token); if (GSS_ERROR(res.maj_stat)) { printf("kernel gss_unwrap failed\n"); report_error(mech_type, res.maj_stat, res.min_stat); goto out; } maj_stat = gss_unwrap(&min_stat, client_context, &res.output_token, &message_buf, NULL, NULL); if (GSS_ERROR(maj_stat)) { printf("gss_unwrap failed\n"); report_error(mech_type, maj_stat, min_stat); goto out; } gss_release_buffer(&min_stat, &message_buf); maj_stat = gss_wrap(&min_stat, client_context, FALSE, GSS_C_QOP_DEFAULT, &message_buf, NULL, &args.input_token); if (GSS_ERROR(maj_stat)) { printf("gss_wrap failed\n"); report_error(mech_type, maj_stat, min_stat); goto out; } args.step = 4; syscall(syscall_num, 2, &args, &res); gss_release_buffer(&min_stat, &args.input_token); if (GSS_ERROR(res.maj_stat)) { printf("kernel gss_unwrap failed\n"); report_error(mech_type, res.maj_stat, res.min_stat); goto out; } maj_stat = gss_unwrap(&min_stat, client_context, &res.output_token, &message_buf, NULL, NULL); if (GSS_ERROR(maj_stat)) { printf("gss_unwrap failed\n"); report_error(mech_type, maj_stat, min_stat); goto out; } gss_release_buffer(&min_stat, &message_buf); args.step = 5; syscall(syscall_num, 2, &args, &res); gss_release_name(&min_stat, &name); gss_release_cred(&min_stat, &client_cred); gss_delete_sec_context(&min_stat, &client_context, GSS_C_NO_BUFFER); } break; } case 3: syscall(syscall_num, 3, NULL, NULL); break; case 4: syscall(syscall_num, 4, NULL, NULL); break; } return (0); out: return (1); }
static bool_t authgss_refresh(AUTH *auth) { struct rpc_gss_data *gd; struct rpc_gss_init_res gr; gss_buffer_desc *recv_tokenp, send_token; OM_uint32 maj_stat, min_stat, call_stat, ret_flags; log_debug("in authgss_refresh()"); gd = AUTH_PRIVATE(auth); if (gd->established) return (TRUE); /* GSS context establishment loop. */ memset(&gr, 0, sizeof(gr)); recv_tokenp = GSS_C_NO_BUFFER; #ifdef DEBUG print_rpc_gss_sec(&gd->sec); #endif /*DEBUG*/ for (;;) { #ifdef DEBUG /* print the token we just received */ if (recv_tokenp != GSS_C_NO_BUFFER) { log_debug("The token we just received (length %d):", recv_tokenp->length); log_hexdump(recv_tokenp->value, recv_tokenp->length, 0); } #endif maj_stat = gss_init_sec_context(&min_stat, gd->sec.cred, &gd->ctx, gd->name, gd->sec.mech, gd->sec.req_flags, 0, /* time req */ NULL, /* channel */ recv_tokenp, NULL, /* used mech */ &send_token, &ret_flags, NULL); /* time rec */ if (recv_tokenp != GSS_C_NO_BUFFER) { gss_release_buffer(&min_stat, &gr.gr_token); recv_tokenp = GSS_C_NO_BUFFER; } if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) { log_status("gss_init_sec_context", maj_stat, min_stat); break; } if (send_token.length != 0) { memset(&gr, 0, sizeof(gr)); #ifdef DEBUG /* print the token we are about to send */ log_debug("The token being sent (length %d):", send_token.length); log_hexdump(send_token.value, send_token.length, 0); #endif call_stat = clnt_call(gd->clnt, NULLPROC, (xdrproc_t)xdr_rpc_gss_init_args, &send_token, (xdrproc_t)xdr_rpc_gss_init_res, (caddr_t)&gr, AUTH_TIMEOUT); gss_release_buffer(&min_stat, &send_token); if (call_stat != RPC_SUCCESS || (gr.gr_major != GSS_S_COMPLETE && gr.gr_major != GSS_S_CONTINUE_NEEDED)) return FALSE; if (gr.gr_ctx.length != 0) { if (gd->gc.gc_ctx.value) gss_release_buffer(&min_stat, &gd->gc.gc_ctx); gd->gc.gc_ctx = gr.gr_ctx; } if (gr.gr_token.length != 0) { if (maj_stat != GSS_S_CONTINUE_NEEDED) break; recv_tokenp = &gr.gr_token; } gd->gc.gc_proc = RPCSEC_GSS_CONTINUE_INIT; } /* GSS_S_COMPLETE => check gss header verifier, * usually checked in gss_validate */ if (maj_stat == GSS_S_COMPLETE) { gss_buffer_desc bufin; gss_buffer_desc bufout; u_int seq, qop_state = 0; seq = htonl(gr.gr_win); bufin.value = (unsigned char *)&seq; bufin.length = sizeof(seq); bufout.value = (unsigned char *)gd->gc_wire_verf.value; bufout.length = gd->gc_wire_verf.length; maj_stat = gss_verify_mic(&min_stat, gd->ctx, &bufin, &bufout, &qop_state); if (maj_stat != GSS_S_COMPLETE || qop_state != gd->sec.qop) { log_status("gss_verify_mic", maj_stat, min_stat); if (maj_stat == GSS_S_CONTEXT_EXPIRED) { gd->established = FALSE; authgss_destroy_context(auth); } return (FALSE); } gd->established = TRUE; gd->gc.gc_proc = RPCSEC_GSS_DATA; gd->gc.gc_seq = 0; gd->win = gr.gr_win; break; } } /* End context negotiation loop. */ if (gd->gc.gc_proc != RPCSEC_GSS_DATA) { if (gr.gr_token.length != 0) gss_release_buffer(&min_stat, &gr.gr_token); authgss_destroy(auth); auth = NULL; rpc_createerr.cf_stat = RPC_AUTHERROR; return (FALSE); } return (TRUE); }
bool_t xdr_rpc_gss_unwrap_data(XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr, gss_ctx_id_t ctx, gss_qop_t qop, rpc_gss_service_t svc, u_int seq) { XDR tmpxdrs; gss_buffer_desc databuf, wrapbuf; OM_uint32 maj_stat, min_stat; u_int seq_num, conf_state, qop_state; bool_t xdr_stat; if (xdr_func == (xdrproc_t) xdr_void || xdr_ptr == NULL) return (TRUE); memset(&databuf, 0, sizeof(databuf)); memset(&wrapbuf, 0, sizeof(wrapbuf)); if (svc == rpc_gss_svc_integrity) { /* Decode databody_integ. */ if (!xdr_gss_buffer_desc(xdrs, &databuf)) { log_debug("xdr decode databody_integ failed"); return (FALSE); } /* Decode checksum. */ if (!xdr_gss_buffer_desc(xdrs, &wrapbuf)) { mem_free(databuf.value, databuf.length); log_debug("xdr decode checksum failed"); return (FALSE); } /* Verify checksum and QOP. */ maj_stat = gss_verify_mic(&min_stat, ctx, &databuf, &wrapbuf, &qop_state); mem_free(wrapbuf.value, wrapbuf.length); if (maj_stat != GSS_S_COMPLETE || qop_state != qop) { mem_free(databuf.value, databuf.length); log_status("gss_verify_mic", NULL, maj_stat, min_stat); return (FALSE); } } else if (svc == rpc_gss_svc_privacy) { /* Decode databody_priv. */ if (!xdr_gss_buffer_desc(xdrs, &wrapbuf)) { log_debug("xdr decode databody_priv failed"); return (FALSE); } /* Decrypt databody. */ maj_stat = gss_unwrap(&min_stat, ctx, &wrapbuf, &databuf, &conf_state, &qop_state); mem_free(wrapbuf.value, wrapbuf.length); /* Verify encryption and QOP. */ if (maj_stat != GSS_S_COMPLETE || qop_state != qop || conf_state != TRUE) { gss_release_buffer(&min_stat, &databuf); log_status("gss_unwrap", NULL, maj_stat, min_stat); return (FALSE); } } /* Decode rpc_gss_data_t (sequence number + arguments). */ xdrmem_create(&tmpxdrs, databuf.value, databuf.length, XDR_DECODE); xdr_stat = (xdr_u_int(&tmpxdrs, &seq_num) && xdr_func(&tmpxdrs, xdr_ptr)); XDR_DESTROY(&tmpxdrs); /* * Integrity service allocates databuf via XDR so free it the * same way. */ if (svc == rpc_gss_svc_integrity) { xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &databuf); } else { gss_release_buffer(&min_stat, &databuf); } /* Verify sequence number. */ if (xdr_stat == TRUE && seq_num != seq) { log_debug("wrong sequence number in databody"); return (FALSE); } return (xdr_stat); }
static OM_uint32 spnego_reply (OM_uint32 * minor_status, const gssspnego_cred cred, gss_ctx_id_t * context_handle, const gss_name_t target_name, const gss_OID mech_type, OM_uint32 req_flags, OM_uint32 time_req, const gss_channel_bindings_t input_chan_bindings, const gss_buffer_t input_token, gss_OID * actual_mech_type, gss_buffer_t output_token, OM_uint32 * ret_flags, OM_uint32 * time_rec ) { OM_uint32 ret, minor; NegotiationToken resp; gss_OID_desc mech; int require_mic; size_t buf_len; gss_buffer_desc mic_buf, mech_buf; gss_buffer_desc mech_output_token; gssspnego_ctx ctx; *minor_status = 0; ctx = (gssspnego_ctx)*context_handle; output_token->length = 0; output_token->value = NULL; mech_output_token.length = 0; mech_output_token.value = NULL; mech_buf.value = NULL; mech_buf.length = 0; ret = decode_NegotiationToken(input_token->value, input_token->length, &resp, NULL); if (ret) return ret; if (resp.element != choice_NegotiationToken_negTokenResp) { free_NegotiationToken(&resp); *minor_status = 0; return GSS_S_BAD_MECH; } if (resp.u.negTokenResp.negResult == NULL || *(resp.u.negTokenResp.negResult) == reject /* || resp.u.negTokenResp.supportedMech == NULL */ ) { free_NegotiationToken(&resp); return GSS_S_BAD_MECH; } /* * Pick up the mechanism that the acceptor selected, only allow it * to be sent in packet. */ HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); if (resp.u.negTokenResp.supportedMech) { if (ctx->oidlen) { free_NegotiationToken(&resp); HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); return GSS_S_BAD_MECH; } ret = der_put_oid(ctx->oidbuf + sizeof(ctx->oidbuf) - 1, sizeof(ctx->oidbuf), resp.u.negTokenResp.supportedMech, &ctx->oidlen); /* Avoid recursively embedded SPNEGO */ if (ret || (ctx->oidlen == GSS_SPNEGO_MECHANISM->length && memcmp(ctx->oidbuf + sizeof(ctx->oidbuf) - ctx->oidlen, GSS_SPNEGO_MECHANISM->elements, ctx->oidlen) == 0)) { free_NegotiationToken(&resp); HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); return GSS_S_BAD_MECH; } /* check if the acceptor took our optimistic token */ if (ctx->oidlen != ctx->preferred_mech_type->length || memcmp(ctx->oidbuf + sizeof(ctx->oidbuf) - ctx->oidlen, ctx->preferred_mech_type->elements, ctx->oidlen) != 0) { gss_delete_sec_context(&minor, &ctx->negotiated_ctx_id, GSS_C_NO_BUFFER); ctx->negotiated_ctx_id = GSS_C_NO_CONTEXT; } } else if (ctx->oidlen == 0) { free_NegotiationToken(&resp); HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); return GSS_S_BAD_MECH; } /* if a token (of non zero length), or no context, pass to underlaying mech */ if ((resp.u.negTokenResp.responseToken != NULL && resp.u.negTokenResp.responseToken->length) || ctx->negotiated_ctx_id == GSS_C_NO_CONTEXT) { gss_buffer_desc mech_input_token; if (resp.u.negTokenResp.responseToken) { mech_input_token.length = resp.u.negTokenResp.responseToken->length; mech_input_token.value = resp.u.negTokenResp.responseToken->data; } else { mech_input_token.length = 0; mech_input_token.value = NULL; } mech.length = ctx->oidlen; mech.elements = ctx->oidbuf + sizeof(ctx->oidbuf) - ctx->oidlen; /* Fall through as if the negotiated mechanism was requested explicitly */ ret = gss_init_sec_context(&minor, (cred != NULL) ? cred->negotiated_cred_id : GSS_C_NO_CREDENTIAL, &ctx->negotiated_ctx_id, ctx->target_name, &mech, req_flags, time_req, input_chan_bindings, &mech_input_token, &ctx->negotiated_mech_type, &mech_output_token, &ctx->mech_flags, &ctx->mech_time_rec); if (GSS_ERROR(ret)) { HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); free_NegotiationToken(&resp); gss_mg_collect_error(&mech, ret, minor); *minor_status = minor; return ret; } if (ret == GSS_S_COMPLETE) { ctx->open = 1; } } else if (*(resp.u.negTokenResp.negResult) == accept_completed) { if (ctx->maybe_open) ctx->open = 1; } if (*(resp.u.negTokenResp.negResult) == request_mic) { ctx->require_mic = 1; } if (ctx->open) { /* * Verify the mechListMIC if one was provided or CFX was * used and a non-preferred mechanism was selected */ if (resp.u.negTokenResp.mechListMIC != NULL) { require_mic = 1; } else { ret = _gss_spnego_require_mechlist_mic(minor_status, ctx, &require_mic); if (ret) { HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); free_NegotiationToken(&resp); gss_release_buffer(&minor, &mech_output_token); return ret; } } } else { require_mic = 0; } if (require_mic) { ASN1_MALLOC_ENCODE(MechTypeList, mech_buf.value, mech_buf.length, &ctx->initiator_mech_types, &buf_len, ret); if (ret) { HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); free_NegotiationToken(&resp); gss_release_buffer(&minor, &mech_output_token); *minor_status = ret; return GSS_S_FAILURE; } if (mech_buf.length != buf_len) abort(); if (resp.u.negTokenResp.mechListMIC == NULL) { HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); free(mech_buf.value); free_NegotiationToken(&resp); *minor_status = 0; return GSS_S_DEFECTIVE_TOKEN; } mic_buf.length = resp.u.negTokenResp.mechListMIC->length; mic_buf.value = resp.u.negTokenResp.mechListMIC->data; if (mech_output_token.length == 0) { ret = gss_verify_mic(minor_status, ctx->negotiated_ctx_id, &mech_buf, &mic_buf, NULL); if (ret) { HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); free(mech_buf.value); gss_release_buffer(&minor, &mech_output_token); free_NegotiationToken(&resp); return GSS_S_DEFECTIVE_TOKEN; } ctx->verified_mic = 1; } } ret = spnego_reply_internal(minor_status, ctx, require_mic ? &mech_buf : NULL, &mech_output_token, output_token); if (mech_buf.value != NULL) free(mech_buf.value); free_NegotiationToken(&resp); gss_release_buffer(&minor, &mech_output_token); if (actual_mech_type) *actual_mech_type = ctx->negotiated_mech_type; if (ret_flags) *ret_flags = ctx->mech_flags; if (time_rec) *time_rec = ctx->mech_time_rec; HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); return ret; }
static bool_t Svcauth_gss_validate(struct svc_req *rqst, struct svc_rpc_gss_data *gd, struct rpc_msg *msg) { struct opaque_auth *oa; gss_buffer_desc rpcbuf, checksum; OM_uint32 maj_stat, min_stat, qop_state; u_char rpchdr[128]; int32_t *buf; char GssError[256]; memset(rpchdr, 0, sizeof(rpchdr)); /* XXX - Reconstruct RPC header for signing (from xdr_callmsg). */ oa = &msg->rm_call.cb_cred; LogFullDebug(COMPONENT_RPCSEC_GSS, "Call to Svcauth_gss_validate --> xid=%u dir=%u rpcvers=%u prog=%u vers=%u proc=%u flavor=%u len=%u base=%p ckeck.len=%u check.val=%p", msg->rm_xid, msg->rm_direction, msg->rm_call.cb_rpcvers, msg->rm_call.cb_prog, msg->rm_call.cb_vers, msg->rm_call.cb_proc, oa->oa_flavor, oa->oa_length, oa->oa_base, msg->rm_call.cb_verf.oa_length, msg->rm_call.cb_verf.oa_base); if(oa->oa_length > MAX_AUTH_BYTES) { LogCrit(COMPONENT_RPCSEC_GSS, "Svcauth_gss_validate oa->oa_length (%u) > MAX_AUTH_BYTES (%u)", oa->oa_length, MAX_AUTH_BYTES); return (FALSE); } /* 8 XDR units from the IXDR macro calls. */ if(sizeof(rpchdr) < (8 * BYTES_PER_XDR_UNIT + RNDUP(oa->oa_length))) { LogCrit(COMPONENT_RPCSEC_GSS, "Svcauth_gss_validate sizeof(rpchdr) (%d) < (8 * BYTES_PER_XDR_UNIT (%d) + RNDUP(oa->oa_length (%u))) (%d)", (int) sizeof(rpchdr), (int) (8 * BYTES_PER_XDR_UNIT), oa->oa_length, (int) (8 * BYTES_PER_XDR_UNIT + RNDUP(oa->oa_length))); return (FALSE); } buf = (int32_t *) (void *)rpchdr; IXDR_PUT_LONG(buf, msg->rm_xid); IXDR_PUT_ENUM(buf, msg->rm_direction); IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers); IXDR_PUT_LONG(buf, msg->rm_call.cb_prog); IXDR_PUT_LONG(buf, msg->rm_call.cb_vers); IXDR_PUT_LONG(buf, msg->rm_call.cb_proc); IXDR_PUT_ENUM(buf, oa->oa_flavor); IXDR_PUT_LONG(buf, oa->oa_length); if(oa->oa_length) { memcpy((caddr_t) buf, oa->oa_base, oa->oa_length); buf += RNDUP(oa->oa_length) / sizeof(int32_t); } rpcbuf.value = rpchdr; rpcbuf.length = (u_char *) buf - rpchdr; checksum.value = msg->rm_call.cb_verf.oa_base; checksum.length = msg->rm_call.cb_verf.oa_length; if(isFullDebug(COMPONENT_RPCSEC_GSS)) { char ctx_str[64]; sprint_ctx(ctx_str, (unsigned char *)gd->ctx, sizeof(gss_union_ctx_id_desc)); LogFullDebug(COMPONENT_RPCSEC_GSS, "Svcauth_gss_validate context %s rpcbuf=%p:%u checksum=%p:$%u)", ctx_str, rpcbuf.value, (unsigned int) rpcbuf.length, checksum.value, (unsigned int) checksum.length); } maj_stat = gss_verify_mic(&min_stat, gd->ctx, &rpcbuf, &checksum, &qop_state); if(maj_stat != GSS_S_COMPLETE) { log_sperror_gss(GssError, maj_stat, min_stat); LogCrit(COMPONENT_RPCSEC_GSS, "Error in gss_verify_mic: %s", GssError); return (FALSE); } return (TRUE); }