mirror of
https://gogs.blitter.com/RLabs/xs
synced 2024-08-14 10:26:42 +00:00
xsd: Added -aK,-aC,-aH to control accepted client proposals
This commit is contained in:
parent
9b90c0558e
commit
d4f50bfdc0
4 changed files with 199 additions and 20 deletions
19
xs/xs.go
19
xs/xs.go
|
@ -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()))
|
||||||
|
|
107
xsd/xsd.go
107
xsd/xsd.go
|
@ -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")
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
75
xsnet/net.go
75
xsnet/net.go
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue