tacplus icon indicating copy to clipboard operation
tacplus copied to clipboard

Cisco decryption error

Open thomseddon opened this issue 8 years ago • 5 comments

Hi,

Thanks for the library, it seems to be the only golang tacas+ and the code is really readable!

I've been attempting to use the library to build a server to interact with a cisco client, however the cisco client seems to immediately disconnect due to a supposed encryption error. With the minimal server shown at the end, I get the following log from Cisco:


002634: .Jan  9 17:26:37.650: TPLUS: Queuing AAA Authentication request 0 for processing
002635: .Jan  9 17:26:37.650: TPLUS: processing authentication start request id 0
002636: .Jan  9 17:26:37.650: TPLUS: Authentication start packet created for 0(thom)
002637: .Jan  9 17:26:37.650: TPLUS: Using server 172.22.252.10
002638: .Jan  9 17:26:37.650: TPLUS(00000000)/0/NB_WAIT/389E5FC: Started 5 sec timeout
002639: .Jan  9 17:26:37.667: TPLUS(00000000)/0/NB_WAIT: socket event 2
002640: .Jan  9 17:26:37.667: T+: Version 192 (0xC0), type 1, seq 1, encryption 1
002641: .Jan  9 17:26:37.667: T+: session_id 1046329656 (0x3E5DB938), dlen 12 (0xC)
002642: .Jan  9 17:26:37.667: T+: type:AUTHEN/START, priv_lvl:1 action:LOGIN ascii
002643: .Jan  9 17:26:37.667: T+: svc:LOGIN user_len:4 port_len:0 (0x0) raddr_len:0 (0x0) data_len:0
002644: .Jan  9 17:26:37.667: T+: user:  thom
002645: .Jan  9 17:26:37.667: T+: port:  
002646: .Jan  9 17:26:37.667: T+: rem_addr:  
002647: .Jan  9 17:26:37.667: T+: data:  
002648: .Jan  9 17:26:37.667: T+: End Packet
002649: .Jan  9 17:26:37.667: TPLUS(00000000)/0/NB_WAIT: wrote entire 24 bytes request
002650: .Jan  9 17:26:37.667: TPLUS(00000000)/0/READ: socket event 1
002651: .Jan  9 17:26:37.667: TPLUS(00000000)/0/READ: Would block while reading
002652: .Jan  9 17:26:37.683: TPLUS(00000000)/0/READ: socket event 1
002653: .Jan  9 17:26:37.683: TPLUS(00000000)/0/READ: read entire 12 header bytes (expect 6 bytes data)
002654: .Jan  9 17:26:37.683: TPLUS(00000000)/0/READ: socket event 1
002655: .Jan  9 17:26:37.683: TPLUS(00000000)/0/READ: read entire 18 bytes response
002656: .Jan  9 17:26:37.683: TPLUS(00000000): Decryption failed for AAA request
002657: .Jan  9 17:26:37.683: TPLUS(00000000)/0/389E5FC: Processing the reply packet
002658: .Jan  9 17:26:37.683: TPLUS: received bad AUTHEN packet: length = 6, expected 41810
002659: .Jan  9 17:26:37.683: TPLUS: Invalid AUTHEN packet (check keys).
002660: .Jan  9 17:26:37.683: TPLUS(00000000)/0/REQ_WAIT/389E5FC: timed out
002661: .Jan  9 17:26:37.683: TPLUS: Authentication start packet created for 0(thom)
002662: .Jan  9 17:26:37.683: TPLUS(00000000)/0/REQ_WAIT/389E5FC: timed out, clean up
002663: .Jan  9 17:26:37.683: TPLUS(00000000)/0/389E5FC: Processing the reply packet

The key is "secret" on both sides!

Wondering if you had any ideas or pointers on why this might be happening?

Thanks.

Minimal server:

package main

import (
  "os"
  "fmt"
  "net"
  "context"
  "syscall"
  "os/signal"
  "github.com/nwaples/tacplus"
)

type tacasHandler struct{}

func (t tacasHandler) HandleAuthenStart(ctx context.Context, a *tacplus.AuthenStart, s *tacplus.ServerSession) *tacplus.AuthenReply {
  fmt.Printf("HandleAuthenStart\n")

  user := a.User
  for user == "" {
    fmt.Printf("Promting for user...\n")
    c, err := s.GetUser(context.Background(), "Username:")
    if err != nil || c.Abort {
      return nil
    }
    user = c.Message
  }

  fmt.Printf("Packet %v\n", a)

  pass := ""
  for pass == "" {
    fmt.Printf("Promting for password...\n")
    c, err := s.GetPass(context.Background(), "Password:")
    if err != nil || c.Abort {
      fmt.Printf("Err during GetPass: %v\n", err)
      return nil
    }
    pass = c.Message
  }

  fmt.Printf("Got: %s:%s", user, pass)
  fmt.Printf("Replying...\n")

  return &tacplus.AuthenReply{Status: tacplus.AuthenStatusFail}
}

