[svn.haxx.se] · SVN Dev · SVN Users · SVN Org · TSVN Dev · TSVN Users · Subclipse Dev · Subclipse Users · this month's index

[PATCH] Authentication for svnserve using LDAP

From: Ivlev, Eugene <eivlev_at_esignaldev.com>
Date: Thu, 31 Jan 2008 14:03:15 +0300
New feature.
Authentication for svnserve using LDAP. Information about LDAP setting contains in svnserve.conf.
Example:

[ldap]
Server=ldap://dc1:389/
Base=OU=PrivateUsers,OU=TSSupportUsers,DC=nwork,DC=local
BindName=CN=LDAPSvcAccount,OU=ServiceAccount,OU=TSSupportUsers,DC=nwork,DC=local
BindPass=supersvcpass
UserIdAttr=sAMaccountName

For compilation You are also should get the open_ldap or novell-cldap library.
Compile without preprocessor directive SVN_HAVE_SASL.

Index: include/svn_config.h
===================================================================
--- include/svn_config.h (revision 28868)
+++ include/svn_config.h (working copy)
@@ -114,6 +114,12 @@
 #define SVN_CONFIG_OPTION_USE_SASL "use-sasl"
 #define SVN_CONFIG_OPTION_MIN_SSF "min-encryption"
 #define SVN_CONFIG_OPTION_MAX_SSF "max-encryption"
+#define SVN_CONFIG_SECTION_LDAP "ldap"
+#define SVN_CONFIG_OPTION_LDAP_SERVER "Server"
+#define SVN_CONFIG_OPTION_LDAP_BASE "Base"
+#define SVN_CONFIG_OPTION_LDAP_BIND_NAME "BindName"
+#define SVN_CONFIG_OPTION_LDAP_BIND_PASS "BindPass"
+#define SVN_CONFIG_OPTION_LDAP_USER_ID_ATTR "UserIdAttr"
 
 /* For repository password database */
 #define SVN_CONFIG_SECTION_USERS "users"
Index: include/svn_ra_svn.h
===================================================================
--- include/svn_ra_svn.h (revision 28868)
+++ include/svn_ra_svn.h (working copy)
@@ -410,6 +410,13 @@
                                     svn_config_t *pwdb, const char **user,
                                     svn_boolean_t *success);
 
