mirror of https://gogs.blitter.com/RLabs/xs
				
				
				
			Misc. fixes to end-of-session conn handling. Outstanding bug w/client chaff enabled & truncated client data
This commit is contained in:
		
							parent
							
								
									5920e06748
								
							
						
					
					
						commit
						00e03c1d54
					
				| 
						 | 
					@ -439,7 +439,12 @@ func (hc Conn) Read(b []byte) (n int, err error) {
 | 
				
			||||||
			log.Printf("[TermSize pkt: rows %v cols %v]\n", hc.Rows, hc.Cols)
 | 
								log.Printf("[TermSize pkt: rows %v cols %v]\n", hc.Rows, hc.Cols)
 | 
				
			||||||
			hc.WinCh <- WinSize{hc.Rows, hc.Cols}
 | 
								hc.WinCh <- WinSize{hc.Rows, hc.Cols}
 | 
				
			||||||
		} else if ctrlStatOp == CSOExitStatus {
 | 
							} else if ctrlStatOp == CSOExitStatus {
 | 
				
			||||||
 | 
								if len(payloadBytes) > 0 {
 | 
				
			||||||
				*hc.closeStat = uint8(payloadBytes[0])
 | 
									*hc.closeStat = uint8(payloadBytes[0])
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									log.Println("[truncated payload, cannot determine CSOExitStatus]")
 | 
				
			||||||
 | 
									*hc.closeStat = 99
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			hc.dBuf.Write(payloadBytes)
 | 
								hc.dBuf.Write(payloadBytes)
 | 
				
			||||||
			//log.Printf("hc.dBuf: %s\n", hex.Dump(hc.dBuf.Bytes()))
 | 
								//log.Printf("hc.dBuf: %s\n", hex.Dump(hc.dBuf.Bytes()))
 | 
				
			||||||
| 
						 | 
					@ -450,12 +455,16 @@ func (hc Conn) Read(b []byte) (n int, err error) {
 | 
				
			||||||
		hTmp := hc.rm.Sum(nil)[0:4]
 | 
							hTmp := hc.rm.Sum(nil)[0:4]
 | 
				
			||||||
		log.Printf("<%04x) HMAC:(i)%s (c)%02x\r\n", decryptN, hex.EncodeToString([]byte(hmacIn[0:])), hTmp)
 | 
							log.Printf("<%04x) HMAC:(i)%s (c)%02x\r\n", decryptN, hex.EncodeToString([]byte(hmacIn[0:])), hTmp)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if *hc.closeStat == 99 {
 | 
				
			||||||
 | 
								log.Println("[cannot verify HMAC]")
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
			// Log alert if hmac didn't match, corrupted channel
 | 
								// Log alert if hmac didn't match, corrupted channel
 | 
				
			||||||
			if !bytes.Equal(hTmp, []byte(hmacIn[0:])) /*|| hmacIn[0] > 0xf8*/ {
 | 
								if !bytes.Equal(hTmp, []byte(hmacIn[0:])) /*|| hmacIn[0] > 0xf8*/ {
 | 
				
			||||||
				fmt.Println("** ALERT - detected HMAC mismatch, possible channel tampering **")
 | 
									fmt.Println("** ALERT - detected HMAC mismatch, possible channel tampering **")
 | 
				
			||||||
				_, _ = hc.c.Write([]byte{CSOHmacInvalid})
 | 
									_, _ = hc.c.Write([]byte{CSOHmacInvalid})
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	retN := hc.dBuf.Len()
 | 
						retN := hc.dBuf.Len()
 | 
				
			||||||
	if retN > len(b) {
 | 
						if retN > len(b) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										103
									
								
								hkexsh/hkexsh.go
								
								
								
								
							
							
						
						
									
										103
									
								
								hkexsh/hkexsh.go
								
								
								
								
							| 
						 | 
					@ -30,7 +30,7 @@ type cmdSpec struct {
 | 
				
			||||||
	who        []byte
 | 
						who        []byte
 | 
				
			||||||
	cmd        []byte
 | 
						cmd        []byte
 | 
				
			||||||
	authCookie []byte
 | 
						authCookie []byte
 | 
				
			||||||
	status     int // though UNIX shell exit status is uint8, os.Exit() wants int
 | 
						status     int // UNIX exit status is uint8, but os.Exit() wants int
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var (
 | 
					var (
 | 
				
			||||||
| 
						 | 
					@ -86,13 +86,13 @@ func parseNonSwitchArgs(a []string, dp string) (user, host, port, path string, i
 | 
				
			||||||
				fancyPort = dp
 | 
									fancyPort = dp
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if fancyPath == "" {
 | 
								//if fancyPath == "" {
 | 
				
			||||||
				fancyPath = "."
 | 
								//	fancyPath = "."
 | 
				
			||||||
			}
 | 
								//}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if i == len(a)-1 {
 | 
								if i == len(a)-1 {
 | 
				
			||||||
				isDest = true
 | 
									isDest = true
 | 
				
			||||||
				fmt.Println("isDest")
 | 
									fmt.Println("remote path isDest")
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			fmt.Println("fancyArgs: user:", fancyUser, "host:", fancyHost, "port:", fancyPort, "path:", fancyPath)
 | 
								fmt.Println("fancyArgs: user:", fancyUser, "host:", fancyHost, "port:", fancyPort, "path:", fancyPath)
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
| 
						 | 
					@ -118,6 +118,7 @@ func main() {
 | 
				
			||||||
	version := "0.1pre (NO WARRANTY)"
 | 
						version := "0.1pre (NO WARRANTY)"
 | 
				
			||||||
	var vopt bool
 | 
						var vopt bool
 | 
				
			||||||
	var dbg bool
 | 
						var dbg bool
 | 
				
			||||||
 | 
						var shellMode bool // if true act as shell, else file copier
 | 
				
			||||||
	var cAlg string
 | 
						var cAlg string
 | 
				
			||||||
	var hAlg string
 | 
						var hAlg string
 | 
				
			||||||
	var server string
 | 
						var server string
 | 
				
			||||||
| 
						 | 
					@ -154,6 +155,7 @@ func main() {
 | 
				
			||||||
		// hkexsh accepts a command (-x) but not
 | 
							// hkexsh accepts a command (-x) but not
 | 
				
			||||||
		// a srcpath (-r) or dstpath (-t)
 | 
							// a srcpath (-r) or dstpath (-t)
 | 
				
			||||||
		flag.StringVar(&cmdStr, "x", "", "command to run (default empty - interactive shell)")
 | 
							flag.StringVar(&cmdStr, "x", "", "command to run (default empty - interactive shell)")
 | 
				
			||||||
 | 
							shellMode = true
 | 
				
			||||||
	} // else {
 | 
						} // else {
 | 
				
			||||||
	//// hkexcp accepts srcpath (-r) and dstpath (-t), but not
 | 
						//// hkexcp accepts srcpath (-r) and dstpath (-t), but not
 | 
				
			||||||
	//// a command (-x)
 | 
						//// a command (-x)
 | 
				
			||||||
| 
						 | 
					@ -162,19 +164,19 @@ func main() {
 | 
				
			||||||
	//}
 | 
						//}
 | 
				
			||||||
	flag.Parse()
 | 
						flag.Parse()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fancyUser, fancyHost, fancyPort, fancyPath, pathIsDest, otherArgs :=
 | 
						tmpUser, tmpHost, tmpPort, tmpPath, pathIsDest, otherArgs :=
 | 
				
			||||||
		parseNonSwitchArgs(flag.Args(), defPort /* defPort */)
 | 
							parseNonSwitchArgs(flag.Args(), defPort /* defPort */)
 | 
				
			||||||
	fmt.Println("otherArgs:", otherArgs)
 | 
						fmt.Println("otherArgs:", otherArgs)
 | 
				
			||||||
	//fmt.Println("fancyHost:", fancyHost)
 | 
						//fmt.Println("tmpHost:", tmpHost)
 | 
				
			||||||
	fmt.Println("fancyPath:", fancyPath)
 | 
						//fmt.Println("tmpPath:", tmpPath)
 | 
				
			||||||
	if fancyUser != "" {
 | 
						if tmpUser != "" {
 | 
				
			||||||
		altUser = fancyUser
 | 
							altUser = tmpUser
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if fancyHost != "" {
 | 
						if tmpHost != "" {
 | 
				
			||||||
		server = fancyHost + ":" + fancyPort
 | 
							server = tmpHost + ":" + tmpPort
 | 
				
			||||||
		//fmt.Println("fancyHost sets server to", server)
 | 
							//fmt.Println("tmpHost sets server to", server)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if fancyPath != "" {
 | 
						if tmpPath != "" {
 | 
				
			||||||
		// -if pathIsSrc && len(otherArgs) > 1 ERROR
 | 
							// -if pathIsSrc && len(otherArgs) > 1 ERROR
 | 
				
			||||||
		// -else flatten otherArgs into space-delim list => copySrc
 | 
							// -else flatten otherArgs into space-delim list => copySrc
 | 
				
			||||||
		if pathIsDest {
 | 
							if pathIsDest {
 | 
				
			||||||
| 
						 | 
					@ -183,17 +185,18 @@ func main() {
 | 
				
			||||||
				copySrc = append(copySrc, v...)
 | 
									copySrc = append(copySrc, v...)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			fmt.Println(">> copySrc:", string(copySrc))
 | 
								fmt.Println(">> copySrc:", string(copySrc))
 | 
				
			||||||
			copyDst = fancyPath
 | 
								copyDst = tmpPath
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			if len(otherArgs) > 1 {
 | 
								if len(otherArgs) > 1 {
 | 
				
			||||||
				log.Fatal("ERROR: cannot specify more than one dest path for copy")
 | 
									log.Fatal("ERROR: cannot specify more than one dest path for copy")
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			copySrc = []byte(fancyPath)
 | 
								copySrc = []byte(tmpPath)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Do some more option consistency checks
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	//fmt.Println("server finally is:", server)
 | 
						//fmt.Println("server finally is:", server)
 | 
				
			||||||
 | 
					 | 
				
			||||||
	if flag.NFlag() == 0 && server == "" {
 | 
						if flag.NFlag() == 0 && server == "" {
 | 
				
			||||||
		flag.Usage()
 | 
							flag.Usage()
 | 
				
			||||||
		os.Exit(0)
 | 
							os.Exit(0)
 | 
				
			||||||
| 
						 | 
					@ -208,12 +211,33 @@ func main() {
 | 
				
			||||||
		log.Fatal("incompatible options -- either cmd (-x) or copy ops but not both")
 | 
							log.Fatal("incompatible options -- either cmd (-x) or copy ops but not both")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//-------------------------------------------------------------------
 | 
				
			||||||
 | 
						// Here we have parsed all options and can now carry out
 | 
				
			||||||
 | 
						// either the shell session or copy operation.
 | 
				
			||||||
 | 
						_ = shellMode
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if dbg {
 | 
						if dbg {
 | 
				
			||||||
		log.SetOutput(os.Stdout)
 | 
							log.SetOutput(os.Stdout)
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		log.SetOutput(ioutil.Discard)
 | 
							log.SetOutput(ioutil.Discard)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// We must make the decision about interactivity before Dial()
 | 
				
			||||||
 | 
						// as it affects chaffing behaviour. 20180805
 | 
				
			||||||
 | 
						if len(cmdStr) == 0 {
 | 
				
			||||||
 | 
							op = []byte{'s'}
 | 
				
			||||||
 | 
							isInteractive = true
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							op = []byte{'c'}
 | 
				
			||||||
 | 
							// non-interactive cmds may complete quickly, so chaff earlier/faster
 | 
				
			||||||
 | 
							// to help ensure there's some cover to the brief traffic.
 | 
				
			||||||
 | 
							// (ignoring cmdline values)
 | 
				
			||||||
 | 
							//!DEBUG
 | 
				
			||||||
 | 
							//chaffEnabled = false
 | 
				
			||||||
 | 
							chaffFreqMin = 2
 | 
				
			||||||
 | 
							chaffFreqMax = 10
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	conn, err := hkexnet.Dial("tcp", server, cAlg, hAlg)
 | 
						conn, err := hkexnet.Dial("tcp", server, cAlg, hAlg)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		fmt.Println("Err!")
 | 
							fmt.Println("Err!")
 | 
				
			||||||
| 
						 | 
					@ -226,6 +250,7 @@ func main() {
 | 
				
			||||||
	// 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 *hkexsh.State
 | 
						var oldState *hkexsh.State
 | 
				
			||||||
 | 
						if shellMode {
 | 
				
			||||||
		if isatty.IsTerminal(os.Stdin.Fd()) {
 | 
							if isatty.IsTerminal(os.Stdin.Fd()) {
 | 
				
			||||||
			oldState, err = hkexsh.MakeRaw(int(os.Stdin.Fd()))
 | 
								oldState, err = hkexsh.MakeRaw(int(os.Stdin.Fd()))
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
| 
						 | 
					@ -235,6 +260,7 @@ func main() {
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			log.Println("NOT A TTY")
 | 
								log.Println("NOT A TTY")
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var uname string
 | 
						var uname string
 | 
				
			||||||
	if len(altUser) == 0 {
 | 
						if len(altUser) == 0 {
 | 
				
			||||||
| 
						 | 
					@ -244,18 +270,6 @@ func main() {
 | 
				
			||||||
		uname = altUser
 | 
							uname = altUser
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if len(cmdStr) == 0 {
 | 
					 | 
				
			||||||
		op = []byte{'s'}
 | 
					 | 
				
			||||||
		isInteractive = true
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		op = []byte{'c'}
 | 
					 | 
				
			||||||
		// non-interactive cmds may complete quickly, so chaff earlier/faster
 | 
					 | 
				
			||||||
		// to help ensure there's some cover to the brief traffic.
 | 
					 | 
				
			||||||
		// (ignoring cmdline values)
 | 
					 | 
				
			||||||
		chaffFreqMin = 2
 | 
					 | 
				
			||||||
		chaffFreqMax = 10
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if len(authCookie) == 0 {
 | 
						if len(authCookie) == 0 {
 | 
				
			||||||
		fmt.Printf("Gimme cookie:")
 | 
							fmt.Printf("Gimme cookie:")
 | 
				
			||||||
		ab, err := hkexsh.ReadPassword(int(os.Stdin.Fd()))
 | 
							ab, err := hkexsh.ReadPassword(int(os.Stdin.Fd()))
 | 
				
			||||||
| 
						 | 
					@ -288,33 +302,26 @@ func main() {
 | 
				
			||||||
	conn.SetupChaff(chaffFreqMin, chaffFreqMax, chaffBytesMax) // enable client->server chaffing
 | 
						conn.SetupChaff(chaffFreqMin, chaffFreqMax, chaffBytesMax) // enable client->server chaffing
 | 
				
			||||||
	if chaffEnabled {
 | 
						if chaffEnabled {
 | 
				
			||||||
		conn.EnableChaff()
 | 
							conn.EnableChaff()
 | 
				
			||||||
 | 
							//defer conn.DisableChaff()
 | 
				
			||||||
 | 
							//defer conn.ShutdownChaff()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	defer conn.DisableChaff()
 | 
					 | 
				
			||||||
	defer conn.ShutdownChaff()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	//client reader (from server) goroutine
 | 
						//client reader (from server) goroutine
 | 
				
			||||||
 | 
						//Read remote end's stdout
 | 
				
			||||||
	wg.Add(1)
 | 
						wg.Add(1)
 | 
				
			||||||
	go func() {
 | 
						go func() {
 | 
				
			||||||
		// By deferring a call to wg.Done(),
 | 
							// By deferring a call to wg.Done(),
 | 
				
			||||||
		// each goroutine guarantees that it marks
 | 
							// each goroutine guarantees that it marks
 | 
				
			||||||
		// its direction's stream as finished.
 | 
							// its direction's stream as finished.
 | 
				
			||||||
		//
 | 
					 | 
				
			||||||
		// Whichever direction's goroutine finishes first
 | 
					 | 
				
			||||||
		// will call wg.Done() once more, explicitly, to
 | 
					 | 
				
			||||||
		// hang up on the other side, so that this client
 | 
					 | 
				
			||||||
		// exits immediately on an EOF from either side.
 | 
					 | 
				
			||||||
		defer wg.Done()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// io.Copy() expects EOF so this will
 | 
							// io.Copy() expects EOF so normally this will
 | 
				
			||||||
		// exit with inerr == nil
 | 
							// exit with inerr == nil
 | 
				
			||||||
		_, inerr := io.Copy(os.Stdout, conn)
 | 
							_, inerr := io.Copy(os.Stdout, conn)
 | 
				
			||||||
		if inerr != nil {
 | 
							if inerr != nil {
 | 
				
			||||||
			if inerr.Error() != "EOF" {
 | 
					 | 
				
			||||||
			fmt.Println(inerr)
 | 
								fmt.Println(inerr)
 | 
				
			||||||
			_ = hkexsh.Restore(int(os.Stdin.Fd()), oldState) // Best effort.
 | 
								_ = hkexsh.Restore(int(os.Stdin.Fd()), oldState) // Best effort.
 | 
				
			||||||
			os.Exit(1)
 | 
								os.Exit(1)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		rec.status = int(conn.GetStatus())
 | 
							rec.status = int(conn.GetStatus())
 | 
				
			||||||
		log.Println("rec.status:", rec.status)
 | 
							log.Println("rec.status:", rec.status)
 | 
				
			||||||
| 
						 | 
					@ -322,41 +329,43 @@ func main() {
 | 
				
			||||||
		if isInteractive {
 | 
							if isInteractive {
 | 
				
			||||||
			log.Println("[* Got EOF *]")
 | 
								log.Println("[* Got EOF *]")
 | 
				
			||||||
			_ = hkexsh.Restore(int(os.Stdin.Fd()), oldState) // Best effort.
 | 
								_ = hkexsh.Restore(int(os.Stdin.Fd()), oldState) // Best effort.
 | 
				
			||||||
			wg.Done()
 | 
					 | 
				
			||||||
			//os.Exit(rec.status)
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							wg.Done()
 | 
				
			||||||
	}()
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Only look for data from stdin to send to remote end
 | 
				
			||||||
 | 
						// for interactive sessions.
 | 
				
			||||||
	if isInteractive {
 | 
						if isInteractive {
 | 
				
			||||||
		handleTermResizes(conn)
 | 
							handleTermResizes(conn)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// client writer (to server) goroutine
 | 
							// client writer (to server) goroutine
 | 
				
			||||||
 | 
							// Write local stdin to remote end
 | 
				
			||||||
		wg.Add(1)
 | 
							wg.Add(1)
 | 
				
			||||||
		go func() {
 | 
							go func() {
 | 
				
			||||||
			defer wg.Done()
 | 
								defer wg.Done()
 | 
				
			||||||
 | 
					 | 
				
			||||||
			// Copy() expects EOF so this will
 | 
								// Copy() expects EOF so this will
 | 
				
			||||||
			// exit with outerr == nil
 | 
								// exit with outerr == nil
 | 
				
			||||||
			//!_, outerr := io.Copy(conn, os.Stdin)
 | 
								//!_, outerr := io.Copy(conn, os.Stdin)
 | 
				
			||||||
			_, outerr := func(conn *hkexnet.Conn, r io.Reader) (w int64, e error) {
 | 
								_, outerr := func(conn *hkexnet.Conn, r io.Reader) (w int64, e error) {
 | 
				
			||||||
				return io.Copy(conn, r)
 | 
									w, e = io.Copy(conn, r)
 | 
				
			||||||
 | 
									return w, e
 | 
				
			||||||
			}(conn, os.Stdin)
 | 
								}(conn, os.Stdin)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if outerr != nil {
 | 
								if outerr != nil {
 | 
				
			||||||
				log.Println(outerr)
 | 
									log.Println(outerr)
 | 
				
			||||||
				if outerr.Error() != "EOF" {
 | 
					 | 
				
			||||||
				fmt.Println(outerr)
 | 
									fmt.Println(outerr)
 | 
				
			||||||
				_ = hkexsh.Restore(int(os.Stdin.Fd()), oldState) // Best effort.
 | 
									_ = hkexsh.Restore(int(os.Stdin.Fd()), oldState) // Best effort.
 | 
				
			||||||
				os.Exit(255)
 | 
									os.Exit(255)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			log.Println("[Sent EOF]")
 | 
								log.Println("[Sent EOF]")
 | 
				
			||||||
			wg.Done() // client hung up, close WaitGroup to exit client
 | 
								wg.Done()
 | 
				
			||||||
		}()
 | 
							}()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Wait until both stdin and stdout goroutines finish
 | 
						// Wait until both stdin and stdout goroutines finish
 | 
				
			||||||
	wg.Wait()
 | 
						wg.Wait()
 | 
				
			||||||
 | 
						conn.DisableChaff()
 | 
				
			||||||
 | 
						conn.ShutdownChaff()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_ = hkexsh.Restore(int(os.Stdin.Fd()), oldState) // Best effort.
 | 
						_ = hkexsh.Restore(int(os.Stdin.Fd()), oldState) // Best effort.
 | 
				
			||||||
	os.Exit(rec.status)
 | 
						os.Exit(rec.status)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,6 +18,7 @@ import (
 | 
				
			||||||
	"os/user"
 | 
						"os/user"
 | 
				
			||||||
	"runtime"
 | 
						"runtime"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
	"syscall"
 | 
						"syscall"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"blitter.com/go/goutmp"
 | 
						"blitter.com/go/goutmp"
 | 
				
			||||||
| 
						 | 
					@ -83,6 +84,7 @@ func runCmdAs(who string, cmd string, conn hkex.Conn) (err error) {
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// Uses ptys to support commands which expect a terminal.
 | 
					// Uses ptys to support commands which expect a terminal.
 | 
				
			||||||
func runShellAs(who string, cmd string, interactive bool, conn hkexnet.Conn, chaffing bool) (err error, exitStatus int) {
 | 
					func runShellAs(who string, cmd string, interactive bool, conn hkexnet.Conn, chaffing bool) (err error, exitStatus int) {
 | 
				
			||||||
 | 
						var wg sync.WaitGroup
 | 
				
			||||||
	u, _ := user.Lookup(who)
 | 
						u, _ := user.Lookup(who)
 | 
				
			||||||
	var uid, gid uint32
 | 
						var uid, gid uint32
 | 
				
			||||||
	fmt.Sscanf(u.Uid, "%d", &uid)
 | 
						fmt.Sscanf(u.Uid, "%d", &uid)
 | 
				
			||||||
| 
						 | 
					@ -135,15 +137,16 @@ func runShellAs(who string, cmd string, interactive bool, conn hkexnet.Conn, cha
 | 
				
			||||||
				log.Printf("[Setting term size to: %v %v]\n", sz.Rows, sz.Cols)
 | 
									log.Printf("[Setting term size to: %v %v]\n", sz.Rows, sz.Cols)
 | 
				
			||||||
				pty.Setsize(ptmx, &pty.Winsize{Rows: sz.Rows, Cols: sz.Cols})
 | 
									pty.Setsize(ptmx, &pty.Winsize{Rows: sz.Rows, Cols: sz.Cols})
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
								fmt.Println("*** WinCh goroutine done ***")
 | 
				
			||||||
		}()
 | 
							}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Copy stdin to the pty.. (bgnd goroutine)
 | 
							// Copy stdin to the pty.. (bgnd goroutine)
 | 
				
			||||||
		go func() {
 | 
							go func() {
 | 
				
			||||||
			_, e := io.Copy(ptmx, conn)
 | 
								_, e := io.Copy(ptmx, conn)
 | 
				
			||||||
			if e != nil {
 | 
								if e != nil {
 | 
				
			||||||
				log.Printf("** std->pty ended **\n")
 | 
									log.Println("** stdin->pty ended **:", e.Error())
 | 
				
			||||||
				return
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
								fmt.Println("*** stdin->pty goroutine done ***")
 | 
				
			||||||
		}()
 | 
							}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if chaffing {
 | 
							if chaffing {
 | 
				
			||||||
| 
						 | 
					@ -153,17 +156,26 @@ func runShellAs(who string, cmd string, interactive bool, conn hkexnet.Conn, cha
 | 
				
			||||||
		defer conn.ShutdownChaff()
 | 
							defer conn.ShutdownChaff()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// ..and the pty to stdout.
 | 
							// ..and the pty to stdout.
 | 
				
			||||||
 | 
							// This may take some time exceeding that of the
 | 
				
			||||||
 | 
							// actual command's lifetime, so the c.Wait() below
 | 
				
			||||||
 | 
							// must synchronize with the completion of this goroutine
 | 
				
			||||||
 | 
							// to ensure all stdout data gets to the client before
 | 
				
			||||||
 | 
							// connection is closed.
 | 
				
			||||||
 | 
							wg.Add(1)
 | 
				
			||||||
		go func() {
 | 
							go func() {
 | 
				
			||||||
 | 
								defer wg.Done()
 | 
				
			||||||
			_, e := io.Copy(conn, ptmx)
 | 
								_, e := io.Copy(conn, ptmx)
 | 
				
			||||||
			if e != nil {
 | 
								if e != nil {
 | 
				
			||||||
				log.Printf("** pty->stdout ended **\n")
 | 
									log.Println("** pty->stdout ended **:", e.Error())
 | 
				
			||||||
				return
 | 
									//wg.Done() //!return
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			// The above io.Copy() will exit when the command attached
 | 
								// The above io.Copy() will exit when the command attached
 | 
				
			||||||
			// to the pty exits
 | 
								// to the pty exits
 | 
				
			||||||
 | 
								fmt.Println("*** pty->stdout goroutine done ***")
 | 
				
			||||||
		}()
 | 
							}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if err := c.Wait(); err != nil {
 | 
							if err := c.Wait(); err != nil {
 | 
				
			||||||
 | 
									fmt.Println("*** c.Wait() done ***")
 | 
				
			||||||
			if exiterr, ok := err.(*exec.ExitError); ok {
 | 
								if exiterr, ok := err.(*exec.ExitError); ok {
 | 
				
			||||||
				// The program has exited with an exit code != 0
 | 
									// The program has exited with an exit code != 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -177,6 +189,9 @@ func runShellAs(who string, cmd string, interactive bool, conn hkexnet.Conn, cha
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							wg.Wait() // Wait on pty->stdout completion to client
 | 
				
			||||||
 | 
							//conn.DisableChaff()
 | 
				
			||||||
 | 
							//conn.ShutdownChaff()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return
 | 
						return
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue