r5588: We currently use a string representing an hex number so conform to that.
[samba.git] / source / dsdb / samdb / ldb_modules / samldb.c
1 /* 
2    ldb database library
3
4    Copyright (C) Simo Sorce  2004
5
6      ** NOTE! The following LGPL license applies to the ldb
7      ** library. This does NOT imply that all of Samba is released
8      ** under the LGPL
9    
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.
14
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.
19
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
23 */
24
25 /*
26  *  Name: ldb
27  *
28  *  Component: ldb samldb module
29  *
30  *  Description: add object timestamping functionality
31  *
32  *  Author: Simo Sorce
33  */
34
35 #include "includes.h"
36 #include "lib/ldb/include/ldb.h"
37 #include "lib/ldb/include/ldb_private.h"
38 #include <time.h>
39
40 #define SAM_ACCOUNT_NAME_BASE "$000000-000000000000"
41
42 struct private_data {
43         const char *error_string;
44 };
45
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)
49 {
50         ldb_debug(module->ldb, LDB_DEBUG_TRACE, "samldb_search\n");
51         return ldb_next_search(module, base, scope, expression, attrs, res);
52 }
53
54 static int samldb_search_free(struct ldb_module *module, struct ldb_message **res)
55 {
56 ldb_debug(module->ldb, LDB_DEBUG_TRACE, "samldb_search_free\n");
57         return ldb_next_search_free(module, res);
58 }
59
60
61 /*
62   allocate a new id, attempting to do it atomically
63   return 0 on failure, the id on success
64 */
65 static int samldb_allocate_next_rid(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
66                                    const char *dn, uint32_t *id)
67 {
68         const char * const attrs[2] = { "nextRid", NULL };
69         struct ldb_message **res = NULL;
70         struct ldb_message msg;
71         int ret;
72         const char *str;
73         struct ldb_val vals[2];
74         struct ldb_message_element els[2];
75
76         ret = ldb_search(ldb, dn, LDB_SCOPE_BASE, "nextRid=*", attrs, &res);
77         if (ret != 1) {
78                 if (res) ldb_search_free(ldb, res);
79                 return -1;
80         }
81         str = ldb_msg_find_string(res[0], "nextRid", NULL);
82         if (str == NULL) {
83                 ldb_debug(ldb, LDB_DEBUG_FATAL, "attribute nextRid not found in %s\n", dn);
84                 ldb_search_free(ldb, res);
85                 return -1;
86         }
87         talloc_steal(mem_ctx, str);
88         ldb_search_free(ldb, res);
89
90         *id = strtol(str, NULL, 0);
91         if ((*id)+1 == 0) {
92                 /* out of IDs ! */
93                 return -1;
94         }
95
96         /* we do a delete and add as a single operation. That prevents
97            a race */
98         ZERO_STRUCT(msg);
99         msg.dn = talloc_strdup(mem_ctx, dn);
100         if (!msg.dn) {
101                 return -1;
102         }
103         msg.num_elements = 2;
104         msg.elements = els;
105
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");
110         if (!els[0].name) {
111                 return -1;
112         }
113
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;
118
119         vals[0].data = talloc_asprintf(mem_ctx, "%u", *id);
120         if (!vals[0].data) {
121                 return -1;
122         }
123         vals[0].length = strlen(vals[0].data);
124
125         vals[1].data = talloc_asprintf(mem_ctx, "%u", (*id)+1);
126         if (!vals[1].data) {
127                 return -1;
128         }
129         vals[1].length = strlen(vals[1].data);
130
131         ret = ldb_modify(ldb, &msg);
132         if (ret != 0) {
133                 return 1;
134         }
135
136         (*id)++;
137
138         return 0;
139 }
140
141 /* search the domain related to the provided dn
142    allocate a new RID for the domain
143    return the new sid string
144 */
145 static char *samldb_get_new_sid(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, const char *obj_dn)
146 {
147         const char * const attrs[2] = { "objectSid", NULL };
148         struct ldb_message **res = NULL;
149         const char *dom_dn, *dom_sid;
150         char *obj_sid;
151         uint32_t rid;
152         int ret, tries = 10;
153
154         /* get the domain component part of the provided dn */
155
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 */
158
159         /* FIXME: "dc=" is probably not utf8 safe either,
160            we need a multibyte safe substring search function here */
161         
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);
165                 return NULL;
166         }
167
168         /* find the domain sid */
169
170         ret = ldb_search(ldb, dom_dn, LDB_SCOPE_BASE, "objectSid=*", attrs, &res);
171         if (ret != 1) {
172                 ldb_debug(ldb, LDB_DEBUG_FATAL, "samldb_get_new_sid: error retrieving domain sid!\n");
173                 if (res) ldb_search_free(ldb, res);
174                 return NULL;
175         }
176
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);
181                 return NULL;
182         }
183
184         talloc_steal(mem_ctx, dom_sid);
185         ldb_search_free(ldb, res);
186
187         /* allocate a new Rid for the domain */
188
189
190         /* we need to try multiple times to cope with two account
191            creations at the same time */
192         while (tries--) {
193                 ret = samldb_allocate_next_rid(ldb, mem_ctx, dom_dn, &rid);
194                 if (ret != 1) {
195                         break;
196                 }
197         }
198         if (ret != 0) {
199                 ldb_debug(ldb, LDB_DEBUG_FATAL, "Failed to increment nextRid of %s\n", dom_dn);
200                 return NULL;
201         }
202
203         /* return the new object sid */
204
205         obj_sid = talloc_asprintf(mem_ctx, "%s-%u", dom_sid, rid);
206
207         return obj_sid;
208 }
209
210 static char *samldb_generate_samAccountName(const void *mem_ctx) {
211         char *name;
212
213         name = talloc_strdup(mem_ctx, SAM_ACCOUNT_NAME_BASE);
214         /* TODO: randomize name */      
215
216         return name;
217 }
218
219 static BOOL samldb_get_rdn_and_basedn(const void *mem_ctx, const char *dn, char **rdn, char **basedn)
220 {
221         char *p;
222
223         p = strchr(dn, ',');
224         if ( ! p ) {
225                 return False;
226         }
227         /* clear separator */
228         *p = '\0';
229
230         *rdn = talloc_strdup(mem_ctx, dn);
231
232         /* put back separator */
233         *p = ',';
234
235         if ( ! *rdn) {
236                 return False;
237         }
238
239         *basedn = talloc_strdup(mem_ctx, p + 1);
240
241         if ( ! *basedn) {
242                 talloc_free(*rdn);
243                 *rdn = NULL;
244                 return False;
245         }
246
247         return True;
248 }
249
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)
252 {
253         int i, j;
254
255         for (i = 0; i < msg->num_elements; i++) {
256                 if (ldb_attr_cmp(name, msg->elements[i].name) == 0) {
257                         if (!value) {
258                                 return &msg->elements[i];
259                         }
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];
263                                 }
264                         }
265                 }
266         }
267
268         return NULL;
269 }
270
271 static BOOL samldb_add_attribute(struct ldb_message *msg, const char *name, const char *value)
272 {
273         struct ldb_message_element *attr;
274         int i;
275
276         attr = samldb_find_attribute(msg, name, NULL);
277         if ( ! attr) {
278                 msg->num_elements++;
279                 msg->elements = talloc_realloc(msg, msg->elements, struct ldb_message_element, msg->num_elements);
280                 if ( ! msg->elements ) {
281                         return False;
282                 }
283                 attr = &msg->elements[msg->num_elements - 1];
284
285                 attr->name = talloc_strdup(msg, name);
286                 if ( ! attr->name ) {
287                         return False;
288                 }
289                 attr->flags = 0;
290                 attr->num_values = 0;
291                 attr->values = NULL;
292         }
293
294         i = attr->num_values;
295         attr->num_values++;
296         attr->values = talloc_realloc(msg, attr->values, struct ldb_val, attr->num_values);
297         if ( ! attr->values ){
298                 return False;
299         }
300
301         attr->values[i].data = talloc_strdup(msg, value);
302         attr->values[i].length = strlen(value);
303
304         if ( ! attr->values[i].data) {
305                 return False;
306         }
307
308         return True;
309 }
310
311 static BOOL samldb_find_or_add_attribute(struct ldb_message *msg, const char *name, const char *value, const char *set_value)
312 {
313         if (samldb_find_attribute(msg, name, value) == NULL) {
314                 if ( ! samldb_add_attribute(msg, name, set_value)) {
315                         return False;
316                 }
317         }
318         return True;
319 }
320
321 static struct ldb_message *samldb_manage_group_object(struct ldb_module *module, const struct ldb_message *msg)
322 {
323         struct ldb_message *msg2;
324         struct ldb_message_element *attribute;
325         char *rdn, *basedn;
326         int i;
327
328         if (samldb_find_attribute(msg, "objectclass", "group") == NULL) {
329                 return NULL;
330         }
331
332         msg2 = talloc(module, struct ldb_message);
333         if (!msg2) {
334                 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_manage_group_object: talloc failed!\n");
335                 return NULL;
336         }
337
338         /* build the new msg */
339         msg2->dn = msg->dn;
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");
345                 talloc_free(msg2);
346                 return NULL;
347         }
348         for (i = 0; i < msg2->num_elements; i++) {
349                 msg2->elements[i] = msg->elements[i];
350         }
351
352         if ( ! samldb_get_rdn_and_basedn(msg2, msg2->dn, &rdn, &basedn)) {
353                 talloc_free(msg2);
354                 return NULL;
355         }
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);
358                 talloc_free(msg2);
359                 return NULL;
360         }
361
362         if (! samldb_find_or_add_attribute(msg2, "objectclass", "top", "top")) {
363                 talloc_free(msg2);
364                 return NULL;
365         }
366
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");
370                         talloc_free(msg2);
371                         return NULL;
372                 }
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])) {
375                         talloc_free(msg2);
376                         return NULL;
377                 }
378         }
379
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");
383                         talloc_free(msg2);
384                         return NULL;
385                 }
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])) {
388                         talloc_free(msg2);
389                         return NULL;
390                 }
391         }
392
393         if ((attribute = samldb_find_attribute(msg2, "objectSid", NULL)) == NULL ) {
394                 char *sidstr;
395
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");
398                         talloc_free(msg2);
399                         return NULL;
400                 }
401                 
402                 if ( ! samldb_add_attribute(msg2, "objectSid", sidstr)) {
403                         talloc_free(msg2);
404                         return NULL;
405                 }
406         }
407
408         if ( ! samldb_find_or_add_attribute(msg2, "instanceType", NULL, "4")) {
409                 return NULL;
410         }
411
412         if ( ! samldb_find_or_add_attribute(msg2, "sAMAccountName", NULL, samldb_generate_samAccountName(msg2))) {
413                 return NULL;
414         }
415
416         if ( ! samldb_find_or_add_attribute(msg2, "sAMAccountType", NULL, "0x10000000")) {
417                 return NULL;
418         }
419
420         if ( ! samldb_find_or_add_attribute(msg2, "groupType", NULL, "0x80000002")) {
421                 return NULL;
422         }
423
424         if ( ! samldb_find_or_add_attribute(msg2, "objectCategory", NULL, "foo")) { /* keep the schema module happy :) */
425                 return NULL;
426         }
427
428         /* TODO: objectGUID, objectSid, objectCategory */
429         /* need a way to lock a new Sid */
430
431         return msg2;
432 }
433
434 static struct ldb_message *samldb_manage_user_object(struct ldb_module *module, const struct ldb_message *msg)
435 {
436         struct ldb_message *msg2;
437         struct ldb_message_element *attribute;
438         char *rdn, *basedn;
439         int i;
440
441         if (samldb_find_attribute(msg, "objectclass", "user") == NULL) {
442                 return NULL;
443         }
444
445         msg2 = talloc(module, struct ldb_message);
446         if (!msg2) {
447                 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_manage_user_object: talloc failed!\n");
448                 return NULL;
449         }
450
451         /* build the new msg */
452         msg2->dn = msg->dn;
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");
458                 talloc_free(msg2);
459                 return NULL;
460         }
461         for (i = 0; i < msg2->num_elements; i++) {
462                 msg2->elements[i] = msg->elements[i];
463         }
464
465         if ( ! samldb_get_rdn_and_basedn(msg2, msg2->dn, &rdn, &basedn)) {
466                 talloc_free(msg2);
467                 return NULL;
468         }
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);
471                 talloc_free(msg2);
472                 return NULL;
473         }
474
475
476         if ( ! samldb_find_or_add_attribute(msg2, "objectclass", "top", "top")) {
477                 talloc_free(msg2);
478                 return NULL;
479         }
480
481         if ( ! samldb_find_or_add_attribute(msg2, "objectclass", "person", "person")) {
482                 talloc_free(msg2);
483                 return NULL;
484         }
485
486         if ( ! samldb_find_or_add_attribute(msg2, "objectclass", "organizationalPerson", "organizationalPerson")) {
487                 talloc_free(msg2);
488                 return NULL;
489         }
490
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");
494                         talloc_free(msg2);
495                         return NULL;
496                 }
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])) {
499                         talloc_free(msg2);
500                         return NULL;
501                 }
502         }
503
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");
507                         talloc_free(msg2);
508                         return NULL;
509                 }
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])) {
512                         talloc_free(msg2);
513                         return NULL;
514                 }
515         }
516
517         if ((attribute = samldb_find_attribute(msg2, "objectSid", NULL)) == NULL ) {
518                 char *sidstr;
519
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");
522                         talloc_free(msg2);
523                         return NULL;
524                 }
525                 
526                 if ( ! samldb_add_attribute(msg2, "objectSid", sidstr)) {
527                         talloc_free(msg2);
528                         return NULL;
529                 }
530         }
531
532         if ( ! samldb_find_or_add_attribute(msg2, "instanceType", NULL, "4")) {
533                 talloc_free(msg2);
534                 return NULL;
535         }
536
537         if ( ! samldb_find_or_add_attribute(msg2, "sAMAccountName", NULL, samldb_generate_samAccountName(msg2))) {
538                 talloc_free(msg2);
539                 return NULL;
540         }
541
542         if ( ! samldb_find_or_add_attribute(msg2, "sAMAccountType", NULL, "0x30000000")) {
543                 talloc_free(msg2);
544                 return NULL;
545         }
546
547         if ( ! samldb_find_or_add_attribute(msg2, "objectCategory", NULL, "foo")) { /* keep the schema module happy :) */
548                 return NULL;
549         }
550
551         /* TODO: objectGUID, objectSid, objectCategory, userAccountControl, badPwdCount, codePage, countryCode, badPasswordTime, lastLogoff, lastLogon, pwdLastSet, primaryGroupID, accountExpires, logonCount */
552
553         return msg2;
554 }
555
556 /* add_record */
557 static int samldb_add_record(struct ldb_module *module, const struct ldb_message *msg)
558 {
559         struct ldb_message *msg2 = NULL;
560         int ret;
561
562         ldb_debug(module->ldb, LDB_DEBUG_TRACE, "samldb_add_record\n");
563
564         if (msg->dn[0] == '@') { /* do not manipulate our control entries */
565                 return ldb_next_add_record(module, msg);
566         }
567
568         /* is group?  add all group relevant missing objects */
569         msg2 = samldb_manage_group_object(module, msg);
570
571         /* is user? add all user relevant missing objects */
572         if ( ! msg2 ) {
573                 msg2 = samldb_manage_user_object(module, msg);
574         }
575
576         if (msg2) {
577                 ret = ldb_next_add_record(module, msg2);
578                 talloc_free(msg2);
579         } else {
580                 ret = ldb_next_add_record(module, msg);
581         }
582
583         return ret;
584 }
585
586 /* modify_record: change modifyTimestamp as well */
587 static int samldb_modify_record(struct ldb_module *module, const struct ldb_message *msg)
588 {
589         ldb_debug(module->ldb, LDB_DEBUG_TRACE, "samldb_modify_record\n");
590         return ldb_next_modify_record(module, msg);
591 }
592
593 static int samldb_delete_record(struct ldb_module *module, const char *dn)
594 {
595         ldb_debug(module->ldb, LDB_DEBUG_TRACE, "samldb_delete_record\n");
596         return ldb_next_delete_record(module, dn);
597 }
598
599 static int samldb_rename_record(struct ldb_module *module, const char *olddn, const char *newdn)
600 {
601         ldb_debug(module->ldb, LDB_DEBUG_TRACE, "samldb_rename_record\n");
602         return ldb_next_rename_record(module, olddn, newdn);
603 }
604
605 static int samldb_lock(struct ldb_module *module, const char *lockname)
606 {
607         ldb_debug(module->ldb, LDB_DEBUG_TRACE, "samldb_lock\n");
608         return ldb_next_named_lock(module, lockname);
609 }
610
611 static int samldb_unlock(struct ldb_module *module, const char *lockname)
612 {
613         ldb_debug(module->ldb, LDB_DEBUG_TRACE, "samldb_unlock\n");
614         return ldb_next_named_unlock(module, lockname);
615 }
616
617 /* return extended error information */
618 static const char *samldb_errstring(struct ldb_module *module)
619 {
620         struct private_data *data = (struct private_data *)module->private_data;
621
622         ldb_debug(module->ldb, LDB_DEBUG_TRACE, "samldb_errstring\n");
623         if (data->error_string) {
624                 const char *error;
625
626                 error = data->error_string;
627                 data->error_string = NULL;
628                 return error;
629         }
630
631         return ldb_next_errstring(module);
632 }
633
634 static int samldb_destructor(void *module_ctx)
635 {
636         struct ldb_module *ctx = module_ctx;
637         /* put your clean-up functions here */
638         return 0;
639 }
640
641 static const struct ldb_module_ops samldb_ops = {
642         "samldb",
643         samldb_search,
644         samldb_search_free,
645         samldb_add_record,
646         samldb_modify_record,
647         samldb_delete_record,
648         samldb_rename_record,
649         samldb_lock,
650         samldb_unlock,
651         samldb_errstring
652 };
653
654
655 /* the init function */
656 #ifdef HAVE_DLOPEN_DISABLED
657  struct ldb_module *init_module(struct ldb_context *ldb, const char *options[])
658 #else
659 struct ldb_module *samldb_module_init(struct ldb_context *ldb, const char *options[])
660 #endif
661 {
662         struct ldb_module *ctx;
663         struct private_data *data;
664
665         ctx = talloc(ldb, struct ldb_module);
666         if (!ctx)
667                 return NULL;
668
669         data = talloc(ctx, struct private_data);
670         if (!data) {
671                 talloc_free(ctx);
672                 return NULL;
673         }
674
675         data->error_string = NULL;
676         ctx->private_data = data;
677         ctx->ldb = ldb;
678         ctx->prev = ctx->next = NULL;
679         ctx->ops = &samldb_ops;
680
681         talloc_set_destructor(ctx, samldb_destructor);
682
683         return ctx;
684 }