+/** This function is only intended for use by svnserve.
+ *
+ * Basic password authentication.
+ */
+svn_error_t *svn_ra_svn_basic_server(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
+ svn_config_t *cfg, const char **user,
+ svn_boolean_t *success);
 /**
  * Get libsvn_ra_svn version information.
  * @since New in 1.1.
Index: libsvn_ra_svn/basic.c
===================================================================
--- libsvn_ra_svn/basic.c (revision 0)
+++ libsvn_ra_svn/basic.c (revision 0)
@@ -0,0 +1,420 @@
+/*
+
+*/
+#define APR_WANT_STRFUNC
+#define APR_WANT_STDIO
+
+#include <apr_want.h>
+#include <apr_general.h>
+#include <apr_strings.h>
+#include <apr_network_io.h>
+#include <apr_time.h>
+#include <apr_base64.h>
+
+#include "svn_types.h"
+#include "svn_string.h"
+#include "svn_error.h"
+#include "svn_ra_svn.h"
+#include "svn_config.h"
+#include "svn_private_config.h"
+
+#include "ra_svn.h"
+
+#include "lber.h"
+#include "ldap.h"
+
+typedef struct {
+ char *host;
+ int port;
+
+ char *base;
+ char *bindname;
+ int searchmode;
+ char *bindpass;
+ int ldap_auth;
+ int reset_username;
+
+ char *userid_attr;
+ char *userpassword_attr;
+ char *groupmember_attr;
+
+ LDAP *ld;
+} ldap_config_struct;
+
+/*
+
+*/
+ldap_config_struct* get_ldap_config(svn_config_t **cfg, apr_pool_t *pool)
+{
+ ldap_config_struct* conf;
+ const char *ldap_server;
+ const char *ldap_base;
+ const char *ldap_bind_name;
+ const char *ldap_bind_pass;
+ const char *ldap_user_id_attr;
+
+ LDAPURLDesc *ldap_url;
+
+ int host_str_len;
+ int base_str_len;
+ int bindname_str_len;
+ int bindpass_str_len;
+ int useridattr_str_len;
+
+ conf = (ldap_config_struct*)apr_palloc(pool, sizeof(ldap_config_struct));
+
+ conf->host = "localhost";
+ conf->port = LDAP_PORT;
+
+ conf->base = NULL;
+ conf->searchmode = LDAP_SCOPE_ONELEVEL;
+ conf->bindname = NULL;
+ conf->bindpass = NULL;
+
+ conf->userid_attr = "userid";
+ conf->userpassword_attr = NULL;
+ conf->groupmember_attr = "uniquemember";
+
+ conf->ldap_auth = 1;
+ conf->reset_username = 0;
+
+ conf->ld = NULL;
+
+ svn_config_get(*cfg, &ldap_server, SVN_CONFIG_SECTION_LDAP,
+ SVN_CONFIG_OPTION_LDAP_SERVER, NULL);
+
+ svn_config_get(*cfg, &ldap_base, SVN_CONFIG_SECTION_LDAP,
+ SVN_CONFIG_OPTION_LDAP_BASE, NULL);
+
+ svn_config_get(*cfg, &ldap_bind_name, SVN_CONFIG_SECTION_LDAP,
+ SVN_CONFIG_OPTION_LDAP_BIND_NAME, NULL);
+
+ svn_config_get(*cfg, &ldap_bind_pass, SVN_CONFIG_SECTION_LDAP,
+ SVN_CONFIG_OPTION_LDAP_BIND_PASS, NULL);
+
+ svn_config_get(*cfg, &ldap_user_id_attr, SVN_CONFIG_SECTION_LDAP,
+ SVN_CONFIG_OPTION_LDAP_USER_ID_ATTR, NULL);
+
+ if (ldap_url_parse(ldap_server,&ldap_url)!=0)
+ {
+ return NULL; // cannot parse LDAP url
+ }
+
+ host_str_len = strlen(ldap_url->lud_host);
+ conf->host = apr_palloc(pool, host_str_len + 1);
+ apr_cpystrn(conf->host, ldap_url->lud_host, host_str_len + 1);
+
+ if (ldap_url->lud_port!=0)
+ conf->port=ldap_url->lud_port;
+
+ base_str_len = strlen(ldap_base);
+ conf->base = apr_palloc(pool, base_str_len + 1);
+ apr_cpystrn(conf->base, ldap_base, base_str_len + 1);
+
+ bindname_str_len = strlen(ldap_bind_name);
+ conf->bindname = apr_palloc(pool, bindname_str_len + 1);
+ apr_cpystrn(conf->bindname, ldap_bind_name, bindname_str_len + 1);
+
+ bindpass_str_len = strlen(ldap_bind_pass);
+ conf->bindpass = apr_palloc(pool, bindpass_str_len + 1);
+ apr_cpystrn(conf->bindpass, ldap_bind_pass, bindpass_str_len + 1);
+
+
+ useridattr_str_len = strlen(ldap_user_id_attr);
+ conf->userid_attr = apr_palloc(pool, useridattr_str_len + 1);
+ apr_cpystrn(conf->userid_attr, ldap_user_id_attr, useridattr_str_len + 1);
+
+ ldap_free_urldesc(ldap_url);
+
+ return conf;
+}
+
+/*
+
+*/
+static LDAP *ldap_open_and_bind (char *host,int port,char *username,char *password) {
+ LDAP *ld;
+ int res;
+
+ ld=ldap_open(host,port);
+ if (!ld)
+ return NULL;
+
+ if (username == NULL) {
+ res = ldap_simple_bind_s(ld,NULL,NULL);
+ } else {
+ res = ldap_simple_bind_s(ld,username,password);
+ }
+ if (res!=LDAP_SUCCESS) {
+ ldap_unbind(ld);
+ return NULL;
+ }
+
+ return ld;
+}
+
+/*
+
+*/
+svn_boolean_t *ldap_authentication(ldap_config_struct* conf, char* user, char* password, apr_pool_t *pool)
+{
+ LDAPMessage *msg, *entry;
+ int res;
+ char *filter;
+ int scope;
+
+ if (conf == NULL)
+ return FALSE;
+
+ if (!conf->ldap_auth) return FALSE;
+
+ /* Changed by Soren Roug */
+ scope = conf->searchmode;
+
+ conf->ld = ldap_open_and_bind(conf->host,
+ conf->port,
+ conf->bindname,
+ conf->bindpass);
+
+ /* we should have an open and bound connection. if not then error */
+ if (conf->ld==NULL)
+ return FALSE;
+
+ filter = apr_pstrcat(pool,
+ "(",
+ conf->userid_attr,
+ "=",
+ user,
+ ")",
+ NULL);
+
+ /* Changed this and one other occurrence from LDAP_SCOPE_whatever to
+ user configurable scope. JM 12/23/98 */
+
+ res=ldap_search_s(conf->ld,
+ conf->base,
+ scope,
+ filter,
+ NULL,
+ 0,
+ &msg);
+ if ((res!=LDAP_SUCCESS) || !msg)
+ {
+ ldap_unbind(conf->ld);
+ return FALSE;
+ }
+
+ entry=ldap_first_entry(conf->ld,msg);
+
+ if (entry)
+ {
+ user=ldap_get_dn(conf->ld, entry);
+ /*
+ return svn_error_create(SVN_ERR_AUTHN_FAILED, NULL,
+ "LDAP authentication failed!");
+ */
+ }
+ else
+ {
+ user=NULL;
+ }
+
+ ldap_msgfree(msg);
+
+ if (user != NULL)
+ {
+ if (!conf->userpassword_attr)
+ {
+ ldap_unbind(conf->ld);
+ conf->ld = ldap_open_and_bind(conf->host,
+ conf->port,
+ user,
+ password);
+
+ if (conf->ld==NULL)
+ {
+ user=NULL;
+ }
+ else
+ {
+ ldap_unbind(conf->ld);
+ }
+ }
+ else
+ {
+ if (ldap_compare_s(conf->ld,
+ user,
+ conf->userpassword_attr,
+ password)!=LDAP_COMPARE_TRUE)
+ {
+ user = NULL;
+ }
+ ldap_unbind(conf->ld);
+ }
+ }
+
+ if (user == NULL)
+ {
+ return FALSE;
+ }
+ else
+ {
+ return TRUE;
+ }
+}
+
+/* If we can, make the nonce with random bytes. If we can't... well,
+* it just has to be different each time. The current time isn't
+* absolutely guaranteed to be different for each connection, but it
+* should prevent replay attacks in practice. */
+static apr_status_t make_nonce(apr_uint64_t *nonce)
+{
+#if APR_HAS_RANDOM
+ return apr_generate_random_bytes((unsigned char *) nonce, sizeof(*nonce));
+#else
+ *nonce = apr_time_now();
+ return APR_SUCCESS;
+#endif
+}
+
+/* Fail the authentication, from the server's perspective. */
+static svn_error_t *fail(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
+ const char *msg)
+{
+ SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "w(c)", "failure", msg));
+ return svn_ra_svn_flush(conn, pool);
+}
+
+svn_error_t *svn_ra_svn_basic_server(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
+ svn_config_t *cfg, const char **user,
+ svn_boolean_t *success)
+{
+ apr_status_t status;
+ apr_uint64_t nonce;
+ char hostbuf[APRMAXHOSTLEN + 1];
+
+ const char *challenge;
+ svn_ra_svn_item_t *item;
+ svn_string_t *resp;
+
+ apr_size_t resp_len;
+ apr_size_t decoded_len;
+ char* decoded_resp;
+ char* in_user;
+ char* password;
+ ldap_config_struct* conf = NULL;
+
+ int user_str_len;
+ int password_str_len;
+ unsigned int i;
+ *success = FALSE;
+
+ /* Send a challenge. */
+
+ status = make_nonce(&nonce);
+ if (!status)
+ status = apr_gethostname(hostbuf, sizeof(hostbuf), pool);
+ if (status)
+ return fail(conn, pool, "Internal server error in authentication");
+ challenge = apr_psprintf(pool,
+ "<%" APR_UINT64_T_FMT ".%" APR_TIME_T_FMT "@%s>",
+ nonce, apr_time_now(), hostbuf);
+ SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "w(c)", "step", challenge));
+
+ /* Read the client's response and decode it into *user and password. */
+
+ SVN_ERR(svn_ra_svn_read_item(conn, pool, &item));
+ if (item->kind != SVN_RA_SVN_STRING)
+ return SVN_NO_ERROR;
+
+ resp = item->u.string;
+ resp_len = strlen(resp->data);
+ decoded_len = apr_base64_decode_len(resp->data);
+ decoded_resp = apr_palloc(pool, decoded_len + 1);
+
+ apr_base64_decode(decoded_resp, resp->data);
+
+ password = apr_palloc(pool, decoded_len + 1);
+ memset(password, 0, decoded_len + 1);
+
+ in_user = apr_palloc(pool, decoded_len + 1);
+ memset(in_user, 0, decoded_len + 1);
+
+ user_str_len = 0;
+ for (i = 0; i < decoded_len && decoded_resp[i] != ':'; i++)user_str_len++;
+ apr_cpystrn(in_user, decoded_resp, user_str_len + 1);
+ password_str_len = decoded_len - user_str_len - 1;
+ apr_cpystrn(password, decoded_resp + user_str_len + 1, password_str_len);
+
+ /* LDAP authentication */
+ conf = get_ldap_config(&cfg, pool);
+ *success = ldap_authentication(conf, in_user, password, pool);
+ if (*success)
+ {
+ *user = in_user;
+ svn_ra_svn_write_tuple(conn, pool, "w()", "success");
+ return SVN_NO_ERROR;
+ }
+ else
+ {
+ svn_ra_svn_write_tuple(conn, pool, "w()", "failure");
+ return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
+ _("Authentication failed!"));
+ }
+}
+
+svn_error_t *svn_ra_svn_basic_client(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
+ const char *user, const char *password,
+ const char **message)
+{
+ const char *status, *str;
+ char *reply;
+ char* tmp_str;
+ int encoded_len;
+
+ /* Read the server challenge. */
+ SVN_ERR(svn_ra_svn_read_tuple(conn, pool, "w(?c)", &status, &str));
+ if (strcmp(status, "failure") == 0 && str)
+ {
+ *message = str;
+ return SVN_NO_ERROR;
+ }
+ else if (strcmp(status, "step") != 0 || !str)
+ return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
+ _("Unexpected server response to authentication"));
+
+ /* Write our response. */
+
+ if (user && password)
+ {
+ int user_len = strlen(user);
+ int password_len = strlen(password);
+ int all_len = user_len + password_len;
+
+ tmp_str = apr_palloc(pool, all_len + 1);
+ memset(tmp_str, 0, all_len + 1);
+
+ sprintf(tmp_str, "%s:%s", user, password);
+
+ encoded_len = apr_base64_encode_len(all_len);
+ reply = apr_palloc(pool, encoded_len + 1);
+ memset(reply, 0, encoded_len + 1);
+ apr_base64_encode(reply, tmp_str, all_len + 1);
+ }
+ SVN_ERR(svn_ra_svn_write_cstring(conn, pool, reply));
+
+ /* Read the success or failure response from the server. */
+ SVN_ERR(svn_ra_svn_read_tuple(conn, pool, "w(?c)", &status, &str));
+ if (strcmp(status, "failure") == 0 && str)
+ {
+ *message = str;
+ return SVN_NO_ERROR;
+ }
+ else if (strcmp(status, "success") != 0 || str)
+ return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
+ _("Unexpected server response to authentication"));
+
+ *message = NULL;
+ return SVN_NO_ERROR;
+}
+
\ No newline at end of file
Index: libsvn_ra_svn/internal_auth.c
===================================================================
--- libsvn_ra_svn/internal_auth.c (revision 28868)
+++ libsvn_ra_svn/internal_auth.c (working copy)
@@ -73,8 +73,31 @@
 
   realmstring = apr_psprintf(pool, "%s %s", sess->realm_prefix, realm);
 
