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