KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_rd_cred(krb5_context context, krb5_auth_context auth_context, krb5_data *in_data, krb5_creds ***ret_creds, krb5_replay_data *outdata) { krb5_error_code ret; size_t len; KRB_CRED cred; EncKrbCredPart enc_krb_cred_part; krb5_data enc_krb_cred_part_data; krb5_crypto crypto; size_t i; memset(&enc_krb_cred_part, 0, sizeof(enc_krb_cred_part)); krb5_data_zero(&enc_krb_cred_part_data); if ((auth_context->flags & (KRB5_AUTH_CONTEXT_RET_TIME | KRB5_AUTH_CONTEXT_RET_SEQUENCE)) && outdata == NULL) return KRB5_RC_REQUIRED; /* XXX better error, MIT returns this */ *ret_creds = NULL; ret = decode_KRB_CRED(in_data->data, in_data->length, &cred, &len); if(ret) { krb5_clear_error_message(context); return ret; } if (cred.pvno != 5) { ret = KRB5KRB_AP_ERR_BADVERSION; krb5_clear_error_message (context); goto out; } if (cred.msg_type != krb_cred) { ret = KRB5KRB_AP_ERR_MSG_TYPE; krb5_clear_error_message (context); goto out; } if (cred.enc_part.etype == ETYPE_NULL) { /* DK: MIT GSS-API Compatibility */ enc_krb_cred_part_data.length = cred.enc_part.cipher.length; enc_krb_cred_part_data.data = cred.enc_part.cipher.data; } else { /* Try both subkey and session key. * * RFC4120 claims we should use the session key, but Heimdal * before 0.8 used the remote subkey if it was send in the * auth_context. */ if (auth_context->remote_subkey) { ret = krb5_crypto_init(context, auth_context->remote_subkey, 0, &crypto); if (ret) goto out; ret = krb5_decrypt_EncryptedData(context, crypto, KRB5_KU_KRB_CRED, &cred.enc_part, &enc_krb_cred_part_data); krb5_crypto_destroy(context, crypto); } /* * If there was not subkey, or we failed using subkey, * retry using the session key */ if (auth_context->remote_subkey == NULL || ret == KRB5KRB_AP_ERR_BAD_INTEGRITY) { ret = krb5_crypto_init(context, auth_context->keyblock, 0, &crypto); if (ret) goto out; ret = krb5_decrypt_EncryptedData(context, crypto, KRB5_KU_KRB_CRED, &cred.enc_part, &enc_krb_cred_part_data); krb5_crypto_destroy(context, crypto); } if (ret) goto out; } ret = decode_EncKrbCredPart(enc_krb_cred_part_data.data, enc_krb_cred_part_data.length, &enc_krb_cred_part, &len); if (enc_krb_cred_part_data.data != cred.enc_part.cipher.data) krb5_data_free(&enc_krb_cred_part_data); if (ret) { krb5_set_error_message(context, ret, N_("Failed to decode " "encrypte credential part", "")); goto out; } /* check sender address */ if (enc_krb_cred_part.s_address && auth_context->remote_address && auth_context->remote_port) { krb5_address *a; ret = krb5_make_addrport (context, &a, auth_context->remote_address, auth_context->remote_port); if (ret) goto out; ret = compare_addrs(context, a, enc_krb_cred_part.s_address, N_("sender address is wrong " "in received creds", "")); krb5_free_address(context, a); free(a); if(ret) goto out; } /* check receiver address */ if (enc_krb_cred_part.r_address && auth_context->local_address) { if(auth_context->local_port && enc_krb_cred_part.r_address->addr_type == KRB5_ADDRESS_ADDRPORT) { krb5_address *a; ret = krb5_make_addrport (context, &a, auth_context->local_address, auth_context->local_port); if (ret) goto out; ret = compare_addrs(context, a, enc_krb_cred_part.r_address, N_("receiver address is wrong " "in received creds", "")); krb5_free_address(context, a); free(a); if(ret) goto out; } else { ret = compare_addrs(context, auth_context->local_address, enc_krb_cred_part.r_address, N_("receiver address is wrong " "in received creds", "")); if(ret) goto out; } } /* check timestamp */ if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_TIME) { krb5_timestamp sec; krb5_timeofday (context, &sec); if (enc_krb_cred_part.timestamp == NULL || enc_krb_cred_part.usec == NULL || abs(*enc_krb_cred_part.timestamp - sec) > context->max_skew) { krb5_clear_error_message (context); ret = KRB5KRB_AP_ERR_SKEW; goto out; } } if ((auth_context->flags & (KRB5_AUTH_CONTEXT_RET_TIME | KRB5_AUTH_CONTEXT_RET_SEQUENCE))) { /* if these fields are not present in the cred-part, silently return zero */ memset(outdata, 0, sizeof(*outdata)); if(enc_krb_cred_part.timestamp) outdata->timestamp = *enc_krb_cred_part.timestamp; if(enc_krb_cred_part.usec) outdata->usec = *enc_krb_cred_part.usec; if(enc_krb_cred_part.nonce) outdata->seq = *enc_krb_cred_part.nonce; } /* Convert to NULL terminated list of creds */ *ret_creds = calloc(enc_krb_cred_part.ticket_info.len + 1, sizeof(**ret_creds)); if (*ret_creds == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto out; } for (i = 0; i < enc_krb_cred_part.ticket_info.len; ++i) { KrbCredInfo *kci = &enc_krb_cred_part.ticket_info.val[i]; krb5_creds *creds; creds = calloc(1, sizeof(*creds)); if(creds == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto out; } ASN1_MALLOC_ENCODE(Ticket, creds->ticket.data, creds->ticket.length, &cred.tickets.val[i], &len, ret); if (ret) { free(creds); goto out; } if(creds->ticket.length != len) krb5_abortx(context, "internal error in ASN.1 encoder"); copy_EncryptionKey (&kci->key, &creds->session); if (kci->prealm && kci->pname) _krb5_principalname2krb5_principal (context, &creds->client, *kci->pname, *kci->prealm); if (kci->flags) creds->flags.b = *kci->flags; if (kci->authtime) creds->times.authtime = *kci->authtime; if (kci->starttime) creds->times.starttime = *kci->starttime; if (kci->endtime) creds->times.endtime = *kci->endtime; if (kci->renew_till) creds->times.renew_till = *kci->renew_till; if (kci->srealm && kci->sname) _krb5_principalname2krb5_principal (context, &creds->server, *kci->sname, *kci->srealm); if (kci->caddr) krb5_copy_addresses (context, kci->caddr, &creds->addresses); (*ret_creds)[i] = creds; } (*ret_creds)[i] = NULL; free_KRB_CRED (&cred); free_EncKrbCredPart(&enc_krb_cred_part); return 0; out: free_EncKrbCredPart(&enc_krb_cred_part); free_KRB_CRED (&cred); if(*ret_creds) { for(i = 0; (*ret_creds)[i]; i++) krb5_free_creds(context, (*ret_creds)[i]); free(*ret_creds); *ret_creds = NULL; } return ret; }
krb5_error_code KRB5_LIB_FUNCTION krb5_rd_rep(krb5_context context, krb5_auth_context auth_context, const krb5_data *inbuf, krb5_ap_rep_enc_part **repl) { krb5_error_code ret; AP_REP ap_rep; size_t len; krb5_data data; krb5_crypto crypto; krb5_data_zero (&data); ret = 0; ret = decode_AP_REP(inbuf->data, inbuf->length, &ap_rep, &len); if (ret) return ret; if (ap_rep.pvno != 5) { ret = KRB5KRB_AP_ERR_BADVERSION; krb5_clear_error_message (context); goto out; } if (ap_rep.msg_type != krb_ap_rep) { ret = KRB5KRB_AP_ERR_MSG_TYPE; krb5_clear_error_message (context); goto out; } ret = krb5_crypto_init(context, auth_context->keyblock, 0, &crypto); if (ret) goto out; ret = krb5_decrypt_EncryptedData (context, crypto, KRB5_KU_AP_REQ_ENC_PART, &ap_rep.enc_part, &data); krb5_crypto_destroy(context, crypto); if (ret) goto out; *repl = malloc(sizeof(**repl)); if (*repl == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto out; } ret = krb5_decode_EncAPRepPart(context, data.data, data.length, *repl, &len); if (ret) return ret; if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_TIME) { if ((*repl)->ctime != auth_context->authenticator->ctime || (*repl)->cusec != auth_context->authenticator->cusec) { krb5_free_ap_rep_enc_part(context, *repl); *repl = NULL; ret = KRB5KRB_AP_ERR_MUT_FAIL; krb5_clear_error_message (context); goto out; } } if ((*repl)->seq_number) krb5_auth_con_setremoteseqnumber(context, auth_context, *((*repl)->seq_number)); if ((*repl)->subkey) krb5_auth_con_setremotesubkey(context, auth_context, (*repl)->subkey); out: krb5_data_free (&data); free_AP_REP (&ap_rep); return ret; }
static krb5_error_code pk_mk_pa_reply_enckey(krb5_context context, krb5_kdc_configuration *config, pk_client_params *cp, const KDC_REQ *req, const krb5_data *req_buffer, krb5_keyblock *reply_key, ContentInfo *content_info, hx509_cert *kdc_cert) { const heim_oid *envelopedAlg = NULL, *sdAlg = NULL, *evAlg = NULL; krb5_error_code ret; krb5_data buf, signed_data; size_t size = 0; int do_win2k = 0; krb5_data_zero(&buf); krb5_data_zero(&signed_data); *kdc_cert = NULL; /* * If the message client is a win2k-type but it send pa data * 09-binding it expects a IETF (checksum) reply so there can be * no replay attacks. */ switch (cp->type) { case PKINIT_WIN2K: { int i = 0; if (_kdc_find_padata(req, &i, KRB5_PADATA_PK_AS_09_BINDING) == NULL && config->pkinit_require_binding == 0) { do_win2k = 1; } sdAlg = &asn1_oid_id_pkcs7_data; evAlg = &asn1_oid_id_pkcs7_data; envelopedAlg = &asn1_oid_id_rsadsi_des_ede3_cbc; break; } case PKINIT_27: sdAlg = &asn1_oid_id_pkrkeydata; evAlg = &asn1_oid_id_pkcs7_signedData; break; default: krb5_abortx(context, "internal pkinit error"); } if (do_win2k) { ReplyKeyPack_Win2k kp; memset(&kp, 0, sizeof(kp)); ret = copy_EncryptionKey(reply_key, &kp.replyKey); if (ret) { krb5_clear_error_message(context); goto out; } kp.nonce = cp->nonce; ASN1_MALLOC_ENCODE(ReplyKeyPack_Win2k, buf.data, buf.length, &kp, &size,ret); free_ReplyKeyPack_Win2k(&kp); } else { krb5_crypto ascrypto; ReplyKeyPack kp; memset(&kp, 0, sizeof(kp)); ret = copy_EncryptionKey(reply_key, &kp.replyKey); if (ret) { krb5_clear_error_message(context); goto out; } ret = krb5_crypto_init(context, reply_key, 0, &ascrypto); if (ret) { krb5_clear_error_message(context); goto out; } ret = krb5_create_checksum(context, ascrypto, 6, 0, req_buffer->data, req_buffer->length, &kp.asChecksum); if (ret) { krb5_clear_error_message(context); goto out; } ret = krb5_crypto_destroy(context, ascrypto); if (ret) { krb5_clear_error_message(context); goto out; } ASN1_MALLOC_ENCODE(ReplyKeyPack, buf.data, buf.length, &kp, &size,ret); free_ReplyKeyPack(&kp); } if (ret) { krb5_set_error_message(context, ret, "ASN.1 encoding of ReplyKeyPack " "failed (%d)", ret); goto out; } if (buf.length != size) krb5_abortx(context, "Internal ASN.1 encoder error"); { hx509_query *q; hx509_cert cert; ret = hx509_query_alloc(context->hx509ctx, &q); if (ret) goto out; hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY); if (config->pkinit_kdc_friendly_name) hx509_query_match_friendly_name(q, config->pkinit_kdc_friendly_name); ret = hx509_certs_find(context->hx509ctx, kdc_identity->certs, q, &cert); hx509_query_free(context->hx509ctx, q); if (ret) goto out; ret = hx509_cms_create_signed_1(context->hx509ctx, 0, sdAlg, buf.data, buf.length, NULL, cert, cp->peer, cp->client_anchors, kdc_identity->certpool, &signed_data); *kdc_cert = cert; } krb5_data_free(&buf); if (ret) goto out; if (cp->type == PKINIT_WIN2K) { ret = hx509_cms_wrap_ContentInfo(&asn1_oid_id_pkcs7_signedData, &signed_data, &buf); if (ret) goto out; krb5_data_free(&signed_data); signed_data = buf; } ret = hx509_cms_envelope_1(context->hx509ctx, HX509_CMS_EV_NO_KU_CHECK, cp->cert, signed_data.data, signed_data.length, envelopedAlg, evAlg, &buf); if (ret) goto out; ret = _krb5_pk_mk_ContentInfo(context, &buf, &asn1_oid_id_pkcs7_envelopedData, content_info); out: if (ret && *kdc_cert) { hx509_cert_free(*kdc_cert); *kdc_cert = NULL; } krb5_data_free(&buf); krb5_data_free(&signed_data); return ret; }
static krb5_error_code get_cred_kdc(krb5_context context, krb5_ccache id, krb5_kdc_flags flags, krb5_addresses *addresses, krb5_creds *in_creds, krb5_creds *krbtgt, krb5_principal impersonate_principal, Ticket *second_ticket, krb5_creds *out_creds) { TGS_REQ req; krb5_data enc; krb5_data resp; krb5_kdc_rep rep; KRB_ERROR error; krb5_error_code ret; unsigned nonce; krb5_keyblock *subkey = NULL; size_t len = 0; Ticket second_ticket_data; METHOD_DATA padata; krb5_data_zero(&resp); krb5_data_zero(&enc); padata.val = NULL; padata.len = 0; krb5_generate_random_block(&nonce, sizeof(nonce)); nonce &= 0xffffffff; if(flags.b.enc_tkt_in_skey && second_ticket == NULL){ ret = decode_Ticket(in_creds->second_ticket.data, in_creds->second_ticket.length, &second_ticket_data, &len); if(ret) return ret; second_ticket = &second_ticket_data; } if (impersonate_principal) { krb5_crypto crypto; PA_S4U2Self self; krb5_data data; void *buf; size_t size = 0; self.name = impersonate_principal->name; self.realm = impersonate_principal->realm; self.auth = estrdup("Kerberos"); ret = _krb5_s4u2self_to_checksumdata(context, &self, &data); if (ret) { free(self.auth); goto out; } ret = krb5_crypto_init(context, &krbtgt->session, 0, &crypto); if (ret) { free(self.auth); krb5_data_free(&data); goto out; } ret = krb5_create_checksum(context, crypto, KRB5_KU_OTHER_CKSUM, 0, data.data, data.length, &self.cksum); krb5_crypto_destroy(context, crypto); krb5_data_free(&data); if (ret) { free(self.auth); goto out; } ASN1_MALLOC_ENCODE(PA_S4U2Self, buf, len, &self, &size, ret); free(self.auth); free_Checksum(&self.cksum); if (ret) goto out; if (len != size) krb5_abortx(context, "internal asn1 error"); ret = krb5_padata_add(context, &padata, KRB5_PADATA_FOR_USER, buf, len); if (ret) goto out; } ret = init_tgs_req (context, id, addresses, flags, second_ticket, in_creds, krbtgt, nonce, &padata, &subkey, &req); if (ret) goto out; ASN1_MALLOC_ENCODE(TGS_REQ, enc.data, enc.length, &req, &len, ret); if (ret) goto out; if(enc.length != len) krb5_abortx(context, "internal error in ASN.1 encoder"); /* don't free addresses */ req.req_body.addresses = NULL; free_TGS_REQ(&req); /* * Send and receive */ { krb5_sendto_ctx stctx; ret = krb5_sendto_ctx_alloc(context, &stctx); if (ret) return ret; krb5_sendto_ctx_set_func(stctx, _krb5_kdc_retry, NULL); ret = krb5_sendto_context (context, stctx, &enc, krbtgt->server->name.name_string.val[1], &resp); krb5_sendto_ctx_free(context, stctx); } if(ret) goto out; memset(&rep, 0, sizeof(rep)); if(decode_TGS_REP(resp.data, resp.length, &rep.kdc_rep, &len) == 0) { unsigned eflags = 0; ret = krb5_copy_principal(context, in_creds->client, &out_creds->client); if(ret) goto out2; ret = krb5_copy_principal(context, in_creds->server, &out_creds->server); if(ret) goto out2; /* this should go someplace else */ out_creds->times.endtime = in_creds->times.endtime; /* XXX should do better testing */ if (flags.b.constrained_delegation || impersonate_principal) eflags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH; ret = _krb5_extract_ticket(context, &rep, out_creds, &krbtgt->session, NULL, 0, &krbtgt->addresses, nonce, eflags, NULL, decrypt_tkt_with_subkey, subkey); out2: krb5_free_kdc_rep(context, &rep); } else if(krb5_rd_error(context, &resp, &error) == 0) { ret = krb5_error_from_rd_error(context, &error, in_creds); krb5_free_error_contents(context, &error); } else if(resp.length > 0 && ((char*)resp.data)[0] == 4) { ret = KRB5KRB_AP_ERR_V4_REPLY; krb5_clear_error_message(context); } else { ret = KRB5KRB_AP_ERR_MSG_TYPE; krb5_clear_error_message(context); } out: if (second_ticket == &second_ticket_data) free_Ticket(&second_ticket_data); free_METHOD_DATA(&padata); krb5_data_free(&resp); krb5_data_free(&enc); if(subkey) krb5_free_keyblock(context, subkey); return ret; }
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL _krb5_build_authenticator (krb5_context context, krb5_auth_context auth_context, krb5_enctype enctype, krb5_creds *cred, Checksum *cksum, krb5_data *result, krb5_key_usage usage) { Authenticator auth; u_char *buf = NULL; size_t buf_size; size_t len = 0; krb5_error_code ret; krb5_crypto crypto; memset(&auth, 0, sizeof(auth)); auth.authenticator_vno = 5; copy_Realm(&cred->client->realm, &auth.crealm); copy_PrincipalName(&cred->client->name, &auth.cname); krb5_us_timeofday (context, &auth.ctime, &auth.cusec); ret = krb5_auth_con_getlocalsubkey(context, auth_context, &auth.subkey); if(ret) goto fail; if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) { if(auth_context->local_seqnumber == 0) krb5_generate_seq_number (context, &cred->session, &auth_context->local_seqnumber); ALLOC(auth.seq_number, 1); if(auth.seq_number == NULL) { ret = ENOMEM; goto fail; } *auth.seq_number = auth_context->local_seqnumber; } else auth.seq_number = NULL; auth.authorization_data = NULL; if (cksum) { ALLOC(auth.cksum, 1); if (auth.cksum == NULL) { ret = ENOMEM; goto fail; } ret = copy_Checksum(cksum, auth.cksum); if (ret) goto fail; if (auth.cksum->cksumtype == CKSUMTYPE_GSSAPI) { /* * This is not GSS-API specific, we only enable it for * GSS for now */ ret = make_etypelist(context, &auth.authorization_data); if (ret) goto fail; } } /* XXX - Copy more to auth_context? */ auth_context->authenticator->ctime = auth.ctime; auth_context->authenticator->cusec = auth.cusec; ASN1_MALLOC_ENCODE(Authenticator, buf, buf_size, &auth, &len, ret); if (ret) goto fail; if(buf_size != len) krb5_abortx(context, "internal error in ASN.1 encoder"); ret = krb5_crypto_init(context, &cred->session, enctype, &crypto); if (ret) goto fail; ret = krb5_encrypt (context, crypto, usage /* KRB5_KU_AP_REQ_AUTH */, buf, len, result); krb5_crypto_destroy(context, crypto); if (ret) goto fail; fail: free_Authenticator (&auth); free (buf); return ret; }
static OM_uint32 gsskrb5_acceptor_start(OM_uint32 * minor_status, gsskrb5_ctx ctx, krb5_context context, const gss_cred_id_t acceptor_cred_handle, const gss_buffer_t input_token_buffer, const gss_channel_bindings_t input_chan_bindings, gss_name_t * src_name, gss_OID * mech_type, gss_buffer_t output_token, OM_uint32 * ret_flags, OM_uint32 * time_rec, gss_cred_id_t * delegated_cred_handle) { krb5_error_code kret; OM_uint32 ret = GSS_S_COMPLETE; krb5_data indata; krb5_flags ap_options; krb5_keytab keytab = NULL; int is_cfx = 0; const gsskrb5_cred acceptor_cred = (gsskrb5_cred)acceptor_cred_handle; krb5_boolean is_hostbased_service = FALSE; /* * We may, or may not, have an escapsulation. */ ret = _gsskrb5_decapsulate (minor_status, input_token_buffer, &indata, "\x01\x00", ctx->mech); if (ret) { /* Assume that there is no OID wrapping. */ indata.length = input_token_buffer->length; indata.data = input_token_buffer->value; } /* * We need to get our keytab */ if (acceptor_cred == NULL) { if (_gsskrb5_keytab != NULL) keytab = _gsskrb5_keytab; } else if (acceptor_cred->keytab != NULL) { keytab = acceptor_cred->keytab; } is_hostbased_service = (acceptor_cred && acceptor_cred->principal && krb5_principal_is_gss_hostbased_service(context, acceptor_cred->principal)); /* * We need to check the ticket and create the AP-REP packet */ { krb5_rd_req_in_ctx in = NULL; krb5_rd_req_out_ctx out = NULL; krb5_principal server = NULL; if (acceptor_cred && !is_hostbased_service) server = acceptor_cred->principal; kret = krb5_rd_req_in_ctx_alloc(context, &in); if (kret == 0) kret = krb5_rd_req_in_set_keytab(context, in, keytab); if (kret) { if (in) krb5_rd_req_in_ctx_free(context, in); *minor_status = kret; return GSS_S_FAILURE; } kret = krb5_rd_req_ctx(context, &ctx->auth_context, &indata, server, in, &out); krb5_rd_req_in_ctx_free(context, in); if (ret && _gss_mg_log_level(5)) { const char *e = krb5_get_error_message(context, ret); char *s = NULL; if (server) (void)krb5_unparse_name(context, server, &s); _gss_mg_log(5, "gss-asc: rd_req (server: %s) failed with: %d: %s", s ? s : "<not specified>", ret, e); krb5_free_error_message(context, e); if (s) krb5_xfree(s); } switch (kret) { case 0: break; case KRB5KRB_AP_ERR_SKEW: case KRB5KRB_AP_ERR_TKT_NYV: /* * No reply in non-MUTUAL mode, but we don't know that its * non-MUTUAL mode yet, thats inside the 8003 checksum, so * lets only send the error token on clock skew, that * limit when send error token for non-MUTUAL. */ return send_error_token(minor_status, context, kret, server, &indata, ctx->mech, output_token); case KRB5KRB_AP_ERR_MODIFIED: case KRB5_KT_NOTFOUND: case KRB5_KT_END: /* * If the error is on the keytab entry missing or bad * decryption, lets assume that the keytab version was * wrong and tell the client that. */ return send_error_token(minor_status, context, KRB5KRB_AP_ERR_MODIFIED, server, NULL, ctx->mech, output_token); default: *minor_status = kret; return GSS_S_FAILURE; } /* * we need to remember some data on the context_handle. */ kret = krb5_rd_req_out_get_ap_req_options(context, out, &ap_options); if (kret == 0) kret = krb5_rd_req_out_get_ticket(context, out, &ctx->ticket); if (kret == 0) kret = krb5_rd_req_out_get_keyblock(context, out, &ctx->service_keyblock); if (kret == 0) { int flags; flags = krb5_rd_req_out_get_flags(context, out); if (flags & KRB5_RD_REQ_OUT_PAC_VALID) ctx->more_flags |= PAC_VALID; } if (kret == 0 && is_hostbased_service) { krb5_principal sp = ctx->ticket->server; if (sp->name.name_string.len < 1 || strcmp(sp->name.name_string.val[0], acceptor_cred->principal->name.name_string.val[0]) != 0) { kret = KRB5KRB_AP_WRONG_PRINC; krb5_set_error_message(context, ret, "Expecting service %s but got %s", acceptor_cred->principal->name.name_string.val[0], sp->name.name_string.val[0]); } } ctx->endtime = ctx->ticket->ticket.endtime; krb5_rd_req_out_ctx_free(context, out); if (kret) { ret = GSS_S_FAILURE; *minor_status = kret; return ret; } } /* * We need to copy the principal names to the context and the * calling layer. */ kret = krb5_copy_principal(context, ctx->ticket->client, &ctx->source); if (kret) { *minor_status = kret; return GSS_S_FAILURE; } kret = krb5_copy_principal(context, ctx->ticket->server, &ctx->target); if (kret) { ret = GSS_S_FAILURE; *minor_status = kret; return ret; } /* * We need to setup some compat stuff, this assumes that * context_handle->target is already set. */ ret = _gss_DES3_get_mic_compat(minor_status, ctx, context); if (ret) return ret; if (src_name != NULL) { kret = krb5_copy_principal (context, ctx->ticket->client, (gsskrb5_name*)src_name); if (kret) { ret = GSS_S_FAILURE; *minor_status = kret; return ret; } } /* * We need to get the flags out of the 8003 checksum. */ { krb5_authenticator authenticator; kret = krb5_auth_con_getauthenticator(context, ctx->auth_context, &authenticator); if(kret) { ret = GSS_S_FAILURE; *minor_status = kret; return ret; } if (authenticator->cksum == NULL) { krb5_free_authenticator(context, &authenticator); *minor_status = 0; return GSS_S_BAD_BINDINGS; } if (authenticator->cksum->cksumtype == CKSUMTYPE_GSSAPI) { krb5_data finished_data; krb5_crypto crypto = NULL; if (ctx->auth_context->remote_subkey) { kret = krb5_crypto_init(context, ctx->auth_context->remote_subkey, 0, &crypto); if (kret) { *minor_status = kret; return GSS_S_FAILURE; } } krb5_data_zero(&finished_data); ret = _gsskrb5_verify_8003_checksum(minor_status, context, crypto, input_chan_bindings, authenticator->cksum, &ctx->flags, &ctx->fwd_data, &finished_data); krb5_free_authenticator(context, &authenticator); if (ret) { krb5_crypto_destroy(context, crypto); return ret; } if (finished_data.length) { GSS_KRB5_FINISHED finished; krb5_data pkt; memset(&finished, 0, sizeof(finished)); if (ctx->messages == NULL) { krb5_crypto_destroy(context, crypto); krb5_data_free(&finished_data); *minor_status = 0; return GSS_S_BAD_SIG; } kret = krb5_storage_to_data(ctx->messages, &pkt); if (kret) { krb5_crypto_destroy(context, crypto); krb5_data_free(&finished_data); *minor_status = kret; return GSS_S_FAILURE; } if (ctx->auth_context->remote_subkey == NULL) { krb5_crypto_destroy(context, crypto); krb5_data_free(&finished_data); krb5_data_free(&pkt); *minor_status = 0; return GSS_S_BAD_SIG; } kret = decode_GSS_KRB5_FINISHED(finished_data.data, finished_data.length, &finished, NULL); krb5_data_free(&finished_data); if (kret) { krb5_crypto_destroy(context, crypto); krb5_data_free(&pkt); *minor_status = kret; return GSS_S_FAILURE; } kret = krb5_verify_checksum(context, crypto, KRB5_KU_FINISHED, pkt.data, pkt.length, &finished.gss_mic); free_GSS_KRB5_FINISHED(&finished); krb5_data_free(&pkt); if (kret) { krb5_crypto_destroy(context, crypto); *minor_status = kret; return GSS_S_FAILURE; } } krb5_crypto_destroy(context, crypto); } else { krb5_crypto crypto; kret = krb5_crypto_init(context, ctx->auth_context->keyblock, 0, &crypto); if(kret) { krb5_free_authenticator(context, &authenticator); ret = GSS_S_FAILURE; *minor_status = kret; return ret; } /* * Windows accepts Samba3's use of a kerberos, rather than * GSSAPI checksum here */ kret = krb5_verify_checksum(context, crypto, KRB5_KU_AP_REQ_AUTH_CKSUM, NULL, 0, authenticator->cksum); krb5_free_authenticator(context, &authenticator); krb5_crypto_destroy(context, crypto); if(kret) { ret = GSS_S_BAD_SIG; *minor_status = kret; return ret; } /* * Samba style get some flags (but not DCE-STYLE), use * ap_options to guess the mutual flag. */ ctx->flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG; if (ap_options & AP_OPTS_MUTUAL_REQUIRED) ctx->flags |= GSS_C_MUTUAL_FLAG; } } if(ctx->flags & GSS_C_MUTUAL_FLAG) { krb5_data outbuf; int use_subkey = 0; _gsskrb5i_is_cfx(context, ctx, 1); is_cfx = (ctx->more_flags & IS_CFX); if (is_cfx || (ap_options & AP_OPTS_USE_SUBKEY)) { use_subkey = 1; } else { krb5_keyblock *rkey; /* * If there is a initiator subkey, copy that to acceptor * subkey to match Windows behavior */ kret = krb5_auth_con_getremotesubkey(context, ctx->auth_context, &rkey); if (kret == 0) { kret = krb5_auth_con_setlocalsubkey(context, ctx->auth_context, rkey); if (kret == 0) use_subkey = 1; krb5_free_keyblock(context, rkey); } } if (use_subkey) { ctx->gk5c.flags |= GK5C_ACCEPTOR_SUBKEY; krb5_auth_con_addflags(context, ctx->auth_context, KRB5_AUTH_CONTEXT_USE_SUBKEY, NULL); } kret = krb5_mk_rep(context, ctx->auth_context, &outbuf); if (kret) { *minor_status = kret; return GSS_S_FAILURE; } if (IS_DCE_STYLE(ctx)) { output_token->length = outbuf.length; output_token->value = outbuf.data; } else { ret = _gsskrb5_encapsulate(minor_status, &outbuf, output_token, "\x02\x00", ctx->mech); krb5_data_free (&outbuf); if (ret) return ret; } } ctx->flags |= GSS_C_TRANS_FLAG; /* Remember the flags */ ctx->endtime = ctx->ticket->ticket.endtime; ctx->more_flags |= OPEN; if (mech_type) *mech_type = ctx->mech; if (time_rec) { ret = _gsskrb5_lifetime_left(minor_status, context, ctx->endtime, time_rec); if (ret) { return ret; } } /* * When GSS_C_DCE_STYLE is in use, we need ask for a AP-REP from * the client. */ if (IS_DCE_STYLE(ctx)) { /* * Return flags to caller, but we haven't processed * delgations yet */ if (ret_flags) *ret_flags = (ctx->flags & ~GSS_C_DELEG_FLAG); ctx->acceptor_state = acceptor_wait_for_dcestyle; return GSS_S_CONTINUE_NEEDED; } ret = gsskrb5_acceptor_ready(minor_status, ctx, context, delegated_cred_handle); if (ret_flags) *ret_flags = ctx->flags; return ret; }
krb5_error_code _kdc_do_524(krb5_context context, krb5_kdc_configuration *config, const Ticket *t, krb5_data *reply, const char *from, struct sockaddr *addr) { krb5_error_code ret = 0; krb5_crypto crypto; hdb_entry_ex *server = NULL; Key *skey; krb5_data et_data; EncTicketPart et; EncryptedData ticket; krb5_storage *sp; char *spn = NULL; unsigned char buf[MAX_KTXT_LEN + 4 * 4]; size_t len; int kvno = 0; if(!config->enable_524) { ret = KRB5KDC_ERR_POLICY; kdc_log(context, config, 0, "Rejected ticket conversion request from %s", from); goto out; } ret = fetch_server (context, config, t, &spn, &server, from); if (ret) { goto out; } ret = hdb_enctype2key(context, &server->entry, t->enc_part.etype, &skey); if(ret){ kdc_log(context, config, 0, "No suitable key found for server (%s) from %s", spn, from); goto out; } ret = krb5_crypto_init(context, &skey->key, 0, &crypto); if (ret) { kdc_log(context, config, 0, "krb5_crypto_init failed: %s", krb5_get_err_text(context, ret)); goto out; } ret = krb5_decrypt_EncryptedData (context, crypto, KRB5_KU_TICKET, &t->enc_part, &et_data); krb5_crypto_destroy(context, crypto); if(ret){ kdc_log(context, config, 0, "Failed to decrypt ticket from %s for %s", from, spn); goto out; } ret = krb5_decode_EncTicketPart(context, et_data.data, et_data.length, &et, &len); krb5_data_free(&et_data); if(ret){ kdc_log(context, config, 0, "Failed to decode ticket from %s for %s", from, spn); goto out; } ret = log_524 (context, config, &et, from, spn); if (ret) { free_EncTicketPart(&et); goto out; } ret = verify_flags (context, config, &et, spn); if (ret) { free_EncTicketPart(&et); goto out; } ret = set_address (context, config, &et, addr, from); if (ret) { free_EncTicketPart(&et); goto out; } ret = encode_524_response(context, config, spn, et, t, server, &ticket, &kvno); free_EncTicketPart(&et); out: /* make reply */ memset(buf, 0, sizeof(buf)); sp = krb5_storage_from_mem(buf, sizeof(buf)); if (sp) { krb5_store_int32(sp, ret); if(ret == 0){ krb5_store_int32(sp, kvno); krb5_store_data(sp, ticket.cipher); /* Aargh! This is coded as a KTEXT_ST. */ krb5_storage_seek(sp, MAX_KTXT_LEN - ticket.cipher.length, SEEK_CUR); krb5_store_int32(sp, 0); /* mbz */ free_EncryptedData(&ticket); } ret = krb5_storage_to_data(sp, reply); reply->length = krb5_storage_seek(sp, 0, SEEK_CUR); krb5_storage_free(sp); } else krb5_data_zero(reply); if(spn) free(spn); if(server) _kdc_free_ent (context, server); return ret; }
krb5_error_code _kdc_as_rep(krb5_context context, krb5_kdc_configuration *config, KDC_REQ *req, const krb5_data *req_buffer, krb5_data *reply, const char *from, struct sockaddr *from_addr, int datagram_reply) { KDC_REQ_BODY *b = &req->req_body; AS_REP rep; KDCOptions f = b->kdc_options; hdb_entry_ex *client = NULL, *server = NULL; HDB *clientdb; krb5_enctype setype, sessionetype; krb5_data e_data; EncTicketPart et; EncKDCRepPart ek; krb5_principal client_princ = NULL, server_princ = NULL; char *client_name = NULL, *server_name = NULL; krb5_error_code ret = 0; const char *e_text = NULL; krb5_crypto crypto; Key *ckey, *skey; EncryptionKey *reply_key = NULL, session_key; int flags = HDB_F_FOR_AS_REQ; #ifdef PKINIT pk_client_params *pkp = NULL; #endif memset(&rep, 0, sizeof(rep)); memset(&session_key, 0, sizeof(session_key)); krb5_data_zero(&e_data); ALLOC(rep.padata); rep.padata->len = 0; rep.padata->val = NULL; if (f.canonicalize) flags |= HDB_F_CANON; if(b->sname == NULL){ ret = KRB5KRB_ERR_GENERIC; e_text = "No server in request"; } else{ ret = _krb5_principalname2krb5_principal (context, &server_princ, *(b->sname), b->realm); if (ret == 0) ret = krb5_unparse_name(context, server_princ, &server_name); } if (ret) { kdc_log(context, config, 0, "AS-REQ malformed server name from %s", from); goto out; } if(b->cname == NULL){ ret = KRB5KRB_ERR_GENERIC; e_text = "No client in request"; } else { ret = _krb5_principalname2krb5_principal (context, &client_princ, *(b->cname), b->realm); if (ret) goto out; ret = krb5_unparse_name(context, client_princ, &client_name); } if (ret) { kdc_log(context, config, 0, "AS-REQ malformed client name from %s", from); goto out; } kdc_log(context, config, 0, "AS-REQ %s from %s for %s", client_name, from, server_name); /* * */ if (_kdc_is_anonymous(context, client_princ)) { if (!b->kdc_options.request_anonymous) { kdc_log(context, config, 0, "Anonymous ticket w/o anonymous flag"); ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; goto out; } } else if (b->kdc_options.request_anonymous) { kdc_log(context, config, 0, "Request for a anonymous ticket with non " "anonymous client name: %s", client_name); ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; goto out; } /* * */ ret = _kdc_db_fetch(context, config, client_princ, HDB_F_GET_CLIENT | flags, NULL, &clientdb, &client); if(ret == HDB_ERR_NOT_FOUND_HERE) { kdc_log(context, config, 5, "client %s does not have secrets at this KDC, need to proxy", client_name); goto out; } else if (ret == HDB_ERR_WRONG_REALM) { char *fixed_client_name = NULL; ret = krb5_unparse_name(context, client->entry.principal, &fixed_client_name); if (ret) { goto out; } kdc_log(context, config, 0, "WRONG_REALM - %s -> %s", client_name, fixed_client_name); free(fixed_client_name); ret = krb5_mk_error_ext(context, KRB5_KDC_ERR_WRONG_REALM, NULL, /* e_text */ NULL, /* e_data */ server_princ, NULL, /* client_name */ &client->entry.principal->realm, NULL, /* client_time */ NULL, /* client_usec */ reply); goto out; } else if(ret){ const char *msg = krb5_get_error_message(context, ret); kdc_log(context, config, 0, "UNKNOWN -- %s: %s", client_name, msg); krb5_free_error_message(context, msg); ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; goto out; } ret = _kdc_db_fetch(context, config, server_princ, HDB_F_GET_SERVER|HDB_F_GET_KRBTGT | flags, NULL, NULL, &server); if(ret == HDB_ERR_NOT_FOUND_HERE) { kdc_log(context, config, 5, "target %s does not have secrets at this KDC, need to proxy", server_name); goto out; } else if(ret){ const char *msg = krb5_get_error_message(context, ret); kdc_log(context, config, 0, "UNKNOWN -- %s: %s", server_name, msg); krb5_free_error_message(context, msg); ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; goto out; } memset(&et, 0, sizeof(et)); memset(&ek, 0, sizeof(ek)); /* * Select a session enctype from the list of the crypto system * supported enctypes that is supported by the client and is one of * the enctype of the enctype of the service (likely krbtgt). * * The latter is used as a hint of what enctypes all KDC support, * to make sure a newer version of KDC won't generate a session * enctype that an older version of a KDC in the same realm can't * decrypt. */ ret = _kdc_find_etype(context, config->as_use_strongest_session_key, FALSE, client, b->etype.val, b->etype.len, &sessionetype, NULL); if (ret) { kdc_log(context, config, 0, "Client (%s) from %s has no common enctypes with KDC " "to use for the session key", client_name, from); goto out; } /* * But if the KDC admin is paranoid and doesn't want to have "not * the best" enctypes on the krbtgt, lets save the best pick from * the client list and hope that that will work for any other * KDCs. */ /* * Pre-auth processing */ if(req->padata){ int i; const PA_DATA *pa; int found_pa = 0; log_patypes(context, config, req->padata); #ifdef PKINIT kdc_log(context, config, 5, "Looking for PKINIT pa-data -- %s", client_name); e_text = "No PKINIT PA found"; i = 0; pa = _kdc_find_padata(req, &i, KRB5_PADATA_PK_AS_REQ); if (pa == NULL) { i = 0; pa = _kdc_find_padata(req, &i, KRB5_PADATA_PK_AS_REQ_WIN); } if (pa) { char *client_cert = NULL; ret = _kdc_pk_rd_padata(context, config, req, pa, client, &pkp); if (ret) { ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; kdc_log(context, config, 5, "Failed to decode PKINIT PA-DATA -- %s", client_name); goto ts_enc; } if (ret == 0 && pkp == NULL) goto ts_enc; ret = _kdc_pk_check_client(context, config, clientdb, client, pkp, &client_cert); if (ret) { e_text = "PKINIT certificate not allowed to " "impersonate principal"; _kdc_pk_free_client_param(context, pkp); kdc_log(context, config, 0, "%s", e_text); pkp = NULL; goto out; } found_pa = 1; et.flags.pre_authent = 1; kdc_log(context, config, 0, "PKINIT pre-authentication succeeded -- %s using %s", client_name, client_cert); free(client_cert); if (pkp) goto preauth_done; } ts_enc: #endif if (client->entry.flags.locked_out) { ret = KRB5KDC_ERR_CLIENT_REVOKED; kdc_log(context, config, 0, "Client (%s) is locked out", client_name); goto out; } kdc_log(context, config, 5, "Looking for ENC-TS pa-data -- %s", client_name); i = 0; e_text = "No ENC-TS found"; while((pa = _kdc_find_padata(req, &i, KRB5_PADATA_ENC_TIMESTAMP))){ krb5_data ts_data; PA_ENC_TS_ENC p; size_t len; EncryptedData enc_data; Key *pa_key; char *str; found_pa = 1; if (b->kdc_options.request_anonymous) { ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; kdc_log(context, config, 0, "ENC-TS doesn't support anon"); goto out; } ret = decode_EncryptedData(pa->padata_value.data, pa->padata_value.length, &enc_data, &len); if (ret) { ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; kdc_log(context, config, 5, "Failed to decode PA-DATA -- %s", client_name); goto out; } ret = hdb_enctype2key(context, &client->entry, enc_data.etype, &pa_key); if(ret){ char *estr; e_text = "No key matches pa-data"; ret = KRB5KDC_ERR_ETYPE_NOSUPP; if(krb5_enctype_to_string(context, enc_data.etype, &estr)) estr = NULL; if(estr == NULL) kdc_log(context, config, 5, "No client key matching pa-data (%d) -- %s", enc_data.etype, client_name); else kdc_log(context, config, 5, "No client key matching pa-data (%s) -- %s", estr, client_name); free(estr); free_EncryptedData(&enc_data); continue; } try_next_key: ret = krb5_crypto_init(context, &pa_key->key, 0, &crypto); if (ret) { const char *msg = krb5_get_error_message(context, ret); kdc_log(context, config, 0, "krb5_crypto_init failed: %s", msg); krb5_free_error_message(context, msg); free_EncryptedData(&enc_data); continue; } ret = krb5_decrypt_EncryptedData (context, crypto, KRB5_KU_PA_ENC_TIMESTAMP, &enc_data, &ts_data); krb5_crypto_destroy(context, crypto); /* * Since the user might have several keys with the same * enctype but with diffrent salting, we need to try all * the keys with the same enctype. */ if(ret){ krb5_error_code ret2; const char *msg = krb5_get_error_message(context, ret); ret2 = krb5_enctype_to_string(context, pa_key->key.keytype, &str); if (ret2) str = NULL; kdc_log(context, config, 5, "Failed to decrypt PA-DATA -- %s " "(enctype %s) error %s", client_name, str ? str : "unknown enctype", msg); krb5_free_error_message(context, msg); free(str); if(hdb_next_enctype2key(context, &client->entry, enc_data.etype, &pa_key) == 0) goto try_next_key; e_text = "Failed to decrypt PA-DATA"; free_EncryptedData(&enc_data); if (clientdb->hdb_auth_status) (clientdb->hdb_auth_status)(context, clientdb, client, HDB_AUTH_WRONG_PASSWORD); ret = KRB5KDC_ERR_PREAUTH_FAILED; continue; } free_EncryptedData(&enc_data); ret = decode_PA_ENC_TS_ENC(ts_data.data, ts_data.length, &p, &len); krb5_data_free(&ts_data); if(ret){ e_text = "Failed to decode PA-ENC-TS-ENC"; ret = KRB5KDC_ERR_PREAUTH_FAILED; kdc_log(context, config, 5, "Failed to decode PA-ENC-TS_ENC -- %s", client_name); continue; } free_PA_ENC_TS_ENC(&p); if (abs(kdc_time - p.patimestamp) > context->max_skew) { char client_time[100]; krb5_format_time(context, p.patimestamp, client_time, sizeof(client_time), TRUE); ret = KRB5KRB_AP_ERR_SKEW; kdc_log(context, config, 0, "Too large time skew, " "client time %s is out by %u > %u seconds -- %s", client_time, (unsigned)abs(kdc_time - p.patimestamp), context->max_skew, client_name); /* * The following is needed to make windows clients to * retry using the timestamp in the error message, if * there is a e_text, they become unhappy. */ e_text = NULL; goto out; } et.flags.pre_authent = 1; set_salt_padata(rep.padata, pa_key->salt); reply_key = &pa_key->key; ret = krb5_enctype_to_string(context, pa_key->key.keytype, &str); if (ret) str = NULL; kdc_log(context, config, 2, "ENC-TS Pre-authentication succeeded -- %s using %s", client_name, str ? str : "unknown enctype"); free(str); break; } #ifdef PKINIT preauth_done: #endif if(found_pa == 0 && config->require_preauth) goto use_pa; /* We come here if we found a pa-enc-timestamp, but if there was some problem with it, other than too large skew */ if(found_pa && et.flags.pre_authent == 0){ kdc_log(context, config, 0, "%s -- %s", e_text, client_name); e_text = NULL; goto out; } }else if (config->require_preauth || b->kdc_options.request_anonymous /* hack to force anon */ || client->entry.flags.require_preauth || server->entry.flags.require_preauth) { METHOD_DATA method_data; PA_DATA *pa; unsigned char *buf; size_t len; use_pa: method_data.len = 0; method_data.val = NULL; ret = realloc_method_data(&method_data); if (ret) { free_METHOD_DATA(&method_data); goto out; } pa = &method_data.val[method_data.len-1]; pa->padata_type = KRB5_PADATA_ENC_TIMESTAMP; pa->padata_value.length = 0; pa->padata_value.data = NULL; #ifdef PKINIT ret = realloc_method_data(&method_data); if (ret) { free_METHOD_DATA(&method_data); goto out; } pa = &method_data.val[method_data.len-1]; pa->padata_type = KRB5_PADATA_PK_AS_REQ; pa->padata_value.length = 0; pa->padata_value.data = NULL; ret = realloc_method_data(&method_data); if (ret) { free_METHOD_DATA(&method_data); goto out; } pa = &method_data.val[method_data.len-1]; pa->padata_type = KRB5_PADATA_PK_AS_REQ_WIN; pa->padata_value.length = 0; pa->padata_value.data = NULL; #endif /* * If there is a client key, send ETYPE_INFO{,2} */ ret = _kdc_find_etype(context, config->preauth_use_strongest_session_key, TRUE, client, b->etype.val, b->etype.len, NULL, &ckey); if (ret == 0) { /* * RFC4120 requires: * - If the client only knows about old enctypes, then send * both info replies (we send 'info' first in the list). * - If the client is 'modern', because it knows about 'new' * enctype types, then only send the 'info2' reply. * * Before we send the full list of etype-info data, we pick * the client key we would have used anyway below, just pick * that instead. */ if (older_enctype(ckey->key.keytype)) { ret = get_pa_etype_info(context, config, &method_data, ckey); if (ret) { free_METHOD_DATA(&method_data); goto out; } } ret = get_pa_etype_info2(context, config, &method_data, ckey); if (ret) { free_METHOD_DATA(&method_data); goto out; } } ASN1_MALLOC_ENCODE(METHOD_DATA, buf, len, &method_data, &len, ret); free_METHOD_DATA(&method_data); e_data.data = buf; e_data.length = len; e_text ="Need to use PA-ENC-TIMESTAMP/PA-PK-AS-REQ", ret = KRB5KDC_ERR_PREAUTH_REQUIRED; kdc_log(context, config, 0, "No preauth found, returning PREAUTH-REQUIRED -- %s", client_name); goto out; } /* * Verify flags after the user been required to prove its identity * with in a preauth mech. */ ret = _kdc_check_access(context, config, client, client_name, server, server_name, req, &e_data); if(ret) goto out; if (clientdb->hdb_auth_status) (clientdb->hdb_auth_status)(context, clientdb, client, HDB_AUTH_SUCCESS); /* * Selelct the best encryption type for the KDC with out regard to * the client since the client never needs to read that data. */ ret = _kdc_get_preferred_key(context, config, server, server_name, &setype, &skey); if(ret) goto out; if(f.renew || f.validate || f.proxy || f.forwarded || f.enc_tkt_in_skey || (f.request_anonymous && !config->allow_anonymous)) { ret = KRB5KDC_ERR_BADOPTION; e_text = "Bad KDC options"; kdc_log(context, config, 0, "Bad KDC options -- %s", client_name); goto out; } rep.pvno = 5; rep.msg_type = krb_as_rep; ret = copy_Realm(&client->entry.principal->realm, &rep.crealm); if (ret) goto out; ret = _krb5_principal2principalname(&rep.cname, client->entry.principal); if (ret) goto out; rep.ticket.tkt_vno = 5; copy_Realm(&server->entry.principal->realm, &rep.ticket.realm); _krb5_principal2principalname(&rep.ticket.sname, server->entry.principal); /* java 1.6 expects the name to be the same type, lets allow that * uncomplicated name-types. */ #define CNT(sp,t) (((sp)->sname->name_type) == KRB5_NT_##t) if (CNT(b, UNKNOWN) || CNT(b, PRINCIPAL) || CNT(b, SRV_INST) || CNT(b, SRV_HST) || CNT(b, SRV_XHST)) rep.ticket.sname.name_type = b->sname->name_type; #undef CNT et.flags.initial = 1; if(client->entry.flags.forwardable && server->entry.flags.forwardable) et.flags.forwardable = f.forwardable; else if (f.forwardable) { e_text = "Ticket may not be forwardable"; ret = KRB5KDC_ERR_POLICY; kdc_log(context, config, 0, "Ticket may not be forwardable -- %s", client_name); goto out; } if(client->entry.flags.proxiable && server->entry.flags.proxiable) et.flags.proxiable = f.proxiable; else if (f.proxiable) { e_text = "Ticket may not be proxiable"; ret = KRB5KDC_ERR_POLICY; kdc_log(context, config, 0, "Ticket may not be proxiable -- %s", client_name); goto out; } if(client->entry.flags.postdate && server->entry.flags.postdate) et.flags.may_postdate = f.allow_postdate; else if (f.allow_postdate){ e_text = "Ticket may not be postdate"; ret = KRB5KDC_ERR_POLICY; kdc_log(context, config, 0, "Ticket may not be postdatable -- %s", client_name); goto out; } /* check for valid set of addresses */ if(!_kdc_check_addresses(context, config, b->addresses, from_addr)) { e_text = "Bad address list in requested"; ret = KRB5KRB_AP_ERR_BADADDR; kdc_log(context, config, 0, "Bad address list requested -- %s", client_name); goto out; } ret = copy_PrincipalName(&rep.cname, &et.cname); if (ret) goto out; ret = copy_Realm(&rep.crealm, &et.crealm); if (ret) goto out; { time_t start; time_t t; start = et.authtime = kdc_time; if(f.postdated && req->req_body.from){ ALLOC(et.starttime); start = *et.starttime = *req->req_body.from; et.flags.invalid = 1; et.flags.postdated = 1; /* XXX ??? */ } _kdc_fix_time(&b->till); t = *b->till; /* be careful not overflowing */ if(client->entry.max_life) t = start + min(t - start, *client->entry.max_life); if(server->entry.max_life) t = start + min(t - start, *server->entry.max_life); #if 0 t = min(t, start + realm->max_life); #endif et.endtime = t; if(f.renewable_ok && et.endtime < *b->till){ f.renewable = 1; if(b->rtime == NULL){ ALLOC(b->rtime); *b->rtime = 0; } if(*b->rtime < *b->till) *b->rtime = *b->till; } if(f.renewable && b->rtime){ t = *b->rtime; if(t == 0) t = MAX_TIME; if(client->entry.max_renew) t = start + min(t - start, *client->entry.max_renew); if(server->entry.max_renew) t = start + min(t - start, *server->entry.max_renew); #if 0 t = min(t, start + realm->max_renew); #endif ALLOC(et.renew_till); *et.renew_till = t; et.flags.renewable = 1; } } if (f.request_anonymous) et.flags.anonymous = 1; if(b->addresses){ ALLOC(et.caddr); copy_HostAddresses(b->addresses, et.caddr); } et.transited.tr_type = DOMAIN_X500_COMPRESS; krb5_data_zero(&et.transited.contents); /* The MIT ASN.1 library (obviously) doesn't tell lengths encoded * as 0 and as 0x80 (meaning indefinite length) apart, and is thus * incapable of correctly decoding SEQUENCE OF's of zero length. * * To fix this, always send at least one no-op last_req * * If there's a pw_end or valid_end we will use that, * otherwise just a dummy lr. */ ek.last_req.val = malloc(2 * sizeof(*ek.last_req.val)); if (ek.last_req.val == NULL) { ret = ENOMEM; goto out; } ek.last_req.len = 0; if (client->entry.pw_end && (config->kdc_warn_pwexpire == 0 || kdc_time + config->kdc_warn_pwexpire >= *client->entry.pw_end)) { ek.last_req.val[ek.last_req.len].lr_type = LR_PW_EXPTIME; ek.last_req.val[ek.last_req.len].lr_value = *client->entry.pw_end; ++ek.last_req.len; } if (client->entry.valid_end) { ek.last_req.val[ek.last_req.len].lr_type = LR_ACCT_EXPTIME; ek.last_req.val[ek.last_req.len].lr_value = *client->entry.valid_end; ++ek.last_req.len; } if (ek.last_req.len == 0) { ek.last_req.val[ek.last_req.len].lr_type = LR_NONE; ek.last_req.val[ek.last_req.len].lr_value = 0; ++ek.last_req.len; } ek.nonce = b->nonce; if (client->entry.valid_end || client->entry.pw_end) { ALLOC(ek.key_expiration); if (client->entry.valid_end) { if (client->entry.pw_end) *ek.key_expiration = min(*client->entry.valid_end, *client->entry.pw_end); else *ek.key_expiration = *client->entry.valid_end; } else *ek.key_expiration = *client->entry.pw_end; } else ek.key_expiration = NULL; ek.flags = et.flags; ek.authtime = et.authtime; if (et.starttime) { ALLOC(ek.starttime); *ek.starttime = *et.starttime; } ek.endtime = et.endtime; if (et.renew_till) { ALLOC(ek.renew_till); *ek.renew_till = *et.renew_till; } copy_Realm(&rep.ticket.realm, &ek.srealm); copy_PrincipalName(&rep.ticket.sname, &ek.sname); if(et.caddr){ ALLOC(ek.caddr); copy_HostAddresses(et.caddr, ek.caddr); } #if PKINIT if (pkp) { e_text = "Failed to build PK-INIT reply"; ret = _kdc_pk_mk_pa_reply(context, config, pkp, client, sessionetype, req, req_buffer, &reply_key, &et.key, rep.padata); if (ret) goto out; ret = _kdc_add_inital_verified_cas(context, config, pkp, &et); if (ret) goto out; } else #endif { ret = krb5_generate_random_keyblock(context, sessionetype, &et.key); if (ret) goto out; } if (reply_key == NULL) { e_text = "Client have no reply key"; ret = KRB5KDC_ERR_CLIENT_NOTYET; goto out; } ret = copy_EncryptionKey(&et.key, &ek.key); if (ret) goto out; if (rep.padata->len == 0) { free(rep.padata); rep.padata = NULL; } /* Add the PAC */ if (send_pac_p(context, req)) { krb5_pac p = NULL; krb5_data data; ret = _kdc_pac_generate(context, client, &p); if (ret) { kdc_log(context, config, 0, "PAC generation failed for -- %s", client_name); goto out; } if (p != NULL) { ret = _krb5_pac_sign(context, p, et.authtime, client->entry.principal, &skey->key, /* Server key */ &skey->key, /* FIXME: should be krbtgt key */ &data); krb5_pac_free(context, p); if (ret) { kdc_log(context, config, 0, "PAC signing failed for -- %s", client_name); goto out; } ret = _kdc_tkt_add_if_relevant_ad(context, &et, KRB5_AUTHDATA_WIN2K_PAC, &data); krb5_data_free(&data); if (ret) goto out; } } _kdc_log_timestamp(context, config, "AS-REQ", et.authtime, et.starttime, et.endtime, et.renew_till); /* do this as the last thing since this signs the EncTicketPart */ ret = _kdc_add_KRB5SignedPath(context, config, server, setype, client->entry.principal, NULL, NULL, &et); if (ret) goto out; log_as_req(context, config, reply_key->keytype, setype, b); ret = _kdc_encode_reply(context, config, &rep, &et, &ek, setype, server->entry.kvno, &skey->key, client->entry.kvno, reply_key, 0, &e_text, reply); free_EncTicketPart(&et); free_EncKDCRepPart(&ek); if (ret) goto out; /* */ if (datagram_reply && reply->length > config->max_datagram_reply_length) { krb5_data_free(reply); ret = KRB5KRB_ERR_RESPONSE_TOO_BIG; e_text = "Reply packet too large"; } out: free_AS_REP(&rep); if(ret != 0 && ret != HDB_ERR_NOT_FOUND_HERE && reply->length == 0) { krb5_mk_error(context, ret, e_text, (e_data.data ? &e_data : NULL), client_princ, server_princ, NULL, NULL, reply); ret = 0; } #ifdef PKINIT if (pkp) _kdc_pk_free_client_param(context, pkp); #endif if (e_data.data) free(e_data.data); if (client_princ) krb5_free_principal(context, client_princ); free(client_name); if (server_princ) krb5_free_principal(context, server_princ); free(server_name); if(client) _kdc_free_ent(context, client); if(server) _kdc_free_ent(context, server); return ret; }
krb5_error_code kcm_store_io(krb5_context context, krb5_uuid uuid, void *ptr, size_t length, krb5_data *data, bool encrypt) { xtsEncrypt_InStruct_t xtsEncrypt_InStruct; size_t inseed_size = 64; io_connect_t conn; kern_return_t kr; uint8_t *inseed; krb5_crypto crypto = NULL; krb5_error_code ret; krb5_data_zero(data); inseed = malloc(inseed_size); if (inseed == NULL) err(1, "malloc"); memset(inseed, 0, inseed_size); conn = openiodev(); if (conn == IO_OBJECT_NULL) { free(inseed); return EINVAL; } uuid_copy(xtsEncrypt_InStruct.key_uuid, uuid); xtsEncrypt_InStruct.bufferAddress = (uint64_t) (intptr_t) inseed; xtsEncrypt_InStruct.bufferLength = (uint64_t) inseed_size; memset(xtsEncrypt_InStruct.tweak, 0, XTS_TWEAK_BYTES); kr = IOConnectCallMethod(conn, kAppleFDEKeyStore_xtsEncrypt, NULL, 0, & xtsEncrypt_InStruct, sizeof(xtsEncrypt_InStruct), NULL, 0, NULL, 0); closeiodev(conn); if (kr != KERN_SUCCESS) { free(inseed); return EINVAL; } CC_SHA256(inseed, (CC_LONG)inseed_size, inseed); krb5_keyblock keyblock; keyblock.keytype = ETYPE_AES128_CTS_HMAC_SHA1_96; keyblock.keyvalue.data = inseed; keyblock.keyvalue.length = 16; ret = krb5_crypto_init(context, &keyblock, 0, &crypto); free(inseed); if (ret) return ret; if (encrypt) ret = krb5_encrypt(context, crypto, 1, ptr, length, data); else ret = krb5_decrypt(context, crypto, 1, ptr, length, data); krb5_crypto_destroy(context, crypto); return ret; }
static OM_uint32 unwrap_des3 (OM_uint32 * minor_status, const gsskrb5_ctx context_handle, krb5_context context, const gss_buffer_t input_message_buffer, gss_buffer_t output_message_buffer, int * conf_state, gss_qop_t * qop_state, krb5_keyblock *key ) { u_char *p; size_t len; u_char *seq; krb5_data seq_data; u_char cksum[20]; uint32_t seq_number; size_t padlength; OM_uint32 ret; int cstate; krb5_crypto crypto; Checksum csum; int cmp; p = input_message_buffer->value; ret = _gsskrb5_verify_header (&p, input_message_buffer->length, "\x02\x01", GSS_KRB5_MECHANISM); if (ret) return ret; if (memcmp (p, "\x04\x00", 2) != 0) /* HMAC SHA1 DES3_KD */ return GSS_S_BAD_SIG; p += 2; if (memcmp (p, "\x02\x00", 2) == 0) { cstate = 1; } else if (memcmp (p, "\xff\xff", 2) == 0) { cstate = 0; } else return GSS_S_BAD_MIC; p += 2; if(conf_state != NULL) *conf_state = cstate; if (memcmp (p, "\xff\xff", 2) != 0) return GSS_S_DEFECTIVE_TOKEN; p += 2; p += 28; len = p - (u_char *)input_message_buffer->value; if(cstate) { /* decrypt data */ krb5_data tmp; ret = krb5_crypto_init(context, key, ETYPE_DES3_CBC_NONE, &crypto); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } ret = krb5_decrypt(context, crypto, KRB5_KU_USAGE_SEAL, p, input_message_buffer->length - len, &tmp); krb5_crypto_destroy(context, crypto); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } assert (tmp.length == input_message_buffer->length - len); memcpy (p, tmp.data, tmp.length); krb5_data_free(&tmp); } /* check pad */ ret = _gssapi_verify_pad(input_message_buffer, input_message_buffer->length - len, &padlength); if (ret) return ret; /* verify sequence number */ HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex); p -= 28; ret = krb5_crypto_init(context, key, ETYPE_DES3_CBC_NONE, &crypto); if (ret) { *minor_status = ret; HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); return GSS_S_FAILURE; } { DES_cblock ivec; memcpy(&ivec, p + 8, 8); ret = krb5_decrypt_ivec (context, crypto, KRB5_KU_USAGE_SEQ, p, 8, &seq_data, &ivec); } krb5_crypto_destroy (context, crypto); if (ret) { *minor_status = ret; HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); return GSS_S_FAILURE; } if (seq_data.length != 8) { krb5_data_free (&seq_data); *minor_status = 0; HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); return GSS_S_BAD_MIC; } seq = seq_data.data; _gsskrb5_decode_om_uint32(seq, &seq_number); if (context_handle->more_flags & LOCAL) cmp = memcmp(&seq[4], "\xff\xff\xff\xff", 4); else cmp = memcmp(&seq[4], "\x00\x00\x00\x00", 4); krb5_data_free (&seq_data); if (cmp != 0) { *minor_status = 0; HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); return GSS_S_BAD_MIC; } ret = _gssapi_msg_order_check(context_handle->order, seq_number); if (ret) { *minor_status = 0; HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); return ret; } HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); /* verify checksum */ memcpy (cksum, p + 8, 20); memcpy (p + 20, p - 8, 8); csum.cksumtype = CKSUMTYPE_HMAC_SHA1_DES3; csum.checksum.length = 20; csum.checksum.data = cksum; ret = krb5_crypto_init(context, key, 0, &crypto); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } ret = krb5_verify_checksum (context, crypto, KRB5_KU_USAGE_SIGN, p + 20, input_message_buffer->length - len + 8, &csum); krb5_crypto_destroy (context, crypto); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } /* copy out data */ output_message_buffer->length = input_message_buffer->length - len - padlength - 8; output_message_buffer->value = malloc(output_message_buffer->length); if(output_message_buffer->length != 0 && output_message_buffer->value == NULL) return GSS_S_FAILURE; memcpy (output_message_buffer->value, p + 36, output_message_buffer->length); return GSS_S_COMPLETE; }
static void do_v5_kvno (int count, char *names[], char * ccache_name, char *etype_str, char *keytab_name, char *sname) { krb5_error_code ret; krb5_context context = 0; int i, errors; krb5_enctype etype; krb5_ccache ccache; krb5_principal me; krb5_creds in_creds, *out_creds = NULL; Ticket ticket; size_t len; char *princ = NULL; krb5_keytab keytab = NULL; ret = krb5_init_context(&context); if (ret) errx(1, "krb5_init_context failed: %d", ret); if (etype_str) { ret = krb5_string_to_enctype(context, etype_str, &etype); if (ret) krb5_err(context, 1, ret, "Failed to convert encryption type %s", etype_str); } else { etype = 0; } if (ccache_name) ret = krb5_cc_resolve(context, ccache_name, &ccache); else ret = krb5_cc_default(context, &ccache); if (ret) krb5_err(context, 1, ret, "Failed to open credentials cache %s", (ccache_name) ? ccache_name : "(Default)"); if (keytab_name) { ret = krb5_kt_resolve(context, keytab_name, &keytab); if (ret) krb5_err(context, 1, ret, "Can't resolve keytab %s", keytab_name); } ret = krb5_cc_get_principal(context, ccache, &me); if (ret) krb5_err(context, 1, ret, "krb5_cc_get_principal"); errors = 0; for (i = 0; i < count; i++) { memset(&in_creds, 0, sizeof(in_creds)); memset(&ticket, 0, sizeof(ticket)); in_creds.client = me; if (sname != NULL) { ret = krb5_sname_to_principal(context, names[i], sname, KRB5_NT_SRV_HST, &in_creds.server); } else { ret = krb5_parse_name(context, names[i], &in_creds.server); } if (ret) { if (!quiet_flag) krb5_warn(context, ret, "Couldn't parse principal name %s", names[i]); errors++; continue; } ret = krb5_unparse_name(context, in_creds.server, &princ); if (ret) { krb5_warn(context, ret, "Couldn't format parsed principal name for '%s'", names[i]); errors++; goto next; } in_creds.session.keytype = etype; ret = krb5_get_credentials(context, 0, ccache, &in_creds, &out_creds); if (ret) { krb5_warn(context, ret, "Couldn't get credentials for %s", princ); errors++; goto next; } ret = decode_Ticket(out_creds->ticket.data, out_creds->ticket.length, &ticket, &len); if (ret) { krb5_err(context, 1, ret, "Can't decode ticket for %s", princ); errors++; goto next; continue; } if (keytab) { krb5_keytab_entry kte; krb5_crypto crypto; krb5_data dec_data; EncTicketPart decr_part; ret = krb5_kt_get_entry(context, keytab, in_creds.server, (ticket.enc_part.kvno != NULL)? *ticket.enc_part.kvno : 0, ticket.enc_part.etype, &kte); if (ret) { krb5_warn(context, ret, "Can't decrypt ticket for %s", princ); if (!quiet_flag) printf("%s: kvno = %d, keytab entry invalid", princ, (ticket.enc_part.kvno != NULL)? *ticket.enc_part.kvno : 0); errors ++; goto next; } ret = krb5_crypto_init(context, &kte.keyblock, 0, &crypto); if (ret) { krb5_warn(context, ret, "krb5_crypto_init"); errors ++; krb5_kt_free_entry(context, &kte); goto next; } ret = krb5_decrypt_EncryptedData (context, crypto, KRB5_KU_TICKET, &ticket.enc_part, &dec_data); krb5_crypto_destroy(context, crypto); krb5_kt_free_entry(context, &kte); if (ret) { krb5_warn(context, ret, "krb5_decrypt_EncryptedData"); errors ++; goto next; } ret = decode_EncTicketPart(dec_data.data, dec_data.length, &decr_part, &len); krb5_data_free(&dec_data); if (ret) { krb5_warn(context, ret, "decode_EncTicketPart"); errors ++; goto next; } if (!quiet_flag) printf("%s: kvno = %d, keytab entry valid\n", princ, (ticket.enc_part.kvno != NULL)? *ticket.enc_part.kvno : 0); free_EncTicketPart(&decr_part); } else { if (!quiet_flag) printf("%s: kvno = %d\n", princ, (ticket.enc_part.kvno != NULL)? *ticket.enc_part.kvno : 0); } next: if (out_creds) { krb5_free_creds(context, out_creds); out_creds = NULL; } if (princ) { krb5_free_unparsed_name(context, princ); princ = NULL; } krb5_free_principal(context, in_creds.server); free_Ticket(&ticket); } if (keytab) krb5_kt_close(context, keytab); krb5_free_principal(context, me); krb5_cc_close(context, ccache); krb5_free_context(context); if (errors) exit(1); exit(0); }
KRB5_LIB_FUNCTION int KRB5_LIB_CALL _krb5_extract_ticket(krb5_context context, krb5_kdc_rep *rep, krb5_creds *creds, krb5_keyblock *key, krb5_const_pointer keyseed, krb5_key_usage key_usage, krb5_addresses *addrs, unsigned nonce, unsigned flags, krb5_data *request, krb5_decrypt_proc decrypt_proc, krb5_const_pointer decryptarg) { krb5_error_code ret; krb5_principal tmp_principal; size_t len = 0; time_t tmp_time; krb5_timestamp sec_now; /* decrypt */ if (decrypt_proc == NULL) decrypt_proc = decrypt_tkt; ret = (*decrypt_proc)(context, key, key_usage, decryptarg, rep); if (ret) goto out; if (rep->enc_part.flags.enc_pa_rep && request) { krb5_crypto crypto = NULL; Checksum cksum; PA_DATA *pa = NULL; int idx = 0; _krb5_debug(context, 5, "processing enc-ap-rep"); if (rep->enc_part.encrypted_pa_data == NULL || (pa = krb5_find_padata(rep->enc_part.encrypted_pa_data->val, rep->enc_part.encrypted_pa_data->len, KRB5_PADATA_REQ_ENC_PA_REP, &idx)) == NULL) { _krb5_debug(context, 5, "KRB5_PADATA_REQ_ENC_PA_REP missing"); ret = KRB5KRB_AP_ERR_MODIFIED; goto out; } ret = krb5_crypto_init(context, key, 0, &crypto); if (ret) goto out; ret = decode_Checksum(pa->padata_value.data, pa->padata_value.length, &cksum, NULL); if (ret) { krb5_crypto_destroy(context, crypto); goto out; } ret = krb5_verify_checksum(context, crypto, KRB5_KU_AS_REQ, request->data, request->length, &cksum); krb5_crypto_destroy(context, crypto); free_Checksum(&cksum); _krb5_debug(context, 5, "enc-ap-rep: %svalid", (ret == 0) ? "" : "in"); if (ret) goto out; } /* save session key */ creds->session.keyvalue.length = 0; creds->session.keyvalue.data = NULL; creds->session.keytype = rep->enc_part.key.keytype; ret = krb5_data_copy (&creds->session.keyvalue, rep->enc_part.key.keyvalue.data, rep->enc_part.key.keyvalue.length); if (ret) { krb5_clear_error_message(context); goto out; } /* compare client and save */ ret = _krb5_principalname2krb5_principal(context, &tmp_principal, rep->kdc_rep.cname, rep->kdc_rep.crealm); if (ret) goto out; /* check client referral and save principal */ /* anonymous here ? */ if((flags & EXTRACT_TICKET_ALLOW_CNAME_MISMATCH) == 0) { ret = check_client_referral(context, rep, creds->client, tmp_principal, &creds->session); if (ret) { krb5_free_principal (context, tmp_principal); goto out; } } krb5_free_principal (context, creds->client); creds->client = tmp_principal; /* check server referral and save principal */ ret = _krb5_principalname2krb5_principal (context, &tmp_principal, rep->enc_part.sname, rep->enc_part.srealm); if (ret) goto out; if((flags & EXTRACT_TICKET_ALLOW_SERVER_MISMATCH) == 0){ ret = check_server_referral(context, rep, flags, creds->server, tmp_principal, &creds->session); if (ret) { krb5_free_principal (context, tmp_principal); goto out; } } krb5_free_principal(context, creds->server); creds->server = tmp_principal; /* verify names */ if(flags & EXTRACT_TICKET_MATCH_REALM){ const char *srealm = krb5_principal_get_realm(context, creds->server); const char *crealm = krb5_principal_get_realm(context, creds->client); if (strcmp(rep->enc_part.srealm, srealm) != 0 || strcmp(rep->enc_part.srealm, crealm) != 0) { ret = KRB5KRB_AP_ERR_MODIFIED; krb5_clear_error_message(context); goto out; } } /* compare nonces */ if (nonce != (unsigned)rep->enc_part.nonce) { ret = KRB5KRB_AP_ERR_MODIFIED; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto out; } /* set kdc-offset */ krb5_timeofday (context, &sec_now); if (rep->enc_part.flags.initial && (flags & EXTRACT_TICKET_TIMESYNC) && context->kdc_sec_offset == 0 && krb5_config_get_bool (context, NULL, "libdefaults", "kdc_timesync", NULL)) { context->kdc_sec_offset = rep->enc_part.authtime - sec_now; krb5_timeofday (context, &sec_now); } /* check all times */ if (rep->enc_part.starttime) { tmp_time = *rep->enc_part.starttime; } else tmp_time = rep->enc_part.authtime; if (creds->times.starttime == 0 && labs(tmp_time - sec_now) > context->max_skew) { ret = KRB5KRB_AP_ERR_SKEW; krb5_set_error_message (context, ret, N_("time skew (%ld) larger than max (%ld)", ""), labs(tmp_time - sec_now), (long)context->max_skew); goto out; } if (creds->times.starttime != 0 && tmp_time != creds->times.starttime) { krb5_clear_error_message (context); ret = KRB5KRB_AP_ERR_MODIFIED; goto out; } creds->times.starttime = tmp_time; if (rep->enc_part.renew_till) { tmp_time = *rep->enc_part.renew_till; } else tmp_time = 0; if (creds->times.renew_till != 0 && tmp_time > creds->times.renew_till) { krb5_clear_error_message (context); ret = KRB5KRB_AP_ERR_MODIFIED; goto out; } creds->times.renew_till = tmp_time; creds->times.authtime = rep->enc_part.authtime; if (creds->times.endtime != 0 && rep->enc_part.endtime > creds->times.endtime) { krb5_clear_error_message (context); ret = KRB5KRB_AP_ERR_MODIFIED; goto out; } creds->times.endtime = rep->enc_part.endtime; if(rep->enc_part.caddr) krb5_copy_addresses (context, rep->enc_part.caddr, &creds->addresses); else if(addrs) krb5_copy_addresses (context, addrs, &creds->addresses); else { creds->addresses.len = 0; creds->addresses.val = NULL; } creds->flags.b = rep->enc_part.flags; creds->authdata.len = 0; creds->authdata.val = NULL; /* extract ticket */ ASN1_MALLOC_ENCODE(Ticket, creds->ticket.data, creds->ticket.length, &rep->kdc_rep.ticket, &len, ret); if(ret) goto out; if (creds->ticket.length != len) krb5_abortx(context, "internal error in ASN.1 encoder"); creds->second_ticket.length = 0; creds->second_ticket.data = NULL; out: memset (rep->enc_part.key.keyvalue.data, 0, rep->enc_part.key.keyvalue.length); return ret; }
/* * Encrypt the provided enc_tkt_part structure with the key from the keytab * entry entry, and place the resulting blob in the ticket_reply structure. */ static int encrypt_enc_tkt(krb5_context context, krb5_principal service_principal, krb5_keytab_entry *entry, void *tr_out, void *er_in) { krb5_error_code code; #if USING_HEIMDAL Ticket *ticket_reply; EncTicketPart *enc_tkt_reply; krb5_crypto crypto = 0; unsigned char *buf = 0; size_t buf_size, buf_len; #else krb5_ticket *ticket_reply; krb5_enc_tkt_part *enc_tkt_reply; #endif /* Requisite aliasing for Heimdal/MIT support. */ ticket_reply = tr_out; enc_tkt_reply = er_in; #if USING_HEIMDAL ticket_reply->sname = service_principal->name; ticket_reply->realm = service_principal->realm; ASN1_MALLOC_ENCODE(EncTicketPart, buf, buf_size, enc_tkt_reply, &buf_len, code); if (code != 0) goto cleanup; if (buf_len != buf_size) goto cleanup; code = krb5_crypto_init(context, &deref_entry_keyblock(entry), deref_entry_enctype(entry), &crypto); if (code != 0) goto cleanup; code = krb5_encrypt_EncryptedData(context, crypto, KRB5_KU_TICKET, buf, buf_len, entry->vno, &(ticket_reply->enc_part)); if (code != 0) goto cleanup; ticket_reply->enc_part.etype = deref_entry_enctype(entry); *ticket_reply->enc_part.kvno = entry->vno; ticket_reply->tkt_vno = 5; #else ticket_reply->server = service_principal; ticket_reply->enc_part2 = enc_tkt_reply; code = krb5_encrypt_tkt_part(context, &deref_entry_keyblock(entry), ticket_reply); if (code != 0) goto cleanup; ticket_reply->enc_part.kvno = entry->vno; #endif cleanup: #if USING_HEIMDAL if (buf != NULL) free(buf); if (crypto != NULL) krb5_crypto_destroy(context, crypto); #endif return code; }
krb5_error_code KRB5_LIB_FUNCTION krb5_rd_priv(krb5_context context, krb5_auth_context auth_context, const krb5_data *inbuf, krb5_data *outbuf, krb5_replay_data *outdata) { krb5_error_code ret; KRB_PRIV priv; EncKrbPrivPart part; size_t len; krb5_data plain; krb5_keyblock *key; krb5_crypto crypto; if (outbuf) krb5_data_zero(outbuf); if ((auth_context->flags & (KRB5_AUTH_CONTEXT_RET_TIME | KRB5_AUTH_CONTEXT_RET_SEQUENCE)) && outdata == NULL) return KRB5_RC_REQUIRED; /* XXX better error, MIT returns this */ memset(&priv, 0, sizeof(priv)); ret = decode_KRB_PRIV (inbuf->data, inbuf->length, &priv, &len); if (ret) goto failure; if (priv.pvno != 5) { krb5_clear_error_string (context); ret = KRB5KRB_AP_ERR_BADVERSION; goto failure; } if (priv.msg_type != krb_priv) { krb5_clear_error_string (context); ret = KRB5KRB_AP_ERR_MSG_TYPE; goto failure; } if (auth_context->remote_subkey) key = auth_context->remote_subkey; else if (auth_context->local_subkey) key = auth_context->local_subkey; else key = auth_context->keyblock; ret = krb5_crypto_init(context, key, 0, &crypto); if (ret) goto failure; ret = krb5_decrypt_EncryptedData(context, crypto, KRB5_KU_KRB_PRIV, &priv.enc_part, &plain); krb5_crypto_destroy(context, crypto); if (ret) goto failure; ret = decode_EncKrbPrivPart (plain.data, plain.length, &part, &len); krb5_data_free (&plain); if (ret) goto failure; /* check sender address */ if (part.s_address && auth_context->remote_address && !krb5_address_compare (context, auth_context->remote_address, part.s_address)) { krb5_clear_error_string (context); ret = KRB5KRB_AP_ERR_BADADDR; goto failure_part; } /* check receiver address */ if (part.r_address && auth_context->local_address && !krb5_address_compare (context, auth_context->local_address, part.r_address)) { krb5_clear_error_string (context); ret = KRB5KRB_AP_ERR_BADADDR; goto failure_part; } /* check timestamp */ if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_TIME) { krb5_timestamp sec; krb5_timeofday (context, &sec); if (part.timestamp == NULL || part.usec == NULL || abs(*part.timestamp - sec) > context->max_skew) { krb5_clear_error_string (context); ret = KRB5KRB_AP_ERR_SKEW; goto failure_part; } } /* XXX - check replay cache */ /* check sequence number. since MIT krb5 cannot generate a sequence number of zero but instead generates no sequence number, we accept that */ if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) { if ((part.seq_number == NULL && auth_context->remote_seqnumber != 0) || (part.seq_number != NULL && *part.seq_number != auth_context->remote_seqnumber)) { krb5_clear_error_string (context); ret = KRB5KRB_AP_ERR_BADORDER; goto failure_part; } auth_context->remote_seqnumber++; } ret = krb5_data_copy (outbuf, part.user_data.data, part.user_data.length); if (ret) goto failure_part; if ((auth_context->flags & (KRB5_AUTH_CONTEXT_RET_TIME | KRB5_AUTH_CONTEXT_RET_SEQUENCE))) { /* if these fields are not present in the priv-part, silently return zero */ memset(outdata, 0, sizeof(*outdata)); if(part.timestamp) outdata->timestamp = *part.timestamp; if(part.usec) outdata->usec = *part.usec; if(part.seq_number) outdata->seq = *part.seq_number; } failure_part: free_EncKrbPrivPart (&part); failure: free_KRB_PRIV (&priv); return ret; }
static krb5_error_code check_client_referral(krb5_context context, krb5_kdc_rep *rep, krb5_const_principal requested, krb5_const_principal mapped, krb5_keyblock const * key) { krb5_error_code ret; PA_ClientCanonicalized canon; krb5_crypto crypto; krb5_data data; PA_DATA *pa; size_t len; int i = 0; if (rep->kdc_rep.padata == NULL) goto noreferral; pa = krb5_find_padata(rep->kdc_rep.padata->val, rep->kdc_rep.padata->len, KRB5_PADATA_CLIENT_CANONICALIZED, &i); if (pa == NULL) goto noreferral; ret = decode_PA_ClientCanonicalized(pa->padata_value.data, pa->padata_value.length, &canon, &len); if (ret) { krb5_set_error_message(context, ret, N_("Failed to decode ClientCanonicalized " "from realm %s", ""), requested->realm); return ret; } ASN1_MALLOC_ENCODE(PA_ClientCanonicalizedNames, data.data, data.length, &canon.names, &len, ret); if (ret) { free_PA_ClientCanonicalized(&canon); return ret; } if (data.length != len) krb5_abortx(context, "internal asn.1 error"); ret = krb5_crypto_init(context, key, 0, &crypto); if (ret) { free(data.data); free_PA_ClientCanonicalized(&canon); return ret; } ret = krb5_verify_checksum(context, crypto, KRB5_KU_CANONICALIZED_NAMES, data.data, data.length, &canon.canon_checksum); krb5_crypto_destroy(context, crypto); free(data.data); if (ret) { krb5_set_error_message(context, ret, N_("Failed to verify client canonicalized " "data from realm %s", ""), requested->realm); free_PA_ClientCanonicalized(&canon); return ret; } if (!_krb5_principal_compare_PrincipalName(context, requested, &canon.names.requested_name)) { free_PA_ClientCanonicalized(&canon); krb5_set_error_message(context, KRB5_PRINC_NOMATCH, N_("Requested name doesn't match" " in client referral", "")); return KRB5_PRINC_NOMATCH; } if (!_krb5_principal_compare_PrincipalName(context, mapped, &canon.names.mapped_name)) { free_PA_ClientCanonicalized(&canon); krb5_set_error_message(context, KRB5_PRINC_NOMATCH, N_("Mapped name doesn't match" " in client referral", "")); return KRB5_PRINC_NOMATCH; } return 0; noreferral: if (krb5_principal_compare(context, requested, mapped) == FALSE) { krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED, N_("Not same client principal returned " "as requested", "")); return KRB5KRB_AP_ERR_MODIFIED; } return 0; }
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_get_forwarded_creds (krb5_context context, krb5_auth_context auth_context, krb5_ccache ccache, krb5_flags flags, const char *hostname, krb5_creds *in_creds, krb5_data *out_data) { krb5_error_code ret; krb5_creds *out_creds; krb5_addresses addrs, *paddrs; KRB_CRED cred; KrbCredInfo *krb_cred_info; EncKrbCredPart enc_krb_cred_part; size_t len; unsigned char *buf; size_t buf_size; krb5_kdc_flags kdc_flags; krb5_crypto crypto; struct addrinfo *ai; krb5_creds *ticket; paddrs = NULL; addrs.len = 0; addrs.val = NULL; ret = krb5_get_credentials(context, 0, ccache, in_creds, &ticket); if(ret == 0) { if (ticket->addresses.len) paddrs = &addrs; krb5_free_creds (context, ticket); } else { krb5_boolean noaddr; krb5_appdefault_boolean(context, NULL, krb5_principal_get_realm(context, in_creds->client), "no-addresses", KRB5_ADDRESSLESS_DEFAULT, &noaddr); if (!noaddr) paddrs = &addrs; } /* * If tickets have addresses, get the address of the remote host. */ if (paddrs != NULL) { ret = getaddrinfo (hostname, NULL, NULL, &ai); if (ret) { krb5_error_code ret2 = krb5_eai_to_heim_errno(ret, errno); krb5_set_error_message(context, ret2, N_("resolving host %s failed: %s", "hostname, error"), hostname, gai_strerror(ret)); return ret2; } ret = add_addrs (context, &addrs, ai); freeaddrinfo (ai); if (ret) return ret; } kdc_flags.b = int2KDCOptions(flags); ret = krb5_get_kdc_cred (context, ccache, kdc_flags, paddrs, NULL, in_creds, &out_creds); krb5_free_addresses (context, &addrs); if (ret) return ret; memset (&cred, 0, sizeof(cred)); cred.pvno = 5; cred.msg_type = krb_cred; ALLOC_SEQ(&cred.tickets, 1); if (cred.tickets.val == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto out2; } ret = decode_Ticket(out_creds->ticket.data, out_creds->ticket.length, cred.tickets.val, &len); if (ret) goto out3; memset (&enc_krb_cred_part, 0, sizeof(enc_krb_cred_part)); ALLOC_SEQ(&enc_krb_cred_part.ticket_info, 1); if (enc_krb_cred_part.ticket_info.val == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto out4; } if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_TIME) { krb5_timestamp sec; int32_t usec; krb5_us_timeofday (context, &sec, &usec); ALLOC(enc_krb_cred_part.timestamp, 1); if (enc_krb_cred_part.timestamp == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto out4; } *enc_krb_cred_part.timestamp = sec; ALLOC(enc_krb_cred_part.usec, 1); if (enc_krb_cred_part.usec == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto out4; } *enc_krb_cred_part.usec = usec; } else { enc_krb_cred_part.timestamp = NULL; enc_krb_cred_part.usec = NULL; } if (auth_context->local_address && auth_context->local_port && paddrs) { ret = krb5_make_addrport (context, &enc_krb_cred_part.s_address, auth_context->local_address, auth_context->local_port); if (ret) goto out4; } if (auth_context->remote_address) { if (auth_context->remote_port) { krb5_boolean noaddr; krb5_const_realm srealm; srealm = krb5_principal_get_realm(context, out_creds->server); /* Is this correct, and should we use the paddrs == NULL trick here as well? Having an address-less ticket may indicate that we don't know our own global address, but it does not necessary mean that we don't know the server's. */ krb5_appdefault_boolean(context, NULL, srealm, "no-addresses", FALSE, &noaddr); if (!noaddr) { ret = krb5_make_addrport (context, &enc_krb_cred_part.r_address, auth_context->remote_address, auth_context->remote_port); if (ret) goto out4; } } else { ALLOC(enc_krb_cred_part.r_address, 1); if (enc_krb_cred_part.r_address == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto out4; } ret = krb5_copy_address (context, auth_context->remote_address, enc_krb_cred_part.r_address); if (ret) goto out4; } } /* fill ticket_info.val[0] */ enc_krb_cred_part.ticket_info.len = 1; krb_cred_info = enc_krb_cred_part.ticket_info.val; copy_EncryptionKey (&out_creds->session, &krb_cred_info->key); ALLOC(krb_cred_info->prealm, 1); copy_Realm (&out_creds->client->realm, krb_cred_info->prealm); ALLOC(krb_cred_info->pname, 1); copy_PrincipalName(&out_creds->client->name, krb_cred_info->pname); ALLOC(krb_cred_info->flags, 1); *krb_cred_info->flags = out_creds->flags.b; ALLOC(krb_cred_info->authtime, 1); *krb_cred_info->authtime = out_creds->times.authtime; ALLOC(krb_cred_info->starttime, 1); *krb_cred_info->starttime = out_creds->times.starttime; ALLOC(krb_cred_info->endtime, 1); *krb_cred_info->endtime = out_creds->times.endtime; ALLOC(krb_cred_info->renew_till, 1); *krb_cred_info->renew_till = out_creds->times.renew_till; ALLOC(krb_cred_info->srealm, 1); copy_Realm (&out_creds->server->realm, krb_cred_info->srealm); ALLOC(krb_cred_info->sname, 1); copy_PrincipalName (&out_creds->server->name, krb_cred_info->sname); ALLOC(krb_cred_info->caddr, 1); copy_HostAddresses (&out_creds->addresses, krb_cred_info->caddr); krb5_free_creds (context, out_creds); /* encode EncKrbCredPart */ ASN1_MALLOC_ENCODE(EncKrbCredPart, buf, buf_size, &enc_krb_cred_part, &len, ret); free_EncKrbCredPart (&enc_krb_cred_part); if (ret) { free_KRB_CRED(&cred); return ret; } if(buf_size != len) krb5_abortx(context, "internal error in ASN.1 encoder"); /** * Some older of the MIT gssapi library used clear-text tickets * (warped inside AP-REQ encryption), use the krb5_auth_context * flag KRB5_AUTH_CONTEXT_CLEAR_FORWARDED_CRED to support those * tickets. The session key is used otherwise to encrypt the * forwarded ticket. */ if (auth_context->flags & KRB5_AUTH_CONTEXT_CLEAR_FORWARDED_CRED) { cred.enc_part.etype = KRB5_ENCTYPE_NULL; cred.enc_part.kvno = NULL; cred.enc_part.cipher.data = buf; cred.enc_part.cipher.length = buf_size; } else { /* * Here older versions then 0.7.2 of Heimdal used the local or * remote subkey. That is wrong, the session key should be * used. Heimdal 0.7.2 and newer have code to try both in the * receiving end. */ ret = krb5_crypto_init(context, auth_context->keyblock, 0, &crypto); if (ret) { free(buf); free_KRB_CRED(&cred); return ret; } ret = krb5_encrypt_EncryptedData (context, crypto, KRB5_KU_KRB_CRED, buf, len, 0, &cred.enc_part); free(buf); krb5_crypto_destroy(context, crypto); if (ret) { free_KRB_CRED(&cred); return ret; } } ASN1_MALLOC_ENCODE(KRB_CRED, buf, buf_size, &cred, &len, ret); free_KRB_CRED (&cred); if (ret) return ret; if(buf_size != len) krb5_abortx(context, "internal error in ASN.1 encoder"); out_data->length = len; out_data->data = buf; return 0; out4: free_EncKrbCredPart(&enc_krb_cred_part); out3: free_KRB_CRED(&cred); out2: krb5_free_creds (context, out_creds); return ret; }
krb5_error_code _krb5_mk_req_internal(krb5_context context, krb5_auth_context *auth_context, const krb5_flags ap_req_options, krb5_data *in_data, krb5_creds *in_creds, krb5_data *outbuf, krb5_key_usage checksum_usage, krb5_key_usage encrypt_usage) { krb5_error_code ret; krb5_data authenticator; Checksum c; Checksum *c_opt; krb5_auth_context ac; if(auth_context) { if(*auth_context == NULL) ret = krb5_auth_con_init(context, auth_context); else ret = 0; ac = *auth_context; } else ret = krb5_auth_con_init(context, &ac); if(ret) return ret; if(ac->local_subkey == NULL && (ap_req_options & AP_OPTS_USE_SUBKEY)) { ret = krb5_auth_con_generatelocalsubkey(context, ac, &in_creds->session); if(ret) goto out; } krb5_free_keyblock(context, ac->keyblock); ret = krb5_copy_keyblock(context, &in_creds->session, &ac->keyblock); if (ret) goto out; /* it's unclear what type of checksum we can use. try the best one, except: * a) if it's configured differently for the current realm, or * b) if the session key is des-cbc-crc */ if (in_data) { if(ac->keyblock->keytype == ETYPE_DES_CBC_CRC) { /* this is to make DCE secd (and older MIT kdcs?) happy */ ret = krb5_create_checksum(context, NULL, 0, CKSUMTYPE_RSA_MD4, in_data->data, in_data->length, &c); } else if(ac->keyblock->keytype == ETYPE_ARCFOUR_HMAC_MD5 || ac->keyblock->keytype == ETYPE_ARCFOUR_HMAC_MD5_56 || ac->keyblock->keytype == ETYPE_DES_CBC_MD4 || ac->keyblock->keytype == ETYPE_DES_CBC_MD5) { /* this is to make MS kdc happy */ ret = krb5_create_checksum(context, NULL, 0, CKSUMTYPE_RSA_MD5, in_data->data, in_data->length, &c); } else { krb5_crypto crypto; ret = krb5_crypto_init(context, ac->keyblock, 0, &crypto); if (ret) goto out; ret = krb5_create_checksum(context, crypto, checksum_usage, 0, in_data->data, in_data->length, &c); krb5_crypto_destroy(context, crypto); } c_opt = &c; } else { c_opt = NULL; } if (ret) goto out; ret = _krb5_build_authenticator(context, ac, ac->keyblock->keytype, in_creds, c_opt, &authenticator, encrypt_usage); if (c_opt) free_Checksum (c_opt); if (ret) goto out; ret = krb5_build_ap_req (context, ac->keyblock->keytype, in_creds, ap_req_options, authenticator, outbuf); out: if(auth_context == NULL) krb5_auth_con_free(context, ac); return ret; }
krb5_error_code _kdc_encode_reply(krb5_context context, krb5_kdc_configuration *config, KDC_REP *rep, const EncTicketPart *et, EncKDCRepPart *ek, krb5_enctype etype, int skvno, const EncryptionKey *skey, int ckvno, const EncryptionKey *ckey, const char **e_text, krb5_data *reply) { unsigned char *buf; size_t buf_size; size_t len; krb5_error_code ret; krb5_crypto crypto; ASN1_MALLOC_ENCODE(EncTicketPart, buf, buf_size, et, &len, ret); if(ret) { kdc_log(context, config, 0, "Failed to encode ticket: %s", krb5_get_err_text(context, ret)); return ret; } if(buf_size != len) { free(buf); kdc_log(context, config, 0, "Internal error in ASN.1 encoder"); *e_text = "KDC internal error"; return KRB5KRB_ERR_GENERIC; } ret = krb5_crypto_init(context, skey, etype, &crypto); if (ret) { free(buf); kdc_log(context, config, 0, "krb5_crypto_init failed: %s", krb5_get_err_text(context, ret)); return ret; } ret = krb5_encrypt_EncryptedData(context, crypto, KRB5_KU_TICKET, buf, len, skvno, &rep->ticket.enc_part); free(buf); krb5_crypto_destroy(context, crypto); if(ret) { kdc_log(context, config, 0, "Failed to encrypt data: %s", krb5_get_err_text(context, ret)); return ret; } if(rep->msg_type == krb_as_rep && !config->encode_as_rep_as_tgs_rep) ASN1_MALLOC_ENCODE(EncASRepPart, buf, buf_size, ek, &len, ret); else ASN1_MALLOC_ENCODE(EncTGSRepPart, buf, buf_size, ek, &len, ret); if(ret) { kdc_log(context, config, 0, "Failed to encode KDC-REP: %s", krb5_get_err_text(context, ret)); return ret; } if(buf_size != len) { free(buf); kdc_log(context, config, 0, "Internal error in ASN.1 encoder"); *e_text = "KDC internal error"; return KRB5KRB_ERR_GENERIC; } ret = krb5_crypto_init(context, ckey, 0, &crypto); if (ret) { free(buf); kdc_log(context, config, 0, "krb5_crypto_init failed: %s", krb5_get_err_text(context, ret)); return ret; } if(rep->msg_type == krb_as_rep) { krb5_encrypt_EncryptedData(context, crypto, KRB5_KU_AS_REP_ENC_PART, buf, len, ckvno, &rep->enc_part); free(buf); ASN1_MALLOC_ENCODE(AS_REP, buf, buf_size, rep, &len, ret); } else { krb5_encrypt_EncryptedData(context, crypto, KRB5_KU_TGS_REP_ENC_PART_SESSION, buf, len, ckvno, &rep->enc_part); free(buf); ASN1_MALLOC_ENCODE(TGS_REP, buf, buf_size, rep, &len, ret); } krb5_crypto_destroy(context, crypto); if(ret) { kdc_log(context, config, 0, "Failed to encode KDC-REP: %s", krb5_get_err_text(context, ret)); return ret; } if(buf_size != len) { free(buf); kdc_log(context, config, 0, "Internal error in ASN.1 encoder"); *e_text = "KDC internal error"; return KRB5KRB_ERR_GENERIC; } reply->data = buf; reply->length = buf_size; return 0; }
krb5_error_code KRB5_LIB_FUNCTION krb5_get_forwarded_creds (krb5_context context, krb5_auth_context auth_context, krb5_ccache ccache, krb5_flags flags, const char *hostname, krb5_creds *in_creds, krb5_data *out_data) { krb5_error_code ret; krb5_creds *out_creds; krb5_addresses addrs, *paddrs; KRB_CRED cred; KrbCredInfo *krb_cred_info; EncKrbCredPart enc_krb_cred_part; size_t len; unsigned char *buf; size_t buf_size; krb5_kdc_flags kdc_flags; krb5_crypto crypto; struct addrinfo *ai; int save_errno; krb5_creds *ticket; char *realm; if (in_creds->client && in_creds->client->realm) realm = in_creds->client->realm; else realm = in_creds->server->realm; addrs.len = 0; addrs.val = NULL; paddrs = &addrs; /* * If tickets are address-less, forward address-less tickets. */ ret = _krb5_get_krbtgt (context, ccache, realm, &ticket); if(ret == 0) { if (ticket->addresses.len == 0) paddrs = NULL; krb5_free_creds (context, ticket); } if (paddrs != NULL) { ret = getaddrinfo (hostname, NULL, NULL, &ai); if (ret) { save_errno = errno; krb5_set_error_string(context, "resolving %s: %s", hostname, gai_strerror(ret)); return krb5_eai_to_heim_errno(ret, save_errno); } ret = add_addrs (context, &addrs, ai); freeaddrinfo (ai); if (ret) return ret; } kdc_flags.b = int2KDCOptions(flags); ret = krb5_get_kdc_cred (context, ccache, kdc_flags, paddrs, NULL, in_creds, &out_creds); krb5_free_addresses (context, &addrs); if (ret) { return ret; } memset (&cred, 0, sizeof(cred)); cred.pvno = 5; cred.msg_type = krb_cred; ALLOC_SEQ(&cred.tickets, 1); if (cred.tickets.val == NULL) { ret = ENOMEM; krb5_set_error_string(context, "malloc: out of memory"); goto out2; } ret = decode_Ticket(out_creds->ticket.data, out_creds->ticket.length, cred.tickets.val, &len); if (ret) goto out3; memset (&enc_krb_cred_part, 0, sizeof(enc_krb_cred_part)); ALLOC_SEQ(&enc_krb_cred_part.ticket_info, 1); if (enc_krb_cred_part.ticket_info.val == NULL) { ret = ENOMEM; krb5_set_error_string(context, "malloc: out of memory"); goto out4; } if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_TIME) { krb5_timestamp sec; int32_t usec; krb5_us_timeofday (context, &sec, &usec); ALLOC(enc_krb_cred_part.timestamp, 1); if (enc_krb_cred_part.timestamp == NULL) { ret = ENOMEM; krb5_set_error_string(context, "malloc: out of memory"); goto out4; } *enc_krb_cred_part.timestamp = sec; ALLOC(enc_krb_cred_part.usec, 1); if (enc_krb_cred_part.usec == NULL) { ret = ENOMEM; krb5_set_error_string(context, "malloc: out of memory"); goto out4; } *enc_krb_cred_part.usec = usec; } else { enc_krb_cred_part.timestamp = NULL; enc_krb_cred_part.usec = NULL; } if (auth_context->local_address && auth_context->local_port) { krb5_boolean noaddr; krb5_const_realm realm; realm = krb5_principal_get_realm(context, out_creds->server); krb5_appdefault_boolean(context, NULL, realm, "no-addresses", paddrs == NULL, &noaddr); if (!noaddr) { ret = krb5_make_addrport (context, &enc_krb_cred_part.s_address, auth_context->local_address, auth_context->local_port); if (ret) goto out4; } } if (auth_context->remote_address) { if (auth_context->remote_port) { krb5_boolean noaddr; krb5_const_realm realm; realm = krb5_principal_get_realm(context, out_creds->server); /* Is this correct, and should we use the paddrs == NULL trick here as well? Having an address-less ticket may indicate that we don't know our own global address, but it does not necessary mean that we don't know the server's. */ krb5_appdefault_boolean(context, NULL, realm, "no-addresses", FALSE, &noaddr); if (!noaddr) { ret = krb5_make_addrport (context, &enc_krb_cred_part.r_address, auth_context->remote_address, auth_context->remote_port); if (ret) goto out4; } } else { ALLOC(enc_krb_cred_part.r_address, 1); if (enc_krb_cred_part.r_address == NULL) { ret = ENOMEM; krb5_set_error_string(context, "malloc: out of memory"); goto out4; } ret = krb5_copy_address (context, auth_context->remote_address, enc_krb_cred_part.r_address); if (ret) goto out4; } } /* fill ticket_info.val[0] */ enc_krb_cred_part.ticket_info.len = 1; krb_cred_info = enc_krb_cred_part.ticket_info.val; copy_EncryptionKey (&out_creds->session, &krb_cred_info->key); ALLOC(krb_cred_info->prealm, 1); copy_Realm (&out_creds->client->realm, krb_cred_info->prealm); ALLOC(krb_cred_info->pname, 1); copy_PrincipalName(&out_creds->client->name, krb_cred_info->pname); ALLOC(krb_cred_info->flags, 1); *krb_cred_info->flags = out_creds->flags.b; ALLOC(krb_cred_info->authtime, 1); *krb_cred_info->authtime = out_creds->times.authtime; ALLOC(krb_cred_info->starttime, 1); *krb_cred_info->starttime = out_creds->times.starttime; ALLOC(krb_cred_info->endtime, 1); *krb_cred_info->endtime = out_creds->times.endtime; ALLOC(krb_cred_info->renew_till, 1); *krb_cred_info->renew_till = out_creds->times.renew_till; ALLOC(krb_cred_info->srealm, 1); copy_Realm (&out_creds->server->realm, krb_cred_info->srealm); ALLOC(krb_cred_info->sname, 1); copy_PrincipalName (&out_creds->server->name, krb_cred_info->sname); ALLOC(krb_cred_info->caddr, 1); copy_HostAddresses (&out_creds->addresses, krb_cred_info->caddr); krb5_free_creds (context, out_creds); /* encode EncKrbCredPart */ ASN1_MALLOC_ENCODE(EncKrbCredPart, buf, buf_size, &enc_krb_cred_part, &len, ret); free_EncKrbCredPart (&enc_krb_cred_part); if (ret) { free_KRB_CRED(&cred); return ret; } if(buf_size != len) krb5_abortx(context, "internal error in ASN.1 encoder"); if (auth_context->flags & KRB5_AUTH_CONTEXT_CLEAR_FORWARDED_CRED) { cred.enc_part.etype = ENCTYPE_NULL; cred.enc_part.kvno = NULL; cred.enc_part.cipher.data = buf; cred.enc_part.cipher.length = buf_size; } else { krb5_keyblock *key; if (auth_context->local_subkey) key = auth_context->local_subkey; else if (auth_context->remote_subkey) key = auth_context->remote_subkey; else key = auth_context->keyblock; ret = krb5_crypto_init(context, key, 0, &crypto); if (ret) { free(buf); free_KRB_CRED(&cred); return ret; } ret = krb5_encrypt_EncryptedData (context, crypto, KRB5_KU_KRB_CRED, buf, len, 0, &cred.enc_part); free(buf); krb5_crypto_destroy(context, crypto); if (ret) { free_KRB_CRED(&cred); return ret; } } ASN1_MALLOC_ENCODE(KRB_CRED, buf, buf_size, &cred, &len, ret); free_KRB_CRED (&cred); if (ret) return ret; if(buf_size != len) krb5_abortx(context, "internal error in ASN.1 encoder"); out_data->length = len; out_data->data = buf; return 0; out4: free_EncKrbCredPart(&enc_krb_cred_part); out3: free_KRB_CRED(&cred); out2: krb5_free_creds (context, out_creds); return ret; }
krb5_error_code _kdc_as_rep(krb5_context context, krb5_kdc_configuration *config, KDC_REQ *req, const krb5_data *req_buffer, krb5_data *reply, const char *from, struct sockaddr *from_addr, int datagram_reply) { KDC_REQ_BODY *b = &req->req_body; AS_REP rep; KDCOptions f = b->kdc_options; hdb_entry_ex *client = NULL, *server = NULL; krb5_enctype cetype, setype, sessionetype; krb5_data e_data; EncTicketPart et; EncKDCRepPart ek; krb5_principal client_princ = NULL, server_princ = NULL; char *client_name = NULL, *server_name = NULL; krb5_error_code ret = 0; const char *e_text = NULL; krb5_crypto crypto; Key *ckey, *skey; EncryptionKey *reply_key; int flags = 0; #ifdef PKINIT pk_client_params *pkp = NULL; #endif memset(&rep, 0, sizeof(rep)); krb5_data_zero(&e_data); if (f.canonicalize) flags |= HDB_F_CANON; if(b->sname == NULL){ ret = KRB5KRB_ERR_GENERIC; e_text = "No server in request"; } else{ ret = _krb5_principalname2krb5_principal (context, &server_princ, *(b->sname), b->realm); if (ret == 0) ret = krb5_unparse_name(context, server_princ, &server_name); } if (ret) { kdc_log(context, config, 0, "AS-REQ malformed server name from %s", from); goto out; } if(b->cname == NULL){ ret = KRB5KRB_ERR_GENERIC; e_text = "No client in request"; } else { if (b->cname->name_type == KRB5_NT_ENTERPRISE_PRINCIPAL) { if (b->cname->name_string.len != 1) { kdc_log(context, config, 0, "AS-REQ malformed canon request from %s, " "enterprise name with %d name components", from, b->cname->name_string.len); ret = KRB5_PARSE_MALFORMED; goto out; } ret = krb5_parse_name(context, b->cname->name_string.val[0], &client_princ); if (ret) goto out; } else { ret = _krb5_principalname2krb5_principal (context, &client_princ, *(b->cname), b->realm); if (ret) goto out; } ret = krb5_unparse_name(context, client_princ, &client_name); } if (ret) { kdc_log(context, config, 0, "AS-REQ malformed client name from %s", from); goto out; } kdc_log(context, config, 0, "AS-REQ %s from %s for %s", client_name, from, server_name); ret = _kdc_db_fetch(context, config, client_princ, HDB_F_GET_CLIENT | flags, NULL, &client); if(ret){ kdc_log(context, config, 0, "UNKNOWN -- %s: %s", client_name, krb5_get_err_text(context, ret)); ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; goto out; } ret = _kdc_db_fetch(context, config, server_princ, HDB_F_GET_SERVER|HDB_F_GET_KRBTGT, NULL, &server); if(ret){ kdc_log(context, config, 0, "UNKNOWN -- %s: %s", server_name, krb5_get_err_text(context, ret)); ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; goto out; } ret = _kdc_windc_client_access(context, client, req); if(ret) goto out; ret = _kdc_check_flags(context, config, client, client_name, server, server_name, TRUE); if(ret) goto out; memset(&et, 0, sizeof(et)); memset(&ek, 0, sizeof(ek)); if(req->padata){ int i; const PA_DATA *pa; int found_pa = 0; log_patypes(context, config, req->padata); #ifdef PKINIT kdc_log(context, config, 5, "Looking for PKINIT pa-data -- %s", client_name); e_text = "No PKINIT PA found"; i = 0; if ((pa = _kdc_find_padata(req, &i, KRB5_PADATA_PK_AS_REQ))) ; if (pa == NULL) { i = 0; if((pa = _kdc_find_padata(req, &i, KRB5_PADATA_PK_AS_REQ_WIN))) ; } if (pa) { char *client_cert = NULL; ret = _kdc_pk_rd_padata(context, config, req, pa, &pkp); if (ret) { ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; kdc_log(context, config, 5, "Failed to decode PKINIT PA-DATA -- %s", client_name); goto ts_enc; } if (ret == 0 && pkp == NULL) goto ts_enc; ret = _kdc_pk_check_client(context, config, client, pkp, &client_cert); if (ret) { e_text = "PKINIT certificate not allowed to " "impersonate principal"; _kdc_pk_free_client_param(context, pkp); kdc_log(context, config, 0, "%s", e_text); pkp = NULL; goto out; } found_pa = 1; et.flags.pre_authent = 1; kdc_log(context, config, 0, "PKINIT pre-authentication succeeded -- %s using %s", client_name, client_cert); free(client_cert); if (pkp) goto preauth_done; } ts_enc: #endif kdc_log(context, config, 5, "Looking for ENC-TS pa-data -- %s", client_name); i = 0; e_text = "No ENC-TS found"; while((pa = _kdc_find_padata(req, &i, KRB5_PADATA_ENC_TIMESTAMP))){ krb5_data ts_data; PA_ENC_TS_ENC p; size_t len; EncryptedData enc_data; Key *pa_key; char *str; found_pa = 1; ret = decode_EncryptedData(pa->padata_value.data, pa->padata_value.length, &enc_data, &len); if (ret) { ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; kdc_log(context, config, 5, "Failed to decode PA-DATA -- %s", client_name); goto out; } ret = hdb_enctype2key(context, &client->entry, enc_data.etype, &pa_key); if(ret){ char *estr; e_text = "No key matches pa-data"; ret = KRB5KDC_ERR_ETYPE_NOSUPP; if(krb5_enctype_to_string(context, enc_data.etype, &estr)) estr = NULL; if(estr == NULL) kdc_log(context, config, 5, "No client key matching pa-data (%d) -- %s", enc_data.etype, client_name); else kdc_log(context, config, 5, "No client key matching pa-data (%s) -- %s", estr, client_name); free(estr); free_EncryptedData(&enc_data); continue; } try_next_key: ret = krb5_crypto_init(context, &pa_key->key, 0, &crypto); if (ret) { kdc_log(context, config, 0, "krb5_crypto_init failed: %s", krb5_get_err_text(context, ret)); free_EncryptedData(&enc_data); continue; } ret = krb5_decrypt_EncryptedData (context, crypto, KRB5_KU_PA_ENC_TIMESTAMP, &enc_data, &ts_data); krb5_crypto_destroy(context, crypto); if(ret){ krb5_error_code ret2; ret2 = krb5_enctype_to_string(context, pa_key->key.keytype, &str); if (ret2) str = NULL; kdc_log(context, config, 5, "Failed to decrypt PA-DATA -- %s " "(enctype %s) error %s", client_name, str ? str : "unknown enctype", krb5_get_err_text(context, ret)); free(str); if(hdb_next_enctype2key(context, &client->entry, enc_data.etype, &pa_key) == 0) goto try_next_key; e_text = "Failed to decrypt PA-DATA"; free_EncryptedData(&enc_data); ret = KRB5KDC_ERR_PREAUTH_FAILED; continue; } free_EncryptedData(&enc_data); ret = decode_PA_ENC_TS_ENC(ts_data.data, ts_data.length, &p, &len); krb5_data_free(&ts_data); if(ret){ e_text = "Failed to decode PA-ENC-TS-ENC"; ret = KRB5KDC_ERR_PREAUTH_FAILED; kdc_log(context, config, 5, "Failed to decode PA-ENC-TS_ENC -- %s", client_name); continue; } free_PA_ENC_TS_ENC(&p); if (abs(kdc_time - p.patimestamp) > context->max_skew) { char client_time[100]; krb5_format_time(context, p.patimestamp, client_time, sizeof(client_time), TRUE); ret = KRB5KRB_AP_ERR_SKEW; kdc_log(context, config, 0, "Too large time skew, " "client time %s is out by %u > %u seconds -- %s", client_time, (unsigned)abs(kdc_time - p.patimestamp), context->max_skew, client_name); #if 0 /* This code is from samba, needs testing */ /* * the following is needed to make windows clients * to retry using the timestamp in the error message * * this is maybe a bug in windows to not trying when e_text * is present... */ e_text = NULL; #else e_text = "Too large time skew"; #endif goto out; } et.flags.pre_authent = 1; ret = krb5_enctype_to_string(context,pa_key->key.keytype, &str); if (ret) str = NULL; kdc_log(context, config, 2, "ENC-TS Pre-authentication succeeded -- %s using %s", client_name, str ? str : "unknown enctype"); free(str); break; } #ifdef PKINIT preauth_done: #endif if(found_pa == 0 && config->require_preauth) goto use_pa; /* We come here if we found a pa-enc-timestamp, but if there was some problem with it, other than too large skew */ if(found_pa && et.flags.pre_authent == 0){ kdc_log(context, config, 0, "%s -- %s", e_text, client_name); e_text = NULL; goto out; } }else if (config->require_preauth || client->entry.flags.require_preauth || server->entry.flags.require_preauth) { METHOD_DATA method_data; PA_DATA *pa; unsigned char *buf; size_t len; use_pa: method_data.len = 0; method_data.val = NULL; ret = realloc_method_data(&method_data); pa = &method_data.val[method_data.len-1]; pa->padata_type = KRB5_PADATA_ENC_TIMESTAMP; pa->padata_value.length = 0; pa->padata_value.data = NULL; #ifdef PKINIT ret = realloc_method_data(&method_data); pa = &method_data.val[method_data.len-1]; pa->padata_type = KRB5_PADATA_PK_AS_REQ; pa->padata_value.length = 0; pa->padata_value.data = NULL; ret = realloc_method_data(&method_data); pa = &method_data.val[method_data.len-1]; pa->padata_type = KRB5_PADATA_PK_AS_REQ_WIN; pa->padata_value.length = 0; pa->padata_value.data = NULL; #endif /* * RFC4120 requires: * - If the client only knows about old enctypes, then send * both info replies (we send 'info' first in the list). * - If the client is 'modern', because it knows about 'new' * enctype types, then only send the 'info2' reply. */ /* XXX check ret */ if (only_older_enctype_p(req)) ret = get_pa_etype_info(context, config, &method_data, &client->entry, b->etype.val, b->etype.len); /* XXX check ret */ ret = get_pa_etype_info2(context, config, &method_data, &client->entry, b->etype.val, b->etype.len); ASN1_MALLOC_ENCODE(METHOD_DATA, buf, len, &method_data, &len, ret); free_METHOD_DATA(&method_data); e_data.data = buf; e_data.length = len; e_text ="Need to use PA-ENC-TIMESTAMP/PA-PK-AS-REQ", ret = KRB5KDC_ERR_PREAUTH_REQUIRED; kdc_log(context, config, 0, "No preauth found, returning PREAUTH-REQUIRED -- %s", client_name); goto out; } /* * Find the client key (for preauth ENC-TS verification and reply * encryption). Then the best encryption type for the KDC and * last the best session key that shared between the client and * KDC runtime enctypes. */ ret = _kdc_find_etype(context, client, b->etype.val, b->etype.len, &ckey, &cetype); if (ret) { kdc_log(context, config, 0, "Client (%s) has no support for etypes", client_name); goto out; } ret = _kdc_get_preferred_key(context, config, server, server_name, &setype, &skey); if(ret) goto out; /* * Select a session enctype from the list of the crypto systems * supported enctype, is supported by the client and is one of the * enctype of the enctype of the krbtgt. * * The later is used as a hint what enctype all KDC are supporting * to make sure a newer version of KDC wont generate a session * enctype that and older version of a KDC in the same realm can't * decrypt. * * But if the KDC admin is paranoid and doesn't want to have "no * the best" enctypes on the krbtgt, lets save the best pick from * the client list and hope that that will work for any other * KDCs. */ { const krb5_enctype *p; krb5_enctype clientbest = ETYPE_NULL; int i, j; p = krb5_kerberos_enctypes(context); sessionetype = ETYPE_NULL; for (i = 0; p[i] != ETYPE_NULL && sessionetype == ETYPE_NULL; i++) { if (krb5_enctype_valid(context, p[i]) != 0) continue; for (j = 0; j < b->etype.len && sessionetype == ETYPE_NULL; j++) { Key *dummy; /* check with client */ if (p[i] != b->etype.val[j]) continue; /* save best of union of { client, crypto system } */ if (clientbest == ETYPE_NULL) clientbest = p[i]; /* check with krbtgt */ ret = hdb_enctype2key(context, &server->entry, p[i], &dummy); if (ret) continue; sessionetype = p[i]; } } /* if krbtgt had no shared keys with client, pick clients best */ if (clientbest != ETYPE_NULL && sessionetype == ETYPE_NULL) { sessionetype = clientbest; } else if (sessionetype == ETYPE_NULL) { kdc_log(context, config, 0, "Client (%s) from %s has no common enctypes with KDC" "to use for the session key", client_name, from); goto out; } } log_as_req(context, config, cetype, setype, b); if(f.renew || f.validate || f.proxy || f.forwarded || f.enc_tkt_in_skey || (f.request_anonymous && !config->allow_anonymous)) { ret = KRB5KDC_ERR_BADOPTION; kdc_log(context, config, 0, "Bad KDC options -- %s", client_name); goto out; } rep.pvno = 5; rep.msg_type = krb_as_rep; copy_Realm(&client->entry.principal->realm, &rep.crealm); if (f.request_anonymous) _kdc_make_anonymous_principalname (&rep.cname); else _krb5_principal2principalname(&rep.cname, client->entry.principal); rep.ticket.tkt_vno = 5; copy_Realm(&server->entry.principal->realm, &rep.ticket.realm); _krb5_principal2principalname(&rep.ticket.sname, server->entry.principal); /* java 1.6 expects the name to be the same type, lets allow that * uncomplicated name-types. */ #define CNT(sp,t) (((sp)->sname->name_type) == KRB5_NT_##t) if (CNT(b, UNKNOWN) || CNT(b, PRINCIPAL) || CNT(b, SRV_INST) || CNT(b, SRV_HST) || CNT(b, SRV_XHST)) rep.ticket.sname.name_type = b->sname->name_type; #undef CNT et.flags.initial = 1; if(client->entry.flags.forwardable && server->entry.flags.forwardable) et.flags.forwardable = f.forwardable; else if (f.forwardable) { ret = KRB5KDC_ERR_POLICY; kdc_log(context, config, 0, "Ticket may not be forwardable -- %s", client_name); goto out; } if(client->entry.flags.proxiable && server->entry.flags.proxiable) et.flags.proxiable = f.proxiable; else if (f.proxiable) { ret = KRB5KDC_ERR_POLICY; kdc_log(context, config, 0, "Ticket may not be proxiable -- %s", client_name); goto out; } if(client->entry.flags.postdate && server->entry.flags.postdate) et.flags.may_postdate = f.allow_postdate; else if (f.allow_postdate){ ret = KRB5KDC_ERR_POLICY; kdc_log(context, config, 0, "Ticket may not be postdatable -- %s", client_name); goto out; } /* check for valid set of addresses */ if(!_kdc_check_addresses(context, config, b->addresses, from_addr)) { ret = KRB5KRB_AP_ERR_BADADDR; kdc_log(context, config, 0, "Bad address list requested -- %s", client_name); goto out; } ret = krb5_generate_random_keyblock(context, sessionetype, &et.key); if (ret) goto out; copy_PrincipalName(&rep.cname, &et.cname); copy_Realm(&rep.crealm, &et.crealm); { time_t start; time_t t; start = et.authtime = kdc_time; if(f.postdated && req->req_body.from){ ALLOC(et.starttime); start = *et.starttime = *req->req_body.from; et.flags.invalid = 1; et.flags.postdated = 1; /* XXX ??? */ } _kdc_fix_time(&b->till); t = *b->till; /* be careful not overflowing */ if(client->entry.max_life) t = start + min(t - start, *client->entry.max_life); if(server->entry.max_life) t = start + min(t - start, *server->entry.max_life); #if 0 t = min(t, start + realm->max_life); #endif et.endtime = t; if(f.renewable_ok && et.endtime < *b->till){ f.renewable = 1; if(b->rtime == NULL){ ALLOC(b->rtime); *b->rtime = 0; } if(*b->rtime < *b->till) *b->rtime = *b->till; } if(f.renewable && b->rtime){ t = *b->rtime; if(t == 0) t = MAX_TIME; if(client->entry.max_renew) t = start + min(t - start, *client->entry.max_renew); if(server->entry.max_renew) t = start + min(t - start, *server->entry.max_renew); #if 0 t = min(t, start + realm->max_renew); #endif ALLOC(et.renew_till); *et.renew_till = t; et.flags.renewable = 1; } } if (f.request_anonymous) et.flags.anonymous = 1; if(b->addresses){ ALLOC(et.caddr); copy_HostAddresses(b->addresses, et.caddr); } et.transited.tr_type = DOMAIN_X500_COMPRESS; krb5_data_zero(&et.transited.contents); copy_EncryptionKey(&et.key, &ek.key); /* The MIT ASN.1 library (obviously) doesn't tell lengths encoded * as 0 and as 0x80 (meaning indefinite length) apart, and is thus * incapable of correctly decoding SEQUENCE OF's of zero length. * * To fix this, always send at least one no-op last_req * * If there's a pw_end or valid_end we will use that, * otherwise just a dummy lr. */ ek.last_req.val = malloc(2 * sizeof(*ek.last_req.val)); if (ek.last_req.val == NULL) { ret = ENOMEM; goto out; } ek.last_req.len = 0; if (client->entry.pw_end && (config->kdc_warn_pwexpire == 0 || kdc_time + config->kdc_warn_pwexpire >= *client->entry.pw_end)) { ek.last_req.val[ek.last_req.len].lr_type = LR_PW_EXPTIME; ek.last_req.val[ek.last_req.len].lr_value = *client->entry.pw_end; ++ek.last_req.len; } if (client->entry.valid_end) { ek.last_req.val[ek.last_req.len].lr_type = LR_ACCT_EXPTIME; ek.last_req.val[ek.last_req.len].lr_value = *client->entry.valid_end; ++ek.last_req.len; } if (ek.last_req.len == 0) { ek.last_req.val[ek.last_req.len].lr_type = LR_NONE; ek.last_req.val[ek.last_req.len].lr_value = 0; ++ek.last_req.len; } ek.nonce = b->nonce; if (client->entry.valid_end || client->entry.pw_end) { ALLOC(ek.key_expiration); if (client->entry.valid_end) { if (client->entry.pw_end) *ek.key_expiration = min(*client->entry.valid_end, *client->entry.pw_end); else *ek.key_expiration = *client->entry.valid_end; } else *ek.key_expiration = *client->entry.pw_end; } else ek.key_expiration = NULL; ek.flags = et.flags; ek.authtime = et.authtime; if (et.starttime) { ALLOC(ek.starttime); *ek.starttime = *et.starttime; } ek.endtime = et.endtime; if (et.renew_till) { ALLOC(ek.renew_till); *ek.renew_till = *et.renew_till; } copy_Realm(&rep.ticket.realm, &ek.srealm); copy_PrincipalName(&rep.ticket.sname, &ek.sname); if(et.caddr){ ALLOC(ek.caddr); copy_HostAddresses(et.caddr, ek.caddr); } ALLOC(rep.padata); rep.padata->len = 0; rep.padata->val = NULL; reply_key = &ckey->key; #if PKINIT if (pkp) { ret = _kdc_pk_mk_pa_reply(context, config, pkp, client, req, req_buffer, &reply_key, rep.padata); if (ret) goto out; ret = _kdc_add_inital_verified_cas(context, config, pkp, &et); if (ret) goto out; } #endif set_salt_padata (rep.padata, ckey->salt); /* Add signing of alias referral */ if (f.canonicalize) { PA_ClientCanonicalized canon; krb5_data data; PA_DATA pa; krb5_crypto crypto; size_t len; memset(&canon, 0, sizeof(canon)); canon.names.requested_name = *b->cname; canon.names.real_name = client->entry.principal->name; ASN1_MALLOC_ENCODE(PA_ClientCanonicalizedNames, data.data, data.length, &canon.names, &len, ret); if (ret) goto out; if (data.length != len) krb5_abortx(context, "internal asn.1 error"); /* sign using "returned session key" */ ret = krb5_crypto_init(context, &et.key, 0, &crypto); if (ret) { free(data.data); goto out; } ret = krb5_create_checksum(context, crypto, KRB5_KU_CANONICALIZED_NAMES, 0, data.data, data.length, &canon.canon_checksum); free(data.data); krb5_crypto_destroy(context, crypto); if (ret) goto out; ASN1_MALLOC_ENCODE(PA_ClientCanonicalized, data.data, data.length, &canon, &len, ret); free_Checksum(&canon.canon_checksum); if (ret) goto out; if (data.length != len) krb5_abortx(context, "internal asn.1 error"); pa.padata_type = KRB5_PADATA_CLIENT_CANONICALIZED; pa.padata_value = data; ret = add_METHOD_DATA(rep.padata, &pa); free(data.data); if (ret) goto out; } if (rep.padata->len == 0) { free(rep.padata); rep.padata = NULL; } /* Add the PAC */ if (send_pac_p(context, req)) { krb5_pac p = NULL; krb5_data data; ret = _kdc_pac_generate(context, client, &p); if (ret) { kdc_log(context, config, 0, "PAC generation failed for -- %s", client_name); goto out; } if (p != NULL) { ret = _krb5_pac_sign(context, p, et.authtime, client->entry.principal, &skey->key, /* Server key */ &skey->key, /* FIXME: should be krbtgt key */ &data); krb5_pac_free(context, p); if (ret) { kdc_log(context, config, 0, "PAC signing failed for -- %s", client_name); goto out; } ret = _kdc_tkt_add_if_relevant_ad(context, &et, KRB5_AUTHDATA_WIN2K_PAC, &data); krb5_data_free(&data); if (ret) goto out; } } _kdc_log_timestamp(context, config, "AS-REQ", et.authtime, et.starttime, et.endtime, et.renew_till); /* do this as the last thing since this signs the EncTicketPart */ ret = _kdc_add_KRB5SignedPath(context, config, server, setype, NULL, NULL, &et); if (ret) goto out; ret = _kdc_encode_reply(context, config, &rep, &et, &ek, setype, server->entry.kvno, &skey->key, client->entry.kvno, reply_key, &e_text, reply); free_EncTicketPart(&et); free_EncKDCRepPart(&ek); if (ret) goto out; /* */ if (datagram_reply && reply->length > config->max_datagram_reply_length) { krb5_data_free(reply); ret = KRB5KRB_ERR_RESPONSE_TOO_BIG; e_text = "Reply packet too large"; } out: free_AS_REP(&rep); if(ret){ krb5_mk_error(context, ret, e_text, (e_data.data ? &e_data : NULL), client_princ, server_princ, NULL, NULL, reply); ret = 0; } #ifdef PKINIT if (pkp) _kdc_pk_free_client_param(context, pkp); #endif if (e_data.data) free(e_data.data); if (client_princ) krb5_free_principal(context, client_princ); free(client_name); if (server_princ) krb5_free_principal(context, server_princ); free(server_name); if(client) _kdc_free_ent(context, client); if(server) _kdc_free_ent(context, server); return ret; }
/* DCE compatible decrypt proc */ static krb5_error_code KRB5_CALLCONV decrypt_tkt_with_subkey (krb5_context context, krb5_keyblock *key, krb5_key_usage usage, krb5_const_pointer skey, krb5_kdc_rep *dec_rep) { const krb5_keyblock *subkey = skey; krb5_error_code ret = 0; krb5_data data; size_t size; krb5_crypto crypto; assert(usage == 0); krb5_data_zero(&data); /* * start out with trying with subkey if we have one */ if (subkey) { ret = krb5_crypto_init(context, subkey, 0, &crypto); if (ret) return ret; ret = krb5_decrypt_EncryptedData (context, crypto, KRB5_KU_TGS_REP_ENC_PART_SUB_KEY, &dec_rep->kdc_rep.enc_part, &data); /* * If the is Windows 2000 DC, we need to retry with key usage * 8 when doing ARCFOUR. */ if (ret && subkey->keytype == ETYPE_ARCFOUR_HMAC_MD5) { ret = krb5_decrypt_EncryptedData(context, crypto, 8, &dec_rep->kdc_rep.enc_part, &data); } krb5_crypto_destroy(context, crypto); } if (subkey == NULL || ret) { ret = krb5_crypto_init(context, key, 0, &crypto); if (ret) return ret; ret = krb5_decrypt_EncryptedData (context, crypto, KRB5_KU_TGS_REP_ENC_PART_SESSION, &dec_rep->kdc_rep.enc_part, &data); krb5_crypto_destroy(context, crypto); } if (ret) return ret; ret = decode_EncASRepPart(data.data, data.length, &dec_rep->enc_part, &size); if (ret) ret = decode_EncTGSRepPart(data.data, data.length, &dec_rep->enc_part, &size); if (ret) krb5_set_error_message(context, ret, N_("Failed to decode encpart in ticket", "")); krb5_data_free (&data); return ret; }
static void encode_ticket (krb5_context context, EncryptionKey *skey, krb5_enctype etype, int skvno, krb5_creds *cred) { size_t len, size; char *buf; krb5_error_code ret; krb5_crypto crypto; EncryptedData enc_part; EncTicketPart et; Ticket ticket; memset (&enc_part, 0, sizeof(enc_part)); memset (&ticket, 0, sizeof(ticket)); /* * Set up `enc_part' */ et.flags = cred->flags.b; et.key = cred->session; et.crealm = cred->client->realm; copy_PrincipalName(&cred->client->name, &et.cname); { krb5_data empty_string; krb5_data_zero(&empty_string); et.transited.tr_type = DOMAIN_X500_COMPRESS; et.transited.contents = empty_string; } et.authtime = cred->times.authtime; et.starttime = NULL; et.endtime = cred->times.endtime; et.renew_till = NULL; et.caddr = &cred->addresses; et.authorization_data = NULL; /* XXX allow random authorization_data */ /* * Encrypt `enc_part' of ticket with service key */ ASN1_MALLOC_ENCODE(EncTicketPart, buf, len, &et, &size, ret); if (ret) krb5_err(context, 1, ret, "EncTicketPart"); ret = krb5_crypto_init(context, skey, etype, &crypto); if (ret) krb5_err(context, 1, ret, "krb5_crypto_init"); ret = krb5_encrypt_EncryptedData (context, crypto, KRB5_KU_TICKET, buf, len, skvno, &ticket.enc_part); if (ret) krb5_err(context, 1, ret, "krb5_encrypt_EncryptedData"); free(buf); krb5_crypto_destroy(context, crypto); /* * Encode ticket */ ticket.tkt_vno = 5; ticket.realm = cred->server->realm; copy_PrincipalName(&cred->server->name, &ticket.sname); ASN1_MALLOC_ENCODE(Ticket, buf, len, &ticket, &size, ret); if(ret) krb5_err (context, 1, ret, "encode_Ticket"); krb5_data_copy(&cred->ticket, buf, len); free(buf); }
static OM_uint32 gsskrb5_acceptor_start(OM_uint32 * minor_status, gsskrb5_ctx ctx, krb5_context context, const gss_cred_id_t acceptor_cred_handle, const gss_buffer_t input_token_buffer, const gss_channel_bindings_t input_chan_bindings, gss_name_t * src_name, gss_OID * mech_type, gss_buffer_t output_token, OM_uint32 * ret_flags, OM_uint32 * time_rec, gss_cred_id_t * delegated_cred_handle) { krb5_error_code kret; OM_uint32 ret = GSS_S_COMPLETE; krb5_data indata; krb5_flags ap_options; krb5_keytab keytab = NULL; int is_cfx = 0; const gsskrb5_cred acceptor_cred = (gsskrb5_cred)acceptor_cred_handle; /* * We may, or may not, have an escapsulation. */ ret = _gsskrb5_decapsulate (minor_status, input_token_buffer, &indata, "\x01\x00", GSS_KRB5_MECHANISM); if (ret) { /* Assume that there is no OID wrapping. */ indata.length = input_token_buffer->length; indata.data = input_token_buffer->value; } /* * We need to get our keytab */ if (acceptor_cred == NULL) { if (_gsskrb5_keytab != NULL) keytab = _gsskrb5_keytab; } else if (acceptor_cred->keytab != NULL) { keytab = acceptor_cred->keytab; } /* * We need to check the ticket and create the AP-REP packet */ { krb5_rd_req_in_ctx in = NULL; krb5_rd_req_out_ctx out = NULL; krb5_principal server = NULL; if (acceptor_cred) server = acceptor_cred->principal; kret = krb5_rd_req_in_ctx_alloc(context, &in); if (kret == 0) kret = krb5_rd_req_in_set_keytab(context, in, keytab); if (kret) { if (in) krb5_rd_req_in_ctx_free(context, in); *minor_status = kret; return GSS_S_FAILURE; } kret = krb5_rd_req_ctx(context, &ctx->auth_context, &indata, server, in, &out); krb5_rd_req_in_ctx_free(context, in); if (kret == KRB5KRB_AP_ERR_SKEW || kret == KRB5KRB_AP_ERR_TKT_NYV) { /* * No reply in non-MUTUAL mode, but we don't know that its * non-MUTUAL mode yet, thats inside the 8003 checksum, so * lets only send the error token on clock skew, that * limit when send error token for non-MUTUAL. */ return send_error_token(minor_status, context, kret, server, &indata, output_token); } else if (kret) { *minor_status = kret; return GSS_S_FAILURE; } /* * we need to remember some data on the context_handle. */ kret = krb5_rd_req_out_get_ap_req_options(context, out, &ap_options); if (kret == 0) kret = krb5_rd_req_out_get_ticket(context, out, &ctx->ticket); if (kret == 0) kret = krb5_rd_req_out_get_keyblock(context, out, &ctx->service_keyblock); ctx->lifetime = ctx->ticket->ticket.endtime; krb5_rd_req_out_ctx_free(context, out); if (kret) { ret = GSS_S_FAILURE; *minor_status = kret; return ret; } } /* * We need to copy the principal names to the context and the * calling layer. */ kret = krb5_copy_principal(context, ctx->ticket->client, &ctx->source); if (kret) { ret = GSS_S_FAILURE; *minor_status = kret; } kret = krb5_copy_principal(context, ctx->ticket->server, &ctx->target); if (kret) { ret = GSS_S_FAILURE; *minor_status = kret; return ret; } /* * We need to setup some compat stuff, this assumes that * context_handle->target is already set. */ ret = _gss_DES3_get_mic_compat(minor_status, ctx, context); if (ret) return ret; if (src_name != NULL) { kret = krb5_copy_principal (context, ctx->ticket->client, (gsskrb5_name*)src_name); if (kret) { ret = GSS_S_FAILURE; *minor_status = kret; return ret; } } /* * We need to get the flags out of the 8003 checksum. */ { krb5_authenticator authenticator; kret = krb5_auth_con_getauthenticator(context, ctx->auth_context, &authenticator); if(kret) { ret = GSS_S_FAILURE; *minor_status = kret; return ret; } if (authenticator->cksum == NULL) { krb5_free_authenticator(context, &authenticator); *minor_status = 0; return GSS_S_BAD_BINDINGS; } if (authenticator->cksum->cksumtype == CKSUMTYPE_GSSAPI) { ret = _gsskrb5_verify_8003_checksum(minor_status, input_chan_bindings, authenticator->cksum, &ctx->flags, &ctx->fwd_data); krb5_free_authenticator(context, &authenticator); if (ret) { return ret; } } else { krb5_crypto crypto; kret = krb5_crypto_init(context, ctx->auth_context->keyblock, 0, &crypto); if(kret) { krb5_free_authenticator(context, &authenticator); ret = GSS_S_FAILURE; *minor_status = kret; return ret; } /* * Windows accepts Samba3's use of a kerberos, rather than * GSSAPI checksum here */ kret = krb5_verify_checksum(context, crypto, KRB5_KU_AP_REQ_AUTH_CKSUM, NULL, 0, authenticator->cksum); krb5_free_authenticator(context, &authenticator); krb5_crypto_destroy(context, crypto); if(kret) { ret = GSS_S_BAD_SIG; *minor_status = kret; return ret; } /* * Samba style get some flags (but not DCE-STYLE), use * ap_options to guess the mutual flag. */ ctx->flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG; if (ap_options & AP_OPTS_MUTUAL_REQUIRED) ctx->flags |= GSS_C_MUTUAL_FLAG; } } if(ctx->flags & GSS_C_MUTUAL_FLAG) { krb5_data outbuf; int use_subkey = 0; _gsskrb5i_is_cfx(context, ctx, 1); is_cfx = (ctx->more_flags & IS_CFX); if (is_cfx || (ap_options & AP_OPTS_USE_SUBKEY)) { use_subkey = 1; } else { krb5_keyblock *rkey; /* * If there is a initiator subkey, copy that to acceptor * subkey to match Windows behavior */ kret = krb5_auth_con_getremotesubkey(context, ctx->auth_context, &rkey); if (kret == 0) { kret = krb5_auth_con_setlocalsubkey(context, ctx->auth_context, rkey); if (kret == 0) use_subkey = 1; krb5_free_keyblock(context, rkey); } } if (use_subkey) { ctx->more_flags |= ACCEPTOR_SUBKEY; krb5_auth_con_addflags(context, ctx->auth_context, KRB5_AUTH_CONTEXT_USE_SUBKEY, NULL); } kret = krb5_mk_rep(context, ctx->auth_context, &outbuf); if (kret) { *minor_status = kret; return GSS_S_FAILURE; } if (IS_DCE_STYLE(ctx)) { output_token->length = outbuf.length; output_token->value = outbuf.data; } else { ret = _gsskrb5_encapsulate(minor_status, &outbuf, output_token, "\x02\x00", GSS_KRB5_MECHANISM); krb5_data_free (&outbuf); if (ret) return ret; } } ctx->flags |= GSS_C_TRANS_FLAG; /* Remember the flags */ ctx->lifetime = ctx->ticket->ticket.endtime; ctx->more_flags |= OPEN; if (mech_type) *mech_type = GSS_KRB5_MECHANISM; if (time_rec) { ret = _gsskrb5_lifetime_left(minor_status, context, ctx->lifetime, time_rec); if (ret) { return ret; } } /* * When GSS_C_DCE_STYLE is in use, we need ask for a AP-REP from * the client. */ if (IS_DCE_STYLE(ctx)) { /* * Return flags to caller, but we haven't processed * delgations yet */ if (ret_flags) *ret_flags = (ctx->flags & ~GSS_C_DELEG_FLAG); ctx->state = ACCEPTOR_WAIT_FOR_DCESTYLE; return GSS_S_CONTINUE_NEEDED; } ret = gsskrb5_acceptor_ready(minor_status, ctx, context, delegated_cred_handle); if (ret_flags) *ret_flags = ctx->flags; return ret; }
static krb5_error_code digest_request(krb5_context context, krb5_realm realm, krb5_ccache ccache, krb5_key_usage usage, const DigestReqInner *ireq, DigestRepInner *irep) { DigestREQ req; DigestREP rep; krb5_error_code ret; krb5_data data, data2; size_t size = 0; krb5_crypto crypto = NULL; krb5_auth_context ac = NULL; krb5_principal principal = NULL; krb5_ccache id = NULL; krb5_realm r = NULL; krb5_data_zero(&data); krb5_data_zero(&data2); memset(&req, 0, sizeof(req)); memset(&rep, 0, sizeof(rep)); if (ccache == NULL) { ret = krb5_cc_default(context, &id); if (ret) goto out; } else id = ccache; if (realm == NULL) { ret = krb5_get_default_realm(context, &r); if (ret) goto out; } else r = realm; /* * */ ret = krb5_make_principal(context, &principal, r, KRB5_DIGEST_NAME, r, NULL); if (ret) goto out; ASN1_MALLOC_ENCODE(DigestReqInner, data.data, data.length, ireq, &size, ret); if (ret) { krb5_set_error_message(context, ret, N_("Failed to encode digest inner request", "")); goto out; } if (size != data.length) krb5_abortx(context, "ASN.1 internal encoder error"); ret = krb5_mk_req_exact(context, &ac, AP_OPTS_USE_SUBKEY|AP_OPTS_MUTUAL_REQUIRED, principal, NULL, id, &req.apReq); if (ret) goto out; { krb5_keyblock *key; ret = krb5_auth_con_getlocalsubkey(context, ac, &key); if (ret) goto out; if (key == NULL) { ret = EINVAL; krb5_set_error_message(context, ret, N_("Digest failed to get local subkey", "")); goto out; } ret = krb5_crypto_init(context, key, 0, &crypto); krb5_free_keyblock (context, key); if (ret) goto out; } ret = krb5_encrypt_EncryptedData(context, crypto, usage, data.data, data.length, 0, &req.innerReq); if (ret) goto out; krb5_data_free(&data); ASN1_MALLOC_ENCODE(DigestREQ, data.data, data.length, &req, &size, ret); if (ret) { krb5_set_error_message(context, ret, N_("Failed to encode DigestREQest", "")); goto out; } if (size != data.length) krb5_abortx(context, "ASN.1 internal encoder error"); ret = krb5_sendto_kdc(context, &data, &r, &data2); if (ret) goto out; ret = decode_DigestREP(data2.data, data2.length, &rep, NULL); if (ret) { krb5_set_error_message(context, ret, N_("Failed to parse digest response", "")); goto out; } { krb5_ap_rep_enc_part *repl; ret = krb5_rd_rep(context, ac, &rep.apRep, &repl); if (ret) goto out; krb5_free_ap_rep_enc_part(context, repl); } { krb5_keyblock *key; ret = krb5_auth_con_getremotesubkey(context, ac, &key); if (ret) goto out; if (key == NULL) { ret = EINVAL; krb5_set_error_message(context, ret, N_("Digest reply have no remote subkey", "")); goto out; } krb5_crypto_destroy(context, crypto); ret = krb5_crypto_init(context, key, 0, &crypto); krb5_free_keyblock (context, key); if (ret) goto out; } krb5_data_free(&data); ret = krb5_decrypt_EncryptedData(context, crypto, usage, &rep.innerRep, &data); if (ret) goto out; ret = decode_DigestRepInner(data.data, data.length, irep, NULL); if (ret) { krb5_set_error_message(context, ret, N_("Failed to decode digest inner reply", "")); goto out; } out: if (ccache == NULL && id) krb5_cc_close(context, id); if (realm == NULL && r) free(r); if (crypto) krb5_crypto_destroy(context, crypto); if (ac) krb5_auth_con_free(context, ac); if (principal) krb5_free_principal(context, principal); krb5_data_free(&data); krb5_data_free(&data2); free_DigestREQ(&req); free_DigestREP(&rep); return ret; }
static OM_uint32 verify_mic_des3 (OM_uint32 * minor_status, const gsskrb5_ctx context_handle, krb5_context context, const gss_buffer_t message_buffer, const gss_buffer_t token_buffer, gss_qop_t * qop_state, krb5_keyblock *key, char *type ) { u_char *p; u_char *seq; uint32_t seq_number; OM_uint32 ret; krb5_crypto crypto; krb5_data seq_data; int cmp, docompat; Checksum csum; char *tmp; char ivec[8]; p = token_buffer->value; ret = _gsskrb5_verify_header (&p, token_buffer->length, type, GSS_KRB5_MECHANISM); if (ret) return ret; if (memcmp(p, "\x04\x00", 2) != 0) /* SGN_ALG = HMAC SHA1 DES3-KD */ return GSS_S_BAD_SIG; p += 2; if (memcmp (p, "\xff\xff\xff\xff", 4) != 0) return GSS_S_BAD_MIC; p += 4; ret = krb5_crypto_init(context, key, ETYPE_DES3_CBC_NONE, &crypto); if (ret){ *minor_status = ret; return GSS_S_FAILURE; } /* verify sequence number */ docompat = 0; retry: if (docompat) memset(ivec, 0, 8); else memcpy(ivec, p + 8, 8); ret = krb5_decrypt_ivec (context, crypto, KRB5_KU_USAGE_SEQ, p, 8, &seq_data, ivec); if (ret) { if (docompat++) { krb5_crypto_destroy (context, crypto); *minor_status = ret; return GSS_S_FAILURE; } else goto retry; } if (seq_data.length != 8) { krb5_data_free (&seq_data); if (docompat++) { krb5_crypto_destroy (context, crypto); return GSS_S_BAD_MIC; } else goto retry; } HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex); seq = seq_data.data; _gsskrb5_decode_om_uint32(seq, &seq_number); if (context_handle->more_flags & LOCAL) cmp = ct_memcmp(&seq[4], "\xff\xff\xff\xff", 4); else cmp = ct_memcmp(&seq[4], "\x00\x00\x00\x00", 4); krb5_data_free (&seq_data); if (cmp != 0) { krb5_crypto_destroy (context, crypto); *minor_status = 0; HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); return GSS_S_BAD_MIC; } ret = _gssapi_msg_order_check(context_handle->order, seq_number); if (ret) { krb5_crypto_destroy (context, crypto); *minor_status = 0; HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); return ret; } /* verify checksum */ tmp = malloc (message_buffer->length + 8); if (tmp == NULL) { krb5_crypto_destroy (context, crypto); HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); *minor_status = ENOMEM; return GSS_S_FAILURE; } memcpy (tmp, p - 8, 8); memcpy (tmp + 8, message_buffer->value, message_buffer->length); csum.cksumtype = CKSUMTYPE_HMAC_SHA1_DES3; csum.checksum.length = 20; csum.checksum.data = p + 8; ret = krb5_verify_checksum (context, crypto, KRB5_KU_USAGE_SIGN, tmp, message_buffer->length + 8, &csum); free (tmp); if (ret) { krb5_crypto_destroy (context, crypto); *minor_status = ret; HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); return GSS_S_BAD_MIC; } HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); krb5_crypto_destroy (context, crypto); return GSS_S_COMPLETE; }
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_mk_rep(krb5_context context, krb5_auth_context auth_context, krb5_data *outbuf) { krb5_error_code ret; AP_REP ap; EncAPRepPart body; u_char *buf = NULL; size_t buf_size; size_t len = 0; krb5_crypto crypto; ap.pvno = 5; ap.msg_type = krb_ap_rep; memset (&body, 0, sizeof(body)); body.ctime = auth_context->authenticator->ctime; body.cusec = auth_context->authenticator->cusec; if (auth_context->flags & KRB5_AUTH_CONTEXT_USE_SUBKEY) { if (auth_context->local_subkey == NULL) { ret = krb5_auth_con_generatelocalsubkey(context, auth_context, auth_context->keyblock); if(ret) { free_EncAPRepPart(&body); return ret; } } ret = krb5_copy_keyblock(context, auth_context->local_subkey, &body.subkey); if (ret) { free_EncAPRepPart(&body); krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); return ENOMEM; } } else body.subkey = NULL; if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) { if(auth_context->local_seqnumber == 0) krb5_generate_seq_number (context, auth_context->keyblock, &auth_context->local_seqnumber); ALLOC(body.seq_number, 1); if (body.seq_number == NULL) { krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); free_EncAPRepPart(&body); return ENOMEM; } *(body.seq_number) = auth_context->local_seqnumber; } else body.seq_number = NULL; ap.enc_part.etype = auth_context->keyblock->keytype; ap.enc_part.kvno = NULL; ASN1_MALLOC_ENCODE(EncAPRepPart, buf, buf_size, &body, &len, ret); free_EncAPRepPart (&body); if(ret) return ret; if (buf_size != len) krb5_abortx(context, "internal error in ASN.1 encoder"); ret = krb5_crypto_init(context, auth_context->keyblock, 0 /* ap.enc_part.etype */, &crypto); if (ret) { free (buf); return ret; } ret = krb5_encrypt (context, crypto, KRB5_KU_AP_REQ_ENC_PART, buf + buf_size - len, len, &ap.enc_part.cipher); krb5_crypto_destroy(context, crypto); free(buf); if (ret) return ret; ASN1_MALLOC_ENCODE(AP_REP, outbuf->data, outbuf->length, &ap, &len, ret); if (ret == 0 && outbuf->length != len) krb5_abortx(context, "internal error in ASN.1 encoder"); free_AP_REP (&ap); return ret; }
krb5_error_code _kdc_pk_mk_pa_reply(krb5_context context, krb5_kdc_configuration *config, pk_client_params *cp, const hdb_entry_ex *client, krb5_enctype sessionetype, const KDC_REQ *req, const krb5_data *req_buffer, krb5_keyblock *reply_key, krb5_keyblock *sessionkey, METHOD_DATA *md) { krb5_error_code ret; void *buf = NULL; size_t len = 0, size = 0; krb5_enctype enctype; int pa_type; hx509_cert kdc_cert = NULL; size_t i; if (!config->enable_pkinit) { krb5_clear_error_message(context); return 0; } if (req->req_body.etype.len > 0) { for (i = 0; i < req->req_body.etype.len; i++) if (krb5_enctype_valid(context, req->req_body.etype.val[i]) == 0) break; if (req->req_body.etype.len <= i) { ret = KRB5KRB_ERR_GENERIC; krb5_set_error_message(context, ret, "No valid enctype available from client"); goto out; } enctype = req->req_body.etype.val[i]; } else enctype = ETYPE_DES3_CBC_SHA1; if (cp->type == PKINIT_27) { PA_PK_AS_REP rep; const char *type, *other = ""; memset(&rep, 0, sizeof(rep)); pa_type = KRB5_PADATA_PK_AS_REP; if (cp->keyex == USE_RSA) { ContentInfo info; type = "enckey"; rep.element = choice_PA_PK_AS_REP_encKeyPack; ret = krb5_generate_random_keyblock(context, enctype, &cp->reply_key); if (ret) { free_PA_PK_AS_REP(&rep); goto out; } ret = pk_mk_pa_reply_enckey(context, config, cp, req, req_buffer, &cp->reply_key, &info, &kdc_cert); if (ret) { free_PA_PK_AS_REP(&rep); goto out; } ASN1_MALLOC_ENCODE(ContentInfo, rep.u.encKeyPack.data, rep.u.encKeyPack.length, &info, &size, ret); free_ContentInfo(&info); if (ret) { krb5_set_error_message(context, ret, "encoding of Key ContentInfo " "failed %d", ret); free_PA_PK_AS_REP(&rep); goto out; } if (rep.u.encKeyPack.length != size) krb5_abortx(context, "Internal ASN.1 encoder error"); ret = krb5_generate_random_keyblock(context, sessionetype, sessionkey); if (ret) { free_PA_PK_AS_REP(&rep); goto out; } } else { ContentInfo info; switch (cp->keyex) { case USE_DH: type = "dh"; break; case USE_ECDH: type = "ecdh"; break; default: krb5_abortx(context, "unknown keyex"); break; } if (cp->dh_group_name) other = cp->dh_group_name; rep.element = choice_PA_PK_AS_REP_dhInfo; ret = generate_dh_keyblock(context, cp, enctype); if (ret) return ret; ret = pk_mk_pa_reply_dh(context, config, cp, &info, &kdc_cert); if (ret) { free_PA_PK_AS_REP(&rep); krb5_set_error_message(context, ret, "create pa-reply-dh " "failed %d", ret); goto out; } ASN1_MALLOC_ENCODE(ContentInfo, rep.u.dhInfo.dhSignedData.data, rep.u.dhInfo.dhSignedData.length, &info, &size, ret); free_ContentInfo(&info); if (ret) { krb5_set_error_message(context, ret, "encoding of Key ContentInfo " "failed %d", ret); free_PA_PK_AS_REP(&rep); goto out; } if (rep.u.encKeyPack.length != size) krb5_abortx(context, "Internal ASN.1 encoder error"); /* generate the session key using the method from RFC6112 */ { krb5_keyblock kdc_contribution_key; krb5_crypto reply_crypto; krb5_crypto kdccont_crypto; krb5_data p1 = { strlen("PKINIT"), "PKINIT"}; krb5_data p2 = { strlen("KEYEXCHANGE"), "KEYEXCHANGE"}; void *kckdata; size_t kcklen; EncryptedData kx; void *kxdata; size_t kxlen; ret = krb5_generate_random_keyblock(context, sessionetype, &kdc_contribution_key); if (ret) { free_PA_PK_AS_REP(&rep); goto out; } ret = krb5_crypto_init(context, &cp->reply_key, enctype, &reply_crypto); if (ret) { krb5_free_keyblock_contents(context, &kdc_contribution_key); free_PA_PK_AS_REP(&rep); goto out; } ret = krb5_crypto_init(context, &kdc_contribution_key, sessionetype, &kdccont_crypto); if (ret) { krb5_crypto_destroy(context, reply_crypto); krb5_free_keyblock_contents(context, &kdc_contribution_key); free_PA_PK_AS_REP(&rep); goto out; } /* KRB-FX-CF2 */ ret = krb5_crypto_fx_cf2(context, kdccont_crypto, reply_crypto, &p1, &p2, sessionetype, sessionkey); krb5_crypto_destroy(context, kdccont_crypto); if (ret) { krb5_crypto_destroy(context, reply_crypto); krb5_free_keyblock_contents(context, &kdc_contribution_key); free_PA_PK_AS_REP(&rep); goto out; } ASN1_MALLOC_ENCODE(EncryptionKey, kckdata, kcklen, &kdc_contribution_key, &size, ret); krb5_free_keyblock_contents(context, &kdc_contribution_key); if (ret) { krb5_set_error_message(context, ret, "encoding of PKINIT-KX Key failed %d", ret); krb5_crypto_destroy(context, reply_crypto); free_PA_PK_AS_REP(&rep); goto out; } if (kcklen != size) krb5_abortx(context, "Internal ASN.1 encoder error"); ret = krb5_encrypt_EncryptedData(context, reply_crypto, KRB5_KU_PA_PKINIT_KX, kckdata, kcklen, 0, &kx); krb5_crypto_destroy(context, reply_crypto); free(kckdata); if (ret) { free_PA_PK_AS_REP(&rep); goto out; } ASN1_MALLOC_ENCODE(EncryptedData, kxdata, kxlen, &kx, &size, ret); free_EncryptedData(&kx); if (ret) { krb5_set_error_message(context, ret, "encoding of PKINIT-KX failed %d", ret); free_PA_PK_AS_REP(&rep); goto out; } if (kxlen != size) krb5_abortx(context, "Internal ASN.1 encoder error"); /* Add PA-PKINIT-KX */ ret = krb5_padata_add(context, md, KRB5_PADATA_PKINIT_KX, kxdata, kxlen); if (ret) { krb5_set_error_message(context, ret, "Failed adding PKINIT-KX %d", ret); free(buf); goto out; } } } #define use_btmm_with_enckey 0 if (use_btmm_with_enckey && rep.element == choice_PA_PK_AS_REP_encKeyPack) { PA_PK_AS_REP_BTMM btmm; heim_any any; any.data = rep.u.encKeyPack.data; any.length = rep.u.encKeyPack.length; btmm.dhSignedData = NULL; btmm.encKeyPack = &any; ASN1_MALLOC_ENCODE(PA_PK_AS_REP_BTMM, buf, len, &btmm, &size, ret); } else { ASN1_MALLOC_ENCODE(PA_PK_AS_REP, buf, len, &rep, &size, ret); } free_PA_PK_AS_REP(&rep); if (ret) { krb5_set_error_message(context, ret, "encode PA-PK-AS-REP failed %d", ret); goto out; } if (len != size) krb5_abortx(context, "Internal ASN.1 encoder error"); kdc_log(context, config, 0, "PK-INIT using %s %s", type, other); } else if (cp->type == PKINIT_WIN2K) { PA_PK_AS_REP_Win2k rep; ContentInfo info; if (cp->keyex != USE_RSA) { ret = KRB5KRB_ERR_GENERIC; krb5_set_error_message(context, ret, "Windows PK-INIT doesn't support DH"); goto out; } memset(&rep, 0, sizeof(rep)); pa_type = KRB5_PADATA_PK_AS_REP_19; rep.element = choice_PA_PK_AS_REP_Win2k_encKeyPack; ret = krb5_generate_random_keyblock(context, enctype, &cp->reply_key); if (ret) { free_PA_PK_AS_REP_Win2k(&rep); goto out; } ret = pk_mk_pa_reply_enckey(context, config, cp, req, req_buffer, &cp->reply_key, &info, &kdc_cert); if (ret) { free_PA_PK_AS_REP_Win2k(&rep); goto out; } ASN1_MALLOC_ENCODE(ContentInfo, rep.u.encKeyPack.data, rep.u.encKeyPack.length, &info, &size, ret); free_ContentInfo(&info); if (ret) { krb5_set_error_message(context, ret, "encoding of Key ContentInfo " "failed %d", ret); free_PA_PK_AS_REP_Win2k(&rep); goto out; } if (rep.u.encKeyPack.length != size) krb5_abortx(context, "Internal ASN.1 encoder error"); ASN1_MALLOC_ENCODE(PA_PK_AS_REP_Win2k, buf, len, &rep, &size, ret); free_PA_PK_AS_REP_Win2k(&rep); if (ret) { krb5_set_error_message(context, ret, "encode PA-PK-AS-REP-Win2k failed %d", ret); goto out; } if (len != size) krb5_abortx(context, "Internal ASN.1 encoder error"); ret = krb5_generate_random_keyblock(context, sessionetype, sessionkey); if (ret) { free(buf); goto out; } } else krb5_abortx(context, "PK-INIT internal error"); ret = krb5_padata_add(context, md, pa_type, buf, len); if (ret) { krb5_set_error_message(context, ret, "Failed adding PA-PK-AS-REP %d", ret); free(buf); goto out; } if (config->pkinit_kdc_ocsp_file) { if (ocsp.expire == 0 && ocsp.next_update > kdc_time) { struct stat sb; int fd; krb5_data_free(&ocsp.data); ocsp.expire = 0; ocsp.next_update = kdc_time + 60 * 5; fd = open(config->pkinit_kdc_ocsp_file, O_RDONLY); if (fd < 0) { kdc_log(context, config, 0, "PK-INIT failed to open ocsp data file %d", errno); goto out_ocsp; } ret = fstat(fd, &sb); if (ret) { ret = errno; close(fd); kdc_log(context, config, 0, "PK-INIT failed to stat ocsp data %d", ret); goto out_ocsp; } ret = krb5_data_alloc(&ocsp.data, sb.st_size); if (ret) { close(fd); kdc_log(context, config, 0, "PK-INIT failed to stat ocsp data %d", ret); goto out_ocsp; } ocsp.data.length = sb.st_size; ret = read(fd, ocsp.data.data, sb.st_size); close(fd); if (ret != sb.st_size) { kdc_log(context, config, 0, "PK-INIT failed to read ocsp data %d", errno); goto out_ocsp; } ret = hx509_ocsp_verify(context->hx509ctx, kdc_time, kdc_cert, 0, ocsp.data.data, ocsp.data.length, &ocsp.expire); if (ret) { kdc_log(context, config, 0, "PK-INIT failed to verify ocsp data %d", ret); krb5_data_free(&ocsp.data); ocsp.expire = 0; } else if (ocsp.expire > 180) { ocsp.expire -= 180; /* refetch the ocsp before it expire */ ocsp.next_update = ocsp.expire; } else { ocsp.next_update = kdc_time; } out_ocsp: ret = 0; } if (ocsp.expire != 0 && ocsp.expire > kdc_time) { ret = krb5_padata_add(context, md, KRB5_PADATA_PA_PK_OCSP_RESPONSE, ocsp.data.data, ocsp.data.length); if (ret) { krb5_set_error_message(context, ret, "Failed adding OCSP response %d", ret); goto out; } } } out: if (kdc_cert) hx509_cert_free(kdc_cert); if (ret == 0) ret = krb5_copy_keyblock_contents(context, &cp->reply_key, reply_key); return ret; }
static krb5_error_code check_server_referral(krb5_context context, krb5_kdc_rep *rep, unsigned flags, krb5_const_principal requested, krb5_const_principal returned, krb5_keyblock * key) { krb5_error_code ret; PA_ServerReferralData ref; krb5_crypto session; EncryptedData ed; size_t len; krb5_data data; PA_DATA *pa; int i = 0, cmp; if (rep->kdc_rep.padata == NULL) goto noreferral; pa = krb5_find_padata(rep->kdc_rep.padata->val, rep->kdc_rep.padata->len, KRB5_PADATA_SERVER_REFERRAL, &i); if (pa == NULL) goto noreferral; memset(&ed, 0, sizeof(ed)); memset(&ref, 0, sizeof(ref)); ret = decode_EncryptedData(pa->padata_value.data, pa->padata_value.length, &ed, &len); if (ret) return ret; if (len != pa->padata_value.length) { free_EncryptedData(&ed); krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED, N_("Referral EncryptedData wrong for realm %s", "realm"), requested->realm); return KRB5KRB_AP_ERR_MODIFIED; } ret = krb5_crypto_init(context, key, 0, &session); if (ret) { free_EncryptedData(&ed); return ret; } ret = krb5_decrypt_EncryptedData(context, session, KRB5_KU_PA_SERVER_REFERRAL, &ed, &data); free_EncryptedData(&ed); krb5_crypto_destroy(context, session); if (ret) return ret; ret = decode_PA_ServerReferralData(data.data, data.length, &ref, &len); if (ret) { krb5_data_free(&data); return ret; } krb5_data_free(&data); if (strcmp(requested->realm, returned->realm) != 0) { free_PA_ServerReferralData(&ref); krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED, N_("server ref realm mismatch, " "requested realm %s got back %s", ""), requested->realm, returned->realm); return KRB5KRB_AP_ERR_MODIFIED; } if (krb5_principal_is_krbtgt(context, returned)) { const char *realm = returned->name.name_string.val[1]; if (ref.referred_realm == NULL || strcmp(*ref.referred_realm, realm) != 0) { free_PA_ServerReferralData(&ref); krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED, N_("tgt returned with wrong ref", "")); return KRB5KRB_AP_ERR_MODIFIED; } } else if (krb5_principal_compare(context, returned, requested) == 0) { free_PA_ServerReferralData(&ref); krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED, N_("req princ no same as returned", "")); return KRB5KRB_AP_ERR_MODIFIED; } if (ref.requested_principal_name) { cmp = _krb5_principal_compare_PrincipalName(context, requested, ref.requested_principal_name); if (!cmp) { free_PA_ServerReferralData(&ref); krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED, N_("referred principal not same " "as requested", "")); return KRB5KRB_AP_ERR_MODIFIED; } } else if (flags & EXTRACT_TICKET_AS_REQ) { free_PA_ServerReferralData(&ref); krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED, N_("Requested principal missing on AS-REQ", "")); return KRB5KRB_AP_ERR_MODIFIED; } free_PA_ServerReferralData(&ref); return ret; noreferral: /* * Expect excact match or that we got a krbtgt */ if (krb5_principal_compare(context, requested, returned) != TRUE && (krb5_realm_compare(context, requested, returned) != TRUE && krb5_principal_is_krbtgt(context, returned) != TRUE)) { krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED, N_("Not same server principal returned " "as requested", "")); return KRB5KRB_AP_ERR_MODIFIED; } return 0; }
static krb5_error_code verify_checksum(krb5_context context, const struct PAC_INFO_BUFFER *sig, const krb5_data *data, void *ptr, size_t len, const krb5_keyblock *key) { krb5_storage *sp = NULL; uint32_t type; krb5_error_code ret; Checksum cksum; memset(&cksum, 0, sizeof(cksum)); sp = krb5_storage_from_mem((char *)data->data + sig->offset_lo, sig->buffersize); if (sp == NULL) return krb5_enomem(context); krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE); CHECK(ret, krb5_ret_uint32(sp, &type), out); cksum.cksumtype = type; cksum.checksum.length = sig->buffersize - krb5_storage_seek(sp, 0, SEEK_CUR); cksum.checksum.data = malloc(cksum.checksum.length); if (cksum.checksum.data == NULL) { ret = krb5_enomem(context); goto out; } ret = krb5_storage_read(sp, cksum.checksum.data, cksum.checksum.length); if (ret != cksum.checksum.length) { ret = EINVAL; krb5_set_error_message(context, ret, "PAC checksum missing checksum"); goto out; } if (!krb5_checksum_is_keyed(context, cksum.cksumtype)) { ret = EINVAL; krb5_set_error_message(context, ret, "Checksum type %d not keyed", cksum.cksumtype); goto out; } /* If the checksum is HMAC-MD5, the checksum type is not tied to * the key type, instead the HMAC-MD5 checksum is applied blindly * on whatever key is used for this connection, avoiding issues * with unkeyed checksums on des-cbc-md5 and des-cbc-crc. See * http://comments.gmane.org/gmane.comp.encryption.kerberos.devel/8743 * for the same issue in MIT, and * http://blogs.msdn.com/b/openspecification/archive/2010/01/01/verifying-the-server-signature-in-kerberos-privilege-account-certificate.aspx * for Microsoft's explaination */ if (cksum.cksumtype == CKSUMTYPE_HMAC_MD5) { Checksum local_checksum; memset(&local_checksum, 0, sizeof(local_checksum)); ret = HMAC_MD5_any_checksum(context, key, ptr, len, KRB5_KU_OTHER_CKSUM, &local_checksum); if (ret != 0 || krb5_data_ct_cmp(&local_checksum.checksum, &cksum.checksum) != 0) { ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; krb5_set_error_message(context, ret, N_("PAC integrity check failed for " "hmac-md5 checksum", "")); } krb5_data_free(&local_checksum.checksum); } else { krb5_crypto crypto = NULL; ret = krb5_crypto_init(context, key, 0, &crypto); if (ret) goto out; ret = krb5_verify_checksum(context, crypto, KRB5_KU_OTHER_CKSUM, ptr, len, &cksum); krb5_crypto_destroy(context, crypto); } free(cksum.checksum.data); krb5_storage_free(sp); return ret; out: if (cksum.checksum.data) free(cksum.checksum.data); if (sp) krb5_storage_free(sp); return ret; }
static int rxkad_keytab_decrypt(int kvno, int et, void *in, size_t inlen, void *out, size_t *outlen) { krb5_error_code code; /* use heimdal api if available, since heimdal's interface to krb5_c_decrypt is non-standard and annoying to use */ #ifdef HAVE_KRB5_CRYPTO_INIT krb5_crypto kcrypto; #else krb5_enc_data ind; #endif krb5_data outd; int i, foundkey; MUTEX_ENTER(&krb5_lock); reload_keys(); if (have_keytab_keys == 0) { MUTEX_EXIT(&krb5_lock); return RXKADUNKNOWNKEY; } foundkey = 0; code = -1; for (i = 0; i < nkeys; i++) { /* foundkey determines what error code we return for failure */ if (ktent[i].vno == kvno) foundkey = 1; /* but check against all keys if the enctype matches, for robustness */ if (kb_enctype(kte_keyblock(ktent[i])) == et) { #ifdef HAVE_KRB5_CRYPTO_INIT code = krb5_crypto_init(k5ctx, kte_keyblock(ktent[i]), et, &kcrypto); if (code == 0) { code = krb5_decrypt(k5ctx, kcrypto, KRB5_KEYUSAGE_KDC_REP_TICKET, in, inlen, &outd); krb5_crypto_destroy(k5ctx, kcrypto); } if (code == 0) { if (outd.length > *outlen) { /* This should never happen, but don't assert since we may * be dealing with untrusted user data. */ code = EINVAL; krb5_data_free(&outd); outd.data = NULL; } } if (code == 0) { /* heimdal allocates new memory for the decrypted data; put * the data back into the requested 'out' buffer */ *outlen = outd.length; memcpy(out, outd.data, outd.length); krb5_data_free(&outd); break; } #else outd.length = *outlen; outd.data = out; ind.ciphertext.length = inlen; ind.ciphertext.data = in; ind.enctype = et; ind.kvno = kvno; code = krb5_c_decrypt(k5ctx, kte_keyblock(ktent[i]), KRB5_KEYUSAGE_KDC_REP_TICKET, NULL, &ind, &outd); if (code == 0) { *outlen = outd.length; break; } #endif } } MUTEX_EXIT(&krb5_lock); if (code == 0) return 0; if (foundkey != 0) return RXKADBADTICKET; return RXKADUNKNOWNKEY; }