auth/credentials: add cli_credentials_set_utf16_password()
[obnox/samba/samba-obnox.git] / auth / credentials / credentials.c
index 83e90344bfcbe34459e5d25af77689588a9f09bc..a9e4fc864d46426de0d769fa0e5926fdd216bad6 100644 (file)
@@ -24,9 +24,9 @@
 #include "includes.h"
 #include "librpc/gen_ndr/samr.h" /* for struct samrPassword */
 #include "auth/credentials/credentials.h"
-#include "auth/credentials/credentials_proto.h"
+#include "auth/credentials/credentials_internal.h"
 #include "libcli/auth/libcli_auth.h"
-#include "lib/events/events.h"
+#include "tevent.h"
 #include "param/param.h"
 #include "system/filesys.h"
 
@@ -104,7 +104,7 @@ _PUBLIC_ struct cli_credentials *cli_credentials_init(TALLOC_CTX *mem_ctx)
 
        cred->machine_account = false;
 
-       cred->tries = 3;
+       cred->password_tries = 0;
 
        cred->callback_running = false;
 
@@ -112,9 +112,37 @@ _PUBLIC_ struct cli_credentials *cli_credentials_init(TALLOC_CTX *mem_ctx)
        cli_credentials_set_gensec_features(cred, 0);
        cli_credentials_set_krb_forwardable(cred, CRED_AUTO_KRB_FORWARDABLE);
 
+       cred->forced_sasl_mech = NULL;
+
        return cred;
 }
 
+_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;
+}
+
+_PUBLIC_ struct cli_credentials *cli_credentials_shallow_copy(TALLOC_CTX *mem_ctx,
+                                               struct cli_credentials *src)
+{
+       struct cli_credentials *dst;
+
+       dst = talloc(mem_ctx, struct cli_credentials);
+       if (dst == NULL) {
+               return NULL;
+       }
+
+       *dst = *src;
+
+       return dst;
+}
+
 /**
  * Create a new anonymous credential
  * @param mem_ctx TALLOC_CTX parent for credentials structure 
@@ -135,6 +163,13 @@ _PUBLIC_ void cli_credentials_set_kerberos_state(struct cli_credentials *creds,
        creds->use_kerberos = use_kerberos;
 }
 
+_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)
 {
@@ -146,6 +181,11 @@ _PUBLIC_ enum credentials_use_kerberos cli_credentials_get_kerberos_state(struct
        return creds->use_kerberos;
 }
 
+_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;
@@ -180,8 +220,10 @@ _PUBLIC_ const char *cli_credentials_get_username(struct cli_credentials *cred)
                cred->callback_running = true;
                cred->username = cred->username_cb(cred);
                cred->callback_running = false;
-               cred->username_obtained = CRED_SPECIFIED;
-               cli_credentials_invalidate_ccache(cred, cred->username_obtained);
+               if (cred->username_obtained == CRED_CALLBACK) {
+                       cred->username_obtained = CRED_CALLBACK_RESULT;
+                       cli_credentials_invalidate_ccache(cred, cred->username_obtained);
+               }
        }
 
        return cred->username;
@@ -200,7 +242,7 @@ _PUBLIC_ bool cli_credentials_set_username(struct cli_credentials *cred,
        return false;
 }
 
-bool cli_credentials_set_username_callback(struct cli_credentials *cred,
+_PUBLIC_ bool cli_credentials_set_username_callback(struct cli_credentials *cred,
                                  const char *(*username_cb) (struct cli_credentials *))
 {
        if (cred->username_obtained < CRED_CALLBACK) {
@@ -237,7 +279,7 @@ _PUBLIC_ const char *cli_credentials_get_bind_dn(struct cli_credentials *cred)
  * @retval The username set on this context.
  * @note Return value will never be NULL except by programmer error.
  */
-const char *cli_credentials_get_principal_and_obtained(struct cli_credentials *cred, TALLOC_CTX *mem_ctx, enum credentials_obtained *obtained)
+_PUBLIC_ const 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,
@@ -249,8 +291,10 @@ const char *cli_credentials_get_principal_and_obtained(struct cli_credentials *c
                cred->callback_running = true;
                cred->principal = cred->principal_cb(cred);
                cred->callback_running = false;
-               cred->principal_obtained = CRED_SPECIFIED;
-               cli_credentials_invalidate_ccache(cred, cred->principal_obtained);
+               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
@@ -268,7 +312,7 @@ const char *cli_credentials_get_principal_and_obtained(struct cli_credentials *c
                }
        }
        *obtained = cred->principal_obtained;
