smbd/chgpasswd.c

説明を見る。
00001 /* 
00002    Unix SMB/CIFS implementation.
00003    Samba utility functions
00004    Copyright (C) Andrew Tridgell 1992-1998
00005    Copyright (C) Andrew Bartlett 2001-2004
00006    
00007    This program is free software; you can redistribute it and/or modify
00008    it under the terms of the GNU General Public License as published by
00009    the Free Software Foundation; either version 2 of the License, or
00010    (at your option) any later version.
00011    
00012    This program is distributed in the hope that it will be useful,
00013    but WITHOUT ANY WARRANTY; without even the implied warranty of
00014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015    GNU General Public License for more details.
00016    
00017    You should have received a copy of the GNU General Public License
00018    along with this program; if not, write to the Free Software
00019    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00020 */
00021 
00022 /* These comments regard the code to change the user's unix password: */
00023 
00024 /* fork a child process to exec passwd and write to its
00025  * tty to change a users password. This is running as the
00026  * user who is attempting to change the password.
00027  */
00028 
00029 /* 
00030  * This code was copied/borrowed and stolen from various sources.
00031  * The primary source was the poppasswd.c from the authors of POPMail. This software
00032  * was included as a client to change passwords using the 'passwd' program
00033  * on the remote machine.
00034  *
00035  * This code has been hacked by Bob Nance (nance@niehs.nih.gov) and Evan Patterson
00036  * (patters2@niehs.nih.gov) at the National Institute of Environmental Health Sciences
00037  * and rights to modify, distribute or incorporate this change to the CAP suite or
00038  * using it for any other reason are granted, so long as this disclaimer is left intact.
00039  */
00040 
00041 /*
00042    This code was hacked considerably for inclusion in Samba, primarily
00043    by Andrew.Tridgell@anu.edu.au. The biggest change was the addition
00044    of the "password chat" option, which allows the easy runtime
00045    specification of the expected sequence of events to change a
00046    password.
00047    */
00048 
00049 #include "includes.h"
00050 
00051 extern struct passdb_ops pdb_ops;
00052 
00053 static NTSTATUS check_oem_password(const char *user,
00054                                    uchar password_encrypted_with_lm_hash[516], 
00055                                    const uchar old_lm_hash_encrypted[16],
00056                                    uchar password_encrypted_with_nt_hash[516], 
00057                                    const uchar old_nt_hash_encrypted[16],
00058                                    struct samu **hnd, char *new_passwd,
00059                                    int new_passwd_size);
00060 
00061 #if ALLOW_CHANGE_PASSWORD
00062 
00063 static int findpty(char **slave)
00064 {
00065         int master;
00066         static fstring line;
00067         SMB_STRUCT_DIR *dirp;
00068         const char *dpname;
00069 
00070 #if defined(HAVE_GRANTPT)
00071         /* Try to open /dev/ptmx. If that fails, fall through to old method. */
00072         if ((master = sys_open("/dev/ptmx", O_RDWR, 0)) >= 0)
00073         {
00074                 grantpt(master);
00075                 unlockpt(master);
00076                 *slave = (char *)ptsname(master);
00077                 if (*slave == NULL)
00078                 {
00079                         DEBUG(0,
00080                               ("findpty: Unable to create master/slave pty pair.\n"));
00081                         /* Stop fd leak on error. */
00082                         close(master);
00083                         return -1;
00084                 }
00085                 else
00086                 {
00087                         DEBUG(10,
00088                               ("findpty: Allocated slave pty %s\n", *slave));
00089                         return (master);
00090                 }
00091         }
00092 #endif /* HAVE_GRANTPT */
00093 
00094         fstrcpy(line, "/dev/ptyXX");
00095 
00096         dirp = sys_opendir("/dev");
00097         if (!dirp)
00098                 return (-1);
00099         while ((dpname = readdirname(dirp)) != NULL)
00100         {
00101                 if (strncmp(dpname, "pty", 3) == 0 && strlen(dpname) == 5)
00102                 {
00103                         DEBUG(3,
00104                               ("pty: try to open %s, line was %s\n", dpname,
00105                                line));
00106                         line[8] = dpname[3];
00107                         line[9] = dpname[4];
00108                         if ((master = sys_open(line, O_RDWR, 0)) >= 0)
00109                         {
00110                                 DEBUG(3, ("pty: opened %s\n", line));
00111                                 line[5] = 't';
00112                                 *slave = line;
00113                                 sys_closedir(dirp);
00114                                 return (master);
00115                         }
00116                 }
00117         }
00118         sys_closedir(dirp);
00119         return (-1);
00120 }
00121 
00122 static int dochild(int master, const char *slavedev, const struct passwd *pass,
00123                    const char *passwordprogram, BOOL as_root)
00124 {
00125         int slave;
00126         struct termios stermios;
00127         gid_t gid;
00128         uid_t uid;
00129         char * const eptrs[1] = { NULL };
00130 
00131         if (pass == NULL)
00132         {
00133                 DEBUG(0,
00134                       ("dochild: user doesn't exist in the UNIX password database.\n"));
00135                 return False;
00136         }
00137 
00138         gid = pass->pw_gid;
00139         uid = pass->pw_uid;
00140 
00141         gain_root_privilege();
00142 
00143         /* Start new session - gets rid of controlling terminal. */
00144         if (setsid() < 0)
00145         {
00146                 DEBUG(3,
00147                       ("Weirdness, couldn't let go of controlling terminal\n"));
00148                 return (False);
00149         }
00150 
00151         /* Open slave pty and acquire as new controlling terminal. */
00152         if ((slave = sys_open(slavedev, O_RDWR, 0)) < 0)
00153         {
00154                 DEBUG(3, ("More weirdness, could not open %s\n", slavedev));
00155                 return (False);
00156         }
00157 #if defined(TIOCSCTTY) && !defined(SUNOS5)
00158         /*
00159          * On patched Solaris 10 TIOCSCTTY is defined but seems not to work,
00160          * see the discussion under
00161          * https://bugzilla.samba.org/show_bug.cgi?id=5366.
00162          */
00163         if (ioctl(slave, TIOCSCTTY, 0) < 0)
00164         {
00165                 DEBUG(3, ("Error in ioctl call for slave pty\n"));
00166                 /* return(False); */
00167         }
00168 #elif defined(I_PUSH) && defined(I_FIND)
00169         if (ioctl(slave, I_FIND, "ptem") == 0) {
00170                 ioctl(slave, I_PUSH, "ptem");
00171         }
00172         if (ioctl(slave, I_FIND, "ldterm") == 0) {
00173                 ioctl(slave, I_PUSH, "ldterm");
00174         }
00175 #endif
00176 
00177         /* Close master. */
00178         close(master);
00179 
00180         /* Make slave stdin/out/err of child. */
00181 
00182         if (sys_dup2(slave, STDIN_FILENO) != STDIN_FILENO)
00183         {
00184                 DEBUG(3, ("Could not re-direct stdin\n"));
00185                 return (False);
00186         }
00187         if (sys_dup2(slave, STDOUT_FILENO) != STDOUT_FILENO)
00188         {
00189                 DEBUG(3, ("Could not re-direct stdout\n"));
00190                 return (False);
00191         }
00192         if (sys_dup2(slave, STDERR_FILENO) != STDERR_FILENO)
00193         {
00194                 DEBUG(3, ("Could not re-direct stderr\n"));
00195                 return (False);
00196         }
00197         if (slave > 2)
00198                 close(slave);
00199 
00200         /* Set proper terminal attributes - no echo, canonical input processing,
00201            no map NL to CR/NL on output. */
00202 
00203         if (tcgetattr(0, &stermios) < 0)
00204         {
00205                 DEBUG(3,
00206                       ("could not read default terminal attributes on pty\n"));
00207                 return (False);
00208         }
00209         stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
00210         stermios.c_lflag |= ICANON;
00211 #ifdef ONLCR
00212         stermios.c_oflag &= ~(ONLCR);
00213 #endif
00214         if (tcsetattr(0, TCSANOW, &stermios) < 0)
00215         {
00216                 DEBUG(3, ("could not set attributes of pty\n"));
00217                 return (False);
00218         }
00219 
00220         /* make us completely into the right uid */
00221         if (!as_root)
00222         {
00223                 become_user_permanently(uid, gid);
00224         }
00225 
00226         DEBUG(10,
00227               ("Invoking '%s' as password change program.\n",
00228                passwordprogram));
00229 
00230         /* execl() password-change application */
00231         if (execle("/bin/sh", "sh", "-c", passwordprogram, NULL, eptrs) < 0)
00232         {
00233                 DEBUG(3, ("Bad status returned from %s\n", passwordprogram));
00234                 return (False);
00235         }
00236         return (True);
00237 }
00238 
00239 static int expect(int master, char *issue, char *expected)
00240 {
00241         pstring buffer;
00242         int attempts, timeout, nread, len;
00243         BOOL match = False;
00244 
00245         for (attempts = 0; attempts < 2; attempts++) {
00246                 if (!strequal(issue, ".")) {
00247                         if (lp_passwd_chat_debug())
00248                                 DEBUG(100, ("expect: sending [%s]\n", issue));
00249 
00250                         if ((len = sys_write(master, issue, strlen(issue))) != strlen(issue)) {
00251                                 DEBUG(2,("expect: (short) write returned %d\n", len ));
00252                                 return False;
00253                         }
00254                 }
00255 
00256                 if (strequal(expected, "."))
00257                         return True;
00258 
00259                 /* Initial timeout. */
00260                 timeout = lp_passwd_chat_timeout() * 1000;
00261                 nread = 0;
00262                 buffer[nread] = 0;
00263 
00264                 while ((len = read_socket_with_timeout(master, buffer + nread, 1,
00265                                                        sizeof(buffer) - nread - 1,
00266                                                        timeout)) > 0) {
00267                         nread += len;
00268                         buffer[nread] = 0;
00269 
00270                         {
00271                                 /* Eat leading/trailing whitespace before match. */
00272                                 pstring str;
00273                                 pstrcpy( str, buffer);
00274                                 trim_char( str, ' ', ' ');
00275 
00276                                 if ((match = unix_wild_match(expected, str)) == True) {
00277                                         /* Now data has started to return, lower timeout. */
00278                                         timeout = lp_passwd_chat_timeout() * 100;
00279                                 }
00280                         }
00281                 }
00282 
00283                 if (lp_passwd_chat_debug())
00284                         DEBUG(100, ("expect: expected [%s] received [%s] match %s\n",
00285                                     expected, buffer, match ? "yes" : "no" ));
00286 
00287                 if (match)
00288                         break;
00289 
00290                 if (len < 0) {
00291                         DEBUG(2, ("expect: %s\n", strerror(errno)));
00292                         return False;
00293                 }
00294         }
00295 
00296         DEBUG(10,("expect: returning %s\n", match ? "True" : "False" ));
00297         return match;
00298 }
00299 
00300 static void pwd_sub(char *buf)
00301 {
00302         all_string_sub(buf, "\\n", "\n", 0);
00303         all_string_sub(buf, "\\r", "\r", 0);
00304         all_string_sub(buf, "\\s", " ", 0);
00305         all_string_sub(buf, "\\t", "\t", 0);
00306 }
00307 
00308 static int talktochild(int master, const char *seq)
00309 {
00310         int count = 0;
00311         fstring issue, expected;
00312 
00313         fstrcpy(issue, ".");
00314 
00315         while (next_token(&seq, expected, NULL, sizeof(expected)))
00316         {
00317                 pwd_sub(expected);
00318                 count++;
00319 
00320                 if (!expect(master, issue, expected))
00321                 {
00322                         DEBUG(3, ("Response %d incorrect\n", count));
00323                         return False;
00324                 }
00325 
00326                 if (!next_token(&seq, issue, NULL, sizeof(issue)))
00327                         fstrcpy(issue, ".");
00328 
00329                 pwd_sub(issue);
00330         }
00331         if (!strequal(issue, ".")) {
00332                 /* we have one final issue to send */
00333                 fstrcpy(expected, ".");
00334                 if (!expect(master, issue, expected))
00335                         return False;
00336         }
00337 
00338         return (count > 0);
00339 }
00340 
00341 static BOOL chat_with_program(char *passwordprogram, const struct passwd *pass,
00342                               char *chatsequence, BOOL as_root)
00343 {
00344         char *slavedev;
00345         int master;
00346         pid_t pid, wpid;
00347         int wstat;
00348         BOOL chstat = False;
00349 
00350         if (pass == NULL) {
00351                 DEBUG(0, ("chat_with_program: user doesn't exist in the UNIX password database.\n"));
00352                 return False;
00353         }
00354 
00355         /* allocate a pseudo-terminal device */
00356         if ((master = findpty(&slavedev)) < 0) {
00357                 DEBUG(3, ("chat_with_program: Cannot Allocate pty for password change: %s\n", pass->pw_name));
00358                 return (False);
00359         }
00360 
00361         /*
00362          * We need to temporarily stop CatchChild from eating
00363          * SIGCLD signals as it also eats the exit status code. JRA.
00364          */
00365 
00366         CatchChildLeaveStatus();
00367 
00368         if ((pid = sys_fork()) < 0) {
00369                 DEBUG(3, ("chat_with_program: Cannot fork() child for password change: %s\n", pass->pw_name));
00370                 close(master);
00371                 CatchChild();
00372                 return (False);
00373         }
00374 
00375         /* we now have a pty */
00376         if (pid > 0) {                  /* This is the parent process */
00377                 if ((chstat = talktochild(master, chatsequence)) == False) {
00378                         DEBUG(3, ("chat_with_program: Child failed to change password: %s\n", pass->pw_name));
00379                         kill(pid, SIGKILL);     /* be sure to end this process */
00380                 }
00381 
00382                 while ((wpid = sys_waitpid(pid, &wstat, 0)) < 0) {
00383                         if (errno == EINTR) {
00384                                 errno = 0;
00385                                 continue;
00386                         }
00387                         break;
00388                 }
00389 
00390                 if (wpid < 0) {
00391                         DEBUG(3, ("chat_with_program: The process is no longer waiting!\n\n"));
00392                         close(master);
00393                         CatchChild();
00394                         return (False);
00395                 }
00396 
00397                 /*
00398                  * Go back to ignoring children.
00399                  */
00400                 CatchChild();
00401 
00402                 close(master);
00403 
00404                 if (pid != wpid) {
00405                         DEBUG(3, ("chat_with_program: We were waiting for the wrong process ID\n"));
00406                         return (False);
00407                 }
00408                 if (WIFEXITED(wstat) && (WEXITSTATUS(wstat) != 0)) {
00409                         DEBUG(3, ("chat_with_program: The process exited with status %d \
00410 while we were waiting\n", WEXITSTATUS(wstat)));
00411                         return (False);
00412                 }
00413 #if defined(WIFSIGNALLED) && defined(WTERMSIG)
00414                 else if (WIFSIGNALLED(wstat)) {
00415                         DEBUG(3, ("chat_with_program: The process was killed by signal %d \
00416 while we were waiting\n", WTERMSIG(wstat)));
00417                         return (False);
00418                 }
00419 #endif
00420         } else {
00421                 /* CHILD */
00422 
00423                 /*
00424                  * Lose any elevated privileges.
00425                  */
00426                 drop_effective_capability(KERNEL_OPLOCK_CAPABILITY);
00427                 drop_effective_capability(DMAPI_ACCESS_CAPABILITY);
00428 
00429                 /* make sure it doesn't freeze */
00430                 alarm(20);
00431 
00432                 if (as_root)
00433                         become_root();
00434 
00435                 DEBUG(3, ("chat_with_program: Dochild for user %s (uid=%d,gid=%d) (as_root = %s)\n", pass->pw_name,
00436                        (int)getuid(), (int)getgid(), BOOLSTR(as_root) ));
00437                 chstat = dochild(master, slavedev, pass, passwordprogram, as_root);
00438 
00439                 if (as_root)
00440                         unbecome_root();
00441 
00442                 /*
00443                  * The child should never return from dochild() ....
00444                  */
00445 
00446                 DEBUG(0, ("chat_with_program: Error: dochild() returned %d\n", chstat));
00447                 exit(1);
00448         }
00449 
00450         if (chstat)
00451                 DEBUG(3, ("chat_with_program: Password change %ssuccessful for user %s\n",
00452                        (chstat ? "" : "un"), pass->pw_name));
00453         return (chstat);
00454 }
00455 
00456 BOOL chgpasswd(const char *name, const struct passwd *pass, 
00457                const char *oldpass, const char *newpass, BOOL as_root)
00458 {
00459         pstring passwordprogram;
00460         pstring chatsequence;
00461         size_t i;
00462         size_t len;
00463 
00464         if (!oldpass) {
00465                 oldpass = "";
00466         }
00467 
00468         DEBUG(3, ("chgpasswd: Password change (as_root=%s) for user: %s\n", BOOLSTR(as_root), name));
00469 
00470 #ifdef DEBUG_PASSWORD
00471         DEBUG(100, ("chgpasswd: Passwords: old=%s new=%s\n", oldpass, newpass));
00472 #endif
00473 
00474         /* Take the passed information and test it for minimum criteria */
00475 
00476         /* Password is same as old password */
00477         if (strcmp(oldpass, newpass) == 0) {
00478                 /* don't allow same password */
00479                 DEBUG(2, ("chgpasswd: Password Change: %s, New password is same as old\n", name));      /* log the attempt */
00480                 return (False); /* inform the user */
00481         }
00482 
00483         /* 
00484          * Check the old and new passwords don't contain any control
00485          * characters.
00486          */
00487 
00488         len = strlen(oldpass);
00489         for (i = 0; i < len; i++) {
00490                 if (iscntrl((int)oldpass[i])) {
00491                         DEBUG(0, ("chgpasswd: oldpass contains control characters (disallowed).\n"));
00492                         return False;
00493                 }
00494         }
00495 
00496         len = strlen(newpass);
00497         for (i = 0; i < len; i++) {
00498                 if (iscntrl((int)newpass[i])) {
00499                         DEBUG(0, ("chgpasswd: newpass contains control characters (disallowed).\n"));
00500                         return False;
00501                 }
00502         }
00503         
00504 #ifdef WITH_PAM
00505         if (lp_pam_password_change()) {
00506                 BOOL ret;
00507 #ifdef HAVE_SETLOCALE
00508                 char *prevlocale = setlocale(LC_ALL, "C");
00509 #endif
00510 
00511                 if (as_root)
00512                         become_root();
00513 
00514                 if (pass) {
00515                         ret = smb_pam_passchange(pass->pw_name, oldpass, newpass);
00516                 } else {
00517                         ret = smb_pam_passchange(name, oldpass, newpass);
00518                 }
00519                         
00520                 if (as_root)
00521                         unbecome_root();
00522 
00523 #ifdef HAVE_SETLOCALE
00524                 setlocale(LC_ALL, prevlocale);
00525 #endif
00526 
00527                 return ret;
00528         }
00529 #endif
00530 
00531         /* A non-PAM password change just doen't make sense without a valid local user */
00532 
00533         if (pass == NULL) {
00534                 DEBUG(0, ("chgpasswd: user %s doesn't exist in the UNIX password database.\n", name));
00535                 return False;
00536         }
00537 
00538         pstrcpy(passwordprogram, lp_passwd_program());
00539         pstrcpy(chatsequence, lp_passwd_chat());
00540 
00541         if (!*chatsequence) {
00542                 DEBUG(2, ("chgpasswd: Null chat sequence - no password changing\n"));
00543                 return (False);
00544         }
00545 
00546         if (!*passwordprogram) {
00547                 DEBUG(2, ("chgpasswd: Null password program - no password changing\n"));
00548                 return (False);
00549         }
00550 
00551         if (as_root) {
00552                 /* The password program *must* contain the user name to work. Fail if not. */
00553                 if (strstr_m(passwordprogram, "%u") == NULL) {
00554                         DEBUG(0,("chgpasswd: Running as root the 'passwd program' parameter *MUST* contain \
00555 the string %%u, and the given string %s does not.\n", passwordprogram ));
00556                         return False;
00557                 }
00558         }
00559 
00560         pstring_sub(passwordprogram, "%u", name);
00561         /* note that we do NOT substitute the %o and %n in the password program
00562            as this would open up a security hole where the user could use
00563            a new password containing shell escape characters */
00564 
00565         pstring_sub(chatsequence, "%u", name);
00566         all_string_sub(chatsequence, "%o", oldpass, sizeof(pstring));
00567         all_string_sub(chatsequence, "%n", newpass, sizeof(pstring));
00568         return (chat_with_program
00569                 (passwordprogram, pass, chatsequence, as_root));
00570 }
00571 
00572 #else /* ALLOW_CHANGE_PASSWORD */
00573 
00574 BOOL chgpasswd(const char *name, const struct passwd *pass, 
00575                const char *oldpass, const char *newpass, BOOL as_root)
00576 {
00577         DEBUG(0, ("chgpasswd: Unix Password changing not compiled in (user=%s)\n", name));
00578         return (False);
00579 }
00580 #endif /* ALLOW_CHANGE_PASSWORD */
00581 
00582 /***********************************************************
00583  Code to check the lanman hashed password.
00584 ************************************************************/
00585 
00586 BOOL check_lanman_password(char *user, uchar * pass1,
00587                            uchar * pass2, struct samu **hnd)
00588 {
00589         uchar unenc_new_pw[16];
00590         uchar unenc_old_pw[16];
00591         struct samu *sampass = NULL;
00592         uint32 acct_ctrl;
00593         const uint8 *lanman_pw;
00594         BOOL ret;
00595 
00596         if ( !(sampass = samu_new(NULL)) ) {
00597                 DEBUG(0, ("samu_new() failed!\n"));
00598                 return False;
00599         }
00600         
00601         become_root();
00602         ret = pdb_getsampwnam(sampass, user);
00603         unbecome_root();
00604 
00605         if (ret == False) {
00606                 DEBUG(0,("check_lanman_password: getsampwnam returned NULL\n"));
00607                 TALLOC_FREE(sampass);
00608                 return False;
00609         }
00610         
00611         acct_ctrl = pdb_get_acct_ctrl     (sampass);
00612         lanman_pw = pdb_get_lanman_passwd (sampass);
00613 
00614         if (acct_ctrl & ACB_DISABLED) {
00615                 DEBUG(0,("check_lanman_password: account %s disabled.\n", user));
00616                 TALLOC_FREE(sampass);
00617                 return False;
00618         }
00619 
00620         if (lanman_pw == NULL) {
00621                 if (acct_ctrl & ACB_PWNOTREQ) {
00622                         /* this saves the pointer for the caller */
00623                         *hnd = sampass;
00624                         return True;
00625                 } else {
00626                         DEBUG(0, ("check_lanman_password: no lanman password !\n"));
00627                         TALLOC_FREE(sampass);
00628                         return False;
00629                 }
00630         }
00631 
00632         /* Get the new lanman hash. */
00633         D_P16(lanman_pw, pass2, unenc_new_pw);
00634 
00635         /* Use this to get the old lanman hash. */
00636         D_P16(unenc_new_pw, pass1, unenc_old_pw);
00637 
00638         /* Check that the two old passwords match. */
00639         if (memcmp(lanman_pw, unenc_old_pw, 16)) {
00640                 DEBUG(0,("check_lanman_password: old password doesn't match.\n"));
00641                 TALLOC_FREE(sampass);
00642                 return False;
00643         }
00644 
00645         /* this saves the pointer for the caller */
00646         *hnd = sampass;
00647         return True;
00648 }
00649 
00650 /***********************************************************
00651  Code to change the lanman hashed password.
00652  It nulls out the NT hashed password as it will
00653  no longer be valid.
00654  NOTE this function is designed to be called as root. Check the old password
00655  is correct before calling. JRA.
00656 ************************************************************/
00657 
00658 BOOL change_lanman_password(struct samu *sampass, uchar *pass2)
00659 {
00660         static uchar null_pw[16];
00661         uchar unenc_new_pw[16];
00662         BOOL ret;
00663         uint32 acct_ctrl;
00664         const uint8 *pwd;
00665 
00666         if (sampass == NULL) {
00667                 DEBUG(0,("change_lanman_password: no smb password entry.\n"));
00668                 return False;
00669         }
00670         
00671         acct_ctrl = pdb_get_acct_ctrl(sampass);
00672         pwd = pdb_get_lanman_passwd(sampass);
00673 
00674         if (acct_ctrl & ACB_DISABLED) {
00675                 DEBUG(0,("change_lanman_password: account %s disabled.\n",
00676                        pdb_get_username(sampass)));
00677                 return False;
00678         }
00679 
00680         if (pwd == NULL) { 
00681                 if (acct_ctrl & ACB_PWNOTREQ) {
00682                         uchar no_pw[14];
00683                         memset(no_pw, '\0', 14);
00684                         E_P16(no_pw, null_pw);
00685 
00686                         /* Get the new lanman hash. */
00687                         D_P16(null_pw, pass2, unenc_new_pw);
00688                 } else {
00689                         DEBUG(0,("change_lanman_password: no lanman password !\n"));
00690                         return False;
00691                 }
00692         } else {
00693                 /* Get the new lanman hash. */
00694                 D_P16(pwd, pass2, unenc_new_pw);
00695         }
00696 
00697         if (!pdb_set_lanman_passwd(sampass, unenc_new_pw, PDB_CHANGED)) {
00698                 return False;
00699         }
00700 
00701         if (!pdb_set_nt_passwd    (sampass, NULL, PDB_CHANGED)) {
00702                 return False;   /* We lose the NT hash. Sorry. */
00703         }
00704 
00705         if (!pdb_set_pass_last_set_time  (sampass, time(NULL), PDB_CHANGED)) {
00706                 TALLOC_FREE(sampass);
00707                 /* Not quite sure what this one qualifies as, but this will do */
00708                 return False; 
00709         }
00710  
00711         /* Now flush the sam_passwd struct to persistent storage */
00712         ret = NT_STATUS_IS_OK(pdb_update_sam_account (sampass));
00713 
00714         return ret;
00715 }
00716 
00717 /***********************************************************
00718  Code to check and change the OEM hashed password.
00719 ************************************************************/
00720 
00721 NTSTATUS pass_oem_change(char *user,
00722                          uchar password_encrypted_with_lm_hash[516], 
00723                          const uchar old_lm_hash_encrypted[16],
00724                          uchar password_encrypted_with_nt_hash[516], 
00725                          const uchar old_nt_hash_encrypted[16],
00726                          uint32 *reject_reason)
00727 {
00728         pstring new_passwd;
00729         struct samu *sampass = NULL;
00730         NTSTATUS nt_status = check_oem_password(user, password_encrypted_with_lm_hash, 
00731                                                 old_lm_hash_encrypted, 
00732                                                 password_encrypted_with_nt_hash, 
00733                                                 old_nt_hash_encrypted,
00734                                                 &sampass, new_passwd, sizeof(new_passwd));
00735         
00736         if (!NT_STATUS_IS_OK(nt_status))
00737                 return nt_status;
00738 
00739         /* We've already checked the old password here.... */
00740         become_root();
00741         nt_status = change_oem_password(sampass, NULL, new_passwd, True, reject_reason);
00742         unbecome_root();
00743 
00744         memset(new_passwd, 0, sizeof(new_passwd));
00745 
00746         TALLOC_FREE(sampass);
00747 
00748         return nt_status;
00749 }
00750 
00751 /***********************************************************
00752  Decrypt and verify a user password change.  
00753 
00754  The 516 byte long buffers are encrypted with the old NT and 
00755  old LM passwords, and if the NT passwords are present, both 
00756  buffers contain a unicode string.
00757 
00758  After decrypting the buffers, check the password is correct by
00759  matching the old hashed passwords with the passwords in the passdb.
00760  
00761 ************************************************************/
00762 
00763 static NTSTATUS check_oem_password(const char *user,
00764                                    uchar password_encrypted_with_lm_hash[516], 
00765                                    const uchar old_lm_hash_encrypted[16],
00766                                    uchar password_encrypted_with_nt_hash[516], 
00767                                    const uchar old_nt_hash_encrypted[16],
00768                                    struct samu **hnd, char *new_passwd,
00769                                    int new_passwd_size)
00770 {
00771         static uchar null_pw[16];
00772         static uchar null_ntpw[16];
00773         struct samu *sampass = NULL;
00774         uint8 *password_encrypted;
00775         const uint8 *encryption_key;
00776         const uint8 *lanman_pw, *nt_pw;
00777         uint32 acct_ctrl;
00778         uint32 new_pw_len;
00779         uchar new_nt_hash[16];
00780         uchar new_lm_hash[16];
00781         uchar verifier[16];
00782         char no_pw[2];
00783         BOOL ret;
00784 
00785         BOOL nt_pass_set = (password_encrypted_with_nt_hash && old_nt_hash_encrypted);
00786         BOOL lm_pass_set = (password_encrypted_with_lm_hash && old_lm_hash_encrypted);
00787 
00788         *hnd = NULL;
00789 
00790         if ( !(sampass = samu_new( NULL )) ) {
00791                 return NT_STATUS_NO_MEMORY;
00792         }
00793 
00794         become_root();
00795         ret = pdb_getsampwnam(sampass, user);
00796         unbecome_root();
00797 
00798         if (ret == False) {
00799                 DEBUG(0, ("check_oem_password: getsmbpwnam returned NULL\n"));
00800                 TALLOC_FREE(sampass);
00801                 return NT_STATUS_NO_SUCH_USER; 
00802         }
00803 
00804         acct_ctrl = pdb_get_acct_ctrl(sampass);
00805         
00806         if (acct_ctrl & ACB_DISABLED) {
00807                 DEBUG(2,("check_lanman_password: account %s disabled.\n", user));
00808                 TALLOC_FREE(sampass);
00809                 return NT_STATUS_ACCOUNT_DISABLED;
00810         }
00811 
00812         if ((acct_ctrl & ACB_PWNOTREQ) && lp_null_passwords()) {
00813                 /* construct a null password (in case one is needed */
00814                 no_pw[0] = 0;
00815                 no_pw[1] = 0;
00816                 nt_lm_owf_gen(no_pw, null_ntpw, null_pw);
00817                 lanman_pw = null_pw;
00818                 nt_pw = null_pw;
00819 
00820         } else {
00821                 /* save pointers to passwords so we don't have to keep looking them up */
00822                 if (lp_lanman_auth()) {
00823                         lanman_pw = pdb_get_lanman_passwd(sampass);
00824                 } else {
00825                         lanman_pw = NULL;
00826                 }
00827                 nt_pw = pdb_get_nt_passwd(sampass);
00828         }
00829 
00830         if (nt_pw && nt_pass_set) {
00831                 /* IDEAL Case: passwords are in unicode, and we can
00832                  * read use the password encrypted with the NT hash 
00833                  */
00834                 password_encrypted = password_encrypted_with_nt_hash;
00835                 encryption_key = nt_pw;
00836         } else if (lanman_pw && lm_pass_set) {
00837                 /* password may still be in unicode, but use LM hash version */
00838                 password_encrypted = password_encrypted_with_lm_hash;
00839                 encryption_key = lanman_pw;
00840         } else if (nt_pass_set) {
00841                 DEBUG(1, ("NT password change supplied for user %s, but we have no NT password to check it with\n", 
00842                           user));
00843                 TALLOC_FREE(sampass);
00844                 return NT_STATUS_WRONG_PASSWORD;        
00845         } else if (lm_pass_set) {
00846                 if (lp_lanman_auth()) {
00847                         DEBUG(1, ("LM password change supplied for user %s, but we have no LanMan password to check it with\n", 
00848                                   user));
00849                 } else {
00850                         DEBUG(1, ("LM password change supplied for user %s, but we have disabled LanMan authentication\n", 
00851                                   user));
00852                 }
00853                 TALLOC_FREE(sampass);
00854                 return NT_STATUS_WRONG_PASSWORD;
00855         } else {
00856                 DEBUG(1, ("password change requested for user %s, but no password supplied!\n", 
00857                           user));
00858                 TALLOC_FREE(sampass);
00859                 return NT_STATUS_WRONG_PASSWORD;
00860         }
00861 
00862         /* 
00863          * Decrypt the password with the key 
00864          */
00865         SamOEMhash( password_encrypted, encryption_key, 516);
00866 
00867         if ( !decode_pw_buffer(password_encrypted, new_passwd, new_passwd_size, &new_pw_len, 
00868                                nt_pass_set ? STR_UNICODE : STR_ASCII)) {
00869                 TALLOC_FREE(sampass);
00870                 return NT_STATUS_WRONG_PASSWORD;
00871         }
00872 
00873         /*
00874          * To ensure we got the correct new password, hash it and
00875          * use it as a key to test the passed old password.
00876          */
00877 
00878         if (nt_pass_set) {
00879                 /* NT passwords, verify the NT hash. */
00880                 
00881                 /* Calculate the MD4 hash (NT compatible) of the password */
00882                 memset(new_nt_hash, '\0', 16);
00883                 E_md4hash(new_passwd, new_nt_hash);
00884 
00885                 if (nt_pw) {
00886                         /*
00887                          * check the NT verifier
00888                          */
00889                         E_old_pw_hash(new_nt_hash, nt_pw, verifier);
00890                         if (memcmp(verifier, old_nt_hash_encrypted, 16)) {
00891                                 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
00892                                 TALLOC_FREE(sampass);
00893                                 return NT_STATUS_WRONG_PASSWORD;
00894                         }
00895                         
00896                         /* We could check the LM password here, but there is
00897                          * little point, we already know the password is
00898                          * correct, and the LM password might not even be
00899                          * present. */
00900 
00901                         /* Further, LM hash generation algorithms
00902                          * differ with charset, so we could
00903                          * incorrectly fail a perfectly valid password
00904                          * change */
00905 #ifdef DEBUG_PASSWORD
00906                         DEBUG(100,
00907                               ("check_oem_password: password %s ok\n", new_passwd));
00908 #endif
00909                         *hnd = sampass;
00910                         return NT_STATUS_OK;
00911                 }
00912                 
00913                 if (lanman_pw) {
00914                         /*
00915                          * check the lm verifier
00916                          */
00917                         E_old_pw_hash(new_nt_hash, lanman_pw, verifier);
00918                         if (memcmp(verifier, old_lm_hash_encrypted, 16)) {
00919                                 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
00920                                 TALLOC_FREE(sampass);
00921                                 return NT_STATUS_WRONG_PASSWORD;
00922                         }
00923 #ifdef DEBUG_PASSWORD
00924                         DEBUG(100,
00925                               ("check_oem_password: password %s ok\n", new_passwd));
00926 #endif
00927                         *hnd = sampass;
00928                         return NT_STATUS_OK;
00929                 }
00930         }
00931 
00932         if (lanman_pw && lm_pass_set) {
00933 
00934                 E_deshash(new_passwd, new_lm_hash);
00935 
00936                 /*
00937                  * check the lm verifier
00938                  */
00939                 E_old_pw_hash(new_lm_hash, lanman_pw, verifier);
00940                 if (memcmp(verifier, old_lm_hash_encrypted, 16)) {
00941                         DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
00942                         TALLOC_FREE(sampass);
00943                         return NT_STATUS_WRONG_PASSWORD;
00944                 }
00945                 
00946 #ifdef DEBUG_PASSWORD
00947                 DEBUG(100,
00948                       ("check_oem_password: password %s ok\n", new_passwd));
00949 #endif
00950                 *hnd = sampass;
00951                 return NT_STATUS_OK;
00952         }
00953 
00954         /* should not be reached */
00955         TALLOC_FREE(sampass);
00956         return NT_STATUS_WRONG_PASSWORD;
00957 }
00958 
00959 /***********************************************************
00960  This routine takes the given password and checks it against
00961  the password history. Returns True if this password has been
00962  found in the history list.
00963 ************************************************************/
00964 
00965 static BOOL check_passwd_history(struct samu *sampass, const char *plaintext)
00966 {
00967         uchar new_nt_p16[NT_HASH_LEN];
00968         uchar zero_md5_nt_pw[SALTED_MD5_HASH_LEN];
00969         const uint8 *nt_pw;
00970         const uint8 *pwhistory;
00971         BOOL found = False;
00972         int i;
00973         uint32 pwHisLen, curr_pwHisLen;
00974 
00975         pdb_get_account_policy(AP_PASSWORD_HISTORY, &pwHisLen);
00976         if (pwHisLen == 0) {
00977                 return False;
00978         }
00979 
00980         pwhistory = pdb_get_pw_history(sampass, &curr_pwHisLen);
00981         if (!pwhistory || curr_pwHisLen == 0) {
00982                 return False;
00983         }
00984 
00985         /* Only examine the minimum of the current history len and
00986            the stored history len. Avoids race conditions. */
00987         pwHisLen = MIN(pwHisLen,curr_pwHisLen);
00988 
00989         nt_pw = pdb_get_nt_passwd(sampass);
00990 
00991         E_md4hash(plaintext, new_nt_p16);
00992 
00993         if (!memcmp(nt_pw, new_nt_p16, NT_HASH_LEN)) {
00994                 DEBUG(10,("check_passwd_history: proposed new password for user %s is the same as the current password !\n",
00995                         pdb_get_username(sampass) ));
00996                 return True;
00997         }
00998 
00999         dump_data(100, (const char *)new_nt_p16, NT_HASH_LEN);
01000         dump_data(100, (const char *)pwhistory, PW_HISTORY_ENTRY_LEN*pwHisLen);
01001 
01002         memset(zero_md5_nt_pw, '\0', SALTED_MD5_HASH_LEN);
01003         for (i=0; i<pwHisLen; i++) {
01004                 uchar new_nt_pw_salted_md5_hash[SALTED_MD5_HASH_LEN];
01005                 const uchar *current_salt = &pwhistory[i*PW_HISTORY_ENTRY_LEN];
01006                 const uchar *old_nt_pw_salted_md5_hash = &pwhistory[(i*PW_HISTORY_ENTRY_LEN)+
01007                                                         PW_HISTORY_SALT_LEN];
01008                 if (!memcmp(zero_md5_nt_pw, old_nt_pw_salted_md5_hash, SALTED_MD5_HASH_LEN)) {
01009                         /* Ignore zero valued entries. */
01010                         continue;
01011                 }
01012                 /* Create salted versions of new to compare. */
01013                 E_md5hash(current_salt, new_nt_p16, new_nt_pw_salted_md5_hash);
01014 
01015                 if (!memcmp(new_nt_pw_salted_md5_hash, old_nt_pw_salted_md5_hash, SALTED_MD5_HASH_LEN)) {
01016                         DEBUG(1,("check_passwd_history: proposed new password for user %s found in history list !\n",
01017                                 pdb_get_username(sampass) ));
01018                         found = True;
01019                         break;
01020                 }
01021         }
01022         return found;
01023 }
01024 
01025 /***********************************************************
01026  Code to change the oem password. Changes both the lanman
01027  and NT hashes.  Old_passwd is almost always NULL.
01028  NOTE this function is designed to be called as root. Check the old password
01029  is correct before calling. JRA.
01030 ************************************************************/
01031 
01032 NTSTATUS change_oem_password(struct samu *hnd, char *old_passwd, char *new_passwd, BOOL as_root, uint32 *samr_reject_reason)
01033 {
01034         uint32 min_len;
01035         struct passwd *pass = NULL;
01036         const char *username = pdb_get_username(hnd);
01037         time_t can_change_time = pdb_get_pass_can_change_time(hnd);
01038 
01039         if (samr_reject_reason) {
01040                 *samr_reject_reason = Undefined;
01041         }
01042 
01043         /* check to see if the secdesc has previously been set to disallow */
01044         if (!pdb_get_pass_can_change(hnd)) {
01045                 DEBUG(1, ("user %s does not have permissions to change password\n", username));
01046                 if (samr_reject_reason) {
01047                         *samr_reject_reason = REJECT_REASON_OTHER;
01048                 }
01049                 return NT_STATUS_ACCOUNT_RESTRICTION;
01050         }
01051 
01052         /* removed calculation here, becuase passdb now calculates
01053            based on policy.  jmcd */
01054         if ((can_change_time != 0) && (time(NULL) < can_change_time)) {
01055                 DEBUG(1, ("user %s cannot change password now, must "
01056                           "wait until %s\n", username,
01057                           http_timestring(can_change_time)));
01058                 if (samr_reject_reason) {
01059                         *samr_reject_reason = REJECT_REASON_OTHER;
01060                 }
01061                 return NT_STATUS_ACCOUNT_RESTRICTION;
01062         }
01063 
01064         if (pdb_get_account_policy(AP_MIN_PASSWORD_LEN, &min_len) && (str_charnum(new_passwd) < min_len)) {
01065                 DEBUG(1, ("user %s cannot change password - password too short\n", 
01066                           username));
01067                 DEBUGADD(1, (" account policy min password len = %d\n", min_len));
01068                 if (samr_reject_reason) {
01069                         *samr_reject_reason = REJECT_REASON_TOO_SHORT;
01070                 }
01071                 return NT_STATUS_PASSWORD_RESTRICTION;
01072 /*              return NT_STATUS_PWD_TOO_SHORT; */
01073         }
01074 
01075         if (check_passwd_history(hnd,new_passwd)) {
01076                 if (samr_reject_reason) {
01077                         *samr_reject_reason = REJECT_REASON_IN_HISTORY;
01078                 }
01079                 return NT_STATUS_PASSWORD_RESTRICTION;
01080         }
01081 
01082         pass = Get_Pwnam(username);
01083         if (!pass) {
01084                 DEBUG(1, ("change_oem_password: Username %s does not exist in system !?!\n", username));
01085                 return NT_STATUS_ACCESS_DENIED;
01086         }
01087 
01088         /* Use external script to check password complexity */
01089         if (lp_check_password_script() && *(lp_check_password_script())) {
01090                 int check_ret;
01091 
01092                 check_ret = smbrunsecret(lp_check_password_script(), new_passwd);
01093                 DEBUG(5, ("change_oem_password: check password script (%s) returned [%d]\n", lp_check_password_script(), check_ret));
01094 
01095                 if (check_ret != 0) {
01096                         DEBUG(1, ("change_oem_password: check password script said new password is not good enough!\n"));
01097                         if (samr_reject_reason) {
01098                                 *samr_reject_reason = REJECT_REASON_NOT_COMPLEX;
01099                         }
01100                         return NT_STATUS_PASSWORD_RESTRICTION;
01101                 }
01102         }
01103 
01104         /*
01105          * If unix password sync was requested, attempt to change
01106          * the /etc/passwd database first. Return failure if this cannot
01107          * be done.
01108          *
01109          * This occurs before the oem change, because we don't want to
01110          * update it if chgpasswd failed.
01111          *
01112          * Conditional on lp_unix_password_sync() because we don't want
01113          * to touch the unix db unless we have admin permission.
01114          */
01115         
01116         if(lp_unix_password_sync() &&
01117                 !chgpasswd(username, pass, old_passwd, new_passwd, as_root)) {
01118                 return NT_STATUS_ACCESS_DENIED;
01119         }
01120 
01121         if (!pdb_set_plaintext_passwd (hnd, new_passwd)) {
01122                 return NT_STATUS_ACCESS_DENIED;
01123         }
01124 
01125         /* Now write it into the file. */
01126         return pdb_update_sam_account (hnd);
01127 }

Sambaに対してSat Aug 29 21:23:23 2009に生成されました。  doxygen 1.4.7