Hi,
I'm trying to get rsync 2.5.6 to authenticate users via openldap-2.0.23. I
was looking through the mailing list archives and found a patch for rsync-2.4.6
that does this for me. I was just wondering if this is still valid, or if there
has been a new patch or new implementation that has superceded this patch. Any
help would be great. The message I am referring to is as follows:
--T4sUOijqQbZv57TR
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
hello,
here is a patch against rsync-2.4.6. It may be used to get rsyncd
authentication data from a ldap directory.
cu, Stefan
--
Stefan Nehlsen | ParlaNet Administration | sn@parlanet.de | +49 431 988-1260
--T4sUOijqQbZv57TR
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="rsyncd-ldap.txt"
LDAP support for rsyncd
I have made a few changes to rsync 2.4.6 to add to 2 new features to rsync
when running as daemon:
1. getting authentification information from an ldap server. The user has to
belong to a special group (groupofuniqueusers) and must have a special
attribute in his ldap entry containing the rsync password in clear text.
2. specification of a list of prefixes that will be removed from the user name
given by the client.
The second option was added to be compatible to our ftp server and is simple
to use or ignore. Maybe there is better, more flexible way to rewrite usernames.
Only the files authenticate.c and loadparm.c have been modified.
How to build a rsync version with ldap support
There is no autoconf support for the new features. To enable them edit the
Makefile after running configure.
- Add -DWITH_LDAP -DWITH_MANGLE_USER to the CFLAGS line.
- On Linux add -lldap and -llber to the LIBS Flags.
- On Solaris add -lldap to the LIBS Flags.
New options in rsyncd.conf
All ldap options will exist even when rsync isn't build ldap support.
To enable anonymous access to rsyncd both "auth users" and
"ldap auth usergroup" have to be empty.
If both ways of authentification are enabled the local password file is checked
before ldap.
Most options are borrowed from samba :-)
GLOBAL OPTIONS
ldap server
This option has to contain the hostname of the ldap server we want to
ask.
example: ldap server = ldap.ParlaNet.de
default: none
ldap port
This option has to contain the tcp port where the ldap service is
listening.
example: ldap port = 11034
default: ldap port = 389
ldap root
This option should contain the dn to bind with to the ldap server.
You may leave this option and "ldap root password" empty to bind
as anonymous but this isn't a good idea.
If you use this option you should not make the configuration file
world readable.
example: ldap root = cn=webserver-rsyncd, o=ParlaNet, c=DE
default: empty
ldap root passwd
This will contain the password to bind with.
see also: ldap root
example: ldap root passwd = sECret
default: empty
ldap suffix
This option should contain the basedn as starting point for search
operations.
example: ldap suffix = o=ParlaNet, c=DE
default: empty
MODULE OPTIONS
ldap filter
This parameter specifies an LDAP search filter used
to search for a user name in the LDAP database. It
must contain the string %u which will be replaced
with the user being searched for.
example: ldap filter = (&(objectclass=myperson)(uid=%u))
default: empty string
ldap passwd attribute
This parameter specifies the name of the attribute in the user
object that contains the rsyncd password in clear text.
You should use access control on this attribute and it has to be case
sensitive.
example: ldap passwd attribute = myRsyncdPassword
default: empty string
ldap auth usergroup
This parameter specifies the dn of a group the user must belong to.
objectclass=groupOfUniqueNames is very good idea
example: ldap auth usergroup = cn=webmaster sub, o=ParlaNet, c=DE
default: empty string
ldap auth users attribute
This parameter specifies the name of the attribute in the
"ldap auth usergroup" object that contain the dn of a member.
If objectclass is groupOfUniqueNames this always should be
"uniqueMember".
default: ldap auth users attribute = uniqueMember
mangle user
This option is no ldap option.
This parameter specifies a list of prefixes that will be removed
from the username before it is tested against the access lists.
example: mangle user = web- adm-
default: empty string
HOWTO use this
1. Create a new user. The rsyncd will use this identity to bind to the
ldap directory.
the very hard way:
ldapadd -D <manager binddn> -w <password> -h <host> -v
<< EOT
dn: cn=web-rsyncd, o=ParlaNet, c=DE
objectclass: person
sn: daemon
cn: web-rsyncd
description: rsyncd user from webserver
userpassword: {bla}xxxxxxxxxxxxx
EOT
2. Define a new case sensitive attribute (myRsyncdPassword in this
example). Make it optional for your standard user class (myuser).
3. Put an ACL on this attribute so that only your rsync user and
your administrators are able to read, compare and search it.
4. Your user must be able to write a value into their objects.
the hard way:
ldapmodify -v -D uid=test,ou=sub,o=ParlaNet,c=DE -w "pw" -h ldap
<< EOT
dn: uid=test,ou=sub,o=ParlaNet,c=DE
add: myRsyncdPassword
myRsyncdPassword: secret
EOT
5. Make sure all your webmaster are in the webmaster group:
$ ldapsearch -b o=ParlaNet,c=DE -h host "cn=webmaster"
cn=webmaster, ou=sub, o=ParlaNet, c=DE
cn=webmaster
description=webmaster group
ou=sub
objectclass=top
objectclass=groupofuniquenames
uniquemember=uid=test,ou=sub,o=ParlaNet,c=DE
uniquemember=uid=kurt,ou=sub,o=ParlaNet,c=DE
uniquemember=uid=tobi,ou=sub,o=ParlaNet,c=DE
5. write your /etc/rsyncd.conf :
# /etc/rsyncd.conf
motd file = /etc/rsyncd.banner
ldap server = ldapserver.parlanet.de
ldap root = cn=web-rsyncd, o=ParlaNet, c=DE
ldap root passwd = password
ldap suffix = o=ParlaNet, c=DE
log file = /var/log/rsyncd.log
[www]
comment = http://www.something.de/
path = /var/www/pages
uid = www
gid = www
read only = yes
list = yes
ldap filter = (&(objectclass=myuser)(ou=sub)(uid=%u))
ldap passwd attribute = myRsyncdPassword
ldap auth usergroup = cn=webmaster,ou=sub,o=ParlaNet,c=de
ldap auth users attribute = uniquemember
auth users =
hosts allow = 192.168.1.0/24
max connections = 1
mangle user = web-
# EOF /etc/ryncd.conf
things to do
- check if "ldap auth users attribute" is really needed
- maybe "ldap filter" should be a module option
- The "mangle user" does exectly what I need. Maybe somebody has a
better idea.
- improve my english :-)
Stefan Nehlsen (sn@ParlaNet.de) 12.12.2000
--T4sUOijqQbZv57TR
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="rsync-2.4.6-ldapauth.diff"
diff -u rsync-2.4.6/authenticate.c rsync-2.4.6-ldapauth/authenticate.c
--- rsync-2.4.6/authenticate.c Wed Sep 6 04:46:43 2000
+++ rsync-2.4.6-ldapauth/authenticate.c Tue Dec 12 10:44:45 2000
@@ -19,6 +19,11 @@
/* support rsync authentication */
#include "rsync.h"
+#ifdef WITH_LDAP
+#include <lber.h>
+#include <ldap.h>
+#endif
+
/***************************************************************************
encode a buffer using base64 - simple and slow algorithm. null terminates
the result.
@@ -78,9 +83,23 @@
STRUCT_STAT st;
int ok = 1;
extern int am_root;
+ char *users, *tok;
if (!fname || !*fname) return 0;
+ /* this code was in auth_server() */
+ users = strdup(lp_auth_users(module));
+ if (!users) return 0;
+
+ for (tok=strtok(users," ,\t"); tok; tok = strtok(NULL,"
,\t")) {
+ if (strcmp(tok, user) == 0) break;
+ }
+ free(users);
+
+ if (!tok) {
+ return 0;
+ }
+
fd = open(fname,O_RDONLY);
if (fd == -1) return 0;
@@ -132,6 +151,84 @@
return 1;
}
+#ifdef WITH_LDAP
+/* return the secret for a user from the ldap server. maximum length
+ is len. null terminate it */
+static int get_ldap_secret(int module, char *user, char *secret, int len)
+{
+ LDAP *ld;
+ LDAPMessage *result, *entry;
+ char *attrs[2], *dn, **vals;
+ char filter[512], *c;
+ char *group=lp_ldap_auth_usergroup(module);
+ int l=0, ok=0;
+
+ /* password attribute to get as result */
+ attrs[0]=lp_ldap_passwd_attribute(module); attrs[1]=NULL;
+
+ /* find nasty character in user that would mess up the ldap filter */
+ for (c="()!&|*=<>~"; *c; c++) {
+ if (strchr(user, *c)) {
+ return 0;
+ }
+ }
+
+ /* $filter=&lp_ldap_filter($module))=~s/%u/$user/g; :-) */
+ memset(filter, 0, sizeof(filter));
+ for (c=lp_ldap_filter(module); *c && l < sizeof(filter) - 1; c++) {
+ if (*c=='%' && *(c+1)=='u') {
+ char *b;
+ for (b=user; *b && l < sizeof(filter) - 1; b++) {
+ filter[l++]=*b;
+ }
+ c++;
+ } else {
+ filter[l++]=*c;
+ }
+ }
+
+ if ((ld=ldap_init(lp_ldap_server(), lp_ldap_port())) == NULL) {
+ rprintf(FERROR,"ldap: init failed (%s:%d)\n", lp_ldap_server(),
lp_ldap_port());
+ } else {
+ if (ldap_simple_bind_s(ld, lp_ldap_root(), lp_ldap_root_passwd()) !=
LDAP_SUCCESS) {
+ rprintf(FERROR,"ldap: bind failed %s\n", lp_ldap_root());
+ } else {
+ if (ldap_search_s(ld, lp_ldap_suffix(), LDAP_SCOPE_SUBTREE, filter, attrs, 0,
&result) != LDAP_SUCCESS) {
+ rprintf(FERROR,"ldap: search_s failed\n");
+ } else {
+ if (!(entry=ldap_first_entry(ld, result))) {
+ rprintf(FERROR,"ldap: first_entry failed or no user found\n");
+ } else {
+ if (!(dn=ldap_get_dn(ld, entry))) {
+ rprintf(FERROR,"ldap: get_dn failed\n");
+ } else {
+ if (ldap_compare_s(ld, group, lp_ldap_auth_users_attribute(module), dn) !=
LDAP_COMPARE_TRUE) {
+ rprintf(FERROR,"ldap: compare_s failed or \"%s\" is not
member of \"%s\"\n", dn, group);
+ } else {
+ if (!(vals=ldap_get_values(ld, result, attrs[0])) || !vals[0] || vals[1]) {
+ rprintf(FERROR,"ldap: \"%s\" has no valid password\n",
dn);
+ if (vals) ldap_value_free(vals);
+ } else {
+ memset(secret, 0, sizeof(secret)); /* paranoid */
+ strlcpy(secret, vals[0], len);
+ ok=1;
+ ldap_value_free(vals);
+ } /* get_values */
+ } /* compare_s */
+ free(dn);
+ } /* get_dn */
+ ldap_msgfree(entry);
+ } /* first_entry */
+ ldap_msgfree(result);
+ } /* search */
+ } /* bind */
+ } /* init */
+ ldap_unbind(ld);
+
+ return ok;
+}
+#endif
+
static char *getpassf(char *filename)
{
char buffer[100];
@@ -205,6 +302,7 @@
char *auth_server(int fd, int module, char *addr, char *leader)
{
char *users = lp_auth_users(module);
+ char *group = lp_ldap_auth_usergroup(module);
char challenge[16];
char b64_challenge[30];
char line[MAXPATHLEN];
@@ -212,10 +310,19 @@
char secret[100];
char pass[30];
char pass2[30];
- char *tok;
/* if no auth list then allow anyone in! */
- if (!users || !*users) return "";
+#ifdef WITH_LDAP
+ if ((!users || !*users) && (!group || !*group)) return "";
+#else
+ if (!users || !*users) {
+ if (group && *group) {
+ rprintf(FERROR,"no ldap support: unset \"ldap auth
usergroup\" for anonymous access\n");
+ return NULL;
+ }
+ return "";
+ }
+#endif
gen_challenge(addr, challenge);
@@ -234,23 +341,38 @@
return NULL;
}
- users = strdup(users);
- if (!users) return NULL;
-
- for (tok=strtok(users," ,\t"); tok; tok = strtok(NULL,"
,\t")) {
- if (strcmp(tok, user) == 0) break;
- }
- free(users);
-
- if (!tok) {
- return NULL;
+#ifdef WITH_MANGLE_USER
+ /* foreach $pattern (split(/[ ,\t]+/, &lp_mangle_user($module)){
$user=~s/^$pattern//;warn "bla";last} */
+ if (lp_mangle_user(module)) {
+ char *prefix, *tofree;
+ prefix = tofree = strdup(lp_mangle_user(module));
+ if(!prefix) return NULL; /* strdup may fail */
+ for (prefix=strtok(prefix, " ,\t"); prefix; prefix=strtok(NULL,
" ,\t")) {
+ if (strstr(user, prefix) == user) {
+ char *p = user + strlen(prefix);
+ rprintf(FINFO,"mangle user: rewriting \"%s\" to
\"%s\"\n", user, p);
+ memmove(user, p, strlen(p) + 1);
+ break;
+ }
+ }
+ free(tofree);
}
+#endif
+ /* checking if user is in "auth users" now happens in get_secret()
*/
memset(secret, 0, sizeof(secret));
+#ifdef WITH_LDAP
+ if (!(users && *users && get_secret(module, user, secret,
sizeof(secret)-1) ||
+ group && *group && get_ldap_secret(module, user, secret,
sizeof(secret)-1))) {
+ memset(secret, 0, sizeof(secret));
+ return NULL;
+ }
+#else
if (!get_secret(module, user, secret, sizeof(secret)-1)) {
memset(secret, 0, sizeof(secret));
return NULL;
}
+#endif
generate_hash(secret, b64_challenge, pass2);
memset(secret, 0, sizeof(secret));
@@ -260,7 +382,6 @@
return NULL;
}
-
void auth_client(int fd, char *user, char *challenge)
{
Common subdirectories: rsync-2.4.6/lib and rsync-2.4.6-ldapauth/lib
diff -u rsync-2.4.6/loadparm.c rsync-2.4.6-ldapauth/loadparm.c
--- rsync-2.4.6/loadparm.c Wed Sep 6 04:46:43 2000
+++ rsync-2.4.6-ldapauth/loadparm.c Tue Dec 12 10:45:01 2000
@@ -44,6 +44,12 @@
*/
#include "rsync.h"
+
+#ifdef WITH_LDAP
+#include <lber.h>
+#include <ldap.h>
+#endif
+
#define PTR_DIFF(p1,p2) ((ptrdiff_t)(((char *)(p1)) - (char *)(p2)))
#define strequal(a,b) (strcasecmp(a,b)==0)
#define BOOLSTR(b) ((b) ? "Yes" : "No")
@@ -96,6 +102,11 @@
char *pid_file;
int syslog_facility;
char *socket_options;
+ char *ldap_server;
+ int ldap_port;
+ char *ldap_root;
+ char *ldap_root_passwd;
+ char *ldap_suffix;
} global;
static global Globals;
@@ -133,6 +144,13 @@
int timeout;
int max_connections;
BOOL ignore_nonreadable;
+ char *ldap_filter;
+ char *ldap_passwd_attribute;
+ char *ldap_auth_usergroup;
+ char *ldap_auth_users_attribute;
+#ifdef WITH_MANGLE_USER
+ char *mangle_user;
+#endif
} service;
@@ -164,7 +182,15 @@
"*.gz *.tgz *.zip *.z *.rpm *.deb *.iso *.bz2 *.tbz", /* dont
compress */
0, /* timeout */
0, /* max connections */
- False /* ignore nonreadable */
+ False, /* ignore nonreadable */
+ NULL, /* ldap filter */
+ NULL, /* ldap passwd attribute */
+ NULL, /* ldap auth usergroup */
+ "uniquemember" /* ldap auth users attribute */
+#ifdef WITH_MANGLE_USER
+ ,
+ NULL /* mangle user */
+#endif
};
@@ -252,6 +278,11 @@
{"socket options", P_STRING, P_GLOBAL,
&Globals.socket_options,NULL, 0},
{"log file", P_STRING, P_GLOBAL, &Globals.log_file,
NULL, 0},
{"pid file", P_STRING, P_GLOBAL, &Globals.pid_file,
NULL, 0},
+ {"ldap server", P_STRING, P_GLOBAL, &Globals.ldap_server,
NULL, 0},
+ {"ldap port", P_INTEGER, P_GLOBAL, &Globals.ldap_port,
NULL, 0},
+ {"ldap root", P_STRING, P_GLOBAL, &Globals.ldap_root,
NULL, 0},
+ {"ldap root passwd", P_STRING, P_GLOBAL,
&Globals.ldap_root_passwd,NULL,0},
+ {"ldap suffix", P_STRING, P_GLOBAL, &Globals.ldap_suffix,
NULL, 0},
{"timeout", P_INTEGER, P_LOCAL, &sDefault.timeout,
NULL, 0},
{"max connections", P_INTEGER, P_LOCAL,
&sDefault.max_connections,NULL, 0},
@@ -279,6 +310,13 @@
{"log format", P_STRING, P_LOCAL, &sDefault.log_format,
NULL, 0},
{"refuse options", P_STRING, P_LOCAL,
&sDefault.refuse_options,NULL, 0},
{"dont compress", P_STRING, P_LOCAL,
&sDefault.dont_compress,NULL, 0},
+ {"ldap filter", P_STRING, P_LOCAL,
&sDefault.ldap_filter, NULL, 0},
+ {"ldap passwd
attribute",P_STRING,P_LOCAL,&sDefault.ldap_passwd_attribute,NULL,0},
+ {"ldap auth usergroup",P_STRING,P_LOCAL,
&sDefault.ldap_auth_usergroup, NULL,0},
+ {"ldap auth users
attribute",P_STRING,P_LOCAL,&sDefault.ldap_auth_users_attribute,
NULL,0},
+#ifdef WITH_MANGLE_USER
+ {"mangle user", P_STRING, P_LOCAL,
&sDefault.mangle_user, NULL, 0},
+#endif
{NULL, P_BOOL, P_NONE, NULL, NULL, 0}
};
@@ -292,6 +330,9 @@
#ifdef LOG_DAEMON
Globals.syslog_facility = LOG_DAEMON;
#endif
+#ifdef WITH_LDAP
+ Globals.ldap_port = LDAP_PORT;
+#endif
}
/***************************************************************************
@@ -358,6 +399,21 @@
FN_LOCAL_STRING(lp_dont_compress, dont_compress)
FN_LOCAL_INTEGER(lp_timeout, timeout)
FN_LOCAL_INTEGER(lp_max_connections, max_connections)
+
+FN_GLOBAL_STRING(lp_ldap_server, &Globals.ldap_server)
+FN_GLOBAL_INTEGER(lp_ldap_port, &Globals.ldap_port)
+FN_GLOBAL_STRING(lp_ldap_root, &Globals.ldap_root)
+FN_GLOBAL_STRING(lp_ldap_root_passwd, &Globals.ldap_root_passwd)
+FN_GLOBAL_STRING(lp_ldap_suffix, &Globals.ldap_suffix)
+
+FN_LOCAL_STRING(lp_ldap_filter, ldap_filter)
+FN_LOCAL_STRING(lp_ldap_passwd_attribute, ldap_passwd_attribute)
+FN_LOCAL_STRING(lp_ldap_auth_usergroup, ldap_auth_usergroup)
+FN_LOCAL_STRING(lp_ldap_auth_users_attribute, ldap_auth_users_attribute)
+
+#ifdef WITH_MANGLE_USER
+FN_LOCAL_STRING(lp_mangle_user, mangle_user)
+#endif
/* local prototypes */
static int strwicmp( char *psz1, char *psz2 );
Common subdirectories: rsync-2.4.6/packaging and rsync-2.4.6-ldapauth/packaging
Common subdirectories: rsync-2.4.6/zlib and rsync-2.4.6-ldapauth/zlib
--T4sUOijqQbZv57TR--