From 2bda290899691b69d0bb98b3862d13b314599488 Mon Sep 17 00:00:00 2001 From: evilsocket Date: Thu, 5 Apr 2018 12:01:33 +0200 Subject: [PATCH] python ui server --- Makefile | 8 +- daemon/rule/rule.go | 2 +- ui.gtk/.gitignore | 1 - ui.gtk/Makefile | 7 - ui.gtk/glade/ask_window.glade | 178 -------------------- ui.gtk/main.go | 138 ---------------- ui.gtk/version.go | 8 - ui.proto/.gitiignore | 1 + ui.proto/Makefile | 4 +- ui.proto/ui.pb.go | 52 +++--- ui.proto/ui.proto | 2 +- ui.proto/ui_pb2.py | 303 ++++++++++++++++++++++++++++++++++ ui.proto/ui_pb2_grpc.py | 63 +++++++ ui/.gitignore | 1 + ui/dialog.py | 40 +++++ ui/main.py | 53 ++++++ ui/res/dialog.ui | 184 +++++++++++++++++++++ 17 files changed, 678 insertions(+), 367 deletions(-) delete mode 100644 ui.gtk/.gitignore delete mode 100644 ui.gtk/Makefile delete mode 100644 ui.gtk/glade/ask_window.glade delete mode 100644 ui.gtk/main.go delete mode 100644 ui.gtk/version.go create mode 100644 ui.proto/.gitiignore create mode 100644 ui.proto/ui_pb2.py create mode 100644 ui.proto/ui_pb2_grpc.py create mode 100644 ui/.gitignore create mode 100644 ui/dialog.py create mode 100644 ui/main.py create mode 100644 ui/res/dialog.ui diff --git a/Makefile b/Makefile index a4924d9e..64060ae3 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -all: protocol osd osgui +all: protocol osd protocol: @cd ui.proto && make @@ -6,12 +6,8 @@ protocol: osd: @cd daemon && make && mv daemon ../osd -osgui: - @cd ui.gtk && make && mv ui.gtk ../osgui - clean: @cd rules && rm -rf user.rule*.json @cd daemon && make clean @cd ui.proto && make clean - @cd ui.gtk && make clean - @rm -rf osd osgui + @rm -rf osd diff --git a/daemon/rule/rule.go b/daemon/rule/rule.go index 8e3acac3..167749bf 100644 --- a/daemon/rule/rule.go +++ b/daemon/rule/rule.go @@ -62,7 +62,7 @@ func FromReply(reply *protocol.RuleReply) *Rule { Duration(reply.Duration), Cmp{ What: OperandType(reply.What), - With: reply.With, + With: reply.Value, }, ) } diff --git a/ui.gtk/.gitignore b/ui.gtk/.gitignore deleted file mode 100644 index 62e7cd66..00000000 --- a/ui.gtk/.gitignore +++ /dev/null @@ -1 +0,0 @@ -ui.gtk diff --git a/ui.gtk/Makefile b/ui.gtk/Makefile deleted file mode 100644 index 5f03c5ca..00000000 --- a/ui.gtk/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -all: ui.gtk - -ui.gtk: - @go build . - -clean: - @rm -rf ui.gtk diff --git a/ui.gtk/glade/ask_window.glade b/ui.gtk/glade/ask_window.glade deleted file mode 100644 index 26e927b8..00000000 --- a/ui.gtk/glade/ask_window.glade +++ /dev/null @@ -1,178 +0,0 @@ - - - - - - - - - - - - Forever -Until Restart -Once - - - - - False - False - center-always - True - False - - - True - False - vertical - - - True - False - 20 - 20 - - - True - False - True - True - gtk-dialog-question - - - 0 - 0 - - - - - True - False - True - - - True - False - start - True - Application Name - - - - - - - - 0 - 0 - - - - - True - False - start - center - True - True - Wants to connect to www.google.com on tcp port 443 - - - 0 - 1 - - - - - 1 - 0 - - - - - False - True - 0 - - - - - True - False - 10 - 10 - - - True - False - 15 - More Details > - - - 0 - 0 - - - - - False - True - 1 - 1 - - - - - True - False - center - True - - - Allow - True - True - True - - - 0 - 0 - - - - - Deny - True - True - True - - - 1 - 0 - - - - - True - False - durationStore - 0 - 0 - - - 2 - 0 - - - - - False - True - 2 - - - - - - diff --git a/ui.gtk/main.go b/ui.gtk/main.go deleted file mode 100644 index a217bd61..00000000 --- a/ui.gtk/main.go +++ /dev/null @@ -1,138 +0,0 @@ -package main - -import ( - "flag" - "net" - "os" - "os/signal" - "syscall" - - "golang.org/x/net/context" - - "github.com/evilsocket/opensnitch/daemon/core" - "github.com/evilsocket/opensnitch/daemon/log" - protocol "github.com/evilsocket/opensnitch/ui.proto" - - "google.golang.org/grpc" - "google.golang.org/grpc/reflection" - - "github.com/gotk3/gotk3/glib" - "github.com/gotk3/gotk3/gtk" -) - -var ( - socketPath = "opensnitch-ui.sock" - uiBuilder = (*gtk.Builder)(nil) - askWindow = (*gtk.Window)(nil) - listener = (net.Listener)(nil) - server = (*grpc.Server)(nil) - err = (error)(nil) - sigChan = (chan os.Signal)(nil) - isClosing = (bool)(false) -) - -type service struct{} - -func (s *service) Ping(ctx context.Context, ping *protocol.PingRequest) (*protocol.PingReply, error) { - log.Debug("Got ping 0x%x", ping.Id) - return &protocol.PingReply{Id: ping.Id}, nil -} - -func (s *service) AskRule(ctx context.Context, req *protocol.RuleRequest) (*protocol.RuleReply, error) { - log.Info("Got rule request: %v", req) - - glib.IdleAdd(func() bool { - askWindow.Show() - return false - }) - - return &protocol.RuleReply{ - Name: "user.choice", - Action: "allow", - Duration: "always", - What: "process.path", - With: req.ProcessPath, - }, nil -} - -func setupSignals() { - sigChan = make(chan os.Signal, 1) - signal.Notify(sigChan, - syscall.SIGHUP, - syscall.SIGINT, - syscall.SIGTERM, - syscall.SIGQUIT) - go func() { - sig := <-sigChan - isClosing = true - log.Raw("\n") - log.Important("Got signal: %v", sig) - - if listener != nil { - listener.Close() - } - os.Exit(0) - }() -} - -// TODO: this will be loaded from compiled resources -const uiGladeFile = "ui.gtk/glade/ask_window.glade" - -func setupGtk() { - gtk.Init(&os.Args) - - if uiBuilder, err = gtk.BuilderNew(); err != nil { - log.Fatal("Error while creating GTK builder: %s", err) - } else if err = uiBuilder.AddFromFile(uiGladeFile); err != nil { - log.Fatal("Error while loading %s: %s", uiGladeFile, err) - } - - obj, err := uiBuilder.GetObject("askWindow") - if err != nil { - log.Fatal("Error while getting window: %s", err) - } - - var ok bool - askWindow, ok = obj.(*gtk.Window) - if !ok { - log.Fatal("Could not cast window object.") - } - - askWindow.SetTitle("OpenSnitch v" + Version) - go func() { - gtk.Main() - }() -} - -func init() { - flag.StringVar(&socketPath, "socket-path", socketPath, "UNIX socket for this gRPC service.") -} - -func main() { - flag.Parse() - - socketPath, err = core.ExpandPath(socketPath) - if err != nil { - log.Fatal("%s", err) - } - - setupSignals() - setupGtk() - - log.Important("Starting %s v%s on socket %s", Name, Version, socketPath) - - listener, err = net.Listen("unix", socketPath) - if err != nil { - log.Fatal("%s", err) - } - - server = grpc.NewServer() - protocol.RegisterUIServer(server, &service{}) - reflection.Register(server) - - if err := server.Serve(listener); err != nil { - if isClosing == false { - log.Fatal("Failed to start: %s", err) - } - } -} diff --git a/ui.gtk/version.go b/ui.gtk/version.go deleted file mode 100644 index f68b450b..00000000 --- a/ui.gtk/version.go +++ /dev/null @@ -1,8 +0,0 @@ -package main - -const ( - Name = "opensnitch-test-ui" - Version = "1.0.0b" - Author = "Simone 'evilsocket' Margaritelli" - Website = "https://github.com/evilsocket/opensnitch" -) diff --git a/ui.proto/.gitiignore b/ui.proto/.gitiignore new file mode 100644 index 00000000..0d20b648 --- /dev/null +++ b/ui.proto/.gitiignore @@ -0,0 +1 @@ +*.pyc diff --git a/ui.proto/Makefile b/ui.proto/Makefile index edb28302..0b802e4a 100644 --- a/ui.proto/Makefile +++ b/ui.proto/Makefile @@ -1,5 +1,7 @@ all: protoc -I. ui.proto --go_out=plugins=grpc:. + python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. ui.proto clean: - @rm -rf ui.pb.go + @rm -rf *.go + @rm -rf *.py diff --git a/ui.proto/ui.pb.go b/ui.proto/ui.pb.go index a82fdcea..21575f9d 100644 --- a/ui.proto/ui.pb.go +++ b/ui.proto/ui.pb.go @@ -152,7 +152,7 @@ type RuleReply struct { Action string `protobuf:"bytes,2,opt,name=action" json:"action,omitempty"` Duration string `protobuf:"bytes,3,opt,name=duration" json:"duration,omitempty"` What string `protobuf:"bytes,4,opt,name=what" json:"what,omitempty"` - With string `protobuf:"bytes,5,opt,name=with" json:"with,omitempty"` + Value string `protobuf:"bytes,5,opt,name=value" json:"value,omitempty"` } func (m *RuleReply) Reset() { *m = RuleReply{} } @@ -188,9 +188,9 @@ func (m *RuleReply) GetWhat() string { return "" } -func (m *RuleReply) GetWith() string { +func (m *RuleReply) GetValue() string { if m != nil { - return m.With + return m.Value } return "" } @@ -310,27 +310,27 @@ var _UI_serviceDesc = grpc.ServiceDesc{ func init() { proto.RegisterFile("ui.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ - // 341 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x51, 0xcd, 0x4a, 0xeb, 0x40, - 0x14, 0x6e, 0xd3, 0x34, 0x3f, 0xa7, 0xb7, 0xf7, 0xc2, 0xc0, 0x95, 0x58, 0x29, 0xd4, 0xac, 0x0a, - 0x42, 0x17, 0xfa, 0x04, 0xdd, 0xd9, 0x5d, 0x09, 0xb8, 0x72, 0x51, 0x62, 0x26, 0x34, 0x83, 0xb1, - 0x33, 0xce, 0x39, 0x41, 0x8a, 0xcf, 0xe0, 0x3b, 0xcb, 0xfc, 0xa4, 0x06, 0xdc, 0x9d, 0xf3, 0xfd, - 0x31, 0xf3, 0x1d, 0x48, 0x3a, 0xb1, 0x51, 0x5a, 0x92, 0x64, 0x41, 0x27, 0xf2, 0x25, 0xcc, 0xf6, - 0xe2, 0x74, 0x2c, 0xea, 0xf7, 0xae, 0x46, 0x62, 0x7f, 0x21, 0x10, 0x3c, 0x1b, 0xaf, 0xc6, 0xeb, - 0xb0, 0x08, 0x04, 0xcf, 0x6f, 0x20, 0x75, 0xb4, 0x6a, 0xcf, 0xbf, 0xc8, 0xaf, 0x00, 0x66, 0x45, - 0xd7, 0xd6, 0xbd, 0x79, 0x01, 0x89, 0x0d, 0xae, 0x64, 0x6b, 0x55, 0x69, 0x71, 0xd9, 0xd9, 0x7f, - 0x88, 0x50, 0x57, 0x07, 0xa1, 0xb2, 0xc0, 0x32, 0x53, 0xd4, 0xd5, 0x4e, 0xb1, 0x6b, 0x48, 0x0c, - 0xac, 0xa4, 0xa6, 0x6c, 0xb2, 0x1a, 0xaf, 0xe7, 0x45, 0x8c, 0xba, 0xda, 0x4b, 0x4d, 0xc6, 0xc1, - 0x91, 0x8c, 0x23, 0x74, 0x0e, 0x8e, 0xe4, 0x1c, 0x06, 0x6e, 0x24, 0x52, 0x36, 0xb5, 0x44, 0xcc, - 0x91, 0x1e, 0x25, 0x52, 0x4f, 0xd9, 0xb0, 0xc8, 0x85, 0x71, 0x24, 0x1b, 0xb6, 0x04, 0x50, 0x5a, - 0x56, 0x35, 0xe2, 0x41, 0xf0, 0x2c, 0xb6, 0x64, 0xea, 0x91, 0x1d, 0x67, 0xb7, 0xf0, 0xa7, 0xa7, - 0x55, 0x49, 0x4d, 0x96, 0xd8, 0xe0, 0x99, 0xc7, 0xf6, 0x25, 0x35, 0x43, 0x49, 0xa9, 0x8f, 0x98, - 0xa5, 0xab, 0xc9, 0x40, 0xb2, 0xd5, 0x47, 0xcc, 0x3f, 0x21, 0x75, 0x75, 0x98, 0xb2, 0x18, 0x84, - 0xa7, 0xf2, 0xad, 0xf6, 0x45, 0xd8, 0x99, 0x5d, 0x41, 0x54, 0x56, 0x24, 0xe4, 0xc9, 0x97, 0xe0, - 0x37, 0x53, 0x1c, 0xef, 0x74, 0x69, 0x99, 0x89, 0x2b, 0xae, 0xdf, 0x4d, 0xce, 0x47, 0x53, 0x92, - 0x2f, 0xc1, 0xce, 0x16, 0x13, 0xd4, 0xf8, 0xff, 0xdb, 0xf9, 0xfe, 0x19, 0x82, 0xa7, 0x1d, 0x5b, - 0x43, 0x68, 0xee, 0xc5, 0xfe, 0x6d, 0x3a, 0xb1, 0x19, 0x1c, 0x76, 0x31, 0xff, 0x01, 0x54, 0x7b, - 0xce, 0x47, 0xec, 0x0e, 0xe2, 0x2d, 0xbe, 0x9a, 0xf7, 0x3a, 0xf1, 0xe0, 0x90, 0x4e, 0x7c, 0xf9, - 0x4a, 0x3e, 0x7a, 0x89, 0xec, 0x1d, 0x1f, 0xbe, 0x03, 0x00, 0x00, 0xff, 0xff, 0xd9, 0x36, 0xbf, - 0xe7, 0x3c, 0x02, 0x00, 0x00, + // 343 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x51, 0xcb, 0x4a, 0xc3, 0x40, + 0x14, 0x6d, 0xd3, 0x34, 0x8f, 0x5b, 0xab, 0x30, 0xa8, 0xc4, 0x4a, 0xa1, 0x66, 0x55, 0x10, 0xba, + 0xd0, 0x2f, 0xe8, 0xce, 0xee, 0x4a, 0xc0, 0x95, 0x8b, 0x32, 0x66, 0x42, 0x33, 0x18, 0x3b, 0xe3, + 0xdc, 0x89, 0xd2, 0x85, 0x9f, 0xe0, 0x3f, 0xcb, 0x3c, 0x52, 0x03, 0xee, 0xe6, 0x9e, 0x17, 0xc9, + 0x39, 0x90, 0xb4, 0x7c, 0x25, 0x95, 0xd0, 0x82, 0x04, 0x2d, 0xcf, 0xe7, 0x30, 0xd9, 0xf2, 0xc3, + 0xbe, 0xa8, 0x3e, 0xda, 0x0a, 0x35, 0x39, 0x87, 0x80, 0xb3, 0x6c, 0xb8, 0x18, 0x2e, 0xc3, 0x22, + 0xe0, 0x2c, 0xbf, 0x85, 0xd4, 0xd1, 0xb2, 0x39, 0xfe, 0x23, 0x7f, 0x02, 0x98, 0x14, 0x6d, 0x53, + 0x75, 0xe6, 0x19, 0x24, 0x36, 0xb8, 0x14, 0x8d, 0x55, 0xa5, 0xc5, 0xe9, 0x26, 0x57, 0x10, 0xa1, + 0x2a, 0x77, 0x5c, 0x66, 0x81, 0x65, 0xc6, 0xa8, 0xca, 0x8d, 0x24, 0x37, 0x90, 0x18, 0x58, 0x0a, + 0xa5, 0xb3, 0xd1, 0x62, 0xb8, 0x9c, 0x16, 0x31, 0xaa, 0x72, 0x2b, 0x94, 0x36, 0x0e, 0x86, 0xda, + 0x38, 0x42, 0xe7, 0x60, 0xa8, 0x9d, 0xc3, 0xc0, 0xb5, 0x40, 0x9d, 0x8d, 0x2d, 0x11, 0x33, 0xd4, + 0x4f, 0x02, 0x75, 0x47, 0xd9, 0xb0, 0xc8, 0x85, 0x31, 0xd4, 0x36, 0x6c, 0x0e, 0x20, 0x95, 0x28, + 0x2b, 0xc4, 0x1d, 0x67, 0x59, 0x6c, 0xc9, 0xd4, 0x23, 0x1b, 0x46, 0xee, 0xe0, 0xac, 0xa3, 0x25, + 0xd5, 0x75, 0x96, 0xd8, 0xe0, 0x89, 0xc7, 0xb6, 0x54, 0xd7, 0x7d, 0x09, 0x55, 0x7b, 0xcc, 0xd2, + 0xc5, 0xa8, 0x27, 0x59, 0xab, 0x3d, 0xe6, 0xdf, 0x90, 0xba, 0x3a, 0x4c, 0x59, 0x04, 0xc2, 0x03, + 0x7d, 0xaf, 0x7c, 0x11, 0xf6, 0x4d, 0xae, 0x21, 0xa2, 0xa5, 0xe6, 0xe2, 0xe0, 0x4b, 0xf0, 0x97, + 0x29, 0x8e, 0xb5, 0x8a, 0x5a, 0x66, 0xe4, 0x8a, 0xeb, 0x6e, 0x93, 0xf3, 0x55, 0x53, 0xed, 0x4b, + 0xb0, 0x6f, 0x72, 0x09, 0xe3, 0x4f, 0xda, 0xb4, 0x95, 0x2f, 0xc0, 0x1d, 0x0f, 0x2f, 0x10, 0x3c, + 0x6f, 0xc8, 0x12, 0x42, 0xb3, 0x18, 0xb9, 0x58, 0xb5, 0x7c, 0xd5, 0x9b, 0x76, 0x36, 0xfd, 0x03, + 0x64, 0x73, 0xcc, 0x07, 0xe4, 0x1e, 0xe2, 0x35, 0xbe, 0x99, 0x2f, 0x76, 0xe2, 0xde, 0x94, 0x4e, + 0x7c, 0xfa, 0x99, 0x7c, 0xf0, 0x1a, 0xd9, 0x25, 0x1f, 0x7f, 0x03, 0x00, 0x00, 0xff, 0xff, 0x8d, + 0xa3, 0x76, 0xb5, 0x3e, 0x02, 0x00, 0x00, } diff --git a/ui.proto/ui.proto b/ui.proto/ui.proto index 987e691a..30d86588 100644 --- a/ui.proto/ui.proto +++ b/ui.proto/ui.proto @@ -32,5 +32,5 @@ message RuleReply { string action = 2; string duration = 3; string what = 4; - string with = 5; + string value = 5; } diff --git a/ui.proto/ui_pb2.py b/ui.proto/ui_pb2.py new file mode 100644 index 00000000..9847e3b6 --- /dev/null +++ b/ui.proto/ui_pb2.py @@ -0,0 +1,303 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: ui.proto + +import sys +_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +from google.protobuf import descriptor_pb2 +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='ui.proto', + package='ui', + syntax='proto3', + serialized_pb=_b('\n\x08ui.proto\x12\x02ui\"\x19\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x04\"\x17\n\tPingReply\x12\n\n\x02id\x18\x01 \x01(\x04\"\xb5\x01\n\x0bRuleRequest\x12\x10\n\x08protocol\x18\x01 \x01(\t\x12\x0e\n\x06src_ip\x18\x02 \x01(\t\x12\x10\n\x08src_port\x18\x03 \x01(\r\x12\x0e\n\x06\x64st_ip\x18\x04 \x01(\t\x12\x10\n\x08\x64st_host\x18\x05 \x01(\t\x12\x10\n\x08\x64st_port\x18\x06 \x01(\r\x12\x12\n\nprocess_id\x18\x07 \x01(\r\x12\x14\n\x0cprocess_path\x18\x08 \x01(\t\x12\x14\n\x0cprocess_args\x18\t \x03(\t\"X\n\tRuleReply\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0e\n\x06\x61\x63tion\x18\x02 \x01(\t\x12\x10\n\x08\x64uration\x18\x03 \x01(\t\x12\x0c\n\x04what\x18\x04 \x01(\t\x12\r\n\x05value\x18\x05 \x01(\t2[\n\x02UI\x12(\n\x04Ping\x12\x0f.ui.PingRequest\x1a\r.ui.PingReply\"\x00\x12+\n\x07\x41skRule\x12\x0f.ui.RuleRequest\x1a\r.ui.RuleReply\"\x00\x62\x06proto3') +) + + + + +_PINGREQUEST = _descriptor.Descriptor( + name='PingRequest', + full_name='ui.PingRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='id', full_name='ui.PingRequest.id', index=0, + number=1, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=16, + serialized_end=41, +) + + +_PINGREPLY = _descriptor.Descriptor( + name='PingReply', + full_name='ui.PingReply', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='id', full_name='ui.PingReply.id', index=0, + number=1, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=43, + serialized_end=66, +) + + +_RULEREQUEST = _descriptor.Descriptor( + name='RuleRequest', + full_name='ui.RuleRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='protocol', full_name='ui.RuleRequest.protocol', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='src_ip', full_name='ui.RuleRequest.src_ip', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='src_port', full_name='ui.RuleRequest.src_port', index=2, + number=3, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='dst_ip', full_name='ui.RuleRequest.dst_ip', index=3, + number=4, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='dst_host', full_name='ui.RuleRequest.dst_host', index=4, + number=5, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='dst_port', full_name='ui.RuleRequest.dst_port', index=5, + number=6, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='process_id', full_name='ui.RuleRequest.process_id', index=6, + number=7, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='process_path', full_name='ui.RuleRequest.process_path', index=7, + number=8, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='process_args', full_name='ui.RuleRequest.process_args', index=8, + number=9, type=9, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=69, + serialized_end=250, +) + + +_RULEREPLY = _descriptor.Descriptor( + name='RuleReply', + full_name='ui.RuleReply', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='name', full_name='ui.RuleReply.name', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='action', full_name='ui.RuleReply.action', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='duration', full_name='ui.RuleReply.duration', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='what', full_name='ui.RuleReply.what', index=3, + number=4, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='value', full_name='ui.RuleReply.value', index=4, + number=5, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=252, + serialized_end=340, +) + +DESCRIPTOR.message_types_by_name['PingRequest'] = _PINGREQUEST +DESCRIPTOR.message_types_by_name['PingReply'] = _PINGREPLY +DESCRIPTOR.message_types_by_name['RuleRequest'] = _RULEREQUEST +DESCRIPTOR.message_types_by_name['RuleReply'] = _RULEREPLY +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +PingRequest = _reflection.GeneratedProtocolMessageType('PingRequest', (_message.Message,), dict( + DESCRIPTOR = _PINGREQUEST, + __module__ = 'ui_pb2' + # @@protoc_insertion_point(class_scope:ui.PingRequest) + )) +_sym_db.RegisterMessage(PingRequest) + +PingReply = _reflection.GeneratedProtocolMessageType('PingReply', (_message.Message,), dict( + DESCRIPTOR = _PINGREPLY, + __module__ = 'ui_pb2' + # @@protoc_insertion_point(class_scope:ui.PingReply) + )) +_sym_db.RegisterMessage(PingReply) + +RuleRequest = _reflection.GeneratedProtocolMessageType('RuleRequest', (_message.Message,), dict( + DESCRIPTOR = _RULEREQUEST, + __module__ = 'ui_pb2' + # @@protoc_insertion_point(class_scope:ui.RuleRequest) + )) +_sym_db.RegisterMessage(RuleRequest) + +RuleReply = _reflection.GeneratedProtocolMessageType('RuleReply', (_message.Message,), dict( + DESCRIPTOR = _RULEREPLY, + __module__ = 'ui_pb2' + # @@protoc_insertion_point(class_scope:ui.RuleReply) + )) +_sym_db.RegisterMessage(RuleReply) + + + +_UI = _descriptor.ServiceDescriptor( + name='UI', + full_name='ui.UI', + file=DESCRIPTOR, + index=0, + options=None, + serialized_start=342, + serialized_end=433, + methods=[ + _descriptor.MethodDescriptor( + name='Ping', + full_name='ui.UI.Ping', + index=0, + containing_service=None, + input_type=_PINGREQUEST, + output_type=_PINGREPLY, + options=None, + ), + _descriptor.MethodDescriptor( + name='AskRule', + full_name='ui.UI.AskRule', + index=1, + containing_service=None, + input_type=_RULEREQUEST, + output_type=_RULEREPLY, + options=None, + ), +]) +_sym_db.RegisterServiceDescriptor(_UI) + +DESCRIPTOR.services_by_name['UI'] = _UI + +# @@protoc_insertion_point(module_scope) diff --git a/ui.proto/ui_pb2_grpc.py b/ui.proto/ui_pb2_grpc.py new file mode 100644 index 00000000..6f69f414 --- /dev/null +++ b/ui.proto/ui_pb2_grpc.py @@ -0,0 +1,63 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +import grpc + +import ui_pb2 as ui__pb2 + + +class UIStub(object): + # missing associated documentation comment in .proto file + pass + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.Ping = channel.unary_unary( + '/ui.UI/Ping', + request_serializer=ui__pb2.PingRequest.SerializeToString, + response_deserializer=ui__pb2.PingReply.FromString, + ) + self.AskRule = channel.unary_unary( + '/ui.UI/AskRule', + request_serializer=ui__pb2.RuleRequest.SerializeToString, + response_deserializer=ui__pb2.RuleReply.FromString, + ) + + +class UIServicer(object): + # missing associated documentation comment in .proto file + pass + + def Ping(self, request, context): + # missing associated documentation comment in .proto file + pass + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def AskRule(self, request, context): + # missing associated documentation comment in .proto file + pass + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + +def add_UIServicer_to_server(servicer, server): + rpc_method_handlers = { + 'Ping': grpc.unary_unary_rpc_method_handler( + servicer.Ping, + request_deserializer=ui__pb2.PingRequest.FromString, + response_serializer=ui__pb2.PingReply.SerializeToString, + ), + 'AskRule': grpc.unary_unary_rpc_method_handler( + servicer.AskRule, + request_deserializer=ui__pb2.RuleRequest.FromString, + response_serializer=ui__pb2.RuleReply.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'ui.UI', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) diff --git a/ui/.gitignore b/ui/.gitignore new file mode 100644 index 00000000..0d20b648 --- /dev/null +++ b/ui/.gitignore @@ -0,0 +1 @@ +*.pyc diff --git a/ui/dialog.py b/ui/dialog.py new file mode 100644 index 00000000..dbbce928 --- /dev/null +++ b/ui/dialog.py @@ -0,0 +1,40 @@ +from PyQt5 import QtCore, QtGui, uic, QtWidgets +from PyQt5 import QtDBus +import threading +import logging +import queue +import sys +import os + +DIALOG_UI_PATH = "%s/res/dialog.ui" % os.path.dirname(sys.modules[__name__].__file__) + +class Dialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]): + def __init__(self, parent=None): + QtWidgets.QDialog.__init__(self, parent, QtCore.Qt.WindowStaysOnTopHint) + + self.setupUi(self) + + self._done = threading.Event() + self._allow_button = self.findChild(QtWidgets.QPushButton, "allowButton") + self._allow_button.clicked.connect(self._on_allow_click) + self._block_button = self.findChild(QtWidgets.QPushButton, "denyButton") + self._block_button.clicked.connect(self._on_block_click) + self._duration_combo = self.findChild(QtWidgets.QComboBox, "actionComboBox") + self._duration_combo.currentIndexChanged[str].connect(self._on_duration_changed) + + def promptUser(self, req): + self._done.clear() + self.show() + self._done.wait() + + def _on_duration_changed(self): + s_option = self._duration_combo.currentText() + + def _on_allow_click(self): + self.hide() + self._done.set() + + def _on_block_click(self): + self.hide() + self._done.set() + diff --git a/ui/main.py b/ui/main.py new file mode 100644 index 00000000..936ad660 --- /dev/null +++ b/ui/main.py @@ -0,0 +1,53 @@ +from PyQt5 import QtWidgets +import sys +import os +import time +import signal + +path = os.path.abspath(os.path.dirname(__file__)) +sys.path.append(path) +sys.path.append(path + "/../ui.proto/") + +import grpc +from concurrent import futures +import ui_pb2 +import ui_pb2_grpc + +from dialog import Dialog + +class UIServicer(ui_pb2_grpc.UIServicer): + def __init__(self): + self.dialog = Dialog() + + def Ping(self, request, context): + # print "Got ping 0x%x" % request.id + return ui_pb2.PingReply(id=request.id) + + def AskRule(self, request, context): + print "Got request: %s" % request + self.dialog.promptUser(request) + return ui_pb2.RuleReply( + name="user.choice", + action="allow", + duration="always", + what="process.path", + value=request.process_path) + +if __name__ == '__main__': + app = QtWidgets.QApplication(sys.argv) + server = grpc.server(futures.ThreadPoolExecutor(max_workers=1)) + + ui_pb2_grpc.add_UIServicer_to_server(UIServicer(), server) + + server.add_insecure_port("unix:./opensnitch-ui.sock") + + # https://stackoverflow.com/questions/5160577/ctrl-c-doesnt-work-with-pyqt + signal.signal(signal.SIGINT, signal.SIG_DFL) + + try: + server.start() + app.exec_() + except KeyboardInterrupt: + app.quit() + server.stop(0) + diff --git a/ui/res/dialog.ui b/ui/res/dialog.ui new file mode 100644 index 00000000..e3817d3d --- /dev/null +++ b/ui/res/dialog.ui @@ -0,0 +1,184 @@ + + + Dialog + + + + 0 + 0 + 653 + 316 + + + + + 37 + 0 + + + + + 490 + 220 + + + + + 1000 + 400 + + + + + true + + + + OpenSnitch + + + + + 20 + 20 + 433 + 42 + + + + + 15 + 75 + true + + + + Chromium Web Browser + + + + + + 0 + 60 + 651 + 16 + + + + Qt::Horizontal + + + + + + 20 + 90 + 611 + 81 + + + + Chromium Web Browser (/usr/lib/chromium-browser) wants to connect to host 127.0.0.1 on TCP port 443 (https) + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + true + + + + + + 220 + 260 + 191 + 50 + + + + Block + + + + + + 10 + 260 + 191 + 50 + + + + + false + + + + false + + + Allow + + + true + + + + + + 450 + 260 + 191 + 50 + + + + + 97 + 26 + + + + + 204 + 50 + + + + + Once + + + + + Until Quit + + + + + Forever + + + + + + + 590 + 5 + 60 + 60 + + + + + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + +