krb5_error_code check_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; cksum.cksumtype = (CKSUMTYPE)sig->type; cksum.checksum.length = sig->signature.length; cksum.checksum.data = sig->signature.data; 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_verify_checksum(context, crypto, KRB5_KU_OTHER_CKSUM, pac_data.data, pac_data.length, &cksum); krb5_crypto_destroy(context, crypto); return ret; }
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_verify_authenticator_checksum(krb5_context context, krb5_auth_context ac, void *data, size_t len) { krb5_error_code ret; krb5_keyblock *key = NULL; krb5_authenticator authenticator; krb5_crypto crypto; ret = krb5_auth_con_getauthenticator(context, ac, &authenticator); if (ret) return ret; if (authenticator->cksum == NULL) { ret = -17; goto out; } ret = krb5_auth_con_getkey(context, ac, &key); if (ret) goto out; ret = krb5_crypto_init(context, key, 0, &crypto); if (ret) goto out; ret = krb5_verify_checksum(context, crypto, KRB5_KU_AP_REQ_AUTH_CKSUM, data, len, authenticator->cksum); krb5_crypto_destroy(context, crypto); out: krb5_free_authenticator(context, &authenticator); krb5_free_keyblock(context, key); return ret; }
static krb5_error_code verify_checksum(krb5_context context, krb5_auth_context auth_context, KRB_SAFE *safe) { krb5_error_code ret; u_char *buf; size_t buf_size; size_t len; Checksum c; krb5_crypto crypto; krb5_keyblock *key; c = safe->cksum; safe->cksum.cksumtype = 0; safe->cksum.checksum.data = NULL; safe->cksum.checksum.length = 0; ASN1_MALLOC_ENCODE(KRB_SAFE, buf, buf_size, safe, &len, ret); if(ret) return ret; if(buf_size != len) krb5_abortx(context, "internal error in ASN.1 encoder"); 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 out; ret = krb5_verify_checksum (context, crypto, KRB5_KU_KRB_SAFE_CKSUM, buf + buf_size - len, len, &c); krb5_crypto_destroy(context, crypto); out: safe->cksum = c; free (buf); return ret; }
static krb5_error_code tgs_build_reply(krb5_context context, krb5_kdc_configuration *config, KDC_REQ *req, KDC_REQ_BODY *b, hdb_entry_ex *krbtgt, krb5_enctype krbtgt_etype, krb5_ticket *ticket, krb5_data *reply, const char *from, const char **e_text, AuthorizationData *auth_data, const struct sockaddr *from_addr, int datagram_reply) { krb5_error_code ret; krb5_principal cp = NULL, sp = NULL; krb5_principal client_principal = NULL; char *spn = NULL, *cpn = NULL; hdb_entry_ex *server = NULL, *client = NULL; EncTicketPart *tgt = &ticket->ticket; KRB5SignedPathPrincipals *spp = NULL; const EncryptionKey *ekey; krb5_keyblock sessionkey; krb5_kvno kvno; krb5_data rspac; int cross_realm = 0; PrincipalName *s; Realm r; int nloop = 0; EncTicketPart adtkt; char opt_str[128]; int require_signedpath = 0; memset(&sessionkey, 0, sizeof(sessionkey)); memset(&adtkt, 0, sizeof(adtkt)); krb5_data_zero(&rspac); s = b->sname; r = b->realm; if(b->kdc_options.enc_tkt_in_skey){ Ticket *t; hdb_entry_ex *uu; krb5_principal p; Key *uukey; if(b->additional_tickets == NULL || b->additional_tickets->len == 0){ ret = KRB5KDC_ERR_BADOPTION; /* ? */ kdc_log(context, config, 0, "No second ticket present in request"); goto out; } t = &b->additional_tickets->val[0]; if(!get_krbtgt_realm(&t->sname)){ kdc_log(context, config, 0, "Additional ticket is not a ticket-granting ticket"); ret = KRB5KDC_ERR_POLICY; goto out; } _krb5_principalname2krb5_principal(context, &p, t->sname, t->realm); ret = _kdc_db_fetch(context, config, p, HDB_F_GET_CLIENT|HDB_F_GET_SERVER, NULL, &uu); krb5_free_principal(context, p); if(ret){ if (ret == HDB_ERR_NOENTRY) ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; goto out; } ret = hdb_enctype2key(context, &uu->entry, t->enc_part.etype, &uukey); if(ret){ _kdc_free_ent(context, uu); ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */ goto out; } ret = krb5_decrypt_ticket(context, t, &uukey->key, &adtkt, 0); _kdc_free_ent(context, uu); if(ret) goto out; ret = verify_flags(context, config, &adtkt, spn); if (ret) goto out; s = &adtkt.cname; r = adtkt.crealm; } _krb5_principalname2krb5_principal(context, &sp, *s, r); ret = krb5_unparse_name(context, sp, &spn); if (ret) goto out; _krb5_principalname2krb5_principal(context, &cp, tgt->cname, tgt->crealm); ret = krb5_unparse_name(context, cp, &cpn); if (ret) goto out; unparse_flags (KDCOptions2int(b->kdc_options), asn1_KDCOptions_units(), opt_str, sizeof(opt_str)); if(*opt_str) kdc_log(context, config, 0, "TGS-REQ %s from %s for %s [%s]", cpn, from, spn, opt_str); else kdc_log(context, config, 0, "TGS-REQ %s from %s for %s", cpn, from, spn); /* * Fetch server */ server_lookup: ret = _kdc_db_fetch(context, config, sp, HDB_F_GET_SERVER, NULL, &server); if(ret){ const char *new_rlm; Realm req_rlm; krb5_realm *realms; if ((req_rlm = get_krbtgt_realm(&sp->name)) != NULL) { if(nloop++ < 2) { new_rlm = find_rpath(context, tgt->crealm, req_rlm); if(new_rlm) { kdc_log(context, config, 5, "krbtgt for realm %s " "not found, trying %s", req_rlm, new_rlm); krb5_free_principal(context, sp); free(spn); krb5_make_principal(context, &sp, r, KRB5_TGS_NAME, new_rlm, NULL); ret = krb5_unparse_name(context, sp, &spn); if (ret) goto out; goto server_lookup; } } } else if(need_referral(context, sp, &realms)) { if (strcmp(realms[0], sp->realm) != 0) { kdc_log(context, config, 5, "Returning a referral to realm %s for " "server %s that was not found", realms[0], spn); krb5_free_principal(context, sp); free(spn); krb5_make_principal(context, &sp, r, KRB5_TGS_NAME, realms[0], NULL); ret = krb5_unparse_name(context, sp, &spn); if (ret) goto out; krb5_free_host_realm(context, realms); goto server_lookup; } krb5_free_host_realm(context, realms); } kdc_log(context, config, 0, "Server not found in database: %s: %s", spn, krb5_get_err_text(context, ret)); if (ret == HDB_ERR_NOENTRY) ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; goto out; } ret = _kdc_db_fetch(context, config, cp, HDB_F_GET_CLIENT, NULL, &client); if(ret) { const char *krbtgt_realm; /* * If the client belongs to the same realm as our krbtgt, it * should exist in the local database. * */ krbtgt_realm = krb5_principal_get_comp_string(context, krbtgt->entry.principal, 1); if(strcmp(krb5_principal_get_realm(context, cp), krbtgt_realm) == 0) { if (ret == HDB_ERR_NOENTRY) ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; kdc_log(context, config, 1, "Client no longer in database: %s", cpn); goto out; } kdc_log(context, config, 1, "Client not found in database: %s: %s", cpn, krb5_get_err_text(context, ret)); cross_realm = 1; } /* * Check that service is in the same realm as the krbtgt. If its * not the same, its someone that is using a uni-directional trust * backward. */ if (strcmp(krb5_principal_get_realm(context, sp), krb5_principal_get_comp_string(context, krbtgt->entry.principal, 1)) != 0) { char *tpn; ret = krb5_unparse_name(context, krbtgt->entry.principal, &tpn); kdc_log(context, config, 0, "Request with wrong krbtgt: %s", (ret == 0) ? tpn : "<unknown>"); if(ret == 0) free(tpn); ret = KRB5KRB_AP_ERR_NOT_US; goto out; } /* * */ client_principal = cp; if (client) { const PA_DATA *sdata; int i = 0; sdata = _kdc_find_padata(req, &i, KRB5_PADATA_S4U2SELF); if (sdata) { krb5_crypto crypto; krb5_data datack; PA_S4U2Self self; char *selfcpn = NULL; const char *str; ret = decode_PA_S4U2Self(sdata->padata_value.data, sdata->padata_value.length, &self, NULL); if (ret) { kdc_log(context, config, 0, "Failed to decode PA-S4U2Self"); goto out; } ret = _krb5_s4u2self_to_checksumdata(context, &self, &datack); if (ret) goto out; ret = krb5_crypto_init(context, &tgt->key, 0, &crypto); if (ret) { free_PA_S4U2Self(&self); krb5_data_free(&datack); kdc_log(context, config, 0, "krb5_crypto_init failed: %s", krb5_get_err_text(context, ret)); goto out; } ret = krb5_verify_checksum(context, crypto, KRB5_KU_OTHER_CKSUM, datack.data, datack.length, &self.cksum); krb5_data_free(&datack); krb5_crypto_destroy(context, crypto); if (ret) { free_PA_S4U2Self(&self); kdc_log(context, config, 0, "krb5_verify_checksum failed for S4U2Self: %s", krb5_get_err_text(context, ret)); goto out; } ret = _krb5_principalname2krb5_principal(context, &client_principal, self.name, self.realm); free_PA_S4U2Self(&self); if (ret) goto out; ret = krb5_unparse_name(context, client_principal, &selfcpn); if (ret) goto out; /* * Check that service doing the impersonating is * requesting a ticket to it-self. */ if (krb5_principal_compare(context, cp, sp) != TRUE) { kdc_log(context, config, 0, "S4U2Self: %s is not allowed " "to impersonate some other user " "(tried for user %s to service %s)", cpn, selfcpn, spn); free(selfcpn); ret = KRB5KDC_ERR_BADOPTION; /* ? */ goto out; } /* * If the service isn't trusted for authentication to * delegation, remove the forward flag. */ if (client->entry.flags.trusted_for_delegation) { str = "[forwardable]"; } else { b->kdc_options.forwardable = 0; str = ""; } kdc_log(context, config, 0, "s4u2self %s impersonating %s to " "service %s %s", cpn, selfcpn, spn, str); free(selfcpn); } } /* * Constrained delegation */ if (client != NULL && b->additional_tickets != NULL && b->additional_tickets->len != 0 && b->kdc_options.enc_tkt_in_skey == 0) { Key *clientkey; Ticket *t; char *str; t = &b->additional_tickets->val[0]; ret = hdb_enctype2key(context, &client->entry, t->enc_part.etype, &clientkey); if(ret){ ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */ goto out; } ret = krb5_decrypt_ticket(context, t, &clientkey->key, &adtkt, 0); if (ret) { kdc_log(context, config, 0, "failed to decrypt ticket for " "constrained delegation from %s to %s ", spn, cpn); goto out; } /* check that ticket is valid */ if (adtkt.flags.forwardable == 0) { kdc_log(context, config, 0, "Missing forwardable flag on ticket for " "constrained delegation from %s to %s ", spn, cpn); ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */ goto out; } ret = check_constrained_delegation(context, config, client, sp); if (ret) { kdc_log(context, config, 0, "constrained delegation from %s to %s not allowed", spn, cpn); goto out; } ret = _krb5_principalname2krb5_principal(context, &client_principal, adtkt.cname, adtkt.crealm); if (ret) goto out; ret = krb5_unparse_name(context, client_principal, &str); if (ret) goto out; ret = verify_flags(context, config, &adtkt, str); if (ret) { free(str); goto out; } /* * Check KRB5SignedPath in authorization data and add new entry to * make sure servers can't fake a ticket to us. */ ret = check_KRB5SignedPath(context, config, krbtgt, &adtkt, &spp, 1); if (ret) { kdc_log(context, config, 0, "KRB5SignedPath check from service %s failed " "for delegation to %s for client %s " "from %s failed with %s", spn, str, cpn, from, krb5_get_err_text(context, ret)); free(str); goto out; } kdc_log(context, config, 0, "constrained delegation for %s " "from %s to %s", str, cpn, spn); free(str); /* * Also require that the KDC have issue the service's krbtgt * used to do the request. */ require_signedpath = 1; } /* * Check flags */ ret = _kdc_check_flags(context, config, client, cpn, server, spn, FALSE); if(ret) goto out; if((b->kdc_options.validate || b->kdc_options.renew) && !krb5_principal_compare(context, krbtgt->entry.principal, server->entry.principal)){ kdc_log(context, config, 0, "Inconsistent request."); ret = KRB5KDC_ERR_SERVER_NOMATCH; goto out; } /* check for valid set of addresses */ if(!_kdc_check_addresses(context, config, tgt->caddr, from_addr)) { ret = KRB5KRB_AP_ERR_BADADDR; kdc_log(context, config, 0, "Request from wrong address"); goto out; } /* * Select enctype, return key and kvno. */ { krb5_enctype etype; if(b->kdc_options.enc_tkt_in_skey) { int i; ekey = &adtkt.key; for(i = 0; i < b->etype.len; i++) if (b->etype.val[i] == adtkt.key.keytype) break; if(i == b->etype.len) { krb5_clear_error_string(context); return KRB5KDC_ERR_ETYPE_NOSUPP; } etype = b->etype.val[i]; kvno = 0; } else { Key *skey; ret = _kdc_find_etype(context, server, b->etype.val, b->etype.len, &skey, &etype); if(ret) { kdc_log(context, config, 0, "Server (%s) has no support for etypes", spp); return ret; } ekey = &skey->key; kvno = server->entry.kvno; } ret = krb5_generate_random_keyblock(context, etype, &sessionkey); if (ret) goto out; } /* check PAC if not cross realm and if there is one */ if (!cross_realm) { Key *tkey; ret = hdb_enctype2key(context, &krbtgt->entry, krbtgt_etype, &tkey); if(ret) { kdc_log(context, config, 0, "Failed to find key for krbtgt PAC check"); goto out; } ret = check_PAC(context, config, client_principal, client, server, ekey, &tkey->key, tgt, &rspac, &require_signedpath); if (ret) { kdc_log(context, config, 0, "Verify PAC failed for %s (%s) from %s with %s", spn, cpn, from, krb5_get_err_text(context, ret)); goto out; } } /* also check the krbtgt for signature */ ret = check_KRB5SignedPath(context, config, krbtgt, tgt, &spp, require_signedpath); if (ret) { kdc_log(context, config, 0, "KRB5SignedPath check failed for %s (%s) from %s with %s", spn, cpn, from, krb5_get_err_text(context, ret)); goto out; } /* * */ ret = tgs_make_reply(context, config, b, client_principal, tgt, ekey, &sessionkey, kvno, auth_data, server, spn, client, cp, krbtgt, krbtgt_etype, spp, &rspac, e_text, reply); out: free(spn); free(cpn); krb5_data_free(&rspac); krb5_free_keyblock_contents(context, &sessionkey); if(server) _kdc_free_ent(context, server); if(client) _kdc_free_ent(context, client); if (client_principal && client_principal != cp) krb5_free_principal(context, client_principal); if (cp) krb5_free_principal(context, cp); if (sp) krb5_free_principal(context, sp); free_EncTicketPart(&adtkt); return ret; }
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 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; }
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 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; }
void kerberos5_is(Authenticator *ap, unsigned char *data, int cnt) { krb5_error_code ret; krb5_data outbuf; krb5_keyblock *key_block; char *name; krb5_principal server; krb5_authenticator authenticator; int zero = 0; if (cnt-- < 1) return; switch (*data++) { case KRB_AUTH: auth.data = (char *)data; auth.length = cnt; auth_context = NULL; ret = krb5_auth_con_init (context, &auth_context); if (ret) { Data(ap, KRB_REJECT, "krb5_auth_con_init failed", -1); auth_finished(ap, AUTH_REJECT); if (auth_debug_mode) printf("Kerberos V5: krb5_auth_con_init failed (%s)\r\n", krb5_get_err_text(context, ret)); return; } ret = krb5_auth_con_setaddrs_from_fd (context, auth_context, &zero); if (ret) { Data(ap, KRB_REJECT, "krb5_auth_con_setaddrs_from_fd failed", -1); auth_finished(ap, AUTH_REJECT); if (auth_debug_mode) printf("Kerberos V5: " "krb5_auth_con_setaddrs_from_fd failed (%s)\r\n", krb5_get_err_text(context, ret)); return; } ret = krb5_sock_to_principal (context, 0, "host", KRB5_NT_SRV_HST, &server); if (ret) { Data(ap, KRB_REJECT, "krb5_sock_to_principal failed", -1); auth_finished(ap, AUTH_REJECT); if (auth_debug_mode) printf("Kerberos V5: " "krb5_sock_to_principal failed (%s)\r\n", krb5_get_err_text(context, ret)); return; } ret = krb5_rd_req(context, &auth_context, &auth, server, NULL, NULL, &ticket); krb5_free_principal (context, server); if (ret) { char *errbuf; asprintf(&errbuf, "Read req failed: %s", krb5_get_err_text(context, ret)); Data(ap, KRB_REJECT, errbuf, -1); if (auth_debug_mode) printf("%s\r\n", errbuf); free (errbuf); return; } ret = krb5_auth_con_getkey(context, auth_context, &key_block); if (ret) { Data(ap, KRB_REJECT, "krb5_auth_con_getkey failed", -1); auth_finished(ap, AUTH_REJECT); if (auth_debug_mode) printf("Kerberos V5: " "krb5_auth_con_getkey failed (%s)\r\n", krb5_get_err_text(context, ret)); return; } ret = krb5_auth_getauthenticator (context, auth_context, &authenticator); if (ret) { Data(ap, KRB_REJECT, "krb5_auth_getauthenticator failed", -1); auth_finished(ap, AUTH_REJECT); if (auth_debug_mode) printf("Kerberos V5: " "krb5_auth_getauthenticator failed (%s)\r\n", krb5_get_err_text(context, ret)); return; } if (authenticator->cksum) { char foo[2]; foo[0] = ap->type; foo[1] = ap->way; ret = krb5_verify_checksum (context, foo, sizeof(foo), key_block, authenticator->cksum); if (ret) { Data(ap, KRB_REJECT, "No checksum", -1); if (auth_debug_mode) printf ("No checksum\r\n"); krb5_free_authenticator (context, &authenticator); return; } } krb5_free_authenticator (context, &authenticator); ret = krb5_auth_con_getremotesubkey (context, auth_context, &key_block); if (ret) { Data(ap, KRB_REJECT, "krb5_auth_con_getremotesubkey failed", -1); auth_finished(ap, AUTH_REJECT); if (auth_debug_mode) printf("Kerberos V5: " "krb5_auth_con_getremotesubkey failed (%s)\r\n", krb5_get_err_text(context, ret)); return; } if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) { ret = krb5_mk_rep(context, &auth_context, &outbuf); if (ret) { Data(ap, KRB_REJECT, "krb5_mk_rep failed", -1); auth_finished(ap, AUTH_REJECT); if (auth_debug_mode) printf("Kerberos V5: " "krb5_mk_rep failed (%s)\r\n", krb5_get_err_text(context, ret)); return; } Data(ap, KRB_RESPONSE, outbuf.data, outbuf.length); } if (krb5_unparse_name(context, ticket->client, &name)) name = 0; if(UserNameRequested && krb5_kuserok(context, ticket->client, UserNameRequested)) { Data(ap, KRB_ACCEPT, name, name ? -1 : 0); if (auth_debug_mode) { printf("Kerberos5 identifies him as ``%s''\r\n", name ? name : ""); } if(key_block->keytype == KEYTYPE_DES) { Session_Key skey; skey.type = SK_DES; skey.length = 8; skey.data = key_block->keyvalue.data; encrypt_session_key(&skey, 0); } } else { char *msg; asprintf (&msg, "user `%s' is not authorized to " "login as `%s'", name ? name : "<unknown>", UserNameRequested ? UserNameRequested : "<nobody>"); if (msg == NULL) Data(ap, KRB_REJECT, NULL, 0); else { Data(ap, KRB_REJECT, (void *)msg, -1); free(msg); } } auth_finished(ap, AUTH_USER); krb5_free_keyblock_contents(context, key_block); break; #ifdef FORWARD case KRB_FORWARD: { struct passwd *pwd; char ccname[1024]; /* XXX */ krb5_data inbuf; krb5_ccache ccache; inbuf.data = (char *)data; inbuf.length = cnt; pwd = getpwnam (UserNameRequested); if (pwd == NULL) break; snprintf (ccname, sizeof(ccname), "FILE:/tmp/krb5cc_%u", pwd->pw_uid); ret = krb5_cc_resolve (context, ccname, &ccache); if (ret) { if (auth_debug_mode) printf ("Kerberos V5: could not get ccache: %s\r\n", krb5_get_err_text(context, ret)); break; } ret = krb5_cc_initialize (context, ccache, ticket->client); if (ret) { if (auth_debug_mode) printf ("Kerberos V5: could not init ccache: %s\r\n", krb5_get_err_text(context, ret)); break; } ret = krb5_rd_cred (context, auth_context, ccache, &inbuf); if(ret) { char *errbuf; asprintf (&errbuf, "Read forwarded creds failed: %s", krb5_get_err_text (context, ret)); if(errbuf == NULL) Data(ap, KRB_FORWARD_REJECT, NULL, 0); else Data(ap, KRB_FORWARD_REJECT, errbuf, -1); if (auth_debug_mode) printf("Could not read forwarded credentials: %s\r\n", errbuf); free (errbuf); } else Data(ap, KRB_FORWARD_ACCEPT, 0, 0); chown (ccname + 5, pwd->pw_uid, -1); if (auth_debug_mode) printf("Forwarded credentials obtained\r\n"); break; } #endif /* FORWARD */ default: if (auth_debug_mode) printf("Unknown Kerberos option %d\r\n", data[-1]); Data(ap, KRB_REJECT, 0, 0); break; } }
krb5_error_code _kdc_fast_unwrap_request(kdc_request_t r) { krb5_principal armor_server = NULL; hdb_entry_ex *armor_user = NULL; PA_FX_FAST_REQUEST fxreq; krb5_auth_context ac = NULL; krb5_ticket *ticket = NULL; krb5_flags ap_req_options; Key *armor_key = NULL; krb5_keyblock armorkey; krb5_error_code ret; krb5_ap_req ap_req; unsigned char *buf = NULL; KrbFastReq fastreq; size_t len, size; krb5_data data; const PA_DATA *pa; int i = 0; /* * First look for FX_COOKIE and and process it */ pa = _kdc_find_padata(&r->req, &i, KRB5_PADATA_FX_COOKIE); if (pa) { ret = fast_parse_cookie(r, pa); if (ret) goto out; } i = 0; pa = _kdc_find_padata(&r->req, &i, KRB5_PADATA_FX_FAST); if (pa == NULL) return 0; ret = decode_PA_FX_FAST_REQUEST(pa->padata_value.data, pa->padata_value.length, &fxreq, &len); if (ret) goto out; if (len != pa->padata_value.length) { ret = KRB5KDC_ERR_PREAUTH_FAILED; goto out; } if (fxreq.element != choice_PA_FX_FAST_REQUEST_armored_data) { kdc_log(r->context, r->config, 0, "AS-REQ FAST contain unknown type: %d", (int)fxreq.element); ret = KRB5KDC_ERR_PREAUTH_FAILED; goto out; } /* pull out armor key */ if (fxreq.u.armored_data.armor == NULL) { kdc_log(r->context, r->config, 0, "AS-REQ armor missing"); ret = KRB5KDC_ERR_PREAUTH_FAILED; goto out; } if (fxreq.u.armored_data.armor->armor_type != 1) { kdc_log(r->context, r->config, 0, "AS-REQ armor type not ap-req"); ret = KRB5KDC_ERR_PREAUTH_FAILED; goto out; } ret = krb5_decode_ap_req(r->context, &fxreq.u.armored_data.armor->armor_value, &ap_req); if(ret) { kdc_log(r->context, r->config, 0, "AP-REQ decode failed"); goto out; } /* Save that principal that was in the request */ ret = _krb5_principalname2krb5_principal(r->context, &armor_server, ap_req.ticket.sname, ap_req.ticket.realm); if (ret) { free_AP_REQ(&ap_req); goto out; } ret = _kdc_db_fetch(r->context, r->config, armor_server, HDB_F_GET_SERVER, NULL, NULL, &armor_user); if(ret == HDB_ERR_NOT_FOUND_HERE) { kdc_log(r->context, r->config, 5, "armor key does not have secrets at this KDC, " "need to proxy"); goto out; } else if (ret) { free_AP_REQ(&ap_req); ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; goto out; } ret = hdb_enctype2key(r->context, &armor_user->entry, NULL, ap_req.ticket.enc_part.etype, &armor_key); if (ret) { free_AP_REQ(&ap_req); goto out; } ret = krb5_verify_ap_req2(r->context, &ac, &ap_req, armor_server, &armor_key->key, 0, &ap_req_options, &ticket, KRB5_KU_AP_REQ_AUTH); free_AP_REQ(&ap_req); if (ret) goto out; if (ac->remote_subkey == NULL) { krb5_auth_con_free(r->context, ac); kdc_log(r->context, r->config, 0, "FAST AP-REQ remote subkey missing"); ret = KRB5KDC_ERR_PREAUTH_FAILED; goto out; } ret = _krb5_fast_armor_key(r->context, ac->remote_subkey, &ticket->ticket.key, &armorkey, &r->armor_crypto); krb5_auth_con_free(r->context, ac); krb5_free_ticket(r->context, ticket); if (ret) goto out; krb5_free_keyblock_contents(r->context, &armorkey); /* verify req-checksum of the outer body */ ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, len, &r->req.req_body, &size, ret); if (ret) goto out; if (size != len) { ret = KRB5KDC_ERR_PREAUTH_FAILED; goto out; } ret = krb5_verify_checksum(r->context, r->armor_crypto, KRB5_KU_FAST_REQ_CHKSUM, buf, len, &fxreq.u.armored_data.req_checksum); if (ret) { kdc_log(r->context, r->config, 0, "FAST request have a bad checksum"); goto out; } ret = krb5_decrypt_EncryptedData(r->context, r->armor_crypto, KRB5_KU_FAST_ENC, &fxreq.u.armored_data.enc_fast_req, &data); if (ret) { kdc_log(r->context, r->config, 0, "Failed to decrypt FAST request"); goto out; } ret = decode_KrbFastReq(data.data, data.length, &fastreq, &size); if (ret) { krb5_data_free(&data); goto out; } if (data.length != size) { krb5_data_free(&data); ret = KRB5KDC_ERR_PREAUTH_FAILED; goto out; } krb5_data_free(&data); free_KDC_REQ_BODY(&r->req.req_body); ret = copy_KDC_REQ_BODY(&fastreq.req_body, &r->req.req_body); if (ret) goto out; /* check for unsupported mandatory options */ if (FastOptions2int(fastreq.fast_options) & 0xfffc) { kdc_log(r->context, r->config, 0, "FAST unsupported mandatory option set"); ret = KRB5KDC_ERR_PREAUTH_FAILED; goto out; } /* KDC MUST ignore outer pa data preauth-14 - 6.5.5 */ if (r->req.padata) free_METHOD_DATA(r->req.padata); else ALLOC(r->req.padata); ret = copy_METHOD_DATA(&fastreq.padata, r->req.padata); if (ret) goto out; free_KrbFastReq(&fastreq); free_PA_FX_FAST_REQUEST(&fxreq); out: if (armor_server) krb5_free_principal(r->context, armor_server); if(armor_user) _kdc_free_ent(r->context, armor_user); 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; }
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 smb_krb5_verify_checksum(krb5_context context, krb5_keyblock *keyblock, krb5_keyusage usage, krb5_checksum *cksum, uint8 *data, size_t length) { krb5_error_code ret; /* verify the checksum */ /* welcome to the wonderful world of samba's kerberos abstraction layer: * * function heimdal 0.6.1rc3 heimdal 0.7 MIT krb 1.4.2 * ----------------------------------------------------------------------------- * krb5_c_verify_checksum - works works * krb5_verify_checksum works (6 args) works (6 args) broken (7 args) */ #if defined(HAVE_KRB5_C_VERIFY_CHECKSUM) { krb5_boolean checksum_valid = False; krb5_data input; input.data = (char *)data; input.length = length; ret = krb5_c_verify_checksum(context, keyblock, usage, &input, cksum, &checksum_valid); if (ret) { DEBUG(3,("smb_krb5_verify_checksum: krb5_c_verify_checksum() failed: %s\n", error_message(ret))); return ret; } if (!checksum_valid) ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; } #elif KRB5_VERIFY_CHECKSUM_ARGS == 6 && defined(HAVE_KRB5_CRYPTO_INIT) && defined(HAVE_KRB5_CRYPTO) && defined(HAVE_KRB5_CRYPTO_DESTROY) /* Warning: MIT's krb5_verify_checksum cannot be used as it will use a key * without enctype and it ignores any key_usage types - Guenther */ { krb5_crypto crypto; ret = krb5_crypto_init(context, keyblock, 0, &crypto); if (ret) { DEBUG(0,("smb_krb5_verify_checksum: krb5_crypto_init() failed: %s\n", error_message(ret))); return ret; } ret = krb5_verify_checksum(context, crypto, usage, data, length, cksum); krb5_crypto_destroy(context, crypto); } #else #error UNKNOWN_KRB5_VERIFY_CHECKSUM_FUNCTION #endif return ret; }
int kerberos5_is_auth (TN_Authenticator * ap, unsigned char *data, int cnt, char *errbuf, int errbuflen) { int r = 0; krb5_keytab keytabid = 0; krb5_authenticator *authenticator; char *name; krb5_data outbuf; krb5_keyblock *newkey = NULL; krb5_principal server; # ifdef ENCRYPTION Session_Key skey; # endif auth.data = (char *) data; auth.length = cnt; if (!r && !auth_context) r = krb5_auth_con_init (telnet_context, &auth_context); if (!r) { krb5_rcache rcache; r = krb5_auth_con_getrcache (telnet_context, auth_context, &rcache); if (!r && !rcache) { r = krb5_sname_to_principal (telnet_context, 0, 0, KRB5_NT_SRV_HST, &server); if (!r) { r = krb5_get_server_rcache (telnet_context, krb5_princ_component (telnet_context, server, 0), &rcache); krb5_free_principal (telnet_context, server); } } if (!r) r = krb5_auth_con_setrcache (telnet_context, auth_context, rcache); } if (!r && telnet_srvtab) r = krb5_kt_resolve (telnet_context, telnet_srvtab, &keytabid); if (!r) r = krb5_rd_req (telnet_context, &auth_context, &auth, NULL, keytabid, NULL, &ticket); if (r) { snprintf (errbuf, errbuflen, "krb5_rd_req failed: %s", error_message (r)); return r; } /* 256 bytes should be much larger than any reasonable first component of a service name especially since the default is of length 4. */ if (krb5_princ_component (telnet_context, ticket->server, 0)->length < 256) { char princ[256]; strncpy (princ, krb5_princ_component (telnet_context, ticket->server, 0)->data, krb5_princ_component (telnet_context, ticket->server, 0)->length); princ[krb5_princ_component (telnet_context, ticket->server, 0)-> length] = '\0'; if (strcmp ("host", princ)) { snprintf (errbuf, errbuflen, "incorrect service name: \"%s\" != \"host\"", princ); return 1; } } else { strncpy (errbuf, "service name too long", errbuflen); return 1; } r = krb5_auth_con_getauthenticator (telnet_context, auth_context, &authenticator); if (r) { snprintf (errbuf, errbuflen, "krb5_auth_con_getauthenticator failed: %s", error_message (r)); return 1; } # ifdef AUTH_ENCRYPT_MASK if ((ap->way & AUTH_ENCRYPT_MASK) == AUTH_ENCRYPT_ON && !authenticator->checksum) { snprintf (errbuf, errbuflen, "authenticator is missing required checksum"); return 1; } # endif if (authenticator->checksum) { char type_check[2]; krb5_checksum *cksum = authenticator->checksum; krb5_keyblock *key; type_check[0] = ap->type; type_check[1] = ap->way; r = krb5_auth_con_getkey (telnet_context, auth_context, &key); if (r) { snprintf (errbuf, errbuflen, "krb5_auth_con_getkey failed: %s", error_message (r)); return 1; } r = krb5_verify_checksum (telnet_context, cksum->checksum_type, cksum, &type_check, 2, key->contents, key->length); if (r) { snprintf (errbuf, errbuflen, "checksum verification failed: %s", error_message (r)); return 1; } krb5_free_keyblock (telnet_context, key); } krb5_free_authenticator (telnet_context, authenticator); if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) { if ((r = krb5_mk_rep (telnet_context, auth_context, &outbuf))) { snprintf (errbuf, errbuflen, "Make reply failed: %s", error_message (r)); return 1; } Data (ap, KRB_RESPONSE, outbuf.data, outbuf.length); } if (krb5_unparse_name (telnet_context, ticket->enc_part2->client, &name)) name = 0; Data (ap, KRB_ACCEPT, name, name ? -1 : 0); DEBUG (("telnetd: Kerberos5 identifies him as ``%s''\r\n", name ? name : "")); auth_finished (ap, AUTH_USER); free (name); krb5_auth_con_getremotesubkey (telnet_context, auth_context, &newkey); if (session_key) { krb5_free_keyblock (telnet_context, session_key); session_key = 0; } if (newkey) { krb5_copy_keyblock (telnet_context, newkey, &session_key); krb5_free_keyblock (telnet_context, newkey); } else { krb5_copy_keyblock (telnet_context, ticket->enc_part2->session, &session_key); } telnet_encrypt_key (&skey); return 0; }
static krb5_error_code check_KRB5SignedPath(krb5_context context, krb5_kdc_configuration *config, hdb_entry_ex *krbtgt, EncTicketPart *tkt, KRB5SignedPathPrincipals **delegated, int require_signedpath) { krb5_error_code ret; krb5_data data; krb5_crypto crypto = NULL; *delegated = NULL; ret = find_KRB5SignedPath(context, tkt->authorization_data, &data); if (ret == 0) { KRB5SignedPathData spd; KRB5SignedPath sp; AuthorizationData *ad; size_t size; ret = decode_KRB5SignedPath(data.data, data.length, &sp, NULL); krb5_data_free(&data); if (ret) return ret; spd.encticket = *tkt; /* the KRB5SignedPath is the last entry */ ad = spd.encticket.authorization_data; if (--ad->len == 0) spd.encticket.authorization_data = NULL; spd.delegated = sp.delegated; ASN1_MALLOC_ENCODE(KRB5SignedPathData, data.data, data.length, &spd, &size, ret); ad->len++; spd.encticket.authorization_data = ad; if (ret) { free_KRB5SignedPath(&sp); return ret; } if (data.length != size) krb5_abortx(context, "internal asn.1 encoder error"); { Key *key; ret = hdb_enctype2key(context, &krbtgt->entry, sp.etype, &key); if (ret == 0) ret = krb5_crypto_init(context, &key->key, 0, &crypto); if (ret) { free(data.data); free_KRB5SignedPath(&sp); return ret; } } ret = krb5_verify_checksum(context, crypto, KRB5_KU_KRB5SIGNEDPATH, data.data, data.length, &sp.cksum); krb5_crypto_destroy(context, crypto); free(data.data); if (ret) { free_KRB5SignedPath(&sp); return ret; } if (sp.delegated) { *delegated = malloc(sizeof(*sp.delegated)); if (*delegated == NULL) { free_KRB5SignedPath(&sp); return ENOMEM; } ret = copy_KRB5SignedPathPrincipals(*delegated, sp.delegated); if (ret) { free_KRB5SignedPath(&sp); free(*delegated); *delegated = NULL; return ret; } } free_KRB5SignedPath(&sp); } else { if (require_signedpath) return KRB5KDC_ERR_BADOPTION; } return 0; }
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; }
static krb5_error_code tgs_check_authenticator(krb5_context context, krb5_kdc_configuration *config, krb5_auth_context ac, KDC_REQ_BODY *b, const char **e_text, krb5_keyblock *key) { krb5_authenticator auth; size_t len; unsigned char *buf; size_t buf_size; krb5_error_code ret; krb5_crypto crypto; krb5_auth_con_getauthenticator(context, ac, &auth); if(auth->cksum == NULL){ kdc_log(context, config, 0, "No authenticator in request"); ret = KRB5KRB_AP_ERR_INAPP_CKSUM; goto out; } /* * according to RFC1510 it doesn't need to be keyed, * but according to the latest draft it needs to. */ if ( #if 0 !krb5_checksum_is_keyed(context, auth->cksum->cksumtype) || #endif !krb5_checksum_is_collision_proof(context, auth->cksum->cksumtype)) { kdc_log(context, config, 0, "Bad checksum type in authenticator: %d", auth->cksum->cksumtype); ret = KRB5KRB_AP_ERR_INAPP_CKSUM; goto out; } /* XXX should not re-encode this */ ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, b, &len, ret); if(ret){ kdc_log(context, config, 0, "Failed to encode KDC-REQ-BODY: %s", krb5_get_err_text(context, ret)); goto out; } if(buf_size != len) { free(buf); kdc_log(context, config, 0, "Internal error in ASN.1 encoder"); *e_text = "KDC internal error"; ret = KRB5KRB_ERR_GENERIC; goto out; } ret = krb5_crypto_init(context, key, 0, &crypto); if (ret) { free(buf); kdc_log(context, config, 0, "krb5_crypto_init failed: %s", krb5_get_err_text(context, ret)); goto out; } ret = krb5_verify_checksum(context, crypto, KRB5_KU_TGS_REQ_AUTH_CKSUM, buf, len, auth->cksum); free(buf); krb5_crypto_destroy(context, crypto); if(ret){ kdc_log(context, config, 0, "Failed to verify authenticator checksum: %s", krb5_get_err_text(context, ret)); } out: free_Authenticator(auth); free(auth); return ret; }
int ksm_process_in_msg(struct snmp_secmod_incoming_params *parms) { long temp; krb5_cksumtype cksumtype; krb5_auth_context auth_context = NULL; krb5_error_code retcode; krb5_checksum checksum; krb5_data ap_req, ivector; krb5_flags flags; krb5_keyblock *subkey = NULL; #ifdef MIT_NEW_CRYPTO krb5_data input, output; krb5_boolean valid; krb5_enc_data in_crypt; #else /* MIT_NEW_CRYPTO */ krb5_encrypt_block eblock; #endif /* MIT_NEW_CRYPTO */ krb5_ticket *ticket = NULL; int retval = SNMPERR_SUCCESS, response = 0; size_t length = parms->wholeMsgLen - (u_int) (parms->secParams - parms->wholeMsg); u_char *current = parms->secParams, type; size_t cksumlength, blocksize; long hint; char *cname; struct ksm_secStateRef *ksm_state; struct ksm_cache_entry *entry; DEBUGMSGTL(("ksm", "Processing has begun\n")); checksum.contents = NULL; ap_req.data = NULL; ivector.length = 0; ivector.data = NULL; /* * First, parse the security parameters (because we need the subkey inside * of the ticket to do anything */ if ((current = asn_parse_sequence(current, &length, &type, (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR), "ksm first octet")) == NULL) { DEBUGMSGTL(("ksm", "Initial security paramter parsing failed\n")); retval = SNMPERR_ASN_PARSE_ERR; goto error; } if ((current = asn_parse_sequence(current, &length, &type, (ASN_SEQUENCE | ASN_CONSTRUCTOR), "ksm sequence")) == NULL) { DEBUGMSGTL(("ksm", "Security parameter sequence parsing failed\n")); retval = SNMPERR_ASN_PARSE_ERR; goto error; } if ((current = asn_parse_int(current, &length, &type, &temp, sizeof(temp))) == NULL) { DEBUGMSGTL(("ksm", "Security parameter checksum type parsing" "failed\n")); retval = SNMPERR_ASN_PARSE_ERR; goto error; } cksumtype = temp; #ifdef MIT_NEW_CRYPTO if (!krb5_c_valid_cksumtype(cksumtype)) { DEBUGMSGTL(("ksm", "Invalid checksum type (%d)\n", cksumtype)); retval = SNMPERR_KRB5; snmp_set_detail("Invalid checksum type"); goto error; } if (!krb5_c_is_keyed_cksum(cksumtype)) { DEBUGMSGTL(("ksm", "Checksum type %d is not a keyed checksum\n", cksumtype)); snmp_set_detail("Checksum is not a keyed checksum"); retval = SNMPERR_KRB5; goto error; } if (!krb5_c_is_coll_proof_cksum(cksumtype)) { DEBUGMSGTL(("ksm", "Checksum type %d is not a collision-proof " "checksum\n", cksumtype)); snmp_set_detail("Checksum is not a collision-proof checksum"); retval = SNMPERR_KRB5; goto error; } #else /* ! MIT_NEW_CRYPTO */ if (!valid_cksumtype(cksumtype)) { DEBUGMSGTL(("ksm", "Invalid checksum type (%d)\n", cksumtype)); retval = SNMPERR_KRB5; snmp_set_detail("Invalid checksum type"); goto error; } if (!is_keyed_cksum(cksumtype)) { DEBUGMSGTL(("ksm", "Checksum type %d is not a keyed checksum\n", cksumtype)); snmp_set_detail("Checksum is not a keyed checksum"); retval = SNMPERR_KRB5; goto error; } if (!is_coll_proof_cksum(cksumtype)) { DEBUGMSGTL(("ksm", "Checksum type %d is not a collision-proof " "checksum\n", cksumtype)); snmp_set_detail("Checksum is not a collision-proof checksum"); retval = SNMPERR_KRB5; goto error; } #endif /* MIT_NEW_CRYPTO */ checksum.checksum_type = cksumtype; cksumlength = length; if ((current = asn_parse_sequence(current, &cksumlength, &type, (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR), "ksm checksum")) == NULL) { DEBUGMSGTL(("ksm", "Security parameter checksum parsing failed\n")); retval = SNMPERR_ASN_PARSE_ERR; goto error; } checksum.contents = malloc(cksumlength); if (!checksum.contents) { DEBUGMSGTL(("ksm", "Unable to malloc %d bytes for checksum.\n", cksumlength)); retval = SNMPERR_MALLOC; goto error; } memcpy(checksum.contents, current, cksumlength); checksum.length = cksumlength; checksum.checksum_type = cksumtype; /* * Zero out the checksum so the validation works correctly */ memset(current, 0, cksumlength); current += cksumlength; length = parms->wholeMsgLen - (u_int) (current - parms->wholeMsg); if ((current = asn_parse_sequence(current, &length, &type, (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR), "ksm ap_req")) == NULL) { DEBUGMSGTL(("ksm", "KSM security parameter AP_REQ/REP parsing " "failed\n")); retval = SNMPERR_ASN_PARSE_ERR; goto error; } ap_req.length = length; ap_req.data = malloc(length); if (!ap_req.data) { DEBUGMSGTL(("ksm", "KSM unable to malloc %d bytes for AP_REQ/REP.\n", length)); retval = SNMPERR_MALLOC; goto error; } memcpy(ap_req.data, current, length); current += length; length = parms->wholeMsgLen - (u_int) (current - parms->wholeMsg); if ((current = asn_parse_int(current, &length, &type, &hint, sizeof(hint))) == NULL) { DEBUGMSGTL(("ksm", "KSM security parameter hint parsing failed\n")); retval = SNMPERR_ASN_PARSE_ERR; goto error; } /* * Okay! We've got it all! Now try decoding the damn ticket. * * But of course there's a WRINKLE! We need to figure out if we're * processing a AP_REQ or an AP_REP. How do we do that? We're going * to cheat, and look at the first couple of bytes (which is what * the Kerberos library routines do anyway). * * If there are ever new Kerberos message formats, we'll need to fix * this here. * * If it's a _response_, then we need to get the auth_context * from our cache. */ if (ap_req.length && (ap_req.data[0] == 0x6e || ap_req.data[0] == 0x4e)) { /* * We need to initalize the authorization context, and set the * replay cache in it (and initialize the replay cache if we * haven't already */ retcode = krb5_auth_con_init(kcontext, &auth_context); if (retcode) { DEBUGMSGTL(("ksm", "krb5_auth_con_init failed: %s\n", error_message(retcode))); retval = SNMPERR_KRB5; snmp_set_detail(error_message(retcode)); goto error; } if (!rcache) { krb5_data server; server.data = "host"; server.length = strlen(server.data); retcode = krb5_get_server_rcache(kcontext, &server, &rcache); if (retcode) { DEBUGMSGTL(("ksm", "krb5_get_server_rcache failed: %s\n", error_message(retcode))); retval = SNMPERR_KRB5; snmp_set_detail(error_message(retcode)); goto error; } } retcode = krb5_auth_con_setrcache(kcontext, auth_context, rcache); if (retcode) { DEBUGMSGTL(("ksm", "krb5_auth_con_setrcache failed: %s\n", error_message(retcode))); retval = SNMPERR_KRB5; snmp_set_detail(error_message(retcode)); goto error; } retcode = krb5_rd_req(kcontext, &auth_context, &ap_req, NULL, keytab, &flags, &ticket); krb5_auth_con_setrcache(kcontext, auth_context, NULL); if (retcode) { DEBUGMSGTL(("ksm", "krb5_rd_req() failed: %s\n", error_message(retcode))); retval = SNMPERR_KRB5; snmp_set_detail(error_message(retcode)); goto error; } retcode = krb5_unparse_name(kcontext, ticket->enc_part2->client, &cname); if (retcode == 0) { DEBUGMSGTL(("ksm", "KSM authenticated principal name: %s\n", cname)); free(cname); } /* * Check to make sure AP_OPTS_MUTUAL_REQUIRED was set */ if (!(flags & AP_OPTS_MUTUAL_REQUIRED)) { DEBUGMSGTL(("ksm", "KSM MUTUAL_REQUIRED not set in request!\n")); retval = SNMPERR_KRB5; snmp_set_detail("MUTUAL_REQUIRED not set in message"); goto error; } retcode = krb5_auth_con_getremotesubkey(kcontext, auth_context, &subkey); if (retcode) { DEBUGMSGTL(("ksm", "KSM remote subkey retrieval failed: %s\n", error_message(retcode))); retval = SNMPERR_KRB5; snmp_set_detail(error_message(retcode)); goto error; } } else if (ap_req.length && (ap_req.data[0] == 0x6f || ap_req.data[0] == 0x4f)) { /* * Looks like a response; let's see if we've got that auth_context * in our cache. */ krb5_ap_rep_enc_part *repl = NULL; response = 1; entry = ksm_get_cache(parms->pdu->msgid); if (!entry) { DEBUGMSGTL(("ksm", "KSM: Unable to find auth_context for PDU with " "message ID of %ld\n", parms->pdu->msgid)); retval = SNMPERR_KRB5; goto error; } auth_context = entry->auth_context; /* * In that case, let's call the rd_rep function */ retcode = krb5_rd_rep(kcontext, auth_context, &ap_req, &repl); if (repl) krb5_free_ap_rep_enc_part(kcontext, repl); if (retcode) { DEBUGMSGTL(("ksm", "KSM: krb5_rd_rep() failed: %s\n", error_message(retcode))); retval = SNMPERR_KRB5; goto error; } DEBUGMSGTL(("ksm", "KSM: krb5_rd_rep() decoded successfully.\n")); retcode = krb5_auth_con_getlocalsubkey(kcontext, auth_context, &subkey); if (retcode) { DEBUGMSGTL(("ksm", "Unable to retrieve local subkey: %s\n", error_message(retcode))); retval = SNMPERR_KRB5; snmp_set_detail("Unable to retrieve local subkey"); goto error; } } else { DEBUGMSGTL(("ksm", "Unknown Kerberos message type (%02x)\n", ap_req.data[0])); retval = SNMPERR_KRB5; snmp_set_detail("Unknown Kerberos message type"); goto error; } #ifdef MIT_NEW_CRYPTO input.data = (char *) parms->wholeMsg; input.length = parms->wholeMsgLen; retcode = krb5_c_verify_checksum(kcontext, subkey, KSM_KEY_USAGE_CHECKSUM, &input, &checksum, &valid); #else /* MIT_NEW_CRYPTO */ retcode = krb5_verify_checksum(kcontext, cksumtype, &checksum, parms->wholeMsg, parms->wholeMsgLen, (krb5_pointer) subkey->contents, subkey->length); #endif /* MIT_NEW_CRYPTO */ if (retcode) { DEBUGMSGTL(("ksm", "KSM checksum verification failed: %s\n", error_message(retcode))); retval = SNMPERR_KRB5; snmp_set_detail(error_message(retcode)); goto error; } /* * Don't ask me why they didn't simply return an error, but we have * to check to see if "valid" is false. */ #ifdef MIT_NEW_CRYPTO if (!valid) { DEBUGMSGTL(("ksm", "Computed checksum did not match supplied " "checksum!\n")); retval = SNMPERR_KRB5; snmp_set_detail ("Computed checksum did not match supplied checksum"); goto error; } #endif /* MIT_NEW_CRYPTO */ /* * Handle an encrypted PDU. Note that it's an OCTET_STRING of the * output of whatever Kerberos cryptosystem you're using (defined by * the encryption type). Note that this is NOT the EncryptedData * sequence - it's what goes in the "cipher" field of EncryptedData. */ if (parms->secLevel == SNMP_SEC_LEVEL_AUTHPRIV) { if ((current = asn_parse_sequence(current, &length, &type, (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR), "ksm pdu")) == NULL) { DEBUGMSGTL(("ksm", "KSM sPDU octet decoding failed\n")); retval = SNMPERR_ASN_PARSE_ERR; goto error; } /* * The PDU is now pointed at by "current", and the length is in * "length". */ DEBUGMSGTL(("ksm", "KSM starting sPDU decode\n")); /* * We need to set up a blank initialization vector for the decryption. * Use a block of all zero's (which is dependent on the block size * of the encryption method). */ #ifdef MIT_NEW_CRYPTO retcode = krb5_c_block_size(kcontext, subkey->enctype, &blocksize); if (retcode) { DEBUGMSGTL(("ksm", "Unable to determine crypto block size: %s\n", error_message(retcode))); snmp_set_detail(error_message(retcode)); retval = SNMPERR_KRB5; goto error; } #else /* MIT_NEW_CRYPTO */ blocksize = krb5_enctype_array[subkey->enctype]->system->block_length; #endif /* MIT_NEW_CRYPTO */ ivector.data = malloc(blocksize); if (!ivector.data) { DEBUGMSGTL(("ksm", "Unable to allocate %d bytes for ivector\n", blocksize)); retval = SNMPERR_MALLOC; goto error; } ivector.length = blocksize; memset(ivector.data, 0, blocksize); #ifndef MIT_NEW_CRYPTO krb5_use_enctype(kcontext, &eblock, subkey->enctype); retcode = krb5_process_key(kcontext, &eblock, subkey); if (retcode) { DEBUGMSGTL(("ksm", "KSM key post-processing failed: %s\n", error_message(retcode))); snmp_set_detail(error_message(retcode)); retval = SNMPERR_KRB5; goto error; } #endif /* !MIT_NEW_CRYPTO */ if (length > *parms->scopedPduLen) { DEBUGMSGTL(("ksm", "KSM not enough room - have %d bytes to " "decrypt but only %d bytes available\n", length, *parms->scopedPduLen)); retval = SNMPERR_TOO_LONG; #ifndef MIT_NEW_CRYPTO krb5_finish_key(kcontext, &eblock); #endif /* ! MIT_NEW_CRYPTO */ goto error; } #ifdef MIT_NEW_CRYPTO in_crypt.ciphertext.data = (char *) current; in_crypt.ciphertext.length = length; in_crypt.enctype = subkey->enctype; output.data = (char *) *parms->scopedPdu; output.length = *parms->scopedPduLen; retcode = krb5_c_decrypt(kcontext, subkey, KSM_KEY_USAGE_ENCRYPTION, &ivector, &in_crypt, &output); #else /* MIT_NEW_CRYPTO */ retcode = krb5_decrypt(kcontext, (krb5_pointer) current, *parms->scopedPdu, length, &eblock, ivector.data); krb5_finish_key(kcontext, &eblock); #endif /* MIT_NEW_CRYPTO */ if (retcode) { DEBUGMSGTL(("ksm", "Decryption failed: %s\n", error_message(retcode))); snmp_set_detail(error_message(retcode)); retval = SNMPERR_KRB5; goto error; } *parms->scopedPduLen = length; } else { /* * Clear PDU */ *parms->scopedPdu = current; *parms->scopedPduLen = parms->wholeMsgLen - (current - parms->wholeMsg); } /* * A HUGE GROSS HACK */ *parms->maxSizeResponse = parms->maxMsgSize - 200; DEBUGMSGTL(("ksm", "KSM processing complete\n")); /* * Set the secName to the right value (a hack for now). But that's * only used for when we're processing a request, not a response. */ if (!response) { retcode = krb5_unparse_name(kcontext, ticket->enc_part2->client, &cname); if (retcode) { DEBUGMSGTL(("ksm", "KSM krb5_unparse_name failed: %s\n", error_message(retcode))); snmp_set_detail(error_message(retcode)); retval = SNMPERR_KRB5; goto error; } if (strlen(cname) > *parms->secNameLen + 1) { DEBUGMSGTL(("ksm", "KSM: Principal length (%d) is too long (%d)\n", strlen(cname), parms->secNameLen)); retval = SNMPERR_TOO_LONG; free(cname); goto error; } strcpy(parms->secName, cname); *parms->secNameLen = strlen(cname); free(cname); /* * Also, if we're not a response, keep around our auth_context so we * can encode the reply message correctly */ ksm_state = SNMP_MALLOC_STRUCT(ksm_secStateRef); if (!ksm_state) { DEBUGMSGTL(("ksm", "KSM unable to malloc memory for " "ksm_secStateRef\n")); retval = SNMPERR_MALLOC; goto error; } ksm_state->auth_context = auth_context; auth_context = NULL; ksm_state->cksumtype = cksumtype; *parms->secStateRef = ksm_state; } else { /* * We _still_ have to set the secName in process_in_msg(). Do * that now with what we were passed in before (we cached it, * remember?) */ memcpy(parms->secName, entry->secName, entry->secNameLen); *parms->secNameLen = entry->secNameLen; } /* * Just in case */ parms->secEngineID = (u_char *) ""; *parms->secEngineIDLen = 0; auth_context = NULL; /* So we don't try to free it on success */ error: if (retval == SNMPERR_ASN_PARSE_ERR && snmp_increment_statistic(STAT_SNMPINASNPARSEERRS) == 0) DEBUGMSGTL(("ksm", "Failed to increment statistics.\n")); if (subkey) krb5_free_keyblock(kcontext, subkey); if (checksum.contents) free(checksum.contents); if (ivector.data) free(ivector.data); if (ticket) krb5_free_ticket(kcontext, ticket); if (!response && auth_context) krb5_auth_con_free(kcontext, auth_context); if (ap_req.data) free(ap_req.data); return retval; }