Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 : Samba utility functions
4 : Copyright (C) Andrew Tridgell 1992-1998
5 : Copyright (C) Andrew Bartlett 2001-2004
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 : /* These comments regard the code to change the user's unix password: */
22 :
23 : /* fork a child process to exec passwd and write to its
24 : * tty to change a users password. This is running as the
25 : * user who is attempting to change the password.
26 : */
27 :
28 : /*
29 : * This code was copied/borrowed and stolen from various sources.
30 : * The primary source was the poppasswd.c from the authors of POPMail. This software
31 : * was included as a client to change passwords using the 'passwd' program
32 : * on the remote machine.
33 : *
34 : * This code has been hacked by Bob Nance (nance@niehs.nih.gov) and Evan Patterson
35 : * (patters2@niehs.nih.gov) at the National Institute of Environmental Health Sciences
36 : * and rights to modify, distribute or incorporate this change to the CAP suite or
37 : * using it for any other reason are granted, so long as this disclaimer is left intact.
38 : */
39 :
40 : /*
41 : This code was hacked considerably for inclusion in Samba, primarily
42 : by Andrew.Tridgell@anu.edu.au. The biggest change was the addition
43 : of the "password chat" option, which allows the easy runtime
44 : specification of the expected sequence of events to change a
45 : password.
46 : */
47 :
48 : #include "includes.h"
49 : #include "system/terminal.h"
50 : #include "system/passwd.h"
51 : #include "system/filesys.h"
52 : #include "../libcli/auth/libcli_auth.h"
53 : #include "rpc_server/samr/srv_samr_util.h"
54 : #include "passdb.h"
55 : #include "auth.h"
56 : #include "lib/util/sys_rw.h"
57 : #include "lib/util/util_str_escape.h"
58 : #include "librpc/rpc/dcerpc_samr.h"
59 :
60 : #include "lib/crypto/gnutls_helpers.h"
61 : #include <gnutls/gnutls.h>
62 : #include <gnutls/crypto.h>
63 :
64 : #undef DBGC_CLASS
65 : #define DBGC_CLASS DBGC_RPC_SRV
66 :
67 : #ifndef ALLOW_CHANGE_PASSWORD
68 : #if (defined(HAVE_TERMIOS_H) && defined(HAVE_DUP2) && defined(HAVE_SETSID))
69 : #define ALLOW_CHANGE_PASSWORD 1
70 : #endif
71 : #endif
72 :
73 : #if ALLOW_CHANGE_PASSWORD
74 :
75 0 : static int findpty(char **slave)
76 : {
77 0 : int master = -1;
78 0 : char *line = NULL;
79 0 : DIR *dirp = NULL;
80 : const char *dpname;
81 :
82 0 : *slave = NULL;
83 :
84 : #if defined(HAVE_GRANTPT)
85 : #if defined(HAVE_POSIX_OPENPT)
86 0 : master = posix_openpt(O_RDWR|O_NOCTTY);
87 : #else
88 : /* Try to open /dev/ptmx. If that fails, fall through to old method. */
89 : master = open("/dev/ptmx", O_RDWR, 0);
90 : #endif
91 0 : if (master >= 0) {
92 0 : grantpt(master);
93 0 : unlockpt(master);
94 0 : line = (char *)ptsname(master);
95 0 : if (line) {
96 0 : *slave = SMB_STRDUP(line);
97 : }
98 :
99 0 : if (*slave == NULL) {
100 0 : DEBUG(0,
101 : ("findpty: Unable to create master/slave pty pair.\n"));
102 : /* Stop fd leak on error. */
103 0 : close(master);
104 0 : return -1;
105 : } else {
106 0 : DEBUG(10,
107 : ("findpty: Allocated slave pty %s\n", *slave));
108 0 : return (master);
109 : }
110 : }
111 : #endif /* HAVE_GRANTPT */
112 :
113 0 : line = SMB_STRDUP("/dev/ptyXX");
114 0 : if (!line) {
115 0 : return (-1);
116 : }
117 :
118 0 : dirp = opendir("/dev");
119 0 : if (!dirp) {
120 0 : SAFE_FREE(line);
121 0 : return (-1);
122 : }
123 :
124 0 : while ((dpname = readdirname(dirp)) != NULL) {
125 0 : if (strncmp(dpname, "pty", 3) == 0 && strlen(dpname) == 5) {
126 0 : DEBUG(3,
127 : ("pty: try to open %s, line was %s\n", dpname,
128 : line));
129 0 : line[8] = dpname[3];
130 0 : line[9] = dpname[4];
131 0 : if ((master = open(line, O_RDWR, 0)) >= 0) {
132 0 : DEBUG(3, ("pty: opened %s\n", line));
133 0 : line[5] = 't';
134 0 : *slave = line;
135 0 : closedir(dirp);
136 0 : return (master);
137 : }
138 : }
139 : }
140 0 : closedir(dirp);
141 0 : SAFE_FREE(line);
142 0 : return (-1);
143 : }
144 :
145 0 : static int dochild(int master, const char *slavedev, const struct passwd *pass,
146 : const char *passwordprogram, bool as_root)
147 : {
148 : int slave;
149 : struct termios stermios;
150 : gid_t gid;
151 : uid_t uid;
152 0 : char * const eptrs[1] = { NULL };
153 :
154 0 : if (pass == NULL)
155 : {
156 0 : DEBUG(0,
157 : ("dochild: user doesn't exist in the UNIX password database.\n"));
158 0 : return False;
159 : }
160 :
161 0 : gid = pass->pw_gid;
162 0 : uid = pass->pw_uid;
163 :
164 0 : gain_root_privilege();
165 :
166 : /* Start new session - gets rid of controlling terminal. */
167 0 : if (setsid() < 0)
168 : {
169 0 : DEBUG(3,
170 : ("Weirdness, couldn't let go of controlling terminal\n"));
171 0 : return (False);
172 : }
173 :
174 : /* Open slave pty and acquire as new controlling terminal. */
175 0 : if ((slave = open(slavedev, O_RDWR, 0)) < 0)
176 : {
177 0 : DEBUG(3, ("More weirdness, could not open %s\n", slavedev));
178 0 : return (False);
179 : }
180 : #if defined(TIOCSCTTY) && !defined(SUNOS5)
181 : /*
182 : * On patched Solaris 10 TIOCSCTTY is defined but seems not to work,
183 : * see the discussion under
184 : * https://bugzilla.samba.org/show_bug.cgi?id=5366.
185 : */
186 0 : if (ioctl(slave, TIOCSCTTY, 0) < 0)
187 : {
188 0 : DEBUG(3, ("Error in ioctl call for slave pty\n"));
189 : /* return(False); */
190 : }
191 : #elif defined(I_PUSH) && defined(I_FIND)
192 : if (ioctl(slave, I_FIND, "ptem") == 0) {
193 : ioctl(slave, I_PUSH, "ptem");
194 : }
195 : if (ioctl(slave, I_FIND, "ldterm") == 0) {
196 : ioctl(slave, I_PUSH, "ldterm");
197 : }
198 : #endif
199 :
200 : /* Close master. */
201 0 : close(master);
202 :
203 : /* Make slave stdin/out/err of child. */
204 :
205 0 : if (dup2(slave, STDIN_FILENO) != STDIN_FILENO)
206 : {
207 0 : DEBUG(3, ("Could not re-direct stdin\n"));
208 0 : return (False);
209 : }
210 0 : if (dup2(slave, STDOUT_FILENO) != STDOUT_FILENO)
211 : {
212 0 : DEBUG(3, ("Could not re-direct stdout\n"));
213 0 : return (False);
214 : }
215 0 : if (dup2(slave, STDERR_FILENO) != STDERR_FILENO)
216 : {
217 0 : DEBUG(3, ("Could not re-direct stderr\n"));
218 0 : return (False);
219 : }
220 0 : if (slave > 2)
221 0 : close(slave);
222 :
223 : /* Set proper terminal attributes - no echo, canonical input processing,
224 : no map NL to CR/NL on output. */
225 :
226 0 : if (tcgetattr(0, &stermios) < 0)
227 : {
228 0 : DEBUG(3,
229 : ("could not read default terminal attributes on pty\n"));
230 0 : return (False);
231 : }
232 0 : stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
233 0 : stermios.c_lflag |= ICANON;
234 : #ifdef ONLCR
235 0 : stermios.c_oflag &= ~(ONLCR);
236 : #endif
237 0 : if (tcsetattr(0, TCSANOW, &stermios) < 0)
238 : {
239 0 : DEBUG(3, ("could not set attributes of pty\n"));
240 0 : return (False);
241 : }
242 :
243 : /* make us completely into the right uid */
244 0 : if (!as_root)
245 : {
246 0 : become_user_permanently(uid, gid);
247 : }
248 :
249 0 : DEBUG(10,
250 : ("Invoking '%s' as password change program.\n",
251 : passwordprogram));
252 :
253 : /* execl() password-change application */
254 0 : if (execle("/bin/sh", "sh", "-c", passwordprogram, NULL, eptrs) < 0)
255 : {
256 0 : DEBUG(3, ("Bad status returned from %s\n", passwordprogram));
257 0 : return (False);
258 : }
259 0 : return (True);
260 : }
261 :
262 0 : static int expect(int master, char *issue, char *expected)
263 : {
264 : char buffer[1024];
265 : int attempts, timeout, nread;
266 : size_t len;
267 0 : bool match = False;
268 :
269 0 : for (attempts = 0; attempts < 2; attempts++) {
270 : NTSTATUS status;
271 0 : if (!strequal(issue, ".")) {
272 0 : if (lp_passwd_chat_debug())
273 0 : DEBUG(100, ("expect: sending [%s]\n", issue));
274 :
275 0 : if ((len = sys_write(master, issue, strlen(issue))) != strlen(issue)) {
276 0 : DEBUG(2,("expect: (short) write returned %d\n",
277 : (int)len ));
278 0 : return False;
279 : }
280 : }
281 :
282 0 : if (strequal(expected, "."))
283 0 : return True;
284 :
285 : /* Initial timeout. */
286 0 : timeout = lp_passwd_chat_timeout() * 1000;
287 0 : nread = 0;
288 0 : buffer[nread] = 0;
289 :
290 : while (True) {
291 0 : status = read_fd_with_timeout(
292 : master, buffer + nread, 1,
293 : sizeof(buffer) - nread - 1,
294 : timeout, &len);
295 :
296 0 : if (!NT_STATUS_IS_OK(status)) {
297 0 : DEBUG(2, ("expect: read error %s\n",
298 : nt_errstr(status)));
299 0 : break;
300 : }
301 0 : nread += len;
302 0 : buffer[nread] = 0;
303 :
304 : {
305 : /* Eat leading/trailing whitespace before match. */
306 0 : char *str = SMB_STRDUP(buffer);
307 0 : if (!str) {
308 0 : DEBUG(2,("expect: ENOMEM\n"));
309 0 : return False;
310 : }
311 0 : trim_char(str, ' ', ' ');
312 :
313 0 : if ((match = unix_wild_match(expected, str)) == True) {
314 : /* Now data has started to return, lower timeout. */
315 0 : timeout = lp_passwd_chat_timeout() * 100;
316 : }
317 0 : SAFE_FREE(str);
318 : }
319 : }
320 :
321 0 : if (lp_passwd_chat_debug())
322 0 : DEBUG(100, ("expect: expected [%s] received [%s] match %s\n",
323 : expected, buffer, match ? "yes" : "no" ));
324 :
325 0 : if (match)
326 0 : break;
327 :
328 0 : if (!NT_STATUS_IS_OK(status)) {
329 0 : DEBUG(2, ("expect: %s\n", nt_errstr(status)));
330 0 : return False;
331 : }
332 : }
333 :
334 0 : DEBUG(10,("expect: returning %s\n", match ? "True" : "False" ));
335 0 : return match;
336 : }
337 :
338 0 : static void pwd_sub(char *buf)
339 : {
340 0 : all_string_sub(buf, "\\n", "\n", 0);
341 0 : all_string_sub(buf, "\\r", "\r", 0);
342 0 : all_string_sub(buf, "\\s", " ", 0);
343 0 : all_string_sub(buf, "\\t", "\t", 0);
344 0 : }
345 :
346 0 : static int talktochild(int master, const char *seq)
347 : {
348 0 : TALLOC_CTX *frame = talloc_stackframe();
349 0 : int count = 0;
350 : char *issue;
351 : char *expected;
352 :
353 0 : issue = talloc_strdup(frame, ".");
354 0 : if (!issue) {
355 0 : TALLOC_FREE(frame);
356 0 : return false;
357 : }
358 :
359 0 : while (next_token_talloc(frame, &seq, &expected, NULL)) {
360 0 : pwd_sub(expected);
361 0 : count++;
362 :
363 0 : if (!expect(master, issue, expected)) {
364 0 : DEBUG(3, ("Response %d incorrect\n", count));
365 0 : TALLOC_FREE(frame);
366 0 : return false;
367 : }
368 :
369 0 : if (!next_token_talloc(frame, &seq, &issue, NULL)) {
370 0 : issue = talloc_strdup(frame, ".");
371 0 : if (!issue) {
372 0 : TALLOC_FREE(frame);
373 0 : return false;
374 : }
375 : }
376 0 : pwd_sub(issue);
377 : }
378 :
379 0 : if (!strequal(issue, ".")) {
380 : /* we have one final issue to send */
381 0 : expected = talloc_strdup(frame, ".");
382 0 : if (!expected) {
383 0 : TALLOC_FREE(frame);
384 0 : return false;
385 : }
386 0 : if (!expect(master, issue, expected)) {
387 0 : TALLOC_FREE(frame);
388 0 : return False;
389 : }
390 : }
391 0 : TALLOC_FREE(frame);
392 0 : return (count > 0);
393 : }
394 :
395 0 : static bool chat_with_program(char *passwordprogram, const struct passwd *pass,
396 : char *chatsequence, bool as_root)
397 : {
398 0 : char *slavedev = NULL;
399 : int master;
400 : pid_t pid, wpid;
401 : int wstat;
402 0 : bool chstat = False;
403 : void (*saved_handler)(int);
404 :
405 0 : if (pass == NULL) {
406 0 : DEBUG(0, ("chat_with_program: user doesn't exist in the UNIX password database.\n"));
407 0 : return False;
408 : }
409 :
410 : /* allocate a pseudo-terminal device */
411 0 : if ((master = findpty(&slavedev)) < 0) {
412 0 : DEBUG(3, ("chat_with_program: Cannot Allocate pty for password change: %s\n", pass->pw_name));
413 0 : return (False);
414 : }
415 :
416 : /*
417 : * We need to temporarily stop CatchChild from eating
418 : * SIGCLD signals as it also eats the exit status code. JRA.
419 : */
420 :
421 0 : saved_handler = CatchChildLeaveStatus();
422 :
423 0 : if ((pid = fork()) < 0) {
424 0 : DEBUG(3, ("chat_with_program: Cannot fork() child for password change: %s\n", pass->pw_name));
425 0 : SAFE_FREE(slavedev);
426 0 : close(master);
427 0 : (void)CatchSignal(SIGCLD, saved_handler);
428 0 : return (False);
429 : }
430 :
431 : /* we now have a pty */
432 0 : if (pid > 0) { /* This is the parent process */
433 : /* Don't need this anymore in parent. */
434 0 : SAFE_FREE(slavedev);
435 :
436 0 : if ((chstat = talktochild(master, chatsequence)) == False) {
437 0 : DEBUG(3, ("chat_with_program: Child failed to change password: %s\n", pass->pw_name));
438 0 : kill(pid, SIGKILL); /* be sure to end this process */
439 : }
440 :
441 0 : while ((wpid = waitpid(pid, &wstat, 0)) < 0) {
442 0 : if (errno == EINTR) {
443 0 : errno = 0;
444 0 : continue;
445 : }
446 0 : break;
447 : }
448 :
449 0 : if (wpid < 0) {
450 0 : DEBUG(3, ("chat_with_program: The process is no longer waiting!\n\n"));
451 0 : close(master);
452 0 : (void)CatchSignal(SIGCLD, saved_handler);
453 0 : return (False);
454 : }
455 :
456 : /*
457 : * Go back to ignoring children.
458 : */
459 0 : (void)CatchSignal(SIGCLD, saved_handler);
460 :
461 0 : close(master);
462 :
463 0 : if (pid != wpid) {
464 0 : DEBUG(3, ("chat_with_program: We were waiting for the wrong process ID\n"));
465 0 : return (False);
466 : }
467 0 : if (WIFEXITED(wstat) && (WEXITSTATUS(wstat) != 0)) {
468 0 : DEBUG(3, ("chat_with_program: The process exited with status %d \
469 : while we were waiting\n", WEXITSTATUS(wstat)));
470 0 : return (False);
471 : }
472 : #if defined(WIFSIGNALLED) && defined(WTERMSIG)
473 : else if (WIFSIGNALLED(wstat)) {
474 : DEBUG(3, ("chat_with_program: The process was killed by signal %d \
475 : while we were waiting\n", WTERMSIG(wstat)));
476 : return (False);
477 : }
478 : #endif
479 : } else {
480 : /* CHILD */
481 :
482 : /*
483 : * Lose any elevated privileges.
484 : */
485 0 : drop_effective_capability(KERNEL_OPLOCK_CAPABILITY);
486 0 : drop_effective_capability(DMAPI_ACCESS_CAPABILITY);
487 :
488 : /* make sure it doesn't freeze */
489 0 : alarm(20);
490 :
491 0 : if (as_root)
492 0 : become_root();
493 :
494 0 : DEBUG(3, ("chat_with_program: Dochild for user %s (uid=%d,gid=%d) (as_root = %s)\n", pass->pw_name,
495 : (int)getuid(), (int)getgid(), BOOLSTR(as_root) ));
496 0 : chstat = dochild(master, slavedev, pass, passwordprogram, as_root);
497 :
498 0 : if (as_root)
499 0 : unbecome_root();
500 :
501 : /*
502 : * The child should never return from dochild() ....
503 : */
504 :
505 0 : DEBUG(0, ("chat_with_program: Error: dochild() returned %d\n", chstat));
506 0 : exit(1);
507 : }
508 :
509 0 : if (chstat)
510 0 : DEBUG(3, ("chat_with_program: Password change %ssuccessful for user %s\n",
511 : (chstat ? "" : "un"), pass->pw_name));
512 0 : return (chstat);
513 : }
514 :
515 0 : bool chgpasswd(const char *name, const char *rhost, const struct passwd *pass,
516 : const char *oldpass, const char *newpass, bool as_root)
517 : {
518 0 : const struct loadparm_substitution *lp_sub =
519 0 : loadparm_s3_global_substitution();
520 0 : char *passwordprogram = NULL;
521 0 : char *chatsequence = NULL;
522 : size_t i;
523 : size_t len;
524 0 : TALLOC_CTX *ctx = talloc_tos();
525 :
526 0 : if (!oldpass) {
527 0 : oldpass = "";
528 : }
529 :
530 0 : DEBUG(3, ("chgpasswd: Password change (as_root=%s) for user: %s\n", BOOLSTR(as_root), name));
531 :
532 : #ifdef DEBUG_PASSWORD
533 0 : DEBUG(100, ("chgpasswd: Passwords: old=%s new=%s\n", oldpass, newpass));
534 : #endif
535 :
536 : /* Take the passed information and test it for minimum criteria */
537 :
538 : /* Password is same as old password */
539 0 : if (strcmp(oldpass, newpass) == 0) {
540 : /* don't allow same password */
541 0 : DEBUG(2, ("chgpasswd: Password Change: %s, New password is same as old\n", name)); /* log the attempt */
542 0 : return (False); /* inform the user */
543 : }
544 :
545 : /*
546 : * Check the old and new passwords don't contain any control
547 : * characters.
548 : */
549 :
550 0 : len = strlen(oldpass);
551 0 : for (i = 0; i < len; i++) {
552 0 : if (iscntrl((int)oldpass[i])) {
553 0 : DEBUG(0, ("chgpasswd: oldpass contains control characters (disallowed).\n"));
554 0 : return False;
555 : }
556 : }
557 :
558 0 : len = strlen(newpass);
559 0 : for (i = 0; i < len; i++) {
560 0 : if (iscntrl((int)newpass[i])) {
561 0 : DEBUG(0, ("chgpasswd: newpass contains control characters (disallowed).\n"));
562 0 : return False;
563 : }
564 : }
565 :
566 : #ifdef WITH_PAM
567 0 : if (lp_pam_password_change()) {
568 : bool ret;
569 : #ifdef HAVE_SETLOCALE
570 0 : const char *prevlocale = setlocale(LC_ALL, "C");
571 : #endif
572 :
573 0 : if (as_root)
574 0 : become_root();
575 :
576 0 : if (pass) {
577 0 : ret = smb_pam_passchange(pass->pw_name, rhost,
578 : oldpass, newpass);
579 : } else {
580 0 : ret = smb_pam_passchange(name, rhost, oldpass,
581 : newpass);
582 : }
583 :
584 0 : if (as_root)
585 0 : unbecome_root();
586 :
587 : #ifdef HAVE_SETLOCALE
588 0 : setlocale(LC_ALL, prevlocale);
589 : #endif
590 :
591 0 : return ret;
592 : }
593 : #endif
594 :
595 : /* A non-PAM password change just doen't make sense without a valid local user */
596 :
597 0 : if (pass == NULL) {
598 0 : DEBUG(0, ("chgpasswd: user %s doesn't exist in the UNIX password database.\n", name));
599 0 : return false;
600 : }
601 :
602 0 : passwordprogram = lp_passwd_program(ctx, lp_sub);
603 0 : if (!passwordprogram || !*passwordprogram) {
604 0 : DEBUG(2, ("chgpasswd: Null password program - no password changing\n"));
605 0 : return false;
606 : }
607 0 : chatsequence = lp_passwd_chat(ctx, lp_sub);
608 0 : if (!chatsequence || !*chatsequence) {
609 0 : DEBUG(2, ("chgpasswd: Null chat sequence - no password changing\n"));
610 0 : return false;
611 : }
612 :
613 0 : if (as_root) {
614 : /* The password program *must* contain the user name to work. Fail if not. */
615 0 : if (strstr_m(passwordprogram, "%u") == NULL) {
616 0 : DEBUG(0,("chgpasswd: Running as root the 'passwd program' parameter *MUST* contain \
617 : the string %%u, and the given string %s does not.\n", passwordprogram ));
618 0 : return false;
619 : }
620 : }
621 :
622 0 : passwordprogram = talloc_string_sub(ctx, passwordprogram, "%u", name);
623 0 : if (!passwordprogram) {
624 0 : return false;
625 : }
626 :
627 : /* note that we do NOT substitute the %o and %n in the password program
628 : as this would open up a security hole where the user could use
629 : a new password containing shell escape characters */
630 :
631 0 : chatsequence = talloc_string_sub(ctx, chatsequence, "%u", name);
632 0 : if (!chatsequence) {
633 0 : return false;
634 : }
635 0 : chatsequence = talloc_all_string_sub(ctx,
636 : chatsequence,
637 : "%o",
638 : oldpass);
639 0 : if (!chatsequence) {
640 0 : return false;
641 : }
642 0 : chatsequence = talloc_all_string_sub(ctx,
643 : chatsequence,
644 : "%n",
645 : newpass);
646 0 : if (chatsequence == NULL) {
647 0 : return false;
648 : }
649 0 : return chat_with_program(passwordprogram,
650 : pass,
651 : chatsequence,
652 : as_root);
653 : }
654 :
655 : #else /* ALLOW_CHANGE_PASSWORD */
656 :
657 : bool chgpasswd(const char *name, const struct passwd *pass,
658 : const char *oldpass, const char *newpass, bool as_root)
659 : {
660 : DEBUG(0, ("chgpasswd: Unix Password changing not compiled in (user=%s)\n", name));
661 : return (False);
662 : }
663 : #endif /* ALLOW_CHANGE_PASSWORD */
664 :
665 : /***********************************************************
666 : Decrypt and verify a user password change.
667 :
668 : The 516 byte long buffers are encrypted with the old NT and
669 : old LM passwords, and if the NT passwords are present, both
670 : buffers contain a unicode string.
671 :
672 : After decrypting the buffers, check the password is correct by
673 : matching the old hashed passwords with the passwords in the passdb.
674 :
675 : ************************************************************/
676 :
677 4 : static NTSTATUS check_oem_password(const char *user,
678 : uchar password_encrypted_with_lm_hash[516],
679 : const uchar old_lm_hash_encrypted[16],
680 : uchar password_encrypted_with_nt_hash[516],
681 : const uchar old_nt_hash_encrypted[16],
682 : struct samu *sampass,
683 : char **pp_new_passwd)
684 : {
685 : uchar null_pw[16];
686 : uchar null_ntpw[16];
687 : uint8_t *password_encrypted;
688 : const uint8_t *encryption_key;
689 : const uint8_t *lanman_pw, *nt_pw;
690 : uint32_t acct_ctrl;
691 : size_t new_pw_len;
692 : uchar new_nt_hash[16];
693 : uchar new_lm_hash[16];
694 : uchar verifier[16];
695 : char no_pw[2];
696 :
697 4 : bool nt_pass_set = (password_encrypted_with_nt_hash && old_nt_hash_encrypted);
698 4 : bool lm_pass_set = (password_encrypted_with_lm_hash && old_lm_hash_encrypted);
699 4 : enum ntlm_auth_level ntlm_auth_level = lp_ntlm_auth();
700 :
701 4 : gnutls_cipher_hd_t cipher_hnd = NULL;
702 : gnutls_datum_t enc_key;
703 : int rc;
704 :
705 : /* this call should be disabled without NTLM auth */
706 4 : if (ntlm_auth_level == NTLM_AUTH_DISABLED) {
707 1 : DBG_WARNING("NTLM password changes not"
708 : "permitted by configuration.\n");
709 1 : return NT_STATUS_NTLM_BLOCKED;
710 : }
711 :
712 3 : acct_ctrl = pdb_get_acct_ctrl(sampass);
713 : #if 0
714 : /* I am convinced this check here is wrong, it is valid to
715 : * change a password of a user that has a disabled account - gd */
716 :
717 : if (acct_ctrl & ACB_DISABLED) {
718 : DEBUG(2,("check_lanman_password: account %s disabled.\n", user));
719 : return NT_STATUS_ACCOUNT_DISABLED;
720 : }
721 : #endif
722 3 : if ((acct_ctrl & ACB_PWNOTREQ) && lp_null_passwords()) {
723 : /* construct a null password (in case one is needed */
724 0 : no_pw[0] = 0;
725 0 : no_pw[1] = 0;
726 0 : nt_lm_owf_gen(no_pw, null_ntpw, null_pw);
727 0 : lanman_pw = null_pw;
728 0 : nt_pw = null_pw;
729 :
730 : } else {
731 : /* save pointers to passwords so we don't have to keep looking them up */
732 3 : if (lp_lanman_auth()) {
733 0 : lanman_pw = pdb_get_lanman_passwd(sampass);
734 : } else {
735 3 : lanman_pw = NULL;
736 : }
737 3 : nt_pw = pdb_get_nt_passwd(sampass);
738 : }
739 :
740 3 : if (nt_pw && nt_pass_set) {
741 : /* IDEAL Case: passwords are in unicode, and we can
742 : * read use the password encrypted with the NT hash
743 : */
744 1 : password_encrypted = password_encrypted_with_nt_hash;
745 1 : encryption_key = nt_pw;
746 2 : } else if (lanman_pw && lm_pass_set) {
747 : /* password may still be in unicode, but use LM hash version */
748 0 : password_encrypted = password_encrypted_with_lm_hash;
749 0 : encryption_key = lanman_pw;
750 2 : } else if (nt_pass_set) {
751 0 : DEBUG(1, ("NT password change supplied for user %s, but we have no NT password to check it with\n",
752 : user));
753 0 : return NT_STATUS_WRONG_PASSWORD;
754 2 : } else if (lm_pass_set) {
755 0 : if (lp_lanman_auth()) {
756 0 : DEBUG(1, ("LM password change supplied for user %s, but we have no LanMan password to check it with\n",
757 : user));
758 : } else {
759 0 : DEBUG(1, ("LM password change supplied for user %s, but we have disabled LanMan authentication\n",
760 : user));
761 : }
762 0 : return NT_STATUS_WRONG_PASSWORD;
763 : } else {
764 2 : DEBUG(1, ("password change requested for user %s, but no password supplied!\n",
765 : user));
766 2 : return NT_STATUS_WRONG_PASSWORD;
767 : }
768 :
769 : /*
770 : * Decrypt the password with the key
771 : */
772 1 : enc_key = (gnutls_datum_t) {
773 : .data = discard_const_p(unsigned char, encryption_key),
774 : .size = 16,
775 : };
776 :
777 0 : GNUTLS_FIPS140_SET_LAX_MODE();
778 1 : rc = gnutls_cipher_init(&cipher_hnd,
779 : GNUTLS_CIPHER_ARCFOUR_128,
780 : &enc_key,
781 : NULL);
782 1 : if (rc < 0) {
783 0 : GNUTLS_FIPS140_SET_STRICT_MODE();
784 0 : return gnutls_error_to_ntstatus(rc, NT_STATUS_CRYPTO_SYSTEM_INVALID);
785 : }
786 :
787 1 : rc = gnutls_cipher_decrypt(cipher_hnd,
788 : password_encrypted,
789 : 516);
790 1 : gnutls_cipher_deinit(cipher_hnd);
791 0 : GNUTLS_FIPS140_SET_STRICT_MODE();
792 1 : if (rc < 0) {
793 0 : return gnutls_error_to_ntstatus(rc, NT_STATUS_CRYPTO_SYSTEM_INVALID);
794 : }
795 :
796 1 : if (!decode_pw_buffer(talloc_tos(),
797 : password_encrypted,
798 : pp_new_passwd,
799 : &new_pw_len,
800 : nt_pass_set ? CH_UTF16 : CH_DOS)) {
801 0 : return NT_STATUS_WRONG_PASSWORD;
802 : }
803 :
804 : /*
805 : * To ensure we got the correct new password, hash it and
806 : * use it as a key to test the passed old password.
807 : */
808 :
809 1 : if (nt_pass_set) {
810 : /* NT passwords, verify the NT hash. */
811 :
812 : /* Calculate the MD4 hash (NT compatible) of the password */
813 1 : memset(new_nt_hash, '\0', 16);
814 1 : E_md4hash(*pp_new_passwd, new_nt_hash);
815 :
816 1 : if (nt_pw) {
817 : /*
818 : * check the NT verifier
819 : */
820 1 : rc = E_old_pw_hash(new_nt_hash, nt_pw, verifier);
821 1 : if (rc != 0) {
822 0 : NTSTATUS status = NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER;
823 0 : return gnutls_error_to_ntstatus(rc, status);
824 : }
825 1 : if (!mem_equal_const_time(verifier, old_nt_hash_encrypted, 16)) {
826 0 : DEBUG(0, ("check_oem_password: old nt "
827 : "password doesn't match.\n"));
828 0 : return NT_STATUS_WRONG_PASSWORD;
829 : }
830 :
831 : /* We could check the LM password here, but there is
832 : * little point, we already know the password is
833 : * correct, and the LM password might not even be
834 : * present. */
835 :
836 : /* Further, LM hash generation algorithms
837 : * differ with charset, so we could
838 : * incorrectly fail a perfectly valid password
839 : * change */
840 : #ifdef DEBUG_PASSWORD
841 1 : DEBUG(100,
842 : ("check_oem_password: password %s ok\n", *pp_new_passwd));
843 : #endif
844 1 : return NT_STATUS_OK;
845 : }
846 :
847 0 : if (lanman_pw) {
848 : /*
849 : * check the lm verifier
850 : */
851 0 : rc = E_old_pw_hash(new_nt_hash, lanman_pw, verifier);
852 0 : if (rc != 0) {
853 0 : NTSTATUS status = NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER;
854 0 : return gnutls_error_to_ntstatus(rc, status);
855 : }
856 0 : if (!mem_equal_const_time(verifier, old_lm_hash_encrypted, 16)) {
857 0 : DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
858 0 : return NT_STATUS_WRONG_PASSWORD;
859 : }
860 : #ifdef DEBUG_PASSWORD
861 0 : DEBUG(100,
862 : ("check_oem_password: password %s ok\n", *pp_new_passwd));
863 : #endif
864 0 : return NT_STATUS_OK;
865 : }
866 : }
867 :
868 0 : if (lanman_pw && lm_pass_set) {
869 :
870 0 : E_deshash(*pp_new_passwd, new_lm_hash);
871 :
872 : /*
873 : * check the lm verifier
874 : */
875 0 : rc = E_old_pw_hash(new_lm_hash, lanman_pw, verifier);
876 0 : if (rc != 0) {
877 0 : NTSTATUS status = NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER;
878 0 : return gnutls_error_to_ntstatus(rc, status);
879 : }
880 0 : if (!mem_equal_const_time(verifier, old_lm_hash_encrypted, 16)) {
881 0 : DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
882 0 : return NT_STATUS_WRONG_PASSWORD;
883 : }
884 :
885 : #ifdef DEBUG_PASSWORD
886 0 : DEBUG(100,
887 : ("check_oem_password: password %s ok\n", *pp_new_passwd));
888 : #endif
889 0 : return NT_STATUS_OK;
890 : }
891 :
892 : /* should not be reached */
893 0 : return NT_STATUS_WRONG_PASSWORD;
894 : }
895 :
896 0 : static bool password_in_history(uint8_t nt_pw[NT_HASH_LEN],
897 : uint32_t pw_history_len,
898 : const uint8_t *pw_history)
899 : {
900 : int i;
901 :
902 0 : dump_data(100, nt_pw, NT_HASH_LEN);
903 0 : dump_data(100, pw_history, PW_HISTORY_ENTRY_LEN * pw_history_len);
904 :
905 0 : for (i=0; i<pw_history_len; i++) {
906 : uint8_t new_nt_pw_salted_md5_hash[SALTED_MD5_HASH_LEN];
907 : const uint8_t *current_salt;
908 : const uint8_t *old_nt_pw_salted_md5_hash;
909 :
910 0 : current_salt = &pw_history[i*PW_HISTORY_ENTRY_LEN];
911 0 : old_nt_pw_salted_md5_hash = current_salt + PW_HISTORY_SALT_LEN;
912 :
913 0 : if (all_zero(old_nt_pw_salted_md5_hash, SALTED_MD5_HASH_LEN)) {
914 : /* Ignore zero valued entries. */
915 0 : continue;
916 : }
917 :
918 0 : if (all_zero(current_salt, PW_HISTORY_SALT_LEN)) {
919 : /*
920 : * New format: zero salt and then plain nt hash.
921 : * Directly compare the hashes.
922 : */
923 0 : if (mem_equal_const_time(nt_pw, old_nt_pw_salted_md5_hash,
924 : SALTED_MD5_HASH_LEN))
925 : {
926 0 : return true;
927 : }
928 : } else {
929 0 : gnutls_hash_hd_t hash_hnd = NULL;
930 : int rc;
931 :
932 : /*
933 : * Old format: md5sum of salted nt hash.
934 : * Create salted version of new pw to compare.
935 : */
936 0 : rc = gnutls_hash_init(&hash_hnd, GNUTLS_DIG_MD5);
937 0 : if (rc < 0) {
938 0 : return false;
939 : }
940 :
941 0 : rc = gnutls_hash(hash_hnd, current_salt, 16);
942 0 : if (rc < 0) {
943 0 : gnutls_hash_deinit(hash_hnd, NULL);
944 0 : return false;
945 : }
946 0 : rc = gnutls_hash(hash_hnd, nt_pw, 16);
947 0 : if (rc < 0) {
948 0 : gnutls_hash_deinit(hash_hnd, NULL);
949 0 : return false;
950 : }
951 0 : gnutls_hash_deinit(hash_hnd, new_nt_pw_salted_md5_hash);
952 :
953 0 : if (mem_equal_const_time(new_nt_pw_salted_md5_hash,
954 : old_nt_pw_salted_md5_hash,
955 : SALTED_MD5_HASH_LEN)) {
956 0 : return true;
957 : }
958 : }
959 : }
960 0 : return false;
961 : }
962 :
963 : /***********************************************************
964 : This routine takes the given password and checks it against
965 : the password history. Returns True if this password has been
966 : found in the history list.
967 : ************************************************************/
968 :
969 2 : static bool check_passwd_history(struct samu *sampass, const char *plaintext)
970 : {
971 : uchar new_nt_p16[NT_HASH_LEN];
972 : const uint8_t *nt_pw;
973 : const uint8_t *pwhistory;
974 : uint32_t pwHisLen, curr_pwHisLen;
975 :
976 2 : pdb_get_account_policy(PDB_POLICY_PASSWORD_HISTORY, &pwHisLen);
977 2 : if (pwHisLen == 0) {
978 2 : return False;
979 : }
980 :
981 0 : pwhistory = pdb_get_pw_history(sampass, &curr_pwHisLen);
982 0 : if (!pwhistory || curr_pwHisLen == 0) {
983 0 : return False;
984 : }
985 :
986 : /* Only examine the minimum of the current history len and
987 : the stored history len. Avoids race conditions. */
988 0 : pwHisLen = MIN(pwHisLen,curr_pwHisLen);
989 :
990 0 : nt_pw = pdb_get_nt_passwd(sampass);
991 :
992 0 : E_md4hash(plaintext, new_nt_p16);
993 :
994 0 : if (mem_equal_const_time(nt_pw, new_nt_p16, NT_HASH_LEN)) {
995 0 : DEBUG(10,("check_passwd_history: proposed new password for user %s is the same as the current password !\n",
996 : pdb_get_username(sampass) ));
997 0 : return True;
998 : }
999 :
1000 0 : if (password_in_history(new_nt_p16, pwHisLen, pwhistory)) {
1001 0 : DEBUG(1,("check_passwd_history: proposed new password for "
1002 : "user %s found in history list !\n",
1003 : pdb_get_username(sampass) ));
1004 0 : return true;
1005 : }
1006 0 : return false;
1007 : }
1008 :
1009 : /***********************************************************
1010 : ************************************************************/
1011 :
1012 0 : NTSTATUS check_password_complexity_internal(TALLOC_CTX *tosctx,
1013 : const char *orig_cmd,
1014 : const char *username,
1015 : char **cmd_out)
1016 : {
1017 0 : const char *fallback_username = "__CVE-2026-4408_FallbackUsername__";
1018 0 : const char *inv = NULL;
1019 0 : char *cmd = NULL;
1020 0 : bool modified = false;
1021 0 : bool masked = false;
1022 0 : bool mixed_fallback = false;
1023 :
1024 0 : *cmd_out = NULL;
1025 :
1026 0 : if (username == NULL) {
1027 0 : return NT_STATUS_INVALID_USER_PRINCIPAL_NAME;
1028 : }
1029 :
1030 : /*
1031 : * This catches invalid characters in account names
1032 : * which might be problematic passing to a shell script.
1033 : */
1034 0 : inv = strstr_for_invalid_account_characters(username);
1035 0 : if (inv != NULL) {
1036 0 : char *le_username = log_escape(tosctx, username);
1037 :
1038 0 : DBG_WARNING("username '%s' has invalid or dangerous characters\n",
1039 : le_username);
1040 :
1041 0 : TALLOC_FREE(le_username);
1042 :
1043 0 : return NT_STATUS_INVALID_USER_PRINCIPAL_NAME;
1044 : }
1045 :
1046 : /*
1047 : * This masks the remaining unsafe characters which
1048 : * are not already caught by strstr_for_invalid_account_characters()
1049 : * with '_'.
1050 : *
1051 : * Then it replaces %u with an single quoted
1052 : * and/or shell escaped version of the masked username.
1053 : */
1054 0 : cmd = talloc_string_sub_unsafe(tosctx,
1055 : orig_cmd,
1056 : 'u',
1057 : username,
1058 : STRING_SUB_UNSAFE_CHARACTERS,
1059 : '_',
1060 : fallback_username,
1061 : &modified,
1062 : &masked,
1063 : &mixed_fallback);
1064 0 : if (cmd == NULL) {
1065 0 : return NT_STATUS_NO_MEMORY;
1066 : }
1067 :
1068 : /*
1069 : * Now warn about unexpected values
1070 : */
1071 :
1072 0 : if (mixed_fallback) {
1073 0 : D_WARNING("CVE-2026-4408: "
1074 : "strange quoting in 'check password script', "
1075 : "falling back to replace %%u with %s, "
1076 : "use testparm to fix the configuration\n",
1077 : fallback_username);
1078 0 : D_WARNING("CVE-2026-4408: "
1079 : "You should use '%%u', or SAMBA_CPS_ACCOUNT_NAME "
1080 : "inside of 'check password script'.\n");
1081 0 : } else if (masked) {
1082 0 : char *le_username = log_escape(tosctx, username);
1083 :
1084 0 : D_WARNING("CVE-2026-4408: "
1085 : "replaced %%u with masked value instead of: %s\n",
1086 : le_username);
1087 0 : D_WARNING("CVE-2026-4408: "
1088 : "You should use SAMBA_CPS_ACCOUNT_NAME inside "
1089 : "'check password script' instead of %%u.\n");
1090 :
1091 0 : TALLOC_FREE(le_username);
1092 : }
1093 :
1094 0 : *cmd_out = cmd;
1095 0 : return NT_STATUS_OK;
1096 : }
1097 :
1098 :
1099 2 : NTSTATUS check_password_complexity(const char *username,
1100 : const char *fullname,
1101 : const char *password,
1102 : enum samPwdChangeReason *samr_reject_reason)
1103 : {
1104 : int check_ret;
1105 : NTSTATUS status;
1106 2 : TALLOC_CTX *tosctx = talloc_tos();
1107 1 : const struct loadparm_substitution *lp_sub =
1108 1 : loadparm_s3_global_substitution();
1109 2 : const char *orig_cmd = NULL;
1110 2 : char *cmd = NULL;
1111 :
1112 2 : orig_cmd = lp_check_password_script(tosctx, lp_sub);
1113 2 : if (orig_cmd == NULL || orig_cmd[0] == '\0') {
1114 2 : return NT_STATUS_OK;
1115 : }
1116 :
1117 : /* note we don't use 'fullname' or 'password' here */
1118 0 : status = check_password_complexity_internal(tosctx,
1119 : orig_cmd,
1120 : username,
1121 : &cmd);
1122 0 : if (!NT_STATUS_IS_OK(status)) {
1123 0 : return status;
1124 : }
1125 :
1126 0 : check_ret = setenv("SAMBA_CPS_ACCOUNT_NAME", username, 1);
1127 0 : if (check_ret != 0) {
1128 0 : return map_nt_error_from_unix_common(errno);
1129 : }
1130 0 : unsetenv("SAMBA_CPS_USER_PRINCIPAL_NAME");
1131 0 : if (fullname != NULL) {
1132 0 : check_ret = setenv("SAMBA_CPS_FULL_NAME", fullname, 1);
1133 : } else {
1134 0 : unsetenv("SAMBA_CPS_FULL_NAME");
1135 : }
1136 0 : if (check_ret != 0) {
1137 0 : return map_nt_error_from_unix_common(errno);
1138 : }
1139 0 : check_ret = smbrunsecret(cmd, password);
1140 0 : unsetenv("SAMBA_CPS_ACCOUNT_NAME");
1141 0 : unsetenv("SAMBA_CPS_USER_PRINCIPAL_NAME");
1142 0 : unsetenv("SAMBA_CPS_FULL_NAME");
1143 0 : DEBUG(5,("check_password_complexity: check password script (%s) "
1144 : "returned [%d]\n", cmd, check_ret));
1145 0 : TALLOC_FREE(cmd);
1146 :
1147 0 : if (check_ret != 0) {
1148 0 : DEBUG(1,("check_password_complexity: "
1149 : "check password script said new password is not good "
1150 : "enough!\n"));
1151 0 : if (samr_reject_reason) {
1152 0 : *samr_reject_reason = SAM_PWD_CHANGE_NOT_COMPLEX;
1153 : }
1154 0 : return NT_STATUS_PASSWORD_RESTRICTION;
1155 : }
1156 :
1157 0 : return NT_STATUS_OK;
1158 : }
1159 :
1160 : /***********************************************************
1161 : Code to change the oem password. Changes both the lanman
1162 : and NT hashes. Old_passwd is almost always NULL.
1163 : NOTE this function is designed to be called as root. Check the old password
1164 : is correct before calling. JRA.
1165 : ************************************************************/
1166 :
1167 2 : NTSTATUS change_oem_password(struct samu *hnd, const char *rhost,
1168 : char *old_passwd, char *new_passwd,
1169 : bool as_root,
1170 : enum samPwdChangeReason *samr_reject_reason)
1171 : {
1172 : uint32_t min_len;
1173 : uint32_t refuse;
1174 2 : TALLOC_CTX *tosctx = talloc_tos();
1175 2 : struct passwd *pass = NULL;
1176 2 : const char *username = pdb_get_username(hnd);
1177 2 : const char *fullname = pdb_get_fullname(hnd);
1178 2 : time_t can_change_time = pdb_get_pass_can_change_time(hnd);
1179 : NTSTATUS status;
1180 :
1181 2 : if (samr_reject_reason) {
1182 0 : *samr_reject_reason = SAM_PWD_CHANGE_NO_ERROR;
1183 : }
1184 :
1185 : /* check to see if the secdesc has previously been set to disallow */
1186 2 : if (!pdb_get_pass_can_change(hnd)) {
1187 0 : DEBUG(1, ("user %s does not have permissions to change password\n", username));
1188 0 : if (samr_reject_reason) {
1189 0 : *samr_reject_reason = SAM_PWD_CHANGE_NO_ERROR;
1190 : }
1191 0 : return NT_STATUS_ACCOUNT_RESTRICTION;
1192 : }
1193 :
1194 : /* check to see if it is a Machine account and if the policy
1195 : * denies machines to change the password. *
1196 : * Should we deny also SRVTRUST and/or DOMSTRUST ? .SSS. */
1197 2 : if (pdb_get_acct_ctrl(hnd) & ACB_WSTRUST) {
1198 0 : if (pdb_get_account_policy(PDB_POLICY_REFUSE_MACHINE_PW_CHANGE, &refuse) && refuse) {
1199 0 : DEBUG(1, ("Machine %s cannot change password now, "
1200 : "denied by Refuse Machine Password Change policy\n",
1201 : username));
1202 0 : if (samr_reject_reason) {
1203 0 : *samr_reject_reason = SAM_PWD_CHANGE_NO_ERROR;
1204 : }
1205 0 : return NT_STATUS_ACCOUNT_RESTRICTION;
1206 : }
1207 : }
1208 :
1209 : /* removed calculation here, because passdb now calculates
1210 : based on policy. jmcd */
1211 2 : if ((can_change_time != 0) && (time(NULL) < can_change_time)) {
1212 0 : DEBUG(1, ("user %s cannot change password now, must "
1213 : "wait until %s\n", username,
1214 : http_timestring(tosctx, can_change_time)));
1215 0 : if (samr_reject_reason) {
1216 0 : *samr_reject_reason = SAM_PWD_CHANGE_NO_ERROR;
1217 : }
1218 0 : return NT_STATUS_ACCOUNT_RESTRICTION;
1219 : }
1220 :
1221 2 : if (pdb_get_account_policy(PDB_POLICY_MIN_PASSWORD_LEN, &min_len) && (str_charnum(new_passwd) < min_len)) {
1222 0 : DEBUG(1, ("user %s cannot change password - password too short\n",
1223 : username));
1224 0 : DEBUGADD(1, (" account policy min password len = %d\n", min_len));
1225 0 : if (samr_reject_reason) {
1226 0 : *samr_reject_reason = SAM_PWD_CHANGE_PASSWORD_TOO_SHORT;
1227 : }
1228 0 : return NT_STATUS_PASSWORD_RESTRICTION;
1229 : /* return NT_STATUS_PWD_TOO_SHORT; */
1230 : }
1231 :
1232 2 : if (check_passwd_history(hnd,new_passwd)) {
1233 0 : if (samr_reject_reason) {
1234 0 : *samr_reject_reason = SAM_PWD_CHANGE_PWD_IN_HISTORY;
1235 : }
1236 0 : return NT_STATUS_PASSWORD_RESTRICTION;
1237 : }
1238 :
1239 2 : pass = Get_Pwnam_alloc(tosctx, username);
1240 2 : if (!pass) {
1241 0 : DEBUG(1, ("change_oem_password: Username %s does not exist in system !?!\n", username));
1242 0 : return NT_STATUS_ACCESS_DENIED;
1243 : }
1244 :
1245 2 : status = check_password_complexity(username,
1246 : fullname,
1247 : new_passwd,
1248 : samr_reject_reason);
1249 2 : if (!NT_STATUS_IS_OK(status)) {
1250 0 : TALLOC_FREE(pass);
1251 0 : return status;
1252 : }
1253 :
1254 : /*
1255 : * If unix password sync was requested, attempt to change
1256 : * the /etc/passwd database first. Return failure if this cannot
1257 : * be done.
1258 : *
1259 : * This occurs before the oem change, because we don't want to
1260 : * update it if chgpasswd failed.
1261 : *
1262 : * Conditional on lp_unix_password_sync() because we don't want
1263 : * to touch the unix db unless we have admin permission.
1264 : */
1265 :
1266 2 : if(lp_unix_password_sync() &&
1267 0 : !chgpasswd(username, rhost, pass, old_passwd, new_passwd,
1268 : as_root)) {
1269 0 : TALLOC_FREE(pass);
1270 0 : return NT_STATUS_ACCESS_DENIED;
1271 : }
1272 :
1273 2 : TALLOC_FREE(pass);
1274 :
1275 2 : if (!pdb_set_plaintext_passwd (hnd, new_passwd)) {
1276 0 : return NT_STATUS_ACCESS_DENIED;
1277 : }
1278 :
1279 : /* Now write it into the file. */
1280 2 : return pdb_update_sam_account (hnd);
1281 : }
1282 :
1283 : /***********************************************************
1284 : Code to check and change the OEM hashed password.
1285 : ************************************************************/
1286 :
1287 4 : NTSTATUS pass_oem_change(char *user, const char *rhost,
1288 : uchar password_encrypted_with_lm_hash[516],
1289 : const uchar old_lm_hash_encrypted[16],
1290 : uchar password_encrypted_with_nt_hash[516],
1291 : const uchar old_nt_hash_encrypted[16],
1292 : enum samPwdChangeReason *reject_reason)
1293 : {
1294 4 : char *new_passwd = NULL;
1295 4 : struct samu *sampass = NULL;
1296 : NTSTATUS nt_status;
1297 4 : bool ret = false;
1298 4 : bool updated_badpw = false;
1299 : NTSTATUS update_login_attempts_status;
1300 4 : char *mutex_name_by_user = NULL;
1301 4 : struct named_mutex *mtx = NULL;
1302 :
1303 4 : if (!(sampass = samu_new(NULL))) {
1304 0 : return NT_STATUS_NO_MEMORY;
1305 : }
1306 :
1307 4 : become_root();
1308 4 : ret = pdb_getsampwnam(sampass, user);
1309 4 : unbecome_root();
1310 :
1311 4 : if (ret == false) {
1312 0 : DEBUG(0,("pass_oem_change: getsmbpwnam returned NULL\n"));
1313 0 : nt_status = NT_STATUS_NO_SUCH_USER;
1314 0 : goto done;
1315 : }
1316 :
1317 : /* Quit if the account was locked out. */
1318 4 : if (pdb_get_acct_ctrl(sampass) & ACB_AUTOLOCK) {
1319 0 : DEBUG(3,("check_sam_security: Account for user %s was locked out.\n", user));
1320 0 : nt_status = NT_STATUS_ACCOUNT_LOCKED_OUT;
1321 0 : goto done;
1322 : }
1323 :
1324 4 : nt_status = check_oem_password(user,
1325 : password_encrypted_with_lm_hash,
1326 : old_lm_hash_encrypted,
1327 : password_encrypted_with_nt_hash,
1328 : old_nt_hash_encrypted,
1329 : sampass,
1330 : &new_passwd);
1331 :
1332 : /*
1333 : * We must re-load the sam acount information under a mutex
1334 : * lock to ensure we don't miss any concurrent account lockout
1335 : * changes.
1336 : */
1337 :
1338 : /* Clear out old sampass info. */
1339 4 : TALLOC_FREE(sampass);
1340 :
1341 4 : sampass = samu_new(NULL);
1342 4 : if (sampass == NULL) {
1343 0 : return NT_STATUS_NO_MEMORY;
1344 : }
1345 :
1346 4 : mutex_name_by_user = talloc_asprintf(NULL,
1347 : "check_sam_security_mutex_%s",
1348 : user);
1349 4 : if (mutex_name_by_user == NULL) {
1350 0 : nt_status = NT_STATUS_NO_MEMORY;
1351 0 : goto done;
1352 : }
1353 :
1354 : /* Grab the named mutex under root with 30 second timeout. */
1355 4 : become_root();
1356 4 : mtx = grab_named_mutex(NULL, mutex_name_by_user, 30);
1357 4 : if (mtx != NULL) {
1358 : /* Re-load the account information if we got the mutex. */
1359 4 : ret = pdb_getsampwnam(sampass, user);
1360 : }
1361 4 : unbecome_root();
1362 :
1363 : /* Everything from here on until mtx is freed is done under the mutex.*/
1364 :
1365 4 : if (mtx == NULL) {
1366 0 : DBG_ERR("Acquisition of mutex %s failed "
1367 : "for user %s\n",
1368 : mutex_name_by_user,
1369 : user);
1370 0 : nt_status = NT_STATUS_INTERNAL_ERROR;
1371 0 : goto done;
1372 : }
1373 :
1374 4 : if (!ret) {
1375 : /*
1376 : * Re-load of account failed. This could only happen if the
1377 : * user was deleted in the meantime.
1378 : */
1379 0 : DBG_NOTICE("reload of user '%s' in passdb failed.\n",
1380 : user);
1381 0 : nt_status = NT_STATUS_NO_SUCH_USER;
1382 0 : goto done;
1383 : }
1384 :
1385 : /*
1386 : * Check if the account is now locked out - now under the mutex.
1387 : * This can happen if the server is under
1388 : * a password guess attack and the ACB_AUTOLOCK is set by
1389 : * another process.
1390 : */
1391 4 : if (pdb_get_acct_ctrl(sampass) & ACB_AUTOLOCK) {
1392 0 : DBG_NOTICE("Account for user %s was locked out.\n", user);
1393 0 : nt_status = NT_STATUS_ACCOUNT_LOCKED_OUT;
1394 0 : goto done;
1395 : }
1396 :
1397 : /*
1398 : * Notify passdb backend of login success/failure. If not
1399 : * NT_STATUS_OK the backend doesn't like the login
1400 : */
1401 4 : update_login_attempts_status = pdb_update_login_attempts(sampass,
1402 4 : NT_STATUS_IS_OK(nt_status));
1403 :
1404 4 : if (!NT_STATUS_IS_OK(nt_status)) {
1405 3 : bool increment_bad_pw_count = false;
1406 :
1407 4 : if (NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD) &&
1408 3 : (pdb_get_acct_ctrl(sampass) & ACB_NORMAL) &&
1409 2 : NT_STATUS_IS_OK(update_login_attempts_status))
1410 : {
1411 2 : increment_bad_pw_count = true;
1412 : }
1413 :
1414 3 : if (increment_bad_pw_count) {
1415 2 : pdb_increment_bad_password_count(sampass);
1416 2 : updated_badpw = true;
1417 : } else {
1418 1 : pdb_update_bad_password_count(sampass,
1419 : &updated_badpw);
1420 : }
1421 : } else {
1422 :
1423 2 : if ((pdb_get_acct_ctrl(sampass) & ACB_NORMAL) &&
1424 1 : (pdb_get_bad_password_count(sampass) > 0)){
1425 0 : pdb_set_bad_password_count(sampass, 0, PDB_CHANGED);
1426 0 : pdb_set_bad_password_time(sampass, 0, PDB_CHANGED);
1427 0 : updated_badpw = true;
1428 : }
1429 : }
1430 :
1431 4 : if (updated_badpw) {
1432 : NTSTATUS update_status;
1433 2 : become_root();
1434 2 : update_status = pdb_update_sam_account(sampass);
1435 2 : unbecome_root();
1436 :
1437 2 : if (!NT_STATUS_IS_OK(update_status)) {
1438 0 : DEBUG(1, ("Failed to modify entry: %s\n",
1439 : nt_errstr(update_status)));
1440 : }
1441 : }
1442 :
1443 4 : if (!NT_STATUS_IS_OK(nt_status)) {
1444 3 : goto done;
1445 : }
1446 :
1447 : /* We've already checked the old password here.... */
1448 1 : become_root();
1449 1 : nt_status = change_oem_password(sampass, rhost, NULL, new_passwd,
1450 : True, reject_reason);
1451 1 : unbecome_root();
1452 :
1453 2 : BURN_STR(new_passwd);
1454 :
1455 3 : done:
1456 4 : TALLOC_FREE(sampass);
1457 4 : TALLOC_FREE(mutex_name_by_user);
1458 4 : TALLOC_FREE(mtx);
1459 :
1460 4 : return nt_status;
1461 : }
1462 :
1463 1 : NTSTATUS samr_set_password_aes(TALLOC_CTX *mem_ctx,
1464 : const DATA_BLOB *cdk,
1465 : struct samr_EncryptedPasswordAES *pwbuf,
1466 : char **new_password_str)
1467 : {
1468 1 : DATA_BLOB pw_data = data_blob_null;
1469 1 : DATA_BLOB new_password = data_blob_null;
1470 0 : const DATA_BLOB ciphertext =
1471 1 : data_blob_const(pwbuf->cipher, pwbuf->cipher_len);
1472 1 : DATA_BLOB iv = data_blob_const(pwbuf->salt, sizeof(pwbuf->salt));
1473 : NTSTATUS status;
1474 : bool ok;
1475 :
1476 1 : *new_password_str = NULL;
1477 :
1478 1 : status = samba_gnutls_aead_aes_256_cbc_hmac_sha512_decrypt(
1479 : mem_ctx,
1480 : &ciphertext,
1481 : cdk,
1482 : &samr_aes256_enc_key_salt,
1483 : &samr_aes256_mac_key_salt,
1484 : &iv,
1485 1 : pwbuf->auth_data,
1486 : &pw_data);
1487 1 : if (!NT_STATUS_IS_OK(status)) {
1488 0 : return NT_STATUS_WRONG_PASSWORD;
1489 : }
1490 :
1491 1 : ok = decode_pwd_string_from_buffer514(mem_ctx,
1492 1 : pw_data.data,
1493 : CH_UTF16,
1494 : &new_password);
1495 1 : TALLOC_FREE(pw_data.data);
1496 1 : if (!ok) {
1497 0 : DBG_NOTICE("samr: failed to decode password buffer\n");
1498 0 : return NT_STATUS_WRONG_PASSWORD;
1499 : }
1500 :
1501 2 : *new_password_str = talloc_strndup(mem_ctx,
1502 1 : (char *)new_password.data,
1503 : new_password.length);
1504 1 : TALLOC_FREE(new_password.data);
1505 1 : if (*new_password_str == NULL) {
1506 0 : return NT_STATUS_NO_MEMORY;
1507 : }
1508 1 : talloc_keep_secret(*new_password_str);
1509 :
1510 1 : return NT_STATUS_OK;
1511 : }
|