Coverage Report

Created: 2026-04-01 06:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/source4/dsdb/schema/schema_query.c
Line
Count
Source
1
/*
2
   Unix SMB/CIFS Implementation.
3
   DSDB schema header
4
5
   Copyright (C) Stefan Metzmacher <metze@samba.org> 2006-2007
6
   Copyright (C) Andrew Bartlett <abartlet@samba.org> 2006-2008
7
8
   This program is free software; you can redistribute it and/or modify
9
   it under the terms of the GNU General Public License as published by
10
   the Free Software Foundation; either version 3 of the License, or
11
   (at your option) any later version.
12
13
   This program is distributed in the hope that it will be useful,
14
   but WITHOUT ANY WARRANTY; without even the implied warranty of
15
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
   GNU General Public License for more details.
17
18
   You should have received a copy of the GNU General Public License
19
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
21
*/
22
23
#include "includes.h"
24
#include "dsdb/samdb/samdb.h"
25
#include <ldb_module.h>
26
#include "lib/util/binsearch.h"
27
#include "lib/util/tsort.h"
28
#include "util/dlinklist.h"
29
30
#undef strcasecmp
31
#undef strncasecmp
32
33
static const char **dsdb_full_attribute_list_internal(TALLOC_CTX *mem_ctx,
34
                  const struct dsdb_schema *schema,
35
                  const char **class_list,
36
                  enum dsdb_attr_list_query query);
37
38
static int uint32_cmp(uint32_t c1, uint32_t c2)
39
0
{
40
0
  if (c1 == c2) return 0;
41
0
  return c1 > c2 ? 1 : -1;
42
0
}
43
44
static int strcasecmp_with_ldb_val(const struct ldb_val *target, const char *str)
45
0
{
46
0
  int ret = strncasecmp((const char *)target->data, str, target->length);
47
0
  if (ret == 0) {
48
0
    size_t len = strlen(str);
49
0
    if (target->length > len) {
50
0
      if (target->data[len] == 0) {
51
0
        return 0;
52
0
      }
53
0
      return 1;
54
0
    }
55
0
    if (target->length < len) {
56
0
      return -1;
57
0
    }
58
0
  }
59
0
  return ret;
60
0
}
61
62
const struct dsdb_attribute *dsdb_attribute_by_attributeID_id(const struct dsdb_schema *schema,
63
                    uint32_t id)
64
0
{
65
0
  struct dsdb_attribute *c;
66
67
  /*
68
   * 0xFFFFFFFF is used as value when no mapping table is available,
69
   * so don't try to match with it
70
   */
71
0
  if (id == 0xFFFFFFFF) return NULL;
72
73
  /* check for msDS-IntId type attribute */
74
0
  if (dsdb_pfm_get_attid_type(id) == DSDB_ATTID_TYPE_INTID) {
75
0
    BINARY_ARRAY_SEARCH_P(schema->attributes_by_msDS_IntId,
76
0
              schema->num_int_id_attr, msDS_IntId, id, uint32_cmp, c);
77
0
    return c;
78
0
  }
79
80
0
  BINARY_ARRAY_SEARCH_P(schema->attributes_by_attributeID_id,
81
0
            schema->num_attributes, attributeID_id, id, uint32_cmp, c);
82
0
  return c;
83
0
}
84
85
const struct dsdb_attribute *dsdb_attribute_by_attributeID_oid(const struct dsdb_schema *schema,
86
                     const char *oid)
87
0
{
88
0
  struct dsdb_attribute *c;
89
90
0
  if (!oid) return NULL;
91
92
0
  BINARY_ARRAY_SEARCH_P(schema->attributes_by_attributeID_oid,
93
0
            schema->num_attributes, attributeID_oid, oid, strcasecmp, c);
94
0
  return c;
95
0
}
96
97
const struct dsdb_attribute *dsdb_attribute_by_lDAPDisplayName(const struct dsdb_schema *schema,
98
                     const char *name)
