/*
Unix SMB/CIFS implementation.
msDS-ManagedPassword attribute for Group Managed Service Accounts
Copyright (C) Catalyst.Net Ltd 2024
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include "includes.h"
#include
#include
#include
#include
#include
#include "lib/crypto/gmsa.h"
#include "lib/util/time.h"
#include "librpc/gen_ndr/ndr_gkdi.h"
#include "librpc/gen_ndr/ndr_gmsa.h"
#include "dsdb/gmsa/util.h"
#include "dsdb/samdb/ldb_modules/managed_pwd.h"
#include "dsdb/samdb/ldb_modules/util.h"
#include "dsdb/samdb/samdb.h"
#undef strcasecmp
static int gmsa_managed_password(struct ldb_context *const ldb,
struct ldb_message *msg,
struct ldb_request *req,
struct ldb_reply *ares)
{
TALLOC_CTX *tmp_ctx = NULL;
const struct dsdb_encrypted_connection_state *conn_state = NULL;
int ret = LDB_SUCCESS;
NTSTATUS status = NT_STATUS_OK;
NTTIME current_time;
struct gmsa_update *gmsa_update = NULL;
struct gmsa_return_pwd return_pwd;
bool ok;
/*
* Prevent viewing msDS-ManagedPassword over an insecure connection. The
* opaque is added in the ldap backend init.
*/
conn_state = ldb_get_opaque(
ldb, DSDB_OPAQUE_ENCRYPTED_CONNECTION_STATE_NAME);
if (conn_state != NULL && !conn_state->using_encrypted_connection) {
ret = dsdb_werror(ldb,
LDB_ERR_OPERATIONS_ERROR,
WERR_DS_CONFIDENTIALITY_REQUIRED,
"Viewing msDS-ManagedPassword requires an "
"encrypted connection");
goto out;
}
{
/* Is the account a Group Managed Service Account? */
const bool is_gmsa = dsdb_account_is_gmsa(ldb, msg);
if (!is_gmsa) {
/* It’s not a GMSA — we’re done here. */
ret = LDB_SUCCESS;
goto out;
}
}
{
bool am_rodc = true;
/* Are we operating as an RODC? */
ret = samdb_rodc(ldb, &am_rodc);
if (ret != LDB_SUCCESS) {
DBG_WARNING("unable to tell if we are an RODC\n");
goto out;
}
if (am_rodc) {
/* TODO: forward the request to a writable DC. */
ret = ldb_error(
ldb,
LDB_ERR_OPERATIONS_ERROR,
"msDS-ManagedPassword may only be viewed on a "
"writeable DC, not an RODC");
goto out;
}
}
tmp_ctx = talloc_new(msg);
if (tmp_ctx == NULL) {
ret = ldb_oom(ldb);
goto out;
}
{
struct dom_sid account_sid;
bool allowed_to_view = false;
ret = samdb_result_dom_sid_buf(msg, "objectSid", &account_sid);
if (ret) {
goto out;
}
ret = gmsa_allowed_to_view_managed_password(
tmp_ctx, ldb, msg, &account_sid, &allowed_to_view);
if (ret) {
goto out;
}
if (!allowed_to_view) {
/* Sorry, you can’t view the password. */
ret = LDB_SUCCESS;
goto out;
}
}
ok = dsdb_gmsa_current_time(ldb, ¤t_time);
if (!ok) {
ret = ldb_operr(ldb);
goto out;
}
ret = gmsa_recalculate_managed_pwd(
tmp_ctx, ldb, msg, current_time, &gmsa_update, &return_pwd);
if (ret) {
goto out;
}
SMB_ASSERT(return_pwd.new_pwd != NULL);
if (gmsa_update != NULL) {
/*
* Return a control to indicate to the LDAP server that it needs
* to refresh the physical passwords — that is, the keys in the
* database, and the ManagedPasswordId attribute.
*/
ret = ldb_reply_add_control(ares,
DSDB_CONTROL_GMSA_UPDATE_OID,
false,
gmsa_update);
if (ret) {
/* Ignore the error. */
ret = LDB_SUCCESS;
} else {
/*
* Link the lifetime of the GMSA update control to that
* of the reply.
*/
talloc_steal(ares, gmsa_update);
}
}
{
DATA_BLOB packed_blob = {};
status = gmsa_pack_managed_pwd(
tmp_ctx,
return_pwd.new_pwd->buf,
return_pwd.prev_pwd != NULL ? return_pwd.prev_pwd->buf
: NULL,
return_pwd.query_interval,
return_pwd.unchanged_interval,
&packed_blob);
if (!NT_STATUS_IS_OK(status)) {
ret = ldb_operr(ldb);
goto out;
}
ret = ldb_msg_add_steal_value(msg,
"msDS-ManagedPassword",
&packed_blob);
if (ret) {
goto out;
}
}
out:
TALLOC_FREE(tmp_ctx);
return ret;
}
int constructed_msds_managed_password(struct ldb_module *module,
struct ldb_message *msg,
enum ldb_scope scope,
struct ldb_request *parent,
struct ldb_reply *ares)
{
return gmsa_managed_password(ldb_module_get_ctx(module),
msg,
parent,
ares);
}