krb5_wrap: Move all ads function to the end
authorAndreas Schneider <asn@samba.org>
Fri, 26 Aug 2016 14:19:42 +0000 (16:19 +0200)
committerAndrew Bartlett <abartlet@samba.org>
Wed, 31 Aug 2016 18:59:13 +0000 (20:59 +0200)
Signed-off-by: Andreas Schneider <asn@samba.org>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
lib/krb5_wrap/krb5_samba.c

index 3ee940a..e6fa4da 100644 (file)
@@ -412,121 +412,6 @@ bool unwrap_edata_ntstatus(TALLOC_CTX *mem_ctx,
 }
 
 
-static bool ads_cleanup_expired_creds(krb5_context context,
-                                     krb5_ccache  ccache,
-                                     krb5_creds  *credsp)
-{
-       krb5_error_code retval;
-       const char *cc_type = krb5_cc_get_type(context, ccache);
-
-       DEBUG(3, ("ads_cleanup_expired_creds: Ticket in ccache[%s:%s] expiration %s\n",
-                 cc_type, krb5_cc_get_name(context, ccache),
-                 http_timestring(talloc_tos(), credsp->times.endtime)));
-
-       /* we will probably need new tickets if the current ones
-          will expire within 10 seconds.
-       */
-       if (credsp->times.endtime >= (time(NULL) + 10))
-               return false;
-
-       /* heimdal won't remove creds from a file ccache, and
-          perhaps we shouldn't anyway, since internally we
-          use memory ccaches, and a FILE one probably means that
-          we're using creds obtained outside of our exectuable
-       */
-       if (strequal(cc_type, "FILE")) {
-               DEBUG(5, ("ads_cleanup_expired_creds: We do not remove creds from a %s ccache\n", cc_type));
-               return false;
-       }
-
-       retval = krb5_cc_remove_cred(context, ccache, 0, credsp);
-       if (retval) {
-               DEBUG(1, ("ads_cleanup_expired_creds: krb5_cc_remove_cred failed, err %s\n",
-                         error_message(retval)));
-               /* If we have an error in this, we want to display it,
-                  but continue as though we deleted it */
-       }
-       return true;
-}
-
-/* Allocate and setup the auth context into the state we need. */
-
-static krb5_error_code setup_auth_context(krb5_context context,
-                       krb5_auth_context *auth_context)
-{
-       krb5_error_code retval;
-
-       retval = krb5_auth_con_init(context, auth_context );
-       if (retval) {
-               DEBUG(1,("krb5_auth_con_init failed (%s)\n",
-                       error_message(retval)));
-               return retval;
-       }
-
-       /* Ensure this is an addressless ticket. */
-       retval = krb5_auth_con_setaddrs(context, *auth_context, NULL, NULL);
-       if (retval) {
-               DEBUG(1,("krb5_auth_con_setaddrs failed (%s)\n",
-                       error_message(retval)));
-       }
-
-       return retval;
-}
-
-#if defined(TKT_FLG_OK_AS_DELEGATE ) && defined(HAVE_KRB5_AUTH_CON_SETUSERUSERKEY) && defined(KRB5_AUTH_CONTEXT_USE_SUBKEY) && defined(HAVE_KRB5_AUTH_CON_SET_REQ_CKSUMTYPE)
-static krb5_error_code create_gss_checksum(krb5_data *in_data, /* [inout] */
-                                               uint32_t gss_flags)
-{
-       unsigned int orig_length = in_data->length;
-       unsigned int base_cksum_size = GSSAPI_CHECKSUM_SIZE;
-       char *gss_cksum = NULL;
-
-       if (orig_length) {
-               /* Extra length field for delgated ticket. */
-               base_cksum_size += 4;
-       }
-
-       if ((unsigned int)base_cksum_size + orig_length <
-                       (unsigned int)base_cksum_size) {
-                return EINVAL;
-        }
-
-       gss_cksum = (char *)SMB_MALLOC(base_cksum_size + orig_length);
-       if (gss_cksum == NULL) {
-               return ENOMEM;
-        }
-
-       memset(gss_cksum, '\0', base_cksum_size + orig_length);
-       SIVAL(gss_cksum, 0, GSSAPI_BNDLENGTH);
-
-       /*
-        * GSS_C_NO_CHANNEL_BINDINGS means 16 zero bytes.
-        * This matches the behavior of heimdal and mit.
-        *
-        * And it is needed to work against some closed source
-        * SMB servers.
-        *
-        * See bug #7883
-        */
-       memset(&gss_cksum[4], 0x00, GSSAPI_BNDLENGTH);
-
-       SIVAL(gss_cksum, 20, gss_flags);
-
-       if (orig_length) {
-               SSVAL(gss_cksum, 24, 1); /* The Delegation Option identifier */
-               SSVAL(gss_cksum, 26, orig_length);
-               /* Copy the kerberos KRB_CRED data */
-               memcpy(gss_cksum + 28, in_data->data, orig_length);
-               free(in_data->data);
-               in_data->data = NULL;
-               in_data->length = 0;
-       }
-       in_data->data = gss_cksum;
-       in_data->length = base_cksum_size + orig_length;
-       return 0;
-}
-#endif
-
 /**************************************************************
  krb5_parse_name that takes a UNIX charset.
 **************************************************************/
@@ -604,682 +489,391 @@ bool smb_krb5_principal_compare_any_realm(krb5_context context,
        return krb5_principal_compare_any_realm(context, princ1, princ2);
 }
 
