Browsing articles tagged with " windows"

WebDAV revisited

In my previous endeavour with WebDAV, I ended up using a complicated lighttpd/apache proxy system to get the permissions right. This did work well enough, but it had a few limitations. In involved modifying the registry on the client systems and it required a new lighty instance running for each share with different permissions to the last (which wasn’t terribly efficient). Additionally, lighty’s mod_webdav lacked support for a few vital operations which made working with the shares a bit difficult, not to mention there were some MIME-type conflicts between lighty/apache/the webDAV client that made working with certain types of files near impossible.

I have since revised some of the methods I’ve used to implement the WebDAV server. For a start, I managed to resolve the XP SP2/Vista/7 basic vs DIGEST authentication method hacks to not need registry editing – it turns out, by default, if the share is SSL encrypted, basic authentication should be fine. I also decided on a new approach. Using Apache, PHP5, suPHP and eZ Component’s WebDAV module, I was able to implement a sturdier WebDAV solutions that still solved the problems.

First things first, Apache, PHP5, mod_php, mod_suphp all needed to be installed and configured. Once that was done, ez Component’s WebDAV module needed to be installed – on Fedora, this is php-ezc-Webdav in the Yum repositories. Then I needed to make a couple of scripts to handle authentication and module loading. These are modified right from the ezc-Webdav documentation. These scripts need to go in Apache’s DocumentRoot.

autoload.php

<?php
require_once "/usr/share/pear/ezc/Base/base.php";

/**
 * Autoload ezc classes
 *
 * @param string $className
 */
function __autoload( $className )
{
    ezcBase::autoload( $className );
}
?>

You may need to alter the path to base.php above.

authenticator.php

<?php
class ldapAuthenticator extends ezcWebdavDigestAuthenticatorBase
                   implements ezcWebdavAuthorizer
{

    public function authenticateAnonymous( ezcWebdavAnonymousAuth $data )
    {
        return false;
    }

    public function authenticateBasic( ezcWebdavBasicAuth $data )
    {
        $username = $data->username;
        $password = $data->password;

	if(strstr($username, '\\')){
		$username = strstr($username, '\\');
		$username = preg_replace("^.", '', $username);
	}

	$ldap = ldap_connect("ldap.company.tld", 389);
	$bind = @ldap_bind($ldap);
	$search = ldap_search($ldap, "dc=company,dc=tld", "(&(objectClass=shadowAccount)(uid=".$username."))", array("dn"));
	$data = ldap_get_entries($ldap, $search);
	$dn = $data[0]['dn'];

	if(!$dn){ return false; }

	$bind = @ldap_bind($ldap, $dn, $password);
	$search = ldap_search($ldap, "dc=company,dc=tld", "(&(objectClass=shadowAccount)(uid=".$username."))", array("dn"));
	$data = ldap_get_entries($ldap, $search);

	if(!$bind){ return false; }

	return true;
    }

    public function authenticateDigest( ezcWebdavDigestAuth $data )
    {
        $username = $data->username;

	error_log("User attempted unsupported DIGEST authentication: ".$username);

	return false;
   } 

    public function authorize( $user, $path, $access = ezcWebdavAuthorizer::ACCESS_READ )
    {
        return true;
    }
}
?>

You will need to modify the authenticateBasic function. The one above is to authenticate with an LDAP backend.

sharename.php

<?php
require_once 'autoload.php';
require_once 'authenticator.php';

$server = ezcWebdavServer::getInstance();

$server->options->realm = "CCPG Solutions Ltd WebDAV";

$server->auth = new ldapAuthenticator();

$backend = new ezcWebdavFileBackend(
   '/path/to/share'
);

$server->handle( $backend );
?>

In this one, you will need to modify the share path (in $backend) and the realm.

Then you need to change the owning uid/gid (chown user:group sharename.php) of the sharename.php file to the user/group you want to use for read/write permissions of the files. You can make as many sharename.php files for each share (or combination of share/permissions) you need.

With all that done, you should be able to go ahead and map the share using the URL https://dav.company.tld/sharename.php

The eZ Component’s WebDAV module seems pretty in-depth – the above is really just a very basic usage of it’s capabilities, but it does work pretty well. The only other thing you may have to do, depending how pedantic your Windows boxes are going to be, is disable the DIGEST authentication headers altogether. To do this, edit /usr/share/pear/ezc/Webdav/servers.php (or wherever your Webdav/server.php is located) and comment the following lines in the createUnauthenticatedResponse function:

$wwwAuthHeader['digest'] = 'Digest realm="' .$this->options->realm . '"'
 . ', nonce="' . $this->getNonce() . '"'
 . ', algorithm="MD5"';

Save the file and exit. That’s all it takes. With all that done, your users should be able to map the share in Windows, authenticate via LDAP and read/write files with the permissions of the PHP script owner.

WebDAV is a … pain

[Titled edited ;P]

As a concept – WebDAV is great. File shares over HTTP – no fussy protocols to deal with, security and encryption built right in.

Problems. You want to use Apache as a WebDAV server with mod_dav. Theroetically, this is fine. In my circumstances, I wanted to use it for a few specific people to reach their /home/username folders, so I thought using Apache I could just export those directories as DAV shares, using LDAP authentication. I hit several problems with this.

