-
Oct 27th, 2021, 02:04 PM
#1
How to access a LINUX SMB share to create a simple text file
Looking for someone with experience accessing a LINUX SMB share from .Net.
I was hoping to open a text file and write to it.
Someone gave me a POWERSHELL script showing how they map a drive to that share username and password being set.
Code:
New-PSDRIVE -NAME "I" -PSProvider FileSystem -Root \\xxx.xxx.xxx.xxx\xx01 -Credential $Credential -Scope Global -Persist
I'll be doing this programmatically - running as a service on a backend server.
-
Oct 27th, 2021, 06:38 PM
#2
Re: How to access a LINUX SMB share to create a simple text file
So you're trying to read a drive on a Linux instance from a Windows Service written in .Net? Is that correct?
-
Oct 27th, 2021, 07:22 PM
#3
Re: How to access a LINUX SMB share to create a simple text file
Yes.
If I run this code I get: System.IO.IOException: 'The user name or password is incorrect.
If I run the script that maps that I: drive (that POWERSHELL thing in my first post), then this code works fine and makes a test.txt file on that IP address/folder name
Code:
Imports System.IO
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim strFolder As String = "\\xxx.xx.xx.xxx\DX01"
Dim strFilename As String = strFolder + "\test.txt"
Using fs As New StreamWriter(strFilename)
fs.WriteLine("Hello World")
End Using
End Sub
End Class
-
Oct 28th, 2021, 12:36 PM
#4
Re: How to access a LINUX SMB share to create a simple text file
Hmmm....It seems the communication needs to be authenticated which isn't surprising. When I need to connect to a Windows machine from Ubuntu to read a Windows drive though SMB, it does require me to enter the user name and password of a Windows account. It seems that your code also needs to be authenticated.
You can try making identical accounts on both Linux and Windows. Identical user name and password. Log in to that account on Windows and run your code again and see if that works.
-
Oct 28th, 2021, 12:51 PM
#5
Re: How to access a LINUX SMB share to create a simple text file
I am very far away from the linux box and have little IT support for requests...
I need IIS running on a web server to drop text files into this share. I can store credentials in my web.config file, but I am clueless on how to supply them. I see .Net 5 has an AuthenticatedStream class...
If i could run the code for mapping the drive someone - that "opens" the folder for credential-less access - athough if that behaves the same on a server as it does on my workstation will have to be tested as well.
-
Oct 28th, 2021, 09:45 PM
#6
Re: How to access a LINUX SMB share to create a simple text file
So you know the credentials of the Linux machine I assume? If you don't have control over the Linux machine then create the account on the Windows side to match the Linux machine and see if that works.
-
Oct 29th, 2021, 06:43 AM
#7
Re: How to access a LINUX SMB share to create a simple text file
I've got this working so far - when I run it on my workstation.
I'm about to put it on the server and run it in IIS. If that PROC.START runs in the same context as the executing web thread in IIS, then this might work...
Code:
Imports System.IO
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim strFolder As String = "\\172.16.47.100\DX01"
Dim strFilename As String = strFolder + "\test.txt"
Open_Remote_Connection("111.222.333.100", "xyzxyz", "123456")
Using fs As New StreamWriter(strFilename)
fs.WriteLine("Hello World")
End Using
End Sub
Public Sub Open_Remote_Connection(ByVal strComputer As String, ByVal strUsername As String, ByVal strPassword As String)
Try
Dim procInfo As New ProcessStartInfo
procInfo.FileName = "net"
procInfo.Arguments = "use \\" & strComputer & "\DX01 /USER:" & strUsername & " " & strPassword
procInfo.WindowStyle = ProcessWindowStyle.Hidden
procInfo.CreateNoWindow = True
Dim proc As New Process
proc.StartInfo = procInfo
proc.Start()
proc.WaitForExit(15000)
Catch ex As Exception
MsgBox("Open_Remote_Connection" & vbCrLf & vbCrLf & ex.Message, 4096, "Error")
End Try
End Sub
End Class
-
Oct 29th, 2021, 07:12 AM
#8
Re: How to access a LINUX SMB share to create a simple text file
WARNING!!! C# code ahead :-))
Code:
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
public class UncShareWithCredentials : IDisposable
{
private string _uncShare;
public UncShareWithCredentials(string uncShare, string userName, string password)
{
var nr = new Native.NETRESOURCE
{
dwType = Native.RESOURCETYPE_DISK,
lpRemoteName = uncShare
};
int result = Native.WNetUseConnection(IntPtr.Zero, nr, password, userName, 0, null, null, null);
if (result != Native.NO_ERROR)
{
throw new Win32Exception(result);
}
_uncShare = uncShare;
}
public void Dispose()
{
if (!string.IsNullOrEmpty(_uncShare))
{
Native.WNetCancelConnection2(_uncShare, Native.CONNECT_UPDATE_PROFILE, false);
_uncShare = null;
}
}
private class Native
{
public const int RESOURCETYPE_DISK = 0x00000001;
public const int CONNECT_UPDATE_PROFILE = 0x00000001;
public const int NO_ERROR = 0;
[DllImport("mpr.dll")]
public static extern int WNetUseConnection(IntPtr hwndOwner, NETRESOURCE lpNetResource, string lpPassword, string lpUserID,
int dwFlags, string lpAccessName, string lpBufferSize, string lpResult);
[DllImport("mpr.dll")]
public static extern int WNetCancelConnection2(string lpName, int dwFlags, bool fForce);
[StructLayout(LayoutKind.Sequential)]
public class NETRESOURCE
{
public int dwScope;
public int dwType;
public int dwDisplayType;
public int dwUsage;
public string lpLocalName;
public string lpRemoteName;
public string lpComment;
public string lpProvider;
}
}
}
Just create an instance of UncShareWithCredentials for your \\server\share with proper credentials and you can then directly access \\server\share from this point on in all I/O operations (not only in your process) -- the OS will use the cached credentials as long as the instance of UncShareWithCredentials is not disposed.
cheers,
</wqw>
-
Oct 29th, 2021, 07:24 AM
#9
Re: How to access a LINUX SMB share to create a simple text file
Originally Posted by wqweto
WARNING!!! C# code ahead :-))
Code:
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
public class UncShareWithCredentials : IDisposable
{
private string _uncShare;
public UncShareWithCredentials(string uncShare, string userName, string password)
{
var nr = new Native.NETRESOURCE
{
dwType = Native.RESOURCETYPE_DISK,
lpRemoteName = uncShare
};
int result = Native.WNetUseConnection(IntPtr.Zero, nr, password, userName, 0, null, null, null);
if (result != Native.NO_ERROR)
{
throw new Win32Exception(result);
}
_uncShare = uncShare;
}
public void Dispose()
{
if (!string.IsNullOrEmpty(_uncShare))
{
Native.WNetCancelConnection2(_uncShare, Native.CONNECT_UPDATE_PROFILE, false);
_uncShare = null;
}
}
private class Native
{
public const int RESOURCETYPE_DISK = 0x00000001;
public const int CONNECT_UPDATE_PROFILE = 0x00000001;
public const int NO_ERROR = 0;
[DllImport("mpr.dll")]
public static extern int WNetUseConnection(IntPtr hwndOwner, NETRESOURCE lpNetResource, string lpPassword, string lpUserID,
int dwFlags, string lpAccessName, string lpBufferSize, string lpResult);
[DllImport("mpr.dll")]
public static extern int WNetCancelConnection2(string lpName, int dwFlags, bool fForce);
[StructLayout(LayoutKind.Sequential)]
public class NETRESOURCE
{
public int dwScope;
public int dwType;
public int dwDisplayType;
public int dwUsage;
public string lpLocalName;
public string lpRemoteName;
public string lpComment;
public string lpProvider;
}
}
}
Just create an instance of UncShareWithCredentials for your \\server\share with proper credentials and you can then directly access \\server\share from this point on in all I/O operations (not only in your process) -- the OS will use the cached credentials as long as the instance of UncShareWithCredentials is not disposed.
cheers,
</wqw>
Thank you very much!
And I can handle the C# - when I dipped my toes in WPF a few years ago I forced myself to use C# so I could get fluent in it - since I'm already a decade into heavy JavaScript code anyway, curly brackets and semicolons are cool!
-
Oct 29th, 2021, 12:16 PM
#10
Re: How to access a LINUX SMB share to create a simple text file
@wqw
Here is the class in VB. For some reason the Dispose that is canceling the share does not appear to work (see more comments and code snippets below).
Code:
Public Class UncShareWithCredentials : Implements IDisposable
Private _uncShare As String
Public Sub New(ByVal uncShare As String, ByVal userName As String, ByVal password As String)
Dim nr = New Native.NETRESOURCE With {.dwType = Native.RESOURCETYPE_DISK, .lpRemoteName = uncShare}
Dim result As Integer = Native.WNetUseConnection(IntPtr.Zero, nr, password, userName, 0, Nothing, Nothing, Nothing)
If result <> Native.NO_ERROR Then
Throw New Win32Exception(result)
End If
_uncShare = uncShare
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
If Not String.IsNullOrEmpty(_uncShare) Then
Native.WNetCancelConnection2(_uncShare, Native.CONNECT_UPDATE_PROFILE, False)
_uncShare = Nothing
End If
End Sub
Private Class Native
Public Const RESOURCETYPE_DISK As Integer = &H1
Public Const CONNECT_UPDATE_PROFILE As Integer = &H1
Public Const NO_ERROR As Integer = 0
<DllImport("mpr.dll")> Public Shared Function WNetUseConnection(ByVal hwndOwner As IntPtr, ByVal lpNetResource As NETRESOURCE, ByVal lpPassword As String, ByVal lpUserID As String, ByVal dwFlags As Integer, ByVal lpAccessName As String, ByVal lpBufferSize As String, ByVal lpResult As String) As Integer
End Function
<DllImport("mpr.dll")> Public Shared Function WNetCancelConnection2(ByVal lpName As String, ByVal dwFlags As Integer, ByVal fForce As Boolean) As Integer
End Function
<StructLayout(LayoutKind.Sequential)> Public Class NETRESOURCE
Public dwScope As Integer
Public dwType As Integer
Public dwDisplayType As Integer
Public dwUsage As Integer
Public lpLocalName As String
Public lpRemoteName As String
Public lpComment As String
Public lpProvider As String
End Class
End Class
End Class
The code below has been tested both on a workstation and on a web server running IIS.
Test Bed 1 fails - bad username / password. This is the expected behavior.
Test Bed 2 uses the UNC share class and the file gets created.
Problem is that running Test Bed 1 again now works. Whatever was done in Test Bed 2 has persisted. I'm not sure this is a problem - but I want to know why or what I can do to fix it.
I have stepped through the code and the DISPOSE is getting called and is not encountering any errors. TIA!
Code:
Dim strFolder As String = "\\111.333.222.111\xx01"
Dim strFilename As String = strFolder & "\testbed" & strTestBed & ".txt"
Select Case strTestBed
Case "1"
Try
strResult = DateTime.Now.ToString()
Using fs As New StreamWriter(strFilename)
fs.WriteLine(strResult)
End Using
Catch ex As Exception
strResult = "?Bed 1 Error:" & ex.Message
End Try
Case "2"
Try
Using xyz As UncShareWithCredentials = New UncShareWithCredentials(strFolder, "scz123", "scz123")
strResult = DateTime.Now.ToString()
Using fs As New StreamWriter(strFilename)
fs.WriteLine(strResult)
End Using
End Using
Catch ex As Exception
strResult = "?Bed 2 Error:" & ex.Message
End Try
Case Else
strResult = "?Unknown Test Bed value=" & strTestBed
End Select
-
Oct 29th, 2021, 12:22 PM
#11
Re: How to access a LINUX SMB share to create a simple text file
Originally Posted by wqweto
WARNING!!! C# code ahead :-))
No warning needed I think it's safe to say that most experienced VB.Net developers read a lot of C# code since there is a lot more of it out there. We learn to do a lot of things in VB.Net by reading C# code.
-
Oct 29th, 2021, 12:27 PM
#12
Re: How to access a LINUX SMB share to create a simple text file
I think this is expected behavior -- Windows caches credentials used for remote shares for a few minutes. Not sure what is the timeout on these.
Also this "stateful" behavior is global i.e. you can view the share with cached credentials in Explorer in the meantime too, not only from your process.
It looks like SMB/Netman legacy has grown to a big ball of mud with no prospect of fixing anything there since Win 3.11.
cheers,
</wqw>
-
Oct 29th, 2021, 12:28 PM
#13
Re: How to access a LINUX SMB share to create a simple text file
Originally Posted by szlamany
Problem is that running Test Bed 1 again now works. Whatever was done in Test Bed 2 has persisted. I'm not sure this is a problem - but I want to know why or what I can do to fix it.
I think I know what might be happening. I'm guessing there's some kind of impersonation going on that isn't getting undone. In any case, I have some code from a program I wrote years ago. I'm gonna see if I can dig it up. I'm curious as to what will happen when you use it. I'll brb.
-
Oct 29th, 2021, 04:23 PM
#14
Re: How to access a LINUX SMB share to create a simple text file
Ok sorry I took so long. Some important things came up after I made that last post. Had to go deal with it.
Anyways put this class in your program:-
Code:
Public Class WindowsAccount
Const LOGON32_PROVIDER_DEFAULT As Integer = 0
Const LOGON32_LOGON_INTERACTIVE As Integer = 2
<DllImport("advapi32.dll", CharSet:=CharSet.Unicode)>
Private Shared Function LogonUser(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
End Function
<DllImport("kernel32.dll")>
Private Shared Function CloseHandle(ByVal handle As IntPtr) As Boolean
End Function
Private _context As WindowsImpersonationContext
Public Sub Login(ByVal userName As String, ByVal password As String)
Dim winID As WindowsIdentity
Dim token As IntPtr
If Not LogonUser(userName, "", password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, token) Then
Throw New Exception("Login failed.")
End If
winID = New WindowsIdentity(token)
_context = winID.Impersonate
If Not CloseHandle(token) Then
Throw New Exception("Failed to close token handle")
End If
End Sub
Public Sub LogOut()
_context.Dispose()
End Sub
End Class
I just wrote that class and what does is cause the current thread to run in the context of another user, basically impersonating that user. You can use it like this:-
Code:
Dim wa As New WindowsAccount
'Log in as user "dingdong"
wa.Login("dingdong", "password12345")
'Print the user name of the currently logged in user to prove it works
Debug.WriteLine(WindowsIdentity.GetCurrent.Name)
'Log out
wa.LogOut()
'Print the user name of the currently logged in user
'to prove we logged out
Debug.WriteLine(WindowsIdentity.GetCurrent.Name)
So what you can do is use the class the log in as the user that has authorization to the Linux share and then run your normal StreamWriter code and see if it works. Note though, the user account must exist on the machine where that code is running. If it works it would give sufficient clues about what is actually happening internally to form an accurate hypothesis about what is going on.
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|