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 (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()
}
Example #3
0
func (s *Session) HandleDirectChannel(newChannel ssh.NewChannel) (bool, ssh.RejectionReason) {

	data, err := UnmarshalTunnelData(newChannel.ExtraData())
	if err != nil {
		return false, ssh.UnknownChannelType
	}

	// look up session by name
	session, host, port := s.Gateway().LookupSessionService(data.Host, uint16(data.Port))
	if session == nil {
		return false, ssh.ConnectionFailed
	}

	// found the service, attempt to open a channel
	data.Host = host
	data.Port = uint32(port)

	c2, err := session.OpenChannel("forwarded-tcpip", MarshalTunnelData(data))
	if err != nil {
		return false, ssh.ConnectionFailed
	}
	defer func() {
		if c2 != nil {
			c2.Close()
		}
	}()

	// accept the channel
	channel, requests, err := newChannel.Accept()
	if err != nil {
		return false, ssh.ResourceShortage
	}

	// cannot return false from this point on
	// also need to accepted close the channel
	defer func() {
		if channel != nil {
			if err := channel.Close(); err != nil {
				glog.Warningf("failed to close accepted channel: %s", err)
			}
		}
	}()

	c, err := NewChannel(s, channel, newChannel.ChannelType(), newChannel.ExtraData())
	if err != nil {
		glog.Errorf("failed to create accepted channel: %s", err)
		return true, 0
	}
	s.AddChannel(c)

	// no failure
	go c.HandleRequests(requests)
	go c.HandleTunnelChannel(c2)

	// do not close channel on exit
	channel = nil
	c2 = nil
	return true, 0
}
Example #4
0
// 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)
	}()
}
Example #5
0
func (s *Session) HandleChannel(newChannel ssh.NewChannel) {
	glog.V(9).Infof("new channel: type = %s, data = %v", newChannel.ChannelType(), newChannel.ExtraData())

	ok := false
	rejection := ssh.UnknownChannelType

	switch newChannel.ChannelType() {
	case "session":
		ok, rejection = s.HandleSessionChannel(newChannel)
	case "direct-tcpip":
		ok, rejection = s.HandleDirectChannel(newChannel)
	}

	if !ok {
		// reject the channel
		if err := newChannel.Reject(rejection, ""); err != nil {
			glog.Warningf("failed to reject channel: %s", err)
		}
	}
}
Example #6
0
func (s *Session) HandleSessionChannel(newChannel ssh.NewChannel) (bool, ssh.RejectionReason) {
	if len(newChannel.ExtraData()) > 0 {
		// do not accept extra data in session channel request
		return false, ssh.Prohibited
	}

	// accept the channel
	channel, requests, err := newChannel.Accept()
	if err != nil {
		return false, ssh.ResourceShortage
	}

	// cannot return false from this point on
	// also need to accepted close the channel
	defer func() {
		if channel != nil {
			if err := channel.Close(); err != nil {
				glog.Warningf("failed to close accepted channel: %s", err)
			}
		}
	}()

	c, err := NewChannel(s, channel, newChannel.ChannelType(), newChannel.ExtraData())
	if err != nil {
		glog.Errorf("failed to create accepted channel: %s", err)
		return true, 0
	}
	s.AddChannel(c)

	// no failure
	go c.HandleRequests(requests)
	go c.HandleSessionChannel()

	// do not close channel on exit
	channel = nil
	return true, 0
}
Example #7
0
// SessionForward performs a regular forward, providing the user with an
// interactive remote host selection if necessary. This forwarding type
// requires agent forwarding in order to work.
func (s *Server) SessionForward(session *Session, newChannel ssh.NewChannel, chans <-chan ssh.NewChannel) {

	// Okay, we're handling this as a regular session
	sesschan, sessReqs, err := newChannel.Accept()
	if err != nil {
		return
	}

	stderr := sesschan.Stderr()

	remote := ""
	switch len(session.Remotes) {
	case 0:
		sesschan.Close()
		return
	case 1:
		remote = session.Remotes[0]
	default:
		comm := rw{Reader: sesschan, Writer: stderr}
		if s.Interactive == nil {
			remote, err = DefaultInteractive(comm, session)
		} else {
			remote, err = s.Interactive(comm, session)
		}
		if err != nil {
			sesschan.Close()
			return
		}
	}

	fmt.Fprintf(stderr, "Connecting to %s\r\n", remote)

	// Set up the agent

	agentChan, agentReqs, err := session.Conn.OpenChannel("*****@*****.**", nil)
	if err != nil {
		fmt.Fprintf(stderr, "\r\n====== sshmux ======\r\n")
		fmt.Fprintf(stderr, "sshmux requires either agent forwarding or secure channel forwarding.\r\n")
		fmt.Fprintf(stderr, "Either enable agent forwarding (-A), or use a ssh -W proxy command.\r\n")
		fmt.Fprintf(stderr, "For more info, see the sshmux wiki.\r\n")
		sesschan.Close()
		return
	}
	defer agentChan.Close()
	go ssh.DiscardRequests(agentReqs)

	// Set up the client

	ag := agent.NewClient(agentChan)

	clientConfig := &ssh.ClientConfig{
		User: session.Conn.User(),
		Auth: []ssh.AuthMethod{
			ssh.PublicKeysCallback(ag.Signers),
		},
	}

	client, err := ssh.Dial("tcp", remote, clientConfig)
	if err != nil {
		fmt.Fprintf(stderr, "Connect failed: %v\r\n", err)
		sesschan.Close()
		return
	}

	// Handle all incoming channel requests
	go func() {
		for newChannel = range chans {
			if newChannel == nil {
				return
			}

			channel2, reqs2, err := client.OpenChannel(newChannel.ChannelType(), newChannel.ExtraData())
			if err != nil {
				x, ok := err.(*ssh.OpenChannelError)
				if ok {
					newChannel.Reject(x.Reason, x.Message)
				} else {
					newChannel.Reject(ssh.Prohibited, "remote server denied channel request")
				}
				continue
			}

			channel, reqs, err := newChannel.Accept()
			if err != nil {
				channel2.Close()
				continue
			}
			go proxy(reqs, reqs2, channel, channel2)
		}
	}()

	// Forward the session channel
	channel2, reqs2, err := client.OpenChannel("session", []byte{})
	if err != nil {
		fmt.Fprintf(stderr, "Remote session setup failed: %v\r\n", err)
		sesschan.Close()
		return
	}

	// Proxy the channel and its requests
	maskedReqs := make(chan *ssh.Request, 1)
	go func() {
		for req := range sessReqs {
			if req.Type == "*****@*****.**" {
				continue
			}
			maskedReqs <- req
		}
	}()
	proxy(maskedReqs, reqs2, sesschan, channel2)

}
Example #8
0
func (svr *sshServer) handleChanReq(chanReq ssh.NewChannel) {
	fmt.Fprintf(sshServerDebugStream, "channel request: %v, extra: '%v'\n", chanReq.ChannelType(), hex.EncodeToString(chanReq.ExtraData()))
	switch chanReq.ChannelType() {
	case "session":
		if ch, reqs, err := chanReq.Accept(); err != nil {
			fmt.Fprintf(sshServerDebugStream, "fail to accept channel request: %v\n", err)
			chanReq.Reject(ssh.ResourceShortage, "channel accept failure")
		} else {
			chsvr := &sshSessionChannelServer{
				sshChannelServer: &sshChannelServer{svr, chanReq, ch, reqs},
				env:              append([]string{}, os.Environ()...),
			}
			chsvr.handle()
		}
	default:
		chanReq.Reject(ssh.UnknownChannelType, "channel type is not a session")
	}
}
Example #9
0
/* handleChan handles a single channel request from sc, proxying it to the
client.  General logging messages will be written to lg, and channel-specific
data and messages will be written to a new file in ldir. */
func handleChan(
	nc ssh.NewChannel,
	client ssh.Conn,
	ldir string,
	lg *log.Logger,
	direction string,
) {
	/* Log the channel request */
	crl := fmt.Sprintf(
		"Type:%q Data:%q Direction:%q",
		nc.ChannelType(),
		nc.ExtraData(),
		direction,
	)

	/* Pass to server */
	cc, creqs, err := client.OpenChannel(
		nc.ChannelType(),
		nc.ExtraData(),
	)
	if nil != err {
		go rejectChannel(err, crl, nc, lg)
		return
	}

	defer cc.Close()

	/* Make channel to attacker, defer close */
	ac, areqs, err := nc.Accept()
	if nil != err {
		lg.Printf(
			"Unable to accept channel request of type %q: %v",
			nc.ChannelType(),
			err,
		)
		return
	}
	defer ac.Close()

	/* Channel worked, make a logger for it */
	clg, lf, clgn, err := logChannel(ldir, nc)
	if nil != err {
		lg.Printf(
			"Unable to open log file for channel of type %q:%v",
			nc.ChannelType(),
			err,
		)
		return
	}
	defer lf.Close()
	clg.Printf("Start of log")

	/* Proxy requests on channels */
	go handleReqs(areqs, Channel{oc: cc}, clg, "attacker->server")
	go handleReqs(creqs, Channel{oc: ac}, clg, "server->attacker")

	/* Log the channel */
	lg.Printf("Channel %s Log:%q", crl, clgn)

	/* Proxy comms */
	wg := make(chan int, 4)
	go ProxyChannel(
		ac,
		cc,
		clg,
		"server->attacker",
		wg,
		1,
	)
	go ProxyChannel(
		cc,
		ac,
		clg,
		"attacker->server",
		wg,
		1,
	)
	go ProxyChannel(
		cc.Stderr(),
		ac.Stderr(),
		clg,
		"attacker-(err)->server",
		wg,
		0,
	)
	go ProxyChannel(
		ac.Stderr(),
		cc.Stderr(),
		clg,
		"server-(err)->attacker",
		wg,
		0,
	)
	sum := 0
	for i := range wg {
		sum += i
		if 2 <= sum {
			break
		}
	}

	/* TODO: Proxy comms */
}
Example #10
0
func proxyChannel(c *ConnInsight, mcha ssh.Channel, mreq <-chan *ssh.Request, master ssh.NewChannel, client *SSHClient, killer <-chan struct{}) error {

	do := new(sync.Once)
	cochan, coreq, err := client.OpenChannel(master.ChannelType(), master.ExtraData())

	checkError(err, fmt.Sprintf("Creating Client Channel for %s", client.RemoteAddr().String()))

	if err != nil {
		return err
	}

	stop := make(chan struct{})
	endClose := func() { close(stop) }

	flux.GoDefer("proxyChannelCopy", func() {
		defer cochan.Close()
		defer mcha.Close()

		func() {
		ploop:
			for {
				select {
				case <-stop:
					break ploop
				case <-killer:
					break ploop
				case slx, ok := <-coreq:
					if !ok {
						return
					}

					Reply(slx, mcha, c)

					switch slx.Type {
					case "exit-status":
						break ploop
					}

				case mlx, ok := <-mreq:
					if !ok {
						return
					}

					Reply(mlx, cochan, c)

					switch mlx.Type {
					case "exit-status":
						break ploop
					}
				}
			}
		}()
	})

	mastercloser := io.ReadCloser(mcha)
	slavecloser := io.ReadCloser(cochan)

	wrapmaster := io.MultiWriter(mcha, c.Out())
	wrapsl := io.MultiWriter(cochan, c.In())

	flux.GoDefer("CopyToSlave", func() {
		defer do.Do(endClose)
		io.Copy(wrapsl, mastercloser)
	})

	flux.GoDefer("CopyToMaster", func() {
		defer do.Do(endClose)
		io.Copy(wrapmaster, slavecloser)
	})

	flux.GoDefer("CopyCloser", func() {
		defer c.Close()

		<-stop

		mx := mastercloser.Close()
		checkError(mx, "Master Writer Closer")

		sx := slavecloser.Close()
		checkError(sx, "Slave Writer Closer")

		ex := client.Close()
		checkError(ex, "Client Writer Closer")
	})

	return nil
}
func (sshClient *sshClient) handleNewDirectTcpipChannel(newChannel ssh.NewChannel) {

	// 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
	}

	targetAddr := fmt.Sprintf("%s:%d",
		directTcpipExtraData.HostToConnect,
		directTcpipExtraData.PortToConnect)

	log.WithContextFields(LogFields{"target": targetAddr}).Debug("dialing")

	// TODO: port forward dial timeout
	// TODO: report ssh.ResourceShortage when appropriate
	// TODO: IPv6 support
	fwdConn, err := net.Dial("tcp4", targetAddr)
	if err != nil {
		sshClient.rejectNewChannel(newChannel, ssh.ConnectionFailed, err.Error())
		return
	}
	defer fwdConn.Close()

	fwdChannel, requests, err := newChannel.Accept()
	if err != nil {
		log.WithContextFields(LogFields{"error": err}).Warning("accept new channel failed")
		return
	}

	sshClient.Lock()
	sshClient.portForwardCount += 1
	sshClient.concurrentPortForwardCount += 1
	if sshClient.concurrentPortForwardCount > sshClient.peakConcurrentPortForwardCount {
		sshClient.peakConcurrentPortForwardCount = sshClient.concurrentPortForwardCount
	}
	sshClient.Unlock()

	log.WithContextFields(LogFields{"target": targetAddr}).Debug("relaying")

	go ssh.DiscardRequests(requests)

	defer fwdChannel.Close()

	// When idle port forward traffic rules are in place, wrap fwdConn
	// in an IdleTimeoutConn configured to reset idle on writes as well
	// as read. This ensures the port forward idle timeout only happens
	// when both upstream and downstream directions are are idle.

	if sshClient.trafficRules.IdlePortForwardTimeoutMilliseconds > 0 {
		fwdConn = psiphon.NewIdleTimeoutConn(
			fwdConn,
			time.Duration(sshClient.trafficRules.IdlePortForwardTimeoutMilliseconds)*time.Millisecond,
			true)
	}

	// relay channel to forwarded connection
	// TODO: relay errors to fwdChannel.Stderr()?

	var bytesUp, bytesDown int64

	relayWaitGroup := new(sync.WaitGroup)
	relayWaitGroup.Add(1)
	go func() {
		defer relayWaitGroup.Done()
		var err error
		bytesUp, err = copyWithThrottle(
			fwdConn, fwdChannel, sshClient.trafficRules.ThrottleUpstreamSleepMilliseconds)
		if err != nil {
			log.WithContextFields(LogFields{"error": err}).Warning("upstream relay failed")
		}
	}()
	bytesDown, err = copyWithThrottle(
		fwdChannel, fwdConn, sshClient.trafficRules.ThrottleDownstreamSleepMilliseconds)
	if err != nil {
		log.WithContextFields(LogFields{"error": err}).Warning("downstream relay failed")
	}
	fwdChannel.CloseWrite()
	relayWaitGroup.Wait()

	sshClient.Lock()
	sshClient.concurrentPortForwardCount -= 1
	sshClient.bytesUp += bytesUp
	sshClient.bytesDown += bytesDown
	sshClient.Unlock()

	log.WithContextFields(LogFields{"target": targetAddr}).Debug("exiting")
}
Example #12
0
/* handleChannel proxies a channel request command or shell to the ssh
connection sc. */
func handleNewChannel(cr ssh.NewChannel, sc ssh.Conn, info string) {
	log.Printf(
		"%v Type:%q Data:%q NewChannel",
		info,
		cr.ChannelType(),
		cr.ExtraData(),
	)

	/* Make the same request to the other side */
	och, oreqs, err := sc.OpenChannel(cr.ChannelType(), cr.ExtraData())
	if nil != err {
		/* If we can't log it, and reject the client */
		oe, ok := err.(*ssh.OpenChannelError)
		var (
			reason  ssh.RejectionReason
			message string
		)
		if !ok {
			log.Printf(
				"%v Type:%q Data:%q Unable to open channel: "+
					"%v",
				info,
				cr.ChannelType(),
				cr.ExtraData(),
				err,
			)
			reason = ssh.ConnectionFailed
			message = "Fail"
			message = err.Error()
		} else {
			log.Printf(
				"%v Type:%q Data:%q Reason:%q Message:%q "+
					"Unable to open channel",
				info,
				cr.ChannelType(),
				cr.ExtraData(),
				oe.Reason.String(),
				oe.Message,
			)
			reason = oe.Reason
			message = oe.Message
		}
		if err := cr.Reject(reason, message); nil != err {
			log.Printf(
				"%v Unable to pass on channel rejecton "+
					"request: %v",
				info,
				err,
			)
		}
		return
	}
	defer och.Close()

	/* Accept the channel request from the requestor */
	rch, rreqs, err := cr.Accept()
	if nil != err {
		log.Printf(
			"%v Unable to accept request for a channel of type "+
				"%q: %v",
			cr.ChannelType(),
			info,
			err,
		)
		return
	}
	defer rch.Close()

	/* Handle passing requests between channels */
	hcrinfo := fmt.Sprintf(" %v ChannelType:%q", info, cr.ChannelType())
	go handleChannelRequests(
		rreqs,
		och,
		hcrinfo+" ReqDir:AsDirection",
	)
	go handleChannelRequests(
		oreqs,
		rch,
		hcrinfo+" ReqDir:AgainstDirection",
	)

	log.Printf(
		"%v Type:%q Data:%q Opened",
		info,
		cr.ChannelType(),
		cr.ExtraData(),
	)

	/* For now, print out read data */
	done := make(chan struct{}, 4)
	go copyOut(och, rch, done)
	go copyOut(rch, och, done)
	go copyOut(och.Stderr(), rch.Stderr(), done)
	go copyOut(rch.Stderr(), och.Stderr(), done)

	/* Wait for a pipe to break */
	<-done
	fmt.Printf("\nDone.\n")
}
Example #13
0
							channelResponse := make([]byte, 7)
							channel.Read(channelResponse)
							Expect(string(channelResponse)).To(Equal("goodbye"))

							channel.Close()
							serverConn.Close()
						}
					})

					It("gets forwarded to the client", func() {
						var newChannel ssh.NewChannel
						Eventually(clientChannels).Should(Receive(&newChannel))

						Expect(newChannel.ChannelType()).To(Equal("test-channel"))
						Expect(newChannel.ExtraData()).To(Equal([]byte("extra-data")))

						channel, requests, err := newChannel.Accept()
						Expect(err).NotTo(HaveOccurred())
						Expect(channel).NotTo(BeNil())
						Expect(requests).NotTo(BeClosed())

						channelRequest := make([]byte, 5)
						channel.Read(channelRequest)
						Expect(string(channelRequest)).To(Equal("hello"))

						channel.Write([]byte("goodbye"))
						channel.Close()
					})
				})
			})