-----BEGIN PGP SIGNED MESSAGE----- MULTIPLE DENIAL OF SERVICE VULNERABILITIES IN KRB4 KDC 2000-06-09 SUMMARY: A buffer overrun capable of creating a denial of service exists in implementations of Kerberos 4 KDC programs. This is IN ADDITION to the krb_rd_req() vulnerability that was previously announced. Many Kerberos 4 KDC implementations derived from MIT sources are believed to be vulnerable. Another denial of service vulnerability exists in the krb5-1.1.x KDC implementations (and krb5-1.2-beta1, but not krb5-1.0.x) that can cause the Kerberos 4 compatibility code to perform a double-free, possibly resulting in a crash of the KDC process. IMPACT: A remote user may be able to cause the KDC to issue bogus tickets, or to return an error of the form "principal unknown" for all principals, necessitating a restart of the KDC to resume proper operation. A remote user may also be able to cause a krb5-1.1.x KDC to experience a segmentation violation or malloc pool corruption, causing the KDC process to crash. DETAILS: A static buffer can be overrun by corrupt requests sent to a KDC process. It is believed that this overrun does not lead to a root compromise, but it can lead to a denial of service by corrupting long-term state in the KDC process. The krb5-1.1.x KDC contains in its Kerberos 4 compatibility mode some code which tickles a memory management bug in the library. This can result in a double-free of memory and corruption of the malloc pool, possibly leading to a crash of the KDC. Whether or not a crash occurs depends on the idiosyncrasies of the malloc implementation used. VULNERABLE DISTRIBUTIONS: Source distributions which may contain vulnerable code include: MIT Kerberos 5 releases krb5-1.0.x, krb5-1.1, krb5-1.1.1 MIT Kerberos 4 patch 10, and probably earlier releases as well KerbNet (Cygnus implementation of Kerberos 5) Cygnus Network Security (CNS -- Cygnus implementation of Kerberos 4) KTH-krb4 before version 0.10 NON-VULNERABLE DISTRIBUTIONS: Source distributions that are believed not to be vulnerable include: KTH-krb4 -- version 0.10 and above Heimdal (KTH implementation of Kerberos 5) -- any version FIXES: The best course of action is to patch your KDC. If you have not done so already, install the patches to deal with the krb_rd_req() vulnerability that was previously announced. Patches and the original announcement may be found at: http://web.mit.edu/kerberos/www/advisories/index.html MIT will soon release krb5-1.2, which will have these changes incorporated. The krb5-1.2-beta1 release does not have this fix, though the upcoming krb5-1.2-beta2 release, tentatively scheduled for the week of June 5, will. The two recent beta patch releases, krb5-1.0.7-beta2 and krb5-1.1.2-beta1, which were intended to fix the krb4 buffer overrun problems, have not been patched for this problem yet. The following patches for this bug will be posted to the aforementioned security advisories web page once this announcement is made public. PATCHES AGAINST krb5-1.0.x: These patches are against krb5-1.0.7-beta1, but will probably apply cleanly against krb5-1.0.6 and earlier releases as well. These patches also will be made available at: http://web.mit.edu/kerberos/www/advisories/krb4kdc_10x_patch.txt Index: kerberos_v4.c =================================================================== RCS file: /cvs/krbdev/krb5/src/kdc/kerberos_v4.c,v retrieving revision 5.56.4.3 diff -c -r5.56.4.3 kerberos_v4.c *** kerberos_v4.c 1998/05/10 03:02:55 5.56.4.3 - --- kerberos_v4.c 2000/05/19 19:38:17 *************** *** 173,183 **** return(retval); if (!*local_realm) { /* local-realm name already set up */ - - /* XXX assumes realm is null-terminated! */ lrealm = master_princ->realm.data; ! if (strlen(lrealm) < sizeof(local_realm)) ! strcpy(local_realm, lrealm); ! else retval = KRB5_CONFIG_NOTENUFSPACE; } /* convert client_fulladdr to client_sockaddr: - --- 173,183 ---- return(retval); if (!*local_realm) { /* local-realm name already set up */ lrealm = master_princ->realm.data; ! if (master_princ->realm.length < sizeof(local_realm)) { ! memcpy(local_realm, lrealm, master_princ->realm.length); ! local_realm[master_princ->realm.length] = '\0'; ! } else retval = KRB5_CONFIG_NOTENUFSPACE; } /* convert client_fulladdr to client_sockaddr: *************** *** 196,201 **** - --- 196,202 ---- return KRB5KRB_ERR_FIELD_TOOLONG; } v4_pkt.length = pkt->length; + v4_pkt.mbz = 0; memcpy( v4_pkt.dat, pkt->data, pkt->length); kerberos_v4( &client_sockaddr, &v4_pkt); *************** *** 507,512 **** - --- 508,516 ---- req_act_vno = req_version; + /* set these to point to something safe */ + req_name_ptr = req_inst_ptr = req_realm_ptr = ""; + /* check packet version */ if (req_version != KRB_PROT_VERSION) { lt = klog(L_KRB_PERR, *************** *** 574,580 **** if ((i = check_princ(req_name_ptr, req_inst_ptr, 0, &a_name_data))) { ! kerb_err_reply(client, pkt, i, lt); a_name_data.key_low = a_name_data.key_high = 0; return; } - --- 578,584 ---- if ((i = check_princ(req_name_ptr, req_inst_ptr, 0, &a_name_data))) { ! kerb_err_reply(client, pkt, i, "check_princ failed"); a_name_data.key_low = a_name_data.key_high = 0; return; } *************** *** 586,592 **** /* this does all the checking */ if ((i = check_princ(service, instance, lifetime, &s_name_data))) { ! kerb_err_reply(client, pkt, i, lt); a_name_data.key_high = a_name_data.key_low = 0; s_name_data.key_high = s_name_data.key_low = 0; return; - --- 590,596 ---- /* this does all the checking */ if ((i = check_princ(service, instance, lifetime, &s_name_data))) { ! kerb_err_reply(client, pkt, i, "check_princ_failed"); a_name_data.key_high = a_name_data.key_low = 0; s_name_data.key_high = s_name_data.key_low = 0; return; *************** *** 664,681 **** tk->length = 0; k_flags = 0; /* various kerberos flags */ auth->length = 4 + strlen((char *)pkt->dat + 3); auth->length += (int) *(pkt->dat + auth->length) + (int) *(pkt->dat + auth->length + 1) + 2; memcpy(auth->dat, pkt->dat, auth->length); strncpy(tktrlm, (char *)auth->dat + 3, REALM_SZ); if (set_tgtkey(tktrlm)) { lt = klog(L_ERR_UNK, "FAILED realm %s unknown. Host: %s ", tktrlm, inet_ntoa(client_host)); ! kerb_err_reply(client, pkt, kerno, lt); return; } kerno = krb_rd_req(auth, "ktbtgt", tktrlm, client_host.s_addr, - --- 668,706 ---- tk->length = 0; k_flags = 0; /* various kerberos flags */ + auth->mbz = 0; /* pkt->mbz already zeroed */ auth->length = 4 + strlen((char *)pkt->dat + 3); + if (auth->length + 1 > MAX_KTXT_LEN) { + lt = klog(L_KRB_PERR, + "APPL request with realm length too long from %s", + inet_ntoa(client_host)); + kerb_err_reply(client, pkt, RD_AP_INCON, + "realm length too long"); + return; + } + auth->length += (int) *(pkt->dat + auth->length) + (int) *(pkt->dat + auth->length + 1) + 2; + if (auth->length > MAX_KTXT_LEN) { + lt = klog(L_KRB_PERR, + "APPL request with funky tkt or req_id length from %s", + inet_ntoa(client_host)); + kerb_err_reply(client, pkt, RD_AP_INCON, + "funky tkt or req_id length"); + return; + } memcpy(auth->dat, pkt->dat, auth->length); strncpy(tktrlm, (char *)auth->dat + 3, REALM_SZ); + tktrlm[REALM_SZ-1] = '\0'; if (set_tgtkey(tktrlm)) { lt = klog(L_ERR_UNK, "FAILED realm %s unknown. Host: %s ", tktrlm, inet_ntoa(client_host)); ! /* no better error code */ ! kerb_err_reply(client, pkt, ! KERB_ERR_PRINCIPAL_UNKNOWN, lt); return; } kerno = krb_rd_req(auth, "ktbtgt", tktrlm, client_host.s_addr, *************** *** 720,726 **** kerno = check_princ(service, instance, req_life, &s_name_data); if (kerno) { ! kerb_err_reply(client, pkt, kerno, lt); return; } /* Bound requested lifetime with service and user */ - --- 745,751 ---- kerno = check_princ(service, instance, req_life, &s_name_data); if (kerno) { ! kerb_err_reply(client, pkt, kerno, "check_princ failed"); return; } /* Bound requested lifetime with service and user */ *************** *** 844,850 **** static char e_msg[128]; strcpy(e_msg, "\nKerberos error -- "); ! strcat(e_msg, string); cr_err_reply(e_pkt, req_name_ptr, req_inst_ptr, req_realm_ptr, req_time_ws, err, e_msg); krb4_sendto(f, (char *) e_pkt->dat, e_pkt->length, 0, - --- 869,875 ---- static char e_msg[128]; strcpy(e_msg, "\nKerberos error -- "); ! strncat(e_msg, string, sizeof(e_msg) - 1 - 19); cr_err_reply(e_pkt, req_name_ptr, req_inst_ptr, req_realm_ptr, req_time_ws, err, e_msg); krb4_sendto(f, (char *) e_pkt->dat, e_pkt->length, 0, *************** *** 989,995 **** kdb_encrypt_key(key, key, master_key, master_key_schedule, DECRYPT); krb_set_key((char *) key, 0); ! strcpy(lastrealm, r); return (KSUCCESS); } - --- 1014,1021 ---- kdb_encrypt_key(key, key, master_key, master_key_schedule, DECRYPT); krb_set_key((char *) key, 0); ! strncpy(lastrealm, r, sizeof(lastrealm) - 1); ! lastrealm[sizeof(lastrealm) - 1] = '\0'; return (KSUCCESS); } PATCHES AGAINST krb5-1.1.1: These patches should apply cleanly against krb5-1.2-beta1 as well. These patches will also be made available at: http://web.mit.edu/kerberos/www/advisories/krb4kdc_111_patch.txt Index: krb5/src/kdc/kerberos_v4.c diff -c krb5/src/kdc/kerberos_v4.c:5.65.2.2 krb5/src/kdc/kerberos_v4.c:5.65.2.3 *** krb5/src/kdc/kerberos_v4.c:5.65.2.2 Wed Sep 22 20:47:22 1999 - --- krb5/src/kdc/kerberos_v4.c Mon Jun 5 13:58:34 2000 *************** *** 233,243 **** return(retval); if (!*local_realm) { /* local-realm name already set up */ - - /* XXX assumes realm is null-terminated! */ lrealm = master_princ->realm.data; ! if (strlen(lrealm) < sizeof(local_realm)) ! strcpy(local_realm, lrealm); ! else retval = KRB5_CONFIG_NOTENUFSPACE; } /* convert client_fulladdr to client_sockaddr: - --- 233,243 ---- return(retval); if (!*local_realm) { /* local-realm name already set up */ lrealm = master_princ->realm.data; ! if (master_princ->realm.length < sizeof(local_realm)) { ! memcpy(local_realm, lrealm, master_princ->realm.length); ! local_realm[master_princ->realm.length] = '\0'; ! } else retval = KRB5_CONFIG_NOTENUFSPACE; } /* convert client_fulladdr to client_sockaddr: *************** *** 256,261 **** - --- 256,262 ---- return KRB5KRB_ERR_FIELD_TOOLONG; } v4_pkt.length = pkt->length; + v4_pkt.mbz = 0; memcpy( v4_pkt.dat, pkt->data, pkt->length); kerberos_v4( &client_sockaddr, &v4_pkt); *************** *** 293,299 **** case L_APPL_REQ: strcpy(log_text, "PROCESS_V4:"); vsprintf(log_text+strlen(log_text), format, pvar); ! krb5_klog_syslog(logpri, log_text); /* ignore the other types... */ } va_end(pvar); - --- 294,300 ---- case L_APPL_REQ: strcpy(log_text, "PROCESS_V4:"); vsprintf(log_text+strlen(log_text), format, pvar); ! krb5_klog_syslog(logpri, "%s", log_text); /* ignore the other types... */ } va_end(pvar); *************** *** 622,627 **** - --- 623,631 ---- req_act_vno = req_version; + /* set these to point to something safe */ + req_name_ptr = req_inst_ptr = req_realm_ptr = ""; + /* check if disabled, but we tell client */ if (kdc_v4 == KDC_V4_DISABLE) { lt = klog(L_KRB_PERR, *************** *** 700,706 **** if ((i = check_princ(req_name_ptr, req_inst_ptr, 0, &a_name_data, &k5key, 0))) { ! kerb_err_reply(client, pkt, i, lt); a_name_data.key_low = a_name_data.key_high = 0; krb5_free_keyblock_contents(kdc_context, &k5key); return; - --- 704,710 ---- if ((i = check_princ(req_name_ptr, req_inst_ptr, 0, &a_name_data, &k5key, 0))) { ! kerb_err_reply(client, pkt, i, "check_princ failed"); a_name_data.key_low = a_name_data.key_high = 0; krb5_free_keyblock_contents(kdc_context, &k5key); return; *************** *** 715,721 **** /* this does all the checking */ if ((i = check_princ(service, instance, lifetime, &s_name_data, &k5key, 1))) { ! kerb_err_reply(client, pkt, i, lt); a_name_data.key_high = a_name_data.key_low = 0; s_name_data.key_high = s_name_data.key_low = 0; krb5_free_keyblock_contents(kdc_context, &k5key); - --- 719,725 ---- /* this does all the checking */ if ((i = check_princ(service, instance, lifetime, &s_name_data, &k5key, 1))) { ! kerb_err_reply(client, pkt, i, "check_princ failed"); a_name_data.key_high = a_name_data.key_low = 0; s_name_data.key_high = s_name_data.key_low = 0; krb5_free_keyblock_contents(kdc_context, &k5key); *************** *** 806,824 **** tk->length = 0; k_flags = 0; /* various kerberos flags */ auth->length = 4 + strlen((char *)pkt->dat + 3); auth->length += (int) *(pkt->dat + auth->length) + (int) *(pkt->dat + auth->length + 1) + 2; memcpy(auth->dat, pkt->dat, auth->length); strncpy(tktrlm, (char *)auth->dat + 3, REALM_SZ); kvno = (krb5_kvno)auth->dat[2]; if (set_tgtkey(tktrlm, kvno)) { lt = klog(L_ERR_UNK, "FAILED set_tgtkey realm %s, kvno %d. Host: %s ", tktrlm, kvno, inet_ntoa(client_host)); ! kerb_err_reply(client, pkt, kerno, lt); return; } kerno = krb_rd_req(auth, "krbtgt", tktrlm, client_host.s_addr, - --- 810,849 ---- tk->length = 0; k_flags = 0; /* various kerberos flags */ + auth->mbz = 0; /* pkt->mbz already zeroed */ auth->length = 4 + strlen((char *)pkt->dat + 3); + if (auth->length + 1 > MAX_KTXT_LEN) { + lt = klog(L_KRB_PERR, + "APPL request with realm length too long from %s", + inet_ntoa(client_host)); + kerb_err_reply(client, pkt, RD_AP_INCON, + "realm length too long"); + return; + } + auth->length += (int) *(pkt->dat + auth->length) + (int) *(pkt->dat + auth->length + 1) + 2; + if (auth->length > MAX_KTXT_LEN) { + lt = klog(L_KRB_PERR, + "APPL request with funky tkt or req_id length from %s", + inet_ntoa(client_host)); + kerb_err_reply(client, pkt, RD_AP_INCON, + "funky tkt or req_id length"); + return; + } memcpy(auth->dat, pkt->dat, auth->length); strncpy(tktrlm, (char *)auth->dat + 3, REALM_SZ); + tktrlm[REALM_SZ-1] = '\0'; kvno = (krb5_kvno)auth->dat[2]; if (set_tgtkey(tktrlm, kvno)) { lt = klog(L_ERR_UNK, "FAILED set_tgtkey realm %s, kvno %d. Host: %s ", tktrlm, kvno, inet_ntoa(client_host)); ! /* no better error code */ ! kerb_err_reply(client, pkt, ! KERB_ERR_PRINCIPAL_UNKNOWN, lt); return; } kerno = krb_rd_req(auth, "krbtgt", tktrlm, client_host.s_addr, *************** *** 863,869 **** kerno = check_princ(service, instance, req_life, &s_name_data, &k5key, 1); if (kerno) { ! kerb_err_reply(client, pkt, kerno, lt); s_name_data.key_high = s_name_data.key_low = 0; krb5_free_keyblock_contents(kdc_context, &k5key); return; - --- 888,894 ---- kerno = check_princ(service, instance, req_life, &s_name_data, &k5key, 1); if (kerno) { ! kerb_err_reply(client, pkt, kerno, "check_princ failed"); s_name_data.key_high = s_name_data.key_low = 0; krb5_free_keyblock_contents(kdc_context, &k5key); return; *************** *** 968,974 **** static char e_msg[128]; strcpy(e_msg, "\nKerberos error -- "); ! strcat(e_msg, string); cr_err_reply(e_pkt, req_name_ptr, req_inst_ptr, req_realm_ptr, req_time_ws, err, e_msg); krb4_sendto(f, (char *) e_pkt->dat, e_pkt->length, 0, - --- 993,999 ---- static char e_msg[128]; strcpy(e_msg, "\nKerberos error -- "); ! strncat(e_msg, string, sizeof(e_msg) - 1 - 19); cr_err_reply(e_pkt, req_name_ptr, req_inst_ptr, req_realm_ptr, req_time_ws, err, e_msg); krb4_sendto(f, (char *) e_pkt->dat, e_pkt->length, 0, *************** *** 1127,1133 **** if (!K4KDC_ENCTYPE_OK(k5key.enctype)) { krb_set_key_krb5(kdc_context, &k5key); ! strcpy(lastrealm, r); last_kvno = kvno; } else { /* unseal tgt key from master key */ - --- 1152,1159 ---- if (!K4KDC_ENCTYPE_OK(k5key.enctype)) { krb_set_key_krb5(kdc_context, &k5key); ! strncpy(lastrealm, r, sizeof(lastrealm) - 1); ! lastrealm[sizeof(lastrealm) - 1] = '\0'; last_kvno = kvno; } else { /* unseal tgt key from master key */ *************** *** 1136,1142 **** kdb_encrypt_key(key, key, master_key, master_key_schedule, DECRYPT); krb_set_key((char *) key, 0); ! strcpy(lastrealm, r); last_kvno = kvno; } krb5_free_keyblock_contents(kdc_context, &k5key); - --- 1162,1169 ---- kdb_encrypt_key(key, key, master_key, master_key_schedule, DECRYPT); krb_set_key((char *) key, 0); ! strncpy(lastrealm, r, sizeof(lastrealm) - 1); ! lastrealm[sizeof(lastrealm) - 1] = '\0'; last_kvno = kvno; } krb5_free_keyblock_contents(kdc_context, &k5key); Index: krb5/src/lib/krb5/krb/kfree.c diff -c krb5/src/lib/krb5/krb/kfree.c:5.3.4.1 krb5/src/lib/krb5/krb/kfree.c:5.3.4.2 *** krb5/src/lib/krb5/krb/kfree.c:5.3.4.1 Wed Sep 22 20:50:11 1999 - --- krb5/src/lib/krb5/krb/kfree.c Mon Jun 5 18:30:06 2000 *************** *** 36,42 **** if (val->contents) krb5_xfree(val->contents); krb5_xfree(val); - - return; } KRB5_DLLIMP void KRB5_CALLCONV - --- 36,41 ---- *************** *** 52,58 **** krb5_xfree(*temp); } krb5_xfree(val); - - return; } - --- 51,56 ---- *************** *** 64,70 **** if (val->enc_part.ciphertext.data) krb5_xfree(val->enc_part.ciphertext.data); krb5_xfree(val); - - return; } KRB5_DLLIMP void KRB5_CALLCONV - --- 62,67 ---- *************** *** 77,83 **** if (val->authenticator.ciphertext.data) krb5_xfree(val->authenticator.ciphertext.data); krb5_xfree(val); - - return; } KRB5_DLLIMP void KRB5_CALLCONV - --- 74,79 ---- *************** *** 88,94 **** if (val->subkey) krb5_free_keyblock(context, val->subkey); krb5_xfree(val); - - return; } KRB5_DLLIMP void KRB5_CALLCONV - --- 84,89 ---- *************** *** 96,110 **** krb5_context context; krb5_authenticator FAR *val; { ! if (val->checksum) krb5_free_checksum(context, val->checksum); ! if (val->client) krb5_free_principal(context, val->client); ! if (val->subkey) krb5_free_keyblock(context, val->subkey); ! if (val->authorization_data) ! krb5_free_authdata(context, val->authorization_data); ! return; } KRB5_DLLIMP void KRB5_CALLCONV - --- 91,112 ---- krb5_context context; krb5_authenticator FAR *val; { ! if (val->checksum) { krb5_free_checksum(context, val->checksum); ! val->checksum = 0; ! } ! if (val->client) { krb5_free_principal(context, val->client); ! val->client = 0; ! } ! if (val->subkey) { krb5_free_keyblock(context, val->subkey); ! val->subkey = 0; ! } ! if (val->authorization_data) { ! krb5_free_authdata(context, val->authorization_data); ! val->authorization_data = 0; ! } } KRB5_DLLIMP void KRB5_CALLCONV *************** *** 120,126 **** krb5_xfree(*temp); } krb5_xfree(val); - - return; } KRB5_DLLIMP void KRB5_CALLCONV - --- 122,127 ---- *************** *** 128,143 **** krb5_context context; krb5_authenticator FAR *val; { ! if (val->checksum) ! krb5_free_checksum(context, val->checksum); ! if (val->client) ! krb5_free_principal(context, val->client); ! if (val->subkey) ! krb5_free_keyblock(context, val->subkey); ! if (val->authorization_data) ! krb5_free_authdata(context, val->authorization_data); krb5_xfree(val); - - return; } KRB5_DLLIMP void KRB5_CALLCONV - --- 129,136 ---- krb5_context context; krb5_authenticator FAR *val; { ! krb5_free_authenticator_contents(context, val); krb5_xfree(val); } KRB5_DLLIMP void KRB5_CALLCONV *************** *** 145,154 **** krb5_context context; register krb5_checksum *val; { ! if (val->contents) ! krb5_xfree(val->contents); krb5_xfree(val); - - return; } KRB5_DLLIMP void KRB5_CALLCONV - --- 138,145 ---- krb5_context context; register krb5_checksum *val; { ! krb5_free_checksum_contents(context, val); krb5_xfree(val); } KRB5_DLLIMP void KRB5_CALLCONV *************** *** 156,164 **** krb5_context context; register krb5_checksum *val; { ! if (val->contents) krb5_xfree(val->contents); ! return; } KRB5_DLLIMP void KRB5_CALLCONV - --- 147,156 ---- krb5_context context; register krb5_checksum *val; { ! if (val->contents) { krb5_xfree(val->contents); ! val->contents = 0; ! } } KRB5_DLLIMP void KRB5_CALLCONV *************** *** 171,177 **** if (val->enc_part.ciphertext.data) krb5_xfree(val->enc_part.ciphertext.data); krb5_xfree(val); - - return; } /* - --- 163,168 ---- *************** *** 184,206 **** krb5_context context; krb5_creds FAR *val; { ! if (val->client) krb5_free_principal(context, val->client); ! if (val->server) krb5_free_principal(context, val->server); if (val->keyblock.contents) { memset((char *)val->keyblock.contents, 0, val->keyblock.length); krb5_xfree(val->keyblock.contents); } ! if (val->ticket.data) krb5_xfree(val->ticket.data); ! if (val->second_ticket.data) krb5_xfree(val->second_ticket.data); ! if (val->addresses) krb5_free_addresses(context, val->addresses); ! if (val->authdata) krb5_free_authdata(context, val->authdata); ! return; } KRB5_DLLIMP void KRB5_CALLCONV - --- 175,209 ---- krb5_context context; krb5_creds FAR *val; { ! if (val->client) { krb5_free_principal(context, val->client); ! val->client = 0; ! } ! if (val->server) { krb5_free_principal(context, val->server); + val->server = 0; + } if (val->keyblock.contents) { memset((char *)val->keyblock.contents, 0, val->keyblock.length); krb5_xfree(val->keyblock.contents); + val->keyblock.contents = 0; } ! if (val->ticket.data) { krb5_xfree(val->ticket.data); ! val->ticket.data = 0; ! } ! if (val->second_ticket.data) { krb5_xfree(val->second_ticket.data); ! val->second_ticket.data = 0; ! } ! if (val->addresses) { krb5_free_addresses(context, val->addresses); ! val->addresses = 0; ! } ! if (val->authdata) { krb5_free_authdata(context, val->authdata); ! val->authdata = 0; ! } } KRB5_DLLIMP void KRB5_CALLCONV *************** *** 210,219 **** { register krb5_cred_info **temp; ! if (val->r_address) ! krb5_free_address(context, val->r_address); ! if (val->s_address) ! krb5_free_address(context, val->s_address); if (val->ticket_info) { for (temp = val->ticket_info; *temp; temp++) { - --- 213,226 ---- { register krb5_cred_info **temp; ! if (val->r_address) { ! krb5_free_address(context, val->r_address); ! val->r_address = 0; ! } ! if (val->s_address) { ! krb5_free_address(context, val->s_address); ! val->s_address = 0; ! } if (val->ticket_info) { for (temp = val->ticket_info; *temp; temp++) { *************** *** 228,235 **** krb5_xfree((*temp)); } krb5_xfree(val->ticket_info); } - - return; } - --- 235,242 ---- krb5_xfree((*temp)); } krb5_xfree(val->ticket_info); + val->ticket_info = 0; } } *************** *** 240,246 **** { krb5_free_cred_contents(context, val); krb5_xfree(val); - - return; } - --- 247,252 ---- *************** *** 252,258 **** if (val->data) krb5_xfree(val->data); krb5_xfree(val); - - return; } KRB5_DLLIMP void KRB5_CALLCONV - --- 258,263 ---- *************** *** 260,268 **** krb5_context context; krb5_data FAR * val; { ! if (val->data) krb5_xfree(val->data); ! return; } void krb5_free_etype_info(context, info) - --- 265,274 ---- krb5_context context; krb5_data FAR * val; { ! if (val->data) { krb5_xfree(val->data); ! val->data = 0; ! } } void krb5_free_etype_info(context, info) *************** *** 294,300 **** if (val->caddrs) krb5_free_addresses(context, val->caddrs); krb5_xfree(val); - - return; } KRB5_DLLIMP void KRB5_CALLCONV - --- 300,305 ---- *************** *** 313,319 **** if (val->authorization_data) krb5_free_authdata(context, val->authorization_data); krb5_xfree(val); - - return; } - --- 318,323 ---- *************** *** 331,337 **** if (val->e_data.data) krb5_xfree(val->e_data.data); krb5_xfree(val); - - return; } KRB5_DLLIMP void KRB5_CALLCONV - --- 335,340 ---- *************** *** 350,356 **** if (val->enc_part2) krb5_free_enc_kdc_rep_part(context, val->enc_part2); krb5_xfree(val); - - return; } - --- 353,358 ---- *************** *** 376,382 **** if (val->second_ticket) krb5_free_tickets(context, val->second_ticket); krb5_xfree(val); - - return; } KRB5_DLLIMP void KRB5_CALLCONV - --- 378,383 ---- *************** *** 387,394 **** if (key->contents) { memset(key->contents, 0, key->length); krb5_xfree(key->contents); } - - return; } KRB5_DLLIMP void KRB5_CALLCONV - --- 388,395 ---- if (key->contents) { memset(key->contents, 0, key->length); krb5_xfree(key->contents); + key->contents = 0; } } KRB5_DLLIMP void KRB5_CALLCONV *************** *** 398,404 **** { krb5_free_keyblock_contents(context, val); krb5_xfree(val); - - return; } - --- 399,404 ---- *************** *** 413,419 **** for (temp = val; *temp; temp++) krb5_xfree(*temp); krb5_xfree(val); - - return; } KRB5_DLLIMP void KRB5_CALLCONV - --- 413,418 ---- *************** *** 429,435 **** krb5_xfree(*temp); } krb5_xfree(val); - - return; } KRB5_DLLIMP void KRB5_CALLCONV - --- 428,433 ---- *************** *** 451,457 **** if (val->realm.data) krb5_xfree(val->realm.data); krb5_xfree(val); - - return; } KRB5_DLLIMP void KRB5_CALLCONV - --- 449,454 ---- *************** *** 462,468 **** if (val->enc_part.ciphertext.data) krb5_xfree(val->enc_part.ciphertext.data); krb5_xfree(val); - - return; } KRB5_DLLIMP void KRB5_CALLCONV - --- 459,464 ---- *************** *** 477,483 **** if (val->s_address) krb5_free_address(context, val->s_address); krb5_xfree(val); - - return; } KRB5_DLLIMP void KRB5_CALLCONV - --- 473,478 ---- *************** *** 488,494 **** if (val->element) krb5_free_pwd_sequences(context, val->element); krb5_xfree(val); - - return; } - --- 483,488 ---- *************** *** 497,507 **** krb5_context context; passwd_phrase_element FAR * FAR *val; { ! if ((*val)->passwd) krb5_xfree((*val)->passwd); ! if ((*val)->phrase) krb5_xfree((*val)->phrase); ! return; } - --- 491,504 ---- krb5_context context; passwd_phrase_element FAR * FAR *val; { ! if ((*val)->passwd) { krb5_xfree((*val)->passwd); ! (*val)->passwd = 0; ! } ! if ((*val)->phrase) { krb5_xfree((*val)->phrase); ! (*val)->phrase = 0; ! } } *************** *** 519,525 **** if (val->checksum) krb5_free_checksum(context, val->checksum); krb5_xfree(val); - - return; } - --- 516,521 ---- *************** *** 535,541 **** if (val->enc_part2) krb5_free_enc_tkt_part(context, val->enc_part2); krb5_xfree(val); - - return; } KRB5_DLLIMP void KRB5_CALLCONV - --- 531,536 ---- *************** *** 548,554 **** for (temp = val; *temp; temp++) krb5_free_ticket(context, *temp); krb5_xfree(val); - - return; } - --- 543,548 ---- *************** *** 573,579 **** if (val->authenticator) krb5_free_authenticator(context, val->authenticator); krb5_xfree(val); - - return; } KRB5_DLLIMP void KRB5_CALLCONV - --- 567,572 ---- *************** *** 583,589 **** { if (val) krb5_xfree(val); - - return; } KRB5_DLLIMP void KRB5_CALLCONV - --- 576,581 ---- *************** *** 612,619 **** krb5_free_data_contents(ctx, &sc->sam_response_prompt); if (sc->sam_pk_for_sad.data) krb5_free_data_contents(ctx, &sc->sam_pk_for_sad); ! if (sc->sam_cksum.contents) krb5_xfree(sc->sam_cksum.contents); } KRB5_DLLIMP void KRB5_CALLCONV - --- 604,613 ---- krb5_free_data_contents(ctx, &sc->sam_response_prompt); if (sc->sam_pk_for_sad.data) krb5_free_data_contents(ctx, &sc->sam_pk_for_sad); ! if (sc->sam_cksum.contents) { krb5_xfree(sc->sam_cksum.contents); + sc->sam_cksum.contents = 0; + } } KRB5_DLLIMP void KRB5_CALLCONV *************** *** 685,688 **** return; krb5_xfree(pa_enc_ts); } - - - --- 679,681 ---- FIXES FOR KTH-krb4: If you are still running a version before 0.10, please upgrade to 1.0.1. See the following URL for retrieving distributions: http://www.pdc.kth.se/kth-krb/ PATCHES AGAINST CNS: These patches compile but have not been tested. They will also be made available at: http://web.mit.edu/kerberos/www/advisories/krb4kdc_cns_patch.txt *** kerberos.c.orig Sat Feb 24 05:43:44 1996 - --- kerberos.c Fri Jun 2 15:25:22 2000 *************** *** 358,363 **** - --- 358,377 ---- } + static void str_length_check(str, max_size) + char *str; + int max_size; + { + int i; + char *cp; + + for (i=0, cp = str; i < max_size-1; i++, cp++) { + if (*cp == 0) + return; + } + *cp = 0; + } + kerberos(client, pkt) struct sockaddr_in *client; KTEXT pkt; *************** *** 397,402 **** - --- 411,419 ---- req_act_vno = req_version; + /* set these to point to something safe */ + req_name_ptr = req_inst_ptr = req_realm_ptr = ""; + /* check packet version */ if (req_version != KRB_PROT_VERSION) { lt = klog(L_KRB_PERR, *************** *** 435,442 **** - --- 452,462 ---- /* set up and correct for byte order and alignment */ req_name_ptr = (char *) pkt_a_name(pkt); + str_length_check(req_name_ptr, ANAME_SZ); req_inst_ptr = (char *) pkt_a_inst(pkt); + str_length_check(req_inst_ptr, INST_SZ); req_realm_ptr = (char *) pkt_a_realm(pkt); + str_length_check(req_realm_ptr, REALM_SZ); memcpy(&req_time_ws, pkt_time_ws(pkt), sizeof(req_time_ws)); /* time has to be diddled */ if (swap_bytes) { *************** *** 460,466 **** if (i = check_princ(req_name_ptr, req_inst_ptr, 0, &a_name_data)) { ! kerb_err_reply(client, pkt, i, lt); return; } tk->length = 0; /* init */ - --- 480,487 ---- if (i = check_princ(req_name_ptr, req_inst_ptr, 0, &a_name_data)) { ! kerb_err_reply(client, pkt, i, "check_princ failed"); ! a_name_data.key_low = a_name_data.key_high = 0; return; } tk->length = 0; /* init */ *************** *** 471,477 **** /* this does all the checking */ if (i = check_princ(service, instance, req_life, &s_name_data)) { ! kerb_err_reply(client, pkt, i, lt); return; } - --- 492,500 ---- /* this does all the checking */ if (i = check_princ(service, instance, req_life, &s_name_data)) { ! kerb_err_reply(client, pkt, i, "check_princ_failed"); ! a_name_data.key_high = a_name_data.key_low = 0; ! s_name_data.key_high = s_name_data.key_low = 0; return; } *************** *** 627,638 **** - --- 650,679 ---- k_flags = 0; /* various kerberos flags */ auth->length = 4 + strlen((char *) pkt->dat + 3); + if (auth->length + 1 > MAX_KTXT_LEN) { + lt = klog(L_KRB_PERR, + "APPL request with realm length too long from %s", + inet_ntoa(client_host)); + kerb_err_reply(client, pkt, RD_AP_INCON, + "realm length too long"); + return; + } + auth->length += (int) *(pkt->dat + auth->length) + (int) *(pkt->dat + auth->length + 1) + 2; + if (auth->length > MAX_KTXT_LEN) { + lt = klog(L_KRB_PERR, + "APPL request with funky tkt or req_id length from %s", + inet_ntoa(client_host)); + kerb_err_reply(client, pkt, RD_AP_INCON, + "funky tkt or req_id length"); + return; + } memcpy(auth->dat, pkt->dat, auth->length); strncpy(tktrlm, (char *) auth->dat + 3, REALM_SZ); + tktrlm[REALM_SZ-1] = '\0'; if (set_tgtkey(tktrlm)) { lt = klog(L_ERR_UNK, "FAILED realm %s unknown. Host: %s ", *************** *** 680,686 **** kerno = check_princ(service, instance, req_life, &s_name_data); if (kerno) { ! kerb_err_reply(client, pkt, kerno, lt); return; } /* Bound requested lifetime with service and user */ - --- 721,727 ---- kerno = check_princ(service, instance, req_life, &s_name_data); if (kerno) { ! kerb_err_reply(client, pkt, kerno, "check_princ failed"); return; } /* Bound requested lifetime with service and user */ *************** *** 804,810 **** static char e_msg[128]; strcpy(e_msg, "\nKerberos error -- "); ! strcat(e_msg, string); cr_err_reply(e_pkt, req_name_ptr, req_inst_ptr, req_realm_ptr, req_time_ws, err, e_msg); sendto(f, e_pkt->dat, e_pkt->length, 0, (struct sockaddr *) client, - --- 845,851 ---- static char e_msg[128]; strcpy(e_msg, "\nKerberos error -- "); ! strncat(e_msg, string, sizeof(e_msg) - 1 - 19); cr_err_reply(e_pkt, req_name_ptr, req_inst_ptr, req_realm_ptr, req_time_ws, err, e_msg); sendto(f, e_pkt->dat, e_pkt->length, 0, (struct sockaddr *) client, *************** *** 928,934 **** kdb_encrypt_key(key, key, master_key, master_key_schedule, DECRYPT); krb_set_key(key, 0); ! strcpy(lastrealm, r); return (KSUCCESS); } - --- 969,976 ---- kdb_encrypt_key(key, key, master_key, master_key_schedule, DECRYPT); krb_set_key(key, 0); ! strncpy(lastrealm, r, sizeof(lastrealm) - 1); ! lastrealm[sizeof(lastrealm) - 1] = '\0'; return (KSUCCESS); } PATCHES AGAINST MIT Kerberos 4 Patch 10: Please note that these patches have not been tested in any way. Use at your own risk. These patches will also be made available at: http://web.mit.edu/kerberos/www/advisories/krb4kdc_krb4p10_patch.txt *** kerberos.c.orig Fri Mar 1 01:34:09 1991 - --- kerberos.c Fri Jun 2 15:37:32 2000 *************** *** 318,323 **** - --- 318,337 ---- } + static void str_length_check(str, max_size) + char *str; + int max_size; + { + int i; + char *cp; + + for (i=0, cp = str; i < max_size-1; i++, cp++) { + if (*cp == 0) + return; + } + *cp = 0; + } + kerberos(client, pkt) struct sockaddr_in *client; KTEXT pkt; *************** *** 357,362 **** - --- 371,379 ---- req_act_vno = req_version; + /* set these to point to something safe */ + req_name_ptr = req_inst_ptr = req_realm_ptr = ""; + /* check packet version */ if (req_version != KRB_PROT_VERSION) { lt = klog(L_KRB_PERR, *************** *** 392,399 **** - --- 409,419 ---- /* set up and correct for byte order and alignment */ req_name_ptr = (char *) pkt_a_name(pkt); + str_length_check(req_name_ptr, ANAME_SZ); req_inst_ptr = (char *) pkt_a_inst(pkt); + str_length_check(req_inst_ptr, INST_SZ); req_realm_ptr = (char *) pkt_a_realm(pkt); + str_length_check(req_realm_ptr, REALM_SZ); bcopy(pkt_time_ws(pkt), &req_time_ws, sizeof(req_time_ws)); /* time has to be diddled */ if (swap_bytes) { *************** *** 413,419 **** if (i = check_princ(req_name_ptr, req_inst_ptr, 0, &a_name_data)) { ! kerb_err_reply(client, pkt, i, lt); return; } tk->length = 0; /* init */ - --- 433,440 ---- if (i = check_princ(req_name_ptr, req_inst_ptr, 0, &a_name_data)) { ! kerb_err_reply(client, pkt, i, "check_princ failed"); ! a_name_data.key_low = a_name_data.key_high = 0; return; } tk->length = 0; /* init */ *************** *** 424,430 **** /* this does all the checking */ if (i = check_princ(service, instance, lifetime, &s_name_data)) { ! kerb_err_reply(client, pkt, i, lt); return; } /* Bound requested lifetime with service and user */ - --- 445,453 ---- /* this does all the checking */ if (i = check_princ(service, instance, lifetime, &s_name_data)) { ! kerb_err_reply(client, pkt, i, "check_princ_failed"); ! a_name_data.key_high = a_name_data.key_low = 0; ! s_name_data.key_high = s_name_data.key_low = 0; return; } /* Bound requested lifetime with service and user */ *************** *** 497,508 **** - --- 520,549 ---- k_flags = 0; /* various kerberos flags */ auth->length = 4 + strlen(pkt->dat + 3); + if (auth->length + 1 > MAX_KTXT_LEN) { + lt = klog(L_KRB_PERR, + "APPL request with realm length too long from %s", + inet_ntoa(client_host)); + kerb_err_reply(client, pkt, RD_AP_INCON, + "realm length too long"); + return; + } + auth->length += (int) *(pkt->dat + auth->length) + (int) *(pkt->dat + auth->length + 1) + 2; + if (auth->length > MAX_KTXT_LEN) { + lt = klog(L_KRB_PERR, + "APPL request with funky tkt or req_id length from %s", + inet_ntoa(client_host)); + kerb_err_reply(client, pkt, RD_AP_INCON, + "funky tkt or req_id length"); + return; + } bcopy(pkt->dat, auth->dat, auth->length); strncpy(tktrlm, auth->dat + 3, REALM_SZ); + tktrlm[REALM_SZ-1] = '\0'; if (set_tgtkey(tktrlm)) { lt = klog(L_ERR_UNK, "FAILED realm %s unknown. Host: %s ", *************** *** 550,556 **** kerno = check_princ(service, instance, req_life, &s_name_data); if (kerno) { ! kerb_err_reply(client, pkt, kerno, lt); return; } /* Bound requested lifetime with service and user */ - --- 591,597 ---- kerno = check_princ(service, instance, req_life, &s_name_data); if (kerno) { ! kerb_err_reply(client, pkt, kerno, "check_princ failed"); return; } /* Bound requested lifetime with service and user */ *************** *** 669,675 **** static char e_msg[128]; strcpy(e_msg, "\nKerberos error -- "); ! strcat(e_msg, string); cr_err_reply(e_pkt, req_name_ptr, req_inst_ptr, req_realm_ptr, req_time_ws, err, e_msg); sendto(f, e_pkt->dat, e_pkt->length, 0, client, S_AD_SZ); - --- 710,716 ---- static char e_msg[128]; strcpy(e_msg, "\nKerberos error -- "); ! strncat(e_msg, string, sizeof(e_msg) - 1 - 19); cr_err_reply(e_pkt, req_name_ptr, req_inst_ptr, req_realm_ptr, req_time_ws, err, e_msg); sendto(f, e_pkt->dat, e_pkt->length, 0, client, S_AD_SZ); *************** *** 793,799 **** kdb_encrypt_key(key, key, master_key, master_key_schedule, DECRYPT); krb_set_key(key, 0); ! strcpy(lastrealm, r); return (KSUCCESS); } - --- 834,841 ---- kdb_encrypt_key(key, key, master_key, master_key_schedule, DECRYPT); krb_set_key(key, 0); ! strncpy(lastrealm, r, sizeof(lastrealm) - 1); ! lastrealm[sizeof(lastrealm) - 1] = '\0'; return (KSUCCESS); } ACKNOWLEDGEMENTS: Thanks to Matt Power of MIT for assistance in debugging this vulnerability. Thanks to Assar Westerlund of SICS for information about KTH-krb4 and Heimdal. Thanks to Nalin Dahyabhai of Redhat for additional buffer paranoia patches. Thanks to Andrew Newman of Yale University for discovering the double-free problem with the krb5-1.1.x KDC. -----BEGIN PGP SIGNATURE----- Version: 2.6.2 iQCVAwUBOUE+AabDgE/zdoE9AQEO3QQA1UAOcnx3tM/KYEVDpYuB0ZpGb4weQT0T nubJKoywGcvxDZ4+F2yd/cQMehG9y0/rsb8iIJmXyeUZTF1ygerS6xvW03bUF2KM l2sra2WeAdMK12H7J2B4Cxah3E96hhGHuZ32MYhsTUMxKH3sL7jJpfabHjwPYcY9 thdTpc1rP1U= =WeXV -----END PGP SIGNATURE-----