1 /*
2    Unix SMB/CIFS implementation.
3 
4    Copyright (C) Stefan Metzmacher	2004
5    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
6    Copyright (C) Brad Henry 2005
7 
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.
12 
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.
17 
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/>.
20 */
21 
22 #include "includes.h"
23 #include "libnet/libnet.h"
24 #include "librpc/gen_ndr/ndr_drsuapi_c.h"
25 #include <ldb.h>
26 #include <ldb_errors.h>
27 #include "dsdb/samdb/samdb.h"
28 #include "ldb_wrap.h"
29 #include "libcli/security/security.h"
30 #include "auth/credentials/credentials.h"
31 #include "auth/credentials/credentials_krb5.h"
32 #include "librpc/gen_ndr/ndr_samr_c.h"
33 #include "param/param.h"
34 #include "param/provision.h"
35 #include "system/kerberos.h"
36 #include "auth/kerberos/kerberos.h"
37 
38 /*
39  * complete a domain join, when joining to a AD domain:
40  * 1.) connect and bind to the DRSUAPI pipe
41  * 2.) do a DsCrackNames() to find the machine account dn
42  * 3.) connect to LDAP
43  * 4.) do an ldap search to find the "msDS-KeyVersionNumber" of the machine account
44  * 5.) set the servicePrincipalName's of the machine account via LDAP, (maybe we should use DsWriteAccountSpn()...)
45  * 6.) do a DsCrackNames() to find the domain dn
46  * 7.) find out Site specific stuff, look at libnet_JoinSite() for details
47  */
libnet_JoinADSDomain(struct libnet_context * ctx,struct libnet_JoinDomain * r)48 static NTSTATUS libnet_JoinADSDomain(struct libnet_context *ctx, struct libnet_JoinDomain *r)
49 {
50 	NTSTATUS status;
51 
52 	TALLOC_CTX *tmp_ctx;
53 
54 	const char *realm = r->out.realm;
55 
56 	const struct dcerpc_binding *samr_binding = r->out.samr_binding;
57 
58 	struct dcerpc_pipe *drsuapi_pipe;
59 	struct dcerpc_binding *drsuapi_binding;
60 	enum dcerpc_transport_t transport;
61 	struct drsuapi_DsBind r_drsuapi_bind;
62 	struct drsuapi_DsCrackNames r_crack_names;
63 	struct drsuapi_DsNameString names[1];
64 	struct policy_handle drsuapi_bind_handle;
65 	struct GUID drsuapi_bind_guid;
66 
67 	struct ldb_context *remote_ldb;
68 	struct ldb_dn *account_dn;
69 	const char *account_dn_str;
70 	const char *remote_ldb_url;
71 	struct ldb_result *res;
72 	struct ldb_message *msg;
73 
74 	int ret, rtn;
75 
76 	const char * const attrs[] = {
77 		"msDS-KeyVersionNumber",
78 		"servicePrincipalName",
79 		"dNSHostName",
80 		"objectGUID",
81 		NULL,
82 	};
83 
84 	r->out.error_string = NULL;
85 
86 	/* We need to convert between a samAccountName and domain to a
87 	 * DN in the directory.  The correct way to do this is with
88 	 * DRSUAPI CrackNames */
89 
90 	/* Fiddle with the bindings, so get to DRSUAPI on
91 	 * NCACN_IP_TCP, sealed */
92 	tmp_ctx = talloc_named(r, 0, "libnet_JoinADSDomain temp context");
93 	if (!tmp_ctx) {
94 		r->out.error_string = NULL;
95 		return NT_STATUS_NO_MEMORY;
96 	}
97 
98 	drsuapi_binding = dcerpc_binding_dup(tmp_ctx, samr_binding);
99 	if (!drsuapi_binding) {
100 		r->out.error_string = NULL;
101 		talloc_free(tmp_ctx);
102 		return NT_STATUS_NO_MEMORY;
103 	}
104 
105 	transport = dcerpc_binding_get_transport(drsuapi_binding);
106 
107 	/* DRSUAPI is only available on IP_TCP, and locally on NCALRPC */
108 	if (transport != NCALRPC) {
109 		status = dcerpc_binding_set_transport(drsuapi_binding, NCACN_IP_TCP);
110 		if (!NT_STATUS_IS_OK(status)) {
111 			r->out.error_string = talloc_asprintf(r,
112 						"dcerpc_binding_set_transport failed: %s",
113 						nt_errstr(status));
114 			talloc_free(tmp_ctx);
115 			return status;
116 		}
117 	}
118 
119 	status = dcerpc_binding_set_string_option(drsuapi_binding, "endpoint", NULL);
120 	if (!NT_STATUS_IS_OK(status)) {
121 		r->out.error_string = talloc_asprintf(r,
122 					"dcerpc_binding_set_string_option failed: %s",
123 					nt_errstr(status));
124 		talloc_free(tmp_ctx);
125 		return status;
126 	}
127 
128 	status = dcerpc_binding_set_flags(drsuapi_binding, DCERPC_SEAL, 0);
129 	if (!NT_STATUS_IS_OK(status)) {
130 		r->out.error_string = talloc_asprintf(r,
131 					"dcerpc_binding_set_flags failed: %s",
132 					nt_errstr(status));
133 		talloc_free(tmp_ctx);
134 		return status;
135 	}
136 
137 	status = dcerpc_pipe_connect_b(tmp_ctx,
138 				       &drsuapi_pipe,
139 				       drsuapi_binding,
140 				       &ndr_table_drsuapi,
141 				       ctx->cred,
142 				       ctx->event_ctx,
143 				       ctx->lp_ctx);
144 	if (!NT_STATUS_IS_OK(status)) {
145 		r->out.error_string = talloc_asprintf(r,
146 					"Connection to DRSUAPI pipe of PDC of domain '%s' failed: %s",
147 					r->out.domain_name,
148 					nt_errstr(status));
149 		talloc_free(tmp_ctx);
150 		return status;
151 	}
152 
153 	/* get a DRSUAPI pipe handle */
154 	GUID_from_string(DRSUAPI_DS_BIND_GUID, &drsuapi_bind_guid);
155 
156 	r_drsuapi_bind.in.bind_guid = &drsuapi_bind_guid;
157 	r_drsuapi_bind.in.bind_info = NULL;
158 	r_drsuapi_bind.out.bind_handle = &drsuapi_bind_handle;
159 
160 	status = dcerpc_drsuapi_DsBind_r(drsuapi_pipe->binding_handle, tmp_ctx, &r_drsuapi_bind);
161 	if (!NT_STATUS_IS_OK(status)) {
162 		r->out.error_string
163 			= talloc_asprintf(r,
164 					  "dcerpc_drsuapi_DsBind failed - %s",
165 					  nt_errstr(status));
166 		talloc_free(tmp_ctx);
167 		return status;
168 	} else if (!W_ERROR_IS_OK(r_drsuapi_bind.out.result)) {
169 		r->out.error_string
170 				= talloc_asprintf(r,
171 						  "DsBind failed - %s",
172 						  win_errstr(r_drsuapi_bind.out.result));
173 			talloc_free(tmp_ctx);
174 		return NT_STATUS_UNSUCCESSFUL;
175 	}
176 
177 	/* Actually 'crack' the names */
178 	ZERO_STRUCT(r_crack_names);
179 	r_crack_names.in.bind_handle		= &drsuapi_bind_handle;
180 	r_crack_names.in.level			= 1;
181 	r_crack_names.in.req			= talloc(r, union drsuapi_DsNameRequest);
182 	if (!r_crack_names.in.req) {
183 		r->out.error_string = NULL;
184 		talloc_free(tmp_ctx);
185 		return NT_STATUS_NO_MEMORY;
186 	}
187 	r_crack_names.in.req->req1.codepage	= 1252; /* western european */
188 	r_crack_names.in.req->req1.language	= 0x00000407; /* german */
189 	r_crack_names.in.req->req1.count	= 1;
190 	r_crack_names.in.req->req1.names	= names;
191 	r_crack_names.in.req->req1.format_flags	= DRSUAPI_DS_NAME_FLAG_NO_FLAGS;
192 	r_crack_names.in.req->req1.format_offered = DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY;
193 	r_crack_names.in.req->req1.format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779;
194 	names[0].str = dom_sid_string(tmp_ctx, r->out.account_sid);
195 	if (!names[0].str) {
196 		r->out.error_string = NULL;
197 		talloc_free(tmp_ctx);
198 		return NT_STATUS_NO_MEMORY;
199 	}
200 
201 	r_crack_names.out.ctr			= talloc(r, union drsuapi_DsNameCtr);
202 	r_crack_names.out.level_out		= talloc(r, uint32_t);
203 	if (!r_crack_names.out.ctr || !r_crack_names.out.level_out) {
204 		r->out.error_string = NULL;
205 		talloc_free(tmp_ctx);
206 		return NT_STATUS_NO_MEMORY;
207 	}
208 
209 	status = dcerpc_drsuapi_DsCrackNames_r(drsuapi_pipe->binding_handle, tmp_ctx, &r_crack_names);
210 	if (!NT_STATUS_IS_OK(status)) {
211 		r->out.error_string
212 			= talloc_asprintf(r,
213 					  "dcerpc_drsuapi_DsCrackNames for [%s] failed - %s",
214 					  names[0].str,
215 					  nt_errstr(status));
216 		talloc_free(tmp_ctx);
217 		return status;
218 	} else if (!W_ERROR_IS_OK(r_crack_names.out.result)) {
219 		r->out.error_string
220 				= talloc_asprintf(r,
221 						  "DsCrackNames failed - %s", win_errstr(r_crack_names.out.result));
222 		talloc_free(tmp_ctx);
223 		return NT_STATUS_UNSUCCESSFUL;
224 	} else if (*r_crack_names.out.level_out != 1
225 		   || !r_crack_names.out.ctr->ctr1
226 		   || r_crack_names.out.ctr->ctr1->count != 1) {
227 		r->out.error_string = talloc_asprintf(r, "DsCrackNames failed");
228 		talloc_free(tmp_ctx);
229 		return NT_STATUS_INVALID_PARAMETER;
230 	} else if (r_crack_names.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) {
231 		r->out.error_string = talloc_asprintf(r, "DsCrackNames failed: %d", r_crack_names.out.ctr->ctr1->array[0].status);
232 		talloc_free(tmp_ctx);
233 		return NT_STATUS_UNSUCCESSFUL;
234 	} else if (r_crack_names.out.ctr->ctr1->array[0].result_name == NULL) {
235 		r->out.error_string = talloc_asprintf(r, "DsCrackNames failed: no result name");
236 		talloc_free(tmp_ctx);
237 		return NT_STATUS_INVALID_PARAMETER;
238 	}
239 
240 	/* Store the DN of our machine account. */
241 	account_dn_str = r_crack_names.out.ctr->ctr1->array[0].result_name;
242 
243 	/* Now we know the user's DN, open with LDAP, read and modify a few things */
244 
245 	remote_ldb_url = talloc_asprintf(tmp_ctx, "ldap://%s",
246 		dcerpc_binding_get_string_option(drsuapi_binding, "target_hostname"));
247 	if (!remote_ldb_url) {
248 		r->out.error_string = NULL;
249 		talloc_free(tmp_ctx);
250 		return NT_STATUS_NO_MEMORY;
251 	}
252 
253 	remote_ldb = ldb_wrap_connect(tmp_ctx, ctx->event_ctx, ctx->lp_ctx,
254 				      remote_ldb_url,
255 				      NULL, ctx->cred, 0);
256 	if (!remote_ldb) {
257 		r->out.error_string = NULL;
258 		talloc_free(tmp_ctx);
259 		return NT_STATUS_UNSUCCESSFUL;
260 	}
261 
262 	account_dn = ldb_dn_new(tmp_ctx, remote_ldb, account_dn_str);
263 	if (account_dn == NULL) {
264 		r->out.error_string = talloc_asprintf(r, "Invalid account dn: %s",
265 						      account_dn_str);
266 		talloc_free(tmp_ctx);
267 		return NT_STATUS_UNSUCCESSFUL;
268 	}
269 
270 	/* search for the user's record */
271 	ret = ldb_search(remote_ldb, tmp_ctx, &res,
272 			 account_dn, LDB_SCOPE_BASE, attrs, NULL);
273 	if (ret != LDB_SUCCESS) {
274 		r->out.error_string = talloc_asprintf(r, "ldb_search for %s failed - %s",
275 						      account_dn_str, ldb_errstring(remote_ldb));
276 		talloc_free(tmp_ctx);
277 		return NT_STATUS_UNSUCCESSFUL;
278 	}
279 
280 	if (res->count != 1) {
281 		r->out.error_string = talloc_asprintf(r, "ldb_search for %s failed - found %d entries",
282 						      account_dn_str, res->count);
283 		talloc_free(tmp_ctx);
284 		return NT_STATUS_UNSUCCESSFUL;
285 	}
286 
287 	/* Prepare a new message, for the modify */
288 	msg = ldb_msg_new(tmp_ctx);
289 	if (!msg) {
290 		r->out.error_string = NULL;
291 		talloc_free(tmp_ctx);
292 		return NT_STATUS_NO_MEMORY;
293 	}
294 	msg->dn = res->msgs[0]->dn;
295 
296 	{
297 		unsigned int i;
298 		const char *service_principal_name[2];
299 		const char *dns_host_name = strlower_talloc(msg,
300 							    talloc_asprintf(msg,
301 									    "%s.%s",
302 									    r->in.netbios_name,
303 									    realm));
304 
305 		if (!dns_host_name) {
306 			r->out.error_string = NULL;
307 			talloc_free(tmp_ctx);
308 			return NT_STATUS_NO_MEMORY;
309 		}
310 
311 		service_principal_name[0] = talloc_asprintf(msg, "HOST/%s",
312 							    dns_host_name);
313 		service_principal_name[1] = talloc_asprintf(msg, "HOST/%s",
314 							    r->in.netbios_name);
315 
316 		for (i=0; i < ARRAY_SIZE(service_principal_name); i++) {
317 			if (!service_principal_name[i]) {
318 				r->out.error_string = NULL;
319 				talloc_free(tmp_ctx);
320 				return NT_STATUS_NO_MEMORY;
321 			}
322 			rtn = ldb_msg_add_string(msg, "servicePrincipalName",
323 						 service_principal_name[i]);
324 			if (rtn != LDB_SUCCESS) {
325 				r->out.error_string = NULL;
326 				talloc_free(tmp_ctx);
327 				return NT_STATUS_NO_MEMORY;
328 			}
329 		}
330 
331 		rtn = ldb_msg_add_string(msg, "dNSHostName", dns_host_name);
332 		if (rtn != LDB_SUCCESS) {
333 			r->out.error_string = NULL;
334 			talloc_free(tmp_ctx);
335 			return NT_STATUS_NO_MEMORY;
336 		}
337 
338 		rtn = dsdb_replace(remote_ldb, msg, 0);
339 		if (rtn != LDB_SUCCESS) {
340 			r->out.error_string
341 				= talloc_asprintf(r,
342 						  "Failed to replace entries on %s",
343 						  ldb_dn_get_linearized(msg->dn));
344 			talloc_free(tmp_ctx);
345 			return NT_STATUS_INTERNAL_DB_CORRUPTION;
346 		}
347 	}
348 
349 	msg = ldb_msg_new(tmp_ctx);
350 	if (!msg) {
351 		r->out.error_string = NULL;
352 		talloc_free(tmp_ctx);
353 		return NT_STATUS_NO_MEMORY;
354 	}
355 	msg->dn = res->msgs[0]->dn;
356 
357 	rtn = samdb_msg_add_uint(remote_ldb, msg, msg,
358 				 "msDS-SupportedEncryptionTypes", ENC_ALL_TYPES);
359 	if (rtn != LDB_SUCCESS) {
360 		r->out.error_string = NULL;
361 		talloc_free(tmp_ctx);
362 		return NT_STATUS_NO_MEMORY;
363 	}
364 
365 	rtn = dsdb_replace(remote_ldb, msg, 0);
366 	/* The remote server may not support this attribute, if it
367 	 * isn't a modern schema */
368 	if (rtn != LDB_SUCCESS && rtn != LDB_ERR_NO_SUCH_ATTRIBUTE) {
369 		r->out.error_string
370 			= talloc_asprintf(r,
371 					  "Failed to replace msDS-SupportedEncryptionTypes on %s",
372 					  ldb_dn_get_linearized(msg->dn));
373 		talloc_free(tmp_ctx);
374 		return NT_STATUS_INTERNAL_DB_CORRUPTION;
375 	}
376 
377 	/* DsCrackNames to find out the DN of the domain. */
378 	r_crack_names.in.req->req1.format_offered = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT;
379 	r_crack_names.in.req->req1.format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779;
380 	names[0].str = talloc_asprintf(tmp_ctx, "%s\\", r->out.domain_name);
381 	if (!names[0].str) {
382 		r->out.error_string = NULL;
383 		talloc_free(tmp_ctx);
384 		return NT_STATUS_NO_MEMORY;
385 	}
386 
387 	status = dcerpc_drsuapi_DsCrackNames_r(drsuapi_pipe->binding_handle, tmp_ctx, &r_crack_names);
388 	if (!NT_STATUS_IS_OK(status)) {
389 		r->out.error_string
390 			= talloc_asprintf(r,
391 					  "dcerpc_drsuapi_DsCrackNames for [%s] failed - %s",
392 					  r->in.domain_name,
393 					  nt_errstr(status));
394 		talloc_free(tmp_ctx);
395 		return status;
396 	} else if (!W_ERROR_IS_OK(r_crack_names.out.result)) {
397 		r->out.error_string
398 			= talloc_asprintf(r,
399 					  "DsCrackNames failed - %s", win_errstr(r_crack_names.out.result));
400 		talloc_free(tmp_ctx);
401 		return NT_STATUS_UNSUCCESSFUL;
402 	} else if (*r_crack_names.out.level_out != 1
403 		   || !r_crack_names.out.ctr->ctr1
404 		   || r_crack_names.out.ctr->ctr1->count != 1
405 		   || !r_crack_names.out.ctr->ctr1->array[0].result_name
406 		   || r_crack_names.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) {
407 		r->out.error_string = talloc_asprintf(r, "DsCrackNames failed");
408 		talloc_free(tmp_ctx);
409 		return NT_STATUS_UNSUCCESSFUL;
410 	}
411 
412 	/* Store the account DN. */
413 	r->out.account_dn_str = account_dn_str;
414 	talloc_steal(r, account_dn_str);
415 
416 	/* Store the domain DN. */
417 	r->out.domain_dn_str = r_crack_names.out.ctr->ctr1->array[0].result_name;
418 	talloc_steal(r, r_crack_names.out.ctr->ctr1->array[0].result_name);
419 
420 	/* Store the KVNO of the account, critical for some kerberos
421 	 * operations */
422 	r->out.kvno = ldb_msg_find_attr_as_uint(res->msgs[0], "msDS-KeyVersionNumber", 0);
423 
424 	/* Store the account GUID. */
425 	r->out.account_guid = samdb_result_guid(res->msgs[0], "objectGUID");
426 
427 	if (r->in.acct_type == ACB_SVRTRUST) {
428 		status = libnet_JoinSite(ctx, remote_ldb, r);
429 	}
430 	talloc_free(tmp_ctx);
431 
432 	return status;
433 }
434 
435 /*
436  * do a domain join using DCERPC/SAMR calls
437  * - connect to the LSA pipe, to try and find out information about the domain
438  * - create a secondary connection to SAMR pipe
439  * - do a samr_Connect to get a policy handle
440  * - do a samr_LookupDomain to get the domain sid
441  * - do a samr_OpenDomain to get a domain handle
442  * - do a samr_CreateAccount to try and get a new account
443  *
444  * If that fails, do:
445  * - do a samr_LookupNames to get the users rid
446  * - do a samr_OpenUser to get a user handle
447  * - potentially delete and recreate the user
448  * - assert the account is of the right type with samrQueryUserInfo
449  *
450  * - call libnet_SetPassword_samr_handle to set the password,
451  *   and pass a samr_UserInfo21 struct to set full_name and the account flags
452  *
453  * - do some ADS specific things when we join as Domain Controller,
454  *    look at libnet_joinADSDomain() for the details
455  */
libnet_JoinDomain(struct libnet_context * ctx,TALLOC_CTX * mem_ctx,struct libnet_JoinDomain * r)456 NTSTATUS libnet_JoinDomain(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_JoinDomain *r)
457 {
458 	TALLOC_CTX *tmp_ctx;
459 
460 	NTSTATUS status, cu_status;
461 
462 	struct libnet_RpcConnect *connect_with_info;
463 	struct dcerpc_pipe *samr_pipe;
464 
465 	struct samr_Connect sc;
466 	struct policy_handle p_handle;
467 	struct samr_OpenDomain od;
468 	struct policy_handle d_handle;
469 	struct samr_LookupNames ln;
470 	struct samr_Ids rids, types;
471 	struct samr_OpenUser ou;
472 	struct samr_CreateUser2 cu;
473 	struct policy_handle *u_handle = NULL;
474 	struct samr_QueryUserInfo qui;
475 	union samr_UserInfo *uinfo;
476 	struct samr_UserInfo21 u_info21;
477 	union libnet_SetPassword r2;
478 	struct samr_GetUserPwInfo pwp;
479 	struct samr_PwInfo info;
480 	struct lsa_String samr_account_name;
481 
482 	uint32_t acct_flags, old_acct_flags;
483 	uint32_t rid, access_granted;
484 	int policy_min_pw_len = 0;
485 
486 	struct dom_sid *account_sid = NULL;
487 	const char *password_str = NULL;
488 
489 	r->out.error_string = NULL;
490 	r2.samr_handle.out.error_string = NULL;
491 
492 	tmp_ctx = talloc_named(mem_ctx, 0, "libnet_Join temp context");
493 	if (!tmp_ctx) {
494 		r->out.error_string = NULL;
495 		return NT_STATUS_NO_MEMORY;
496 	}
497 
498 	u_handle = talloc(tmp_ctx, struct policy_handle);
499 	if (!u_handle) {
500 		r->out.error_string = NULL;
501 		talloc_free(tmp_ctx);
502 		return NT_STATUS_NO_MEMORY;
503 	}
504 
505 	connect_with_info = talloc_zero(tmp_ctx, struct libnet_RpcConnect);
506 	if (!connect_with_info) {
507 		r->out.error_string = NULL;
508 		talloc_free(tmp_ctx);
509 		return NT_STATUS_NO_MEMORY;
510 	}
511 
512 	/* prepare connect to the SAMR pipe of PDC */
513 	if (r->in.level == LIBNET_JOINDOMAIN_AUTOMATIC) {
514 		connect_with_info->in.binding = NULL;
515 		connect_with_info->in.name    = r->in.domain_name;
516 	} else {
517 		connect_with_info->in.binding = r->in.binding;
518 		connect_with_info->in.name    = NULL;
519 	}
520 
521 	/* This level makes a connection to the LSA pipe on the way,
522 	 * to get some useful bits of information about the domain */
523 	connect_with_info->level              = LIBNET_RPC_CONNECT_DC_INFO;
524 	connect_with_info->in.dcerpc_iface    = &ndr_table_samr;
525 
526 	/*
527 	  establish the SAMR connection
528 	*/
529 	status = libnet_RpcConnect(ctx, tmp_ctx, connect_with_info);
530 	if (!NT_STATUS_IS_OK(status)) {
531 		if (r->in.binding) {
532 			r->out.error_string = talloc_asprintf(mem_ctx,
533 							      "Connection to SAMR pipe of DC %s failed: %s",
534 							      r->in.binding, connect_with_info->out.error_string);
535 		} else {
536 			r->out.error_string = talloc_asprintf(mem_ctx,
537 							      "Connection to SAMR pipe of PDC for %s failed: %s",
538 							      r->in.domain_name, connect_with_info->out.error_string);
539 		}
540 		talloc_free(tmp_ctx);
541 		return status;
542 	}
543 
544 	samr_pipe = connect_with_info->out.dcerpc_pipe;
545 
546 	/* prepare samr_Connect */
547 	ZERO_STRUCT(p_handle);
548 	sc.in.system_name = NULL;
549 	sc.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
550 	sc.out.connect_handle = &p_handle;
551 
552 	/* 2. do a samr_Connect to get a policy handle */
553 	status = dcerpc_samr_Connect_r(samr_pipe->binding_handle, tmp_ctx, &sc);
554 	if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(sc.out.result)) {
555 		status = sc.out.result;
556 	}
557 	if (!NT_STATUS_IS_OK(status)) {
558 		r->out.error_string = talloc_asprintf(mem_ctx,
559 						"samr_Connect failed: %s",
560 						nt_errstr(status));
561 		talloc_free(tmp_ctx);
562 		return status;
563 	}
564 
565 	/* If this is a connection on ncacn_ip_tcp to Win2k3 SP1, we don't get back this useful info */
566 	if (!connect_with_info->out.domain_name) {
567 		if (r->in.level == LIBNET_JOINDOMAIN_AUTOMATIC) {
568 			connect_with_info->out.domain_name = talloc_strdup(tmp_ctx, r->in.domain_name);
569 		} else {
570 			/* Bugger, we just lost our way to automatically find the domain name */
571 			connect_with_info->out.domain_name = talloc_strdup(tmp_ctx, lpcfg_workgroup(ctx->lp_ctx));
572 			connect_with_info->out.realm = talloc_strdup(tmp_ctx, lpcfg_realm(ctx->lp_ctx));
573 		}
574 	}
575 
576 	/* Perhaps we didn't get a SID above, because we are against ncacn_ip_tcp */
577 	if (!connect_with_info->out.domain_sid) {
578 		struct lsa_String name;
579 		struct samr_LookupDomain l;
580 		struct dom_sid2 *sid = NULL;
581 		name.string = connect_with_info->out.domain_name;
582 		l.in.connect_handle = &p_handle;
583 		l.in.domain_name = &name;
584 		l.out.sid = &sid;
585 
586 		status = dcerpc_samr_LookupDomain_r(samr_pipe->binding_handle, tmp_ctx, &l);
587 		if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(l.out.result)) {
588 			status = l.out.result;
589 		}
590 		if (!NT_STATUS_IS_OK(status)) {
591 			r->out.error_string = talloc_asprintf(mem_ctx,
592 							      "SAMR LookupDomain failed: %s",
593 							      nt_errstr(status));
594 			talloc_free(tmp_ctx);
595 			return status;
596 		}
597 		connect_with_info->out.domain_sid = *l.out.sid;
598 	}
599 
600 	/* prepare samr_OpenDomain */
601 	ZERO_STRUCT(d_handle);
602 	od.in.connect_handle = &p_handle;
603 	od.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
604 	od.in.sid = connect_with_info->out.domain_sid;
605 	od.out.domain_handle = &d_handle;
606 
607 	/* do a samr_OpenDomain to get a domain handle */
608 	status = dcerpc_samr_OpenDomain_r(samr_pipe->binding_handle, tmp_ctx, &od);
609 	if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(od.out.result)) {
610 		status = od.out.result;
611 	}
612 	if (!NT_STATUS_IS_OK(status)) {
613 		struct dom_sid_buf buf;
614 		r->out.error_string = talloc_asprintf(
615 			mem_ctx,
616 			"samr_OpenDomain for [%s] failed: %s",
617 			dom_sid_str_buf(connect_with_info->out.domain_sid,
618 					&buf),
619 			nt_errstr(status));
620 		talloc_free(tmp_ctx);
621 		return status;
622 	}
623 
624 	/* prepare samr_CreateUser2 */
625 	ZERO_STRUCTP(u_handle);
626 	cu.in.domain_handle  = &d_handle;
627 	cu.in.access_mask     = SEC_FLAG_MAXIMUM_ALLOWED;
628 	samr_account_name.string = r->in.account_name;
629 	cu.in.account_name    = &samr_account_name;
630 	cu.in.acct_flags      = r->in.acct_type;
631 	cu.out.user_handle    = u_handle;
632 	cu.out.rid            = &rid;
633 	cu.out.access_granted = &access_granted;
634 
635 	/* do a samr_CreateUser2 to get an account handle, or an error */
636 	cu_status = dcerpc_samr_CreateUser2_r(samr_pipe->binding_handle, tmp_ctx, &cu);
637 	if (NT_STATUS_IS_OK(cu_status) && !NT_STATUS_IS_OK(cu.out.result)) {
638 		cu_status = cu.out.result;
639 	}
640 	status = cu_status;
641 	if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
642 		/* prepare samr_LookupNames */
643 		ln.in.domain_handle = &d_handle;
644 		ln.in.num_names = 1;
645 		ln.in.names = talloc_array(tmp_ctx, struct lsa_String, 1);
646 		ln.out.rids = &rids;
647 		ln.out.types = &types;
648 		if (!ln.in.names) {
649 			r->out.error_string = NULL;
650 			talloc_free(tmp_ctx);
651 			return NT_STATUS_NO_MEMORY;
652 		}
653 		ln.in.names[0].string = r->in.account_name;
654 
655 		/* 5. do a samr_LookupNames to get the users rid */
656 		status = dcerpc_samr_LookupNames_r(samr_pipe->binding_handle, tmp_ctx, &ln);
657 		if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(ln.out.result)) {
658 			status = ln.out.result;
659 		}
660 		if (!NT_STATUS_IS_OK(status)) {
661 			r->out.error_string = talloc_asprintf(mem_ctx,
662 						"samr_LookupNames for [%s] failed: %s",
663 						r->in.account_name,
664 						nt_errstr(status));
665 			talloc_free(tmp_ctx);
666 			return status;
667 		}
668 
669 		/* check if we got one RID for the user */
670 		if (ln.out.rids->count != 1) {
671 			r->out.error_string = talloc_asprintf(mem_ctx,
672 							      "samr_LookupNames for [%s] returns %d RIDs",
673 							      r->in.account_name, ln.out.rids->count);
674 			talloc_free(tmp_ctx);
675 			return NT_STATUS_INVALID_NETWORK_RESPONSE;
676 		}
677 
678 		if (ln.out.types->count != 1) {
679 			r->out.error_string = talloc_asprintf(mem_ctx,
680 								"samr_LookupNames for [%s] returns %d RID TYPEs",
681 								r->in.account_name, ln.out.types->count);
682 			talloc_free(tmp_ctx);
683 			return NT_STATUS_INVALID_NETWORK_RESPONSE;
684 		}
685 
686 		/* prepare samr_OpenUser */
687 		ZERO_STRUCTP(u_handle);
688 		ou.in.domain_handle = &d_handle;
689 		ou.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
690 		ou.in.rid = ln.out.rids->ids[0];
691 		rid = ou.in.rid;
692 		ou.out.user_handle = u_handle;
693 
694 		/* 6. do a samr_OpenUser to get a user handle */
695 		status = dcerpc_samr_OpenUser_r(samr_pipe->binding_handle, tmp_ctx, &ou);
696 		if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(ou.out.result)) {
697 			status = ou.out.result;
698 		}
699 		if (!NT_STATUS_IS_OK(status)) {
700 			r->out.error_string = talloc_asprintf(mem_ctx,
701 							"samr_OpenUser for [%s] failed: %s",
702 							r->in.account_name,
703 							nt_errstr(status));
704 			talloc_free(tmp_ctx);
705 			return status;
706 		}
707 
708 		if (r->in.recreate_account) {
709 			struct samr_DeleteUser d;
710 			d.in.user_handle = u_handle;
711 			d.out.user_handle = u_handle;
712 			status = dcerpc_samr_DeleteUser_r(samr_pipe->binding_handle, mem_ctx, &d);
713 			if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(d.out.result)) {
714 				status = d.out.result;
715 			}
716 			if (!NT_STATUS_IS_OK(status)) {
717 				r->out.error_string = talloc_asprintf(mem_ctx,
718 								      "samr_DeleteUser (for recreate) of [%s] failed: %s",
719 								      r->in.account_name,
720 								      nt_errstr(status));
721 				talloc_free(tmp_ctx);
722 				return status;
723 			}
724 
725 			/* We want to recreate, so delete and another samr_CreateUser2 */
726 
727 			/* &cu filled in above */
728 			status = dcerpc_samr_CreateUser2_r(samr_pipe->binding_handle, tmp_ctx, &cu);
729 			if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(cu.out.result)) {
730 				status = cu.out.result;
731 			}
732 			if (!NT_STATUS_IS_OK(status)) {
733 				r->out.error_string = talloc_asprintf(mem_ctx,
734 								      "samr_CreateUser2 (recreate) for [%s] failed: %s",
735 								      r->in.account_name, nt_errstr(status));
736 				talloc_free(tmp_ctx);
737 				return status;
738 			}
739 		}
740 	} else if (!NT_STATUS_IS_OK(status)) {
741 		r->out.error_string = talloc_asprintf(mem_ctx,
742 						      "samr_CreateUser2 for [%s] failed: %s",
743 						      r->in.account_name, nt_errstr(status));
744 		talloc_free(tmp_ctx);
745 		return status;
746 	}
747 
748 	/* prepare samr_QueryUserInfo (get flags) */
749 	qui.in.user_handle = u_handle;
750 	qui.in.level = 16;
751 	qui.out.info = &uinfo;
752 
753 	status = dcerpc_samr_QueryUserInfo_r(samr_pipe->binding_handle, tmp_ctx, &qui);
754 	if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(qui.out.result)) {
755 		status = qui.out.result;
756 	}
757 	if (!NT_STATUS_IS_OK(status)) {
758 		r->out.error_string = talloc_asprintf(mem_ctx,
759 						"samr_QueryUserInfo for [%s] failed: %s",
760 						r->in.account_name,
761 						nt_errstr(status));
762 		talloc_free(tmp_ctx);
763 		return status;
764 	}
765 
766 	if (!uinfo) {
767 		status = NT_STATUS_INVALID_PARAMETER;
768 		r->out.error_string
769 			= talloc_asprintf(mem_ctx,
770 					  "samr_QueryUserInfo failed to return qui.out.info for [%s]: %s",
771 					  r->in.account_name, nt_errstr(status));
772 		talloc_free(tmp_ctx);
773 		return status;
774 	}
775 
776 	old_acct_flags = (uinfo->info16.acct_flags & (ACB_WSTRUST | ACB_SVRTRUST | ACB_DOMTRUST));
777 	/* Possibly bail if the account is of the wrong type */
778 	if (old_acct_flags
779 	    != r->in.acct_type) {
780 		const char *old_account_type, *new_account_type;
781 		switch (old_acct_flags) {
782 		case ACB_WSTRUST:
783 			old_account_type = "domain member (member)";
784 			break;
785 		case ACB_SVRTRUST:
786 			old_account_type = "domain controller (bdc)";
787 			break;
788 		case ACB_DOMTRUST:
789 			old_account_type = "trusted domain";
790 			break;
791 		default:
792 			return NT_STATUS_INVALID_PARAMETER;
793 		}
794 		switch (r->in.acct_type) {
795 		case ACB_WSTRUST:
796 			new_account_type = "domain member (member)";
797 			break;
798 		case ACB_SVRTRUST:
799 			new_account_type = "domain controller (bdc)";
800 			break;
801 		case ACB_DOMTRUST:
802 			new_account_type = "trusted domain";
803 			break;
804 		default:
805 			return NT_STATUS_INVALID_PARAMETER;
806 		}
807 
808 		if (!NT_STATUS_EQUAL(cu_status, NT_STATUS_USER_EXISTS)) {
809 			/* We created a new user, but they didn't come out the right type?!? */
810 			r->out.error_string
811 				= talloc_asprintf(mem_ctx,
812 						  "We asked to create a new machine account (%s) of type %s, "
813 						  "but we got an account of type %s.  This is unexpected.  "
814 						  "Perhaps delete the account and try again.",
815 						  r->in.account_name, new_account_type, old_account_type);
816 			talloc_free(tmp_ctx);
817 			return NT_STATUS_INVALID_PARAMETER;
818 		} else {
819 			/* The account is of the wrong type, so bail */
820 
821 			/* TODO: We should allow a --force option to override, and redo this from the top setting r.in.recreate_account */
822 			r->out.error_string
823 				= talloc_asprintf(mem_ctx,
824 						  "The machine account (%s) already exists in the domain %s, "
825 						  "but is a %s.  You asked to join as a %s.  Please delete "
826 						  "the account and try again.",
827 						  r->in.account_name, connect_with_info->out.domain_name, old_account_type, new_account_type);
828 			talloc_free(tmp_ctx);
829 			return NT_STATUS_USER_EXISTS;
830 		}
831 	} else {
832 		acct_flags = uinfo->info16.acct_flags;
833 	}
834 
835 	acct_flags = (acct_flags & ~(ACB_DISABLED|ACB_PWNOTREQ));
836 
837 	/* Find out what password policy this user has */
838 	pwp.in.user_handle = u_handle;
839 	pwp.out.info = &info;
840 
841 	status = dcerpc_samr_GetUserPwInfo_r(samr_pipe->binding_handle, tmp_ctx, &pwp);
842 	if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(pwp.out.result)) {
843 		status = pwp.out.result;
844 	}
845 	if (NT_STATUS_IS_OK(status)) {
846 		policy_min_pw_len = pwp.out.info->min_password_length;
847 	}
848 
849 	if (r->in.account_pass != NULL) {
850 		password_str = talloc_strdup(tmp_ctx, r->in.account_pass);
851 	} else {
852 		/* Grab a password of that minimum length */
853 		password_str = generate_random_password(tmp_ctx,
854 					MAX(8, policy_min_pw_len), 255);
855 	}
856 	if (!password_str) {
857 		r->out.error_string = NULL;
858 		talloc_free(tmp_ctx);
859 		return NT_STATUS_NO_MEMORY;
860 	}
861 
862 	/* set full_name and reset flags */
863 	ZERO_STRUCT(u_info21);
864 	u_info21.full_name.string	= r->in.account_name;
865 	u_info21.acct_flags		= acct_flags;
866 	u_info21.fields_present		= SAMR_FIELD_FULL_NAME | SAMR_FIELD_ACCT_FLAGS;
867 
868 	r2.samr_handle.level		= LIBNET_SET_PASSWORD_SAMR_HANDLE;
869 	r2.samr_handle.in.account_name	= r->in.account_name;
870 	r2.samr_handle.in.newpassword	= password_str;
871 	r2.samr_handle.in.user_handle   = u_handle;
872 	r2.samr_handle.in.dcerpc_pipe   = samr_pipe;
873 	r2.samr_handle.in.info21	= &u_info21;
874 
875 	status = libnet_SetPassword(ctx, tmp_ctx, &r2);
876 	if (!NT_STATUS_IS_OK(status)) {
877 		r->out.error_string = talloc_steal(mem_ctx, r2.samr_handle.out.error_string);
878 		talloc_free(tmp_ctx);
879 		return status;
880 	}
881 
882 	account_sid = dom_sid_add_rid(mem_ctx, connect_with_info->out.domain_sid, rid);
883 	if (!account_sid) {
884 		r->out.error_string = NULL;
885 		talloc_free(tmp_ctx);
886 		return NT_STATUS_NO_MEMORY;
887 	}
888 
889 	/* Finish out by pushing various bits of status data out for the caller to use */
890 	r->out.join_password = password_str;
891 	talloc_steal(mem_ctx, r->out.join_password);
892 
893 	r->out.domain_sid = connect_with_info->out.domain_sid;
894 	talloc_steal(mem_ctx, r->out.domain_sid);
895 
896 	r->out.account_sid = account_sid;
897 	talloc_steal(mem_ctx, r->out.account_sid);
898 
899 	r->out.domain_name = connect_with_info->out.domain_name;
900 	talloc_steal(mem_ctx, r->out.domain_name);
901 	r->out.realm = connect_with_info->out.realm;
902 	talloc_steal(mem_ctx, r->out.realm);
903 	r->out.samr_pipe = samr_pipe;
904 	talloc_reparent(tmp_ctx, mem_ctx, samr_pipe);
905 	r->out.samr_binding = samr_pipe->binding;
906 	r->out.user_handle = u_handle;
907 	talloc_steal(mem_ctx, u_handle);
908 	r->out.error_string = r2.samr_handle.out.error_string;
909 	talloc_steal(mem_ctx, r2.samr_handle.out.error_string);
910 	r->out.kvno = 0;
911 	r->out.server_dn_str = NULL;
912 	talloc_free(tmp_ctx);
913 
914 	/* Now, if it was AD, then we want to start looking changing a
915 	 * few more things.  Otherwise, we are done. */
916 	if (r->out.realm) {
917 		status = libnet_JoinADSDomain(ctx, r);
918 		return status;
919 	}
920 
921 	return status;
922 }
923 
libnet_Join_member(struct libnet_context * ctx,TALLOC_CTX * mem_ctx,struct libnet_Join_member * r)924 NTSTATUS libnet_Join_member(struct libnet_context *ctx,
925 			    TALLOC_CTX *mem_ctx,
926 			    struct libnet_Join_member *r)
927 {
928 	NTSTATUS status;
929 	TALLOC_CTX *tmp_mem;
930 	struct libnet_JoinDomain *r2;
931 	struct provision_store_self_join_settings *set_secrets;
932 	uint32_t acct_type = 0;
933 	const char *account_name;
934 	const char *netbios_name;
935 	const char *error_string = NULL;
936 
937 	r->out.error_string = NULL;
938 
939 	tmp_mem = talloc_new(mem_ctx);
940 	if (!tmp_mem) {
941 		return NT_STATUS_NO_MEMORY;
942 	}
943 
944 	r2 = talloc_zero(tmp_mem, struct libnet_JoinDomain);
945 	if (!r2) {
946 		status = NT_STATUS_NO_MEMORY;
947 		goto out;
948 	}
949 
950 	acct_type = ACB_WSTRUST;
951 
952 	if (r->in.netbios_name != NULL) {
953 		netbios_name = r->in.netbios_name;
954 	} else {
955 		netbios_name = talloc_strdup(tmp_mem, lpcfg_netbios_name(ctx->lp_ctx));
956 		if (!netbios_name) {
957 			status = NT_STATUS_NO_MEMORY;
958 			goto out;
959 		}
960 	}
961 
962 	account_name = talloc_asprintf(tmp_mem, "%s$", netbios_name);
963 	if (!account_name) {
964 		status = NT_STATUS_NO_MEMORY;
965 		goto out;
966 	}
967 
968 	/*
969 	 * join the domain
970 	 */
971 	r2->in.domain_name	= r->in.domain_name;
972 	r2->in.account_name	= account_name;
973 	r2->in.netbios_name	= netbios_name;
974 	r2->in.level		= LIBNET_JOINDOMAIN_AUTOMATIC;
975 	r2->in.acct_type	= acct_type;
976 	r2->in.recreate_account = false;
977 	r2->in.account_pass	= r->in.account_pass;
978 	status = libnet_JoinDomain(ctx, r2, r2);
979 	if (!NT_STATUS_IS_OK(status)) {
980 		r->out.error_string = talloc_steal(mem_ctx, r2->out.error_string);
981 		goto out;
982 	}
983 
984 	set_secrets = talloc_zero(tmp_mem,
985 				  struct provision_store_self_join_settings);
986 	if (!set_secrets) {
987 		status = NT_STATUS_NO_MEMORY;
988 		goto out;
989 	}
990 
991 	set_secrets->domain_name = r2->out.domain_name;
992 	set_secrets->realm = r2->out.realm;
993 	set_secrets->netbios_name = netbios_name;
994 	set_secrets->secure_channel_type = SEC_CHAN_WKSTA;
995 	set_secrets->machine_password = r2->out.join_password;
996 	set_secrets->key_version_number = r2->out.kvno;
997 	set_secrets->domain_sid = r2->out.domain_sid;
998 
999 	status = provision_store_self_join(ctx, ctx->lp_ctx, ctx->event_ctx, set_secrets, &error_string);
1000 	if (!NT_STATUS_IS_OK(status)) {
1001 		if (error_string) {
1002 			r->out.error_string = talloc_steal(mem_ctx, error_string);
1003 		} else {
1004 			r->out.error_string
1005 				= talloc_asprintf(mem_ctx,
1006 						  "provision_store_self_join failed with %s",
1007 						  nt_errstr(status));
1008 		}
1009 		goto out;
1010 	}
1011 
1012 	/* move all out parameter to the callers TALLOC_CTX */
1013 	r->out.join_password	= talloc_move(mem_ctx, &r2->out.join_password);
1014 	r->out.domain_sid	= talloc_move(mem_ctx, &r2->out.domain_sid);
1015 	r->out.domain_name      = talloc_move(mem_ctx, &r2->out.domain_name);
1016 	status = NT_STATUS_OK;
1017 out:
1018 	talloc_free(tmp_mem);
1019 	return status;
1020 }
1021 
1022