r4722: Start to add 'net join' to Samba4.
[samba.git] / source4 / libnet / libnet_join.c
1 /* 
2    Unix SMB/CIFS implementation.
3    
4    Copyright (C) Stefan Metzmacher      2004
5    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
6  
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.
11    
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.
16    
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.
20 */
21
22 #include "includes.h"
23 #include "libnet/libnet.h"
24 #include "librpc/gen_ndr/ndr_samr.h"
25 #include "lib/crypto/crypto.h"
26
27 /*
28  * do a domain join using DCERPC/SAMR calls
29  * 1. connect to the SAMR pipe of users domain PDC (maybe a standalone server or workstation)
30  *    is it correct to contact the the pdc of the domain of the user who's password should be set?
31  * 2. do a samr_Connect to get a policy handle
32  * 3. do a samr_LookupDomain to get the domain sid
33  * 4. do a samr_OpenDomain to get a domain handle
34  * 5. do a samr_CreateAccount to try and get a new account 
35  * 
36  * If that fails, do:
37  * 5.1. do a samr_LookupNames to get the users rid
38  * 5.2. do a samr_OpenUser to get a user handle
39  * 
40  * 6. call libnet_SetPassword_samr_handle to set the password
41  *
42  * 7. do a samrSetUserInfo to set the account flags
43  */
44 static NTSTATUS libnet_JoinDomain_samr(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_JoinDomain *r)
45 {
46         NTSTATUS status;
47         union libnet_rpc_connect c;
48         struct samr_Connect sc;
49         struct policy_handle p_handle;
50         struct samr_LookupDomain ld;
51         struct samr_String d_name;
52         struct samr_OpenDomain od;
53         struct policy_handle d_handle;
54         struct samr_LookupNames ln;
55         struct samr_OpenUser ou;
56         struct samr_CreateUser2 cu;
57         struct policy_handle u_handle;
58         struct samr_SetUserInfo sui;
59         union samr_UserInfo u_info;
60         union libnet_SetPassword r2;
61         struct samr_GetUserPwInfo pwp;
62         struct samr_String samr_account_name;
63
64         uint32 rid, access_granted;
65         int policy_min_pw_len = 0;
66
67         /* prepare connect to the SAMR pipe of users domain PDC */
68         c.pdc.level                     = LIBNET_RPC_CONNECT_PDC;
69         c.pdc.in.domain_name            = r->samr.in.domain_name;
70         c.pdc.in.dcerpc_iface_name      = DCERPC_SAMR_NAME;
71         c.pdc.in.dcerpc_iface_uuid      = DCERPC_SAMR_UUID;
72         c.pdc.in.dcerpc_iface_version   = DCERPC_SAMR_VERSION;
73
74         /* 1. connect to the SAMR pipe of users domain PDC (maybe a standalone server or workstation) */
75         status = libnet_rpc_connect(ctx, mem_ctx, &c);
76         if (!NT_STATUS_IS_OK(status)) {
77                 r->samr.out.error_string = talloc_asprintf(mem_ctx,
78                                                 "Connection to SAMR pipe of PDC of domain '%s' failed: %s\n",
79                                                 r->samr.in.domain_name, nt_errstr(status));
80                 return status;
81         }
82
83         /* prepare samr_Connect */
84         ZERO_STRUCT(p_handle);
85         sc.in.system_name = NULL;
86         sc.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
87         sc.out.connect_handle = &p_handle;
88
89         /* 2. do a samr_Connect to get a policy handle */
90         status = dcerpc_samr_Connect(c.pdc.out.dcerpc_pipe, mem_ctx, &sc);
91         if (!NT_STATUS_IS_OK(status)) {
92                 r->samr.out.error_string = talloc_asprintf(mem_ctx,
93                                                 "samr_Connect failed: %s\n",
94                                                 nt_errstr(status));
95                 goto disconnect;
96         }
97
98         /* check result of samr_Connect */
99         if (!NT_STATUS_IS_OK(sc.out.result)) {
100                 r->samr.out.error_string = talloc_asprintf(mem_ctx,
101                                                 "samr_Connect failed: %s\n", 
102                                                 nt_errstr(sc.out.result));
103                 status = sc.out.result;
104                 goto disconnect;
105         }
106
107         /* prepare samr_LookupDomain */
108         d_name.string = r->samr.in.domain_name;
109         ld.in.connect_handle = &p_handle;
110         ld.in.domain = &d_name;
111
112         /* 3. do a samr_LookupDomain to get the domain sid */
113         status = dcerpc_samr_LookupDomain(c.pdc.out.dcerpc_pipe, mem_ctx, &ld);
114         if (!NT_STATUS_IS_OK(status)) {
115                 r->samr.out.error_string = talloc_asprintf(mem_ctx,
116                                                 "samr_LookupDomain for [%s] failed: %s\n",
117                                                 r->samr.in.domain_name, nt_errstr(status));
118                 goto disconnect;
119         }
120
121         /* check result of samr_LookupDomain */
122         if (!NT_STATUS_IS_OK(ld.out.result)) {
123                 r->samr.out.error_string = talloc_asprintf(mem_ctx,
124                                                 "samr_LookupDomain for [%s] failed: %s\n",
125                                                 r->samr.in.domain_name, nt_errstr(ld.out.result));
126                 status = ld.out.result;
127                 goto disconnect;
128         }
129
130         /* prepare samr_OpenDomain */
131         ZERO_STRUCT(d_handle);
132         od.in.connect_handle = &p_handle;
133         od.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
134         od.in.sid = ld.out.sid;
135         od.out.domain_handle = &d_handle;
136
137         /* 4. do a samr_OpenDomain to get a domain handle */
138         status = dcerpc_samr_OpenDomain(c.pdc.out.dcerpc_pipe, mem_ctx, &od);
139         if (!NT_STATUS_IS_OK(status)) {
140                 r->samr.out.error_string = talloc_asprintf(mem_ctx,
141                                                 "samr_OpenDomain for [%s] failed: %s\n",
142                                                 r->samr.in.domain_name, nt_errstr(status));
143                 goto disconnect;
144         }
145
146         /* prepare samr_CreateUser2 */
147         ZERO_STRUCT(u_handle);
148         cu.in.domain_handle  = &d_handle;
149         cu.in.access_mask     = SEC_FLAG_MAXIMUM_ALLOWED;
150         samr_account_name.string = r->samr.in.account_name;
151         cu.in.account_name    = &samr_account_name;
152         cu.in.acct_flags      = r->samr.in.acct_type;
153         cu.out.user_handle    = &u_handle;
154         cu.out.rid            = &rid;
155         cu.out.access_granted = &access_granted;
156
157         /* 4. do a samr_CreateUser2 to get an account handle, or an error */
158         status = dcerpc_samr_CreateUser2(c.pdc.out.dcerpc_pipe, mem_ctx, &cu);
159         if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
160                         r->samr.out.error_string = talloc_asprintf(mem_ctx,
161                                                                    "samr_CreateUser2 for [%s] failed: %s\n",
162                                                                    r->samr.in.domain_name, nt_errstr(status));
163                         goto disconnect;
164
165         } else if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
166                 /* prepare samr_LookupNames */
167                 ln.in.domain_handle = &d_handle;
168                 ln.in.num_names = 1;
169                 ln.in.names = talloc_array_p(mem_ctx, struct samr_String, 1);
170                 if (!ln.in.names) {
171                         r->samr.out.error_string = "Out of Memory";
172                         return NT_STATUS_NO_MEMORY;
173                 }
174                 ln.in.names[0].string = r->samr.in.account_name;
175                 
176                 /* 5. do a samr_LookupNames to get the users rid */
177                 status = dcerpc_samr_LookupNames(c.pdc.out.dcerpc_pipe, mem_ctx, &ln);
178                 if (!NT_STATUS_IS_OK(status)) {
179                         r->samr.out.error_string = talloc_asprintf(mem_ctx,
180                                                                    "samr_LookupNames for [%s] failed: %s\n",
181                                                 r->samr.in.account_name, nt_errstr(status));
182                         goto disconnect;
183                 }
184                 
185                 
186                 /* check if we got one RID for the user */
187                 if (ln.out.rids.count != 1) {
188                         r->samr.out.error_string = talloc_asprintf(mem_ctx,
189                                                                    "samr_LookupNames for [%s] returns %d RIDs\n",
190                                                                    r->samr.in.account_name, ln.out.rids.count);
191                         status = NT_STATUS_INVALID_PARAMETER;
192                         goto disconnect;        
193                 }
194                 
195                 /* prepare samr_OpenUser */
196                 ZERO_STRUCT(u_handle);
197                 ou.in.domain_handle = &d_handle;
198                 ou.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
199                 ou.in.rid = ln.out.rids.ids[0];
200                 ou.out.user_handle = &u_handle;
201                 
202                 /* 6. do a samr_OpenUser to get a user handle */
203                 status = dcerpc_samr_OpenUser(c.pdc.out.dcerpc_pipe, mem_ctx, &ou);
204                 if (!NT_STATUS_IS_OK(status)) {
205                         r->samr.out.error_string = talloc_asprintf(mem_ctx,
206                                                                    "samr_OpenUser for [%s] failed: %s\n",
207                                                                    r->samr.in.account_name, nt_errstr(status));
208                         goto disconnect;
209                 }
210         }
211
212         pwp.in.user_handle = &u_handle;
213
214         status = dcerpc_samr_GetUserPwInfo(c.pdc.out.dcerpc_pipe, mem_ctx, &pwp);
215         if (NT_STATUS_IS_OK(status)) {
216                 policy_min_pw_len = pwp.out.info.min_password_length;
217         }
218
219         r->samr.out.join_password = generate_random_str(mem_ctx, MAX(8, policy_min_pw_len));
220
221         r2.samr_handle.level            = LIBNET_SET_PASSWORD_SAMR_HANDLE;
222         r2.samr_handle.in.account_name  = r->samr.in.account_name;
223         r2.samr_handle.in.newpassword   = r->samr.out.join_password;
224         r2.samr_handle.in.user_handle   = &u_handle;
225         r2.samr_handle.in.dcerpc_pipe   = c.pdc.out.dcerpc_pipe;
226
227         status = libnet_SetPassword(ctx, mem_ctx, &r2);
228
229         r->samr.out.error_string = r2.samr_handle.out.error_string;
230
231         if (!NT_STATUS_IS_OK(status)) {
232                 goto disconnect;
233         }
234
235         /* prepare samr_SetUserInfo level 23 */
236         ZERO_STRUCT(u_info);
237         u_info.info16.acct_flags = r->samr.in.acct_type;
238
239         sui.in.user_handle = &u_handle;
240         sui.in.info = &u_info;
241         sui.in.level = 16;
242         
243         dcerpc_samr_SetUserInfo(c.pdc.out.dcerpc_pipe, mem_ctx, &sui);
244
245 disconnect:
246         /* close connection */
247         dcerpc_pipe_close(c.pdc.out.dcerpc_pipe);
248
249         return status;
250 }
251
252 static NTSTATUS libnet_JoinDomain_generic(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_JoinDomain *r)
253 {
254         NTSTATUS status;
255         union libnet_JoinDomain r2;
256
257         r2.samr.level           = LIBNET_JOIN_DOMAIN_SAMR;
258         r2.samr.in.account_name = r->generic.in.account_name;
259         r2.samr.in.domain_name  = r->generic.in.domain_name;
260         r2.samr.in.acct_type    = r->generic.in.acct_type;
261
262         status = libnet_JoinDomain(ctx, mem_ctx, &r2);
263
264         r->generic.out.error_string = r2.samr.out.error_string;
265
266         return status;
267 }
268
269 NTSTATUS libnet_JoinDomain(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_JoinDomain *r)
270 {
271         switch (r->generic.level) {
272                 case LIBNET_JOIN_DOMAIN_GENERIC:
273                         return libnet_JoinDomain_generic(ctx, mem_ctx, r);
274                 case LIBNET_JOIN_DOMAIN_SAMR:
275                         return libnet_JoinDomain_samr(ctx, mem_ctx, r);
276         }
277
278         return NT_STATUS_INVALID_LEVEL;
279 }