Example #1
0
func ProxyRequests(logger lager.Logger, channelType string, reqs <-chan *ssh.Request, channel ssh.Channel) {
	logger = logger.Session("proxy-requests", lager.Data{
		"channel-type": channelType,
	})

	logger.Info("started")
	defer logger.Info("completed")
	defer channel.Close()

	for req := range reqs {
		logger.Info("request", lager.Data{
			"type":      req.Type,
			"wantReply": req.WantReply,
			"payload":   req.Payload,
		})
		success, err := channel.SendRequest(req.Type, req.WantReply, req.Payload)
		if err != nil {
			logger.Error("send-request-failed", err)
			continue
		}

		if req.WantReply {
			req.Reply(success, nil)
		}
	}
}
Example #2
0
// Payload: int: command size, string: command
func handleExec(ch ssh.Channel, req *ssh.Request) {
	command := string(req.Payload[4:])

	client, session, err := connectUpstream()
	if err != nil {
		ch.Write([]byte("fail to connect upstream: " + err.Error() + "\r\n"))
		ch.Close()
		return
	}

	exitStatus, err := pipe(ch, client, session, command)
	if err != nil {
		ch.Write([]byte("fail to pipe command:" + err.Error()))
		ch.Close()
		return
	}

	exitStatusBuffer := make([]byte, 4)
	binary.PutUvarint(exitStatusBuffer, uint64(exitStatus))
	log.Println("forward exit-code", exitStatus, "to client")
	_, err = ch.SendRequest("exit-status", false, exitStatusBuffer)
	if err != nil {
		log.Println("Failed to forward exit-status to client:", err)
	}

	ch.Close()
	client.Close()
	log.Println("End of exec")
}
Example #3
0
func (handle *handle) Request(ch ssh.Channel, req *ssh.Request) (bool, error) {
	config.Log.Debug("handle %s", req.Type)
	switch req.Type {
	case "pty-req":
		fallthrough
	case "shell":
		return true, ShellDisabled
	case "env":
		// we store these off??
		return false, nil
	case "exec":
		// it is prefixed with the length so we strip it off
		command := string(req.Payload[4:])
		config.Log.Debug("handle cmd %s", command)
		// find the correct handler and run it
		config.Log.Debug("looking for command in %d", len(repo.Commands()))
		for _, cmd := range repo.Commands() {
			if cmd.Match(command) {
				config.Log.Debug("found match", command)
				code, err := cmd.Run(command, ch)
				exitStatusBuffer := make([]byte, 4)
				binary.PutUvarint(exitStatusBuffer, uint64(code))
				config.Log.Debug("cmd finished", code, err)
				// purposefully ignoring the possible error
				ch.SendRequest("exit-status", false, exitStatusBuffer)
				return true, err
			}
		}

		return true, UnknownCommand
	}
	return true, UnkownRequest
}
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
}
Example #5
0
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
}
Example #6
0
func proxy(reqs1, reqs2 <-chan *ssh.Request, channel1, channel2 ssh.Channel) {
	var closer sync.Once
	closeFunc := func() {
		channel1.Close()
		channel2.Close()
	}

	defer closer.Do(closeFunc)

	closerChan := make(chan bool, 1)

	go func() {
		io.Copy(channel1, channel2)
		closerChan <- true
	}()

	go func() {
		io.Copy(channel2, channel1)
		closerChan <- true
	}()

	for {
		select {
		case req := <-reqs1:
			if req == nil {
				return
			}
			b, err := channel2.SendRequest(req.Type, req.WantReply, req.Payload)
			if err != nil {
				return
			}
			req.Reply(b, nil)

		case req := <-reqs2:
			if req == nil {
				return
			}
			b, err := channel1.SendRequest(req.Type, req.WantReply, req.Payload)
			if err != nil {
				return
			}
			req.Reply(b, nil)
		case <-closerChan:
			return
		}
	}
}
Example #7
0
//Reply handles the operation between a req and channel
func Reply(req *ssh.Request, dest ssh.Channel, c *ConnInsight) {

	dx, err := dest.SendRequest(req.Type, req.WantReply, req.Payload)

	checkError(err, fmt.Sprintf("Request %s processed", req.Type))

	if req.WantReply {
		req.Reply(dx, nil)
	}

	meta := map[string]interface{}{
		"type":    "reply",
		"name":    req.Type,
		"payload": req.Payload,
	}

	c.Aux().Emit(meta)
}
Example #8
0
func forwardRequest(req *ssh.Request, channel ssh.Channel) error {
	if string(req.Type) != "subsystem" && string(req.Type) != "exit-status" {
		req.Reply(false, nil)

		if req.Type == "env" {
			return nil
		}

		return fmt.Errorf("Ignoring unsupported request type: %s", string(req.Type))
	}
	reply, err := channel.SendRequest(req.Type, req.WantReply, req.Payload)
	if err != nil {
		return err
	}
	if req.WantReply {
		req.Reply(reply, nil)
	}

	return nil
}
Example #9
0
/* handleChannelRequests handles proxying requests read from reqs to the SSH
channel c.  info is used for logging. */
func handleChannelRequests(
	reqs <-chan *ssh.Request,
	c ssh.Channel,
	info string,
) {
	for r := range reqs {
		go handleRequest(
			r,
			func( /* Ugh, a closure */
				name string,
				wantReply bool,
				payload []byte,
			) (bool, []byte, error) {
				b, e := c.SendRequest(name, wantReply, payload)
				return b, nil, e
			},
			func() error { return c.Close() }, /* Another? */
			info,
		)
	}
}
			var err error
			channel, requests, err = client.OpenChannel("session", nil)
			Expect(err).NotTo(HaveOccurred())

			go ssh.DiscardRequests(requests)
		})

		AfterEach(func() {
			if channel != nil {
				channel.Close()
			}
		})

		Context("and an exec request fails to unmarshal", func() {
			It("rejects the request", func() {
				accepted, err := channel.SendRequest("exec", true, ssh.Marshal(struct{ Bogus uint32 }{Bogus: 1138}))
				Expect(err).NotTo(HaveOccurred())
				Expect(accepted).To(BeFalse())
			})
		})

		Context("and an env request fails to unmarshal", func() {
			It("rejects the request", func() {
				accepted, err := channel.SendRequest("env", true, ssh.Marshal(struct{ Bogus int }{Bogus: 1234}))
				Expect(err).NotTo(HaveOccurred())
				Expect(accepted).To(BeFalse())
			})
		})

		Context("and a signal request fails to unmarshal", func() {
			It("rejects the request", func() {
Example #11
0
func sendExitStatus(status uint32, channel ssh.Channel) error {
	exit := struct{ Status uint32 }{uint32(0)}
	_, err := channel.SendRequest("exit-status", false, ssh.Marshal(exit))
	return err
}
Example #12
0
func (p *SshConn) serve() error {
	serverConn, chans, reqs, err := ssh.NewServerConn(p, p.config)
	if err != nil {
		log.Println("failed to handshake")
		return (err)
	}

	defer serverConn.Close()

	clientConn, err := p.callbackFn(serverConn)
	if err != nil {
		log.Printf("%s", err.Error())
		return (err)
	}

	defer clientConn.Close()

	go ssh.DiscardRequests(reqs)

	for newChannel := range chans {

		channel2, requests2, err2 := clientConn.OpenChannel(newChannel.ChannelType(), newChannel.ExtraData())
		if err2 != nil {
			log.Printf("Could not accept client channel: %s", err.Error())
			return err
		}

		channel, requests, err := newChannel.Accept()
		if err != nil {
			log.Printf("Could not accept server channel: %s", err.Error())
			return err
		}

		// connect requests
		go func() {
			log.Printf("Waiting for request")

		r:
			for {
				var req *ssh.Request
				var dst ssh.Channel

				select {
				case req = <-requests:
					dst = channel2
				case req = <-requests2:
					dst = channel
				}

				log.Printf("Request: %s %s %s %s\n", dst, req.Type, req.WantReply, req.Payload)

				b, err := dst.SendRequest(req.Type, req.WantReply, req.Payload)
				if err != nil {
					log.Printf("%s", err)
				}

				if req.WantReply {
					req.Reply(b, nil)
				}

				switch req.Type {
				case "exit-status":
					break r
				case "exec":
					// not supported (yet)
				default:
					log.Println(req.Type)
				}
			}

			channel.Close()
			channel2.Close()
		}()

		// connect channels
		log.Printf("Connecting channels.")

		var wrappedChannel io.ReadCloser = channel
		var wrappedChannel2 io.ReadCloser = channel2

		if p.wrapFn != nil {
			// wrappedChannel, err = p.wrapFn(channel)
			wrappedChannel2, err = p.wrapFn(serverConn, channel2)
		}

		go io.Copy(channel2, wrappedChannel)
		go io.Copy(channel, wrappedChannel2)

		defer wrappedChannel.Close()
		defer wrappedChannel2.Close()
	}

	if p.closeFn != nil {
		p.closeFn(serverConn)
	}

	return nil
}
Example #13
0
func (s *server) handleExec(ch ssh.Channel, req *ssh.Request, authInfo map[string]string) {
	defer ch.Close()
	args := strings.SplitN(string(req.Payload[4:]), " ", 2) //remove the 4 bytes of git protocol indicating line length
	command := args[0]
	repo := strings.TrimSuffix(strings.TrimPrefix(args[1], "'/"), ".git'")

	//auth the user
	if os.Getenv("GITHUB_AUTH") == "true" {
		gauth, err := auth.NewGithubAuth()
		if err != nil {
			writePktLine(fmt.Sprintf("github auth error, contact an administrator: %s", err), ch)
			return
		}

		if err := gauth.Authenticate(authInfo["user"], authInfo["public_key"], repo); err != nil {
			writePktLine(fmt.Sprintf("github auth failed: %s", err), ch)
			return
		}
	}

	//check if allowed command
	allowed := []string{pullCmd, pushCmd}
	ok := false
	for _, c := range allowed {
		if command == c {
			ok = true
			break
		}
	}

	if !ok {
		log.Infof("command %s not allowed on this server", command)
		writePktLine(fmt.Sprintf("%s not allowed on this server", command), ch)
		return
	}

	log.Infof("receiving %s command for repo %s", command, repo)

	repoPath, err := s.prepareRepo(repo)
	if err != nil {
		log.Errorf("unable to create repo: %v", err)
		writePktLine(err.Error(), ch)
		return
	}

	defer func() {
		if err := s.unlockRepo(repo); err != nil {
			log.Errorf("unable to unlock repo: %v", err)
			writePktLine(err.Error(), ch)
		}
	}()

	cmd := exec.Command(command, repoPath)
	wg, err := attachCmd(cmd, ch)
	if err != nil {
		log.Errorf("unable to attach command stdio: %v", err)
		writePktLine(err.Error(), ch)
		return
	}

	if err := cmd.Start(); err != nil {
		log.Errorf("unable to start command: %v", err)
		writePktLine(err.Error(), ch)
		return
	}
	wg.Wait()
	syscallErr := cmd.Wait()

	ch.SendRequest("exit-status", false, ssh.Marshal(exitStatus(syscallErr)))
}
Example #14
0
func (self *SSHConn) serve() error {
	serverConn, chans, reqs, err := ssh.NewServerConn(self.Conn, self.config)
	if err != nil {
		return err
	}
	defer serverConn.Close()

	clientConn, err := getClient(serverConn)
	if err != nil {
		return err
	}
	defer clientConn.Close()

	go ssh.DiscardRequests(reqs)

	for newChannel := range chans {
		remoteChannel, remoteRequest, err := clientConn.OpenChannel(newChannel.ChannelType(), newChannel.ExtraData())
		if err != nil {
			return err
		}

		localChannel, localRequest, err := newChannel.Accept()
		if err != nil {
			return err
		}

		// connect requests
		go func() {
			logs.Debug("Waiting for request")
		r:
			for {
				var req *ssh.Request
				var dst ssh.Channel

				select {
				case req = <-localRequest:
					dst = remoteChannel
					logs.Debug("from local to remote")
				case req = <-remoteRequest:
					dst = localChannel
					logs.Debug("from remote to local")
				}

				if req == nil {
					break
				}

				logs.Debug("Request", req.Type, req.WantReply)
				b, err := dst.SendRequest(req.Type, req.WantReply, req.Payload)
				if err != nil {
					logs.Info(err)
				}
				if req.WantReply {
					req.Reply(b, nil)
				}
				switch req.Type {
				case "exit-status":
					break r
				}
			}
		}()

		// connect channels
		logs.Debug("Connecting channels")

		go func() {
			defer remoteChannel.Close()
			io.Copy(remoteChannel, localChannel)
			remoteChannel.SendRequest("exit-status", false, []byte{0, 0, 0, 0})
		}()
		go func() {
			defer localChannel.Close()
			io.Copy(localChannel, remoteChannel)
			localChannel.SendRequest("exit-status", false, []byte{0, 0, 0, 0})
		}()
	}

	closeConn(serverConn)
	return nil
}
Example #15
0
File: sshd.go Project: bachue/pages
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
	}
}