I've been experimenting a bit, and LogonUser / LoadUserProfile during FBA
works too, except for one quirk: LogonUser fails with error 0x52F (account
restriction) if the password is blank.
That turned out to be easy: reset the 'blank password' policy (in the
registry) before calling LogonUser, restore the original value after the
work is done.
It works now: all I have to do to set a per-user shell is add an FBA
generic command that runs the exe I created with username, password
(optional) and shell path as arguments.
It could use more checking, testing, cleaning up and better argument
parsing, but if anyone is interested, the source follows below (as is, all
disclaimers you ever heard of apply plus a few more).
And watch out, it creates a log file with the password in plaintext in it.
It's a VS2003 standard win32 application.
Add #includes for stdio.h and userenv.h in stdafx.h, and add userenv.lib to
the linked libraries.
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
FILE *fLog;
char *cp1, *cp2;
char sUserName[MAX_NAME_LEN];
char sPassword[MAX_PASS_LEN];
char *sShell;
HANDLE hToken;
PROFILEINFO ProfileInfo;
HKEY hKey;
int l;
DWORD dw;
DWORD dwPasswordPolicy;
fLog = fopen("SetUserShell.log", "w");
fprintf(fLog, "SetUserShell started with command line {%s}\n",
lpCmdLine);
// Expected command line args: -u:UserName [-p

assword] -s:ShellPath
// in that order, default password = blank
__try {
cp1 = strstr(lpCmdLine, "-u:");
if(cp1 == NULL){
fprintf(fLog, "No user name specified\n");
__leave;
}
cp1 += 3;
cp2 = strchr(cp1, ' ');
if(cp2 == NULL){
fprintf(fLog, "No space after user name\n");
__leave;
}
l = cp2 - cp1;
if(l == 0 || l >= MAX_NAME_LEN){
fprintf(fLog, "User name length 0 or too long\n");
__leave;
}
strncpy(sUserName, cp1, l);
sUserName[l] = '\0';
cp1 = strstr(lpCmdLine, "-p:");
if(cp1){
cp1 += 3;
cp2 = strchr(cp1, ' ');
if(cp2 == NULL){
fprintf(fLog, "No space after password\n");
__leave;
}
l = cp2 - cp1;
if(l >= MAX_PASS_LEN){
fprintf(fLog, "Password too long\n");
__leave;
}
if(l) strncpy(sPassword, cp1, l);
sPassword[l] = '\0';
} else {
*sPassword = '\0';
}
sShell = strstr(lpCmdLine, "-s:");
if(sShell == NULL){
fprintf(fLog, "Shell not specified\n");
__leave;
}
sShell += 3;
// Clear blank password policy if necessary
dw = RegOpenKey(HKEY_LOCAL_MACHINE,
"System\\CurrentControlSet\\Control\\Lsa",&hKey);
if(dw != ERROR_SUCCESS){
fprintf(fLog, "RegOpenKey (Lsa) failed with 0x%08X\n", dw);
} else {
dwPasswordPolicy = 0;
dw = 4;
RegQueryValueEx(hKey, "limitblankpassworduse", 0, NULL, (BYTE
*)&dwPasswordPolicy, &dw);
if(dwPasswordPolicy){
dw = 0;
dw = RegSetValueEx(hKey, "limitblankpassworduse", 0, REG_DWORD,
(BYTE *)&dw, 4);
if(dw != ERROR_SUCCESS) fprintf(fLog, "RegSetValueEx (Lsa)
failed with 0x%08X on reset policy\n", dw);
}
RegCloseKey(hKey);
}
if(!LogonUser(sUserName, ".", sPassword, LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT, &hToken)){
fprintf(fLog, "LogonUser failed with 0x%08X\n", GetLastError());
__leave;
}
__try {
ZeroMemory(&ProfileInfo, sizeof(PROFILEINFO));
ProfileInfo.dwSize = sizeof(PROFILEINFO);
ProfileInfo.lpUserName = sUserName;
if(!LoadUserProfile(hToken, &ProfileInfo)){
fprintf(fLog, "LoadUserProfile failed with 0x%08X\n",
GetLastError());
__leave;
}
dw = RegOpenKeyEx((HKEY)ProfileInfo.hProfile,
"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\WinLogon", 0,
KEY_ALL_ACCESS, &hKey);
if(dw != ERROR_SUCCESS){
fprintf(fLog, "RegOpenKey failed with 0x%08X\n", dw);
__leave;
}
__try {
dw = RegSetValueEx(hKey, "Shell", 0, REG_SZ,(BYTE *)sShell,
strlen(sShell));
if(dw != ERROR_SUCCESS){
fprintf(fLog, "RegSetValue failed with 0x%08X\n", dw);
__leave;
}
fprintf(fLog, "Shell successfully set.\n");
}
__finally {
if(ERROR_SUCCESS != RegCloseKey(hKey)){
fprintf(fLog, "RegCloseKey failed with 0x%08X\n");
}
}
if(!UnloadUserProfile(hToken, ProfileInfo.hProfile)){
fprintf(fLog, "UnloadUserProfile failed with 0x%08X\n",
GetLastError());
}
}
__finally {
if(!CloseHandle(hToken)) fprintf(fLog, "CloseHandle failed with
0x%08X\n", GetLastError());
}
}
__finally {
// Reset blank password policy if necessary
if(dwPasswordPolicy){
RegOpenKey(HKEY_LOCAL_MACHINE,
"System\\CurrentControlSet\\Control\\Lsa",&hKey);
dw = RegSetValueEx(hKey, "limitblankpassworduse", 0, REG_DWORD,
(BYTE *)&dwPasswordPolicy, 4);
if(dw != ERROR_SUCCESS) fprintf(fLog, "RegSetValueEx (Lsa) failed
with 0x%08X on restore policy\n", dw);
RegCloseKey(hKey);
}
fclose(fLog);
}
return 0;
}