static krb5_error_code create_checksum(krb5_context context, const krb5_keyblock *key, uint32_t cksumtype, void *data, size_t datalen, void *sig, size_t siglen) { krb5_crypto crypto = NULL; krb5_error_code ret; Checksum cksum; /* 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 (cksumtype == (uint32_t)CKSUMTYPE_HMAC_MD5) { ret = HMAC_MD5_any_checksum(context, key, data, datalen, KRB5_KU_OTHER_CKSUM, &cksum); if (ret) return ret; } else { ret = krb5_crypto_init(context, key, 0, &crypto); if (ret) return ret; ret = krb5_create_checksum(context, crypto, KRB5_KU_OTHER_CKSUM, 0, data, datalen, &cksum); krb5_crypto_destroy(context, crypto); if (ret) return ret; } if (cksum.checksum.length != siglen) { krb5_set_error_message(context, EINVAL, "pac checksum wrong length"); free_Checksum(&cksum); return EINVAL; } memcpy(sig, cksum.checksum.data, siglen); free_Checksum(&cksum); return 0; }
static krb5_error_code pk_check_pkauthenticator(krb5_context context, PKAuthenticator *a, const KDC_REQ *req) { u_char *buf = NULL; size_t buf_size; krb5_error_code ret; size_t len = 0; krb5_timestamp now; Checksum checksum; krb5_timeofday (context, &now); /* XXX cusec */ if (a->ctime == 0 || labs(a->ctime - now) > context->max_skew) { krb5_clear_error_message(context); return KRB5KRB_AP_ERR_SKEW; } ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, &req->req_body, &len, ret); if (ret) { krb5_clear_error_message(context); return ret; } if (buf_size != len) krb5_abortx(context, "Internal error in ASN.1 encoder"); ret = krb5_create_checksum(context, NULL, 0, CKSUMTYPE_SHA1, buf, len, &checksum); free(buf); if (ret) { krb5_clear_error_message(context); return ret; } if (a->paChecksum == NULL) { krb5_clear_error_message(context); ret = KRB5_KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED; goto out; } if (der_heim_octet_string_cmp(a->paChecksum, &checksum.checksum) != 0) { krb5_clear_error_message(context); ret = KRB5KRB_ERR_GENERIC; } out: free_Checksum(&checksum); return ret; }
static krb5_error_code arcfour_mic_cksum(krb5_context context, krb5_keyblock *key, unsigned usage, u_char *sgn_cksum, size_t sgn_cksum_sz, const u_char *v1, size_t l1, const void *v2, size_t l2, const void *v3, size_t l3) { Checksum CKSUM; u_char *ptr; size_t len; krb5_crypto crypto; krb5_error_code ret; assert(sgn_cksum_sz == 8); len = l1 + l2 + l3; ptr = malloc(len); if (ptr == NULL) return ENOMEM; memcpy(ptr, v1, l1); memcpy(ptr + l1, v2, l2); memcpy(ptr + l1 + l2, v3, l3); ret = krb5_crypto_init(context, key, 0, &crypto); if (ret) { free(ptr); return ret; } ret = krb5_create_checksum(context, crypto, usage, 0, ptr, len, &CKSUM); free(ptr); if (ret == 0) { memcpy(sgn_cksum, CKSUM.checksum.data, sgn_cksum_sz); free_Checksum(&CKSUM); } krb5_crypto_destroy(context, crypto); return ret; }
static krb5_error_code make_pac_checksum(TALLOC_CTX *mem_ctx, DATA_BLOB *pac_data, struct PAC_SIGNATURE_DATA *sig, krb5_context context, const krb5_keyblock *keyblock) { krb5_error_code ret; krb5_crypto crypto; Checksum cksum; ret = krb5_crypto_init(context, keyblock, 0, &crypto); if (ret) { DEBUG(0,("krb5_crypto_init() failed: %s\n", smb_get_krb5_error_message(context, ret, mem_ctx))); return ret; } ret = krb5_create_checksum(context, crypto, KRB5_KU_OTHER_CKSUM, 0, pac_data->data, pac_data->length, &cksum); if (ret) { DEBUG(2, ("PAC Verification failed: %s\n", smb_get_krb5_error_message(context, ret, mem_ctx))); } krb5_crypto_destroy(context, crypto); if (ret) { return ret; } sig->type = cksum.cksumtype; sig->signature = data_blob_talloc(mem_ctx, cksum.checksum.data, cksum.checksum.length); free_Checksum(&cksum); return 0; }
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; 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; 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, 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 void KRB5_LIB_CALL krb5_checksum_free(krb5_context context, krb5_checksum *cksum) { free_Checksum(cksum); }
krb5_error_code _kdc_add_KRB5SignedPath(krb5_context context, krb5_kdc_configuration *config, hdb_entry_ex *krbtgt, krb5_enctype enctype, krb5_const_principal server, KRB5SignedPathPrincipals *principals, EncTicketPart *tkt) { krb5_error_code ret; KRB5SignedPath sp; krb5_data data; krb5_crypto crypto = NULL; size_t size; if (server && principals) { ret = add_KRB5SignedPathPrincipals(principals, server); if (ret) return ret; } { KRB5SignedPathData spd; spd.encticket = *tkt; spd.delegated = principals; ASN1_MALLOC_ENCODE(KRB5SignedPathData, data.data, data.length, &spd, &size, ret); if (ret) return ret; if (data.length != size) krb5_abortx(context, "internal asn.1 encoder error"); } { Key *key; ret = hdb_enctype2key(context, &krbtgt->entry, enctype, &key); if (ret == 0) ret = krb5_crypto_init(context, &key->key, 0, &crypto); if (ret) { free(data.data); return ret; } } /* * Fill in KRB5SignedPath */ sp.etype = enctype; sp.delegated = principals; ret = krb5_create_checksum(context, crypto, KRB5_KU_KRB5SIGNEDPATH, 0, data.data, data.length, &sp.cksum); krb5_crypto_destroy(context, crypto); free(data.data); if (ret) return ret; ASN1_MALLOC_ENCODE(KRB5SignedPath, data.data, data.length, &sp, &size, ret); free_Checksum(&sp.cksum); if (ret) return ret; if (data.length != size) krb5_abortx(context, "internal asn.1 encoder error"); /* * Add IF-RELEVANT(KRB5SignedPath) to the last slot in * authorization data field. */ ret = _kdc_tkt_add_if_relevant_ad(context, tkt, KRB5_AUTHDATA_SIGNTICKET, &data); krb5_data_free(&data); 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 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 { 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, &clientdb, &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; } memset(&et, 0, sizeof(et)); memset(&ek, 0, sizeof(ek)); /* * Find the client key for reply encryption and pa-type salt, Pick * the client key upfront before the other keys because that is * going to affect what enctypes we are going to use in * ETYPE-INFO{,2}. */ 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; } /* * 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, 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; 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) { 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); /* * 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; 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); 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); #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 || 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} */ if (ckey) { /* * 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; } if (clientdb->hdb_auth_status) (clientdb->hdb_auth_status)(context, clientdb, client, HDB_AUTH_SUCCESS); /* * 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; /* * 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; /* * 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; 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) { 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 = 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); } ALLOC(rep.padata); rep.padata->len = 0; rep.padata->val = NULL; #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 if (ckey) { reply_key = &ckey->key; ret = krb5_generate_random_keyblock(context, sessionetype, &et.key); if (ret) goto out; } else { 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 (ckey) 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.mapped_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; }
krb5_error_code _krb5_init_tgs_req(krb5_context context, krb5_ccache ccache, krb5_addresses *addresses, krb5_kdc_flags flags, krb5_const_principal impersonate_principal, Ticket *second_ticket, krb5_creds *in_creds, krb5_creds *krbtgt, unsigned nonce, METHOD_DATA *padata, krb5_keyblock **subkey, TGS_REQ *t) { krb5_auth_context ac = NULL; krb5_error_code ret = 0; /* inherit the forwardable/proxyable flags from the krbtgt */ flags.b.forwardable = krbtgt->flags.b.forwardable; flags.b.proxiable = krbtgt->flags.b.proxiable; if (ccache->ops->tgt_req) { KERB_TGS_REQ_OUT out; KERB_TGS_REQ_IN in; memset(&in, 0, sizeof(in)); memset(&out, 0, sizeof(out)); ret = ccache->ops->tgt_req(context, ccache, &in, &out); if (ret) return ret; free_KERB_TGS_REQ_OUT(&out); return 0; } memset(t, 0, sizeof(*t)); if (impersonate_principal) { krb5_crypto crypto; PA_S4U2Self self; krb5_data data; void *buf; size_t size, len; self.name = impersonate_principal->name; self.realm = impersonate_principal->realm; self.auth = rk_UNCONST("Kerberos"); ret = _krb5_s4u2self_to_checksumdata(context, &self, &data); if (ret) goto fail; ret = krb5_crypto_init(context, &krbtgt->session, 0, &crypto); if (ret) { krb5_data_free(&data); goto fail; } 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) goto fail; ASN1_MALLOC_ENCODE(PA_S4U2Self, buf, len, &self, &size, ret); free_Checksum(&self.cksum); if (ret) goto fail; if (len != size) krb5_abortx(context, "internal asn1 error"); ret = krb5_padata_add(context, padata, KRB5_PADATA_FOR_USER, buf, len); if (ret) goto fail; } t->pvno = 5; t->msg_type = krb_tgs_req; if (in_creds->session.keytype) { ALLOC_SEQ(&t->req_body.etype, 1); if(t->req_body.etype.val == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto fail; } t->req_body.etype.val[0] = in_creds->session.keytype; } else { ret = _krb5_init_etype(context, KRB5_PDU_TGS_REQUEST, &t->req_body.etype.len, &t->req_body.etype.val, NULL); } if (ret) goto fail; t->req_body.addresses = addresses; t->req_body.kdc_options = flags.b; ret = copy_Realm(&in_creds->server->realm, &t->req_body.realm); if (ret) goto fail; ALLOC(t->req_body.sname, 1); if (t->req_body.sname == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto fail; } /* some versions of some code might require that the client be present in TGS-REQs, but this is clearly against the spec */ ret = copy_PrincipalName(&in_creds->server->name, t->req_body.sname); if (ret) goto fail; /* req_body.till should be NULL if there is no endtime specified, but old MIT code (like DCE secd) doesn't like that */ ALLOC(t->req_body.till, 1); if(t->req_body.till == NULL){ ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto fail; } *t->req_body.till = in_creds->times.endtime; t->req_body.nonce = nonce; if(second_ticket){ ALLOC(t->req_body.additional_tickets, 1); if (t->req_body.additional_tickets == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto fail; } ALLOC_SEQ(t->req_body.additional_tickets, 1); if (t->req_body.additional_tickets->val == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto fail; } ret = copy_Ticket(second_ticket, t->req_body.additional_tickets->val); if (ret) goto fail; } ALLOC(t->padata, 1); if (t->padata == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto fail; } ALLOC_SEQ(t->padata, 1 + padata->len); if (t->padata->val == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto fail; } { size_t i; for (i = 0; i < padata->len; i++) { ret = copy_PA_DATA(&padata->val[i], &t->padata->val[i + 1]); if (ret) { krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto fail; } } } ret = krb5_auth_con_init(context, &ac); if(ret) goto fail; ret = krb5_auth_con_generatelocalsubkey(context, ac, &krbtgt->session); if (ret) goto fail; ret = set_auth_data (context, &t->req_body, &in_creds->authdata, ac->local_subkey); if (ret) goto fail; ret = make_pa_tgs_req(context, ac, &t->req_body, &t->padata->val[0], ccache, krbtgt); if(ret) goto fail; ret = krb5_auth_con_getlocalsubkey(context, ac, subkey); if (ret) goto fail; fail: if (ac) krb5_auth_con_free(context, ac); if (ret) { t->req_body.addresses = NULL; free_TGS_REQ (t); } return ret; }
static OM_uint32 init_auth (OM_uint32 * minor_status, gsskrb5_cred initiator_cred_handle, gsskrb5_ctx ctx, krb5_const_principal name, const gss_OID mech_type, OM_uint32 req_flags, OM_uint32 time_req, const gss_channel_bindings_t input_chan_bindings, const gss_buffer_t input_token, gss_OID * actual_mech_type, gss_buffer_t output_token, OM_uint32 * ret_flags, OM_uint32 * time_rec ) { OM_uint32 ret = GSS_S_FAILURE; krb5_error_code kret; krb5_flags ap_options; krb5_creds *cred = NULL; krb5_data outbuf; krb5_ccache ccache = NULL; uint32_t flags; krb5_data authenticator; Checksum cksum; krb5_enctype enctype; krb5_data fwd_data; OM_uint32 lifetime_rec; krb5_data_zero(&outbuf); krb5_data_zero(&fwd_data); *minor_status = 0; if (actual_mech_type) *actual_mech_type = GSS_KRB5_MECHANISM; if (initiator_cred_handle == NULL) { kret = krb5_cc_default (_gsskrb5_context, &ccache); if (kret) { _gsskrb5_set_error_string (); *minor_status = kret; ret = GSS_S_FAILURE; goto failure; } } else ccache = initiator_cred_handle->ccache; kret = krb5_cc_get_principal (_gsskrb5_context, ccache, &ctx->source); if (kret) { _gsskrb5_set_error_string (); *minor_status = kret; ret = GSS_S_FAILURE; goto failure; } kret = krb5_copy_principal (_gsskrb5_context, name, &ctx->target); if (kret) { _gsskrb5_set_error_string (); *minor_status = kret; ret = GSS_S_FAILURE; goto failure; } ret = _gss_DES3_get_mic_compat(minor_status, ctx); if (ret) goto failure; ret = gsskrb5_get_creds(minor_status, ccache, ctx, ctx->target, time_req, time_rec, &cred); if (ret) goto failure; ctx->lifetime = cred->times.endtime; ret = _gsskrb5_lifetime_left(minor_status, ctx->lifetime, &lifetime_rec); if (ret) { goto failure; } if (lifetime_rec == 0) { *minor_status = 0; ret = GSS_S_CONTEXT_EXPIRED; goto failure; } krb5_auth_con_setkey(_gsskrb5_context, ctx->auth_context, &cred->session); kret = krb5_auth_con_generatelocalsubkey(_gsskrb5_context, ctx->auth_context, &cred->session); if(kret) { _gsskrb5_set_error_string (); *minor_status = kret; ret = GSS_S_FAILURE; goto failure; } /* * If the credential doesn't have ok-as-delegate, check what local * policy say about ok-as-delegate, default is FALSE that makes * code ignore the KDC setting and follow what the application * requested. If its TRUE, strip of the GSS_C_DELEG_FLAG if the * KDC doesn't set ok-as-delegate. */ if (!cred->flags.b.ok_as_delegate) { krb5_boolean delegate; krb5_appdefault_boolean(_gsskrb5_context, "gssapi", name->realm, "ok-as-delegate", FALSE, &delegate); if (delegate) req_flags &= ~GSS_C_DELEG_FLAG; } flags = 0; ap_options = 0; if (req_flags & GSS_C_DELEG_FLAG) do_delegation (ctx->auth_context, ccache, cred, name, &fwd_data, &flags); if (req_flags & GSS_C_MUTUAL_FLAG) { flags |= GSS_C_MUTUAL_FLAG; ap_options |= AP_OPTS_MUTUAL_REQUIRED; } if (req_flags & GSS_C_REPLAY_FLAG) flags |= GSS_C_REPLAY_FLAG; if (req_flags & GSS_C_SEQUENCE_FLAG) flags |= GSS_C_SEQUENCE_FLAG; if (req_flags & GSS_C_ANON_FLAG) ; /* XXX */ if (req_flags & GSS_C_DCE_STYLE) { /* GSS_C_DCE_STYLE implies GSS_C_MUTUAL_FLAG */ flags |= GSS_C_DCE_STYLE | GSS_C_MUTUAL_FLAG; ap_options |= AP_OPTS_MUTUAL_REQUIRED; } if (req_flags & GSS_C_IDENTIFY_FLAG) flags |= GSS_C_IDENTIFY_FLAG; if (req_flags & GSS_C_EXTENDED_ERROR_FLAG) flags |= GSS_C_EXTENDED_ERROR_FLAG; flags |= GSS_C_CONF_FLAG; flags |= GSS_C_INTEG_FLAG; flags |= GSS_C_TRANS_FLAG; if (ret_flags) *ret_flags = flags; ctx->flags = flags; ctx->more_flags |= LOCAL; ret = _gsskrb5_create_8003_checksum (minor_status, input_chan_bindings, flags, &fwd_data, &cksum); krb5_data_free (&fwd_data); if (ret) goto failure; enctype = ctx->auth_context->keyblock->keytype; kret = krb5_build_authenticator (_gsskrb5_context, ctx->auth_context, enctype, cred, &cksum, NULL, &authenticator, KRB5_KU_AP_REQ_AUTH); if (kret) { _gsskrb5_set_error_string (); *minor_status = kret; ret = GSS_S_FAILURE; goto failure; } kret = krb5_build_ap_req (_gsskrb5_context, enctype, cred, ap_options, authenticator, &outbuf); if (kret) { _gsskrb5_set_error_string (); *minor_status = kret; ret = GSS_S_FAILURE; goto failure; } ret = _gsskrb5_encapsulate (minor_status, &outbuf, output_token, (u_char *)"\x01\x00", GSS_KRB5_MECHANISM); if (ret) goto failure; krb5_data_free (&outbuf); krb5_free_creds(_gsskrb5_context, cred); free_Checksum(&cksum); if (initiator_cred_handle == NULL) krb5_cc_close(_gsskrb5_context, ccache); if (flags & GSS_C_MUTUAL_FLAG) { ctx->state = INITIATOR_WAIT_FOR_MUTAL; return GSS_S_CONTINUE_NEEDED; } return gsskrb5_initiator_ready(minor_status, ctx); failure: if(cred) krb5_free_creds(_gsskrb5_context, cred); if (ccache && initiator_cred_handle == NULL) krb5_cc_close(_gsskrb5_context, ccache); 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_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; }
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; } #if 0 { /* This is somewhat bogus since we're possibly overwriting a value specified by the user, but it's the easiest way to make the code use a compatible enctype */ Ticket ticket; krb5_keytype ticket_keytype; ret = decode_Ticket(in_creds->ticket.data, in_creds->ticket.length, &ticket, NULL); krb5_enctype_to_keytype (context, ticket.enc_part.etype, &ticket_keytype); if (ticket_keytype == in_creds->session.keytype) krb5_auth_setenctype(context, ac, ticket.enc_part.etype); free_Ticket(&ticket); } #endif 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) { /* 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, NULL, &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; }