[Patches] auth/credentials for user_auth_info

Stefan Metzmacher metze at samba.org
Mon Dec 19 22:54:47 UTC 2016


Am 18.12.2016 um 20:08 schrieb Andrew Bartlett:
> On Sun, 2016-12-18 at 13:51 +0100, Stefan Metzmacher wrote:
>> Hi Andrew,
>>
>>>
>>>>
>>>> here're some patches to prepare the auth/credentials logic for
>>>> usage within struct user_auth_info.
>>>>
>>>> This took quite some cycles to pass a full autobuild, it turns
>>>> out that we have a lot of explicit and implicit test cavarage
>>>> of the source3 POPT_COMMON_CREDENTIALS handling.
>>>>
>>>> Please review and push:-)
>>>>
>>>> This depends on the "Avoid selftest/autobuild interaction with
>>>> /tmp"
>>>> patchset.
>>>
>>> Thank you so much for doing this.  The main issue I have with it is
>>> the
>>> new password_will_be_nt_hash logic.  This seems to me to be a
>>> horrible
>>> API!
>>>
>>> This however isn't an objection, assuming you tell me (as I trust
>>> you
>>> will) that all the other options were even worse.
>>
>> I started with implementing it only in source3/lib/util_cmdline.c
>> until some tests failed and I realized that
>> cli_credentials_parse_string()
>> or the callback also need to handle the hexstring.
>> And we still have places were we use get_cmdline_auth_info_password()
>> and get_cmdline_auth_info_use_pw_nt_hash() and pass down the
>> hexstring
>> through some layers.
> 
> Thanks.  Hopefully we can improve those wrappers in time, and perhaps
> get a cleaner API eventually. 
> 
>>>
>>> I am very glad to see cli_credentials starting to get good use
>>> across
>>> the codebase.  I'm well aware it isn't ideal, but it is an
>>> improvement
>>> and the consistency brings us great opportunities. 
>>
>> Yes, there're a lot of things to do, but we can't change everything
>> on
>> one day:-)
> 
> Indeed. 
> 
> I'll look over the rest at work today.  The only other thing I noted
> was the changes to the existing tests.  Can you clarify (here, and in
> the commit message) further why the existing tests needed to be
> changed, specifically around the realm behaviour?  

See the updated commit messages. I hope that clarifies it.

Thanks!
metze
-------------- next part --------------
From 41bbc7dda1dd796d01c246002dc1cfc88d221506 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Wed, 14 Dec 2016 08:50:51 +0100
Subject: [PATCH 01/19] auth/credentials: make use of talloc_zero() in
 cli_credentials_init()

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 auth/credentials/credentials.c | 76 +-----------------------------------------
 1 file changed, 1 insertion(+), 75 deletions(-)

diff --git a/auth/credentials/credentials.c b/auth/credentials/credentials.c
index c8f86ba..7f4c15f 100644
--- a/auth/credentials/credentials.c
+++ b/auth/credentials/credentials.c
@@ -36,85 +36,11 @@
  */
 _PUBLIC_ struct cli_credentials *cli_credentials_init(TALLOC_CTX *mem_ctx) 
 {
-	struct cli_credentials *cred = talloc(mem_ctx, struct cli_credentials);
+	struct cli_credentials *cred = talloc_zero(mem_ctx, struct cli_credentials);
 	if (cred == NULL) {
 		return cred;
 	}
 
-	cred->workstation_obtained = CRED_UNINITIALISED;
-	cred->username_obtained = CRED_UNINITIALISED;
-	cred->password_obtained = CRED_UNINITIALISED;
-	cred->domain_obtained = CRED_UNINITIALISED;
-	cred->realm_obtained = CRED_UNINITIALISED;
-	cred->ccache_obtained = CRED_UNINITIALISED;
-	cred->client_gss_creds_obtained = CRED_UNINITIALISED;
-	cred->principal_obtained = CRED_UNINITIALISED;
-	cred->keytab_obtained = CRED_UNINITIALISED;
-	cred->server_gss_creds_obtained = CRED_UNINITIALISED;
-
-	cred->ccache_threshold = CRED_UNINITIALISED;
-	cred->client_gss_creds_threshold = CRED_UNINITIALISED;
-
-	cred->workstation = NULL;
-	cred->username = NULL;
-	cred->password = NULL;
-	cred->old_password = NULL;
-	cred->domain = NULL;
-	cred->realm = NULL;
-	cred->principal = NULL;
-	cred->salt_principal = NULL;
-	cred->impersonate_principal = NULL;
-	cred->self_service = NULL;
-	cred->target_service = NULL;
-
-	cred->bind_dn = NULL;
-
-	cred->nt_hash = NULL;
-	cred->old_nt_hash = NULL;
-
-	cred->lm_response.data = NULL;
-	cred->lm_response.length = 0;
-	cred->nt_response.data = NULL;
-	cred->nt_response.length = 0;
-
-	cred->ccache = NULL;
-	cred->client_gss_creds = NULL;
-	cred->keytab = NULL;
-	cred->server_gss_creds = NULL;
-
-	cred->workstation_cb = NULL;
-	cred->password_cb = NULL;
-	cred->username_cb = NULL;
-	cred->domain_cb = NULL;
-	cred->realm_cb = NULL;
-	cred->principal_cb = NULL;
-
-	cred->priv_data = NULL;
-
-	cred->netlogon_creds = NULL;
-	cred->secure_channel_type = SEC_CHAN_NULL;
-
-	cred->kvno = 0;
-
-	cred->password_last_changed_time = 0;
-
-	cred->smb_krb5_context = NULL;
-
-	cred->machine_account_pending = false;
-	cred->machine_account_pending_lp_ctx = NULL;
-
-	cred->machine_account = false;
-
-	cred->password_tries = 0;
-
-	cred->callback_running = false;
-
-	cli_credentials_set_kerberos_state(cred, CRED_AUTO_USE_KERBEROS);
-	cli_credentials_set_gensec_features(cred, 0);
-	cli_credentials_set_krb_forwardable(cred, CRED_AUTO_KRB_FORWARDABLE);
-
-	cred->forced_sasl_mech = NULL;
-
 	cred->winbind_separator = '\\';
 
 	return cred;
-- 
1.9.1


From e1d7292ad771e6705407b72a7c41122645e456e2 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Wed, 14 Dec 2016 08:52:12 +0100
Subject: [PATCH 02/19] auth/credentials: let cli_credentials_set_password()
 fail if talloc_strdup() fails

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 auth/credentials/credentials.c | 27 ++++++++++++++++++++-------
 1 file changed, 20 insertions(+), 7 deletions(-)

diff --git a/auth/credentials/credentials.c b/auth/credentials/credentials.c
index 7f4c15f..17f4b5d 100644
--- a/auth/credentials/credentials.c
+++ b/auth/credentials/credentials.c
@@ -339,18 +339,31 @@ _PUBLIC_ bool cli_credentials_set_password(struct cli_credentials *cred,
 				  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;
+		}
+
 		cred->password = talloc_strdup(cred, val);
-		if (cred->password) {
-			/* Don't print the actual password in talloc memory dumps */
-			talloc_set_name_const(cred->password, "password set via cli_credentials_set_password");
+		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;
-		cli_credentials_invalidate_ccache(cred, cred->password_obtained);
 
-		cred->nt_hash = NULL;
-		cred->lm_response = data_blob(NULL, 0);
-		cred->nt_response = data_blob(NULL, 0);
 		return true;
 	}
 
-- 
1.9.1


From dc99244d76b3da88a6337c1bf75209dd262d2329 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Wed, 14 Dec 2016 10:02:10 +0100
Subject: [PATCH 03/19] auth/credentials: add
 cli_credentials_set_password_will_be_nt_hash() and the related logic

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 auth/credentials/credentials.c          | 107 +++++++++++++++++++++++++++-----
 auth/credentials/credentials.h          |   2 +
 auth/credentials/credentials_internal.h |   2 +
 auth/credentials/credentials_ntlm.c     |  19 ++++++
 auth/credentials/pycredentials.c        |  20 ++++++
 5 files changed, 135 insertions(+), 15 deletions(-)

diff --git a/auth/credentials/credentials.c b/auth/credentials/credentials.c
index 17f4b5d..e024428 100644
--- a/auth/credentials/credentials.c
+++ b/auth/credentials/credentials.c
@@ -318,7 +318,8 @@ _PUBLIC_ const char *cli_credentials_get_password(struct cli_credentials *cred)
 	}
 
 	if (cred->password_obtained == CRED_CALLBACK && 
-	    !cred->callback_running) {
+	    !cred->callback_running &&
+	    !cred->password_will_be_nt_hash) {
 		cred->callback_running = true;
 		cred->password = cred->password_cb(cred);
 		cred->callback_running = false;
@@ -354,6 +355,29 @@ _PUBLIC_ bool cli_credentials_set_password(struct cli_credentials *cred,
 			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;
@@ -424,32 +448,85 @@ _PUBLIC_ bool cli_credentials_set_old_password(struct cli_credentials *cred,
 _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) {
-		struct samr_Password *nt_hash = talloc(mem_ctx, struct samr_Password);
-		if (!nt_hash) {
-			return NULL;
-		}
+		/*
+		 * If we already have a hash it's easy.
+		 */
+		goto return_hash;
+	}
 
-		*nt_hash = *cred->nt_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.
+	 */
 
-		return nt_hash;
-	}
+	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);
-	if (password) {
-		struct samr_Password *nt_hash = talloc(mem_ctx, struct samr_Password);
-		if (!nt_hash) {
-			return NULL;
-		}
 
+	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 false;
+		}
+	} else {
 		E_md4hash(password, nt_hash->hash);
+	}
 
-		return nt_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;
 	}
 
-	return NULL;
+	*nt_hash = *cred->nt_hash;
+
+	return nt_hash;
 }
 
 /**
diff --git a/auth/credentials/credentials.h b/auth/credentials/credentials.h
index 523793f..6b0d83b 100644
--- a/auth/credentials/credentials.h
+++ b/auth/credentials/credentials.h
@@ -201,6 +201,8 @@ bool cli_credentials_set_utf16_password(struct cli_credentials *cred,
 					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);
diff --git a/auth/credentials/credentials_internal.h b/auth/credentials/credentials_internal.h
index f88ae70..68f1f25 100644
--- a/auth/credentials/credentials_internal.h
+++ b/auth/credentials/credentials_internal.h
@@ -115,6 +115,8 @@ struct cli_credentials {
 	bool callback_running;
 
 	char winbind_separator;
+
+	bool password_will_be_nt_hash;
 };
 
 #endif /* __CREDENTIALS_INTERNAL_H__ */
diff --git a/auth/credentials/credentials_ntlm.c b/auth/credentials/credentials_ntlm.c
index 2a4c141..e6859bf 100644
--- a/auth/credentials/credentials_ntlm.c
+++ b/auth/credentials/credentials_ntlm.c
@@ -301,6 +301,8 @@ _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);
 	}
