Exemple #1
0
func handleServerConn(sConn *ssh.ServerConn) {
	defer sConn.Close()
	for {
		// Accept reads from the connection, demultiplexes packets
		// to their corresponding channels and returns when a new
		// channel request is seen. Some goroutine must always be
		// calling Accept; otherwise no messages will be forwarded
		// to the channels.
		ch, err := sConn.Accept()
		if err == io.EOF {
			return
		}
		if err != nil {
			log.Println("handleServerConn Accept:", err)
			break
		}
		// Channels have a type, depending on the application level
		// protocol intended. In the case of a shell, the type is
		// "session" and ServerShell may be used to present a simple
		// terminal interface.
		if ch.ChannelType() != "session" {
			ch.Reject(ssh.UnknownChannelType, "unknown channel type")
			break
		}
		go handleChannel(ch)
	}
}
Exemple #2
0
func handleChannels(sshCon *ssh.ServerConn, chans <-chan ssh.NewChannel, sh SessionHandler) {
	// Service the incoming Channel channel in go routine
	for newChannel := range chans {
		handleChannel(newChannel, sh)
		sshCon.Close()
	}
}
Exemple #3
0
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()
}
Exemple #4
0
func (u *UrlDispatcher) Dispatch(c context.Context, conn *ssh.ServerConn, ch ssh.NewChannel) {
	defer conn.Close()

	// Get channel type
	chType := ch.ChannelType()

	// Parse channel URI
	uri, err := url.ParseRequestURI(chType)
	if err != nil {
		u.Logger.Warn("Error parsing channel type", "type", chType, "err", err)
		ch.Reject(InvalidChannelType, "invalid channel URI")
		return
	} else if reject(chType, uri, ch, u.Logger) {
		return
	}
	chType = uri.Path

	// Parse query params
	values, err := url.ParseQuery(uri.RawQuery)
	if err != nil {
		u.Logger.Warn("Error parsing query params", "values", values, "err", err)
		ch.Reject(InvalidQueryParams, "invalid query params in channel type")
		return
	}

	// Determine if channel is acceptable (has a registered handler)
	if !u.Router.HasRoute(chType) {
		u.Logger.Info("UnknownChannelType", "type", chType)
		ch.Reject(ssh.UnknownChannelType, chType)
		return
	}

	// Otherwise, accept the channel
	channel, requests, err := ch.Accept()
	if err != nil {
		u.Logger.Warn("Error creating channel", "type", chType, "err", err)
		ch.Reject(ChannelAcceptError, chType)
		return
	}

	// Handle the channel
	err = u.Router.Handle(&router.UrlContext{
		Path:     uri.Path,
		Context:  c,
		Values:   values,
		Channel:  channel,
		Requests: requests,
	})
	if err != nil {
		u.Logger.Warn("Error handling channel", "type", chType, "err", err)
		ch.Reject(ChannelHandleError, fmt.Sprintf("error handling channel: %s", err.Error()))
		return
	}
}
Exemple #5
0
func (h *SCPHandler) SinkRequest(conn ssh.ServerConn, parameters scp.Parameter, pattern string) bool {

	h.conn = conn

	// Remote address lookup
	var err error
	h.name, err = lookupIP(conn.RemoteAddr().String())
	if err != nil {
		log.Println("Failed to lookup IP: %s", err)
		return false
	}

	log.Println("Accepting SCP request from %s", h.name)
	return true
}
Exemple #6
0
func NewSession(gateway *Gateway, connection *ssh.ServerConn) (*Session, error) {
	glog.V(1).Infof("new session: user = %s, remote = %v", connection.User(), connection.RemoteAddr())

	return &Session{
		gateway:        gateway,
		connection:     connection,
		user:           connection.User(),
		remoteAddr:     connection.RemoteAddr(),
		localAddr:      connection.LocalAddr(),
		services:       make(map[string]map[uint16]bool),
		lock:           &sync.Mutex{},
		active:         true,
		created:        time.Now(),
		used:           time.Now(),
		channelsClosed: 0,
		bytesRead:      0,
		bytesWritten:   0,
	}, nil
}
Exemple #7
0
func (u *SimpleDispatcher) Dispatch(c context.Context, conn *ssh.ServerConn, ch ssh.NewChannel) {
	defer conn.Close()

	var ctx *Context
	if u.PanicHandler != nil {
		if rcv := recover(); rcv != nil {
			u.PanicHandler.Handle(ctx, rcv)
		}
	}

	// Get channel type
	chType := ch.ChannelType()

	handler, ok := u.Handlers[chType]
	if !ok {
		return
	}

	// Otherwise, accept the channel
	channel, requests, err := ch.Accept()
	if err != nil {
		u.Logger.Warn("Error creating channel", "type", chType, "err", err)
		ch.Reject(ChannelAcceptError, chType)
		return
	}

	// Handle the channel
	ctx = &Context{
		Context:  c,
		Channel:  channel,
		Requests: requests,
	}
	err = handler.Handle(ctx)
	if err != nil {
		u.Logger.Warn("Error handling channel", "type", chType, "err", err)
		ch.Reject(ChannelHandleError, fmt.Sprintf("error handling channel: %s", err.Error()))
		return
	}
}
Exemple #8
0
// Make new terminal from a session channel
func NewTerminal(conn *ssh.ServerConn, ch ssh.NewChannel) (*Terminal, error) {
	if ch.ChannelType() != "session" {
		return nil, errors.New("terminal requires session channel")
	}
	channel, requests, err := ch.Accept()
	if err != nil {
		return nil, err
	}
	term := Terminal{
		*terminal.NewTerminal(channel, "Connecting..."),
		sshConn{conn},
		channel,
	}

	go term.listen(requests)
	go func() {
		// FIXME: Is this necessary?
		conn.Wait()
		channel.Close()
	}()

	return &term, nil
}
Exemple #9
0
/* connectionLogger opens a log file for the authenticated connection in the
given logDir.  It returns the logger itself, as well as the name of the
logfile and the session directory.  Should look like
	logdir/address/sessiontime/log
The returned *os.File must be closed when it's no longer needed to prevent
memory/fd leakage.
*/
func connectionLogger(
	sc *ssh.ServerConn,
	logDir string,
) (lg *log.Logger, name, dir string, file *os.File, err error) {
	/* Each host gets its own directory */
	addrDir, _, err := net.SplitHostPort(sc.RemoteAddr().String())
	if nil != err {
		log.Printf(
			"Address:%v Unable to split host from port: %v",
			sc.RemoteAddr().String(),
			err,
		)
		addrDir = sc.RemoteAddr().String() + "err"
	}

	/* Each authenticated session does, as well */
	sessionDir := filepath.Join(
		logDir,
		addrDir,
		time.Now().Format(LOGFORMAT),
	)
	if err := os.MkdirAll(sessionDir, 0700); nil != err {
		return nil, "", "", nil, err
	}
	/* Open the main logfile */
	logName := filepath.Join(sessionDir, LOGNAME)
	lf, err := os.OpenFile(
		logName,
		os.O_WRONLY|os.O_APPEND|os.O_CREATE|os.O_EXCL,
		0600,
	)
	if nil != err {
		return nil, "", "", nil, err
	}

	/* Logify it. */
	return log.New(
		//lf,
		io.MultiWriter(lf, os.Stderr), /* DEBUG */
		"",
		log.LstdFlags|log.Lmicroseconds,
	), logName, sessionDir, lf, nil
}
Exemple #10
0
func (server *Server) handleChannel(newChannel ssh.NewChannel, conn *ssh.ServerConn) {
	channelType := newChannel.ChannelType()
	if channelType != "session" {
		newChannel.Reject(ssh.UnknownChannelType,
			fmt.Sprintf("Unknown SSH Channel Type: %s, only `session` is supported", channelType))
		server.Logger.Errorf("Rejected SSH Channel Request from %s due to unknown channel type: %s",
			conn.RemoteAddr().String(), newChannel.ChannelType())
		return
	}
	channel, requests, err := newChannel.Accept()
	if err != nil {
		newChannel.Reject(ssh.ConnectionFailed, "Failed to accept SSH Channel Request, developers are working on it.")
		server.Logger.Errorf("Rejected SSH Channel Request from %s due to accept request failure: %s",
			conn.RemoteAddr().String(), err)
		return
	}
	server.Logger.Debugf("Accepted new SSH Channel Request from %s", conn.RemoteAddr().String())

	server.handleRequest(channel, requests, conn)
}
Exemple #11
0
// NewClient initializes a new client
func NewClient(conn *ssh.ServerConn, chans <-chan ssh.NewChannel, reqs <-chan *ssh.Request, server *Server) *Client {
	client := Client{
		Idx:        clientCounter,
		ClientID:   conn.RemoteAddr().String(),
		ChannelIdx: 0,
		Conn:       conn,
		Chans:      chans,
		Reqs:       reqs,
		Server:     server,

		// Default ClientConfig, will be overwritten if a hook is used
		Config: &ClientConfig{
			ImageName:              strings.Replace(conn.User(), "_", "/", -1),
			RemoteUser:             "******",
			AuthenticationMethod:   "noauth",
			AuthenticationComment:  "",
			AuthenticationAttempts: 0,
			Env:     envhelper.Environment{},
			Command: make([]string, 0),
		},
	}

	if server.LocalUser != "" {
		client.Config.IsLocal = client.Config.ImageName == server.LocalUser
	}

	if _, found := server.ClientConfigs[client.ClientID]; !found {
		server.ClientConfigs[client.ClientID] = client.Config
	}

	client.Config = server.ClientConfigs[conn.RemoteAddr().String()]
	client.Config.Env.ApplyDefaults()

	clientCounter++

	remoteAddr := strings.Split(client.ClientID, ":")
	log.Infof("Accepted %s for %s from %s port %s ssh2: %s", client.Config.AuthenticationMethod, conn.User(), remoteAddr[0], remoteAddr[1], client.Config.AuthenticationComment)
	return &client
}
Exemple #12
0
func (server *Server) handleRequest(channel ssh.Channel, requests <-chan *ssh.Request, conn *ssh.ServerConn) {
	defer func() {
		err := channel.Close()
		if err != nil {
			server.Logger.Errorf("Failed to close SSH Channel from %s due to %s",
				conn.RemoteAddr().String(), err)
		}
		server.Logger.Debugf("Close SSH Channel from %s", conn.RemoteAddr().String())
	}()
	for req := range requests {
		server.Logger.Debugf("Received new SSH Request (type = %s) from %s", req.Type, conn.RemoteAddr().String())

		switch req.Type {
		case "exec":
			server.handleExecRequest(channel, req, conn)
		default:
			var err error
			if req.Type == "env" {
				_, err = channel.Stderr().Write([]byte("error: Pages does not support SendEnv.\n"))
			} else {
				_, err = channel.Write([]byte("You've successfully authenticated, but Pages does not provide shell access.\n"))
			}
			if err != nil && err != io.EOF {
				server.Logger.Errorf("Failed to Talk to SSH Request due to %s", err)
			}
			err = req.Reply(false, nil)
			if err != nil && err != io.EOF {
				server.Logger.Errorf("Failed to Reply false to SSH Request due to %s", err)
			}
			err = channel.Close()
			if err != nil && err != io.EOF {
				server.Logger.Errorf("Failed to close SSH Request due to %s", err)
			}
			server.Logger.Errorf("Close SSH Request due to unsupported SSH Request type: %s", req.Type)
		}
		return
	}
}
Exemple #13
0
// run should not be called directly, but via start
// run will establish an ssh server listening on the backchannel
func (t *attachServerSSH) run() error {
	defer trace.End(trace.Begin("main attach server loop"))

	var sConn *ssh.ServerConn
	var chans <-chan ssh.NewChannel
	var reqs <-chan *ssh.Request
	var err error

	// keep waiting for the connection to establish
	for t.enabled && sConn == nil {
		conn := t.conn
		if conn == nil {
			// Stop has probably been called as t.conn is set in Start and should
			// never be nil otherwise
			err := fmt.Errorf("connection provided for backchannel is nil")
			log.Debug(err.Error())
			return err
		}

		// wait for backchannel to establish
		err = backchannel(context.Background(), conn)
		if err != nil {
			detail := fmt.Sprintf("failed to establish backchannel: %s", err)
			log.Error(detail)
			continue
		}

		// create the SSH server
		sConn, chans, reqs, err = ssh.NewServerConn(*conn, t.sshConfig)
		if err != nil {
			detail := fmt.Sprintf("failed to establish ssh handshake: %s", err)
			log.Error(detail)
			continue
		}
	}
	if err != nil {
		detail := fmt.Sprintf("abandoning attempt to start attach server: %s", err)
		log.Error(detail)
		return err
	}

	defer func() {
		if sConn != nil {
			sConn.Close()
		}
	}()

	// Global requests
	go t.globalMux(reqs)

	log.Println("ready to service attach requests")
	// Service the incoming channels
	for attachchan := range chans {
		// The only channel type we'll support is attach
		if attachchan.ChannelType() != attachChannelType {
			detail := fmt.Sprintf("unknown channel type %s", attachchan.ChannelType())
			log.Error(detail)
			attachchan.Reject(ssh.UnknownChannelType, "unknown channel type")
			continue
		}

		// check we have a Session matching the requested ID
		bytes := attachchan.ExtraData()
		if bytes == nil {
			detail := "attach channel requires ID in ExtraData"
			log.Error(detail)
			attachchan.Reject(ssh.Prohibited, detail)
			continue
		}

		sessionid := string(bytes)
		session, ok := t.config.Sessions[sessionid]

		reason := ""
		if !ok {
			reason = "is unknown"
		} else if session.Cmd.Process == nil {
			reason = "process has not been launched"
		} else if session.Cmd.Process.Signal(syscall.Signal(0)) != nil {
			reason = "process has exited"
		}

		if reason != "" {
			detail := fmt.Sprintf("attach request: session %s %s", sessionid, reason)
			log.Error(detail)
			attachchan.Reject(ssh.Prohibited, detail)
			continue
		}

		log.Infof("accepting incoming channel for %s", sessionid)
		channel, requests, err := attachchan.Accept()
		log.Debugf("accepted incoming channel for %s", sessionid)
		if err != nil {
			detail := fmt.Sprintf("could not accept channel: %s", err)
			log.Errorf(detail)
			continue
		}

		// bind the channel to the Session
		log.Debugf("binding reader/writers for channel for %s", sessionid)
		session.Outwriter.Add(channel)
		session.Reader.Add(channel)

		// cleanup on detach from the session
		detach := func() {
			session.Outwriter.Remove(channel)
			session.Reader.Remove(channel)
		}

		// tty's merge stdout and stderr so we don't bind an additional reader in that case
		// but we need to do so for non-tty
		if session.Pty == nil {
			session.Errwriter.Add(channel.Stderr())

			// no good way to function chain, so reimplement appropriately
			detach = func() {
				session.Outwriter.Remove(channel)
				session.Reader.Remove(channel)
				session.Errwriter.Remove(channel)
			}
		}
		log.Debugf("reader/writers bound for channel for %s", sessionid)

		go t.channelMux(requests, session.Cmd.Process, session.Pty, detach)
	}

	log.Info("incoming attach channel closed")

	return nil
}
Exemple #14
0
func (agent *Agent) handleConn(conn *ssh.ServerConn, chans <-chan ssh.NewChannel, reqs <-chan *ssh.Request) {
	defer conn.Close()

	for newChannel := range chans {
		if newChannel.ChannelType() != "session" {
			log.Errorf("rejecting unknown channel type: %s\n", newChannel.ChannelType())
			newChannel.Reject(ssh.UnknownChannelType, "unknown channel type")
			continue
		}

		channel, requests, err := newChannel.Accept()
		if err != nil {
			log.Errorf("failed to accept channel: %s\n", err)
			return
		}

		defer channel.Close()

		for req := range requests {
			if req.Type != "exec" {
				log.Errorf("rejecting non-exec channel request (type=%s)\n", req.Type)
				req.Reply(false, nil)
				continue
			}

			request, err := ParseRequest(req)
			if err != nil {
				log.Errorf("%s\n", err)
				req.Reply(false, nil)
				continue
			}

			if err = request.ResolvePaths(agent); err != nil {
				log.Errorf("%s\n", err)
				req.Reply(false, nil)
				continue
			}

			//log.Errorf("got an agent-request [%s]\n", request.JSON)
			req.Reply(true, nil)

			// drain output to the SSH channel stream
			output := make(chan string)
			done := make(chan int)
			go func(out io.Writer, in chan string, done chan int) {
				for {
					s, ok := <-in
					if !ok {
						break
					}
					fmt.Fprintf(out, "%s", s)
					log.Debugf("%s", s)
				}
				close(done)
			}(channel, output, done)

			// run the agent request
			err = request.Run(output)
			<-done
			var rc int
			if exitErr, ok := err.(*exec.ExitError); ok {
				sys := exitErr.ProcessState.Sys()
				// os.ProcessState.Sys() may not return syscall.WaitStatus on non-UNIX machines,
				// so currently this feature only works on UNIX, but shouldn't crash on other OSes
				if ws, ok := sys.(syscall.WaitStatus); ok {
					if ws.Exited() {
						rc = ws.ExitStatus()
					} else {
						var signal syscall.Signal
						if ws.Signaled() {
							signal = ws.Signal()
						}
						if ws.Stopped() {
							signal = ws.StopSignal()
						}
						sigStr, ok := SIGSTRING[signal]
						if !ok {
							sigStr = "ABRT" // use ABRT as catch-all signal for any that don't translate
							log.Infof("Task execution terminted due to %s, translating as ABRT for ssh transport", signal)
						} else {
							log.Infof("Task execution terminated due to SIG%s", sigStr)
						}
						sigMsg := struct {
							Signal     string
							CoreDumped bool
							Error      string
							Lang       string
						}{
							Signal:     sigStr,
							CoreDumped: false,
							Error:      fmt.Sprintf("shield-pipe terminated due to SIG%s", sigStr),
							Lang:       "en-US",
						}
						channel.SendRequest("exit-signal", false, ssh.Marshal(&sigMsg))
						channel.Close()
						continue
					}
				}
			} else if err != nil {
				// we got some kind of error that isn't a command execution error,
				// from a UNIX system, use an magical error code to signal this to
				// the shield daemon - 16777216
				log.Infof("Task could not execute: %s", err)
				rc = 16777216
			}

			log.Infof("Task completed with rc=%d", rc)
			byteCode := make([]byte, 4)
			binary.BigEndian.PutUint32(byteCode, uint32(rc)) // SSH protocol is big-endian byte ordering
			channel.SendRequest("exit-status", false, byteCode)
			channel.Close()
		}
	}
}
Exemple #15
0
func (s *Server) handleChannel(conn *ssh.ServerConn, newChan ssh.NewChannel) {
	ch, reqs, err := newChan.Accept()
	if err != nil {
		log.Println("newChan.Accept failed:", err)
		return
	}
	defer ch.Close()
	for req := range reqs {
		switch req.Type {
		case "exec":
			fail := func(at string, err error) {
				log.Printf("%s failed: %s", at, err)
				ch.Stderr().Write([]byte("Internal error.\n"))
			}
			if req.WantReply {
				req.Reply(true, nil)
			}
			cmdline := string(req.Payload[4:])
			cmdargs, err := shlex.Split(cmdline)
			if err != nil || len(cmdargs) != 2 {
				ch.Stderr().Write([]byte("Invalid arguments.\n"))
				return
			}
			if cmdargs[0] != "git-upload-pack" {
				ch.Stderr().Write([]byte("Only `git fetch` is supported.\n"))
				return
			}
			cmdargs[1] = strings.TrimSuffix(strings.TrimPrefix(cmdargs[1], "/"), ".git")
			if strings.Contains(cmdargs[1], "..") {
				ch.Stderr().Write([]byte("Invalid repo.\n"))
				return
			}

			cmd := exec.Command(s.Shell, "-c", cmdargs[0]+" '"+cmdargs[1]+"'")
			cmd.Dir = s.Dir
			cmd.Env = append(os.Environ(),
				"RECEIVE_USER="******"RECEIVE_REPO="+cmdargs[1],
			)
			done, err := attachCmd(cmd, ch, ch.Stderr(), ch)
			if err != nil {
				fail("attachCmd", err)
				return
			}
			if err := cmd.Start(); err != nil {
				fail("cmd.Start", err)
				return
			}
			done.Wait()
			status, err := exitStatus(cmd.Wait())
			if err != nil {
				fail("exitStatus", err)
				return
			}
			if _, err := ch.SendRequest("exit-status", false, ssh.Marshal(&status)); err != nil {
				fail("sendExit", err)
			}
			return
		case "env":
			if req.WantReply {
				req.Reply(true, nil)
			}
		}
	}
}
Exemple #16
0
func handleRegs(reqs <-chan *ssh.Request, sshConn *ssh.ServerConn) {
	defer sshConn.Close()
	for req := range reqs {
		if req.Type == "keepalive" && req.WantReply {
			req.Reply(true, nil)
			continue
		}

		var payload tcpipforwardPayload
		if err := ssh.Unmarshal(req.Payload, &payload); err != nil {
			fmt.Println("ERROR", err)
			continue
		}

		addr := fmt.Sprintf("%s:%d", payload.Addr, payload.Port)
		ln, err := net.Listen("tcp", addr)
		if err != nil {
			fmt.Println("Unable to listen on address: ", addr)
			req.Reply(false, nil)
			continue
		}
		defer ln.Close()

		reply := (payload.Port == 0) && req.WantReply
		if !reply {
			req.Reply(true, nil)
		} else {
			req.Reply(false, nil)
		}
		go func() {
			fmt.Println("Listening on address: ", ln.Addr().String())
			quit := make(chan bool)

			go func() {
				go func() {
					t := time.NewTicker(30 * time.Second)
					defer t.Stop()
					for {
						<-t.C
						_, _, err := sshConn.SendRequest("keepalive", true, nil)
						if err != nil {
							fmt.Println("closed", sshConn)
							sshConn.Close()
							return
						}
					}
				}()
				for {
					select {
					case <-quit:
						return
					default:
						conn, err := ln.Accept()
						if err != nil {
							continue
						}
						go func(conn net.Conn) {
							p := forwardedTCPPayload{}
							var err error
							var portnum int

							p.Addr = payload.Addr
							p.Port = payload.Port
							p.OriginAddr, portnum, err = getHostPortFromAddr(conn.RemoteAddr())
							if err != nil {
								conn.Close()
								return
							}

							p.OriginPort = uint32(portnum)
							ch, reqs, err := sshConn.OpenChannel("forwarded-tcpip", ssh.Marshal(p))
							if err != nil {
								conn.Close()
								log.Println("Open forwarded Channel: ", err.Error())
								return
							}

							go ssh.DiscardRequests(reqs)
							go func(ch ssh.Channel, conn net.Conn) {
								close := func() {
									ch.Close()
									conn.Close()
								}
								go copyConnections(conn, ch, close)
							}(ch, conn)
						}(conn)
					}
				}
			}()
			sshConn.Wait()
			fmt.Println("Stop forwarding/listening on ", ln.Addr())
			quit <- true
		}()
	}
}
Exemple #17
0
func (server *registrarSSHServer) handleConn(logger lager.Logger, conn *ssh.ServerConn, chans <-chan ssh.NewChannel, reqs <-chan *ssh.Request) {
	defer conn.Close()

	forwardedTCPIPs := make(chan forwardedTCPIP, 1)
	go server.handleForwardRequests(logger, conn, reqs, forwardedTCPIPs)

	var processes []ifrit.Process

	// ensure processes get cleaned up
	defer func() {
		cleanupLog := logger.Session("cleanup")

		for _, p := range processes {
			cleanupLog.Debug("interrupting")

			p.Signal(os.Interrupt)
		}

		for _, p := range processes {
			err := <-p.Wait()
			if err != nil {
				cleanupLog.Error("process-exited-with-failure", err)
			} else {
				cleanupLog.Debug("process-exited-successfully")
			}
		}
	}()

	for newChannel := range chans {
		if newChannel.ChannelType() != "session" {
			logger.Info("rejecting-unknown-channel-type", lager.Data{
				"type": newChannel.ChannelType(),
			})

			newChannel.Reject(ssh.UnknownChannelType, "unknown channel type")
			continue
		}

		channel, requests, err := newChannel.Accept()
		if err != nil {
			logger.Error("failed-to-accept-channel", err)
			return
		}

		defer channel.Close()

		for req := range requests {
			logger.Info("channel-request", lager.Data{
				"type": req.Type,
			})

			if req.Type != "exec" {
				logger.Info("rejecting")

				req.Reply(false, nil)
				continue
			}

			var request execRequest
			err = ssh.Unmarshal(req.Payload, &request)
			if err != nil {
				logger.Error("malformed-exec-request", err)

				req.Reply(false, nil)

				return
			}

			switch request.Command {
			case "register-worker":
				logger := logger.Session("register-worker")

				req.Reply(true, nil)

				process, err := server.continuouslyRegisterWorkerDirectly(logger, channel)
				if err != nil {
					logger.Error("failed-to-register", err)
					return
				}

				processes = append(processes, process)

				err = conn.Wait()
				logger.Error("connection-closed", err)

			case "forward-worker":
				logger := logger.Session("forward-worker")

				var forwarded forwardedTCPIP

				select {
				case forwarded = <-forwardedTCPIPs:
					logger.Info("forwarded-tcpip", lager.Data{
						"bound-port": forwarded.boundPort,
					})

					processes = append(processes, forwarded.process)

					process, err := server.continuouslyRegisterForwardedWorker(logger, channel, forwarded.boundPort)
					if err != nil {
						logger.Error("failed-to-register", err)
						return
					}

					processes = append(processes, process)

					err = conn.Wait()
					logger.Error("connection-closed", err)

				case <-time.After(10 * time.Second): // todo better?
					logger.Info("never-forwarded-tcpip")
				}

			default:
				logger.Info("invalid-command", lager.Data{
					"command": request.Command,
				})

				req.Reply(false, nil)
			}
		}
	}
}
Exemple #18
0
func (server *Server) handleChannels(chans <-chan ssh.NewChannel, conn *ssh.ServerConn) {
	// Service the incoming Channel channel in go routine
	for newChannel := range chans {
		server.Logger.Debugf("New SSH Channel Request %s from %s", newChannel.ChannelType(), conn.RemoteAddr().String())
		// TODO: Find Channel ID to log
		go server.handleChannel(newChannel, conn)
	}
}
Exemple #19
0
func newAttacker(conn *ssh.ServerConn, username string, password string) *Attacker {
	addr := conn.RemoteAddr().String()
	addr = ipAddrFromRemoteAddr(addr)
	return &Attacker{addr, username, password}
}
Exemple #20
0
func (server *Server) handleExecRequest(channel ssh.Channel, request *ssh.Request, conn *ssh.ServerConn) {
	doReply := func(ok bool) {
		err := request.Reply(ok, nil)
		if err != nil {
			server.Logger.Errorf("Failed to reply %t to SSH Request from %s due to %s",
				ok, conn.RemoteAddr().String(), err)
		}
		server.Logger.Debugf("Reply to SSH Request `%t` from %s", ok, conn.RemoteAddr().String())
	}
	if len(request.Payload) < 4 {
		server.Logger.Errorf("Payload must not be shorter than 4 bytes, but only %d bytes", len(request.Payload))
		doReply(false)
		return
	}
	header := request.Payload[:4]
	cmdLen := int64(binary.BigEndian.Uint32(header))
	if int64(len(request.Payload)) < 4+cmdLen {
		server.Logger.Errorf("Payload must not be shorter than %d bytes, but only %d bytes", 4+cmdLen, len(request.Payload))
		doReply(false)
		return
	}
	cmd := request.Payload[4 : 4+cmdLen]
	server.Logger.Debugf("Execute command `%s` via SSH from %s",
		string(cmd), conn.RemoteAddr().String())

	shellCmd := exec.Command(server.Config.ShellPath, "-c", string(cmd))

	stdinPipe, err := shellCmd.StdinPipe()
	if err != nil {
		server.Logger.Errorf("Failed to create STDIN pipe error for command: %s", err)
		doReply(false)
		return
	}
	defer stdinPipe.Close()

	stdoutPipe, err := shellCmd.StdoutPipe()
	if err != nil {
		server.Logger.Errorf("Failed to create STDOUT pipe error for command: %s", err)
		doReply(false)
		return
	}
	defer stdoutPipe.Close()

	stderrPipe, err := shellCmd.StderrPipe()
	if err != nil {
		server.Logger.Errorf("Failed to create STDERR pipe error for command: %s", err)
		doReply(false)
		return
	}
	defer stderrPipe.Close()

	sendExitStatus := func() {
		channel.SendRequest("exit-status", false, []byte{0, 0, 0, 0})
		server.Logger.Debugf("Sent exit status 0 to %s", conn.RemoteAddr().String())
	}

	var once sync.Once

	go func() {
		io.Copy(stdinPipe, channel)
		once.Do(sendExitStatus)
	}()
	go func() {
		io.Copy(channel, stdoutPipe)
		once.Do(sendExitStatus)
	}()
	go func() {
		io.Copy(channel.Stderr(), stderrPipe)
		once.Do(sendExitStatus)
	}()

	err = shellCmd.Start()
	if err != nil {
		server.Logger.Errorf("Close SSH Channel from %s due to command error: %s", conn.RemoteAddr().String(), err)
		doReply(false)
		return
	}

	doReply(true)
	_, err = shellCmd.Process.Wait()
	if err != nil {
		_, ok := err.(*exec.ExitError)
		if !ok {
			server.Logger.Errorf("Failed to wait command(PID = %d) due to %s", shellCmd.Process.Pid, err)
		}
		return
	}
}