xsd: Added -aK,-aC,-aH to control accepted client proposals

This commit is contained in:
Russ Magee 2019-12-15 11:38:04 -08:00
parent 9b90c0558e
commit d4f50bfdc0
4 changed files with 199 additions and 20 deletions

View file

@ -34,9 +34,9 @@ import (
_ "net/http/pprof" _ "net/http/pprof"
xs "blitter.com/go/xs" xs "blitter.com/go/xs"
"blitter.com/go/xs/xsnet"
"blitter.com/go/xs/logger" "blitter.com/go/xs/logger"
"blitter.com/go/xs/spinsult" "blitter.com/go/xs/spinsult"
"blitter.com/go/xs/xsnet"
isatty "github.com/mattn/go-isatty" isatty "github.com/mattn/go-isatty"
) )
@ -824,20 +824,22 @@ func main() {
proto := "tcp" proto := "tcp"
if kcpMode != "unused" { if kcpMode != "unused" {
proto = "kcp" proto = "kcp"
} }
conn, err := xsnet.Dial(proto, server, cipherAlg, hmacAlg, kexAlg, kcpMode) conn, err := xsnet.Dial(proto, server, cipherAlg, hmacAlg, kexAlg, kcpMode)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
exitWithStatus(3) exitWithStatus(3)
} }
defer conn.Close() // nolint: errcheck
// From this point on, conn is a secure encrypted channel
// Set stdin in raw mode if it's an interactive session // Set stdin in raw mode if it's an interactive session
// TODO: send flag to server side indicating this // TODO: send flag to server side indicating this
// affects shell command used // affects shell command used
var oldState *xs.State var oldState *xs.State
defer conn.Close() // nolint: errcheck
// From this point on, conn is a secure encrypted channel
if shellMode { if shellMode {
if isatty.IsTerminal(os.Stdin.Fd()) { if isatty.IsTerminal(os.Stdin.Fd()) {
oldState, err = xs.MakeRaw(int(os.Stdin.Fd())) oldState, err = xs.MakeRaw(int(os.Stdin.Fd()))
@ -869,7 +871,11 @@ func main() {
rec := xs.NewSession(op, []byte(uname), []byte(remoteHost), []byte(os.Getenv("TERM")), []byte(cmdStr), []byte(authCookie), 0) rec := xs.NewSession(op, []byte(uname), []byte(remoteHost), []byte(os.Getenv("TERM")), []byte(cmdStr), []byte(authCookie), 0)
sendErr := sendSessionParams(&conn, rec) sendErr := sendSessionParams(&conn, rec)
if sendErr != nil { if sendErr != nil {
log.Fatal(sendErr) _ = xs.Restore(int(os.Stdin.Fd()), oldState) // nolint: errcheck,gosec
rec.SetStatus(254)
fmt.Fprintln(os.Stderr, "Error: server rejected secure proposal params") // nolint: errcheck
exitWithStatus(int(rec.Status()))
//log.Fatal(sendErr)
} }
//Security scrub //Security scrub
@ -924,13 +930,14 @@ func main() {
} }
if rec.Status() != 0 { if rec.Status() != 0 {
_ = xs.Restore(int(os.Stdin.Fd()), oldState) // nolint: errcheck,gosec _ = xs.Restore(int(os.Stdin.Fd()), oldState) // nolint: errcheck,gosec
fmt.Fprintln(os.Stderr, "Session exited with status:", rec.Status()) // nolint: errcheck fmt.Fprintln(os.Stderr, "Session exited with status:", rec.Status()) // nolint: errcheck
} }
} }
if oldState != nil { if oldState != nil {
_ = xs.Restore(int(os.Stdin.Fd()), oldState) // nolint: gosec _ = xs.Restore(int(os.Stdin.Fd()), oldState) // nolint: gosec
oldState = nil
} }
exitWithStatus(int(rec.Status())) exitWithStatus(int(rec.Status()))

View file

@ -23,14 +23,15 @@ import (
"os/signal" "os/signal"
"os/user" "os/user"
"path" "path"
"strings"
"sync" "sync"
"syscall" "syscall"
"unsafe" "unsafe"
"blitter.com/go/goutmp" "blitter.com/go/goutmp"
xs "blitter.com/go/xs" xs "blitter.com/go/xs"
"blitter.com/go/xs/xsnet"
"blitter.com/go/xs/logger" "blitter.com/go/xs/logger"
"blitter.com/go/xs/xsnet"
"github.com/kr/pty" "github.com/kr/pty"
) )
@ -429,9 +430,73 @@ func GenAuthToken(who string, connhost string) string {
return fmt.Sprintf("%s:%s", tokenA, hex.EncodeToString(tokenB)) return fmt.Sprintf("%s:%s", tokenA, hex.EncodeToString(tokenB))
} }
// Demo of a simple server that listens and spawns goroutines for each var (
// connecting client. Note this code is identical to standard tcp aKEXAlgs allowedKEXAlgs
// server code, save for declaring 'xsnet' rather than 'net' aCipherAlgs allowedCipherAlgs
aHMACAlgs allowedHMACAlgs
)
type allowedKEXAlgs []string // TODO
type allowedCipherAlgs []string // TODO
type allowedHMACAlgs []string // TODO
func (a allowedKEXAlgs) allowed(k xsnet.KEXAlg) bool {
for i := 0; i < len(a); i++ {
if a[i] == "KEX_all" || a[i] == k.String() {
return true
}
}
return false
}
func (a *allowedKEXAlgs) String() string {
return fmt.Sprintf("allowedKEXAlgs: %v", *a)
}
func (a *allowedKEXAlgs) Set(value string) error {
*a = append(*a, strings.TrimSpace(value))
return nil
}
func (a allowedCipherAlgs) allowed(c xsnet.CSCipherAlg) bool {
for i := 0; i < len(a); i++ {
if a[i] == "C_all" || a[i] == c.String() {
return true
}
}
return false
}
func (a *allowedCipherAlgs) String() string {
return fmt.Sprintf("allowedCipherAlgs: %v", *a)
}
func (a *allowedCipherAlgs) Set(value string) error {
*a = append(*a, strings.TrimSpace(value))
return nil
}
func (a allowedHMACAlgs) allowed(h xsnet.CSHmacAlg) bool {
for i := 0; i < len(a); i++ {
if a[i] == "H_all" || a[i] == h.String() {
return true
}
}
return false
}
func (a *allowedHMACAlgs) String() string {
return fmt.Sprintf("allowedHMACAlgs: %v", *a)
}
func (a *allowedHMACAlgs) Set(value string) error {
*a = append(*a, strings.TrimSpace(value))
return nil
}
// Main server that listens and spawns goroutines for each
// connecting client. Note this code is mostly identical to standard
// tcp server code, save for declaring 'xsnet' rather than 'net'
// Listener and Conns. The KEx and encrypt/decrypt is done within the type. // Listener and Conns. The KEx and encrypt/decrypt is done within the type.
// Compare to 'serverp.go' in this directory to see the equivalence. // Compare to 'serverp.go' in this directory to see the equivalence.
// TODO: reduce gocyclo // TODO: reduce gocyclo
@ -453,6 +518,11 @@ func main() {
flag.UintVar(&chaffFreqMax, "F", 5000, "chaff pkt freq max (msecs)") flag.UintVar(&chaffFreqMax, "F", 5000, "chaff pkt freq max (msecs)")
flag.UintVar(&chaffBytesMax, "B", 64, "chaff pkt size max (bytes)") flag.UintVar(&chaffBytesMax, "B", 64, "chaff pkt size max (bytes)")
flag.BoolVar(&dbg, "d", false, "debug logging") flag.BoolVar(&dbg, "d", false, "debug logging")
flag.Var(&aKEXAlgs, "aK", `List of allowed KEX algs (eg. 'KEXAlgA KEXAlgB ... KEXAlgN') (default allow all)`)
flag.Var(&aCipherAlgs, "aC", `List of allowed ciphers (eg. 'CipherAlgA CipherAlgB ... CipherAlgN') (default allow all)`)
flag.Var(&aHMACAlgs, "aH", `List of allowed HMACs (eg. 'HMACAlgA HMACAlgB ... HMACAlgN') (default allow all)`)
flag.Parse() flag.Parse()
if vopt { if vopt {
@ -486,6 +556,22 @@ func main() {
log.SetOutput(ioutil.Discard) log.SetOutput(ioutil.Discard)
} }
// Set up allowed algs, if specified (default allow all)
if len(aKEXAlgs) == 0 {
aKEXAlgs = []string{"KEX_all"}
}
logger.LogNotice(fmt.Sprintf("Allowed KEXAlgs: %v\n", aKEXAlgs)) // nolint: gosec,errcheck
if len(aCipherAlgs) == 0 {
aCipherAlgs = []string{"C_all"}
}
logger.LogNotice(fmt.Sprintf("Allowed CipherAlgs: %v\n", aCipherAlgs)) // nolint: gosec,errcheck
if len(aHMACAlgs) == 0 {
aHMACAlgs = []string{"H_all"}
}
logger.LogNotice(fmt.Sprintf("Allowed HMACAlgs: %v\n", aHMACAlgs)) // nolint: gosec,errcheck
// Set up handler for daemon signalling // Set up handler for daemon signalling
exitCh := make(chan os.Signal, 1) exitCh := make(chan os.Signal, 1)
signal.Notify(exitCh, os.Signal(syscall.SIGTERM), os.Signal(syscall.SIGINT), os.Signal(syscall.SIGHUP), os.Signal(syscall.SIGUSR1), os.Signal(syscall.SIGUSR2)) signal.Notify(exitCh, os.Signal(syscall.SIGTERM), os.Signal(syscall.SIGINT), os.Signal(syscall.SIGHUP), os.Signal(syscall.SIGUSR1), os.Signal(syscall.SIGUSR2))
@ -522,9 +608,22 @@ func main() {
log.Println("Serving on", laddr) log.Println("Serving on", laddr)
for { for {
// Wait for a connection. // Wait for a connection.
// Then check if client-proposed algs are allowed
conn, err := l.Accept() conn, err := l.Accept()
if err != nil { if err != nil {
log.Printf("Accept() got error(%v), hanging up.\n", err) log.Printf("Accept() got error(%v), hanging up.\n", err)
} else if !aKEXAlgs.allowed(conn.KEX()) {
log.Printf("Accept() rejected for banned KEX alg %d, hanging up.\n", conn.KEX())
conn.SetStatus(xsnet.CSEKEXAlgDenied)
conn.Close()
} else if !aCipherAlgs.allowed(conn.CAlg()) {
log.Printf("Accept() rejected for banned Cipher alg %d, hanging up.\n", conn.CAlg())
conn.SetStatus(xsnet.CSECipherAlgDenied)
conn.Close()
} else if !aHMACAlgs.allowed(conn.HAlg()) {
log.Printf("Accept() rejected for banned HMAC alg %d, hanging up.\n", conn.HAlg())
conn.SetStatus(xsnet.CSEHMACAlgDenied)
conn.Close()
} else { } else {
log.Println("Accepted client") log.Println("Accepted client")

View file

@ -29,6 +29,7 @@ const (
KEX_NEWHOPE_SIMPLE // 'NewHopeLP-Simple' - https://eprint.iacr.org/2016/1157 KEX_NEWHOPE_SIMPLE // 'NewHopeLP-Simple' - https://eprint.iacr.org/2016/1157
KEX_resvd14 KEX_resvd14
KEX_resvd15 KEX_resvd15
KEX_invalid = 255
) )
// Sent from client to server in order to specify which // Sent from client to server in order to specify which
@ -38,12 +39,15 @@ type KEXAlg uint8
// Extended exit status codes - indicate comm/pty issues // Extended exit status codes - indicate comm/pty issues
// rather than remote end normal UNIX exit codes // rather than remote end normal UNIX exit codes
const ( const (
CSENone = 1024 + iota CSENone = 1024 + iota
CSETruncCSO // No CSOExitStatus in payload CSETruncCSO // No CSOExitStatus in payload
CSEStillOpen // Channel closed unexpectedly CSEStillOpen // Channel closed unexpectedly
CSEExecFail // cmd.Start() (exec) failed CSEExecFail // cmd.Start() (exec) failed
CSEPtyExecFail // pty.Start() (exec w/pty) failed CSEPtyExecFail // pty.Start() (exec w/pty) failed
CSEPtyGetNameFail // failed to obtain pty name CSEPtyGetNameFail // failed to obtain pty name
CSEKEXAlgDenied // server rejected proposed KEX alg
CSECipherAlgDenied // server rejected proposed Cipher alg
CSEHMACAlgDenied // server rejected proposed HMAC alg
) )
// Extended (>255 UNIX exit status) codes // Extended (>255 UNIX exit status) codes

View file

@ -70,9 +70,10 @@ type (
// Conn is a connection wrapping net.Conn with KEX & session state // Conn is a connection wrapping net.Conn with KEX & session state
Conn struct { Conn struct {
kex KEXAlg // KEX/KEM proposal (client -> server) kex KEXAlg // KEX/KEM proposal (client -> server)
m *sync.Mutex // (internal)
c *net.Conn // which also implements io.Reader, io.Writer, ... m *sync.Mutex // (internal)
c *net.Conn // which also implements io.Reader, io.Writer, ...
logCipherText bool // somewhat expensive, for debugging logCipherText bool // somewhat expensive, for debugging
logPlainText bool // INSECURE and somewhat expensive, for debugging logPlainText bool // INSECURE and somewhat expensive, for debugging
@ -105,6 +106,67 @@ func (t *TunEndpoint) String() string {
return fmt.Sprintf("[%d:%s:%d]", t.Lport, t.Peer, t.Rport) return fmt.Sprintf("[%d:%s:%d]", t.Lport, t.Peer, t.Rport)
} }
func (k *KEXAlg) String() string {
switch *k {
case KEX_HERRADURA256:
return "KEX_HERRADURA256"
case KEX_HERRADURA512:
return "KEX_HERRADURA512"
case KEX_HERRADURA1024:
return "KEX_HERRADURA1024"
case KEX_HERRADURA2048:
return "KEX_HERRADURA2048"
case KEX_KYBER512:
return "KEX_KYBER512"
case KEX_KYBER768:
return "KEX_KYBER768"
case KEX_KYBER1024:
return "KEX_KYBER1024"
case KEX_NEWHOPE:
return "KEX_NEWHOPE"
case KEX_NEWHOPE_SIMPLE:
return "KEX_NEWHOPE_SIMPLE"
default:
return "KEX_ERR_UNK"
}
}
func (hc *Conn) CAlg() CSCipherAlg {
return CSCipherAlg(hc.cipheropts & 0x0FF)
}
func (c *CSCipherAlg) String() string {
switch *c & 0x0FF {
case CAlgAES256:
return "C_AES_256"
case CAlgTwofish128:
return "C_TWOFISH_128"
case CAlgBlowfish64:
return "C_BLOWFISH_64"
case CAlgCryptMT1:
return "C_CRYPTMT1"
case CAlgWanderer:
return "C_WANDERER"
default:
return "C_ERR_UNK"
}
}
func (hc *Conn) HAlg() CSHmacAlg {
return CSHmacAlg((hc.cipheropts >> 8) & 0x0FF)
}
func (h *CSHmacAlg) String() string {
switch (*h >> 8) & 0x0FF {
case HmacSHA256:
return "H_SHA256"
case HmacSHA512:
return "C_SHA512"
default:
return "H_ERR_UNK"
}
}
func _initLogging(d bool, c string, f logger.Priority) { func _initLogging(d bool, c string, f logger.Priority) {
if Log == nil { if Log == nil {
Log, _ = logger.New(f, fmt.Sprintf("%s:xsnet", c)) Log, _ = logger.New(f, fmt.Sprintf("%s:xsnet", c))
@ -129,6 +191,10 @@ func (hc *Conn) Unlock() {
hc.m.Unlock() hc.m.Unlock()
} }
func (hc Conn) KEX() KEXAlg {
return hc.kex
}
func (hc Conn) GetStatus() CSOType { func (hc Conn) GetStatus() CSOType {
return *hc.closeStat return *hc.closeStat
} }
@ -935,6 +1001,9 @@ func (hl *HKExListener) Accept() (hc Conn, err error) {
default: default:
return Conn{}, err return Conn{}, err
} }
// Finally, ensure alg proposed by client is allowed by server config
//if hc.kex.String() {
log.Println("[hc.Accept successful]") log.Println("[hc.Accept successful]")
return return
} }