Saturday, September 27, 2008

Installing Subversion on Windows Server with Apache HTTP Server, SSL, Active Directory Authentication and Log Rotation

Necessary Software

First you will need to get hold of the software itself.
  • Subversion can be downloaded at This is the direct link to the installation packages. This guide assumes you download the Subversion binaries for Win32 as zip file and not as installer (at the time of writing the appropriate package is name

  • Since we want to use SSL for communication with the subversion repository we need a Win32 binary of Apache HTTP server that has SSL support compiled in. This site has them. At the time of writing the latest 2.2.x release available is The Microsoft Visual C++ 2008 Redistributable Package (x86) is needed to run the Apache binaries.
    Note that for Apache 2.0.x you will need a different svn distribution since the Subversion Apache modules need to be linked to a specific Apache version.



Subversion is installed by simply unzipping the contents of the zip file (see above) to a directory of your choice. For this guide the directory c:\Program Files (x86)\Subversion is assumed to be the installation directory. A directory name that does not include the version number has the benefit that a new version of Subversion can easily be installed by overwriting the older version files.

In order to use the Subversion executables from the command line (like svnadmin for administering a repository) it is recommended to not add the c:\Program Files (x86)\Subversion\bin directory to the computer's PATH environment variable. The problem that might occur if you do so is that DDLs like ssleay32.dll and libeay32.dll that are part of the Subversion distribution might conflict with other versions of these libraries installed by other applications.

Apache HTTP Server

The Apache HTTP Server is installed by unzipping the contents of the zip file to a directory of your choice. For this guide the directory c:\Program Files (x86)\Apache2 is assumed to be the installation directory. Again, upgrading can be done by replacing the files in that directory.


Apache HTTP Server

The official Apache HTTP Server documentation can be found here . The platform specific notes for Windows are especially interesting for this guide.

Basic Setup

Apache uses config files found in the conf subdirectory of the Apache installation. For the purpose of this guide only the httpd.conf, http-ssl.conf and http-dav.conf files are of interest.

The first thing to do is to adjust the path directives in httpd.conf. By default, all paths point to c:/Apache2. If you decided to install into this directory you can skip this step. Otherwise all paths directives to c:/Apache2 need to be replaced with c:/Program Files (x86)/Apache2 . Notice that the original config file uses forward slashes as path separators. Apache understands both forward slashes and backslashes but the rotatelogs program used for log rotation only recognizes the forward slashes as path separator so using them everywhere will yield a more uniform configuration file. There should be nine places (not including the comments) where the path needs to be changed.

Next, the Listen 80 directive should be changed to the port you want the server to listen to for non SSL requests. For this guide it is changed to 12080.

The following directives should also be adjusted to make sense for your installation: ServerAdmin and ServerName.

Integrating Subversion

Subversion is integrated into the Apache HTTP Server using two modules that are part of the Subversion distribution. The two modules need to be enabled in the httpd.conf file. Add the
following two lines to the Dynamic Shared Object (DSO) Support section:

LoadModule dav_svn_module "c:/Program Files (x86)/Subversion/bin/"
LoadModule authz_svn_module "c:/Program Files (x86)/Subversion/bin/"
These two modules need the DAV module that comes with Apache HTTP Server. Uncomment the following line:
LoadModule dav_module modules/
Next, an URL needs to be defines to access the repositories. This is done using the following Location directive:

DAV svn
SVNListParentPath on
SVNParentPath "c:/svn"

Details about the Subversion-specific directives can be found in the subversion book . SVNParentPath points to the location of the repositories to expose using the /svn URL.

Enabling SSL

Configure SSL Modules
Uncomment the following line in the Dynamic Shared Object (DSO) Support section:
LoadModule ssl_module modules/
Generating a Self-Sign Certificate

A certificate is needed for SSL communication. It can either be a certificate issued by a trusted Certification Authority (CA) or one created yourself (a so called self-signed certificate). This guide assumes that a self-signed certificate is to be used and thus must first be generated.

The following command executed in the bin directory of the Apache HTTP server installation directory will generate a certificate named server.crt that is valid for ten years (3650 days).

openssl req -new -x509 -nodes -days 3650 -out server.crt -keyout server.key -config ../conf/openssl.cnf

When you run the command from a command window you will be prompted for details about the certificate like name of your organization, country and so on. This information will be stored into the certificate and later be available to the clients accessing the Apache HTTP Server using https/SSL. Make sure you specify the same name as CN (common name) when you are generating the keys as the one in ServerName in the config files. Otherwise, Apache will not start with SSL enabled.

The process also generates a private key file called server.key which is the private key used to sign the certificate created. OS-level access rights should be used to restrict access to the file.

Enabling SSL Mode

All SSL relevant settings are stored in httpd-ssl.conf. This file needs to be adjusted next. Again, the path to the actual installation needs to be changed like the httpd.conf file before.

The next thing to adjust is the Listen 443 directive. Change it to Listen 12443 to use 12443 as the SSL port (of course you may use different values for your setup).

The SSL Virtual Host Context section needs to be adjusted to match your installation. Like you did before in httpd.conf change the ServerName and ServerAdmin directives. The default port number of 443 also needs to be changed to 12443 everywhere.

The certificate created before is referenced in http-ssl.conf using the directives SSLCertificateFile and SSLCertificateKeyFile. Change it to point to the files created before (move them from the bin to the conf directory before):

SSLCertificateFile conf/server.crt
SSLCertificateKeyFile conf/server.key

Now, uncomment the following line in httpd.conf:

Include conf/extra/httpd-ssl.conf

The last step is to tell Apache to use the SLL mode. This requires passing the -D SSL start parameter when starting the server. See how this is done below.

Configuring Log File Rotation

Log files should not grow infinitely. The rotatelogs.exe program that comes bundled with the Apache HTTP Server can be used to implement log rotation. The relevant directive are found in httpd.conf and httpd-ssl.conf.

Replace the following directives in httpd.conf :
ErrorLog "logs/error.log"
with this one
ErrorLog '|"C:/Program Files (x86)/Apache2/bin/rotatelogs.exe" "C:/windows/system32/LogFiles/Apache2/error_%Y-%m-%d-%H_%M_%S.log" 10M'

CustomLog "logs/access.log" common
with this one
CustomLog '|"C:/Program Files (x86)/Apache2/bin/rotatelogs.exe" "C:/windows/system32/LogFiles/Apache2/access_%Y-%m-%d-%H_%M_%S.log" 10M' common

Replace the following directives in httpd-ssl.conf in the SSL Virtual Host Context section:
ErrorLog "logs/error.log"
with this one
ErrorLog '|"C:/Program Files (x86)/Apache2/bin/rotatelogs.exe" "C:/windows/system32/LogFiles/Apache2/ssl_error_%Y-%m-%d-%H_%M_%S.log" 10M'

CustomLog logs/ssl_request_log \
"%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"
with this one
CustomLog '|"C:/Program Files (x86)/Apache2/bin/rotatelogs.exe" "C:/windows/system32/LogFiles/Apache2/ssl-request_%Y-%m-%d-%H_%M_%S.log" 10M' \
"%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"

TransferLog logs/access_log
with this one
TransferLog '|"C:/Program Files (x86)/Apache2/bin/rotatelogs.exe" "C:/windows/system32/LogFiles/Apache2/ssl_access_%Y-%m-%d-%H_%M_%S.log" 10M'

Authentication using Active Directory

To use Active Directory for authentication when a request is made to access a Subversion repository it is necessary to add the following two lines to load the necessary modules:

LoadModule ldap_module modules/
LoadModule authnz_ldap_module modules/

The Location directive added above needs to be augmented by the following lines:
Require valid-user
AuthType Basic
AuthBasicProvider ldap
AuthName "Subversion repositories"
AuthLDAPBindDN CN=LDAP,CN=Users,DC=somedomain,DC=com
AuthLDAPBindPassword xxxx
AuthLDAPURL "ldap://,dc=somedomain,dc=com?sAMAccountName?sub?(objectClass=*)"
AuthzSVNAccessFile c:/svn/svn-access.txt

so that it looks like this:

DAV svn
SVNListParentPath on
SVNParentPath c:/svn
Require valid-user
AuthType Basic
AuthBasicProvider ldap
AuthName "Subversion repositories"
AuthLDAPBindDN CN=LDAP,CN=Users,DC=somedomain,DC=com
AuthLDAPBindPassword xxxx
AuthLDAPURL "ldap://,dc=somedomain,dc=com?sAMAccountName?sub?(objectClass=*)"
AuthzSVNAccessFile c:/svn/svn-access.txt

The LDAP user (AuthLDAPBindDN) and the associated password are necessary in case Active Directory is not configured for anonymous queries.

For authorization via Active Directory take a look at PTEROPUS' blog.

WebDav for File Upload

When there a lot of files to add to the repository it makes sense to do that directly on the server because it is much faster. For this the files to add need to be available on the server. One easy way to provide a file upload possibility for the users that want something imported into the repository by an admin on the server is to provide a WebDav share. Users can then connect to this share using clients that are built-into many operating systems (such as Web Folders on Windows).

To setup WebDav uncomment these two line in httpd.conf:

LoadModule dav_fs_module modules/

Include conf/extra/httpd-dav.conf
and add the following lines to file httpd-dav.conf:

Alias /svnupload c:/svnupload

DAV on
Options Indexes
AllowOverride None
Order deny,allow
Allow from all
Require valid-user
AuthType Basic
AuthBasicProvider ldap
AuthName "Subversion Repository Upload"
AuthLDAPBindDN CN=LDAP,CN=Users,DC=somedomain,DC=com
AuthLDAPBindPassword xxxx
AuthLDAPURL "ldap://
This will make the directory c:\svnupload accessible for uploading files using WebDav. The httpd-dav.conf file already contains a sample configuration called uploads which can be removed or commented out.

After the config files have been adjusted it is time to setup the server to run as a service. First, the sytnax of the config files need to be checked by running the command from the Apache bin directory.:

httpd.exe -t

If there are errors reported Apache will not be able to start.

Running Apache as a Windows Service

After the config files have been adjusted it is time to setup the server to run as a service. This can be acomplished by running the following command:
httpd -k install -n "Apache for Subversion"
See the platform specific notes for Windows for more options on installing the service.

This will create a new key in the Windows registry under HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\ApacheforSubversion. A string value named ImagePath will contain the following value: "C:\Program Files (x86)\Apache2\bin\httpd.exe" -k runservice

Use the registry editor to add -D SSL at the end of this value so that is will look like this: "C:\Program Files (x86)\Apache2\bin\httpd.exe" -k runservice -D SSL to enable the SSL mode.

User Account Setup

By default the Apache HTTPS Server runs under the Local System account when installed as a service. For security reasons it should run using a less privileged account. Use the Windows specific tools to create a new account for the server and the Services console to change the account under which the server is run.


Create a repository at the location pointed to before if non exists yet by running:

svnadmin create c:\svn\test

from the Subversion bin directory. This will create a repository test which you should now be able to access via http://localhost:12080/svn/test and https://localhost:12443/svn/test

Saturday, August 30, 2008

Fixing attachment download with Active Sync / Air Sync in Windows Mobile 6.1 and Exchange Server

I recently bought myself a new smartphone - an XDA Diamond - which runs Windows Mobile 6.1.

After setting up Outlook Mobile to sync with an Exchange server I noticed that attachments in email would not download to the device. I searched google because I thought there must be many people having this problem but actually found only two of them.

I thought that there must be a difference in the way Active Sync works between Windows Mobile 6.0 and 6.1 because syncing with my old MDA Varion III worked without any problems. So I analyzed the Active Sync protocol and found out that there actually is a difference regarding the way URLs are used.

Let me show you what I mean.

This is a request from my MDA Vario III as recorded in the HTTP server log:

2008-07-19 13:44:11 W3SVC1 EMELIE POST
/Microsoft-Server-ActiveSync User=yyy&DeviceId=yyy
0A0C0D0SP:1C2I739S232418R0S0L0H0P 443 HOME\yyy HTTP/1.1 MSFT-PPC/5.1.2200 - - 200 0 0 176995 546 1312

And this is the same request from my XDA Diamond:

2008-07-21 21:24:24 W3SVC1 EMELIE POST
/Microsoft-Server-ActiveSync User=yyy&DeviceId=yyy
0A0C0D0SP:3C2I739S1902R0S0L0H0P 443 HOME\yyy HTTP/1.1 MSFT-PPC/5.2.1301 - - 500 0 0 278 539 15

The difference is that the request URL sent from the WM6.1 device contains URL-encoded URLs. The slash (/) is encoded as %2F. The log entry also shows that the HTTP status is 500 (Internal Server Error) for the URL-encoded request.

So what's the problem? I don't see why it should not be legal to URL-encode the request like it is done in WM6.1. If it were Microsoft would probably not have implemented it this way:) and a lot of people would have the same problem
So, I figured it must be something special on the IIS (the MS HTTP server) that causes this. This was confirmed by this post. It looked like the Apache Jakarta ISAPI Redirector filter (JK) would cause the problem. So I removed it from the Web site and suddenly I was able to download attachments with WM6.1!