@@ -389,10 +391,27 @@ _PUBLIC_ bool cli_credentials_set_old_utf16_password(struct cli_credentials *cre
 	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) {
diff --git a/auth/credentials/pycredentials.c b/auth/credentials/pycredentials.c
index 9ea0682..e0b7392 100644
--- a/auth/credentials/pycredentials.c
+++ b/auth/credentials/pycredentials.c
@@ -299,6 +299,21 @@ static PyObject *py_creds_parse_string(PyObject *self, PyObject *args)
 	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;
@@ -564,6 +579,11 @@ static PyMethodDef py_creds_methods[] = {
 	{ "parse_string", py_creds_parse_string, METH_VARARGS,
 		"S.parse_string(text, obtained=CRED_SPECIFIED) -> None\n"
 		"Parse credentials string." },
+	{ "set_password_will_be_nt_hash",
+		py_cli_credentials_set_password_will_be_nt_hash, METH_VARARGS,
+		"S.set_password_will_be_nt_hash(bool) -> None\n"
+		"Alters the behaviour of S.set_password() "
+		"to expect the NTHASH as hexstring." },
 	{ "get_nt_hash", py_creds_get_nt_hash, METH_NOARGS,
 		NULL },
 	{ "get_kerberos_state", py_creds_get_kerberos_state, METH_NOARGS,
-- 
1.9.1


From ee09bdc5b5bbec8389ee8f9d017e3abb879366d5 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 15 Dec 2016 09:34:45 +0100
Subject: [PATCH 04/19] tests/credentials.py: add test for
 cli_credentials_set_password_will_be_nt_hash()

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 python/samba/tests/credentials.py | 16 ++++++++++++++--
 1 file changed, 14 insertions(+), 2 deletions(-)

diff --git a/python/samba/tests/credentials.py b/python/samba/tests/credentials.py
index a228327..c27edd77 100644
--- a/python/samba/tests/credentials.py
+++ b/python/samba/tests/credentials.py
@@ -24,6 +24,7 @@ the functionality, that's already done in other tests.
 from samba import credentials
 import samba.tests
 import os
+import binascii
 
 class CredentialsTests(samba.tests.TestCase):
 
@@ -95,8 +96,19 @@ class CredentialsTests(samba.tests.TestCase):
         self.assertEqual("myworksta", self.creds.get_workstation())
 
     def test_get_nt_hash(self):
-        self.creds.set_password("geheim")
-        self.assertEqual('\xc2\xae\x1f\xe6\xe6H\x84cRE>\x81o*\xeb\x93',
+        password="geheim"
+        hex_nthash="c2ae1fe6e648846352453e816f2aeb93"
+        self.creds.set_password(password)
+        self.assertEqual(password, self.creds.get_password())
+        self.assertEqual(binascii.a2b_hex(hex_nthash),
+                         self.creds.get_nt_hash())
+
+    def test_get_nt_hash_string(self):
+        self.creds.set_password_will_be_nt_hash(True)
+        hex_nthash="c2ae1fe6e648846352453e816f2aeb93"
+        self.creds.set_password(hex_nthash)
+        self.assertEqual(None, self.creds.get_password())
+        self.assertEqual(binascii.a2b_hex(hex_nthash),
                          self.creds.get_nt_hash())
 
     def test_set_cmdline_callbacks(self):
-- 
1.9.1


From f2f6c56df3786738f27dca1a44b9c0e91d7015b3 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 15 Dec 2016 09:42:20 +0100
Subject: [PATCH 05/19] tests/credentials.py: verify the difference of
 parse_string("someone") and parse_string("someone%")

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 python/samba/tests/credentials.py | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/python/samba/tests/credentials.py b/python/samba/tests/credentials.py
index c27edd77..ad3f663 100644
--- a/python/samba/tests/credentials.py
+++ b/python/samba/tests/credentials.py
@@ -70,6 +70,16 @@ class CredentialsTests(samba.tests.TestCase):
         self.assertEqual("", self.creds.get_username())
         self.assertEqual(None, self.creds.get_password())
 
+    def test_parse_string_empty_pw(self):
+        self.creds.parse_string("someone%")
+        self.assertEqual("someone", self.creds.get_username())
+        self.assertEqual("", self.creds.get_password())
+
+    def test_parse_string_none_pw(self):
+        self.creds.parse_string("someone")
+        self.assertEqual("someone", self.creds.get_username())
+        self.assertEqual(None, self.creds.get_password())
+
     def test_parse_string_user_pw_domain(self):
         self.creds.parse_string("dom\\someone%secr")
         self.assertEqual("someone", self.creds.get_username())
-- 
1.9.1


From f49f1b098e26512d811206597297de40e2676a07 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 15 Dec 2016 10:06:25 +0100
Subject: [PATCH 06/19] auth/credentials: add py_creds_parse_file()

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 auth/credentials/pycredentials.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/auth/credentials/pycredentials.c b/auth/credentials/pycredentials.c
index e0b7392..d38d0e3 100644
--- a/auth/credentials/pycredentials.c
+++ b/auth/credentials/pycredentials.c
@@ -299,6 +299,21 @@ static PyObject *py_creds_parse_string(PyObject *self, PyObject *args)
 	Py_RETURN_NONE;
 }
 
+static PyObject *py_creds_parse_file(PyObject *self, PyObject *args)
+{
+	char *newval;
+	enum credentials_obtained obt = CRED_SPECIFIED;
+	int _obt = obt;
+
+	if (!PyArg_ParseTuple(args, "s|i", &newval, &_obt)) {
+		return NULL;
+	}
+	obt = _obt;
+
+	cli_credentials_parse_file(PyCredentials_AsCliCredentials(self), 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);
@@ -579,6 +594,9 @@ static PyMethodDef py_creds_methods[] = {
 	{ "parse_string", py_creds_parse_string, METH_VARARGS,
 		"S.parse_string(text, obtained=CRED_SPECIFIED) -> None\n"
 		"Parse credentials string." },
+	{ "parse_file", py_creds_parse_file, METH_VARARGS,
+		"S.parse_file(filename, obtained=CRED_SPECIFIED) -> None\n"
+		"Parse credentials file." },
 	{ "set_password_will_be_nt_hash",
 		py_cli_credentials_set_password_will_be_nt_hash, METH_VARARGS,
 		"S.set_password_will_be_nt_hash(bool) -> None\n"
-- 
1.9.1


From 032f5892a2a50c874e0703996aad390bc2b209f3 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 15 Dec 2016 10:30:29 +0100
Subject: [PATCH 07/19] tests/credentials.py: add very simple test for
 py_creds_parse_file

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 python/samba/tests/credentials.py | 24 +++++++++++++++++++++++-
 1 file changed, 23 insertions(+), 1 deletion(-)

diff --git a/python/samba/tests/credentials.py b/python/samba/tests/credentials.py
index ad3f663..0a64179d 100644
--- a/python/samba/tests/credentials.py
+++ b/python/samba/tests/credentials.py
@@ -26,7 +26,7 @@ import samba.tests
 import os
 import binascii
 
-class CredentialsTests(samba.tests.TestCase):
+class CredentialsTests(samba.tests.TestCaseInTempDir):
 
     def setUp(self):
         super(CredentialsTests, self).setUp()
@@ -156,6 +156,28 @@ class CredentialsTests(samba.tests.TestCase):
         self.assertEqual(creds.is_anonymous(), True)
         self.assertEqual(creds.authentication_requested(), False)
 
+    def test_parse_file_1(self):
+        realm="realm.example.com"
+        domain="dom"
+        password="pass"
+        username="user"
+
+        passwd_file_name = os.path.join(self.tempdir, "parse_file")
+        passwd_file_fd = open(passwd_file_name, 'wx')
+        passwd_file_fd.write("realm=%s\n" % realm)
+        passwd_file_fd.write("domain=%s\n" % domain)
+        passwd_file_fd.write("username=%s\n" % username)
+        passwd_file_fd.write("password=%s\n" % password)
+        passwd_file_fd.close()
+        self.creds.parse_file(passwd_file_name)
+        self.assertEqual(self.creds.get_username(), username)
+        self.assertEqual(self.creds.get_password(), password)
+        self.assertEqual(self.creds.get_domain(), domain.upper())
+        self.assertEqual(self.creds.get_realm(), realm.upper())
+        self.assertEqual(self.creds.is_anonymous(), False)
+        self.assertEqual(self.creds.authentication_requested(), True)
+        os.unlink(passwd_file_name)
+
     def test_parse_username(self):
         creds = credentials.Credentials()
         lp = samba.tests.env_loadparm()
-- 
1.9.1


From 5df3b97248f64e4ff9d49dfdf95e5f454618efbd Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 15 Dec 2016 11:37:33 +0100
Subject: [PATCH 08/19] auth/credentials: add python bindings for enum
 credentials_obtained

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 auth/credentials/pycredentials.c | 27 +++++++++++++++++----------
 1 file changed, 17 insertions(+), 10 deletions(-)

diff --git a/auth/credentials/pycredentials.c b/auth/credentials/pycredentials.c
index d38d0e3..6da64a6 100644
--- a/auth/credentials/pycredentials.c
+++ b/auth/credentials/pycredentials.c
@@ -528,12 +528,12 @@ static PyMethodDef py_creds_methods[] = {
 	{ "get_username", py_creds_get_username, METH_NOARGS,
 		"S.get_username() -> username\nObtain username." },
 	{ "set_username", py_creds_set_username, METH_VARARGS,
-		"S.set_username(name, obtained=CRED_SPECIFIED) -> None\n"
+		"S.set_username(name[, credentials.SPECIFIED]) -> None\n"
 		"Change username." },
 	{ "get_principal", py_creds_get_principal, METH_NOARGS,
 		"S.get_principal() -> user at realm\nObtain user principal." },
 	{ "set_principal", py_creds_set_principal, METH_VARARGS,
-		"S.set_principal(name, obtained=CRED_SPECIFIED) -> None\n"
+		"S.set_principal(name[, credentials.SPECIFIED]) -> None\n"
 		"Change principal." },
 	{ "get_password", py_creds_get_password, METH_NOARGS,
 		"S.get_password() -> password\n"
@@ -542,31 +542,31 @@ static PyMethodDef py_creds_methods[] = {
 		"S.get_ntlm_username_domain() -> (domain, username)\n"
 		"Obtain NTLM username and domain, split up either as (DOMAIN, user) or (\"\", \"user at realm\")." },
 	{ "set_password", py_creds_set_password, METH_VARARGS,
-		"S.set_password(password, obtained=CRED_SPECIFIED) -> None\n"
+		"S.set_password(password[, credentials.SPECIFIED]) -> None\n"
 		"Change password." },
 	{ "set_utf16_password", py_creds_set_utf16_password, METH_VARARGS,
-		"S.set_utf16_password(password, obtained=CRED_SPECIFIED) -> None\n"
+		"S.set_utf16_password(password[, credentials.SPECIFIED]) -> None\n"
 		"Change password." },
 	{ "get_old_password", py_creds_get_old_password, METH_NOARGS,
 		"S.get_old_password() -> password\n"
 		"Obtain old password." },
 	{ "set_old_password", py_creds_set_old_password, METH_VARARGS,
-		"S.set_old_password(password, obtained=CRED_SPECIFIED) -> None\n"
+		"S.set_old_password(password[, credentials.SPECIFIED]) -> None\n"
 		"Change old password." },
 	{ "set_old_utf16_password", py_creds_set_old_utf16_password, METH_VARARGS,
-		"S.set_old_utf16_password(password, obtained=CRED_SPECIFIED) -> None\n"
+		"S.set_old_utf16_password(password[, credentials.SPECIFIED]) -> None\n"
 		"Change old password." },
 	{ "get_domain", py_creds_get_domain, METH_NOARGS,
 		"S.get_domain() -> domain\n"
 		"Obtain domain name." },
 	{ "set_domain", py_creds_set_domain, METH_VARARGS,
-		"S.set_domain(domain, obtained=CRED_SPECIFIED) -> None\n"
+		"S.set_domain(domain[, credentials.SPECIFIED]) -> None\n"
 		"Change domain name." },
 	{ "get_realm", py_creds_get_realm, METH_NOARGS,
 		"S.get_realm() -> realm\n"
 		"Obtain realm name." },
 	{ "set_realm", py_creds_set_realm, METH_VARARGS,
-		"S.set_realm(realm, obtained=CRED_SPECIFIED) -> None\n"
+		"S.set_realm(realm[, credentials.SPECIFIED]) -> None\n"
 		"Change realm name." },
 	{ "get_bind_dn", py_creds_get_bind_dn, METH_NOARGS,
 		"S.get_bind_dn() -> bind dn\n"
@@ -592,10 +592,10 @@ static PyMethodDef py_creds_methods[] = {
 		"S.set_cmdline_callbacks() -> bool\n"
 		"Use command-line to obtain credentials not explicitly set." },
 	{ "parse_string", py_creds_parse_string, METH_VARARGS,
-		"S.parse_string(text, obtained=CRED_SPECIFIED) -> None\n"
+		"S.parse_string(text[, credentials.SPECIFIED]) -> None\n"
 		"Parse credentials string." },
 	{ "parse_file", py_creds_parse_file, METH_VARARGS,
-		"S.parse_file(filename, obtained=CRED_SPECIFIED) -> None\n"
+		"S.parse_file(filename[, credentials.SPECIFIED]) -> None\n"
 		"Parse credentials file." },
 	{ "set_password_will_be_nt_hash",
 		py_cli_credentials_set_password_will_be_nt_hash, METH_VARARGS,
@@ -649,6 +649,13 @@ void initcredentials(void)
 	if (m == NULL)
 		return;
 
+	PyModule_AddObject(m, "UNINITIALISED", PyInt_FromLong(CRED_UNINITIALISED));
+	PyModule_AddObject(m, "CALLBACK", PyInt_FromLong(CRED_CALLBACK));
+	PyModule_AddObject(m, "GUESS_ENV", PyInt_FromLong(CRED_GUESS_ENV));
+	PyModule_AddObject(m, "GUESS_FILE", PyInt_FromLong(CRED_GUESS_FILE));
+	PyModule_AddObject(m, "CALLBACK_RESULT", PyInt_FromLong(CRED_CALLBACK_RESULT));
+	PyModule_AddObject(m, "SPECIFIED", PyInt_FromLong(CRED_SPECIFIED));
+
 	PyModule_AddObject(m, "AUTO_USE_KERBEROS", PyInt_FromLong(CRED_AUTO_USE_KERBEROS));
 	PyModule_AddObject(m, "DONT_USE_KERBEROS", PyInt_FromLong(CRED_DONT_USE_KERBEROS));
 	PyModule_AddObject(m, "MUST_USE_KERBEROS", PyInt_FromLong(CRED_MUST_USE_KERBEROS));
-- 
1.9.1


From d04c1d068d0fb1a2660b0f923e746f6c6a6abc0c Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 15 Dec 2016 11:04:02 +0100
Subject: [PATCH 09/19] auth/credentials: handle situations without a
 configured (default) realm

We should not have cli_credentials_get_realm() return "" without a
configured (default) realm in smb.conf.
Note that the existing tests with creds.get_realm() == lp.get("realm")
also work with "" as string.

At the same time we should never let cli_credentials_get_principal()
return "@REALM.EXAMPLE.COM" nor "username@".

If cli_credentials_parse_string() gets "OTHERDOMAIN\username"
we must not use cli_credentials_get_realm() to generate
a principal unless cli_credentials_get_domain() returns
also "OTHERDOMAIN". What we need to do is using
username at OTHERDOMAIN as principal, whild we still
use cli_credentials_get_realm to get a default kdc,
(which may route us to the correct kdc with WRONG_REALM
messages).

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 auth/credentials/credentials.c    | 43 +++++++++++++++++++++++++++++++--------
 python/samba/tests/credentials.py | 19 ++++++++++++-----
 2 files changed, 48 insertions(+), 14 deletions(-)

diff --git a/auth/credentials/credentials.c b/auth/credentials/credentials.c
index e024428..a0f91e9 100644
--- a/auth/credentials/credentials.c
+++ b/auth/credentials/credentials.c
@@ -213,16 +213,37 @@ _PUBLIC_ const char *cli_credentials_get_principal_and_obtained(struct cli_crede
 
 	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) {
-			*obtained = MIN(cred->domain_obtained, cred->username_obtained);
-			return talloc_asprintf(mem_ctx, "%s@%s", 
-					       cli_credentials_get_username(cred),
-					       cli_credentials_get_domain(cred));
+			effective_realm = cli_credentials_get_domain(cred);
+			effective_obtained = MIN(cred->domain_obtained,
+						 cred->username_obtained);
 		} else {
-			*obtained = MIN(cred->realm_obtained, cred->username_obtained);
+			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", 
-					       cli_credentials_get_username(cred),
-					       cli_credentials_get_realm(cred));
+					       effective_username,
+					       effective_realm);
 		}
 	}
 	*obtained = cred->principal_obtained;
@@ -816,6 +837,7 @@ _PUBLIC_ void cli_credentials_set_conf(struct cli_credentials *cred,
 			      struct loadparm_context *lp_ctx)
 {
 	const char *sep = NULL;
+	const char *realm = lpcfg_realm(lp_ctx);
 
 	cli_credentials_set_username(cred, "", CRED_UNINITIALISED);
 	if (lpcfg_parm_is_cmdline(lp_ctx, "workgroup")) {
@@ -828,10 +850,13 @@ _PUBLIC_ void cli_credentials_set_conf(struct cli_credentials *cred,
 	} else {
 		cli_credentials_set_workstation(cred, lpcfg_netbios_name(lp_ctx), CRED_UNINITIALISED);
 	}
+	if (realm != NULL && strlen(realm) == 0) {
+		realm = NULL;
+	}
 	if (lpcfg_parm_is_cmdline(lp_ctx, "realm")) {
-		cli_credentials_set_realm(cred, lpcfg_realm(lp_ctx), CRED_SPECIFIED);
+		cli_credentials_set_realm(cred, realm, CRED_SPECIFIED);
 	} else {
-		cli_credentials_set_realm(cred, lpcfg_realm(lp_ctx), CRED_UNINITIALISED);
+		cli_credentials_set_realm(cred, realm, CRED_UNINITIALISED);
 	}
 
 	sep = lpcfg_winbind_separator(lp_ctx);
diff --git a/python/samba/tests/credentials.py b/python/samba/tests/credentials.py
index 0a64179d..1cbd540 100644
--- a/python/samba/tests/credentials.py
+++ b/python/samba/tests/credentials.py
@@ -60,10 +60,12 @@ class CredentialsTests(samba.tests.TestCaseInTempDir):
     def test_set_domain(self):
         self.creds.set_domain("ABMAS")
         self.assertEqual("ABMAS", self.creds.get_domain())
+        self.assertEqual(self.creds.get_principal(), None)
 
     def test_set_realm(self):
         self.creds.set_realm("myrealm")
         self.assertEqual("MYREALM", self.creds.get_realm())
+        self.assertEqual(self.creds.get_principal(), None)
 
     def test_parse_string_anon(self):
         self.creds.parse_string("%")
@@ -140,7 +142,8 @@ class CredentialsTests(samba.tests.TestCaseInTempDir):
         creds.guess(lp)
         self.assertEqual(creds.get_username(), "env_user")
         self.assertEqual(creds.get_domain(), lp.get("workgroup").upper())
-        self.assertEqual(creds.get_realm(), lp.get("realm").upper())
+        self.assertEqual(creds.get_realm(), None)
+        self.assertEqual(creds.get_principal(), "env_user@%s" % creds.get_domain())
         self.assertEqual(creds.is_anonymous(), False)
         self.assertEqual(creds.authentication_requested(), False)
 
@@ -153,6 +156,7 @@ class CredentialsTests(samba.tests.TestCaseInTempDir):
         self.assertEqual(creds.get_username(), "")
         self.assertEqual(creds.get_domain(), "")
         self.assertEqual(creds.get_realm(), None)
+        self.assertEqual(creds.get_principal(), None)
         self.assertEqual(creds.is_anonymous(), True)
         self.assertEqual(creds.authentication_requested(), False)
 
@@ -186,7 +190,8 @@ class CredentialsTests(samba.tests.TestCaseInTempDir):
         creds.parse_string("user")
         self.assertEqual(creds.get_username(), "user")
         self.assertEqual(creds.get_domain(), lp.get("workgroup").upper())
-        self.assertEqual(creds.get_realm(), lp.get("realm").upper())
+        self.assertEqual(creds.get_realm(), None)
+        self.assertEqual(creds.get_principal(), "user@%s" % lp.get("workgroup").upper())
         self.assertEqual(creds.is_anonymous(), False)
         self.assertEqual(creds.authentication_requested(), True)
 
@@ -198,7 +203,8 @@ class CredentialsTests(samba.tests.TestCaseInTempDir):
         creds.parse_string("domain\user")
         self.assertEqual(creds.get_username(), "user")
         self.assertEqual(creds.get_domain(), "DOMAIN")
-        self.assertEqual(creds.get_realm(), lp.get("realm").upper())
+        self.assertEqual(creds.get_realm(), None)
+        self.assertEqual(creds.get_principal(), "user at DOMAIN")
         self.assertEqual(creds.is_anonymous(), False)
         self.assertEqual(creds.authentication_requested(), True)
 
@@ -211,6 +217,7 @@ class CredentialsTests(samba.tests.TestCaseInTempDir):
         self.assertEqual(creds.get_username(), "env_user")
         self.assertEqual(creds.get_domain(), lp.get("workgroup").upper())
         self.assertEqual(creds.get_realm(), "SAMBA.ORG")
+        self.assertEqual(creds.get_principal(), "user at samba.org")
         self.assertEqual(creds.is_anonymous(), False)
         self.assertEqual(creds.authentication_requested(), True)
 
@@ -223,7 +230,8 @@ class CredentialsTests(samba.tests.TestCaseInTempDir):
         self.assertEqual(creds.get_username(), "user")
         self.assertEqual(creds.get_password(), "pass")
         self.assertEqual(creds.get_domain(), lp.get("workgroup"))
-        self.assertEqual(creds.get_realm(), lp.get("realm"))
+        self.assertEqual(creds.get_realm(), None)
+        self.assertEqual(creds.get_principal(), "user@%s" % lp.get("workgroup"))
         self.assertEqual(creds.is_anonymous(), False)
         self.assertEqual(creds.authentication_requested(), True)
 
@@ -236,7 +244,8 @@ class CredentialsTests(samba.tests.TestCaseInTempDir):
         self.assertEqual(creds.get_username(), "user")
         self.assertEqual(creds.get_domain(), "DOMAIN")
         self.assertEqual(creds.get_password(), "pass")
-        self.assertEqual(creds.get_realm(), lp.get("realm"))
+        self.assertEqual(creds.get_realm(), None)
+        self.assertEqual(creds.get_principal(), "user at DOMAIN")
         self.assertEqual(creds.is_anonymous(), False)
         self.assertEqual(creds.authentication_requested(), True)
 
-- 
1.9.1


From 83b90d229751c482eb97a5f13dc373d9cd774460 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 15 Dec 2016 14:49:18 +0100
Subject: [PATCH 10/19] tests/credentials.py: add tests with a realm from
 smb.conf

As we don't want to create a new smb.conf file
we just simulate it with "creds.set_realm(realm, credentials.UNINITIALISED)".

That's basically the same as the cli_credentials_set_conf() behaviour
if a realm is specified in the configuration.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 python/samba/tests/credentials.py | 17 ++++++++++++++++-
 1 file changed, 16 insertions(+), 1 deletion(-)

diff --git a/python/samba/tests/credentials.py b/python/samba/tests/credentials.py
index 1cbd540..8bfab7a 100644
--- a/python/samba/tests/credentials.py
+++ b/python/samba/tests/credentials.py
@@ -182,7 +182,7 @@ class CredentialsTests(samba.tests.TestCaseInTempDir):
         self.assertEqual(self.creds.authentication_requested(), True)
         os.unlink(passwd_file_name)
 
-    def test_parse_username(self):
+    def test_parse_username_0(self):
         creds = credentials.Credentials()
         lp = samba.tests.env_loadparm()
         os.environ["USER"] = "env_user"
@@ -195,6 +195,21 @@ class CredentialsTests(samba.tests.TestCaseInTempDir):
         self.assertEqual(creds.is_anonymous(), False)
         self.assertEqual(creds.authentication_requested(), True)
 
+    def test_parse_username_1(self):
+        creds = credentials.Credentials()
+        lp = samba.tests.env_loadparm()
+        os.environ["USER"] = "env_user"
+        creds.guess(lp)
+        realm = "realm.example.com"
+        creds.set_realm(realm, credentials.UNINITIALISED)
+        creds.parse_string("user")
+        self.assertEqual(creds.get_username(), "user")
+        self.assertEqual(creds.get_domain(), lp.get("workgroup").upper())
+        self.assertEqual(creds.get_realm(), realm.upper())
+        self.assertEqual(creds.get_principal(), "user@%s" % realm.upper())
+        self.assertEqual(creds.is_anonymous(), False)
+        self.assertEqual(creds.authentication_requested(), True)
+
     def test_parse_username_with_domain(self):
         creds = credentials.Credentials()
         lp = samba.tests.env_loadparm()
-- 
1.9.1


From 5ac8f0af300b9c1ffd29f5879360201769320dee Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Fri, 9 Dec 2016 12:20:19 +0100
Subject: [PATCH 11/19] auth/credentials: let cli_credentials_parse_string()
 always reset username and domain

If cli_credentials_parse_string() is used we should no longer use
any guessed values and need to make sure username and domain
are reset if principal and realm are set.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 auth/credentials/credentials.c    | 8 ++++++++
 python/samba/tests/credentials.py | 8 ++++----
 2 files changed, 12 insertions(+), 4 deletions(-)

diff --git a/auth/credentials/credentials.c b/auth/credentials/credentials.c
index a0f91e9..cca772d 100644
--- a/auth/credentials/credentials.c
+++ b/auth/credentials/credentials.c
@@ -781,6 +781,14 @@ _PUBLIC_ void cli_credentials_parse_string(struct cli_credentials *credentials,
 	}
 
 	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);
diff --git a/python/samba/tests/credentials.py b/python/samba/tests/credentials.py
index 8bfab7a..82a8ad9 100644
--- a/python/samba/tests/credentials.py
+++ b/python/samba/tests/credentials.py
@@ -229,8 +229,8 @@ class CredentialsTests(samba.tests.TestCaseInTempDir):
         os.environ["USER"] = "env_user"
         creds.guess(lp)
         creds.parse_string("user at samba.org")
-        self.assertEqual(creds.get_username(), "env_user")
-        self.assertEqual(creds.get_domain(), lp.get("workgroup").upper())
+        self.assertEqual(creds.get_username(), "user at samba.org")
+        self.assertEqual(creds.get_domain(), "")
         self.assertEqual(creds.get_realm(), "SAMBA.ORG")
         self.assertEqual(creds.get_principal(), "user at samba.org")
         self.assertEqual(creds.is_anonymous(), False)
@@ -270,8 +270,8 @@ class CredentialsTests(samba.tests.TestCaseInTempDir):
         os.environ["USER"] = "env_user"
         creds.guess(lp)
         creds.parse_string("user at samba.org%pass")
-        self.assertEqual(creds.get_username(), "env_user")
-        self.assertEqual(creds.get_domain(), lp.get("workgroup").upper())
+        self.assertEqual(creds.get_username(), "user at samba.org")
+        self.assertEqual(creds.get_domain(), "")
         self.assertEqual(creds.get_password(), "pass")
         self.assertEqual(creds.get_realm(), "SAMBA.ORG")
         self.assertEqual(creds.get_principal(), "user at samba.org")
-- 
1.9.1


From 7c9ffb1af631076818e36c2487071c48571c6746 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Wed, 14 Dec 2016 16:47:57 +0100
Subject: [PATCH 12/19] auth/credentials: let cli_credentials_parse_string()
 always reset principal and realm

If we reset username we need to reset principal if it was set at the same level.

If domain is reset we also need to use it as realm if realm
was set at the same level. Otherwise we'd build a principal
that belongs to a different user, which would not work
and only increment the wrong lockout counter and result
in wrong authorization tokens to be used.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 auth/credentials/credentials.c | 33 ++++++++++++++++++++++++++++++++-
 1 file changed, 32 insertions(+), 1 deletion(-)

diff --git a/auth/credentials/credentials.c b/auth/credentials/credentials.c
index cca772d..14c8403 100644
--- a/auth/credentials/credentials.c
+++ b/auth/credentials/credentials.c
@@ -797,9 +797,40 @@ _PUBLIC_ void cli_credentials_parse_string(struct cli_credentials *credentials,
 		   || (p = strchr_m(uname, '/'))
 		   || (p = strchr_m(uname, credentials->winbind_separator)))
 	{
+		const char *domain = NULL;
+
+		domain = uname;
 		*p = 0;
-		cli_credentials_set_domain(credentials, uname, obtained);
 		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);
 }
-- 
1.9.1


From 7d11a14abd717064583ecb3e8f6ee8a2906cf61a Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 15 Dec 2016 14:12:31 +0100
Subject: [PATCH 13/19] tests/credentials.py: add tests to verify
 realm/principal behaviour of cli_credentials_parse_string()

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 python/samba/tests/credentials.py | 50 ++++++++++++++++++++++++++++++++++++++-
 1 file changed, 49 insertions(+), 1 deletion(-)

diff --git a/python/samba/tests/credentials.py b/python/samba/tests/credentials.py
index 82a8ad9..d13d4df 100644
--- a/python/samba/tests/credentials.py
+++ b/python/samba/tests/credentials.py
@@ -210,7 +210,7 @@ class CredentialsTests(samba.tests.TestCaseInTempDir):
         self.assertEqual(creds.is_anonymous(), False)
         self.assertEqual(creds.authentication_requested(), True)
 
-    def test_parse_username_with_domain(self):
+    def test_parse_username_with_domain_0(self):
         creds = credentials.Credentials()
         lp = samba.tests.env_loadparm()
         os.environ["USER"] = "env_user"
@@ -223,6 +223,54 @@ class CredentialsTests(samba.tests.TestCaseInTempDir):
         self.assertEqual(creds.is_anonymous(), False)
         self.assertEqual(creds.authentication_requested(), True)
 
+    def test_parse_username_with_domain_1(self):
+        creds = credentials.Credentials()
+        lp = samba.tests.env_loadparm()
+        os.environ["USER"] = "env_user"
+        creds.guess(lp)
+        realm = "realm.example.com"
+        creds.set_realm(realm, credentials.UNINITIALISED)
+        self.assertEqual(creds.get_username(), "env_user")
+        self.assertEqual(creds.get_domain(), lp.get("workgroup").upper())
+        self.assertEqual(creds.get_realm(), realm.upper())
+        self.assertEqual(creds.get_principal(), "env_user@%s" % realm.upper())
+        creds.set_principal("unknown at realm.example.com")
+        self.assertEqual(creds.get_username(), "env_user")
+        self.assertEqual(creds.get_domain(), lp.get("workgroup").upper())
+        self.assertEqual(creds.get_realm(), realm.upper())
+        self.assertEqual(creds.get_principal(), "unknown at realm.example.com")
+        creds.parse_string("domain\user")
+        self.assertEqual(creds.get_username(), "user")
+        self.assertEqual(creds.get_domain(), "DOMAIN")
+        self.assertEqual(creds.get_realm(), realm.upper())
+        self.assertEqual(creds.get_principal(), "user at DOMAIN")
+        self.assertEqual(creds.is_anonymous(), False)
+        self.assertEqual(creds.authentication_requested(), True)
+
+    def test_parse_username_with_domain_2(self):
+        creds = credentials.Credentials()
+        lp = samba.tests.env_loadparm()
+        os.environ["USER"] = "env_user"
+        creds.guess(lp)
+        realm = "realm.example.com"
+        creds.set_realm(realm, credentials.SPECIFIED)
+        self.assertEqual(creds.get_username(), "env_user")
+        self.assertEqual(creds.get_domain(), lp.get("workgroup").upper())
+        self.assertEqual(creds.get_realm(), realm.upper())
+        self.assertEqual(creds.get_principal(), "env_user@%s" % realm.upper())
+        creds.set_principal("unknown at realm.example.com")
+        self.assertEqual(creds.get_username(), "env_user")
+        self.assertEqual(creds.get_domain(), lp.get("workgroup").upper())
+        self.assertEqual(creds.get_realm(), realm.upper())
+        self.assertEqual(creds.get_principal(), "unknown at realm.example.com")
+        creds.parse_string("domain\user")
+        self.assertEqual(creds.get_username(), "user")
+        self.assertEqual(creds.get_domain(), "DOMAIN")
+        self.assertEqual(creds.get_realm(), "DOMAIN")
+        self.assertEqual(creds.get_principal(), "user at DOMAIN")
+        self.assertEqual(creds.is_anonymous(), False)
+        self.assertEqual(creds.authentication_requested(), True)
+
     def test_parse_username_with_realm(self):
         creds = credentials.Credentials()
         lp = samba.tests.env_loadparm()
-- 
1.9.1


From d25ba3bfd27d8aa6f2e5a27a789aecc305e563e3 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Sun, 11 Dec 2016 22:50:53 +0100
Subject: [PATCH 14/19] auth/credentials: let cli_credentials_parse_file()
 handle 'username' with cli_credentials_parse_string()

Some existing source3 tests (test_smbclient_s3.sh test_auth_file()) use a credentials file
that looks like this:

  username=DOMAIN/username
  password=password
  domain=DOMAIN

This change allows us to parse the same.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 auth/credentials/credentials.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/auth/credentials/credentials.c b/auth/credentials/credentials.c
index 14c8403..0ffcc5c 100644
--- a/auth/credentials/credentials.c
+++ b/auth/credentials/credentials.c
@@ -1149,7 +1149,7 @@ _PUBLIC_ bool cli_credentials_parse_file(struct cli_credentials *cred, const cha
 		if (strwicmp("password", param) == 0) {
 			cli_credentials_set_password(cred, val, obtained);
 		} else if (strwicmp("username", param) == 0) {
-			cli_credentials_set_username(cred, val, obtained);
+			cli_credentials_parse_string(cred, val, obtained);
 		} else if (strwicmp("domain", param) == 0) {
 			cli_credentials_set_domain(cred, val, obtained);
 		} else if (strwicmp("realm", param) == 0) {
-- 
1.9.1


From 8135289a1de46452a30148cd806d89c51384a84e Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 15 Dec 2016 14:01:35 +0100
Subject: [PATCH 15/19] tests/credentials.py: verify the new
 cli_credentials_parse_file() 'username' parsing

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 python/samba/tests/credentials.py | 49 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 49 insertions(+)

diff --git a/python/samba/tests/credentials.py b/python/samba/tests/credentials.py
index d13d4df..7aaa711 100644
--- a/python/samba/tests/credentials.py
+++ b/python/samba/tests/credentials.py
@@ -178,6 +178,55 @@ class CredentialsTests(samba.tests.TestCaseInTempDir):
         self.assertEqual(self.creds.get_password(), password)
         self.assertEqual(self.creds.get_domain(), domain.upper())
         self.assertEqual(self.creds.get_realm(), realm.upper())
+        self.assertEqual(self.creds.get_principal(), "%s@%s" % (username, realm.upper()))
+        self.assertEqual(self.creds.is_anonymous(), False)
+        self.assertEqual(self.creds.authentication_requested(), True)
+        os.unlink(passwd_file_name)
+
+    def test_parse_file_2(self):
+        realm="realm.example.com"
+        domain="dom"
+        password="pass"
+        username="user"
+
+        passwd_file_name = os.path.join(self.tempdir, "parse_file")
+        passwd_file_fd = open(passwd_file_name, 'wx')
+        passwd_file_fd.write("realm=%s\n" % realm)
+        passwd_file_fd.write("domain=%s\n" % domain)
+        passwd_file_fd.write("username=%s\\%s\n" % (domain, username))
+        passwd_file_fd.write("password=%s\n" % password)
+        passwd_file_fd.close()
+        self.creds.parse_file(passwd_file_name)
+        self.assertEqual(self.creds.get_username(), username)
+        self.assertEqual(self.creds.get_password(), password)
+        self.assertEqual(self.creds.get_domain(), domain.upper())
+        self.assertEqual(self.creds.get_realm(), realm.upper())
+        self.assertEqual(self.creds.get_principal(), "%s@%s" % (username, realm.upper()))
+        self.assertEqual(self.creds.is_anonymous(), False)
+        self.assertEqual(self.creds.authentication_requested(), True)
+        os.unlink(passwd_file_name)
+
+    def test_parse_file_3(self):
+        realm="realm.example.com"
+        domain="domain"
+        password="password"
+        username="username"
+
+        userdom="userdom"
+
+        passwd_file_name = os.path.join(self.tempdir, "parse_file")
+        passwd_file_fd = open(passwd_file_name, 'wx')
+        passwd_file_fd.write("realm=%s\n" % realm)
+        passwd_file_fd.write("domain=%s\n" % domain)
+        passwd_file_fd.write("username=%s/%s\n" % (userdom, username))
+        passwd_file_fd.write("password=%s\n" % password)
+        passwd_file_fd.close()
+        self.creds.parse_file(passwd_file_name)
+        self.assertEqual(self.creds.get_username(), username)
+        self.assertEqual(self.creds.get_password(), password)
+        self.assertEqual(self.creds.get_domain(), userdom.upper())
+        self.assertEqual(self.creds.get_realm(), userdom.upper())
+        self.assertEqual(self.creds.get_principal(), "%s@%s" % (username, userdom.upper()))
         self.assertEqual(self.creds.is_anonymous(), False)
         self.assertEqual(self.creds.authentication_requested(), True)
         os.unlink(passwd_file_name)
-- 
1.9.1


From 0fd1c02619d1e658c60da3a9a58dc9cfd95c5f12 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 15 Dec 2016 12:41:58 +0100
Subject: [PATCH 16/19] auth/credentials: change the parsing order of
 cli_credentials_parse_file()

We now first just remember the domain, realm, username, password values
(the last value wins).

At the end we call cli_credentials_set_{realm,domain,password}()
followed by cli_credentials_parse_string() for 'username'.

It means the last 'username' line beats the domain, realm or password lines, e.g.:

 username=USERDOMAIN\username
 domain=DOMAIN

will result in cli_credentials_get_domain() returning "USERDOMAIN" instead of
DOMAIN.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 auth/credentials/credentials.c | 54 ++++++++++++++++++++++++++++++++++++++----
 1 file changed, 49 insertions(+), 5 deletions(-)

diff --git a/auth/credentials/credentials.c b/auth/credentials/credentials.c
index 0ffcc5c..9a935c6 100644
--- a/auth/credentials/credentials.c
+++ b/auth/credentials/credentials.c
@@ -1117,6 +1117,10 @@ _PUBLIC_ bool cli_credentials_parse_file(struct cli_credentials *cred, const cha
 	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);
 
@@ -1147,17 +1151,57 @@ _PUBLIC_ bool cli_credentials_parse_file(struct cli_credentials *cred, const cha
 			val++;
 
 		if (strwicmp("password", param) == 0) {
-			cli_credentials_set_password(cred, val, obtained);
+			password = val;
 		} else if (strwicmp("username", param) == 0) {
-			cli_credentials_parse_string(cred, val, obtained);
+			username = val;
 		} else if (strwicmp("domain", param) == 0) {
-			cli_credentials_set_domain(cred, val, obtained);
+			domain = val;
 		} else if (strwicmp("realm", param) == 0) {
-			cli_credentials_set_realm(cred, val, obtained);
+			realm = val;
 		}
-		memset(lines[i], 0, len);
+
+		/*
+		 * 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;
-- 
1.9.1


From 2963d08a2b2204ca41115c231c8ab39659879eda Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 15 Dec 2016 15:30:28 +0100
Subject: [PATCH 17/19] tests/credentials.py: demonstrate the last 'username'
 line of creds.parse_file() beats other lines

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 python/samba/tests/credentials.py | 50 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 50 insertions(+)

diff --git a/python/samba/tests/credentials.py b/python/samba/tests/credentials.py
index 7aaa711..4e5746e 100644
--- a/python/samba/tests/credentials.py
+++ b/python/samba/tests/credentials.py
@@ -231,6 +231,56 @@ class CredentialsTests(samba.tests.TestCaseInTempDir):
         self.assertEqual(self.creds.authentication_requested(), True)
         os.unlink(passwd_file_name)
 
+    def test_parse_file_4(self):
+        realm="realm.example.com"
+        domain="domain"
+        password="password"
+        username="username"
+
+        userdom="userdom"
+
+        passwd_file_name = os.path.join(self.tempdir, "parse_file")
+        passwd_file_fd = open(passwd_file_name, 'wx')
+        passwd_file_fd.write("username=%s\\%s%%%s\n" % (userdom, username, password))
+        passwd_file_fd.write("realm=ignorerealm\n")
+        passwd_file_fd.write("domain=ignoredomain\n")
+        passwd_file_fd.write("password=ignorepassword\n")
+        passwd_file_fd.close()
+        self.creds.parse_file(passwd_file_name)
+        self.assertEqual(self.creds.get_username(), username)
+        self.assertEqual(self.creds.get_password(), password)
+        self.assertEqual(self.creds.get_domain(), userdom.upper())
+        self.assertEqual(self.creds.get_realm(), userdom.upper())
+        self.assertEqual(self.creds.get_principal(), "%s@%s" % (username, userdom.upper()))
+        self.assertEqual(self.creds.is_anonymous(), False)
+        self.assertEqual(self.creds.authentication_requested(), True)
+        os.unlink(passwd_file_name)
+
+    def test_parse_file_5(self):
+        realm="realm.example.com"
+        domain="domain"
+        password="password"
+        username="username"
+
+        userdom="userdom"
+
+        passwd_file_name = os.path.join(self.tempdir, "parse_file")
+        passwd_file_fd = open(passwd_file_name, 'wx')
+        passwd_file_fd.write("realm=ignorerealm\n")
+        passwd_file_fd.write("username=%s\\%s%%%s\n" % (userdom, username, password))
+        passwd_file_fd.write("domain=ignoredomain\n")
+        passwd_file_fd.write("password=ignorepassword\n")
+        passwd_file_fd.close()
+        self.creds.parse_file(passwd_file_name)
+        self.assertEqual(self.creds.get_username(), username)
+        self.assertEqual(self.creds.get_password(), password)
+        self.assertEqual(self.creds.get_domain(), userdom.upper())
+        self.assertEqual(self.creds.get_realm(), userdom.upper())
+        self.assertEqual(self.creds.get_principal(), "%s@%s" % (username, userdom.upper()))
+        self.assertEqual(self.creds.is_anonymous(), False)
+        self.assertEqual(self.creds.authentication_requested(), True)
+        os.unlink(passwd_file_name)
+
     def test_parse_username_0(self):
         creds = credentials.Credentials()
         lp = samba.tests.env_loadparm()
-- 
1.9.1


From d99e2522a437d61dd59daadbb30892dc67e2aca7 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Fri, 9 Dec 2016 16:04:38 +0100
Subject: [PATCH 18/19] s3:popt_common: let POPT_COMMON_CREDENTIALS imply
 logfile and conffile loading

All users of POPT_COMMON_CREDENTIALS basically need the same logic,
while some ignore a broken smb.conf and some complain about it.

This will allow the future usage of config options in the
credential post processing.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source3/client/client.c       | 11 +----------
 source3/include/popt_common.h |  1 +
 source3/lib/popt_common.c     | 25 +++++++++++++++++++++++++
 source3/rpcclient/rpcclient.c | 15 ---------------
 source3/utils/regedit.c       |  5 -----
 source3/utils/smbcacls.c      |  5 ++---
 source3/utils/smbcquotas.c    | 12 ------------
 source3/utils/smbtree.c       |  5 ++---
 8 files changed, 31 insertions(+), 48 deletions(-)

diff --git a/source3/client/client.c b/source3/client/client.c
index 82521288..cde9776 100644
--- a/source3/client/client.c
+++ b/source3/client/client.c
@@ -5765,6 +5765,7 @@ int main(int argc,char *argv[])
 
 	lp_set_cmdline("log level", "1");
 
+	popt_common_credentials_set_ignore_missing_conf();
 	popt_common_credentials_set_delay_post();
 
 	/* skip argv(0) */
@@ -5901,16 +5902,6 @@ int main(int argc,char *argv[])
 					       poptGetArg(pc));
 	}
 
-	if ( override_logfile )
-		setup_logging( lp_logfile(talloc_tos()), DEBUG_FILE );
-
-	if (!lp_load_client(get_dyn_CONFIGFILE())) {
-		fprintf(stderr, "%s: Can't load %s - run testparm to debug it\n",
-			argv[0], get_dyn_CONFIGFILE());
-	}
-
-	load_interfaces();
-
 	if (service_opt && service) {
 		size_t len;
 
diff --git a/source3/include/popt_common.h b/source3/include/popt_common.h
index ec7568c..2abfa04 100644
--- a/source3/include/popt_common.h
+++ b/source3/include/popt_common.h
@@ -50,6 +50,7 @@ extern const struct poptOption popt_common_dynconfig[];
 
 extern struct user_auth_info *cmdline_auth_info;
 
+void popt_common_credentials_set_ignore_missing_conf(void);
 void popt_common_credentials_set_delay_post(void);
 void popt_common_credentials_post(void);
 void popt_burn_cmdline_password(int argc, char *argv[]);
diff --git a/source3/lib/popt_common.c b/source3/lib/popt_common.c
index b2fd39c..e229ad2 100644
--- a/source3/lib/popt_common.c
+++ b/source3/lib/popt_common.c
@@ -348,8 +348,14 @@ static void get_credentials_file(struct user_auth_info *auth_info,
  */
 
 struct user_auth_info *cmdline_auth_info;
+static bool popt_common_credentials_ignore_missing_conf;
 static bool popt_common_credentials_delay_post;
 
+void popt_common_credentials_set_ignore_missing_conf(void)
+{
+	popt_common_credentials_delay_post = true;
+}
+
 void popt_common_credentials_set_delay_post(void)
 {
 	popt_common_credentials_delay_post = true;
@@ -412,6 +418,25 @@ static void popt_common_credentials_callback(poptContext con,
 	}
 
 	if (reason == POPT_CALLBACK_REASON_POST) {
+		bool ok;
+
+		if (override_logfile) {
+			setup_logging(lp_logfile(talloc_tos()), DEBUG_FILE );
+		}
+
+		ok = lp_load_client(get_dyn_CONFIGFILE());
+		if (!ok) {
+			const char *pname = poptGetInvocationName(con);
+
+			fprintf(stderr, "%s: Can't load %s - run testparm to debug it\n",
+				pname, get_dyn_CONFIGFILE());
+			if (!popt_common_credentials_ignore_missing_conf) {
+				exit(1);
+			}
+		}
+
+		load_interfaces();
+
 		if (popt_common_credentials_delay_post) {
 			return;
 		}
diff --git a/source3/rpcclient/rpcclient.c b/source3/rpcclient/rpcclient.c
index 138fdba..9faf01b 100644
--- a/source3/rpcclient/rpcclient.c
+++ b/source3/rpcclient/rpcclient.c
@@ -949,7 +949,6 @@ out_free:
 	char *user, *domain, *q;
 	const char *host;
 	int signing_state = SMB_SIGNING_IPC_DEFAULT;
-	bool ok;
 
 	/* make sure the vars that get altered (4th field) are in
 	   a fixed location or certain compilers complain */
@@ -1016,17 +1015,6 @@ out_free:
 	popt_burn_cmdline_password(argc, argv);
 	rpcclient_auth_info = cmdline_auth_info;
 
-	/* Load smb.conf file */
-
-	ok = lp_load_global(get_dyn_CONFIGFILE());
-	if (!ok) {
-		fprintf(stderr,
-			"Can't load %s - run testparm to debug it\n",
-			get_dyn_CONFIGFILE());
-		result = 1;
-		goto done;
-	}
-
 	nt_status = messaging_init_client(talloc_autofree_context(),
 					  samba_tevent_context_init(talloc_autofree_context()),
 					  &rpcclient_msg_ctx);
@@ -1045,9 +1033,6 @@ out_free:
 		goto done;
 	}
 
-	/* We must load interfaces after we load the smb.conf */
-	load_interfaces();
-
 	if (!init_names()) {
 		result = 1;
 		goto done;
diff --git a/source3/utils/regedit.c b/source3/utils/regedit.c
index 476897f..14e75c2 100644
--- a/source3/utils/regedit.c
+++ b/source3/utils/regedit.c
@@ -781,11 +781,6 @@ int main(int argc, const char **argv)
 		/* TODO */
 	}
 
-	if (!lp_load_global(get_dyn_CONFIGFILE())) {
-		DEBUG(0, ("ERROR loading config file...\n"));
-		exit(1);
-	}
-
 	rv = reg_open_samba3(frame, &ctx);
 	if (!W_ERROR_IS_OK(rv)) {
 		fprintf(stderr, "Unable to open registry: %s\n",
diff --git a/source3/utils/smbcacls.c b/source3/utils/smbcacls.c
index 7705eb4..255ff97 100644
--- a/source3/utils/smbcacls.c
+++ b/source3/utils/smbcacls.c
@@ -831,6 +831,8 @@ int main(int argc, char *argv[])
 
 	setlinebuf(stdout);
 
+	popt_common_credentials_set_ignore_missing_conf();
+
 	pc = poptGetContext("smbcacls", argc, argv_const, long_options, 0);
 
 	poptSetOtherOptionHelp(pc, "//server1/share1 filename\nACLs look like: "
@@ -894,9 +896,6 @@ int main(int argc, char *argv[])
 		return -1;
 	}
 
-	lp_load_global(get_dyn_CONFIGFILE());
-	load_interfaces();
-
 	filename = talloc_strdup(frame, poptGetArg(pc));
 	if (!filename) {
 		return -1;
diff --git a/source3/utils/smbcquotas.c b/source3/utils/smbcquotas.c
index ba80926..a1cf70a1 100644
--- a/source3/utils/smbcquotas.c
+++ b/source3/utils/smbcquotas.c
@@ -575,7 +575,6 @@ int main(int argc, char *argv[])
 	static bool test_args = False;
 	struct cli_state *cli;
 	bool fix_user = False;
-	bool ok;
 	SMB_NTQUOTA_STRUCT qt;
 	TALLOC_CTX *frame = talloc_stackframe();
 	poptContext pc;
@@ -700,17 +699,6 @@ FSQFLAGS:QUOTA_ENABLED/DENY_DISK/LOG_SOFTLIMIT/LOG_HARD_LIMIT", "SETSTRING" },
 	poptFreeContext(pc);
 	popt_burn_cmdline_password(argc, argv);
 
-	ok = lp_load_global(get_dyn_CONFIGFILE());
-	if (!ok) {
-		DBG_ERR("ERROR: Loading config file %s - "
-			"run testparm to debug it\n",
-			get_dyn_CONFIGFILE());
-		exit(EXIT_PARSE_ERROR);
-	}
-
-	/* We must load interfaces after we load the smb.conf */
-	load_interfaces();
-
 	string_replace(path, '/', '\\');
 
 	server = SMB_STRDUP(path+2);
diff --git a/source3/utils/smbtree.c b/source3/utils/smbtree.c
index 2f11dce..ba7b318 100644
--- a/source3/utils/smbtree.c
+++ b/source3/utils/smbtree.c
@@ -307,15 +307,14 @@ int main(int argc, char *argv[])
 
 	setup_logging(argv[0], DEBUG_STDERR);
 
+	popt_common_credentials_set_ignore_missing_conf();
+
 	pc = poptGetContext("smbtree", argc, argv_const, long_options,
 			    POPT_CONTEXT_KEEP_FIRST);
 	while(poptGetNextOpt(pc) != -1);
 	poptFreeContext(pc);
 	popt_burn_cmdline_password(argc, argv);
 
-	lp_load_global(get_dyn_CONFIGFILE());
-	load_interfaces();
-
 	/* Now do our stuff */
 
         if (!print_tree(cmdline_auth_info)) {
-- 
1.9.1


From 81d5ed9470dceee6e7b1d8f56aeb99de9317b045 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Fri, 28 Oct 2016 12:14:37 +0200
Subject: [PATCH 19/19] s3:user_auth_info: let struct user_auth_info use struct
 cli_credentials internally

This way we can have a very simple get_cmdline_auth_info_creds() function,
which can be used pass cli_credentials down the stack instead of
constantly translating from user_auth_info to cli_credentials, while
loosing information.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source3/include/auth_info.h |   6 +
 source3/lib/popt_common.c   | 182 +----------------------
 source3/lib/util_cmdline.c  | 346 +++++++++++++++++++++++++++++---------------
 3 files changed, 245 insertions(+), 289 deletions(-)

diff --git a/source3/include/auth_info.h b/source3/include/auth_info.h
index 04ac422..c6f71ad 100644
--- a/source3/include/auth_info.h
+++ b/source3/include/auth_info.h
@@ -23,6 +23,9 @@
 struct user_auth_info;
 
 struct user_auth_info *user_auth_info_init(TALLOC_CTX *mem_ctx);
+void set_cmdline_auth_info_guess(struct user_auth_info *auth_info);
+void set_cmdline_auth_info_from_file(struct user_auth_info *auth_info,
+				     const char *filename);
 const char *get_cmdline_auth_info_username(const struct user_auth_info *auth_info);
 void set_cmdline_auth_info_username(struct user_auth_info *auth_info,
 				    const char *username);
@@ -59,4 +62,7 @@ bool get_cmdline_auth_info_use_machine_account(const struct user_auth_info *auth
 bool set_cmdline_auth_info_machine_account_creds(struct user_auth_info *auth_info);
 void set_cmdline_auth_info_getpass(struct user_auth_info *auth_info);
 
+struct cli_credentials *get_cmdline_auth_info_creds(
+	const struct user_auth_info *auth_info);
+
 #endif /* _AUTH_INFO_H */
diff --git a/source3/lib/popt_common.c b/source3/lib/popt_common.c
index e229ad2..3589a4f 100644
--- a/source3/lib/popt_common.c
+++ b/source3/lib/popt_common.c
@@ -21,7 +21,6 @@
 */
 
 #include "includes.h"
-#include "system/filesys.h"
 #include "popt_common.h"
 #include "lib/param/param.h"
 
@@ -211,131 +210,6 @@ struct poptOption popt_common_option[] = {
 	POPT_TABLEEND
 };
 
-/****************************************************************************
- * get a password from a a file or file descriptor
- * exit on failure
- * ****************************************************************************/
-
-static void get_password_file(struct user_auth_info *auth_info)
-{
-	int fd = -1;
-	char *p;
-	bool close_it = False;
-	char *spec = NULL;
-	char pass[128];
-
-	if ((p = getenv("PASSWD_FD")) != NULL) {
-		if (asprintf(&spec, "descriptor %s", p) < 0) {
-			return;
-		}
-		sscanf(p, "%d", &fd);
-		close_it = false;
-	} else if ((p = getenv("PASSWD_FILE")) != NULL) {
-		fd = open(p, O_RDONLY, 0);
-		spec = SMB_STRDUP(p);
-		if (fd < 0) {
-			fprintf(stderr, "Error opening PASSWD_FILE %s: %s\n",
-					spec, strerror(errno));
-			exit(1);
-		}
-		close_it = True;
-	}
-
-	if (fd < 0) {
-		fprintf(stderr, "fd = %d, < 0\n", fd);
-		exit(1);
-	}
-
-	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;
-			}
-		case 0:
-			if (p - pass) {
-				*p = '\0'; /* null-terminate it, just in case... */
-				p = NULL; /* then force the loop condition to become false */
-				break;
-			} else {
-				fprintf(stderr, "Error reading password from file %s: %s\n",
-						spec, "empty password\n");
-				SAFE_FREE(spec);
-				exit(1);
-			}
-
-		default:
-			fprintf(stderr, "Error reading password from file %s: %s\n",
-					spec, strerror(errno));
-			SAFE_FREE(spec);
-			exit(1);
-		}
-	}
-	SAFE_FREE(spec);
-
-	set_cmdline_auth_info_password(auth_info, pass);
-	if (close_it) {
-		close(fd);
-	}
-}
-
-static void get_credentials_file(struct user_auth_info *auth_info,
-				 const char *file)
-{
-	FILE *auth;
-	fstring buf;
-	uint16_t len = 0;
-	char *ptr, *val, *param;
-
-	auth = fopen(file, "r");
-	if (auth == NULL) {
-		/* fail if we can't open the credentials file */
-		d_printf("ERROR: Unable to open credentials file!\n");
-		exit(-1);
-	}
-
-	while (!feof(auth))
-	{
-		/* get a line from the file */
-		if (!fgets(buf, sizeof(buf), auth))
-			continue;
-		len = strlen(buf);
-
-		if ((len) && (buf[len-1]=='\n'))
-		{
-			buf[len-1] = '\0';
-			len--;
-		}
-		if (len == 0)
-			continue;
-
-		/* break up the line into parameter & value.
-		 * will need to eat a little whitespace possibly */
-		param = buf;
-		if (!(ptr = strchr_m (buf, '=')))
-			continue;
-
-		val = ptr+1;
-		*ptr = '\0';
-
-		/* eat leading white space */
-		while ((*val!='\0') && ((*val==' ') || (*val=='\t')))
-			val++;
-
-		if (strwicmp("password", param) == 0) {
-			set_cmdline_auth_info_password(auth_info, val);
-		} else if (strwicmp("username", param) == 0) {
-			set_cmdline_auth_info_username(auth_info, val);
-		} else if (strwicmp("domain", param) == 0) {
-			set_cmdline_auth_info_domain(auth_info, val);
-		}
-		memset(buf, 0, sizeof(buf));
-	}
-	fclose(auth);
-}
-
 /* Handle command line options:
  *		-U,--user
  *		-A,--authentication-file
@@ -384,36 +258,12 @@ static void popt_common_credentials_callback(poptContext con,
 	struct user_auth_info *auth_info = cmdline_auth_info;
 
 	if (reason == POPT_CALLBACK_REASON_PRE) {
+		auth_info = user_auth_info_init(talloc_autofree_context());
 		if (auth_info == NULL) {
-			auth_info = user_auth_info_init(talloc_autofree_context());
-			if (auth_info == NULL) {
-				fprintf(stderr, "user_auth_info_init() failed\n");
-				exit(1);
-			}
-			cmdline_auth_info = auth_info;
-		}
-
-		set_cmdline_auth_info_username(auth_info, "GUEST");
-
-		if (getenv("LOGNAME")) {
-			set_cmdline_auth_info_username(auth_info,
-						       getenv("LOGNAME"));
-		}
-
-		if (getenv("USER")) {
-			set_cmdline_auth_info_username(auth_info,
-						       getenv("USER"));
-		}
-
-		if (getenv("PASSWD")) {
-			set_cmdline_auth_info_password(auth_info,
-						       getenv("PASSWD"));
-		}
-
-		if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) {
-			get_password_file(auth_info);
+			fprintf(stderr, "user_auth_info_init() failed\n");
+			exit(1);
 		}
-
+		cmdline_auth_info = auth_info;
 		return;
 	}
 
@@ -437,6 +287,8 @@ static void popt_common_credentials_callback(poptContext con,
 
 		load_interfaces();
 
+		set_cmdline_auth_info_guess(auth_info);
+
 		if (popt_common_credentials_delay_post) {
 			return;
 		}
@@ -447,29 +299,11 @@ static void popt_common_credentials_callback(poptContext con,
 
 	switch(opt->val) {
 	case 'U':
-		{
-			char *lp;
-			char *puser = SMB_STRDUP(arg);
-
-			if ((lp=strchr_m(puser,'%'))) {
-				size_t len;
-				*lp = '\0';
-				set_cmdline_auth_info_username(auth_info,
-							       puser);
-				set_cmdline_auth_info_password(auth_info,
-							       lp+1);
-				len = strlen(lp+1);
-				memset(lp + 1, '\0', len);
-			} else {
-				set_cmdline_auth_info_username(auth_info,
-							       puser);
-			}
-			SAFE_FREE(puser);
-		}
+		set_cmdline_auth_info_username(auth_info, arg);
 		break;
 
 	case 'A':
-		get_credentials_file(auth_info, arg);
+		set_cmdline_auth_info_from_file(auth_info, arg);
 		break;
 
 	case 'k':
diff --git a/source3/lib/util_cmdline.c b/source3/lib/util_cmdline.c
index 68ba7aa..6c98b44 100644
--- a/source3/lib/util_cmdline.c
+++ b/source3/lib/util_cmdline.c
@@ -24,6 +24,10 @@
 #include "includes.h"
 #include "auth_info.h"
 #include "secrets.h"
+#include "param/param.h"
+#include "librpc/gen_ndr/samr.h"
+#include "auth/credentials/credentials.h"
+#include "auth/gensec/gensec.h"
 
 /**************************************************************************n
   Code to cope with username/password auth options from the commandline.
@@ -31,129 +35,174 @@
 ****************************************************************************/
 
 struct user_auth_info {
-	char *username;
-	char *domain;
-	char *password;
+	struct cli_credentials *creds;
+	struct loadparm_context *lp_ctx;
 	bool got_pass;
-	bool use_kerberos;
 	int signing_state;
 	bool smb_encrypt;
 	bool use_machine_account;
-	bool fallback_after_kerberos;
-	bool use_ccache;
 	bool use_pw_nt_hash;
+	char *pw_nt_hash;
 };
 
 struct user_auth_info *user_auth_info_init(TALLOC_CTX *mem_ctx)
 {
-	struct user_auth_info *result;
+	struct user_auth_info *result = NULL;
 
 	result = talloc_zero(mem_ctx, struct user_auth_info);
 	if (result == NULL) {
 		return NULL;
 	}
 
+	result->lp_ctx = loadparm_init_s3(result, loadparm_s3_helpers());
+	if (result->lp_ctx == NULL) {
+		TALLOC_FREE(result);
+		return NULL;
+	}
+
+	result->creds = cli_credentials_init(result);
+	if (result->creds == NULL) {
+		TALLOC_FREE(result);
+		return NULL;
+	}
+
+	cli_credentials_set_conf(result->creds, result->lp_ctx);
+
 	result->signing_state = SMB_SIGNING_DEFAULT;
 	return result;
 }
 
-const char *get_cmdline_auth_info_username(const struct user_auth_info *auth_info)
+void set_cmdline_auth_info_guess(struct user_auth_info *auth_info)
 {
-	if (!auth_info->username) {
-		return "";
-	}
-	return auth_info->username;
+	/*
+	 * Note that cli_credentials_guess() calls
+	 * cli_credentials_set_conf() again, which will
+	 * hopefully cope with a reloaded smb.conf.
+	 */
+	cli_credentials_set_username(auth_info->creds, "GUEST", CRED_GUESS_ENV);
+	cli_credentials_guess(auth_info->creds, auth_info->lp_ctx);
 }
 
-void set_cmdline_auth_info_username(struct user_auth_info *auth_info,
-				    const char *username)
+void set_cmdline_auth_info_from_file(struct user_auth_info *auth_info,
+				     const char *filename)
 {
-	char *s;
-	char *p;
-	bool contains_domain = false;
-
-	s = talloc_strdup(auth_info, username);
-	if (s == NULL) {
-		exit(ENOMEM);
-	}
+	bool ok;
 
-	p = strchr_m(s, '\\');
-	if (p != NULL) {
-		contains_domain = true;
-	}
-	if (!contains_domain) {
-		p = strchr_m(s, '/');
-		if (p != NULL) {
-			contains_domain = true;
-		}
-	}
-	if (!contains_domain) {
-		char sep = *lp_winbind_separator();
-
-		if (sep != '\0') {
-			p = strchr_m(s, *lp_winbind_separator());
-			if (p != NULL) {
-				contains_domain = true;
-			}
-		}
+	ok = cli_credentials_parse_file(auth_info->creds, filename,
+					CRED_SPECIFIED);
+	if (!ok) {
+		exit(EIO);
 	}
+}
 
-	if (contains_domain) {
-		*p = '\0';
-		username = p + 1;
+const char *get_cmdline_auth_info_username(const struct user_auth_info *auth_info)
+{
+	const char *username = NULL;
 
-		/* s is now the workgroup part */
-		set_cmdline_auth_info_domain(auth_info, s);
+	username = cli_credentials_get_username(auth_info->creds);
+	if (username == NULL) {
+		return "";
 	}
 
-	TALLOC_FREE(auth_info->username);
-	auth_info->username = talloc_strdup(auth_info, username);
+	return username;
+}
 
-	TALLOC_FREE(s);
+void set_cmdline_auth_info_username(struct user_auth_info *auth_info,
+				    const char *username)
+{
+	const char *new_val = NULL;
 
-	if (!auth_info->username) {
+	cli_credentials_parse_string(auth_info->creds,
+				     username,
+				     CRED_SPECIFIED);
+	new_val = cli_credentials_get_username(auth_info->creds);
+	if (username != NULL && new_val == NULL) {
 		exit(ENOMEM);
 	}
+
+	if (strchr_m(username, '%') != NULL) {
+		auth_info->got_pass = true;
+	}
 }
 
 const char *get_cmdline_auth_info_domain(const struct user_auth_info *auth_info)
 {
-	if (!auth_info->domain) {
+	const char *domain = NULL;
+
+	domain = cli_credentials_get_domain(auth_info->creds);
+	if (domain == NULL) {
 		return "";
 	}
-	return auth_info->domain;
+
+	return domain;
 }
 
 void set_cmdline_auth_info_domain(struct user_auth_info *auth_info,
 				  const char *domain)
 {
-	TALLOC_FREE(auth_info->domain);
-	auth_info->domain = talloc_strdup(auth_info, domain);
-	if (!auth_info->domain) {
+	bool ok;
+
+	ok = cli_credentials_set_domain(auth_info->creds, domain, CRED_SPECIFIED);
+	if (!ok) {
 		exit(ENOMEM);
 	}
 }
 
 const char *get_cmdline_auth_info_password(const struct user_auth_info *auth_info)
 {
-	if (!auth_info->password) {
+	const char *password = NULL;
+
+	if (auth_info->pw_nt_hash != NULL) {
+		return auth_info->pw_nt_hash;
+	}
+
+	if (auth_info->use_pw_nt_hash) {
+		struct user_auth_info *ai =
+			discard_const_p(struct user_auth_info, auth_info);
+		struct samr_Password *nt_hash = NULL;
+
+		nt_hash = cli_credentials_get_nt_hash(ai->creds,
+						      ai);
+		if (nt_hash == NULL) {
+			return "";
+		}
+
+		ai->pw_nt_hash = hex_encode_talloc(ai,
+						   nt_hash->hash,
+						   sizeof(nt_hash->hash));
+		TALLOC_FREE(nt_hash);
+		if (ai->pw_nt_hash == NULL) {
+			return "";
+		}
+
+		return auth_info->pw_nt_hash;
+	}
+
+	password = cli_credentials_get_password(auth_info->creds);
+	if (password == NULL) {
 		return "";
 	}
-	return auth_info->password;
+
+	return password;
 }
 
 void set_cmdline_auth_info_password(struct user_auth_info *auth_info,
 				    const char *password)
 {
-	TALLOC_FREE(auth_info->password);
-	if (password == NULL) {
-		password = "";
+	bool ok;
+
+	auth_info->got_pass = true;
+
+	if (password != NULL && strlen(password) == 0) {
+		password = NULL;
 	}
-	auth_info->password = talloc_strdup(auth_info, password);
-	if (!auth_info->password) {
+
+	ok = cli_credentials_set_password(auth_info->creds,
+					  password,
+					  CRED_SPECIFIED);
+	if (!ok) {
 		exit(ENOMEM);
 	}
-	auth_info->got_pass = true;
 }
 
 bool set_cmdline_auth_info_signing_state(struct user_auth_info *auth_info,
@@ -189,18 +238,31 @@ int get_cmdline_auth_info_signing_state(const struct user_auth_info *auth_info)
 
 void set_cmdline_auth_info_use_ccache(struct user_auth_info *auth_info, bool b)
 {
-        auth_info->use_ccache = b;
+	uint32_t gensec_features;
+
+	gensec_features = cli_credentials_get_gensec_features(auth_info->creds);
+	gensec_features |= GENSEC_FEATURE_NTLM_CCACHE;
+	cli_credentials_set_gensec_features(auth_info->creds, gensec_features);
 }
 
 bool get_cmdline_auth_info_use_ccache(const struct user_auth_info *auth_info)
 {
-	return auth_info->use_ccache;
+	uint32_t gensec_features;
+
+	gensec_features = cli_credentials_get_gensec_features(auth_info->creds);
+	if (gensec_features & GENSEC_FEATURE_NTLM_CCACHE) {
+		return true;
+	}
+
+	return false;
 }
 
 void set_cmdline_auth_info_use_pw_nt_hash(struct user_auth_info *auth_info,
 					  bool b)
 {
+	TALLOC_FREE(auth_info->pw_nt_hash);
 	auth_info->use_pw_nt_hash = b;
+	cli_credentials_set_password_will_be_nt_hash(auth_info->creds, b);
 }
 
 bool get_cmdline_auth_info_use_pw_nt_hash(
@@ -212,29 +274,73 @@ bool get_cmdline_auth_info_use_pw_nt_hash(
 void set_cmdline_auth_info_use_kerberos(struct user_auth_info *auth_info,
 					bool b)
 {
-        auth_info->use_kerberos = b;
+	enum credentials_use_kerberos krb5_state;
+
+	if (b) {
+		krb5_state = CRED_MUST_USE_KERBEROS;
+	} else {
+		krb5_state = CRED_DONT_USE_KERBEROS;
+	}
+
+	cli_credentials_set_kerberos_state(auth_info->creds, krb5_state);
 }
 
 bool get_cmdline_auth_info_use_kerberos(const struct user_auth_info *auth_info)
 {
-	return auth_info->use_kerberos;
+	enum credentials_use_kerberos krb5_state;
+
+	krb5_state = cli_credentials_get_kerberos_state(auth_info->creds);
+
+	if (krb5_state == CRED_MUST_USE_KERBEROS) {
+		return true;
+	}
+
+	return false;
 }
 
 void set_cmdline_auth_info_fallback_after_kerberos(struct user_auth_info *auth_info,
 					bool b)
 {
-	auth_info->fallback_after_kerberos = b;
+	enum credentials_use_kerberos krb5_state;
+
+	krb5_state = cli_credentials_get_kerberos_state(auth_info->creds);
+
+	switch (krb5_state) {
+	case CRED_MUST_USE_KERBEROS:
+		if (b) {
+			krb5_state = CRED_AUTO_USE_KERBEROS;
+		}
+		break;
+	case CRED_AUTO_USE_KERBEROS:
+		if (!b) {
+			krb5_state = CRED_MUST_USE_KERBEROS;
+		}
+		break;
+	case CRED_DONT_USE_KERBEROS:
+		/* nothing to do */
+		break;
+	}
+
+	cli_credentials_set_kerberos_state(auth_info->creds, krb5_state);
 }
 
 bool get_cmdline_auth_info_fallback_after_kerberos(const struct user_auth_info *auth_info)
 {
-	return auth_info->fallback_after_kerberos;
+	enum credentials_use_kerberos krb5_state;
+
+	krb5_state = cli_credentials_get_kerberos_state(auth_info->creds);
+
+	if (krb5_state == CRED_AUTO_USE_KERBEROS) {
+		return true;
+	}
+
+	return false;
 }
 
 /* This should only be used by lib/popt_common.c JRA */
 void set_cmdline_auth_info_use_krb5_ticket(struct user_auth_info *auth_info)
 {
-	auth_info->use_kerberos = true;
+	set_cmdline_auth_info_use_kerberos(auth_info, true);
 	auth_info->got_pass = true;
 }
 
@@ -246,6 +352,8 @@ void set_cmdline_auth_info_smb_encrypt(struct user_auth_info *auth_info)
 
 void set_cmdline_auth_info_use_machine_account(struct user_auth_info *auth_info)
 {
+	cli_credentials_set_machine_account_pending(auth_info->creds,
+						    auth_info->lp_ctx);
 	auth_info->use_machine_account = true;
 }
 
@@ -266,54 +374,67 @@ bool get_cmdline_auth_info_use_machine_account(const struct user_auth_info *auth
 
 bool set_cmdline_auth_info_machine_account_creds(struct user_auth_info *auth_info)
 {
-	char *pass = NULL;
-	char *account = NULL;
-	const char *realm = lp_realm();
-	int rc;
-
+	struct db_context *db_ctx = NULL;
+	NTSTATUS status;
 
 	if (!get_cmdline_auth_info_use_machine_account(auth_info)) {
 		return false;
 	}
 
-	if (!secrets_init()) {
+	db_ctx = secrets_db_ctx();
+	if (db_ctx == NULL) {
 		d_printf("ERROR: Unable to open secrets database\n");
 		return false;
 	}
 
-	if (realm != NULL && realm[0] != '\0') {
-		rc = asprintf(&account,
-			      "%s$@%s",
-			      lp_netbios_name(),
-			      realm);
-		if (rc < 0) {
-			return false;
-		}
-	} else {
-		rc = asprintf(&account,
-			      "%s$",
-			      lp_netbios_name());
-		if (rc < 0) {
-			return false;
-		}
-	}
+	cli_credentials_set_domain(auth_info->creds, lpcfg_workgroup(auth_info->lp_ctx),
+				   CRED_SPECIFIED);
 
-	pass = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
-	if (!pass) {
+	status = cli_credentials_set_machine_account_db_ctx(auth_info->creds,
+							    auth_info->lp_ctx,
+							    db_ctx);
+	if (!NT_STATUS_IS_OK(status)) {
 		d_printf("ERROR: Unable to fetch machine password for "
-			"%s in domain %s\n",
-			account, lp_workgroup());
-		SAFE_FREE(account);
+			 "%s in domain %s - %s\n",
+			 lpcfg_netbios_name(auth_info->lp_ctx),
+			 lpcfg_workgroup(auth_info->lp_ctx),
+			 nt_errstr(status));
 		return false;
 	}
 
-	set_cmdline_auth_info_username(auth_info, account);
-	set_cmdline_auth_info_password(auth_info, pass);
+	return true;
+}
 
-	SAFE_FREE(account);
-	SAFE_FREE(pass);
+static const char *cmdline_auth_info_pw_callback(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;
 
-	return true;
+	name = cli_credentials_get_unparsed_name(creds, frame);
+	if (name == NULL) {
+		goto fail;
+	}
+	label = talloc_asprintf(frame, "Enter %s's password: ", 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;
 }
 
 /****************************************************************************
@@ -322,11 +443,6 @@ bool set_cmdline_auth_info_machine_account_creds(struct user_auth_info *auth_inf
 
 void set_cmdline_auth_info_getpass(struct user_auth_info *auth_info)
 {
-	char *label = NULL;
-	char pwd[256] = {0};
-	int rc;
-	TALLOC_CTX *frame;
-
 	if (get_cmdline_auth_info_got_pass(auth_info) ||
 	    get_cmdline_auth_info_use_ccache(auth_info) ||
 	    get_cmdline_auth_info_use_kerberos(auth_info)) {
@@ -334,12 +450,12 @@ void set_cmdline_auth_info_getpass(struct user_auth_info *auth_info)
 		return;
 	}
 
-	frame = talloc_stackframe();
-	label = talloc_asprintf(frame, "Enter %s's password: ",
-			get_cmdline_auth_info_username(auth_info));
-	rc = samba_getpass(label, pwd, sizeof(pwd), false, false);
-	if (rc == 0) {
-		set_cmdline_auth_info_password(auth_info, pwd);
-	}
-	TALLOC_FREE(frame);
+	cli_credentials_set_password_callback(auth_info->creds,
+					cmdline_auth_info_pw_callback);
+}
+
+struct cli_credentials *get_cmdline_auth_info_creds(
+	const struct user_auth_info *auth_info)
+{
+	return auth_info->creds;
 }
-- 
1.9.1

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: OpenPGP digital signature
URL: <http://lists.samba.org/pipermail/samba-technical/attachments/20161219/35e90c75/signature-0001.sig>


More information about the samba-technical mailing list