Obtaining Kerberos Tickets without the Microsoft PAC

Douglas E. Engert deengert at anl.gov
Thu Aug 21 14:53:29 EDT 2003


The Microsoft AD issues Kerberos tickets with an authorization data 
called a PAC.  I would like to request Microsoft make a change in this area
as stated below. The PAC and its use has been documented in: 

   Kerberos working group                                      John Brezak 
   Internet Draft                                                Microsoft 
   Document: draft-brezak-win2k-krb-authz-01.txt                           
   Category: Informational                                   October, 2002 
 
 
   Utilizing the Windows 2000 Authorization Data in Kerberos Tickets for 
                      Access Control to Resources 


The PAC is large, and can easily increase the size of a ticket more then 500%.
Tickets without the PAC are about 240 bytes. With a simple PAC, they may be 
1,200 bytes. With Windows 2003, the max size of the ticket was increased 
from 8,000 to 12,000 bytes! (Someone must have run into this limit!!)

This increase in size can cause problems with older applications. where
a ticket might be used with UDP or RPCs. 

The draft describes in its section 6, a PA-DATA element that can be used 
to request a ticket without a PAC. But unfortunately it has limited value 
in my option.

The PA-DATA element can only be used with an AS-REQ, and not a TGS-REQ.
(Some tests confirm that W2K works this way.) This means that the pa-data must 
be requested at the time of the initial TGT when the password is entered, and
all subsequent ticket will have or not have a PAC. This restricts the use of 
the forwarded tickets, when some services may need a PAC, and others don't.   

The Microsoft runas.exe for example has the /netonly option. If a client sets 
the SEC_WINNT_AUTH_IDENTITY_ONLY flag in the SEC_WINNT_AUTH_IDENTITY_EX  
structure when calling the SSPI AcquireCredentialsHandle, the service ticket
obtained will not have a PAC. The best I can tell, it appears to actually 
use the stashed password from login to get a new TGT using a AS-REQ. 

If they have not done so already, I would like to request Microsoft to do 
one or both of the following:

 o Honor the PA-PAC-REQUEST when sent in with an TGT-REQ. This would allow
   the client to authenticate only once, but request a service ticket with or
   without the PAC. Having to use the stashed password, is not acceptable,
   and can not be used in situations where the password in not available.
   (This would require a change to the KDC which should be backward compatable.) 

 o Add a flag in the registry for each service, indicating if a PAC should
   be added to tickets for this service. Service either will use
   the PAC or ignore it. If you know a service does not understand the PAC,
   don't add it! This is especially true for some services where the size 
   of the ticket is a problem. (This would require a change to the KDC, and
   the registry which are also backwards compatible.)     

I would prefer the second choice, as it should not be up to the client to
decide if the server needs or wants a PAC. It is really an attribute of the
server which the KDC could know. 
          
Attached are some mods to the MIT krb5-1.3.1 kinit.c and the library
that will send the KERB-PA-PAC-REQUEST in the AS-REQ if a -m is added
to the kinit command. This works to a W2K KDC, but since the MIT KDC 
does not understand this pa-data type, it gets rejected. 

A simple test of getting a TGT and a service ticket for afs/cell at realm show
the size of the ticket cache with a PAC was 2,637 bytes, and without the PACs
(both the TGT and the service ticket have a copy) was 769 bytes. The actual 
size of the ticket for use by AFS dropped from 1,176 to 242 bytes.        
 
So without these changes or major changes to AFS it is not practical to use 
the Microsoft KDC to directly issue a ticket that can be used for an AFS token.
An intermediate conversion server, such as krb524d or gssklogd is needed
to decrypt the ticket, and discard the unused PAC.  



Here are the mods to krb5-1.3.1 used for testing. You need to compile with 
the -DNOMSPAC flag, then use the kinit -m option. 
    

*** ./include/,krb5.hin Mon Jul 21 13:43:37 2003
--- ./include/krb5.hin  Wed Aug 20 15:46:45 2003
***************
*** 876,881 ****
--- 876,884 ----
  #define KRB5_PADATA_SAM_CHALLENGE_2   30 /* draft challenge system, updated */
  #define KRB5_PADATA_SAM_RESPONSE_2    31 /* draft challenge system, updated */
      
+ #ifdef NOMSPAC
+ #define KRB5_PADATA_PAC_REQUEST               128
+ #endif
  #define       KRB5_SAM_USE_SAD_AS_KEY         0x80000000
  #define       KRB5_SAM_SEND_ENCRYPTED_SAD     0x40000000
  #define       KRB5_SAM_MUST_PK_ENCRYPT_SAD    0x20000000 /* currently must be zero */
