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_Ids rids, types;
448 struct samr_OpenUser ou;
449 struct samr_CreateUser2 cu;
450 struct policy_handle *u_handle = NULL;
451 struct samr_QueryUserInfo qui;
452 struct samr_UserInfo21 u_info21;
453 union libnet_SetPassword r2;
454 struct samr_GetUserPwInfo pwp;
455 struct samr_PwInfo info;
456 struct lsa_String samr_account_name;
458 uint32_t acct_flags, old_acct_flags;
459 uint32_t rid, access_granted;
460 int policy_min_pw_len = 0;
462 struct dom_sid *account_sid = NULL;
463 const char *password_str = NULL;
465 r->out.error_string = NULL;
466 r2.samr_handle.out.error_string = NULL;
468 tmp_ctx = talloc_named(mem_ctx, 0, "libnet_Join temp context");
470 r->out.error_string = NULL;
471 return NT_STATUS_NO_MEMORY;
474 u_handle = talloc(tmp_ctx, struct policy_handle);
476 r->out.error_string = NULL;
477 talloc_free(tmp_ctx);
478 return NT_STATUS_NO_MEMORY;
481 connect_with_info = talloc(tmp_ctx, struct libnet_RpcConnect);
482 if (!connect_with_info) {
483 r->out.error_string = NULL;
484 talloc_free(tmp_ctx);
485 return NT_STATUS_NO_MEMORY;
488 /* prepare connect to the SAMR pipe of PDC */
489 if (r->in.level == LIBNET_JOINDOMAIN_AUTOMATIC) {
490 connect_with_info->in.binding = NULL;
491 connect_with_info->in.name = r->in.domain_name;
493 connect_with_info->in.binding = r->in.binding;
494 connect_with_info->in.name = NULL;
497 /* This level makes a connection to the LSA pipe on the way,
498 * to get some useful bits of information about the domain */
499 connect_with_info->level = LIBNET_RPC_CONNECT_DC_INFO;
500 connect_with_info->in.dcerpc_iface = &ndr_table_samr;
503 establish the SAMR connection
505 status = libnet_RpcConnect(ctx, tmp_ctx, connect_with_info);
506 if (!NT_STATUS_IS_OK(status)) {
508 r->out.error_string = talloc_asprintf(mem_ctx,
509 "Connection to SAMR pipe of DC %s failed: %s",
510 r->in.binding, connect_with_info->out.error_string);
512 r->out.error_string = talloc_asprintf(mem_ctx,
513 "Connection to SAMR pipe of PDC for %s failed: %s",
514 r->in.domain_name, connect_with_info->out.error_string);
516 talloc_free(tmp_ctx);
520 samr_pipe = connect_with_info->out.dcerpc_pipe;
522 status = dcerpc_pipe_auth(tmp_ctx, &samr_pipe,
523 connect_with_info->out.dcerpc_pipe->binding,
524 &ndr_table_samr, ctx->cred, ctx->lp_ctx);
525 if (!NT_STATUS_IS_OK(status)) {
526 r->out.error_string = talloc_asprintf(mem_ctx,
527 "SAMR bind failed: %s",
529 talloc_free(tmp_ctx);
533 /* prepare samr_Connect */
534 ZERO_STRUCT(p_handle);
535 sc.in.system_name = NULL;
536 sc.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
537 sc.out.connect_handle = &p_handle;
539 /* 2. do a samr_Connect to get a policy handle */
540 status = dcerpc_samr_Connect(samr_pipe, tmp_ctx, &sc);
541 if (!NT_STATUS_IS_OK(status)) {
542 r->out.error_string = talloc_asprintf(mem_ctx,
543 "samr_Connect failed: %s",
545 talloc_free(tmp_ctx);
549 /* If this is a connection on ncacn_ip_tcp to Win2k3 SP1, we don't get back this useful info */
550 if (!connect_with_info->out.domain_name) {
551 if (r->in.level == LIBNET_JOINDOMAIN_AUTOMATIC) {
552 connect_with_info->out.domain_name = talloc_strdup(tmp_ctx, r->in.domain_name);
554 /* Bugger, we just lost our way to automaticly find the domain name */
555 connect_with_info->out.domain_name = talloc_strdup(tmp_ctx, lp_workgroup(ctx->lp_ctx));
556 connect_with_info->out.realm = talloc_strdup(tmp_ctx, lp_realm(ctx->lp_ctx));
560 /* Perhaps we didn't get a SID above, because we are against ncacn_ip_tcp */
561 if (!connect_with_info->out.domain_sid) {
562 struct lsa_String name;
563 struct samr_LookupDomain l;
564 name.string = connect_with_info->out.domain_name;
565 l.in.connect_handle = &p_handle;
566 l.in.domain_name = &name;
568 status = dcerpc_samr_LookupDomain(samr_pipe, tmp_ctx, &l);
569 if (!NT_STATUS_IS_OK(status)) {
570 r->out.error_string = talloc_asprintf(mem_ctx,
571 "SAMR LookupDomain failed: %s",
573 talloc_free(tmp_ctx);
576 connect_with_info->out.domain_sid = l.out.sid;
579 /* prepare samr_OpenDomain */
580 ZERO_STRUCT(d_handle);
581 od.in.connect_handle = &p_handle;
582 od.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
583 od.in.sid = connect_with_info->out.domain_sid;
584 od.out.domain_handle = &d_handle;
586 /* do a samr_OpenDomain to get a domain handle */
587 status = dcerpc_samr_OpenDomain(samr_pipe, tmp_ctx, &od);
588 if (!NT_STATUS_IS_OK(status)) {
589 r->out.error_string = talloc_asprintf(mem_ctx,
590 "samr_OpenDomain for [%s] failed: %s",
591 dom_sid_string(tmp_ctx, connect_with_info->out.domain_sid),
593 talloc_free(tmp_ctx);
597 /* prepare samr_CreateUser2 */
598 ZERO_STRUCTP(u_handle);
599 cu.in.domain_handle = &d_handle;
600 cu.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
601 samr_account_name.string = r->in.account_name;
602 cu.in.account_name = &samr_account_name;
603 cu.in.acct_flags = r->in.acct_type;
604 cu.out.user_handle = u_handle;
606 cu.out.access_granted = &access_granted;
608 /* do a samr_CreateUser2 to get an account handle, or an error */
609 cu_status = dcerpc_samr_CreateUser2(samr_pipe, tmp_ctx, &cu);
611 if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
612 /* prepare samr_LookupNames */
613 ln.in.domain_handle = &d_handle;
615 ln.in.names = talloc_array(tmp_ctx, struct lsa_String, 1);
617 ln.out.types = &types;
619 r->out.error_string = NULL;
620 talloc_free(tmp_ctx);
621 return NT_STATUS_NO_MEMORY;
623 ln.in.names[0].string = r->in.account_name;
625 /* 5. do a samr_LookupNames to get the users rid */
626 status = dcerpc_samr_LookupNames(samr_pipe, tmp_ctx, &ln);
627 if (!NT_STATUS_IS_OK(status)) {
628 r->out.error_string = talloc_asprintf(mem_ctx,
629 "samr_LookupNames for [%s] failed: %s",
632 talloc_free(tmp_ctx);
636 /* check if we got one RID for the user */
637 if (ln.out.rids->count != 1) {
638 r->out.error_string = talloc_asprintf(mem_ctx,
639 "samr_LookupNames for [%s] returns %d RIDs",
640 r->in.account_name, ln.out.rids->count);
641 talloc_free(tmp_ctx);
642 return NT_STATUS_INVALID_PARAMETER;
645 /* prepare samr_OpenUser */
646 ZERO_STRUCTP(u_handle);
647 ou.in.domain_handle = &d_handle;
648 ou.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
649 ou.in.rid = ln.out.rids->ids[0];
651 ou.out.user_handle = u_handle;
653 /* 6. do a samr_OpenUser to get a user handle */
654 status = dcerpc_samr_OpenUser(samr_pipe, tmp_ctx, &ou);
655 if (!NT_STATUS_IS_OK(status)) {
656 r->out.error_string = talloc_asprintf(mem_ctx,
657 "samr_OpenUser for [%s] failed: %s",
660 talloc_free(tmp_ctx);
664 if (r->in.recreate_account) {
665 struct samr_DeleteUser d;
666 d.in.user_handle = u_handle;
667 d.out.user_handle = u_handle;
668 status = dcerpc_samr_DeleteUser(samr_pipe, mem_ctx, &d);
669 if (!NT_STATUS_IS_OK(status)) {
670 r->out.error_string = talloc_asprintf(mem_ctx,
671 "samr_DeleteUser (for recreate) of [%s] failed: %s",
674 talloc_free(tmp_ctx);
678 /* We want to recreate, so delete and another samr_CreateUser2 */
680 /* &cu filled in above */
681 status = dcerpc_samr_CreateUser2(samr_pipe, tmp_ctx, &cu);
682 if (!NT_STATUS_IS_OK(status)) {
683 r->out.error_string = talloc_asprintf(mem_ctx,
684 "samr_CreateUser2 (recreate) for [%s] failed: %s",
685 r->in.account_name, nt_errstr(status));
686 talloc_free(tmp_ctx);
690 } else if (!NT_STATUS_IS_OK(status)) {
691 r->out.error_string = talloc_asprintf(mem_ctx,
692 "samr_CreateUser2 for [%s] failed: %s",
693 r->in.account_name, nt_errstr(status));
694 talloc_free(tmp_ctx);
698 /* prepare samr_QueryUserInfo (get flags) */
699 qui.in.user_handle = u_handle;
702 status = dcerpc_samr_QueryUserInfo(samr_pipe, tmp_ctx, &qui);
703 if (!NT_STATUS_IS_OK(status)) {
704 r->out.error_string = talloc_asprintf(mem_ctx,
705 "samr_QueryUserInfo for [%s] failed: %s",
708 talloc_free(tmp_ctx);
713 status = NT_STATUS_INVALID_PARAMETER;
715 = talloc_asprintf(mem_ctx,
716 "samr_QueryUserInfo failed to return qui.out.info for [%s]: %s",
717 r->in.account_name, nt_errstr(status));
718 talloc_free(tmp_ctx);
722 old_acct_flags = (qui.out.info->info16.acct_flags & (ACB_WSTRUST | ACB_SVRTRUST | ACB_DOMTRUST));
723 /* Possibly bail if the account is of the wrong type */
725 != r->in.acct_type) {
726 const char *old_account_type, *new_account_type;
727 switch (old_acct_flags) {
729 old_account_type = "domain member (member)";
732 old_account_type = "domain controller (bdc)";
735 old_account_type = "trusted domain";
738 return NT_STATUS_INVALID_PARAMETER;
740 switch (r->in.acct_type) {
742 new_account_type = "domain member (member)";
745 new_account_type = "domain controller (bdc)";
748 new_account_type = "trusted domain";
751 return NT_STATUS_INVALID_PARAMETER;
754 if (!NT_STATUS_EQUAL(cu_status, NT_STATUS_USER_EXISTS)) {
755 /* We created a new user, but they didn't come out the right type?!? */
757 = talloc_asprintf(mem_ctx,
758 "We asked to create a new machine account (%s) of type %s, "
759 "but we got an account of type %s. This is unexpected. "
760 "Perhaps delete the account and try again.",
761 r->in.account_name, new_account_type, old_account_type);
762 talloc_free(tmp_ctx);
763 return NT_STATUS_INVALID_PARAMETER;
765 /* The account is of the wrong type, so bail */
767 /* TODO: We should allow a --force option to override, and redo this from the top setting r.in.recreate_account */
769 = talloc_asprintf(mem_ctx,
770 "The machine account (%s) already exists in the domain %s, "
771 "but is a %s. You asked to join as a %s. Please delete "
772 "the account and try again.",
773 r->in.account_name, connect_with_info->out.domain_name, old_account_type, new_account_type);
774 talloc_free(tmp_ctx);
775 return NT_STATUS_USER_EXISTS;
778 acct_flags = qui.out.info->info16.acct_flags;
781 acct_flags = (acct_flags & ~(ACB_DISABLED|ACB_PWNOTREQ));
783 /* Find out what password policy this user has */
784 pwp.in.user_handle = u_handle;
785 pwp.out.info = &info;
787 status = dcerpc_samr_GetUserPwInfo(samr_pipe, tmp_ctx, &pwp);
788 if (NT_STATUS_IS_OK(status)) {
789 policy_min_pw_len = pwp.out.info->min_password_length;
792 /* Grab a password of that minimum length */
794 password_str = generate_random_str(tmp_ctx, MAX(8, policy_min_pw_len));
796 /* set full_name and reset flags */
797 ZERO_STRUCT(u_info21);
798 u_info21.full_name.string = r->in.account_name;
799 u_info21.acct_flags = acct_flags;
800 u_info21.fields_present = SAMR_FIELD_FULL_NAME | SAMR_FIELD_ACCT_FLAGS;
802 r2.samr_handle.level = LIBNET_SET_PASSWORD_SAMR_HANDLE;
803 r2.samr_handle.in.account_name = r->in.account_name;
804 r2.samr_handle.in.newpassword = password_str;
805 r2.samr_handle.in.user_handle = u_handle;
806 r2.samr_handle.in.dcerpc_pipe = samr_pipe;
807 r2.samr_handle.in.info21 = &u_info21;
809 status = libnet_SetPassword(ctx, tmp_ctx, &r2);
810 if (!NT_STATUS_IS_OK(status)) {
811 r->out.error_string = talloc_steal(mem_ctx, r2.samr_handle.out.error_string);
812 talloc_free(tmp_ctx);
816 account_sid = dom_sid_add_rid(mem_ctx, connect_with_info->out.domain_sid, rid);
818 r->out.error_string = NULL;
819 talloc_free(tmp_ctx);
820 return NT_STATUS_NO_MEMORY;
823 /* Finish out by pushing various bits of status data out for the caller to use */
824 r->out.join_password = password_str;
825 talloc_steal(mem_ctx, r->out.join_password);
827 r->out.domain_sid = connect_with_info->out.domain_sid;
828 talloc_steal(mem_ctx, r->out.domain_sid);
830 r->out.account_sid = account_sid;
831 talloc_steal(mem_ctx, r->out.account_sid);
833 r->out.domain_name = connect_with_info->out.domain_name;
834 talloc_steal(mem_ctx, r->out.domain_name);
835 r->out.realm = connect_with_info->out.realm;
836 talloc_steal(mem_ctx, r->out.realm);
837 r->out.samr_pipe = samr_pipe;
838 talloc_steal(mem_ctx, samr_pipe);
839 r->out.samr_binding = samr_pipe->binding;
840 talloc_steal(mem_ctx, r->out.samr_binding);
841 r->out.user_handle = u_handle;
842 talloc_steal(mem_ctx, u_handle);
843 r->out.error_string = r2.samr_handle.out.error_string;
844 talloc_steal(mem_ctx, r2.samr_handle.out.error_string);
846 r->out.server_dn_str = NULL;
847 talloc_free(tmp_ctx);
849 /* Now, if it was AD, then we want to start looking changing a
850 * few more things. Otherwise, we are done. */
852 status = libnet_JoinADSDomain(ctx, r);
859 NTSTATUS libnet_set_join_secrets(struct libnet_context *ctx,
861 struct libnet_set_join_secrets *r)
865 struct ldb_context *ldb;
866 struct ldb_dn *base_dn;
867 struct ldb_message **msgs, *msg;
869 const char * const attrs[] = {
879 tmp_mem = talloc_new(mem_ctx);
881 return NT_STATUS_NO_MEMORY;
884 /* Open the secrets database */
885 ldb = secrets_db_connect(tmp_mem, ctx->event_ctx, ctx->lp_ctx);
888 = talloc_asprintf(mem_ctx,
889 "Could not open secrets database");
890 talloc_free(tmp_mem);
891 return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
895 * now prepare the record for secrets.ldb
897 sct = talloc_asprintf(tmp_mem, "%d", r->in.join_type);
899 r->out.error_string = NULL;
900 talloc_free(tmp_mem);
901 return NT_STATUS_NO_MEMORY;
904 msg = ldb_msg_new(tmp_mem);
906 r->out.error_string = NULL;
907 talloc_free(tmp_mem);
908 return NT_STATUS_NO_MEMORY;
911 base_dn = ldb_dn_new(tmp_mem, ldb, "cn=Primary Domains");
913 r->out.error_string = NULL;
914 talloc_free(tmp_mem);
915 return NT_STATUS_NO_MEMORY;
918 msg->dn = ldb_dn_copy(tmp_mem, base_dn);
919 if ( ! ldb_dn_add_child_fmt(msg->dn, "flatname=%s", r->in.domain_name)) {
920 r->out.error_string = NULL;
921 talloc_free(tmp_mem);
922 return NT_STATUS_NO_MEMORY;
925 rtn = samdb_msg_add_string(ldb, tmp_mem, msg, "flatname", r->in.domain_name);
927 r->out.error_string = NULL;
928 talloc_free(tmp_mem);
929 return NT_STATUS_NO_MEMORY;
933 rtn = samdb_msg_add_string(ldb, tmp_mem, msg, "realm", r->in.realm);
935 r->out.error_string = NULL;
936 talloc_free(tmp_mem);
937 return NT_STATUS_NO_MEMORY;
940 rtn = samdb_msg_add_string(ldb, tmp_mem, msg, "objectClass", "primaryDomain");
942 r->out.error_string = NULL;
943 talloc_free(tmp_mem);
944 return NT_STATUS_NO_MEMORY;
948 rtn = samdb_msg_add_string(ldb, tmp_mem, msg, "objectClass", "primaryDomain");
950 r->out.error_string = NULL;
951 talloc_free(tmp_mem);
952 return NT_STATUS_NO_MEMORY;
955 rtn = samdb_msg_add_string(ldb, tmp_mem, msg, "secret", r->in.join_password);
957 r->out.error_string = NULL;
958 talloc_free(tmp_mem);
959 return NT_STATUS_NO_MEMORY;
962 rtn = samdb_msg_add_string(ldb, tmp_mem, msg, "samAccountName", r->in.account_name);
964 r->out.error_string = NULL;
965 talloc_free(tmp_mem);
966 return NT_STATUS_NO_MEMORY;
969 rtn = samdb_msg_add_string(ldb, tmp_mem, msg, "secureChannelType", sct);
971 r->out.error_string = NULL;
972 talloc_free(tmp_mem);
973 return NT_STATUS_NO_MEMORY;
977 rtn = samdb_msg_add_uint(ldb, tmp_mem, msg, "msDS-KeyVersionNumber",
980 r->out.error_string = NULL;
981 talloc_free(tmp_mem);
982 return NT_STATUS_NO_MEMORY;
986 if (r->in.domain_sid) {
987 rtn = samdb_msg_add_dom_sid(ldb, tmp_mem, msg, "objectSid",
990 r->out.error_string = NULL;
991 talloc_free(tmp_mem);
992 return NT_STATUS_NO_MEMORY;
997 * search for the secret record
998 * - remove the records we find
999 * - and fetch the old secret and store it under priorSecret
1001 ret = gendb_search(ldb,
1004 "(|" SECRETS_PRIMARY_DOMAIN_FILTER "(realm=%s))",
1005 r->in.domain_name, r->in.realm);
1007 rtn = samdb_msg_set_string(ldb, tmp_mem, msg, "secretsKeytab", "secrets.keytab");
1009 r->out.error_string = NULL;
1010 talloc_free(tmp_mem);
1011 return NT_STATUS_NO_MEMORY;
1013 } else if (ret == -1) {
1015 = talloc_asprintf(mem_ctx,
1016 "Search for domain: %s and realm: %s failed: %s",
1017 r->in.domain_name, r->in.realm, ldb_errstring(ldb));
1018 talloc_free(tmp_mem);
1019 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1021 const struct ldb_val *private_keytab;
1022 const struct ldb_val *krb5_main_keytab;
1023 const struct ldb_val *prior_secret;
1024 const struct ldb_val *prior_modified_time;
1027 for (i = 0; i < ret; i++) {
1028 ldb_delete(ldb, msgs[i]->dn);
1031 prior_secret = ldb_msg_find_ldb_val(msgs[0], "secret");
1033 rtn = samdb_msg_set_value(ldb, tmp_mem, msg, "priorSecret", prior_secret);
1035 r->out.error_string = NULL;
1036 talloc_free(tmp_mem);
1037 return NT_STATUS_NO_MEMORY;
1040 rtn = samdb_msg_set_string(ldb, tmp_mem, msg, "secret", r->in.join_password);
1042 r->out.error_string = NULL;
1043 talloc_free(tmp_mem);
1044 return NT_STATUS_NO_MEMORY;
1047 prior_modified_time = ldb_msg_find_ldb_val(msgs[0],
1049 if (prior_modified_time) {
1050 rtn = samdb_msg_set_value(ldb, tmp_mem, msg, "priorWhenChanged",
1051 prior_modified_time);
1053 r->out.error_string = NULL;
1054 talloc_free(tmp_mem);
1055 return NT_STATUS_NO_MEMORY;
1059 rtn = samdb_msg_set_string(ldb, tmp_mem, msg, "samAccountName", r->in.account_name);
1061 r->out.error_string = NULL;
1062 talloc_free(tmp_mem);
1063 return NT_STATUS_NO_MEMORY;
1066 rtn = samdb_msg_set_string(ldb, tmp_mem, msg, "secureChannelType", sct);
1068 r->out.error_string = NULL;
1069 talloc_free(tmp_mem);
1070 return NT_STATUS_NO_MEMORY;
1073 /* We will want to keep the keytab names */
1074 private_keytab = ldb_msg_find_ldb_val(msgs[0], "privateKeytab");
1075 if (private_keytab) {
1076 rtn = samdb_msg_set_value(ldb, tmp_mem, msg, "privateKeytab", private_keytab);
1078 r->out.error_string = NULL;
1079 talloc_free(tmp_mem);
1080 return NT_STATUS_NO_MEMORY;
1083 krb5_main_keytab = ldb_msg_find_ldb_val(msgs[0], "krb5Keytab");
1084 if (krb5_main_keytab) {
1085 rtn = samdb_msg_set_value(ldb, tmp_mem, msg,
1086 "krb5Keytab", krb5_main_keytab);
1088 r->out.error_string = NULL;
1089 talloc_free(tmp_mem);
1090 return NT_STATUS_NO_MEMORY;
1095 /* create the secret */
1096 ret = ldb_add(ldb, msg);
1098 r->out.error_string = talloc_asprintf(mem_ctx, "Failed to create secret record %s",
1099 ldb_dn_get_linearized(msg->dn));
1100 talloc_free(tmp_mem);
1101 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1104 return NT_STATUS_OK;
1107 static NTSTATUS libnet_Join_primary_domain(struct libnet_context *ctx,
1108 TALLOC_CTX *mem_ctx,
1109 struct libnet_Join *r)
1112 TALLOC_CTX *tmp_mem;
1113 struct libnet_JoinDomain *r2;
1114 struct libnet_set_join_secrets *r3;
1115 uint32_t acct_type = 0;
1116 const char *account_name;
1117 const char *netbios_name;
1119 r->out.error_string = NULL;
1121 tmp_mem = talloc_new(mem_ctx);
1123 return NT_STATUS_NO_MEMORY;
1126 r2 = talloc(tmp_mem, struct libnet_JoinDomain);
1128 r->out.error_string = NULL;
1129 talloc_free(tmp_mem);
1130 return NT_STATUS_NO_MEMORY;
1133 if (r->in.join_type == SEC_CHAN_BDC) {
1134 acct_type = ACB_SVRTRUST;
1135 } else if (r->in.join_type == SEC_CHAN_WKSTA) {
1136 acct_type = ACB_WSTRUST;
1138 r->out.error_string = NULL;
1139 talloc_free(tmp_mem);
1140 return NT_STATUS_INVALID_PARAMETER;
1143 if (r->in.netbios_name != NULL) {
1144 netbios_name = r->in.netbios_name;
1146 netbios_name = talloc_reference(tmp_mem, lp_netbios_name(ctx->lp_ctx));
1147 if (!netbios_name) {
1148 r->out.error_string = NULL;
1149 talloc_free(tmp_mem);
1150 return NT_STATUS_NO_MEMORY;
1154 account_name = talloc_asprintf(tmp_mem, "%s$", netbios_name);
1155 if (!account_name) {
1156 r->out.error_string = NULL;
1157 talloc_free(tmp_mem);
1158 return NT_STATUS_NO_MEMORY;
1165 r2->in.domain_name = r->in.domain_name;
1166 r2->in.account_name = account_name;
1167 r2->in.netbios_name = netbios_name;
1168 r2->in.level = LIBNET_JOINDOMAIN_AUTOMATIC;
1169 r2->in.acct_type = acct_type;
1170 r2->in.recreate_account = false;
1171 status = libnet_JoinDomain(ctx, r2, r2);
1172 if (!NT_STATUS_IS_OK(status)) {
1173 r->out.error_string = talloc_steal(mem_ctx, r2->out.error_string);
1174 talloc_free(tmp_mem);
1178 r3 = talloc(tmp_mem, struct libnet_set_join_secrets);
1180 r->out.error_string = NULL;
1181 talloc_free(tmp_mem);
1182 return NT_STATUS_NO_MEMORY;
1186 r3->in.domain_name = r2->out.domain_name;
1187 r3->in.realm = r2->out.realm;
1188 r3->in.account_name = account_name;
1189 r3->in.netbios_name = netbios_name;
1190 r3->in.join_type = r->in.join_type;
1191 r3->in.join_password = r2->out.join_password;
1192 r3->in.kvno = r2->out.kvno;
1193 r3->in.domain_sid = r2->out.domain_sid;
1195 status = libnet_set_join_secrets(ctx, r3, r3);
1196 if (!NT_STATUS_IS_OK(status)) {
1197 r->out.error_string = talloc_steal(mem_ctx, r3->out.error_string);
1198 talloc_free(tmp_mem);
1202 /* move all out parameter to the callers TALLOC_CTX */
1203 r->out.error_string = NULL;
1204 r->out.join_password = r2->out.join_password;
1205 talloc_steal(mem_ctx, r2->out.join_password);
1206 r->out.domain_sid = r2->out.domain_sid;
1207 talloc_steal(mem_ctx, r2->out.domain_sid);
1208 r->out.domain_name = r2->out.domain_name;
1209 talloc_steal(mem_ctx, r2->out.domain_name);
1210 talloc_free(tmp_mem);
1211 return NT_STATUS_OK;
1214 NTSTATUS libnet_Join(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_Join *r)
1216 switch (r->in.join_type) {
1217 case SEC_CHAN_WKSTA:
1218 return libnet_Join_primary_domain(ctx, mem_ctx, r);
1220 return libnet_Join_primary_domain(ctx, mem_ctx, r);
1221 case SEC_CHAN_DOMAIN:
1225 r->out.error_string = talloc_asprintf(mem_ctx,
1226 "Invalid join type specified (%08X) attempting to join domain %s",
1227 r->in.join_type, r->in.domain_name);
1228 return NT_STATUS_INVALID_PARAMETER;