1 /*
2    ldb database library
3 
4    Copyright (C) Stefan Metzmacher <metze@samba.org> 2009
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 3 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, see <http://www.gnu.org/licenses/>.
18 */
19 
20 #include "includes.h"
21 #include "ldb_module.h"
22 #include "dsdb/samdb/samdb.h"
23 
resolve_oids_need_value(struct ldb_context * ldb,struct dsdb_schema * schema,const struct dsdb_attribute * a,const struct ldb_val * valp)24 static int resolve_oids_need_value(struct ldb_context *ldb,
25 				   struct dsdb_schema *schema,
26 				   const struct dsdb_attribute *a,
27 				   const struct ldb_val *valp)
28 {
29 	const struct dsdb_attribute *va = NULL;
30 	const struct dsdb_class *vo = NULL;
31 	const void *p2;
32 	char *str = NULL;
33 
34 	if (a->syntax->oMSyntax != 6) {
35 		return LDB_ERR_COMPARE_FALSE;
36 	}
37 
38 	if (valp) {
39 		p2 = memchr(valp->data, '.', valp->length);
40 	} else {
41 		p2 = NULL;
42 	}
43 
44 	if (!p2) {
45 		return LDB_ERR_COMPARE_FALSE;
46 	}
47 
48 	switch (a->attributeID_id) {
49 	case DRSUAPI_ATTID_objectClass:
50 	case DRSUAPI_ATTID_subClassOf:
51 	case DRSUAPI_ATTID_auxiliaryClass:
52 	case DRSUAPI_ATTID_systemPossSuperiors:
53 	case DRSUAPI_ATTID_possSuperiors:
54 		str = talloc_strndup(ldb, (char *)valp->data, valp->length);
55 		if (!str) {
56 			return ldb_oom(ldb);
57 		}
58 		vo = dsdb_class_by_governsID_oid(schema, str);
59 		talloc_free(str);
60 		if (!vo) {
61 			return LDB_ERR_COMPARE_FALSE;
62 		}
63 		return LDB_ERR_COMPARE_TRUE;
64 	case DRSUAPI_ATTID_systemMustContain:
65 	case DRSUAPI_ATTID_systemMayContain:
66 	case DRSUAPI_ATTID_mustContain:
67 	case DRSUAPI_ATTID_mayContain:
68 		str = talloc_strndup(ldb, (char *)valp->data, valp->length);
69 		if (!str) {
70 			return ldb_oom(ldb);
71 		}
72 		va = dsdb_attribute_by_attributeID_oid(schema, str);
73 		talloc_free(str);
74 		if (!va) {
75 			return LDB_ERR_COMPARE_FALSE;
76 		}
77 		return LDB_ERR_COMPARE_TRUE;
78 	case DRSUAPI_ATTID_governsID:
79 	case DRSUAPI_ATTID_attributeID:
80 	case DRSUAPI_ATTID_attributeSyntax:
81 		return LDB_ERR_COMPARE_FALSE;
82 	}
83 
84 	return LDB_ERR_COMPARE_FALSE;
85 }
86 
resolve_oids_parse_tree_need(struct ldb_context * ldb,struct dsdb_schema * schema,const struct ldb_parse_tree * tree)87 static int resolve_oids_parse_tree_need(struct ldb_context *ldb,
88 					struct dsdb_schema *schema,
89 					const struct ldb_parse_tree *tree)
90 {
91 	unsigned int i;
92 	const struct dsdb_attribute *a = NULL;
93 	const char *attr;
94 	const char *p1;
95 	const void *p2;
96 	const struct ldb_val *valp = NULL;
97 	int ret;
98 
99 	switch (tree->operation) {
100 	case LDB_OP_AND:
101 	case LDB_OP_OR:
102 		for (i=0;i<tree->u.list.num_elements;i++) {
103 			ret = resolve_oids_parse_tree_need(ldb, schema,
104 						tree->u.list.elements[i]);
105 			if (ret != LDB_ERR_COMPARE_FALSE) {
106 				return ret;
107 			}
108 		}
109 		return LDB_ERR_COMPARE_FALSE;
110 	case LDB_OP_NOT:
111 		return resolve_oids_parse_tree_need(ldb, schema,
112 						tree->u.isnot.child);
113 	case LDB_OP_EQUALITY:
114 	case LDB_OP_GREATER:
115 	case LDB_OP_LESS:
116 	case LDB_OP_APPROX:
117 		attr = tree->u.equality.attr;
118 		valp = &tree->u.equality.value;
119 		break;
120 	case LDB_OP_SUBSTRING:
121 		attr = tree->u.substring.attr;
122 		break;
123 	case LDB_OP_PRESENT:
124 		attr = tree->u.present.attr;
125 		break;
126 	case LDB_OP_EXTENDED:
127 		attr = tree->u.extended.attr;
128 		valp = &tree->u.extended.value;
129 		break;
130 	default:
131 		return LDB_ERR_COMPARE_FALSE;
132 	}
133 
134 	p1 = strchr(attr, '.');
135 
136 	if (valp) {
137 		p2 = memchr(valp->data, '.', valp->length);
138 	} else {
139 		p2 = NULL;
140 	}
141 
142 	if (!p1 && !p2) {
143 		return LDB_ERR_COMPARE_FALSE;
144 	}
145 
146 	if (p1) {
147 		a = dsdb_attribute_by_attributeID_oid(schema, attr);
148 	} else {
149 		a = dsdb_attribute_by_lDAPDisplayName(schema, attr);
150 	}
151 	if (!a) {
152 		return LDB_ERR_COMPARE_FALSE;
153 	}
154 
155 	if (!p2) {
156 		return LDB_ERR_COMPARE_FALSE;
157 	}
158 
159 	return resolve_oids_need_value(ldb, schema, a, valp);
160 }
161 
resolve_oids_element_need(struct ldb_context * ldb,struct dsdb_schema * schema,const struct ldb_message_element * el)162 static int resolve_oids_element_need(struct ldb_context *ldb,
163 				     struct dsdb_schema *schema,
164 				     const struct ldb_message_element *el)
165 {
166 	unsigned int i;
167 	const struct dsdb_attribute *a = NULL;
168 	const char *p1;
169 
170 	p1 = strchr(el->name, '.');
171 
172 	if (p1) {
173 		a = dsdb_attribute_by_attributeID_oid(schema, el->name);
174 	} else {
175 		a = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
176 	}
177 	if (!a) {
178 		return LDB_ERR_COMPARE_FALSE;
179 	}
180 
181 	for (i=0; i < el->num_values; i++) {
182 		int ret;
183 		ret = resolve_oids_need_value(ldb, schema, a,
184 					      &el->values[i]);
185 		if (ret != LDB_ERR_COMPARE_FALSE) {
186 			return ret;
187 		}
188 	}
189 
190 	return LDB_ERR_COMPARE_FALSE;
191 }
192 
resolve_oids_message_need(struct ldb_context * ldb,struct dsdb_schema * schema,const struct ldb_message * msg)193 static int resolve_oids_message_need(struct ldb_context *ldb,
194 				     struct dsdb_schema *schema,
195 				     const struct ldb_message *msg)
196 {
197 	unsigned int i;
198 
199 	for (i=0; i < msg->num_elements; i++) {
200 		int ret;
201 		ret = resolve_oids_element_need(ldb, schema,
202 						&msg->elements[i]);
203 		if (ret != LDB_ERR_COMPARE_FALSE) {
204 			return ret;
205 		}
206 	}
207 
208 	return LDB_ERR_COMPARE_FALSE;
209 }
210 
resolve_oids_replace_value(struct ldb_context * ldb,struct dsdb_schema * schema,const struct dsdb_attribute * a,struct ldb_val * valp)211 static int resolve_oids_replace_value(struct ldb_context *ldb,
212 				      struct dsdb_schema *schema,
213 				      const struct dsdb_attribute *a,
214 				      struct ldb_val *valp)
215 {
216 	const struct dsdb_attribute *va = NULL;
217 	const struct dsdb_class *vo = NULL;
218 	const void *p2;
219 	char *str = NULL;
220 
221 	if (a->syntax->oMSyntax != 6) {
222 		return LDB_SUCCESS;
223 	}
224 
225 	if (valp) {
226 		p2 = memchr(valp->data, '.', valp->length);
227 	} else {
228 		p2 = NULL;
229 	}
230 
231 	if (!p2) {
232 		return LDB_SUCCESS;
233 	}
234 
235 	switch (a->attributeID_id) {
236 	case DRSUAPI_ATTID_objectClass:
237 	case DRSUAPI_ATTID_subClassOf:
238 	case DRSUAPI_ATTID_auxiliaryClass:
239 	case DRSUAPI_ATTID_systemPossSuperiors:
240 	case DRSUAPI_ATTID_possSuperiors:
241 		str = talloc_strndup(schema, (char *)valp->data, valp->length);
242 		if (!str) {
243 			return ldb_oom(ldb);
244 		}
245 		vo = dsdb_class_by_governsID_oid(schema, str);
246 		talloc_free(str);
247 		if (!vo) {
248 			return LDB_SUCCESS;
249 		}
250 		*valp = data_blob_string_const(vo->lDAPDisplayName);
251 		return LDB_SUCCESS;
252 	case DRSUAPI_ATTID_systemMustContain:
253 	case DRSUAPI_ATTID_systemMayContain:
254 	case DRSUAPI_ATTID_mustContain:
255 	case DRSUAPI_ATTID_mayContain:
256 		str = talloc_strndup(schema, (char *)valp->data, valp->length);
257 		if (!str) {
258 			return ldb_oom(ldb);
259 		}
260 		va = dsdb_attribute_by_attributeID_oid(schema, str);
261 		talloc_free(str);
262 		if (!va) {
263 			return LDB_SUCCESS;
264 		}
265 		*valp = data_blob_string_const(va->lDAPDisplayName);
266 		return LDB_SUCCESS;
267 	case DRSUAPI_ATTID_governsID:
268 	case DRSUAPI_ATTID_attributeID:
269 	case DRSUAPI_ATTID_attributeSyntax:
270 		return LDB_SUCCESS;
271 	}
272 
273 	return LDB_SUCCESS;
274 }
275 
resolve_oids_parse_tree_replace(struct ldb_context * ldb,struct dsdb_schema * schema,struct ldb_parse_tree * tree)276 static int resolve_oids_parse_tree_replace(struct ldb_context *ldb,
277 					   struct dsdb_schema *schema,
278 					   struct ldb_parse_tree *tree)
279 {
280 	unsigned int i;
281 	const struct dsdb_attribute *a = NULL;
282 	const char **attrp;
283 	const char *p1;
284 	const void *p2;
285 	struct ldb_val *valp = NULL;
286 	int ret;
287 
288 	switch (tree->operation) {
289 	case LDB_OP_AND:
290 	case LDB_OP_OR:
291 		for (i=0;i<tree->u.list.num_elements;i++) {
292 			ret = resolve_oids_parse_tree_replace(ldb, schema,
293 							tree->u.list.elements[i]);
294 			if (ret != LDB_SUCCESS) {
295 				return ret;
296 			}
297 		}
298 		return LDB_SUCCESS;
299 	case LDB_OP_NOT:
300 		return resolve_oids_parse_tree_replace(ldb, schema,
301 						tree->u.isnot.child);
302 	case LDB_OP_EQUALITY:
303 	case LDB_OP_GREATER:
304 	case LDB_OP_LESS:
305 	case LDB_OP_APPROX:
306 		attrp = &tree->u.equality.attr;
307 		valp = &tree->u.equality.value;
308 		break;
309 	case LDB_OP_SUBSTRING:
310 		attrp = &tree->u.substring.attr;
311 		break;
312 	case LDB_OP_PRESENT:
313 		attrp = &tree->u.present.attr;
314 		break;
315 	case LDB_OP_EXTENDED:
316 		attrp = &tree->u.extended.attr;
317 		valp = &tree->u.extended.value;
318 		break;
319 	default:
320 		return LDB_SUCCESS;
321 	}
322 
323 	p1 = strchr(*attrp, '.');
324 
325 	if (valp) {
326 		p2 = memchr(valp->data, '.', valp->length);
327 	} else {
328 		p2 = NULL;
329 	}
330 
331 	if (!p1 && !p2) {
332 		return LDB_SUCCESS;
333 	}
334 
335 	if (p1) {
336 		a = dsdb_attribute_by_attributeID_oid(schema, *attrp);
337 	} else {
338 		a = dsdb_attribute_by_lDAPDisplayName(schema, *attrp);
339 	}
340 	if (!a) {
341 		return LDB_SUCCESS;
342 	}
343 
344 	*attrp = a->lDAPDisplayName;
345 
346 	if (!p2) {
347 		return LDB_SUCCESS;
348 	}
349 
350 	if (a->syntax->oMSyntax != 6) {
351 		return LDB_SUCCESS;
352 	}
353 
354 	return resolve_oids_replace_value(ldb, schema, a, valp);
355 }
356 
resolve_oids_element_replace(struct ldb_context * ldb,struct dsdb_schema * schema,struct ldb_message_element * el)357 static int resolve_oids_element_replace(struct ldb_context *ldb,
358 					struct dsdb_schema *schema,
359 					struct ldb_message_element *el)
360 {
361 	unsigned int i;
362 	const struct dsdb_attribute *a = NULL;
363 	const char *p1;
364 
365 	p1 = strchr(el->name, '.');
366 
367 	if (p1) {
368 		a = dsdb_attribute_by_attributeID_oid(schema, el->name);
369 	} else {
370 		a = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
371 	}
372 	if (!a) {
373 		return LDB_SUCCESS;
374 	}
375 
376 	el->name = a->lDAPDisplayName;
377 
378 	for (i=0; i < el->num_values; i++) {
379 		int ret;
380 		ret = resolve_oids_replace_value(ldb, schema, a,
381 						 &el->values[i]);
382 		if (ret != LDB_SUCCESS) {
383 			return ret;
384 		}
385 	}
386 
387 	return LDB_SUCCESS;
388 }
389 
resolve_oids_message_replace(struct ldb_context * ldb,struct dsdb_schema * schema,struct ldb_message * msg)390 static int resolve_oids_message_replace(struct ldb_context *ldb,
391 					struct dsdb_schema *schema,
392 					struct ldb_message *msg)
393 {
394 	unsigned int i;
395 
396 	for (i=0; i < msg->num_elements; i++) {
397 		int ret;
398 		ret = resolve_oids_element_replace(ldb, schema,
399 						   &msg->elements[i]);
400 		if (ret != LDB_SUCCESS) {
401 			return ret;
402 		}
403 	}
404 
405 	return LDB_SUCCESS;
406 }
407 
408 struct resolve_oids_context {
409 	struct ldb_module *module;
410 	struct ldb_request *req;
411 };
412 
resolve_oids_callback(struct ldb_request * req,struct ldb_reply * ares)413 static int resolve_oids_callback(struct ldb_request *req, struct ldb_reply *ares)
414 {
415 	struct resolve_oids_context *ac;
416 
417 	ac = talloc_get_type_abort(req->context, struct resolve_oids_context);
418 
419 	if (!ares) {
420 		return ldb_module_done(ac->req, NULL, NULL,
421 					LDB_ERR_OPERATIONS_ERROR);
422 	}
423 	if (ares->error != LDB_SUCCESS) {
424 		return ldb_module_done(ac->req, ares->controls,
425 					ares->response, ares->error);
426 	}
427 
428 	switch (ares->type) {
429 	case LDB_REPLY_ENTRY:
430 		return ldb_module_send_entry(ac->req, ares->message, ares->controls);
431 
432 	case LDB_REPLY_REFERRAL:
433 		return ldb_module_send_referral(ac->req, ares->referral);
434 
435 	case LDB_REPLY_DONE:
436 		return ldb_module_done(ac->req, ares->controls,
437 				       ares->response, LDB_SUCCESS);
438 
439 	}
440 	return LDB_SUCCESS;
441 }
442 
resolve_oids_search(struct ldb_module * module,struct ldb_request * req)443 static int resolve_oids_search(struct ldb_module *module, struct ldb_request *req)
444 {
445 	struct ldb_context *ldb;
446 	struct dsdb_schema *schema;
447 	struct ldb_parse_tree *tree;
448 	struct ldb_request *down_req;
449 	struct resolve_oids_context *ac;
450 	int ret;
451 	bool needed = false;
452 	const char * const *attrs1;
453 	const char **attrs2;
454 	unsigned int i;
455 
456 	ldb = ldb_module_get_ctx(module);
457 	schema = dsdb_get_schema(ldb, NULL);
458 
459 	if (!schema) {
460 		return ldb_next_request(module, req);
461 	}
462 
463 	/* do not manipulate our control entries */
464 	if (ldb_dn_is_special(req->op.search.base)) {
465 		return ldb_next_request(module, req);
466 	}
467 
468 	ret = resolve_oids_parse_tree_need(ldb, schema,
469 					   req->op.search.tree);
470 	if (ret == LDB_ERR_COMPARE_TRUE) {
471 		needed = true;
472 	} else if (ret != LDB_ERR_COMPARE_FALSE) {
473 		return ret;
474 	}
475 
476 	attrs1 = req->op.search.attrs;
477 
478 	for (i=0; attrs1 && attrs1[i]; i++) {
479 		const char *p;
480 		const struct dsdb_attribute *a;
481 
482 		p = strchr(attrs1[i], '.');
483 		if (p == NULL) {
484 			continue;
485 		}
486 
487 		a = dsdb_attribute_by_attributeID_oid(schema, attrs1[i]);
488 		if (a == NULL) {
489 			continue;
490 		}
491 
492 		needed = true;
493 		break;
494 	}
495 
496 	if (!needed) {
497 		return ldb_next_request(module, req);
498 	}
499 
500 	ac = talloc(req, struct resolve_oids_context);
501 	if (ac == NULL) {
502 		return ldb_oom(ldb);
503 	}
504 	ac->module = module;
505 	ac->req = req;
506 
507 	tree = ldb_parse_tree_copy_shallow(ac, req->op.search.tree);
508 	if (!tree) {
509 		return ldb_oom(ldb);
510 	}
511 
512 	schema = talloc_reference(tree, schema);
513 	if (!schema) {
514 		return ldb_oom(ldb);
515 	}
516 
517 	ret = resolve_oids_parse_tree_replace(ldb, schema,
518 					      tree);
519 	if (ret != LDB_SUCCESS) {
520 		return ret;
521 	}
522 
523 	attrs2 = str_list_copy_const(ac,
524 				     discard_const_p(const char *, req->op.search.attrs));
525 	if (req->op.search.attrs && !attrs2) {
526 		return ldb_oom(ldb);
527 	}
528 
529 	for (i=0; attrs2 && attrs2[i]; i++) {
530 		const char *p;
531 		const struct dsdb_attribute *a;
532 
533 		p = strchr(attrs2[i], '.');
534 		if (p == NULL) {
535 			continue;
536 		}
537 
538 		a = dsdb_attribute_by_attributeID_oid(schema, attrs2[i]);
539 		if (a == NULL) {
540 			continue;
541 		}
542 
543 		attrs2[i] = a->lDAPDisplayName;
544 	}
545 
546 	ret = ldb_build_search_req_ex(&down_req, ldb, ac,
547 				      req->op.search.base,
548 				      req->op.search.scope,
549 				      tree,
550 				      attrs2,
551 				      req->controls,
552 				      ac, resolve_oids_callback,
553 				      req);
554 	LDB_REQ_SET_LOCATION(down_req);
555 	if (ret != LDB_SUCCESS) {
556 		return ret;
557 	}
558 
559 	/* go on with the call chain */
560 	return ldb_next_request(module, down_req);
561 }
562 
resolve_oids_add(struct ldb_module * module,struct ldb_request * req)563 static int resolve_oids_add(struct ldb_module *module, struct ldb_request *req)
564 {
565 	struct ldb_context *ldb;
566 	struct dsdb_schema *schema;
567 	int ret;
568 	struct ldb_message *msg;
569 	struct ldb_request *down_req;
570 	struct resolve_oids_context *ac;
571 
572 	ldb = ldb_module_get_ctx(module);
573 	schema = dsdb_get_schema(ldb, NULL);
574 
575 	if (!schema) {
576 		return ldb_next_request(module, req);
577 	}
578 
579 	/* do not manipulate our control entries */
580 	if (ldb_dn_is_special(req->op.add.message->dn)) {
581 		return ldb_next_request(module, req);
582 	}
583 
584 	ret = resolve_oids_message_need(ldb, schema,
585 					req->op.add.message);
586 	if (ret == LDB_ERR_COMPARE_FALSE) {
587 		return ldb_next_request(module, req);
588 	} else if (ret != LDB_ERR_COMPARE_TRUE) {
589 		return ret;
590 	}
591 
592 	ac = talloc(req, struct resolve_oids_context);
593 	if (ac == NULL) {
594 		return ldb_oom(ldb);
595 	}
596 	ac->module = module;
597 	ac->req = req;
598 
599 	msg = ldb_msg_copy_shallow(ac, ac->req->op.add.message);
600 	if (!msg) {
601 		return ldb_oom(ldb);
602 	}
603 
604 	if (!talloc_reference(msg, schema)) {
605 		return ldb_oom(ldb);
606 	}
607 
608 	ret = resolve_oids_message_replace(ldb, schema, msg);
609 	if (ret != LDB_SUCCESS) {
610 		return ret;
611 	}
612 
613 	ret = ldb_build_add_req(&down_req, ldb, ac,
614 				msg,
615 				req->controls,
616 				ac, resolve_oids_callback,
617 				req);
618 	LDB_REQ_SET_LOCATION(down_req);
619 	if (ret != LDB_SUCCESS) {
620 		return ret;
621 	}
622 
623 	/* go on with the call chain */
624 	return ldb_next_request(module, down_req);
625 }
626 
resolve_oids_modify(struct ldb_module * module,struct ldb_request * req)627 static int resolve_oids_modify(struct ldb_module *module, struct ldb_request *req)
628 {
629 	struct ldb_context *ldb;
630 	struct dsdb_schema *schema;
631 	int ret;
632 	struct ldb_message *msg;
633 	struct ldb_request *down_req;
634 	struct resolve_oids_context *ac;
635 
636 	ldb = ldb_module_get_ctx(module);
637 	schema = dsdb_get_schema(ldb, NULL);
638 
639 	if (!schema) {
640 		return ldb_next_request(module, req);
641 	}
642 
643 	/* do not manipulate our control entries */
644 	if (ldb_dn_is_special(req->op.mod.message->dn)) {
645 		return ldb_next_request(module, req);
646 	}
647 
648 	ret = resolve_oids_message_need(ldb, schema,
649 					req->op.mod.message);
650 	if (ret == LDB_ERR_COMPARE_FALSE) {
651 		return ldb_next_request(module, req);
652 	} else if (ret != LDB_ERR_COMPARE_TRUE) {
653 		return ret;
654 	}
655 
656 	ac = talloc(req, struct resolve_oids_context);
657 	if (ac == NULL) {
658 		return ldb_oom(ldb);
659 	}
660 	ac->module = module;
661 	ac->req = req;
662 
663 	/* we have to copy the message as the caller might have it as a const */
664 	msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
665 	if (msg == NULL) {
666 		return ldb_oom(ldb);
667 	}
668 
669 	if (!talloc_reference(msg, schema)) {
670 		return ldb_oom(ldb);
671 	}
672 
673 	ret = resolve_oids_message_replace(ldb, schema, msg);
674 	if (ret != LDB_SUCCESS) {
675 		return ret;
676 	}
677 
678 	ret = ldb_build_mod_req(&down_req, ldb, ac,
679 				msg,
680 				req->controls,
681 				ac, resolve_oids_callback,
682 				req);
683 	LDB_REQ_SET_LOCATION(down_req);
684 	if (ret != LDB_SUCCESS) {
685 		return ret;
686 	}
687 
688 	/* go on with the call chain */
689 	return ldb_next_request(module, down_req);
690 }
691 
692 static const struct ldb_module_ops ldb_resolve_oids_module_ops = {
693 	.name		= "resolve_oids",
694 	.search		= resolve_oids_search,
695 	.add		= resolve_oids_add,
696 	.modify		= resolve_oids_modify,
697 };
698 
699 
ldb_resolve_oids_module_init(const char * version)700 int ldb_resolve_oids_module_init(const char *version)
701 {
702 	LDB_MODULE_CHECK_VERSION(version);
703 	return ldb_register_module(&ldb_resolve_oids_module_ops);
704 }
705