s4:dsdb Change dsdb_get_schema() callers to use new talloc argument
[samba.git] / source4 / dsdb / samdb / ldb_modules / linked_attributes.c
1 /* 
2    ldb database library
3
4    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2007
5    Copyright (C) Simo Sorce <idra@samba.org> 2008
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 /*
22  *  Name: ldb
23  *
24  *  Component: ldb linked_attributes module
25  *
26  *  Description: Module to ensure linked attribute pairs remain in sync
27  *
28  *  Author: Andrew Bartlett
29  */
30
31 #include "includes.h"
32 #include "ldb_module.h"
33 #include "dlinklist.h"
34 #include "dsdb/samdb/samdb.h"
35 #include "librpc/gen_ndr/ndr_misc.h"
36 #include "dsdb/samdb/ldb_modules/util.h"
37
38
39 static int linked_attributes_fix_links(struct ldb_module *module,
40                                        struct ldb_dn *old_dn, struct ldb_dn *new_dn,
41                                        struct ldb_message_element *el, struct dsdb_schema *schema,
42                                        const struct dsdb_attribute *schema_attr)
43 {
44         unsigned int i;
45         TALLOC_CTX *tmp_ctx = talloc_new(module);
46         struct ldb_context *ldb = ldb_module_get_ctx(module);
47         const struct dsdb_attribute *target;
48         const char *attrs[2];
49
50         target = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
51         if (target == NULL) {
52                 /* there is no counterpart link to change */
53                 return LDB_SUCCESS;
54         }
55
56         attrs[0] = target->lDAPDisplayName;
57         attrs[1] = NULL;
58
59         for (i=0; i<el->num_values; i++) {
60                 struct dsdb_dn *dsdb_dn;
61                 unsigned int j;
62                 int ret;
63                 struct ldb_result *res;
64                 struct ldb_message *msg;
65                 struct ldb_message_element *el2;
66
67                 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], schema_attr->syntax->ldap_oid);
68                 if (dsdb_dn == NULL) {
69                         talloc_free(tmp_ctx);
70                         return LDB_ERR_INVALID_DN_SYNTAX;
71                 }
72
73                 ret = dsdb_module_search_dn(module, tmp_ctx, &res, dsdb_dn->dn,
74                                             attrs,
75                                             DSDB_SEARCH_SHOW_DELETED |
76                                             DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
77                                             DSDB_SEARCH_REVEAL_INTERNALS);
78                 if (ret != LDB_SUCCESS) {
79                         ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - remote not found - %s",
80                                                el->name, target->lDAPDisplayName,
81                                                ldb_dn_get_linearized(old_dn),
82                                                ldb_dn_get_linearized(dsdb_dn->dn),
83                                                ldb_errstring(ldb));
84                         talloc_free(tmp_ctx);
85                         return ret;
86                 }
87                 msg = res->msgs[0];
88
89                 if (msg->num_elements != 1 ||
90                     ldb_attr_cmp(msg->elements[0].name, target->lDAPDisplayName) != 0) {
91                         ldb_set_errstring(ldb, "Bad msg elements in linked_attributes_fix_links");
92                         talloc_free(tmp_ctx);
93                         return LDB_ERR_OPERATIONS_ERROR;
94                 }
95                 el2 = &msg->elements[0];
96
97                 el2->flags = LDB_FLAG_MOD_REPLACE;
98
99                 /* find our DN in the values */
100                 for (j=0; j<el2->num_values; j++) {
101                         struct dsdb_dn *dsdb_dn2;
102                         dsdb_dn2 = dsdb_dn_parse(msg, ldb, &el2->values[j], target->syntax->ldap_oid);
103                         if (dsdb_dn2 == NULL) {
104                                 talloc_free(tmp_ctx);
105                                 return LDB_ERR_INVALID_DN_SYNTAX;
106                         }
107                         if (ldb_dn_compare(old_dn, dsdb_dn2->dn) != 0) {
108                                 continue;
109                         }
110                         ret = ldb_dn_update_components(dsdb_dn2->dn, new_dn);
111                         if (ret != LDB_SUCCESS) {
112                                 talloc_free(tmp_ctx);
113                                 return ret;
114                         }
115
116                         el2->values[j] = data_blob_string_const(
117                                 dsdb_dn_get_extended_linearized(el2->values, dsdb_dn2, 1));
118                 }
119
120                 ret = dsdb_check_single_valued_link(target, el2);
121                 if (ret != LDB_SUCCESS) {
122                         talloc_free(tmp_ctx);
123                         return ret;
124                 }
125
126                 ret = dsdb_module_modify(module, msg, DSDB_MODIFY_RELAX);
127                 if (ret != LDB_SUCCESS) {
128                         ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - update failed - %s",
129                                                el->name, target->lDAPDisplayName,
130                                                ldb_dn_get_linearized(old_dn),
131                                                ldb_dn_get_linearized(dsdb_dn->dn),
132                                                ldb_errstring(ldb));
133                         talloc_free(tmp_ctx);
134                         return ret;
135                 }
136         }
137
138         talloc_free(tmp_ctx);
139         return LDB_SUCCESS;
140 }
141
142
143 /* rename */
144 static int linked_attributes_rename(struct ldb_module *module, struct ldb_request *req)
145 {
146         struct ldb_result *res;
147         struct ldb_message *msg;
148         unsigned int i;
149         int ret;
150         struct ldb_context *ldb = ldb_module_get_ctx(module);
151         struct dsdb_schema *schema;
152         /*
153            - load the current msg
154            - find any linked attributes
155            - if its a link then find the target object
156            - modify the target linked attributes with the new DN
157         */
158         ret = dsdb_module_search_dn(module, req, &res, req->op.rename.olddn,
159                                     NULL, DSDB_SEARCH_SHOW_DELETED);
160         if (ret != LDB_SUCCESS) {
161                 return ret;
162         }
163
164         schema = dsdb_get_schema(ldb, res);
165         if (!schema) {
166                 ldb_oom(schema);
167                 return LDB_ERR_OPERATIONS_ERROR;
168         }
169
170         msg = res->msgs[0];
171
172         for (i=0; i<msg->num_elements; i++) {
173                 struct ldb_message_element *el = &msg->elements[i];
174                 const struct dsdb_attribute *schema_attr
175                         = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
176                 if (!schema_attr || schema_attr->linkID == 0) {
177                         continue;
178                 }
179                 ret = linked_attributes_fix_links(module, msg->dn, req->op.rename.newdn, el,
180                                                   schema, schema_attr);
181                 if (ret != LDB_SUCCESS) {
182                         talloc_free(res);
183                         return ret;
184                 }
185         }
186
187         talloc_free(res);
188
189         return ldb_next_request(module, req);
190 }
191
192
193 _PUBLIC_ const struct ldb_module_ops ldb_linked_attributes_module_ops = {
194         .name              = "linked_attributes",
195         .rename            = linked_attributes_rename,
196 };