root/source3/smbd/chgpasswd.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. findpty
  2. dochild
  3. expect
  4. pwd_sub
  5. talktochild
  6. chat_with_program
  7. chgpasswd
  8. chgpasswd
  9. check_lanman_password
  10. change_lanman_password
  11. pass_oem_change
  12. check_oem_password
  13. check_passwd_history
  14. change_oem_password

   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 
  50 static NTSTATUS check_oem_password(const char *user,
  51                                    uchar password_encrypted_with_lm_hash[516],
  52                                    const uchar old_lm_hash_encrypted[16],
  53                                    uchar password_encrypted_with_nt_hash[516],
  54                                    const uchar old_nt_hash_encrypted[16],
  55                                    struct samu *sampass,
  56                                    char **pp_new_passwd);
  57 
  58 #if ALLOW_CHANGE_PASSWORD
  59 
  60 static int findpty(char **slave)
     /* [<][>][^][v][top][bottom][index][help] */
  61 {
  62         int master = -1;
  63         char *line = NULL;
  64         SMB_STRUCT_DIR *dirp = NULL;
  65         const char *dpname;
  66 
  67         *slave = NULL;
  68 
  69 #if defined(HAVE_GRANTPT)
  70         /* Try to open /dev/ptmx. If that fails, fall through to old method. */
  71         if ((master = sys_open("/dev/ptmx", O_RDWR, 0)) >= 0) {
  72                 grantpt(master);
  73                 unlockpt(master);
  74                 line = (char *)ptsname(master);
  75                 if (line) {
  76                         *slave = SMB_STRDUP(line);
  77                 }
  78 
  79                 if (*slave == NULL) {
  80                         DEBUG(0,
  81                               ("findpty: Unable to create master/slave pty pair.\n"));
  82                         /* Stop fd leak on error. */
  83                         close(master);
  84                         return -1;
  85                 } else {
  86                         DEBUG(10,
  87                               ("findpty: Allocated slave pty %s\n", *slave));
  88                         return (master);
  89                 }
  90         }
  91 #endif /* HAVE_GRANTPT */
  92 
  93         line = SMB_STRDUP("/dev/ptyXX");
  94         if (!line) {
  95                 return (-1);
  96         }
  97 
  98         dirp = sys_opendir("/dev");
  99         if (!dirp) {
 100                 SAFE_FREE(line);
 101                 return (-1);
 102         }
 103 
 104         while ((dpname = readdirname(dirp)) != NULL) {
 105                 if (strncmp(dpname, "pty", 3) == 0 && strlen(dpname) == 5) {
 106                         DEBUG(3,
 107                               ("pty: try to open %s, line was %s\n", dpname,
 108                                line));
 109                         line[8] = dpname[3];
 110                         line[9] = dpname[4];
 111                         if ((master = sys_open(line, O_RDWR, 0)) >= 0) {
 112                                 DEBUG(3, ("pty: opened %s\n", line));
 113                                 line[5] = 't';
 114                                 *slave = line;
 115                                 sys_closedir(dirp);
 116                                 return (master);
 117                         }
 118                 }
 119         }
 120         sys_closedir(dirp);
 121         SAFE_FREE(line);
 122         return (-1);
 123 }
 124 
 125 static int dochild(int master, const char *slavedev, const struct passwd *pass,
     /* [<][>][^][v][top][bottom][index][help] */
 126                    const char *passwordprogram, bool as_root)
 127 {
 128         int slave;
 129         struct termios stermios;
 130         gid_t gid;
 131         uid_t uid;
 132         char * const eptrs[1] = { NULL };
 133 
 134         if (pass == NULL)
 135         {
 136                 DEBUG(0,
 137                       ("dochild: user doesn't exist in the UNIX password database.\n"));
 138                 return False;
 139         }
 140 
 141         gid = pass->pw_gid;
 142         uid = pass->pw_uid;
 143 
 144         gain_root_privilege();
 145 
 146         /* Start new session - gets rid of controlling terminal. */
 147         if (setsid() < 0)
 148         {
 149                 DEBUG(3,
 150                       ("Weirdness, couldn't let go of controlling terminal\n"));
 151                 return (False);
 152         }
 153 
 154         /* Open slave pty and acquire as new controlling terminal. */
 155         if ((slave = sys_open(slavedev, O_RDWR, 0)) < 0)
 156         {
 157                 DEBUG(3, ("More weirdness, could not open %s\n", slavedev));
 158                 return (False);
 159         }
 160 #if defined(TIOCSCTTY) && !defined(SUNOS5)
 161         /*
 162          * On patched Solaris 10 TIOCSCTTY is defined but seems not to work,
 163          * see the discussion under
 164          * https://bugzilla.samba.org/show_bug.cgi?id=5366.
 165          */
 166         if (ioctl(slave, TIOCSCTTY, 0) < 0)
 167         {
 168                 DEBUG(3, ("Error in ioctl call for slave pty\n"));
 169                 /* return(False); */
 170         }
 171 #elif defined(I_PUSH) && defined(I_FIND)
 172         if (ioctl(slave, I_FIND, "ptem") == 0) {
 173                 ioctl(slave, I_PUSH, "ptem");
 174         }
 175         if (ioctl(slave, I_FIND, "ldterm") == 0) {
 176                 ioctl(slave, I_PUSH, "ldterm");
 177         }
 178 #endif
 179 
 180         /* Close master. */
 181         close(master);
 182 
 183         /* Make slave stdin/out/err of child. */
 184 
 185         if (dup2(slave, STDIN_FILENO) != STDIN_FILENO)
 186         {
 187                 DEBUG(3, ("Could not re-direct stdin\n"));
 188                 return (False);
 189         }
 190         if (dup2(slave, STDOUT_FILENO) != STDOUT_FILENO)
 191         {
 192                 DEBUG(3, ("Could not re-direct stdout\n"));
 193                 return (False);
 194         }
 195         if (dup2(slave, STDERR_FILENO) != STDERR_FILENO)
 196         {
 197                 DEBUG(3, ("Could not re-direct stderr\n"));
 198                 return (False);
 199         }
 200         if (slave > 2)
 201                 close(slave);
 202 
 203         /* Set proper terminal attributes - no echo, canonical input processing,
 204            no map NL to CR/NL on output. */
 205 
 206         if (tcgetattr(0, &stermios) < 0)
 207         {
 208                 DEBUG(3,
 209                       ("could not read default terminal attributes on pty\n"));
 210                 return (False);
 211         }
 212         stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
 213         stermios.c_lflag |= ICANON;
 214 #ifdef ONLCR
 215         stermios.c_oflag &= ~(ONLCR);
 216 #endif
 217         if (tcsetattr(0, TCSANOW, &stermios) < 0)
 218         {
 219                 DEBUG(3, ("could not set attributes of pty\n"));
 220                 return (False);
 221         }
 222 
 223         /* make us completely into the right uid */
 224         if (!as_root)
 225         {
 226                 become_user_permanently(uid, gid);
 227         }
 228 
 229         DEBUG(10,
 230               ("Invoking '%s' as password change program.\n",
 231                passwordprogram));
 232 
 233         /* execl() password-change application */
 234         if (execle("/bin/sh", "sh", "-c", passwordprogram, NULL, eptrs) < 0)
 235         {
 236                 DEBUG(3, ("Bad status returned from %s\n", passwordprogram));
 237                 return (False);
 238         }
 239         return (True);
 240 }
 241 
 242 static int expect(int master, char *issue, char *expected)
     /* [<][>][^][v][top][bottom][index][help] */
 243 {
 244         char buffer[1024];
 245         int attempts, timeout, nread;
 246         size_t len;
 247         bool match = False;
 248 
 249         for (attempts = 0; attempts < 2; attempts++) {
 250                 NTSTATUS status;
 251                 if (!strequal(issue, ".")) {
 252                         if (lp_passwd_chat_debug())
 253                                 DEBUG(100, ("expect: sending [%s]\n", issue));
 254 
 255                         if ((len = sys_write(master, issue, strlen(issue))) != strlen(issue)) {
 256                                 DEBUG(2,("expect: (short) write returned %d\n",
 257                                          (int)len ));
 258                                 return False;
 259                         }
 260                 }
 261 
 262                 if (strequal(expected, "."))
 263                         return True;
 264 
 265                 /* Initial timeout. */
 266                 timeout = lp_passwd_chat_timeout() * 1000;
 267                 nread = 0;
 268                 buffer[nread] = 0;
 269 
 270                 while (True) {
 271                         status = read_fd_with_timeout(
 272                                 master, buffer + nread, 1,
 273                                 sizeof(buffer) - nread - 1,
 274                                 timeout, &len);
 275 
 276                         if (!NT_STATUS_IS_OK(status)) {
 277                                 break;
 278                         }
 279                         nread += len;
 280                         buffer[nread] = 0;
 281 
 282                         {
 283                                 /* Eat leading/trailing whitespace before match. */
 284                                 char *str = SMB_STRDUP(buffer);
 285                                 if (!str) {
 286                                         DEBUG(2,("expect: ENOMEM\n"));
 287                                         return False;
 288                                 }
 289                                 trim_char(str, ' ', ' ');
 290 
 291                                 if ((match = unix_wild_match(expected, str)) == True) {
 292                                         /* Now data has started to return, lower timeout. */
 293                                         timeout = lp_passwd_chat_timeout() * 100;
 294                                 }
 295                                 SAFE_FREE(str);
 296                         }
 297                 }
 298 
 299                 if (lp_passwd_chat_debug())
 300                         DEBUG(100, ("expect: expected [%s] received [%s] match %s\n",
 301                                     expected, buffer, match ? "yes" : "no" ));
 302 
 303                 if (match)
 304                         break;
 305 
 306                 if (!NT_STATUS_IS_OK(status)) {
 307                         DEBUG(2, ("expect: %s\n", nt_errstr(status)));
 308                         return False;
 309                 }
 310         }
 311 
 312         DEBUG(10,("expect: returning %s\n", match ? "True" : "False" ));
 313         return match;
 314 }
 315 
 316 static void pwd_sub(char *buf)
     /* [<][>][^][v][top][bottom][index][help] */
 317 {
 318         all_string_sub(buf, "\\n", "\n", 0);
 319         all_string_sub(buf, "\\r", "\r", 0);
 320         all_string_sub(buf, "\\s", " ", 0);
 321         all_string_sub(buf, "\\t", "\t", 0);
 322 }
 323 
 324 static int talktochild(int master, const char *seq)
     /* [<][>][^][v][top][bottom][index][help] */
 325 {
 326         TALLOC_CTX *frame = talloc_stackframe();
 327         int count = 0;
 328         char *issue;
 329         char *expected;
 330 
 331         issue = talloc_strdup(frame, ".");
 332         if (!issue) {
 333                 TALLOC_FREE(frame);
 334                 return false;
 335         }
 336 
 337         while (next_token_talloc(frame, &seq, &expected, NULL)) {
 338                 pwd_sub(expected);
 339                 count++;
 340 
 341                 if (!expect(master, issue, expected)) {
 342                         DEBUG(3, ("Response %d incorrect\n", count));
 343                         TALLOC_FREE(frame);
 344                         return false;
 345                 }
 346 
 347                 if (!next_token_talloc(frame, &seq, &issue, NULL)) {
 348                         issue = talloc_strdup(frame, ".");
 349                         if (!issue) {
 350                                 TALLOC_FREE(frame);
 351                                 return false;
 352                         }
 353                 }
 354                 pwd_sub(issue);
 355         }
 356 
 357         if (!strequal(issue, ".")) {
 358                 /* we have one final issue to send */
 359                 expected = talloc_strdup(frame, ".");
 360                 if (!expected) {
 361                         TALLOC_FREE(frame);
 362                         return false;
 363                 }
 364                 if (!expect(master, issue, expected)) {
 365                         TALLOC_FREE(frame);
 366                         return False;
 367                 }
 368         }
 369         TALLOC_FREE(frame);
 370         return (count > 0);
 371 }
 372 
 373 static bool chat_with_program(char *passwordprogram, const struct passwd *pass,
     /* [<][>][^][v][top][bottom][index][help] */
 374                               char *chatsequence, bool as_root)
 375 {
 376         char *slavedev = NULL;
 377         int master;
 378         pid_t pid, wpid;
 379         int wstat;
 380         bool chstat = False;
 381 
 382         if (pass == NULL) {
 383                 DEBUG(0, ("chat_with_program: user doesn't exist in the UNIX password database.\n"));
 384                 return False;
 385         }
 386 
 387         /* allocate a pseudo-terminal device */
 388         if ((master = findpty(&slavedev)) < 0) {
 389                 DEBUG(3, ("chat_with_program: Cannot Allocate pty for password change: %s\n", pass->pw_name));
 390                 return (False);
 391         }
 392 
 393         /*
 394          * We need to temporarily stop CatchChild from eating
 395          * SIGCLD signals as it also eats the exit status code. JRA.
 396          */
 397 
 398         CatchChildLeaveStatus();
 399 
 400         if ((pid = sys_fork()) < 0) {
 401                 DEBUG(3, ("chat_with_program: Cannot fork() child for password change: %s\n", pass->pw_name));
 402                 SAFE_FREE(slavedev);
 403                 close(master);
 404                 CatchChild();
 405                 return (False);
 406         }
 407 
 408         /* we now have a pty */
 409         if (pid > 0) {                  /* This is the parent process */
 410                 /* Don't need this anymore in parent. */
 411                 SAFE_FREE(slavedev);
 412 
 413                 if ((chstat = talktochild(master, chatsequence)) == False) {
 414                         DEBUG(3, ("chat_with_program: Child failed to change password: %s\n", pass->pw_name));
 415                         kill(pid, SIGKILL);     /* be sure to end this process */
 416                 }
 417 
 418                 while ((wpid = sys_waitpid(pid, &wstat, 0)) < 0) {
 419                         if (errno == EINTR) {
 420                                 errno = 0;
 421                                 continue;
 422                         }
 423                         break;
 424                 }
 425 
 426                 if (wpid < 0) {
 427                         DEBUG(3, ("chat_with_program: The process is no longer waiting!\n\n"));
 428                         close(master);
 429                         CatchChild();
 430                         return (False);
 431                 }
 432 
 433                 /*
 434                  * Go back to ignoring children.
 435                  */
 436                 CatchChild();
 437 
 438                 close(master);
 439 
 440                 if (pid != wpid) {
 441                         DEBUG(3, ("chat_with_program: We were waiting for the wrong process ID\n"));
 442                         return (False);
 443                 }
 444                 if (WIFEXITED(wstat) && (WEXITSTATUS(wstat) != 0)) {
 445                         DEBUG(3, ("chat_with_program: The process exited with status %d \
 446 while we were waiting\n", WEXITSTATUS(wstat)));
 447                         return (False);
 448                 }
 449 #if defined(WIFSIGNALLED) && defined(WTERMSIG)
 450                 else if (WIFSIGNALLED(wstat)) {
 451                         DEBUG(3, ("chat_with_program: The process was killed by signal %d \
 452 while we were waiting\n", WTERMSIG(wstat)));
 453                         return (False);
 454                 }
 455 #endif
 456         } else {
 457                 /* CHILD */
 458 
 459                 /*
 460                  * Lose any elevated privileges.
 461                  */
 462                 drop_effective_capability(KERNEL_OPLOCK_CAPABILITY);
 463                 drop_effective_capability(DMAPI_ACCESS_CAPABILITY);
 464 
 465                 /* make sure it doesn't freeze */
 466                 alarm(20);
 467 
 468                 if (as_root)
 469                         become_root();
 470 
 471                 DEBUG(3, ("chat_with_program: Dochild for user %s (uid=%d,gid=%d) (as_root = %s)\n", pass->pw_name,
 472                        (int)getuid(), (int)getgid(), BOOLSTR(as_root) ));
 473                 chstat = dochild(master, slavedev, pass, passwordprogram, as_root);
 474 
 475                 if (as_root)
 476                         unbecome_root();
 477 
 478                 /*
 479                  * The child should never return from dochild() ....
 480                  */
 481 
 482                 DEBUG(0, ("chat_with_program: Error: dochild() returned %d\n", chstat));
 483                 exit(1);
 484         }
 485 
 486         if (chstat)
 487                 DEBUG(3, ("chat_with_program: Password change %ssuccessful for user %s\n",
 488                        (chstat ? "" : "un"), pass->pw_name));
 489         return (chstat);
 490 }
 491 
 492 bool chgpasswd(const char *name, const struct passwd *pass,
     /* [<][>][^][v][top][bottom][index][help] */
 493                const char *oldpass, const char *newpass, bool as_root)
 494 {
 495         char *passwordprogram = NULL;
 496         char *chatsequence = NULL;
 497         size_t i;
 498         size_t len;
 499         TALLOC_CTX *ctx = talloc_tos();
 500 
 501         if (!oldpass) {
 502                 oldpass = "";
 503         }
 504 
 505         DEBUG(3, ("chgpasswd: Password change (as_root=%s) for user: %s\n", BOOLSTR(as_root), name));
 506 
 507 #ifdef DEBUG_PASSWORD
 508         DEBUG(100, ("chgpasswd: Passwords: old=%s new=%s\n", oldpass, newpass));
 509 #endif
 510 
 511         /* Take the passed information and test it for minimum criteria */
 512 
 513         /* Password is same as old password */
 514         if (strcmp(oldpass, newpass) == 0) {
 515                 /* don't allow same password */
 516                 DEBUG(2, ("chgpasswd: Password Change: %s, New password is same as old\n", name));      /* log the attempt */
 517                 return (False); /* inform the user */
 518         }
 519 
 520         /*
 521          * Check the old and new passwords don't contain any control
 522          * characters.
 523          */
 524 
 525         len = strlen(oldpass);
 526         for (i = 0; i < len; i++) {
 527                 if (iscntrl((int)oldpass[i])) {
 528                         DEBUG(0, ("chgpasswd: oldpass contains control characters (disallowed).\n"));
 529                         return False;
 530                 }
 531         }
 532 
 533         len = strlen(newpass);
 534         for (i = 0; i < len; i++) {
 535                 if (iscntrl((int)newpass[i])) {
 536                         DEBUG(0, ("chgpasswd: newpass contains control characters (disallowed).\n"));
 537                         return False;
 538                 }
 539         }
 540 
 541 #ifdef WITH_PAM
 542         if (lp_pam_password_change()) {
 543                 bool ret;
 544 #ifdef HAVE_SETLOCALE
 545                 const char *prevlocale = setlocale(LC_ALL, "C");
 546 #endif
 547 
 548                 if (as_root)
 549                         become_root();
 550 
 551                 if (pass) {
 552                         ret = smb_pam_passchange(pass->pw_name, oldpass, newpass);
 553                 } else {
 554                         ret = smb_pam_passchange(name, oldpass, newpass);
 555                 }
 556 
 557                 if (as_root)
 558                         unbecome_root();
 559 
 560 #ifdef HAVE_SETLOCALE
 561                 setlocale(LC_ALL, prevlocale);
 562 #endif
 563 
 564                 return ret;
 565         }
 566 #endif
 567 
 568         /* A non-PAM password change just doen't make sense without a valid local user */
 569 
 570         if (pass == NULL) {
 571                 DEBUG(0, ("chgpasswd: user %s doesn't exist in the UNIX password database.\n", name));
 572                 return false;
 573         }
 574 
 575         passwordprogram = talloc_strdup(ctx, lp_passwd_program());
 576         if (!passwordprogram || !*passwordprogram) {
 577                 DEBUG(2, ("chgpasswd: Null password program - no password changing\n"));
 578                 return false;
 579         }
 580         chatsequence = talloc_strdup(ctx, lp_passwd_chat());
 581         if (!chatsequence || !*chatsequence) {
 582                 DEBUG(2, ("chgpasswd: Null chat sequence - no password changing\n"));
 583                 return false;
 584         }
 585 
 586         if (as_root) {
 587                 /* The password program *must* contain the user name to work. Fail if not. */
 588                 if (strstr_m(passwordprogram, "%u") == NULL) {
 589                         DEBUG(0,("chgpasswd: Running as root the 'passwd program' parameter *MUST* contain \
 590 the string %%u, and the given string %s does not.\n", passwordprogram ));
 591                         return false;
 592                 }
 593         }
 594 
 595         passwordprogram = talloc_string_sub(ctx, passwordprogram, "%u", name);
 596         if (!passwordprogram) {
 597                 return false;
 598         }
 599 
 600         /* note that we do NOT substitute the %o and %n in the password program
 601            as this would open up a security hole where the user could use
 602            a new password containing shell escape characters */
 603 
 604         chatsequence = talloc_string_sub(ctx, chatsequence, "%u", name);
 605         if (!chatsequence) {
 606                 return false;
 607         }
 608         chatsequence = talloc_all_string_sub(ctx,
 609                                         chatsequence,
 610                                         "%o",
 611                                         oldpass);
 612         if (!chatsequence) {
 613                 return false;
 614         }
 615         chatsequence = talloc_all_string_sub(ctx,
 616                                         chatsequence,
 617                                         "%n",
 618                                         newpass);
 619         return chat_with_program(passwordprogram,
 620                                 pass,
 621                                 chatsequence,
 622                                 as_root);
 623 }
 624 
 625 #else /* ALLOW_CHANGE_PASSWORD */
 626 
 627 bool chgpasswd(const char *name, const struct passwd *pass, 
     /* [<][>][^][v][top][bottom][index][help] */
 628                const char *oldpass, const char *newpass, bool as_root)
 629 {
 630         DEBUG(0, ("chgpasswd: Unix Password changing not compiled in (user=%s)\n", name));
 631         return (False);
 632 }
 633 #endif /* ALLOW_CHANGE_PASSWORD */
 634 
 635 /***********************************************************
 636  Code to check the lanman hashed password.
 637 ************************************************************/
 638 
 639 bool check_lanman_password(char *user, uchar * pass1,
     /* [<][>][^][v][top][bottom][index][help] */
 640                            uchar * pass2, struct samu **hnd)
 641 {
 642         uchar unenc_new_pw[16];
 643         uchar unenc_old_pw[16];
 644         struct samu *sampass = NULL;
 645         uint32 acct_ctrl;
 646         const uint8 *lanman_pw;
 647         bool ret;
 648 
 649         if ( !(sampass = samu_new(NULL)) ) {
 650                 DEBUG(0, ("samu_new() failed!\n"));
 651                 return False;
 652         }
 653         
 654         become_root();
 655         ret = pdb_getsampwnam(sampass, user);
 656         unbecome_root();
 657 
 658         if (ret == False) {
 659                 DEBUG(0,("check_lanman_password: getsampwnam returned NULL\n"));
 660                 TALLOC_FREE(sampass);
 661                 return False;
 662         }
 663         
 664         acct_ctrl = pdb_get_acct_ctrl     (sampass);
 665         lanman_pw = pdb_get_lanman_passwd (sampass);
 666 
 667         if (acct_ctrl & ACB_DISABLED) {
 668                 DEBUG(0,("check_lanman_password: account %s disabled.\n", user));
 669                 TALLOC_FREE(sampass);
 670                 return False;
 671         }
 672 
 673         if (lanman_pw == NULL) {
 674                 if (acct_ctrl & ACB_PWNOTREQ) {
 675                         /* this saves the pointer for the caller */
 676                         *hnd = sampass;
 677                         return True;
 678                 } else {
 679                         DEBUG(0, ("check_lanman_password: no lanman password !\n"));
 680                         TALLOC_FREE(sampass);
 681                         return False;
 682                 }
 683         }
 684 
 685         /* Get the new lanman hash. */
 686         D_P16(lanman_pw, pass2, unenc_new_pw);
 687 
 688         /* Use this to get the old lanman hash. */
 689         D_P16(unenc_new_pw, pass1, unenc_old_pw);
 690 
 691         /* Check that the two old passwords match. */
 692         if (memcmp(lanman_pw, unenc_old_pw, 16)) {
 693                 DEBUG(0,("check_lanman_password: old password doesn't match.\n"));
 694                 TALLOC_FREE(sampass);
 695                 return False;
 696         }
 697 
 698         /* this saves the pointer for the caller */
 699         *hnd = sampass;
 700         return True;
 701 }
 702 
 703 /***********************************************************
 704  Code to change the lanman hashed password.
 705  It nulls out the NT hashed password as it will
 706  no longer be valid.
 707  NOTE this function is designed to be called as root. Check the old password
 708  is correct before calling. JRA.
 709 ************************************************************/
 710 
 711 bool change_lanman_password(struct samu *sampass, uchar *pass2)
     /* [<][>][^][v][top][bottom][index][help] */
 712 {
 713         uchar null_pw[16];
 714         uchar unenc_new_pw[16];
 715         bool ret;
 716         uint32 acct_ctrl;
 717         const uint8 *pwd;
 718 
 719         if (sampass == NULL) {
 720                 DEBUG(0,("change_lanman_password: no smb password entry.\n"));
 721                 return False;
 722         }
 723         
 724         acct_ctrl = pdb_get_acct_ctrl(sampass);
 725         pwd = pdb_get_lanman_passwd(sampass);
 726 
 727         if (acct_ctrl & ACB_DISABLED) {
 728                 DEBUG(0,("change_lanman_password: account %s disabled.\n",
 729                        pdb_get_username(sampass)));
 730                 return False;
 731         }
 732 
 733         if (pwd == NULL) { 
 734                 if (acct_ctrl & ACB_PWNOTREQ) {
 735                         uchar no_pw[14];
 736 
 737                         ZERO_STRUCT(no_pw);
 738 
 739                         E_P16(no_pw, null_pw);
 740 
 741                         pwd = null_pw;
 742                 } else {
 743                         DEBUG(0,("change_lanman_password: no lanman password !\n"));
 744                         return False;
 745                 }
 746         }
 747 
 748         /* Get the new lanman hash. */
 749         D_P16(pwd, pass2, unenc_new_pw);
 750 
 751         if (!pdb_set_lanman_passwd(sampass, unenc_new_pw, PDB_CHANGED)) {
 752                 return False;
 753         }
 754 
 755         if (!pdb_set_nt_passwd    (sampass, NULL, PDB_CHANGED)) {
 756                 return False;   /* We lose the NT hash. Sorry. */
 757         }
 758 
 759         if (!pdb_set_pass_last_set_time  (sampass, time(NULL), PDB_CHANGED)) {
 760                 TALLOC_FREE(sampass);
 761                 /* Not quite sure what this one qualifies as, but this will do */
 762                 return False;
 763         }
 764 
 765         /* Now flush the sam_passwd struct to persistent storage */
 766         ret = NT_STATUS_IS_OK(pdb_update_sam_account (sampass));
 767 
 768         return ret;
 769 }
 770 
 771 /***********************************************************
 772  Code to check and change the OEM hashed password.
 773 ************************************************************/
 774 
 775 NTSTATUS pass_oem_change(char *user,
     /* [<][>][^][v][top][bottom][index][help] */
 776                          uchar password_encrypted_with_lm_hash[516],
 777                          const uchar old_lm_hash_encrypted[16],
 778                          uchar password_encrypted_with_nt_hash[516],
 779                          const uchar old_nt_hash_encrypted[16],
 780                          uint32 *reject_reason)
 781 {
 782         char *new_passwd = NULL;
 783         struct samu *sampass = NULL;
 784         NTSTATUS nt_status;
 785         bool ret = false;
 786 
 787         if (!(sampass = samu_new(NULL))) {
 788                 return NT_STATUS_NO_MEMORY;
 789         }
 790 
 791         become_root();
 792         ret = pdb_getsampwnam(sampass, user);
 793         unbecome_root();
 794 
 795         if (ret == false) {
 796                 DEBUG(0,("pass_oem_change: getsmbpwnam returned NULL\n"));
 797                 TALLOC_FREE(sampass);
 798                 return NT_STATUS_NO_SUCH_USER;
 799         }
 800 
 801         nt_status = check_oem_password(user,
 802                                        password_encrypted_with_lm_hash,
 803                                        old_lm_hash_encrypted,
 804                                        password_encrypted_with_nt_hash,
 805                                        old_nt_hash_encrypted,
 806                                        sampass,
 807                                        &new_passwd);
 808 
 809         if (!NT_STATUS_IS_OK(nt_status)) {
 810                 TALLOC_FREE(sampass);
 811                 return nt_status;
 812         }
 813 
 814         /* We've already checked the old password here.... */
 815         become_root();
 816         nt_status = change_oem_password(sampass, NULL, new_passwd, True, reject_reason);
 817         unbecome_root();
 818 
 819         memset(new_passwd, 0, strlen(new_passwd));
 820 
 821         TALLOC_FREE(sampass);
 822 
 823         return nt_status;
 824 }
 825 
 826 /***********************************************************
 827  Decrypt and verify a user password change.
 828 
 829  The 516 byte long buffers are encrypted with the old NT and
 830  old LM passwords, and if the NT passwords are present, both
 831  buffers contain a unicode string.
 832 
 833  After decrypting the buffers, check the password is correct by
 834  matching the old hashed passwords with the passwords in the passdb.
 835 
 836 ************************************************************/
 837 
 838 static NTSTATUS check_oem_password(const char *user,
     /* [<][>][^][v][top][bottom][index][help] */
 839                                    uchar password_encrypted_with_lm_hash[516],
 840                                    const uchar old_lm_hash_encrypted[16],
 841                                    uchar password_encrypted_with_nt_hash[516],
 842                                    const uchar old_nt_hash_encrypted[16],
 843                                    struct samu *sampass,
 844                                    char **pp_new_passwd)
 845 {
 846         uchar null_pw[16];
 847         uchar null_ntpw[16];
 848         uint8 *password_encrypted;
 849         const uint8 *encryption_key;
 850         const uint8 *lanman_pw, *nt_pw;
 851         uint32 acct_ctrl;
 852         uint32 new_pw_len;
 853         uchar new_nt_hash[16];
 854         uchar new_lm_hash[16];
 855         uchar verifier[16];
 856         char no_pw[2];
 857 
 858         bool nt_pass_set = (password_encrypted_with_nt_hash && old_nt_hash_encrypted);
 859         bool lm_pass_set = (password_encrypted_with_lm_hash && old_lm_hash_encrypted);
 860 
 861         acct_ctrl = pdb_get_acct_ctrl(sampass);
 862 #if 0
 863         /* I am convinced this check here is wrong, it is valid to
 864          * change a password of a user that has a disabled account - gd */
 865 
 866         if (acct_ctrl & ACB_DISABLED) {
 867                 DEBUG(2,("check_lanman_password: account %s disabled.\n", user));
 868                 return NT_STATUS_ACCOUNT_DISABLED;
 869         }
 870 #endif
 871         if ((acct_ctrl & ACB_PWNOTREQ) && lp_null_passwords()) {
 872                 /* construct a null password (in case one is needed */
 873                 no_pw[0] = 0;
 874                 no_pw[1] = 0;
 875                 nt_lm_owf_gen(no_pw, null_ntpw, null_pw);
 876                 lanman_pw = null_pw;
 877                 nt_pw = null_pw;
 878 
 879         } else {
 880                 /* save pointers to passwords so we don't have to keep looking them up */
 881                 if (lp_lanman_auth()) {
 882                         lanman_pw = pdb_get_lanman_passwd(sampass);
 883                 } else {
 884                         lanman_pw = NULL;
 885                 }
 886                 nt_pw = pdb_get_nt_passwd(sampass);
 887         }
 888 
 889         if (nt_pw && nt_pass_set) {
 890                 /* IDEAL Case: passwords are in unicode, and we can
 891                  * read use the password encrypted with the NT hash
 892                  */
 893                 password_encrypted = password_encrypted_with_nt_hash;
 894                 encryption_key = nt_pw;
 895         } else if (lanman_pw && lm_pass_set) {
 896                 /* password may still be in unicode, but use LM hash version */
 897                 password_encrypted = password_encrypted_with_lm_hash;
 898                 encryption_key = lanman_pw;
 899         } else if (nt_pass_set) {
 900                 DEBUG(1, ("NT password change supplied for user %s, but we have no NT password to check it with\n", 
 901                           user));
 902                 return NT_STATUS_WRONG_PASSWORD;
 903         } else if (lm_pass_set) {
 904                 if (lp_lanman_auth()) {
 905                         DEBUG(1, ("LM password change supplied for user %s, but we have no LanMan password to check it with\n", 
 906                                   user));
 907                 } else {
 908                         DEBUG(1, ("LM password change supplied for user %s, but we have disabled LanMan authentication\n", 
 909                                   user));
 910                 }
 911                 return NT_STATUS_WRONG_PASSWORD;
 912         } else {
 913                 DEBUG(1, ("password change requested for user %s, but no password supplied!\n", 
 914                           user));
 915                 return NT_STATUS_WRONG_PASSWORD;
 916         }
 917 
 918         /*
 919          * Decrypt the password with the key
 920          */
 921         SamOEMhash( password_encrypted, encryption_key, 516);
 922 
 923         if (!decode_pw_buffer(talloc_tos(),
 924                                 password_encrypted,
 925                                 pp_new_passwd,
 926                                 &new_pw_len,
 927                                 nt_pass_set ? STR_UNICODE : STR_ASCII)) {
 928                 return NT_STATUS_WRONG_PASSWORD;
 929         }
 930 
 931         /*
 932          * To ensure we got the correct new password, hash it and
 933          * use it as a key to test the passed old password.
 934          */
 935 
 936         if (nt_pass_set) {
 937                 /* NT passwords, verify the NT hash. */
 938 
 939                 /* Calculate the MD4 hash (NT compatible) of the password */
 940                 memset(new_nt_hash, '\0', 16);
 941                 E_md4hash(*pp_new_passwd, new_nt_hash);
 942 
 943                 if (nt_pw) {
 944                         /*
 945                          * check the NT verifier
 946                          */
 947                         E_old_pw_hash(new_nt_hash, nt_pw, verifier);
 948                         if (memcmp(verifier, old_nt_hash_encrypted, 16)) {
 949                                 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
 950                                 return NT_STATUS_WRONG_PASSWORD;
 951                         }
 952 
 953                         /* We could check the LM password here, but there is
 954                          * little point, we already know the password is
 955                          * correct, and the LM password might not even be
 956                          * present. */
 957 
 958                         /* Further, LM hash generation algorithms
 959                          * differ with charset, so we could
 960                          * incorrectly fail a perfectly valid password
 961                          * change */
 962 #ifdef DEBUG_PASSWORD
 963                         DEBUG(100,
 964                               ("check_oem_password: password %s ok\n", *pp_new_passwd));
 965 #endif
 966                         return NT_STATUS_OK;
 967                 }
 968 
 969                 if (lanman_pw) {
 970                         /*
 971                          * check the lm verifier
 972                          */
 973                         E_old_pw_hash(new_nt_hash, lanman_pw, verifier);
 974                         if (memcmp(verifier, old_lm_hash_encrypted, 16)) {
 975                                 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
 976                                 return NT_STATUS_WRONG_PASSWORD;
 977                         }
 978 #ifdef DEBUG_PASSWORD
 979                         DEBUG(100,
 980                               ("check_oem_password: password %s ok\n", *pp_new_passwd));
 981 #endif
 982                         return NT_STATUS_OK;
 983                 }
 984         }
 985 
 986         if (lanman_pw && lm_pass_set) {
 987 
 988                 E_deshash(*pp_new_passwd, new_lm_hash);
 989 
 990                 /*
 991                  * check the lm verifier
 992                  */
 993                 E_old_pw_hash(new_lm_hash, lanman_pw, verifier);
 994                 if (memcmp(verifier, old_lm_hash_encrypted, 16)) {
 995                         DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
 996                         return NT_STATUS_WRONG_PASSWORD;
 997                 }
 998 
 999 #ifdef DEBUG_PASSWORD
1000                 DEBUG(100,
1001                       ("check_oem_password: password %s ok\n", *pp_new_passwd));
1002 #endif
1003                 return NT_STATUS_OK;
1004         }
1005 
1006         /* should not be reached */
1007         return NT_STATUS_WRONG_PASSWORD;
1008 }
1009 
1010 /***********************************************************
1011  This routine takes the given password and checks it against
1012  the password history. Returns True if this password has been
1013  found in the history list.
1014 ************************************************************/
1015 
1016 static bool check_passwd_history(struct samu *sampass, const char *plaintext)
     /* [<][>][^][v][top][bottom][index][help] */
