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