Massive changes to do_as_req.c, do_tgs_req.c, kdc_util.c, and policy.c
authorTheodore Tso <tytso@mit.edu>
Wed, 30 Sep 1992 14:08:14 +0000 (14:08 +0000)
committerTheodore Tso <tytso@mit.edu>
Wed, 30 Sep 1992 14:08:14 +0000 (14:08 +0000)
Fixed bug so that renewable/forwardable/proxiable/tickets work on all
tickets, not just the TGS server.  Fixed bug so that proxiable tickets
don't work on TGT tickets.

Revamped structure to make things cleaner, and easier to understand.  Nearly
all of the validation routines have been moved to a validate_as_request
and a validate_tgs_request subroutine in kdc_util.c.

git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@2451 dc483132-0cff-0310-8789-dd5450dbe970

src/kdc/do_as_req.c
src/kdc/do_tgs_req.c
src/kdc/kdc_util.c
src/kdc/policy.c

index 57fc8c17fee5bc40bba5b87ce3717fbf7640b74b..02495fcd7bb02123be96c25cceba75d985c0aa5f 100644 (file)
@@ -32,6 +32,7 @@ static char rcsid_do_as_req_c[] =
 
 #include <krb5/krb5.h>
 #include <krb5/kdb.h>
+#include <krb5/preauth.h>
 #include <krb5/los-proto.h>
 #include <krb5/asn1.h>
 #include <krb5/osconf.h>
@@ -43,8 +44,10 @@ static char rcsid_do_as_req_c[] =
 #ifdef KRB5_USE_INET
 #include <sys/types.h>
 #include <netinet/in.h>
+#ifndef hpux
 #include <arpa/inet.h>
-#endif
+#endif /* hpux */
+#endif /* KRB5_USE_INET */
 
 #include "kdc_util.h"
 #include "policy.h"
@@ -53,12 +56,58 @@ static char rcsid_do_as_req_c[] =
 static krb5_error_code prepare_error_as PROTOTYPE((krb5_kdc_req *,
                                                   int,
                                                   krb5_data **));
-
 /*
- * Do all the processing required for a AS_REQ
+ * This routine is called to verify the preauthentication information
+ * for a V5 request.  Client contains information about the principal
+ * from the database. Padata contains pre-auth info received from
+ * the network.
+ *
+ * Returns 0 if the pre-authentication is valid, non-zero to indicate
+ * an error code of some sort.
  */
 
-/* XXX needs lots of cleanup and modularizing */
+static krb5_error_code
+check_padata (client, src_addr, padata, pa_id, flags)
+    krb5_db_entry  *client;
+    krb5_address **src_addr;
+    krb5_pa_data **padata;
+    int *pa_id;                        /* Unique id which can be used for replay
+                                  of padata. */
+    int *flags;
+{
+    krb5_encrypted_keyblock *enckey;
+    krb5_keyblock tmpkey;
+    krb5_error_code retval;
+   
+    enckey = &(client->key);
+    /*         Extract client key/alt_key from master key */
+   
+    retval = KDB_CONVERT_KEY_OUTOF_DB(enckey,&tmpkey);
+    if (retval) {
+       syslog( LOG_ERR, "AS_REQ: Unable to Extract Client Key/alt_key\n");
+       return(0);
+    }
+    retval =  krb5_verify_padata(*padata,client->principal,src_addr,
+                                &tmpkey, pa_id, flags);
+    memset((char *)tmpkey.contents, 0, tmpkey.length);
+    xfree(tmpkey.contents);
+    if (retval && client->alt_key.length) {
+       /*
+        * If we failed, try again with the alternative key
+        */
+       enckey = &(client->alt_key);
+       /* Extract client key/alt_key from master key */
+       if (retval = KDB_CONVERT_KEY_OUTOF_DB(enckey,&tmpkey)){
+           syslog( LOG_ERR, "AS_REQ: Unable to Extract Client Key/alt_key\n");
+           return(0);
+       }
+       retval = krb5_verify_padata(*padata,client->principal,src_addr,
+                                   &tmpkey, pa_id, flags);
+       memset((char *)tmpkey.contents, 0, tmpkey.length);
+       xfree(tmpkey.contents);
+    }
+    return retval;
+}
 
 /*ARGSUSED*/
 krb5_error_code
@@ -75,13 +124,17 @@ krb5_data **response;                      /* filled in with a response packet */
     krb5_ticket ticket_reply;
     krb5_enc_tkt_part enc_tkt_reply;
     krb5_error_code retval;
+    int        errcode;
     int nprincs;
+    char cpw_service[255];
+    int pwreq, pa_id, pa_flags;
     krb5_boolean more;
-    krb5_timestamp kdc_time;
+    krb5_timestamp kdc_time, authtime;
     krb5_keyblock *session_key;
     krb5_keyblock encrypting_key;
     krb5_enctype useetype;
     krb5_pa_data *padat_tmp[2], padat_local;
+    char *status;
 
     register int i;
 
@@ -106,19 +159,24 @@ krb5_data **response;                     /* filled in with a response packet */
     }
 #ifdef KRB5_USE_INET
     if (from->address->addrtype == ADDRTYPE_INET)
-       fromstring = inet_ntoa(*(struct in_addr *)from->address->contents);
+       fromstring = (char *) inet_ntoa(*(struct in_addr *)from->address->contents);
 #endif
     if (!fromstring)
        fromstring = "<unknown>";
 
-    if (is_secondary)
-       syslog(LOG_INFO, "AS_REQ; host %s, %s for %s", fromstring, cname,
-              sname);
-    else
-       syslog(LOG_INFO, "AS_REQ: host %s, %s for %s", fromstring, cname,
-              sname);
-    free(cname);
-    free(sname);
+    /*
+     * Special considerations are allowed when changing passwords. Is
+     * this request for changepw?
+     *
+     * XXX This logic should be moved someplace else, perhaps the
+     * site-specific policiy file....
+     */
+    pwreq = 0;
+    sprintf(cpw_service, "%s@%s", "changepw/kerberos", 
+           krb5_princ_realm(request->server)->data);
+    if (strcmp(sname, cpw_service) == 0) pwreq++;
+
+#define cleanup() { free(cname); free(sname); }
 
     nprincs = 1;
     if (retval = krb5_db_get_principal(request->client, &client, &nprincs,
@@ -126,44 +184,66 @@ krb5_data **response;                     /* filled in with a response packet */
        return(retval);
     if (more) {
        krb5_db_free_principal(&client, nprincs);
+       cleanup();
        return(prepare_error_as(request, KDC_ERR_PRINCIPAL_NOT_UNIQUE, response));
     } else if (nprincs != 1) {
        krb5_db_free_principal(&client, nprincs);
+       cleanup();
+#ifdef KRBCONF_VAGUE_ERRORS
+       return(prepare_error_as(request, KRB_ERR_GENERIC, response));
+#else
        return(prepare_error_as(request, KDC_ERR_C_PRINCIPAL_UNKNOWN, response));
-    }  
+#endif
+    }
+    
+#undef cleanup
+#define cleanup() { krb5_db_free_principal(&client, 1); \
+                   free(sname); free(cname);}
        
     nprincs = 1;
     if (retval = krb5_db_get_principal(request->server, &server, &nprincs,
                                       &more))
        return(retval);
     if (more) {
-       krb5_db_free_principal(&client, 1);
+       cleanup();
        krb5_db_free_principal(&server, nprincs);
        return(prepare_error_as(request, KDC_ERR_PRINCIPAL_NOT_UNIQUE, response));
     } else if (nprincs != 1) {
-       krb5_db_free_principal(&client, 1);
+       cleanup();
        krb5_db_free_principal(&server, nprincs);
        return(prepare_error_as(request, KDC_ERR_S_PRINCIPAL_UNKNOWN, response));
     }
 
