2 Unix SMB/CIFS implementation.
6 Copyright (C) Tim Potter 2000
7 Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
8 Copyright (C) Jeremy Allison 2006
9 Copyright (C) Simo Sorce 2003-2006
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
30 #define DBGC_CLASS DBGC_IDMAP
32 /* High water mark keys */
33 #define HWM_GROUP "GROUP HWM"
34 #define HWM_USER "USER HWM"
36 static struct idmap_tdb_state
{
38 /* User and group id pool */
39 uid_t low_uid
, high_uid
; /* Range of uids to allocate */
40 gid_t low_gid
, high_gid
; /* Range of gids to allocate */
44 /*****************************************************************************
45 For idmap conversion: convert one record to new format
46 Ancient versions (eg 2.2.3a) of winbindd_idmap.tdb mapped DOMAINNAME/rid
48 *****************************************************************************/
49 static int convert_fn(TDB_CONTEXT
*tdb
, TDB_DATA key
, TDB_DATA data
, void *state
)
51 struct winbindd_domain
*domain
;
58 BOOL
*failed
= (BOOL
*)state
;
60 DEBUG(10,("Converting %s\n", key
.dptr
));
62 p
= strchr(key
.dptr
, '/');
67 fstrcpy(dom_name
, key
.dptr
);
70 domain
= find_domain_from_name(dom_name
);
72 /* We must delete the old record. */
73 DEBUG(0,("Unable to find domain %s\n", dom_name
));
74 DEBUG(0,("deleting record %s\n", key
.dptr
));
76 if (tdb_delete(tdb
, key
) != 0) {
77 DEBUG(0, ("Unable to delete record %s\n", key
.dptr
));
87 sid_copy(&sid
, &domain
->sid
);
88 sid_append_rid(&sid
, rid
);
90 sid_to_string(keystr
, &sid
);
92 key2
.dsize
= strlen(keystr
) + 1;
94 if (tdb_store(tdb
, key2
, data
, TDB_INSERT
) != 0) {
95 DEBUG(0,("Unable to add record %s\n", key2
.dptr
));
100 if (tdb_store(tdb
, data
, key2
, TDB_REPLACE
) != 0) {
101 DEBUG(0,("Unable to update record %s\n", data
.dptr
));
106 if (tdb_delete(tdb
, key
) != 0) {
107 DEBUG(0,("Unable to delete record %s\n", key
.dptr
));
115 /*****************************************************************************
116 Convert the idmap database from an older version.
117 *****************************************************************************/
119 static BOOL
idmap_tdb_convert(const char *idmap_name
)
122 BOOL bigendianheader
;
124 TDB_CONTEXT
*idmap_tdb
;
126 if (!(idmap_tdb
= tdb_open_log(idmap_name
, 0,
129 DEBUG(0, ("Unable to open idmap database\n"));
133 bigendianheader
= (tdb_get_flags(idmap_tdb
) & TDB_BIGENDIAN
) ? True
: False
;
135 vers
= tdb_fetch_int32(idmap_tdb
, "IDMAP_VERSION");
137 if (((vers
== -1) && bigendianheader
) || (IREV(vers
) == IDMAP_VERSION
)) {
138 /* Arrggghh ! Bytereversed or old big-endian - make order independent ! */
140 * high and low records were created on a
141 * big endian machine and will need byte-reversing.
146 wm
= tdb_fetch_int32(idmap_tdb
, HWM_USER
);
151 wm
= idmap_tdb_state
.low_uid
;
154 if (tdb_store_int32(idmap_tdb
, HWM_USER
, wm
) == -1) {
155 DEBUG(0, ("Unable to byteswap user hwm in idmap database\n"));
156 tdb_close(idmap_tdb
);
160 wm
= tdb_fetch_int32(idmap_tdb
, HWM_GROUP
);
164 wm
= idmap_tdb_state
.low_gid
;
167 if (tdb_store_int32(idmap_tdb
, HWM_GROUP
, wm
) == -1) {
168 DEBUG(0, ("Unable to byteswap group hwm in idmap database\n"));
169 tdb_close(idmap_tdb
);
174 /* the old format stored as DOMAIN/rid - now we store the SID direct */
175 tdb_traverse(idmap_tdb
, convert_fn
, &failed
);
178 DEBUG(0, ("Problem during conversion\n"));
179 tdb_close(idmap_tdb
);
183 if (tdb_store_int32(idmap_tdb
, "IDMAP_VERSION", IDMAP_VERSION
) == -1) {
184 DEBUG(0, ("Unable to dtore idmap version in databse\n"));
185 tdb_close(idmap_tdb
);
189 tdb_close(idmap_tdb
);
193 /*****************************************************************************
194 Convert the idmap database from an older version if necessary
195 *****************************************************************************/
197 BOOL
idmap_tdb_upgrade(TALLOC_CTX
*ctx
, const char *tdbfile
)
201 DEBUG(0, ("Upgrading winbindd_idmap.tdb from an old version\n"));
203 backup_name
= talloc_asprintf(ctx
, "%s.bak", tdbfile
);
204 if ( ! backup_name
) {
205 DEBUG(0, ("Out of memory!\n"));
209 if (backup_tdb(tdbfile
, backup_name
, 0) != 0) {
210 DEBUG(0, ("Could not backup idmap database\n"));
211 talloc_free(backup_name
);
215 talloc_free(backup_name
);
216 return idmap_tdb_convert(tdbfile
);
219 /* WARNING: We can't open a tdb twice inthe same process, for that reason
220 * I'm going to use a hack with open ref counts to open the winbindd_idmap.tdb
221 * only once. We will later decide whether to split the db in multiple files
222 * or come up with a better solution to share them. */
224 static TDB_CONTEXT
*idmap_tdb_common_ctx
;
225 static int idmap_tdb_open_ref_count
= 0;
227 static NTSTATUS
idmap_tdb_open_db(TALLOC_CTX
*memctx
, TDB_CONTEXT
**tdbctx
)
231 SMB_STRUCT_STAT stbuf
;
232 char *tdbfile
= NULL
;
234 BOOL tdb_is_new
= False
;
236 if (idmap_tdb_open_ref_count
) { /* the tdb has already been opened */
237 idmap_tdb_open_ref_count
++;
238 *tdbctx
= idmap_tdb_common_ctx
;
242 /* use our own context here */
243 ctx
= talloc_new(memctx
);
245 DEBUG(0, ("Out of memory!\n"));
246 return NT_STATUS_NO_MEMORY
;
249 /* use the old database if present */
250 tdbfile
= talloc_strdup(ctx
, lock_path("winbindd_idmap.tdb"));
252 DEBUG(0, ("Out of memory!\n"));
253 ret
= NT_STATUS_NO_MEMORY
;
257 if (!file_exist(tdbfile
, &stbuf
)) {
261 DEBUG(10,("Opening tdbfile %s\n", tdbfile
));
263 /* Open idmap repository */
264 if (!(idmap_tdb_common_ctx
= tdb_open_log(tdbfile
, 0, TDB_DEFAULT
, O_RDWR
| O_CREAT
, 0644))) {
265 DEBUG(0, ("Unable to open idmap database\n"));
266 ret
= NT_STATUS_UNSUCCESSFUL
;
271 /* the file didn't existed before opening it, let's
272 * store idmap version as nobody else yet opened and
273 * stored it. I do not like this method but didn't
274 * found a way to understand if an opened tdb have
275 * been just created or not --- SSS */
276 tdb_store_int32(idmap_tdb_common_ctx
, "IDMAP_VERSION", IDMAP_VERSION
);
279 /* check against earlier versions */
280 version
= tdb_fetch_int32(idmap_tdb_common_ctx
, "IDMAP_VERSION");
281 if (version
!= IDMAP_VERSION
) {
283 /* backup_tdb expects the tdb not to be open */
284 tdb_close(idmap_tdb_common_ctx
);
286 if ( ! idmap_tdb_upgrade(ctx
, tdbfile
)) {
288 DEBUG(0, ("Unable to open idmap database, it's in an old formati, and upgrade failed!\n"));
289 ret
= NT_STATUS_INTERNAL_DB_ERROR
;
293 /* Re-Open idmap repository */
294 if (!(idmap_tdb_common_ctx
= tdb_open_log(tdbfile
, 0, TDB_DEFAULT
, O_RDWR
| O_CREAT
, 0644))) {
295 DEBUG(0, ("Unable to open idmap database\n"));
296 ret
= NT_STATUS_UNSUCCESSFUL
;
301 *tdbctx
= idmap_tdb_common_ctx
;
302 idmap_tdb_open_ref_count
++;
310 /* NEVER use tdb_close() except for the conversion routines that are guaranteed
311 * to run only when the database is opened the first time, always use this function. */
313 BOOL
idmap_tdb_tdb_close(TDB_CONTEXT
*tdbctx
)
315 if (tdbctx
!= idmap_tdb_common_ctx
) {
316 DEBUG(0, ("ERROR: Invalid tdb context!"));
320 idmap_tdb_open_ref_count
--;
321 if (idmap_tdb_open_ref_count
) {
325 return tdb_close(idmap_tdb_common_ctx
);
328 /**********************************************************************
329 IDMAP ALLOC TDB BACKEND
330 **********************************************************************/
332 static TDB_CONTEXT
*idmap_alloc_tdb
;
334 /**********************************
335 Initialise idmap alloc database.
336 **********************************/
338 static NTSTATUS
idmap_tdb_alloc_init( const char *params
)
348 /* use our own context here */
349 ctx
= talloc_new(NULL
);
351 DEBUG(0, ("Out of memory!\n"));
352 return NT_STATUS_NO_MEMORY
;
355 ret
= idmap_tdb_open_db(ctx
, &idmap_alloc_tdb
);
356 if ( ! NT_STATUS_IS_OK(ret
)) {
364 idmap_tdb_state
.low_uid
= 0;
365 idmap_tdb_state
.high_uid
= 0;
366 idmap_tdb_state
.low_gid
= 0;
367 idmap_tdb_state
.high_gid
= 0;
369 range
= lp_parm_const_string(-1, "idmap alloc config", "range", NULL
);
370 if (range
&& range
[0]) {
371 unsigned low_id
, high_id
;
373 if (sscanf(range
, "%u - %u", &low_id
, &high_id
) == 2) {
374 if (low_id
< high_id
) {
375 idmap_tdb_state
.low_gid
= idmap_tdb_state
.low_uid
= low_id
;
376 idmap_tdb_state
.high_gid
= idmap_tdb_state
.high_uid
= high_id
;
378 DEBUG(1, ("ERROR: invalid idmap alloc range [%s]", range
));
381 DEBUG(1, ("ERROR: invalid syntax for idmap alloc config:range [%s]", range
));
385 /* Create high water marks for group and user id */
386 if (lp_idmap_uid(&low_uid
, &high_uid
)) {
387 idmap_tdb_state
.low_uid
= low_uid
;
388 idmap_tdb_state
.high_uid
= high_uid
;
391 if (lp_idmap_gid(&low_gid
, &high_gid
)) {
392 idmap_tdb_state
.low_gid
= low_gid
;
393 idmap_tdb_state
.high_gid
= high_gid
;
396 if (idmap_tdb_state
.high_uid
<= idmap_tdb_state
.low_uid
) {
397 DEBUG(1, ("idmap uid range missing or invalid\n"));
398 DEBUGADD(1, ("idmap will be unable to map foreign SIDs\n"));
399 return NT_STATUS_UNSUCCESSFUL
;
403 if (((low_id
= tdb_fetch_int32(idmap_alloc_tdb
, HWM_USER
)) == -1) ||
404 (low_id
< idmap_tdb_state
.low_uid
)) {
405 if (tdb_store_int32(idmap_alloc_tdb
, HWM_USER
, idmap_tdb_state
.low_uid
) == -1) {
406 DEBUG(0, ("Unable to initialise user hwm in idmap database\n"));
407 return NT_STATUS_INTERNAL_DB_ERROR
;
412 if (idmap_tdb_state
.high_gid
<= idmap_tdb_state
.low_gid
) {
413 DEBUG(1, ("idmap gid range missing or invalid\n"));
414 DEBUGADD(1, ("idmap will be unable to map foreign SIDs\n"));
415 return NT_STATUS_UNSUCCESSFUL
;
419 if (((low_id
= tdb_fetch_int32(idmap_alloc_tdb
, HWM_GROUP
)) == -1) ||
420 (low_id
< idmap_tdb_state
.low_gid
)) {
421 if (tdb_store_int32(idmap_alloc_tdb
, HWM_GROUP
, idmap_tdb_state
.low_gid
) == -1) {
422 DEBUG(0, ("Unable to initialise group hwm in idmap database\n"));
423 return NT_STATUS_INTERNAL_DB_ERROR
;
431 /**********************************
433 **********************************/
435 static NTSTATUS
idmap_tdb_allocate_id(struct unixid
*xid
)
443 /* Get current high water mark */
449 high_hwm
= idmap_tdb_state
.high_uid
;
455 high_hwm
= idmap_tdb_state
.high_gid
;
459 DEBUG(2, ("Invalid ID type (0x%x)\n", xid
->type
));
460 return NT_STATUS_INVALID_PARAMETER
;
463 if ((hwm
= tdb_fetch_int32(idmap_alloc_tdb
, hwmkey
)) == -1) {
464 return NT_STATUS_INTERNAL_DB_ERROR
;
467 /* check it is in the range */
468 if (hwm
> high_hwm
) {
469 DEBUG(1, ("Fatal Error: %s range full!! (max: %lu)\n",
470 hwmtype
, (unsigned long)high_hwm
));
471 return NT_STATUS_UNSUCCESSFUL
;
474 /* fetch a new id and increment it */
475 ret
= tdb_change_uint32_atomic(idmap_alloc_tdb
, hwmkey
, &hwm
, 1);
477 DEBUG(1, ("Fatal error while fetching a new %s value\n!", hwmtype
));
478 return NT_STATUS_UNSUCCESSFUL
;
481 /* recheck it is in the range */
482 if (hwm
> high_hwm
) {
483 DEBUG(1, ("Fatal Error: %s range full!! (max: %lu)\n",
484 hwmtype
, (unsigned long)high_hwm
));
485 return NT_STATUS_UNSUCCESSFUL
;
489 DEBUG(10,("New %s = %d\n", hwmtype
, hwm
));
494 /**********************************
495 Get current highest id.
496 **********************************/
498 static NTSTATUS
idmap_tdb_get_hwm(struct unixid
*xid
)
505 /* Get current high water mark */
511 high_hwm
= idmap_tdb_state
.high_uid
;
517 high_hwm
= idmap_tdb_state
.high_gid
;
521 return NT_STATUS_INVALID_PARAMETER
;
524 if ((hwm
= tdb_fetch_int32(idmap_alloc_tdb
, hwmkey
)) == -1) {
525 return NT_STATUS_INTERNAL_DB_ERROR
;
530 /* Warn if it is out of range */
531 if (hwm
>= high_hwm
) {
532 DEBUG(0, ("Warning: %s range full!! (max: %lu)\n",
533 hwmtype
, (unsigned long)high_hwm
));
539 /**********************************
541 **********************************/
543 static NTSTATUS
idmap_tdb_set_hwm(struct unixid
*xid
)
550 /* Get current high water mark */
556 high_hwm
= idmap_tdb_state
.high_uid
;
562 high_hwm
= idmap_tdb_state
.high_gid
;
566 return NT_STATUS_INVALID_PARAMETER
;
571 if ((hwm
= tdb_store_int32(idmap_alloc_tdb
, hwmkey
, hwm
)) == -1) {
572 return NT_STATUS_INTERNAL_DB_ERROR
;
575 /* Warn if it is out of range */
576 if (hwm
>= high_hwm
) {
577 DEBUG(0, ("Warning: %s range full!! (max: %lu)\n",
578 hwmtype
, (unsigned long)high_hwm
));
584 /**********************************
586 **********************************/
588 static NTSTATUS
idmap_tdb_alloc_close(void)
590 if (idmap_alloc_tdb
) {
591 if (idmap_tdb_tdb_close(idmap_alloc_tdb
) == 0) {
594 return NT_STATUS_UNSUCCESSFUL
;
600 /**********************************************************************
601 IDMAP MAPPING TDB BACKEND
602 **********************************************************************/
604 struct idmap_tdb_context
{
606 uint32_t filter_low_id
;
607 uint32_t filter_high_id
;
610 /*****************************
611 Initialise idmap database.
612 *****************************/
614 static NTSTATUS
idmap_tdb_db_init(struct idmap_domain
*dom
)
617 struct idmap_tdb_context
*ctx
;
618 char *config_option
= NULL
;
621 ctx
= talloc(dom
, struct idmap_tdb_context
);
623 DEBUG(0, ("Out of memory!\n"));
624 return NT_STATUS_NO_MEMORY
;
627 config_option
= talloc_asprintf(ctx
, "idmap config %s", dom
->name
);
628 if ( ! config_option
) {
629 DEBUG(0, ("Out of memory!\n"));
630 ret
= NT_STATUS_NO_MEMORY
;
634 ret
= idmap_tdb_open_db(ctx
, &ctx
->tdb
);
635 if ( ! NT_STATUS_IS_OK(ret
)) {
639 range
= lp_parm_const_string(-1, config_option
, "range", NULL
);
641 (sscanf(range
, "%u - %u", &ctx
->filter_low_id
, &ctx
->filter_high_id
) != 2) ||
642 (ctx
->filter_low_id
> ctx
->filter_high_id
)) {
643 ctx
->filter_low_id
= 0;
644 ctx
->filter_high_id
= 0;
647 dom
->private_data
= ctx
;
648 dom
->initialized
= True
;
650 talloc_free(config_option
);
658 /**********************************
659 Single id to sid lookup function.
660 **********************************/
662 static NTSTATUS
idmap_tdb_id_to_sid(struct idmap_tdb_context
*ctx
, struct id_map
*map
)
668 return NT_STATUS_INVALID_PARAMETER
;
671 /* apply filters before checking */
672 if ((ctx
->filter_low_id
&& (map
->xid
.id
< ctx
->filter_low_id
)) ||
673 (ctx
->filter_high_id
&& (map
->xid
.id
> ctx
->filter_high_id
))) {
674 DEBUG(5, ("Requested id (%u) out of range (%u - %u). Filtered!\n",
675 map
->xid
.id
, ctx
->filter_low_id
, ctx
->filter_high_id
));
676 return NT_STATUS_NONE_MAPPED
;
679 switch (map
->xid
.type
) {
682 key
.dptr
= talloc_asprintf(ctx
, "UID %lu", (unsigned long)map
->xid
.id
);
686 key
.dptr
= talloc_asprintf(ctx
, "GID %lu", (unsigned long)map
->xid
.id
);
690 DEBUG(2, ("INVALID unix ID type: 0x02%x\n", map
->xid
.type
));
691 return NT_STATUS_INVALID_PARAMETER
;
694 /* final SAFE_FREE safe */
697 if (key
.dptr
== NULL
) {
698 DEBUG(0, ("Out of memory!\n"));
699 ret
= NT_STATUS_NO_MEMORY
;
703 key
.dsize
= strlen(key
.dptr
) + 1;
705 DEBUG(10,("Fetching record %s\n", key
.dptr
));
707 /* Check if the mapping exists */
708 data
= tdb_fetch(ctx
->tdb
, key
);
711 DEBUG(10,("Record %s not found\n", key
.dptr
));
712 ret
= NT_STATUS_NONE_MAPPED
;
716 if (!string_to_sid(map
->sid
, data
.dptr
)) {
717 DEBUG(10,("INVALID SID (%s) in record %s\n",
718 data
.dptr
, key
.dptr
));
719 ret
= NT_STATUS_INTERNAL_DB_ERROR
;
723 DEBUG(10,("Found record %s -> %s\n", key
.dptr
, data
.dptr
));
727 SAFE_FREE(data
.dptr
);
728 talloc_free(key
.dptr
);
732 /**********************************
733 Single sid to id lookup function.
734 **********************************/
736 static NTSTATUS
idmap_tdb_sid_to_id(struct idmap_tdb_context
*ctx
, struct id_map
*map
)
740 unsigned long rec_id
= 0;
742 if ((key
.dptr
= talloc_asprintf(ctx
, "%s", sid_string_static(map
->sid
))) == NULL
) {
743 DEBUG(0, ("Out of memory!\n"));
744 ret
= NT_STATUS_NO_MEMORY
;
748 key
.dsize
= strlen(key
.dptr
) + 1;
750 DEBUG(10,("Fetching record %s\n", key
.dptr
));
752 /* Check if sid is present in database */
753 data
= tdb_fetch(ctx
->tdb
, key
);
755 DEBUG(10,("Record %s not found\n", key
.dptr
));
756 ret
= NT_STATUS_NONE_MAPPED
;
760 /* What type of record is this ? */
761 if (sscanf(data
.dptr
, "UID %lu", &rec_id
) == 1) { /* Try a UID record. */
762 map
->xid
.id
= rec_id
;
763 map
->xid
.type
= ID_TYPE_UID
;
764 DEBUG(10,("Found uid record %s -> %s \n", key
.dptr
, data
.dptr
));
767 } else if (sscanf(data
.dptr
, "GID %lu", &rec_id
) == 1) { /* Try a GID record. */
768 map
->xid
.id
= rec_id
;
769 map
->xid
.type
= ID_TYPE_GID
;
770 DEBUG(10,("Found gid record %s -> %s \n", key
.dptr
, data
.dptr
));
773 } else { /* Unknown record type ! */
774 DEBUG(2, ("Found INVALID record %s -> %s\n", key
.dptr
, data
.dptr
));
775 ret
= NT_STATUS_INTERNAL_DB_ERROR
;
778 SAFE_FREE(data
.dptr
);
780 /* apply filters before returning result */
781 if ((ctx
->filter_low_id
&& (map
->xid
.id
< ctx
->filter_low_id
)) ||
782 (ctx
->filter_high_id
&& (map
->xid
.id
> ctx
->filter_high_id
))) {
783 DEBUG(5, ("Requested id (%u) out of range (%u - %u). Filtered!\n",
784 map
->xid
.id
, ctx
->filter_low_id
, ctx
->filter_high_id
));
785 ret
= NT_STATUS_NONE_MAPPED
;
789 talloc_free(key
.dptr
);
793 /**********************************
794 lookup a set of unix ids.
795 **********************************/
797 static NTSTATUS
idmap_tdb_unixids_to_sids(struct idmap_domain
*dom
, struct id_map
**ids
)
799 struct idmap_tdb_context
*ctx
;
803 /* make sure we initialized */
804 if ( ! dom
->initialized
) {
805 ret
= idmap_tdb_db_init(dom
);
806 if ( ! NT_STATUS_IS_OK(ret
)) {
811 ctx
= talloc_get_type(dom
->private_data
, struct idmap_tdb_context
);
813 for (i
= 0; ids
[i
]; i
++) {
814 ret
= idmap_tdb_id_to_sid(ctx
, ids
[i
]);
815 if ( ! NT_STATUS_IS_OK(ret
)) {
817 /* if it is just a failed mapping continue */
818 if (NT_STATUS_EQUAL(ret
, NT_STATUS_NONE_MAPPED
)) {
820 /* make sure it is marked as unmapped */
821 ids
[i
]->status
= ID_UNMAPPED
;
825 /* some fatal error occurred, return immediately */
829 /* all ok, id is mapped */
830 ids
[i
]->status
= ID_MAPPED
;
839 /**********************************
840 lookup a set of sids.
841 **********************************/
843 static NTSTATUS
idmap_tdb_sids_to_unixids(struct idmap_domain
*dom
, struct id_map
**ids
)
845 struct idmap_tdb_context
*ctx
;
849 /* make sure we initialized */
850 if ( ! dom
->initialized
) {
851 ret
= idmap_tdb_db_init(dom
);
852 if ( ! NT_STATUS_IS_OK(ret
)) {
857 ctx
= talloc_get_type(dom
->private_data
, struct idmap_tdb_context
);
859 for (i
= 0; ids
[i
]; i
++) {
860 ret
= idmap_tdb_sid_to_id(ctx
, ids
[i
]);
861 if ( ! NT_STATUS_IS_OK(ret
)) {
863 /* if it is just a failed mapping continue */
864 if (NT_STATUS_EQUAL(ret
, NT_STATUS_NONE_MAPPED
)) {
866 /* make sure it is marked as unmapped */
867 ids
[i
]->status
= ID_UNMAPPED
;
871 /* some fatal error occurred, return immediately */
875 /* all ok, id is mapped */
876 ids
[i
]->status
= ID_MAPPED
;
885 /**********************************
887 **********************************/
889 static NTSTATUS
idmap_tdb_set_mapping(struct idmap_domain
*dom
, const struct id_map
*map
)
891 struct idmap_tdb_context
*ctx
;
893 TDB_DATA ksid
, kid
, data
;
895 /* make sure we initialized */
896 if ( ! dom
->initialized
) {
897 ret
= idmap_tdb_db_init(dom
);
898 if ( ! NT_STATUS_IS_OK(ret
)) {
903 if (!map
|| !map
->sid
) {
904 return NT_STATUS_INVALID_PARAMETER
;
907 ksid
.dptr
= kid
.dptr
= data
.dptr
= NULL
;
909 /* TODO: should we filter a set_mapping using low/high filters ? */
911 ctx
= talloc_get_type(dom
->private_data
, struct idmap_tdb_context
);
913 switch (map
->xid
.type
) {
916 kid
.dptr
= talloc_asprintf(ctx
, "UID %lu", (unsigned long)map
->xid
.id
);
920 kid
.dptr
= talloc_asprintf(ctx
, "GID %lu", (unsigned long)map
->xid
.id
);
924 DEBUG(2, ("INVALID unix ID type: 0x02%x\n", map
->xid
.type
));
925 return NT_STATUS_INVALID_PARAMETER
;
928 if (kid
.dptr
== NULL
) {
929 DEBUG(0, ("ERROR: Out of memory!\n"));
930 ret
= NT_STATUS_NO_MEMORY
;
933 kid
.dsize
= strlen(kid
.dptr
) + 1;
935 if ((ksid
.dptr
= talloc_asprintf(ctx
, "%s", sid_string_static(map
->sid
))) == NULL
) {
936 DEBUG(0, ("Out of memory!\n"));
937 ret
= NT_STATUS_NO_MEMORY
;
940 ksid
.dsize
= strlen(ksid
.dptr
) + 1;
942 DEBUG(10, ("Storing %s <-> %s map\n", ksid
.dptr
, kid
.dptr
));
944 /* *DELETE* previous mappings if any.
945 * This is done both SID and [U|G]ID passed in */
947 /* Lock the record for this SID. */
948 if (tdb_chainlock(ctx
->tdb
, ksid
) != 0) {
949 DEBUG(10,("Failed to lock record %s. Error %s\n",
950 ksid
.dptr
, tdb_errorstr(ctx
->tdb
) ));
951 return NT_STATUS_UNSUCCESSFUL
;
954 data
= tdb_fetch(ctx
->tdb
, ksid
);
956 DEBUG(10, ("Deleting existing mapping %s <-> %s\n", data
.dptr
, ksid
.dptr
));
957 tdb_delete(ctx
->tdb
, data
);
958 tdb_delete(ctx
->tdb
, ksid
);
959 SAFE_FREE(data
.dptr
);
962 data
= tdb_fetch(ctx
->tdb
, kid
);
964 DEBUG(10,("Deleting existing mapping %s <-> %s\n", data
.dptr
, kid
.dptr
));
965 tdb_delete(ctx
->tdb
, data
);
966 tdb_delete(ctx
->tdb
, kid
);
967 SAFE_FREE(data
.dptr
);
970 if (tdb_store(ctx
->tdb
, ksid
, kid
, TDB_INSERT
) == -1) {
971 DEBUG(0, ("Error storing SID -> ID: %s\n", tdb_errorstr(ctx
->tdb
)));
972 tdb_chainunlock(ctx
->tdb
, ksid
);
973 ret
= NT_STATUS_UNSUCCESSFUL
;
976 if (tdb_store(ctx
->tdb
, kid
, ksid
, TDB_INSERT
) == -1) {
977 DEBUG(0, ("Error stroing ID -> SID: %s\n", tdb_errorstr(ctx
->tdb
)));
978 /* try to remove the previous stored SID -> ID map */
979 tdb_delete(ctx
->tdb
, ksid
);
980 tdb_chainunlock(ctx
->tdb
, ksid
);
981 ret
= NT_STATUS_UNSUCCESSFUL
;
985 tdb_chainunlock(ctx
->tdb
, ksid
);
986 DEBUG(10,("Stored %s <-> %s\n", ksid
.dptr
, kid
.dptr
));
990 talloc_free(ksid
.dptr
);
991 talloc_free(kid
.dptr
);
992 SAFE_FREE(data
.dptr
);
996 /**********************************
998 **********************************/
1000 static NTSTATUS
idmap_tdb_remove_mapping(struct idmap_domain
*dom
, const struct id_map
*map
)
1002 struct idmap_tdb_context
*ctx
;
1004 TDB_DATA ksid
, kid
, data
;
1006 /* make sure we initialized */
1007 if ( ! dom
->initialized
) {
1008 ret
= idmap_tdb_db_init(dom
);
1009 if ( ! NT_STATUS_IS_OK(ret
)) {
1014 if (!map
|| !map
->sid
) {
1015 return NT_STATUS_INVALID_PARAMETER
;
1018 ksid
.dptr
= kid
.dptr
= data
.dptr
= NULL
;
1020 /* TODO: should we filter a remove_mapping using low/high filters ? */
1022 ctx
= talloc_get_type(dom
->private_data
, struct idmap_tdb_context
);
1024 switch (map
->xid
.type
) {
1027 kid
.dptr
= talloc_asprintf(ctx
, "UID %lu", (unsigned long)map
->xid
.id
);
1031 kid
.dptr
= talloc_asprintf(ctx
, "GID %lu", (unsigned long)map
->xid
.id
);
1035 DEBUG(2, ("INVALID unix ID type: 0x02%x\n", map
->xid
.type
));
1036 return NT_STATUS_INVALID_PARAMETER
;
1039 if (kid
.dptr
== NULL
) {
1040 DEBUG(0, ("ERROR: Out of memory!\n"));
1041 ret
= NT_STATUS_NO_MEMORY
;
1044 kid
.dsize
= strlen(kid
.dptr
) + 1;
1046 if ((ksid
.dptr
= talloc_asprintf(ctx
, "%s", sid_string_static(map
->sid
))) == NULL
) {
1047 DEBUG(0, ("Out of memory!\n"));
1048 ret
= NT_STATUS_NO_MEMORY
;
1051 ksid
.dsize
= strlen(ksid
.dptr
) + 1;
1053 DEBUG(10, ("Checking %s <-> %s map\n", ksid
.dptr
, kid
.dptr
));
1055 /* Lock the record for this SID. */
1056 if (tdb_chainlock(ctx
->tdb
, ksid
) != 0) {
1057 DEBUG(10,("Failed to lock record %s. Error %s\n",
1058 ksid
.dptr
, tdb_errorstr(ctx
->tdb
) ));
1059 return NT_STATUS_UNSUCCESSFUL
;
1062 /* Check if sid is present in database */
1063 data
= tdb_fetch(ctx
->tdb
, ksid
);
1065 DEBUG(10,("Record %s not found\n", ksid
.dptr
));
1066 tdb_chainunlock(ctx
->tdb
, ksid
);
1067 ret
= NT_STATUS_NONE_MAPPED
;
1071 /* Check if sid is mapped to the specified ID */
1072 if ((data
.dsize
!= kid
.dsize
) ||
1073 (memcmp(data
.dptr
, kid
.dptr
, data
.dsize
) != 0)) {
1074 DEBUG(10,("Specified SID does not map to specified ID\n"));
1075 DEBUGADD(10,("Actual mapping is %s -> %s\n", ksid
.dptr
, data
.dptr
));
1076 tdb_chainunlock(ctx
->tdb
, ksid
);
1077 ret
= NT_STATUS_NONE_MAPPED
;
1081 DEBUG(10, ("Removing %s <-> %s map\n", ksid
.dptr
, kid
.dptr
));
1083 /* Delete previous mappings. */
1085 DEBUG(10, ("Deleting existing mapping %s -> %s\n", ksid
.dptr
, kid
.dptr
));
1086 tdb_delete(ctx
->tdb
, ksid
);
1088 DEBUG(10,("Deleting existing mapping %s -> %s\n", kid
.dptr
, ksid
.dptr
));
1089 tdb_delete(ctx
->tdb
, kid
);
1091 tdb_chainunlock(ctx
->tdb
, ksid
);
1095 talloc_free(ksid
.dptr
);
1096 talloc_free(kid
.dptr
);
1097 SAFE_FREE(data
.dptr
);
1101 /**********************************
1102 Close the idmap tdb instance
1103 **********************************/
1105 static NTSTATUS
idmap_tdb_close(struct idmap_domain
*dom
)
1107 struct idmap_tdb_context
*ctx
;
1109 if (dom
->private_data
) {
1110 ctx
= talloc_get_type(dom
->private_data
, struct idmap_tdb_context
);
1112 if (idmap_tdb_tdb_close(ctx
->tdb
) == 0) {
1113 return NT_STATUS_OK
;
1115 return NT_STATUS_UNSUCCESSFUL
;
1118 return NT_STATUS_OK
;
1123 struct id_map
**maps
;
1128 static int idmap_tdb_dump_one_entry(TDB_CONTEXT
*tdb
, TDB_DATA key
, TDB_DATA value
, void *pdata
)
1130 struct dump_data
*data
= talloc_get_type(pdata
, struct dump_data
);
1131 struct id_map
*maps
;
1132 int num_maps
= *data
->num_maps
;
1134 /* ignore any record but the ones with a SID as key */
1135 if (strncmp(key
.dptr
, "S-", 2) == 0) {
1137 maps
= talloc_realloc(NULL
, *data
->maps
, struct id_map
, num_maps
+1);
1139 DEBUG(0, ("Out of memory!\n"));
1140 data
->ret
= NT_STATUS_NO_MEMORY
;
1144 maps
[num_maps
].sid
= talloc(maps
, DOM_SID
);
1145 if ( ! maps
[num_maps
].sid
) {
1146 DEBUG(0, ("Out of memory!\n"));
1147 data
->ret
= NT_STATUS_NO_MEMORY
;
1151 if (!string_to_sid(maps
[num_maps
].sid
, key
.dptr
)) {
1152 DEBUG(10,("INVALID record %s\n", key
.dptr
));
1153 /* continue even with errors */
1157 /* Try a UID record. */
1158 if (sscanf(value
.dptr
, "UID %u", &(maps
[num_maps
].xid
.id
)) == 1) {
1159 maps
[num_maps
].xid
.type
= ID_TYPE_UID
;
1160 maps
[num_maps
].status
= ID_MAPPED
;
1161 *data
->num_maps
= num_maps
+ 1;
1163 /* Try a GID record. */
1165 if (sscanf(value
.dptr
, "GID %u", &(maps
[num_maps
].xid
.id
)) == 1) {
1166 maps
[num_maps
].xid
.type
= ID_TYPE_GID
;
1167 maps
[num_maps
].status
= ID_MAPPED
;
1168 *data
->num_maps
= num_maps
+ 1;
1170 /* Unknown record type ! */
1172 maps
[num_maps
].status
= ID_UNKNOWN
;
1173 DEBUG(2, ("Found INVALID record %s -> %s\n", key
.dptr
, value
.dptr
));
1174 /* do not increment num_maps */
1181 /**********************************
1182 Dump all mappings out
1183 **********************************/
1185 static NTSTATUS
idmap_tdb_dump_data(struct idmap_domain
*dom
, struct id_map
**maps
, int *num_maps
)
1187 struct idmap_tdb_context
*ctx
;
1188 struct dump_data
*data
;
1189 NTSTATUS ret
= NT_STATUS_OK
;
1191 /* make sure we initialized */
1192 if ( ! dom
->initialized
) {
1193 ret
= idmap_tdb_db_init(dom
);
1194 if ( ! NT_STATUS_IS_OK(ret
)) {
1199 ctx
= talloc_get_type(dom
->private_data
, struct idmap_tdb_context
);
1201 data
= TALLOC_ZERO_P(ctx
, struct dump_data
);
1203 DEBUG(0, ("Out of memory!\n"));
1204 return NT_STATUS_NO_MEMORY
;
1207 data
->num_maps
= num_maps
;
1208 data
->ret
= NT_STATUS_OK
;
1210 tdb_traverse(ctx
->tdb
, idmap_tdb_dump_one_entry
, data
);
1212 if ( ! NT_STATUS_IS_OK(data
->ret
)) {
1220 static struct idmap_methods db_methods
= {
1222 .init
= idmap_tdb_db_init
,
1223 .unixids_to_sids
= idmap_tdb_unixids_to_sids
,
1224 .sids_to_unixids
= idmap_tdb_sids_to_unixids
,
1225 .set_mapping
= idmap_tdb_set_mapping
,
1226 .remove_mapping
= idmap_tdb_remove_mapping
,
1227 .dump_data
= idmap_tdb_dump_data
,
1228 .close_fn
= idmap_tdb_close
1231 static struct idmap_alloc_methods db_alloc_methods
= {
1233 .init
= idmap_tdb_alloc_init
,
1234 .allocate_id
= idmap_tdb_allocate_id
,
1235 .get_id_hwm
= idmap_tdb_get_hwm
,
1236 .set_id_hwm
= idmap_tdb_set_hwm
,
1237 .close_fn
= idmap_tdb_alloc_close
1240 NTSTATUS
idmap_alloc_tdb_init(void)
1242 return smb_register_idmap_alloc(SMB_IDMAP_INTERFACE_VERSION
, "tdb", &db_alloc_methods
);
1245 NTSTATUS
idmap_tdb_init(void)
1249 /* FIXME: bad hack to actually register also the alloc_tdb module without changining configure.in */
1250 ret
= idmap_alloc_tdb_init();
1251 if (! NT_STATUS_IS_OK(ret
)) {
1254 return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION
, "tdb", &db_methods
);