Beispiel #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)
	}
}
Beispiel #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()
	}
}
Beispiel #3
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
	}
}
Beispiel #4
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
	}
}
Beispiel #5
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
}
Beispiel #6
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()
		}
	}
}
Beispiel #7
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
		}()
	}
}
Beispiel #8
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)
			}
		}
	}
}