diff options
Diffstat (limited to 'Emby.Common.Implementations/IO/SharpCifs/Smb/SmbSession.cs')
| -rw-r--r-- | Emby.Common.Implementations/IO/SharpCifs/Smb/SmbSession.cs | 570 |
1 files changed, 570 insertions, 0 deletions
diff --git a/Emby.Common.Implementations/IO/SharpCifs/Smb/SmbSession.cs b/Emby.Common.Implementations/IO/SharpCifs/Smb/SmbSession.cs new file mode 100644 index 000000000..6dc5087d0 --- /dev/null +++ b/Emby.Common.Implementations/IO/SharpCifs/Smb/SmbSession.cs @@ -0,0 +1,570 @@ +// This code is derived from jcifs smb client library <jcifs at samba dot org> +// Ported by J. Arturo <webmaster at komodosoft dot net> +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using SharpCifs.Netbios; +using SharpCifs.Util.Sharpen; + +namespace SharpCifs.Smb +{ + public sealed class SmbSession + { + private static readonly string LogonShare = Config.GetProperty("jcifs.smb.client.logonShare" + , null); + + private static readonly int LookupRespLimit = Config.GetInt("jcifs.netbios.lookupRespLimit" + , 3); + + private static readonly string Domain = Config.GetProperty("jcifs.smb.client.domain" + , null); + + private static readonly string Username = Config.GetProperty("jcifs.smb.client.username" + , null); + + private static readonly int CachePolicy = Config.GetInt("jcifs.netbios.cachePolicy" + , 60 * 10) * 60; + + internal static NbtAddress[] DcList; + + internal static long DcListExpiration; + + internal static int DcListCounter; + + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + private static NtlmChallenge Interrogate(NbtAddress addr) + { + UniAddress dc = new UniAddress(addr); + SmbTransport trans = SmbTransport.GetSmbTransport(dc, 0); + if (Username == null) + { + trans.Connect(); + if (SmbTransport.LogStatic.Level >= 3) + { + SmbTransport.LogStatic.WriteLine("Default credentials (jcifs.smb.client.username/password)" + + " not specified. SMB signing may not work propertly." + " Skipping DC interrogation." + ); + } + } + else + { + SmbSession ssn = trans.GetSmbSession(NtlmPasswordAuthentication.Default + ); + ssn.GetSmbTree(LogonShare, null).TreeConnect(null, null); + } + return new NtlmChallenge(trans.Server.EncryptionKey, dc); + } + + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + /// <exception cref="UnknownHostException"></exception> + public static NtlmChallenge GetChallengeForDomain() + { + if (Domain == null) + { + throw new SmbException("A domain was not specified"); + } + lock (Domain) + { + long now = Runtime.CurrentTimeMillis(); + int retry = 1; + do + { + if (DcListExpiration < now) + { + NbtAddress[] list = NbtAddress.GetAllByName(Domain, 0x1C, null, + null); + DcListExpiration = now + CachePolicy * 1000L; + if (list != null && list.Length > 0) + { + DcList = list; + } + else + { + DcListExpiration = now + 1000 * 60 * 15; + if (SmbTransport.LogStatic.Level >= 2) + { + SmbTransport.LogStatic.WriteLine("Failed to retrieve DC list from WINS"); + } + } + } + int max = Math.Min(DcList.Length, LookupRespLimit); + for (int j = 0; j < max; j++) + { + int i = DcListCounter++ % max; + if (DcList[i] != null) + { + try + { + return Interrogate(DcList[i]); + } + catch (SmbException se) + { + if (SmbTransport.LogStatic.Level >= 2) + { + SmbTransport.LogStatic.WriteLine("Failed validate DC: " + DcList[i]); + if (SmbTransport.LogStatic.Level > 2) + { + Runtime.PrintStackTrace(se, SmbTransport.LogStatic); + } + } + } + DcList[i] = null; + } + } + DcListExpiration = 0; + } + while (retry-- > 0); + DcListExpiration = now + 1000 * 60 * 15; + } + throw new UnknownHostException("Failed to negotiate with a suitable domain controller for " + + Domain); + } + + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + /// <exception cref="UnknownHostException"></exception> + public static byte[] GetChallenge(UniAddress dc) + { + return GetChallenge(dc, 0); + } + + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + /// <exception cref="UnknownHostException"></exception> + public static byte[] GetChallenge(UniAddress dc, int port) + { + SmbTransport trans = SmbTransport.GetSmbTransport(dc, port); + trans.Connect(); + return trans.Server.EncryptionKey; + } + + /// <summary> + /// Authenticate arbitrary credentials represented by the + /// <tt>NtlmPasswordAuthentication</tt> object against the domain controller + /// specified by the <tt>UniAddress</tt> parameter. + /// </summary> + /// <remarks> + /// Authenticate arbitrary credentials represented by the + /// <tt>NtlmPasswordAuthentication</tt> object against the domain controller + /// specified by the <tt>UniAddress</tt> parameter. If the credentials are + /// not accepted, an <tt>SmbAuthException</tt> will be thrown. If an error + /// occurs an <tt>SmbException</tt> will be thrown. If the credentials are + /// valid, the method will return without throwing an exception. See the + /// last <a href="../../../faq.html">FAQ</a> question. + /// <p> + /// See also the <tt>jcifs.smb.client.logonShare</tt> property. + /// </remarks> + /// <exception cref="SmbException"></exception> + public static void Logon(UniAddress dc, NtlmPasswordAuthentication auth) + { + Logon(dc, -1, auth); + } + + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + public static void Logon(UniAddress dc, int port, NtlmPasswordAuthentication auth + ) + { + SmbTree tree = SmbTransport.GetSmbTransport(dc, port).GetSmbSession(auth).GetSmbTree + (LogonShare, null); + if (LogonShare == null) + { + tree.TreeConnect(null, null); + } + else + { + Trans2FindFirst2 req = new Trans2FindFirst2("\\", "*", SmbFile.AttrDirectory); + Trans2FindFirst2Response resp = new Trans2FindFirst2Response(); + tree.Send(req, resp); + } + } + + internal int ConnectionState; + + internal int Uid; + + internal List<object> Trees; + + private UniAddress _address; + + private int _port; + + private int _localPort; + + private IPAddress _localAddr; + + internal SmbTransport transport; + + internal NtlmPasswordAuthentication Auth; + + internal long Expiration; + + internal string NetbiosName; + + internal SmbSession(UniAddress address, int port, IPAddress localAddr, int localPort + , NtlmPasswordAuthentication auth) + { + // Transport parameters allows trans to be removed from CONNECTIONS + this._address = address; + this._port = port; + this._localAddr = localAddr; + this._localPort = localPort; + this.Auth = auth; + Trees = new List<object>(); + ConnectionState = 0; + } + + internal SmbTree GetSmbTree(string share, string service) + { + lock (this) + { + SmbTree t; + if (share == null) + { + share = "IPC$"; + } + /*for (IEnumeration e = trees.GetEnumerator(); e.MoveNext(); ) + { + t = (SmbTree)e.Current; + if (t.Matches(share, service)) + { + return t; + } + }*/ + foreach (var e in Trees) + { + t = (SmbTree)e; + if (t.Matches(share, service)) + { + return t; + } + } + + t = new SmbTree(this, share, service); + Trees.Add(t); + return t; + } + } + + internal bool Matches(NtlmPasswordAuthentication auth) + { + return this.Auth == auth || this.Auth.Equals(auth); + } + + internal SmbTransport Transport() + { + lock (this) + { + if (transport == null) + { + transport = SmbTransport.GetSmbTransport(_address, _port, _localAddr, _localPort, null + ); + } + return transport; + } + } + + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + internal void Send(ServerMessageBlock request, ServerMessageBlock response) + { + lock (Transport()) + { + if (response != null) + { + response.Received = false; + } + Expiration = Runtime.CurrentTimeMillis() + SmbConstants.SoTimeout; + SessionSetup(request, response); + if (response != null && response.Received) + { + return; + } + if (request is SmbComTreeConnectAndX) + { + SmbComTreeConnectAndX tcax = (SmbComTreeConnectAndX)request; + if (NetbiosName != null && tcax.path.EndsWith("\\IPC$")) + { + tcax.path = "\\\\" + NetbiosName + "\\IPC$"; + } + } + request.Uid = Uid; + request.Auth = Auth; + try + { + transport.Send(request, response); + } + catch (SmbException se) + { + if (request is SmbComTreeConnectAndX) + { + Logoff(true); + } + request.Digest = null; + throw; + } + } + } + + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + internal void SessionSetup(ServerMessageBlock andx, ServerMessageBlock andxResponse + ) + { + lock (Transport()) + { + NtlmContext nctx = null; + SmbException ex = null; + SmbComSessionSetupAndX request; + SmbComSessionSetupAndXResponse response; + byte[] token = new byte[0]; + int state = 10; + while (ConnectionState != 0) + { + if (ConnectionState == 2 || ConnectionState == 3) + { + // connected or disconnecting + return; + } + try + { + Runtime.Wait(transport); + } + catch (Exception ie) + { + throw new SmbException(ie.Message, ie); + } + } + ConnectionState = 1; + // trying ... + try + { + transport.Connect(); + if (transport.Log.Level >= 4) + { + transport.Log.WriteLine("sessionSetup: accountName=" + Auth.Username + ",primaryDomain=" + + Auth.Domain); + } + Uid = 0; + do + { + switch (state) + { + case 10: + { + if (Auth != NtlmPasswordAuthentication.Anonymous && transport.HasCapability(SmbConstants + .CapExtendedSecurity)) + { + state = 20; + break; + } + request = new SmbComSessionSetupAndX(this, andx, Auth); + response = new SmbComSessionSetupAndXResponse(andxResponse); + if (transport.IsSignatureSetupRequired(Auth)) + { + if (Auth.HashesExternal && NtlmPasswordAuthentication.DefaultPassword != NtlmPasswordAuthentication + .Blank) + { + transport.GetSmbSession(NtlmPasswordAuthentication.Default).GetSmbTree(LogonShare + , null).TreeConnect(null, null); + } + else + { + byte[] signingKey = Auth.GetSigningKey(transport.Server.EncryptionKey); + request.Digest = new SigningDigest(signingKey, false); + } + } + request.Auth = Auth; + try + { + transport.Send(request, response); + } + catch (SmbAuthException sae) + { + throw; + } + catch (SmbException se) + { + ex = se; + } + if (response.IsLoggedInAsGuest && Runtime.EqualsIgnoreCase("GUEST", Auth. + Username) == false && transport.Server.Security != SmbConstants.SecurityShare && + Auth != NtlmPasswordAuthentication.Anonymous) + { + throw new SmbAuthException(NtStatus.NtStatusLogonFailure); + } + if (ex != null) + { + throw ex; + } + Uid = response.Uid; + if (request.Digest != null) + { + transport.Digest = request.Digest; + } + ConnectionState = 2; + state = 0; + break; + } + + case 20: + { + if (nctx == null) + { + bool doSigning = (transport.Flags2 & SmbConstants.Flags2SecuritySignatures + ) != 0; + nctx = new NtlmContext(Auth, doSigning); + } + if (SmbTransport.LogStatic.Level >= 4) + { + SmbTransport.LogStatic.WriteLine(nctx); + } + if (nctx.IsEstablished()) + { + NetbiosName = nctx.GetNetbiosName(); + ConnectionState = 2; + state = 0; + break; + } + try + { + token = nctx.InitSecContext(token, 0, token.Length); + } + catch (SmbException se) + { + try + { + transport.Disconnect(true); + } + catch (IOException) + { + } + Uid = 0; + throw; + } + if (token != null) + { + request = new SmbComSessionSetupAndX(this, null, token); + response = new SmbComSessionSetupAndXResponse(null); + if (transport.IsSignatureSetupRequired(Auth)) + { + byte[] signingKey = nctx.GetSigningKey(); + if (signingKey != null) + { + request.Digest = new SigningDigest(signingKey, true); + } + } + request.Uid = Uid; + Uid = 0; + try + { + transport.Send(request, response); + } + catch (SmbAuthException sae) + { + throw; + } + catch (SmbException se) + { + ex = se; + try + { + transport.Disconnect(true); + } + catch (Exception) + { + } + } + if (response.IsLoggedInAsGuest && Runtime.EqualsIgnoreCase("GUEST", Auth. + Username) == false) + { + throw new SmbAuthException(NtStatus.NtStatusLogonFailure); + } + if (ex != null) + { + throw ex; + } + Uid = response.Uid; + if (request.Digest != null) + { + transport.Digest = request.Digest; + } + token = response.Blob; + } + break; + } + + default: + { + throw new SmbException("Unexpected session setup state: " + state); + } + } + } + while (state != 0); + } + catch (SmbException se) + { + Logoff(true); + ConnectionState = 0; + throw; + } + finally + { + Runtime.NotifyAll(transport); + } + } + } + + internal void Logoff(bool inError) + { + lock (Transport()) + { + if (ConnectionState != 2) + { + // not-connected + return; + } + ConnectionState = 3; + // disconnecting + NetbiosName = null; + + foreach (SmbTree t in Trees) + { + t.TreeDisconnect(inError); + } + + if (!inError && transport.Server.Security != SmbConstants.SecurityShare) + { + SmbComLogoffAndX request = new SmbComLogoffAndX(null); + request.Uid = Uid; + try + { + transport.Send(request, null); + } + catch (SmbException) + { + } + Uid = 0; + } + ConnectionState = 0; + Runtime.NotifyAll(transport); + } + } + + public override string ToString() + { + return "SmbSession[accountName=" + Auth.Username + ",primaryDomain=" + Auth.Domain + + ",uid=" + Uid + ",connectionState=" + ConnectionState + "]"; + } + } +} |