-#define cleanup() {krb5_db_free_principal(&client, 1); krb5_db_free_principal(&server, 1); }
+#undef cleanup
+#define cleanup() {krb5_db_free_principal(&client, 1); \
+                  krb5_db_free_principal(&server, 1); \
+                  free(cname); free(sname);  \
+              }
 
-    if (retval = check_kdb_flags_as(request, client, server)) {
-       cleanup();
-       return(prepare_error_as(request, retval, response));
-    }
-      
     if (retval = krb5_timeofday(&kdc_time)) {
+       syslog(LOG_INFO, "AS_REQ: TIME_OF_DAY: host %s, %s for %s", 
+                  fromstring, cname, sname);
        cleanup();
        return(retval);
     }
 
+    status = "UNKNOWN REASON";
+    if (retval = validate_as_request(request, client, server,
+                                    kdc_time, &status)) {
+       syslog(LOG_INFO, "AS_REQ: %s: host %s, %s for %s", status,
+                  fromstring, cname, sname);
+       cleanup();
+       return(prepare_error_as(request, retval, response));
+    }
+      
     for (i = 0; i < request->netypes; i++)
        if (valid_etype(request->etype[i]))
            break;
     if (i == request->netypes) {
        /* unsupported etype */
-
+           
+       syslog(LOG_INFO, "AS_REQ: BAD ENCRYPTION TYPE: host %s, %s for %s",
+                  fromstring, cname, sname);
        cleanup();
        return(prepare_error_as(request, KDC_ERR_ETYPE_NOSUPP, response));
     }
@@ -171,6 +251,8 @@ krb5_data **response;                       /* filled in with a response packet */
 
     if (retval = (*(krb5_csarray[useetype]->system->random_key))(krb5_csarray[useetype]->random_sequence, &session_key)) {
        /* random key failed */
+       syslog(LOG_INFO, "AS_REQ: RANDOM KEY FAILED: host %s, %s for %s",
+                  fromstring, cname, sname);
        cleanup();
        return(retval);
     }
@@ -178,6 +260,7 @@ krb5_data **response;                       /* filled in with a response packet */
 #undef cleanup
 #define cleanup() {krb5_db_free_principal(&client, 1); \
                   krb5_db_free_principal(&server, 1); \
+                  free(cname); free(sname); \
                   krb5_free_keyblock(session_key); }
 
     ticket_reply.server = request->server;
@@ -187,24 +270,18 @@ krb5_data **response;                     /* filled in with a response packet */
     enc_tkt_reply.flags = 0;
     setflag(enc_tkt_reply.flags, TKT_FLG_INITIAL);
 
-        /* It should be noted that local policy may affect the  */
+       /* It should be noted that local policy may affect the  */
         /* processing of any of these flags.  For example, some */
         /* realms may refuse to issue renewable tickets         */
 
-    if (against_flag_policy_as(request)) {
-       cleanup();
-       return(prepare_error_as(request, KDC_ERR_BADOPTION, response));
-    }
-
     if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE))
        setflag(enc_tkt_reply.flags, TKT_FLG_FORWARDABLE);
 
     if (isflagset(request->kdc_options, KDC_OPT_PROXIABLE))
-       setflag(enc_tkt_reply.flags, TKT_FLG_PROXIABLE);
+           setflag(enc_tkt_reply.flags, TKT_FLG_PROXIABLE);
 
     if (isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE))
-       setflag(enc_tkt_reply.flags, TKT_FLG_MAY_POSTDATE);
-
+           setflag(enc_tkt_reply.flags, TKT_FLG_MAY_POSTDATE);
 
     enc_tkt_reply.session = session_key;
     enc_tkt_reply.client = request->client;
@@ -214,10 +291,6 @@ krb5_data **response;                      /* filled in with a response packet */
     enc_tkt_reply.times.authtime = kdc_time;
 
     if (isflagset(request->kdc_options, KDC_OPT_POSTDATED)) {
-       if (against_postdate_policy(request->from)) {
-           cleanup();
-           return(prepare_error_as(request, KDC_ERR_POLICY, response));
-       }
        setflag(enc_tkt_reply.flags, TKT_FLG_INVALID);
        enc_tkt_reply.times.starttime = request->from;
     } else
@@ -232,9 +305,9 @@ krb5_data **response;                       /* filled in with a response packet */
                min(enc_tkt_reply.times.starttime + server.max_life,
                    enc_tkt_reply.times.starttime + max_life_for_realm)));
 
-    /* XXX why && request->till ? */
-    if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE_OK) && 
-       request->till && (enc_tkt_reply.times.endtime < request->till)) {
+    if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE_OK) &&
+       !isflagset(client.attributes, KRB5_KDB_DISALLOW_RENEWABLE) &&
+       (enc_tkt_reply.times.endtime < request->till)) {
 
        /* we set the RENEWABLE option for later processing */
 
@@ -243,9 +316,11 @@ krb5_data **response;                      /* filled in with a response packet */
     }
     rtime = (request->rtime == 0) ? kdc_infinity : request->rtime;
 
