aboutsummaryrefslogtreecommitdiff
path: root/src/kdc/kerberos_v4.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/kdc/kerberos_v4.c')
-rw-r--r--src/kdc/kerberos_v4.c198
1 files changed, 144 insertions, 54 deletions
diff --git a/src/kdc/kerberos_v4.c b/src/kdc/kerberos_v4.c
index f05452e..3580c19 100644
--- a/src/kdc/kerberos_v4.c
+++ b/src/kdc/kerberos_v4.c
@@ -149,13 +149,13 @@ static krb5_data *response;
void kerberos_v4 PROTOTYPE((struct sockaddr_in *, KTEXT));
void kerb_err_reply PROTOTYPE((struct sockaddr_in *, KTEXT, long, char *));
-static int set_tgtkey PROTOTYPE((char *, krb5_kvno));
+static int set_tgtkey PROTOTYPE((char *, krb5_kvno, krb5_boolean));
/* Attributes converted from V5 to V4 - internal representation */
#define V4_KDB_REQUIRES_PREAUTH 0x1
#define V4_KDB_DISALLOW_ALL_TIX 0x2
#define V4_KDB_REQUIRES_PWCHANGE 0x4
-
+#define V4_KDB_DISALLOW_SVR 0x8
/* v4 compatibitly mode switch */
#define KDC_V4_NONE 0 /* Don't even respond to packets */
@@ -182,6 +182,7 @@ static const struct v4mode_lookup_entry v4mode_table[] = {
static const int v4mode_table_nents = sizeof(v4mode_table)/
sizeof(v4mode_table[0]);
+static int allow_v4_crossrealm = 0;
void process_v4_mode(progname, string)
const char *progname;
@@ -210,6 +211,11 @@ void process_v4_mode(progname, string)
return;
}
+void enable_v4_crossrealm ( char *programname) {
+ allow_v4_crossrealm = 1;
+ krb5_klog_syslog(LOG_ERR, "Enabling v4 cross-realm compatibility; this is a known security hole");
+}
+
krb5_error_code
process_v4( pkt, client_fulladdr, is_secondary, resp)
const krb5_data *pkt;
@@ -233,11 +239,11 @@ krb5_data **resp;
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
+ 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,6 +262,7 @@ krb5_data **resp;
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);
@@ -400,6 +407,14 @@ compat_decrypt_key (in5, out4, out5, issrv)
#define MIN5 300
#define HR21 255
+/*
+ * Previously this code returned either a v4 key or a v5 key and you
+ * could tell from the enctype of the v5 key whether the v4 key was
+ * useful. Now we return both keys so the code can try both des3 and
+ * des decryption. We fail if the ticket doesn't have a v4 key.
+ * Also, note as a side effect, the v5 key is basically useless in
+ * the client case. It is still returned so the caller can free it.
+ */
static int
kerb_get_principal(name, inst, principal, maxn, more, k5key, kvno, issrv)
char *name; /* could have wild card */
@@ -481,8 +496,28 @@ kerb_get_principal(name, inst, principal, maxn, more, k5key, kvno, issrv)
return(0);
}
} else {
- /* XXX yes I know this is a hardcoded search order */
- if (krb5_dbe_find_enctype(kdc_context, &entries,
+ if ( krb5_dbe_find_enctype(kdc_context, &entries,
+ ENCTYPE_DES_CBC_CRC,
+ KRB5_KDB_SALTTYPE_V4, kvno, &pkey) &&
+ krb5_dbe_find_enctype(kdc_context, &entries,
+ ENCTYPE_DES_CBC_CRC,
+ -1, kvno, &pkey)) {
+ lt = klog(L_KRB_PERR,
+ "KDC V4: failed to find key for %s.%s #%d",
+ name, inst, kvno);
+ krb5_db_free_principal(kdc_context, &entries, nprinc);
+ return(0);
+ }
+ }
+
+ if (!compat_decrypt_key(pkey, k, k5key, issrv)) {
+ memcpy( &principal->key_low, k, LONGLEN);
+ memcpy( &principal->key_high, (krb5_ui_4 *) k + 1, LONGLEN);
+ }
+ memset(k, 0, sizeof k);
+ if (issrv) {
+ krb5_free_keyblock_contents (kdc_context, k5key);
+ if (krb5_dbe_find_enctype(kdc_context, &entries,
ENCTYPE_DES3_CBC_RAW,
-1, kvno, &pkey) &&
krb5_dbe_find_enctype(kdc_context, &entries,
@@ -503,12 +538,10 @@ kerb_get_principal(name, inst, principal, maxn, more, k5key, kvno, issrv)
krb5_db_free_principal(kdc_context, &entries, nprinc);
return(0);
}
+ compat_decrypt_key(pkey, k, k5key, issrv);
+ memset (k, 0, sizeof k);
}
- if (!compat_decrypt_key(pkey, k, k5key, issrv)) {
- memcpy( &principal->key_low, k, LONGLEN);
- memcpy( &principal->key_high, (krb5_ui_4 *) k + 1, LONGLEN);
- }
/* convert v5's entries struct to v4's Principal struct:
* v5's time-unit for lifetimes is 1 sec, while v4 uses 5 minutes.
*/
@@ -539,6 +572,9 @@ kerb_get_principal(name, inst, principal, maxn, more, k5key, kvno, issrv)
if (isflagset(entries.attributes, KRB5_KDB_DISALLOW_ALL_TIX)) {
principal->attributes |= V4_KDB_DISALLOW_ALL_TIX;
}
+ if (issrv && isflagset(entries.attributes, KRB5_KDB_DISALLOW_SVR)) {
+ principal->attributes |= V4_KDB_DISALLOW_SVR;
+ }
if (isflagset(entries.attributes, KRB5_KDB_REQUIRES_PWCHANGE)) {
principal->attributes |= V4_KDB_REQUIRES_PWCHANGE;
}
@@ -622,6 +658,9 @@ kerberos_v4(client, pkt)
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,7 +739,7 @@ kerberos_v4(client, pkt)
if ((i = check_princ(req_name_ptr, req_inst_ptr, 0,
&a_name_data, &k5key, 0))) {
- kerb_err_reply(client, pkt, i, lt);
+ 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,7 +754,7 @@ kerberos_v4(client, pkt)
/* this does all the checking */
if ((i = check_princ(service, instance, lifetime,
&s_name_data, &k5key, 1))) {
- kerb_err_reply(client, pkt, i, lt);
+ 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);
@@ -739,21 +778,14 @@ kerberos_v4(client, pkt)
kdb_encrypt_key(key, key, master_key,
master_key_schedule, DECRYPT);
/* construct and seal the ticket */
- if (K4KDC_ENCTYPE_OK(k5key.enctype)) {
- krb_create_ticket(tk, k_flags, a_name_data.name,
- a_name_data.instance, local_realm,
- client_host.s_addr, (char *) session_key,
- lifetime, kerb_time.tv_sec,
- s_name_data.name, s_name_data.instance,
- key);
- } else {
- krb_cr_tkt_krb5(tk, k_flags, a_name_data.name,
- a_name_data.instance, local_realm,
- client_host.s_addr, (char *) session_key,
- lifetime, kerb_time.tv_sec,
- s_name_data.name, s_name_data.instance,
- &k5key);
- }
+ /* We always issue des tickets; the 3des tickets are a broken hack*/
+ krb_create_ticket(tk, k_flags, a_name_data.name,
+ a_name_data.instance, local_realm,
+ client_host.s_addr, (char *) session_key,
+ lifetime, kerb_time.tv_sec,
+ s_name_data.name, s_name_data.instance,
+ key);
+
krb5_free_keyblock_contents(kdc_context, &k5key);
memset(key, 0, sizeof(key));
memset(key_s, 0, sizeof(key_s));
@@ -806,23 +838,64 @@ kerberos_v4(client, pkt)
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,
+ if ((!allow_v4_crossrealm)&&strcmp(tktrlm, local_realm) != 0) {
+ lt = klog(L_ERR_UNK,
+ "Cross realm ticket from %s denied by policy,", tktrlm);
+ kerb_err_reply(client, pkt,
+ KERB_ERR_PRINCIPAL_UNKNOWN, lt);
+ return;
+ }
+ if (set_tgtkey(tktrlm, kvno, 0)) {
+ 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);
+ /* 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,
ad, 0);
+ if (kerno) {
+ if (set_tgtkey(tktrlm, kvno, 1)) {
+ lt = klog(L_ERR_UNK,
+ "FAILED 3des 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,
+ ad, 0);
+ }
if (kerno) {
klog(L_ERR_UNK, "FAILED krb_rd_req from %s: %s",
@@ -863,7 +936,7 @@ kerberos_v4(client, pkt)
kerno = check_princ(service, instance, req_life,
&s_name_data, &k5key, 1);
if (kerno) {
- kerb_err_reply(client, pkt, kerno, lt);
+ 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;
@@ -888,21 +961,13 @@ kerberos_v4(client, pkt)
des_new_random_key(session_key);
#endif
- if (K4KDC_ENCTYPE_OK(k5key.enctype)) {
- krb_create_ticket(tk, k_flags, ad->pname, ad->pinst,
- ad->prealm, client_host.s_addr,
- (char *) session_key, lifetime,
- kerb_time.tv_sec,
- s_name_data.name, s_name_data.instance,
- key);
- } else {
- krb_cr_tkt_krb5(tk, k_flags, ad->pname, ad->pinst,
- ad->prealm, client_host.s_addr,
- (char *) session_key, lifetime,
- kerb_time.tv_sec,
- s_name_data.name, s_name_data.instance,
- &k5key);
- }
+ /* ALways issue des tickets*/
+ krb_create_ticket(tk, k_flags, ad->pname, ad->pinst,
+ ad->prealm, client_host.s_addr,
+ (char *) session_key, lifetime,
+ kerb_time.tv_sec,
+ s_name_data.name, s_name_data.instance,
+ key);
krb5_free_keyblock_contents(kdc_context, &k5key);
memset(key, 0, sizeof(key));
memset(key_s, 0, sizeof(key_s));
@@ -968,7 +1033,7 @@ kerb_err_reply(client, pkt, err, string)
static char e_msg[128];
strcpy(e_msg, "\nKerberos error -- ");
- strcat(e_msg, string);
+ 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,
@@ -1066,6 +1131,13 @@ check_princ(p_name, instance, lifetime, p, k5key, issrv)
return KERB_ERR_NAME_EXP;
}
+ if (isflagset(p->attributes, V4_KDB_DISALLOW_SVR)) {
+ lt = klog(L_ERR_SEXP, "V5 DISALLOW_SVR set: "
+ "\"%s\" \"%s\"", p_name, instance);
+ /* Not sure of a better error to return */
+ return KERB_ERR_NAME_EXP;
+ }
+
/*
* Does the principal require preauthentication?
*/
@@ -1103,20 +1175,22 @@ check_princ(p_name, instance, lifetime, p, k5key, issrv)
/* Set the key for krb_rd_req so we can check tgt */
static int
-set_tgtkey(r, kvno)
+set_tgtkey(r, kvno, use_3des)
char *r; /* Realm for desired key */
krb5_kvno kvno;
+ krb5_boolean use_3des;
{
int n;
static char lastrealm[REALM_SZ] = "";
static int last_kvno = 0;
+ static krb5_boolean last_use_3des = 0;
Principal p_st;
Principal *p = &p_st;
C_Block key;
krb5_keyblock k5key;
k5key.contents = NULL;
- if (!strcmp(lastrealm, r) && last_kvno == kvno)
+ if (!strcmp(lastrealm, r) && last_kvno == kvno && last_use_3des == use_3des)
return (KSUCCESS);
/* log("Getting key for %s", r); */
@@ -1125,10 +1199,25 @@ set_tgtkey(r, kvno)
if (n == 0)
return (KFAILURE);
- if (!K4KDC_ENCTYPE_OK(k5key.enctype)) {
+ if (isflagset(p->attributes, V4_KDB_DISALLOW_ALL_TIX)) {
+ lt = klog(L_ERR_SEXP,
+ "V5 DISALLOW_ALL_TIX set: \"krbtgt\" \"%s\"", r);
+ krb5_free_keyblock_contents(kdc_context, &k5key);
+ return KFAILURE;
+ }
+
+ if (isflagset(p->attributes, V4_KDB_DISALLOW_SVR)) {
+ lt = klog(L_ERR_SEXP, "V5 DISALLOW_SVR set: \"krbtgt\" \"%s\"", r);
+ krb5_free_keyblock_contents(kdc_context, &k5key);
+ return KFAILURE;
+ }
+
+ if (use_3des&&!K4KDC_ENCTYPE_OK(k5key.enctype)) {
krb_set_key_krb5(kdc_context, &k5key);
- strcpy(lastrealm, r);
+ strncpy(lastrealm, r, sizeof(lastrealm) - 1);
+ lastrealm[sizeof(lastrealm) - 1] = '\0';
last_kvno = kvno;
+ last_use_3des = use_3des;
} else {
/* unseal tgt key from master key */
memcpy(key, &p->key_low, 4);
@@ -1136,7 +1225,8 @@ set_tgtkey(r, kvno)
kdb_encrypt_key(key, key, master_key,
master_key_schedule, DECRYPT);
krb_set_key((char *) key, 0);
- strcpy(lastrealm, r);
+ strncpy(lastrealm, r, sizeof(lastrealm) - 1);
+ lastrealm[sizeof(lastrealm) - 1] = '\0';
last_kvno = kvno;
}
krb5_free_keyblock_contents(kdc_context, &k5key);