func (sess *session) handleExecRequest(request *ssh.Request) { logger := sess.logger.Session("handle-exec-request") type execMsg struct { Command string } var execMessage execMsg err := ssh.Unmarshal(request.Payload, &execMessage) if err != nil { logger.Error("unmarshal-failed", err) if request.WantReply { request.Reply(false, nil) } return } if scpRegex.MatchString(execMessage.Command) { logger.Info("handling-scp-command", lager.Data{"Command": execMessage.Command}) sess.executeSCP(execMessage.Command, request) } else { logger.Info("executeShell", lager.Data{"request": request, "command": execMessage.Command}) sess.executeShell(request, "-c", execMessage.Command) } }
func (s *server) runReceive( req *ssh.Request, sshConn *ssh.ServerConn, channel ssh.Channel, repoName string, parts []string, connData string, ) func() error { return func() error { req.Reply(true, nil) // We processed. Yay. if !strings.Contains(sshConn.Permissions.Extensions["apps"], repoName) { return errBuildAppPerm } repo := repoName + ".git" recvErr := git.Receive( repo, parts[0], s.gitHome, channel, sshConn.Permissions.Extensions["fingerprint"], sshConn.Permissions.Extensions["user"], connData, s.receivetype, ) if recvErr != nil { return recvErr } return nil } }
func (h *sshHandler) handleEnv(req *ssh.Request) { var pair EnvVar ssh.Unmarshal(req.Payload, &pair) envvar := fmt.Sprintf("%s=%s", pair.Name, pair.Value) h.Env = append(h.Env, envvar) req.Reply(true, nil) }
func (sess *session) handleSignalRequest(request *ssh.Request) { logger := sess.logger.Session("handle-signal-request") type signalMsg struct { Signal string } var signalMessage signalMsg err := ssh.Unmarshal(request.Payload, &signalMessage) if err != nil { logger.Error("unmarshal-failed", err) if request.WantReply { request.Reply(false, nil) } return } sess.Lock() defer sess.Unlock() cmd := sess.command if cmd != nil { signal := signals.SyscallSignals[ssh.Signal(signalMessage.Signal)] err := sess.runner.Signal(cmd, signal) if err != nil { logger.Error("process-signal-failed", err) } } if request.WantReply { request.Reply(true, nil) } }
func (sess *session) handlePtyRequest(request *ssh.Request) { logger := sess.logger.Session("handle-pty-request") var ptyRequestMessage ptyRequestMsg err := ssh.Unmarshal(request.Payload, &ptyRequestMessage) if err != nil { logger.Error("unmarshal-failed", err) if request.WantReply { request.Reply(false, nil) } return } sess.Lock() defer sess.Unlock() sess.allocPty = true sess.ptyRequest = ptyRequestMessage sess.env["TERM"] = ptyRequestMessage.Term if request.WantReply { request.Reply(true, nil) } }
func rejectRequest(req *ssh.Request) error { fmt.Fprintf(sshServerDebugStream, "ssh rejecting request, type: %s\n", req.Type) err := req.Reply(false, []byte{}) if err != nil { fmt.Fprintf(sshServerDebugStream, "ssh request reply had error: %v\n", err) } return err }
// Ping handles a simple test SSH exec. // // Returns the string PONG and exit status 0. // // Params: // - channel (ssh.Channel): The channel to respond on. // - request (*ssh.Request): The request. // func Ping(channel ssh.Channel, req *ssh.Request) error { log.Info("PING") if _, err := channel.Write([]byte("pong")); err != nil { log.Err("Failed to write to channel: %s", err) } sendExitStatus(0, channel) req.Reply(true, nil) return nil }
func (chsvr *sshSessionChannelServer) handleSubsystem(req *ssh.Request) error { defer func() { err1 := chsvr.ch.CloseWrite() err2 := chsvr.ch.Close() fmt.Fprintf(sshServerDebugStream, "ssh server subsystem request complete, err: %v %v\n", err1, err2) }() subsystemReq := &sshSubsystemRequest{} if err := ssh.Unmarshal(req.Payload, subsystemReq); err != nil { return rejectRequestUnmarshalError(req, subsystemReq, err) } // reply to the ssh client // no idea if this is actually correct spec-wise. // just enough for an sftp server to start. if subsystemReq.Name != "sftp" { return req.Reply(false, nil) } req.Reply(true, nil) if !chsvr.svr.useSubsystem { // use the openssh sftp server backend; this is to test the ssh code, not the sftp code, // or is used for comparison between our sftp subsystem and the openssh sftp subsystem cmd := exec.Command(*testSftp, "-e", "-l", "DEBUG") // log to stderr cmd.Stdin = chsvr.ch cmd.Stdout = chsvr.ch cmd.Stderr = sftpServerDebugStream if err := cmd.Start(); err != nil { return err } return cmd.Wait() } sftpServer, err := NewServer( chsvr.ch, chsvr.ch, WithDebug(sftpServerDebugStream), ) if err != nil { return err } // wait for the session to close runErr := sftpServer.Serve() exitStatus := uint32(1) if runErr == nil { exitStatus = uint32(0) } _, exitStatusErr := chsvr.ch.SendRequest("exit-status", false, ssh.Marshal(sshSubsystemExitStatus{exitStatus})) return exitStatusErr }
func (h *sshHandler) Request(req *ssh.Request) { switch req.Type { case "exec": h.handleExec(req) case "pty-req": h.handlePty(req) case "window-change": h.handleWinch(req) default: if req.WantReply { req.Reply(true, nil) } } }
func (sess *session) executeSCP(command string, request *ssh.Request) { logger := sess.logger.Session("execute-scp") if request.WantReply { request.Reply(true, nil) } copier, err := scp.NewFromCommand(command, sess.channel, sess.channel, sess.channel.Stderr(), logger) if err == nil { err = copier.Copy() } sess.sendSCPExitMessage(err) sess.destroy() }
func (c *Channel) HandleRequest(request *ssh.Request) { glog.V(9).Infof("request received: type = %s, want_reply = %v, payload = %v", request.Type, request.WantReply, request.Payload) // check parameters ok := false switch request.Type { case "env": // just ignore the env settings from client ok = true case "shell": // let client open shell without any command if len(request.Payload) > 0 { break } ok = true } // reply to client if request.WantReply { if err := request.Reply(ok, nil); err != nil { glog.Warningf("failed to reply to request: %s", err) } } // do actual work here switch request.Type { case "shell": defer c.Close() status := c.Session().Gateway().Status() encoded, err := json.MarshalIndent(status, "", " ") if err != nil { glog.Warningf("failed to marshal status: %s", err) break } if _, err := c.Write(encoded); err != nil { glog.Warningf("failed to send status: %s", err) break } if _, err := c.Write([]byte("\n")); err != nil { glog.Warningf("failed to send status: %s", err) break } } }
func (s *Session) HandleRequest(request *ssh.Request) { glog.V(9).Infof("request received: type = %s, want_reply = %v, payload = %v", request.Type, request.WantReply, request.Payload) ok := false switch request.Type { case "tcpip-forward": request, err := UnmarshalForwardRequest(request.Payload) if err != nil { glog.Errorf("failed to decode request: %s", err) break } if request.Port == 0 { glog.Errorf("requested forwarding port is not allowed: %d", request.Port) break } if err := s.RegisterService(request.Host, uint16(request.Port)); err != nil { glog.Errorf("failed to register service in session: %s", err) break } ok = true case "cancel-tcpip-forward": request, err := UnmarshalForwardRequest(request.Payload) if err != nil { glog.Errorf("failed to decode request: %s", err) break } if err := s.DeregisterService(request.Host, uint16(request.Port)); err != nil { glog.Errorf("failed to register service in session: %s", err) break } ok = true } if request.WantReply { if err := request.Reply(ok, nil); err != nil { glog.Warningf("failed to reply to request: %s", err) } } }
//Reply handles the operation between a req and channel func Reply(req *ssh.Request, dest ssh.Channel, c *ConnInsight) { dx, err := dest.SendRequest(req.Type, req.WantReply, req.Payload) checkError(err, fmt.Sprintf("Request %s processed", req.Type)) if req.WantReply { req.Reply(dx, nil) } meta := map[string]interface{}{ "type": "reply", "name": req.Type, "payload": req.Payload, } c.Aux().Emit(meta) }
/* handleRequest handles proxying a request r via sr, which should be a closure which sends the request passed to it on a channel or SSH connection. If the request can't be proxied, cl will be called to close whatever sr sends r on. info is used for logging. */ func handleRequest( r *ssh.Request, sr func( name string, wantReply bool, payload []byte, ) (bool, []byte, error), cl func() error, info string) { /* If this is the wrong sort of request, respond no */ if s, ok := delayedReqs[r.Type]; ok { log.Printf( "%v Type:%v Delay:%v", info, r.Type, s, ) time.Sleep(s) } logRequest(r, info) /* Ask the other side */ ok, data, err := sr(r.Type, r.WantReply, r.Payload) if nil != err { log.Printf( "%v Unable to receive reply for %v request: %v", info, r.Type, err, ) cl() return } logRequestResponse(r, ok, data, info) /* Proxy back */ if err := r.Reply(ok, nil); nil != err { log.Printf( "%v Unable to reply to %v request: %v", info, r.Type, err, ) cl() } }
func forwardRequest(req *ssh.Request, channel ssh.Channel) error { if string(req.Type) != "subsystem" && string(req.Type) != "exit-status" { req.Reply(false, nil) if req.Type == "env" { return nil } return fmt.Errorf("Ignoring unsupported request type: %s", string(req.Type)) } reply, err := channel.SendRequest(req.Type, req.WantReply, req.Payload) if err != nil { return err } if req.WantReply { req.Reply(reply, nil) } return nil }
func (chsvr *sshSessionChannelServer) handleEnv(req *ssh.Request) error { envReq := &sshEnvRequest{} if err := ssh.Unmarshal(req.Payload, envReq); err != nil { return rejectRequestUnmarshalError(req, envReq, err) } req.Reply(true, nil) found := false for i, envstr := range chsvr.env { if strings.HasPrefix(envstr, envReq.Envvar+"=") { found = true chsvr.env[i] = envReq.Envvar + "=" + envReq.Value } } if !found { chsvr.env = append(chsvr.env, envReq.Envvar+"="+envReq.Value) } return nil }
func (h *sshHandler) handleExec(req *ssh.Request) { h.Lock() defer h.Unlock() var payload = struct{ Value string }{} ssh.Unmarshal(req.Payload, &payload) cmdline := payload.Value // Initialize Cmd var cmd *exec.Cmd if *shell { shellcmd := flag.Arg(1) + " " + cmdline cmd = exec.Command(os.Getenv("SHELL"), "-c", shellcmd) } else { cmdargs, err := shlex.Split(cmdline) if h.assert("exec shlex.Split", err) { h.channel.Close() return } cmd = exec.Command(h.ExecHandler[0], append(h.ExecHandler[1:], cmdargs...)...) } cmd.Env = append(h.Env, "SSH_ORIGINAL_COMMAND="+cmdline) cmd.Stdout = h.stdout cmd.Stderr = h.stderr // cmd.Wait closes the stdin when it's done, so we need to proxy it through a pipe stdinPipe, err := cmd.StdinPipe() if h.assert("exec cmd.StdinPipe", err) { h.channel.Close() return } go io.Copy(stdinPipe, h.channel) if req.WantReply { req.Reply(true, nil) } // We run inline to prevent concurrent exec requests for the channel as the lock is held. h.Exit(cmd.Run()) }
// Payload: int: command size, string: command func handleExec(ch ssh.Channel, req *ssh.Request) { command := string(req.Payload[4:]) gitCmds := []string{"git-receive-pack", "git-upload-pack"} valid := false for _, cmd := range gitCmds { if strings.HasPrefix(command, cmd) { valid = true } } req.Reply(true, []byte("0\r\n")) if !valid { ch.Write([]byte("command is not a GIT command\r\n")) ch.Close() return } ch.Write([]byte("well done!\r\n")) ch.Close() }
func (sess *session) handleSubsystemRequest(request *ssh.Request) { logger := sess.logger.Session("handle-subsystem-request") logger.Info("starting") defer logger.Info("finished") type subsysMsg struct { Subsystem string } var subsystemMessage subsysMsg err := ssh.Unmarshal(request.Payload, &subsystemMessage) if err != nil { logger.Error("unmarshal-failed", err) if request.WantReply { request.Reply(false, nil) } return } if subsystemMessage.Subsystem != "sftp" { logger.Info("unsupported-subsystem", lager.Data{"subsystem": subsystemMessage.Subsystem}) if request.WantReply { request.Reply(false, nil) } return } lagerWriter := helpers.NewLagerWriter(logger.Session("sftp-server")) sftpServer, err := sftp.NewServer(sess.channel, sess.channel, sftp.WithDebug(lagerWriter)) if err != nil { logger.Error("sftp-new-server-failed", err) if request.WantReply { request.Reply(false, nil) } return } if request.WantReply { request.Reply(true, nil) } logger.Info("starting-server") go func() { defer sess.destroy() err = sftpServer.Serve() if err != nil { logger.Error("sftp-serve-error", err) } }() }
/* handleRequest handles a single request, which is proxied to rable and logged via lg. */ func handleRequest( r *ssh.Request, rable Requestable, lg *log.Logger, direction string, ) { rl := fmt.Sprintf( "Type:%q WantReply:%v Payload:%q Direction:%q", r.Type, r.WantReply, r.Payload, direction, ) /* Ignore certain requests, because we're bad people */ if IGNORENMS { for _, ir := range IGNOREREQUESTS { if 1 == subtle.ConstantTimeCompare( []byte(r.Type), []byte(ir), ) { lg.Printf("Ignoring Request %s", rl) return } } } /* Proxy to server */ ok, data, err := rable.SendRequest(r.Type, r.WantReply, r.Payload) if nil != err { lg.Printf("Unable to proxy request %s Error:%v", rl, err) return } /* TODO: Pass to server */ if err := r.Reply(ok, data); nil != err { lg.Printf("Unable to respond to request %s Error:%v", rl, err) return } lg.Printf("Request %s Ok:%v Response:%q", rl, ok, data) }
func (h *sshHandler) handlePty(req *ssh.Request) { h.Lock() defer h.Unlock() if h.ptyShell != nil { // Only allow one pty per channel req.Reply(false, nil) return } width, height, okSize := parsePtyRequest(req.Payload) // Initialize Cmd var cmd *exec.Cmd if *shell { cmd = exec.Command(os.Getenv("SHELL")) } else { cmd = exec.Command(h.ExecHandler[0], h.ExecHandler[1:]...) } cmd.Env = h.Env // attachShell does cmd.Start() so we need to do cmd.Wait() later ptyShell, _, err := attachShell(cmd, h.stdout, h.channel) if h.assert("pty attachShell", err) { h.channel.Close() return } h.ptyShell = ptyShell if okSize { setWinsize(ptyShell.Fd(), width, height) } // Ready to receive input req.Reply(true, nil) // We run this concurrently so that the lock is released for window-change events. go h.Exit(cmd.Wait()) }
// This executes the commands requested to be run on the server. // Used to test the SSH secret backend. func executeServerCommand(ch ssh.Channel, req *ssh.Request) { command := string(req.Payload[4:]) cmd := exec.Command("/bin/bash", []string{"-c", command}...) req.Reply(true, nil) cmd.Stdout = ch cmd.Stderr = ch cmd.Stdin = ch err := cmd.Start() if err != nil { panic(fmt.Sprintf("Error starting the command: '%s'", err)) } go func() { _, err := cmd.Process.Wait() if err != nil { panic(fmt.Sprintf("Error while waiting for command to finish:'%s'", err)) } ch.Close() }() }
func (s *SSHServer) execHandler(channel ssh.Channel, request *ssh.Request) error { defer channel.Close() var payload = struct { Value string }{} if err := ssh.Unmarshal(request.Payload, &payload); err != nil { return fmt.Errorf("failed to unmarshal payload: %v", err) } result, status, err := s.runCmd(payload.Value) if err != nil { return fmt.Errorf("failed to run command: %v", err) } if err := sendCmdResult(channel, result, status); err != nil { return fmt.Errorf("failed to send result: %v", err) } if err := request.Reply(true, nil); err != nil { return fmt.Errorf("failed to send reply: %v", err) } return nil }
func (sess *session) handleWindowChangeRequest(request *ssh.Request) { logger := sess.logger.Session("handle-window-change") type windowChangeMsg struct { Columns uint32 Rows uint32 WidthPx uint32 HeightPx uint32 } var windowChangeMessage windowChangeMsg err := ssh.Unmarshal(request.Payload, &windowChangeMessage) if err != nil { logger.Error("unmarshal-failed", err) if request.WantReply { request.Reply(false, nil) } return } sess.Lock() defer sess.Unlock() if sess.allocPty { sess.ptyRequest.Columns = windowChangeMessage.Columns sess.ptyRequest.Rows = windowChangeMessage.Rows } if sess.ptyMaster != nil { err = setWindowSize(logger, sess.ptyMaster, sess.ptyRequest.Columns, sess.ptyRequest.Rows) if err != nil { logger.Error("failed-to-set-window-size", err) } } if request.WantReply { request.Reply(true, nil) } }
func (h *sshHandler) handlePty(req *ssh.Request) { h.Lock() defer h.Unlock() if h.ptyShell != nil { // Only allow one pty per channel req.Reply(false, nil) return } width, height, okSize := parsePtyRequest(req.Payload) cmd, err := handlerCmd(flag.Arg(0)) if err != nil { debug("failed handler init:", err) h.channel.Close() return } cmd.Env = h.Env // attachShell does cmd.Start() so we need to do cmd.Wait() later ptyShell, _, err := attachShell(cmd, h.stdout, h.channel) if h.assert("pty attachShell", err) { h.channel.Close() return } h.ptyShell = ptyShell if okSize { setWinsize(ptyShell.Fd(), width, height) } // Ready to receive input req.Reply(true, nil) // We run this concurrently so that the lock is released for window-change events. go h.Exit(cmd.Wait()) }
func (h *sshHandler) handleExec(req *ssh.Request) { h.Lock() defer h.Unlock() var payload = struct{ Value string }{} ssh.Unmarshal(req.Payload, &payload) cmdargs, err := shlex.Split(payload.Value) if err != nil { debug("failed exec split:", err) h.channel.Close() return } cmd, err := handlerCmd(flag.Arg(0), cmdargs...) if err != nil { debug("failed handler init:", err) h.channel.Close() return } cmd.Env = append(h.Env, "SSH_ORIGINAL_COMMAND="+strings.Join(cmdargs, " ")) cmd.Stdout = h.stdout cmd.Stderr = h.stderr // cmd.Wait closes the stdin when it's done, so we need to proxy it through a pipe stdinPipe, err := cmd.StdinPipe() if h.assert("exec cmd.StdinPipe", err) { h.channel.Close() return } go io.Copy(stdinPipe, h.channel) if req.WantReply { req.Reply(true, nil) } // We run inline to prevent concurrent exec requests for the channel as the lock is held. h.Exit(cmd.Run()) }
func (sess *session) executeShell(request *ssh.Request, args ...string) { logger := sess.logger.Session("execute-shell") sess.Lock() cmd, err := sess.createCommand(args...) if err != nil { sess.Unlock() logger.Error("failed-to-create-command", err) if request.WantReply { request.Reply(false, nil) } return } if request.WantReply { request.Reply(true, nil) } if sess.allocPty { err = sess.runWithPty(cmd) } else { err = sess.run(cmd) } sess.Unlock() if err != nil { sess.sendExitMessage(err) sess.destroy() return } go func() { err := sess.wait(cmd) sess.sendExitMessage(err) sess.destroy() }() }
func (chsvr *sshSessionChannelServer) handleSubsystem(req *ssh.Request) error { defer func() { err1 := chsvr.ch.CloseWrite() err2 := chsvr.ch.Close() fmt.Fprintf(sshServerDebugStream, "ssh server subsystem request complete, err: %v %v\n", err1, err2) }() subsystemReq := &sshSubsystemRequest{} if err := ssh.Unmarshal(req.Payload, subsystemReq); err != nil { return rejectRequestUnmarshalError(req, subsystemReq, err) } // reply to the ssh client // no idea if this is actually correct spec-wise. // just enough for an sftp server to start. if subsystemReq.Name == "sftp" { req.Reply(true, nil) sftpServer, err := sftp.NewServer(chsvr.ch, chsvr.ch, sftpServerDebugStream, 0, false, ".") if err != nil { return err } // wait for the session to close runErr := sftpServer.Serve() exitStatus := uint32(1) if runErr == nil { exitStatus = uint32(0) } _, exitStatusErr := chsvr.ch.SendRequest("exit-status", false, ssh.Marshal(sshSubsystemExitStatus{exitStatus})) return exitStatusErr } else { return req.Reply(false, nil) } }
func (sess *session) handleEnvironmentRequest(request *ssh.Request) { logger := sess.logger.Session("handle-environment-request") type envMsg struct { Name string Value string } var envMessage envMsg err := ssh.Unmarshal(request.Payload, &envMessage) if err != nil { logger.Error("unmarshal-failed", err) request.Reply(false, nil) return } sess.Lock() sess.env[envMessage.Name] = envMessage.Value sess.Unlock() if request.WantReply { request.Reply(true, nil) } }
func (s *Server) handleWS(ws *websocket.Conn) { // Before use, a handshake must be performed on the incoming net.Conn. sshConn, chans, reqs, err := ssh.NewServerConn(ws, s.sshConfig) if err != nil { s.Debugf("Failed to handshake (%s)", err) return } //load user var user *chshare.User if len(s.Users) > 0 { sid := string(sshConn.SessionID()) user = s.sessions[sid] defer delete(s.sessions, sid) } //verify configuration s.Debugf("Verifying configuration") //wait for request, with timeout var r *ssh.Request select { case r = <-reqs: case <-time.After(10 * time.Second): sshConn.Close() return } failed := func(err error) { r.Reply(false, []byte(err.Error())) } if r.Type != "config" { failed(s.Errorf("expecting config request")) return } c, err := chshare.DecodeConfig(r.Payload) if err != nil { failed(s.Errorf("invalid config")) return } //if user is provided, ensure they have //access to the desired remotes if user != nil { for _, r := range c.Remotes { addr := r.RemoteHost + ":" + r.RemotePort if !user.HasAccess(addr) { failed(s.Errorf("access to '%s' denied", addr)) return } } } //success! r.Reply(true, nil) //prepare connection logger s.wsCount++ id := s.wsCount l := s.Fork("session#%d", id) l.Debugf("Open") l.Debugf("Test") go func() { for r := range reqs { switch r.Type { case "ping": r.Reply(true, nil) default: l.Debugf("Unknown request: %s", r.Type) } } }() go func() { var streamCount int l.Debugf("look at the channels...") /*stream, reqs, err := sshConn.OpenChannel("session", nil) if err != nil { l.Debugf("Failed to open channel", err) return } go ssh.DiscardRequests(reqs) */ for ch := range chans { l.Debugf("channel %d", streamCount) //addr := string(ch.ExtraData()) stream, reqs, err := ch.Accept() if err != nil { l.Debugf("Failed to accept stream: %s", err) continue } streamCount++ //id := streamCount l.Debugf("Request Shell") var succ bool succ, err = stream.SendRequest("shell", true, nil) if !succ || err != nil { l.Debugf("Failed to get shell: %s", err) continue } go ssh.DiscardRequests(reqs) go io.Copy(stream, os.Stdout) go io.Copy(os.Stdin, stream) //go handleShellStream(l.Fork("stream#%d", id), stream, addr) } }() //go chshare.ConnectStreams(l, chans) sshConn.Wait() l.Debugf("Close") }