diff -Nurbd samba-3.2.11.orig/source/smbd/chgpasswd.c samba-3.2.11.fix-chgpasswd/source/smbd/chgpasswd.c --- samba-3.2.11.orig/source/smbd/chgpasswd.c 2009-04-17 11:58:26.000000000 +0200 +++ samba-3.2.11.fix-chgpasswd/source/smbd/chgpasswd.c 2009-05-29 18:00:02.000000000 +0200 @@ -241,20 +241,160 @@ 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) +{ + /* we expand target in portions of chunk_size */ + size_t chunk_size = 10; + 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; + size_t target_size = target_len; + size_t target_used = 0; + char *target_start = target; + char *pattern_starter_found; + char pattern_starter = '\0'; + char empty[] = ""; + const char *chars_source; + TALLOC_CTX *ctx = talloc_tos(); + + if ((target == NULL) || (target_len == 0)) { + return; + } + 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; + } + + while (*source) { + /* 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 - chars_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; + } + } + while (chars_to_copy > target_len) { + target_used = target - target_start; + target_size += chunk_size; + target_start = talloc_realloc(ctx, target_start, char, + target_size); + if (!target_start) { + DEBUG(1,("expand_pass_pattern: talloc fail\n")); + return NULL; + } + target = target_start + target_used; + target_len = target_size - target_used; + } + strncpy(target, chars_source , chars_to_copy); + target_len -= chars_to_copy; + target += chars_to_copy; + source += skip_source_chars; + } + /* terminate target */ + if (target_len == 0) { + target_used = target - target_start; + target_size += chunk_size; + target_start = talloc_realloc(ctx, target_start, char, + target_size); + if (!target_start) { + DEBUG(1,("expand_pass_pattern: talloc fail\n")); + return NULL; + } + target = target_start + target_used; + target_len = target_size - target_used; + } + *target = '\0'; + return target_start; +} + +static int expect(int master, const char *issue, const char *expected, + const char *oldpass, const char *newpass) { char buffer[1024]; int attempts, timeout, nread; size_t len; bool match = False; + char *newissue; + size_t newissue_len; + + /* + * We know we're safe allowing "unsafe" characters here + * as we know there are no embedded control characters. + */ + newissue = talloc(talloc_tos(), char); + newissue = expand_pass_pattern(newissue, sizeof(char), issue, + "%o", oldpass, + "%n", newpass); + + if (!newissue) { + DEBUG(1,("expect: talloc fail\n")); + return false; + } + newissue_len = strlen(newissue); for (attempts = 0; attempts < 2; attempts++) { NTSTATUS status; - if (!strequal(issue, ".")) { + if (!strequal(newissue, ".")) { if (lp_passwd_chat_debug()) - DEBUG(100, ("expect: sending [%s]\n", issue)); + DEBUG(100, ("expect: sending [%s]\n", newissue)); - if ((len = sys_write(master, issue, strlen(issue))) != strlen(issue)) { + if ((len = sys_write(master, newissue, newissue_len)) != newissue_len) { DEBUG(2,("expect: (short) write returned %d\n", (int)len )); return False; @@ -310,6 +450,7 @@ return False; } } + talloc_free(newissue); DEBUG(10,("expect: returning %s\n", match ? "True" : "False" )); return match; @@ -323,7 +464,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) { TALLOC_CTX *frame = talloc_stackframe(); int count = 0; @@ -340,7 +482,7 @@ pwd_sub(expected); count++; - if (!expect(master, issue, expected)) { + if (!expect(master, issue, expected, oldpass, newpass)) { DEBUG(3, ("Response %d incorrect\n", count)); TALLOC_FREE(frame); return false; @@ -363,7 +505,7 @@ TALLOC_FREE(frame); return false; } - if (!expect(master, issue, expected)) { + if (!expect(master, issue, expected, oldpass, newpass)) { TALLOC_FREE(frame); return False; } @@ -373,7 +515,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 = NULL; int master; @@ -412,7 +555,7 @@ /* Don't need this anymore in parent. */ SAFE_FREE(slavedev); - 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 */ } @@ -607,21 +750,12 @@ if (!chatsequence) { return false; } - chatsequence = talloc_all_string_sub(ctx, - chatsequence, - "%o", - oldpass); - if (!chatsequence) { - return false; - } - chatsequence = talloc_all_string_sub(ctx, - chatsequence, - "%n", - newpass); return chat_with_program(passwordprogram, pass, chatsequence, - as_root); + as_root, + oldpass, + newpass); } #else /* ALLOW_CHANGE_PASSWORD */