LCOV - code coverage report
Current view: top level - source4/dsdb/gmsa - util.c (source / functions) Hit Total Coverage
Test: coverage report for master 98b443d9 Lines: 508 715 71.0 %
Date: 2024-05-31 13:13:24 Functions: 30 30 100.0 %

          Line data    Source code
       1             : /*
       2             :    Unix SMB/CIFS implementation.
       3             :    msDS-ManagedPassword attribute for Group Managed Service Accounts
       4             : 
       5             :    Copyright (C) Catalyst.Net Ltd 2024
       6             : 
       7             :    This program is free software: you can redistribute it and/or modify
       8             :    it under the terms of the GNU General Public License as published by
       9             :    the Free Software Foundation, either version 3 of the License, or
      10             :    (at your option) any later version.
      11             : 
      12             :    This program is distributed in the hope that it will be useful,
      13             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      14             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      15             :    GNU General Public License for more details.
      16             : 
      17             :    You should have received a copy of the GNU General Public License
      18             :    along with this program.  If not, see <https://www.gnu.org/licenses/>.
      19             : */
      20             : 
      21             : #include "includes.h"
      22             : #include "ldb.h"
      23             : #include "ldb_module.h"
      24             : #include "ldb_errors.h"
      25             : #include "ldb_private.h"
      26             : #include "lib/crypto/gkdi.h"
      27             : #include "lib/crypto/gmsa.h"
      28             : #include "lib/util/data_blob.h"
      29             : #include "lib/util/fault.h"
      30             : #include "lib/util/time.h"
      31             : #include "libcli/security/access_check.h"
      32             : #include "librpc/gen_ndr/auth.h"
      33             : #include "librpc/gen_ndr/ndr_gkdi.h"
      34             : #include "librpc/gen_ndr/ndr_gmsa.h"
      35             : #include "librpc/gen_ndr/ndr_security.h"
      36             : #include "dsdb/common/util.h"
      37             : #include "dsdb/gmsa/gkdi.h"
      38             : #include "dsdb/gmsa/util.h"
      39             : #include "dsdb/samdb/samdb.h"
      40             : 
      41             : #undef strcasecmp
      42             : 
      43             : enum RootKeyType {
      44             :         ROOT_KEY_NONE,
      45             :         ROOT_KEY_SPECIFIC,
      46             :         ROOT_KEY_NONSPECIFIC,
      47             :         ROOT_KEY_OBTAINED,
      48             : };
      49             : 
      50             : struct RootKey {
      51             :         TALLOC_CTX *mem_ctx;
      52             :         enum RootKeyType type;
      53             :         union {
      54             :                 struct KeyEnvelopeId specific;
      55             :                 struct {
      56             :                         NTTIME key_start_time;
      57             :                 } nonspecific;
      58             :                 struct {
      59             :                         struct gmsa_update_pwd_part key;
      60             :                         struct gmsa_null_terminated_password *password;
      61             :                 } obtained;
      62             :         } u;
      63             : };
      64             : 
      65             : static const struct RootKey empty_root_key = {.type = ROOT_KEY_NONE};
      66             : 
      67          56 : int gmsa_allowed_to_view_managed_password(TALLOC_CTX *mem_ctx,
      68             :                                           struct ldb_context *ldb,
      69             :                                           const struct ldb_message *msg,
      70             :                                           const struct dom_sid *account_sid,
      71             :                                           bool *allowed_out)
      72             : {
      73          56 :         TALLOC_CTX *tmp_ctx = NULL;
      74          56 :         struct security_descriptor group_msa_membership_sd = {};
      75          56 :         const struct security_token *user_token = NULL;
      76          56 :         NTSTATUS status = NT_STATUS_OK;
      77          56 :         int ret = LDB_SUCCESS;
      78             : 
      79          56 :         if (allowed_out == NULL) {
      80           0 :                 ret = ldb_operr(ldb);
      81           0 :                 goto out;
      82             :         }
      83          56 :         *allowed_out = false;
      84             : 
      85             :         {
      86          56 :                 const struct auth_session_info *session_info = ldb_get_opaque(
      87             :                         ldb, DSDB_SESSION_INFO);
      88           0 :                 const enum security_user_level level =
      89          56 :                         security_session_user_level(session_info, NULL);
      90             : 
      91          56 :                 if (level == SECURITY_SYSTEM) {
      92          16 :                         *allowed_out = true;
      93          16 :                         ret = LDB_SUCCESS;
      94          16 :                         goto out;
      95             :                 }
      96             : 
      97          40 :                 if (session_info == NULL) {
      98           0 :                         ret = dsdb_werror(ldb,
      99             :                                           LDB_ERR_OPERATIONS_ERROR,
     100             :                                           WERR_DS_CANT_RETRIEVE_ATTS,
     101             :                                           "no right to view attribute");
     102           0 :                         goto out;
     103             :                 }
     104             : 
     105          40 :                 user_token = session_info->security_token;
     106             :         }
     107             : 
     108          40 :         tmp_ctx = talloc_new(msg);
     109          40 :         if (tmp_ctx == NULL) {
     110           0 :                 ret = ldb_oom(ldb);
     111           0 :                 goto out;
     112             :         }
     113             : 
     114             :         {
     115          40 :                 const struct ldb_val *group_msa_membership = NULL;
     116           0 :                 enum ndr_err_code err;
     117             : 
     118             :                 /* [MS-ADTS] 3.1.1.4.4: Extended Access Checks. */
     119          40 :                 group_msa_membership = ldb_msg_find_ldb_val(
     120             :                         msg, "msDS-GroupMSAMembership");
     121          40 :                 if (group_msa_membership == NULL) {
     122           0 :                         ret = dsdb_werror(ldb,
     123             :                                           LDB_ERR_OPERATIONS_ERROR,
     124             :                                           WERR_DS_CANT_RETRIEVE_ATTS,
     125             :                                           "no right to view attribute");
     126           0 :                         goto out;
     127             :                 }
     128             : 
     129          40 :                 err = ndr_pull_struct_blob_all(
     130             :                         group_msa_membership,
     131             :                         tmp_ctx,
     132             :                         &group_msa_membership_sd,
     133             :                         (ndr_pull_flags_fn_t)ndr_pull_security_descriptor);
     134          40 :                 if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
     135           0 :                         status = ndr_map_error2ntstatus(err);
     136           0 :                         DBG_WARNING("msDS-GroupMSAMembership pull failed: %s\n",
     137             :                                     nt_errstr(status));
     138           0 :                         ret = ldb_operr(ldb);
     139           0 :                         goto out;
     140             :                 }
     141             :         }
     142             : 
     143             :         {
     144          40 :                 const uint32_t access_desired = SEC_ADS_READ_PROP;
     145          40 :                 uint32_t access_granted = 0;
     146             : 
     147          40 :                 status = sec_access_check_ds(&group_msa_membership_sd,
     148             :                                              user_token,
     149             :                                              access_desired,
     150             :                                              &access_granted,
     151             :                                              NULL,
     152             :                                              account_sid);
     153          40 :                 if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
     154             :                         /*
     155             :                          * The principal is not allowed to view the managed
     156             :                          * password.
     157             :                          */
     158          33 :                 } else if (!NT_STATUS_IS_OK(status)) {
     159           0 :                         DBG_WARNING("msDS-GroupMSAMembership: "
     160             :                                     "sec_access_check_ds(access_desired=%#08x, "
     161             :                                     "access_granted:%#08x) failed with: %s\n",
     162             :                                     access_desired,
     163             :                                     access_granted,
     164             :                                     nt_errstr(status));
     165             : 
     166           0 :                         ret = dsdb_werror(
     167             :                                 ldb,
     168             :                                 LDB_ERR_OPERATIONS_ERROR,
     169             :                                 WERR_DS_CANT_RETRIEVE_ATTS,
     170             :                                 "access check to view managed password failed");
     171           0 :                         goto out;
     172             :                 } else {
     173             :                         /* Cool, the principal may view the password. */
     174          33 :                         *allowed_out = true;
     175             :                 }
     176             :         }
     177             : 
     178          56 : out:
     179          56 :         TALLOC_FREE(tmp_ctx);
     180          56 :         return ret;
     181             : }
     182             : 
     183         129 : static NTSTATUS gmsa_managed_pwd_id(struct ldb_context *ldb,
     184             :                                     TALLOC_CTX *mem_ctx,
     185             :                                     const struct ldb_val *pwd_id_blob,
     186             :                                     const struct ProvRootKey *root_key,
     187             :                                     struct KeyEnvelope *pwd_id_out)
     188             : {
     189         129 :         if (root_key == NULL) {
     190           0 :                 return NT_STATUS_INVALID_PARAMETER;
     191             :         }
     192             : 
     193         129 :         if (pwd_id_blob != NULL) {
     194          31 :                 return gkdi_pull_KeyEnvelope(mem_ctx, pwd_id_blob, pwd_id_out);
     195             :         }
     196             : 
     197             :         {
     198          98 :                 const char *domain_name = NULL;
     199          98 :                 const char *forest_name = NULL;
     200             : 
     201          98 :                 domain_name = samdb_default_domain_name(ldb, mem_ctx);
     202          98 :                 if (domain_name == NULL) {
     203           0 :                         return NT_STATUS_NO_MEMORY;
     204             :                 }
     205             : 
     206          98 :                 forest_name = samdb_forest_name(ldb, mem_ctx);
     207          98 :                 if (forest_name == NULL) {
     208             :                         /* We leak ‘domain_name’, but that can’t be helped. */
     209           0 :                         return NT_STATUS_NO_MEMORY;
     210             :                 }
     211             : 
     212          98 :                 *pwd_id_out = (struct KeyEnvelope){
     213          98 :                         .version = root_key->version,
     214             :                         .flags = ENVELOPE_FLAG_KEY_MAY_ENCRYPT_NEW_DATA,
     215             :                         .domain_name = domain_name,
     216             :                         .forest_name = forest_name,
     217             :                 };
     218             :         }
     219             : 
     220          98 :         return NT_STATUS_OK;
     221             : }
     222             : 
     223         143 : void gmsa_update_managed_pwd_id(struct KeyEnvelope *pwd_id,
     224             :                                 const struct gmsa_update_pwd_part *new_pwd)
     225             : {
     226         143 :         pwd_id->l0_index = new_pwd->gkid.l0_idx;
     227         143 :         pwd_id->l1_index = new_pwd->gkid.l1_idx;
     228         143 :         pwd_id->l2_index = new_pwd->gkid.l2_idx;
     229         143 :         pwd_id->root_key_id = new_pwd->root_key->id;
     230         143 : }
     231             : 
     232         143 : NTSTATUS gmsa_pack_managed_pwd_id(TALLOC_CTX *mem_ctx,
     233             :                                   const struct KeyEnvelope *pwd_id,
     234             :                                   DATA_BLOB *pwd_id_out)
     235             : {
     236         143 :         NTSTATUS status = NT_STATUS_OK;
     237           0 :         enum ndr_err_code err;
     238             : 
     239         143 :         err = ndr_push_struct_blob(pwd_id_out,
     240             :                                    mem_ctx,
     241             :                                    pwd_id,
     242             :                                    (ndr_push_flags_fn_t)ndr_push_KeyEnvelope);
     243         143 :         status = ndr_map_error2ntstatus(err);
     244         143 :         if (!NT_STATUS_IS_OK(status)) {
     245           0 :                 DBG_WARNING("KeyEnvelope push failed: %s\n", nt_errstr(status));
     246             :         }
     247             : 
     248         143 :         return status;
     249             : }
     250             : 
     251          39 : static int gmsa_specific_password(TALLOC_CTX *mem_ctx,
     252             :                                   struct ldb_context *ldb,
     253             :                                   const struct KeyEnvelopeId pwd_id,
     254             :                                   struct gmsa_update_pwd_part *new_pwd_out)
     255             : {
     256          39 :         TALLOC_CTX *tmp_ctx = NULL;
     257          39 :         NTSTATUS status = NT_STATUS_OK;
     258          39 :         int ret = LDB_SUCCESS;
     259             : 
     260          39 :         tmp_ctx = talloc_new(mem_ctx);
     261          39 :         if (tmp_ctx == NULL) {
     262           0 :                 ret = ldb_oom(ldb);
     263           0 :                 goto out;
     264             :         }
     265             : 
     266             :         {
     267          39 :                 const struct ldb_message *root_key_msg = NULL;
     268             : 
     269          39 :                 ret = gkdi_root_key_from_id(tmp_ctx,
     270             :                                             ldb,
     271             :                                             &pwd_id.root_key_id,
     272             :                                             &root_key_msg);
     273          39 :                 if (ret) {
     274           0 :                         goto out;
     275             :                 }
     276             : 
     277          39 :                 status = gkdi_root_key_from_msg(mem_ctx,
     278             :                                                 pwd_id.root_key_id,
     279             :                                                 root_key_msg,
     280             :                                                 &new_pwd_out->root_key);
     281          39 :                 if (!NT_STATUS_IS_OK(status)) {
     282           0 :                         ret = ldb_operr(ldb);
     283           0 :                         goto out;
     284             :                 }
     285             :         }
     286             : 
     287          39 :         new_pwd_out->gkid = pwd_id.gkid;
     288             : 
     289          39 : out:
     290          39 :         talloc_free(tmp_ctx);
     291          39 :         return ret;
     292             : }
     293             : 
     294          53 : static int gmsa_nonspecific_password(TALLOC_CTX *mem_ctx,
     295             :                                      struct ldb_context *ldb,
     296             :                                      const NTTIME key_start_time,
     297             :                                      const NTTIME current_time,
     298             :                                      struct gmsa_update_pwd_part *new_pwd_out)
     299             : {
     300          53 :         TALLOC_CTX *tmp_ctx = NULL;
     301          53 :         int ret = LDB_SUCCESS;
     302             : 
     303          53 :         tmp_ctx = talloc_new(mem_ctx);
     304          53 :         if (tmp_ctx == NULL) {
     305           0 :                 ret = ldb_oom(ldb);
     306           0 :                 goto out;
     307             :         }
     308             : 
     309             :         {
     310          53 :                 const struct ldb_message *root_key_msg = NULL;
     311           0 :                 struct GUID root_key_id;
     312          53 :                 NTSTATUS status = NT_STATUS_OK;
     313             : 
     314          53 :                 ret = gkdi_most_recently_created_root_key(tmp_ctx,
     315             :                                                           ldb,
     316             :                                                           current_time,
     317             :                                                           key_start_time,
     318             :                                                           &root_key_id,
     319             :                                                           &root_key_msg);
     320          53 :                 if (ret) {
     321           0 :                         goto out;
     322             :                 }
     323             : 
     324          53 :                 status = gkdi_root_key_from_msg(mem_ctx,
     325             :                                                 root_key_id,
     326             :                                                 root_key_msg,
     327             :                                                 &new_pwd_out->root_key);
     328          53 :                 if (!NT_STATUS_IS_OK(status)) {
     329           0 :                         ret = ldb_operr(ldb);
     330           0 :                         goto out;
     331             :                 }
     332             :         }
     333             : 
     334          53 :         new_pwd_out->gkid = gkdi_get_interval_id(key_start_time);
     335             : 
     336          53 : out:
     337          53 :         talloc_free(tmp_ctx);
     338          53 :         return ret;
     339             : }
     340             : 
     341          39 : static int gmsa_specifc_root_key(TALLOC_CTX *mem_ctx,
     342             :                                  const struct KeyEnvelopeId pwd_id,
     343             :                                  struct RootKey *root_key_out)
     344             : {
     345          39 :         if (root_key_out == NULL) {
     346           0 :                 return LDB_ERR_OPERATIONS_ERROR;
     347             :         }
     348             : 
     349          39 :         *root_key_out = (struct RootKey){.mem_ctx = mem_ctx,
     350             :                                          .type = ROOT_KEY_SPECIFIC,
     351             :                                          .u.specific = pwd_id};
     352          39 :         return LDB_SUCCESS;
     353             : }
     354             : 
     355          69 : static int gmsa_nonspecifc_root_key(TALLOC_CTX *mem_ctx,
     356             :                                     const NTTIME key_start_time,
     357             :                                     struct RootKey *root_key_out)
     358             : {
     359          69 :         if (root_key_out == NULL) {
     360           0 :                 return LDB_ERR_OPERATIONS_ERROR;
     361             :         }
     362             : 
     363          69 :         *root_key_out = (struct RootKey){
     364             :                 .mem_ctx = mem_ctx,
     365             :                 .type = ROOT_KEY_NONSPECIFIC,
     366             :                 .u.nonspecific.key_start_time = key_start_time};
     367          69 :         return LDB_SUCCESS;
     368             : }
     369             : 
     370          92 : static int gmsa_obtained_root_key_steal(
     371             :         TALLOC_CTX *mem_ctx,
     372             :         const struct gmsa_update_pwd_part key,
     373             :         struct gmsa_null_terminated_password *password,
     374             :         struct RootKey *root_key_out)
     375             : {
     376          92 :         if (root_key_out == NULL) {
     377           0 :                 return LDB_ERR_OPERATIONS_ERROR;
     378             :         }
     379             : 
     380             :         /* Steal the data on to the appropriate memory context. */
     381          92 :         talloc_steal(mem_ctx, key.root_key);
     382          92 :         talloc_steal(mem_ctx, password);
     383             : 
     384          92 :         *root_key_out = (struct RootKey){.mem_ctx = mem_ctx,
     385             :                                          .type = ROOT_KEY_OBTAINED,
     386             :                                          .u.obtained = {.key = key,
     387             :                                                         .password = password}};
     388          92 :         return LDB_SUCCESS;
     389             : }
     390             : 
     391         146 : static int gmsa_fetch_root_key(struct ldb_context *ldb,
     392             :                                const NTTIME current_time,
     393             :                                struct RootKey *root_key,
     394             :                                const struct dom_sid *const account_sid)
     395             : {
     396         146 :         TALLOC_CTX *tmp_ctx = NULL;
     397         146 :         NTSTATUS status = NT_STATUS_OK;
     398         146 :         int ret = LDB_SUCCESS;
     399             : 
     400         146 :         if (root_key == NULL) {
     401           0 :                 ret = ldb_operr(ldb);
     402           0 :                 goto out;
     403             :         }
     404             : 
     405         146 :         switch (root_key->type) {
     406          92 :         case ROOT_KEY_SPECIFIC:
     407             :         case ROOT_KEY_NONSPECIFIC: {
     408          92 :                 struct gmsa_null_terminated_password *password = NULL;
     409           0 :                 struct gmsa_update_pwd_part key;
     410             : 
     411          92 :                 tmp_ctx = talloc_new(NULL);
     412          92 :                 if (tmp_ctx == NULL) {
     413           0 :                         ret = ldb_oom(ldb);
     414           0 :                         goto out;
     415             :                 }
     416             : 
     417          92 :                 if (root_key->type == ROOT_KEY_SPECIFIC) {
     418             :                         /* Search for a specific root key. */
     419          39 :                         ret = gmsa_specific_password(tmp_ctx,
     420             :                                                      ldb,
     421             :                                                      root_key->u.specific,
     422             :                                                      &key);
     423          39 :                         if (ret) {
     424             :                                 /*
     425             :                                  * We couldn’t find a specific key — treat this
     426             :                                  * as an error.
     427             :                                  */
     428           0 :                                 goto out;
     429             :                         }
     430             :                 } else {
     431             :                         /*
     432             :                          * Search for the most recent root key meeting the start
     433             :                          * time requirement.
     434             :                          */
     435          53 :                         ret = gmsa_nonspecific_password(
     436             :                                 tmp_ctx,
     437             :                                 ldb,
     438             :                                 root_key->u.nonspecific.key_start_time,
     439             :                                 current_time,
     440             :                                 &key);
     441             :                         /* Handle errors below. */
     442             :                 }
     443          92 :                 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
     444             :                         /*
     445             :                          * We couldn’t find a key meeting the requirements —
     446             :                          * that’s OK, presumably. It’s not critical if we can’t
     447             :                          * find a key for deriving a previous gMSA password, for
     448             :                          * example.
     449             :                          */
     450           0 :                         ret = LDB_SUCCESS;
     451           0 :                         *root_key = empty_root_key;
     452          92 :                 } else if (ret) {
     453           0 :                         goto out;
     454             :                 } else {
     455             :                         /* Derive the password. */
     456          92 :                         status = gmsa_talloc_password_based_on_key_id(
     457             :                                 tmp_ctx,
     458             :                                 key.gkid,
     459             :                                 current_time,
     460             :                                 key.root_key,
     461             :                                 account_sid,
     462             :                                 &password);
     463          92 :                         if (!NT_STATUS_IS_OK(status)) {
     464           0 :                                 ret = ldb_operr(ldb);
     465           0 :                                 goto out;
     466             :                         }
     467             : 
     468             :                         /*
     469             :                          * Initialize the obtained structure, and give it the
     470             :                          * appropriate memory context.
     471             :                          */
     472          92 :                         ret = gmsa_obtained_root_key_steal(root_key->mem_ctx,
     473             :                                                            key,
     474             :                                                            password,
     475             :                                                            root_key);
     476          92 :                         if (ret) {
     477           0 :                                 goto out;
     478             :                         }
     479             :                 }
     480          92 :         } break;
     481          40 :         case ROOT_KEY_NONE:
     482             :                 /* No key is available. */
     483          40 :                 break;
     484          14 :         case ROOT_KEY_OBTAINED:
     485             :                 /* The key has already been obtained. */
     486          14 :                 break;
     487           0 :         default:
     488           0 :                 ret = ldb_operr(ldb);
     489           0 :                 goto out;
     490             :         }
     491             : 
     492         146 : out:
     493         146 :         TALLOC_FREE(tmp_ctx);
     494         146 :         return ret;
     495             : }
     496             : 
     497             : /*
     498             :  * Get the password and update information associated with a root key. The
     499             :  * caller *does not* own these structures; the root key object retains
     500             :  * ownership.
     501             :  */
     502         146 : static int gmsa_get_root_key(
     503             :         struct ldb_context *ldb,
     504             :         const NTTIME current_time,
     505             :         const struct dom_sid *const account_sid,
     506             :         struct RootKey *root_key,
     507             :         struct gmsa_null_terminated_password **password_out,
     508             :         struct gmsa_update_pwd_part *update_out)
     509             : {
     510         146 :         int ret = LDB_SUCCESS;
     511             : 
     512         146 :         if (password_out == NULL) {
     513           0 :                 ret = ldb_operr(ldb);
     514           0 :                 goto out;
     515             :         }
     516         146 :         *password_out = NULL;
     517             : 
     518         146 :         if (update_out != NULL) {
     519          48 :                 *update_out = (struct gmsa_update_pwd_part){};
     520             :         }
     521             : 
     522             :         /* Fetch the root key from the database and obtain the password. */
     523         146 :         ret = gmsa_fetch_root_key(ldb, current_time, root_key, account_sid);
     524         146 :         if (ret) {
     525           0 :                 goto out;
     526             :         }
     527             : 
     528         146 :         switch (root_key->type) {
     529          40 :         case ROOT_KEY_NONE:
     530             :                 /* No key is available. */
     531          40 :                 break;
     532         106 :         case ROOT_KEY_OBTAINED:
     533         106 :                 *password_out = root_key->u.obtained.password;
     534         106 :                 if (update_out != NULL) {
     535          45 :                         *update_out = root_key->u.obtained.key;
     536             :                 }
     537         106 :                 break;
     538           0 :         default:
     539             :                 /* Unexpected. */
     540           0 :                 ret = ldb_operr(ldb);
     541           0 :                 goto out;
     542             :         }
     543             : 
     544         146 : out:
     545         146 :         return ret;
     546             : }
     547             : 
     548          31 : static int gmsa_system_update_password_id_req(
     549             :         struct ldb_context *ldb,
     550             :         TALLOC_CTX *mem_ctx,
     551             :         const struct ldb_message *msg,
     552             :         const struct gmsa_update_pwd *new_pwd,
     553             :         const bool current_key_becomes_previous,
     554             :         struct ldb_request **req_out)
     555             : {
     556          31 :         TALLOC_CTX *tmp_ctx = NULL;
     557          31 :         const struct ldb_val *pwd_id_blob = ldb_msg_find_ldb_val(
     558             :                 msg, "msDS-ManagedPasswordId");
     559           0 :         struct KeyEnvelope pwd_id;
     560          31 :         struct ldb_message *mod_msg = NULL;
     561          31 :         NTSTATUS status = NT_STATUS_OK;
     562          31 :         int ret = LDB_SUCCESS;
     563             : 
     564          31 :         tmp_ctx = talloc_new(mem_ctx);
     565          31 :         if (tmp_ctx == NULL) {
     566           0 :                 ret = ldb_oom(ldb);
     567           0 :                 goto out;
     568             :         }
     569             : 
     570             :         /* Create a new ldb message. */
     571          31 :         mod_msg = ldb_msg_new(tmp_ctx);
     572          31 :         if (mod_msg == NULL) {
     573           0 :                 ret = ldb_oom(ldb);
     574           0 :                 goto out;
     575             :         }
     576             :         {
     577          31 :                 struct ldb_dn *dn = ldb_dn_copy(mod_msg, msg->dn);
     578          31 :                 if (dn == NULL) {
     579           0 :                         ret = ldb_oom(ldb);
     580           0 :                         goto out;
     581             :                 }
     582          31 :                 mod_msg->dn = dn;
     583             :         }
     584             : 
     585             :         /* Get the Managed Password ID. */
     586          31 :         status = gmsa_managed_pwd_id(
     587          31 :                 ldb, tmp_ctx, pwd_id_blob, new_pwd->new_id.root_key, &pwd_id);
     588          31 :         if (!NT_STATUS_IS_OK(status)) {
     589           0 :                 ret = ldb_operr(ldb);
     590           0 :                 goto out;
     591             :         }
     592             : 
     593             :         /* Update the password ID to contain the new GKID and root key ID. */
     594          31 :         gmsa_update_managed_pwd_id(&pwd_id, &new_pwd->new_id);
     595             : 
     596             :         {
     597          31 :                 DATA_BLOB new_pwd_id_blob = {};
     598             : 
     599             :                 /* Pack the current password ID. */
     600          31 :                 status = gmsa_pack_managed_pwd_id(tmp_ctx,
     601             :                                                   &pwd_id,
     602             :                                                   &new_pwd_id_blob);
     603          31 :                 if (!NT_STATUS_IS_OK(status)) {
     604           0 :                         ret = ldb_operr(ldb);
     605           0 :                         goto out;
     606             :                 }
     607             : 
     608             :                 /* Update the msDS-ManagedPasswordId attribute. */
     609          31 :                 ret = ldb_msg_append_steal_value(mod_msg,
     610             :                                                  "msDS-ManagedPasswordId",
     611             :                                                  &new_pwd_id_blob,
     612             :                                                  LDB_FLAG_MOD_REPLACE);
     613          31 :                 if (ret) {
     614           0 :                         goto out;
     615             :                 }
     616             :         }
     617             : 
     618             :         {
     619          31 :                 DATA_BLOB *prev_pwd_id_blob = NULL;
     620           0 :                 DATA_BLOB _prev_pwd_id_blob;
     621          31 :                 DATA_BLOB prev_pwd_id = {};
     622             : 
     623          31 :                 if (new_pwd->prev_id.root_key != NULL) {
     624             :                         /*
     625             :                          * Update the password ID to contain the previous GKID
     626             :                          * and root key ID.
     627             :                          */
     628          14 :                         gmsa_update_managed_pwd_id(&pwd_id, &new_pwd->prev_id);
     629             : 
     630             :                         /* Pack the previous password ID. */
     631          14 :                         status = gmsa_pack_managed_pwd_id(tmp_ctx,
     632             :                                                           &pwd_id,
     633             :                                                           &prev_pwd_id);
     634          14 :                         if (!NT_STATUS_IS_OK(status)) {
     635           0 :                                 ret = ldb_operr(ldb);
     636           0 :                                 goto out;
     637             :                         }
     638             : 
     639          14 :                         prev_pwd_id_blob = &prev_pwd_id;
     640          17 :                 } else if (current_key_becomes_previous && pwd_id_blob != NULL)
     641             :                 {
     642             :                         /* Copy the current password ID to the previous ID. */
     643          14 :                         _prev_pwd_id_blob = ldb_val_dup(tmp_ctx, pwd_id_blob);
     644          14 :                         if (_prev_pwd_id_blob.length != pwd_id_blob->length) {
     645           0 :                                 ret = ldb_oom(ldb);
     646           0 :                                 goto out;
     647             :                         }
     648             : 
     649          14 :                         prev_pwd_id_blob = &_prev_pwd_id_blob;
     650             :                 }
     651             : 
     652          31 :                 if (prev_pwd_id_blob != NULL) {
     653             :                         /*
     654             :                          * Update the msDS-ManagedPasswordPreviousId attribute.
     655             :                          */
     656          28 :                         ret = ldb_msg_append_steal_value(
     657             :                                 mod_msg,
     658             :                                 "msDS-ManagedPasswordPreviousId",
     659             :                                 prev_pwd_id_blob,
     660             :                                 LDB_FLAG_MOD_REPLACE);
     661          28 :                         if (ret) {
     662           0 :                                 goto out;
     663             :                         }
     664             :                 }
     665             :         }
     666             : 
     667             :         {
     668          31 :                 struct ldb_request *req = NULL;
     669             : 
     670             :                 /* Build the ldb request to return. */
     671          31 :                 ret = ldb_build_mod_req(&req,
     672             :                                         ldb,
     673             :                                         tmp_ctx,
     674             :                                         mod_msg,
     675             :                                         NULL,
     676             :                                         NULL,
     677             :                                         ldb_op_default_callback,
     678             :                                         NULL);
     679          31 :                 if (ret) {
     680           0 :                         goto out;
     681             :                 }
     682             : 
     683             :                 /* Tie the lifetime of the message to that of the request. */
     684          31 :                 talloc_steal(req, mod_msg);
     685             : 
     686             :                 /* Make sure the password ID update happens as System. */
     687          31 :                 ret = dsdb_request_add_controls(req, DSDB_FLAG_AS_SYSTEM);
     688          31 :                 if (ret) {
     689           0 :                         goto out;
     690             :                 }
     691             : 
     692          31 :                 *req_out = talloc_steal(mem_ctx, req);
     693             :         }
     694             : 
     695          31 : out:
     696          31 :         talloc_free(tmp_ctx);
     697          31 :         return ret;
     698             : }
     699             : 
     700          98 : int gmsa_generate_blobs(struct ldb_context *ldb,
     701             :                         TALLOC_CTX *mem_ctx,
     702             :                         const NTTIME current_time,
     703             :                         const struct dom_sid *const account_sid,
     704             :                         DATA_BLOB *pwd_id_blob_out,
     705             :                         struct gmsa_null_terminated_password **password_out)
     706             : {
     707          98 :         TALLOC_CTX *tmp_ctx = NULL;
     708           0 :         struct KeyEnvelope pwd_id;
     709          98 :         const struct ProvRootKey *root_key = NULL;
     710          98 :         NTSTATUS status = NT_STATUS_OK;
     711          98 :         int ret = LDB_SUCCESS;
     712             : 
     713          98 :         tmp_ctx = talloc_new(mem_ctx);
     714          98 :         if (tmp_ctx == NULL) {
     715           0 :                 ret = ldb_oom(ldb);
     716           0 :                 goto out;
     717             :         }
     718             : 
     719             :         {
     720          98 :                 const struct ldb_message *root_key_msg = NULL;
     721           0 :                 struct GUID root_key_id;
     722          98 :                 const NTTIME one_interval = gkdi_key_cycle_duration +
     723             :                                             gkdi_max_clock_skew;
     724          98 :                 const NTTIME one_interval_ago = current_time -
     725          98 :                                                 MIN(one_interval, current_time);
     726             : 
     727          98 :                 ret = gkdi_most_recently_created_root_key(tmp_ctx,
     728             :                                                           ldb,
     729             :                                                           current_time,
     730             :                                                           one_interval_ago,
     731             :                                                           &root_key_id,
     732             :                                                           &root_key_msg);
     733          98 :                 if (ret) {
     734           0 :                         goto out;
     735             :                 }
     736             : 
     737          98 :                 status = gkdi_root_key_from_msg(tmp_ctx,
     738             :                                                 root_key_id,
     739             :                                                 root_key_msg,
     740             :                                                 &root_key);
     741          98 :                 if (!NT_STATUS_IS_OK(status)) {
     742           0 :                         ret = ldb_operr(ldb);
     743           0 :                         goto out;
     744             :                 }
     745             :         }
     746             : 
     747             :         /* Get the Managed Password ID. */
     748          98 :         status = gmsa_managed_pwd_id(ldb, tmp_ctx, NULL, root_key, &pwd_id);
     749          98 :         if (!NT_STATUS_IS_OK(status)) {
     750           0 :                 ret = ldb_operr(ldb);
     751           0 :                 goto out;
     752             :         }
     753             : 
     754             :         {
     755          98 :                 const struct Gkid current_gkid = gkdi_get_interval_id(
     756             :                         current_time);
     757             : 
     758             :                 /* Derive the password. */
     759          98 :                 status = gmsa_talloc_password_based_on_key_id(tmp_ctx,
     760             :                                                               current_gkid,
     761             :                                                               current_time,
     762             :                                                               root_key,
     763             :                                                               account_sid,
     764             :                                                               password_out);
     765          98 :                 if (!NT_STATUS_IS_OK(status)) {
     766           0 :                         ret = ldb_operr(ldb);
     767           0 :                         goto out;
     768             :                 }
     769             : 
     770             :                 {
     771          98 :                         const struct gmsa_update_pwd_part new_id = {
     772             :                                 .root_key = root_key,
     773             :                                 .gkid = current_gkid,
     774             :                         };
     775             : 
     776             :                         /*
     777             :                          * Update the password ID to contain the new GKID and
     778             :                          * root key ID.
     779             :                          */
     780          98 :                         gmsa_update_managed_pwd_id(&pwd_id, &new_id);
     781             :                 }
     782             :         }
     783             : 
     784             :         /* Pack the current password ID. */
     785          98 :         status = gmsa_pack_managed_pwd_id(mem_ctx, &pwd_id, pwd_id_blob_out);
     786          98 :         if (!NT_STATUS_IS_OK(status)) {
     787           0 :                 ret = ldb_operr(ldb);
     788           0 :                 goto out;
     789             :         }
     790             : 
     791             :         /* Transfer ownership of the password to the caller’s memory context. */
     792          98 :         talloc_steal(mem_ctx, *password_out);
     793             : 
     794          98 : out:
     795          98 :         talloc_free(tmp_ctx);
     796          98 :         return ret;
     797             : }
     798             : 
     799          31 : static int gmsa_create_update(TALLOC_CTX *mem_ctx,
     800             :                               struct ldb_context *ldb,
     801             :                               const struct ldb_message *msg,
     802             :                               const NTTIME current_time,
     803             :                               const struct dom_sid *account_sid,
     804             :                               const bool current_key_becomes_previous,
     805             :                               struct RootKey *current_key,
     806             :                               struct RootKey *previous_key,
     807             :                               struct gmsa_update **update_out)
     808             : {
     809          31 :         TALLOC_CTX *tmp_ctx = NULL;
     810          31 :         const DATA_BLOB *found_pwd_id = NULL;
     811          31 :         struct ldb_request *old_pw_req = NULL;
     812          31 :         struct ldb_request *new_pw_req = NULL;
     813          31 :         struct ldb_request *pwd_id_req = NULL;
     814          31 :         struct ldb_dn *account_dn = NULL;
     815          31 :         struct gmsa_update_pwd new_pwd = {};
     816          31 :         struct gmsa_update *update = NULL;
     817          31 :         NTSTATUS status = NT_STATUS_OK;
     818          31 :         int ret = LDB_SUCCESS;
     819             : 
     820          31 :         if (update_out == NULL) {
     821           0 :                 ret = ldb_operr(ldb);
     822           0 :                 goto out;
     823             :         }
     824          31 :         *update_out = NULL;
     825             : 
     826          31 :         if (current_key == NULL) {
     827           0 :                 ret = ldb_operr(ldb);
     828           0 :                 goto out;
     829             :         }
     830             : 
     831          31 :         tmp_ctx = talloc_new(mem_ctx);
     832          31 :         if (tmp_ctx == NULL) {
     833           0 :                 ret = ldb_oom(ldb);
     834           0 :                 goto out;
     835             :         }
     836             : 
     837             :         {
     838             :                 /*
     839             :                  * The password_hash module expects these passwords to be
     840             :                  * null‐terminated.
     841             :                  */
     842          31 :                 struct gmsa_null_terminated_password *new_password = NULL;
     843             : 
     844          31 :                 ret = gmsa_get_root_key(ldb,
     845             :                                         current_time,
     846             :                                         account_sid,
     847             :                                         current_key,
     848             :                                         &new_password,
     849             :                                         &new_pwd.new_id);
     850          31 :                 if (ret) {
     851           0 :                         goto out;
     852             :                 }
     853             : 
     854          31 :                 if (new_password == NULL) {
     855           0 :                         ret = ldb_operr(ldb);
     856           0 :                         goto out;
     857             :                 }
     858             : 
     859          31 :                 status = gmsa_system_password_update_request(
     860          31 :                         ldb, tmp_ctx, msg->dn, new_password->buf, &new_pw_req);
     861          31 :                 if (!NT_STATUS_IS_OK(status)) {
     862           0 :                         ret = ldb_operr(ldb);
     863           0 :                         goto out;
     864             :                 }
     865             :         }
     866             : 
     867             :         /* Does the previous password need to be updated? */
     868          31 :         if (current_key_becomes_previous) {
     869             :                 /*
     870             :                  * When we perform the password set, the now‐current password
     871             :                  * will become the previous password automatically. We don’t
     872             :                  * have to manage that ourselves.
     873             :                  */
     874             :         } else {
     875          17 :                 struct gmsa_null_terminated_password *old_password = NULL;
     876             : 
     877             :                 /* The current key cannot be reused as the previous key. */
     878          17 :                 ret = gmsa_get_root_key(ldb,
     879             :                                         current_time,
     880             :                                         account_sid,
     881             :                                         previous_key,
     882             :                                         &old_password,
     883             :                                         &new_pwd.prev_id);
     884          17 :                 if (ret) {
     885           0 :                         goto out;
     886             :                 }
     887             : 
     888          17 :                 if (old_password != NULL) {
     889          14 :                         status = gmsa_system_password_update_request(
     890             :                                 ldb,
     891             :                                 tmp_ctx,
     892          14 :                                 msg->dn,
     893          14 :                                 old_password->buf,
     894             :                                 &old_pw_req);
     895          14 :                         if (!NT_STATUS_IS_OK(status)) {
     896           0 :                                 ret = ldb_operr(ldb);
     897           0 :                                 goto out;
     898             :                         }
     899             :                 }
     900             :         }
     901             : 
     902             :         /* Ready the update of the msDS-ManagedPasswordId attribute. */
     903          31 :         ret = gmsa_system_update_password_id_req(ldb,
     904             :                                                  tmp_ctx,
     905             :                                                  msg,
     906             :                                                  &new_pwd,
     907             :                                                  current_key_becomes_previous,
     908             :                                                  &pwd_id_req);
     909          31 :         if (ret) {
     910           0 :                 goto out;
     911             :         }
     912             : 
     913             :         {
     914             :                 /*
     915             :                  * Remember the original managed password ID so that we can
     916             :                  * confirm it hasn’t changed when we perform the update.
     917             :                  */
     918             : 
     919          31 :                 const struct ldb_val *pwd_id_blob = ldb_msg_find_ldb_val(
     920             :                         msg, "msDS-ManagedPasswordId");
     921             : 
     922          31 :                 if (pwd_id_blob != NULL) {
     923          31 :                         DATA_BLOB found_pwd_id_data = {};
     924          31 :                         DATA_BLOB *found_pwd_id_blob = NULL;
     925             : 
     926          31 :                         found_pwd_id_blob = talloc(tmp_ctx, DATA_BLOB);
     927          31 :                         if (found_pwd_id_blob == NULL) {
     928           0 :                                 ret = ldb_oom(ldb);
     929           0 :                                 goto out;
     930             :                         }
     931             : 
     932          31 :                         found_pwd_id_data = data_blob_dup_talloc(
     933             :                                 found_pwd_id_blob, *pwd_id_blob);
     934          31 :                         if (found_pwd_id_data.length != pwd_id_blob->length) {
     935           0 :                                 ret = ldb_oom(ldb);
     936           0 :                                 goto out;
     937             :                         }
     938             : 
     939          31 :                         *found_pwd_id_blob = found_pwd_id_data;
     940          31 :                         found_pwd_id = found_pwd_id_blob;
     941             :                 }
     942             :         }
     943             : 
     944          31 :         account_dn = ldb_dn_copy(tmp_ctx, msg->dn);
     945          31 :         if (account_dn == NULL) {
     946           0 :                 ret = ldb_oom(ldb);
     947           0 :                 goto out;
     948             :         }
     949             : 
     950          31 :         update = talloc(tmp_ctx, struct gmsa_update);
     951          31 :         if (update == NULL) {
     952           0 :                 ret = ldb_oom(ldb);
     953           0 :                 goto out;
     954             :         }
     955             : 
     956          31 :         *update = (struct gmsa_update){
     957          31 :                 .dn = talloc_steal(update, account_dn),
     958          31 :                 .found_pwd_id = talloc_steal(update, found_pwd_id),
     959          31 :                 .old_pw_req = talloc_steal(update, old_pw_req),
     960          31 :                 .new_pw_req = talloc_steal(update, new_pw_req),
     961          31 :                 .pwd_id_req = talloc_steal(update, pwd_id_req)};
     962             : 
     963          31 :         *update_out = talloc_steal(mem_ctx, update);
     964             : 
     965          31 : out:
     966          31 :         TALLOC_FREE(tmp_ctx);
     967          31 :         return ret;
     968             : }
     969             : 
     970          49 : NTSTATUS gmsa_pack_managed_pwd(TALLOC_CTX *mem_ctx,
     971             :                                const uint8_t *new_password,
     972             :                                const uint8_t *old_password,
     973             :                                uint64_t query_interval,
     974             :                                uint64_t unchanged_interval,
     975             :                                DATA_BLOB *managed_pwd_out)
     976             : {
     977          49 :         const struct MANAGEDPASSWORD_BLOB managed_pwd = {
     978             :                 .passwords = {.current = new_password,
     979             :                               .previous = old_password,
     980             :                               .query_interval = &query_interval,
     981             :                               .unchanged_interval = &unchanged_interval}};
     982          49 :         NTSTATUS status = NT_STATUS_OK;
     983           0 :         enum ndr_err_code err;
     984             : 
     985          49 :         err = ndr_push_struct_blob(managed_pwd_out,
     986             :                                    mem_ctx,
     987             :                                    &managed_pwd,
     988             :                                    (ndr_push_flags_fn_t)
     989             :                                            ndr_push_MANAGEDPASSWORD_BLOB);
     990          49 :         status = ndr_map_error2ntstatus(err);
     991          49 :         if (!NT_STATUS_IS_OK(status)) {
     992           0 :                 DBG_WARNING("MANAGEDPASSWORD_BLOB push failed: %s\n",
     993             :                             nt_errstr(status));
     994             :         }
     995             : 
     996          49 :         return status;
     997             : }
     998             : 
     999      384772 : bool dsdb_account_is_gmsa(struct ldb_context *ldb,
    1000             :                           const struct ldb_message *msg)
    1001             : {
    1002             :         /*
    1003             :          * Check if the account has objectClass
    1004             :          * ‘msDS-GroupManagedServiceAccount’.
    1005             :          */
    1006      384772 :         return samdb_find_attribute(ldb,
    1007             :                                     msg,
    1008             :                                     "objectclass",
    1009      384772 :                                     "msDS-GroupManagedServiceAccount") != NULL;
    1010             : }
    1011             : 
    1012             : static struct new_key {
    1013             :         NTTIME start_time;
    1014             :         bool immediately_follows_previous;
    1015          31 : } calculate_new_key(const NTTIME current_time,
    1016             :                     const NTTIME current_key_expiration_time,
    1017             :                     const NTTIME rollover_interval)
    1018             : {
    1019          31 :         NTTIME new_key_start_time = current_key_expiration_time;
    1020          31 :         bool immediately_follows_previous = false;
    1021             : 
    1022          31 :         if (new_key_start_time < current_time && rollover_interval) {
    1023             :                 /*
    1024             :                  * Advance the key start time by the rollover interval until it
    1025             :                  * would be greater than the current time.
    1026             :                  */
    1027          27 :                 const NTTIME time_to_advance_by = current_time + 1 -
    1028             :                                                   new_key_start_time;
    1029          27 :                 const uint64_t stale_count = time_to_advance_by /
    1030             :                                              rollover_interval;
    1031          27 :                 new_key_start_time += stale_count * rollover_interval;
    1032             : 
    1033          27 :                 SMB_ASSERT(new_key_start_time <= current_time);
    1034             : 
    1035          27 :                 immediately_follows_previous = stale_count == 0;
    1036             :         } else {
    1037             :                 /*
    1038             :                  * It is possible that new_key_start_time ≥ current_time;
    1039             :                  * specifically, if there is no password ID, and the creation
    1040             :                  * time of the gMSA is in the future (perhaps due to replication
    1041             :                  * weirdness).
    1042             :                  */
    1043           0 :         }
    1044             : 
    1045          31 :         return (struct new_key){
    1046             :                 .start_time = new_key_start_time,
    1047             :                 .immediately_follows_previous = immediately_follows_previous};
    1048             : }
    1049             : 
    1050         163 : static bool gmsa_creation_time(const struct ldb_message *msg,
    1051             :                                const NTTIME current_time,
    1052             :                                NTTIME *creation_time_out)
    1053             : {
    1054         163 :         const struct ldb_val *when_created = NULL;
    1055           0 :         time_t creation_unix_time;
    1056           0 :         int ret;
    1057             : 
    1058         163 :         when_created = ldb_msg_find_ldb_val(msg, "whenCreated");
    1059         163 :         ret = ldb_val_to_time(when_created, &creation_unix_time);
    1060         163 :         if (ret) {
    1061             :                 /* Fail if we can’t read the attribute or it isn’t present. */
    1062           0 :                 return false;
    1063             :         }
    1064             : 
    1065         163 :         unix_to_nt_time(creation_time_out, creation_unix_time);
    1066         163 :         return true;
    1067             : }
    1068             : 
    1069         172 : static const struct KeyEnvelopeId *gmsa_get_managed_pwd_id_attr_name(
    1070             :         const struct ldb_message *msg,
    1071             :         const char *attr_name,
    1072             :         struct KeyEnvelopeId *key_env_out)
    1073             : {
    1074         172 :         const struct ldb_val *pwd_id_blob = ldb_msg_find_ldb_val(msg,
    1075             :                                                                  attr_name);
    1076         172 :         if (pwd_id_blob == NULL) {
    1077           4 :                 return NULL;
    1078             :         }
    1079             : 
    1080         168 :         return gkdi_pull_KeyEnvelopeId(*pwd_id_blob, key_env_out);
    1081             : }
    1082             : 
    1083         168 : const struct KeyEnvelopeId *gmsa_get_managed_pwd_id(
    1084             :         const struct ldb_message *msg,
    1085             :         struct KeyEnvelopeId *key_env_out)
    1086             : {
    1087         168 :         return gmsa_get_managed_pwd_id_attr_name(msg,
    1088             :                                                  "msDS-ManagedPasswordId",
    1089             :                                                  key_env_out);
    1090             : }
    1091             : 
    1092           4 : static const struct KeyEnvelopeId *gmsa_get_managed_pwd_prev_id(
    1093             :         const struct ldb_message *msg,
    1094             :         struct KeyEnvelopeId *key_env_out)
    1095             : {
    1096           4 :         return gmsa_get_managed_pwd_id_attr_name(
    1097             :                 msg, "msDS-ManagedPasswordPreviousId", key_env_out);
    1098             : }
    1099             : 
    1100         163 : static bool samdb_result_gkdi_rollover_interval(const struct ldb_message *msg,
    1101             :                                                 NTTIME *rollover_interval_out)
    1102             : {
    1103           0 :         int64_t managed_password_interval;
    1104             : 
    1105         163 :         managed_password_interval = ldb_msg_find_attr_as_int64(
    1106             :                 msg, "msDS-ManagedPasswordInterval", 30);
    1107         163 :         return gkdi_rollover_interval(managed_password_interval,
    1108             :                                       rollover_interval_out);
    1109             : }
    1110             : 
    1111           5 : bool samdb_gmsa_key_is_recent(const struct ldb_message *msg,
    1112             :                               const NTTIME current_time)
    1113             : {
    1114           5 :         const struct KeyEnvelopeId *pwd_id = NULL;
    1115           0 :         struct KeyEnvelopeId pwd_id_buf;
    1116           0 :         NTTIME key_start_time;
    1117           0 :         bool ok;
    1118             : 
    1119           5 :         pwd_id = gmsa_get_managed_pwd_id(msg, &pwd_id_buf);
    1120           5 :         if (pwd_id == NULL) {
    1121           0 :                 return false;
    1122             :         }
    1123             : 
    1124           5 :         ok = gkdi_get_key_start_time(pwd_id->gkid, &key_start_time);
    1125           5 :         if (!ok) {
    1126           0 :                 return false;
    1127             :         }
    1128             : 
    1129           5 :         if (current_time < key_start_time) {
    1130           0 :                 return false;
    1131             :         }
    1132             : 
    1133           5 :         return current_time - key_start_time < gkdi_max_clock_skew;
    1134             : }
    1135             : 
    1136             : /*
    1137             :  * Recalculate the managed password of an account. The account referred to by
    1138             :  * ‘msg’ should be a Group Managed Service Account.
    1139             :  *
    1140             :  * Updated passwords are returned in ‘update_out’.
    1141             :  *
    1142             :  * Pass in a non‐NULL pointer for ‘return_out’ if you want the passwords as
    1143             :  * reflected by the msDS-ManagedPassword operational attribute.
    1144             :  */
    1145         163 : int gmsa_recalculate_managed_pwd(TALLOC_CTX *mem_ctx,
    1146             :                                  struct ldb_context *ldb,
    1147             :                                  const struct ldb_message *msg,
    1148             :                                  const NTTIME current_time,
    1149             :                                  struct gmsa_update **update_out,
    1150             :                                  struct gmsa_return_pwd *return_out)
    1151             : {
    1152         163 :         TALLOC_CTX *tmp_ctx = NULL;
    1153         163 :         int ret = LDB_SUCCESS;
    1154           0 :         NTTIME rollover_interval;
    1155           0 :         NTTIME current_key_expiration_time;
    1156           0 :         NTTIME key_expiration_time;
    1157           0 :         struct dom_sid account_sid;
    1158           0 :         struct KeyEnvelopeId pwd_id_buf;
    1159         163 :         const struct KeyEnvelopeId *pwd_id = NULL;
    1160         163 :         struct RootKey previous_key = empty_root_key;
    1161         163 :         struct RootKey current_key = empty_root_key;
    1162         163 :         struct gmsa_update *update = NULL;
    1163         163 :         struct gmsa_null_terminated_password *previous_password = NULL;
    1164         163 :         struct gmsa_null_terminated_password *current_password = NULL;
    1165         163 :         NTTIME query_interval = 0;
    1166         163 :         NTTIME unchanged_interval = 0;
    1167         163 :         NTTIME creation_time = 0;
    1168         163 :         NTTIME account_age = 0;
    1169         163 :         NTTIME key_start_time = 0;
    1170         163 :         bool have_key_start_time = false;
    1171         163 :         bool ok = true;
    1172         163 :         bool current_key_is_valid = false;
    1173             : 
    1174         163 :         if (update_out == NULL) {
    1175           0 :                 ret = ldb_operr(ldb);
    1176           0 :                 goto out;
    1177             :         }
    1178         163 :         *update_out = NULL;
    1179             : 
    1180             :         /* Calculate the rollover interval. */
    1181         163 :         ok = samdb_result_gkdi_rollover_interval(msg, &rollover_interval);
    1182         163 :         if (!ok || rollover_interval == 0) {
    1183             :                 /* We can’t do anything if the rollover interval is zero. */
    1184           0 :                 ret = ldb_operr(ldb);
    1185           0 :                 goto out;
    1186             :         }
    1187             : 
    1188         163 :         ok = gmsa_creation_time(msg, current_time, &creation_time);
    1189         163 :         if (!ok) {
    1190           0 :                 return ldb_error(ldb,
    1191             :                                  LDB_ERR_OPERATIONS_ERROR,
    1192             :                                  "unable to determine creation time of Group "
    1193             :                                  "Managed Service Account");
    1194             :         }
    1195         163 :         account_age = current_time - MIN(creation_time, current_time);
    1196             : 
    1197             :         /* Calculate the expiration time of the current key. */
    1198         163 :         pwd_id = gmsa_get_managed_pwd_id(msg, &pwd_id_buf);
    1199         326 :         if (pwd_id != NULL &&
    1200         163 :             gkdi_get_key_start_time(pwd_id->gkid, &key_start_time))
    1201             :         {
    1202         163 :                 have_key_start_time = true;
    1203             : 
    1204             :                 /* Check for overflow. */
    1205         163 :                 if (key_start_time > UINT64_MAX - rollover_interval) {
    1206           0 :                         ret = ldb_operr(ldb);
    1207           0 :                         goto out;
    1208             :                 }
    1209         163 :                 current_key_expiration_time = key_start_time +
    1210             :                                               rollover_interval;
    1211             :         } else {
    1212             :                 /*
    1213             :                  * [MS-ADTS] does not say to use gkdi_get_interval_start_time(),
    1214             :                  * but surely it makes no sense to have keys starting or ending
    1215             :                  * at random times.
    1216             :                  */
    1217           0 :                 current_key_expiration_time = gkdi_get_interval_start_time(
    1218             :                         creation_time);
    1219             :         }
    1220             : 
    1221             :         /* Fetch the account’s SID, necessary for deriving passwords. */
    1222         163 :         ret = samdb_result_dom_sid_buf(msg, "objectSid", &account_sid);
    1223         163 :         if (ret) {
    1224           0 :                 goto out;
    1225             :         }
    1226             : 
    1227         163 :         tmp_ctx = talloc_new(mem_ctx);
    1228         163 :         if (tmp_ctx == NULL) {
    1229           0 :                 ret = ldb_oom(ldb);
    1230           0 :                 goto out;
    1231             :         }
    1232             : 
    1233             :         /*
    1234             :          * In determining whether the account’s passwords should be updated, we
    1235             :          * do not validate that the unicodePwd attribute is up‐to‐date, or even
    1236             :          * that it exists. We rely entirely on the fact that the managed
    1237             :          * password ID should be updated *only* as part of a successful gMSA
    1238             :          * password update. In any case, unicodePwd is optional in Samba — save
    1239             :          * for machine accounts (which gMSAs are :)) — and we can’t always rely
    1240             :          * on its presence.
    1241             :          *
    1242             :          * All this means that an admin (or a DC that doesn’t support gMSAs)
    1243             :          * could reset a gMSA’s password outside of the normal procedure, and
    1244             :          * the account would then have the wrong password until the key was due
    1245             :          * to roll over again. There’s nothing much we can do about this if we
    1246             :          * don’t want to re‐derive and verify the password every time we look up
    1247             :          * the keys.
    1248             :          */
    1249             : 
    1250             :         /*
    1251             :          * Administrators should be careful not to set a DC’s clock too far in
    1252             :          * the future, or a gMSA’s keys may be stuck at that future time and
    1253             :          * stop updating until said time rolls around for real.
    1254             :          */
    1255             : 
    1256         163 :         current_key_is_valid = pwd_id != NULL &&
    1257           0 :                                current_time < current_key_expiration_time;
    1258         163 :         if (current_key_is_valid) {
    1259         132 :                 key_expiration_time = current_key_expiration_time;
    1260             : 
    1261         132 :                 if (return_out != NULL) {
    1262           0 :                         struct KeyEnvelopeId prev_pwd_id_buf;
    1263          39 :                         const struct KeyEnvelopeId *prev_pwd_id = NULL;
    1264             : 
    1265          39 :                         ret = gmsa_specifc_root_key(tmp_ctx,
    1266             :                                                     *pwd_id,
    1267             :                                                     &current_key);
    1268          39 :                         if (ret) {
    1269           0 :                                 goto out;
    1270             :                         }
    1271             : 
    1272          39 :                         if (account_age >= rollover_interval) {
    1273           4 :                                 prev_pwd_id = gmsa_get_managed_pwd_prev_id(
    1274             :                                         msg, &prev_pwd_id_buf);
    1275           4 :                                 if (prev_pwd_id != NULL) {
    1276           0 :                                         ret = gmsa_specifc_root_key(
    1277             :                                                 tmp_ctx,
    1278             :                                                 *prev_pwd_id,
    1279             :                                                 &previous_key);
    1280           0 :                                         if (ret) {
    1281           0 :                                                 goto out;
    1282             :                                         }
    1283           4 :                                 } else if (have_key_start_time &&
    1284           4 :                                            key_start_time >= rollover_interval)
    1285             :                                 {
    1286             :                                         /*
    1287             :                                          * The account’s old enough to have a
    1288             :                                          * previous password, but it doesn’t
    1289             :                                          * have a previous password ID for some
    1290             :                                          * reason. This can happen in our tests
    1291             :                                          * (python/samba/krb5/gmsa_tests.py)
    1292             :                                          * when we’re mucking about with times.
    1293             :                                          * Just produce what would have been the
    1294             :                                          * previous key.
    1295             :                                          */
    1296           4 :                                         ret = gmsa_nonspecifc_root_key(
    1297             :                                                 tmp_ctx,
    1298             :                                                 key_start_time -
    1299             :                                                         rollover_interval,
    1300             :                                                 &previous_key);
    1301           4 :                                         if (ret) {
    1302           0 :                                                 goto out;
    1303             :                                         }
    1304             :                                 }
    1305             :                         } else {
    1306             :                                 /*
    1307             :                                  * The account is not old enough to have a
    1308             :                                  * previous password. The old password will not
    1309             :                                  * be returned.
    1310             :                                  */
    1311           0 :                         }
    1312             :                 }
    1313             :         } else {
    1314             :                 /* Calculate the start time of the new key. */
    1315          31 :                 const struct new_key new_key = calculate_new_key(
    1316             :                         current_time,
    1317             :                         current_key_expiration_time,
    1318             :                         rollover_interval);
    1319          31 :                 const bool current_key_becomes_previous =
    1320          31 :                         pwd_id != NULL && new_key.immediately_follows_previous;
    1321             : 
    1322             :                 /* Check for overflow. */
    1323          31 :                 if (new_key.start_time > UINT64_MAX - rollover_interval) {
    1324           0 :                         ret = ldb_operr(ldb);
    1325           0 :                         goto out;
    1326             :                 }
    1327          31 :                 key_expiration_time = new_key.start_time + rollover_interval;
    1328             : 
    1329          31 :                 ret = gmsa_nonspecifc_root_key(tmp_ctx,
    1330          31 :                                                new_key.start_time,
    1331             :                                                &current_key);
    1332          31 :                 if (ret) {
    1333           0 :                         goto out;
    1334             :                 }
    1335             : 
    1336          31 :                 if (account_age >= rollover_interval) {
    1337             :                         /* Check for underflow. */
    1338          28 :                         if (new_key.start_time < rollover_interval) {
    1339           0 :                                 ret = ldb_operr(ldb);
    1340           0 :                                 goto out;
    1341             :                         }
    1342          28 :                         ret = gmsa_nonspecifc_root_key(
    1343             :                                 tmp_ctx,
    1344          28 :                                 new_key.start_time - rollover_interval,
    1345             :                                 &previous_key);
    1346          28 :                         if (ret) {
    1347           0 :                                 goto out;
    1348             :                         }
    1349             :                 } else {
    1350             :                         /*
    1351             :                          * The account is not old enough to have a previous
    1352             :                          * password. The old password will not be returned.
    1353             :                          */
    1354           0 :                 }
    1355             : 
    1356             :                 /*
    1357             :                  * The current GMSA key, according to the Managed Password ID,
    1358             :                  * is no longer valid. We should update the account’s Managed
    1359             :                  * Password ID and keys in anticipation of their being needed in
    1360             :                  * the near future.
    1361             :                  */
    1362             : 
    1363          31 :                 ret = gmsa_create_update(tmp_ctx,
    1364             :                                          ldb,
    1365             :                                          msg,
    1366             :                                          current_time,
    1367             :                                          &account_sid,
    1368             :                                          current_key_becomes_previous,
    1369             :                                          &current_key,
    1370             :                                          &previous_key,
    1371             :                                          &update);
    1372          31 :                 if (ret) {
    1373           0 :                         goto out;
    1374             :                 }
    1375             :         }
    1376             : 
    1377         163 :         if (return_out != NULL) {
    1378           0 :                 bool return_future_key;
    1379             : 
    1380          49 :                 unchanged_interval = query_interval = key_expiration_time -
    1381          49 :                                                       MIN(current_time,
    1382             :                                                           key_expiration_time);
    1383             : 
    1384             :                 /* Derive the current and previous passwords. */
    1385          49 :                 return_future_key = query_interval <= gkdi_max_clock_skew;
    1386          49 :                 if (return_future_key) {
    1387           6 :                         struct RootKey future_key = empty_root_key;
    1388             : 
    1389             :                         /*
    1390             :                          * The current key hasn’t expired yet, but it
    1391             :                          * soon will. Return a new key that will be valid in the
    1392             :                          * next epoch.
    1393             :                          */
    1394             : 
    1395           6 :                         ret = gmsa_nonspecifc_root_key(tmp_ctx,
    1396             :                                                        key_expiration_time,
    1397             :                                                        &future_key);
    1398           6 :                         if (ret) {
    1399           0 :                                 goto out;
    1400             :                         }
    1401             : 
    1402           6 :                         ret = gmsa_get_root_key(ldb,
    1403             :                                                 current_time,
    1404             :                                                 &account_sid,
    1405             :                                                 &future_key,
    1406             :                                                 &current_password,
    1407             :                                                 NULL);
    1408           6 :                         if (ret) {
    1409           0 :                                 goto out;
    1410             :                         }
    1411             : 
    1412           6 :                         ret = gmsa_get_root_key(ldb,
    1413             :                                                 current_time,
    1414             :                                                 &account_sid,
    1415             :                                                 &current_key,
    1416             :                                                 &previous_password,
    1417             :                                                 NULL);
    1418           6 :                         if (ret) {
    1419           0 :                                 goto out;
    1420             :                         }
    1421             : 
    1422             :                         /* Check for overflow. */
    1423           6 :                         if (unchanged_interval > UINT64_MAX - rollover_interval)
    1424             :                         {
    1425           0 :                                 ret = ldb_operr(ldb);
    1426           0 :                                 goto out;
    1427             :                         }
    1428           6 :                         unchanged_interval += rollover_interval;
    1429             :                 } else {
    1430             :                         /*
    1431             :                          * Note that a gMSA will become unusable (at least until
    1432             :                          * the next rollover) if its associated root key is ever
    1433             :                          * deleted.
    1434             :                          */
    1435             : 
    1436          43 :                         ret = gmsa_get_root_key(ldb,
    1437             :                                                 current_time,
    1438             :                                                 &account_sid,
    1439             :                                                 &current_key,
    1440             :                                                 &current_password,
    1441             :                                                 NULL);
    1442          43 :                         if (ret) {
    1443           0 :                                 goto out;
    1444             :                         }
    1445             : 
    1446          43 :                         ret = gmsa_get_root_key(ldb,
    1447             :                                                 current_time,
    1448             :                                                 &account_sid,
    1449             :                                                 &previous_key,
    1450             :                                                 &previous_password,
    1451             :                                                 NULL);
    1452          43 :                         if (ret) {
    1453           0 :                                 goto out;
    1454             :                         }
    1455             :                 }
    1456             : 
    1457          49 :                 unchanged_interval -= MIN(gkdi_max_clock_skew,
    1458             :                                           unchanged_interval);
    1459             :         }
    1460             : 
    1461         163 :         *update_out = talloc_steal(mem_ctx, update);
    1462         163 :         if (return_out != NULL) {
    1463          49 :                 *return_out = (struct gmsa_return_pwd){
    1464          49 :                         .prev_pwd = talloc_steal(mem_ctx, previous_password),
    1465          49 :                         .new_pwd = talloc_steal(mem_ctx, current_password),
    1466             :                         .query_interval = query_interval,
    1467             :                         .unchanged_interval = unchanged_interval,
    1468             :                 };
    1469             :         }
    1470             : 
    1471         114 : out:
    1472         163 :         TALLOC_FREE(tmp_ctx);
    1473         163 :         return ret;
    1474             : }
    1475             : 
    1476          23 : static void gmsa_update_debug(const struct gmsa_update *gmsa_update)
    1477             : {
    1478          23 :         struct ldb_dn *dn = NULL;
    1479          23 :         const char *account_dn = "<unknown>";
    1480             : 
    1481          23 :         if (!CHECK_DEBUGLVL(DBGLVL_NOTICE)) {
    1482          23 :                 return;
    1483             :         }
    1484             : 
    1485           0 :         dn = gmsa_update->dn;
    1486           0 :         if (dn != NULL) {
    1487           0 :                 const char *dn_str = NULL;
    1488             : 
    1489           0 :                 dn_str = ldb_dn_get_linearized(dn);
    1490           0 :                 if (dn_str != NULL) {
    1491           0 :                         account_dn = dn_str;
    1492             :                 }
    1493             :         }
    1494             : 
    1495           0 :         DBG_NOTICE("Updating keys for Group Managed Service Account %s\n",
    1496             :                    account_dn);
    1497             : }
    1498             : 
    1499          69 : static int gmsa_perform_request(struct ldb_context *ldb,
    1500             :                                 struct ldb_request *req)
    1501             : {
    1502          69 :         int ret = LDB_SUCCESS;
    1503             : 
    1504          69 :         if (req == NULL) {
    1505          15 :                 return LDB_SUCCESS;
    1506             :         }
    1507             : 
    1508          54 :         ret = ldb_request(ldb, req);
    1509          54 :         if (ret) {
    1510           0 :                 return ret;
    1511             :         }
    1512             : 
    1513          54 :         return ldb_wait(req->handle, LDB_WAIT_ALL);
    1514             : }
    1515             : 
    1516          23 : static bool dsdb_data_blobs_equal(const DATA_BLOB *d1, const DATA_BLOB *d2)
    1517             : {
    1518          23 :         if (d1 == NULL && d2 == NULL) {
    1519           0 :                 return true;
    1520             :         }
    1521             : 
    1522          23 :         if (d1 == NULL || d2 == NULL) {
    1523           0 :                 return false;
    1524             :         }
    1525             : 
    1526             :         {
    1527          23 :                 const int cmp = data_blob_cmp(d1, d2);
    1528          23 :                 return cmp == 0;
    1529             :         }
    1530             : }
    1531             : 
    1532          23 : int dsdb_update_gmsa_entry_keys(struct ldb_context *ldb,
    1533             :                                 TALLOC_CTX *mem_ctx,
    1534             :                                 const struct gmsa_update *gmsa_update)
    1535             : {
    1536          23 :         TALLOC_CTX *tmp_ctx = NULL;
    1537          23 :         int ret = LDB_SUCCESS;
    1538          23 :         bool in_transaction = false;
    1539             : 
    1540          23 :         if (gmsa_update == NULL) {
    1541           0 :                 ret = ldb_operr(ldb);
    1542           0 :                 goto out;
    1543             :         }
    1544             : 
    1545          23 :         tmp_ctx = talloc_new(mem_ctx);
    1546          23 :         if (tmp_ctx == NULL) {
    1547           0 :                 ret = ldb_oom(ldb);
    1548           0 :                 goto out;
    1549             :         }
    1550             : 
    1551          23 :         gmsa_update_debug(gmsa_update);
    1552             : 
    1553             :         /* The following must take place in a single transaction. */
    1554          23 :         ret = ldb_transaction_start(ldb);
    1555          23 :         if (ret) {
    1556           0 :                 goto out;
    1557             :         }
    1558          23 :         in_transaction = true;
    1559             : 
    1560             :         {
    1561             :                 /*
    1562             :                  * Before performing the update, ensure that the managed
    1563             :                  * password ID in the database has the value we expect.
    1564             :                  */
    1565             : 
    1566          23 :                 struct ldb_result *res = NULL;
    1567          23 :                 const struct ldb_val *pwd_id_blob = NULL;
    1568           0 :                 static const char *const managed_pwd_id_attr[] = {
    1569             :                         "msDS-ManagedPasswordId",
    1570             :                         NULL,
    1571             :                 };
    1572             : 
    1573          23 :                 if (gmsa_update->dn == NULL) {
    1574           0 :                         ret = ldb_operr(ldb);
    1575           0 :                         goto out;
    1576             :                 }
    1577             : 
    1578          23 :                 ret = dsdb_search_dn(ldb,
    1579             :                                      tmp_ctx,
    1580             :                                      &res,
    1581          23 :                                      gmsa_update->dn,
    1582             :                                      managed_pwd_id_attr,
    1583             :                                      0);
    1584          23 :                 if (ret) {
    1585           0 :                         goto out;
    1586             :                 }
    1587             : 
    1588          23 :                 if (res->count != 1) {
    1589           0 :                         ret = ldb_error(
    1590             :                                 ldb,
    1591             :                                 LDB_ERR_NO_SUCH_OBJECT,
    1592             :                                 "failed to find Group Managed Service Account "
    1593             :                                 "to verify managed password ID");
    1594           0 :                         goto out;
    1595             :                 }
    1596             : 
    1597          23 :                 pwd_id_blob = ldb_msg_find_ldb_val(res->msgs[0],
    1598             :                                                    "msDS-ManagedPasswordId");
    1599          23 :                 if (!dsdb_data_blobs_equal(pwd_id_blob,
    1600          23 :                                            gmsa_update->found_pwd_id))
    1601             :                 {
    1602             :                         /*
    1603             :                          * The account’s managed password ID doesn’t match what
    1604             :                          * we thought it was — cancel the update. If the caller
    1605             :                          * needs the latest values, it will retry the search,
    1606             :                          * performing the update again if necessary.
    1607             :                          */
    1608           0 :                         ret = LDB_SUCCESS;
    1609           0 :                         goto out;
    1610             :                 }
    1611             :         }
    1612             : 
    1613             :         /*
    1614             :          * First update the previous password (if the request is not NULL,
    1615             :          * indicating that the previous password already matches the password of
    1616             :          * the account).
    1617             :          */
    1618          23 :         ret = gmsa_perform_request(ldb, gmsa_update->old_pw_req);
    1619          23 :         if (ret) {
    1620           0 :                 goto out;
    1621             :         }
    1622             : 
    1623             :         /* Then update the current password. */
    1624          23 :         ret = gmsa_perform_request(ldb, gmsa_update->new_pw_req);
    1625          23 :         if (ret) {
    1626           0 :                 goto out;
    1627             :         }
    1628             : 
    1629             :         /* Finally, update the msDS-ManagedPasswordId attribute. */
    1630          23 :         ret = gmsa_perform_request(ldb, gmsa_update->pwd_id_req);
    1631          23 :         if (ret) {
    1632           0 :                 goto out;
    1633             :         }
    1634             : 
    1635             :         /* Commit the transaction. */
    1636          23 :         ret = ldb_transaction_commit(ldb);
    1637          23 :         in_transaction = false;
    1638          23 :         if (ret) {
    1639           0 :                 goto out;
    1640             :         }
    1641             : 
    1642          23 : out:
    1643          23 :         if (in_transaction) {
    1644           0 :                 int ret2 = ldb_transaction_cancel(ldb);
    1645           0 :                 if (ret2) {
    1646           0 :                         ret = ret2;
    1647             :                 }
    1648             :         }
    1649          23 :         talloc_free(tmp_ctx);
    1650          23 :         return ret;
    1651             : }
    1652             : 
    1653      342390 : int dsdb_update_gmsa_keys(struct ldb_context *ldb,
    1654             :                           TALLOC_CTX *mem_ctx,
    1655             :                           const struct ldb_result *res,
    1656             :                           bool *retry_out)
    1657             : {
    1658      342390 :         TALLOC_CTX *tmp_ctx = NULL;
    1659      342390 :         int ret = LDB_SUCCESS;
    1660      342390 :         bool retry = false;
    1661       12204 :         unsigned i;
    1662       12204 :         NTTIME current_time;
    1663      342390 :         bool am_rodc = true;
    1664             : 
    1665             :         /*
    1666             :          * This is non-zero if we are local to the sam.ldb, this is an
    1667             :          * opaque set by the samba_dsdb module
    1668             :          */
    1669      342390 :         void *samba_dsdb_opaque = ldb_get_opaque(
    1670             :                 ldb, DSDB_OPAQUE_PARTITION_MODULE_MSG_OPAQUE_NAME);
    1671             : 
    1672      342390 :         if (samba_dsdb_opaque == NULL) {
    1673             :                 /*
    1674             :                  * We are not connected locally, so no point trying to
    1675             :                  * set passwords
    1676             :                  */
    1677         178 :                 *retry_out = false;
    1678         178 :                 return LDB_SUCCESS;
    1679             :         }
    1680             : 
    1681             :         {
    1682             :                 /* Calculate the current time, as reckoned for gMSAs. */
    1683      342212 :                 bool ok = dsdb_gmsa_current_time(ldb, &current_time);
    1684      342212 :                 if (!ok) {
    1685           0 :                         ret = ldb_operr(ldb);
    1686           0 :                         goto out;
    1687             :                 }
    1688             :         }
    1689             : 
    1690      342212 :         tmp_ctx = talloc_new(mem_ctx);
    1691      342212 :         if (tmp_ctx == NULL) {
    1692           0 :                 ret = ldb_oom(ldb);
    1693           0 :                 goto out;
    1694             :         }
    1695             : 
    1696             :         /* Are we operating as an RODC? */
    1697      342212 :         ret = samdb_rodc(ldb, &am_rodc);
    1698      342212 :         if (ret != LDB_SUCCESS) {
    1699           0 :                 DBG_WARNING("unable to tell if we are an RODC\n");
    1700           0 :                 goto out;
    1701             :         }
    1702             : 
    1703             :         /* Loop through each entry in the results. */
    1704      684540 :         for (i = 0; i < res->count; ++i) {
    1705      342328 :                 struct ldb_message *msg = res->msgs[i];
    1706      342328 :                 struct gmsa_update *gmsa_update = NULL;
    1707      342328 :                 const bool is_gmsa = dsdb_account_is_gmsa(ldb, msg);
    1708             : 
    1709             :                 /* Is the account a Group Managed Service Account? */
    1710      342328 :                 if (!is_gmsa) {
    1711             :                         /*
    1712             :                          * It’s not a gMSA, and there’s nothing more to do for
    1713             :                          * this result.
    1714             :                          */
    1715      342307 :                         continue;
    1716             :                 }
    1717             : 
    1718         114 :                 if (am_rodc) {
    1719             :                         static const char *const secret_attributes[] = {
    1720             :                                 DSDB_SECRET_ATTRIBUTES};
    1721             :                         size_t j;
    1722             : 
    1723             :                         /*
    1724             :                          * If we’re an RODC, we won’t be able to update the
    1725             :                          * database entry with the new gMSA keys. The simplest
    1726             :                          * thing to do is redact all the password attributes in
    1727             :                          * the message. If our caller is the KDC, it will
    1728             :                          * recognize the missing keys and dispatch a referral to
    1729             :                          * a writable DC. */
    1730           0 :                         for (j = 0; j < ARRAY_SIZE(secret_attributes); ++j) {
    1731           0 :                                 ldb_msg_remove_attr(msg, secret_attributes[j]);
    1732             :                         }
    1733             : 
    1734             :                         /* Proceed to the next search result. */
    1735           0 :                         continue;
    1736             :                 }
    1737             : 
    1738             :                 /* Update any old gMSA state. */
    1739         114 :                 ret = gmsa_recalculate_managed_pwd(
    1740             :                         tmp_ctx, ldb, msg, current_time, &gmsa_update, NULL);
    1741         114 :                 if (ret) {
    1742           0 :                         goto out;
    1743             :                 }
    1744             : 
    1745         114 :                 if (gmsa_update == NULL) {
    1746             :                         /*
    1747             :                          * The usual case; the keys are up‐to‐date, and there’s
    1748             :                          * nothing more to do for this result.
    1749             :                          */
    1750          93 :                         continue;
    1751             :                 }
    1752             : 
    1753          21 :                 ret = dsdb_update_gmsa_entry_keys(ldb, tmp_ctx, gmsa_update);
    1754          21 :                 if (ret) {
    1755           0 :                         goto out;
    1756             :                 }
    1757             : 
    1758             :                 /*
    1759             :                  * Since the database entry has been updated, the caller will
    1760             :                  * need to perform the search again.
    1761             :                  */
    1762          21 :                 retry = true;
    1763             :         }
    1764             : 
    1765      342212 :         *retry_out = retry;
    1766             : 
    1767      342212 : out:
    1768      342212 :         talloc_free(tmp_ctx);
    1769      342212 :         return ret;
    1770             : }
    1771             : 
    1772      364684 : bool dsdb_gmsa_current_time(struct ldb_context *ldb, NTTIME *current_time_out)
    1773             : {
    1774      364684 :         const unsigned long long *gmsa_time = talloc_get_type(
    1775             :                 ldb_get_opaque(ldb, DSDB_GMSA_TIME_OPAQUE), unsigned long long);
    1776             : 
    1777      364684 :         if (gmsa_time != NULL) {
    1778         186 :                 *current_time_out = *gmsa_time;
    1779         186 :                 return true;
    1780             :         }
    1781             : 
    1782      364498 :         return gmsa_current_time(current_time_out);
    1783             : }

Generated by: LCOV version 1.14