2 Unix SMB/CIFS implementation.
4 Copyright (C) Stefan Metzmacher 2004
5 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 #include "libnet/libnet.h"
24 #include "librpc/gen_ndr/ndr_samr.h"
25 #include "lib/crypto/crypto.h"
26 #include "lib/ldb/include/ldb.h"
27 #include "include/secrets.h"
30 * do a domain join using DCERPC/SAMR calls
31 * 1. connect to the SAMR pipe of users domain PDC (maybe a standalone server or workstation)
32 * is it correct to contact the the pdc of the domain of the user who's password should be set?
33 * 2. do a samr_Connect to get a policy handle
34 * 3. do a samr_LookupDomain to get the domain sid
35 * 4. do a samr_OpenDomain to get a domain handle
36 * 5. do a samr_CreateAccount to try and get a new account
39 * 5.1. do a samr_LookupNames to get the users rid
40 * 5.2. do a samr_OpenUser to get a user handle
42 * 6. call libnet_SetPassword_samr_handle to set the password
44 * 7. do a samrSetUserInfo to set the account flags
46 static NTSTATUS
libnet_JoinDomain_samr(struct libnet_context
*ctx
,
47 TALLOC_CTX
*mem_ctx
, union libnet_JoinDomain
*r
)
50 union libnet_rpc_connect c
;
51 struct samr_Connect sc
;
52 struct policy_handle p_handle
;
53 struct samr_LookupDomain ld
;
54 struct samr_String d_name
;
55 struct samr_OpenDomain od
;
56 struct policy_handle d_handle
;
57 struct samr_LookupNames ln
;
58 struct samr_OpenUser ou
;
59 struct samr_CreateUser2 cu
;
60 struct policy_handle u_handle
;
61 struct samr_QueryUserInfo qui
;
62 struct samr_SetUserInfo sui
;
63 union samr_UserInfo u_info
;
64 union libnet_SetPassword r2
;
65 struct samr_GetUserPwInfo pwp
;
66 struct samr_String samr_account_name
;
69 uint32_t rid
, access_granted
;
70 int policy_min_pw_len
= 0;
72 /* prepare connect to the SAMR pipe of users domain PDC */
73 c
.pdc
.level
= LIBNET_RPC_CONNECT_PDC
;
74 c
.pdc
.in
.domain_name
= r
->samr
.in
.domain_name
;
75 c
.pdc
.in
.dcerpc_iface_name
= DCERPC_SAMR_NAME
;
76 c
.pdc
.in
.dcerpc_iface_uuid
= DCERPC_SAMR_UUID
;
77 c
.pdc
.in
.dcerpc_iface_version
= DCERPC_SAMR_VERSION
;
79 /* 1. connect to the SAMR pipe of users domain PDC (maybe a standalone server or workstation) */
80 status
= libnet_rpc_connect(ctx
, mem_ctx
, &c
);
81 if (!NT_STATUS_IS_OK(status
)) {
82 r
->samr
.out
.error_string
= talloc_asprintf(mem_ctx
,
83 "Connection to SAMR pipe of PDC of domain '%s' failed: %s\n",
84 r
->samr
.in
.domain_name
, nt_errstr(status
));
88 /* prepare samr_Connect */
89 ZERO_STRUCT(p_handle
);
90 sc
.in
.system_name
= NULL
;
91 sc
.in
.access_mask
= SEC_FLAG_MAXIMUM_ALLOWED
;
92 sc
.out
.connect_handle
= &p_handle
;
94 /* 2. do a samr_Connect to get a policy handle */
95 status
= dcerpc_samr_Connect(c
.pdc
.out
.dcerpc_pipe
, mem_ctx
, &sc
);
96 if (!NT_STATUS_IS_OK(status
)) {
97 r
->samr
.out
.error_string
= talloc_asprintf(mem_ctx
,
98 "samr_Connect failed: %s\n",
103 /* check result of samr_Connect */
104 if (!NT_STATUS_IS_OK(sc
.out
.result
)) {
105 r
->samr
.out
.error_string
= talloc_asprintf(mem_ctx
,
106 "samr_Connect failed: %s\n",
107 nt_errstr(sc
.out
.result
));
108 status
= sc
.out
.result
;
112 /* prepare samr_LookupDomain */
113 d_name
.string
= r
->samr
.in
.domain_name
;
114 ld
.in
.connect_handle
= &p_handle
;
115 ld
.in
.domain_name
= &d_name
;
117 /* 3. do a samr_LookupDomain to get the domain sid */
118 status
= dcerpc_samr_LookupDomain(c
.pdc
.out
.dcerpc_pipe
, mem_ctx
, &ld
);
119 if (!NT_STATUS_IS_OK(status
)) {
120 r
->samr
.out
.error_string
= talloc_asprintf(mem_ctx
,
121 "samr_LookupDomain for [%s] failed: %s\n",
122 r
->samr
.in
.domain_name
, nt_errstr(status
));
126 /* check result of samr_LookupDomain */
127 if (!NT_STATUS_IS_OK(ld
.out
.result
)) {
128 r
->samr
.out
.error_string
= talloc_asprintf(mem_ctx
,
129 "samr_LookupDomain for [%s] failed: %s\n",
130 r
->samr
.in
.domain_name
, nt_errstr(ld
.out
.result
));
131 status
= ld
.out
.result
;
135 /* prepare samr_OpenDomain */
136 ZERO_STRUCT(d_handle
);
137 od
.in
.connect_handle
= &p_handle
;
138 od
.in
.access_mask
= SEC_FLAG_MAXIMUM_ALLOWED
;
139 od
.in
.sid
= ld
.out
.sid
;
140 od
.out
.domain_handle
= &d_handle
;
142 /* 4. do a samr_OpenDomain to get a domain handle */
143 status
= dcerpc_samr_OpenDomain(c
.pdc
.out
.dcerpc_pipe
, mem_ctx
, &od
);
144 if (!NT_STATUS_IS_OK(status
)) {
145 r
->samr
.out
.error_string
= talloc_asprintf(mem_ctx
,
146 "samr_OpenDomain for [%s] failed: %s\n",
147 r
->samr
.in
.domain_name
, nt_errstr(status
));
151 /* prepare samr_CreateUser2 */
152 ZERO_STRUCT(u_handle
);
153 cu
.in
.domain_handle
= &d_handle
;
154 cu
.in
.access_mask
= SEC_FLAG_MAXIMUM_ALLOWED
;
155 samr_account_name
.string
= r
->samr
.in
.account_name
;
156 cu
.in
.account_name
= &samr_account_name
;
157 cu
.in
.acct_flags
= r
->samr
.in
.acct_type
;
158 cu
.out
.user_handle
= &u_handle
;
160 cu
.out
.access_granted
= &access_granted
;
162 /* 4. do a samr_CreateUser2 to get an account handle, or an error */
163 status
= dcerpc_samr_CreateUser2(c
.pdc
.out
.dcerpc_pipe
, mem_ctx
, &cu
);
164 if (!NT_STATUS_IS_OK(status
) && !NT_STATUS_EQUAL(status
, NT_STATUS_USER_EXISTS
)) {
165 r
->samr
.out
.error_string
= talloc_asprintf(mem_ctx
,
166 "samr_CreateUser2 for [%s] failed: %s\n",
167 r
->samr
.in
.domain_name
, nt_errstr(status
));
170 } else if (NT_STATUS_EQUAL(status
, NT_STATUS_USER_EXISTS
)) {
171 /* prepare samr_LookupNames */
172 ln
.in
.domain_handle
= &d_handle
;
174 ln
.in
.names
= talloc_array(mem_ctx
, struct samr_String
, 1);
176 r
->samr
.out
.error_string
= "Out of Memory";
177 return NT_STATUS_NO_MEMORY
;
179 ln
.in
.names
[0].string
= r
->samr
.in
.account_name
;
181 /* 5. do a samr_LookupNames to get the users rid */
182 status
= dcerpc_samr_LookupNames(c
.pdc
.out
.dcerpc_pipe
, mem_ctx
, &ln
);
183 if (!NT_STATUS_IS_OK(status
)) {
184 r
->samr
.out
.error_string
= talloc_asprintf(mem_ctx
,
185 "samr_LookupNames for [%s] failed: %s\n",
186 r
->samr
.in
.account_name
, nt_errstr(status
));
191 /* check if we got one RID for the user */
192 if (ln
.out
.rids
.count
!= 1) {
193 r
->samr
.out
.error_string
= talloc_asprintf(mem_ctx
,
194 "samr_LookupNames for [%s] returns %d RIDs\n",
195 r
->samr
.in
.account_name
, ln
.out
.rids
.count
);
196 status
= NT_STATUS_INVALID_PARAMETER
;
200 /* prepare samr_OpenUser */
201 ZERO_STRUCT(u_handle
);
202 ou
.in
.domain_handle
= &d_handle
;
203 ou
.in
.access_mask
= SEC_FLAG_MAXIMUM_ALLOWED
;
204 ou
.in
.rid
= ln
.out
.rids
.ids
[0];
205 ou
.out
.user_handle
= &u_handle
;
207 /* 6. do a samr_OpenUser to get a user handle */
208 status
= dcerpc_samr_OpenUser(c
.pdc
.out
.dcerpc_pipe
, mem_ctx
, &ou
);
209 if (!NT_STATUS_IS_OK(status
)) {
210 r
->samr
.out
.error_string
= talloc_asprintf(mem_ctx
,
211 "samr_OpenUser for [%s] failed: %s\n",
212 r
->samr
.in
.account_name
, nt_errstr(status
));
217 pwp
.in
.user_handle
= &u_handle
;
219 status
= dcerpc_samr_GetUserPwInfo(c
.pdc
.out
.dcerpc_pipe
, mem_ctx
, &pwp
);
220 if (NT_STATUS_IS_OK(status
)) {
221 policy_min_pw_len
= pwp
.out
.info
.min_password_length
;
224 r
->samr
.out
.join_password
= generate_random_str(mem_ctx
, MAX(8, policy_min_pw_len
));
226 r2
.samr_handle
.level
= LIBNET_SET_PASSWORD_SAMR_HANDLE
;
227 r2
.samr_handle
.in
.account_name
= r
->samr
.in
.account_name
;
228 r2
.samr_handle
.in
.newpassword
= r
->samr
.out
.join_password
;
229 r2
.samr_handle
.in
.user_handle
= &u_handle
;
230 r2
.samr_handle
.in
.dcerpc_pipe
= c
.pdc
.out
.dcerpc_pipe
;
232 status
= libnet_SetPassword(ctx
, mem_ctx
, &r2
);
234 r
->samr
.out
.error_string
= r2
.samr_handle
.out
.error_string
;
236 if (!NT_STATUS_IS_OK(status
)) {
240 /* prepare samr_SetUserInfo level 23 */
241 qui
.in
.user_handle
= &u_handle
;
244 status
= dcerpc_samr_QueryUserInfo(c
.pdc
.out
.dcerpc_pipe
, mem_ctx
, &qui
);
245 if (!NT_STATUS_IS_OK(status
)) {
246 r
->samr
.out
.error_string
247 = talloc_asprintf(mem_ctx
,
248 "samr_QueryUserInfo for [%s] failed: %s\n",
249 r
->samr
.in
.account_name
, nt_errstr(status
));
253 status
= NT_STATUS_INVALID_PARAMETER
;
254 r
->samr
.out
.error_string
255 = talloc_asprintf(mem_ctx
,
256 "samr_QueryUserInfo failed to return qui.out.info for [%s]: %s\n",
257 r
->samr
.in
.account_name
, nt_errstr(status
));
261 if ((qui
.out
.info
->info16
.acct_flags
& (ACB_WSTRUST
| ACB_SVRTRUST
| ACB_DOMTRUST
))
262 != r
->samr
.in
.acct_type
) {
263 acct_flags
= (qui
.out
.info
->info16
.acct_flags
& ~(ACB_WSTRUST
| ACB_SVRTRUST
| ACB_DOMTRUST
))
264 | r
->samr
.in
.acct_type
;
266 acct_flags
= qui
.out
.info
->info16
.acct_flags
;
269 acct_flags
= (acct_flags
& ~ACB_DISABLED
);
271 if (acct_flags
!= qui
.out
.info
->info16
.acct_flags
) {
273 u_info
.info16
.acct_flags
= acct_flags
;
275 sui
.in
.user_handle
= &u_handle
;
276 sui
.in
.info
= &u_info
;
279 dcerpc_samr_SetUserInfo(c
.pdc
.out
.dcerpc_pipe
, mem_ctx
, &sui
);
280 if (!NT_STATUS_IS_OK(status
)) {
281 r
->samr
.out
.error_string
282 = talloc_asprintf(mem_ctx
,
283 "samr_SetUserInfo for [%s] failed to remove ACB_DISABLED flag: %s\n",
284 r
->samr
.in
.account_name
, nt_errstr(status
));
290 /* close connection */
291 talloc_free(c
.pdc
.out
.dcerpc_pipe
);
296 static NTSTATUS
libnet_JoinDomain_generic(struct libnet_context
*ctx
, TALLOC_CTX
*mem_ctx
, union libnet_JoinDomain
*r
)
299 union libnet_JoinDomain r2
;
301 r2
.samr
.level
= LIBNET_JOIN_DOMAIN_SAMR
;
302 r2
.samr
.in
.account_name
= r
->generic
.in
.account_name
;
303 r2
.samr
.in
.domain_name
= r
->generic
.in
.domain_name
;
304 r2
.samr
.in
.acct_type
= r
->generic
.in
.acct_type
;
306 status
= libnet_JoinDomain(ctx
, mem_ctx
, &r2
);
308 r
->generic
.out
.error_string
= r2
.samr
.out
.error_string
;
309 r
->generic
.out
.join_password
= r2
.samr
.out
.join_password
;
314 NTSTATUS
libnet_JoinDomain(struct libnet_context
*ctx
, TALLOC_CTX
*mem_ctx
, union libnet_JoinDomain
*r
)
316 switch (r
->generic
.level
) {
317 case LIBNET_JOIN_DOMAIN_GENERIC
:
318 return libnet_JoinDomain_generic(ctx
, mem_ctx
, r
);
319 case LIBNET_JOIN_DOMAIN_SAMR
:
320 return libnet_JoinDomain_samr(ctx
, mem_ctx
, r
);
323 return NT_STATUS_INVALID_LEVEL
;
327 static NTSTATUS
libnet_Join_primary_domain(struct libnet_context
*ctx
,
329 union libnet_Join
*r
)
334 struct ldb_context
*ldb
;
335 union libnet_JoinDomain r2
;
336 const char *base_dn
= "cn=Primary Domains";
337 const struct ldb_val
*prior_secret
;
338 const char *prior_modified_time
;
339 struct ldb_message
**msgs
, *msg
;
341 const char *attrs
[] = {
349 r2
.generic
.level
= LIBNET_JOIN_DOMAIN_GENERIC
;
351 if (r
->generic
.in
.secure_channel_type
== SEC_CHAN_BDC
) {
352 r2
.generic
.in
.acct_type
= ACB_SVRTRUST
;
353 } else if (r
->generic
.in
.secure_channel_type
== SEC_CHAN_WKSTA
) {
354 r2
.generic
.in
.acct_type
= ACB_WSTRUST
;
356 r2
.generic
.in
.domain_name
= r
->generic
.in
.domain_name
;
358 r2
.generic
.in
.account_name
= talloc_asprintf(mem_ctx
, "%s$", lp_netbios_name());
360 /* Local secrets are stored in secrets.ldb */
361 ldb
= secrets_db_connect(mem_ctx
);
364 status
= libnet_JoinDomain(ctx
, mem_ctx
, &r2
);
366 r
->generic
.out
.error_string
= r2
.generic
.out
.error_string
;
368 /* store in secrets.ldb or samdb.ldb, depending on secret type */
369 if (!NT_STATUS_IS_OK(status
)) {
373 sct
= talloc_asprintf(mem_ctx
, "%d", r
->generic
.in
.secure_channel_type
);
374 msg
= ldb_msg_new(mem_ctx
);
376 /* search for the secret record */
377 ret
= gendb_search(ldb
,
378 mem_ctx
, base_dn
, &msgs
, attrs
,
379 SECRETS_PRIMARY_DOMAIN_FILTER
,
380 r
->generic
.in
.domain_name
);
382 msg
->dn
= talloc_asprintf(mem_ctx
, "flatname=%s,%s",
383 r
->generic
.in
.domain_name
,
386 samdb_msg_add_string(ldb
, mem_ctx
, msg
, "flatname", r
->generic
.in
.domain_name
);
387 samdb_msg_add_string(ldb
, mem_ctx
, msg
, "objectClass", "primaryDomain");
388 samdb_msg_add_string(ldb
, mem_ctx
, msg
, "secret", r2
.generic
.out
.join_password
);
390 samdb_msg_add_string(ldb
, mem_ctx
, msg
, "samAccountName", r2
.generic
.in
.account_name
);
392 samdb_msg_add_string(ldb
, mem_ctx
, msg
, "secureChannelType", sct
);
394 /* create the secret */
395 ret
= samdb_add(ldb
, mem_ctx
, msg
);
397 r
->generic
.out
.error_string
398 = talloc_asprintf(mem_ctx
,
399 "Failed to create secret record %s\n",
401 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
404 } else if (ret
!= 1) {
405 r
->generic
.out
.error_string
406 = talloc_asprintf(mem_ctx
,
407 "Found %d records matching cn=%s under DN %s\n", ret
,
408 r
->generic
.in
.domain_name
, base_dn
);
409 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
412 msg
->dn
= msgs
[0]->dn
;
414 prior_secret
= ldb_msg_find_ldb_val(msgs
[0], "secret");
416 samdb_msg_set_value(ldb
, mem_ctx
, msg
, "priorSecret", prior_secret
);
418 samdb_msg_set_string(ldb
, mem_ctx
, msg
, "secret", r2
.generic
.out
.join_password
);
420 prior_modified_time
= ldb_msg_find_string(msgs
[0],
421 "whenChanged", NULL
);
422 if (prior_modified_time
) {
423 samdb_msg_set_string(ldb
, mem_ctx
, msg
, "priorWhenChanged",
424 prior_modified_time
);
427 samdb_msg_set_string(ldb
, mem_ctx
, msg
, "samAccountName", r2
.generic
.in
.account_name
);
428 samdb_msg_set_string(ldb
, mem_ctx
, msg
, "secureChannelType", sct
);
430 /* update the secret */
431 ret
= samdb_replace(ldb
, mem_ctx
, msg
);
433 DEBUG(0,("Failed to create secret record %s\n", msg
->dn
));
434 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
439 NTSTATUS
libnet_Join_generic(struct libnet_context
*ctx
, TALLOC_CTX
*mem_ctx
, union libnet_Join
*r
)
442 union libnet_Join r2
;
443 r2
.generic
.in
.secure_channel_type
= r
->generic
.in
.secure_channel_type
;
444 r2
.generic
.in
.domain_name
= r
->generic
.in
.domain_name
;
446 if ((r
->generic
.in
.secure_channel_type
== SEC_CHAN_WKSTA
)
447 || (r
->generic
.in
.secure_channel_type
== SEC_CHAN_BDC
)) {
448 r2
.generic
.level
= LIBNET_JOIN_PRIMARY
;
449 nt_status
= libnet_Join(ctx
, mem_ctx
, &r2
);
451 r
->generic
.out
.error_string
452 = talloc_asprintf(mem_ctx
, "Invalid secure channel type specified (%08X) attempting to join domain %s",
453 r
->generic
.in
.secure_channel_type
, r
->generic
.in
.domain_name
);
454 return NT_STATUS_INVALID_PARAMETER
;
456 r
->generic
.out
.error_string
= r2
.generic
.out
.error_string
;
460 NTSTATUS
libnet_Join(struct libnet_context
*ctx
, TALLOC_CTX
*mem_ctx
, union libnet_Join
*r
)
462 switch (r
->generic
.level
) {
463 case LIBNET_JOIN_GENERIC
:
464 return libnet_Join_generic(ctx
, mem_ctx
, r
);
465 case LIBNET_JOIN_PRIMARY
:
466 return libnet_Join_primary_domain(ctx
, mem_ctx
, r
);
469 return NT_STATUS_INVALID_LEVEL
;