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