Line data Source code
1 : /*
2 : ldb database module to enforce unique local objectSIDs
3 :
4 : Copyright (C) Andrew Bartlett <abartlet@samba.org> 2017
5 :
6 : This program is free software; you can redistribute it and/or modify
7 : it under the terms of the GNU General Public License as published by
8 : the Free Software Foundation; either version 3 of the License, or
9 : (at your option) any later version.
10 :
11 : This program is distributed in the hope that it will be useful,
12 : but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : GNU General Public License for more details.
15 :
16 : You should have received a copy of the GNU General Public License
17 : along with this program. If not, see <http://www.gnu.org/licenses/>.
18 : */
19 :
20 : /*
21 :
22 : Duplicate ObjectSIDs are possible on foreign security principals and
23 : replication conflict records. However a duplicate objectSID within
24 : the local domainSID is an error.
25 :
26 : As the uniqueness requirement depends on the source domain it is not possible
27 : to enforce this with a unique index.
28 :
29 : This module sets the LDB_FLAG_FORCE_UNIQUE_INDEX for objectSIDs in the
30 : local domain.
31 : */
32 :
33 : #include "includes.h"
34 : #include "ldb_module.h"
35 : #include "dsdb/samdb/samdb.h"
36 : #include "libcli/security/dom_sid.h"
37 : #include "dsdb/samdb/ldb_modules/util.h"
38 :
39 : struct private_data {
40 : const struct dom_sid *domain_sid;
41 : };
42 :
43 :
44 : /*
45 : * Does the add request contain a local objectSID
46 : */
47 2192983 : static bool message_contains_local_objectSID(
48 : struct ldb_module *module,
49 : const struct ldb_message *msg)
50 : {
51 2192983 : struct dom_sid *objectSID = NULL;
52 :
53 114314 : struct private_data *data =
54 2192983 : talloc_get_type(
55 : ldb_module_get_private(module),
56 : struct private_data);
57 :
58 2192983 : TALLOC_CTX *frame = talloc_stackframe();
59 :
60 2192983 : objectSID = samdb_result_dom_sid(frame, msg, "objectSID");
61 2192983 : if (objectSID == NULL) {
62 2089232 : TALLOC_FREE(frame);
63 2089232 : return false;
64 : }
65 :
66 : /*
67 : * data->domain_sid can be NULL but dom_sid_in_domain handles this
68 : * case correctly. See unique_object_sids_init for more details.
69 : */
70 103751 : if (!dom_sid_in_domain(data->domain_sid, objectSID)) {
71 57905 : TALLOC_FREE(frame);
72 57905 : return false;
73 : }
74 45846 : TALLOC_FREE(frame);
75 45706 : return true;
76 : }
77 :
78 45845 : static int flag_objectSID(
79 : struct ldb_module *module,
80 : struct ldb_request *req,
81 : const struct ldb_message *msg,
82 : struct ldb_message **new_msg)
83 : {
84 45845 : struct ldb_message_element *el = NULL;
85 :
86 45845 : *new_msg = ldb_msg_copy_shallow(req, msg);
87 45845 : if (!*new_msg) {
88 0 : return ldb_module_oom(module);
89 : }
90 :
91 45845 : el = ldb_msg_find_element(*new_msg, "objectSID");
92 45845 : if (el == NULL) {
93 0 : struct ldb_context *ldb = NULL;
94 0 : ldb = ldb_module_get_ctx(module);
95 0 : ldb_asprintf_errstring(
96 : ldb,
97 : "Unable to locate objectSID in copied request\n");
98 0 : return LDB_ERR_OPERATIONS_ERROR;
99 : }
100 45845 : el->flags |= LDB_FLAG_INTERNAL_FORCE_UNIQUE_INDEX;
101 45845 : return LDB_SUCCESS;
102 : }
103 :
104 : /* add */
105 924122 : static int unique_object_sids_add(
106 : struct ldb_module *module,
107 : struct ldb_request *req)
108 : {
109 924122 : const struct ldb_message *msg = req->op.add.message;
110 924122 : struct ldb_message *new_msg = NULL;
111 924122 : struct ldb_request *new_req = NULL;
112 924122 : struct ldb_context *ldb = NULL;
113 83790 : int rc;
114 :
115 924122 : if (!message_contains_local_objectSID(module, msg)) {
116 : /*
117 : * Request does not contain a local objectSID so chain the
118 : * next module
119 : */
120 885576 : return ldb_next_request(module, req);
121 : }
122 :
123 : /*
124 : * The add request contains an objectSID for the local domain
125 : */
126 :
127 38546 : rc = flag_objectSID(module, req, msg, &new_msg);
128 38546 : if (rc != LDB_SUCCESS) {
129 0 : return rc;
130 : }
131 :
132 38546 : ldb = ldb_module_get_ctx(module);
133 38546 : rc = ldb_build_add_req(
134 : &new_req,
135 : ldb,
136 : req,
137 : new_msg,
138 : req->controls,
139 : req,
140 : dsdb_next_callback,
141 : req);
142 38546 : if (rc != LDB_SUCCESS) {
143 0 : return rc;
144 : }
145 :
146 38546 : return ldb_next_request(module, new_req);
147 : }
148 :
149 : /* modify */
150 1268861 : static int unique_object_sids_modify(
151 : struct ldb_module *module,
152 : struct ldb_request *req)
153 : {
154 :
155 1268861 : const struct ldb_message *msg = req->op.mod.message;
156 1268861 : struct ldb_message *new_msg = NULL;
157 1268861 : struct ldb_request *new_req = NULL;
158 1268861 : struct ldb_context *ldb = NULL;
159 30524 : int rc;
160 :
161 1268861 : if (!message_contains_local_objectSID(module, msg)) {
162 : /*
163 : * Request does not contain a local objectSID so chain the
164 : * next module
165 : */
166 1261561 : return ldb_next_request(module, req);
167 : }
168 :
169 7300 : ldb = ldb_module_get_ctx(module);
170 :
171 : /*
172 : * If DSDB_CONTROL_REPLICATED_UPDATE_OID replicated is set we know
173 : * that the modify request is well formed and objectSID only appears
174 : * once.
175 : *
176 : * Enforcing this assumption simplifies the subsequent code.
177 : *
178 : */
179 7300 : if(!ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)) {
180 2 : ldb_asprintf_errstring(
181 : ldb,
182 : "Modify of %s rejected, "
183 : "as it is modifying an objectSID\n",
184 1 : ldb_dn_get_linearized(msg->dn));
185 1 : return LDB_ERR_UNWILLING_TO_PERFORM;
186 : }
187 :
188 :
189 7299 : rc = flag_objectSID(module, req, msg, &new_msg);
190 7299 : if (rc != LDB_SUCCESS) {
191 0 : return rc;
192 : }
193 :
194 7299 : ldb = ldb_module_get_ctx(module);
195 7299 : rc = ldb_build_mod_req(
196 : &new_req,
197 : ldb,
198 : req,
199 : new_msg,
200 : req->controls,
201 : req,
202 : dsdb_next_callback,
203 : req);
204 7299 : if (rc != LDB_SUCCESS) {
205 0 : return rc;
206 : }
207 :
208 7299 : return ldb_next_request(module, new_req);
209 : }
210 :
211 : /* init */
212 182010 : static int unique_object_sids_init(
213 : struct ldb_module *module)
214 : {
215 182010 : struct ldb_context *ldb = ldb_module_get_ctx(module);
216 182010 : struct private_data *data = NULL;
217 6022 : int ret;
218 :
219 182010 : ret = ldb_next_init(module);
220 :
221 182010 : if (ret != LDB_SUCCESS) {
222 0 : return ret;
223 : }
224 :
225 182010 : data = talloc_zero(module, struct private_data);
226 182010 : if (!data) {
227 0 : return ldb_module_oom(module);
228 : }
229 :
230 182010 : data->domain_sid = samdb_domain_sid(ldb);
231 182010 : if (data->domain_sid == NULL) {
232 : /*
233 : * Unable to determine the domainSID, this normally occurs
234 : * when provisioning. As there is no easy way to detect
235 : * that we are provisioning. We currently just log this as a
236 : * warning.
237 : */
238 205 : ldb_debug(
239 : ldb,
240 : LDB_DEBUG_WARNING,
241 : "Unable to determine the DomainSID, "
242 : "can not enforce uniqueness constraint on local "
243 : "domainSIDs\n");
244 : }
245 :
246 182010 : ldb_module_set_private(module, data);
247 :
248 182010 : return LDB_SUCCESS;
249 : }
250 :
251 : static const struct ldb_module_ops ldb_unique_object_sids_module_ops = {
252 : .name = "unique_object_sids",
253 : .init_context = unique_object_sids_init,
254 : .add = unique_object_sids_add,
255 : .modify = unique_object_sids_modify,
256 : };
257 :
258 6040 : int ldb_unique_object_sids_init(const char *version)
259 : {
260 6040 : LDB_MODULE_CHECK_VERSION(version);
261 6040 : return ldb_register_module(&ldb_unique_object_sids_module_ops);
262 : }
|