1017 {
1018         uchar new_nt_p16[NT_HASH_LEN];
1019         uchar zero_md5_nt_pw[SALTED_MD5_HASH_LEN];
1020         const uint8 *nt_pw;
1021         const uint8 *pwhistory;
1022         bool found = False;
1023         int i;
1024         uint32 pwHisLen, curr_pwHisLen;
1025 
1026         pdb_get_account_policy(AP_PASSWORD_HISTORY, &pwHisLen);
1027         if (pwHisLen == 0) {
1028                 return False;
1029         }
1030 
1031         pwhistory = pdb_get_pw_history(sampass, &curr_pwHisLen);
1032         if (!pwhistory || curr_pwHisLen == 0) {
1033                 return False;
1034         }
1035 
1036         /* Only examine the minimum of the current history len and
1037            the stored history len. Avoids race conditions. */
1038         pwHisLen = MIN(pwHisLen,curr_pwHisLen);
1039 
1040         nt_pw = pdb_get_nt_passwd(sampass);
1041 
1042         E_md4hash(plaintext, new_nt_p16);
1043 
1044         if (!memcmp(nt_pw, new_nt_p16, NT_HASH_LEN)) {
1045                 DEBUG(10,("check_passwd_history: proposed new password for user %s is the same as the current password !\n",
1046                         pdb_get_username(sampass) ));
1047                 return True;
1048         }
1049 
1050         dump_data(100, new_nt_p16, NT_HASH_LEN);
1051         dump_data(100, pwhistory, PW_HISTORY_ENTRY_LEN*pwHisLen);
1052 
1053         memset(zero_md5_nt_pw, '\0', SALTED_MD5_HASH_LEN);
1054         for (i=0; i<pwHisLen; i++) {
1055                 uchar new_nt_pw_salted_md5_hash[SALTED_MD5_HASH_LEN];
1056                 const uchar *current_salt = &pwhistory[i*PW_HISTORY_ENTRY_LEN];
1057                 const uchar *old_nt_pw_salted_md5_hash = &pwhistory[(i*PW_HISTORY_ENTRY_LEN)+
1058                                                         PW_HISTORY_SALT_LEN];
1059                 if (!memcmp(zero_md5_nt_pw, old_nt_pw_salted_md5_hash, SALTED_MD5_HASH_LEN)) {
1060                         /* Ignore zero valued entries. */
1061                         continue;
1062                 }
1063                 /* Create salted versions of new to compare. */
1064                 E_md5hash(current_salt, new_nt_p16, new_nt_pw_salted_md5_hash);
1065 
1066                 if (!memcmp(new_nt_pw_salted_md5_hash, old_nt_pw_salted_md5_hash, SALTED_MD5_HASH_LEN)) {
1067                         DEBUG(1,("check_passwd_history: proposed new password for user %s found in history list !\n",
1068                                 pdb_get_username(sampass) ));
1069                         found = True;
1070                         break;
1071                 }
1072         }
1073         return found;
1074 }
1075 
1076 /***********************************************************
1077  Code to change the oem password. Changes both the lanman
1078  and NT hashes.  Old_passwd is almost always NULL.
1079  NOTE this function is designed to be called as root. Check the old password
1080  is correct before calling. JRA.
1081 ************************************************************/
1082 
1083 NTSTATUS change_oem_password(struct samu *hnd, char *old_passwd, char *new_passwd, bool as_root, uint32 *samr_reject_reason)
     /* [<][>][^][v][top][bottom][index][help] */
