summaryrefslogtreecommitdiffstats
path: root/auth/credentials
diff options
context:
space:
mode:
Diffstat (limited to 'auth/credentials')
-rw-r--r--auth/credentials/credentials.c1913
-rw-r--r--auth/credentials/credentials.h353
-rw-r--r--auth/credentials/credentials_cmdline.c73
-rw-r--r--auth/credentials/credentials_internal.h136
-rw-r--r--auth/credentials/credentials_krb5.c1530
-rw-r--r--auth/credentials/credentials_krb5.h45
-rw-r--r--auth/credentials/credentials_ntlm.c555
-rw-r--r--auth/credentials/credentials_secrets.c485
-rw-r--r--auth/credentials/pycredentials.c1626
-rw-r--r--auth/credentials/pycredentials.h40
-rw-r--r--auth/credentials/samba-credentials.pc.in12
-rwxr-xr-xauth/credentials/tests/bind.py261
-rw-r--r--auth/credentials/tests/test_creds.c311
-rw-r--r--auth/credentials/wscript_build43
14 files changed, 7383 insertions, 0 deletions
diff --git a/auth/credentials/credentials.c b/auth/credentials/credentials.c
new file mode 100644
index 0000000..67644e8
--- /dev/null
+++ b/auth/credentials/credentials.c
@@ -0,0 +1,1913 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ User credentials handling
+
+ Copyright (C) Jelmer Vernooij 2005
+ Copyright (C) Tim Potter 2001
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
+
+ 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 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "librpc/gen_ndr/samr.h" /* for struct samrPassword */
+#include "auth/credentials/credentials.h"
+#include "auth/credentials/credentials_internal.h"
+#include "auth/gensec/gensec.h"
+#include "libcli/auth/libcli_auth.h"
+#include "tevent.h"
+#include "param/param.h"
+#include "system/filesys.h"
+#include "system/passwd.h"
+
+/**
+ * Create a new credentials structure
+ * @param mem_ctx TALLOC_CTX parent for credentials structure
+ */
+_PUBLIC_ struct cli_credentials *cli_credentials_init(TALLOC_CTX *mem_ctx)
+{
+ struct cli_credentials *cred = talloc_zero(mem_ctx, struct cli_credentials);
+ if (cred == NULL) {
+ return cred;
+ }
+
+ cred->winbind_separator = '\\';
+
+ cred->kerberos_state = CRED_USE_KERBEROS_DESIRED;
+
+ cred->signing_state = SMB_SIGNING_DEFAULT;
+
+ /*
+ * The default value of lpcfg_client_ipc_signing() is REQUIRED, so use
+ * the same value here.
+ */
+ cred->ipc_signing_state = SMB_SIGNING_REQUIRED;
+ cred->encryption_state = SMB_ENCRYPTION_DEFAULT;
+
+ return cred;
+}
+
+_PUBLIC_
+struct cli_credentials *cli_credentials_init_server(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx)
+{
+ struct cli_credentials *server_creds = NULL;
+ NTSTATUS status;
+ bool ok;
+
+ server_creds = cli_credentials_init(mem_ctx);
+ if (server_creds == NULL) {
+ return NULL;
+ }
+
+ ok = cli_credentials_set_conf(server_creds, lp_ctx);
+ if (!ok) {
+ TALLOC_FREE(server_creds);
+ return NULL;
+ }
+
+ status = cli_credentials_set_machine_account(server_creds, lp_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to obtain server credentials: %s\n",
+ nt_errstr(status)));
+ TALLOC_FREE(server_creds);
+ return NULL;
+ }
+
+ return server_creds;
+}
+
+_PUBLIC_ void cli_credentials_set_callback_data(struct cli_credentials *cred,
+ void *callback_data)
+{
+ cred->priv_data = callback_data;
+}
+
+_PUBLIC_ void *_cli_credentials_callback_data(struct cli_credentials *cred)
+{
+ return cred->priv_data;
+}
+
+/**
+ * Create a new anonymous credential
+ * @param mem_ctx TALLOC_CTX parent for credentials structure
+ */
+_PUBLIC_ struct cli_credentials *cli_credentials_init_anon(TALLOC_CTX *mem_ctx)
+{
+ struct cli_credentials *anon_credentials;
+
+ anon_credentials = cli_credentials_init(mem_ctx);
+ cli_credentials_set_anonymous(anon_credentials);
+
+ return anon_credentials;
+}
+
+_PUBLIC_ bool cli_credentials_set_kerberos_state(struct cli_credentials *creds,
+ enum credentials_use_kerberos kerberos_state,
+ enum credentials_obtained obtained)
+{
+ if (obtained >= creds->kerberos_state_obtained) {
+ creds->kerberos_state = kerberos_state;
+ creds->kerberos_state_obtained = obtained;
+
+ return true;
+ }
+
+ return false;
+}
+
+_PUBLIC_ void cli_credentials_set_forced_sasl_mech(struct cli_credentials *creds,
+ const char *sasl_mech)
+{
+ TALLOC_FREE(creds->forced_sasl_mech);
+ creds->forced_sasl_mech = talloc_strdup(creds, sasl_mech);
+}
+
+_PUBLIC_ void cli_credentials_set_krb_forwardable(struct cli_credentials *creds,
+ enum credentials_krb_forwardable krb_forwardable)
+{
+ creds->krb_forwardable = krb_forwardable;
+}
+
+_PUBLIC_ enum credentials_use_kerberos cli_credentials_get_kerberos_state(struct cli_credentials *creds)
+{
+ return creds->kerberos_state;
+}
+
+_PUBLIC_ const char *cli_credentials_get_forced_sasl_mech(struct cli_credentials *creds)
+{
+ return creds->forced_sasl_mech;
+}
+
+_PUBLIC_ enum credentials_krb_forwardable cli_credentials_get_krb_forwardable(struct cli_credentials *creds)
+{
+ return creds->krb_forwardable;
+}
+
+_PUBLIC_ bool cli_credentials_set_gensec_features(struct cli_credentials *creds,
+ uint32_t gensec_features,
+ enum credentials_obtained obtained)
+{
+ if (obtained >= creds->gensec_features_obtained) {
+ creds->gensec_features_obtained = obtained;
+ creds->gensec_features = gensec_features;
+
+ return true;
+ }
+
+ return false;
+}
+
+_PUBLIC_ uint32_t cli_credentials_get_gensec_features(struct cli_credentials *creds)
+{
+ return creds->gensec_features;
+}
+
+
+/**
+ * Obtain the username for this credentials context.
+ * @param cred credentials context
+ * @retval The username set on this context.
+ * @note Return value will never be NULL except by programmer error.
+ */
+_PUBLIC_ const char *cli_credentials_get_username(struct cli_credentials *cred)
+{
+ if (cred->machine_account_pending) {
+ cli_credentials_set_machine_account(cred,
+ cred->machine_account_pending_lp_ctx);
+ }
+
+ if (cred->username_obtained == CRED_CALLBACK &&
+ !cred->callback_running) {
+ cred->callback_running = true;
+ cred->username = cred->username_cb(cred);
+ cred->callback_running = false;
+ if (cred->username_obtained == CRED_CALLBACK) {
+ cred->username_obtained = CRED_CALLBACK_RESULT;
+ cli_credentials_invalidate_ccache(cred, cred->username_obtained);
+ }
+ }
+
+ return cred->username;
+}
+
+/**
+ * @brief Obtain the username for this credentials context.
+ *
+ * @param[in] cred The credential context.
+ *
+ * @param[in] obtained A pointer to store the obtained information.
+ *
+ * return The user name or NULL if an error occured.
+ */
+_PUBLIC_ const char *
+cli_credentials_get_username_and_obtained(struct cli_credentials *cred,
+ enum credentials_obtained *obtained)
+{
+ if (obtained != NULL) {
+ *obtained = cred->username_obtained;
+ }
+
+ return cli_credentials_get_username(cred);
+}
+
+_PUBLIC_ bool cli_credentials_set_username(struct cli_credentials *cred,
+ const char *val, enum credentials_obtained obtained)
+{
+ if (obtained >= cred->username_obtained) {
+ cred->username = talloc_strdup(cred, val);
+ cred->username_obtained = obtained;
+ cli_credentials_invalidate_ccache(cred, cred->username_obtained);
+ return true;
+ }
+
+ return false;
+}
+
+_PUBLIC_ bool cli_credentials_set_username_callback(struct cli_credentials *cred,
+ const char *(*username_cb) (struct cli_credentials *))
+{
+ if (cred->username_obtained < CRED_CALLBACK) {
+ cred->username_cb = username_cb;
+ cred->username_obtained = CRED_CALLBACK;
+ return true;
+ }
+
+ return false;
+}
+
+_PUBLIC_ bool cli_credentials_set_bind_dn(struct cli_credentials *cred,
+ const char *bind_dn)
+{
+ cred->bind_dn = talloc_strdup(cred, bind_dn);
+ return true;
+}
+
+/**
+ * Obtain the BIND DN for this credentials context.
+ * @param cred credentials context
+ * @retval The username set on this context.
+ * @note Return value will be NULL if not specified explictly
+ */
+_PUBLIC_ const char *cli_credentials_get_bind_dn(struct cli_credentials *cred)
+{
+ return cred->bind_dn;
+}
+
+
+/**
+ * Obtain the client principal for this credentials context.
+ * @param cred credentials context
+ * @retval The username set on this context.
+ * @note Return value will never be NULL except by programmer error.
+ */
+_PUBLIC_ char *cli_credentials_get_principal_and_obtained(struct cli_credentials *cred, TALLOC_CTX *mem_ctx, enum credentials_obtained *obtained)
+{
+ if (cred->machine_account_pending) {
+ cli_credentials_set_machine_account(cred,
+ cred->machine_account_pending_lp_ctx);
+ }
+
+ if (cred->principal_obtained == CRED_CALLBACK &&
+ !cred->callback_running) {
+ cred->callback_running = true;
+ cred->principal = cred->principal_cb(cred);
+ cred->callback_running = false;
+ if (cred->principal_obtained == CRED_CALLBACK) {
+ cred->principal_obtained = CRED_CALLBACK_RESULT;
+ cli_credentials_invalidate_ccache(cred, cred->principal_obtained);
+ }
+ }
+
+ if (cred->principal_obtained < cred->username_obtained
+ || cred->principal_obtained < MAX(cred->domain_obtained, cred->realm_obtained)) {
+ const char *effective_username = NULL;
+ const char *effective_realm = NULL;
+ enum credentials_obtained effective_obtained;
+
+ effective_username = cli_credentials_get_username(cred);
+ if (effective_username == NULL || strlen(effective_username) == 0) {
+ *obtained = cred->username_obtained;
+ return NULL;
+ }
+
+ if (cred->domain_obtained > cred->realm_obtained) {
+ effective_realm = cli_credentials_get_domain(cred);
+ effective_obtained = MIN(cred->domain_obtained,
+ cred->username_obtained);
+ } else {
+ effective_realm = cli_credentials_get_realm(cred);
+ effective_obtained = MIN(cred->realm_obtained,
+ cred->username_obtained);
+ }
+
+ if (effective_realm == NULL || strlen(effective_realm) == 0) {
+ effective_realm = cli_credentials_get_domain(cred);
+ effective_obtained = MIN(cred->domain_obtained,
+ cred->username_obtained);
+ }
+
+ if (effective_realm != NULL && strlen(effective_realm) != 0) {
+ *obtained = effective_obtained;
+ return talloc_asprintf(mem_ctx, "%s@%s",
+ effective_username,
+ effective_realm);
+ }
+ }
+ *obtained = cred->principal_obtained;
+ return talloc_strdup(mem_ctx, cred->principal);
+}
+
+/**
+ * Obtain the client principal for this credentials context.
+ * @param cred credentials context
+ * @retval The username set on this context.
+ * @note Return value will never be NULL except by programmer error.
+ */
+_PUBLIC_ char *cli_credentials_get_principal(struct cli_credentials *cred, TALLOC_CTX *mem_ctx)
+{
+ enum credentials_obtained obtained;
+ return cli_credentials_get_principal_and_obtained(cred, mem_ctx, &obtained);
+}
+
+_PUBLIC_ bool cli_credentials_set_principal(struct cli_credentials *cred,
+ const char *val,
+ enum credentials_obtained obtained)
+{
+ if (obtained >= cred->principal_obtained) {
+ cred->principal = talloc_strdup(cred, val);
+ if (cred->principal == NULL) {
+ return false;
+ }
+ cred->principal_obtained = obtained;
+
+ cli_credentials_invalidate_ccache(cred, cred->principal_obtained);
+ return true;
+ }
+
+ return false;
+}
+
+/* Set a callback to get the principal. This could be a popup dialog,
+ * a terminal prompt or similar. */
+_PUBLIC_ bool cli_credentials_set_principal_callback(struct cli_credentials *cred,
+ const char *(*principal_cb) (struct cli_credentials *))
+{
+ if (cred->principal_obtained < CRED_CALLBACK) {
+ cred->principal_cb = principal_cb;
+ cred->principal_obtained = CRED_CALLBACK;
+ return true;
+ }
+
+ return false;
+}
+
+/* Some of our tools are 'anonymous by default'. This is a single
+ * function to determine if authentication has been explicitly
+ * requested */
+
+_PUBLIC_ bool cli_credentials_authentication_requested(struct cli_credentials *cred)
+{
+ uint32_t gensec_features = 0;
+
+ if (cred->bind_dn) {
+ return true;
+ }
+
+ /*
+ * If we forced the mech we clearly want authentication. E.g. to use
+ * SASL/EXTERNAL which has no credentials.
+ */
+ if (cred->forced_sasl_mech) {
+ return true;
+ }
+
+ if (cli_credentials_is_anonymous(cred)){
+ return false;
+ }
+
+ if (cred->principal_obtained >= CRED_SPECIFIED) {
+ return true;
+ }
+ if (cred->username_obtained >= CRED_SPECIFIED) {
+ return true;
+ }
+
+ if (cli_credentials_get_kerberos_state(cred) == CRED_USE_KERBEROS_REQUIRED) {
+ return true;
+ }
+
+ gensec_features = cli_credentials_get_gensec_features(cred);
+ if (gensec_features & GENSEC_FEATURE_NTLM_CCACHE) {
+ return true;
+ }
+
+ if (gensec_features & GENSEC_FEATURE_SIGN) {
+ return true;
+ }
+
+ if (gensec_features & GENSEC_FEATURE_SEAL) {
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Obtain the password for this credentials context.
+ * @param cred credentials context
+ * @retval If set, the cleartext password, otherwise NULL
+ */
+_PUBLIC_ const char *cli_credentials_get_password(struct cli_credentials *cred)
+{
+ if (cred->machine_account_pending) {
+ cli_credentials_set_machine_account(cred,
+ cred->machine_account_pending_lp_ctx);
+ }
+
+ if (cred->password_obtained == CRED_CALLBACK &&
+ !cred->callback_running &&
+ !cred->password_will_be_nt_hash) {
+ cred->callback_running = true;
+ cred->password = cred->password_cb(cred);
+ cred->callback_running = false;
+ if (cred->password_obtained == CRED_CALLBACK) {
+ cred->password_obtained = CRED_CALLBACK_RESULT;
+ cli_credentials_invalidate_ccache(cred, cred->password_obtained);
+ }
+ }
+
+ return cred->password;
+}
+
+/**
+ * @brief Obtain the password for this credentials context.
+ *
+ * @param[in] cred The credential context.
+ *
+ * @param[in] obtained A pointer to store the obtained information.
+ *
+ * return The user name or NULL if an error occured.
+ */
+_PUBLIC_ const char *
+cli_credentials_get_password_and_obtained(struct cli_credentials *cred,
+ enum credentials_obtained *obtained)
+{
+ if (obtained != NULL) {
+ *obtained = cred->password_obtained;
+ }
+
+ return cli_credentials_get_password(cred);
+}
+
+/* Set a password on the credentials context, including an indication
+ * of 'how' the password was obtained */
+
+_PUBLIC_ bool cli_credentials_set_password(struct cli_credentials *cred,
+ const char *val,
+ enum credentials_obtained obtained)
+{
+ if (obtained >= cred->password_obtained) {
+
+ cred->lm_response = data_blob_null;
+ cred->nt_response = data_blob_null;
+ cred->nt_hash = NULL;
+ cred->password = NULL;
+
+ cli_credentials_invalidate_ccache(cred, obtained);
+
+ cred->password_tries = 0;
+
+ if (val == NULL) {
+ cred->password_obtained = obtained;
+ return true;
+ }
+
+ if (cred->password_will_be_nt_hash) {
+ struct samr_Password *nt_hash = NULL;
+ size_t val_len = strlen(val);
+ size_t converted;
+
+ nt_hash = talloc(cred, struct samr_Password);
+ if (nt_hash == NULL) {
+ return false;
+ }
+
+ converted = strhex_to_str((char *)nt_hash->hash,
+ sizeof(nt_hash->hash),
+ val, val_len);
+ if (converted != sizeof(nt_hash->hash)) {
+ TALLOC_FREE(nt_hash);
+ return false;
+ }
+
+ cred->nt_hash = nt_hash;
+ cred->password_obtained = obtained;
+ return true;
+ }
+
+ cred->password = talloc_strdup(cred, val);
+ if (cred->password == NULL) {
+ return false;
+ }
+
+ /* Don't print the actual password in talloc memory dumps */
+ talloc_set_name_const(cred->password,
+ "password set via cli_credentials_set_password");
+ cred->password_obtained = obtained;
+
+ return true;
+ }
+
+ return false;
+}
+
+_PUBLIC_ bool cli_credentials_set_password_callback(struct cli_credentials *cred,
+ const char *(*password_cb) (struct cli_credentials *))
+{
+ if (cred->password_obtained < CRED_CALLBACK) {
+ cred->password_tries = 3;
+ cred->password_cb = password_cb;
+ cred->password_obtained = CRED_CALLBACK;
+ cli_credentials_invalidate_ccache(cred, cred->password_obtained);
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Obtain the 'old' password for this credentials context (used for join accounts).
+ * @param cred credentials context
+ * @retval If set, the cleartext password, otherwise NULL
+ */
+_PUBLIC_ const char *cli_credentials_get_old_password(struct cli_credentials *cred)
+{
+ if (cred->machine_account_pending) {
+ cli_credentials_set_machine_account(cred,
+ cred->machine_account_pending_lp_ctx);
+ }
+
+ return cred->old_password;
+}
+
+_PUBLIC_ bool cli_credentials_set_old_password(struct cli_credentials *cred,
+ const char *val,
+ enum credentials_obtained obtained)
+{
+ cred->old_password = talloc_strdup(cred, val);
+ if (cred->old_password) {
+ /* Don't print the actual password in talloc memory dumps */
+ talloc_set_name_const(cred->old_password, "password set via cli_credentials_set_old_password");
+ }
+ cred->old_nt_hash = NULL;
+ return true;
+}
+
+/**
+ * Obtain the password, in the form MD4(unicode(password)) for this credentials context.
+ *
+ * Sometimes we only have this much of the password, while the rest of
+ * the time this call avoids calling E_md4hash themselves.
+ *
+ * @param cred credentials context
+ * @retval If set, the cleartext password, otherwise NULL
+ */
+_PUBLIC_ struct samr_Password *cli_credentials_get_nt_hash(struct cli_credentials *cred,
+ TALLOC_CTX *mem_ctx)
+{
+ enum credentials_obtained password_obtained;
+ enum credentials_obtained ccache_threshold;
+ enum credentials_obtained client_gss_creds_threshold;
+ bool password_is_nt_hash;
+ const char *password = NULL;
+ struct samr_Password *nt_hash = NULL;
+
+ if (cred->nt_hash != NULL) {
+ /*
+ * If we already have a hash it's easy.
+ */
+ goto return_hash;
+ }
+
+ /*
+ * This is a bit tricky, with password_will_be_nt_hash
+ * we still need to get the value via the password_callback
+ * but if we did that we should not remember it's state
+ * in the long run so we need to undo it.
+ */
+
+ password_obtained = cred->password_obtained;
+ ccache_threshold = cred->ccache_threshold;
+ client_gss_creds_threshold = cred->client_gss_creds_threshold;
+ password_is_nt_hash = cred->password_will_be_nt_hash;
+
+ cred->password_will_be_nt_hash = false;
+ password = cli_credentials_get_password(cred);
+
+ cred->password_will_be_nt_hash = password_is_nt_hash;
+ if (password_is_nt_hash && password_obtained == CRED_CALLBACK) {
+ /*
+ * We got the nt_hash as string via the callback,
+ * so we need to undo the state change.
+ *
+ * And also don't remember it as plaintext password.
+ */
+ cred->client_gss_creds_threshold = client_gss_creds_threshold;
+ cred->ccache_threshold = ccache_threshold;
+ cred->password_obtained = password_obtained;
+ cred->password = NULL;
+ }
+
+ if (password == NULL) {
+ return NULL;
+ }
+
+ nt_hash = talloc(cred, struct samr_Password);
+ if (nt_hash == NULL) {
+ return NULL;
+ }
+
+ if (password_is_nt_hash) {
+ size_t password_len = strlen(password);
+ size_t converted;
+
+ converted = strhex_to_str((char *)nt_hash->hash,
+ sizeof(nt_hash->hash),
+ password, password_len);
+ if (converted != sizeof(nt_hash->hash)) {
+ TALLOC_FREE(nt_hash);
+ return NULL;
+ }
+ } else {
+ E_md4hash(password, nt_hash->hash);
+ }
+
+ cred->nt_hash = nt_hash;
+ nt_hash = NULL;
+
+return_hash:
+ nt_hash = talloc(mem_ctx, struct samr_Password);
+ if (nt_hash == NULL) {
+ return NULL;
+ }
+
+ *nt_hash = *cred->nt_hash;
+
+ return nt_hash;
+}
+
+/**
+ * Obtain the old password, in the form MD4(unicode(password)) for this credentials context.
+ *
+ * Sometimes we only have this much of the password, while the rest of
+ * the time this call avoids calling E_md4hash themselves.
+ *
+ * @param cred credentials context
+ * @retval If set, the cleartext password, otherwise NULL
+ */
+_PUBLIC_ struct samr_Password *cli_credentials_get_old_nt_hash(struct cli_credentials *cred,
+ TALLOC_CTX *mem_ctx)
+{
+ const char *old_password = NULL;
+
+ if (cred->old_nt_hash != NULL) {
+ struct samr_Password *nt_hash = talloc(mem_ctx, struct samr_Password);
+ if (!nt_hash) {
+ return NULL;
+ }
+
+ *nt_hash = *cred->old_nt_hash;
+
+ return nt_hash;
+ }
+
+ old_password = cli_credentials_get_old_password(cred);
+ if (old_password) {
+ struct samr_Password *nt_hash = talloc(mem_ctx, struct samr_Password);
+ if (!nt_hash) {
+ return NULL;
+ }
+
+ E_md4hash(old_password, nt_hash->hash);
+
+ return nt_hash;
+ }
+
+ return NULL;
+}
+
+/**
+ * Obtain the 'short' or 'NetBIOS' domain for this credentials context.
+ * @param cred credentials context
+ * @retval The domain set on this context.
+ * @note Return value will never be NULL except by programmer error.
+ */
+_PUBLIC_ const char *cli_credentials_get_domain(struct cli_credentials *cred)
+{
+ if (cred->machine_account_pending) {
+ cli_credentials_set_machine_account(cred,
+ cred->machine_account_pending_lp_ctx);
+ }
+
+ if (cred->domain_obtained == CRED_CALLBACK &&
+ !cred->callback_running) {
+ cred->callback_running = true;
+ cred->domain = cred->domain_cb(cred);
+ cred->callback_running = false;
+ if (cred->domain_obtained == CRED_CALLBACK) {
+ cred->domain_obtained = CRED_CALLBACK_RESULT;
+ cli_credentials_invalidate_ccache(cred, cred->domain_obtained);
+ }
+ }
+
+ return cred->domain;
+}
+
+
+_PUBLIC_ bool cli_credentials_set_domain(struct cli_credentials *cred,
+ const char *val,
+ enum credentials_obtained obtained)
+{
+ if (obtained >= cred->domain_obtained) {
+ /* it is important that the domain be in upper case,
+ * particularly for the sensitive NTLMv2
+ * calculations */
+ cred->domain = strupper_talloc(cred, val);
+ cred->domain_obtained = obtained;
+ /* setting domain does not mean we have to invalidate ccache
+ * because domain in not used for Kerberos operations.
+ * If ccache invalidation is required, one will anyway specify
+ * a password to kinit, and that will force invalidation of the ccache
+ */
+ return true;
+ }
+
+ return false;
+}
+
+bool cli_credentials_set_domain_callback(struct cli_credentials *cred,
+ const char *(*domain_cb) (struct cli_credentials *))
+{
+ if (cred->domain_obtained < CRED_CALLBACK) {
+ cred->domain_cb = domain_cb;
+ cred->domain_obtained = CRED_CALLBACK;
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Obtain the Kerberos realm for this credentials context.
+ * @param cred credentials context
+ * @retval The realm set on this context.
+ * @note Return value will never be NULL except by programmer error.
+ */
+_PUBLIC_ const char *cli_credentials_get_realm(struct cli_credentials *cred)
+{
+ if (cred->machine_account_pending) {
+ cli_credentials_set_machine_account(cred,
+ cred->machine_account_pending_lp_ctx);
+ }
+
+ if (cred->realm_obtained == CRED_CALLBACK &&
+ !cred->callback_running) {
+ cred->callback_running = true;
+ cred->realm = cred->realm_cb(cred);
+ cred->callback_running = false;
+ if (cred->realm_obtained == CRED_CALLBACK) {
+ cred->realm_obtained = CRED_CALLBACK_RESULT;
+ cli_credentials_invalidate_ccache(cred, cred->realm_obtained);
+ }
+ }
+
+ return cred->realm;
+}
+
+/**
+ * Set the realm for this credentials context, and force it to
+ * uppercase for the sanity of our local kerberos libraries
+ */
+_PUBLIC_ bool cli_credentials_set_realm(struct cli_credentials *cred,
+ const char *val,
+ enum credentials_obtained obtained)
+{
+ if (obtained >= cred->realm_obtained) {
+ cred->realm = strupper_talloc(cred, val);
+ cred->realm_obtained = obtained;
+ cli_credentials_invalidate_ccache(cred, cred->realm_obtained);
+ return true;
+ }
+
+ return false;
+}
+
+bool cli_credentials_set_realm_callback(struct cli_credentials *cred,
+ const char *(*realm_cb) (struct cli_credentials *))
+{
+ if (cred->realm_obtained < CRED_CALLBACK) {
+ cred->realm_cb = realm_cb;
+ cred->realm_obtained = CRED_CALLBACK;
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Obtain the 'short' or 'NetBIOS' workstation name for this credentials context.
+ *
+ * @param cred credentials context
+ * @retval The workstation name set on this context.
+ * @note Return value will never be NULL except by programmer error.
+ */
+_PUBLIC_ const char *cli_credentials_get_workstation(struct cli_credentials *cred)
+{
+ if (cred->workstation_obtained == CRED_CALLBACK &&
+ !cred->callback_running) {
+ cred->callback_running = true;
+ cred->workstation = cred->workstation_cb(cred);
+ cred->callback_running = false;
+ if (cred->workstation_obtained == CRED_CALLBACK) {
+ cred->workstation_obtained = CRED_CALLBACK_RESULT;
+ }
+ }
+
+ return cred->workstation;
+}
+
+_PUBLIC_ bool cli_credentials_set_workstation(struct cli_credentials *cred,
+ const char *val,
+ enum credentials_obtained obtained)
+{
+ if (obtained >= cred->workstation_obtained) {
+ cred->workstation = talloc_strdup(cred, val);
+ cred->workstation_obtained = obtained;
+ return true;
+ }
+
+ return false;
+}
+
+bool cli_credentials_set_workstation_callback(struct cli_credentials *cred,
+ const char *(*workstation_cb) (struct cli_credentials *))
+{
+ if (cred->workstation_obtained < CRED_CALLBACK) {
+ cred->workstation_cb = workstation_cb;
+ cred->workstation_obtained = CRED_CALLBACK;
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Given a string, typically obtained from a -U argument, parse it into domain, username, realm and password fields
+ *
+ * The format accepted is [domain\\]user[%password] or user[@realm][%password]
+ *
+ * @param credentials Credentials structure on which to set the password
+ * @param data the string containing the username, password etc
+ * @param obtained This enum describes how 'specified' this password is
+ */
+
+_PUBLIC_ void cli_credentials_parse_string(struct cli_credentials *credentials, const char *data, enum credentials_obtained obtained)
+{
+ char *uname, *p;
+ char *uname_free = NULL;
+
+ if (strcmp("%",data) == 0) {
+ cli_credentials_set_anonymous(credentials);
+ return;
+ }
+
+ uname = talloc_strdup(credentials, data);
+ uname_free = uname;
+
+ if ((p = strchr_m(uname,'%'))) {
+ *p = 0;
+ cli_credentials_set_password(credentials, p+1, obtained);
+ }
+
+ if ((p = strchr_m(uname,'@'))) {
+ /*
+ * We also need to set username and domain
+ * in order to undo the effect of
+ * cli_credentials_guess().
+ */
+ cli_credentials_set_username(credentials, uname, obtained);
+ cli_credentials_set_domain(credentials, "", obtained);
+
+ cli_credentials_set_principal(credentials, uname, obtained);
+ *p = 0;
+ cli_credentials_set_realm(credentials, p+1, obtained);
+ TALLOC_FREE(uname_free);
+ return;
+ } else if ((p = strchr_m(uname,'\\'))
+ || (p = strchr_m(uname, '/'))
+ || (p = strchr_m(uname, credentials->winbind_separator)))
+ {
+ const char *domain = NULL;
+
+ domain = uname;
+ *p = 0;
+ uname = p+1;
+
+ if (obtained == credentials->realm_obtained &&
+ !strequal_m(credentials->domain, domain))
+ {
+ /*
+ * We need to undo a former set with the same level
+ * in order to get the expected result from
+ * cli_credentials_get_principal().
+ *
+ * But we only need to do that if the domain
+ * actually changes.
+ */
+ cli_credentials_set_realm(credentials, domain, obtained);
+ }
+ cli_credentials_set_domain(credentials, domain, obtained);
+ }
+ if (obtained == credentials->principal_obtained &&
+ !strequal_m(credentials->username, uname))
+ {
+ /*
+ * We need to undo a former set with the same level
+ * in order to get the expected result from
+ * cli_credentials_get_principal().
+ *
+ * But we only need to do that if the username
+ * actually changes.
+ */
+ credentials->principal_obtained = CRED_UNINITIALISED;
+ credentials->principal = NULL;
+ }
+ cli_credentials_set_username(credentials, uname, obtained);
+
+ TALLOC_FREE(uname_free);
+}
+
+/**
+ * Given a a credentials structure, print it as a string
+ *
+ * The format output is [domain\\]user[%password] or user[@realm][%password]
+ *
+ * @param credentials Credentials structure on which to set the password
+ * @param mem_ctx The memory context to place the result on
+ */
+
+_PUBLIC_ char *cli_credentials_get_unparsed_name(struct cli_credentials *credentials, TALLOC_CTX *mem_ctx)
+{
+ const char *bind_dn = cli_credentials_get_bind_dn(credentials);
+ const char *domain = NULL;
+ const char *username = NULL;
+ char *name = NULL;
+
+ if (bind_dn) {
+ name = talloc_strdup(mem_ctx, bind_dn);
+ } else {
+ cli_credentials_get_ntlm_username_domain(credentials, mem_ctx, &username, &domain);
+ if (domain && domain[0]) {
+ name = talloc_asprintf(mem_ctx, "%s\\%s",
+ domain, username);
+ } else {
+ name = talloc_asprintf(mem_ctx, "%s",
+ username);
+ }
+ }
+ return name;
+}
+
+
+/**
+ * Specifies default values for domain, workstation and realm
+ * from the smb.conf configuration file
+ *
+ * @param cred Credentials structure to fill in
+ *
+ * @return true on success, false on error.
+ */
+_PUBLIC_ bool cli_credentials_set_conf(struct cli_credentials *cred,
+ struct loadparm_context *lp_ctx)
+{
+ const char *sep = NULL;
+ const char *realm = lpcfg_realm(lp_ctx);
+ enum credentials_client_protection protection =
+ lpcfg_client_protection(lp_ctx);
+ const char *workgroup = lpcfg_workgroup(lp_ctx);
+ const char *netbios_name = lpcfg_netbios_name(lp_ctx);
+ bool ok;
+
+ (void)cli_credentials_set_username(cred, "", CRED_UNINITIALISED);
+
+ if (workgroup != NULL && strlen(workgroup) == 0) {
+ workgroup = NULL;
+ }
+
+ if (workgroup != NULL) {
+ if (lpcfg_parm_is_cmdline(lp_ctx, "workgroup")) {
+ ok = cli_credentials_set_domain(cred,
+ workgroup,
+ CRED_SPECIFIED);
+ if (!ok) {
+ DBG_ERR("Failed to set domain!\n");
+ return false;
+ }
+ } else {
+ (void)cli_credentials_set_domain(cred,
+ workgroup,
+ CRED_SMB_CONF);
+ }
+ }
+
+ if (netbios_name != NULL && strlen(netbios_name) == 0) {
+ netbios_name = NULL;
+ }
+
+ if (netbios_name != NULL) {
+ if (lpcfg_parm_is_cmdline(lp_ctx, "netbios name")) {
+ ok = cli_credentials_set_workstation(cred,
+ netbios_name,
+ CRED_SPECIFIED);
+ if (!ok) {
+ DBG_ERR("Failed to set workstation!\n");
+ return false;
+ }
+ } else {
+ (void)cli_credentials_set_workstation(cred,
+ netbios_name,
+ CRED_SMB_CONF);
+ }
+ }
+
+ if (realm != NULL && strlen(realm) == 0) {
+ realm = NULL;
+ }
+
+ if (realm != NULL) {
+ if (lpcfg_parm_is_cmdline(lp_ctx, "realm")) {
+ ok = cli_credentials_set_realm(cred,
+ realm,
+ CRED_SPECIFIED);
+ if (!ok) {
+ DBG_ERR("Failed to set realm!\n");
+ return false;
+ }
+ } else {
+ (void)cli_credentials_set_realm(cred,
+ realm,
+ CRED_SMB_CONF);
+ }
+ }
+
+ sep = lpcfg_winbind_separator(lp_ctx);
+ if (sep != NULL && sep[0] != '\0') {
+ cred->winbind_separator = *lpcfg_winbind_separator(lp_ctx);
+ }
+
+ if (cred->signing_state_obtained <= CRED_SMB_CONF) {
+ /* Will be set to default for invalid smb.conf values */
+ cred->signing_state = lpcfg_client_signing(lp_ctx);
+ if (cred->signing_state == SMB_SIGNING_DEFAULT) {
+ switch (protection) {
+ case CRED_CLIENT_PROTECTION_DEFAULT:
+ break;
+ case CRED_CLIENT_PROTECTION_PLAIN:
+ cred->signing_state = SMB_SIGNING_OFF;
+ break;
+ case CRED_CLIENT_PROTECTION_SIGN:
+ case CRED_CLIENT_PROTECTION_ENCRYPT:
+ cred->signing_state = SMB_SIGNING_REQUIRED;
+ break;
+ }
+ }
+
+ cred->signing_state_obtained = CRED_SMB_CONF;
+ }
+
+ if (cred->ipc_signing_state_obtained <= CRED_SMB_CONF) {
+ /* Will be set to required for invalid smb.conf values */
+ cred->ipc_signing_state = lpcfg_client_ipc_signing(lp_ctx);
+ cred->ipc_signing_state_obtained = CRED_SMB_CONF;
+ }
+
+ if (cred->encryption_state_obtained <= CRED_SMB_CONF) {
+ /* Will be set to default for invalid smb.conf values */
+ cred->encryption_state = lpcfg_client_smb_encrypt(lp_ctx);
+ if (cred->encryption_state == SMB_ENCRYPTION_DEFAULT) {
+ switch (protection) {
+ case CRED_CLIENT_PROTECTION_DEFAULT:
+ break;
+ case CRED_CLIENT_PROTECTION_PLAIN:
+ case CRED_CLIENT_PROTECTION_SIGN:
+ cred->encryption_state = SMB_ENCRYPTION_OFF;
+ break;
+ case CRED_CLIENT_PROTECTION_ENCRYPT:
+ cred->encryption_state = SMB_ENCRYPTION_REQUIRED;
+ break;
+ }
+ }
+ }
+
+ if (cred->kerberos_state_obtained <= CRED_SMB_CONF) {
+ /* Will be set to default for invalid smb.conf values */
+ cred->kerberos_state = lpcfg_client_use_kerberos(lp_ctx);
+ cred->kerberos_state_obtained = CRED_SMB_CONF;
+ }
+
+ if (cred->gensec_features_obtained <= CRED_SMB_CONF) {
+ switch (protection) {
+ case CRED_CLIENT_PROTECTION_DEFAULT:
+ break;
+ case CRED_CLIENT_PROTECTION_PLAIN:
+ cred->gensec_features = 0;
+ break;
+ case CRED_CLIENT_PROTECTION_SIGN:
+ cred->gensec_features = GENSEC_FEATURE_SIGN;
+ break;
+ case CRED_CLIENT_PROTECTION_ENCRYPT:
+ cred->gensec_features =
+ GENSEC_FEATURE_SIGN|GENSEC_FEATURE_SEAL;
+ break;
+ }
+ cred->gensec_features_obtained = CRED_SMB_CONF;
+ }
+
+ return true;
+}
+
+/**
+ * Guess defaults for credentials from environment variables,
+ * and from the configuration file
+ *
+ * @param cred Credentials structure to fill in
+ */
+_PUBLIC_ bool cli_credentials_guess(struct cli_credentials *cred,
+ struct loadparm_context *lp_ctx)
+{
+ const char *error_string;
+ const char *env = NULL;
+ struct passwd *pwd = NULL;
+ bool ok;
+
+ if (lp_ctx != NULL) {
+ ok = cli_credentials_set_conf(cred, lp_ctx);
+ if (!ok) {
+ return false;
+ }
+ }
+
+ pwd = getpwuid(getuid());
+ if (pwd != NULL) {
+ size_t len = strlen(pwd->pw_name);
+
+ if (len > 0 && len <= 1024) {
+ (void)cli_credentials_parse_string(cred,
+ pwd->pw_name,
+ CRED_GUESS_ENV);
+ }
+ }
+
+ env = getenv("LOGNAME");
+ if (env != NULL) {
+ size_t len = strlen(env);
+
+ if (len > 0 && len <= 1024) {
+ (void)cli_credentials_set_username(cred,
+ env,
+ CRED_GUESS_ENV);
+ }
+ }
+
+ env = getenv("USER");
+ if (env != NULL) {
+ size_t len = strlen(env);
+
+ if (len > 0 && len <= 1024) {
+ char *p = NULL;
+
+ (void)cli_credentials_parse_string(cred,
+ env,
+ CRED_GUESS_ENV);
+ if ((p = strchr_m(env, '%'))) {
+ memset(p, '\0', strlen(cred->password));
+ }
+ }
+ }
+
+ env = getenv("PASSWD");
+ if (env != NULL) {
+ size_t len = strlen(env);
+
+ if (len > 0 && len <= 1024) {
+ (void)cli_credentials_set_password(cred,
+ env,
+ CRED_GUESS_ENV);
+ }
+ }
+
+ env = getenv("PASSWD_FD");
+ if (env != NULL) {
+ size_t len = strlen(env);
+
+ if (len > 0 && len <= 1024) {
+ int fd = atoi(env);
+
+ (void)cli_credentials_parse_password_fd(cred,
+ fd,
+ CRED_GUESS_FILE);
+ }
+ }
+
+ env = getenv("PASSWD_FILE");
+ if (env != NULL) {
+ size_t len = strlen(env);
+
+ if (len > 0 && len <= 4096) {
+ (void)cli_credentials_parse_password_file(cred,
+ env,
+ CRED_GUESS_FILE);
+ }
+ }
+
+ if (lp_ctx != NULL &&
+ cli_credentials_get_kerberos_state(cred) != CRED_USE_KERBEROS_DISABLED) {
+ (void)cli_credentials_set_ccache(cred,
+ lp_ctx,
+ NULL,
+ CRED_GUESS_FILE,
+ &error_string);
+ }
+
+ return true;
+}
+
+/**
+ * Attach NETLOGON credentials for use with SCHANNEL
+ */
+
+_PUBLIC_ void cli_credentials_set_netlogon_creds(
+ struct cli_credentials *cred,
+ const struct netlogon_creds_CredentialState *netlogon_creds)
+{
+ TALLOC_FREE(cred->netlogon_creds);
+ if (netlogon_creds == NULL) {
+ return;
+ }
+ cred->netlogon_creds = netlogon_creds_copy(cred, netlogon_creds);
+}
+
+/**
+ * Return attached NETLOGON credentials
+ */
+
+_PUBLIC_ struct netlogon_creds_CredentialState *cli_credentials_get_netlogon_creds(struct cli_credentials *cred)
+{
+ return cred->netlogon_creds;
+}
+
+/**
+ * Set NETLOGON secure channel type
+ */
+
+_PUBLIC_ void cli_credentials_set_secure_channel_type(struct cli_credentials *cred,
+ enum netr_SchannelType secure_channel_type)
+{
+ cred->secure_channel_type = secure_channel_type;
+}
+
+/**
+ * Return NETLOGON secure chanel type
+ */
+
+_PUBLIC_ time_t cli_credentials_get_password_last_changed_time(struct cli_credentials *cred)
+{
+ return cred->password_last_changed_time;
+}
+
+/**
+ * Set NETLOGON secure channel type
+ */
+
+_PUBLIC_ void cli_credentials_set_password_last_changed_time(struct cli_credentials *cred,
+ time_t last_changed_time)
+{
+ cred->password_last_changed_time = last_changed_time;
+}
+
+/**
+ * Return NETLOGON secure chanel type
+ */
+
+_PUBLIC_ enum netr_SchannelType cli_credentials_get_secure_channel_type(struct cli_credentials *cred)
+{
+ return cred->secure_channel_type;
+}
+
+/**
+ * Fill in a credentials structure as the anonymous user
+ */
+_PUBLIC_ void cli_credentials_set_anonymous(struct cli_credentials *cred)
+{
+ cli_credentials_set_username(cred, "", CRED_SPECIFIED);
+ cli_credentials_set_domain(cred, "", CRED_SPECIFIED);
+ cli_credentials_set_password(cred, NULL, CRED_SPECIFIED);
+ cli_credentials_set_principal(cred, NULL, CRED_SPECIFIED);
+ cli_credentials_set_realm(cred, NULL, CRED_SPECIFIED);
+ cli_credentials_set_workstation(cred, "", CRED_UNINITIALISED);
+ cli_credentials_set_kerberos_state(cred,
+ CRED_USE_KERBEROS_DISABLED,
+ CRED_SPECIFIED);
+}
+
+/**
+ * Describe a credentials context as anonymous or authenticated
+ * @retval true if anonymous, false if a username is specified
+ */
+
+_PUBLIC_ bool cli_credentials_is_anonymous(struct cli_credentials *cred)
+{
+ const char *username;
+
+ /* if bind dn is set it's not anonymous */
+ if (cred->bind_dn) {
+ return false;
+ }
+
+ if (cred->machine_account_pending) {
+ cli_credentials_set_machine_account(cred,
+ cred->machine_account_pending_lp_ctx);
+ }
+
+ /* if principal is set, it's not anonymous */
+ if ((cred->principal != NULL) && cred->principal_obtained >= cred->username_obtained) {
+ return false;
+ }
+
+ username = cli_credentials_get_username(cred);
+
+ /* Yes, it is deliberate that we die if we have a NULL pointer
+ * here - anonymous is "", not NULL, which is 'never specified,
+ * never guessed', ie programmer bug */
+ if (!username[0]) {
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Mark the current password for a credentials struct as wrong. This will
+ * cause the password to be prompted again (if a callback is set).
+ *
+ * This will decrement the number of times the password can be tried.
+ *
+ * @retval whether the credentials struct is finished
+ */
+_PUBLIC_ bool cli_credentials_wrong_password(struct cli_credentials *cred)
+{
+ if (cred->password_obtained != CRED_CALLBACK_RESULT) {
+ return false;
+ }
+
+ if (cred->password_tries == 0) {
+ return false;
+ }
+
+ cred->password_tries--;
+
+ if (cred->password_tries == 0) {
+ return false;
+ }
+
+ cred->password_obtained = CRED_CALLBACK;
+ return true;
+}
+
+_PUBLIC_ void cli_credentials_get_ntlm_username_domain(struct cli_credentials *cred, TALLOC_CTX *mem_ctx,
+ const char **username,
+ const char **domain)
+{
+ if (cred->principal_obtained >= cred->username_obtained) {
+ *domain = talloc_strdup(mem_ctx, "");
+ *username = cli_credentials_get_principal(cred, mem_ctx);
+ } else {
+ *domain = cli_credentials_get_domain(cred);
+ *username = cli_credentials_get_username(cred);
+ }
+}
+
+/**
+ * Read a named file, and parse it for username, domain, realm and password
+ *
+ * @param credentials Credentials structure on which to set the password
+ * @param file a named file to read the details from
+ * @param obtained This enum describes how 'specified' this password is
+ */
+
+_PUBLIC_ bool cli_credentials_parse_file(struct cli_credentials *cred, const char *file, enum credentials_obtained obtained)
+{
+ uint16_t len = 0;
+ char *ptr, *val, *param;
+ char **lines;
+ int i, numlines;
+ const char *realm = NULL;
+ const char *domain = NULL;
+ const char *password = NULL;
+ const char *username = NULL;
+
+ lines = file_lines_load(file, &numlines, 0, NULL);
+
+ if (lines == NULL)
+ {
+ /* fail if we can't open the credentials file */
+ d_printf("ERROR: Unable to open credentials file!\n");
+ return false;
+ }
+
+ for (i = 0; i < numlines; i++) {
+ len = strlen(lines[i]);
+
+ if (len == 0)
+ continue;
+
+ /* break up the line into parameter & value.
+ * will need to eat a little whitespace possibly */
+ param = lines[i];
+ if (!(ptr = strchr_m (lines[i], '=')))
+ continue;
+
+ val = ptr+1;
+ *ptr = '\0';
+
+ /* eat leading white space */
+ while ((*val!='\0') && ((*val==' ') || (*val=='\t')))
+ val++;
+
+ if (strwicmp("password", param) == 0) {
+ password = val;
+ } else if (strwicmp("username", param) == 0) {
+ username = val;
+ } else if (strwicmp("domain", param) == 0) {
+ domain = val;
+ } else if (strwicmp("realm", param) == 0) {
+ realm = val;
+ }
+
+ /*
+ * We need to readd '=' in order to let
+ * the strlen() work in the last loop
+ * that clears the memory.
+ */
+ *ptr = '=';
+ }
+
+ if (realm != NULL && strlen(realm) != 0) {
+ /*
+ * only overwrite with a valid string
+ */
+ cli_credentials_set_realm(cred, realm, obtained);
+ }
+
+ if (domain != NULL && strlen(domain) != 0) {
+ /*
+ * only overwrite with a valid string
+ */
+ cli_credentials_set_domain(cred, domain, obtained);
+ }
+
+ if (password != NULL) {
+ /*
+ * Here we allow "".
+ */
+ cli_credentials_set_password(cred, password, obtained);
+ }
+
+ if (username != NULL) {
+ /*
+ * The last "username" line takes preference
+ * if the string also contains domain, realm or
+ * password.
+ */
+ cli_credentials_parse_string(cred, username, obtained);
+ }
+
+ for (i = 0; i < numlines; i++) {
+ len = strlen(lines[i]);
+ memset(lines[i], 0, len);
+ }
+ talloc_free(lines);
+
+ return true;
+}
+
+/**
+ * Read a named file, and parse it for a password
+ *
+ * @param credentials Credentials structure on which to set the password
+ * @param file a named file to read the password from
+ * @param obtained This enum describes how 'specified' this password is
+ */
+
+_PUBLIC_ bool cli_credentials_parse_password_file(struct cli_credentials *credentials, const char *file, enum credentials_obtained obtained)
+{
+ int fd = open(file, O_RDONLY, 0);
+ bool ret;
+
+ if (fd < 0) {
+ fprintf(stderr, "Error opening password file %s: %s\n",
+ file, strerror(errno));
+ return false;
+ }
+
+ ret = cli_credentials_parse_password_fd(credentials, fd, obtained);
+
+ close(fd);
+
+ return ret;
+}
+
+
+/**
+ * Read a file descriptor, and parse it for a password (eg from a file or stdin)
+ *
+ * @param credentials Credentials structure on which to set the password
+ * @param fd open file descriptor to read the password from
+ * @param obtained This enum describes how 'specified' this password is
+ */
+
+_PUBLIC_ bool cli_credentials_parse_password_fd(struct cli_credentials *credentials,
+ int fd, enum credentials_obtained obtained)
+{
+ char *p;
+ char pass[128];
+
+ for(p = pass, *p = '\0'; /* ensure that pass is null-terminated */
+ p && p - pass < sizeof(pass);) {
+ switch (read(fd, p, 1)) {
+ case 1:
+ if (*p != '\n' && *p != '\0') {
+ *++p = '\0'; /* advance p, and null-terminate pass */
+ break;
+ }
+
+ FALL_THROUGH;
+ case 0:
+ if (p - pass) {
+ *p = '\0'; /* null-terminate it, just in case... */
+ p = NULL; /* then force the loop condition to become false */
+ break;
+ }
+
+ fprintf(stderr,
+ "Error reading password from file descriptor "
+ "%d: empty password\n",
+ fd);
+ return false;
+
+ default:
+ fprintf(stderr, "Error reading password from file descriptor %d: %s\n",
+ fd, strerror(errno));
+ return false;
+ }
+ }
+
+ cli_credentials_set_password(credentials, pass, obtained);
+ return true;
+}
+
+/**
+ * @brief Set the SMB signing state to request for a SMB connection.
+ *
+ * @param[in] creds The credentials structure to update.
+ *
+ * @param[in] signing_state The signing state to set.
+ *
+ * @param obtained This way the described signing state was specified.
+ *
+ * @return true if we could set the signing state, false otherwise.
+ */
+_PUBLIC_ bool cli_credentials_set_smb_signing(struct cli_credentials *creds,
+ enum smb_signing_setting signing_state,
+ enum credentials_obtained obtained)
+{
+ if (obtained >= creds->signing_state_obtained) {
+ creds->signing_state_obtained = obtained;
+ creds->signing_state = signing_state;
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * @brief Obtain the SMB signing state from a credentials structure.
+ *
+ * @param[in] creds The credential structure to obtain the SMB signing state
+ * from.
+ *
+ * @return The SMB singing state.
+ */
+_PUBLIC_ enum smb_signing_setting
+cli_credentials_get_smb_signing(struct cli_credentials *creds)
+{
+ return creds->signing_state;
+}
+
+/**
+ * @brief Set the SMB IPC signing state to request for a SMB connection.
+ *
+ * @param[in] creds The credentials structure to update.
+ *
+ * @param[in] signing_state The signing state to set.
+ *
+ * @param obtained This way the described signing state was specified.
+ *
+ * @return true if we could set the signing state, false otherwise.
+ */
+_PUBLIC_ bool
+cli_credentials_set_smb_ipc_signing(struct cli_credentials *creds,
+ enum smb_signing_setting ipc_signing_state,
+ enum credentials_obtained obtained)
+{
+ if (obtained >= creds->ipc_signing_state_obtained) {
+ creds->ipc_signing_state_obtained = obtained;
+ creds->ipc_signing_state = ipc_signing_state;
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * @brief Obtain the SMB IPC signing state from a credentials structure.
+ *
+ * @param[in] creds The credential structure to obtain the SMB IPC signing
+ * state from.
+ *
+ * @return The SMB singing state.
+ */
+_PUBLIC_ enum smb_signing_setting
+cli_credentials_get_smb_ipc_signing(struct cli_credentials *creds)
+{
+ return creds->ipc_signing_state;
+}
+
+/**
+ * @brief Set the SMB encryption state to request for a SMB connection.
+ *
+ * @param[in] creds The credentials structure to update.
+ *
+ * @param[in] encryption_state The encryption state to set.
+ *
+ * @param obtained This way the described encryption state was specified.
+ *
+ * @return true if we could set the encryption state, false otherwise.
+ */
+_PUBLIC_ bool cli_credentials_set_smb_encryption(struct cli_credentials *creds,
+ enum smb_encryption_setting encryption_state,
+ enum credentials_obtained obtained)
+{
+ if (obtained >= creds->encryption_state_obtained) {
+ creds->encryption_state_obtained = obtained;
+ creds->encryption_state = encryption_state;
+ return true;
+ }
+
+ return false;
+}
+
+static const char *obtained_to_str(enum credentials_obtained obtained)
+{
+ switch (obtained) {
+ case CRED_UNINITIALISED:
+ return "CRED_UNINITIALISED";
+ case CRED_SMB_CONF:
+ return "CRED_SMB_CONF";
+ case CRED_CALLBACK:
+ return "CRED_CALLBACK";
+ case CRED_GUESS_ENV:
+ return "CRED_GUESS_ENV";
+ case CRED_GUESS_FILE:
+ return "CRED_GUESS_FILE";
+ case CRED_CALLBACK_RESULT:
+ return "CRED_CALLBACK_RESULT";
+ case CRED_SPECIFIED:
+ return "CRED_SPECIFIED";
+ }
+
+ /* Never reached */
+ return "";
+}
+
+static const char *krb5_state_to_str(enum credentials_use_kerberos krb5_state)
+{
+ switch (krb5_state) {
+ case CRED_USE_KERBEROS_DISABLED:
+ return "CRED_USE_KERBEROS_DISABLED";
+ case CRED_USE_KERBEROS_DESIRED:
+ return "CRED_USE_KERBEROS_DESIRED";
+ case CRED_USE_KERBEROS_REQUIRED:
+ return "CRED_USE_KERBEROS_REQUIRED";
+ }
+
+ /* Never reached */
+ return "";
+}
+
+static const char *krb5_fwd_to_str(enum credentials_krb_forwardable krb5_fwd)
+{
+ switch (krb5_fwd) {
+ case CRED_AUTO_KRB_FORWARDABLE:
+ return "CRED_AUTO_KRB_FORWARDABLE";
+ case CRED_NO_KRB_FORWARDABLE:
+ return "CRED_NO_KRB_FORWARDABLE";
+ case CRED_FORCE_KRB_FORWARDABLE:
+ return "CRED_FORCE_KRB_FORWARDABLE";
+ }
+
+ /* Never reached */
+ return "";
+}
+
+static const char *signing_state_to_str(enum smb_signing_setting signing_state)
+{
+ switch(signing_state) {
+ case SMB_SIGNING_IPC_DEFAULT:
+ return "SMB_SIGNING_IPC_DEFAULT";
+ case SMB_SIGNING_DEFAULT:
+ return "SMB_SIGNING_DEFAULT";
+ case SMB_SIGNING_OFF:
+ return "SMB_SIGNING_OFF";
+ case SMB_SIGNING_IF_REQUIRED:
+ return "SMB_SIGNING_IF_REQUIRED";
+ case SMB_SIGNING_DESIRED:
+ return "SMB_SIGNING_DESIRED";
+ case SMB_SIGNING_REQUIRED:
+ return "SMB_SIGNING_REQUIRED";
+ }
+
+ /* Never reached */
+ return "";
+}
+
+static const char *encryption_state_to_str(enum smb_encryption_setting encryption_state)
+{
+ switch(encryption_state) {
+ case SMB_ENCRYPTION_DEFAULT:
+ return "SMB_ENCRYPTION_DEFAULT";
+ case SMB_ENCRYPTION_OFF:
+ return "SMB_ENCRYPTION_OFF";
+ case SMB_ENCRYPTION_IF_REQUIRED:
+ return "SMB_ENCRYPTION_IF_REQUIRED";
+ case SMB_ENCRYPTION_DESIRED:
+ return "SMB_ENCRYPTION_DESIRED";
+ case SMB_ENCRYPTION_REQUIRED:
+ return "SMB_ENCRYPTION_REQUIRED";
+ }
+
+ /* Never reached */
+ return "";
+}
+
+_PUBLIC_ void cli_credentials_dump(struct cli_credentials *creds)
+{
+ DBG_ERR("CLI_CREDENTIALS:\n");
+ DBG_ERR("\n");
+ DBG_ERR(" Username: %s - %s\n",
+ creds->username,
+ obtained_to_str(creds->username_obtained));
+ DBG_ERR(" Workstation: %s - %s\n",
+ creds->workstation,
+ obtained_to_str(creds->workstation_obtained));
+ DBG_ERR(" Domain: %s - %s\n",
+ creds->domain,
+ obtained_to_str(creds->domain_obtained));
+ DBG_ERR(" Password: %s - %s\n",
+ creds->password != NULL ? "*SECRET*" : "NULL",
+ obtained_to_str(creds->password_obtained));
+ DBG_ERR(" Old password: %s\n",
+ creds->old_password != NULL ? "*SECRET*" : "NULL");
+ DBG_ERR(" Password tries: %u\n",
+ creds->password_tries);
+ DBG_ERR(" Realm: %s - %s\n",
+ creds->realm,
+ obtained_to_str(creds->realm_obtained));
+ DBG_ERR(" Principal: %s - %s\n",
+ creds->principal,
+ obtained_to_str(creds->principal_obtained));
+ DBG_ERR(" Salt principal: %s\n",
+ creds->salt_principal);
+ DBG_ERR(" Impersonate principal: %s\n",
+ creds->impersonate_principal);
+ DBG_ERR(" Self service: %s\n",
+ creds->self_service);
+ DBG_ERR(" Target service: %s\n",
+ creds->target_service);
+ DBG_ERR(" Kerberos state: %s - %s\n",
+ krb5_state_to_str(creds->kerberos_state),
+ obtained_to_str(creds->kerberos_state_obtained));
+ DBG_ERR(" Kerberos forwardable ticket: %s\n",
+ krb5_fwd_to_str(creds->krb_forwardable));
+ DBG_ERR(" Signing state: %s - %s\n",
+ signing_state_to_str(creds->signing_state),
+ obtained_to_str(creds->signing_state_obtained));
+ DBG_ERR(" IPC signing state: %s - %s\n",
+ signing_state_to_str(creds->ipc_signing_state),
+ obtained_to_str(creds->ipc_signing_state_obtained));
+ DBG_ERR(" Encryption state: %s - %s\n",
+ encryption_state_to_str(creds->encryption_state),
+ obtained_to_str(creds->encryption_state_obtained));
+ DBG_ERR(" Gensec features: %#X\n",
+ creds->gensec_features);
+ DBG_ERR(" Forced sasl mech: %s\n",
+ creds->forced_sasl_mech);
+ DBG_ERR(" CCACHE: %p - %s\n",
+ creds->ccache,
+ obtained_to_str(creds->ccache_obtained));
+ DBG_ERR(" CLIENT_GSS_CREDS: %p - %s\n",
+ creds->client_gss_creds,
+ obtained_to_str(creds->client_gss_creds_obtained));
+ DBG_ERR(" SERVER_GSS_CREDS: %p - %s\n",
+ creds->server_gss_creds,
+ obtained_to_str(creds->server_gss_creds_obtained));
+ DBG_ERR(" KEYTAB: %p - %s\n",
+ creds->keytab,
+ obtained_to_str(creds->keytab_obtained));
+ DBG_ERR(" KVNO: %u\n",
+ creds->kvno);
+ DBG_ERR("\n");
+}
+
+/**
+ * @brief Obtain the SMB encryption state from a credentials structure.
+ *
+ * @param[in] creds The credential structure to obtain the SMB encryption state
+ * from.
+ *
+ * @return The SMB singing state.
+ */
+_PUBLIC_ enum smb_encryption_setting
+cli_credentials_get_smb_encryption(struct cli_credentials *creds)
+{
+ return creds->encryption_state;
+}
+
+/**
+ * Encrypt a data blob using the session key and the negotiated encryption
+ * algorithm
+ *
+ * @param state Credential state, contains the session key and algorithm
+ * @param data Data blob containing the data to be encrypted.
+ *
+ */
+_PUBLIC_ NTSTATUS netlogon_creds_session_encrypt(
+ struct netlogon_creds_CredentialState *state,
+ DATA_BLOB data)
+{
+ NTSTATUS status;
+
+ if (data.data == NULL || data.length == 0) {
+ DBG_ERR("Nothing to encrypt "
+ "data.data == NULL or data.length == 0");
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ /*
+ * Don't crypt an all-zero password it will give away the
+ * NETLOGON pipe session key .
+ */
+ if (all_zero(data.data, data.length)) {
+ DBG_ERR("Supplied data all zeros, could leak session key");
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ if (state->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
+ status = netlogon_creds_aes_encrypt(state,
+ data.data,
+ data.length);
+ } else if (state->negotiate_flags & NETLOGON_NEG_ARCFOUR) {
+ status = netlogon_creds_arcfour_crypt(state,
+ data.data,
+ data.length);
+ } else {
+ DBG_ERR("Unsupported encryption option negotiated");
+ status = NT_STATUS_NOT_SUPPORTED;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ return NT_STATUS_OK;
+}
+
diff --git a/auth/credentials/credentials.h b/auth/credentials/credentials.h
new file mode 100644
index 0000000..e9d8b8a
--- /dev/null
+++ b/auth/credentials/credentials.h
@@ -0,0 +1,353 @@
+/*
+ samba -- Unix SMB/CIFS implementation.
+
+ Client credentials structure
+
+ Copyright (C) Jelmer Vernooij 2004-2006
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
+
+ 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 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ 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, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef __CREDENTIALS_H__
+#define __CREDENTIALS_H__
+
+#include "../lib/util/time.h"
+#include "../lib/util/data_blob.h"
+#include "librpc/gen_ndr/misc.h"
+
+struct cli_credentials;
+struct ccache_container;
+struct tevent_context;
+struct netlogon_creds_CredentialState;
+struct ldb_context;
+struct ldb_message;
+struct loadparm_context;
+struct ccache_container;
+struct gssapi_creds_container;
+struct smb_krb5_context;
+struct keytab_container;
+struct db_context;
+enum smb_signing_setting;
+enum smb_encryption_setting;
+
+/* In order of priority */
+enum credentials_obtained {
+ CRED_UNINITIALISED = 0, /* We don't even have a guess yet */
+ CRED_SMB_CONF, /* Current value should be used, which comes from smb.conf */
+ CRED_CALLBACK, /* Callback should be used to obtain value */
+ CRED_GUESS_ENV, /* Current value should be used, which was guessed */
+ CRED_GUESS_FILE, /* A guess from a file (or file pointed at in env variable) */
+ CRED_CALLBACK_RESULT, /* Value was obtained from a callback */
+ CRED_SPECIFIED /* Was explicitly specified on the command-line */
+};
+
+enum credentials_use_kerberos {
+ /** Sometimes trying kerberos just does 'bad things', so don't */
+ CRED_USE_KERBEROS_DISABLED = 0,
+ /** Default, we try kerberos if available */
+ CRED_USE_KERBEROS_DESIRED,
+ /** Sometimes administrators are paranoid, so always do kerberos */
+ CRED_USE_KERBEROS_REQUIRED,
+};
+
+enum credentials_client_protection {
+ CRED_CLIENT_PROTECTION_DEFAULT = -1,
+ CRED_CLIENT_PROTECTION_PLAIN = 0,
+ CRED_CLIENT_PROTECTION_SIGN,
+ CRED_CLIENT_PROTECTION_ENCRYPT,
+};
+
+enum credentials_krb_forwardable {
+ CRED_AUTO_KRB_FORWARDABLE = 0, /* Default, follow library defaults */
+ CRED_NO_KRB_FORWARDABLE, /* not forwardable */
+ CRED_FORCE_KRB_FORWARDABLE /* forwardable */
+};
+
+#define CLI_CRED_NTLM2 0x01
+#define CLI_CRED_NTLMv2_AUTH 0x02
+#define CLI_CRED_LANMAN_AUTH 0x04
+#define CLI_CRED_NTLM_AUTH 0x08
+#define CLI_CRED_CLEAR_AUTH 0x10 /* TODO: Push cleartext auth with this flag */
+
+const char *cli_credentials_get_workstation(struct cli_credentials *cred);
+bool cli_credentials_set_workstation(struct cli_credentials *cred,
+ const char *val,
+ enum credentials_obtained obtained);
+bool cli_credentials_is_anonymous(struct cli_credentials *cred);
+struct cli_credentials *cli_credentials_init(TALLOC_CTX *mem_ctx);
+struct cli_credentials *cli_credentials_init_server(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx);
+void cli_credentials_set_anonymous(struct cli_credentials *cred);
+bool cli_credentials_wrong_password(struct cli_credentials *cred);
+const char *cli_credentials_get_password(struct cli_credentials *cred);
+const char *cli_credentials_get_password_and_obtained(struct cli_credentials *cred,
+ enum credentials_obtained *obtained);
+void cli_credentials_get_ntlm_username_domain(struct cli_credentials *cred, TALLOC_CTX *mem_ctx,
+ const char **username,
+ const char **domain);
+NTSTATUS cli_credentials_get_ntlm_response(struct cli_credentials *cred, TALLOC_CTX *mem_ctx,
+ int *flags,
+ DATA_BLOB challenge,
+ const NTTIME *server_timestamp,
+ DATA_BLOB target_info,
+ DATA_BLOB *_lm_response, DATA_BLOB *_nt_response,
+ DATA_BLOB *_lm_session_key, DATA_BLOB *_session_key);
+const char *cli_credentials_get_realm(struct cli_credentials *cred);
+const char *cli_credentials_get_username(struct cli_credentials *cred);
+const char *cli_credentials_get_username_and_obtained(struct cli_credentials *cred,
+ enum credentials_obtained *obtained);
+int cli_credentials_get_krb5_context(struct cli_credentials *cred,
+ struct loadparm_context *lp_ctx,
+ struct smb_krb5_context **smb_krb5_context);
+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);
+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);
+bool cli_credentials_failed_kerberos_login(struct cli_credentials *cred,
+ const char *principal,
+ unsigned int *count);
+int cli_credentials_get_keytab(struct cli_credentials *cred,
+ struct loadparm_context *lp_ctx,
+ struct keytab_container **_ktc);
+const char *cli_credentials_get_domain(struct cli_credentials *cred);
+struct netlogon_creds_CredentialState *cli_credentials_get_netlogon_creds(struct cli_credentials *cred);
+void cli_credentials_set_machine_account_pending(struct cli_credentials *cred,
+ struct loadparm_context *lp_ctx);
+bool cli_credentials_set_conf(struct cli_credentials *cred,
+ struct loadparm_context *lp_ctx);
+char *cli_credentials_get_principal(struct cli_credentials *cred, TALLOC_CTX *mem_ctx);
+int cli_credentials_get_server_gss_creds(struct cli_credentials *cred,
+ struct loadparm_context *lp_ctx,
+ struct gssapi_creds_container **_gcc);
+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);
+void cli_credentials_set_forced_sasl_mech(struct cli_credentials *creds,
+ const char *sasl_mech);
+bool cli_credentials_set_kerberos_state(struct cli_credentials *creds,
+ enum credentials_use_kerberos kerberos_state,
+ enum credentials_obtained obtained);
+void cli_credentials_set_krb_forwardable(struct cli_credentials *creds,
+ enum credentials_krb_forwardable krb_forwardable);
+bool cli_credentials_set_domain(struct cli_credentials *cred,
+ const char *val,
+ enum credentials_obtained obtained);
+bool cli_credentials_set_domain_callback(struct cli_credentials *cred,
+ const char *(*domain_cb) (struct cli_credentials *));
+bool cli_credentials_set_username(struct cli_credentials *cred,
+ const char *val, enum credentials_obtained obtained);
+bool cli_credentials_set_username_callback(struct cli_credentials *cred,
+ const char *(*username_cb) (struct cli_credentials *));
+bool cli_credentials_set_principal(struct cli_credentials *cred,
+ const char *val,
+ enum credentials_obtained obtained);
+bool cli_credentials_set_principal_callback(struct cli_credentials *cred,
+ const char *(*principal_cb) (struct cli_credentials *));
+bool cli_credentials_set_password(struct cli_credentials *cred,
+ const char *val,
+ enum credentials_obtained obtained);
+struct cli_credentials *cli_credentials_init_anon(TALLOC_CTX *mem_ctx);
+void cli_credentials_parse_string(struct cli_credentials *credentials, const char *data, enum credentials_obtained obtained);
+struct samr_Password *cli_credentials_get_nt_hash(struct cli_credentials *cred,
+ TALLOC_CTX *mem_ctx);
+struct samr_Password *cli_credentials_get_old_nt_hash(struct cli_credentials *cred,
+ TALLOC_CTX *mem_ctx);
+bool cli_credentials_set_realm(struct cli_credentials *cred,
+ const char *val,
+ enum credentials_obtained obtained);
+void cli_credentials_set_secure_channel_type(struct cli_credentials *cred,
+ enum netr_SchannelType secure_channel_type);
+void cli_credentials_set_password_last_changed_time(struct cli_credentials *cred,
+ time_t last_change_time);
+void cli_credentials_set_netlogon_creds(
+ struct cli_credentials *cred,
+ const struct netlogon_creds_CredentialState *netlogon_creds);
+NTSTATUS cli_credentials_set_krb5_context(struct cli_credentials *cred,
+ struct smb_krb5_context *smb_krb5_context);
+NTSTATUS cli_credentials_set_stored_principal(struct cli_credentials *cred,
+ struct loadparm_context *lp_ctx,
+ const char *serviceprincipal);
+NTSTATUS cli_credentials_set_machine_account(struct cli_credentials *cred,
+ struct loadparm_context *lp_ctx);
+/**
+ * Fill in credentials for the machine trust account, from the
+ * secrets.ldb or passed in handle to secrets.tdb (perhaps in CTDB).
+ *
+ * This version is used in parts of the code that can link in the
+ * CTDB dbwrap backend, by passing down the already open handle.
+ *
+ * @param cred Credentials structure to fill in
+ * @param db_ctx dbwrap context for secrets.tdb
+ * @retval NTSTATUS error detailing any failure
+ */
+NTSTATUS cli_credentials_set_machine_account_db_ctx(struct cli_credentials *cred,
+ struct loadparm_context *lp_ctx,
+ struct db_context *db_ctx);
+
+bool cli_credentials_authentication_requested(struct cli_credentials *cred);
+bool cli_credentials_guess(struct cli_credentials *cred,
+ struct loadparm_context *lp_ctx);
+bool cli_credentials_set_bind_dn(struct cli_credentials *cred,
+ const char *bind_dn);
+const char *cli_credentials_get_bind_dn(struct cli_credentials *cred);
+bool cli_credentials_parse_file(struct cli_credentials *cred, const char *file, enum credentials_obtained obtained);
+char *cli_credentials_get_unparsed_name(struct cli_credentials *credentials, TALLOC_CTX *mem_ctx);
+bool cli_credentials_set_password_callback(struct cli_credentials *cred,
+ const char *(*password_cb) (struct cli_credentials *));
+enum netr_SchannelType cli_credentials_get_secure_channel_type(struct cli_credentials *cred);
+time_t cli_credentials_get_password_last_changed_time(struct cli_credentials *cred);
+void cli_credentials_set_kvno(struct cli_credentials *cred,
+ int kvno);
+bool cli_credentials_set_utf16_password(struct cli_credentials *cred,
+ const DATA_BLOB *password_utf16,
+ enum credentials_obtained obtained);
+bool cli_credentials_set_old_utf16_password(struct cli_credentials *cred,
+ const DATA_BLOB *password_utf16);
+void cli_credentials_set_password_will_be_nt_hash(struct cli_credentials *cred,
+ bool val);
+bool cli_credentials_set_nt_hash(struct cli_credentials *cred,
+ const struct samr_Password *nt_hash,
+ enum credentials_obtained obtained);
+bool cli_credentials_set_old_nt_hash(struct cli_credentials *cred,
+ const struct samr_Password *nt_hash);
+bool cli_credentials_set_ntlm_response(struct cli_credentials *cred,
+ const DATA_BLOB *lm_response,
+ const DATA_BLOB *lm_session_key,
+ const DATA_BLOB *nt_response,
+ const DATA_BLOB *nt_session_key,
+ enum credentials_obtained obtained);
+int cli_credentials_set_keytab_name(struct cli_credentials *cred,
+ struct loadparm_context *lp_ctx,
+ const char *keytab_name,
+ enum credentials_obtained obtained);
+bool cli_credentials_set_gensec_features(struct cli_credentials *creds,
+ uint32_t gensec_features,
+ enum credentials_obtained obtained);
+uint32_t cli_credentials_get_gensec_features(struct cli_credentials *creds);
+int cli_credentials_set_ccache(struct cli_credentials *cred,
+ struct loadparm_context *lp_ctx,
+ const char *name,
+ enum credentials_obtained obtained,
+ const char **error_string);
+bool cli_credentials_parse_password_file(struct cli_credentials *credentials, const char *file, enum credentials_obtained obtained);
+bool cli_credentials_parse_password_fd(struct cli_credentials *credentials,
+ int fd, enum credentials_obtained obtained);
+void cli_credentials_invalidate_ccache(struct cli_credentials *cred,
+ enum credentials_obtained obtained);
+void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal);
+void cli_credentials_set_impersonate_principal(struct cli_credentials *cred,
+ const char *principal,
+ const char *self_service);
+void cli_credentials_set_target_service(struct cli_credentials *cred, const char *principal);
+const char *cli_credentials_get_salt_principal(struct cli_credentials *cred);
+const char *cli_credentials_get_impersonate_principal(struct cli_credentials *cred);
+const char *cli_credentials_get_self_service(struct cli_credentials *cred);
+const char *cli_credentials_get_target_service(struct cli_credentials *cred);
+enum credentials_use_kerberos cli_credentials_get_kerberos_state(struct cli_credentials *creds);
+const char *cli_credentials_get_forced_sasl_mech(struct cli_credentials *cred);
+enum credentials_krb_forwardable cli_credentials_get_krb_forwardable(struct cli_credentials *creds);
+NTSTATUS cli_credentials_set_secrets(struct cli_credentials *cred,
+ struct loadparm_context *lp_ctx,
+ struct ldb_context *ldb,
+ const char *base,
+ const char *filter,
+ char **error_string);
+ int cli_credentials_get_kvno(struct cli_credentials *cred);
+
+bool cli_credentials_set_username_callback(struct cli_credentials *cred,
+ const char *(*username_cb) (struct cli_credentials *));
+
+/**
+ * Obtain the client principal for this credentials context.
+ * @param cred credentials context
+ * @retval The username set on this context.
+ * @note Return value will never be NULL except by programmer error.
+ */
+char *cli_credentials_get_principal_and_obtained(struct cli_credentials *cred, TALLOC_CTX *mem_ctx, enum credentials_obtained *obtained);
+bool cli_credentials_set_principal(struct cli_credentials *cred,
+ const char *val,
+ enum credentials_obtained obtained);
+bool cli_credentials_set_principal_callback(struct cli_credentials *cred,
+ const char *(*principal_cb) (struct cli_credentials *));
+
+/**
+ * Obtain the 'old' password for this credentials context (used for join accounts).
+ * @param cred credentials context
+ * @retval If set, the cleartext password, otherwise NULL
+ */
+const char *cli_credentials_get_old_password(struct cli_credentials *cred);
+bool cli_credentials_set_old_password(struct cli_credentials *cred,
+ const char *val,
+ enum credentials_obtained obtained);
+bool cli_credentials_set_domain_callback(struct cli_credentials *cred,
+ const char *(*domain_cb) (struct cli_credentials *));
+bool cli_credentials_set_realm_callback(struct cli_credentials *cred,
+ const char *(*realm_cb) (struct cli_credentials *));
+bool cli_credentials_set_workstation_callback(struct cli_credentials *cred,
+ const char *(*workstation_cb) (struct cli_credentials *));
+
+void cli_credentials_set_callback_data(struct cli_credentials *cred,
+ void *callback_data);
+void *_cli_credentials_callback_data(struct cli_credentials *cred);
+#define cli_credentials_callback_data(_cred, _type) \
+ talloc_get_type_abort(_cli_credentials_callback_data(_cred), _type)
+#define cli_credentials_callback_data_void(_cred) \
+ _cli_credentials_callback_data(_cred)
+
+bool cli_credentials_set_smb_signing(struct cli_credentials *cred,
+ enum smb_signing_setting signing_state,
+ enum credentials_obtained obtained);
+enum smb_signing_setting
+cli_credentials_get_smb_signing(struct cli_credentials *cred);
+
+bool cli_credentials_set_smb_ipc_signing(struct cli_credentials *cred,
+ enum smb_signing_setting ipc_signing_state,
+ enum credentials_obtained obtained);
+enum smb_signing_setting
+cli_credentials_get_smb_ipc_signing(struct cli_credentials *cred);
+
+bool cli_credentials_set_smb_encryption(struct cli_credentials *cred,
+ enum smb_encryption_setting encryption_state,
+ enum credentials_obtained obtained);
+enum smb_encryption_setting
+cli_credentials_get_smb_encryption(struct cli_credentials *cred);
+
+bool cli_credentials_set_cmdline_callbacks(struct cli_credentials *cred);
+
+void cli_credentials_dump(struct cli_credentials *creds);
+
+/**
+ * Return attached NETLOGON credentials
+ */
+struct netlogon_creds_CredentialState *cli_credentials_get_netlogon_creds(struct cli_credentials *cred);
+
+NTSTATUS netlogon_creds_session_encrypt(
+ struct netlogon_creds_CredentialState *state,
+ DATA_BLOB data);
+
+int cli_credentials_get_aes256_key(struct cli_credentials *cred,
+ TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ const char *salt,
+ DATA_BLOB *aes_256);
+
+#endif /* __CREDENTIALS_H__ */
diff --git a/auth/credentials/credentials_cmdline.c b/auth/credentials/credentials_cmdline.c
new file mode 100644
index 0000000..11b1ab9
--- /dev/null
+++ b/auth/credentials/credentials_cmdline.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2005 Jelmer Vernooij <jelmer@samba.org>
+ * Copyright (c) 2016 Stefan Metzmacher <metze@samba.org>
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "auth/credentials/credentials.h"
+
+static const char *cmdline_get_userpassword(struct cli_credentials *creds)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ const char *name = NULL;
+ char *label = NULL;
+ char *ret = NULL;
+ char pwd[256] = {0};
+ int rc;
+
+ name = cli_credentials_get_unparsed_name(creds, frame);
+ if (name == NULL) {
+ goto fail;
+ }
+ label = talloc_asprintf(frame, "Password for [%s]:", name);
+ if (label == NULL) {
+ goto fail;
+ }
+ rc = samba_getpass(label, pwd, sizeof(pwd), false, false);
+ if (rc != 0) {
+ goto fail;
+ }
+ ret = talloc_strdup(creds, pwd);
+ if (ret == NULL) {
+ goto fail;
+ }
+ talloc_set_name_const(ret, __location__);
+fail:
+ ZERO_STRUCT(pwd);
+ TALLOC_FREE(frame);
+ return ret;
+}
+
+/**
+ * @brief Set the command line password callback.
+ *
+ * This will set the callback to get the password from the command prompt or
+ * read it from 'stdin'.
+ *
+ * @param[in] cred The credential context.
+ *
+ * @return On success true, false otherwise.
+ */
+bool cli_credentials_set_cmdline_callbacks(struct cli_credentials *cred)
+{
+ /*
+ * The there is no tty, then we will try to read the password from
+ * stdin.
+ */
+ return cli_credentials_set_password_callback(cred,
+ cmdline_get_userpassword);
+}
diff --git a/auth/credentials/credentials_internal.h b/auth/credentials/credentials_internal.h
new file mode 100644
index 0000000..3b1581a
--- /dev/null
+++ b/auth/credentials/credentials_internal.h
@@ -0,0 +1,136 @@
+/*
+ samba -- Unix SMB/CIFS implementation.
+
+ Client credentials structure
+
+ Copyright (C) Jelmer Vernooij 2004-2006
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
+
+ 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 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ 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, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef __CREDENTIALS_INTERNAL_H__
+#define __CREDENTIALS_INTERNAL_H__
+
+#include "../lib/util/data_blob.h"
+#include "librpc/gen_ndr/misc.h"
+#include "libcli/smb/smb_constants.h"
+
+struct cli_credentials {
+ enum credentials_obtained workstation_obtained;
+ enum credentials_obtained username_obtained;
+ enum credentials_obtained password_obtained;
+ enum credentials_obtained domain_obtained;
+ enum credentials_obtained realm_obtained;
+ enum credentials_obtained ccache_obtained;
+ enum credentials_obtained client_gss_creds_obtained;
+ enum credentials_obtained principal_obtained;
+ enum credentials_obtained keytab_obtained;
+ enum credentials_obtained server_gss_creds_obtained;
+ enum credentials_obtained signing_state_obtained;
+ enum credentials_obtained ipc_signing_state_obtained;
+ enum credentials_obtained encryption_state_obtained;
+ enum credentials_obtained kerberos_state_obtained;
+ enum credentials_obtained gensec_features_obtained;
+
+ /* Threshold values (essentially a MAX() over a number of the
+ * above) for the ccache and GSS credentials, to ensure we
+ * regenerate/pick correctly */
+
+ enum credentials_obtained ccache_threshold;
+ enum credentials_obtained client_gss_creds_threshold;
+
+ const char *workstation;
+ const char *username;
+ const char *password;
+ const char *old_password;
+ const char *domain;
+ const char *realm;
+ const char *principal;
+ char *salt_principal;
+ char *impersonate_principal;
+ char *self_service;
+ char *target_service;
+
+ const char *bind_dn;
+
+ /* Allows authentication from a keytab or similar */
+ struct samr_Password *nt_hash;
+ struct samr_Password *old_nt_hash;
+
+ /* Allows NTLM pass-though authentication */
+ DATA_BLOB lm_response;
+ DATA_BLOB lm_session_key;
+ DATA_BLOB nt_response;
+ DATA_BLOB nt_session_key;
+
+ struct ccache_container *ccache;
+ struct gssapi_creds_container *client_gss_creds;
+ struct keytab_container *keytab;
+ struct gssapi_creds_container *server_gss_creds;
+
+ const char *(*workstation_cb) (struct cli_credentials *);
+ const char *(*password_cb) (struct cli_credentials *);
+ const char *(*username_cb) (struct cli_credentials *);
+ const char *(*domain_cb) (struct cli_credentials *);
+ const char *(*realm_cb) (struct cli_credentials *);
+ const char *(*principal_cb) (struct cli_credentials *);
+
+ /* Private handle for the callback routines to use */
+ void *priv_data;
+
+ struct netlogon_creds_CredentialState *netlogon_creds;
+ enum netr_SchannelType secure_channel_type;
+ int kvno;
+ time_t password_last_changed_time;
+
+ struct smb_krb5_context *smb_krb5_context;
+
+ /* We are flagged to get machine account details from the
+ * secrets.ldb when we are asked for a username or password */
+ bool machine_account_pending;
+ struct loadparm_context *machine_account_pending_lp_ctx;
+
+ /* Is this a machine account? */
+ bool machine_account;
+
+ /* Should we be trying to use kerberos? */
+ enum credentials_use_kerberos kerberos_state;
+
+ /* Should we get a forwardable ticket? */
+ enum credentials_krb_forwardable krb_forwardable;
+
+ /* Forced SASL mechansim */
+ char *forced_sasl_mech;
+
+ /* gensec features which should be used for connections */
+ uint32_t gensec_features;
+
+ /* Number of retries left before bailing out */
+ uint32_t password_tries;
+
+ /* Whether any callback is currently running */
+ bool callback_running;
+
+ char winbind_separator;
+
+ bool password_will_be_nt_hash;
+
+ enum smb_signing_setting signing_state;
+
+ enum smb_signing_setting ipc_signing_state;
+
+ enum smb_encryption_setting encryption_state;
+};
+
+#endif /* __CREDENTIALS_INTERNAL_H__ */
diff --git a/auth/credentials/credentials_krb5.c b/auth/credentials/credentials_krb5.c
new file mode 100644
index 0000000..bd47113
--- /dev/null
+++ b/auth/credentials/credentials_krb5.c
@@ -0,0 +1,1530 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Handle user credentials (as regards krb5)
+
+ Copyright (C) Jelmer Vernooij 2005
+ Copyright (C) Tim Potter 2001
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
+
+ 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 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/kerberos.h"
+#include "system/gssapi.h"
+#include "auth/kerberos/kerberos.h"
+#include "auth/credentials/credentials.h"
+#include "auth/credentials/credentials_internal.h"
+#include "auth/credentials/credentials_krb5.h"
+#include "auth/kerberos/kerberos_credentials.h"
+#include "auth/kerberos/kerberos_srv_keytab.h"
+#include "auth/kerberos/kerberos_util.h"
+#include "auth/kerberos/pac_utils.h"
+#include "param/param.h"
+#include "../libds/common/flags.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+#undef strncasecmp
+
+static void cli_credentials_invalidate_client_gss_creds(
+ struct cli_credentials *cred,
+ enum credentials_obtained obtained);
+
+/* Free a memory ccache */
+static int free_mccache(struct ccache_container *ccc)
+{
+ if (ccc->ccache != NULL) {
+ krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
+ ccc->ccache);
+ ccc->ccache = NULL;
+ }
+
+ return 0;
+}
+
+/* Free a disk-based ccache */
+static int free_dccache(struct ccache_container *ccc)
+{
+ if (ccc->ccache != NULL) {
+ krb5_cc_close(ccc->smb_krb5_context->krb5_context,
+ ccc->ccache);
+ ccc->ccache = NULL;
+ }
+
+ return 0;
+}
+
+static uint32_t smb_gss_krb5_copy_ccache(uint32_t *min_stat,
+ gss_cred_id_t cred,
+ struct ccache_container *ccc)
+{
+#ifndef SAMBA4_USES_HEIMDAL /* MIT 1.10 */
+ krb5_context context = ccc->smb_krb5_context->krb5_context;
+ krb5_ccache dummy_ccache = NULL;
+ krb5_creds creds = {0};
+ krb5_cc_cursor cursor = NULL;
+ krb5_principal princ = NULL;
+ krb5_error_code code;
+ char *dummy_name;
+ uint32_t maj_stat = GSS_S_FAILURE;
+
+ dummy_name = talloc_asprintf(ccc,
+ "MEMORY:gss_krb5_copy_ccache-%p",
+ &ccc->ccache);
+ if (dummy_name == NULL) {
+ *min_stat = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+
+ /*
+ * Create a dummy ccache, so we can iterate over the credentials
+ * and find the default principal for the ccache we want to
+ * copy. The new ccache needs to be initialized with this
+ * principal.
+ */
+ code = krb5_cc_resolve(context, dummy_name, &dummy_ccache);
+ TALLOC_FREE(dummy_name);
+ if (code != 0) {
+ *min_stat = code;
+ return GSS_S_FAILURE;
+ }
+
+ /*
+ * We do not need set a default principal on the temporary dummy
+ * ccache, as we do consume it at all in this function.
+ */
+ maj_stat = gss_krb5_copy_ccache(min_stat, cred, dummy_ccache);
+ if (maj_stat != 0) {
+ krb5_cc_close(context, dummy_ccache);
+ return maj_stat;
+ }
+
+ code = krb5_cc_start_seq_get(context, dummy_ccache, &cursor);
+ if (code != 0) {
+ krb5_cc_close(context, dummy_ccache);
+ *min_stat = EINVAL;
+ return GSS_S_FAILURE;
+ }
+
+ code = krb5_cc_next_cred(context,
+ dummy_ccache,
+ &cursor,
+ &creds);
+ if (code != 0) {
+ krb5_cc_close(context, dummy_ccache);
+ *min_stat = EINVAL;
+ return GSS_S_FAILURE;
+ }
+
+ do {
+ if (creds.ticket_flags & TKT_FLG_PRE_AUTH) {
+ krb5_data *tgs;
+
+ tgs = krb5_princ_component(context,
+ creds.server,
+ 0);
+ if (tgs != NULL && tgs->length >= 1) {
+ int cmp;
+
+ cmp = memcmp(tgs->data,
+ KRB5_TGS_NAME,
+ tgs->length);
+ if (cmp == 0 && creds.client != NULL) {
+ princ = creds.client;
+ code = KRB5_CC_END;
+ break;
+ }
+ }
+ }
+
+ krb5_free_cred_contents(context, &creds);
+
+ code = krb5_cc_next_cred(context,
+ dummy_ccache,
+ &cursor,
+ &creds);
+ } while (code == 0);
+
+ if (code == KRB5_CC_END) {
+ krb5_cc_end_seq_get(context, dummy_ccache, &cursor);
+ code = 0;
+ }
+ krb5_cc_close(context, dummy_ccache);
+
+ if (code != 0 || princ == NULL) {
+ krb5_free_cred_contents(context, &creds);
+ *min_stat = EINVAL;
+ return GSS_S_FAILURE;
+ }
+
+ /*
+ * Set the default principal for the cache we copy
+ * into. This is needed to be able that other calls
+ * can read it with e.g. gss_acquire_cred() or
+ * krb5_cc_get_principal().
+ */
+ code = krb5_cc_initialize(context, ccc->ccache, princ);
+ if (code != 0) {
+ krb5_free_cred_contents(context, &creds);
+ *min_stat = EINVAL;
+ return GSS_S_FAILURE;
+ }
+ krb5_free_cred_contents(context, &creds);
+
+#endif /* SAMBA4_USES_HEIMDAL */
+
+ return gss_krb5_copy_ccache(min_stat,
+ cred,
+ ccc->ccache);
+}
+
+_PUBLIC_ int cli_credentials_get_krb5_context(struct cli_credentials *cred,
+ struct loadparm_context *lp_ctx,
+ struct smb_krb5_context **smb_krb5_context)
+{
+ int ret;
+ if (cred->smb_krb5_context) {
+ *smb_krb5_context = cred->smb_krb5_context;
+ return 0;
+ }
+
+ ret = smb_krb5_init_context(cred, lp_ctx,
+ &cred->smb_krb5_context);
+ if (ret) {
+ cred->smb_krb5_context = NULL;
+ return ret;
+ }
+ *smb_krb5_context = cred->smb_krb5_context;
+ return 0;
+}
+
+/* For most predictable behaviour, this needs to be called directly after the cli_credentials_init(),
+ * otherwise we may still have references to the old smb_krb5_context in a credential cache etc
+ */
+_PUBLIC_ NTSTATUS cli_credentials_set_krb5_context(struct cli_credentials *cred,
+ struct smb_krb5_context *smb_krb5_context)
+{
+ if (smb_krb5_context == NULL) {
+ talloc_unlink(cred, cred->smb_krb5_context);
+ cred->smb_krb5_context = NULL;
+ return NT_STATUS_OK;
+ }
+
+ if (!talloc_reference(cred, smb_krb5_context)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ cred->smb_krb5_context = smb_krb5_context;
+ return NT_STATUS_OK;
+}
+
+static int cli_credentials_set_from_ccache(struct cli_credentials *cred,
+ struct ccache_container *ccache,
+ enum credentials_obtained obtained,
+ const char **error_string)
+{
+ bool ok;
+ char *realm;
+ krb5_principal princ;
+ krb5_error_code ret;
+ char *name;
+
+ if (cred->ccache_obtained > obtained) {
+ return 0;
+ }
+
+ ret = krb5_cc_get_principal(ccache->smb_krb5_context->krb5_context,
+ ccache->ccache, &princ);
+
+ if (ret) {
+ (*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) {
+ (*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;
+ }
+
+ ok = cli_credentials_set_principal(cred, name, obtained);
+ krb5_free_unparsed_name(ccache->smb_krb5_context->krb5_context, name);
+ if (!ok) {
+ krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
+ return ENOMEM;
+ }
+
+ realm = smb_krb5_principal_get_realm(
+ cred, ccache->smb_krb5_context->krb5_context, princ);
+ krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
+ if (realm == NULL) {
+ return ENOMEM;
+ }
+ ok = cli_credentials_set_realm(cred, realm, obtained);
+ TALLOC_FREE(realm);
+ if (!ok) {
+ return ENOMEM;
+ }
+
+ /* set the ccache_obtained here, as it just got set to UNINITIALISED by the calls above */
+ cred->ccache_obtained = obtained;
+
+ return 0;
+}
+
+_PUBLIC_ int cli_credentials_set_ccache(struct cli_credentials *cred,
+ struct loadparm_context *lp_ctx,
+ const char *name,
+ enum credentials_obtained obtained,
+ const char **error_string)
+{
+ krb5_error_code ret;
+ krb5_principal princ;
+ struct ccache_container *ccc;
+ if (cred->ccache_obtained > obtained) {
+ return 0;
+ }
+
+ ccc = talloc(cred, struct ccache_container);
+ if (!ccc) {
+ (*error_string) = error_message(ENOMEM);
+ return ENOMEM;
+ }
+
+ ret = cli_credentials_get_krb5_context(cred, 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) {
+ (*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) {
+ (*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;
+ }
+ }
+
+ talloc_set_destructor(ccc, free_dccache);
+
+ ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context, ccc->ccache, &princ);
+
+ if (ret == 0) {
+ krb5_free_principal(ccc->smb_krb5_context->krb5_context, princ);
+ ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
+
+ if (ret) {
+ (*error_string) = error_message(ret);
+ TALLOC_FREE(ccc);
+ return ret;
+ }
+ }
+
+ cred->ccache = ccc;
+ cred->ccache_obtained = obtained;
+
+ cli_credentials_invalidate_client_gss_creds(
+ cred, cred->ccache_obtained);
+
+ return 0;
+}
+
+#ifndef SAMBA4_USES_HEIMDAL
+/*
+ * This function is a workaround for old MIT Kerberos versions which did not
+ * implement the krb5_cc_remove_cred function. It creates a temporary
+ * credentials cache to copy the credentials in the current cache
+ * except the one we want to remove and then overwrites the contents of the
+ * current cache with the temporary copy.
+ */
+static krb5_error_code krb5_cc_remove_cred_wrap(struct ccache_container *ccc,
+ krb5_creds *creds)
+{
+ krb5_ccache dummy_ccache = NULL;
+ krb5_creds cached_creds = {0};
+ krb5_cc_cursor cursor = NULL;
+ krb5_error_code code;
+ char *dummy_name;
+
+ dummy_name = talloc_asprintf(ccc,
+ "MEMORY:copy_ccache-%p",
+ &ccc->ccache);
+ if (dummy_name == NULL) {
+ return KRB5_CC_NOMEM;
+ }
+
+ code = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context,
+ dummy_name,
+ &dummy_ccache);
+ if (code != 0) {
+ DBG_ERR("krb5_cc_resolve failed: %s\n",
+ smb_get_krb5_error_message(
+ ccc->smb_krb5_context->krb5_context,
+ code, ccc));
+ TALLOC_FREE(dummy_name);
+ return code;
+ }
+
+ TALLOC_FREE(dummy_name);
+
+ code = krb5_cc_start_seq_get(ccc->smb_krb5_context->krb5_context,
+ ccc->ccache,
+ &cursor);
+ if (code != 0) {
+ krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
+ dummy_ccache);
+
+ DBG_ERR("krb5_cc_start_seq_get failed: %s\n",
+ smb_get_krb5_error_message(
+ ccc->smb_krb5_context->krb5_context,
+ code, ccc));
+ return code;
+ }
+
+ while ((code = krb5_cc_next_cred(ccc->smb_krb5_context->krb5_context,
+ ccc->ccache,
+ &cursor,
+ &cached_creds)) == 0) {
+ /* If the principal matches skip it and do not copy to the
+ * temporary cache as this is the one we want to remove */
+ if (krb5_principal_compare_flags(
+ ccc->smb_krb5_context->krb5_context,
+ creds->server,
+ cached_creds.server,
+ 0)) {
+ continue;
+ }
+
+ code = krb5_cc_store_cred(
+ ccc->smb_krb5_context->krb5_context,
+ dummy_ccache,
+ &cached_creds);
+ if (code != 0) {
+ krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
+ dummy_ccache);
+ DBG_ERR("krb5_cc_store_cred failed: %s\n",
+ smb_get_krb5_error_message(
+ ccc->smb_krb5_context->krb5_context,
+ code, ccc));
+ return code;
+ }
+ }
+
+ if (code == KRB5_CC_END) {
+ krb5_cc_end_seq_get(ccc->smb_krb5_context->krb5_context,
+ dummy_ccache,
+ &cursor);
+ code = 0;
+ }
+
+ if (code != 0) {
+ krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
+ dummy_ccache);
+ DBG_ERR("krb5_cc_next_cred failed: %s\n",
+ smb_get_krb5_error_message(
+ ccc->smb_krb5_context->krb5_context,
+ code, ccc));
+ return code;
+ }
+
+ code = krb5_cc_initialize(ccc->smb_krb5_context->krb5_context,
+ ccc->ccache,
+ creds->client);
+ if (code != 0) {
+ krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
+ dummy_ccache);
+ DBG_ERR("krb5_cc_initialize failed: %s\n",
+ smb_get_krb5_error_message(
+ ccc->smb_krb5_context->krb5_context,
+ code, ccc));
+ return code;
+ }
+
+ code = krb5_cc_copy_creds(ccc->smb_krb5_context->krb5_context,
+ dummy_ccache,
+ ccc->ccache);
+ if (code != 0) {
+ krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
+ dummy_ccache);
+ DBG_ERR("krb5_cc_copy_creds failed: %s\n",
+ smb_get_krb5_error_message(
+ ccc->smb_krb5_context->krb5_context,
+ code, ccc));
+ return code;
+ }
+
+ code = krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
+ dummy_ccache);
+ if (code != 0) {
+ DBG_ERR("krb5_cc_destroy failed: %s\n",
+ smb_get_krb5_error_message(
+ ccc->smb_krb5_context->krb5_context,
+ code, ccc));
+ return code;
+ }
+
+ return code;
+}
+#endif
+
+/*
+ * Indicate the we failed to log in to this service/host with these
+ * credentials. The caller passes an unsigned int which they
+ * initialise to the number of times they would like to retry.
+ *
+ * This method is used to support re-trying with freshly fetched
+ * credentials in case a server is rebuilt while clients have
+ * non-expired tickets. When the client code gets a logon failure they
+ * throw away the existing credentials for the server and retry.
+ */
+_PUBLIC_ bool cli_credentials_failed_kerberos_login(struct cli_credentials *cred,
+ const char *principal,
+ unsigned int *count)
+{
+ struct ccache_container *ccc;
+ krb5_creds creds, creds2;
+ int ret;
+
+ if (principal == NULL) {
+ /* no way to delete if we don't know the principal */
+ return false;
+ }
+
+ ccc = cred->ccache;
+ if (ccc == NULL) {
+ /* not a kerberos connection */
+ return false;
+ }
+
+ if (*count > 0) {
+ /* We have already tried discarding the credentials */
+ return false;
+ }
+ (*count)++;
+
+ ZERO_STRUCT(creds);
+ ret = krb5_parse_name(ccc->smb_krb5_context->krb5_context, principal, &creds.server);
+ if (ret != 0) {
+ return false;
+ }
+
+ /* MIT kerberos requires creds.client to match against cached
+ * credentials */
+ ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context,
+ ccc->ccache,
+ &creds.client);
+ if (ret != 0) {
+ krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context,
+ &creds);
+ DBG_ERR("krb5_cc_get_principal failed: %s\n",
+ smb_get_krb5_error_message(
+ ccc->smb_krb5_context->krb5_context,
+ ret, ccc));
+ return false;
+ }
+
+ ret = krb5_cc_retrieve_cred(ccc->smb_krb5_context->krb5_context, ccc->ccache, KRB5_TC_MATCH_SRV_NAMEONLY, &creds, &creds2);
+ if (ret != 0) {
+ /* don't retry - we didn't find these credentials to remove */
+ krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds);
+ return false;
+ }
+
+ ret = krb5_cc_remove_cred(ccc->smb_krb5_context->krb5_context, ccc->ccache, KRB5_TC_MATCH_SRV_NAMEONLY, &creds);
+#ifndef SAMBA4_USES_HEIMDAL
+ if (ret == KRB5_CC_NOSUPP) {
+ /* Old MIT kerberos versions did not implement
+ * krb5_cc_remove_cred */
+ ret = krb5_cc_remove_cred_wrap(ccc, &creds);
+ }
+#endif
+ krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds);
+ krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds2);
+ if (ret != 0) {
+ /* don't retry - we didn't find these credentials to
+ * remove. Note that with the current backend this
+ * never happens, as it always returns 0 even if the
+ * creds don't exist, which is why we do a separate
+ * krb5_cc_retrieve_cred() above.
+ */
+ DBG_ERR("krb5_cc_remove_cred failed: %s\n",
+ smb_get_krb5_error_message(
+ ccc->smb_krb5_context->krb5_context,
+ ret, ccc));
+ return false;
+ }
+ return true;
+}
+
+
+static int cli_credentials_new_ccache(struct cli_credentials *cred,
+ 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);
+ if (!ccc) {
+ return ENOMEM;
+ }
+
+ ret = cli_credentials_get_krb5_context(cred, 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;
+
+ if (lpcfg_parm_bool(lp_ctx, NULL, "credentials", "krb5_cc_file", false)) {
+ ccache_name = talloc_asprintf(ccc, "FILE:/tmp/krb5_cc_samba_%u_%p",
+ (unsigned int)getpid(), ccc);
+ } else {
+ 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) {
+ (*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;
+ }
+
+ if (strncasecmp(ccache_name, "MEMORY:", 7) == 0) {
+ talloc_set_destructor(ccc, free_mccache);
+ } else {
+ talloc_set_destructor(ccc, free_dccache);
+ }
+
+ if (must_free_cc_name) {
+ talloc_free(ccache_name);
+ }
+
+ *_ccc = ccc;
+
+ return 0;
+}
+
+_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;
+ enum credentials_obtained obtained;
+
+ if (cred->machine_account_pending) {
+ cli_credentials_set_machine_account(cred, lp_ctx);
+ }
+
+ if (cred->ccache_obtained >= cred->ccache_threshold &&
+ cred->ccache_obtained > CRED_UNINITIALISED) {
+ time_t lifetime;
+ bool expired = false;
+ ret = smb_krb5_cc_get_lifetime(cred->ccache->smb_krb5_context->krb5_context,
+ cred->ccache->ccache, &lifetime);
+ if (ret == KRB5_CC_END || ret == ENOENT) {
+ /* If we have a particular ccache set, without
+ * an initial ticket, then assume there is a
+ * good reason */
+ } else if (ret == 0) {
+ if (lifetime == 0) {
+ DEBUG(3, ("Ticket in credentials cache for %s expired, will refresh\n",
+ cli_credentials_get_principal(cred, cred)));
+ expired = true;
+ } else if (lifetime < 300) {
+ DEBUG(3, ("Ticket in credentials cache for %s will shortly expire (%u secs), will refresh\n",
+ cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
+ expired = true;
+ }
+ } else {
+ (*error_string) = talloc_asprintf(cred, "failed to get ccache lifetime: %s\n",
+ smb_get_krb5_error_message(cred->ccache->smb_krb5_context->krb5_context,
+ ret, cred));
+ return ret;
+ }
+
+ DEBUG(5, ("Ticket in credentials cache for %s will expire in %u secs\n",
+ cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
+
+ if (!expired) {
+ *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, lp_ctx, ccache_name, ccc, error_string);
+ if (ret) {
+ return ret;
+ }
+
+ ret = kinit_to_ccache(cred, cred, (*ccc)->smb_krb5_context, event_ctx, (*ccc)->ccache, &obtained, error_string);
+ if (ret) {
+ return ret;
+ }
+
+ ret = cli_credentials_set_from_ccache(cred, *ccc,
+ obtained, error_string);
+
+ cred->ccache = *ccc;
+ cred->ccache_obtained = cred->principal_obtained;
+ if (ret) {
+ 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);
+}
+
+/* We have good reason to think the ccache in these credentials is invalid - blow it away */
+static void cli_credentials_unconditionally_invalidate_client_gss_creds(struct cli_credentials *cred)
+{
+ 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;
+}
+
+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 command 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;
+ }
+}
+
+/* We have good reason to think this CCACHE is invalid. Blow it away */
+static void cli_credentials_unconditionally_invalidate_ccache(struct cli_credentials *cred)
+{
+ if (cred->ccache_obtained > CRED_UNINITIALISED) {
+ talloc_unlink(cred, cred->ccache);
+ cred->ccache = NULL;
+ }
+ cred->ccache_obtained = CRED_UNINITIALISED;
+
+ cli_credentials_unconditionally_invalidate_client_gss_creds(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
+ * 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. i.e, if the username is on the command 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)
+{
+ OM_uint32 min_stat;
+ (void)gss_release_cred(&min_stat, &gcc->creds);
+ return 0;
+}
+
+_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;
+#ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
+ gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
+ gss_OID oid = discard_const(GSS_KRB5_CRED_NO_CI_FLAGS_X);
+#endif
+ krb5_enctype *etypes = NULL;
+
+ if (cred->client_gss_creds_obtained >= cred->client_gss_creds_threshold &&
+ cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
+ bool expired = false;
+ OM_uint32 lifetime = 0;
+ gss_cred_usage_t usage = 0;
+ maj_stat = gss_inquire_cred(&min_stat, cred->client_gss_creds->creds,
+ NULL, &lifetime, &usage, NULL);
+ if (maj_stat == GSS_S_CREDENTIALS_EXPIRED) {
+ DEBUG(3, ("Credentials for %s expired, must refresh credentials cache\n", cli_credentials_get_principal(cred, cred)));
+ expired = true;
+ } else if (maj_stat == GSS_S_COMPLETE && lifetime < 300) {
+ DEBUG(3, ("Credentials for %s will expire shortly (%u sec), must refresh credentials cache\n", cli_credentials_get_principal(cred, cred), lifetime));
+ expired = true;
+ } else if (maj_stat != GSS_S_COMPLETE) {
+ *error_string = talloc_asprintf(cred, "inquiry of credential lifefime via GSSAPI gss_inquire_cred failed: %s\n",
+ gssapi_error_string(cred, maj_stat, min_stat, NULL));
+ return EINVAL;
+ }
+ if (expired) {
+ cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
+ } else {
+ DEBUG(5, ("GSSAPI credentials for %s will expire in %u secs\n",
+ cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
+
+ *_gcc = cred->client_gss_creds;
+ return 0;
+ }
+ }
+
+ ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
+ &ccache, error_string);
+ if (ret) {
+ if (cli_credentials_get_kerberos_state(cred) == CRED_USE_KERBEROS_REQUIRED) {
+ DEBUG(1, ("Failed to get kerberos credentials (kerberos required): %s\n", *error_string));
+ } else {
+ DEBUG(4, ("Failed to get kerberos credentials: %s\n", *error_string));
+ }
+ return ret;
+ }
+
+ gcc = talloc(cred, struct gssapi_creds_container);
+ if (!gcc) {
+ (*error_string) = error_message(ENOMEM);
+ return ENOMEM;
+ }
+
+ maj_stat = smb_gss_krb5_import_cred(&min_stat, ccache->smb_krb5_context->krb5_context,
+ ccache->ccache, NULL, NULL,
+ &gcc->creds);
+ if ((maj_stat == GSS_S_FAILURE) &&
+ (min_stat == (OM_uint32)KRB5_CC_END ||
+ min_stat == (OM_uint32)KRB5_CC_NOTFOUND ||
+ min_stat == (OM_uint32)KRB5_FCC_NOFILE))
+ {
+ /* This CCACHE is no good. Ensure we don't use it again */
+ cli_credentials_unconditionally_invalidate_ccache(cred);
+
+ /* Now try again to get a ccache */
+ ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
+ &ccache, error_string);
+ if (ret) {
+ DEBUG(1, ("Failed to re-get CCACHE for GSSAPI client: %s\n", error_message(ret)));
+ return ret;
+ }
+
+ maj_stat = smb_gss_krb5_import_cred(&min_stat, ccache->smb_krb5_context->krb5_context,
+ ccache->ccache, NULL, NULL,
+ &gcc->creds);
+
+ }
+
+ if (maj_stat) {
+ talloc_free(gcc);
+ if (min_stat) {
+ ret = min_stat;
+ } else {
+ ret = EINVAL;
+ }
+ (*error_string) = talloc_asprintf(cred, "smb_gss_krb5_import_cred failed: %s", error_message(ret));
+ return ret;
+ }
+
+
+ /*
+ * 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 = smb_krb5_get_allowed_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,
+ (int32_t *) etypes);
+ SAFE_FREE(etypes);
+ if (maj_stat) {
+ talloc_free(gcc);
+ if (min_stat) {
+ ret = min_stat;
+ } else {
+ ret = EINVAL;
+ }
+ (*error_string) = talloc_asprintf(cred, "gss_krb5_set_allowable_enctypes failed: %s", error_message(ret));
+ return ret;
+ }
+ }
+
+#ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
+ /*
+ * Don't force GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG.
+ *
+ * This allows us to disable SIGN and SEAL on a TLS connection with
+ * GSS-SPNENO. For example ldaps:// connections.
+ *
+ * https://groups.yahoo.com/neo/groups/cat-ietf/conversations/topics/575
+ * http://krbdev.mit.edu/rt/Ticket/Display.html?id=6938
+ */
+ maj_stat = gss_set_cred_option(&min_stat, &gcc->creds,
+ oid,
+ &empty_buffer);
+ if (maj_stat) {
+ talloc_free(gcc);
+ if (min_stat) {
+ ret = min_stat;
+ } else {
+ ret = EINVAL;
+ }
+ (*error_string) = talloc_asprintf(cred, "gss_set_cred_option failed: %s", error_message(ret));
+ return ret;
+ }
+#endif
+ 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 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
+ the case where we deal with GSSAPI mechs other than krb5.
+
+ On sucess, the caller must not free gssapi_cred, as it now belongs
+ to the credentials system.
+*/
+
+ int cli_credentials_set_client_gss_creds(struct cli_credentials *cred,
+ 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;
+ struct ccache_container *ccc = NULL;
+ struct gssapi_creds_container *gcc = NULL;
+ if (cred->client_gss_creds_obtained > obtained) {
+ return 0;
+ }
+
+ gcc = talloc(cred, struct gssapi_creds_container);
+ if (!gcc) {
+ (*error_string) = error_message(ENOMEM);
+ return ENOMEM;
+ }
+
+ ret = cli_credentials_new_ccache(cred, lp_ctx, NULL, &ccc, error_string);
+ if (ret != 0) {
+ return ret;
+ }
+
+ maj_stat = smb_gss_krb5_copy_ccache(&min_stat,
+ gssapi_cred,
+ ccc);
+ if (maj_stat) {
+ if (min_stat) {
+ ret = min_stat;
+ } else {
+ ret = EINVAL;
+ }
+ if (ret) {
+ (*error_string) = error_message(ENOMEM);
+ }
+ }
+
+ if (ret == 0) {
+ 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;
+ }
+ return ret;
+}
+
+static int cli_credentials_shallow_ccache(struct cli_credentials *cred)
+{
+ krb5_error_code ret;
+ const struct ccache_container *old_ccc = NULL;
+ enum credentials_obtained old_obtained;
+ struct ccache_container *ccc = NULL;
+ char *ccache_name = NULL;
+ krb5_principal princ;
+
+ old_obtained = cred->ccache_obtained;
+ old_ccc = cred->ccache;
+ if (old_ccc == NULL) {
+ return 0;
+ }
+
+ cred->ccache = NULL;
+ cred->ccache_obtained = CRED_UNINITIALISED;
+ cred->client_gss_creds = NULL;
+ cred->client_gss_creds_obtained = CRED_UNINITIALISED;
+
+ ret = krb5_cc_get_principal(
+ old_ccc->smb_krb5_context->krb5_context,
+ old_ccc->ccache,
+ &princ);
+ if (ret != 0) {
+ /*
+ * This is an empty ccache. No point in copying anything.
+ */
+ return 0;
+ }
+ krb5_free_principal(old_ccc->smb_krb5_context->krb5_context, princ);
+
+ ccc = talloc(cred, struct ccache_container);
+ if (ccc == NULL) {
+ return ENOMEM;
+ }
+ *ccc = *old_ccc;
+ ccc->ccache = NULL;
+
+ ccache_name = talloc_asprintf(ccc, "MEMORY:%p", ccc);
+
+ ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context,
+ ccache_name, &ccc->ccache);
+ if (ret != 0) {
+ TALLOC_FREE(ccc);
+ return ret;
+ }
+
+ talloc_set_destructor(ccc, free_mccache);
+
+ TALLOC_FREE(ccache_name);
+
+ ret = smb_krb5_cc_copy_creds(ccc->smb_krb5_context->krb5_context,
+ old_ccc->ccache, ccc->ccache);
+ if (ret != 0) {
+ TALLOC_FREE(ccc);
+ return ret;
+ }
+
+ cred->ccache = ccc;
+ cred->ccache_obtained = old_obtained;
+ return ret;
+}
+
+_PUBLIC_ struct cli_credentials *cli_credentials_shallow_copy(TALLOC_CTX *mem_ctx,
+ struct cli_credentials *src)
+{
+ struct cli_credentials *dst;
+ int ret;
+
+ dst = talloc(mem_ctx, struct cli_credentials);
+ if (dst == NULL) {
+ return NULL;
+ }
+
+ *dst = *src;
+
+ ret = cli_credentials_shallow_ccache(dst);
+ if (ret != 0) {
+ TALLOC_FREE(dst);
+ return NULL;
+ }
+
+ return dst;
+}
+
+/* Get the keytab (actually, a container containing the krb5_keytab)
+ * attached to this context. If this hasn't been done or set before,
+ * it will be generated from the password.
+ */
+_PUBLIC_ int cli_credentials_get_keytab(struct cli_credentials *cred,
+ struct loadparm_context *lp_ctx,
+ struct keytab_container **_ktc)
+{
+ krb5_error_code ret;
+ struct keytab_container *ktc;
+ struct smb_krb5_context *smb_krb5_context;
+ const char *keytab_name;
+ krb5_keytab keytab;
+ TALLOC_CTX *mem_ctx;
+ const char *username = cli_credentials_get_username(cred);
+ const char *upn = NULL;
+ const char *realm = cli_credentials_get_realm(cred);
+ char *salt_principal = NULL;
+ uint32_t uac_flags = 0;
+
+ if (cred->keytab_obtained >= (MAX(cred->principal_obtained,
+ cred->username_obtained))) {
+ *_ktc = cred->keytab;
+ return 0;
+ }
+
+ if (cli_credentials_is_anonymous(cred)) {
+ return EINVAL;
+ }
+
+ ret = cli_credentials_get_krb5_context(cred, lp_ctx,
+ &smb_krb5_context);
+ if (ret) {
+ return ret;
+ }
+
+ mem_ctx = talloc_new(cred);
+ if (!mem_ctx) {
+ return ENOMEM;
+ }
+
+ switch (cred->secure_channel_type) {
+ case SEC_CHAN_WKSTA:
+ case SEC_CHAN_RODC:
+ uac_flags = UF_WORKSTATION_TRUST_ACCOUNT;
+ break;
+ case SEC_CHAN_BDC:
+ uac_flags = UF_SERVER_TRUST_ACCOUNT;
+ break;
+ case SEC_CHAN_DOMAIN:
+ case SEC_CHAN_DNS_DOMAIN:
+ uac_flags = UF_INTERDOMAIN_TRUST_ACCOUNT;
+ break;
+ default:
+ upn = cli_credentials_get_principal(cred, mem_ctx);
+ if (upn == NULL) {
+ TALLOC_FREE(mem_ctx);
+ return ENOMEM;
+ }
+ uac_flags = UF_NORMAL_ACCOUNT;
+ break;
+ }
+
+ ret = smb_krb5_salt_principal_str(realm,
+ username, /* sAMAccountName */
+ upn, /* userPrincipalName */
+ uac_flags,
+ mem_ctx,
+ &salt_principal);
+ if (ret) {
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ ret = smb_krb5_create_memory_keytab(mem_ctx,
+ smb_krb5_context->krb5_context,
+ cli_credentials_get_password(cred),
+ username,
+ realm,
+ salt_principal,
+ cli_credentials_get_kvno(cred),
+ &keytab,
+ &keytab_name);
+ if (ret) {
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
+ keytab, keytab_name, &ktc);
+ if (ret) {
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ cred->keytab_obtained = (MAX(cred->principal_obtained,
+ cred->username_obtained));
+
+ /* We make this keytab up based on a password. Therefore
+ * match-by-key is acceptable, we can't match on the wrong
+ * principal */
+ ktc->password_based = true;
+
+ talloc_steal(cred, ktc);
+ cred->keytab = ktc;
+ *_ktc = cred->keytab;
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+/* Given the name of a keytab (presumably in the format
+ * FILE:/etc/krb5.keytab), open it and attach it */
+
+_PUBLIC_ int cli_credentials_set_keytab_name(struct cli_credentials *cred,
+ struct loadparm_context *lp_ctx,
+ const char *keytab_name,
+ enum credentials_obtained obtained)
+{
+ krb5_error_code ret;
+ struct keytab_container *ktc;
+ struct smb_krb5_context *smb_krb5_context;
+ TALLOC_CTX *mem_ctx;
+
+ if (cred->keytab_obtained >= obtained) {
+ return 0;
+ }
+
+ ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
+ if (ret) {
+ return ret;
+ }
+
+ mem_ctx = talloc_new(cred);
+ if (!mem_ctx) {
+ return ENOMEM;
+ }
+
+ ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
+ NULL, keytab_name, &ktc);
+ if (ret) {
+ return ret;
+ }
+
+ cred->keytab_obtained = obtained;
+
+ talloc_steal(cred, ktc);
+ cred->keytab = ktc;
+ talloc_free(mem_ctx);
+
+ return ret;
+}
+
+/* Get server gss credentials (in gsskrb5, this means the keytab) */
+
+_PUBLIC_ int cli_credentials_get_server_gss_creds(struct cli_credentials *cred,
+ struct loadparm_context *lp_ctx,
+ struct gssapi_creds_container **_gcc)
+{
+ int ret = 0;
+ OM_uint32 maj_stat, min_stat;
+ struct gssapi_creds_container *gcc;
+ struct keytab_container *ktc;
+ struct smb_krb5_context *smb_krb5_context;
+ TALLOC_CTX *mem_ctx;
+ krb5_principal princ;
+ const char *error_string;
+ enum credentials_obtained obtained;
+
+ mem_ctx = talloc_new(cred);
+ if (!mem_ctx) {
+ return ENOMEM;
+ }
+
+ ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
+ if (ret) {
+ return ret;
+ }
+
+ ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ, &obtained, &error_string);
+ if (ret) {
+ DEBUG(1,("cli_credentials_get_server_gss_creds: making krb5 principal failed (%s)\n",
+ error_string));
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained, obtained))) {
+ talloc_free(mem_ctx);
+ *_gcc = cred->server_gss_creds;
+ return 0;
+ }
+
+ ret = cli_credentials_get_keytab(cred, lp_ctx, &ktc);
+ if (ret) {
+ DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret)));
+ return ret;
+ }
+
+ gcc = talloc(cred, struct gssapi_creds_container);
+ if (!gcc) {
+ talloc_free(mem_ctx);
+ return ENOMEM;
+ }
+
+ if (ktc->password_based || obtained < CRED_SPECIFIED) {
+ /*
+ * This creates a GSSAPI cred_id_t for match-by-key with only
+ * the keytab set
+ */
+ princ = NULL;
+ }
+ maj_stat = smb_gss_krb5_import_cred(&min_stat,
+ smb_krb5_context->krb5_context,
+ NULL, princ,
+ ktc->keytab,
+ &gcc->creds);
+ if (maj_stat) {
+ if (min_stat) {
+ ret = min_stat;
+ } else {
+ ret = EINVAL;
+ }
+ }
+ if (ret == 0) {
+ cred->server_gss_creds_obtained = cred->keytab_obtained;
+ talloc_set_destructor(gcc, free_gssapi_creds);
+ cred->server_gss_creds = gcc;
+ *_gcc = gcc;
+ }
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+/**
+ * Set Kerberos KVNO
+ */
+
+_PUBLIC_ void cli_credentials_set_kvno(struct cli_credentials *cred,
+ int kvno)
+{
+ cred->kvno = kvno;
+}
+
+/**
+ * Return Kerberos KVNO
+ */
+
+_PUBLIC_ int cli_credentials_get_kvno(struct cli_credentials *cred)
+{
+ return cred->kvno;
+}
+
+
+const char *cli_credentials_get_salt_principal(struct cli_credentials *cred)
+{
+ return cred->salt_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 one 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 */
+
+_PUBLIC_ const char *cli_credentials_get_impersonate_principal(struct cli_credentials *cred)
+{
+ return cred->impersonate_principal;
+}
+
+/*
+ * The 'self_service' is the service principal that
+ * represents the same object (by its objectSid)
+ * as the client principal (typically our machine account).
+ * When trying to impersonate 'impersonate_principal' with
+ * S4U2Self.
+ */
+_PUBLIC_ const char *cli_credentials_get_self_service(struct cli_credentials *cred)
+{
+ return cred->self_service;
+}
+
+_PUBLIC_ void cli_credentials_set_impersonate_principal(struct cli_credentials *cred,
+ const char *principal,
+ const char *self_service)
+{
+ talloc_free(cred->impersonate_principal);
+ cred->impersonate_principal = talloc_strdup(cred, principal);
+ talloc_free(cred->self_service);
+ cred->self_service = talloc_strdup(cred, self_service);
+ cli_credentials_set_kerberos_state(cred,
+ CRED_USE_KERBEROS_REQUIRED,
+ CRED_SPECIFIED);
+}
+
+/*
+ * when impersonating for S4U2proxy we need to set the target principal.
+ * 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);
+}
+
+_PUBLIC_ int cli_credentials_get_aes256_key(struct cli_credentials *cred,
+ TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ const char *salt,
+ DATA_BLOB *aes_256)
+{
+ struct smb_krb5_context *smb_krb5_context = NULL;
+ krb5_error_code krb5_ret;
+ int ret;
+ const char *password = NULL;
+ krb5_data cleartext_data;
+ krb5_data salt_data;
+ krb5_keyblock key;
+
+ if (cred->password_will_be_nt_hash) {
+ DEBUG(1,("cli_credentials_get_aes256_key: cannot generate AES256 key using NT hash\n"));
+ return EINVAL;
+ }
+
+ password = cli_credentials_get_password(cred);
+ if (password == NULL) {
+ return EINVAL;
+ }
+
+ cleartext_data.data = discard_const_p(char, password);
+ cleartext_data.length = strlen(password);
+
+ ret = cli_credentials_get_krb5_context(cred, lp_ctx,
+ &smb_krb5_context);
+ if (ret != 0) {
+ return ret;
+ }
+
+ salt_data.data = discard_const_p(char, salt);
+ salt_data.length = strlen(salt);
+
+ /*
+ * create ENCTYPE_AES256_CTS_HMAC_SHA1_96 key out of
+ * the salt and the cleartext password
+ */
+ krb5_ret = smb_krb5_create_key_from_string(smb_krb5_context->krb5_context,
+ NULL,
+ &salt_data,
+ &cleartext_data,
+ ENCTYPE_AES256_CTS_HMAC_SHA1_96,
+ &key);
+ if (krb5_ret != 0) {
+ DEBUG(1,("cli_credentials_get_aes256_key: "
+ "generation of a aes256-cts-hmac-sha1-96 key failed: %s",
+ smb_get_krb5_error_message(smb_krb5_context->krb5_context,
+ krb5_ret, mem_ctx)));
+ return EINVAL;
+ }
+ *aes_256 = data_blob_talloc(mem_ctx,
+ KRB5_KEY_DATA(&key),
+ KRB5_KEY_LENGTH(&key));
+ krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &key);
+ if (aes_256->data == NULL) {
+ return ENOMEM;
+ }
+ talloc_keep_secret(aes_256->data);
+
+ return 0;
+}
diff --git a/auth/credentials/credentials_krb5.h b/auth/credentials/credentials_krb5.h
new file mode 100644
index 0000000..ae60104
--- /dev/null
+++ b/auth/credentials/credentials_krb5.h
@@ -0,0 +1,45 @@
+/*
+ samba -- Unix SMB/CIFS implementation.
+
+ Client credentials structure
+
+ Copyright (C) Jelmer Vernooij 2004-2006
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
+
+ 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 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __CREDENTIALS_KRB5_H__
+#define __CREDENTIALS_KRB5_H__
+
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_krb5.h>
+#include <krb5.h>
+
+struct gssapi_creds_container {
+ gss_cred_id_t creds;
+};
+
+/* Manually prototyped here to avoid needing gss headers in most callers */
+int cli_credentials_set_client_gss_creds(struct cli_credentials *cred,
+ struct loadparm_context *lp_ctx,
+ gss_cred_id_t gssapi_cred,
+ enum credentials_obtained obtained,
+ const char **error_string);
+
+struct cli_credentials *cli_credentials_shallow_copy(TALLOC_CTX *mem_ctx,
+ struct cli_credentials *src);
+
+
+#endif /* __CREDENTIALS_KRB5_H__ */
diff --git a/auth/credentials/credentials_ntlm.c b/auth/credentials/credentials_ntlm.c
new file mode 100644
index 0000000..1c17148
--- /dev/null
+++ b/auth/credentials/credentials_ntlm.c
@@ -0,0 +1,555 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ User credentials handling
+
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-2005
+ Copyright (C) Stefan Metzmacher 2005
+
+ 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 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "librpc/gen_ndr/samr.h" /* for struct samrPassword */
+#include "../lib/crypto/crypto.h"
+#include "libcli/auth/libcli_auth.h"
+#include "auth/credentials/credentials.h"
+#include "auth/credentials/credentials_internal.h"
+
+#include "lib/crypto/gnutls_helpers.h"
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+_PUBLIC_ NTSTATUS cli_credentials_get_ntlm_response(struct cli_credentials *cred, TALLOC_CTX *mem_ctx,
+ int *flags,
+ DATA_BLOB challenge,
+ const NTTIME *server_timestamp,
+ DATA_BLOB target_info,
+ DATA_BLOB *_lm_response, DATA_BLOB *_nt_response,
+ DATA_BLOB *_lm_session_key, DATA_BLOB *_session_key)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ const char *user = NULL;
+ const char *domain = NULL;
+ DATA_BLOB lm_response = data_blob_null;
+ DATA_BLOB nt_response = data_blob_null;
+ DATA_BLOB lm_session_key = data_blob_null;
+ DATA_BLOB session_key = data_blob_null;
+ const struct samr_Password *nt_hash = NULL;
+ int rc;
+
+ if (cred->kerberos_state == CRED_USE_KERBEROS_REQUIRED) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_INVALID_PARAMETER_MIX;
+ }
+
+ /* We may already have an NTLM response we prepared earlier.
+ * This is used for NTLM pass-though authentication */
+ if (cred->nt_response.data || cred->lm_response.data) {
+ if (cred->nt_response.length != 0) {
+ nt_response = data_blob_dup_talloc(frame,
+ cred->nt_response);
+ if (nt_response.data == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+ if (cred->nt_session_key.length != 0) {
+ session_key = data_blob_dup_talloc(frame,
+ cred->nt_session_key);
+ if (session_key.data == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+ if (cred->lm_response.length != 0) {
+ lm_response = data_blob_dup_talloc(frame,
+ cred->lm_response);
+ if (lm_response.data == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+ if (cred->lm_session_key.length != 0) {
+ lm_session_key = data_blob_dup_talloc(frame,
+ cred->lm_session_key);
+ if (lm_session_key.data == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ if (cred->lm_response.data == NULL) {
+ *flags = *flags & ~CLI_CRED_LANMAN_AUTH;
+ }
+ goto done;
+ }
+
+ nt_hash = cli_credentials_get_nt_hash(cred, frame);
+
+ cli_credentials_get_ntlm_username_domain(cred, frame, &user, &domain);
+ if (user == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+ if (domain == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* If we are sending a username@realm login (see function
+ * above), then we will not send LM, it will not be
+ * accepted */
+ if (cred->principal_obtained > cred->username_obtained) {
+ *flags = *flags & ~CLI_CRED_LANMAN_AUTH;
+ }
+
+ /* Likewise if we are a machine account (avoid protocol downgrade attacks) */
+ if (cred->machine_account) {
+ *flags = *flags & ~CLI_CRED_LANMAN_AUTH;
+ }
+
+ if (!nt_hash) {
+ /* do nothing - blobs are zero length */
+
+ /* session key is all zeros */
+ session_key = data_blob_talloc_zero(frame, 16);
+ if (session_key.data == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+ lm_session_key = data_blob_talloc_zero(frame, 16);
+ if (lm_session_key.data == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* not doing NTLM2 without a password */
+ *flags &= ~CLI_CRED_NTLM2;
+ } else if (*flags & CLI_CRED_NTLMv2_AUTH) {
+
+ if (!target_info.length) {
+ /* be lazy, match win2k - we can't do NTLMv2 without it */
+ DEBUG(1, ("Server did not provide 'target information', required for NTLMv2\n"));
+ TALLOC_FREE(frame);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* TODO: if the remote server is standalone, then we should replace 'domain'
+ with the server name as supplied above */
+
+ if (!SMBNTLMv2encrypt_hash(frame,
+ user,
+ domain,
+ nt_hash->hash, &challenge,
+ server_timestamp, &target_info,
+ &lm_response, &nt_response,
+ NULL, &session_key)) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* LM Key is incompatible... */
+ *flags &= ~CLI_CRED_LANMAN_AUTH;
+ if (lm_response.length != 0) {
+ /*
+ * We should not expose the lm key.
+ */
+ memset(lm_response.data, 0, lm_response.length);
+ }
+ } else if (*flags & CLI_CRED_NTLM2) {
+ uint8_t session_nonce[16];
+ uint8_t session_nonce_hash[16];
+ uint8_t user_session_key[16];
+
+ lm_response = data_blob_talloc_zero(frame, 24);
+ if (lm_response.data == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+ generate_random_buffer(lm_response.data, 8);
+
+ memcpy(session_nonce, challenge.data, 8);
+ memcpy(&session_nonce[8], lm_response.data, 8);
+
+ rc = gnutls_hash_fast(GNUTLS_DIG_MD5,
+ session_nonce,
+ sizeof(session_nonce),
+ session_nonce_hash);
+ if (rc < 0) {
+ return gnutls_error_to_ntstatus(rc, NT_STATUS_NTLM_BLOCKED);
+ }
+
+ DEBUG(5, ("NTLMSSP challenge set by NTLM2\n"));
+ DEBUG(5, ("challenge is: \n"));
+ dump_data(5, session_nonce_hash, 8);
+
+ nt_response = data_blob_talloc_zero(frame, 24);
+ if (nt_response.data == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+ rc = SMBOWFencrypt(nt_hash->hash,
+ session_nonce_hash,
+ nt_response.data);
+ if (rc != 0) {
+ TALLOC_FREE(frame);
+ return gnutls_error_to_ntstatus(rc, NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER);
+ }
+
+ ZERO_ARRAY(session_nonce_hash);
+
+ session_key = data_blob_talloc_zero(frame, 16);
+ if (session_key.data == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ SMBsesskeygen_ntv1(nt_hash->hash, user_session_key);
+
+ rc = gnutls_hmac_fast(GNUTLS_MAC_MD5,
+ user_session_key,
+ sizeof(user_session_key),
+ session_nonce,
+ sizeof(session_nonce),
+ session_key.data);
+ if (rc < 0) {
+ return gnutls_error_to_ntstatus(rc, NT_STATUS_NTLM_BLOCKED);
+ }
+
+ ZERO_ARRAY(user_session_key);
+
+ dump_data_pw("NTLM2 session key:\n", session_key.data, session_key.length);
+
+ /* LM Key is incompatible... */
+ *flags &= ~CLI_CRED_LANMAN_AUTH;
+ } else {
+ const char *password = cli_credentials_get_password(cred);
+ uint8_t lm_hash[16];
+ bool do_lm = false;
+
+ nt_response = data_blob_talloc_zero(frame, 24);
+ if (nt_response.data == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+ rc = SMBOWFencrypt(nt_hash->hash, challenge.data,
+ nt_response.data);
+ if (rc != 0) {
+ TALLOC_FREE(frame);
+ return gnutls_error_to_ntstatus(rc, NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER);
+ }
+
+ session_key = data_blob_talloc_zero(frame, 16);
+ if (session_key.data == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+ SMBsesskeygen_ntv1(nt_hash->hash, session_key.data);
+ dump_data_pw("NT session key:\n", session_key.data, session_key.length);
+
+ /* lanman auth is insecure, it may be disabled.
+ We may also not have a password */
+
+ if (password != NULL) {
+ do_lm = E_deshash(password, lm_hash);
+ }
+
+ if (*flags & CLI_CRED_LANMAN_AUTH && do_lm) {
+ lm_response = data_blob_talloc_zero(frame, 24);
+ if (lm_response.data == NULL) {
+ ZERO_STRUCT(lm_hash);
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rc = SMBencrypt_hash(lm_hash,
+ challenge.data,
+ lm_response.data);
+ if (rc != 0) {
+ ZERO_STRUCT(lm_hash);
+ TALLOC_FREE(frame);
+ return gnutls_error_to_ntstatus(rc, NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER);
+ }
+ } else {
+ /* just copy the nt_response */
+ lm_response = data_blob_dup_talloc(frame, nt_response);
+ if (lm_response.data == NULL) {
+ ZERO_STRUCT(lm_hash);
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ if (do_lm) {
+ lm_session_key = data_blob_talloc_zero(frame, 16);
+ if (lm_session_key.data == NULL) {
+ ZERO_STRUCT(lm_hash);
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+ memcpy(lm_session_key.data, lm_hash, 8);
+
+ if (!(*flags & CLI_CRED_NTLM_AUTH)) {
+ memcpy(session_key.data, lm_session_key.data, 16);
+ }
+ ZERO_STRUCT(lm_hash);
+ }
+ }
+
+done:
+ if (_lm_response != NULL) {
+ talloc_steal(mem_ctx, lm_response.data);
+ *_lm_response = lm_response;
+ } else {
+ data_blob_clear(&lm_response);
+ }
+ if (_nt_response != NULL) {
+ talloc_steal(mem_ctx, nt_response.data);
+ *_nt_response = nt_response;
+ } else {
+ data_blob_clear(&nt_response);
+ }
+ if (_lm_session_key != NULL) {
+ talloc_steal(mem_ctx, lm_session_key.data);
+ *_lm_session_key = lm_session_key;
+ } else {
+ data_blob_clear(&lm_session_key);
+ }
+ if (_session_key != NULL) {
+ talloc_steal(mem_ctx, session_key.data);
+ *_session_key = session_key;
+ } else {
+ data_blob_clear(&session_key);
+ }
+ TALLOC_FREE(frame);
+ return NT_STATUS_OK;
+}
+
+/*
+ * Set a utf16 password on the credentials context, including an indication
+ * of 'how' the password was obtained
+ *
+ * This is required because the nt_hash is calculated over the raw utf16 blob,
+ * which might not be completely valid utf16, which means the conversion
+ * from CH_UTF16MUNGED to CH_UTF8 might loose information.
+ */
+_PUBLIC_ bool cli_credentials_set_utf16_password(struct cli_credentials *cred,
+ const DATA_BLOB *password_utf16,
+ enum credentials_obtained obtained)
+{
+ cred->password_will_be_nt_hash = false;
+
+ if (password_utf16 == NULL) {
+ return cli_credentials_set_password(cred, NULL, obtained);
+ }
+
+ if (obtained >= cred->password_obtained) {
+ struct samr_Password *nt_hash = NULL;
+ char *password_talloc = NULL;
+ size_t password_len = 0;
+ bool ok;
+
+ nt_hash = talloc(cred, struct samr_Password);
+ if (nt_hash == NULL) {
+ return false;
+ }
+
+ ok = convert_string_talloc(cred,
+ CH_UTF16MUNGED, CH_UTF8,
+ password_utf16->data,
+ password_utf16->length,
+ (void *)&password_talloc,
+ &password_len);
+ if (!ok) {
+ TALLOC_FREE(nt_hash);
+ return false;
+ }
+
+ ok = cli_credentials_set_password(cred, password_talloc, obtained);
+ TALLOC_FREE(password_talloc);
+ if (!ok) {
+ TALLOC_FREE(nt_hash);
+ return false;
+ }
+
+ mdfour(nt_hash->hash, password_utf16->data, password_utf16->length);
+ cred->nt_hash = nt_hash;
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * Set a old utf16 password on the credentials context.
+ *
+ * This is required because the nt_hash is calculated over the raw utf16 blob,
+ * which might not be completely valid utf16, which means the conversion
+ * from CH_UTF16MUNGED to CH_UTF8 might loose information.
+ */
+_PUBLIC_ bool cli_credentials_set_old_utf16_password(struct cli_credentials *cred,
+ const DATA_BLOB *password_utf16)
+{
+ struct samr_Password *nt_hash = NULL;
+ char *password_talloc = NULL;
+ size_t password_len = 0;
+ bool ok;
+
+ if (password_utf16 == NULL) {
+ return cli_credentials_set_old_password(cred, NULL, CRED_SPECIFIED);
+ }
+
+ nt_hash = talloc(cred, struct samr_Password);
+ if (nt_hash == NULL) {
+ return false;
+ }
+
+ ok = convert_string_talloc(cred,
+ CH_UTF16MUNGED, CH_UTF8,
+ password_utf16->data,
+ password_utf16->length,
+ (void *)&password_talloc,
+ &password_len);
+ if (!ok) {
+ TALLOC_FREE(nt_hash);
+ return false;
+ }
+
+ ok = cli_credentials_set_old_password(cred, password_talloc, CRED_SPECIFIED);
+ TALLOC_FREE(password_talloc);
+ if (!ok) {
+ TALLOC_FREE(nt_hash);
+ return false;
+ }
+
+ mdfour(nt_hash->hash, password_utf16->data, password_utf16->length);
+ cred->old_nt_hash = nt_hash;
+ return true;
+}
+
+_PUBLIC_ void cli_credentials_set_password_will_be_nt_hash(struct cli_credentials *cred,
+ bool val)
+{
+ /*
+ * We set this here and the next cli_credentials_set_password()
+ * that resets the password or password callback
+ * will pick this up.
+ *
+ * cli_credentials_set_nt_hash() and
+ * cli_credentials_set_utf16_password() will reset this
+ * to false.
+ */
+ cred->password_will_be_nt_hash = val;
+}
+
+_PUBLIC_ bool cli_credentials_set_nt_hash(struct cli_credentials *cred,
+ const struct samr_Password *nt_hash,
+ enum credentials_obtained obtained)
+{
+ cred->password_will_be_nt_hash = false;
+
+ if (obtained >= cred->password_obtained) {
+ cli_credentials_set_password(cred, NULL, obtained);
+ if (nt_hash) {
+ cred->nt_hash = talloc(cred, struct samr_Password);
+ if (cred->nt_hash == NULL) {
+ return false;
+ }
+ *cred->nt_hash = *nt_hash;
+ } else {
+ cred->nt_hash = NULL;
+ }
+ return true;
+ }
+
+ return false;
+}
+
+_PUBLIC_ bool cli_credentials_set_old_nt_hash(struct cli_credentials *cred,
+ const struct samr_Password *nt_hash)
+{
+ cli_credentials_set_old_password(cred, NULL, CRED_SPECIFIED);
+ if (nt_hash) {
+ cred->old_nt_hash = talloc(cred, struct samr_Password);
+ if (cred->old_nt_hash == NULL) {
+ return false;
+ }
+ *cred->old_nt_hash = *nt_hash;
+ } else {
+ cred->old_nt_hash = NULL;
+ }
+
+ return true;
+}
+
+_PUBLIC_ bool cli_credentials_set_ntlm_response(struct cli_credentials *cred,
+ const DATA_BLOB *lm_response,
+ const DATA_BLOB *lm_session_key,
+ const DATA_BLOB *nt_response,
+ const DATA_BLOB *nt_session_key,
+ enum credentials_obtained obtained)
+{
+ if (obtained >= cred->password_obtained) {
+ cli_credentials_set_password(cred, NULL, obtained);
+
+ data_blob_clear_free(&cred->lm_response);
+ data_blob_clear_free(&cred->lm_session_key);
+ data_blob_clear_free(&cred->nt_response);
+ data_blob_clear_free(&cred->nt_session_key);
+
+ if (lm_response != NULL && lm_response->length != 0) {
+ cred->lm_response = data_blob_talloc(cred,
+ lm_response->data,
+ lm_response->length);
+ if (cred->lm_response.data == NULL) {
+ return false;
+ }
+ }
+ if (lm_session_key != NULL && lm_session_key->length != 0) {
+ cred->lm_session_key = data_blob_talloc(cred,
+ lm_session_key->data,
+ lm_session_key->length);
+ if (cred->lm_session_key.data == NULL) {
+ return false;
+ }
+ }
+
+ if (nt_response != NULL && nt_response->length != 0) {
+ cred->nt_response = data_blob_talloc(cred,
+ nt_response->data,
+ nt_response->length);
+ if (cred->nt_response.data == NULL) {
+ return false;
+ }
+ }
+ if (nt_session_key != NULL && nt_session_key->length != 0) {
+ cred->nt_session_key = data_blob_talloc(cred,
+ nt_session_key->data,
+ nt_session_key->length);
+ if (cred->nt_session_key.data == NULL) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
diff --git a/auth/credentials/credentials_secrets.c b/auth/credentials/credentials_secrets.c
new file mode 100644
index 0000000..ab2c9dd
--- /dev/null
+++ b/auth/credentials/credentials_secrets.c
@@ -0,0 +1,485 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ User credentials handling (as regards on-disk files)
+
+ Copyright (C) Jelmer Vernooij 2005
+ Copyright (C) Tim Potter 2001
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
+
+ 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 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include <ldb.h>
+#include "librpc/gen_ndr/samr.h" /* for struct samrPassword */
+#include "param/secrets.h"
+#include "system/filesys.h"
+#include "auth/credentials/credentials.h"
+#include "auth/credentials/credentials_internal.h"
+#include "auth/credentials/credentials_krb5.h"
+#include "auth/kerberos/kerberos_util.h"
+#include "param/param.h"
+#include "lib/events/events.h"
+#include "dsdb/samdb/samdb.h"
+#include "source3/include/secrets.h"
+#include "dbwrap/dbwrap.h"
+#include "dbwrap/dbwrap_open.h"
+#include "lib/util/util_tdb.h"
+#include "libds/common/roles.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+/**
+ * Fill in credentials for the machine trust account, from the secrets database.
+ *
+ * @param cred Credentials structure to fill in
+ * @retval NTSTATUS error detailing any failure
+ */
+static NTSTATUS cli_credentials_set_secrets_lct(struct cli_credentials *cred,
+ struct loadparm_context *lp_ctx,
+ struct ldb_context *ldb,
+ const char *base,
+ const char *filter,
+ time_t secrets_tdb_last_change_time,
+ const char *secrets_tdb_password,
+ char **error_string)
+{
+ TALLOC_CTX *mem_ctx;
+
+ int ldb_ret;
+ struct ldb_message *msg;
+
+ const char *machine_account;
+ const char *password;
+ const char *domain;
+ const char *realm;
+ enum netr_SchannelType sct;
+ const char *salt_principal;
+ char *keytab;
+ const struct ldb_val *whenChanged;
+ time_t lct;
+
+ /* ok, we are going to get it now, don't recurse back here */
+ cred->machine_account_pending = false;
+
+ /* some other parts of the system will key off this */
+ cred->machine_account = true;
+
+ mem_ctx = talloc_named(cred, 0, "cli_credentials_set_secrets from ldb");
+
+ if (!ldb) {
+ /* Local secrets are stored in secrets.ldb */
+ ldb = secrets_db_connect(mem_ctx, lp_ctx);
+ if (!ldb) {
+ *error_string = talloc_strdup(cred, "Could not open secrets.ldb");
+ talloc_free(mem_ctx);
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ }
+ }
+
+ ldb_ret = dsdb_search_one(ldb, mem_ctx, &msg,
+ ldb_dn_new(mem_ctx, ldb, base),
+ LDB_SCOPE_SUBTREE,
+ NULL, 0, "%s", filter);
+
+ if (ldb_ret != LDB_SUCCESS) {
+ *error_string = talloc_asprintf(cred, "Could not find entry to match filter: '%s' base: '%s': %s: %s",
+ filter, base ? base : "",
+ ldb_strerror(ldb_ret), ldb_errstring(ldb));
+ talloc_free(mem_ctx);
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ }
+
+ password = ldb_msg_find_attr_as_string(msg, "secret", NULL);
+
+ whenChanged = ldb_msg_find_ldb_val(msg, "whenChanged");
+ if (!whenChanged || ldb_val_to_time(whenChanged, &lct) != LDB_SUCCESS) {
+ /* This attribute is mandatory */
+ talloc_free(mem_ctx);
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ /* Don't set secrets.ldb info if the secrets.tdb entry was more recent */
+ if (lct < secrets_tdb_last_change_time) {
+ talloc_free(mem_ctx);
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ if ((lct == secrets_tdb_last_change_time) &&
+ (secrets_tdb_password != NULL) &&
+ (password != NULL) &&
+ (strcmp(password, secrets_tdb_password) != 0)) {
+ talloc_free(mem_ctx);
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ cli_credentials_set_password_last_changed_time(cred, lct);
+
+ machine_account = ldb_msg_find_attr_as_string(msg, "samAccountName", NULL);
+
+ if (!machine_account) {
+ machine_account = ldb_msg_find_attr_as_string(msg, "servicePrincipalName", NULL);
+
+ if (!machine_account) {
+ const char *ldap_bind_dn = ldb_msg_find_attr_as_string(msg, "ldapBindDn", NULL);
+ if (!ldap_bind_dn) {
+ *error_string = talloc_asprintf(cred,
+ "Could not find 'samAccountName', "
+ "'servicePrincipalName' or "
+ "'ldapBindDn' in secrets record: %s",
+ ldb_dn_get_linearized(msg->dn));
+ talloc_free(mem_ctx);
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ } else {
+ /* store bind dn in credentials */
+ cli_credentials_set_bind_dn(cred, ldap_bind_dn);
+ }
+ }
+ }
+
+ salt_principal = ldb_msg_find_attr_as_string(msg, "saltPrincipal", NULL);
+ cli_credentials_set_salt_principal(cred, salt_principal);
+
+ sct = ldb_msg_find_attr_as_int(msg, "secureChannelType", 0);
+ if (sct) {
+ cli_credentials_set_secure_channel_type(cred, sct);
+ }
+
+ if (!password) {
+ const struct ldb_val *nt_password_hash = ldb_msg_find_ldb_val(msg, "unicodePwd");
+ struct samr_Password hash;
+ ZERO_STRUCT(hash);
+ if (nt_password_hash) {
+ memcpy(hash.hash, nt_password_hash->data,
+ MIN(nt_password_hash->length, sizeof(hash.hash)));
+
+ cli_credentials_set_nt_hash(cred, &hash, CRED_SPECIFIED);
+ } else {
+ cli_credentials_set_password(cred, NULL, CRED_SPECIFIED);
+ }
+ } else {
+ cli_credentials_set_password(cred, password, CRED_SPECIFIED);
+ }
+
+ domain = ldb_msg_find_attr_as_string(msg, "flatname", NULL);
+ if (domain) {
+ cli_credentials_set_domain(cred, domain, CRED_SPECIFIED);
+ }
+
+ realm = ldb_msg_find_attr_as_string(msg, "realm", NULL);
+ if (realm) {
+ cli_credentials_set_realm(cred, realm, CRED_SPECIFIED);
+ }
+
+ if (machine_account) {
+ cli_credentials_set_username(cred, machine_account, CRED_SPECIFIED);
+ }
+
+ cli_credentials_set_kvno(cred, ldb_msg_find_attr_as_int(msg, "msDS-KeyVersionNumber", 0));
+
+ /* If there was an external keytab specified by reference in
+ * the LDB, then use this. Otherwise we will make one up
+ * (chewing CPU time) from the password */
+ keytab = keytab_name_from_msg(cred, ldb, msg);
+ if (keytab) {
+ cli_credentials_set_keytab_name(cred, lp_ctx, keytab, CRED_SPECIFIED);
+ talloc_free(keytab);
+ }
+ talloc_free(mem_ctx);
+
+ return NT_STATUS_OK;
+}
+
+
+/**
+ * Fill in credentials for the machine trust account, from the secrets database.
+ *
+ * @param cred Credentials structure to fill in
+ * @retval NTSTATUS error detailing any failure
+ */
+_PUBLIC_ NTSTATUS cli_credentials_set_secrets(struct cli_credentials *cred,
+ struct loadparm_context *lp_ctx,
+ struct ldb_context *ldb,
+ const char *base,
+ const char *filter,
+ char **error_string)
+{
+ NTSTATUS status = cli_credentials_set_secrets_lct(cred, lp_ctx, ldb, base, filter, 0, NULL, error_string);
+ if (!NT_STATUS_IS_OK(status)) {
+ /* set anonymous as the fallback, if the machine account won't work */
+ cli_credentials_set_anonymous(cred);
+ }
+ return status;
+}
+
+/**
+ * Fill in credentials for the machine trust account, from the secrets database.
+ *
+ * @param cred Credentials structure to fill in
+ * @retval NTSTATUS error detailing any failure
+ */
+_PUBLIC_ NTSTATUS cli_credentials_set_machine_account(struct cli_credentials *cred,
+ struct loadparm_context *lp_ctx)
+{
+ struct db_context *db_ctx;
+ char *secrets_tdb_path;
+ int hash_size, tdb_flags;
+
+ secrets_tdb_path = lpcfg_private_db_path(cred, lp_ctx, "secrets");
+ if (secrets_tdb_path == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ hash_size = lpcfg_tdb_hash_size(lp_ctx, secrets_tdb_path);
+ tdb_flags = lpcfg_tdb_flags(lp_ctx, TDB_DEFAULT);
+
+ db_ctx = dbwrap_local_open(
+ cred,
+ secrets_tdb_path,
+ hash_size,
+ tdb_flags,
+ O_RDWR,
+ 0600,
+ DBWRAP_LOCK_ORDER_1,
+ DBWRAP_FLAG_NONE);
+ TALLOC_FREE(secrets_tdb_path);
+
+ /*
+ * We do not check for errors here, we might not have a
+ * secrets.tdb at all, and so we just need to check the
+ * secrets.ldb
+ */
+ return cli_credentials_set_machine_account_db_ctx(cred, lp_ctx, db_ctx);
+}
+
+/**
+ * Fill in credentials for the machine trust account, from the
+ * secrets.ldb or passed in handle to secrets.tdb (perhaps in CTDB).
+ *
+ * This version is used in parts of the code that can link in the
+ * CTDB dbwrap backend, by passing down the already open handle.
+ *
+ * @param cred Credentials structure to fill in
+ * @param db_ctx dbwrap context for secrets.tdb
+ * @retval NTSTATUS error detailing any failure
+ */
+_PUBLIC_ NTSTATUS cli_credentials_set_machine_account_db_ctx(struct cli_credentials *cred,
+ struct loadparm_context *lp_ctx,
+ struct db_context *db_ctx)
+{
+ NTSTATUS status;
+ char *filter;
+ char *error_string = NULL;
+ const char *domain;
+ bool secrets_tdb_password_more_recent;
+ time_t secrets_tdb_lct = 0;
+ char *secrets_tdb_password = NULL;
+ char *secrets_tdb_old_password = NULL;
+ uint32_t secrets_tdb_secure_channel_type = SEC_CHAN_NULL;
+ int server_role = lpcfg_server_role(lp_ctx);
+ int security = lpcfg_security(lp_ctx);
+ char *keystr;
+ char *keystr_upper = NULL;
+ TALLOC_CTX *tmp_ctx = talloc_named(cred, 0, "cli_credentials_set_secrets from ldb");
+ if (!tmp_ctx) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Bleh, nasty recursion issues: We are setting a machine
+ * account here, so we don't want the 'pending' flag around
+ * any more */
+ cred->machine_account_pending = false;
+
+ /* We have to do this, as the fallback in
+ * cli_credentials_set_secrets is to run as anonymous, so the domain is wiped */
+ domain = cli_credentials_get_domain(cred);
+
+ if (db_ctx) {
+ TDB_DATA dbuf;
+ keystr = talloc_asprintf(tmp_ctx, "%s/%s",
+ SECRETS_MACHINE_LAST_CHANGE_TIME,
+ domain);
+ keystr_upper = strupper_talloc(tmp_ctx, keystr);
+ status = dbwrap_fetch(db_ctx, tmp_ctx, string_tdb_data(keystr_upper),
+ &dbuf);
+ if (NT_STATUS_IS_OK(status) && dbuf.dsize == 4) {
+ secrets_tdb_lct = IVAL(dbuf.dptr,0);
+ }
+
+ keystr = talloc_asprintf(tmp_ctx, "%s/%s",
+ SECRETS_MACHINE_PASSWORD,
+ domain);
+ keystr_upper = strupper_talloc(tmp_ctx, keystr);
+ status = dbwrap_fetch(db_ctx, tmp_ctx, string_tdb_data(keystr_upper),
+ &dbuf);
+ if (NT_STATUS_IS_OK(status)) {
+ secrets_tdb_password = (char *)dbuf.dptr;
+ }
+
+ keystr = talloc_asprintf(tmp_ctx, "%s/%s",
+ SECRETS_MACHINE_PASSWORD_PREV,
+ domain);
+ keystr_upper = strupper_talloc(tmp_ctx, keystr);
+ status = dbwrap_fetch(db_ctx, tmp_ctx, string_tdb_data(keystr_upper),
+ &dbuf);
+ if (NT_STATUS_IS_OK(status)) {
+ secrets_tdb_old_password = (char *)dbuf.dptr;
+ }
+
+ keystr = talloc_asprintf(tmp_ctx, "%s/%s",
+ SECRETS_MACHINE_SEC_CHANNEL_TYPE,
+ domain);
+ keystr_upper = strupper_talloc(tmp_ctx, keystr);
+ status = dbwrap_fetch(db_ctx, tmp_ctx, string_tdb_data(keystr_upper),
+ &dbuf);
+ if (NT_STATUS_IS_OK(status) && dbuf.dsize == 4) {
+ secrets_tdb_secure_channel_type = IVAL(dbuf.dptr,0);
+ }
+ }
+
+ filter = talloc_asprintf(cred, SECRETS_PRIMARY_DOMAIN_FILTER,
+ domain);
+ status = cli_credentials_set_secrets_lct(cred, lp_ctx, NULL,
+ SECRETS_PRIMARY_DOMAIN_DN,
+ filter, secrets_tdb_lct, secrets_tdb_password, &error_string);
+ if (secrets_tdb_password == NULL) {
+ secrets_tdb_password_more_recent = false;
+ } else if (NT_STATUS_EQUAL(NT_STATUS_CANT_ACCESS_DOMAIN_INFO, status)
+ || NT_STATUS_EQUAL(NT_STATUS_NOT_FOUND, status)) {
+ secrets_tdb_password_more_recent = true;
+ } else if (secrets_tdb_lct > cli_credentials_get_password_last_changed_time(cred)) {
+ secrets_tdb_password_more_recent = true;
+ } else if (secrets_tdb_lct == cli_credentials_get_password_last_changed_time(cred)) {
+ secrets_tdb_password_more_recent = strcmp(secrets_tdb_password, cli_credentials_get_password(cred)) != 0;
+ } else {
+ secrets_tdb_password_more_recent = false;
+ }
+
+ if (secrets_tdb_password_more_recent) {
+ enum credentials_use_kerberos use_kerberos =
+ CRED_USE_KERBEROS_DISABLED;
+ char *machine_account = talloc_asprintf(tmp_ctx, "%s$", lpcfg_netbios_name(lp_ctx));
+ cli_credentials_set_password(cred, secrets_tdb_password, CRED_SPECIFIED);
+ cli_credentials_set_old_password(cred, secrets_tdb_old_password, CRED_SPECIFIED);
+ cli_credentials_set_domain(cred, domain, CRED_SPECIFIED);
+ if (strequal(domain, lpcfg_workgroup(lp_ctx))) {
+ cli_credentials_set_realm(cred, lpcfg_realm(lp_ctx), CRED_SPECIFIED);
+
+ switch (server_role) {
+ case ROLE_DOMAIN_MEMBER:
+ if (security != SEC_ADS) {
+ break;
+ }
+
+ FALL_THROUGH;
+ case ROLE_ACTIVE_DIRECTORY_DC:
+ use_kerberos = CRED_USE_KERBEROS_DESIRED;
+ break;
+ }
+ }
+ cli_credentials_set_kerberos_state(cred,
+ use_kerberos,
+ CRED_SPECIFIED);
+ cli_credentials_set_username(cred, machine_account, CRED_SPECIFIED);
+ cli_credentials_set_password_last_changed_time(cred, secrets_tdb_lct);
+ cli_credentials_set_secure_channel_type(cred, secrets_tdb_secure_channel_type);
+ status = NT_STATUS_OK;
+ } else if (!NT_STATUS_IS_OK(status)) {
+ if (db_ctx) {
+ error_string
+ = talloc_asprintf(cred,
+ "Failed to fetch machine account password for %s from both "
+ "secrets.ldb (%s) and from %s",
+ domain,
+ error_string == NULL ? "error" : error_string,
+ dbwrap_name(db_ctx));
+ } else {
+ char *secrets_tdb_path;
+
+ secrets_tdb_path = lpcfg_private_db_path(tmp_ctx,
+ lp_ctx,
+ "secrets");
+ if (secrets_tdb_path == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ error_string = talloc_asprintf(cred,
+ "Failed to fetch machine account password from "
+ "secrets.ldb: %s and failed to open %s",
+ error_string == NULL ? "error" : error_string,
+ secrets_tdb_path);
+ }
+ DEBUG(1, ("Could not find machine account in secrets database: %s: %s\n",
+ error_string == NULL ? "error" : error_string,
+ nt_errstr(status)));
+ /* set anonymous as the fallback, if the machine account won't work */
+ cli_credentials_set_anonymous(cred);
+ }
+
+ TALLOC_FREE(tmp_ctx);
+ return status;
+}
+
+/**
+ * Fill in credentials for a particular principal, from the secrets database.
+ *
+ * @param cred Credentials structure to fill in
+ * @retval NTSTATUS error detailing any failure
+ */
+_PUBLIC_ NTSTATUS cli_credentials_set_stored_principal(struct cli_credentials *cred,
+ struct loadparm_context *lp_ctx,
+ const char *serviceprincipal)
+{
+ NTSTATUS status;
+ char *filter;
+ char *error_string = NULL;
+ /* Bleh, nasty recursion issues: We are setting a machine
+ * account here, so we don't want the 'pending' flag around
+ * any more */
+ cred->machine_account_pending = false;
+ filter = talloc_asprintf(cred, SECRETS_PRINCIPAL_SEARCH,
+ cli_credentials_get_realm(cred),
+ cli_credentials_get_domain(cred),
+ serviceprincipal);
+ status = cli_credentials_set_secrets_lct(cred, lp_ctx, NULL,
+ SECRETS_PRINCIPALS_DN, filter,
+ 0, NULL, &error_string);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Could not find %s principal in secrets database: %s: %s\n",
+ serviceprincipal, nt_errstr(status),
+ error_string ? error_string : "<no error>"));
+ }
+ return status;
+}
+
+/**
+ * Ask that when required, the credentials system will be filled with
+ * machine trust account, from the secrets database.
+ *
+ * @param cred Credentials structure to fill in
+ * @note This function is used to call the above function after, rather
+ * than during, popt processing.
+ *
+ */
+_PUBLIC_ void cli_credentials_set_machine_account_pending(struct cli_credentials *cred,
+ struct loadparm_context *lp_ctx)
+{
+ cred->machine_account_pending = true;
+ cred->machine_account_pending_lp_ctx = lp_ctx;
+}
+
+
diff --git a/auth/credentials/pycredentials.c b/auth/credentials/pycredentials.c
new file mode 100644
index 0000000..013d295
--- /dev/null
+++ b/auth/credentials/pycredentials.c
@@ -0,0 +1,1626 @@
+/*
+ Unix SMB/CIFS implementation.
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007
+
+ 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 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <Python.h>
+#include "python/py3compat.h"
+#include "includes.h"
+#include "python/modules.h"
+#include "pycredentials.h"
+#include "param/param.h"
+#include "auth/credentials/credentials_internal.h"
+#include "librpc/gen_ndr/samr.h" /* for struct samr_Password */
+#include "librpc/gen_ndr/netlogon.h"
+#include "libcli/util/pyerrors.h"
+#include "libcli/auth/libcli_auth.h"
+#include "param/pyparam.h"
+#include <tevent.h>
+#include "libcli/auth/libcli_auth.h"
+#include "system/kerberos.h"
+#include "auth/kerberos/kerberos.h"
+#include "libcli/smb/smb_constants.h"
+
+void initcredentials(void);
+
+static PyObject *py_creds_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+{
+ return pytalloc_steal(type, cli_credentials_init(NULL));
+}
+
+static PyObject *py_creds_get_username(PyObject *self, PyObject *unused)
+{
+ struct cli_credentials *creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_Format(PyExc_TypeError, "Credentials expected");
+ return NULL;
+ }
+ return PyString_FromStringOrNULL(cli_credentials_get_username(creds));
+}
+
+static PyObject *py_creds_set_username(PyObject *self, PyObject *args)
+{
+ char *newval;
+ enum credentials_obtained obt = CRED_SPECIFIED;
+ int _obt = obt;
+ struct cli_credentials *creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_Format(PyExc_TypeError, "Credentials expected");
+ return NULL;
+ }
+
+ if (!PyArg_ParseTuple(args, "s|i", &newval, &_obt)) {
+ return NULL;
+ }
+ obt = _obt;
+
+ return PyBool_FromLong(cli_credentials_set_username(creds, newval, obt));
+}
+
+static PyObject *py_creds_get_ntlm_username_domain(PyObject *self, PyObject *unused)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ const char *user = NULL;
+ const char *domain = NULL;
+ PyObject *ret = NULL;
+ struct cli_credentials *creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_Format(PyExc_TypeError, "Credentials expected");
+ return NULL;
+ }
+ cli_credentials_get_ntlm_username_domain(creds,
+ frame, &user, &domain);
+ ret = Py_BuildValue("(ss)",
+ user,
+ domain);
+
+ TALLOC_FREE(frame);
+ return ret;
+}
+
+static PyObject *py_creds_get_ntlm_response(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ PyObject *ret = NULL;
+ int flags;
+ struct timeval tv_now;
+ NTTIME server_timestamp;
+ DATA_BLOB challenge = data_blob_null;
+ DATA_BLOB target_info = data_blob_null;
+ NTSTATUS status;
+ DATA_BLOB lm_response = data_blob_null;
+ DATA_BLOB nt_response = data_blob_null;
+ DATA_BLOB lm_session_key = data_blob_null;
+ DATA_BLOB nt_session_key = data_blob_null;
+ const char *kwnames[] = { "flags", "challenge",
+ "target_info",
+ NULL };
+ struct cli_credentials *creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_Format(PyExc_TypeError, "Credentials expected");
+ return NULL;
+ }
+
+ tv_now = timeval_current();
+ server_timestamp = timeval_to_nttime(&tv_now);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "is#|s#",
+ discard_const_p(char *, kwnames),
+ &flags,
+ &challenge.data,
+ &challenge.length,
+ &target_info.data,
+ &target_info.length)) {
+ return NULL;
+ }
+
+ status = cli_credentials_get_ntlm_response(creds,
+ frame, &flags,
+ challenge,
+ &server_timestamp,
+ target_info,
+ &lm_response, &nt_response,
+ &lm_session_key, &nt_session_key);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ PyErr_SetNTSTATUS(status);
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+
+ ret = Py_BuildValue("{sis" PYARG_BYTES_LEN "s" PYARG_BYTES_LEN
+ "s" PYARG_BYTES_LEN "s" PYARG_BYTES_LEN "}",
+ "flags", flags,
+ "lm_response",
+ (const char *)lm_response.data, lm_response.length,
+ "nt_response",
+ (const char *)nt_response.data, nt_response.length,
+ "lm_session_key",
+ (const char *)lm_session_key.data, lm_session_key.length,
+ "nt_session_key",
+ (const char *)nt_session_key.data, nt_session_key.length);
+ TALLOC_FREE(frame);
+ return ret;
+}
+
+static PyObject *py_creds_get_principal(PyObject *self, PyObject *unused)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ PyObject *ret = NULL;
+ struct cli_credentials *creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_Format(PyExc_TypeError, "Credentials expected");
+ return NULL;
+ }
+ ret = PyString_FromStringOrNULL(cli_credentials_get_principal(creds, frame));
+ TALLOC_FREE(frame);
+ return ret;
+}
+
+static PyObject *py_creds_set_principal(PyObject *self, PyObject *args)
+{
+ char *newval;
+ enum credentials_obtained obt = CRED_SPECIFIED;
+ int _obt = obt;
+ struct cli_credentials *creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_Format(PyExc_TypeError, "Credentials expected");
+ return NULL;
+ }
+
+ if (!PyArg_ParseTuple(args, "s|i", &newval, &_obt)) {
+ return NULL;
+ }
+ obt = _obt;
+
+ return PyBool_FromLong(cli_credentials_set_principal(creds, newval, obt));
+}
+
+static PyObject *py_creds_get_password(PyObject *self, PyObject *unused)
+{
+ struct cli_credentials *creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_Format(PyExc_TypeError, "Credentials expected");
+ return NULL;
+ }
+ return PyString_FromStringOrNULL(cli_credentials_get_password(creds));
+}
+
+static PyObject *py_creds_set_password(PyObject *self, PyObject *args)
+{
+ const char *newval = NULL;
+ enum credentials_obtained obt = CRED_SPECIFIED;
+ int _obt = obt;
+ PyObject *result = NULL;
+ struct cli_credentials *creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_Format(PyExc_TypeError, "Credentials expected");
+ return NULL;
+ }
+
+ if (!PyArg_ParseTuple(args, PYARG_STR_UNI"|i", "utf8", &newval, &_obt)) {
+ return NULL;
+ }
+ obt = _obt;
+
+ result = PyBool_FromLong(cli_credentials_set_password(creds, newval, obt));
+ PyMem_Free(discard_const_p(void*, newval));
+ return result;
+}
+
+static PyObject *py_creds_set_utf16_password(PyObject *self, PyObject *args)
+{
+ enum credentials_obtained obt = CRED_SPECIFIED;
+ int _obt = obt;
+ PyObject *newval = NULL;
+ DATA_BLOB blob = data_blob_null;
+ Py_ssize_t size = 0;
+ int result;
+ bool ok;
+ struct cli_credentials *creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_Format(PyExc_TypeError, "Credentials expected");
+ return NULL;
+ }
+
+ if (!PyArg_ParseTuple(args, "O|i", &newval, &_obt)) {
+ return NULL;
+ }
+ obt = _obt;
+
+ result = PyBytes_AsStringAndSize(newval, (char **)&blob.data, &size);
+ if (result != 0) {
+ PyErr_SetString(PyExc_RuntimeError, "Failed to convert passed value to Bytes");
+ return NULL;
+ }
+ blob.length = size;
+
+ ok = cli_credentials_set_utf16_password(creds,
+ &blob, obt);
+
+ return PyBool_FromLong(ok);
+}
+
+static PyObject *py_creds_get_old_password(PyObject *self, PyObject *unused)
+{
+ struct cli_credentials *creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_Format(PyExc_TypeError, "Credentials expected");
+ return NULL;
+ }
+ return PyString_FromStringOrNULL(cli_credentials_get_old_password(creds));
+}
+
+static PyObject *py_creds_set_old_password(PyObject *self, PyObject *args)
+{
+ char *oldval;
+ enum credentials_obtained obt = CRED_SPECIFIED;
+ int _obt = obt;
+ struct cli_credentials *creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_Format(PyExc_TypeError, "Credentials expected");
+ return NULL;
+ }
+
+ if (!PyArg_ParseTuple(args, "s|i", &oldval, &_obt)) {
+ return NULL;
+ }
+ obt = _obt;
+
+ return PyBool_FromLong(cli_credentials_set_old_password(creds, oldval, obt));
+}
+
+static PyObject *py_creds_set_old_utf16_password(PyObject *self, PyObject *args)
+{
+ PyObject *oldval = NULL;
+ DATA_BLOB blob = data_blob_null;
+ Py_ssize_t size = 0;
+ int result;
+ bool ok;
+ struct cli_credentials *creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_Format(PyExc_TypeError, "Credentials expected");
+ return NULL;
+ }
+
+ if (!PyArg_ParseTuple(args, "O", &oldval)) {
+ return NULL;
+ }
+
+ result = PyBytes_AsStringAndSize(oldval, (char **)&blob.data, &size);
+ if (result != 0) {
+ PyErr_SetString(PyExc_RuntimeError, "Failed to convert passed value to Bytes");
+ return NULL;
+ }
+ blob.length = size;
+
+ ok = cli_credentials_set_old_utf16_password(creds,
+ &blob);
+
+ return PyBool_FromLong(ok);
+}
+
+static PyObject *py_creds_get_domain(PyObject *self, PyObject *unused)
+{
+ struct cli_credentials *creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_Format(PyExc_TypeError, "Credentials expected");
+ return NULL;
+ }
+ return PyString_FromStringOrNULL(cli_credentials_get_domain(creds));
+}
+
+static PyObject *py_creds_set_domain(PyObject *self, PyObject *args)
+{
+ char *newval;
+ enum credentials_obtained obt = CRED_SPECIFIED;
+ int _obt = obt;
+ struct cli_credentials *creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_Format(PyExc_TypeError, "Credentials expected");
+ return NULL;
+ }
+
+ if (!PyArg_ParseTuple(args, "s|i", &newval, &_obt)) {
+ return NULL;
+ }
+ obt = _obt;
+
+ return PyBool_FromLong(cli_credentials_set_domain(creds, newval, obt));
+}
+
+static PyObject *py_creds_get_realm(PyObject *self, PyObject *unused)
+{
+ struct cli_credentials *creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_Format(PyExc_TypeError, "Credentials expected");
+ return NULL;
+ }
+ return PyString_FromStringOrNULL(cli_credentials_get_realm(creds));
+}
+
+static PyObject *py_creds_set_realm(PyObject *self, PyObject *args)
+{
+ char *newval;
+ enum credentials_obtained obt = CRED_SPECIFIED;
+ int _obt = obt;
+ struct cli_credentials *creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_Format(PyExc_TypeError, "Credentials expected");
+ return NULL;
+ }
+
+ if (!PyArg_ParseTuple(args, "s|i", &newval, &_obt)) {
+ return NULL;
+ }
+ obt = _obt;
+
+ return PyBool_FromLong(cli_credentials_set_realm(creds, newval, obt));
+}
+
+static PyObject *py_creds_get_bind_dn(PyObject *self, PyObject *unused)
+{
+ struct cli_credentials *creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_Format(PyExc_TypeError, "Credentials expected");
+ return NULL;
+ }
+ return PyString_FromStringOrNULL(cli_credentials_get_bind_dn(creds));
+}
+
+static PyObject *py_creds_set_bind_dn(PyObject *self, PyObject *args)
+{
+ char *newval;
+ struct cli_credentials *creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_Format(PyExc_TypeError, "Credentials expected");
+ return NULL;
+ }
+ if (!PyArg_ParseTuple(args, "s", &newval))
+ return NULL;
+
+ return PyBool_FromLong(cli_credentials_set_bind_dn(creds, newval));
+}
+
+static PyObject *py_creds_get_workstation(PyObject *self, PyObject *unused)
+{
+ struct cli_credentials *creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_Format(PyExc_TypeError, "Credentials expected");
+ return NULL;
+ }
+ return PyString_FromStringOrNULL(cli_credentials_get_workstation(creds));
+}
+
+static PyObject *py_creds_set_workstation(PyObject *self, PyObject *args)
+{
+ char *newval;
+ enum credentials_obtained obt = CRED_SPECIFIED;
+ int _obt = obt;
+ struct cli_credentials *creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_Format(PyExc_TypeError, "Credentials expected");
+ return NULL;
+ }
+
+ if (!PyArg_ParseTuple(args, "s|i", &newval, &_obt)) {
+ return NULL;
+ }
+ obt = _obt;
+
+ return PyBool_FromLong(cli_credentials_set_workstation(creds, newval, obt));
+}
+
+static PyObject *py_creds_is_anonymous(PyObject *self, PyObject *unused)
+{
+ struct cli_credentials *creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_Format(PyExc_TypeError, "Credentials expected");
+ return NULL;
+ }
+ return PyBool_FromLong(cli_credentials_is_anonymous(creds));
+}
+
+static PyObject *py_creds_set_anonymous(PyObject *self, PyObject *unused)
+{
+ struct cli_credentials *creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_Format(PyExc_TypeError, "Credentials expected");
+ return NULL;
+ }
+ cli_credentials_set_anonymous(creds);
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_creds_authentication_requested(PyObject *self, PyObject *unused)
+{
+ struct cli_credentials *creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_Format(PyExc_TypeError, "Credentials expected");
+ return NULL;
+ }
+ return PyBool_FromLong(cli_credentials_authentication_requested(creds));
+}
+
+static PyObject *py_creds_wrong_password(PyObject *self, PyObject *unused)
+{
+ struct cli_credentials *creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_Format(PyExc_TypeError, "Credentials expected");
+ return NULL;
+ }
+ return PyBool_FromLong(cli_credentials_wrong_password(creds));
+}
+
+static PyObject *py_creds_set_cmdline_callbacks(PyObject *self, PyObject *unused)
+{
+ struct cli_credentials *creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_Format(PyExc_TypeError, "Credentials expected");
+ return NULL;
+ }
+ return PyBool_FromLong(cli_credentials_set_cmdline_callbacks(creds));
+}
+
+static PyObject *py_creds_parse_string(PyObject *self, PyObject *args)
+{
+ char *newval;
+ enum credentials_obtained obt = CRED_SPECIFIED;
+ int _obt = obt;
+ struct cli_credentials *creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_Format(PyExc_TypeError, "Credentials expected");
+ return NULL;
+ }
+
+ if (!PyArg_ParseTuple(args, "s|i", &newval, &_obt)) {
+ return NULL;
+ }
+ obt = _obt;
+
+ cli_credentials_parse_string(creds, newval, obt);
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_creds_parse_file(PyObject *self, PyObject *args)
+{
+ char *newval;
+ enum credentials_obtained obt = CRED_SPECIFIED;
+ int _obt = obt;
+ struct cli_credentials *creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_Format(PyExc_TypeError, "Credentials expected");
+ return NULL;
+ }
+
+ if (!PyArg_ParseTuple(args, "s|i", &newval, &_obt)) {
+ return NULL;
+ }
+ obt = _obt;
+
+ cli_credentials_parse_file(creds, newval, obt);
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_cli_credentials_set_password_will_be_nt_hash(PyObject *self, PyObject *args)
+{
+ struct cli_credentials *creds = PyCredentials_AsCliCredentials(self);
+ PyObject *py_val = NULL;
+ bool val = false;
+
+ if (!PyArg_ParseTuple(args, "O!", &PyBool_Type, &py_val)) {
+ return NULL;
+ }
+ val = PyObject_IsTrue(py_val);
+
+ cli_credentials_set_password_will_be_nt_hash(creds, val);
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_creds_get_nt_hash(PyObject *self, PyObject *unused)
+{
+ PyObject *ret;
+ struct samr_Password *ntpw = NULL;
+ struct cli_credentials *creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_Format(PyExc_TypeError, "Credentials expected");
+ return NULL;
+ }
+ ntpw = cli_credentials_get_nt_hash(creds, creds);
+
+ ret = PyBytes_FromStringAndSize(discard_const_p(char, ntpw->hash), 16);
+ TALLOC_FREE(ntpw);
+ return ret;
+}
+
+static PyObject *py_creds_get_kerberos_state(PyObject *self, PyObject *unused)
+{
+ int state;
+ struct cli_credentials *creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_Format(PyExc_TypeError, "Credentials expected");
+ return NULL;
+ }
+ state = cli_credentials_get_kerberos_state(creds);
+ return PyLong_FromLong(state);
+}
+
+static PyObject *py_creds_set_kerberos_state(PyObject *self, PyObject *args)
+{
+ int state;
+ struct cli_credentials *creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_Format(PyExc_TypeError, "Credentials expected");
+ return NULL;
+ }
+ if (!PyArg_ParseTuple(args, "i", &state))
+ return NULL;
+
+ cli_credentials_set_kerberos_state(creds, state, CRED_SPECIFIED);
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_creds_set_krb_forwardable(PyObject *self, PyObject *args)
+{
+ int state;
+ struct cli_credentials *creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_Format(PyExc_TypeError, "Credentials expected");
+ return NULL;
+ }
+ if (!PyArg_ParseTuple(args, "i", &state))
+ return NULL;
+
+ cli_credentials_set_krb_forwardable(creds, state);
+ Py_RETURN_NONE;
+}
+
+
+static PyObject *py_creds_get_forced_sasl_mech(PyObject *self, PyObject *unused)
+{
+ struct cli_credentials *creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_Format(PyExc_TypeError, "Credentials expected");
+ return NULL;
+ }
+ return PyString_FromStringOrNULL(cli_credentials_get_forced_sasl_mech(creds));
+}
+
+static PyObject *py_creds_set_forced_sasl_mech(PyObject *self, PyObject *args)
+{
+ char *newval;
+ struct cli_credentials *creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_Format(PyExc_TypeError, "Credentials expected");
+ return NULL;
+ }
+
+ if (!PyArg_ParseTuple(args, "s", &newval)) {
+ return NULL;
+ }
+
+ cli_credentials_set_forced_sasl_mech(creds, newval);
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_creds_set_conf(PyObject *self, PyObject *args)
+{
+ PyObject *py_lp_ctx = Py_None;
+ struct loadparm_context *lp_ctx;
+ TALLOC_CTX *mem_ctx;
+ struct cli_credentials *creds;
+ bool ok;
+
+ creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_Format(PyExc_TypeError, "Credentials expected");
+ return NULL;
+ }
+
+ if (!PyArg_ParseTuple(args, "|O", &py_lp_ctx)) {
+ return NULL;
+ }
+
+ mem_ctx = talloc_new(NULL);
+ if (mem_ctx == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ lp_ctx = lpcfg_from_py_object(mem_ctx, py_lp_ctx);
+ if (lp_ctx == NULL) {
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ ok = cli_credentials_set_conf(creds, lp_ctx);
+ talloc_free(mem_ctx);
+ if (!ok) {
+ return NULL;
+ }
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_creds_guess(PyObject *self, PyObject *args)
+{
+ PyObject *py_lp_ctx = Py_None;
+ struct loadparm_context *lp_ctx;
+ TALLOC_CTX *mem_ctx;
+ struct cli_credentials *creds;
+ bool ok;
+
+ creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_Format(PyExc_TypeError, "Credentials expected");
+ return NULL;
+ }
+
+ if (!PyArg_ParseTuple(args, "|O", &py_lp_ctx))
+ return NULL;
+
+ mem_ctx = talloc_new(NULL);
+ if (mem_ctx == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ lp_ctx = lpcfg_from_py_object(mem_ctx, py_lp_ctx);
+ if (lp_ctx == NULL) {
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ ok = cli_credentials_guess(creds, lp_ctx);
+ talloc_free(mem_ctx);
+ if (!ok) {
+ return NULL;
+ }
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_creds_set_machine_account(PyObject *self, PyObject *args)
+{
+ PyObject *py_lp_ctx = Py_None;
+ struct loadparm_context *lp_ctx;
+ NTSTATUS status;
+ struct cli_credentials *creds;
+ TALLOC_CTX *mem_ctx;
+
+ creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_Format(PyExc_TypeError, "Credentials expected");
+ return NULL;
+ }
+
+ if (!PyArg_ParseTuple(args, "|O", &py_lp_ctx))
+ return NULL;
+
+ mem_ctx = talloc_new(NULL);
+ if (mem_ctx == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ lp_ctx = lpcfg_from_py_object(mem_ctx, py_lp_ctx);
+ if (lp_ctx == NULL) {
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ status = cli_credentials_set_machine_account(creds, lp_ctx);
+ talloc_free(mem_ctx);
+
+ PyErr_NTSTATUS_IS_ERR_RAISE(status);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *PyCredentialCacheContainer_from_ccache_container(struct ccache_container *ccc)
+{
+ return pytalloc_reference(&PyCredentialCacheContainer, ccc);
+}
+
+
+static PyObject *py_creds_get_named_ccache(PyObject *self, PyObject *args)
+{
+ PyObject *py_lp_ctx = Py_None;
+ char *ccache_name = NULL;
+ struct loadparm_context *lp_ctx;
+ struct ccache_container *ccc;
+ struct tevent_context *event_ctx;
+ int ret;
+ const char *error_string;
+ struct cli_credentials *creds;
+ TALLOC_CTX *mem_ctx;
+
+ creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_Format(PyExc_TypeError, "Credentials expected");
+ return NULL;
+ }
+
+ if (!PyArg_ParseTuple(args, "|Os", &py_lp_ctx, &ccache_name))
+ return NULL;
+
+ mem_ctx = talloc_new(NULL);
+ if (mem_ctx == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ lp_ctx = lpcfg_from_py_object(mem_ctx, py_lp_ctx);
+ if (lp_ctx == NULL) {
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ event_ctx = samba_tevent_context_init(mem_ctx);
+
+ ret = cli_credentials_get_named_ccache(creds, event_ctx, lp_ctx,
+ ccache_name, &ccc, &error_string);
+ talloc_unlink(mem_ctx, lp_ctx);
+ if (ret == 0) {
+ talloc_steal(ccc, event_ctx);
+ talloc_free(mem_ctx);
+ return PyCredentialCacheContainer_from_ccache_container(ccc);
+ }
+
+ PyErr_SetString(PyExc_RuntimeError, error_string?error_string:"NULL");
+
+ talloc_free(mem_ctx);
+ return NULL;
+}
+
+static PyObject *py_creds_set_named_ccache(PyObject *self, PyObject *args)
+{
+ struct loadparm_context *lp_ctx = NULL;
+ enum credentials_obtained obt = CRED_SPECIFIED;
+ const char *error_string = NULL;
+ TALLOC_CTX *mem_ctx = NULL;
+ char *newval = NULL;
+ PyObject *py_lp_ctx = Py_None;
+ int _obt = obt;
+ int ret;
+ struct cli_credentials *creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_Format(PyExc_TypeError, "Credentials expected");
+ return NULL;
+ }
+
+ if (!PyArg_ParseTuple(args, "s|iO", &newval, &_obt, &py_lp_ctx))
+ return NULL;
+ obt = _obt;
+
+ mem_ctx = talloc_new(NULL);
+ if (mem_ctx == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ lp_ctx = lpcfg_from_py_object(mem_ctx, py_lp_ctx);
+ if (lp_ctx == NULL) {
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ ret = cli_credentials_set_ccache(creds,
+ lp_ctx,
+ newval, obt,
+ &error_string);
+
+ if (ret != 0) {
+ PyErr_SetString(PyExc_RuntimeError,
+ error_string != NULL ? error_string : "NULL");
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ talloc_free(mem_ctx);
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_creds_set_gensec_features(PyObject *self, PyObject *args)
+{
+ unsigned int gensec_features;
+ struct cli_credentials *creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_Format(PyExc_TypeError, "Credentials expected");
+ return NULL;
+ }
+
+ if (!PyArg_ParseTuple(args, "I", &gensec_features))
+ return NULL;
+
+ cli_credentials_set_gensec_features(creds,
+ gensec_features,
+ CRED_SPECIFIED);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_creds_get_gensec_features(PyObject *self, PyObject *args)
+{
+ unsigned int gensec_features;
+ struct cli_credentials *creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_Format(PyExc_TypeError, "Credentials expected");
+ return NULL;
+ }
+
+ gensec_features = cli_credentials_get_gensec_features(creds);
+ return PyLong_FromLong(gensec_features);
+}
+
+static PyObject *py_creds_new_client_authenticator(PyObject *self,
+ PyObject *args)
+{
+ struct netr_Authenticator auth;
+ struct cli_credentials *creds = NULL;
+ struct netlogon_creds_CredentialState *nc = NULL;
+ PyObject *ret = NULL;
+ NTSTATUS status;
+
+ creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "Failed to get credentials from python");
+ return NULL;
+ }
+
+ nc = creds->netlogon_creds;
+ if (nc == NULL) {
+ PyErr_SetString(PyExc_ValueError,
+ "No netlogon credentials cannot make "
+ "client authenticator");
+ return NULL;
+ }
+
+ status = netlogon_creds_client_authenticator(nc, &auth);
+ if (!NT_STATUS_IS_OK(status)) {
+ PyErr_SetString(PyExc_ValueError,
+ "Failed to create client authenticator");
+ return NULL;
+ }
+
+ ret = Py_BuildValue("{s"PYARG_BYTES_LEN"si}",
+ "credential",
+ (const char *) &auth.cred, sizeof(auth.cred),
+ "timestamp", auth.timestamp);
+ return ret;
+}
+
+static PyObject *py_creds_set_secure_channel_type(PyObject *self, PyObject *args)
+{
+ unsigned int channel_type;
+ struct cli_credentials *creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_Format(PyExc_TypeError, "Credentials expected");
+ return NULL;
+ }
+
+ if (!PyArg_ParseTuple(args, "I", &channel_type))
+ return NULL;
+
+ cli_credentials_set_secure_channel_type(
+ creds,
+ channel_type);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_creds_get_secure_channel_type(PyObject *self, PyObject *args)
+{
+ enum netr_SchannelType channel_type = SEC_CHAN_NULL;
+ struct cli_credentials *creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_Format(PyExc_TypeError, "Credentials expected");
+ return NULL;
+ }
+
+ channel_type = cli_credentials_get_secure_channel_type(creds);
+
+ return PyLong_FromLong(channel_type);
+}
+
+static PyObject *py_creds_get_aes256_key(PyObject *self, PyObject *args)
+{
+ struct loadparm_context *lp_ctx = NULL;
+ TALLOC_CTX *mem_ctx = NULL;
+ PyObject *py_lp_ctx = Py_None;
+ const char *salt = NULL;
+ DATA_BLOB aes_256;
+ int code;
+ PyObject *ret = NULL;
+ struct cli_credentials *creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_Format(PyExc_TypeError, "Credentials expected");
+ return NULL;
+ }
+
+ if (!PyArg_ParseTuple(args, "s|O", &salt, &py_lp_ctx))
+ return NULL;
+
+ mem_ctx = talloc_new(NULL);
+ if (mem_ctx == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ lp_ctx = lpcfg_from_py_object(mem_ctx, py_lp_ctx);
+ if (lp_ctx == NULL) {
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ code = cli_credentials_get_aes256_key(creds,
+ mem_ctx,
+ lp_ctx,
+ salt,
+ &aes_256);
+ if (code != 0) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "Failed to generate AES256 key");
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ ret = PyBytes_FromStringAndSize((const char *)aes_256.data,
+ aes_256.length);
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+static PyObject *py_creds_encrypt_netr_crypt_password(PyObject *self,
+ PyObject *args)
+{
+ DATA_BLOB data = data_blob_null;
+ struct cli_credentials *creds = NULL;
+ struct netr_CryptPassword *pwd = NULL;
+ NTSTATUS status;
+ PyObject *py_cp = Py_None;
+
+ creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_Format(PyExc_TypeError, "Credentials expected");
+ return NULL;
+ }
+
+ if (!PyArg_ParseTuple(args, "O", &py_cp)) {
+ return NULL;
+ }
+
+ pwd = pytalloc_get_type(py_cp, struct netr_CryptPassword);
+ if (pwd == NULL) {
+ /* pytalloc_get_type sets TypeError */
+ return NULL;
+ }
+ data.length = sizeof(struct netr_CryptPassword);
+ data.data = (uint8_t *)pwd;
+ status = netlogon_creds_session_encrypt(creds->netlogon_creds, data);
+
+ PyErr_NTSTATUS_IS_ERR_RAISE(status);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_creds_encrypt_samr_password(PyObject *self,
+ PyObject *args)
+{
+ DATA_BLOB data = data_blob_null;
+ struct cli_credentials *creds = NULL;
+ struct samr_Password *pwd = NULL;
+ NTSTATUS status;
+ PyObject *py_cp = Py_None;
+
+ creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_Format(PyExc_TypeError, "Credentials expected");
+ return NULL;
+ }
+
+ if (!PyArg_ParseTuple(args, "O", &py_cp)) {
+ return NULL;
+ }
+
+ pwd = pytalloc_get_type(py_cp, struct samr_Password);
+ if (pwd == NULL) {
+ /* pytalloc_get_type sets TypeError */
+ return NULL;
+ }
+ data = data_blob_const(pwd->hash, sizeof(pwd->hash));
+ status = netlogon_creds_session_encrypt(creds->netlogon_creds, data);
+
+ PyErr_NTSTATUS_IS_ERR_RAISE(status);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_creds_get_smb_signing(PyObject *self, PyObject *unused)
+{
+ enum smb_signing_setting signing_state;
+ struct cli_credentials *creds = NULL;
+
+ creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_Format(PyExc_TypeError, "Credentials expected");
+ return NULL;
+ }
+
+ signing_state = cli_credentials_get_smb_signing(creds);
+ return PyLong_FromLong(signing_state);
+}
+
+static PyObject *py_creds_set_smb_signing(PyObject *self, PyObject *args)
+{
+ enum smb_signing_setting signing_state;
+ struct cli_credentials *creds = NULL;
+ enum credentials_obtained obt = CRED_SPECIFIED;
+
+ creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_Format(PyExc_TypeError, "Credentials expected");
+ return NULL;
+ }
+ if (!PyArg_ParseTuple(args, "i|i", &signing_state, &obt)) {
+ return NULL;
+ }
+
+ switch (signing_state) {
+ case SMB_SIGNING_DEFAULT:
+ case SMB_SIGNING_OFF:
+ case SMB_SIGNING_IF_REQUIRED:
+ case SMB_SIGNING_DESIRED:
+ case SMB_SIGNING_REQUIRED:
+ break;
+ default:
+ PyErr_Format(PyExc_TypeError, "Invalid signing state value");
+ return NULL;
+ }
+
+ cli_credentials_set_smb_signing(creds, signing_state, obt);
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_creds_get_smb_ipc_signing(PyObject *self, PyObject *unused)
+{
+ enum smb_signing_setting signing_state;
+ struct cli_credentials *creds = NULL;
+
+ creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_Format(PyExc_TypeError, "Credentials expected");
+ return NULL;
+ }
+
+ signing_state = cli_credentials_get_smb_ipc_signing(creds);
+ return PyLong_FromLong(signing_state);
+}
+
+static PyObject *py_creds_set_smb_ipc_signing(PyObject *self, PyObject *args)
+{
+ enum smb_signing_setting signing_state;
+ struct cli_credentials *creds = NULL;
+ enum credentials_obtained obt = CRED_SPECIFIED;
+
+ creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_Format(PyExc_TypeError, "Credentials expected");
+ return NULL;
+ }
+ if (!PyArg_ParseTuple(args, "i|i", &signing_state, &obt)) {
+ return NULL;
+ }
+
+ switch (signing_state) {
+ case SMB_SIGNING_DEFAULT:
+ case SMB_SIGNING_OFF:
+ case SMB_SIGNING_IF_REQUIRED:
+ case SMB_SIGNING_DESIRED:
+ case SMB_SIGNING_REQUIRED:
+ break;
+ default:
+ PyErr_Format(PyExc_TypeError, "Invalid signing state value");
+ return NULL;
+ }
+
+ cli_credentials_set_smb_ipc_signing(creds, signing_state, obt);
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_creds_get_smb_encryption(PyObject *self, PyObject *unused)
+{
+ enum smb_encryption_setting encryption_state;
+ struct cli_credentials *creds = NULL;
+
+ creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_Format(PyExc_TypeError, "Credentials expected");
+ return NULL;
+ }
+
+ encryption_state = cli_credentials_get_smb_encryption(creds);
+ return PyLong_FromLong(encryption_state);
+}
+
+static PyObject *py_creds_set_smb_encryption(PyObject *self, PyObject *args)
+{
+ enum smb_encryption_setting encryption_state;
+ struct cli_credentials *creds = NULL;
+ enum credentials_obtained obt = CRED_SPECIFIED;
+
+ creds = PyCredentials_AsCliCredentials(self);
+ if (creds == NULL) {
+ PyErr_Format(PyExc_TypeError, "Credentials expected");
+ return NULL;
+ }
+ if (!PyArg_ParseTuple(args, "i|i", &encryption_state, &obt)) {
+ return NULL;
+ }
+
+ switch (encryption_state) {
+ case SMB_ENCRYPTION_DEFAULT:
+ case SMB_ENCRYPTION_OFF:
+ case SMB_ENCRYPTION_IF_REQUIRED:
+ case SMB_ENCRYPTION_DESIRED:
+ case SMB_ENCRYPTION_REQUIRED:
+ break;
+ default:
+ PyErr_Format(PyExc_TypeError, "Invalid encryption state value");
+ return NULL;
+ }
+
+ (void)cli_credentials_set_smb_encryption(creds, encryption_state, obt);
+ Py_RETURN_NONE;
+}
+
+static PyMethodDef py_creds_methods[] = {
+ {
+ .ml_name = "get_username",
+ .ml_meth = py_creds_get_username,
+ .ml_flags = METH_NOARGS,
+ .ml_doc = "S.get_username() -> username\nObtain username.",
+ },
+ {
+ .ml_name = "set_username",
+ .ml_meth = py_creds_set_username,
+ .ml_flags = METH_VARARGS,
+ .ml_doc = "S.set_username(name[, credentials.SPECIFIED]) -> None\n"
+ "Change username.",
+ },
+ {
+ .ml_name = "get_principal",
+ .ml_meth = py_creds_get_principal,
+ .ml_flags = METH_NOARGS,
+ .ml_doc = "S.get_principal() -> user@realm\nObtain user principal.",
+ },
+ {
+ .ml_name = "set_principal",
+ .ml_meth = py_creds_set_principal,
+ .ml_flags = METH_VARARGS,
+ .ml_doc = "S.set_principal(name[, credentials.SPECIFIED]) -> None\n"
+ "Change principal.",
+ },
+ {
+ .ml_name = "get_password",
+ .ml_meth = py_creds_get_password,
+ .ml_flags = METH_NOARGS,
+ .ml_doc = "S.get_password() -> password\n"
+ "Obtain password.",
+ },
+ {
+ .ml_name = "get_ntlm_username_domain",
+ .ml_meth = py_creds_get_ntlm_username_domain,
+ .ml_flags = METH_NOARGS,
+ .ml_doc = "S.get_ntlm_username_domain() -> (domain, username)\n"
+ "Obtain NTLM username and domain, split up either as (DOMAIN, user) or (\"\", \"user@realm\").",
+ },
+ {
+ .ml_name = "get_ntlm_response",
+ .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction,
+ py_creds_get_ntlm_response),
+ .ml_flags = METH_VARARGS | METH_KEYWORDS,
+ .ml_doc = "S.get_ntlm_response"
+ "(flags, challenge[, target_info]) -> "
+ "(flags, lm_response, nt_response, lm_session_key, nt_session_key)\n"
+ "Obtain LM or NTLM response.",
+ },
+ {
+ .ml_name = "set_password",
+ .ml_meth = py_creds_set_password,
+ .ml_flags = METH_VARARGS,
+ .ml_doc = "S.set_password(password[, credentials.SPECIFIED]) -> None\n"
+ "Change password.",
+ },
+ {
+ .ml_name = "set_utf16_password",
+ .ml_meth = py_creds_set_utf16_password,
+ .ml_flags = METH_VARARGS,
+ .ml_doc = "S.set_utf16_password(password[, credentials.SPECIFIED]) -> None\n"
+ "Change password.",
+ },
+ {
+ .ml_name = "get_old_password",
+ .ml_meth = py_creds_get_old_password,
+ .ml_flags = METH_NOARGS,
+ .ml_doc = "S.get_old_password() -> password\n"
+ "Obtain old password.",
+ },
+ {
+ .ml_name = "set_old_password",
+ .ml_meth = py_creds_set_old_password,
+ .ml_flags = METH_VARARGS,
+ .ml_doc = "S.set_old_password(password[, credentials.SPECIFIED]) -> None\n"
+ "Change old password.",
+ },
+ {
+ .ml_name = "set_old_utf16_password",
+ .ml_meth = py_creds_set_old_utf16_password,
+ .ml_flags = METH_VARARGS,
+ .ml_doc = "S.set_old_utf16_password(password[, credentials.SPECIFIED]) -> None\n"
+ "Change old password.",
+ },
+ {
+ .ml_name = "get_domain",
+ .ml_meth = py_creds_get_domain,
+ .ml_flags = METH_NOARGS,
+ .ml_doc = "S.get_domain() -> domain\n"
+ "Obtain domain name.",
+ },
+ {
+ .ml_name = "set_domain",
+ .ml_meth = py_creds_set_domain,
+ .ml_flags = METH_VARARGS,
+ .ml_doc = "S.set_domain(domain[, credentials.SPECIFIED]) -> None\n"
+ "Change domain name.",
+ },
+ {
+ .ml_name = "get_realm",
+ .ml_meth = py_creds_get_realm,
+ .ml_flags = METH_NOARGS,
+ .ml_doc = "S.get_realm() -> realm\n"
+ "Obtain realm name.",
+ },
+ {
+ .ml_name = "set_realm",
+ .ml_meth = py_creds_set_realm,
+ .ml_flags = METH_VARARGS,
+ .ml_doc = "S.set_realm(realm[, credentials.SPECIFIED]) -> None\n"
+ "Change realm name.",
+ },
+ {
+ .ml_name = "get_bind_dn",
+ .ml_meth = py_creds_get_bind_dn,
+ .ml_flags = METH_NOARGS,
+ .ml_doc = "S.get_bind_dn() -> bind dn\n"
+ "Obtain bind DN.",
+ },
+ {
+ .ml_name = "set_bind_dn",
+ .ml_meth = py_creds_set_bind_dn,
+ .ml_flags = METH_VARARGS,
+ .ml_doc = "S.set_bind_dn(bind_dn) -> None\n"
+ "Change bind DN.",
+ },
+ {
+ .ml_name = "is_anonymous",
+ .ml_meth = py_creds_is_anonymous,
+ .ml_flags = METH_NOARGS,
+ },
+ {
+ .ml_name = "set_anonymous",
+ .ml_meth = py_creds_set_anonymous,
+ .ml_flags = METH_NOARGS,
+ .ml_doc = "S.set_anonymous() -> None\n"
+ "Use anonymous credentials.",
+ },
+ {
+ .ml_name = "get_workstation",
+ .ml_meth = py_creds_get_workstation,
+ .ml_flags = METH_NOARGS,
+ },
+ {
+ .ml_name = "set_workstation",
+ .ml_meth = py_creds_set_workstation,
+ .ml_flags = METH_VARARGS,
+ },
+ {
+ .ml_name = "authentication_requested",
+ .ml_meth = py_creds_authentication_requested,
+ .ml_flags = METH_NOARGS,
+ },
+ {
+ .ml_name = "wrong_password",
+ .ml_meth = py_creds_wrong_password,
+ .ml_flags = METH_NOARGS,
+ .ml_doc = "S.wrong_password() -> bool\n"
+ "Indicate the returned password was incorrect.",
+ },
+ {
+ .ml_name = "set_cmdline_callbacks",
+ .ml_meth = py_creds_set_cmdline_callbacks,
+ .ml_flags = METH_NOARGS,
+ .ml_doc = "S.set_cmdline_callbacks() -> bool\n"
+ "Use command-line to obtain credentials not explicitly set.",
+ },
+ {
+ .ml_name = "parse_string",
+ .ml_meth = py_creds_parse_string,
+ .ml_flags = METH_VARARGS,
+ .ml_doc = "S.parse_string(text[, credentials.SPECIFIED]) -> None\n"
+ "Parse credentials string.",
+ },
+ {
+ .ml_name = "parse_file",
+ .ml_meth = py_creds_parse_file,
+ .ml_flags = METH_VARARGS,
+ .ml_doc = "S.parse_file(filename[, credentials.SPECIFIED]) -> None\n"
+ "Parse credentials file.",
+ },
+ {
+ .ml_name = "set_password_will_be_nt_hash",
+ .ml_meth = py_cli_credentials_set_password_will_be_nt_hash,
+ .ml_flags = METH_VARARGS,
+ .ml_doc = "S.set_password_will_be_nt_hash(bool) -> None\n"
+ "Alters the behaviour of S.set_password() "
+ "to expect the NTHASH as hexstring.",
+ },
+ {
+ .ml_name = "get_nt_hash",
+ .ml_meth = py_creds_get_nt_hash,
+ .ml_flags = METH_NOARGS,
+ },
+ {
+ .ml_name = "get_kerberos_state",
+ .ml_meth = py_creds_get_kerberos_state,
+ .ml_flags = METH_NOARGS,
+ },
+ {
+ .ml_name = "set_kerberos_state",
+ .ml_meth = py_creds_set_kerberos_state,
+ .ml_flags = METH_VARARGS,
+ },
+ {
+ .ml_name = "set_krb_forwardable",
+ .ml_meth = py_creds_set_krb_forwardable,
+ .ml_flags = METH_VARARGS,
+ },
+ {
+ .ml_name = "set_conf",
+ .ml_meth = py_creds_set_conf,
+ .ml_flags = METH_VARARGS,
+ },
+ {
+ .ml_name = "guess",
+ .ml_meth = py_creds_guess,
+ .ml_flags = METH_VARARGS,
+ },
+ {
+ .ml_name = "set_machine_account",
+ .ml_meth = py_creds_set_machine_account,
+ .ml_flags = METH_VARARGS,
+ },
+ {
+ .ml_name = "get_named_ccache",
+ .ml_meth = py_creds_get_named_ccache,
+ .ml_flags = METH_VARARGS,
+ },
+ {
+ .ml_name = "set_named_ccache",
+ .ml_meth = py_creds_set_named_ccache,
+ .ml_flags = METH_VARARGS,
+ .ml_doc = "S.set_named_ccache(krb5_ccache_name, obtained, lp) -> None\n"
+ "Set credentials to KRB5 Credentials Cache (by name).",
+ },
+ {
+ .ml_name = "set_gensec_features",
+ .ml_meth = py_creds_set_gensec_features,
+ .ml_flags = METH_VARARGS,
+ },
+ {
+ .ml_name = "get_gensec_features",
+ .ml_meth = py_creds_get_gensec_features,
+ .ml_flags = METH_NOARGS,
+ },
+ {
+ .ml_name = "get_forced_sasl_mech",
+ .ml_meth = py_creds_get_forced_sasl_mech,
+ .ml_flags = METH_NOARGS,
+ .ml_doc = "S.get_forced_sasl_mech() -> SASL mechanism\nObtain forced SASL mechanism.",
+ },
+ {
+ .ml_name = "set_forced_sasl_mech",
+ .ml_meth = py_creds_set_forced_sasl_mech,
+ .ml_flags = METH_VARARGS,
+ .ml_doc = "S.set_forced_sasl_mech(name) -> None\n"
+ "Set forced SASL mechanism.",
+ },
+ {
+ .ml_name = "new_client_authenticator",
+ .ml_meth = py_creds_new_client_authenticator,
+ .ml_flags = METH_NOARGS,
+ .ml_doc = "S.new_client_authenticator() -> Authenticator\n"
+ "Get a new client NETLOGON_AUTHENTICATOR"},
+ {
+ .ml_name = "set_secure_channel_type",
+ .ml_meth = py_creds_set_secure_channel_type,
+ .ml_flags = METH_VARARGS,
+ },
+ {
+ .ml_name = "get_secure_channel_type",
+ .ml_meth = py_creds_get_secure_channel_type,
+ .ml_flags = METH_VARARGS,
+ },
+ {
+ .ml_name = "get_aes256_key",
+ .ml_meth = py_creds_get_aes256_key,
+ .ml_flags = METH_VARARGS,
+ .ml_doc = "S.get_aes256_key(salt[, lp]) -> bytes\n"
+ "Generate an AES256 key using the current password and\n"
+ "the specified salt",
+ },
+ {
+ .ml_name = "encrypt_netr_crypt_password",
+ .ml_meth = py_creds_encrypt_netr_crypt_password,
+ .ml_flags = METH_VARARGS,
+ .ml_doc = "S.encrypt_netr_crypt_password(password) -> None\n"
+ "Encrypt the supplied password using the session key and\n"
+ "the negotiated encryption algorithm in place\n"
+ "i.e. it overwrites the original data"},
+ {
+ .ml_name = "encrypt_samr_password",
+ .ml_meth = py_creds_encrypt_samr_password,
+ .ml_flags = METH_VARARGS,
+ .ml_doc = "S.encrypt_samr_password(password) -> None\n"
+ "Encrypt the supplied password using the session key and\n"
+ "the negotiated encryption algorithm in place\n"
+ "i.e. it overwrites the original data"
+ },
+ {
+ .ml_name = "get_smb_signing",
+ .ml_meth = py_creds_get_smb_signing,
+ .ml_flags = METH_NOARGS,
+ },
+ {
+ .ml_name = "set_smb_signing",
+ .ml_meth = py_creds_set_smb_signing,
+ .ml_flags = METH_VARARGS,
+ },
+ {
+ .ml_name = "get_smb_ipc_signing",
+ .ml_meth = py_creds_get_smb_ipc_signing,
+ .ml_flags = METH_NOARGS,
+ },
+ {
+ .ml_name = "set_smb_ipc_signing",
+ .ml_meth = py_creds_set_smb_ipc_signing,
+ .ml_flags = METH_VARARGS,
+ },
+ {
+ .ml_name = "get_smb_encryption",
+ .ml_meth = py_creds_get_smb_encryption,
+ .ml_flags = METH_NOARGS,
+ },
+ {
+ .ml_name = "set_smb_encryption",
+ .ml_meth = py_creds_set_smb_encryption,
+ .ml_flags = METH_VARARGS,
+ },
+ { .ml_name = NULL }
+};
+
+static struct PyModuleDef moduledef = {
+ PyModuleDef_HEAD_INIT,
+ .m_name = "credentials",
+ .m_doc = "Credentials management.",
+ .m_size = -1,
+ .m_methods = py_creds_methods,
+};
+
+PyTypeObject PyCredentials = {
+ .tp_name = "credentials.Credentials",
+ .tp_new = py_creds_new,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ .tp_methods = py_creds_methods,
+};
+
+static PyObject *py_ccache_name(PyObject *self, PyObject *unused)
+{
+ struct ccache_container *ccc = NULL;
+ char *name = NULL;
+ PyObject *py_name = NULL;
+ int ret;
+
+ ccc = pytalloc_get_type(self, struct ccache_container);
+
+ ret = krb5_cc_get_full_name(ccc->smb_krb5_context->krb5_context,
+ ccc->ccache, &name);
+ if (ret == 0) {
+ py_name = PyString_FromStringOrNULL(name);
+ SAFE_FREE(name);
+ } else {
+ PyErr_SetString(PyExc_RuntimeError,
+ "Failed to get ccache name");
+ return NULL;
+ }
+ return py_name;
+}
+
+static PyMethodDef py_ccache_container_methods[] = {
+ { "get_name", py_ccache_name, METH_NOARGS,
+ "S.get_name() -> name\nObtain KRB5 credentials cache name." },
+ {0}
+};
+
+PyTypeObject PyCredentialCacheContainer = {
+ .tp_name = "credentials.CredentialCacheContainer",
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+ .tp_methods = py_ccache_container_methods,
+};
+
+MODULE_INIT_FUNC(credentials)
+{
+ PyObject *m;
+ if (pytalloc_BaseObject_PyType_Ready(&PyCredentials) < 0)
+ return NULL;
+
+ if (pytalloc_BaseObject_PyType_Ready(&PyCredentialCacheContainer) < 0)
+ return NULL;
+
+ m = PyModule_Create(&moduledef);
+ if (m == NULL)
+ return NULL;
+
+ PyModule_AddObject(m, "UNINITIALISED", PyLong_FromLong(CRED_UNINITIALISED));
+ PyModule_AddObject(m, "SMB_CONF", PyLong_FromLong(CRED_SMB_CONF));
+ PyModule_AddObject(m, "CALLBACK", PyLong_FromLong(CRED_CALLBACK));
+ PyModule_AddObject(m, "GUESS_ENV", PyLong_FromLong(CRED_GUESS_ENV));
+ PyModule_AddObject(m, "GUESS_FILE", PyLong_FromLong(CRED_GUESS_FILE));
+ PyModule_AddObject(m, "CALLBACK_RESULT", PyLong_FromLong(CRED_CALLBACK_RESULT));
+ PyModule_AddObject(m, "SPECIFIED", PyLong_FromLong(CRED_SPECIFIED));
+
+ PyModule_AddObject(m, "AUTO_USE_KERBEROS", PyLong_FromLong(CRED_USE_KERBEROS_DESIRED));
+ PyModule_AddObject(m, "DONT_USE_KERBEROS", PyLong_FromLong(CRED_USE_KERBEROS_DISABLED));
+ PyModule_AddObject(m, "MUST_USE_KERBEROS", PyLong_FromLong(CRED_USE_KERBEROS_REQUIRED));
+
+ PyModule_AddObject(m, "AUTO_KRB_FORWARDABLE", PyLong_FromLong(CRED_AUTO_KRB_FORWARDABLE));
+ PyModule_AddObject(m, "NO_KRB_FORWARDABLE", PyLong_FromLong(CRED_NO_KRB_FORWARDABLE));
+ PyModule_AddObject(m, "FORCE_KRB_FORWARDABLE", PyLong_FromLong(CRED_FORCE_KRB_FORWARDABLE));
+ PyModule_AddObject(m, "CLI_CRED_NTLM2", PyLong_FromLong(CLI_CRED_NTLM2));
+ PyModule_AddObject(m, "CLI_CRED_NTLMv2_AUTH", PyLong_FromLong(CLI_CRED_NTLMv2_AUTH));
+ PyModule_AddObject(m, "CLI_CRED_LANMAN_AUTH", PyLong_FromLong(CLI_CRED_LANMAN_AUTH));
+ PyModule_AddObject(m, "CLI_CRED_NTLM_AUTH", PyLong_FromLong(CLI_CRED_NTLM_AUTH));
+ PyModule_AddObject(m, "CLI_CRED_CLEAR_AUTH", PyLong_FromLong(CLI_CRED_CLEAR_AUTH));
+
+ PyModule_AddObject(m, "SMB_SIGNING_DEFAULT", PyLong_FromLong(SMB_SIGNING_DEFAULT));
+ PyModule_AddObject(m, "SMB_SIGNING_OFF", PyLong_FromLong(SMB_SIGNING_OFF));
+ PyModule_AddObject(m, "SMB_SIGNING_IF_REQUIRED", PyLong_FromLong(SMB_SIGNING_IF_REQUIRED));
+ PyModule_AddObject(m, "SMB_SIGNING_DESIRED", PyLong_FromLong(SMB_SIGNING_DESIRED));
+ PyModule_AddObject(m, "SMB_SIGNING_REQUIRED", PyLong_FromLong(SMB_SIGNING_REQUIRED));
+
+ PyModule_AddObject(m, "SMB_ENCRYPTION_DEFAULT", PyLong_FromLong(SMB_ENCRYPTION_DEFAULT));
+ PyModule_AddObject(m, "SMB_ENCRYPTION_OFF", PyLong_FromLong(SMB_ENCRYPTION_OFF));
+ PyModule_AddObject(m, "SMB_ENCRYPTION_IF_REQUIRED", PyLong_FromLong(SMB_ENCRYPTION_IF_REQUIRED));
+ PyModule_AddObject(m, "SMB_ENCRYPTION_DESIRED", PyLong_FromLong(SMB_ENCRYPTION_DESIRED));
+ PyModule_AddObject(m, "SMB_ENCRYPTION_REQUIRED", PyLong_FromLong(SMB_ENCRYPTION_REQUIRED));
+
+ Py_INCREF(&PyCredentials);
+ PyModule_AddObject(m, "Credentials", (PyObject *)&PyCredentials);
+ Py_INCREF(&PyCredentialCacheContainer);
+ PyModule_AddObject(m, "CredentialCacheContainer", (PyObject *)&PyCredentialCacheContainer);
+ return m;
+}
diff --git a/auth/credentials/pycredentials.h b/auth/credentials/pycredentials.h
new file mode 100644
index 0000000..bf6962c
--- /dev/null
+++ b/auth/credentials/pycredentials.h
@@ -0,0 +1,40 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2008
+
+ 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 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ 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, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef _PYCREDENTIALS_H_
+#define _PYCREDENTIALS_H_
+
+#include "auth/credentials/credentials.h"
+#include "librpc/rpc/pyrpc_util.h"
+#include <pytalloc.h>
+
+extern PyTypeObject PyCredentials;
+extern PyTypeObject PyCredentialCacheContainer;
+#define PyCredentials_Check(py_obj) \
+ py_check_dcerpc_type(py_obj, "samba.credentials", "Credentials")
+
+#define PyCredentials_AsCliCredentials(py_obj) \
+ (PyCredentials_Check(py_obj) ? \
+ pytalloc_get_type(py_obj, struct cli_credentials) : NULL)
+
+#define cli_credentials_from_py_object(py_obj) \
+ ((py_obj == Py_None) ? \
+ cli_credentials_init_anon(NULL) : \
+ PyCredentials_AsCliCredentials(py_obj))
+
+#endif /* _PYCREDENTIALS_H_ */
diff --git a/auth/credentials/samba-credentials.pc.in b/auth/credentials/samba-credentials.pc.in
new file mode 100644
index 0000000..d25bf5e
--- /dev/null
+++ b/auth/credentials/samba-credentials.pc.in
@@ -0,0 +1,12 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+modulesdir=${prefix}/modules/gensec
+
+Name: samba-credentials
+Description: Credentials management
+Requires: samba-util ndr
+Version: @PACKAGE_VERSION@
+Libs: @LIB_RPATH@ -L${libdir} -lsamba-credentials
+Cflags: -I${includedir} -DHAVE_IMMEDIATE_STRUCTURES=1
diff --git a/auth/credentials/tests/bind.py b/auth/credentials/tests/bind.py
new file mode 100755
index 0000000..ce81b73
--- /dev/null
+++ b/auth/credentials/tests/bind.py
@@ -0,0 +1,261 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# This is unit with tests for LDAP access checks
+
+import optparse
+import sys
+import base64
+import copy
+
+sys.path.insert(0, "bin/python")
+import samba
+from samba.tests.subunitrun import SubunitOptions, TestProgram
+
+import samba.getopt as options
+
+from ldb import SCOPE_BASE, SCOPE_SUBTREE, LdbError, ERR_INVALID_CREDENTIALS
+
+from samba import gensec
+import samba.tests
+from samba.tests import delete_force
+from samba.credentials import Credentials
+
+def create_credential(lp, other):
+ c = Credentials()
+ c.guess(lp)
+ c.set_gensec_features(other.get_gensec_features())
+ return c
+
+parser = optparse.OptionParser("bind [options] <host>")
+sambaopts = options.SambaOptions(parser)
+parser.add_option_group(sambaopts)
+
+# use command line creds if available
+credopts = options.CredentialsOptions(parser)
+parser.add_option_group(credopts)
+subunitopts = SubunitOptions(parser)
+parser.add_option_group(subunitopts)
+opts, args = parser.parse_args()
+
+if len(args) < 1:
+ parser.print_usage()
+ sys.exit(1)
+
+host = args[0]
+lp = sambaopts.get_loadparm()
+creds = credopts.get_credentials(lp)
+creds.set_gensec_features(creds.get_gensec_features() | gensec.FEATURE_SEAL)
+
+creds_machine = create_credential(lp, creds)
+creds_virtual = create_credential(lp, creds)
+creds_user1 = create_credential(lp, creds)
+creds_user2 = create_credential(lp, creds)
+creds_user3 = create_credential(lp, creds)
+creds_user4 = create_credential(lp, creds)
+creds_user5 = create_credential(lp, creds)
+creds_user6 = create_credential(lp, creds)
+creds_user7 = create_credential(lp, creds)
+
+class BindTests(samba.tests.TestCase):
+
+ info_dc = None
+
+ def setUp(self):
+ super(BindTests, self).setUp()
+ # fetch rootDSEs
+
+ self.ldb = samba.tests.connect_samdb(host, credentials=creds, lp=lp, ldap_only=True)
+
+ if self.info_dc is None:
+ res = self.ldb.search(base="", expression="", scope=SCOPE_BASE, attrs=["*"])
+ self.assertEqual(len(res), 1)
+ BindTests.info_dc = res[0]
+ # cache some of RootDSE props
+ self.schema_dn = self.info_dc["schemaNamingContext"][0]
+ self.domain_dn = self.info_dc["defaultNamingContext"][0]
+ self.config_dn = self.info_dc["configurationNamingContext"][0]
+ self.realm = self.info_dc["ldapServiceName"][0].split(b'@')[1].decode('utf-8')
+ self.computer_dn = "CN=centos53,CN=Computers,%s" % self.domain_dn
+ self.virtual_user_dn = "CN=frednurk@%s,CN=Computers,%s" % (self.realm, self.domain_dn)
+ self.password = "P@ssw0rd"
+ self.username = "BindTestUser"
+
+ def tearDown(self):
+ delete_force(self.ldb, self.virtual_user_dn)
+ super(BindTests, self).tearDown()
+
+ def test_virtual_email_account_style_bind(self):
+ # create a user in the style often deployed for authentication
+ # of virtual email account at a hosting provider
+ #
+ # The userPrincipalName must not match the samAccountName for
+ # this test to detect when the LDAP DN is being double-parsed
+ # but must be in the user@realm style to allow the account to
+ # be created
+ try:
+ self.ldb.add_ldif("""
+dn: """ + self.virtual_user_dn + """
+cn: frednurk@""" + self.realm + """
+displayName: Fred Nurk
+sAMAccountName: frednurk@""" + self.realm + """
+userPrincipalName: frednurk@NOT.""" + self.realm + """
+countryCode: 0
+objectClass: computer
+objectClass: organizationalPerson
+objectClass: person
+objectClass: top
+objectClass: user
+""")
+ except LdbError as e:
+ (num, msg) = e.args
+ self.fail(f"Failed to create e-mail user: {msg}")
+
+ self.addCleanup(delete_force, self.ldb, self.virtual_user_dn)
+ try:
+ self.ldb.modify_ldif("""
+dn: """ + self.virtual_user_dn + """
+changetype: modify
+replace: unicodePwd
+unicodePwd:: """ + base64.b64encode(u"\"P@ssw0rd\"".encode('utf-16-le')).decode('utf8') + """
+""")
+ except LdbError as e:
+ (num, msg) = e.args
+ self.fail(f"Failed to set password on e-mail user: {msg}")
+
+ self.ldb.enable_account('distinguishedName=%s' % self.virtual_user_dn)
+
+ # do a simple bind and search with the machine account
+ creds_virtual.set_bind_dn(self.virtual_user_dn)
+ creds_virtual.set_password(self.password)
+ print("BindTest with: " + creds_virtual.get_bind_dn())
+ try:
+ ldb_virtual = samba.tests.connect_samdb(host, credentials=creds_virtual,
+ lp=lp, ldap_only=True)
+ except LdbError as e:
+ (num, msg) = e.args
+ if num != ERR_INVALID_CREDENTIALS:
+ raise
+ self.fail(msg)
+
+ res = ldb_virtual.search(base="", expression="", scope=SCOPE_BASE, attrs=["*"])
+
+ def test_computer_account_bind(self):
+ # create a computer acocount for the test
+ delete_force(self.ldb, self.computer_dn)
+ self.ldb.add_ldif("""
+dn: """ + self.computer_dn + """
+cn: CENTOS53
+displayName: CENTOS53$
+name: CENTOS53
+sAMAccountName: CENTOS53$
+countryCode: 0
+objectClass: computer
+objectClass: organizationalPerson
+objectClass: person
+objectClass: top
+objectClass: user
+codePage: 0
+userAccountControl: 4096
+dNSHostName: centos53.alabala.test
+operatingSystemVersion: 5.2 (3790)
+operatingSystem: Windows Server 2003
+""")
+ self.ldb.modify_ldif("""
+dn: """ + self.computer_dn + """
+changetype: modify
+replace: unicodePwd
+unicodePwd:: """ + base64.b64encode(u"\"P@ssw0rd\"".encode('utf-16-le')).decode('utf8') + """
+""")
+
+ # do a simple bind and search with the machine account
+ creds_machine.set_bind_dn(self.computer_dn)
+ creds_machine.set_password(self.password)
+ print("BindTest with: " + creds_machine.get_bind_dn())
+ ldb_machine = samba.tests.connect_samdb(host, credentials=creds_machine,
+ lp=lp, ldap_only=True)
+ res = ldb_machine.search(base="", expression="", scope=SCOPE_BASE, attrs=["*"])
+
+ def test_user_account_bind(self):
+ # create user
+ self.ldb.newuser(username=self.username, password=self.password)
+ ldb_res = self.ldb.search(base=self.domain_dn,
+ scope=SCOPE_SUBTREE,
+ expression="(samAccountName=%s)" % self.username,
+ attrs=["objectSid"])
+ self.assertEqual(len(ldb_res), 1)
+ user_dn = ldb_res[0]["dn"]
+ self.addCleanup(delete_force, self.ldb, user_dn)
+
+ # do a simple bind and search with the user account in format user@realm
+ creds_user1.set_bind_dn(self.username + "@" + creds.get_realm())
+ creds_user1.set_password(self.password)
+ print("BindTest with: " + creds_user1.get_bind_dn())
+ ldb_user1 = samba.tests.connect_samdb(host, credentials=creds_user1,
+ lp=lp, ldap_only=True)
+ res = ldb_user1.search(base="", expression="", scope=SCOPE_BASE, attrs=["*"])
+
+ # do a simple bind and search with the user account in format domain\user
+ creds_user2.set_bind_dn(creds.get_domain() + "\\" + self.username)
+ creds_user2.set_password(self.password)
+ print("BindTest with: " + creds_user2.get_bind_dn())
+ ldb_user2 = samba.tests.connect_samdb(host, credentials=creds_user2,
+ lp=lp, ldap_only=True)
+ res = ldb_user2.search(base="", expression="", scope=SCOPE_BASE, attrs=["*"])
+
+ # do a simple bind and search with the user account DN
+ creds_user3.set_bind_dn(str(user_dn))
+ creds_user3.set_password(self.password)
+ print("BindTest with: " + creds_user3.get_bind_dn())
+ ldb_user3 = samba.tests.connect_samdb(host, credentials=creds_user3,
+ lp=lp, ldap_only=True)
+ res = ldb_user3.search(base="", expression="", scope=SCOPE_BASE, attrs=["*"])
+
+ # do a simple bind and search with the user account SID
+ creds_user5.set_bind_dn(self.ldb.schema_format_value("objectSid", ldb_res[0]["objectSid"][0]).decode('utf8'))
+ creds_user5.set_password(self.password)
+ print("BindTest with: " + creds_user5.get_bind_dn())
+ ldb_user5 = samba.tests.connect_samdb(host, credentials=creds_user5,
+ lp=lp, ldap_only=True)
+ res = ldb_user5.search(base="", expression="", scope=SCOPE_BASE, attrs=["*"])
+
+ # do a simple bind and search with the canonical name
+ creds_user6.set_bind_dn(user_dn.canonical_str())
+ creds_user6.set_password(self.password)
+ print("BindTest with: " + creds_user6.get_bind_dn())
+ ldb_user6 = samba.tests.connect_samdb(host, credentials=creds_user6,
+ lp=lp, ldap_only=True)
+ res = ldb_user6.search(base="", expression="", scope=SCOPE_BASE, attrs=["*"])
+
+ # do a simple bind and search with the extended canonical name
+ creds_user7.set_bind_dn(user_dn.canonical_ex_str())
+ creds_user7.set_password(self.password)
+ print("BindTest with: " + creds_user7.get_bind_dn())
+ ldb_user7 = samba.tests.connect_samdb(host, credentials=creds_user7,
+ lp=lp, ldap_only=True)
+ res = ldb_user7.search(base="", expression="", scope=SCOPE_BASE, attrs=["*"])
+
+ def test_user_account_bind_no_domain(self):
+ # create user
+ self.ldb.newuser(username=self.username, password=self.password)
+ ldb_res = self.ldb.search(base=self.domain_dn,
+ scope=SCOPE_SUBTREE,
+ expression="(samAccountName=%s)" % self.username)
+ self.assertEqual(len(ldb_res), 1)
+ user_dn = ldb_res[0]["dn"]
+ self.addCleanup(delete_force, self.ldb, user_dn)
+
+ creds_user4.set_username(self.username)
+ creds_user4.set_password(self.password)
+ creds_user4.set_domain('')
+ creds_user4.set_workstation('')
+ print("BindTest (no domain) with: " + self.username)
+ try:
+ ldb_user4 = samba.tests.connect_samdb(host, credentials=creds_user4,
+ lp=lp, ldap_only=True)
+ except:
+ self.fail("Failed to connect without the domain set")
+
+ res = ldb_user4.search(base="", expression="", scope=SCOPE_BASE, attrs=["*"])
+
+
+TestProgram(module=__name__, opts=subunitopts)
diff --git a/auth/credentials/tests/test_creds.c b/auth/credentials/tests/test_creds.c
new file mode 100644
index 0000000..a2f9642
--- /dev/null
+++ b/auth/credentials/tests/test_creds.c
@@ -0,0 +1,311 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * Copyright (C) 2018-2019 Andreas Schneider <asn@samba.org>
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include "lib/replace/replace.h"
+#include "auth/credentials/credentials.c"
+
+static int setup_talloc_context(void **state)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ *state = frame;
+ return 0;
+}
+
+static int teardown_talloc_context(void **state)
+{
+ TALLOC_CTX *frame = *state;
+ TALLOC_FREE(frame);
+ return 0;
+}
+
+static void torture_creds_init(void **state)
+{
+ TALLOC_CTX *mem_ctx = *state;
+ struct cli_credentials *creds = NULL;
+ const char *username = NULL;
+ const char *domain = NULL;
+ const char *password = NULL;
+ enum credentials_obtained usr_obtained = CRED_UNINITIALISED;
+ enum credentials_obtained pwd_obtained = CRED_UNINITIALISED;
+ bool ok;
+
+ creds = cli_credentials_init(mem_ctx);
+ assert_non_null(creds);
+ assert_null(creds->username);
+ assert_int_equal(creds->username_obtained, CRED_UNINITIALISED);
+
+ domain = cli_credentials_get_domain(creds);
+ assert_null(domain);
+ ok = cli_credentials_set_domain(creds, "WURST", CRED_SPECIFIED);
+ assert_true(ok);
+ assert_int_equal(creds->domain_obtained, CRED_SPECIFIED);
+ domain = cli_credentials_get_domain(creds);
+ assert_string_equal(domain, "WURST");
+
+ username = cli_credentials_get_username(creds);
+ assert_null(username);
+ ok = cli_credentials_set_username(creds, "brot", CRED_SPECIFIED);
+ assert_true(ok);
+ assert_int_equal(creds->username_obtained, CRED_SPECIFIED);
+ username = cli_credentials_get_username(creds);
+ assert_string_equal(username, "brot");
+
+ username = cli_credentials_get_username_and_obtained(creds,
+ &usr_obtained);
+ assert_int_equal(usr_obtained, CRED_SPECIFIED);
+ assert_string_equal(username, "brot");
+
+ password = cli_credentials_get_password(creds);
+ assert_null(password);
+ ok = cli_credentials_set_password(creds, "SECRET", CRED_SPECIFIED);
+ assert_true(ok);
+ assert_int_equal(creds->password_obtained, CRED_SPECIFIED);
+ password = cli_credentials_get_password(creds);
+ assert_string_equal(password, "SECRET");
+
+ password = cli_credentials_get_password_and_obtained(creds,
+ &pwd_obtained);
+ assert_int_equal(pwd_obtained, CRED_SPECIFIED);
+ assert_string_equal(password, "SECRET");
+
+ /* Run dump to check it works */
+ cli_credentials_dump(creds);
+}
+
+static void torture_creds_init_anonymous(void **state)
+{
+ TALLOC_CTX *mem_ctx = *state;
+ struct cli_credentials *creds = NULL;
+
+ creds = cli_credentials_init_anon(mem_ctx);
+ assert_non_null(creds);
+
+ assert_string_equal(creds->domain, "");
+ assert_int_equal(creds->domain_obtained, CRED_SPECIFIED);
+
+ assert_string_equal(creds->username, "");
+ assert_int_equal(creds->username_obtained, CRED_SPECIFIED);
+
+ assert_null(creds->password);
+ assert_int_equal(creds->password_obtained, CRED_SPECIFIED);
+}
+
+static void torture_creds_guess(void **state)
+{
+ TALLOC_CTX *mem_ctx = *state;
+ struct cli_credentials *creds = NULL;
+ const char *env_user = getenv("USER");
+ bool ok;
+
+ creds = cli_credentials_init(mem_ctx);
+ assert_non_null(creds);
+
+ setenv("PASSWD", "SECRET", 1);
+ ok = cli_credentials_guess(creds, NULL);
+ assert_true(ok);
+
+ assert_string_equal(creds->username, env_user);
+ assert_int_equal(creds->username_obtained, CRED_GUESS_ENV);
+
+ assert_string_equal(creds->password, "SECRET");
+ assert_int_equal(creds->password_obtained, CRED_GUESS_ENV);
+ unsetenv("PASSWD");
+}
+
+static void torture_creds_anon_guess(void **state)
+{
+ TALLOC_CTX *mem_ctx = *state;
+ struct cli_credentials *creds = NULL;
+ bool ok;
+
+ creds = cli_credentials_init_anon(mem_ctx);
+ assert_non_null(creds);
+
+ setenv("PASSWD", "SECRET", 1);
+ ok = cli_credentials_guess(creds, NULL);
+ assert_true(ok);
+
+ assert_string_equal(creds->username, "");
+ assert_int_equal(creds->username_obtained, CRED_SPECIFIED);
+
+ assert_null(creds->password);
+ assert_int_equal(creds->password_obtained, CRED_SPECIFIED);
+ unsetenv("PASSWD");
+}
+
+static void torture_creds_parse_string(void **state)
+{
+ TALLOC_CTX *mem_ctx = *state;
+ struct cli_credentials *creds = NULL;
+
+ creds = cli_credentials_init(mem_ctx);
+ assert_non_null(creds);
+
+ /* Anonymous */
+ cli_credentials_parse_string(creds, "%", CRED_SPECIFIED);
+
+ assert_string_equal(creds->domain, "");
+ assert_int_equal(creds->domain_obtained, CRED_SPECIFIED);
+
+ assert_string_equal(creds->username, "");
+ assert_int_equal(creds->username_obtained, CRED_SPECIFIED);
+
+ assert_null(creds->password);
+ assert_int_equal(creds->password_obtained, CRED_SPECIFIED);
+
+ /* Username + password */
+ cli_credentials_parse_string(creds, "wurst%BROT", CRED_SPECIFIED);
+
+ assert_string_equal(creds->domain, "");
+ assert_int_equal(creds->domain_obtained, CRED_SPECIFIED);
+
+ assert_string_equal(creds->username, "wurst");
+ assert_int_equal(creds->username_obtained, CRED_SPECIFIED);
+
+ assert_string_equal(creds->password, "BROT");
+ assert_int_equal(creds->password_obtained, CRED_SPECIFIED);
+
+ /* Domain + username + password */
+ cli_credentials_parse_string(creds, "XXL\\wurst%BROT", CRED_SPECIFIED);
+
+ assert_string_equal(creds->domain, "XXL");
+ assert_int_equal(creds->domain_obtained, CRED_SPECIFIED);
+
+ assert_string_equal(creds->username, "wurst");
+ assert_int_equal(creds->username_obtained, CRED_SPECIFIED);
+
+ assert_string_equal(creds->password, "BROT");
+ assert_int_equal(creds->password_obtained, CRED_SPECIFIED);
+
+ /* Principal */
+ cli_credentials_parse_string(creds, "wurst@brot.realm", CRED_SPECIFIED);
+
+ assert_string_equal(creds->domain, "");
+ assert_int_equal(creds->domain_obtained, CRED_SPECIFIED);
+
+ assert_string_equal(creds->username, "wurst@brot.realm");
+ assert_int_equal(creds->username_obtained, CRED_SPECIFIED);
+
+ assert_string_equal(creds->principal, "wurst@brot.realm");
+ assert_int_equal(creds->principal_obtained, CRED_SPECIFIED);
+
+ assert_string_equal(creds->password, "BROT");
+ assert_int_equal(creds->password_obtained, CRED_SPECIFIED);
+}
+
+static void torture_creds_krb5_state(void **state)
+{
+ TALLOC_CTX *mem_ctx = *state;
+ struct cli_credentials *creds = NULL;
+ struct loadparm_context *lp_ctx = NULL;
+ bool ok;
+
+ lp_ctx = loadparm_init_global(true);
+ assert_non_null(lp_ctx);
+
+ creds = cli_credentials_init(mem_ctx);
+ assert_non_null(creds);
+ assert_int_equal(creds->kerberos_state_obtained, CRED_UNINITIALISED);
+ assert_int_equal(creds->kerberos_state, CRED_USE_KERBEROS_DESIRED);
+
+ ok = cli_credentials_set_conf(creds, lp_ctx);
+ assert_true(ok);
+ assert_int_equal(creds->kerberos_state_obtained, CRED_SMB_CONF);
+ assert_int_equal(creds->kerberos_state, CRED_USE_KERBEROS_DESIRED);
+
+ ok = cli_credentials_guess(creds, lp_ctx);
+ assert_true(ok);
+ assert_int_equal(creds->kerberos_state_obtained, CRED_SMB_CONF);
+ assert_int_equal(creds->kerberos_state, CRED_USE_KERBEROS_DESIRED);
+ assert_int_equal(creds->ccache_obtained, CRED_GUESS_FILE);
+ assert_non_null(creds->ccache);
+
+ ok = cli_credentials_set_kerberos_state(creds,
+ CRED_USE_KERBEROS_REQUIRED,
+ CRED_SPECIFIED);
+ assert_true(ok);
+ assert_int_equal(creds->kerberos_state_obtained, CRED_SPECIFIED);
+ assert_int_equal(creds->kerberos_state, CRED_USE_KERBEROS_REQUIRED);
+
+ ok = cli_credentials_set_kerberos_state(creds,
+ CRED_USE_KERBEROS_DISABLED,
+ CRED_SMB_CONF);
+ assert_false(ok);
+ assert_int_equal(creds->kerberos_state_obtained, CRED_SPECIFIED);
+ assert_int_equal(creds->kerberos_state, CRED_USE_KERBEROS_REQUIRED);
+
+}
+
+static void torture_creds_gensec_feature(void **state)
+{
+ TALLOC_CTX *mem_ctx = *state;
+ struct cli_credentials *creds = NULL;
+ bool ok;
+
+ creds = cli_credentials_init(mem_ctx);
+ assert_non_null(creds);
+ assert_int_equal(creds->gensec_features_obtained, CRED_UNINITIALISED);
+ assert_int_equal(creds->gensec_features, 0);
+
+ ok = cli_credentials_set_gensec_features(creds,
+ GENSEC_FEATURE_SIGN,
+ CRED_SPECIFIED);
+ assert_true(ok);
+ assert_int_equal(creds->gensec_features_obtained, CRED_SPECIFIED);
+ assert_int_equal(creds->gensec_features, GENSEC_FEATURE_SIGN);
+
+ ok = cli_credentials_set_gensec_features(creds,
+ GENSEC_FEATURE_SEAL,
+ CRED_SMB_CONF);
+ assert_false(ok);
+ assert_int_equal(creds->gensec_features_obtained, CRED_SPECIFIED);
+ assert_int_equal(creds->gensec_features, GENSEC_FEATURE_SIGN);
+}
+
+int main(int argc, char *argv[])
+{
+ int rc;
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(torture_creds_init),
+ cmocka_unit_test(torture_creds_init_anonymous),
+ cmocka_unit_test(torture_creds_guess),
+ cmocka_unit_test(torture_creds_anon_guess),
+ cmocka_unit_test(torture_creds_parse_string),
+ cmocka_unit_test(torture_creds_krb5_state),
+ cmocka_unit_test(torture_creds_gensec_feature),
+ };
+
+ if (argc == 2) {
+ cmocka_set_test_filter(argv[1]);
+ }
+ cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
+
+ rc = cmocka_run_group_tests(tests,
+ setup_talloc_context,
+ teardown_talloc_context);
+
+ return rc;
+}
diff --git a/auth/credentials/wscript_build b/auth/credentials/wscript_build
new file mode 100644
index 0000000..7568554
--- /dev/null
+++ b/auth/credentials/wscript_build
@@ -0,0 +1,43 @@
+#!/usr/bin/env python
+
+bld.SAMBA_LIBRARY('samba-credentials',
+ source='credentials.c',
+ public_headers='credentials.h',
+ pc_files='samba-credentials.pc',
+ deps='LIBCRYPTO samba-errors events LIBCLI_AUTH samba-security CREDENTIALS_SECRETS CREDENTIALS_KRB5',
+ vnum='1.0.0'
+ )
+
+bld.SAMBA_SUBSYSTEM('CREDENTIALS_KRB5',
+ source='credentials_krb5.c',
+ deps='KERBEROS_SRV_KEYTAB KERBEROS_UTIL gssapi samba-credentials',
+ public_deps='com_err authkrb5',
+ )
+
+bld.SAMBA_SUBSYSTEM('CREDENTIALS_SECRETS',
+ source='credentials_secrets.c',
+ deps='CREDENTIALS_KRB5 CREDENTIALS_NTLM ldb SECRETS samdb-common dbwrap',
+ )
+
+bld.SAMBA_SUBSYSTEM('CREDENTIALS_NTLM',
+ source='credentials_ntlm.c',
+ deps='samba-credentials GNUTLS_HELPERS')
+
+bld.SAMBA_SUBSYSTEM('CREDENTIALS_CMDLINE',
+ source='credentials_cmdline.c',
+ deps='samba-credentials')
+
+pytalloc_util = bld.pyembed_libname('pytalloc-util')
+pyparam_util = bld.pyembed_libname('pyparam_util')
+
+bld.SAMBA_PYTHON('pycredentials',
+ source='pycredentials.c',
+ public_deps='samba-credentials %s %s CREDENTIALS_CMDLINE CREDENTIALS_KRB5 CREDENTIALS_SECRETS' % (pytalloc_util, pyparam_util),
+ realname='samba/credentials.so'
+)
+
+bld.SAMBA_BINARY('test_creds',
+ source='tests/test_creds.c',
+ deps='cmocka samba-credentials',
+ local_include=False,
+ for_selftest=True)