First, permissions. Apache obviously runs as ‘nobody’ or ‘apache’ or ‘httpd’ depending on your system. This user doesn’t have permission to access your home directory, much less, write to it. And you don’t REALLY want your front-facing web server having that permission. I solved this by installing mod_proxy and mod_proxy_http, then running a tiny little lighttpd instance as the user(s) who wanted access to their home directories, with it’s own mod_webdav inserted. I had Apache pass connections to the webdav-server.domain/username vhost/folder alias through the proxy to the lighttpd server for that user, which was exporting that user’s home directory as a single share. I also used lighttpd’s own mod_auth to do the LDAP authentication for each user instead of Apache, because I didn’t want to chance someone getting around the front-facing proxy and connecting directly to one of the DAV servers, obviously. It is worth noting that you will want to set the document-root to either /home or /home/username for the lighty instance, as COPY and MOVE will not work if you don’t. Since authentication is required for the site anyway, it’s not a huge concern to set it to the user’s home directory. This setup seems to work pretty well.

Which brings us to the second problem. Windows XP SP2, Windows Vista and Windows 7 refuse to authenticate to a WebDAV server using BASIC as it’s authentication method (instead of DIGEST). If I were using password files, this would be no problem to change to DIGEST authentication using lighty’s mod_auth, but because I’m using LDAP as a backend, it’s not feasible. You can’t use DIGEST with LDAP because, with DIGEST, the password is never actually sent – just a hash to be compared, but the comparison hash will simply never match what’s in the LDAP database, so there’s no way to do the auth. Instead, the solution seems to be dealing with Windows. After lots and lots of Googling, I found a few registry hacks:

HKLM\SYSTEM\CurrentControlSet\services\WebClient\Parameters\UseBasicAuth
HKLM\SYSTEM\CurrentControlSet\services\WebClient\Parameters\BasicAuthLevel

If you set UseBasicAuth (a DWORD value) to 0×00000001 and BasicAuthLevel (also a DWORD value) to 0×00000002, you will be able to authenticate to the DAV server with BASIC authentication. The first parameter enabled BASIC authentication altogether. The latter has three possible values: 0, which disabled BASIC authentication, 1, which enabled it for SSL-secured sites and 2, the value I’m using above, to enable it for all sites. When you’re done, restart the webclient service. (In Vista/7, you can only do this from services.msc or by using an ELEVATED privileges command prompt – not a normal one.)

Now, I wanted to use this with Active Directory to set a user’s N-drive to their home directory. I know it would be easier to use Samba, but if a user is behind a vaguely-filtered internet connection, say Comcast, Virgin Media or Verizon Fios, you can’t actually hit a Samba share over the internet. And VPNs can be even more of a … pain than WebDAV. In theory, you can address a WebDAV share with a UNC path and it works (\\webdav-server.fqdn\sharename). It doesn’t, however, seem to work when you set that UNC path to a drive letter in someone’s AD account. Which sucks.

So I thought, ooh – I know! Map it with a logon script! DERP. Logon scripts have to run from a Samba share. Which we can’t get to. Hence using WebDAV. The solution, if I say so myself, is pretty creative, if not a little over-the-top. I created a folder, say, C:\logon. In there, there is ‘wget.exe’, a Windows binary-only version of the ‘wget’ tool for Linux, which is a simple command-line based HTTP/FTP downloader. Then there’s a file called loader. bat. This file fetches a logon.bat from a web server, and runs it. Then, in their Active Directory profile, I set their logon script to \\localhost\c$\logon\loader.bat – causing them, each time they logon, to download the latest logon.bat and run it. In logon.bat, I use wget again to download a little .exe file written in AutoIT that checks if the registry has already been updated to connect to WebDAV shares using BASIC authentication as above, and if not, requests administrative privileges to update the registry and then restart the webclient service for you. After all this happens, it finally uses ‘net use’ to map the WebDAV shares (the lighty one from earlier as well as an additional SharePoint share) to their relevant drive letters. Scripts below.

The wget.exe I’m using comes from: http://pages.interlog.com/~tcharron/wgetwin.html

loader.bat

@echo off
cd C:\logon
del c:\logon\logon.bat
C:\logon\wget http://some-http-server.fqdn/logon/logon.bat
C:\logon\logon.bat

logon.bat

@echo off
cd C:\logon
del C:\logon\dav-setup.exe
C:\logon\wget http://some-http-server.fqdn/logon/dav-setup.exe
C:\logon\dav-setup.exe

net use N: "http://apache-proxy-to-lighty.fqdn/sharename" /user:username /persistent:yes /savecred
net use T: "http://sharepoint.fqdn/sharename" /user:domain\username /persistent:yes /savecred

dav-setup.au3

#RequireAdmin

$useBasicAuth = RegRead("HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\WebClient\Parameters", "UseBasicAuth")
$basicAuthLevel = RegRead("HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\WebClient\Parameters", "BasicAuthLevel")

$restartService = 0

if $useBasicAuth <> 1 Then
    RegWrite("HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\WebClient\Parameters", "UseBasicAuth", "REG_DWORD", "0x00000001")
    $restartService = 1
EndIf

if $basicAuthLevel <> 2 Then
    RegWrite("HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\WebClient\Parameters", "BasicAuthLevel", "REG_DWORD", "0x00000002")
    $restartService = 1
EndIf

if $restartService = 1 Then
    RunWait(@WindowsDir & "\System32\net stop webclient")
    RunWait(@WindowsDir & "\System32\net start webclient")
EndIf