func (sc *SshSession) RequestPty(term string, h int, w int, termmodes ssh.TerminalModes) (err error) { if sc.session != nil { return sc.session.RequestPty(term, h, w, termmodes) } else { var tm []byte for k, v := range termmodes { kv := struct { Key byte Val uint32 }{k, v} tm = append(tm, ssh.Marshal(&kv)...) } tm = append(tm, tty_OP_END) req := ptyRequestMsg{ Term: term, Columns: uint32(w), Rows: uint32(h), Width: uint32(w * 8), Height: uint32(h * 8), Modelist: string(tm), } ok, err := sc.ch.SendRequest("pty-req", true, ssh.Marshal(&req)) if err == nil && !ok { err = errors.New("ssh: pty-req failed") } return err } }
func (c *client) insertCert(s interface{}, cert *ssh.Certificate, comment string, constraints []byte) error { var req []byte switch k := s.(type) { case *rsa.PrivateKey: if len(k.Primes) != 2 { return fmt.Errorf("agent: unsupported RSA key with %d primes", len(k.Primes)) } k.Precompute() req = ssh.Marshal(rsaCertMsg{ Type: cert.Type(), CertBytes: cert.Marshal(), D: k.D, Iqmp: k.Precomputed.Qinv, P: k.Primes[0], Q: k.Primes[1], Comments: comment, Constraints: constraints, }) case *dsa.PrivateKey: req = ssh.Marshal(dsaCertMsg{ Type: cert.Type(), CertBytes: cert.Marshal(), X: k.X, Comments: comment, }) case *ecdsa.PrivateKey: req = ssh.Marshal(ecdsaCertMsg{ Type: cert.Type(), CertBytes: cert.Marshal(), D: k.D, Comments: comment, }) default: return fmt.Errorf("agent: unsupported key type %T", s) } // if constraints are present then the message type needs to be changed. if len(constraints) != 0 { req[0] = agentAddIdConstrained } signer, err := ssh.NewSignerFromKey(s) if err != nil { return err } if bytes.Compare(cert.Key.Marshal(), signer.PublicKey().Marshal()) != 0 { return errors.New("agent: signer and cert have different public key") } resp, err := c.call(req) if err != nil { return err } if _, ok := resp.(*successAgentMsg); ok { return nil } return errors.New("agent: failure") }
// Insert adds a private key to the agent. func (c *client) insertKey(s interface{}, comment string, constraints []byte) error { var req []byte switch k := s.(type) { case *rsa.PrivateKey: if len(k.Primes) != 2 { return fmt.Errorf("agent: unsupported RSA key with %d primes", len(k.Primes)) } k.Precompute() req = ssh.Marshal(rsaKeyMsg{ Type: ssh.KeyAlgoRSA, N: k.N, E: big.NewInt(int64(k.E)), D: k.D, Iqmp: k.Precomputed.Qinv, P: k.Primes[0], Q: k.Primes[1], Comments: comment, Constraints: constraints, }) case *dsa.PrivateKey: req = ssh.Marshal(dsaKeyMsg{ Type: ssh.KeyAlgoDSA, P: k.P, Q: k.Q, G: k.G, Y: k.Y, X: k.X, Comments: comment, Constraints: constraints, }) case *ecdsa.PrivateKey: nistID := fmt.Sprintf("nistp%d", k.Params().BitSize) req = ssh.Marshal(ecdsaKeyMsg{ Type: "ecdsa-sha2-" + nistID, Curve: nistID, KeyBytes: elliptic.Marshal(k.Curve, k.X, k.Y), D: k.D, Comments: comment, Constraints: constraints, }) default: return fmt.Errorf("agent: unsupported key type %T", s) } // if constraints are present then the message type needs to be changed. if len(constraints) != 0 { req[0] = agentAddIdConstrained } resp, err := c.call(req) if err != nil { return err } if _, ok := resp.(*successAgentMsg); ok { return nil } return errors.New("agent: failure") }
func (sess *session) sendExitMessage(err error) { logger := sess.logger.Session("send-exit-message") logger.Info("started") defer logger.Info("finished") if err != nil { logger.Error("building-exit-message-from-error", err) } if err == nil { _, sendErr := sess.channel.SendRequest("exit-status", false, ssh.Marshal(exitStatusMsg{})) if sendErr != nil { logger.Error("send-exit-status-failed", sendErr) } return } exitError, ok := err.(*exec.ExitError) if !ok { exitMessage := exitStatusMsg{Status: 255} _, sendErr := sess.channel.SendRequest("exit-status", false, ssh.Marshal(exitMessage)) if sendErr != nil { logger.Error("send-exit-status-failed", sendErr) } return } waitStatus, ok := exitError.Sys().(syscall.WaitStatus) if !ok { exitMessage := exitStatusMsg{Status: 255} _, sendErr := sess.channel.SendRequest("exit-status", false, ssh.Marshal(exitMessage)) if sendErr != nil { logger.Error("send-exit-status-failed", sendErr) } return } if waitStatus.Signaled() { exitMessage := exitSignalMsg{ Signal: string(signals.SSHSignals[waitStatus.Signal()]), CoreDumped: waitStatus.CoreDump(), } _, sendErr := sess.channel.SendRequest("exit-signal", false, ssh.Marshal(exitMessage)) if sendErr != nil { logger.Error("send-exit-status-failed", sendErr) } return } exitMessage := exitStatusMsg{Status: uint32(waitStatus.ExitStatus())} _, sendErr := sess.channel.SendRequest("exit-status", false, ssh.Marshal(exitMessage)) if sendErr != nil { logger.Error("send-exit-status-failed", sendErr) } }
func (ss *SecureShell) resize(resized <-chan os.Signal, session SecureSession, terminalFd uintptr, initialWidth, initialHeight int) { type resizeMessage struct { Width uint32 Height uint32 PixelWidth uint32 PixelHeight uint32 } var previousWidth, previousHeight int previousWidth = initialWidth previousHeight = initialHeight for _ = range resized { width, height := ss.Term.GetWinsize(terminalFd) if width == previousWidth && height == previousHeight { continue } message := resizeMessage{ Width: uint32(width), Height: uint32(height), } session.SendRequest("window-change", false, ssh.Marshal(message)) previousWidth = width previousHeight = height } }
func (f *File) ReadAt(b []byte, off int64) (n int, err error) { req := fxpReadMsg{Handle: f.handle} fxpCh, err := f.sftp.chans.newChan() if err != nil { return } defer f.sftp.chans.remove(fxpCh.id) req.SetID(fxpCh.id) for len(b) > 0 { req.Offset = uint64(n) + uint64(off) req.Length = uint32(min(uint64(len(b)), maxDataBytes)) if err = f.sftp.writePacket(ssh.Marshal(req)); err != nil { return } resp := fxpCh.waitForResponse() switch msg := resp.(type) { case *fxpStatusResp: if msg.Status == eof { err = io.EOF return } err = msg return case *fxpDataResp: n += copy(b, msg.Data) b = b[len(msg.Data):] default: panic("unexpected message type returned from server") } } return }
func handleSSHChannel(newChan ssh.NewChannel, session *Session) { ch, reqs, err := newChan.Accept() if err != nil { log.Println("handle channel failed:", err) return } exitCh := make(chan int) go func() { status := struct{ Status uint32 }{uint32(<-exitCh)} _, err = ch.SendRequest("exit-status", false, ssh.Marshal(&status)) assert(err) ch.Close() }() for req := range reqs { go func(req *ssh.Request) { if req.WantReply { req.Reply(true, nil) } switch req.Type { case "exec": var payload = struct{ Value string }{} ssh.Unmarshal(req.Payload, &payload) line := strings.Trim(payload.Value, "\n") var args []string if line != "" { args = strings.Split(line, " ") } RunCmd(args, ch, ch, ch.Stderr(), exitCh, session) } }(req) } }
func (c *secureShell) resize(resized <-chan os.Signal, session SecureSession, terminalFd uintptr) { type resizeMessage struct { Width uint32 Height uint32 PixelWidth uint32 PixelHeight uint32 } var previousWidth, previousHeight int for range resized { width, height := c.getWindowDimensions(terminalFd) if width == previousWidth && height == previousHeight { continue } message := resizeMessage{ Width: uint32(width), Height: uint32(height), } _, _ = session.SendRequest("window-change", false, ssh.Marshal(message)) previousWidth = width previousHeight = height } }
func (f *File) WriteAt(b []byte, off int64) (n int, err error) { req := fxpWriteMsg{Handle: f.handle} fxpCh, err := f.sftp.chans.newChan() if err != nil { return } defer f.sftp.chans.remove(fxpCh.id) req.SetID(fxpCh.id) for len(b) > 0 { req.Offset = uint64(n) + uint64(off) l := min(uint64(len(b)), maxDataBytes) req.Data = b[:l] if err = f.sftp.writePacket(ssh.Marshal(req)); err != nil { return } resp := fxpCh.waitForResponse() switch msg := resp.(type) { case *fxpStatusResp: if msg.Status != OK { err = msg return } n += int(l) b = b[l:] default: panic("unexpected message type returned from server") } } return }
func forwardLocalConn(logger lager.Logger, localConn net.Conn, conn *ssh.ServerConn, forwardIP string, forwardPort uint32) { defer localConn.Close() var req forwardTCPIPChannelRequest req.ForwardIP = forwardIP req.ForwardPort = forwardPort host, port, err := net.SplitHostPort(localConn.RemoteAddr().String()) if err != nil { logger.Error("failed-to-split-host-port", err) return } req.OriginIP = host _, err = fmt.Sscanf(port, "%d", &req.OriginPort) if err != nil { logger.Error("failed-to-parse-port", err) return } channel, reqs, err := conn.OpenChannel("forwarded-tcpip", ssh.Marshal(req)) if err != nil { logger.Error("failed-to-open-channel", err) return } defer channel.Close() go func() { for r := range reqs { logger.Info("ignoring-request", lager.Data{ "type": r.Type, }) r.Reply(false, nil) } }() wg := new(sync.WaitGroup) pipe := func(to io.WriteCloser, from io.ReadCloser) { // if either end breaks, close both ends to ensure they're both unblocked, // otherwise io.Copy can block forever if e.g. reading after write end has // gone away defer to.Close() defer from.Close() defer wg.Done() io.Copy(to, from) } wg.Add(1) go pipe(localConn, channel) wg.Add(1) go pipe(channel, localConn) wg.Wait() }
func marshalKey(k *Key) []byte { var record struct { Blob []byte Comment string } record.Blob = k.Marshal() record.Comment = k.Comment return ssh.Marshal(&record) }
func (ek *Ed25519PublicKey) Marshal() []byte { wirekey := struct { Name string Pub []byte }{ ek.Type(), (*ek.bytes)[:], } return ssh.Marshal(&wirekey) }
// init starts the SFTP protocol by negotiating the protocol version to use and // starts the response handler in a goroutine. func (s *Client) init() error { msg := fxpInitMsg{ Version: 3, } if err := s.writePacket(ssh.Marshal(msg)); err != nil { return err } packet, err := s.readOnePacket() if err != nil { return err } resp, err := decodeClient(packet) if err != nil { return err } switch resp := resp.(type) { case *fxpVersionMsg: if resp.Version != 3 { return errors.New("only version 3 of Client protocol supported") } default: return errors.New("invalid packet received during initialization") } vers := resp.(*fxpVersionMsg) s.exts = make(map[string]extension) if len(vers.Ext) > 0 { exts := vers.Ext for len(exts) > 0 { ew := extensionWire{} if err := ssh.Unmarshal(exts, &ew); err != nil { return err } if len(exts) < 2 { break } exts = ew.Rest e := extension{ Name: ew.Name, Data: ew.Data, } // OpenSSH's sftp-server implementation specifies that // the data portion of an extension is an ASCII-encoded // version number. This is not part of the SFTP // specification, however. if n, err := strconv.Atoi(ew.Data); err == nil { e.version = n } s.exts[e.Name] = e } } go s.mainLoop() return nil }
// sendRequests sends a request to the server and returns a new channel on // which the response will be sent. func (s *Client) sendRequest(req ider) (*fxpChan, error) { fxpCh, err := s.chans.newChan() if err != nil { return nil, err } req.SetID(fxpCh.id) if err := s.writePacket(ssh.Marshal(req)); err != nil { return nil, err } return fxpCh, nil }
func (inst *instance) SendWindowSize() { if w, h, err := inst.terminal.GetSize(); err != nil { color.Redln("Error getting term size:", err) } else { msg := ssh.Marshal(&winchMsg{uint32(w), uint32(h), 0, 0}) _, err = inst.session.SendRequest("window-change", false, msg) if err != nil { color.Redln("Error sending winch:", err) } } }
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 (s *Session) Resize(width, height int) error { message := struct { Width uint32 Height uint32 PixelWidth uint32 PixelHeight uint32 }{uint32(width), uint32(height), 0, 0} _, err := s.sshSession.SendRequest("window-change", false, ssh.Marshal(message)) return err }
// Exit sends an exit-status request to the channel based on the err. func (h *sshHandler) Exit(err error) error { defer h.channel.Close() status, err := exitStatus(err) if !h.assert("exit", err) { _, err := h.channel.SendRequest("exit-status", false, ssh.Marshal(&status)) h.assert("status", err) return err } return err }
func sendCmdResult(channel ssh.Channel, result []byte, statusCode uint32) error { if _, err := channel.Write(result); err != nil { return fmt.Errorf("failed to write to ssh-channel: %v", err) } status := struct { Status uint32 }{ statusCode, } _, err := channel.SendRequest("exit-status", false, ssh.Marshal(&status)) if err != nil { return fmt.Errorf("failed to SendRequest: %v", err) } return nil }
func (s *server) processRequestBytes(reqData []byte) []byte { rep, err := s.processRequest(reqData) if err != nil { if err != errLocked { // TODO(hanwen): provide better logging interface? log.Printf("agent %d: %v", reqData[0], err) } return []byte{agentFailure} } if err == nil && rep == nil { return []byte{agentSuccess} } return ssh.Marshal(rep) }
func (sess *session) sendSCPExitMessage(err error) { logger := sess.logger.Session("send-scp-exit-message") logger.Info("started") defer logger.Info("finished") var exitMessage exitStatusMsg if err != nil { logger.Error("building-scp-exit-message-from-error", err) exitMessage = exitStatusMsg{Status: 1} } _, sendErr := sess.channel.SendRequest("exit-status", false, ssh.Marshal(exitMessage)) if sendErr != nil { logger.Error("send-exit-status-failed", sendErr) } }
func handleSSHChannel(newChan ssh.NewChannel) { ch, reqs, err := newChan.Accept() if err != nil { log.Println("handle channel failed:", err) return } for req := range reqs { go func(req *ssh.Request) { if req.WantReply { req.Reply(true, nil) } switch req.Type { case "exec": defer ch.Close() var payload = struct{ Value string }{} ssh.Unmarshal(req.Payload, &payload) line := strings.Trim(payload.Value, "\n") var args []string if line != "" { args = strings.Split(line, " ") } cmd := exec.Command("/bin/envy", args...) cmd.Stdout = ch cmd.Stderr = ch.Stderr() err := cmd.Run() status := struct{ Status uint32 }{0} if err != nil { if exiterr, ok := err.(*exec.ExitError); ok { if stat, ok := exiterr.Sys().(syscall.WaitStatus); ok { status = struct{ Status uint32 }{uint32(stat.ExitStatus())} } else { assert(err) } } } _, err = ch.SendRequest("exit-status", false, ssh.Marshal(&status)) assert(err) return } }(req) } }
func (s *sshServer) run(c *gc.C) { netconn, err := s.listener.Accept() c.Assert(err, jc.ErrorIsNil) defer func() { err := netconn.Close() c.Assert(err, jc.ErrorIsNil) }() conn, chans, reqs, err := cryptossh.NewServerConn(netconn, s.cfg) c.Assert(err, jc.ErrorIsNil) s.client = cryptossh.NewClient(conn, chans, reqs) var wg sync.WaitGroup defer wg.Wait() sessionChannels := s.client.HandleChannelOpen("session") c.Assert(sessionChannels, gc.NotNil) for newChannel := range sessionChannels { c.Assert(newChannel.ChannelType(), gc.Equals, "session") channel, reqs, err := newChannel.Accept() c.Assert(err, jc.ErrorIsNil) wg.Add(1) go func() { defer wg.Done() defer channel.Close() for req := range reqs { switch req.Type { case "exec": c.Assert(req.WantReply, jc.IsTrue) n := binary.BigEndian.Uint32(req.Payload[:4]) command := string(req.Payload[4 : n+4]) c.Assert(command, gc.Equals, testCommandFlat) req.Reply(true, nil) channel.Write([]byte("abc value\n")) _, err := channel.SendRequest("exit-status", false, cryptossh.Marshal(&struct{ n uint32 }{0})) c.Assert(err, jc.ErrorIsNil) return default: c.Fatalf("Unexpected request type: %v", req.Type) } } }() } }
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) } }
// Sign has the agent sign the data using a protocol 2 key as defined // in [PROTOCOL.agent] section 2.6.2. func (c *client) Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error) { req := ssh.Marshal(signRequestAgentMsg{ KeyBlob: key.Marshal(), Data: data, }) msg, err := c.call(req) if err != nil { return nil, err } switch msg := msg.(type) { case *signResponseAgentMsg: var sig ssh.Signature if err := ssh.Unmarshal(msg.SigBlob, &sig); err != nil { return nil, err } return &sig, nil case *failureAgentMsg: return nil, errors.New("agent: failed to sign challenge") } panic("unreachable") }
}) Context("when a window change request is received", func() { type winChangeMsg struct { Columns uint32 Rows uint32 WidthPx uint32 HeightPx uint32 } var result []byte Context("before a pty is allocated", func() { BeforeEach(func() { _, err := session.SendRequest("window-change", false, ssh.Marshal(winChangeMsg{ Rows: 50, Columns: 132, })) Expect(err).NotTo(HaveOccurred()) err = session.RequestPty("vt100", 43, 80, ssh.TerminalModes{}) Expect(err).NotTo(HaveOccurred()) result, err = session.Output("stty size") Expect(err).NotTo(HaveOccurred()) }) It("ignores the request", func() { Expect(result).To(ContainSubstring("43 80")) }) })
func (s *SignalMsg) Marshal() []byte { return ssh.Marshal(*s) }
func (wc *WindowChangeMsg) Marshal() []byte { return ssh.Marshal(*wc) }
func (s *ContainersMsg) Marshal() []byte { return ssh.Marshal(*s) }
It("the handler returns", func() { Consistently(completed).ShouldNot(Receive()) Expect(echoHandler.HandleConnectionCallCount()).To(Equal(1)) echoConn := echoHandler.HandleConnectionArgsForCall(0) echoConn.Close() Eventually(completed).Should(Receive()) }) }) }) }) Context("when the direct-tcpip extra data fails to unmarshal", func() { It("rejects the open channel request", func() { _, _, err := client.OpenChannel("direct-tcpip", ssh.Marshal(struct{ Bogus int }{Bogus: 1234})) Expect(err).To(Equal(&ssh.OpenChannelError{ Reason: ssh.ConnectionFailed, Message: "Failed to parse open channel message", })) }) }) Context("when dialing the target fails", func() { BeforeEach(func() { testDialer.DialStub = func(net, addr string) (net.Conn, error) { return nil, errors.New("woops") } })