/*++ Copyright (c) 1991 Microsoft Corporation Module Name: vrnetapi.c Abstract: This module contains routines which support Vdm Redir lanman APIs and private functions: VrGetCDNames VrGetComputerName VrGetDomainName VrGetLogonServer VrGetUserName VrNetGetDCName VrNetMessageBufferSend VrNetNullTransactApi VrNetRemoteApi OemToUppercaseUnicode VrNetServiceControl VrNetServiceEnum VrNetServerEnum VrNetTransactApi VrNetUseAdd VrNetUseDel VrNetUseEnum VrNetUseGetInfo VrNetWkstaGetInfo (DumpWkstaInfo) VrNetWkstaSetInfo VrReturnAssignMode VrSetAssignMode VrGetAssignListEntry VrDefineMacro VrBreakMacro (VrpTransactVdm) EncryptPassword Author: Richard L Firth (rfirth) 21-Oct-1991 Revision History: 21-Oct-1991 rfirth Created 02-May-1994 rfirth Upped password limit (& path length limit) to LM20_ values for VrDefineMacro --*/ #include #include // ASSERT, DbgPrint #include #include #include // x86 virtual machine definitions #include #include // common Vdm Redir stuff #include // LM20_PATHLEN #include // lan manager error codes #include // USE_LOTS_OF_FORCE #include // NetWkstaGetInfo #include // SV_TYPE_ALL #include // NetApiBufferFree #include // prototypes #include // VrRemoteApi prototypes #include // structures in apistruc.h are not dword-only #include // tr_packet #include // switch back on structure packing #include // remotable API numbers #include // remotable API descriptors #include // remotable API descriptor characters #include // RxpTransactSmb #include // XS_NET_USE_ADD #include // XS_PARAMETER_HEADER #include // XsNetUseAdd etc #include // Dos still dealing with ASCII #include // NetpNtStatusToApiStatus() #include "vrputil.h" // VrpMapDosError() #include "vrdebug.h" // VrDebugFlags etc #include "dlstruct.h" // down-level structures #include // RxNetUser... #include // USER_PASSWORD_PARMNUM #include // Needed by NetUserPasswordSet // // private routine prototypes // #if DBG VOID DumpWkstaInfo( IN DWORD Level, IN LPBYTE Buffer ); #endif NET_API_STATUS VrpTransactVdm( IN BOOL NullSessionFlag ); #if DBG PRIVATE VOID DumpTransactionPacket( IN struct tr_packet* TransactionPacket, IN BOOL IsInput, IN BOOL DumpData ); #endif // // external functions // NET_API_STATUS GetLanmanSessionKey( IN LPWSTR ServerName, OUT LPBYTE pSessionKey ); // // internal functions (not necessarily private) // BOOL OemToUppercaseUnicode( IN LPSTR AnsiStringPointer, OUT LPWSTR UnicodeStringPointer, IN DWORD MaxLength ); BOOL EncryptPassword( IN LPWSTR ServerName, IN OUT LPBYTE Password ); // // public routines // // // net APIs now unicode // #define NET_UNICODE VOID VrGetCDNames( VOID ) /*++ Routine Description: Performs the private redir function to get the computer and domain names. These are usually stored in the redir after they are read out of lanman.ini NOTE: This code assumes that the pointers are valid and point to valid, writable memory which has enough reserved space for the types of strings to be written Arguments: None. All arguments are extracted from the Vdm context registers/memory The dos redir gets passed a buffer in es:di which contains 3 far pointers to: place to store computer name place to store primary domain controller name place to store logon domain name Return Value: None. Results returned via VDM registers or in VDM memory, according to request Note that in the dos redir function, there is no return code, so if this routine fails the results will be unpredictable --*/ { struct I_CDNames* structurePointer; LPSTR stringPointer; LPWSTR infoString; NET_API_STATUS rc1; NET_API_STATUS rc2; ANSI_STRING ansiString; UNICODE_STRING unicodeString; LPWKSTA_INFO_100 wkstaInfo = NULL; LPWKSTA_USER_INFO_1 userInfo = NULL; CHAR ansiBuf[LM20_CNLEN+1]; NTSTATUS status; register DWORD len; #if DBG IF_DEBUG(NETAPI) { DbgPrint("VrGetCDNames\n"); IF_DEBUG(BREAKPOINT) { DbgBreakPoint(); } } #endif rc1 = NetWkstaGetInfo(NULL, 100, (LPBYTE*)&wkstaInfo); rc2 = NetWkstaUserGetInfo(0, 1, (LPBYTE*)&userInfo); ansiBuf[0] = 0; ansiString.MaximumLength = sizeof(ansiBuf); ansiString.Length = 0; ansiString.Buffer = ansiBuf; structurePointer = (struct I_CDNames*)POINTER_FROM_WORDS(getES(), getDI()); stringPointer = POINTER_FROM_POINTER(&structurePointer->CDN_pszComputer); if (stringPointer) { *stringPointer = 0; if (rc1 == NERR_Success) { infoString = (LPWSTR)wkstaInfo->wki100_computername; len = wcslen(infoString); if (len <= LM20_CNLEN) { RtlInitUnicodeString(&unicodeString, infoString); status = RtlUnicodeStringToAnsiString(&ansiString, &unicodeString, FALSE); if (NT_SUCCESS(status)) { RtlCopyMemory(stringPointer, ansiBuf, len+1); _strupr(stringPointer); } } } } stringPointer = POINTER_FROM_POINTER(&structurePointer->CDN_pszPrimaryDomain); if (stringPointer) { *stringPointer = 0; if (rc1 == NERR_Success) { infoString = (LPWSTR)wkstaInfo->wki100_langroup; len = wcslen(infoString); if (len <= LM20_CNLEN) { RtlInitUnicodeString(&unicodeString, infoString); status = RtlUnicodeStringToAnsiString(&ansiString, &unicodeString, FALSE); if (NT_SUCCESS(status)) { RtlCopyMemory(stringPointer, ansiBuf, len+1); _strupr(stringPointer); } } } } stringPointer = POINTER_FROM_POINTER(&structurePointer->CDN_pszLogonDomain); if (stringPointer) { *stringPointer = 0; if (rc2 == NERR_Success) { infoString = (LPWSTR)userInfo->wkui1_logon_domain; len = wcslen(infoString); if (len <= LM20_CNLEN) { RtlInitUnicodeString(&unicodeString, infoString); status = RtlUnicodeStringToAnsiString(&ansiString, &unicodeString, FALSE); if (NT_SUCCESS(status)) { RtlCopyMemory(stringPointer, ansiBuf, len+1); _strupr(stringPointer); } } } } if (wkstaInfo) { NetApiBufferFree((LPVOID)wkstaInfo); } if (userInfo) { NetApiBufferFree((LPVOID)userInfo); } #if DBG IF_DEBUG(NETAPI) { DbgPrint("VrGetCDNames: computername=%s, PDCname=%s, logon domain=%s\n\n", POINTER_FROM_POINTER(&structurePointer->CDN_pszComputer) ? POINTER_FROM_POINTER(&structurePointer->CDN_pszComputer) : "", POINTER_FROM_POINTER(&structurePointer->CDN_pszPrimaryDomain) ? POINTER_FROM_POINTER(&structurePointer->CDN_pszPrimaryDomain) : "", POINTER_FROM_POINTER(&structurePointer->CDN_pszLogonDomain) ? POINTER_FROM_POINTER(&structurePointer->CDN_pszLogonDomain) : "" ); } #endif } VOID VrGetComputerName( VOID ) /*++ Routine Description: Performs the private redir function to return the computer name stored in the redir Arguments: ENTRY ES:DI = buffer to copy computer name into Return Value: None. --*/ { BOOL ok; CHAR nameBuf[MAX_COMPUTERNAME_LENGTH+1]; DWORD nameLen; #if DBG IF_DEBUG(NETAPI) { DbgPrint("VrGetComputerName\n"); } #endif nameLen = sizeof(nameBuf)-1; ok = GetComputerName(nameBuf, &nameLen); if (!ok) { SET_ERROR(ERROR_NOT_SUPPORTED); } else { if (nameLen > LM20_CNLEN) { SET_ERROR(NERR_BufTooSmall); #if DBG IF_DEBUG(NETAPI) { DbgPrint("VrGetComputerName returning ERROR %d!\n", getAX()); } #endif } else { strcpy(LPSTR_FROM_WORDS(getES(), getDI()), nameBuf); setAX(0); setCF(0); #if DBG IF_DEBUG(NETAPI) { DbgPrint("VrGetComputerName returning %s\n", nameBuf); } #endif } } } VOID VrGetDomainName( VOID ) /*++ Routine Description: Performs the private redir function to return the primary domain name. This info is stored in the redir after being read from lanman.ini at configuration time Arguments: None. All arguments are extracted from the Vdm context registers/memory Return Value: None. Results returned via VDM registers or in VDM memory, according to request --*/ { #if DBG IF_DEBUG(NETAPI) { DbgPrint("VrGetDomainName\n"); IF_DEBUG(BREAKPOINT) { DbgBreakPoint(); } } #endif #if DBG IF_DEBUG(NETAPI) { DbgPrint("VrGetDomainName - unsupported SVC\n"); } #endif SET_ERROR(ERROR_NOT_SUPPORTED); } VOID VrGetLogonServer( VOID ) /*++ Routine Description: Performs the private redir function to return the name of the computer which logged this user onto the network Arguments: None. All arguments are extracted from the Vdm context registers/memory Return Value: None. Results returned via VDM registers or in VDM memory, according to request --*/ { #if DBG IF_DEBUG(NETAPI) { DbgPrint("VrGetLogonServer\n"); IF_DEBUG(BREAKPOINT) { DbgBreakPoint(); } } #endif #if DBG IF_DEBUG(NETAPI) { DbgPrint("VrGetLogonServer - unsupported SVC\n"); } #endif SET_ERROR(ERROR_NOT_SUPPORTED); } VOID VrGetUserName( VOID ) /*++ Routine Description: Performs the private redir function to return the logged on user name which is normally stored in the redir Arguments: ENTRY BX = 0 call doesn't care about buffer length (NetGetEnumInfo) BX = 1 call is for NetGetUserName, which does care about buffer length CX = buffer length, if BX = 1 ES:DI = buffer Return Value: None. --*/ { NET_API_STATUS status; LPBYTE buffer; LPWKSTA_USER_INFO_0 pInfo; BOOL itFits; DWORD len; #if DBG IF_DEBUG(NETAPI) { DbgPrint("VrGetUserName\n"); IF_DEBUG(BREAKPOINT) { DbgBreakPoint(); } } #endif status = NetWkstaUserGetInfo(NULL, 0, &buffer); if (status == NERR_Success) { pInfo = (LPWKSTA_USER_INFO_0)buffer; #ifdef DBCS /*fix for DBCS char sets*/ len = (DWORD)NetpUnicodeToDBCSLen(pInfo->wkui0_username); #else // !DBCS len = (DWORD)wcslen(pInfo->wkui0_username); #endif // !DBCS if (getBX()) { itFits = (len) <= (DWORD)getCX()-1; if (itFits) { SET_SUCCESS(); } else { SET_ERROR(NERR_BufTooSmall); } } else { itFits = TRUE; } if (itFits) { #ifdef DBCS /*fix for DBCS charsets*/ NetpCopyWStrToStrDBCS(LPSTR_FROM_WORDS(getES(), getDI()), pInfo->wkui0_username); #else // !DBCS NetpCopyWStrToStr(LPSTR_FROM_WORDS(getES(), getDI()), pInfo->wkui0_username); #endif // !DBCS } NetApiBufferFree(buffer); } else { SET_ERROR((WORD)status); } } VOID VrNetGetDCName( VOID ) /*++ Routine Description: Performs local NetGetDCName on behalf of the Vdm client Arguments: None. All arguments are extracted from the Vdm context registers/memory Return Value: None. Results returned via VDM registers or in VDM memory, according to request --*/ { #if DBG IF_DEBUG(NETAPI) { DbgPrint("VrGetDCName\n"); IF_DEBUG(BREAKPOINT) { DbgBreakPoint(); } } #endif #if DBG IF_DEBUG(NETAPI) { DbgPrint("VrNetGetDCName - unsupported SVC\n"); } #endif SET_ERROR(ERROR_NOT_SUPPORTED); } VOID VrNetMessageBufferSend( VOID ) /*++ Routine Description: Performs local NetMessageBufferSend on behalf of the Vdm client Arguments: Function 5F40h ENTRY DS:DX = NetMessageBufferSendStruc: char far* NMBSS_NetName; char far* NMBSS_Buffer; unsigned int NMBSS_BufSize; Return Value: None. Results returned via VDM registers: CF = 0 Success CF = 1 AX = Error code --*/ { NTSTATUS ntstatus; NET_API_STATUS status; XS_PARAMETER_HEADER header; XS_NET_MESSAGE_BUFFER_SEND parameters; struct NetMessageBufferSendStruc* structurePointer; #if DBG IF_DEBUG(NETAPI) { DbgPrint("VrNetMessageBufferSend\n"); IF_DEBUG(BREAKPOINT) { DbgBreakPoint(); } } #endif structurePointer = (struct NetMessageBufferSendStruc*) POINTER_FROM_WORDS(getDS(), getDX()); parameters.Recipient = LPSTR_FROM_POINTER(&structurePointer->NMBSS_NetName); parameters.Buffer = LPBYTE_FROM_POINTER(&structurePointer->NMBSS_Buffer); parameters.BufLen = READ_WORD(&structurePointer->NMBSS_BufSize); header.Status = 0; header.ClientMachineName = NULL; header.ClientTransportName = NULL; ntstatus = XsNetMessageBufferSend(&header, ¶meters, NULL, NULL); if (ntstatus != STATUS_SUCCESS) { status = NetpNtStatusToApiStatus(ntstatus); } else { status = header.Status; } if (status) { SET_ERROR(VrpMapDosError(status)); #if DBG IF_DEBUG(NETAPI) { DbgPrint("VrNetMessageBufferSend: returning %d\n", status); } #endif } else { setCF(0); } } VOID VrNetNullTransactApi( VOID ) /*++ Routine Description: Performs a transaction IOCTL using the NULL session for a Vdm client Arguments: None. All arguments are extracted from the Vdm context registers/memory Return Value: None. Results returned via VDM registers or in VDM memory, according to request --*/ { DWORD status; #if DBG IF_DEBUG(NETAPI) { DbgPrint("VrNetNullTransactApi\n"); IF_DEBUG(BREAKPOINT) { DbgBreakPoint(); } } #endif status = VrpTransactVdm(TRUE); if (status) { SET_ERROR((WORD)status); } else { setCF(0); } } VOID VrNetRemoteApi( VOID ) /*++ Routine Description: This routine is invoked when a dos program in a Vdm makes a lanman API call which in turn calls the redir NetIRemoteAPI function to send the request to a lanman server Arguments: None. All arguments are extracted from the Vdm context registers/memory Return Value: None. Results returned via VDM registers or in VDM memory, according to request --*/ { DWORD ApiNumber; BOOL NullSessionFlag; NET_API_STATUS status; LPBYTE ServerNamePointer = LPBYTE_FROM_WORDS(getES(), getBX()); #define ParameterDescriptor LPSTR_FROM_WORDS(getDS(), getSI()) #define DataDescriptor LPSTR_FROM_WORDS(getDS(), getDI()) #define AuxDescriptor LPSTR_FROM_WORDS(getDS(), getDX()) ApiNumber = (DWORD)getCX(); NullSessionFlag = ApiNumber & USE_NULL_SESSION_FLAG; ApiNumber &= ~USE_NULL_SESSION_FLAG; // // get pointers to the various descriptors which are readable from 32-bit // context and call the routine to perform the 16-bit remote api function // #if DBG IF_DEBUG(NETAPI) { DbgPrint("VrNetRemoteApi: ApiNumber=%d, ServerName=%s\n" "ParmDesc=%s, DataDesc=%s, AuxDesc=%s\n", ApiNumber, LPSTR_FROM_POINTER(ServerNamePointer), ParameterDescriptor, DataDescriptor, AuxDescriptor ); } #endif // // RLF 04/21/93 // // Yikes! It looks like passwords entered in DOS programs are not getting // encrypted. What a security hole. Let's block it- // // if this is a NetUserPasswordSet2 then we call the RxNetUserPasswordSet // function to remotely change the password. This function takes care of // correctly encrypting the password and sending the request over the NULL // session. In this case, ServerNamePointer points at the server name // parameter in the following PASCAL calling convention stack frame in DOS // memory: // // tos: far pointer to new password (OEM string) // far pointer to old password (OEM string) // far pointer to user name (OEM string) // far pointer to server name (OEM string) <- ServerNamePointer // if (ApiNumber == API_WUserPasswordSet2) { WCHAR uServerName[LM20_UNCLEN + 1]; WCHAR uUserName[LM20_UNLEN + 1]; WCHAR uOldPassword[LM20_PWLEN + 1]; WCHAR uNewPassword[LM20_PWLEN + 1]; NTSTATUS ntStatus; DWORD length; LPSTR ansiStringPointer; ansiStringPointer = LPSTR_FROM_POINTER(ServerNamePointer); ntStatus = RtlOemToUnicodeN(uServerName, sizeof(uServerName) - sizeof(uServerName[0]), &length, ansiStringPointer, strlen(ansiStringPointer) ); if (NT_SUCCESS(ntStatus)) { uServerName[length/sizeof(uServerName[0])] = 0; } else { status = ERROR_INVALID_PARAMETER; goto VrNetRemoteApi_exit; } // // copy, upper case and convert to UNICODE, user name // ServerNamePointer -= sizeof(LPSTR); ansiStringPointer = LPSTR_FROM_POINTER(ServerNamePointer); if (!OemToUppercaseUnicode(ansiStringPointer, uUserName, ARRAY_ELEMENTS(uUserName) - 1)) { status = ERROR_INVALID_PARAMETER; goto VrNetRemoteApi_exit; } // // copy, upper case and convert to UNICODE, old password // ServerNamePointer -= sizeof(LPSTR); ansiStringPointer = LPSTR_FROM_POINTER(ServerNamePointer); if (!OemToUppercaseUnicode(ansiStringPointer, uOldPassword, ARRAY_ELEMENTS(uOldPassword) - 1)) { status = ERROR_INVALID_PARAMETER; goto VrNetRemoteApi_exit; } // // copy, upper case and convert to UNICODE, new password // ServerNamePointer -= sizeof(LPSTR); ansiStringPointer = LPSTR_FROM_POINTER(ServerNamePointer); if (!OemToUppercaseUnicode(ansiStringPointer, uNewPassword, ARRAY_ELEMENTS(uNewPassword) - 1)) { status = ERROR_INVALID_PARAMETER; goto VrNetRemoteApi_exit; } // // make the call to the down-level password set function // status = RxNetUserPasswordSet((LPTSTR)uServerName, (LPTSTR)uUserName, (LPTSTR)uOldPassword, (LPTSTR)uNewPassword ); } else { CHAR aPassword[ENCRYPTED_PWLEN]; LPBYTE parameterPointer; LPBYTE passwordPointer = NULL; DWORD passwordEncrypted; DWORD passwordLength; // // we are going to remote the API as requested. However, if the request // is NetUserAdd2 or NetUserSetInfo2 then we check to see if a password // is being sent over the wire. We may need to encrypt the password on // behalf of the DOS app // if (ApiNumber == API_WUserAdd2 || ApiNumber == API_WUserSetInfo2) { // // API request is to add a user or set a user's info. The former will // contain a password which needs to be encrypted, the latter MAY // contain a password which needs to be encrypted if the request is // to set all the information, or just the password // DWORD level; DWORD parmNum = PARMNUM_ALL; LPBYTE dataLengthPointer; // // in the case of NetUserAdd2, the stack frame in DOS memory looks like // this: // // tos: original password length // password encryption flag // buffer length // far pointer to buffer containing user_info_1 or user_info_2 // info level // far pointer to server name <- ServerNamePointer // // and the NetUserSetInfo2 stack looks like this: // // tos: original password length // password encryption flag // parameter number // buffer length // far pointer to user_info_1 or user_info_2 or single parameter // info level // far pointer to user name // far pointer to server name <- ServerNamePointer // parameterPointer = ServerNamePointer; if (ApiNumber == API_WUserSetInfo2) { // // for SetInfo: bump the stack parameter pointer past the user // name pointer // parameterPointer -= sizeof(LPSTR); } // // bump the stack parameter pointer to the level parameter and // retrieve it // parameterPointer -= sizeof(WORD); level = (DWORD)READ_WORD(parameterPointer); // // bump the stack parameter pointer to point to the buffer address // parameterPointer -= sizeof(LPBYTE); passwordPointer = parameterPointer; // // move the stack parameter pointer to the password encryption flag // in the case of UserAdd2 or the parmNum parameter in the case of // SetInfo2. If SetInfo2, retrieve the parmNum parameter and move // the parameterPointer to point at the password encryption flag // parameterPointer -= sizeof(WORD); if (ApiNumber == API_WUserSetInfo2) { dataLengthPointer = parameterPointer; } parameterPointer -= sizeof(WORD); if (ApiNumber == API_WUserSetInfo2) { parmNum = (DWORD)READ_WORD(parameterPointer); parameterPointer -= sizeof(WORD); } // // get the password encryption flag and cleartext password length // from the DOS stack frame. Leave the stack frame pointer pointing // at the location for the encryption flag: we'll need to replace // this with TRUE and restore it before we return control // passwordEncrypted = (DWORD)READ_WORD(parameterPointer); passwordLength = (DWORD)READ_WORD(parameterPointer - sizeof(WORD)); // // if the DOS app has already encrypted the password (how'd it do that?) // then we'll leave the password alone. Otherwise, we need to read // out the cleartext password from the user_info_1 or _2 structure // or SetInfo buffer, encrypt it and write back the encrypted // password, submit the request, then replace the encrypted password // in DOS memory with the original cleartext password. // // Note: passwordEncrypted might be 0 because this is a SetInfo2 // call which is NOT setting the password // if (!passwordEncrypted && (parmNum == PARMNUM_ALL || parmNum == USER_PASSWORD_PARMNUM) && (level == 1 || level == 2)) { LM_OWF_PASSWORD lmOwfPassword; LM_SESSION_KEY lanmanKey; ENCRYPTED_LM_OWF_PASSWORD encryptedLmOwfPassword; NTSTATUS ntStatus; WCHAR uServerName[LM20_CNLEN + 1]; DWORD length; LPSTR lpServerName; // // get into passwordPointer the address of the buffer. If UserAdd2 // or SetInfo2 with PARMNUM_ALL, this is the address of a // user_info_1 or _2 structure, and we need to bump the pointer // again to be the address of the password field within the // structure. // // If the request is SetInfo2 with USER_PASSWORD_PARMNUM then // the buffer is the address of the password to set. // // Otherwise, this is a SetInfo2 call which is not setting a // password, so we don't have anything left to do // // if this is a SetInfo2 call with USER_PASSWORD_PARMNUM then // we have a slight kludge to perform. We need to encrypt the // password in 16-bit memory space, but if we just copy over // the cleartext password then we risk blatting over whatever // is in memory after the password. We have reserved a 16-byte // buffer in REDIR.EXE at CS:AX for this very purpose // if (parmNum == USER_PASSWORD_PARMNUM) { RtlCopyMemory(POINTER_FROM_WORDS(getCS(), getAX()), LPBYTE_FROM_POINTER(passwordPointer), ENCRYPTED_PWLEN ); // // set the address of the buffer in the stack to point to // the encryption buffer in REDIR.EXE // WRITE_WORD(passwordPointer, getAX()); WRITE_WORD(passwordPointer+2, getCS()); passwordPointer = POINTER_FROM_WORDS(getCS(), getAX()); } else { passwordPointer = LPBYTE_FROM_POINTER(passwordPointer); } // // BUGBUG - I have no idea (currently) if we ever get a NULL // pointer, but I think it is wrong. If we do, just // skip ahead and remote the function - let the // server handle it // if (!passwordPointer) { goto VrNetRemoteApi_do_remote; } // // if passwordPointer currently points at a user_info_1 or // user_info_2 structure, bump it to point at the password // field within the structure // if (parmNum == PARMNUM_ALL) { passwordPointer += (DWORD)&((struct user_info_1*)0)->usri1_password[0]; } // // if the password is NULL_USERSETINFO_PASSWD (14 spaces and // terminating 0) there is nothing to do // if (!strcmp(passwordPointer, NULL_USERSETINFO_PASSWD)) { passwordPointer = NULL; goto VrNetRemoteApi_do_remote; } // // okay, let's do some encryption (exciting isn't it?) // RtlCopyMemory(aPassword, passwordPointer, sizeof(((struct user_info_1*)0)->usri1_password) ); // // BUGBUG, this isn't necessarily the correct upper-case function // _strupr(aPassword); // // convert the ANSI server name to UNICODE for GetLanmanSessionKey // lpServerName = LPSTR_FROM_POINTER(ServerNamePointer); ntStatus = RtlOemToUnicodeN(uServerName, sizeof(uServerName) - sizeof(uServerName[0]), &length, lpServerName, strlen(lpServerName) ); if (NT_SUCCESS(ntStatus)) { uServerName[length/sizeof(uServerName[0])] = 0; } else { status = ERROR_INVALID_PARAMETER; goto VrNetRemoteApi_exit; } ntStatus = RtlCalculateLmOwfPassword(aPassword, &lmOwfPassword); if (NT_SUCCESS(ntStatus)) { ntStatus = GetLanmanSessionKey((LPWSTR)uServerName, (LPBYTE)&lanmanKey); if (NT_SUCCESS(ntStatus)) { ntStatus = RtlEncryptLmOwfPwdWithLmSesKey(&lmOwfPassword, &lanmanKey, &encryptedLmOwfPassword ); if (NT_SUCCESS(ntStatus)) { RtlCopyMemory(passwordPointer, &encryptedLmOwfPassword, sizeof(encryptedLmOwfPassword) ); // // fake it // WRITE_WORD(parameterPointer, 1); // // if this is SetInfo2 with USER_PASSWORD_PARMNUM // then we don't need to copy back the cleartext // password because we have not modified the // original buffer in the app's space // // We also have to change the size of data being // passed in to the size of the encrypted password // if (parmNum == USER_PASSWORD_PARMNUM) { WRITE_WORD(dataLengthPointer, ENCRYPTED_PWLEN); passwordPointer = NULL; } } } } // // if we fell by the wayside, quit out // if (!NT_SUCCESS(ntStatus)) { status = RtlNtStatusToDosError(ntStatus); goto VrNetRemoteApi_exit; } } else { // // we are not encrypting the password - set the pointer back // to NULL. Used as a flag after call to VrRemoteApi // passwordPointer = NULL; } } // // build a transaction request from the caller's parameters // VrNetRemoteApi_do_remote: status = VrRemoteApi(ApiNumber, ServerNamePointer, ParameterDescriptor, DataDescriptor, AuxDescriptor, NullSessionFlag ); // // if we replaced a cleartext password with an encrypted password in // DOS memory, then undo the change before giving control back to DOS // if (passwordPointer) { RtlCopyMemory(passwordPointer, aPassword, sizeof(((struct user_info_1*)0)->usri1_password) ); WRITE_WORD(parameterPointer, 0); } } VrNetRemoteApi_exit: if (status != NERR_Success) { SET_ERROR((WORD)status); #if DBG IF_DEBUG(NETAPI) { DbgPrint("Error: VrNetRemoteApi returning %u\n", (DWORD)getAX()); } #endif } else { setCF(0); } } BOOL OemToUppercaseUnicode( IN LPSTR AnsiStringPointer, OUT LPWSTR UnicodeStringPointer, IN DWORD MaxLength ) /*++ Routine Description: given a string in OEM character set, upper cases it then converts it to UNICODE Arguments: AnsiStringPointer - pointer to 8-bit string to convert UnicodeStringPointer - pointer to resultant 16-bit (UNICODE) string MaxLength - maximum output buffer length in # of characters, NOT including terminating NUL Return Value: BOOL TRUE - string converted FALSE - failed for some reason (string too long, Rtl function failed) --*/ { DWORD stringLength; char scratchpad[UNLEN + 1]; // UNLEN is the largest type of string we'll get NTSTATUS ntStatus; DWORD length; stringLength = strlen(AnsiStringPointer); if (stringLength > MaxLength) { return FALSE; } strcpy(scratchpad, AnsiStringPointer); // // BUGBUG - this is not necessarily the correct upper-case function // _strupr(scratchpad); ntStatus = RtlOemToUnicodeN(UnicodeStringPointer, MaxLength * sizeof(*UnicodeStringPointer), &length, scratchpad, stringLength ); if (NT_SUCCESS(ntStatus)) { UnicodeStringPointer[length/sizeof(*UnicodeStringPointer)] = 0; return TRUE; } else { return FALSE; } } VOID VrNetServerEnum( VOID ) /*++ Routine Description: Handles NetServerEnum and NetServerEnum2 Arguments: NetServerEnum ENTRY AL = 4Ch BL = level (0 or 1) CX = size of buffer ES:DI = buffer EXIT CF = 1 AX = Error code: NERR_BufTooSmall ERROR_MORE_DATA CF = 0 BX = entries read CX = total available NetServerEnum2 ENTRY AL = 53h DS:SI = NetServerEnum2Struct: DW Level DD Buffer DW Buflen DD Type DD Domain EXIT CF = 1 AX = Error code: NERR_BufTooSmall ERROR_MORE_DATA CF = 0 BX = entries read CX = total available Return Value: None. --*/ { BYTE callType = getAL(); struct NetServerEnum2Struct* structPtr; LPBYTE buffer; WORD bufferSegment; WORD bufferOffset; LPDESC descriptor; WORD level; WORD buflen; DWORD serverType; LPSTR domain; XS_NET_SERVER_ENUM_2 parameters; XS_PARAMETER_HEADER header; NTSTATUS ntstatus; NET_API_STATUS status; // LPBYTE enumPtr; // DWORD nRead; // DWORD nAvail; #if DBG IF_DEBUG(NETAPI) { DbgPrint("VrNetServerEnum: type=0x%02x\n", callType); IF_DEBUG(BREAKPOINT) { DbgBreakPoint(); } } #endif if (callType == 0x4c) { // // call is NetServerEnum // bufferSegment = getES(); bufferOffset = getDI(); buffer = LPBYTE_FROM_WORDS(bufferSegment, bufferOffset); buflen = (WORD)getCX(); level = (WORD)getBL(); serverType = SV_TYPE_ALL; domain = NULL; } else { // // call is NetServerEnum2 // structPtr = (struct NetServerEnum2Struct*)POINTER_FROM_WORDS(getDS(), getSI()); bufferSegment = GET_SEGMENT(&structPtr->NSE_buf); bufferOffset = GET_OFFSET(&structPtr->NSE_buf); buffer = POINTER_FROM_WORDS(bufferSegment, bufferOffset); buflen = READ_WORD(&structPtr->NSE_buflen); level = READ_WORD(&structPtr->NSE_level); serverType = READ_DWORD(&structPtr->NSE_type); domain = LPSTR_FROM_POINTER(&structPtr->NSE_domain); } // // set the returned EntriesRead (BX) and TotalAvail (CX) to zero here for // the benefit of the 16-bit Windows NETAPI.DLL!NetServerEnum2 // This function tries to unpack BX entries from the enum buffer as // soon as control is returned after the call to the redir via DoIntx. BUT // IT DOESN'T LOOK AT THE RETURN CODE FIRST. As Sam Kinnison used to say // AAAAAAAAAAAAAAARRRRRGH AAAAAAARGHH AAAAAAARGHHHHHHH!!!!!!! // setBX(0); setCX(0); // // first, check level - both types only handle 0 or 1 // switch (level) { case 0: descriptor = REM16_server_info_0; break; case 1: descriptor = REM16_server_info_1; break; // // levels 2 & 3 not used in enum // default: #if DBG IF_DEBUG(NETAPI) { DbgPrint("VrNetServerEnum - invalid level %d. Returning early\n", level); } #endif SET_ERROR(ERROR_INVALID_LEVEL); return; } parameters.Level = level; parameters.Buffer = buffer; parameters.BufLen = buflen; parameters.ServerType = serverType; parameters.Domain = domain; #if DBG IF_DEBUG(NETAPI) { DbgPrint("buffer @%04x:%04x, length=%d, level=%d, type=0x%08x, domain=%s\n", bufferSegment, bufferOffset, parameters.BufLen, level, parameters.ServerType, parameters.Domain ); } #endif // // // // I_BrowserServerEnum which XsNetServerEnum2 calls requires a transport // // name. If we don't give it one, it'll return ERROR_INVALID_PARAMETER // // // // status = NetWkstaTransportEnum(NULL, // 0, // &enumPtr, // -1L, // we'll take everything // &nRead, // number returned // &nAvail, // total available // NULL // no resume handle // ); // if (status != NERR_Success) { // //#if DBG // IF_DEBUG(NETAPI) { // DbgPrint("VrNetServerEnum: Error: NetWkstaTransportEnum returns %d\n", status); // } //#endif // // SET_ERROR(status); // return; // } header.Status = 0; header.ClientMachineName = NULL; // // use the first enumerated transport name // // header.ClientTransportName = ((LPWKSTA_TRANSPORT_INFO_0)enumPtr)->wkti0_transport_name; header.ClientTransportName = NULL; ntstatus = XsNetServerEnum2(&header, ¶meters, descriptor, NULL); if (!NT_SUCCESS(ntstatus)) { status = NetpNtStatusToApiStatus(ntstatus); } else { status = (NET_API_STATUS)header.Status; } if (status == NERR_Success) { SET_SUCCESS(); } else { SET_ERROR((WORD)status); } if (status == NERR_Success || status == ERROR_MORE_DATA) { if (parameters.EntriesRead) { VrpConvertReceiveBuffer(buffer, bufferSegment, bufferOffset, header.Converter, parameters.EntriesRead, descriptor, NULL ); } setBX(parameters.EntriesRead); setCX(parameters.TotalAvail); } #if DBG IF_DEBUG(NETAPI) { DbgPrint("VrNetServerEnum: returning %d for NetServerEnum2\n", getAX()); if (getAX() == NERR_Success || getAX() == ERROR_MORE_DATA) { DbgPrint("EntriesRead=%d, TotalAvail=%d\n", parameters.EntriesRead, parameters.TotalAvail ); } } #endif // // // // free up the buffer returned by NetWkstaTransportEnum // // // // NetApiBufferFree(enumPtr); } VOID VrNetServiceControl( VOID ) /*++ Routine Description: We allow the interrogate function for specific services. The other functions are pause, continue and stop (uninstall) which we disallow Arguments: Function 5F42 DL = opcode: 0 = interrogate SUPPORTED 1 = pause service * NOT SUPPORTED * 2 = continue service * NOT SUPPORTED * 3 = uninstall service * NOT SUPPORTED * 4 - 127 = reserved 127 - 255 = OEM defined DH = OEM defined argument ES:BX = NetServiceControl structure: char far* service name unsigned short buffer length char far* buffer Return Value: None. --*/ { BYTE opcode = getDL(); BYTE oemArg = getDH(); struct NetServiceControlStruc* structPtr = (struct NetServiceControlStruc*) POINTER_FROM_WORDS(getES(), getBX()); LPSTR serviceName = READ_FAR_POINTER(&structPtr->NSCS_Service); WORD buflen = READ_WORD(&structPtr->NSCS_BufLen); LPSTR buffer = READ_FAR_POINTER(&structPtr->NSCS_BufferAddr); WORD seg = GET_SEGMENT(&structPtr->NSCS_BufferAddr); WORD off = GET_OFFSET(&structPtr->NSCS_BufferAddr); XS_NET_SERVICE_CONTROL parameters; XS_PARAMETER_HEADER header; NTSTATUS ntstatus; NET_API_STATUS status; #if DBG IF_DEBUG(NETAPI) { DbgPrint("VrNetServiceControl: Service=%s, Opcode=%d, OemArg=%d, Buffer @%04x:%04x, len=%d\n", serviceName, opcode, oemArg, seg, off, buflen ); } #endif if (opcode > 4) { SET_ERROR(NERR_ServiceCtlNotValid); return; } // // we are disallowing anything other than 0 (interrogate) by returning // ERROR_INVALID_PARAMETER, which may be a new error code // if (opcode) { SET_ERROR(ERROR_INVALID_PARAMETER); return; } // // KLUDGE - if the service name is NETPOPUP then we return NERR_ServiceNotInstalled. // LANMAN.DRV checks to see if this service is loaded. If it is then // it sticks Load=WinPopUp in WIN.INI. We don't want it to do this // if (!_stricmp(serviceName, NETPOPUP_SERVICE)) { // // roll our own service_info_2 structure // if (buflen >= sizeof(struct service_info_2)) { SET_ERROR(NERR_ServiceNotInstalled); } else { SET_ERROR(NERR_BufTooSmall); } return; } // // leave the work to XsNetServiceControl // parameters.Service = serviceName; parameters.OpCode = opcode; parameters.Arg = oemArg; parameters.Buffer = buffer; parameters.BufLen = buflen; header.Status = 0; header.ClientMachineName = NULL; header.ClientTransportName = NULL; ntstatus = XsNetServiceControl(&header, ¶meters, REM16_service_info_2, NULL); if (!NT_SUCCESS(ntstatus)) { status = NetpNtStatusToApiStatus(ntstatus); } else { status = (NET_API_STATUS)header.Status; } #if DBG IF_DEBUG(NETAPI) { DbgPrint("VrNetServiceControl: returning %d\n", status); } #endif if (status == NERR_Success || status == ERROR_MORE_DATA) { // // there are no pointers in a service_info_2 structure, so there is // no need to call VrpConvertReceiveBuffer. Also, we are not going to // allow the DOS process to pause, continue, start or stop any of our // 32-bit services, so we must tell the DOS app that the service // cannot accept these controls: zero out bit 4 // (SERVICE_NOT_UNINSTALLABLE) and bit 5 (SERVICE_NOT_PAUSABLE) // ((struct service_info_2*)buffer)->svci2_status &= 0xff0f; SET_OK((WORD)status); } else { SET_ERROR((WORD)status); } } VOID VrNetServiceEnum( VOID ) /*++ Routine Description: description-of-function. Arguments: None. Return Value: None. --*/ { SET_ERROR(ERROR_NOT_SUPPORTED); } VOID VrNetTransactApi( VOID ) /*++ Routine Description: Performs a transaction on behalf of the Vdm Arguments: None. All arguments are extracted from the Vdm context registers/memory Return Value: None. Results returned via VDM registers or in VDM memory, according to request --*/ { DWORD status; #if DBG IF_DEBUG(NETAPI) { DbgPrint("VrNetTransactApi\n"); IF_DEBUG(BREAKPOINT) { DbgBreakPoint(); } } #endif status = VrpTransactVdm(FALSE); if (status) { SET_ERROR((WORD)status); } else { setCF(0); } } VOID VrNetUseAdd( VOID ) /*++ Routine Description: Performs local NetUseAdd on behalf of the Vdm client Arguments: Function 5F47h ENTRY BX = level CX = buffer length DS:SI = server name for remote call (MBZ) ES:DI = buffer containing use_info_1 structure Return Value: None. --*/ { NET_API_STATUS status; XS_NET_USE_ADD parameters; XS_PARAMETER_HEADER header; NTSTATUS ntstatus; LPSTR computerName; LPBYTE buffer; WORD level; BOOL allocated; DWORD buflen; DWORD auxOffset; char myDescriptor[sizeof(REM16_use_info_1)]; char myDataBuffer[sizeof(struct use_info_1) + LM20_PWLEN + 1]; #if DBG IF_DEBUG(NETAPI) { DbgPrint("VrNetUseAdd\n"); IF_DEBUG(BREAKPOINT) { DbgBreakPoint(); } } #endif // // ensure the computer name designates the local machine (NULL) // computerName = LPSTR_FROM_WORDS(getDS(), getSI()); level = (WORD)getBX(); if (level != 1) { // // level must be 1 for an add // SET_ERROR(ERROR_INVALID_LEVEL); return; } // // preset the modifiable descriptor string // strcpy(myDescriptor, REM16_use_info_1); // // pack the use_info_1 buffer as if we were getting ready to ship it over // the net. Return errors // buffer = LPBYTE_FROM_WORDS(getES(), getDI()); buflen = (DWORD)getCX(); // // copy the DOS buffer to 32-bit memory. Do this to avoid irritating problem // of getting an already packed buffer from the client, and not being able // to do anything with it // RtlCopyMemory(myDataBuffer, buffer, sizeof(struct use_info_1)); buffer = myDataBuffer; buflen = sizeof(myDataBuffer); status = VrpPackSendBuffer(&buffer, &buflen, &allocated, myDescriptor, // modifiable descriptor NULL, // AuxDescriptor VrpGetStructureSize(REM16_use_info_1, &auxOffset), (DWORD)-1, // AuxOffset (-1 means there is no aux char 'N') 0, // AuxSize FALSE, // not a SetInfo call TRUE // OkToModifyDescriptor ); if (status) { SET_ERROR(VrpMapDosError(status)); return; } parameters.Level = level; parameters.Buffer = buffer; parameters.BufLen = (WORD)buflen; header.Status = 0; header.ClientMachineName = NULL; header.ClientTransportName = NULL; ntstatus = XsNetUseAdd(&header, ¶meters, myDescriptor, NULL); if (ntstatus != STATUS_SUCCESS) { status = NetpNtStatusToApiStatus(ntstatus); } else { // // no error generated in XsNetUseAdd. Get the status of the NetUseAdd // proper from the header // status = (NET_API_STATUS)header.Status; } if (status != NERR_Success) { #if DBG IF_DEBUG(NETAPI) { DbgPrint("Error: VrNetUseAdd: XsNetUseAdd returns %u\n", status); } #endif SET_ERROR((WORD)status); } else { setCF(0); } // // if VrpPackSendBuffer allocated a new buffer then free it // if (allocated) { LocalFree(buffer); } } VOID VrNetUseDel( VOID ) /*++ Routine Description: Performs local NetUseDel on behalf of the Vdm client Arguments: Function 5F48h ENTRY BX = force flag DS:SI = server name for remote call (MBZ) ES:DI = use name Return Value: None. --*/ { NTSTATUS ntstatus; NET_API_STATUS status; WORD force; LPSTR name; XS_NET_USE_DEL parameters; XS_PARAMETER_HEADER header; #if DBG IF_DEBUG(NETAPI) { DbgPrint("VrNetUseDel\n"); IF_DEBUG(BREAKPOINT) { DbgBreakPoint(); } } #endif force = (WORD)getBX(); if (force > USE_LOTS_OF_FORCE) { SET_ERROR(ERROR_INVALID_PARAMETER); return; } name = LPSTR_FROM_WORDS(getDS(), getSI()); // // make sure name is local // name = LPSTR_FROM_WORDS(getES(), getDI()); parameters.UseName = name; parameters.Force = force; header.Status = 0; header.ClientMachineName = NULL; header.ClientTransportName = NULL; ntstatus = XsNetUseDel(&header, ¶meters, NULL, NULL); // // if XsNetUseDel failed then map the NT error returned into a Net error // else get the result of the NetUseDel proper from the header structure // if (ntstatus != STATUS_SUCCESS) { status = NetpNtStatusToApiStatus(ntstatus); } else { status = (NET_API_STATUS)header.Status; } if (status != NERR_Success) { SET_ERROR(VrpMapDosError(status)); } else { setCF(0); } } VOID VrNetUseEnum( VOID ) /*++ Routine Description: Performs local NetUseEnum on behalf of the Vdm client Arguments: Function 5F46h ENTRY BX = level of info required - 0 or 1 CX = buffer length ES:DI = buffer for enum info Return Value: None. --*/ { NTSTATUS ntstatus; NET_API_STATUS status; WORD level = getBX(); XS_NET_USE_ENUM parameters; XS_PARAMETER_HEADER header; LPDESC dataDesc; LPBYTE receiveBuffer; #if DBG IF_DEBUG(NETAPI) { DbgPrint("VrNetUseEnum\n"); IF_DEBUG(BREAKPOINT) { DbgBreakPoint(); } } #endif if (level <= 1) { dataDesc = (level == 1) ? REM16_use_info_1 : REM16_use_info_0; parameters.Level = level; receiveBuffer = POINTER_FROM_WORDS(getES(), getDI()); parameters.Buffer = receiveBuffer; parameters.BufLen = getCX(); header.Status = 0; header.ClientMachineName = NULL; header.ClientTransportName = NULL; ntstatus = XsNetUseEnum(&header, ¶meters, dataDesc, NULL); // // if XsNetUseEnum didn't have any problems, convert the actual status // code to that returned in the header // if (ntstatus != STATUS_SUCCESS) { status = NetpNtStatusToApiStatus(ntstatus); } else { status = (DWORD)header.Status; } } else { status = ERROR_INVALID_LEVEL; } // // NetUseEnum sets these even in the event of failure. We do the same // setCX(parameters.EntriesRead); setDX(parameters.TotalAvail); // // if we're returning data, convert the pointer offsets to something // meaningful // if (((status == NERR_Success) || (status == ERROR_MORE_DATA)) && parameters.EntriesRead) { VrpConvertReceiveBuffer(receiveBuffer, (WORD)getES(), (WORD)getDI(), (WORD)header.Converter, parameters.EntriesRead, dataDesc, NULL ); } // // only return carry clear if no error occurred. Even if ERROR_MORE_DATA // set CF // if (status) { SET_ERROR(VrpMapDosError(status)); } else { setCF(0); } } VOID VrNetUseGetInfo( VOID ) /*++ Routine Description: Performs local NetUseGetInfo on behalf of the Vdm client Arguments: Function 5F49h ENTRY DS:DX = NetUseGetInfoStruc: const char FAR* NUGI_usename; short NUGI_level; char FAR* NUGI_buffer; unsigned short NUGI_buflen; Return Value: None. --*/ { NTSTATUS ntstatus; NET_API_STATUS status; XS_NET_USE_GET_INFO parameters; XS_PARAMETER_HEADER header; struct NetUseGetInfoStruc* structurePointer; WORD level; LPDESC dataDesc; LPBYTE receiveBuffer; #if DBG IF_DEBUG(NETAPI) { DbgPrint("VrNetUseGetInfo\n"); IF_DEBUG(BREAKPOINT) { DbgBreakPoint(); } } #endif // // pull info out of Vdm context // structurePointer = (struct NetUseGetInfoStruc*) POINTER_FROM_WORDS(getDS(), getDX()); level = structurePointer->NUGI_level; // // level can be 0 or 1 // if (level <= 1) { dataDesc = (level == 1) ? REM16_use_info_1 : REM16_use_info_0; #if DBG IF_DEBUG(NETAPI) { DbgPrint("VrNetUseGetInfo: dataDesc=%s\n", dataDesc); } #endif parameters.UseName= POINTER_FROM_POINTER(&(structurePointer->NUGI_usename)); #if DBG IF_DEBUG(NETAPI) { DbgPrint("VrNetUseGetInfo: UseName=%s\n", parameters.UseName); } #endif parameters.Level = level; #if DBG IF_DEBUG(NETAPI) { DbgPrint("VrNetUseGetInfo: level=%d\n", level); } #endif receiveBuffer = POINTER_FROM_POINTER(&(structurePointer->NUGI_buffer)); #if DBG IF_DEBUG(NETAPI) { DbgPrint("VrNetUseGetInfo: receiveBuffer=%x\n", receiveBuffer); } #endif parameters.Buffer = receiveBuffer; parameters.BufLen = READ_WORD(&structurePointer->NUGI_buflen); #if DBG IF_DEBUG(NETAPI) { DbgPrint("VrNetUseGetInfo: BufLen=%d\n", parameters.BufLen); } #endif header.Status = 0; header.ClientMachineName = NULL; header.ClientTransportName = NULL; ntstatus = XsNetUseGetInfo(&header, ¶meters, dataDesc, NULL); if (ntstatus != STATUS_SUCCESS) { status = NetpNtStatusToApiStatus(ntstatus); } else { status = header.Status; } } else { status = ERROR_INVALID_LEVEL; } if ((status == NERR_Success) || (status == ERROR_MORE_DATA) || (status == NERR_BufTooSmall) ) { setDX(parameters.TotalAvail); #if DBG IF_DEBUG(NETAPI) { DbgPrint("VrNetUseGetInfo: TotalAvail=%d\n", parameters.TotalAvail); } #endif if ((status == NERR_Success) || (status == ERROR_MORE_DATA)) { VrpConvertReceiveBuffer( receiveBuffer, GET_SELECTOR(&(structurePointer->NUGI_buffer)), GET_OFFSET(&(structurePointer->NUGI_buffer)), (WORD)header.Converter, 1, dataDesc, NULL ); } } else { // // the first thing NetUseGetInfo does is set the returned total available // count to 0. Lets be compatible! // setDX(0); } if (status) { SET_ERROR(VrpMapDosError(status)); } else { setCF(0); } } VOID VrNetWkstaGetInfo( VOID ) /*++ Routine Description: Performs local NetWkstaGetInfo on behalf of the Vdm client Arguments: Function 5F44h ENTRY BX = level (0, 1 or 10) CX = size of caller's buffer DS:SI = computer name for remote call (IGNORED) ES:DI = caller's buffer Return Value: CF = 0 DX = size of buffer required to honour request CF = 1 AX = error code --*/ { DWORD level; DWORD bufLen; LPBYTE buffer; LPDESC dataDesc; NET_API_STATUS status; NTSTATUS ntStatus; XS_PARAMETER_HEADER header; XS_NET_WKSTA_GET_INFO parameters; WORD bufferSegment; WORD bufferOffset; INT bufferLeft; DWORD totalAvail; #if DBG IF_DEBUG(NETAPI) { DbgPrint("\nVrNetWkstaGetInfo\n"); IF_DEBUG(BREAKPOINT) { DbgBreakPoint(); } } #endif level = (DWORD)getBX(); switch (level) { case 0: dataDesc = REMSmb_wksta_info_0; break; case 1: dataDesc = REMSmb_wksta_info_1; break; case 10: dataDesc = REMSmb_wksta_info_10; break; default: SET_ERROR(ERROR_INVALID_LEVEL); #if DBG IF_DEBUG(NETAPI) { DbgPrint("VrNetWkstaGetInfo: Error: returning %d for level %d\n", getAX(), level ); } #endif return; } bufLen = (DWORD)getCX(); bufferSegment = getES(); bufferOffset = getDI(); buffer = LPBYTE_FROM_WORDS(bufferSegment, bufferOffset); if (bufLen && !buffer) { SET_ERROR(ERROR_INVALID_PARAMETER); #if DBG IF_DEBUG(NETAPI) { DbgPrint("VrNetWkstaGetInfo: Error: buffer=NULL, buflen=%d\n", bufLen); } #endif return; } // // clear out the caller's buffer - just in case XsNetWkstaGetInfo forgets // to fill in some fields // if (bufLen) { RtlZeroMemory(buffer, bufLen); } #if DBG IF_DEBUG(NETAPI) { DbgPrint("VrNetWkstaGetInfo: level=%d, bufLen = %d (0x%x), buffer = %x:%x\n", level, bufLen, bufLen, bufferSegment, bufferOffset ); } #endif parameters.Level = (WORD)level; parameters.Buffer = buffer; parameters.BufLen = (WORD)bufLen; header.Status = 0; header.Converter = 0; header.ClientMachineName = NULL; header.ClientTransportName = NULL; header.EncryptionKey = NULL; ntStatus = XsNetWkstaGetInfo(&header, ¶meters, dataDesc, NULL); if (!NT_SUCCESS(ntStatus)) { status = NetpNtStatusToApiStatus(ntStatus); } else { status = (NET_API_STATUS)header.Status; } if (status != NERR_Success) { SET_ERROR((WORD)status); } else { setCF(0); setAX((WORD)status); } #if DBG IF_DEBUG(NETAPI) { DbgPrint("VrNetWkstaGetInfo: status after XsNetWkstaGetInfo=%d, TotalAvail=%d\n", status, parameters.TotalAvail ); // DumpWkstaInfo(level, buffer); } #endif // // This next bit of code will add the per-user information only if there // is space to add all of it - XsNetWkstaGetInfo returns either all the // variable data, or none of it. This is incorrect, but we'll play along. // // Assumes that the variable data is packed into the buffer starting at // the end of the fixed structure + 1 // // Irrespective of whether data is returned, we have to update the // TotalAvail parameter to reflect the adjusted amount of data // totalAvail = parameters.TotalAvail; bufferLeft = (INT)(bufLen - totalAvail); if ((status == NERR_Success) || (status == ERROR_MORE_DATA) || (status == NERR_BufTooSmall)) { // // because of NT's ability to instantaneously support more than one // user, XsNetWkstaGetInfo no longer returns information pertinent to // the current user. Thus, we have to furnish the information from // this user's context: // // field\level 0 1 10 // ------------------------ // user name x x x // logon server x x // logon domain x x // other domains x x // // all this info is returned from NetWkstaUserGetInfo, level 1 // LPBYTE info; NET_API_STATUS net_status; char username[LM20_UNLEN + 1]; char logonServer[LM20_UNCLEN + 1]; char logonDomain[LM20_DNLEN + 1]; char otherDomains[512]; // arbitrary DWORD len; LPWSTR UNALIGNED str; //BOOL nullPointer; BOOL addSlashes; //// TEST_DATA // static INT testindex = 0; // static WCHAR* testnames[] = { // NULL, // NULL, // L"", // L"", // L"A", // L"A", // L"AB", // L"AB", // L"ABC", // L"ABC", // L"ABCDEFGHIJKLMNO", // L"ABCDEFGHIJKLMNO", // L"\\\\", // L"\\\\", // L"\\\\A", // L"\\\\A", // L"\\\\AB", // L"\\\\AB", // L"\\\\ABC", // L"\\\\ABC", // L"\\\\ABCDEFGHIJKLMNO", // L"\\\\ABCDEFGHIJKLMNO" // }; //// TEST_DATA // // first off, modify the pointers for any data returned by // XsNetWkstaGetInfo // if (status == NERR_Success) { //#if DBG // IF_DEBUG(NETAPI) { // DbgPrint("VrNetWkstaGetInfo: calling VrpConvertReceiveBuffer: Converter=%04x\n", // header.Converter // ); // } //#endif VrpConvertReceiveBuffer( buffer, bufferSegment, bufferOffset, (WORD)header.Converter, 1, dataDesc, NULL ); } // // get the per-user information // net_status = NetWkstaUserGetInfo(NULL, 1, &info); if (net_status == NERR_Success) { //#if DBG // IF_DEBUG(NETAPI) { // DbgPrint("NetWkstaUserGetInfo:\n" // "user name %ws\n" // "logon domain %ws\n" // "other domains %ws\n" // "logon server %ws\n" // "\n", // ((PWKSTA_USER_INFO_1)info)->wkui1_username, // ((PWKSTA_USER_INFO_1)info)->wkui1_logon_domain, // ((PWKSTA_USER_INFO_1)info)->wkui1_oth_domains, // ((PWKSTA_USER_INFO_1)info)->wkui1_logon_server // ); // } //#endif // // username for all levels // str = (LPWSTR)((PWKSTA_USER_INFO_1)info)->wkui1_username; if (!str) { str = L""; } //nullPointer = ((level == 10) // ? ((struct wksta_info_10*)buffer)->wki10_username // : ((struct wksta_info_0*)buffer)->wki0_username // ) == NULL; //len = wcslen(str) + nullPointer ? 1 : 0; #ifdef DBCS /*fix for DBCS char sets*/ len = (DWORD)NetpUnicodeToDBCSLen(str) + 1; #else // !DBCS len = wcslen(str) + 1; #endif // !DBCS bufferLeft -= len; totalAvail += len; if (len <= sizeof(username)) { #ifdef DBCS /*fix for DBCS char sets*/ NetpCopyWStrToStrDBCS(username, str); #else // !DBCS NetpCopyWStrToStr(username, str); #endif // !DBCS } else { username[0] = 0; } // // logon_server for levels 0 & 1 // if (level <= 1) { str = (LPWSTR)((PWKSTA_USER_INFO_1)info)->wkui1_logon_server; //// TEST_CODE // if (testindex < sizeof(testnames)/sizeof(testnames[0])) { // str = testnames[testindex++]; // } //// TEST_CODE if (!str) { str = L""; } #ifdef DBCS /*fix for DBCS char sets*/ len = (DWORD)NetpUnicodeToDBCSLen(str) + 1; #else // !DBCS len = wcslen(str) + 1; #endif // !DBCS // // DOS returns "\\logon_server" whereas NT returns "logon_server". // We need to account for the extra backslashes (but only if not // NULL string) // At this time, len includes +1 for terminating \0, so even a // NULL string has length 1 // addSlashes = TRUE; if (len >= 3 && IS_PATH_SEPARATOR(str[0]) && IS_PATH_SEPARATOR(str[1])) { addSlashes = FALSE; } else if (len == 1) { // NULL string addSlashes = FALSE; } if (addSlashes) { len += 2; } bufferLeft -= len; totalAvail += len; if (len <= sizeof(logonServer)) { INT offset = 0; if (addSlashes) { logonServer[0] = logonServer[1] = '\\'; offset = 2; } #ifdef DBCS /*fix for DBCS char sets*/ NetpCopyWStrToStrDBCS(&logonServer[offset], str); #else // !DBCS NetpCopyWStrToStr(&logonServer[offset], str); #endif // !DBCS } else { logonServer[0] = 0; } } // // logon_domain and oth_domains for levels 1 & 10 // if (level >= 1) { str = (LPWSTR)((PWKSTA_USER_INFO_1)info)->wkui1_logon_domain; if (!str) { str = L""; } #ifdef DBCS /*fix for DBCS char sets*/ len = (DWORD)NetpUnicodeToDBCSLen(str) + 1; #else // !DBCS len = wcslen(str) + 1; #endif // !DBCS bufferLeft -= len; totalAvail += len; if (len <= sizeof(logonDomain)) { #ifdef DBCS /*fix for DBCS char sets*/ NetpCopyWStrToStrDBCS(logonDomain, str); #else // !DBCS NetpCopyWStrToStr(logonDomain, str); #endif // !DBCS } else { logonDomain[0] = 0; } str = (LPWSTR)((PWKSTA_USER_INFO_1)info)->wkui1_oth_domains; if (!str) { str = L""; } #ifdef DBCS /*fix for DBCS char sets*/ len = (DWORD)NetpUnicodeToDBCSLen(str) + 1; #else // !DBCS len = wcslen(str) + 1; #endif // !DBCS bufferLeft -= len; totalAvail += len; if (len <= sizeof(otherDomains)) { #ifdef DBCS /*fix for DBCS char sets*/ NetpCopyWStrToStrDBCS(otherDomains, str); #else // !DBCS NetpCopyWStrToStr(otherDomains, str); #endif // !DBCS } else { otherDomains[0] = 0; } } // // if there's enough space in the buffer then copy the strings // if (status == NERR_Success && bufferLeft >= 0) { WORD offset = bufferOffset + parameters.TotalAvail; LPSTR UNALIGNED ptr = POINTER_FROM_WORDS(bufferSegment, offset); // // username for all levels // strcpy(ptr, username); len = strlen(username) + 1; if (level <= 1) { // // levels 0 & 1 have username field at same offset // WRITE_WORD(&((struct wksta_info_1*)buffer)->wki1_username, offset); WRITE_WORD((LPWORD)&((struct wksta_info_1*)buffer)->wki1_username+1, bufferSegment); } else { WRITE_WORD(&((struct wksta_info_10*)buffer)->wki10_username, offset); WRITE_WORD((LPWORD)&((struct wksta_info_10*)buffer)->wki10_username+1, bufferSegment); } ptr += len; offset += (WORD)len; // // logon_server for levels 0 & 1 // if (level <= 1) { strcpy(ptr, logonServer); len = strlen(logonServer) + 1; // // levels 0 & 1 have logon_server field at same offset // WRITE_WORD(&((struct wksta_info_1*)buffer)->wki1_logon_server, offset); WRITE_WORD((LPWORD)&((struct wksta_info_1*)buffer)->wki1_logon_server+1, bufferSegment); ptr += len; offset += (WORD)len; } // // logon_domain and oth_domains for levels 1 & 10 // if (level >= 1) { if (level == 1) { strcpy(ptr, logonDomain); len = strlen(logonDomain) + 1; WRITE_WORD(&((struct wksta_info_1*)buffer)->wki1_logon_domain, offset); WRITE_WORD((LPWORD)&((struct wksta_info_1*)buffer)->wki1_logon_domain+1, bufferSegment); ptr += len; offset += (WORD)len; strcpy(ptr, otherDomains); WRITE_WORD(&((struct wksta_info_1*)buffer)->wki1_oth_domains, offset); WRITE_WORD((LPWORD)&((struct wksta_info_1*)buffer)->wki1_oth_domains+1, bufferSegment); } else { strcpy(ptr, logonDomain); len = strlen(logonDomain) + 1; WRITE_WORD(&((struct wksta_info_10*)buffer)->wki10_logon_domain, offset); WRITE_WORD((LPWORD)&((struct wksta_info_10*)buffer)->wki10_logon_domain+1, bufferSegment); ptr += len; offset += (WORD)len; strcpy(ptr, otherDomains); WRITE_WORD(&((struct wksta_info_10*)buffer)->wki10_oth_domains, offset); WRITE_WORD((LPWORD)&((struct wksta_info_10*)buffer)->wki10_oth_domains+1, bufferSegment); } } } else if (status == NERR_Success) { // // the additional data will overflow the caller's buffer: // return ERROR_MORE_STATUS and NULL out any pointer fields // that XsNetWkstaGetInfo managed to set // switch (level) { case 1: WRITE_FAR_POINTER(&((struct wksta_info_1*)buffer)->wki1_logon_domain, NULL); WRITE_FAR_POINTER(&((struct wksta_info_1*)buffer)->wki1_oth_domains, NULL); // // FALL THROUGH TO LEVEL 0 FOR REST OF FIELDS // case 0: WRITE_FAR_POINTER(&((struct wksta_info_0*)buffer)->wki0_root, NULL); WRITE_FAR_POINTER(&((struct wksta_info_0*)buffer)->wki0_computername, NULL); WRITE_FAR_POINTER(&((struct wksta_info_0*)buffer)->wki0_username, NULL); WRITE_FAR_POINTER(&((struct wksta_info_0*)buffer)->wki0_langroup, NULL); WRITE_FAR_POINTER(&((struct wksta_info_0*)buffer)->wki0_logon_server, NULL); WRITE_FAR_POINTER(&((struct wksta_info_0*)buffer)->wki0_wrkheuristics, NULL); break; case 10: WRITE_FAR_POINTER(&((struct wksta_info_10*)buffer)->wki10_computername, NULL); WRITE_FAR_POINTER(&((struct wksta_info_10*)buffer)->wki10_username, NULL); WRITE_FAR_POINTER(&((struct wksta_info_10*)buffer)->wki10_langroup, NULL); WRITE_FAR_POINTER(&((struct wksta_info_10*)buffer)->wki10_logon_domain, NULL); WRITE_FAR_POINTER(&((struct wksta_info_10*)buffer)->wki10_oth_domains, NULL); break; } status = ERROR_MORE_DATA; SET_ERROR((WORD)status); } // // free the wksta user info buffer // NetApiBufferFree((LPVOID)info); } else { #if DBG IF_DEBUG(NETAPI) { DbgPrint("VrNetWkstaGetInfo: NetWkstaUserGetInfo returns %d\n", net_status); } #endif } // // update the amount of data available when we return NERR_Success, // ERROR_MORE_DATA or NERR_BufTooSmall // setDX((WORD)totalAvail); #if DBG IF_DEBUG(NETAPI) { DbgPrint("VrNetWkstaGetInfo: TotalAvail=%d\n", getDX()); } #endif } // // if we got data back, then we must change the version number from // 3.0 to 2.1 so lanman.drv thinks it is compatible with this version // of LM // if (status == NERR_Success || status == ERROR_MORE_DATA) { switch (level) { case 0: ((struct wksta_info_0*)buffer)->wki0_ver_major = LANMAN_EMULATION_MAJOR_VERSION; ((struct wksta_info_0*)buffer)->wki0_ver_minor = LANMAN_EMULATION_MINOR_VERSION; break; case 1: ((struct wksta_info_1*)buffer)->wki1_ver_major = LANMAN_EMULATION_MAJOR_VERSION; ((struct wksta_info_1*)buffer)->wki1_ver_minor = LANMAN_EMULATION_MINOR_VERSION; break; case 10: ((struct wksta_info_10*)buffer)->wki10_ver_major = LANMAN_EMULATION_MAJOR_VERSION; ((struct wksta_info_10*)buffer)->wki10_ver_minor = LANMAN_EMULATION_MINOR_VERSION; break; } } #if DBG IF_DEBUG(NETAPI) { DbgPrint("VrNetWkstaGetInfo: return status=%d, TotalAvail=%d\n", getAX(), getDX()); } if (status == NERR_Success || status == ERROR_MORE_DATA) { IF_DEBUG(NETAPI) { DumpWkstaInfo(level, buffer); } } #endif } #if DBG #define POSSIBLE_STRING(s) ((s) ? (s) : "") VOID DumpWkstaInfo( IN DWORD level, IN LPBYTE buffer ) { switch (level) { case 0: case 1: // // DbgPrint resets the test machine if we try it with this // string & these args all at once! // DbgPrint( "reserved1 %04x\n", READ_WORD(&((struct wksta_info_0*)buffer)->wki0_reserved_1) ); DbgPrint( "reserved2 %08x\n", READ_DWORD(&((struct wksta_info_0*)buffer)->wki0_reserved_2) ); DbgPrint( "lanroot %04x:%04x \"%s\"\n", GET_SEGMENT(&((struct wksta_info_0*)buffer)->wki0_root), GET_OFFSET(&((struct wksta_info_0*)buffer)->wki0_root), POSSIBLE_STRING(LPSTR_FROM_POINTER(&((struct wksta_info_0*)buffer)->wki0_root)) ); DbgPrint( "computername %04x:%04x \"%s\"\n", GET_SEGMENT(&((struct wksta_info_0*)buffer)->wki0_computername), GET_OFFSET(&((struct wksta_info_0*)buffer)->wki0_computername), POSSIBLE_STRING(LPSTR_FROM_POINTER(&((struct wksta_info_0*)buffer)->wki0_computername)) ); DbgPrint( "username %04x:%04x \"%s\"\n", GET_SEGMENT(&((struct wksta_info_0*)buffer)->wki0_username), GET_OFFSET(&((struct wksta_info_0*)buffer)->wki0_username), POSSIBLE_STRING(LPSTR_FROM_POINTER(&((struct wksta_info_0*)buffer)->wki0_username)) ); DbgPrint( "langroup %04x:%04x \"%s\"\n", GET_SEGMENT(&((struct wksta_info_0*)buffer)->wki0_langroup), GET_OFFSET(&((struct wksta_info_0*)buffer)->wki0_langroup), POSSIBLE_STRING(LPSTR_FROM_POINTER(&((struct wksta_info_0*)buffer)->wki0_langroup)) ); DbgPrint( "ver major %02x\n" "ver minor %02x\n" "reserved3 %08x\n" "charwait %04x\n" "chartime %08x\n" "charcount %04x\n", READ_BYTE(&((struct wksta_info_0*)buffer)->wki0_ver_major), READ_BYTE(&((struct wksta_info_0*)buffer)->wki0_ver_minor), READ_DWORD(&((struct wksta_info_0*)buffer)->wki0_reserved_3), READ_WORD(&((struct wksta_info_0*)buffer)->wki0_charwait), READ_DWORD(&((struct wksta_info_0*)buffer)->wki0_chartime), READ_WORD(&((struct wksta_info_0*)buffer)->wki0_charcount) ); DbgPrint( "reserved4 %04x\n" "reserved5 %04x\n" "keepconn %04x\n" "keepsearch %04x\n" "maxthreads %04x\n" "maxcmds %04x\n", READ_WORD(&((struct wksta_info_0*)buffer)->wki0_reserved_4), READ_WORD(&((struct wksta_info_0*)buffer)->wki0_reserved_5), READ_WORD(&((struct wksta_info_0*)buffer)->wki0_keepconn), READ_WORD(&((struct wksta_info_0*)buffer)->wki0_keepsearch), READ_WORD(&((struct wksta_info_0*)buffer)->wki0_maxthreads), READ_WORD(&((struct wksta_info_0*)buffer)->wki0_maxcmds) ); DbgPrint( "reserved6 %04x\n" "numworkbuf %04x\n" "sizworkbuf %04x\n" "maxwrkcache %04x\n" "sesstimeout %04x\n" "sizerror %04x\n", READ_WORD(&((struct wksta_info_0*)buffer)->wki0_reserved_6), READ_WORD(&((struct wksta_info_0*)buffer)->wki0_numworkbuf), READ_WORD(&((struct wksta_info_0*)buffer)->wki0_sizworkbuf), READ_WORD(&((struct wksta_info_0*)buffer)->wki0_maxwrkcache), READ_WORD(&((struct wksta_info_0*)buffer)->wki0_sesstimeout), READ_WORD(&((struct wksta_info_0*)buffer)->wki0_sizerror) ); DbgPrint( "numalerts %04x\n" "numservices %04x\n" "errlogsz %04x\n" "printbuftime %04x\n" "numcharbuf %04x\n" "sizcharbuf %04x\n", READ_WORD(&((struct wksta_info_0*)buffer)->wki0_numalerts), READ_WORD(&((struct wksta_info_0*)buffer)->wki0_numservices), READ_WORD(&((struct wksta_info_0*)buffer)->wki0_errlogsz), READ_WORD(&((struct wksta_info_0*)buffer)->wki0_printbuftime), READ_WORD(&((struct wksta_info_0*)buffer)->wki0_numcharbuf), READ_WORD(&((struct wksta_info_0*)buffer)->wki0_sizcharbuf) ); DbgPrint( "logon server %04x:%04x \"%s\"\n", GET_SEGMENT(&((struct wksta_info_0*)buffer)->wki0_logon_server), GET_OFFSET(&((struct wksta_info_0*)buffer)->wki0_logon_server), POSSIBLE_STRING(LPSTR_FROM_POINTER(&((struct wksta_info_0*)buffer)->wki0_logon_server)) ); DbgPrint( "wrkheuristics %04x:%04x \"%s\"\n", GET_SEGMENT(&((struct wksta_info_0*)buffer)->wki0_wrkheuristics), GET_OFFSET(&((struct wksta_info_0*)buffer)->wki0_wrkheuristics), POSSIBLE_STRING(LPSTR_FROM_POINTER(&((struct wksta_info_0*)buffer)->wki0_wrkheuristics)) ); DbgPrint( "mailslots %04x\n", READ_WORD(&((struct wksta_info_0*)buffer)->wki0_mailslots) ); if (level == 1) { DbgPrint( "logon domain %04x:%04x \"%s\"\n", GET_SEGMENT(&((struct wksta_info_1*)buffer)->wki1_logon_domain), GET_OFFSET(&((struct wksta_info_1*)buffer)->wki1_logon_domain), POSSIBLE_STRING(LPSTR_FROM_POINTER(&((struct wksta_info_1*)buffer)->wki1_logon_domain)) ); DbgPrint( "other domains %04x:%04x \"%s\"\n", GET_SEGMENT(&((struct wksta_info_1*)buffer)->wki1_oth_domains), GET_OFFSET(&((struct wksta_info_1*)buffer)->wki1_oth_domains), POSSIBLE_STRING(LPSTR_FROM_POINTER(&((struct wksta_info_1*)buffer)->wki1_oth_domains)) ); DbgPrint( "numdgrambuf %04x\n", ((struct wksta_info_1*)buffer)->wki1_numdgrambuf ); } break; case 10: DbgPrint( "computername %04x:%04x \"%s\"\n", GET_SEGMENT(&((struct wksta_info_10*)buffer)->wki10_computername), GET_OFFSET(&((struct wksta_info_10*)buffer)->wki10_computername), POSSIBLE_STRING(LPSTR_FROM_POINTER(&((struct wksta_info_10*)buffer)->wki10_computername)) ); DbgPrint( "username %04x:%04x \"%s\"\n", GET_SEGMENT(&((struct wksta_info_10*)buffer)->wki10_username), GET_OFFSET(&((struct wksta_info_10*)buffer)->wki10_username), POSSIBLE_STRING(LPSTR_FROM_POINTER(&((struct wksta_info_10*)buffer)->wki10_username)) ); DbgPrint( "langroup %04x:%04x \"%s\"\n", GET_SEGMENT(&((struct wksta_info_10*)buffer)->wki10_langroup), GET_OFFSET(&((struct wksta_info_10*)buffer)->wki10_langroup), POSSIBLE_STRING(LPSTR_FROM_POINTER(&((struct wksta_info_10*)buffer)->wki10_langroup)) ); DbgPrint( "ver major %02x\n" "ver minor %02x\n" "logon domain %04x:%04x \"%s\"\n", READ_BYTE(&((struct wksta_info_10*)buffer)->wki10_ver_major), READ_BYTE(&((struct wksta_info_10*)buffer)->wki10_ver_minor), GET_SEGMENT(&((struct wksta_info_10*)buffer)->wki10_logon_domain), GET_OFFSET(&((struct wksta_info_10*)buffer)->wki10_logon_domain), POSSIBLE_STRING(LPSTR_FROM_POINTER(&((struct wksta_info_10*)buffer)->wki10_logon_domain)) ); DbgPrint( "other domains %04x:%04x \"%s\"\n", GET_SEGMENT(&((struct wksta_info_10*)buffer)->wki10_oth_domains), GET_OFFSET(&((struct wksta_info_10*)buffer)->wki10_oth_domains), POSSIBLE_STRING(LPSTR_FROM_POINTER(&((struct wksta_info_10*)buffer)->wki10_oth_domains)) ); break; } DbgPrint("\n"); } #endif VOID VrNetWkstaSetInfo( VOID ) /*++ Routine Description: Performs local NetUseEnum on behalf of the Vdm client Arguments: None. Return Value: None. --*/ { #if DBG IF_DEBUG(NETAPI) { DbgPrint("VrNetWkstaSetInfo\n"); IF_DEBUG(BREAKPOINT) { DbgBreakPoint(); } } #endif #if DBG IF_DEBUG(NETAPI) { DbgPrint("VrNetWkstaSetInfo - unsupported SVC\n"); } #endif SET_ERROR(ERROR_NOT_SUPPORTED); } VOID VrReturnAssignMode( VOID ) /*++ Routine Description: Returns net pause/continue status Arguments: Function 5F00h None. All arguments are extracted from the Vdm context registers/memory Return Value: None. Results returned via VDM registers or in VDM memory, according to request --*/ { } VOID VrSetAssignMode( VOID ) /*++ Routine Description: Pauses or continues net (drive/printer) redirection Arguments: Function 5F01h None. All arguments are extracted from the Vdm context registers/memory Return Value: None. Results returned via VDM registers or in VDM memory, according to request --*/ { } // // DefineMacroDriveUserWords - the old DefineMacro call (int 21h/ax=5f03h) // allows the caller to associate a (16-bit) word value with the assignment. // This value can be returned from GetAssignListEntry (int 21h/ax=5f02h). // NetUse doesn't support this, so we fake it // // DefineMacroPrintUserWords - same idea for printers; we reserve 8 max // static WORD DefineMacroDriveUserWords[26]; static WORD DefineMacroPrintUserWords[8]; VOID VrGetAssignListEntry( VOID ) /*++ Routine Description: Old version of NetUseGetInfo. In DOS this function performs the following: look along CDS list for entry # bx with IS_NET bit set if found return local device name and remote net name else look along list of printers for entry # bx if found return local device name and remote net name endif endif Every time a drive entry is found with IS_NET set or a printer entry found, bx is decremented. When bx reaches 0, then that's the entry to return NOTE: This function DOES NOT support UNC connections Arguments: Function 5F02h (GetAssignList) Function 5F05h (GetAssignList2) ENTRY BX = which item to return (starts @ 0) DS:SI points to local redirection name ES:DI points to remote redirection name AL != 0 means return LSN in BP (GetAssignList2)? Return Value: CF = 0 BL = macro type (3 = printer, 4 = drive) BH = 'interesting' bits ** UNSUPPORTED ** AX = net name ID ** UNSUPPORTED ** CX = user word DX = max xmit size ** UNSUPPORTED ** BP = LSN if AL != 0 on entry ** UNSUPPORTED ** DS:SI has device name ES:DI has net path CF = 1 AX = ERROR_NO_MORE_FILES --*/ { NTSTATUS ntstatus; NET_API_STATUS status; XS_NET_USE_ENUM parameters; XS_PARAMETER_HEADER header; LPBYTE receiveBuffer; DWORD entryNumber; struct use_info_1* driveInfo[26]; struct use_info_1* printInfo[8]; // is overkill, 3 is more like it struct use_info_1* infoPtr; struct use_info_1* infoBase; DWORD index; DWORD i; LPSTR remoteName; WORD userWord; DWORD converter; WORD wstatus; LPSTR dosPointer; #if DBG IF_DEBUG(NETAPI) { DbgPrint("VrGetAssignListEntry\n"); VrDumpRealMode16BitRegisters(FALSE); } #endif // // maximum possible enumeration buffer size = 26 * (26 + 256 + 3) = 7410 // which we'll round to 8K, which is overkill. Decided to allocate 2K // #define ASSIGN_LIST_BUFFER_SIZE 2048 receiveBuffer = (LPBYTE)LocalAlloc(LMEM_FIXED, ASSIGN_LIST_BUFFER_SIZE); if (receiveBuffer == NULL) { // // BUGBUG - possibly incompatible error code // SET_ERROR((WORD)ERROR_NOT_ENOUGH_MEMORY); return; } parameters.Level = 1; parameters.Buffer = receiveBuffer; parameters.BufLen = ASSIGN_LIST_BUFFER_SIZE; header.Status = 0; header.ClientMachineName = NULL; header.ClientTransportName = NULL; ntstatus = XsNetUseEnum(&header, ¶meters, REM16_use_info_1, NULL); // // if XsNetUseEnum didn't have any problems, convert the actual status // code to that returned in the header // if (ntstatus != STATUS_SUCCESS) { status = NetpNtStatusToApiStatus(ntstatus); } else { status = (DWORD)header.Status; // // we really want to brute-force this, so make sure we have all the // data // #if DBG IF_DEBUG(NETAPI) { if (status != NERR_Success) { DbgPrint("VrGetAssignListEntry: XsNetUseEnum returns header.Status == %d\n", status); } } if (status == NERR_Success) { ASSERT(parameters.EntriesRead == parameters.TotalAvail); } #endif } entryNumber = getBX(); if (status == NERR_Success) { // // only do the following if the bx'th entry is in the list // if (parameters.EntriesRead > entryNumber) { // // we need to emulate the action of the DOS Redirector: we need to // sort the entries into ascending drive entries followed by // ascending printer entries. There were no such things as UNC // connections in the original (3.1) version of DOS, so we ignore // any in our list. Also ignored are IPC connections and comms // connections // RtlZeroMemory(driveInfo, sizeof(driveInfo)); RtlZeroMemory(printInfo, sizeof(printInfo)); infoPtr = (struct use_info_1*)receiveBuffer; // // XsNetUseEnum returns pointers in the structure as actual offsets // from the start of the buffer + a converter word. We have to // recalculate the actual pointers as // // start of enum buffer + (pointer offset - converter dword) // // we have to convert the 16-bit converter word to a dword for // 32-bit pointer arithmetic // driveInfo[index] = infoBase + ((DWORD)infoPtr->ui1_remote - converter); // infoBase = infoPtr; converter = (DWORD)header.Converter; for (i = 0; i < parameters.EntriesRead; ++i) { // // ignore UNCs - local name is NULL string (\0) // if (infoPtr->ui1_asg_type == USE_DISKDEV && infoPtr->ui1_local[0]) { index = toupper(infoPtr->ui1_local[0])-'A'; driveInfo[index] = infoPtr; #if DBG IF_DEBUG(NETAPI) { DbgPrint("Index=%d Drive=%s Netname=%s\n", index, infoPtr->ui1_local, (LPSTR)infoBase + ((DWORD)infoPtr->ui1_remote - converter) ); } #endif } else if (infoPtr->ui1_asg_type == USE_SPOOLDEV && infoPtr->ui1_local[0]) { // // NOTE: assume there was never, is not, and will never be // such a thing as LPT0: // index = infoPtr->ui1_local[3] - '1'; printInfo[index] = infoPtr; #if DBG IF_DEBUG(NETAPI) { DbgPrint("Index=%d Printer=%s Netname=%s\n", index, infoPtr->ui1_local, (LPSTR)infoBase + ((DWORD)infoPtr->ui1_remote - converter) ); } #endif } ++infoPtr; } // // now look along the list(s) for the bx'th (in entryNumber) entry // ++entryNumber; for (i = 0; i < ARRAY_ELEMENTS(driveInfo); ++i) { if (driveInfo[i]) { --entryNumber; if (!entryNumber) { infoPtr = driveInfo[i]; userWord = DefineMacroDriveUserWords[i]; break; } } } // // if entryNumber was not reduced to 0 then check the printers // if (entryNumber) { for (i = 0; i < ARRAY_ELEMENTS(printInfo); ++i) { if (printInfo[i]) { --entryNumber; if (!entryNumber) { infoPtr = printInfo[i]; userWord = DefineMacroPrintUserWords[i]; break; } } } } // // if entryNumber is 0 then we found the bx'th entry. Return it. // if (!entryNumber) { #if DBG IF_DEBUG(NETAPI) { DbgPrint("LocalName=%s, RemoteName=%s, UserWord=%04x\n", infoPtr->ui1_local, (LPSTR)infoBase + ((DWORD)infoPtr->ui1_remote - converter), userWord ); } #endif // // copy the strings to DOS memory, making sure to upper case // them and convert / to \. // strcpy(POINTER_FROM_WORDS(getDS(), getSI()), infoPtr->ui1_local); dosPointer = LPSTR_FROM_WORDS(getES(), getDI()); remoteName = (LPSTR)infoBase + ((DWORD)infoPtr->ui1_remote - converter); wstatus = VrpTranslateDosNetPath(&remoteName, &dosPointer); #if DBG IF_DEBUG(NETAPI) { if (wstatus != 0) { DbgPrint("VrGetAssignListEntry: wstatus == %d\n", wstatus); } } #endif setBL((BYTE)(infoPtr->ui1_asg_type == 0 ? 4 : 3)); setCX(userWord); // // return some innocuous (?!) values for the unsupported // returned parameters // setBH((BYTE)(infoPtr->ui1_status ? 1 : 0)); // 'interesting' bits (?) } else { status = ERROR_NO_MORE_FILES; } } else { status = ERROR_NO_MORE_FILES; } } // // only return carry clear if no error occurred. Even if ERROR_MORE_DATA // set CF // if (status) { SET_ERROR(VrpMapDosError(status)); } else { setCF(0); } // // free resources // LocalFree(receiveBuffer); } VOID VrDefineMacro( VOID ) /*++ Routine Description: Old version of NetUseAdd. Convert to NetUseAdd Arguments: Function 5F03h ENTRY BL = device type 3 = printer 4 = drive bit 7 on means use the wksta password when connecting ** UNSUPPORTED ** CX = user word DS:SI = local device Can be NUL device name, indicating UNC use ES:DI = remote name Return Value: CF = 0 success CF = 1 AX = ERROR_INVALID_PARAMETER (87) ERROR_INVALID_PASSWORD (86) ERROR_INVALID_DRIVE (15) ERROR_ALREADY_ASSIGNED (85) ERROR_PATH_NOT_FOUND (3) ERROR_ACCESS_DENIED (5) ERROR_NOT_ENOUGH_MEMORY (8) ERROR_NO_MORE_FILES (18) ERROR_REDIR_PAUSED (72) --*/ { NET_API_STATUS status; XS_NET_USE_ADD parameters; XS_PARAMETER_HEADER header; NTSTATUS ntstatus; BYTE bl; LPSTR netStringPointer; WORD index; // // modifiable descriptor string // char descriptor[sizeof(REM16_use_info_1)]; // // buffer for use_info_1 plus remote string plus password // char useBuffer[sizeof(struct use_info_1) + LM20_PATHLEN + 1 + LM20_PWLEN + 1]; WORD wstatus; LPBYTE variableData; DWORD len; LPSTR dosString; #if DBG IF_DEBUG(NETAPI) { DbgPrint("VrDefineMacro \"%s\" == \"%s\"\n", LPSTR_FROM_WORDS(getDS(), getSI()), LPSTR_FROM_WORDS(getES(), getDI()) ); } #endif bl = getBL(); if (bl == 3) { ((struct use_info_1*)useBuffer)->ui1_asg_type = 1; // USE_SPOOLDEV } else if (bl == 4) { ((struct use_info_1*)useBuffer)->ui1_asg_type = 0; // USE_DISKDEV } else { SET_ERROR(ERROR_INVALID_PARAMETER); return; } // // copy the standard 16-bit use_info_1 structure descriptor to the // modifiable descriptor string: if we discover a NUL password then we // set the ui1_password field to NULL and the corresponding descriptor // character to 'O' // strcpy(descriptor, REM16_use_info_1); // // check the local name length // dosString = LPSTR_FROM_WORDS(getDS(), getSI()); if (dosString) { if ((len = strlen(dosString) + 1) > LM20_DEVLEN + 1) { SET_ERROR(ERROR_INVALID_PARAMETER); return; } // // copy the local device name into the use_info_1 structure // RtlCopyMemory(((struct use_info_1*)useBuffer)->ui1_local, dosString, len); // // BUGBUG - Code Page, Kanji, DBCS, Locale? // _strupr(((struct use_info_1*)useBuffer)->ui1_local); } else { ((struct use_info_1*)useBuffer)->ui1_local[0] = 0; } // // copy the remote name to the end of the use_info_1 structure. If there's // an error, return it // netStringPointer = POINTER_FROM_WORDS(getES(), getDI()); variableData = (LPBYTE)&((struct use_info_1*)useBuffer)[1]; ((struct use_info_1*)useBuffer)->ui1_remote = variableData; wstatus = VrpTranslateDosNetPath(&netStringPointer, &variableData); if (wstatus) { SET_ERROR(wstatus); return; } // // if there was a password with this remote name, copy it to the end of // the variable data area // if (*netStringPointer) { if ((len = strlen(netStringPointer) + 1) > LM20_PWLEN + 1) { SET_ERROR(ERROR_INVALID_PASSWORD); return; } else { ((struct use_info_1*)useBuffer)->ui1_password = netStringPointer; RtlCopyMemory(variableData, netStringPointer, len); } } else { // // there is no password - set the password pointer field to NULL and // change the descriptor character for this field to 'O' signifying // that there will be no string in the variable data for this field // ((struct use_info_1*)useBuffer)->ui1_password = NULL; descriptor[4] = REM_NULL_PTR; // 'O' } parameters.Level = 1; parameters.Buffer = useBuffer; parameters.BufLen = sizeof(useBuffer); header.Status = 0; header.ClientMachineName = NULL; header.ClientTransportName = NULL; ntstatus = XsNetUseAdd(&header, ¶meters, descriptor, NULL); if (!NT_SUCCESS(ntstatus)) { status = NetpNtStatusToApiStatus(ntstatus); #if DBG if (!NT_SUCCESS(ntstatus)) { IF_DEBUG(NETAPI) { DbgPrint("VrDefineMacro: Error: XsNetUseAdd returns %x\n", ntstatus); } } #endif } else { // // no error generated in XsNetUseAdd. Get the status of the NetUseAdd // proper from the header // status = (NET_API_STATUS)header.Status; } if (status != NERR_Success) { #if DBG IF_DEBUG(NETAPI) { DbgPrint("Error: VrDefineMacro: XsNetUseAdd returns %u\n", status); } #endif SET_ERROR((WORD)status); } else { // // set the user word in the appropriate list // if (bl == 3) { index = ((struct use_info_1*)useBuffer)->ui1_local[3] - '0'; DefineMacroPrintUserWords[index] = getCX(); } else if (((struct use_info_1*)useBuffer)->ui1_local[0]) { // // note that we already upper-cased the device name // index = ((struct use_info_1*)useBuffer)->ui1_local[0] - 'A'; DefineMacroDriveUserWords[index] = getCX(); } // // BUGBUG - don't record user word for UNC connections???? // setCF(0); } } VOID VrBreakMacro( VOID ) /*++ Routine Description: Old version of NetUseDel. Convert to NetUseDel Arguments: Function 5F04h ENTRY DS:SI = buffer containing device name of redirection to break Return Value: CF = 0 success CF = 1 AX = ERROR_PATH_NOT_FOUND (3) ERROR_ACCESS_DENIED (5) ERROR_NOT_ENOUGH_MEMORY (8) ERROR_REDIR_PAUSED (72) ERROR_NO_MORE_FILES (18) --*/ { NTSTATUS ntstatus; NET_API_STATUS status; XS_NET_USE_DEL parameters; XS_PARAMETER_HEADER header; #if DBG IF_DEBUG(NETAPI) { DbgPrint("VrBreakMacro %s\n", LPSTR_FROM_WORDS(getDS(), getSI())); } #endif parameters.UseName = LPSTR_FROM_WORDS(getDS(), getSI()); parameters.Force = USE_LOTS_OF_FORCE; header.Status = 0; header.ClientMachineName = NULL; header.ClientTransportName = NULL; ntstatus = XsNetUseDel(&header, ¶meters, NULL, NULL); // // if XsNetUseDel failed then map the NT error returned into a Net error // else get the result of the NetUseDel proper from the header structure // if (ntstatus != STATUS_SUCCESS) { status = NetpNtStatusToApiStatus(ntstatus); } else { status = (NET_API_STATUS)header.Status; if (status != NERR_Success) { SET_ERROR(VrpMapDosError(status)); } else { setCF(0); } } } // // private routines // NET_API_STATUS VrpTransactVdm( IN BOOL NullSessionFlag ) /*++ Routine Description: Performs transaction request for NetTransactAPI and NetNullTransactAPI Arguments: NullSessionFlag - TRUE if the transaction request will use a NULL session VDM DS:SI points at a transaction descriptor structure: far pointer to transaction name (\\COMPUTER\PIPE\LANMAN) far pointer to password for connection far pointer to send parameter buffer far pointer to send data buffer far pointer to receive set-up buffer far pointer to receive parameter buffer far pointer to receive data buffer unsigned short send parameter buffer length unsigned short send data buffer length unsigned short receive parameter buffer length unsigned short receive data buffer length unsigned short receive set-up buffer length unsigned short flags unsigned long timeout unsigned short reserved unsigned short send set-up buffer length Return Value: NET_API_STATUS Success - NERR_Success Failure - return code from RxpTransactSmb --*/ { struct tr_packet* transactionPacket; DWORD receiveBufferLen; NET_API_STATUS status; char computerName[LM20_UNCLEN+1]; LPSTR pipeName; DWORD i; LPWSTR uncName; UNICODE_STRING uString; ANSI_STRING aString; NTSTATUS ntstatus; LPBYTE parameterBuffer; LPBYTE pSendParameters; LPBYTE pReceiveParameters; WORD sendParameterLen; WORD receiveParameterLen; WORD apiNumber; #if DBG BOOL dumpRxData; IF_DEBUG(NETAPI) { DbgPrint("VrpTransactVdm: tr_packet @ %04x:%04x\n", getDS(), getSI()); } #endif transactionPacket = (struct tr_packet*)POINTER_FROM_WORDS(getDS(), getSI()); #if DBG IF_DEBUG(NETAPI) { DumpTransactionPacket(transactionPacket, TRUE, TRUE); } #endif receiveBufferLen = (DWORD)READ_WORD(&transactionPacket->tr_rdlen); // // try to extract the UNC computer name from the pipe name // pipeName = LPSTR_FROM_POINTER(&transactionPacket->tr_name); if (IS_ASCII_PATH_SEPARATOR(pipeName[0]) && IS_ASCII_PATH_SEPARATOR(pipeName[1])) { computerName[0] = computerName[1] = '\\'; for (i = 2; i < sizeof(computerName)-1; ++i) { if (IS_ASCII_PATH_SEPARATOR(pipeName[i])) { break; } computerName[i] = pipeName[i]; } if (IS_ASCII_PATH_SEPARATOR(pipeName[i])) { computerName[i] = '\0'; pipeName = computerName; } } RtlInitAnsiString(&aString, pipeName); ntstatus = RtlAnsiStringToUnicodeString(&uString, &aString, (BOOLEAN)TRUE); if (!NT_SUCCESS(ntstatus)) { #if DBG IF_DEBUG(NETAPI) { DbgPrint("VrpTransactVdm: Unexpected situation: RtlAnsiStringToUnicodeString returns %x\n", ntstatus); } #endif return ERROR_NOT_ENOUGH_MEMORY; } uncName = uString.Buffer; #if DBG IF_DEBUG(NETAPI) { DbgPrint("VrpTransactVdm: UncName=%ws\n", uncName); } #endif // // if the app supplies different send and receive parameter buffer pointers // we have to collapse them into the same buffer // pSendParameters = LPBYTE_FROM_POINTER(&transactionPacket->tr_spbuf); pReceiveParameters = LPBYTE_FROM_POINTER(&transactionPacket->tr_rpbuf); sendParameterLen = READ_WORD(&transactionPacket->tr_splen); receiveParameterLen = READ_WORD(&transactionPacket->tr_rplen); if (pSendParameters != pReceiveParameters) { parameterBuffer = (LPBYTE)LocalAlloc( LMEM_FIXED, max(sendParameterLen, receiveParameterLen) ); if (parameterBuffer == NULL) { return ERROR_NOT_ENOUGH_MEMORY; } RtlMoveMemory(parameterBuffer, pSendParameters, sendParameterLen); pSendParameters = pReceiveParameters = parameterBuffer; } else { parameterBuffer = NULL; } // // in the case of remoted NetUserAdd2, NetUserPasswordSet2 and NetUserSetInfo2 // we have to encrypt any passwords if not already encrypted. We will change // data in the parameter and send data buffer. Since we assume that this call // is coming from the NET function library and not from the app, it should // be okay to modify these buffers and not restore them before this function // is complete // apiNumber = READ_WORD(pSendParameters); if (apiNumber == API_WUserAdd2 || apiNumber == API_WUserPasswordSet2 || apiNumber == API_WUserSetInfo2) { LPBYTE parameterPointer = pSendParameters + sizeof(WORD); LPBYTE passwordPointer; DWORD parmNum = PARMNUM_ALL; // // skip over parameter descriptor and data descriptor // parameterPointer += strlen(parameterPointer) + 1; parameterPointer += strlen(parameterPointer) + 1; // // the next thing in the parameter buffer for SetInfo2 and PasswordSet2 // is the user name: skip it // if (apiNumber != API_WUserAdd2) { parameterPointer += strlen(parameterPointer) + 1; } // // if this is PasswordSet2 then parameterPointer is pointing at the // old and new passwords. Remember this address and scan forward to // the password encryption flag/new cleartext password length // // if this is AddUser2, we are pointing at the level which we are not // interested in; skip forward to the encryption flag/cleartext password // length // // if this is SetInfo2, we are pointing at the level which we are not // interested in; skip forward to the parmnum. Record that. Then skip // forward again to the encryption flag/cleartext password length // if (apiNumber == API_WUserPasswordSet2) { passwordPointer = parameterPointer; parameterPointer += ENCRYPTED_PWLEN * 2; } else { parameterPointer += sizeof(WORD); if (apiNumber == API_WUserSetInfo2) { parmNum = (DWORD)READ_WORD(parameterPointer); parameterPointer += sizeof(WORD); } // // in the case of NetUserAdd2 and NetUserSetInfo2, the data buffer // contains the password. If the SetInfo2 is using PARMNUM_ALL then // the password is in the same place as for AddUser2: in a user_info_1 // or user_info_2 structure. Luckily, the password is at the same // offset for both structures. // // If this is SetInfo2 with USER_PASSWORD_PARMNUM then the send data // pointer points at the password // passwordPointer = LPBYTE_FROM_POINTER(&transactionPacket->tr_sdbuf); if (parmNum == PARMNUM_ALL) { passwordPointer += (DWORD)&((struct user_info_1*)0)->usri1_password; } } // // only perform encryption if parmNum is PARMNUM_ALL or USER_PASSWORD_PARMNUM // if (parmNum == PARMNUM_ALL || parmNum == USER_PASSWORD_PARMNUM) { // // in all cases, parameterPointer points at the encryption flag // if (!READ_WORD(parameterPointer)) { WORD cleartextLength; // // the password(s) is (are) not already encrypted (surprise!). We // have to do it. If encryption fails for any reason, return an // internal error. We do not want to fail-back to putting clear-text // passwords on the wire in this case // cleartextLength = (WORD)strlen(passwordPointer); // // NetUserPasswordSet2 requires a different method than the // other 2 // if (apiNumber == API_WUserPasswordSet2) { NTSTATUS ntStatus; LPBYTE oldPasswordPointer = passwordPointer; ENCRYPTED_LM_OWF_PASSWORD oldEncryptedWithNew; ENCRYPTED_LM_OWF_PASSWORD newEncryptedWithOld; ntStatus = RtlCalculateLmOwfPassword( passwordPointer, (PLM_OWF_PASSWORD)passwordPointer ); if (!NT_SUCCESS(ntStatus)) { status = NERR_InternalError; goto VrpTransactVdm_exit; } passwordPointer += ENCRYPTED_PWLEN; cleartextLength = (WORD)strlen(passwordPointer); ntStatus = RtlCalculateLmOwfPassword( passwordPointer, (PLM_OWF_PASSWORD)passwordPointer ); if (!NT_SUCCESS(ntStatus)) { status = NERR_InternalError; goto VrpTransactVdm_exit; } // // for PasswordSet2, we need to double-encrypt the passwords // ntStatus = RtlEncryptLmOwfPwdWithLmOwfPwd( (PLM_OWF_PASSWORD)oldPasswordPointer, (PLM_OWF_PASSWORD)passwordPointer, &oldEncryptedWithNew ); if (!NT_SUCCESS(ntStatus)) { status = NERR_InternalError; goto VrpTransactVdm_exit; } ntStatus = RtlEncryptLmOwfPwdWithLmOwfPwd( (PLM_OWF_PASSWORD)passwordPointer, (PLM_OWF_PASSWORD)oldPasswordPointer, &newEncryptedWithOld ); if (!NT_SUCCESS(ntStatus)) { status = NERR_InternalError; goto VrpTransactVdm_exit; } RtlCopyMemory(oldPasswordPointer, &oldEncryptedWithNew, sizeof(oldEncryptedWithNew) ); RtlCopyMemory(passwordPointer, &newEncryptedWithOld, sizeof(newEncryptedWithOld) ); } else { if (!EncryptPassword(uncName, passwordPointer)) { status = NERR_InternalError; goto VrpTransactVdm_exit; } } // // set the password encrypted flag in the parameter buffer // WRITE_WORD(parameterPointer, 1); // // record the length of the cleartext password (the new one in case // of PasswordSet2) // WRITE_WORD(parameterPointer + sizeof(WORD), cleartextLength); } } } status = RxpTransactSmb( (LPTSTR)uncName, NULL, // transport name pSendParameters, (DWORD)sendParameterLen, LPBYTE_FROM_POINTER(&transactionPacket->tr_sdbuf), (DWORD)READ_WORD(&transactionPacket->tr_sdlen), pReceiveParameters, (DWORD)receiveParameterLen, LPBYTE_FROM_POINTER(&transactionPacket->tr_rdbuf), &receiveBufferLen, NullSessionFlag ); // // if we received data, set the received data length in the structure // if (status == NERR_Success || status == ERROR_MORE_DATA) { WRITE_WORD(&transactionPacket->tr_rdlen, receiveBufferLen); } // // if we munged the parameter buffer then copy the returned parameters to // the app's supplied buffer // if (parameterBuffer) { RtlMoveMemory(LPBYTE_FROM_POINTER(&transactionPacket->tr_rpbuf), pReceiveParameters, receiveParameterLen ); } #if DBG IF_DEBUG(NETAPI) { DbgPrint("VrpTransactVdm: returning %d\n\n", status); if (status == NERR_Success || status == ERROR_MORE_DATA) { dumpRxData = TRUE; } else { dumpRxData = FALSE; } DumpTransactionPacket(transactionPacket, FALSE, dumpRxData); } #endif VrpTransactVdm_exit: RtlFreeUnicodeString(&uString); if (parameterBuffer) { LocalFree((HLOCAL)parameterBuffer); } return status; } BOOL EncryptPassword( IN LPWSTR ServerName, IN OUT LPBYTE Password ) /*++ Routine Description: Encrypts an ANSI password Arguments: ServerName - pointer to UNICODE server name. Server is where we are going to send the encrypted password Password - pointer to buffer containing on input an ANSI password (<= 14 characters, plus NUL), and on output contains the 16-byte encrypted password Return Value: BOOL TRUE - Password has been encrypted FALSE - couldn't encrypt password. Password is in unknown state --*/ { NTSTATUS ntStatus; LM_OWF_PASSWORD lmOwfPassword; LM_SESSION_KEY lanmanKey; _strupr(Password); ntStatus = RtlCalculateLmOwfPassword(Password, &lmOwfPassword); if (NT_SUCCESS(ntStatus)) { ntStatus = GetLanmanSessionKey(ServerName, (LPBYTE)&lanmanKey); if (NT_SUCCESS(ntStatus)) { ntStatus = RtlEncryptLmOwfPwdWithLmSesKey(&lmOwfPassword, &lanmanKey, (PENCRYPTED_LM_OWF_PASSWORD)Password ); } } return NT_SUCCESS(ntStatus); } #if DBG PRIVATE VOID DumpTransactionPacket( IN struct tr_packet* TransactionPacket, IN BOOL IsInput, IN BOOL DumpData ) { LPBYTE password; WORD parmSeg; WORD parmOff; WORD dataSeg; WORD dataOff; DWORD parmLen; DWORD dataLen; char passwordBuf[8*3+1]; password = LPBYTE_FROM_POINTER(&TransactionPacket->tr_passwd); if (password) { sprintf(passwordBuf, "%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x", password[0], password[1], password[2], password[3], password[4], password[5], password[6], password[7] ); } else { passwordBuf[0] = 0; } DbgPrint( "DumpTransactionPacket(%08x)\n" "name %04x:%04x \"%s\"\n" "password %04x:%04x %s\n" "send parm buffer %04x:%04x\n" "send data buffer %04x:%04x\n" "rcv setup buffer %04x:%04x\n" "rcv parm buffer %04x:%04x\n" "rcv data buffer %04x:%04x\n" "send parm len %04x\n" "send data len %04x\n" "rcv parm len %04x\n" "rcv data len %04x\n" "rcv setup len %04x\n" "flags %04x\n" "timeout %08x (%d)\n" "reserved %04x\n" "send setup len %04x\n" "\n", TransactionPacket, GET_SEGMENT(&TransactionPacket->tr_name), GET_OFFSET(&TransactionPacket->tr_name), LPSTR_FROM_POINTER(&TransactionPacket->tr_name), GET_SEGMENT(&TransactionPacket->tr_passwd), GET_OFFSET(&TransactionPacket->tr_passwd), passwordBuf, GET_SEGMENT(&TransactionPacket->tr_spbuf), GET_OFFSET(&TransactionPacket->tr_spbuf), GET_SEGMENT(&TransactionPacket->tr_sdbuf), GET_OFFSET(&TransactionPacket->tr_sdbuf), GET_SEGMENT(&TransactionPacket->tr_rsbuf), GET_OFFSET(&TransactionPacket->tr_rsbuf), GET_SEGMENT(&TransactionPacket->tr_rpbuf), GET_OFFSET(&TransactionPacket->tr_rpbuf), GET_SEGMENT(&TransactionPacket->tr_rdbuf), GET_OFFSET(&TransactionPacket->tr_rdbuf), READ_WORD(&TransactionPacket->tr_splen), READ_WORD(&TransactionPacket->tr_sdlen), READ_WORD(&TransactionPacket->tr_rplen), READ_WORD(&TransactionPacket->tr_rdlen), READ_WORD(&TransactionPacket->tr_rslen), READ_WORD(&TransactionPacket->tr_flags), READ_DWORD(&TransactionPacket->tr_timeout), READ_DWORD(&TransactionPacket->tr_timeout), READ_WORD(&TransactionPacket->tr_resvd), READ_WORD(&TransactionPacket->tr_sslen) ); if (IsInput) { parmLen = (DWORD)READ_WORD(&TransactionPacket->tr_splen); dataLen = (DWORD)READ_WORD(&TransactionPacket->tr_sdlen); parmSeg = GET_SEGMENT(&TransactionPacket->tr_spbuf); parmOff = GET_OFFSET(&TransactionPacket->tr_spbuf); dataSeg = GET_SEGMENT(&TransactionPacket->tr_sdbuf); dataOff = GET_OFFSET(&TransactionPacket->tr_sdbuf); } else { parmLen = (DWORD)READ_WORD(&TransactionPacket->tr_rplen); dataLen = (DWORD)READ_WORD(&TransactionPacket->tr_rdlen); parmSeg = GET_SEGMENT(&TransactionPacket->tr_rpbuf); parmOff = GET_OFFSET(&TransactionPacket->tr_rpbuf); dataSeg = GET_SEGMENT(&TransactionPacket->tr_rdbuf); dataOff = GET_OFFSET(&TransactionPacket->tr_rdbuf); } if (DumpData) { if (IsInput) { IF_DEBUG(TRANSACT_TX) { if (parmLen) { DbgPrint("Send Parameters:\n"); VrDumpDosMemory('B', parmLen, parmSeg, parmOff); } if (dataLen) { DbgPrint("Send Data:\n"); VrDumpDosMemory('B', dataLen, dataSeg, dataOff); } } } else { IF_DEBUG(TRANSACT_RX) { if (parmLen) { DbgPrint("Received Parameters:\n"); VrDumpDosMemory('B', parmLen, parmSeg, parmOff); } if (dataLen) { DbgPrint("Received Data:\n"); VrDumpDosMemory('B', dataLen, dataSeg, dataOff); } } } } } #endif