mirror of
https://github.com/RsyncProject/rsync.git
synced 2026-10-28 16:48:17 -04:00
auth: read only the first password-file line (#1137)
* auth: read only the first password-file line strtok() skips leading delimiters, so an empty first line followed by a password was interpreted as that password. Stop at the first CR/LF instead, keeping an empty first line empty and making file and stdin behavior consistent. Add daemon-auth regression coverage for LF, CRLF, empty input, an empty first line, and a final line without a newline for both input sources. Fixes #1134. Signed-off-by: Eduard Zateev <hackerowskiy@gmail.com> * tests: reject password-file fallback for empty first lines Supply the valid daemon password through RSYNC_PASSWORD in negative cases, then require exit status 1 and the password read diagnostic. Signed-off-by: Eduard Zateev <hackerowskiy@gmail.com> --------- Signed-off-by: Eduard Zateev <hackerowskiy@gmail.com>
This commit is contained in:
1 parent
92a2a09e3f
commit
be124601c4
2 files changed
+74
-2
No files matched your search
+4
-2
@@ -271,8 +271,10 @@ static const char *getpassf(const char *filename)
|
||||
|
||||
if (n > 0) {
|
||||
buffer[n] = '\0';
|
||||
if ((p = strtok(buffer, "\n\r")) != NULL)
|
||||
return strdup(p);
|
||||
p = buffer + strcspn(buffer, "\n\r");
|
||||
*p = '\0';
|
||||
if (buffer[0] != '\0')
|
||||
return strdup(buffer);
|
||||
}
|
||||
|
||||
rprintf(FERROR, "ERROR: failed to read a password from %s\n", filename);
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Only the first line of --password-file is used, for files and stdin."""
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
from rsyncfns import (
|
||||
SCRATCHDIR, makepath, rmtree, rsync_argv, start_test_daemon,
|
||||
test_fail, write_daemon_conf,
|
||||
)
|
||||
|
||||
DAEMON_PORT = 12904
|
||||
USER = 'tuser'
|
||||
PASSWORD = b'secretpass'
|
||||
|
||||
base = SCRATCHDIR / 'password-file-first-line'
|
||||
rmtree(base)
|
||||
src = base / 'src'
|
||||
dest = base / 'dest'
|
||||
makepath(src)
|
||||
(src / 'payload').write_text('regression test\n')
|
||||
secrets = base / 'secrets'
|
||||
secrets.write_bytes(USER.encode() + b':' + PASSWORD + b'\n')
|
||||
secrets.chmod(0o600)
|
||||
conf = write_daemon_conf([
|
||||
('auth', {'path': dest, 'read only': 'no',
|
||||
'auth users': USER, 'secrets file': secrets}),
|
||||
])
|
||||
url = start_test_daemon(conf, DAEMON_PORT)
|
||||
userurl = url.replace('rsync://', f'rsync://{USER}@', 1)
|
||||
|
||||
cases = (
|
||||
('LF', PASSWORD + b'\nignored\n', True),
|
||||
('CRLF', PASSWORD + b'\r\nignored\r\n', True),
|
||||
('empty-file', b'', False),
|
||||
('empty-first-line', b'\n' + PASSWORD + b'\n', False),
|
||||
('no-final-newline', PASSWORD, True),
|
||||
)
|
||||
|
||||
for source in ('file', 'stdin'):
|
||||
for name, contents, succeeds in cases:
|
||||
rmtree(dest)
|
||||
makepath(dest)
|
||||
args = ['-a', f'{src}/', f'{userurl}auth/']
|
||||
if source == 'file':
|
||||
pwfile = base / f'{name}.password'
|
||||
pwfile.write_bytes(contents)
|
||||
pwfile.chmod(0o600)
|
||||
args.insert(1, f'--password-file={pwfile}')
|
||||
stdin = subprocess.DEVNULL
|
||||
else:
|
||||
args.insert(1, '--password-file=-')
|
||||
stdin = subprocess.PIPE
|
||||
proc = subprocess.run(rsync_argv(*args), input=contents if source == 'stdin' else None,
|
||||
stdin=stdin if source == 'file' else None,
|
||||
stdout=subprocess.DEVNULL, stderr=subprocess.PIPE,
|
||||
text=False, timeout=20,
|
||||
env={**os.environ, 'RSYNC_PASSWORD':
|
||||
'wrong-fallback' if succeeds else PASSWORD.decode()})
|
||||
passed = proc.returncode == 0
|
||||
if passed != succeeds:
|
||||
test_fail(f'{source}/{name}: expected success={succeeds}, '
|
||||
f'rc={proc.returncode}, stderr={proc.stderr!r}')
|
||||
|
||||
if not succeeds and (proc.returncode != 1
|
||||
or b'failed to read a password' not in proc.stderr):
|
||||
test_fail(f'{source}/{name}: expected password read failure, '
|
||||
f'rc={proc.returncode}, stderr={proc.stderr!r}')
|
||||
|
||||
print('password-file: first-line behavior matches for files and stdin')
|
||||
Reference in new issue
Block a user