#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)
{
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;
* 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)) {
}
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;
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);
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;
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;
}
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;
}
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 &&
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;
}
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;
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,
}
}
-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
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;
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;
}
/**
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;
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;
}
} 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;
* 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;
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;
/* 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)
{
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;
}
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;
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;
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;
/* 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;
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,
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;
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;
}
* 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;
* 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;
}
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);
+}