r8660: Use templates for the initial provision of user and computer accounts.
[kai/samba.git] / source / dsdb / samdb / ldb_modules / samldb.c
index 6b8546e2b83eb8a7809c51a86d547cc2f9f37e6a..40b6b72713392a267c9eb6a22e514a00d23b2e69 100644 (file)
@@ -1,25 +1,25 @@
 /* 
-   ldb database library
+   SAM ldb module
 
    Copyright (C) Simo Sorce  2004
 
-     ** NOTE! The following LGPL license applies to the ldb
-     ** library. This does NOT imply that all of Samba is released
-     ** under the LGPL
-   
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2 of the License, or (at your option) any later version.
+   * NOTICE: this module is NOT released under the GNU LGPL license as
+   * other ldb code. This module is release under the GNU GPL v2 or
+   * later license.
 
-   This library is distributed in the hope that it will be useful,
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   You should have received a copy of the GNU Lesser General Public
-   License along with this library; if not, write to the Free Software
-   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
 /*
@@ -27,7 +27,7 @@
  *
  *  Component: ldb samldb module
  *
- *  Description: add object timestamping functionality
+ *  Description: add embedded user/group creation functionality
  *
  *  Author: Simo Sorce
  */
@@ -35,7 +35,8 @@
 #include "includes.h"
 #include "lib/ldb/include/ldb.h"
 #include "lib/ldb/include/ldb_private.h"
-#include <time.h>
+#include "system/time.h"
+#include "librpc/gen_ndr/ndr_security.h"
 
 #define SAM_ACCOUNT_NAME_BASE "$000000-000000000000"
 
@@ -51,10 +52,186 @@ static int samldb_search(struct ldb_module *module, const char *base,
        return ldb_next_search(module, base, scope, expression, attrs, res);
 }
 