-    /* should we squelch the output renew_till to be no earlier
-       than the endtime of the ticket? XXX */
     if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE)) {
+       /*
+        * XXX Should we squelch the output renew_till to be no
+        * earlier than the endtime of the ticket? 
+        */
        setflag(enc_tkt_reply.flags, TKT_FLG_RENEWABLE);
        enc_tkt_reply.times.renew_till =
            min(rtime, enc_tkt_reply.times.starttime +
@@ -261,9 +336,72 @@ krb5_data **response;                      /* filled in with a response packet */
        enc_tkt_reply.times.starttime = 0;
 
     enc_tkt_reply.caddrs = request->addresses;
-    enc_tkt_reply.authorization_data = 0; /* XXX? */
+    enc_tkt_reply.authorization_data = 0;
+
+    /* 
+     * Check the preauthentication if it is there.
+     */
+    if (request->padata) {
+       retval = check_padata(&client,request->addresses,
+                             request->padata, &pa_id, &pa_flags);
+       if (retval) {
+#ifdef KRBCONF_KDC_MODIFIES_KDB
+           /*
+            * Note: this doesn't work if you're using slave servers!!!
+            * It also causes the database to be modified (and thus
+            * need to be locked) frequently.
+            */
+           if (client.fail_auth_count < KRB5_MAX_FAIL_COUNT) {
+               client.fail_auth_count = client.fail_auth_count + 1;
+               if (client.fail_auth_count == KRB5_MAX_FAIL_COUNT) { 
+                   client.attributes |= KRB5_KDB_DISALLOW_ALL_TIX;
+               }
+           }
+            krb5_db_put_principal(&client, &one);
+#endif
+            syslog(LOG_INFO, "AS_REQ: PREAUTH FAILED: host %s, %s for %s (%s)",
+                  fromstring, cname, sname, error_message(retval));
+            cleanup();
+#ifdef KRBCONF_VAGUE_ERRORS
+            return(prepare_error_as(request, KRB_ERR_GENERIC, response));
+#else
+           retval -= ERROR_TABLE_BASE_krb5;
+           if ((retval < 0) || (retval > 127))
+                   retval = KDC_PREAUTH_FAILED;
+            return(prepare_error_as(request, retval, response));
+#endif
+       } 
+
+       setflag(enc_tkt_reply.flags, TKT_FLG_PRE_AUTH);
+       /*
+        * If pa_type is one in which additional hardware authentication
+        * was performed set TKT_FLG_HW_AUTH too.
+        */
+       if (pa_flags & KRB5_PREAUTH_FLAGS_HARDWARE)
+            setflag(enc_tkt_reply.flags, TKT_FLG_HW_AUTH);
+    }
 
-    ticket_reply.enc_part2 = &enc_tkt_reply;
+    /*
+     * Final check before handing out ticket: If the client requires
+     * Hardware authentication, verify ticket flag is set
+     */  
+
+    if (isflagset(client.attributes, KRB5_KDB_REQUIRES_HW_AUTH) &&
+       !isflagset(enc_tkt_reply.flags, TKT_FLG_HW_AUTH)) {
+
+         /* Of course their are always exceptions, in this case if the
+            service requested is for changing of the key (password), then
+            if TKT_FLG_PRE_AUTH is set allow it. */
+       
+         if (!pwreq || !(enc_tkt_reply.flags & TKT_FLG_PRE_AUTH)){
+              syslog(LOG_INFO, "AS_REQ: Needed HW preauth: host %s, %s for %s",
+                    fromstring, cname, sname);
+              cleanup();
+              return(prepare_error_as(request, KRB_ERR_GENERIC, response));
+         }
+      }
+
+ticket_reply.enc_part2 = &enc_tkt_reply;
 
     /* convert server.key into a real key (it may be encrypted
        in the database) */
@@ -286,6 +424,7 @@ krb5_data **response;                       /* filled in with a response packet */
                   krb5_free_keyblock(session_key); \
                   memset(ticket_reply.enc_part.ciphertext.data, 0, \
                         ticket_reply.enc_part.ciphertext.length); \
+                  free(cname); free(sname); \
                   free(ticket_reply.enc_part.ciphertext.data);}
 
     /* Start assembling the response */
@@ -335,7 +474,9 @@ krb5_data **response;                       /* filled in with a response packet */
                   memset(ticket_reply.enc_part.ciphertext.data, 0, \
                         ticket_reply.enc_part.ciphertext.length); \
                   free(ticket_reply.enc_part.ciphertext.data); \
-                  if (client.salt_type == KRB5_KDB_SALTTYPE_NOREALM) xfree(padat_tmp[0]->contents);}
+                  free(cname); free(sname); \
+                  if (client.salt_type == KRB5_KDB_SALTTYPE_NOREALM) \
+                      xfree(padat_tmp[0]->contents);}
 
     reply.client = request->client;
     /* XXX need separate etypes for ticket encryption and kdc_rep encryption */
@@ -357,7 +498,7 @@ krb5_data **response;                       /* filled in with a response packet */
     /* copy the time fields EXCEPT for authtime; it's location
        is used for ktime */
     reply_encpart.times = enc_tkt_reply.times;
-    reply_encpart.times.authtime = kdc_time;
+    reply_encpart.times.authtime = authtime = kdc_time;
 
     reply_encpart.caddrs = enc_tkt_reply.caddrs;
 
@@ -379,6 +520,22 @@ krb5_data **response;                      /* filled in with a response packet */
     memset(reply.enc_part.ciphertext.data, 0,
           reply.enc_part.ciphertext.length);
     free(reply.enc_part.ciphertext.data);
+
+    if (retval) {
+       syslog(LOG_INFO, "AS_REQ; ENCODE_KDC_REP: host %s, %s for %s",
+              fromstring, cname, sname);
+    } else {
+       if (is_secondary)
+           syslog(LOG_INFO, "AS_REQ; ISSUE: authtime %d, host %s, %s for %s",
+                  authtime, fromstring, cname, sname);
+       else
+           syslog(LOG_INFO, "AS_REQ: ISSUE: authtime %d, host %s, %s for %s",
+                  authtime, fromstring, cname, sname);
+    }
+
+    free(cname);
+    free(sname);
+
     return retval;
 }
 
index 8baef7927a8e5c94c6e0afa6a1fc26e297ff4a3e..16bcebc230a362e2b699b625854ed3f31ed5b9e7 100644 (file)
@@ -42,8 +42,10 @@ static char rcsid_do_tgs_req_c[] =
 #ifdef KRB5_USE_INET
 #include <sys/types.h>
 #include <netinet/in.h>
+#ifndef hpux
 #include <arpa/inet.h>
 #endif
+#endif
 
 #include "kdc_util.h"
 #include "policy.h"
@@ -81,7 +83,7 @@ krb5_data **response;                 /* filled in with a response packet */
     krb5_error_code retval;
     int nprincs;
     krb5_boolean more;
-    krb5_timestamp kdc_time;
+    krb5_timestamp kdc_time, authtime;
     krb5_keyblock *session_key;
     krb5_timestamp until, rtime;
     krb5_keyblock encrypting_key;
@@ -89,12 +91,15 @@ krb5_data **response;                       /* filled in with a response packet */
     krb5_last_req_entry *nolrarray[2], nolrentry;
 /*    krb5_address *noaddrarray[1]; */
     krb5_enctype useetype;
+    int        errcode;
     register int i;
     int firstpass = 1;
+    char       *status;
 
 #ifdef KRB5_USE_INET
     if (from->address->addrtype == ADDRTYPE_INET)
-       fromstring = inet_ntoa(*(struct in_addr *)from->address->contents);
+       fromstring =
+           (char *) inet_ntoa(*(struct in_addr *)from->address->contents);
 #endif
     if (!fromstring)
        fromstring = "<unknown>";
@@ -109,16 +114,17 @@ krb5_data **response;                     /* filled in with a response packet */
            krb5_free_tkt_authent(req_authdat);
            header_ticket = 0;
        }
-       if (retval > ERROR_TABLE_BASE_krb5 &&
-           retval < ERROR_TABLE_BASE_krb5 + 128) {
-           /* protocol error */
-           return(prepare_error_tgs(request,
-                                    header_ticket,
-                                    retval - ERROR_TABLE_BASE_krb5,
-                                    fromstring,
-                                    response));
-       } else
+       errcode = retval - ERROR_TABLE_BASE_krb5;
+       if (errcode < 0 || errcode > 128) {
+           errcode = KRB_ERR_GENERIC;
+           prepare_error_tgs(request, header_ticket, errcode,
+                                    fromstring, response);
            return retval;
+       } else {
+           /* protocol error */
+           return(prepare_error_tgs(request, header_ticket, errcode,
+                                    fromstring, response));
+       }
     }
        
     /* we've already dealt with the AP_REQ authentication, so
@@ -135,15 +141,7 @@ krb5_data **response;                      /* filled in with a response packet */
        krb5_free_tkt_authent(req_authdat);
        return(retval);
     }
-
-    if (is_secondary)
-       syslog(LOG_INFO, "TGS_REQ; host %s, %s for %s", fromstring, cname,
-              sname);
-    else
-       syslog(LOG_INFO, "TGS_REQ: host %s, %s for %s", fromstring, cname,
-              sname);
-    free(cname);
-    free(sname);
+    authtime = header_ticket->enc_part2->times.authtime;
 
     /* XXX make sure server here has the proper realm...taken from AP_REQ
        header? */