***************
*** 2414,2419 ****
--- 2417,2425 ----
  #define KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST  0x0020
  #define KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST  0x0040
  #define KRB5_GET_INIT_CREDS_OPT_SALT          0x0080
+ #ifdef NOMSPAC
+ #define KRB5_GET_INIT_CREDS_OPT_PA_PAC_REQUEST 0x0100 
+ #endif
  
  
  void KRB5_CALLCONV
***************
*** 2461,2466 ****
--- 2467,2479 ----
  krb5_get_init_creds_opt_set_salt
  (krb5_get_init_creds_opt *opt,
                krb5_data *salt);
+ 
+ #ifdef NOMSPAC
+ void KRB5_CALLCONV
+ krb5_get_init_creds_opt_set_pa_pac_request
+ (krb5_get_init_creds_opt *opt);
+ #endif
+ 
  
  krb5_error_code KRB5_CALLCONV
  krb5_get_init_creds_password
*** ./clients/kinit/,kinit.c    Thu Aug 14 10:46:02 2003
--- ./clients/kinit/kinit.c     Wed Aug 20 15:56:39 2003
***************
*** 141,146 ****
--- 142,150 ----
      char* k4_cache_name;
  
      action_type action;
+ #ifdef NOMSPAC
+     int nomspac;
+ #endif
  };
  
  struct k5_data
***************
*** 211,220 ****
--- 215,226 ----
            USAGE_BREAK_LONG
            "[-A" USAGE_LONG_ADDRESSES "] "
            USAGE_BREAK
+           "[-m] "
            "[-v] [-R] "
            "[-k [-t keytab_file]] "
            USAGE_BREAK
            "[-c cachename] "
            "[-S service_name] [principal]"
            "\n\n", 
            progname);
***************
*** 257,266 ****
--- 263,276 ----
      ULINE("\t", "-p proxiable",                 OPTTYPE_KRB5);
      ULINE("\t", "-P not proxiable",             OPTTYPE_KRB5);
      ULINE("\t", "-A do not include addresses",  OPTTYPE_KRB5);
+ #ifdef NOMSPAC
+     ULINE("\t", "-m No PAC in ticket",                        OPTTYPE_KRB5);
+ #endif
      ULINE("\t", "-v validate",                  OPTTYPE_KRB5);
      ULINE("\t", "-R renew",                     OPTTYPE_BOTH);
      ULINE("\t", "-k use keytab",                OPTTYPE_BOTH);
      ULINE("\t", "-t filename of keytab to use", OPTTYPE_BOTH);
      ULINE("\t", "-c Kerberos 5 cache name",     OPTTYPE_KRB5);
      /* This options is not yet available: */
      /* ULINE("\t", "-C Kerberos 4 cache name",     OPTTYPE_KRB4); */
***************
*** 281,289 ****
      int use_k5 = 0;
      int i;
  
