parallel but separate streams?

Sep 8, 2009 at 5:14 PM

to fix a issue with my application in the past i had to have the zip file created 1st, then have the file copied after the file is created. i had to do this because the file is copied to a secured server/share that the user WILL NOT have access to. i use a UserImpersonator class that effectivly overrides the users fileshare credentials so the application can access the share. the problem is that while Impersonation is active, the application WILL NOT be able to access any of the files specified ad being added to the zip if they reside on a remote server/share that the service account cannot access. creating the file locally then copying fixes this, but it depends on the user having massive ammounts of temeraty HD space.

because the application used to stream directly to the share, i am being asked to look into making it that way again. the only way i can think of doing this is if i open separate steams somehow using separate credentials. does DotNetZip support this?

here is the Impersonation code.

'Imports System
Imports System.IO
Imports System.Security.Principal
Imports System.Security.Permissions

Public Class Impersonator

    Private TokenHandle As New IntPtr(0)
    Private DupTokenHandle As New IntPtr(0)
    Private ImpersonatedUser As WindowsImpersonationContext
    'Private MyUserInfo As UserSettings
    'Private MyDirectoryInfo As FilePaths
    Private WinID As WindowsIdentity
    Private MyUserID, MyPassword, MyDomain As String
    Private Success As Boolean

    Public Enum LogonType As Integer
        'This logon type is intended for users who will be interactively using the computer, such as a user being logged on
        'by a terminal server, remote shell, or similar process.
        'This logon type has the additional expense of caching logon information for disconnected operations;
        'therefore, it is inappropriate for some client/server applications,
        'such as a mail server.

        'This logon type is intended for high performance servers to authenticate plaintext passwords.
        'The LogonUser function does not cache credentials for this logon type.

        'This logon type is intended for batch servers, where processes may be executing on behalf of a user without
        'their direct intervention. This type is also for higher performance servers that process many plaintext
        'authentication attempts at a time, such as mail or Web servers.
        'The LogonUser function does not cache credentials for this logon type.
        LOGON32_LOGON_BATCH = 4

        'Indicates a service-type logon. The account provided must have the service privilege enabled.

        'This logon type is for GINA DLLs that log on users who will be interactively using the computer.
        'This logon type can generate a unique audit record that shows when the workstation was unlocked.
        LOGON32_LOGON_UNLOCK = 7

        'This logon type preserves the name and password in the authentication package, which allows the server to make
        'connections to other network servers while impersonating the client. A server can accept plaintext credentials
        'from a client, call LogonUser, verify that the user can access the system across the network, and still
        'communicate with other servers.
        'NOTE: Windows NT:  This value is not supported.

        'This logon type allows the caller to clone its current token and specify new credentials for outbound connections.
        'The new logon session has the same local identifier but uses different credentials for other network connections.
        'NOTE: This logon type is supported only by the LOGON32_PROVIDER_WINNT50 logon provider.
        'NOTE: Windows NT:  This value is not supported.
    End Enum

    Public Enum LogonProvider As Integer
        'Use the standard logon provider for the system.
        'The default security provider is negotiate, unless you pass NULL for the domain name and the user name
        'is not in UPN format. In this case, the default provider is NTLM.
        'NOTE: Windows 2000/NT:   The default security provider is NTLM.
    End Enum

    ' Declare the Windows API Function used to login as a different user
    Private Declare Auto Function LogonUser Lib "advapi32.dll" (ByVal lpszUserName As String, ByVal lpszDomain As String, ByVal lpszPassword As String, ByVal dwLogonType As Integer, ByVal dwLogonProvider As Integer, ByRef phToken As IntPtr) As Boolean

    Public Sub New(ByVal UserID As String, ByVal Password As String, ByVal Domain As String)
        MyUserID = UserID.Trim
        MyPassword = Password.Trim
        MyDomain = Domain.Trim
        Success = StartImpersonation()
    End Sub

    Public ReadOnly Property WasSuccessful() As Boolean
            Return Success
        End Get
    End Property

    Private Function StartImpersonation() As Boolean
        ' This function impersonates the service account for the file server
        ' Initialize the Token Handle (the Token Handle represents
        '  the impersonated user ID)
        TokenHandle = IntPtr.Zero
        Dim LoggedIn As Boolean
        ' Call the Windows API LogonUser function
        '  If the logon is successful, the Toke Handle is loaded with the
        '  Impersonated User
        LoggedIn = LogonUser(MyUserID, MyDomain, MyPassword, LogonType.LOGON32_LOGON_NEW_CREDENTIALS, LogonProvider.LOGON32_PROVIDER_DEFAULT, TokenHandle)
        If Not LoggedIn Then
            ' Login failed so return FALSE
            Return False
            Exit Function
        End If
            ' Create a Windows Identity Object from the Token Handle
            WinID = New WindowsIdentity(TokenHandle)
            ' Create the Impersonation Object
            ImpersonatedUser = WinID.Impersonate
            ' Impersonation Worked so return TRUE
            Return True
            ' Error trapped during Impersonation attempt
            ' so return FALSE
            Return False
        End Try
    End Function

    Public Sub EndImpersonation()
        ' Stop Impersonation
        End Try
    End Sub

End Class


Sep 8, 2009 at 7:08 PM

If I understand what you are trying to do,

  • your app is run (as a desktop app?)  by a number of different people
  • you want the app to be able to read files for the zip, using the user's own credentials
  • you want the app to write a zipfile to a secure share, using some other credentials
  • you want to do this without writing the zip file twice.

If that's the case, then I think you need WNetAddConnection2.  It is described here; .   It lets you make a connection to a network resource, like a share, using credentials that you specify.   Here's an example of how to call it in VB.NET

In your program, you'd set up the connection, then create the zipfile.  call ZipFile.Save("\\server\share\") . The zip file will be saved over the secure connection you set up. Then you need to call WNetCancelConnection2 to close that secure connection.

Sep 8, 2009 at 7:20 PM

i have a mapping class already that would work, but it was problematic because it would fail if the user was already connected to that server regardless of the share. this is because windows does not allow two connections to a server from different accounts. my users were getting errors like crazy, and the only workarround was to tell the user to unmap the drive. we called microsoft and they supplied the impersonator to help us out. it doesnt care if the drive is mapped or not. adding the impersonator so far has reduced the errors ALOT. copying the files locally reduces them 100%.

Sep 8, 2009 at 7:29 PM

Ah, I see. 

Hmm, then I don't know how to resolve this.

You cannot use the new connection idea, because of complications.

You can't use impersonation because the impersonated user cannot read the input files.

You've sort of painted yourself into a corner here.

Either you need a single user that can read the inputs AND write the outputs,  OR
you need separate secure connections to the inputs and ouputs. 

Maybe you can create the secure connection only when it does not already exist?  Maybe there's a way to see if the user is already connected to the server.  But if the user is connected and cannot write anyway, well that doesn't solve the problem, does it?

You probably have a better handle on how to make this work, than I do.



Sep 8, 2009 at 7:39 PM

thanks for looking at it. this problem has been a real pain for months since i took over the project. this has been a known issue for many years. perhaps i could merge the two, and use impersonation as needed... not perfect, but it would be better.

Thanks again


Sep 8, 2009 at 7:42 PM

Or grant the impersonated user rights to read the files being zipped.

or create a new share on the server where all users have the right to write (but not necessarily read, or delete files).


there's gotta be a way.

anyway good luck with it.