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