diff -Nurbd samba-3.0.34.orig/source/smbd/chgpasswd.c samba-3.0.34/source/smbd/chgpasswd.c --- samba-3.0.34.orig/source/smbd/chgpasswd.c 2009-01-19 11:40:09.000000000 +0100 +++ samba-3.0.34/source/smbd/chgpasswd.c 2009-05-29 16:57:39.000000000 +0200 @@ -236,18 +236,126 @@ return (True); } -static int expect(int master, char *issue, char *expected) +static char *expand_pass_pattern(char *target, size_t target_len, + const char *source, + const char *pattern_old_pass, + const char *old_pass, + const char *pattern_new_pass, + const char *new_pass) +{ + size_t chars_to_copy = 0; + size_t skip_source_chars = 0; + size_t old_pass_len = 0; + size_t new_pass_len = 0; + size_t pattern_old_pass_len = 0; + size_t pattern_new_pass_len = 0; + char *target_start = target; + char *pattern_starter_found; + char pattern_starter = '\0'; + char empty[] = ""; + const char *chars_source; + + if ((target == NULL) || (target_len == 0)) { + return target; + } + if (source == NULL) { + source = empty; + } + if (pattern_old_pass != NULL) { + pattern_old_pass_len = strlen(pattern_old_pass); + pattern_starter = *pattern_old_pass; + } + if (pattern_new_pass != NULL) { + pattern_new_pass_len = strlen(pattern_new_pass); + pattern_starter = *pattern_new_pass; + } + if (old_pass != NULL) { + old_pass_len = strlen(old_pass); + } + else { + old_pass = empty; + } + if (new_pass != NULL) { + new_pass_len = strlen(new_pass); + } + else { + new_pass = empty; + } + + if (target_len > 0) { + /* leave room for string-terminator */ + target_len--; + } + + while ((*source) && (target_len > 0)) { + /* find possible start of pattern in source */ + pattern_starter_found = strchr(source, pattern_starter); + chars_source = source; + if (pattern_starter_found == NULL) { + /* pattern not found - copy whole source */ + chars_to_copy = strlen(source); + } + else { + chars_to_copy = pattern_starter_found - source; + } + skip_source_chars = chars_to_copy; + + if (chars_to_copy == 0) { + /* current source char may start a pattern */ + if ((pattern_old_pass) && (*pattern_old_pass) + && (strncmp(source, pattern_old_pass, + pattern_old_pass_len) == 0)) { + /* insert old password */ + chars_source = old_pass; + chars_to_copy = old_pass_len; + skip_source_chars = pattern_old_pass_len; + } + else if ((pattern_new_pass) && (*pattern_new_pass) + && (strncmp(source, pattern_new_pass, + pattern_new_pass_len) == 0)) { + /* insert new password */ + chars_source = new_pass; + chars_to_copy = new_pass_len; + skip_source_chars = pattern_new_pass_len; + } + else { + /* no pattern matched - copy char to target */ + chars_to_copy = 1; + skip_source_chars = chars_to_copy; + } + } + chars_to_copy = MIN(chars_to_copy, target_len); + strncpy(target, chars_source, chars_to_copy); + + target_len -= chars_to_copy; + target += chars_to_copy; + source += skip_source_chars; + } + /* terminate target */ + *target = '\0'; + return target_start; +} + +static int expect(int master, char *issue, char *expected, + const char *oldpass, const char *newpass) { pstring buffer; + pstring issue_password; + size_t issue_password_len; int attempts, timeout, nread, len; BOOL match = False; + /* replace placeholder by password */ + expand_pass_pattern(issue_password, sizeof(pstring), issue, + "%o", oldpass, "%n", newpass); + issue_password_len = strlen(issue_password); + for (attempts = 0; attempts < 2; attempts++) { if (!strequal(issue, ".")) { if (lp_passwd_chat_debug()) - DEBUG(100, ("expect: sending [%s]\n", issue)); + DEBUG(100, ("expect: sending [%s]\n", issue_password)); - if ((len = sys_write(master, issue, strlen(issue))) != strlen(issue)) { + if ((len = sys_write(master, issue_password, issue_password_len)) != issue_password_len) { DEBUG(2,("expect: (short) write returned %d\n", len )); return False; } @@ -305,7 +413,8 @@ all_string_sub(buf, "\\t", "\t", 0); } -static int talktochild(int master, const char *seq) +static int talktochild(int master, const char *seq, + const char *oldpass, const char *newpass) { int count = 0; fstring issue, expected; @@ -317,7 +426,7 @@ pwd_sub(expected); count++; - if (!expect(master, issue, expected)) + if (!expect(master, issue, expected, oldpass, newpass)) { DEBUG(3, ("Response %d incorrect\n", count)); return False; @@ -331,7 +440,7 @@ if (!strequal(issue, ".")) { /* we have one final issue to send */ fstrcpy(expected, "."); - if (!expect(master, issue, expected)) + if (!expect(master, issue, expected, oldpass, newpass)) return False; } @@ -339,7 +448,8 @@ } static BOOL chat_with_program(char *passwordprogram, const struct passwd *pass, - char *chatsequence, BOOL as_root) + char *chatsequence, BOOL as_root, + const char *oldpass, const char *newpass) { char *slavedev; int master; @@ -374,7 +484,7 @@ /* we now have a pty */ if (pid > 0) { /* This is the parent process */ - if ((chstat = talktochild(master, chatsequence)) == False) { + if ((chstat = talktochild(master, chatsequence, oldpass, newpass)) == False) { DEBUG(3, ("chat_with_program: Child failed to change password: %s\n", pass->pw_name)); kill(pid, SIGKILL); /* be sure to end this process */ } @@ -563,10 +673,8 @@ a new password containing shell escape characters */ pstring_sub(chatsequence, "%u", name); - all_string_sub(chatsequence, "%o", oldpass, sizeof(pstring)); - all_string_sub(chatsequence, "%n", newpass, sizeof(pstring)); return (chat_with_program - (passwordprogram, pass, chatsequence, as_root)); + (passwordprogram, pass, chatsequence, as_root, oldpass, newpass)); } #else /* ALLOW_CHANGE_PASSWORD */