-/*
-  we can't use krb5_mk_req because w2k wants the service to be in a particular format
-*/
-static krb5_error_code ads_krb5_mk_req(krb5_context context,
-                                      krb5_auth_context *auth_context,
-                                      const krb5_flags ap_req_options,
-                                      const char *principal,
-                                      krb5_ccache ccache,
-                                      krb5_data *outbuf,
-                                      time_t *expire_time,
-                                      const char *impersonate_princ_s)
+/**
+ * @brief Free the contents of a krb5_data structure and zero the data field.
+ *
+ * @param[in]  context  The krb5 context
+ *
+ * @param[in]  pdata    The data structure to free contents of
+ *
+ * This function frees the contents, not the structure itself.
+ */
+void smb_krb5_free_data_contents(krb5_context context, krb5_data *pdata)
 {
-       krb5_error_code           retval;
-       krb5_principal    server;
-       krb5_principal impersonate_princ = NULL;
-       krb5_creds              * credsp;
-       krb5_creds                creds;
-       krb5_data in_data;
-       bool creds_ready = false;
-       int i = 0, maxtries = 3;
-
-       ZERO_STRUCT(in_data);
-
-       retval = smb_krb5_parse_name(context, principal, &server);
-       if (retval) {
-               DEBUG(1,("ads_krb5_mk_req: Failed to parse principal %s\n", principal));
-               return retval;
+#if defined(HAVE_KRB5_FREE_DATA_CONTENTS)
+       if (pdata->data) {
+               krb5_free_data_contents(context, pdata);
        }
+#elif defined(HAVE_KRB5_DATA_FREE)
+       krb5_data_free(context, pdata);
+#else
+       SAFE_FREE(pdata->data);
+#endif
+}
 
-       if (impersonate_princ_s) {
-               retval = smb_krb5_parse_name(context, impersonate_princ_s,
-                                            &impersonate_princ);
-               if (retval) {
-                       DEBUG(1,("ads_krb5_mk_req: Failed to parse principal %s\n", impersonate_princ_s));
-                       goto cleanup_princ;
+/*
+ * @brief copy a buffer into a krb5_data struct
+ *
+ * @param[in] p                        The krb5_data
+ * @param[in] data             The data to copy
+ * @param[in] length           The length of the data to copy
+ * @return krb5_error_code
+ *
+ * Caller has to free krb5_data with smb_krb5_free_data_contents().
+ */
+krb5_error_code smb_krb5_copy_data_contents(krb5_data *p,
+                                           const void *data,
+                                           size_t len)
+{
+#if defined(HAVE_KRB5_DATA_COPY)
+       return krb5_data_copy(p, data, len);
+#else
+       if (len) {
+               p->data = malloc(len);
+               if (p->data == NULL) {
+                       return ENOMEM;
                }
+               memmove(p->data, data, len);
+       } else {
+               p->data = NULL;
        }
+       p->length = len;
+       p->magic = KV5M_DATA;
+       return 0;
+#endif
+}
 
-       /* obtain ticket & session key */
-       ZERO_STRUCT(creds);
-       if ((retval = krb5_copy_principal(context, server, &creds.server))) {
-               DEBUG(1,("ads_krb5_mk_req: krb5_copy_principal failed (%s)\n",
-                        error_message(retval)));
-               goto cleanup_princ;
+bool get_krb5_smb_session_key(TALLOC_CTX *mem_ctx,
+                             krb5_context context,
+                             krb5_auth_context auth_context,
+                             DATA_BLOB *session_key, bool remote)
+{
+       krb5_keyblock *skey = NULL;
+       krb5_error_code err = 0;
+       bool ret = false;
+
+       if (remote) {
+#ifdef HAVE_KRB5_AUTH_CON_GETRECVSUBKEY
+               err = krb5_auth_con_getrecvsubkey(context,
+                                                 auth_context,
+                                                 &skey);
+#else /* HAVE_KRB5_AUTH_CON_GETRECVSUBKEY */
+               err = krb5_auth_con_getremotesubkey(context,
+                                                   auth_context, &skey);
+#endif /* HAVE_KRB5_AUTH_CON_GETRECVSUBKEY */
+       } else {
+#ifdef HAVE_KRB5_AUTH_CON_GETSENDSUBKEY
+               err = krb5_auth_con_getsendsubkey(context,
+                                                 auth_context,
+                                                 &skey);
+#else /* HAVE_KRB5_AUTH_CON_GETSENDSUBKEY */
+               err = krb5_auth_con_getlocalsubkey(context,
+                                                  auth_context, &skey);
+#endif /* HAVE_KRB5_AUTH_CON_GETSENDSUBKEY */
        }
 
-       if ((retval = krb5_cc_get_principal(context, ccache, &creds.client))) {
-               /* This can commonly fail on smbd startup with no ticket in the cache.
-                * Report at higher level than 1. */
-               DEBUG(3,("ads_krb5_mk_req: krb5_cc_get_principal failed (%s)\n",
-                        error_message(retval)));
-               goto cleanup_creds;
+       if (err || skey == NULL) {
+               DEBUG(10, ("KRB5 error getting session key %d\n", err));
+               goto done;
        }
 
-       while (!creds_ready && (i < maxtries)) {
+       DEBUG(10, ("Got KRB5 session key of length %d\n",
+                  (int)KRB5_KEY_LENGTH(skey)));
 
-               if ((retval = smb_krb5_get_credentials(context, ccache,
-                                                      creds.client,
-                                                      creds.server,
-                                                      impersonate_princ,
-                                                      &credsp))) {
-                       DEBUG(1,("ads_krb5_mk_req: smb_krb5_get_credentials failed for %s (%s)\n",
-                               principal, error_message(retval)));
-                       goto cleanup_creds;
-               }
+       *session_key = data_blob_talloc(mem_ctx,
+                                        KRB5_KEY_DATA(skey),
+                                        KRB5_KEY_LENGTH(skey));
+       dump_data_pw("KRB5 Session Key:\n",
+                    session_key->data,
+                    session_key->length);
 
-               /* cope with ticket being in the future due to clock skew */
-               if ((unsigned)credsp->times.starttime > time(NULL)) {
-                       time_t t = time(NULL);
-                       int time_offset =(int)((unsigned)credsp->times.starttime-t);
-                       DEBUG(4,("ads_krb5_mk_req: Advancing clock by %d seconds to cope with clock skew\n", time_offset));
-                       krb5_set_real_time(context, t + time_offset + 1, 0);
-               }
-
-               if (!ads_cleanup_expired_creds(context, ccache, credsp)) {
-                       creds_ready = true;
-               }
+       ret = true;
 
-               i++;
+done:
+       if (skey) {
+               krb5_free_keyblock(context, skey);
        }
 
-       DEBUG(10,("ads_krb5_mk_req: Ticket (%s) in ccache (%s:%s) is valid until: (%s - %u)\n",
-                 principal, krb5_cc_get_type(context, ccache), krb5_cc_get_name(context, ccache),
-                 http_timestring(talloc_tos(), (unsigned)credsp->times.endtime), 
-                 (unsigned)credsp->times.endtime));
+       return ret;
+}
 
-       if (expire_time) {
-               *expire_time = (time_t)credsp->times.endtime;
+
+#if defined(HAVE_KRB5_PRINCIPAL_GET_COMP_STRING) && !defined(HAVE_KRB5_PRINC_COMPONENT)
+ const krb5_data *krb5_princ_component(krb5_context context, krb5_principal principal, int i );
+
+ const krb5_data *krb5_princ_component(krb5_context context, krb5_principal principal, int i )
+{
+       static krb5_data kdata;
+
+       kdata.data = discard_const_p(char, krb5_principal_get_comp_string(context, principal, i));
+       kdata.length = strlen((const char *)kdata.data);
+       return &kdata;
+}
+#endif
+
+/*
+ * @brief Get talloced string component of a principal
+ *
+ * @param[in] mem_ctx          The TALLOC_CTX
+ * @param[in] context          The krb5_context
+ * @param[in] principal                The principal
+ * @param[in] component                The component
+ * @return string component
+ *
+ * Caller must talloc_free if the return value is not NULL.
+ *
+ */
+
+/* caller has to free returned string with talloc_free() */
+char *smb_krb5_principal_get_comp_string(TALLOC_CTX *mem_ctx,
+                                        krb5_context context,
+                                        krb5_const_principal principal,
+                                        unsigned int component)
+{
+#if defined(HAVE_KRB5_PRINCIPAL_GET_COMP_STRING)
+       return talloc_strdup(mem_ctx, krb5_principal_get_comp_string(context, principal, component));
+#else
+       krb5_data *data;
+
+       if (component >= krb5_princ_size(context, principal)) {
+               return NULL;
        }
 
-       /* Allocate the auth_context. */
-       retval = setup_auth_context(context, auth_context);
-       if (retval) {
-               DEBUG(1,("setup_auth_context failed (%s)\n",
-                       error_message(retval)));
-               goto cleanup_creds;
+       data = krb5_princ_component(context, principal, component);
+       if (data == NULL) {
+               return NULL;
        }
 
-#if defined(TKT_FLG_OK_AS_DELEGATE ) && defined(HAVE_KRB5_AUTH_CON_SETUSERUSERKEY) && defined(KRB5_AUTH_CONTEXT_USE_SUBKEY) && defined(HAVE_KRB5_AUTH_CON_SET_REQ_CKSUMTYPE)
-       {
-               uint32_t gss_flags = 0;
+       return talloc_strndup(mem_ctx, data->data, data->length);
+#endif
+}
 
-               if( credsp->ticket_flags & TKT_FLG_OK_AS_DELEGATE ) {
-                       /* Fetch a forwarded TGT from the KDC so that we can hand off a 2nd ticket
-                        as part of the kerberos exchange. */
+/* Prototypes */
 
-                       DEBUG( 3, ("ads_krb5_mk_req: server marked as OK to delegate to, building forwardable TGT\n")  );
+ krb5_error_code smb_krb5_renew_ticket(const char *ccache_string,      /* FILE:/tmp/krb5cc_0 */
+                                      const char *client_string,       /* gd@BER.SUSE.DE */
+                                      const char *service_string,      /* krbtgt/BER.SUSE.DE@BER.SUSE.DE */
+                                      time_t *expire_time)
+{
+       krb5_error_code ret;
+       krb5_context context = NULL;
+       krb5_ccache ccache = NULL;
+       krb5_principal client = NULL;
+       krb5_creds creds, creds_in;
 
-                       retval = krb5_auth_con_setuseruserkey(context,
-                                       *auth_context,
-                                       &credsp->keyblock );
-                       if (retval) {
-                               DEBUG(1,("krb5_auth_con_setuseruserkey failed (%s)\n",
-                                       error_message(retval)));
-                               goto cleanup_creds;
-                       }
+       ZERO_STRUCT(creds);
+       ZERO_STRUCT(creds_in);
 
-                       /* Must use a subkey for forwarded tickets. */
-                       retval = krb5_auth_con_setflags(context,
-                               *auth_context,
-                               KRB5_AUTH_CONTEXT_USE_SUBKEY);
-                       if (retval) {
-                               DEBUG(1,("krb5_auth_con_setflags failed (%s)\n",
-                                       error_message(retval)));
-                               goto cleanup_creds;
-                       }
+       initialize_krb5_error_table();
+       ret = krb5_init_context(&context);
+       if (ret) {
+               goto done;
+       }
 
-                       retval = krb5_fwd_tgt_creds(context,/* Krb5 context [in] */
-                               *auth_context,  /* Authentication context [in] */
-                               discard_const_p(char, KRB5_TGS_NAME),  /* Ticket service name ("krbtgt") [in] */
-                               credsp->client, /* Client principal for the tgt [in] */
-                               credsp->server, /* Server principal for the tgt [in] */
-                               ccache,         /* Credential cache to use for storage [in] */
-                               1,              /* Turn on for "Forwardable ticket" [in] */
-                               &in_data );     /* Resulting response [out] */
+       if (!ccache_string) {
+               ccache_string = krb5_cc_default_name(context);
+       }
 
-                       if (retval) {
-                               DEBUG( 3, ("krb5_fwd_tgt_creds failed (%s)\n",
-                                          error_message( retval ) ) );
+       if (!ccache_string) {
+               ret = EINVAL;
+               goto done;
+       }
 
-                               /*
-                                * This is not fatal. Delete the *auth_context and continue
-                                * with krb5_mk_req_extended to get a non-forwardable ticket.
-                                */
+       DEBUG(10,("smb_krb5_renew_ticket: using %s as ccache\n", ccache_string));
 
-                               if (in_data.data) {
-                                       free( in_data.data );
-                                       in_data.data = NULL;
-                                       in_data.length = 0;
-                               }
-                               krb5_auth_con_free(context, *auth_context);
-                               *auth_context = NULL;
-                               retval = setup_auth_context(context, auth_context);
-                               if (retval) {
-                                       DEBUG(1,("setup_auth_context failed (%s)\n",
-                                               error_message(retval)));
-                                       goto cleanup_creds;
-                               }
-                       } else {
-                               /* We got a delegated ticket. */
-                               gss_flags |= GSS_C_DELEG_FLAG;
-                       }
-               }
+       /* FIXME: we should not fall back to defaults */
+       ret = krb5_cc_resolve(context, discard_const_p(char, ccache_string), &ccache);
+       if (ret) {
+               goto done;
+       }
 
-               /* Frees and reallocates in_data into a GSS checksum blob. */
-               retval = create_gss_checksum(&in_data, gss_flags);
-               if (retval) {
-                       goto cleanup_data;
+       if (client_string) {
+               ret = smb_krb5_parse_name(context, client_string, &client);
+               if (ret) {
+                       goto done;
                }
-
-               /* We always want GSS-checksum types. */
-               retval = krb5_auth_con_set_req_cksumtype(context, *auth_context, GSSAPI_CHECKSUM );
-               if (retval) {
-                       DEBUG(1,("krb5_auth_con_set_req_cksumtype failed (%s)\n",
-                               error_message(retval)));
-                       goto cleanup_data;
+       } else {
+               ret = krb5_cc_get_principal(context, ccache, &client);
+               if (ret) {
+                       goto done;
                }
        }
-#endif
 
-       retval = krb5_mk_req_extended(context, auth_context, ap_req_options,
-                                     &in_data, credsp, outbuf);
-       if (retval) {
-               DEBUG(1,("ads_krb5_mk_req: krb5_mk_req_extended failed (%s)\n", 
-                        error_message(retval)));
+       ret = krb5_get_renewed_creds(context, &creds, client, ccache, discard_const_p(char, service_string));
+       if (ret) {
+               DEBUG(10,("smb_krb5_renew_ticket: krb5_get_kdc_cred failed: %s\n", error_message(ret)));
+               goto done;
        }
 
-#if defined(TKT_FLG_OK_AS_DELEGATE ) && defined(HAVE_KRB5_AUTH_CON_SETUSERUSERKEY) && defined(KRB5_AUTH_CONTEXT_USE_SUBKEY) && defined(HAVE_KRB5_AUTH_CON_SET_REQ_CKSUMTYPE)
-cleanup_data:
-#endif
-
-       if (in_data.data) {
-               free( in_data.data );
-               in_data.length = 0;
+       /* hm, doesn't that create a new one if the old one wasn't there? - Guenther */
+       ret = krb5_cc_initialize(context, ccache, client);
+       if (ret) {
+               goto done;
        }
 
-       krb5_free_creds(context, credsp);
+       ret = krb5_cc_store_cred(context, ccache, &creds);
 
-cleanup_creds:
+       if (expire_time) {
+               *expire_time = (time_t) creds.times.endtime;
+       }
+
+done:
+       krb5_free_cred_contents(context, &creds_in);
        krb5_free_cred_contents(context, &creds);
 
-cleanup_princ:
-       krb5_free_principal(context, server);
-       if (impersonate_princ) {
-               krb5_free_principal(context, impersonate_princ);
+       if (client) {
+               krb5_free_principal(context, client);
+       }
+       if (ccache) {
+               krb5_cc_close(context, ccache);
+       }
+       if (context) {
+               krb5_free_context(context);
        }
 
-       return retval;
+       return ret;
 }
 
-/**
- * @brief Free the contents of a krb5_data structure and zero the data field.
- *
- * @param[in]  context  The krb5 context
- *
- * @param[in]  pdata    The data structure to free contents of
- *
- * This function frees the contents, not the structure itself.
- */
-void smb_krb5_free_data_contents(krb5_context context, krb5_data *pdata)
+ krb5_error_code smb_krb5_free_addresses(krb5_context context, smb_krb5_addresses *addr)
 {
-#if defined(HAVE_KRB5_FREE_DATA_CONTENTS)
-       if (pdata->data) {
-               krb5_free_data_contents(context, pdata);
+       krb5_error_code ret = 0;
+       if (addr == NULL) {
+               return ret;
        }
-#elif defined(HAVE_KRB5_DATA_FREE)
-       krb5_data_free(context, pdata);
-#else
-       SAFE_FREE(pdata->data);
+#if defined(HAVE_MAGIC_IN_KRB5_ADDRESS) && defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS) /* MIT */
+       krb5_free_addresses(context, addr->addrs);
+#elif defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS) /* Heimdal */
+       ret = krb5_free_addresses(context, addr->addrs);
+       SAFE_FREE(addr->addrs);
 #endif
+       SAFE_FREE(addr);
+       addr = NULL;
+       return ret;
 }
 
-/*
- * @brief copy a buffer into a krb5_data struct
- *
- * @param[in] p                        The krb5_data
- * @param[in] data             The data to copy
- * @param[in] length           The length of the data to copy
- * @return krb5_error_code
- *
- * Caller has to free krb5_data with smb_krb5_free_data_contents().
- */
-krb5_error_code smb_krb5_copy_data_contents(krb5_data *p,
-                                           const void *data,
-                                           size_t len)
-{
-#if defined(HAVE_KRB5_DATA_COPY)
-       return krb5_data_copy(p, data, len);
-#else
-       if (len) {
-               p->data = malloc(len);
-               if (p->data == NULL) {
-                       return ENOMEM;
-               }
-               memmove(p->data, data, len);
-       } else {
-               p->data = NULL;
-       }
-       p->length = len;
-       p->magic = KV5M_DATA;
-       return 0;
-#endif
-}
-
-/*
-  get a kerberos5 ticket for the given service
-*/
-int cli_krb5_get_ticket(TALLOC_CTX *mem_ctx,
-                       const char *principal, time_t time_offset,
-                       DATA_BLOB *ticket, DATA_BLOB *session_key_krb5,
-                       uint32_t extra_ap_opts, const char *ccname,
-                       time_t *tgs_expire,
-                       const char *impersonate_princ_s)
-
+#define MAX_NETBIOSNAME_LEN 16
+ krb5_error_code smb_krb5_gen_netbios_krb5_address(smb_krb5_addresses **kerb_addr,
+                                                  const char *netbios_name)
 {
-       krb5_error_code retval;
-       krb5_data packet;
-       krb5_context context = NULL;
-       krb5_ccache ccdef = NULL;
-       krb5_auth_context auth_context = NULL;
-       krb5_enctype enc_types[] = {
-#ifdef HAVE_ENCTYPE_AES256_CTS_HMAC_SHA1_96
-               ENCTYPE_AES256_CTS_HMAC_SHA1_96,
-#endif
-#ifdef HAVE_ENCTYPE_AES128_CTS_HMAC_SHA1_96
-               ENCTYPE_AES128_CTS_HMAC_SHA1_96,
+       krb5_error_code ret = 0;
+       char buf[MAX_NETBIOSNAME_LEN];
+       int len;
+#if defined(HAVE_MAGIC_IN_KRB5_ADDRESS) && defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS) /* MIT */
+       krb5_address **addrs = NULL;
+#elif defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS) /* Heimdal */
+       krb5_addresses *addrs = NULL;
 #endif
-               ENCTYPE_ARCFOUR_HMAC,
-               ENCTYPE_DES_CBC_MD5,
-               ENCTYPE_DES_CBC_CRC,
-               ENCTYPE_NULL};
 
-       initialize_krb5_error_table();
-       retval = krb5_init_context(&context);
-       if (retval) {
-               DEBUG(1, ("krb5_init_context failed (%s)\n",
-                        error_message(retval)));
-               goto failed;
-       }
-
-       if (time_offset != 0) {
-               krb5_set_real_time(context, time(NULL) + time_offset, 0);
-       }
-
-       if ((retval = krb5_cc_resolve(context, ccname ?
-                       ccname : krb5_cc_default_name(context), &ccdef))) {
-               DEBUG(1, ("krb5_cc_default failed (%s)\n",
-                        error_message(retval)));
-               goto failed;
-       }
-
-       if ((retval = krb5_set_default_tgs_ktypes(context, enc_types))) {
-               DEBUG(1, ("krb5_set_default_tgs_ktypes failed (%s)\n",
-                        error_message(retval)));
-               goto failed;
+       *kerb_addr = (smb_krb5_addresses *)SMB_MALLOC(sizeof(smb_krb5_addresses));
+       if (*kerb_addr == NULL) {
+               return ENOMEM;
        }
 
-       retval = ads_krb5_mk_req(context, &auth_context,
-                               AP_OPTS_USE_SUBKEY | (krb5_flags)extra_ap_opts,
-                               principal, ccdef, &packet,
-                               tgs_expire, impersonate_princ_s);
-       if (retval) {
-               goto failed;
+       /* temporarily duplicate put_name() code here to avoid dependency
+        * issues for a 5 lines function */
+       len = strlen(netbios_name);
+       memcpy(buf, netbios_name,
+               (len < MAX_NETBIOSNAME_LEN) ? len : MAX_NETBIOSNAME_LEN - 1);
+       if (len < MAX_NETBIOSNAME_LEN - 1) {
+               memset(buf + len, ' ', MAX_NETBIOSNAME_LEN - 1 - len);
        }
+       buf[MAX_NETBIOSNAME_LEN - 1] = 0x20;
 
-       get_krb5_smb_session_key(mem_ctx, context, auth_context,
-                                session_key_krb5, false);
-
-       *ticket = data_blob_talloc(mem_ctx, packet.data, packet.length);
-
-       smb_krb5_free_data_contents(context, &packet);
+#if defined(HAVE_MAGIC_IN_KRB5_ADDRESS) && defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS) /* MIT */
+       {
+               int num_addr = 2;
 
-failed:
+               addrs = (krb5_address **)SMB_MALLOC(sizeof(krb5_address *) * num_addr);
+               if (addrs == NULL) {
+                       SAFE_FREE(*kerb_addr);
+                       return ENOMEM;
+               }
 
-       if (context) {
-               if (ccdef)
-                       krb5_cc_close(context, ccdef);
-               if (auth_context)
-                       krb5_auth_con_free(context, auth_context);
-               krb5_free_context(context);
-       }
+               memset(addrs, 0, sizeof(krb5_address *) * num_addr);
 
-       return retval;
-}
+               addrs[0] = (krb5_address *)SMB_MALLOC(sizeof(krb5_address));
+               if (addrs[0] == NULL) {
+                       SAFE_FREE(addrs);
+                       SAFE_FREE(*kerb_addr);
+                       return ENOMEM;
+               }
 
-bool get_krb5_smb_session_key(TALLOC_CTX *mem_ctx,
-                             krb5_context context,
-                             krb5_auth_context auth_context,
-                             DATA_BLOB *session_key, bool remote)
-{
-       krb5_keyblock *skey = NULL;
-       krb5_error_code err = 0;
-       bool ret = false;
+               addrs[0]->magic = KV5M_ADDRESS;
+               addrs[0]->addrtype = KRB5_ADDR_NETBIOS;
+               addrs[0]->length = MAX_NETBIOSNAME_LEN;
+               addrs[0]->contents = (unsigned char *)SMB_MALLOC(addrs[0]->length);
+               if (addrs[0]->contents == NULL) {
+                       SAFE_FREE(addrs[0]);
+                       SAFE_FREE(addrs);
+                       SAFE_FREE(*kerb_addr);
+                       return ENOMEM;
+               }
 
-       if (remote) {
-#ifdef HAVE_KRB5_AUTH_CON_GETRECVSUBKEY
-               err = krb5_auth_con_getrecvsubkey(context,
-                                                 auth_context,
-                                                 &skey);
-#else /* HAVE_KRB5_AUTH_CON_GETRECVSUBKEY */
-               err = krb5_auth_con_getremotesubkey(context,
-                                                   auth_context, &skey);
-#endif /* HAVE_KRB5_AUTH_CON_GETRECVSUBKEY */
-       } else {
-#ifdef HAVE_KRB5_AUTH_CON_GETSENDSUBKEY
-               err = krb5_auth_con_getsendsubkey(context,
-                                                 auth_context,
-                                                 &skey);
-#else /* HAVE_KRB5_AUTH_CON_GETSENDSUBKEY */
-               err = krb5_auth_con_getlocalsubkey(context,
-                                                  auth_context, &skey);
-#endif /* HAVE_KRB5_AUTH_CON_GETSENDSUBKEY */
-       }
+               memcpy(addrs[0]->contents, buf, addrs[0]->length);
 
-       if (err || skey == NULL) {
-               DEBUG(10, ("KRB5 error getting session key %d\n", err));
-               goto done;
+               addrs[1] = NULL;
        }
+#elif defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS) /* Heimdal */
+       {
+               addrs = (krb5_addresses *)SMB_MALLOC(sizeof(krb5_addresses));
+               if (addrs == NULL) {
+                       SAFE_FREE(*kerb_addr);
+                       return ENOMEM;
+               }
 
-       DEBUG(10, ("Got KRB5 session key of length %d\n",
-                  (int)KRB5_KEY_LENGTH(skey)));
+               memset(addrs, 0, sizeof(krb5_addresses));
 
-       *session_key = data_blob_talloc(mem_ctx,
-                                        KRB5_KEY_DATA(skey),
-                                        KRB5_KEY_LENGTH(skey));
-       dump_data_pw("KRB5 Session Key:\n",
-                    session_key->data,
-                    session_key->length);
+               addrs->len = 1;
+               addrs->val = (krb5_address *)SMB_MALLOC(sizeof(krb5_address));
+               if (addrs->val == NULL) {
+                       SAFE_FREE(addrs);
+                       SAFE_FREE(kerb_addr);
+                       return ENOMEM;
+               }
 
-       ret = true;
+               addrs->val[0].addr_type = KRB5_ADDR_NETBIOS;
+               addrs->val[0].address.length = MAX_NETBIOSNAME_LEN;
+               addrs->val[0].address.data = (unsigned char *)SMB_MALLOC(addrs->val[0].address.length);
+               if (addrs->val[0].address.data == NULL) {
+                       SAFE_FREE(addrs->val);
+                       SAFE_FREE(addrs);
+                       SAFE_FREE(*kerb_addr);
+                       return ENOMEM;
+               }
 
-done:
-       if (skey) {
-               krb5_free_keyblock(context, skey);
+               memcpy(addrs->val[0].address.data, buf, addrs->val[0].address.length);
        }
+#else
+#error UNKNOWN_KRB5_ADDRESS_FORMAT
+#endif
+       (*kerb_addr)->addrs = addrs;
 
        return ret;
 }
 
+ void smb_krb5_free_error(krb5_context context, krb5_error *krberror)
+{
+#ifdef HAVE_KRB5_FREE_ERROR_CONTENTS /* Heimdal */
+       krb5_free_error_contents(context, krberror);
+#else /* MIT */
+       krb5_free_error(context, krberror);
+#endif
+}
 
-#if defined(HAVE_KRB5_PRINCIPAL_GET_COMP_STRING) && !defined(HAVE_KRB5_PRINC_COMPONENT)
- const krb5_data *krb5_princ_component(krb5_context context, krb5_principal principal, int i );
-
- const krb5_data *krb5_princ_component(krb5_context context, krb5_principal principal, int i )
+ krb5_error_code handle_krberror_packet(krb5_context context,
+                                       krb5_data *packet)
 {
-       static krb5_data kdata;
+       krb5_error_code ret;
+       bool got_error_code = false;
 
-       kdata.data = discard_const_p(char, krb5_principal_get_comp_string(context, principal, i));
-       kdata.length = strlen((const char *)kdata.data);
-       return &kdata;
-}
-#endif
+       DEBUG(10,("handle_krberror_packet: got error packet\n"));
 
-/*
- * @brief Get talloced string component of a principal
- *
- * @param[in] mem_ctx          The TALLOC_CTX
- * @param[in] context          The krb5_context
- * @param[in] principal                The principal
- * @param[in] component                The component
- * @return string component
- *
- * Caller must talloc_free if the return value is not NULL.
- *
- */
-
-/* caller has to free returned string with talloc_free() */
-char *smb_krb5_principal_get_comp_string(TALLOC_CTX *mem_ctx,
-                                        krb5_context context,
-                                        krb5_const_principal principal,
-                                        unsigned int component)
-{
-#if defined(HAVE_KRB5_PRINCIPAL_GET_COMP_STRING)
-       return talloc_strdup(mem_ctx, krb5_principal_get_comp_string(context, principal, component));
-#else
-       krb5_data *data;
-
-       if (component >= krb5_princ_size(context, principal)) {
-               return NULL;
-       }
-
-       data = krb5_princ_component(context, principal, component);
-       if (data == NULL) {
-               return NULL;
-       }
-
-       return talloc_strndup(mem_ctx, data->data, data->length);
-#endif
-}
-
-/* Prototypes */
-
- krb5_error_code smb_krb5_renew_ticket(const char *ccache_string,      /* FILE:/tmp/krb5cc_0 */
-                                      const char *client_string,       /* gd@BER.SUSE.DE */
-                                      const char *service_string,      /* krbtgt/BER.SUSE.DE@BER.SUSE.DE */
-                                      time_t *expire_time)
-{
-       krb5_error_code ret;
-       krb5_context context = NULL;
-       krb5_ccache ccache = NULL;
-       krb5_principal client = NULL;
-       krb5_creds creds, creds_in;
-
-       ZERO_STRUCT(creds);
-       ZERO_STRUCT(creds_in);
-
-       initialize_krb5_error_table();
-       ret = krb5_init_context(&context);
-       if (ret) {
-               goto done;
-       }
-
-       if (!ccache_string) {
-               ccache_string = krb5_cc_default_name(context);
-       }
-
-       if (!ccache_string) {
-               ret = EINVAL;
-               goto done;
-       }
-
-       DEBUG(10,("smb_krb5_renew_ticket: using %s as ccache\n", ccache_string));
-
-       /* FIXME: we should not fall back to defaults */
-       ret = krb5_cc_resolve(context, discard_const_p(char, ccache_string), &ccache);
-       if (ret) {
-               goto done;
-       }
-
-       if (client_string) {
-               ret = smb_krb5_parse_name(context, client_string, &client);
-               if (ret) {
-                       goto done;
-               }
-       } else {
-               ret = krb5_cc_get_principal(context, ccache, &client);
-               if (ret) {
-                       goto done;
-               }
-       }
-
-       ret = krb5_get_renewed_creds(context, &creds, client, ccache, discard_const_p(char, service_string));
-       if (ret) {
-               DEBUG(10,("smb_krb5_renew_ticket: krb5_get_kdc_cred failed: %s\n", error_message(ret)));
-               goto done;
-       }
-
-       /* hm, doesn't that create a new one if the old one wasn't there? - Guenther */
-       ret = krb5_cc_initialize(context, ccache, client);
-       if (ret) {
-               goto done;
-       }
-
-       ret = krb5_cc_store_cred(context, ccache, &creds);
-
-       if (expire_time) {
-               *expire_time = (time_t) creds.times.endtime;
-       }
-
-done:
-       krb5_free_cred_contents(context, &creds_in);
-       krb5_free_cred_contents(context, &creds);
-
-       if (client) {
-               krb5_free_principal(context, client);
-       }
-       if (ccache) {
-               krb5_cc_close(context, ccache);
-       }
-       if (context) {
-               krb5_free_context(context);
-       }
-
-       return ret;
-}
-
- krb5_error_code smb_krb5_free_addresses(krb5_context context, smb_krb5_addresses *addr)
-{
-       krb5_error_code ret = 0;
-       if (addr == NULL) {
-               return ret;
-       }
-#if defined(HAVE_MAGIC_IN_KRB5_ADDRESS) && defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS) /* MIT */
-       krb5_free_addresses(context, addr->addrs);
-#elif defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS) /* Heimdal */
-       ret = krb5_free_addresses(context, addr->addrs);
-       SAFE_FREE(addr->addrs);
-#endif
-       SAFE_FREE(addr);
-       addr = NULL;
-       return ret;
-}
-
-#define MAX_NETBIOSNAME_LEN 16
- krb5_error_code smb_krb5_gen_netbios_krb5_address(smb_krb5_addresses **kerb_addr,
-                                                  const char *netbios_name)
-{
-       krb5_error_code ret = 0;
-       char buf[MAX_NETBIOSNAME_LEN];
-       int len;
-#if defined(HAVE_MAGIC_IN_KRB5_ADDRESS) && defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS) /* MIT */
-       krb5_address **addrs = NULL;
-#elif defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS) /* Heimdal */
-       krb5_addresses *addrs = NULL;
-#endif
-
-       *kerb_addr = (smb_krb5_addresses *)SMB_MALLOC(sizeof(smb_krb5_addresses));
-       if (*kerb_addr == NULL) {
-               return ENOMEM;
-       }
-
-       /* temporarily duplicate put_name() code here to avoid dependency
-        * issues for a 5 lines function */
-       len = strlen(netbios_name);
-       memcpy(buf, netbios_name,
-               (len < MAX_NETBIOSNAME_LEN) ? len : MAX_NETBIOSNAME_LEN - 1);
-       if (len < MAX_NETBIOSNAME_LEN - 1) {
-               memset(buf + len, ' ', MAX_NETBIOSNAME_LEN - 1 - len);
-       }
-       buf[MAX_NETBIOSNAME_LEN - 1] = 0x20;
-
-#if defined(HAVE_MAGIC_IN_KRB5_ADDRESS) && defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS) /* MIT */
-       {
-               int num_addr = 2;
-
-               addrs = (krb5_address **)SMB_MALLOC(sizeof(krb5_address *) * num_addr);
-               if (addrs == NULL) {
-                       SAFE_FREE(*kerb_addr);
-                       return ENOMEM;
-               }
-
-               memset(addrs, 0, sizeof(krb5_address *) * num_addr);
-
-               addrs[0] = (krb5_address *)SMB_MALLOC(sizeof(krb5_address));
-               if (addrs[0] == NULL) {
-                       SAFE_FREE(addrs);
-                       SAFE_FREE(*kerb_addr);
-                       return ENOMEM;
-               }
-
-               addrs[0]->magic = KV5M_ADDRESS;
-               addrs[0]->addrtype = KRB5_ADDR_NETBIOS;
-               addrs[0]->length = MAX_NETBIOSNAME_LEN;
-               addrs[0]->contents = (unsigned char *)SMB_MALLOC(addrs[0]->length);
-               if (addrs[0]->contents == NULL) {
-                       SAFE_FREE(addrs[0]);
-                       SAFE_FREE(addrs);
-                       SAFE_FREE(*kerb_addr);
-                       return ENOMEM;
-               }
-
-               memcpy(addrs[0]->contents, buf, addrs[0]->length);
-
-               addrs[1] = NULL;
-       }
-#elif defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS) /* Heimdal */
-       {
-               addrs = (krb5_addresses *)SMB_MALLOC(sizeof(krb5_addresses));
-               if (addrs == NULL) {
-                       SAFE_FREE(*kerb_addr);
-                       return ENOMEM;
-               }
-
-               memset(addrs, 0, sizeof(krb5_addresses));
-
-               addrs->len = 1;
-               addrs->val = (krb5_address *)SMB_MALLOC(sizeof(krb5_address));
-               if (addrs->val == NULL) {
-                       SAFE_FREE(addrs);
-                       SAFE_FREE(kerb_addr);
-                       return ENOMEM;
-               }
-
-               addrs->val[0].addr_type = KRB5_ADDR_NETBIOS;
-               addrs->val[0].address.length = MAX_NETBIOSNAME_LEN;
-               addrs->val[0].address.data = (unsigned char *)SMB_MALLOC(addrs->val[0].address.length);
-               if (addrs->val[0].address.data == NULL) {
-                       SAFE_FREE(addrs->val);
-                       SAFE_FREE(addrs);
-                       SAFE_FREE(*kerb_addr);
-                       return ENOMEM;
-               }
-
-               memcpy(addrs->val[0].address.data, buf, addrs->val[0].address.length);
-       }
-#else
-#error UNKNOWN_KRB5_ADDRESS_FORMAT
-#endif
-       (*kerb_addr)->addrs = addrs;
-
-       return ret;
-}
-
- void smb_krb5_free_error(krb5_context context, krb5_error *krberror)
-{
-#ifdef HAVE_KRB5_FREE_ERROR_CONTENTS /* Heimdal */
-       krb5_free_error_contents(context, krberror);
-#else /* MIT */
-       krb5_free_error(context, krberror);
-#endif
-}
-
- krb5_error_code handle_krberror_packet(krb5_context context,
-                                       krb5_data *packet)
-{
-       krb5_error_code ret;
-       bool got_error_code = false;
-
-       DEBUG(10,("handle_krberror_packet: got error packet\n"));
-
-#ifdef HAVE_E_DATA_POINTER_IN_KRB5_ERROR /* Heimdal */
-       {
-               krb5_error krberror;
+#ifdef HAVE_E_DATA_POINTER_IN_KRB5_ERROR /* Heimdal */
+       {
+               krb5_error krberror;
 
                if ((ret = krb5_rd_error(context, packet, &krberror))) {
-                       DEBUG(10,("handle_krberror_packet: krb5_rd_error failed with: %s\n", 
+                       DEBUG(10,("handle_krberror_packet: krb5_rd_error failed with: %s\n",
                                error_message(ret)));
                        return ret;
                }
@@ -1296,7 +890,7 @@ done:
                krb5_error *krberror;
 
                if ((ret = krb5_rd_error(context, packet, &krberror))) {
-                       DEBUG(10,("handle_krberror_packet: krb5_rd_error failed with: %s\n", 
+                       DEBUG(10,("handle_krberror_packet: krb5_rd_error failed with: %s\n",
                                error_message(ret)));
                        return ret;
                }
@@ -1313,7 +907,7 @@ done:
        }
 #endif
        if (got_error_code) {
-               DEBUG(5,("handle_krberror_packet: got KERBERR from kpasswd: %s (%d)\n", 
+               DEBUG(5,("handle_krberror_packet: got KERBERR from kpasswd: %s (%d)\n",
                        error_message(ret), ret));
        }
        return ret;
@@ -1358,7 +952,7 @@ krb5_error_code smb_krb5_kt_free_entry(krb5_context context,
 
 /* caller needs to free etype_s */
 krb5_error_code smb_krb5_enctype_to_string(krb5_context context,
-                                          krb5_enctype enctype,
+                                          krb5_enctype enctype,
                                           char **etype_s)
 {
 #ifdef HAVE_KRB5_ENCTYPE_TO_STRING_WITH_KRB5_CONTEXT_ARG
@@ -1432,7 +1026,7 @@ krb5_error_code smb_krb5_open_keytab_relative(krb5_context context,
 
                if ((strncmp(keytab_name_req, "WRFILE:/", 8) == 0) ||
                    (strncmp(keytab_name_req, "FILE:/", 6) == 0)) {
-                       tmp = keytab_name_req;
+                       tmp = keytab_name_req;
                        goto resolve;
                }
 
@@ -1506,12 +1100,12 @@ krb5_error_code smb_krb5_open_keytab_relative(krb5_context context,
        }
 
  resolve:
-       DEBUG(10,("smb_krb5_open_keytab: resolving: %s\n", tmp));
+       DEBUG(10,("smb_krb5_open_keytab: resolving: %s\n", tmp));
        ret = krb5_kt_resolve(context, tmp, keytab);
 
  out:
-       TALLOC_FREE(mem_ctx);
-       return ret;
+       TALLOC_FREE(mem_ctx);
+       return ret;
 }
 
 krb5_error_code smb_krb5_open_keytab(krb5_context context,
@@ -3107,6 +2701,416 @@ krb5_error_code smb_krb5_cc_copy_creds(krb5_context context,
 #endif
 }
 
+/**********************************************************
+ * ADS KRB5 CALLS
+ **********************************************************/
+
+static bool ads_cleanup_expired_creds(krb5_context context,
+                                     krb5_ccache  ccache,
+                                     krb5_creds  *credsp)
+{
+       krb5_error_code retval;
+       const char *cc_type = krb5_cc_get_type(context, ccache);
+
+       DEBUG(3, ("ads_cleanup_expired_creds: Ticket in ccache[%s:%s] expiration %s\n",
+                 cc_type, krb5_cc_get_name(context, ccache),
+                 http_timestring(talloc_tos(), credsp->times.endtime)));
+
+       /* we will probably need new tickets if the current ones
+          will expire within 10 seconds.
+       */
+       if (credsp->times.endtime >= (time(NULL) + 10))
+               return false;
+
+       /* heimdal won't remove creds from a file ccache, and
+          perhaps we shouldn't anyway, since internally we
+          use memory ccaches, and a FILE one probably means that
+          we're using creds obtained outside of our exectuable
+       */
+       if (strequal(cc_type, "FILE")) {
+               DEBUG(5, ("ads_cleanup_expired_creds: We do not remove creds from a %s ccache\n", cc_type));
+               return false;
+       }
+
+       retval = krb5_cc_remove_cred(context, ccache, 0, credsp);
+       if (retval) {
+               DEBUG(1, ("ads_cleanup_expired_creds: krb5_cc_remove_cred failed, err %s\n",
+                         error_message(retval)));
+               /* If we have an error in this, we want to display it,
+                  but continue as though we deleted it */
+       }
+       return true;
+}
+
+/* Allocate and setup the auth context into the state we need. */
+
+static krb5_error_code setup_auth_context(krb5_context context,
+                       krb5_auth_context *auth_context)
+{
+       krb5_error_code retval;
+
+       retval = krb5_auth_con_init(context, auth_context );
+       if (retval) {
+               DEBUG(1,("krb5_auth_con_init failed (%s)\n",
+                       error_message(retval)));
+               return retval;
+       }
+
+       /* Ensure this is an addressless ticket. */
+       retval = krb5_auth_con_setaddrs(context, *auth_context, NULL, NULL);
+       if (retval) {
+               DEBUG(1,("krb5_auth_con_setaddrs failed (%s)\n",
+                       error_message(retval)));
+       }
+
+       return retval;
+}
+
+#if defined(TKT_FLG_OK_AS_DELEGATE ) && defined(HAVE_KRB5_AUTH_CON_SETUSERUSERKEY) && defined(KRB5_AUTH_CONTEXT_USE_SUBKEY) && defined(HAVE_KRB5_AUTH_CON_SET_REQ_CKSUMTYPE)
+static krb5_error_code create_gss_checksum(krb5_data *in_data, /* [inout] */
+                                               uint32_t gss_flags)
+{
+       unsigned int orig_length = in_data->length;
+       unsigned int base_cksum_size = GSSAPI_CHECKSUM_SIZE;
+       char *gss_cksum = NULL;
+
+       if (orig_length) {
+               /* Extra length field for delgated ticket. */
+               base_cksum_size += 4;
+       }
+
+       if ((unsigned int)base_cksum_size + orig_length <
+                       (unsigned int)base_cksum_size) {
+                return EINVAL;
+        }
+
+       gss_cksum = (char *)SMB_MALLOC(base_cksum_size + orig_length);
+       if (gss_cksum == NULL) {
+               return ENOMEM;
+        }
+
+       memset(gss_cksum, '\0', base_cksum_size + orig_length);
+       SIVAL(gss_cksum, 0, GSSAPI_BNDLENGTH);
+
+       /*
+        * GSS_C_NO_CHANNEL_BINDINGS means 16 zero bytes.
+        * This matches the behavior of heimdal and mit.
+        *
+        * And it is needed to work against some closed source
+        * SMB servers.
+        *
+        * See bug #7883
+        */
+       memset(&gss_cksum[4], 0x00, GSSAPI_BNDLENGTH);
+
+       SIVAL(gss_cksum, 20, gss_flags);
+
+       if (orig_length) {
+               SSVAL(gss_cksum, 24, 1); /* The Delegation Option identifier */
+               SSVAL(gss_cksum, 26, orig_length);
+               /* Copy the kerberos KRB_CRED data */
+               memcpy(gss_cksum + 28, in_data->data, orig_length);
+               free(in_data->data);
+               in_data->data = NULL;
+               in_data->length = 0;
+       }
+       in_data->data = gss_cksum;
+       in_data->length = base_cksum_size + orig_length;
+       return 0;
+}
+#endif
+
+/*
+  we can't use krb5_mk_req because w2k wants the service to be in a particular format
+*/
+static krb5_error_code ads_krb5_mk_req(krb5_context context,
+                                      krb5_auth_context *auth_context,
+                                      const krb5_flags ap_req_options,
+                                      const char *principal,
+                                      krb5_ccache ccache,
+                                      krb5_data *outbuf,
+                                      time_t *expire_time,
+                                      const char *impersonate_princ_s)
+{
+       krb5_error_code           retval;
+       krb5_principal    server;
+       krb5_principal impersonate_princ = NULL;
+       krb5_creds              * credsp;
+       krb5_creds                creds;
+       krb5_data in_data;
+       bool creds_ready = false;
+       int i = 0, maxtries = 3;
+
+       ZERO_STRUCT(in_data);
+
+       retval = smb_krb5_parse_name(context, principal, &server);
+       if (retval) {
+               DEBUG(1,("ads_krb5_mk_req: Failed to parse principal %s\n", principal));
+               return retval;
+       }
+
+       if (impersonate_princ_s) {
+               retval = smb_krb5_parse_name(context, impersonate_princ_s,
+                                            &impersonate_princ);
+               if (retval) {
+                       DEBUG(1,("ads_krb5_mk_req: Failed to parse principal %s\n", impersonate_princ_s));
+                       goto cleanup_princ;
+               }
+       }
+
+       /* obtain ticket & session key */
+       ZERO_STRUCT(creds);
+       if ((retval = krb5_copy_principal(context, server, &creds.server))) {
+               DEBUG(1,("ads_krb5_mk_req: krb5_copy_principal failed (%s)\n",
+                        error_message(retval)));
+               goto cleanup_princ;
+       }
+
+       if ((retval = krb5_cc_get_principal(context, ccache, &creds.client))) {
+               /* This can commonly fail on smbd startup with no ticket in the cache.
+                * Report at higher level than 1. */
+               DEBUG(3,("ads_krb5_mk_req: krb5_cc_get_principal failed (%s)\n",
+                        error_message(retval)));
+               goto cleanup_creds;
+       }
+
+       while (!creds_ready && (i < maxtries)) {
+
+               if ((retval = smb_krb5_get_credentials(context, ccache,
+                                                      creds.client,
+                                                      creds.server,
+                                                      impersonate_princ,
+                                                      &credsp))) {
+                       DEBUG(1,("ads_krb5_mk_req: smb_krb5_get_credentials failed for %s (%s)\n",
+                               principal, error_message(retval)));
+                       goto cleanup_creds;
+               }
+
+               /* cope with ticket being in the future due to clock skew */
+               if ((unsigned)credsp->times.starttime > time(NULL)) {
+                       time_t t = time(NULL);
+                       int time_offset =(int)((unsigned)credsp->times.starttime-t);
+                       DEBUG(4,("ads_krb5_mk_req: Advancing clock by %d seconds to cope with clock skew\n", time_offset));
+                       krb5_set_real_time(context, t + time_offset + 1, 0);
+               }
+
+               if (!ads_cleanup_expired_creds(context, ccache, credsp)) {
+                       creds_ready = true;
+               }
+
+               i++;
+       }
+
+       DEBUG(10,("ads_krb5_mk_req: Ticket (%s) in ccache (%s:%s) is valid until: (%s - %u)\n",
+                 principal, krb5_cc_get_type(context, ccache), krb5_cc_get_name(context, ccache),
+                 http_timestring(talloc_tos(), (unsigned)credsp->times.endtime),
+                 (unsigned)credsp->times.endtime));
+
+       if (expire_time) {
+               *expire_time = (time_t)credsp->times.endtime;
+       }
+
+       /* Allocate the auth_context. */
+       retval = setup_auth_context(context, auth_context);
+       if (retval) {
+               DEBUG(1,("setup_auth_context failed (%s)\n",
+                       error_message(retval)));
+               goto cleanup_creds;
+       }
+
+#if defined(TKT_FLG_OK_AS_DELEGATE ) && defined(HAVE_KRB5_AUTH_CON_SETUSERUSERKEY) && defined(KRB5_AUTH_CONTEXT_USE_SUBKEY) && defined(HAVE_KRB5_AUTH_CON_SET_REQ_CKSUMTYPE)
+       {
+               uint32_t gss_flags = 0;
+
+               if( credsp->ticket_flags & TKT_FLG_OK_AS_DELEGATE ) {
+                       /* Fetch a forwarded TGT from the KDC so that we can hand off a 2nd ticket
+                        as part of the kerberos exchange. */
+
+                       DEBUG( 3, ("ads_krb5_mk_req: server marked as OK to delegate to, building forwardable TGT\n")  );
+
+                       retval = krb5_auth_con_setuseruserkey(context,
+                                       *auth_context,
+                                       &credsp->keyblock );
+                       if (retval) {
+                               DEBUG(1,("krb5_auth_con_setuseruserkey failed (%s)\n",
+                                       error_message(retval)));
+                               goto cleanup_creds;
+                       }
+
+                       /* Must use a subkey for forwarded tickets. */
+                       retval = krb5_auth_con_setflags(context,
+                               *auth_context,
+                               KRB5_AUTH_CONTEXT_USE_SUBKEY);
+                       if (retval) {
+                               DEBUG(1,("krb5_auth_con_setflags failed (%s)\n",
+                                       error_message(retval)));
+                               goto cleanup_creds;
+                       }
+
+                       retval = krb5_fwd_tgt_creds(context,/* Krb5 context [in] */
+                               *auth_context,  /* Authentication context [in] */
+                               discard_const_p(char, KRB5_TGS_NAME),  /* Ticket service name ("krbtgt") [in] */
+                               credsp->client, /* Client principal for the tgt [in] */
+                               credsp->server, /* Server principal for the tgt [in] */
+                               ccache,         /* Credential cache to use for storage [in] */
+                               1,              /* Turn on for "Forwardable ticket" [in] */
+                               &in_data );     /* Resulting response [out] */
+
+                       if (retval) {
+                               DEBUG( 3, ("krb5_fwd_tgt_creds failed (%s)\n",
+                                          error_message( retval ) ) );
+
+                               /*
+                                * This is not fatal. Delete the *auth_context and continue
+                                * with krb5_mk_req_extended to get a non-forwardable ticket.
+                                */
+
+                               if (in_data.data) {
+                                       free( in_data.data );
+                                       in_data.data = NULL;
+                                       in_data.length = 0;
+                               }
+                               krb5_auth_con_free(context, *auth_context);
+                               *auth_context = NULL;
+                               retval = setup_auth_context(context, auth_context);
+                               if (retval) {
+                                       DEBUG(1,("setup_auth_context failed (%s)\n",
+                                               error_message(retval)));
+                                       goto cleanup_creds;
+                               }
+                       } else {
+                               /* We got a delegated ticket. */
+                               gss_flags |= GSS_C_DELEG_FLAG;
+                       }
+               }
+
+               /* Frees and reallocates in_data into a GSS checksum blob. */
+               retval = create_gss_checksum(&in_data, gss_flags);
+               if (retval) {
+                       goto cleanup_data;
+               }
+
+               /* We always want GSS-checksum types. */
+               retval = krb5_auth_con_set_req_cksumtype(context, *auth_context, GSSAPI_CHECKSUM );
+               if (retval) {
+                       DEBUG(1,("krb5_auth_con_set_req_cksumtype failed (%s)\n",
+                               error_message(retval)));
+                       goto cleanup_data;
+               }
+       }
+#endif
+
+       retval = krb5_mk_req_extended(context, auth_context, ap_req_options,
+                                     &in_data, credsp, outbuf);
+       if (retval) {
+               DEBUG(1,("ads_krb5_mk_req: krb5_mk_req_extended failed (%s)\n",
+                        error_message(retval)));
+       }
+
+#if defined(TKT_FLG_OK_AS_DELEGATE ) && defined(HAVE_KRB5_AUTH_CON_SETUSERUSERKEY) && defined(KRB5_AUTH_CONTEXT_USE_SUBKEY) && defined(HAVE_KRB5_AUTH_CON_SET_REQ_CKSUMTYPE)
+cleanup_data:
+#endif
+
+       if (in_data.data) {
+               free( in_data.data );
+               in_data.length = 0;
+       }
+
+       krb5_free_creds(context, credsp);
+
+cleanup_creds:
+       krb5_free_cred_contents(context, &creds);
+
+cleanup_princ:
+       krb5_free_principal(context, server);
+       if (impersonate_princ) {
+               krb5_free_principal(context, impersonate_princ);
+       }
+
+       return retval;
+}
+
+/*
+  get a kerberos5 ticket for the given service
+*/
+int cli_krb5_get_ticket(TALLOC_CTX *mem_ctx,
+                       const char *principal, time_t time_offset,
+                       DATA_BLOB *ticket, DATA_BLOB *session_key_krb5,
+                       uint32_t extra_ap_opts, const char *ccname,
+                       time_t *tgs_expire,
+                       const char *impersonate_princ_s)
+
+{
+       krb5_error_code retval;
+       krb5_data packet;
+       krb5_context context = NULL;
+       krb5_ccache ccdef = NULL;
+       krb5_auth_context auth_context = NULL;
+       krb5_enctype enc_types[] = {
+#ifdef HAVE_ENCTYPE_AES256_CTS_HMAC_SHA1_96
+               ENCTYPE_AES256_CTS_HMAC_SHA1_96,
+#endif
+#ifdef HAVE_ENCTYPE_AES128_CTS_HMAC_SHA1_96
+               ENCTYPE_AES128_CTS_HMAC_SHA1_96,
+#endif
+               ENCTYPE_ARCFOUR_HMAC,
+               ENCTYPE_DES_CBC_MD5,
+               ENCTYPE_DES_CBC_CRC,
+               ENCTYPE_NULL};
+
+       initialize_krb5_error_table();
+       retval = krb5_init_context(&context);
+       if (retval) {
+               DEBUG(1, ("krb5_init_context failed (%s)\n",
+                        error_message(retval)));
+               goto failed;
+       }
+
+       if (time_offset != 0) {
+               krb5_set_real_time(context, time(NULL) + time_offset, 0);
+       }
+
+       if ((retval = krb5_cc_resolve(context, ccname ?
+                       ccname : krb5_cc_default_name(context), &ccdef))) {
+               DEBUG(1, ("krb5_cc_default failed (%s)\n",
+                        error_message(retval)));
+               goto failed;
+       }
+
+       if ((retval = krb5_set_default_tgs_ktypes(context, enc_types))) {
+               DEBUG(1, ("krb5_set_default_tgs_ktypes failed (%s)\n",
+                        error_message(retval)));
+               goto failed;
+       }
+
+       retval = ads_krb5_mk_req(context, &auth_context,
+                               AP_OPTS_USE_SUBKEY | (krb5_flags)extra_ap_opts,
+                               principal, ccdef, &packet,
+                               tgs_expire, impersonate_princ_s);
+       if (retval) {
+               goto failed;
+       }
+
+       get_krb5_smb_session_key(mem_ctx, context, auth_context,
+                                session_key_krb5, false);
+
+       *ticket = data_blob_talloc(mem_ctx, packet.data, packet.length);
+
+       smb_krb5_free_data_contents(context, &packet);
+
+failed:
+
+       if (context) {
+               if (ccdef)
+                       krb5_cc_close(context, ccdef);
+               if (auth_context)
+                       krb5_auth_con_free(context, auth_context);
+               krb5_free_context(context);
+       }
+
+       return retval;
+}
+
 #else /* HAVE_KRB5 */
  /* this saves a few linking headaches */
  int cli_krb5_get_ticket(TALLOC_CTX *mem_ctx,