func (t tacasHandler) HandleAuthorRequest(ctx context.Context, a *tacplus.AuthorRequest) *tacplus.AuthorResponse {
  fmt.Printf("HandleAuthorRequest\n")

  return &tacplus.AuthorResponse{Status: tacplus.AuthorStatusFail}
}

func (t tacasHandler) HandleAcctRequest(ctx context.Context, a *tacplus.AcctRequest) *tacplus.AcctReply {
  fmt.Printf("HandleAcctRequest\n")

  return &tacplus.AcctReply{Status: tacplus.AcctStatusSuccess}
}


func main() {
  sock, err := net.Listen("tcp", "0.0.0.0:5549")
  if err != nil {
    fmt.Printf("Can't listen...\n")
    return
  }

  handler := tacplus.ServerConnHandler{
    Handler: &tacasHandler{},
    ConnConfig: tacplus.ConnConfig{
      Secret: []byte("secret"),
      Mux: true,
    },
  }
  server := &tacplus.Server{
    ServeConn: func (nc net.Conn) {
      handler.Serve(nc)
    },
  }

  // Listen & Serve
  signalChan := make(chan os.Signal, 1)
  signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)
  errChan := make(chan error)

  go func() {
    fmt.Println("Waiting for request...")
    err := server.Serve(sock)
    if err != nil {
      errChan <- err
    }
  }()

  select {
  case <-signalChan:
    fmt.Println("Stopping server")
    sock.Close()
  case err := <- errChan:
    fmt.Println("[error] %v", err.Error())
  }
}

thomseddon avatar Jan 09 '17 17:01 thomseddon

Unfortunately I don't have access to cisco devices anymore, so most of the testing has been with itself.

What log output does your server produce? I was able to run a windows command line tacacs client against your server program successfully.

The only other thing is how do you define the key in the cisco config? Cisco docs mention not to include quotation marks unless they are part of the key.

nwaples avatar Jan 09 '17 23:01 nwaples

Ah, no worries.

The output from the above server is:

Waiting for request...
HandleAuthenStart
Packet &{1 1 1 1 thom   []}
Promting for password...
Err during GetPass: session closed

I put some debugging in the library it's self, the error is first returned here, and comes after it has written the password reply and as soon as it tries to read the clients reply packet from the socket

Cisco definition is like so:

tacacs server test
 address ipv4 172.22.252.10
 key secret
 port 5549

No doubt it's some Cisco oddity, I did do a quick read over C tac_plus server but couldn't see any specific behaviour to handle Cisco, will have another crack later in the week

thomseddon avatar Jan 10 '17 09:01 thomseddon

Maybe, maybe not. It was able to receive and decrypt the initial authstart packet correctly. But according to the cisco logs, the received auth reply packet body length was only 6 bytes. It should have been 15 (6 + 9 for 'Password:') even with an incorrect secret as the packet headers aren't encrypted. There is the chance that there may have been some other bad packets, but an error should have been logged in that case, and auth error packets should be longer as they include the error message.

Having logs of raw packets read and written might at least rule in/out whether it is sending back bad data. If (in conn.go) you could add a log.Println("Write packet", req.p) before c.nc.Write in writeLoop(), and log.Println("Read packet", p) after c.readPacket() in readLoop(). If it is sending back an 18 (12+6) byte packet then it is wrong.

Only other things I can think of would be an endian or race problem. Have you tried building with -race option? What go version are you using? (>= 1.7?)

nwaples avatar Jan 10 '17 13:01 nwaples

I had the same issue with Cisco devices, my current fix is to disable the mux option in the ConnConfig.

th3p14gu3 avatar Oct 26 '17 14:10 th3p14gu3

Did you have single-connect enabled on your devices?

I suspect its due to the single connect header flag always being set when mux option is set, even if the client doesn't set it. I guess thats why disabling mux works. The tacacs+ draft does say the server may set this flag even if the client doesn't which is what I do. Ive changed it to only set it if the client sets it, to see if that helps.

I came across mention of Legacy Single Connect vs Draft Single Connect. From what I can tell legacy does not use the single connect header flag, which would require another config option for legacy single-connect (mux) devices.

nwaples avatar Oct 28 '17 10:10 nwaples