4 Copyright (C) Simo Sorce 2004
6 ** NOTE! The following LGPL license applies to the ldb
7 ** library. This does NOT imply that all of Samba is released
10 This library is free software; you can redistribute it and/or
11 modify it under the terms of the GNU Lesser General Public
12 License as published by the Free Software Foundation; either
13 version 2 of the License, or (at your option) any later version.
15 This library 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 GNU
18 Lesser General Public License for more details.
20 You should have received a copy of the GNU Lesser General Public
21 License along with this library; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 * Component: ldb samldb module
30 * Description: add object timestamping functionality
36 #include "lib/ldb/include/ldb.h"
37 #include "lib/ldb/include/ldb_private.h"
40 #define SAM_ACCOUNT_NAME_BASE "$000000-000000000000"
43 const char *error_string
;
46 static int samldb_search(struct ldb_module
*module
, const char *base
,
47 enum ldb_scope scope
, const char *expression
,
48 const char * const *attrs
, struct ldb_message
***res
)
50 ldb_debug(module
->ldb
, LDB_DEBUG_TRACE
, "samldb_search\n");
51 return ldb_next_search(module
, base
, scope
, expression
, attrs
, res
);
54 static int samldb_search_free(struct ldb_module
*module
, struct ldb_message
**res
)
56 ldb_debug(module
->ldb
, LDB_DEBUG_TRACE
, "samldb_search_free\n");
57 return ldb_next_search_free(module
, res
);
62 allocate a new id, attempting to do it atomically
63 return 0 on failure, the id on success
65 static int samldb_allocate_next_rid(struct ldb_context
*ldb
, TALLOC_CTX
*mem_ctx
,
66 const char *dn
, uint32_t *id
)
68 const char * const attrs
[2] = { "nextRid", NULL
};
69 struct ldb_message
**res
= NULL
;
70 struct ldb_message msg
;
73 struct ldb_val vals
[2];
74 struct ldb_message_element els
[2];
76 ret
= ldb_search(ldb
, dn
, LDB_SCOPE_BASE
, "nextRid=*", attrs
, &res
);
78 if (res
) ldb_search_free(ldb
, res
);
81 str
= ldb_msg_find_string(res
[0], "nextRid", NULL
);
83 ldb_debug(ldb
, LDB_DEBUG_FATAL
, "attribute nextRid not found in %s\n", dn
);
84 ldb_search_free(ldb
, res
);
87 talloc_steal(mem_ctx
, str
);
88 ldb_search_free(ldb
, res
);
90 *id
= strtol(str
, NULL
, 0);
96 /* we do a delete and add as a single operation. That prevents
99 msg
.dn
= talloc_strdup(mem_ctx
, dn
);
103 msg
.num_elements
= 2;
106 els
[0].num_values
= 1;
107 els
[0].values
= &vals
[0];
108 els
[0].flags
= LDB_FLAG_MOD_DELETE
;
109 els
[0].name
= talloc_strdup(mem_ctx
, "nextRid");
114 els
[1].num_values
= 1;
115 els
[1].values
= &vals
[1];
116 els
[1].flags
= LDB_FLAG_MOD_ADD
;
117 els
[1].name
= els
[0].name
;
119 vals
[0].data
= talloc_asprintf(mem_ctx
, "%u", *id
);
123 vals
[0].length
= strlen(vals
[0].data
);
125 vals
[1].data
= talloc_asprintf(mem_ctx
, "%u", (*id
)+1);
129 vals
[1].length
= strlen(vals
[1].data
);
131 ret
= ldb_modify(ldb
, &msg
);
141 /* search the domain related to the provided dn
142 allocate a new RID for the domain
143 return the new sid string
145 static char *samldb_get_new_sid(struct ldb_context
*ldb
, TALLOC_CTX
*mem_ctx
, const char *obj_dn
)
147 const char * const attrs
[2] = { "objectSid", NULL
};
148 struct ldb_message
**res
= NULL
;
149 const char *dom_dn
, *dom_sid
;
154 /* get the domain component part of the provided dn */
156 /* FIXME: quick search here, I think we should use something like
157 ldap_parse_dn here to be 100% sure we get the right domain dn */
159 /* FIXME: "dc=" is probably not utf8 safe either,
160 we need a multibyte safe substring search function here */
162 dom_dn
= strstr(obj_dn
, "dc=");
163 if (dom_dn
== NULL
) {
164 ldb_debug(ldb
, LDB_DEBUG_FATAL
, "Invalid dn (%s)!\n", obj_dn
);
168 /* find the domain sid */
170 ret
= ldb_search(ldb
, dom_dn
, LDB_SCOPE_BASE
, "objectSid=*", attrs
, &res
);
172 ldb_debug(ldb
, LDB_DEBUG_FATAL
, "samldb_get_new_sid: error retrieving domain sid!\n");
173 if (res
) ldb_search_free(ldb
, res
);
177 dom_sid
= ldb_msg_find_string(res
[0], "objectSid", NULL
);
178 if (dom_sid
== NULL
) {
179 ldb_debug(ldb
, LDB_DEBUG_FATAL
, "samldb_get_new_sid: error retrieving domain sid!\n");
180 ldb_search_free(ldb
, res
);
184 talloc_steal(mem_ctx
, dom_sid
);
185 ldb_search_free(ldb
, res
);
187 /* allocate a new Rid for the domain */
190 /* we need to try multiple times to cope with two account
191 creations at the same time */
193 ret
= samldb_allocate_next_rid(ldb
, mem_ctx
, dom_dn
, &rid
);
199 ldb_debug(ldb
, LDB_DEBUG_FATAL
, "Failed to increment nextRid of %s\n", dom_dn
);
203 /* return the new object sid */
205 obj_sid
= talloc_asprintf(mem_ctx
, "%s-%u", dom_sid
, rid
);
210 static char *samldb_generate_samAccountName(const void *mem_ctx
) {
213 name
= talloc_strdup(mem_ctx
, SAM_ACCOUNT_NAME_BASE
);
214 /* TODO: randomize name */
219 static BOOL
samldb_get_rdn_and_basedn(const void *mem_ctx
, const char *dn
, char **rdn
, char **basedn
)
227 /* clear separator */
230 *rdn
= talloc_strdup(mem_ctx
, dn
);
232 /* put back separator */
239 *basedn
= talloc_strdup(mem_ctx
, p
+ 1);
250 /* if value is not null also check for attribute to have exactly that value */
251 static struct ldb_message_element
*samldb_find_attribute(const struct ldb_message
*msg
, const char *name
, const char *value
)
255 for (i
= 0; i
< msg
->num_elements
; i
++) {
256 if (ldb_attr_cmp(name
, msg
->elements
[i
].name
) == 0) {
258 return &msg
->elements
[i
];
260 for (j
= 0; j
< msg
->elements
[i
].num_values
; j
++) {
261 if (strcasecmp(value
, msg
->elements
[i
].values
[j
].data
) == 0) {
262 return &msg
->elements
[i
];
271 static BOOL
samldb_add_attribute(struct ldb_message
*msg
, const char *name
, const char *value
)
273 struct ldb_message_element
*attr
;
276 attr
= samldb_find_attribute(msg
, name
, NULL
);
279 msg
->elements
= talloc_realloc(msg
, msg
->elements
, struct ldb_message_element
, msg
->num_elements
);
280 if ( ! msg
->elements
) {
283 attr
= &msg
->elements
[msg
->num_elements
- 1];
285 attr
->name
= talloc_strdup(msg
, name
);
286 if ( ! attr
->name
) {
290 attr
->num_values
= 0;
294 i
= attr
->num_values
;
296 attr
->values
= talloc_realloc(msg
, attr
->values
, struct ldb_val
, attr
->num_values
);
297 if ( ! attr
->values
){
301 attr
->values
[i
].data
= talloc_strdup(msg
, value
);
302 attr
->values
[i
].length
= strlen(value
);
304 if ( ! attr
->values
[i
].data
) {
311 static BOOL
samldb_find_or_add_attribute(struct ldb_message
*msg
, const char *name
, const char *value
, const char *set_value
)
313 if (samldb_find_attribute(msg
, name
, value
) == NULL
) {
314 if ( ! samldb_add_attribute(msg
, name
, set_value
)) {
321 static struct ldb_message
*samldb_manage_group_object(struct ldb_module
*module
, const struct ldb_message
*msg
)
323 struct ldb_message
*msg2
;
324 struct ldb_message_element
*attribute
;
328 if (samldb_find_attribute(msg
, "objectclass", "group") == NULL
) {
332 msg2
= talloc(module
, struct ldb_message
);
334 ldb_debug(module
->ldb
, LDB_DEBUG_FATAL
, "samldb_manage_group_object: talloc failed!\n");
338 /* build the new msg */
340 msg2
->num_elements
= msg
->num_elements
;
341 msg2
->private_data
= msg
->private_data
;
342 msg2
->elements
= talloc_array(msg2
, struct ldb_message_element
, msg2
->num_elements
);
343 if (! msg2
->elements
) {
344 ldb_debug(module
->ldb
, LDB_DEBUG_FATAL
, "samldb_manage_group_object: talloc_array failed!\n");
348 for (i
= 0; i
< msg2
->num_elements
; i
++) {
349 msg2
->elements
[i
] = msg
->elements
[i
];
352 if ( ! samldb_get_rdn_and_basedn(msg2
, msg2
->dn
, &rdn
, &basedn
)) {
356 if (strncasecmp(rdn
, "cn", 2) != 0) {
357 ldb_debug(module
->ldb
, LDB_DEBUG_FATAL
, "samldb_manage_group_object: Bad RDN (%s) for group!\n", rdn
);
362 if (! samldb_find_or_add_attribute(msg2
, "objectclass", "top", "top")) {
367 if ((attribute
= samldb_find_attribute(msg2
, "cn", NULL
)) != NULL
) {
368 if (strcasecmp(rdn
, attribute
->values
[0].data
) != 0) {
369 ldb_debug(module
->ldb
, LDB_DEBUG_FATAL
, "samldb_manage_group_object: Bad Attribute Syntax for CN\n");
373 } else { /* FIXME: remove this if ldb supports natively aliasing between the rdn and the "cn" attribute */
374 if ( ! samldb_add_attribute(msg2
, "cn", &rdn
[3])) {
380 if ((attribute
= samldb_find_attribute(msg2
, "name", NULL
)) != NULL
) {
381 if (strcasecmp(rdn
, attribute
->values
[0].data
) != 0) {
382 ldb_debug(module
->ldb
, LDB_DEBUG_FATAL
, "samldb_manage_group_object: Bad Attribute Syntax for name\n");
386 } else { /* FIXME: remove this if ldb supports natively aliasing between the rdn and the "name" attribute */
387 if ( ! samldb_add_attribute(msg2
, "name", &rdn
[3])) {
393 if ((attribute
= samldb_find_attribute(msg2
, "objectSid", NULL
)) == NULL
) {
396 if ((sidstr
= samldb_get_new_sid(module
->ldb
, msg2
, msg2
->dn
)) == NULL
) {
397 ldb_debug(module
->ldb
, LDB_DEBUG_FATAL
, "samldb_manage_group_object: internal error! Can't generate new sid\n");
402 if ( ! samldb_add_attribute(msg2
, "objectSid", sidstr
)) {
408 if ( ! samldb_find_or_add_attribute(msg2
, "instanceType", NULL
, "4")) {
412 if ( ! samldb_find_or_add_attribute(msg2
, "sAMAccountName", NULL
, samldb_generate_samAccountName(msg2
))) {
416 if ( ! samldb_find_or_add_attribute(msg2
, "sAMAccountType", NULL
, "268435456")) {
420 if ( ! samldb_find_or_add_attribute(msg2
, "groupType", NULL
, "-2147483646")) {
424 if ( ! samldb_find_or_add_attribute(msg2
, "objectCategory", NULL
, "foo")) { /* keep the schema module happy :) */
428 /* TODO: objectGUID, objectSid, objectCategory */
429 /* need a way to lock a new Sid */
434 static struct ldb_message
*samldb_manage_user_object(struct ldb_module
*module
, const struct ldb_message
*msg
)
436 struct ldb_message
*msg2
;
437 struct ldb_message_element
*attribute
;
441 if (samldb_find_attribute(msg
, "objectclass", "user") == NULL
) {
445 msg2
= talloc(module
, struct ldb_message
);
447 ldb_debug(module
->ldb
, LDB_DEBUG_FATAL
, "samldb_manage_user_object: talloc failed!\n");
451 /* build the new msg */
453 msg2
->num_elements
= msg
->num_elements
;
454 msg2
->private_data
= msg
->private_data
;
455 msg2
->elements
= talloc_array(msg2
, struct ldb_message_element
, msg2
->num_elements
);
456 if (! msg2
->elements
) {
457 ldb_debug(module
->ldb
, LDB_DEBUG_FATAL
, "samldb_manage_user_object: talloc_array failed!\n");
461 for (i
= 0; i
< msg2
->num_elements
; i
++) {
462 msg2
->elements
[i
] = msg
->elements
[i
];
465 if ( ! samldb_get_rdn_and_basedn(msg2
, msg2
->dn
, &rdn
, &basedn
)) {
469 if (strncasecmp(rdn
, "cn", 2) != 0) {
470 ldb_debug(module
->ldb
, LDB_DEBUG_FATAL
, "samldb_manage_group_object: Bad RDN (%s) for group!\n", rdn
);
476 if ( ! samldb_find_or_add_attribute(msg2
, "objectclass", "top", "top")) {
481 if ( ! samldb_find_or_add_attribute(msg2
, "objectclass", "person", "person")) {
486 if ( ! samldb_find_or_add_attribute(msg2
, "objectclass", "organizationalPerson", "organizationalPerson")) {
491 if ((attribute
= samldb_find_attribute(msg2
, "cn", NULL
)) != NULL
) {
492 if (strcasecmp(rdn
, attribute
->values
[0].data
) != 0) {
493 ldb_debug(module
->ldb
, LDB_DEBUG_FATAL
, "samldb_manage_user_object: Bad Attribute Syntax for CN\n");
497 } else { /* FIXME: remove this if ldb supports natively aliasing between the rdn and the "cn" attribute */
498 if ( ! samldb_add_attribute(msg2
, "cn", &rdn
[3])) {
504 if ((attribute
= samldb_find_attribute(msg2
, "name", NULL
)) != NULL
) {
505 if (strcasecmp(rdn
, attribute
->values
[0].data
) != 0) {
506 ldb_debug(module
->ldb
, LDB_DEBUG_FATAL
, "samldb_manage_user_object: Bad Attribute Syntax for name\n");
510 } else { /* FIXME: remove this if ldb supports natively aliasing between the rdn and the "name" attribute */
511 if ( ! samldb_add_attribute(msg2
, "name", &rdn
[3])) {
517 if ((attribute
= samldb_find_attribute(msg2
, "objectSid", NULL
)) == NULL
) {
520 if ((sidstr
= samldb_get_new_sid(module
->ldb
, msg2
, msg2
->dn
)) == NULL
) {
521 ldb_debug(module
->ldb
, LDB_DEBUG_FATAL
, "samldb_manage_user_object: internal error! Can't generate new sid\n");
526 if ( ! samldb_add_attribute(msg2
, "objectSid", sidstr
)) {
532 if ( ! samldb_find_or_add_attribute(msg2
, "instanceType", NULL
, "4")) {
537 if ( ! samldb_find_or_add_attribute(msg2
, "sAMAccountName", NULL
, samldb_generate_samAccountName(msg2
))) {
542 if ( ! samldb_find_or_add_attribute(msg2
, "sAMAccountType", NULL
, "805306368")) {
547 if ( ! samldb_find_or_add_attribute(msg2
, "objectCategory", NULL
, "foo")) { /* keep the schema module happy :) */
551 /* TODO: objectGUID, objectSid, objectCategory, userAccountControl, badPwdCount, codePage, countryCode, badPasswordTime, lastLogoff, lastLogon, pwdLastSet, primaryGroupID, accountExpires, logonCount */
557 static int samldb_add_record(struct ldb_module
*module
, const struct ldb_message
*msg
)
559 struct ldb_message
*msg2
= NULL
;
562 ldb_debug(module
->ldb
, LDB_DEBUG_TRACE
, "samldb_add_record\n");
564 if (msg
->dn
[0] == '@') { /* do not manipulate our control entries */
565 return ldb_next_add_record(module
, msg
);
568 /* is group? add all group relevant missing objects */
569 msg2
= samldb_manage_group_object(module
, msg
);
571 /* is user? add all user relevant missing objects */
573 msg2
= samldb_manage_user_object(module
, msg
);
577 ret
= ldb_next_add_record(module
, msg2
);
580 ret
= ldb_next_add_record(module
, msg
);
586 /* modify_record: change modifyTimestamp as well */
587 static int samldb_modify_record(struct ldb_module
*module
, const struct ldb_message
*msg
)
589 ldb_debug(module
->ldb
, LDB_DEBUG_TRACE
, "samldb_modify_record\n");
590 return ldb_next_modify_record(module
, msg
);
593 static int samldb_delete_record(struct ldb_module
*module
, const char *dn
)
595 ldb_debug(module
->ldb
, LDB_DEBUG_TRACE
, "samldb_delete_record\n");
596 return ldb_next_delete_record(module
, dn
);
599 static int samldb_rename_record(struct ldb_module
*module
, const char *olddn
, const char *newdn
)
601 ldb_debug(module
->ldb
, LDB_DEBUG_TRACE
, "samldb_rename_record\n");
602 return ldb_next_rename_record(module
, olddn
, newdn
);
605 static int samldb_lock(struct ldb_module
*module
, const char *lockname
)
607 ldb_debug(module
->ldb
, LDB_DEBUG_TRACE
, "samldb_lock\n");
608 return ldb_next_named_lock(module
, lockname
);
611 static int samldb_unlock(struct ldb_module
*module
, const char *lockname
)
613 ldb_debug(module
->ldb
, LDB_DEBUG_TRACE
, "samldb_unlock\n");
614 return ldb_next_named_unlock(module
, lockname
);
617 /* return extended error information */
618 static const char *samldb_errstring(struct ldb_module
*module
)
620 struct private_data
*data
= (struct private_data
*)module
->private_data
;
622 ldb_debug(module
->ldb
, LDB_DEBUG_TRACE
, "samldb_errstring\n");
623 if (data
->error_string
) {
626 error
= data
->error_string
;
627 data
->error_string
= NULL
;
631 return ldb_next_errstring(module
);
634 static int samldb_destructor(void *module_ctx
)
636 struct ldb_module
*ctx
= module_ctx
;
637 /* put your clean-up functions here */
641 static const struct ldb_module_ops samldb_ops
= {
646 samldb_modify_record
,
647 samldb_delete_record
,
648 samldb_rename_record
,
655 /* the init function */
656 #ifdef HAVE_DLOPEN_DISABLED
657 struct ldb_module
*init_module(struct ldb_context
*ldb
, const char *options
[])
659 struct ldb_module
*samldb_module_init(struct ldb_context
*ldb
, const char *options
[])
662 struct ldb_module
*ctx
;
663 struct private_data
*data
;
665 ctx
= talloc(ldb
, struct ldb_module
);
669 data
= talloc(ctx
, struct private_data
);
675 data
->error_string
= NULL
;
676 ctx
->private_data
= data
;
678 ctx
->prev
= ctx
->next
= NULL
;
679 ctx
->ops
= &samldb_ops
;
681 talloc_set_destructor(ctx
, samldb_destructor
);