99
0
{
100
0
  struct dsdb_attribute *c;
101
102
0
  if (!name) return NULL;
103
104
0
  BINARY_ARRAY_SEARCH_P(schema->attributes_by_lDAPDisplayName,
105
0
            schema->num_attributes, lDAPDisplayName, name, strcasecmp, c);
106
0
  return c;
107
0
}
108
109
const struct dsdb_attribute *dsdb_attribute_by_lDAPDisplayName_ldb_val(const struct dsdb_schema *schema,
110
                       const struct ldb_val *name)
111
0
{
112
0
  struct dsdb_attribute *a;
113
114
0
  if (!name) return NULL;
115
116
0
  BINARY_ARRAY_SEARCH_P(schema->attributes_by_lDAPDisplayName,
117
0
            schema->num_attributes, lDAPDisplayName, name, strcasecmp_with_ldb_val, a);
118
0
  return a;
119
0
}
120
121
const struct dsdb_attribute *dsdb_attribute_by_linkID(const struct dsdb_schema *schema,
122
                  int linkID)
123
0
{
124
0
  struct dsdb_attribute *c;
125
126
0
  BINARY_ARRAY_SEARCH_P(schema->attributes_by_linkID,
127
0
            schema->num_attributes, linkID, linkID, uint32_cmp, c);
128
0
  return c;
129
0
}
130
131
const struct dsdb_attribute *dsdb_attribute_by_cn_ldb_val(const struct dsdb_schema *schema,
132
                const struct ldb_val *cn)
133
0
{
134
0
  struct dsdb_attribute *c;
135
136
0
  BINARY_ARRAY_SEARCH_P(schema->attributes_by_cn,
137
0
            schema->num_attributes, cn, cn, strcasecmp_with_ldb_val, c);
138
0
  return c;
139
0
}
140
141
const struct dsdb_class *dsdb_class_by_governsID_id(const struct dsdb_schema *schema,
142
                uint32_t id)
143
0
{
144
0
  struct dsdb_class *c;
145
146
  /*
147
   * 0xFFFFFFFF is used as value when no mapping table is available,
148
   * so don't try to match with it
149
   */
150
0
  if (id == 0xFFFFFFFF) return NULL;
151
152
0
  BINARY_ARRAY_SEARCH_P(schema->classes_by_governsID_id,
153
0
            schema->num_classes, governsID_id, id, uint32_cmp, c);
154
0
  return c;
155
0
}
156
157
const struct dsdb_class *dsdb_class_by_governsID_oid(const struct dsdb_schema *schema,
158
                 const char *oid)
159
0
{
160
0
  struct dsdb_class *c;
161
0
  if (!oid) return NULL;
162
0
  BINARY_ARRAY_SEARCH_P(schema->classes_by_governsID_oid,
163
0
            schema->num_classes, governsID_oid, oid, strcasecmp, c);
164
0
  return c;
165
0
}
166
167
const struct dsdb_class *dsdb_class_by_lDAPDisplayName(const struct dsdb_schema *schema,
168
                   const char *name)
169
0
{
170
0
  struct dsdb_class *c;
171
0
  if (!name) return NULL;
172
0
  BINARY_ARRAY_SEARCH_P(schema->classes_by_lDAPDisplayName,
173
0
            schema->num_classes, lDAPDisplayName, name, strcasecmp, c);
174
0
  return c;
175
0
}
176
177
const struct dsdb_class *dsdb_class_by_lDAPDisplayName_ldb_val(const struct dsdb_schema *schema,
178
                     const struct ldb_val *name)
179
0
{
180
0
  struct dsdb_class *c;
181
0
  if (!name) return NULL;
182
0
  BINARY_ARRAY_SEARCH_P(schema->classes_by_lDAPDisplayName,
183
0
            schema->num_classes, lDAPDisplayName, name, strcasecmp_with_ldb_val, c);
184
0
  return c;
185
0
}
186
187
const struct dsdb_class *dsdb_class_by_cn_ldb_val(const struct dsdb_schema *schema,
188
              const struct ldb_val *cn)
