s4:credentials Add the functions needed to do S4U2Self with cli_credentials
[bbaumbach/samba-autobuild/.git] / source4 / auth / credentials / credentials_krb5.c
index 58b5c1df3bf2a288a05729cd055c704abc194386..1e0db3cb36f35a775bf1e4d2414179df01b5b1de 100644 (file)
@@ -9,7 +9,7 @@
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "includes.h"
 #include "system/kerberos.h"
 #include "auth/kerberos/kerberos.h"
 #include "auth/credentials/credentials.h"
+#include "auth/credentials/credentials_proto.h"
 #include "auth/credentials/credentials_krb5.h"
+#include "param/param.h"
 
-int cli_credentials_get_krb5_context(struct cli_credentials *cred, 
+_PUBLIC_ int cli_credentials_get_krb5_context(struct cli_credentials *cred, 
+                                             struct tevent_context *event_ctx,
+                                    struct loadparm_context *lp_ctx,
                                     struct smb_krb5_context **smb_krb5_context) 
 {
        int ret;
@@ -37,9 +40,10 @@ int cli_credentials_get_krb5_context(struct cli_credentials *cred,
                return 0;
        }
 
-       ret = smb_krb5_init_context(cred, cli_credentials_get_event_context(cred)
+       ret = smb_krb5_init_context(cred, event_ctx, lp_ctx
                                    &cred->smb_krb5_context);
        if (ret) {
+               cred->smb_krb5_context = NULL;
                return ret;
        }
        *smb_krb5_context = cred->smb_krb5_context;
@@ -50,7 +54,7 @@ int cli_credentials_get_krb5_context(struct cli_credentials *cred,
  * otherwise we might have problems with the krb5 context already
  * being here.
  */
-NTSTATUS cli_credentials_set_krb5_context(struct cli_credentials *cred, 
+_PUBLIC_ NTSTATUS cli_credentials_set_krb5_context(struct cli_credentials *cred, 
                                          struct smb_krb5_context *smb_krb5_context)
 {
        if (!talloc_reference(cred, smb_krb5_context)) {
@@ -60,47 +64,45 @@ NTSTATUS cli_credentials_set_krb5_context(struct cli_credentials *cred,
        return NT_STATUS_OK;
 }
 
-int cli_credentials_set_from_ccache(struct cli_credentials *cred, 
-                                   enum credentials_obtained obtained)
+static int cli_credentials_set_from_ccache(struct cli_credentials *cred, 
+                                          struct ccache_container *ccache,
+                                          enum credentials_obtained obtained,
+                                          const char **error_string)
 {
        
        krb5_principal princ;
        krb5_error_code ret;
        char *name;
-       char **realm;
 
        if (cred->ccache_obtained > obtained) {
                return 0;
        }
 
-       ret = krb5_cc_get_principal(cred->ccache->smb_krb5_context->krb5_context, 
-                                   cred->ccache->ccache, &princ);
+       ret = krb5_cc_get_principal(ccache->smb_krb5_context->krb5_context, 
+                                   ccache->ccache, &princ);
 
        if (ret) {
-               char *err_mess = smb_get_krb5_error_message(cred->ccache->smb_krb5_context->krb5_context, ret, cred);
-               DEBUG(1,("failed to get principal from ccache: %s\n", 
-                        err_mess));
-               talloc_free(err_mess);
+               (*error_string) = talloc_asprintf(cred, "failed to get principal from ccache: %s\n",
+                                                 smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
+                                                                            ret, cred));
                return ret;
        }
        
-       ret = krb5_unparse_name(cred->ccache->smb_krb5_context->krb5_context, princ, &name);
+       ret = krb5_unparse_name(ccache->smb_krb5_context->krb5_context, princ, &name);
        if (ret) {
-               char *err_mess = smb_get_krb5_error_message(cred->ccache->smb_krb5_context->krb5_context, ret, cred);
-               DEBUG(1,("failed to unparse principal from ccache: %s\n", 
-                        err_mess));
-               talloc_free(err_mess);
+               (*error_string) = talloc_asprintf(cred, "failed to unparse principal from ccache: %s\n",
+                                                 smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
+                                                                            ret, cred));
                return ret;
        }
 
-       realm = krb5_princ_realm(cred->ccache->smb_krb5_context->krb5_context, princ);
-
        cli_credentials_set_principal(cred, name, obtained);
 
        free(name);
 
-       krb5_free_principal(cred->ccache->smb_krb5_context->krb5_context, princ);
+       krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
 
+       /* set the ccache_obtained here, as it just got set to UNINITIALISED by the calls above */
        cred->ccache_obtained = obtained;
 
        return 0;
@@ -121,9 +123,12 @@ static int free_dccache(struct ccache_container *ccc) {
        return 0;
 }
 
-int cli_credentials_set_ccache(struct cli_credentials *cred, 
-                              const char *name, 
-                              enum credentials_obtained obtained)
+_PUBLIC_ int cli_credentials_set_ccache(struct cli_credentials *cred, 
+                                       struct tevent_context *event_ctx,
+                                       struct loadparm_context *lp_ctx,
+                                       const char *name,
+                                       enum credentials_obtained obtained,
+                                       const char **error_string)
 {
        krb5_error_code ret;
        krb5_principal princ;
@@ -134,33 +139,39 @@ int cli_credentials_set_ccache(struct cli_credentials *cred,
 
        ccc = talloc(cred, struct ccache_container);
        if (!ccc) {
+               (*error_string) = error_message(ENOMEM);
                return ENOMEM;
        }
 
-       ret = cli_credentials_get_krb5_context(cred, &ccc->smb_krb5_context);
+       ret = cli_credentials_get_krb5_context(cred, event_ctx, lp_ctx, 
+                                              &ccc->smb_krb5_context);
        if (ret) {
+               (*error_string) = error_message(ret);
                talloc_free(ccc);
                return ret;
        }
        if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
                talloc_free(ccc);
+               (*error_string) = error_message(ENOMEM);
                return ENOMEM;
        }
 
        if (name) {
                ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, name, &ccc->ccache);
                if (ret) {
-                       DEBUG(1,("failed to read krb5 ccache: %s: %s\n", 
-                                name, 
-                                smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
+                       (*error_string) = talloc_asprintf(cred, "failed to read krb5 ccache: %s: %s\n",
+                                                         name,
+                                                         smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
+                                                                                    ret, ccc));
                        talloc_free(ccc);
                        return ret;
                }
        } else {
                ret = krb5_cc_default(ccc->smb_krb5_context->krb5_context, &ccc->ccache);
                if (ret) {
-                       DEBUG(3,("failed to read default krb5 ccache: %s\n", 
-                                smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
+                       (*error_string) = talloc_asprintf(cred, "failed to read default krb5 ccache: %s\n",
+                                                         smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
+                                                                                    ret, ccc));
                        talloc_free(ccc);
                        return ret;
                }
@@ -171,118 +182,197 @@ int cli_credentials_set_ccache(struct cli_credentials *cred,
        ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context, ccc->ccache, &princ);
 
        if (ret) {
-               DEBUG(3,("failed to get principal from default ccache: %s\n", 
-                        smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
-               talloc_free(ccc);               
+               (*error_string) = talloc_asprintf(cred, "failed to get principal from default ccache: %s\n",
+                                                 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
+                                                                            ret, ccc));
+               talloc_free(ccc);
                return ret;
        }
 
        krb5_free_principal(ccc->smb_krb5_context->krb5_context, princ);
 
-       cred->ccache = ccc;
-       talloc_steal(cred, ccc);
-
-       ret = cli_credentials_set_from_ccache(cred, obtained);
+       ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
 
        if (ret) {
+               (*error_string) = error_message(ret);
                return ret;
        }
 
+       cred->ccache = ccc;
+       cred->ccache_obtained = obtained;
+       talloc_steal(cred, ccc);
+
+       cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
        return 0;
 }
 
 
-int cli_credentials_new_ccache(struct cli_credentials *cred, struct ccache_container **_ccc)
+static int cli_credentials_new_ccache(struct cli_credentials *cred, 
+                                     struct tevent_context *event_ctx,
+                                     struct loadparm_context *lp_ctx,
+                                     char *ccache_name,
+                                     struct ccache_container **_ccc,
+                                     const char **error_string)
 {
+       bool must_free_cc_name = false;
        krb5_error_code ret;
-       char *rand_string;
        struct ccache_container *ccc = talloc(cred, struct ccache_container);
-       char *ccache_name;
        if (!ccc) {
                return ENOMEM;
        }
 
-       rand_string = generate_random_str(NULL, 16);
-       if (!rand_string) {
-               talloc_free(ccc);
-               return ENOMEM;
-       }
-
-       ccache_name = talloc_asprintf(ccc, "MEMORY:%s", 
-                                     rand_string);
-       talloc_free(rand_string);
-
-       if (!ccache_name) {
-               talloc_free(ccc);
-               return ENOMEM;
-       }
-
-       ret = cli_credentials_get_krb5_context(cred, &ccc->smb_krb5_context);
+       ret = cli_credentials_get_krb5_context(cred, event_ctx, lp_ctx, 
+                                              &ccc->smb_krb5_context);
        if (ret) {
                talloc_free(ccc);
+               (*error_string) = talloc_asprintf(cred, "Failed to get krb5_context: %s",
+                                                 error_message(ret));
                return ret;
        }
        if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
                talloc_free(ccc);
+               (*error_string) = strerror(ENOMEM);
                return ENOMEM;
        }
 
-       ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, ccache_name, &ccc->ccache);
+       if (!ccache_name) {
+               must_free_cc_name = true;
+               ccache_name = talloc_asprintf(ccc, "MEMORY:%p", 
+                                             ccc);
+               
+               if (!ccache_name) {
+                       talloc_free(ccc);
+                       (*error_string) = strerror(ENOMEM);
+                       return ENOMEM;
+               }
+       }
+
+       ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, ccache_name, 
+                             &ccc->ccache);
        if (ret) {
-               DEBUG(1,("failed to generate a new krb5 ccache (%s): %s\n", 
-                        ccache_name,
-                        smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
+               (*error_string) = talloc_asprintf(cred, "failed to resolve a krb5 ccache (%s): %s\n",
+                                                 ccache_name,
+                                                 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
+                                                                            ret, ccc));
                talloc_free(ccache_name);
                talloc_free(ccc);
                return ret;
        }
 
-       talloc_set_destructor(ccc, free_mccache);
-
-       cred->ccache = ccc;
-       talloc_steal(cred, ccc);
-       talloc_free(ccache_name);
+       if (strncasecmp(ccache_name, "MEMORY:", 7) == 0) {
+               talloc_set_destructor(ccc, free_mccache);
+       } else {
+               talloc_set_destructor(ccc, free_dccache);
+       }
 
-       if (_ccc) {
-               *_ccc = ccc;
+       if (must_free_cc_name) {
+               talloc_free(ccache_name);
        }
 
-       return ret;
+       *_ccc = ccc;
+
+       return 0;
 }
 
-int cli_credentials_get_ccache(struct cli_credentials *cred, 
-                              struct ccache_container **ccc)
+_PUBLIC_ int cli_credentials_get_named_ccache(struct cli_credentials *cred, 
+                                             struct tevent_context *event_ctx,
+                                             struct loadparm_context *lp_ctx,
+                                             char *ccache_name,
+                                             struct ccache_container **ccc,
+                                             const char **error_string)
 {
        krb5_error_code ret;
        
        if (cred->machine_account_pending) {
-               cli_credentials_set_machine_account(cred);
+               cli_credentials_set_machine_account(cred, lp_ctx);
        }
 
-       if (cred->ccache_obtained >= (MAX(cred->principal_obtained, 
-                                         cred->username_obtained))) {
+       if (cred->ccache_obtained >= cred->ccache_threshold && 
+           cred->ccache_obtained > CRED_UNINITIALISED) {
                *ccc = cred->ccache;
                return 0;
        }
        if (cli_credentials_is_anonymous(cred)) {
+               (*error_string) = "Cannot get anonymous kerberos credentials";
                return EINVAL;
        }
 
-       ret = cli_credentials_new_ccache(cred, NULL);
+       ret = cli_credentials_new_ccache(cred, event_ctx, lp_ctx, ccache_name, ccc, error_string);
        if (ret) {
                return ret;
        }
-       ret = kinit_to_ccache(cred, cred, cred->ccache->smb_krb5_context, cred->ccache->ccache);
+
+       ret = kinit_to_ccache(cred, cred, (*ccc)->smb_krb5_context, (*ccc)->ccache, error_string);
        if (ret) {
                return ret;
        }
-       ret = cli_credentials_set_from_ccache(cred, cred->principal_obtained);
 
+       ret = cli_credentials_set_from_ccache(cred, *ccc, 
+                                             (MAX(MAX(cred->principal_obtained, 
+                                                      cred->username_obtained), 
+                                                  cred->password_obtained)), error_string);
+       
+       cred->ccache = *ccc;
+       cred->ccache_obtained = cred->principal_obtained;
        if (ret) {
                return ret;
        }
-       *ccc = cred->ccache;
-       return ret;
+       cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
+       return 0;
+}
+
+_PUBLIC_ int cli_credentials_get_ccache(struct cli_credentials *cred, 
+                                       struct tevent_context *event_ctx,
+                                       struct loadparm_context *lp_ctx,
+                                       struct ccache_container **ccc,
+                                       const char **error_string)
+{
+       return cli_credentials_get_named_ccache(cred, event_ctx, lp_ctx, NULL, ccc, error_string);
+}
+
+void cli_credentials_invalidate_client_gss_creds(struct cli_credentials *cred, 
+                                                enum credentials_obtained obtained)
+{
+       /* If the caller just changed the username/password etc, then
+        * any cached credentials are now invalid */
+       if (obtained >= cred->client_gss_creds_obtained) {
+               if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
+                       talloc_unlink(cred, cred->client_gss_creds);
+                       cred->client_gss_creds = NULL;
+               }
+               cred->client_gss_creds_obtained = CRED_UNINITIALISED;
+       }
+       /* Now that we know that the data is 'this specified', then
+        * don't allow something less 'known' to be returned as a
+        * ccache.  Ie, if the username is on the commmand line, we
+        * don't want to later guess to use a file-based ccache */
+       if (obtained > cred->client_gss_creds_threshold) {
+               cred->client_gss_creds_threshold = obtained;
+       }
+}
+
+_PUBLIC_ void cli_credentials_invalidate_ccache(struct cli_credentials *cred, 
+                                      enum credentials_obtained obtained)
+{
+       /* If the caller just changed the username/password etc, then
+        * any cached credentials are now invalid */
+       if (obtained >= cred->ccache_obtained) {
+               if (cred->ccache_obtained > CRED_UNINITIALISED) {
+                       talloc_unlink(cred, cred->ccache);
+                       cred->ccache = NULL;
+               }
+               cred->ccache_obtained = CRED_UNINITIALISED;
+       }
+       /* Now that we know that the data is 'this specified', then
+        * don't allow something less 'known' to be returned as a
+        * ccache.  Ie, if the username is on the commmand line, we
+        * don't want to later guess to use a file-based ccache */
+       if (obtained > cred->ccache_threshold) {
+               cred->ccache_threshold  = obtained;
+       }
+
+       cli_credentials_invalidate_client_gss_creds(cred, 
+                                                   obtained);
 }
 
 static int free_gssapi_creds(struct gssapi_creds_container *gcc)
@@ -292,21 +382,27 @@ static int free_gssapi_creds(struct gssapi_creds_container *gcc)
        return 0;
 }
 
-int cli_credentials_get_client_gss_creds(struct cli_credentials *cred, 
-                                        struct gssapi_creds_container **_gcc) 
+_PUBLIC_ int cli_credentials_get_client_gss_creds(struct cli_credentials *cred, 
+                                                 struct tevent_context *event_ctx,
+                                                 struct loadparm_context *lp_ctx,
+                                                 struct gssapi_creds_container **_gcc,
+                                                 const char **error_string)
 {
        int ret = 0;
        OM_uint32 maj_stat, min_stat;
        struct gssapi_creds_container *gcc;
        struct ccache_container *ccache;
-       if (cred->client_gss_creds_obtained >= (MAX(cred->ccache_obtained, 
-                                            MAX(cred->principal_obtained, 
-                                                cred->username_obtained)))) {
+       gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
+       krb5_enctype *etypes = NULL;
+
+       if (cred->client_gss_creds_obtained >= cred->client_gss_creds_threshold && 
+           cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
                *_gcc = cred->client_gss_creds;
                return 0;
        }
-       ret = cli_credentials_get_ccache(cred, 
-                                        &ccache);
+
+       ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx, 
+                                        &ccache, error_string);
        if (ret) {
                DEBUG(1, ("Failed to get CCACHE for GSSAPI client: %s\n", error_message(ret)));
                return ret;
@@ -314,29 +410,80 @@ int cli_credentials_get_client_gss_creds(struct cli_credentials *cred,
 
        gcc = talloc(cred, struct gssapi_creds_container);
        if (!gcc) {
+               (*error_string) = error_message(ENOMEM);
                return ENOMEM;
        }
 
        maj_stat = gss_krb5_import_cred(&min_stat, ccache->ccache, NULL, NULL, 
                                        &gcc->creds);
        if (maj_stat) {
+               talloc_free(gcc);
                if (min_stat) {
                        ret = min_stat;
                } else {
                        ret = EINVAL;
                }
+               (*error_string) = error_message(ENOMEM);
+               return ret;
        }
-       if (ret == 0) {
-               cred->client_gss_creds_obtained = cred->ccache_obtained;
-               talloc_set_destructor(gcc, free_gssapi_creds);
-               cred->client_gss_creds = gcc;
-               *_gcc = gcc;
+
+       /*
+        * transfer the enctypes from the smb_krb5_context to the gssapi layer
+        *
+        * We use 'our' smb_krb5_context to do the AS-REQ and it is possible
+        * to configure the enctypes via the krb5.conf.
+        *
+        * And the gss_init_sec_context() creates it's own krb5_context and
+        * the TGS-REQ had all enctypes in it and only the ones configured
+        * and used for the AS-REQ, so it wasn't possible to disable the usage
+        * of AES keys.
+        */
+       min_stat = krb5_get_default_in_tkt_etypes(ccache->smb_krb5_context->krb5_context,
+                                                 &etypes);
+       if (min_stat == 0) {
+               OM_uint32 num_ktypes;
+
+               for (num_ktypes = 0; etypes[num_ktypes]; num_ktypes++);
+
+               maj_stat = gss_krb5_set_allowable_enctypes(&min_stat, gcc->creds,
+                                                          num_ktypes, etypes);
+               krb5_xfree (etypes);
+               if (maj_stat) {
+                       talloc_free(gcc);
+                       if (min_stat) {
+                               ret = min_stat;
+                       } else {
+                               ret = EINVAL;
+                       }
+                       (*error_string) = error_message(ENOMEM);
+                       return ret;
+               }
        }
-       return ret;
+
+       /* don't force GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG */
+       maj_stat = gss_set_cred_option(&min_stat, &gcc->creds,
+                                      GSS_KRB5_CRED_NO_CI_FLAGS_X,
+                                      &empty_buffer);
+       if (maj_stat) {
+               talloc_free(gcc);
+               if (min_stat) {
+                       ret = min_stat;
+               } else {
+                       ret = EINVAL;
+               }
+               (*error_string) = error_message(ENOMEM);
+               return ret;
+       }
+
+       cred->client_gss_creds_obtained = cred->ccache_obtained;
+       talloc_set_destructor(gcc, free_gssapi_creds);
+       cred->client_gss_creds = gcc;
+       *_gcc = gcc;
+       return 0;
 }
 
 /**
-   Set a gssapi cred_id_t into the credentails system. (Client case)
+   Set a gssapi cred_id_t into the credentials system. (Client case)
 
    This grabs the credentials both 'intact' and getting the krb5
    ccache out of it.  This routine can be generalised in future for
@@ -346,9 +493,12 @@ int cli_credentials_get_client_gss_creds(struct cli_credentials *cred,
    to the credentials system.
 */
 
-int cli_credentials_set_client_gss_creds(struct cli_credentials *cred, 
-                                        gss_cred_id_t gssapi_cred,
-                                        enum credentials_obtained obtained) 
+ int cli_credentials_set_client_gss_creds(struct cli_credentials *cred, 
+                                         struct tevent_context *event_ctx,
+                                         struct loadparm_context *lp_ctx,
+                                         gss_cred_id_t gssapi_cred,
+                                         enum credentials_obtained obtained,
+                                         const char **error_string)
 {
        int ret;
        OM_uint32 maj_stat, min_stat;
@@ -360,10 +510,11 @@ int cli_credentials_set_client_gss_creds(struct cli_credentials *cred,
 
        gcc = talloc(cred, struct gssapi_creds_container);
        if (!gcc) {
+               (*error_string) = error_message(ENOMEM);
                return ENOMEM;
        }
 
-       ret = cli_credentials_new_ccache(cred, &ccc);
+       ret = cli_credentials_new_ccache(cred, event_ctx, lp_ctx, NULL, &ccc, error_string);
        if (ret != 0) {
                return ret;
        }
@@ -376,15 +527,22 @@ int cli_credentials_set_client_gss_creds(struct cli_credentials *cred,
                } else {
                        ret = EINVAL;
                }
+               if (ret) {
+                       (*error_string) = error_message(ENOMEM);
+               }
        }
 
        if (ret == 0) {
-               ret = cli_credentials_set_from_ccache(cred, obtained);
+               ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
        }
+       cred->ccache = ccc;
+       cred->ccache_obtained = obtained;
        if (ret == 0) {
                gcc->creds = gssapi_cred;
                talloc_set_destructor(gcc, free_gssapi_creds);
                
+               /* set the clinet_gss_creds_obtained here, as it just 
+                  got set to UNINITIALISED by the calls above */
                cred->client_gss_creds_obtained = obtained;
                cred->client_gss_creds = gcc;
        }
@@ -395,7 +553,9 @@ int cli_credentials_set_client_gss_creds(struct cli_credentials *cred,
  * attached to this context.  If this hasn't been done or set before,
  * it will be generated from the password.
  */
-int cli_credentials_get_keytab(struct cli_credentials *cred, 
+_PUBLIC_ int cli_credentials_get_keytab(struct cli_credentials *cred, 
+                                       struct tevent_context *event_ctx,
+                              struct loadparm_context *lp_ctx,
                               struct keytab_container **_ktc)
 {
        krb5_error_code ret;
@@ -414,7 +574,8 @@ int cli_credentials_get_keytab(struct cli_credentials *cred,
                return EINVAL;
        }
 
-       ret = cli_credentials_get_krb5_context(cred, &smb_krb5_context);
+       ret = cli_credentials_get_krb5_context(cred, event_ctx, lp_ctx, 
+                                              &smb_krb5_context);
        if (ret) {
                return ret;
        }
@@ -447,7 +608,9 @@ int cli_credentials_get_keytab(struct cli_credentials *cred,
 /* Given the name of a keytab (presumably in the format
  * FILE:/etc/krb5.keytab), open it and attach it */
 
-int cli_credentials_set_keytab_name(struct cli_credentials *cred, 
+_PUBLIC_ int cli_credentials_set_keytab_name(struct cli_credentials *cred, 
+                                            struct tevent_context *event_ctx,
+                                   struct loadparm_context *lp_ctx,
                                    const char *keytab_name, 
                                    enum credentials_obtained obtained) 
 {
@@ -460,7 +623,7 @@ int cli_credentials_set_keytab_name(struct cli_credentials *cred,
                return 0;
        }
 
-       ret = cli_credentials_get_krb5_context(cred, &smb_krb5_context);
+       ret = cli_credentials_get_krb5_context(cred, event_ctx, lp_ctx, &smb_krb5_context);
        if (ret) {
                return ret;
        }
@@ -485,7 +648,9 @@ int cli_credentials_set_keytab_name(struct cli_credentials *cred,
        return ret;
 }
 
-int cli_credentials_update_keytab(struct cli_credentials *cred) 
+_PUBLIC_ int cli_credentials_update_keytab(struct cli_credentials *cred, 
+                                          struct tevent_context *event_ctx,
+                                 struct loadparm_context *lp_ctx) 
 {
        krb5_error_code ret;
        struct keytab_container *ktc;
@@ -498,7 +663,7 @@ int cli_credentials_update_keytab(struct cli_credentials *cred)
                return ENOMEM;
        }
 
-       ret = cli_credentials_get_krb5_context(cred, &smb_krb5_context);
+       ret = cli_credentials_get_krb5_context(cred, event_ctx, lp_ctx, &smb_krb5_context);
        if (ret) {
                talloc_free(mem_ctx);
                return ret;
@@ -506,7 +671,7 @@ int cli_credentials_update_keytab(struct cli_credentials *cred)
 
        enctype_strings = cli_credentials_get_enctype_strings(cred);
        
-       ret = cli_credentials_get_keytab(cred, &ktc);
+       ret = cli_credentials_get_keytab(cred, event_ctx, lp_ctx, &ktc);
        if (ret != 0) {
                talloc_free(mem_ctx);
                return ret;
@@ -520,7 +685,9 @@ int cli_credentials_update_keytab(struct cli_credentials *cred)
 
 /* Get server gss credentials (in gsskrb5, this means the keytab) */
 
-int cli_credentials_get_server_gss_creds(struct cli_credentials *cred, 
+_PUBLIC_ int cli_credentials_get_server_gss_creds(struct cli_credentials *cred, 
+                                                 struct tevent_context *event_ctx,
+                                        struct loadparm_context *lp_ctx,
                                         struct gssapi_creds_container **_gcc) 
 {
        int ret = 0;
@@ -530,6 +697,7 @@ int cli_credentials_get_server_gss_creds(struct cli_credentials *cred,
        struct smb_krb5_context *smb_krb5_context;
        TALLOC_CTX *mem_ctx;
        krb5_principal princ;
+       const char *error_string;
 
        if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained, 
                                                    MAX(cred->principal_obtained, 
@@ -538,13 +706,12 @@ int cli_credentials_get_server_gss_creds(struct cli_credentials *cred,
                return 0;
        }
 
-       ret = cli_credentials_get_krb5_context(cred, &smb_krb5_context);
+       ret = cli_credentials_get_krb5_context(cred, event_ctx, lp_ctx, &smb_krb5_context);
        if (ret) {
                return ret;
        }
 
-       ret = cli_credentials_get_keytab(cred, 
-                                        &ktc);
+       ret = cli_credentials_get_keytab(cred, event_ctx, lp_ctx, &ktc);
        if (ret) {
                DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret)));
                return ret;
@@ -555,11 +722,10 @@ int cli_credentials_get_server_gss_creds(struct cli_credentials *cred,
                return ENOMEM;
        }
 
-       ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ);
+       ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ, &error_string);
        if (ret) {
                DEBUG(1,("cli_credentials_get_server_gss_creds: makeing krb5 principal failed (%s)\n",
-                        smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
-                                                   ret, mem_ctx)));
+                        error_string));
                talloc_free(mem_ctx);
                return ret;
        }
@@ -594,7 +760,7 @@ int cli_credentials_get_server_gss_creds(struct cli_credentials *cred,
  * Set Kerberos KVNO
  */
 
-void cli_credentials_set_kvno(struct cli_credentials *cred,
+_PUBLIC_ void cli_credentials_set_kvno(struct cli_credentials *cred,
                              int kvno)
 {
        cred->kvno = kvno;
@@ -604,7 +770,7 @@ void cli_credentials_set_kvno(struct cli_credentials *cred,
  * Return Kerberos KVNO
  */
 
-int cli_credentials_get_kvno(struct cli_credentials *cred)
+_PUBLIC_ int cli_credentials_get_kvno(struct cli_credentials *cred)
 {
        return cred->kvno;
 }
@@ -630,9 +796,48 @@ const char *cli_credentials_get_salt_principal(struct cli_credentials *cred)
        return cred->salt_principal;
 }
 
-void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal) 
+_PUBLIC_ void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal) 
 {
+       talloc_free(cred->salt_principal);
        cred->salt_principal = talloc_strdup(cred, principal);
 }
 
+/* The 'impersonate_principal' is used to allow on Kerberos principal
+ * (and it's associated keytab etc) to impersonate another.  The
+ * ability to do this is controlled by the KDC, but it is generally
+ * permitted to impersonate anyone to yourself.  This allows any
+ * member of the domain to get the groups of a user.  This is also
+ * known as S4U2Self */
+
+const char *cli_credentials_get_impersonate_principal(struct cli_credentials *cred)
+{
+       return cred->impersonate_principal;
+}
+
+_PUBLIC_ void cli_credentials_set_impersonate_principal(struct cli_credentials *cred, const char *principal)
+{
+       talloc_free(cred->impersonate_principal);
+       cred->impersonate_principal = talloc_strdup(cred, principal);
+}
+
+/* when impersonating for S4U2Self we need to set the target principal
+ * to ourself, as otherwise we would need additional rights.
+ * Similarly, we may only be authorized to do general impersonation to
+ * some particular services.
+ *
+ * Likewise, password changes typically require a ticket to kpasswd/realm directly, not via a TGT
+ *
+ * NULL means that tickets will be obtained for the krbtgt service.
+*/
+
+const char *cli_credentials_get_target_service(struct cli_credentials *cred)
+{
+       return cred->target_service;
+}
+
+_PUBLIC_ void cli_credentials_set_target_service(struct cli_credentials *cred, const char *target_service)
+{
+       talloc_free(cred->target_service);
+       cred->target_service = talloc_strdup(cred, target_service);
+}