]> git.samba.org - obnox/samba/samba-obnox.git/blobdiff - source4/kdc/db-glue.c
s4:kdc: add aes key support for trusted domains
[obnox/samba/samba-obnox.git] / source4 / kdc / db-glue.c
index ab606e8e237f578f318c2ad74f9f71c7f39223a3..caeb1b2effeb3d87d78312af015ba125412369c7 100644 (file)
@@ -132,7 +132,7 @@ static HDBFlags uf2HDBFlags(krb5_context context, uint32_t userAccountControl, e
                flags.client = 0;
        }
        if (userAccountControl & UF_LOCKOUT) {
-               flags.invalid = 1;
+               flags.locked_out = 1;
        }
 /*
        if (userAccountControl & UF_PASSWORD_NOTREQD) {
@@ -546,6 +546,7 @@ static krb5_error_code samba_kdc_message2entry(krb5_context context,
 {
        struct loadparm_context *lp_ctx = kdc_db_ctx->lp_ctx;
        uint32_t userAccountControl;
+       uint32_t msDS_User_Account_Control_Computed;
        unsigned int i;
        krb5_error_code ret = 0;
        krb5_boolean is_computer = FALSE;
@@ -604,6 +605,25 @@ static krb5_error_code samba_kdc_message2entry(krb5_context context,
 
        userAccountControl = ldb_msg_find_attr_as_uint(msg, "userAccountControl", 0);
 
+       msDS_User_Account_Control_Computed
+               = ldb_msg_find_attr_as_uint(msg,
+                                           "msDS-User-Account-Control-Computed",
+                                           UF_ACCOUNTDISABLE);
+
+       /*
+        * This brings in the lockout flag, block the account if not
+        * found.  We need the weird UF_ACCOUNTDISABLE check because
+        * we do not want to fail open if the value is not returned,
+        * but 0 is a valid value (all OK)
+        */
+       if (msDS_User_Account_Control_Computed == UF_ACCOUNTDISABLE) {
+               ret = EINVAL;
+               krb5_set_error_message(context, ret, "samba_kdc_message2entry: "
+                               "no msDS-User-Account-Control-Computed present");
+               goto out;
+       } else {
+               userAccountControl |= msDS_User_Account_Control_Computed;
+       }
 
        entry_ex->entry.principal = malloc(sizeof(*(entry_ex->entry.principal)));
        if (ent_type == SAMBA_KDC_ENT_TYPE_ANY && principal == NULL) {
@@ -838,17 +858,27 @@ static krb5_error_code samba_kdc_trust_message2entry(krb5_context context,
        struct loadparm_context *lp_ctx = kdc_db_ctx->lp_ctx;
        const char *dnsdomain;
        const char *realm = lpcfg_realm(lp_ctx);
-       DATA_BLOB password_utf16;
-       struct samr_Password password_hash;
+       DATA_BLOB password_utf16 = data_blob_null;
+       DATA_BLOB password_utf8 = data_blob_null;
+       struct samr_Password _password_hash;
+       const struct samr_Password *password_hash = NULL;
        const struct ldb_val *password_val;
        struct trustAuthInOutBlob password_blob;
        struct samba_kdc_entry *p;
        bool use_previous;
        uint32_t current_kvno;
+       uint32_t num_keys = 0;
        enum ndr_err_code ndr_err;
        int ret, trust_direction_flags;
        unsigned int i;
        struct AuthenticationInformationArray *auth_array;
+       uint32_t supported_enctypes = ENCTYPE_ARCFOUR_HMAC;
+
+       if (dsdb_functional_level(kdc_db_ctx->samdb) >= DS_DOMAIN_FUNCTION_2008) {
+               supported_enctypes = ldb_msg_find_attr_as_uint(msg,
+                                       "msDS-SupportedEncryptionTypes",
+                                       supported_enctypes);
+       }
 
        p = talloc(mem_ctx, struct samba_kdc_entry);
        if (!p) {
@@ -875,6 +905,29 @@ static krb5_error_code samba_kdc_trust_message2entry(krb5_context context,
                            &entry_ex->entry.created_by.principal,
                            realm, "kadmin", NULL);
 
+       entry_ex->entry.principal = malloc(sizeof(*(entry_ex->entry.principal)));
+       if (entry_ex->entry.principal == NULL) {
+               krb5_clear_error_message(context);
+               ret = ENOMEM;
+               goto out;
+       }
+
+       ret = copy_Principal(principal, entry_ex->entry.principal);
+       if (ret) {
+               krb5_clear_error_message(context);
+               goto out;
+       }
+
+       /*
+        * While we have copied the client principal, tests
+        * show that Win2k3 returns the 'corrected' realm, not
+        * the client-specified realm.  This code attempts to
+        * replace the client principal's realm with the one
+        * we determine from our records
+        */
+
+       krb5_principal_set_realm(context, entry_ex->entry.principal, realm);
+
        entry_ex->entry.valid_start = NULL;
 
        trust_direction_flags = ldb_msg_find_attr_as_int(msg, "trustDirection", 0);
@@ -890,13 +943,15 @@ static krb5_error_code samba_kdc_trust_message2entry(krb5_context context,
        }
 
        if (!password_val || !(trust_direction_flags & direction)) {
-               ret = ENOENT;
+               krb5_clear_error_message(context);
+               ret = HDB_ERR_NOENTRY;
                goto out;
        }
 
        ndr_err = ndr_pull_struct_blob(password_val, mem_ctx, &password_blob,
                                           (ndr_pull_flags_fn_t)ndr_pull_trustAuthInOutBlob);
        if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+               krb5_clear_error_message(context);
                ret = EINVAL;
                goto out;
        }
@@ -932,7 +987,8 @@ static krb5_error_code samba_kdc_trust_message2entry(krb5_context context,
        } else {
                DEBUG(1,(__location__ ": Request for unknown kvno %u - current kvno is %u\n",
                         kvno, current_kvno));
-               ret = ENOENT;
+               krb5_clear_error_message(context);
+               ret = HDB_ERR_NOENTRY;
                goto out;
        }
 
@@ -951,38 +1007,124 @@ static krb5_error_code samba_kdc_trust_message2entry(krb5_context context,
 
        for (i=0; i < auth_array->count; i++) {
                if (auth_array->array[i].AuthType == TRUST_AUTH_TYPE_CLEAR) {
+                       bool ok;
+
                        password_utf16 = data_blob_const(auth_array->array[i].AuthInfo.clear.password,
                                                         auth_array->array[i].AuthInfo.clear.size);
-                       /* In the future, generate all sorts of
-                        * hashes, but for now we can't safely convert
-                        * the random strings windows uses into
-                        * utf8 */
+                       if (password_utf16.length == 0) {
+                               break;
+                       }
+
+                       if (supported_enctypes & ENCTYPE_ARCFOUR_HMAC) {
+                               mdfour(_password_hash.hash, password_utf16.data, password_utf16.length);
+                               if (password_hash == NULL) {
+                                       num_keys += 1;
+                               }
+                               password_hash = &_password_hash;
+                       }
+
+                       if (!(supported_enctypes & (ENC_HMAC_SHA1_96_AES128|ENC_HMAC_SHA1_96_AES256))) {
+                               break;
+                       }
 
-                       /* but as it is utf16 already, we can get the NT password/arcfour-hmac-md5 key */
-                       mdfour(password_hash.hash, password_utf16.data, password_utf16.length);
+                       ok = convert_string_talloc(mem_ctx,
+                                                  CH_UTF16MUNGED, CH_UTF8,
+                                                  password_utf16.data,
+                                                  password_utf16.length,
+                                                  (void *)&password_utf8.data,
+                                                  &password_utf8.length);
+                       if (!ok) {
+                               krb5_clear_error_message(context);
+                               ret = ENOMEM;
+                               goto out;
+                       }
+
+                       if (supported_enctypes & ENC_HMAC_SHA1_96_AES128) {
+                               num_keys += 1;
+                       }
+                       if (supported_enctypes & ENC_HMAC_SHA1_96_AES256) {
+                               num_keys += 1;
+                       }
                        break;
                } else if (auth_array->array[i].AuthType == TRUST_AUTH_TYPE_NT4OWF) {
-                       password_hash = auth_array->array[i].AuthInfo.nt4owf.password;
-                       break;
+                       if (supported_enctypes & ENCTYPE_ARCFOUR_HMAC) {
+                               password_hash = &auth_array->array[i].AuthInfo.nt4owf.password;
+                               num_keys += 1;
+                       }
                }
        }
 
-       if (i < auth_array->count) {
-               Key key;
-               /* Must have found a cleartext or MD4 password */
-               entry_ex->entry.keys.val = calloc(1, sizeof(Key));
+       /* Must have found a cleartext or MD4 password */
+       if (num_keys == 0) {
+               DEBUG(1,(__location__ ": no usable key found\n"));
+               krb5_clear_error_message(context);
+               ret = HDB_ERR_NOENTRY;
+               goto out;
+       }
 
-               key.mkvno = 0;
-               key.salt = NULL; /* No salt for this enc type */
+       entry_ex->entry.keys.val = calloc(num_keys, sizeof(Key));
+       if (entry_ex->entry.keys.val == NULL) {
+               krb5_clear_error_message(context);
+               ret = ENOMEM;
+               goto out;
+       }
 
-               if (entry_ex->entry.keys.val == NULL) {
-                       ret = ENOMEM;
+       if (password_utf8.length != 0) {
+               Key key = {};
+               krb5_const_principal salt_principal = principal;
+               krb5_salt salt;
+               krb5_data cleartext_data;
+
+               cleartext_data.data = password_utf8.data;
+               cleartext_data.length = password_utf8.length;
+
+               ret = krb5_get_pw_salt(context,
+                                      salt_principal,
+                                      &salt);
+               if (ret != 0) {
                        goto out;
                }
 
+               if (supported_enctypes & ENCTYPE_AES256_CTS_HMAC_SHA1_96) {
+                       ret = krb5_string_to_key_data_salt(context,
+                                                          ENCTYPE_AES256_CTS_HMAC_SHA1_96,
+                                                          cleartext_data,
+                                                          salt,
+                                                          &key.key);
+                       if (ret != 0) {
+                               krb5_free_salt(context, salt);
+                               goto out;
+                       }
+
+                       entry_ex->entry.keys.val[entry_ex->entry.keys.len] = key;
+                       entry_ex->entry.keys.len++;
+               }
+
+               if (supported_enctypes & ENCTYPE_AES128_CTS_HMAC_SHA1_96) {
+                       ret = krb5_string_to_key_data_salt(context,
+                                                          ENCTYPE_AES128_CTS_HMAC_SHA1_96,
+                                                          cleartext_data,
+                                                          salt,
+                                                          &key.key);
+                       if (ret != 0) {
+                               krb5_free_salt(context, salt);
+                               goto out;
+                       }
+
+                       entry_ex->entry.keys.val[entry_ex->entry.keys.len] = key;
+                       entry_ex->entry.keys.len++;
+               }
+
+               krb5_free_salt(context, salt);
+       }
+
+       if (password_hash != NULL) {
+               Key key = {};
+
                ret = krb5_keyblock_init(context,
                                         ENCTYPE_ARCFOUR_HMAC,
-                                        password_hash.hash, sizeof(password_hash.hash),
+                                        password_hash->hash,
+                                        sizeof(password_hash->hash),
                                         &key.key);
                if (ret != 0) {
                        goto out;
@@ -992,21 +1134,6 @@ static krb5_error_code samba_kdc_trust_message2entry(krb5_context context,
                entry_ex->entry.keys.len++;
        }
 
-       entry_ex->entry.principal = malloc(sizeof(*(entry_ex->entry.principal)));
-
-       ret = copy_Principal(principal, entry_ex->entry.principal);
-       if (ret) {
-               krb5_clear_error_message(context);
-               goto out;
-       }
-
-       /* While we have copied the client principal, tests
-        * show that Win2k3 returns the 'corrected' realm, not
-        * the client-specified realm.  This code attempts to
-        * replace the client principal's realm with the one
-        * we determine from our records */
-
-       krb5_principal_set_realm(context, entry_ex->entry.principal, realm);
        entry_ex->entry.flags = int2HDBFlags(0);
        entry_ex->entry.flags.immutable = 1;
        entry_ex->entry.flags.invalid = 0;
@@ -1059,47 +1186,25 @@ static krb5_error_code samba_kdc_lookup_trust(krb5_context context, struct ldb_c
                                        struct ldb_dn *realm_dn,
                                        struct ldb_message **pmsg)
 {
-       int lret;
-       krb5_error_code ret;
-       char *filter = NULL;
+       NTSTATUS status;
        const char * const *attrs = trust_attrs;
 
-       struct ldb_result *res = NULL;
-       char *realm_encoded = ldb_binary_encode_string(mem_ctx, realm);
-       if (!realm_encoded) {
-               if (!filter) {
-                       ret = ENOMEM;
-                       krb5_set_error_message(context, ret, "talloc_asprintf: out of memory");
-                       return ret;
-               }
-       }
-       filter = talloc_asprintf(mem_ctx, "(&(objectClass=trustedDomain)(|(flatname=%s)(trustPartner=%s)))", 
-                                realm_encoded, realm_encoded);
-
-       if (!filter) {
-               talloc_free(realm_encoded);
-               ret = ENOMEM;
-               krb5_set_error_message(context, ret, "talloc_asprintf: out of memory");
-               return ret;
-       }
-
-       lret = dsdb_search(ldb_ctx, mem_ctx, &res,
-                          ldb_get_default_basedn(ldb_ctx),
-                          LDB_SCOPE_SUBTREE, attrs,
-                          DSDB_SEARCH_NO_GLOBAL_CATALOG,
-                          "%s", filter);
-       if (lret != LDB_SUCCESS) {
-               DEBUG(3, ("Failed to search for %s: %s\n", filter, ldb_errstring(ldb_ctx)));
-               return HDB_ERR_NOENTRY;
-       } else if (res->count == 0 || res->count > 1) {
-               DEBUG(3, ("Failed find a single entry for %s: got %d\n", filter, res->count));
-               talloc_free(res);
+       status = sam_get_results_trust(ldb_ctx,
+                                      mem_ctx, realm, realm, attrs,
+                                      pmsg);
+       if (NT_STATUS_IS_OK(status)) {
+               return 0;
+       } else if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
                return HDB_ERR_NOENTRY;
+       } else if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MEMORY)) {
+               int ret = ENOMEM;
+               krb5_set_error_message(context, ret, "get_sam_result_trust: out of memory");
+               return ret;
+       } else {
+               int ret = EINVAL;
+               krb5_set_error_message(context, ret, "get_sam_result_trust: %s", nt_errstr(status));
+               return ret;
        }
-       talloc_steal(mem_ctx, res->msgs);
-       *pmsg = res->msgs[0];
-       talloc_free(res);
-       return 0;
 }
 
 static krb5_error_code samba_kdc_lookup_client(krb5_context context,
@@ -1309,7 +1414,11 @@ static krb5_error_code samba_kdc_fetch_krbtgt(krb5_context context,
                                                    principal, direction,
                                                    realm_dn, flags, kvno, msg, entry_ex);
                if (ret != 0) {
-                       krb5_warnx(context, "samba_kdc_fetch: trust_message2entry failed");
+                       krb5_warnx(context, "samba_kdc_fetch: trust_message2entry failed for %s",
+                                  ldb_dn_get_linearized(msg->dn));
+                       krb5_set_error_message(context, ret, "samba_kdc_fetch: "
+                                              "trust_message2entry failed for %s",
+                                              ldb_dn_get_linearized(msg->dn));
                }
                return ret;
        }
@@ -1364,10 +1473,10 @@ static krb5_error_code samba_kdc_lookup_server(krb5_context context,
        } else {
                int lret;
                char *short_princ;
-               const char *realm;
+               /* const char *realm; */
                /* server as client principal case, but we must not lookup userPrincipalNames */
                *realm_dn = ldb_get_default_basedn(kdc_db_ctx->samdb);
-               realm = krb5_principal_get_realm(context, principal);
+               /* realm = krb5_principal_get_realm(context, principal); */
 
                /* TODO: Check if it is our realm, otherwise give referral */
 
@@ -1560,6 +1669,7 @@ krb5_error_code samba_kdc_firstkey(krb5_context context,
                TALLOC_FREE(priv);
                return ret;
        }
+       krb5_free_default_realm(context, realm);
 
        lret = dsdb_search(ldb_ctx, priv, &res,
                           priv->realm_dn, LDB_SCOPE_SUBTREE, user_attrs,