diff --git a/cmd/sshpiperd/daemon.go b/cmd/sshpiperd/daemon.go index 81823a64..63f120b5 100644 --- a/cmd/sshpiperd/daemon.go +++ b/cmd/sshpiperd/daemon.go @@ -16,7 +16,7 @@ import ( ) type daemon struct { - config *ssh.PiperConfig + config *plugin.GrpcPluginConfig lis net.Listener loginGraceTime time.Duration @@ -25,7 +25,7 @@ type daemon struct { } func newDaemon(ctx *cli.Context) (*daemon, error) { - config := &ssh.PiperConfig{} + config := &plugin.GrpcPluginConfig{} config.SetDefaults() keybase64 := ctx.String("server-key-data") @@ -139,7 +139,7 @@ func (d *daemon) run() error { errorc := make(chan error) go func() { - p, err := ssh.NewSSHPiperConn(c, d.config) + p, err := ssh.NewSSHPiperConn(c, &d.config.PiperConfig) if err != nil { errorc <- err @@ -200,8 +200,16 @@ func (d *daemon) run() error { } } + if d.config.PipeStartCallback != nil { + d.config.PipeStartCallback(p.DownstreamConnMeta(), p.ChallengeContext()) + } + err = p.WaitWithHook(uphook, downhook) + if d.config.PipeErrorCallback != nil { + d.config.PipeErrorCallback(p.DownstreamConnMeta(), p.ChallengeContext(), err) + } + log.Infof("connection from %v closed reason: %v", c.RemoteAddr(), err) }(conn) } diff --git a/cmd/sshpiperd/internal/plugin/chain.go b/cmd/sshpiperd/internal/plugin/chain.go index f8a8691a..e392b1ba 100644 --- a/cmd/sshpiperd/internal/plugin/chain.go +++ b/cmd/sshpiperd/internal/plugin/chain.go @@ -10,7 +10,7 @@ import ( ) type ChainPlugins struct { - pluginsCallback []*ssh.PiperConfig + pluginsCallback []*GrpcPluginConfig plugins []*GrpcPlugin } @@ -96,7 +96,7 @@ func (cp *ChainPlugins) NextAuthMethods(conn ssh.ConnMetadata, challengeCtx ssh. return methods, nil } -func (cp *ChainPlugins) InstallPiperConfig(config *ssh.PiperConfig) error { +func (cp *ChainPlugins) InstallPiperConfig(config *GrpcPluginConfig) error { config.CreateChallengeContext = func(conn ssh.ConnMetadata) (ssh.ChallengeContext, error) { ctx, err := cp.CreateChallengeContext(conn) @@ -140,5 +140,19 @@ func (cp *ChainPlugins) InstallPiperConfig(config *ssh.PiperConfig) error { return "" } + config.PipeStartCallback = func(conn ssh.ConnMetadata, challengeCtx ssh.ChallengeContext) { + cur := cp.pluginsCallback[challengeCtx.(*chainConnMeta).current] + if cur.PipeStartCallback != nil { + cur.PipeStartCallback(conn, challengeCtx) + } + } + + config.PipeErrorCallback = func(conn ssh.ConnMetadata, challengeCtx ssh.ChallengeContext, err error) { + cur := cp.pluginsCallback[challengeCtx.(*chainConnMeta).current] + if cur.PipeErrorCallback != nil { + cur.PipeErrorCallback(conn, challengeCtx, err) + } + } + return nil } diff --git a/cmd/sshpiperd/internal/plugin/grpc.go b/cmd/sshpiperd/internal/plugin/grpc.go index d167288a..b34083f6 100644 --- a/cmd/sshpiperd/internal/plugin/grpc.go +++ b/cmd/sshpiperd/internal/plugin/grpc.go @@ -19,6 +19,13 @@ import ( "google.golang.org/grpc/credentials/insecure" ) +type GrpcPluginConfig struct { + ssh.PiperConfig + + PipeStartCallback func(conn ssh.ConnMetadata, challengeCtx ssh.ChallengeContext) + PipeErrorCallback func(conn ssh.ConnMetadata, challengeCtx ssh.ChallengeContext, err error) +} + type GrpcPlugin struct { Name string OnNextPlugin func(conn ssh.ChallengeContext, upstream *libplugin.UpstreamNextPluginAuth) error @@ -41,7 +48,7 @@ func DialGrpc(conn *grpc.ClientConn) (*GrpcPlugin, error) { return p, nil } -func (g *GrpcPlugin) InstallPiperConfig(config *ssh.PiperConfig) error { +func (g *GrpcPlugin) InstallPiperConfig(config *GrpcPluginConfig) error { cb, err := g.client.ListCallbacks(context.Background(), &libplugin.ListCallbackRequest{}) if err != nil { @@ -119,6 +126,10 @@ func (g *GrpcPlugin) InstallPiperConfig(config *ssh.PiperConfig) error { config.BannerCallback = g.BannerCallback case "VerifyHostKey": // ignore + case "PipeStart": + config.PipeStartCallback = g.PipeStartCallback + case "PipeError": + config.PipeErrorCallback = g.PipeErrorCallback default: return fmt.Errorf("unknown callback %s", c) } @@ -127,8 +138,8 @@ func (g *GrpcPlugin) InstallPiperConfig(config *ssh.PiperConfig) error { return nil } -func (g *GrpcPlugin) CreatePiperConfig() (*ssh.PiperConfig, error) { - config := &ssh.PiperConfig{} +func (g *GrpcPlugin) CreatePiperConfig() (*GrpcPluginConfig, error) { + config := &GrpcPluginConfig{} return config, g.InstallPiperConfig(config) } @@ -474,6 +485,21 @@ func (g *GrpcPlugin) BannerCallback(conn ssh.ConnMetadata, challengeCtx ssh.Chal return reply.GetMessage() } +func (g *GrpcPlugin) PipeStartCallback(conn ssh.ConnMetadata, challengeCtx ssh.ChallengeContext) { + meta := toMeta(challengeCtx, conn) + _, _ = g.client.PipeStartNotice(context.Background(), &libplugin.PipeStartNoticeRequest{ + Meta: meta, + }) +} + +func (g *GrpcPlugin) PipeErrorCallback(conn ssh.ConnMetadata, challengeCtx ssh.ChallengeContext, pipeerr error) { + meta := toMeta(challengeCtx, conn) + _, _ = g.client.PipeErrorNotice(context.Background(), &libplugin.PipeErrorNoticeRequest{ + Meta: meta, + Error: pipeerr.Error(), + }) +} + func (g *GrpcPlugin) RecvLogs(writer io.Writer) error { uid, err := uuid.NewRandom() if err != nil { diff --git a/cmd/sshpiperd/sshpiperd b/cmd/sshpiperd/sshpiperd new file mode 100755 index 00000000..03083a6c Binary files /dev/null and b/cmd/sshpiperd/sshpiperd differ diff --git a/crypto b/crypto index 0598fea7..d8f78e96 160000 --- a/crypto +++ b/crypto @@ -1 +1 @@ -Subproject commit 0598fea726dd2233beea0324f988d0587efe246d +Subproject commit d8f78e9617d38c367b9e86ebc355a60b5e82e479 diff --git a/e2e/plugin_test.go b/e2e/plugin_test.go new file mode 100644 index 00000000..d3fa23d2 --- /dev/null +++ b/e2e/plugin_test.go @@ -0,0 +1,196 @@ +package e2e_test + +import ( + "fmt" + "net" + "net/http" + "net/rpc" + "testing" + "time" + + "golang.org/x/crypto/ssh" +) + +func createFakeSshServer(config *ssh.ServerConfig) net.Listener { + config.SetDefaults() + private, _ := ssh.ParsePrivateKey([]byte(testprivatekey)) + config.AddHostKey(private) + + l, err := net.Listen("tcp", "0.0.0.0:0") + if err != nil { + panic(err) + } + + go func() { + for { + l, err := l.Accept() + if err != nil { + break + } + + go func() { + _, _, reqs, err := ssh.NewServerConn(l, config) + if err != nil { + panic(err) + } + + go ssh.DiscardRequests(reqs) + }() + } + }() + + return l +} + +type rpcServer struct { + NewConnectionCallback func() error + PasswordCallback func(string) (string, error) + PipeStartCallback func() error + PipeErrorCallback func(string) error +} + +func (r *rpcServer) NewConnection(args string, reply *string) error { + *reply = "" + + if r.NewConnectionCallback != nil { + return r.NewConnectionCallback() + } + + return nil +} + +func (r *rpcServer) PipeStart(args string, reply *string) error { + *reply = "" + + if r.PipeStartCallback != nil { + return r.PipeStartCallback() + } + + return nil +} + +func (r *rpcServer) PipeError(args string, reply *string) error { + *reply = "" + + if r.PipeErrorCallback != nil { + return r.PipeErrorCallback(args) + } + + return nil +} + +func (r *rpcServer) Password(args string, reply *string) error { + if r.PasswordCallback != nil { + rpl, err := r.PasswordCallback(args) + if err != nil { + return err + } + *reply = rpl + return nil + } + + *reply = "" + return nil +} + +func createRpcServer(r *rpcServer) net.Listener { + l, err := net.Listen("tcp", "0.0.0.0:0") + if err != nil { + panic(err) + } + + _ = rpc.RegisterName("TestPlugin", r) + rpc.HandleHTTP() + go func() { + _ = http.Serve(l, nil) + }() + + return l +} + +func TestPlugin(t *testing.T) { + + sshsvr := createFakeSshServer(&ssh.ServerConfig{ + PasswordCallback: func(c ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) { + if string(pass) != "rpcpassword" { + return nil, fmt.Errorf("invalid password") + } + return nil, nil + }, + }) + defer sshsvr.Close() + + cbtriggered := make(map[string]bool) + + rpcsvr := createRpcServer(&rpcServer{ + NewConnectionCallback: func() error { + cbtriggered["NewConnection"] = true + return nil + }, + PasswordCallback: func(pass string) (string, error) { + cbtriggered["Password"] = true + return "rpcpassword", nil + }, + PipeStartCallback: func() error { + cbtriggered["PipeStart"] = true + return nil + }, + PipeErrorCallback: func(err string) error { + cbtriggered["PipeError"] = true + return nil + }, + }) + defer rpcsvr.Close() + + piperaddr, piperport := nextAvailablePiperAddress() + + piper, _, _, err := runCmd("/sshpiperd/sshpiperd", + "-p", + piperport, + "/sshpiperd/plugins/testplugin", + "--testsshserver", + sshsvr.Addr().String(), + "--rpcserver", + rpcsvr.Addr().String(), + ) + + if err != nil { + t.Errorf("failed to run sshpiperd: %v", err) + } + + defer killCmd(piper) + + waitForEndpointReady(piperaddr) + + client, err := ssh.Dial("tcp", piperaddr, &ssh.ClientConfig{ + User: "username", + Auth: []ssh.AuthMethod{ + ssh.Password("yourpassword"), + }, + HostKeyCallback: ssh.InsecureIgnoreHostKey(), + }) + + if err != nil { + t.Fatalf("failed to connect to sshpiperd: %v", err) + } + + client.Close() + + time.Sleep(1 * time.Second) // wait for callbacks to be triggered + + if !cbtriggered["NewConnection"] { + t.Errorf("NewConnection callback not triggered") + } + + if !cbtriggered["Password"] { + t.Errorf("Password callback not triggered") + } + + if !cbtriggered["PipeStart"] { + t.Errorf("PipeStart callback not triggered") + } + + if !cbtriggered["PipeError"] { + t.Errorf("PipeError callback not triggered") + } +} diff --git a/libplugin/plugin.pb.go b/libplugin/plugin.pb.go index 78b20b42..ed160af7 100644 --- a/libplugin/plugin.pb.go +++ b/libplugin/plugin.pb.go @@ -1825,7 +1825,7 @@ func (x *VerifyHostKeyRequest) GetNetaddress() string { return "" } -type VerifyHostKeyReply struct { +type VerifyHostKeyResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields @@ -1833,8 +1833,8 @@ type VerifyHostKeyReply struct { Verified bool `protobuf:"varint,1,opt,name=verified,proto3" json:"verified,omitempty"` } -func (x *VerifyHostKeyReply) Reset() { - *x = VerifyHostKeyReply{} +func (x *VerifyHostKeyResponse) Reset() { + *x = VerifyHostKeyResponse{} if protoimpl.UnsafeEnabled { mi := &file_plugin_proto_msgTypes[32] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1842,13 +1842,13 @@ func (x *VerifyHostKeyReply) Reset() { } } -func (x *VerifyHostKeyReply) String() string { +func (x *VerifyHostKeyResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*VerifyHostKeyReply) ProtoMessage() {} +func (*VerifyHostKeyResponse) ProtoMessage() {} -func (x *VerifyHostKeyReply) ProtoReflect() protoreflect.Message { +func (x *VerifyHostKeyResponse) ProtoReflect() protoreflect.Message { mi := &file_plugin_proto_msgTypes[32] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1860,18 +1860,196 @@ func (x *VerifyHostKeyReply) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use VerifyHostKeyReply.ProtoReflect.Descriptor instead. -func (*VerifyHostKeyReply) Descriptor() ([]byte, []int) { +// Deprecated: Use VerifyHostKeyResponse.ProtoReflect.Descriptor instead. +func (*VerifyHostKeyResponse) Descriptor() ([]byte, []int) { return file_plugin_proto_rawDescGZIP(), []int{32} } -func (x *VerifyHostKeyReply) GetVerified() bool { +func (x *VerifyHostKeyResponse) GetVerified() bool { if x != nil { return x.Verified } return false } +type PipeStartNoticeRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Meta *ConnMeta `protobuf:"bytes,1,opt,name=meta,proto3" json:"meta,omitempty"` +} + +func (x *PipeStartNoticeRequest) Reset() { + *x = PipeStartNoticeRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_plugin_proto_msgTypes[33] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PipeStartNoticeRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PipeStartNoticeRequest) ProtoMessage() {} + +func (x *PipeStartNoticeRequest) ProtoReflect() protoreflect.Message { + mi := &file_plugin_proto_msgTypes[33] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PipeStartNoticeRequest.ProtoReflect.Descriptor instead. +func (*PipeStartNoticeRequest) Descriptor() ([]byte, []int) { + return file_plugin_proto_rawDescGZIP(), []int{33} +} + +func (x *PipeStartNoticeRequest) GetMeta() *ConnMeta { + if x != nil { + return x.Meta + } + return nil +} + +type PipeStartNoticeResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *PipeStartNoticeResponse) Reset() { + *x = PipeStartNoticeResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_plugin_proto_msgTypes[34] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PipeStartNoticeResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PipeStartNoticeResponse) ProtoMessage() {} + +func (x *PipeStartNoticeResponse) ProtoReflect() protoreflect.Message { + mi := &file_plugin_proto_msgTypes[34] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PipeStartNoticeResponse.ProtoReflect.Descriptor instead. +func (*PipeStartNoticeResponse) Descriptor() ([]byte, []int) { + return file_plugin_proto_rawDescGZIP(), []int{34} +} + +type PipeErrorNoticeRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Meta *ConnMeta `protobuf:"bytes,1,opt,name=meta,proto3" json:"meta,omitempty"` + Error string `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"` +} + +func (x *PipeErrorNoticeRequest) Reset() { + *x = PipeErrorNoticeRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_plugin_proto_msgTypes[35] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PipeErrorNoticeRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PipeErrorNoticeRequest) ProtoMessage() {} + +func (x *PipeErrorNoticeRequest) ProtoReflect() protoreflect.Message { + mi := &file_plugin_proto_msgTypes[35] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PipeErrorNoticeRequest.ProtoReflect.Descriptor instead. +func (*PipeErrorNoticeRequest) Descriptor() ([]byte, []int) { + return file_plugin_proto_rawDescGZIP(), []int{35} +} + +func (x *PipeErrorNoticeRequest) GetMeta() *ConnMeta { + if x != nil { + return x.Meta + } + return nil +} + +func (x *PipeErrorNoticeRequest) GetError() string { + if x != nil { + return x.Error + } + return "" +} + +type PipeErrorNoticeResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *PipeErrorNoticeResponse) Reset() { + *x = PipeErrorNoticeResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_plugin_proto_msgTypes[36] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PipeErrorNoticeResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PipeErrorNoticeResponse) ProtoMessage() {} + +func (x *PipeErrorNoticeResponse) ProtoReflect() protoreflect.Message { + mi := &file_plugin_proto_msgTypes[36] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PipeErrorNoticeResponse.ProtoReflect.Descriptor instead. +func (*PipeErrorNoticeResponse) Descriptor() ([]byte, []int) { + return file_plugin_proto_rawDescGZIP(), []int{36} +} + type KeyboardInteractivePromptRequest_Question struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1884,7 +2062,7 @@ type KeyboardInteractivePromptRequest_Question struct { func (x *KeyboardInteractivePromptRequest_Question) Reset() { *x = KeyboardInteractivePromptRequest_Question{} if protoimpl.UnsafeEnabled { - mi := &file_plugin_proto_msgTypes[34] + mi := &file_plugin_proto_msgTypes[38] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1897,7 +2075,7 @@ func (x *KeyboardInteractivePromptRequest_Question) String() string { func (*KeyboardInteractivePromptRequest_Question) ProtoMessage() {} func (x *KeyboardInteractivePromptRequest_Question) ProtoReflect() protoreflect.Message { - mi := &file_plugin_proto_msgTypes[34] + mi := &file_plugin_proto_msgTypes[38] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2131,75 +2309,100 @@ var file_plugin_proto_rawDesc = []byte{ 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x6e, 0x65, 0x74, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6e, 0x65, 0x74, 0x61, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x22, 0x30, 0x0a, 0x12, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x48, 0x6f, 0x73, 0x74, - 0x4b, 0x65, 0x79, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x76, 0x65, 0x72, 0x69, - 0x66, 0x69, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x76, 0x65, 0x72, 0x69, - 0x66, 0x69, 0x65, 0x64, 0x2a, 0x4d, 0x0a, 0x0a, 0x41, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x68, - 0x6f, 0x64, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, - 0x50, 0x41, 0x53, 0x53, 0x57, 0x4f, 0x52, 0x44, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x50, 0x55, - 0x42, 0x4c, 0x49, 0x43, 0x4b, 0x45, 0x59, 0x10, 0x02, 0x12, 0x18, 0x0a, 0x14, 0x4b, 0x45, 0x59, - 0x42, 0x4f, 0x41, 0x52, 0x44, 0x5f, 0x49, 0x4e, 0x54, 0x45, 0x52, 0x41, 0x43, 0x54, 0x49, 0x56, - 0x45, 0x10, 0x03, 0x32, 0xc1, 0x07, 0x0a, 0x0e, 0x53, 0x73, 0x68, 0x50, 0x69, 0x70, 0x65, 0x72, - 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x12, 0x36, 0x0a, 0x04, 0x4c, 0x6f, 0x67, 0x73, 0x12, 0x1a, - 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, - 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0e, 0x2e, 0x6c, 0x69, 0x62, - 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4c, 0x6f, 0x67, 0x22, 0x00, 0x30, 0x01, 0x12, 0x52, - 0x0a, 0x0d, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x73, 0x12, - 0x1e, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4c, 0x69, 0x73, 0x74, - 0x43, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1f, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4c, 0x69, 0x73, 0x74, - 0x43, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x12, 0x54, 0x0a, 0x0d, 0x4e, 0x65, 0x77, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, - 0x4e, 0x65, 0x77, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, - 0x2e, 0x4e, 0x65, 0x77, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5a, 0x0a, 0x0f, 0x4e, 0x65, 0x78, 0x74, - 0x41, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x12, 0x21, 0x2e, 0x6c, 0x69, - 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4e, 0x65, 0x78, 0x74, 0x41, 0x75, 0x74, 0x68, - 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, - 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4e, 0x65, 0x78, 0x74, 0x41, - 0x75, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x00, 0x12, 0x45, 0x0a, 0x08, 0x4e, 0x6f, 0x6e, 0x65, 0x41, 0x75, 0x74, 0x68, - 0x12, 0x1a, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4e, 0x6f, 0x6e, - 0x65, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, - 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4e, 0x6f, 0x6e, 0x65, 0x41, 0x75, 0x74, - 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x51, 0x0a, 0x0c, 0x50, - 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x41, 0x75, 0x74, 0x68, 0x12, 0x1e, 0x2e, 0x6c, 0x69, - 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, - 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6c, 0x69, - 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, - 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x54, - 0x0a, 0x0d, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x41, 0x75, 0x74, 0x68, 0x12, - 0x1f, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x50, 0x75, 0x62, 0x6c, - 0x69, 0x63, 0x4b, 0x65, 0x79, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x20, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x50, 0x75, 0x62, - 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x00, 0x12, 0x73, 0x0a, 0x17, 0x4b, 0x65, 0x79, 0x62, 0x6f, 0x61, 0x72, 0x64, - 0x49, 0x6e, 0x74, 0x65, 0x72, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x41, 0x75, 0x74, 0x68, 0x12, + 0x73, 0x73, 0x22, 0x33, 0x0a, 0x15, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x48, 0x6f, 0x73, 0x74, + 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x76, + 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x76, + 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x22, 0x41, 0x0a, 0x16, 0x50, 0x69, 0x70, 0x65, 0x53, + 0x74, 0x61, 0x72, 0x74, 0x4e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x27, 0x0a, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x13, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, + 0x4d, 0x65, 0x74, 0x61, 0x52, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x22, 0x19, 0x0a, 0x17, 0x50, 0x69, + 0x70, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x4e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x57, 0x0a, 0x16, 0x50, 0x69, 0x70, 0x65, 0x45, 0x72, 0x72, + 0x6f, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x27, 0x0a, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, + 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x4d, 0x65, + 0x74, 0x61, 0x52, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, + 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x19, + 0x0a, 0x17, 0x50, 0x69, 0x70, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x63, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2a, 0x4d, 0x0a, 0x0a, 0x41, 0x75, 0x74, + 0x68, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x10, + 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x50, 0x41, 0x53, 0x53, 0x57, 0x4f, 0x52, 0x44, 0x10, 0x01, 0x12, + 0x0d, 0x0a, 0x09, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x4b, 0x45, 0x59, 0x10, 0x02, 0x12, 0x18, + 0x0a, 0x14, 0x4b, 0x45, 0x59, 0x42, 0x4f, 0x41, 0x52, 0x44, 0x5f, 0x49, 0x4e, 0x54, 0x45, 0x52, + 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x03, 0x32, 0xfc, 0x08, 0x0a, 0x0e, 0x53, 0x73, 0x68, + 0x50, 0x69, 0x70, 0x65, 0x72, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x12, 0x36, 0x0a, 0x04, 0x4c, + 0x6f, 0x67, 0x73, 0x12, 0x1a, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, + 0x53, 0x74, 0x61, 0x72, 0x74, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x0e, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4c, 0x6f, 0x67, 0x22, + 0x00, 0x30, 0x01, 0x12, 0x52, 0x0a, 0x0d, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x61, 0x6c, 0x6c, 0x62, + 0x61, 0x63, 0x6b, 0x73, 0x12, 0x1e, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x54, 0x0a, 0x0d, 0x4e, 0x65, 0x77, 0x43, 0x6f, + 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4e, 0x65, 0x77, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6c, 0x69, 0x62, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4e, 0x65, 0x77, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5a, 0x0a, + 0x0f, 0x4e, 0x65, 0x78, 0x74, 0x41, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, + 0x12, 0x21, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4e, 0x65, 0x78, + 0x74, 0x41, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, + 0x4e, 0x65, 0x78, 0x74, 0x41, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x45, 0x0a, 0x08, 0x4e, 0x6f, 0x6e, + 0x65, 0x41, 0x75, 0x74, 0x68, 0x12, 0x1a, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x2e, 0x4e, 0x6f, 0x6e, 0x65, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4e, 0x6f, + 0x6e, 0x65, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, + 0x12, 0x51, 0x0a, 0x0c, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x41, 0x75, 0x74, 0x68, + 0x12, 0x1e, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x50, 0x61, 0x73, + 0x73, 0x77, 0x6f, 0x72, 0x64, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1f, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x50, 0x61, 0x73, + 0x73, 0x77, 0x6f, 0x72, 0x64, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x12, 0x54, 0x0a, 0x0d, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, + 0x41, 0x75, 0x74, 0x68, 0x12, 0x1f, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x41, 0x75, 0x74, 0x68, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x73, 0x0a, 0x17, 0x4b, 0x65, 0x79, + 0x62, 0x6f, 0x61, 0x72, 0x64, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, + 0x41, 0x75, 0x74, 0x68, 0x12, 0x29, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x2e, 0x4b, 0x65, 0x79, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x61, 0x63, + 0x74, 0x69, 0x76, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x29, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4b, 0x65, 0x79, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x41, - 0x75, 0x74, 0x68, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x29, 0x2e, 0x6c, 0x69, 0x62, - 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4b, 0x65, 0x79, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x49, - 0x6e, 0x74, 0x65, 0x72, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x28, 0x01, 0x30, 0x01, 0x12, 0x78, 0x0a, 0x19, 0x55, 0x70, 0x73, - 0x74, 0x72, 0x65, 0x61, 0x6d, 0x41, 0x75, 0x74, 0x68, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, - 0x4e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x12, 0x2b, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, - 0x69, 0x6e, 0x2e, 0x55, 0x70, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x41, 0x75, 0x74, 0x68, 0x46, - 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x4e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, - 0x55, 0x70, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x41, 0x75, 0x74, 0x68, 0x46, 0x61, 0x69, 0x6c, - 0x75, 0x72, 0x65, 0x4e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x12, 0x3f, 0x0a, 0x06, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x12, 0x18, 0x2e, - 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, - 0x67, 0x69, 0x6e, 0x2e, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x00, 0x12, 0x51, 0x0a, 0x0d, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x48, 0x6f, - 0x73, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x1f, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, - 0x6e, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x48, 0x6f, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, - 0x69, 0x6e, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x48, 0x6f, 0x73, 0x74, 0x4b, 0x65, 0x79, - 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x42, 0x25, 0x5a, 0x23, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x75, 0x74, 0x68, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x28, 0x01, 0x30, 0x01, 0x12, 0x78, + 0x0a, 0x19, 0x55, 0x70, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x41, 0x75, 0x74, 0x68, 0x46, 0x61, + 0x69, 0x6c, 0x75, 0x72, 0x65, 0x4e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x12, 0x2b, 0x2e, 0x6c, 0x69, + 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x55, 0x70, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, + 0x41, 0x75, 0x74, 0x68, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x4e, 0x6f, 0x74, 0x69, 0x63, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x55, 0x70, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x41, 0x75, 0x74, + 0x68, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x4e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3f, 0x0a, 0x06, 0x42, 0x61, 0x6e, 0x6e, + 0x65, 0x72, 0x12, 0x18, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x42, + 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, + 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x54, 0x0a, 0x0d, 0x56, 0x65, 0x72, + 0x69, 0x66, 0x79, 0x48, 0x6f, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x1f, 0x2e, 0x6c, 0x69, 0x62, + 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x48, 0x6f, 0x73, + 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6c, 0x69, + 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x48, 0x6f, + 0x73, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, + 0x5a, 0x0a, 0x0f, 0x50, 0x69, 0x70, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x4e, 0x6f, 0x74, 0x69, + 0x63, 0x65, 0x12, 0x21, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x50, + 0x69, 0x70, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x4e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x2e, 0x50, 0x69, 0x70, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x4e, 0x6f, 0x74, 0x69, 0x63, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5a, 0x0a, 0x0f, 0x50, + 0x69, 0x70, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x12, 0x21, + 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x50, 0x69, 0x70, 0x65, 0x45, + 0x72, 0x72, 0x6f, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x22, 0x2e, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x50, 0x69, + 0x70, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x25, 0x5a, 0x23, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x67, 0x31, 0x32, 0x33, 0x2f, 0x73, 0x73, 0x68, 0x70, 0x69, 0x70, 0x65, 0x72, 0x2f, 0x6c, 0x69, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, @@ -2218,7 +2421,7 @@ func file_plugin_proto_rawDescGZIP() []byte { } var file_plugin_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_plugin_proto_msgTypes = make([]protoimpl.MessageInfo, 35) +var file_plugin_proto_msgTypes = make([]protoimpl.MessageInfo, 39) var file_plugin_proto_goTypes = []interface{}{ (AuthMethod)(0), // 0: libplugin.AuthMethod (*ConnMeta)(nil), // 1: libplugin.ConnMeta @@ -2253,9 +2456,13 @@ var file_plugin_proto_goTypes = []interface{}{ (*BannerRequest)(nil), // 30: libplugin.BannerRequest (*BannerResponse)(nil), // 31: libplugin.BannerResponse (*VerifyHostKeyRequest)(nil), // 32: libplugin.VerifyHostKeyRequest - (*VerifyHostKeyReply)(nil), // 33: libplugin.VerifyHostKeyReply - nil, // 34: libplugin.UpstreamNextPluginAuth.MetaEntry - (*KeyboardInteractivePromptRequest_Question)(nil), // 35: libplugin.KeyboardInteractivePromptRequest.Question + (*VerifyHostKeyResponse)(nil), // 33: libplugin.VerifyHostKeyResponse + (*PipeStartNoticeRequest)(nil), // 34: libplugin.PipeStartNoticeRequest + (*PipeStartNoticeResponse)(nil), // 35: libplugin.PipeStartNoticeResponse + (*PipeErrorNoticeRequest)(nil), // 36: libplugin.PipeErrorNoticeRequest + (*PipeErrorNoticeResponse)(nil), // 37: libplugin.PipeErrorNoticeResponse + nil, // 38: libplugin.UpstreamNextPluginAuth.MetaEntry + (*KeyboardInteractivePromptRequest_Question)(nil), // 39: libplugin.KeyboardInteractivePromptRequest.Question } var file_plugin_proto_depIdxs = []int32{ 3, // 0: libplugin.Upstream.none:type_name -> libplugin.UpstreamNoneAuth @@ -2263,7 +2470,7 @@ var file_plugin_proto_depIdxs = []int32{ 5, // 2: libplugin.Upstream.private_key:type_name -> libplugin.UpstreamPrivateKeyAuth 6, // 3: libplugin.Upstream.remote_signer:type_name -> libplugin.UpstreamRemoteSignerAuth 7, // 4: libplugin.Upstream.next_plugin:type_name -> libplugin.UpstreamNextPluginAuth - 34, // 5: libplugin.UpstreamNextPluginAuth.meta:type_name -> libplugin.UpstreamNextPluginAuth.MetaEntry + 38, // 5: libplugin.UpstreamNextPluginAuth.meta:type_name -> libplugin.UpstreamNextPluginAuth.MetaEntry 1, // 6: libplugin.NewConnectionRequest.meta:type_name -> libplugin.ConnMeta 1, // 7: libplugin.NextAuthMethodsRequest.meta:type_name -> libplugin.ConnMeta 0, // 8: libplugin.NextAuthMethodsResponse.methods:type_name -> libplugin.AuthMethod @@ -2273,7 +2480,7 @@ var file_plugin_proto_depIdxs = []int32{ 2, // 12: libplugin.PasswordAuthResponse.upstream:type_name -> libplugin.Upstream 1, // 13: libplugin.PublicKeyAuthRequest.meta:type_name -> libplugin.ConnMeta 2, // 14: libplugin.PublicKeyAuthResponse.upstream:type_name -> libplugin.Upstream - 35, // 15: libplugin.KeyboardInteractivePromptRequest.questions:type_name -> libplugin.KeyboardInteractivePromptRequest.Question + 39, // 15: libplugin.KeyboardInteractivePromptRequest.questions:type_name -> libplugin.KeyboardInteractivePromptRequest.Question 1, // 16: libplugin.KeyboardInteractiveMetaResponse.meta:type_name -> libplugin.ConnMeta 2, // 17: libplugin.KeyboardInteractiveFinishRequest.upstream:type_name -> libplugin.Upstream 23, // 18: libplugin.KeyboardInteractiveAuthMessage.prompt_request:type_name -> libplugin.KeyboardInteractivePromptRequest @@ -2285,33 +2492,39 @@ var file_plugin_proto_depIdxs = []int32{ 0, // 24: libplugin.UpstreamAuthFailureNoticeRequest.allowed_methods:type_name -> libplugin.AuthMethod 1, // 25: libplugin.BannerRequest.meta:type_name -> libplugin.ConnMeta 1, // 26: libplugin.VerifyHostKeyRequest.meta:type_name -> libplugin.ConnMeta - 8, // 27: libplugin.SshPiperPlugin.Logs:input_type -> libplugin.StartLogRequest - 10, // 28: libplugin.SshPiperPlugin.ListCallbacks:input_type -> libplugin.ListCallbackRequest - 12, // 29: libplugin.SshPiperPlugin.NewConnection:input_type -> libplugin.NewConnectionRequest - 14, // 30: libplugin.SshPiperPlugin.NextAuthMethods:input_type -> libplugin.NextAuthMethodsRequest - 16, // 31: libplugin.SshPiperPlugin.NoneAuth:input_type -> libplugin.NoneAuthRequest - 18, // 32: libplugin.SshPiperPlugin.PasswordAuth:input_type -> libplugin.PasswordAuthRequest - 20, // 33: libplugin.SshPiperPlugin.PublicKeyAuth:input_type -> libplugin.PublicKeyAuthRequest - 27, // 34: libplugin.SshPiperPlugin.KeyboardInteractiveAuth:input_type -> libplugin.KeyboardInteractiveAuthMessage - 28, // 35: libplugin.SshPiperPlugin.UpstreamAuthFailureNotice:input_type -> libplugin.UpstreamAuthFailureNoticeRequest - 30, // 36: libplugin.SshPiperPlugin.Banner:input_type -> libplugin.BannerRequest - 32, // 37: libplugin.SshPiperPlugin.VerifyHostKey:input_type -> libplugin.VerifyHostKeyRequest - 9, // 38: libplugin.SshPiperPlugin.Logs:output_type -> libplugin.Log - 11, // 39: libplugin.SshPiperPlugin.ListCallbacks:output_type -> libplugin.ListCallbackResponse - 13, // 40: libplugin.SshPiperPlugin.NewConnection:output_type -> libplugin.NewConnectionResponse - 15, // 41: libplugin.SshPiperPlugin.NextAuthMethods:output_type -> libplugin.NextAuthMethodsResponse - 17, // 42: libplugin.SshPiperPlugin.NoneAuth:output_type -> libplugin.NoneAuthResponse - 19, // 43: libplugin.SshPiperPlugin.PasswordAuth:output_type -> libplugin.PasswordAuthResponse - 21, // 44: libplugin.SshPiperPlugin.PublicKeyAuth:output_type -> libplugin.PublicKeyAuthResponse - 27, // 45: libplugin.SshPiperPlugin.KeyboardInteractiveAuth:output_type -> libplugin.KeyboardInteractiveAuthMessage - 29, // 46: libplugin.SshPiperPlugin.UpstreamAuthFailureNotice:output_type -> libplugin.UpstreamAuthFailureNoticeResponse - 31, // 47: libplugin.SshPiperPlugin.Banner:output_type -> libplugin.BannerResponse - 33, // 48: libplugin.SshPiperPlugin.VerifyHostKey:output_type -> libplugin.VerifyHostKeyReply - 38, // [38:49] is the sub-list for method output_type - 27, // [27:38] is the sub-list for method input_type - 27, // [27:27] is the sub-list for extension type_name - 27, // [27:27] is the sub-list for extension extendee - 0, // [0:27] is the sub-list for field type_name + 1, // 27: libplugin.PipeStartNoticeRequest.meta:type_name -> libplugin.ConnMeta + 1, // 28: libplugin.PipeErrorNoticeRequest.meta:type_name -> libplugin.ConnMeta + 8, // 29: libplugin.SshPiperPlugin.Logs:input_type -> libplugin.StartLogRequest + 10, // 30: libplugin.SshPiperPlugin.ListCallbacks:input_type -> libplugin.ListCallbackRequest + 12, // 31: libplugin.SshPiperPlugin.NewConnection:input_type -> libplugin.NewConnectionRequest + 14, // 32: libplugin.SshPiperPlugin.NextAuthMethods:input_type -> libplugin.NextAuthMethodsRequest + 16, // 33: libplugin.SshPiperPlugin.NoneAuth:input_type -> libplugin.NoneAuthRequest + 18, // 34: libplugin.SshPiperPlugin.PasswordAuth:input_type -> libplugin.PasswordAuthRequest + 20, // 35: libplugin.SshPiperPlugin.PublicKeyAuth:input_type -> libplugin.PublicKeyAuthRequest + 27, // 36: libplugin.SshPiperPlugin.KeyboardInteractiveAuth:input_type -> libplugin.KeyboardInteractiveAuthMessage + 28, // 37: libplugin.SshPiperPlugin.UpstreamAuthFailureNotice:input_type -> libplugin.UpstreamAuthFailureNoticeRequest + 30, // 38: libplugin.SshPiperPlugin.Banner:input_type -> libplugin.BannerRequest + 32, // 39: libplugin.SshPiperPlugin.VerifyHostKey:input_type -> libplugin.VerifyHostKeyRequest + 34, // 40: libplugin.SshPiperPlugin.PipeStartNotice:input_type -> libplugin.PipeStartNoticeRequest + 36, // 41: libplugin.SshPiperPlugin.PipeErrorNotice:input_type -> libplugin.PipeErrorNoticeRequest + 9, // 42: libplugin.SshPiperPlugin.Logs:output_type -> libplugin.Log + 11, // 43: libplugin.SshPiperPlugin.ListCallbacks:output_type -> libplugin.ListCallbackResponse + 13, // 44: libplugin.SshPiperPlugin.NewConnection:output_type -> libplugin.NewConnectionResponse + 15, // 45: libplugin.SshPiperPlugin.NextAuthMethods:output_type -> libplugin.NextAuthMethodsResponse + 17, // 46: libplugin.SshPiperPlugin.NoneAuth:output_type -> libplugin.NoneAuthResponse + 19, // 47: libplugin.SshPiperPlugin.PasswordAuth:output_type -> libplugin.PasswordAuthResponse + 21, // 48: libplugin.SshPiperPlugin.PublicKeyAuth:output_type -> libplugin.PublicKeyAuthResponse + 27, // 49: libplugin.SshPiperPlugin.KeyboardInteractiveAuth:output_type -> libplugin.KeyboardInteractiveAuthMessage + 29, // 50: libplugin.SshPiperPlugin.UpstreamAuthFailureNotice:output_type -> libplugin.UpstreamAuthFailureNoticeResponse + 31, // 51: libplugin.SshPiperPlugin.Banner:output_type -> libplugin.BannerResponse + 33, // 52: libplugin.SshPiperPlugin.VerifyHostKey:output_type -> libplugin.VerifyHostKeyResponse + 35, // 53: libplugin.SshPiperPlugin.PipeStartNotice:output_type -> libplugin.PipeStartNoticeResponse + 37, // 54: libplugin.SshPiperPlugin.PipeErrorNotice:output_type -> libplugin.PipeErrorNoticeResponse + 42, // [42:55] is the sub-list for method output_type + 29, // [29:42] is the sub-list for method input_type + 29, // [29:29] is the sub-list for extension type_name + 29, // [29:29] is the sub-list for extension extendee + 0, // [0:29] is the sub-list for field type_name } func init() { file_plugin_proto_init() } @@ -2705,7 +2918,19 @@ func file_plugin_proto_init() { } } file_plugin_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VerifyHostKeyReply); i { + switch v := v.(*VerifyHostKeyResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_plugin_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PipeStartNoticeRequest); i { case 0: return &v.state case 1: @@ -2717,6 +2942,42 @@ func file_plugin_proto_init() { } } file_plugin_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PipeStartNoticeResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_plugin_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PipeErrorNoticeRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_plugin_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PipeErrorNoticeResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_plugin_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*KeyboardInteractivePromptRequest_Question); i { case 0: return &v.state @@ -2749,7 +3010,7 @@ func file_plugin_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_plugin_proto_rawDesc, NumEnums: 1, - NumMessages: 35, + NumMessages: 39, NumExtensions: 0, NumServices: 1, }, diff --git a/libplugin/plugin.proto b/libplugin/plugin.proto index 5e567598..b0334509 100644 --- a/libplugin/plugin.proto +++ b/libplugin/plugin.proto @@ -57,7 +57,9 @@ service SshPiperPlugin { rpc KeyboardInteractiveAuth(stream KeyboardInteractiveAuthMessage) returns (stream KeyboardInteractiveAuthMessage); rpc UpstreamAuthFailureNotice(UpstreamAuthFailureNoticeRequest) returns (UpstreamAuthFailureNoticeResponse) {}; rpc Banner(BannerRequest) returns (BannerResponse) {}; - rpc VerifyHostKey (VerifyHostKeyRequest) returns (VerifyHostKeyReply) {} + rpc VerifyHostKey (VerifyHostKeyRequest) returns (VerifyHostKeyResponse) {} + rpc PipeStartNotice(PipeStartNoticeRequest) returns (PipeStartNoticeResponse) {} + rpc PipeErrorNotice(PipeErrorNoticeRequest) returns (PipeErrorNoticeResponse) {} } message StartLogRequest { @@ -186,6 +188,21 @@ message VerifyHostKeyRequest { string netaddress = 4; } -message VerifyHostKeyReply { +message VerifyHostKeyResponse { bool verified = 1; +} + +message PipeStartNoticeRequest { + ConnMeta meta = 1; +} + +message PipeStartNoticeResponse { +} + +message PipeErrorNoticeRequest { + ConnMeta meta = 1; + string error = 2; +} + +message PipeErrorNoticeResponse { } \ No newline at end of file diff --git a/libplugin/plugin_grpc.pb.go b/libplugin/plugin_grpc.pb.go index 34bcf9c5..943286cd 100644 --- a/libplugin/plugin_grpc.pb.go +++ b/libplugin/plugin_grpc.pb.go @@ -32,7 +32,9 @@ type SshPiperPluginClient interface { KeyboardInteractiveAuth(ctx context.Context, opts ...grpc.CallOption) (SshPiperPlugin_KeyboardInteractiveAuthClient, error) UpstreamAuthFailureNotice(ctx context.Context, in *UpstreamAuthFailureNoticeRequest, opts ...grpc.CallOption) (*UpstreamAuthFailureNoticeResponse, error) Banner(ctx context.Context, in *BannerRequest, opts ...grpc.CallOption) (*BannerResponse, error) - VerifyHostKey(ctx context.Context, in *VerifyHostKeyRequest, opts ...grpc.CallOption) (*VerifyHostKeyReply, error) + VerifyHostKey(ctx context.Context, in *VerifyHostKeyRequest, opts ...grpc.CallOption) (*VerifyHostKeyResponse, error) + PipeStartNotice(ctx context.Context, in *PipeStartNoticeRequest, opts ...grpc.CallOption) (*PipeStartNoticeResponse, error) + PipeErrorNotice(ctx context.Context, in *PipeErrorNoticeRequest, opts ...grpc.CallOption) (*PipeErrorNoticeResponse, error) } type sshPiperPluginClient struct { @@ -178,8 +180,8 @@ func (c *sshPiperPluginClient) Banner(ctx context.Context, in *BannerRequest, op return out, nil } -func (c *sshPiperPluginClient) VerifyHostKey(ctx context.Context, in *VerifyHostKeyRequest, opts ...grpc.CallOption) (*VerifyHostKeyReply, error) { - out := new(VerifyHostKeyReply) +func (c *sshPiperPluginClient) VerifyHostKey(ctx context.Context, in *VerifyHostKeyRequest, opts ...grpc.CallOption) (*VerifyHostKeyResponse, error) { + out := new(VerifyHostKeyResponse) err := c.cc.Invoke(ctx, "/libplugin.SshPiperPlugin/VerifyHostKey", in, out, opts...) if err != nil { return nil, err @@ -187,6 +189,24 @@ func (c *sshPiperPluginClient) VerifyHostKey(ctx context.Context, in *VerifyHost return out, nil } +func (c *sshPiperPluginClient) PipeStartNotice(ctx context.Context, in *PipeStartNoticeRequest, opts ...grpc.CallOption) (*PipeStartNoticeResponse, error) { + out := new(PipeStartNoticeResponse) + err := c.cc.Invoke(ctx, "/libplugin.SshPiperPlugin/PipeStartNotice", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *sshPiperPluginClient) PipeErrorNotice(ctx context.Context, in *PipeErrorNoticeRequest, opts ...grpc.CallOption) (*PipeErrorNoticeResponse, error) { + out := new(PipeErrorNoticeResponse) + err := c.cc.Invoke(ctx, "/libplugin.SshPiperPlugin/PipeErrorNotice", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // SshPiperPluginServer is the server API for SshPiperPlugin service. // All implementations must embed UnimplementedSshPiperPluginServer // for forward compatibility @@ -201,7 +221,9 @@ type SshPiperPluginServer interface { KeyboardInteractiveAuth(SshPiperPlugin_KeyboardInteractiveAuthServer) error UpstreamAuthFailureNotice(context.Context, *UpstreamAuthFailureNoticeRequest) (*UpstreamAuthFailureNoticeResponse, error) Banner(context.Context, *BannerRequest) (*BannerResponse, error) - VerifyHostKey(context.Context, *VerifyHostKeyRequest) (*VerifyHostKeyReply, error) + VerifyHostKey(context.Context, *VerifyHostKeyRequest) (*VerifyHostKeyResponse, error) + PipeStartNotice(context.Context, *PipeStartNoticeRequest) (*PipeStartNoticeResponse, error) + PipeErrorNotice(context.Context, *PipeErrorNoticeRequest) (*PipeErrorNoticeResponse, error) mustEmbedUnimplementedSshPiperPluginServer() } @@ -239,9 +261,15 @@ func (UnimplementedSshPiperPluginServer) UpstreamAuthFailureNotice(context.Conte func (UnimplementedSshPiperPluginServer) Banner(context.Context, *BannerRequest) (*BannerResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method Banner not implemented") } -func (UnimplementedSshPiperPluginServer) VerifyHostKey(context.Context, *VerifyHostKeyRequest) (*VerifyHostKeyReply, error) { +func (UnimplementedSshPiperPluginServer) VerifyHostKey(context.Context, *VerifyHostKeyRequest) (*VerifyHostKeyResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method VerifyHostKey not implemented") } +func (UnimplementedSshPiperPluginServer) PipeStartNotice(context.Context, *PipeStartNoticeRequest) (*PipeStartNoticeResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method PipeStartNotice not implemented") +} +func (UnimplementedSshPiperPluginServer) PipeErrorNotice(context.Context, *PipeErrorNoticeRequest) (*PipeErrorNoticeResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method PipeErrorNotice not implemented") +} func (UnimplementedSshPiperPluginServer) mustEmbedUnimplementedSshPiperPluginServer() {} // UnsafeSshPiperPluginServer may be embedded to opt out of forward compatibility for this service. @@ -464,6 +492,42 @@ func _SshPiperPlugin_VerifyHostKey_Handler(srv interface{}, ctx context.Context, return interceptor(ctx, in, info, handler) } +func _SshPiperPlugin_PipeStartNotice_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(PipeStartNoticeRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SshPiperPluginServer).PipeStartNotice(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/libplugin.SshPiperPlugin/PipeStartNotice", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SshPiperPluginServer).PipeStartNotice(ctx, req.(*PipeStartNoticeRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _SshPiperPlugin_PipeErrorNotice_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(PipeErrorNoticeRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SshPiperPluginServer).PipeErrorNotice(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/libplugin.SshPiperPlugin/PipeErrorNotice", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SshPiperPluginServer).PipeErrorNotice(ctx, req.(*PipeErrorNoticeRequest)) + } + return interceptor(ctx, in, info, handler) +} + // SshPiperPlugin_ServiceDesc is the grpc.ServiceDesc for SshPiperPlugin service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -507,6 +571,14 @@ var SshPiperPlugin_ServiceDesc = grpc.ServiceDesc{ MethodName: "VerifyHostKey", Handler: _SshPiperPlugin_VerifyHostKey_Handler, }, + { + MethodName: "PipeStartNotice", + Handler: _SshPiperPlugin_PipeStartNotice_Handler, + }, + { + MethodName: "PipeErrorNotice", + Handler: _SshPiperPlugin_PipeErrorNotice_Handler, + }, }, Streams: []grpc.StreamDesc{ { diff --git a/libplugin/pluginbase.go b/libplugin/pluginbase.go index 04adc357..9d06e2a4 100644 --- a/libplugin/pluginbase.go +++ b/libplugin/pluginbase.go @@ -54,6 +54,10 @@ type SshPiperPluginConfig struct { BannerCallback func(conn ConnMetadata) string VerifyHostKeyCallback func(conn ConnMetadata, hostname, netaddr string, key []byte) error + + PipeStartCallback func(conn ConnMetadata) + + PipeErrorCallback func(conn ConnMetadata, err error) } type SshPiperPlugin interface { @@ -180,6 +184,14 @@ func (s *server) ListCallbacks(ctx context.Context, req *ListCallbackRequest) (* cb = append(cb, "VerifyHostKey") } + if s.config.PipeStartCallback != nil { + cb = append(cb, "PipeStart") + } + + if s.config.PipeErrorCallback != nil { + cb = append(cb, "PipeError") + } + return &ListCallbackResponse{ Callbacks: cb, }, nil @@ -377,7 +389,7 @@ func (s *server) Banner(ctx context.Context, req *BannerRequest) (*BannerRespons }, nil } -func (s *server) VerifyHostKey(ctx context.Context, req *VerifyHostKeyRequest) (*VerifyHostKeyReply, error) { +func (s *server) VerifyHostKey(ctx context.Context, req *VerifyHostKeyRequest) (*VerifyHostKeyResponse, error) { if s.config.VerifyHostKeyCallback == nil { return nil, status.Errorf(codes.Unimplemented, "method VerifyHostKey not implemented") } @@ -387,7 +399,27 @@ func (s *server) VerifyHostKey(ctx context.Context, req *VerifyHostKeyRequest) ( return nil, err } - return &VerifyHostKeyReply{ + return &VerifyHostKeyResponse{ Verified: true, }, nil } + +func (s *server) PipeStartNotice(ctx context.Context, req *PipeStartNoticeRequest) (*PipeStartNoticeResponse, error) { + if s.config.PipeStartCallback == nil { + return nil, status.Errorf(codes.Unimplemented, "method PipeStartNotice not implemented") + } + + s.config.PipeStartCallback(req.Meta) + + return &PipeStartNoticeResponse{}, nil +} + +func (s *server) PipeErrorNotice(ctx context.Context, req *PipeErrorNoticeRequest) (*PipeErrorNoticeResponse, error) { + if s.config.PipeErrorCallback == nil { + return nil, status.Errorf(codes.Unimplemented, "method PipeErrorNotice not implemented") + } + + s.config.PipeErrorCallback(req.Meta, fmt.Errorf(req.Error)) + + return &PipeErrorNoticeResponse{}, nil +} diff --git a/plugin/testplugin/main.go b/plugin/testplugin/main.go new file mode 100644 index 00000000..cd041967 --- /dev/null +++ b/plugin/testplugin/main.go @@ -0,0 +1,66 @@ +//go:build e2e + +package main + +import ( + "net/rpc" + + "github.com/tg123/sshpiper/libplugin" + "github.com/urfave/cli/v2" +) + +func main() { + + libplugin.CreateAndRunPluginTemplate(&libplugin.PluginTemplate{ + Name: "testplugin", + Usage: "e2e test plugin only", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "rpcserver", + Required: true, + }, + &cli.StringFlag{ + Name: "testsshserver", + Required: true, + }, + }, + CreateConfig: func(c *cli.Context) (*libplugin.SshPiperPluginConfig, error) { + + rpcclient, err := rpc.DialHTTP("tcp", c.String("rpcserver")) + if err != nil { + return nil, err + } + + host, port, err := libplugin.SplitHostPortForSSH(c.String("testsshserver")) + if err != nil { + return nil, err + } + + return &libplugin.SshPiperPluginConfig{ + NewConnectionCallback: func(conn libplugin.ConnMetadata) error { + return rpcclient.Call("TestPlugin.NewConnection", "", nil) + }, + PipeStartCallback: func(conn libplugin.ConnMetadata) { + rpcclient.Call("TestPlugin.PipeStart", "", nil) + }, + PipeErrorCallback: func(conn libplugin.ConnMetadata, err error) { + rpcclient.Call("TestPlugin.PipeError", err.Error(), nil) + }, + PasswordCallback: func(conn libplugin.ConnMetadata, password []byte) (*libplugin.Upstream, error) { + var newpass string + err := rpcclient.Call("TestPlugin.Password", string(password), &newpass) + if err != nil { + return nil, err + } + + return &libplugin.Upstream{ + Host: host, + Port: int32(port), + Auth: libplugin.CreatePasswordAuthFromString(newpass), + IgnoreHostKey: true, + }, nil + }, + }, nil + }, + }) +}