This was just a workaround of course since the ISAPI filter was installed on purpose. So I needed to find out what was causing the problem inside JK. The first thing I tried was looking for a newer version of JK. I downloaded version 1.2.26 but this did not improve things. After searching around a bit I dsicovered that there was a second Apache project called JK2 (version 2). I got that one and tried to configure it without any success. Later, I realized that the JK2 project is no longer being developed and JK (the first version) is still being developed. So using JK2 was not an option for me.

Back to JK, I turned on tracing for the ISAPI filter and noticed the following entry in the trace file for the Active Sync request responsible for the downloading of the attachement:

[Fri Aug 29 23:59:33.326 2008] [1004:4836] [debug]
jk_isapi_plugin.c (1199): Filter started
[Fri Aug 29 23:59:33.326 2008] [1004:4836] [emerg]
jk_isapi_plugin.c (1234): [/exchange/
/Inbox/attachment name.EML/1_multipart_xF8FF_2_image.jpg]
contains forbidden escape sequences.
So it turned out that JK actually does not like the way the HTTP request is encoded.
In order to fix this I downloaded the source code for JK and searched for the place where this message is written to the log. It is inside the callback method that is being called on the ISAPI filter for each request:

DWORD dwNotificationType, LPVOID pvNotification)
rc = unescape_url(uri);
if (rc == BAD_REQUEST) {
jk_log(logger, JK_LOG_ERROR,
"[%s] contains one or more invalid escape sequences.",
write_error_response(pfc, "400 Bad Request",
else if (rc == BAD_PATH) {
jk_log(logger, JK_LOG_EMERG,
"[%s] contains forbidden escape sequences.",
write_error_response(pfc, "404 Not Found",

As you can see this happens when the function unescape_url returns BAD_PATH which happens when the decoded character is a slash:

static int unescape_url(char *url)
register int x, y, badesc, badpath;

badesc = 0;
badpath = 0;
for (x = 0, y = 0; url[y]; ++x, ++y) {
if (url[y] != '%')
url[x] = url[y];
else {
if (!isxdigit(url[y + 1]) || !isxdigit(url[y + 2])) {
badesc = 1;
url[x] = '%';
else {
url[x] = x2c(&url[y + 1]);
y += 2;
if (url[x] == '/' || url[x] == '\0')
badpath = 1;
url[x] = '\0';
if (badesc)
else if (badpath)
    return BAD_PATH;
return 0;

static char x2c(const char *what)
register char digit;

digit =
((what[0] >= 'A') ? ((what[0] & 0xdf) - 'A') + 10 : (what[0] - '0'));
digit *= 16;
digit +=
(what[1] >= 'A' ? ((what[1] & 0xdf) - 'A') + 10 : (what[1] - '0'));
return (digit);

What to do now? One possibility would be to change the code and rebuild JK from scratch. To do this I would need Visual C++ and probably some time to get it all worked out. Another way to fix this is to patch the JK DLL file so that HttpFilterProc does not enter the path of execution that causes the problem. This is exactly what I did.

I disassembled the DDL and found the place to patch it:

.text:6A6BAB0B loc_6A6BAB0B:                           ; CODE XREF: HttpFilterProc+22A j
.text:6A6BAB0B                 lea     edx, [esp+39A8h+Str1]
.text:6A6BAB12                 push    edx
.text:6A6BAB13                 call    sub_6A6BB490
.text:6A6BAB18                 add     esp, 4
.text:6A6BAB1B                 cmp     eax, 0FFFFFFFFh
.text:6A6BAB1E                 jnz     short loc_6A6BAB47
.text:6A6BAB20                 mov     ecx, Time
.text:6A6BAB26                 lea     eax, [esp+39A8h+Str1]
.text:6A6BAB2D                 push    eax
.text:6A6BAB2E                 push    offset aSContainsOneOrMoreInvalidEscapeSequences_ ; "[%s] contains one or more invalid escap"...
.text:6A6BAB33                 push    4
.text:6A6BAB35                 push    0
.text:6A6BAB37                 push    4CAh
.text:6A6BAB3C                 push    offset aJk_isapi_plugin_c ; "jk_isapi_plugin.c"
.text:6A6BAB41                 push    ecx
.text:6A6BAB42                 jmp     loc_6A6BB000
.text:6A6BAB47 ; ---------------------------------------------------------------------------
.text:6A6BAB47 loc_6A6BAB47:                           ; CODE XREF: HttpFilterProc+26E j
.text:6A6BAB47                 cmp     eax, 0FFFFFFFEh
.text:6A6BAB4A                 jmp     short loc_6A6BAB97
.text:6A6BAB4C ; ---------------------------------------------------------------------------
.text:6A6BAB4C                 mov     eax, Time
.text:6A6BAB51                 lea     edx, [esp+39A8h+Str1]
.text:6A6BAB58                 push    edx             ; ArgList
.text:6A6BAB59                 push    offset aSContainsForbiddenEscapeSequences_ ; "[%s] contains forbidden escape sequence"...
.text:6A6BAB5E                 push    5               ; int
.text:6A6BAB60                 push    0               ; Source
.text:6A6BAB62                 push    4D2h            ; int
.text:6A6BAB67                 push    offset aJk_isapi_plugin_c ; "jk_isapi_plugin.c"
.text:6A6BAB6C                 push    eax             ; Time
.text:6A6BAB6D                 call    sub_6A6CEDF0
.text:6A6BAB72                 push    offset aDoctypeHtmlPublicW3cDtdHtml4_0TransitionalEn ; 
I replaced the original conditional jump instruction with an unconditional one (jmp) so that the problematic code is never executed.

I hear that this is a problem for people who have a Citrix License Server installed on their IIS along with an Exchange (Front-End) server because Citrix uses Apache Tomcat and thus requires the functionallity of JK. Of course all other installation that use JK to redirect to an Apache Tomcat engine are affected.

I will see if I find time to discuss this on the JK developers mailing list because I believe the best way to solve this is to change the source code and release a new version of JK. Of course, it needs to be discussed why the developers chose to reject request URls with encoded slashes inside first. Unfortunaltley, the code does not give any hints.

So what can you do until this is hopefully fixed for real?

Well, you could get the source code yourself, make the necessary change and rebuild the DLL or just patch it like I did.

For the DLL version 1.2.26 (32bit) the offset to patch and the bytes to change are as follows:
0000AB4A: 75 EB
You can also download the patched version that I made here.

Wow, this post got pretty long for the first one...

Please leave a comment if you find this useful.

Newgroup Discussion
Thread on German PPC Forum PPC-Welt

Information in this post or the patched DLL must not be used commercially without mentioning this blog post.