struct sockaddr_in *from;
char *realm;
{
-#ifdef HAVE_KRB4
- int result;
- char rlmprincipal[ANAME_SZ+INST_SZ+REALM_SZ+4];
- char srcprincipal[ANAME_SZ+INST_SZ+REALM_SZ+4];
- KTEXT_ST authent, ticket;
- AUTH_DAT dat;
- ZChecksum_t checksum;
- CREDENTIALS cred;
- C_Block session_key;
+#ifdef HAVE_KRB5
+ char *authbuf;
+ char rlmprincipal[ANAME_SZ+INST_SZ+REALM_SZ+4+1024];
+ krb5_principal princ;
+ krb5_data packet;
+ krb5_ticket *tkt;
+ char *name;
+ krb5_error_code result;
+ krb5_principal server;
+ krb5_keytab keytabid = 0;
+ krb5_auth_context authctx;
+ krb5_keyblock *keyblock;
+ krb5_enctype enctype;
+ krb5_cksumtype cksumtype;
+ krb5_data cksumbuf;
+#if HAVE_KRB5_C_MAKE_CHECKSUM
+ krb5_checksum checksum;
+ krb5_boolean valid;
+#else
+ krb5_crypto cryptctx;
+ Checksum checksum;
+ size_t xlen;
+#endif
+ char *cksum0_base, *cksum1_base, *cksum2_base;
+ char *svcinst, *x, *y;
+ char *asn1_data, *key_data;
+ int asn1_len, key_len, cksum0_len, cksum1_len, cksum2_len;
+#ifdef KRB5_AUTH_CON_GETAUTHENTICATOR_TAKES_DOUBLE_POINTER
+ krb5_authenticator *authenticator;
+#define KRB5AUTHENT authenticator
+#else
+ krb5_authenticator authenticator;
+#define KRB5AUTHENT &authenticator
+#endif
+ int len;
if (!notice->z_auth)
return ZAUTH_NO;
if (notice->z_authent_len <= 0)
return ZAUTH_FAILED;
+ len = strlen(notice->z_ascii_authent)+1;
+ authbuf=malloc(len);
+
/* Read in the authentication data. */
- if (ZReadAscii(notice->z_ascii_authent,
- strlen(notice->z_ascii_authent)+1,
- (unsigned char *)authent.dat,
- notice->z_authent_len) == ZERR_BADFIELD) {
+ if (ZReadZcode(notice->z_ascii_authent,
+ authbuf,
+ len, &len) == ZERR_BADFIELD) {
return ZAUTH_FAILED;
}
- authent.length = notice->z_authent_len;
-
- /* Copy the ticket out of the authentication data. */
- if (krb_find_ticket(&authent, &ticket) != RD_AP_OK)
- return ZAUTH_FAILED;
- (void) sprintf(rlmprincipal, "%s.%s@%s", SERVER_SERVICE,
+ (void) sprintf(rlmprincipal, "%s/%s@%s", SERVER_SERVICE,
SERVER_INSTANCE, realm);
- /* Try to do a fast check against the cryptographic checksum. */
- if (find_session_key(&ticket, session_key, srcprincipal) >= 0) {
- if (strcmp(srcprincipal, rlmprincipal) != 0)
- return ZAUTH_FAILED;
- if (notice->z_time.tv_sec - NOW > CLOCK_SKEW)
- return ZAUTH_FAILED;
- checksum = compute_rlm_checksum(notice, session_key);
+ packet.length = len;
+ packet.data = authbuf;
- /* If checksum matches, packet is authentic. If not, we might
- * have an outdated session key, so keep going the slow way.
- */
- if (checksum == notice->z_checksum) {
- (void) memcpy((char *)__Zephyr_session, (char *)session_key,
- sizeof(C_Block)); /* For control_dispatch() */
- return ZAUTH_YES;
- }
-
- /* Try again. This way we can switch to the same checksums
- * that the rest of Zephyr uses at a future date, but for now
- * we need to be compatible */
- checksum = compute_checksum(notice, session_key);
- if (checksum == notice->z_checksum) {
- memcpy(__Zephyr_session, session_key, sizeof(C_Block));
- return ZAUTH_YES;
- }
+ result = krb5_kt_resolve(Z_krb5_ctx,
+ keytab_file, &keytabid);
+ if (result) {
+ free(authbuf);
+ return (result);
}
- /* We don't have the session key cached; do it the long way. */
- result = krb_rd_req(&authent, SERVER_SERVICE, SERVER_INSTANCE,
- from->sin_addr.s_addr, &dat, srvtab_file);
- if (result == RD_AP_OK) {
- sprintf(srcprincipal, "%s%s%s@%s", dat.pname, dat.pinst[0] ? "." : "",
- dat.pinst, dat.prealm);
- if (strcmp(rlmprincipal, srcprincipal))
- return ZAUTH_FAILED;
- } else {
- return ZAUTH_FAILED; /* didn't decode correctly */
+ /* HOLDING: authbuf, keytabid */
+ /* Create the auth context */
+ result = krb5_auth_con_init(Z_krb5_ctx, &authctx);
+ if (result) {
+ krb5_kt_close(Z_krb5_ctx, keytabid);
+ free(authbuf);
+ return (result);
}
- /* Check the cryptographic checksum. */
-#ifdef NOENCRYPTION
- checksum = 0;
+ /* HOLDING: authbuf, authctx */
+ result = krb5_build_principal(Z_krb5_ctx, &server, strlen(__Zephyr_realm),
+ __Zephyr_realm, SERVER_SERVICE,
+ SERVER_INSTANCE, NULL);
+ if (!result) {
+ result = krb5_rd_req(Z_krb5_ctx, &authctx, &packet, server,
+ keytabid, 0, &tkt);
+ krb5_free_principal(Z_krb5_ctx, server);
+ }
+ krb5_kt_close(Z_krb5_ctx, keytabid);
+
+ if (result) {
+ if (result == KRB5KRB_AP_ERR_REPEAT)
+ syslog(LOG_DEBUG, "k5 auth failed: %s", error_message(result));
+ else
+ syslog(LOG_WARNING,"k5 auth failed: %s", error_message(result));
+ free(authbuf);
+ krb5_auth_con_free(Z_krb5_ctx, authctx);
+ return ZAUTH_FAILED;
+ }
+
+ /* HOLDING: authbuf, authctx */
+#ifndef HAVE_KRB5_TICKET_ENC_PART2
+ if (tkt == 0 || tkt->client == 0) {
+ if (tkt) krb5_free_ticket(Z_krb5_ctx, tkt);
+ free(authbuf);
+ krb5_auth_con_free(Z_krb5_ctx, authctx);
+ return ZAUTH_FAILED;
+ }
+ princ = tkt->client;
#else
- checksum = compute_rlm_checksum(notice, dat.session);
-#endif
- if (checksum != notice->z_checksum) {
-#ifndef NOENCRYPTION
- checksum = compute_checksum(notice, dat.session);
- if (checksum != notice->z_checksum)
+ if (tkt == 0 || tkt->enc_part2 == 0) {
+ if (tkt) krb5_free_ticket(Z_krb5_ctx, tkt);
+ free(authbuf);
+ krb5_auth_con_free(Z_krb5_ctx, authctx);
+ return ZAUTH_FAILED;
+ }
+ princ = tkt->enc_part2->client;
#endif
+ if (princ == 0) {
+ krb5_free_ticket(Z_krb5_ctx, tkt);
+ free(authbuf);
+ krb5_auth_con_free(Z_krb5_ctx, authctx);
return ZAUTH_FAILED;
}
- /* Record the session key, expiry time, and source principal in the
- * hash table, so we can do a fast check next time. */
- add_session_key(&ticket, dat.session, srcprincipal,
- (time_t)(dat.time_sec + dat.life * 5 * 60));
+ /* HOLDING: authbuf, authctx, tkt */
+ result = krb5_unparse_name(Z_krb5_ctx, princ, &name);
+ if (result) {
+ syslog(LOG_WARNING, "k5 unparse_name failed: %s",
+ error_message(result));
+ free(authbuf);
+ krb5_auth_con_free(Z_krb5_ctx, authctx);
+ krb5_free_ticket(Z_krb5_ctx, tkt);
+ return ZAUTH_FAILED;
+ }
- return ZAUTH_YES;
+ krb5_free_ticket(Z_krb5_ctx, tkt);
-#else /* !HAVE_KRB4 */
+ /* HOLDING: authbuf, authctx, name */
+ if (strcmp(name, rlmprincipal)) {
+ syslog(LOG_WARNING, "k5 name mismatch: '%s' vs '%s'",
+ name, rlmprincipal);
+ krb5_auth_con_free(Z_krb5_ctx, authctx);
+ free(name);
+ free(authbuf);
+ return ZAUTH_FAILED;
+ }
+ free(name);
+ free(authbuf);
+
+ /* HOLDING: authctx */
+ /* Get an authenticator so we can get the keyblock */
+ result = krb5_auth_con_getauthenticator (Z_krb5_ctx, authctx,
+ &authenticator);
+ if(result) {
+ krb5_auth_con_free(Z_krb5_ctx, authctx);
+ return result;
+ }
+
+ /* HOLDING: authctx, authenticator */
+ result = krb5_auth_con_getkey(Z_krb5_ctx, authctx, &keyblock);
+ if (result) {
+ krb5_auth_con_free(Z_krb5_ctx, authctx);
+ krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
+ return (ZAUTH_FAILED);
+ }
+
+ /* HOLDING: authctx, authenticator, keyblock */
+ /* Figure out what checksum type to use */
+#if HAVE_KRB5_CREDS_KEYBLOCK_ENCTYPE
+ key_data = keyblock->contents;
+ key_len = keyblock->length;
+ enctype = keyblock->enctype;
+ result = Z_krb5_lookup_cksumtype(enctype, &cksumtype);
+ if (result) {
+ krb5_free_keyblock(Z_krb5_ctx, keyblock);
+ krb5_auth_con_free(Z_krb5_ctx, authctx);
+ krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
+ return (ZAUTH_FAILED);
+ }
+#else
+ key_data = keyblock->keyvalue.data;
+ key_len = keyblock->keyvalue.length;
+ {
+ unsigned int len;
+ ENCTYPE *val;
+ int i = 0;
+
+ result = krb5_keytype_to_enctypes(Z_krb5_ctx, keyblock->keytype,
+ &len, &val);
+ if (result) {
+ krb5_free_keyblock(Z_krb5_ctx, keyblock);
+ krb5_auth_con_free(Z_krb5_ctx, authctx);
+ krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
+ return (ZAUTH_FAILED);
+ }
+
+ do {
+ if (i == len) break;
+ result = Z_krb5_lookup_cksumtype(val[i], &cksumtype);
+ i++;
+ } while (result != 0);
+
+ if (result) {
+ krb5_free_keyblock(Z_krb5_ctx, keyblock);
+ krb5_auth_con_free(Z_krb5_ctx, authctx);
+ krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
+ return (ZAUTH_FAILED);
+ }
+ enctype = val[i-1];
+ }
+#endif
+ /* HOLDING: authctx, authenticator, keyblock */
+
+ /* Assemble the things to be checksummed */
+ /* first part is from start of packet through z_default_format:
+ * - z_version
+ * - z_num_other_fields
+ * - z_kind
+ * - z_uid
+ * - z_port
+ * - z_auth
+ * - z_authent_len
+ * - z_ascii_authent
+ * - z_class
+ * - z_class_inst
+ * - z_opcode
+ * - z_sender
+ * - z_recipient
+ * - z_default_format
+ */
+ cksum0_base = notice->z_packet;
+ x = notice->z_default_format;
+ cksum0_len = x + strlen(x) + 1 - cksum0_base;
+ /* second part is from z_multinotice through other fields:
+ * - z_multinotice
+ * - z_multiuid
+ * - z_other_fields[]
+ */
+ cksum1_base = notice->z_multinotice;
+ if (notice->z_num_other_fields)
+ x = notice->z_other_fields[notice->z_num_other_fields];
+ else
+ x = cksum1_base + strlen(cksum1_base) + 1; /* multiuid */
+ cksum1_len = x + strlen(x) + 1 - cksum1_base;
+
+ /* last part is the message body */
+ cksum2_base = notice->z_message;
+ cksum2_len = notice->z_message_len;
+
+ if ((!notice->z_ascii_checksum || *notice->z_ascii_checksum != 'Z') &&
+ key_len == 8 &&
+ (enctype == ENCTYPE_DES_CBC_CRC ||
+ enctype == ENCTYPE_DES_CBC_MD4 ||
+ enctype == ENCTYPE_DES_CBC_MD5)) {
+ /* try old-format checksum (covers cksum0 only) */
+
+ ZChecksum_t our_checksum;
+
+ our_checksum = des_quad_cksum(cksum0_base, NULL, cksum0_len, 0,
+ key_data);
+ if (our_checksum == notice->z_checksum) {
+ krb5_free_keyblock(Z_krb5_ctx, keyblock);
+ krb5_auth_con_free(Z_krb5_ctx, authctx);
+ krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
+ return ZAUTH_YES;
+ }
+ }
+
+ /* HOLDING: authctx, authenticator */
+
+ cksumbuf.length = cksum0_len + cksum1_len + cksum2_len;
+ cksumbuf.data = malloc(cksumbuf.length);
+ if (!cksumbuf.data) {
+ krb5_free_keyblock(Z_krb5_ctx, keyblock);
+ krb5_auth_con_free(Z_krb5_ctx, authctx);
+ krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
+ return ZAUTH_NO;
+ }
+ /* HOLDING: authctx, authenticator, cksumbuf.data */
+
+ memcpy(cksumbuf.data, cksum0_base, cksum0_len);
+ memcpy(cksumbuf.data + cksum0_len, cksum1_base, cksum1_len);
+ memcpy(cksumbuf.data + cksum0_len + cksum1_len,
+ cksum2_base, cksum2_len);
+
+ /* decode zcoded checksum */
+ /* The encoded form is always longer than the original */
+ asn1_len = strlen(notice->z_ascii_checksum) + 1;
+ asn1_data = malloc(asn1_len);
+ if (!asn1_data) {
+ krb5_free_keyblock(Z_krb5_ctx, keyblock);
+ krb5_auth_con_free(Z_krb5_ctx, authctx);
+ krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
+ free(cksumbuf.data);
+ return ZAUTH_FAILED;
+ }
+ /* HOLDING: authctx, authenticator, cksumbuf.data, asn1_data */
+ result = ZReadZcode(notice->z_ascii_checksum,
+ asn1_data, asn1_len, &asn1_len);
+ if (result != ZERR_NONE) {
+ krb5_free_keyblock(Z_krb5_ctx, keyblock);
+ krb5_auth_con_free(Z_krb5_ctx, authctx);
+ krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
+ free(asn1_data);
+ free(cksumbuf.data);
+ return ZAUTH_FAILED;
+ }
+ /* HOLDING: asn1_data, cksumbuf.data */
+
+#if HAVE_KRB5_C_MAKE_CHECKSUM
+ /* Verify the checksum -- MIT crypto API */
+ memset(&checksum, 0, sizeof(checksum));
+ checksum.length = asn1_len;
+ checksum.contents = asn1_data;
+ checksum.checksum_type = cksumtype;
+ result = krb5_c_verify_checksum(Z_krb5_ctx,
+ keyblock, Z_KEYUSAGE_SRV_CKSUM,
+ &cksumbuf, &checksum, &valid);
+ free(asn1_data);
+ krb5_auth_con_free(Z_krb5_ctx, authctx);
+ krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
+ krb5_free_keyblock(Z_krb5_ctx, keyblock);
+ free(cksumbuf.data);
+ if (!result && valid)
+ return (ZAUTH_YES);
+ else
+ return (ZAUTH_FAILED);
+#else
+ /* Verify the checksum -- heimdal crypto API */
+ checksum.checksum.length = asn1_len;
+ checksum.checksum.data = asn1_data;
+ checksum.cksumtype = cksumtype;
+
+ /* HOLDING: authctx, authenticator, cksumbuf.data, asn1_data */
+
+ result = krb5_crypto_init(Z_krb5_ctx, keyblock, enctype, &cryptctx);
+ if (result) {
+ krb5_auth_con_free(Z_krb5_ctx, authctx);
+ krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
+ krb5_free_keyblock(Z_krb5_ctx, keyblock);
+ free(asn1_data);
+ free(cksumbuf.data);
+ return result;
+ }
+ /* HOLDING: authctx, authenticator, cryptctx, cksumbuf.data, checksum */
+ result = krb5_verify_checksum(Z_krb5_ctx, cryptctx,
+ Z_KEYUSAGE_SRV_CKSUM,
+ cksumbuf.data, cksumbuf.length,
+ &checksum);
+ krb5_free_keyblock(Z_krb5_ctx, keyblock);
+ krb5_crypto_destroy(Z_krb5_ctx, cryptctx);
+ krb5_auth_con_free(Z_krb5_ctx, authctx);
+ krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
+ free(asn1_data);
+ free(cksumbuf.data);
+ if (result)
+ return (ZAUTH_FAILED);
+ else
+ return (ZAUTH_YES);
+#endif
+#else
return (notice->z_auth) ? ZAUTH_YES : ZAUTH_NO;
#endif
}
+#undef KRB5AUTHENT
+
Code_t
ZCheckAuthentication(notice, from)
ZNotice_t *notice;
AUTH_DAT dat;
ZChecksum_t checksum;
C_Block session_key;
+ char instance[INST_SZ+1];
if (!notice->z_auth)
return ZAUTH_NO;
}
authent.length = notice->z_authent_len;
+#if 0
/* Copy the ticket out of the authentication data. */
if (krb_find_ticket(&authent, &ticket) != RD_AP_OK)
return ZAUTH_FAILED;
return ZAUTH_YES;
}
}
+#endif
+
+ strcpy(instance, SERVER_INSTANCE);
/* We don't have the session key cached; do it the long way. */
- result = krb_rd_req(&authent, SERVER_SERVICE, SERVER_INSTANCE,
+ result = krb_rd_req(&authent, SERVER_SERVICE, instance,
from->sin_addr.s_addr, &dat, srvtab_file);
if (result == RD_AP_OK) {
memcpy(__Zephyr_session, dat.session, sizeof(C_Block));
if (checksum != notice->z_checksum)
return ZAUTH_FAILED;
+#if 0
/* Record the session key, expiry time, and source principal in the
* hash table, so we can do a fast check next time. */
add_session_key(&ticket, dat.session, srcprincipal,
(time_t)(dat.time_sec + dat.life * 5 * 60));
+#endif
return ZAUTH_YES;
#endif
}
+
#ifdef HAVE_KRB4
static int hash_ticket(p, len)