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 | [EMAIL PROTECTED] | +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 ([EMAIL PROTECTED]) 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-- -- To unsubscribe or change options: http://lists.samba.org/mailman/listinfo/rsync Before posting, read: http://www.tuxedo.org/~esr/faqs/smart-questions.html