/*++ Copyright (c) 1995 Microsoft Corporation Module Name: NdsLib32.c Abstract: This module implements the exposed user-mode link to Netware NDS support in the Netware redirector. For more comments, see ndslib32.h. Author: Cory West [CoryWest] 23-Feb-1995 --*/ #include NTSTATUS NwNdsOpenTreeHandle( IN PUNICODE_STRING puNdsTree, OUT PHANDLE phNwRdrHandle ) { NTSTATUS ntstatus, OpenStatus; IO_STATUS_BLOCK IoStatusBlock; OBJECT_ATTRIBUTES ObjectAttributes; ACCESS_MASK DesiredAccess = SYNCHRONIZE | FILE_LIST_DIRECTORY; WCHAR DevicePreamble[] = L"\\Device\\Nwrdr\\"; UINT PreambleLength = 14; WCHAR NameStr[128]; UNICODE_STRING uOpenName; UINT i; PNWR_NDS_REQUEST_PACKET Rrp; BYTE RrpData[1024]; // // Prepare the open name. // uOpenName.MaximumLength = sizeof( NameStr ); for ( i = 0; i < PreambleLength ; i++ ) NameStr[i] = DevicePreamble[i]; try { for ( i = 0 ; i < ( puNdsTree->Length / sizeof( WCHAR ) ) ; i++ ) { NameStr[i + PreambleLength] = puNdsTree->Buffer[i]; } } except ( EXCEPTION_EXECUTE_HANDLER ) { return STATUS_INVALID_PARAMETER; } uOpenName.Length = ( i * sizeof( WCHAR ) ) + ( PreambleLength * sizeof( WCHAR ) ); uOpenName.Buffer = NameStr; // // Set up the object attributes. // InitializeObjectAttributes( &ObjectAttributes, &uOpenName, OBJ_CASE_INSENSITIVE, NULL, NULL ); ntstatus = NtOpenFile( phNwRdrHandle, DesiredAccess, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_VALID_FLAGS, FILE_SYNCHRONOUS_IO_NONALERT ); if ( !NT_SUCCESS(ntstatus) ) return ntstatus; OpenStatus = IoStatusBlock.Status; // // Verify that this is a tree handle, not a server handle. // Rrp = (PNWR_NDS_REQUEST_PACKET)RrpData; Rrp->Version = 0; RtlCopyMemory( &(Rrp->Parameters).VerifyTree, puNdsTree, sizeof( UNICODE_STRING ) ); RtlCopyMemory( (BYTE *)(&(Rrp->Parameters).VerifyTree) + sizeof( UNICODE_STRING ), puNdsTree->Buffer, puNdsTree->Length ); try { ntstatus = NtFsControlFile( *phNwRdrHandle, NULL, NULL, NULL, &IoStatusBlock, FSCTL_NWR_NDS_VERIFY_TREE, (PVOID) Rrp, sizeof( NWR_NDS_REQUEST_PACKET ), NULL, 0 ); } except ( EXCEPTION_EXECUTE_HANDLER ) { ntstatus = GetExceptionCode(); goto CloseAndExit; } if ( !NT_SUCCESS( ntstatus )) { goto CloseAndExit; } // // Otherwise, all is well! // return OpenStatus; CloseAndExit: NtClose( *phNwRdrHandle ); *phNwRdrHandle = NULL; return ntstatus; } NTSTATUS NwNdsOpenGenericHandle( IN PUNICODE_STRING puNdsTree, OUT LPDWORD lpdwHandleType, OUT PHANDLE phNwRdrHandle ) { NTSTATUS ntstatus, OpenStatus; IO_STATUS_BLOCK IoStatusBlock; OBJECT_ATTRIBUTES ObjectAttributes; ACCESS_MASK DesiredAccess = SYNCHRONIZE | FILE_LIST_DIRECTORY; WCHAR DevicePreamble[] = L"\\Device\\Nwrdr\\"; UINT PreambleLength = 14; WCHAR NameStr[128]; UNICODE_STRING uOpenName; UINT i; PNWR_NDS_REQUEST_PACKET Rrp; BYTE RrpData[1024]; // // Prepare the open name. // uOpenName.MaximumLength = sizeof( NameStr ); for ( i = 0; i < PreambleLength ; i++ ) NameStr[i] = DevicePreamble[i]; try { for ( i = 0 ; i < ( puNdsTree->Length / sizeof( WCHAR ) ) ; i++ ) { NameStr[i + PreambleLength] = puNdsTree->Buffer[i]; } } except ( EXCEPTION_EXECUTE_HANDLER ) { return STATUS_INVALID_PARAMETER; } uOpenName.Length = ( i * sizeof( WCHAR ) ) + ( PreambleLength * sizeof( WCHAR ) ); uOpenName.Buffer = NameStr; // // Set up the object attributes. // InitializeObjectAttributes( &ObjectAttributes, &uOpenName, OBJ_CASE_INSENSITIVE, NULL, NULL ); ntstatus = NtOpenFile( phNwRdrHandle, DesiredAccess, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_VALID_FLAGS, FILE_SYNCHRONOUS_IO_NONALERT ); if ( !NT_SUCCESS(ntstatus) ) return ntstatus; OpenStatus = IoStatusBlock.Status; // // Verify that this is a tree handle, not a server handle. // Rrp = (PNWR_NDS_REQUEST_PACKET)RrpData; Rrp->Version = 0; RtlCopyMemory( &(Rrp->Parameters).VerifyTree, puNdsTree, sizeof( UNICODE_STRING ) ); RtlCopyMemory( (BYTE *)(&(Rrp->Parameters).VerifyTree) + sizeof( UNICODE_STRING ), puNdsTree->Buffer, puNdsTree->Length ); try { ntstatus = NtFsControlFile( *phNwRdrHandle, NULL, NULL, NULL, &IoStatusBlock, FSCTL_NWR_NDS_VERIFY_TREE, (PVOID) Rrp, sizeof( NWR_NDS_REQUEST_PACKET ), NULL, 0 ); } except ( EXCEPTION_EXECUTE_HANDLER ) { ntstatus = GetExceptionCode(); goto CloseAndExit2; } if ( !NT_SUCCESS( ntstatus )) { *lpdwHandleType = HANDLE_TYPE_NCP_SERVER; } else { *lpdwHandleType = HANDLE_TYPE_NDS_TREE; } return OpenStatus; CloseAndExit2: NtClose( *phNwRdrHandle ); *phNwRdrHandle = NULL; return ntstatus; } NTSTATUS NwOpenHandleWithSupplementalCredentials( IN PUNICODE_STRING puResourceName, IN PUNICODE_STRING puUserName, IN PUNICODE_STRING puPassword, OUT LPDWORD lpdwHandleType, OUT PHANDLE phNwHandle ) { NTSTATUS ntstatus, OpenStatus; IO_STATUS_BLOCK IoStatusBlock; OBJECT_ATTRIBUTES ObjectAttributes; ACCESS_MASK DesiredAccess = FILE_GENERIC_READ | FILE_GENERIC_WRITE; WCHAR DevicePreamble[] = L"\\Device\\Nwrdr\\"; UINT PreambleLength = 14; WCHAR NameStr[128]; UNICODE_STRING uOpenName; UINT i; PFILE_FULL_EA_INFORMATION pEaEntry; PBYTE EaBuffer; ULONG EaLength, EaNameLength, EaTotalLength; ULONG UserNameLen, PasswordLen, TypeLen, CredLen; PNWR_NDS_REQUEST_PACKET Rrp; BYTE RrpData[1024]; // // Prepare the open name. // uOpenName.MaximumLength = sizeof( NameStr ); for ( i = 0; i < PreambleLength ; i++ ) NameStr[i] = DevicePreamble[i]; try { for ( i = 0 ; i < ( puResourceName->Length / sizeof( WCHAR ) ) ; i++ ) { NameStr[i + PreambleLength] = puResourceName->Buffer[i]; } } except ( EXCEPTION_EXECUTE_HANDLER ) { return STATUS_INVALID_PARAMETER; } uOpenName.Length = ( i * sizeof( WCHAR ) ) + ( PreambleLength * sizeof( WCHAR ) ); uOpenName.Buffer = NameStr; // // Set up the object attributes. // InitializeObjectAttributes( &ObjectAttributes, &uOpenName, OBJ_CASE_INSENSITIVE, NULL, NULL ); // // Allocate the EA buffer - be a little generous. // UserNameLen = strlen( EA_NAME_USERNAME ); PasswordLen = strlen( EA_NAME_PASSWORD ); TypeLen = strlen( EA_NAME_TYPE ); CredLen = strlen( EA_NAME_CREDENTIAL_EX ); EaLength = 4 * sizeof( FILE_FULL_EA_INFORMATION ); EaLength += 4 * sizeof( ULONG ); EaLength += ROUNDUP4( UserNameLen ); EaLength += ROUNDUP4( PasswordLen ); EaLength += ROUNDUP4( TypeLen ); EaLength += ROUNDUP4( CredLen ); EaLength += ROUNDUP4( puUserName->Length ); EaLength += ROUNDUP4( puPassword->Length ); EaBuffer = LocalAlloc( LMEM_ZEROINIT, EaLength ); if ( !EaBuffer ) { return STATUS_INSUFFICIENT_RESOURCES; } // // Pack in the first EA: UserName. // pEaEntry = (PFILE_FULL_EA_INFORMATION) EaBuffer; EaNameLength = UserNameLen + sizeof( CHAR ); pEaEntry->EaNameLength = (UCHAR) EaNameLength; pEaEntry->EaValueLength = puUserName->Length; RtlCopyMemory( &(pEaEntry->EaName[0]), EA_NAME_USERNAME, EaNameLength ); EaNameLength = ROUNDUP2( EaNameLength + sizeof( CHAR ) ); RtlCopyMemory( &(pEaEntry->EaName[EaNameLength]), puUserName->Buffer, puUserName->Length ); EaLength = ( 2 * sizeof( DWORD ) ) + EaNameLength + puUserName->Length; EaLength = ROUNDUP4( EaLength ); EaTotalLength = EaLength; pEaEntry->NextEntryOffset = EaLength; // // Pack in the second EA: Password. // pEaEntry = (PFILE_FULL_EA_INFORMATION) ( ( (PBYTE)pEaEntry ) + pEaEntry->NextEntryOffset ); EaNameLength = PasswordLen + sizeof( CHAR ); pEaEntry->EaNameLength = (UCHAR) EaNameLength; pEaEntry->EaValueLength = puPassword->Length; RtlCopyMemory( &(pEaEntry->EaName[0]), EA_NAME_PASSWORD, EaNameLength ); EaNameLength = ROUNDUP2( EaNameLength + sizeof( CHAR ) ); RtlCopyMemory( &(pEaEntry->EaName[EaNameLength]), puPassword->Buffer, puPassword->Length ); EaLength = ( 2 * sizeof( DWORD ) ) + EaNameLength + puPassword->Length; EaLength = ROUNDUP4( EaLength ); EaTotalLength += EaLength; pEaEntry->NextEntryOffset = EaLength; // // Pack in the third EA: Type. // pEaEntry = (PFILE_FULL_EA_INFORMATION) ( ( (PBYTE)pEaEntry ) + pEaEntry->NextEntryOffset ); EaNameLength = TypeLen + sizeof( CHAR ); pEaEntry->EaNameLength = (UCHAR) EaNameLength; pEaEntry->EaValueLength = sizeof( ULONG ); RtlCopyMemory( &(pEaEntry->EaName[0]), EA_NAME_TYPE, EaNameLength ); EaNameLength = ROUNDUP2( EaNameLength + sizeof( CHAR ) ); EaLength = ( 2 * sizeof( DWORD ) ) + EaNameLength + sizeof( ULONG ); EaLength = ROUNDUP4( EaLength ); EaTotalLength += EaLength; pEaEntry->NextEntryOffset = EaLength; // // Pack in the fourth EA: the CredentialEx flag. // pEaEntry = (PFILE_FULL_EA_INFORMATION) ( ( (PBYTE)pEaEntry ) + pEaEntry->NextEntryOffset ); EaNameLength = CredLen + sizeof( CHAR ); pEaEntry->EaNameLength = (UCHAR) EaNameLength; pEaEntry->EaValueLength = sizeof( ULONG ); RtlCopyMemory( &(pEaEntry->EaName[0]), EA_NAME_CREDENTIAL_EX, EaNameLength ); EaNameLength = ROUNDUP2( EaNameLength + sizeof( CHAR ) ); EaLength = ( 2 * sizeof( DWORD ) ) + EaNameLength + sizeof( ULONG ); EaLength = ROUNDUP4( EaLength ); EaTotalLength += EaLength; pEaEntry->NextEntryOffset = 0; // // Do the open. // ntstatus = NtCreateFile( phNwHandle, // File handle (OUT) DesiredAccess, // Access mask &ObjectAttributes, // Object attributes &IoStatusBlock, // Io status NULL, // Optional Allocation size FILE_ATTRIBUTE_NORMAL, // File attributes FILE_SHARE_VALID_FLAGS, // File share access FILE_OPEN, // Create disposition 0, // Create options (PVOID) EaBuffer, // Our EA buffer EaTotalLength ); // Ea buffer length LocalFree( EaBuffer ); if ( !NT_SUCCESS(ntstatus) ) return ntstatus; OpenStatus = IoStatusBlock.Status; // // Check the handle type. // Rrp = (PNWR_NDS_REQUEST_PACKET)RrpData; Rrp->Version = 0; RtlCopyMemory( &(Rrp->Parameters).VerifyTree, puResourceName, sizeof( UNICODE_STRING ) ); RtlCopyMemory( (BYTE *)(&(Rrp->Parameters).VerifyTree) + sizeof( UNICODE_STRING ), puResourceName->Buffer, puResourceName->Length ); try { ntstatus = NtFsControlFile( *phNwHandle, NULL, NULL, NULL, &IoStatusBlock, FSCTL_NWR_NDS_VERIFY_TREE, (PVOID) Rrp, sizeof( NWR_NDS_REQUEST_PACKET ), NULL, 0 ); } except ( EXCEPTION_EXECUTE_HANDLER ) { ntstatus = GetExceptionCode(); goto CloseAndExit2; } if ( !NT_SUCCESS( ntstatus )) { *lpdwHandleType = HANDLE_TYPE_NCP_SERVER; } else { *lpdwHandleType = HANDLE_TYPE_NDS_TREE; } return OpenStatus; CloseAndExit2: NtClose( *phNwHandle ); *phNwHandle = NULL; return ntstatus; } NTSTATUS NwNdsSetTreeContext ( IN HANDLE hNdsRdr, IN PUNICODE_STRING puTree, IN PUNICODE_STRING puContext ) /*+++ This sets the current context in the requested tree. ---*/ { NTSTATUS ntstatus; IO_STATUS_BLOCK IoStatusBlock; PNWR_NDS_REQUEST_PACKET Rrp; DWORD RrpSize; BYTE *CurrentString; // // Set up the request. // RrpSize = sizeof( NWR_NDS_REQUEST_PACKET ) + puTree->Length + puContext->Length; Rrp = LocalAlloc( LMEM_ZEROINIT, RrpSize ); if ( !Rrp ) { return STATUS_INSUFFICIENT_RESOURCES; } try { (Rrp->Parameters).SetContext.TreeNameLen = puTree->Length; (Rrp->Parameters).SetContext.ContextLen = puContext->Length; CurrentString = (BYTE *)(Rrp->Parameters).SetContext.TreeAndContextString; RtlCopyMemory( CurrentString, puTree->Buffer, puTree->Length ); CurrentString += puTree->Length; RtlCopyMemory( CurrentString, puContext->Buffer, puContext->Length ); } except ( EXCEPTION_EXECUTE_HANDLER ) { ntstatus = STATUS_INVALID_PARAMETER; goto ExitWithCleanup; } try { ntstatus = NtFsControlFile( hNdsRdr, NULL, NULL, NULL, &IoStatusBlock, FSCTL_NWR_NDS_SETCONTEXT, (PVOID) Rrp, RrpSize, NULL, 0 ); } except ( EXCEPTION_EXECUTE_HANDLER ) { ntstatus = GetExceptionCode(); goto ExitWithCleanup; } ExitWithCleanup: LocalFree( Rrp ); return ntstatus; } NTSTATUS NwNdsGetTreeContext ( IN HANDLE hNdsRdr, IN PUNICODE_STRING puTree, OUT PUNICODE_STRING puContext ) /*+++ This gets the current context of the requested tree. ---*/ { NTSTATUS ntstatus; IO_STATUS_BLOCK IoStatusBlock; PNWR_NDS_REQUEST_PACKET Rrp; DWORD RrpSize; // // Set up the request. // RrpSize = sizeof( NWR_NDS_REQUEST_PACKET ) + puTree->Length; Rrp = LocalAlloc( LMEM_ZEROINIT, RrpSize ); if ( !Rrp ) { return STATUS_INSUFFICIENT_RESOURCES; } try { (Rrp->Parameters).GetContext.TreeNameLen = puTree->Length; RtlCopyMemory( (BYTE *)(Rrp->Parameters).GetContext.TreeNameString, puTree->Buffer, puTree->Length ); (Rrp->Parameters).GetContext.Context.MaximumLength = puContext->MaximumLength; (Rrp->Parameters).GetContext.Context.Length = 0; (Rrp->Parameters).GetContext.Context.Buffer = puContext->Buffer; } except ( EXCEPTION_EXECUTE_HANDLER ) { ntstatus = STATUS_INVALID_PARAMETER; goto ExitWithCleanup; } try { ntstatus = NtFsControlFile( hNdsRdr, NULL, NULL, NULL, &IoStatusBlock, FSCTL_NWR_NDS_GETCONTEXT, (PVOID) Rrp, RrpSize, NULL, 0 ); } except ( EXCEPTION_EXECUTE_HANDLER ) { ntstatus = GetExceptionCode(); goto ExitWithCleanup; } // // Copy out the length; the buffer has already been written. // puContext->Length = (Rrp->Parameters).GetContext.Context.Length; ExitWithCleanup: LocalFree( Rrp ); return ntstatus; } NTSTATUS NwNdsResolveName ( IN HANDLE hNdsTree, IN PUNICODE_STRING puObjectName, OUT DWORD *dwObjectId, OUT PUNICODE_STRING puReferredServer, OUT PBYTE pbRawResponse, IN DWORD dwResponseBufferLen ) { // // BUGBUG: Do I want or need to expose the flags parameter? // NTSTATUS ntstatus; IO_STATUS_BLOCK IoStatusBlock; PNWR_NDS_REQUEST_PACKET Rrp; PNDS_RESPONSE_RESOLVE_NAME Rsp; DWORD dwRspBufferLen, dwNameLen, dwPadding; BYTE RrpData[1024]; BYTE RspData[256]; Rrp = (PNWR_NDS_REQUEST_PACKET) RrpData; RtlZeroMemory( Rrp, 1024 ); // // NW NDS strings are null terminated, so we make sure we // report the correct length... // dwNameLen = puObjectName->Length + sizeof( WCHAR ); Rrp->Version = 0; Rrp->Parameters.ResolveName.ObjectNameLength = ROUNDUP4( dwNameLen ); Rrp->Parameters.ResolveName.ResolverFlags = RSLV_DEREF_ALIASES | RSLV_WALK_TREE | RSLV_WRITABLE; try { // // But don't try to copy more than the user gave us. // memcpy( Rrp->Parameters.ResolveName.ObjectName, puObjectName->Buffer, puObjectName->Length ); } except ( EXCEPTION_EXECUTE_HANDLER ) { return STATUS_INVALID_PARAMETER; } // // Send the request to the Redirector FSD. // if ( dwResponseBufferLen != 0 && pbRawResponse != NULL ) { Rsp = ( PNDS_RESPONSE_RESOLVE_NAME ) pbRawResponse; dwRspBufferLen = dwResponseBufferLen; } else { Rsp = ( PNDS_RESPONSE_RESOLVE_NAME ) RspData; dwRspBufferLen = 256; } try { ntstatus = NtFsControlFile( hNdsTree, NULL, NULL, NULL, &IoStatusBlock, FSCTL_NWR_NDS_RESOLVE_NAME, (PVOID) Rrp, sizeof( NWR_NDS_REQUEST_PACKET ), (PVOID) Rsp, dwRspBufferLen ); } except ( EXCEPTION_EXECUTE_HANDLER ) { return GetExceptionCode(); } // // Dig out the object id and referred server. // if ( NT_SUCCESS( ntstatus ) ) { try { *dwObjectId = Rsp->EntryId; if ( Rsp->ServerNameLength > puReferredServer->MaximumLength ) { ntstatus = STATUS_BUFFER_TOO_SMALL; } else { RtlCopyMemory( puReferredServer->Buffer, Rsp->ReferredServer, Rsp->ServerNameLength ); puReferredServer->Length = (USHORT)Rsp->ServerNameLength; } } except ( EXCEPTION_EXECUTE_HANDLER ) { return ntstatus; } } return ntstatus; } NTSTATUS NwNdsList ( IN HANDLE hNdsTree, IN DWORD dwObjectId, OUT DWORD *dwIterHandle, OUT BYTE *pbReplyBuf, IN DWORD dwReplyBufLen ) { NTSTATUS Status; IO_STATUS_BLOCK IoStatusBlock; PNWR_NDS_REQUEST_PACKET Rrp; PNDS_RESPONSE_SUBORDINATE_LIST Rsp; DWORD dwRspBufferLen; BYTE RrpData[256]; BYTE RspData[1024]; // // BUGBUG: I should make the reply buffer small and start testing // fragment assembly stuff. // Rrp = (PNWR_NDS_REQUEST_PACKET) RrpData; Rrp->Parameters.ListSubordinates.ObjectId = dwObjectId; Rrp->Parameters.ListSubordinates.IterHandle = *dwIterHandle; if ( dwReplyBufLen != 0 && pbReplyBuf != NULL ) { Rsp = ( PNDS_RESPONSE_SUBORDINATE_LIST ) pbReplyBuf; dwRspBufferLen = dwReplyBufLen; } else { Rsp = ( PNDS_RESPONSE_SUBORDINATE_LIST ) RspData; dwRspBufferLen = 1024; } try { Status = NtFsControlFile( hNdsTree, NULL, NULL, NULL, &IoStatusBlock, FSCTL_NWR_NDS_LIST_SUBS, (PVOID) Rrp, sizeof( NWR_NDS_REQUEST_PACKET ), (PVOID) Rsp, dwRspBufferLen ); } except ( EXCEPTION_EXECUTE_HANDLER ) { return GetExceptionCode(); } return Status; } NTSTATUS NwNdsReadObjectInfo( IN HANDLE hNdsTree, IN DWORD dwObjectId, OUT BYTE *pbRawReply, IN DWORD dwReplyBufLen ) { NTSTATUS Status; IO_STATUS_BLOCK IoStatusBlock; PNWR_NDS_REQUEST_PACKET Rrp; PNDS_RESPONSE_GET_OBJECT_INFO Rsp; DWORD dwRspBufferLen; BYTE RrpData[256]; BYTE RspData[1024]; Rrp = (PNWR_NDS_REQUEST_PACKET) RrpData; Rrp->Parameters.GetObjectInfo.ObjectId = dwObjectId; if ( dwReplyBufLen != 0 && pbRawReply != NULL ) { Rsp = ( PNDS_RESPONSE_GET_OBJECT_INFO ) pbRawReply; dwRspBufferLen = dwReplyBufLen; } else { Rsp = ( PNDS_RESPONSE_GET_OBJECT_INFO ) RspData; dwRspBufferLen = 1024; } try { Status = NtFsControlFile( hNdsTree, NULL, NULL, NULL, &IoStatusBlock, FSCTL_NWR_NDS_READ_INFO, (PVOID) Rrp, sizeof( NWR_NDS_REQUEST_PACKET ), (PVOID) Rsp, dwRspBufferLen ); } except ( EXCEPTION_EXECUTE_HANDLER ) { return GetExceptionCode(); } return Status; } NTSTATUS NwNdsReadAttribute ( IN HANDLE hNdsTree, IN DWORD dwObjectId, IN DWORD *dwIterHandle, IN PUNICODE_STRING puAttrName, OUT BYTE *pbReplyBuf, IN DWORD dwReplyBufLen ) { NTSTATUS ntstatus; IO_STATUS_BLOCK IoStatusBlock; PNWR_NDS_REQUEST_PACKET Rrp; PNDS_RESPONSE_READ_ATTRIBUTE Rsp; DWORD dwAttributeNameLen; BYTE RrpData[1024]; // // Check the incoming buffer. // if ( !dwReplyBufLen || !pbReplyBuf ) { return STATUS_INVALID_PARAMETER; } // // Set up the request. // Rrp = (PNWR_NDS_REQUEST_PACKET) RrpData; RtlZeroMemory( Rrp, 1024 ); (Rrp->Parameters).ReadAttribute.ObjectId = dwObjectId; (Rrp->Parameters).ReadAttribute.IterHandle = *dwIterHandle; // // Nds strings are NULL terminated; watch the size. // dwAttributeNameLen = puAttrName->Length + sizeof( WCHAR ); (Rrp->Parameters).ReadAttribute.AttributeNameLength = dwAttributeNameLen; try { // // But don't try to copy more than the user gave us. // memcpy( (Rrp->Parameters).ReadAttribute.AttributeName, puAttrName->Buffer, puAttrName->Length ); } except ( EXCEPTION_EXECUTE_HANDLER ) { return STATUS_INVALID_PARAMETER; } // // Send the request to the Redirector FSD. // try { ntstatus = NtFsControlFile( hNdsTree, NULL, NULL, NULL, &IoStatusBlock, FSCTL_NWR_NDS_READ_ATTR, (PVOID) Rrp, sizeof( NWR_NDS_REQUEST_PACKET ), (PVOID) pbReplyBuf, dwReplyBufLen ); } except ( EXCEPTION_EXECUTE_HANDLER ) { return GetExceptionCode(); } // // There's no buffer post processing on this one. // return ntstatus; } NTSTATUS NwNdsOpenStream ( IN HANDLE hNdsTree, IN DWORD dwObjectId, IN PUNICODE_STRING puStreamName, IN DWORD dwOpenFlags, OUT DWORD *pdwFileLength ) { NTSTATUS ntstatus; IO_STATUS_BLOCK IoStatusBlock; PNWR_NDS_REQUEST_PACKET Rrp; BYTE RrpData[1024]; // // Set up the request. // Rrp = (PNWR_NDS_REQUEST_PACKET) RrpData; RtlZeroMemory( Rrp, 1024 ); (Rrp->Parameters).OpenStream.StreamAccess = dwOpenFlags; (Rrp->Parameters).OpenStream.ObjectOid = dwObjectId; (Rrp->Parameters).OpenStream.StreamName.Length = puStreamName->Length; (Rrp->Parameters).OpenStream.StreamName.MaximumLength = sizeof( RrpData ) - sizeof( (Rrp->Parameters).OpenStream ); (Rrp->Parameters).OpenStream.StreamName.Buffer = (Rrp->Parameters).OpenStream.StreamNameString; // // Make sure we're not trashing memory. // if ( (Rrp->Parameters).OpenStream.StreamName.Length > (Rrp->Parameters).OpenStream.StreamName.MaximumLength ) { return STATUS_INVALID_PARAMETER; } try { // // But don't try to copy more than the user gave us. // memcpy( (Rrp->Parameters).OpenStream.StreamNameString, puStreamName->Buffer, puStreamName->Length ); } except ( EXCEPTION_EXECUTE_HANDLER ) { return STATUS_INVALID_PARAMETER; } // // Send the request to the Redirector FSD. // try { ntstatus = NtFsControlFile( hNdsTree, NULL, NULL, NULL, &IoStatusBlock, FSCTL_NWR_NDS_OPEN_STREAM, (PVOID) Rrp, sizeof( NWR_NDS_REQUEST_PACKET ), NULL, 0 ); } except ( EXCEPTION_EXECUTE_HANDLER ) { return GetExceptionCode(); } if ( pdwFileLength ) { *pdwFileLength = (Rrp->Parameters).OpenStream.FileLength; } return ntstatus; } NTSTATUS NwNdsGetQueueInformation( IN HANDLE hNdsTree, IN PUNICODE_STRING puQueueName, OUT PUNICODE_STRING puHostServer, OUT PDWORD pdwQueueId ) { NTSTATUS ntstatus; IO_STATUS_BLOCK IoStatusBlock; PNWR_NDS_REQUEST_PACKET Rrp; BYTE RrpData[1024]; // // Set up the request. // Rrp = (PNWR_NDS_REQUEST_PACKET) RrpData; RtlZeroMemory( Rrp, sizeof( RrpData ) ); if ( puQueueName ) { (Rrp->Parameters).GetQueueInfo.QueueName.Length = puQueueName->Length; (Rrp->Parameters).GetQueueInfo.QueueName.MaximumLength = puQueueName->MaximumLength; (Rrp->Parameters).GetQueueInfo.QueueName.Buffer = puQueueName->Buffer; } if ( puHostServer ) { (Rrp->Parameters).GetQueueInfo.HostServer.Length = 0; (Rrp->Parameters).GetQueueInfo.HostServer.MaximumLength = puHostServer->MaximumLength; (Rrp->Parameters).GetQueueInfo.HostServer.Buffer = puHostServer->Buffer; } // // Send the request to the Redirector FSD. // try { ntstatus = NtFsControlFile( hNdsTree, NULL, NULL, NULL, &IoStatusBlock, FSCTL_NWR_NDS_GET_QUEUE_INFO, (PVOID) Rrp, sizeof( NWR_NDS_REQUEST_PACKET ), NULL, 0 ); } except ( EXCEPTION_EXECUTE_HANDLER ) { return GetExceptionCode(); } if ( NT_SUCCESS( ntstatus ) ) { if ( pdwQueueId ) { *pdwQueueId = (Rrp->Parameters).GetQueueInfo.QueueId; } puHostServer->Length = (Rrp->Parameters).GetQueueInfo.HostServer.Length; } return ntstatus; } NTSTATUS NwNdsGetVolumeInformation( IN HANDLE hNdsTree, IN PUNICODE_STRING puVolumeName, OUT PUNICODE_STRING puHostServer, OUT PUNICODE_STRING puHostVolume ) { NTSTATUS ntstatus; IO_STATUS_BLOCK IoStatusBlock; PNWR_NDS_REQUEST_PACKET Rrp; DWORD RequestSize; BYTE RrpData[1024]; BYTE ReplyData[1024]; PBYTE NameStr; // // Set up the request. // Rrp = (PNWR_NDS_REQUEST_PACKET) RrpData; RtlZeroMemory( Rrp, sizeof( RrpData ) ); if ( !puVolumeName || puVolumeName->Length > MAX_NDS_NAME_SIZE || !puHostServer || !puHostVolume ) { return STATUS_INVALID_PARAMETER; } try { (Rrp->Parameters).GetVolumeInfo.VolumeNameLen = puVolumeName->Length; RtlCopyMemory( &((Rrp->Parameters).GetVolumeInfo.VolumeName[0]), puVolumeName->Buffer, puVolumeName->Length ); } except ( EXCEPTION_EXECUTE_HANDLER ) { return STATUS_INVALID_PARAMETER; } // // Send the request to the Redirector FSD. // RequestSize = sizeof( NWR_NDS_REQUEST_PACKET ) + (Rrp->Parameters).GetVolumeInfo.VolumeNameLen; try { ntstatus = NtFsControlFile( hNdsTree, NULL, NULL, NULL, &IoStatusBlock, FSCTL_NWR_NDS_GET_VOLUME_INFO, (PVOID) Rrp, RequestSize, ReplyData, sizeof( ReplyData ) ); } except ( EXCEPTION_EXECUTE_HANDLER ) { return GetExceptionCode(); } if ( NT_SUCCESS( ntstatus ) ) { try { if ( ( puHostServer->MaximumLength < (Rrp->Parameters).GetVolumeInfo.ServerNameLen ) || ( puHostVolume->MaximumLength < (Rrp->Parameters).GetVolumeInfo.TargetVolNameLen ) ) { return STATUS_BUFFER_TOO_SMALL; } puHostServer->Length = (USHORT)(Rrp->Parameters).GetVolumeInfo.ServerNameLen; puHostVolume->Length = (USHORT)(Rrp->Parameters).GetVolumeInfo.TargetVolNameLen; NameStr = &ReplyData[0]; RtlCopyMemory( puHostServer->Buffer, NameStr, puHostServer->Length ); NameStr += puHostServer->Length; RtlCopyMemory( puHostVolume->Buffer, NameStr, puHostVolume->Length ); } except ( EXCEPTION_EXECUTE_HANDLER ) { return STATUS_INVALID_PARAMETER; } } return ntstatus; } // // User mode fragment exchange. // int _cdecl FormatBuf( char *buf, int bufLen, const char *format, va_list args ); NTSTATUS _cdecl FragExWithWait( IN HANDLE hNdsServer, IN DWORD NdsVerb, IN BYTE *pReplyBuffer, IN DWORD pReplyBufferLen, IN OUT DWORD *pdwReplyLen, IN BYTE *NdsRequestStr, ... ) /* Routine Description: Exchanges an NDS request in fragments and collects the fragments of the response and writes them to the reply buffer. Routine Arguments: hNdsServer - A handle to the server you want to talk to. NdsVerb - The verb for that indicates the request. pReplyBuffer - The reply buffer. pReplyBufferLen - The length of the reply buffer. NdsReqestStr - The format string for the arguments to this NDS request. Arguments - The arguments that satisfy the NDS format string. Return Value: NTSTATUS - Status of the exchange, but not the result code in the packet. */ { NTSTATUS Status; IO_STATUS_BLOCK IoStatusBlock; PNWR_NDS_REQUEST_PACKET RawRequest; BYTE *NdsRequestBuf; DWORD NdsRequestLen; va_list Arguments; // // Allocate a request buffer. // RawRequest = LocalAlloc( LMEM_ZEROINIT, NDS_BUFFER_SIZE ); if ( !RawRequest ) { return STATUS_INSUFFICIENT_RESOURCES; } // // Build the request in our local buffer. The first DWORD // is the verb and the rest is the formatted request. // RawRequest->Parameters.RawRequest.NdsVerb = NdsVerb; NdsRequestBuf = &RawRequest->Parameters.RawRequest.Request[0]; if ( NdsRequestStr != NULL ) { va_start( Arguments, NdsRequestStr ); NdsRequestLen = FormatBuf( NdsRequestBuf, NDS_BUFFER_SIZE - sizeof( NWR_NDS_REQUEST_PACKET ), NdsRequestStr, Arguments ); if ( !NdsRequestLen ) { Status = STATUS_INVALID_PARAMETER; goto ExitWithCleanup; } va_end( Arguments ); } else { NdsRequestLen = 0; } RawRequest->Parameters.RawRequest.RequestLength = NdsRequestLen; // // Pass this buffer to kernel mode via FSCTL. // try { Status = NtFsControlFile( hNdsServer, NULL, NULL, NULL, &IoStatusBlock, FSCTL_NWR_NDS_RAW_FRAGEX, (PVOID) RawRequest, NdsRequestLen + sizeof( NWR_NDS_REQUEST_PACKET ), (PVOID) pReplyBuffer, pReplyBufferLen ); if ( NT_SUCCESS( Status ) ) { *pdwReplyLen = RawRequest->Parameters.RawRequest.ReplyLength; } } except ( EXCEPTION_EXECUTE_HANDLER ) { Status = GetExceptionCode(); } ExitWithCleanup: if ( RawRequest ) { LocalFree( RawRequest ); } return Status; } int _cdecl FormatBuf( char *buf, int bufLen, const char *format, va_list args ) /* Routine Description: Formats a buffer according to supplied the format string. FormatString - Supplies an ANSI string which describes how to convert from the input arguments into NCP request fields, and from the NCP response fields into the output arguments. Field types, request/response: 'b' byte ( byte / byte* ) 'w' hi-lo word ( word / word* ) 'd' hi-lo dword ( dword / dword* ) 'W' lo-hi word ( word / word*) 'D' lo-hi dword ( dword / dword*) '-' zero/skip byte ( void ) '=' zero/skip word ( void ) ._. zero/skip string ( word ) 'p' pstring ( char* ) 'c' cstring ( char* ) 'C' cstring followed skip word ( char*, word ) 'V' sized NDS value ( byte *, dword / byte **, dword *) 'S' p unicode string copy as NDS_STRING (UNICODE_STRING *) 's' cstring copy as NDS_STRING (char* / char *, word) 'r' raw bytes ( byte*, word ) 'u' p unicode string ( UNICODE_STRING * ) 'U' p uppercase string( UNICODE_STRING * ) Routine Arguments: char *buf - destination buffer. int buflen - length of the destination buffer. char *format - format string. args - args to the format string. Implementation Notes: This comes verbatim from kernel mode. */ { ULONG ix; NTSTATUS status; const char *z = format; // // Convert the input arguments into request packet. // ix = 0; while ( *z ) { switch ( *z ) { case '=': buf[ix++] = 0; case '-': buf[ix++] = 0; break; case '_': { WORD l = va_arg ( args, WORD ); if (ix + (ULONG)l > (ULONG)bufLen) { goto ErrorExit; } while ( l-- ) buf[ix++] = 0; break; } case 'b': buf[ix++] = va_arg ( args, BYTE ); break; case 'w': { WORD w = va_arg ( args, WORD ); buf[ix++] = (BYTE) (w >> 8); buf[ix++] = (BYTE) (w >> 0); break; } case 'd': { DWORD d = va_arg ( args, DWORD ); buf[ix++] = (BYTE) (d >> 24); buf[ix++] = (BYTE) (d >> 16); buf[ix++] = (BYTE) (d >> 8); buf[ix++] = (BYTE) (d >> 0); break; } case 'W': { WORD w = va_arg(args, WORD); (* (WORD *)&buf[ix]) = w; ix += 2; break; } case 'D': { DWORD d = va_arg (args, DWORD); (* (DWORD *)&buf[ix]) = d; ix += 4; break; } case 'c': { char* c = va_arg ( args, char* ); WORD l = strlen( c ); if (ix + (ULONG)l > (ULONG)bufLen) { goto ErrorExit; } RtlCopyMemory( &buf[ix], c, l+1 ); ix += l + 1; break; } case 'C': { char* c = va_arg ( args, char* ); WORD l = va_arg ( args, WORD ); WORD len = strlen( c ) + 1; if (ix + (ULONG)l > (ULONG)bufLen) { goto ErrorExit; } RtlCopyMemory( &buf[ix], c, len > l? l : len); ix += l; buf[ix-1] = 0; break; } case 'p': { char* c = va_arg ( args, char* ); BYTE l = strlen( c ); if (ix + (ULONG)l +1 > (ULONG)bufLen) { goto ErrorExit; } buf[ix++] = l; RtlCopyMemory( &buf[ix], c, l ); ix += l; break; } case 'u': { PUNICODE_STRING pUString = va_arg ( args, PUNICODE_STRING ); OEM_STRING OemString; ULONG Length; // // Calculate required string length, excluding trailing NUL. // Length = RtlUnicodeStringToOemSize( pUString ) - 1; ASSERT( Length < 0x100 ); if ( ix + Length > (ULONG)bufLen ) { goto ErrorExit; } buf[ix++] = (UCHAR)Length; OemString.Buffer = &buf[ix]; OemString.MaximumLength = (USHORT)Length + 1; status = RtlUnicodeStringToOemString( &OemString, pUString, FALSE ); ASSERT( NT_SUCCESS( status )); ix += (USHORT)Length; break; } case 'S': { PUNICODE_STRING pUString = va_arg (args, PUNICODE_STRING); ULONG Length, rLength; Length = pUString->Length; if (ix + Length + sizeof(Length) + sizeof( WCHAR ) > (ULONG)bufLen) { goto ErrorExit; } // // The VLM client uses the rounded up length and it seems to // make a difference! Also, don't forget that NDS strings have // to be NULL terminated. // rLength = ROUNDUP4(Length + sizeof( WCHAR )); *((DWORD *)&buf[ix]) = rLength; ix += 4; RtlCopyMemory(&buf[ix], pUString->Buffer, Length); ix += Length; rLength -= Length; RtlFillMemory( &buf[ix], rLength, '\0' ); ix += rLength; break; } case 's': { PUNICODE_STRING pUString = va_arg (args, PUNICODE_STRING); ULONG Length, rLength; Length = pUString->Length; if (ix + Length + sizeof(Length) + sizeof( WCHAR ) > (ULONG)bufLen) { // DebugTrace( 0, Dbg, "FormatBuf: case 's' request buffer too small.\n", 0 ); goto ErrorExit; } // // Don't use the padded size here, only the NDS null terminator. // rLength = Length + sizeof( WCHAR ); *((DWORD *)&buf[ix]) = rLength; ix += 4; RtlCopyMemory(&buf[ix], pUString->Buffer, Length); ix += Length; rLength -= Length; RtlFillMemory( &buf[ix], rLength, '\0' ); ix += rLength; break; } case 'V': { // too similar to 'S' - should be combined BYTE* b = va_arg ( args, BYTE* ); DWORD l = va_arg ( args, DWORD ); if ( ix + l + sizeof(DWORD) > (ULONG) bufLen ) { goto ErrorExit; } *((DWORD *)&buf[ix]) = l; ix += sizeof(DWORD); RtlCopyMemory( &buf[ix], b, l ); l = ROUNDUP4(l); ix += l; break; } case 'r': { BYTE* b = va_arg ( args, BYTE* ); WORD l = va_arg ( args, WORD ); if ( b == NULL || l == 0 ) { break; } if ( ix + l > (ULONG)bufLen ) { goto ErrorExit; } RtlCopyMemory( &buf[ix], b, l ); ix += l; break; } default: ; } if ( ix > (ULONG)bufLen ) { goto ErrorExit; } z++; } return(ix); ErrorExit: return 0; } NTSTATUS _cdecl ParseResponse( PUCHAR Response, ULONG ResponseLength, char* FormatString, ... // format specific parameters ) /*++ Routine Description: This routine parse an NCP response. Packet types: 'G' Generic packet ( ) Field types, request/response: 'b' byte ( byte* ) 'w' hi-lo word ( word* ) 'x' ordered word ( word* ) 'd' hi-lo dword ( dword* ) 'e' ordered dword ( dword* ) '-' zero/skip byte ( void ) '=' zero/skip word ( void ) ._. zero/skip string ( word ) 'p' pstring ( char* ) 'c' cstring ( char* ) 'r' raw bytes ( byte*, word ) Added 3/29/95 by CoryWest: 'W' lo-hi word ( word / word*) 'D' lo-hi dword ( dword / dword*) 'S' unicode string copy as NDS_STRING (UNICODE_STRING *) 'T' terminal unicode string copy as NDS_STRING (UNICODE_STRING *) 't' terminal unicode string with the nds null copied as NDS_STRING (UNICODE_STRING *) (for GetUseName) Return Value: STATUS - Success or failure, depending on the response. --*/ { NTSTATUS Status = STATUS_SUCCESS; PCHAR FormatByte; va_list Arguments; ULONG Length = 0; va_start( Arguments, FormatString ); // // User mode parse response handles only generic packets. // if ( *FormatString != 'G' ) { return STATUS_INVALID_PARAMETER; } FormatByte = FormatString + 1; while ( *FormatByte ) { switch ( *FormatByte ) { case '-': Length += 1; break; case '=': Length += 2; break; case '_': { WORD l = va_arg ( Arguments, WORD ); Length += l; break; } case 'b': { BYTE* b = va_arg ( Arguments, BYTE* ); *b = Response[Length++]; break; } case 'w': { BYTE* b = va_arg ( Arguments, BYTE* ); b[1] = Response[Length++]; b[0] = Response[Length++]; break; } case 'x': { WORD* w = va_arg ( Arguments, WORD* ); *w = *(WORD UNALIGNED *)&Response[Length]; Length += 2; break; } case 'd': { BYTE* b = va_arg ( Arguments, BYTE* ); b[3] = Response[Length++]; b[2] = Response[Length++]; b[1] = Response[Length++]; b[0] = Response[Length++]; break; } case 'e': { DWORD UNALIGNED * d = va_arg ( Arguments, DWORD* ); *d = *(DWORD UNALIGNED *)&Response[Length]; Length += 4; break; } case 'c': { char* c = va_arg ( Arguments, char* ); WORD l = strlen( &Response[Length] ); memcpy ( c, &Response[Length], l+1 ); Length += l+1; break; } case 'p': { char* c = va_arg ( Arguments, char* ); BYTE l = Response[Length++]; memcpy ( c, &Response[Length], l ); c[l+1] = 0; break; } case 'r': { BYTE* b = va_arg ( Arguments, BYTE* ); WORD l = va_arg ( Arguments, WORD ); RtlCopyMemory( b, &Response[Length], l ); Length += l; break; } case 'W': { WORD *w = va_arg ( Arguments, WORD* ); *w = (* (WORD *)&Response[Length]); Length += 2; break; } case 'D': { DWORD *d = va_arg ( Arguments, DWORD* ); *d = (* (DWORD *)&Response[Length]); Length += 4; break; } case 'S': { PUNICODE_STRING pU = va_arg( Arguments, PUNICODE_STRING ); USHORT strl; if (pU) { strl = (USHORT)(* (DWORD *)&Response[Length]); // // Don't count the null terminator that is part of // Novell's counted unicode string. // pU->Length = strl - sizeof( WCHAR ); Length += 4; RtlCopyMemory( pU->Buffer, &Response[Length], pU->Length ); Length += ROUNDUP4(strl); } else { // // Skip over the string since we don't want it. // Length += ROUNDUP4((* (DWORD *)&Response[Length] )); Length += 4; } break; } case 's': { PUNICODE_STRING pU = va_arg( Arguments, PUNICODE_STRING ); USHORT strl; if (pU) { strl = (USHORT)(* (DWORD *)&Response[Length]); pU->Length = strl; Length += 4; RtlCopyMemory( pU->Buffer, &Response[Length], pU->Length ); Length += ROUNDUP4(strl); } else { // // Skip over the string since we don't want it. // Length += ROUNDUP4((* (DWORD *)&Response[Length] )); Length += 4; } break; } case 'T': { PUNICODE_STRING pU = va_arg( Arguments, PUNICODE_STRING ); USHORT strl; if (pU) { strl = (USHORT)(* (DWORD *)&Response[Length] ); strl -= sizeof( WCHAR ); // Don't count the NULL from NDS. if ( strl <= pU->MaximumLength ) { pU->Length = strl; Length += 4; RtlCopyMemory( pU->Buffer, &Response[Length], pU->Length ); // // No need to advance the pointers since this is // specifically a termination case! // } else { pU->Length = 0; } } break; } case 't': { PUNICODE_STRING pU = va_arg( Arguments, PUNICODE_STRING ); USHORT strl; if (pU) { strl = (USHORT)(* (DWORD *)&Response[Length] ); if ( strl <= pU->MaximumLength ) { pU->Length = strl; Length += 4; RtlCopyMemory( pU->Buffer, &Response[Length], pU->Length ); // // No need to advance the pointers since this is // specifically a termination case! // } else { pU->Length = 0; } } break; } } if ( Length > ResponseLength ) { return( STATUS_INVALID_PARAMETER ); } FormatByte++; } va_end( Arguments ); return( Status ); } NTSTATUS NwNdsChangePassword( IN HANDLE hNwRdr, IN PUNICODE_STRING puTreeName, IN PUNICODE_STRING puUserName, IN PUNICODE_STRING puCurrentPassword, IN PUNICODE_STRING puNewPassword ) { NTSTATUS Status; PNWR_NDS_REQUEST_PACKET pNdsRequest; DWORD dwRequestLength; PBYTE CurrentString; IO_STATUS_BLOCK IoStatusBlock; // // Allocate the request. // dwRequestLength = sizeof( NWR_NDS_REQUEST_PACKET ) + puTreeName->Length + puUserName->Length + puCurrentPassword->Length + puNewPassword->Length; pNdsRequest = LocalAlloc( LMEM_ZEROINIT, dwRequestLength ); if ( !pNdsRequest) { return STATUS_INSUFFICIENT_RESOURCES; } // // Copy the parameters into the request buffer. // try { (pNdsRequest->Parameters).ChangePass.NdsTreeNameLength = puTreeName->Length; (pNdsRequest->Parameters).ChangePass.UserNameLength = puUserName->Length; (pNdsRequest->Parameters).ChangePass.CurrentPasswordLength = puCurrentPassword->Length; (pNdsRequest->Parameters).ChangePass.NewPasswordLength = puNewPassword->Length; CurrentString = ( PBYTE ) &((pNdsRequest->Parameters).ChangePass.StringBuffer[0]); RtlCopyMemory( CurrentString, puTreeName->Buffer, puTreeName->Length ); CurrentString += puTreeName->Length; RtlCopyMemory( CurrentString, puUserName->Buffer, puUserName->Length ); CurrentString += puUserName->Length; RtlCopyMemory( CurrentString, puCurrentPassword->Buffer, puCurrentPassword->Length ); CurrentString += puCurrentPassword->Length; RtlCopyMemory( CurrentString, puNewPassword->Buffer, puNewPassword->Length ); Status = NtFsControlFile( hNwRdr, NULL, NULL, NULL, &IoStatusBlock, FSCTL_NWR_NDS_CHANGE_PASS, (PVOID) pNdsRequest, dwRequestLength, NULL, 0 ); } except ( EXCEPTION_EXECUTE_HANDLER ) { Status = STATUS_INVALID_PARAMETER; } LocalFree( pNdsRequest ); return Status; }