s4-idmap: Add mapping using uidNumber and gidNumber like idmap_ad
[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                         ret = dsdb_search_one(idmap_ctx->samdb, tmp_ctx, &msg, NULL, LDB_SCOPE_SUBTREE, 
234                                               sam_attrs, 0,
235                                               "(&(sAMaccountType:" LDB_OID_COMPARATOR_AND ":=%u)(uidNumber=%u)(objectSid=*)"
236                                               "(|(objectClass=posixAccount)(objectClass=posixGroup)))",
237                                               ATYPE_ACCOUNT, unixid->id);
238                         if (ret == LDB_ERR_CONSTRAINT_VIOLATION) {
239                                 DEBUG(1, ("Search for uidNumber=%lu gave duplicate results, failing to map to a SID!\n",
240                                           (unsigned long)unixid->id));
241                                 status = NT_STATUS_NONE_MAPPED;
242                                 goto failed;
243                         } else if (ret == LDB_SUCCESS) {
244                                 *sid = samdb_result_dom_sid(mem_ctx, msg, "objectSid");
245                                 if (*sid) {
246                                         DEBUG(1, ("Search for uidNumber=%lu did not return an objectSid!\n",
247                                                   (unsigned long)unixid->id));
248                                         status = NT_STATUS_NONE_MAPPED;
249                                         goto failed;
250                                 }
251                                 talloc_free(tmp_ctx);
252                                 return NT_STATUS_OK;
253                         } else if (ret != LDB_ERR_NO_SUCH_OBJECT) {
254                                 DEBUG(1, ("Search for uidNumber=%lu gave '%s', failing to map to a SID!\n",
255                                           (unsigned long)unixid->id, ldb_errstring(idmap_ctx->samdb)));
256                                 status = NT_STATUS_NONE_MAPPED;
257                                 goto failed;
258                         }
259
260                         id_type = "ID_TYPE_UID";
261                         break;
262                 case ID_TYPE_GID:
263                         ret = dsdb_search_one(idmap_ctx->samdb, tmp_ctx, &msg, NULL, LDB_SCOPE_SUBTREE, 
264                                               sam_attrs, 0,
265                                               "(&(|(sAMaccountType=%u)(sAMaccountType=%u))(gidNumber=%u)"
266                                               "(|(objectClass=posixAccount)(objectClass=posixGroup)))",
267                                               ATYPE_SECURITY_GLOBAL_GROUP, ATYPE_SECURITY_LOCAL_GROUP, unixid->id);
268                         if (ret == LDB_ERR_CONSTRAINT_VIOLATION) {
269                                 DEBUG(1, ("Search for gidNumber=%lu gave duplicate results, failing to map to a SID!\n",
270                                           (unsigned long)unixid->id));
271                                 status = NT_STATUS_NONE_MAPPED;
272                                 goto failed;
273                         } else if (ret == LDB_SUCCESS) {
274                                 *sid = samdb_result_dom_sid(mem_ctx, msg, "objectSid");
275                                 if (*sid) {
276                                         DEBUG(1, ("Search for gidNumber=%lu did not return an objectSid!\n",
277                                                   (unsigned long)unixid->id));
278                                         status = NT_STATUS_NONE_MAPPED;
279                                         goto failed;
280                                 }
281                                 talloc_free(tmp_ctx);
282                                 return NT_STATUS_OK;
283                         } else if (ret != LDB_ERR_NO_SUCH_OBJECT) {
284                                 DEBUG(1, ("Search for gidNumber=%lu gave '%s', failing to map to a SID!\n",
285                                           (unsigned long)unixid->id, ldb_errstring(idmap_ctx->samdb)));
286                                 status = NT_STATUS_NONE_MAPPED;
287                                 goto failed;
288                         }
289
290                         id_type = "ID_TYPE_GID";
291                         break;
292                 default:
293                         DEBUG(1, ("unixid->type must be type gid or uid (got %u) for lookup with id %lu\n",
294                                   (unsigned)unixid->type, (unsigned long)unixid->id));
295                         status = NT_STATUS_NONE_MAPPED;
296                         goto failed;
297         }
298
299         ret = ldb_search(ldb, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE,
300                                  NULL, "(&(|(type=ID_TYPE_BOTH)(type=%s))"
301                                  "(xidNumber=%u))", id_type, unixid->id);
302         if (ret != LDB_SUCCESS) {
303                 DEBUG(1, ("Search failed: %s\n", ldb_errstring(ldb)));
304                 status = NT_STATUS_NONE_MAPPED;
305                 goto failed;
306         }
307
308         if (res->count == 1) {
309                 *sid = idmap_msg_get_dom_sid(mem_ctx, res->msgs[0],
310                                              "objectSid");
311                 if (*sid == NULL) {
312                         DEBUG(1, ("Failed to get sid from db: %u\n", ret));
313                         status = NT_STATUS_NONE_MAPPED;
314                         goto failed;
315                 }
316                 talloc_free(tmp_ctx);
317                 return NT_STATUS_OK;
318         }
319
320         DEBUG(6, ("xid not found in idmap db, create S-1-22- SID.\n"));
321
322         /* For local users/groups , we just create a rid = uid/gid */
323         if (unixid->type == ID_TYPE_UID) {
324                 unix_sid = dom_sid_parse_talloc(tmp_ctx, "S-1-22-1");
325         } else {
326                 unix_sid = dom_sid_parse_talloc(tmp_ctx, "S-1-22-2");
327         }
328         if (unix_sid == NULL) {
329                 status = NT_STATUS_NO_MEMORY;
330                 goto failed;
331         }
332
333         new_sid = dom_sid_add_rid(mem_ctx, unix_sid, unixid->id);
334         if (new_sid == NULL) {
335                 status = NT_STATUS_NO_MEMORY;
336                 goto failed;
337         }
338
339         *sid = new_sid;
340         talloc_free(tmp_ctx);
341         return NT_STATUS_OK;
342
343 failed:
344         talloc_free(tmp_ctx);
345         return status;
346 }
347
348
349 /**
350  * Map a SID to an unixid struct.
351  *
352  * If no mapping exists, a new mapping will be created.
353  *
354  * \todo Check if SIDs can be resolved if lpcfg_idmap_trusted_only() == true
355  * \todo Fix backwards compatibility for Samba3
356  *
357  * \param idmap_ctx idmap context to use
358  * \param mem_ctx talloc context to use
359  * \param sid SID to map to an unixid struct
360  * \param unixid pointer to a unixid struct
361  * \return NT_STATUS_OK on success, NT_STATUS_INVALID_SID if the sid is not from
362  * a trusted domain and idmap trusted only = true, NT_STATUS_NONE_MAPPED if the
363  * mapping failed.
364  */
365 static NTSTATUS idmap_sid_to_xid(struct idmap_context *idmap_ctx,
366                                  TALLOC_CTX *mem_ctx,
367                                  const struct dom_sid *sid,
368                                  struct unixid *unixid)
369 {
370         int ret;
371         NTSTATUS status = NT_STATUS_NONE_MAPPED;
372         struct ldb_context *ldb = idmap_ctx->ldb_ctx;
373         struct ldb_dn *dn;
374         struct ldb_message *hwm_msg, *map_msg, *sam_msg;
375         struct ldb_result *res = NULL;
376         int trans;
377         uint32_t low, high, hwm, new_xid;
378         char *sid_string, *unixid_string, *hwm_string;
379         bool hwm_entry_exists;
380         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
381         const char *sam_attrs[] = {"uidNumber", "gidNumber", "samAccountType", NULL};
382
383         if (dom_sid_in_domain(idmap_ctx->unix_users_sid, sid)) {
384                 uint32_t rid;
385                 DEBUG(6, ("This is a local unix uid, just calculate that.\n"));
386                 status = dom_sid_split_rid(tmp_ctx, sid, NULL, &rid);
387                 if (!NT_STATUS_IS_OK(status)) {
388                         talloc_free(tmp_ctx);
389                         return status;
390                 }
391
392                 unixid->id = rid;
393                 unixid->type = ID_TYPE_UID;
394
395                 talloc_free(tmp_ctx);
396                 return NT_STATUS_OK;
397         }
398
399         if (dom_sid_in_domain(idmap_ctx->unix_groups_sid, sid)) {
400                 uint32_t rid;
401                 DEBUG(6, ("This is a local unix gid, just calculate that.\n"));
402                 status = dom_sid_split_rid(tmp_ctx, sid, NULL, &rid);
403                 if (!NT_STATUS_IS_OK(status)) {
404                         talloc_free(tmp_ctx);
405                         return status;
406                 }
407
408                 unixid->id = rid;
409                 unixid->type = ID_TYPE_GID;
410
411                 talloc_free(tmp_ctx);
412                 return NT_STATUS_OK;
413         }
414
415         /* 
416          * First check against our local DB, to see if this user has a
417          * mapping there.  This means that the Samba4 AD DC behaves
418          * much like a winbindd member server running idmap_ad
419          */
420         
421         ret = dsdb_search_one(idmap_ctx->samdb, tmp_ctx, &sam_msg, NULL, LDB_SCOPE_SUBTREE, sam_attrs, 0,
422                               "(&(objectSid=%s)"
423                               "(|(sAMaccountType:" LDB_OID_COMPARATOR_AND ":=%u)"
424                               "(sAMaccountType=%u)"
425                               "(sAMaccountType=%u))"
426                               "(|(uidNumber=*)(gidNumber=*))"
427                               "(|(objectClass=posixAccount)(objectClass=posixGroup)))",
428                               dom_sid_string(tmp_ctx, sid), ATYPE_ACCOUNT, ATYPE_SECURITY_GLOBAL_GROUP, ATYPE_SECURITY_LOCAL_GROUP);
429         if (ret == LDB_ERR_CONSTRAINT_VIOLATION) {
430                 DEBUG(1, ("Search for objectSid=%s gave duplicate results, failing to map to a unix ID!\n",
431                           dom_sid_string(tmp_ctx, sid)));
432                 status = NT_STATUS_NONE_MAPPED;
433                 goto failed;
434         } else if (ret == LDB_SUCCESS) {
435                 uint32_t account_type = ldb_msg_find_attr_as_uint(sam_msg, "sAMaccountType", 0);
436                 if (account_type & ATYPE_ACCOUNT) {
437                         const struct ldb_val *v = ldb_msg_find_ldb_val(sam_msg, "uidNumber");
438                         if (v) {
439                                 unixid->type = ID_TYPE_UID;
440                                 unixid->id = ldb_msg_find_attr_as_uint(sam_msg, "uidNumber", -1);
441                                 talloc_free(tmp_ctx);
442                                 return NT_STATUS_OK;
443                         }
444
445                 } else if ((account_type == ATYPE_SECURITY_GLOBAL_GROUP) || (account_type == ATYPE_SECURITY_LOCAL_GROUP)) {
446                         const struct ldb_val *v = ldb_msg_find_ldb_val(sam_msg, "gidNumber");
447                         if (v) {
448                                 unixid->type = ID_TYPE_GID;
449                                 unixid->id = ldb_msg_find_attr_as_uint(sam_msg, "gidNumber", -1);
450                                 talloc_free(tmp_ctx);
451                                 return NT_STATUS_OK;
452                         }
453                 }
454         } else if (ret != LDB_ERR_NO_SUCH_OBJECT) {
455                 DEBUG(1, ("Search for objectSid=%s gave '%s', failing to map to a SID!\n",
456                           dom_sid_string(tmp_ctx, sid), ldb_errstring(idmap_ctx->samdb)));
457
458                 status = NT_STATUS_NONE_MAPPED;
459                 goto failed;
460         }
461
462         ret = ldb_search(ldb, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE,
463                                  NULL, "(&(objectClass=sidMap)(objectSid=%s))",
464                                  ldap_encode_ndr_dom_sid(tmp_ctx, sid));
465         if (ret != LDB_SUCCESS) {
466                 DEBUG(1, ("Search failed: %s\n", ldb_errstring(ldb)));
467                 talloc_free(tmp_ctx);
468                 return NT_STATUS_NONE_MAPPED;
469         }
470
471         if (res->count == 1) {
472                 const char *type = ldb_msg_find_attr_as_string(res->msgs[0],
473                                                                "type", NULL);
474                 new_xid = ldb_msg_find_attr_as_uint(res->msgs[0], "xidNumber",
475                                                     -1);
476                 if (new_xid == (uint32_t) -1) {
477                         DEBUG(1, ("Invalid xid mapping.\n"));
478                         talloc_free(tmp_ctx);
479                         return NT_STATUS_NONE_MAPPED;
480                 }
481
482                 if (type == NULL) {
483                         DEBUG(1, ("Invalid type for mapping entry.\n"));
484                         talloc_free(tmp_ctx);
485                         return NT_STATUS_NONE_MAPPED;
486                 }
487
488                 unixid->id = new_xid;
489
490                 if (strcmp(type, "ID_TYPE_BOTH") == 0) {
491                         unixid->type = ID_TYPE_BOTH;
492                 } else if (strcmp(type, "ID_TYPE_UID") == 0) {
493                         unixid->type = ID_TYPE_UID;
494                 } else {
495                         unixid->type = ID_TYPE_GID;
496                 }
497
498                 talloc_free(tmp_ctx);
499                 return NT_STATUS_OK;
500         }
501
502         DEBUG(6, ("No existing mapping found, attempting to create one.\n"));
503
504         trans = ldb_transaction_start(ldb);
505         if (trans != LDB_SUCCESS) {
506                 status = NT_STATUS_NONE_MAPPED;
507                 goto failed;
508         }
509
510         /* Redo the search to make sure noone changed the mapping while we
511          * weren't looking */
512         ret = ldb_search(ldb, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE,
513                                  NULL, "(&(objectClass=sidMap)(objectSid=%s))",
514                                  ldap_encode_ndr_dom_sid(tmp_ctx, sid));
515         if (ret != LDB_SUCCESS) {
516                 DEBUG(1, ("Search failed: %s\n", ldb_errstring(ldb)));
517                 status = NT_STATUS_NONE_MAPPED;
518                 goto failed;
519         }
520
521         if (res->count > 0) {
522                 DEBUG(1, ("Database changed while trying to add a sidmap.\n"));
523                 status = NT_STATUS_RETRY;
524                 goto failed;
525         }
526
527         /*FIXME: if lpcfg_idmap_trusted_only() == true, check if SID can be
528          * resolved here. */
529
530         ret = idmap_get_bounds(idmap_ctx, &low, &high);
531         if (ret != LDB_SUCCESS) {
532                 status = NT_STATUS_NONE_MAPPED;
533                 goto failed;
534         }
535
536         dn = ldb_dn_new(tmp_ctx, ldb, "CN=CONFIG");
537         if (dn == NULL) {
538                 status = NT_STATUS_NO_MEMORY;
539                 goto failed;
540         }
541
542         ret = ldb_search(ldb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, NULL, NULL);
543         if (ret != LDB_SUCCESS) {
544                 DEBUG(1, ("Search failed: %s\n", ldb_errstring(ldb)));
545                 status = NT_STATUS_NONE_MAPPED;
546                 goto failed;
547         }
548
549         if (res->count != 1) {
550                 DEBUG(1, ("No CN=CONFIG record, idmap database is broken.\n"));
551                 status = NT_STATUS_NONE_MAPPED;
552                 goto failed;
553         }
554
555         hwm = ldb_msg_find_attr_as_uint(res->msgs[0], "xidNumber", -1);
556         if (hwm == (uint32_t)-1) {
557                 hwm = low;
558                 hwm_entry_exists = false;
559         } else {
560                 hwm_entry_exists = true;
561         }
562
563         if (hwm > high) {
564                 DEBUG(1, ("Out of xids to allocate.\n"));
565                 status = NT_STATUS_NONE_MAPPED;
566                 goto failed;
567         }
568
569         hwm_msg = ldb_msg_new(tmp_ctx);
570         if (hwm_msg == NULL) {
571                 DEBUG(1, ("Out of memory when creating ldb_message\n"));
572                 status = NT_STATUS_NO_MEMORY;
573                 goto failed;
574         }
575
576         hwm_msg->dn = dn;
577
578         new_xid = hwm;
579         hwm++;
580
581         hwm_string = talloc_asprintf(tmp_ctx, "%u", hwm);
582         if (hwm_string == NULL) {
583                 status = NT_STATUS_NO_MEMORY;
584                 goto failed;
585         }
586
587         sid_string = dom_sid_string(tmp_ctx, sid);
588         if (sid_string == NULL) {
589                 status = NT_STATUS_NO_MEMORY;
590                 goto failed;
591         }
592
593         unixid_string = talloc_asprintf(tmp_ctx, "%u", new_xid);
594         if (unixid_string == NULL) {
595                 status = NT_STATUS_NO_MEMORY;
596                 goto failed;
597         }
598
599         if (hwm_entry_exists) {
600                 struct ldb_message_element *els;
601                 struct ldb_val *vals;
602
603                 /* We're modifying the entry, not just adding a new one. */
604                 els = talloc_array(tmp_ctx, struct ldb_message_element, 2);
605                 if (els == NULL) {
606                         status = NT_STATUS_NO_MEMORY;
607                         goto failed;
608                 }
609
610                 vals = talloc_array(tmp_ctx, struct ldb_val, 2);
611                 if (els == NULL) {
612                         status = NT_STATUS_NO_MEMORY;
613                         goto failed;
614                 }
615
616                 hwm_msg->num_elements = 2;
617                 hwm_msg->elements = els;
618
619                 els[0].num_values = 1;
620                 els[0].values = &vals[0];
621                 els[0].flags = LDB_FLAG_MOD_DELETE;
622                 els[0].name = talloc_strdup(tmp_ctx, "xidNumber");
623                 if (els[0].name == NULL) {
624                         status = NT_STATUS_NO_MEMORY;
625                         goto failed;
626                 }
627
628                 els[1].num_values = 1;
629                 els[1].values = &vals[1];
630                 els[1].flags = LDB_FLAG_MOD_ADD;
631                 els[1].name = els[0].name;
632
633                 vals[0].data = (uint8_t *)unixid_string;
634                 vals[0].length = strlen(unixid_string);
635                 vals[1].data = (uint8_t *)hwm_string;
636                 vals[1].length = strlen(hwm_string);
637         } else {
638                 ret = ldb_msg_add_empty(hwm_msg, "xidNumber", LDB_FLAG_MOD_ADD,
639                                         NULL);
640                 if (ret != LDB_SUCCESS) {
641                         status = NT_STATUS_NONE_MAPPED;
642                         goto failed;
643                 }
644
645                 ret = ldb_msg_add_string(hwm_msg, "xidNumber", hwm_string);
646                 if (ret != LDB_SUCCESS)
647                 {
648                         status = NT_STATUS_NONE_MAPPED;
649                         goto failed;
650                 }
651         }
652
653         ret = ldb_modify(ldb, hwm_msg);
654         if (ret != LDB_SUCCESS) {
655                 DEBUG(1, ("Updating the xid high water mark failed: %s\n",
656                           ldb_errstring(ldb)));
657                 status = NT_STATUS_NONE_MAPPED;
658                 goto failed;
659         }
660
661         map_msg = ldb_msg_new(tmp_ctx);
662         if (map_msg == NULL) {
663                 status = NT_STATUS_NO_MEMORY;
664                 goto failed;
665         }
666
667         map_msg->dn = ldb_dn_new_fmt(tmp_ctx, ldb, "CN=%s", sid_string);
668         if (map_msg->dn == NULL) {
669                 status = NT_STATUS_NO_MEMORY;
670                 goto failed;
671         }
672
673         ret = ldb_msg_add_string(map_msg, "xidNumber", unixid_string);
674         if (ret != LDB_SUCCESS) {
675                 status = NT_STATUS_NONE_MAPPED;
676                 goto failed;
677         }
678
679         ret = idmap_msg_add_dom_sid(idmap_ctx, tmp_ctx, map_msg, "objectSid",
680                         sid);
681         if (ret != LDB_SUCCESS) {
682                 status = NT_STATUS_NONE_MAPPED;
683                 goto failed;
684         }
685
686         ret = ldb_msg_add_string(map_msg, "objectClass", "sidMap");
687         if (ret != LDB_SUCCESS) {
688                 status = NT_STATUS_NONE_MAPPED;
689                 goto failed;
690         }
691
692         ret = ldb_msg_add_string(map_msg, "type", "ID_TYPE_BOTH");
693         if (ret != LDB_SUCCESS) {
694                 status = NT_STATUS_NONE_MAPPED;
695                 goto failed;
696         }
697
698         ret = ldb_msg_add_string(map_msg, "cn", sid_string);
699         if (ret != LDB_SUCCESS) {
700                 status = NT_STATUS_NONE_MAPPED;
701                 goto failed;
702         }
703
704         ret = ldb_add(ldb, map_msg);
705         if (ret != LDB_SUCCESS) {
706                 DEBUG(1, ("Adding a sidmap failed: %s\n", ldb_errstring(ldb)));
707                 status = NT_STATUS_NONE_MAPPED;
708                 goto failed;
709         }
710
711         trans = ldb_transaction_commit(ldb);
712         if (trans != LDB_SUCCESS) {
713                 DEBUG(1, ("Transaction failed: %s\n", ldb_errstring(ldb)));
714                 status = NT_STATUS_NONE_MAPPED;
715                 goto failed;
716         }
717
718         unixid->id = new_xid;
719         unixid->type = ID_TYPE_BOTH;
720         talloc_free(tmp_ctx);
721         return NT_STATUS_OK;
722
723 failed:
724         if (trans == LDB_SUCCESS) ldb_transaction_cancel(ldb);
725         talloc_free(tmp_ctx);
726         return status;
727 }
728
729 /**
730  * Convert an array of unixids to the corresponding array of SIDs
731  *
732  * \param idmap_ctx idmap context to use
733  * \param mem_ctx talloc context the memory for the dom_sids is allocated
734  * from.
735  * \param count length of id_mapping array.
736  * \param id array of id_mappings.
737  * \return NT_STATUS_OK on success, NT_STATUS_NONE_MAPPED if mapping is not
738  * possible at all, NT_STATUS_SOME_UNMAPPED if some mappings worked and some
739  * did not.
740  */
741
742 NTSTATUS idmap_xids_to_sids(struct idmap_context *idmap_ctx,
743                             TALLOC_CTX *mem_ctx,
744                             struct id_map **id)
745 {
746         unsigned int i, error_count = 0;
747         NTSTATUS status;
748
749         for (i = 0; id && id[i]; i++) {
750                 status = idmap_xid_to_sid(idmap_ctx, mem_ctx,
751                                                 &id[i]->xid, &id[i]->sid);
752                 if (NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) {
753                         status = idmap_xid_to_sid(idmap_ctx, mem_ctx,
754                                                         &id[i]->xid,
755                                                         &id[i]->sid);
756                 }
757                 if (!NT_STATUS_IS_OK(status)) {
758                         DEBUG(1, ("idmapping xid_to_sid failed for id[%d]=%lu: %s\n",
759                                   i, (unsigned long)id[i]->xid.id, nt_errstr(status)));
760                         error_count++;
761                         id[i]->status = ID_UNMAPPED;
762                 } else {
763                         id[i]->status = ID_MAPPED;
764                 }
765         }
766
767         if (error_count == i) {
768                 /* Mapping did not work at all. */
769                 return NT_STATUS_NONE_MAPPED;
770         } else if (error_count > 0) {
771                 /* Some mappings worked, some did not. */
772                 return STATUS_SOME_UNMAPPED;
773         } else {
774                 return NT_STATUS_OK;
775         }
776 }
777
778 /**
779  * Convert an array of SIDs to the corresponding array of unixids
780  *
781  * \param idmap_ctx idmap context to use
782  * \param mem_ctx talloc context the memory for the unixids is allocated
783  * from.
784  * \param count length of id_mapping array.
785  * \param id array of id_mappings.
786  * \return NT_STATUS_OK on success, NT_STATUS_NONE_MAPPED if mapping is not
787  * possible at all, NT_STATUS_SOME_UNMAPPED if some mappings worked and some
788  * did not.
789  */
790
791 NTSTATUS idmap_sids_to_xids(struct idmap_context *idmap_ctx,
792                             TALLOC_CTX *mem_ctx,
793                             struct id_map **id)
794 {
795         unsigned int i, error_count = 0;
796         NTSTATUS status;
797
798         for (i = 0; id && id[i]; i++) {
799                 status = idmap_sid_to_xid(idmap_ctx, mem_ctx,
800                                           id[i]->sid, &id[i]->xid);
801                 if (NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) {
802                         status = idmap_sid_to_xid(idmap_ctx, mem_ctx,
803                                                   id[i]->sid,
804                                                   &id[i]->xid);
805                 }
806                 if (!NT_STATUS_IS_OK(status)) {
807                         char *str = dom_sid_string(mem_ctx, id[i]->sid);
808                         DEBUG(1, ("idmapping sid_to_xid failed for id[%d]=%s: %s\n",
809                                   i, str, nt_errstr(status)));
810                         talloc_free(str);
811                         error_count++;
812                         id[i]->status = ID_UNMAPPED;
813                 } else {
814                         id[i]->status = ID_MAPPED;
815                 }
816         }
817
818         if (error_count == i) {
819                 /* Mapping did not work at all. */
820                 return NT_STATUS_NONE_MAPPED;
821         } else if (error_count > 0) {
822                 /* Some mappings worked, some did not. */
823                 return STATUS_SOME_UNMAPPED;
824         } else {
825                 return NT_STATUS_OK;
826         }
827 }
828