krb5_error_code KRB5_CALLCONV krb5_authdata_context_copy(krb5_context kcontext, krb5_authdata_context src, krb5_authdata_context *pdst) { int i; krb5_error_code code; krb5_authdata_context dst; /* XXX we need to init a new context because we can't copy plugins */ code = krb5_authdata_context_init(kcontext, &dst); if (code != 0) return code; for (i = 0; i < src->n_modules; i++) { struct _krb5_authdata_context_module *module = &src->modules[i]; code = k5_copy_ad_module_data(kcontext, src, module, dst); if (code != 0) break; } if (code != 0) { krb5_authdata_context_free(kcontext, dst); return code; } *pdst = dst; return 0; }
/* * Import serialized authdata context */ static krb5_error_code import_name_composite(krb5_context context, unsigned char *enc_data, size_t enc_length, krb5_authdata_context *pad_context) { krb5_authdata_context ad_context; krb5_error_code code; krb5_data data; code = krb5_authdata_context_init(context, &ad_context); if (code != 0) return code; data.data = (char *)enc_data; data.length = enc_length; code = krb5_authdata_import_attributes(context, ad_context, AD_USAGE_MASK, &data); if (code != 0) { krb5_authdata_context_free(context, ad_context); return code; } *pad_context = ad_context; return 0; }
OM_uint32 KRB5_CALLCONV krb5_gss_release_any_name_mapping(OM_uint32 *minor_status, gss_name_t name, gss_buffer_t type_id, gss_any_t *input) { krb5_context context; krb5_error_code code; krb5_gss_name_t kname; char *kmodule; if (minor_status != NULL) *minor_status = 0; code = krb5_gss_init_context(&context); if (code != 0) { *minor_status = code; return GSS_S_FAILURE; } kname = (krb5_gss_name_t)name; code = k5_mutex_lock(&kname->lock); if (code != 0) { *minor_status = code; return GSS_S_FAILURE; } if (kname->ad_context == NULL) { code = krb5_authdata_context_init(context, &kname->ad_context); if (code != 0) { *minor_status = code; k5_mutex_unlock(&kname->lock); krb5_free_context(context); return GSS_S_UNAVAILABLE; } } kmodule = (char *)type_id->value; if (kmodule[type_id->length] != '\0') { k5_mutex_unlock(&kname->lock); krb5_free_context(context); return GSS_S_UNAVAILABLE; } code = krb5_authdata_free_internal(context, kname->ad_context, kmodule, *input); if (code == 0) *input = NULL; k5_mutex_unlock(&kname->lock); krb5_free_context(context); return kg_map_name_error(minor_status, code); }
OM_uint32 KRB5_CALLCONV krb5_gss_set_name_attribute(OM_uint32 *minor_status, gss_name_t name, int complete, gss_buffer_t attr, gss_buffer_t value) { krb5_context context; krb5_error_code code; krb5_gss_name_t kname; krb5_data kattr; krb5_data kvalue; if (minor_status != NULL) *minor_status = 0; code = krb5_gss_init_context(&context); if (code != 0) { *minor_status = code; return GSS_S_FAILURE; } kname = (krb5_gss_name_t)name; code = k5_mutex_lock(&kname->lock); if (code != 0) { *minor_status = code; return GSS_S_FAILURE; } if (kname->ad_context == NULL) { code = krb5_authdata_context_init(context, &kname->ad_context); if (code != 0) { *minor_status = code; k5_mutex_unlock(&kname->lock); krb5_free_context(context); return GSS_S_UNAVAILABLE; } } kattr.data = (char *)attr->value; kattr.length = attr->length; kvalue.data = (char *)value->value; kvalue.length = value->length; code = krb5_authdata_set_attribute(context, kname->ad_context, complete, &kattr, &kvalue); k5_mutex_unlock(&kname->lock); krb5_free_context(context); return kg_map_name_error(minor_status, code); }
OM_uint32 KRB5_CALLCONV krb5_gss_inquire_name(OM_uint32 *minor_status, gss_name_t name, int *name_is_MN, gss_OID *MN_mech, gss_buffer_set_t *attrs) { krb5_context context; krb5_error_code code; krb5_gss_name_t kname; krb5_data *kattrs = NULL; if (minor_status != NULL) *minor_status = 0; if (attrs != NULL) *attrs = GSS_C_NO_BUFFER_SET; code = krb5_gss_init_context(&context); if (code != 0) { *minor_status = code; return GSS_S_FAILURE; } kname = (krb5_gss_name_t)name; code = k5_mutex_lock(&kname->lock); if (code != 0) { *minor_status = code; return GSS_S_FAILURE; } if (kname->ad_context == NULL) { code = krb5_authdata_context_init(context, &kname->ad_context); if (code != 0) goto cleanup; } code = krb5_authdata_get_attribute_types(context, kname->ad_context, &kattrs); if (code != 0) goto cleanup; code = data_list_to_buffer_set(context, kattrs, attrs); kattrs = NULL; if (code != 0) goto cleanup; cleanup: k5_mutex_unlock(&kname->lock); krb5int_free_data_list(context, kattrs); krb5_free_context(context); return kg_map_name_error(minor_status, code); }
OM_uint32 krb5_gss_delete_name_attribute(OM_uint32 *minor_status, gss_name_t name, gss_buffer_t attr) { krb5_context context; krb5_error_code code; krb5_gss_name_t kname; krb5_data kattr; if (minor_status != NULL) *minor_status = 0; code = krb5_gss_init_context(&context); if (code != 0) { *minor_status = code; return GSS_S_FAILURE; } if (!kg_validate_name(name)) { *minor_status = (OM_uint32)G_VALIDATE_FAILED; krb5_free_context(context); return GSS_S_CALL_BAD_STRUCTURE|GSS_S_BAD_NAME; } kname = (krb5_gss_name_t)name; code = k5_mutex_lock(&kname->lock); if (code != 0) { *minor_status = code; return GSS_S_FAILURE; } if (kname->ad_context == NULL) { code = krb5_authdata_context_init(context, &kname->ad_context); if (code != 0) { *minor_status = code; k5_mutex_unlock(&kname->lock); krb5_free_context(context); return GSS_S_UNAVAILABLE; } } kattr.data = (char *)attr->value; kattr.length = attr->length; code = krb5_authdata_delete_attribute(context, kname->ad_context, &kattr); k5_mutex_unlock(&kname->lock); krb5_free_context(context); return kg_map_name_error(minor_status, code); }
/* * Internalize an authdata context. */ static krb5_error_code krb5_authdata_context_internalize(krb5_context kcontext, krb5_pointer *ptr, krb5_octet **buffer, size_t *lenremain) { krb5_error_code code; krb5_authdata_context context; krb5_int32 ibuf; krb5_octet *bp; size_t remain; bp = *buffer; remain = *lenremain; code = krb5_ser_unpack_int32(&ibuf, &bp, &remain); if (code != 0) return code; if (ibuf != KV5M_AUTHDATA_CONTEXT) return EINVAL; code = krb5_authdata_context_init(kcontext, &context); if (code != 0) return code; code = k5_ad_internalize(kcontext, context, AD_USAGE_MASK, &bp, &remain); if (code != 0) { krb5_authdata_context_free(kcontext, context); return code; } code = krb5_ser_unpack_int32(&ibuf, &bp, &remain); if (code != 0) return code; if (ibuf != KV5M_AUTHDATA_CONTEXT) { krb5_authdata_context_free(kcontext, context); return EINVAL; } *buffer = bp; *lenremain = remain; *ptr = context; return 0; }
OM_uint32 KRB5_CALLCONV krb5_gss_get_name_attribute(OM_uint32 *minor_status, gss_name_t name, gss_buffer_t attr, int *authenticated, int *complete, gss_buffer_t value, gss_buffer_t display_value, int *more) { krb5_context context; krb5_error_code code; krb5_gss_name_t kname; krb5_data kattr; krb5_boolean kauthenticated; krb5_boolean kcomplete; krb5_data kvalue; krb5_data kdisplay_value; if (minor_status != NULL) *minor_status = 0; code = krb5_gss_init_context(&context); if (code != 0) { *minor_status = code; return GSS_S_FAILURE; } kname = (krb5_gss_name_t)name; code = k5_mutex_lock(&kname->lock); if (code != 0) { *minor_status = code; krb5_free_context(context); return GSS_S_FAILURE; } if (kname->ad_context == NULL) { code = krb5_authdata_context_init(context, &kname->ad_context); if (code != 0) { *minor_status = code; k5_mutex_unlock(&kname->lock); krb5_free_context(context); return GSS_S_UNAVAILABLE; } } kattr.data = (char *)attr->value; kattr.length = attr->length; kauthenticated = FALSE; kcomplete = FALSE; code = krb5_authdata_get_attribute(context, kname->ad_context, &kattr, &kauthenticated, &kcomplete, value ? &kvalue : NULL, display_value ? &kdisplay_value : NULL, more); if (code == 0) { if (value != NULL) code = data_to_gss(&kvalue, value); if (authenticated != NULL) *authenticated = kauthenticated; if (complete != NULL) *complete = kcomplete; if (display_value != NULL) { if (code == 0) code = data_to_gss(&kdisplay_value, display_value); else free(kdisplay_value.data); } } k5_mutex_unlock(&kname->lock); krb5_free_context(context); return kg_map_name_error(minor_status, code); }
static krb5_error_code rd_req_decoded_opt(krb5_context context, krb5_auth_context *auth_context, const krb5_ap_req *req, krb5_const_principal server, krb5_keytab keytab, krb5_flags *ap_req_options, krb5_ticket **ticket, int check_valid_flag) { krb5_error_code retval = 0; krb5_enctype *desired_etypes = NULL; int desired_etypes_len = 0; int rfc4537_etypes_len = 0; krb5_enctype *permitted_etypes = NULL; int permitted_etypes_len = 0; krb5_keyblock decrypt_key; decrypt_key.enctype = ENCTYPE_NULL; decrypt_key.contents = NULL; req->ticket->enc_part2 = NULL; /* if (req->ap_options & AP_OPTS_USE_SESSION_KEY) do we need special processing here ? */ /* decrypt the ticket */ if ((*auth_context)->key) { /* User to User authentication */ if ((retval = krb5_decrypt_tkt_part(context, &(*auth_context)->key->keyblock, req->ticket))) goto cleanup; if (check_valid_flag) { decrypt_key = (*auth_context)->key->keyblock; (*auth_context)->key->keyblock.contents = NULL; } krb5_k_free_key(context, (*auth_context)->key); (*auth_context)->key = NULL; } else { retval = decrypt_ticket(context, req, server, keytab, check_valid_flag ? &decrypt_key : NULL); if (retval) goto cleanup; } TRACE_RD_REQ_TICKET(context, req->ticket->enc_part2->client, req->ticket->server, req->ticket->enc_part2->session); /* XXX this is an evil hack. check_valid_flag is set iff the call is not from inside the kdc. we can use this to determine which key usage to use */ #ifndef LEAN_CLIENT if ((retval = decrypt_authenticator(context, req, &((*auth_context)->authentp), check_valid_flag))) goto cleanup; #endif if (!krb5_principal_compare(context, (*auth_context)->authentp->client, req->ticket->enc_part2->client)) { retval = KRB5KRB_AP_ERR_BADMATCH; goto cleanup; } if ((*auth_context)->remote_addr && !krb5_address_search(context, (*auth_context)->remote_addr, req->ticket->enc_part2->caddrs)) { retval = KRB5KRB_AP_ERR_BADADDR; goto cleanup; } if (!server) { server = req->ticket->server; } /* Get an rcache if necessary. */ if (((*auth_context)->rcache == NULL) && ((*auth_context)->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) && server) { if ((retval = krb5_get_server_rcache(context, krb5_princ_component(context,server,0), &(*auth_context)->rcache))) goto cleanup; } /* okay, now check cross-realm policy */ #if defined(_SINGLE_HOP_ONLY) /* Single hop cross-realm tickets only */ { krb5_transited *trans = &(req->ticket->enc_part2->transited); /* If the transited list is empty, then we have at most one hop */ if (trans->tr_contents.length > 0 && trans->tr_contents.data[0]) retval = KRB5KRB_AP_ERR_ILL_CR_TKT; } #elif defined(_NO_CROSS_REALM) /* No cross-realm tickets */ { char * lrealm; krb5_data * realm; krb5_transited * trans; realm = krb5_princ_realm(context, req->ticket->enc_part2->client); trans = &(req->ticket->enc_part2->transited); /* * If the transited list is empty, then we have at most one hop * So we also have to check that the client's realm is the local one */ krb5_get_default_realm(context, &lrealm); if ((trans->tr_contents.length > 0 && trans->tr_contents.data[0]) || !data_eq_string(*realm, lrealm)) { retval = KRB5KRB_AP_ERR_ILL_CR_TKT; } free(lrealm); } #else /* Hierarchical Cross-Realm */ { krb5_data * realm; krb5_transited * trans; realm = krb5_princ_realm(context, req->ticket->enc_part2->client); trans = &(req->ticket->enc_part2->transited); /* * If the transited list is not empty, then check that all realms * transited are within the hierarchy between the client's realm * and the local realm. */ if (trans->tr_contents.length > 0 && trans->tr_contents.data[0]) { retval = krb5_check_transited_list(context, &(trans->tr_contents), realm, krb5_princ_realm (context,server)); } } #endif if (retval) goto cleanup; /* only check rcache if sender has provided one---some services may not be able to use replay caches (such as datagram servers) */ if ((*auth_context)->rcache) { krb5_donot_replay rep; krb5_tkt_authent tktauthent; tktauthent.ticket = req->ticket; tktauthent.authenticator = (*auth_context)->authentp; if (!(retval = krb5_auth_to_rep(context, &tktauthent, &rep))) { retval = krb5_rc_hash_message(context, &req->authenticator.ciphertext, &rep.msghash); if (!retval) { retval = krb5_rc_store(context, (*auth_context)->rcache, &rep); free(rep.msghash); } free(rep.server); free(rep.client); } if (retval) goto cleanup; } retval = krb5int_validate_times(context, &req->ticket->enc_part2->times); if (retval != 0) goto cleanup; if ((retval = krb5_check_clockskew(context, (*auth_context)->authentp->ctime))) goto cleanup; if (check_valid_flag) { if (req->ticket->enc_part2->flags & TKT_FLG_INVALID) { retval = KRB5KRB_AP_ERR_TKT_INVALID; goto cleanup; } if ((retval = krb5_authdata_context_init(context, &(*auth_context)->ad_context))) goto cleanup; if ((retval = krb5int_authdata_verify(context, (*auth_context)->ad_context, AD_USAGE_MASK, auth_context, &decrypt_key, req))) goto cleanup; } /* read RFC 4537 etype list from sender */ retval = decode_etype_list(context, (*auth_context)->authentp, &desired_etypes, &rfc4537_etypes_len); if (retval != 0) goto cleanup; if (desired_etypes == NULL) desired_etypes = (krb5_enctype *)calloc(4, sizeof(krb5_enctype)); else desired_etypes = (krb5_enctype *)realloc(desired_etypes, (rfc4537_etypes_len + 4) * sizeof(krb5_enctype)); if (desired_etypes == NULL) { retval = ENOMEM; goto cleanup; } desired_etypes_len = rfc4537_etypes_len; /* * RFC 4537: * * If the EtypeList is present and the server prefers an enctype from * the client's enctype list over that of the AP-REQ authenticator * subkey (if that is present) or the service ticket session key, the * server MUST create a subkey using that enctype. This negotiated * subkey is sent in the subkey field of AP-REP message, and it is then * used as the protocol key or base key [RFC3961] for subsequent * communication. * * If the enctype of the ticket session key is included in the enctype * list sent by the client, it SHOULD be the last on the list; * otherwise, this enctype MUST NOT be negotiated if it was not included * in the list. * * The second paragraph does appear to contradict the first with respect * to whether it is legal to negotiate the ticket session key type if it * is absent in the EtypeList. A literal reading suggests that we can use * the AP-REQ subkey enctype. Also a client has no way of distinguishing * a server that does not RFC 4537 from one that has chosen the same * enctype as the ticket session key for the acceptor subkey, surely. */ if ((*auth_context)->authentp->subkey != NULL) { desired_etypes[desired_etypes_len++] = (*auth_context)->authentp->subkey->enctype; } desired_etypes[desired_etypes_len++] = req->ticket->enc_part2->session->enctype; desired_etypes[desired_etypes_len] = ENCTYPE_NULL; if (((*auth_context)->auth_context_flags & KRB5_AUTH_CONTEXT_PERMIT_ALL) == 0) { if ((*auth_context)->permitted_etypes != NULL) { permitted_etypes = (*auth_context)->permitted_etypes; } else { retval = krb5_get_permitted_enctypes(context, &permitted_etypes); if (retval != 0) goto cleanup; } permitted_etypes_len = krb5int_count_etypes(permitted_etypes); } else { permitted_etypes = NULL; permitted_etypes_len = 0; } /* check if the various etypes are permitted */ retval = negotiate_etype(context, desired_etypes, desired_etypes_len, rfc4537_etypes_len, permitted_etypes, permitted_etypes_len, &(*auth_context)->negotiated_etype); if (retval != 0) goto cleanup; TRACE_RD_REQ_NEGOTIATED_ETYPE(context, (*auth_context)->negotiated_etype); assert((*auth_context)->negotiated_etype != ENCTYPE_NULL); (*auth_context)->remote_seq_number = (*auth_context)->authentp->seq_number; if ((*auth_context)->authentp->subkey) { TRACE_RD_REQ_SUBKEY(context, (*auth_context)->authentp->subkey); if ((retval = krb5_k_create_key(context, (*auth_context)->authentp->subkey, &((*auth_context)->recv_subkey)))) goto cleanup; retval = krb5_k_create_key(context, (*auth_context)->authentp->subkey, &((*auth_context)->send_subkey)); if (retval) { krb5_k_free_key(context, (*auth_context)->recv_subkey); (*auth_context)->recv_subkey = NULL; goto cleanup; } } else { (*auth_context)->recv_subkey = 0; (*auth_context)->send_subkey = 0; } if ((retval = krb5_k_create_key(context, req->ticket->enc_part2->session, &((*auth_context)->key)))) goto cleanup; debug_log_authz_data("ticket", req->ticket->enc_part2->authorization_data); /* * If not AP_OPTS_MUTUAL_REQUIRED then and sequence numbers are used * then the default sequence number is the one's complement of the * sequence number sent ot us. */ if ((!(req->ap_options & AP_OPTS_MUTUAL_REQUIRED)) && (*auth_context)->remote_seq_number) { (*auth_context)->local_seq_number ^= (*auth_context)->remote_seq_number; } if (ticket) if ((retval = krb5_copy_ticket(context, req->ticket, ticket))) goto cleanup; if (ap_req_options) { *ap_req_options = req->ap_options & AP_OPTS_WIRE_MASK; if (rfc4537_etypes_len != 0) *ap_req_options |= AP_OPTS_ETYPE_NEGOTIATION; if ((*auth_context)->negotiated_etype != krb5_k_key_enctype(context, (*auth_context)->key)) *ap_req_options |= AP_OPTS_USE_SUBKEY; } retval = 0; cleanup: if (desired_etypes != NULL) free(desired_etypes); if (permitted_etypes != NULL && permitted_etypes != (*auth_context)->permitted_etypes) free(permitted_etypes); if (retval) { /* only free if we're erroring out...otherwise some applications will need the output. */ if (req->ticket->enc_part2) krb5_free_enc_tkt_part(context, req->ticket->enc_part2); req->ticket->enc_part2 = NULL; } if (check_valid_flag) krb5_free_keyblock_contents(context, &decrypt_key); return retval; }
OM_uint32 krb5_gss_map_name_to_any(OM_uint32 *minor_status, gss_name_t name, int authenticated, gss_buffer_t type_id, gss_any_t *output) { krb5_context context; krb5_error_code code; krb5_gss_name_t kname; char *kmodule; if (minor_status != NULL) *minor_status = 0; code = krb5_gss_init_context(&context); if (code != 0) { *minor_status = code; return GSS_S_FAILURE; } if (!kg_validate_name(name)) { *minor_status = (OM_uint32)G_VALIDATE_FAILED; krb5_free_context(context); return GSS_S_CALL_BAD_STRUCTURE|GSS_S_BAD_NAME; } kname = (krb5_gss_name_t)name; code = k5_mutex_lock(&kname->lock); if (code != 0) { *minor_status = code; return GSS_S_FAILURE; } if (kname->ad_context == NULL) { code = krb5_authdata_context_init(context, &kname->ad_context); if (code != 0) { *minor_status = code; k5_mutex_unlock(&kname->lock); krb5_free_context(context); return GSS_S_UNAVAILABLE; } } kmodule = (char *)type_id->value; if (kmodule[type_id->length] != '\0') { k5_mutex_unlock(&kname->lock); krb5_free_context(context); return GSS_S_UNAVAILABLE; } code = krb5_authdata_export_internal(context, kname->ad_context, authenticated, kmodule, (void **)output); k5_mutex_unlock(&kname->lock); krb5_free_context(context); return kg_map_name_error(minor_status, code); }
OM_uint32 krb5_gss_get_name_attribute(OM_uint32 *minor_status, gss_name_t name, gss_buffer_t attr, int *authenticated, int *complete, gss_buffer_t value, gss_buffer_t display_value, int *more) { krb5_context context; krb5_error_code code; krb5_gss_name_t kname; krb5_data kattr; krb5_boolean kauthenticated; krb5_boolean kcomplete; krb5_data kvalue; krb5_data kdisplay_value; if (minor_status != NULL) *minor_status = 0; code = krb5_gss_init_context(&context); if (code != 0) { *minor_status = code; return GSS_S_FAILURE; } if (!kg_validate_name(name)) { *minor_status = (OM_uint32)G_VALIDATE_FAILED; krb5_free_context(context); return GSS_S_CALL_BAD_STRUCTURE|GSS_S_BAD_NAME; } kname = (krb5_gss_name_t)name; code = k5_mutex_lock(&kname->lock); if (code != 0) { *minor_status = code; krb5_free_context(context); return GSS_S_FAILURE; } if (kname->ad_context == NULL) { code = krb5_authdata_context_init(context, &kname->ad_context); if (code != 0) { *minor_status = code; k5_mutex_unlock(&kname->lock); krb5_free_context(context); return GSS_S_UNAVAILABLE; } } kattr.data = (char *)attr->value; kattr.length = attr->length; kauthenticated = FALSE; kcomplete = FALSE; code = krb5_authdata_get_attribute(context, kname->ad_context, &kattr, &kauthenticated, &kcomplete, value ? &kvalue : NULL, display_value ? &kdisplay_value : NULL, more); if (code == 0) { if (value != NULL) { value->value = kvalue.data; value->length = kvalue.length; } if (authenticated != NULL) *authenticated = kauthenticated; if (complete != NULL) *complete = kcomplete; if (display_value != NULL) { display_value->value = kdisplay_value.data; display_value->length = kdisplay_value.length; } } k5_mutex_unlock(&kname->lock); krb5_free_context(context); return kg_map_name_error(minor_status, code); }