LCOV - code coverage report
Current view: top level - source3/rpc_server/samr - srv_samr_chgpasswd.c (source / functions) Hit Total Coverage
Test: coverage report for v4-17-lts 011c5a9f Lines: 146 614 23.8 %
Date: 2026-05-29 04:08:37 Functions: 6 15 40.0 %

          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             : }

Generated by: LCOV version 1.13