2 Unix SMB/CIFS implementation.
4 Copyright (C) Stefan Metzmacher 2004
5 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
6 Copyright (C) Brad Henry 2005
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "libnet/libnet.h"
24 #include "librpc/gen_ndr/ndr_drsuapi_c.h"
25 #include "lib/ldb/include/ldb.h"
26 #include "lib/ldb/include/ldb_errors.h"
27 #include "param/secrets.h"
28 #include "dsdb/samdb/samdb.h"
30 #include "../lib/util/util_ldb.h"
31 #include "libcli/security/security.h"
32 #include "auth/credentials/credentials.h"
33 #include "auth/credentials/credentials_krb5.h"
34 #include "librpc/gen_ndr/ndr_samr_c.h"
35 #include "param/param.h"
38 * complete a domain join, when joining to a AD domain:
39 * 1.) connect and bind to the DRSUAPI pipe
40 * 2.) do a DsCrackNames() to find the machine account dn
42 * 4.) do an ldap search to find the "msDS-KeyVersionNumber" of the machine account
43 * 5.) set the servicePrincipalName's of the machine account via LDAP, (maybe we should use DsWriteAccountSpn()...)
44 * 6.) do a DsCrackNames() to find the domain dn
45 * 7.) find out Site specific stuff, look at libnet_JoinSite() for details
47 static NTSTATUS libnet_JoinADSDomain(struct libnet_context *ctx, struct libnet_JoinDomain *r)
53 const char *realm = r->out.realm;
55 struct dcerpc_binding *samr_binding = r->out.samr_binding;
57 struct dcerpc_pipe *drsuapi_pipe;
58 struct dcerpc_binding *drsuapi_binding;
59 struct drsuapi_DsBind r_drsuapi_bind;
60 struct drsuapi_DsCrackNames r_crack_names;
61 struct drsuapi_DsNameString names[1];
62 struct policy_handle drsuapi_bind_handle;
63 struct GUID drsuapi_bind_guid;
65 struct ldb_context *remote_ldb;
66 struct ldb_dn *account_dn;
67 const char *account_dn_str;
68 const char *remote_ldb_url;
69 struct ldb_result *res;
70 struct ldb_message *msg;
74 const char * const attrs[] = {
75 "msDS-KeyVersionNumber",
76 "servicePrincipalName",
82 r->out.error_string = NULL;
84 /* We need to convert between a samAccountName and domain to a
85 * DN in the directory. The correct way to do this is with
86 * DRSUAPI CrackNames */
88 /* Fiddle with the bindings, so get to DRSUAPI on
89 * NCACN_IP_TCP, sealed */
90 tmp_ctx = talloc_named(r, 0, "libnet_JoinADSDomain temp context");
92 r->out.error_string = NULL;
93 return NT_STATUS_NO_MEMORY;
96 drsuapi_binding = talloc(tmp_ctx, struct dcerpc_binding);
97 if (!drsuapi_binding) {
98 r->out.error_string = NULL;
100 return NT_STATUS_NO_MEMORY;
103 *drsuapi_binding = *samr_binding;
105 /* DRSUAPI is only available on IP_TCP, and locally on NCALRPC */
106 if (drsuapi_binding->transport != NCALRPC) {
107 drsuapi_binding->transport = NCACN_IP_TCP;
109 drsuapi_binding->endpoint = NULL;
110 drsuapi_binding->flags |= DCERPC_SEAL;
112 status = dcerpc_pipe_connect_b(tmp_ctx,
119 if (!NT_STATUS_IS_OK(status)) {
120 r->out.error_string = talloc_asprintf(r,
121 "Connection to DRSUAPI pipe of PDC of domain '%s' failed: %s",
124 talloc_free(tmp_ctx);
128 /* get a DRSUAPI pipe handle */
129 GUID_from_string(DRSUAPI_DS_BIND_GUID, &drsuapi_bind_guid);
131 r_drsuapi_bind.in.bind_guid = &drsuapi_bind_guid;
132 r_drsuapi_bind.in.bind_info = NULL;
133 r_drsuapi_bind.out.bind_handle = &drsuapi_bind_handle;
135 status = dcerpc_drsuapi_DsBind(drsuapi_pipe, tmp_ctx, &r_drsuapi_bind);
136 if (!NT_STATUS_IS_OK(status)) {
137 if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
140 "dcerpc_drsuapi_DsBind failed - %s",
141 dcerpc_errstr(tmp_ctx, drsuapi_pipe->last_fault_code));
142 talloc_free(tmp_ctx);
147 "dcerpc_drsuapi_DsBind failed - %s",
149 talloc_free(tmp_ctx);
152 } else if (!W_ERROR_IS_OK(r_drsuapi_bind.out.result)) {
155 "DsBind failed - %s",
156 win_errstr(r_drsuapi_bind.out.result));
157 talloc_free(tmp_ctx);
158 return NT_STATUS_UNSUCCESSFUL;
161 /* Actually 'crack' the names */
162 ZERO_STRUCT(r_crack_names);
163 r_crack_names.in.bind_handle = &drsuapi_bind_handle;
164 r_crack_names.in.level = 1;
165 r_crack_names.in.req = talloc(r, union drsuapi_DsNameRequest);
166 if (!r_crack_names.in.req) {
167 r->out.error_string = NULL;
168 talloc_free(tmp_ctx);
169 return NT_STATUS_NO_MEMORY;
171 r_crack_names.in.req->req1.codepage = 1252; /* western european */
172 r_crack_names.in.req->req1.language = 0x00000407; /* german */
173 r_crack_names.in.req->req1.count = 1;
174 r_crack_names.in.req->req1.names = names;
175 r_crack_names.in.req->req1.format_flags = DRSUAPI_DS_NAME_FLAG_NO_FLAGS;
176 r_crack_names.in.req->req1.format_offered = DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY;
177 r_crack_names.in.req->req1.format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779;
178 names[0].str = dom_sid_string(tmp_ctx, r->out.account_sid);
180 r->out.error_string = NULL;
181 talloc_free(tmp_ctx);
182 return NT_STATUS_NO_MEMORY;
185 r_crack_names.out.ctr = talloc(r, union drsuapi_DsNameCtr);
186 r_crack_names.out.level_out = talloc(r, int32_t);
187 if (!r_crack_names.out.ctr || !r_crack_names.out.level_out) {
188 r->out.error_string = NULL;
189 talloc_free(tmp_ctx);
190 return NT_STATUS_NO_MEMORY;
193 status = dcerpc_drsuapi_DsCrackNames(drsuapi_pipe, tmp_ctx, &r_crack_names);
194 if (!NT_STATUS_IS_OK(status)) {
195 if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
198 "dcerpc_drsuapi_DsCrackNames for [%s] failed - %s",
200 dcerpc_errstr(tmp_ctx, drsuapi_pipe->last_fault_code));
201 talloc_free(tmp_ctx);
206 "dcerpc_drsuapi_DsCrackNames for [%s] failed - %s",
209 talloc_free(tmp_ctx);
212 } else if (!W_ERROR_IS_OK(r_crack_names.out.result)) {
215 "DsCrackNames failed - %s", win_errstr(r_crack_names.out.result));
216 talloc_free(tmp_ctx);
217 return NT_STATUS_UNSUCCESSFUL;
218 } else if (*r_crack_names.out.level_out != 1
219 || !r_crack_names.out.ctr->ctr1
220 || r_crack_names.out.ctr->ctr1->count != 1) {
221 r->out.error_string = talloc_asprintf(r, "DsCrackNames failed");
222 talloc_free(tmp_ctx);
223 return NT_STATUS_INVALID_PARAMETER;
224 } else if (r_crack_names.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) {
225 r->out.error_string = talloc_asprintf(r, "DsCrackNames failed: %d", r_crack_names.out.ctr->ctr1->array[0].status);
226 talloc_free(tmp_ctx);
227 return NT_STATUS_UNSUCCESSFUL;
228 } else if (r_crack_names.out.ctr->ctr1->array[0].result_name == NULL) {
229 r->out.error_string = talloc_asprintf(r, "DsCrackNames failed: no result name");
230 talloc_free(tmp_ctx);
231 return NT_STATUS_INVALID_PARAMETER;
234 /* Store the DN of our machine account. */
235 account_dn_str = r_crack_names.out.ctr->ctr1->array[0].result_name;
237 /* Now we know the user's DN, open with LDAP, read and modify a few things */
239 remote_ldb_url = talloc_asprintf(tmp_ctx, "ldap://%s",
240 drsuapi_binding->target_hostname);
241 if (!remote_ldb_url) {
242 r->out.error_string = NULL;
243 talloc_free(tmp_ctx);
244 return NT_STATUS_NO_MEMORY;
247 remote_ldb = ldb_wrap_connect(tmp_ctx, ctx->event_ctx, ctx->lp_ctx,
249 NULL, ctx->cred, 0, NULL);
251 r->out.error_string = NULL;
252 talloc_free(tmp_ctx);
253 return NT_STATUS_UNSUCCESSFUL;
256 account_dn = ldb_dn_new(tmp_ctx, remote_ldb, account_dn_str);
257 if (! ldb_dn_validate(account_dn)) {
258 r->out.error_string = talloc_asprintf(r, "Invalid account dn: %s",
260 talloc_free(tmp_ctx);
261 return NT_STATUS_UNSUCCESSFUL;
264 /* search for the user's record */
265 ret = ldb_search(remote_ldb, tmp_ctx, &res,
266 account_dn, LDB_SCOPE_BASE, attrs, NULL);
267 if (ret != LDB_SUCCESS) {
268 r->out.error_string = talloc_asprintf(r, "ldb_search for %s failed - %s",
269 account_dn_str, ldb_errstring(remote_ldb));
270 talloc_free(tmp_ctx);
271 return NT_STATUS_UNSUCCESSFUL;
274 if (res->count != 1) {
275 r->out.error_string = talloc_asprintf(r, "ldb_search for %s failed - found %d entries",
276 account_dn_str, res->count);
277 talloc_free(tmp_ctx);
278 return NT_STATUS_UNSUCCESSFUL;
281 /* Prepare a new message, for the modify */
282 msg = ldb_msg_new(tmp_ctx);
284 r->out.error_string = NULL;
285 talloc_free(tmp_ctx);
286 return NT_STATUS_NO_MEMORY;
288 msg->dn = res->msgs[0]->dn;
292 const char *service_principal_name[6];
293 const char *dns_host_name = strlower_talloc(tmp_ctx,
294 talloc_asprintf(tmp_ctx,
299 if (!dns_host_name) {
300 r->out.error_string = NULL;
301 talloc_free(tmp_ctx);
302 return NT_STATUS_NO_MEMORY;
305 service_principal_name[0] = talloc_asprintf(tmp_ctx, "host/%s", dns_host_name);
306 service_principal_name[1] = talloc_asprintf(tmp_ctx, "host/%s", strlower_talloc(tmp_ctx, r->in.netbios_name));
307 service_principal_name[2] = talloc_asprintf(tmp_ctx, "host/%s/%s", dns_host_name, realm);
308 service_principal_name[3] = talloc_asprintf(tmp_ctx, "host/%s/%s", strlower_talloc(tmp_ctx, r->in.netbios_name), realm);
309 service_principal_name[4] = talloc_asprintf(tmp_ctx, "host/%s/%s", dns_host_name, r->out.domain_name);
310 service_principal_name[5] = talloc_asprintf(tmp_ctx, "host/%s/%s", strlower_talloc(tmp_ctx, r->in.netbios_name), r->out.domain_name);
312 for (i=0; i < ARRAY_SIZE(service_principal_name); i++) {
313 if (!service_principal_name[i]) {
314 r->out.error_string = NULL;
315 talloc_free(tmp_ctx);
316 return NT_STATUS_NO_MEMORY;
318 rtn = samdb_msg_add_string(remote_ldb, tmp_ctx, msg, "servicePrincipalName", service_principal_name[i]);
320 r->out.error_string = NULL;
321 talloc_free(tmp_ctx);
322 return NT_STATUS_NO_MEMORY;
326 rtn = samdb_msg_add_string(remote_ldb, tmp_ctx, msg, "dNSHostName", dns_host_name);
328 r->out.error_string = NULL;
329 talloc_free(tmp_ctx);
330 return NT_STATUS_NO_MEMORY;
333 rtn = samdb_replace(remote_ldb, tmp_ctx, msg);
337 "Failed to replace entries on %s",
338 ldb_dn_get_linearized(msg->dn));
339 talloc_free(tmp_ctx);
340 return NT_STATUS_INTERNAL_DB_CORRUPTION;
344 /* DsCrackNames to find out the DN of the domain. */
345 r_crack_names.in.req->req1.format_offered = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT;
346 r_crack_names.in.req->req1.format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779;
347 names[0].str = talloc_asprintf(tmp_ctx, "%s\\", r->out.domain_name);
349 r->out.error_string = NULL;
350 talloc_free(tmp_ctx);
351 return NT_STATUS_NO_MEMORY;
354 status = dcerpc_drsuapi_DsCrackNames(drsuapi_pipe, tmp_ctx, &r_crack_names);
355 if (!NT_STATUS_IS_OK(status)) {
356 if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
359 "dcerpc_drsuapi_DsCrackNames for [%s] failed - %s",
361 dcerpc_errstr(tmp_ctx, drsuapi_pipe->last_fault_code));
362 talloc_free(tmp_ctx);
367 "dcerpc_drsuapi_DsCrackNames for [%s] failed - %s",
370 talloc_free(tmp_ctx);
373 } else if (!W_ERROR_IS_OK(r_crack_names.out.result)) {
376 "DsCrackNames failed - %s", win_errstr(r_crack_names.out.result));
377 talloc_free(tmp_ctx);
378 return NT_STATUS_UNSUCCESSFUL;
379 } else if (*r_crack_names.out.level_out != 1
380 || !r_crack_names.out.ctr->ctr1
381 || r_crack_names.out.ctr->ctr1->count != 1
382 || !r_crack_names.out.ctr->ctr1->array[0].result_name
383 || r_crack_names.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) {
384 r->out.error_string = talloc_asprintf(r, "DsCrackNames failed");
385 talloc_free(tmp_ctx);
386 return NT_STATUS_UNSUCCESSFUL;
389 /* Store the account DN. */
390 r->out.account_dn_str = account_dn_str;
391 talloc_steal(r, account_dn_str);
393 /* Store the domain DN. */
394 r->out.domain_dn_str = r_crack_names.out.ctr->ctr1->array[0].result_name;
395 talloc_steal(r, r_crack_names.out.ctr->ctr1->array[0].result_name);
397 /* Store the KVNO of the account, critical for some kerberos
399 r->out.kvno = ldb_msg_find_attr_as_uint(res->msgs[0], "msDS-KeyVersionNumber", 0);
401 /* Store the account GUID. */
402 r->out.account_guid = samdb_result_guid(res->msgs[0], "objectGUID");
404 if (r->in.acct_type == ACB_SVRTRUST) {
405 status = libnet_JoinSite(ctx, remote_ldb, r);
407 talloc_free(tmp_ctx);
413 * do a domain join using DCERPC/SAMR calls
414 * - connect to the LSA pipe, to try and find out information about the domain
415 * - create a secondary connection to SAMR pipe
416 * - do a samr_Connect to get a policy handle
417 * - do a samr_LookupDomain to get the domain sid
418 * - do a samr_OpenDomain to get a domain handle
419 * - do a samr_CreateAccount to try and get a new account
422 * - do a samr_LookupNames to get the users rid
423 * - do a samr_OpenUser to get a user handle
424 * - potentially delete and recreate the user
425 * - assert the account is of the right type with samrQueryUserInfo
427 * - call libnet_SetPassword_samr_handle to set the password,
428 * and pass a samr_UserInfo21 struct to set full_name and the account flags
430 * - do some ADS specific things when we join as Domain Controller,
431 * look at libnet_joinADSDomain() for the details
433 NTSTATUS libnet_JoinDomain(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_JoinDomain *r)
437 NTSTATUS status, cu_status;
439 struct libnet_RpcConnect *connect_with_info;
440 struct dcerpc_pipe *samr_pipe;
442 struct samr_Connect sc;
443 struct policy_handle p_handle;
444 struct samr_OpenDomain od;
445 struct policy_handle d_handle;
446 struct samr_LookupNames ln;
447 struct samr_OpenUser ou;
448 struct samr_CreateUser2 cu;
449 struct policy_handle *u_handle = NULL;
450 struct samr_QueryUserInfo qui;
451 struct samr_UserInfo21 u_info21;
452 union libnet_SetPassword r2;
453 struct samr_GetUserPwInfo pwp;
454 struct lsa_String samr_account_name;
456 uint32_t acct_flags, old_acct_flags;
457 uint32_t rid, access_granted;
458 int policy_min_pw_len = 0;
460 struct dom_sid *account_sid = NULL;
461 const char *password_str = NULL;
463 r->out.error_string = NULL;
464 r2.samr_handle.out.error_string = NULL;
466 tmp_ctx = talloc_named(mem_ctx, 0, "libnet_Join temp context");
468 r->out.error_string = NULL;
469 return NT_STATUS_NO_MEMORY;
472 u_handle = talloc(tmp_ctx, struct policy_handle);
474 r->out.error_string = NULL;
475 talloc_free(tmp_ctx);
476 return NT_STATUS_NO_MEMORY;
479 connect_with_info = talloc(tmp_ctx, struct libnet_RpcConnect);
480 if (!connect_with_info) {
481 r->out.error_string = NULL;
482 talloc_free(tmp_ctx);
483 return NT_STATUS_NO_MEMORY;
486 /* prepare connect to the SAMR pipe of PDC */
487 if (r->in.level == LIBNET_JOINDOMAIN_AUTOMATIC) {
488 connect_with_info->in.binding = NULL;
489 connect_with_info->in.name = r->in.domain_name;
491 connect_with_info->in.binding = r->in.binding;
492 connect_with_info->in.name = NULL;
495 /* This level makes a connection to the LSA pipe on the way,
496 * to get some useful bits of information about the domain */
497 connect_with_info->level = LIBNET_RPC_CONNECT_DC_INFO;
498 connect_with_info->in.dcerpc_iface = &ndr_table_samr;
501 establish the SAMR connection
503 status = libnet_RpcConnect(ctx, tmp_ctx, connect_with_info);
504 if (!NT_STATUS_IS_OK(status)) {
506 r->out.error_string = talloc_asprintf(mem_ctx,
507 "Connection to SAMR pipe of DC %s failed: %s",
508 r->in.binding, connect_with_info->out.error_string);
510 r->out.error_string = talloc_asprintf(mem_ctx,
511 "Connection to SAMR pipe of PDC for %s failed: %s",
512 r->in.domain_name, connect_with_info->out.error_string);
514 talloc_free(tmp_ctx);
518 samr_pipe = connect_with_info->out.dcerpc_pipe;
520 status = dcerpc_pipe_auth(tmp_ctx, &samr_pipe,
521 connect_with_info->out.dcerpc_pipe->binding,
522 &ndr_table_samr, ctx->cred, ctx->lp_ctx);
523 if (!NT_STATUS_IS_OK(status)) {
524 r->out.error_string = talloc_asprintf(mem_ctx,
525 "SAMR bind failed: %s",
527 talloc_free(tmp_ctx);
531 /* prepare samr_Connect */
532 ZERO_STRUCT(p_handle);
533 sc.in.system_name = NULL;
534 sc.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
535 sc.out.connect_handle = &p_handle;
537 /* 2. do a samr_Connect to get a policy handle */
538 status = dcerpc_samr_Connect(samr_pipe, tmp_ctx, &sc);
539 if (!NT_STATUS_IS_OK(status)) {
540 r->out.error_string = talloc_asprintf(mem_ctx,
541 "samr_Connect failed: %s",
543 talloc_free(tmp_ctx);
547 /* If this is a connection on ncacn_ip_tcp to Win2k3 SP1, we don't get back this useful info */
548 if (!connect_with_info->out.domain_name) {
549 if (r->in.level == LIBNET_JOINDOMAIN_AUTOMATIC) {
550 connect_with_info->out.domain_name = talloc_strdup(tmp_ctx, r->in.domain_name);
552 /* Bugger, we just lost our way to automaticly find the domain name */
553 connect_with_info->out.domain_name = talloc_strdup(tmp_ctx, lp_workgroup(ctx->lp_ctx));
554 connect_with_info->out.realm = talloc_strdup(tmp_ctx, lp_realm(ctx->lp_ctx));
558 /* Perhaps we didn't get a SID above, because we are against ncacn_ip_tcp */
559 if (!connect_with_info->out.domain_sid) {
560 struct lsa_String name;
561 struct samr_LookupDomain l;
562 name.string = connect_with_info->out.domain_name;
563 l.in.connect_handle = &p_handle;
564 l.in.domain_name = &name;
566 status = dcerpc_samr_LookupDomain(samr_pipe, tmp_ctx, &l);
567 if (!NT_STATUS_IS_OK(status)) {
568 r->out.error_string = talloc_asprintf(mem_ctx,
569 "SAMR LookupDomain failed: %s",
571 talloc_free(tmp_ctx);
574 connect_with_info->out.domain_sid = l.out.sid;
577 /* prepare samr_OpenDomain */
578 ZERO_STRUCT(d_handle);
579 od.in.connect_handle = &p_handle;
580 od.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
581 od.in.sid = connect_with_info->out.domain_sid;
582 od.out.domain_handle = &d_handle;
584 /* do a samr_OpenDomain to get a domain handle */
585 status = dcerpc_samr_OpenDomain(samr_pipe, tmp_ctx, &od);
586 if (!NT_STATUS_IS_OK(status)) {
587 r->out.error_string = talloc_asprintf(mem_ctx,
588 "samr_OpenDomain for [%s] failed: %s",
589 dom_sid_string(tmp_ctx, connect_with_info->out.domain_sid),
591 talloc_free(tmp_ctx);
595 /* prepare samr_CreateUser2 */
596 ZERO_STRUCTP(u_handle);
597 cu.in.domain_handle = &d_handle;
598 cu.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
599 samr_account_name.string = r->in.account_name;
600 cu.in.account_name = &samr_account_name;
601 cu.in.acct_flags = r->in.acct_type;
602 cu.out.user_handle = u_handle;
604 cu.out.access_granted = &access_granted;
606 /* do a samr_CreateUser2 to get an account handle, or an error */
607 cu_status = dcerpc_samr_CreateUser2(samr_pipe, tmp_ctx, &cu);
609 if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
610 /* prepare samr_LookupNames */
611 ln.in.domain_handle = &d_handle;
613 ln.in.names = talloc_array(tmp_ctx, struct lsa_String, 1);
615 r->out.error_string = NULL;
616 talloc_free(tmp_ctx);
617 return NT_STATUS_NO_MEMORY;
619 ln.in.names[0].string = r->in.account_name;
621 /* 5. do a samr_LookupNames to get the users rid */
622 status = dcerpc_samr_LookupNames(samr_pipe, tmp_ctx, &ln);
623 if (!NT_STATUS_IS_OK(status)) {
624 r->out.error_string = talloc_asprintf(mem_ctx,
625 "samr_LookupNames for [%s] failed: %s",
628 talloc_free(tmp_ctx);
632 /* check if we got one RID for the user */
633 if (ln.out.rids.count != 1) {
634 r->out.error_string = talloc_asprintf(mem_ctx,
635 "samr_LookupNames for [%s] returns %d RIDs",
636 r->in.account_name, ln.out.rids.count);
637 talloc_free(tmp_ctx);
638 return NT_STATUS_INVALID_PARAMETER;
641 /* prepare samr_OpenUser */
642 ZERO_STRUCTP(u_handle);
643 ou.in.domain_handle = &d_handle;
644 ou.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
645 ou.in.rid = ln.out.rids.ids[0];
647 ou.out.user_handle = u_handle;
649 /* 6. do a samr_OpenUser to get a user handle */
650 status = dcerpc_samr_OpenUser(samr_pipe, tmp_ctx, &ou);
651 if (!NT_STATUS_IS_OK(status)) {
652 r->out.error_string = talloc_asprintf(mem_ctx,
653 "samr_OpenUser for [%s] failed: %s",
656 talloc_free(tmp_ctx);
660 if (r->in.recreate_account) {
661 struct samr_DeleteUser d;
662 d.in.user_handle = u_handle;
663 d.out.user_handle = u_handle;
664 status = dcerpc_samr_DeleteUser(samr_pipe, mem_ctx, &d);
665 if (!NT_STATUS_IS_OK(status)) {
666 r->out.error_string = talloc_asprintf(mem_ctx,
667 "samr_DeleteUser (for recreate) of [%s] failed: %s",
670 talloc_free(tmp_ctx);
674 /* We want to recreate, so delete and another samr_CreateUser2 */
676 /* &cu filled in above */
677 status = dcerpc_samr_CreateUser2(samr_pipe, tmp_ctx, &cu);
678 if (!NT_STATUS_IS_OK(status)) {
679 r->out.error_string = talloc_asprintf(mem_ctx,
680 "samr_CreateUser2 (recreate) for [%s] failed: %s",
681 r->in.account_name, nt_errstr(status));
682 talloc_free(tmp_ctx);
686 } else if (!NT_STATUS_IS_OK(status)) {
687 r->out.error_string = talloc_asprintf(mem_ctx,
688 "samr_CreateUser2 for [%s] failed: %s",
689 r->in.account_name, nt_errstr(status));
690 talloc_free(tmp_ctx);
694 /* prepare samr_QueryUserInfo (get flags) */
695 qui.in.user_handle = u_handle;
698 status = dcerpc_samr_QueryUserInfo(samr_pipe, tmp_ctx, &qui);
699 if (!NT_STATUS_IS_OK(status)) {
700 r->out.error_string = talloc_asprintf(mem_ctx,
701 "samr_QueryUserInfo for [%s] failed: %s",
704 talloc_free(tmp_ctx);
709 status = NT_STATUS_INVALID_PARAMETER;
711 = talloc_asprintf(mem_ctx,
712 "samr_QueryUserInfo failed to return qui.out.info for [%s]: %s",
713 r->in.account_name, nt_errstr(status));
714 talloc_free(tmp_ctx);
718 old_acct_flags = (qui.out.info->info16.acct_flags & (ACB_WSTRUST | ACB_SVRTRUST | ACB_DOMTRUST));
719 /* Possibly bail if the account is of the wrong type */
721 != r->in.acct_type) {
722 const char *old_account_type, *new_account_type;
723 switch (old_acct_flags) {
725 old_account_type = "domain member (member)";
728 old_account_type = "domain controller (bdc)";
731 old_account_type = "trusted domain";
734 return NT_STATUS_INVALID_PARAMETER;
736 switch (r->in.acct_type) {
738 new_account_type = "domain member (member)";
741 new_account_type = "domain controller (bdc)";
744 new_account_type = "trusted domain";
747 return NT_STATUS_INVALID_PARAMETER;
750 if (!NT_STATUS_EQUAL(cu_status, NT_STATUS_USER_EXISTS)) {
751 /* We created a new user, but they didn't come out the right type?!? */
753 = talloc_asprintf(mem_ctx,
754 "We asked to create a new machine account (%s) of type %s, "
755 "but we got an account of type %s. This is unexpected. "
756 "Perhaps delete the account and try again.",
757 r->in.account_name, new_account_type, old_account_type);
758 talloc_free(tmp_ctx);
759 return NT_STATUS_INVALID_PARAMETER;
761 /* The account is of the wrong type, so bail */
763 /* TODO: We should allow a --force option to override, and redo this from the top setting r.in.recreate_account */
765 = talloc_asprintf(mem_ctx,
766 "The machine account (%s) already exists in the domain %s, "
767 "but is a %s. You asked to join as a %s. Please delete "
768 "the account and try again.",
769 r->in.account_name, connect_with_info->out.domain_name, old_account_type, new_account_type);
770 talloc_free(tmp_ctx);
771 return NT_STATUS_USER_EXISTS;
774 acct_flags = qui.out.info->info16.acct_flags;
777 acct_flags = (acct_flags & ~(ACB_DISABLED|ACB_PWNOTREQ));
779 /* Find out what password policy this user has */
780 pwp.in.user_handle = u_handle;
782 status = dcerpc_samr_GetUserPwInfo(samr_pipe, tmp_ctx, &pwp);
783 if (NT_STATUS_IS_OK(status)) {
784 policy_min_pw_len = pwp.out.info.min_password_length;
787 /* Grab a password of that minimum length */
789 password_str = generate_random_str(tmp_ctx, MAX(8, policy_min_pw_len));
791 /* set full_name and reset flags */
792 ZERO_STRUCT(u_info21);
793 u_info21.full_name.string = r->in.account_name;
794 u_info21.acct_flags = acct_flags;
795 u_info21.fields_present = SAMR_FIELD_FULL_NAME | SAMR_FIELD_ACCT_FLAGS;
797 r2.samr_handle.level = LIBNET_SET_PASSWORD_SAMR_HANDLE;
798 r2.samr_handle.in.account_name = r->in.account_name;
799 r2.samr_handle.in.newpassword = password_str;
800 r2.samr_handle.in.user_handle = u_handle;
801 r2.samr_handle.in.dcerpc_pipe = samr_pipe;
802 r2.samr_handle.in.info21 = &u_info21;
804 status = libnet_SetPassword(ctx, tmp_ctx, &r2);
805 if (!NT_STATUS_IS_OK(status)) {
806 r->out.error_string = talloc_steal(mem_ctx, r2.samr_handle.out.error_string);
807 talloc_free(tmp_ctx);
811 account_sid = dom_sid_add_rid(mem_ctx, connect_with_info->out.domain_sid, rid);
813 r->out.error_string = NULL;
814 talloc_free(tmp_ctx);
815 return NT_STATUS_NO_MEMORY;
818 /* Finish out by pushing various bits of status data out for the caller to use */
819 r->out.join_password = password_str;
820 talloc_steal(mem_ctx, r->out.join_password);
822 r->out.domain_sid = connect_with_info->out.domain_sid;
823 talloc_steal(mem_ctx, r->out.domain_sid);
825 r->out.account_sid = account_sid;
826 talloc_steal(mem_ctx, r->out.account_sid);
828 r->out.domain_name = connect_with_info->out.domain_name;
829 talloc_steal(mem_ctx, r->out.domain_name);
830 r->out.realm = connect_with_info->out.realm;
831 talloc_steal(mem_ctx, r->out.realm);
832 r->out.samr_pipe = samr_pipe;
833 talloc_steal(mem_ctx, samr_pipe);
834 r->out.samr_binding = samr_pipe->binding;
835 talloc_steal(mem_ctx, r->out.samr_binding);
836 r->out.user_handle = u_handle;
837 talloc_steal(mem_ctx, u_handle);
838 r->out.error_string = r2.samr_handle.out.error_string;
839 talloc_steal(mem_ctx, r2.samr_handle.out.error_string);
841 r->out.server_dn_str = NULL;
842 talloc_free(tmp_ctx);
844 /* Now, if it was AD, then we want to start looking changing a
845 * few more things. Otherwise, we are done. */
847 status = libnet_JoinADSDomain(ctx, r);
854 NTSTATUS libnet_set_join_secrets(struct libnet_context *ctx,
856 struct libnet_set_join_secrets *r)
860 struct ldb_context *ldb;
861 struct ldb_dn *base_dn;
862 struct ldb_message **msgs, *msg;
864 const char * const attrs[] = {
874 tmp_mem = talloc_new(mem_ctx);
876 return NT_STATUS_NO_MEMORY;
879 /* Open the secrets database */
880 ldb = secrets_db_connect(tmp_mem, ctx->event_ctx, ctx->lp_ctx);
883 = talloc_asprintf(mem_ctx,
884 "Could not open secrets database");
885 talloc_free(tmp_mem);
886 return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
890 * now prepare the record for secrets.ldb
892 sct = talloc_asprintf(tmp_mem, "%d", r->in.join_type);
894 r->out.error_string = NULL;
895 talloc_free(tmp_mem);
896 return NT_STATUS_NO_MEMORY;
899 msg = ldb_msg_new(tmp_mem);
901 r->out.error_string = NULL;
902 talloc_free(tmp_mem);
903 return NT_STATUS_NO_MEMORY;
906 base_dn = ldb_dn_new(tmp_mem, ldb, "cn=Primary Domains");
908 r->out.error_string = NULL;
909 talloc_free(tmp_mem);
910 return NT_STATUS_NO_MEMORY;
913 msg->dn = ldb_dn_copy(tmp_mem, base_dn);
914 if ( ! ldb_dn_add_child_fmt(msg->dn, "flatname=%s", r->in.domain_name)) {
915 r->out.error_string = NULL;
916 talloc_free(tmp_mem);
917 return NT_STATUS_NO_MEMORY;
920 rtn = samdb_msg_add_string(ldb, tmp_mem, msg, "flatname", r->in.domain_name);
922 r->out.error_string = NULL;
923 talloc_free(tmp_mem);
924 return NT_STATUS_NO_MEMORY;
928 rtn = samdb_msg_add_string(ldb, tmp_mem, msg, "realm", r->in.realm);
930 r->out.error_string = NULL;
931 talloc_free(tmp_mem);
932 return NT_STATUS_NO_MEMORY;
935 rtn = samdb_msg_add_string(ldb, tmp_mem, msg, "objectClass", "primaryDomain");
937 r->out.error_string = NULL;
938 talloc_free(tmp_mem);
939 return NT_STATUS_NO_MEMORY;
943 rtn = samdb_msg_add_string(ldb, tmp_mem, msg, "objectClass", "primaryDomain");
945 r->out.error_string = NULL;
946 talloc_free(tmp_mem);
947 return NT_STATUS_NO_MEMORY;
950 rtn = samdb_msg_add_string(ldb, tmp_mem, msg, "secret", r->in.join_password);
952 r->out.error_string = NULL;
953 talloc_free(tmp_mem);
954 return NT_STATUS_NO_MEMORY;
957 rtn = samdb_msg_add_string(ldb, tmp_mem, msg, "samAccountName", r->in.account_name);
959 r->out.error_string = NULL;
960 talloc_free(tmp_mem);
961 return NT_STATUS_NO_MEMORY;
964 rtn = samdb_msg_add_string(ldb, tmp_mem, msg, "secureChannelType", sct);
966 r->out.error_string = NULL;
967 talloc_free(tmp_mem);
968 return NT_STATUS_NO_MEMORY;
972 rtn = samdb_msg_add_uint(ldb, tmp_mem, msg, "msDS-KeyVersionNumber",
975 r->out.error_string = NULL;
976 talloc_free(tmp_mem);
977 return NT_STATUS_NO_MEMORY;
981 if (r->in.domain_sid) {
982 rtn = samdb_msg_add_dom_sid(ldb, tmp_mem, msg, "objectSid",
985 r->out.error_string = NULL;
986 talloc_free(tmp_mem);
987 return NT_STATUS_NO_MEMORY;
992 * search for the secret record
993 * - remove the records we find
994 * - and fetch the old secret and store it under priorSecret
996 ret = gendb_search(ldb,
999 "(|" SECRETS_PRIMARY_DOMAIN_FILTER "(realm=%s))",
1000 r->in.domain_name, r->in.realm);
1002 rtn = samdb_msg_set_string(ldb, tmp_mem, msg, "secretsKeytab", "secrets.keytab");
1004 r->out.error_string = NULL;
1005 talloc_free(tmp_mem);
1006 return NT_STATUS_NO_MEMORY;
1008 } else if (ret == -1) {
1010 = talloc_asprintf(mem_ctx,
1011 "Search for domain: %s and realm: %s failed: %s",
1012 r->in.domain_name, r->in.realm, ldb_errstring(ldb));
1013 talloc_free(tmp_mem);
1014 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1016 const struct ldb_val *private_keytab;
1017 const struct ldb_val *krb5_main_keytab;
1018 const struct ldb_val *prior_secret;
1019 const struct ldb_val *prior_modified_time;
1022 for (i = 0; i < ret; i++) {
1023 ldb_delete(ldb, msgs[i]->dn);
1026 prior_secret = ldb_msg_find_ldb_val(msgs[0], "secret");
1028 rtn = samdb_msg_set_value(ldb, tmp_mem, msg, "priorSecret", prior_secret);
1030 r->out.error_string = NULL;
1031 talloc_free(tmp_mem);
1032 return NT_STATUS_NO_MEMORY;
1035 rtn = samdb_msg_set_string(ldb, tmp_mem, msg, "secret", r->in.join_password);
1037 r->out.error_string = NULL;
1038 talloc_free(tmp_mem);
1039 return NT_STATUS_NO_MEMORY;
1042 prior_modified_time = ldb_msg_find_ldb_val(msgs[0],
1044 if (prior_modified_time) {
1045 rtn = samdb_msg_set_value(ldb, tmp_mem, msg, "priorWhenChanged",
1046 prior_modified_time);
1048 r->out.error_string = NULL;
1049 talloc_free(tmp_mem);
1050 return NT_STATUS_NO_MEMORY;
1054 rtn = samdb_msg_set_string(ldb, tmp_mem, msg, "samAccountName", r->in.account_name);
1056 r->out.error_string = NULL;
1057 talloc_free(tmp_mem);
1058 return NT_STATUS_NO_MEMORY;
1061 rtn = samdb_msg_set_string(ldb, tmp_mem, msg, "secureChannelType", sct);
1063 r->out.error_string = NULL;
1064 talloc_free(tmp_mem);
1065 return NT_STATUS_NO_MEMORY;
1068 /* We will want to keep the keytab names */
1069 private_keytab = ldb_msg_find_ldb_val(msgs[0], "privateKeytab");
1070 if (private_keytab) {
1071 rtn = samdb_msg_set_value(ldb, tmp_mem, msg, "privateKeytab", private_keytab);
1073 r->out.error_string = NULL;
1074 talloc_free(tmp_mem);
1075 return NT_STATUS_NO_MEMORY;
1078 krb5_main_keytab = ldb_msg_find_ldb_val(msgs[0], "krb5Keytab");
1079 if (krb5_main_keytab) {
1080 rtn = samdb_msg_set_value(ldb, tmp_mem, msg,
1081 "krb5Keytab", krb5_main_keytab);
1083 r->out.error_string = NULL;
1084 talloc_free(tmp_mem);
1085 return NT_STATUS_NO_MEMORY;
1090 /* create the secret */
1091 ret = ldb_add(ldb, msg);
1093 r->out.error_string = talloc_asprintf(mem_ctx, "Failed to create secret record %s",
1094 ldb_dn_get_linearized(msg->dn));
1095 talloc_free(tmp_mem);
1096 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1099 return NT_STATUS_OK;
1102 static NTSTATUS libnet_Join_primary_domain(struct libnet_context *ctx,
1103 TALLOC_CTX *mem_ctx,
1104 struct libnet_Join *r)
1107 TALLOC_CTX *tmp_mem;
1108 struct libnet_JoinDomain *r2;
1109 struct libnet_set_join_secrets *r3;
1110 uint32_t acct_type = 0;
1111 const char *account_name;
1112 const char *netbios_name;
1114 r->out.error_string = NULL;
1116 tmp_mem = talloc_new(mem_ctx);
1118 return NT_STATUS_NO_MEMORY;
1121 r2 = talloc(tmp_mem, struct libnet_JoinDomain);
1123 r->out.error_string = NULL;
1124 talloc_free(tmp_mem);
1125 return NT_STATUS_NO_MEMORY;
1128 if (r->in.join_type == SEC_CHAN_BDC) {
1129 acct_type = ACB_SVRTRUST;
1130 } else if (r->in.join_type == SEC_CHAN_WKSTA) {
1131 acct_type = ACB_WSTRUST;
1133 r->out.error_string = NULL;
1134 talloc_free(tmp_mem);
1135 return NT_STATUS_INVALID_PARAMETER;
1138 if (r->in.netbios_name != NULL) {
1139 netbios_name = r->in.netbios_name;
1141 netbios_name = talloc_reference(tmp_mem, lp_netbios_name(ctx->lp_ctx));
1142 if (!netbios_name) {
1143 r->out.error_string = NULL;
1144 talloc_free(tmp_mem);
1145 return NT_STATUS_NO_MEMORY;
1149 account_name = talloc_asprintf(tmp_mem, "%s$", netbios_name);
1150 if (!account_name) {
1151 r->out.error_string = NULL;
1152 talloc_free(tmp_mem);
1153 return NT_STATUS_NO_MEMORY;
1160 r2->in.domain_name = r->in.domain_name;
1161 r2->in.account_name = account_name;
1162 r2->in.netbios_name = netbios_name;
1163 r2->in.level = LIBNET_JOINDOMAIN_AUTOMATIC;
1164 r2->in.acct_type = acct_type;
1165 r2->in.recreate_account = false;
1166 status = libnet_JoinDomain(ctx, r2, r2);
1167 if (!NT_STATUS_IS_OK(status)) {
1168 r->out.error_string = talloc_steal(mem_ctx, r2->out.error_string);
1169 talloc_free(tmp_mem);
1173 r3 = talloc(tmp_mem, struct libnet_set_join_secrets);
1175 r->out.error_string = NULL;
1176 talloc_free(tmp_mem);
1177 return NT_STATUS_NO_MEMORY;
1181 r3->in.domain_name = r2->out.domain_name;
1182 r3->in.realm = r2->out.realm;
1183 r3->in.account_name = account_name;
1184 r3->in.netbios_name = netbios_name;
1185 r3->in.join_type = r->in.join_type;
1186 r3->in.join_password = r2->out.join_password;
1187 r3->in.kvno = r2->out.kvno;
1188 r3->in.domain_sid = r2->out.domain_sid;
1190 status = libnet_set_join_secrets(ctx, r3, r3);
1191 if (!NT_STATUS_IS_OK(status)) {
1192 r->out.error_string = talloc_steal(mem_ctx, r3->out.error_string);
1193 talloc_free(tmp_mem);
1197 /* move all out parameter to the callers TALLOC_CTX */
1198 r->out.error_string = NULL;
1199 r->out.join_password = r2->out.join_password;
1200 talloc_steal(mem_ctx, r2->out.join_password);
1201 r->out.domain_sid = r2->out.domain_sid;
1202 talloc_steal(mem_ctx, r2->out.domain_sid);
1203 r->out.domain_name = r2->out.domain_name;
1204 talloc_steal(mem_ctx, r2->out.domain_name);
1205 talloc_free(tmp_mem);
1206 return NT_STATUS_OK;
1209 NTSTATUS libnet_Join(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_Join *r)
1211 switch (r->in.join_type) {
1212 case SEC_CHAN_WKSTA:
1213 return libnet_Join_primary_domain(ctx, mem_ctx, r);
1215 return libnet_Join_primary_domain(ctx, mem_ctx, r);
1216 case SEC_CHAN_DOMAIN:
1220 r->out.error_string = talloc_asprintf(mem_ctx,
1221 "Invalid join type specified (%08X) attempting to join domain %s",
1222 r->in.join_type, r->in.domain_name);
1223 return NT_STATUS_INVALID_PARAMETER;