939365840b902a3077cae2f1308c14ff555e7ab6
[ddiss/samba.git] / source4 / rpc_server / dnsserver / dnsdb.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    DNS Server
5
6    Copyright (C) Amitay Isaacs 2011
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 #include "includes.h"
23 #include "dnsserver.h"
24 #include "lib/util/dlinklist.h"
25 #include "librpc/gen_ndr/ndr_dnsp.h"
26 #include "dsdb/samdb/samdb.h"
27
28 /* There are only 2 fixed partitions for DNS */
29 struct dnsserver_partition *dnsserver_db_enumerate_partitions(TALLOC_CTX *mem_ctx,
30                                                         struct dnsserver_serverinfo *serverinfo,
31                                                         struct ldb_context *samdb)
32 {
33         struct dnsserver_partition *partitions, *p;
34
35         partitions = NULL;
36
37         /* Domain partition */
38         p = talloc_zero(mem_ctx, struct dnsserver_partition);
39         if (p == NULL) {
40                 goto failed;
41         }
42
43         p->partition_dn = ldb_dn_new(p, samdb, serverinfo->pszDomainDirectoryPartition);
44         if (p->partition_dn == NULL) {
45                 goto failed;
46         }
47
48         p->pszDpFqdn = samdb_dn_to_dns_domain(p, p->partition_dn);
49         p->dwDpFlags = DNS_DP_AUTOCREATED | DNS_DP_DOMAIN_DEFAULT | DNS_DP_ENLISTED;
50         p->is_forest = false;
51
52         DLIST_ADD_END(partitions, p, NULL);
53
54         /* Forest Partition */
55         p = talloc_zero(mem_ctx, struct dnsserver_partition);
56         if (p == NULL) {
57                 goto failed;
58         }
59
60         p->partition_dn = ldb_dn_new(p, samdb, serverinfo->pszForestDirectoryPartition);
61         if (p->partition_dn == NULL) {
62                 goto failed;
63         }
64
65         p->pszDpFqdn = samdb_dn_to_dns_domain(p, p->partition_dn);
66         p->dwDpFlags = DNS_DP_AUTOCREATED | DNS_DP_FOREST_DEFAULT | DNS_DP_ENLISTED;
67         p->is_forest = true;
68
69         DLIST_ADD_END(partitions, p, NULL);
70
71         return partitions;
72
73 failed:
74         return NULL;
75
76 }
77
78
79 /* Search for all dnsZone records */
80 struct dnsserver_zone *dnsserver_db_enumerate_zones(TALLOC_CTX *mem_ctx,
81                                                 struct ldb_context *samdb,
82                                                 struct dnsserver_partition *p)
83 {
84         TALLOC_CTX *tmp_ctx;
85         const char * const attrs[] = {"name", NULL};
86         struct ldb_dn *dn;
87         struct ldb_result *res;
88         struct dnsserver_zone *zones, *z;
89         int i, ret;
90
91         tmp_ctx = talloc_new(mem_ctx);
92         if (tmp_ctx == NULL) {
93                 return NULL;
94         }
95
96         dn = ldb_dn_copy(tmp_ctx, p->partition_dn);
97         if (dn == NULL) {
98                 goto failed;
99         }
100         if (!ldb_dn_add_child_fmt(dn, "CN=MicrosoftDNS")) {
101                 goto failed;
102         }
103
104         ret = ldb_search(samdb, tmp_ctx, &res, dn, LDB_SCOPE_SUBTREE,
105                           attrs, "(objectClass=dnsZone)");
106         if (ret != LDB_SUCCESS) {
107                 DEBUG(0, ("dnsserver: Failed to find DNS Zones in %s\n",
108                         ldb_dn_get_linearized(dn)));
109                 goto failed;
110         }
111
112         zones = NULL;
113         for(i=0; i<res->count; i++) {
114                 char *name;
115                 z = talloc_zero(mem_ctx, struct dnsserver_zone);
116                 if (z == NULL) {
117                         goto failed;
118                 }
119
120                 z->partition = p;
121                 name = talloc_strdup(z,
122                                 ldb_msg_find_attr_as_string(res->msgs[i], "name", NULL));
123                 if (strcmp(name, "RootDNSServers") == 0) {
124                         talloc_free(name);
125                         z->name = talloc_strdup(z, ".");
126                 } else {
127                         z->name = name;
128                 }
129                 z->zone_dn = talloc_steal(z, res->msgs[i]->dn);
130
131                 DLIST_ADD_END(zones, z, NULL);
132                 DEBUG(2, ("dnsserver: Found DNS zone %s\n", z->name));
133         }
134
135         return zones;
136
137 failed:
138         talloc_free(tmp_ctx);
139         return NULL;
140 }
141
142
143 /* Find DNS partition information */
144 struct dnsserver_partition_info *dnsserver_db_partition_info(TALLOC_CTX *mem_ctx,
145                                                         struct ldb_context *samdb,
146                                                         struct dnsserver_partition *p)
147 {
148         const char * const attrs[] = { "instanceType", "msDs-masteredBy", NULL };
149         const char * const attrs_none[] = { NULL };
150         struct ldb_result *res;
151         struct ldb_message_element *el;
152         struct ldb_dn *dn;
153         struct dnsserver_partition_info *partinfo;
154         int i, ret, instance_type;
155         TALLOC_CTX *tmp_ctx;
156
157         tmp_ctx = talloc_new(mem_ctx);
158         if (tmp_ctx == NULL) {
159                 return NULL;
160         }
161
162         partinfo = talloc_zero(mem_ctx, struct dnsserver_partition_info);
163         if (partinfo == NULL) {
164                 talloc_free(tmp_ctx);
165                 return NULL;
166         }
167
168         /* Search for the active replica and state */
169         ret = ldb_search(samdb, tmp_ctx, &res, p->partition_dn, LDB_SCOPE_BASE,
170                         attrs, NULL);
171         if (ret != LDB_SUCCESS || res->count != 1) {
172                 goto failed;
173         }
174
175         /* Set the state of the partition */
176         instance_type = ldb_msg_find_attr_as_int(res->msgs[0], "instanceType", -1);
177         if (instance_type == -1) {
178                 partinfo->dwState = DNS_DP_STATE_UNKNOWN;
179         } else if (instance_type & INSTANCE_TYPE_NC_COMING) {
180                 partinfo->dwState = DNS_DP_STATE_REPL_INCOMING;
181         } else if (instance_type & INSTANCE_TYPE_NC_GOING) {
182                 partinfo->dwState = DNS_DP_STATE_REPL_OUTGOING;
183         } else {
184                 partinfo->dwState = DNS_DP_OKAY;
185         }
186
187         el = ldb_msg_find_element(res->msgs[0], "msDs-masteredBy");
188         if (el == NULL) {
189                 partinfo->dwReplicaCount = 0;
190                 partinfo->ReplicaArray = NULL;
191         } else {
192                 partinfo->dwReplicaCount = el->num_values;
193                 partinfo->ReplicaArray = talloc_zero_array(partinfo,
194                                                            struct DNS_RPC_DP_REPLICA *,
195                                                            el->num_values);
196                 if (partinfo->ReplicaArray == NULL) {
197                         goto failed;
198                 }
199                 for (i=0; i<el->num_values; i++) {
200                         partinfo->ReplicaArray[i] = talloc_zero(partinfo,
201                                                         struct DNS_RPC_DP_REPLICA);
202                         if (partinfo->ReplicaArray[i] == NULL) {
203                                 goto failed;
204                         }
205                         partinfo->ReplicaArray[i]->pszReplicaDn = talloc_strdup(
206                                                                         partinfo,
207                                                                         (const char *)el->values[i].data);
208                         if (partinfo->ReplicaArray[i]->pszReplicaDn == NULL) {
209                                 goto failed;
210                         }
211                 }
212         }
213         talloc_free(res);
214
215         /* Search for cross-reference object */
216         dn = ldb_dn_copy(tmp_ctx, ldb_get_config_basedn(samdb));
217         if (dn == NULL) {
218                 goto failed;
219         }
220
221         ret = ldb_search(samdb, tmp_ctx, &res, dn, LDB_SCOPE_DEFAULT, attrs_none,
222                         "(nCName=%s)", ldb_dn_get_linearized(p->partition_dn));
223         if (ret != LDB_SUCCESS || res->count != 1) {
224                 goto failed;
225         }
226         partinfo->pszCrDn = talloc_strdup(partinfo, ldb_dn_get_linearized(res->msgs[0]->dn));
227         if (partinfo->pszCrDn == NULL) {
228                 goto failed;
229         }
230         talloc_free(res);
231
232         talloc_free(tmp_ctx);
233         return partinfo;
234
235 failed:
236         talloc_free(tmp_ctx);
237         talloc_free(partinfo);
238         return NULL;
239 }
240
241
242 static unsigned int dnsserver_update_soa(TALLOC_CTX *mem_ctx,
243                                 struct ldb_context *samdb,
244                                 struct dnsserver_zone *z)
245 {
246         const char * const attrs[] = { "dnsRecord", NULL };
247         struct ldb_result *res;
248         struct dnsp_DnssrvRpcRecord rec;
249         struct ldb_message_element *el;
250         enum ndr_err_code ndr_err;
251         int ret, i, serial = -1;
252         NTTIME t;
253
254         unix_to_nt_time(&t, time(NULL));
255         t /= 10*1000*1000; /* convert to seconds (NT time is in 100ns units) */
256         t /= 3600;         /* convert to hours */
257
258         ret = ldb_search(samdb, mem_ctx, &res, z->zone_dn, LDB_SCOPE_ONELEVEL, attrs,
259                         "(&(objectClass=dnsNode)(name=@))");
260         if (ret != LDB_SUCCESS || res->count == 0) {
261                 return -1;
262         }
263
264         el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
265         if (el == NULL) {
266                 return -1;
267         }
268
269         for (i=0; i<el->num_values; i++) {
270                 ndr_err = ndr_pull_struct_blob(&el->values[i], mem_ctx, &rec,
271                                         (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
272                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
273                         continue;
274                 }
275
276                 if (rec.wType == DNS_TYPE_SOA) {
277                         serial = rec.data.soa.serial + 1;
278                         rec.dwSerial = serial;
279                         rec.dwTimeStamp = (uint32_t)t;
280                         rec.data.soa.serial = serial;
281
282                         ndr_err = ndr_push_struct_blob(&el->values[i], mem_ctx, &rec,
283                                         (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
284                         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
285                                 return -1;
286                         }
287                         break;
288                 }
289         }
290
291         if (serial != -1) {
292                 el->flags = LDB_FLAG_MOD_REPLACE;
293                 ret = ldb_modify(samdb, res->msgs[0]);
294                 if (ret != LDB_SUCCESS) {
295                         return -1;
296                 }
297         }
298
299         return serial;
300 }
301
302
303 /* Add DNS record to the database */
304 static WERROR dnsserver_db_do_add_rec(TALLOC_CTX *mem_ctx,
305                                 struct ldb_context *samdb,
306                                 struct ldb_dn *dn,
307                                 int num_rec,
308                                 struct dnsp_DnssrvRpcRecord *rec)
309 {
310         struct ldb_message *msg;
311         struct ldb_val v;
312         int ret;
313         enum ndr_err_code ndr_err;
314         int i;
315
316         msg = ldb_msg_new(mem_ctx);
317         W_ERROR_HAVE_NO_MEMORY(msg);
318
319         msg->dn = dn;
320         ret = ldb_msg_add_string(msg, "objectClass", "dnsNode");
321         if (ret != LDB_SUCCESS) {
322                 return WERR_NOMEM;
323         }
324
325         if (num_rec > 0 && rec) {
326                 for (i=0; i<num_rec; i++) {
327                         ndr_err = ndr_push_struct_blob(&v, mem_ctx, &rec[i],
328                                         (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
329                         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
330                                 return WERR_GENERAL_FAILURE;
331                         }
332
333                         ret = ldb_msg_add_value(msg, "dnsRecord", &v, NULL);
334                         if (ret != LDB_SUCCESS) {
335                                 return WERR_NOMEM;
336                         }
337                 }
338         }
339
340         ret = ldb_add(samdb, msg);
341         if (ret != LDB_SUCCESS) {
342                 return WERR_INTERNAL_DB_ERROR;
343         }
344
345         return WERR_OK;
346 }
347
348
349 WERROR dnsserver_db_add_empty_node(TALLOC_CTX *mem_ctx,
350                                         struct ldb_context *samdb,
351                                         struct dnsserver_zone *z,
352                                         const char *name)
353 {
354         const char * const attrs[] = { "name", NULL };
355         struct ldb_result *res;
356         struct ldb_dn *dn;
357         int ret;
358
359         ret = ldb_search(samdb, mem_ctx, &res, z->zone_dn, LDB_SCOPE_BASE, attrs,
360                         "(&(objectClass=dnsNode)(name=%s))", name);
361         if (ret != LDB_SUCCESS) {
362                 return WERR_INTERNAL_DB_ERROR;
363         }
364
365         if (res->count > 0) {
366                 talloc_free(res);
367                 return WERR_DNS_ERROR_RECORD_ALREADY_EXISTS;
368         }
369
370         dn = ldb_dn_copy(mem_ctx, z->zone_dn);
371         W_ERROR_HAVE_NO_MEMORY(dn);
372
373         if (!ldb_dn_add_child_fmt(dn, "DC=%s", name)) {
374                 return WERR_NOMEM;
375         }
376
377         return dnsserver_db_do_add_rec(mem_ctx, samdb, dn, 0, NULL);
378 }
379
380
381 WERROR dnsserver_db_add_record(TALLOC_CTX *mem_ctx,
382                                         struct ldb_context *samdb,
383                                         struct dnsserver_zone *z,
384                                         const char *name,
385                                         struct DNS_RPC_RECORD *add_record)
386 {
387         const char * const attrs[] = { "dnsRecord", NULL };
388         struct ldb_result *res;
389         struct dnsp_DnssrvRpcRecord *rec;
390         struct ldb_message_element *el;
391         struct ldb_dn *dn;
392         enum ndr_err_code ndr_err;
393         NTTIME t;
394         int ret, i;
395         int serial;
396
397         rec = dns_to_dnsp_copy(mem_ctx, add_record);
398         W_ERROR_HAVE_NO_MEMORY(rec);
399
400         serial = dnsserver_update_soa(mem_ctx, samdb, z);
401         if (serial < 0) {
402                 return WERR_INTERNAL_DB_ERROR;
403         }
404
405         unix_to_nt_time(&t, time(NULL));
406         t /= 10*1000*1000; /* convert to seconds (NT time is in 100ns units) */
407         t /= 3600;         /* convert to hours */
408
409         rec->dwSerial = serial;
410         rec->dwTimeStamp = t;
411
412         ret = ldb_search(samdb, mem_ctx, &res, z->zone_dn, LDB_SCOPE_ONELEVEL, attrs,
413                         "(&(objectClass=dnsNode)(name=%s))", name);
414         if (ret != LDB_SUCCESS) {
415                 return WERR_INTERNAL_DB_ERROR;
416         }
417
418         if (res->count == 0) {
419                 dn = dnsserver_name_to_dn(mem_ctx, z, name);
420                 W_ERROR_HAVE_NO_MEMORY(dn);
421
422                 return dnsserver_db_do_add_rec(mem_ctx, samdb, dn, 1, rec);
423         }
424
425         el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
426         if (el == NULL) {
427                 ret = ldb_msg_add_empty(res->msgs[0], "dnsRecord", 0, &el);
428                 if (ret != LDB_SUCCESS) {
429                         return WERR_NOMEM;
430                 }
431         }
432
433         for (i=0; i<el->num_values; i++) {
434                 struct dnsp_DnssrvRpcRecord rec2;
435
436                 ndr_err = ndr_pull_struct_blob(&el->values[i], mem_ctx, &rec2,
437                                                 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
438                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
439                         return WERR_GENERAL_FAILURE;
440                 }
441
442                 if (dns_record_match(rec, &rec2)) {
443                         break;
444                 }
445         }
446         if (i < el->num_values) {
447                 return WERR_DNS_ERROR_RECORD_ALREADY_EXISTS;
448         }
449         if (i == el->num_values) {
450                 /* adding a new value */
451                 el->values = talloc_realloc(el, el->values, struct ldb_val, el->num_values+1);
452                 W_ERROR_HAVE_NO_MEMORY(el->values);
453                 el->num_values++;
454         }
455
456         ndr_err = ndr_push_struct_blob(&el->values[i], mem_ctx, rec,
457                                         (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
458         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
459                 return WERR_GENERAL_FAILURE;
460         }
461
462         el->flags = LDB_FLAG_MOD_REPLACE;
463         ret = ldb_modify(samdb, res->msgs[0]);
464         if (ret != LDB_SUCCESS) {
465                 return WERR_INTERNAL_DB_ERROR;
466         }
467
468         return WERR_OK;
469 }
470
471 WERROR dnsserver_db_update_record(TALLOC_CTX *mem_ctx,
472                                         struct ldb_context *samdb,
473                                         struct dnsserver_zone *z,
474                                         const char *name,
475                                         struct DNS_RPC_RECORD *add_record,
476                                         struct DNS_RPC_RECORD *del_record)
477 {
478         const char * const attrs[] = { "dnsRecord", NULL };
479         struct ldb_result *res;
480         struct dnsp_DnssrvRpcRecord *arec, *drec;
481         struct ldb_message_element *el;
482         enum ndr_err_code ndr_err;
483         NTTIME t;
484         int ret, i;
485         int serial;
486
487         serial = dnsserver_update_soa(mem_ctx, samdb, z);
488         if (serial < 0) {
489                 return WERR_INTERNAL_DB_ERROR;
490         }
491
492         arec = dns_to_dnsp_copy(mem_ctx, add_record);
493         W_ERROR_HAVE_NO_MEMORY(arec);
494
495         drec = dns_to_dnsp_copy(mem_ctx, del_record);
496         W_ERROR_HAVE_NO_MEMORY(drec);
497
498         unix_to_nt_time(&t, time(NULL));
499         t /= 10*1000*1000;
500
501         arec->dwSerial = serial;
502         arec->dwTimeStamp = t;
503
504         ret = ldb_search(samdb, mem_ctx, &res, z->zone_dn, LDB_SCOPE_ONELEVEL, attrs,
505                         "(&(objectClass=dnsNode)(name=%s))", name);
506         if (ret != LDB_SUCCESS) {
507                 return WERR_INTERNAL_DB_ERROR;
508         }
509
510         if (res->count == 0) {
511                 return WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST;
512         }
513
514         el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
515         if (el == NULL || el->num_values == 0) {
516                 return WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST;
517         }
518
519         for (i=0; i<el->num_values; i++) {
520                 struct dnsp_DnssrvRpcRecord rec2;
521
522                 ndr_err = ndr_pull_struct_blob(&el->values[i], mem_ctx, &rec2,
523                                                 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
524                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
525                         return WERR_GENERAL_FAILURE;
526                 }
527
528                 if (dns_record_match(arec, &rec2)) {
529                         break;
530                 }
531         }
532         if (i < el->num_values) {
533                 return WERR_DNS_ERROR_RECORD_ALREADY_EXISTS;
534         }
535
536
537         for (i=0; i<el->num_values; i++) {
538                 struct dnsp_DnssrvRpcRecord rec2;
539
540                 ndr_err = ndr_pull_struct_blob(&el->values[i], mem_ctx, &rec2,
541                                                 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
542                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
543                         return WERR_GENERAL_FAILURE;
544                 }
545
546                 if (dns_record_match(drec, &rec2)) {
547                         break;
548                 }
549         }
550         if (i == el->num_values) {
551                 return WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST;
552         }
553
554         ndr_err = ndr_push_struct_blob(&el->values[i], mem_ctx, arec,
555                                         (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
556         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
557                 return WERR_GENERAL_FAILURE;
558         }
559
560         el->flags = LDB_FLAG_MOD_REPLACE;
561         ret = ldb_modify(samdb, res->msgs[0]);
562         if (ret != LDB_SUCCESS) {
563                 return WERR_INTERNAL_DB_ERROR;
564         }
565
566         return WERR_OK;
567 }
568
569 WERROR dnsserver_db_delete_record(TALLOC_CTX *mem_ctx,
570                                         struct ldb_context *samdb,
571                                         struct dnsserver_zone *z,
572                                         const char *name,
573                                         struct DNS_RPC_RECORD *del_record)
574 {
575         const char * const attrs[] = { "dnsRecord", NULL };
576         struct ldb_result *res;
577         struct dnsp_DnssrvRpcRecord *rec;
578         struct ldb_message_element *el;
579         enum ndr_err_code ndr_err;
580         int ret, i;
581         int serial;
582
583         serial = dnsserver_update_soa(mem_ctx, samdb, z);
584         if (serial < 0) {
585                 return WERR_INTERNAL_DB_ERROR;
586         }
587
588         rec = dns_to_dnsp_copy(mem_ctx, del_record);
589         W_ERROR_HAVE_NO_MEMORY(rec);
590
591         ret = ldb_search(samdb, mem_ctx, &res, z->zone_dn, LDB_SCOPE_ONELEVEL, attrs,
592                         "(&(objectClass=dnsNode)(name=%s))", name);
593         if (ret != LDB_SUCCESS) {
594                 return WERR_INTERNAL_DB_ERROR;
595         }
596
597         if (res->count == 0) {
598                 return WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST;
599         }
600
601         el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
602         if (el == NULL || el->num_values == 0) {
603                 return WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST;
604         }
605
606         for (i=0; i<el->num_values; i++) {
607                 struct dnsp_DnssrvRpcRecord rec2;
608
609                 ndr_err = ndr_pull_struct_blob(&el->values[i], mem_ctx, &rec2,
610                                                 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
611                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
612                         return WERR_GENERAL_FAILURE;
613                 }
614
615                 if (dns_record_match(rec, &rec2)) {
616                         break;
617                 }
618         }
619         if (i == el->num_values) {
620                 return WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST;
621         }
622         if (i < el->num_values-1) {
623                 memmove(&el->values[i], &el->values[i+1], sizeof(el->values[0])*((el->num_values-1)-i));
624         }
625         el->num_values--;
626
627         if (el->num_values == 0) {
628                 ret = ldb_delete(samdb, res->msgs[0]->dn);
629         } else {
630                 el->flags = LDB_FLAG_MOD_REPLACE;
631                 ret = ldb_modify(samdb, res->msgs[0]);
632         }
633         if (ret != LDB_SUCCESS) {
634                 return WERR_INTERNAL_DB_ERROR;
635         }
636
637         return WERR_OK;
638 }