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