[[[ Add option cross-realm support to cyrus_auth.c. Adds three boolean configuration entries in the [sasl] section: enable-cross-realm: set to enable cross-realm support remove-local-realm: set to true to remove the local realm from a user name, false to keep the realm on a local user. Defaults to !enable-cross-realm remove-remote-realm: set to true to remove the realm of a remote user. This is a potential security hazard and should not be enabled unless you're confident all realms are trustworthy. ]]] Index: subversion/include/svn_config.h =================================================================== --- subversion/include/svn_config.h (revision 36738) +++ subversion/include/svn_config.h (working copy) @@ -126,6 +126,9 @@ #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_OPTION_ENABLE_CROSS_REALM "enable-cross-realm" +#define SVN_CONFIG_OPTION_REMOVE_LOCAL_REALM "remove-local-realm" +#define SVN_CONFIG_OPTION_REMOVE_REMOTE_REALM "remove-remote-realm" /* For repository password database */ #define SVN_CONFIG_SECTION_USERS "users" Index: subversion/svnserve/cyrus_auth.c =================================================================== --- subversion/svnserve/cyrus_auth.c (revision 36738) +++ subversion/svnserve/cyrus_auth.c (working copy) @@ -48,7 +48,7 @@ '\0'-terminated; we just need to set *OUT_LEN correctly. */ static int canonicalize_username(sasl_conn_t *conn, - void *context, /* not used */ + server_baton_t *b, /* set by cyrus_auth_request */ const char *in, /* the username */ unsigned inlen, /* its length */ unsigned flags, /* not used */ @@ -56,11 +56,15 @@ char *out, /* the output buffer */ unsigned out_max, unsigned *out_len) { + svn_boolean_t crossrealm_enabled; int realm_len = strlen(user_realm); char *pos; *out_len = inlen; + svn_config_get_server_setting_bool(b->cfg, &crossrealm_enabled, + SVN_CONFIG_SECTION_SASL, SVN_CONFIG_OPTION_ENABLE_CROSS_REALM, FALSE); + /* If the username contains an '@', the part after the '@' is the realm that the user wants to authenticate in. */ pos = memchr(in, '@', inlen); @@ -68,7 +72,7 @@ { /* The only valid realm is user_realm (i.e. the repository's realm). If the user gave us another realm, complain. */ - if (strncmp(pos+1, user_realm, inlen-(pos-in+1)) != 0) + if (!crossrealm_enabled && strncmp(pos+1, user_realm, inlen-(pos-in+1)) != 0) return SASL_BADPROT; } else @@ -91,6 +95,7 @@ return SASL_OK; } +static int callbackslen = 2; static sasl_callback_t callbacks[] = { { SASL_CB_CANON_USER, canonicalize_username, NULL }, @@ -249,6 +254,7 @@ sasl_security_properties_t secprops; svn_boolean_t success, no_anonymous; int mech_count, result = SASL_OK; + sasl_callback_t *my_callbacks; SVN_ERR(svn_ra_svn__get_addresses(&localaddrport, &remoteaddrport, conn, pool)); @@ -260,12 +266,23 @@ return svn_ra_svn_flush(conn, pool); } + /* Initialize my own callbacks with my context */ + my_callbacks = apr_palloc(pool, callbackslen*sizeof(sasl_callback_t)); + if (!my_callbacks) + { + svn_error_t *err = svn_error_wrap_apr(apr_err, _("Can't allocate memory")); + SVN_ERR(write_failure(conn, pool, &err)); + return svn_ra_svn_flush(conn, pool); + } + memcpy(my_callbacks, callbacks, callbackslen*sizeof(sasl_callback_t)); + my_callbacks[0].context = b; + /* Create a SASL context. SASL_SUCCESS_DATA tells SASL that the protocol supports sending data along with the final "success" message. */ result = sasl_server_new(SVN_RA_SVN_SASL_NAME, hostname, b->realm, localaddrport, remoteaddrport, - NULL, SASL_SUCCESS_DATA, + my_callbacks, SASL_SUCCESS_DATA, &sasl_ctx); if (result != SASL_OK) { @@ -347,6 +364,9 @@ if (no_anonymous) { + svn_boolean_t crossrealm_enabled; + svn_boolean_t remove_local_realm; + svn_boolean_t remove_remote_realm; char *p; const void *user; @@ -356,9 +376,28 @@ if (result != SASL_OK) return fail_cmd(conn, pool, sasl_ctx); + svn_config_get_server_setting_bool(b->cfg, &crossrealm_enabled, + SVN_CONFIG_SECTION_SASL, SVN_CONFIG_OPTION_ENABLE_CROSS_REALM, FALSE); + + svn_config_get_server_setting_bool(b->cfg, &remove_local_realm, + SVN_CONFIG_SECTION_SASL, SVN_CONFIG_OPTION_REMOVE_LOCAL_REALM, !crossrealm_enabled); + + svn_config_get_server_setting_bool(b->cfg, &remove_remote_realm, + SVN_CONFIG_SECTION_SASL, SVN_CONFIG_OPTION_REMOVE_REMOTE_REALM, FALSE); + if ((p = strchr(user, '@')) != NULL) - /* Drop the realm part. */ - b->user = apr_pstrndup(b->pool, user, p - (char *)user); + { + svn_boolean_t is_local_realm = !strcmp(p+1, b->realm); + svn_boolean_t remove_realm = + remove_local_realm && is_local_realm || + remove_remote_realm && !is_local_realm; + + if (remove_realm) + /* Drop the realm part. */ + b->user = apr_pstrndup(b->pool, user, p - (char *)user); + else + b->user = apr_pstrdup(b->pool, user); + } else { svn_error_t *err;