Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
L
liblongpath-rsync
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
liblongpath
liblongpath-rsync
Commits
5ebe9a46
Commit
5ebe9a46
authored
Oct 12, 2010
by
Wayne Davison
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add @group auth and overrides to "auth user" daemon config.
parent
d64bda1c
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
207 additions
and
101 deletions
+207
-101
NEWS
NEWS
+3
-0
authenticate.c
authenticate.c
+136
-79
clientserver.c
clientserver.c
+4
-6
io.c
io.c
+6
-3
rsyncd.conf.yo
rsyncd.conf.yo
+58
-13
No files found.
NEWS
View file @
5ebe9a46
...
...
@@ -83,6 +83,9 @@ Changes since 3.0.4:
daemon to complain about an inability to set explicitly-specified uid/gid
values, even when not run by a super-user.
- Added per-user authorization options and group-authorization support to
the daemon's "auth users" parameter.
- Added a way to reference environment variables in a daemon's config file
(using %VAR% references).
...
...
authenticate.c
View file @
5ebe9a46
...
...
@@ -19,7 +19,9 @@
*/
#include "rsync.h"
#include "itypes.h"
extern
int
read_only
;
extern
char
*
password_file
;
/***************************************************************************
...
...
@@ -76,25 +78,40 @@ static void gen_challenge(const char *addr, char *challenge)
base64_encode
(
digest
,
len
,
challenge
,
0
);
}
/* Generate an MD4 hash created from the combination of the password
* and the challenge string and return it base64-encoded. */
static
void
generate_hash
(
const
char
*
in
,
const
char
*
challenge
,
char
*
out
)
{
char
buf
[
MAX_DIGEST_LEN
];
int
len
;
sum_init
(
0
);
sum_update
(
in
,
strlen
(
in
));
sum_update
(
challenge
,
strlen
(
challenge
));
len
=
sum_end
(
buf
);
base64_encode
(
buf
,
len
,
out
,
0
);
}
/* Return the secret for a user from the secret file, null terminated.
* Maximum length is len (not counting the null). */
static
int
get_secret
(
int
module
,
const
char
*
user
,
char
*
secret
,
int
len
)
static
const
char
*
check_secret
(
int
module
,
const
char
*
user
,
const
char
*
group
,
const
char
*
challenge
,
const
char
*
pass
)
{
char
line
[
1024
];
char
pass2
[
MAX_DIGEST_LEN
*
2
];
const
char
*
fname
=
lp_secrets_file
(
module
);
STRUCT_STAT
st
;
int
fd
,
ok
=
1
;
const
char
*
p
;
char
ch
,
*
s
;
int
user_len
=
strlen
(
user
);
int
group_len
=
group
?
strlen
(
group
)
:
0
;
char
*
err
;
if
(
!
fname
||
!*
fname
)
return
0
;
if
(
!
fname
||
!*
fname
||
(
fd
=
open
(
fname
,
O_RDONLY
))
<
0
)
return
"no secrets file"
;
if
((
fd
=
open
(
fname
,
O_RDONLY
))
<
0
)
return
0
;
if
(
do_stat
(
fname
,
&
st
)
==
-
1
)
{
rsyserr
(
FLOG
,
errno
,
"stat(%s)"
,
fname
);
if
(
do_fstat
(
fd
,
&
st
)
==
-
1
)
{
rsyserr
(
FLOG
,
errno
,
"fstat(%s)"
,
fname
);
ok
=
0
;
}
else
if
(
lp_strict_modes
(
module
))
{
if
((
st
.
st_mode
&
06
)
!=
0
)
{
...
...
@@ -106,50 +123,47 @@ static int get_secret(int module, const char *user, char *secret, int len)
}
}
if
(
!
ok
)
{
rprintf
(
FLOG
,
"continuing without secrets file
\n
"
);
close
(
fd
);
return
0
;
return
"ignoring secrets file"
;
}
if
(
*
user
==
'#'
)
{
/* Reject attempt to match a comment. */
close
(
fd
);
return
0
;
return
"invalid username"
;
}
/* Try to find a line that starts with the user name and a ':'. */
p
=
user
;
while
(
1
)
{
if
(
read
(
fd
,
&
ch
,
1
)
!=
1
)
{
close
(
fd
);
return
0
;
/* Try to find a line that starts with the user (or @group) name and a ':'. */
err
=
"secret not found"
;
while
((
user
||
group
)
&&
read_line_old
(
fd
,
line
,
sizeof
line
,
1
))
{
const
char
**
ptr
,
*
s
;
int
len
;
if
(
*
line
==
'@'
)
{
ptr
=
&
group
;
len
=
group_len
;
s
=
line
+
1
;
}
else
{
ptr
=
&
user
;
len
=
user_len
;
s
=
line
;
}
if
(
ch
==
'\n'
)
p
=
user
;
else
if
(
p
)
{
if
(
*
p
==
ch
)
p
++
;
else
if
(
!*
p
&&
ch
==
':'
)
break
;
else
p
=
NULL
;
if
(
!*
ptr
||
strncmp
(
s
,
*
ptr
,
len
)
!=
0
||
s
[
len
]
!=
':'
)
continue
;
generate_hash
(
s
+
len
+
1
,
challenge
,
pass2
);
if
(
strcmp
(
pass
,
pass2
)
==
0
)
{
err
=
NULL
;
break
;
}
err
=
"password mismatch"
;
*
ptr
=
NULL
;
/* Don't look for name again. */
}
/* Slurp the secret into the "secret" buffer. */
s
=
secret
;
while
(
len
>
0
)
{
if
(
read
(
fd
,
s
,
1
)
!=
1
||
*
s
==
'\n'
)
break
;
if
(
*
s
==
'\r'
)
continue
;
s
++
;
len
--
;
}
*
s
=
'\0'
;
close
(
fd
);
return
1
;
memset
(
line
,
0
,
sizeof
line
);
memset
(
pass2
,
0
,
sizeof
pass2
);
return
err
;
}
static
const
char
*
getpassf
(
const
char
*
filename
)
...
...
@@ -199,21 +213,6 @@ static const char *getpassf(const char *filename)
return
NULL
;
}
/* Generate an MD4 hash created from the combination of the password
* and the challenge string and return it base64-encoded. */
static
void
generate_hash
(
const
char
*
in
,
const
char
*
challenge
,
char
*
out
)
{
char
buf
[
MAX_DIGEST_LEN
];
int
len
;
sum_init
(
0
);
sum_update
(
in
,
strlen
(
in
));
sum_update
(
challenge
,
strlen
(
challenge
));
len
=
sum_end
(
buf
);
base64_encode
(
buf
,
len
,
out
,
0
);
}
/* Possibly negotiate authentication with the client. Use "leader" to
* start off the auth if necessary.
*
...
...
@@ -226,9 +225,12 @@ char *auth_server(int f_in, int f_out, int module, const char *host,
char
*
users
=
lp_auth_users
(
module
);
char
challenge
[
MAX_DIGEST_LEN
*
2
];
char
line
[
BIGPATHBUFLEN
];
char
secret
[
512
];
char
pass2
[
MAX_DIGEST_LEN
*
2
];
char
**
auth_uid_groups
=
NULL
;
int
auth_uid_groups_cnt
=
-
1
;
const
char
*
err
=
NULL
;
int
group_match
=
-
1
;
char
*
tok
,
*
pass
;
char
opt_ch
=
'\0'
;
/* if no auth list then allow anyone in! */
if
(
!
users
||
!*
users
)
...
...
@@ -238,7 +240,7 @@ char *auth_server(int f_in, int f_out, int module, const char *host,
io_printf
(
f_out
,
"%s%s
\n
"
,
leader
,
challenge
);
if
(
!
read_line_old
(
f_in
,
line
,
sizeof
line
)
if
(
!
read_line_old
(
f_in
,
line
,
sizeof
line
,
0
)
||
(
pass
=
strchr
(
line
,
' '
))
==
NULL
)
{
rprintf
(
FLOG
,
"auth failed on module %s from %s (%s): "
"invalid challenge response
\n
"
,
...
...
@@ -251,37 +253,92 @@ char *auth_server(int f_in, int f_out, int module, const char *host,
out_of_memory
(
"auth_server"
);
for
(
tok
=
strtok
(
users
,
" ,
\t
"
);
tok
;
tok
=
strtok
(
NULL
,
" ,
\t
"
))
{
if
(
wildmatch
(
tok
,
line
))
break
;
char
*
opts
;
/* See if the user appended :deny, :ro, or :rw. */
if
((
opts
=
strchr
(
tok
,
':'
))
!=
NULL
)
{
*
opts
++
=
'\0'
;
opt_ch
=
isUpper
(
opts
)
?
toLower
(
opts
)
:
*
opts
;
if
(
opt_ch
==
'r'
)
{
/* handle ro and rw */
opt_ch
=
isUpper
(
opts
+
1
)
?
toLower
(
opts
+
1
)
:
opts
[
1
];
if
(
opt_ch
==
'o'
)
opt_ch
=
'r'
;
else
if
(
opt_ch
!=
'w'
)
opt_ch
=
'\0'
;
}
else
if
(
opt_ch
!=
'd'
)
/* if it's not deny, ignore it */
opt_ch
=
'\0'
;
}
else
opt_ch
=
'\0'
;
if
(
*
tok
!=
'@'
)
{
/* Match the username */
if
(
wildmatch
(
tok
,
line
))
break
;
}
else
{
#ifdef HAVE_GETGROUPLIST
int
j
;
/* See if authorizing user is a real user, and if so, see
* if it is in a group that matches tok+1 wildmat. */
if
(
auth_uid_groups_cnt
<
0
)
{
gid_t
gid_list
[
64
];
uid_t
auth_uid
;
auth_uid_groups_cnt
=
sizeof
gid_list
/
sizeof
(
gid_t
);
if
(
!
user_to_uid
(
line
,
&
auth_uid
,
False
)
||
getallgroups
(
auth_uid
,
gid_list
,
&
auth_uid_groups_cnt
)
!=
NULL
)
auth_uid_groups_cnt
=
0
;
else
{
if
((
auth_uid_groups
=
new_array
(
char
*
,
auth_uid_groups_cnt
))
==
NULL
)
out_of_memory
(
"auth_server"
);
for
(
j
=
0
;
j
<
auth_uid_groups_cnt
;
j
++
)
auth_uid_groups
[
j
]
=
gid_to_group
(
gid_list
[
j
]);
}
}
for
(
j
=
0
;
j
<
auth_uid_groups_cnt
;
j
++
)
{
if
(
auth_uid_groups
[
j
]
&&
wildmatch
(
tok
+
1
,
auth_uid_groups
[
j
]))
{
group_match
=
j
;
break
;
}
}
if
(
group_match
>=
0
)
break
;
#else
rprintf
(
FLOG
,
"your computer doesn't support getgrouplist(), so no @group authorization is possible.
\n
"
);
#endif
}
}
free
(
users
);
if
(
!
tok
)
{
rprintf
(
FLOG
,
"auth failed on module %s from %s (%s): "
"unauthorized user
\n
"
,
lp_name
(
module
),
host
,
addr
);
return
NULL
;
if
(
!
tok
)
err
=
"no matching rule"
;
else
if
(
opt_ch
==
'd'
)
err
=
"denied by rule"
;
else
{
char
*
group
=
group_match
>=
0
?
auth_uid_groups
[
group_match
]
:
NULL
;
err
=
check_secret
(
module
,
line
,
group
,
challenge
,
pass
);
}
memset
(
secret
,
0
,
sizeof
secret
);
if
(
!
get_secret
(
module
,
line
,
secret
,
sizeof
secret
-
1
))
{
memset
(
secret
,
0
,
sizeof
secret
);
rprintf
(
FLOG
,
"auth failed on module %s from %s (%s): "
"missing secret for user
\"
%s
\"\n
"
,
lp_name
(
module
),
host
,
addr
,
line
);
return
NULL
;
}
memset
(
challenge
,
0
,
sizeof
challenge
);
memset
(
pass
,
0
,
strlen
(
pass
));
generate_hash
(
secret
,
challenge
,
pass2
);
memset
(
secret
,
0
,
sizeof
secret
);
if
(
auth_uid_groups
)
{
int
j
;
for
(
j
=
0
;
j
<
auth_uid_groups_cnt
;
j
++
)
{
if
(
auth_uid_groups
[
j
])
free
(
auth_uid_groups
[
j
]);
}
free
(
auth_uid_groups
);
}
if
(
strcmp
(
pass
,
pass2
)
!=
0
)
{
rprintf
(
FLOG
,
"auth failed on module %s from %s (%s): "
"password mismatch
\n
"
,
lp_name
(
module
),
host
,
addr
);
if
(
err
)
{
rprintf
(
FLOG
,
"auth failed on module %s from %s (%s) for %s: %s
\n
"
,
lp_name
(
module
),
host
,
addr
,
line
,
err
);
return
NULL
;
}
if
(
opt_ch
==
'r'
)
read_only
=
1
;
else
if
(
opt_ch
==
'w'
)
read_only
=
0
;
return
strdup
(
line
);
}
...
...
clientserver.c
View file @
5ebe9a46
...
...
@@ -164,7 +164,7 @@ static int exchange_protocols(int f_in, int f_out, char *buf, size_t bufsiz, int
}
/* This strips the \n. */
if
(
!
read_line_old
(
f_in
,
buf
,
bufsiz
))
{
if
(
!
read_line_old
(
f_in
,
buf
,
bufsiz
,
0
))
{
if
(
am_client
)
rprintf
(
FERROR
,
"rsync: did not see server greeting
\n
"
);
return
-
1
;
...
...
@@ -283,7 +283,7 @@ int start_inband_exchange(int f_in, int f_out, const char *user, int argc, char
kluge_around_eof
=
list_only
&&
protocol_version
<
25
?
1
:
0
;
while
(
1
)
{
if
(
!
read_line_old
(
f_in
,
line
,
sizeof
line
))
{
if
(
!
read_line_old
(
f_in
,
line
,
sizeof
line
,
0
))
{
rprintf
(
FERROR
,
"rsync: didn't get server startup line
\n
"
);
return
-
1
;
}
...
...
@@ -544,6 +544,7 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
return
-
1
;
}
read_only
=
lp_read_only
(
i
);
/* may also be overridden by auth_server() */
auth_user
=
auth_server
(
f_in
,
f_out
,
i
,
host
,
addr
,
"@RSYNCD: AUTHREQD "
);
if
(
!
auth_user
)
{
...
...
@@ -554,9 +555,6 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
module_id
=
i
;
if
(
lp_read_only
(
i
))
read_only
=
1
;
if
(
lp_transfer_logging
(
i
)
&&
!
logfile_format
)
logfile_format
=
lp_log_format
(
i
);
if
(
log_format_has
(
logfile_format
,
'i'
))
...
...
@@ -1034,7 +1032,7 @@ int start_daemon(int f_in, int f_out)
return
-
1
;
line
[
0
]
=
0
;
if
(
!
read_line_old
(
f_in
,
line
,
sizeof
line
))
if
(
!
read_line_old
(
f_in
,
line
,
sizeof
line
,
0
))
return
-
1
;
if
(
!*
line
||
strcmp
(
line
,
"#list"
)
==
0
)
{
...
...
io.c
View file @
5ebe9a46
...
...
@@ -2205,13 +2205,16 @@ int32 read_ndx(int f)
/* Read a line of up to bufsiz-1 characters into buf. Strips
* the (required) trailing newline and all carriage returns.
* Returns 1 for success; 0 for I/O error or truncation. */
int
read_line_old
(
int
fd
,
char
*
buf
,
size_t
bufsiz
)
int
read_line_old
(
int
fd
,
char
*
buf
,
size_t
bufsiz
,
int
eof_ok
)
{
assert
(
fd
!=
iobuf
.
in_fd
);
bufsiz
--
;
/* leave room for the null */
while
(
bufsiz
>
0
)
{
assert
(
fd
!=
iobuf
.
in_fd
);
if
(
safe_read
(
fd
,
buf
,
1
)
==
0
)
if
(
safe_read
(
fd
,
buf
,
1
)
==
0
)
{
if
(
eof_ok
)
break
;
return
0
;
}
if
(
*
buf
==
'\0'
)
return
0
;
if
(
*
buf
==
'\n'
)
...
...
rsyncd.conf.yo
View file @
5ebe9a46
...
...
@@ -320,6 +320,8 @@ attempted uploads will fail. If "read only" is false then uploads will
be possible if file permissions on the daemon side allow them. The default
is for all modules to be read only.
Note that "auth users" can override this setting on a per-user basis.
dit(bf(write only)) This parameter determines whether clients
will be able to download files or not. If "write only" is true then any
attempted downloads will fail. If "write only" is false then downloads
...
...
@@ -432,10 +434,12 @@ be on to the clients.
See the description of the bf(--chmod) rsync option and the bf(chmod)(1)
manpage for information on the format of this string.
dit(bf(auth users)) This parameter specifies a comma and
space-separated list of usernames that will be allowed to connect to
dit(bf(auth users)) This parameter specifies a comma and/or space-separated
list of authorization rules. In its simplest form, you list the usernames
that will be allowed to connect to
this module. The usernames do not need to exist on the local
system. The usernames may also contain shell wildcard characters. If
system. The rules may contain shell wildcard characters that will be matched
against the username provided by the client for authentication. If
"auth users" is set then the client will be challenged to supply a
username and password to connect to the module. A challenge response
authentication protocol is used for this exchange. The plain text
...
...
@@ -443,24 +447,65 @@ usernames and passwords are stored in the file specified by the
"secrets file" parameter. The default is for all users to be able to
connect without a password (this is called "anonymous rsync").
In addition to username matching, you can specify groupname matching via a '@'
prefix. When using groupname matching, the authenticating username must be a
real user on the system, or it will be assumed to be a member of no groups.
For example, specifying "@rsync" will match the authenticating user if the
named user is a member of the rsync group.
Finally, options may be specified after a colon (:). The options allow you to
"deny" a user or a group, set the access to "ro" (read-only), or set the access
to "rw" (read/write). Setting an auth-rule-specific ro/rw setting overrides
the module's "read only" setting.
Be sure to put the rules in the order you want them to be matched, because the
checking stops at the first matching user or group, and that is the only auth
that is checked. For example:
verb( auth users = joe:deny @guest:deny admin:rw @rsync:ro susan joe sam )
In the above rule, user joe will be denied access no matter what. Any user
that is in the group "guest" is also denied access. The user "admin" gets
access in read/write mode, but only if the admin user is not in group "guest"
(because the admin user-matching rule would never be reached if the user is in
group "guest"). Any other user who is in group "rsync" will get read-only
access. Finally, users susan, joe, and sam get the ro/rw setting of the
module, but only if the user didn't match an earlier group-matching rule.
See the description of the secrets file for how you can have per-user passwords
as well as per-group passwords. It also explains how a user can authenticate
using their user password or (when applicable) a group password, depending on
what rule is being authenticated.
See also the section entitled "USING RSYNC-DAEMON FEATURES VIA A REMOTE
SHELL CONNECTION" in bf(rsync)(1) for information on how handle an
rsyncd.conf-level username that differs from the remote-shell-level
username when using a remote shell to connect to an rsync daemon.
dit(bf(secrets file)) This parameter specifies the name of
a file that contains the username:password pairs used for
authenticating this module. This file is only consulted if the "auth
users" parameter is specified. The file is line based and contains
username:password pairs separated by a single colon. Any line starting
with a hash (#) is considered a comment and is skipped. The passwords
can contain any characters but be warned that many operating systems
limit the length of passwords that can be typed at the client end, so
you may find that passwords longer than 8 characters don't work.
dit(bf(secrets file)) This parameter specifies the name of a file that contains
the username:password and/or @groupname:password pairs used for authenticating
this module. This file is only consulted if the "auth users" parameter is
specified. The file is line-based and contains one name:password pair per
line. Any line has a hash (#) as the very first character on the line is
considered a comment and is skipped. The passwords can contain any characters
but be warned that many operating systems limit the length of passwords that
can be typed at the client end, so you may find that passwords longer than 8
characters don't work.
The use of group-specific lines are only relevant when the module is being
authorized using a matching "@groupname" rule. When that happens, the user
can be authorized via either their "username:password" line or the
"@groupname:password" line for the group that triggered the authentication.
It is up to you what kind of password entries you want to include, either
users, groups, or both. The use of group rules in "auth users" does not
require that you specify a group password if you do not want to use shared
passwords.
There is no default for the "secrets file" parameter, you must choose a name
(such as tt(/etc/rsyncd.secrets)). The file must normally not be readable
by "other"; see "strict modes".
by "other"; see "strict modes". If the file is not found or is rejected, no
logins for a "user auth" module will be possible.
dit(bf(strict modes)) This parameter determines whether or not
the permissions on the secrets file will be checked. If "strict modes" is
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment