7ec1ea1a296dfe18cd287c57a2bda977bd2c0a10
[samba.git] / source / dsdb / samdb / ldb_modules / samldb.c
1 /* 
2    SAM ldb module
3
4    Copyright (C) Simo Sorce  2004
5
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
8    * later license.
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 2 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, write to the Free Software
22    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 */
24
25 /*
26  *  Name: ldb
27  *
28  *  Component: ldb samldb module
29  *
30  *  Description: add embedded user/group creation 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   allocate a new id, attempting to do it atomically
62   return 0 on failure, the id on success
63 */
64 static int samldb_allocate_next_rid(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
65                                    const char *dn, uint32_t *id)
66 {
67         const char * const attrs[2] = { "nextRid", NULL };
68         struct ldb_message **res = NULL;
69         struct ldb_message msg;
70         int ret;
71         const char *str;
72         struct ldb_val vals[2];
73         struct ldb_message_element els[2];
74
75         ret = ldb_search(ldb, dn, LDB_SCOPE_BASE, "nextRid=*", attrs, &res);
76         if (ret != 1) {
77                 if (res) ldb_search_free(ldb, res);
78                 return -1;
79         }
80         str = ldb_msg_find_string(res[0], "nextRid", NULL);
81         if (str == NULL) {
82                 ldb_debug(ldb, LDB_DEBUG_FATAL, "attribute nextRid not found in %s\n", dn);
83                 ldb_search_free(ldb, res);
84                 return -1;
85         }
86
87         *id = strtol(str, NULL, 0);
88         if ((*id)+1 == 0) {
89                 /* out of IDs ! */
90                 ldb_debug(ldb, LDB_DEBUG_FATAL, "Are we out of valid IDs ?\n");
91                 ldb_search_free(ldb, res);
92                 return -1;
93         }
94         ldb_search_free(ldb, res);
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 static char *samldb_search_domain(struct ldb_module *module, TALLOC_CTX *mem_ctx, const char *dn)
142 {
143         const char *sdn;
144         struct ldb_message **res = NULL;
145         int ret;
146
147         sdn = dn;
148         while ((sdn = strchr(sdn, ',')) != NULL) {
149
150                 sdn++;
151
152                 ret = ldb_search(module->ldb, sdn, LDB_SCOPE_BASE, "objectClass=domain", NULL, &res);
153                 ldb_search_free(module->ldb, res);
154
155                 if (ret == 1)
156                         break;
157         }
158
159         if (ret != 1) {
160                 return NULL;
161         }
162
163         return talloc_strdup(mem_ctx, sdn);
164 }
165
166 /* search the domain related to the provided dn
167    allocate a new RID for the domain
168    return the new sid string
169 */
170 static char *samldb_get_new_sid(struct ldb_module *module, TALLOC_CTX *mem_ctx, const char *obj_dn)
171 {
172         const char * const attrs[2] = { "objectSid", NULL };
173         struct ldb_message **res = NULL;
174         const char *dom_dn, *dom_sid;
175         char *obj_sid;
176         uint32_t rid;
177         int ret, tries = 10;
178
179         /* get the domain component part of the provided dn */
180
181         /* FIXME: quick search here, I think we should use something like
182            ldap_parse_dn here to be 100% sure we get the right domain dn */
183
184         /* FIXME: "dc=" is probably not utf8 safe either,
185            we need a multibyte safe substring search function here */
186         
187         dom_dn = samldb_search_domain(module, mem_ctx, obj_dn);
188         if (dom_dn == NULL) {
189                 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "Invalid dn (%s) not child of a domain object!\n", obj_dn);
190                 return NULL;
191         }
192
193         /* find the domain sid */
194
195         ret = ldb_search(module->ldb, dom_dn, LDB_SCOPE_BASE, "objectSid=*", attrs, &res);
196         if (ret != 1) {
197                 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_get_new_sid: error retrieving domain sid!\n");
198                 if (res) ldb_search_free(module->ldb, res);
199                 return NULL;
200         }
201
202         dom_sid = ldb_msg_find_string(res[0], "objectSid", NULL);
203         if (dom_sid == NULL) {
204                 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_get_new_sid: error retrieving domain sid!\n");
205                 ldb_search_free(module->ldb, res);
206                 return NULL;
207         }
208
209         /* allocate a new Rid for the domain */
210
211         /* we need to try multiple times to cope with two account
212            creations at the same time */
213         while (tries--) {
214                 ret = samldb_allocate_next_rid(module->ldb, mem_ctx, dom_dn, &rid);
215                 if (ret != 1) {
216                         break;
217                 }
218         }
219         if (ret != 0) {
220                 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "Failed to increment nextRid of %s\n", dom_dn);
221                 ldb_search_free(module->ldb, res);
222                 return NULL;
223         }
224
225         /* return the new object sid */
226
227         obj_sid = talloc_asprintf(mem_ctx, "%s-%u", dom_sid, rid);
228
229         ldb_search_free(module->ldb, res);
230
231         return obj_sid;
232 }
233
234 static char *samldb_generate_samAccountName(const void *mem_ctx) {
235         char *name;
236
237         name = talloc_strdup(mem_ctx, SAM_ACCOUNT_NAME_BASE);
238         /* TODO: randomize name */      
239
240         return name;
241 }
242
243 static BOOL samldb_get_rdn_and_basedn(const void *mem_ctx, const char *dn, char **rdn, char **basedn)
244 {
245         char *p;
246
247         p = strchr(dn, ',');
248         if ( ! p ) {
249                 return False;
250         }
251
252         *rdn = talloc_strndup(mem_ctx, dn, p - dn);
253
254         if ( ! *rdn) {
255                 return False;
256         }
257
258         *basedn = talloc_strdup(mem_ctx, p + 1);
259
260         if ( ! *basedn) {
261                 talloc_free(*rdn);
262                 *rdn = NULL;
263                 return False;
264         }
265
266         return True;
267 }
268
269 /* if value is not null also check for attribute to have exactly that value */
270 static struct ldb_message_element *samldb_find_attribute(const struct ldb_message *msg, const char *name, const char *value)
271 {
272         int i, j;
273
274         for (i = 0; i < msg->num_elements; i++) {
275                 if (ldb_attr_cmp(name, msg->elements[i].name) == 0) {
276                         if (!value) {
277                                 return &msg->elements[i];
278                         }
279                         for (j = 0; j < msg->elements[i].num_values; j++) {
280                                 if (strcasecmp(value, msg->elements[i].values[j].data) == 0) {
281                                         return &msg->elements[i];
282                                 }
283                         }
284                 }
285         }
286
287         return NULL;
288 }
289
290 static BOOL samldb_msg_add_string(struct ldb_module *module, struct ldb_message *msg, const char *name, const char *value)
291 {
292         char *aname = talloc_strdup(msg, name);
293         char *aval = talloc_strdup(msg, value);
294
295         if (aname == NULL || aval == NULL) {
296                 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_msg_add_string: talloc_strdup failed!\n");
297                 return False;
298         }
299
300         if (ldb_msg_add_string(module->ldb, msg, aname, aval) != 0) {
301                 return False;
302         }
303
304         return True;
305 }
306
307 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)
308 {
309         if (samldb_find_attribute(msg, name, value) == NULL) {
310                 return samldb_msg_add_string(module, msg, name, set_value);
311         }
312         return True;
313 }
314
315 static int samldb_copy_template(struct ldb_module *module, struct ldb_message *msg, const char *filter)
316 {
317         struct ldb_message **res, *t;
318         int ret, i, j;
319         
320
321         /* pull the template record */
322         ret = ldb_search(module->ldb, NULL, LDB_SCOPE_SUBTREE, filter, NULL, &res);
323         if (ret != 1) {
324                 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb: ERROR: template '%s' matched %d records\n", filter, ret);
325                 return -1;
326         }
327         t = res[0];
328
329         for (i = 0; i < t->num_elements; i++) {
330                 struct ldb_message_element *el = &t->elements[i];
331                 /* some elements should not be copied from the template */
332                 if (strcasecmp(el->name, "cn") == 0 ||
333                     strcasecmp(el->name, "name") == 0 ||
334                     strcasecmp(el->name, "sAMAccountName") == 0) {
335                         continue;
336                 }
337                 for (j = 0; j < el->num_values; j++) {
338                         if (strcasecmp(el->name, "objectClass") == 0 &&
339                             (strcasecmp((char *)el->values[j].data, "Template") == 0 ||
340                              strcasecmp((char *)el->values[j].data, "userTemplate") == 0 ||
341                              strcasecmp((char *)el->values[j].data, "groupTemplate") == 0 ||
342                              strcasecmp((char *)el->values[j].data, "foreignSecurityTemplate") == 0 ||
343                              strcasecmp((char *)el->values[j].data, "aliasTemplate") == 0 || 
344                              strcasecmp((char *)el->values[j].data, "trustedDomainTemplate") == 0 || 
345                              strcasecmp((char *)el->values[j].data, "secretTemplate") == 0)) {
346                                 continue;
347                         }
348                         if ( ! samldb_find_or_add_attribute(module, msg, el->name, 
349                                                             NULL,
350                                                             (char *)el->values[j].data)) {
351                                 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "Attribute adding failed...\n");
352                                 ldb_search_free(module->ldb, res);
353                                 return -1;
354                         }
355                 }
356         }
357
358         ldb_search_free(module->ldb, res);
359
360         return 0;
361 }
362
363 static struct ldb_message *samldb_fill_group_object(struct ldb_module *module, const struct ldb_message *msg)
364 {
365         struct ldb_message *msg2;
366         struct ldb_message_element *attribute;
367         char *rdn, *basedn, *sidstr;
368
369         if (samldb_find_attribute(msg, "objectclass", "group") == NULL) {
370                 return NULL;
371         }
372
373         ldb_debug(module->ldb, LDB_DEBUG_TRACE, "samldb_fill_group_object\n");
374
375         /* build the new msg */
376         msg2 = ldb_msg_copy(module->ldb, msg);
377         if (!msg2) {
378                 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_fill_group_object: ldb_msg_copy failed!\n");
379                 return NULL;
380         }
381
382         if (samldb_copy_template(module, msg2, "(&(name=TemplateGroup)(objectclass=groupTemplate))") != 0) {
383                 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_fill_group_object: Error copying template!\n");
384                 return NULL;
385         }
386
387         if ( ! samldb_get_rdn_and_basedn(msg2, msg2->dn, &rdn, &basedn)) {
388                 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_fill_group_object: Bad DN (%s)!\n", msg2->dn);
389                 return NULL;
390         }
391         if (strncasecmp(rdn, "cn", 2) != 0) {
392                 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_fill_group_object: Bad RDN (%s) for group!\n", rdn);
393                 return NULL;
394         }
395
396         if ((attribute = samldb_find_attribute(msg2, "cn", NULL)) != NULL) {
397                 if (strcasecmp(&rdn[3], attribute->values[0].data) != 0) {
398                         ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_fill_group_object: Bad Attribute Syntax for CN\n");
399                         return NULL;
400                 }
401         } else { /* FIXME: remove this if ldb supports natively aliasing between the rdn and the "cn" attribute */
402                 if ( ! samldb_msg_add_string(module, msg2, "cn", &rdn[3])) {
403                         return NULL;
404                 }
405         }
406
407         if ((attribute = samldb_find_attribute(msg2, "name", NULL)) != NULL) {
408                 if (strcasecmp(&rdn[3], attribute->values[0].data) != 0) {
409                         return NULL;
410                 }
411         } else { /* FIXME: remove this if ldb supports natively aliasing between the rdn and the "name" attribute */
412                 if ( ! samldb_msg_add_string(module, msg2, "name", &rdn[3])) {
413                         return NULL;
414                 }
415         }
416
417         if ((attribute = samldb_find_attribute(msg2, "objectSid", NULL)) == NULL ) {
418
419                 if ((sidstr = samldb_get_new_sid(module, msg2, msg2->dn)) == NULL) {
420                         ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_fill_group_object: internal error! Can't generate new sid\n");
421                         return NULL;
422                 }
423
424                 if ( ! samldb_msg_add_string(module, msg2, "objectSid", sidstr)) {
425                         return NULL;
426                 }
427         }
428
429         if ( ! samldb_find_or_add_attribute(module, msg2, "sAMAccountName", NULL, samldb_generate_samAccountName(msg2))) {
430                 return NULL;
431         }
432
433         /* TODO: objectGUID */
434
435         talloc_steal(msg, msg2);
436
437         return msg2;
438 }
439
440 static struct ldb_message *samldb_fill_user_or_computer_object(struct ldb_module *module, const struct ldb_message *msg)
441 {
442         struct ldb_message *msg2;
443         struct ldb_message_element *attribute;
444         char *rdn, *basedn, *sidstr;
445
446         if ((samldb_find_attribute(msg, "objectclass", "user") == NULL) && (samldb_find_attribute(msg, "objectclass", "computer") == NULL)) {
447                 return NULL;
448         }
449
450         ldb_debug(module->ldb, LDB_DEBUG_TRACE, "samldb_fill_user_or_computer_object\n");
451
452         /* build the new msg */
453         msg2 = ldb_msg_copy(module->ldb, msg);
454         if (!msg2) {
455                 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_fill_group_object: ldb_msg_copy failed!\n");
456                 return NULL;
457         }
458
459         if (samldb_copy_template(module, msg2, "(&(name=TemplateUser)(objectclass=userTemplate))") != 0) {
460                 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_fill_user_or_computer_object: Error copying template!\n");
461                 return NULL;
462         }
463
464         if ( ! samldb_get_rdn_and_basedn(msg2, msg2->dn, &rdn, &basedn)) {
465                 return NULL;
466         }
467         if (strncasecmp(rdn, "cn", 2) != 0) {
468                 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_fill_user_or_computer_object: Bad RDN (%s) for group!\n", rdn);
469                 return NULL;
470         }
471
472         /* if the only attribute was: "objectclass: computer", then make sure we also add "user" objectclass */
473         if ( ! samldb_find_or_add_attribute(module, msg2, "objectclass", "user", "user")) {
474                 return NULL;
475         }
476
477         if ((attribute = samldb_find_attribute(msg2, "cn", NULL)) != NULL) {
478                 if (strcasecmp(&rdn[3], attribute->values[0].data) != 0) {
479                         ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_fill_user_or_computer_object: Bad Attribute Syntax for CN\n");
480                         return NULL;
481                 }
482         } else { /* FIXME: remove this if ldb supports natively aliasing between the rdn and the "cn" attribute */
483                 if ( ! samldb_msg_add_string(module, msg2, "cn", &rdn[3])) {
484                         return NULL;
485                 }
486         }
487
488         if ((attribute = samldb_find_attribute(msg2, "name", NULL)) != NULL) {
489                 if (strcasecmp(&rdn[3], attribute->values[0].data) != 0) {
490                         ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_fill_user_or_computer_object: Bad Attribute Syntax for name\n");
491                         return NULL;
492                 }
493         } else { /* FIXME: remove this if ldb supports natively aliasing between the rdn and the "name" attribute */
494                 if ( ! samldb_msg_add_string(module, msg2, "name", &rdn[3])) {
495                         return NULL;
496                 }
497         }
498
499         if ((attribute = samldb_find_attribute(msg2, "objectSid", NULL)) == NULL ) {
500
501                 if ((sidstr = samldb_get_new_sid(module, msg2, msg2->dn)) == NULL) {
502                         ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_fill_user_or_computer_object: internal error! Can't generate new sid\n");
503                         return NULL;
504                 }
505
506                 if ( ! samldb_msg_add_string(module, msg2, "objectSid", sidstr)) {
507                         return NULL;
508                 }
509         }
510
511         if ( ! samldb_find_or_add_attribute(module, msg2, "sAMAccountName", NULL, samldb_generate_samAccountName(msg2))) {
512                 return NULL;
513         }
514
515         /* TODO: objectGUID, objectCategory, userAccountControl, badPwdCount, codePage, countryCode, badPasswordTime, lastLogoff, lastLogon, pwdLastSet, primaryGroupID, accountExpires, logonCount */
516
517         talloc_steal(msg, msg2);
518
519         return msg2;
520 }
521
522 /* add_record */
523 static int samldb_add_record(struct ldb_module *module, const struct ldb_message *msg)
524 {
525         struct ldb_message *msg2 = NULL;
526         int ret;
527
528         ldb_debug(module->ldb, LDB_DEBUG_TRACE, "samldb_add_record\n");
529
530         if (msg->dn[0] == '@') { /* do not manipulate our control entries */
531                 return ldb_next_add_record(module, msg);
532         }
533
534         /* is user or computer?  add all relevant missing objects */
535         msg2 = samldb_fill_user_or_computer_object(module, msg);
536
537         /* is group? add all relevant missing objects */
538         if ( ! msg2 ) {
539                 msg2 = samldb_fill_group_object(module, msg);
540         }
541
542         if (msg2) {
543                 ret = ldb_next_add_record(module, msg2);
544         } else {
545                 ret = ldb_next_add_record(module, msg);
546         }
547
548         return ret;
549 }
550
551 /* modify_record: change modifyTimestamp as well */
552 static int samldb_modify_record(struct ldb_module *module, const struct ldb_message *msg)
553 {
554         ldb_debug(module->ldb, LDB_DEBUG_TRACE, "samldb_modify_record\n");
555         return ldb_next_modify_record(module, msg);
556 }
557
558 static int samldb_delete_record(struct ldb_module *module, const char *dn)
559 {
560         ldb_debug(module->ldb, LDB_DEBUG_TRACE, "samldb_delete_record\n");
561         return ldb_next_delete_record(module, dn);
562 }
563
564 static int samldb_rename_record(struct ldb_module *module, const char *olddn, const char *newdn)
565 {
566         ldb_debug(module->ldb, LDB_DEBUG_TRACE, "samldb_rename_record\n");
567         return ldb_next_rename_record(module, olddn, newdn);
568 }
569
570 static int samldb_lock(struct ldb_module *module, const char *lockname)
571 {
572         ldb_debug(module->ldb, LDB_DEBUG_TRACE, "samldb_lock\n");
573         return ldb_next_named_lock(module, lockname);
574 }
575
576 static int samldb_unlock(struct ldb_module *module, const char *lockname)
577 {
578         ldb_debug(module->ldb, LDB_DEBUG_TRACE, "samldb_unlock\n");
579         return ldb_next_named_unlock(module, lockname);
580 }
581
582 /* return extended error information */
583 static const char *samldb_errstring(struct ldb_module *module)
584 {
585         struct private_data *data = (struct private_data *)module->private_data;
586
587         ldb_debug(module->ldb, LDB_DEBUG_TRACE, "samldb_errstring\n");
588         if (data->error_string) {
589                 const char *error;
590
591                 error = data->error_string;
592                 data->error_string = NULL;
593                 return error;
594         }
595
596         return ldb_next_errstring(module);
597 }
598
599 static int samldb_destructor(void *module_ctx)
600 {
601         /* struct ldb_module *ctx = module_ctx; */
602         /* put your clean-up functions here */
603         return 0;
604 }
605
606 static const struct ldb_module_ops samldb_ops = {
607         "samldb",
608         samldb_search,
609         samldb_search_free,
610         samldb_add_record,
611         samldb_modify_record,
612         samldb_delete_record,
613         samldb_rename_record,
614         samldb_lock,
615         samldb_unlock,
616         samldb_errstring
617 };
618
619
620 /* the init function */
621 #ifdef HAVE_DLOPEN_DISABLED
622  struct ldb_module *init_module(struct ldb_context *ldb, const char *options[])
623 #else
624 struct ldb_module *samldb_module_init(struct ldb_context *ldb, const char *options[])
625 #endif
626 {
627         struct ldb_module *ctx;
628         struct private_data *data;
629
630         ctx = talloc(ldb, struct ldb_module);
631         if (!ctx)
632                 return NULL;
633
634         data = talloc(ctx, struct private_data);
635         if (!data) {
636                 talloc_free(ctx);
637                 return NULL;
638         }
639
640         data->error_string = NULL;
641         ctx->private_data = data;
642         ctx->ldb = ldb;
643         ctx->prev = ctx->next = NULL;
644         ctx->ops = &samldb_ops;
645
646         talloc_set_destructor(ctx, samldb_destructor);
647
648         return ctx;
649 }