/*++ 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 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 // Needed by #include // Needed by #include // Needed by #include // _lread(), etc. #include // NET_API_STATUS, etc. // These may be included in any order: #include #include // I_NetNameCompare #include #include // NetApiBufferAllocate(), NetApiBufferFree(). #include #include // NetpRotateLogonHours(). #include #include // DBGSTATIC, NetpAssert(). #include // NetpNtStatusToApiStatus(). #include // WARNING_MSG(), etc. #include // PREFIX_ equates. #include #include // strcmp(), strlen() etc. #include // NetpCopy{type}To{type}, NetpSubsetStr(), etc. #include #include #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, ¤tUser ) ); // // 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, ¤tUser ) ) { 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; }