r19531: Make struct ldb_dn opaque and local to ldb_dn.c
[abartlet/samba.git/.git] / source4 / dsdb / samdb / ldb_modules / rootdse.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    rootDSE ldb module
5
6    Copyright (C) Andrew Tridgell 2005
7    Copyright (C) Simo Sorce 2005
8    
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24 #include "includes.h"
25 #include "lib/ldb/include/ldb.h"
26 #include "lib/ldb/include/ldb_errors.h"
27 #include "lib/ldb/include/ldb_private.h"
28 #include "system/time.h"
29
30 struct private_data {
31         int num_controls;
32         char **controls;
33         int num_partitions;
34         struct ldb_dn **partitions;
35 };
36
37 /*
38   return 1 if a specific attribute has been requested
39 */
40 static int do_attribute(const char * const *attrs, const char *name)
41 {
42         return attrs == NULL ||
43                 ldb_attr_in_list(attrs, name) ||
44                 ldb_attr_in_list(attrs, "*");
45 }
46
47
48 /*
49   add dynamically generated attributes to rootDSE result
50 */
51 static int rootdse_add_dynamic(struct ldb_module *module, struct ldb_message *msg, const char * const *attrs)
52 {
53         struct private_data *priv = talloc_get_type(module->private_data, struct private_data);
54         char **server_sasl;
55
56         msg->dn = ldb_dn_explode(msg, "");
57
58         /* don't return the distinduishedName, cn and name attributes */
59         ldb_msg_remove_attr(msg, "distinguishedName");
60         ldb_msg_remove_attr(msg, "cn");
61         ldb_msg_remove_attr(msg, "name");
62
63         if (do_attribute(attrs, "currentTime")) {
64                 if (ldb_msg_add_steal_string(msg, "currentTime", 
65                                              ldb_timestring(msg, time(NULL))) != 0) {
66                         goto failed;
67                 }
68         }
69
70         if (do_attribute(attrs, "supportedControl")) {
71                 int i;
72                 for (i = 0; i < priv->num_controls; i++) {
73                         char *control = talloc_strdup(msg, priv->controls[i]);
74                         if (!control) {
75                                 goto failed;
76                         }
77                         if (ldb_msg_add_steal_string(msg, "supportedControl",
78                                                      control) != 0) {
79                                 goto failed;
80                         }
81                 }
82         }
83
84         if (do_attribute(attrs, "namingContexts")) {
85                 int i;
86                 for (i = 0; i < priv->num_partitions; i++) {
87                         struct ldb_dn *dn = priv->partitions[i];
88                         if (ldb_msg_add_steal_string(msg, "namingContexts",
89                                                      ldb_dn_linearize(msg, dn)) != 0) {
90                                 goto failed;
91                         }
92                 }
93         }
94
95         server_sasl = talloc_get_type(ldb_get_opaque(module->ldb, "supportedSASLMechanims"), 
96                                        char *);
97         if (server_sasl && do_attribute(attrs, "supportedSASLMechanisms")) {
98                 int i;
99                 for (i = 0; server_sasl && server_sasl[i]; i++) {
100                         char *sasl_name = talloc_strdup(msg, server_sasl[i]);
101                         if (!sasl_name) {
102                                 goto failed;
103                         }
104                         if (ldb_msg_add_steal_string(msg, "supportedSASLMechanisms",
105                                                      sasl_name) != 0) {
106                                 goto failed;
107                         }
108                 }
109         }
110
111         if (do_attribute(attrs, "highestCommittedUSN")) {
112                 uint64_t seq_num;
113                 int ret = ldb_sequence_number(module->ldb, LDB_SEQ_HIGHEST_SEQ, &seq_num);
114                 if (ret == LDB_SUCCESS) {
115                         if (ldb_msg_add_fmt(msg, "highestCommittedUSN", 
116                                             "%llu", (unsigned long long)seq_num) != 0) {
117                                 goto failed;
118                         }
119                 }
120         }
121
122         /* TODO: lots more dynamic attributes should be added here */
123
124         return LDB_SUCCESS;
125
126 failed:
127         return LDB_ERR_OPERATIONS_ERROR;
128 }
129
130 /*
131   handle search requests
132 */
133
134 struct rootdse_context {
135         struct ldb_module *module;
136         void *up_context;
137         int (*up_callback)(struct ldb_context *, void *, struct ldb_reply *);
138
139         const char * const * attrs;
140 };
141
142 static int rootdse_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
143 {
144         struct rootdse_context *ac;
145
146         if (!context || !ares) {
147                 ldb_set_errstring(ldb, "NULL Context or Result in callback");
148                 goto error;
149         }
150
151         ac = talloc_get_type(context, struct rootdse_context);
152
153         if (ares->type == LDB_REPLY_ENTRY) {
154                 /* for each record returned post-process to add any dynamic
155                    attributes that have been asked for */
156                 if (rootdse_add_dynamic(ac->module, ares->message, ac->attrs) != LDB_SUCCESS) {
157                         goto error;
158                 }
159         }
160
161         return ac->up_callback(ldb, ac->up_context, ares);
162
163 error:
164         talloc_free(ares);
165         return LDB_ERR_OPERATIONS_ERROR;
166 }
167
168 static int rootdse_search(struct ldb_module *module, struct ldb_request *req)
169 {
170         struct rootdse_context *ac;
171         struct ldb_request *down_req;
172         int ret;
173
174         /* see if its for the rootDSE */
175         if (req->op.search.scope != LDB_SCOPE_BASE ||
176             (req->op.search.base && ldb_dn_get_comp_num(req->op.search.base) != 0)) {
177                 return ldb_next_request(module, req);
178         }
179
180         ac = talloc(req, struct rootdse_context);
181         if (ac == NULL) {
182                 return LDB_ERR_OPERATIONS_ERROR;
183         }
184
185         ac->module = module;
186         ac->up_context = req->context;
187         ac->up_callback = req->callback;
188         ac->attrs = req->op.search.attrs;
189
190         down_req = talloc_zero(req, struct ldb_request);
191         if (down_req == NULL) {
192                 return LDB_ERR_OPERATIONS_ERROR;
193         }
194
195         down_req->operation = req->operation;
196         /* in our db we store the rootDSE with a DN of cn=rootDSE */
197         down_req->op.search.base = ldb_dn_explode(down_req, "cn=rootDSE");
198         down_req->op.search.scope = LDB_SCOPE_BASE;
199         down_req->op.search.tree = ldb_parse_tree(down_req, NULL);
200         if (down_req->op.search.base == NULL || down_req->op.search.tree == NULL) {
201                 ldb_oom(module->ldb);
202                 talloc_free(down_req);
203                 return LDB_ERR_OPERATIONS_ERROR;
204         }
205         down_req->op.search.attrs = req->op.search.attrs;
206         down_req->controls = req->controls;
207
208         down_req->context = ac;
209         down_req->callback = rootdse_callback;
210         ldb_set_timeout_from_prev_req(module->ldb, req, down_req);
211
212         /* perform the search */
213         ret = ldb_next_request(module, down_req);
214
215         /* do not free down_req as the call results may be linked to it,
216          * it will be freed when the upper level request get freed */
217         if (ret == LDB_SUCCESS) {
218                 req->handle = down_req->handle;
219         }
220
221         return ret;
222 }
223
224 static int rootdse_register_control(struct ldb_module *module, struct ldb_request *req)
225 {
226         struct private_data *priv = talloc_get_type(module->private_data, struct private_data);
227         char **list;
228
229         list = talloc_realloc(priv, priv->controls, char *, priv->num_controls + 1);
230         if (!list) {
231                 return LDB_ERR_OPERATIONS_ERROR;
232         }
233
234         list[priv->num_controls] = talloc_strdup(list, req->op.reg_control.oid);
235         if (!list[priv->num_controls]) {
236                 return LDB_ERR_OPERATIONS_ERROR;
237         }
238
239         priv->num_controls += 1;
240         priv->controls = list;
241
242         return LDB_SUCCESS;
243 }
244  
245 static int rootdse_register_partition(struct ldb_module *module, struct ldb_request *req)
246 {
247         struct private_data *priv = talloc_get_type(module->private_data, struct private_data);
248         struct ldb_dn **list;
249
250         list = talloc_realloc(priv, priv->partitions, struct ldb_dn *, priv->num_partitions + 1);
251         if (!list) {
252                 return LDB_ERR_OPERATIONS_ERROR;
253         }
254
255         list[priv->num_partitions] = ldb_dn_copy(list, req->op.reg_partition.dn);
256         if (!list[priv->num_partitions]) {
257                 return LDB_ERR_OPERATIONS_ERROR;
258         }
259
260         priv->num_partitions += 1;
261         priv->partitions = list;
262
263         return LDB_SUCCESS;
264 }
265  
266
267 static int rootdse_request(struct ldb_module *module, struct ldb_request *req)
268 {
269         switch (req->operation) {
270
271         case LDB_REQ_REGISTER_CONTROL:
272                 return rootdse_register_control(module, req);
273         case LDB_REQ_REGISTER_PARTITION:
274                 return rootdse_register_partition(module, req);
275
276         default:
277                 break;
278         }
279         return ldb_next_request(module, req);
280 }
281
282 static int rootdse_init(struct ldb_module *module)
283 {
284         struct private_data *data;
285
286         data = talloc(module, struct private_data);
287         if (data == NULL) {
288                 return -1;
289         }
290
291         data->num_controls = 0;
292         data->controls = NULL;
293         data->num_partitions = 0;
294         data->partitions = NULL;
295         module->private_data = data;
296
297         return ldb_next_init(module);
298 }
299
300 static const struct ldb_module_ops rootdse_ops = {
301         .name                   = "rootdse",
302         .init_context           = rootdse_init,
303         .search                 = rootdse_search,
304         .request                = rootdse_request
305 };
306
307 int rootdse_module_init(void)
308 {
309         return ldb_register_module(&rootdse_ops);
310 }
311