jpetter
asked on
Programmatically Create a Window's User Profile
I have written a utility program that we are using to help migrate users from our old domain controllers over to active directory. One of the pieces of this utility creates their new profile on their machine, and then migrates all of their settings from their previous profile over to their new one. I used Microsoft's KB article 196070 along with the platform SDK as my "go-by", and it seemed to work very well. That is it did until I tested it on a Win2k SP4 machine. When testing it on that system (Win2k SP4), LogonUser returns 1314 which indicates that a required privilege is not held by the client. So, what I don't understand is: why does this work on XP (SP1 and SP2) without any error, but on 2K it throws the 1314 error?
If anyone has run across this, or has any ideas, I would greatly appreciate any and all help.
Thanks,
Jeff
btw, I've pasted my code below fwiw.
////////////////////////// ////////// ////////// ////////// ///////
// CreateProfile
////////////////////////// ////////// ////////// ////////// ///////
bool CreateProfile (LPSTR lpUser, LPSTR lpPwd)
{
// DWORD dwError;
HRESULT hr = S_OK;
HANDLE hToken;
PROFILEINFO pi;
TCHAR errorMsg[256];
DWORD cchPath = 1024;
TCHAR szUserName[20];
bool bReturn = false;
// Set USERENV.DLL function pointers
if ( !InitUserEnv() )
{
_stprintf(errorMsg, TEXT("Failed to set USERENV.DLL function pointers: %d"), GetLastError());
MessageBox(NULL, errorMsg, TEXT("CreateProfile"), MB_OK);
bReturn = false;;
}
// Set USERENV.DLL function pointers
if ( !InitUserEnv() )
{
_stprintf(errorMsg, TEXT("Failed to set USERENV.DLL function pointers: %d"), GetLastError());
MessageBox(NULL, errorMsg, TEXT("CreateProfile"), MB_OK);
return false;
}
lstrcpy( szUserName, lpUser );
if ( !LogonUser(
szUserName, // user name
"US1", // domain or server
lpPwd, // password
LOGON32_LOGON_INTERACTIVE, // type of logon operation
LOGON32_PROVIDER_DEFAULT, // logon provider
&hToken ) ) // pointer to token handle
{
char szError[128];
wsprintf (szError, "LogonUser falied with: %d", GetLastError());
MessageBox (NULL, szError, "_processWkstnInfo error", MB_OK);
bReturn = false;
}
else
{
// Set up the PROFILEINFO structure that will be used to load the
// new user's profile
bReturn = true;
ZeroMemory( &pi, sizeof(pi) );
pi.dwSize = sizeof(pi);
pi.lpUserName = (LPTSTR)szUserName;
pi.dwFlags = PI_NOUI;
// Load the profile. Since it doesn't exist, it will be created
if ( !LoadUserProfile( hToken, // token for the user
&pi ) ) // pointer to PROFILEINFO structure
{
_stprintf(errorMsg, TEXT("LoadUserProfile() failed. Error: %d"), GetLastError());
MessageBox(NULL, errorMsg, TEXT("CreateProfile"), MB_OK);
bReturn = false;
}
else
{
// Unload the profile when it is no longer needed
if ( !UnloadUserProfile(hToken, // token for the user
pi.hProfile ) ) // registry key handle
{
_stprintf(errorMsg, TEXT("UnloadUserProfile() failed. Error: %d"), GetLastError());
MessageBox(NULL, errorMsg, TEXT("CreateProfile"), MB_OK);
bReturn = false;
}
// Release USERENV.DLL
if ( g_hUserEnvLib )
{
FreeLibrary( g_hUserEnvLib );
}
}
}
return bReturn;
}
If anyone has run across this, or has any ideas, I would greatly appreciate any and all help.
Thanks,
Jeff
btw, I've pasted my code below fwiw.
//////////////////////////
// CreateProfile
//////////////////////////
bool CreateProfile (LPSTR lpUser, LPSTR lpPwd)
{
// DWORD dwError;
HRESULT hr = S_OK;
HANDLE hToken;
PROFILEINFO pi;
TCHAR errorMsg[256];
DWORD cchPath = 1024;
TCHAR szUserName[20];
bool bReturn = false;
// Set USERENV.DLL function pointers
if ( !InitUserEnv() )
{
_stprintf(errorMsg, TEXT("Failed to set USERENV.DLL function pointers: %d"), GetLastError());
MessageBox(NULL, errorMsg, TEXT("CreateProfile"), MB_OK);
bReturn = false;;
}
// Set USERENV.DLL function pointers
if ( !InitUserEnv() )
{
_stprintf(errorMsg, TEXT("Failed to set USERENV.DLL function pointers: %d"), GetLastError());
MessageBox(NULL, errorMsg, TEXT("CreateProfile"), MB_OK);
return false;
}
lstrcpy( szUserName, lpUser );
if ( !LogonUser(
szUserName, // user name
"US1", // domain or server
lpPwd, // password
LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT, // logon provider
&hToken ) ) // pointer to token handle
{
char szError[128];
wsprintf (szError, "LogonUser falied with: %d", GetLastError());
MessageBox (NULL, szError, "_processWkstnInfo error", MB_OK);
bReturn = false;
}
else
{
// Set up the PROFILEINFO structure that will be used to load the
// new user's profile
bReturn = true;
ZeroMemory( &pi, sizeof(pi) );
pi.dwSize = sizeof(pi);
pi.lpUserName = (LPTSTR)szUserName;
pi.dwFlags = PI_NOUI;
// Load the profile. Since it doesn't exist, it will be created
if ( !LoadUserProfile( hToken, // token for the user
&pi ) ) // pointer to PROFILEINFO structure
{
_stprintf(errorMsg, TEXT("LoadUserProfile() failed. Error: %d"), GetLastError());
MessageBox(NULL, errorMsg, TEXT("CreateProfile"), MB_OK);
bReturn = false;
}
else
{
// Unload the profile when it is no longer needed
if ( !UnloadUserProfile(hToken,
pi.hProfile ) ) // registry key handle
{
_stprintf(errorMsg, TEXT("UnloadUserProfile() failed. Error: %d"), GetLastError());
MessageBox(NULL, errorMsg, TEXT("CreateProfile"), MB_OK);
bReturn = false;
}
// Release USERENV.DLL
if ( g_hUserEnvLib )
{
FreeLibrary( g_hUserEnvLib );
}
}
}
return bReturn;
}
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
jkr,
After exchanging a number of emails with our Name Admin group, I was able to confirm what you suspected: the account does not hold the required privilege. I think what really threw me is that even though it lacks the SE_TCB_NAME privilege, it works fine on XP (both service packs).
One more question if I may. My intent here is to create the user's profile. Do you know whether I can create a profile with CreateProcessWithLogonW if I pass the value LOGON_WITH_PROFILE for the dwLogonFlags parameter? The documentation states that while time consuming, it will load the user's profile. But it doesn't state whether it will create one if none exists. I was thinking that among my options for creating the profile, I have: (1) wait for the Name Admin team to add the privilege - could take a while due to the politics involved in breaking a corporate policy. (modifying domain level policies) (2) Call some small innocuous program CreateProcessWithLogonW, and passing the LOGON_WITH_PROFILE - if it works. (3) using a service so I can makes the calls in the local system context.
Thanks,
Jeff
After exchanging a number of emails with our Name Admin group, I was able to confirm what you suspected: the account does not hold the required privilege. I think what really threw me is that even though it lacks the SE_TCB_NAME privilege, it works fine on XP (both service packs).
One more question if I may. My intent here is to create the user's profile. Do you know whether I can create a profile with CreateProcessWithLogonW if I pass the value LOGON_WITH_PROFILE for the dwLogonFlags parameter? The documentation states that while time consuming, it will load the user's profile. But it doesn't state whether it will create one if none exists. I was thinking that among my options for creating the profile, I have: (1) wait for the Name Admin team to add the privilege - could take a while due to the politics involved in breaking a corporate policy. (modifying domain level policies) (2) Call some small innocuous program CreateProcessWithLogonW, and passing the LOGON_WITH_PROFILE - if it works. (3) using a service so I can makes the calls in the local system context.
Thanks,
Jeff
>>Do you know whether I can create a profile with CreateProcessWithLogonW if I pass the value
>>LOGON_WITH_PROFILE for the dwLogonFlags parameter?
Actually, I don't know whether you can do that, never tried it. But, since the docs (http://msdn.microsoft.com/library/en-us/dllproc/base/createprocesswithlogonw.asp) state "It is your responsibility to load the user registry hive into HKEY_USERS before calling CreateProcessWithLogonW, by using LOGON_WITH_PROFILE, or by calling the LoadUserProfile function.", I doubt that you'll be able to achieve the desired result. Also, using a service might not work, since the "SeTcbPrivilege" is not granted to anyone by default...
>>LOGON_WITH_PROFILE for the dwLogonFlags parameter?
Actually, I don't know whether you can do that, never tried it. But, since the docs (http://msdn.microsoft.com/library/en-us/dllproc/base/createprocesswithlogonw.asp) state "It is your responsibility to load the user registry hive into HKEY_USERS before calling CreateProcessWithLogonW, by using LOGON_WITH_PROFILE, or by calling the LoadUserProfile function.", I doubt that you'll be able to achieve the desired result. Also, using a service might not work, since the "SeTcbPrivilege" is not granted to anyone by default...
ASKER
So, I did an end around your question. It was a concern of mine, I was surprised that it would work on XP, but not on 2K - both of which use the same account - same code. But, I should know the particulars of the account soon.
Thanks,
Jeff