1/*
2 *  Unix SMB/CIFS implementation.
3 *
4 *  group mapping code on top of ldb
5 *
6 *  Copyright (C) Andrew Tridgell              2006
7 *
8 * based on tdb group mapping code from groupdb/mapping.c
9 *
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.
14 *
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.
19 *
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/>.
22 */
23
24#include "includes.h"
25#include "groupdb/mapping.h"
26#include "lib/ldb/include/ldb.h"
27#include "lib/ldb/include/ldb_errors.h"
28
29static struct ldb_context *ldb;
30
31static bool mapping_upgrade(const char *tdb_path);
32
33/*
34  connect to the group mapping ldb
35*/
36static bool init_group_mapping(void)
37{
38	bool existed;
39	const char *init_ldif[] =
40		{ "dn: @ATTRIBUTES\n" \
41		  "ntName: CASE_INSENSITIVE\n" \
42		  "\n",
43		  "dn: @INDEXLIST\n" \
44		  "@IDXATTR: gidNumber\n" \
45		  "@IDXATTR: ntName\n" \
46		  "@IDXATTR: member\n" };
47	const char *db_path, *tdb_path;
48	int ret;
49	int flags = 0;
50
51	if (ldb != NULL) {
52		return True;
53	}
54
55	/* this is needed as Samba3 doesn't have this globally yet */
56	ldb_global_init();
57
58	db_path = state_path("group_mapping.ldb");
59
60	ldb = ldb_init(NULL, NULL);
61	if (ldb == NULL) goto failed;
62
63	/* Ensure this db is created read/write for root only. */
64	ldb_set_create_perms(ldb, 0600);
65
66	existed = file_exist(db_path);
67
68	if (lp_parm_bool(-1, "groupmap", "nosync", False)) {
69		flags |= LDB_FLG_NOSYNC;
70	}
71
72	if (!lp_use_mmap()) {
73		flags |= LDB_FLG_NOMMAP;
74	}
75
76	ret = ldb_connect(ldb, db_path, flags, NULL);
77	if (ret != LDB_SUCCESS) {
78		goto failed;
79	}
80
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) {
84		goto failed;
85	}
86
87	if (!existed) {
88		/* initialise the ldb with an index */
89		struct ldb_ldif *ldif;
90		int i;
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);
95			talloc_free(ldif);
96			if (ret == -1) goto failed;
97		}
98	}
99
100	/* possibly upgrade */
101	tdb_path = state_path("group_mapping.tdb");
102	if (file_exist(tdb_path) && !mapping_upgrade(tdb_path)) {
103		unlink(state_path("group_mapping.ldb"));
104		goto failed;
105	}
106
107	return True;
108
109failed:
110	DEBUG(0,("Failed to open group mapping ldb '%s' - '%s'\n",
111		 db_path, ldb?ldb_errstring(ldb):strerror(errno)));
112	talloc_free(ldb);
113	ldb = NULL;
114	return False;
115}
116
117
118/*
119  form the DN for a mapping entry from a SID
120 */
121static struct ldb_dn *mapping_dn(TALLOC_CTX *mem_ctx, const DOM_SID *sid)
122{
123	fstring string_sid;
124	uint32_t rid;
125	DOM_SID domsid;
126
127	sid_copy(&domsid, sid);
128	if (!sid_split_rid(&domsid, &rid)) {
129		return NULL;
130	}
131      	if (!sid_to_fstring(string_sid, &domsid)) {
132		return NULL;
133	}
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_new_fmt(mem_ctx, ldb, "rid=%u,domain=%s",
137			      rid, string_sid);
138}
139
140/*
141  add a group mapping entry
142 */
143static bool add_mapping_entry(GROUP_MAP *map, int flag)
144{
145	struct ldb_message *msg;
146	int ret, i;
147	fstring string_sid;
148
149	msg = ldb_msg_new(ldb);
150	if (msg == NULL) {
151		return False;
152	}
153
154	msg->dn = mapping_dn(msg, &map->sid);
155	if (msg->dn == NULL) {
156		goto failed;
157	}
158
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) {
166		goto failed;
167	}
168
169	ret = ldb_add(ldb, msg);
170
171	/* if it exists we update it. This is a hangover from the semantics the
172	   tdb backend had */
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;
176		}
177		ret = ldb_modify(ldb, msg);
178	}
179
180	talloc_free(msg);
181
182	return ret == LDB_SUCCESS;
183
184failed:
185	talloc_free(msg);
186	return False;
187}
188
189/*
190  unpack a ldb message into a GROUP_MAP structure
191*/
192static bool msg_to_group_map(struct ldb_message *msg, GROUP_MAP *map)
193{
194	const char *sidstr;
195
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);
201
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"));
206		return False;
207	}
208
209	return True;
210}
211
212/*
213 return a group map entry for a given sid
214*/
215static bool get_group_map_from_sid(DOM_SID sid, GROUP_MAP *map)
216{
217	int ret;
218	struct ldb_dn *dn;
219	struct ldb_result *res=NULL;
220	bool result = false;
221
222	dn = mapping_dn(talloc_tos(), &sid);
223	if (dn == NULL) {
224		goto failed;
225	}
226
227	ret = ldb_search(ldb, dn, &res, dn, LDB_SCOPE_BASE, NULL, NULL);
228	if (ret != LDB_SUCCESS || res->count != 1) {
229		goto failed;
230	}
231
232	if (!msg_to_group_map(res->msgs[0], map)) {
233		goto failed;
234	}
235
236	result = true;
237 failed:
238	talloc_free(dn);
239	return result;
240}
241
242/*
243 return a group map entry for a given gid
244*/
245static bool get_group_map_from_gid(gid_t gid, GROUP_MAP *map)
246{
247	int ret;
248	struct ldb_result *res=NULL;
249	bool result = false;
250
251	ret = ldb_search(ldb, talloc_tos(), &res, NULL, LDB_SCOPE_SUBTREE,
252			 NULL, "(&(gidNumber=%u)(objectClass=groupMap))",
253			 (unsigned)gid);
254	if (ret != LDB_SUCCESS || res->count != 1) {
255		goto failed;
256	}
257
258	if (!msg_to_group_map(res->msgs[0], map)) {
259		goto failed;
260	}
261
262	result = true;
263failed:
264	TALLOC_FREE(res);
265	return result;
266}
267
268/*
269  Return the sid and the type of the unix group.
270*/
271static bool get_group_map_from_ntname(const char *name, GROUP_MAP *map)
272{
273	int ret;
274	struct ldb_result *res=NULL;
275	bool result = false;
276
277	ret = ldb_search(ldb, talloc_tos(), &res, NULL, LDB_SCOPE_SUBTREE,
278			 NULL, "(&(ntName=%s)(objectClass=groupMap))", name);
279	if (ret != LDB_SUCCESS || res->count != 1) {
280		goto failed;
281	}
282
283	if (!msg_to_group_map(res->msgs[0], map)) {
284		goto failed;
285	}
286
287	result = true;
288 failed:
289	TALLOC_FREE(res);
290	return result;
291}
292
293/*
294 Remove a group mapping entry.
295*/
296static bool group_map_remove(const DOM_SID *sid)
297{
298	struct ldb_dn *dn;
299	int ret;
300
301	dn = mapping_dn(ldb, sid);
302	if (dn == NULL) {
303		return False;
304	}
305	ret = ldb_delete(ldb, dn);
306	talloc_free(dn);
307
308	return ret == LDB_SUCCESS;
309}
310
311
312/*
313  Enumerate the group mappings for a domain
314*/
315static bool enum_group_mapping(const DOM_SID *domsid, enum lsa_SidType sid_name_use,
316			       GROUP_MAP **pp_rmap,
317			       size_t *p_num_entries, bool unix_only)
318{
319	int i, ret;
320	fstring name;
321	struct ldb_result *res = NULL;
322	struct ldb_dn *basedn=NULL;
323	TALLOC_CTX *tmp_ctx;
324
325	tmp_ctx = talloc_new(ldb);
326	if (tmp_ctx == NULL) goto failed;
327
328	/* we do a subtree search on the domain */
329	if (domsid != NULL) {
330		sid_to_fstring(name, domsid);
331		basedn = ldb_dn_new_fmt(tmp_ctx, ldb, "domain=%s", name);
332		if (basedn == NULL) goto failed;
333	}
334
335	if (sid_name_use == SID_NAME_UNKNOWN) {
336		ret = ldb_search(ldb, tmp_ctx, &res, basedn, LDB_SCOPE_SUBTREE,
337				 NULL, "(&(objectClass=groupMap))");
338	} else {
339		ret = ldb_search(ldb, tmp_ctx, &res, basedn, LDB_SCOPE_SUBTREE,
340				 NULL, "(&(sidNameUse=%u)(objectClass=groupMap))",
341				 sid_name_use);
342	}
343
344	if (ret != LDB_SUCCESS) goto failed;
345
346	(*pp_rmap) = NULL;
347	*p_num_entries = 0;
348
349	for (i=0;i<res->count;i++) {
350		(*pp_rmap) = SMB_REALLOC_ARRAY((*pp_rmap), GROUP_MAP,
351					       (*p_num_entries)+1);
352		if (!(*pp_rmap)) goto failed;
353
354		if (!msg_to_group_map(res->msgs[i], &(*pp_rmap)[*p_num_entries])) {
355			goto failed;
356		}
357
358		(*p_num_entries)++;
359	}
360
361	talloc_free(tmp_ctx);
362	return True;
363
364failed:
365	talloc_free(tmp_ctx);
366	return False;
367}
368
369/*
370   This operation happens on session setup, so it should better be fast. We
371   store a list of aliases a SID is member of hanging off MEMBEROF/SID.
372*/
373static NTSTATUS one_alias_membership(const DOM_SID *member,
374				     DOM_SID **sids, size_t *num)
375{
376	const char *attrs[] = {
377		"sid",
378		NULL
379	};
380	DOM_SID alias;
381	int ret, i;
382	struct ldb_result *res=NULL;
383	fstring string_sid;
384	NTSTATUS status;
385
386      	if (!sid_to_fstring(string_sid, member)) {
387		return NT_STATUS_INVALID_PARAMETER;
388	}
389
390	ret = ldb_search(ldb, talloc_tos(), &res, NULL, LDB_SCOPE_SUBTREE,
391			 attrs, "(&(member=%s)(objectClass=groupMap))",
392			 string_sid);
393	if (ret != LDB_SUCCESS) {
394		status = NT_STATUS_INTERNAL_DB_CORRUPTION;
395		goto failed;
396	}
397
398	for (i=0;i<res->count;i++) {
399		struct ldb_message_element *el;
400		el = ldb_msg_find_element(res->msgs[i], "sid");
401		if (el == NULL || el->num_values != 1) {
402			status = NT_STATUS_INTERNAL_DB_CORRUPTION;
403			goto failed;
404		}
405		string_to_sid(&alias, (char *)el->values[0].data);
406		status = add_sid_to_array_unique(NULL, &alias, sids, num);
407		if (!NT_STATUS_IS_OK(status)) {
408			goto failed;
409		}
410	}
411
412	status = NT_STATUS_OK;
413 failed:
414	TALLOC_FREE(res);
415	return status;
416}
417
418/*
419  add/remove a member field
420*/
421static NTSTATUS modify_aliasmem(const DOM_SID *alias, const DOM_SID *member,
422				int operation)
423{
424	fstring string_sid;
425	int ret;
426	struct ldb_message msg;
427	struct ldb_message_element el;
428	struct ldb_val val;
429	TALLOC_CTX *tmp_ctx;
430	GROUP_MAP map;
431
432	if (!get_group_map_from_sid(*alias, &map)) {
433		sid_to_fstring(string_sid, alias);
434		return NT_STATUS_NO_SUCH_ALIAS;
435	}
436
437	if ((map.sid_name_use != SID_NAME_ALIAS) &&
438	    (map.sid_name_use != SID_NAME_WKN_GRP)) {
439		DEBUG(0,("sid_name_use=%d\n", map.sid_name_use));
440		return NT_STATUS_NO_SUCH_ALIAS;
441	}
442
443	tmp_ctx = talloc_new(NULL);
444	if (tmp_ctx == NULL) {
445		return NT_STATUS_NO_MEMORY;
446	}
447
448	msg.dn = mapping_dn(tmp_ctx, alias);
449	if (msg.dn == NULL) {
450		return NT_STATUS_NO_MEMORY;
451	}
452	msg.num_elements = 1;
453	msg.elements = &el;
454	el.flags = operation;
455	el.name = talloc_strdup(tmp_ctx, "member");
456	el.num_values = 1;
457	el.values = &val;
458	sid_to_fstring(string_sid, member);
459	val.data = (uint8_t *)string_sid;
460	val.length = strlen(string_sid);
461
462	ret = ldb_modify(ldb, &msg);
463	talloc_free(tmp_ctx);
464
465	if (ret == LDB_ERR_NO_SUCH_OBJECT) {
466		return NT_STATUS_NO_SUCH_ALIAS;
467	}
468
469	if (operation == LDB_FLAG_MOD_ADD &&
470	    ret == LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS) {
471		return NT_STATUS_MEMBER_IN_ALIAS;
472	}
473
474	return (ret == LDB_SUCCESS ? NT_STATUS_OK : NT_STATUS_ACCESS_DENIED);
475}
476
477static NTSTATUS add_aliasmem(const DOM_SID *alias, const DOM_SID *member)
478{
479	return modify_aliasmem(alias, member, LDB_FLAG_MOD_ADD);
480}
481
482static NTSTATUS del_aliasmem(const DOM_SID *alias, const DOM_SID *member)
483{
484	return modify_aliasmem(alias, member, LDB_FLAG_MOD_DELETE);
485}
486
487
488/*
489  enumerate sids that have the given alias set in member
490*/
491static NTSTATUS enum_aliasmem(const DOM_SID *alias, TALLOC_CTX *mem_ctx,
492			      DOM_SID **sids, size_t *num)
493{
494	const char *attrs[] = {
495		"member",
496		NULL
497	};
498	int ret, i;
499	NTSTATUS status = NT_STATUS_OK;
500	struct ldb_result *res=NULL;
501	struct ldb_dn *dn;
502	struct ldb_message_element *el;
503
504	*sids = NULL;
505	*num = 0;
506
507	dn = mapping_dn(ldb, alias);
508	if (dn == NULL) {
509		return NT_STATUS_NO_MEMORY;
510	}
511
512	ret = ldb_search(ldb, ldb, &res, dn, LDB_SCOPE_BASE, attrs, NULL);
513	if (ret == LDB_SUCCESS && res->count == 0) {
514		talloc_free(res);
515		talloc_free(dn);
516		return NT_STATUS_OK;
517	}
518	if (ret != LDB_SUCCESS) {
519		talloc_free(dn);
520		return NT_STATUS_INTERNAL_DB_CORRUPTION;
521	}
522
523	talloc_steal(dn, res);
524	el = ldb_msg_find_element(res->msgs[0], "member");
525	if (el == NULL) {
526		talloc_free(dn);
527		return NT_STATUS_OK;
528	}
529
530	for (i=0;i<el->num_values;i++) {
531		DOM_SID sid;
532		string_to_sid(&sid, (const char *)el->values[i].data);
533		status = add_sid_to_array_unique(mem_ctx, &sid, sids, num);
534		if (!NT_STATUS_IS_OK(status)) {
535			goto done;
536		}
537	}
538
539done:
540	talloc_free(dn);
541	return status;
542}
543
544/*
545  upgrade one group mapping record from the old tdb format
546*/
547static int upgrade_map_record(TDB_CONTEXT *tdb_ctx, TDB_DATA key,
548			      TDB_DATA data, void *state)
549{
550	int ret;
551	GROUP_MAP map;
552
553	if (strncmp((char *)key.dptr, GROUP_PREFIX,
554		    MIN(key.dsize, strlen(GROUP_PREFIX))) != 0) {
555		return 0;
556	}
557
558	if (!string_to_sid(&map.sid, strlen(GROUP_PREFIX) + (const char *)key.dptr)) {
559		DEBUG(0,("Bad sid key '%s' during upgrade\n", (const char *)key.dptr));
560		*(int *)state = -1;
561		return -1;
562	}
563
564	ret = tdb_unpack(data.dptr, data.dsize, "ddff",
565			 &map.gid, &map.sid_name_use, &map.nt_name, &map.comment);
566	if (ret == -1) {
567		DEBUG(0,("Failed to unpack group map record during upgrade\n"));
568		*(int *)state = -1;
569		return -1;
570	}
571
572	if ((int)map.gid == -1) {
573		/*
574		 * Ignore old invalid mappings
575		 */
576		return 0;
577	}
578
579	if (!add_mapping_entry(&map, 0)) {
580		DEBUG(0,("Failed to add mapping entry during upgrade\n"));
581		*(int *)state = -1;
582		return -1;
583	}
584
585	return 0;
586}
587
588/*
589  upgrade one alias record from the old tdb format
590*/
591static int upgrade_alias_record(TDB_CONTEXT *tdb_ctx, TDB_DATA key,
592				TDB_DATA data, void *state)
593{
594	const char *p = (const char *)data.dptr;
595	char *string_sid;
596	DOM_SID member;
597	TALLOC_CTX *frame;
598
599	if (strncmp((char *)key.dptr, MEMBEROF_PREFIX,
600		    MIN(key.dsize, strlen(MEMBEROF_PREFIX))) != 0) {
601		return 0;
602	}
603
604	if (!string_to_sid(&member, strlen(MEMBEROF_PREFIX) + (const char *)key.dptr)) {
605		DEBUG(0,("Bad alias key %s during upgrade\n",
606			 (const char *)key.dptr));
607		*(int *)state = -1;
608	}
609
610	frame = talloc_stackframe();
611	while (next_token_talloc(frame,&p, &string_sid, " ")) {
612		DOM_SID alias;
613		NTSTATUS status;
614		string_to_sid(&alias, string_sid);
615		status = add_aliasmem(&alias, &member);
616		if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_ALIAS)) {
617			DEBUG(0,("Ignoring orphaned alias record '%s'\n",
618				 string_sid));
619		} else if (!NT_STATUS_IS_OK(status)) {
620			DEBUG(0,("Failed to add alias member during upgrade - %s\n",
621				 nt_errstr(status)));
622			*(int *)state = -1;
623			TALLOC_FREE(frame);
624			return -1;
625		}
626	}
627	TALLOC_FREE(frame);
628	return 0;
629}
630
631/*
632  upgrade from a old style tdb
633*/
634static bool mapping_upgrade(const char *tdb_path)
635{
636	static TDB_CONTEXT *tdb;
637	int ret, status=0;
638
639	tdb = tdb_open_log(tdb_path, 0, TDB_DEFAULT, O_RDWR, 0600);
640	if (tdb == NULL) goto failed;
641
642	/* we have to do the map records first, as alias records may
643	   reference them */
644	ret = tdb_traverse(tdb, upgrade_map_record, &status);
645	if (ret == -1 || status == -1) goto failed;
646
647	ret = tdb_traverse(tdb, upgrade_alias_record, &status);
648	if (ret == -1 || status == -1) goto failed;
649
650	if (tdb) {
651		tdb_close(tdb);
652		tdb = NULL;
653	}
654
655	{
656		const char *old_path = tdb_path;
657		char *new_path = state_path("group_mapping.tdb.upgraded");
658
659		if (!new_path) {
660			goto failed;
661		}
662		if (rename(old_path, new_path) != 0) {
663			DEBUG(0,("Failed to rename old group mapping database\n"));
664			goto failed;
665		}
666	}
667	return True;
668
669failed:
670	DEBUG(0,("Failed to upgrade group mapping database\n"));
671	if (tdb) tdb_close(tdb);
672	return False;
673}
674
675
676
677static const struct mapping_backend ldb_backend = {
678	.add_mapping_entry         = add_mapping_entry,
679	.get_group_map_from_sid    = get_group_map_from_sid,
680	.get_group_map_from_gid    = get_group_map_from_gid,
681	.get_group_map_from_ntname = get_group_map_from_ntname,
682	.group_map_remove          = group_map_remove,
683	.enum_group_mapping        = enum_group_mapping,
684	.one_alias_membership      = one_alias_membership,
685	.add_aliasmem              = add_aliasmem,
686	.del_aliasmem              = del_aliasmem,
687	.enum_aliasmem             = enum_aliasmem
688};
689
690/*
691  initialise the ldb mapping backend
692 */
693const struct mapping_backend *groupdb_ldb_init(void)
694{
695	if (!init_group_mapping()) {
696		DEBUG(0,("Failed to initialise ldb mapping backend\n"));
697		return NULL;
698	}
699
700	return &ldb_backend;
701}
702