]> asedeno.scripts.mit.edu Git - 1ts-debian.git/blobdiff - zephyr/server/kstuff.c
krb5-interrealm patches
[1ts-debian.git] / zephyr / server / kstuff.c
index 5fd31c9f2a0e57b54938a9c012c3028314e457d3..423d5bd02eca7204de322eaabbff7864ccc44be9 100644 (file)
@@ -148,15 +148,41 @@ ZCheckRealmAuthentication(notice, from, realm)
     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;
@@ -165,87 +191,327 @@ ZCheckRealmAuthentication(notice, from, realm)
     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;
@@ -258,6 +524,7 @@ ZCheckAuthentication(notice, from)
     AUTH_DAT dat;
     ZChecksum_t checksum;
     C_Block session_key;
+    char instance[INST_SZ+1];
 
     if (!notice->z_auth)
        return ZAUTH_NO;
@@ -275,6 +542,7 @@ ZCheckAuthentication(notice, from)
     }
     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;
@@ -295,9 +563,12 @@ ZCheckAuthentication(notice, from)
            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));
@@ -318,10 +589,12 @@ ZCheckAuthentication(notice, from)
     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;
 
@@ -330,6 +603,7 @@ ZCheckAuthentication(notice, from)
 #endif
 }
 
+
 #ifdef HAVE_KRB4
 
 static int hash_ticket(p, len)