@@ -151,12 +149,22 @@ krb5_data **response;                     /* filled in with a response packet */
     nprincs = 1;
     if (retval = krb5_db_get_principal(request->server, &server, &nprincs,
                                       &more)) {
+        syslog(LOG_INFO,
+              "TGS_REQ: GET_PRINCIPAL: authtime %d, host %s, %s for %s (%s)",
+              authtime, fromstring, cname, sname, error_message(retval));
+       free(cname);
+       free(sname);
        krb5_free_tkt_authent(req_authdat);
        return(retval);
     }
 tgt_again:
     if (more) {
+        syslog(LOG_INFO,
+              "TGS_REQ: NON_UNIQUE_PRINCIPAL: authtime %d, host %s, %s for %s",
+              authtime, fromstring, cname, sname);
        krb5_db_free_principal(&server, nprincs);
+       free(cname);
+       free(sname);
        return(prepare_error_tgs(request,
                                 header_ticket,
                                 KDC_ERR_PRINCIPAL_NOT_UNIQUE,
@@ -181,6 +189,9 @@ tgt_again:
            goto tgt_again;
        }
        krb5_db_free_principal(&server, nprincs);
+        syslog(LOG_INFO,
+              "TGS_REQ: UNKNOWN PRINCIPAL: authtime %d, host %s, %s for %s",
+              authtime, fromstring, cname, sname);
        return(prepare_error_tgs(request,
                                 header_ticket,
                                 KDC_ERR_S_PRINCIPAL_UNKNOWN,
@@ -189,9 +200,13 @@ tgt_again:
     }
 
 #define tkt_cleanup() {krb5_free_tkt_authent(req_authdat); }
-#define cleanup() { krb5_db_free_principal(&server, 1);}
+#define cleanup() { krb5_db_free_principal(&server, 1); free(cname); free(sname); }
 
-    if (retval = check_kdb_flags_tgs(request, server)) {
+    status = "UNKNOWN_REASON";
+    if (retval = validate_tgs_request(request, server, header_ticket,
+                                     kdc_time, &status)) {
+       syslog(LOG_INFO, "TGS_REQ: %s: authtime %d, host %s, %s for %s",
+              status, authtime, fromstring, cname, sname);
        cleanup();
        return(prepare_error_tgs(request,
                                 header_ticket,
@@ -201,6 +216,8 @@ tgt_again:
     }
 
     if (retval = krb5_timeofday(&kdc_time)) {
+       syslog(LOG_INFO, "TGS_REQ: TIME_OF_DAY: authtime %d, host %s, %s for %s", 
+                  authtime, fromstring, cname, sname);
        tkt_cleanup();
        cleanup();
        return(retval);
@@ -212,6 +229,8 @@ tgt_again:
     if (i == request->netypes) {
        /* unsupported etype */
 
+       syslog(LOG_INFO, "TGS_REQ: BAD ENCRYPTION TYPE: authtime %d, host %s, %s for %s",
+                  authtime, cname, sname);
        cleanup();
        return(prepare_error_tgs(request,
                                 header_ticket,
@@ -222,6 +241,8 @@ tgt_again:
 
     if (retval = (*(krb5_csarray[useetype]->system->random_key))(krb5_csarray[useetype]->random_sequence, &session_key)) {
        /* random key failed */
+       syslog(LOG_INFO, "TGS_REQ: RANDOM KEY FAILED: authtime %d, host %s, %s for %s",
+                  authtime, fromstring, cname, sname);
        tkt_cleanup();
        cleanup();
        return(retval);
@@ -229,8 +250,9 @@ tgt_again:
 
 #undef cleanup
 #define cleanup() { krb5_db_free_principal(&server, 1); \
-                  krb5_free_keyblock(session_key);}
-
+                   krb5_free_keyblock(session_key); \
+                   free(cname); free(sname); }
+    
     ticket_reply.server = request->server; /* XXX careful for realm... */
     ticket_reply.enc_part.etype = useetype;
     ticket_reply.enc_part.kvno = server.kvno;
@@ -238,6 +260,13 @@ tgt_again:
     enc_tkt_reply.flags = 0;
     enc_tkt_reply.times.starttime = 0;
 
+    /*
+     * Fix header_ticket's starttime; if it's zero, fill in the
+     * authtime's value.
+     */
+    if (!(header_ticket->enc_part2->times.starttime))
+       header_ticket->enc_part2->times.starttime =
+           header_ticket->enc_part2->times.authtime;
 
     /* don't use new addresses unless forwarded, see below */
 
@@ -245,19 +274,9 @@ tgt_again:
     /* noaddrarray[0] = 0; */
     reply_encpart.caddrs = 0;          /* optional...don't put it in */
 
-        /* It should be noted that local policy may affect the  */
-        /* processing of any of these flags.  For example, some */
-        /* realms may refuse to issue renewable tickets         */
-
-    /* the policy check MUST make sure there are no invalid option/flag
-       combinations */
-
-    if (against_flag_policy_tgs(request, header_ticket)) {
-       cleanup();
-       return(prepare_error_tgs(request, header_ticket,
-                                KDC_ERR_BADOPTION,
-                                fromstring, response));
-    }
+    /* It should be noted that local policy may affect the  */
+    /* processing of any of these flags.  For example, some */
+    /* realms may refuse to issue renewable tickets         */
 
     if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE))
        setflag(enc_tkt_reply.flags, TKT_FLG_FORWARDABLE);
@@ -291,59 +310,21 @@ tgt_again:
     if (isflagset(request->kdc_options, KDC_OPT_POSTDATED)) {
        setflag(enc_tkt_reply.flags, TKT_FLG_POSTDATED);
        setflag(enc_tkt_reply.flags, TKT_FLG_INVALID);
-       if (against_postdate_policy(request->from)) {
-           cleanup();
-           return(prepare_error_tgs(request, header_ticket,
-                                    KDC_ERR_BADOPTION,
-                                    fromstring, response));
-       }           
        enc_tkt_reply.times.starttime = request->from;
     } else
        enc_tkt_reply.times.starttime = kdc_time;
 
     if (isflagset(request->kdc_options, KDC_OPT_VALIDATE)) {
-       if (header_ticket->enc_part2->times.starttime > kdc_time) {
-           cleanup();
-           return(prepare_error_tgs(request, header_ticket,
-                                    KRB_AP_ERR_TKT_NYV,
-                                    fromstring, response));
-       }
-       /* XXX move this check out elsewhere? */
-       if (check_hot_list(header_ticket)) {
-           cleanup();
-           return(prepare_error_tgs(request, header_ticket,
-                                    KRB_AP_ERR_REPEAT,
-                                    fromstring, response));
-       }
        /* BEWARE of allocation hanging off of ticket & enc_part2, it belongs
           to the caller */
        ticket_reply = *(header_ticket);
        enc_tkt_reply = *(header_ticket->enc_part2);
        clear(enc_tkt_reply.flags, TKT_FLG_INVALID);
     }
-    /* 
-        if (req.kdc_options.(any flag except ENC-TKT-IN-SKEY, RENEW,
-                             and those already processed) then
-                return KRB_ERROR, code KDC_ERR_BADOPTION;
-        endif
-       */
 
-    enc_tkt_reply.times.authtime = header_ticket->enc_part2->times.authtime;
     if (isflagset(request->kdc_options, KDC_OPT_RENEW)) {
        krb5_deltat old_life;
 
-          /* Note that if the endtime has already passed, the ticket would  */
-          /* have been rejected in the initial authentication stage, so     */
-          /* there is no need to check again here                           */
-
-       /* has it completely run out? */
-       if (header_ticket->enc_part2->times.renew_till < kdc_time) {
-           cleanup();
-           return(prepare_error_tgs(request, header_ticket,
-                                    KRB_AP_ERR_TKT_EXPIRED,
-                                    fromstring, response));
-       }    
-
        /* BEWARE of allocation hanging off of ticket & enc_part2, it belongs
           to the caller */
        ticket_reply = *(header_ticket);
@@ -386,9 +367,23 @@ tgt_again:
                    min(server.max_renewable_life,
                        max_renewable_life_for_realm)));
     } else {
-       enc_tkt_reply.times.renew_till = 0; /* XXX */
+       enc_tkt_reply.times.renew_till = 0;
     }
-
+    
+    /*
+     * Set authtime to be the same as header_ticket's
+     */
+    enc_tkt_reply.times.authtime = header_ticket->enc_part2->times.authtime;
+    
+    /*
+     * Propagate the preauthentication flags through to the returned ticket.
+     */
+    if (isflagset(header_ticket->enc_part2->flags, TKT_FLG_PRE_AUTH))
+       setflag(enc_tkt_reply.flags, TKT_FLG_PRE_AUTH);
+
+    if (isflagset(header_ticket->enc_part2->flags, TKT_FLG_HW_AUTH))
+       setflag(enc_tkt_reply.flags, TKT_FLG_HW_AUTH);
+    
     /* starttime is optional, and treated as authtime if not present.
        so we can nuke it if it matches */
     if (enc_tkt_reply.times.starttime == enc_tkt_reply.times.authtime)
@@ -401,6 +396,8 @@ tgt_again:
 
        /* decrypt the authdata in the request */
        if (!valid_etype(request->authorization_data.etype)) {
+           syslog(LOG_INFO, "TGS_REQ: BAD_AUTH_ETYPE: authtime %d, host %s, %s for %s",
+                  authtime, fromstring, cname, sname);
            cleanup();
            return prepare_error_tgs(request, header_ticket,
                                     KDC_ERR_ETYPE_NOSUPP,
@@ -413,6 +410,8 @@ tgt_again:
        scratch.length = request->authorization_data.ciphertext.length;
        if (!(scratch.data =
              malloc(request->authorization_data.ciphertext.length))) {
+           syslog(LOG_INFO, "TGS_REQ: AUTH_NOMEM: authtime %d, host %s, %s for %s",
+                  authtime, fromstring, cname, sname);
            tkt_cleanup();
            cleanup();
            return(ENOMEM);
@@ -420,6 +419,8 @@ tgt_again:
        /* do any necessary key pre-processing */
        if (retval = krb5_process_key(&eblock,
                                      header_ticket->enc_part2->session)) {
+           syslog(LOG_INFO, "TGS_REQ: AUTH_PROCESS_KEY: authtime %d, host %s, %s for %s",
+                  authtime, fromstring, cname, sname);
            free(scratch.data);
            tkt_cleanup();
            cleanup();
@@ -430,6 +431,8 @@ tgt_again:
        if (retval = krb5_decrypt((krb5_pointer) request->authorization_data.ciphertext.data,
                                  (krb5_pointer) scratch.data,
                                  scratch.length, &eblock, 0)) {
+           syslog(LOG_INFO, "TGS_REQ: AUTH_ENCRYPT_FAIL: authtime %d, host %s, %s for %s",
+                  authtime, fromstring, cname, sname);
            (void) krb5_finish_key(&eblock);
            free(scratch.data);
            tkt_cleanup();
@@ -437,6 +440,8 @@ tgt_again:
            return retval;
        }
        if (retval = krb5_finish_key(&eblock)) {
+           syslog(LOG_INFO, "TGS_REQ: FINISH_KEY: authtime %d, host %s, %s for %s",
+                  authtime, fromstring, cname, sname);
            free(scratch.data);
            tkt_cleanup();
            cleanup();
@@ -446,6 +451,8 @@ tgt_again:
        retval = decode_krb5_authdata(&scratch, request->unenc_authdata);
        free(scratch.data);
        if (retval) {
+           syslog(LOG_INFO, "TGS_REQ: DECODE_AUTH: authtime %d, host %s, %s for %s",
+                  authtime, fromstring, cname, sname);
            tkt_cleanup();
            cleanup();
            return retval;
@@ -455,6 +462,8 @@ tgt_again:
            concat_authorization_data(request->unenc_authdata,
                                      header_ticket->enc_part2->authorization_data, 
                                      &enc_tkt_reply.authorization_data)) {
+           syslog(LOG_INFO, "TGS_REQ: CONCAT_AUTH: authtime %d, host %s, %s for %s",
+                  authtime, fromstring, cname, sname);
            tkt_cleanup();
            cleanup();
            return retval;
@@ -477,6 +486,8 @@ tgt_again:
        /* assemble new transited field into allocated storage */
        if (header_ticket->enc_part2->transited.tr_type !=
            KRB5_DOMAIN_X500_COMPRESS) {
+           syslog(LOG_INFO, "TGS_REQ: BAD_TRTYPE: authtime %d, %s for %s",
+                  authtime, fromstring, cname, sname);
            tkt_cleanup();
            cleanup();
            return KRB5KDC_ERR_TRTYPE_NOSUPP;
@@ -491,6 +502,8 @@ tgt_again:
                               header_ticket->server,
                               enc_tkt_reply.client,
                               request->server)) {
+           syslog(LOG_INFO, "TGS_REQ: ADD_TR_FAIL: authtime %d, host %s, %s for %s",
+                  authtime, fromstring, cname, sname);
            tkt_cleanup();
            cleanup();
            return retval;
@@ -500,7 +513,8 @@ tgt_again:
 #undef cleanup
 #define cleanup() { krb5_db_free_principal(&server, 1); \
                   krb5_free_keyblock(session_key); \
-                  if (newtransited) free(enc_tkt_reply.transited.tr_contents.data);}
+                  free(cname); free(sname); \
+          if (newtransited) free(enc_tkt_reply.transited.tr_contents.data); }
 
     ticket_reply.enc_part2 = &enc_tkt_reply;
 
@@ -508,17 +522,11 @@ tgt_again:
        krb5_keyblock *st_sealing_key;
        krb5_kvno st_srv_kvno;
 
-       if (!request->second_ticket ||
-           !request->second_ticket[st_idx]) {
-               cleanup();
-               return(prepare_error_tgs(request, header_ticket,
-                                        KDC_ERR_BADOPTION,
-                                        fromstring,
-                                        response));
-           }
        if (retval = kdc_get_server_key(request->second_ticket[st_idx],
                                        &st_sealing_key,
                                        &st_srv_kvno)) {
+           syslog(LOG_INFO, "TGS_REQ: 2ND_TKT_SERVER: authtime %d, host %s, %s for %s",
+                  authtime, fromstring, cname, sname);
            tkt_cleanup();
            cleanup();
            return retval;
@@ -529,6 +537,8 @@ tgt_again:
                                       request->second_ticket[st_idx]);
        krb5_free_keyblock(st_sealing_key);
        if (retval) {
+           syslog(LOG_INFO, "TGS_REQ: 2ND_TKT_DECRYPT: authtime %d, host %s, %s for %s",
+                  authtime, fromstring, cname, sname);
            tkt_cleanup();
            cleanup();
            return retval;
@@ -536,6 +546,8 @@ tgt_again:
                                        
        if (retval = krb5_encrypt_tkt_part(request->second_ticket[st_idx]->enc_part2->session,
                                           &ticket_reply)) {
+           syslog(LOG_INFO, "TGS_REQ: 2ND_TKT_ENCRYPT: authtime %d, host %s, %s for %s",
+                  authtime, fromstring, cname, sname);
            tkt_cleanup();
            cleanup();
            return retval;
@@ -545,6 +557,8 @@ tgt_again:
        /* convert server.key into a real key (it may be encrypted
           in the database) */
        if (retval = KDB_CONVERT_KEY_OUTOF_DB(&server.key, &encrypting_key)) {
+           syslog(LOG_INFO, "TGS_REQ: CONV_KEY: authtime %d, host %s, %s for %s",
+                  authtime, fromstring, cname, sname);
            tkt_cleanup();
            cleanup();
            return retval;
@@ -556,6 +570,8 @@ tgt_again:
        xfree(encrypting_key.contents);
 
        if (retval) {
+           syslog(LOG_INFO, "TGS_REQ: TKT_ENCRYPT: authtime %d, host %s, %s for %s",
+                  authtime, fromstring, cname, sname);
            tkt_cleanup();
            cleanup();
            return retval;
@@ -582,8 +598,12 @@ tgt_again:
     /* copy the time fields EXCEPT for authtime; its location
        is used for ktime */
     reply_encpart.times = enc_tkt_reply.times;
-    reply_encpart.times.authtime = kdc_time;
+    reply_encpart.times.authtime = header_ticket->enc_part2->times.authtime;
 
+    /* starttime is optional, and treated as authtime if not present.
+       so we can nuke it if it matches */
+    if (enc_tkt_reply.times.starttime == enc_tkt_reply.times.authtime)
+       enc_tkt_reply.times.starttime = 0;
 
     nolrentry.lr_type = KRB5_LRQ_NONE;
     nolrentry.value = 0;
@@ -593,7 +613,7 @@ tgt_again:
     reply_encpart.key_exp = 0;         /* ditto */
     reply_encpart.flags = enc_tkt_reply.flags;
     reply_encpart.server = ticket_reply.server;
-
+    
     /* use the session key in the ticket, unless there's a subsession key
        in the AP_REQ */
 
@@ -602,6 +622,20 @@ tgt_again:
                                 req_authdat->authenticator->subkey :
                                 header_ticket->enc_part2->session,
                                 &reply, response);
+    if (retval) {
+       syslog(LOG_INFO, "TGS_REQ; ENCODE_KDC_REP: authtime %d, host %s, %s for %s",
+              authtime, fromstring, cname, sname);
+    } else {
+       if (is_secondary)
+           syslog(LOG_INFO, "TGS_REQ; ISSUE: authtime %d, host %s, %s for %s",
+                  authtime, fromstring, cname, sname);
+       else
+           syslog(LOG_INFO, "TGS_REQ: ISSUE: authtime %d, host %s, %s for %s",
+                  authtime, fromstring, cname, sname);
+    }
+    free(cname);
+    free(sname);
+
     krb5_free_keyblock(session_key);
     tkt_cleanup();
     memset(ticket_reply.enc_part.ciphertext.data, 0,
index 5748ca8a46909b2f9361a99eed0d547d07a97505..6492e6751dbefae433d207f157242e6aa371aed0 100644 (file)
@@ -178,13 +178,6 @@ krb5_tkt_authent **ret_authdat;
     authdat->ticket = apreq->ticket;
     *ret_authdat = authdat;
 
-    /* Verify that the server principal in authdat->ticket is
-       the ticket granting service.  */
-    if (! krb5_principal_compare (authdat->ticket->server, tgs_server)) {
-       cleanup_apreq();
-       return KRB5KRB_AP_ERR_NOT_US;
-    }
-
     if (isflagset(apreq->ap_options, AP_OPTS_USE_SESSION_KEY) ||
        isflagset(apreq->ap_options, AP_OPTS_MUTUAL_REQUIRED)) {
         cleanup_apreq();
@@ -613,3 +606,378 @@ krb5_principal server;
     }
     return 0;
 }
+
+/*
+ * Routines that validate a AS request; checks a lot of things.  :-)
+ *
+ * Returns a Kerberos protocol error number, which is _not_ the same
+ * as a com_err error number!
+ */
+#define AS_OPTIONS_HANDLED (KDC_OPT_FORWARDABLE | KDC_OPT_PROXIABLE | \
+                            KDC_OPT_ALLOW_POSTDATE | KDC_OPT_POSTDATED | \
+                            KDC_OPT_RENEWABLE | KDC_OPT_RENEWABLE_OK)
+int
+validate_as_request(request, client, server, kdc_time, status)
+register krb5_kdc_req *request;
+krb5_db_entry client;
+krb5_db_entry server;
+krb5_timestamp kdc_time;
+char   **status;
+{
+    int                errcode;
+    
+    /*
+     * If an illegal option is set, complain.
+     */
+    if (request->kdc_options & ~(AS_OPTIONS_HANDLED)) {
+       *status = "INVALID AS OPTIONS";
+       return KDC_ERR_BADOPTION;
+    }
+
+     /* An AS request must include the addresses field */
+    if (request->addresses == 0) {
+       *status = "NO ADDRESS";
+       return KRB_AP_ERR_BADADDR;
+    }
+    
+    /* The client's password must not be expired */
+    if (client.pw_expiration && client.pw_expiration < kdc_time) {
+       *status = "CLIENT KEY EXPIRED";
+#ifdef KRBCONF_VAGUE_ERRORS
+       return(KRB_ERR_GENERIC);
+#else
+       return(KDC_ERR_KEY_EXP);
+#endif
+    }
+
+    /* The client must not be expired */
+    if (client.expiration && client.expiration < kdc_time) {
+       *status = "CLIENT EXPIRED";
+#ifdef KRBCONF_VAGUE_ERRORS
+       return(KRB_ERR_GENERIC);
+#else
+       return(KDC_ERR_NAME_EXP);
+#endif
+    }
+
+    /* The server must not be expired */
+    if (server.expiration && server.expiration < kdc_time) {
+       *status = "SERVICE EXPIRED";
+           return(KDC_ERR_SERVICE_EXP);
+    }
+
+    /*
+     * If the client requires password changing, then only allow the 
+     * pwchange service.
+     */
+    if (isflagset(client.attributes, KRB5_KDB_REQUIRES_PWCHANGE) &&
+       !isflagset(server.attributes, KRB5_KDB_PWCHANGE_SERVICE)) {
+       *status = "REQUIRED PWCHANGE";
+       return(KDC_ERR_KEY_EXP);
+    }
+
+    /* Client and server must allow postdating tickets */
+    if ((isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE) ||
+        isflagset(request->kdc_options, KDC_OPT_POSTDATED)) && 
+       (isflagset(client.attributes, KRB5_KDB_DISALLOW_POSTDATED) ||
+        isflagset(server.attributes, KRB5_KDB_DISALLOW_POSTDATED))) {
+       *status = "POSTDATE NOT ALLOWED";
+       return(KDC_ERR_CANNOT_POSTDATE);
+    }
+    
+    /* Client and server must allow forwardable tickets */
+    if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE) &&
+       (isflagset(client.attributes, KRB5_KDB_DISALLOW_FORWARDABLE) ||
+        isflagset(server.attributes, KRB5_KDB_DISALLOW_FORWARDABLE))) {
+       *status = "FORWARDABLE NOT ALLOWED";
+       return(KDC_ERR_POLICY);
+    }
+    
+    /* Client and server must allow renewable tickets */
+    if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE) &&
+       (isflagset(client.attributes, KRB5_KDB_DISALLOW_RENEWABLE) ||
+        isflagset(server.attributes, KRB5_KDB_DISALLOW_RENEWABLE))) {
+       *status = "RENEWABLE NOT ALLOWED";
+       return(KDC_ERR_POLICY);
+    }
+    
+    /* Client and server must allow proxiable tickets */
+    if (isflagset(request->kdc_options, KDC_OPT_PROXIABLE) &&
+       (isflagset(client.attributes, KRB5_KDB_DISALLOW_PROXIABLE) ||
+        isflagset(server.attributes, KRB5_KDB_DISALLOW_PROXIABLE))) {
+       *status = "PROXIABLE NOT ALLOWED";
+       return(KDC_ERR_POLICY);
+    }
+    
+    /* Check to see if client is locked out */
+    if (isflagset(client.attributes, KRB5_KDB_DISALLOW_ALL_TIX)) {
+       *status = "CLIENT LOCKED OUT";
+       return(KDC_ERR_C_PRINCIPAL_UNKNOWN);
+    }
+
+    /* Check to see if server is locked out */
+    if (isflagset(server.attributes, KRB5_KDB_DISALLOW_ALL_TIX)) {
+       *status = "SERVICE LOCKED OUT";
+       return(KDC_ERR_S_PRINCIPAL_UNKNOWN);
+    }
+       
+    /* Check to see if server is allowed to be a service */
+    if (isflagset(server.attributes, KRB5_KDB_DISALLOW_SVR)) {
+       *status = "SERVICE NOT ALLOWED";
+       return(KDC_ERR_S_PRINCIPAL_UNKNOWN);
+    }
+
+    /* Check to see if preauthentication is required */
+    if (isflagset(client.attributes, KRB5_KDB_REQUIRES_PRE_AUTH) &&
+        !request->padata) {
+       *status = "MISSING PRE_AUTH";
+#ifdef KRBCONF_VAGUE_ERRORS
+       return KRB_ERR_GENERIC;
+#else
+       return KDC_PREAUTH_FAILED;
+#endif
+    }
+
+    /*
+     * Check against local policy
+     */
+    errcode = against_local_policy_as(request, server, client,
+                                     kdc_time, status); 
+    if (errcode)
+       return errcode;
+
+    return 0;
+}
+
+/*
+ * Routines that validate a TGS request; checks a lot of things.  :-)
+ *
+ * Returns a Kerberos protocol error number, which is _not_ the same
+ * as a com_err error number!
+ */
+#define TGS_OPTIONS_HANDLED (KDC_OPT_FORWARDABLE | KDC_OPT_FORWARDED | \
+                            KDC_OPT_PROXIABLE | KDC_OPT_PROXY | \
+                            KDC_OPT_ALLOW_POSTDATE | KDC_OPT_POSTDATED | \
+                            KDC_OPT_RENEWABLE | KDC_OPT_RENEWABLE_OK | \
+                            KDC_OPT_ENC_TKT_IN_SKEY | KDC_OPT_RENEW | \
+                            KDC_OPT_VALIDATE)
+
+#define TGS_SPECIAL_OPTS       (KDC_OPT_FORWARDED | KDC_OPT_PROXY | \
+                                KDC_OPT_RENEW | KDC_OPT_VALIDATE)
+
+int
+validate_tgs_request(request, server, ticket, kdc_time, status)
+register krb5_kdc_req *request;
+krb5_db_entry server;
+krb5_ticket *ticket;
+krb5_timestamp kdc_time;
+char **status;
+{
+    int                errcode;
+    int                st_idx = 0;
+
+    /*
+     * If an illegal option is set, complain.
+     */
+    if (request->kdc_options & ~(TGS_OPTIONS_HANDLED)) {
+       *status = "INVALID TGS OPTIONS";
+       return KDC_ERR_BADOPTION;
+    }
+    
+    /* Check to see if server has expired */
+    if (server.expiration && server.expiration < kdc_time) {
+       *status = "SERVICE EXPIRED";
+           return(KDC_ERR_SERVICE_EXP);
+    }
+
+    /*
+     * Verify that the server principal in authdat->ticket is correct
+     * (either the ticket granting service or the service we're
+     * looking for)
+     */
+
+    if (request->kdc_options & TGS_SPECIAL_OPTS) {
+       /*
+        * This is one of the KDC options which allow a non-TGT ticket
+        * for the purposes of renewing, forwarding, proxying, or
+        * validating it.
+        *
+        * We just make sure the service in the ticket matches service
+        * the user is request.
+        */
+       if (!krb5_principal_compare(ticket->server,
+                                   request->server)) {
+           *status = "SERVER MISMATCH";
+           return KRB5KDC_SERVER_NOMATCH;
+       }
+    } else {
+       /*
+        * This is a normal TGS request; the ticket must belong to the
+        * TGS server
+        */
+       if (!krb5_principal_compare(ticket->server, tgs_server)) {
+           *status = "NOT TGS TICKET";
+           return KRB5KRB_AP_ERR_NOT_US;
+       }
+       
+       /* Server must allow TGS based issuances */
+       if (isflagset(server.attributes, KRB5_KDB_DISALLOW_TGT_BASED)) {
+           *status = "TGT BASED NOT ALLOWED";
+           return(KDC_ERR_POLICY);
+       }
+    }
+    
+    /* TGS must be forwardable to get forwarded or forwardable ticket */
+    if ((isflagset(request->kdc_options, KDC_OPT_FORWARDED) ||
+        isflagset(request->kdc_options, KDC_OPT_FORWARDABLE)) &&
+       !isflagset(ticket->enc_part2->flags, TKT_FLG_FORWARDABLE)) {
+       *status = "TGT NOT FORWARDABLE";
+
+       return KDC_ERR_BADOPTION;
+    }
+
+    /* TGS must be proxiable to get proxiable ticket */    
+    if ((isflagset(request->kdc_options, KDC_OPT_PROXY) ||
+        isflagset(request->kdc_options, KDC_OPT_PROXIABLE)) &&
+       !isflagset(ticket->enc_part2->flags, TKT_FLG_PROXIABLE)) {
+       *status = "TGT NOT PROXIABLE";
+       return KDC_ERR_BADOPTION;
+    }
+
+    /* TGS must allow postdating to get postdated ticket */
+    if ((isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE) ||
+         isflagset(request->kdc_options, KDC_OPT_POSTDATED)) &&
+       !isflagset(ticket->enc_part2->flags, TKT_FLG_MAY_POSTDATE)) {
+       *status = "TGT NOT POSTDATABLE";
+       return KDC_ERR_BADOPTION;
+    }
+
+    /* can only validate invalid tix */
+    if (isflagset(request->kdc_options, KDC_OPT_VALIDATE) &&
+       !isflagset(ticket->enc_part2->flags, TKT_FLG_INVALID)) {
+       *status = "VALIDATE VALID TICKET";
+       return KDC_ERR_BADOPTION;
+    }
+
+    /* can only renew renewable tix */
+    if ((isflagset(request->kdc_options, KDC_OPT_RENEW) ||
+         isflagset(request->kdc_options, KDC_OPT_RENEWABLE)) &&
+       !isflagset(ticket->enc_part2->flags, TKT_FLG_RENEWABLE)) {
+       *status = "TICKET NOT RENEWABLE";
+       return KDC_ERR_BADOPTION;
+    }
+
+    /* can not proxy ticket granting tickets */
+    if (isflagset(request->kdc_options, KDC_OPT_PROXY) &&
+       (!request->server->data ||
+        request->server->data[0].length != 6 ||
+        memcmp(request->server->data[0].data, "krbtgt", 6))) {
+       *status = "CAN'T PROXY TGT";
+       return KDC_ERR_BADOPTION;
+    }
+    
+    /* Server must allow forwardable tickets */
+    if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE) &&
+       isflagset(server.attributes, KRB5_KDB_DISALLOW_FORWARDABLE)) {
+       *status = "NON-FORWARDABLE TICKET";
+       return(KDC_ERR_POLICY);
+    }
+    
+    /* Server must allow renewable tickets */
+    if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE) &&
+       isflagset(server.attributes, KRB5_KDB_DISALLOW_RENEWABLE)) {
+       *status = "NON-RENEWABLE TICKET";
+       return(KDC_ERR_POLICY);
+    }
+    
+    /* Server must allow proxiable tickets */
+    if (isflagset(request->kdc_options, KDC_OPT_PROXIABLE) &&
+       isflagset(server.attributes, KRB5_KDB_DISALLOW_PROXIABLE)) {
+       *status = "NON-PROXIABLE TICKET";
+       return(KDC_ERR_POLICY);
+    }
+    
+    /* Server must allow postdated tickets */
+    if (isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE) &&
+       isflagset(server.attributes, KRB5_KDB_DISALLOW_POSTDATED)) {
+       *status = "NON-POSTDATABLE TICKET";
+       return(KDC_ERR_CANNOT_POSTDATE);
+    }
+    
+    /* Server must allow DUP SKEY requests */
+    if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY) &&
+       isflagset(server.attributes, KRB5_KDB_DISALLOW_DUP_SKEY)) {
+       *status = "DUP_SKEY DISALLOED";
+       return(KDC_ERR_POLICY);
+    }
+
+    /* Server must not be locked out */
+    if (isflagset(server.attributes, KRB5_KDB_DISALLOW_ALL_TIX)) {
+       *status = "SERVER LOCKED OUT";
+       return(KDC_ERR_S_PRINCIPAL_UNKNOWN);
+    }
+       
+    /* Server must be allowed to be a service */
+    if (isflagset(server.attributes, KRB5_KDB_DISALLOW_SVR)) {
+       *status = "SERVER NOT ALLOWED";
+       return(KDC_ERR_S_PRINCIPAL_UNKNOWN);
+    }
+
+    /* Check the hot list */
+    if (check_hot_list(ticket)) {
+       *status = "HOT_LIST";
+       return(KRB_AP_ERR_REPEAT);
+    }
+    
+    /* Check the start time vs. the KDC time */
+    if (isflagset(request->kdc_options, KDC_OPT_VALIDATE)) {
+       if (ticket->enc_part2->times.starttime > kdc_time) {
+           *status = "NOT_YET_VALID";
+           return(KRB_AP_ERR_TKT_NYV);
+       }
+    }
+    
+    /*
+     * Check the renew_till time.  The endtime was already
+     * been checked in the initial authentication check.
+     */
+    if (isflagset(request->kdc_options, KDC_OPT_RENEW) &&
+       (ticket->enc_part2->times.renew_till < kdc_time)) {
+       *status = "TKT_EXPIRED";
+       return(KRB_AP_ERR_TKT_EXPIRED);
+    }
+    
+    /* Make sure second ticket was provided for ENC_TKT_IN_SKEY */
+    if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY)) {
+       if (!request->second_ticket ||
+           !request->second_ticket[st_idx]) {
+           *status = "NO_2ND_TKT";
+           return(KDC_ERR_BADOPTION);
+       }
+       st_idx++;
+    }
+
+    /* Check for hardware preauthentication */
+    if (isflagset(server.attributes, KRB5_KDB_REQUIRES_HW_AUTH) &&
+       !isflagset(ticket->enc_part2->flags,TKT_FLG_HW_AUTH)) {
+       *status = "NO HW PREAUTH";
+       return KRB_ERR_GENERIC;
+    }
+
+    /* Check for any kind of preauthentication */
+    if (isflagset(server.attributes, KRB5_KDB_REQUIRES_PRE_AUTH) &&
+       !isflagset(ticket->enc_part2->flags, TKT_FLG_PRE_AUTH)) {
+       *status = "NO PREAUTH";
+       return KRB_ERR_GENERIC;
+    }
+    
+    /*
+     * Check local policy
+     */
+    errcode = against_local_policy_tgs(request, server, ticket, status);
+    if (errcode)
+       return errcode;
+    
+    
+    return 0;
+}
index 4010c542085cd1f276b787f81c575cb897555ceb..79229827a4b4212067238f8b73c741bda084ba1e 100644 (file)
@@ -37,62 +37,45 @@ static char rcsid_policy_c[] =
 
 #include "kdc_util.h"
 
