Custom shell

L

Lucvdv

I've created XPe targets with custom shells before, but now I need
different shells for different user accounts.

Is there a way to accomplish this?

Basically, I must set the Shell registry value in
HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\Winlogon for
one user account only, without affecting the same for other users.

It can't be done during FBA, because the user.dat file with that user's
HKCU branch doesn't exist until the user first logs on.
 
K

KM

Lucvdv,

You can always load the user reg.hive at run time (during FBA with Generic command, e.g.) with reg.exe (/LOAD) and add/remove/modify
any registry settings there.
The same can be accomplished with Registry APIs.
 
L

Lucvdv

KM said:
Lucvdv,

You can always load the user reg.hive at run time (during FBA with Generic command, e.g.) with reg.exe (/LOAD) and add/remove/modify
any registry settings there.
The same can be accomplished with Registry APIs.

The problem is that a user hive along with the rest of his profile is
created only the first time the user logs on.

The entire "Documents and Settings\{username}" subtree doesn't exist
before the first logon, and that's where the reg hive is stored in the
ntuser.dat and ntuser.dat.log files. Als long as those files don't
exist, there's nothing to load :(

It looks like Slobodan's reply means it can't be done until after FBA
(see the last paragraph of the "How it works" section in the article).

Reading your replies gave me an idea though: I could try using the
LoadUserProfile API in FBA, and see if it forces creation of the
profile. I'll let you know if it works.


Thanks to both for your help.
 
K

KM

Lucvdv,
The problem is that a user hive along with the rest of his profile is
created only the first time the user logs on.

I did mean that you should load the user hive during FBA when the user hive is already created.
You can force the logon of the user by using Autologon feature.

I did the switching between user accounts using Autologon on some of my images and set up all I needed during FBA.

KM
 
L

Lucvdv

Lucvdv,


I did mean that you should load the user hive during FBA when the user hive is already created.
You can force the logon of the user by using Autologon feature.

I did the switching between user accounts using Autologon on some of my images and set up all I needed during FBA.

KM

Thanks, I didn't know autologon can make FBA run under a user account (I
thought it ran as local system).

Did you also try to set a custom shell? The WinLogon reg key is normally
read-only for all but administrators, so that could be a problem.


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. Logging on interactively works, but
LogonUser with logon type LOGON32_LOGON_INTERACTIVE fails.

And because of Murphy (of the law), the account I have to set the shell for
has a blank password ;)


Otherwise it was easy: just call LogonUser with username and password, and
pass the returned token to LoadUserProfile. It will create the user
profile, and it returns an open handle to the user's reg hive in the
PROFILEINFO structure.
 
L

Lucvdv

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:password] -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;
}
 
K

KM

Lucvdv,
Thanks, I didn't know autologon can make FBA run under a user account (I
thought it ran as local system).

Hey.. I never said that :) The FBA does run under Local System.
It is just what to consider the FBA process. I always say during FBA until all the image auto setup steps are done, which may be not
perfectly correct saying. Basically, you have automolog in help after FBA to automate to create the end user environment as you
need.
Did you also try to set a custom shell? The WinLogon reg key is normally
read-only for all but administrators, so that could be a problem.

While you set up image (until you send the final image to the customer) you can have any limitted user account to access the
Winlogon key. E.g., all user account temporarily could be with admin priviledges. Then it is easy to change back just before the
cloning (or before sending the image to clients) with sc.exe or etc.
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.

This is on SP2, right?
 
K

KM

Sorry for previous reply. I didn't notice you already posted the right solution later.

You probably want to post your code to www.xpefiles.com. Community would definitely benefit from it.
Thanks!

--
Regards,
KM, BSquare Corp.

PS. Btw, I believe there is an FBA API call that does the same job of creating account profile. It is undocumented, though :-(

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:password] -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;
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top