s4:credentials Add the functions needed to do S4U2Self with cli_credentials
[bbaumbach/samba-autobuild/.git] / source4 / auth / credentials / credentials_krb5.c
index cfdc2e3f5a392e16ee36852d5d638bfd5c538fce..1e0db3cb36f35a775bf1e4d2414179df01b5b1de 100644 (file)
 #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) 
 {
@@ -38,8 +40,8 @@ int cli_credentials_get_krb5_context(struct cli_credentials *cred,
                return 0;
        }
 
-       ret = smb_krb5_init_context(cred, cli_credentials_get_event_context(cred)
-                                   lp_ctx, &cred->smb_krb5_context);
+       ret = smb_krb5_init_context(cred, event_ctx, lp_ctx
+                                   &cred->smb_krb5_context);
        if (ret) {
                cred->smb_krb5_context = NULL;
                return ret;
@@ -52,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)) {
@@ -63,14 +65,14 @@ NTSTATUS cli_credentials_set_krb5_context(struct cli_credentials *cred,
 }
 
 static int cli_credentials_set_from_ccache(struct cli_credentials *cred, 
-                                   struct ccache_container *ccache,
-                                   enum credentials_obtained obtained)
+                                          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;
@@ -80,25 +82,20 @@ static int cli_credentials_set_from_ccache(struct cli_credentials *cred,
                                    ccache->ccache, &princ);
 
        if (ret) {
-               char *err_mess = smb_get_krb5_error_message(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(ccache->smb_krb5_context->krb5_context, princ, &name);
        if (ret) {
-               char *err_mess = smb_get_krb5_error_message(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(ccache->smb_krb5_context->krb5_context, princ);
-
        cli_credentials_set_principal(cred, name, obtained);
 
        free(name);
@@ -126,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;
@@ -139,34 +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, global_loadparm
+       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;
                }
@@ -177,17 +182,19 @@ 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);
 
-       ret = cli_credentials_set_from_ccache(cred, ccc, obtained);
+       ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
 
        if (ret) {
+               (*error_string) = error_message(ret);
                return ret;
        }
 
@@ -201,61 +208,83 @@ int cli_credentials_set_ccache(struct cli_credentials *cred,
 
 
 static int cli_credentials_new_ccache(struct cli_credentials *cred, 
-                                     struct ccache_container **_ccc)
+                                     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;
        struct ccache_container *ccc = talloc(cred, struct ccache_container);
-       char *ccache_name;
        if (!ccc) {
                return ENOMEM;
        }
 
-       ccache_name = talloc_asprintf(ccc, "MEMORY:%p", 
-                                     ccc);
-
-       if (!ccache_name) {
-               talloc_free(ccc);
-               return ENOMEM;
-       }
-
-       ret = cli_credentials_get_krb5_context(cred, global_loadparm, 
+       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;
        }
 
+       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);
+       if (strncasecmp(ccache_name, "MEMORY:", 7) == 0) {
+               talloc_set_destructor(ccc, free_mccache);
+       } else {
+               talloc_set_destructor(ccc, free_dccache);
+       }
 
-       talloc_free(ccache_name);
+       if (must_free_cc_name) {
+               talloc_free(ccache_name);
+       }
 
        *_ccc = ccc;
 
-       return ret;
+       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 >= cred->ccache_threshold && 
@@ -264,15 +293,16 @@ int cli_credentials_get_ccache(struct cli_credentials *cred,
                return 0;
        }
        if (cli_credentials_is_anonymous(cred)) {
+               (*error_string) = "Cannot get anonymous kerberos credentials";
                return EINVAL;
        }
 
-       ret = cli_credentials_new_ccache(cred, ccc);
+       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, (*ccc)->smb_krb5_context, (*ccc)->ccache);
+       ret = kinit_to_ccache(cred, cred, (*ccc)->smb_krb5_context, (*ccc)->ccache, error_string);
        if (ret) {
                return ret;
        }
@@ -280,7 +310,7 @@ int cli_credentials_get_ccache(struct cli_credentials *cred,
        ret = cli_credentials_set_from_ccache(cred, *ccc, 
                                              (MAX(MAX(cred->principal_obtained, 
                                                       cred->username_obtained), 
-                                                  cred->password_obtained)));
+                                                  cred->password_obtained)), error_string);
        
        cred->ccache = *ccc;
        cred->ccache_obtained = cred->principal_obtained;
@@ -288,7 +318,16 @@ int cli_credentials_get_ccache(struct cli_credentials *cred,
                return ret;
        }
        cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
-       return ret;
+       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, 
@@ -312,7 +351,7 @@ void cli_credentials_invalidate_client_gss_creds(struct cli_credentials *cred,
        }
 }
 
-void cli_credentials_invalidate_ccache(struct cli_credentials *cred, 
+_PUBLIC_ void cli_credentials_invalidate_ccache(struct cli_credentials *cred, 
                                       enum credentials_obtained obtained)
 {
        /* If the caller just changed the username/password etc, then
@@ -343,20 +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;
+       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;
@@ -364,25 +410,76 @@ 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;
 }
 
 /**
@@ -396,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;
@@ -410,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;
        }
@@ -426,10 +527,13 @@ 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, ccc, obtained);
+               ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
        }
        cred->ccache = ccc;
        cred->ccache_obtained = obtained;
@@ -449,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;
@@ -468,7 +574,7 @@ int cli_credentials_get_keytab(struct cli_credentials *cred,
                return EINVAL;
        }
 
-       ret = cli_credentials_get_krb5_context(cred, global_loadparm
+       ret = cli_credentials_get_krb5_context(cred, event_ctx, lp_ctx
                                               &smb_krb5_context);
        if (ret) {
                return ret;
@@ -502,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) 
 {
@@ -515,7 +623,7 @@ int cli_credentials_set_keytab_name(struct cli_credentials *cred,
                return 0;
        }
 
-       ret = cli_credentials_get_krb5_context(cred, global_loadparm, &smb_krb5_context);
+       ret = cli_credentials_get_krb5_context(cred, event_ctx, lp_ctx, &smb_krb5_context);
        if (ret) {
                return ret;
        }
@@ -540,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;
@@ -553,7 +663,7 @@ int cli_credentials_update_keytab(struct cli_credentials *cred)
                return ENOMEM;
        }
 
-       ret = cli_credentials_get_krb5_context(cred, global_loadparm, &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;
@@ -561,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;
@@ -575,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;
@@ -585,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, 
@@ -593,13 +706,12 @@ int cli_credentials_get_server_gss_creds(struct cli_credentials *cred,
                return 0;
        }
 
-       ret = cli_credentials_get_krb5_context(cred, global_loadparm, &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;
@@ -610,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;
        }
@@ -649,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;
@@ -659,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;
 }
@@ -685,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);
+}