-       return talloc_reference(mem_ctx, cred->principal);
+       return talloc_strdup(mem_ctx, cred->principal);
 }
 
 /**
@@ -283,7 +327,7 @@ _PUBLIC_ const char *cli_credentials_get_principal(struct cli_credentials *cred,
        return cli_credentials_get_principal_and_obtained(cred, mem_ctx, &obtained);
 }
 
-bool cli_credentials_set_principal(struct cli_credentials *cred, 
+_PUBLIC_ bool cli_credentials_set_principal(struct cli_credentials *cred, 
                                   const char *val, 
                                   enum credentials_obtained obtained)
 {
@@ -299,7 +343,7 @@ bool cli_credentials_set_principal(struct cli_credentials *cred,
 
 /* Set a callback to get the principal.  This could be a popup dialog,
  * a terminal prompt or similar.  */
-bool cli_credentials_set_principal_callback(struct cli_credentials *cred,
+_PUBLIC_ bool cli_credentials_set_principal_callback(struct cli_credentials *cred,
                                  const char *(*principal_cb) (struct cli_credentials *))
 {
        if (cred->principal_obtained < CRED_CALLBACK) {
@@ -321,6 +365,14 @@ _PUBLIC_ bool cli_credentials_authentication_requested(struct cli_credentials *c
                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;
        }
@@ -353,11 +405,13 @@ _PUBLIC_ const char *cli_credentials_get_password(struct cli_credentials *cred)
 
        if (cred->password_obtained == CRED_CALLBACK && 
            !cred->callback_running) {
-               cred->callback_running = true;
+               cred->callback_running = true;
                cred->password = cred->password_cb(cred);
-               cred->callback_running = false;
-               cred->password_obtained = CRED_CALLBACK_RESULT;
-               cli_credentials_invalidate_ccache(cred, cred->password_obtained);
+               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;
@@ -371,7 +425,12 @@ _PUBLIC_ bool cli_credentials_set_password(struct cli_credentials *cred,
                                  enum credentials_obtained obtained)
 {
        if (obtained >= cred->password_obtained) {
+               cred->password_tries = 0;
                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");
+               }
                cred->password_obtained = obtained;
                cli_credentials_invalidate_ccache(cred, cred->password_obtained);
 
@@ -388,6 +447,7 @@ _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);
@@ -402,7 +462,7 @@ _PUBLIC_ bool cli_credentials_set_password_callback(struct cli_credentials *cred
  * @param cred credentials context
  * @retval If set, the cleartext password, otherwise NULL
  */
-const char *cli_credentials_get_old_password(struct cli_credentials *cred)
+_PUBLIC_ const char *cli_credentials_get_old_password(struct cli_credentials *cred)
 {
        if (cred->machine_account_pending) {
                cli_credentials_set_machine_account(cred,
@@ -412,11 +472,15 @@ const char *cli_credentials_get_old_password(struct cli_credentials *cred)
        return cred->old_password;
 }
 
-bool cli_credentials_set_old_password(struct cli_credentials *cred, 
+_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");
+       }
        return true;
 }
 
@@ -429,23 +493,35 @@ bool cli_credentials_set_old_password(struct cli_credentials *cred,
  * @param cred credentials context
  * @retval If set, the cleartext password, otherwise NULL
  */
-_PUBLIC_ const struct samr_Password *cli_credentials_get_nt_hash(struct cli_credentials *cred, 
-                                                       TALLOC_CTX *mem_ctx)
+_PUBLIC_ struct samr_Password *cli_credentials_get_nt_hash(struct cli_credentials *cred,
+                                                          TALLOC_CTX *mem_ctx)
 {
-       const char *password = cli_credentials_get_password(cred);
+       const char *password = NULL;
 
+       if (cred->nt_hash != NULL) {
+               struct samr_Password *nt_hash = talloc(mem_ctx, struct samr_Password);
+               if (!nt_hash) {
+                       return NULL;
+               }
+
+               *nt_hash = *cred->nt_hash;
+
+               return nt_hash;
+       }
+
+       password = cli_credentials_get_password(cred);
        if (password) {
                struct samr_Password *nt_hash = talloc(mem_ctx, struct samr_Password);
                if (!nt_hash) {
                        return NULL;
                }
-               
-               E_md4hash(password, nt_hash->hash);    
+
+               E_md4hash(password, nt_hash->hash);
 
                return nt_hash;
-       } else {
-               return cred->nt_hash;
        }
+
+       return NULL;
 }
 
 /**
@@ -466,8 +542,10 @@ _PUBLIC_ const char *cli_credentials_get_domain(struct cli_credentials *cred)
                cred->callback_running = true;
                cred->domain = cred->domain_cb(cred);
                cred->callback_running = false;
-               cred->domain_obtained = CRED_SPECIFIED;
-               cli_credentials_invalidate_ccache(cred, cred->domain_obtained);
+               if (cred->domain_obtained == CRED_CALLBACK) {
+                       cred->domain_obtained = CRED_CALLBACK_RESULT;
+                       cli_credentials_invalidate_ccache(cred, cred->domain_obtained);
+               }
        }
 
        return cred->domain;
@@ -484,7 +562,11 @@ _PUBLIC_ bool cli_credentials_set_domain(struct cli_credentials *cred,
                 * calculations */
                cred->domain = strupper_talloc(cred, val);
                cred->domain_obtained = obtained;
-               cli_credentials_invalidate_ccache(cred, cred->domain_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;
        }
 
@@ -521,8 +603,10 @@ _PUBLIC_ const char *cli_credentials_get_realm(struct cli_credentials *cred)
                cred->callback_running = true;
                cred->realm = cred->realm_cb(cred);
                cred->callback_running = false;
-               cred->realm_obtained = CRED_SPECIFIED;
-               cli_credentials_invalidate_ccache(cred, cred->realm_obtained);
+               if (cred->realm_obtained == CRED_CALLBACK) {
+                       cred->realm_obtained = CRED_CALLBACK_RESULT;
+                       cli_credentials_invalidate_ccache(cred, cred->realm_obtained);
+               }
        }
 
        return cred->realm;
@@ -572,7 +656,9 @@ _PUBLIC_ const char *cli_credentials_get_workstation(struct cli_credentials *cre
                cred->callback_running = true;
                cred->workstation = cred->workstation_cb(cred);
                cred->callback_running = false;
-               cred->workstation_obtained = CRED_SPECIFIED;
+               if (cred->workstation_obtained == CRED_CALLBACK) {
+                       cred->workstation_obtained = CRED_CALLBACK_RESULT;
+               }
        }
 
        return cred->workstation;
@@ -658,7 +744,7 @@ _PUBLIC_ const char *cli_credentials_get_unparsed_name(struct cli_credentials *c
        const char *name;
 
        if (bind_dn) {
-               name = talloc_reference(mem_ctx, 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]) {
@@ -682,9 +768,21 @@ _PUBLIC_ void cli_credentials_set_conf(struct cli_credentials *cred,
                              struct loadparm_context *lp_ctx)
 {
        cli_credentials_set_username(cred, "", CRED_UNINITIALISED);
-       cli_credentials_set_domain(cred, lpcfg_workgroup(lp_ctx), CRED_UNINITIALISED);
-       cli_credentials_set_workstation(cred, lpcfg_netbios_name(lp_ctx), CRED_UNINITIALISED);
-       cli_credentials_set_realm(cred, lpcfg_realm(lp_ctx), CRED_UNINITIALISED);
+       if (lpcfg_parm_is_cmdline(lp_ctx, "workgroup")) {
+               cli_credentials_set_domain(cred, lpcfg_workgroup(lp_ctx), CRED_SPECIFIED);
+       } else {
+               cli_credentials_set_domain(cred, lpcfg_workgroup(lp_ctx), CRED_UNINITIALISED);
+       }
+       if (lpcfg_parm_is_cmdline(lp_ctx, "netbios name")) {
+               cli_credentials_set_workstation(cred, lpcfg_netbios_name(lp_ctx), CRED_SPECIFIED);
+       } else {
+               cli_credentials_set_workstation(cred, lpcfg_netbios_name(lp_ctx), CRED_UNINITIALISED);
+       }
+       if (lpcfg_parm_is_cmdline(lp_ctx, "realm")) {
+               cli_credentials_set_realm(cred, lpcfg_realm(lp_ctx), CRED_SPECIFIED);
+       } else {
+               cli_credentials_set_realm(cred, lpcfg_realm(lp_ctx), CRED_UNINITIALISED);
+       }
 }
 
 /**
@@ -741,14 +839,18 @@ _PUBLIC_ void cli_credentials_guess(struct cli_credentials *cred,
 _PUBLIC_ void cli_credentials_set_netlogon_creds(struct cli_credentials *cred, 
                                                 struct netlogon_creds_CredentialState *netlogon_creds)
 {
-       cred->netlogon_creds = talloc_reference(cred, 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 
  */
 
-struct netlogon_creds_CredentialState *cli_credentials_get_netlogon_creds(struct cli_credentials *cred)
+_PUBLIC_ struct netlogon_creds_CredentialState *cli_credentials_get_netlogon_creds(struct cli_credentials *cred)
 {
        return cred->netlogon_creds;
 }
@@ -847,12 +949,19 @@ _PUBLIC_ bool cli_credentials_wrong_password(struct cli_credentials *cred)
        if (cred->password_obtained != CRED_CALLBACK_RESULT) {
                return false;
        }
-       
-       cred->password_obtained = CRED_CALLBACK;
 
-       cred->tries--;
+       if (cred->password_tries == 0) {
+               return false;
+       }
+
+       cred->password_tries--;
+
+       if (cred->password_tries == 0) {
+               return false;
+       }
 
-       return (cred->tries > 0);
+       cred->password_obtained = CRED_CALLBACK;
+       return true;
 }
 
 _PUBLIC_ void cli_credentials_get_ntlm_username_domain(struct cli_credentials *cred, TALLOC_CTX *mem_ctx,