static krb5_error_code pac_checksum(krb5_context context, const krb5_keyblock *key, uint32_t *cksumtype, size_t *cksumsize) { krb5_cksumtype cktype; krb5_error_code ret; krb5_crypto crypto = NULL; ret = krb5_crypto_init(context, key, 0, &crypto); if (ret) return ret; ret = krb5_crypto_get_checksum_type(context, crypto, &cktype); krb5_crypto_destroy(context, crypto); if (ret) return ret; if (krb5_checksum_is_keyed(context, cktype) == FALSE) { *cksumtype = CKSUMTYPE_HMAC_MD5; *cksumsize = 16; } ret = krb5_checksumsize(context, cktype, cksumsize); if (ret) return ret; *cksumtype = (uint32_t)cktype; return 0; }
KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL krb5_c_is_keyed_cksum(krb5_cksumtype ctype) { return krb5_checksum_is_keyed(NULL, ctype); }
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; }
krb5_error_code KRB5_LIB_FUNCTION krb5_rd_safe(krb5_context context, krb5_auth_context auth_context, const krb5_data *inbuf, krb5_data *outbuf, krb5_replay_data *outdata) { krb5_error_code ret; KRB_SAFE safe; size_t len; krb5_data_zero(outbuf); if ((auth_context->flags & (KRB5_AUTH_CONTEXT_RET_TIME | KRB5_AUTH_CONTEXT_RET_SEQUENCE))) { if (outdata == NULL) { krb5_set_error_message(context, KRB5_RC_REQUIRED, N_("rd_safe: need outdata " "to return data", "")); return KRB5_RC_REQUIRED; /* XXX better error, MIT returns this */ } /* if these fields are not present in the safe-part, silently return zero */ memset(outdata, 0, sizeof(*outdata)); } ret = decode_KRB_SAFE (inbuf->data, inbuf->length, &safe, &len); if (ret) return ret; if (safe.pvno != 5) { ret = KRB5KRB_AP_ERR_BADVERSION; krb5_clear_error_message (context); goto failure; } if (safe.msg_type != krb_safe) { ret = KRB5KRB_AP_ERR_MSG_TYPE; krb5_clear_error_message (context); goto failure; } if (!krb5_checksum_is_keyed(context, safe.cksum.cksumtype) || !krb5_checksum_is_collision_proof(context, safe.cksum.cksumtype)) { ret = KRB5KRB_AP_ERR_INAPP_CKSUM; krb5_clear_error_message (context); goto failure; } /* check sender address */ if (safe.safe_body.s_address && auth_context->remote_address && !krb5_address_compare (context, auth_context->remote_address, safe.safe_body.s_address)) { ret = KRB5KRB_AP_ERR_BADADDR; krb5_clear_error_message (context); goto failure; } /* check receiver address */ if (safe.safe_body.r_address && auth_context->local_address && !krb5_address_compare (context, auth_context->local_address, safe.safe_body.r_address)) { ret = KRB5KRB_AP_ERR_BADADDR; krb5_clear_error_message (context); goto failure; } /* check timestamp */ if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_TIME) { krb5_timestamp sec; krb5_timeofday (context, &sec); if (safe.safe_body.timestamp == NULL || safe.safe_body.usec == NULL || abs(*safe.safe_body.timestamp - sec) > context->max_skew) { ret = KRB5KRB_AP_ERR_SKEW; krb5_clear_error_message (context); goto failure; } } /* XXX - check replay cache */ /* check sequence number. since MIT krb5 cannot generate a sequence number of zero but instead generates no sequence number, we accept that */ if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) { if ((safe.safe_body.seq_number == NULL && auth_context->remote_seqnumber != 0) || (safe.safe_body.seq_number != NULL && *safe.safe_body.seq_number != auth_context->remote_seqnumber)) { ret = KRB5KRB_AP_ERR_BADORDER; krb5_clear_error_message (context); goto failure; } auth_context->remote_seqnumber++; } ret = verify_checksum (context, auth_context, &safe); if (ret) goto failure; outbuf->length = safe.safe_body.user_data.length; outbuf->data = malloc(outbuf->length); if (outbuf->data == NULL && outbuf->length != 0) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); krb5_data_zero(outbuf); goto failure; } memcpy (outbuf->data, safe.safe_body.user_data.data, outbuf->length); if ((auth_context->flags & (KRB5_AUTH_CONTEXT_RET_TIME | KRB5_AUTH_CONTEXT_RET_SEQUENCE))) { if(safe.safe_body.timestamp) outdata->timestamp = *safe.safe_body.timestamp; if(safe.safe_body.usec) outdata->usec = *safe.safe_body.usec; if(safe.safe_body.seq_number) outdata->seq = *safe.safe_body.seq_number; } failure: free_KRB_SAFE (&safe); 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; }