1084 {
1085         uint32 min_len;
1086         uint32 refuse;
1087         struct passwd *pass = NULL;
1088         const char *username = pdb_get_username(hnd);
1089         time_t can_change_time = pdb_get_pass_can_change_time(hnd);
1090 
1091         if (samr_reject_reason) {
1092                 *samr_reject_reason = Undefined;
1093         }
1094 
1095         /* check to see if the secdesc has previously been set to disallow */
1096         if (!pdb_get_pass_can_change(hnd)) {
1097                 DEBUG(1, ("user %s does not have permissions to change password\n", username));
1098                 if (samr_reject_reason) {
1099                         *samr_reject_reason = SAMR_REJECT_OTHER;
1100                 }
1101                 return NT_STATUS_ACCOUNT_RESTRICTION;
1102         }
1103 
1104         /* check to see if it is a Machine account and if the policy
1105          * denies machines to change the password. *
1106          * Should we deny also SRVTRUST and/or DOMSTRUST ? .SSS. */
1107         if (pdb_get_acct_ctrl(hnd) & ACB_WSTRUST) {
1108                 if (pdb_get_account_policy(AP_REFUSE_MACHINE_PW_CHANGE, &refuse) && refuse) {
1109                         DEBUG(1, ("Machine %s cannot change password now, "
1110                                   "denied by Refuse Machine Password Change policy\n",
1111                                   username));
1112                         if (samr_reject_reason) {
1113                                 *samr_reject_reason = SAMR_REJECT_OTHER;
1114                         }
1115                         return NT_STATUS_ACCOUNT_RESTRICTION;
1116                 }
1117         }
1118 
1119         /* removed calculation here, becuase passdb now calculates
1120            based on policy.  jmcd */
1121         if ((can_change_time != 0) && (time(NULL) < can_change_time)) {
1122                 DEBUG(1, ("user %s cannot change password now, must "
1123                           "wait until %s\n", username,
1124                           http_timestring(talloc_tos(), can_change_time)));
1125                 if (samr_reject_reason) {
1126                         *samr_reject_reason = SAMR_REJECT_OTHER;
1127                 }
1128                 return NT_STATUS_ACCOUNT_RESTRICTION;
1129         }
1130 
1131         if (pdb_get_account_policy(AP_MIN_PASSWORD_LEN, &min_len) && (str_charnum(new_passwd) < min_len)) {
1132                 DEBUG(1, ("user %s cannot change password - password too short\n", 
1133                           username));
1134                 DEBUGADD(1, (" account policy min password len = %d\n", min_len));
1135                 if (samr_reject_reason) {
1136                         *samr_reject_reason = SAMR_REJECT_TOO_SHORT;
1137                 }
1138                 return NT_STATUS_PASSWORD_RESTRICTION;
1139 /*              return NT_STATUS_PWD_TOO_SHORT; */
1140         }
1141 
1142         if (check_passwd_history(hnd,new_passwd)) {
1143                 if (samr_reject_reason) {
1144                         *samr_reject_reason = SAMR_REJECT_IN_HISTORY;
1145                 }
1146                 return NT_STATUS_PASSWORD_RESTRICTION;
1147         }
1148 
1149         pass = Get_Pwnam_alloc(talloc_tos(), username);
1150         if (!pass) {
1151                 DEBUG(1, ("change_oem_password: Username %s does not exist in system !?!\n", username));
1152                 return NT_STATUS_ACCESS_DENIED;
1153         }
1154 
1155         /* Use external script to check password complexity */
1156         if (lp_check_password_script() && *(lp_check_password_script())) {
1157                 int check_ret;
1158 
1159                 check_ret = smbrunsecret(lp_check_password_script(), new_passwd);
1160                 DEBUG(5, ("change_oem_password: check password script (%s) returned [%d]\n", lp_check_password_script(), check_ret));
1161 
1162                 if (check_ret != 0) {
1163                         DEBUG(1, ("change_oem_password: check password script said new password is not good enough!\n"));
1164                         if (samr_reject_reason) {
1165                                 *samr_reject_reason = SAMR_REJECT_COMPLEXITY;
1166                         }
1167                         TALLOC_FREE(pass);
1168                         return NT_STATUS_PASSWORD_RESTRICTION;
1169                 }
1170         }
1171 
1172         /*
1173          * If unix password sync was requested, attempt to change
1174          * the /etc/passwd database first. Return failure if this cannot
1175          * be done.
1176          *
1177          * This occurs before the oem change, because we don't want to
1178          * update it if chgpasswd failed.
1179          *
1180          * Conditional on lp_unix_password_sync() because we don't want
1181          * to touch the unix db unless we have admin permission.
1182          */
1183         
1184         if(lp_unix_password_sync() &&
1185                 !chgpasswd(username, pass, old_passwd, new_passwd, as_root)) {
1186                 TALLOC_FREE(pass);
1187                 return NT_STATUS_ACCESS_DENIED;
1188         }
1189 
1190         TALLOC_FREE(pass);
1191 
1192         if (!pdb_set_plaintext_passwd (hnd, new_passwd)) {
1193                 return NT_STATUS_ACCESS_DENIED;
1194         }
1195 
1196         /* Now write it into the file. */
1197         return pdb_update_sam_account (hnd);
1198 }

/* [<][>][^][v][top][bottom][index][help] */