|
@@ -0,0 +1,174 @@
|
|
|
+package SSHServer
|
|
|
+
|
|
|
+import (
|
|
|
+ "git.swzry.com/NSMCServerLauncher/Utils"
|
|
|
+ "fmt"
|
|
|
+ "git.swzry.com/NSMCServerLauncher/Logger"
|
|
|
+ "crypto/rsa"
|
|
|
+ "crypto/rand"
|
|
|
+ "crypto/x509"
|
|
|
+ "encoding/pem"
|
|
|
+ "os"
|
|
|
+ "golang.org/x/crypto/ssh"
|
|
|
+ "io/ioutil"
|
|
|
+ "net"
|
|
|
+ "github.com/swzry/go.TSmap"
|
|
|
+ "golang.org/x/crypto/ssh/terminal"
|
|
|
+ "git.swzry.com/NSMCServerLauncher/Terminal"
|
|
|
+ "golang.org/x/crypto/bcrypt"
|
|
|
+)
|
|
|
+
|
|
|
+var mainThreadBlockChan chan byte
|
|
|
+var ServerConf *ssh.ServerConfig
|
|
|
+var _clientListRawMap map[interface{}]interface{}
|
|
|
+var ClientList TSmap.TSmap
|
|
|
+
|
|
|
+func StartSSHServer(mtbc chan byte) {
|
|
|
+ mainThreadBlockChan = mtbc
|
|
|
+ if r,err := Utils.PathExists(SSHServerConf.host_key_file); ((!r) || (err != nil)) {
|
|
|
+ fmt.Fprintln(&Logger.Log.SysInfo,"Generating SSH Host Key......")
|
|
|
+ fmt.Fprintln(&Logger.Log.SysInfo,"(It may takes a long time. Please wait.)")
|
|
|
+ fmt.Println("Generating SSH Host Key......")
|
|
|
+ fmt.Println("(It may takes a long time. Please wait.)")
|
|
|
+ GenerateRSAKey(SSHServerConf.host_key_file, SSHServerConf.defult_key_length)
|
|
|
+ }
|
|
|
+ ServerConf = &ssh.ServerConfig{
|
|
|
+ NoClientAuth: false,
|
|
|
+ MaxAuthTries: SSHServerConf.max_auth_tries,
|
|
|
+ PasswordCallback: SSHPasswordCallback,
|
|
|
+ PublicKeyCallback: SSHPublicKeyCallback,
|
|
|
+ AuthLogCallback: SSHAuthLogCallback,
|
|
|
+ ServerVersion: "SSH-2.0-NSMCServerLauncher-SSH",
|
|
|
+ }
|
|
|
+ keyBytes, err := ioutil.ReadFile(SSHServerConf.host_key_file)
|
|
|
+ if(err != nil){
|
|
|
+ fmt.Fprintln(&Logger.Log.SysFatal,"Failed to Load Host Key File: ", err)
|
|
|
+ mainThreadBlockChan <- 1
|
|
|
+ return
|
|
|
+ }
|
|
|
+ key, err := ssh.ParsePrivateKey(keyBytes)
|
|
|
+ if(err != nil){
|
|
|
+ fmt.Fprintln(&Logger.Log.SysFatal,"Failed to Load Host Key: ", err)
|
|
|
+ mainThreadBlockChan <- 1
|
|
|
+ return
|
|
|
+ }
|
|
|
+ ServerConf.AddHostKey(key)
|
|
|
+ listener,err := net.Listen("tcp", SSHServerConf.bind_addr)
|
|
|
+ if(err != nil){
|
|
|
+ fmt.Fprintln(&Logger.Log.SysFatal,"Failed to Bind Address '",SSHServerConf.bind_addr,"': ", err)
|
|
|
+ mainThreadBlockChan <- 1
|
|
|
+ return
|
|
|
+ }
|
|
|
+ fmt.Fprintln(&Logger.Log.SysInfo,"Listening At '",listener.Addr().String(),"'.")
|
|
|
+ _clientListRawMap = make(map[interface{}]interface{})
|
|
|
+ ClientList = &TSmap.NewTSmap{
|
|
|
+ ConMap:_clientListRawMap,
|
|
|
+ }
|
|
|
+ for{
|
|
|
+ tcpConn,err := listener.Accept()
|
|
|
+ if(err == nil){
|
|
|
+ fmt.Fprintf(&Logger.Log.SSH,"New Client '%v' Entered.\n",tcpConn.RemoteAddr())
|
|
|
+ _, schan, reqchan, err := ssh.NewServerConn(tcpConn, ServerConf)
|
|
|
+ if(err != nil){
|
|
|
+ fmt.Fprintf(&Logger.Log.SSH,"Failed Handle Client '%v': %v\n",tcpConn.RemoteAddr(),err)
|
|
|
+ tcpConn.Close()
|
|
|
+ continue
|
|
|
+ }else {
|
|
|
+ ClientList.Set(tcpConn,&Utils.ClientConnection{
|
|
|
+ Channels: &TSmap.NewTSmap{
|
|
|
+ ConMap:make(map[interface{}]interface{}),
|
|
|
+ },
|
|
|
+ })
|
|
|
+ go ssh.DiscardRequests(reqchan)
|
|
|
+ go handleChannels(schan, tcpConn)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func handleChannels(ch <-chan ssh.NewChannel, conn net.Conn) {
|
|
|
+ cnt := 0
|
|
|
+ chlist,ok := ClientList.Get(conn)
|
|
|
+ if(!ok){
|
|
|
+ fmt.Fprintf(&Logger.Log.SSH,"Failed Handle Client '%v': Channels Not Found.\n",conn.RemoteAddr())
|
|
|
+ }
|
|
|
+ for newchan := range ch {
|
|
|
+ cnt++;
|
|
|
+ fmt.Fprintf(&Logger.Log.SSH,"Client '%v', Channels %v, Handling.\n",conn.RemoteAddr(),cnt)
|
|
|
+ go handleChannel(newchan,conn,cnt,chlist.(*Utils.ClientConnection))
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func handleChannel(nch ssh.NewChannel,conn net.Conn,num int,chlist *Utils.ClientConnection) {
|
|
|
+ if(nch.ChannelType() == "session"){
|
|
|
+ ch,req,err := nch.Accept()
|
|
|
+ if(err != nil){
|
|
|
+ fmt.Fprintf(&Logger.Log.SSH,"Client '%v', Channels %v, Failed Handling : %v\n",conn.RemoteAddr(),num,err)
|
|
|
+ return
|
|
|
+ }else{
|
|
|
+ r:= <-req
|
|
|
+ r.Reply(true,nil)
|
|
|
+ chlist.Channels.Set(num,Utils.AvaliableChannel{
|
|
|
+ Channel: ch,
|
|
|
+ Term: terminal.NewTerminal(ch,"NSMC >"),
|
|
|
+ UserContext: Utils.UserContextType{
|
|
|
+ PWD: "/",
|
|
|
+ },
|
|
|
+ })
|
|
|
+ go Terminal.HandlerTerminal(ClientList,conn, num)
|
|
|
+ }
|
|
|
+ }else{
|
|
|
+ nch.Reject(ssh.UnknownChannelType, "Unknown Channel Type")
|
|
|
+ fmt.Fprintf(&Logger.Log.SSH,"Client '%v', Channels %v, Rejected : Unknown Channel Type '%v'\n",conn.RemoteAddr(),num,nch.ChannelType())
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func SSHPasswordCallback(conn ssh.ConnMetadata, password []byte) (*ssh.Permissions, error) {
|
|
|
+ val, ok := SSHServerConf.passwd[conn.User()]
|
|
|
+ if(!ok){
|
|
|
+ fmt.Fprintf(&Logger.Log.SSH,"Client '%v' Auth Failed : Unknown User '%v'\n",conn.RemoteAddr(),conn.User())
|
|
|
+ return nil,fmt.Errorf("Password Error or User '%v' Does Not Exist..",conn.User())
|
|
|
+ }
|
|
|
+ err := bcrypt.CompareHashAndPassword([]byte(val),password)
|
|
|
+ if(err != nil){
|
|
|
+ fmt.Fprintf(&Logger.Log.SSH,"Client '%v', User '%v', Auth Failed : Password Error, Detail: '%v'\n",conn.RemoteAddr(),conn.User(),err)
|
|
|
+ return nil,fmt.Errorf("Password Error or User '%v' Does Not Exist..",conn.User())
|
|
|
+ }
|
|
|
+ return nil,nil
|
|
|
+}
|
|
|
+
|
|
|
+func SSHPublicKeyCallback(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error){
|
|
|
+ return nil,fmt.Errorf("PublicKey Auth Not Support Yet.")
|
|
|
+}
|
|
|
+
|
|
|
+func SSHAuthLogCallback(conn ssh.ConnMetadata, method string, err error){
|
|
|
+ fmt.Fprintf(&Logger.Log.SSH,"AuthFailed. Info: client='%v', user='%v', method='%v'. Err: %v\n",conn.RemoteAddr(),conn.User(),method,err)
|
|
|
+}
|
|
|
+
|
|
|
+func GenerateRSAKey(keyfn string,keyLength int) {
|
|
|
+ privateKey, err := rsa.GenerateKey(rand.Reader, keyLength)
|
|
|
+ if err != nil {
|
|
|
+ fmt.Fprintln(&Logger.Log.SysFatal,"Failed to Generate RSA Private Key: ", err)
|
|
|
+ mainThreadBlockChan <- 1
|
|
|
+ return
|
|
|
+ }
|
|
|
+ derStream := x509.MarshalPKCS1PrivateKey(privateKey)
|
|
|
+ block := &pem.Block{
|
|
|
+ Type: "RSA PRIVATE KEY",
|
|
|
+ Bytes: derStream,
|
|
|
+ }
|
|
|
+ file, err := os.Create(keyfn)
|
|
|
+ if err != nil {
|
|
|
+ fmt.Fprintln(&Logger.Log.SysFatal,"Failed to Create Key File: ", err)
|
|
|
+ mainThreadBlockChan <- 1
|
|
|
+ return
|
|
|
+ }
|
|
|
+ err = pem.Encode(file, block)
|
|
|
+ if err != nil {
|
|
|
+ fmt.Fprintln(&Logger.Log.SysFatal,"Failed to Encode Key: ", err)
|
|
|
+ mainThreadBlockChan <- 1
|
|
|
+ return
|
|
|
+ }
|
|
|
+ fmt.Fprintln(&Logger.Log.SysInfo,"SSH Host Key Generated Successfully..")
|
|
|
+ fmt.Println("SSH Host Key Generated Successfully..")
|
|
|
+}
|