-
Notifications
You must be signed in to change notification settings - Fork 2
/
ntlm.go
818 lines (772 loc) · 28.8 KB
/
ntlm.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
// package ntlm implements the NTLM client security service as described in
// MS-NLMP (https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp).
//
// This package also contains client-side GSSAPI bindings (InitSecurityContext, Wrap, Unwrap and so on).
package ntlm
import (
"bytes"
"context"
"errors"
"time"
"github.com/oiweiwei/go-msrpc/ssp/credential"
"github.com/oiweiwei/go-msrpc/ssp/ntlm/internal"
"github.com/oiweiwei/go-msrpc/text/encoding/utf16le"
)
var (
ErrInvalidNTLMSignature = errors.New("invalid ntlmssp signature, expected 'NTLMSSP'")
ErrInvalidMessageType = errors.New("unexpected message type")
)
// Use password credential.
type Credential = credential.Password
// The NTLM authentification version.
type NTLMVersion interface {
// WithConfig function adds the configuration to the NTLM version.
WithConfig(context.Context, *Config) NTLMVersion
// LMOWF function is a NT LAN Manager (LM) one-way function used to create
// a hash based on the user's password.
LMOWF(context.Context, Credential) ([]byte, error)
// NTOWF function is a NT LAN Manager (NT) one-way function used to create
// a hash based on user's password.
NTOWF(context.Context, Credential) ([]byte, error)
// ChallengeResponse function computes the response for the server challenge message
// based on client challenge `nonce` parameter and set of negotiated flags.
ChallengeResponse(context.Context, Credential, *ChallengeMessage, []byte) (*ChallengeResponse, error)
// KeyExchangeKey function returns the key used to protect the session key that is
// generated by the client.
KeyExchangeKey(context.Context, *ChallengeMessage, *ChallengeResponse) ([]byte, error)
}
// The computed challenge response.
type ChallengeResponse struct {
// NT challenge response.
NT []byte
// LM challenge response.
LM []byte
// Tmp.
Tmp []byte
// The session base key.
SessionBaseKey []byte
// The NT one-way-function.
KeyNT []byte
// The LM one-way-function.
KeyLM []byte
// The key exchange key.
KeyExchangeKey []byte
// If 'true', username and password are empty.
// (anonymous session).
IsAnonymous bool
// RequestMIC.
RequestMIC bool
}
// The NTLM message type.
type MessageType uint32
const (
// None.
MessageTypeNone MessageType = 0x00000000
// Negotiate Message.
MessageTypeNegotiate MessageType = 0x00000001
// Challenge Message.
MessageTypeChallenge MessageType = 0x00000002
// Authenticate Message.
MessageTypeAuthenticate MessageType = 0x00000003
)
// The negotiation flag.
type Flag uint32
// IsSet returns `true` if flag is set.
func (f Flag) IsSet(ff Flag) bool {
return f&ff != 0
}
// Set function sets the provided flag.
func (f Flag) Set(ff Flag) Flag {
return f | ff
}
// Unset function unsets the provided flag.
func (f Flag) Unset(ff Flag) Flag {
return f & ^ff
}
const (
// If set, requests 56-bit encryption. If the client sends NegotiateSeal
// or NegotiateSign with Negotiate56 to the server in the
// NegotiateMessage, the server MUST return Negotiate56 to the client
// in the ChallengeMessage. Otherwise it is ignored. If both Negotiate56
// and Negotiate128 are requested and supported by the client and server,
// Negotiate56 and Negotiate128 will both be returned to the client.
// Clients and servers that set NegotiateSeal SHOULD set Negotiate56
// if it is supported.
Negotiate56 Flag = 1 << (31 - 0)
// If set, requests an explicit key exchange. This capability SHOULD be used because
// it improves security for message integrity or confidentiality.
NegotiateKeyExchange Flag = 1 << (31 - 1)
// If set, requests 128-bit session key negotiation. If the client sends Negotiate128
// to the server in the NegotiateMessage, the server MUST return Negotiate128
// to the client in the ChallengeMessage only if the client sets NegotiateSeal
// or NegotiateSign. Otherwise it is ignored. If both Negotiate56 and
// Negotiate128 are requested and supported by the client and server,
// Negotiate56 and Negotiate128 will both be returned to the client.
// Clients and servers that set NegotiateSeal SHOULD set Negotiate128
// if it is supported.
Negotiate128 Flag = 1 << (31 - 2)
// This bit is unused and MUST be zero.
R1 Flag = 1 << (31 - 3)
// This bit is unused and MUST be zero.
R2 Flag = 1 << (31 - 4)
// This bit is unused and MUST be zero.
R3 Flag = 1 << (31 - 5)
// If set, requests the protocol version number. The data corresponding to this flag is
// provided in the Version field of the NegotiateMessage, the ChallengeMessage, and
// the AuthenticateMessage.
NegotiateVersion Flag = 1 << (31 - 6)
// This bit is unused and MUST be zero.
R4 Flag = 1 << (31 - 7)
// If set, indicates that the TargetInfo fields in the ChallengeMessage
// are populated.
NegotiateTargetInfo Flag = 1 << (31 - 8)
// If set, requests the usage of the LMOWF.
RequestNonNTSessionKey Flag = 1 << (31 - 9)
// This bit is unused and MUST be zero.
R5 Flag = 1 << (31 - 10)
// If set, requests an identify level token.
NegotiateIdentify Flag = 1 << (31 - 11)
// If set, requests usage of the NTLM v2 session security. NTLM v2 session security
// is a misnomer because it is not NTLM v2. It is NTLM v1 using the extended session
// security that is also in NTLM v2. NegotiateLMKey and
// NegotiateExtendedSessionSecurity are mutually exclusive. If both
// NegotiateExtendedSessionSecurity and NegotiateLMKey are
// requested, NegotiateExtendedSessionSecurity alone MUST be returned
// to the client. NTLM v2 authentication session key generation MUST be supported
// by both the client and the DC in order to be used, and extended session security
// signing and sealing requires support from the client and the server in order
// to be used.
NegotiateExtendedSessionSecurity Flag = 1 << (31 - 12)
// This bit is unused and MUST be zero.
R6 Flag = 1 << (31 - 13)
// If set, TargetName MUST be a server name. The data corresponding to this flag
// is provided by the server in the TargetName field of the ChallengeMessage.
// If this bit is set, then TARGET_TYPE_DOMAIN MUST NOT be set.
// This flag MUST be ignored in the NegotiateMessage and the AuthenticateMessage.
TargetTypeServer Flag = 1 << (31 - 14)
// If set, TargetName MUST be a domain name. The data corresponding to this flag is
// provided by the server in the TargetName field of the ChallengeMessage. If set,
// then TARGET_TYPE_SERVER MUST NOT be set. This flag MUST be ignored in
// the NegotiateMessage and the AuthenticateMessage.
TargetTypeDomain Flag = 1 << (31 - 15)
// If set, a session key is generated regardless of the states of NegotiateSign
// and NegotiateSeal. A session key MUST always exist to generate the MIC in
// the authenticate message. NegotiateAlwaysSign MUST be set in the
// NegotiateMessage to the server and the ChallengeMessage to the client.
// NegotiateAlwaysSign is overridden by NegotiateSign and
// NegotiateSeal, if they are supported.
NegotiateAlwaysSign Flag = 1 << (31 - 16)
// This bit is unused and MUST be zero.
R7 Flag = 1 << (31 - 17)
// This flag indicates whether the Workstation field is present.
// If this flag is not set, the Workstation field MUST be ignored.
// If this flag is set, the length of the Workstation field specifies
// whether the workstation name is nonempty or not.
NegotiateOEMWorkstationSupplied Flag = 1 << (31 - 18)
// If set, the domain name is provided.
NegotiateOEMDomainSupplied Flag = 1 << (31 - 19)
// If set, the connection SHOULD be anonymous.
Anonymous Flag = 1 << (31 - 20)
// This bit is unused and MUST be zero.
R8 Flag = 1 << (31 - 21)
// If set, requests usage of the NTLM v1 session security protocol.
// NegotiateNTLM MUST be set in the NegotiateMessage to
// the server and the ChallengeMessage to the client.
NegotiateNTLM Flag = 1 << (31 - 22)
// This bit is unused and MUST be zero.
R9 Flag = 1 << (31 - 23)
// If set, requests LAN Manager (LM) session key computation.
// NegotiateLMKey and NegotiateExtendedSessionSecurity
// are mutually exclusive. If both NegotiateLMKey and
// NegotiateExtendedSessionSecurity are requested,
// NegotiateExtendedSessionSecurity alone MUST be returned
// to the client. NTLM v2 authentication session key generation MUST be
// supported by both the client and the DC in order to be used, and extended
// session security signing and sealing requires support from the client and
// the server to be used.
NegotiateLMKey Flag = 1 << (31 - 24)
// If set, requests connectionless authentication. If NegotiateDatagram
// is set, then NegotiateKeyExchange MUST always be set in the
// AuthenticateMessage to the server and the ChallengeMessage to the client.
NegotiateDatagram Flag = 1 << (31 - 25)
// If set, requests session key negotiation for message confidentiality.
// If the client sends NegotiateSeal to the server in the
// NegotiateMessage, the server MUST return NegotiateSeal to the
// client in the ChallengeMessage. Clients and servers that set
// NegotiateSeal SHOULD always set Negotiate56 and
// Negotiate128, if they are supported.
NegotiateSeal Flag = 1 << (31 - 26)
// If set, requests session key negotiation for message signatures.
// If the client sends NegotiateSign to the server in the
// NegotiateMessage, the server MUST return NegotiateSign
// to the client in the ChallengeMessage.
NegotiateSign Flag = 1 << (31 - 27)
// This bit is unused and MUST be zero.
R10 Flag = 1 << (31 - 28)
// If set, a TargetName field of the ChallengeMessage MUST be supplied.
RequestTarget Flag = 1 << (31 - 29)
// If set, requests OEM character set encoding.
NegotiateOEM Flag = 1 << (31 - 30)
// If set, requests Unicode character set encoding.
NegotiateUnicode Flag = 1 << (31 - 31)
)
var (
// The default NTLM signature.
NTLMSignature = []byte{'N', 'T', 'L', 'M', 'S', 'S', 'P', 0}
)
// The NegotiateMessage defines an NTLM negotiate message that is sent from
// the client to the server. This message allows the client to specify its
// supported NTLM options to the server.
type NegotiateMessage struct {
// An 8-byte character array that MUST contain the ASCII
// string ('N', 'T', 'L', 'M', 'S', 'S', 'P', '\0').
Signature []byte
// A 32-bit unsigned integer that indicates the message type.
// This field MUST be set to 0x00000001.
MessageType MessageType
// A NEGOTIATE structure that contains a set of flags.
// The client sets flags to indicate options it supports.
Negotiate Flag
// A 16-bit unsigned integer that defines the size, in bytes,
// of DomainName in the Payload.
DomainNameLen uint16
// A 16-bit unsigned integer that SHOULD be set to the value of
// DomainNameLen, and MUST be ignored on receipt.
DomainNameMaxLen uint16
// A 32-bit unsigned integer that defines the offset, in bytes,
// from the beginning of the NegotiateMessage to DomainName in
// Payload.
DomainNameOffset uint32
// A 16-bit unsigned integer that defines the size, in bytes,
// of WorkStationName in the Payload.
WorkstationLen uint16
// A 16-bit unsigned integer that SHOULD be set to the value of
// WorkstationLen and MUST be ignored on receipt.
WorkstationMaxLen uint16
// A 32-bit unsigned integer that defines the offset, in bytes, from
// the beginning of the NegotiateMessage to WorkstationName in the
// Payload.
WorkstationOffset uint32
// A Version structure that is populated only when the NegotiateVersion
// flag is set in the NegotiateFlags field; otherwise, it MUST be set to
// all zero. This structure SHOULD be used for debugging purposes only.
// In normal (nondebugging) protocol messages, it is ignored and does not
// affect the NTLM message processing.
Version Version
// If DomainNameLen does not equal 0x0000, DomainName MUST be a byte-array
// that contains the name of the client authentication domain that MUST be
// encoded using the OEM character set. Otherwise, this data is not present.
DomainName string
// If WorkstationLen does not equal 0x0000, WorkstationName MUST be a byte
// array that contains the name of the client machine that MUST be encoded
// using the OEM character set. Otherwise, this data is not present.
Workstation string
}
// Marshal function marshals the NegotiateMessage.
func (m *NegotiateMessage) Marshal(ctx context.Context) ([]byte, error) {
e := internal.NewCodec(ctx, nil)
// signature.
if m.Signature == nil {
m.Signature = NTLMSignature
}
e.WriteBytes(ctx, m.Signature, 8)
// message_type.
if m.MessageType == MessageTypeNone {
m.MessageType = MessageTypeNegotiate
}
e.WriteData(ctx, (uint32)(m.MessageType))
// negotiate.
e.WriteData(ctx, (uint32)(m.Negotiate))
// domain_name.
e.WritePayload(ctx, internal.PayloadField(
&m.DomainNameLen,
&m.DomainNameMaxLen,
&m.DomainNameOffset,
m.DomainName,
))
// workstation.
e.WritePayload(ctx, internal.PayloadField(
&m.WorkstationLen,
&m.WorkstationMaxLen,
&m.WorkstationOffset,
m.Workstation,
))
// version.
if m.Negotiate.IsSet(NegotiateVersion) {
if m.Version.IsZero() {
m.Version = DefaultVersion
}
e.WriteData(ctx, m.Version)
} else {
e.WriteData(ctx, Version{})
}
return e.Bytes(ctx)
}
// Unmarshal function unmarshals the NegotiateMessage.
func (m *NegotiateMessage) Unmarshal(ctx context.Context, b []byte) error {
e := internal.NewCodec(ctx, b)
// signature.
e.ReadBytes(ctx, &m.Signature, 8)
if !bytes.Equal(m.Signature, NTLMSignature) {
return ErrInvalidNTLMSignature
}
// message_type.
e.ReadData(ctx, (*uint32)(&m.MessageType))
if m.MessageType != MessageTypeNegotiate {
return ErrInvalidMessageType
}
// negotiate.
e.ReadData(ctx, (*uint32)(&m.Negotiate))
// domain_name.
e.ReadPayload(ctx, internal.PayloadField(
&m.DomainNameLen,
&m.DomainNameMaxLen,
&m.DomainNameOffset,
&m.DomainName,
))
// workstation.
e.ReadPayload(ctx, internal.PayloadField(
&m.WorkstationLen,
&m.WorkstationMaxLen,
&m.WorkstationOffset,
&m.Workstation,
))
// version.
e.ReadData(ctx, &m.Version)
return e.ReadAll(ctx)
}
// The ChallengeMessage defines an NTLM challenge message that is sent from
// the server to the client. The ChallengeMessage is used by the server to
// challenge the client to prove its identity. For connection-oriented requests,
// the ChallengeMessage generated by the server is in response to the
// NegotiateMessage from the client.
type ChallengeMessage struct {
// An 8-byte character array that MUST contain the ASCII
// string ('N', 'T', 'L', 'M', 'S', 'S', 'P', '\0').
Signature []byte
// A 32-bit unsigned integer that indicates the message type.
// This field MUST be set to 0x00000002.
MessageType MessageType
// A 16-bit unsigned integer that defines the size, in bytes,
// of TargetInfo in Payload.
TargetNameLen uint16
// A 16-bit unsigned integer that SHOULD be set to the value
// of TargetInfoLen and MUST be ignored on receipt.
TargetNameMaxLen uint16
// A 32-bit unsigned integer that defines the offset, in bytes,
// from the beginning of the CHALLENGE_MESSAGE to TargetInfo in
// Payload.
TargetNameOffset uint32
// A set of flags. The server sets flags to indicate options it
// supports or, if there has been a NegotiateMessage, the choices
// it has made from the options offered by the client.
Negotiate Flag
// A 64-bit value that contains the NTLM challenge. The challenge is a
// 64-bit nonce.
ServerChallenge []byte
// An 8-byte array whose elements MUST be zero when sent and MUST be
// ignored on receipt.
_ [8]byte
// A 16-bit unsigned integer that defines the size, in bytes, of
// TargetInfo in Payload.
TargetInfoLen uint16
// A 16-bit unsigned integer that SHOULD be set to the value of
// TargetInfoLen and MUST be ignored on receipt.
TargetInfoMaxLen uint16
// A 32-bit unsigned integer that defines the offset, in bytes,
// from the beginning of the ChallengeMessage to TargetInfo in
// Payload.
TargetInfoOffset uint32
// A VERSION structure that is populated only when the NegotiateVersion
// flag is set in the NegotiateFlags field; otherwise, it MUST be set to
// all zero. This structure SHOULD be used for debugging purposes only.
// In normal (nondebugging) protocol messages, it is ignored and does not
// affect the NTLM message processing.
Version Version
// If TargetNameLen does not equal 0x0000, TargetName MUST be a byte array
// that contains the name of the server authentication realm, and MUST be
// expressed in the negotiated character set. A server that is a member of
// a domain returns the domain of which it is a member, and a server that
// is not a member of a domain returns the server name.
TargetName string
// If TargetInfoLen does not equal 0x0000, TargetInfo MUST be a byte array
// that contains a sequence of AV_PAIR structures.
// The length of each AV_PAIR is determined by its AvLen field (plus 4 bytes).
TargetInfo AttrValues
}
// Marshal function marshals the ChallengeMessage.
func (m *ChallengeMessage) Marshal(ctx context.Context) ([]byte, error) {
e := internal.NewCodec(ctx, nil)
// string_encoding.
if m.Negotiate.IsSet(NegotiateUnicode) {
e = e.WithStringCodec(ctx, utf16le.NewEncoding())
}
// signature.
if m.Signature == nil {
m.Signature = NTLMSignature
}
e.WriteBytes(ctx, m.Signature, 8)
// message_type.
if m.MessageType == MessageTypeNone {
m.MessageType = MessageTypeChallenge
}
e.WriteData(ctx, (uint32)(m.MessageType))
// target_name.
e.WritePayload(ctx, internal.PayloadField(
&m.TargetNameLen,
&m.TargetNameMaxLen,
&m.TargetNameOffset,
m.TargetName,
))
// negotiate.
e.WriteData(ctx, (uint32)(m.Negotiate))
// server_challenge.
e.WriteBytes(ctx, m.ServerChallenge, 8)
// pad.
e.WriteBytes(ctx, nil, 8)
// target_info.
e.WritePayload(ctx, internal.PayloadField(
&m.TargetInfoLen,
&m.TargetInfoMaxLen,
&m.TargetInfoOffset,
&m.TargetInfo,
))
// version.
if m.Negotiate.IsSet(NegotiateVersion) {
if m.Version.IsZero() {
m.Version = DefaultVersion
}
e.WriteData(ctx, m.Version)
} else {
e.WriteData(ctx, Version{})
}
return e.Bytes(ctx)
}
// Unmarshal function unmarshals the ChallengeMessage.
func (m *ChallengeMessage) Unmarshal(ctx context.Context, b []byte) error {
e := internal.NewCodec(ctx, b)
// signature.
e.ReadBytes(ctx, &m.Signature, 8)
if !bytes.Equal(m.Signature, NTLMSignature) {
return ErrInvalidNTLMSignature
}
// message_type.
e.ReadData(ctx, (*uint32)(&m.MessageType))
if m.MessageType != MessageTypeChallenge {
return ErrInvalidMessageType
}
// target_name.
e.ReadPayload(ctx, internal.PayloadField(
&m.TargetNameLen,
&m.TargetNameMaxLen,
&m.TargetNameOffset,
&m.TargetName,
))
// negotiate.
e.ReadData(ctx, (*uint32)(&m.Negotiate))
// string_encoding.
if m.Negotiate.IsSet(NegotiateUnicode) {
e = e.WithStringCodec(ctx, utf16le.NewEncoding())
}
// server_challenge.
e.ReadBytes(ctx, &m.ServerChallenge, 8)
// pad.
e.ReadBytes(ctx, nil, 8)
// target_info.
e.ReadPayload(ctx, internal.PayloadField(
&m.TargetInfoLen,
&m.TargetInfoMaxLen,
&m.TargetInfoOffset,
&m.TargetInfo,
))
// version.
e.ReadData(ctx, &m.Version)
return e.ReadAll(ctx)
}
// The AuthenticateMessage defines an NTLM authenticate message that is sent
// from the client to the server after the ChallengeMessage is processed by
// the client.
type AuthenticateMessage struct {
// An 8-byte character array that MUST contain the ASCII
// string ('N', 'T', 'L', 'M', 'S', 'S', 'P', '\0').
Signature []byte
// A 32-bit unsigned integer that indicates the message type.
// This field MUST be set to 0x00000003.
MessageType MessageType
// A 16-bit unsigned integer that defines the size, in bytes,
// of LmChallengeResponse in Payload.
LMChallengeResponseLen uint16
// A 16-bit unsigned integer that SHOULD be set to the value of
// LmChallengeResponseLen and MUST be ignored on receipt.
LMChallengeResponseMaxLen uint16
// A 32-bit unsigned integer that defines the offset, in bytes,
// from the beginning of the AuthenticateMessage to LmChallengeResponse
// in Payload.
LMChallengeResponseOffset uint32
// A 16-bit unsigned integer that defines the size, in bytes, of
// NtChallengeResponse in Payload.
NTChallengeResponseLen uint16
// A 16-bit unsigned integer that SHOULD be set to the value of
// NtChallengeResponseLen and MUST be ignored on receipt.
NTChallengeResponseMaxLen uint16
// A 32-bit unsigned integer that defines the offset, in bytes,
// from the beginning of the AuthenticateMessage to NtChallengeResponse
// in Payload.
NTChallengeResponseOffset uint32
// A 16-bit unsigned integer that defines the size, in bytes, of DomainName
// in Payload.
DomainNameLen uint16
// A 16-bit unsigned integer that SHOULD be set to the value of DomainNameLen
// and MUST be ignored on receipt.
DomainNameMaxLen uint16
// A 32-bit unsigned integer that defines the offset, in bytes, from the
// beginning of the AuthenticateMessage to DomainName in Payload. If DomainName
// is a Unicode string, the values of DomainNameBufferOffset and DomainNameLen
// MUST be multiples of 2.
DomainNameOffset uint32
// A 16-bit unsigned integer that defines the size, in bytes, of UserName in
// Payload, not including a NULL terminator.
UserNameLen uint16
// A 16-bit unsigned integer that SHOULD be set to the value of UserNameLen
// and MUST be ignored on receipt.
UserNameMaxLen uint16
// A 32-bit unsigned integer that defines the offset, in bytes, from the
// beginning of the AuthenticateMessage to UserName in Payload. If the UserName
// to be sent contains a Unicode string, the values of UserNameBufferOffset
// and UserNameLen MUST be multiples of 2.
UserNameOffset uint32
// A 16-bit unsigned integer that defines the size, in bytes, of Workstation
// in Payload.
WorkstationLen uint16
// A 16-bit unsigned integer that SHOULD be set to the value of WorkstationLen
// and MUST be ignored on receipt.
WorkstationMaxLen uint16
// A 32-bit unsigned integer that defines the offset, in bytes, from the beginning
// of the AuthenticateMessage to Workstation in Payload. If Workstation contains
// a Unicode string, the values of WorkstationBufferOffset and WorkstationLen
// MUST be multiples of 2.
WorkstationOffset uint32
// A 16-bit unsigned integer that defines the size, in bytes,
// of EncryptedRandomSessionKey in Payload.
EncryptedRandomSessionKeyLen uint16
// A 16-bit unsigned integer that SHOULD be set to the value of
// EncryptedRandomSessionKeyLen and MUST be ignored on receipt.
EncryptedRandomSessionKeyMaxLen uint16
// A 32-bit unsigned integer that defines the offset, in bytes,
// from the beginning of the AuthenticateMessage to EncryptedRandomSessionKey
// in Payload.
EncryptedRandomSessionKeyOffset uint32
// In connectionless mode, a NEGOTIATE structure that contains a set of
// flags and represents the conclusion of negotiation—the choices the
// client has made from the options the server offered in the ChallengeMessage.
// In connection-oriented mode, a NEGOTIATE structure that contains the set of
// bit flags negotiated in the previous messages.
Negotiate Flag
// A Version structure that is populated only when the NegotiateVersion
// flag is set in the NegotiateFlags field; otherwise, it MUST be set to
// all zero. This structure SHOULD be used for debugging purposes only.
// In normal (nondebugging) protocol messages, it is ignored and does not
// affect the NTLM message processing.
Version Version
// The message integrity for the NTLM NegotiateMessage, ChallengeMessage, and
// AuthenticateMessage.
MIC []byte
// An LMResponse structure or an LMv2Response structure that contains
// the computed LM response to the challenge. If NTLM v2 authentication is
// configured, then LmChallengeResponse MUST be an LMv2Response structure.
// Otherwise, it MUST be an LMResponse structure.
LMChallengeResponse []byte
// An NTLMResponse structure or NTLMv2Response structure
// that contains the computed NT response to the challenge. If NTLM v2
// authentication is configured, NtChallengeResponse MUST be an NTLMv2Response.
// Otherwise, it MUST be an NTLMResponse.
NTChallengeResponse []byte
// The domain or computer name hosting the user account. DomainName MUST
// be encoded in the negotiated character set.
DomainName string
// The name of the user to be authenticated. UserName MUST be encoded in the
// negotiated character set.
UserName string
// The name of the computer to which the user is logged on. Workstation MUST
// be encoded in the negotiated character set.
Workstation string
// The client's encrypted random session key.
EncryptedRandomSessionKey []byte
}
// Marshal function marshals the AuthenticateMessage.
func (m *AuthenticateMessage) Marshal(ctx context.Context) ([]byte, error) {
e := internal.NewCodec(ctx, nil)
// string_encoding.
if m.Negotiate.IsSet(NegotiateUnicode) {
e = e.WithStringCodec(ctx, utf16le.NewEncoding())
}
// signature.
if m.Signature == nil {
m.Signature = NTLMSignature
}
e.WriteBytes(ctx, m.Signature, 8)
// message_type.
if m.MessageType == MessageTypeNone {
m.MessageType = MessageTypeAuthenticate
}
e.WriteData(ctx, (uint32)(m.MessageType))
// lm_challenge_response.
e.WritePayload(ctx, internal.PayloadField(
&m.LMChallengeResponseLen,
&m.LMChallengeResponseMaxLen,
&m.LMChallengeResponseOffset,
m.LMChallengeResponse,
))
// nt_challenge_response.
if m.NTChallengeResponse != nil {
e.WritePayload(ctx, internal.PayloadField(
&m.NTChallengeResponseLen,
&m.NTChallengeResponseMaxLen,
&m.NTChallengeResponseOffset,
m.NTChallengeResponse,
))
} else {
// write zero-offset.
e.WriteBytes(ctx, nil, 8)
}
// domain_name.
e.WritePayload(ctx, internal.PayloadField(
&m.DomainNameLen,
&m.DomainNameMaxLen,
&m.DomainNameOffset,
m.DomainName,
))
// user_name.
e.WritePayload(ctx, internal.PayloadField(
&m.UserNameLen,
&m.UserNameMaxLen,
&m.UserNameOffset,
m.UserName,
))
// workstation.
e.WritePayload(ctx, internal.PayloadField(
&m.WorkstationLen,
&m.WorkstationMaxLen,
&m.WorkstationOffset,
m.Workstation,
))
// encrypted_random_session_key.
e.WritePayload(ctx, internal.PayloadField(
&m.EncryptedRandomSessionKeyLen,
&m.EncryptedRandomSessionKeyMaxLen,
&m.EncryptedRandomSessionKeyOffset,
m.EncryptedRandomSessionKey,
))
// negotitate.
e.WriteData(ctx, (uint32)(m.Negotiate))
// version.
if m.Negotiate.IsSet(NegotiateVersion) {
if m.Version.IsZero() {
m.Version = DefaultVersion
}
e.WriteData(ctx, m.Version)
} else {
e.WriteData(ctx, Version{})
}
// mic. (if present must be of length 16).
if m.MIC != nil {
e.WriteBytes(ctx, m.MIC, 16)
}
return e.Bytes(ctx)
}
// Unmarshal function unmarshals the AuthenticateMessage.
func (m *AuthenticateMessage) Unmarshal(ctx context.Context, b []byte) error {
e := internal.NewCodec(ctx, b)
// signature.
e.ReadBytes(ctx, &m.Signature, 8)
if !bytes.Equal(m.Signature, NTLMSignature) {
return ErrInvalidNTLMSignature
}
// message_type.
e.ReadData(ctx, (*uint32)(&m.MessageType))
if m.MessageType != MessageTypeAuthenticate {
return ErrInvalidMessageType
}
// lm_challenge_response.
e.ReadPayload(ctx, internal.PayloadField(
&m.LMChallengeResponseLen,
&m.LMChallengeResponseMaxLen,
&m.LMChallengeResponseOffset,
&m.LMChallengeResponse,
))
// nt_challenge_response.
e.ReadPayload(ctx, internal.PayloadField(
&m.NTChallengeResponseLen,
&m.NTChallengeResponseMaxLen,
&m.NTChallengeResponseOffset,
&m.NTChallengeResponse,
))
// domain_name.
e.ReadPayload(ctx, internal.PayloadField(
&m.DomainNameLen,
&m.DomainNameMaxLen,
&m.DomainNameOffset,
&m.DomainName,
))
// user_name.
e.ReadPayload(ctx, internal.PayloadField(
&m.UserNameLen,
&m.UserNameMaxLen,
&m.UserNameOffset,
&m.UserName,
))
// workstation.
e.ReadPayload(ctx, internal.PayloadField(
&m.WorkstationLen,
&m.WorkstationMaxLen,
&m.WorkstationOffset,
&m.Workstation,
))
// encrypted_random_session_key.
e.ReadPayload(ctx, internal.PayloadField(
&m.EncryptedRandomSessionKeyLen,
&m.EncryptedRandomSessionKeyMaxLen,
&m.EncryptedRandomSessionKeyOffset,
&m.EncryptedRandomSessionKey,
))
// negotitate.
e.ReadData(ctx, (*uint32)(&m.Negotiate))
// string_encoding.
if m.Negotiate.IsSet(NegotiateUnicode) {
e = e.WithStringCodec(ctx, utf16le.NewEncoding())
}
// version.
e.ReadData(ctx, &m.Version)
// mic. (decode nt_challenge_response av_pairs).
if m.NTChallengeResponseLen > 24 { // at position 24 av_pairs start.
attrs := AttrValues{}
if err := (&attrs).Unmarshal(ctx, b[m.NTChallengeResponseOffset+24:]); err != nil {
return err
}
if attrs.Flag().IsSet(MICProvided) {
e.ReadBytes(ctx, &m.MIC, 16)
}
}
return e.ReadAll(ctx)
}
type Filetime uint64
// TimeToFiletime function converts the time.Time to filetime.
func TimeToFiletime(t time.Time) Filetime {
if t.IsZero() {
return Filetime(0)
}
return Filetime(t.UnixNano()/100 + 116444736000000000)
}
// AsTime function returns the time.Time.
func (ft Filetime) AsTime() time.Time {
if ft == 0 {
return time.Time{}
}
// 100-nanosecond intervals since January 1, 1601
nsec := int64(ft)
// change starting time to the Epoch (00:00:00 UTC, January 1, 1970)
nsec -= 116444736000000000
// convert into nanoseconds
// nsec *= 100
return time.Unix(nsec/10000000, nsec%10000000)
}