- if (sess->is_tunneled && find_mech(mechlist, "EXTERNAL"))
+ /* new mech BASIC*/
+ if (find_mech(mechlist, "BASIC"))
     {
+ SVN_ERR(svn_auth_first_credentials(&creds, &iterstate,
+ SVN_AUTH_CRED_SIMPLE, realmstring,
+ sess->callbacks->auth_baton, pool));
+ if (!creds)
+ return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
+ _("Can't get password"));
+
+ while (creds)
+ {
+ user = ((svn_auth_cred_simple_t *) creds)->username;
+ password = ((svn_auth_cred_simple_t *) creds)->password;
+ SVN_ERR(svn_ra_svn__auth_response(conn, pool, "BASIC", NULL));
+ /*SVN_ERR(svn_ra_svn__cram_client(conn, pool, user, password, &msg));*/
+ SVN_ERR(svn_ra_svn_basic_client(conn, pool, user, password, &msg));
+ if (!msg)
+ break;
+ SVN_ERR(svn_auth_next_credentials(&creds, iterstate, pool));
+ }
+ return SVN_NO_ERROR;
+ }
+ else if (sess->is_tunneled && find_mech(mechlist, "EXTERNAL"))
+ {
         /* Ask the server to use the tunnel connection environment (on
         * Unix, that means uid) to determine the authentication name. */
       SVN_ERR(svn_ra_svn__auth_response(conn, pool, "EXTERNAL", ""));
Index: libsvn_ra_svn/ra_svn.h
===================================================================
--- libsvn_ra_svn/ra_svn.h (revision 28868)
+++ libsvn_ra_svn/ra_svn.h (working copy)
@@ -116,6 +116,11 @@
                                      const char *user, const char *password,
                                      const char **message);
 
