Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : endpoint server for the samr pipe
5 :
6 : Copyright (C) Andrew Tridgell 2004
7 : Copyright (C) Volker Lendecke 2004
8 : Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
9 : Copyright (C) Matthias Dieter Wallnöfer 2009
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 "includes.h"
26 : #include "librpc/gen_ndr/ndr_samr.h"
27 : #include "rpc_server/dcerpc_server.h"
28 : #include "rpc_server/common/common.h"
29 : #include "rpc_server/samr/dcesrv_samr.h"
30 : #include "system/time.h"
31 : #include <ldb.h>
32 : #include <ldb_errors.h>
33 : #include "../libds/common/flags.h"
34 : #include "dsdb/samdb/samdb.h"
35 : #include "dsdb/common/util.h"
36 : #include "libcli/ldap/ldap_ndr.h"
37 : #include "libcli/security/security.h"
38 : #include "rpc_server/samr/proto.h"
39 : #include "../lib/util/util_ldb.h"
40 : #include "param/param.h"
41 : #include "lib/util/tsort.h"
42 : #include "libds/common/flag_mapping.h"
43 :
44 : #undef strcasecmp
45 :
46 : #define DCESRV_INTERFACE_SAMR_BIND(context, iface) \
47 : dcesrv_interface_samr_bind(context, iface)
48 1775 : static NTSTATUS dcesrv_interface_samr_bind(struct dcesrv_connection_context *context,
49 : const struct dcesrv_interface *iface)
50 : {
51 1957 : return dcesrv_interface_bind_reject_connect(context, iface);
52 : }
53 :
54 : /* these query macros make samr_Query[User|Group|Alias]Info a bit easier to read */
55 :
56 : #define QUERY_STRING(msg, field, attr) \
57 : info->field.string = ldb_msg_find_attr_as_string(msg, attr, "");
58 : #define QUERY_UINT(msg, field, attr) \
59 : info->field = ldb_msg_find_attr_as_uint(msg, attr, 0);
60 : #define QUERY_RID(msg, field, attr) \
61 : info->field = samdb_result_rid_from_sid(mem_ctx, msg, attr, 0);
62 : #define QUERY_UINT64(msg, field, attr) \
63 : info->field = ldb_msg_find_attr_as_uint64(msg, attr, 0);
64 : #define QUERY_APASSC(msg, field, attr) \
65 : info->field = samdb_result_allow_password_change(sam_ctx, mem_ctx, \
66 : a_state->domain_state->domain_dn, msg, attr);
67 : #define QUERY_BPWDCT(msg, field, attr) \
68 : info->field = samdb_result_effective_badPwdCount(sam_ctx, mem_ctx, \
69 : a_state->domain_state->domain_dn, msg);
70 : #define QUERY_LHOURS(msg, field, attr) \
71 : info->field = samdb_result_logon_hours(mem_ctx, msg, attr);
72 : #define QUERY_AFLAGS(msg, field, attr) \
73 : info->field = samdb_result_acct_flags(msg, attr);
74 :
75 :
76 : /* these are used to make the Set[User|Group]Info code easier to follow */
77 :
78 : #define SET_STRING(msg, field, attr) do { \
79 : struct ldb_message_element *set_el; \
80 : if (r->in.info->field.string == NULL) return NT_STATUS_INVALID_PARAMETER; \
81 : if (r->in.info->field.string[0] == '\0') { \
82 : if (ldb_msg_add_empty(msg, attr, LDB_FLAG_MOD_DELETE, NULL) != LDB_SUCCESS) { \
83 : return NT_STATUS_NO_MEMORY; \
84 : } \
85 : } \
86 : if (ldb_msg_add_string(msg, attr, r->in.info->field.string) != LDB_SUCCESS) { \
87 : return NT_STATUS_NO_MEMORY; \
88 : } \
89 : set_el = ldb_msg_find_element(msg, attr); \
90 : set_el->flags = LDB_FLAG_MOD_REPLACE; \
91 : } while (0)
92 :
93 : #define SET_UINT(msg, field, attr) do { \
94 : struct ldb_message_element *set_el; \
95 : if (samdb_msg_add_uint(sam_ctx, mem_ctx, msg, attr, r->in.info->field) != LDB_SUCCESS) { \
96 : return NT_STATUS_NO_MEMORY; \
97 : } \
98 : set_el = ldb_msg_find_element(msg, attr); \
99 : set_el->flags = LDB_FLAG_MOD_REPLACE; \
100 : } while (0)
101 :
102 : #define SET_INT64(msg, field, attr) do { \
103 : struct ldb_message_element *set_el; \
104 : if (samdb_msg_add_int64(sam_ctx, mem_ctx, msg, attr, r->in.info->field) != LDB_SUCCESS) { \
105 : return NT_STATUS_NO_MEMORY; \
106 : } \
107 : set_el = ldb_msg_find_element(msg, attr); \
108 : set_el->flags = LDB_FLAG_MOD_REPLACE; \
109 : } while (0)
110 :
111 : #define SET_UINT64(msg, field, attr) do { \
112 : struct ldb_message_element *set_el; \
113 : if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg, attr, r->in.info->field) != LDB_SUCCESS) { \
114 : return NT_STATUS_NO_MEMORY; \
115 : } \
116 : set_el = ldb_msg_find_element(msg, attr); \
117 : set_el->flags = LDB_FLAG_MOD_REPLACE; \
118 : } while (0)
119 :
120 : /* Set account flags, discarding flags that cannot be set with SAMR */
121 : #define SET_AFLAGS(msg, field, attr) do { \
122 : struct ldb_message_element *set_el; \
123 : if (samdb_msg_add_acct_flags(sam_ctx, mem_ctx, msg, attr, r->in.info->field) != 0) { \
124 : return NT_STATUS_NO_MEMORY; \
125 : } \
126 : set_el = ldb_msg_find_element(msg, attr); \
127 : set_el->flags = LDB_FLAG_MOD_REPLACE; \
128 : } while (0)
129 :
130 : #define SET_LHOURS(msg, field, attr) do { \
131 : struct ldb_message_element *set_el; \
132 : if (samdb_msg_add_logon_hours(sam_ctx, mem_ctx, msg, attr, &r->in.info->field) != LDB_SUCCESS) { \
133 : return NT_STATUS_NO_MEMORY; \
134 : } \
135 : set_el = ldb_msg_find_element(msg, attr); \
136 : set_el->flags = LDB_FLAG_MOD_REPLACE; \
137 : } while (0)
138 :
139 : #define SET_PARAMETERS(msg, field, attr) do { \
140 : struct ldb_message_element *set_el; \
141 : if (r->in.info->field.length != 0) { \
142 : if (samdb_msg_add_parameters(sam_ctx, mem_ctx, msg, attr, &r->in.info->field) != LDB_SUCCESS) { \
143 : return NT_STATUS_NO_MEMORY; \
144 : } \
145 : set_el = ldb_msg_find_element(msg, attr); \
146 : set_el->flags = LDB_FLAG_MOD_REPLACE; \
147 : } \
148 : } while (0)
149 :
150 : /*
151 : * Clear a GUID cache
152 : */
153 629 : static void clear_guid_cache(struct samr_guid_cache *cache)
154 : {
155 629 : cache->handle = 0;
156 629 : cache->size = 0;
157 629 : TALLOC_FREE(cache->entries);
158 629 : }
159 :
160 : /*
161 : * initialize a GUID cache
162 : */
163 5124 : static void initialize_guid_cache(struct samr_guid_cache *cache)
164 : {
165 5634 : cache->handle = 0;
166 5634 : cache->size = 0;
167 5634 : cache->entries = NULL;
168 5124 : }
169 :
170 283 : static NTSTATUS load_guid_cache(
171 : struct samr_guid_cache *cache,
172 : struct samr_domain_state *d_state,
173 : unsigned int ldb_cnt,
174 : struct ldb_message **res)
175 : {
176 283 : NTSTATUS status = NT_STATUS_OK;
177 : unsigned int i;
178 283 : TALLOC_CTX *frame = talloc_stackframe();
179 :
180 283 : clear_guid_cache(cache);
181 :
182 : /*
183 : * Store the GUID's in the cache.
184 : */
185 283 : cache->handle = 0;
186 283 : cache->size = ldb_cnt;
187 283 : cache->entries = talloc_array(d_state, struct GUID, ldb_cnt);
188 283 : if (cache->entries == NULL) {
189 0 : clear_guid_cache(cache);
190 0 : status = NT_STATUS_NO_MEMORY;
191 0 : goto exit;
192 : }
193 :
194 : /*
195 : * Extract a list of the GUIDs for all the matching objects
196 : * we cache just the GUIDS to reduce the memory overhead of
197 : * the result cache.
198 : */
199 8182 : for (i = 0; i < ldb_cnt; i++) {
200 7899 : cache->entries[i] = samdb_result_guid(res[i], "objectGUID");
201 : }
202 283 : exit:
203 283 : TALLOC_FREE(frame);
204 283 : return status;
205 : }
206 :
207 : /*
208 : samr_Connect
209 :
210 : create a connection to the SAM database
211 : */
212 2393 : static NTSTATUS dcesrv_samr_Connect(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
213 : struct samr_Connect *r)
214 : {
215 1825 : struct auth_session_info *session_info =
216 568 : dcesrv_call_session_info(dce_call);
217 : struct samr_connect_state *c_state;
218 : struct dcesrv_handle *handle;
219 :
220 2393 : ZERO_STRUCTP(r->out.connect_handle);
221 :
222 2393 : c_state = talloc(mem_ctx, struct samr_connect_state);
223 2393 : if (!c_state) {
224 0 : return NT_STATUS_NO_MEMORY;
225 : }
226 :
227 : /* make sure the sam database is accessible */
228 4786 : c_state->sam_ctx = samdb_connect(c_state,
229 : dce_call->event_ctx,
230 2393 : dce_call->conn->dce_ctx->lp_ctx,
231 : session_info,
232 2393 : dce_call->conn->remote_address,
233 : 0);
234 2393 : if (c_state->sam_ctx == NULL) {
235 0 : talloc_free(c_state);
236 0 : return NT_STATUS_INVALID_SYSTEM_SERVICE;
237 : }
238 :
239 :
240 2393 : handle = dcesrv_handle_create(dce_call, SAMR_HANDLE_CONNECT);
241 2393 : if (!handle) {
242 0 : talloc_free(c_state);
243 0 : return NT_STATUS_NO_MEMORY;
244 : }
245 :
246 2393 : handle->data = talloc_steal(handle, c_state);
247 :
248 2393 : c_state->access_mask = r->in.access_mask;
249 2393 : *r->out.connect_handle = handle->wire_handle;
250 :
251 2393 : return NT_STATUS_OK;
252 : }
253 :
254 :
255 : /*
256 : samr_Close
257 : */
258 5072 : static NTSTATUS dcesrv_samr_Close(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
259 : struct samr_Close *r)
260 : {
261 : struct dcesrv_handle *h;
262 :
263 5072 : *r->out.handle = *r->in.handle;
264 :
265 5072 : DCESRV_PULL_HANDLE(h, r->in.handle, DCESRV_HANDLE_ANY);
266 :
267 5048 : talloc_free(h);
268 :
269 5048 : ZERO_STRUCTP(r->out.handle);
270 :
271 5048 : return NT_STATUS_OK;
272 : }
273 :
274 :
275 : /*
276 : samr_SetSecurity
277 : */
278 2 : static NTSTATUS dcesrv_samr_SetSecurity(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
279 : struct samr_SetSecurity *r)
280 : {
281 2 : DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
282 : }
283 :
284 :
285 : /*
286 : samr_QuerySecurity
287 : */
288 282 : static NTSTATUS dcesrv_samr_QuerySecurity(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
289 : struct samr_QuerySecurity *r)
290 : {
291 : struct dcesrv_handle *h;
292 : struct sec_desc_buf *sd;
293 :
294 282 : *r->out.sdbuf = NULL;
295 :
296 282 : DCESRV_PULL_HANDLE(h, r->in.handle, DCESRV_HANDLE_ANY);
297 :
298 282 : sd = talloc(mem_ctx, struct sec_desc_buf);
299 282 : if (sd == NULL) {
300 0 : return NT_STATUS_NO_MEMORY;
301 : }
302 :
303 282 : sd->sd = samdb_default_security_descriptor(mem_ctx);
304 :
305 282 : *r->out.sdbuf = sd;
306 :
307 282 : return NT_STATUS_OK;
308 : }
309 :
310 :
311 : /*
312 : samr_Shutdown
313 :
314 : we refuse this operation completely. If a admin wants to shutdown samr
315 : in Samba then they should use the samba admin tools to disable the samr pipe
316 : */
317 0 : static NTSTATUS dcesrv_samr_Shutdown(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
318 : struct samr_Shutdown *r)
319 : {
320 0 : return NT_STATUS_ACCESS_DENIED;
321 : }
322 :
323 :
324 : /*
325 : samr_LookupDomain
326 :
327 : this maps from a domain name to a SID
328 : */
329 634 : static NTSTATUS dcesrv_samr_LookupDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
330 : struct samr_LookupDomain *r)
331 : {
332 : struct samr_connect_state *c_state;
333 : struct dcesrv_handle *h;
334 : struct dom_sid *sid;
335 634 : const char * const dom_attrs[] = { "objectSid", NULL};
336 : struct ldb_message **dom_msgs;
337 : int ret;
338 :
339 634 : *r->out.sid = NULL;
340 :
341 634 : DCESRV_PULL_HANDLE(h, r->in.connect_handle, SAMR_HANDLE_CONNECT);
342 :
343 634 : c_state = h->data;
344 :
345 634 : if (r->in.domain_name->string == NULL) {
346 76 : return NT_STATUS_INVALID_PARAMETER;
347 : }
348 :
349 558 : if (strcasecmp(r->in.domain_name->string, "BUILTIN") == 0) {
350 38 : ret = gendb_search(c_state->sam_ctx,
351 : mem_ctx, NULL, &dom_msgs, dom_attrs,
352 : "(objectClass=builtinDomain)");
353 520 : } else if (strcasecmp_m(r->in.domain_name->string, lpcfg_sam_name(dce_call->conn->dce_ctx->lp_ctx)) == 0) {
354 444 : ret = gendb_search_dn(c_state->sam_ctx,
355 : mem_ctx, ldb_get_default_basedn(c_state->sam_ctx),
356 : &dom_msgs, dom_attrs);
357 : } else {
358 76 : return NT_STATUS_NO_SUCH_DOMAIN;
359 : }
360 482 : if (ret != 1) {
361 0 : return NT_STATUS_NO_SUCH_DOMAIN;
362 : }
363 :
364 482 : sid = samdb_result_dom_sid(mem_ctx, dom_msgs[0],
365 : "objectSid");
366 :
367 482 : if (sid == NULL) {
368 0 : return NT_STATUS_NO_SUCH_DOMAIN;
369 : }
370 :
371 482 : *r->out.sid = sid;
372 :
373 482 : return NT_STATUS_OK;
374 : }
375 :
376 :
377 : /*
378 : samr_EnumDomains
379 :
380 : list the domains in the SAM
381 : */
382 190 : static NTSTATUS dcesrv_samr_EnumDomains(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
383 : struct samr_EnumDomains *r)
384 : {
385 : struct dcesrv_handle *h;
386 : struct samr_SamArray *array;
387 : uint32_t i, start_i;
388 :
389 190 : *r->out.resume_handle = 0;
390 190 : *r->out.sam = NULL;
391 190 : *r->out.num_entries = 0;
392 :
393 190 : DCESRV_PULL_HANDLE(h, r->in.connect_handle, SAMR_HANDLE_CONNECT);
394 :
395 190 : *r->out.resume_handle = 2;
396 :
397 190 : start_i = *r->in.resume_handle;
398 :
399 190 : if (start_i >= 2) {
400 : /* search past end of list is not an error for this call */
401 38 : return NT_STATUS_OK;
402 : }
403 :
404 152 : array = talloc(mem_ctx, struct samr_SamArray);
405 152 : if (array == NULL) {
406 0 : return NT_STATUS_NO_MEMORY;
407 : }
408 :
409 152 : array->count = 0;
410 152 : array->entries = NULL;
411 :
412 152 : array->entries = talloc_array(mem_ctx, struct samr_SamEntry, 2 - start_i);
413 152 : if (array->entries == NULL) {
414 0 : return NT_STATUS_NO_MEMORY;
415 : }
416 :
417 456 : for (i=0;i<2-start_i;i++) {
418 304 : array->entries[i].idx = start_i + i;
419 304 : if (i == 0) {
420 152 : array->entries[i].name.string = lpcfg_sam_name(dce_call->conn->dce_ctx->lp_ctx);
421 : } else {
422 152 : array->entries[i].name.string = "BUILTIN";
423 : }
424 : }
425 :
426 152 : *r->out.sam = array;
427 152 : *r->out.num_entries = i;
428 152 : array->count = *r->out.num_entries;
429 :
430 152 : return NT_STATUS_OK;
431 : }
432 :
433 :
434 : /*
435 : samr_OpenDomain
436 : */
437 1888 : static NTSTATUS dcesrv_samr_OpenDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
438 : struct samr_OpenDomain *r)
439 : {
440 : struct dcesrv_handle *h_conn, *h_domain;
441 : struct samr_connect_state *c_state;
442 : struct samr_domain_state *d_state;
443 1888 : const char * const dom_attrs[] = { "cn", NULL};
444 : struct ldb_message **dom_msgs;
445 : int ret;
446 : unsigned int i;
447 :
448 1888 : ZERO_STRUCTP(r->out.domain_handle);
449 :
450 1888 : DCESRV_PULL_HANDLE(h_conn, r->in.connect_handle, SAMR_HANDLE_CONNECT);
451 :
452 1878 : c_state = h_conn->data;
453 :
454 1878 : if (r->in.sid == NULL) {
455 0 : return NT_STATUS_INVALID_PARAMETER;
456 : }
457 :
458 1878 : d_state = talloc(mem_ctx, struct samr_domain_state);
459 1878 : if (!d_state) {
460 0 : return NT_STATUS_NO_MEMORY;
461 : }
462 :
463 1878 : d_state->domain_sid = talloc_steal(d_state, r->in.sid);
464 :
465 1878 : if (dom_sid_equal(d_state->domain_sid, dom_sid_parse_talloc(mem_ctx, SID_BUILTIN))) {
466 566 : d_state->builtin = true;
467 566 : d_state->domain_name = "BUILTIN";
468 : } else {
469 1312 : d_state->builtin = false;
470 1312 : d_state->domain_name = lpcfg_sam_name(dce_call->conn->dce_ctx->lp_ctx);
471 : }
472 :
473 1878 : ret = gendb_search(c_state->sam_ctx,
474 : mem_ctx, ldb_get_default_basedn(c_state->sam_ctx), &dom_msgs, dom_attrs,
475 : "(objectSid=%s)",
476 1878 : ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
477 :
478 1878 : if (ret == 0) {
479 0 : talloc_free(d_state);
480 0 : return NT_STATUS_NO_SUCH_DOMAIN;
481 1878 : } else if (ret > 1) {
482 0 : talloc_free(d_state);
483 0 : return NT_STATUS_INTERNAL_DB_CORRUPTION;
484 1878 : } else if (ret == -1) {
485 0 : talloc_free(d_state);
486 0 : DEBUG(1, ("Failed to open domain %s: %s\n", dom_sid_string(mem_ctx, r->in.sid), ldb_errstring(c_state->sam_ctx)));
487 0 : return NT_STATUS_INTERNAL_DB_CORRUPTION;
488 : }
489 :
490 1878 : d_state->domain_dn = talloc_steal(d_state, dom_msgs[0]->dn);
491 1878 : d_state->role = lpcfg_server_role(dce_call->conn->dce_ctx->lp_ctx);
492 1878 : d_state->connect_state = talloc_reference(d_state, c_state);
493 1878 : d_state->sam_ctx = c_state->sam_ctx;
494 1878 : d_state->access_mask = r->in.access_mask;
495 1878 : d_state->domain_users_cached = NULL;
496 :
497 1878 : d_state->lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
498 :
499 7512 : for (i = 0; i < SAMR_LAST_CACHE; i++) {
500 6144 : initialize_guid_cache(&d_state->guid_caches[i]);
501 : }
502 :
503 1878 : h_domain = dcesrv_handle_create(dce_call, SAMR_HANDLE_DOMAIN);
504 1878 : if (!h_domain) {
505 0 : talloc_free(d_state);
506 0 : return NT_STATUS_NO_MEMORY;
507 : }
508 :
509 1878 : h_domain->data = talloc_steal(h_domain, d_state);
510 :
511 1878 : *r->out.domain_handle = h_domain->wire_handle;
512 :
513 1878 : return NT_STATUS_OK;
514 : }
515 :
516 : /*
517 : return DomInfo1
518 : */
519 57 : static NTSTATUS dcesrv_samr_info_DomInfo1(struct samr_domain_state *state,
520 : TALLOC_CTX *mem_ctx,
521 : struct ldb_message **dom_msgs,
522 : struct samr_DomInfo1 *info)
523 : {
524 57 : info->min_password_length =
525 57 : ldb_msg_find_attr_as_uint(dom_msgs[0], "minPwdLength", 0);
526 57 : info->password_history_length =
527 57 : ldb_msg_find_attr_as_uint(dom_msgs[0], "pwdHistoryLength", 0);
528 57 : info->password_properties =
529 57 : ldb_msg_find_attr_as_uint(dom_msgs[0], "pwdProperties", 0);
530 57 : info->max_password_age =
531 57 : ldb_msg_find_attr_as_int64(dom_msgs[0], "maxPwdAge", 0);
532 57 : info->min_password_age =
533 57 : ldb_msg_find_attr_as_int64(dom_msgs[0], "minPwdAge", 0);
534 :
535 57 : return NT_STATUS_OK;
536 : }
537 :
538 : /*
539 : return DomInfo2
540 : */
541 152 : static NTSTATUS dcesrv_samr_info_DomGeneralInformation(struct samr_domain_state *state,
542 : TALLOC_CTX *mem_ctx,
543 : struct ldb_message **dom_msgs,
544 : struct samr_DomGeneralInformation *info)
545 : {
546 152 : size_t count = 0;
547 152 : const enum ldb_scope scope = LDB_SCOPE_SUBTREE;
548 152 : int ret = 0;
549 :
550 : /* MS-SAMR 2.2.4.1 - ReplicaSourceNodeName: "domainReplica" attribute */
551 152 : info->primary.string = ldb_msg_find_attr_as_string(dom_msgs[0],
552 : "domainReplica",
553 : "");
554 :
555 152 : info->force_logoff_time = ldb_msg_find_attr_as_uint64(dom_msgs[0], "forceLogoff",
556 : 0x8000000000000000LL);
557 :
558 152 : info->oem_information.string = ldb_msg_find_attr_as_string(dom_msgs[0],
559 : "oEMInformation",
560 : "");
561 152 : info->domain_name.string = state->domain_name;
562 :
563 152 : info->sequence_num = ldb_msg_find_attr_as_uint64(dom_msgs[0], "modifiedCount",
564 : 0);
565 152 : switch (state->role) {
566 108 : case ROLE_ACTIVE_DIRECTORY_DC:
567 : /* This pulls the NetBIOS name from the
568 : cn=NTDS Settings,cn=<NETBIOS name of PDC>,....
569 : string */
570 108 : if (samdb_is_pdc(state->sam_ctx)) {
571 96 : info->role = SAMR_ROLE_DOMAIN_PDC;
572 : } else {
573 12 : info->role = SAMR_ROLE_DOMAIN_BDC;
574 : }
575 130 : break;
576 0 : case ROLE_DOMAIN_PDC:
577 : case ROLE_DOMAIN_BDC:
578 : case ROLE_AUTO:
579 0 : return NT_STATUS_INTERNAL_ERROR;
580 44 : case ROLE_DOMAIN_MEMBER:
581 44 : info->role = SAMR_ROLE_DOMAIN_MEMBER;
582 44 : break;
583 0 : case ROLE_STANDALONE:
584 0 : info->role = SAMR_ROLE_STANDALONE;
585 0 : break;
586 : }
587 :
588 : /*
589 : * Users are not meant to be in BUILTIN
590 : * so to speed up the query we do not filter on domain_sid
591 : */
592 253 : ret = dsdb_domain_count(
593 152 : state->sam_ctx,
594 : &count,
595 : state->domain_dn,
596 : NULL,
597 : scope,
598 : "(objectClass=user)");
599 152 : if (ret != LDB_SUCCESS || count > UINT32_MAX) {
600 0 : goto error;
601 : }
602 152 : info->num_users = count;
603 :
604 : /*
605 : * Groups are not meant to be in BUILTIN
606 : * so to speed up the query we do not filter on domain_sid
607 : */
608 253 : ret = dsdb_domain_count(
609 152 : state->sam_ctx,
610 : &count,
611 : state->domain_dn,
612 : NULL,
613 : scope,
614 : "(&(objectClass=group)(|(groupType=%d)(groupType=%d)))",
615 : GTYPE_SECURITY_UNIVERSAL_GROUP,
616 : GTYPE_SECURITY_GLOBAL_GROUP);
617 253 : if (ret != LDB_SUCCESS || count > UINT32_MAX) {
618 0 : goto error;
619 : }
620 152 : info->num_groups = count;
621 :
622 253 : ret = dsdb_domain_count(
623 152 : state->sam_ctx,
624 : &count,
625 : state->domain_dn,
626 : state->domain_sid,
627 : scope,
628 : "(&(objectClass=group)(|(groupType=%d)(groupType=%d)))",
629 : GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
630 : GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
631 253 : if (ret != LDB_SUCCESS || count > UINT32_MAX) {
632 0 : goto error;
633 : }
634 152 : info->num_aliases = count;
635 :
636 152 : return NT_STATUS_OK;
637 :
638 0 : error:
639 0 : if (count > UINT32_MAX) {
640 0 : return NT_STATUS_INTEGER_OVERFLOW;
641 : }
642 0 : return dsdb_ldb_err_to_ntstatus(ret);
643 :
644 : }
645 :
646 : /*
647 : return DomInfo3
648 : */
649 34 : static NTSTATUS dcesrv_samr_info_DomInfo3(struct samr_domain_state *state,
650 : TALLOC_CTX *mem_ctx,
651 : struct ldb_message **dom_msgs,
652 : struct samr_DomInfo3 *info)
653 : {
654 34 : info->force_logoff_time = ldb_msg_find_attr_as_uint64(dom_msgs[0], "forceLogoff",
655 : 0x8000000000000000LL);
656 :
657 34 : return NT_STATUS_OK;
658 : }
659 :
660 : /*
661 : return DomInfo4
662 : */
663 30 : static NTSTATUS dcesrv_samr_info_DomOEMInformation(struct samr_domain_state *state,
664 : TALLOC_CTX *mem_ctx,
665 : struct ldb_message **dom_msgs,
666 : struct samr_DomOEMInformation *info)
667 : {
668 30 : info->oem_information.string = ldb_msg_find_attr_as_string(dom_msgs[0],
669 : "oEMInformation",
670 : "");
671 :
672 30 : return NT_STATUS_OK;
673 : }
674 :
675 : /*
676 : return DomInfo5
677 : */
678 31 : static NTSTATUS dcesrv_samr_info_DomInfo5(struct samr_domain_state *state,
679 : TALLOC_CTX *mem_ctx,
680 : struct ldb_message **dom_msgs,
681 : struct samr_DomInfo5 *info)
682 : {
683 31 : info->domain_name.string = state->domain_name;
684 :
685 31 : return NT_STATUS_OK;
686 : }
687 :
688 : /*
689 : return DomInfo6
690 : */
691 31 : static NTSTATUS dcesrv_samr_info_DomInfo6(struct samr_domain_state *state,
692 : TALLOC_CTX *mem_ctx,
693 : struct ldb_message **dom_msgs,
694 : struct samr_DomInfo6 *info)
695 : {
696 : /* MS-SAMR 2.2.4.1 - ReplicaSourceNodeName: "domainReplica" attribute */
697 31 : info->primary.string = ldb_msg_find_attr_as_string(dom_msgs[0],
698 : "domainReplica",
699 : "");
700 :
701 31 : return NT_STATUS_OK;
702 : }
703 :
704 : /*
705 : return DomInfo7
706 : */
707 31 : static NTSTATUS dcesrv_samr_info_DomInfo7(struct samr_domain_state *state,
708 : TALLOC_CTX *mem_ctx,
709 : struct ldb_message **dom_msgs,
710 : struct samr_DomInfo7 *info)
711 : {
712 :
713 31 : switch (state->role) {
714 19 : case ROLE_ACTIVE_DIRECTORY_DC:
715 : /* This pulls the NetBIOS name from the
716 : cn=NTDS Settings,cn=<NETBIOS name of PDC>,....
717 : string */
718 19 : if (samdb_is_pdc(state->sam_ctx)) {
719 19 : info->role = SAMR_ROLE_DOMAIN_PDC;
720 : } else {
721 0 : info->role = SAMR_ROLE_DOMAIN_BDC;
722 : }
723 25 : break;
724 0 : case ROLE_DOMAIN_PDC:
725 : case ROLE_DOMAIN_BDC:
726 : case ROLE_AUTO:
727 0 : return NT_STATUS_INTERNAL_ERROR;
728 12 : case ROLE_DOMAIN_MEMBER:
729 12 : info->role = SAMR_ROLE_DOMAIN_MEMBER;
730 12 : break;
731 0 : case ROLE_STANDALONE:
732 0 : info->role = SAMR_ROLE_STANDALONE;
733 0 : break;
734 : }
735 :
736 31 : return NT_STATUS_OK;
737 : }
738 :
739 : /*
740 : return DomInfo8
741 : */
742 35 : static NTSTATUS dcesrv_samr_info_DomInfo8(struct samr_domain_state *state,
743 : TALLOC_CTX *mem_ctx,
744 : struct ldb_message **dom_msgs,
745 : struct samr_DomInfo8 *info)
746 : {
747 35 : info->sequence_num = ldb_msg_find_attr_as_uint64(dom_msgs[0], "modifiedCount",
748 35 : time(NULL));
749 :
750 35 : info->domain_create_time = ldb_msg_find_attr_as_uint(dom_msgs[0], "creationTime",
751 : 0x0LL);
752 :
753 35 : return NT_STATUS_OK;
754 : }
755 :
756 : /*
757 : return DomInfo9
758 : */
759 30 : static NTSTATUS dcesrv_samr_info_DomInfo9(struct samr_domain_state *state,
760 : TALLOC_CTX *mem_ctx,
761 : struct ldb_message **dom_msgs,
762 : struct samr_DomInfo9 *info)
763 : {
764 30 : info->domain_server_state = DOMAIN_SERVER_ENABLED;
765 :
766 30 : return NT_STATUS_OK;
767 : }
768 :
769 : /*
770 : return DomInfo11
771 : */
772 30 : static NTSTATUS dcesrv_samr_info_DomGeneralInformation2(struct samr_domain_state *state,
773 : TALLOC_CTX *mem_ctx,
774 : struct ldb_message **dom_msgs,
775 : struct samr_DomGeneralInformation2 *info)
776 : {
777 : NTSTATUS status;
778 30 : status = dcesrv_samr_info_DomGeneralInformation(state, mem_ctx, dom_msgs, &info->general);
779 30 : if (!NT_STATUS_IS_OK(status)) {
780 0 : return status;
781 : }
782 :
783 30 : info->lockout_duration = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutDuration",
784 : -18000000000LL);
785 30 : info->lockout_window = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockOutObservationWindow",
786 : -18000000000LL);
787 30 : info->lockout_threshold = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutThreshold", 0);
788 :
789 30 : return NT_STATUS_OK;
790 : }
791 :
792 : /*
793 : return DomInfo12
794 : */
795 51 : static NTSTATUS dcesrv_samr_info_DomInfo12(struct samr_domain_state *state,
796 : TALLOC_CTX *mem_ctx,
797 : struct ldb_message **dom_msgs,
798 : struct samr_DomInfo12 *info)
799 : {
800 51 : info->lockout_duration = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutDuration",
801 : -18000000000LL);
802 51 : info->lockout_window = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockOutObservationWindow",
803 : -18000000000LL);
804 51 : info->lockout_threshold = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutThreshold", 0);
805 :
806 51 : return NT_STATUS_OK;
807 : }
808 :
809 : /*
810 : return DomInfo13
811 : */
812 30 : static NTSTATUS dcesrv_samr_info_DomInfo13(struct samr_domain_state *state,
813 : TALLOC_CTX *mem_ctx,
814 : struct ldb_message **dom_msgs,
815 : struct samr_DomInfo13 *info)
816 : {
817 30 : info->sequence_num = ldb_msg_find_attr_as_uint64(dom_msgs[0], "modifiedCount",
818 30 : time(NULL));
819 :
820 30 : info->domain_create_time = ldb_msg_find_attr_as_uint(dom_msgs[0], "creationTime",
821 : 0x0LL);
822 :
823 30 : info->modified_count_at_last_promotion = 0;
824 :
825 30 : return NT_STATUS_OK;
826 : }
827 :
828 : /*
829 : samr_QueryDomainInfo
830 : */
831 512 : static NTSTATUS dcesrv_samr_QueryDomainInfo(struct dcesrv_call_state *dce_call,
832 : TALLOC_CTX *mem_ctx,
833 : struct samr_QueryDomainInfo *r)
834 : {
835 : struct dcesrv_handle *h;
836 : struct samr_domain_state *d_state;
837 : union samr_DomainInfo *info;
838 :
839 : struct ldb_message **dom_msgs;
840 512 : const char * const *attrs = NULL;
841 :
842 512 : *r->out.info = NULL;
843 :
844 512 : DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
845 :
846 512 : d_state = h->data;
847 :
848 512 : switch (r->in.level) {
849 57 : case 1:
850 : {
851 : static const char * const attrs2[] = { "minPwdLength",
852 : "pwdHistoryLength",
853 : "pwdProperties",
854 : "maxPwdAge",
855 : "minPwdAge",
856 : NULL };
857 57 : attrs = attrs2;
858 57 : break;
859 : }
860 122 : case 2:
861 : {
862 : static const char * const attrs2[] = {"forceLogoff",
863 : "oEMInformation",
864 : "modifiedCount",
865 : "domainReplica",
866 : NULL};
867 122 : attrs = attrs2;
868 122 : break;
869 : }
870 34 : case 3:
871 : {
872 : static const char * const attrs2[] = {"forceLogoff",
873 : NULL};
874 34 : attrs = attrs2;
875 34 : break;
876 : }
877 30 : case 4:
878 : {
879 : static const char * const attrs2[] = {"oEMInformation",
880 : NULL};
881 30 : attrs = attrs2;
882 30 : break;
883 : }
884 31 : case 5:
885 : {
886 31 : attrs = NULL;
887 31 : break;
888 : }
889 31 : case 6:
890 : {
891 : static const char * const attrs2[] = { "domainReplica",
892 : NULL };
893 31 : attrs = attrs2;
894 31 : break;
895 : }
896 31 : case 7:
897 : {
898 31 : attrs = NULL;
899 31 : break;
900 : }
901 35 : case 8:
902 : {
903 : static const char * const attrs2[] = { "modifiedCount",
904 : "creationTime",
905 : NULL };
906 35 : attrs = attrs2;
907 35 : break;
908 : }
909 30 : case 9:
910 : {
911 30 : attrs = NULL;
912 30 : break;
913 : }
914 30 : case 11:
915 : {
916 : static const char * const attrs2[] = { "oEMInformation",
917 : "forceLogoff",
918 : "modifiedCount",
919 : "lockoutDuration",
920 : "lockOutObservationWindow",
921 : "lockoutThreshold",
922 : NULL};
923 30 : attrs = attrs2;
924 30 : break;
925 : }
926 51 : case 12:
927 : {
928 : static const char * const attrs2[] = { "lockoutDuration",
929 : "lockOutObservationWindow",
930 : "lockoutThreshold",
931 : NULL};
932 51 : attrs = attrs2;
933 51 : break;
934 : }
935 30 : case 13:
936 : {
937 : static const char * const attrs2[] = { "modifiedCount",
938 : "creationTime",
939 : NULL };
940 30 : attrs = attrs2;
941 30 : break;
942 : }
943 0 : default:
944 : {
945 0 : return NT_STATUS_INVALID_INFO_CLASS;
946 : }
947 : }
948 :
949 : /* some levels don't need a search */
950 512 : if (attrs) {
951 : int ret;
952 420 : ret = gendb_search_dn(d_state->sam_ctx, mem_ctx,
953 : d_state->domain_dn, &dom_msgs, attrs);
954 420 : if (ret == 0) {
955 0 : return NT_STATUS_NO_SUCH_DOMAIN;
956 : }
957 420 : if (ret != 1) {
958 0 : return NT_STATUS_INTERNAL_DB_CORRUPTION;
959 : }
960 : }
961 :
962 : /* allocate the info structure */
963 512 : info = talloc_zero(mem_ctx, union samr_DomainInfo);
964 512 : if (info == NULL) {
965 0 : return NT_STATUS_NO_MEMORY;
966 : }
967 :
968 512 : *r->out.info = info;
969 :
970 512 : switch (r->in.level) {
971 57 : case 1:
972 57 : return dcesrv_samr_info_DomInfo1(d_state, mem_ctx, dom_msgs,
973 : &info->info1);
974 122 : case 2:
975 122 : return dcesrv_samr_info_DomGeneralInformation(d_state, mem_ctx, dom_msgs,
976 : &info->general);
977 34 : case 3:
978 34 : return dcesrv_samr_info_DomInfo3(d_state, mem_ctx, dom_msgs,
979 : &info->info3);
980 30 : case 4:
981 30 : return dcesrv_samr_info_DomOEMInformation(d_state, mem_ctx, dom_msgs,
982 : &info->oem);
983 31 : case 5:
984 31 : return dcesrv_samr_info_DomInfo5(d_state, mem_ctx, dom_msgs,
985 : &info->info5);
986 31 : case 6:
987 31 : return dcesrv_samr_info_DomInfo6(d_state, mem_ctx, dom_msgs,
988 : &info->info6);
989 31 : case 7:
990 31 : return dcesrv_samr_info_DomInfo7(d_state, mem_ctx, dom_msgs,
991 : &info->info7);
992 35 : case 8:
993 35 : return dcesrv_samr_info_DomInfo8(d_state, mem_ctx, dom_msgs,
994 : &info->info8);
995 30 : case 9:
996 30 : return dcesrv_samr_info_DomInfo9(d_state, mem_ctx, dom_msgs,
997 : &info->info9);
998 30 : case 11:
999 30 : return dcesrv_samr_info_DomGeneralInformation2(d_state, mem_ctx, dom_msgs,
1000 : &info->general2);
1001 51 : case 12:
1002 51 : return dcesrv_samr_info_DomInfo12(d_state, mem_ctx, dom_msgs,
1003 : &info->info12);
1004 30 : case 13:
1005 30 : return dcesrv_samr_info_DomInfo13(d_state, mem_ctx, dom_msgs,
1006 : &info->info13);
1007 0 : default:
1008 0 : return NT_STATUS_INVALID_INFO_CLASS;
1009 : }
1010 : }
1011 :
1012 :
1013 : /*
1014 : samr_SetDomainInfo
1015 : */
1016 258 : static NTSTATUS dcesrv_samr_SetDomainInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1017 : struct samr_SetDomainInfo *r)
1018 : {
1019 : struct dcesrv_handle *h;
1020 : struct samr_domain_state *d_state;
1021 : struct ldb_message *msg;
1022 : int ret;
1023 : struct ldb_context *sam_ctx;
1024 :
1025 258 : DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1026 :
1027 258 : d_state = h->data;
1028 258 : sam_ctx = d_state->sam_ctx;
1029 :
1030 258 : msg = ldb_msg_new(mem_ctx);
1031 258 : if (msg == NULL) {
1032 0 : return NT_STATUS_NO_MEMORY;
1033 : }
1034 :
1035 258 : msg->dn = talloc_reference(mem_ctx, d_state->domain_dn);
1036 258 : if (!msg->dn) {
1037 0 : return NT_STATUS_NO_MEMORY;
1038 : }
1039 :
1040 258 : switch (r->in.level) {
1041 63 : case 1:
1042 63 : SET_UINT (msg, info1.min_password_length, "minPwdLength");
1043 63 : SET_UINT (msg, info1.password_history_length, "pwdHistoryLength");
1044 63 : SET_UINT (msg, info1.password_properties, "pwdProperties");
1045 63 : SET_INT64 (msg, info1.max_password_age, "maxPwdAge");
1046 63 : SET_INT64 (msg, info1.min_password_age, "minPwdAge");
1047 63 : break;
1048 11 : case 3:
1049 11 : SET_UINT64 (msg, info3.force_logoff_time, "forceLogoff");
1050 11 : break;
1051 20 : case 4:
1052 20 : SET_STRING(msg, oem.oem_information, "oEMInformation");
1053 20 : break;
1054 :
1055 30 : case 6:
1056 : case 7:
1057 : case 9:
1058 : /* No op, we don't know where to set these */
1059 30 : return NT_STATUS_OK;
1060 :
1061 84 : case 12:
1062 : /*
1063 : * It is not possible to set lockout_duration < lockout_window.
1064 : * (The test is the other way around since the negative numbers
1065 : * are stored...)
1066 : *
1067 : * TODO:
1068 : * This check should be moved to the backend, i.e. to some
1069 : * ldb module under dsdb/samdb/ldb_modules/ .
1070 : *
1071 : * This constraint is documented here for the samr rpc service:
1072 : * MS-SAMR 3.1.1.6 Attribute Constraints for Originating Updates
1073 : * http://msdn.microsoft.com/en-us/library/cc245667%28PROT.10%29.aspx
1074 : *
1075 : * And here for the ldap backend:
1076 : * MS-ADTS 3.1.1.5.3.2 Constraints
1077 : * http://msdn.microsoft.com/en-us/library/cc223462(PROT.10).aspx
1078 : */
1079 159 : if (r->in.info->info12.lockout_duration >
1080 84 : r->in.info->info12.lockout_window)
1081 : {
1082 12 : return NT_STATUS_INVALID_PARAMETER;
1083 : }
1084 72 : SET_INT64 (msg, info12.lockout_duration, "lockoutDuration");
1085 72 : SET_INT64 (msg, info12.lockout_window, "lockOutObservationWindow");
1086 72 : SET_INT64 (msg, info12.lockout_threshold, "lockoutThreshold");
1087 72 : break;
1088 :
1089 50 : default:
1090 : /* many info classes are not valid for SetDomainInfo */
1091 50 : return NT_STATUS_INVALID_INFO_CLASS;
1092 : }
1093 :
1094 : /* modify the samdb record */
1095 166 : ret = ldb_modify(sam_ctx, msg);
1096 166 : if (ret != LDB_SUCCESS) {
1097 0 : DEBUG(1,("Failed to modify record %s: %s\n",
1098 : ldb_dn_get_linearized(d_state->domain_dn),
1099 : ldb_errstring(sam_ctx)));
1100 0 : return dsdb_ldb_err_to_ntstatus(ret);
1101 : }
1102 :
1103 166 : return NT_STATUS_OK;
1104 : }
1105 :
1106 : /*
1107 : samr_CreateDomainGroup
1108 : */
1109 1586 : static NTSTATUS dcesrv_samr_CreateDomainGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1110 : struct samr_CreateDomainGroup *r)
1111 : {
1112 : NTSTATUS status;
1113 : struct samr_domain_state *d_state;
1114 : struct samr_account_state *a_state;
1115 : struct dcesrv_handle *h;
1116 : const char *groupname;
1117 : struct dom_sid *group_sid;
1118 : struct ldb_dn *group_dn;
1119 : struct dcesrv_handle *g_handle;
1120 :
1121 1586 : ZERO_STRUCTP(r->out.group_handle);
1122 1586 : *r->out.rid = 0;
1123 :
1124 1586 : DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1125 :
1126 1586 : d_state = h->data;
1127 :
1128 1586 : if (d_state->builtin) {
1129 755 : DEBUG(5, ("Cannot create a domain group in the BUILTIN domain"));
1130 755 : return NT_STATUS_ACCESS_DENIED;
1131 : }
1132 :
1133 831 : groupname = r->in.name->string;
1134 :
1135 831 : if (groupname == NULL) {
1136 0 : return NT_STATUS_INVALID_PARAMETER;
1137 : }
1138 :
1139 831 : status = dsdb_add_domain_group(d_state->sam_ctx, mem_ctx, groupname, &group_sid, &group_dn);
1140 831 : if (!NT_STATUS_IS_OK(status)) {
1141 1 : return status;
1142 : }
1143 :
1144 830 : a_state = talloc(mem_ctx, struct samr_account_state);
1145 830 : if (!a_state) {
1146 0 : return NT_STATUS_NO_MEMORY;
1147 : }
1148 830 : a_state->sam_ctx = d_state->sam_ctx;
1149 830 : a_state->access_mask = r->in.access_mask;
1150 830 : a_state->domain_state = talloc_reference(a_state, d_state);
1151 830 : a_state->account_dn = talloc_steal(a_state, group_dn);
1152 :
1153 830 : a_state->account_name = talloc_steal(a_state, groupname);
1154 :
1155 : /* create the policy handle */
1156 830 : g_handle = dcesrv_handle_create(dce_call, SAMR_HANDLE_GROUP);
1157 830 : if (!g_handle) {
1158 0 : return NT_STATUS_NO_MEMORY;
1159 : }
1160 :
1161 830 : g_handle->data = talloc_steal(g_handle, a_state);
1162 :
1163 830 : *r->out.group_handle = g_handle->wire_handle;
1164 830 : *r->out.rid = group_sid->sub_auths[group_sid->num_auths-1];
1165 :
1166 830 : return NT_STATUS_OK;
1167 : }
1168 :
1169 :
1170 : /*
1171 : comparison function for sorting SamEntry array
1172 : */
1173 45714 : static int compare_SamEntry(struct samr_SamEntry *e1, struct samr_SamEntry *e2)
1174 : {
1175 45714 : return e1->idx - e2->idx;
1176 : }
1177 :
1178 17976 : static int compare_msgRid(struct ldb_message **m1, struct ldb_message **m2) {
1179 17976 : struct dom_sid *sid1 = NULL;
1180 17976 : struct dom_sid *sid2 = NULL;
1181 : uint32_t rid1;
1182 : uint32_t rid2;
1183 17976 : int res = 0;
1184 : NTSTATUS status;
1185 17976 : TALLOC_CTX *frame = talloc_stackframe();
1186 :
1187 17976 : sid1 = samdb_result_dom_sid(frame, *m1, "objectSid");
1188 17976 : sid2 = samdb_result_dom_sid(frame, *m2, "objectSid");
1189 :
1190 : /*
1191 : * If entries don't have a SID we want to sort them to the end of
1192 : * the list.
1193 : */
1194 17976 : if (sid1 == NULL && sid2 == NULL) {
1195 0 : res = 0;
1196 0 : goto exit;
1197 17976 : } else if (sid2 == NULL) {
1198 0 : res = 1;
1199 0 : goto exit;
1200 17976 : } else if (sid1 == NULL) {
1201 0 : res = -1;
1202 0 : goto exit;
1203 : }
1204 :
1205 : /*
1206 : * Get and compare the rids, if we fail to extract a rid treat it as a
1207 : * missing SID and sort to the end of the list
1208 : */
1209 17976 : status = dom_sid_split_rid(NULL, sid1, NULL, &rid1);
1210 17976 : if (!NT_STATUS_IS_OK(status)) {
1211 0 : res = 1;
1212 0 : goto exit;
1213 : }
1214 :
1215 17976 : status = dom_sid_split_rid(NULL, sid2, NULL, &rid2);
1216 17976 : if (!NT_STATUS_IS_OK(status)) {
1217 0 : res = -1;
1218 0 : goto exit;
1219 : }
1220 :
1221 17976 : if (rid1 == rid2) {
1222 0 : res = 0;
1223 : }
1224 17976 : else if (rid1 > rid2) {
1225 9390 : res = 1;
1226 : }
1227 : else {
1228 8586 : res = -1;
1229 : }
1230 17976 : exit:
1231 17976 : TALLOC_FREE(frame);
1232 17976 : return res;
1233 : }
1234 :
1235 : /*
1236 : samr_EnumDomainGroups
1237 : */
1238 183 : static NTSTATUS dcesrv_samr_EnumDomainGroups(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1239 : struct samr_EnumDomainGroups *r)
1240 : {
1241 : struct dcesrv_handle *h;
1242 : struct samr_domain_state *d_state;
1243 : struct ldb_message **res;
1244 : uint32_t i;
1245 : uint32_t count;
1246 : uint32_t results;
1247 : uint32_t max_entries;
1248 : uint32_t remaining_entries;
1249 : uint32_t resume_handle;
1250 : struct samr_SamEntry *entries;
1251 183 : const char * const attrs[] = { "objectSid", "sAMAccountName", NULL };
1252 183 : const char * const cache_attrs[] = { "objectSid", "objectGUID", NULL };
1253 : struct samr_SamArray *sam;
1254 183 : struct samr_guid_cache *cache = NULL;
1255 :
1256 183 : *r->out.resume_handle = 0;
1257 183 : *r->out.sam = NULL;
1258 183 : *r->out.num_entries = 0;
1259 :
1260 183 : DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1261 :
1262 183 : d_state = h->data;
1263 183 : cache = &d_state->guid_caches[SAMR_ENUM_DOMAIN_GROUPS_CACHE];
1264 :
1265 : /*
1266 : * If the resume_handle is zero, query the database and cache the
1267 : * matching GUID's
1268 : */
1269 183 : if (*r->in.resume_handle == 0) {
1270 : NTSTATUS status;
1271 : int ldb_cnt;
1272 64 : clear_guid_cache(cache);
1273 : /*
1274 : * search for all domain groups in this domain.
1275 : */
1276 109 : ldb_cnt = samdb_search_domain(
1277 64 : d_state->sam_ctx,
1278 : mem_ctx,
1279 : d_state->domain_dn,
1280 : &res,
1281 : cache_attrs,
1282 64 : d_state->domain_sid,
1283 : "(&(|(groupType=%d)(groupType=%d))(objectClass=group))",
1284 : GTYPE_SECURITY_UNIVERSAL_GROUP,
1285 : GTYPE_SECURITY_GLOBAL_GROUP);
1286 64 : if (ldb_cnt < 0) {
1287 0 : return NT_STATUS_INTERNAL_DB_CORRUPTION;
1288 : }
1289 : /*
1290 : * Sort the results into RID order, while the spec states there
1291 : * is no order, Windows appears to sort the results by RID and
1292 : * so it is possible that there are clients that depend on
1293 : * this ordering
1294 : */
1295 64 : TYPESAFE_QSORT(res, ldb_cnt, compare_msgRid);
1296 :
1297 : /*
1298 : * cache the sorted GUID's
1299 : */
1300 64 : status = load_guid_cache(cache, d_state, ldb_cnt, res);
1301 64 : TALLOC_FREE(res);
1302 64 : if (!NT_STATUS_IS_OK(status)) {
1303 0 : return status;
1304 : }
1305 64 : cache->handle = 0;
1306 : }
1307 :
1308 :
1309 : /*
1310 : * If the resume handle is out of range we return an empty response
1311 : * and invalidate the cache.
1312 : *
1313 : * From the specification:
1314 : * Servers SHOULD validate that EnumerationContext is an expected
1315 : * value for the server's implementation. Windows does NOT validate
1316 : * the input, though the result of malformed information merely results
1317 : * in inconsistent output to the client.
1318 : */
1319 183 : if (*r->in.resume_handle >= cache->size) {
1320 16 : clear_guid_cache(cache);
1321 16 : sam = talloc(mem_ctx, struct samr_SamArray);
1322 16 : if (!sam) {
1323 0 : return NT_STATUS_NO_MEMORY;
1324 : }
1325 16 : sam->entries = NULL;
1326 16 : sam->count = 0;
1327 :
1328 16 : *r->out.sam = sam;
1329 16 : *r->out.resume_handle = 0;
1330 16 : return NT_STATUS_OK;
1331 : }
1332 :
1333 :
1334 : /*
1335 : * Calculate the number of entries to return limit by max_size.
1336 : * Note that we use the w2k3 element size value of 54
1337 : */
1338 167 : max_entries = 1 + (r->in.max_size/SAMR_ENUM_USERS_MULTIPLIER);
1339 167 : remaining_entries = cache->size - *r->in.resume_handle;
1340 167 : results = MIN(remaining_entries, max_entries);
1341 :
1342 : /*
1343 : * Process the list of result GUID's.
1344 : * Read the details of each object and populate the Entries
1345 : * for the current level.
1346 : */
1347 167 : count = 0;
1348 167 : resume_handle = *r->in.resume_handle;
1349 167 : entries = talloc_array(mem_ctx, struct samr_SamEntry, results);
1350 167 : if (entries == NULL) {
1351 0 : clear_guid_cache(cache);
1352 0 : return NT_STATUS_NO_MEMORY;
1353 : }
1354 3324 : for (i = 0; i < results; i++) {
1355 : struct dom_sid *objectsid;
1356 : uint32_t rid;
1357 : struct ldb_result *rec;
1358 3157 : const uint32_t idx = *r->in.resume_handle + i;
1359 : int ret;
1360 : NTSTATUS status;
1361 3157 : const char *name = NULL;
1362 3157 : resume_handle++;
1363 : /*
1364 : * Read an object from disk using the GUID as the key
1365 : *
1366 : * If the object can not be read, or it does not have a SID
1367 : * it is ignored.
1368 : *
1369 : * As a consequence of this, if all the remaining GUID's
1370 : * have been deleted an empty result will be returned.
1371 : * i.e. even if the previous call returned a non zero
1372 : * resume_handle it is possible for no results to be returned.
1373 : *
1374 : */
1375 3157 : ret = dsdb_search_by_dn_guid(d_state->sam_ctx,
1376 : mem_ctx,
1377 : &rec,
1378 3157 : &cache->entries[idx],
1379 : attrs,
1380 : 0);
1381 3157 : if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1382 : struct GUID_txt_buf guid_buf;
1383 1 : DBG_WARNING(
1384 : "GUID [%s] not found\n",
1385 : GUID_buf_string(&cache->entries[idx], &guid_buf));
1386 1 : continue;
1387 3156 : } else if (ret != LDB_SUCCESS) {
1388 0 : clear_guid_cache(cache);
1389 0 : return NT_STATUS_INTERNAL_DB_CORRUPTION;
1390 : }
1391 :
1392 3156 : objectsid = samdb_result_dom_sid(mem_ctx,
1393 3156 : rec->msgs[0],
1394 : "objectSID");
1395 3156 : if (objectsid == NULL) {
1396 : struct GUID_txt_buf guid_buf;
1397 0 : DBG_WARNING(
1398 : "objectSID for GUID [%s] not found\n",
1399 : GUID_buf_string(&cache->entries[idx], &guid_buf));
1400 0 : continue;
1401 : }
1402 3156 : status = dom_sid_split_rid(NULL,
1403 : objectsid,
1404 : NULL,
1405 : &rid);
1406 3156 : if (!NT_STATUS_IS_OK(status)) {
1407 : struct dom_sid_buf sid_buf;
1408 : struct GUID_txt_buf guid_buf;
1409 0 : DBG_WARNING(
1410 : "objectSID [%s] for GUID [%s] invalid\n",
1411 : dom_sid_str_buf(objectsid, &sid_buf),
1412 : GUID_buf_string(&cache->entries[idx], &guid_buf));
1413 0 : continue;
1414 : }
1415 :
1416 3156 : entries[count].idx = rid;
1417 3156 : name = ldb_msg_find_attr_as_string(
1418 3156 : rec->msgs[0], "sAMAccountName", "");
1419 3156 : entries[count].name.string = talloc_strdup(entries, name);
1420 3156 : count++;
1421 : }
1422 :
1423 167 : sam = talloc(mem_ctx, struct samr_SamArray);
1424 167 : if (!sam) {
1425 0 : clear_guid_cache(cache);
1426 0 : return NT_STATUS_NO_MEMORY;
1427 : }
1428 :
1429 167 : sam->entries = entries;
1430 167 : sam->count = count;
1431 :
1432 167 : *r->out.sam = sam;
1433 167 : *r->out.resume_handle = resume_handle;
1434 167 : *r->out.num_entries = count;
1435 :
1436 : /*
1437 : * Signal no more results by returning zero resume handle,
1438 : * the cache is also cleared at this point
1439 : */
1440 167 : if (*r->out.resume_handle >= cache->size) {
1441 47 : *r->out.resume_handle = 0;
1442 47 : clear_guid_cache(cache);
1443 47 : return NT_STATUS_OK;
1444 : }
1445 : /*
1446 : * There are more results to be returned.
1447 : */
1448 120 : return STATUS_MORE_ENTRIES;
1449 : }
1450 :
1451 :
1452 : /*
1453 : samr_CreateUser2
1454 :
1455 : This call uses transactions to ensure we don't get a new conflicting
1456 : user while we are processing this, and to ensure the user either
1457 : completly exists, or does not.
1458 : */
1459 2350 : static NTSTATUS dcesrv_samr_CreateUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1460 : struct samr_CreateUser2 *r)
1461 : {
1462 : NTSTATUS status;
1463 : struct samr_domain_state *d_state;
1464 : struct samr_account_state *a_state;
1465 : struct dcesrv_handle *h;
1466 : struct ldb_dn *dn;
1467 : struct dom_sid *sid;
1468 : struct dcesrv_handle *u_handle;
1469 : const char *account_name;
1470 :
1471 2350 : ZERO_STRUCTP(r->out.user_handle);
1472 2350 : *r->out.access_granted = 0;
1473 2350 : *r->out.rid = 0;
1474 :
1475 2350 : DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1476 :
1477 2350 : d_state = h->data;
1478 :
1479 2350 : if (d_state->builtin) {
1480 885 : DEBUG(5, ("Cannot create a user in the BUILTIN domain"));
1481 885 : return NT_STATUS_ACCESS_DENIED;
1482 1465 : } else if (r->in.acct_flags == ACB_DOMTRUST) {
1483 : /* Domain trust accounts must be created by the LSA calls */
1484 8 : return NT_STATUS_ACCESS_DENIED;
1485 : }
1486 1457 : account_name = r->in.account_name->string;
1487 :
1488 1457 : if (account_name == NULL) {
1489 0 : return NT_STATUS_INVALID_PARAMETER;
1490 : }
1491 :
1492 1457 : status = dsdb_add_user(d_state->sam_ctx, mem_ctx, account_name, r->in.acct_flags, NULL,
1493 : &sid, &dn);
1494 1457 : if (!NT_STATUS_IS_OK(status)) {
1495 95 : return status;
1496 : }
1497 1362 : a_state = talloc(mem_ctx, struct samr_account_state);
1498 1362 : if (!a_state) {
1499 0 : return NT_STATUS_NO_MEMORY;
1500 : }
1501 1362 : a_state->sam_ctx = d_state->sam_ctx;
1502 1362 : a_state->access_mask = r->in.access_mask;
1503 1362 : a_state->domain_state = talloc_reference(a_state, d_state);
1504 1362 : a_state->account_dn = talloc_steal(a_state, dn);
1505 :
1506 1362 : a_state->account_name = talloc_steal(a_state, account_name);
1507 1362 : if (!a_state->account_name) {
1508 0 : return NT_STATUS_NO_MEMORY;
1509 : }
1510 :
1511 : /* create the policy handle */
1512 1362 : u_handle = dcesrv_handle_create(dce_call, SAMR_HANDLE_USER);
1513 1362 : if (!u_handle) {
1514 0 : return NT_STATUS_NO_MEMORY;
1515 : }
1516 :
1517 1362 : u_handle->data = talloc_steal(u_handle, a_state);
1518 :
1519 1362 : *r->out.user_handle = u_handle->wire_handle;
1520 1362 : *r->out.access_granted = 0xf07ff; /* TODO: fix access mask calculations */
1521 :
1522 1362 : *r->out.rid = sid->sub_auths[sid->num_auths-1];
1523 :
1524 1362 : return NT_STATUS_OK;
1525 : }
1526 :
1527 :
1528 : /*
1529 : samr_CreateUser
1530 : */
1531 1561 : static NTSTATUS dcesrv_samr_CreateUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1532 : struct samr_CreateUser *r)
1533 : {
1534 : struct samr_CreateUser2 r2;
1535 1561 : uint32_t access_granted = 0;
1536 :
1537 :
1538 : /* a simple wrapper around samr_CreateUser2 works nicely */
1539 :
1540 1561 : r2 = (struct samr_CreateUser2) {
1541 1561 : .in.domain_handle = r->in.domain_handle,
1542 1561 : .in.account_name = r->in.account_name,
1543 : .in.acct_flags = ACB_NORMAL,
1544 1561 : .in.access_mask = r->in.access_mask,
1545 1561 : .out.user_handle = r->out.user_handle,
1546 : .out.access_granted = &access_granted,
1547 1561 : .out.rid = r->out.rid
1548 : };
1549 :
1550 1561 : return dcesrv_samr_CreateUser2(dce_call, mem_ctx, &r2);
1551 : }
1552 :
1553 : struct enum_dom_users_ctx {
1554 : struct samr_SamEntry *entries;
1555 : uint32_t num_entries;
1556 : uint32_t acct_flags;
1557 : struct dom_sid *domain_sid;
1558 : };
1559 :
1560 : static int user_iterate_callback(struct ldb_request *req,
1561 : struct ldb_reply *ares);
1562 :
1563 : /*
1564 : * Iterate users and add all those that match a domain SID and pass an acct
1565 : * flags check to an array of SamEntry objects.
1566 : */
1567 5248 : static int user_iterate_callback(struct ldb_request *req,
1568 : struct ldb_reply *ares)
1569 : {
1570 3125 : struct enum_dom_users_ctx *ac =\
1571 5248 : talloc_get_type(req->context, struct enum_dom_users_ctx);
1572 5248 : int ret = LDB_ERR_OPERATIONS_ERROR;
1573 :
1574 5248 : if (!ares) {
1575 0 : return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
1576 : }
1577 5248 : if (ares->error != LDB_SUCCESS) {
1578 0 : return ldb_request_done(req, ares->error);
1579 : }
1580 :
1581 5248 : switch (ares->type) {
1582 4994 : case LDB_REPLY_ENTRY:
1583 : {
1584 4994 : struct ldb_message *msg = ares->message;
1585 : const struct ldb_val *val;
1586 : struct samr_SamEntry *ent;
1587 : struct dom_sid objectsid;
1588 : uint32_t rid;
1589 4994 : size_t entries_array_len = 0;
1590 : NTSTATUS status;
1591 : ssize_t sid_size;
1592 :
1593 7172 : if (ac->acct_flags && ((samdb_result_acct_flags(msg, NULL) &
1594 4068 : ac->acct_flags) == 0)) {
1595 138 : ret = LDB_SUCCESS;
1596 138 : break;
1597 : }
1598 :
1599 4856 : val = ldb_msg_find_ldb_val(msg, "objectSID");
1600 4856 : if (val == NULL) {
1601 0 : DBG_WARNING("objectSID for DN %s not found\n",
1602 : ldb_dn_get_linearized(msg->dn));
1603 0 : ret = ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
1604 0 : break;
1605 : }
1606 :
1607 4856 : sid_size = sid_parse(val->data, val->length, &objectsid);
1608 4856 : if (sid_size == -1) {
1609 : struct dom_sid_buf sid_buf;
1610 0 : DBG_WARNING("objectsid [%s] for DN [%s] invalid\n",
1611 : dom_sid_str_buf(&objectsid, &sid_buf),
1612 : ldb_dn_get_linearized(msg->dn));
1613 0 : ret = ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
1614 0 : break;
1615 : }
1616 :
1617 4856 : if (!dom_sid_in_domain(ac->domain_sid, &objectsid)) {
1618 : /* Ignore if user isn't in the domain */
1619 0 : ret = LDB_SUCCESS;
1620 0 : break;
1621 : }
1622 :
1623 4856 : status = dom_sid_split_rid(ares, &objectsid, NULL, &rid);
1624 4856 : if (!NT_STATUS_IS_OK(status)) {
1625 : struct dom_sid_buf sid_buf;
1626 0 : DBG_WARNING("Couldn't split RID from "
1627 : "SID [%s] of DN [%s]\n",
1628 : dom_sid_str_buf(&objectsid, &sid_buf),
1629 : ldb_dn_get_linearized(msg->dn));
1630 0 : ret = ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
1631 0 : break;
1632 : }
1633 :
1634 4856 : entries_array_len = talloc_array_length(ac->entries);
1635 4856 : if (ac->num_entries >= entries_array_len) {
1636 27 : if (entries_array_len * 2 < entries_array_len) {
1637 0 : ret = ldb_request_done(req,
1638 : LDB_ERR_OPERATIONS_ERROR);
1639 0 : break;
1640 : }
1641 27 : ac->entries = talloc_realloc(ac,
1642 : ac->entries,
1643 : struct samr_SamEntry,
1644 : entries_array_len * 2);
1645 27 : if (ac->entries == NULL) {
1646 0 : ret = ldb_request_done(req,
1647 : LDB_ERR_OPERATIONS_ERROR);
1648 0 : break;
1649 : }
1650 : }
1651 :
1652 4856 : ent = &(ac->entries[ac->num_entries++]);
1653 4856 : val = ldb_msg_find_ldb_val(msg, "samaccountname");
1654 4856 : if (val == NULL) {
1655 0 : DBG_WARNING("samaccountname attribute not found\n");
1656 0 : ret = ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
1657 0 : break;
1658 : }
1659 4856 : ent->name.string = talloc_steal(ac->entries,
1660 : (char *)val->data);
1661 4856 : ent->idx = rid;
1662 4856 : ret = LDB_SUCCESS;
1663 4856 : break;
1664 : }
1665 75 : case LDB_REPLY_DONE:
1666 : {
1667 118 : if (ac->num_entries != 0 &&
1668 60 : ac->num_entries != talloc_array_length(ac->entries)) {
1669 60 : ac->entries = talloc_realloc(ac,
1670 : ac->entries,
1671 : struct samr_SamEntry,
1672 : ac->num_entries);
1673 60 : if (ac->entries == NULL) {
1674 0 : ret = ldb_request_done(req,
1675 : LDB_ERR_OPERATIONS_ERROR);
1676 0 : break;
1677 : }
1678 : }
1679 75 : ret = ldb_request_done(req, LDB_SUCCESS);
1680 75 : break;
1681 : }
1682 179 : case LDB_REPLY_REFERRAL:
1683 : {
1684 179 : ret = LDB_SUCCESS;
1685 179 : break;
1686 : }
1687 0 : default:
1688 : /* Doesn't happen */
1689 0 : ret = LDB_ERR_OPERATIONS_ERROR;
1690 : }
1691 5248 : TALLOC_FREE(ares);
1692 :
1693 5248 : return ret;
1694 : }
1695 :
1696 : /*
1697 : * samr_EnumDomainUsers
1698 : * The previous implementation did an initial search and stored a list of
1699 : * matching GUIDs on the connection handle's domain state, then did direct
1700 : * GUID lookups for each record in a page indexed by resume_handle. That
1701 : * approach was memory efficient, requiring only 16 bytes per record, but
1702 : * was too slow for winbind which needs this RPC call for getpwent.
1703 : *
1704 : * Now we use an iterate pattern to populate a cached list of the rids and
1705 : * names for each record. This improves runtime performance but requires
1706 : * about 200 bytes per record which will mean for a 100k database we use
1707 : * about 2MB, which is fine. The speedup achieved by this new approach is
1708 : * around 50%.
1709 : */
1710 123 : static NTSTATUS dcesrv_samr_EnumDomainUsers(struct dcesrv_call_state *dce_call,
1711 : TALLOC_CTX *mem_ctx,
1712 : struct samr_EnumDomainUsers *r)
1713 : {
1714 : struct dcesrv_handle *h;
1715 : struct samr_domain_state *d_state;
1716 : uint32_t results;
1717 : uint32_t max_entries;
1718 : uint32_t num_entries;
1719 : uint32_t remaining_entries;
1720 : struct samr_SamEntry *entries;
1721 123 : const char * const attrs[] = { "objectSid", "sAMAccountName",
1722 : "userAccountControl", NULL };
1723 : struct samr_SamArray *sam;
1724 : struct ldb_request *req;
1725 :
1726 123 : *r->out.resume_handle = 0;
1727 123 : *r->out.sam = NULL;
1728 123 : *r->out.num_entries = 0;
1729 :
1730 123 : DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1731 :
1732 123 : d_state = h->data;
1733 123 : entries = d_state->domain_users_cached;
1734 :
1735 : /*
1736 : * If the resume_handle is zero, query the database and cache the
1737 : * matching entries.
1738 : */
1739 123 : if (*r->in.resume_handle == 0) {
1740 : int ret;
1741 : struct enum_dom_users_ctx *ac;
1742 75 : if (entries != NULL) {
1743 39 : talloc_free(entries);
1744 39 : d_state->domain_users_cached = NULL;
1745 : }
1746 :
1747 75 : ac = talloc(mem_ctx, struct enum_dom_users_ctx);
1748 75 : ac->num_entries = 0;
1749 75 : ac->domain_sid = d_state->domain_sid;
1750 75 : ac->entries = talloc_array(ac,
1751 : struct samr_SamEntry,
1752 : 100);
1753 75 : if (ac->entries == NULL) {
1754 0 : talloc_free(ac);
1755 0 : return NT_STATUS_NO_MEMORY;
1756 : }
1757 75 : ac->acct_flags = r->in.acct_flags;
1758 :
1759 128 : ret = ldb_build_search_req(&req,
1760 75 : d_state->sam_ctx,
1761 : mem_ctx,
1762 : d_state->domain_dn,
1763 : LDB_SCOPE_SUBTREE,
1764 : "(objectClass=user)",
1765 : attrs,
1766 : NULL,
1767 : ac,
1768 : user_iterate_callback,
1769 : NULL);
1770 75 : if (ret != LDB_SUCCESS) {
1771 0 : talloc_free(ac);
1772 0 : return dsdb_ldb_err_to_ntstatus(ret);
1773 : }
1774 :
1775 75 : ret = ldb_request(d_state->sam_ctx, req);
1776 75 : if (ret != LDB_SUCCESS) {
1777 0 : talloc_free(ac);
1778 0 : return dsdb_ldb_err_to_ntstatus(ret);
1779 : }
1780 :
1781 75 : ret = ldb_wait(req->handle, LDB_WAIT_ALL);
1782 75 : if (ret != LDB_SUCCESS) {
1783 0 : return dsdb_ldb_err_to_ntstatus(ret);
1784 : }
1785 :
1786 75 : if (ac->num_entries == 0) {
1787 15 : DBG_WARNING("No users in domain %s",
1788 : ldb_dn_get_linearized(d_state->domain_dn));
1789 15 : talloc_free(ac);
1790 15 : return NT_STATUS_OK;
1791 : }
1792 :
1793 60 : entries = talloc_steal(d_state, ac->entries);
1794 60 : d_state->domain_users_cached = entries;
1795 60 : num_entries = ac->num_entries;
1796 60 : talloc_free(ac);
1797 :
1798 : /*
1799 : * Sort the entries into RID order, while the spec states there
1800 : * is no order, Windows appears to sort the results by RID and
1801 : * so it is possible that there are clients that depend on
1802 : * this ordering
1803 : */
1804 60 : TYPESAFE_QSORT(entries, num_entries, compare_SamEntry);
1805 : } else {
1806 48 : num_entries = talloc_array_length(entries);
1807 : }
1808 :
1809 : /*
1810 : * If the resume handle is out of range we return an empty response
1811 : * and invalidate the cache.
1812 : *
1813 : * From the specification:
1814 : * Servers SHOULD validate that EnumerationContext is an expected
1815 : * value for the server's implementation. Windows does NOT validate
1816 : * the input, though the result of malformed information merely results
1817 : * in inconsistent output to the client.
1818 : */
1819 108 : if (*r->in.resume_handle >= num_entries) {
1820 1 : talloc_free(entries);
1821 1 : d_state->domain_users_cached = NULL;
1822 1 : sam = talloc(mem_ctx, struct samr_SamArray);
1823 1 : if (!sam) {
1824 0 : return NT_STATUS_NO_MEMORY;
1825 : }
1826 1 : sam->entries = NULL;
1827 1 : sam->count = 0;
1828 :
1829 1 : *r->out.sam = sam;
1830 1 : *r->out.resume_handle = 0;
1831 1 : return NT_STATUS_OK;
1832 : }
1833 :
1834 : /*
1835 : * Calculate the number of entries to return limit by max_size.
1836 : * Note that we use the w2k3 element size value of 54
1837 : */
1838 107 : max_entries = 1 + (r->in.max_size / SAMR_ENUM_USERS_MULTIPLIER);
1839 107 : remaining_entries = num_entries - *r->in.resume_handle;
1840 107 : results = MIN(remaining_entries, max_entries);
1841 :
1842 107 : sam = talloc(mem_ctx, struct samr_SamArray);
1843 107 : if (!sam) {
1844 0 : d_state->domain_users_cached = NULL;
1845 0 : return NT_STATUS_NO_MEMORY;
1846 : }
1847 :
1848 107 : sam->entries = entries + *r->in.resume_handle;
1849 107 : sam->count = results;
1850 :
1851 107 : *r->out.sam = sam;
1852 107 : *r->out.resume_handle = *r->in.resume_handle + results;
1853 107 : *r->out.num_entries = results;
1854 :
1855 : /*
1856 : * Signal no more results by returning zero resume handle,
1857 : * the cache is also cleared at this point
1858 : */
1859 107 : if (*r->out.resume_handle >= num_entries) {
1860 59 : *r->out.resume_handle = 0;
1861 59 : return NT_STATUS_OK;
1862 : }
1863 : /*
1864 : * There are more results to be returned.
1865 : */
1866 48 : return STATUS_MORE_ENTRIES;
1867 : }
1868 :
1869 :
1870 : /*
1871 : samr_CreateDomAlias
1872 : */
1873 1510 : static NTSTATUS dcesrv_samr_CreateDomAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1874 : struct samr_CreateDomAlias *r)
1875 : {
1876 : struct samr_domain_state *d_state;
1877 : struct samr_account_state *a_state;
1878 : struct dcesrv_handle *h;
1879 : const char *alias_name;
1880 : struct dom_sid *sid;
1881 : struct dcesrv_handle *a_handle;
1882 : struct ldb_dn *dn;
1883 : NTSTATUS status;
1884 :
1885 1510 : ZERO_STRUCTP(r->out.alias_handle);
1886 1510 : *r->out.rid = 0;
1887 :
1888 1510 : DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1889 :
1890 1510 : d_state = h->data;
1891 :
1892 1510 : if (d_state->builtin) {
1893 755 : DEBUG(5, ("Cannot create a domain alias in the BUILTIN domain"));
1894 755 : return NT_STATUS_ACCESS_DENIED;
1895 : }
1896 :
1897 755 : alias_name = r->in.alias_name->string;
1898 :
1899 755 : if (alias_name == NULL) {
1900 0 : return NT_STATUS_INVALID_PARAMETER;
1901 : }
1902 :
1903 755 : status = dsdb_add_domain_alias(d_state->sam_ctx, mem_ctx, alias_name, &sid, &dn);
1904 755 : if (!NT_STATUS_IS_OK(status)) {
1905 0 : return status;
1906 : }
1907 :
1908 755 : a_state = talloc(mem_ctx, struct samr_account_state);
1909 755 : if (!a_state) {
1910 0 : return NT_STATUS_NO_MEMORY;
1911 : }
1912 :
1913 755 : a_state->sam_ctx = d_state->sam_ctx;
1914 755 : a_state->access_mask = r->in.access_mask;
1915 755 : a_state->domain_state = talloc_reference(a_state, d_state);
1916 755 : a_state->account_dn = talloc_steal(a_state, dn);
1917 :
1918 755 : a_state->account_name = talloc_steal(a_state, alias_name);
1919 :
1920 : /* create the policy handle */
1921 755 : a_handle = dcesrv_handle_create(dce_call, SAMR_HANDLE_ALIAS);
1922 755 : if (a_handle == NULL)
1923 0 : return NT_STATUS_NO_MEMORY;
1924 :
1925 755 : a_handle->data = talloc_steal(a_handle, a_state);
1926 :
1927 755 : *r->out.alias_handle = a_handle->wire_handle;
1928 :
1929 755 : *r->out.rid = sid->sub_auths[sid->num_auths-1];
1930 :
1931 755 : return NT_STATUS_OK;
1932 : }
1933 :
1934 :
1935 : /*
1936 : samr_EnumDomainAliases
1937 : */
1938 54 : static NTSTATUS dcesrv_samr_EnumDomainAliases(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1939 : struct samr_EnumDomainAliases *r)
1940 : {
1941 : struct dcesrv_handle *h;
1942 : struct samr_domain_state *d_state;
1943 : struct ldb_message **res;
1944 : int i, ldb_cnt;
1945 : uint32_t first, count;
1946 : struct samr_SamEntry *entries;
1947 54 : const char * const attrs[] = { "objectSid", "sAMAccountName", NULL };
1948 : struct samr_SamArray *sam;
1949 :
1950 54 : *r->out.resume_handle = 0;
1951 54 : *r->out.sam = NULL;
1952 54 : *r->out.num_entries = 0;
1953 :
1954 54 : DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1955 :
1956 54 : d_state = h->data;
1957 :
1958 : /* search for all domain aliases in this domain. This could possibly be
1959 : cached and resumed based on resume_key */
1960 54 : ldb_cnt = samdb_search_domain(d_state->sam_ctx, mem_ctx, NULL,
1961 : &res, attrs,
1962 54 : d_state->domain_sid,
1963 : "(&(|(grouptype=%d)(grouptype=%d)))"
1964 : "(objectclass=group))",
1965 : GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
1966 : GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
1967 54 : if (ldb_cnt < 0) {
1968 0 : return NT_STATUS_INTERNAL_DB_CORRUPTION;
1969 : }
1970 :
1971 : /* convert to SamEntry format */
1972 54 : entries = talloc_array(mem_ctx, struct samr_SamEntry, ldb_cnt);
1973 54 : if (!entries) {
1974 0 : return NT_STATUS_NO_MEMORY;
1975 : }
1976 :
1977 54 : count = 0;
1978 :
1979 3135 : for (i=0;i<ldb_cnt;i++) {
1980 : struct dom_sid *alias_sid;
1981 :
1982 3081 : alias_sid = samdb_result_dom_sid(mem_ctx, res[i],
1983 : "objectSid");
1984 :
1985 3081 : if (alias_sid == NULL) {
1986 0 : return NT_STATUS_INTERNAL_DB_CORRUPTION;
1987 : }
1988 :
1989 4934 : entries[count].idx =
1990 4934 : alias_sid->sub_auths[alias_sid->num_auths-1];
1991 6162 : entries[count].name.string =
1992 4934 : ldb_msg_find_attr_as_string(res[i], "sAMAccountName", "");
1993 3081 : count += 1;
1994 : }
1995 :
1996 : /* sort the results by rid */
1997 54 : TYPESAFE_QSORT(entries, count, compare_SamEntry);
1998 :
1999 : /* find the first entry to return */
2000 102 : for (first=0;
2001 64 : first<count && entries[first].idx <= *r->in.resume_handle;
2002 10 : first++) ;
2003 :
2004 : /* return the rest, limit by max_size. Note that we
2005 : use the w2k3 element size value of 54 */
2006 54 : *r->out.num_entries = count - first;
2007 54 : *r->out.num_entries = MIN(*r->out.num_entries,
2008 : 1+(r->in.max_size/SAMR_ENUM_USERS_MULTIPLIER));
2009 :
2010 54 : sam = talloc(mem_ctx, struct samr_SamArray);
2011 54 : if (!sam) {
2012 0 : return NT_STATUS_NO_MEMORY;
2013 : }
2014 :
2015 54 : sam->entries = entries+first;
2016 54 : sam->count = *r->out.num_entries;
2017 :
2018 54 : *r->out.sam = sam;
2019 :
2020 54 : if (first == count) {
2021 0 : return NT_STATUS_OK;
2022 : }
2023 :
2024 54 : if (*r->out.num_entries < count - first) {
2025 10 : *r->out.resume_handle =
2026 10 : entries[first+*r->out.num_entries-1].idx;
2027 5 : return STATUS_MORE_ENTRIES;
2028 : }
2029 :
2030 49 : return NT_STATUS_OK;
2031 : }
2032 :
2033 :
2034 : /*
2035 : samr_GetAliasMembership
2036 : */
2037 1171 : static NTSTATUS dcesrv_samr_GetAliasMembership(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2038 : struct samr_GetAliasMembership *r)
2039 : {
2040 : struct dcesrv_handle *h;
2041 : struct samr_domain_state *d_state;
2042 : char *filter;
2043 1171 : const char * const attrs[] = { "objectSid", NULL };
2044 : struct ldb_message **res;
2045 : uint32_t i;
2046 1171 : int count = 0;
2047 :
2048 1171 : DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
2049 :
2050 1171 : d_state = h->data;
2051 :
2052 1171 : filter = talloc_asprintf(mem_ctx,
2053 : "(&(|(grouptype=%d)(grouptype=%d))"
2054 : "(objectclass=group)(|",
2055 : GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
2056 : GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
2057 1171 : if (filter == NULL) {
2058 0 : return NT_STATUS_NO_MEMORY;
2059 : }
2060 :
2061 3135 : for (i=0; i<r->in.sids->num_sids; i++) {
2062 : struct dom_sid_buf buf;
2063 :
2064 1964 : filter = talloc_asprintf_append(
2065 : filter,
2066 : "(member=<SID=%s>)",
2067 1964 : dom_sid_str_buf(r->in.sids->sids[i].sid, &buf));
2068 :
2069 1964 : if (filter == NULL) {
2070 0 : return NT_STATUS_NO_MEMORY;
2071 : }
2072 : }
2073 :
2074 : /* Find out if we had at least one valid member SID passed - otherwise
2075 : * just skip the search. */
2076 1171 : if (strstr(filter, "member") != NULL) {
2077 835 : count = samdb_search_domain(d_state->sam_ctx, mem_ctx, NULL,
2078 835 : &res, attrs, d_state->domain_sid,
2079 : "%s))", filter);
2080 835 : if (count < 0) {
2081 0 : return NT_STATUS_INTERNAL_DB_CORRUPTION;
2082 : }
2083 : }
2084 :
2085 1171 : r->out.rids->count = 0;
2086 1171 : r->out.rids->ids = talloc_array(mem_ctx, uint32_t, count);
2087 1171 : if (r->out.rids->ids == NULL)
2088 0 : return NT_STATUS_NO_MEMORY;
2089 :
2090 1645 : for (i=0; i<count; i++) {
2091 : struct dom_sid *alias_sid;
2092 :
2093 474 : alias_sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
2094 474 : if (alias_sid == NULL) {
2095 0 : return NT_STATUS_INTERNAL_DB_CORRUPTION;
2096 : }
2097 :
2098 768 : r->out.rids->ids[r->out.rids->count] =
2099 768 : alias_sid->sub_auths[alias_sid->num_auths-1];
2100 474 : r->out.rids->count += 1;
2101 : }
2102 :
2103 1171 : return NT_STATUS_OK;
2104 : }
2105 :
2106 :
2107 : /*
2108 : samr_LookupNames
2109 : */
2110 5839 : static NTSTATUS dcesrv_samr_LookupNames(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2111 : struct samr_LookupNames *r)
2112 : {
2113 : struct dcesrv_handle *h;
2114 : struct samr_domain_state *d_state;
2115 : uint32_t i, num_mapped;
2116 5839 : NTSTATUS status = NT_STATUS_OK;
2117 5839 : const char * const attrs[] = { "sAMAccountType", "objectSid", NULL };
2118 : int count;
2119 :
2120 5839 : ZERO_STRUCTP(r->out.rids);
2121 5839 : ZERO_STRUCTP(r->out.types);
2122 :
2123 5839 : DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
2124 :
2125 5839 : d_state = h->data;
2126 :
2127 5839 : if (r->in.num_names == 0) {
2128 310 : return NT_STATUS_OK;
2129 : }
2130 :
2131 5529 : r->out.rids->ids = talloc_array(mem_ctx, uint32_t, r->in.num_names);
2132 5529 : r->out.types->ids = talloc_array(mem_ctx, uint32_t, r->in.num_names);
2133 5529 : if (!r->out.rids->ids || !r->out.types->ids) {
2134 0 : return NT_STATUS_NO_MEMORY;
2135 : }
2136 5529 : r->out.rids->count = r->in.num_names;
2137 5529 : r->out.types->count = r->in.num_names;
2138 :
2139 5529 : num_mapped = 0;
2140 :
2141 11678 : for (i=0;i<r->in.num_names;i++) {
2142 : struct ldb_message **res;
2143 : struct dom_sid *sid;
2144 : uint32_t atype, rtype;
2145 :
2146 6149 : r->out.rids->ids[i] = 0;
2147 6149 : r->out.types->ids[i] = SID_NAME_UNKNOWN;
2148 :
2149 6149 : count = gendb_search(d_state->sam_ctx, mem_ctx, d_state->domain_dn, &res, attrs,
2150 : "sAMAccountName=%s",
2151 6149 : ldb_binary_encode_string(mem_ctx, r->in.names[i].string));
2152 6149 : if (count != 1) {
2153 4119 : status = STATUS_SOME_UNMAPPED;
2154 7240 : continue;
2155 : }
2156 :
2157 2030 : sid = samdb_result_dom_sid(mem_ctx, res[0], "objectSid");
2158 2030 : if (sid == NULL) {
2159 0 : status = STATUS_SOME_UNMAPPED;
2160 0 : continue;
2161 : }
2162 :
2163 2030 : atype = ldb_msg_find_attr_as_uint(res[0], "sAMAccountType", 0);
2164 2030 : if (atype == 0) {
2165 0 : status = STATUS_SOME_UNMAPPED;
2166 0 : continue;
2167 : }
2168 :
2169 2030 : rtype = ds_atype_map(atype);
2170 :
2171 2030 : if (rtype == SID_NAME_UNKNOWN) {
2172 0 : status = STATUS_SOME_UNMAPPED;
2173 0 : continue;
2174 : }
2175 :
2176 2030 : r->out.rids->ids[i] = sid->sub_auths[sid->num_auths-1];
2177 2030 : r->out.types->ids[i] = rtype;
2178 2030 : num_mapped++;
2179 : }
2180 :
2181 5529 : if (num_mapped == 0) {
2182 3499 : return NT_STATUS_NONE_MAPPED;
2183 : }
2184 2030 : return status;
2185 : }
2186 :
2187 :
2188 : /*
2189 : samr_LookupRids
2190 : */
2191 1389 : static NTSTATUS dcesrv_samr_LookupRids(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2192 : struct samr_LookupRids *r)
2193 : {
2194 : NTSTATUS status;
2195 : struct dcesrv_handle *h;
2196 : struct samr_domain_state *d_state;
2197 : const char **names;
2198 : struct lsa_String *lsa_names;
2199 : enum lsa_SidType *ids;
2200 :
2201 1389 : ZERO_STRUCTP(r->out.names);
2202 1389 : ZERO_STRUCTP(r->out.types);
2203 :
2204 1389 : DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
2205 :
2206 1389 : d_state = h->data;
2207 :
2208 1389 : if (r->in.num_rids == 0)
2209 4 : return NT_STATUS_OK;
2210 :
2211 1385 : lsa_names = talloc_zero_array(mem_ctx, struct lsa_String, r->in.num_rids);
2212 1385 : names = talloc_zero_array(mem_ctx, const char *, r->in.num_rids);
2213 1385 : ids = talloc_zero_array(mem_ctx, enum lsa_SidType, r->in.num_rids);
2214 :
2215 1385 : if ((lsa_names == NULL) || (names == NULL) || (ids == NULL))
2216 0 : return NT_STATUS_NO_MEMORY;
2217 :
2218 1385 : r->out.names->names = lsa_names;
2219 1385 : r->out.names->count = r->in.num_rids;
2220 :
2221 1385 : r->out.types->ids = (uint32_t *) ids;
2222 1385 : r->out.types->count = r->in.num_rids;
2223 :
2224 1385 : status = dsdb_lookup_rids(d_state->sam_ctx, mem_ctx, d_state->domain_sid,
2225 : r->in.num_rids, r->in.rids, names, ids);
2226 1385 : if (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED) || NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED)) {
2227 : uint32_t i;
2228 3529 : for (i = 0; i < r->in.num_rids; i++) {
2229 2144 : lsa_names[i].string = names[i];
2230 : }
2231 : }
2232 1385 : return status;
2233 : }
2234 :
2235 :
2236 : /*
2237 : samr_OpenGroup
2238 : */
2239 601 : static NTSTATUS dcesrv_samr_OpenGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2240 : struct samr_OpenGroup *r)
2241 : {
2242 : struct samr_domain_state *d_state;
2243 : struct samr_account_state *a_state;
2244 : struct dcesrv_handle *h;
2245 : const char *groupname;
2246 : struct dom_sid *sid;
2247 : struct ldb_message **msgs;
2248 : struct dcesrv_handle *g_handle;
2249 601 : const char * const attrs[2] = { "sAMAccountName", NULL };
2250 : int ret;
2251 :
2252 601 : ZERO_STRUCTP(r->out.group_handle);
2253 :
2254 601 : DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
2255 :
2256 596 : d_state = h->data;
2257 :
2258 : /* form the group SID */
2259 596 : sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2260 596 : if (!sid) {
2261 0 : return NT_STATUS_NO_MEMORY;
2262 : }
2263 :
2264 : /* search for the group record */
2265 596 : if (d_state->builtin) {
2266 0 : ret = gendb_search(d_state->sam_ctx,
2267 : mem_ctx, d_state->domain_dn, &msgs, attrs,
2268 : "(&(objectSid=%s)(objectClass=group)"
2269 : "(groupType=%d))",
2270 : ldap_encode_ndr_dom_sid(mem_ctx, sid),
2271 : GTYPE_SECURITY_BUILTIN_LOCAL_GROUP);
2272 : } else {
2273 596 : ret = gendb_search(d_state->sam_ctx,
2274 : mem_ctx, d_state->domain_dn, &msgs, attrs,
2275 : "(&(objectSid=%s)(objectClass=group)"
2276 : "(|(groupType=%d)(groupType=%d)))",
2277 : ldap_encode_ndr_dom_sid(mem_ctx, sid),
2278 : GTYPE_SECURITY_UNIVERSAL_GROUP,
2279 : GTYPE_SECURITY_GLOBAL_GROUP);
2280 : }
2281 596 : if (ret == 0) {
2282 0 : return NT_STATUS_NO_SUCH_GROUP;
2283 : }
2284 596 : if (ret != 1) {
2285 0 : DEBUG(0,("Found %d records matching sid %s\n",
2286 : ret, dom_sid_string(mem_ctx, sid)));
2287 0 : return NT_STATUS_INTERNAL_DB_CORRUPTION;
2288 : }
2289 :
2290 596 : groupname = ldb_msg_find_attr_as_string(msgs[0], "sAMAccountName", NULL);
2291 596 : if (groupname == NULL) {
2292 0 : DEBUG(0,("sAMAccountName field missing for sid %s\n",
2293 : dom_sid_string(mem_ctx, sid)));
2294 0 : return NT_STATUS_INTERNAL_DB_CORRUPTION;
2295 : }
2296 :
2297 596 : a_state = talloc(mem_ctx, struct samr_account_state);
2298 596 : if (!a_state) {
2299 0 : return NT_STATUS_NO_MEMORY;
2300 : }
2301 596 : a_state->sam_ctx = d_state->sam_ctx;
2302 596 : a_state->access_mask = r->in.access_mask;
2303 596 : a_state->domain_state = talloc_reference(a_state, d_state);
2304 596 : a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
2305 596 : a_state->account_sid = talloc_steal(a_state, sid);
2306 596 : a_state->account_name = talloc_strdup(a_state, groupname);
2307 596 : if (!a_state->account_name) {
2308 0 : return NT_STATUS_NO_MEMORY;
2309 : }
2310 :
2311 : /* create the policy handle */
2312 596 : g_handle = dcesrv_handle_create(dce_call, SAMR_HANDLE_GROUP);
2313 596 : if (!g_handle) {
2314 0 : return NT_STATUS_NO_MEMORY;
2315 : }
2316 :
2317 596 : g_handle->data = talloc_steal(g_handle, a_state);
2318 :
2319 596 : *r->out.group_handle = g_handle->wire_handle;
2320 :
2321 596 : return NT_STATUS_OK;
2322 : }
2323 :
2324 : /*
2325 : samr_QueryGroupInfo
2326 : */
2327 348 : static NTSTATUS dcesrv_samr_QueryGroupInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2328 : struct samr_QueryGroupInfo *r)
2329 : {
2330 : struct dcesrv_handle *h;
2331 : struct samr_account_state *a_state;
2332 : struct ldb_message *msg, **res;
2333 348 : const char * const attrs[4] = { "sAMAccountName", "description",
2334 : "numMembers", NULL };
2335 : int ret;
2336 : union samr_GroupInfo *info;
2337 :
2338 348 : *r->out.info = NULL;
2339 :
2340 348 : DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2341 :
2342 348 : a_state = h->data;
2343 :
2344 : /* pull all the group attributes */
2345 348 : ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
2346 : a_state->account_dn, &res, attrs);
2347 348 : if (ret == 0) {
2348 0 : return NT_STATUS_NO_SUCH_GROUP;
2349 : }
2350 348 : if (ret != 1) {
2351 0 : return NT_STATUS_INTERNAL_DB_CORRUPTION;
2352 : }
2353 348 : msg = res[0];
2354 :
2355 : /* allocate the info structure */
2356 348 : info = talloc_zero(mem_ctx, union samr_GroupInfo);
2357 348 : if (info == NULL) {
2358 0 : return NT_STATUS_NO_MEMORY;
2359 : }
2360 :
2361 : /* Fill in the level */
2362 348 : switch (r->in.level) {
2363 69 : case GROUPINFOALL:
2364 69 : QUERY_STRING(msg, all.name, "sAMAccountName");
2365 69 : info->all.attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED; /* Do like w2k3 */
2366 69 : QUERY_UINT (msg, all.num_members, "numMembers")
2367 69 : QUERY_STRING(msg, all.description, "description");
2368 69 : break;
2369 68 : case GROUPINFONAME:
2370 68 : QUERY_STRING(msg, name, "sAMAccountName");
2371 68 : break;
2372 70 : case GROUPINFOATTRIBUTES:
2373 70 : info->attributes.attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED; /* Do like w2k3 */
2374 70 : break;
2375 68 : case GROUPINFODESCRIPTION:
2376 68 : QUERY_STRING(msg, description, "description");
2377 68 : break;
2378 73 : case GROUPINFOALL2:
2379 73 : QUERY_STRING(msg, all2.name, "sAMAccountName");
2380 73 : info->all.attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED; /* Do like w2k3 */
2381 73 : QUERY_UINT (msg, all2.num_members, "numMembers")
2382 73 : QUERY_STRING(msg, all2.description, "description");
2383 73 : break;
2384 0 : default:
2385 0 : talloc_free(info);
2386 0 : return NT_STATUS_INVALID_INFO_CLASS;
2387 : }
2388 :
2389 348 : *r->out.info = info;
2390 :
2391 348 : return NT_STATUS_OK;
2392 : }
2393 :
2394 :
2395 : /*
2396 : samr_SetGroupInfo
2397 : */
2398 21 : static NTSTATUS dcesrv_samr_SetGroupInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2399 : struct samr_SetGroupInfo *r)
2400 : {
2401 : struct dcesrv_handle *h;
2402 : struct samr_account_state *g_state;
2403 : struct ldb_message *msg;
2404 : int ret;
2405 :
2406 21 : DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2407 :
2408 21 : g_state = h->data;
2409 :
2410 21 : msg = ldb_msg_new(mem_ctx);
2411 21 : if (msg == NULL) {
2412 0 : return NT_STATUS_NO_MEMORY;
2413 : }
2414 :
2415 21 : msg->dn = ldb_dn_copy(mem_ctx, g_state->account_dn);
2416 21 : if (!msg->dn) {
2417 0 : return NT_STATUS_NO_MEMORY;
2418 : }
2419 :
2420 21 : switch (r->in.level) {
2421 5 : case GROUPINFODESCRIPTION:
2422 5 : SET_STRING(msg, description, "description");
2423 5 : break;
2424 6 : case GROUPINFONAME:
2425 : /* On W2k3 this does not change the name, it changes the
2426 : * sAMAccountName attribute */
2427 6 : SET_STRING(msg, name, "sAMAccountName");
2428 6 : break;
2429 5 : case GROUPINFOATTRIBUTES:
2430 : /* This does not do anything obviously visible in W2k3 LDAP */
2431 5 : return NT_STATUS_OK;
2432 5 : default:
2433 5 : return NT_STATUS_INVALID_INFO_CLASS;
2434 : }
2435 :
2436 : /* modify the samdb record */
2437 11 : ret = ldb_modify(g_state->sam_ctx, msg);
2438 11 : if (ret != LDB_SUCCESS) {
2439 0 : return dsdb_ldb_err_to_ntstatus(ret);
2440 : }
2441 :
2442 11 : return NT_STATUS_OK;
2443 : }
2444 :
2445 :
2446 : /*
2447 : samr_AddGroupMember
2448 : */
2449 72 : static NTSTATUS dcesrv_samr_AddGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2450 : struct samr_AddGroupMember *r)
2451 : {
2452 : struct dcesrv_handle *h;
2453 : struct samr_account_state *a_state;
2454 : struct samr_domain_state *d_state;
2455 : struct ldb_message *mod;
2456 : struct dom_sid *membersid;
2457 : const char *memberdn;
2458 : struct ldb_result *res;
2459 72 : const char * const attrs[] = { NULL };
2460 : int ret;
2461 :
2462 72 : DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2463 :
2464 72 : a_state = h->data;
2465 72 : d_state = a_state->domain_state;
2466 :
2467 72 : membersid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2468 72 : if (membersid == NULL) {
2469 0 : return NT_STATUS_NO_MEMORY;
2470 : }
2471 :
2472 : /* according to MS-SAMR 3.1.5.8.2 all type of accounts are accepted */
2473 72 : ret = ldb_search(d_state->sam_ctx, mem_ctx, &res,
2474 : d_state->domain_dn, LDB_SCOPE_SUBTREE, attrs,
2475 : "(objectSid=%s)",
2476 : ldap_encode_ndr_dom_sid(mem_ctx, membersid));
2477 :
2478 72 : if (ret != LDB_SUCCESS) {
2479 0 : return NT_STATUS_INTERNAL_DB_CORRUPTION;
2480 : }
2481 :
2482 72 : if (res->count == 0) {
2483 0 : return NT_STATUS_NO_SUCH_USER;
2484 : }
2485 :
2486 72 : if (res->count > 1) {
2487 0 : return NT_STATUS_INTERNAL_DB_CORRUPTION;
2488 : }
2489 :
2490 72 : memberdn = ldb_dn_alloc_linearized(mem_ctx, res->msgs[0]->dn);
2491 :
2492 72 : if (memberdn == NULL)
2493 0 : return NT_STATUS_NO_MEMORY;
2494 :
2495 72 : mod = ldb_msg_new(mem_ctx);
2496 72 : if (mod == NULL) {
2497 0 : return NT_STATUS_NO_MEMORY;
2498 : }
2499 :
2500 72 : mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
2501 :
2502 72 : ret = samdb_msg_add_addval(d_state->sam_ctx, mem_ctx, mod, "member",
2503 : memberdn);
2504 72 : if (ret != LDB_SUCCESS) {
2505 0 : return dsdb_ldb_err_to_ntstatus(ret);
2506 : }
2507 :
2508 72 : ret = ldb_modify(a_state->sam_ctx, mod);
2509 72 : switch (ret) {
2510 72 : case LDB_SUCCESS:
2511 72 : return NT_STATUS_OK;
2512 0 : case LDB_ERR_ENTRY_ALREADY_EXISTS:
2513 0 : return NT_STATUS_MEMBER_IN_GROUP;
2514 0 : case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
2515 0 : return NT_STATUS_ACCESS_DENIED;
2516 0 : default:
2517 0 : return dsdb_ldb_err_to_ntstatus(ret);
2518 : }
2519 : }
2520 :
2521 :
2522 : /*
2523 : samr_DeleteDomainGroup
2524 : */
2525 530 : static NTSTATUS dcesrv_samr_DeleteDomainGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2526 : struct samr_DeleteDomainGroup *r)
2527 : {
2528 : struct dcesrv_handle *h;
2529 : struct samr_account_state *a_state;
2530 : int ret;
2531 :
2532 530 : *r->out.group_handle = *r->in.group_handle;
2533 :
2534 530 : DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2535 :
2536 530 : a_state = h->data;
2537 :
2538 530 : ret = ldb_delete(a_state->sam_ctx, a_state->account_dn);
2539 530 : if (ret != LDB_SUCCESS) {
2540 0 : return dsdb_ldb_err_to_ntstatus(ret);
2541 : }
2542 :
2543 530 : talloc_free(h);
2544 530 : ZERO_STRUCTP(r->out.group_handle);
2545 :
2546 530 : return NT_STATUS_OK;
2547 : }
2548 :
2549 :
2550 : /*
2551 : samr_DeleteGroupMember
2552 : */
2553 76 : static NTSTATUS dcesrv_samr_DeleteGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2554 : struct samr_DeleteGroupMember *r)
2555 : {
2556 : struct dcesrv_handle *h;
2557 : struct samr_account_state *a_state;
2558 : struct samr_domain_state *d_state;
2559 : struct ldb_message *mod;
2560 : struct dom_sid *membersid;
2561 : const char *memberdn;
2562 : struct ldb_result *res;
2563 76 : const char * const attrs[] = { NULL };
2564 : int ret;
2565 :
2566 76 : DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2567 :
2568 76 : a_state = h->data;
2569 76 : d_state = a_state->domain_state;
2570 :
2571 76 : membersid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2572 76 : if (membersid == NULL) {
2573 0 : return NT_STATUS_NO_MEMORY;
2574 : }
2575 :
2576 : /* according to MS-SAMR 3.1.5.8.2 all type of accounts are accepted */
2577 76 : ret = ldb_search(d_state->sam_ctx, mem_ctx, &res,
2578 : d_state->domain_dn, LDB_SCOPE_SUBTREE, attrs,
2579 : "(objectSid=%s)",
2580 : ldap_encode_ndr_dom_sid(mem_ctx, membersid));
2581 :
2582 76 : if (ret != LDB_SUCCESS) {
2583 0 : return NT_STATUS_INTERNAL_DB_CORRUPTION;
2584 : }
2585 :
2586 76 : if (res->count == 0) {
2587 0 : return NT_STATUS_NO_SUCH_USER;
2588 : }
2589 :
2590 76 : if (res->count > 1) {
2591 0 : return NT_STATUS_INTERNAL_DB_CORRUPTION;
2592 : }
2593 :
2594 76 : memberdn = ldb_dn_alloc_linearized(mem_ctx, res->msgs[0]->dn);
2595 :
2596 76 : if (memberdn == NULL)
2597 0 : return NT_STATUS_NO_MEMORY;
2598 :
2599 76 : mod = ldb_msg_new(mem_ctx);
2600 76 : if (mod == NULL) {
2601 0 : return NT_STATUS_NO_MEMORY;
2602 : }
2603 :
2604 76 : mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
2605 :
2606 76 : ret = samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod, "member",
2607 : memberdn);
2608 76 : if (ret != LDB_SUCCESS) {
2609 0 : return NT_STATUS_NO_MEMORY;
2610 : }
2611 :
2612 76 : ret = ldb_modify(a_state->sam_ctx, mod);
2613 76 : switch (ret) {
2614 71 : case LDB_SUCCESS:
2615 71 : return NT_STATUS_OK;
2616 0 : case LDB_ERR_UNWILLING_TO_PERFORM:
2617 0 : return NT_STATUS_MEMBER_NOT_IN_GROUP;
2618 0 : case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
2619 0 : return NT_STATUS_ACCESS_DENIED;
2620 5 : default:
2621 5 : return dsdb_ldb_err_to_ntstatus(ret);
2622 : }
2623 : }
2624 :
2625 :
2626 : /*
2627 : samr_QueryGroupMember
2628 : */
2629 502 : static NTSTATUS dcesrv_samr_QueryGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2630 : struct samr_QueryGroupMember *r)
2631 : {
2632 : struct dcesrv_handle *h;
2633 : struct samr_account_state *a_state;
2634 : struct samr_domain_state *d_state;
2635 : struct samr_RidAttrArray *array;
2636 : unsigned int i, num_members;
2637 : struct dom_sid *members;
2638 : NTSTATUS status;
2639 :
2640 502 : DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2641 :
2642 502 : a_state = h->data;
2643 502 : d_state = a_state->domain_state;
2644 :
2645 502 : status = dsdb_enum_group_mem(d_state->sam_ctx, mem_ctx,
2646 : a_state->account_dn, &members,
2647 : &num_members);
2648 502 : if (!NT_STATUS_IS_OK(status)) {
2649 0 : return status;
2650 : }
2651 :
2652 502 : array = talloc_zero(mem_ctx, struct samr_RidAttrArray);
2653 502 : if (array == NULL) {
2654 0 : return NT_STATUS_NO_MEMORY;
2655 : }
2656 :
2657 502 : if (num_members == 0) {
2658 379 : *r->out.rids = array;
2659 :
2660 379 : return NT_STATUS_OK;
2661 : }
2662 :
2663 123 : array->rids = talloc_array(array, uint32_t, num_members);
2664 123 : if (array->rids == NULL) {
2665 0 : return NT_STATUS_NO_MEMORY;
2666 : }
2667 :
2668 123 : array->attributes = talloc_array(array, uint32_t, num_members);
2669 123 : if (array->attributes == NULL) {
2670 0 : return NT_STATUS_NO_MEMORY;
2671 : }
2672 :
2673 123 : array->count = 0;
2674 246 : for (i=0; i<num_members; i++) {
2675 123 : if (!dom_sid_in_domain(d_state->domain_sid, &members[i])) {
2676 0 : continue;
2677 : }
2678 :
2679 123 : status = dom_sid_split_rid(NULL, &members[i], NULL,
2680 123 : &array->rids[array->count]);
2681 123 : if (!NT_STATUS_IS_OK(status)) {
2682 0 : return status;
2683 : }
2684 :
2685 123 : array->attributes[array->count] = SE_GROUP_MANDATORY |
2686 : SE_GROUP_ENABLED_BY_DEFAULT |
2687 : SE_GROUP_ENABLED;
2688 123 : array->count++;
2689 : }
2690 :
2691 123 : *r->out.rids = array;
2692 :
2693 123 : return NT_STATUS_OK;
2694 : }
2695 :
2696 :
2697 : /*
2698 : samr_SetMemberAttributesOfGroup
2699 : */
2700 0 : static NTSTATUS dcesrv_samr_SetMemberAttributesOfGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2701 : struct samr_SetMemberAttributesOfGroup *r)
2702 : {
2703 0 : DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
2704 : }
2705 :
2706 :
2707 : /*
2708 : samr_OpenAlias
2709 : */
2710 133 : static NTSTATUS dcesrv_samr_OpenAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2711 : struct samr_OpenAlias *r)
2712 : {
2713 : struct samr_domain_state *d_state;
2714 : struct samr_account_state *a_state;
2715 : struct dcesrv_handle *h;
2716 : const char *alias_name;
2717 : struct dom_sid *sid;
2718 : struct ldb_message **msgs;
2719 : struct dcesrv_handle *g_handle;
2720 133 : const char * const attrs[2] = { "sAMAccountName", NULL };
2721 : int ret;
2722 :
2723 133 : ZERO_STRUCTP(r->out.alias_handle);
2724 :
2725 133 : DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
2726 :
2727 133 : d_state = h->data;
2728 :
2729 : /* form the alias SID */
2730 133 : sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2731 133 : if (sid == NULL)
2732 0 : return NT_STATUS_NO_MEMORY;
2733 :
2734 : /* search for the group record */
2735 133 : ret = gendb_search(d_state->sam_ctx, mem_ctx, NULL, &msgs, attrs,
2736 : "(&(objectSid=%s)(objectclass=group)"
2737 : "(|(grouptype=%d)(grouptype=%d)))",
2738 : ldap_encode_ndr_dom_sid(mem_ctx, sid),
2739 : GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
2740 : GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
2741 133 : if (ret == 0) {
2742 0 : return NT_STATUS_NO_SUCH_ALIAS;
2743 : }
2744 133 : if (ret != 1) {
2745 0 : DEBUG(0,("Found %d records matching sid %s\n",
2746 : ret, dom_sid_string(mem_ctx, sid)));
2747 0 : return NT_STATUS_INTERNAL_DB_CORRUPTION;
2748 : }
2749 :
2750 133 : alias_name = ldb_msg_find_attr_as_string(msgs[0], "sAMAccountName", NULL);
2751 133 : if (alias_name == NULL) {
2752 0 : DEBUG(0,("sAMAccountName field missing for sid %s\n",
2753 : dom_sid_string(mem_ctx, sid)));
2754 0 : return NT_STATUS_INTERNAL_DB_CORRUPTION;
2755 : }
2756 :
2757 133 : a_state = talloc(mem_ctx, struct samr_account_state);
2758 133 : if (!a_state) {
2759 0 : return NT_STATUS_NO_MEMORY;
2760 : }
2761 133 : a_state->sam_ctx = d_state->sam_ctx;
2762 133 : a_state->access_mask = r->in.access_mask;
2763 133 : a_state->domain_state = talloc_reference(a_state, d_state);
2764 133 : a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
2765 133 : a_state->account_sid = talloc_steal(a_state, sid);
2766 133 : a_state->account_name = talloc_strdup(a_state, alias_name);
2767 133 : if (!a_state->account_name) {
2768 0 : return NT_STATUS_NO_MEMORY;
2769 : }
2770 :
2771 : /* create the policy handle */
2772 133 : g_handle = dcesrv_handle_create(dce_call, SAMR_HANDLE_ALIAS);
2773 133 : if (!g_handle) {
2774 0 : return NT_STATUS_NO_MEMORY;
2775 : }
2776 :
2777 133 : g_handle->data = talloc_steal(g_handle, a_state);
2778 :
2779 133 : *r->out.alias_handle = g_handle->wire_handle;
2780 :
2781 133 : return NT_STATUS_OK;
2782 : }
2783 :
2784 :
2785 : /*
2786 : samr_QueryAliasInfo
2787 : */
2788 424 : static NTSTATUS dcesrv_samr_QueryAliasInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2789 : struct samr_QueryAliasInfo *r)
2790 : {
2791 : struct dcesrv_handle *h;
2792 : struct samr_account_state *a_state;
2793 : struct ldb_message *msg, **res;
2794 424 : const char * const attrs[4] = { "sAMAccountName", "description",
2795 : "numMembers", NULL };
2796 : int ret;
2797 : union samr_AliasInfo *info;
2798 :
2799 424 : *r->out.info = NULL;
2800 :
2801 424 : DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2802 :
2803 424 : a_state = h->data;
2804 :
2805 : /* pull all the alias attributes */
2806 424 : ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
2807 : a_state->account_dn, &res, attrs);
2808 424 : if (ret == 0) {
2809 0 : return NT_STATUS_NO_SUCH_ALIAS;
2810 : }
2811 424 : if (ret != 1) {
2812 0 : return NT_STATUS_INTERNAL_DB_CORRUPTION;
2813 : }
2814 424 : msg = res[0];
2815 :
2816 : /* allocate the info structure */
2817 424 : info = talloc_zero(mem_ctx, union samr_AliasInfo);
2818 424 : if (info == NULL) {
2819 0 : return NT_STATUS_NO_MEMORY;
2820 : }
2821 :
2822 424 : switch(r->in.level) {
2823 138 : case ALIASINFOALL:
2824 138 : QUERY_STRING(msg, all.name, "sAMAccountName");
2825 138 : QUERY_UINT (msg, all.num_members, "numMembers");
2826 138 : QUERY_STRING(msg, all.description, "description");
2827 138 : break;
2828 143 : case ALIASINFONAME:
2829 143 : QUERY_STRING(msg, name, "sAMAccountName");
2830 143 : break;
2831 143 : case ALIASINFODESCRIPTION:
2832 143 : QUERY_STRING(msg, description, "description");
2833 143 : break;
2834 0 : default:
2835 0 : talloc_free(info);
2836 0 : return NT_STATUS_INVALID_INFO_CLASS;
2837 : }
2838 :
2839 424 : *r->out.info = info;
2840 :
2841 424 : return NT_STATUS_OK;
2842 : }
2843 :
2844 :
2845 : /*
2846 : samr_SetAliasInfo
2847 : */
2848 10 : static NTSTATUS dcesrv_samr_SetAliasInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2849 : struct samr_SetAliasInfo *r)
2850 : {
2851 : struct dcesrv_handle *h;
2852 : struct samr_account_state *a_state;
2853 : struct ldb_message *msg;
2854 : int ret;
2855 :
2856 10 : DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2857 :
2858 10 : a_state = h->data;
2859 :
2860 10 : msg = ldb_msg_new(mem_ctx);
2861 10 : if (msg == NULL) {
2862 0 : return NT_STATUS_NO_MEMORY;
2863 : }
2864 :
2865 10 : msg->dn = ldb_dn_copy(mem_ctx, a_state->account_dn);
2866 10 : if (!msg->dn) {
2867 0 : return NT_STATUS_NO_MEMORY;
2868 : }
2869 :
2870 10 : switch (r->in.level) {
2871 5 : case ALIASINFODESCRIPTION:
2872 5 : SET_STRING(msg, description, "description");
2873 5 : break;
2874 5 : case ALIASINFONAME:
2875 : /* On W2k3 this does not change the name, it changes the
2876 : * sAMAccountName attribute */
2877 5 : SET_STRING(msg, name, "sAMAccountName");
2878 5 : break;
2879 0 : default:
2880 0 : return NT_STATUS_INVALID_INFO_CLASS;
2881 : }
2882 :
2883 : /* modify the samdb record */
2884 10 : ret = ldb_modify(a_state->sam_ctx, msg);
2885 10 : if (ret != LDB_SUCCESS) {
2886 0 : return dsdb_ldb_err_to_ntstatus(ret);
2887 : }
2888 :
2889 10 : return NT_STATUS_OK;
2890 : }
2891 :
2892 :
2893 : /*
2894 : samr_DeleteDomAlias
2895 : */
2896 455 : static NTSTATUS dcesrv_samr_DeleteDomAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2897 : struct samr_DeleteDomAlias *r)
2898 : {
2899 : struct dcesrv_handle *h;
2900 : struct samr_account_state *a_state;
2901 : int ret;
2902 :
2903 455 : *r->out.alias_handle = *r->in.alias_handle;
2904 :
2905 455 : DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2906 :
2907 455 : a_state = h->data;
2908 :
2909 455 : ret = ldb_delete(a_state->sam_ctx, a_state->account_dn);
2910 455 : if (ret != LDB_SUCCESS) {
2911 0 : return dsdb_ldb_err_to_ntstatus(ret);
2912 : }
2913 :
2914 455 : talloc_free(h);
2915 455 : ZERO_STRUCTP(r->out.alias_handle);
2916 :
2917 455 : return NT_STATUS_OK;
2918 : }
2919 :
2920 :
2921 : /*
2922 : samr_AddAliasMember
2923 : */
2924 5 : static NTSTATUS dcesrv_samr_AddAliasMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2925 : struct samr_AddAliasMember *r)
2926 : {
2927 : struct dcesrv_handle *h;
2928 : struct samr_account_state *a_state;
2929 : struct samr_domain_state *d_state;
2930 : struct ldb_message *mod;
2931 : struct ldb_message **msgs;
2932 5 : const char * const attrs[] = { NULL };
2933 5 : struct ldb_dn *memberdn = NULL;
2934 : int ret;
2935 : NTSTATUS status;
2936 :
2937 5 : DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2938 :
2939 5 : a_state = h->data;
2940 5 : d_state = a_state->domain_state;
2941 :
2942 5 : ret = gendb_search(d_state->sam_ctx, mem_ctx, NULL,
2943 : &msgs, attrs, "(objectsid=%s)",
2944 5 : ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
2945 :
2946 5 : if (ret == 1) {
2947 5 : memberdn = msgs[0]->dn;
2948 0 : } else if (ret == 0) {
2949 0 : status = samdb_create_foreign_security_principal(
2950 0 : d_state->sam_ctx, mem_ctx, r->in.sid, &memberdn);
2951 0 : if (!NT_STATUS_IS_OK(status)) {
2952 0 : return status;
2953 : }
2954 : } else {
2955 0 : DEBUG(0,("Found %d records matching sid %s\n",
2956 : ret, dom_sid_string(mem_ctx, r->in.sid)));
2957 0 : return NT_STATUS_INTERNAL_DB_CORRUPTION;
2958 : }
2959 :
2960 5 : if (memberdn == NULL) {
2961 0 : DEBUG(0, ("Could not find memberdn\n"));
2962 0 : return NT_STATUS_INTERNAL_DB_CORRUPTION;
2963 : }
2964 :
2965 5 : mod = ldb_msg_new(mem_ctx);
2966 5 : if (mod == NULL) {
2967 0 : return NT_STATUS_NO_MEMORY;
2968 : }
2969 :
2970 5 : mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
2971 :
2972 8 : ret = samdb_msg_add_addval(d_state->sam_ctx, mem_ctx, mod, "member",
2973 5 : ldb_dn_alloc_linearized(mem_ctx, memberdn));
2974 5 : if (ret != LDB_SUCCESS) {
2975 0 : return dsdb_ldb_err_to_ntstatus(ret);
2976 : }
2977 :
2978 5 : ret = ldb_modify(a_state->sam_ctx, mod);
2979 5 : switch (ret) {
2980 5 : case LDB_SUCCESS:
2981 5 : return NT_STATUS_OK;
2982 0 : case LDB_ERR_ENTRY_ALREADY_EXISTS:
2983 0 : return NT_STATUS_MEMBER_IN_GROUP;
2984 0 : case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
2985 0 : return NT_STATUS_ACCESS_DENIED;
2986 0 : default:
2987 0 : return dsdb_ldb_err_to_ntstatus(ret);
2988 : }
2989 : }
2990 :
2991 :
2992 : /*
2993 : samr_DeleteAliasMember
2994 : */
2995 5 : static NTSTATUS dcesrv_samr_DeleteAliasMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2996 : struct samr_DeleteAliasMember *r)
2997 : {
2998 : struct dcesrv_handle *h;
2999 : struct samr_account_state *a_state;
3000 : struct samr_domain_state *d_state;
3001 : struct ldb_message *mod;
3002 : const char *memberdn;
3003 : int ret;
3004 :
3005 5 : DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
3006 :
3007 5 : a_state = h->data;
3008 5 : d_state = a_state->domain_state;
3009 :
3010 5 : memberdn = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL,
3011 : "distinguishedName", "(objectSid=%s)",
3012 5 : ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
3013 5 : if (memberdn == NULL) {
3014 0 : return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3015 : }
3016 :
3017 5 : mod = ldb_msg_new(mem_ctx);
3018 5 : if (mod == NULL) {
3019 0 : return NT_STATUS_NO_MEMORY;
3020 : }
3021 :
3022 5 : mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
3023 :
3024 5 : ret = samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod, "member",
3025 : memberdn);
3026 5 : if (ret != LDB_SUCCESS) {
3027 0 : return dsdb_ldb_err_to_ntstatus(ret);
3028 : }
3029 :
3030 5 : ret = ldb_modify(a_state->sam_ctx, mod);
3031 5 : switch (ret) {
3032 5 : case LDB_SUCCESS:
3033 5 : return NT_STATUS_OK;
3034 0 : case LDB_ERR_UNWILLING_TO_PERFORM:
3035 0 : return NT_STATUS_MEMBER_NOT_IN_GROUP;
3036 0 : case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
3037 0 : return NT_STATUS_ACCESS_DENIED;
3038 0 : default:
3039 0 : return dsdb_ldb_err_to_ntstatus(ret);
3040 : }
3041 : }
3042 :
3043 :
3044 : /*
3045 : samr_GetMembersInAlias
3046 : */
3047 133 : static NTSTATUS dcesrv_samr_GetMembersInAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3048 : struct samr_GetMembersInAlias *r)
3049 : {
3050 : struct dcesrv_handle *h;
3051 : struct samr_account_state *a_state;
3052 : struct samr_domain_state *d_state;
3053 : struct lsa_SidPtr *array;
3054 : unsigned int i, num_members;
3055 : struct dom_sid *members;
3056 : NTSTATUS status;
3057 :
3058 133 : DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
3059 :
3060 133 : a_state = h->data;
3061 133 : d_state = a_state->domain_state;
3062 :
3063 133 : status = dsdb_enum_group_mem(d_state->sam_ctx, mem_ctx,
3064 : a_state->account_dn, &members,
3065 : &num_members);
3066 133 : if (!NT_STATUS_IS_OK(status)) {
3067 0 : return status;
3068 : }
3069 :
3070 133 : if (num_members == 0) {
3071 93 : r->out.sids->sids = NULL;
3072 :
3073 93 : return NT_STATUS_OK;
3074 : }
3075 :
3076 40 : array = talloc_array(mem_ctx, struct lsa_SidPtr, num_members);
3077 40 : if (array == NULL) {
3078 0 : return NT_STATUS_NO_MEMORY;
3079 : }
3080 :
3081 140 : for (i=0; i<num_members; i++) {
3082 100 : array[i].sid = &members[i];
3083 : }
3084 :
3085 40 : r->out.sids->num_sids = num_members;
3086 40 : r->out.sids->sids = array;
3087 :
3088 40 : return NT_STATUS_OK;
3089 : }
3090 :
3091 : /*
3092 : samr_OpenUser
3093 : */
3094 2206 : static NTSTATUS dcesrv_samr_OpenUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3095 : struct samr_OpenUser *r)
3096 : {
3097 : struct samr_domain_state *d_state;
3098 : struct samr_account_state *a_state;
3099 : struct dcesrv_handle *h;
3100 : const char *account_name;
3101 : struct dom_sid *sid;
3102 : struct ldb_message **msgs;
3103 : struct dcesrv_handle *u_handle;
3104 2206 : const char * const attrs[2] = { "sAMAccountName", NULL };
3105 : int ret;
3106 :
3107 2206 : ZERO_STRUCTP(r->out.user_handle);
3108 :
3109 2206 : DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
3110 :
3111 2201 : d_state = h->data;
3112 :
3113 : /* form the users SID */
3114 2201 : sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
3115 2201 : if (!sid) {
3116 0 : return NT_STATUS_NO_MEMORY;
3117 : }
3118 :
3119 : /* search for the user record */
3120 2201 : ret = gendb_search(d_state->sam_ctx,
3121 : mem_ctx, d_state->domain_dn, &msgs, attrs,
3122 : "(&(objectSid=%s)(objectclass=user))",
3123 : ldap_encode_ndr_dom_sid(mem_ctx, sid));
3124 2201 : if (ret == 0) {
3125 35 : return NT_STATUS_NO_SUCH_USER;
3126 : }
3127 2166 : if (ret != 1) {
3128 0 : DEBUG(0,("Found %d records matching sid %s\n", ret,
3129 : dom_sid_string(mem_ctx, sid)));
3130 0 : return NT_STATUS_INTERNAL_DB_CORRUPTION;
3131 : }
3132 :
3133 2166 : account_name = ldb_msg_find_attr_as_string(msgs[0], "sAMAccountName", NULL);
3134 2166 : if (account_name == NULL) {
3135 0 : DEBUG(0,("sAMAccountName field missing for sid %s\n",
3136 : dom_sid_string(mem_ctx, sid)));
3137 0 : return NT_STATUS_INTERNAL_DB_CORRUPTION;
3138 : }
3139 :
3140 2166 : a_state = talloc(mem_ctx, struct samr_account_state);
3141 2166 : if (!a_state) {
3142 0 : return NT_STATUS_NO_MEMORY;
3143 : }
3144 2166 : a_state->sam_ctx = d_state->sam_ctx;
3145 2166 : a_state->access_mask = r->in.access_mask;
3146 2166 : a_state->domain_state = talloc_reference(a_state, d_state);
3147 2166 : a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
3148 2166 : a_state->account_sid = talloc_steal(a_state, sid);
3149 2166 : a_state->account_name = talloc_strdup(a_state, account_name);
3150 2166 : if (!a_state->account_name) {
3151 0 : return NT_STATUS_NO_MEMORY;
3152 : }
3153 :
3154 : /* create the policy handle */
3155 2166 : u_handle = dcesrv_handle_create(dce_call, SAMR_HANDLE_USER);
3156 2166 : if (!u_handle) {
3157 0 : return NT_STATUS_NO_MEMORY;
3158 : }
3159 :
3160 2166 : u_handle->data = talloc_steal(u_handle, a_state);
3161 :
3162 2166 : *r->out.user_handle = u_handle->wire_handle;
3163 :
3164 2166 : return NT_STATUS_OK;
3165 :
3166 : }
3167 :
3168 :
3169 : /*
3170 : samr_DeleteUser
3171 : */
3172 1048 : static NTSTATUS dcesrv_samr_DeleteUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3173 : struct samr_DeleteUser *r)
3174 : {
3175 : struct dcesrv_handle *h;
3176 : struct samr_account_state *a_state;
3177 : int ret;
3178 :
3179 1048 : *r->out.user_handle = *r->in.user_handle;
3180 :
3181 1048 : DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
3182 :
3183 1048 : a_state = h->data;
3184 :
3185 1048 : ret = ldb_delete(a_state->sam_ctx, a_state->account_dn);
3186 1048 : if (ret != LDB_SUCCESS) {
3187 0 : DEBUG(1, ("Failed to delete user: %s: %s\n",
3188 : ldb_dn_get_linearized(a_state->account_dn),
3189 : ldb_errstring(a_state->sam_ctx)));
3190 0 : return dsdb_ldb_err_to_ntstatus(ret);
3191 : }
3192 :
3193 1048 : talloc_free(h);
3194 1048 : ZERO_STRUCTP(r->out.user_handle);
3195 :
3196 1048 : return NT_STATUS_OK;
3197 : }
3198 :
3199 :
3200 : /*
3201 : samr_QueryUserInfo
3202 : */
3203 10724 : static NTSTATUS dcesrv_samr_QueryUserInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3204 : struct samr_QueryUserInfo *r)
3205 : {
3206 : struct dcesrv_handle *h;
3207 : struct samr_account_state *a_state;
3208 : struct ldb_message *msg, **res;
3209 : int ret;
3210 : struct ldb_context *sam_ctx;
3211 :
3212 10724 : const char * const *attrs = NULL;
3213 : union samr_UserInfo *info;
3214 :
3215 : NTSTATUS status;
3216 :
3217 10724 : *r->out.info = NULL;
3218 :
3219 10724 : DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
3220 :
3221 10724 : a_state = h->data;
3222 10724 : sam_ctx = a_state->sam_ctx;
3223 :
3224 : /* fill in the reply */
3225 10724 : switch (r->in.level) {
3226 203 : case 1:
3227 : {
3228 : static const char * const attrs2[] = {"sAMAccountName",
3229 : "displayName",
3230 : "primaryGroupID",
3231 : "description",
3232 : "comment",
3233 : NULL};
3234 203 : attrs = attrs2;
3235 203 : break;
3236 : }
3237 195 : case 2:
3238 : {
3239 : static const char * const attrs2[] = {"comment",
3240 : "countryCode",
3241 : "codePage",
3242 : NULL};
3243 195 : attrs = attrs2;
3244 195 : break;
3245 : }
3246 1458 : case 3:
3247 : {
3248 : static const char * const attrs2[] = {"sAMAccountName",
3249 : "displayName",
3250 : "objectSid",
3251 : "primaryGroupID",
3252 : "homeDirectory",
3253 : "homeDrive",
3254 : "scriptPath",
3255 : "profilePath",
3256 : "userWorkstations",
3257 : "lastLogon",
3258 : "lastLogoff",
3259 : "pwdLastSet",
3260 : "msDS-UserPasswordExpiryTimeComputed",
3261 : "logonHours",
3262 : "badPwdCount",
3263 : "badPasswordTime",
3264 : "logonCount",
3265 : "userAccountControl",
3266 : "msDS-User-Account-Control-Computed",
3267 : NULL};
3268 1458 : attrs = attrs2;
3269 1458 : break;
3270 : }
3271 112 : case 4:
3272 : {
3273 : static const char * const attrs2[] = {"logonHours",
3274 : NULL};
3275 112 : attrs = attrs2;
3276 112 : break;
3277 : }
3278 1614 : case 5:
3279 : {
3280 : static const char * const attrs2[] = {"sAMAccountName",
3281 : "displayName",
3282 : "objectSid",
3283 : "primaryGroupID",
3284 : "homeDirectory",
3285 : "homeDrive",
3286 : "scriptPath",
3287 : "profilePath",
3288 : "description",
3289 : "userWorkstations",
3290 : "lastLogon",
3291 : "lastLogoff",
3292 : "logonHours",
3293 : "badPwdCount",
3294 : "badPasswordTime",
3295 : "logonCount",
3296 : "pwdLastSet",
3297 : "msDS-ResultantPSO",
3298 : "msDS-UserPasswordExpiryTimeComputed",
3299 : "accountExpires",
3300 : "userAccountControl",
3301 : "msDS-User-Account-Control-Computed",
3302 : NULL};
3303 1614 : attrs = attrs2;
3304 1614 : break;
3305 : }
3306 406 : case 6:
3307 : {
3308 : static const char * const attrs2[] = {"sAMAccountName",
3309 : "displayName",
3310 : NULL};
3311 406 : attrs = attrs2;
3312 406 : break;
3313 : }
3314 210 : case 7:
3315 : {
3316 : static const char * const attrs2[] = {"sAMAccountName",
3317 : NULL};
3318 210 : attrs = attrs2;
3319 210 : break;
3320 : }
3321 112 : case 8:
3322 : {
3323 : static const char * const attrs2[] = {"displayName",
3324 : NULL};
3325 112 : attrs = attrs2;
3326 112 : break;
3327 : }
3328 28 : case 9:
3329 : {
3330 : static const char * const attrs2[] = {"primaryGroupID",
3331 : NULL};
3332 28 : attrs = attrs2;
3333 28 : break;
3334 : }
3335 236 : case 10:
3336 : {
3337 : static const char * const attrs2[] = {"homeDirectory",
3338 : "homeDrive",
3339 : NULL};
3340 236 : attrs = attrs2;
3341 236 : break;
3342 : }
3343 112 : case 11:
3344 : {
3345 : static const char * const attrs2[] = {"scriptPath",
3346 : NULL};
3347 112 : attrs = attrs2;
3348 112 : break;
3349 : }
3350 126 : case 12:
3351 : {
3352 : static const char * const attrs2[] = {"profilePath",
3353 : NULL};
3354 126 : attrs = attrs2;
3355 126 : break;
3356 : }
3357 112 : case 13:
3358 : {
3359 : static const char * const attrs2[] = {"description",
3360 : NULL};
3361 112 : attrs = attrs2;
3362 112 : break;
3363 : }
3364 126 : case 14:
3365 : {
3366 : static const char * const attrs2[] = {"userWorkstations",
3367 : NULL};
3368 126 : attrs = attrs2;
3369 126 : break;
3370 : }
3371 2107 : case 16:
3372 : {
3373 : static const char * const attrs2[] = {"userAccountControl",
3374 : "msDS-User-Account-Control-Computed",
3375 : "pwdLastSet",
3376 : "msDS-UserPasswordExpiryTimeComputed",
3377 : NULL};
3378 2107 : attrs = attrs2;
3379 2107 : break;
3380 : }
3381 98 : case 17:
3382 : {
3383 : static const char * const attrs2[] = {"accountExpires",
3384 : NULL};
3385 98 : attrs = attrs2;
3386 98 : break;
3387 : }
3388 0 : case 18:
3389 : {
3390 0 : return NT_STATUS_NOT_SUPPORTED;
3391 : }
3392 112 : case 20:
3393 : {
3394 : static const char * const attrs2[] = {"userParameters",
3395 : NULL};
3396 112 : attrs = attrs2;
3397 112 : break;
3398 : }
3399 3357 : case 21:
3400 : {
3401 : static const char * const attrs2[] = {"lastLogon",
3402 : "lastLogoff",
3403 : "pwdLastSet",
3404 : "msDS-ResultantPSO",
3405 : "msDS-UserPasswordExpiryTimeComputed",
3406 : "accountExpires",
3407 : "sAMAccountName",
3408 : "displayName",
3409 : "homeDirectory",
3410 : "homeDrive",
3411 : "scriptPath",
3412 : "profilePath",
3413 : "description",
3414 : "userWorkstations",
3415 : "comment",
3416 : "userParameters",
3417 : "objectSid",
3418 : "primaryGroupID",
3419 : "userAccountControl",
3420 : "msDS-User-Account-Control-Computed",
3421 : "logonHours",
3422 : "badPwdCount",
3423 : "badPasswordTime",
3424 : "logonCount",
3425 : "countryCode",
3426 : "codePage",
3427 : NULL};
3428 3357 : attrs = attrs2;
3429 3357 : break;
3430 : }
3431 0 : case 23:
3432 : case 24:
3433 : case 25:
3434 : case 26:
3435 : {
3436 0 : return NT_STATUS_NOT_SUPPORTED;
3437 : }
3438 0 : default:
3439 : {
3440 0 : return NT_STATUS_INVALID_INFO_CLASS;
3441 : }
3442 : }
3443 :
3444 : /* pull all the user attributes */
3445 10724 : ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
3446 : a_state->account_dn, &res, attrs);
3447 10724 : if (ret == 0) {
3448 0 : return NT_STATUS_NO_SUCH_USER;
3449 : }
3450 10724 : if (ret != 1) {
3451 0 : return NT_STATUS_INTERNAL_DB_CORRUPTION;
3452 : }
3453 10724 : msg = res[0];
3454 :
3455 : /* allocate the info structure */
3456 10724 : info = talloc_zero(mem_ctx, union samr_UserInfo);
3457 10724 : if (info == NULL) {
3458 0 : return NT_STATUS_NO_MEMORY;
3459 : }
3460 :
3461 : /* fill in the reply */
3462 10724 : switch (r->in.level) {
3463 203 : case 1:
3464 203 : QUERY_STRING(msg, info1.account_name, "sAMAccountName");
3465 203 : QUERY_STRING(msg, info1.full_name, "displayName");
3466 203 : QUERY_UINT (msg, info1.primary_gid, "primaryGroupID");
3467 203 : QUERY_STRING(msg, info1.description, "description");
3468 203 : QUERY_STRING(msg, info1.comment, "comment");
3469 203 : break;
3470 :
3471 195 : case 2:
3472 195 : QUERY_STRING(msg, info2.comment, "comment");
3473 195 : QUERY_UINT (msg, info2.country_code, "countryCode");
3474 195 : QUERY_UINT (msg, info2.code_page, "codePage");
3475 195 : break;
3476 :
3477 1458 : case 3:
3478 1458 : QUERY_STRING(msg, info3.account_name, "sAMAccountName");
3479 1458 : QUERY_STRING(msg, info3.full_name, "displayName");
3480 1458 : QUERY_RID (msg, info3.rid, "objectSid");
3481 1458 : QUERY_UINT (msg, info3.primary_gid, "primaryGroupID");
3482 1458 : QUERY_STRING(msg, info3.home_directory, "homeDirectory");
3483 1458 : QUERY_STRING(msg, info3.home_drive, "homeDrive");
3484 1458 : QUERY_STRING(msg, info3.logon_script, "scriptPath");
3485 1458 : QUERY_STRING(msg, info3.profile_path, "profilePath");
3486 1458 : QUERY_STRING(msg, info3.workstations, "userWorkstations");
3487 1458 : QUERY_UINT64(msg, info3.last_logon, "lastLogon");
3488 1458 : QUERY_UINT64(msg, info3.last_logoff, "lastLogoff");
3489 1458 : QUERY_UINT64(msg, info3.last_password_change, "pwdLastSet");
3490 1458 : QUERY_APASSC(msg, info3.allow_password_change, "pwdLastSet");
3491 1458 : QUERY_UINT64(msg, info3.force_password_change, "msDS-UserPasswordExpiryTimeComputed");
3492 1458 : QUERY_LHOURS(msg, info3.logon_hours, "logonHours");
3493 : /* level 3 gives the raw badPwdCount value */
3494 1458 : QUERY_UINT (msg, info3.bad_password_count, "badPwdCount");
3495 1458 : QUERY_UINT (msg, info3.logon_count, "logonCount");
3496 1458 : QUERY_AFLAGS(msg, info3.acct_flags, "msDS-User-Account-Control-Computed");
3497 1458 : break;
3498 :
3499 112 : case 4:
3500 112 : QUERY_LHOURS(msg, info4.logon_hours, "logonHours");
3501 112 : break;
3502 :
3503 1614 : case 5:
3504 1614 : QUERY_STRING(msg, info5.account_name, "sAMAccountName");
3505 1614 : QUERY_STRING(msg, info5.full_name, "displayName");
3506 1614 : QUERY_RID (msg, info5.rid, "objectSid");
3507 1614 : QUERY_UINT (msg, info5.primary_gid, "primaryGroupID");
3508 1614 : QUERY_STRING(msg, info5.home_directory, "homeDirectory");
3509 1614 : QUERY_STRING(msg, info5.home_drive, "homeDrive");
3510 1614 : QUERY_STRING(msg, info5.logon_script, "scriptPath");
3511 1614 : QUERY_STRING(msg, info5.profile_path, "profilePath");
3512 1614 : QUERY_STRING(msg, info5.description, "description");
3513 1614 : QUERY_STRING(msg, info5.workstations, "userWorkstations");
3514 1614 : QUERY_UINT64(msg, info5.last_logon, "lastLogon");
3515 1614 : QUERY_UINT64(msg, info5.last_logoff, "lastLogoff");
3516 1614 : QUERY_LHOURS(msg, info5.logon_hours, "logonHours");
3517 1614 : QUERY_BPWDCT(msg, info5.bad_password_count, "badPwdCount");
3518 1614 : QUERY_UINT (msg, info5.logon_count, "logonCount");
3519 1614 : QUERY_UINT64(msg, info5.last_password_change, "pwdLastSet");
3520 1614 : QUERY_UINT64(msg, info5.acct_expiry, "accountExpires");
3521 1614 : QUERY_AFLAGS(msg, info5.acct_flags, "msDS-User-Account-Control-Computed");
3522 1614 : break;
3523 :
3524 406 : case 6:
3525 406 : QUERY_STRING(msg, info6.account_name, "sAMAccountName");
3526 406 : QUERY_STRING(msg, info6.full_name, "displayName");
3527 406 : break;
3528 :
3529 210 : case 7:
3530 210 : QUERY_STRING(msg, info7.account_name, "sAMAccountName");
3531 210 : break;
3532 :
3533 112 : case 8:
3534 112 : QUERY_STRING(msg, info8.full_name, "displayName");
3535 112 : break;
3536 :
3537 28 : case 9:
3538 28 : QUERY_UINT (msg, info9.primary_gid, "primaryGroupID");
3539 28 : break;
3540 :
3541 236 : case 10:
3542 236 : QUERY_STRING(msg, info10.home_directory,"homeDirectory");
3543 236 : QUERY_STRING(msg, info10.home_drive, "homeDrive");
3544 236 : break;
3545 :
3546 112 : case 11:
3547 112 : QUERY_STRING(msg, info11.logon_script, "scriptPath");
3548 112 : break;
3549 :
3550 126 : case 12:
3551 126 : QUERY_STRING(msg, info12.profile_path, "profilePath");
3552 126 : break;
3553 :
3554 112 : case 13:
3555 112 : QUERY_STRING(msg, info13.description, "description");
3556 112 : break;
3557 :
3558 126 : case 14:
3559 126 : QUERY_STRING(msg, info14.workstations, "userWorkstations");
3560 126 : break;
3561 :
3562 2107 : case 16:
3563 2107 : QUERY_AFLAGS(msg, info16.acct_flags, "msDS-User-Account-Control-Computed");
3564 2107 : break;
3565 :
3566 98 : case 17:
3567 98 : QUERY_UINT64(msg, info17.acct_expiry, "accountExpires");
3568 98 : break;
3569 :
3570 112 : case 20:
3571 112 : status = samdb_result_parameters(mem_ctx, msg, "userParameters", &info->info20.parameters);
3572 112 : if (!NT_STATUS_IS_OK(status)) {
3573 0 : talloc_free(info);
3574 0 : return status;
3575 : }
3576 112 : break;
3577 :
3578 3357 : case 21:
3579 3357 : QUERY_UINT64(msg, info21.last_logon, "lastLogon");
3580 3357 : QUERY_UINT64(msg, info21.last_logoff, "lastLogoff");
3581 3357 : QUERY_UINT64(msg, info21.last_password_change, "pwdLastSet");
3582 3357 : QUERY_UINT64(msg, info21.acct_expiry, "accountExpires");
3583 3357 : QUERY_APASSC(msg, info21.allow_password_change,"pwdLastSet");
3584 3357 : QUERY_UINT64(msg, info21.force_password_change, "msDS-UserPasswordExpiryTimeComputed");
3585 3357 : QUERY_STRING(msg, info21.account_name, "sAMAccountName");
3586 3357 : QUERY_STRING(msg, info21.full_name, "displayName");
3587 3357 : QUERY_STRING(msg, info21.home_directory, "homeDirectory");
3588 3357 : QUERY_STRING(msg, info21.home_drive, "homeDrive");
3589 3357 : QUERY_STRING(msg, info21.logon_script, "scriptPath");
3590 3357 : QUERY_STRING(msg, info21.profile_path, "profilePath");
3591 3357 : QUERY_STRING(msg, info21.description, "description");
3592 3357 : QUERY_STRING(msg, info21.workstations, "userWorkstations");
3593 3357 : QUERY_STRING(msg, info21.comment, "comment");
3594 3357 : status = samdb_result_parameters(mem_ctx, msg, "userParameters", &info->info21.parameters);
3595 3357 : if (!NT_STATUS_IS_OK(status)) {
3596 0 : talloc_free(info);
3597 0 : return status;
3598 : }
3599 :
3600 3357 : QUERY_RID (msg, info21.rid, "objectSid");
3601 3357 : QUERY_UINT (msg, info21.primary_gid, "primaryGroupID");
3602 3357 : QUERY_AFLAGS(msg, info21.acct_flags, "msDS-User-Account-Control-Computed");
3603 3357 : info->info21.fields_present = 0x08FFFFFF;
3604 3357 : QUERY_LHOURS(msg, info21.logon_hours, "logonHours");
3605 3357 : QUERY_BPWDCT(msg, info21.bad_password_count, "badPwdCount");
3606 3357 : QUERY_UINT (msg, info21.logon_count, "logonCount");
3607 3357 : if ((info->info21.acct_flags & ACB_PW_EXPIRED) != 0) {
3608 1260 : info->info21.password_expired = PASS_MUST_CHANGE_AT_NEXT_LOGON;
3609 : } else {
3610 2097 : info->info21.password_expired = PASS_DONT_CHANGE_AT_NEXT_LOGON;
3611 : }
3612 3357 : QUERY_UINT (msg, info21.country_code, "countryCode");
3613 3357 : QUERY_UINT (msg, info21.code_page, "codePage");
3614 3357 : break;
3615 :
3616 :
3617 0 : default:
3618 0 : talloc_free(info);
3619 0 : return NT_STATUS_INVALID_INFO_CLASS;
3620 : }
3621 :
3622 10724 : *r->out.info = info;
3623 :
3624 10724 : return NT_STATUS_OK;
3625 : }
3626 :
3627 :
3628 : /*
3629 : samr_SetUserInfo
3630 : */
3631 4473 : static NTSTATUS dcesrv_samr_SetUserInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3632 : struct samr_SetUserInfo *r)
3633 : {
3634 : struct dcesrv_handle *h;
3635 : struct samr_account_state *a_state;
3636 : struct ldb_message *msg;
3637 : int ret;
3638 4473 : NTSTATUS status = NT_STATUS_OK;
3639 : struct ldb_context *sam_ctx;
3640 :
3641 4473 : DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
3642 :
3643 4473 : a_state = h->data;
3644 4473 : sam_ctx = a_state->sam_ctx;
3645 :
3646 4473 : msg = ldb_msg_new(mem_ctx);
3647 4473 : if (msg == NULL) {
3648 0 : return NT_STATUS_NO_MEMORY;
3649 : }
3650 :
3651 4473 : msg->dn = talloc_reference(mem_ctx, a_state->account_dn);
3652 4473 : if (!msg->dn) {
3653 0 : return NT_STATUS_NO_MEMORY;
3654 : }
3655 :
3656 4473 : switch (r->in.level) {
3657 131 : case 2:
3658 131 : SET_STRING(msg, info2.comment, "comment");
3659 131 : SET_UINT (msg, info2.country_code, "countryCode");
3660 131 : SET_UINT (msg, info2.code_page, "codePage");
3661 131 : break;
3662 :
3663 84 : case 4:
3664 84 : SET_LHOURS(msg, info4.logon_hours, "logonHours");
3665 84 : break;
3666 :
3667 336 : case 6:
3668 336 : SET_STRING(msg, info6.account_name, "samAccountName");
3669 336 : SET_STRING(msg, info6.full_name, "displayName");
3670 336 : break;
3671 :
3672 172 : case 7:
3673 172 : SET_STRING(msg, info7.account_name, "samAccountName");
3674 172 : break;
3675 :
3676 62 : case 8:
3677 62 : SET_STRING(msg, info8.full_name, "displayName");
3678 62 : break;
3679 :
3680 0 : case 9:
3681 0 : SET_UINT(msg, info9.primary_gid, "primaryGroupID");
3682 0 : break;
3683 :
3684 178 : case 10:
3685 178 : SET_STRING(msg, info10.home_directory, "homeDirectory");
3686 178 : SET_STRING(msg, info10.home_drive, "homeDrive");
3687 178 : break;
3688 :
3689 86 : case 11:
3690 86 : SET_STRING(msg, info11.logon_script, "scriptPath");
3691 86 : break;
3692 :
3693 94 : case 12:
3694 94 : SET_STRING(msg, info12.profile_path, "profilePath");
3695 94 : break;
3696 :
3697 89 : case 13:
3698 89 : SET_STRING(msg, info13.description, "description");
3699 89 : break;
3700 :
3701 84 : case 14:
3702 84 : SET_STRING(msg, info14.workstations, "userWorkstations");
3703 84 : break;
3704 :
3705 320 : case 16:
3706 320 : SET_AFLAGS(msg, info16.acct_flags, "userAccountControl");
3707 320 : break;
3708 :
3709 67 : case 17:
3710 67 : SET_UINT64(msg, info17.acct_expiry, "accountExpires");
3711 67 : break;
3712 :
3713 54 : case 18:
3714 270 : status = samr_set_password_buffers(dce_call,
3715 54 : a_state->sam_ctx,
3716 : a_state->account_dn,
3717 54 : a_state->domain_state->domain_dn,
3718 : mem_ctx,
3719 99 : r->in.info->info18.lm_pwd_active ? r->in.info->info18.lm_pwd.hash : NULL,
3720 99 : r->in.info->info18.nt_pwd_active ? r->in.info->info18.nt_pwd.hash : NULL);
3721 54 : if (!NT_STATUS_IS_OK(status)) {
3722 0 : return status;
3723 : }
3724 :
3725 54 : if (r->in.info->info18.password_expired > 0) {
3726 : struct ldb_message_element *set_el;
3727 24 : if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg, "pwdLastSet", 0) != LDB_SUCCESS) {
3728 0 : return NT_STATUS_NO_MEMORY;
3729 : }
3730 24 : set_el = ldb_msg_find_element(msg, "pwdLastSet");
3731 24 : set_el->flags = LDB_FLAG_MOD_REPLACE;
3732 : }
3733 54 : break;
3734 :
3735 56 : case 20:
3736 56 : SET_PARAMETERS(msg, info20.parameters, "userParameters");
3737 56 : break;
3738 :
3739 1692 : case 21:
3740 1692 : if (r->in.info->info21.fields_present == 0)
3741 12 : return NT_STATUS_INVALID_PARAMETER;
3742 :
3743 : #define IFSET(bit) if (bit & r->in.info->info21.fields_present)
3744 1680 : IFSET(SAMR_FIELD_LAST_LOGON)
3745 0 : SET_UINT64(msg, info21.last_logon, "lastLogon");
3746 1680 : IFSET(SAMR_FIELD_LAST_LOGOFF)
3747 0 : SET_UINT64(msg, info21.last_logoff, "lastLogoff");
3748 1680 : IFSET(SAMR_FIELD_ACCT_EXPIRY)
3749 86 : SET_UINT64(msg, info21.acct_expiry, "accountExpires");
3750 1680 : IFSET(SAMR_FIELD_ACCOUNT_NAME)
3751 29 : SET_STRING(msg, info21.account_name, "samAccountName");
3752 1680 : IFSET(SAMR_FIELD_FULL_NAME)
3753 590 : SET_STRING(msg, info21.full_name, "displayName");
3754 1680 : IFSET(SAMR_FIELD_HOME_DIRECTORY)
3755 58 : SET_STRING(msg, info21.home_directory, "homeDirectory");
3756 1680 : IFSET(SAMR_FIELD_HOME_DRIVE)
3757 58 : SET_STRING(msg, info21.home_drive, "homeDrive");
3758 1680 : IFSET(SAMR_FIELD_LOGON_SCRIPT)
3759 30 : SET_STRING(msg, info21.logon_script, "scriptPath");
3760 1680 : IFSET(SAMR_FIELD_PROFILE_PATH)
3761 30 : SET_STRING(msg, info21.profile_path, "profilePath");
3762 1680 : IFSET(SAMR_FIELD_DESCRIPTION)
3763 563 : SET_STRING(msg, info21.description, "description");
3764 1680 : IFSET(SAMR_FIELD_WORKSTATIONS)
3765 116 : SET_STRING(msg, info21.workstations, "userWorkstations");
3766 1680 : IFSET(SAMR_FIELD_COMMENT)
3767 610 : SET_STRING(msg, info21.comment, "comment");
3768 1680 : IFSET(SAMR_FIELD_PARAMETERS)
3769 112 : SET_PARAMETERS(msg, info21.parameters, "userParameters");
3770 1680 : IFSET(SAMR_FIELD_PRIMARY_GID)
3771 2 : SET_UINT(msg, info21.primary_gid, "primaryGroupID");
3772 1680 : IFSET(SAMR_FIELD_ACCT_FLAGS)
3773 47 : SET_AFLAGS(msg, info21.acct_flags, "userAccountControl");
3774 1680 : IFSET(SAMR_FIELD_LOGON_HOURS)
3775 31 : SET_LHOURS(msg, info21.logon_hours, "logonHours");
3776 1680 : IFSET(SAMR_FIELD_BAD_PWD_COUNT)
3777 0 : SET_UINT (msg, info21.bad_password_count, "badPwdCount");
3778 1680 : IFSET(SAMR_FIELD_NUM_LOGONS)
3779 0 : SET_UINT (msg, info21.logon_count, "logonCount");
3780 1680 : IFSET(SAMR_FIELD_COUNTRY_CODE)
3781 48 : SET_UINT (msg, info21.country_code, "countryCode");
3782 1680 : IFSET(SAMR_FIELD_CODE_PAGE)
3783 48 : SET_UINT (msg, info21.code_page, "codePage");
3784 :
3785 : /* password change fields */
3786 1680 : IFSET(SAMR_FIELD_LAST_PWD_CHANGE)
3787 60 : return NT_STATUS_ACCESS_DENIED;
3788 :
3789 1620 : IFSET((SAMR_FIELD_LM_PASSWORD_PRESENT
3790 : | SAMR_FIELD_NT_PASSWORD_PRESENT)) {
3791 222 : uint8_t *lm_pwd_hash = NULL, *nt_pwd_hash = NULL;
3792 :
3793 222 : if (r->in.info->info21.lm_password_set) {
3794 114 : if ((r->in.info->info21.lm_owf_password.length != 16)
3795 108 : || (r->in.info->info21.lm_owf_password.size != 16)) {
3796 6 : return NT_STATUS_INVALID_PARAMETER;
3797 : }
3798 :
3799 108 : lm_pwd_hash = (uint8_t *) r->in.info->info21.lm_owf_password.array;
3800 : }
3801 216 : if (r->in.info->info21.nt_password_set) {
3802 216 : if ((r->in.info->info21.nt_owf_password.length != 16)
3803 204 : || (r->in.info->info21.nt_owf_password.size != 16)) {
3804 12 : return NT_STATUS_INVALID_PARAMETER;
3805 : }
3806 :
3807 204 : nt_pwd_hash = (uint8_t *) r->in.info->info21.nt_owf_password.array;
3808 : }
3809 374 : status = samr_set_password_buffers(dce_call,
3810 204 : a_state->sam_ctx,
3811 : a_state->account_dn,
3812 204 : a_state->domain_state->domain_dn,
3813 : mem_ctx,
3814 : lm_pwd_hash,
3815 : nt_pwd_hash);
3816 204 : if (!NT_STATUS_IS_OK(status)) {
3817 0 : return status;
3818 : }
3819 : }
3820 :
3821 :
3822 1602 : IFSET(SAMR_FIELD_EXPIRED_FLAG) {
3823 146 : const char *t = "0";
3824 : struct ldb_message_element *set_el;
3825 146 : if (r->in.info->info21.password_expired
3826 : == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
3827 74 : t = "-1";
3828 : }
3829 146 : if (ldb_msg_add_string(msg, "pwdLastSet", t) != LDB_SUCCESS) {
3830 0 : return NT_STATUS_NO_MEMORY;
3831 : }
3832 146 : set_el = ldb_msg_find_element(msg, "pwdLastSet");
3833 146 : set_el->flags = LDB_FLAG_MOD_REPLACE;
3834 : }
3835 : #undef IFSET
3836 1530 : break;
3837 :
3838 38 : case 23:
3839 38 : if (r->in.info->info23.info.fields_present == 0)
3840 0 : return NT_STATUS_INVALID_PARAMETER;
3841 :
3842 : #define IFSET(bit) if (bit & r->in.info->info23.info.fields_present)
3843 38 : IFSET(SAMR_FIELD_LAST_LOGON)
3844 0 : SET_UINT64(msg, info23.info.last_logon, "lastLogon");
3845 38 : IFSET(SAMR_FIELD_LAST_LOGOFF)
3846 0 : SET_UINT64(msg, info23.info.last_logoff, "lastLogoff");
3847 38 : IFSET(SAMR_FIELD_ACCT_EXPIRY)
3848 0 : SET_UINT64(msg, info23.info.acct_expiry, "accountExpires");
3849 38 : IFSET(SAMR_FIELD_ACCOUNT_NAME)
3850 0 : SET_STRING(msg, info23.info.account_name, "samAccountName");
3851 38 : IFSET(SAMR_FIELD_FULL_NAME)
3852 0 : SET_STRING(msg, info23.info.full_name, "displayName");
3853 38 : IFSET(SAMR_FIELD_HOME_DIRECTORY)
3854 0 : SET_STRING(msg, info23.info.home_directory, "homeDirectory");
3855 38 : IFSET(SAMR_FIELD_HOME_DRIVE)
3856 0 : SET_STRING(msg, info23.info.home_drive, "homeDrive");
3857 38 : IFSET(SAMR_FIELD_LOGON_SCRIPT)
3858 0 : SET_STRING(msg, info23.info.logon_script, "scriptPath");
3859 38 : IFSET(SAMR_FIELD_PROFILE_PATH)
3860 0 : SET_STRING(msg, info23.info.profile_path, "profilePath");
3861 38 : IFSET(SAMR_FIELD_DESCRIPTION)
3862 0 : SET_STRING(msg, info23.info.description, "description");
3863 38 : IFSET(SAMR_FIELD_WORKSTATIONS)
3864 0 : SET_STRING(msg, info23.info.workstations, "userWorkstations");
3865 38 : IFSET(SAMR_FIELD_COMMENT)
3866 0 : SET_STRING(msg, info23.info.comment, "comment");
3867 38 : IFSET(SAMR_FIELD_PARAMETERS)
3868 0 : SET_PARAMETERS(msg, info23.info.parameters, "userParameters");
3869 38 : IFSET(SAMR_FIELD_PRIMARY_GID)
3870 0 : SET_UINT(msg, info23.info.primary_gid, "primaryGroupID");
3871 38 : IFSET(SAMR_FIELD_ACCT_FLAGS)
3872 0 : SET_AFLAGS(msg, info23.info.acct_flags, "userAccountControl");
3873 38 : IFSET(SAMR_FIELD_LOGON_HOURS)
3874 0 : SET_LHOURS(msg, info23.info.logon_hours, "logonHours");
3875 38 : IFSET(SAMR_FIELD_BAD_PWD_COUNT)
3876 0 : SET_UINT (msg, info23.info.bad_password_count, "badPwdCount");
3877 38 : IFSET(SAMR_FIELD_NUM_LOGONS)
3878 0 : SET_UINT (msg, info23.info.logon_count, "logonCount");
3879 :
3880 38 : IFSET(SAMR_FIELD_COUNTRY_CODE)
3881 0 : SET_UINT (msg, info23.info.country_code, "countryCode");
3882 38 : IFSET(SAMR_FIELD_CODE_PAGE)
3883 0 : SET_UINT (msg, info23.info.code_page, "codePage");
3884 :
3885 : /* password change fields */
3886 38 : IFSET(SAMR_FIELD_LAST_PWD_CHANGE)
3887 0 : return NT_STATUS_ACCESS_DENIED;
3888 :
3889 38 : IFSET(SAMR_FIELD_NT_PASSWORD_PRESENT) {
3890 68 : status = samr_set_password(dce_call,
3891 26 : a_state->sam_ctx,
3892 : a_state->account_dn,
3893 26 : a_state->domain_state->domain_dn,
3894 : mem_ctx,
3895 26 : &r->in.info->info23.password);
3896 12 : } else IFSET(SAMR_FIELD_LM_PASSWORD_PRESENT) {
3897 32 : status = samr_set_password(dce_call,
3898 12 : a_state->sam_ctx,
3899 : a_state->account_dn,
3900 12 : a_state->domain_state->domain_dn,
3901 : mem_ctx,
3902 12 : &r->in.info->info23.password);
3903 : }
3904 38 : if (!NT_STATUS_IS_OK(status)) {
3905 18 : return status;
3906 : }
3907 :
3908 20 : IFSET(SAMR_FIELD_EXPIRED_FLAG) {
3909 2 : const char *t = "0";
3910 : struct ldb_message_element *set_el;
3911 2 : if (r->in.info->info23.info.password_expired
3912 : == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
3913 2 : t = "-1";
3914 : }
3915 2 : if (ldb_msg_add_string(msg, "pwdLastSet", t) != LDB_SUCCESS) {
3916 0 : return NT_STATUS_NO_MEMORY;
3917 : }
3918 2 : set_el = ldb_msg_find_element(msg, "pwdLastSet");
3919 2 : set_el->flags = LDB_FLAG_MOD_REPLACE;
3920 : }
3921 : #undef IFSET
3922 20 : break;
3923 :
3924 : /* the set password levels are handled separately */
3925 291 : case 24:
3926 731 : status = samr_set_password(dce_call,
3927 291 : a_state->sam_ctx,
3928 : a_state->account_dn,
3929 291 : a_state->domain_state->domain_dn,
3930 : mem_ctx,
3931 291 : &r->in.info->info24.password);
3932 291 : if (!NT_STATUS_IS_OK(status)) {
3933 0 : return status;
3934 : }
3935 :
3936 291 : if (r->in.info->info24.password_expired > 0) {
3937 : struct ldb_message_element *set_el;
3938 4 : if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg, "pwdLastSet", 0) != LDB_SUCCESS) {
3939 0 : return NT_STATUS_NO_MEMORY;
3940 : }
3941 4 : set_el = ldb_msg_find_element(msg, "pwdLastSet");
3942 4 : set_el->flags = LDB_FLAG_MOD_REPLACE;
3943 : }
3944 291 : break;
3945 :
3946 568 : case 25:
3947 568 : if (r->in.info->info25.info.fields_present == 0)
3948 0 : return NT_STATUS_INVALID_PARAMETER;
3949 :
3950 : #define IFSET(bit) if (bit & r->in.info->info25.info.fields_present)
3951 568 : IFSET(SAMR_FIELD_LAST_LOGON)
3952 0 : SET_UINT64(msg, info25.info.last_logon, "lastLogon");
3953 568 : IFSET(SAMR_FIELD_LAST_LOGOFF)
3954 0 : SET_UINT64(msg, info25.info.last_logoff, "lastLogoff");
3955 568 : IFSET(SAMR_FIELD_ACCT_EXPIRY)
3956 0 : SET_UINT64(msg, info25.info.acct_expiry, "accountExpires");
3957 568 : IFSET(SAMR_FIELD_ACCOUNT_NAME)
3958 4 : SET_STRING(msg, info25.info.account_name, "samAccountName");
3959 568 : IFSET(SAMR_FIELD_FULL_NAME)
3960 524 : SET_STRING(msg, info25.info.full_name, "displayName");
3961 568 : IFSET(SAMR_FIELD_HOME_DIRECTORY)
3962 0 : SET_STRING(msg, info25.info.home_directory, "homeDirectory");
3963 568 : IFSET(SAMR_FIELD_HOME_DRIVE)
3964 0 : SET_STRING(msg, info25.info.home_drive, "homeDrive");
3965 568 : IFSET(SAMR_FIELD_LOGON_SCRIPT)
3966 0 : SET_STRING(msg, info25.info.logon_script, "scriptPath");
3967 568 : IFSET(SAMR_FIELD_PROFILE_PATH)
3968 0 : SET_STRING(msg, info25.info.profile_path, "profilePath");
3969 568 : IFSET(SAMR_FIELD_DESCRIPTION)
3970 2 : SET_STRING(msg, info25.info.description, "description");
3971 568 : IFSET(SAMR_FIELD_WORKSTATIONS)
3972 0 : SET_STRING(msg, info25.info.workstations, "userWorkstations");
3973 568 : IFSET(SAMR_FIELD_COMMENT)
3974 0 : SET_STRING(msg, info25.info.comment, "comment");
3975 568 : IFSET(SAMR_FIELD_PARAMETERS)
3976 0 : SET_PARAMETERS(msg, info25.info.parameters, "userParameters");
3977 568 : IFSET(SAMR_FIELD_PRIMARY_GID)
3978 0 : SET_UINT(msg, info25.info.primary_gid, "primaryGroupID");
3979 568 : IFSET(SAMR_FIELD_ACCT_FLAGS)
3980 528 : SET_AFLAGS(msg, info25.info.acct_flags, "userAccountControl");
3981 568 : IFSET(SAMR_FIELD_LOGON_HOURS)
3982 0 : SET_LHOURS(msg, info25.info.logon_hours, "logonHours");
3983 568 : IFSET(SAMR_FIELD_BAD_PWD_COUNT)
3984 0 : SET_UINT (msg, info25.info.bad_password_count, "badPwdCount");
3985 568 : IFSET(SAMR_FIELD_NUM_LOGONS)
3986 0 : SET_UINT (msg, info25.info.logon_count, "logonCount");
3987 568 : IFSET(SAMR_FIELD_COUNTRY_CODE)
3988 0 : SET_UINT (msg, info25.info.country_code, "countryCode");
3989 568 : IFSET(SAMR_FIELD_CODE_PAGE)
3990 0 : SET_UINT (msg, info25.info.code_page, "codePage");
3991 :
3992 : /* password change fields */
3993 568 : IFSET(SAMR_FIELD_LAST_PWD_CHANGE)
3994 0 : return NT_STATUS_ACCESS_DENIED;
3995 :
3996 568 : IFSET(SAMR_FIELD_NT_PASSWORD_PRESENT) {
3997 1444 : status = samr_set_password_ex(dce_call,
3998 556 : a_state->sam_ctx,
3999 : a_state->account_dn,
4000 556 : a_state->domain_state->domain_dn,
4001 : mem_ctx,
4002 484 : &r->in.info->info25.password);
4003 12 : } else IFSET(SAMR_FIELD_LM_PASSWORD_PRESENT) {
4004 32 : status = samr_set_password_ex(dce_call,
4005 12 : a_state->sam_ctx,
4006 : a_state->account_dn,
4007 12 : a_state->domain_state->domain_dn,
4008 : mem_ctx,
4009 12 : &r->in.info->info25.password);
4010 : }
4011 568 : if (!NT_STATUS_IS_OK(status)) {
4012 18 : return status;
4013 : }
4014 :
4015 550 : IFSET(SAMR_FIELD_EXPIRED_FLAG) {
4016 0 : const char *t = "0";
4017 : struct ldb_message_element *set_el;
4018 0 : if (r->in.info->info25.info.password_expired
4019 : == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
4020 0 : t = "-1";
4021 : }
4022 0 : if (ldb_msg_add_string(msg, "pwdLastSet", t) != LDB_SUCCESS) {
4023 0 : return NT_STATUS_NO_MEMORY;
4024 : }
4025 0 : set_el = ldb_msg_find_element(msg, "pwdLastSet");
4026 0 : set_el->flags = LDB_FLAG_MOD_REPLACE;
4027 : }
4028 : #undef IFSET
4029 478 : break;
4030 :
4031 : /* the set password levels are handled separately */
4032 71 : case 26:
4033 193 : status = samr_set_password_ex(dce_call,
4034 71 : a_state->sam_ctx,
4035 : a_state->account_dn,
4036 71 : a_state->domain_state->domain_dn,
4037 : mem_ctx,
4038 71 : &r->in.info->info26.password);
4039 71 : if (!NT_STATUS_IS_OK(status)) {
4040 11 : return status;
4041 : }
4042 :
4043 60 : if (r->in.info->info26.password_expired > 0) {
4044 24 : const char *t = "0";
4045 : struct ldb_message_element *set_el;
4046 24 : if (r->in.info->info26.password_expired
4047 : == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
4048 0 : t = "-1";
4049 : }
4050 24 : if (ldb_msg_add_string(msg, "pwdLastSet", t) != LDB_SUCCESS) {
4051 0 : return NT_STATUS_NO_MEMORY;
4052 : }
4053 24 : set_el = ldb_msg_find_element(msg, "pwdLastSet");
4054 24 : set_el->flags = LDB_FLAG_MOD_REPLACE;
4055 : }
4056 60 : break;
4057 :
4058 0 : default:
4059 : /* many info classes are not valid for SetUserInfo */
4060 0 : return NT_STATUS_INVALID_INFO_CLASS;
4061 : }
4062 :
4063 4336 : if (!NT_STATUS_IS_OK(status)) {
4064 0 : return status;
4065 : }
4066 :
4067 : /* modify the samdb record */
4068 4336 : if (msg->num_elements > 0) {
4069 3751 : ret = ldb_modify(a_state->sam_ctx, msg);
4070 3751 : if (ret != LDB_SUCCESS) {
4071 0 : DEBUG(1,("Failed to modify record %s: %s\n",
4072 : ldb_dn_get_linearized(a_state->account_dn),
4073 : ldb_errstring(a_state->sam_ctx)));
4074 :
4075 0 : return dsdb_ldb_err_to_ntstatus(ret);
4076 : }
4077 : }
4078 :
4079 4336 : return NT_STATUS_OK;
4080 : }
4081 :
4082 :
4083 : /*
4084 : samr_GetGroupsForUser
4085 : */
4086 800 : static NTSTATUS dcesrv_samr_GetGroupsForUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4087 : struct samr_GetGroupsForUser *r)
4088 : {
4089 : struct dcesrv_handle *h;
4090 : struct samr_account_state *a_state;
4091 : struct samr_domain_state *d_state;
4092 : struct ldb_result *res, *res_memberof;
4093 800 : const char * const attrs[] = { "primaryGroupID",
4094 : "memberOf",
4095 : NULL };
4096 800 : const char * const group_attrs[] = { "objectSid",
4097 : NULL };
4098 :
4099 : struct samr_RidWithAttributeArray *array;
4100 : struct ldb_message_element *memberof_el;
4101 800 : int i, ret, count = 0;
4102 : uint32_t primary_group_id;
4103 : char *filter;
4104 :
4105 800 : DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
4106 :
4107 800 : a_state = h->data;
4108 800 : d_state = a_state->domain_state;
4109 :
4110 800 : ret = dsdb_search_dn(a_state->sam_ctx, mem_ctx,
4111 : &res,
4112 : a_state->account_dn,
4113 : attrs, DSDB_SEARCH_SHOW_EXTENDED_DN);
4114 :
4115 800 : if (ret == LDB_ERR_NO_SUCH_OBJECT) {
4116 0 : return NT_STATUS_NO_SUCH_USER;
4117 800 : } else if (ret != LDB_SUCCESS) {
4118 0 : return NT_STATUS_INTERNAL_DB_CORRUPTION;
4119 800 : } else if (res->count != 1) {
4120 0 : return NT_STATUS_NO_SUCH_USER;
4121 : }
4122 :
4123 800 : primary_group_id = ldb_msg_find_attr_as_uint(res->msgs[0], "primaryGroupID",
4124 : 0);
4125 :
4126 800 : filter = talloc_asprintf(mem_ctx,
4127 : "(&(|(grouptype=%d)(grouptype=%d))"
4128 : "(objectclass=group)(|",
4129 : GTYPE_SECURITY_UNIVERSAL_GROUP,
4130 : GTYPE_SECURITY_GLOBAL_GROUP);
4131 800 : if (filter == NULL) {
4132 0 : return NT_STATUS_NO_MEMORY;
4133 : }
4134 :
4135 800 : memberof_el = ldb_msg_find_element(res->msgs[0], "memberOf");
4136 800 : if (memberof_el != NULL) {
4137 236 : for (i = 0; i < memberof_el->num_values; i++) {
4138 : const struct ldb_val *memberof_sid_binary;
4139 : char *memberof_sid_escaped;
4140 137 : struct ldb_dn *memberof_dn
4141 22 : = ldb_dn_from_ldb_val(mem_ctx,
4142 159 : a_state->sam_ctx,
4143 159 : &memberof_el->values[i]);
4144 159 : if (memberof_dn == NULL) {
4145 0 : return NT_STATUS_INTERNAL_DB_CORRUPTION;
4146 : }
4147 :
4148 : memberof_sid_binary
4149 159 : = ldb_dn_get_extended_component(memberof_dn,
4150 : "SID");
4151 159 : if (memberof_sid_binary == NULL) {
4152 0 : return NT_STATUS_INTERNAL_DB_CORRUPTION;
4153 : }
4154 :
4155 159 : memberof_sid_escaped = ldb_binary_encode(mem_ctx,
4156 : *memberof_sid_binary);
4157 159 : if (memberof_sid_escaped == NULL) {
4158 0 : return NT_STATUS_NO_MEMORY;
4159 : }
4160 159 : filter = talloc_asprintf_append(filter, "(objectSID=%s)",
4161 : memberof_sid_escaped);
4162 159 : if (filter == NULL) {
4163 0 : return NT_STATUS_NO_MEMORY;
4164 : }
4165 : }
4166 :
4167 77 : ret = dsdb_search(a_state->sam_ctx, mem_ctx,
4168 : &res_memberof,
4169 : d_state->domain_dn,
4170 : LDB_SCOPE_SUBTREE,
4171 : group_attrs, 0,
4172 : "%s))", filter);
4173 :
4174 77 : if (ret != LDB_SUCCESS) {
4175 0 : return NT_STATUS_INTERNAL_DB_CORRUPTION;
4176 : }
4177 77 : count = res_memberof->count;
4178 : }
4179 :
4180 800 : array = talloc(mem_ctx, struct samr_RidWithAttributeArray);
4181 800 : if (array == NULL)
4182 0 : return NT_STATUS_NO_MEMORY;
4183 :
4184 800 : array->count = 0;
4185 800 : array->rids = NULL;
4186 :
4187 800 : array->rids = talloc_array(mem_ctx, struct samr_RidWithAttribute,
4188 : count + 1);
4189 800 : if (array->rids == NULL)
4190 0 : return NT_STATUS_NO_MEMORY;
4191 :
4192 : /* Adds the primary group */
4193 :
4194 800 : array->rids[0].rid = primary_group_id;
4195 800 : array->rids[0].attributes = SE_GROUP_MANDATORY
4196 : | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED;
4197 800 : array->count += 1;
4198 :
4199 : /* Adds the additional groups */
4200 894 : for (i = 0; i < count; i++) {
4201 : struct dom_sid *group_sid;
4202 :
4203 94 : group_sid = samdb_result_dom_sid(mem_ctx,
4204 94 : res_memberof->msgs[i],
4205 : "objectSid");
4206 94 : if (group_sid == NULL) {
4207 0 : return NT_STATUS_INTERNAL_DB_CORRUPTION;
4208 : }
4209 :
4210 177 : array->rids[i + 1].rid =
4211 177 : group_sid->sub_auths[group_sid->num_auths-1];
4212 94 : array->rids[i + 1].attributes = SE_GROUP_MANDATORY
4213 : | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED;
4214 94 : array->count += 1;
4215 : }
4216 :
4217 800 : *r->out.rids = array;
4218 :
4219 800 : return NT_STATUS_OK;
4220 : }
4221 :
4222 : /*
4223 : * samr_QueryDisplayInfo
4224 : *
4225 : * A cache of the GUID's matching the last query is maintained
4226 : * in the SAMR_QUERY_DISPLAY_INFO_CACHE guid_cache maintained o
4227 : * n the dcesrv_handle.
4228 : */
4229 579 : static NTSTATUS dcesrv_samr_QueryDisplayInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4230 : struct samr_QueryDisplayInfo *r)
4231 : {
4232 : struct dcesrv_handle *h;
4233 : struct samr_domain_state *d_state;
4234 : struct ldb_result *res;
4235 : uint32_t i;
4236 579 : uint32_t results = 0;
4237 579 : uint32_t count = 0;
4238 579 : const char *const cache_attrs[] = {"objectGUID", NULL};
4239 579 : const char *const attrs[] = {
4240 : "objectSID", "sAMAccountName", "displayName", "description", NULL};
4241 579 : struct samr_DispEntryFull *entriesFull = NULL;
4242 579 : struct samr_DispEntryFullGroup *entriesFullGroup = NULL;
4243 579 : struct samr_DispEntryAscii *entriesAscii = NULL;
4244 579 : struct samr_DispEntryGeneral *entriesGeneral = NULL;
4245 : const char *filter;
4246 : int ret;
4247 : NTSTATUS status;
4248 579 : struct samr_guid_cache *cache = NULL;
4249 :
4250 579 : DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
4251 :
4252 579 : d_state = h->data;
4253 :
4254 579 : cache = &d_state->guid_caches[SAMR_QUERY_DISPLAY_INFO_CACHE];
4255 : /*
4256 : * Can the cached results be used?
4257 : * The cache is discarded if the start index is zero, or the requested
4258 : * level is different from that in the cache.
4259 : */
4260 579 : if ((r->in.start_idx == 0) || (r->in.level != cache->handle)) {
4261 : /*
4262 : * The cached results can not be used, so will need to query
4263 : * the database.
4264 : */
4265 :
4266 : /*
4267 : * Get the search filter for the current level
4268 : */
4269 219 : switch (r->in.level) {
4270 93 : case 1:
4271 : case 4:
4272 93 : filter = talloc_asprintf(mem_ctx,
4273 : "(&(objectclass=user)"
4274 : "(sAMAccountType=%d))",
4275 : ATYPE_NORMAL_ACCOUNT);
4276 93 : break;
4277 34 : case 2:
4278 34 : filter = talloc_asprintf(mem_ctx,
4279 : "(&(objectclass=user)"
4280 : "(sAMAccountType=%d))",
4281 : ATYPE_WORKSTATION_TRUST);
4282 34 : break;
4283 92 : case 3:
4284 : case 5:
4285 62 : filter =
4286 30 : talloc_asprintf(mem_ctx,
4287 : "(&(|(groupType=%d)(groupType=%d))"
4288 : "(objectClass=group))",
4289 : GTYPE_SECURITY_UNIVERSAL_GROUP,
4290 : GTYPE_SECURITY_GLOBAL_GROUP);
4291 92 : break;
4292 0 : default:
4293 0 : return NT_STATUS_INVALID_INFO_CLASS;
4294 : }
4295 219 : clear_guid_cache(cache);
4296 :
4297 : /*
4298 : * search for all requested objects in all domains.
4299 : */
4300 219 : ret = dsdb_search(d_state->sam_ctx,
4301 : mem_ctx,
4302 : &res,
4303 219 : ldb_get_default_basedn(d_state->sam_ctx),
4304 : LDB_SCOPE_SUBTREE,
4305 : cache_attrs,
4306 : 0,
4307 : "%s",
4308 : filter);
4309 219 : if (ret != LDB_SUCCESS) {
4310 0 : return NT_STATUS_INTERNAL_DB_CORRUPTION;
4311 : }
4312 219 : if ((res->count == 0) || (r->in.max_entries == 0)) {
4313 0 : return NT_STATUS_OK;
4314 : }
4315 :
4316 219 : status = load_guid_cache(cache, d_state, res->count, res->msgs);
4317 219 : TALLOC_FREE(res);
4318 219 : if (!NT_STATUS_IS_OK(status)) {
4319 0 : return status;
4320 : }
4321 219 : cache->handle = r->in.level;
4322 : }
4323 579 : *r->out.total_size = cache->size;
4324 :
4325 : /*
4326 : * if there are no entries or the requested start index is greater
4327 : * than the number of entries, we return an empty response.
4328 : */
4329 579 : if (r->in.start_idx >= cache->size) {
4330 15 : *r->out.returned_size = 0;
4331 15 : switch(r->in.level) {
4332 11 : case 1:
4333 11 : r->out.info->info1.count = *r->out.returned_size;
4334 11 : r->out.info->info1.entries = NULL;
4335 11 : break;
4336 1 : case 2:
4337 1 : r->out.info->info2.count = *r->out.returned_size;
4338 1 : r->out.info->info2.entries = NULL;
4339 1 : break;
4340 1 : case 3:
4341 1 : r->out.info->info3.count = *r->out.returned_size;
4342 1 : r->out.info->info3.entries = NULL;
4343 1 : break;
4344 1 : case 4:
4345 1 : r->out.info->info4.count = *r->out.returned_size;
4346 1 : r->out.info->info4.entries = NULL;
4347 1 : break;
4348 1 : case 5:
4349 1 : r->out.info->info5.count = *r->out.returned_size;
4350 1 : r->out.info->info5.entries = NULL;
4351 1 : break;
4352 : }
4353 15 : return NT_STATUS_OK;
4354 : }
4355 :
4356 : /*
4357 : * Allocate an array of the appropriate result structures for the
4358 : * current query level.
4359 : *
4360 : * r->in.start_idx is always < cache->size due to the check above
4361 : */
4362 564 : results = MIN((cache->size - r->in.start_idx), r->in.max_entries);
4363 564 : switch (r->in.level) {
4364 218 : case 1:
4365 218 : entriesGeneral = talloc_array(
4366 : mem_ctx, struct samr_DispEntryGeneral, results);
4367 218 : break;
4368 38 : case 2:
4369 26 : entriesFull =
4370 12 : talloc_array(mem_ctx, struct samr_DispEntryFull, results);
4371 38 : break;
4372 102 : case 3:
4373 102 : entriesFullGroup = talloc_array(
4374 : mem_ctx, struct samr_DispEntryFullGroup, results);
4375 102 : break;
4376 206 : case 4:
4377 : case 5:
4378 130 : entriesAscii =
4379 76 : talloc_array(mem_ctx, struct samr_DispEntryAscii, results);
4380 206 : break;
4381 : }
4382 :
4383 564 : if ((entriesGeneral == NULL) && (entriesFull == NULL) &&
4384 102 : (entriesAscii == NULL) && (entriesFullGroup == NULL))
4385 0 : return NT_STATUS_NO_MEMORY;
4386 :
4387 : /*
4388 : * Process the list of result GUID's.
4389 : * Read the details of each object and populate the result structure
4390 : * for the current level.
4391 : */
4392 564 : count = 0;
4393 5400 : for (i = 0; i < results; i++) {
4394 : struct dom_sid *objectsid;
4395 : struct ldb_result *rec;
4396 4836 : const uint32_t idx = r->in.start_idx + i;
4397 : uint32_t rid;
4398 :
4399 : /*
4400 : * Read an object from disk using the GUID as the key
4401 : *
4402 : * If the object can not be read, or it does not have a SID
4403 : * it is ignored. In this case the number of entries returned
4404 : * will be less than the requested size, there will also be
4405 : * a gap in the idx numbers in the returned elements e.g. if
4406 : * there are 3 GUIDs a, b, c in the cache and b is deleted from
4407 : * disk then details for a, and c will be returned with
4408 : * idx values of 1 and 3 respectively.
4409 : *
4410 : */
4411 4836 : ret = dsdb_search_by_dn_guid(d_state->sam_ctx,
4412 : mem_ctx,
4413 : &rec,
4414 4836 : &cache->entries[idx],
4415 : attrs,
4416 : 0);
4417 4836 : if (ret == LDB_ERR_NO_SUCH_OBJECT) {
4418 : struct GUID_txt_buf guid_buf;
4419 18 : char *guid_str =
4420 18 : GUID_buf_string(&cache->entries[idx],
4421 : &guid_buf);
4422 18 : DBG_WARNING("GUID [%s] not found\n", guid_str);
4423 18 : continue;
4424 4818 : } else if (ret != LDB_SUCCESS) {
4425 0 : clear_guid_cache(cache);
4426 0 : return NT_STATUS_INTERNAL_DB_CORRUPTION;
4427 : }
4428 4818 : objectsid = samdb_result_dom_sid(mem_ctx,
4429 4818 : rec->msgs[0],
4430 : "objectSID");
4431 4818 : if (objectsid == NULL) {
4432 : struct GUID_txt_buf guid_buf;
4433 0 : DBG_WARNING(
4434 : "objectSID for GUID [%s] not found\n",
4435 : GUID_buf_string(&cache->entries[idx], &guid_buf));
4436 0 : continue;
4437 : }
4438 4818 : status = dom_sid_split_rid(NULL,
4439 : objectsid,
4440 : NULL,
4441 : &rid);
4442 4818 : if (!NT_STATUS_IS_OK(status)) {
4443 : struct dom_sid_buf sid_buf;
4444 : struct GUID_txt_buf guid_buf;
4445 0 : DBG_WARNING(
4446 : "objectSID [%s] for GUID [%s] invalid\n",
4447 : dom_sid_str_buf(objectsid, &sid_buf),
4448 : GUID_buf_string(&cache->entries[idx], &guid_buf));
4449 0 : continue;
4450 : }
4451 :
4452 : /*
4453 : * Populate the result structure for the current object
4454 : */
4455 4818 : switch(r->in.level) {
4456 2085 : case 1:
4457 :
4458 2085 : entriesGeneral[count].idx = idx + 1;
4459 2085 : entriesGeneral[count].rid = rid;
4460 :
4461 4170 : entriesGeneral[count].acct_flags =
4462 3503 : samdb_result_acct_flags(rec->msgs[0], NULL);
4463 4170 : entriesGeneral[count].account_name.string =
4464 3503 : ldb_msg_find_attr_as_string(
4465 2085 : rec->msgs[0], "sAMAccountName", "");
4466 4170 : entriesGeneral[count].full_name.string =
4467 3503 : ldb_msg_find_attr_as_string(
4468 2085 : rec->msgs[0], "displayName", "");
4469 4170 : entriesGeneral[count].description.string =
4470 3503 : ldb_msg_find_attr_as_string(
4471 2085 : rec->msgs[0], "description", "");
4472 2085 : break;
4473 64 : case 2:
4474 64 : entriesFull[count].idx = idx + 1;
4475 64 : entriesFull[count].rid = rid;
4476 :
4477 : /*
4478 : * No idea why we need to or in ACB_NORMAL here,
4479 : * but this is what Win2k3 seems to do...
4480 : */
4481 116 : entriesFull[count].acct_flags =
4482 116 : samdb_result_acct_flags(rec->msgs[0], NULL) |
4483 : ACB_NORMAL;
4484 128 : entriesFull[count].account_name.string =
4485 116 : ldb_msg_find_attr_as_string(
4486 64 : rec->msgs[0], "sAMAccountName", "");
4487 128 : entriesFull[count].description.string =
4488 116 : ldb_msg_find_attr_as_string(
4489 64 : rec->msgs[0], "description", "");
4490 64 : break;
4491 1668 : case 3:
4492 1668 : entriesFullGroup[count].idx = idx + 1;
4493 1668 : entriesFullGroup[count].rid = rid;
4494 :
4495 : /*
4496 : * We get a "7" here for groups
4497 : */
4498 1668 : entriesFullGroup[count].acct_flags =
4499 : SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT |
4500 : SE_GROUP_ENABLED;
4501 3336 : entriesFullGroup[count].account_name.string =
4502 2870 : ldb_msg_find_attr_as_string(
4503 1668 : rec->msgs[0], "sAMAccountName", "");
4504 3336 : entriesFullGroup[count].description.string =
4505 2870 : ldb_msg_find_attr_as_string(
4506 1668 : rec->msgs[0], "description", "");
4507 1668 : break;
4508 1001 : case 4:
4509 : case 5:
4510 1001 : entriesAscii[count].idx = idx + 1;
4511 2002 : entriesAscii[count].account_name.string =
4512 1674 : ldb_msg_find_attr_as_string(
4513 1001 : rec->msgs[0], "sAMAccountName", "");
4514 1001 : break;
4515 : }
4516 4818 : count++;
4517 : }
4518 :
4519 : /*
4520 : * Build the response based on the request level.
4521 : */
4522 564 : *r->out.returned_size = count;
4523 564 : switch(r->in.level) {
4524 218 : case 1:
4525 218 : r->out.info->info1.count = count;
4526 218 : r->out.info->info1.entries = entriesGeneral;
4527 218 : break;
4528 38 : case 2:
4529 38 : r->out.info->info2.count = count;
4530 38 : r->out.info->info2.entries = entriesFull;
4531 38 : break;
4532 102 : case 3:
4533 102 : r->out.info->info3.count = count;
4534 102 : r->out.info->info3.entries = entriesFullGroup;
4535 102 : break;
4536 88 : case 4:
4537 88 : r->out.info->info4.count = count;
4538 88 : r->out.info->info4.entries = entriesAscii;
4539 88 : break;
4540 118 : case 5:
4541 118 : r->out.info->info5.count = count;
4542 118 : r->out.info->info5.entries = entriesAscii;
4543 118 : break;
4544 : }
4545 :
4546 564 : return ((r->in.start_idx + results) < cache->size)
4547 : ? STATUS_MORE_ENTRIES
4548 564 : : NT_STATUS_OK;
4549 : }
4550 :
4551 :
4552 : /*
4553 : samr_GetDisplayEnumerationIndex
4554 : */
4555 4 : static NTSTATUS dcesrv_samr_GetDisplayEnumerationIndex(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4556 : struct samr_GetDisplayEnumerationIndex *r)
4557 : {
4558 4 : DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4559 : }
4560 :
4561 :
4562 : /*
4563 : samr_TestPrivateFunctionsDomain
4564 : */
4565 10 : static NTSTATUS dcesrv_samr_TestPrivateFunctionsDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4566 : struct samr_TestPrivateFunctionsDomain *r)
4567 : {
4568 10 : return NT_STATUS_NOT_IMPLEMENTED;
4569 : }
4570 :
4571 :
4572 : /*
4573 : samr_TestPrivateFunctionsUser
4574 : */
4575 14 : static NTSTATUS dcesrv_samr_TestPrivateFunctionsUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4576 : struct samr_TestPrivateFunctionsUser *r)
4577 : {
4578 14 : return NT_STATUS_NOT_IMPLEMENTED;
4579 : }
4580 :
4581 :
4582 : /*
4583 : samr_GetUserPwInfo
4584 : */
4585 1349 : static NTSTATUS dcesrv_samr_GetUserPwInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4586 : struct samr_GetUserPwInfo *r)
4587 : {
4588 : struct dcesrv_handle *h;
4589 : struct samr_account_state *a_state;
4590 :
4591 1349 : ZERO_STRUCTP(r->out.info);
4592 :
4593 1349 : DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
4594 :
4595 1349 : a_state = h->data;
4596 :
4597 1614 : r->out.info->min_password_length = samdb_search_uint(a_state->sam_ctx,
4598 1349 : mem_ctx, 0, a_state->domain_state->domain_dn, "minPwdLength",
4599 : NULL);
4600 1349 : r->out.info->password_properties = samdb_search_uint(a_state->sam_ctx,
4601 : mem_ctx, 0, a_state->account_dn, "pwdProperties", NULL);
4602 :
4603 1349 : return NT_STATUS_OK;
4604 : }
4605 :
4606 :
4607 : /*
4608 : samr_RemoveMemberFromForeignDomain
4609 : */
4610 14 : static NTSTATUS dcesrv_samr_RemoveMemberFromForeignDomain(struct dcesrv_call_state *dce_call,
4611 : TALLOC_CTX *mem_ctx,
4612 : struct samr_RemoveMemberFromForeignDomain *r)
4613 : {
4614 : struct dcesrv_handle *h;
4615 : struct samr_domain_state *d_state;
4616 : const char *memberdn;
4617 : struct ldb_message **res;
4618 14 : const char *no_attrs[] = { NULL };
4619 : int i, count;
4620 :
4621 14 : DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
4622 :
4623 14 : d_state = h->data;
4624 :
4625 14 : memberdn = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL,
4626 : "distinguishedName", "(objectSid=%s)",
4627 14 : ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
4628 : /* Nothing to do */
4629 14 : if (memberdn == NULL) {
4630 10 : return NT_STATUS_OK;
4631 : }
4632 :
4633 4 : count = samdb_search_domain(d_state->sam_ctx, mem_ctx,
4634 : d_state->domain_dn, &res, no_attrs,
4635 4 : d_state->domain_sid,
4636 : "(&(member=%s)(objectClass=group)"
4637 : "(|(groupType=%d)(groupType=%d)))",
4638 : memberdn,
4639 : GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
4640 : GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
4641 :
4642 4 : if (count < 0)
4643 0 : return NT_STATUS_INTERNAL_DB_CORRUPTION;
4644 :
4645 4 : for (i=0; i<count; i++) {
4646 : struct ldb_message *mod;
4647 : int ret;
4648 :
4649 0 : mod = ldb_msg_new(mem_ctx);
4650 0 : if (mod == NULL) {
4651 0 : return NT_STATUS_NO_MEMORY;
4652 : }
4653 :
4654 0 : mod->dn = res[i]->dn;
4655 :
4656 0 : if (samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod,
4657 : "member", memberdn) != LDB_SUCCESS)
4658 0 : return NT_STATUS_NO_MEMORY;
4659 :
4660 0 : ret = ldb_modify(d_state->sam_ctx, mod);
4661 0 : talloc_free(mod);
4662 0 : if (ret != LDB_SUCCESS) {
4663 0 : return dsdb_ldb_err_to_ntstatus(ret);
4664 : }
4665 : }
4666 :
4667 4 : return NT_STATUS_OK;
4668 : }
4669 :
4670 :
4671 : /*
4672 : samr_QueryDomainInfo2
4673 :
4674 : just an alias for samr_QueryDomainInfo
4675 : */
4676 171 : static NTSTATUS dcesrv_samr_QueryDomainInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4677 : struct samr_QueryDomainInfo2 *r)
4678 : {
4679 : struct samr_QueryDomainInfo r1;
4680 : NTSTATUS status;
4681 :
4682 171 : r1 = (struct samr_QueryDomainInfo) {
4683 171 : .in.domain_handle = r->in.domain_handle,
4684 171 : .in.level = r->in.level,
4685 171 : .out.info = r->out.info,
4686 : };
4687 :
4688 171 : status = dcesrv_samr_QueryDomainInfo(dce_call, mem_ctx, &r1);
4689 :
4690 171 : return status;
4691 : }
4692 :
4693 :
4694 : /*
4695 : samr_QueryUserInfo2
4696 :
4697 : just an alias for samr_QueryUserInfo
4698 : */
4699 262 : static NTSTATUS dcesrv_samr_QueryUserInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4700 : struct samr_QueryUserInfo2 *r)
4701 : {
4702 : struct samr_QueryUserInfo r1;
4703 : NTSTATUS status;
4704 :
4705 262 : r1 = (struct samr_QueryUserInfo) {
4706 262 : .in.user_handle = r->in.user_handle,
4707 262 : .in.level = r->in.level,
4708 262 : .out.info = r->out.info
4709 : };
4710 :
4711 262 : status = dcesrv_samr_QueryUserInfo(dce_call, mem_ctx, &r1);
4712 :
4713 262 : return status;
4714 : }
4715 :
4716 :
4717 : /*
4718 : samr_QueryDisplayInfo2
4719 : */
4720 54 : static NTSTATUS dcesrv_samr_QueryDisplayInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4721 : struct samr_QueryDisplayInfo2 *r)
4722 : {
4723 : struct samr_QueryDisplayInfo q;
4724 : NTSTATUS result;
4725 :
4726 54 : q = (struct samr_QueryDisplayInfo) {
4727 54 : .in.domain_handle = r->in.domain_handle,
4728 54 : .in.level = r->in.level,
4729 54 : .in.start_idx = r->in.start_idx,
4730 54 : .in.max_entries = r->in.max_entries,
4731 54 : .in.buf_size = r->in.buf_size,
4732 54 : .out.total_size = r->out.total_size,
4733 54 : .out.returned_size = r->out.returned_size,
4734 54 : .out.info = r->out.info,
4735 : };
4736 :
4737 54 : result = dcesrv_samr_QueryDisplayInfo(dce_call, mem_ctx, &q);
4738 :
4739 54 : return result;
4740 : }
4741 :
4742 :
4743 : /*
4744 : samr_GetDisplayEnumerationIndex2
4745 : */
4746 4 : static NTSTATUS dcesrv_samr_GetDisplayEnumerationIndex2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4747 : struct samr_GetDisplayEnumerationIndex2 *r)
4748 : {
4749 4 : DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4750 : }
4751 :
4752 :
4753 : /*
4754 : samr_QueryDisplayInfo3
4755 : */
4756 50 : static NTSTATUS dcesrv_samr_QueryDisplayInfo3(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4757 : struct samr_QueryDisplayInfo3 *r)
4758 : {
4759 : struct samr_QueryDisplayInfo q;
4760 : NTSTATUS result;
4761 :
4762 50 : q = (struct samr_QueryDisplayInfo) {
4763 50 : .in.domain_handle = r->in.domain_handle,
4764 50 : .in.level = r->in.level,
4765 50 : .in.start_idx = r->in.start_idx,
4766 50 : .in.max_entries = r->in.max_entries,
4767 50 : .in.buf_size = r->in.buf_size,
4768 50 : .out.total_size = r->out.total_size,
4769 50 : .out.returned_size = r->out.returned_size,
4770 50 : .out.info = r->out.info,
4771 : };
4772 :
4773 50 : result = dcesrv_samr_QueryDisplayInfo(dce_call, mem_ctx, &q);
4774 :
4775 50 : return result;
4776 : }
4777 :
4778 :
4779 : /*
4780 : samr_AddMultipleMembersToAlias
4781 : */
4782 0 : static NTSTATUS dcesrv_samr_AddMultipleMembersToAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4783 : struct samr_AddMultipleMembersToAlias *r)
4784 : {
4785 0 : DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4786 : }
4787 :
4788 :
4789 : /*
4790 : samr_RemoveMultipleMembersFromAlias
4791 : */
4792 0 : static NTSTATUS dcesrv_samr_RemoveMultipleMembersFromAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4793 : struct samr_RemoveMultipleMembersFromAlias *r)
4794 : {
4795 0 : DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4796 : }
4797 :
4798 :
4799 : /*
4800 : samr_GetDomPwInfo
4801 :
4802 : this fetches the default password properties for a domain
4803 :
4804 : note that w2k3 completely ignores the domain name in this call, and
4805 : always returns the information for the servers primary domain
4806 : */
4807 2965 : static NTSTATUS dcesrv_samr_GetDomPwInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4808 : struct samr_GetDomPwInfo *r)
4809 : {
4810 2392 : struct auth_session_info *session_info =
4811 573 : dcesrv_call_session_info(dce_call);
4812 : struct ldb_message **msgs;
4813 : int ret;
4814 2965 : const char * const attrs[] = {"minPwdLength", "pwdProperties", NULL };
4815 : struct ldb_context *sam_ctx;
4816 :
4817 2965 : ZERO_STRUCTP(r->out.info);
4818 :
4819 5357 : sam_ctx = samdb_connect(mem_ctx,
4820 : dce_call->event_ctx,
4821 2965 : dce_call->conn->dce_ctx->lp_ctx,
4822 : session_info,
4823 2965 : dce_call->conn->remote_address,
4824 : 0);
4825 2965 : if (sam_ctx == NULL) {
4826 0 : return NT_STATUS_INVALID_SYSTEM_SERVICE;
4827 : }
4828 :
4829 : /* The domain name in this call is ignored */
4830 2965 : ret = gendb_search_dn(sam_ctx,
4831 : mem_ctx, NULL, &msgs, attrs);
4832 2965 : if (ret <= 0) {
4833 0 : talloc_free(sam_ctx);
4834 :
4835 0 : return NT_STATUS_NO_SUCH_DOMAIN;
4836 : }
4837 2965 : if (ret > 1) {
4838 0 : talloc_free(msgs);
4839 0 : talloc_free(sam_ctx);
4840 :
4841 0 : return NT_STATUS_INTERNAL_DB_CORRUPTION;
4842 : }
4843 :
4844 2965 : r->out.info->min_password_length = ldb_msg_find_attr_as_uint(msgs[0],
4845 : "minPwdLength", 0);
4846 2965 : r->out.info->password_properties = ldb_msg_find_attr_as_uint(msgs[0],
4847 : "pwdProperties", 1);
4848 :
4849 2965 : talloc_free(msgs);
4850 2965 : talloc_unlink(mem_ctx, sam_ctx);
4851 :
4852 2965 : return NT_STATUS_OK;
4853 : }
4854 :
4855 :
4856 : /*
4857 : samr_Connect2
4858 : */
4859 533 : static NTSTATUS dcesrv_samr_Connect2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4860 : struct samr_Connect2 *r)
4861 : {
4862 : struct samr_Connect c;
4863 :
4864 535 : c = (struct samr_Connect) {
4865 : .in.system_name = NULL,
4866 533 : .in.access_mask = r->in.access_mask,
4867 533 : .out.connect_handle = r->out.connect_handle,
4868 : };
4869 :
4870 535 : return dcesrv_samr_Connect(dce_call, mem_ctx, &c);
4871 : }
4872 :
4873 :
4874 : /*
4875 : samr_SetUserInfo2
4876 :
4877 : just an alias for samr_SetUserInfo
4878 : */
4879 1657 : static NTSTATUS dcesrv_samr_SetUserInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4880 : struct samr_SetUserInfo2 *r)
4881 : {
4882 : struct samr_SetUserInfo r2;
4883 :
4884 1729 : r2 = (struct samr_SetUserInfo) {
4885 1729 : .in.user_handle = r->in.user_handle,
4886 1729 : .in.level = r->in.level,
4887 1729 : .in.info = r->in.info,
4888 : };
4889 :
4890 1729 : return dcesrv_samr_SetUserInfo(dce_call, mem_ctx, &r2);
4891 : }
4892 :
4893 :
4894 : /*
4895 : samr_SetBootKeyInformation
4896 : */
4897 0 : static NTSTATUS dcesrv_samr_SetBootKeyInformation(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4898 : struct samr_SetBootKeyInformation *r)
4899 : {
4900 0 : DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4901 : }
4902 :
4903 :
4904 : /*
4905 : samr_GetBootKeyInformation
4906 : */
4907 10 : static NTSTATUS dcesrv_samr_GetBootKeyInformation(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4908 : struct samr_GetBootKeyInformation *r)
4909 : {
4910 : /* Windows Server 2008 returns this */
4911 10 : return NT_STATUS_NOT_SUPPORTED;
4912 : }
4913 :
4914 :
4915 : /*
4916 : samr_Connect3
4917 : */
4918 114 : static NTSTATUS dcesrv_samr_Connect3(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4919 : struct samr_Connect3 *r)
4920 : {
4921 : struct samr_Connect c;
4922 :
4923 114 : c = (struct samr_Connect) {
4924 : .in.system_name = NULL,
4925 114 : .in.access_mask = r->in.access_mask,
4926 114 : .out.connect_handle = r->out.connect_handle,
4927 : };
4928 :
4929 114 : return dcesrv_samr_Connect(dce_call, mem_ctx, &c);
4930 : }
4931 :
4932 :
4933 : /*
4934 : samr_Connect4
4935 : */
4936 114 : static NTSTATUS dcesrv_samr_Connect4(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4937 : struct samr_Connect4 *r)
4938 : {
4939 : struct samr_Connect c;
4940 :
4941 114 : c = (struct samr_Connect) {
4942 : .in.system_name = NULL,
4943 114 : .in.access_mask = r->in.access_mask,
4944 114 : .out.connect_handle = r->out.connect_handle,
4945 : };
4946 :
4947 114 : return dcesrv_samr_Connect(dce_call, mem_ctx, &c);
4948 : }
4949 :
4950 :
4951 : /*
4952 : samr_Connect5
4953 : */
4954 208 : static NTSTATUS dcesrv_samr_Connect5(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4955 : struct samr_Connect5 *r)
4956 : {
4957 : struct samr_Connect c;
4958 : NTSTATUS status;
4959 :
4960 208 : c = (struct samr_Connect) {
4961 : .in.system_name = NULL,
4962 208 : .in.access_mask = r->in.access_mask,
4963 208 : .out.connect_handle = r->out.connect_handle,
4964 : };
4965 :
4966 208 : status = dcesrv_samr_Connect(dce_call, mem_ctx, &c);
4967 :
4968 208 : r->out.info_out->info1.client_version = SAMR_CONNECT_AFTER_W2K;
4969 208 : r->out.info_out->info1.unknown2 = 0;
4970 208 : *r->out.level_out = r->in.level_in;
4971 :
4972 208 : return status;
4973 : }
4974 :
4975 :
4976 : /*
4977 : samr_RidToSid
4978 : */
4979 40 : static NTSTATUS dcesrv_samr_RidToSid(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4980 : struct samr_RidToSid *r)
4981 : {
4982 : struct samr_domain_state *d_state;
4983 : struct dcesrv_handle *h;
4984 :
4985 40 : DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
4986 :
4987 40 : d_state = h->data;
4988 :
4989 : /* form the users SID */
4990 40 : *r->out.sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
4991 40 : if (!*r->out.sid) {
4992 0 : return NT_STATUS_NO_MEMORY;
4993 : }
4994 :
4995 40 : return NT_STATUS_OK;
4996 : }
4997 :
4998 :
4999 : /*
5000 : samr_SetDsrmPassword
5001 : */
5002 0 : static NTSTATUS dcesrv_samr_SetDsrmPassword(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
5003 : struct samr_SetDsrmPassword *r)
5004 : {
5005 0 : DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
5006 : }
5007 :
5008 :
5009 : /*
5010 : samr_ValidatePassword
5011 :
5012 : For now the call checks the password complexity (if active) and the minimum
5013 : password length on level 2 and 3. Level 1 is ignored for now.
5014 : */
5015 11 : static NTSTATUS dcesrv_samr_ValidatePassword(struct dcesrv_call_state *dce_call,
5016 : TALLOC_CTX *mem_ctx,
5017 : struct samr_ValidatePassword *r)
5018 : {
5019 : struct samr_GetDomPwInfo r2;
5020 : struct samr_PwInfo pwInfo;
5021 11 : const char *account = NULL;
5022 : DATA_BLOB password;
5023 : enum samr_ValidationStatus res;
5024 : NTSTATUS status;
5025 8 : enum dcerpc_transport_t transport =
5026 11 : dcerpc_binding_get_transport(dce_call->conn->endpoint->ep_description);
5027 11 : enum dcerpc_AuthLevel auth_level = DCERPC_AUTH_LEVEL_NONE;
5028 :
5029 11 : if (transport != NCACN_IP_TCP && transport != NCALRPC) {
5030 0 : DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
5031 : }
5032 :
5033 11 : dcesrv_call_auth_info(dce_call, NULL, &auth_level);
5034 11 : if (auth_level != DCERPC_AUTH_LEVEL_PRIVACY) {
5035 2 : DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
5036 : }
5037 :
5038 9 : (*r->out.rep) = talloc_zero(mem_ctx, union samr_ValidatePasswordRep);
5039 :
5040 9 : r2 = (struct samr_GetDomPwInfo) {
5041 : .in.domain_name = NULL,
5042 : .out.info = &pwInfo,
5043 : };
5044 :
5045 9 : status = dcesrv_samr_GetDomPwInfo(dce_call, mem_ctx, &r2);
5046 9 : if (!NT_STATUS_IS_OK(status)) {
5047 0 : return status;
5048 : }
5049 :
5050 9 : switch (r->in.level) {
5051 0 : case NetValidateAuthentication:
5052 : /* we don't support this yet */
5053 0 : return NT_STATUS_NOT_SUPPORTED;
5054 : break;
5055 0 : case NetValidatePasswordChange:
5056 0 : account = r->in.req->req2.account.string;
5057 0 : password = data_blob_const(r->in.req->req2.password.string,
5058 0 : r->in.req->req2.password.length);
5059 0 : res = samdb_check_password(mem_ctx,
5060 0 : dce_call->conn->dce_ctx->lp_ctx,
5061 : account,
5062 : NULL, /* userPrincipalName */
5063 : NULL, /* displayName/full_name */
5064 : &password,
5065 : pwInfo.password_properties,
5066 0 : pwInfo.min_password_length);
5067 0 : (*r->out.rep)->ctr2.status = res;
5068 0 : break;
5069 9 : case NetValidatePasswordReset:
5070 9 : account = r->in.req->req3.account.string;
5071 9 : password = data_blob_const(r->in.req->req3.password.string,
5072 9 : r->in.req->req3.password.length);
5073 15 : res = samdb_check_password(mem_ctx,
5074 9 : dce_call->conn->dce_ctx->lp_ctx,
5075 : account,
5076 : NULL, /* userPrincipalName */
5077 : NULL, /* displayName/full_name */
5078 : &password,
5079 : pwInfo.password_properties,
5080 9 : pwInfo.min_password_length);
5081 9 : (*r->out.rep)->ctr3.status = res;
5082 9 : break;
5083 0 : default:
5084 0 : return NT_STATUS_INVALID_INFO_CLASS;
5085 : break;
5086 : }
5087 :
5088 9 : return NT_STATUS_OK;
5089 : }
5090 :
5091 :
5092 : /* include the generated boilerplate */
5093 : #include "librpc/gen_ndr/ndr_samr_s.c"
|