189
0
{
190
0
  struct dsdb_class *c;
191
0
  if (!cn) return NULL;
192
0
  BINARY_ARRAY_SEARCH_P(schema->classes_by_cn,
193
0
            schema->num_classes, cn, cn, strcasecmp_with_ldb_val, c);
194
0
  return c;
195
0
}
196
197
const char *dsdb_lDAPDisplayName_by_id(const struct dsdb_schema *schema,
198
               uint32_t id)
199
0
{
200
0
  const struct dsdb_attribute *a;
201
0
  const struct dsdb_class *c;
202
203
0
  a = dsdb_attribute_by_attributeID_id(schema, id);
204
0
  if (a) {
205
0
    return a->lDAPDisplayName;
206
0
  }
207
208
0
  c = dsdb_class_by_governsID_id(schema, id);
209
0
  if (c) {
210
0
    return c->lDAPDisplayName;
211
0
  }
212
213
0
  return NULL;
214
0
}
215
216
/**
217
    Return a list of linked attributes, in lDAPDisplayName format.
218
219
    This may be used to determine if a modification would require
220
    backlinks to be updated, for example
221
*/
222
223
WERROR dsdb_linked_attribute_lDAPDisplayName_list(const struct dsdb_schema *schema, TALLOC_CTX *mem_ctx, const char ***attr_list_ret)
224
0
{
225
0
  const char **attr_list = NULL;
226
0
  struct dsdb_attribute *cur;
227
0
  unsigned int i = 0;
228
0
  for (cur = schema->attributes; cur; cur = cur->next) {
229
0
    if (cur->linkID == 0) continue;
230
231
0
    attr_list = talloc_realloc(mem_ctx, attr_list, const char *, i+2);
232
0
    if (!attr_list) {
233
0
      return WERR_NOT_ENOUGH_MEMORY;
234
0
    }
235
0
    attr_list[i] = cur->lDAPDisplayName;
236
0
    i++;
237
0
  }
238
0
  if (attr_list != NULL && attr_list[i] != NULL) {
239
0
    attr_list[i] = NULL;
240
0
  }
241
0
  *attr_list_ret = attr_list;
242
0
  return WERR_OK;
243
0
}
244
245
const char **merge_attr_list(TALLOC_CTX *mem_ctx,
246
           const char **attrs, const char * const*new_attrs)
