r2876: - more than one rootDSE entry in the database is an error!
[gd/samba-autobuild/.git] / source4 / ldap_server / ldap_rootdse.c
1 /* 
2    Unix SMB/CIFS implementation.
3    LDAP server ROOT DSE
4    Copyright (C) Stefan Metzmacher 2004
5    
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include "includes.h"
22
23 #define ATTR_BLOB_CONST(val) data_blob_talloc(mem_ctx, val, sizeof(val)-1)
24 #define ATTR_SINGLE_NOVAL(ctx, attr, blob, num, nam) do { \
25         attr->name = talloc_strdup(ctx, nam);\
26         if (!attr->name) {\
27                 return NT_STATUS_NO_MEMORY;\
28         }\
29         attr->num_values = num; \
30         attr->values = blob;\
31 } while(0)
32 #define ALLOC_CHECK(ptr) do {\
33         if (!(ptr)) {\
34                 return NT_STATUS_NO_MEMORY;\
35         }\
36 } while(0)
37
38
39 struct rootdse_db_context {
40         struct ldb_context *ldb;
41         struct rootdse_db_context **static_ptr;
42 };
43
44 /*
45   this is used to catch debug messages from ldb
46 */
47 void rootdse_db_debug(void *context, enum ldb_debug_level level, const char *fmt, va_list ap)  _PRINTF_ATTRIBUTE(3,0)
48 {
49         char *s = NULL;
50         if (DEBUGLEVEL < 4 && level > LDB_DEBUG_WARNING) {
51                 return;
52         }
53         vasprintf(&s, fmt, ap);
54         if (!s) return;
55         DEBUG(level, ("rootdse: %s\n", s));
56         free(s);
57 }
58
59
60 /* destroy the last connection to the sam */
61 static int rootdse_db_destructor(void *ctx)
62 {
63         struct rootdse_db_context *rd_ctx = ctx;
64         ldb_close(rd_ctx->ldb);
65         *(rd_ctx->static_ptr) = NULL;
66         return 0;
67 }                                
68
69 /*
70   connect to the SAM database
71   return an opaque context pointer on success, or NULL on failure
72  */
73 void *rootdse_db_connect(TALLOC_CTX *mem_ctx)
74 {
75         static struct rootdse_db_context *ctx;
76         char *db_path;
77         /*
78           the way that unix fcntl locking works forces us to have a
79           static ldb handle here rather than a much more sensible
80           approach of having the ldb handle as part of the
81           ldap base structures. Otherwise we would try to open
82           the ldb more than once, and tdb would rightly refuse the
83           second open due to the broken nature of unix locking.
84         */
85         if (ctx != NULL) {
86                 return talloc_reference(mem_ctx, ctx);
87         }
88
89         ctx = talloc_p(mem_ctx, struct rootdse_db_context);
90         if (ctx == NULL) {
91                 errno = ENOMEM;
92                 return NULL;
93         }
94
95         ctx->static_ptr = &ctx;
96
97         db_path = talloc_asprintf(ctx, "tdb://%s/rootdse.ldb", dyn_PRIVATE_DIR);
98         if (db_path == NULL) {
99                 errno = ENOMEM;
100                 return NULL;
101         }
102
103         DEBUG(10, ("opening %s\n", db_path));
104         ctx->ldb = ldb_connect(db_path, 0, NULL);
105         if (ctx->ldb == NULL) {
106                 talloc_free(ctx);
107                 return NULL;
108         }
109
110         talloc_set_destructor(ctx, rootdse_db_destructor);
111         ldb_set_debug(ctx->ldb, rootdse_db_debug, NULL);
112
113         return ctx;
114 }
115
116
117 static NTSTATUS fill_dynamic_values(void *mem_ctx, struct ldap_attribute *attrs)
118 {
119         /* 
120          * currentTime
121          * 20040918090350.0Z
122          */
123
124         DEBUG(10, ("fill_dynamic_values for %s\n", attrs[0].name));
125
126         if (strcasecmp(attrs->name, "currentTime") == 0)
127         {
128                 int num_currentTime = 1;
129                 DATA_BLOB *currentTime = talloc_array_p(mem_ctx, DATA_BLOB, num_currentTime);
130                 char *str = ldap_timestring(mem_ctx, time(NULL));
131                 if (!str) {
132                         return NT_STATUS_NO_MEMORY;
133                 }
134                 currentTime[0].data = str;
135                 currentTime[0].length = strlen(str);
136                 ATTR_SINGLE_NOVAL(mem_ctx, attrs, currentTime, num_currentTime, "currentTime");
137                 return NT_STATUS_OK;
138         }
139
140         /* 
141          * subschemaSubentry 
142          * CN=Aggregate,CN=Schema,CN=Configuration,DC=DOM,DC=TLD
143          */
144
145         /* 
146          * dsServiceName
147          * CN=NTDS Settings,CN=NETBIOSNAME,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=DOM,DC=TLD
148          */
149
150         /* 
151          * namingContexts
152          * DC=DOM,DC=TLD
153          * CN=Configuration,DC=DOM,DC=TLD
154          * CN=Schema,CN=Configuration,DC=DOM,DC=TLD
155          * DC=DomainDnsZones,DC=DOM,DC=TLD
156          * DC=ForestDnsZones,DC=DOM,DC=TLD
157          */
158
159         /* 
160          * defaultNamingContext
161          * DC=DOM,DC=TLD
162          */
163
164         /* 
165          * schemaNamingContext
166          * CN=Schema,CN=Configuration,DC=DOM,DC=TLD
167          */
168
169         /* 
170          * configurationNamingContext
171          * CN=Configuration,DC=DOM,DC=TLD
172          */
173
174         /* 
175          * rootDomainNamingContext
176          * DC=DOM,DC=TLD
177          */
178
179         /* 
180          * supportedControl
181          * 1.2.840.113556.1.4.319
182          * 1.2.840.113556.1.4.801
183          * 1.2.840.113556.1.4.473
184          * 1.2.840.113556.1.4.528
185          * 1.2.840.113556.1.4.417
186          * 1.2.840.113556.1.4.619
187          * 1.2.840.113556.1.4.841
188          * 1.2.840.113556.1.4.529
189          * 1.2.840.113556.1.4.805
190          * 1.2.840.113556.1.4.521
191          * 1.2.840.113556.1.4.970
192          * 1.2.840.113556.1.4.1338
193          * 1.2.840.113556.1.4.474
194          * 1.2.840.113556.1.4.1339
195          * 1.2.840.113556.1.4.1340
196          * 1.2.840.113556.1.4.1413
197          * 2.16.840.1.113730.3.4.9
198          * 2.16.840.1.113730.3.4.10
199          * 1.2.840.113556.1.4.1504
200          * 1.2.840.113556.1.4.1852
201          * 1.2.840.113556.1.4.802
202          */
203
204         /* 
205          * supportedLDAPVersion 
206          * 3
207          * 2
208          */
209         if (strcasecmp(attrs->name, "supportedLDAPVersion") == 0)
210         {
211                 int num_supportedLDAPVersion = 1;
212                 DATA_BLOB *supportedLDAPVersion = talloc_array_p(mem_ctx, DATA_BLOB, num_supportedLDAPVersion);
213                 supportedLDAPVersion[0] = ATTR_BLOB_CONST("3");
214                 ATTR_SINGLE_NOVAL(mem_ctx, attrs, supportedLDAPVersion, num_supportedLDAPVersion, "supportedLDAPVersion");
215                 return NT_STATUS_OK;
216         }
217
218         /* 
219          * supportedLDAPPolicies
220          * MaxPoolThreads
221          * MaxDatagramRecv
222          * MaxReceiveBuffer
223          * InitRecvTimeout
224          * MaxConnections
225          * MaxConnIdleTime
226          * MaxPageSize
227          * MaxQueryDuration
228          * MaxTempTableSize
229          * MaxResultSetSize
230          * MaxNotificationPerConn
231          * MaxValRange
232          */
233
234         /* 
235          * highestCommittedUSN 
236          * 4555
237          */
238
239         /* 
240          * supportedSASLMechanisms
241          * GSSAPI
242          * GSS-SPNEGO
243          * EXTERNAL
244          * DIGEST-MD5
245          */
246
247         /* 
248          * dnsHostName
249          * netbiosname.dom.tld
250          */
251
252         /* 
253          * ldapServiceName
254          * dom.tld:netbiosname$@DOM.TLD
255          */
256
257         /* 
258          * serverName:
259          * CN=NETBIOSNAME,CN=Servers,CN=Default-First-Site,CN=Sites,CN=Configuration,DC=DOM,DC=TLD
260          */
261
262         /* 
263          * supportedCapabilities
264          * 1.2.840.113556.1.4.800
265          * 1.2.840.113556.1.4.1670
266          * 1.2.840.113556.1.4.1791
267          */
268
269         /* 
270          * isSynchronized:
271          * TRUE/FALSE
272          */
273
274         /* 
275          * isGlobalCatalogReady
276          * TRUE/FALSE
277          */
278
279         /* 
280          * domainFunctionality
281          * 0
282          */
283
284         /* 
285          * forestFunctionality
286          * 0
287          */
288
289         /* 
290          * domainControllerFunctionality
291          * 2
292          */
293
294         {
295                 DATA_BLOB *x = talloc_array_p(mem_ctx, DATA_BLOB, 1);
296                 x[0] = ATTR_BLOB_CONST("0");
297                 ATTR_SINGLE_NOVAL(mem_ctx, attrs, x, 1, attrs->name);
298         }
299         return NT_STATUS_OK;
300 }
301
302 static NTSTATUS rootdse_Search(struct ldapsrv_partition *partition, struct ldapsrv_call *call,
303                                      struct ldap_SearchRequest *r)
304 {
305         NTSTATUS status;
306         void *local_ctx;
307         struct ldap_SearchResEntry *ent;
308         struct ldap_Result *done;
309         struct ldb_message **res;
310         int result = 0;
311         struct ldapsrv_reply *ent_r, *done_r;
312         struct  rootdse_db_context *rootdsedb;
313         const char *errstr = NULL;
314         int count, j, y;
315         const char **attrs = NULL;
316
317         if (r->scope != LDAP_SEARCH_SCOPE_BASE) {
318                 count = -1;
319                 goto no_base_scope;
320         }
321
322         local_ctx = talloc_named(call, 0, "rootdse_Search local memory context");
323         ALLOC_CHECK(local_ctx);
324
325         rootdsedb = rootdse_db_connect(local_ctx);
326         ALLOC_CHECK(rootdsedb);
327
328         if (r->num_attributes >= 1) {
329                 attrs = talloc_array_p(rootdsedb, const char *, r->num_attributes+1);
330                 ALLOC_CHECK(attrs);
331
332                 for (j=0; j < r->num_attributes; j++) {
333                         DEBUG(10,("rootDSE_Search: attrs: [%s]\n",r->attributes[j]));
334                         attrs[j] = r->attributes[j];
335                 }
336                 attrs[j] = NULL;
337         }
338
339         ldb_set_alloc(rootdsedb->ldb, talloc_realloc_fn, rootdsedb);
340         count = ldb_search(rootdsedb->ldb, "", 0, "dn=cn=rootDSE", attrs, &res);
341
342         if (count == 1) {
343                 ent_r = ldapsrv_init_reply(call, LDAP_TAG_SearchResultEntry);
344                 if (!ent_r) {
345                         return NT_STATUS_NO_MEMORY;
346                 }
347
348                 ent = &ent_r->msg.r.SearchResultEntry;
349                 ent->dn = "";
350                 ent->num_attributes = 0;
351                 ent->attributes = NULL;
352                 if (res[0]->num_elements == 0) {
353                         goto queue_reply;
354                 }
355                 ent->num_attributes = res[0]->num_elements;
356                 ent->attributes = talloc_array_p(ent_r, struct ldap_attribute, ent->num_attributes);
357                 ALLOC_CHECK(ent->attributes);
358                 for (j=0; j < ent->num_attributes; j++) {
359                         ent->attributes[j].name = talloc_steal(ent->attributes, res[0]->elements[j].name);
360                         ent->attributes[j].num_values = 0;
361                         ent->attributes[j].values = NULL;
362                         ent->attributes[j].num_values = res[0]->elements[j].num_values;
363                         if (ent->attributes[j].num_values == 1 &&
364                                 strncmp(res[0]->elements[j].values[0].data, "_DYNAMIC_", 9) == 0) {
365                                 fill_dynamic_values(ent->attributes, &(ent->attributes[j]));
366                                 if (ent->attributes[j].values[0].data == NULL) {
367                                         DEBUG (10, ("ARRGHH!\n"));
368                                 }
369                         } else {
370                                 ent->attributes[j].values = talloc_array_p(ent->attributes,
371                                                                 DATA_BLOB, ent->attributes[j].num_values);
372                                 ALLOC_CHECK(ent->attributes[j].values);
373                                 for (y=0; y < ent->attributes[j].num_values; y++) {
374                                         ent->attributes[j].values[y].length = res[0]->elements[j].values[y].length;
375                                         ent->attributes[j].values[y].data = talloc_steal(ent->attributes[j].values,
376                                                                                 res[0]->elements[j].values[y].data);
377                                 }
378                         }
379                 }
380 queue_reply:
381                 status = ldapsrv_queue_reply(call, ent_r);
382                 if (!NT_STATUS_IS_OK(status)) {
383                         return status;
384                 }
385         }
386
387
388
389 no_base_scope:
390
391         done_r = ldapsrv_init_reply(call, LDAP_TAG_SearchResultDone);
392         if (!done_r) {
393                 return NT_STATUS_NO_MEMORY;
394         }
395
396         if (count == 1) {
397                 DEBUG(10,("rootdse_Search: results: [%d]\n",count));
398                 result = 0;
399                 errstr = NULL;
400         } else if (count > 1) {
401                 DEBUG(10,("rootdse_Search: to many results[%d]\n", count));
402                 result = 80; /* nosuchobject */
403                 errstr = talloc_strdup(done_r, "internal error");       
404         } else if (count == 0) {
405                 DEBUG(10,("rootdse_Search: no results\n"));
406                 result = 32; /* nosuchobject */
407                 errstr = talloc_strdup(done_r, ldb_errstring(rootdsedb->ldb));
408         } else if (count == -1) {
409                 DEBUG(10,("rootdse_Search: error\n"));
410                 result = 1;
411                 errstr = talloc_strdup(done_r, ldb_errstring(rootdsedb->ldb));
412         }
413
414         done = &done_r->msg.r.SearchResultDone;
415         done->resultcode = result;
416         done->dn = NULL;
417         done->errormessage = NULL;
418         done->referral = NULL;
419
420         talloc_free(local_ctx);
421
422         return ldapsrv_queue_reply(call, done_r);
423 }
424
425 static const struct ldapsrv_partition_ops rootdse_ops = {
426         .Search         = rootdse_Search
427 };
428
429 const struct ldapsrv_partition_ops *ldapsrv_get_rootdse_partition_ops(void)
430 {
431         return &rootdse_ops;
432 }