Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix not working domain and workstation parameter on sessionSetup #47

Merged
merged 1 commit into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 0 additions & 16 deletions Sources/SMBClient/Data.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,6 @@ extension BinaryConvertible {
}
}

extension BinaryConvertible {
func hexString() -> String {
(Data() + self).hex
}
}

extension UInt8: BinaryConvertible {}
extension UInt16: BinaryConvertible {}
extension UInt32: BinaryConvertible {}
Expand All @@ -31,13 +25,3 @@ extension Int16: BinaryConvertible {}
extension Int32: BinaryConvertible {}
extension Int64: BinaryConvertible {}
extension Int: BinaryConvertible {}

extension String: BinaryConvertible {
static func +(lhs: Data, rhs: Self) -> Data {
return lhs + (rhs.data(using: .utf16LittleEndian) ?? Data())
}

static func +=(lhs: inout Data, rhs: Self) {
lhs = lhs + rhs
}
}
188 changes: 128 additions & 60 deletions Sources/SMBClient/NTLM.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ public enum NTLM {
public let signature: UInt64
public let messageType: UInt32
public let negotiateFlags: NegotiateFlags
public let domainName: Fields
public let workstationName: Fields
public let domainNameFields: Fields
public let workstationNameFields: Fields
public let version: UInt64

public init(
Expand All @@ -28,28 +28,48 @@ public enum NTLM {
domainName: String? = nil,
workstationName: String? = nil
) {
signature = 0x4e544c4d53535000
signature = 0x005053534D4C544E
messageType = 0x00000001
self.negotiateFlags = negotiateFlags
let offset: UInt32 = 64
self.domainName = Fields(value: domainName, offset: offset)
self.workstationName = Fields(value: workstationName, offset: offset + UInt32(self.domainName.len))

let domainNameFields = Fields(
value: domainName,
offset: 8 + // Signature
4 + // MessageType
4 + // NegotiateFlags
8 + // DomainNameFields
8 + // WorkstationFields
8 // Version
)
self.domainNameFields = domainNameFields

let workstationNameFields = Fields(
value: workstationName,
offset: domainNameFields.nextOffset
)
self.workstationNameFields = workstationNameFields

version = 0x0000000000000000
}

public func encoded() -> Data {
var data = Data()
data += signature.bigEndian

data += signature
data += messageType
data += negotiateFlags.rawValue
data += domainName.len
data += domainName.maxLen
data += domainName.bufferOffset
data += workstationName.len
data += workstationName.maxLen
data += workstationName.bufferOffset

data += domainNameFields.len
data += domainNameFields.maxLen
data += domainNameFields.bufferOffset

data += workstationNameFields.len
data += workstationNameFields.maxLen
data += workstationNameFields.bufferOffset

data += version
data += domainName.encoded() + workstationName.encoded()
data += domainNameFields.value + workstationNameFields.value

return data
}
}
Expand All @@ -74,6 +94,7 @@ public enum NTLM {

public init(data: Data) {
let reader = ByteReader(data)

signature = reader.read()
messageType = reader.read()
targetNameLen = reader.read()
Expand All @@ -97,13 +118,13 @@ public enum NTLM {
password: String,
domain: String
) -> Data {
let passwordData = password.data(using: .utf16LittleEndian)!
let passwordData = password.data(using: .utf16LittleEndian) ?? Data()
var passwordHash = [UInt8](repeating: 0, count: Int(CC_MD4_DIGEST_LENGTH))
_ = passwordData.withUnsafeBytes { (bytes) in
CC_MD4(bytes.baseAddress, CC_LONG(passwordData.count), &passwordHash)
}

let usernameData = (username.uppercased() + domain).data(using: .utf16LittleEndian)!
let usernameData = (username.uppercased() + domain).data(using: .utf16LittleEndian) ?? Data()
let responseKeyNT = Crypto.hmacMD5(key: Data(passwordHash), data: usernameData)

return responseKeyNT
Expand All @@ -113,6 +134,7 @@ public enum NTLM {
username: String? = nil,
password: String? = nil,
domain: String? = nil,
workstation: String? = nil,
negotiateMessage: Data,
signingKey: Data
) -> AuthenticateMessage {
Expand All @@ -138,38 +160,36 @@ public enum NTLM {

let ntChallengeResponse = ntProofStr + temp

let authenticateMessage = NTLM.AuthenticateMessage(
var authenticateMessage = NTLM.AuthenticateMessage(
ntChallengeResponse: ntChallengeResponse,
domainName: domain,
userName: username,
workstationName: workstation,
encryptedRandomSessionKey: encryptedRandomSessionKey
)
let mic = Crypto.hmacMD5(
key: signingKey,
data: negotiateMessage + buffer + authenticateMessage.encoded()
)

return NTLM.AuthenticateMessage(
ntChallengeResponse: ntChallengeResponse,
userName: username,
workstationName: "",
encryptedRandomSessionKey: encryptedRandomSessionKey,
mic: mic
)
authenticateMessage.mic = mic

return authenticateMessage
}
}

public struct AuthenticateMessage {
public let signature: UInt64
public let messageType: UInt32
public let lmChallengeResponse: Fields
public let ntChallengeResponse: Fields
public let domainName: Fields
public let userName: Fields
public let workstationName: Fields
public let encryptedRandomSessionKey: Fields
public let lmChallengeResponseFields: Fields
public let ntChallengeResponseFields: Fields
public let domainNameFields: Fields
public let userNameFields: Fields
public let workstationNameFields: Fields
public let encryptedRandomSessionKeyFields: Fields
public let negotiateFlags: NegotiateFlags
public let version: UInt64
public let mic: Data
public internal(set) var mic: Data

public init(
ntChallengeResponse: Data,
Expand All @@ -179,12 +199,13 @@ public enum NTLM {
encryptedRandomSessionKey: Data? = nil,
mic: Data = Data()
) {
signature = 0x4e544c4d53535000
signature = 0x005053534D4C544E
messageType = 0x00000003
lmChallengeResponse = Fields(

let lmChallengeResponseFields = Fields(
value: Data(count: 24),
offset: 8 + // signature
4 + // messageType
offset: 8 + // Signature
4 + // MessageType
8 + // LmChallengeResponseFields
8 + // NtChallengeResponseFields
8 + // DomainNameFields
Expand All @@ -195,11 +216,37 @@ public enum NTLM {
8 + // Version
16 // MIC
)
self.ntChallengeResponse = Fields(value: ntChallengeResponse, offset: lmChallengeResponse.bufferOffset + UInt32(lmChallengeResponse.len))
self.domainName = Fields(value: domainName, offset: self.ntChallengeResponse.bufferOffset + UInt32(self.ntChallengeResponse.len))
self.userName = Fields(value: userName, offset: self.domainName.bufferOffset + UInt32(self.domainName.len))
self.workstationName = Fields(value: workstationName, offset: self.userName.bufferOffset + UInt32(self.userName.len))
self.encryptedRandomSessionKey = Fields(value: encryptedRandomSessionKey ?? Data(), offset: self.workstationName.bufferOffset + UInt32(self.workstationName.len))
self.lmChallengeResponseFields = lmChallengeResponseFields

let ntChallengeResponseFields = Fields(
value: ntChallengeResponse,
offset: lmChallengeResponseFields.nextOffset
)
self.ntChallengeResponseFields = ntChallengeResponseFields

let domainNameFields = Fields(
value: domainName?.data(using: .utf16LittleEndian),
offset: ntChallengeResponseFields.nextOffset
)
self.domainNameFields = domainNameFields

let userNameFields = Fields(
value: userName?.data(using: .utf16LittleEndian),
offset: domainNameFields.nextOffset
)
self.userNameFields = userNameFields

let workstationNameFields = Fields(
value: workstationName?.data(using: .utf16LittleEndian),
offset: userNameFields.nextOffset
)
self.workstationNameFields = workstationNameFields

let encryptedRandomSessionKeyFields = Fields(
value: encryptedRandomSessionKey,
offset: workstationNameFields.nextOffset
)
self.encryptedRandomSessionKeyFields = encryptedRandomSessionKeyFields

negotiateFlags = [
.negotiateKeyExchange,
Expand All @@ -215,29 +262,33 @@ public enum NTLM {
.unicode,
]

version = UInt64(0x000a02000000000f).bigEndian
version = 0x0F00000000020A00
self.mic = mic.isEmpty ? Data(count: 16) : mic
}

public func encoded() -> Data {
var data = Data()
data += signature.bigEndian

data += signature
data += messageType
data += lmChallengeResponse.encoded()
data += ntChallengeResponse.encoded()
data += domainName.encoded()
data += userName.encoded()
data += workstationName.encoded()
data += encryptedRandomSessionKey.encoded()
data += lmChallengeResponseFields.encoded()
data += ntChallengeResponseFields.encoded()
data += domainNameFields.encoded()
data += userNameFields.encoded()
data += workstationNameFields.encoded()
data += encryptedRandomSessionKeyFields.encoded()
data += negotiateFlags.rawValue
data += version
data += mic
data += lmChallengeResponse.value
data += ntChallengeResponse.value
data += domainName.value
data += userName.value
data += workstationName.value
data += encryptedRandomSessionKey.value

data += lmChallengeResponseFields.value
data += ntChallengeResponseFields.value

data += domainNameFields.value
data += userNameFields.value
data += workstationNameFields.value
data += encryptedRandomSessionKeyFields.value

return data
}
}
Expand Down Expand Up @@ -279,19 +330,36 @@ public enum NTLM {
public let bufferOffset: UInt32
public let value: Data

public init(value: Data, offset: UInt32) {
let nextOffset: UInt32

public init(value: Data?, offset: UInt32) {
let value = value ?? Data()

len = UInt16(value.count)
maxLen = len
bufferOffset = offset
self.value = value

nextOffset = offset + UInt32(len)
}

public init(value: String?, offset: UInt32) {
let data = Data() + (value ?? "")
len = UInt16(data.count)
maxLen = len
bufferOffset = offset
self.value = data
let value = value ?? ""
if let data = value.data(using: .ascii), !data.isEmpty {
len = UInt16(truncatingIfNeeded: data.count)
maxLen = len
bufferOffset = offset
self.value = data + Data(count: 1)

nextOffset = offset + UInt32(len) + 1
} else {
len = 0
maxLen = len
bufferOffset = offset
self.value = Data()

nextOffset = offset
}
}

public func encoded() -> Data {
Expand Down
1 change: 1 addition & 0 deletions Sources/SMBClient/Session.swift
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ public class Session {
username: username,
password: password,
domain: domain,
workstation: workstation,
negotiateMessage: securityBuffer,
signingKey: signingKey
)
Expand Down
32 changes: 31 additions & 1 deletion Tests/SMBClientTests/SessionTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,37 @@ final class SessionTests: XCTestCase {
XCTAssertEqual(response.dialectRevision , Negotiate.Dialects.smb210.rawValue)
}

func testLoginSucceeded() async throws {
func testSessionSetup01() async throws {
let session = Session(host: "localhost", port: 4445)

try await session.connect()
try await session.negotiate()

try await session.sessionSetup(username: "alice", password: "alipass")
try await session.logoff()
}

func testSessionSetup02() async throws {
let session = Session(host: "localhost", port: 4445)

try await session.connect()
try await session.negotiate()

try await session.sessionSetup(username: "alice", password: "alipass", domain: ".")
try await session.logoff()
}

func testSessionSetup03() async throws {
let session = Session(host: "localhost", port: 4445)

try await session.connect()
try await session.negotiate()

try await session.sessionSetup(username: "alice", password: "alipass", domain: ".", workstation: "WORKSTATION")
try await session.logoff()
}

func testSessionSetup04() async throws {
let session = Session(host: "localhost", port: 4445)

try await session.connect()
Expand Down
Loading