ldb-samba: ldif_read_objectSid avoids VLA
[samba.git] / source4 / dsdb / schema / schema_convert_to_ol.c
1 /*
2    schema conversion routines
3
4    Copyright (C) Andrew Bartlett 2006-2008
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
21 #include "includes.h"
22 #include "ldb.h"
23 #include "dsdb/samdb/samdb.h"
24 #include "system/locale.h"
25
26 #undef strcasecmp
27
28 #define SEPARATOR "\n  "
29
30 struct attr_map {
31         char *old_attr;
32         char *new_attr;
33 };
34
35 struct oid_map {
36         char *old_oid;
37         char *new_oid;
38 };
39
40 static char *print_schema_recursive(char *append_to_string, struct dsdb_schema *schema, const char *print_class,
41                                     enum dsdb_schema_convert_target target,
42                                     const char **attrs_skip, const struct attr_map *attr_map, const struct oid_map *oid_map)
43 {
44         char *out = append_to_string;
45         const struct dsdb_class *objectclass;
46         objectclass = dsdb_class_by_lDAPDisplayName(schema, print_class);
47         if (!objectclass) {
48                 DEBUG(0, ("Cannot find class %s in schema\n", print_class));
49                 return NULL;
50         }
51
52         /* We have been asked to skip some attributes/objectClasses */
53         if (attrs_skip == NULL || !str_list_check_ci(attrs_skip, objectclass->lDAPDisplayName)) {
54                 TALLOC_CTX *mem_ctx = talloc_new(append_to_string);
55                 const char *name = objectclass->lDAPDisplayName;
56                 const char *oid = objectclass->governsID_oid;
57                 const char *subClassOf = objectclass->subClassOf;
58                 int objectClassCategory = objectclass->objectClassCategory;
59                 const char **must;
60                 const char **may;
61                 char *schema_entry = NULL;
62                 struct ldb_val objectclass_name_as_ldb_val = data_blob_string_const(objectclass->lDAPDisplayName);
63                 struct ldb_message_element objectclass_name_as_el = {
64                         .name = "objectClass",
65                         .num_values = 1,
66                         .values = &objectclass_name_as_ldb_val
67                 };
68                 unsigned int j;
69                 unsigned int attr_idx;
70
71                 if (!mem_ctx) {
72                         DEBUG(0, ("Failed to create new talloc context\n"));
73                         return NULL;
74                 }
75
76                 /* We might have been asked to remap this oid, due to a conflict */
77                 for (j=0; oid_map && oid_map[j].old_oid; j++) {
78                         if (strcasecmp(oid, oid_map[j].old_oid) == 0) {
79                                 oid =  oid_map[j].new_oid;
80                                 break;
81                         }
82                 }
83
84                 /* We might have been asked to remap this name, due to a conflict */
85                 for (j=0; name && attr_map && attr_map[j].old_attr; j++) {
86                         if (strcasecmp(name, attr_map[j].old_attr) == 0) {
87                                 name =  attr_map[j].new_attr;
88                                 break;
89                         }
90                 }
91
92                 /* We might have been asked to remap this subClassOf, due to a conflict */
93                 for (j=0; subClassOf && attr_map && attr_map[j].old_attr; j++) {
94                         if (strcasecmp(subClassOf, attr_map[j].old_attr) == 0) {
95                                 subClassOf =  attr_map[j].new_attr;
96                                 break;
97                         }
98                 }
99
100                 may = dsdb_full_attribute_list(mem_ctx, schema, &objectclass_name_as_el, DSDB_SCHEMA_ALL_MAY);
101
102                 for (j=0; may && may[j]; j++) {
103                         /* We might have been asked to remap this name, due to a conflict */
104                         for (attr_idx=0; attr_map && attr_map[attr_idx].old_attr; attr_idx++) {
105                                 if (strcasecmp(may[j], attr_map[attr_idx].old_attr) == 0) {
106                                         may[j] =  attr_map[attr_idx].new_attr;
107                                         break;
108                                 }
109                         }
110                 }
111
112                 must = dsdb_full_attribute_list(mem_ctx, schema, &objectclass_name_as_el, DSDB_SCHEMA_ALL_MUST);
113
114                 for (j=0; must && must[j]; j++) {
115                         /* We might have been asked to remap this name, due to a conflict */
116                         for (attr_idx=0; attr_map && attr_map[attr_idx].old_attr; attr_idx++) {
117                                 if (strcasecmp(must[j], attr_map[attr_idx].old_attr) == 0) {
118                                         must[j] =  attr_map[attr_idx].new_attr;
119                                         break;
120                                 }
121                         }
122                 }
123
124                 schema_entry = schema_class_description(mem_ctx, target,
125                                                         SEPARATOR,
126                                                         oid,
127                                                         name,
128                                                         NULL,
129                                                         subClassOf,
130                                                         objectClassCategory,
131                                                         must,
132                                                         may,
133                                                         NULL);
134                 if (schema_entry == NULL) {
135                         talloc_free(mem_ctx);
136                         DEBUG(0, ("failed to generate schema description for %s\n", name));
137                         return NULL;
138                 }
139
140                 switch (target) {
141                 case TARGET_OPENLDAP:
142                         out = talloc_asprintf_append(out, "objectclass %s\n\n", schema_entry);
143                         break;
144                 case TARGET_FEDORA_DS:
145                         out = talloc_asprintf_append(out, "objectClasses: %s\n", schema_entry);
146                         break;
147                 default:
148                         talloc_free(mem_ctx);
149                         DEBUG(0,(__location__ " Wrong type of target %u!\n", (unsigned)target));
150                         return NULL;
151                 }
152                 talloc_free(mem_ctx);
153         }
154
155
156         for (objectclass=schema->classes; objectclass; objectclass = objectclass->next) {
157                 if (ldb_attr_cmp(objectclass->subClassOf, print_class) == 0
158                     && ldb_attr_cmp(objectclass->lDAPDisplayName, print_class) != 0) {
159                         out = print_schema_recursive(out, schema, objectclass->lDAPDisplayName,
160                                                      target, attrs_skip, attr_map, oid_map);
161                 }
162         }
163         return out;
164 }
165
166 /* Routine to linearise our internal schema into the format that
167    OpenLDAP and Fedora DS use for their backend.
168
169    The 'mappings' are of a format like:
170
171 #Standard OpenLDAP attributes
172 labeledURI
173 #The memberOf plugin provides this attribute
174 memberOf
175 #These conflict with OpenLDAP builtins
176 attributeTypes:samba4AttributeTypes
177 2.5.21.5:1.3.6.1.4.1.7165.4.255.7
178
179 */
180
181
182 char *dsdb_convert_schema_to_openldap(struct ldb_context *ldb, char *target_str, const char *mappings)
183 {
184         /* Read list of attributes to skip, OIDs to map */
185         TALLOC_CTX *mem_ctx = talloc_new(ldb);
186         char *line;
187         char *out;
188         const char **attrs_skip = NULL;
189         unsigned int num_skip = 0;
190         struct oid_map *oid_map = NULL;
191         unsigned int num_oid_maps = 0;
192         struct attr_map *attr_map = NULL;
193         unsigned int num_attr_maps = 0;
194         struct dsdb_attribute *attribute;
195         struct dsdb_schema *schema;
196         enum dsdb_schema_convert_target target;
197
198         char *next_line = talloc_strdup(mem_ctx, mappings);
199
200         if (!target_str || strcasecmp(target_str, "openldap") == 0) {
201                 target = TARGET_OPENLDAP;
202         } else if (strcasecmp(target_str, "fedora-ds") == 0) {
203                 target = TARGET_FEDORA_DS;
204         } else {
205                 talloc_free(mem_ctx);
206                 DEBUG(0, ("Invalid target type for schema conversion %s\n", target_str));
207                 return NULL;
208         }
209
210         /* The mappings are line-separated, and specify details such as OIDs to skip etc */
211         while (1) {
212                 line = next_line;
213                 next_line = strchr(line, '\n');
214                 if (!next_line) {
215                         break;
216                 }
217                 next_line[0] = '\0';
218                 next_line++;
219
220                 /* Blank Line */
221                 if (line[0] == '\0') {
222                         continue;
223                 }
224                 /* Comment */
225                 if (line[0] == '#') {
226                         continue;
227                 }
228
229                 if (isdigit(line[0])) {
230                         char *p = strchr(line, ':');
231                         if (!p) {
232                                 DEBUG(0, ("schema mapping file line has OID but no OID to map to: %s\n", line));
233                                 return NULL;
234                         }
235                         p[0] = '\0';
236                         p++;
237                         oid_map = talloc_realloc(mem_ctx, oid_map, struct oid_map, num_oid_maps + 2);
238                         trim_string(line, " ", " ");
239                         oid_map[num_oid_maps].old_oid = talloc_strdup(oid_map, line);
240                         trim_string(p, " ", " ");
241                         oid_map[num_oid_maps].new_oid = p;
242                         num_oid_maps++;
243                         oid_map[num_oid_maps].old_oid = NULL;
244                 } else {
245                         char *p = strchr(line, ':');
246                         if (p) {
247                                 /* remap attribute/objectClass */
248                                 p[0] = '\0';
249                                 p++;
250                                 attr_map = talloc_realloc(mem_ctx, attr_map, struct attr_map, num_attr_maps + 2);
251                                 trim_string(line, " ", " ");
252                                 attr_map[num_attr_maps].old_attr = talloc_strdup(attr_map, line);
253                                 trim_string(p, " ", " ");
254                                 attr_map[num_attr_maps].new_attr = p;
255                                 num_attr_maps++;
256                                 attr_map[num_attr_maps].old_attr = NULL;
257                         } else {
258                                 /* skip attribute/objectClass */
259                                 attrs_skip = talloc_realloc(mem_ctx, attrs_skip, const char *, num_skip + 2);
260                                 trim_string(line, " ", " ");
261                                 attrs_skip[num_skip] = talloc_strdup(attrs_skip, line);
262                                 num_skip++;
263                                 attrs_skip[num_skip] = NULL;
264                         }
265                 }
266         }
267
268         schema = dsdb_get_schema(ldb, mem_ctx);
269         if (!schema) {
270                 talloc_free(mem_ctx);
271                 DEBUG(0, ("No schema on ldb to convert!\n"));
272                 return NULL;
273         }
274
275         switch (target) {
276         case TARGET_OPENLDAP:
277                 out = talloc_strdup(mem_ctx, "");
278                 break;
279         case TARGET_FEDORA_DS:
280                 out = talloc_strdup(mem_ctx, "dn: cn=schema\n");
281                 break;
282         default:
283                 talloc_free(mem_ctx);
284                 DEBUG(0,(__location__ " Wrong type of target %u!\n", (unsigned)target));
285                 return NULL;
286         }
287
288         for (attribute=schema->attributes; attribute; attribute = attribute->next) {
289                 const char *name = attribute->lDAPDisplayName;
290                 const char *oid = attribute->attributeID_oid;
291                 const char *syntax = attribute->attributeSyntax_oid;
292                 const char *equality = NULL, *substring = NULL;
293                 bool single_value = attribute->isSingleValued;
294
295                 char *schema_entry = NULL;
296                 unsigned int j;
297
298                 /* We have been asked to skip some attributes/objectClasses */
299                 if (attrs_skip && str_list_check_ci(attrs_skip, name)) {
300                         continue;
301                 }
302
303                 /* We might have been asked to remap this oid, due to a conflict */
304                 for (j=0; oid && oid_map && oid_map[j].old_oid; j++) {
305                         if (strcasecmp(oid, oid_map[j].old_oid) == 0) {
306                                 oid =  oid_map[j].new_oid;
307                                 break;
308                         }
309                 }
310
311                 if (attribute->syntax) {
312                         /* We might have been asked to remap this oid,
313                          * due to a conflict, or lack of
314                          * implementation */
315                         syntax = attribute->syntax->ldap_oid;
316                         /* We might have been asked to remap this oid, due to a conflict */
317                         for (j=0; syntax && oid_map && oid_map[j].old_oid; j++) {
318                                 if (strcasecmp(syntax, oid_map[j].old_oid) == 0) {
319                                         syntax =  oid_map[j].new_oid;
320                                         break;
321                                 }
322                         }
323
324                         equality = attribute->syntax->equality;
325                         substring = attribute->syntax->substring;
326                 }
327
328                 /* We might have been asked to remap this name, due to a conflict */
329                 for (j=0; name && attr_map && attr_map[j].old_attr; j++) {
330                         if (strcasecmp(name, attr_map[j].old_attr) == 0) {
331                                 name =  attr_map[j].new_attr;
332                                 break;
333                         }
334                 }
335
336                 schema_entry = schema_attribute_description(mem_ctx,
337                                                             target,
338                                                             SEPARATOR,
339                                                             oid,
340                                                             name,
341                                                             equality,
342                                                             substring,
343                                                             syntax,
344                                                             single_value,
345                                                             false,
346                                                             NULL, NULL,
347                                                             NULL, NULL,
348                                                             false, false);
349
350                 if (schema_entry == NULL) {
351                         talloc_free(mem_ctx);
352                         DEBUG(0, ("failed to generate attribute description for %s\n", name));
353                         return NULL;
354                 }
355
356                 switch (target) {
357                 case TARGET_OPENLDAP:
358                         out = talloc_asprintf_append(out, "attributetype %s\n\n", schema_entry);
359                         break;
360                 case TARGET_FEDORA_DS:
361                         out = talloc_asprintf_append(out, "attributeTypes: %s\n", schema_entry);
362                         break;
363                 default:
364                         talloc_free(mem_ctx);
365                         DEBUG(0,(__location__ " Wrong type of target %u!\n", (unsigned)target));
366                         return NULL;
367                 }
368         }
369
370         out = print_schema_recursive(out, schema, "top", target, attrs_skip, attr_map, oid_map);
371
372         talloc_steal(ldb, out);
373         talloc_free(mem_ctx);
374
375         return out;
376 }
377