4 Copyright (C) Simo Sorce 2004
6 * NOTICE: this module is NOT released under the GNU LGPL license as
7 * other ldb code. This module is release under the GNU GPL v2 or
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 2 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, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 * Component: ldb samldb module
30 * Description: add embedded user/group creation functionality
36 #include "lib/ldb/include/ldb.h"
37 #include "lib/ldb/include/ldb_errors.h"
38 #include "lib/ldb/include/ldb_private.h"
39 #include "dsdb/samdb/samdb.h"
41 #define SAM_ACCOUNT_NAME_BASE "$000000-000000000000"
44 allocate a new id, attempting to do it atomically
45 return 0 on failure, the id on success
47 static int samldb_allocate_next_rid(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
48 const struct ldb_dn *dn, uint32_t *id)
50 const char * const attrs[2] = { "nextRid", NULL };
51 struct ldb_result *res = NULL;
52 struct ldb_message msg;
55 struct ldb_val vals[2];
56 struct ldb_message_element els[2];
58 ret = ldb_search(ldb, dn, LDB_SCOPE_BASE, "nextRid=*", attrs, &res);
59 if (ret != LDB_SUCCESS || res->count != 1) {
60 if (res) talloc_free(res);
63 str = ldb_msg_find_string(res->msgs[0], "nextRid", NULL);
65 ldb_debug(ldb, LDB_DEBUG_FATAL, "attribute nextRid not found in %s\n", ldb_dn_linearize(res, dn));
70 *id = strtol(str, NULL, 0);
73 ldb_debug(ldb, LDB_DEBUG_FATAL, "Are we out of valid IDs ?\n");
79 /* we do a delete and add as a single operation. That prevents
82 msg.dn = ldb_dn_copy(mem_ctx, dn);
89 els[0].num_values = 1;
90 els[0].values = &vals[0];
91 els[0].flags = LDB_FLAG_MOD_DELETE;
92 els[0].name = talloc_strdup(mem_ctx, "nextRid");
97 els[1].num_values = 1;
98 els[1].values = &vals[1];
99 els[1].flags = LDB_FLAG_MOD_ADD;
100 els[1].name = els[0].name;
102 vals[0].data = (uint8_t *)talloc_asprintf(mem_ctx, "%u", *id);
106 vals[0].length = strlen((char *)vals[0].data);
108 vals[1].data = (uint8_t *)talloc_asprintf(mem_ctx, "%u", (*id)+1);
112 vals[1].length = strlen((char *)vals[1].data);
114 ret = ldb_modify(ldb, &msg);
124 static struct ldb_dn *samldb_search_domain(struct ldb_module *module, TALLOC_CTX *mem_ctx, const struct ldb_dn *dn)
126 TALLOC_CTX *local_ctx;
128 struct ldb_result *res = NULL;
131 local_ctx = talloc_new(mem_ctx);
132 if (local_ctx == NULL) return NULL;
134 sdn = ldb_dn_copy(local_ctx, dn);
136 ret = ldb_search(module->ldb, sdn, LDB_SCOPE_BASE, "objectClass=domain", NULL, &res);
137 talloc_steal(local_ctx, res);
138 if (ret == LDB_SUCCESS && res->count == 1)
140 } while ((sdn = ldb_dn_get_parent(local_ctx, sdn)));
142 if (ret != LDB_SUCCESS || res->count != 1) {
143 talloc_free(local_ctx);
147 talloc_steal(mem_ctx, sdn);
148 talloc_free(local_ctx);
153 /* search the domain related to the provided dn
154 allocate a new RID for the domain
155 return the new sid string
157 static struct dom_sid *samldb_get_new_sid(struct ldb_module *module,
158 TALLOC_CTX *mem_ctx, const struct ldb_dn *obj_dn)
160 const char * const attrs[2] = { "objectSid", NULL };
161 struct ldb_result *res = NULL;
162 const struct ldb_dn *dom_dn;
165 struct dom_sid *dom_sid, *obj_sid;
167 /* get the domain component part of the provided dn */
169 /* FIXME: quick search here, I think we should use something like
170 ldap_parse_dn here to be 100% sure we get the right domain dn */
172 /* FIXME: "dc=" is probably not utf8 safe either,
173 we need a multibyte safe substring search function here */
175 dom_dn = samldb_search_domain(module, mem_ctx, obj_dn);
176 if (dom_dn == NULL) {
177 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "Invalid dn (%s) not child of a domain object!\n", ldb_dn_linearize(mem_ctx, obj_dn));
181 /* find the domain sid */
183 ret = ldb_search(module->ldb, dom_dn, LDB_SCOPE_BASE, "objectSid=*", attrs, &res);
184 if (ret != LDB_SUCCESS || res->count != 1) {
185 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_get_new_sid: error retrieving domain sid!\n");
190 dom_sid = samdb_result_dom_sid(res, res->msgs[0], "objectSid");
191 if (dom_sid == NULL) {
192 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_get_new_sid: error retrieving domain sid!\n");
197 /* allocate a new Rid for the domain */
198 ret = samldb_allocate_next_rid(module->ldb, mem_ctx, dom_dn, &rid);
200 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "Failed to increment nextRid of %s\n", ldb_dn_linearize(mem_ctx, dom_dn));
205 /* return the new object sid */
206 obj_sid = dom_sid_add_rid(mem_ctx, dom_sid, rid);
213 static char *samldb_generate_samAccountName(const void *mem_ctx) {
216 name = talloc_strdup(mem_ctx, SAM_ACCOUNT_NAME_BASE);
217 /* TODO: randomize name */
222 /* if value is not null also check for attribute to have exactly that value */
223 static struct ldb_message_element *samldb_find_attribute(const struct ldb_message *msg, const char *name, const char *value)
227 for (i = 0; i < msg->num_elements; i++) {
228 if (ldb_attr_cmp(name, msg->elements[i].name) == 0) {
230 return &msg->elements[i];
232 for (j = 0; j < msg->elements[i].num_values; j++) {
233 if (strcasecmp(value,
234 (char *)msg->elements[i].values[j].data) == 0) {
235 return &msg->elements[i];
244 static BOOL samldb_msg_add_string(struct ldb_module *module, struct ldb_message *msg, const char *name, const char *value)
246 char *aname = talloc_strdup(msg, name);
247 char *aval = talloc_strdup(msg, value);
249 if (aname == NULL || aval == NULL) {
250 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_msg_add_string: talloc_strdup failed!\n");
254 if (ldb_msg_add_string(msg, aname, aval) != 0) {
261 static BOOL samldb_msg_add_sid(struct ldb_module *module, struct ldb_message *msg, const char *name, const struct dom_sid *sid)
265 status = ndr_push_struct_blob(&v, msg, sid,
266 (ndr_push_flags_fn_t)ndr_push_dom_sid);
267 if (!NT_STATUS_IS_OK(status)) {
270 return (ldb_msg_add_value(msg, name, &v) == 0);
273 static BOOL samldb_find_or_add_attribute(struct ldb_module *module, struct ldb_message *msg, const char *name, const char *value, const char *set_value)
275 if (samldb_find_attribute(msg, name, value) == NULL) {
276 return samldb_msg_add_string(module, msg, name, set_value);
281 static int samldb_copy_template(struct ldb_module *module, struct ldb_message *msg, const char *filter)
283 struct ldb_result *res;
284 struct ldb_message *t;
288 /* pull the template record */
289 ret = ldb_search(module->ldb, NULL, LDB_SCOPE_SUBTREE, filter, NULL, &res);
290 if (ret != LDB_SUCCESS || res->count != 1) {
291 ldb_debug(module->ldb, LDB_DEBUG_WARNING, "samldb: ERROR: template '%s' matched too many records\n", filter);
296 for (i = 0; i < t->num_elements; i++) {
297 struct ldb_message_element *el = &t->elements[i];
298 /* some elements should not be copied from the template */
299 if (strcasecmp(el->name, "cn") == 0 ||
300 strcasecmp(el->name, "name") == 0 ||
301 strcasecmp(el->name, "sAMAccountName") == 0 ||
302 strcasecmp(el->name, "objectGUID") == 0) {
305 for (j = 0; j < el->num_values; j++) {
306 if (strcasecmp(el->name, "objectClass") == 0) {
307 if (strcasecmp((char *)el->values[j].data, "Template") == 0 ||
308 strcasecmp((char *)el->values[j].data, "userTemplate") == 0 ||
309 strcasecmp((char *)el->values[j].data, "groupTemplate") == 0 ||
310 strcasecmp((char *)el->values[j].data, "foreignSecurityPrincipalTemplate") == 0 ||
311 strcasecmp((char *)el->values[j].data, "aliasTemplate") == 0 ||
312 strcasecmp((char *)el->values[j].data, "trustedDomainTemplate") == 0 ||
313 strcasecmp((char *)el->values[j].data, "secretTemplate") == 0) {
316 if ( ! samldb_find_or_add_attribute(module, msg, el->name,
317 (char *)el->values[j].data,
318 (char *)el->values[j].data)) {
319 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "Attribute adding failed...\n");
324 if ( ! samldb_find_or_add_attribute(module, msg, el->name,
326 (char *)el->values[j].data)) {
327 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "Attribute adding failed...\n");
340 static struct ldb_message *samldb_fill_group_object(struct ldb_module *module, const struct ldb_message *msg)
342 struct ldb_message *msg2;
343 struct ldb_message_element *attribute;
344 struct ldb_dn_component *rdn;
346 if (samldb_find_attribute(msg, "objectclass", "group") == NULL) {
350 ldb_debug(module->ldb, LDB_DEBUG_TRACE, "samldb_fill_group_object\n");
352 /* build the new msg */
353 msg2 = ldb_msg_copy(module->ldb, msg);
355 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_fill_group_object: ldb_msg_copy failed!\n");
359 if (samldb_copy_template(module, msg2, "(&(CN=TemplateGroup)(objectclass=groupTemplate))") != 0) {
360 ldb_debug(module->ldb, LDB_DEBUG_WARNING, "samldb_fill_group_object: Error copying template!\n");
364 if ((rdn = ldb_dn_get_rdn(msg2, msg2->dn)) == NULL) {
365 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_fill_group_object: Bad DN (%s)!\n", ldb_dn_linearize(msg2, msg2->dn));
368 if (strcasecmp(rdn->name, "cn") != 0) {
369 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_fill_group_object: Bad RDN (%s) for group!\n", rdn->name);
373 if ((attribute = samldb_find_attribute(msg2, "objectSid", NULL)) == NULL ) {
374 struct dom_sid *sid = samldb_get_new_sid(module, msg2, msg2->dn);
376 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_fill_group_object: internal error! Can't generate new sid\n");
380 if (!samldb_msg_add_sid(module, msg2, "objectSid", sid)) {
387 if ( ! samldb_find_or_add_attribute(module, msg2, "sAMAccountName", NULL, samldb_generate_samAccountName(msg2))) {
391 talloc_steal(msg, msg2);
396 static struct ldb_message *samldb_fill_user_or_computer_object(struct ldb_module *module, const struct ldb_message *msg)
398 struct ldb_message *msg2;
399 struct ldb_message_element *attribute;
400 struct ldb_dn_component *rdn;
402 if ((samldb_find_attribute(msg, "objectclass", "user") == NULL) &&
403 (samldb_find_attribute(msg, "objectclass", "computer") == NULL)) {
407 ldb_debug(module->ldb, LDB_DEBUG_TRACE, "samldb_fill_user_or_computer_object\n");
409 /* build the new msg */
410 msg2 = ldb_msg_copy(module->ldb, msg);
412 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_fill_group_object: ldb_msg_copy failed!\n");
416 if (samldb_find_attribute(msg, "objectclass", "computer") != NULL) {
417 if (samldb_copy_template(module, msg2, "(&(CN=TemplateComputer)(objectclass=userTemplate))") != 0) {
418 ldb_debug(module->ldb, LDB_DEBUG_WARNING, "samldb_fill_user_or_computer_object: Error copying computer template!\n");
422 if (samldb_copy_template(module, msg2, "(&(CN=TemplateUser)(objectclass=userTemplate))") != 0) {
423 ldb_debug(module->ldb, LDB_DEBUG_WARNING, "samldb_fill_user_or_computer_object: Error copying user template!\n");
428 if ((rdn = ldb_dn_get_rdn(msg2, msg2->dn)) == NULL) {
431 if (strcasecmp(rdn->name, "cn") != 0) {
432 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_fill_user_or_computer_object: Bad RDN (%s) for user/computer!\n", rdn->name);
436 /* if the only attribute was: "objectclass: computer", then make sure we also add "user" objectclass */
437 if ( ! samldb_find_or_add_attribute(module, msg2, "objectclass", "user", "user")) {
441 if ((attribute = samldb_find_attribute(msg2, "objectSid", NULL)) == NULL ) {
443 sid = samldb_get_new_sid(module, msg2, msg2->dn);
445 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_fill_user_or_computer_object: internal error! Can't generate new sid\n");
449 if ( ! samldb_msg_add_sid(module, msg2, "objectSid", sid)) {
456 if ( ! samldb_find_or_add_attribute(module, msg2, "sAMAccountName", NULL, samldb_generate_samAccountName(msg2))) {
461 useraccountcontrol: setting value 0 gives 0x200 for users
464 /* TODO: objectCategory, userAccountControl, badPwdCount, codePage, countryCode, badPasswordTime, lastLogoff, lastLogon, pwdLastSet, primaryGroupID, accountExpires, logonCount */
469 static struct ldb_message *samldb_fill_foreignSecurityPrincipal_object(struct ldb_module *module, const struct ldb_message *msg)
471 struct ldb_message *msg2;
472 struct ldb_message_element *attribute;
473 struct ldb_dn_component *rdn;
475 if (samldb_find_attribute(msg, "objectclass", "foreignSecurityPrincipal") == NULL) {
479 ldb_debug(module->ldb, LDB_DEBUG_TRACE, "samldb_fill_foreignSecurityPrincipal_object\n");
481 /* build the new msg */
482 msg2 = ldb_msg_copy(module->ldb, msg);
484 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_fill_foreignSecurityPrincpal_object: ldb_msg_copy failed!\n");
488 talloc_steal(msg, msg2);
490 if (samldb_copy_template(module, msg2, "(&(CN=TemplateForeignSecurityPrincipal)(objectclass=foreignSecurityPrincipalTemplate))") != 0) {
491 ldb_debug(module->ldb, LDB_DEBUG_WARNING, "samldb_fill_foreignSecurityPrincipal_object: Error copying template!\n");
495 if ((rdn = ldb_dn_get_rdn(msg2, msg2->dn)) == NULL) {
496 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_fill_foreignSecurityPrincipal_object: Bad DN (%s)!\n", ldb_dn_linearize(msg2, msg2->dn));
499 if (strcasecmp(rdn->name, "cn") != 0) {
500 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_fill_foreignSecurityPrincipal_object: Bad RDN (%s) for foreignSecurityPrincpal!\n", rdn->name);
504 if ((attribute = samldb_find_attribute(msg2, "objectSid", NULL)) == NULL ) {
505 struct dom_sid *sid = dom_sid_parse_talloc(msg2, (char *)rdn->value.data);
507 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_fill_foreignSecurityPrincipal_object: internal error! Can't parse sid in CN\n");
511 if (!samldb_msg_add_sid(module, msg2, "objectSid", sid)) {
522 static int samldb_add(struct ldb_module *module, struct ldb_request *req)
524 const struct ldb_message *msg = req->op.add.message;
525 struct ldb_message *msg2 = NULL;
528 ldb_debug(module->ldb, LDB_DEBUG_TRACE, "samldb_add_record\n");
531 if (ldb_dn_is_special(msg->dn)) { /* do not manipulate our control entries */
532 return ldb_next_request(module, req);
535 /* is user or computer? add all relevant missing objects */
536 msg2 = samldb_fill_user_or_computer_object(module, msg);
538 /* is group? add all relevant missing objects */
540 msg2 = samldb_fill_group_object(module, msg);
543 /* perhaps a foreignSecurityPrincipal? */
545 msg2 = samldb_fill_foreignSecurityPrincipal_object(module, msg);
549 req->op.add.message = msg2;
550 ret = ldb_next_request(module, req);
551 req->op.add.message = msg;
553 ret = ldb_next_request(module, req);
559 static int samldb_destructor(void *module_ctx)
561 /* struct ldb_module *ctx = module_ctx; */
562 /* put your clean-up functions here */
566 static int samldb_request(struct ldb_module *module, struct ldb_request *req)
568 switch (req->operation) {
571 return samldb_add(module, req);
574 return ldb_next_request(module, req);
579 static const struct ldb_module_ops samldb_ops = {
581 .request = samldb_request
585 /* the init function */
586 #ifdef HAVE_DLOPEN_DISABLED
587 struct ldb_module *init_module(struct ldb_context *ldb, const char *options[])
589 struct ldb_module *samldb_module_init(struct ldb_context *ldb, const char *options[])
592 struct ldb_module *ctx;
594 ctx = talloc(ldb, struct ldb_module);
598 ctx->private_data = NULL;
600 ctx->prev = ctx->next = NULL;
601 ctx->ops = &samldb_ops;
603 talloc_set_destructor(ctx, samldb_destructor);