Browse Source

Forked ntlm auth package, disabled 'print' calls

Eugene Fryntov 10 years ago
parent
commit
d786ad0575
2 changed files with 282 additions and 1 deletions
  1. 1 1
      psiphon/upstreamproxy/auth_ntlm.go
  2. 281 0
      psiphon/upstreamproxy/ntlm/ntlm.go

+ 1 - 1
psiphon/upstreamproxy/auth_ntlm.go

@@ -3,7 +3,7 @@ package upstreamproxy
 import (
 	"encoding/base64"
 	"errors"
-	"github.com/jmckaskill/gontlm"
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/upstreamproxy/ntlm"
 	"net/http"
 	"strings"
 )

+ 281 - 0
psiphon/upstreamproxy/ntlm/ntlm.go

@@ -0,0 +1,281 @@
+/*
+This project is licensed under the MIT license as stated below:
+
+Copyright (C) 2013 James McKaskill
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+package ntlm
+
+import (
+	"code.google.com/p/go.crypto/md4"
+	"crypto/des"
+	"encoding/binary"
+	"errors"
+	"strings"
+	"unicode/utf16"
+)
+
+func append16(v []byte, val uint16) []byte {
+	return append(v, byte(val), byte(val>>8))
+}
+
+func append32(v []byte, val uint16) []byte {
+	return append(v, byte(val), byte(val>>8), byte(val>>16), byte(val>>24))
+}
+
+func consume16(v []byte) (uint16, []byte) {
+	if len(v) < 2 {
+		panic(ErrProtocol)
+	}
+	return uint16(v[0]) | uint16(v[1])<<8, v[2:]
+}
+
+func consume32(v []byte) (uint32, []byte) {
+	if len(v) < 4 {
+		panic(ErrProtocol)
+	}
+	return uint32(v[0]) | uint32(v[1])<<8 | uint32(v[2])<<16 | uint32(v[3])<<24, v[4:]
+}
+
+func consume(v []byte, n int) ([]byte, []byte) {
+	if n < 0 || len(v) < n {
+		panic(ErrProtocol)
+	}
+	return v[:n], v[n:]
+}
+
+var put32 = binary.LittleEndian.PutUint32
+var put16 = binary.LittleEndian.PutUint16
+
+const (
+	negotiateUnicode    = 0x0001 // Text strings are in unicode
+	negotiateOEM        = 0x0002 // Text strings are in OEM
+	requestTarget       = 0x0004 // Server return its auth realm
+	negotiateSign       = 0x0010 // Request signature capability
+	negotiateSeal       = 0x0020 // Request confidentiality
+	negotiateLMKey      = 0x0080 // Generate session key
+	negotiateNTLM       = 0x0200 // NTLM authentication
+	negotiateLocalCall  = 0x4000 // client/server on same machine
+	negotiateAlwaysSign = 0x8000 // Sign for all security levels
+)
+
+var (
+	ErrProtocol = errors.New("ntlm: protocol error")
+)
+
+func Negotiate() []byte {
+	var ret []byte
+	flags := negotiateAlwaysSign | negotiateNTLM | requestTarget | negotiateOEM
+
+	ret = append(ret, "NTLMSSP\x00"...) // protocol
+	ret = append32(ret, 1)              // type
+	ret = append32(ret, uint16(flags))  // flags
+	ret = append16(ret, 0)              // NT domain name length
+	ret = append16(ret, 0)              // NT domain name max length
+	ret = append32(ret, 0)              // NT domain name offset
+	ret = append16(ret, 0)              // local workstation name length
+	ret = append16(ret, 0)              // local workstation name max length
+	ret = append32(ret, 0)              // local workstation name offset
+	ret = append16(ret, 0)              // unknown name length
+	ret = append16(ret, 0)              // ...
+	ret = append16(ret, 0x30)           // unknown offset
+	ret = append16(ret, 0)              // unknown name length
+	ret = append16(ret, 0)              // ...
+	ret = append16(ret, 0x30)           // unknown offset
+
+	return ret
+}
+
+func fromUTF16LE(d []byte) string {
+	u16 := make([]uint16, len(d)/2)
+	for i := 0; i < len(d); i += 2 {
+		u16 = append(u16, uint16(d[0])|uint16(d[1])<<8)
+	}
+
+	return string(utf16.Decode(u16))
+}
+
+func appendUTF16LE(v []byte, val string) []byte {
+	for _, r := range val {
+		if utf16.IsSurrogate(r) {
+			r1, r2 := utf16.EncodeRune(r)
+			v = append16(v, uint16(r1))
+			v = append16(v, uint16(r2))
+		} else {
+			v = append16(v, uint16(r))
+		}
+	}
+	return v
+}
+
+func des56To64(dst, src []byte) {
+	dst[0] = src[0]
+	dst[1] = (src[1] >> 1) | (src[0] << 7)
+	dst[2] = (src[2] >> 2) | (src[1] << 6)
+	dst[3] = (src[3] >> 3) | (src[2] << 5)
+	dst[4] = (src[4] >> 4) | (src[3] << 4)
+	dst[5] = (src[5] >> 5) | (src[4] << 3)
+	dst[6] = (src[6] >> 6) | (src[5] << 2)
+	dst[7] = src[6] << 1
+
+	// fix parity
+	for i := 0; i < 8; i++ {
+		c := 0
+		for bit := uint(0); bit < 8; bit++ {
+			if (dst[i] & (1 << bit)) != 0 {
+				c++
+			}
+		}
+		if (c & 1) == 0 {
+			dst[i] ^= 1
+		}
+	}
+}
+
+func calcNTLMResponse(nonce [8]byte, hash [21]byte) [24]byte {
+	var ret [24]byte
+	var key [24]byte
+
+	des56To64(key[:8], hash[:7])
+	des56To64(key[8:16], hash[7:14])
+	des56To64(key[16:], hash[14:])
+
+	blk, _ := des.NewCipher(key[:8])
+	blk.Encrypt(ret[:8], nonce[:])
+
+	blk, _ = des.NewCipher(key[8:16])
+	blk.Encrypt(ret[8:16], nonce[:])
+
+	blk, _ = des.NewCipher(key[16:])
+	blk.Encrypt(ret[16:], nonce[:])
+
+	return ret
+}
+
+func calcLanManResponse(nonce [8]byte, password string) [24]byte {
+	var lmpass [14]byte
+	var key [16]byte
+	var hash [21]byte
+
+	copy(lmpass[:14], []byte(strings.ToUpper(password)))
+
+	des56To64(key[:8], lmpass[:7])
+	des56To64(key[8:], lmpass[7:])
+
+	blk, _ := des.NewCipher(key[:8])
+	blk.Encrypt(hash[:8], []byte("KGS!@#$%"))
+
+	blk, _ = des.NewCipher(key[8:])
+	blk.Encrypt(hash[8:], []byte("KGS!@#$%"))
+
+	return calcNTLMResponse(nonce, hash)
+}
+
+func calcNTResponse(nonce [8]byte, password string) [24]byte {
+	var hash [21]byte
+	h := md4.New()
+	h.Write(appendUTF16LE(nil, password))
+	h.Sum(hash[:0])
+	return calcNTLMResponse(nonce, hash)
+}
+
+const (
+	dataWINSName    = 1
+	dataNTDomain    = 2
+	dataDNSName     = 3
+	dataWin2KDomain = 4
+)
+
+func Authenticate(chlg []byte, domain, user, password string) (v []byte, err error) {
+	defer func() {
+		if v := recover(); v != nil {
+			err, _ = v.(error)
+		}
+	}()
+
+	proto, chlg := consume(chlg, len("NTLMSSP\x00"))
+	if string(proto) != "NTLMSSP\x00" {
+		return nil, ErrProtocol
+	}
+
+	domain16 := appendUTF16LE(nil, domain)
+	user16 := appendUTF16LE(nil, user)
+
+	typ, chlg := consume32(chlg)       // Type 2
+	domainLen, chlg := consume16(chlg) // NT domain name length
+	_, chlg = consume16(chlg)          // NT domain name max length
+	_, chlg = consume32(chlg)          // NT domain name offset
+	_, chlg = consume32(chlg)          // flags
+	nonce, chlg := consume(chlg, 8)    // nonce
+	_, chlg = consume(chlg, 8)         // zero
+	dataLen, chlg := consume16(chlg)   // length of data following domain
+	_, chlg = consume16(chlg)          // max length of data following domain
+	_, chlg = consume32(chlg)          // offset of data following domain
+
+	_, chlg = consume(chlg, int(domainLen)) // server domain
+	alldata, chlg := consume(chlg, int(dataLen))
+
+	if typ != 2 {
+		return nil, ErrProtocol
+	}
+
+	for len(alldata) > 0 {
+		_, alldata := consume16(alldata) // type of this data item
+		length, alldata := consume16(alldata)
+		_, alldata = consume(alldata, int(length))
+	}
+
+	var noncev [8]byte
+	copy(noncev[:], nonce)
+
+	lanman := calcLanManResponse(noncev, password)
+	nt := calcNTResponse(noncev, password)
+
+	auth := make([]byte, 48)
+	copy(auth, []byte("NTLMSSP\x00"))
+	put32(auth[8:], 3) // type
+
+	put16(auth[12:], uint16(len(lanman)))                              // LanManager response length
+	put16(auth[14:], uint16(len(lanman)))                              // LanManager response max length
+	put32(auth[16:], uint32(48+len(domain16)+len(user16)))             // LanManager response offset
+	put16(auth[20:], uint16(len(nt)))                                  // NT response length
+	put16(auth[22:], uint16(len(nt)))                                  // NT response max length
+	put32(auth[24:], uint32(48+len(domain16)+len(user16)+len(lanman))) // NT repsonse offset
+	put16(auth[28:], uint16(len(domain16)))                            // username NT domain length
+	put16(auth[30:], uint16(len(domain16)))                            // username NT domain max length
+	put32(auth[32:], 48)                                               // username NT domain offset
+	put16(auth[36:], uint16(len(user16)))                              // username length
+	put16(auth[38:], uint16(len(user16)))                              // username max length
+	put32(auth[40:], uint32(48+len(domain16)))                         // username offset
+	put16(auth[44:], 0)                                                // local workstation name length
+	put16(auth[46:], 0)                                                // local workstation name max length
+	put32(auth[48:], 0)                                                // local workstation name offset
+	put16(auth[52:], 0)                                                // session key length
+	put16(auth[54:], 0)                                                // session key max length
+	put32(auth[56:], 0)                                                // session key offset
+	put32(auth[60:], 0x8201)                                           // flags
+
+	auth = append(auth, domain16...)
+	auth = append(auth, user16...)
+	auth = append(auth, lanman[:]...)
+	auth = append(auth, nt[:]...)
+
+	return auth, nil
+}