-/*ARGSUSED*/
-krb5_boolean
-against_postdate_policy(fromtime)
-krb5_timestamp fromtime;
+int
+against_local_policy_as(request, client, server, kdc_time, status)
+register krb5_kdc_req *request;
+krb5_db_entry client;
+krb5_db_entry server;
+krb5_timestamp kdc_time;
+char   **status;
 {
-    return FALSE;
+    return 0;                  /* not against policy */
 }
 
-krb5_boolean
-against_flag_policy_as(request)
-const register krb5_kdc_req *request;
+/*
+ * This is where local policy restrictions for the TGS should placed.
+ */
+krb5_error_code
+against_local_policy_tgs(request, server, ticket, status)
+register krb5_kdc_req *request;
+krb5_db_entry server;
+krb5_ticket *ticket;
+char **status;
 {
-    if (isflagset(request->kdc_options, KDC_OPT_FORWARDED) ||
-       isflagset(request->kdc_options, KDC_OPT_PROXY) ||
-       isflagset(request->kdc_options, KDC_OPT_RENEW) ||
-       isflagset(request->kdc_options, KDC_OPT_VALIDATE) ||
-       isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY))
-       return TRUE;                    /* against policy */
-
-    return FALSE;                      /* not against policy */
+#ifdef 0
+    /*
+     * For example, if your site wants to disallow ticket forwarding,
+     * you might do something like this:
+     */
+    
+    if (isflagset(request->kdc_options, KDC_OPT_FORWARDED)) {
+       *status = "FORWARD POLICY";
+       return KRB5KDC_ERR_POLICY;
+    }
+#endif
+    
+    return 0;                          /* not against policy */
 }
 
