OM_uint32 krb5_gss_canonicalize_name(OM_uint32 *minor_status, const gss_name_t input_name, const gss_OID mech_type, gss_name_t *output_name) { if ((mech_type != GSS_C_NULL_OID) && !g_OID_equal(gss_mech_krb5, mech_type) && !g_OID_equal(gss_mech_krb5_old, mech_type)) { *minor_status = 0; return(GSS_S_BAD_MECH); } return(krb5_gss_duplicate_name(minor_status, input_name, output_name)); }
static int get_hostbased_client_name(gss_name_t client_name, gss_OID mech, char **hostbased_name) { u_int32_t maj_stat, min_stat; gss_buffer_desc name; gss_OID name_type = GSS_C_NO_OID; char *cname; int res = -1; *hostbased_name = NULL; /* preset in case we fail */ /* Get the client's gss authenticated name */ maj_stat = gss_display_name(&min_stat, client_name, &name, &name_type); if (maj_stat != GSS_S_COMPLETE) { pgsserr("get_hostbased_client_name: gss_display_name", maj_stat, min_stat, mech); goto out_err; } if (name.length >= 0xffff) { /* don't overflow */ printerr(0, "ERROR: get_hostbased_client_name: " "received gss_name is too long (%d bytes)\n", name.length); goto out_rel_buf; } /* For Kerberos, transform the NT_KRB5_PRINCIPAL name to * an NT_HOSTBASED_SERVICE name */ if (g_OID_equal(&krb5oid, mech)) { if (get_krb5_hostbased_name(&name, &cname) == 0) *hostbased_name = cname; } /* No support for SPKM3, just print a warning (for now) */ if (g_OID_equal(&spkm3oid, mech)) { printerr(1, "WARNING: get_hostbased_client_name: " "no hostbased_name support for SPKM3\n"); } res = 0; out_rel_buf: gss_release_buffer(&min_stat, &name); out_err: return res; }
int serialize_context_for_kernel(gss_ctx_id_t ctx, gss_buffer_desc *buf, gss_OID mech, int32_t *endtime) { if (g_OID_equal(&krb5oid, mech)) return serialize_krb5_ctx(ctx, buf, endtime); #ifdef HAVE_SPKM3_H else if (g_OID_equal(&spkm3oid, mech)) return serialize_spkm3_ctx(ctx, buf, endtime); #endif else { printerr(0, "ERROR: attempting to serialize context with " "unknown/unsupported mechanism oid\n"); return -1; } }
/* * Find the Linux svcgssd downcall file name given the mechanism */ char * mech2file(gss_OID mech) { struct mech2file *m2fp = m2f; while(m2fp->mech.length != 0) { if (g_OID_equal(mech,&m2fp->mech)) return(m2fp->filename); m2fp++; } return NULL; }
OM_uint32 generic_gss_display_mech_attr( OM_uint32 *minor_status, gss_const_OID mech_attr, gss_buffer_t name, gss_buffer_t short_desc, gss_buffer_t long_desc) { size_t i; if (name != GSS_C_NO_BUFFER) { name->length = 0; name->value = NULL; } if (short_desc != GSS_C_NO_BUFFER) { short_desc->length = 0; short_desc->value = NULL; } if (long_desc != GSS_C_NO_BUFFER) { long_desc->length = 0; long_desc->value = NULL; } for (i = 0; i < sizeof(mech_attr_info)/sizeof(mech_attr_info[0]); i++) { struct mech_attr_info_desc *mai = &mech_attr_info[i]; if (g_OID_equal(mech_attr, mai->mech_attr)) { if (name != GSS_C_NO_BUFFER && !g_make_string_buffer((char *)mai->name.value, name)) { *minor_status = ENOMEM; return GSS_S_FAILURE; } if (short_desc != GSS_C_NO_BUFFER && !g_make_string_buffer((char *)mai->short_desc.value, short_desc)) { *minor_status = ENOMEM; return GSS_S_FAILURE; } if (long_desc != GSS_C_NO_BUFFER && !g_make_string_buffer((char *)mai->long_desc.value, long_desc)) { *minor_status = ENOMEM; return GSS_S_FAILURE; } return GSS_S_COMPLETE; } } return GSS_S_BAD_MECH_ATTR; }
/* * Naming extensions based local login authorization. */ static OM_uint32 attr_authorize_localname(OM_uint32 *minor, const gss_name_t name, const gss_union_name_t unionUser) { OM_uint32 major = GSS_S_UNAVAILABLE; /* attribute not present */ gss_buffer_t externalName; int more = -1; if (unionUser->name_type != GSS_C_NO_OID && !g_OID_equal(unionUser->name_type, GSS_C_NT_USER_NAME)) return (GSS_S_BAD_NAMETYPE); externalName = unionUser->external_name; assert(externalName != GSS_C_NO_BUFFER); while (more != 0 && major != GSS_S_COMPLETE) { OM_uint32 tmpMajor, tmpMinor; gss_buffer_desc value; gss_buffer_desc display_value; int authenticated = 0, complete = 0; tmpMajor = gss_get_name_attribute(minor, name, GSS_C_ATTR_LOCAL_LOGIN_USER, &authenticated, &complete, &value, &display_value, &more); if (GSS_ERROR(tmpMajor)) { major = tmpMajor; break; } if (authenticated && value.length == externalName->length && memcmp(value.value, externalName->value, externalName->length) == 0) major = GSS_S_COMPLETE; else major = GSS_S_UNAUTHORIZED; gss_release_buffer(&tmpMinor, &value); gss_release_buffer(&tmpMinor, &display_value); } return (major); }
/** * @brief Set Security Context Option * @ingroup globus_gsi_gssapi_extensions * @details * GSSAPI routine to initiate the sending of a security context * See: <draft-ietf-cat-gssv2-cbind-04.txt> * * @param minor_status * @param context_handle * @param option * @param value * * @return */ OM_uint32 GSS_CALLCONV gss_set_sec_context_option( OM_uint32 * minor_status, gss_ctx_id_t * context_handle, const gss_OID option, const gss_buffer_t value) { gss_ctx_id_desc * context = NULL; OM_uint32 major_status = GSS_S_COMPLETE; OM_uint32 local_minor_status; globus_result_t local_result = GLOBUS_SUCCESS; int index; GLOBUS_I_GSI_GSSAPI_DEBUG_ENTER; if(minor_status == NULL) { major_status = GSS_S_FAILURE; goto exit; } *minor_status = (OM_uint32) GLOBUS_SUCCESS; if(context_handle == NULL) { GLOBUS_GSI_GSSAPI_ERROR_RESULT( minor_status, GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT, (_GGSL("Invalid context_handle passed to function - cannot be NULL"))); major_status = GSS_S_FAILURE; goto exit; } context = *context_handle; if(option == GSS_C_NO_OID) { GLOBUS_GSI_GSSAPI_ERROR_RESULT( minor_status, GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT, (_GGSL("Invalid option passed to function with value: GSS_C_NO_OID"))); major_status = GSS_S_FAILURE; goto exit; } if (*context_handle == GSS_C_NO_CONTEXT) { /* for now just malloc and zero the context */ context = (gss_ctx_id_desc *) malloc(sizeof(gss_ctx_id_desc)); if (context == NULL) { GLOBUS_GSI_GSSAPI_MALLOC_ERROR(minor_status); major_status = GSS_S_FAILURE; goto exit; } *context_handle = context; memset(context, 0, sizeof(gss_ctx_id_desc)); context->ctx_flags = 0; major_status = gss_create_empty_oid_set( &local_minor_status, (gss_OID_set *) &context->extension_oids); /* initialize the callback_data in the context. This needs * to be done so the verify_callback func can be set later. */ local_result = globus_gsi_callback_data_init( &context->callback_data); if(local_result != GLOBUS_SUCCESS) { GLOBUS_GSI_GSSAPI_ERROR_RESULT( minor_status, GLOBUS_GSI_GSSAPI_ERROR_WITH_GSS_CONTEXT, (_GGSL("The callback data in the context " "could not be initialized."))); major_status = GSS_S_FAILURE; goto exit; } } else if(context->ctx_flags & GSS_I_CTX_INITIALIZED) { GLOBUS_GSI_GSSAPI_ERROR_RESULT( minor_status, GLOBUS_GSI_GSSAPI_ERROR_WITH_GSS_CONTEXT, (_GGSL("The context has already been initialized! %s should be " "called on a context before initialization"), __func__)); major_status = GSS_S_FAILURE; goto exit; } if(g_OID_equal(option, GSS_DISALLOW_ENCRYPTION)) { context->ctx_flags |= GSS_I_DISALLOW_ENCRYPTION; } else if(g_OID_equal(option, GSS_PROTECTION_FAIL_ON_CONTEXT_EXPIRATION)) { context->ctx_flags |= GSS_I_PROTECTION_FAIL_ON_CONTEXT_EXPIRATION; } else if(g_OID_equal(option, GSS_APPLICATION_WILL_HANDLE_EXTENSIONS)) { if(value == GSS_C_NO_BUFFER) { GLOBUS_GSI_GSSAPI_ERROR_RESULT( minor_status, GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT, (_GGSL("Invalid buffer passed to function"))); major_status = GSS_S_FAILURE; goto exit; } for(index = 0; index < ((gss_OID_set_desc *) value->value)->count; index++) { major_status = gss_add_oid_set_member( &local_minor_status, (gss_OID) &((gss_OID_set_desc *) value->value)->elements[index], (gss_OID_set *) &context->extension_oids); if(GSS_ERROR(major_status)) { GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT( minor_status, local_minor_status, GLOBUS_GSI_GSSAPI_ERROR_WITH_OID); goto exit; } } local_result = globus_gsi_callback_set_extension_cb( context->callback_data, globus_i_gsi_gss_verify_extensions_callback); if(local_result != GLOBUS_SUCCESS) { GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT( minor_status, local_result, GLOBUS_GSI_GSSAPI_ERROR_WITH_CALLBACK_DATA); major_status = GSS_S_FAILURE; goto exit; } /* if the extension_oids are set, * then we set them in the callback data */ local_result = globus_gsi_callback_set_extension_oids( context->callback_data, (void *) context->extension_oids); if(local_result != GLOBUS_SUCCESS) { GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT( minor_status, local_result, GLOBUS_GSI_GSSAPI_ERROR_WITH_CALLBACK_DATA); major_status = GSS_S_FAILURE; goto exit; } context->ctx_flags |= GSS_I_APPLICATION_WILL_HANDLE_EXTENSIONS; } else { /* unknown option */ GLOBUS_GSI_GSSAPI_ERROR_RESULT( minor_status, GLOBUS_GSI_GSSAPI_ERROR_UNKNOWN_OPTION, (NULL)); major_status = GSS_S_FAILURE; goto exit; } *context_handle = context; exit: GLOBUS_I_GSI_GSSAPI_DEBUG_EXIT; return major_status; }
static bool_t svcauth_gss_accept_sec_context(struct svc_req *rqst, struct rpc_gss_init_res *gr) { struct svc_rpc_gss_data *gd; struct rpc_gss_cred *gc; gss_buffer_desc recv_tok, seqbuf; gss_OID mech; OM_uint32 maj_stat = 0, min_stat = 0, ret_flags, seq; log_debug("in svcauth_gss_accept_context()"); gd = SVCAUTH_PRIVATE(rqst->rq_xprt->xp_auth); gc = (struct rpc_gss_cred *)rqst->rq_clntcred; memset(gr, 0, sizeof(*gr)); /* Deserialize arguments. */ memset(&recv_tok, 0, sizeof(recv_tok)); if (!svc_getargs(rqst->rq_xprt, xdr_rpc_gss_init_args, (caddr_t)&recv_tok)) return (FALSE); gr->gr_major = gss_accept_sec_context(&gr->gr_minor, &gd->ctx, svcauth_gss_creds, &recv_tok, GSS_C_NO_CHANNEL_BINDINGS, &gd->client_name, &mech, &gr->gr_token, &ret_flags, NULL, NULL); svc_freeargs(rqst->rq_xprt, xdr_rpc_gss_init_args, (caddr_t)&recv_tok); log_status("accept_sec_context", gr->gr_major, gr->gr_minor); if (gr->gr_major != GSS_S_COMPLETE && gr->gr_major != GSS_S_CONTINUE_NEEDED) { badauth(gr->gr_major, gr->gr_minor, rqst->rq_xprt); gd->ctx = GSS_C_NO_CONTEXT; goto errout; } gr->gr_ctx.value = "xxxx"; gr->gr_ctx.length = 4; /* gr->gr_win = 0x00000005; ANDROS: for debugging linux kernel version... */ gr->gr_win = sizeof(gd->seqmask) * 8; /* Save client info. */ gd->sec.mech = mech; gd->sec.qop = GSS_C_QOP_DEFAULT; gd->sec.svc = gc->gc_svc; gd->seq = gc->gc_seq; gd->win = gr->gr_win; if (gr->gr_major == GSS_S_COMPLETE) { #ifdef SPKM /* spkm3: no src_name (anonymous) */ if(!g_OID_equal(gss_mech_spkm3, mech)) { #endif maj_stat = gss_display_name(&min_stat, gd->client_name, &gd->cname, &gd->sec.mech); #ifdef SPKM } #endif if (maj_stat != GSS_S_COMPLETE) { log_status("display_name", maj_stat, min_stat); goto errout; } #ifdef DEBUG #ifdef HAVE_HEIMDAL log_debug("accepted context for %.*s with " "<mech {}, qop %d, svc %d>", gd->cname.length, (char *)gd->cname.value, gd->sec.qop, gd->sec.svc); #else { gss_buffer_desc mechname; gss_oid_to_str(&min_stat, mech, &mechname); log_debug("accepted context for %.*s with " "<mech %.*s, qop %d, svc %d>", gd->cname.length, (char *)gd->cname.value, mechname.length, (char *)mechname.value, gd->sec.qop, gd->sec.svc); gss_release_buffer(&min_stat, &mechname); } #endif #endif /* DEBUG */ seq = htonl(gr->gr_win); seqbuf.value = &seq; seqbuf.length = sizeof(seq); gss_release_buffer(&min_stat, &gd->checksum); maj_stat = gss_sign(&min_stat, gd->ctx, GSS_C_QOP_DEFAULT, &seqbuf, &gd->checksum); if (maj_stat != GSS_S_COMPLETE) { goto errout; } rqst->rq_xprt->xp_verf.oa_flavor = RPCSEC_GSS; rqst->rq_xprt->xp_verf.oa_base = gd->checksum.value; rqst->rq_xprt->xp_verf.oa_length = gd->checksum.length; } return (TRUE); errout: gss_release_buffer(&min_stat, &gr->gr_token); return (FALSE); }
/** * @brief Init Sec Context * @ingroup globus_gsi_gssapi */ OM_uint32 GSS_CALLCONV gss_init_sec_context( OM_uint32 * minor_status, const gss_cred_id_t initiator_cred_handle, gss_ctx_id_t * context_handle_P, 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) { gss_ctx_id_desc * context = NULL; OM_uint32 major_status = GSS_S_COMPLETE; OM_uint32 local_minor_status; OM_uint32 local_major_status; globus_result_t local_result; int rc; char cbuf[1]; globus_gsi_cert_utils_cert_type_t cert_type; GLOBUS_I_GSI_GSSAPI_DEBUG_ENTER; *minor_status = (OM_uint32) GLOBUS_SUCCESS; output_token->length = 0; context = *context_handle_P; /* module activation if not already done by calling * globus_module_activate */ globus_thread_once( &once_control, globus_l_gsi_gssapi_activate_once); globus_mutex_lock(&globus_i_gssapi_activate_mutex); if (!globus_i_gssapi_active) { globus_module_activate(GLOBUS_GSI_GSSAPI_MODULE); } globus_mutex_unlock(&globus_i_gssapi_activate_mutex); if(req_flags & GSS_C_ANON_FLAG && req_flags & GSS_C_DELEG_FLAG) { major_status = GSS_S_FAILURE; GLOBUS_GSI_GSSAPI_ERROR_RESULT( minor_status, GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT, (_GGSL("Can't initialize a context to be both anonymous and " "provide delegation"))); goto error_exit; } if(req_flags & GSS_C_GLOBUS_SSL_COMPATIBLE && req_flags & GSS_C_DELEG_FLAG) { major_status = GSS_S_FAILURE; GLOBUS_GSI_GSSAPI_ERROR_RESULT( minor_status, GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT, (_GGSL("Can't initialize a context to both use SSL compatible " "context establishment and provide delegation"))); goto error_exit; } if(req_flags & GSS_C_DELEG_FLAG && target_name == GSS_C_NO_NAME) { major_status = GSS_S_FAILURE; GLOBUS_GSI_GSSAPI_ERROR_RESULT( minor_status, GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT, (_GGSL("Need a target name for authorization prior " "to doing delegation"))); goto error_exit; } if ((context == (gss_ctx_id_t) GSS_C_NO_CONTEXT) || !(context->ctx_flags & GSS_I_CTX_INITIALIZED)) { GLOBUS_I_GSI_GSSAPI_DEBUG_FPRINTF( 2, (globus_i_gsi_gssapi_debug_fstream, "Creating context w/ %s.\n", (initiator_cred_handle == GSS_C_NO_CREDENTIAL) ? "GSS_C_NO_CREDENTIAL" : "Credentials provided")); major_status = globus_i_gsi_gss_create_and_fill_context(&local_minor_status, &context, initiator_cred_handle, GSS_C_INITIATE, req_flags); if (GSS_ERROR(major_status)) { GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT( minor_status, local_minor_status, GLOBUS_GSI_GSSAPI_ERROR_WITH_GSS_CONTEXT); goto error_exit; } *context_handle_P = context; if (actual_mech_type != NULL) { *actual_mech_type = (gss_OID) gss_mech_globus_gssapi_openssl; } if (ret_flags != NULL) { *ret_flags = 0 ; } } else { /* first time there is no input token, but after that * there will always be one */ major_status = globus_i_gsi_gss_put_token(&local_minor_status, context, NULL, input_token); if (GSS_ERROR(major_status)) { GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT( minor_status, local_minor_status, GLOBUS_GSI_GSSAPI_ERROR_TOKEN_FAIL); goto error_exit; } } switch (context->gss_state) { case(GSS_CON_ST_HANDSHAKE): /* do the handshake work */ major_status = globus_i_gsi_gss_handshake(&local_minor_status, context); if (major_status == GSS_S_CONTINUE_NEEDED) { break; } if(GSS_ERROR(major_status)) { GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT( minor_status, local_minor_status, GLOBUS_GSI_GSSAPI_ERROR_HANDSHAKE); context->gss_state = GSS_CON_ST_DONE; break; } /* make sure we are talking to the correct server */ major_status = globus_i_gsi_gss_retrieve_peer(&local_minor_status, context, GSS_C_INITIATE); if (GSS_ERROR(major_status)) { GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT( minor_status, local_minor_status, GLOBUS_GSI_GSSAPI_ERROR_WITH_GSS_CONTEXT); context->gss_state = GSS_CON_ST_DONE; break; } local_result = globus_gsi_callback_get_cert_type( context->callback_data, &cert_type); if(local_result != GLOBUS_SUCCESS) { GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT( minor_status, local_minor_status, GLOBUS_GSI_GSSAPI_ERROR_WITH_CALLBACK_DATA); major_status = GSS_S_FAILURE; goto error_exit; } /* * Need to check if the server is using a limited proxy. * And if that is acceptable here. * Caller tells us if it is not acceptable to * use a limited proxy. */ if ((context->req_flags & GSS_C_GLOBUS_DONT_ACCEPT_LIMITED_PROXY_FLAG) && GLOBUS_GSI_CERT_UTILS_IS_LIMITED_PROXY(cert_type)) { major_status = GSS_S_UNAUTHORIZED; GLOBUS_GSI_GSSAPI_ERROR_RESULT( minor_status, GLOBUS_GSI_GSSAPI_ERROR_PROXY_VIOLATION, (_GGSL("Function set to not accept limited proxies"))); context->gss_state = GSS_CON_ST_DONE; break; } /* this is the mutual authentication test */ if (target_name != NULL) { major_status = gss_compare_name(&local_minor_status, context->peer_cred_handle->globusid, target_name, &rc); if (GSS_ERROR(major_status)) { GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT( minor_status, local_minor_status, GLOBUS_GSI_GSSAPI_ERROR_BAD_NAME); context->gss_state = GSS_CON_ST_DONE; break; } else if(rc == GSS_NAMES_NOT_EQUAL) { char * expected_name; char * actual_name; if(g_OID_equal(((gss_name_desc*) target_name)->name_oid, GSS_C_NT_HOSTBASED_SERVICE)) { GLOBUS_GSI_GSSAPI_ERROR_RESULT( minor_status, GLOBUS_GSI_GSSAPI_ERROR_AUTHZ_DENIED, (_GGSL("The expected name for the remote host (%s%s%s) does not match the authenticated " "name of the remote host (%s%s%s). This happens when the name in the host certificate does not match the information obtained from DNS and is often a DNS configuration problem."), target_name->service_name ? target_name->service_name : "", target_name->service_name ? "@" : "", target_name->host_name ? target_name->host_name : "unknown", context->peer_cred_handle->globusid->service_name ? context->peer_cred_handle->globusid->service_name : "", context->peer_cred_handle->globusid->service_name ? "@" : "", context->peer_cred_handle->globusid->host_name ? context->peer_cred_handle->globusid->host_name : "unknown")); } else { expected_name = ((gss_name_desc*) target_name)->x509n_oneline; actual_name = ((gss_name_desc*) context->peer_cred_handle->globusid)->x509n_oneline; GLOBUS_GSI_GSSAPI_ERROR_RESULT( minor_status, GLOBUS_GSI_GSSAPI_ERROR_AUTHZ_DENIED, (_GGSL("The name of the remote entity (%s), and the expected " "name for the remote entity (%s) do not match"), actual_name, expected_name)); } major_status = GSS_S_UNAUTHORIZED; context->gss_state = GSS_CON_ST_DONE; break; } } context->ret_flags |= GSS_C_MUTUAL_FLAG; context->ret_flags |= GSS_C_PROT_READY_FLAG; context->ret_flags |= GSS_C_INTEG_FLAG | GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG | GSS_C_ANON_FLAG | GSS_C_DELEG_FLAG; if (GLOBUS_GSI_CERT_UTILS_IS_LIMITED_PROXY(cert_type)) { context->ret_flags |= GSS_C_GLOBUS_RECEIVED_LIMITED_PROXY_FLAG; } # if LINK_WITH_INTERNAL_OPENSSL_API context->ret_flags |= GSS_C_TRANS_FLAG; # endif /* * IF we are talking to a real SSL server, * we don't want to do delegation, so we are done */ if (context->req_flags & GSS_C_GLOBUS_SSL_COMPATIBLE) { context->gss_state = GSS_CON_ST_DONE; break; } /* * If we have completed the handshake, but don't * have any more data to send, we can send the flag * now. i.e. fall through without break, * Otherwise, we will wait for the null byte * to get back in sync which we will ignore */ if (output_token->length != 0) { context->gss_state=GSS_CON_ST_FLAGS; break; } case(GSS_CON_ST_FLAGS): if (input_token->length > 0) { BIO_read(context->gss_sslbio, cbuf, 1); } /* send D if we want delegation, 0 otherwise */ if (context->req_flags & GSS_C_DELEG_FLAG) { BIO_write(context->gss_sslbio, "D", 1); context->gss_state = GSS_CON_ST_REQ; } else { BIO_write(context->gss_sslbio, "0", 1); context->gss_state = GSS_CON_ST_DONE; } break; case(GSS_CON_ST_REQ): local_result = globus_gsi_proxy_inquire_req( context->proxy_handle, context->gss_sslbio); if(local_result != GLOBUS_SUCCESS) { GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT( minor_status, local_result, GLOBUS_GSI_GSSAPI_ERROR_PROXY_NOT_RECEIVED); major_status = GSS_S_FAILURE; context->gss_state = GSS_CON_ST_DONE; goto error_exit; } local_result = globus_gsi_cred_get_cert_type( context->cred_handle->cred_handle, &cert_type); if(local_result != GLOBUS_SUCCESS) { GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT( minor_status, local_result, GLOBUS_GSI_GSSAPI_ERROR_WITH_GSI_CREDENTIAL); major_status = GSS_S_FAILURE; context->gss_state = GSS_CON_ST_DONE; goto error_exit; } local_result = globus_gsi_proxy_handle_set_type( context->proxy_handle, (context->req_flags & GSS_C_GLOBUS_DELEGATE_LIMITED_PROXY_FLAG) ? GLOBUS_GSI_CERT_UTILS_TYPE_LIMITED_PROXY : GLOBUS_GSI_CERT_UTILS_TYPE_IMPERSONATION_PROXY); if(local_result != GLOBUS_SUCCESS) { GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT( minor_status, local_result, GLOBUS_GSI_GSSAPI_ERROR_WITH_GSI_PROXY); major_status = GSS_S_FAILURE; context->gss_state = GSS_CON_ST_DONE; goto exit; } local_result = globus_gsi_proxy_sign_req( context->proxy_handle, context->cred_handle->cred_handle, context->gss_sslbio); if(local_result != GLOBUS_SUCCESS) { GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT( minor_status, local_result, GLOBUS_GSI_GSSAPI_ERROR_WITH_GSI_PROXY); major_status = GSS_S_FAILURE; context->gss_state = GSS_CON_ST_DONE; goto error_exit; } context->gss_state = GSS_CON_ST_DONE; break; case(GSS_CON_ST_CERT): ; case(GSS_CON_ST_DONE): ; } /* end of switch for gss_con_st */ local_major_status = globus_i_gsi_gss_get_token(&local_minor_status, context, NULL, output_token); if(GSS_ERROR(local_major_status)) { GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT( minor_status, local_minor_status, GLOBUS_GSI_GSSAPI_ERROR_TOKEN_FAIL); major_status = GSS_S_FAILURE; context->gss_state = GSS_CON_ST_DONE; goto error_exit; } /* some error occurred during switch */ if(GSS_ERROR(major_status)) { goto error_exit; } if (context->gss_state != GSS_CON_ST_DONE) { major_status |= GSS_S_CONTINUE_NEEDED; } else if(time_rec != NULL) { time_t lifetime; time_t current_time; major_status = globus_i_gsi_gss_get_context_goodtill( &local_minor_status, context, &lifetime); if(GSS_ERROR(major_status)) { GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT( minor_status, local_minor_status, GLOBUS_GSI_GSSAPI_ERROR_WITH_GSS_CONTEXT); goto exit; } current_time = time(NULL); if(current_time > lifetime) { *time_rec = 0; } else { *time_rec = (OM_uint32) (lifetime - current_time); } } if (ret_flags != NULL) { *ret_flags = context->ret_flags; } GLOBUS_I_GSI_GSSAPI_DEBUG_FPRINTF( 2, (globus_i_gsi_gssapi_debug_fstream, "init_sec_context:major_status:%08x" ":gss_state:%d req_flags=%08x:ret_flags=%08x\n", (unsigned int) major_status, context->gss_state, (unsigned int) req_flags, (unsigned int) context->ret_flags)); goto exit; error_exit: gss_delete_sec_context(&local_minor_status, (gss_ctx_id_t *) &context, output_token); *context_handle_P = (gss_ctx_id_t) context; exit: GLOBUS_I_GSI_GSSAPI_DEBUG_EXIT; return major_status; }
static OM_uint32 kg_unseal_v1_iov(krb5_context context, OM_uint32 *minor_status, krb5_gss_ctx_id_rec *ctx, gss_iov_buffer_desc *iov, int iov_count, size_t token_wrapper_len, int *conf_state, gss_qop_t *qop_state, int toktype) { OM_uint32 code; gss_iov_buffer_t header; gss_iov_buffer_t trailer; unsigned char *ptr; int sealalg; int signalg; krb5_checksum cksum; krb5_checksum md5cksum; size_t cksum_len = 0; size_t conflen = 0; int direction; krb5_ui_4 seqnum; OM_uint32 retval; size_t sumlen; krb5_keyusage sign_usage = KG_USAGE_SIGN; md5cksum.length = cksum.length = 0; md5cksum.contents = cksum.contents = NULL; header = kg_locate_header_iov(iov, iov_count, toktype); assert(header != NULL); trailer = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER); if (trailer != NULL && trailer->buffer.length != 0) { *minor_status = (OM_uint32)KRB5_BAD_MSIZE; return GSS_S_DEFECTIVE_TOKEN; } if (header->buffer.length < token_wrapper_len + 14) { *minor_status = 0; return GSS_S_DEFECTIVE_TOKEN; } ptr = (unsigned char *)header->buffer.value + token_wrapper_len; signalg = ptr[0]; signalg |= ptr[1] << 8; sealalg = ptr[2]; sealalg |= ptr[3] << 8; if (ptr[4] != 0xFF || ptr[5] != 0xFF) { *minor_status = 0; return GSS_S_DEFECTIVE_TOKEN; } if (toktype != KG_TOK_WRAP_MSG && sealalg != 0xFFFF) { *minor_status = 0; return GSS_S_DEFECTIVE_TOKEN; } if (toktype == KG_TOK_WRAP_MSG && !(sealalg == 0xFFFF || sealalg == ctx->sealalg)) { *minor_status = 0; return GSS_S_DEFECTIVE_TOKEN; } if ((ctx->sealalg == SEAL_ALG_NONE && signalg > 1) || (ctx->sealalg == SEAL_ALG_1 && signalg != SGN_ALG_3) || (ctx->sealalg == SEAL_ALG_DES3KD && signalg != SGN_ALG_HMAC_SHA1_DES3_KD)|| (ctx->sealalg == SEAL_ALG_MICROSOFT_RC4 && signalg != SGN_ALG_HMAC_MD5)) { *minor_status = 0; return GSS_S_DEFECTIVE_TOKEN; } switch (signalg) { case SGN_ALG_DES_MAC_MD5: case SGN_ALG_MD2_5: case SGN_ALG_HMAC_MD5: cksum_len = 8; if (toktype != KG_TOK_WRAP_MSG) sign_usage = 15; break; case SGN_ALG_3: cksum_len = 16; break; case SGN_ALG_HMAC_SHA1_DES3_KD: cksum_len = 20; break; default: *minor_status = 0; return GSS_S_DEFECTIVE_TOKEN; } /* get the token parameters */ code = kg_get_seq_num(context, ctx->seq, ptr + 14, ptr + 6, &direction, &seqnum); if (code != 0) { *minor_status = code; return GSS_S_BAD_SIG; } /* decode the message, if SEAL */ if (toktype == KG_TOK_WRAP_MSG) { if (sealalg != 0xFFFF) { if (ctx->sealalg == SEAL_ALG_MICROSOFT_RC4) { unsigned char bigend_seqnum[4]; krb5_keyblock *enc_key; size_t i; store_32_be(seqnum, bigend_seqnum); code = krb5_k_key_keyblock(context, ctx->enc, &enc_key); if (code != 0) { retval = GSS_S_FAILURE; goto cleanup; } assert(enc_key->length == 16); for (i = 0; i < enc_key->length; i++) ((char *)enc_key->contents)[i] ^= 0xF0; code = kg_arcfour_docrypt_iov(context, enc_key, 0, &bigend_seqnum[0], 4, iov, iov_count); krb5_free_keyblock(context, enc_key); } else { code = kg_decrypt_iov(context, 0, ((ctx->gss_flags & GSS_C_DCE_STYLE) != 0), 0 /*EC*/, 0 /*RRC*/, ctx->enc, KG_USAGE_SEAL, NULL, iov, iov_count); } if (code != 0) { retval = GSS_S_FAILURE; goto cleanup; } } conflen = kg_confounder_size(context, ctx->enc->keyblock.enctype); } if (header->buffer.length != token_wrapper_len + 14 + cksum_len + conflen) { retval = GSS_S_DEFECTIVE_TOKEN; goto cleanup; } /* compute the checksum of the message */ /* initialize the checksum */ switch (signalg) { case SGN_ALG_DES_MAC_MD5: case SGN_ALG_MD2_5: case SGN_ALG_DES_MAC: case SGN_ALG_3: md5cksum.checksum_type = CKSUMTYPE_RSA_MD5; break; case SGN_ALG_HMAC_MD5: md5cksum.checksum_type = CKSUMTYPE_HMAC_MD5_ARCFOUR; break; case SGN_ALG_HMAC_SHA1_DES3_KD: md5cksum.checksum_type = CKSUMTYPE_HMAC_SHA1_DES3; break; default: abort(); } code = krb5_c_checksum_length(context, md5cksum.checksum_type, &sumlen); if (code != 0) { retval = GSS_S_FAILURE; goto cleanup; } md5cksum.length = sumlen; /* compute the checksum of the message */ code = kg_make_checksum_iov_v1(context, md5cksum.checksum_type, cksum_len, ctx->seq, ctx->enc, sign_usage, iov, iov_count, toktype, &md5cksum); if (code != 0) { retval = GSS_S_FAILURE; goto cleanup; } switch (signalg) { case SGN_ALG_DES_MAC_MD5: case SGN_ALG_3: code = kg_encrypt_inplace(context, ctx->seq, KG_USAGE_SEAL, (g_OID_equal(ctx->mech_used, gss_mech_krb5_old) ? ctx->seq->keyblock.contents : NULL), md5cksum.contents, 16); if (code != 0) { retval = GSS_S_FAILURE; goto cleanup; } cksum.length = cksum_len; cksum.contents = md5cksum.contents + 16 - cksum.length; code = k5_bcmp(cksum.contents, ptr + 14, cksum.length); break; case SGN_ALG_HMAC_SHA1_DES3_KD: case SGN_ALG_HMAC_MD5: code = k5_bcmp(md5cksum.contents, ptr + 14, cksum_len); break; default: code = 0; retval = GSS_S_DEFECTIVE_TOKEN; goto cleanup; break; } if (code != 0) { code = 0; retval = GSS_S_BAD_SIG; goto cleanup; } /* * For GSS_C_DCE_STYLE, the caller manages the padding, because the * pad length is in the RPC PDU. The value of the padding may be * uninitialized. For normal GSS, the last bytes of the decrypted * data contain the pad length. kg_fixup_padding_iov() will find * this and fixup the last data IOV appropriately. */ if (toktype == KG_TOK_WRAP_MSG && (ctx->gss_flags & GSS_C_DCE_STYLE) == 0) { retval = kg_fixup_padding_iov(&code, iov, iov_count); if (retval != GSS_S_COMPLETE) goto cleanup; } if (conf_state != NULL) *conf_state = (sealalg != 0xFFFF); if (qop_state != NULL) *qop_state = GSS_C_QOP_DEFAULT; if ((ctx->initiate && direction != 0xff) || (!ctx->initiate && direction != 0)) { *minor_status = (OM_uint32)G_BAD_DIRECTION; retval = GSS_S_BAD_SIG; } code = 0; retval = g_order_check(&ctx->seqstate, (gssint_uint64)seqnum); cleanup: krb5_free_checksum_contents(context, &md5cksum); *minor_status = code; return retval; }
static krb5_error_code make_seal_token_v1_iov(krb5_context context, krb5_gss_ctx_id_rec *ctx, int conf_req_flag, int *conf_state, gss_iov_buffer_desc *iov, int iov_count, int toktype) { krb5_error_code code = 0; gss_iov_buffer_t header; gss_iov_buffer_t padding; gss_iov_buffer_t trailer; krb5_checksum md5cksum; krb5_checksum cksum; size_t k5_headerlen = 0, k5_trailerlen = 0; size_t data_length = 0, assoc_data_length = 0; size_t tmsglen = 0, tlen; unsigned char *ptr; krb5_keyusage sign_usage = KG_USAGE_SIGN; assert(toktype == KG_TOK_WRAP_MSG); md5cksum.length = cksum.length = 0; md5cksum.contents = cksum.contents = NULL; header = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_HEADER); if (header == NULL) return EINVAL; padding = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING); if (padding == NULL && (ctx->gss_flags & GSS_C_DCE_STYLE) == 0) return EINVAL; trailer = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER); if (trailer != NULL) trailer->buffer.length = 0; /* Determine confounder length */ if (toktype == KG_TOK_WRAP_MSG || conf_req_flag) k5_headerlen = kg_confounder_size(context, ctx->enc); /* Check padding length */ if (toktype == KG_TOK_WRAP_MSG) { size_t k5_padlen = (ctx->sealalg == SEAL_ALG_MICROSOFT_RC4) ? 1 : 8; size_t gss_padlen; size_t conf_data_length; kg_iov_msglen(iov, iov_count, &data_length, &assoc_data_length); conf_data_length = k5_headerlen + data_length - assoc_data_length; if (k5_padlen == 1) gss_padlen = 1; /* one byte to indicate one byte of padding */ else gss_padlen = k5_padlen - (conf_data_length % k5_padlen); if (ctx->gss_flags & GSS_C_DCE_STYLE) { /* DCE will pad the actual data itself; padding buffer optional and will be zeroed */ gss_padlen = 0; if (conf_data_length % k5_padlen) code = KRB5_BAD_MSIZE; } else if (padding->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) { code = kg_allocate_iov(padding, gss_padlen); } else if (padding->buffer.length < gss_padlen) { code = KRB5_BAD_MSIZE; } if (code != 0) goto cleanup; /* Initialize padding buffer to pad itself */ if (padding != NULL) { padding->buffer.length = gss_padlen; memset(padding->buffer.value, (int)gss_padlen, gss_padlen); } if (ctx->gss_flags & GSS_C_DCE_STYLE) tmsglen = k5_headerlen; /* confounder length */ else tmsglen = conf_data_length + padding->buffer.length + assoc_data_length; } /* Determine token size */ tlen = g_token_size(ctx->mech_used, 14 + ctx->cksum_size + tmsglen); k5_headerlen += tlen - tmsglen; if (header->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) code = kg_allocate_iov(header, k5_headerlen); else if (header->buffer.length < k5_headerlen) code = KRB5_BAD_MSIZE; if (code != 0) goto cleanup; header->buffer.length = k5_headerlen; ptr = (unsigned char *)header->buffer.value; g_make_token_header(ctx->mech_used, 14 + ctx->cksum_size + tmsglen, &ptr, toktype); /* 0..1 SIGN_ALG */ store_16_le(ctx->signalg, &ptr[0]); /* 2..3 SEAL_ALG or Filler */ if (toktype == KG_TOK_WRAP_MSG && conf_req_flag) { store_16_le(ctx->sealalg, &ptr[2]); } else { /* No seal */ ptr[2] = 0xFF; ptr[3] = 0xFF; } /* 4..5 Filler */ ptr[4] = 0xFF; ptr[5] = 0xFF; /* pad the plaintext, encrypt if needed, and stick it in the token */ /* initialize the checksum */ switch (ctx->signalg) { case SGN_ALG_DES_MAC_MD5: case SGN_ALG_MD2_5: md5cksum.checksum_type = CKSUMTYPE_RSA_MD5; break; case SGN_ALG_HMAC_SHA1_DES3_KD: md5cksum.checksum_type = CKSUMTYPE_HMAC_SHA1_DES3; break; case SGN_ALG_HMAC_MD5: md5cksum.checksum_type = CKSUMTYPE_HMAC_MD5_ARCFOUR; if (toktype != KG_TOK_WRAP_MSG) sign_usage = 15; break; default: case SGN_ALG_DES_MAC: abort (); } code = krb5_c_checksum_length(context, md5cksum.checksum_type, &k5_trailerlen); if (code != 0) goto cleanup; md5cksum.length = k5_trailerlen; if (k5_headerlen != 0) { code = kg_make_confounder(context, ctx->enc, ptr + 14 + ctx->cksum_size); if (code != 0) goto cleanup; } /* compute the checksum */ code = kg_make_checksum_iov_v1(context, md5cksum.checksum_type, ctx->cksum_size, ctx->seq, ctx->enc, sign_usage, iov, iov_count, toktype, &md5cksum); if (code != 0) goto cleanup; switch (ctx->signalg) { case SGN_ALG_DES_MAC_MD5: case SGN_ALG_3: code = kg_encrypt(context, ctx->seq, KG_USAGE_SEAL, (g_OID_equal(ctx->mech_used, gss_mech_krb5_old) ? ctx->seq->contents : NULL), md5cksum.contents, md5cksum.contents, 16); if (code != 0) goto cleanup; cksum.length = ctx->cksum_size; cksum.contents = md5cksum.contents + 16 - cksum.length; memcpy(ptr + 14, cksum.contents, cksum.length); break; case SGN_ALG_HMAC_SHA1_DES3_KD: assert(md5cksum.length == ctx->cksum_size); memcpy(ptr + 14, md5cksum.contents, md5cksum.length); break; case SGN_ALG_HMAC_MD5: memcpy(ptr + 14, md5cksum.contents, ctx->cksum_size); break; } /* create the seq_num */ code = kg_make_seq_num(context, ctx->seq, ctx->initiate ? 0 : 0xFF, (OM_uint32)ctx->seq_send, ptr + 14, ptr + 6); if (code != 0) goto cleanup; if (conf_req_flag) { if (ctx->sealalg == SEAL_ALG_MICROSOFT_RC4) { unsigned char bigend_seqnum[4]; krb5_keyblock *enc_key; size_t i; store_32_be(ctx->seq_send, bigend_seqnum); code = krb5_copy_keyblock(context, ctx->enc, &enc_key); if (code != 0) goto cleanup; assert(enc_key->length == 16); for (i = 0; i < enc_key->length; i++) ((char *)enc_key->contents)[i] ^= 0xF0; code = kg_arcfour_docrypt_iov(context, enc_key, 0, bigend_seqnum, 4, iov, iov_count); krb5_free_keyblock(context, enc_key); } else { code = kg_encrypt_iov(context, ctx->proto, ((ctx->gss_flags & GSS_C_DCE_STYLE) != 0), 0 /*EC*/, 0 /*RRC*/, ctx->enc, KG_USAGE_SEAL, NULL, iov, iov_count); } if (code != 0) goto cleanup; } ctx->seq_send++; ctx->seq_send &= 0xFFFFFFFFL; code = 0; if (conf_state != NULL) *conf_state = conf_req_flag; cleanup: if (code != 0) kg_release_iov(iov, iov_count); krb5_free_checksum_contents(context, &md5cksum); return code; }
u32 spkm3_make_token(struct spkm3_ctx *ctx, struct xdr_buf * text, struct xdr_netobj * token, int toktype) { s32 checksum_type; char tokhdrbuf[25]; char cksumdata[16]; struct xdr_netobj md5cksum = {.len = 0, .data = cksumdata}; struct xdr_netobj mic_hdr = {.len = 0, .data = tokhdrbuf}; int tokenlen = 0; unsigned char *ptr; s32 now; int ctxelen = 0, ctxzbit = 0; int md5elen = 0, md5zbit = 0; now = jiffies; if (ctx->ctx_id.len != 16) { dprintk("RPC: spkm3_make_token BAD ctx_id.len %d\n", ctx->ctx_id.len); goto out_err; } if (!g_OID_equal(&ctx->intg_alg, &hmac_md5_oid)) { dprintk("RPC: gss_spkm3_seal: unsupported I-ALG " "algorithm. only support hmac-md5 I-ALG.\n"); goto out_err; } else checksum_type = CKSUMTYPE_HMAC_MD5; if (!g_OID_equal(&ctx->conf_alg, &cast5_cbc_oid)) { dprintk("RPC: gss_spkm3_seal: unsupported C-ALG " "algorithm\n"); goto out_err; } if (toktype == SPKM_MIC_TOK) { /* Calculate checksum over the mic-header */ asn1_bitstring_len(&ctx->ctx_id, &ctxelen, &ctxzbit); spkm3_mic_header(&mic_hdr.data, &mic_hdr.len, ctx->ctx_id.data, ctxelen, ctxzbit); if (make_spkm3_checksum(checksum_type, &ctx->derived_integ_key, (char *)mic_hdr.data, mic_hdr.len, text, 0, &md5cksum)) goto out_err; asn1_bitstring_len(&md5cksum, &md5elen, &md5zbit); tokenlen = 10 + ctxelen + 1 + md5elen + 1; /* Create token header using generic routines */ token->len = g_token_size(&ctx->mech_used, tokenlen); ptr = token->data; g_make_token_header(&ctx->mech_used, tokenlen, &ptr); spkm3_make_mic_token(&ptr, tokenlen, &mic_hdr, &md5cksum, md5elen, md5zbit); } else if (toktype == SPKM_WRAP_TOK) { /* Not Supported */ dprintk("RPC: gss_spkm3_seal: SPKM_WRAP_TOK " "not supported\n"); goto out_err; } /* XXX need to implement sequence numbers, and ctx->expired */ return GSS_S_COMPLETE; out_err: token->data = NULL; token->len = 0; return GSS_S_FAILURE; } static int spkm3_checksummer(struct scatterlist *sg, void *data) { struct hash_desc *desc = data; return crypto_hash_update(desc, sg, sg->length); } /* checksum the plaintext data and hdrlen bytes of the token header */ s32 make_spkm3_checksum(s32 cksumtype, struct xdr_netobj *key, char *header, unsigned int hdrlen, struct xdr_buf *body, unsigned int body_offset, struct xdr_netobj *cksum) { char *cksumname; struct hash_desc desc; /* XXX add to ctx? */ struct scatterlist sg[1]; int err; switch (cksumtype) { case CKSUMTYPE_HMAC_MD5: cksumname = "hmac(md5)"; break; default: dprintk("RPC: spkm3_make_checksum:" " unsupported checksum %d", cksumtype); return GSS_S_FAILURE; } if (key->data == NULL || key->len <= 0) return GSS_S_FAILURE; desc.tfm = crypto_alloc_hash(cksumname, 0, CRYPTO_ALG_ASYNC); if (IS_ERR(desc.tfm)) return GSS_S_FAILURE; cksum->len = crypto_hash_digestsize(desc.tfm); desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP; err = crypto_hash_setkey(desc.tfm, key->data, key->len); if (err) goto out; err = crypto_hash_init(&desc); if (err) goto out; sg_set_buf(sg, header, hdrlen); crypto_hash_update(&desc, sg, sg->length); xdr_process_buf(body, body_offset, body->len - body_offset, spkm3_checksummer, &desc); crypto_hash_final(&desc, cksum->data); out: crypto_free_hash(desc.tfm); return err ? GSS_S_FAILURE : 0; }
/** * @brief Initiate Delegation * @ingroup globus_gsi_gssapi_extensions_delegation * @details * This functions drives the initiating side of the credential * delegation process. It is expected to be called in tandem with the * gss_accept_delegation function. * * @param minor_status * The minor status returned by this function. This parameter * will be 0 upon success. * @param context_handle * The security context over which the credential is * delegated. * @param cred_handle * The credential to be delegated. May be GSS_C_NO_CREDENTIAL * in which case the credential associated with the security * context is used. * @param desired_mech * The desired security mechanism. Currently not used. May be * GSS_C_NO_OID. * @param extension_oids * A set of extension OIDs corresponding to buffers in the * extension_buffers parameter below. The extensions specified * will be added to the delegated credential. May be * GSS_C_NO_BUFFER_SET. * @param extension_buffers * A set of extension buffers corresponding to OIDs in the * extension_oids parameter above. May be * GSS_C_NO_BUFFER_SET. * @param input_token * The token that was produced by a prior call to * gss_accept_delegation. This parameter will be ignored the * first time this function is called. * @param req_flags * Flags that modify the behavior of the function. Currently * only GSS_C_GLOBUS_SSL_COMPATIBLE and * GSS_C_GLOBUS_LIMITED_DELEG_PROXY_FLAG are checked for. The * GSS_C_GLOBUS_SSL_COMPATIBLE flag results in tokens that * aren't wrapped and GSS_C_GLOBUS_LIMITED_DELEG_PROXY_FLAG * causes the delegated proxy to be limited (requires that no * extensions are specified. * * @param time_req * The requested period of validity (seconds) of the delegated * credential. Passing a time_req of 0 cause the delegated credential to * have the same lifetime as the credential that issued it. * @param output_token * A token that should be passed to gss_accept_delegation if the * return value is GSS_S_CONTINUE_NEEDED. * @retval GSS_S_COMPLETE Success * @retval GSS_S_CONTINUE_NEEDED This function needs to be called again. * @retval GSS_S_FAILURE upon failure */ OM_uint32 GSS_CALLCONV gss_init_delegation( OM_uint32 * minor_status, const gss_ctx_id_t context_handle, const gss_cred_id_t cred_handle, const gss_OID desired_mech, const gss_OID_set extension_oids, const gss_buffer_set_t extension_buffers, const gss_buffer_t input_token, OM_uint32 req_flags, OM_uint32 time_req, gss_buffer_t output_token) { BIO * bio = NULL; BIO * read_bio = NULL; BIO * write_bio = NULL; OM_uint32 major_status = GSS_S_COMPLETE; OM_uint32 local_minor_status; gss_ctx_id_desc * context; gss_cred_id_desc * cred; X509 * cert = NULL; STACK_OF(X509) * cert_chain = NULL; PROXYCERTINFO * pci; globus_gsi_cert_utils_cert_type_t cert_type; int index; globus_result_t local_result = GLOBUS_SUCCESS; static char * _function_name_ = "init_delegation"; GLOBUS_I_GSI_GSSAPI_DEBUG_ENTER; if(minor_status == NULL) { major_status = GSS_S_FAILURE; goto exit; } *minor_status = (OM_uint32) GLOBUS_SUCCESS; if(context_handle == GSS_C_NO_CONTEXT) { GLOBUS_GSI_GSSAPI_ERROR_RESULT( minor_status, GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT, (_GGSL("Invalid context_handle passed to function"))); major_status = GSS_S_FAILURE; goto exit; } context = (gss_ctx_id_desc *) context_handle; cred = (gss_cred_id_desc *) cred_handle; if (cred_handle == GSS_C_NO_CREDENTIAL) { cred = (gss_cred_id_desc *) context->cred_handle; } if(cred == GSS_C_NO_CREDENTIAL) { GLOBUS_GSI_GSSAPI_ERROR_RESULT( minor_status, GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT, (_GGSL("Couldn't initialize delegation credential handle"))); major_status = GSS_S_FAILURE; goto exit; } if(desired_mech != GSS_C_NO_OID && desired_mech != (gss_OID) gss_mech_globus_gssapi_openssl) { GLOBUS_GSI_GSSAPI_ERROR_RESULT( minor_status, GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT, (_GGSL("Invalid desired_mech passed to function"))); major_status = GSS_S_FAILURE; goto exit; } if(extension_oids != GSS_C_NO_OID_SET && (extension_buffers == GSS_C_NO_BUFFER_SET || extension_oids->count != extension_buffers->count)) { GLOBUS_GSI_GSSAPI_ERROR_RESULT( minor_status, GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT, (_GGSL("Invalid extension parameters passed to function"))); major_status = GSS_S_FAILURE; goto exit; } if(output_token == GSS_C_NO_BUFFER) { GLOBUS_GSI_GSSAPI_ERROR_RESULT( minor_status, GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT, (_GGSL("Invalid output_token passed to function"))); major_status = GSS_S_FAILURE; goto exit; } output_token->length = 0; if(req_flags & GSS_C_GLOBUS_SSL_COMPATIBLE) { bio = BIO_new(BIO_s_mem()); read_bio = bio; write_bio = bio; } else { bio = context->gss_sslbio; } /* lock the context mutex */ globus_mutex_lock(&context->mutex); /* pass the input to the BIO */ if(context->delegation_state != GSS_DELEGATION_START) { /* * first time there is no input token, but after that * there will always be one */ if(input_token == GSS_C_NO_BUFFER) { GLOBUS_GSI_GSSAPI_ERROR_RESULT( minor_status, GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT, (_GGSL("Invalid input_token passed to function: " "delegation is not at initial state"))); major_status = GSS_S_FAILURE; goto mutex_unlock; } major_status = globus_i_gsi_gss_put_token(&local_minor_status, context, read_bio, input_token); if (GSS_ERROR(major_status)) { GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT( minor_status, local_minor_status, GLOBUS_GSI_GSSAPI_ERROR_TOKEN_FAIL); goto mutex_unlock; } } /* delegation state machine */ switch (context->delegation_state) { case GSS_DELEGATION_START: /* start delegation by sending a "D" */ BIO_write(bio, "D", 1); context->delegation_state = GSS_DELEGATION_SIGN_CERT; break; case GSS_DELEGATION_SIGN_CERT: /* get the returned cert from the ssl BIO, make sure it is * correct and then sign it and place it in the output_token */ local_result = globus_gsi_proxy_inquire_req(context->proxy_handle, bio); if(local_result != GLOBUS_SUCCESS) { GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT( minor_status, local_result, GLOBUS_GSI_GSSAPI_ERROR_WITH_GSI_PROXY); major_status = GSS_S_FAILURE; goto mutex_unlock; } local_result = globus_gsi_cred_get_cert_type( context->cred_handle->cred_handle, &cert_type); if(local_result != GLOBUS_SUCCESS) { GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT( minor_status, local_result, GLOBUS_GSI_GSSAPI_ERROR_WITH_GSI_CREDENTIAL); major_status = GSS_S_FAILURE; context->delegation_state = GSS_DELEGATION_DONE; goto mutex_unlock; } /* set the requested time */ if(time_req != 0) { if(time_req%60) { /* round up */ time_req += 60; } local_result = globus_gsi_proxy_handle_set_time_valid( context->proxy_handle, time_req/60); if(local_result != GLOBUS_SUCCESS) { GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT( minor_status, local_result, GLOBUS_GSI_GSSAPI_ERROR_WITH_GSI_PROXY); major_status = GSS_S_FAILURE; context->delegation_state = GSS_DELEGATION_DONE; goto mutex_unlock; } } /* clear the proxycertinfo */ local_result = globus_gsi_proxy_handle_clear_cert_info(context->proxy_handle); if(local_result != GLOBUS_SUCCESS) { GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT( minor_status, local_result, GLOBUS_GSI_GSSAPI_ERROR_WITH_GSI_PROXY); major_status = GSS_S_FAILURE; context->delegation_state = GSS_DELEGATION_DONE; goto mutex_unlock; } if(cert_type == GLOBUS_GSI_CERT_UTILS_TYPE_CA) { GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT( minor_status, local_result, GLOBUS_GSI_GSSAPI_ERROR_WITH_GSI_CREDENTIAL); major_status = GSS_S_FAILURE; context->delegation_state = GSS_DELEGATION_DONE; goto mutex_unlock; } local_result = globus_gsi_proxy_handle_set_type( context->proxy_handle, (req_flags & GSS_C_GLOBUS_DELEGATE_LIMITED_PROXY_FLAG) ? GLOBUS_GSI_CERT_UTILS_TYPE_LIMITED_PROXY : GLOBUS_GSI_CERT_UTILS_TYPE_IMPERSONATION_PROXY); if(local_result != GLOBUS_SUCCESS) { GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT( minor_status, local_result, GLOBUS_GSI_GSSAPI_ERROR_WITH_GSI_PROXY); major_status = GSS_S_FAILURE; context->delegation_state = GSS_DELEGATION_DONE; goto mutex_unlock; } /* set the proxycertinfo here */ if(extension_oids != GSS_C_NO_OID_SET) { if(GLOBUS_GSI_CERT_UTILS_IS_GSI_2_PROXY(cert_type)) { GLOBUS_GSI_GSSAPI_ERROR_RESULT( minor_status, GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT, (_GGSL("A restricted globus proxy may not be created " "from a legacy globus proxy"))); context->delegation_state = GSS_DELEGATION_DONE; major_status = GSS_S_FAILURE; goto mutex_unlock; } for(index = 0; index < extension_oids->count; index++) { if(g_OID_equal((gss_OID) &extension_oids->elements[index], gss_proxycertinfo_extension)) { pci = extension_buffers->elements[index].value; local_result = globus_gsi_proxy_handle_set_proxy_cert_info( context->proxy_handle, pci); if(local_result != GLOBUS_SUCCESS) { GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT( minor_status, local_result, GLOBUS_GSI_GSSAPI_ERROR_WITH_GSI_PROXY); major_status = GSS_S_FAILURE; context->delegation_state = GSS_DELEGATION_DONE; goto mutex_unlock; } } } } local_result = globus_gsi_proxy_sign_req( context->proxy_handle, cred->cred_handle, bio); if(local_result != GLOBUS_SUCCESS) { GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT( minor_status, local_result, GLOBUS_GSI_GSSAPI_ERROR_WITH_GSI_PROXY); major_status = GSS_S_FAILURE; context->delegation_state = GSS_DELEGATION_DONE; goto mutex_unlock; } local_result = globus_gsi_cred_get_cert(cred->cred_handle, &cert); if(local_result != GLOBUS_SUCCESS) { GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT( minor_status, local_result, GLOBUS_GSI_GSSAPI_ERROR_WITH_GSI_CREDENTIAL); major_status = GSS_S_FAILURE; context->delegation_state = GSS_DELEGATION_DONE; goto mutex_unlock; } /* push the cert used to sign the proxy */ i2d_X509_bio(bio, cert); X509_free(cert); /* push the number of certs in the cert chain */ local_result = globus_gsi_cred_get_cert_chain(cred->cred_handle, &cert_chain); if(local_result != GLOBUS_SUCCESS) { GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT( minor_status, local_result, GLOBUS_GSI_GSSAPI_ERROR_WITH_GSI_CREDENTIAL); major_status = GSS_S_FAILURE; context->delegation_state = GSS_DELEGATION_DONE; goto mutex_unlock; } for(index = 0; index < sk_X509_num(cert_chain); index++) { cert = sk_X509_value(cert_chain, index); if(!cert) { GLOBUS_GSI_GSSAPI_OPENSSL_ERROR_RESULT( minor_status, GLOBUS_GSI_GSSAPI_ERROR_WITH_OPENSSL, (_GGSL("Couldn't get cert from cert chain"))); major_status = GSS_S_FAILURE; context->delegation_state = GSS_DELEGATION_DONE; goto mutex_unlock; } i2d_X509_bio(bio, cert); } sk_X509_pop_free(cert_chain, X509_free); /* reset state machine */ context->delegation_state = GSS_DELEGATION_START; break; case GSS_DELEGATION_COMPLETE_CRED: case GSS_DELEGATION_DONE: break; } major_status = globus_i_gsi_gss_get_token(&local_minor_status, context, write_bio, output_token); if(GSS_ERROR(major_status)) { GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT( minor_status, local_minor_status, GLOBUS_GSI_GSSAPI_ERROR_TOKEN_FAIL); goto mutex_unlock; } if (context->delegation_state != GSS_DELEGATION_START) { major_status |= GSS_S_CONTINUE_NEEDED; } mutex_unlock: globus_mutex_unlock(&context->mutex); exit: if(req_flags & GSS_C_GLOBUS_SSL_COMPATIBLE) { BIO_free(bio); } GLOBUS_I_GSI_GSSAPI_DEBUG_EXIT; return major_status; }
/* V2 KRB5_CALLCONV */ OM_uint32 KRB5_CALLCONV gss_add_cred_impersonate_name(OM_uint32 *minor_status, gss_cred_id_t input_cred_handle, const gss_cred_id_t impersonator_cred_handle, const gss_name_t desired_name, const gss_OID desired_mech, gss_cred_usage_t cred_usage, OM_uint32 initiator_time_req, OM_uint32 acceptor_time_req, gss_cred_id_t *output_cred_handle, gss_OID_set *actual_mechs, OM_uint32 *initiator_time_rec, OM_uint32 *acceptor_time_rec) { OM_uint32 status, temp_minor_status; OM_uint32 time_req, time_rec; gss_union_name_t union_name; gss_union_cred_t new_union_cred, union_cred; gss_cred_id_t mech_impersonator_cred; gss_name_t internal_name = GSS_C_NO_NAME; gss_name_t allocated_name = GSS_C_NO_NAME; gss_mechanism mech; gss_cred_id_t cred = NULL; gss_OID new_mechs_array = NULL; gss_cred_id_t * new_cred_array = NULL; status = val_add_cred_impersonate_name_args(minor_status, input_cred_handle, impersonator_cred_handle, desired_name, desired_mech, cred_usage, initiator_time_req, acceptor_time_req, output_cred_handle, actual_mechs, initiator_time_rec, acceptor_time_rec); if (status != GSS_S_COMPLETE) return (status); mech = gssint_get_mechanism(desired_mech); if (!mech) return GSS_S_BAD_MECH; else if (!mech->gss_acquire_cred) return (GSS_S_UNAVAILABLE); if (input_cred_handle == GSS_C_NO_CREDENTIAL) { union_cred = malloc(sizeof (gss_union_cred_desc)); if (union_cred == NULL) return (GSS_S_FAILURE); (void) memset(union_cred, 0, sizeof (gss_union_cred_desc)); /* for default credentials we will use GSS_C_NO_NAME */ internal_name = GSS_C_NO_NAME; } else { union_cred = (gss_union_cred_t)input_cred_handle; if (gssint_get_mechanism_cred(union_cred, desired_mech) != GSS_C_NO_CREDENTIAL) return (GSS_S_DUPLICATE_ELEMENT); } mech_impersonator_cred = gssint_get_mechanism_cred((gss_union_cred_t)impersonator_cred_handle, desired_mech); if (mech_impersonator_cred == GSS_C_NO_CREDENTIAL) return (GSS_S_NO_CRED); /* may need to create a mechanism specific name */ union_name = (gss_union_name_t)desired_name; if (union_name->mech_type && g_OID_equal(union_name->mech_type, &mech->mech_type)) internal_name = union_name->mech_name; else { if (gssint_import_internal_name(minor_status, &mech->mech_type, union_name, &allocated_name) != GSS_S_COMPLETE) return (GSS_S_BAD_NAME); internal_name = allocated_name; } if (cred_usage == GSS_C_ACCEPT) time_req = acceptor_time_req; else if (cred_usage == GSS_C_INITIATE) time_req = initiator_time_req; else if (cred_usage == GSS_C_BOTH) time_req = (acceptor_time_req > initiator_time_req) ? acceptor_time_req : initiator_time_req; else time_req = 0; status = mech->gss_acquire_cred_impersonate_name(minor_status, mech_impersonator_cred, internal_name, time_req, GSS_C_NULL_OID_SET, cred_usage, &cred, NULL, &time_rec); if (status != GSS_S_COMPLETE) { map_error(minor_status, mech); goto errout; } /* may need to set credential auxinfo strucutre */ if (union_cred->auxinfo.creation_time == 0) { union_cred->auxinfo.creation_time = time(NULL); union_cred->auxinfo.time_rec = time_rec; union_cred->auxinfo.cred_usage = cred_usage; /* * we must set the name; if name is not supplied * we must do inquire cred to get it */ if (internal_name == NULL) { if (mech->gss_inquire_cred == NULL || ((status = mech->gss_inquire_cred( &temp_minor_status, cred, &allocated_name, NULL, NULL, NULL)) != GSS_S_COMPLETE)) goto errout; internal_name = allocated_name; } if (internal_name != GSS_C_NO_NAME) { status = mech->gss_display_name(&temp_minor_status, internal_name, &union_cred->auxinfo.name, &union_cred->auxinfo.name_type); if (status != GSS_S_COMPLETE) goto errout; } } /* now add the new credential elements */ new_mechs_array = (gss_OID) malloc(sizeof (gss_OID_desc) * (union_cred->count+1)); new_cred_array = (gss_cred_id_t *) malloc(sizeof (gss_cred_id_t) * (union_cred->count+1)); if (!new_mechs_array || !new_cred_array) { status = GSS_S_FAILURE; goto errout; } if (acceptor_time_rec) if (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH) *acceptor_time_rec = time_rec; if (initiator_time_rec) if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH) *initiator_time_rec = time_rec; /* * OK, expand the mechanism array and the credential array */ (void) memcpy(new_mechs_array, union_cred->mechs_array, sizeof (gss_OID_desc) * union_cred->count); (void) memcpy(new_cred_array, union_cred->cred_array, sizeof (gss_cred_id_t) * union_cred->count); new_cred_array[union_cred->count] = cred; if ((new_mechs_array[union_cred->count].elements = malloc(mech->mech_type.length)) == NULL) goto errout; g_OID_copy(&new_mechs_array[union_cred->count], &mech->mech_type); if (actual_mechs != NULL) { gss_OID_set_desc oids; oids.count = union_cred->count + 1; oids.elements = new_mechs_array; status = generic_gss_copy_oid_set(minor_status, &oids, actual_mechs); if (GSS_ERROR(status)) { free(new_mechs_array[union_cred->count].elements); goto errout; } } if (output_cred_handle == NULL) { free(union_cred->mechs_array); free(union_cred->cred_array); new_union_cred = union_cred; } else { new_union_cred = malloc(sizeof (gss_union_cred_desc)); if (new_union_cred == NULL) { free(new_mechs_array[union_cred->count].elements); goto errout; } *new_union_cred = *union_cred; *output_cred_handle = (gss_cred_id_t)new_union_cred; } new_union_cred->mechs_array = new_mechs_array; new_union_cred->cred_array = new_cred_array; new_union_cred->count++; new_union_cred->loopback = new_union_cred; /* We're done with the internal name. Free it if we allocated it. */ if (allocated_name) (void) gssint_release_internal_name(&temp_minor_status, &mech->mech_type, &allocated_name); return (GSS_S_COMPLETE); errout: if (new_mechs_array) free(new_mechs_array); if (new_cred_array) free(new_cred_array); if (cred != NULL && mech->gss_release_cred) mech->gss_release_cred(&temp_minor_status, &cred); if (allocated_name) (void) gssint_release_internal_name(&temp_minor_status, &mech->mech_type, &allocated_name); if (input_cred_handle == GSS_C_NO_CREDENTIAL && union_cred) { if (union_cred->auxinfo.name.value) free(union_cred->auxinfo.name.value); free(union_cred); } return (status); }
OM_uint32 KRB5_CALLCONV gss_display_name_ext (OM_uint32 *minor_status, gss_name_t input_name, gss_OID display_as_name_type, gss_buffer_t output_name_buffer) { OM_uint32 status; gss_union_name_t union_name; gss_mechanism mech; status = val_dsp_name_ext_args(minor_status, input_name, display_as_name_type, output_name_buffer); if (status != GSS_S_COMPLETE) return status; union_name = (gss_union_name_t) input_name; if (union_name->mech_type) { mech = gssint_get_mechanism(union_name->mech_type); if (mech == NULL) status = GSS_S_BAD_NAME; else if (mech->gss_display_name_ext == NULL) { if (mech->gss_display_name != NULL && union_name->name_type != GSS_C_NO_OID && g_OID_equal(display_as_name_type, union_name->name_type)) { status = (*mech->gss_display_name)(minor_status, union_name->mech_name, output_name_buffer, NULL); if (status != GSS_S_COMPLETE) map_error(minor_status, mech); } else status = GSS_S_UNAVAILABLE; } else { status = (*mech->gss_display_name_ext)(minor_status, union_name->mech_name, display_as_name_type, output_name_buffer); if (status != GSS_S_COMPLETE) map_error(minor_status, mech); } return status; } if (union_name->name_type == GSS_C_NO_OID || !g_OID_equal(display_as_name_type, union_name->name_type)) return GSS_S_UNAVAILABLE; if ((output_name_buffer->value = malloc(union_name->external_name->length + 1)) == NULL) { return GSS_S_FAILURE; } output_name_buffer->length = union_name->external_name->length; (void) memcpy(output_name_buffer->value, union_name->external_name->value, union_name->external_name->length); ((char *)output_name_buffer->value)[output_name_buffer->length] = '\0'; return GSS_S_COMPLETE; }
static bool_t Svcauth_gss_accept_sec_context(struct svc_req *rqst, struct rpc_gss_init_res *gr) { struct svc_rpc_gss_data *gd; struct rpc_gss_cred *gc; gss_buffer_desc recv_tok, seqbuf; gss_OID mech; OM_uint32 maj_stat = 0, min_stat = 0, ret_flags, seq; gd = SVCAUTH_PRIVATE(rqst->rq_xprt->xp_auth); gc = (struct rpc_gss_cred *)rqst->rq_clntcred; memset(gr, 0, sizeof(*gr)); /* Deserialize arguments. */ memset(&recv_tok, 0, sizeof(recv_tok)); if(!svc_getargs(rqst->rq_xprt, (xdrproc_t)xdr_rpc_gss_init_args, (caddr_t) & recv_tok)) return (FALSE); gr->gr_major = gss_accept_sec_context(&gr->gr_minor, &gd->ctx, svcauth_gss_creds, &recv_tok, GSS_C_NO_CHANNEL_BINDINGS, &gd->client_name, &mech, &gr->gr_token, &ret_flags, NULL, NULL); svc_freeargs(rqst->rq_xprt, (xdrproc_t)xdr_rpc_gss_init_args, (caddr_t) & recv_tok); if(gr->gr_major != GSS_S_COMPLETE && gr->gr_major != GSS_S_CONTINUE_NEEDED) { sockaddr_t addr; char ipstring[SOCK_NAME_MAX]; copy_xprt_addr(&addr, rqst->rq_xprt); sprint_sockaddr(&addr, ipstring, sizeof(ipstring)); LogWarn(COMPONENT_RPCSEC_GSS, "Bad authentication major=%u minor=%u addr=%s", gr->gr_major, gr->gr_minor, ipstring); gd->ctx = GSS_C_NO_CONTEXT; goto errout; } /* * ANDROS: krb5 mechglue returns ctx of size 8 - two pointers, * one to the mechanism oid, one to the internal_ctx_id */ if((gr->gr_ctx.value = Mem_Alloc(sizeof(gss_union_ctx_id_desc))) == NULL) { LogCrit(COMPONENT_RPCSEC_GSS, "svcauth_gss_accept_context: out of memory"); goto errout; } memcpy(gr->gr_ctx.value, gd->ctx, sizeof(gss_union_ctx_id_desc)); gr->gr_ctx.length = sizeof(gss_union_ctx_id_desc); /* gr->gr_win = 0x00000005; ANDROS: for debugging linux kernel version... */ gr->gr_win = sizeof(gd->seqmask) * 8; /* Save client info. */ gd->sec.mech = mech; gd->sec.qop = GSS_C_QOP_DEFAULT; gd->sec.svc = gc->gc_svc; gd->seq = gc->gc_seq; gd->win = gr->gr_win; if(gr->gr_major == GSS_S_COMPLETE) { #ifdef SPKM /* spkm3: no src_name (anonymous) */ if(!g_OID_equal(gss_mech_spkm3, mech)) { #endif maj_stat = gss_display_name(&min_stat, gd->client_name, &gd->cname, &gd->sec.mech); LogFullDebug(COMPONENT_RPCSEC_GSS, "cname.val: %s cname.len: %d", (char *)gd->cname.value, (int)gd->cname.length); #ifdef SPKM } #endif if(maj_stat != GSS_S_COMPLETE) { } #ifdef HAVE_HEIMDAL #else if(isFullDebug(COMPONENT_RPCSEC_GSS)) { gss_buffer_desc mechname; gss_oid_to_str(&min_stat, mech, &mechname); gss_release_buffer(&min_stat, &mechname); } #endif seq = htonl(gr->gr_win); seqbuf.value = &seq; seqbuf.length = sizeof(seq); gss_release_buffer(&min_stat, &gd->checksum); LogFullDebug(COMPONENT_RPCSEC_GSS, "gss_sign in sec_accept_context"); maj_stat = gss_sign(&min_stat, gd->ctx, GSS_C_QOP_DEFAULT, &seqbuf, &gd->checksum); if(maj_stat != GSS_S_COMPLETE) { goto errout; } rqst->rq_xprt->xp_verf.oa_flavor = RPCSEC_GSS; rqst->rq_xprt->xp_verf.oa_base = gd->checksum.value; rqst->rq_xprt->xp_verf.oa_length = gd->checksum.length; } return (TRUE); errout: gss_release_buffer(&min_stat, &gr->gr_token); return (FALSE); }
OM_uint32 KRB5_CALLCONV gss_localname(OM_uint32 *minor, const gss_name_t pname, gss_const_OID mech_type, gss_buffer_t localname) { OM_uint32 major, tmpMinor; gss_mechanism mech; gss_union_name_t unionName; gss_name_t mechName = GSS_C_NO_NAME, mechNameP; gss_OID selected_mech = GSS_C_NO_OID; if (localname != GSS_C_NO_BUFFER) { localname->length = 0; localname->value = NULL; } if (minor == NULL) return GSS_S_CALL_INACCESSIBLE_WRITE; *minor = 0; if (pname == GSS_C_NO_NAME) return GSS_S_CALL_INACCESSIBLE_READ; if (localname == NULL) return GSS_S_CALL_INACCESSIBLE_WRITE; unionName = (gss_union_name_t)pname; if (mech_type != GSS_C_NO_OID) { major = gssint_select_mech_type(minor, mech_type, &selected_mech); if (major != GSS_S_COMPLETE) return major; mech = gssint_get_mechanism(selected_mech); } else mech = gssint_get_mechanism(unionName->mech_type); if (mech == NULL) return GSS_S_UNAVAILABLE; /* may need to create a mechanism specific name */ if (unionName->mech_type == GSS_C_NO_OID || (unionName->mech_type != GSS_C_NO_OID && !g_OID_equal(unionName->mech_type, &mech->mech_type))) { major = gssint_import_internal_name(minor, &mech->mech_type, unionName, &mechName); if (GSS_ERROR(major)) return major; mechNameP = mechName; } else mechNameP = unionName->mech_name; major = GSS_S_UNAVAILABLE; if (mech->gss_localname != NULL) { major = mech->gss_localname(minor, mechNameP, mech_type, localname); if (GSS_ERROR(major)) map_error(minor, mech); } if (GSS_ERROR(major)) major = attr_localname(minor, mech, mechNameP, localname); if (mechName != GSS_C_NO_NAME) gssint_release_internal_name(&tmpMinor, &mech->mech_type, &mechName); return major; }
OM_uint32 gss_pname_to_uid(OM_uint32 *minor, const gss_name_t pname, const gss_OID mech_type, uid_t *uidp) { OM_uint32 major, tmpMinor; gss_mechanism mech; gss_union_name_t unionName; gss_name_t mechName = GSS_C_NO_NAME, mechNameP; /* * find the appropriate mechanism specific pname_to_uid procedure and * call it. */ if (minor == NULL) return GSS_S_CALL_INACCESSIBLE_WRITE; *minor = 0; if (pname == GSS_C_NO_NAME) return GSS_S_CALL_INACCESSIBLE_READ; if (uidp == NULL) return GSS_S_CALL_INACCESSIBLE_WRITE; unionName = (gss_union_name_t)pname; if (mech_type != GSS_C_NO_OID) mech = gssint_get_mechanism(mech_type); else mech = gssint_get_mechanism(unionName->mech_type); if (mech == NULL) return GSS_S_UNAVAILABLE; /* may need to create a mechanism specific name */ if (unionName->mech_type == GSS_C_NO_OID || (unionName->mech_type != GSS_C_NO_OID && !g_OID_equal(unionName->mech_type, &mech->mech_type))) { major = gssint_import_internal_name(minor, &mech->mech_type, unionName, &mechName); if (GSS_ERROR(major)) return major; mechNameP = mechName; } else mechNameP = unionName->mech_name; major = GSS_S_UNAVAILABLE; if (mech->gss_pname_to_uid != NULL) { major = mech->gss_pname_to_uid(minor, mechNameP, mech_type, uidp); if (GSS_ERROR(major)) map_error(minor, mech); } if (GSS_ERROR(major)) major = attr_pname_to_uid(minor, mech, mechNameP, uidp); if (mechName != GSS_C_NO_NAME) gssint_release_internal_name(&tmpMinor, &mech->mech_type, &mechName); return major; }
OM_uint32 GSS_CALLCONV gss_inquire_names_for_mech( OM_uint32 * minor_status, const gss_OID mechanism, gss_OID_set * name_types ) { static char * _function_name_ = "gss_inquire_names_for_mech"; OM_uint32 major_status; int i; gss_OID oids[] = { GSS_C_NT_HOSTBASED_SERVICE, GSS_C_NT_ANONYMOUS, GSS_C_NT_EXPORT_NAME, (gss_OID) gss_nt_host_ip, (gss_OID) gss_nt_x509, NULL }; if (minor_status == NULL || mechanism == NULL || name_types == NULL) { if (minor_status != NULL) { GLOBUS_GSI_GSSAPI_OPENSSL_ERROR_RESULT( minor_status, GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT, (_GGSL("Invalid parameter"))); } major_status = GSS_S_FAILURE; goto out; } if (! g_OID_equal(mechanism, gss_mech_globus_gssapi_openssl)) { GLOBUS_GSI_GSSAPI_OPENSSL_ERROR_RESULT( minor_status, GLOBUS_GSI_GSSAPI_ERROR_BAD_MECH, (_GGSL("Requested mechanism not supported"))); major_status = GSS_S_BAD_MECH; goto out; } major_status = gss_create_empty_oid_set(minor_status, name_types); if (major_status != GSS_S_COMPLETE) { goto out; } for (i = 0; oids[i] != NULL; i++) { major_status = gss_add_oid_set_member( minor_status, oids[i], name_types); if (major_status != GSS_S_COMPLETE) { goto free_oids; } } free_oids: if (major_status != GSS_S_COMPLETE) { OM_uint32 local_major; OM_uint32 local_minor; local_major = gss_release_oid_set( &local_minor, name_types); } out: return major_status; }
/** * @brief Export a GSSAPI credential * @ingroup globus_gsi_gssapi_extensions * @details * Saves the credential so it can be checkpointed and * imported by gss_import_cred * * @param minor_status * @param cred_handle * @param desired_mech * Should either be gss_mech_globus_gssapi_openssl or * NULL (in which case gss_mech_globus_gssapi_openssl is * assumed). * @param option_req * @param export_buffer * * @return */ OM_uint32 GSS_CALLCONV gss_export_cred( OM_uint32 * minor_status, const gss_cred_id_t cred_handle, const gss_OID desired_mech, OM_uint32 option_req, gss_buffer_t export_buffer) { OM_uint32 major_status = GLOBUS_SUCCESS; BIO * bp = NULL; gss_cred_id_desc * cred_desc = NULL; globus_result_t local_result; char * proxy_filename = NULL; GLOBUS_I_GSI_GSSAPI_DEBUG_ENTER; cred_desc = (gss_cred_id_desc *) cred_handle; *minor_status = (OM_uint32) GLOBUS_SUCCESS; if (export_buffer == NULL || export_buffer == GSS_C_NO_BUFFER) { major_status = GSS_S_FAILURE; GLOBUS_GSI_GSSAPI_ERROR_RESULT( minor_status, GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT, (_GGSL("NULL or empty export_buffer parameter passed to function: %s"), __func__)); goto exit; } export_buffer->length = 0; export_buffer->value = NULL; if (cred_handle == NULL) { major_status = GSS_S_FAILURE; GLOBUS_GSI_GSSAPI_ERROR_RESULT( minor_status, GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT, (_GGSL("NULL or empty export_buffer parameter passed to function: %s"), __func__)); goto exit; } if(desired_mech != NULL && g_OID_equal(desired_mech, (gss_OID) gss_mech_globus_gssapi_openssl)) { major_status = GSS_S_BAD_MECH; GLOBUS_GSI_GSSAPI_ERROR_RESULT( minor_status, GLOBUS_GSI_GSSAPI_ERROR_BAD_MECH, (_GGSL("The desired mechanism of: %s, is not supported by this " "GSS implementation"), desired_mech->elements)); goto exit; } if(option_req == GSS_IMPEXP_OPAQUE_FORM) { /* When option_req is equal to EXPORT_OPAQUE_FORM (0), it exports * an opaque buffer suitable for storage in memory or on * disk or passing to another process, which * can import the buffer with gss_import_cred(). */ bp = BIO_new(BIO_s_mem()); if(bp == NULL) { GLOBUS_GSI_GSSAPI_OPENSSL_ERROR_RESULT( minor_status, GLOBUS_GSI_GSSAPI_ERROR_WITH_OPENSSL, (_GGSL("Couldn't initialize IO bio for exporting credential"))); major_status = GSS_S_FAILURE; goto exit; } local_result = globus_gsi_cred_write(cred_desc->cred_handle, bp); if(local_result != GLOBUS_SUCCESS) { GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT( minor_status, local_result, GLOBUS_GSI_GSSAPI_ERROR_IMPEXP_BIO_SSL); major_status = GSS_S_FAILURE; goto exit; } export_buffer->length = BIO_pending(bp); if (export_buffer->length > 0) { export_buffer->value = (char *) malloc(export_buffer->length); if (export_buffer->value == NULL) { export_buffer->length = 0; GLOBUS_GSI_GSSAPI_MALLOC_ERROR(minor_status); major_status = GSS_S_FAILURE; goto exit; } BIO_read(bp, export_buffer->value, export_buffer->length); } else { export_buffer->value = NULL; } major_status = GSS_S_COMPLETE; } else if(option_req == GSS_IMPEXP_MECH_SPECIFIC) { /* With option_req is equal to EXPORT_MECH_SPECIFIC (1), * it exports a buffer filled with mechanism-specific * information that the calling application can use * to pass the credentials to another process that * is not written to the GSS-API. */ local_result = GLOBUS_GSI_SYSCONFIG_GET_UNIQUE_PROXY_FILENAME(&proxy_filename); if(local_result != GLOBUS_SUCCESS) { proxy_filename = NULL; GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT( minor_status, local_result, GLOBUS_GSI_GSSAPI_ERROR_WITH_GSI_PROXY); major_status = GSS_S_FAILURE; goto exit; } GLOBUS_I_GSI_GSSAPI_DEBUG_FPRINTF( 3, (globus_i_gsi_gssapi_debug_fstream, "Writing exported cred to: %s\n", proxy_filename)); local_result = globus_gsi_cred_write_proxy(cred_desc->cred_handle, proxy_filename); if(local_result != GLOBUS_SUCCESS) { GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT( minor_status, local_result, GLOBUS_GSI_GSSAPI_ERROR_WITH_GSI_PROXY); major_status = GSS_S_FAILURE; goto exit; } export_buffer->value = globus_common_create_string( "X509_USER_PROXY=%s", proxy_filename); export_buffer->length = strlen(export_buffer->value)+1; } else { major_status = GSS_S_FAILURE; GLOBUS_GSI_GSSAPI_ERROR_RESULT( minor_status, GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT, (_GGSL("Unrecognized option_req of: %d"), option_req)); goto exit; } exit: if(proxy_filename != NULL) { free(proxy_filename); } if (bp) { BIO_free(bp); } GLOBUS_I_GSI_GSSAPI_DEBUG_EXIT; return major_status; }
static krb5_error_code make_seal_token_v1 (krb5_context context, krb5_keyblock *enc, krb5_keyblock *seq, gssint_uint64 *seqnum, int direction, gss_buffer_t text, gss_buffer_t token, int signalg, size_t cksum_size, int sealalg, int do_encrypt, int toktype, int bigend, gss_OID oid) { krb5_error_code code; size_t sumlen; char *data_ptr; krb5_data plaind; krb5_checksum md5cksum; krb5_checksum cksum; /* msglen contains the message length * we are signing/encrypting. tmsglen * contains the length of the message * we plan to write out to the token. * tlen is the length of the token * including header. */ unsigned int conflen=0, tmsglen, tlen, msglen; unsigned char *t, *ptr; unsigned char *plain; unsigned char pad; krb5_keyusage sign_usage = KG_USAGE_SIGN; assert((!do_encrypt) || (toktype == KG_TOK_SEAL_MSG)); /* create the token buffer */ /* Do we need confounder? */ if (do_encrypt || (!bigend && (toktype == KG_TOK_SEAL_MSG))) conflen = kg_confounder_size(context, enc); else conflen = 0; if (toktype == KG_TOK_SEAL_MSG) { switch (sealalg) { case SEAL_ALG_MICROSOFT_RC4: msglen = conflen + text->length+1; pad = 1; break; default: /* XXX knows that des block size is 8 */ msglen = (conflen+text->length+8)&(~7); pad = 8-(text->length%8); } tmsglen = msglen; } else { tmsglen = 0; msglen = text->length; pad = 0; } tlen = g_token_size((gss_OID) oid, 14+cksum_size+tmsglen); if ((t = (unsigned char *) xmalloc(tlen)) == NULL) return(ENOMEM); /*** fill in the token */ ptr = t; g_make_token_header(oid, 14+cksum_size+tmsglen, &ptr, toktype); /* 0..1 SIGN_ALG */ store_16_le(signalg, &ptr[0]); /* 2..3 SEAL_ALG or Filler */ if ((toktype == KG_TOK_SEAL_MSG) && do_encrypt) { store_16_le(sealalg, &ptr[2]); } else { /* No seal */ ptr[2] = 0xff; ptr[3] = 0xff; } /* 4..5 Filler */ ptr[4] = 0xff; ptr[5] = 0xff; /* pad the plaintext, encrypt if needed, and stick it in the token */ /* initialize the the cksum */ switch (signalg) { case SGN_ALG_DES_MAC_MD5: case SGN_ALG_MD2_5: md5cksum.checksum_type = CKSUMTYPE_RSA_MD5; break; case SGN_ALG_HMAC_SHA1_DES3_KD: md5cksum.checksum_type = CKSUMTYPE_HMAC_SHA1_DES3; break; case SGN_ALG_HMAC_MD5: md5cksum.checksum_type = CKSUMTYPE_HMAC_MD5_ARCFOUR; if (toktype != KG_TOK_SEAL_MSG) sign_usage = 15; break; default: case SGN_ALG_DES_MAC: abort (); } code = krb5_c_checksum_length(context, md5cksum.checksum_type, &sumlen); if (code) { xfree(t); return(code); } md5cksum.length = sumlen; if ((plain = (unsigned char *) xmalloc(msglen ? msglen : 1)) == NULL) { xfree(t); return(ENOMEM); } if (conflen) { if ((code = kg_make_confounder(context, enc, plain))) { xfree(plain); xfree(t); return(code); } } memcpy(plain+conflen, text->value, text->length); if (pad) memset(plain+conflen+text->length, pad, pad); /* compute the checksum */ /* 8 = head of token body as specified by mech spec */ if (! (data_ptr = (char *) xmalloc(8 + (bigend ? text->length : msglen)))) { xfree(plain); xfree(t); return(ENOMEM); } (void) memcpy(data_ptr, ptr-2, 8); if (bigend) (void) memcpy(data_ptr+8, text->value, text->length); else (void) memcpy(data_ptr+8, plain, msglen); plaind.length = 8 + (bigend ? text->length : msglen); plaind.data = data_ptr; code = krb5_c_make_checksum(context, md5cksum.checksum_type, seq, sign_usage, &plaind, &md5cksum); xfree(data_ptr); if (code) { xfree(plain); xfree(t); return(code); } switch(signalg) { case SGN_ALG_DES_MAC_MD5: case 3: if ((code = kg_encrypt(context, seq, KG_USAGE_SEAL, (g_OID_equal(oid, gss_mech_krb5_old) ? seq->contents : NULL), md5cksum.contents, md5cksum.contents, 16))) { krb5_free_checksum_contents(context, &md5cksum); xfree (plain); xfree(t); return code; } cksum.length = cksum_size; cksum.contents = md5cksum.contents + 16 - cksum.length; memcpy(ptr+14, cksum.contents, cksum.length); break; case SGN_ALG_HMAC_SHA1_DES3_KD: /* * Using key derivation, the call to krb5_c_make_checksum * already dealt with encrypting. */ if (md5cksum.length != cksum_size) abort (); memcpy (ptr+14, md5cksum.contents, md5cksum.length); break; case SGN_ALG_HMAC_MD5: memcpy (ptr+14, md5cksum.contents, cksum_size); break; } krb5_free_checksum_contents(context, &md5cksum); /* create the seq_num */ if ((code = kg_make_seq_num(context, seq, direction?0:0xff, (krb5_ui_4)*seqnum, ptr+14, ptr+6))) { xfree (plain); xfree(t); return(code); } if (do_encrypt) { switch(sealalg) { case SEAL_ALG_MICROSOFT_RC4: { unsigned char bigend_seqnum[4]; krb5_keyblock *enc_key; int i; store_32_be(*seqnum, bigend_seqnum); code = krb5_copy_keyblock (context, enc, &enc_key); if (code) { xfree(plain); xfree(t); return(code); } assert (enc_key->length == 16); for (i = 0; i <= 15; i++) ((char *) enc_key->contents)[i] ^=0xf0; code = kg_arcfour_docrypt (enc_key, 0, bigend_seqnum, 4, plain, tmsglen, ptr+14+cksum_size); krb5_free_keyblock (context, enc_key); if (code) { xfree(plain); xfree(t); return(code); } } break; default: if ((code = kg_encrypt(context, enc, KG_USAGE_SEAL, NULL, (krb5_pointer) plain, (krb5_pointer) (ptr+cksum_size+14), tmsglen))) { xfree(plain); xfree(t); return(code); } } }else { if (tmsglen) memcpy(ptr+14+cksum_size, plain, tmsglen); } xfree(plain); /* that's it. return the token */ (*seqnum)++; *seqnum &= 0xffffffffL; token->length = tlen; token->value = (void *) t; return(0); }
/* * spkm3_read_token() * * only SPKM_MIC_TOK with md5 intg-alg is supported */ u32 spkm3_read_token(struct spkm3_ctx *ctx, struct xdr_netobj *read_token, /* checksum */ struct xdr_buf *message_buffer, /* signbuf */ int toktype) { s32 checksum_type; s32 code; struct xdr_netobj wire_cksum = {.len =0, .data = NULL}; char cksumdata[16]; struct xdr_netobj md5cksum = {.len = 0, .data = cksumdata}; unsigned char *ptr = (unsigned char *)read_token->data; unsigned char *cksum; int bodysize, md5elen; int mic_hdrlen; u32 ret = GSS_S_DEFECTIVE_TOKEN; if (g_verify_token_header((struct xdr_netobj *) &ctx->mech_used, &bodysize, &ptr, read_token->len)) goto out; /* decode the token */ if (toktype != SPKM_MIC_TOK) { dprintk("RPC: BAD SPKM3 token type: %d\n", toktype); goto out; } if ((ret = spkm3_verify_mic_token(&ptr, &mic_hdrlen, &cksum))) goto out; if (*cksum++ != 0x03) { dprintk("RPC: spkm3_read_token BAD checksum type\n"); goto out; } md5elen = *cksum++; cksum++; /* move past the zbit */ if (!decode_asn1_bitstring(&wire_cksum, cksum, md5elen - 1, 16)) goto out; /* HARD CODED FOR MD5 */ /* compute the checksum of the message. * ptr + 2 = start of header piece of checksum * mic_hdrlen + 2 = length of header piece of checksum */ ret = GSS_S_DEFECTIVE_TOKEN; if (!g_OID_equal(&ctx->intg_alg, &hmac_md5_oid)) { dprintk("RPC: gss_spkm3_seal: unsupported I-ALG " "algorithm\n"); goto out; } checksum_type = CKSUMTYPE_HMAC_MD5; code = make_spkm3_checksum(checksum_type, &ctx->derived_integ_key, ptr + 2, mic_hdrlen + 2, message_buffer, 0, &md5cksum); if (code) goto out; ret = GSS_S_BAD_SIG; code = memcmp(md5cksum.data, wire_cksum.data, wire_cksum.len); if (code) { dprintk("RPC: bad MIC checksum\n"); goto out; } ret = GSS_S_COMPLETE; out: kfree(wire_cksum.data); return ret; }