LCOV - code coverage report
Current view: top level - source4/dsdb/gmsa - util.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 399 559 71.4 %
Date: 2024-04-21 15:09:00 Functions: 24 24 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          42 : 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          42 :         TALLOC_CTX *tmp_ctx = NULL;
      74          42 :         struct security_descriptor group_msa_membership_sd = {};
      75          42 :         const struct security_token *user_token = NULL;
      76          42 :         NTSTATUS status = NT_STATUS_OK;
      77          42 :         int ret = LDB_SUCCESS;
      78             : 
      79          42 :         if (allowed_out == NULL) {
      80           0 :                 ret = ldb_operr(ldb);
      81           0 :                 goto out;
      82             :         }
      83          42 :         *allowed_out = false;
      84             : 
      85             :         {
      86          42 :                 const struct auth_session_info *session_info = ldb_get_opaque(
      87             :                         ldb, DSDB_SESSION_INFO);
      88           0 :                 const enum security_user_level level =
      89          42 :                         security_session_user_level(session_info, NULL);
      90             : 
      91          42 :                 if (level == SECURITY_SYSTEM) {
      92          16 :                         *allowed_out = true;
      93          16 :                         ret = LDB_SUCCESS;
      94          16 :                         goto out;
      95             :                 }
      96             : 
      97          26 :                 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          26 :                 user_token = session_info->security_token;
     106             :         }
     107             : 
     108          26 :         tmp_ctx = talloc_new(msg);
     109          26 :         if (tmp_ctx == NULL) {
     110           0 :                 ret = ldb_oom(ldb);
     111           0 :                 goto out;
     112             :         }
     113             : 
     114             :         {
     115          26 :                 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          26 :                 group_msa_membership = ldb_msg_find_ldb_val(
     120             :                         msg, "msDS-GroupMSAMembership");
     121          26 :                 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          26 :                 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          26 :                 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          26 :                 const uint32_t access_desired = SEC_ADS_READ_PROP;
     145          26 :                 uint32_t access_granted = 0;
     146             : 
     147          26 :                 status = sec_access_check_ds(&group_msa_membership_sd,
     148             :                                              user_token,
     149             :                                              access_desired,
     150             :                                              &access_granted,
     151             :                                              NULL,
     152             :                                              account_sid);
     153          26 :                 if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
     154             :                         /*
     155             :                          * The principal is not allowed to view the managed
     156             :                          * password.
     157             :                          */
     158          19 :                 } 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          19 :                         *allowed_out = true;
     175             :                 }
     176             :         }
     177             : 
     178          42 : out:
     179          42 :         TALLOC_FREE(tmp_ctx);
     180          42 :         return ret;
     181             : }
     182             : 
     183          60 : 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          60 :         if (root_key == NULL) {
     190           0 :                 return NT_STATUS_INVALID_PARAMETER;
     191             :         }
     192             : 
     193          60 :         if (pwd_id_blob != NULL) {
     194           8 :                 return gkdi_pull_KeyEnvelope(mem_ctx, pwd_id_blob, pwd_id_out);
     195             :         }
     196             : 
     197             :         {
     198          52 :                 const char *domain_name = NULL;
     199          52 :                 const char *forest_name = NULL;
     200             : 
     201          52 :                 domain_name = samdb_default_domain_name(ldb, mem_ctx);
     202          52 :                 if (domain_name == NULL) {
     203           0 :                         return NT_STATUS_NO_MEMORY;
     204             :                 }
     205             : 
     206          52 :                 forest_name = samdb_forest_name(ldb, mem_ctx);
     207          52 :                 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          52 :                 *pwd_id_out = (struct KeyEnvelope){
     213          52 :                         .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          52 :         return NT_STATUS_OK;
     221             : }
     222             : 
     223          66 : void gmsa_update_managed_pwd_id(struct KeyEnvelope *pwd_id,
     224             :                                 const struct gmsa_update_pwd_part *new_pwd)
     225             : {
     226          66 :         pwd_id->l0_index = new_pwd->gkid.l0_idx;
     227          66 :         pwd_id->l1_index = new_pwd->gkid.l1_idx;
     228          66 :         pwd_id->l2_index = new_pwd->gkid.l2_idx;
     229          66 :         pwd_id->root_key_id = new_pwd->root_key->id;
     230          66 : }
     231             : 
     232          66 : NTSTATUS gmsa_pack_managed_pwd_id(TALLOC_CTX *mem_ctx,
     233             :                                   const struct KeyEnvelope *pwd_id,
     234             :                                   DATA_BLOB *pwd_id_out)
     235             : {
     236          66 :         NTSTATUS status = NT_STATUS_OK;
     237           0 :         enum ndr_err_code err;
     238             : 
     239          66 :         err = ndr_push_struct_blob(pwd_id_out,
     240             :                                    mem_ctx,
     241             :                                    pwd_id,
     242             :                                    (ndr_push_flags_fn_t)ndr_push_KeyEnvelope);
     243          66 :         status = ndr_map_error2ntstatus(err);
     244          66 :         if (!NT_STATUS_IS_OK(status)) {
     245           0 :                 DBG_WARNING("KeyEnvelope push failed: %s\n", nt_errstr(status));
     246             :         }
     247             : 
     248          66 :         return status;
     249             : }
     250             : 
     251          27 : 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          27 :         TALLOC_CTX *tmp_ctx = NULL;
     257          27 :         NTSTATUS status = NT_STATUS_OK;
     258          27 :         int ret = LDB_SUCCESS;
     259             : 
     260          27 :         tmp_ctx = talloc_new(mem_ctx);
     261          27 :         if (tmp_ctx == NULL) {
     262           0 :                 ret = ldb_oom(ldb);
     263           0 :                 goto out;
     264             :         }
     265             : 
     266             :         {
     267          27 :                 const struct ldb_message *root_key_msg = NULL;
     268             : 
     269          27 :                 ret = gkdi_root_key_from_id(tmp_ctx,
     270             :                                             ldb,
     271             :                                             &pwd_id.root_key_id,
     272             :                                             &root_key_msg);
     273          27 :                 if (ret) {
     274           0 :                         goto out;
     275             :                 }
     276             : 
     277          27 :                 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          27 :                 if (!NT_STATUS_IS_OK(status)) {
     282           0 :                         ret = ldb_operr(ldb);
     283           0 :                         goto out;
     284             :                 }
     285             :         }
     286             : 
     287          27 :         new_pwd_out->gkid = pwd_id.gkid;
     288             : 
     289          27 : out:
     290          27 :         talloc_free(tmp_ctx);
     291          27 :         return ret;
     292             : }
     293             : 
     294          22 : 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          22 :         TALLOC_CTX *tmp_ctx = NULL;
     301          22 :         int ret = LDB_SUCCESS;
     302             : 
     303          22 :         tmp_ctx = talloc_new(mem_ctx);
     304          22 :         if (tmp_ctx == NULL) {
     305           0 :                 ret = ldb_oom(ldb);
     306           0 :                 goto out;
     307             :         }
     308             : 
     309             :         {
     310          22 :                 const struct ldb_message *root_key_msg = NULL;
     311           0 :                 struct GUID root_key_id;
     312          22 :                 NTSTATUS status = NT_STATUS_OK;
     313             : 
     314          22 :                 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          22 :                 if (ret) {
     321           0 :                         goto out;
     322             :                 }
     323             : 
     324          22 :                 status = gkdi_root_key_from_msg(mem_ctx,
     325             :                                                 root_key_id,
     326             :                                                 root_key_msg,
     327             :                                                 &new_pwd_out->root_key);
     328          22 :                 if (!NT_STATUS_IS_OK(status)) {
     329           0 :                         ret = ldb_operr(ldb);
     330           0 :                         goto out;
     331             :                 }
     332             :         }
     333             : 
     334          22 :         new_pwd_out->gkid = gkdi_get_interval_id(key_start_time);
     335             : 
     336          22 : out:
     337          22 :         talloc_free(tmp_ctx);
     338          22 :         return ret;
     339             : }
     340             : 
     341          27 : static int gmsa_specifc_root_key(TALLOC_CTX *mem_ctx,
     342             :                                  const struct KeyEnvelopeId pwd_id,
     343             :                                  struct RootKey *root_key_out)
     344             : {
     345          27 :         if (root_key_out == NULL) {
     346           0 :                 return LDB_ERR_OPERATIONS_ERROR;
     347             :         }
     348             : 
     349          27 :         *root_key_out = (struct RootKey){.mem_ctx = mem_ctx,
     350             :                                          .type = ROOT_KEY_SPECIFIC,
     351             :                                          .u.specific = pwd_id};
     352          27 :         return LDB_SUCCESS;
     353             : }
     354             : 
     355          26 : static int gmsa_nonspecifc_root_key(TALLOC_CTX *mem_ctx,
     356             :                                     const NTTIME key_start_time,
     357             :                                     struct RootKey *root_key_out)
     358             : {
     359          26 :         if (root_key_out == NULL) {
     360           0 :                 return LDB_ERR_OPERATIONS_ERROR;
     361             :         }
     362             : 
     363          26 :         *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          26 :         return LDB_SUCCESS;
     368             : }
     369             : 
     370          49 : 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          49 :         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          49 :         talloc_steal(mem_ctx, key.root_key);
     382          49 :         talloc_steal(mem_ctx, password);
     383             : 
     384          49 :         *root_key_out = (struct RootKey){.mem_ctx = mem_ctx,
     385             :                                          .type = ROOT_KEY_OBTAINED,
     386             :                                          .u.obtained = {.key = key,
     387             :                                                         .password = password}};
     388          49 :         return LDB_SUCCESS;
     389             : }
     390             : 
     391          84 : 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          84 :         TALLOC_CTX *tmp_ctx = NULL;
     397          84 :         NTSTATUS status = NT_STATUS_OK;
     398          84 :         int ret = LDB_SUCCESS;
     399             : 
     400          84 :         if (root_key == NULL) {
     401           0 :                 ret = ldb_operr(ldb);
     402           0 :                 goto out;
     403             :         }
     404             : 
     405          84 :         switch (root_key->type) {
     406          49 :         case ROOT_KEY_SPECIFIC:
     407             :         case ROOT_KEY_NONSPECIFIC: {
     408          49 :                 struct gmsa_null_terminated_password *password = NULL;
     409           0 :                 struct gmsa_update_pwd_part key;
     410             : 
     411          49 :                 tmp_ctx = talloc_new(NULL);
     412          49 :                 if (tmp_ctx == NULL) {
     413           0 :                         ret = ldb_oom(ldb);
     414           0 :                         goto out;
     415             :                 }
     416             : 
     417          49 :                 if (root_key->type == ROOT_KEY_SPECIFIC) {
     418             :                         /* Search for a specific root key. */
     419          27 :                         ret = gmsa_specific_password(tmp_ctx,
     420             :                                                      ldb,
     421             :                                                      root_key->u.specific,
     422             :                                                      &key);
     423          27 :                         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          22 :                         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          49 :                 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          49 :                 } else if (ret) {
     453           0 :                         goto out;
     454             :                 } else {
     455             :                         /* Derive the password. */
     456          49 :                         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          49 :                         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          49 :                         ret = gmsa_obtained_root_key_steal(root_key->mem_ctx,
     473             :                                                            key,
     474             :                                                            password,
     475             :                                                            root_key);
     476          49 :                         if (ret) {
     477           0 :                                 goto out;
     478             :                         }
     479             :                 }
     480          49 :         } break;
     481          23 :         case ROOT_KEY_NONE:
     482             :                 /* No key is available. */
     483          23 :                 break;
     484          12 :         case ROOT_KEY_OBTAINED:
     485             :                 /* The key has already been obtained. */
     486          12 :                 break;
     487           0 :         default:
     488           0 :                 ret = ldb_operr(ldb);
     489           0 :                 goto out;
     490             :         }
     491             : 
     492          84 : out:
     493          84 :         TALLOC_FREE(tmp_ctx);
     494          84 :         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          84 : 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          84 :         int ret = LDB_SUCCESS;
     511             : 
     512          84 :         if (password_out == NULL) {
     513           0 :                 ret = ldb_operr(ldb);
     514           0 :                 goto out;
     515             :         }
     516          84 :         *password_out = NULL;
     517             : 
     518          84 :         if (update_out != NULL) {
     519          14 :                 *update_out = (struct gmsa_update_pwd_part){};
     520             :         }
     521             : 
     522             :         /* Fetch the root key from the database and obtain the password. */
     523          84 :         ret = gmsa_fetch_root_key(ldb, current_time, root_key, account_sid);
     524          84 :         if (ret) {
     525           0 :                 goto out;
     526             :         }
     527             : 
     528          84 :         switch (root_key->type) {
     529          23 :         case ROOT_KEY_NONE:
     530             :                 /* No key is available. */
     531          23 :                 break;
     532          61 :         case ROOT_KEY_OBTAINED:
     533          61 :                 *password_out = root_key->u.obtained.password;
     534          61 :                 if (update_out != NULL) {
     535          14 :                         *update_out = root_key->u.obtained.key;
     536             :                 }
     537          61 :                 break;
     538           0 :         default:
     539             :                 /* Unexpected. */
     540           0 :                 ret = ldb_operr(ldb);
     541           0 :                 goto out;
     542             :         }
     543             : 
     544          84 : out:
     545          84 :         return ret;
     546             : }
     547             : 
     548           8 : 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             :         struct ldb_request **req_out)
     554             : {
     555           8 :         TALLOC_CTX *tmp_ctx = NULL;
     556           8 :         const struct ldb_val *pwd_id_blob = ldb_msg_find_ldb_val(
     557             :                 msg, "msDS-ManagedPasswordId");
     558           0 :         struct KeyEnvelope pwd_id;
     559           8 :         struct ldb_message *mod_msg = NULL;
     560           8 :         NTSTATUS status = NT_STATUS_OK;
     561           8 :         int ret = LDB_SUCCESS;
     562             : 
     563           8 :         tmp_ctx = talloc_new(mem_ctx);
     564           8 :         if (tmp_ctx == NULL) {
     565           0 :                 ret = ldb_oom(ldb);
     566           0 :                 goto out;
     567             :         }
     568             : 
     569             :         /* Create a new ldb message. */
     570           8 :         mod_msg = ldb_msg_new(tmp_ctx);
     571           8 :         if (mod_msg == NULL) {
     572           0 :                 ret = ldb_oom(ldb);
     573           0 :                 goto out;
     574             :         }
     575             :         {
     576           8 :                 struct ldb_dn *dn = ldb_dn_copy(mod_msg, msg->dn);
     577           8 :                 if (dn == NULL) {
     578           0 :                         ret = ldb_oom(ldb);
     579           0 :                         goto out;
     580             :                 }
     581           8 :                 mod_msg->dn = dn;
     582             :         }
     583             : 
     584             :         /* Get the Managed Password ID. */
     585           8 :         status = gmsa_managed_pwd_id(
     586           8 :                 ldb, tmp_ctx, pwd_id_blob, new_pwd->new_id.root_key, &pwd_id);
     587           8 :         if (!NT_STATUS_IS_OK(status)) {
     588           0 :                 ret = ldb_operr(ldb);
     589           0 :                 goto out;
     590             :         }
     591             : 
     592             :         /* Update the password ID to contain the new GKID and root key ID. */
     593           8 :         gmsa_update_managed_pwd_id(&pwd_id, &new_pwd->new_id);
     594             : 
     595             :         {
     596           8 :                 DATA_BLOB new_pwd_id_blob = {};
     597             : 
     598             :                 /* Pack the current password ID. */
     599           8 :                 status = gmsa_pack_managed_pwd_id(tmp_ctx,
     600             :                                                   &pwd_id,
     601             :                                                   &new_pwd_id_blob);
     602           8 :                 if (!NT_STATUS_IS_OK(status)) {
     603           0 :                         ret = ldb_operr(ldb);
     604           0 :                         goto out;
     605             :                 }
     606             : 
     607             :                 /* Update the msDS-ManagedPasswordId attribute. */
     608           8 :                 ret = ldb_msg_append_steal_value(mod_msg,
     609             :                                                  "msDS-ManagedPasswordId",
     610             :                                                  &new_pwd_id_blob,
     611             :                                                  LDB_FLAG_MOD_REPLACE);
     612           8 :                 if (ret) {
     613           0 :                         goto out;
     614             :                 }
     615             :         }
     616             : 
     617             :         {
     618           8 :                 DATA_BLOB *prev_pwd_id_blob = NULL;
     619           0 :                 DATA_BLOB _prev_pwd_id_blob;
     620           8 :                 DATA_BLOB prev_pwd_id = {};
     621             : 
     622           8 :                 if (new_pwd->prev_id.root_key != NULL) {
     623             :                         /*
     624             :                          * Update the password ID to contain the previous GKID
     625             :                          * and root key ID.
     626             :                          */
     627           6 :                         gmsa_update_managed_pwd_id(&pwd_id, &new_pwd->prev_id);
     628             : 
     629             :                         /* Pack the previous password ID. */
     630           6 :                         status = gmsa_pack_managed_pwd_id(tmp_ctx,
     631             :                                                           &pwd_id,
     632             :                                                           &prev_pwd_id);
     633           6 :                         if (!NT_STATUS_IS_OK(status)) {
     634           0 :                                 ret = ldb_operr(ldb);
     635           0 :                                 goto out;
     636             :                         }
     637             : 
     638           6 :                         prev_pwd_id_blob = &prev_pwd_id;
     639           2 :                 } else if (pwd_id_blob != NULL) {
     640             :                         /* Copy the current password ID to the previous ID. */
     641           2 :                         _prev_pwd_id_blob = ldb_val_dup(tmp_ctx, pwd_id_blob);
     642           2 :                         if (_prev_pwd_id_blob.length != pwd_id_blob->length) {
     643           0 :                                 ret = ldb_oom(ldb);
     644           0 :                                 goto out;
     645             :                         }
     646             : 
     647           2 :                         prev_pwd_id_blob = &_prev_pwd_id_blob;
     648             :                 }
     649             : 
     650           8 :                 if (prev_pwd_id_blob != NULL) {
     651             :                         /*
     652             :                          * Update the msDS-ManagedPasswordPreviousId attribute.
     653             :                          */
     654           8 :                         ret = ldb_msg_append_steal_value(
     655             :                                 mod_msg,
     656             :                                 "msDS-ManagedPasswordPreviousId",
     657             :                                 prev_pwd_id_blob,
     658             :                                 LDB_FLAG_MOD_REPLACE);
     659           8 :                         if (ret) {
     660           0 :                                 goto out;
     661             :                         }
     662             :                 }
     663             :         }
     664             : 
     665             :         {
     666           8 :                 struct ldb_request *req = NULL;
     667             : 
     668             :                 /* Build the ldb request to return. */
     669           8 :                 ret = ldb_build_mod_req(&req,
     670             :                                         ldb,
     671             :                                         tmp_ctx,
     672             :                                         mod_msg,
     673             :                                         NULL,
     674             :                                         NULL,
     675             :                                         ldb_op_default_callback,
     676             :                                         NULL);
     677           8 :                 if (ret) {
     678           0 :                         goto out;
     679             :                 }
     680             : 
     681             :                 /* Tie the lifetime of the message to that of the request. */
     682           8 :                 talloc_steal(req, mod_msg);
     683             : 
     684             :                 /* Make sure the password ID update happens as System. */
     685           8 :                 ret = dsdb_request_add_controls(req, DSDB_FLAG_AS_SYSTEM);
     686           8 :                 if (ret) {
     687           0 :                         goto out;
     688             :                 }
     689             : 
     690           8 :                 *req_out = talloc_steal(mem_ctx, req);
     691             :         }
     692             : 
     693           8 : out:
     694           8 :         talloc_free(tmp_ctx);
     695           8 :         return ret;
     696             : }
     697             : 
     698          52 : int gmsa_generate_blobs(struct ldb_context *ldb,
     699             :                         TALLOC_CTX *mem_ctx,
     700             :                         const NTTIME current_time,
     701             :                         const struct dom_sid *const account_sid,
     702             :                         DATA_BLOB *pwd_id_blob_out,
     703             :                         struct gmsa_null_terminated_password **password_out)
     704             : {
     705          52 :         TALLOC_CTX *tmp_ctx = NULL;
     706           0 :         struct KeyEnvelope pwd_id;
     707          52 :         const struct ProvRootKey *root_key = NULL;
     708          52 :         NTSTATUS status = NT_STATUS_OK;
     709          52 :         int ret = LDB_SUCCESS;
     710             : 
     711          52 :         tmp_ctx = talloc_new(mem_ctx);
     712          52 :         if (tmp_ctx == NULL) {
     713           0 :                 ret = ldb_oom(ldb);
     714           0 :                 goto out;
     715             :         }
     716             : 
     717             :         {
     718          52 :                 const struct ldb_message *root_key_msg = NULL;
     719           0 :                 struct GUID root_key_id;
     720          52 :                 const NTTIME one_interval = gkdi_key_cycle_duration +
     721             :                                             gkdi_max_clock_skew;
     722          52 :                 const NTTIME one_interval_ago = current_time -
     723          52 :                                                 MIN(one_interval, current_time);
     724             : 
     725          52 :                 ret = gkdi_most_recently_created_root_key(tmp_ctx,
     726             :                                                           ldb,
     727             :                                                           current_time,
     728             :                                                           one_interval_ago,
     729             :                                                           &root_key_id,
     730             :                                                           &root_key_msg);
     731          52 :                 if (ret) {
     732           0 :                         goto out;
     733             :                 }
     734             : 
     735          52 :                 status = gkdi_root_key_from_msg(tmp_ctx,
     736             :                                                 root_key_id,
     737             :                                                 root_key_msg,
     738             :                                                 &root_key);
     739          52 :                 if (!NT_STATUS_IS_OK(status)) {
     740           0 :                         ret = ldb_operr(ldb);
     741           0 :                         goto out;
     742             :                 }
     743             :         }
     744             : 
     745             :         /* Get the Managed Password ID. */
     746          52 :         status = gmsa_managed_pwd_id(ldb, tmp_ctx, NULL, root_key, &pwd_id);
     747          52 :         if (!NT_STATUS_IS_OK(status)) {
     748           0 :                 ret = ldb_operr(ldb);
     749           0 :                 goto out;
     750             :         }
     751             : 
     752             :         {
     753          52 :                 const struct Gkid current_gkid = gkdi_get_interval_id(
     754             :                         current_time);
     755             : 
     756             :                 /* Derive the password. */
     757          52 :                 status = gmsa_talloc_password_based_on_key_id(tmp_ctx,
     758             :                                                               current_gkid,
     759             :                                                               current_time,
     760             :                                                               root_key,
     761             :                                                               account_sid,
     762             :                                                               password_out);
     763          52 :                 if (!NT_STATUS_IS_OK(status)) {
     764           0 :                         ret = ldb_operr(ldb);
     765           0 :                         goto out;
     766             :                 }
     767             : 
     768             :                 {
     769          52 :                         const struct gmsa_update_pwd_part new_id = {
     770             :                                 .root_key = root_key,
     771             :                                 .gkid = current_gkid,
     772             :                         };
     773             : 
     774             :                         /*
     775             :                          * Update the password ID to contain the new GKID and
     776             :                          * root key ID.
     777             :                          */
     778          52 :                         gmsa_update_managed_pwd_id(&pwd_id, &new_id);
     779             :                 }
     780             :         }
     781             : 
     782             :         /* Pack the current password ID. */
     783          52 :         status = gmsa_pack_managed_pwd_id(mem_ctx, &pwd_id, pwd_id_blob_out);
     784          52 :         if (!NT_STATUS_IS_OK(status)) {
     785           0 :                 ret = ldb_operr(ldb);
     786           0 :                 goto out;
     787             :         }
     788             : 
     789             :         /* Transfer ownership of the password to the caller’s memory context. */
     790          52 :         talloc_steal(mem_ctx, *password_out);
     791             : 
     792          52 : out:
     793          52 :         talloc_free(tmp_ctx);
     794          52 :         return ret;
     795             : }
     796             : 
     797           8 : static int gmsa_create_update(TALLOC_CTX *mem_ctx,
     798             :                               struct ldb_context *ldb,
     799             :                               const struct ldb_message *msg,
     800             :                               const NTTIME current_time,
     801             :                               const struct dom_sid *account_sid,
     802             :                               const bool current_key_becomes_previous,
     803             :                               struct RootKey *current_key,
     804             :                               struct RootKey *previous_key,
     805             :                               struct gmsa_update **update_out)
     806             : {
     807           8 :         TALLOC_CTX *tmp_ctx = NULL;
     808           8 :         struct ldb_request *old_pw_req = NULL;
     809           8 :         struct ldb_request *new_pw_req = NULL;
     810           8 :         struct ldb_request *pwd_id_req = NULL;
     811           8 :         struct gmsa_update_pwd new_pwd = {};
     812           8 :         struct gmsa_update *update = NULL;
     813           8 :         NTSTATUS status = NT_STATUS_OK;
     814           8 :         int ret = LDB_SUCCESS;
     815             : 
     816           8 :         if (update_out == NULL) {
     817           0 :                 ret = ldb_operr(ldb);
     818           0 :                 goto out;
     819             :         }
     820           8 :         *update_out = NULL;
     821             : 
     822           8 :         if (current_key == NULL) {
     823           0 :                 ret = ldb_operr(ldb);
     824           0 :                 goto out;
     825             :         }
     826             : 
     827           8 :         tmp_ctx = talloc_new(mem_ctx);
     828           8 :         if (tmp_ctx == NULL) {
     829           0 :                 ret = ldb_oom(ldb);
     830           0 :                 goto out;
     831             :         }
     832             : 
     833             :         {
     834             :                 /*
     835             :                  * The password_hash module expects these passwords to be
     836             :                  * null‐terminated.
     837             :                  */
     838           8 :                 struct gmsa_null_terminated_password *new_password = NULL;
     839             : 
     840           8 :                 ret = gmsa_get_root_key(ldb,
     841             :                                         current_time,
     842             :                                         account_sid,
     843             :                                         current_key,
     844             :                                         &new_password,
     845             :                                         &new_pwd.new_id);
     846           8 :                 if (ret) {
     847           0 :                         goto out;
     848             :                 }
     849             : 
     850           8 :                 if (new_password == NULL) {
     851           0 :                         ret = ldb_operr(ldb);
     852           0 :                         goto out;
     853             :                 }
     854             : 
     855           8 :                 status = gmsa_system_password_update_request(
     856           8 :                         ldb, tmp_ctx, msg->dn, new_password->buf, &new_pw_req);
     857           8 :                 if (!NT_STATUS_IS_OK(status)) {
     858           0 :                         ret = ldb_operr(ldb);
     859           0 :                         goto out;
     860             :                 }
     861             :         }
     862             : 
     863             :         /* Does the previous password need to be updated? */
     864           8 :         if (current_key_becomes_previous) {
     865             :                 /*
     866             :                  * When we perform the password set, the now‐current password
     867             :                  * will become the previous password automatically. We don’t
     868             :                  * have to manage that ourselves.
     869             :                  */
     870             :         } else {
     871           6 :                 struct gmsa_null_terminated_password *old_password = NULL;
     872             : 
     873             :                 /* The current key cannot be reused as the previous key. */
     874           6 :                 ret = gmsa_get_root_key(ldb,
     875             :                                         current_time,
     876             :                                         account_sid,
     877             :                                         previous_key,
     878             :                                         &old_password,
     879             :                                         &new_pwd.prev_id);
     880           6 :                 if (ret) {
     881           0 :                         goto out;
     882             :                 }
     883             : 
     884           6 :                 if (old_password != NULL) {
     885           6 :                         status = gmsa_system_password_update_request(
     886             :                                 ldb,
     887             :                                 tmp_ctx,
     888           6 :                                 msg->dn,
     889           6 :                                 old_password->buf,
     890             :                                 &old_pw_req);
     891           6 :                         if (!NT_STATUS_IS_OK(status)) {
     892           0 :                                 ret = ldb_operr(ldb);
     893           0 :                                 goto out;
     894             :                         }
     895             :                 }
     896             :         }
     897             : 
     898             :         /* Ready the update of the msDS-ManagedPasswordId attribute. */
     899           8 :         ret = gmsa_system_update_password_id_req(
     900             :                 ldb, tmp_ctx, msg, &new_pwd, &pwd_id_req);
     901           8 :         if (ret) {
     902           0 :                 goto out;
     903             :         }
     904             : 
     905           8 :         update = talloc(tmp_ctx, struct gmsa_update);
     906           8 :         if (update == NULL) {
     907           0 :                 ret = ldb_oom(ldb);
     908           0 :                 goto out;
     909             :         }
     910             : 
     911           8 :         *update = (struct gmsa_update){
     912           8 :                 .old_pw_req = talloc_steal(update, old_pw_req),
     913           8 :                 .new_pw_req = talloc_steal(update, new_pw_req),
     914           8 :                 .pwd_id_req = talloc_steal(update, pwd_id_req)};
     915             : 
     916           8 :         *update_out = talloc_steal(mem_ctx, update);
     917             : 
     918           8 : out:
     919           8 :         TALLOC_FREE(tmp_ctx);
     920           8 :         return ret;
     921             : }
     922             : 
     923          35 : NTSTATUS gmsa_pack_managed_pwd(TALLOC_CTX *mem_ctx,
     924             :                                const uint8_t *new_password,
     925             :                                const uint8_t *old_password,
     926             :                                uint64_t query_interval,
     927             :                                uint64_t unchanged_interval,
     928             :                                DATA_BLOB *managed_pwd_out)
     929             : {
     930          35 :         const struct MANAGEDPASSWORD_BLOB managed_pwd = {
     931             :                 .passwords = {.current = new_password,
     932             :                               .previous = old_password,
     933             :                               .query_interval = &query_interval,
     934             :                               .unchanged_interval = &unchanged_interval}};
     935          35 :         NTSTATUS status = NT_STATUS_OK;
     936           0 :         enum ndr_err_code err;
     937             : 
     938          35 :         err = ndr_push_struct_blob(managed_pwd_out,
     939             :                                    mem_ctx,
     940             :                                    &managed_pwd,
     941             :                                    (ndr_push_flags_fn_t)
     942             :                                            ndr_push_MANAGEDPASSWORD_BLOB);
     943          35 :         status = ndr_map_error2ntstatus(err);
     944          35 :         if (!NT_STATUS_IS_OK(status)) {
     945           0 :                 DBG_WARNING("MANAGEDPASSWORD_BLOB push failed: %s\n",
     946             :                             nt_errstr(status));
     947             :         }
     948             : 
     949          35 :         return status;
     950             : }
     951             : 
     952       35156 : bool dsdb_account_is_gmsa(struct ldb_context *ldb,
     953             :                           const struct ldb_message *msg)
     954             : {
     955             :         /*
     956             :          * Check if the account has objectClass
     957             :          * ‘msDS-GroupManagedServiceAccount’.
     958             :          */
     959       35156 :         return samdb_find_attribute(ldb,
     960             :                                     msg,
     961             :                                     "objectclass",
     962       35156 :                                     "msDS-GroupManagedServiceAccount") != NULL;
     963             : }
     964             : 
     965             : static struct new_key {
     966             :         NTTIME start_time;
     967             :         bool immediately_follows_previous;
     968           8 : } calculate_new_key(const NTTIME current_time,
     969             :                     const NTTIME current_key_expiration_time,
     970             :                     const NTTIME rollover_interval)
     971             : {
     972           8 :         NTTIME new_key_start_time = current_key_expiration_time;
     973           8 :         bool immediately_follows_previous = false;
     974             : 
     975           8 :         if (new_key_start_time < current_time && rollover_interval) {
     976             :                 /*
     977             :                  * Advance the key start time by the rollover interval until it
     978             :                  * would be greater than the current time.
     979             :                  */
     980           6 :                 const NTTIME time_to_advance_by = current_time + 1 -
     981             :                                                   new_key_start_time;
     982           6 :                 const uint64_t stale_count = time_to_advance_by /
     983             :                                              rollover_interval;
     984           6 :                 new_key_start_time += stale_count * rollover_interval;
     985             : 
     986           6 :                 SMB_ASSERT(new_key_start_time <= current_time);
     987             : 
     988           6 :                 immediately_follows_previous = stale_count == 0;
     989             :         } else {
     990             :                 /*
     991             :                  * It is possible that new_key_start_time ≥ current_time;
     992             :                  * specifically, if there is no password ID, and the creation
     993             :                  * time of the gMSA is in the future (perhaps due to replication
     994             :                  * weirdness).
     995             :                  */
     996           0 :         }
     997             : 
     998           8 :         return (struct new_key){
     999             :                 .start_time = new_key_start_time,
    1000             :                 .immediately_follows_previous = immediately_follows_previous};
    1001             : }
    1002             : 
    1003          35 : static bool gmsa_creation_time(const struct ldb_message *msg,
    1004             :                                const NTTIME current_time,
    1005             :                                NTTIME *creation_time_out)
    1006             : {
    1007          35 :         const struct ldb_val *when_created = NULL;
    1008           0 :         time_t creation_unix_time;
    1009           0 :         int ret;
    1010             : 
    1011          35 :         when_created = ldb_msg_find_ldb_val(msg, "whenCreated");
    1012          35 :         ret = ldb_val_to_time(when_created, &creation_unix_time);
    1013          35 :         if (ret) {
    1014             :                 /* Fail if we can’t read the attribute or it isn’t present. */
    1015           0 :                 return false;
    1016             :         }
    1017             : 
    1018          35 :         unix_to_nt_time(creation_time_out, creation_unix_time);
    1019          35 :         return true;
    1020             : }
    1021             : 
    1022          39 : static const struct KeyEnvelopeId *gmsa_get_managed_pwd_id_attr_name(
    1023             :         const struct ldb_message *msg,
    1024             :         const char *attr_name,
    1025             :         struct KeyEnvelopeId *key_env_out)
    1026             : {
    1027          39 :         const struct ldb_val *pwd_id_blob = ldb_msg_find_ldb_val(msg,
    1028             :                                                                  attr_name);
    1029          39 :         if (pwd_id_blob == NULL) {
    1030           4 :                 return NULL;
    1031             :         }
    1032             : 
    1033          35 :         return gkdi_pull_KeyEnvelopeId(*pwd_id_blob, key_env_out);
    1034             : }
    1035             : 
    1036          35 : const struct KeyEnvelopeId *gmsa_get_managed_pwd_id(
    1037             :         const struct ldb_message *msg,
    1038             :         struct KeyEnvelopeId *key_env_out)
    1039             : {
    1040          35 :         return gmsa_get_managed_pwd_id_attr_name(msg,
    1041             :                                                  "msDS-ManagedPasswordId",
    1042             :                                                  key_env_out);
    1043             : }
    1044             : 
    1045           4 : static const struct KeyEnvelopeId *gmsa_get_managed_pwd_prev_id(
    1046             :         const struct ldb_message *msg,
    1047             :         struct KeyEnvelopeId *key_env_out)
    1048             : {
    1049           4 :         return gmsa_get_managed_pwd_id_attr_name(
    1050             :                 msg, "msDS-ManagedPasswordPreviousId", key_env_out);
    1051             : }
    1052             : 
    1053          35 : static bool samdb_result_gkdi_rollover_interval(const struct ldb_message *msg,
    1054             :                                                 NTTIME *rollover_interval_out)
    1055             : {
    1056           0 :         int64_t managed_password_interval;
    1057             : 
    1058          35 :         managed_password_interval = ldb_msg_find_attr_as_int64(
    1059             :                 msg, "msDS-ManagedPasswordInterval", 30);
    1060          35 :         return gkdi_rollover_interval(managed_password_interval,
    1061             :                                       rollover_interval_out);
    1062             : }
    1063             : 
    1064          35 : int gmsa_recalculate_managed_pwd(TALLOC_CTX *mem_ctx,
    1065             :                                  struct ldb_context *ldb,
    1066             :                                  const struct ldb_message *msg,
    1067             :                                  const NTTIME current_time,
    1068             :                                  struct gmsa_update **update_out,
    1069             :                                  struct gmsa_return_pwd *return_out)
    1070             : {
    1071          35 :         TALLOC_CTX *tmp_ctx = NULL;
    1072          35 :         int ret = LDB_SUCCESS;
    1073           0 :         NTTIME rollover_interval;
    1074           0 :         NTTIME current_key_expiration_time;
    1075           0 :         NTTIME key_expiration_time;
    1076           0 :         struct dom_sid account_sid;
    1077           0 :         struct KeyEnvelopeId pwd_id_buf;
    1078          35 :         const struct KeyEnvelopeId *pwd_id = NULL;
    1079          35 :         struct RootKey previous_key = empty_root_key;
    1080          35 :         struct RootKey current_key = empty_root_key;
    1081          35 :         struct gmsa_update *update = NULL;
    1082          35 :         struct gmsa_null_terminated_password *previous_password = NULL;
    1083          35 :         struct gmsa_null_terminated_password *current_password = NULL;
    1084          35 :         NTTIME query_interval = 0;
    1085          35 :         NTTIME unchanged_interval = 0;
    1086          35 :         NTTIME creation_time = 0;
    1087          35 :         NTTIME account_age = 0;
    1088          35 :         NTTIME key_start_time = 0;
    1089          35 :         bool have_key_start_time = false;
    1090          35 :         bool ok = true;
    1091          35 :         bool current_key_is_valid = false;
    1092             : 
    1093          35 :         if (update_out == NULL) {
    1094           0 :                 ret = ldb_operr(ldb);
    1095           0 :                 goto out;
    1096             :         }
    1097          35 :         *update_out = NULL;
    1098             : 
    1099             :         {
    1100             :                 /* Is the account a Group Managed Service Account? */
    1101          35 :                 const bool is_gmsa = dsdb_account_is_gmsa(ldb, msg);
    1102          35 :                 if (!is_gmsa) {
    1103             :                         /* It’s not a GMSA — we’re done here. */
    1104           0 :                         *update_out = NULL;
    1105           0 :                         if (return_out != NULL) {
    1106           0 :                                 *return_out = (struct gmsa_return_pwd){};
    1107             :                         }
    1108           0 :                         ret = LDB_SUCCESS;
    1109           0 :                         goto out;
    1110             :                 }
    1111             :         }
    1112             : 
    1113             :         /* Calculate the rollover interval. */
    1114          35 :         ok = samdb_result_gkdi_rollover_interval(msg, &rollover_interval);
    1115          35 :         if (!ok || rollover_interval == 0) {
    1116             :                 /* We can’t do anything if the rollover interval is zero. */
    1117           0 :                 ret = ldb_operr(ldb);
    1118           0 :                 goto out;
    1119             :         }
    1120             : 
    1121          35 :         ok = gmsa_creation_time(msg, current_time, &creation_time);
    1122          35 :         if (!ok) {
    1123           0 :                 return ldb_error(ldb,
    1124             :                                  LDB_ERR_OPERATIONS_ERROR,
    1125             :                                  "unable to determine creation time of Group "
    1126             :                                  "Managed Service Account");
    1127             :         }
    1128          35 :         account_age = current_time - MIN(creation_time, current_time);
    1129             : 
    1130             :         /* Calculate the expiration time of the current key. */
    1131          35 :         pwd_id = gmsa_get_managed_pwd_id(msg, &pwd_id_buf);
    1132          70 :         if (pwd_id != NULL &&
    1133          35 :             gkdi_get_key_start_time(pwd_id->gkid, &key_start_time))
    1134             :         {
    1135          35 :                 have_key_start_time = true;
    1136             : 
    1137             :                 /* Check for overflow. */
    1138          35 :                 if (key_start_time > UINT64_MAX - rollover_interval) {
    1139           0 :                         ret = ldb_operr(ldb);
    1140           0 :                         goto out;
    1141             :                 }
    1142          35 :                 current_key_expiration_time = key_start_time +
    1143             :                                               rollover_interval;
    1144             :         } else {
    1145             :                 /*
    1146             :                  * [MS-ADTS] does not say to use gkdi_get_interval_start_time(),
    1147             :                  * but surely it makes no sense to have keys starting or ending
    1148             :                  * at random times.
    1149             :                  */
    1150           0 :                 current_key_expiration_time = gkdi_get_interval_start_time(
    1151             :                         creation_time);
    1152             :         }
    1153             : 
    1154             :         /* Fetch the account’s SID, necessary for deriving passwords. */
    1155          35 :         ret = samdb_result_dom_sid_buf(msg, "objectSid", &account_sid);
    1156          35 :         if (ret) {
    1157           0 :                 goto out;
    1158             :         }
    1159             : 
    1160          35 :         tmp_ctx = talloc_new(mem_ctx);
    1161          35 :         if (tmp_ctx == NULL) {
    1162           0 :                 ret = ldb_oom(ldb);
    1163           0 :                 goto out;
    1164             :         }
    1165             : 
    1166             :         /*
    1167             :          * In determining whether the account’s passwords should be updated, we
    1168             :          * do not validate that the unicodePwd attribute is up‐to‐date, or even
    1169             :          * that it exists. We rely entirely on the fact that the managed
    1170             :          * password ID should be updated *only* as part of a successful gMSA
    1171             :          * password update. In any case, unicodePwd is optional in Samba — save
    1172             :          * for machine accounts (which gMSAs are :)) — and we can’t always rely
    1173             :          * on its presence.
    1174             :          *
    1175             :          * All this means that an admin (or a DC that doesn’t support gMSAs)
    1176             :          * could reset a gMSA’s password outside of the normal procedure, and
    1177             :          * the account would then have the wrong password until the key was due
    1178             :          * to roll over again. There’s nothing much we can do about this if we
    1179             :          * don’t want to re‐derive and verify the password every time we look up
    1180             :          * the keys.
    1181             :          */
    1182             : 
    1183          35 :         current_key_is_valid = pwd_id != NULL &&
    1184           0 :                                current_time < current_key_expiration_time;
    1185          35 :         if (current_key_is_valid) {
    1186          27 :                 key_expiration_time = current_key_expiration_time;
    1187             : 
    1188          27 :                 if (return_out != NULL) {
    1189           0 :                         struct KeyEnvelopeId prev_pwd_id_buf;
    1190          27 :                         const struct KeyEnvelopeId *prev_pwd_id = NULL;
    1191             : 
    1192          27 :                         ret = gmsa_specifc_root_key(tmp_ctx,
    1193             :                                                     *pwd_id,
    1194             :                                                     &current_key);
    1195          27 :                         if (ret) {
    1196           0 :                                 goto out;
    1197             :                         }
    1198             : 
    1199          27 :                         if (account_age >= rollover_interval) {
    1200           4 :                                 prev_pwd_id = gmsa_get_managed_pwd_prev_id(
    1201             :                                         msg, &prev_pwd_id_buf);
    1202           4 :                                 if (prev_pwd_id != NULL) {
    1203           0 :                                         ret = gmsa_specifc_root_key(
    1204             :                                                 tmp_ctx,
    1205             :                                                 *prev_pwd_id,
    1206             :                                                 &previous_key);
    1207           0 :                                         if (ret) {
    1208           0 :                                                 goto out;
    1209             :                                         }
    1210           4 :                                 } else if (have_key_start_time &&
    1211           4 :                                            key_start_time >= rollover_interval)
    1212             :                                 {
    1213             :                                         /*
    1214             :                                          * The account’s old enough to have a
    1215             :                                          * previous password, but it doesn’t
    1216             :                                          * have a previous password ID for some
    1217             :                                          * reason. This can happen in our tests
    1218             :                                          * (python/samba/krb5/gmsa_tests.py)
    1219             :                                          * when we’re mucking about with times.
    1220             :                                          * Just produce what would have been the
    1221             :                                          * previous key.
    1222             :                                          */
    1223           4 :                                         ret = gmsa_nonspecifc_root_key(
    1224             :                                                 tmp_ctx,
    1225             :                                                 key_start_time -
    1226             :                                                         rollover_interval,
    1227             :                                                 &previous_key);
    1228           4 :                                         if (ret) {
    1229           0 :                                                 goto out;
    1230             :                                         }
    1231             :                                 }
    1232             :                         } else {
    1233             :                                 /*
    1234             :                                  * The account is not old enough to have a
    1235             :                                  * previous password. The old password will not
    1236             :                                  * be returned.
    1237             :                                  */
    1238           0 :                         }
    1239             :                 }
    1240             :         } else {
    1241             :                 /* Calculate the start time of the new key. */
    1242           8 :                 const struct new_key new_key = calculate_new_key(
    1243             :                         current_time,
    1244             :                         current_key_expiration_time,
    1245             :                         rollover_interval);
    1246           8 :                 const bool current_key_becomes_previous =
    1247           8 :                         pwd_id != NULL && new_key.immediately_follows_previous;
    1248             : 
    1249             :                 /* Check for overflow. */
    1250           8 :                 if (new_key.start_time > UINT64_MAX - rollover_interval) {
    1251           0 :                         ret = ldb_operr(ldb);
    1252           0 :                         goto out;
    1253             :                 }
    1254           8 :                 key_expiration_time = new_key.start_time + rollover_interval;
    1255             : 
    1256           8 :                 ret = gmsa_nonspecifc_root_key(tmp_ctx,
    1257           8 :                                                new_key.start_time,
    1258             :                                                &current_key);
    1259           8 :                 if (ret) {
    1260           0 :                         goto out;
    1261             :                 }
    1262             : 
    1263           8 :                 if (account_age >= rollover_interval) {
    1264             :                         /* Check for underflow. */
    1265           8 :                         if (new_key.start_time < rollover_interval) {
    1266           0 :                                 ret = ldb_operr(ldb);
    1267           0 :                                 goto out;
    1268             :                         }
    1269           8 :                         ret = gmsa_nonspecifc_root_key(
    1270             :                                 tmp_ctx,
    1271           8 :                                 new_key.start_time - rollover_interval,
    1272             :                                 &previous_key);
    1273           8 :                         if (ret) {
    1274           0 :                                 goto out;
    1275             :                         }
    1276             :                 } else {
    1277             :                         /*
    1278             :                          * The account is not old enough to have a previous
    1279             :                          * password. The old password will not be returned.
    1280             :                          */
    1281           0 :                 }
    1282             : 
    1283             :                 /*
    1284             :                  * The current GMSA key, according to the Managed Password ID,
    1285             :                  * is no longer valid. We should update the account’s Managed
    1286             :                  * Password ID and keys in anticipation of their being needed in
    1287             :                  * the near future.
    1288             :                  */
    1289             : 
    1290           8 :                 ret = gmsa_create_update(tmp_ctx,
    1291             :                                          ldb,
    1292             :                                          msg,
    1293             :                                          current_time,
    1294             :                                          &account_sid,
    1295             :                                          current_key_becomes_previous,
    1296             :                                          &current_key,
    1297             :                                          &previous_key,
    1298             :                                          &update);
    1299           8 :                 if (ret) {
    1300           0 :                         goto out;
    1301             :                 }
    1302             :         }
    1303             : 
    1304          35 :         if (return_out != NULL) {
    1305           0 :                 bool return_future_key;
    1306             : 
    1307          35 :                 unchanged_interval = query_interval = key_expiration_time -
    1308          35 :                                                       MIN(current_time,
    1309             :                                                           key_expiration_time);
    1310             : 
    1311             :                 /* Derive the current and previous passwords. */
    1312          35 :                 return_future_key = query_interval <= gkdi_max_clock_skew;
    1313          35 :                 if (return_future_key) {
    1314           6 :                         struct RootKey future_key = empty_root_key;
    1315             : 
    1316             :                         /*
    1317             :                          * The current key hasn’t expired yet, but it
    1318             :                          * soon will. Return a new key that will be valid in the
    1319             :                          * next epoch.
    1320             :                          */
    1321             : 
    1322           6 :                         ret = gmsa_nonspecifc_root_key(tmp_ctx,
    1323             :                                                        key_expiration_time,
    1324             :                                                        &future_key);
    1325           6 :                         if (ret) {
    1326           0 :                                 goto out;
    1327             :                         }
    1328             : 
    1329           6 :                         ret = gmsa_get_root_key(ldb,
    1330             :                                                 current_time,
    1331             :                                                 &account_sid,
    1332             :                                                 &future_key,
    1333             :                                                 &current_password,
    1334             :                                                 NULL);
    1335           6 :                         if (ret) {
    1336           0 :                                 goto out;
    1337             :                         }
    1338             : 
    1339           6 :                         ret = gmsa_get_root_key(ldb,
    1340             :                                                 current_time,
    1341             :                                                 &account_sid,
    1342             :                                                 &current_key,
    1343             :                                                 &previous_password,
    1344             :                                                 NULL);
    1345           6 :                         if (ret) {
    1346           0 :                                 goto out;
    1347             :                         }
    1348             : 
    1349             :                         /* Check for overflow. */
    1350           6 :                         if (unchanged_interval > UINT64_MAX - rollover_interval)
    1351             :                         {
    1352           0 :                                 ret = ldb_operr(ldb);
    1353           0 :                                 goto out;
    1354             :                         }
    1355           6 :                         unchanged_interval += rollover_interval;
    1356             :                 } else {
    1357             :                         /*
    1358             :                          * Note that a gMSA will become unusable (at least until
    1359             :                          * the next rollover) if its associated root key is ever
    1360             :                          * deleted.
    1361             :                          */
    1362             : 
    1363          29 :                         ret = gmsa_get_root_key(ldb,
    1364             :                                                 current_time,
    1365             :                                                 &account_sid,
    1366             :                                                 &current_key,
    1367             :                                                 &current_password,
    1368             :                                                 NULL);
    1369          29 :                         if (ret) {
    1370           0 :                                 goto out;
    1371             :                         }
    1372             : 
    1373          29 :                         ret = gmsa_get_root_key(ldb,
    1374             :                                                 current_time,
    1375             :                                                 &account_sid,
    1376             :                                                 &previous_key,
    1377             :                                                 &previous_password,
    1378             :                                                 NULL);
    1379          29 :                         if (ret) {
    1380           0 :                                 goto out;
    1381             :                         }
    1382             :                 }
    1383             : 
    1384          35 :                 unchanged_interval -= MIN(gkdi_max_clock_skew,
    1385             :                                           unchanged_interval);
    1386             :         }
    1387             : 
    1388          35 :         *update_out = talloc_steal(mem_ctx, update);
    1389          35 :         if (return_out != NULL) {
    1390          35 :                 *return_out = (struct gmsa_return_pwd){
    1391          35 :                         .prev_pwd = talloc_steal(mem_ctx, previous_password),
    1392          35 :                         .new_pwd = talloc_steal(mem_ctx, current_password),
    1393             :                         .query_interval = query_interval,
    1394             :                         .unchanged_interval = unchanged_interval,
    1395             :                 };
    1396             :         }
    1397             : 
    1398           0 : out:
    1399          35 :         TALLOC_FREE(tmp_ctx);
    1400          35 :         return ret;
    1401             : }
    1402             : 
    1403          87 : bool dsdb_gmsa_current_time(struct ldb_context *ldb, NTTIME *current_time_out)
    1404             : {
    1405          87 :         const unsigned long long *gmsa_time = talloc_get_type(
    1406             :                 ldb_get_opaque(ldb, DSDB_GMSA_TIME_OPAQUE), unsigned long long);
    1407             : 
    1408          87 :         if (gmsa_time != NULL) {
    1409          24 :                 *current_time_out = *gmsa_time;
    1410          24 :                 return true;
    1411             :         }
    1412             : 
    1413          63 :         return gmsa_current_time(current_time_out);
    1414             : }

Generated by: LCOV version 1.14