/*++ Copyright (c) 1991-1993 Microsoft Corporation Module Name: ApiUser.c Abstract: This module contains individual API handlers for the NetUser APIs. SUPPORTED : NetUserAdd2, NetUserDel, NetUserEnum, NetUserEnum2, NetUserGetGroups, NetUserGetInfo, NetUserModalsGet, NetUserModalsSet, NetUserSetGroups, NetUserSetInfo2, NetUserSetInfo, NetUserPasswordSet2 UNSUPPORTED : NetUserValidate2. Author: Shanku Niyogi (w-shanku) 11-Feb-1991 Revision History: --*/ // // NetUser APIs are UNICODE only. // #ifndef UNICODE #define UNICODE #endif #include "xactsrvp.h" #include #include "changepw.h" #include #include #include #include // PREFIX_ equates. // // Declaration of descriptor strings. // STATIC const LPDESC Desc16_user_info_0 = REM16_user_info_0; STATIC const LPDESC Desc32_user_info_0 = REM32_user_info_0; STATIC const LPDESC Desc16_user_info_1 = REM16_user_info_1; STATIC const LPDESC Desc32_user_info_1 = REM32_user_info_1; STATIC const LPDESC Desc32_user_info_1_NC = REM32_user_info_1_NOCRYPT; STATIC const LPDESC Desc32_user_info_1_OWF = REM32_user_info_1_OWF; STATIC const LPDESC Desc16_user_info_1_setinfo = REM16_user_info_1_setinfo; STATIC const LPDESC Desc32_user_info_1_setinfo = REM32_user_info_1_setinfo; STATIC const LPDESC Desc32_user_info_1_setinfo_NC = REM32_user_info_1_setinfo_NOCRYPT; STATIC const LPDESC Desc16_user_info_2 = REM16_user_info_2; STATIC const LPDESC Desc32_user_info_2 = REM32_user_info_2; STATIC const LPDESC Desc32_user_info_2_NC = REM32_user_info_2_NOCRYPT; STATIC const LPDESC Desc16_user_info_2_setinfo = REM16_user_info_2_setinfo; STATIC const LPDESC Desc32_user_info_2_setinfo = REM32_user_info_2_setinfo; STATIC const LPDESC Desc32_user_info_2_setinfo_NC = REM32_user_info_2_setinfo_NOCRYPT; STATIC const LPDESC Desc16_user_info_10 = REM16_user_info_10; STATIC const LPDESC Desc32_user_info_10 = REM32_user_info_10; STATIC const LPDESC Desc16_user_info_11 = REM16_user_info_11; STATIC const LPDESC Desc32_user_info_11 = REM32_user_info_11; STATIC const LPDESC Desc32_user_info_22 = REM32_user_info_22; STATIC const LPDESC Desc16_user_group_info_0 = REM16_group_info_0; STATIC const LPDESC Desc32_user_group_info_0 = REM32_group_info_0; STATIC const LPDESC Desc16_user_group_info_0_set = REM16_group_users_info_0_set; STATIC const LPDESC Desc32_user_group_info_0_set = REM32_group_users_info_0_set; STATIC const LPDESC Desc16_user_modals_info_0 = REM16_user_modals_info_0; STATIC const LPDESC Desc32_user_modals_info_0 = REM32_user_modals_info_0; STATIC const LPDESC Desc16_user_modals_info_0_setinfo = REM16_user_modals_info_0_setinfo; STATIC const LPDESC Desc32_user_modals_info_0_setinfo = REM32_user_modals_info_0_setinfo; STATIC const LPDESC Desc16_user_modals_info_1 = REM16_user_modals_info_1; STATIC const LPDESC Desc32_user_modals_info_1 = REM32_user_modals_info_1; STATIC const LPDESC Desc16_user_modals_info_1_setinfo = REM16_user_modals_info_1_setinfo; STATIC const LPDESC Desc32_user_modals_info_1_setinfo = REM32_user_modals_info_1_setinfo; STATIC NET_API_STATUS XsGetMinPasswordLength( LPDWORD minPasswordLength ) { NET_API_STATUS apiStatus; LPUSER_MODALS_INFO_0 modals = NULL; HANDLE OpenedToken; NetpAssert( minPasswordLength != NULL ); // // Revert to Local System // (VOID)NtOpenThreadToken( NtCurrentThread(), MAXIMUM_ALLOWED, TRUE, &OpenedToken ); RevertToSelf(); // // Find out how long the password has to be. // apiStatus = NetUserModalsGet( NULL, // local (no server name) 0, // level (LPBYTE *)&modals ); // alloc and set ptr // // Re-impersonate the client // (VOID)NtSetInformationThread( NtCurrentThread(), ThreadImpersonationToken, &OpenedToken, sizeof( OpenedToken ) ); if ( apiStatus != NO_ERROR ) { NetpKdPrint(( PREFIX_XACTSRV "XsGetMinPasswordLength: Problems getting modals: " FORMAT_API_STATUS ".\n", apiStatus )); return (apiStatus); } NetpAssert( modals != NULL ); *minPasswordLength = modals->usrmod0_min_passwd_len; (VOID) NetApiBufferFree( (LPVOID)modals ); return (NO_ERROR); } // XsGetMinPasswordLength STATIC NET_API_STATUS XsCheckAndReplacePassword ( IN DWORD Length ) /*++ Routine Description This routine checks the current password's real length to make sure it is valid, and then generates a reasonably random replacement password long enough to satisfy the system's modal for minimum password length. This routine is used by Add and SetInfo handlers below. Arguments: Length - Real length of the current password. Seed - A seed number. TempPassword - Receives a pointer to a new temporary password. If this is not specified, the new password is not generated. Return Value: NET_API_STATUS - NERR_Success on successful completion, or some other error status. --*/ { NET_API_STATUS status; DWORD minPasswordLength; // // Find out how long the password has to be. // status = XsGetMinPasswordLength( &minPasswordLength ); if ( status != NERR_Success ) { NetpKdPrint(( PREFIX_XACTSRV "XsCheckAndReplacePassword: Problems getting min PW len: " FORMAT_API_STATUS ".\n", status )); return status; } // // Check length of current password. // if ( Length < minPasswordLength ) { return NERR_PasswordTooShort; } return NERR_Success; } NET_API_STATUS XsNameToRid( IN LPCTSTR Name, // may be user or group name. IN SID_NAME_USE ExpectedType, OUT PULONG UserRid ) { NET_API_STATUS status; PSID_NAME_USE nameUse; NTSTATUS ntstatus; UNICODE_STRING unicodeName; PULONG tempRid; PSID accountsDomainId; SAM_HANDLE samConnectHandle; SAM_HANDLE samAccountsDomainHandle; if( ARGUMENT_PRESENT( UserRid ) ) { *UserRid = 0; } // // Get a connection to SAM. // ntstatus = SamConnect( NULL, // no server name (local) &samConnectHandle, // resulting SAM handle SAM_SERVER_LOOKUP_DOMAIN, // desired access NULL // no object attributes ); if ( !NT_SUCCESS( ntstatus ) ) { status = NetpNtStatusToApiStatus( ntstatus ); return status; } // // To open the accounts domain, we'll need the domain ID. // status = NetpGetLocalDomainId ( LOCAL_DOMAIN_TYPE_ACCOUNTS, // type we want. &accountsDomainId ); if ( status != NO_ERROR ) { SamCloseHandle( samConnectHandle ); return status; } // // Open the accounts domain. // ntstatus = SamOpenDomain( samConnectHandle, DOMAIN_LOOKUP, accountsDomainId, &samAccountsDomainHandle ); if ( !NT_SUCCESS( ntstatus ) ) { LocalFree( accountsDomainId ); SamCloseHandle( samConnectHandle ); status = NetpNtStatusToApiStatus( ntstatus ); return status; } // // Get a RID for this user name. // RtlInitUnicodeString( &unicodeName, // dest (NT struct) Name ); // src (null-terminated) ntstatus = SamLookupNamesInDomain( samAccountsDomainHandle, // users live in accounts domain (ULONG)1, // only want one name. &unicodeName, // name (in NT struct) &tempRid, // alloc and set RIDs. &nameUse // alloc and set name types. ); if ( !NT_SUCCESS( ntstatus ) ) { status = NetpNtStatusToApiStatus( ntstatus ); goto cleanup; } *UserRid = *tempRid; // // Did type user wanted match the actual one? // if ( ExpectedType != *nameUse ) { status = ERROR_INVALID_PARAMETER; } else { status = NO_ERROR; } // // Free memory which SAM allocated for us. // ntstatus = SamFreeMemory( nameUse ); if ( !NT_SUCCESS( ntstatus ) ) { status = NetpNtStatusToApiStatus( ntstatus ); } ntstatus = SamFreeMemory( tempRid ); if ( !NT_SUCCESS( ntstatus ) ) { status = NetpNtStatusToApiStatus( ntstatus ); } cleanup: LocalFree( accountsDomainId ); SamCloseHandle( samAccountsDomainHandle ); SamCloseHandle( samConnectHandle ); return status; } // XsNameToRid NET_API_STATUS XsSetMacPrimaryGroup( IN LPCTSTR UserName, IN LPCTSTR MacPrimaryField // field in "mGroup:junk" format. ) { NET_API_STATUS status; LPTSTR groupName = NULL; ULONG groupRid; USER_INFO_1051 userInfo; // // Extract the primary group name from the Mac field. // status = NetpGetPrimaryGroupFromMacField( MacPrimaryField, // name in "mGroup:" format. &groupName // alloc and set ptr. ); if ( status != NO_ERROR ) { goto cleanup; } // // Make sure this user is a member of the group (add to group if needed). // This will also check if the group and user exist. // status = NetGroupAddUser( NULL, // local (no server name) groupName, // group to update (LPTSTR)UserName // user name to add to group ); if ( (status != NO_ERROR) && (status != NERR_UserInGroup) ) { goto cleanup; } // // Convert the group name to a RID. // status = XsNameToRid( (LPCWSTR)groupName, SidTypeGroup, // expected type &groupRid ); if ( status != NO_ERROR ) { goto cleanup; } // // Call NetUserSetInfo to set the primary group ID using the RID. // userInfo.usri1051_primary_group_id = (DWORD)groupRid; status = NetUserSetInfo ( NULL, // local (no server name) (LPTSTR)UserName, PARMNUM_BASE_INFOLEVEL + USER_PRIMARY_GROUP_PARMNUM, (LPVOID)&userInfo, NULL // don't care about parmnum ); cleanup: if ( groupName != NULL ) { NetpMemoryFree( groupName ); } return status; } // XsSetMacPrimaryGroup NTSTATUS XsNetUserAdd2 ( API_HANDLER_PARAMETERS ) /*++ Routine Description: This routine handles a call to NetUserAdd. A remote NetUserAdd call from a 16-bit machine will translate to a NetUserAdd2 call, with a doubly encrypted password. We will call a special level of NetUserSetInfo to set this later, after the user has been added. Arguments: API_HANDLER_PARAMETERS - information about the API call. See XsTypes.h for details. Return Value: NTSTATUS - STATUS_SUCCESS or reason for failure. --*/ { NET_API_STATUS status; PXS_NET_USER_ADD_2 parameters = Parameters; LPVOID buffer = NULL; // Native parameters LPBYTE stringLocation = NULL; // Conversion variables DWORD bytesRequired = 0; DWORD bufferSize; LPBYTE nativeStructureDesc; LPUSER_INFO_1 user = NULL; DWORD parmError; DWORD level; BOOLEAN encryptionSupported = TRUE; PUSER_INFO_22 usri22; BYTE tempPwdBuffer[ENCRYPTED_PWLEN]; API_HANDLER_PARAMETERS_REFERENCE; // Avoid warnings IF_DEBUG(USER) { NetpKdPrint(( "XsNetUserAdd2: header at %lx, params at %lx, " "level %ld\n", Header, parameters, SmbGetUshort( ¶meters->Level ) )); } try { // // Check if password is encrypted. We know for a fact that dos redirs // don't support encryption // encryptionSupported = (BOOLEAN) ( SmbGetUshort( ¶meters->DataEncryption ) == TRUE ); level = SmbGetUshort( ¶meters->Level ); // // Check for password length // status = XsCheckAndReplacePassword( (DWORD)( SmbGetUshort( ¶meters->PasswordLength )) ); if ( status != NERR_Success ) { IF_DEBUG(ERRORS) { NetpKdPrint(( "XsNetUserAdd2: XsCheckAndReplacePassword failed: " "%X\n", status )); } goto cleanup; } // // Use the requested level to determine the format of the 32-bit // we need to pass to NetUserAdd. The format of the // 16-bit structure is stored in the transaction block, and we // got a pointer to it as a parameter. // switch ( level ) { case 1: StructureDesc = Desc16_user_info_1; nativeStructureDesc = Desc32_user_info_1_OWF; break; case 2: StructureDesc = Desc16_user_info_2; nativeStructureDesc = Desc32_user_info_22; break; default: status = ERROR_INVALID_LEVEL; goto cleanup; } // // Figure out if there is enough room in the buffer for all the // data required. If not, return NERR_BufTooSmall. // if ( !XsCheckBufferSize( SmbGetUshort( ¶meters->BufLen ), StructureDesc, FALSE // not in native format )) { IF_DEBUG(ERRORS) { NetpKdPrint(( "XsNetUserAdd2: Buffer too small.\n" )); } status = NERR_BufTooSmall; goto cleanup; } // // Find out how big a buffer we need to allocate to hold the native // 32-bit version of the input data structure. Always allocate // a level 22 buffer since we will always be making a level 22 // call to netuseradd. // bufferSize = XsBytesForConvertedStructure( (LPBYTE)XsSmbGetPointer( ¶meters->Buffer ), StructureDesc, Desc32_user_info_22, RapToNative, TRUE ); // // Allocate enough memory to hold the converted native buffer. // buffer = NetpMemoryAllocate( bufferSize ); if ( buffer == NULL ) { IF_DEBUG(ERRORS) { NetpKdPrint(( "XsNetUserAdd2: failed to create buffer" )); } status = NERR_NoRoom; goto cleanup; } IF_DEBUG(USER) { NetpKdPrint(( "XsNetUserAdd2: buffer of %ld bytes at %lx\n", bufferSize, buffer )); } // // Convert the buffer from 16-bit to 32-bit. // stringLocation = (LPBYTE)buffer + bufferSize; bytesRequired = 0; status = RapConvertSingleEntry( (LPBYTE)XsSmbGetPointer( ¶meters->Buffer ), StructureDesc, TRUE, buffer, buffer, nativeStructureDesc, FALSE, &stringLocation, &bytesRequired, Response, RapToNative ); if ( status != NERR_Success ) { IF_DEBUG(ERRORS) { NetpKdPrint(( "XsNetUserAdd: RapConvertSingleEntry failed: " "%X\n", status )); } status = NERR_InternalError; goto cleanup; } usri22 = buffer; // // If this is a level 1 call, then we did not fill up all the // entries required for a level 22 call. Put the default values // here. // if ( level == 1 ) { // // These are not used in a NetUserAdd. // // usri22->usri22_last_logon // usri22->usri22_last_logoff // usri22->usri22_units_per_week // usri22->usri22_bad_pw_count // usri22->usri22_num_logons // usri22->usri22_auth_flags = 0; usri22->usri22_full_name = NULL; usri22->usri22_usr_comment = NULL; usri22->usri22_parms = NULL; usri22->usri22_workstations = NULL; usri22->usri22_acct_expires = TIMEQ_FOREVER; usri22->usri22_max_storage = USER_MAXSTORAGE_UNLIMITED; usri22->usri22_logon_hours = NULL; usri22->usri22_logon_server = NULL; usri22->usri22_country_code = 0; usri22->usri22_code_page = 0; } else if ( usri22->usri22_logon_hours != NULL ) { // // Call NetpRotateLogonHours to make sure we behave properly // during DST. // if ( !NetpRotateLogonHours( usri22->usri22_logon_hours, usri22->usri22_units_per_week, TRUE ) ) { status = ERROR_INVALID_PARAMETER; goto cleanup; } } // // If the password is clear text, we need to convert it to an OWF // password. This is to fix a LMUNIX bug which forgets to upper // case the password it sends across. Converting it to OWF // tells sam not to do upcasing. // // If the password is encrypted, then we get the owf by decrypting // it with the session key. // RtlCopyMemory( tempPwdBuffer, usri22->usri22_password, ENCRYPTED_PWLEN ); if ( !encryptionSupported ) { (VOID) RtlCalculateLmOwfPassword( (PLM_PASSWORD) tempPwdBuffer, (PLM_OWF_PASSWORD) usri22->usri22_password ); } else { (VOID) RtlDecryptLmOwfPwdWithLmSesKey( (PENCRYPTED_LM_OWF_PASSWORD) tempPwdBuffer, (PLM_SESSION_KEY) Header->EncryptionKey, (PLM_OWF_PASSWORD) usri22->usri22_password ); } // // Make the local call. // status = NetUserAdd( NULL, 22, (LPBYTE) usri22, &parmError ); if ( !XsApiSuccess( status )) { IF_DEBUG(ERRORS) { NetpKdPrint(( "XsNetUserAdd2: NetUserAdd failed: %X\n", status )); if ( status == ERROR_INVALID_PARAMETER ) { NetpKdPrint(( "XsNetUserAdd2: ParmError: %ld\n", parmError )); } } goto cleanup; } // // If there was a Macintosh primary group field for this user, then // set the primary group. // if ( NetpIsMacPrimaryGroupFieldValid( (LPCTSTR)usri22->usri22_parms ) ) { NET_API_STATUS status1; status1 = XsSetMacPrimaryGroup( (LPCTSTR)usri22->usri22_name, (LPCTSTR)usri22->usri22_parms ); if ( !XsApiSuccess( status1 )) { IF_DEBUG(ERRORS) { NetpKdPrint(( "XsNetUserAdd2: SetMacPrimaryGroup failed: %X\n", status1 )); } } } // // There is no real return information for this API. // cleanup: ; } except( EXCEPTION_EXECUTE_HANDLER ) { status = (WORD)RtlNtStatusToDosError( GetExceptionCode() ); } Header->Status = (WORD)status; NetpMemoryFree( buffer ); return STATUS_SUCCESS; } // XsNetUserAdd2 NTSTATUS XsNetUserDel ( API_HANDLER_PARAMETERS ) /*++ Routine Description: This routine handles a call to NetUserDel. Arguments: API_HANDLER_PARAMETERS - information about the API call. See XsTypes.h for details. Return Value: NTSTATUS - STATUS_SUCCESS or reason for failure. --*/ { NET_API_STATUS status; PXS_NET_USER_DEL parameters = Parameters; LPTSTR nativeUserName = NULL; // Native parameters API_HANDLER_PARAMETERS_REFERENCE; // Avoid warnings IF_DEBUG(USER) { NetpKdPrint(( "XsNetUserDel: header at %lx, params at %lx, name %s\n", Header, parameters, SmbGetUlong( ¶meters->UserName ))); } try { // // Translate parameters, check for errors. // XsConvertTextParameter( nativeUserName, (LPSTR)XsSmbGetPointer( ¶meters->UserName ) ); // // Make the local call. // status = NetUserDel( NULL, nativeUserName ); if ( !XsApiSuccess( status )) { IF_DEBUG(ERRORS) { NetpKdPrint(( "XsNetUserDel: NetUserDel failed: %X\n", status )); } } cleanup: ; } except( EXCEPTION_EXECUTE_HANDLER ) { Header->Status = (WORD)RtlNtStatusToDosError( GetExceptionCode() ); } NetpMemoryFree( nativeUserName ); // // Nothing to return. // Header->Status = (WORD)status; return STATUS_SUCCESS; } NTSTATUS XsNetUserEnum ( API_HANDLER_PARAMETERS ) /*++ Routine Description This routine handles a call to NetUserEnum. Arguments: API_HANDLER_PARAMETERS - information about the API call. See XsTypes.h for details. Return Value: NTSTATUS - STATUS_SUCCESS or reason for failure. --*/ { NET_API_STATUS status; PXS_NET_USER_ENUM parameters = Parameters; LPVOID outBuffer = NULL; // Native parameters DWORD entriesRead; DWORD totalEntries; DWORD entriesFilled = 0; // Conversion variables DWORD bytesRequired = 0; LPBYTE nativeStructureDesc; API_HANDLER_PARAMETERS_REFERENCE; // Avoid parameters IF_DEBUG(USER) { NetpKdPrint(( "XsNetUserEnum: header at %lx, params at %lx, " "level %ld, buf size %ld\n", Header, parameters, SmbGetUshort( ¶meters->Level ), SmbGetUshort( ¶meters->BufLen ))); } try { // // Check for errors. // if (( XsWordParamOutOfRange( parameters->Level, 0, 2 )) && SmbGetUshort( ¶meters->Level ) != 10 ) { Header->Status = ERROR_INVALID_LEVEL; goto cleanup; } // // Make the local call. // status = NetUserEnum( NULL, (DWORD)SmbGetUshort( ¶meters->Level ), FILTER_NORMAL_ACCOUNT, (LPBYTE *)&outBuffer, XsNativeBufferSize( SmbGetUshort( ¶meters->BufLen )), &entriesRead, &totalEntries, NULL ); if ( !XsApiSuccess( status )) { IF_DEBUG(API_ERRORS) { NetpKdPrint(( "XsNetUserEnum: NetUserEnum failed: %X\n", status )); } Header->Status = (WORD)status; goto cleanup; } IF_DEBUG(USER) { NetpKdPrint(( "XsNetUserEnum: received %ld entries at %lx\n", entriesRead, outBuffer )); } // // Use the requested level to determine the format of the // data structure. // switch ( SmbGetUshort( ¶meters->Level ) ) { case 0: nativeStructureDesc = Desc32_user_info_0; StructureDesc = Desc16_user_info_0; break; case 1: nativeStructureDesc = Desc32_user_info_1; StructureDesc = Desc16_user_info_1; break; case 2: nativeStructureDesc = Desc32_user_info_2; StructureDesc = Desc16_user_info_2; break; case 10: nativeStructureDesc = Desc32_user_info_10; StructureDesc = Desc16_user_info_10; break; } // // Do the actual conversion from the 32-bit structures to 16-bit // structures. // XsFillEnumBuffer( outBuffer, entriesRead, nativeStructureDesc, (LPVOID)XsSmbGetPointer( ¶meters->Buffer ), (LPVOID)XsSmbGetPointer( ¶meters->Buffer ), SmbGetUshort( ¶meters->BufLen ), StructureDesc, NULL, // verify function &bytesRequired, &entriesFilled, NULL ); IF_DEBUG(USER) { NetpKdPrint(( "32-bit data at %lx, 16-bit data at %lx, %ld BR," " Entries %ld of %ld\n", outBuffer, SmbGetUlong( ¶meters->Buffer ), bytesRequired, entriesFilled, totalEntries )); } // // If all the entries could not be filled, return ERROR_MORE_DATA, // and return the buffer as is. Otherwise, the data needs to be // packed so that we don't send too much useless data. // if ( entriesFilled < totalEntries ) { Header->Status = ERROR_MORE_DATA; } else { Header->Converter = XsPackReturnData( (LPVOID)XsSmbGetPointer( ¶meters->Buffer ), SmbGetUshort( ¶meters->BufLen ), StructureDesc, entriesFilled ); } // // Set up the response parameters. // SmbPutUshort( ¶meters->EntriesRead, (WORD)entriesFilled ); SmbPutUshort( ¶meters->TotalAvail, (WORD)totalEntries ); cleanup: ; } except( EXCEPTION_EXECUTE_HANDLER ) { Header->Status = (WORD)RtlNtStatusToDosError( GetExceptionCode() ); } NetApiBufferFree( outBuffer ); // // Determine return buffer size. // XsSetDataCount( ¶meters->BufLen, StructureDesc, Header->Converter, entriesFilled, Header->Status ); return STATUS_SUCCESS; } // XsNetUserEnum NTSTATUS XsNetUserEnum2 ( API_HANDLER_PARAMETERS ) /*++ Routine Description: This routine handles a call to NetUserEnum. This version supports a resumable handle. Arguments: API_HANDLER_PARAMETERS - information about the API call. See XsTypes.h for details. Return Value: NTSTATUS - STATUS_SUCCESS or reason for failure. --*/ { NET_API_STATUS status; PXS_NET_USER_ENUM_2 parameters = Parameters; LPVOID outBuffer = NULL; // Native parameters DWORD TotalEntriesToReturn = 0; LPDESC nativeStructureDesc; DWORD nativeBufferSize = 0xFFFFFFFF; DWORD entriesRead = 0; DWORD PreviousEntriesRead; DWORD totalEntries; DWORD entriesFilled = 0; // Conversion variables DWORD bytesRequired; LPBYTE bufferBegin; DWORD bufferSize; DWORD totalEntriesRead= 0; DWORD resumeKey; LPBYTE SavedBufferBegin; DWORD SavedBufferSize; DWORD SavedTotalEntriesRead; DWORD SavedResumeKey; API_HANDLER_PARAMETERS_REFERENCE; // Avoid warnings try { IF_DEBUG(USER) { NetpKdPrint(( "XsNetUserEnum2: header at %lx, params at %lx, " "level %ld, buf size %ld\n", Header, parameters, SmbGetUshort( ¶meters->Level ), SmbGetUshort( ¶meters->BufLen ))); } // // Copy input resume handle to output resume handle, and get a copy of it. // resumeKey = SmbGetUlong( ¶meters->ResumeIn ); SmbPutUlong( ¶meters->ResumeOut, resumeKey ); IF_DEBUG(USER) { NetpKdPrint(( "XsNetUserEnum2: resume key is %ld\n", resumeKey )); } // // Use the level to determine the descriptor string. // switch ( SmbGetUshort( ¶meters->Level ) ) { case 0: nativeStructureDesc = Desc32_user_info_0; StructureDesc = Desc16_user_info_0; break; case 1: nativeStructureDesc = Desc32_user_info_1; StructureDesc = Desc16_user_info_1; break; case 2: nativeStructureDesc = Desc32_user_info_2; StructureDesc = Desc16_user_info_2; break; case 10: nativeStructureDesc = Desc32_user_info_10; StructureDesc = Desc16_user_info_10; break; default: // // Unsupported levels, abort before any work. // Header->Status = ERROR_INVALID_LEVEL; goto cleanup; } // // NetUserEnum2 is a resumable API, so we cannot get more information // from the native call than we can send back. The most efficient way // to do this is in a loop...we use the 16-bit buffer size to determine // a safe native buffer size, make the call, fill the entries, then // take the amount of space remaining and determine a safe size again, // and so on, until NetUserEnum returns either no entries or all entries // read. // // // Initialize important variables for loop. // bufferBegin = (LPBYTE)XsSmbGetPointer( ¶meters->Buffer ); bufferSize = (DWORD)SmbGetUshort( ¶meters->BufLen ); totalEntriesRead = 0; for ( ; ; ) { // // Compute a safe size for the native buffer. // // It is better to underguess than overguess. NetUserEnum is relatively // efficient (especially in the local case) at resuming an enumeration. // It is relatively inefficient at returning detailed information about // the enumerated users. // // If nativeBufferSize reaches 1 (or 0), // NetUserEnum will typically enumerate a single user. // if ( nativeBufferSize > bufferSize/2 ) { nativeBufferSize = bufferSize/2; } // // Remember how many we read last time to ensure we make progress. // PreviousEntriesRead = entriesRead; // // Save away a copy of all the important variables. // // The NetUserEnum API can actually overshoot its PrefMaxLen. The // values being saved are values known to not already have been overshot. // We can restore these values later if needed. // SavedBufferBegin = bufferBegin; SavedBufferSize = bufferSize; SavedTotalEntriesRead = totalEntriesRead; SavedResumeKey = resumeKey; // // Make the local call. // status = NetUserEnum( NULL, (DWORD)SmbGetUshort( ¶meters->Level ), FILTER_NORMAL_ACCOUNT, (LPBYTE *)&outBuffer, nativeBufferSize, &entriesRead, &totalEntries, &resumeKey ); if ( !XsApiSuccess( status )) { IF_DEBUG(API_ERRORS) { NetpKdPrint(( "XsNetUserEnum2: NetUserEnum failed: %X\n", status )); } Header->Status = (WORD)status; goto cleanup; } IF_DEBUG(USER) { NetpKdPrint(( "XsNetUserEnum2: received %ld entries out of %ld at %lx asking for %ld bytes.\n", entriesRead, totalEntries, outBuffer, nativeBufferSize )); NetpKdPrint(( "XsNetUserEnum2: resume key is now %ld\n", resumeKey )); } // // Keep track of the total entries available. // if ( totalEntries > TotalEntriesToReturn ) { TotalEntriesToReturn = totalEntries; } // // Was NetUserEnum able to read at least one complete entry? // if ( entriesRead == 0 ) { break; } // // Do the actual conversion from the 32-bit structures to 16-bit // structures. // XsFillEnumBuffer( outBuffer, entriesRead, nativeStructureDesc, bufferBegin, (LPBYTE)XsSmbGetPointer( ¶meters->Buffer ), bufferSize, StructureDesc, NULL, // verify function &bytesRequired, &entriesFilled, NULL ); IF_DEBUG(USER) { NetpKdPrint(( "XsNetUserEnum2: 32-bit data at %lx, 16-bit data at %lx, %ld BR," " Entries %ld of %ld\n", outBuffer, SmbGetUlong( ¶meters->Buffer ), bytesRequired, entriesFilled, entriesRead )); } // // If NetUserEnum overshot PrefMaxLen, // we can't simply return the collected data since we wouldn't // know what to use as a ResumeHandle. // if ( entriesRead != entriesFilled ) { // // Restore the saved values. // bufferBegin = SavedBufferBegin; bufferSize = SavedBufferSize; totalEntriesRead = SavedTotalEntriesRead; resumeKey = SavedResumeKey; // // If we have ANY data to return to the caller, // return the short list now rather than trying to outguess NetUserEnum // if ( totalEntriesRead != 0 ) { IF_DEBUG(USER) { NetpKdPrint(( "XsNetUserEnum2: couldn't pack data: return previous data\n" )); } break; } // // If we've already asked NetUserEnum for the smallest amount, // just give up. // if ( nativeBufferSize == 1 || entriesRead == 1 ) { status = NERR_BufTooSmall; IF_DEBUG(API_ERRORS) { NetpKdPrint(( "XsNetUserEnum2: NetUserEnum buffer too small: %X\n", status )); } Header->Status = (WORD)status; goto cleanup; } // // Otherwise, trim it down and try again. // If we've tried twice and gotten the same result, // be really agressive. // if ( entriesRead == PreviousEntriesRead || entriesRead < 10 ) { nativeBufferSize = 1; } else { nativeBufferSize /= 2; } // // If NetUserEnum returned useful data, // account for it. // } else { // // Update count of entries read. // totalEntriesRead += entriesRead; // // Are there any more entries to read? // if ( entriesRead == totalEntries ) { break; } // // If we've made the nativeBufferSize so small we're barely making // progress, // just return what we have to the caller. // if ( entriesRead == 1 ) { break; } // // Calculate new buffer beginning and size. // bufferBegin += entriesRead * RapStructureSize( StructureDesc, Response, FALSE ); bufferSize -= bytesRequired; // // Don't hassle the last few bytes, // we'll just overshoot anyway. // if ( bufferSize < 50 ) { break; } } // // Free last native buffer. // NetApiBufferFree( outBuffer ); outBuffer = NULL; } // // Upon exit from the loop, totalEntriesRead has the number of entries // read, TotalEntriesToReturn has the number available from NetUserEnum. // Formulate return codes, etc. from these values. // if ( totalEntriesRead < TotalEntriesToReturn ) { Header->Status = ERROR_MORE_DATA; } else { Header->Converter = XsPackReturnData( (LPVOID)XsSmbGetPointer( ¶meters->Buffer ), SmbGetUshort( ¶meters->BufLen ), StructureDesc, totalEntriesRead ); } IF_DEBUG(USER) { NetpKdPrint(( "XsNetUserEnum2: returning %ld entries of %ld. Resume key is now %ld\n", totalEntriesRead, TotalEntriesToReturn, resumeKey )); } // // Set up the response parameters. // SmbPutUshort( ¶meters->EntriesRead, (WORD)totalEntriesRead ); SmbPutUshort( ¶meters->TotalAvail, (WORD)( TotalEntriesToReturn )); SmbPutUlong( ¶meters->ResumeOut, resumeKey ); cleanup: ; } except( EXCEPTION_EXECUTE_HANDLER ) { Header->Status = (WORD)RtlNtStatusToDosError( GetExceptionCode() ); } NetApiBufferFree( outBuffer ); // // Determine return buffer size. // XsSetDataCount( ¶meters->BufLen, StructureDesc, Header->Converter, totalEntriesRead, Header->Status ); return STATUS_SUCCESS; } // XsNetUserEnum2 NTSTATUS XsNetUserGetGroups ( API_HANDLER_PARAMETERS ) /*++ Routine Description: This routine handles a call to NetUserGetGroups. Arguments: API_HANDLER_PARAMETERS - information about the API call. See XsTypes.h for details. Return Value: NTSTATUS - STATUS_SUCCESS or reason for failure. --*/ { NET_API_STATUS status; PXS_NET_USER_GET_GROUPS parameters = Parameters; LPTSTR nativeUserName = NULL; // Native parameters LPVOID outBuffer= NULL; DWORD entriesRead; DWORD totalEntries; DWORD entriesFilled = 0; // Conversion variables DWORD bytesRequired = 0; API_HANDLER_PARAMETERS_REFERENCE; // Avoid warnings try { IF_DEBUG(USER) { NetpKdPrint(( "XsNetUserGetGroups: header at %lx, params at %lx, " "level %ld, buf size %ld\n", Header, parameters, SmbGetUshort( ¶meters->Level ), SmbGetUshort( ¶meters->BufLen ))); } // // Translate parameters, check for errors. // if ( SmbGetUshort( ¶meters->Level ) != 0 ) { Header->Status = ERROR_INVALID_LEVEL; goto cleanup; } XsConvertTextParameter( nativeUserName, (LPSTR)XsSmbGetPointer( ¶meters->UserName ) ); // // Get the actual information from the local 32-bit call. // status = NetUserGetGroups( NULL, nativeUserName, (DWORD)SmbGetUshort( ¶meters->Level ), (LPBYTE *)&outBuffer, XsNativeBufferSize( SmbGetUshort( ¶meters->BufLen )), &entriesRead, &totalEntries ); if ( !XsApiSuccess( status )) { IF_DEBUG(API_ERRORS) { NetpKdPrint(( "XsNetUserGetGroups: NetUserGetGroups failed: %X\n", status )); } Header->Status = (WORD)status; goto cleanup; } IF_DEBUG(USER) { NetpKdPrint(( "XsNetUserGetGroups: received %ld entries at %lx\n", entriesRead, outBuffer )); } // // Do the conversion from 32- to 16-bit data. // XsFillEnumBuffer( outBuffer, entriesRead, Desc32_user_group_info_0, (LPVOID)XsSmbGetPointer( ¶meters->Buffer ), (LPVOID)XsSmbGetPointer( ¶meters->Buffer ), SmbGetUshort( ¶meters->BufLen ), Desc16_user_group_info_0, NULL, // verify function &bytesRequired, &entriesFilled, NULL ); IF_DEBUG(USER) { NetpKdPrint(( "32-bit data at %lx, 16-bit data at %lx, %ld BR," " Entries %ld of %ld\n", outBuffer, SmbGetUlong( ¶meters->Buffer ), bytesRequired, entriesFilled, totalEntries )); } // // If there is no room for one fixed structure, return NERR_BufTooSmall. // If all the entries could not be filled, return ERROR_MORE_DATA, // and return the buffer as is. GROUP_INFO_0 structures don't // need to be packed, because they have no variable data. // if ( !XsCheckBufferSize( SmbGetUshort( ¶meters->BufLen ), Desc16_user_group_info_0, FALSE // not in native format )) { Header->Status = NERR_BufTooSmall; } else if ( entriesFilled < totalEntries ) { Header->Status = ERROR_MORE_DATA; } // // Set up the response parameters. // SmbPutUshort( ¶meters->EntriesRead, (WORD)entriesFilled ); SmbPutUshort( ¶meters->TotalAvail, (WORD)totalEntries ); cleanup: ; } except( EXCEPTION_EXECUTE_HANDLER ) { Header->Status = (WORD)RtlNtStatusToDosError( GetExceptionCode() ); } NetApiBufferFree( outBuffer ); NetpMemoryFree( nativeUserName ); // // Determine return buffer size. // XsSetDataCount( ¶meters->BufLen, Desc16_user_group_info_0, Header->Converter, entriesFilled, Header->Status ); return STATUS_SUCCESS; } // XsNetUserGetGroups NTSTATUS XsNetUserGetInfo ( API_HANDLER_PARAMETERS ) /*++ Routine Description: This routine handles a call to NetUserGetInfo. Arguments: API_HANDLER_PARAMETERS - information about the API call. See XsTypes.h for details. Return Value: NTSTATUS - STATUS_SUCCESS or reason for failure. --*/ { NET_API_STATUS status; PXS_NET_USER_GET_INFO parameters = Parameters; LPTSTR nativeUserName = NULL; // Native parameters LPVOID outBuffer = NULL; LPBYTE stringLocation = NULL; // Conversion variables DWORD bytesRequired = 0; LPBYTE nativeStructureDesc; DWORD level; API_HANDLER_PARAMETERS_REFERENCE; // Avoid warnings try { IF_DEBUG(USER) { NetpKdPrint(( "XsNetUserGetInfo: header at %lx, " "params at %lx, level %ld\n", Header, parameters, SmbGetUshort( ¶meters->Level ) )); } // // Translate parameters, check for errors. // level = SmbGetUshort( ¶meters->Level ); if ( XsWordParamOutOfRange( level, 0, 2 ) && XsWordParamOutOfRange( level, 10, 11 )) { Header->Status = ERROR_INVALID_LEVEL; goto cleanup; } XsConvertTextParameter( nativeUserName, (LPSTR)XsSmbGetPointer( ¶meters->UserName ) ); // // Make the local call. // status = NetUserGetInfo( NULL, nativeUserName, level, (LPBYTE *)&outBuffer ); if ( !XsApiSuccess( status )) { IF_DEBUG(API_ERRORS) { NetpKdPrint(( "XsNetUserGetInfo: NetUserGetInfo failed: " "%X\n", status )); } Header->Status = (WORD)status; goto cleanup; } // // Use the requested level to determine the format of the // data structure. // switch ( level ) { case 0: nativeStructureDesc = Desc32_user_info_0; StructureDesc = Desc16_user_info_0; break; case 1: nativeStructureDesc = Desc32_user_info_1; StructureDesc = Desc16_user_info_1; break; case 2: { PUSER_INFO_2 usri2 = outBuffer; // // Call NetpRotateLogonHours to make sure we behave properly // during DST. // if ( usri2->usri2_logon_hours != NULL ) { if ( !NetpRotateLogonHours( usri2->usri2_logon_hours, usri2->usri2_units_per_week, FALSE ) ) { Header->Status = NERR_InternalError; goto cleanup; } } // // Truncate UserParms to 48 bytes // if (( usri2->usri2_parms != NULL ) && (wcslen(usri2->usri2_parms) > LM20_MAXCOMMENTSZ)) { *(usri2->usri2_parms + LM20_MAXCOMMENTSZ) = UNICODE_NULL; } nativeStructureDesc = Desc32_user_info_2; StructureDesc = Desc16_user_info_2; } break; case 10: nativeStructureDesc = Desc32_user_info_10; StructureDesc = Desc16_user_info_10; break; case 11: { PUSER_INFO_11 usri11 = outBuffer; // // Call NetpRotateLogonHours to make sure we behave properly // during DST. // if ( usri11->usri11_logon_hours != NULL ) { if ( !NetpRotateLogonHours( usri11->usri11_logon_hours, usri11->usri11_units_per_week, FALSE ) ) { Header->Status = NERR_InternalError; goto cleanup; } } // // Truncate UserParms to 48 bytes // if (( usri11->usri11_parms != NULL ) && (wcslen(usri11->usri11_parms) > LM20_MAXCOMMENTSZ)) { *(usri11->usri11_parms + LM20_MAXCOMMENTSZ) = UNICODE_NULL; } nativeStructureDesc = Desc32_user_info_11; StructureDesc = Desc16_user_info_11; } break; } // // Convert the structure returned by the 32-bit call to a 16-bit // structure. The last possible location for variable data is // calculated from buffer location and length. // stringLocation = (LPBYTE)( XsSmbGetPointer( ¶meters->Buffer ) + SmbGetUshort( ¶meters->BufLen ) ); status = RapConvertSingleEntry( outBuffer, nativeStructureDesc, FALSE, (LPBYTE)XsSmbGetPointer( ¶meters->Buffer ), (LPBYTE)XsSmbGetPointer( ¶meters->Buffer ), StructureDesc, TRUE, &stringLocation, &bytesRequired, Response, NativeToRap ); if ( status != NERR_Success ) { IF_DEBUG(ERRORS) { NetpKdPrint(( "XsNetUserGetInfo: RapConvertSingleEntry failed: " "%X\n", status )); } Header->Status = NERR_InternalError; goto cleanup; } IF_DEBUG(USER) { NetpKdPrint(( "32-bit data at %lx, 16-bit data at %lx, %ld BR\n", outBuffer, SmbGetUlong( ¶meters->Buffer ), bytesRequired )); } // // Determine return code based on the size of the buffer. // if ( !XsCheckBufferSize( SmbGetUshort( ¶meters->BufLen ), StructureDesc, FALSE // not in native format )) { IF_DEBUG(ERRORS) { NetpKdPrint(( "XsNetUserGetInfo: Buffer too small %ld s.b. %ld.\n", SmbGetUshort( ¶meters->BufLen ), RapStructureSize( StructureDesc, Response, FALSE ) )); } Header->Status = NERR_BufTooSmall; } else if ( bytesRequired > (DWORD)SmbGetUshort( ¶meters-> BufLen )) { IF_DEBUG(ERRORS) { NetpKdPrint(( "NetUserGetInfo: More data available.\n" )); } Header->Status = ERROR_MORE_DATA; } else { // // Pack the response data. // Header->Converter = XsPackReturnData( (LPVOID)XsSmbGetPointer( ¶meters->Buffer ), SmbGetUshort( ¶meters->BufLen ), StructureDesc, 1 ); } // // Set up the response parameters. // SmbPutUshort( ¶meters->TotalAvail, (WORD)bytesRequired ); cleanup: ; } except( EXCEPTION_EXECUTE_HANDLER ) { Header->Status = (WORD)RtlNtStatusToDosError( GetExceptionCode() ); } NetApiBufferFree( outBuffer ); NetpMemoryFree( nativeUserName ); // // Determine return buffer size. // XsSetDataCount( ¶meters->BufLen, StructureDesc, Header->Converter, 1, Header->Status ); return STATUS_SUCCESS; } // XsNetUserGetInfo NTSTATUS XsNetUserModalsGet ( API_HANDLER_PARAMETERS ) /*++ Routine Description: This routine handles a call to NetUserModalsGet. Arguments: API_HANDLER_PARAMETERS - information about the API call. See XsTypes.h for details. Return Value: NTSTATUS - STATUS_SUCCESS or reason for failure. --*/ { NET_API_STATUS status; PXS_NET_USER_MODALS_GET parameters = Parameters; LPVOID outBuffer = NULL; // Native parameters LPBYTE stringLocation = NULL; // Conversion variables DWORD bytesRequired = 0; LPBYTE nativeStructureDesc; API_HANDLER_PARAMETERS_REFERENCE; // Avoid warnings try { IF_DEBUG(USER) { NetpKdPrint(( "XsNetUserModalsGet: header at %lx, " "params at %lx, level %ld\n", Header, parameters, SmbGetUshort( ¶meters->Level ) )); } // // Check for errors. // if ( XsWordParamOutOfRange( parameters->Level, 0, 1 )) { Header->Status = ERROR_INVALID_LEVEL; goto cleanup; } // // Make the local call. // status = NetUserModalsGet( NULL, (DWORD)SmbGetUshort( ¶meters->Level ), (LPBYTE *)&outBuffer ); if ( !XsApiSuccess( status )) { IF_DEBUG(API_ERRORS) { NetpKdPrint(( "XsNetUserModalsGet: NetUserModalsGet failed: " "%X\n", status )); } Header->Status = (WORD)status; goto cleanup; } // // Use the requested level to determine the format of the // data structure. // switch ( SmbGetUshort( ¶meters->Level ) ) { case 0: nativeStructureDesc = Desc32_user_modals_info_0; StructureDesc = Desc16_user_modals_info_0; break; case 1: nativeStructureDesc = Desc32_user_modals_info_1; StructureDesc = Desc16_user_modals_info_1; break; } // // Convert the structure returned by the 32-bit call to a 16-bit // structure. The last possible location for variable data is // calculated from buffer location and length. // stringLocation = (LPBYTE)( XsSmbGetPointer( ¶meters->Buffer ) + SmbGetUshort( ¶meters->BufLen ) ); status = RapConvertSingleEntry( outBuffer, nativeStructureDesc, FALSE, (LPBYTE)XsSmbGetPointer( ¶meters->Buffer ), (LPBYTE)XsSmbGetPointer( ¶meters->Buffer ), StructureDesc, TRUE, &stringLocation, &bytesRequired, Response, NativeToRap ); if ( status != NERR_Success ) { IF_DEBUG(ERRORS) { NetpKdPrint(( "XsNetUserModalsGet: RapConvertSingleEntry failed: " "%X\n", status )); } Header->Status = NERR_InternalError; goto cleanup; } IF_DEBUG(USER) { NetpKdPrint(( "32-bit data at %lx, 16-bit data at %lx, %ld BR\n", outBuffer, SmbGetUlong( ¶meters->Buffer ), bytesRequired )); } // // Determine return code based on the size of the buffer. // if ( !XsCheckBufferSize( SmbGetUshort( ¶meters->BufLen ), StructureDesc, FALSE // not in native format )) { IF_DEBUG(ERRORS) { NetpKdPrint(( "XsNetUserModalsGet: Buffer too small.\n" )); } Header->Status = NERR_BufTooSmall; } else if ( bytesRequired > (DWORD)SmbGetUshort( ¶meters-> BufLen )) { IF_DEBUG(ERRORS) { NetpKdPrint(( "NetUserModalsGet: More data available.\n" )); } Header->Status = ERROR_MORE_DATA; } else { // // Pack the response data. // Header->Converter = XsPackReturnData( (LPVOID)XsSmbGetPointer( ¶meters->Buffer ), SmbGetUshort( ¶meters->BufLen ), StructureDesc, 1 ); } // // Set up the response parameters. // SmbPutUshort( ¶meters->TotalAvail, (WORD)bytesRequired ); cleanup: ; } except( EXCEPTION_EXECUTE_HANDLER ) { Header->Status = (WORD)RtlNtStatusToDosError( GetExceptionCode() ); } NetApiBufferFree( outBuffer ); // // Determine return buffer size. // XsSetDataCount( ¶meters->BufLen, StructureDesc, Header->Converter, 1, Header->Status ); return STATUS_SUCCESS; } // XsNetUserModalsGet NTSTATUS XsNetUserModalsSet ( API_HANDLER_PARAMETERS ) /*++ Routine Description: This routine handles a call to NetUserModalsSet. Arguments: API_HANDLER_PARAMETERS - information about the API call. See XsTypes.h for details. Return Value: NTSTATUS - STATUS_SUCCESS or reason for failure. --*/ { NET_API_STATUS status; PXS_NET_USER_MODALS_SET parameters = Parameters; DWORD nativeLevel; // Native parameters LPVOID buffer = NULL; LPDESC setInfoDesc; // Conversion variables LPDESC nativeSetInfoDesc; LPDESC nativeStructureDesc; API_HANDLER_PARAMETERS_REFERENCE; // Avoid warnings try { // // Check for errors. // if ( XsWordParamOutOfRange( parameters->Level, 0, 1 )) { Header->Status = ERROR_INVALID_LEVEL; goto cleanup; } // // First of all, the 32-bit parmnum is a bit messed up. If the level // is 2, the new parmnum is 5 plus the old parmnum. // nativeLevel = XsLevelFromParmNum( SmbGetUshort( ¶meters->Level ), SmbGetUshort( ¶meters->ParmNum )); switch ( SmbGetUshort( ¶meters->Level )) { case 0: StructureDesc = Desc16_user_modals_info_0; nativeStructureDesc = Desc32_user_modals_info_0; setInfoDesc = Desc16_user_modals_info_0_setinfo; nativeSetInfoDesc = Desc32_user_modals_info_0_setinfo; break; case 1: StructureDesc = Desc16_user_modals_info_1; nativeStructureDesc = Desc32_user_modals_info_1; setInfoDesc = Desc16_user_modals_info_1_setinfo; nativeSetInfoDesc = Desc32_user_modals_info_1_setinfo; if ( nativeLevel != (DWORD)SmbGetUshort( ¶meters->Level )) { nativeLevel += 5; } break; } status = XsConvertSetInfoBuffer( (LPBYTE)XsSmbGetPointer( ¶meters->Buffer ), SmbGetUshort( ¶meters->BufLen ), SmbGetUshort( ¶meters->ParmNum ), TRUE, TRUE, StructureDesc, nativeStructureDesc, setInfoDesc, nativeSetInfoDesc, (LPBYTE *)&buffer, NULL ); if ( status != NERR_Success ) { IF_DEBUG(ERRORS) { NetpKdPrint(( "XsNetUserModalsSet: Problem with conversion: %X\n", status )); } Header->Status = (WORD)status; goto cleanup; } // // Make the local call. // status = NetUserModalsSet( NULL, nativeLevel, buffer, NULL ); if ( !XsApiSuccess( status )) { IF_DEBUG(ERRORS) { NetpKdPrint(( "XsNetUserModalsSet: NetUserModalsSet failed: %X\n", status )); } Header->Status = (WORD)status; goto cleanup; } // // No return information for this API. // cleanup: ; } except( EXCEPTION_EXECUTE_HANDLER ) { Header->Status = (WORD)RtlNtStatusToDosError( GetExceptionCode() ); } // // If there is a native 32-bit buffer, free it. // NetpMemoryFree( buffer ); return STATUS_SUCCESS; } // XsNetUserModalsSet NTSTATUS XsNetUserPasswordSet2 ( API_HANDLER_PARAMETERS ) /*++ Routine Description: This routine handles a call to NetUserPasswordSet. This call is translated to NetUserPasswordSet2 when remotely called from a 16-bit machine. Arguments: API_HANDLER_PARAMETERS - information about the API call. See XsTypes.h for details. Return Value: NTSTATUS - STATUS_SUCCESS or reason for failure. --*/ { NET_API_STATUS status; PXS_NET_USER_PASSWORD_SET_2 parameters = Parameters; LPTSTR nativeUserName = NULL; // Native parameters UNICODE_STRING UserName; API_HANDLER_PARAMETERS_REFERENCE; // Avoid warnings try { // // Convert the username. // XsConvertTextParameter( nativeUserName, (LPSTR)XsSmbGetPointer( ¶meters->UserName ) ); RtlInitUnicodeString( &UserName, nativeUserName ); // // Check the password length. // status = XsCheckAndReplacePassword( (DWORD)( SmbGetUshort( ¶meters->PasswordLength )) ); if ( status != NERR_Success ) { IF_DEBUG(ERRORS) { NetpKdPrint(( "XsNetUserPasswordSet2: XsCheckAndReplacePassword " "failed: %X\n", status )); } goto cleanup; } status = XsChangePasswordSam( &UserName, parameters->OldPassword, parameters->NewPassword, (BOOLEAN)SmbGetUshort( ¶meters->DataEncryption ) ); // // No return data. // cleanup: ; } except( EXCEPTION_EXECUTE_HANDLER ) { Header->Status = (WORD)RtlNtStatusToDosError( GetExceptionCode() ); } NetpMemoryFree( nativeUserName ); Header->Status = (WORD)status; return STATUS_SUCCESS; } // XsNetUserPasswordSet2 NTSTATUS XsNetUserSetGroups ( API_HANDLER_PARAMETERS ) /*++ Routine Description: This routine handles a call to NetUserSetGroups. Arguments: API_HANDLER_PARAMETERS - information about the API call. See XsTypes.h for details. Return Value: NTSTATUS - STATUS_SUCCESS or reason for failure. --*/ { NET_API_STATUS status; PXS_NET_USER_SET_GROUPS parameters = Parameters; LPTSTR nativeUserName = NULL; // Native parameters LPBYTE actualBuffer = NULL; DWORD groupCount; LPBYTE stringLocation = NULL; // Conversion variables LPVOID buffer = NULL; DWORD bytesRequired = 0; LPDESC longDescriptor = NULL; LPDESC longNativeDescriptor = NULL; DWORD bufferSize; DWORD i; API_HANDLER_PARAMETERS_REFERENCE; // Avoid warnings try { IF_DEBUG(USER) { NetpKdPrint(( "XsNetUserSetGroups: header at %lx, params at %lx," "level %ld\n", Header, parameters, SmbGetUshort( ¶meters->Level ) )); } // // Translate parameters, check for errors. // if ( SmbGetUshort( ¶meters->Level ) != 0 ) { Header->Status = ERROR_INVALID_LEVEL; goto cleanup; } StructureDesc = Desc16_user_group_info_0_set; AuxStructureDesc = Desc16_user_group_info_0; XsConvertTextParameter( nativeUserName, (LPSTR)XsSmbGetPointer( ¶meters->UserName ) ); // // Use the count of group_info_0 structures to form a long // descriptor string which can be used to do all the conversion // in one pass. // groupCount = (DWORD)SmbGetUshort( ¶meters->Entries ); longDescriptor = NetpMemoryAllocate( strlen( StructureDesc ) + strlen( AuxStructureDesc ) * groupCount + 1 ); longNativeDescriptor = NetpMemoryAllocate( strlen( Desc32_user_group_info_0_set ) + strlen( Desc32_user_group_info_0 ) * groupCount + 1 ); if (( longDescriptor == NULL ) || ( longNativeDescriptor == NULL )) { IF_DEBUG(ERRORS) { NetpKdPrint(( "XsNetUserSetGroups: failed to allocate memory" )); } Header->Status = NERR_NoRoom; goto cleanup; } strcpy( longDescriptor, StructureDesc ); strcpy( longNativeDescriptor, Desc32_user_group_info_0_set ); for ( i = 0; i < groupCount; i++ ) { strcat( longDescriptor, AuxStructureDesc ); strcat( longNativeDescriptor, Desc32_user_group_info_0 ); } // // Figure out if there is enough room in the buffer for all this // data. If not, return NERR_BufTooSmall. // if ( !XsCheckBufferSize( SmbGetUshort( ¶meters->BufLen ), longDescriptor, FALSE // not in native format )) { IF_DEBUG(ERRORS) { NetpKdPrint(( "XsNetUserSetGroups: Buffer too small.\n" )); } Header->Status = NERR_BufTooSmall; goto cleanup; } // // Find out how big a buffer we need to allocate to hold the native // 32-bit version of the input data structure. // bufferSize = XsBytesForConvertedStructure( (LPBYTE)XsSmbGetPointer( ¶meters->Buffer ), longDescriptor, longNativeDescriptor, RapToNative, TRUE ); // // Allocate enough memory to hold the converted native buffer. // buffer = NetpMemoryAllocate( bufferSize ); if ( buffer == NULL ) { IF_DEBUG(ERRORS) { NetpKdPrint(( "XsNetUserSetGroups: failed to create buffer" )); } Header->Status = NERR_NoRoom; goto cleanup; } IF_DEBUG(USER) { NetpKdPrint(( "XsNetUserSetGroups: buffer of %ld bytes at %lx\n", bufferSize, buffer )); } // // Convert the buffer from 16-bit to 32-bit. // stringLocation = (LPBYTE)buffer + bufferSize; bytesRequired = 0; status = RapConvertSingleEntry( (LPBYTE)XsSmbGetPointer( ¶meters->Buffer ), longDescriptor, TRUE, buffer, buffer, longNativeDescriptor, FALSE, &stringLocation, &bytesRequired, Response, RapToNative ); if ( status != NERR_Success ) { IF_DEBUG(ERRORS) { NetpKdPrint(( "XsNetUserSetGroups: RapConvertSingleEntry failed: " "%X\n", status )); } Header->Status = NERR_InternalError; goto cleanup; } // // Check if we got all the entries. If not, we'll quit. // if ( RapAuxDataCount( buffer, Desc32_user_group_info_0_set, Both, TRUE ) != groupCount ) { Header->Status = NERR_BufTooSmall; goto cleanup; } // // If there are no entries, there's no data. Otherwise, the data comes // after an initial header structure. // if ( groupCount > 0 ) { actualBuffer = (LPBYTE)buffer + RapStructureSize( Desc32_user_group_info_0_set, Both, TRUE ); } else { actualBuffer = NULL; } // // Make the local call. // status = NetUserSetGroups( NULL, nativeUserName, (DWORD)SmbGetUshort( ¶meters->Level ), actualBuffer, (DWORD)groupCount ); if ( !XsApiSuccess( status )) { IF_DEBUG(ERRORS) { NetpKdPrint(( "XsNetUserSetGroups: NetUserSetGroups failed: %X\n", status )); } Header->Status = (WORD)status; goto cleanup; } // // There is no real return information for this API. // cleanup: ; } except( EXCEPTION_EXECUTE_HANDLER ) { Header->Status = (WORD)RtlNtStatusToDosError( GetExceptionCode() ); } NetpMemoryFree( buffer ); NetpMemoryFree( longDescriptor ); NetpMemoryFree( longNativeDescriptor ); NetpMemoryFree( nativeUserName ); return STATUS_SUCCESS; } // XsNetUserSetGroups NTSTATUS XsNetUserSetInfo2 ( API_HANDLER_PARAMETERS ) /*++ Routine Description: This routine handles a call to NetUserSetInfo2. A remote NetUserGetInfo2 call from a 16-bit machine is translated to NetUserGetInfo2, with an encrypted password. This routine has to check for a password set and handle it properly, by using level 21 to set the password. Arguments: API_HANDLER_PARAMETERS - information about the API call. See XsTypes.h for details. Return Value: NTSTATUS - STATUS_SUCCESS or reason for failure. --*/ { NET_API_STATUS status = NO_ERROR; PXS_NET_USER_SET_INFO_2 parameters = Parameters; LPTSTR nativeUserName = NULL; // Native parameters LPVOID buffer = NULL; WORD bufLen; DWORD level; BYTE newPassword[ENCRYPTED_PWLEN]; LPDESC setInfoDesc; // Conversion variables LPVOID nativePasswordArea = NULL; // Points to Unicode or encrypted. LPDESC nativeSetInfoDesc; LPDESC nativeStructureDesc; LPUSER_INFO_2 user = NULL; PUSER_16_INFO_1 user16 = NULL; USER_INFO_1020 usri1020; BOOLEAN changePassword = FALSE; BOOLEAN changeUserInfo = FALSE; BOOLEAN encryptionSupported = TRUE; WORD parmNum; PUSER_INFO_2 Susri2 = NULL; // // avoid warnings; // API_HANDLER_PARAMETERS_REFERENCE; try { bufLen = SmbGetUshort( ¶meters->BufLen ); level = SmbGetUshort( ¶meters->Level ); parmNum = SmbGetUshort( ¶meters->ParmNum ); IF_DEBUG(USER) { NetpKdPrint(( "XsNetUserSetInfo2: header at " FORMAT_LPVOID ", " "params at " FORMAT_LPVOID ",\n " "level " FORMAT_DWORD ", parmnum " FORMAT_DWORD ", " "buflen " FORMAT_WORD_ONLY "\n", Header, parameters, level, parmNum, bufLen )); } // // Translate parameters // XsConvertTextParameter( nativeUserName, (LPSTR)XsSmbGetPointer( ¶meters->UserName ) ); // // Check if password is encrypted. We know for a fact that dos redirs // don't support encryption // encryptionSupported = (BOOLEAN) ( SmbGetUshort( ¶meters->DataEncryption ) == TRUE ); // // Determine descriptor strings based on level. // switch ( level ) { case 1: StructureDesc = Desc16_user_info_1; setInfoDesc = Desc16_user_info_1_setinfo; if ( encryptionSupported ) { nativeStructureDesc = Desc32_user_info_1; nativeSetInfoDesc = Desc32_user_info_1_setinfo; } else { nativeStructureDesc = Desc32_user_info_1_NC; nativeSetInfoDesc = Desc32_user_info_1_setinfo_NC; } break; case 2: StructureDesc = Desc16_user_info_2; setInfoDesc = Desc16_user_info_2_setinfo; if ( encryptionSupported ) { nativeStructureDesc = Desc32_user_info_2; nativeSetInfoDesc = Desc32_user_info_2_setinfo; } else { nativeStructureDesc = Desc32_user_info_2_NC; nativeSetInfoDesc = Desc32_user_info_2_setinfo_NC; } break; default: status = ERROR_INVALID_LEVEL; goto cleanup; } if (parmNum != USER_PASSWORD_PARMNUM) { status = XsConvertSetInfoBuffer( (LPBYTE)XsSmbGetPointer( ¶meters->Buffer ), bufLen, parmNum, TRUE, // yes, convert strings TRUE, // yes, meaningless input pointers StructureDesc, nativeStructureDesc, setInfoDesc, nativeSetInfoDesc, (LPBYTE *)&buffer, NULL // don't need output buffer size ); if ( status != NERR_Success ) { IF_DEBUG(ERRORS) { NetpKdPrint(( "XsNetUserSetInfo2: Problem with conversion: " FORMAT_API_STATUS "\n", status )); } goto cleanup; } } else { XsConvertTextParameter( buffer, (LPSTR)XsSmbGetPointer( ¶meters->Buffer ) ); } NetpAssert( buffer != NULL ); // // Check the password length. A value of -1 means caller wants us // to compute the length; see XsNetUserSetInfo below. // if ( parmNum == PARMNUM_ALL || parmNum == USER_PASSWORD_PARMNUM) { WORD passwordLength = SmbGetUshort( ¶meters->PasswordLength ); if (parmNum == PARMNUM_ALL) { LPUSER_INFO_2 userInfo = (LPVOID) buffer; // Native structure. nativePasswordArea = userInfo->usri2_password; // May be NULL. } else { nativePasswordArea = buffer; // Entire native buffer. if (nativePasswordArea == NULL) { status = ERROR_INVALID_PARAMETER; goto cleanup; } } if (passwordLength == (WORD)(-1)) { if (parameters->DataEncryption) { parameters->PasswordLength = ENCRYPTED_PWLEN; } else if (nativePasswordArea != NULL) { // Unencrypted, count is number of chars, w/o null char. parameters->PasswordLength = (USHORT)wcslen( nativePasswordArea ); } else { parameters->PasswordLength = 0; } } status = XsCheckAndReplacePassword( (DWORD)( SmbGetUshort( ¶meters->PasswordLength )) ); if ( status != NERR_Success ) { IF_DEBUG(ERRORS) { NetpKdPrint(( "XsNetUserSetInfo2: XsCheckAndReplacePassword " "failed: " FORMAT_API_STATUS "\n", status )); } goto cleanup; } } // // If necessary, do work with passwords. Also, translate the parmnum to // an info level. // switch( parmNum ) { case PARMNUM_ALL: // // Get the encrypted password. // user16 = (PUSER_16_INFO_1)XsSmbGetPointer( ¶meters->Buffer ); RtlCopyMemory( newPassword, user16->usri1_password, ENCRYPTED_PWLEN ); user = (LPUSER_INFO_2)buffer; user->usri2_password = NULL; if ( level == 2 && user->usri2_logon_hours != NULL ) { // // Call NetpRotateLogonHours to make sure we behave properly // during DST. // if ( !NetpRotateLogonHours( user->usri2_logon_hours, user->usri2_units_per_week, TRUE ) ) { status = ERROR_INVALID_PARAMETER; goto cleanup; } } changePassword = TRUE; changeUserInfo = TRUE; break; case USER_PASSWORD_PARMNUM: // // We will use level 21 for changing passwords. // // // Get the encrypted password. // RtlCopyMemory( newPassword, (PVOID)XsSmbGetPointer( ¶meters->Buffer ), ENCRYPTED_PWLEN ); changePassword = TRUE; break; case USER_LOGON_HOURS_PARMNUM: usri1020.usri1020_units_per_week = UNITS_PER_WEEK; usri1020.usri1020_logon_hours = (LPBYTE)XsSmbGetPointer( ¶meters->Buffer ); // // Call NetpRotateLogonHours to make sure we behave properly // during DST. // if ( !NetpRotateLogonHours( usri1020.usri1020_logon_hours, usri1020.usri1020_units_per_week, TRUE ) ) { status = ERROR_INVALID_PARAMETER; goto cleanup; } // // Lack of break is intentional // default: changeUserInfo = TRUE; level = PARMNUM_BASE_INFOLEVEL + parmNum; break; } // // Bug 114883 // Downlevel clients cannot set more than 48 wchars and if the server // did have more than 48 wchars, we were truncating it! We merge the // data that the client sent with what exists on the server // if ((buffer != NULL) && (changeUserInfo) && ((level == 2) || (parmNum == USER_PARMS_PARMNUM))) { PUSER_INFO_2 Cusri2 = NULL; PUSER_INFO_1013 Cusri1013 = NULL; LPWSTR UserParms = NULL; // Get the pointer to the client's userparms if (level == 2) { Cusri2 = buffer; UserParms = Cusri2->usri2_parms; } else { Cusri1013 = buffer; UserParms = Cusri1013->usri1013_parms; } // // Make the local call. // status = NetUserGetInfo( NULL, nativeUserName, level, (LPBYTE *)&Susri2 ); if ( !XsApiSuccess( status )) { IF_DEBUG(API_ERRORS) { NetpKdPrint(( "XsNetUserGetInfo: NetUserGetInfo failed: " "%X\n", status )); } goto cleanup; } // If the server userparms field is > 48, we want to do something special if (( Susri2->usri2_parms != NULL ) && (wcslen(Susri2->usri2_parms) > LM20_MAXCOMMENTSZ)) { // // We need to merge the returned bytes with the local ones // UINT Length = 0; if ( UserParms != NULL ) { // // Just to be safe, we never over-write more than 48 wchars. // Length = wcslen(UserParms); if (Length > LM20_MAXCOMMENTSZ) { Length = LM20_MAXCOMMENTSZ; } } // we copy the bytes that the client sent, but only upto // 48 wchars. RtlCopyMemory( Susri2->usri2_parms, UserParms, Length * sizeof(WCHAR)); // From Length to LM20_MAXCOMMENTSZ, we pad with blanks while (Length < LM20_MAXCOMMENTSZ) { Susri2->usri2_parms[Length++] = L' '; } // Save the merged user parms if (level == 2 ) { Cusri2->usri2_parms = Susri2->usri2_parms; } else { Cusri1013->usri1013_parms = Susri2->usri2_parms; } } } // // Change user infos other than the password // if ( changeUserInfo ) { status = NetUserSetInfo( NULL, nativeUserName, level, buffer, NULL ); if ( !XsApiSuccess( status )) { IF_DEBUG(ERRORS) { NetpKdPrint(( "XsNetUserSetInfo2: NetUserSetInfo failed: %X\n", status )); } goto cleanup; } // // If there was a Macintosh primary group field for this user, then // set the primary group. // if ( (level == 2) && NetpIsMacPrimaryGroupFieldValid( (LPCTSTR)user->usri2_parms ) ) { NET_API_STATUS status1; status1 = XsSetMacPrimaryGroup( (LPCTSTR)nativeUserName, (LPCTSTR)user->usri2_parms ); if ( !XsApiSuccess( status1 )) { IF_DEBUG(ERRORS) { NetpKdPrint(( "XsNetUserSetInfo2: SetMacPrimaryGroup " "failed: %X\n", status1 )); } } } else if ( (level == USER_PARMS_INFOLEVEL) && NetpIsMacPrimaryGroupFieldValid( (LPCTSTR)((LPUSER_INFO_1013)buffer)->usri1013_parms ) ) { NET_API_STATUS status1; status1 = XsSetMacPrimaryGroup( (LPCTSTR)nativeUserName, (LPCTSTR)((LPUSER_INFO_1013)buffer)->usri1013_parms ); if ( !XsApiSuccess( status1 )) { IF_DEBUG(ERRORS) { NetpKdPrint(( "XsNetUserSetInfo2: SetMacPrimaryGroup " "failed: %X\n", status1 )); } } } } // // If there is a pending password change, do it now. // if ( changePassword ) { USER_INFO_21 user21; if ( !encryptionSupported ) { // // Do not change password if user sent all blanks. Clear text // passwords are only 14 bytes (LM20_PWLEN) long. // if ( RtlCompareMemory( newPassword, NULL_USERSETINFO_PASSWD, LM20_PWLEN ) == LM20_PWLEN ) { status = NERR_Success; goto cleanup; } // // Change clear text password to OWF // (VOID) RtlCalculateLmOwfPassword( (PLM_PASSWORD) newPassword, (PLM_OWF_PASSWORD) user21.usri21_password ); } else { BYTE NullOwfPassword[ENCRYPTED_PWLEN]; // // Decrypt doubly encrypted password with the encryption key // provided creating an OWF encrypted password. // (VOID) RtlDecryptLmOwfPwdWithLmSesKey( (PENCRYPTED_LM_OWF_PASSWORD) newPassword, (PLM_SESSION_KEY) Header->EncryptionKey, (PLM_OWF_PASSWORD) user21.usri21_password ); // // Generate the NULL Owf Password. // (VOID) RtlCalculateLmOwfPassword( (PLM_PASSWORD) NULL_USERSETINFO_PASSWD, (PLM_OWF_PASSWORD) NullOwfPassword ); // // Compare the Owf password the client sent and the Owf password // for the NULL password. Do not change the password if this is // the case. // if ( RtlCompareMemory( user21.usri21_password, NullOwfPassword, ENCRYPTED_PWLEN ) == ENCRYPTED_PWLEN ) { status = NERR_Success; goto cleanup; } } status = NetUserSetInfo( NULL, nativeUserName, 21, (LPBYTE)&user21, NULL ); if ( !XsApiSuccess( status )) { IF_DEBUG(ERRORS) { NetpKdPrint(( "XsNetUserSetInfo2: NetUserSetInfo failed: " "%X\n", status )); } goto cleanup; } } // // No return information for this API. // cleanup: ; } except( EXCEPTION_EXECUTE_HANDLER ) { status = (WORD)RtlNtStatusToDosError( GetExceptionCode() ); } // // If there is a native 32-bit buffer, free it. // if (Susri2) NetApiBufferFree( Susri2); Header->Status = (WORD)status; NetpMemoryFree( buffer ); NetpMemoryFree( nativeUserName ); return STATUS_SUCCESS; } // XsNetUserSetInfo2 NTSTATUS XsNetUserSetInfo ( API_HANDLER_PARAMETERS ) /*++ Routine Description: This routine handles a call to NetUserSetInfo. Since this is a subset of the newer NetUserSetInfo2, we just convert into a call to that. Arguments: API_HANDLER_PARAMETERS - information about the API call. See XsTypes.h for details. Return Value: NTSTATUS - STATUS_SUCCESS or reason for failure. --*/ { WORD dataEncryption; WORD bufLen; WORD level; NTSTATUS ntStatus; WORD parmNum; PXS_NET_USER_SET_INFO subsetParameters = Parameters; XS_NET_USER_SET_INFO_2 supersetParameters; bufLen = SmbGetUshort( &subsetParameters->BufLen ); dataEncryption = SmbGetUshort( &subsetParameters->DataEncryption ); level = SmbGetUshort( &subsetParameters->Level ); parmNum = SmbGetUshort( &subsetParameters->ParmNum ); try { IF_DEBUG(USER) { NetpKdPrint(( "XsNetUserSetInfo: header at " FORMAT_LPVOID ", " "params at " FORMAT_LPVOID ",\n level " FORMAT_DWORD ", " "parmnum " FORMAT_DWORD ", buflen " FORMAT_LONG "\n", Header, subsetParameters, (DWORD) level, (DWORD) parmNum, (LONG) bufLen )); } // // Create parms for XsNetUserSetInfo2()... // supersetParameters.Buffer = subsetParameters->Buffer; supersetParameters.UserName = subsetParameters->UserName; SmbPutUshort( &supersetParameters.Level, level ); SmbPutUshort( &supersetParameters.BufLen, bufLen ); SmbPutUshort( &supersetParameters.ParmNum, parmNum ); SmbPutUshort( &supersetParameters.DataEncryption, dataEncryption ); // // Set info 2 will calc password length for us if we give it -1. // SmbPutUshort( &supersetParameters.PasswordLength, (WORD)(-1) ); // // Invoke new version of API. // ntStatus = XsNetUserSetInfo2( Header, &supersetParameters, StructureDesc, AuxStructureDesc ); } except( EXCEPTION_EXECUTE_HANDLER ) { ntStatus = GetExceptionCode(); } return (ntStatus); } // XsNetUserSetInfo