samdb: Add remote address to connect
[amitay/samba.git] / source4 / rpc_server / samr / samr_password.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    samr server password set/change handling
5
6    Copyright (C) Andrew Tridgell 2004
7    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
8
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 3 of the License, or
12    (at your option) any later version.
13
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18
19    You should have received a copy of the GNU General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include "includes.h"
24 #include "rpc_server/dcerpc_server.h"
25 #include "rpc_server/samr/dcesrv_samr.h"
26 #include "system/time.h"
27 #include "../lib/crypto/crypto.h"
28 #include "dsdb/samdb/samdb.h"
29 #include "auth/auth.h"
30 #include "libcli/auth/libcli_auth.h"
31 #include "../lib/util/util_ldb.h"
32 #include "rpc_server/samr/proto.h"
33 #include "auth/auth_sam.h"
34
35 static void log_password_change_event(struct imessaging_context *msg_ctx,
36                                       struct loadparm_context *lp_ctx,
37                                       const struct tsocket_address *remote_client_address,
38                                       const struct tsocket_address *local_server_address,
39                                       const char *auth_description,
40                                       const char *password_type,
41                                       const char *original_client_name,
42                                       const char *account_name_from_db,
43                                       NTSTATUS status,
44                                       struct dom_sid *sid)
45 {
46         /*
47          * Forcing this via the NTLM auth structure is not ideal, but
48          * it is the most practical option right now, and ensures the
49          * logs are consistent, even if some elements are always NULL.
50          */
51         struct auth_usersupplied_info ui = {
52                 .mapped_state = true,
53                 .was_mapped = true,
54                 .client = {
55                         .account_name = original_client_name,
56                         .domain_name = lpcfg_sam_name(lp_ctx),
57                 },
58                 .mapped = {
59                         .account_name = account_name_from_db,
60                         .domain_name = lpcfg_sam_name(lp_ctx),
61                 },
62                 .remote_host = remote_client_address,
63                 .local_host = local_server_address,
64                 .service_description = "SAMR Password Change",
65                 .auth_description = auth_description,
66                 .password_type = password_type,
67         };
68
69         log_authentication_event(msg_ctx,
70                                  lp_ctx,
71                                  &ui,
72                                  status,
73                                  ui.mapped.domain_name,
74                                  ui.mapped.account_name,
75                                  NULL,
76                                  sid);
77 }
78 /*
79   samr_ChangePasswordUser
80
81   So old it is just not worth implementing
82   because it does not supply a plaintext and so we can't do password
83   complexity checking and cannot update all the other password hashes.
84
85 */
86 NTSTATUS dcesrv_samr_ChangePasswordUser(struct dcesrv_call_state *dce_call,
87                                         TALLOC_CTX *mem_ctx,
88                                         struct samr_ChangePasswordUser *r)
89 {
90         return NT_STATUS_NOT_IMPLEMENTED;
91 }
92
93 /*
94   samr_OemChangePasswordUser2
95 */
96 NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call,
97                                             TALLOC_CTX *mem_ctx,
98                                             struct samr_OemChangePasswordUser2 *r)
99 {
100         NTSTATUS status = NT_STATUS_WRONG_PASSWORD;
101         DATA_BLOB new_password, new_unicode_password;
102         char *new_pass;
103         struct samr_CryptPassword *pwbuf = r->in.password;
104         struct ldb_context *sam_ctx;
105         struct ldb_dn *user_dn;
106         int ret;
107         struct ldb_message **res;
108         const char * const attrs[] = { "objectSid", "dBCSPwd",
109                                        "userAccountControl",
110                                        "msDS-User-Account-Control-Computed",
111                                        "badPwdCount", "badPasswordTime",
112                                        "samAccountName",
113                                        NULL };
114         struct samr_Password *lm_pwd;
115         DATA_BLOB lm_pwd_blob;
116         uint8_t new_lm_hash[16];
117         struct samr_Password lm_verifier;
118         size_t unicode_pw_len;
119         size_t converted_size = 0;
120         const char *user_samAccountName = NULL;
121         struct dom_sid *user_objectSid = NULL;
122
123         if (pwbuf == NULL) {
124                 return NT_STATUS_INVALID_PARAMETER;
125         }
126
127         if (r->in.hash == NULL) {
128                 return NT_STATUS_INVALID_PARAMETER;
129         }
130
131         /* this call can only work with lanman auth */
132         if (!lpcfg_lanman_auth(dce_call->conn->dce_ctx->lp_ctx)) {
133                 return NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER;
134         }
135
136         /* Connect to a SAMDB with system privileges for fetching the old pw
137          * hashes. */
138         sam_ctx = samdb_connect(mem_ctx,
139                                 dce_call->event_ctx,
140                                 dce_call->conn->dce_ctx->lp_ctx,
141                                 system_session(dce_call->conn->dce_ctx->lp_ctx),
142                                 dce_call->conn->remote_address,
143                                 0);
144         if (sam_ctx == NULL) {
145                 return NT_STATUS_INVALID_SYSTEM_SERVICE;
146         }
147
148         /* we need the users dn and the domain dn (derived from the
149            user SID). We also need the current lm password hash in
150            order to decrypt the incoming password */
151         ret = gendb_search(sam_ctx,
152                            mem_ctx, NULL, &res, attrs,
153                            "(&(sAMAccountName=%s)(objectclass=user))",
154                            ldb_binary_encode_string(mem_ctx, r->in.account->string));
155         if (ret != 1) {
156                 status = NT_STATUS_NO_SUCH_USER; /* Converted to WRONG_PASSWORD below */
157                 goto failed;
158         }
159
160         user_dn = res[0]->dn;
161
162         user_samAccountName = ldb_msg_find_attr_as_string(res[0], "samAccountName", NULL);
163         user_objectSid = samdb_result_dom_sid(res, res[0], "objectSid");
164
165         status = samdb_result_passwords(mem_ctx, dce_call->conn->dce_ctx->lp_ctx,
166                                         res[0], &lm_pwd, NULL);
167         if (!NT_STATUS_IS_OK(status)) {
168                 goto failed;
169         } else if (!lm_pwd) {
170                 status = NT_STATUS_WRONG_PASSWORD;
171                 goto failed;
172         }
173
174         /* decrypt the password we have been given */
175         lm_pwd_blob = data_blob(lm_pwd->hash, sizeof(lm_pwd->hash));
176         arcfour_crypt_blob(pwbuf->data, 516, &lm_pwd_blob);
177         data_blob_free(&lm_pwd_blob);
178
179         if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
180                 DEBUG(3,("samr: failed to decode password buffer\n"));
181                 authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx));
182                 status =  NT_STATUS_WRONG_PASSWORD;
183                 goto failed;
184         }
185
186         if (!convert_string_talloc_handle(mem_ctx, lpcfg_iconv_handle(dce_call->conn->dce_ctx->lp_ctx),
187                                   CH_DOS, CH_UNIX,
188                                   (const char *)new_password.data,
189                                   new_password.length,
190                                   (void **)&new_pass, &converted_size)) {
191                 DEBUG(3,("samr: failed to convert incoming password buffer to unix charset\n"));
192                 authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx));
193                 status =  NT_STATUS_WRONG_PASSWORD;
194                 goto failed;
195         }
196
197         if (!convert_string_talloc_handle(mem_ctx, lpcfg_iconv_handle(dce_call->conn->dce_ctx->lp_ctx),
198                                                CH_DOS, CH_UTF16,
199                                                (const char *)new_password.data,
200                                                new_password.length,
201                                                (void **)&new_unicode_password.data, &unicode_pw_len)) {
202                 DEBUG(3,("samr: failed to convert incoming password buffer to UTF16 charset\n"));
203                 authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx));
204                 status =  NT_STATUS_WRONG_PASSWORD;
205                 goto failed;
206         }
207         new_unicode_password.length = unicode_pw_len;
208
209         E_deshash(new_pass, new_lm_hash);
210         E_old_pw_hash(new_lm_hash, lm_pwd->hash, lm_verifier.hash);
211         if (memcmp(lm_verifier.hash, r->in.hash->hash, 16) != 0) {
212                 authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx));
213                 status =  NT_STATUS_WRONG_PASSWORD;
214                 goto failed;
215         }
216
217         /* Connect to a SAMDB with user privileges for the password change */
218         sam_ctx = samdb_connect(mem_ctx,
219                                 dce_call->event_ctx,
220                                 dce_call->conn->dce_ctx->lp_ctx,
221                                 dce_call->conn->auth_state.session_info,
222                                 dce_call->conn->remote_address,
223                                 0);
224         if (sam_ctx == NULL) {
225                 return NT_STATUS_INVALID_SYSTEM_SERVICE;
226         }
227
228         /* Start transaction */
229         ret = ldb_transaction_start(sam_ctx);
230         if (ret != LDB_SUCCESS) {
231                 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
232                 return NT_STATUS_TRANSACTION_ABORTED;
233         }
234
235         /* Performs the password modification. We pass the old hashes read out
236          * from the database since they were already checked against the user-
237          * provided ones. */
238         status = samdb_set_password(sam_ctx, mem_ctx,
239                                     user_dn, NULL,
240                                     &new_unicode_password,
241                                     NULL, NULL,
242                                     lm_pwd, NULL, /* this is a user password change */
243                                     NULL,
244                                     NULL);
245         if (!NT_STATUS_IS_OK(status)) {
246                 ldb_transaction_cancel(sam_ctx);
247                 goto failed;
248         }
249
250         /* And this confirms it in a transaction commit */
251         ret = ldb_transaction_commit(sam_ctx);
252         if (ret != LDB_SUCCESS) {
253                 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
254                          ldb_dn_get_linearized(user_dn),
255                          ldb_errstring(sam_ctx)));
256                 status = NT_STATUS_TRANSACTION_ABORTED;
257                 goto failed;
258         }
259
260         status = NT_STATUS_OK;
261
262 failed:
263
264         log_password_change_event(dce_call->conn->msg_ctx,
265                                   dce_call->conn->dce_ctx->lp_ctx,
266                                   dce_call->conn->remote_address,
267                                   dce_call->conn->local_address,
268                                   "OemChangePasswordUser2",
269                                   "RC4/DES using LanMan-hash",
270                                   r->in.account->string,
271                                   user_samAccountName,
272                                   status,
273                                   user_objectSid);
274         if (NT_STATUS_IS_OK(status)) {
275                 return NT_STATUS_OK;
276         }
277         /* Only update the badPwdCount if we found the user */
278         if (NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
279                 authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx));
280         } else if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
281                 /* Don't give the game away:  (don't allow anonymous users to prove the existence of usernames) */
282                 status = NT_STATUS_WRONG_PASSWORD;
283         }
284
285         return status;
286 }
287
288
289 /*
290   samr_ChangePasswordUser3
291 */
292 NTSTATUS dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
293                                          TALLOC_CTX *mem_ctx,
294                                          struct samr_ChangePasswordUser3 *r)
295 {
296         NTSTATUS status = NT_STATUS_WRONG_PASSWORD;
297         DATA_BLOB new_password;
298         struct ldb_context *sam_ctx = NULL;
299         struct ldb_dn *user_dn = NULL;
300         int ret;
301         struct ldb_message **res;
302         const char * const attrs[] = { "unicodePwd", "dBCSPwd",
303                                        "userAccountControl",
304                                        "msDS-User-Account-Control-Computed",
305                                        "badPwdCount", "badPasswordTime",
306                                        "objectSid", NULL };
307         struct samr_Password *nt_pwd, *lm_pwd;
308         DATA_BLOB nt_pwd_blob;
309         struct samr_DomInfo1 *dominfo = NULL;
310         struct userPwdChangeFailureInformation *reject = NULL;
311         enum samPwdChangeReason reason = SAM_PWD_CHANGE_NO_ERROR;
312         uint8_t new_nt_hash[16], new_lm_hash[16];
313         struct samr_Password nt_verifier, lm_verifier;
314         const char *user_samAccountName = NULL;
315         struct dom_sid *user_objectSid = NULL;
316         enum ntlm_auth_level ntlm_auth_level
317                 = lpcfg_ntlm_auth(dce_call->conn->dce_ctx->lp_ctx);
318
319         *r->out.dominfo = NULL;
320         *r->out.reject = NULL;
321
322         /* this call should be disabled without NTLM auth */
323         if (ntlm_auth_level == NTLM_AUTH_DISABLED) {
324                 DBG_WARNING("NTLM password changes not"
325                             "permitted by configuration.\n");
326                 return NT_STATUS_NTLM_BLOCKED;
327         }
328
329         if (r->in.nt_password == NULL ||
330             r->in.nt_verifier == NULL) {
331                 return NT_STATUS_INVALID_PARAMETER;
332         }
333
334         /* Connect to a SAMDB with system privileges for fetching the old pw
335          * hashes. */
336         sam_ctx = samdb_connect(mem_ctx,
337                                 dce_call->event_ctx,
338                                 dce_call->conn->dce_ctx->lp_ctx,
339                                 system_session(dce_call->conn->dce_ctx->lp_ctx),
340                                 dce_call->conn->remote_address,
341                                 0);
342         if (sam_ctx == NULL) {
343                 return NT_STATUS_INVALID_SYSTEM_SERVICE;
344         }
345
346         /* we need the users dn and the domain dn (derived from the
347            user SID). We also need the current lm and nt password hashes
348            in order to decrypt the incoming passwords */
349         ret = gendb_search(sam_ctx,
350                            mem_ctx, NULL, &res, attrs,
351                            "(&(sAMAccountName=%s)(objectclass=user))",
352                            ldb_binary_encode_string(mem_ctx, r->in.account->string));
353         if (ret != 1) {
354                 status = NT_STATUS_NO_SUCH_USER; /* Converted to WRONG_PASSWORD below */
355                 goto failed;
356         }
357
358         user_dn = res[0]->dn;
359         user_samAccountName = ldb_msg_find_attr_as_string(res[0], "samAccountName", NULL);
360         user_objectSid = samdb_result_dom_sid(res, res[0], "objectSid");
361
362         status = samdb_result_passwords(mem_ctx, dce_call->conn->dce_ctx->lp_ctx,
363                                         res[0], &lm_pwd, &nt_pwd);
364         if (!NT_STATUS_IS_OK(status) ) {
365                 goto failed;
366         }
367
368         if (!nt_pwd) {
369                 status = NT_STATUS_WRONG_PASSWORD;
370                 goto failed;
371         }
372
373         /* decrypt the password we have been given */
374         nt_pwd_blob = data_blob(nt_pwd->hash, sizeof(nt_pwd->hash));
375         arcfour_crypt_blob(r->in.nt_password->data, 516, &nt_pwd_blob);
376         data_blob_free(&nt_pwd_blob);
377
378         if (!extract_pw_from_buffer(mem_ctx, r->in.nt_password->data, &new_password)) {
379                 DEBUG(3,("samr: failed to decode password buffer\n"));
380                 status =  NT_STATUS_WRONG_PASSWORD;
381                 goto failed;
382         }
383
384         if (r->in.nt_verifier == NULL) {
385                 status = NT_STATUS_WRONG_PASSWORD;
386                 goto failed;
387         }
388
389         /* check NT verifier */
390         mdfour(new_nt_hash, new_password.data, new_password.length);
391
392         E_old_pw_hash(new_nt_hash, nt_pwd->hash, nt_verifier.hash);
393         if (memcmp(nt_verifier.hash, r->in.nt_verifier->hash, 16) != 0) {
394                 status = NT_STATUS_WRONG_PASSWORD;
395                 goto failed;
396         }
397
398         /* check LM verifier (really not needed as we just checked the
399          * much stronger NT hash, but the RPC-SAMR test checks for
400          * this) */
401         if (lm_pwd && r->in.lm_verifier != NULL) {
402                 char *new_pass;
403                 size_t converted_size = 0;
404
405                 if (!convert_string_talloc_handle(mem_ctx, lpcfg_iconv_handle(dce_call->conn->dce_ctx->lp_ctx),
406                                           CH_UTF16, CH_UNIX,
407                                           (const char *)new_password.data,
408                                           new_password.length,
409                                           (void **)&new_pass, &converted_size)) {
410                         E_deshash(new_pass, new_lm_hash);
411                         E_old_pw_hash(new_nt_hash, lm_pwd->hash, lm_verifier.hash);
412                         if (memcmp(lm_verifier.hash, r->in.lm_verifier->hash, 16) != 0) {
413                                 status = NT_STATUS_WRONG_PASSWORD;
414                                 goto failed;
415                         }
416                 }
417         }
418
419         /* Connect to a SAMDB with user privileges for the password change */
420         sam_ctx = samdb_connect(mem_ctx,
421                                 dce_call->event_ctx,
422                                 dce_call->conn->dce_ctx->lp_ctx,
423                                 dce_call->conn->auth_state.session_info,
424                                 dce_call->conn->remote_address,
425                                 0);
426         if (sam_ctx == NULL) {
427                 return NT_STATUS_INVALID_SYSTEM_SERVICE;
428         }
429
430         ret = ldb_transaction_start(sam_ctx);
431         if (ret != LDB_SUCCESS) {
432                 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
433                 return NT_STATUS_TRANSACTION_ABORTED;
434         }
435
436         /* Performs the password modification. We pass the old hashes read out
437          * from the database since they were already checked against the user-
438          * provided ones. */
439         status = samdb_set_password(sam_ctx, mem_ctx,
440                                     user_dn, NULL,
441                                     &new_password,
442                                     NULL, NULL,
443                                     lm_pwd, nt_pwd, /* this is a user password change */
444                                     &reason,
445                                     &dominfo);
446
447         if (!NT_STATUS_IS_OK(status)) {
448                 ldb_transaction_cancel(sam_ctx);
449                 goto failed;
450         }
451
452         /* And this confirms it in a transaction commit */
453         ret = ldb_transaction_commit(sam_ctx);
454         if (ret != LDB_SUCCESS) {
455                 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
456                          ldb_dn_get_linearized(user_dn),
457                          ldb_errstring(sam_ctx)));
458                 status = NT_STATUS_TRANSACTION_ABORTED;
459                 goto failed;
460         }
461
462         status = NT_STATUS_OK;
463
464 failed:
465
466         log_password_change_event(dce_call->conn->msg_ctx,
467                                   dce_call->conn->dce_ctx->lp_ctx,
468                                   dce_call->conn->remote_address,
469                                   dce_call->conn->local_address,
470                                   "samr_ChangePasswordUser3",
471                                   "RC4/DES using NTLM-hash",
472                                   r->in.account->string,
473                                   user_samAccountName,
474                                   status,
475                                   user_objectSid);
476         if (NT_STATUS_IS_OK(status)) {
477                 return NT_STATUS_OK;
478         }
479
480         /* Only update the badPwdCount if we found the user */
481         if (NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
482                 authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx));
483         } else if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
484                 /* Don't give the game away:  (don't allow anonymous users to prove the existence of usernames) */
485                 status = NT_STATUS_WRONG_PASSWORD;
486         }
487
488         reject = talloc_zero(mem_ctx, struct userPwdChangeFailureInformation);
489         if (reject != NULL) {
490                 reject->extendedFailureReason = reason;
491
492                 *r->out.reject = reject;
493         }
494
495         *r->out.dominfo = dominfo;
496
497         return status;
498 }
499
500 /*
501   samr_ChangePasswordUser2
502
503   easy - just a subset of samr_ChangePasswordUser3
504 */
505 NTSTATUS dcesrv_samr_ChangePasswordUser2(struct dcesrv_call_state *dce_call,
506                                          TALLOC_CTX *mem_ctx,
507                                          struct samr_ChangePasswordUser2 *r)
508 {
509         struct samr_ChangePasswordUser3 r2;
510         struct samr_DomInfo1 *dominfo = NULL;
511         struct userPwdChangeFailureInformation *reject = NULL;
512
513         r2.in.server = r->in.server;
514         r2.in.account = r->in.account;
515         r2.in.nt_password = r->in.nt_password;
516         r2.in.nt_verifier = r->in.nt_verifier;
517         r2.in.lm_change = r->in.lm_change;
518         r2.in.lm_password = r->in.lm_password;
519         r2.in.lm_verifier = r->in.lm_verifier;
520         r2.in.password3 = NULL;
521         r2.out.dominfo = &dominfo;
522         r2.out.reject = &reject;
523
524         return dcesrv_samr_ChangePasswordUser3(dce_call, mem_ctx, &r2);
525 }
526
527
528 /*
529   set password via a samr_CryptPassword buffer
530 */
531 NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
532                            struct ldb_context *sam_ctx,
533                            struct ldb_dn *account_dn, struct ldb_dn *domain_dn,
534                            TALLOC_CTX *mem_ctx,
535                            struct samr_CryptPassword *pwbuf)
536 {
537         NTSTATUS nt_status;
538         DATA_BLOB new_password;
539         DATA_BLOB session_key = data_blob(NULL, 0);
540
541         nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
542         if (!NT_STATUS_IS_OK(nt_status)) {
543                 DEBUG(3,("samr: failed to get session key: %s "
544                          "=> NT_STATUS_WRONG_PASSWORD\n",
545                         nt_errstr(nt_status)));
546                 return NT_STATUS_WRONG_PASSWORD;
547         }
548
549         arcfour_crypt_blob(pwbuf->data, 516, &session_key);
550
551         if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
552                 DEBUG(3,("samr: failed to decode password buffer\n"));
553                 return NT_STATUS_WRONG_PASSWORD;
554         }
555
556         /* set the password - samdb needs to know both the domain and user DNs,
557            so the domain password policy can be used */
558         return samdb_set_password(sam_ctx, mem_ctx,
559                                   account_dn, domain_dn,
560                                   &new_password,
561                                   NULL, NULL,
562                                   NULL, NULL, /* This is a password set, not change */
563                                   NULL, NULL);
564 }
565
566
567 /*
568   set password via a samr_CryptPasswordEx buffer
569 */
570 NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call,
571                               struct ldb_context *sam_ctx,
572                               struct ldb_dn *account_dn,
573                               struct ldb_dn *domain_dn,
574                               TALLOC_CTX *mem_ctx,
575                               struct samr_CryptPasswordEx *pwbuf)
576 {
577         NTSTATUS nt_status;
578         DATA_BLOB new_password;
579         DATA_BLOB co_session_key;
580         DATA_BLOB session_key = data_blob(NULL, 0);
581         MD5_CTX ctx;
582
583         nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
584         if (!NT_STATUS_IS_OK(nt_status)) {
585                 DEBUG(3,("samr: failed to get session key: %s "
586                          "=> NT_STATUS_WRONG_PASSWORD\n",
587                         nt_errstr(nt_status)));
588                 return NT_STATUS_WRONG_PASSWORD;
589         }
590
591         co_session_key = data_blob_talloc(mem_ctx, NULL, 16);
592         if (!co_session_key.data) {
593                 return NT_STATUS_NO_MEMORY;
594         }
595
596         MD5Init(&ctx);
597         MD5Update(&ctx, &pwbuf->data[516], 16);
598         MD5Update(&ctx, session_key.data, session_key.length);
599         MD5Final(co_session_key.data, &ctx);
600
601         arcfour_crypt_blob(pwbuf->data, 516, &co_session_key);
602
603         if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
604                 DEBUG(3,("samr: failed to decode password buffer\n"));
605                 return NT_STATUS_WRONG_PASSWORD;
606         }
607
608         /* set the password - samdb needs to know both the domain and user DNs,
609            so the domain password policy can be used */
610         return samdb_set_password(sam_ctx, mem_ctx,
611                                   account_dn, domain_dn,
612                                   &new_password,
613                                   NULL, NULL,
614                                   NULL, NULL, /* This is a password set, not change */
615                                   NULL, NULL);
616 }
617
618 /*
619   set password via encrypted NT and LM hash buffers
620 */
621 NTSTATUS samr_set_password_buffers(struct dcesrv_call_state *dce_call,
622                                    struct ldb_context *sam_ctx,
623                                    struct ldb_dn *account_dn,
624                                    struct ldb_dn *domain_dn,
625                                    TALLOC_CTX *mem_ctx,
626                                    const uint8_t *lm_pwd_hash,
627                                    const uint8_t *nt_pwd_hash)
628 {
629         struct samr_Password *d_lm_pwd_hash = NULL, *d_nt_pwd_hash = NULL;
630         uint8_t random_session_key[16] = { 0, };
631         DATA_BLOB session_key = data_blob(NULL, 0);
632         DATA_BLOB in, out;
633         NTSTATUS nt_status = NT_STATUS_OK;
634
635         nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
636         if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_USER_SESSION_KEY)) {
637                 DEBUG(3,("samr: failed to get session key: %s "
638                          "=> use a random session key\n",
639                          nt_errstr(nt_status)));
640
641                 /*
642                  * Windows just uses a random key
643                  */
644                 generate_random_buffer(random_session_key,
645                                        sizeof(random_session_key));
646                 session_key = data_blob_const(random_session_key,
647                                               sizeof(random_session_key));
648                 nt_status = NT_STATUS_OK;
649         }
650         if (!NT_STATUS_IS_OK(nt_status)) {
651                 return nt_status;
652         }
653
654         if (lm_pwd_hash != NULL) {
655                 in = data_blob_const(lm_pwd_hash, 16);
656                 out = data_blob_talloc_zero(mem_ctx, 16);
657
658                 sess_crypt_blob(&out, &in, &session_key, false);
659
660                 d_lm_pwd_hash = (struct samr_Password *) out.data;
661         }
662         if (nt_pwd_hash != NULL) {
663                 in = data_blob_const(nt_pwd_hash, 16);
664                 out = data_blob_talloc_zero(mem_ctx, 16);
665
666                 sess_crypt_blob(&out, &in, &session_key, false);
667
668                 d_nt_pwd_hash = (struct samr_Password *) out.data;
669         }
670
671         if ((d_lm_pwd_hash != NULL) || (d_nt_pwd_hash != NULL)) {
672                 nt_status = samdb_set_password(sam_ctx, mem_ctx, account_dn,
673                                                domain_dn, NULL,
674                                                d_lm_pwd_hash, d_nt_pwd_hash,
675                                                NULL, NULL, /* this is a password set */
676                                                NULL, NULL);
677         }
678
679         return nt_status;
680 }