!     while ((i = GETOPT(argc, argv, "r:fpFP54AVl:s:c:kt:RS:v"))
           != -1) {
        switch (i) {
        case 'V':
            opts->verbose = 1;
            break;
--- 291,304 ----
      int use_k5 = 0;
      int i;
  
!     while ((i = GETOPT(argc, argv, "r:fpFP54AVl:s:c:kt:RS:vm"))
           != -1) {
        switch (i) {
+ #ifdef NOMSPAC
+       case 'm':
+           opts->nomspac = 1;
+           break;
+ #endif
        case 'V':
            opts->verbose = 1;
            break;
***************
*** 749,754 ****
--- 769,778 ----
        initialized.
      */
  
+ #ifdef NOMSPAC
+     if (opts->nomspac) 
+     krb5_get_init_creds_opt_set_pa_pac_request(&options);
+ #endif
      if (opts->lifetime)
        krb5_get_init_creds_opt_set_tkt_life(&options, opts->lifetime);
      if (opts->rlife)
*** ./lib/krb5/krb/,gic_opt.c   Mon Sep  2 20:13:46 2002
--- ./lib/krb5/krb/gic_opt.c    Wed Aug 20 15:29:41 2003
***************
*** 63,65 ****
--- 63,73 ----
     opt->flags |= KRB5_GET_INIT_CREDS_OPT_SALT;
     opt->salt = salt;
  }
+ 
+ #ifdef NOMSPAC
+ void KRB5_CALLCONV
+ krb5_get_init_creds_opt_set_pa_pac_request(krb5_get_init_creds_opt *opt)
+ {
+       opt->flags |= KRB5_GET_INIT_CREDS_OPT_PA_PAC_REQUEST;
+ }
+ #endif
*** ./lib/krb5/krb/,get_in_tkt.c        Fri Jun  6 17:02:01 2003
--- ./lib/krb5/krb/get_in_tkt.c Wed Aug 20 16:10:06 2003
***************
*** 78,83 ****
--- 78,147 ----
  static krb5_error_code make_preauth_list (krb5_context, 
                                                    krb5_preauthtype *,
                                                    int, krb5_pa_data ***);
+ #ifdef NOMSPAC
+       /* Add the Microsoft PA_PAC_REQUEST to the AS
+        * request. Since this needs to be sent on each AS_REQ
+        * the krb5_preauth routines can not be used, as they
+        * add a list the first time, but then only respond to the
+        * pa-data in the error response. 
+        * See draft-brezak-win2k-krb-authz-01.txt section 6
+        * Also see MSDN for SEC_WINNT_AUTH_IDENTITY_ONLY 
+        */
+ static krb5_error_code add_ms_pac_req(krb5_context context,
+               krb5_pa_data *** padata)
+ {
+       int i;
+       /* ASN1  for: 
+        * KERB-PA-PAC-REQUEST ::= SEQUENCE {
+        *    include-pac[0] BOOLEAN
+        * } 
+        * where the value is false
+        */
+       static krb5_octet booldata[] = {"\x30\x05\xa0\x03\x01\x01\x00"};
+       void * tmp;
+       krb5_pa_data * pa;
+       krb5_pa_data * * new_padata;
+       
+       if ((tmp = (void *)malloc(sizeof(booldata)-1)) == NULL) 
+               return(ENOMEM);
+       memcpy(tmp, booldata, sizeof(booldata)-1);
+ 
+       if ((pa = (krb5_pa_data *)malloc(sizeof(krb5_pa_data))) == NULL) {
+               krb5_xfree(tmp);
+               return(ENOMEM);
+       }
+ 
+       pa->magic = KV5M_PA_DATA;
+       pa->pa_type = KRB5_PADATA_PAC_REQUEST;
+       pa->length = sizeof(booldata)-1;
+       pa->contents = tmp;
+ 
+       if (*padata == NULL) {
+               new_padata = (krb5_pa_data **)
+                       malloc(2 * sizeof(krb5_pa_data *));
+               i = 0;
+       } else {
+       for (i=0; (*padata)[i]; i++); /* count */
+ 
+               new_padata = *padata;
+       new_padata = (krb5_pa_data **)
+               realloc(new_padata, (i+2) * sizeof(krb5_pa_data *));
+       }
+       if (new_padata == NULL) {
+               krb5_xfree(pa->contents);
+               krb5_xfree(pa);
+               return(ENOMEM);
+     }
+ 
+       new_padata[i++] = pa;
+       new_padata[i] = NULL;
+ 
+       *padata = new_padata;
+ 
+       return(0);
+ }
+ #endif
+ 
  /*
   * This function sends a request to the KDC, and gets back a response;
   * the response is parsed into ret_err_reply or ret_as_reply if the
***************
*** 951,956 ****
--- 1015,1029 ----
                                   prompter_data, gak_fct, gak_data)))
            goto cleanup;
  
+ #ifdef NOMSPAC
+       if (options && (options->flags & 
+                       KRB5_GET_INIT_CREDS_OPT_PA_PAC_REQUEST)) {
+               if (ret = add_ms_pac_req(context, &request.padata)) {
+                       goto cleanup;
+               }
+       }
+ #endif
+ 
        if (padata) {
            krb5_free_pa_data(context, padata);
            padata = 0;
***************
*** 996,1001 ****
--- 1069,1082 ----
                               &salt, &s2kparams, &etype, &as_key, prompter,
                               prompter_data, gak_fct, gak_data)))
        goto cleanup;
+ #ifdef NOMSPAC
+       if (options && (options->flags & 
+                       KRB5_GET_INIT_CREDS_OPT_PA_PAC_REQUEST)) {
+               if (ret = add_ms_pac_req(context, &padata)) {
+                       goto cleanup;
+               }
+       }
+ #endif
  
      /* XXX if there's padata on output, something is wrong, but it's
         not obviously an error */



-- 

 Douglas E. Engert  <DEEngert at anl.gov>
 Argonne National Laboratory
 9700 South Cass Avenue
 Argonne, Illinois  60439 
 (630) 252-5444


More information about the krbdev mailing list