func parseECDSACert(req []byte) (*AddedKey, error) { var k ecdsaCertMsg if err := ssh.Unmarshal(req, &k); err != nil { return nil, err } pubKey, err := ssh.ParsePublicKey(k.CertBytes) if err != nil { return nil, err } cert, ok := pubKey.(*ssh.Certificate) if !ok { return nil, errors.New("agent: bad ECDSA certificate") } // An ECDSA publickey as marshaled by ecdsaPublicKey.Marshal() in keys.go var ecdsaPub struct { Name string ID string Key []byte } if err := ssh.Unmarshal(cert.Key.Marshal(), &ecdsaPub); err != nil { return nil, err } priv, err := unmarshalECDSA(ecdsaPub.ID, ecdsaPub.Key, k.D) if err != nil { return nil, err } return &AddedKey{PrivateKey: priv, Certificate: cert, Comment: k.Comments}, nil }
func (s *server) insertIdentity(req []byte) error { var record struct { Type string `sshtype:"17"` Rest []byte `ssh:"rest"` } if err := ssh.Unmarshal(req, &record); err != nil { return err } switch record.Type { case ssh.KeyAlgoRSA: var k rsaKeyMsg if err := ssh.Unmarshal(req, &k); err != nil { return err } priv := rsa.PrivateKey{ PublicKey: rsa.PublicKey{ E: int(k.E.Int64()), N: k.N, }, D: k.D, Primes: []*big.Int{k.P, k.Q}, } priv.Precompute() return s.agent.Add(AddedKey{PrivateKey: &priv, Comment: k.Comments}) } return fmt.Errorf("not implemented: %s", record.Type) }
func (s *sshServer) handleRequests(channel ssh.Channel, in <-chan *ssh.Request) error { env := make(map[string]string) for req := range in { switch req.Type { default: log.Printf("unrecognized ssh request type=%q payload=%s wantreply=%t", req.Type, req.Payload, req.WantReply) req.Reply(false, nil) // unhandled; tell them so case "env": var e envReq if err := ssh.Unmarshal(req.Payload, &e); err != nil { req.Reply(false, nil) return err } req.Reply(true, nil) env[string(e.Key)] = string(e.Val) case "exec": var e execReq if err := ssh.Unmarshal(req.Payload, &e); err != nil { req.Reply(false, nil) return err } req.Reply(true, nil) var cmdbuf bytes.Buffer for k, v := range env { cmdbuf.WriteString(k) cmdbuf.WriteByte('=') cmdbuf.WriteString(v) cmdbuf.WriteByte(' ') } cmdbuf.Write(e.Command) log.Printf("Running command %q", cmdbuf.String()) cmd := &packer.RemoteCmd{Command: cmdbuf.String()} cmd.Stdout = channel cmd.Stderr = channel.Stderr() var rc int if err := s.comm.Start(cmd); err != nil { rc = 255 // TODO: What is a better choice here? } else { cmd.Wait() rc = cmd.ExitStatus } channel.CloseWrite() channel.SendRequest("exit-status", false, []byte{0, 0, 0, byte(rc)}) channel.Close() } } return nil }
// SSHNativeParsePublicKey extracts the key type and length using the golang SSH library. // NOTE: ed25519 is not supported. func SSHNativeParsePublicKey(keyLine string) (string, int, error) { fields := strings.Fields(keyLine) if len(fields) < 2 { return "", 0, fmt.Errorf("not enough fields in public key line: %s", string(keyLine)) } raw, err := base64.StdEncoding.DecodeString(fields[1]) if err != nil { return "", 0, err } pkey, err := ssh.ParsePublicKey(raw) if err != nil { if strings.Contains(err.Error(), "ssh: unknown key algorithm") { return "", 0, ErrKeyUnableVerify{err.Error()} } return "", 0, fmt.Errorf("ssh.ParsePublicKey: %v", err) } // The ssh library can parse the key, so next we find out what key exactly we have. switch pkey.Type() { case ssh.KeyAlgoDSA: rawPub := struct { Name string P, Q, G, Y *big.Int }{} if err := ssh.Unmarshal(pkey.Marshal(), &rawPub); err != nil { return "", 0, err } // as per https://bugzilla.mindrot.org/show_bug.cgi?id=1647 we should never // see dsa keys != 1024 bit, but as it seems to work, we will not check here return "dsa", rawPub.P.BitLen(), nil // use P as per crypto/dsa/dsa.go (is L) case ssh.KeyAlgoRSA: rawPub := struct { Name string E *big.Int N *big.Int }{} if err := ssh.Unmarshal(pkey.Marshal(), &rawPub); err != nil { return "", 0, err } return "rsa", rawPub.N.BitLen(), nil // use N as per crypto/rsa/rsa.go (is bits) case ssh.KeyAlgoECDSA256: return "ecdsa", 256, nil case ssh.KeyAlgoECDSA384: return "ecdsa", 384, nil case ssh.KeyAlgoECDSA521: return "ecdsa", 521, nil case "ssh-ed25519": // TODO replace with ssh constant when available return "ed25519", 256, nil } return "", 0, fmt.Errorf("Unsupported key length detection for type: %s", pkey.Type()) }
// decodeClient decodes a response packet's raw data into its corresponding // message structure. func decodeClient(packet []byte) (interface{}, error) { var msg interface{} switch packet[0] { case fxpPacketVersion: msg = new(fxpVersionMsg) case fxpPacketStatus: msg = new(fxpStatusResp) case fxpPacketHandle: msg = new(fxpHandleResp) case fxpPacketData: msg = new(fxpDataResp) case fxpPacketName: msg = new(fxpNameResp) case fxpPacketAttrs: msg = new(fxpAttrsResp) case fxpPacketExtendedReply: msg = new(fxpExtendedResp) default: return nil, UnexpectedMessageError{0, packet[0]} } if err := ssh.Unmarshal(packet, msg); err != nil { return nil, err } return msg, 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 (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 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 (s *server) insertIdentity(req []byte) error { var record struct { Type string `sshtype:"17|25"` Rest []byte `ssh:"rest"` } if err := ssh.Unmarshal(req, &record); err != nil { return err } var addedKey *AddedKey var err error switch record.Type { case ssh.KeyAlgoRSA: addedKey, err = parseRSAKey(req) case ssh.KeyAlgoDSA: addedKey, err = parseDSAKey(req) case ssh.KeyAlgoECDSA256, ssh.KeyAlgoECDSA384, ssh.KeyAlgoECDSA521: addedKey, err = parseECDSACert(req) case ssh.CertAlgoRSAv01: addedKey, err = parseRSACert(req) case ssh.CertAlgoDSAv01: addedKey, err = parseDSACert(req) case ssh.CertAlgoECDSA256v01, ssh.CertAlgoECDSA384v01, ssh.CertAlgoECDSA521v01: addedKey, err = parseECDSACert(req) default: return fmt.Errorf("agent: not implemented: %q", record.Type) } if err != nil { return err } return s.agent.Add(*addedKey) }
func (sshClient *sshClient) handleNewPortForwardChannel(newChannel ssh.NewChannel) { defer sshClient.channelHandlerWaitGroup.Done() // http://tools.ietf.org/html/rfc4254#section-7.2 var directTcpipExtraData struct { HostToConnect string PortToConnect uint32 OriginatorIPAddress string OriginatorPort uint32 } err := ssh.Unmarshal(newChannel.ExtraData(), &directTcpipExtraData) if err != nil { sshClient.rejectNewChannel(newChannel, ssh.Prohibited, "invalid extra data") return } // Intercept TCP port forwards to a specified udpgw server and handle directly. // TODO: also support UDP explicitly, e.g. with a custom "direct-udp" channel type? isUDPChannel := sshClient.sshServer.support.Config.UDPInterceptUdpgwServerAddress != "" && sshClient.sshServer.support.Config.UDPInterceptUdpgwServerAddress == fmt.Sprintf("%s:%d", directTcpipExtraData.HostToConnect, directTcpipExtraData.PortToConnect) if isUDPChannel { sshClient.handleUDPChannel(newChannel) } else { sshClient.handleTCPChannel( directTcpipExtraData.HostToConnect, int(directTcpipExtraData.PortToConnect), newChannel) } }
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 (handler *DirectTcpipChannelHandler) HandleNewChannel(logger lager.Logger, newChannel ssh.NewChannel) { type channelOpenDirectTcpipMsg struct { TargetAddr string TargetPort uint32 OriginAddr string OriginPort uint32 } var directTcpipMessage channelOpenDirectTcpipMsg err := ssh.Unmarshal(newChannel.ExtraData(), &directTcpipMessage) if err != nil { newChannel.Reject(ssh.ConnectionFailed, "Failed to parse open channel message") return } destination := fmt.Sprintf("%s:%d", directTcpipMessage.TargetAddr, directTcpipMessage.TargetPort) conn, err := handler.dialer.Dial("tcp", destination) if err != nil { newChannel.Reject(ssh.ConnectionFailed, err.Error()) return } channel, requests, err := newChannel.Accept() go ssh.DiscardRequests(requests) wg := &sync.WaitGroup{} wg.Add(2) go helpers.CopyAndClose(logger.Session("to-target"), wg, conn, channel) go helpers.CopyAndClose(logger.Session("to-channel"), wg, channel, conn) wg.Wait() }
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) } }
// cleanExec cleans the exec string. func cleanExec(pay []byte) string { e := &ExecCmd{} ssh.Unmarshal(pay, e) // TODO: Minimal escaping of values in command. There is probably a better // way of doing this. r := strings.NewReplacer("$", "", "`", "'") return r.Replace(e.Value) }
func parseEd25519Key(req []byte) (*AddedKey, error) { var k ed25519KeyMsg if err := ssh.Unmarshal(req, &k); err != nil { return nil, err } priv := ed25519.PrivateKey(k.Priv) return &AddedKey{PrivateKey: &priv, Comment: k.Comments}, nil }
func newEnvRequest(raw *ssh.Request) (*envRequest, error) { r := new(envRequest) r.Request = raw if err := ssh.Unmarshal(raw.Payload, &r.Payload); err != nil { return nil, err } return r, nil }
// 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 }
func parseRSACert(req []byte) (*AddedKey, error) { var k rsaCertMsg if err := ssh.Unmarshal(req, &k); err != nil { return nil, err } pubKey, err := ssh.ParsePublicKey(k.CertBytes) if err != nil { return nil, err } cert, ok := pubKey.(*ssh.Certificate) if !ok { return nil, errors.New("agent: bad RSA certificate") } // An RSA publickey as marshaled by rsaPublicKey.Marshal() in keys.go var rsaPub struct { Name string E *big.Int N *big.Int } if err := ssh.Unmarshal(cert.Key.Marshal(), &rsaPub); err != nil { return nil, fmt.Errorf("agent: Unmarshal failed to parse public key: %v", err) } if rsaPub.E.BitLen() > 30 { return nil, errors.New("agent: RSA public exponent too large") } priv := rsa.PrivateKey{ PublicKey: rsa.PublicKey{ E: int(rsaPub.E.Int64()), N: rsaPub.N, }, D: k.D, Primes: []*big.Int{k.Q, k.P}, } priv.Precompute() return &AddedKey{PrivateKey: &priv, Certificate: cert, Comment: k.Comments}, nil }
func ParseRequest(req *ssh.Request) (*Request, error) { var raw struct { Value []byte } err := ssh.Unmarshal(req.Payload, &raw) if err != nil { return nil, err } return ParseRequestValue(raw.Value) }
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 }
// ChannelForward establishes a secure channel forward (ssh -W) to the server // requested by the user, assuming it is a permitted host. func (s *Server) ChannelForward(session *Session, newChannel ssh.NewChannel) { var msg channelOpenDirectMsg ssh.Unmarshal(newChannel.ExtraData(), &msg) address := fmt.Sprintf("%s:%d", msg.RAddr, msg.RPort) permitted := false for _, remote := range session.Remotes { if remote == address { permitted = true break } } if !permitted { log.Printf("Disallowed access to %s for user %s", address, session.User.Name) newChannel.Reject(ssh.Prohibited, "remote host access denied for user") return } // Log the selection if s.Selected != nil { if err := s.Selected(session, address); err != nil { newChannel.Reject(ssh.Prohibited, "access denied") return } } conn, err := net.Dial("tcp", address) if err != nil { newChannel.Reject(ssh.ConnectionFailed, fmt.Sprintf("error: %v", err)) return } channel, reqs, err := newChannel.Accept() go ssh.DiscardRequests(reqs) var closer sync.Once closeFunc := func() { channel.Close() conn.Close() } go func() { io.Copy(channel, conn) closer.Do(closeFunc) }() go func() { io.Copy(conn, channel) closer.Do(closeFunc) }() }
func parseKey(in []byte) (out *Key, rest []byte, err error) { var record struct { Blob []byte Comment string Rest []byte `ssh:"rest"` } if err := ssh.Unmarshal(in, &record); err != nil { return nil, nil, err } var wk wireKey if err := ssh.Unmarshal(record.Blob, &wk); err != nil { return nil, nil, err } return &Key{ Format: wk.Format, Blob: record.Blob, Comment: record.Comment, }, record.Rest, nil }
func parseECDSAKey(req []byte) (*AddedKey, error) { var k ecdsaKeyMsg if err := ssh.Unmarshal(req, &k); err != nil { return nil, err } priv, err := unmarshalECDSA(k.Curve, k.KeyBytes, k.D) if err != nil { return nil, err } return &AddedKey{PrivateKey: &priv, Comment: k.Comments}, nil }
func UnmarshalForwardRequest(payload []byte) (*ForwardRequest, error) { request := &ForwardRequest{} if err := ssh.Unmarshal(payload, request); err != nil { return nil, err } // TODO: check host if request.Port > 65535 { return nil, ErrInvalidForwardRequest } return request, nil }
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) } }() }
func parseDSACert(req []byte) (*AddedKey, error) { var k dsaCertMsg if err := ssh.Unmarshal(req, &k); err != nil { return nil, err } pubKey, err := ssh.ParsePublicKey(k.CertBytes) if err != nil { return nil, err } cert, ok := pubKey.(*ssh.Certificate) if !ok { return nil, errors.New("agent: bad DSA certificate") } // A DSA publickey as marshaled by dsaPublicKey.Marshal() in keys.go var w struct { Name string P, Q, G, Y *big.Int } if err := ssh.Unmarshal(cert.Key.Marshal(), &w); err != nil { return nil, fmt.Errorf("agent: Unmarshal failed to parse public key: %v", err) } priv := &dsa.PrivateKey{ PublicKey: dsa.PublicKey{ Parameters: dsa.Parameters{ P: w.P, Q: w.Q, G: w.G, }, Y: w.Y, }, X: k.X, } return &AddedKey{PrivateKey: priv, Certificate: cert, Comment: k.Comments}, nil }
func parseEd25519Cert(req []byte) (*AddedKey, error) { var k ed25519CertMsg if err := ssh.Unmarshal(req, &k); err != nil { return nil, err } pubKey, err := ssh.ParsePublicKey(k.CertBytes) if err != nil { return nil, err } priv := ed25519.PrivateKey(k.Priv) cert, ok := pubKey.(*ssh.Certificate) if !ok { return nil, errors.New("agent: bad ED25519 certificate") } return &AddedKey{PrivateKey: &priv, Certificate: cert, Comment: k.Comments}, nil }
func (s *session) handleExec(req *ssh.Request) error { cmd := execRequest{} if err := ssh.Unmarshal(req.Payload, &cmd); err != nil { return err } args, err := shellArgs([]byte(cmd.Command)) if err != nil { return err } if err := s.exec(args); err != nil { log.Println("handleExec:", err) return fmt.Errorf("%s: %s", err, cmd.Command) } return nil }
func UnmarshalTunnelData(payload []byte) (*TunnelData, error) { data := &TunnelData{} if err := ssh.Unmarshal(payload, data); err != nil { return nil, err } // TODO: check host if data.Port > 65535 { return nil, ErrInvalidTunnelData } if data.OriginPort > 65535 { return nil, ErrInvalidTunnelData } return data, nil }
func parseDSAKey(req []byte) (*AddedKey, error) { var k dsaKeyMsg if err := ssh.Unmarshal(req, &k); err != nil { return nil, err } priv := &dsa.PrivateKey{ PublicKey: dsa.PublicKey{ Parameters: dsa.Parameters{ P: k.P, Q: k.Q, G: k.G, }, Y: k.Y, }, X: k.X, } return &AddedKey{PrivateKey: priv, Comment: k.Comments}, nil }