s3:utils: let smbstatus report anonymous signing/encryption explicitly
[samba.git] / source4 / auth / kerberos / srv_keytab.c
index 024fab547296f503af4cc08d467b2b1dcc9eaa0a..875d06971cc2dfd849776f583f60c7dec11514e4 100644 (file)
 #include "includes.h"
 #include "system/kerberos.h"
 #include "auth/credentials/credentials.h"
+#include "auth/credentials/credentials_krb5.h"
 #include "auth/kerberos/kerberos.h"
 #include "auth/kerberos/kerberos_util.h"
 #include "auth/kerberos/kerberos_srv_keytab.h"
+#include "librpc/gen_ndr/ndr_gmsa.h"
+#include "dsdb/samdb/samdb.h"
 
 static void keytab_principals_free(krb5_context context,
                                   uint32_t num_principals,
@@ -81,11 +84,36 @@ static krb5_error_code keytab_add_keys(TALLOC_CTX *parent_ctx,
                        return ret;
                }
 
-                entry.vno = kvno;
+               entry.vno = kvno;
 
                for (p = 0; p < num_principals; p++) {
+                       bool found = false;
+
                        unparsed = NULL;
                        entry.principal = principals[p];
+
+                       ret = smb_krb5_is_exact_entry_in_keytab(parent_ctx,
+                                                               context,
+                                                               keytab,
+                                                               &entry,
+                                                               &found,
+                                                               error_string);
+                       if (ret != 0) {
+                               krb5_free_keyblock_contents(context,
+                                                           KRB5_KT_KEY(&entry));
+                               return ret;
+                       }
+
+                       /*
+                        * Do not add the exact same key twice, this
+                        * will allow "samba-tool domain exportkeytab"
+                        * to refresh a keytab rather than infinitely
+                        * extend it
+                        */
+                       if (found) {
+                               continue;
+                       }
+
                        ret = krb5_kt_add_entry(context, keytab, &entry);
                        if (ret != 0) {
                                char *k5_error_string =
@@ -145,7 +173,7 @@ static krb5_error_code smb_krb5_fill_keytab(TALLOC_CTX *parent_ctx,
        mem_ctx = talloc_new(parent_ctx);
        if (!mem_ctx) {
                *perror_string = talloc_strdup(parent_ctx,
-                       "unable to allocate tmp_ctx for create_keytab");
+                       "unable to allocate tmp_ctx for smb_krb5_fill_keytab");
                return ENOMEM;
        }
 
@@ -163,7 +191,7 @@ static krb5_error_code smb_krb5_fill_keytab(TALLOC_CTX *parent_ctx,
        ret = ms_suptypes_to_ietf_enctypes(mem_ctx, supp_enctypes, &enctypes);
        if (ret) {
                *perror_string = talloc_asprintf(parent_ctx,
-                                       "create_keytab: generating list of "
+                                       "smb_krb5_fill_keytab: generating list of "
                                        "encryption types failed (%s)\n",
                                        smb_get_krb5_error_message(context,
                                                                ret, mem_ctx));
@@ -197,6 +225,199 @@ done:
        return ret;
 }
 
+NTSTATUS smb_krb5_fill_keytab_gmsa_keys(TALLOC_CTX *mem_ctx,
+                                       struct smb_krb5_context *smb_krb5_context,
+                                       krb5_keytab keytab,
+                                       krb5_principal principal,
+                                       struct ldb_context *samdb,
+                                       struct ldb_dn *dn,
+                                       bool include_historic_keys,
+                                       const char **error_string)
+{
+       const char *gmsa_attrs[] = {
+               "msDS-ManagedPassword",
+               "msDS-KeyVersionNumber",
+               "sAMAccountName",
+               "msDS-SupportedEncryptionTypes",
+               NULL
+       };
+
+       NTSTATUS status;
+       struct ldb_message *msg;
+       const struct ldb_val *managed_password_blob;
+       const char *managed_pw_utf8;
+       const char *previous_managed_pw_utf8;
+       const char *username;
+       const char *salt_principal;
+       uint32_t kvno = 0;
+       uint32_t supported_enctypes = 0;
+       krb5_context context = smb_krb5_context->krb5_context;
+       struct cli_credentials *cred = NULL;
+       const char *realm = NULL;
+
+       /*
+        * Search for msDS-ManagedPassword (and other attributes to
+        * avoid a race) as this was not in the original search.
+        */
+       int ret;
+
+       TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+       if (tmp_ctx == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       ret = dsdb_search_one(samdb,
+                             tmp_ctx,
+                             &msg,
+                             dn,
+                             LDB_SCOPE_BASE,
+                             gmsa_attrs, 0,
+                             "(objectClass=msDS-GroupManagedServiceAccount)");
+
+       if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+               /*
+                * Race condition, object has gone, or just wasn't a
+                * gMSA
+                */
+               *error_string = talloc_asprintf(mem_ctx,
+                                               "Did not find gMSA at %s",
+                                               ldb_dn_get_linearized(dn));
+               TALLOC_FREE(tmp_ctx);
+               return NT_STATUS_NO_SUCH_USER;
+       }
+
+       if (ret != LDB_SUCCESS) {
+               *error_string = talloc_asprintf(mem_ctx,
+                                               "Error looking for gMSA at %s: %s",
+                                               ldb_dn_get_linearized(dn), ldb_errstring(samdb));
+               TALLOC_FREE(tmp_ctx);
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       /* Extract out passwords */
+       managed_password_blob = ldb_msg_find_ldb_val(msg, "msDS-ManagedPassword");
+
+       if (managed_password_blob == NULL) {
+               /*
+                * No password set on this yet or not readable by this user
+                */
+               *error_string = talloc_asprintf(mem_ctx,
+                                               "Did not find msDS-ManagedPassword at %s",
+                                               ldb_dn_get_extended_linearized(mem_ctx, msg->dn, 1));
+               TALLOC_FREE(tmp_ctx);
+               return NT_STATUS_NO_USER_KEYS;
+       }
+
+       cred = cli_credentials_init(tmp_ctx);
+       if (cred == NULL) {
+               *error_string = talloc_asprintf(mem_ctx,
+                                               "Could not allocate cli_credentials for %s",
+                                               ldb_dn_get_linearized(msg->dn));
+               TALLOC_FREE(tmp_ctx);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       realm = smb_krb5_principal_get_realm(tmp_ctx,
+                                            context,
+                                            principal);
+       if (realm == NULL) {
+               *error_string = talloc_asprintf(mem_ctx,
+                                               "Could not allocate copy of realm for %s",
+                                               ldb_dn_get_linearized(msg->dn));
+               TALLOC_FREE(tmp_ctx);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       cli_credentials_set_realm(cred, realm, CRED_SPECIFIED);
+
+       username = ldb_msg_find_attr_as_string(msg, "sAMAccountName", NULL);
+       if (username == NULL) {
+               *error_string = talloc_asprintf(mem_ctx,
+                                               "No sAMAccountName on %s",
+                                               ldb_dn_get_linearized(msg->dn));
+               TALLOC_FREE(tmp_ctx);
+               return NT_STATUS_INVALID_ACCOUNT_NAME;
+       }
+
+       cli_credentials_set_username(cred, username, CRED_SPECIFIED);
+
+       /*
+        * Note that this value may not be correct, it is updated
+        * after the query that gives us the passwords
+        */
+       kvno = ldb_msg_find_attr_as_uint(msg, "msDS-KeyVersionNumber", 0);
+
+       cli_credentials_set_kvno(cred, kvno);
+
+       supported_enctypes = ldb_msg_find_attr_as_uint(msg,
+                                                      "msDS-SupportedEncryptionTypes",
+                                                      ENC_STRONG_SALTED_TYPES);
+       /*
+        * We trim this down to just the salted AES types, as the
+        * passwords are now wrong for rc4-hmac due to the mapping of
+        * invalid sequences in UTF16_MUNGED -> UTF8 string conversion
+        * within cli_credentials_get_password(). Users using this new
+        * feature won't be using such weak crypto anyway.  If
+        * required we could also set the NT Hash as a key directly,
+        * this is just a limitation of smb_krb5_fill_keytab() taking
+        * a simple string as input.
+        */
+       supported_enctypes &= ENC_STRONG_SALTED_TYPES;
+
+       /* Update the keytab */
+
+       status = cli_credentials_set_gmsa_passwords(cred,
+                                                   managed_password_blob,
+                                                   true /* for keytab */,
+                                                   error_string);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               *error_string = talloc_asprintf(mem_ctx,
+                                               "Could not parse gMSA passwords on %s: %s",
+                                               ldb_dn_get_linearized(msg->dn),
+                                               *error_string);
+               TALLOC_FREE(tmp_ctx);
+               return status;
+       }
+
+       managed_pw_utf8 = cli_credentials_get_password(cred);
+
+       previous_managed_pw_utf8 = cli_credentials_get_old_password(cred);
+
+       salt_principal = cli_credentials_get_salt_principal(cred, tmp_ctx);
+       if (salt_principal == NULL) {
+               *error_string = talloc_asprintf(mem_ctx,
+                                               "Failed to generate salt principal for %s",
+                                               ldb_dn_get_linearized(msg->dn));
+               TALLOC_FREE(tmp_ctx);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       ret = smb_krb5_fill_keytab(tmp_ctx,
+                                  salt_principal,
+                                  kvno,
+                                  managed_pw_utf8,
+                                  previous_managed_pw_utf8,
+                                  supported_enctypes,
+                                  1,
+                                  &principal,
+                                  context,
+                                  keytab,
+                                  include_historic_keys,
+                                  error_string);
+       if (ret) {
+               *error_string = talloc_asprintf(mem_ctx,
+                                               "Failed to add keys from %s to keytab: %s",
+                                               ldb_dn_get_linearized(msg->dn),
+                                               *error_string);
+               TALLOC_FREE(tmp_ctx);
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       TALLOC_FREE(tmp_ctx);
+       return NT_STATUS_OK;
+}
+
 /**
  * @brief Update a Kerberos keytab and removes any obsolete keytab entries.
  *