/*++ Copyright (c) 1991 Microsoft Corporation Module Name: ApiLogon.c Abstract: This module contains individual API handlers for the NetLogon APIs. SUPPORTED : NetGetDCName, NetLogonEnum, NetServerAuthenticate, NetServerPasswordSet, NetServerReqChallenge, NetWkstaUserLogoff, NetWkstaUserLogon. SEE ALSO : NetAccountDeltas, NetAccountSync - in ApiAcct.c. Author: Shanku Niyogi (w-shanku) 04-Apr-1991 Revision History: --*/ // // Logon APIs are Unicode only. // #ifndef UNICODE #define UNICODE #endif #include "xactsrvp.h" #include #include // must be included before #include // must be included before #include // must be included before #include // I_NetAccountDeltas and I_NetAccountSync prototypes // // Declaration of descriptor strings. // STATIC const LPDESC Desc16_user_logon_info_0 = REM16_user_logon_info_0; STATIC const LPDESC Desc32_user_logon_info_0 = REM32_user_logon_info_0; STATIC const LPDESC Desc16_user_logon_info_1 = REM16_user_logon_info_1; STATIC const LPDESC Desc32_user_logon_info_1 = REM32_user_logon_info_1; STATIC const LPDESC Desc16_user_logon_info_2 = REM16_user_logon_info_2; STATIC const LPDESC Desc32_user_logon_info_2 = REM32_user_logon_info_2; STATIC const LPDESC Desc16_user_logoff_info_1 = REM16_user_logoff_info_1; STATIC const LPDESC Desc32_user_logoff_info_1 = REM32_user_logoff_info_1; NTSTATUS XsNetGetDCName ( API_HANDLER_PARAMETERS ) /*++ Routine Description: This routine handles a call to NetGetDCName. 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_GET_DC_NAME parameters = Parameters; LPTSTR nativeDomain = NULL; // Native parameters LPTSTR dcName = NULL; API_HANDLER_PARAMETERS_REFERENCE; // Avoid warnings try { // // Translate parameters, check for errors. // XsConvertTextParameter( nativeDomain, (LPSTR)XsSmbGetPointer( ¶meters->Domain ) ); // // Make the local call. // status = NetGetDCName( NULL, nativeDomain, (LPBYTE *)&dcName ); if ( !XsApiSuccess( status )) { IF_DEBUG(ERRORS) { NetpKdPrint(( "XsNetGetDCName: NetGetDCName failed: %X\n", status )); } goto cleanup; } // // Put string into buffer. Convert from Unicode if necessary. // if ( (DWORD)SmbGetUshort( ¶meters->BufLen ) <= NetpUnicodeToDBCSLen( dcName )) { status = NERR_BufTooSmall; } else { NetpCopyWStrToStrDBCS( (LPSTR)XsSmbGetPointer( ¶meters->Buffer ), dcName ); } IF_DEBUG(LOGON) { NetpKdPrint(( "Name is %ws\n", dcName )); } cleanup: ; } except ( EXCEPTION_EXECUTE_HANDLER ) { status = (WORD)RtlNtStatusToDosError( GetExceptionCode() ); } // // Set return data count. // if ( status == NERR_Success ) { SmbPutUshort( ¶meters->BufLen, (USHORT)( STRLEN( dcName ) + 1 )); } else { SmbPutUshort( ¶meters->BufLen, 0 ); } Header->Status = (WORD)status; NetpMemoryFree( nativeDomain ); NetApiBufferFree( dcName ); return STATUS_SUCCESS; } // XsNetGetDCName NTSTATUS XsNetLogonEnum ( API_HANDLER_PARAMETERS ) /*++ Routine Description: This routine handles a call to NetLogonEnum. 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_LOGON_ENUM parameters = Parameters; LPVOID outBuffer= NULL; DWORD entriesRead = 0; DWORD totalEntries = 0; DWORD entriesFilled = 0; DWORD bytesRequired = 0; LPDESC nativeStructureDesc; API_HANDLER_PARAMETERS_REFERENCE; IF_DEBUG(LOGON) { NetpKdPrint(( "XsNetLogonEnum: 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 (( SmbGetUshort( ¶meters->Level ) != 0 ) && ( SmbGetUshort( ¶meters->Level ) != 2 )) { Header->Status = ERROR_INVALID_LEVEL; goto cleanup; } // // Make the local call. // #ifdef LOGON_ENUM_SUPPORTED status = NetLogonEnum( NULL, (DWORD)SmbGetUshort( ¶meters->Level ), (LPBYTE *)&outBuffer, XsNativeBufferSize( SmbGetUshort( ¶meters->BufLen )), &entriesRead, &totalEntries, NULL ); #else // LOGON_ENUM_SUPPORTED status = NERR_InvalidAPI; #endif // LOGON_ENUM_SUPPORTED if ( !XsApiSuccess( status )) { IF_DEBUG(API_ERRORS) { NetpKdPrint(( "XsNetLogonEnum: NetLogonEnum failed: " "%X\n", status )); } Header->Status = (WORD)status; goto cleanup; } IF_DEBUG(LOGON) { NetpKdPrint(( "XsNetLogonEnum: 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_logon_info_0; StructureDesc = Desc16_user_logon_info_0; break; case 2: nativeStructureDesc = Desc32_user_logon_info_2; StructureDesc = Desc16_user_logon_info_2; 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(LOGON) { 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; } // XsNetLogonEnum NTSTATUS XsNetServerAuthenticate ( API_HANDLER_PARAMETERS ) /*++ Routine Description: This routine handles a call to NetServerAuthenticate. Arguments: API_HANDLER_PARAMETERS - Information about the API call. See XsTypes.h for details. Return Value: NTSTATUS - STATUS_SUCCESS or reason for failure. --*/ { PXS_NET_SERVER_AUTHENTICATE parameters = Parameters; NET_API_STATUS status; // Native parameters LPTSTR nativeRequestor = NULL; NETLOGON_CREDENTIAL inCredential = {0}; NETLOGON_CREDENTIAL outCredential = {0}; WCHAR AccountName[MAX_PATH+1]; API_HANDLER_PARAMETERS_REFERENCE; // Avoid warnings try { // // Translate parameters, check for errors. // XsConvertTextParameter( nativeRequestor, (LPSTR)XsSmbGetPointer( ¶meters->Requestor ) ); // // Copy the source credential, and zero out the destination // credential. // RtlCopyMemory( &inCredential, (PVOID)XsSmbGetPointer( ¶meters->Caller ), sizeof(NETLOGON_CREDENTIAL) ); RtlZeroMemory( &outCredential, sizeof(NETLOGON_CREDENTIAL) ); // // Build the account name. // NetpNCopyTStrToWStr( AccountName, nativeRequestor, MAX_PATH ); // // Make the local call. // status = NetpNtStatusToApiStatus( I_NetServerAuthenticate( NULL, AccountName, UasServerSecureChannel, nativeRequestor, &inCredential, &outCredential )); if ( !XsApiSuccess( status )) { IF_DEBUG(ERRORS) { NetpKdPrint(( "XsNetServerAuthenticate: I_NetServerAuthenticate " "failed: %X\n", status )); } Header->Status = (WORD)status; goto cleanup; } cleanup: // // Set the return credential. // RtlCopyMemory( parameters->Primary, &outCredential, sizeof(NETLOGON_CREDENTIAL) ); } except ( EXCEPTION_EXECUTE_HANDLER ) { Header->Status = (WORD)RtlNtStatusToDosError( GetExceptionCode() ); } NetpMemoryFree( nativeRequestor ); return STATUS_SUCCESS; } // XsNetServerAuthenticate NTSTATUS XsNetServerPasswordSet ( API_HANDLER_PARAMETERS ) /*++ Routine Description: This routine handles a call to NetGetDCName. Arguments: API_HANDLER_PARAMETERS - Information about the API call. See XsTypes.h for details. Return Value: NTSTATUS - STATUS_SUCCESS or reason for failure. --*/ { PXS_NET_SERVER_PASSWORD_SET parameters = Parameters; NET_API_STATUS status; // Native parameters LPTSTR nativeRequestor = NULL; NETLOGON_AUTHENTICATOR authIn = {0}; NETLOGON_AUTHENTICATOR authOut = {0}; ENCRYPTED_LM_OWF_PASSWORD password; WCHAR AccountName[MAX_PATH+1]; LPBYTE structure = NULL; // Conversion variables API_HANDLER_PARAMETERS_REFERENCE; // Avoid warnings try { // // Translate parameters, check for errors. // XsConvertTextParameter( nativeRequestor, (LPSTR)XsSmbGetPointer( ¶meters->Requestor ) ); // // Copy the source authenticator and password, and zero out the // destination authenticator. // structure = (LPBYTE)XsSmbGetPointer( ¶meters->Authenticator ); RtlCopyMemory( &authIn.Credential, structure, sizeof(NETLOGON_CREDENTIAL) ); structure += sizeof(NETLOGON_CREDENTIAL); authIn.timestamp = SmbGetUlong( structure ); RtlCopyMemory( &password, parameters->Password, sizeof(ENCRYPTED_LM_OWF_PASSWORD) ); RtlZeroMemory( &authOut, sizeof(NETLOGON_CREDENTIAL) ); // // Build the account name. // if( STRLEN( nativeRequestor ) >= MAX_PATH ) { Header->Status = NERR_PasswordTooShort; goto cleanup; } // Make sure its NULL terminated AccountName[MAX_PATH] = L'\0'; NetpNCopyTStrToWStr( AccountName, nativeRequestor, MAX_PATH ); // // Make the local call. // status = NetpNtStatusToApiStatus( I_NetServerPasswordSet( NULL, AccountName, UasServerSecureChannel, nativeRequestor, &authIn, &authOut, &password )); if ( !XsApiSuccess( status )) { IF_DEBUG(ERRORS) { NetpKdPrint(( "XsNetServerPasswordSet: " "I_NetServerPasswordSet failed: %X\n", status )); } // // !!! When protocol level is available in the header information, // we can check it. Right now, we ignore this code. // // For clients older than LanMan 2.1, return a different error code. // LANMAN 2.1 Protocol Level is 6. // #if 0 if ( status == NERR_TimeDiffAtDC && Header->ProtocolLevel < 6 ) { status = NERR_SyncRequired; } #endif Header->Status = (WORD)status; goto cleanup; } cleanup: // // Fill in 16 bit return structures. // structure = parameters->RetAuth; RtlCopyMemory( structure, &authOut.Credential, sizeof(NETLOGON_CREDENTIAL) ); structure += sizeof(NETLOGON_CREDENTIAL); SmbPutUlong( (LPDWORD)structure, authOut.timestamp ); } except ( EXCEPTION_EXECUTE_HANDLER ) { Header->Status = (WORD)RtlNtStatusToDosError( GetExceptionCode() ); } NetpMemoryFree( nativeRequestor ); return STATUS_SUCCESS; } // XsNetServerPasswordSet NTSTATUS XsNetServerReqChallenge ( API_HANDLER_PARAMETERS ) /*++ Routine Description: This routine handles a call to NetGetDCName. Arguments: API_HANDLER_PARAMETERS - Information about the API call. See XsTypes.h for details. Return Value: NTSTATUS - STATUS_SUCCESS or reason for failure. --*/ { PXS_NET_SERVER_REQ_CHALLENGE parameters = Parameters; NET_API_STATUS status; // Native parameters LPTSTR nativeRequestor = NULL; NETLOGON_CREDENTIAL inChallenge = {0}; NETLOGON_CREDENTIAL outChallenge = {0}; API_HANDLER_PARAMETERS_REFERENCE; // Avoid warnings try { // // Translate parameters, check for errors. // XsConvertTextParameter( nativeRequestor, (LPSTR)XsSmbGetPointer( ¶meters->Requestor ) ); // // Copy the source challenge, and zero out the destination // challenge. // RtlCopyMemory( &inChallenge, (PVOID)XsSmbGetPointer( ¶meters->Caller ), sizeof(NETLOGON_CREDENTIAL) ); RtlZeroMemory( &outChallenge, sizeof(NETLOGON_CREDENTIAL) ); // // Make the local call. // status = NetpNtStatusToApiStatus( I_NetServerReqChallenge( NULL, nativeRequestor, &inChallenge, &outChallenge )); if ( !XsApiSuccess( status )) { IF_DEBUG(ERRORS) { NetpKdPrint(( "XsNetServerReqChallenge: " "I_NetServerReqChallenge failed: %X\n", status )); } Header->Status = (WORD)status; goto cleanup; } cleanup: // // Set the return credential. // RtlCopyMemory( parameters->Primary, &outChallenge, sizeof(NETLOGON_CREDENTIAL) ); } except ( EXCEPTION_EXECUTE_HANDLER ) { Header->Status = (WORD)RtlNtStatusToDosError( GetExceptionCode() ); } NetpMemoryFree( nativeRequestor ); return STATUS_SUCCESS; } // XsNetServerReqChallenge NTSTATUS XsNetWkstaUserLogoff ( API_HANDLER_PARAMETERS ) /*++ Routine Description: This temporary routine just returns STATUS_NOT_IMPLEMENTED. 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_WKSTA_USER_LOGOFF parameters = Parameters; LPWSTR machineName = NULL; // Native parameters LPWSTR userName = NULL; NETLOGON_LOGOFF_UAS_INFORMATION buffer; LPBYTE stringLocation = NULL; // Conversion variables DWORD bytesRequired = 0; PWKSTA_16_USER_LOGOFF_REQUEST_1 usrLogoffReq = (PWKSTA_16_USER_LOGOFF_REQUEST_1)parameters->InBuf; PUSER_16_LOGOFF_INFO_1 logoffInfo; API_HANDLER_PARAMETERS_REFERENCE; // Avoid warnings try { // // Translate parameters, check for errors. // XsConvertUnicodeTextParameter( userName, (LPSTR)( usrLogoffReq->wlreq1_name ) ); XsConvertUnicodeTextParameter( machineName, (LPSTR)( usrLogoffReq->wlreq1_workstation ) ); if ( SmbGetUshort( ¶meters->Level ) != 1 ) { Header->Status = ERROR_INVALID_LEVEL; goto cleanup; } // // Make sure the workstation name in the logon request is the // name of the workstation from which the request came. // if ( wcscmp( machineName, Header->ClientMachineName ) ) { Header->Status = (WORD)ERROR_ACCESS_DENIED; goto cleanup; } // // Make the local call. // status = I_NetLogonUasLogoff( userName, machineName, &buffer ); if ( !XsApiSuccess(status)) { IF_DEBUG(ERRORS) { NetpKdPrint(( "XsNetWkstaUserLogoff: I_NetLogonUasLogoff " "failed: %X\n", status )); } Header->Status = (WORD)status; goto cleanup; } // // 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->OutBuf ) + SmbGetUshort( ¶meters->OutBufLen ) ); status = RapConvertSingleEntry( (LPBYTE)&buffer, Desc32_user_logoff_info_1, FALSE, (LPBYTE)XsSmbGetPointer( ¶meters->OutBuf ), (LPBYTE)XsSmbGetPointer( ¶meters->OutBuf ), Desc16_user_logoff_info_1, TRUE, &stringLocation, &bytesRequired, Response, NativeToRap ); if ( status != NERR_Success ) { IF_DEBUG(ERRORS) { NetpKdPrint(( "XsNetWkstaUserLogoff: RapConvertSingleEntry " "failed: %X\n", status )); } Header->Status = NERR_InternalError; goto cleanup; } IF_DEBUG(LOGON) { NetpKdPrint(( "32-bit data at %lx, 16-bit data at %lx, %ld BR\n", &buffer, SmbGetUlong( ¶meters->OutBuf ), bytesRequired )); } // // Determine return code based on the size of the buffer. // The user_logoff_info_1 structure has no variable data to pack, // but we do need to fill in the code field of the return structure. // if ( !XsCheckBufferSize( SmbGetUshort( ¶meters->OutBufLen ), Desc16_user_logoff_info_1, FALSE // not in native format )) { IF_DEBUG(ERRORS) { NetpKdPrint(( "XsNetWkstaUserLogoff: Buffer too small.\n" )); } Header->Status = NERR_BufTooSmall; } else if ( bytesRequired > (DWORD)SmbGetUshort( ¶meters->OutBufLen )) { IF_DEBUG(ERRORS) { NetpKdPrint(( "XsNetWkstaUserLogoff: More data available.\n" )); } Header->Status = ERROR_MORE_DATA; } if ( SmbGetUshort( ¶meters->OutBufLen ) > sizeof(WORD)) { logoffInfo = (PUSER_16_LOGOFF_INFO_1)XsSmbGetPointer( ¶meters->OutBuf ); SmbPutUshort( &logoffInfo->usrlogf1_code, VALID_LOGOFF ); } // // Set up the response parameters. // SmbPutUshort( ¶meters->TotalAvail, (WORD)bytesRequired ); cleanup: ; } except ( EXCEPTION_EXECUTE_HANDLER ) { Header->Status = (WORD)RtlNtStatusToDosError( GetExceptionCode() ); } NetpMemoryFree( userName ); NetpMemoryFree( machineName ); // // Determine return buffer size. // XsSetDataCount( ¶meters->OutBufLen, Desc16_user_logoff_info_1, Header->Converter, 1, Header->Status ); return STATUS_SUCCESS; } // XsNetWkstaUserLogoff NTSTATUS XsNetWkstaUserLogon ( API_HANDLER_PARAMETERS ) /*++ Routine Description: This routine handles a call to NetWkstaUserLogon. 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_WKSTA_USER_LOGON parameters = Parameters; LPWSTR machineName = NULL; // Native parameters LPWSTR userName = NULL; PNETLOGON_VALIDATION_UAS_INFO buffer = NULL; LPBYTE stringLocation = NULL; // Conversion variables DWORD bytesRequired = 0; PWKSTA_16_USER_LOGON_REQUEST_1 usrLogonReq = (PWKSTA_16_USER_LOGON_REQUEST_1)parameters->InBuf; PUSER_16_LOGON_INFO_1 logonInfo; API_HANDLER_PARAMETERS_REFERENCE; // Avoid warnings try { // // Translate parameters, check for errors. // XsConvertUnicodeTextParameter( userName, (LPSTR)( usrLogonReq->wlreq1_name ) ); XsConvertUnicodeTextParameter( machineName, (LPSTR)( usrLogonReq->wlreq1_workstation ) ); if ( SmbGetUshort( ¶meters->Level ) != 1 ) { Header->Status = ERROR_INVALID_LEVEL; goto cleanup; } // // Make sure the workstation name in the logon request is the // name of the workstation from which the request came. // if ( wcscmp( machineName, Header->ClientMachineName ) ) { Header->Status = (WORD)ERROR_ACCESS_DENIED; goto cleanup; } // // Make the local call. // status = I_NetLogonUasLogon( userName, machineName, &buffer ); if ( !XsApiSuccess ( status )) { IF_DEBUG(API_ERRORS) { NetpKdPrint(( "XsNetWkstaUserLogon: I_NetLogonUasLogon failed: " "%X\n", status)); } Header->Status = (WORD) status; goto cleanup; } // // 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->OutBuf ) + SmbGetUshort( ¶meters->OutBufLen ) ); status = RapConvertSingleEntry( (LPBYTE)buffer, Desc32_user_logon_info_1, FALSE, (LPBYTE)XsSmbGetPointer( ¶meters->OutBuf ), (LPBYTE)XsSmbGetPointer( ¶meters->OutBuf ), Desc16_user_logon_info_1, TRUE, &stringLocation, &bytesRequired, Response, NativeToRap ); if ( status != NERR_Success ) { IF_DEBUG(ERRORS) { NetpKdPrint(( "XsNetWkstaUserLogon: RapConvertSingleEntry " "failed: %X\n", status )); } Header->Status = NERR_InternalError; goto cleanup; } IF_DEBUG(LOGON) { NetpKdPrint(( "32-bit data at %lx, 16-bit data at %lx, %ld BR\n", buffer, SmbGetUlong( ¶meters->OutBuf ), bytesRequired )); } // // Determine return code based on the size of the buffer. // The user_logoff_info_1 structure has no variable data to pack, // but we do need to fill in the code field of the return structure. // if ( !XsCheckBufferSize( SmbGetUshort( ¶meters->OutBufLen ), Desc16_user_logon_info_1, FALSE // not in native format )) { IF_DEBUG(ERRORS) { NetpKdPrint(( "XsNetWkstaUserLogon: Buffer too small.\n" )); } Header->Status = NERR_BufTooSmall; } else if ( bytesRequired > (DWORD)SmbGetUshort( ¶meters->OutBufLen )) { IF_DEBUG(ERRORS) { NetpKdPrint(( "XsNetWkstaUserLogoff: More data available.\n" )); } Header->Status = ERROR_MORE_DATA; } else { // // Pack the response data. // Header->Converter = XsPackReturnData( (LPVOID)XsSmbGetPointer( ¶meters->OutBuf ), SmbGetUshort( ¶meters->OutBufLen ), Desc16_user_logon_info_1, 1 ); } if ( SmbGetUshort( ¶meters->OutBufLen ) > sizeof(WORD)) { logonInfo = (PUSER_16_LOGON_INFO_1)XsSmbGetPointer( ¶meters->OutBuf ); SmbPutUshort( &logonInfo->usrlog1_code, VALIDATED_LOGON ); } // // Set up the response parameters. // SmbPutUshort( ¶meters->TotalAvail, (WORD)bytesRequired ); cleanup: ; } except ( EXCEPTION_EXECUTE_HANDLER ) { Header->Status = (WORD)RtlNtStatusToDosError( GetExceptionCode() ); } NetpMemoryFree( userName ); NetpMemoryFree( machineName ); if ( buffer != NULL ) { NetApiBufferFree( buffer ); } // // Determine return buffer size. // XsSetDataCount( ¶meters->OutBufLen, Desc16_user_logon_info_1, Header->Converter, 1, Header->Status ); return STATUS_SUCCESS; } // XsNetWkstaUserLogon