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 <talloc.h>
23 : #include <ldb.h>
24 : #include <ldb_module.h>
25 : #include <ldb_errors.h>
26 : #include <ldb_private.h>
27 : #include "lib/crypto/gmsa.h"
28 : #include "lib/util/time.h"
29 : #include "librpc/gen_ndr/ndr_gkdi.h"
30 : #include "librpc/gen_ndr/ndr_gmsa.h"
31 : #include "dsdb/gmsa/util.h"
32 : #include "dsdb/samdb/ldb_modules/managed_pwd.h"
33 : #include "dsdb/samdb/ldb_modules/util.h"
34 : #include "dsdb/samdb/samdb.h"
35 :
36 : #undef strcasecmp
37 :
38 4895 : static int gmsa_managed_password(struct ldb_context *const ldb,
39 : struct ldb_message *msg,
40 : struct ldb_request *req,
41 : struct ldb_reply *ares)
42 : {
43 4895 : TALLOC_CTX *tmp_ctx = NULL;
44 4895 : const struct dsdb_encrypted_connection_state *conn_state = NULL;
45 4895 : int ret = LDB_SUCCESS;
46 4895 : NTSTATUS status = NT_STATUS_OK;
47 12 : NTTIME current_time;
48 4895 : struct gmsa_update *gmsa_update = NULL;
49 12 : struct gmsa_return_pwd return_pwd;
50 12 : bool ok;
51 :
52 : /*
53 : * Prevent viewing msDS-ManagedPassword over an insecure connection. The
54 : * opaque is added in the ldap backend init.
55 : */
56 4895 : conn_state = ldb_get_opaque(
57 : ldb, DSDB_OPAQUE_ENCRYPTED_CONNECTION_STATE_NAME);
58 4895 : if (conn_state != NULL && !conn_state->using_encrypted_connection) {
59 4 : ret = dsdb_werror(ldb,
60 : LDB_ERR_OPERATIONS_ERROR,
61 : WERR_DS_CONFIDENTIALITY_REQUIRED,
62 : "Viewing msDS-ManagedPassword requires an "
63 : "encrypted connection");
64 4 : goto out;
65 : }
66 :
67 : {
68 : /* Is the account a Group Managed Service Account? */
69 4891 : const bool is_gmsa = dsdb_account_is_gmsa(ldb, msg);
70 4891 : if (!is_gmsa) {
71 : /* It’s not a GMSA — we’re done here. */
72 4835 : ret = LDB_SUCCESS;
73 4835 : goto out;
74 : }
75 : }
76 :
77 : {
78 56 : bool am_rodc = true;
79 :
80 : /* Are we operating as an RODC? */
81 56 : ret = samdb_rodc(ldb, &am_rodc);
82 56 : if (ret != LDB_SUCCESS) {
83 0 : DBG_WARNING("unable to tell if we are an RODC\n");
84 0 : goto out;
85 : }
86 :
87 56 : if (am_rodc) {
88 : /* TODO: forward the request to a writable DC. */
89 0 : ret = ldb_error(
90 : ldb,
91 : LDB_ERR_OPERATIONS_ERROR,
92 : "msDS-ManagedPassword may only be viewed on a "
93 : "writeable DC, not an RODC");
94 0 : goto out;
95 : }
96 : }
97 :
98 56 : tmp_ctx = talloc_new(msg);
99 56 : if (tmp_ctx == NULL) {
100 0 : ret = ldb_oom(ldb);
101 0 : goto out;
102 : }
103 :
104 : {
105 0 : struct dom_sid account_sid;
106 56 : bool allowed_to_view = false;
107 :
108 56 : ret = samdb_result_dom_sid_buf(msg, "objectSid", &account_sid);
109 56 : if (ret) {
110 7 : goto out;
111 : }
112 :
113 56 : ret = gmsa_allowed_to_view_managed_password(
114 : tmp_ctx, ldb, msg, &account_sid, &allowed_to_view);
115 56 : if (ret) {
116 0 : goto out;
117 : }
118 :
119 56 : if (!allowed_to_view) {
120 : /* Sorry, you can’t view the password. */
121 7 : ret = LDB_SUCCESS;
122 7 : goto out;
123 : }
124 : }
125 :
126 49 : ok = dsdb_gmsa_current_time(ldb, ¤t_time);
127 49 : if (!ok) {
128 0 : ret = ldb_operr(ldb);
129 0 : goto out;
130 : }
131 :
132 49 : ret = gmsa_recalculate_managed_pwd(
133 : tmp_ctx, ldb, msg, current_time, &gmsa_update, &return_pwd);
134 49 : if (ret) {
135 0 : goto out;
136 : }
137 :
138 49 : SMB_ASSERT(return_pwd.new_pwd != NULL);
139 :
140 49 : if (gmsa_update != NULL) {
141 : /*
142 : * Return a control to indicate to the LDAP server that it needs
143 : * to refresh the physical passwords — that is, the keys in the
144 : * database, and the ManagedPasswordId attribute.
145 : */
146 10 : ret = ldb_reply_add_control(ares,
147 : DSDB_CONTROL_GMSA_UPDATE_OID,
148 : false,
149 : gmsa_update);
150 10 : if (ret) {
151 : /* Ignore the error. */
152 0 : ret = LDB_SUCCESS;
153 : } else {
154 : /*
155 : * Link the lifetime of the GMSA update control to that
156 : * of the reply.
157 : */
158 10 : talloc_steal(ares, gmsa_update);
159 : }
160 : }
161 :
162 : {
163 49 : DATA_BLOB packed_blob = {};
164 :
165 37 : status = gmsa_pack_managed_pwd(
166 : tmp_ctx,
167 49 : return_pwd.new_pwd->buf,
168 49 : return_pwd.prev_pwd != NULL ? return_pwd.prev_pwd->buf
169 : : NULL,
170 : return_pwd.query_interval,
171 : return_pwd.unchanged_interval,
172 : &packed_blob);
173 49 : if (!NT_STATUS_IS_OK(status)) {
174 0 : ret = ldb_operr(ldb);
175 0 : goto out;
176 : }
177 :
178 49 : ret = ldb_msg_add_steal_value(msg,
179 : "msDS-ManagedPassword",
180 : &packed_blob);
181 49 : if (ret) {
182 0 : goto out;
183 : }
184 : }
185 :
186 4883 : out:
187 4895 : TALLOC_FREE(tmp_ctx);
188 4895 : return ret;
189 : }
190 :
191 4895 : int constructed_msds_managed_password(struct ldb_module *module,
192 : struct ldb_message *msg,
193 : enum ldb_scope scope,
194 : struct ldb_request *parent,
195 : struct ldb_reply *ares)
196 : {
197 4895 : return gmsa_managed_password(ldb_module_get_ctx(module),
198 : msg,
199 : parent,
200 : ares);
201 : }
|