/src/samba/source4/winbind/idmap.c
Line | Count | Source |
1 | | /* |
2 | | Unix SMB/CIFS implementation. |
3 | | |
4 | | Map SIDs to unixids and back |
5 | | |
6 | | Copyright (C) Kai Blin 2008 |
7 | | Copyright (C) Andrew Bartlett 2012 |
8 | | |
9 | | This program is free software; you can redistribute it and/or modify |
10 | | it under the terms of the GNU General Public License as published by |
11 | | the Free Software Foundation; either version 3 of the License, or |
12 | | (at your option) any later version. |
13 | | |
14 | | This program is distributed in the hope that it will be useful, |
15 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 | | GNU General Public License for more details. |
18 | | |
19 | | You should have received a copy of the GNU General Public License |
20 | | along with this program. If not, see <http://www.gnu.org/licenses/>. |
21 | | */ |
22 | | |
23 | | #include "includes.h" |
24 | | #include "auth/auth.h" |
25 | | #include "librpc/gen_ndr/ndr_security.h" |
26 | | #include "lib/util_unixsids.h" |
27 | | #include <ldb.h> |
28 | | #include "ldb_wrap.h" |
29 | | #include "param/param.h" |
30 | | #include "winbind/idmap.h" |
31 | | #include "libcli/security/security.h" |
32 | | #include "libcli/ldap/ldap_ndr.h" |
33 | | #include "dsdb/samdb/samdb.h" |
34 | | #include "../libds/common/flags.h" |
35 | | |
36 | | /** |
37 | | * Get uid/gid bounds from idmap database |
38 | | * |
39 | | * \param idmap_ctx idmap context to use |
40 | | * \param low lower uid/gid bound is stored here |
41 | | * \param high upper uid/gid bound is stored here |
42 | | * \return 0 on success, nonzero on failure |
43 | | */ |
44 | | static int idmap_get_bounds(struct idmap_context *idmap_ctx, uint32_t *low, |
45 | | uint32_t *high) |
46 | 0 | { |
47 | 0 | int ret = -1; |
48 | 0 | struct ldb_context *ldb = idmap_ctx->ldb_ctx; |
49 | 0 | struct ldb_dn *dn; |
50 | 0 | struct ldb_result *res = NULL; |
51 | 0 | TALLOC_CTX *tmp_ctx = talloc_new(idmap_ctx); |
52 | 0 | uint32_t lower_bound = (uint32_t) -1; |
53 | 0 | uint32_t upper_bound = (uint32_t) -1; |
54 | |
|
55 | 0 | dn = ldb_dn_new(tmp_ctx, ldb, "CN=CONFIG"); |
56 | 0 | if (dn == NULL) goto failed; |
57 | | |
58 | 0 | ret = ldb_search(ldb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, NULL, NULL); |
59 | 0 | if (ret != LDB_SUCCESS) goto failed; |
60 | | |
61 | 0 | if (res->count != 1) { |
62 | 0 | ret = -1; |
63 | 0 | goto failed; |
64 | 0 | } |
65 | | |
66 | 0 | lower_bound = ldb_msg_find_attr_as_uint(res->msgs[0], "lowerBound", -1); |
67 | 0 | if (lower_bound != (uint32_t) -1) { |
68 | 0 | ret = LDB_SUCCESS; |
69 | 0 | } else { |
70 | 0 | ret = -1; |
71 | 0 | goto failed; |
72 | 0 | } |
73 | | |
74 | 0 | upper_bound = ldb_msg_find_attr_as_uint(res->msgs[0], "upperBound", -1); |
75 | 0 | if (upper_bound != (uint32_t) -1) { |
76 | 0 | ret = LDB_SUCCESS; |
77 | 0 | } else { |
78 | 0 | ret = -1; |
79 | 0 | } |
80 | |
|
81 | 0 | failed: |
82 | 0 | talloc_free(tmp_ctx); |
83 | 0 | *low = lower_bound; |
84 | 0 | *high = upper_bound; |
85 | 0 | return ret; |
86 | 0 | } |
87 | | |
88 | | /** |
89 | | * Add a dom_sid structure to a ldb_message |
90 | | * \param idmap_ctx idmap context to use |
91 | | * \param mem_ctx talloc context to use |
92 | | * \param ldb_message ldb message to add dom_sid to |
93 | | * \param attr_name name of the attribute to store the dom_sid in |
94 | | * \param sid dom_sid to store |
95 | | * \return 0 on success, an ldb error code on failure. |
96 | | */ |
97 | | static int idmap_msg_add_dom_sid(struct idmap_context *idmap_ctx, |
98 | | TALLOC_CTX *mem_ctx, struct ldb_message *msg, |
99 | | const char *attr_name, const struct dom_sid *sid) |
100 | 0 | { |
101 | 0 | struct ldb_val val; |
102 | 0 | enum ndr_err_code ndr_err; |
103 | |
|
104 | 0 | ndr_err = ndr_push_struct_blob(&val, mem_ctx, sid, |
105 | 0 | (ndr_push_flags_fn_t)ndr_push_dom_sid); |
106 | |
|
107 | 0 | if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { |
108 | 0 | return -1; |
109 | 0 | } |
110 | | |
111 | 0 | return ldb_msg_add_value(msg, attr_name, &val, NULL); |
112 | 0 | } |
113 | | |
114 | | /** |
115 | | * Get a dom_sid structure from a ldb message. |
116 | | * |
117 | | * \param mem_ctx talloc context to allocate dom_sid memory in |
118 | | * \param msg ldb_message to get dom_sid from |
119 | | * \param attr_name key that has the dom_sid as data |
120 | | * \return dom_sid structure on success, NULL on failure |
121 | | */ |
122 | | static struct dom_sid *idmap_msg_get_dom_sid(TALLOC_CTX *mem_ctx, |
123 | | struct ldb_message *msg, const char *attr_name) |
124 | 0 | { |
125 | 0 | struct dom_sid *sid; |
126 | 0 | const struct ldb_val *val; |
127 | 0 | enum ndr_err_code ndr_err; |
128 | |
|
129 | 0 | val = ldb_msg_find_ldb_val(msg, attr_name); |
130 | 0 | if (val == NULL) { |
131 | 0 | return NULL; |
132 | 0 | } |
133 | | |
134 | 0 | sid = talloc(mem_ctx, struct dom_sid); |
135 | 0 | if (sid == NULL) { |
136 | 0 | return NULL; |
137 | 0 | } |
138 | | |
139 | 0 | ndr_err = ndr_pull_struct_blob(val, sid, sid, |
140 | 0 | (ndr_pull_flags_fn_t)ndr_pull_dom_sid); |
141 | 0 | if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { |
142 | 0 | talloc_free(sid); |
143 | 0 | return NULL; |
144 | 0 | } |
145 | | |
146 | 0 | return sid; |
147 | 0 | } |
148 | | |
149 | | /** |
150 | | * Initialize idmap context |
151 | | * |
152 | | * talloc_free to close. |
153 | | * |
154 | | * \param mem_ctx talloc context to use. |
155 | | * \return allocated idmap_context on success, NULL on error |
156 | | */ |
157 | | struct idmap_context *idmap_init(TALLOC_CTX *mem_ctx, |
158 | | struct tevent_context *ev_ctx, |
159 | | struct loadparm_context *lp_ctx) |
160 | 0 | { |
161 | 0 | struct idmap_context *idmap_ctx; |
162 | |
|
163 | 0 | idmap_ctx = talloc(mem_ctx, struct idmap_context); |
164 | 0 | if (idmap_ctx == NULL) { |
165 | 0 | return NULL; |
166 | 0 | } |
167 | | |
168 | 0 | idmap_ctx->lp_ctx = lp_ctx; |
169 | |
|
170 | 0 | idmap_ctx->ldb_ctx = ldb_wrap_connect(idmap_ctx, ev_ctx, lp_ctx, |
171 | 0 | "idmap.ldb", |
172 | 0 | system_session(lp_ctx), |
173 | 0 | NULL, 0); |
174 | 0 | if (idmap_ctx->ldb_ctx == NULL) { |
175 | 0 | goto fail; |
176 | 0 | } |
177 | | |
178 | 0 | idmap_ctx->samdb = samdb_connect(idmap_ctx, |
179 | 0 | ev_ctx, |
180 | 0 | lp_ctx, |
181 | 0 | system_session(lp_ctx), |
182 | 0 | NULL, |
183 | 0 | 0); |
184 | 0 | if (idmap_ctx->samdb == NULL) { |
185 | 0 | DEBUG(0, ("Failed to load sam.ldb in idmap_init\n")); |
186 | 0 | goto fail; |
187 | 0 | } |
188 | | |
189 | 0 | return idmap_ctx; |
190 | 0 | fail: |
191 | 0 | TALLOC_FREE(idmap_ctx); |
192 | 0 | return NULL; |
193 | 0 | } |
194 | | |
195 | | /** |
196 | | * Convert an unixid to the corresponding SID |
197 | | * |
198 | | * \param idmap_ctx idmap context to use |
199 | | * \param mem_ctx talloc context the memory for the struct dom_sid is allocated |
200 | | * from. |
201 | | * \param unixid pointer to a unixid struct to convert |
202 | | * \param sid pointer that will take the struct dom_sid pointer if the mapping |
203 | | * succeeds. |
204 | | * \return NT_STATUS_OK on success, NT_STATUS_NONE_MAPPED if mapping not |
205 | | * possible or some other NTSTATUS that is more descriptive on failure. |
206 | | */ |
207 | | |
208 | | static NTSTATUS idmap_xid_to_sid(struct idmap_context *idmap_ctx, |
209 | | TALLOC_CTX *mem_ctx, |
210 | | struct unixid *unixid, |
211 | | struct dom_sid **sid) |
212 | 0 | { |
213 | 0 | int ret; |
214 | 0 | NTSTATUS status; |
215 | 0 | struct ldb_context *ldb = idmap_ctx->ldb_ctx; |
216 | 0 | struct ldb_result *res = NULL; |
217 | 0 | struct ldb_message *msg; |
218 | 0 | const struct dom_sid *unix_sid; |
219 | 0 | struct dom_sid *new_sid; |
220 | 0 | TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); |
221 | 0 | const char *id_type; |
222 | |
|
223 | 0 | const char *sam_attrs[] = {"objectSid", NULL}; |
224 | | |
225 | | /* |
226 | | * First check against our local DB, to see if this user has a |
227 | | * mapping there. This means that the Samba4 AD DC behaves |
228 | | * much like a winbindd member server running idmap_ad |
229 | | */ |
230 | | |
231 | 0 | switch (unixid->type) { |
232 | 0 | case ID_TYPE_UID: |
233 | 0 | if (lpcfg_parm_bool(idmap_ctx->lp_ctx, NULL, "idmap_ldb", "use rfc2307", false)) { |
234 | 0 | ret = dsdb_search_one(idmap_ctx->samdb, tmp_ctx, &msg, |
235 | 0 | ldb_get_default_basedn(idmap_ctx->samdb), |
236 | 0 | LDB_SCOPE_SUBTREE, |
237 | 0 | sam_attrs, 0, |
238 | 0 | "(&(|(sAMaccountType=%u)(sAMaccountType=%u)(sAMaccountType=%u))" |
239 | 0 | "(uidNumber=%u)(objectSid=*))", |
240 | 0 | ATYPE_ACCOUNT, ATYPE_WORKSTATION_TRUST, ATYPE_INTERDOMAIN_TRUST, unixid->id); |
241 | 0 | } else { |
242 | | /* If we are not to use the rfc2307 attributes, we just emulate a non-match */ |
243 | 0 | ret = LDB_ERR_NO_SUCH_OBJECT; |
244 | 0 | } |
245 | |
|
246 | 0 | if (ret == LDB_ERR_CONSTRAINT_VIOLATION) { |
247 | 0 | DEBUG(1, ("Search for uidNumber=%lu gave duplicate results, failing to map to a SID!\n", |
248 | 0 | (unsigned long)unixid->id)); |
249 | 0 | status = NT_STATUS_NONE_MAPPED; |
250 | 0 | goto failed; |
251 | 0 | } else if (ret == LDB_SUCCESS) { |
252 | 0 | *sid = samdb_result_dom_sid(mem_ctx, msg, "objectSid"); |
253 | 0 | if (*sid == NULL) { |
254 | 0 | DEBUG(1, ("Search for uidNumber=%lu did not return an objectSid!\n", |
255 | 0 | (unsigned long)unixid->id)); |
256 | 0 | status = NT_STATUS_NONE_MAPPED; |
257 | 0 | goto failed; |
258 | 0 | } |
259 | 0 | talloc_free(tmp_ctx); |
260 | 0 | return NT_STATUS_OK; |
261 | 0 | } else if (ret != LDB_ERR_NO_SUCH_OBJECT) { |
262 | 0 | DEBUG(1, ("Search for uidNumber=%lu gave '%s', failing to map to a SID!\n", |
263 | 0 | (unsigned long)unixid->id, ldb_errstring(idmap_ctx->samdb))); |
264 | 0 | status = NT_STATUS_NONE_MAPPED; |
265 | 0 | goto failed; |
266 | 0 | } |
267 | | |
268 | 0 | id_type = "ID_TYPE_UID"; |
269 | 0 | break; |
270 | 0 | case ID_TYPE_GID: |
271 | 0 | if (lpcfg_parm_bool(idmap_ctx->lp_ctx, NULL, "idmap_ldb", "use rfc2307", false)) { |
272 | 0 | ret = dsdb_search_one(idmap_ctx->samdb, tmp_ctx, &msg, |
273 | 0 | ldb_get_default_basedn(idmap_ctx->samdb), |
274 | 0 | LDB_SCOPE_SUBTREE, |
275 | 0 | sam_attrs, 0, |
276 | 0 | "(&(|(sAMaccountType=%u)(sAMaccountType=%u))(gidNumber=%u))", |
277 | 0 | ATYPE_SECURITY_GLOBAL_GROUP, ATYPE_SECURITY_LOCAL_GROUP, |
278 | 0 | unixid->id); |
279 | 0 | } else { |
280 | | /* If we are not to use the rfc2307 attributes, we just emulate a non-match */ |
281 | 0 | ret = LDB_ERR_NO_SUCH_OBJECT; |
282 | 0 | } |
283 | 0 | if (ret == LDB_ERR_CONSTRAINT_VIOLATION) { |
284 | 0 | DEBUG(1, ("Search for gidNumber=%lu gave duplicate results, failing to map to a SID!\n", |
285 | 0 | (unsigned long)unixid->id)); |
286 | 0 | status = NT_STATUS_NONE_MAPPED; |
287 | 0 | goto failed; |
288 | 0 | } else if (ret == LDB_SUCCESS) { |
289 | 0 | *sid = samdb_result_dom_sid(mem_ctx, msg, "objectSid"); |
290 | 0 | if (*sid == NULL) { |
291 | 0 | DEBUG(1, ("Search for gidNumber=%lu did not return an objectSid!\n", |
292 | 0 | (unsigned long)unixid->id)); |
293 | 0 | status = NT_STATUS_NONE_MAPPED; |
294 | 0 | goto failed; |
295 | 0 | } |
296 | 0 | talloc_free(tmp_ctx); |
297 | 0 | return NT_STATUS_OK; |
298 | 0 | } else if (ret != LDB_ERR_NO_SUCH_OBJECT) { |
299 | 0 | DEBUG(1, ("Search for gidNumber=%lu gave '%s', failing to map to a SID!\n", |
300 | 0 | (unsigned long)unixid->id, ldb_errstring(idmap_ctx->samdb))); |
301 | 0 | status = NT_STATUS_NONE_MAPPED; |
302 | 0 | goto failed; |
303 | 0 | } |
304 | | |
305 | 0 | id_type = "ID_TYPE_GID"; |
306 | 0 | break; |
307 | 0 | default: |
308 | 0 | DEBUG(1, ("unixid->type must be type gid or uid (got %u) for lookup with id %lu\n", |
309 | 0 | (unsigned)unixid->type, (unsigned long)unixid->id)); |
310 | 0 | status = NT_STATUS_NONE_MAPPED; |
311 | 0 | goto failed; |
312 | 0 | } |
313 | | |
314 | 0 | ret = ldb_search(ldb, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, |
315 | 0 | NULL, "(&(|(type=ID_TYPE_BOTH)(type=%s))" |
316 | 0 | "(xidNumber=%u))", id_type, unixid->id); |
317 | 0 | if (ret != LDB_SUCCESS) { |
318 | 0 | DEBUG(1, ("Search failed: %s\n", ldb_errstring(ldb))); |
319 | 0 | status = NT_STATUS_NONE_MAPPED; |
320 | 0 | goto failed; |
321 | 0 | } |
322 | | |
323 | 0 | if (res->count == 1) { |
324 | 0 | const char *type = ldb_msg_find_attr_as_string(res->msgs[0], |
325 | 0 | "type", NULL); |
326 | |
|
327 | 0 | *sid = idmap_msg_get_dom_sid(mem_ctx, res->msgs[0], |
328 | 0 | "objectSid"); |
329 | 0 | if (*sid == NULL) { |
330 | 0 | DEBUG(1, ("Failed to get sid from db: %u\n", ret)); |
331 | 0 | status = NT_STATUS_NONE_MAPPED; |
332 | 0 | goto failed; |
333 | 0 | } |
334 | | |
335 | 0 | if (type == NULL) { |
336 | 0 | DEBUG(1, ("Invalid type for mapping entry.\n")); |
337 | 0 | talloc_free(tmp_ctx); |
338 | 0 | return NT_STATUS_NONE_MAPPED; |
339 | 0 | } |
340 | | |
341 | 0 | if (strcmp(type, "ID_TYPE_BOTH") == 0) { |
342 | 0 | unixid->type = ID_TYPE_BOTH; |
343 | 0 | } else if (strcmp(type, "ID_TYPE_UID") == 0) { |
344 | 0 | unixid->type = ID_TYPE_UID; |
345 | 0 | } else { |
346 | 0 | unixid->type = ID_TYPE_GID; |
347 | 0 | } |
348 | |
|
349 | 0 | talloc_free(tmp_ctx); |
350 | 0 | return NT_STATUS_OK; |
351 | 0 | } |
352 | | |
353 | 0 | DEBUG(6, ("xid not found in idmap db, create S-1-22- SID.\n")); |
354 | | |
355 | | /* For local users/groups , we just create a rid = uid/gid */ |
356 | 0 | if (unixid->type == ID_TYPE_UID) { |
357 | 0 | unix_sid = &global_sid_Unix_Users; |
358 | 0 | } else { |
359 | 0 | unix_sid = &global_sid_Unix_Groups; |
360 | 0 | } |
361 | |
|
362 | 0 | new_sid = dom_sid_add_rid(mem_ctx, unix_sid, unixid->id); |
363 | 0 | if (new_sid == NULL) { |
364 | 0 | status = NT_STATUS_NO_MEMORY; |
365 | 0 | goto failed; |
366 | 0 | } |
367 | | |
368 | 0 | *sid = new_sid; |
369 | 0 | talloc_free(tmp_ctx); |
370 | 0 | return NT_STATUS_OK; |
371 | | |
372 | 0 | failed: |
373 | 0 | talloc_free(tmp_ctx); |
374 | 0 | return status; |
375 | 0 | } |
376 | | |
377 | | |
378 | | /** |
379 | | * Map a SID to an unixid struct. |
380 | | * |
381 | | * If no mapping exists, a new mapping will be created. |
382 | | * |
383 | | * \param idmap_ctx idmap context to use |
384 | | * \param mem_ctx talloc context to use |
385 | | * \param sid SID to map to an unixid struct |
386 | | * \param unixid pointer to a unixid struct |
387 | | * \return NT_STATUS_OK on success, NT_STATUS_INVALID_SID if the sid is not from |
388 | | * a trusted domain and idmap trusted only = true, NT_STATUS_NONE_MAPPED if the |
389 | | * mapping failed. |
390 | | */ |
391 | | static NTSTATUS idmap_sid_to_xid(struct idmap_context *idmap_ctx, |
392 | | TALLOC_CTX *mem_ctx, |
393 | | const struct dom_sid *sid, |
394 | | struct unixid *unixid) |
395 | 0 | { |
396 | 0 | int ret; |
397 | 0 | NTSTATUS status; |
398 | 0 | struct ldb_context *ldb = idmap_ctx->ldb_ctx; |
399 | 0 | struct ldb_dn *dn; |
400 | 0 | struct ldb_message *hwm_msg, *map_msg, *sam_msg; |
401 | 0 | struct ldb_result *res = NULL; |
402 | 0 | int trans = -1; |
403 | 0 | uint32_t low, high, hwm, new_xid; |
404 | 0 | struct dom_sid_buf sid_string; |
405 | 0 | char *unixid_string, *hwm_string; |
406 | 0 | bool hwm_entry_exists; |
407 | 0 | TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); |
408 | 0 | const char *sam_attrs[] = {"uidNumber", "gidNumber", "samAccountType", NULL}; |
409 | |
|
410 | 0 | if (sid_check_is_in_unix_users(sid)) { |
411 | 0 | uint32_t rid; |
412 | 0 | DEBUG(6, ("This is a local unix uid, just calculate that.\n")); |
413 | 0 | status = dom_sid_split_rid(tmp_ctx, sid, NULL, &rid); |
414 | 0 | if (!NT_STATUS_IS_OK(status)) { |
415 | 0 | talloc_free(tmp_ctx); |
416 | 0 | return status; |
417 | 0 | } |
418 | | |
419 | 0 | unixid->id = rid; |
420 | 0 | unixid->type = ID_TYPE_UID; |
421 | |
|
422 | 0 | talloc_free(tmp_ctx); |
423 | 0 | return NT_STATUS_OK; |
424 | 0 | } |
425 | | |
426 | 0 | if (sid_check_is_in_unix_groups(sid)) { |
427 | 0 | uint32_t rid; |
428 | 0 | DEBUG(6, ("This is a local unix gid, just calculate that.\n")); |
429 | 0 | status = dom_sid_split_rid(tmp_ctx, sid, NULL, &rid); |
430 | 0 | if (!NT_STATUS_IS_OK(status)) { |
431 | 0 | talloc_free(tmp_ctx); |
432 | 0 | return status; |
433 | 0 | } |
434 | | |
435 | 0 | unixid->id = rid; |
436 | 0 | unixid->type = ID_TYPE_GID; |
437 | |
|
438 | 0 | talloc_free(tmp_ctx); |
439 | 0 | return NT_STATUS_OK; |
440 | 0 | } |
441 | | |
442 | | /* |
443 | | * First check against our local DB, to see if this user has a |
444 | | * mapping there. This means that the Samba4 AD DC behaves |
445 | | * much like a winbindd member server running idmap_ad |
446 | | */ |
447 | | |
448 | 0 | if (lpcfg_parm_bool(idmap_ctx->lp_ctx, NULL, "idmap_ldb", "use rfc2307", false)) { |
449 | 0 | struct dom_sid_buf buf; |
450 | 0 | ret = dsdb_search_one(idmap_ctx->samdb, tmp_ctx, &sam_msg, |
451 | 0 | ldb_get_default_basedn(idmap_ctx->samdb), |
452 | 0 | LDB_SCOPE_SUBTREE, sam_attrs, 0, |
453 | 0 | "(&(objectSid=%s)" |
454 | 0 | "(|(sAMaccountType=%u)(sAMaccountType=%u)(sAMaccountType=%u)" |
455 | 0 | "(sAMaccountType=%u)(sAMaccountType=%u))" |
456 | 0 | "(|(uidNumber=*)(gidNumber=*)))", |
457 | 0 | dom_sid_str_buf(sid, &buf), |
458 | 0 | ATYPE_ACCOUNT, ATYPE_WORKSTATION_TRUST, ATYPE_INTERDOMAIN_TRUST, |
459 | 0 | ATYPE_SECURITY_GLOBAL_GROUP, ATYPE_SECURITY_LOCAL_GROUP); |
460 | 0 | } else { |
461 | | /* If we are not to use the rfc2307 attributes, we just emulate a non-match */ |
462 | 0 | ret = LDB_ERR_NO_SUCH_OBJECT; |
463 | 0 | } |
464 | |
|
465 | 0 | if (ret == LDB_ERR_CONSTRAINT_VIOLATION) { |
466 | 0 | struct dom_sid_buf buf; |
467 | 0 | DEBUG(1, ("Search for objectSid=%s gave duplicate results, failing to map to a unix ID!\n", |
468 | 0 | dom_sid_str_buf(sid, &buf))); |
469 | 0 | status = NT_STATUS_NONE_MAPPED; |
470 | 0 | goto failed; |
471 | 0 | } else if (ret == LDB_SUCCESS) { |
472 | 0 | uint32_t account_type = ldb_msg_find_attr_as_uint(sam_msg, "sAMaccountType", 0); |
473 | 0 | if ((account_type == ATYPE_ACCOUNT) || |
474 | 0 | (account_type == ATYPE_WORKSTATION_TRUST ) || |
475 | 0 | (account_type == ATYPE_INTERDOMAIN_TRUST )) |
476 | 0 | { |
477 | 0 | const struct ldb_val *v = ldb_msg_find_ldb_val(sam_msg, "uidNumber"); |
478 | 0 | if (v) { |
479 | 0 | unixid->type = ID_TYPE_UID; |
480 | 0 | unixid->id = ldb_msg_find_attr_as_uint(sam_msg, "uidNumber", -1); |
481 | 0 | talloc_free(tmp_ctx); |
482 | 0 | return NT_STATUS_OK; |
483 | 0 | } |
484 | |
|
485 | 0 | } else if ((account_type == ATYPE_SECURITY_GLOBAL_GROUP) || |
486 | 0 | (account_type == ATYPE_SECURITY_LOCAL_GROUP)) |
487 | 0 | { |
488 | 0 | const struct ldb_val *v = ldb_msg_find_ldb_val(sam_msg, "gidNumber"); |
489 | 0 | if (v) { |
490 | 0 | unixid->type = ID_TYPE_GID; |
491 | 0 | unixid->id = ldb_msg_find_attr_as_uint(sam_msg, "gidNumber", -1); |
492 | 0 | talloc_free(tmp_ctx); |
493 | 0 | return NT_STATUS_OK; |
494 | 0 | } |
495 | 0 | } |
496 | 0 | } else if (ret != LDB_ERR_NO_SUCH_OBJECT) { |
497 | 0 | struct dom_sid_buf buf; |
498 | 0 | DEBUG(1, ("Search for objectSid=%s gave '%s', failing to map to a SID!\n", |
499 | 0 | dom_sid_str_buf(sid, &buf), |
500 | 0 | ldb_errstring(idmap_ctx->samdb))); |
501 | |
|
502 | 0 | status = NT_STATUS_NONE_MAPPED; |
503 | 0 | goto failed; |
504 | 0 | } |
505 | | |
506 | 0 | ret = ldb_search(ldb, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, |
507 | 0 | NULL, "(&(objectClass=sidMap)(objectSid=%s))", |
508 | 0 | ldap_encode_ndr_dom_sid(tmp_ctx, sid)); |
509 | 0 | if (ret != LDB_SUCCESS) { |
510 | 0 | DEBUG(1, ("Search failed: %s\n", ldb_errstring(ldb))); |
511 | 0 | talloc_free(tmp_ctx); |
512 | 0 | return NT_STATUS_NONE_MAPPED; |
513 | 0 | } |
514 | | |
515 | 0 | if (res->count == 1) { |
516 | 0 | const char *type = ldb_msg_find_attr_as_string(res->msgs[0], |
517 | 0 | "type", NULL); |
518 | 0 | new_xid = ldb_msg_find_attr_as_uint(res->msgs[0], "xidNumber", |
519 | 0 | -1); |
520 | 0 | if (new_xid == (uint32_t) -1) { |
521 | 0 | DEBUG(1, ("Invalid xid mapping.\n")); |
522 | 0 | talloc_free(tmp_ctx); |
523 | 0 | return NT_STATUS_NONE_MAPPED; |
524 | 0 | } |
525 | | |
526 | 0 | if (type == NULL) { |
527 | 0 | DEBUG(1, ("Invalid type for mapping entry.\n")); |
528 | 0 | talloc_free(tmp_ctx); |
529 | 0 | return NT_STATUS_NONE_MAPPED; |
530 | 0 | } |
531 | | |
532 | 0 | unixid->id = new_xid; |
533 | |
|
534 | 0 | if (strcmp(type, "ID_TYPE_BOTH") == 0) { |
535 | 0 | unixid->type = ID_TYPE_BOTH; |
536 | 0 | } else if (strcmp(type, "ID_TYPE_UID") == 0) { |
537 | 0 | unixid->type = ID_TYPE_UID; |
538 | 0 | } else { |
539 | 0 | unixid->type = ID_TYPE_GID; |
540 | 0 | } |
541 | |
|
542 | 0 | talloc_free(tmp_ctx); |
543 | 0 | return NT_STATUS_OK; |
544 | 0 | } |
545 | | |
546 | 0 | DEBUG(6, ("No existing mapping found, attempting to create one.\n")); |
547 | |
|
548 | 0 | trans = ldb_transaction_start(ldb); |
549 | 0 | if (trans != LDB_SUCCESS) { |
550 | 0 | status = NT_STATUS_NONE_MAPPED; |
551 | 0 | goto failed; |
552 | 0 | } |
553 | | |
554 | | /* Redo the search to make sure no one changed the mapping while we |
555 | | * weren't looking */ |
556 | 0 | ret = ldb_search(ldb, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, |
557 | 0 | NULL, "(&(objectClass=sidMap)(objectSid=%s))", |
558 | 0 | ldap_encode_ndr_dom_sid(tmp_ctx, sid)); |
559 | 0 | if (ret != LDB_SUCCESS) { |
560 | 0 | DEBUG(1, ("Search failed: %s\n", ldb_errstring(ldb))); |
561 | 0 | status = NT_STATUS_NONE_MAPPED; |
562 | 0 | goto failed; |
563 | 0 | } |
564 | | |
565 | 0 | if (res->count > 0) { |
566 | 0 | DEBUG(1, ("Database changed while trying to add a sidmap.\n")); |
567 | 0 | status = NT_STATUS_RETRY; |
568 | 0 | goto failed; |
569 | 0 | } |
570 | | |
571 | 0 | ret = idmap_get_bounds(idmap_ctx, &low, &high); |
572 | 0 | if (ret != LDB_SUCCESS) { |
573 | 0 | status = NT_STATUS_NONE_MAPPED; |
574 | 0 | goto failed; |
575 | 0 | } |
576 | | |
577 | 0 | dn = ldb_dn_new(tmp_ctx, ldb, "CN=CONFIG"); |
578 | 0 | if (dn == NULL) { |
579 | 0 | status = NT_STATUS_NO_MEMORY; |
580 | 0 | goto failed; |
581 | 0 | } |
582 | | |
583 | 0 | ret = ldb_search(ldb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, NULL, NULL); |
584 | 0 | if (ret != LDB_SUCCESS) { |
585 | 0 | DEBUG(1, ("Search failed: %s\n", ldb_errstring(ldb))); |
586 | 0 | status = NT_STATUS_NONE_MAPPED; |
587 | 0 | goto failed; |
588 | 0 | } |
589 | | |
590 | 0 | if (res->count != 1) { |
591 | 0 | DEBUG(1, ("No CN=CONFIG record, idmap database is broken.\n")); |
592 | 0 | status = NT_STATUS_NONE_MAPPED; |
593 | 0 | goto failed; |
594 | 0 | } |
595 | | |
596 | 0 | hwm = ldb_msg_find_attr_as_uint(res->msgs[0], "xidNumber", -1); |
597 | 0 | if (hwm == (uint32_t)-1) { |
598 | 0 | hwm = low; |
599 | 0 | hwm_entry_exists = false; |
600 | 0 | } else { |
601 | 0 | hwm_entry_exists = true; |
602 | 0 | } |
603 | |
|
604 | 0 | if (hwm > high) { |
605 | 0 | DEBUG(1, ("Out of xids to allocate.\n")); |
606 | 0 | status = NT_STATUS_NONE_MAPPED; |
607 | 0 | goto failed; |
608 | 0 | } |
609 | | |
610 | 0 | hwm_msg = ldb_msg_new(tmp_ctx); |
611 | 0 | if (hwm_msg == NULL) { |
612 | 0 | DEBUG(1, ("Out of memory when creating ldb_message\n")); |
613 | 0 | status = NT_STATUS_NO_MEMORY; |
614 | 0 | goto failed; |
615 | 0 | } |
616 | | |
617 | 0 | hwm_msg->dn = dn; |
618 | |
|
619 | 0 | new_xid = hwm; |
620 | 0 | hwm++; |
621 | |
|
622 | 0 | hwm_string = talloc_asprintf(tmp_ctx, "%u", hwm); |
623 | 0 | if (hwm_string == NULL) { |
624 | 0 | status = NT_STATUS_NO_MEMORY; |
625 | 0 | goto failed; |
626 | 0 | } |
627 | | |
628 | 0 | dom_sid_str_buf(sid, &sid_string); |
629 | |
|
630 | 0 | unixid_string = talloc_asprintf(tmp_ctx, "%u", new_xid); |
631 | 0 | if (unixid_string == NULL) { |
632 | 0 | status = NT_STATUS_NO_MEMORY; |
633 | 0 | goto failed; |
634 | 0 | } |
635 | | |
636 | 0 | if (hwm_entry_exists) { |
637 | 0 | struct ldb_message_element *els; |
638 | 0 | struct ldb_val *vals; |
639 | | |
640 | | /* We're modifying the entry, not just adding a new one. */ |
641 | 0 | els = talloc_array(tmp_ctx, struct ldb_message_element, 2); |
642 | 0 | if (els == NULL) { |
643 | 0 | status = NT_STATUS_NO_MEMORY; |
644 | 0 | goto failed; |
645 | 0 | } |
646 | | |
647 | 0 | vals = talloc_array(tmp_ctx, struct ldb_val, 2); |
648 | 0 | if (vals == NULL) { |
649 | 0 | status = NT_STATUS_NO_MEMORY; |
650 | 0 | goto failed; |
651 | 0 | } |
652 | | |
653 | 0 | hwm_msg->num_elements = 2; |
654 | 0 | hwm_msg->elements = els; |
655 | |
|
656 | 0 | els[0].num_values = 1; |
657 | 0 | els[0].values = &vals[0]; |
658 | 0 | els[0].flags = LDB_FLAG_MOD_DELETE; |
659 | 0 | els[0].name = talloc_strdup(tmp_ctx, "xidNumber"); |
660 | 0 | if (els[0].name == NULL) { |
661 | 0 | status = NT_STATUS_NO_MEMORY; |
662 | 0 | goto failed; |
663 | 0 | } |
664 | | |
665 | 0 | els[1].num_values = 1; |
666 | 0 | els[1].values = &vals[1]; |
667 | 0 | els[1].flags = LDB_FLAG_MOD_ADD; |
668 | 0 | els[1].name = els[0].name; |
669 | |
|
670 | 0 | vals[0].data = (uint8_t *)unixid_string; |
671 | 0 | vals[0].length = strlen(unixid_string); |
672 | 0 | vals[1].data = (uint8_t *)hwm_string; |
673 | 0 | vals[1].length = strlen(hwm_string); |
674 | 0 | } else { |
675 | 0 | ret = ldb_msg_append_string(hwm_msg, "xidNumber", hwm_string, |
676 | 0 | LDB_FLAG_MOD_ADD); |
677 | 0 | if (ret != LDB_SUCCESS) |
678 | 0 | { |
679 | 0 | status = NT_STATUS_NONE_MAPPED; |
680 | 0 | goto failed; |
681 | 0 | } |
682 | 0 | } |
683 | | |
684 | 0 | ret = ldb_modify(ldb, hwm_msg); |
685 | 0 | if (ret != LDB_SUCCESS) { |
686 | 0 | DEBUG(1, ("Updating the xid high water mark failed: %s\n", |
687 | 0 | ldb_errstring(ldb))); |
688 | 0 | status = NT_STATUS_NONE_MAPPED; |
689 | 0 | goto failed; |
690 | 0 | } |
691 | | |
692 | 0 | map_msg = ldb_msg_new(tmp_ctx); |
693 | 0 | if (map_msg == NULL) { |
694 | 0 | status = NT_STATUS_NO_MEMORY; |
695 | 0 | goto failed; |
696 | 0 | } |
697 | | |
698 | 0 | map_msg->dn = ldb_dn_new_fmt(tmp_ctx, ldb, "CN=%s", sid_string.buf); |
699 | 0 | if (map_msg->dn == NULL) { |
700 | 0 | status = NT_STATUS_NO_MEMORY; |
701 | 0 | goto failed; |
702 | 0 | } |
703 | | |
704 | 0 | ret = ldb_msg_add_string(map_msg, "xidNumber", unixid_string); |
705 | 0 | if (ret != LDB_SUCCESS) { |
706 | 0 | status = NT_STATUS_NONE_MAPPED; |
707 | 0 | goto failed; |
708 | 0 | } |
709 | | |
710 | 0 | ret = idmap_msg_add_dom_sid(idmap_ctx, tmp_ctx, map_msg, "objectSid", |
711 | 0 | sid); |
712 | 0 | if (ret != LDB_SUCCESS) { |
713 | 0 | status = NT_STATUS_NONE_MAPPED; |
714 | 0 | goto failed; |
715 | 0 | } |
716 | | |
717 | 0 | ret = ldb_msg_add_string(map_msg, "objectClass", "sidMap"); |
718 | 0 | if (ret != LDB_SUCCESS) { |
719 | 0 | status = NT_STATUS_NONE_MAPPED; |
720 | 0 | goto failed; |
721 | 0 | } |
722 | | |
723 | 0 | ret = ldb_msg_add_string(map_msg, "type", "ID_TYPE_BOTH"); |
724 | 0 | if (ret != LDB_SUCCESS) { |
725 | 0 | status = NT_STATUS_NONE_MAPPED; |
726 | 0 | goto failed; |
727 | 0 | } |
728 | | |
729 | 0 | ret = ldb_msg_add_string(map_msg, "cn", sid_string.buf); |
730 | 0 | if (ret != LDB_SUCCESS) { |
731 | 0 | status = NT_STATUS_NONE_MAPPED; |
732 | 0 | goto failed; |
733 | 0 | } |
734 | | |
735 | 0 | ret = ldb_add(ldb, map_msg); |
736 | 0 | if (ret != LDB_SUCCESS) { |
737 | 0 | DEBUG(1, ("Adding a sidmap failed: %s\n", ldb_errstring(ldb))); |
738 | 0 | status = NT_STATUS_NONE_MAPPED; |
739 | 0 | goto failed; |
740 | 0 | } |
741 | | |
742 | 0 | trans = ldb_transaction_commit(ldb); |
743 | 0 | if (trans != LDB_SUCCESS) { |
744 | 0 | DEBUG(1, ("Transaction failed: %s\n", ldb_errstring(ldb))); |
745 | 0 | status = NT_STATUS_NONE_MAPPED; |
746 | 0 | goto failed; |
747 | 0 | } |
748 | | |
749 | 0 | unixid->id = new_xid; |
750 | 0 | unixid->type = ID_TYPE_BOTH; |
751 | 0 | talloc_free(tmp_ctx); |
752 | 0 | return NT_STATUS_OK; |
753 | | |
754 | 0 | failed: |
755 | 0 | if (trans == LDB_SUCCESS) ldb_transaction_cancel(ldb); |
756 | 0 | talloc_free(tmp_ctx); |
757 | 0 | return status; |
758 | 0 | } |
759 | | |
760 | | /** |
761 | | * Convert an array of unixids to the corresponding array of SIDs |
762 | | * |
763 | | * \param idmap_ctx idmap context to use |
764 | | * \param mem_ctx talloc context the memory for the dom_sids is allocated |
765 | | * from. |
766 | | * \param count length of id_mapping array. |
767 | | * \param id array of id_mappings. |
768 | | * \return NT_STATUS_OK on success, NT_STATUS_NONE_MAPPED if mapping is not |
769 | | * possible at all, NT_STATUS_SOME_UNMAPPED if some mappings worked and some |
770 | | * did not. |
771 | | */ |
772 | | |
773 | | NTSTATUS idmap_xids_to_sids(struct idmap_context *idmap_ctx, |
774 | | TALLOC_CTX *mem_ctx, |
775 | | struct id_map **id) |
776 | 0 | { |
777 | 0 | unsigned int i, error_count = 0; |
778 | 0 | NTSTATUS status; |
779 | |
|
780 | 0 | for (i = 0; id && id[i]; i++) { |
781 | 0 | status = idmap_xid_to_sid(idmap_ctx, mem_ctx, |
782 | 0 | &id[i]->xid, &id[i]->sid); |
783 | 0 | if (NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) { |
784 | 0 | status = idmap_xid_to_sid(idmap_ctx, mem_ctx, |
785 | 0 | &id[i]->xid, |
786 | 0 | &id[i]->sid); |
787 | 0 | } |
788 | 0 | if (!NT_STATUS_IS_OK(status)) { |
789 | 0 | DEBUG(1, ("idmapping xid_to_sid failed for id[%d]=%lu: %s\n", |
790 | 0 | i, (unsigned long)id[i]->xid.id, nt_errstr(status))); |
791 | 0 | error_count++; |
792 | 0 | id[i]->status = ID_UNMAPPED; |
793 | 0 | } else { |
794 | 0 | id[i]->status = ID_MAPPED; |
795 | 0 | } |
796 | 0 | } |
797 | |
|
798 | 0 | if (error_count == i) { |
799 | | /* Mapping did not work at all. */ |
800 | 0 | return NT_STATUS_NONE_MAPPED; |
801 | 0 | } else if (error_count > 0) { |
802 | | /* Some mappings worked, some did not. */ |
803 | 0 | return STATUS_SOME_UNMAPPED; |
804 | 0 | } else { |
805 | 0 | return NT_STATUS_OK; |
806 | 0 | } |
807 | 0 | } |
808 | | |
809 | | /** |
810 | | * Convert an array of SIDs to the corresponding array of unixids |
811 | | * |
812 | | * \param idmap_ctx idmap context to use |
813 | | * \param mem_ctx talloc context the memory for the unixids is allocated |
814 | | * from. |
815 | | * \param count length of id_mapping array. |
816 | | * \param id array of id_mappings. |
817 | | * \return NT_STATUS_OK on success, NT_STATUS_NONE_MAPPED if mapping is not |
818 | | * possible at all, NT_STATUS_SOME_UNMAPPED if some mappings worked and some |
819 | | * did not. |
820 | | */ |
821 | | |
822 | | NTSTATUS idmap_sids_to_xids(struct idmap_context *idmap_ctx, |
823 | | TALLOC_CTX *mem_ctx, |
824 | | struct id_map **id) |
825 | 0 | { |
826 | 0 | unsigned int i, error_count = 0; |
827 | 0 | NTSTATUS status; |
828 | |
|
829 | 0 | for (i = 0; id && id[i]; i++) { |
830 | 0 | status = idmap_sid_to_xid(idmap_ctx, mem_ctx, |
831 | 0 | id[i]->sid, &id[i]->xid); |
832 | 0 | if (NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) { |
833 | 0 | status = idmap_sid_to_xid(idmap_ctx, mem_ctx, |
834 | 0 | id[i]->sid, |
835 | 0 | &id[i]->xid); |
836 | 0 | } |
837 | 0 | if (!NT_STATUS_IS_OK(status)) { |
838 | 0 | struct dom_sid_buf buf; |
839 | 0 | DEBUG(1, ("idmapping sid_to_xid failed for id[%d]=%s: %s\n", |
840 | 0 | i, |
841 | 0 | dom_sid_str_buf(id[i]->sid, &buf), |
842 | 0 | nt_errstr(status))); |
843 | 0 | error_count++; |
844 | 0 | id[i]->status = ID_UNMAPPED; |
845 | 0 | } else { |
846 | 0 | id[i]->status = ID_MAPPED; |
847 | 0 | } |
848 | 0 | } |
849 | |
|
850 | 0 | if (error_count == i) { |
851 | | /* Mapping did not work at all. */ |
852 | 0 | return NT_STATUS_NONE_MAPPED; |
853 | 0 | } else if (error_count > 0) { |
854 | | /* Some mappings worked, some did not. */ |
855 | 0 | return STATUS_SOME_UNMAPPED; |
856 | 0 | } else { |
857 | 0 | return NT_STATUS_OK; |
858 | 0 | } |
859 | 0 | } |
860 | | |