Line data Source code
1 : /*
2 : SSSD
3 :
4 : AD Domain Info Module
5 :
6 : Authors:
7 : Sumit Bose <sbose@redhat.com>
8 :
9 : Copyright (C) 2013 Red Hat
10 :
11 : This program is free software; you can redistribute it and/or modify
12 : it under the terms of the GNU General Public License as published by
13 : the Free Software Foundation; either version 3 of the License, or
14 : (at your option) any later version.
15 :
16 : This program is distributed in the hope that it will be useful,
17 : but WITHOUT ANY WARRANTY; without even the implied warranty of
18 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 : GNU General Public License for more details.
20 :
21 : You should have received a copy of the GNU General Public License
22 : along with this program. If not, see <http://www.gnu.org/licenses/>.
23 : */
24 :
25 : #include <errno.h>
26 : #include <tevent.h>
27 : #include <ctype.h>
28 : #include <ndr.h>
29 : #include <ndr/ndr_nbt.h>
30 :
31 : #include "providers/ldap/sdap.h"
32 : #include "providers/ldap/sdap_async.h"
33 : #include "providers/ldap/sdap_idmap.h"
34 : #include "providers/ad/ad_domain_info.h"
35 : #include "providers/ad/ad_common.h"
36 : #include "util/util.h"
37 :
38 : static errno_t
39 0 : netlogon_get_domain_info(TALLOC_CTX *mem_ctx,
40 : struct sysdb_attrs *reply,
41 : char **_flat_name,
42 : char **_site,
43 : char **_forest)
44 : {
45 : errno_t ret;
46 : struct ldb_message_element *el;
47 : DATA_BLOB blob;
48 0 : struct ndr_pull *ndr_pull = NULL;
49 : enum ndr_err_code ndr_err;
50 : struct netlogon_samlogon_response response;
51 : const char *flat_name;
52 : const char *site;
53 : const char *forest;
54 :
55 0 : ret = sysdb_attrs_get_el(reply, AD_AT_NETLOGON, &el);
56 0 : if (ret != EOK) {
57 0 : DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_el() failed\n");
58 0 : return ret;
59 : }
60 :
61 0 : if (el->num_values == 0) {
62 0 : DEBUG(SSSDBG_OP_FAILURE, "netlogon has no value\n");
63 0 : return ENOENT;
64 0 : } else if (el->num_values > 1) {
65 0 : DEBUG(SSSDBG_OP_FAILURE, "More than one netlogon value?\n");
66 0 : return EIO;
67 : }
68 :
69 0 : blob.data = el->values[0].data;
70 0 : blob.length = el->values[0].length;
71 :
72 0 : ndr_pull = ndr_pull_init_blob(&blob, mem_ctx);
73 0 : if (ndr_pull == NULL) {
74 0 : DEBUG(SSSDBG_OP_FAILURE, "ndr_pull_init_blob() failed.\n");
75 0 : return ENOMEM;
76 : }
77 :
78 0 : ndr_err = ndr_pull_netlogon_samlogon_response(ndr_pull, NDR_SCALARS,
79 : &response);
80 0 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
81 0 : DEBUG(SSSDBG_OP_FAILURE, "ndr_pull_netlogon_samlogon_response() "
82 : "failed [%d]\n", ndr_err);
83 0 : ret = EBADMSG;
84 0 : goto done;
85 : }
86 :
87 0 : if (!(response.ntver & NETLOGON_NT_VERSION_5EX)) {
88 0 : DEBUG(SSSDBG_OP_FAILURE, "Wrong version returned [%x]\n",
89 : response.ntver);
90 0 : ret = EBADMSG;
91 0 : goto done;
92 : }
93 :
94 : /* get flat name */
95 0 : if (response.data.nt5_ex.domain_name != NULL &&
96 0 : *response.data.nt5_ex.domain_name != '\0') {
97 0 : flat_name = response.data.nt5_ex.domain_name;
98 : } else {
99 0 : DEBUG(SSSDBG_MINOR_FAILURE,
100 : "No netlogon domain name data available\n");
101 0 : ret = ENOENT;
102 0 : goto done;
103 : }
104 :
105 0 : *_flat_name = talloc_strdup(mem_ctx, flat_name);
106 0 : if (*_flat_name == NULL) {
107 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
108 0 : ret = ENOMEM;
109 0 : goto done;
110 : }
111 :
112 : /* get forest */
113 0 : if (response.data.nt5_ex.forest != NULL &&
114 0 : *response.data.nt5_ex.forest != '\0') {
115 0 : forest = response.data.nt5_ex.forest;
116 : } else {
117 0 : DEBUG(SSSDBG_MINOR_FAILURE, "No netlogon forest data available\n");
118 0 : ret = ENOENT;
119 0 : goto done;
120 : }
121 :
122 0 : *_forest = talloc_strdup(mem_ctx, forest);
123 0 : if (*_forest == NULL) {
124 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
125 0 : ret = ENOMEM;
126 0 : goto done;
127 : }
128 :
129 : /* get site name */
130 0 : if (response.data.nt5_ex.client_site != NULL
131 0 : && response.data.nt5_ex.client_site[0] != '\0') {
132 0 : site = response.data.nt5_ex.client_site;
133 : } else {
134 0 : DEBUG(SSSDBG_MINOR_FAILURE,
135 : "No netlogon site name data available\n");
136 0 : ret = ENOENT;
137 0 : goto done;
138 : }
139 :
140 0 : *_site = talloc_strdup(mem_ctx, site);
141 0 : if (*_site == NULL) {
142 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
143 0 : ret = ENOMEM;
144 0 : goto done;
145 : }
146 :
147 0 : ret = EOK;
148 : done:
149 0 : talloc_free(ndr_pull);
150 0 : return ret;
151 : }
152 :
153 : struct ad_master_domain_state {
154 : struct tevent_context *ev;
155 : struct sdap_id_conn_ctx *conn;
156 : struct sdap_id_op *id_op;
157 : struct sdap_id_ctx *id_ctx;
158 : struct sdap_options *opts;
159 :
160 : const char *dom_name;
161 : int base_iter;
162 :
163 : char *flat;
164 : char *site;
165 : char *forest;
166 : char *sid;
167 : };
168 :
169 : static errno_t ad_master_domain_next(struct tevent_req *req);
170 : static void ad_master_domain_next_done(struct tevent_req *subreq);
171 : static void ad_master_domain_netlogon_done(struct tevent_req *req);
172 :
173 : struct tevent_req *
174 0 : ad_master_domain_send(TALLOC_CTX *mem_ctx,
175 : struct tevent_context *ev,
176 : struct sdap_id_conn_ctx *conn,
177 : struct sdap_id_op *op,
178 : const char *dom_name)
179 : {
180 : errno_t ret;
181 : struct tevent_req *req;
182 : struct ad_master_domain_state *state;
183 :
184 0 : req = tevent_req_create(mem_ctx, &state, struct ad_master_domain_state);
185 0 : if (!req) return NULL;
186 :
187 0 : state->ev = ev;
188 0 : state->id_op = op;
189 0 : state->conn = conn;
190 0 : state->id_ctx = conn->id_ctx;
191 0 : state->opts = conn->id_ctx->opts;
192 0 : state->dom_name = dom_name;
193 :
194 0 : ret = ad_master_domain_next(req);
195 0 : if (ret != EOK && ret != EAGAIN) {
196 0 : goto immediate;
197 : }
198 :
199 0 : return req;
200 :
201 : immediate:
202 0 : if (ret != EOK) {
203 0 : tevent_req_error(req, ret);
204 : } else {
205 0 : tevent_req_done(req);
206 : }
207 0 : tevent_req_post(req, ev);
208 0 : return req;
209 : }
210 :
211 : static errno_t
212 0 : ad_master_domain_next(struct tevent_req *req)
213 : {
214 : struct tevent_req *subreq;
215 : struct sdap_search_base *base;
216 0 : const char *master_sid_attrs[] = {AD_AT_OBJECT_SID, NULL};
217 :
218 0 : struct ad_master_domain_state *state =
219 0 : tevent_req_data(req, struct ad_master_domain_state);
220 :
221 0 : base = state->opts->sdom->search_bases[state->base_iter];
222 0 : if (base == NULL) {
223 0 : return EOK;
224 : }
225 :
226 0 : subreq = sdap_get_generic_send(state, state->ev,
227 0 : state->id_ctx->opts,
228 : sdap_id_op_handle(state->id_op),
229 : base->basedn, LDAP_SCOPE_BASE,
230 : MASTER_DOMAIN_SID_FILTER, master_sid_attrs,
231 : NULL, 0,
232 0 : dp_opt_get_int(state->opts->basic,
233 : SDAP_SEARCH_TIMEOUT),
234 : false);
235 0 : if (subreq == NULL) {
236 0 : DEBUG(SSSDBG_OP_FAILURE, "sdap_get_generic_send failed.\n");
237 0 : return ENOMEM;
238 : }
239 0 : tevent_req_set_callback(subreq, ad_master_domain_next_done, req);
240 :
241 0 : return EAGAIN;
242 : }
243 :
244 : static void
245 0 : ad_master_domain_next_done(struct tevent_req *subreq)
246 : {
247 : errno_t ret;
248 : size_t reply_count;
249 0 : struct sysdb_attrs **reply = NULL;
250 : struct ldb_message_element *el;
251 : char *sid_str;
252 : enum idmap_error_code err;
253 : static const char *attrs[] = {AD_AT_NETLOGON, NULL};
254 : char *filter;
255 : char *ntver;
256 :
257 0 : struct tevent_req *req = tevent_req_callback_data(subreq,
258 : struct tevent_req);
259 0 : struct ad_master_domain_state *state =
260 0 : tevent_req_data(req, struct ad_master_domain_state);
261 :
262 0 : ret = sdap_get_generic_recv(subreq, state, &reply_count, &reply);
263 0 : talloc_zfree(subreq);
264 0 : if (ret != EOK) {
265 0 : DEBUG(SSSDBG_OP_FAILURE, "sdap_get_generic_send request failed.\n");
266 0 : goto done;
267 : }
268 :
269 0 : if (reply_count == 0) {
270 0 : state->base_iter++;
271 0 : ret = ad_master_domain_next(req);
272 0 : if (ret == EAGAIN) {
273 : /* Async request will get us back here again */
274 0 : return;
275 0 : } else if (ret != EOK) {
276 0 : goto done;
277 : }
278 :
279 : /* EOK */
280 0 : tevent_req_done(req);
281 0 : return;
282 0 : } else if (reply_count == 1) {
283 0 : ret = sysdb_attrs_get_el(reply[0], AD_AT_OBJECT_SID, &el);
284 0 : if (ret != EOK || el->num_values != 1) {
285 0 : DEBUG(SSSDBG_OP_FAILURE, "sdap_attrs_get_el failed.\n");
286 0 : goto done;
287 : }
288 :
289 0 : err = sss_idmap_bin_sid_to_sid(state->opts->idmap_ctx->map,
290 0 : el->values[0].data,
291 0 : el->values[0].length,
292 : &sid_str);
293 0 : if (err != IDMAP_SUCCESS) {
294 0 : DEBUG(SSSDBG_MINOR_FAILURE,
295 : "Could not convert SID: [%s].\n", idmap_error_string(err));
296 0 : ret = EFAULT;
297 0 : goto done;
298 : }
299 :
300 0 : state->sid = talloc_steal(state, sid_str);
301 : } else {
302 0 : DEBUG(SSSDBG_OP_FAILURE,
303 : "More than one result for domain SID found.\n");
304 0 : ret = EINVAL;
305 0 : goto done;
306 : }
307 :
308 0 : DEBUG(SSSDBG_TRACE_FUNC, "Found SID [%s].\n", state->sid);
309 :
310 0 : ntver = sss_ldap_encode_ndr_uint32(state, NETLOGON_NT_VERSION_5EX |
311 : NETLOGON_NT_VERSION_WITH_CLOSEST_SITE);
312 0 : if (ntver == NULL) {
313 0 : DEBUG(SSSDBG_OP_FAILURE, "sss_ldap_encode_ndr_uint32 failed.\n");
314 0 : ret = ENOMEM;
315 0 : goto done;
316 : }
317 :
318 0 : filter = talloc_asprintf(state, "(&(%s=%s)(%s=%s))",
319 : AD_AT_DNS_DOMAIN, state->dom_name,
320 : AD_AT_NT_VERSION, ntver);
321 0 : if (filter == NULL) {
322 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
323 0 : ret = ENOMEM;
324 0 : goto done;
325 : }
326 :
327 0 : subreq = sdap_get_generic_send(state, state->ev,
328 0 : state->id_ctx->opts,
329 : sdap_id_op_handle(state->id_op),
330 : "", LDAP_SCOPE_BASE, filter, attrs, NULL, 0,
331 0 : dp_opt_get_int(state->opts->basic,
332 : SDAP_SEARCH_TIMEOUT),
333 : false);
334 0 : if (subreq == NULL) {
335 0 : DEBUG(SSSDBG_OP_FAILURE, "sdap_get_generic_send failed.\n");
336 0 : ret = ENOMEM;
337 0 : goto done;
338 : }
339 :
340 0 : tevent_req_set_callback(subreq, ad_master_domain_netlogon_done, req);
341 0 : return;
342 :
343 : done:
344 0 : tevent_req_error(req, ret);
345 : }
346 :
347 : static void
348 0 : ad_master_domain_netlogon_done(struct tevent_req *subreq)
349 : {
350 : int ret;
351 : size_t reply_count;
352 0 : struct sysdb_attrs **reply = NULL;
353 :
354 0 : struct tevent_req *req = tevent_req_callback_data(subreq,
355 : struct tevent_req);
356 0 : struct ad_master_domain_state *state =
357 0 : tevent_req_data(req, struct ad_master_domain_state);
358 :
359 0 : ret = sdap_get_generic_recv(subreq, state, &reply_count, &reply);
360 0 : talloc_zfree(subreq);
361 0 : if (ret != EOK) {
362 0 : DEBUG(SSSDBG_OP_FAILURE, "sdap_get_generic_send request failed.\n");
363 0 : tevent_req_error(req, ret);
364 0 : return;
365 : }
366 :
367 : /* Failure to get the flat name is not fatal. Just quit. */
368 0 : if (reply_count == 0) {
369 0 : DEBUG(SSSDBG_MINOR_FAILURE, "No netlogon data available. Flat name " \
370 : "might not be usable\n");
371 0 : goto done;
372 0 : } else if (reply_count > 1) {
373 0 : DEBUG(SSSDBG_MINOR_FAILURE,
374 : "More than one netlogon info returned.\n");
375 0 : goto done;
376 : }
377 :
378 : /* Exactly one flat name. Carry on */
379 :
380 0 : ret = netlogon_get_domain_info(state, reply[0], &state->flat,
381 : &state->site, &state->forest);
382 0 : if (ret != EOK) {
383 0 : DEBUG(SSSDBG_MINOR_FAILURE,
384 : "Could not get the flat name or forest: %d:[%s]\n",
385 : ret, sss_strerror(ret));
386 : /* Not fatal. Just quit. */
387 0 : goto done;
388 : }
389 :
390 0 : DEBUG(SSSDBG_TRACE_FUNC, "Found flat name [%s].\n", state->flat);
391 0 : DEBUG(SSSDBG_TRACE_FUNC, "Found site [%s].\n", state->site);
392 0 : DEBUG(SSSDBG_TRACE_FUNC, "Found forest [%s].\n", state->forest);
393 :
394 : done:
395 0 : tevent_req_done(req);
396 0 : return;
397 : }
398 :
399 : errno_t
400 0 : ad_master_domain_recv(struct tevent_req *req,
401 : TALLOC_CTX *mem_ctx,
402 : char **_flat,
403 : char **_id,
404 : char **_site,
405 : char **_forest)
406 : {
407 0 : struct ad_master_domain_state *state = tevent_req_data(req,
408 : struct ad_master_domain_state);
409 :
410 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
411 :
412 0 : if (_flat) {
413 0 : *_flat = talloc_steal(mem_ctx, state->flat);
414 : }
415 :
416 0 : if (_site) {
417 0 : *_site = talloc_steal(mem_ctx, state->site);
418 : }
419 :
420 0 : if (_forest) {
421 0 : *_forest = talloc_steal(mem_ctx, state->forest);
422 : }
423 :
424 0 : if (_id) {
425 0 : *_id = talloc_steal(mem_ctx, state->sid);
426 : }
427 :
428 0 : return EOK;
429 : }
|