247
0
{
248
0
  const char **ret_attrs;
249
0
  unsigned int i;
250
0
  size_t new_len, new_attr_len, orig_len = str_list_length(attrs);
251
0
  if (new_attrs == NULL || new_attrs[0] == NULL) {
252
0
    return attrs;
253
0
  }
254
0
  new_attr_len = str_list_length(new_attrs);
255
256
0
  ret_attrs = talloc_realloc(mem_ctx,
257
0
           attrs, const char *, orig_len + new_attr_len + 1);
258
0
  if (ret_attrs) {
259
0
    for (i = 0; i < new_attr_len; i++) {
260
0
      ret_attrs[orig_len + i] = new_attrs[i];
261
0
    }
262
0
    new_len = orig_len + new_attr_len;
263
264
0
    ret_attrs[new_len] = NULL;
265
0
  }
266
267
0
  return ret_attrs;
268
0
}
269
270
/*
271
  Return a merged list of the attributes of exactly one class (not
272
  considering subclasses, auxiliary classes etc)
273
*/
274
275
const char **dsdb_attribute_list(TALLOC_CTX *mem_ctx, const struct dsdb_class *sclass, enum dsdb_attr_list_query query)
276
0
{
277
0
  const char **attr_list = NULL;
278
0
  switch (query) {
279
0
  case DSDB_SCHEMA_ALL_MAY:
280
0
    attr_list = merge_attr_list(mem_ctx, attr_list, sclass->mayContain);
281
0
    attr_list = merge_attr_list(mem_ctx, attr_list, sclass->systemMayContain);
282
0
    break;
283
284
0
  case DSDB_SCHEMA_ALL_MUST:
285
0
    attr_list = merge_attr_list(mem_ctx, attr_list, sclass->mustContain);
286
0
    attr_list = merge_attr_list(mem_ctx, attr_list, sclass->systemMustContain);
287
0
    break;
288
289
0
  case DSDB_SCHEMA_SYS_MAY:
290
0
    attr_list = merge_attr_list(mem_ctx, attr_list, sclass->systemMayContain);
291
0
    break;
292
293
0
  case DSDB_SCHEMA_SYS_MUST:
294
0
    attr_list = merge_attr_list(mem_ctx, attr_list, sclass->systemMustContain);
295
0
    break;
296
297
0
  case DSDB_SCHEMA_MAY:
298
0
    attr_list = merge_attr_list(mem_ctx, attr_list, sclass->mayContain);
299
0
    break;
300
301
0
  case DSDB_SCHEMA_MUST:
302
0
    attr_list = merge_attr_list(mem_ctx, attr_list, sclass->mustContain);
303
0
    break;
304
305
0
  case DSDB_SCHEMA_ALL:
306
0
    attr_list = merge_attr_list(mem_ctx, attr_list, sclass->mayContain);
307
0
    attr_list = merge_attr_list(mem_ctx, attr_list, sclass->systemMayContain);
308
0
    attr_list = merge_attr_list(mem_ctx, attr_list, sclass->mustContain);
309
0
    attr_list = merge_attr_list(mem_ctx, attr_list, sclass->systemMustContain);
310
0
    break;
311
0
  }
312
0
  return attr_list;
313
0
}
314
315
static const char **attribute_list_from_class(TALLOC_CTX *mem_ctx,
316
                const struct dsdb_schema *schema,
317
                const struct dsdb_class *sclass,
318
                enum dsdb_attr_list_query query)
319
0
{
320
0
  const char **this_class_list;
321
0
  const char **system_recursive_list;
322
0
  const char **recursive_list;
323
0
  const char **attr_list;
324
325
0
  this_class_list = dsdb_attribute_list(mem_ctx, sclass, query);
326
327
0
  recursive_list = dsdb_full_attribute_list_internal(mem_ctx, schema,
328
0
                 sclass->systemAuxiliaryClass,
329
0
                 query);
330
331
0
  system_recursive_list = dsdb_full_attribute_list_internal(mem_ctx, schema,
332
0
                  sclass->auxiliaryClass,
333
0
                  query);
334
335
0
  attr_list = this_class_list;
336
0
  attr_list = merge_attr_list(mem_ctx, attr_list, recursive_list);
337
0
  attr_list = merge_attr_list(mem_ctx, attr_list, system_recursive_list);
338
0
  return attr_list;
339
0
}
340
341
/* Return a full attribute list for a given class list
342
343
   Via attribute_list_from_class() this calls itself when recursing on auxiliary classes
344
 */
345
static const char **dsdb_full_attribute_list_internal(TALLOC_CTX *mem_ctx,
346
                  const struct dsdb_schema *schema,
347
                  const char **class_list,
348
                  enum dsdb_attr_list_query query)
349
0
{
350
0
  unsigned int i;
351
0
  const char **attr_list = NULL;
352
353
0
  for (i=0; class_list && class_list[i]; i++) {
354
0
    const char **sclass_list
355
0
      = attribute_list_from_class(mem_ctx, schema,
356
0
                dsdb_class_by_lDAPDisplayName(schema, class_list[i]),
357
0
                query);
358
359
0
    attr_list = merge_attr_list(mem_ctx, attr_list, sclass_list);
360
0
  }
361
0
  return attr_list;
362
0
}
363
364
/* Return a full attribute list for a given class list (as a ldb_message_element)
365
366
   Using the ldb_message_element ensures we do length-limited
367
   comparisons, rather than casting the possibly-unterminated string
368
369
   Via attribute_list_from_class() this calls
370
   dsdb_full_attribute_list_internal() when recursing on auxiliary classes
371
 */
