Skip to content

Commit

Permalink
bnet: Add support for SHA1 pw auth (used in old PvPGN servers)
Browse files Browse the repository at this point in the history
  • Loading branch information
nielsAD committed Mar 25, 2019
1 parent d3a17c2 commit e6ecf3b
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 40 deletions.
14 changes: 14 additions & 0 deletions cmd/bncsclient/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,15 @@ Usage
|`-roc` |`string`|ROC CD-key|
|`-tft` |`string`|TFT CD-key|
|`-verify` |`bool` |Verify server signature|
|`-sha1` |`bool` |SHA1 password authentication (used in old PvPGN servers)|
|`-create` |`bool` |Create account|
|`-changepass`|`bool` |Change password|

Example
-------

Loading version and CD-key info from the default Warcraft III installation directory:

```bash
➜ ./bncsclient -u niels europe.battle.net
Enter password:
Expand All @@ -42,6 +45,17 @@ Enter password:
12:00:05 [INFO] No one hears you.
```

Preset version and CD-key info, SHA1 (PvPGN) password authentication:

```bash
➜ ./bncsclient -sha1 -u niels -roc FFFFFFFFFFFFFFFFFFFFFFFFFF -tft FFFFFFFFFFFFFFFFFFFFFFFFFF -ev 0x011b01ad -eh 0xaaaba048 rubattle.net
12:00:00 Succesfully logged onto niels@rubattle.net:6112
12:00:00 Joined channel 'Warcraft III RUS-1'
12:00:00 niels has joined the channel (ping: 41ms)
12:00:00 [INFO] Obey the law, rules are the law!
12:00:00 [INFO] Hello niels, welcome to Rubattle.net!
```

Download
--------