+/* Basic client implementation*/
+svn_error_t *svn_ra_svn_basic_client(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
+ const char *user, const char *password,
+ const char **message);
+
 /* Return an error chain based on @a params (which contains a
  * command response indicating failure). The error chain will be
  * in the same order as the errors indicated in @a params. Use
Index: svnserve/serve.c
===================================================================
--- svnserve/serve.c (revision 28868)
+++ svnserve/serve.c (working copy)
@@ -255,6 +255,10 @@
     SVN_ERR(svn_ra_svn_write_word(conn, pool, "EXTERNAL"));
   if (b->pwdb && get_access(b, AUTHENTICATED) >= required)
     SVN_ERR(svn_ra_svn_write_word(conn, pool, "CRAM-MD5"));
+ /* new mech BASIC */
+ if (get_access(b, AUTHENTICATED) >= required)
+ SVN_ERR(svn_ra_svn_write_word(conn, pool, "BASIC"));
+
   return SVN_NO_ERROR;
 }
 
@@ -351,6 +355,17 @@
       return SVN_NO_ERROR;
     }
 
+ /*new mech BASIC*/
+ if (get_access(b, AUTHENTICATED) >= required
+ && strcmp(mech, "BASIC") == 0)
+ {
+ SVN_ERR(svn_ra_svn_basic_server(conn, pool, b->cfg, &user, success));
+ b->user = apr_pstrdup(b->pool, user);
+ return SVN_NO_ERROR;
+ }
+
+
+
   return svn_ra_svn_write_tuple(conn, pool, "w(c)", "failure",
                                 "Must authenticate with listed mechanism");
 }

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe_at_subversion.tigris.org
For additional commands, e-mail: dev-help_at_subversion.tigris.org
Received on 2008-01-31 21:33:52 CET

This is an archived mail posted to the Subversion Dev mailing list.