372
static const char **dsdb_full_attribute_list_internal_el(TALLOC_CTX *mem_ctx,
373
               const struct dsdb_schema *schema,
374
               const struct ldb_message_element *el,
375
               enum dsdb_attr_list_query query)
376
0
{
377
0
  unsigned int i;
378
0
  const char **attr_list = NULL;
379
380
0
  for (i=0; i < el->num_values; i++) {
381
0
    const char **sclass_list
382
0
      = attribute_list_from_class(mem_ctx, schema,
383
0
                dsdb_class_by_lDAPDisplayName_ldb_val(schema, &el->values[i]),
384
0
                query);
385
386
0
    attr_list = merge_attr_list(mem_ctx, attr_list, sclass_list);
387
0
  }
388
0
  return attr_list;
389
0
}
390
391
static int qsort_string(const char **s1, const char **s2)
392
0
{
393
0
  return strcasecmp(*s1, *s2);
394
0
}
395
396
/* Helper function to remove duplicates from the attribute list to be returned */
397
static const char **dedup_attr_list(const char **attr_list)
398
0
{
399
0
  size_t new_len = str_list_length(attr_list);
400
  /* Remove duplicates */
401
0
  if (new_len > 1) {
402
0
    size_t i;
403
0
    TYPESAFE_QSORT(attr_list, new_len, qsort_string);
404
405
0
    for (i=1; new_len > 0 && i < new_len; i++) {
406
0
      const char **val1 = &attr_list[i-1];
407
0
      const char **val2 = &attr_list[i];
408
0
      if (ldb_attr_cmp(*val1, *val2) == 0) {
409
0
        memmove(val1, val2, (new_len - i) * sizeof( *attr_list));
410
0
        attr_list[new_len-1] = NULL;
411
0
        new_len--;
412
0
        i--;
413
0
      }
414
0
    }
415
0
  }
416
0
  return attr_list;
417
0
}
418
419
/* Return a full attribute list for a given class list (as a ldb_message_element)
420
421
   Using the ldb_message_element ensures we do length-limited
422
   comparisons, rather than casting the possibly-unterminated string
423
424
   The result contains only unique values
425
 */
426
const char **dsdb_full_attribute_list(TALLOC_CTX *mem_ctx,
427
              const struct dsdb_schema *schema,
428
              const struct ldb_message_element *class_list,
429
              enum dsdb_attr_list_query query)
430
0
{
431
0
  const char **attr_list = dsdb_full_attribute_list_internal_el(mem_ctx, schema, class_list, query);
432
0
  return dedup_attr_list(attr_list);
433
0
}
434
435
/* Return the schemaIDGUID of a class */
436
437
const struct GUID *class_schemaid_guid_by_lDAPDisplayName(const struct dsdb_schema *schema,
438
                                                          const char *name)
439
0
{
440
0
        const struct dsdb_class *object_class = dsdb_class_by_lDAPDisplayName(schema, name);
441
0
        if (!object_class)
442
0
                return NULL;
443
444
0
        return &object_class->schemaIDGUID;
445
0
}
446
447
const struct GUID *attribute_schemaid_guid_by_lDAPDisplayName(const struct dsdb_schema *schema,
448
                    const char *name)
449
0
{
450
0
        const struct dsdb_attribute *attr = dsdb_attribute_by_lDAPDisplayName(schema, name);
451
0
        if (!attr)
452
0
                return NULL;
453
454
0
        return &attr->schemaIDGUID;
455
0
}
456
457
/*
458
 * Sort a "objectClass" attribute (LDB message element "objectclass_element")
459
 * into correct order and validate that all object classes specified actually
460
 * exist in the schema.
461
 * The output is written in an existing LDB message element
462
 * "out_objectclass_element" where the values will be allocated on "mem_ctx".
463
 */