Expand Down
11 changes: 7 additions & 4 deletions cmd/bncsclient/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ var (
password = flag.String("p", "", "Password")
newpassword = flag.String("np", "", "New password")
verify = flag.Bool("verify", false, "Verify server signature")
sha1 = flag.Bool("sha1", false, "SHA1 password authentication (used in old PvPGN servers)")
create = flag.Bool("create", false, "Create account")
changepass = flag.Bool("changepass", false, "Change password")
)
Expand All @@ -46,10 +47,12 @@ func main() {
flag.Parse()

c, err := bnet.NewClient(&bnet.Config{
BinPath: *binpath,
ExeInfo: *exeinfo,
ExeVersion: uint32(*exevers),
ExeHash: uint32(*exehash),
BinPath: *binpath,
ExeInfo: *exeinfo,
ExeVersion: uint32(*exevers),
ExeHash: uint32(*exehash),
VerifySignature: *verify,
SHA1Auth: *sha1,
})
if err != nil {
logErr.Fatal("NewClient error: ", err)
Expand Down
62 changes: 55 additions & 7 deletions network/bnet/bncsutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,18 +88,27 @@ func CreateBNCSKeyInfo(cdkey string, clientToken uint32, serverToken uint32) (*b
}, nil
}

// VerifyNLSSignature received in SID_AUTH_INFO (0x50)
func VerifyNLSSignature(ip net.IP, sig *[128]byte) bool {
// VerifyServerSignature received in SID_AUTH_INFO (0x50)
func VerifyServerSignature(ip net.IP, sig *[128]byte) bool {
var aton = binary.LittleEndian.Uint32(ip.To4())
return C.nls_check_signature(C.uint32_t(aton), (*C.char)(unsafe.Pointer(&sig[0]))) != 0
}

// NLS password hashing helper
// SRP password helper
type SRP interface {
AccountCreate() ([]byte, []byte, error)
ClientKey() [32]byte
PasswordProof(serverKey *[32]byte, salt *[32]byte) [20]byte
VerifyPassword(proof *[20]byte) bool
Free()
}

// NLS provider for SRP
type NLS struct {
n *C.nls_t
}

// NewNLS initializes a new NLS struct
// NewNLS initializes a new NLS provider for SRP
func NewNLS(username string, password string) (*NLS, error) {
var cstru = C.CString(username)
defer C.free(unsafe.Pointer(cstru))
Expand Down Expand Up @@ -133,19 +142,58 @@ func (n *NLS) AccountCreate() ([]byte, []byte, error) {
return buf[0:32], buf[32:64], nil
}

// ClientKey for NLS exchange
// ClientKey for SRP exchange
func (n *NLS) ClientKey() (res [32]byte) {
C.nls_get_A(n.n, (*C.char)(unsafe.Pointer(&res[0])))
return res
}

// PasswordProof for NLS exchange
// PasswordProof for SRP exchange
func (n *NLS) PasswordProof(serverKey *[32]byte, salt *[32]byte) (res [20]byte) {
C.nls_get_M1(n.n, (*C.char)(unsafe.Pointer(&res[0])), (*C.char)(unsafe.Pointer(&serverKey[0])), (*C.char)(unsafe.Pointer(&salt[0])))
return res
}

// VerifyPassword after NLS exchange
// VerifyPassword after SRP exchange
func (n *NLS) VerifyPassword(proof *[20]byte) bool {
return C.nls_check_M2(n.n, (*C.char)(unsafe.Pointer(&proof[0])), nil, nil) != 0
}

// SHA1 provider for SRP
type SHA1 struct {
password string
}

// NewSHA1 initializes a new SHA1 provider for SRP
func NewSHA1(password string) *SHA1 {
return &SHA1{
password: password,
}
}

// Free SHA1 struct
func (p *SHA1) Free() {}

// AccountCreate generates the content for an SID_AUTH_ACCOUNTCREATE packet
func (p *SHA1) AccountCreate() ([]byte, []byte, error) {
return nil, []byte(p.password), nil
}

// ClientKey for SRP exchange
func (p *SHA1) ClientKey() (res [32]byte) {
return res
}

// PasswordProof for SRP exchange
func (p *SHA1) PasswordProof(serverKey *[32]byte, salt *[32]byte) (res [20]byte) {
var cstrp = C.CString(p.password)
defer C.free(unsafe.Pointer(cstrp))

C.hashPassword(cstrp, (*C.char)(unsafe.Pointer(&res[0])))
return res
}

// VerifyPassword after SRP exchange
func (p *SHA1) VerifyPassword(proof *[20]byte) bool {
return true
}
64 changes: 36 additions & 28 deletions network/bnet/bnet.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ type Config struct {
ExeVersion uint32
ExeHash uint32
VerifySignature bool
SHA1Auth bool
Username string
Password string
CDKeyOwner string
Expand Down Expand Up @@ -237,7 +238,7 @@ func (b *Client) Dial() (*network.BNCSConn, error) {
return nil, err
}

if b.VerifySignature && !VerifyNLSSignature(conn.RemoteAddr().(*net.TCPAddr).IP, &authInfo.ServerSignature) {
if b.VerifySignature && !VerifyServerSignature(conn.RemoteAddr().(*net.TCPAddr).IP, &authInfo.ServerSignature) {
bncsconn.Close()
return nil, ErrInvalidServerSig
}
Expand All @@ -261,7 +262,7 @@ func (b *Client) Dial() (*network.BNCSConn, error) {
//
// Logon sequence:
// 1. Client starts with Dial sequence ([0x50] SID_AUTH_INFO and [0x51] SID_AUTH_CHECK)
// 2. Client waits for user to enter account information (standard logon shown, uses NLS):
// 2. Client waits for user to enter account information (standard logon shown, uses SRP):
// 1. C > S [0x53] SID_AUTH_ACCOUNTLOGON
// 2. S > C [0x53] SID_AUTH_ACCOUNTLOGON
// 3. C > S [0x54] SID_AUTH_ACCOUNTLOGONPROOF
Expand All @@ -279,19 +280,19 @@ func (b *Client) Dial() (*network.BNCSConn, error) {
// 13. A sequence of chat events for entering chat follow.
//
func (b *Client) Logon() error {
nls, err := NewNLS(b.Username, b.Password)
srp, err := b.newSRP(b.Password)
if err != nil {
return err
}

defer nls.Free()
defer srp.Free()

bncsconn, err := b.Dial()
if err != nil {
return err
}

logon, err := b.sendLogon(bncsconn, nls)
logon, err := b.sendLogon(bncsconn, srp)
if err != nil {
bncsconn.Close()
return err
Expand All @@ -302,7 +303,7 @@ func (b *Client) Logon() error {
return LogonResultToError(logon.Result)
}

proof, err := b.sendLogonProof(bncsconn, nls, logon)
proof, err := b.sendLogonProof(bncsconn, srp, logon)
if err != nil {
bncsconn.Close()
return err
Expand All @@ -321,7 +322,7 @@ func (b *Client) Logon() error {
return LogonProofResultToError(proof.Result)
}

if !nls.VerifyPassword(&proof.ServerPasswordProof) {
if !srp.VerifyPassword(&proof.ServerPasswordProof) {
bncsconn.Close()
return ErrPasswordVerification
}
Expand Down Expand Up @@ -352,12 +353,12 @@ func (b *Client) Logon() error {
// 3. Client can continue with logon ([0x53] SID_AUTH_ACCOUNTLOGON)
//
func (b *Client) CreateAccount() error {
nls, err := NewNLS(b.Username, b.Password)
srp, err := b.newSRP(b.Password)
if err != nil {
return err
}

defer nls.Free()
defer srp.Free()

bncsconn, err := b.Dial()
if err != nil {
Expand All @@ -366,7 +367,7 @@ func (b *Client) CreateAccount() error {

defer bncsconn.Close()

create, err := b.sendCreateAccount(bncsconn, nls)
create, err := b.sendCreateAccount(bncsconn, srp)
if err != nil {
return err
}
Expand All @@ -390,19 +391,19 @@ func (b *Client) CreateAccount() error {
// 3. Client can continue with logon ([0x53] SID_AUTH_ACCOUNTLOGON)
//
func (b *Client) ChangePassword(newPassword string) error {
oldNLS, err := NewNLS(b.Username, b.Password)
oldSRP, err := b.newSRP(b.Password)
if err != nil {
return err
}

defer oldNLS.Free()
defer oldSRP.Free()

newNLS, err := NewNLS(b.Username, newPassword)
newSRP, err := b.newSRP(newPassword)
if err != nil {
return err
}

defer newNLS.Free()
defer newSRP.Free()

bncsconn, err := b.Dial()
if err != nil {
Expand All @@ -411,7 +412,7 @@ func (b *Client) ChangePassword(newPassword string) error {

defer bncsconn.Close()

resp, err := b.sendChangePass(bncsconn, oldNLS)
resp, err := b.sendChangePass(bncsconn, oldSRP)
if err != nil {
return err
}
Expand All @@ -420,7 +421,7 @@ func (b *Client) ChangePassword(newPassword string) error {
return LogonResultToError(resp.Result)
}

proof, err := b.sendChangePassProof(bncsconn, oldNLS, newNLS, resp)
proof, err := b.sendChangePassProof(bncsconn, oldSRP, newSRP, resp)
if err != nil {
return err
}
Expand All @@ -429,14 +430,21 @@ func (b *Client) ChangePassword(newPassword string) error {
return LogonProofResultToError(proof.Result)
}

if !oldNLS.VerifyPassword(&proof.ServerPasswordProof) {
if !oldSRP.VerifyPassword(&proof.ServerPasswordProof) {
return ErrPasswordVerification
}

b.Password = newPassword
return nil
}

func (b *Client) newSRP(password string) (SRP, error) {
if b.SHA1Auth {
return NewSHA1(password), nil
}
return NewNLS(b.Username, password)
}

func (b *Client) sendAuthInfo(conn *network.BNCSConn) (*bncs.AuthInfoResp, error) {
if _, err := conn.Send(&b.Platform); err != nil {
return nil, err
Expand Down Expand Up @@ -551,9 +559,9 @@ func (b *Client) sendAuthCheck(conn *network.BNCSConn, clientToken uint32, authi
}
}

func (b *Client) sendLogon(conn *network.BNCSConn, nls *NLS) (*bncs.AuthAccountLogonResp, error) {
func (b *Client) sendLogon(conn *network.BNCSConn, srp SRP) (*bncs.AuthAccountLogonResp, error) {
var req = &bncs.AuthAccountLogonReq{
ClientKey: nls.ClientKey(),
ClientKey: srp.ClientKey(),
Username: b.Username,
}

Expand All @@ -573,9 +581,9 @@ func (b *Client) sendLogon(conn *network.BNCSConn, nls *NLS) (*bncs.AuthAccountL
}
}

func (b *Client) sendLogonProof(conn *network.BNCSConn, nls *NLS, logon *bncs.AuthAccountLogonResp) (*bncs.AuthAccountLogonProofResp, error) {
func (b *Client) sendLogonProof(conn *network.BNCSConn, srp SRP, logon *bncs.AuthAccountLogonResp) (*bncs.AuthAccountLogonProofResp, error) {
var req = &bncs.AuthAccountLogonProofReq{
ClientPasswordProof: nls.PasswordProof(&logon.ServerKey, &logon.Salt),
ClientPasswordProof: srp.PasswordProof(&logon.ServerKey, &logon.Salt),
}

if _, err := conn.Send(req); err != nil {
Expand All @@ -594,8 +602,8 @@ func (b *Client) sendLogonProof(conn *network.BNCSConn, nls *NLS, logon *bncs.Au
}
}

func (b *Client) sendCreateAccount(conn *network.BNCSConn, nls *NLS) (*bncs.AuthAccountCreateResp, error) {
salt, verifier, err := nls.AccountCreate()
func (b *Client) sendCreateAccount(conn *network.BNCSConn, srp SRP) (*bncs.AuthAccountCreateResp, error) {
salt, verifier, err := srp.AccountCreate()
if err != nil {
return nil, err
}
Expand All @@ -620,10 +628,10 @@ func (b *Client) sendCreateAccount(conn *network.BNCSConn, nls *NLS) (*bncs.Auth
}
}

func (b *Client) sendChangePass(conn *network.BNCSConn, nls *NLS) (*bncs.AuthAccountChangePassResp, error) {
func (b *Client) sendChangePass(conn *network.BNCSConn, srp SRP) (*bncs.AuthAccountChangePassResp, error) {
var req = &bncs.AuthAccountChangePassReq{
AuthAccountLogonReq: bncs.AuthAccountLogonReq{
ClientKey: nls.ClientKey(),
ClientKey: srp.ClientKey(),
Username: b.Username,
},
}
Expand All @@ -644,14 +652,14 @@ func (b *Client) sendChangePass(conn *network.BNCSConn, nls *NLS) (*bncs.AuthAcc
}
}

func (b *Client) sendChangePassProof(conn *network.BNCSConn, oldNLS *NLS, newNLS *NLS, resp *bncs.AuthAccountChangePassResp) (*bncs.AuthAccountChangePassProofResp, error) {
salt, verifier, err := newNLS.AccountCreate()
func (b *Client) sendChangePassProof(conn *network.BNCSConn, oldSRP SRP, newSRP SRP, resp *bncs.AuthAccountChangePassResp) (*bncs.AuthAccountChangePassProofResp, error) {
salt, verifier, err := newSRP.AccountCreate()
if err != nil {
return nil, err
}

var req = &bncs.AuthAccountChangePassProofReq{
ClientPasswordProof: oldNLS.PasswordProof(&resp.ServerKey, &resp.Salt),
ClientPasswordProof: oldSRP.PasswordProof(&resp.ServerKey, &resp.Salt),
}
copy(req.NewSalt[:], salt)
copy(req.NewVerifier[:], verifier)
Expand Down
7 changes: 6 additions & 1 deletion protocol/bncs/packets.go
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,11 @@ func (pkt *ChatEvent) Serialize(buf *protocol.Buffer) error {
return nil
}

func baadf00d(n uint32) bool {
// PvPGN quirk
return n == 0xbaadf00d || n == 0x0df0adba
}

// Deserialize decodes the binary data generated by Serialize.
func (pkt *ChatEvent) Deserialize(buf *protocol.Buffer) error {
var size = readPacketSize(buf)
Expand All @@ -451,7 +456,7 @@ func (pkt *ChatEvent) Deserialize(buf *protocol.Buffer) error {
}
pkt.Ping = buf.ReadUInt32()

if buf.ReadUInt32() != 0 || buf.ReadUInt32() != 0xbaadf00d || buf.ReadUInt32() != 0xbaadf00d {
if buf.ReadUInt32() != 0 || !baadf00d(buf.ReadUInt32()) || !baadf00d(buf.ReadUInt32()) {
return ErrUnexpectedConst
}

Expand Down

0 comments on commit e6ecf3b

Please sign in to comment.