2 * Unix SMB/CIFS implementation.
4 * group mapping code on top of ldb
6 * Copyright (C) Andrew Tridgell 2006
8 * based on tdb group mapping code from groupdb/mapping.c
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 3 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, see <http://www.gnu.org/licenses/>.
25 #include "groupdb/mapping.h"
26 #include "lib/ldb/include/includes.h"
27 #include "lib/ldb/include/ldb_errors.h"
29 static struct ldb_context
*ldb
;
31 static bool mapping_upgrade(const char *tdb_path
);
34 connect to the group mapping ldb
36 static bool init_group_mapping(void)
39 const char *init_ldif
[] =
40 { "dn: @ATTRIBUTES\n" \
41 "ntName: CASE_INSENSITIVE\n" \
44 "@IDXATTR: gidNumber\n" \
45 "@IDXATTR: ntName\n" \
46 "@IDXATTR: member\n" };
47 const char *db_path
, *tdb_path
;
55 /* this is needed as Samba3 doesn't have this globally yet */
58 db_path
= state_path("group_mapping.ldb");
61 if (ldb
== NULL
) goto failed
;
63 /* Ensure this db is created read/write for root only. */
64 ldb_set_create_perms(ldb
, 0600);
66 existed
= file_exist(db_path
, NULL
);
68 if (lp_parm_bool(-1, "groupmap", "nosync", False
)) {
69 flags
|= LDB_FLG_NOSYNC
;
73 flags
|= LDB_FLG_NOMMAP
;
76 ret
= ldb_connect(ldb
, db_path
, flags
, NULL
);
77 if (ret
!= LDB_SUCCESS
) {
81 /* force the permissions on the ldb to 0600 - this will fix
82 existing databases as well as new ones */
83 if (chmod(db_path
, 0600) != 0) {
88 /* initialise the ldb with an index */
89 struct ldb_ldif
*ldif
;
91 for (i
=0;i
<ARRAY_SIZE(init_ldif
);i
++) {
92 ldif
= ldb_ldif_read_string(ldb
, &init_ldif
[i
]);
93 if (ldif
== NULL
) goto failed
;
94 ret
= ldb_add(ldb
, ldif
->msg
);
96 if (ret
== -1) goto failed
;
100 /* possibly upgrade */
101 tdb_path
= state_path("group_mapping.tdb");
102 if (file_exist(tdb_path
, NULL
) && !mapping_upgrade(tdb_path
)) {
103 unlink(state_path("group_mapping.ldb"));
110 DEBUG(0,("Failed to open group mapping ldb '%s' - '%s'\n",
111 db_path
, ldb
?ldb_errstring(ldb
):strerror(errno
)));
119 form the DN for a mapping entry from a SID
121 static struct ldb_dn
*mapping_dn(TALLOC_CTX
*mem_ctx
, const DOM_SID
*sid
)
127 sid_copy(&domsid
, sid
);
128 if (!sid_split_rid(&domsid
, &rid
)) {
131 if (!sid_to_fstring(string_sid
, &domsid
)) {
134 /* we split by domain and rid so we can do a subtree search
135 when we only want one domain */
136 return ldb_dn_string_compose(mem_ctx
, NULL
, "rid=%u,domain=%s",
141 add a group mapping entry
143 static bool add_mapping_entry(GROUP_MAP
*map
, int flag
)
145 struct ldb_message
*msg
;
149 msg
= ldb_msg_new(ldb
);
154 msg
->dn
= mapping_dn(msg
, &map
->sid
);
155 if (msg
->dn
== NULL
) {
159 if (ldb_msg_add_string(msg
, "objectClass", "groupMap") != LDB_SUCCESS
||
160 ldb_msg_add_string(msg
, "sid",
161 sid_to_fstring(string_sid
, &map
->sid
)) != LDB_SUCCESS
||
162 ldb_msg_add_fmt(msg
, "gidNumber", "%u", (unsigned)map
->gid
) != LDB_SUCCESS
||
163 ldb_msg_add_fmt(msg
, "sidNameUse", "%u", (unsigned)map
->sid_name_use
) != LDB_SUCCESS
||
164 ldb_msg_add_string(msg
, "comment", map
->comment
) != LDB_SUCCESS
||
165 ldb_msg_add_string(msg
, "ntName", map
->nt_name
) != LDB_SUCCESS
) {
169 ret
= ldb_add(ldb
, msg
);
171 /* if it exists we update it. This is a hangover from the semantics the
173 if (ret
== LDB_ERR_ENTRY_ALREADY_EXISTS
) {
174 for (i
=0;i
<msg
->num_elements
;i
++) {
175 msg
->elements
[i
].flags
= LDB_FLAG_MOD_REPLACE
;
177 ret
= ldb_modify(ldb
, msg
);
182 return ret
== LDB_SUCCESS
;
190 unpack a ldb message into a GROUP_MAP structure
192 static bool msg_to_group_map(struct ldb_message
*msg
, GROUP_MAP
*map
)
196 map
->gid
= ldb_msg_find_attr_as_int(msg
, "gidNumber", -1);
197 map
->sid_name_use
= ldb_msg_find_attr_as_int(msg
, "sidNameUse", -1);
198 fstrcpy(map
->nt_name
, ldb_msg_find_attr_as_string(msg
, "ntName", NULL
));
199 fstrcpy(map
->comment
, ldb_msg_find_attr_as_string(msg
, "comment", NULL
));
200 sidstr
= ldb_msg_find_attr_as_string(msg
, "sid", NULL
);
202 if (!string_to_sid(&map
->sid
, sidstr
) ||
203 map
->gid
== (gid_t
)-1 ||
204 map
->sid_name_use
== (enum lsa_SidType
)-1) {
205 DEBUG(0,("Unable to unpack group mapping\n"));
213 return a group map entry for a given sid
215 static bool get_group_map_from_sid(DOM_SID sid
, GROUP_MAP
*map
)
219 struct ldb_result
*res
=NULL
;
221 dn
= mapping_dn(ldb
, &sid
);
222 if (dn
== NULL
) goto failed
;
224 ret
= ldb_search(ldb
, dn
, LDB_SCOPE_BASE
, NULL
, NULL
, &res
);
225 talloc_steal(dn
, res
);
226 if (ret
!= LDB_SUCCESS
|| res
->count
!= 1) {
230 if (!msg_to_group_map(res
->msgs
[0], map
)) goto failed
;
241 return a group map entry for a given gid
243 static bool get_group_map_from_gid(gid_t gid
, GROUP_MAP
*map
)
247 struct ldb_result
*res
=NULL
;
249 expr
= talloc_asprintf(ldb
, "(&(gidNumber=%u)(objectClass=groupMap))",
251 if (expr
== NULL
) goto failed
;
253 ret
= ldb_search(ldb
, NULL
, LDB_SCOPE_SUBTREE
, expr
, NULL
, &res
);
254 talloc_steal(expr
, res
);
255 if (ret
!= LDB_SUCCESS
|| res
->count
!= 1) goto failed
;
257 if (!msg_to_group_map(res
->msgs
[0], map
)) goto failed
;
268 Return the sid and the type of the unix group.
270 static bool get_group_map_from_ntname(const char *name
, GROUP_MAP
*map
)
274 struct ldb_result
*res
=NULL
;
276 expr
= talloc_asprintf(ldb
, "(&(ntName=%s)(objectClass=groupMap))", name
);
277 if (expr
== NULL
) goto failed
;
279 ret
= ldb_search(ldb
, NULL
, LDB_SCOPE_SUBTREE
, expr
, NULL
, &res
);
280 talloc_steal(expr
, res
);
281 if (ret
!= LDB_SUCCESS
|| res
->count
!= 1) goto failed
;
283 if (!msg_to_group_map(res
->msgs
[0], map
)) goto failed
;
294 Remove a group mapping entry.
296 static bool group_map_remove(const DOM_SID
*sid
)
301 dn
= mapping_dn(ldb
, sid
);
305 ret
= ldb_delete(ldb
, dn
);
308 return ret
== LDB_SUCCESS
;
313 Enumerate the group mappings for a domain
315 static bool enum_group_mapping(const DOM_SID
*domsid
, enum lsa_SidType sid_name_use
,
317 size_t *p_num_entries
, bool unix_only
)
322 struct ldb_result
*res
= NULL
;
323 struct ldb_dn
*basedn
=NULL
;
326 tmp_ctx
= talloc_new(ldb
);
327 if (tmp_ctx
== NULL
) goto failed
;
329 if (sid_name_use
== SID_NAME_UNKNOWN
) {
330 expr
= talloc_asprintf(tmp_ctx
, "(&(objectClass=groupMap))");
332 expr
= talloc_asprintf(tmp_ctx
, "(&(sidNameUse=%u)(objectClass=groupMap))",
335 if (expr
== NULL
) goto failed
;
337 /* we do a subtree search on the domain */
338 if (domsid
!= NULL
) {
339 sid_to_fstring(name
, domsid
);
340 basedn
= ldb_dn_string_compose(tmp_ctx
, NULL
, "domain=%s", name
);
341 if (basedn
== NULL
) goto failed
;
344 ret
= ldb_search(ldb
, basedn
, LDB_SCOPE_SUBTREE
, expr
, NULL
, &res
);
345 talloc_steal(tmp_ctx
, res
);
346 if (ret
!= LDB_SUCCESS
) goto failed
;
351 for (i
=0;i
<res
->count
;i
++) {
352 (*pp_rmap
) = SMB_REALLOC_ARRAY((*pp_rmap
), GROUP_MAP
,
354 if (!(*pp_rmap
)) goto failed
;
356 if (!msg_to_group_map(res
->msgs
[i
], &(*pp_rmap
)[*p_num_entries
])) {
363 talloc_free(tmp_ctx
);
367 talloc_free(tmp_ctx
);
372 This operation happens on session setup, so it should better be fast. We
373 store a list of aliases a SID is member of hanging off MEMBEROF/SID.
375 static NTSTATUS
one_alias_membership(const DOM_SID
*member
,
376 DOM_SID
**sids
, size_t *num
)
378 const char *attrs
[] = {
385 struct ldb_result
*res
=NULL
;
387 NTSTATUS status
= NT_STATUS_INTERNAL_DB_CORRUPTION
;
389 if (!sid_to_fstring(string_sid
, member
)) {
390 return NT_STATUS_INVALID_PARAMETER
;
393 expr
= talloc_asprintf(ldb
, "(&(member=%s)(objectClass=groupMap))",
395 if (expr
== NULL
) goto failed
;
397 ret
= ldb_search(ldb
, NULL
, LDB_SCOPE_SUBTREE
, expr
, attrs
, &res
);
398 talloc_steal(expr
, res
);
399 if (ret
!= LDB_SUCCESS
) {
403 for (i
=0;i
<res
->count
;i
++) {
404 struct ldb_message_element
*el
;
405 el
= ldb_msg_find_element(res
->msgs
[i
], "sid");
406 if (el
== NULL
|| el
->num_values
!= 1) {
407 status
= NT_STATUS_INTERNAL_DB_CORRUPTION
;
410 string_to_sid(&alias
, (char *)el
->values
[0].data
);
411 status
= add_sid_to_array_unique(NULL
, &alias
, sids
, num
);
412 if (!NT_STATUS_IS_OK(status
)) {
426 add/remove a member field
428 static NTSTATUS
modify_aliasmem(const DOM_SID
*alias
, const DOM_SID
*member
,
433 struct ldb_message msg
;
434 struct ldb_message_element el
;
439 if (!get_group_map_from_sid(*alias
, &map
)) {
440 sid_to_fstring(string_sid
, alias
);
441 return NT_STATUS_NO_SUCH_ALIAS
;
444 if ((map
.sid_name_use
!= SID_NAME_ALIAS
) &&
445 (map
.sid_name_use
!= SID_NAME_WKN_GRP
)) {
446 DEBUG(0,("sid_name_use=%d\n", map
.sid_name_use
));
447 return NT_STATUS_NO_SUCH_ALIAS
;
450 tmp_ctx
= talloc_new(NULL
);
451 if (tmp_ctx
== NULL
) {
452 return NT_STATUS_NO_MEMORY
;
455 msg
.dn
= mapping_dn(tmp_ctx
, alias
);
456 if (msg
.dn
== NULL
) {
457 return NT_STATUS_NO_MEMORY
;
459 msg
.num_elements
= 1;
461 el
.flags
= operation
;
462 el
.name
= talloc_strdup(tmp_ctx
, "member");
465 sid_to_fstring(string_sid
, member
);
466 val
.data
= (uint8_t *)string_sid
;
467 val
.length
= strlen(string_sid
);
469 ret
= ldb_modify(ldb
, &msg
);
470 talloc_free(tmp_ctx
);
472 if (ret
== LDB_ERR_NO_SUCH_OBJECT
) {
473 return NT_STATUS_NO_SUCH_ALIAS
;
476 if (operation
== LDB_FLAG_MOD_ADD
&&
477 ret
== LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS
) {
478 return NT_STATUS_MEMBER_IN_ALIAS
;
481 return (ret
== LDB_SUCCESS
? NT_STATUS_OK
: NT_STATUS_ACCESS_DENIED
);
484 static NTSTATUS
add_aliasmem(const DOM_SID
*alias
, const DOM_SID
*member
)
486 return modify_aliasmem(alias
, member
, LDB_FLAG_MOD_ADD
);
489 static NTSTATUS
del_aliasmem(const DOM_SID
*alias
, const DOM_SID
*member
)
491 return modify_aliasmem(alias
, member
, LDB_FLAG_MOD_DELETE
);
496 enumerate sids that have the given alias set in member
498 static NTSTATUS
enum_aliasmem(const DOM_SID
*alias
, DOM_SID
**sids
, size_t *num
)
500 const char *attrs
[] = {
505 NTSTATUS status
= NT_STATUS_OK
;
506 struct ldb_result
*res
=NULL
;
508 struct ldb_message_element
*el
;
513 dn
= mapping_dn(ldb
, alias
);
515 return NT_STATUS_NO_MEMORY
;
518 ret
= ldb_search(ldb
, dn
, LDB_SCOPE_BASE
, NULL
, attrs
, &res
);
519 talloc_steal(dn
, res
);
520 if (ret
== LDB_SUCCESS
&& res
->count
== 0) {
524 if (ret
!= LDB_SUCCESS
) {
526 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
529 el
= ldb_msg_find_element(res
->msgs
[0], "member");
532 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
535 for (i
=0;i
<el
->num_values
;i
++) {
537 string_to_sid(&sid
, (const char *)el
->values
[i
].data
);
538 status
= add_sid_to_array_unique(NULL
, &sid
, sids
, num
);
539 if (!NT_STATUS_IS_OK(status
)) {
550 upgrade one group mapping record from the old tdb format
552 static int upgrade_map_record(TDB_CONTEXT
*tdb_ctx
, TDB_DATA key
,
553 TDB_DATA data
, void *state
)
558 if (strncmp((char *)key
.dptr
, GROUP_PREFIX
,
559 MIN(key
.dsize
, strlen(GROUP_PREFIX
))) != 0) {
563 if (!string_to_sid(&map
.sid
, strlen(GROUP_PREFIX
) + (const char *)key
.dptr
)) {
564 DEBUG(0,("Bad sid key '%s' during upgrade\n", (const char *)key
.dptr
));
569 ret
= tdb_unpack(data
.dptr
, data
.dsize
, "ddff",
570 &map
.gid
, &map
.sid_name_use
, &map
.nt_name
, &map
.comment
);
572 DEBUG(0,("Failed to unpack group map record during upgrade\n"));
577 if (!add_mapping_entry(&map
, 0)) {
578 DEBUG(0,("Failed to add mapping entry during upgrade\n"));
587 upgrade one alias record from the old tdb format
589 static int upgrade_alias_record(TDB_CONTEXT
*tdb_ctx
, TDB_DATA key
,
590 TDB_DATA data
, void *state
)
592 const char *p
= (const char *)data
.dptr
;
597 if (strncmp((char *)key
.dptr
, MEMBEROF_PREFIX
,
598 MIN(key
.dsize
, strlen(MEMBEROF_PREFIX
))) != 0) {
602 if (!string_to_sid(&member
, strlen(MEMBEROF_PREFIX
) + (const char *)key
.dptr
)) {
603 DEBUG(0,("Bad alias key %s during upgrade\n",
604 (const char *)key
.dptr
));
608 frame
= talloc_stackframe();
609 while (next_token_talloc(frame
,&p
, &string_sid
, " ")) {
612 string_to_sid(&alias
, string_sid
);
613 status
= add_aliasmem(&alias
, &member
);
614 if (NT_STATUS_EQUAL(status
, NT_STATUS_NO_SUCH_ALIAS
)) {
615 DEBUG(0,("Ignoring orphaned alias record '%s'\n",
617 } else if (!NT_STATUS_IS_OK(status
)) {
618 DEBUG(0,("Failed to add alias member during upgrade - %s\n",
630 upgrade from a old style tdb
632 static bool mapping_upgrade(const char *tdb_path
)
634 static TDB_CONTEXT
*tdb
;
637 tdb
= tdb_open_log(tdb_path
, 0, TDB_DEFAULT
, O_RDWR
, 0600);
638 if (tdb
== NULL
) goto failed
;
640 /* we have to do the map records first, as alias records may
642 ret
= tdb_traverse(tdb
, upgrade_map_record
, &status
);
643 if (ret
== -1 || status
== -1) goto failed
;
645 ret
= tdb_traverse(tdb
, upgrade_alias_record
, &status
);
646 if (ret
== -1 || status
== -1) goto failed
;
654 const char *old_path
= tdb_path
;
655 char *new_path
= state_path("group_mapping.tdb.upgraded");
660 if (rename(old_path
, new_path
) != 0) {
661 DEBUG(0,("Failed to rename old group mapping database\n"));
668 DEBUG(0,("Failed to upgrade group mapping database\n"));
669 if (tdb
) tdb_close(tdb
);
675 static const struct mapping_backend ldb_backend
= {
676 .add_mapping_entry
= add_mapping_entry
,
677 .get_group_map_from_sid
= get_group_map_from_sid
,
678 .get_group_map_from_gid
= get_group_map_from_gid
,
679 .get_group_map_from_ntname
= get_group_map_from_ntname
,
680 .group_map_remove
= group_map_remove
,
681 .enum_group_mapping
= enum_group_mapping
,
682 .one_alias_membership
= one_alias_membership
,
683 .add_aliasmem
= add_aliasmem
,
684 .del_aliasmem
= del_aliasmem
,
685 .enum_aliasmem
= enum_aliasmem
689 initialise the ldb mapping backend
691 const struct mapping_backend
*groupdb_ldb_init(void)
693 if (!init_group_mapping()) {
694 DEBUG(0,("Failed to initialise ldb mapping backend\n"));