NT4/private/net/portuas/uasread.c

1308 lines
34 KiB
C
Raw Normal View History

2001-01-01 00:00:00 +01:00
/*++
Copyright (c) 1991-1993 Microsoft Corporation
Module Name:
UASREAD.C
Abstract:
Routines to _read UAS database entries.
Author:
Shanku Niyogi (W-SHANKN) 24-Oct-1991
Revision History:
24-Oct-1991 w-shankn
Created.
07-Feb-1992 JohnRo
Fixed compile errors!
Fixed memory free bug (hAlloc was both local and global variable).
Made changes suggested by PC-LINT.
28-Feb-1992 JohnRo
User info must include units_per_week field.
28-Feb-1992 JohnRo
Fixed bugs getting some numbers (e.g. codepage).
03-Mar-1992 JohnRo
Added some checking for non-null group name.
11-Mar-1992 JohnRo
Added command-line parsing stuff.
Use WARNING_MSG(), ERROR_MSG(), PROGRESS_MSG() macros.
18-Mar-1992 JohnRo
Use iterator to allow hash-table collision handling.
Added flag for verbose output at run time.
19-Mar-1992 JohnRo
Use our own version of decrypt code.
Fixed bug in user iterator bump code.
Added display of number of users in database and returned.
05-May-1992 JohnRo
Avoid dummy passwords altogether.
Minor changes to verbose level vs. messages printed.
Made changes suggested by PC-LINT.
23-Mar-1993 JohnRo
RAID 1187: logon hours are inappropriately set.
Got rid of new warnings from the new compiler.
Use <prefix.h> equates.
10-May-1993 JohnRo
RAID 6113: PortUAS: "dangerous handling of Unicode". Actually, PortUAS
was handling Unicode better than other parts of the system, so PortUAS
has to go down to the lowest common denominator. Hopefully after
NT product 1, we can correct this. See BRAIN_DAMAGE_UNICODE below.
30-Jul-1993 JohnRo
Use NetpKdPrint() where possible.
Made changes suggested by PC-LINT 5.0
02-Sep-1993 JohnRo
Use NetpNameCompare() to compare user names.
Use NetApiBufferAllocate() instead of private version.
--*/
// These must be included first:
#include <nt.h> // Needed by <crypt.h>
#include <ntrtl.h> // Needed by <windows.h>
#include <nturtl.h> // Needed by <windows.h>
#include <windows.h> // _lread(), etc.
#include <lmcons.h> // NET_API_STATUS, etc.
// These may be included in any order:
#include <crypt.h>
#include <icanon.h> // I_NetNameCompare
#include <lmaccess.h>
#include <lmapibuf.h> // NetApiBufferAllocate(), NetApiBufferFree().
#include <lmerr.h>
#include <loghours.h> // NetpRotateLogonHours().
#include <memory.h>
#include <netdebug.h> // DBGSTATIC, NetpAssert().
#include <netlibnt.h> // NetpNtStatusToApiStatus().
#include <portuasp.h> // WARNING_MSG(), etc.
#include <prefix.h> // PREFIX_ equates.
#include <smbgtpt.h>
#include <string.h> // strcmp(), strlen() etc.
#include <tstring.h> // NetpCopy{type}To{type}, NetpSubsetStr(), etc.
#include <wchar.h>
#include <tchar.h>
#include "nlstxt.h" // NLS messages
//
// Global variables (set by PortUasParseCommandLine()).
//
LPTSTR PortUasGlobalUserToSkipTo = NULL;
BOOL Verbose = FALSE;
extern int CurrentCP;
//
// Static pointers to memory where data will be read in from UAS database.
//
DBGSTATIC HANDLE hAlloc;
DBGSTATIC HANDLE database = INVALID_HANDLE_VALUE;
DBGSTATIC LPUAS_AHDR UasHeader;
DBGSTATIC LPUAS_GROUPREC groups;
DBGSTATIC LPUAS_DISKUSERHASH userHashes;
DBGSTATIC LPUAS_USER_OBJECT user;
DBGSTATIC BOOL uasInMemory = FALSE;
DBGSTATIC USER_ITERATOR currentUser;
//
// Static counters for user-interface.
//
DBGSTATIC DWORD usersDoneSoFar = 0; // Users processed or skipped.
DBGSTATIC DWORD usersTotal;
//
// Private functions.
//
DBGSTATIC
NET_API_STATUS
PortUaspGetUserData(
IN LPUSER_ITERATOR Iterator
);
//FARBUGBUG This is a pretty big macro, now; probably should be a function.
#define PortUaspPutString( Record, Field, String, Var ) \
{ \
int cch; \
cch = strlen(String) + 1; \
( Record ).Field = Var; \
cch = MultiByteToWideChar(CurrentCP, MB_PRECOMPOSED, String, cch, Var, cch); \
NetpAssert(cch != 0); \
Var += cch; \
}
#define MAKE_PTR( User, Field ) \
((LPSTR)( &(User)->uo_record ) \
+ ( SmbGetUshort( &(User)->uo_record.Field )))
#define UAS_GROUP_ENTRY_IS_VALID( EntryPtr ) \
( ( ((BYTE) (EntryPtr)->name[0]) != UAS_REC_EMPTY ) \
&& ( ((BYTE) (EntryPtr)->name[0]) != UAS_REC_DELETE ) )
NET_API_STATUS
PortUasOpen(
IN LPTSTR File
)
/*++
Routine Description:
Open LM2.0 UAS database and read contents into memory.
Arguments:
File - pathname of UAS database file. Usually C:\LANMAN\ACCOUNTS\NET.ACC.
Return Value:
NET_API_STATUS - NERR_Success if successful, or one of the following:
ERROR_NOT_ENOUGH_MEMORY - self-explanatory
NERR_InvalidDatabase - file is corrupt or invalid
NERR_ACFNotFound - can't find the file
NERR_ACFFileIOFail - problem reading file
--*/
{
#ifdef BRAIN_DAMAGE_UNICODE
DWORD i;
#endif
DWORD size;
INT cb;
LPBYTE alloc;
//
// Allocate memory. Memory required is for: UasHeader
// group data
// user hash table
// single user entry
size = UAS_HASH_TBL_OFFSET + UAS_HASH_TBL_SIZE + UAS_MAX_USER_SIZE;
if (( hAlloc =
LocalAlloc( LMEM_MOVEABLE | LMEM_ZEROINIT, size )) == NULL ) {
return ERROR_NOT_ENOUGH_MEMORY;
}
if (( alloc = LocalLock( hAlloc )) == NULL ) {
(void) LocalFree( hAlloc );
return ERROR_NOT_ENOUGH_MEMORY;
}
//
// Assign portions of allocated memory to data sections.
//
UasHeader = (LPUAS_AHDR)alloc;
groups = (LPUAS_GROUPREC)( alloc + UAS_GROUP_HASH_START );
userHashes = (LPUAS_DISKUSERHASH)( alloc + UAS_HASH_TBL_OFFSET );
user = (LPUAS_USER_OBJECT)( alloc + UAS_HASH_TBL_OFFSET
+ UAS_HASH_TBL_SIZE );
//
// Open UAS database.
//
database = CreateFile(File, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (database == INVALID_HANDLE_VALUE) {
return NERR_ACFNotFound;
}
//
// Read UasHeader.
//
if (!ReadFile(database, UasHeader, UAS_GROUP_HASH_START, &cb, NULL) ||
cb != UAS_GROUP_HASH_START ) {
PortUasClose();
return NERR_ACFFileIOFail;
}
//
// Check if the file is an actual UAS database.
//
if ( strcmp( UasHeader->signature, UAS_LMSIG ) ||
strcmp( UasHeader->DBIdInfo, UAS_DBIDINFO_TEXT ) ||
SmbGetUshort( &UasHeader->integrity_flag ) == FALSE ) {
PortUasClose();
return NERR_InvalidDatabase;
}
//
// Check size of database.
//
if (( size = GetFileSize( database, NULL )) == (DWORD)-1L ) {
PortUasClose();
return NERR_ACFFileIOFail;
}
if (( size < ( SmbGetUlong( &UasHeader->free_list )
+ UAS_DISK_BLOCK_SIZE ))
|| ( size < ( SmbGetUlong( &UasHeader->access_list )
+ UAS_DISK_BLOCK_SIZE ))
|| (( size - UAS_HASH_TBL_OFFSET - 2 * UAS_HASH_TBL_SIZE )
% UAS_DISK_BLOCK_SIZE )) {
PortUasClose();
return NERR_InvalidDatabase;
}
usersTotal = (DWORD) SmbGetUshort( &(UasHeader->num_users) );
if (Verbose) {
//PROGRESS_MSG( ("UAS database has " FORMAT_DWORD " users.\n",
// usersTotal ) );
(void)NlsPutMsg(STDOUT, PUAS_UAS_DATABASE_HAS_USERS, usersTotal );
}
usersDoneSoFar = 0;
//
// Get group information.
//
if (!ReadFile(database, groups, UAS_GRECSIZE * UAS_MAXGROUP, &cb, NULL) ||
cb != UAS_GRECSIZE * UAS_MAXGROUP ) {
PortUasClose();
return NERR_ACFFileIOFail;
}
//
// Get user hash table.
//
if (SetFilePointer(database, UAS_HASH_TBL_OFFSET, NULL, FILE_BEGIN) == -1) {
PortUasClose();
return NERR_ACFFileIOFail;
}
if (!ReadFile(database, userHashes, UAS_HASH_TBL_SIZE, &cb, NULL) ||
cb != UAS_HASH_TBL_SIZE ) {
PortUasClose();
return NERR_ACFFileIOFail;
}
uasInMemory = TRUE;
PortUasInitUserIterator( currentUser );
return NERR_Success;
}
VOID
PortUasClose(
VOID
)
/*++
Routine Description:
Close the currently open UAS database.
Arguments:
none.
Return Value:
None.
--*/
{
if ( database != INVALID_HANDLE_VALUE ) {
uasInMemory = FALSE;
PortUasInitUserIterator( currentUser );
CloseHandle( database );
(VOID) LocalUnlock( hAlloc );
(VOID) LocalFree( hAlloc );
database = INVALID_HANDLE_VALUE;
if (Verbose) {
//PROGRESS_MSG( ("Processed or skipped " FORMAT_DWORD
// " users of an expected " FORMAT_DWORD ".\n",
// usersDoneSoFar, usersTotal) );
(void)NlsPutMsg(STDOUT, PUAS_PROCESSED_OR_SKIPPED_USERS_OF_TOTAL,
usersDoneSoFar, usersTotal );
}
NetpAssert( usersDoneSoFar == usersTotal );
}
return;
}
NET_API_STATUS
PortUasGetModals(
OUT LPUSER_MODALS_INFO_0 * Modals0
)
/*++
Routine Description:
Get user modals settings from current database.
Arguments:
Modals0 - A pointer to an LPUSER_MODALS_INFO_0 which will get a pointer
to the returned level 0 user modals structure. This buffer
must be freed with NetApiBufferFree.
Return Value:
NET_API_STATUS - NERR_Success if successful, or one of the following:
NERR_ACFNotLoaded - database not open
ERROR_NOT_ENOUGH_MEMORY - self-explanatory
--*/
{
//
// Check if the database is in memory.
//
if ( !uasInMemory ) {
return NERR_ACFNotLoaded;
}
//
// Try to allocate two buffers for all the user modals data.
//
if ( NetApiBufferAllocate( sizeof(USER_MODALS_INFO_0), (PVOID *)Modals0 )){
return ERROR_NOT_ENOUGH_MEMORY;
}
//
// Fill buffer with data.
//
(*Modals0)->usrmod0_min_passwd_len
= (DWORD)SmbGetUshort( &UasHeader->min_passwd_len );
(*Modals0)->usrmod0_max_passwd_age
= SmbGetUlong( &UasHeader->max_passwd_age );
(*Modals0)->usrmod0_min_passwd_age
= SmbGetUlong( &UasHeader->min_passwd_age );
(*Modals0)->usrmod0_force_logoff
= SmbGetUlong( &UasHeader->force_logoff );
(*Modals0)->usrmod0_password_hist_len
= (DWORD)SmbGetUshort( &UasHeader->passwd_hist_len );
return NERR_Success;
}
NET_API_STATUS
PortUasGetGroups(
OUT LPBYTE * Buffer,
OUT LPBYTE * Gids,
OUT LPDWORD Entries
)
/*++
Routine Description:
Get groups from the current database.
Arguments:
Buffer - A pointer to an LPBYTE which will get a pointer to the returned
buffer. The buffer will contain a number of GROUP_INFO_1
structures, similar to that returned from NetGroupEnum. This
buffer must be freed with NetApiBufferFree.
Gids - A pointer to an LPBYTE which will get a pointer to an array
of group IDs, corresponding to the entries in the returned
buffer. This array must be freed with NetApiBufferFree.
Entries - A pointer to a DWORD which will receive the number of
entries in the buffer. There are no more entries in the
database.
Return Value:
NET_API_STATUS - NERR_Success if successful, or one of the following:
NERR_ACFNotLoaded - database not open
ERROR_NOT_ENOUGH_MEMORY - self-explanatory
--*/
{
DWORD i;
DWORD count;
DWORD size; // byte count.
LPDWORD groupIds;
LPGROUP_INFO_1 groupInfo;
LPWSTR varData;
*Entries = 0;
//
// Check if the database is in memory.
//
if ( !uasInMemory ) {
return NERR_ACFNotLoaded;
}
//
// Go through the group entries, calculating required buffer size.
//
size = 16;
count = 0;
for ( i = 0; i < UAS_MAXGROUP; i++ ) {
//
// Only enumerate valid entries.
//
if ( UAS_GROUP_ENTRY_IS_VALID( &groups[i] ) ) {
count++;
size += sizeof(GROUP_INFO_1);
size += sizeof(WCHAR) * ( strlen( (char *) groups[i].name ) + 1);
size += sizeof(WCHAR) * ( strlen( (char *) groups[i].comment ) + 1);
}
}
//
// Try to get buffers big enough.
//
if ( NetApiBufferAllocate( 16 + count * sizeof(DWORD), (PVOID *)Gids )) {
return ERROR_NOT_ENOUGH_MEMORY;
}
if ( NetApiBufferAllocate( size, (PVOID *)Buffer )) {
(void) NetApiBufferFree( *Gids );
return ERROR_NOT_ENOUGH_MEMORY;
}
//
// Fill buffer with data.
//
groupInfo = (LPGROUP_INFO_1)*Buffer;
groupIds = (LPDWORD)*Gids;
varData = (LPVOID)( &groupInfo[count] );
count = 0;
for ( i = 0; i < UAS_MAXGROUP; i++ ) {
//
// Only enumerate valid entries.
//
if ( UAS_GROUP_ENTRY_IS_VALID( &groups[i] ) ) {
NetpAssert( groups[i].name[0] != NULLC );
groupIds[count] = (DWORD)i;
PortUaspPutString( groupInfo[count], grpi1_name,
groups[i].name, varData );
PortUaspPutString( groupInfo[count], grpi1_comment,
groups[i].comment, varData );
count++;
}
}
*Entries = count;
return NERR_Success;
}
DBGSTATIC BOOL
PortUasIsUserIteratorValid(
IN LPUSER_ITERATOR Iterator
)
{
#define M(text) \
{ \
NetpKdPrint(( PREFIX_PORTUAS "PortUasIsUserIteratorValid: " \
text ".\n" )); \
}
if (Iterator == NULL) {
M("null ptr");
return (FALSE);
} else if ( (Iterator->Index == NULL_INDEX)
&& (Iterator->DiskOffset==NULL_DISK_OFFSET) ) {
return (TRUE); // initial value
} else if (Iterator->Index == UAS_USER_HASH_ENTRIES) {
return (TRUE); // done
} else if (Iterator->Index > UAS_USER_HASH_ENTRIES) {
M("index too large");
return (FALSE);
} else if (Iterator->Index == NULL_INDEX) {
M("null index w/o null disk offset");
return (FALSE);
} else if (Iterator->DiskOffset == NULL_DISK_OFFSET) {
M("null disk offset w/o null index");
return (FALSE);
} else {
return (TRUE); // normal (in between initial and done) value.
}
/*NOTREACHED*/
}
DBGSTATIC VOID
PortUasFindNextNonemptyUserChain(
IN OUT LPUSER_ITERATOR Iterator
)
{
DWORD CurrentChain;
NetpAssert( PortUasIsUserIteratorValid( Iterator ) );
CurrentChain = Iterator->Index;
if (CurrentChain == NULL_INDEX) {
CurrentChain = 0;
} else if (CurrentChain == UAS_USER_HASH_ENTRIES) {
return; // Already done.
} else {
++CurrentChain;
}
while (CurrentChain < UAS_USER_HASH_ENTRIES) {
if ( (userHashes[CurrentChain].dh_disk) != NULL_DISK_OFFSET ) {
Iterator->DiskOffset = userHashes[CurrentChain].dh_disk;
break; // found a nonempty chain
}
++CurrentChain;
}
Iterator->Index = CurrentChain; // may be UAS_USER_HASH_ENTRIES (done)
NetpAssert( PortUasIsUserIteratorValid( Iterator ) );
}
DBGSTATIC VOID
PortUasBumpUserIterator(
IN OUT LPUSER_ITERATOR Iterator
)
{
NetpAssert( PortUasIsUserIteratorValid( Iterator ) );
if (Iterator->Index == NULL_INDEX) {
// Just starting out.
PortUasFindNextNonemptyUserChain( Iterator );
} else if (Iterator->Index == UAS_USER_HASH_ENTRIES) {
// Already done. Nothing else to do.
} else {
DWORD NextDiskOffset;
// We're in a chain...
// We should only be here if we've got the one pointing to the
// next one.
NetpAssert( PortUasUserIteratorEqual( Iterator, &currentUser ) );
//
// Set disk offset to point to the next entry (if any) in chain.
//
NextDiskOffset = user->uo_header.do_next;
if (NextDiskOffset != NULL_DISK_OFFSET) {
//
// We've got at least one more entry in this chain.
//
Iterator->DiskOffset = NextDiskOffset;
} else {
//
// Handle end of chain by finding next nonempty chain.
//
PortUasFindNextNonemptyUserChain( Iterator );
}
}
NetpAssert( PortUasIsUserIteratorValid( Iterator ) );
} // PortUasBumpUserIterator
DBGSTATIC BOOL
PortUasIsUserIteratorDone(
IN LPUSER_ITERATOR Iterator
)
{
NetpAssert( PortUasIsUserIteratorValid( Iterator ) );
if (Iterator->Index == UAS_USER_HASH_ENTRIES) {
return (TRUE);
} else {
return (FALSE);
}
}
NET_API_STATUS
PortUasGetUser(
IN OUT LPUSER_ITERATOR Iterator,
OUT LPBYTE * Buffer
)
/*++
Routine Description:
Get a user from the current database.
Arguments:
BUGBUG: Index (DWORD) is now Iterator (structure). Update doc!
Index - A pointer to a DWORD indicating which user entry to get. When
the index is NULL_INDEX, the first entry is read. Otherwise,
the index is incremented, and the next entry is read. On return,
this index contains the index of the entry just read.
Buffer - A pointer to an LPBYTE which will get a pointer to the returned
buffer. The buffer will contain a single USER_INFO_22
structure, similar to that returned from NetUserGetInfo,
except that the password will be a null array( filled with
zeros). This buffer must be freed with NetApiBufferFree.
Return Value:
NET_API_STATUS - NERR_Success if successful, or one of the following:
NERR_UserNotFound - no more users left
NERR_ACFNotLoaded - database not open
ERROR_INVALID_PARAMETER - iterator is bad
ERROR_NOT_ENOUGH_MEMORY - self-explanatory
NERR_ACFFileIOFail - error reading file
--*/
{
NET_API_STATUS rc;
DWORD size;
LPUSER_INFO_22 userInfo;
LPWSTR varData;
//
// Check if the database is in memory.
//
if ( !uasInMemory ) {
return NERR_ACFNotLoaded;
}
//
// (loop if we're skipping to a particular user)
//
/*lint -save -e716 */ // disable warnings for while(TRUE)
while (TRUE) { // (loop if we're skipping to a particular user)
NetpAssert( PortUasIsUserIteratorValid( Iterator ) );
PortUasBumpUserIterator( Iterator );
if (PortUasIsUserIteratorDone( Iterator ) ) {
return NERR_UserNotFound;
}
++usersDoneSoFar;
if (Verbose) {
//PROGRESS_MSG( ("Reading user " FORMAT_DWORD " of " FORMAT_DWORD
// ".\n", usersDoneSoFar, usersTotal) );
(VOID) NlsPutMsg(
STDOUT,
PUAS_READING_USER_OF_TOTAL,
usersDoneSoFar,
usersTotal );
}
//
// Get the user data in a global variable.
//
rc = PortUaspGetUserData( Iterator );
if (rc != NO_ERROR) {
NetpAssert( rc != NERR_UserNotFound ); // Already handled!
return rc;
}
//
// Decide whether or not this is the one we're looking for or
// if we're processing all users. If neither, then loop back
// and get the next user.
//
if (PortUasGlobalUserToSkipTo == NULL) {
break; // Not skipping users, so use this one by definition.
} else {
LONG Result;
TCHAR TempUserName[UNLEN+1];
if ( strlen( user->uo_record.name ) > UNLEN ) {
// Don't use this one, we would trash the stack below.
return (NERR_ACFFileIOFail); // corrupt database!
}
NetpCopyStrToTStr(
TempUserName, // dest (TCHARs)
(LPSTR) (user->uo_record.name) ); // src
Result = I_NetNameCompare(
NULL, // local (no server name)
PortUasGlobalUserToSkipTo,
TempUserName,
NAMETYPE_USER, // type is user name
0 ); // flags: nothing special
if (Result != 0) {
//PROGRESS_MSG( ("Skipping user " FORMAT_LPSTR "...\n",
// user->uo_record.name) );
(void)NlsPutMsg(STDOUT, PUAS_SKIPPING_USER,
user->uo_record.name);
} else {
// Found the one we're looking for.
break;
}
}
}
/*lint -restore */ // re-enable warnings for while(TRUE)
//
// Find required return buffer size
//
size = strlen( user->uo_record.name )
// no password at all, so don't add chars for it here.
+ strlen( MAKE_PTR( user, directory_o ))
+ strlen( MAKE_PTR( user, comment_o ))
+ strlen( MAKE_PTR( user, script_o ))
+ strlen( MAKE_PTR( user, full_name_o ))
+ strlen( MAKE_PTR( user, usr_comment_o ))
+ strlen( MAKE_PTR( user, parms_o ))
+ strlen( MAKE_PTR( user, workstation_o ))
+ strlen( MAKE_PTR( user, logon_server_o ))
+ 10;
size = size * sizeof(WCHAR)
+ sizeof(USER_INFO_22)
+ UNITS_PER_WEEK / 8 + 1;
//
// Try to get a buffer big enough.
//
rc = NetApiBufferAllocate( size, (PVOID *)Buffer );
if (rc != NO_ERROR) {
NetpAssert( rc == ERROR_NOT_ENOUGH_MEMORY );
return ERROR_NOT_ENOUGH_MEMORY;
}
NetpAssert( Buffer != NULL );
//
// Fill buffer with data.
//
userInfo = (LPUSER_INFO_22)*Buffer;
varData = (LPVOID)( &userInfo[1] );
PortUaspPutString( *userInfo, usri22_name,
user->uo_record.name, varData );
RtlFillMemory( userInfo->usri22_password,
sizeof(userInfo->usri22_password), 0);
userInfo->usri22_priv
= (DWORD)SmbGetUshort( &user->uo_record.user.uc0_priv );
PortUaspPutString( *userInfo, usri22_home_dir,
MAKE_PTR( user, directory_o ), varData );
PortUaspPutString( *userInfo, usri22_comment,
MAKE_PTR( user, comment_o ), varData );
userInfo->usri22_flags = (DWORD)SmbGetUshort( &user->uo_record.flags );
PortUaspPutString( *userInfo, usri22_script_path,
MAKE_PTR( user, script_o ), varData );
userInfo->usri22_auth_flags
= SmbGetUlong( &user->uo_record.user.uc0_auth_flags );
PortUaspPutString( *userInfo, usri22_full_name,
MAKE_PTR( user, full_name_o ), varData );
PortUaspPutString( *userInfo, usri22_usr_comment,
MAKE_PTR( user, usr_comment_o ), varData );
PortUaspPutString( *userInfo, usri22_parms,
MAKE_PTR( user, parms_o ), varData );
//
// Convert the list of workstations from a blank separated list to
// comma separated list.
//
PortUaspPutString( *userInfo, usri22_workstations,
MAKE_PTR( user, workstation_o ), varData );
if ( wcslen( userInfo->usri22_workstations ) > 0 ) {
NTSTATUS NtStatus;
UNICODE_STRING BlankSep;
UNICODE_STRING CommaSep;
RtlInitUnicodeString( &BlankSep, userInfo->usri22_workstations );
NtStatus = RtlConvertUiListToApiList( &BlankSep,
&CommaSep,
TRUE ); // Blank Is Delimiter
if ( !NT_SUCCESS(NtStatus) ) {
// ERROR_MSG( ("Error processing workstation list for user " FORMAT_LPSTR,
// user->uo_record.name) );
(VOID) NlsPutMsg( STDOUT, PUAS_ERROR_PROCESSING_WORKSTATIONS,
user->uo_record.name );
return NetpNtStatusToApiStatus( NtStatus );
}
NetpAssert( wcslen(CommaSep.Buffer) ==
wcslen(userInfo->usri22_workstations) );
(VOID) wcscpy( userInfo->usri22_workstations, CommaSep.Buffer );
(VOID) RtlFreeHeap(RtlProcessHeap(), 0, CommaSep.Buffer );
}
userInfo->usri22_acct_expires
= SmbGetUlong( &user->uo_record.acct_expires );
#if 0 // BUGBUG - SAM doesn't support max storage
userInfo->usri22_max_storage
= SmbGetUlong( &user->uo_record.max_storage );
#else
userInfo->usri22_max_storage = USER_MAXSTORAGE_UNLIMITED;
#endif
userInfo->usri22_units_per_week = UNITS_PER_WEEK;
userInfo->usri22_logon_hours = (LPBYTE)varData;
memcpy( (LPBYTE)varData, user->uo_record.logonhrs, UNITS_PER_WEEK / 8 );
if ( !NetpRotateLogonHours(
(LPVOID) varData, // rotate these hours (update in place)
UNITS_PER_WEEK,
TRUE /* yes, convert to GMT */ ) ) {
//ERROR_MSG( ("Error processing logon hours for user " FORMAT_LPSTR,
// user->uo_record.name) );
(VOID) NlsPutMsg(STDOUT, PUAS_ERROR_PROCESSING_LOGON_HOURS,
user->uo_record.name);
// Tell caller about error. This may or may not have been our
// fault, but NetpRotateLogonHours doesn't give any other clues.
return (NERR_InternalError);
}
varData = (LPWSTR)( (LPBYTE)varData + UNITS_PER_WEEK / 8 + 1 );
PortUaspPutString( *userInfo, usri22_logon_server,
MAKE_PTR( user, logon_server_o ), varData );
userInfo->usri22_country_code
= (DWORD) SmbGetUshort( &user->uo_record.country_code );
userInfo->usri22_code_page
= (DWORD) SmbGetUshort( &user->uo_record.code_page );
return NERR_Success;
}
NET_API_STATUS
PortUasGetUserOWFPassword(
IN LPUSER_ITERATOR Iterator,
OUT LPBYTE * Password
)
/*++
Routine Description:
Get a user's password, in one-way-encryption form.
Arguments:
BUGBUG: Index (DWORD) is now Iterator (structure). Update doc!
Index - A DWORD indicating which entry to get. This can, but doesn't
have to, be the value of the index returned from PortUasGetUser.
Password - A pointer to an LPBYTE which will get a pointer to the
returned password. This buffer must be freed with
NetApiBufferFree.
Return Value:
NET_API_STATUS - NERR_Success if successful, or one of the following:
NERR_UserNotFound - not a valid user index
NERR_ACFNotLoaded - database not open
ERROR_INVALID_PASSWORD - can't be decrypted
ERROR_INVALID_PARAMETER - index is bad
ERROR_NOT_ENOUGH_MEMORY - self-explanatory
NERR_ACFFileIOFail - error reading file
--*/
{
NET_API_STATUS rc;
//
// Check if the database is in memory.
//
if ( !uasInMemory ) {
return NERR_ACFNotLoaded;
}
//
// Check the iterator.
//
NetpAssert( PortUasIsUserIteratorValid( Iterator ) );
//
// Get the user data, if necessary.
//
if ( rc = PortUaspGetUserData( Iterator )) {
return rc;
}
//
// Try to get a buffer.
//
if ( NetApiBufferAllocate( sizeof(LM_OWF_PASSWORD), (PVOID *)Password )) {
return ERROR_NOT_ENOUGH_MEMORY;
}
//
// Decrypt password into buffer.
//
#if 0
if (Verbose) {
DumpPassword( "from UAS", user->uo_record.passwd );
}
#endif
if ( PortUasDecryptLmOwfPwdWithIndex(
(PENCRYPTED_LM_OWF_PASSWORD)( user->uo_record.passwd ),
(LPDWORD) &(Iterator->Index),
(PLM_OWF_PASSWORD)*Password )) {
(void) NetApiBufferFree( *Password );
return ERROR_INVALID_PASSWORD;
}
#if 0
if (Verbose) {
DumpPassword( "decrypted once", *Password );
}
#endif
return NERR_Success;
}
NET_API_STATUS
PortUasGetUserGroups(
IN LPUSER_ITERATOR UserIterator,
OUT LPBYTE * Buffer,
OUT LPBYTE * Gids,
OUT LPDWORD Entries
)
/*++
Routine Description:
Get groups for a user in the current database.
Arguments:
BUGBUG: Index (DWORD) is now UserIterator (structure). Update doc!
Index - A DWORD indicating which entry to get. This can, but doesn't
have to, be the value of the index returned from PortUasGetUser.
Buffer - A pointer to an LPBYTE which will get a pointer to the returned
buffer. The buffer will contain a number of GROUP_INFO_0
structures, similar to that returned from NetUserGetGroups.
Gids - A pointer to an LPBYTE which will get a pointer to an array
of group IDs, corresponding to the entries in the returned
buffer. This array must be freed with NetApiBufferFree.
Entries - A pointer to a DWORD which will receive a count of the number
of group entries returned.
Return Value:
NET_API_STATUS - NERR_Success if successful, or one of the following:
NERR_UserNotFound - not a valid user iterator
NERR_ACFNotLoaded - database not open
ERROR_INVALID_PARAMETER - iterator is bad
ERROR_NOT_ENOUGH_MEMORY - self-explanatory
NERR_ACFFileIOFail - error reading file
--*/
{
NET_API_STATUS rc;
DWORD count;
DWORD size;
LPBYTE groupMask;
LPDWORD groupIds;
LPGROUP_INFO_0 groupInfo;
LPWSTR varData;
DWORD i;
//
// Check if the database is in memory.
//
if ( !uasInMemory ) {
return NERR_ACFNotLoaded;
}
//
// Check the user iterator.
//
NetpAssert( PortUasIsUserIteratorValid( UserIterator ) );
//
// Get the user data, if necessary.
//
if ( rc = PortUaspGetUserData( UserIterator )) {
return rc;
}
//
// Find the number of groups.
//
count = 0;
groupMask = user->uo_record.user.uc0_groups;
for ( i = 0; i < UAS_MAXGROUP; i++ ) {
if ( UAS_ISBITON( groupMask, i )) {
count++;
}
}
//
// Try to get a buffer of adequate size. The maximum size required is
// calculated using the count above and the maximum size of a group name.
// LANMAN 2.0 group names are smaller, we'll use that maximum.
//
if ( NetApiBufferAllocate( 16 + count * sizeof(DWORD), (PVOID *)Gids )) {
return ERROR_NOT_ENOUGH_MEMORY;
}
size = count * (( LM20_GNLEN + 1 ) * sizeof(WCHAR) + sizeof(GROUP_INFO_0))
+ 16;
if ( NetApiBufferAllocate( size, (PVOID *)Buffer )) {
(void) NetApiBufferFree( *Gids );
return ERROR_NOT_ENOUGH_MEMORY;
}
//
// One by one, get the group names.
//
groupInfo = (LPGROUP_INFO_0)*Buffer;
groupIds = (LPDWORD)*Gids;
varData = (LPVOID)( &groupInfo[count] );
count = 0;
for ( i = 0; i < UAS_MAXGROUP; i++ ) {
//
// Only enumerate valid entries.
//
if ( UAS_GROUP_ENTRY_IS_VALID( &groups[i] ) ) {
if ( UAS_ISBITON( groupMask, i )) {
groupIds[count] = i;
PortUaspPutString( groupInfo[count], grpi0_name,
groups[i].name, varData );
count++;
}
}
}
*Entries = count;
return NERR_Success;
}
DBGSTATIC
NET_API_STATUS
PortUaspGetUserData(
IN LPUSER_ITERATOR Iterator
)
/*++
Routine Description:
Get actual data for a user indexed in the hashing table. If the
particular user is already loaded, it does nothing.
Arguments:
Iterator - indicates user to get.
Return Value:
NET_API_STATUS - NERR_Success if successful, or one of the following:
NERR_UserNotFound - not a valid user
NERR_ACFFileIOFail - error reading file
--*/
{
INT cb;
UAS_DISK_OBJ_HDR usrHeader;
NetpAssert( PortUasIsUserIteratorValid( Iterator ) );
//
// Is the user already loaded?
//
if ( PortUasUserIteratorEqual( Iterator, &currentUser ) ) {
return NERR_Success;
}
//
// Is this a valid user?
//
NetpAssert( Iterator->Index != NULL_INDEX );
NetpAssert( Iterator->Index != UAS_USER_HASH_ENTRIES );
NetpAssert( Iterator->DiskOffset != NULL_DISK_OFFSET );
NetpAssert( userHashes[Iterator->Index].dh_disk != NULL_DISK_OFFSET );
//
// Flag global data as being invalid, in case we get error during read.
//
PortUasInitUserIterator( currentUser );
//
// Load the header for the user data.
//
if (SetFilePointer(database, (LONG)Iterator->DiskOffset, NULL, FILE_BEGIN)
== -1) {
return NERR_ACFFileIOFail;
}
if (!ReadFile(database, &usrHeader, sizeof(UAS_DISK_OBJ_HDR), &cb, NULL) ||
cb != sizeof(UAS_DISK_OBJ_HDR)) {
return NERR_ACFFileIOFail;
}
//
// Use information in the header to rewind and read the correct amount.
//
if (SetFilePointer(database, (LONG)Iterator->DiskOffset, NULL, FILE_BEGIN)
== -1) {
return NERR_ACFFileIOFail;
}
if (!ReadFile(database, user,
(DWORD)usrHeader.do_numblocks * UAS_DISK_BLOCK_SIZE,
&cb, NULL) ||
cb != usrHeader.do_numblocks * UAS_DISK_BLOCK_SIZE ) {
return NERR_ACFFileIOFail;
}
//
// Flag global data as valid again.
//
PortUasCopyUserIterator(
& currentUser, // dest
Iterator ); // src
return NERR_Success;
}