685a8e7864a027de7e320017cf57aedac685325c
[obnox/samba/samba-obnox.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
34 /*
35   samr_ChangePasswordUser
36
37   So old it is just not worth implementing
38   because it does not supply a plaintext and so we can't do password
39   complexity checking and cannot update all the other password hashes.
40
41 */
42 NTSTATUS dcesrv_samr_ChangePasswordUser(struct dcesrv_call_state *dce_call,
43                                         TALLOC_CTX *mem_ctx,
44                                         struct samr_ChangePasswordUser *r)
45 {
46         return NT_STATUS_NOT_IMPLEMENTED;
47 }
48
49 /*
50   samr_OemChangePasswordUser2
51 */
52 NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call,
53                                             TALLOC_CTX *mem_ctx,
54                                             struct samr_OemChangePasswordUser2 *r)
55 {
56         NTSTATUS status;
57         DATA_BLOB new_password, new_unicode_password;
58         char *new_pass;
59         struct samr_CryptPassword *pwbuf = r->in.password;
60         struct ldb_context *sam_ctx;
61         struct ldb_dn *user_dn;
62         int ret;
63         struct ldb_message **res;
64         const char * const attrs[] = { "objectSid", "dBCSPwd", NULL };
65         struct samr_Password *lm_pwd;
66         DATA_BLOB lm_pwd_blob;
67         uint8_t new_lm_hash[16];
68         struct samr_Password lm_verifier;
69         size_t unicode_pw_len;
70         size_t converted_size = 0;
71
72         if (pwbuf == NULL) {
73                 return NT_STATUS_INVALID_PARAMETER;
74         }
75
76         if (r->in.hash == NULL) {
77                 return NT_STATUS_INVALID_PARAMETER;
78         }
79
80         /* this call can only work with lanman auth */
81         if (!lpcfg_lanman_auth(dce_call->conn->dce_ctx->lp_ctx)) {
82                 return NT_STATUS_WRONG_PASSWORD;
83         }
84
85         /* Connect to a SAMDB with system privileges for fetching the old pw
86          * hashes. */
87         sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx,
88                                 dce_call->conn->dce_ctx->lp_ctx,
89                                 system_session(dce_call->conn->dce_ctx->lp_ctx), 0);
90         if (sam_ctx == NULL) {
91                 return NT_STATUS_INVALID_SYSTEM_SERVICE;
92         }
93
94         /* we need the users dn and the domain dn (derived from the
95            user SID). We also need the current lm password hash in
96            order to decrypt the incoming password */
97         ret = gendb_search(sam_ctx,
98                            mem_ctx, NULL, &res, attrs,
99                            "(&(sAMAccountName=%s)(objectclass=user))",
100                            r->in.account->string);
101         if (ret != 1) {
102                 /* Don't give the game away:  (don't allow anonymous users to prove the existance of usernames) */
103                 return NT_STATUS_WRONG_PASSWORD;
104         }
105
106         user_dn = res[0]->dn;
107
108         status = samdb_result_passwords(mem_ctx, dce_call->conn->dce_ctx->lp_ctx,
109                                         res[0], &lm_pwd, NULL);
110         if (!NT_STATUS_IS_OK(status) || !lm_pwd) {
111                 return NT_STATUS_WRONG_PASSWORD;
112         }
113
114         /* decrypt the password we have been given */
115         lm_pwd_blob = data_blob(lm_pwd->hash, sizeof(lm_pwd->hash));
116         arcfour_crypt_blob(pwbuf->data, 516, &lm_pwd_blob);
117         data_blob_free(&lm_pwd_blob);
118
119         if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
120                 DEBUG(3,("samr: failed to decode password buffer\n"));
121                 return NT_STATUS_WRONG_PASSWORD;
122         }
123
124         if (!convert_string_talloc_handle(mem_ctx, lpcfg_iconv_handle(dce_call->conn->dce_ctx->lp_ctx),
125                                   CH_DOS, CH_UNIX,
126                                   (const char *)new_password.data,
127                                   new_password.length,
128                                   (void **)&new_pass, &converted_size)) {
129                 DEBUG(3,("samr: failed to convert incoming password buffer to unix charset\n"));
130                 return NT_STATUS_WRONG_PASSWORD;
131         }
132
133         if (!convert_string_talloc_handle(mem_ctx, lpcfg_iconv_handle(dce_call->conn->dce_ctx->lp_ctx),
134                                                CH_DOS, CH_UTF16,
135                                                (const char *)new_password.data,
136                                                new_password.length,
137                                                (void **)&new_unicode_password.data, &unicode_pw_len)) {
138                 DEBUG(3,("samr: failed to convert incoming password buffer to UTF16 charset\n"));
139                 return NT_STATUS_WRONG_PASSWORD;
140         }
141         new_unicode_password.length = unicode_pw_len;
142
143         E_deshash(new_pass, new_lm_hash);
144         E_old_pw_hash(new_lm_hash, lm_pwd->hash, lm_verifier.hash);
145         if (memcmp(lm_verifier.hash, r->in.hash->hash, 16) != 0) {
146                 return NT_STATUS_WRONG_PASSWORD;
147         }
148
149         /* Connect to a SAMDB with user privileges for the password change */
150         sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx,
151                                 dce_call->conn->dce_ctx->lp_ctx,
152                                 dce_call->conn->auth_state.session_info, 0);
153         if (sam_ctx == NULL) {
154                 return NT_STATUS_INVALID_SYSTEM_SERVICE;
155         }
156
157         /* Start transaction */
158         ret = ldb_transaction_start(sam_ctx);
159         if (ret != LDB_SUCCESS) {
160                 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
161                 return NT_STATUS_TRANSACTION_ABORTED;
162         }
163
164         /* Performs the password modification. We pass the old hashes read out
165          * from the database since they were already checked against the user-
166          * provided ones. */
167         status = samdb_set_password(sam_ctx, mem_ctx,
168                                     user_dn, NULL,
169                                     &new_unicode_password,
170                                     NULL, NULL,
171                                     lm_pwd, NULL, /* this is a user password change */
172                                     NULL,
173                                     NULL);
174         if (!NT_STATUS_IS_OK(status)) {
175                 ldb_transaction_cancel(sam_ctx);
176                 return status;
177         }
178
179         /* And this confirms it in a transaction commit */
180         ret = ldb_transaction_commit(sam_ctx);
181         if (ret != LDB_SUCCESS) {
182                 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
183                          ldb_dn_get_linearized(user_dn),
184                          ldb_errstring(sam_ctx)));
185                 return NT_STATUS_TRANSACTION_ABORTED;
186         }
187
188         return NT_STATUS_OK;
189 }
190
191
192 /*
193   samr_ChangePasswordUser3
194 */
195 NTSTATUS dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
196                                          TALLOC_CTX *mem_ctx,
197                                          struct samr_ChangePasswordUser3 *r)
198 {
199         NTSTATUS status;
200         DATA_BLOB new_password;
201         struct ldb_context *sam_ctx = NULL;
202         struct ldb_dn *user_dn;
203         int ret;
204         struct ldb_message **res;
205         const char * const attrs[] = { "unicodePwd", "dBCSPwd", NULL };
206         struct samr_Password *nt_pwd, *lm_pwd;
207         DATA_BLOB nt_pwd_blob;
208         struct samr_DomInfo1 *dominfo = NULL;
209         struct userPwdChangeFailureInformation *reject = NULL;
210         enum samPwdChangeReason reason = SAM_PWD_CHANGE_NO_ERROR;
211         uint8_t new_nt_hash[16], new_lm_hash[16];
212         struct samr_Password nt_verifier, lm_verifier;
213
214         *r->out.dominfo = NULL;
215         *r->out.reject = NULL;
216
217         if (r->in.nt_password == NULL ||
218             r->in.nt_verifier == NULL) {
219                 return NT_STATUS_INVALID_PARAMETER;
220         }
221
222         /* Connect to a SAMDB with system privileges for fetching the old pw
223          * hashes. */
224         sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx,
225                                 dce_call->conn->dce_ctx->lp_ctx,
226                                 system_session(dce_call->conn->dce_ctx->lp_ctx), 0);
227         if (sam_ctx == NULL) {
228                 return NT_STATUS_INVALID_SYSTEM_SERVICE;
229         }
230
231         /* we need the users dn and the domain dn (derived from the
232            user SID). We also need the current lm and nt password hashes
233            in order to decrypt the incoming passwords */
234         ret = gendb_search(sam_ctx,
235                            mem_ctx, NULL, &res, attrs,
236                            "(&(sAMAccountName=%s)(objectclass=user))",
237                            r->in.account->string);
238         if (ret != 1) {
239                 /* Don't give the game away:  (don't allow anonymous users to prove the existance of usernames) */
240                 status = NT_STATUS_WRONG_PASSWORD;
241                 goto failed;
242         }
243
244         user_dn = res[0]->dn;
245
246         status = samdb_result_passwords(mem_ctx, dce_call->conn->dce_ctx->lp_ctx,
247                                         res[0], &lm_pwd, &nt_pwd);
248         if (!NT_STATUS_IS_OK(status) ) {
249                 goto failed;
250         }
251
252         if (!nt_pwd) {
253                 status = NT_STATUS_WRONG_PASSWORD;
254                 goto failed;
255         }
256
257         /* decrypt the password we have been given */
258         nt_pwd_blob = data_blob(nt_pwd->hash, sizeof(nt_pwd->hash));
259         arcfour_crypt_blob(r->in.nt_password->data, 516, &nt_pwd_blob);
260         data_blob_free(&nt_pwd_blob);
261
262         if (!extract_pw_from_buffer(mem_ctx, r->in.nt_password->data, &new_password)) {
263                 DEBUG(3,("samr: failed to decode password buffer\n"));
264                 status =  NT_STATUS_WRONG_PASSWORD;
265                 goto failed;
266         }
267
268         if (r->in.nt_verifier == NULL) {
269                 status = NT_STATUS_WRONG_PASSWORD;
270                 goto failed;
271         }
272
273         /* check NT verifier */
274         mdfour(new_nt_hash, new_password.data, new_password.length);
275
276         E_old_pw_hash(new_nt_hash, nt_pwd->hash, nt_verifier.hash);
277         if (memcmp(nt_verifier.hash, r->in.nt_verifier->hash, 16) != 0) {
278                 status = NT_STATUS_WRONG_PASSWORD;
279                 goto failed;
280         }
281
282         /* check LM verifier (really not needed as we just checked the
283          * much stronger NT hash, but the RPC-SAMR test checks for
284          * this) */
285         if (lm_pwd && r->in.lm_verifier != NULL) {
286                 char *new_pass;
287                 size_t converted_size = 0;
288
289                 if (!convert_string_talloc_handle(mem_ctx, lpcfg_iconv_handle(dce_call->conn->dce_ctx->lp_ctx),
290                                           CH_UTF16, CH_UNIX,
291                                           (const char *)new_password.data,
292                                           new_password.length,
293                                           (void **)&new_pass, &converted_size)) {
294                         E_deshash(new_pass, new_lm_hash);
295                         E_old_pw_hash(new_nt_hash, lm_pwd->hash, lm_verifier.hash);
296                         if (memcmp(lm_verifier.hash, r->in.lm_verifier->hash, 16) != 0) {
297                                 status = NT_STATUS_WRONG_PASSWORD;
298                                 goto failed;
299                         }
300                 }
301         }
302
303         /* Connect to a SAMDB with user privileges for the password change */
304         sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx,
305                                 dce_call->conn->dce_ctx->lp_ctx,
306                                 dce_call->conn->auth_state.session_info, 0);
307         if (sam_ctx == NULL) {
308                 return NT_STATUS_INVALID_SYSTEM_SERVICE;
309         }
310
311         ret = ldb_transaction_start(sam_ctx);
312         if (ret != LDB_SUCCESS) {
313                 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
314                 return NT_STATUS_TRANSACTION_ABORTED;
315         }
316
317         /* Performs the password modification. We pass the old hashes read out
318          * from the database since they were already checked against the user-
319          * provided ones. */
320         status = samdb_set_password(sam_ctx, mem_ctx,
321                                     user_dn, NULL,
322                                     &new_password,
323                                     NULL, NULL,
324                                     lm_pwd, nt_pwd, /* this is a user password change */
325                                     &reason,
326                                     &dominfo);
327
328         if (!NT_STATUS_IS_OK(status)) {
329                 ldb_transaction_cancel(sam_ctx);
330                 goto failed;
331         }
332
333         /* And this confirms it in a transaction commit */
334         ret = ldb_transaction_commit(sam_ctx);
335         if (ret != LDB_SUCCESS) {
336                 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
337                          ldb_dn_get_linearized(user_dn),
338                          ldb_errstring(sam_ctx)));
339                 status = NT_STATUS_TRANSACTION_ABORTED;
340                 goto failed;
341         }
342
343         return NT_STATUS_OK;
344
345 failed:
346         reject = talloc_zero(mem_ctx, struct userPwdChangeFailureInformation);
347         if (reject != NULL) {
348                 reject->extendedFailureReason = reason;
349
350                 *r->out.reject = reject;
351         }
352
353         *r->out.dominfo = dominfo;
354
355         return status;
356 }
357
358
359 /*
360   samr_ChangePasswordUser2
361
362   easy - just a subset of samr_ChangePasswordUser3
363 */
364 NTSTATUS dcesrv_samr_ChangePasswordUser2(struct dcesrv_call_state *dce_call,
365                                          TALLOC_CTX *mem_ctx,
366                                          struct samr_ChangePasswordUser2 *r)
367 {
368         struct samr_ChangePasswordUser3 r2;
369         struct samr_DomInfo1 *dominfo = NULL;
370         struct userPwdChangeFailureInformation *reject = NULL;
371
372         r2.in.server = r->in.server;
373         r2.in.account = r->in.account;
374         r2.in.nt_password = r->in.nt_password;
375         r2.in.nt_verifier = r->in.nt_verifier;
376         r2.in.lm_change = r->in.lm_change;
377         r2.in.lm_password = r->in.lm_password;
378         r2.in.lm_verifier = r->in.lm_verifier;
379         r2.in.password3 = NULL;
380         r2.out.dominfo = &dominfo;
381         r2.out.reject = &reject;
382
383         return dcesrv_samr_ChangePasswordUser3(dce_call, mem_ctx, &r2);
384 }
385
386
387 /*
388   set password via a samr_CryptPassword buffer
389 */
390 NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
391                            struct ldb_context *sam_ctx,
392                            struct ldb_dn *account_dn, struct ldb_dn *domain_dn,
393                            TALLOC_CTX *mem_ctx,
394                            struct samr_CryptPassword *pwbuf)
395 {
396         NTSTATUS nt_status;
397         DATA_BLOB new_password;
398         DATA_BLOB session_key = data_blob(NULL, 0);
399
400         nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
401         if (!NT_STATUS_IS_OK(nt_status)) {
402                 return nt_status;
403         }
404
405         arcfour_crypt_blob(pwbuf->data, 516, &session_key);
406
407         if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
408                 DEBUG(3,("samr: failed to decode password buffer\n"));
409                 return NT_STATUS_WRONG_PASSWORD;
410         }
411
412         /* set the password - samdb needs to know both the domain and user DNs,
413            so the domain password policy can be used */
414         return samdb_set_password(sam_ctx, mem_ctx,
415                                   account_dn, domain_dn,
416                                   &new_password,
417                                   NULL, NULL,
418                                   NULL, NULL, /* This is a password set, not change */
419                                   NULL, NULL);
420 }
421
422
423 /*
424   set password via a samr_CryptPasswordEx buffer
425 */
426 NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call,
427                               struct ldb_context *sam_ctx,
428                               struct ldb_dn *account_dn,
429                               struct ldb_dn *domain_dn,
430                               TALLOC_CTX *mem_ctx,
431                               struct samr_CryptPasswordEx *pwbuf)
432 {
433         NTSTATUS nt_status;
434         DATA_BLOB new_password;
435         DATA_BLOB co_session_key;
436         DATA_BLOB session_key = data_blob(NULL, 0);
437         MD5_CTX ctx;
438
439         nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
440         if (!NT_STATUS_IS_OK(nt_status)) {
441                 return nt_status;
442         }
443
444         co_session_key = data_blob_talloc(mem_ctx, NULL, 16);
445         if (!co_session_key.data) {
446                 return NT_STATUS_NO_MEMORY;
447         }
448
449         MD5Init(&ctx);
450         MD5Update(&ctx, &pwbuf->data[516], 16);
451         MD5Update(&ctx, session_key.data, session_key.length);
452         MD5Final(co_session_key.data, &ctx);
453
454         arcfour_crypt_blob(pwbuf->data, 516, &co_session_key);
455
456         if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
457                 DEBUG(3,("samr: failed to decode password buffer\n"));
458                 return NT_STATUS_WRONG_PASSWORD;
459         }
460
461         /* set the password - samdb needs to know both the domain and user DNs,
462            so the domain password policy can be used */
463         return samdb_set_password(sam_ctx, mem_ctx,
464                                   account_dn, domain_dn,
465                                   &new_password,
466                                   NULL, NULL,
467                                   NULL, NULL, /* This is a password set, not change */
468                                   NULL, NULL);
469 }
470
471 /*
472   set password via encrypted NT and LM hash buffers
473 */
474 NTSTATUS samr_set_password_buffers(struct dcesrv_call_state *dce_call,
475                                    struct ldb_context *sam_ctx,
476                                    struct ldb_dn *account_dn,
477                                    struct ldb_dn *domain_dn,
478                                    TALLOC_CTX *mem_ctx,
479                                    const uint8_t *lm_pwd_hash,
480                                    const uint8_t *nt_pwd_hash)
481 {
482         struct samr_Password *d_lm_pwd_hash = NULL, *d_nt_pwd_hash = NULL;
483         DATA_BLOB session_key = data_blob(NULL, 0);
484         DATA_BLOB in, out;
485         NTSTATUS nt_status = NT_STATUS_OK;
486
487         nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
488         if (!NT_STATUS_IS_OK(nt_status)) {
489                 return nt_status;
490         }
491
492         if (lm_pwd_hash != NULL) {
493                 in = data_blob_const(lm_pwd_hash, 16);
494                 out = data_blob_talloc_zero(mem_ctx, 16);
495
496                 sess_crypt_blob(&out, &in, &session_key, false);
497
498                 d_lm_pwd_hash = (struct samr_Password *) out.data;
499         }
500         if (nt_pwd_hash != NULL) {
501                 in = data_blob_const(nt_pwd_hash, 16);
502                 out = data_blob_talloc_zero(mem_ctx, 16);
503
504                 sess_crypt_blob(&out, &in, &session_key, false);
505
506                 d_nt_pwd_hash = (struct samr_Password *) out.data;
507         }
508
509         if ((d_lm_pwd_hash != NULL) || (d_nt_pwd_hash != NULL)) {
510                 nt_status = samdb_set_password(sam_ctx, mem_ctx, account_dn,
511                                                domain_dn, NULL,
512                                                d_lm_pwd_hash, d_nt_pwd_hash,
513                                                NULL, NULL, /* this is a password set */
514                                                NULL, NULL);
515         }
516
517         return nt_status;
518 }