static krb5_error_code nonce_verify(krb5_context ctx, krb5_keyblock *armor_key, const krb5_data *nonce) { krb5_error_code retval; krb5_timestamp ts; krb5_data *er = NULL; if (armor_key == NULL || nonce->data == NULL) { retval = EINVAL; goto out; } /* Decode the PA-OTP-ENC-REQUEST structure. */ retval = decode_krb5_pa_otp_enc_req(nonce, &er); if (retval != 0) goto out; /* Make sure the nonce is exactly the same size as the one generated. */ if (er->length != armor_key->length + sizeof(krb5_timestamp)) goto out; /* Check to make sure the timestamp at the beginning is still valid. */ ts = load_32_be(er->data); retval = krb5_check_clockskew(ctx, ts); out: krb5_free_data(ctx, er); return retval; }
static krb5_error_code timestamp_verify(krb5_context ctx, const krb5_data *nonce) { krb5_error_code retval = EINVAL; krb5_pa_enc_ts *et = NULL; if (nonce->data == NULL) goto out; /* Decode the PA-ENC-TS-ENC structure. */ retval = decode_krb5_pa_enc_ts(nonce, &et); if (retval != 0) goto out; /* Check the clockskew. */ retval = krb5_check_clockskew(ctx, et->patimestamp); out: krb5_free_pa_enc_ts(ctx, et); return retval; }
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; }
krb5_error_code KRB5_CALLCONV krb5_rd_safe(krb5_context context, krb5_auth_context auth_context, const krb5_data *inbuf, krb5_data *outbuf, krb5_replay_data *outdata) { krb5_error_code retval; krb5_key key; krb5_replay_data replaydata; if (((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME) || (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) && (outdata == NULL)) /* Need a better error */ return KRB5_RC_REQUIRED; if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) && (auth_context->remote_addr == NULL)) return KRB5_REMOTE_ADDR_REQUIRED; if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) && (auth_context->rcache == NULL)) return KRB5_RC_REQUIRED; /* Get key */ if ((key = auth_context->recv_subkey) == NULL) key = auth_context->key; memset(&replaydata, 0, sizeof(replaydata)); retval = rd_safe_basic(context, auth_context, inbuf, key, &replaydata, outbuf); if (retval) return retval; if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) { krb5_donot_replay replay; if ((retval = krb5_check_clockskew(context, replaydata.timestamp))) goto error; if ((retval = krb5_gen_replay_name(context, auth_context->remote_addr, "_safe", &replay.client))) goto error; replay.server = ""; /* XXX */ replay.msghash = NULL; replay.cusec = replaydata.usec; replay.ctime = replaydata.timestamp; if ((retval = krb5_rc_store(context, auth_context->rcache, &replay))) { free(replay.client); goto error; } free(replay.client); } if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) { if (!k5_privsafe_check_seqnum(context, auth_context, replaydata.seq)) { retval = KRB5KRB_AP_ERR_BADORDER; goto error; } auth_context->remote_seq_number++; } if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME) || (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) { outdata->timestamp = replaydata.timestamp; outdata->usec = replaydata.usec; outdata->seq = replaydata.seq; } /* everything is ok - return data to the user */ return 0; error: free(outbuf->data); return retval; }
/* * This functions takes as input an KRB_CRED message, validates it, and * outputs the array of the forwarded credentials and replay cache information */ krb5_error_code KRB5_CALLCONV krb5_rd_cred(krb5_context context, krb5_auth_context auth_context, krb5_data *pcreddata, krb5_creds ***pppcreds, krb5_replay_data *outdata) { krb5_error_code retval = 0; krb5_replay_data replaydata; if (((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME) || (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) && (outdata == NULL)) /* Need a better error */ return KRB5_RC_REQUIRED; if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) && (auth_context->rcache == NULL)) return KRB5_RC_REQUIRED; /* * If decrypting with the subsession key fails, perhaps the * credentials are stored in the session key so try decrypting with that. */ if (auth_context->recv_subkey == NULL || (retval = krb5_rd_cred_basic(context, pcreddata, auth_context->recv_subkey, &replaydata, pppcreds))) { retval = krb5_rd_cred_basic(context, pcreddata, auth_context->key, &replaydata, pppcreds); if (retval) return retval; } if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) { krb5_donot_replay replay; if ((retval = krb5_check_clockskew(context, replaydata.timestamp))) goto error; if ((retval = krb5_gen_replay_name(context, auth_context->remote_addr, "_forw", &replay.client))) goto error; replay.server = ""; /* XXX */ replay.msghash = NULL; replay.cusec = replaydata.usec; replay.ctime = replaydata.timestamp; if ((retval = krb5_rc_store(context, auth_context->rcache, &replay))) { free(replay.client); goto error; } free(replay.client); } if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) { if (auth_context->remote_seq_number != replaydata.seq) { retval = KRB5KRB_AP_ERR_BADORDER; goto error; } auth_context->remote_seq_number++; } if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME) || (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) { outdata->timestamp = replaydata.timestamp; outdata->usec = replaydata.usec; outdata->seq = replaydata.seq; } error:; if (retval) { krb5_free_tgt_creds(context, *pppcreds); *pppcreds = NULL; } return retval; }