Reputation: 6015
I'm running PHP, Apache, and Windows. I do not have a domain setup, so I would like my website's forms-based authentication to use the local user accounts database built in to Windows (I think it's called SAM).
I know that if Active Directory is setup, you can use the PHP LDAP module to connect and authenticate in your script, but without AD there is no LDAP. What is the equivalent for standalone machines?
Upvotes: 5
Views: 5126
Reputation: 6015
Building a PHP extension requires a pretty large investment in terms of hard disk space. Since I already have the GCC (MinGW) environment installed, I have decided to take the performance hit of launching a process from the PHP script. The source code is below.
// Usage: logonuser.exe /user username /password password [/domain domain]
// Exit code is 0 on logon success and 1 on failure.
#include <windows.h>
int main(int argc, char *argv[]) {
HANDLE r = 0;
char *user = 0;
char *password = 0;
char *domain = 0;
int i;
for(i = 1; i < argc; i++) {
if(!strcmp(argv[i], "/user")) {
if(i + 1 < argc) {
user = argv[i + 1];
i++;
}
} else if(!strcmp(argv[i], "/domain")) {
if(i + 1 < argc) {
domain = argv[i + 1];
i++;
}
} else if(!strcmp(argv[i], "/password")) {
if(i + 1 < argc) {
password = argv[i + 1];
i++;
}
}
}
if(user && password) {
LogonUser(user, domain, password, LOGON32_LOGON_BATCH, LOGON32_PROVIDER_DEFAULT, &r);
}
return r ? 0 : 1;
}
Next is the PHP source demonstrating its use.
if($_SERVER['REQUEST_METHOD'] == 'POST') {
if(isset($_REQUEST['user'], $_REQUEST['password'], $_REQUEST['domain'])) {
$failure = 1;
$user = $_REQUEST['user'];
$password = $_REQUEST['password'];
$domain = $_REQUEST['domain'];
if($user && $password) {
$cmd = "logonuser.exe /user " . escapeshellarg($user) . " /password " . escapeshellarg($password);
if($domain) $cmd .= " /domain " . escapeshellarg($domain);
system($cmd, $failure);
}
if($failure) {
echo("Incorrect credentials.");
} else {
echo("Correct credentials!");
}
}
}
?>
<form action="<?php echo(htmlentities($_SERVER['PHP_SELF'])); ?>" method="post">
Username: <input type="text" name="user" value="<?php echo(htmlentities($user)); ?>" /><br />
Password: <input type="password" name="password" value="" /><br />
Domain: <input type="text" name="domain" value="<?php echo(htmlentities($domain)); ?>" /><br />
<input type="submit" value="logon" />
</form>
Upvotes: 0
Reputation: 96159
I haven't found a simple solution either. There are examples using CreateObject and the WinNT ADSI provider. But eventually they all bump into
User authentication issues with the Active Directory Service Interfaces WinNT provider. I'm not 100% sure but I guess the WSH/network connect approach has the same problem.
According to How to validate user credentials on Microsoft operating systems you should use LogonUser or SSPI.
It also says
Therefore, if I were certain Win9x/2000 support isn't needed, I would write an extension that exposes LogonUser to php.LogonUser Win32 API does not require TCB privilege in Microsoft Windows Server 2003, however, for downlevel compatibility, this is still the best approach.On Windows XP, it is no longer required that a process have the SE_TCB_NAME privilege in order to call LogonUser. Therefore, the simplest method to validate a user's credentials on Windows XP, is to call the LogonUser API.
edit:
I've also tried to utilize System.Security.Principal.WindowsIdentity and php's com/.net extension. But the dotnet constructor doesn't seem to allow passing parameters to the objects constructor and my "experiment" to get the assembly (and with it CreateInstance()) from GetType() has failed with an "unknown zval" error.
Upvotes: 3
Reputation: 93348
Good Question!
I've given this some thought... and I can't think of a good solution. What I can think of is a horrible horrible hack that just might work. After seeing that no one has posted an answer to this question for nearly a day, I figured a bad, but working answer would be ok.
The SAM file is off limits while the system is running. There are some DLL Injection tricks which you may be able to get working but in the end you'll just end up with password hashes and you'd have to hash the user provided passwords to match against them anyway.
What you really want is something that tries to authenticate the user against the SAM file. I think you can do this by doing something like the following.
I know it's not pretty, but it should work.
I feel dirty :|
Edit: reason for invoking the external wsh script is that PHP doesn't allow you to use UNC paths (as far as I can remember).
Upvotes: 0