464
int dsdb_sort_objectClass_attr(struct ldb_context *ldb,
465
             const struct dsdb_schema *schema,
466
             const struct ldb_message_element *objectclass_element,
467
             TALLOC_CTX *mem_ctx,
468
             struct ldb_message_element *out_objectclass_element)
469
0
{
470
0
  unsigned int i, lowest;
471
0
  struct class_list {
472
0
    struct class_list *prev, *next;
473
0
    const struct dsdb_class *objectclass;
474
0
  } *unsorted = NULL, *sorted = NULL, *current = NULL,
475
0
    *poss_parent = NULL, *new_parent = NULL,
476
0
    *current_lowest = NULL, *current_lowest_struct = NULL;
477
0
  struct ldb_message_element *el;
478
0
  TALLOC_CTX *tmp_mem_ctx;
479
480
0
  tmp_mem_ctx = talloc_new(mem_ctx);
481
0
  if (tmp_mem_ctx == NULL) {
482
0
    return ldb_oom(ldb);
483
0
  }
484
485
  /*
486
   * DESIGN:
487
   *
488
   * We work on 4 different 'bins' (implemented here as linked lists):
489
   *
490
   * * sorted:       the eventual list, in the order we wish to push
491
   *                 into the database.  This is the only ordered list.
492
   *
493
   * * parent_class: The current parent class 'bin' we are
494
   *                 trying to find subclasses for
495
   *
496
   * * subclass:     The subclasses we have found so far
497
   *
498
   * * unsorted:     The remaining objectClasses
499
   *
500
   * The process is a matter of filtering objectClasses up from
501
   * unsorted into sorted.  Order is irrelevant in the later 3 'bins'.
502
   *
503
   * We start with 'top' (found and promoted to parent_class
504
   * initially).  Then we find (in unsorted) all the direct
505
   * subclasses of 'top'.  parent_classes is concatenated onto
506
   * the end of 'sorted', and subclass becomes the list in
507
   * parent_class.
508
   *
509
   * We then repeat, until we find no more subclasses.  Any left
510
   * over classes are added to the end.
511
   *
512
   */
513
514
  /*
515
   * Firstly, dump all the "objectClass" values into the unsorted bin,
516
   * except for 'top', which is special
517
   */
518
0
  for (i=0; i < objectclass_element->num_values; i++) {
519
0
    current = talloc(tmp_mem_ctx, struct class_list);
520
0
    if (!current) {
521
0
      talloc_free(tmp_mem_ctx);
522
0
      return ldb_oom(ldb);
523
0
    }
524
0
    current->objectclass = dsdb_class_by_lDAPDisplayName_ldb_val(schema, &objectclass_element->values[i]);
525
0
    if (!current->objectclass) {
526
0
      ldb_asprintf_errstring(ldb, "objectclass %.*s is not a valid objectClass in schema",
527
0
                 (int)objectclass_element->values[i].length, (const char *)objectclass_element->values[i].data);
528
      /* This looks weird, but windows apparently returns this for invalid objectClass values */
529
0
      talloc_free(tmp_mem_ctx);
530
0
      return LDB_ERR_NO_SUCH_ATTRIBUTE;
531
0
    } else if (current->objectclass->isDefunct) {
532
0
      ldb_asprintf_errstring(ldb, "objectclass %.*s marked as isDefunct objectClass in schema - not valid for new objects",
533
0
                 (int)objectclass_element->values[i].length, (const char *)objectclass_element->values[i].data);
534
      /* This looks weird, but windows apparently returns this for invalid objectClass values */
535
0
      talloc_free(tmp_mem_ctx);
536
0
      return LDB_ERR_NO_SUCH_ATTRIBUTE;
537
0
    }
538
539
    /* Don't add top to list, we will do that later */
540
0
    if (ldb_attr_cmp("top", current->objectclass->lDAPDisplayName) != 0) {
541
0
      DLIST_ADD_END(unsorted, current);
542
0
    }
543
0
  }
544
545
546
  /* Add top here, to prevent duplicates */
547
0
  current = talloc(tmp_mem_ctx, struct class_list);
548
0
  current->objectclass = dsdb_class_by_lDAPDisplayName(schema, "top");
549
0
  DLIST_ADD_END(sorted, current);
550
551
  /* For each object: find parent chain */
552
0
  for (current = unsorted; current != NULL; current = current->next) {
553
0
    for (poss_parent = unsorted; poss_parent; poss_parent = poss_parent->next) {
554
0
      if (ldb_attr_cmp(poss_parent->objectclass->lDAPDisplayName, current->objectclass->subClassOf) == 0) {
555
0
        break;
556
0
      }
557
0
    }
558
    /* If we didn't get to the end of the list, we need to add this parent */
559
0
    if (poss_parent || (ldb_attr_cmp("top", current->objectclass->subClassOf) == 0)) {
560
0
      continue;
561
0
    }
562
563
0
    new_parent = talloc(tmp_mem_ctx, struct class_list);
564
0
    new_parent->objectclass = dsdb_class_by_lDAPDisplayName(schema, current->objectclass->subClassOf);
565
0
    DLIST_ADD_END(unsorted, new_parent);
566
0
  }
567
568
  /* For each object: order by hierarchy */
569
0
  while (unsorted != NULL) {
570
0
    lowest = UINT_MAX;
571
0
    current_lowest = current_lowest_struct = NULL;
572
0
    for (current = unsorted; current != NULL; current = current->next) {
573
0
      if (current->objectclass->subClass_order <= lowest) {
574
        /*
575
         * According to MS-ADTS 3.1.1.1.4 structural
576
         * and 88 object classes are always listed after
577
         * the other class types in a subclass hierarchy
578
         */
579
0
        if (current->objectclass->objectClassCategory > 1) {
580
0
          current_lowest = current;
581
0
        } else {
582
0
          current_lowest_struct = current;
583
0
        }
584
0
        lowest = current->objectclass->subClass_order;
585
0
      }
586
0
    }
587
0
    if (current_lowest == NULL) {
588
0
      current_lowest = current_lowest_struct;
589
0
    }
590
591
0
    if (current_lowest != NULL) {
592
0
      DLIST_REMOVE(unsorted,current_lowest);
593
0
      DLIST_ADD_END(sorted,current_lowest);
594
0
    }
595
0
  }
596
597
  /* Now rebuild the sorted "objectClass" message element */
598
0
  el = out_objectclass_element;
599
600
0
  el->flags = objectclass_element->flags;
601
0
  el->name = talloc_strdup(mem_ctx, objectclass_element->name);
602
0
  if (el->name == NULL) {
603
0
    talloc_free(tmp_mem_ctx);
604
0
    return ldb_oom(ldb);
605
0
  }
606
0
  el->num_values = 0;
607
0
  el->values = NULL;
608
0
  for (current = sorted; current != NULL; current = current->next) {
609
0
    el->values = talloc_realloc(mem_ctx, el->values,
610
0
              struct ldb_val, el->num_values + 1);
611
0
    if (el->values == NULL) {
612
0
      talloc_free(tmp_mem_ctx);
613
0
      return ldb_oom(ldb);
614
0
    }
615
0
    el->values[el->num_values] = data_blob_string_const(current->objectclass->lDAPDisplayName);
616
617
0
    ++(el->num_values);
618
0
  }
619
620
0
  talloc_free(tmp_mem_ctx);
621
0
  return LDB_SUCCESS;
622
0
}