-krb5_boolean
-against_flag_policy_tgs(request, ticket)
-const register krb5_kdc_req *request;
-const register krb5_ticket *ticket;
-{
 
-    if (((isflagset(request->kdc_options, KDC_OPT_FORWARDED) ||
-         isflagset(request->kdc_options, KDC_OPT_FORWARDABLE)) &&
-        !isflagset(ticket->enc_part2->flags,
-               TKT_FLG_FORWARDABLE)) || /* TGS must be forwardable to get
-                                           forwarded or forwardable ticket */
 
-       ((isflagset(request->kdc_options, KDC_OPT_PROXY) ||
-         isflagset(request->kdc_options, KDC_OPT_PROXIABLE)) &&
-        !isflagset(ticket->enc_part2->flags,
-               TKT_FLG_PROXIABLE)) ||  /* TGS must be proxiable to get
-                                          proxiable ticket */
 
-       ((isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE) ||
-         isflagset(request->kdc_options, KDC_OPT_POSTDATED)) &&
-        !isflagset(ticket->enc_part2->flags,
-               TKT_FLG_MAY_POSTDATE)) || /* TGS must allow postdating to get
-                                          postdated ticket */
-        
-       (isflagset(request->kdc_options, KDC_OPT_VALIDATE) &&
-        !isflagset(ticket->enc_part2->flags,
-               TKT_FLG_INVALID)) ||    /* can only validate invalid tix */
 
-       ((isflagset(request->kdc_options, KDC_OPT_RENEW) ||
-         isflagset(request->kdc_options, KDC_OPT_RENEWABLE)) &&
-        !isflagset(ticket->enc_part2->flags,
-               TKT_FLG_RENEWABLE)))    /* can only renew renewable tix */
 
-       return TRUE;                    /* against policy */
 
-    return FALSE;                      /* XXX not against policy */
-}