-static int samldb_search_free(struct ldb_module *module, struct ldb_message **res)
+static int samldb_search_bytree(struct ldb_module *module, const char *base,
+                               enum ldb_scope scope, struct ldb_parse_tree *tree,
+                               const char * const *attrs, struct ldb_message ***res)
 {
-ldb_debug(module->ldb, LDB_DEBUG_TRACE, "samldb_search_free\n");
-       return ldb_next_search_free(module, res);
+       ldb_debug(module->ldb, LDB_DEBUG_TRACE, "samldb_search\n");
+       return ldb_next_search_bytree(module, base, scope, tree, attrs, res);
+}
+
+/*
+  allocate a new id, attempting to do it atomically
+  return 0 on failure, the id on success
+*/
+static int samldb_allocate_next_rid(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
+                                  const char *dn, uint32_t *id)
+{
+       const char * const attrs[2] = { "nextRid", NULL };
+       struct ldb_message **res = NULL;
+       struct ldb_message msg;
+       int ret;
+       const char *str;
+       struct ldb_val vals[2];
+       struct ldb_message_element els[2];
+
+       ret = ldb_search(ldb, dn, LDB_SCOPE_BASE, "nextRid=*", attrs, &res);
+       if (ret != 1) {
+               if (res) talloc_free(res);
+               return -1;
+       }
+       str = ldb_msg_find_string(res[0], "nextRid", NULL);
+       if (str == NULL) {
+               ldb_debug(ldb, LDB_DEBUG_FATAL, "attribute nextRid not found in %s\n", dn);
+               talloc_free(res);
+               return -1;
+       }
+
+       *id = strtol(str, NULL, 0);
+       if ((*id)+1 == 0) {
+               /* out of IDs ! */
+               ldb_debug(ldb, LDB_DEBUG_FATAL, "Are we out of valid IDs ?\n");
+               talloc_free(res);
+               return -1;
+       }
+       talloc_free(res);
+
+       /* we do a delete and add as a single operation. That prevents
+          a race */
+       ZERO_STRUCT(msg);
+       msg.dn = talloc_strdup(mem_ctx, dn);
+       if (!msg.dn) {
+               return -1;
+       }
+       msg.num_elements = 2;
+       msg.elements = els;
+
+       els[0].num_values = 1;
+       els[0].values = &vals[0];
+       els[0].flags = LDB_FLAG_MOD_DELETE;
+       els[0].name = talloc_strdup(mem_ctx, "nextRid");
+       if (!els[0].name) {
+               return -1;
+       }
+
+       els[1].num_values = 1;
+       els[1].values = &vals[1];
+       els[1].flags = LDB_FLAG_MOD_ADD;
+       els[1].name = els[0].name;
+
+       vals[0].data = talloc_asprintf(mem_ctx, "%u", *id);
+       if (!vals[0].data) {
+               return -1;
+       }
+       vals[0].length = strlen(vals[0].data);
+
+       vals[1].data = talloc_asprintf(mem_ctx, "%u", (*id)+1);
+       if (!vals[1].data) {
+               return -1;
+       }
+       vals[1].length = strlen(vals[1].data);
+
+       ret = ldb_modify(ldb, &msg);
+       if (ret != 0) {
+               return 1;
+       }
+
+       (*id)++;
+
+       return 0;
+}
+
+static char *samldb_search_domain(struct ldb_module *module, TALLOC_CTX *mem_ctx, const char *dn)
+{
+       const char *sdn;
+       struct ldb_message **res = NULL;
+       int ret = 0;
+
+       sdn = dn;
+       while ((sdn = strchr(sdn, ',')) != NULL) {
+
+               sdn++;
+
+               ret = ldb_search(module->ldb, sdn, LDB_SCOPE_BASE, "objectClass=domain", NULL, &res);
+               talloc_free(res);
+
+               if (ret == 1)
+                       break;
+       }
+
+       if (ret != 1) {
+               return NULL;
+       }
+
+       return talloc_strdup(mem_ctx, sdn);
+}
+
+/* search the domain related to the provided dn
+   allocate a new RID for the domain
+   return the new sid string
+*/
+static struct dom_sid *samldb_get_new_sid(struct ldb_module *module, 
+                                         TALLOC_CTX *mem_ctx, const char *obj_dn)
+{
+       const char * const attrs[2] = { "objectSid", NULL };
+       struct ldb_message **res = NULL;
+       const char *dom_dn;
+       uint32_t rid;
+       int ret, tries = 10;
+       struct dom_sid *dom_sid, *obj_sid;
+
+       /* get the domain component part of the provided dn */
+
+       /* FIXME: quick search here, I think we should use something like
+          ldap_parse_dn here to be 100% sure we get the right domain dn */
+
+       /* FIXME: "dc=" is probably not utf8 safe either,
+          we need a multibyte safe substring search function here */
+       
+       dom_dn = samldb_search_domain(module, mem_ctx, obj_dn);
+       if (dom_dn == NULL) {
+               ldb_debug(module->ldb, LDB_DEBUG_FATAL, "Invalid dn (%s) not child of a domain object!\n", obj_dn);
+               return NULL;
+       }
+
+       /* find the domain sid */
+
+       ret = ldb_search(module->ldb, dom_dn, LDB_SCOPE_BASE, "objectSid=*", attrs, &res);
+       if (ret != 1) {
+               ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_get_new_sid: error retrieving domain sid!\n");
+               talloc_free(res);
+               return NULL;
+       }
+
+       dom_sid = samdb_result_dom_sid(res, res[0], "objectSid");
+       if (dom_sid == NULL) {
+               ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_get_new_sid: error retrieving domain sid!\n");
+               talloc_free(res);
+               return NULL;
+       }
+
+       /* allocate a new Rid for the domain */
+
+       /* we need to try multiple times to cope with two account
+          creations at the same time */
+       while (tries--) {
+               ret = samldb_allocate_next_rid(module->ldb, mem_ctx, dom_dn, &rid);
+               if (ret != 1) {
+                       break;
+               }
+       }
+       if (ret != 0) {
+               ldb_debug(module->ldb, LDB_DEBUG_FATAL, "Failed to increment nextRid of %s\n", dom_dn);
+               talloc_free(res);
+               return NULL;
+       }
+
+       /* return the new object sid */
+       obj_sid = dom_sid_add_rid(mem_ctx, dom_sid, rid);
+
+       talloc_free(res);
+
+       return obj_sid;
 }
 
 static char *samldb_generate_samAccountName(const void *mem_ctx) {
@@ -74,13 +251,8 @@ static BOOL samldb_get_rdn_and_basedn(const void *mem_ctx, const char *dn, char
        if ( ! p ) {
                return False;
        }
-       /* clear separator */
-       *p = '\0';
-
-       *rdn = talloc_strdup(mem_ctx, dn);
 
-       /* put back separator */
-       *p = ',';
+       *rdn = talloc_strndup(mem_ctx, dn, p - dn);
 
        if ( ! *rdn) {
                return False;
@@ -118,265 +290,256 @@ static struct ldb_message_element *samldb_find_attribute(const struct ldb_messag
        return NULL;
 }
 
-static BOOL samldb_add_attribute(struct ldb_message *msg, const char *name, const char *value)
+static BOOL samldb_msg_add_string(struct ldb_module *module, struct ldb_message *msg, const char *name, const char *value)
 {
-       struct ldb_message_element *attr;
-       int i;
-
-       attr = samldb_find_attribute(msg, name, NULL);
-       if ( ! attr) {
-               msg->num_elements++;
-               msg->elements = talloc_realloc(msg, msg->elements, struct ldb_message_element, msg->num_elements);
-               if ( ! msg->elements ) {
-                       return False;
-               }
-               attr = &msg->elements[msg->num_elements - 1];
-
-               attr->name = talloc_strdup(msg, name);
-               if ( ! attr->name ) {
-                       return False;
-               }
-               attr->flags = 0;
-               attr->num_values = 0;
-               attr->values = NULL;
-       }
+       char *aname = talloc_strdup(msg, name);
+       char *aval = talloc_strdup(msg, value);
 
-       i = attr->num_values;
-       attr->num_values++;
-       attr->values = talloc_realloc(msg, attr->values, struct ldb_val, attr->num_values);
-       if ( ! attr->values ){
+       if (aname == NULL || aval == NULL) {
+               ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_msg_add_string: talloc_strdup failed!\n");
                return False;
        }
 
-       attr->values[i].data = talloc_strdup(msg, value);
-       attr->values[i].length = strlen(value);
-
-       if ( ! attr->values[i].data) {
+       if (ldb_msg_add_string(module->ldb, msg, aname, aval) != 0) {
                return False;
        }
 
        return True;
 }
 
-static BOOL samldb_find_or_add_attribute(struct ldb_message *msg, const char *name, const char *value, const char *set_value)
+static BOOL samldb_msg_add_sid(struct ldb_module *module, struct ldb_message *msg, const char *name, const struct dom_sid *sid)
+{
+       struct ldb_val v;
+       NTSTATUS status;
+       status = ndr_push_struct_blob(&v, msg, sid, 
+                                     (ndr_push_flags_fn_t)ndr_push_dom_sid);
+       if (!NT_STATUS_IS_OK(status)) {
+               return -1;
+       }
+       return (ldb_msg_add_value(module->ldb, msg, name, &v) == 0);
+}
+
+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)
 {
        if (samldb_find_attribute(msg, name, value) == NULL) {
-               if ( ! samldb_add_attribute(msg, name, set_value)) {
-                       return False;
-               }
+               return samldb_msg_add_string(module, msg, name, set_value);
        }
        return True;
 }
 
-static struct ldb_message *samldb_manage_group_object(struct ldb_module *module, const struct ldb_message *msg)
+static int samldb_copy_template(struct ldb_module *module, struct ldb_message *msg, const char *filter)
+{
+       struct ldb_message **res, *t;
+       int ret, i, j;
+       
+
+       /* pull the template record */
+       ret = ldb_search(module->ldb, NULL, LDB_SCOPE_SUBTREE, filter, NULL, &res);
+       if (ret != 1) {
+               ldb_debug(module->ldb, LDB_DEBUG_WARNING, "samldb: ERROR: template '%s' matched %d records\n", filter, ret);
+               return -1;
+       }
+       t = res[0];
+
+       for (i = 0; i < t->num_elements; i++) {
+               struct ldb_message_element *el = &t->elements[i];
+               /* some elements should not be copied from the template */
+               if (strcasecmp(el->name, "cn") == 0 ||
+                   strcasecmp(el->name, "name") == 0 ||
+                   strcasecmp(el->name, "sAMAccountName") == 0 ||
+                   strcasecmp(el->name, "objectGUID") == 0) {
+                       continue;
+               }
+               for (j = 0; j < el->num_values; j++) {
+                       if (strcasecmp(el->name, "objectClass") == 0 &&
+                           (strcasecmp((char *)el->values[j].data, "Template") == 0 ||
+                            strcasecmp((char *)el->values[j].data, "userTemplate") == 0 ||
+                            strcasecmp((char *)el->values[j].data, "groupTemplate") == 0 ||
+                            strcasecmp((char *)el->values[j].data, "foreignSecurityTemplate") == 0 ||
+                            strcasecmp((char *)el->values[j].data, "aliasTemplate") == 0 || 
+                            strcasecmp((char *)el->values[j].data, "trustedDomainTemplate") == 0 || 
+                            strcasecmp((char *)el->values[j].data, "secretTemplate") == 0)) {
+                               continue;
+                       }
+                       if ( ! samldb_find_or_add_attribute(module, msg, el->name, 
+                                                           NULL,
+                                                           (char *)el->values[j].data)) {
+                               ldb_debug(module->ldb, LDB_DEBUG_FATAL, "Attribute adding failed...\n");
+                               talloc_free(res);
+                               return -1;
+                       }
+               }
+       }
+
+       talloc_free(res);
+
+       return 0;
+}
+
+static struct ldb_message *samldb_fill_group_object(struct ldb_module *module, const struct ldb_message *msg)
 {
        struct ldb_message *msg2;
        struct ldb_message_element *attribute;
        char *rdn, *basedn;
-       int i;
 
        if (samldb_find_attribute(msg, "objectclass", "group") == NULL) {
                return NULL;
        }
 
-       msg2 = talloc(module, struct ldb_message);
+       ldb_debug(module->ldb, LDB_DEBUG_TRACE, "samldb_fill_group_object\n");
+
+       /* build the new msg */
+       msg2 = ldb_msg_copy(module->ldb, msg);
        if (!msg2) {
-               ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_manage_group_object: talloc failed!\n");
+               ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_fill_group_object: ldb_msg_copy failed!\n");
                return NULL;
        }
 
-       /* build the new msg */
-       msg2->dn = msg->dn;
-       msg2->num_elements = msg->num_elements;
-       msg2->private_data = msg->private_data;
-       msg2->elements = talloc_array(msg2, struct ldb_message_element, msg2->num_elements);
-       if (! msg2->elements) {
-               ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_manage_group_object: talloc_array failed!\n");
-               talloc_free(msg2);
+       if (samldb_copy_template(module, msg2, "(&(CN=TemplateGroup)(objectclass=groupTemplate))") != 0) {
+               ldb_debug(module->ldb, LDB_DEBUG_WARNING, "samldb_fill_group_object: Error copying template!\n");
                return NULL;
        }
-       for (i = 0; i < msg2->num_elements; i++) {
-               msg2->elements[i] = msg->elements[i];
-       }
 
        if ( ! samldb_get_rdn_and_basedn(msg2, msg2->dn, &rdn, &basedn)) {
-               talloc_free(msg2);
+               ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_fill_group_object: Bad DN (%s)!\n", msg2->dn);
                return NULL;
        }
        if (strncasecmp(rdn, "cn", 2) != 0) {
-               ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_manage_group_object: Bad RDN (%s) for group!\n", rdn);
-               talloc_free(msg2);
-               return NULL;
-       }
-
-       if (! samldb_find_or_add_attribute(msg2, "objectclass", "top", "top")) {
-               talloc_free(msg2);
+               ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_fill_group_object: Bad RDN (%s) for group!\n", rdn);
                return NULL;
        }
 
        if ((attribute = samldb_find_attribute(msg2, "cn", NULL)) != NULL) {
-               if (strcasecmp(rdn, attribute->values[0].data) != 0) {
-                       ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_manage_group_object: Bad Attribute Syntax for CN\n");
-                       talloc_free(msg2);
+               if (strcasecmp(&rdn[3], attribute->values[0].data) != 0) {
+                       ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_fill_group_object: Bad Attribute Syntax for CN\n");
                        return NULL;
                }
        } else { /* FIXME: remove this if ldb supports natively aliasing between the rdn and the "cn" attribute */
-               if ( ! samldb_add_attribute(msg2, "cn", &rdn[3])) {
-                       talloc_free(msg2);
+               if ( ! samldb_msg_add_string(module, msg2, "cn", &rdn[3])) {
                        return NULL;
                }
        }
 
        if ((attribute = samldb_find_attribute(msg2, "name", NULL)) != NULL) {
-               if (strcasecmp(rdn, attribute->values[0].data) != 0) {
-                       ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_manage_group_object: Bad Attribute Syntax for name\n");
-                       talloc_free(msg2);
+               if (strcasecmp(&rdn[3], attribute->values[0].data) != 0) {
                        return NULL;
                }
        } else { /* FIXME: remove this if ldb supports natively aliasing between the rdn and the "name" attribute */
-               if ( ! samldb_add_attribute(msg2, "name", &rdn[3])) {
-                       talloc_free(msg2);
+               if ( ! samldb_msg_add_string(module, msg2, "name", &rdn[3])) {
                        return NULL;
                }
        }
 
-       if ( ! samldb_find_or_add_attribute(msg2, "instanceType", NULL, "4")) {
-               return NULL;
-       }
-
-       if ( ! samldb_find_or_add_attribute(msg2, "sAMAccountName", NULL, samldb_generate_samAccountName(msg2))) {
-               return NULL;
-       }
-
-       if ( ! samldb_find_or_add_attribute(msg2, "sAMAccountType", NULL, "268435456")) {
-               return NULL;
-       }
-
-       if ( ! samldb_find_or_add_attribute(msg2, "groupType", NULL, "-2147483646")) {
-               return NULL;
-       }
+       if ((attribute = samldb_find_attribute(msg2, "objectSid", NULL)) == NULL ) {
+               struct dom_sid *sid = samldb_get_new_sid(module, msg2, msg2->dn);
+               if (sid == NULL) {
+                       ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_fill_group_object: internal error! Can't generate new sid\n");
+                       return NULL;
+               }
 
-       if ( ! samldb_find_or_add_attribute(msg2, "objectCategory", NULL, "foo")) { /* keep the schema module happy :) */
-               return NULL;
+               if (!samldb_msg_add_sid(module, msg2, "objectSid", sid)) {
+                       talloc_free(sid);
+                       return NULL;
+               }
+               talloc_free(sid);
        }
 
-       if ( ! samldb_find_or_add_attribute(msg2, "objectSid", NULL, "foo")) { /* keep the schema module happy :) */
+       if ( ! samldb_find_or_add_attribute(module, msg2, "sAMAccountName", NULL, samldb_generate_samAccountName(msg2))) {
                return NULL;
        }
 
-       /* TODO: objectGUID, objectSid, objectCategory */
-       /* need a way to lock a new Sid */
+       talloc_steal(msg, msg2);
 
        return msg2;
 }
 
-static struct ldb_message *samldb_manage_user_object(struct ldb_module *module, const struct ldb_message *msg)
+static struct ldb_message *samldb_fill_user_or_computer_object(struct ldb_module *module, const struct ldb_message *msg)
 {
        struct ldb_message *msg2;
        struct ldb_message_element *attribute;
        char *rdn, *basedn;
-       int i;
 
-       if (samldb_find_attribute(msg, "objectclass", "user") == NULL) {
+       if ((samldb_find_attribute(msg, "objectclass", "user") == NULL) && 
+           (samldb_find_attribute(msg, "objectclass", "computer") == NULL)) {
                return NULL;
        }
 
-       msg2 = talloc(module, struct ldb_message);
-       if (!msg2) {
-               ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_manage_user_object: talloc failed!\n");
-               return NULL;
-       }
+       ldb_debug(module->ldb, LDB_DEBUG_TRACE, "samldb_fill_user_or_computer_object\n");
 
        /* build the new msg */
-       msg2->dn = msg->dn;
-       msg2->num_elements = msg->num_elements;
-       msg2->private_data = msg->private_data;
-       msg2->elements = talloc_array(msg2, struct ldb_message_element, msg2->num_elements);
-       if (! msg2->elements) {
-               ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_manage_user_object: talloc_array failed!\n");
-               talloc_free(msg2);
+       msg2 = ldb_msg_copy(module->ldb, msg);
+       if (!msg2) {
+               ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_fill_group_object: ldb_msg_copy failed!\n");
                return NULL;
        }
-       for (i = 0; i < msg2->num_elements; i++) {
-               msg2->elements[i] = msg->elements[i];
+
+       if (samldb_find_attribute(msg, "objectclass", "computer") == NULL) {
+               if (samldb_copy_template(module, msg2, "(&(CN=TemplateMemberServer)(objectclass=userTemplate))") != 0) {
+                       ldb_debug(module->ldb, LDB_DEBUG_WARNING, "samldb_fill_user_or_computer_object: Error copying computer template!\n");
+                       return NULL;
+               }
+       } else {
+               if (samldb_copy_template(module, msg2, "(&(CN=TemplateUser)(objectclass=userTemplate))") != 0) {
+                       ldb_debug(module->ldb, LDB_DEBUG_WARNING, "samldb_fill_user_or_computer_object: Error copying user template!\n");
+                       return NULL;
+               }
        }
 
        if ( ! samldb_get_rdn_and_basedn(msg2, msg2->dn, &rdn, &basedn)) {
-               talloc_free(msg2);
                return NULL;
        }
        if (strncasecmp(rdn, "cn", 2) != 0) {
-               ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_manage_group_object: Bad RDN (%s) for group!\n", rdn);
-               talloc_free(msg2);
-               return NULL;
-       }
-
-
-       if ( ! samldb_find_or_add_attribute(msg2, "objectclass", "top", "top")) {
-               talloc_free(msg2);
+               ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_fill_user_or_computer_object: Bad RDN (%s) for group!\n", rdn);
                return NULL;
        }
 
-       if ( ! samldb_find_or_add_attribute(msg2, "objectclass", "person", "person")) {
-               talloc_free(msg2);
-               return NULL;
-       }
-
-       if ( ! samldb_find_or_add_attribute(msg2, "objectclass", "organizationalPerson", "organizationalPerson")) {
-               talloc_free(msg2);
+       /* if the only attribute was: "objectclass: computer", then make sure we also add "user" objectclass */
+       if ( ! samldb_find_or_add_attribute(module, msg2, "objectclass", "user", "user")) {
                return NULL;
        }
 
        if ((attribute = samldb_find_attribute(msg2, "cn", NULL)) != NULL) {
-               if (strcasecmp(rdn, attribute->values[0].data) != 0) {
-                       ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_manage_user_object: Bad Attribute Syntax for CN\n");
-                       talloc_free(msg2);
+               if (strcasecmp(&rdn[3], attribute->values[0].data) != 0) {
+                       ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_fill_user_or_computer_object: Bad Attribute Syntax for CN\n");
                        return NULL;
                }
        } else { /* FIXME: remove this if ldb supports natively aliasing between the rdn and the "cn" attribute */
-               if ( ! samldb_add_attribute(msg2, "cn", &rdn[3])) {
-                       talloc_free(msg2);
+               if ( ! samldb_msg_add_string(module, msg2, "cn", &rdn[3])) {
                        return NULL;
                }
        }
 
        if ((attribute = samldb_find_attribute(msg2, "name", NULL)) != NULL) {
-               if (strcasecmp(rdn, attribute->values[0].data) != 0) {
-                       ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_manage_user_object: Bad Attribute Syntax for name\n");
-                       talloc_free(msg2);
+               if (strcasecmp(&rdn[3], attribute->values[0].data) != 0) {
+                       ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_fill_user_or_computer_object: Bad Attribute Syntax for name\n");
                        return NULL;
                }
        } else { /* FIXME: remove this if ldb supports natively aliasing between the rdn and the "name" attribute */
-               if ( ! samldb_add_attribute(msg2, "name", &rdn[3])) {
-                       talloc_free(msg2);
+               if ( ! samldb_msg_add_string(module, msg2, "name", &rdn[3])) {
                        return NULL;
                }
        }
 
-       if ( ! samldb_find_or_add_attribute(msg2, "instanceType", NULL, "4")) {
-               talloc_free(msg2);
-               return NULL;
-       }
-
-       if ( ! samldb_find_or_add_attribute(msg2, "sAMAccountName", NULL, samldb_generate_samAccountName(msg2))) {
-               talloc_free(msg2);
-               return NULL;
-       }
-
-       if ( ! samldb_find_or_add_attribute(msg2, "sAMAccountType", NULL, "805306368")) {
-               talloc_free(msg2);
-               return NULL;
-       }
+       if ((attribute = samldb_find_attribute(msg2, "objectSid", NULL)) == NULL ) {
+               struct dom_sid *sid;
+               sid = samldb_get_new_sid(module, msg2, msg2->dn);
+               if (sid == NULL) {
+                       ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_fill_user_or_computer_object: internal error! Can't generate new sid\n");
+                       return NULL;
+               }
 
-       if ( ! samldb_find_or_add_attribute(msg2, "objectCategory", NULL, "foo")) { /* keep the schema module happy :) */
-               return NULL;
+               if ( ! samldb_msg_add_sid(module, msg2, "objectSid", sid)) {
+                       talloc_free(sid);
+                       return NULL;
+               }
+               talloc_free(sid);
        }
 
-       if ( ! samldb_find_or_add_attribute(msg2, "objectSid", NULL, "foo")) { /* keep the schema module happy :) */
+       if ( ! samldb_find_or_add_attribute(module, msg2, "sAMAccountName", NULL, samldb_generate_samAccountName(msg2))) {
                return NULL;
        }
 
-       /* TODO: objectGUID, objectSid, objectCategory, userAccountControl, badPwdCount, codePage, countryCode, badPasswordTime, lastLogoff, lastLogon, pwdLastSet, primaryGroupID, accountExpires, logonCount */
+       /* TODO: objectCategory, userAccountControl, badPwdCount, codePage, countryCode, badPasswordTime, lastLogoff, lastLogon, pwdLastSet, primaryGroupID, accountExpires, logonCount */
 
        return msg2;
 }
@@ -393,17 +556,16 @@ static int samldb_add_record(struct ldb_module *module, const struct ldb_message
                return ldb_next_add_record(module, msg);
        }
 
-       /* is group?  add all group relevant missing objects */
-       msg2 = samldb_manage_group_object(module, msg);
+       /* is user or computer?  add all relevant missing objects */
+       msg2 = samldb_fill_user_or_computer_object(module, msg);
 
-       /* is user? add all user relevant missing objects */
+       /* is group? add all relevant missing objects */
        if ( ! msg2 ) {
-               msg2 = samldb_manage_user_object(module, msg);
+               msg2 = samldb_fill_group_object(module, msg);
        }
 
        if (msg2) {
                ret = ldb_next_add_record(module, msg2);
-               talloc_free(msg2);
        } else {
                ret = ldb_next_add_record(module, msg);
        }
@@ -461,22 +623,22 @@ static const char *samldb_errstring(struct ldb_module *module)
 
 static int samldb_destructor(void *module_ctx)
 {
-       struct ldb_module *ctx = module_ctx;
+       /* struct ldb_module *ctx = module_ctx; */
        /* put your clean-up functions here */
        return 0;
 }
 
 static const struct ldb_module_ops samldb_ops = {
-       "samldb",
-       samldb_search,
-       samldb_search_free,
-       samldb_add_record,
-       samldb_modify_record,
-       samldb_delete_record,
-       samldb_rename_record,
-       samldb_lock,
-       samldb_unlock,
-       samldb_errstring
+       .name          = "samldb",
+       .search        = samldb_search,
+       .search_bytree = samldb_search_bytree,
+       .add_record    = samldb_add_record,
+       .modify_record = samldb_modify_record,
+       .delete_record = samldb_delete_record,
+       .rename_record = samldb_rename_record,
+       .named_lock    = samldb_lock,
+       .named_unlock  = samldb_unlock,
+       .errstring     = samldb_errstring
 };