lib/crypto: move gnutls error wrapper to own subsystem
[samba.git] / source4 / libnet / libnet_passwd.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 3 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, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "includes.h"
22 #include "libnet/libnet.h"
23 #include "../lib/crypto/crypto.h"
24 #include "libcli/auth/libcli_auth.h"
25 #include "librpc/gen_ndr/ndr_samr_c.h"
26
27 #include "lib/crypto/gnutls_helpers.h"
28 #include <gnutls/gnutls.h>
29 #include <gnutls/crypto.h>
30
31 /*
32  * do a password change using DCERPC/SAMR calls
33  * 1. connect to the SAMR pipe of users domain PDC (maybe a standalone server or workstation)
34  * 2. try samr_ChangePasswordUser3
35  * 3. try samr_ChangePasswordUser2
36  * 4. try samr_OemChangePasswordUser2
37  * (not yet: 5. try samr_ChangePasswordUser)
38  */
39 static NTSTATUS libnet_ChangePassword_samr(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_ChangePassword *r)
40 {
41         NTSTATUS status;
42         struct libnet_RpcConnect c;
43 #if 0
44         struct policy_handle user_handle;
45         struct samr_Password hash1, hash2, hash3, hash4, hash5, hash6;
46         struct samr_ChangePasswordUser pw;
47 #endif
48         struct samr_OemChangePasswordUser2 oe2;
49         struct samr_ChangePasswordUser2 pw2;
50         struct samr_ChangePasswordUser3 pw3;
51         struct lsa_String server, account;
52         struct lsa_AsciiString a_server, a_account;
53         struct samr_CryptPassword nt_pass, lm_pass;
54         struct samr_Password nt_verifier, lm_verifier;
55         uint8_t old_nt_hash[16], new_nt_hash[16];
56         uint8_t old_lm_hash[16], new_lm_hash[16];
57         struct samr_DomInfo1 *dominfo = NULL;
58         struct userPwdChangeFailureInformation *reject = NULL;
59
60         ZERO_STRUCT(c);
61
62         /* prepare connect to the SAMR pipe of the users domain PDC */
63         c.level                    = LIBNET_RPC_CONNECT_PDC;
64         c.in.name                  = r->samr.in.domain_name;
65         c.in.dcerpc_iface          = &ndr_table_samr;
66         c.in.dcerpc_flags          = DCERPC_ANON_FALLBACK;
67
68         /* 1. connect to the SAMR pipe of users domain PDC (maybe a standalone server or workstation) */
69         status = libnet_RpcConnect(ctx, mem_ctx, &c);
70         if (!NT_STATUS_IS_OK(status)) {
71                 r->samr.out.error_string = talloc_asprintf(mem_ctx,
72                                                 "Connection to SAMR pipe of PDC of domain '%s' failed: %s",
73                                                 r->samr.in.domain_name, nt_errstr(status));
74                 return status;
75         }
76
77         /* prepare password change for account */
78         server.string = talloc_asprintf(mem_ctx, "\\\\%s", dcerpc_server_name(c.out.dcerpc_pipe));
79         account.string = r->samr.in.account_name;
80
81         E_md4hash(r->samr.in.oldpassword, old_nt_hash);
82         E_md4hash(r->samr.in.newpassword, new_nt_hash);
83
84         E_deshash(r->samr.in.oldpassword, old_lm_hash);
85         E_deshash(r->samr.in.newpassword, new_lm_hash);
86
87         /* prepare samr_ChangePasswordUser3 */
88         encode_pw_buffer(lm_pass.data, r->samr.in.newpassword, STR_UNICODE);
89         arcfour_crypt(lm_pass.data, old_nt_hash, 516);
90         E_old_pw_hash(new_lm_hash, old_lm_hash, lm_verifier.hash);
91
92         encode_pw_buffer(nt_pass.data,  r->samr.in.newpassword, STR_UNICODE);
93         arcfour_crypt(nt_pass.data, old_nt_hash, 516);
94         E_old_pw_hash(new_nt_hash, old_nt_hash, nt_verifier.hash);
95
96         pw3.in.server = &server;
97         pw3.in.account = &account;
98         pw3.in.nt_password = &nt_pass;
99         pw3.in.nt_verifier = &nt_verifier;
100         pw3.in.lm_change = 1;
101         pw3.in.lm_password = &lm_pass;
102         pw3.in.lm_verifier = &lm_verifier;
103         pw3.in.password3 = NULL;
104         pw3.out.dominfo = &dominfo;
105         pw3.out.reject = &reject;
106
107         /* 2. try samr_ChangePasswordUser3 */
108         status = dcerpc_samr_ChangePasswordUser3_r(c.out.dcerpc_pipe->binding_handle, mem_ctx, &pw3);
109         if (!NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
110                 if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(pw3.out.result)) {
111                         status = pw3.out.result;
112                 }
113                 if (!NT_STATUS_IS_OK(status)) {
114                         r->samr.out.error_string = talloc_asprintf(mem_ctx,
115                                                                    "samr_ChangePasswordUser3 failed: %s",
116                                                                    nt_errstr(status));
117                         r->samr.out.error_string = talloc_asprintf(mem_ctx,
118                                                                    "samr_ChangePasswordUser3 for '%s\\%s' failed: %s",
119                                                                    r->samr.in.domain_name, r->samr.in.account_name,
120                                                                    nt_errstr(status));
121                 }
122                 goto disconnect;
123         }
124
125         /* prepare samr_ChangePasswordUser2 */
126         encode_pw_buffer(lm_pass.data, r->samr.in.newpassword, STR_ASCII|STR_TERMINATE);
127         arcfour_crypt(lm_pass.data, old_lm_hash, 516);
128         E_old_pw_hash(new_lm_hash, old_lm_hash, lm_verifier.hash);
129
130         encode_pw_buffer(nt_pass.data, r->samr.in.newpassword, STR_UNICODE);
131         arcfour_crypt(nt_pass.data, old_nt_hash, 516);
132         E_old_pw_hash(new_nt_hash, old_nt_hash, nt_verifier.hash);
133
134         pw2.in.server = &server;
135         pw2.in.account = &account;
136         pw2.in.nt_password = &nt_pass;
137         pw2.in.nt_verifier = &nt_verifier;
138         pw2.in.lm_change = 1;
139         pw2.in.lm_password = &lm_pass;
140         pw2.in.lm_verifier = &lm_verifier;
141
142         /* 3. try samr_ChangePasswordUser2 */
143         status = dcerpc_samr_ChangePasswordUser2_r(c.out.dcerpc_pipe->binding_handle, mem_ctx, &pw2);
144         if (!NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
145                 if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(pw2.out.result)) {
146                         status = pw2.out.result;
147                 }
148                 if (!NT_STATUS_IS_OK(status)) {
149                         r->samr.out.error_string = talloc_asprintf(mem_ctx,
150                                                                    "samr_ChangePasswordUser2 for '%s\\%s' failed: %s",
151                                                                    r->samr.in.domain_name, r->samr.in.account_name, 
152                                                                    nt_errstr(status));
153                 }
154                 goto disconnect;
155         }
156
157
158         /* prepare samr_OemChangePasswordUser2 */
159         a_server.string = talloc_asprintf(mem_ctx, "\\\\%s", dcerpc_server_name(c.out.dcerpc_pipe));
160         a_account.string = r->samr.in.account_name;
161
162         encode_pw_buffer(lm_pass.data, r->samr.in.newpassword, STR_ASCII);
163         arcfour_crypt(lm_pass.data, old_lm_hash, 516);
164         E_old_pw_hash(new_lm_hash, old_lm_hash, lm_verifier.hash);
165
166         oe2.in.server = &a_server;
167         oe2.in.account = &a_account;
168         oe2.in.password = &lm_pass;
169         oe2.in.hash = &lm_verifier;
170
171         /* 4. try samr_OemChangePasswordUser2 */
172         status = dcerpc_samr_OemChangePasswordUser2_r(c.out.dcerpc_pipe->binding_handle, mem_ctx, &oe2);
173         if (!NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
174                 if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(oe2.out.result)) {
175                         status = oe2.out.result;
176                 }
177                 if (!NT_STATUS_IS_OK(oe2.out.result)) {
178                         r->samr.out.error_string = talloc_asprintf(mem_ctx,
179                                                                    "samr_OemChangePasswordUser2 for '%s\\%s' failed: %s",
180                                                                    r->samr.in.domain_name, r->samr.in.account_name, 
181                                                                    nt_errstr(status));
182                 }
183                 goto disconnect;
184         }
185
186 #if 0
187         /* prepare samr_ChangePasswordUser */
188         E_old_pw_hash(new_lm_hash, old_lm_hash, hash1.hash);
189         E_old_pw_hash(old_lm_hash, new_lm_hash, hash2.hash);
190         E_old_pw_hash(new_nt_hash, old_nt_hash, hash3.hash);
191         E_old_pw_hash(old_nt_hash, new_nt_hash, hash4.hash);
192         E_old_pw_hash(old_lm_hash, new_nt_hash, hash5.hash);
193         E_old_pw_hash(old_nt_hash, new_lm_hash, hash6.hash);
194
195         /* TODO: ask for a user_handle */
196         pw.in.handle = &user_handle;
197         pw.in.lm_present = 1;
198         pw.in.old_lm_crypted = &hash1;
199         pw.in.new_lm_crypted = &hash2;
200         pw.in.nt_present = 1;
201         pw.in.old_nt_crypted = &hash3;
202         pw.in.new_nt_crypted = &hash4;
203         pw.in.cross1_present = 1;
204         pw.in.nt_cross = &hash5;
205         pw.in.cross2_present = 1;
206         pw.in.lm_cross = &hash6;
207
208         /* 5. try samr_ChangePasswordUser */
209         status = dcerpc_samr_ChangePasswordUser_r(c.pdc.out.dcerpc_pipe->binding_handle, mem_ctx, &pw);
210         if (!NT_STATUS_IS_OK(status)) {
211                 r->samr.out.error_string = talloc_asprintf(mem_ctx,
212                                                 "samr_ChangePasswordUser failed: %s",
213                                                 nt_errstr(status));
214                 goto disconnect;
215         }
216
217         /* check result of samr_ChangePasswordUser */
218         if (!NT_STATUS_IS_OK(pw.out.result)) {
219                 r->samr.out.error_string = talloc_asprintf(mem_ctx,
220                                                 "samr_ChangePasswordUser for '%s\\%s' failed: %s",
221                                                 r->samr.in.domain_name, r->samr.in.account_name, 
222                                                 nt_errstr(pw.out.result));
223                 if (NT_STATUS_EQUAL(pw.out.result, NT_STATUS_PASSWORD_RESTRICTION)) {
224                         status = pw.out.result;
225                         goto disconnect;
226                 }
227                 goto disconnect;
228         }
229 #endif
230 disconnect:
231         /* close connection */
232         talloc_unlink(ctx, c.out.dcerpc_pipe);
233
234         return status;
235 }
236
237 static NTSTATUS libnet_ChangePassword_generic(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_ChangePassword *r)
238 {
239         NTSTATUS status;
240         union libnet_ChangePassword r2;
241
242         r2.samr.level           = LIBNET_CHANGE_PASSWORD_SAMR;
243         r2.samr.in.account_name = r->generic.in.account_name;
244         r2.samr.in.domain_name  = r->generic.in.domain_name;
245         r2.samr.in.oldpassword  = r->generic.in.oldpassword;
246         r2.samr.in.newpassword  = r->generic.in.newpassword;
247
248         status = libnet_ChangePassword(ctx, mem_ctx, &r2);
249
250         r->generic.out.error_string = r2.samr.out.error_string;
251
252         return status;
253 }
254
255 NTSTATUS libnet_ChangePassword(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_ChangePassword *r)
256 {
257         switch (r->generic.level) {
258                 case LIBNET_CHANGE_PASSWORD_GENERIC:
259                         return libnet_ChangePassword_generic(ctx, mem_ctx, r);
260                 case LIBNET_CHANGE_PASSWORD_SAMR:
261                         return libnet_ChangePassword_samr(ctx, mem_ctx, r);
262                 case LIBNET_CHANGE_PASSWORD_KRB5:
263                         return NT_STATUS_NOT_IMPLEMENTED;
264                 case LIBNET_CHANGE_PASSWORD_LDAP:
265                         return NT_STATUS_NOT_IMPLEMENTED;
266                 case LIBNET_CHANGE_PASSWORD_RAP:
267                         return NT_STATUS_NOT_IMPLEMENTED;
268         }
269
270         return NT_STATUS_INVALID_LEVEL;
271 }
272
273 static NTSTATUS libnet_SetPassword_samr_handle_26(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_SetPassword *r)
274 {
275         NTSTATUS status;
276         struct samr_SetUserInfo2 sui;
277         union samr_UserInfo u_info;
278         DATA_BLOB session_key;
279         DATA_BLOB confounded_session_key = data_blob_talloc(mem_ctx, NULL, 16);
280         uint8_t confounder[16]; 
281         gnutls_hash_hd_t hash_hnd = NULL;
282         int rc;
283
284         if (r->samr_handle.in.info21) {
285                 return NT_STATUS_INVALID_PARAMETER_MIX;
286         }
287
288         /* prepare samr_SetUserInfo2 level 26 */
289         ZERO_STRUCT(u_info);
290         encode_pw_buffer(u_info.info26.password.data, r->samr_handle.in.newpassword, STR_UNICODE);
291         u_info.info26.password_expired = 0;
292         
293         status = dcerpc_fetch_session_key(r->samr_handle.in.dcerpc_pipe, &session_key);
294         if (!NT_STATUS_IS_OK(status)) {
295                 r->samr_handle.out.error_string = talloc_asprintf(mem_ctx,
296                                                                   "dcerpc_fetch_session_key failed: %s",
297                                                                   nt_errstr(status));
298                 return status;
299         }
300         
301         generate_random_buffer((uint8_t *)confounder, 16);
302
303         rc = gnutls_hash_init(&hash_hnd, GNUTLS_DIG_MD5);
304         if (rc < 0) {
305                 status = gnutls_error_to_ntstatus(rc, NT_STATUS_HASH_NOT_SUPPORTED);
306                 goto out;
307         }
308
309         rc = gnutls_hash(hash_hnd, confounder, 16);
310         if (rc < 0) {
311                 gnutls_hash_deinit(hash_hnd, NULL);
312                 status = gnutls_error_to_ntstatus(rc, NT_STATUS_HASH_NOT_SUPPORTED);
313                 goto out;
314         }
315         rc = gnutls_hash(hash_hnd, session_key.data, session_key.length);
316         if (rc < 0) {
317                 gnutls_hash_deinit(hash_hnd, NULL);
318                 status = gnutls_error_to_ntstatus(rc, NT_STATUS_HASH_NOT_SUPPORTED);
319                 goto out;
320         }
321
322         gnutls_hash_deinit(hash_hnd, confounded_session_key.data);
323
324         arcfour_crypt_blob(u_info.info26.password.data, 516, &confounded_session_key);
325         ZERO_ARRAY_LEN(confounded_session_key.data,
326                        confounded_session_key.length);
327         data_blob_free(&confounded_session_key);
328
329         memcpy(&u_info.info26.password.data[516], confounder, 16);
330         ZERO_ARRAY(confounder);
331
332         sui.in.user_handle = r->samr_handle.in.user_handle;
333         sui.in.info = &u_info;
334         sui.in.level = 26;
335         
336         /* 7. try samr_SetUserInfo2 level 26 to set the password */
337         status = dcerpc_samr_SetUserInfo2_r(r->samr_handle.in.dcerpc_pipe->binding_handle, mem_ctx, &sui);
338         /* check result of samr_SetUserInfo2 level 26 */
339         if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(sui.out.result)) {
340                 status = sui.out.result;
341         }
342         if (!NT_STATUS_IS_OK(status)) {
343                 r->samr_handle.out.error_string
344                         = talloc_asprintf(mem_ctx,
345                                           "SetUserInfo2 level 26 for [%s] failed: %s",
346                                           r->samr_handle.in.account_name, nt_errstr(status));
347         }
348
349 out:
350         return status;
351 }
352
353 static NTSTATUS libnet_SetPassword_samr_handle_25(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_SetPassword *r)
354 {
355         NTSTATUS status;
356         struct samr_SetUserInfo2 sui;
357         union samr_UserInfo u_info;
358         DATA_BLOB session_key;
359         DATA_BLOB confounded_session_key = data_blob_talloc(mem_ctx, NULL, 16);
360         uint8_t confounder[16]; 
361         gnutls_hash_hd_t hash_hnd = NULL;
362         int rc;
363
364         if (!r->samr_handle.in.info21) {
365                 return NT_STATUS_INVALID_PARAMETER_MIX;
366         }
367
368         /* prepare samr_SetUserInfo2 level 25 */
369         ZERO_STRUCT(u_info);
370         u_info.info25.info = *r->samr_handle.in.info21;
371         u_info.info25.info.fields_present |= SAMR_FIELD_NT_PASSWORD_PRESENT;
372         encode_pw_buffer(u_info.info25.password.data, r->samr_handle.in.newpassword, STR_UNICODE);
373
374         status = dcerpc_fetch_session_key(r->samr_handle.in.dcerpc_pipe, &session_key);
375         if (!NT_STATUS_IS_OK(status)) {
376                 r->samr_handle.out.error_string = talloc_asprintf(mem_ctx,
377                                                 "dcerpc_fetch_session_key failed: %s",
378                                                 nt_errstr(status));
379                 return status;
380         }
381
382         generate_random_buffer((uint8_t *)confounder, 16);
383
384         rc = gnutls_hash_init(&hash_hnd, GNUTLS_DIG_MD5);
385         if (rc < 0) {
386                 status = gnutls_error_to_ntstatus(rc, NT_STATUS_HASH_NOT_SUPPORTED);
387                 goto out;
388         }
389
390         rc = gnutls_hash(hash_hnd, confounder, 16);
391         if (rc < 0) {
392                 gnutls_hash_deinit(hash_hnd, NULL);
393                 status = gnutls_error_to_ntstatus(rc, NT_STATUS_HASH_NOT_SUPPORTED);
394                 goto out;
395         }
396         rc = gnutls_hash(hash_hnd, session_key.data, session_key.length);
397         if (rc < 0) {
398                 gnutls_hash_deinit(hash_hnd, NULL);
399                 status = gnutls_error_to_ntstatus(rc, NT_STATUS_HASH_NOT_SUPPORTED);
400                 goto out;
401         }
402
403         gnutls_hash_deinit(hash_hnd, confounded_session_key.data);
404
405         arcfour_crypt_blob(u_info.info25.password.data, 516, &confounded_session_key);
406         ZERO_ARRAY_LEN(confounded_session_key.data,
407                        confounded_session_key.length);
408         data_blob_free(&confounded_session_key);
409
410         memcpy(&u_info.info25.password.data[516], confounder, 16);
411         ZERO_ARRAY(confounder);
412
413         sui.in.user_handle = r->samr_handle.in.user_handle;
414         sui.in.info = &u_info;
415         sui.in.level = 25;
416
417         /* 8. try samr_SetUserInfo2 level 25 to set the password */
418         status = dcerpc_samr_SetUserInfo2_r(r->samr_handle.in.dcerpc_pipe->binding_handle, mem_ctx, &sui);
419         if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(sui.out.result)) {
420                 status = sui.out.result;
421         }
422         if (!NT_STATUS_IS_OK(status)) {
423                 r->samr_handle.out.error_string
424                         = talloc_asprintf(mem_ctx,
425                                           "SetUserInfo2 level 25 for [%s] failed: %s",
426                                           r->samr_handle.in.account_name, nt_errstr(status));
427         }
428
429 out:
430         return status;
431 }
432
433 static NTSTATUS libnet_SetPassword_samr_handle_24(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_SetPassword *r)
434 {
435         NTSTATUS status;
436         struct samr_SetUserInfo2 sui;
437         union samr_UserInfo u_info;
438         DATA_BLOB session_key;
439
440         if (r->samr_handle.in.info21) {
441                 return NT_STATUS_INVALID_PARAMETER_MIX;
442         }
443
444         /* prepare samr_SetUserInfo2 level 24 */
445         ZERO_STRUCT(u_info);
446         encode_pw_buffer(u_info.info24.password.data, r->samr_handle.in.newpassword, STR_UNICODE);
447         u_info.info24.password_expired = 0;
448
449         status = dcerpc_fetch_session_key(r->samr_handle.in.dcerpc_pipe, &session_key);
450         if (!NT_STATUS_IS_OK(status)) {
451                 r->samr_handle.out.error_string = talloc_asprintf(mem_ctx,
452                                                 "dcerpc_fetch_session_key failed: %s",
453                                                 nt_errstr(status));
454                 return status;
455         }
456
457         arcfour_crypt_blob(u_info.info24.password.data, 516, &session_key);
458
459         sui.in.user_handle = r->samr_handle.in.user_handle;
460         sui.in.info = &u_info;
461         sui.in.level = 24;
462
463         /* 9. try samr_SetUserInfo2 level 24 to set the password */
464         status = dcerpc_samr_SetUserInfo2_r(r->samr_handle.in.dcerpc_pipe->binding_handle, mem_ctx, &sui);
465         if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(sui.out.result)) {
466                 status = sui.out.result;
467         }
468         if (!NT_STATUS_IS_OK(status)) {
469                 r->samr_handle.out.error_string
470                         = talloc_asprintf(mem_ctx,
471                                           "SetUserInfo2 level 24 for [%s] failed: %s",
472                                           r->samr_handle.in.account_name, nt_errstr(status));
473         }
474         return status;
475 }
476
477 static NTSTATUS libnet_SetPassword_samr_handle_23(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_SetPassword *r)
478 {
479         NTSTATUS status;
480         struct samr_SetUserInfo2 sui;
481         union samr_UserInfo u_info;
482         DATA_BLOB session_key;
483
484         if (!r->samr_handle.in.info21) {
485                 return NT_STATUS_INVALID_PARAMETER_MIX;
486         }
487
488         /* prepare samr_SetUserInfo2 level 23 */
489         ZERO_STRUCT(u_info);
490         u_info.info23.info = *r->samr_handle.in.info21;
491         u_info.info23.info.fields_present |= SAMR_FIELD_NT_PASSWORD_PRESENT;
492         encode_pw_buffer(u_info.info23.password.data, r->samr_handle.in.newpassword, STR_UNICODE);
493
494         status = dcerpc_fetch_session_key(r->samr_handle.in.dcerpc_pipe, &session_key);
495         if (!NT_STATUS_IS_OK(status)) {
496                 r->samr_handle.out.error_string
497                         = talloc_asprintf(mem_ctx,
498                                           "dcerpc_fetch_session_key failed: %s",
499                                           nt_errstr(status));
500                 return status;
501         }
502
503         arcfour_crypt_blob(u_info.info23.password.data, 516, &session_key);
504
505         sui.in.user_handle = r->samr_handle.in.user_handle;
506         sui.in.info = &u_info;
507         sui.in.level = 23;
508
509         /* 10. try samr_SetUserInfo2 level 23 to set the password */
510         status = dcerpc_samr_SetUserInfo2_r(r->samr_handle.in.dcerpc_pipe->binding_handle, mem_ctx, &sui);
511         if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(sui.out.result)) {
512                 status = sui.out.result;
513         }
514         if (!NT_STATUS_IS_OK(status)) {
515                 r->samr_handle.out.error_string
516                         = talloc_asprintf(mem_ctx,
517                                           "SetUserInfo2 level 23 for [%s] failed: %s",
518                                           r->samr_handle.in.account_name, nt_errstr(status));
519         }
520         return status;
521 }
522
523 /*
524  * 1. try samr_SetUserInfo2 level 26 to set the password
525  * 2. try samr_SetUserInfo2 level 25 to set the password
526  * 3. try samr_SetUserInfo2 level 24 to set the password
527  * 4. try samr_SetUserInfo2 level 23 to set the password
528 */
529 static NTSTATUS libnet_SetPassword_samr_handle(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_SetPassword *r)
530 {
531
532         NTSTATUS status;
533         enum libnet_SetPassword_level levels[] = {
534                 LIBNET_SET_PASSWORD_SAMR_HANDLE_26,
535                 LIBNET_SET_PASSWORD_SAMR_HANDLE_25,
536                 LIBNET_SET_PASSWORD_SAMR_HANDLE_24,
537                 LIBNET_SET_PASSWORD_SAMR_HANDLE_23,
538         };
539         unsigned int i;
540
541         for (i=0; i < ARRAY_SIZE(levels); i++) {
542                 r->generic.level = levels[i];
543                 status = libnet_SetPassword(ctx, mem_ctx, r);
544                 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_INFO_CLASS)
545                     || NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER_MIX)
546                     || NT_STATUS_EQUAL(status, NT_STATUS_RPC_ENUM_VALUE_OUT_OF_RANGE)) {
547                         /* Try another password set mechanism */
548                         continue;
549                 }
550                 break;
551         }
552         
553         return status;
554 }
555 /*
556  * set a password with DCERPC/SAMR calls
557  * 1. connect to the SAMR pipe of users domain PDC (maybe a standalone server or workstation)
558  *    is it correct to contact the the pdc of the domain of the user who's password should be set?
559  * 2. do a samr_Connect to get a policy handle
560  * 3. do a samr_LookupDomain to get the domain sid
561  * 4. do a samr_OpenDomain to get a domain handle
562  * 5. do a samr_LookupNames to get the users rid
563  * 6. do a samr_OpenUser to get a user handle
564  * 7  call libnet_SetPassword_samr_handle to set the password
565  */
566 static NTSTATUS libnet_SetPassword_samr(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_SetPassword *r)
567 {
568         NTSTATUS status;
569         struct libnet_RpcConnect c;
570         struct samr_Connect sc;
571         struct policy_handle p_handle;
572         struct samr_LookupDomain ld;
573         struct dom_sid2 *sid = NULL;
574         struct lsa_String d_name;
575         struct samr_OpenDomain od;
576         struct policy_handle d_handle;
577         struct samr_LookupNames ln;
578         struct samr_Ids rids, types;
579         struct samr_OpenUser ou;
580         struct policy_handle u_handle;
581         union libnet_SetPassword r2;
582
583         ZERO_STRUCT(c);
584         /* prepare connect to the SAMR pipe of users domain PDC */
585         c.level               = LIBNET_RPC_CONNECT_PDC;
586         c.in.name             = r->samr.in.domain_name;
587         c.in.dcerpc_iface     = &ndr_table_samr;
588         
589         /* 1. connect to the SAMR pipe of users domain PDC (maybe a standalone server or workstation) */
590         status = libnet_RpcConnect(ctx, mem_ctx, &c);
591         if (!NT_STATUS_IS_OK(status)) {
592                 r->samr.out.error_string = talloc_asprintf(mem_ctx,
593                                                            "Connection to SAMR pipe of PDC of domain '%s' failed: %s",
594                                                            r->samr.in.domain_name, nt_errstr(status));
595                 return status;
596         }
597
598         /* prepare samr_Connect */
599         ZERO_STRUCT(p_handle);
600         sc.in.system_name = NULL;
601         sc.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
602         sc.out.connect_handle = &p_handle;
603
604         /* 2. do a samr_Connect to get a policy handle */
605         status = dcerpc_samr_Connect_r(c.out.dcerpc_pipe->binding_handle, mem_ctx, &sc);
606         if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(sc.out.result)) {
607                 status = sc.out.result;
608         }
609         if (!NT_STATUS_IS_OK(status)) {
610                 r->samr.out.error_string = talloc_asprintf(mem_ctx,
611                                                 "samr_Connect failed: %s",
612                                                 nt_errstr(status));
613                 goto disconnect;
614         }
615
616         /* prepare samr_LookupDomain */
617         d_name.string = r->samr.in.domain_name;
618         ld.in.connect_handle = &p_handle;
619         ld.in.domain_name = &d_name;
620         ld.out.sid = &sid;
621
622         /* 3. do a samr_LookupDomain to get the domain sid */
623         status = dcerpc_samr_LookupDomain_r(c.out.dcerpc_pipe->binding_handle, mem_ctx, &ld);
624         if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(ld.out.result)) {
625                 status = ld.out.result;
626         }
627         if (!NT_STATUS_IS_OK(status)) {
628                 r->samr.out.error_string = talloc_asprintf(mem_ctx,
629                                                 "samr_LookupDomain for [%s] failed: %s",
630                                                 r->samr.in.domain_name, nt_errstr(status));
631                 goto disconnect;
632         }
633
634         /* prepare samr_OpenDomain */
635         ZERO_STRUCT(d_handle);
636         od.in.connect_handle = &p_handle;
637         od.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
638         od.in.sid = *ld.out.sid;
639         od.out.domain_handle = &d_handle;
640
641         /* 4. do a samr_OpenDomain to get a domain handle */
642         status = dcerpc_samr_OpenDomain_r(c.out.dcerpc_pipe->binding_handle, mem_ctx, &od);
643         if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(od.out.result)) {
644                 status = od.out.result;
645         }
646         if (!NT_STATUS_IS_OK(status)) {
647                 r->samr.out.error_string = talloc_asprintf(mem_ctx,
648                                                 "samr_OpenDomain for [%s] failed: %s",
649                                                 r->samr.in.domain_name, nt_errstr(status));
650                 goto disconnect;
651         }
652
653         /* prepare samr_LookupNames */
654         ln.in.domain_handle = &d_handle;
655         ln.in.num_names = 1;
656         ln.in.names = talloc_array(mem_ctx, struct lsa_String, 1);
657         ln.out.rids = &rids;
658         ln.out.types = &types;
659         if (!ln.in.names) {
660                 r->samr.out.error_string = "Out of Memory";
661                 return NT_STATUS_NO_MEMORY;
662         }
663         ln.in.names[0].string = r->samr.in.account_name;
664
665         /* 5. do a samr_LookupNames to get the users rid */
666         status = dcerpc_samr_LookupNames_r(c.out.dcerpc_pipe->binding_handle, mem_ctx, &ln);
667         if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(ln.out.result)) {
668                 status = ln.out.result;
669         }
670         if (!NT_STATUS_IS_OK(status)) {
671                 r->samr.out.error_string = talloc_asprintf(mem_ctx,
672                                                 "samr_LookupNames for [%s] failed: %s",
673                                                 r->samr.in.account_name, nt_errstr(status));
674                 goto disconnect;
675         }
676
677         /* check if we got one RID for the user */
678         if (ln.out.rids->count != 1) {
679                 r->samr.out.error_string = talloc_asprintf(mem_ctx,
680                                                 "samr_LookupNames for [%s] returns %d RIDs",
681                                                 r->samr.in.account_name, ln.out.rids->count);
682                 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
683                 goto disconnect;        
684         }
685
686         if (ln.out.types->count != 1) {
687                 r->samr.out.error_string = talloc_asprintf(mem_ctx,
688                                                 "samr_LookupNames for [%s] returns %d RID TYPEs",
689                                                 r->samr.in.account_name, ln.out.types->count);
690                 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
691                 goto disconnect;
692         }
693
694         /* prepare samr_OpenUser */
695         ZERO_STRUCT(u_handle);
696         ou.in.domain_handle = &d_handle;
697         ou.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
698         ou.in.rid = ln.out.rids->ids[0];
699         ou.out.user_handle = &u_handle;
700
701         /* 6. do a samr_OpenUser to get a user handle */
702         status = dcerpc_samr_OpenUser_r(c.out.dcerpc_pipe->binding_handle, mem_ctx, &ou);
703         if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(ou.out.result)) {
704                 status = ou.out.result;
705         }
706         if (!NT_STATUS_IS_OK(status)) {
707                 r->samr.out.error_string = talloc_asprintf(mem_ctx,
708                                                 "samr_OpenUser for [%s] failed: %s",
709                                                 r->samr.in.account_name, nt_errstr(status));
710                 goto disconnect;
711         }
712
713         r2.samr_handle.level            = LIBNET_SET_PASSWORD_SAMR_HANDLE;
714         r2.samr_handle.in.account_name  = r->samr.in.account_name;
715         r2.samr_handle.in.newpassword   = r->samr.in.newpassword;
716         r2.samr_handle.in.user_handle   = &u_handle;
717         r2.samr_handle.in.dcerpc_pipe   = c.out.dcerpc_pipe;
718         r2.samr_handle.in.info21        = NULL;
719
720         status = libnet_SetPassword(ctx, mem_ctx, &r2);
721
722         r->generic.out.error_string = r2.samr_handle.out.error_string;
723
724 disconnect:
725         /* close connection */
726         talloc_unlink(ctx, c.out.dcerpc_pipe);
727
728         return status;
729 }
730
731 static NTSTATUS libnet_SetPassword_generic(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_SetPassword *r)
732 {
733         NTSTATUS status;
734         union libnet_SetPassword r2;
735
736         r2.samr.level           = LIBNET_SET_PASSWORD_SAMR;
737         r2.samr.in.account_name = r->generic.in.account_name;
738         r2.samr.in.domain_name  = r->generic.in.domain_name;
739         r2.samr.in.newpassword  = r->generic.in.newpassword;
740
741         r->generic.out.error_string = "Unknown Error";
742         status = libnet_SetPassword(ctx, mem_ctx, &r2);
743
744         r->generic.out.error_string = r2.samr.out.error_string;
745
746         return status;
747 }
748
749 NTSTATUS libnet_SetPassword(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_SetPassword *r)
750 {
751         switch (r->generic.level) {
752                 case LIBNET_SET_PASSWORD_GENERIC:
753                         return libnet_SetPassword_generic(ctx, mem_ctx, r);
754                 case LIBNET_SET_PASSWORD_SAMR:
755                         return libnet_SetPassword_samr(ctx, mem_ctx, r);
756                 case LIBNET_SET_PASSWORD_SAMR_HANDLE:
757                         return libnet_SetPassword_samr_handle(ctx, mem_ctx, r);
758                 case LIBNET_SET_PASSWORD_SAMR_HANDLE_26:
759                         return libnet_SetPassword_samr_handle_26(ctx, mem_ctx, r);
760                 case LIBNET_SET_PASSWORD_SAMR_HANDLE_25:
761                         return libnet_SetPassword_samr_handle_25(ctx, mem_ctx, r);
762                 case LIBNET_SET_PASSWORD_SAMR_HANDLE_24:
763                         return libnet_SetPassword_samr_handle_24(ctx, mem_ctx, r);
764                 case LIBNET_SET_PASSWORD_SAMR_HANDLE_23:
765                         return libnet_SetPassword_samr_handle_23(ctx, mem_ctx, r);
766                 case LIBNET_SET_PASSWORD_KRB5:
767                         return NT_STATUS_NOT_IMPLEMENTED;
768                 case LIBNET_SET_PASSWORD_LDAP:
769                         return NT_STATUS_NOT_IMPLEMENTED;
770                 case LIBNET_SET_PASSWORD_RAP:
771                         return NT_STATUS_NOT_IMPLEMENTED;
772         }
773
774         return NT_STATUS_INVALID_LEVEL;
775 }