diff --git a/demo/server/server.go b/demo/server/server.go index f133271..675cb86 100644 --- a/demo/server/server.go +++ b/demo/server/server.go @@ -4,11 +4,82 @@ import ( "flag" "fmt" "log" + "os/exec" + "os/user" + "strings" + "syscall" "time" hkex "blitter.com/herradurakex" ) +const ( + OpR = 'r' // read(file) (binary mode) + OpW = 'w' // (over)write + OpA = 'a' // append + OpRm = 'd' // rm + OpRmD = 'D' // rmdir (rm -rf) + OpM = 'm' // mkdir (-p) + OpN = 'n' // re(n)ame (mv) + OpCm = 'c' // chmod + OpCo = 'C' // chown + OpX = 'x' // exec +) + +type Op uint8 + +type cmdRunner struct { + op Op + who string + arg string + authCookie string + CloseHandler func(*cmdRunner) + status int +} + +func testCloseHandler(r *cmdRunner) { + fmt.Println("[testCloseHandler()]") + r.arg = "/usr/bin/touch " + r.arg + cmd(r) +} + +func cmd(r *cmdRunner) { + switch r.op { + case OpR: + //Clean up r.cmd beforehand + r.arg = strings.TrimSpace(r.arg) + fmt.Printf("[cmd was:'%s']\n", r.arg) + runCmdAs(r.who, r.arg) + fmt.Println(r.arg) + break + default: + fmt.Printf("[cmd %d ignored:%d]\n", int(r.op)) + break + } +} + +// Run a command (via os.exec) as a specific user +func runCmdAs(who string, cmd string) (err error) { + u, _ := user.Lookup(who) + var uid, gid uint32 + fmt.Sscanf(u.Uid, "%d", &uid) + fmt.Sscanf(u.Gid, "%d", &gid) + //fmt.Println("uid:", uid, "gid:", gid) + + args := strings.Split(cmd, " ") + arg0 := args[0] + args = args[1:] + c := exec.Command(arg0, args...) + c.SysProcAttr = &syscall.SysProcAttr{} + c.SysProcAttr.Credential = &syscall.Credential{Uid: uid, Gid: gid} + err = c.Run() + if err != nil { + log.Printf("Command finished with error: %v", err) + log.Printf("[%s]\n", cmd) + } + return +} + // Demo of a simple server that listens and spawns goroutines for each // connecting client. Note this code is identical to standard tcp // server code, save for declaring 'hkex' rather than 'net' @@ -41,10 +112,10 @@ func main() { // The loop then returns to accepting, so that // multiple connections may be served concurrently. go func(c hkex.Conn) (e error) { + defer c.Close() ch := make(chan []byte) chN := 0 eCh := make(chan error) - var connOp *byte = nil // Start a goroutine to read from our net connection go func(ch chan []byte, eCh chan error) { @@ -57,23 +128,14 @@ func main() { eCh <- err return } - if connOp == nil { - // Initial xmit - get op byte - // (TODO: determine valid ops - // for now 'e' (echo), 'i' (interactive), 'x' (exec), ... ?) - connOp = new(byte) - *connOp = data[0] - data = data[1:] - chN -= 1 - fmt.Printf("[* connOp '%c']\n", *connOp) - } - // send data if we read some. ch <- data[0:chN] } }(ch, eCh) ticker := time.Tick(time.Second / 100) + var r cmdRunner + var connOp *byte = nil Term: // continuously read from the connection for { @@ -82,12 +144,32 @@ func main() { case data := <-ch: // Do something with the data fmt.Printf("Client sent %+v\n", data[0:chN]) + if connOp == nil { + // Initial xmit - get op byte + // (TODO: determine valid ops + // for now 'e' (echo), 'i' (interactive), 'x' (exec), ... ?) + connOp = new(byte) + *connOp = data[0] + data = data[1:chN] + chN -= 1 + + fmt.Printf("[* connOp '%c']\n", *connOp) + // The CloseHandler typically handles the + // accumulated command data + r = cmdRunner{op: Op(*connOp), + who: "larissa", arg: string(data), + authCookie: "c00ki3", + CloseHandler: testCloseHandler, + status: 0} + } + //fmt.Printf("Client sent %s\n", string(data)) // This case means we got an error and the goroutine has finished case err := <-eCh: // handle our error then exit for loop if err.Error() == "EOF" { fmt.Printf("[Client disconnected]\n") + r.CloseHandler(&r) } else { fmt.Printf("Error reading client data! (%+v)\n", err) } @@ -100,7 +182,7 @@ func main() { } } // Shut down the connection. - c.Close() + //c.Close() return }(conn) } diff --git a/hkexnet.go b/hkexnet.go index 7743316..5075dcf 100644 --- a/hkexnet.go +++ b/hkexnet.go @@ -34,7 +34,6 @@ import ( // Conn is a HKex connection - a drop-in replacement for net.Conn type Conn struct { - //net.Conn c net.Conn // which also implements io.Reader, io.Writer, ... h *HerraduraKEx cipheropts uint32 // post-KEx cipher/hmac options @@ -44,16 +43,12 @@ type Conn struct { w cipher.Stream } -func (c *Conn) SetReadDeadline(t time.Time) error { - return c.SetReadDeadline(t) -} - // ConnOpts returns the cipher/hmac options value, which is sent to the // peer but is not itself part of the KEx. // // (Used for protocol-level negotiations after KEx such as // cipher/HMAC algorithm options etc.) -func (c *Conn) ConnOpts() uint32 { +func (c Conn) ConnOpts() uint32 { return c.cipheropts } @@ -61,7 +56,7 @@ func (c *Conn) ConnOpts() uint32 { // peer as part of KEx but not part of the KEx itself. // // opts - bitfields for cipher and hmac alg. to use after KEx -func (c *Conn) SetConnOpts(copts uint32) { +func (c Conn) SetConnOpts(copts uint32) { c.cipheropts = copts } @@ -70,7 +65,7 @@ func (c *Conn) SetConnOpts(copts uint32) { // // Consumers of this lib may use this for protocol-level options not part // of the KEx or encryption info used by the connection. -func (c *Conn) Opts() uint32 { +func (c Conn) Opts() uint32 { return c.opts } @@ -81,7 +76,7 @@ func (c *Conn) Opts() uint32 { // of the KEx of encryption info used by the connection. // // opts - a uint32, caller-defined -func (c *Conn) SetOpts(opts uint32) { +func (c Conn) SetOpts(opts uint32) { c.opts = opts } @@ -90,7 +85,7 @@ func (c *Conn) SetOpts(opts uint32) { // // Consumers of this lib may use this to indicate connection-specific // operations not part of the KEx or encryption info used by the connection. -func (c *Conn) Op() uint8 { +func (c Conn) Op() uint8 { return c.op } @@ -101,11 +96,11 @@ func (c *Conn) Op() uint8 { // operations not part of the KEx or encryption info used by the connection. // // op - a uint8, caller-defined -func (c *Conn) SetOp(op uint8) { +func (c Conn) SetOp(op uint8) { c.op = op } -func (c *Conn) applyConnExtensions(extensions ...string) { +func (c Conn) applyConnExtensions(extensions ...string) { for _, s := range extensions { switch s { case "C_AES_256": @@ -177,12 +172,57 @@ func Dial(protocol string, ipport string, extensions ...string) (hc *Conn, err e } // Close a hkex.Conn -func (c *Conn) Close() (err error) { +func (c Conn) Close() (err error) { err = c.c.Close() fmt.Println("[Conn Closing]") return } +// LocalAddr returns the local network address. +func (c Conn) LocalAddr() net.Addr { + return c.c.LocalAddr() +} + +// RemoteAddr returns the remote network address. +func (c Conn) RemoteAddr() net.Addr { + return c.c.RemoteAddr() +} + +// SetDeadline sets the read and write deadlines associated +// with the connection. It is equivalent to calling both +// SetReadDeadline and SetWriteDeadline. +// +// A deadline is an absolute time after which I/O operations +// fail with a timeout (see type Error) instead of +// blocking. The deadline applies to all future and pending +// I/O, not just the immediately following call to Read or +// Write. After a deadline has been exceeded, the connection +// can be refreshed by setting a deadline in the future. +// +// An idle timeout can be implemented by repeatedly extending +// the deadline after successful Read or Write calls. +// +// A zero value for t means I/O operations will not time out. +func (c Conn) SetDeadline(t time.Time) error { + return c.SetDeadline(t) +} + +// SetWriteDeadline sets the deadline for future Write calls +// and any currently-blocked Write call. +// Even if write times out, it may return n > 0, indicating that +// some of the data was successfully written. +// A zero value for t means Write will not time out. +func (c Conn) SetWriteDeadline(t time.Time) error { + return c.SetWriteDeadline(t) +} + +// SetReadDeadline sets the deadline for future Read calls +// and any currently-blocked Read call. +// A zero value for t means Read will not time out. +func (c Conn) SetReadDeadline(t time.Time) error { + return c.SetReadDeadline(t) +} + /*---------------------------------------------------------------------*/ // HKExListener is a Listener conforming to net.Listener @@ -208,7 +248,7 @@ func Listen(protocol string, ipport string) (hl HKExListener, e error) { // Close a hkex Listener // // See go doc io.Close -func (hl *HKExListener) Close() error { +func (hl HKExListener) Close() error { fmt.Println("[Listener Closed]") return hl.l.Close() } @@ -216,7 +256,7 @@ func (hl *HKExListener) Close() error { // Accept a client connection, conforming to net.Listener.Accept() // // See go doc net.Listener.Accept -func (hl *HKExListener) Accept() (hc Conn, err error) { +func (hl HKExListener) Accept() (hc Conn, err error) { c, err := hl.l.Accept() if err != nil { return Conn{c: nil, h: nil, cipheropts: 0, opts: 0,