diff --git a/build.sh b/build.sh index 35fad00..beff407 100644 --- a/build.sh +++ b/build.sh @@ -20,7 +20,7 @@ if [ $# -ge 1 ]; then output="$1" fi -printf "Building...\n" +echo "Building.." go build -buildvcs=false -trimpath -ldflags "-s -w" -o "$output" cmd/main.go which upx &> /dev/null @@ -28,4 +28,4 @@ if [ $? -eq 0 ] && [ -f "$output" ]; then upx --compress-exports=1 --strip-relocs=1 --compress-icons=2 --best --no-backup -9 "$output" fi -printf "Done!\n" +echo "Done!" diff --git a/doc.go b/doc.go index f5d9107..771a72c 100644 --- a/doc.go +++ b/doc.go @@ -106,7 +106,7 @@ Server Options (for --new and --edit) during server runtime. VPN Network Options - --crosstalk Clients are allowed to connect to eachother. + --crosstalk Clients are allowed to connect to each other. --net The network that clients will use. --net-start The starting address for leases. --net-end The end address for leases. @@ -136,7 +136,7 @@ Server Options (for --new and --edit) over stored DHParams data. --dh-file Read and use the provided file for DHParams. This will fail if the file - does not exist. Overrites the previous + does not exist. Overrides the previous DHParam data saved. --dh-size <0|2048|[4096]> Size of the initial DHparam file. Can be 2048 or 4096, defaults to 4096. diff --git a/mail.go b/mail.go index ec16caf..5e79949 100644 --- a/mail.go +++ b/mail.go @@ -26,7 +26,7 @@ import ( "github.com/iDigitalFlame/akcss/xerr" ) -var mailer = &net.Dialer{Timeout: timeout, KeepAlive: timeout, DualStack: true} +var mailer = &net.Dialer{Timeout: timeout, KeepAlive: timeout} type mail struct { To string diff --git a/manager.go b/manager.go index 1af686f..8ca7fdc 100644 --- a/manager.go +++ b/manager.go @@ -31,6 +31,7 @@ import ( "time" "github.com/PurpleSec/logx" + "github.com/iDigitalFlame/akcss/userid" "github.com/iDigitalFlame/akcss/vpn" "github.com/iDigitalFlame/akcss/xerr" ) @@ -109,10 +110,10 @@ func daemon(f string, t bool) error { return xerr.Wrap(`could not listen on "`+m.Config.Socket+`"`, err) } if _, ok := l.(*net.UnixListener); ok { - if err = lookupNobody(); err != nil { - m.log.Warning(`[daemon] Could not lookup the "nobody" user, you may have permission issues: %s!`, err.Error()) + if n, err1 := userid.Nobody(); err1 != nil { + m.log.Warning(`[daemon] Could not lookup the "nobody" user, you may have permission issues: %s!`, err1.Error()) } else { - if err = os.Chown(m.Config.Socket[5:], 0, nobody); err != nil { + if err = os.Chown(m.Config.Socket[5:], 0, n); err != nil { l.Close() m.shutdown(l) return xerr.Wrap(`could not set permissions on "`+m.Config.Socket+`"`, err) @@ -417,7 +418,7 @@ func (m *manager) listen(x context.Context, l net.Listener) { break } m.log.Error("[daemon/listen] Error occurred during accept: %s!", err.Error()) - if ok && !e.Timeout() && !e.Temporary() { + if ok && !e.Timeout() { break } continue @@ -425,24 +426,9 @@ func (m *manager) listen(x context.Context, l net.Listener) { if c == nil { continue } - if n, ok := c.(*net.UnixConn); ok { - var ( - f *os.File - u *syscall.Ucred - ) - if f, err = n.File(); err != nil { - m.log.Error("[daemon/listen] Could not grab file handle of socket: %s!", err.Error()) - c.Close() - continue - } - if u, err = syscall.GetsockoptUcred(int(f.Fd()), syscall.SOL_SOCKET, syscall.SO_PEERCRED); err != nil { - m.log.Error("[daemon/listen] Could get file handle peer creds: %s!", err.Error()) - f.Close() - c.Close() - continue - } - f.Close() - m.log.Trace("[daemon/listen] Connection established by PID: %d/UID: %d!", u.Pid, u.Uid) + if identify(m.log, c) != nil { + c.Close() + continue } go m.accept(x, c) } diff --git a/message.go b/message.go index e14d5f6..5f18c84 100644 --- a/message.go +++ b/message.go @@ -270,7 +270,7 @@ func raw(a uint8, d []byte, w io.Writer) error { ) (*b)[0], (*b)[1], (*b)[2] = a, byte(n>>8), byte(n) if _, err = w.Write((*b)[0:3]); err == nil && len(d) > 0 { - _, err = w.Write([]byte(d)) + _, err = w.Write(d) } bufs.Put(b) return err diff --git a/pki/authority.go b/pki/authority.go index df1f156..08c125e 100644 --- a/pki/authority.go +++ b/pki/authority.go @@ -159,7 +159,12 @@ func (a *Authority) crl() ([]Update, error) { if a.lock.Unlock(); err != nil { return nil, err } - b, err := a.cert.CreateCRL(rand.Reader, a.key, r, t, t.AddDate(0, 0, a.Lifetime.crl())) + b, err := x509.CreateRevocationList(rand.Reader, &x509.RevocationList{ + Issuer: a.cert.Issuer, + ThisUpdate: t, + NextUpdate: t.AddDate(0, 0, a.Lifetime.crl()), + RevokedCertificates: r, + }, a.cert, a.key) if err != nil { return nil, xerr.Wrap("could not generate CRL", err) } diff --git a/pki/certfificate.go b/pki/certfificate.go index 00ed2d9..4938338 100644 --- a/pki/certfificate.go +++ b/pki/certfificate.go @@ -35,7 +35,7 @@ var ( errPrivateKey = xerr.New("no private key found") ) -// Certificate is a struct representation of an x509 Certificate. This struct +// Certificate is a struct representation of a x509 Certificate. This struct // contains some functions for convince and easy management. // // The certificate data is not loaded from the specified file path until it is @@ -54,7 +54,7 @@ type Certificate struct { // Revoke will revoke the Certificate if not already revoked. This function does // not return any values. The CRL must be regenerated using the 'Authority.Update()' -// function in order to take affect. +// function in order to take effect. func (c *Certificate) Revoke() { c.PrivateKey, c.Status = nil, statusRevoked } @@ -155,7 +155,7 @@ func (c *Certificate) WriteKey(w io.Writer) error { } // ValidFor returns true if the certificate is valid and is not expired nor revoked and ensures it will be valid -// for the suplied time duration. +// for the supplied time duration. func (c *Certificate) ValidFor(d time.Duration) bool { if !c.Valid() || c.init() != nil { return false diff --git a/process.go b/process.go index 23df348..18d0f17 100644 --- a/process.go +++ b/process.go @@ -136,7 +136,7 @@ func (s *server) edit(d *details) error { } return s.Save() } -func (m *manager) new(x context.Context, d *details) (*server, error) { +func (m *manager) new(_ context.Context, d *details) (*server, error) { var ( p uint16 n, h string @@ -316,7 +316,7 @@ func (m *manager) process(x context.Context, n message) (*message, error) { } s, err = m.new(x, v) if err != nil { - m.log.Error("[daemon/process/new] Attemtping to create a new server %q failed: %s!", i, err.Error()) + m.log.Error("[daemon/process/new] Attempting to create a new server %q failed: %s!", i, err.Error()) } if err == nil && s != nil { m.log.Trace("[daemon/process/new] %s: Attempting to acquire server lock...", s.ID) @@ -324,7 +324,7 @@ func (m *manager) process(x context.Context, n message) (*message, error) { m.log.Trace("[daemon/process/new] %s: Server lock acquired.", s.ID) m.log.Debug("[daemon/process/new] %s: Editing new server, options [%+v]", s.ID, v) if err = s.edit(v); err != nil { - m.log.Error("[daemon/process/new] Attemtping to edit server %q failed: %s!", i, err.Error()) + m.log.Error("[daemon/process/new] Attempting to edit server %q failed: %s!", i, err.Error()) } if s.lock.Unlock(); err == nil && v.Restart { err = s.Start() @@ -347,7 +347,7 @@ func (m *manager) process(x context.Context, n message) (*message, error) { r, w, err := s.process(x, m, n) if w { if err != nil { - m.log.Warning("[daemon/process] %s: Received an error during previous operation (%s), attemping to save anyway!", s.ID, err.Error()) + m.log.Warning("[daemon/process] %s: Received an error during previous operation (%s), attempting to save anyway!", s.ID, err.Error()) } if err2 := s.Save(); err2 != nil { // We need to preserve the OG error. @@ -360,7 +360,7 @@ func (m *manager) process(x context.Context, n message) (*message, error) { s.lock.Unlock() return r, err } -func (s *server) process(x context.Context, m *manager, n message) (*message, bool, error) { +func (s *server) process(_ context.Context, m *manager, n message) (*message, bool, error) { switch n.Action { case actionCRL: m.log.Debug("[daemon/process/crl] %s: Triggering a CRL generation...", s.ID) @@ -425,14 +425,14 @@ func (s *server) process(x context.Context, m *manager, n message) (*message, bo return nil, false, err } if err := s.edit(v); err != nil { - m.log.Error("[daemon/process/edit] %s: Attemtping to edit server failed: %s!", s.ID, err.Error()) + m.log.Error("[daemon/process/edit] %s: Attempting to edit server failed: %s!", s.ID, err.Error()) return nil, false, err } if !v.Restart { return nil, true, nil } if err := s.Restart(); err != nil { - m.log.Error("[daemon/process/edit] %s: Attemtping to restart server failed: %s!", s.ID, err.Error()) + m.log.Error("[daemon/process/edit] %s: Attempting to restart server failed: %s!", s.ID, err.Error()) return nil, true, err } return nil, true, nil @@ -451,7 +451,7 @@ func (s *server) process(x context.Context, m *manager, n message) (*message, bo } m.log.Debug("[daemon/process/notify] %s: Adding notifications of %q for email %q.", s.ID, v.Action, v.Email) if err := s.AddNotify(v.Email, v.Action); err != nil { - m.log.Error("[daemon/process/notify] %s: Attemtping to add notification entry failed: %s!", s.ID, err.Error()) + m.log.Error("[daemon/process/notify] %s: Attempting to add notification entry failed: %s!", s.ID, err.Error()) return nil, false, err } return nil, true, nil @@ -621,7 +621,7 @@ func (s *server) process(x context.Context, m *manager, n message) (*message, bo m.log.Error("[daemon/process/newclient] %s: Error creating a new client %q: %s!", s.ID, v.Name, err.Error()) return nil, false, err } - return &message{Action: responseClientNew, Data: []byte(r)}, false, nil + return &message{Action: responseClientNew, Data: r}, false, nil case actionClientDelete: v, ok := n.e.(*typeClientDelete) if !ok || n.e == nil { @@ -642,7 +642,7 @@ func (s *server) process(x context.Context, m *manager, n message) (*message, bo m.log.Debug("[daemon/process/deleteclient] %s: Client %q removed and revoked.", s.ID, v.Name) return nil, true, nil } - m.log.Warning("[daemon/process/deleteclient] %s: Cannot revoke a non-existant certificate %q!", s.ID, v.Name) + m.log.Warning("[daemon/process/deleteclient] %s: Cannot revoke a non-existent certificate %q!", s.ID, v.Name) return nil, false, xerr.New(`certificate "` + v.Name + `" does not exist`) } m.log.Trace("[daemon/process] Received an invalid message, ignoring!") diff --git a/userid/lookup_nix.go b/userid/lookup_nix.go new file mode 100644 index 0000000..3bfe7a8 --- /dev/null +++ b/userid/lookup_nix.go @@ -0,0 +1,75 @@ +//go:build !windows + +// Copyright (C) 2021 - 2022 iDigitalFlame +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +package userid + +import ( + "os/user" + "strconv" + "sync" + + "github.com/iDigitalFlame/akcss/xerr" +) + +var nobodyOnce struct { + sync.Once + err error + user int +} + +func nobodyLookupOnce() { + u, err := user.Lookup("nobody") + if err != nil { + nobodyOnce.err = xerr.Wrap(`cannot lookup user "nobody"`, err) + return + } + v := u.Gid + if len(v) == 0 { + v = u.Uid + } + if len(v) == 0 || v == "0" { + nobodyOnce.err = xerr.New(`lookup for "nobody" returned invalid UID/GID`) + return + } + n, err := strconv.ParseInt(v, 10, 64) + if err != nil { + nobodyOnce.err = xerr.Wrap(`cannot parse "nobody" UID "`+v+`"`, err) + return + } + nobodyOnce.user = int(n) +} + +// Nobody will look up and cache the result of the GID for the "nobody" user, +// which is used to set permissions for OpenVPN sessions running with lower +// permissions can read public keys and validate clients. +// +// If the "rootok" build tag was used when compiling, this function will return +// (0, nil) if the lookup for "nobody" fails, which is useful when running in a +// container or a distro that does not have a "nobody" user. +// +// On Windows devices, this function does nothing and always returns (0, nil) +// although it shouldn't be used (hopefully the compiler optimizes it out). +func Nobody() (int, error) { + if nobodyOnce.Do(nobodyLookupOnce); nobodyOnce.err == nil { + return nobodyOnce.user, nil + } + if !rootOk { + return 0, nobodyOnce.err + } + return 0, nil +} diff --git a/userid/lookup_windows.go b/userid/lookup_windows.go new file mode 100644 index 0000000..8a14c00 --- /dev/null +++ b/userid/lookup_windows.go @@ -0,0 +1,33 @@ +//go:build windows + +// Copyright (C) 2021 - 2022 iDigitalFlame +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +package userid + +// Nobody will look up and cache the result of the GID for the "nobody" user, +// which is used to set permissions for OpenVPN sessions running with lower +// permissions can read public keys and validate clients. +// +// If the "rootok" build tag was used when compiling, this function will return +// (0, nil) if the lookup for "nobody" fails, which is useful when running in a +// container or a distro that does not have a "nobody" user. +// +// On Windows devices, this function does nothing and always returns (0, nil) +// although it shouldn't be used (hopefully the compiler optimizes it out). +func Nobody() (int, error) { + return 0, nil +} diff --git a/userid/z_no_priv.go b/userid/z_no_priv.go new file mode 100644 index 0000000..1da472b --- /dev/null +++ b/userid/z_no_priv.go @@ -0,0 +1,21 @@ +//go:build !rootok && !windows + +// Copyright (C) 2021 - 2022 iDigitalFlame +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +package userid + +const rootOk = true diff --git a/userid/z_priv.go b/userid/z_priv.go new file mode 100644 index 0000000..6da15fb --- /dev/null +++ b/userid/z_priv.go @@ -0,0 +1,21 @@ +//go:build rootok && !windows + +// Copyright (C) 2021 - 2022 iDigitalFlame +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +package userid + +const rootOk = true diff --git a/util.go b/util.go index ba66dda..17b35f9 100644 --- a/util.go +++ b/util.go @@ -19,17 +19,13 @@ package akcss import ( "io/fs" "os" - "os/user" "path/filepath" "runtime" - "strconv" "strings" - "github.com/iDigitalFlame/akcss/xerr" + "github.com/iDigitalFlame/akcss/userid" ) -var nobody = 0 - func valid(s string) bool { if len(s) == 0 { return false @@ -45,24 +41,6 @@ func valid(s string) bool { } return true } -func lookupNobody() error { - if nobody > 0 { - return nil - } - u, err := user.Lookup("nobody") - if err != nil { - return xerr.Wrap(`cannot lookup user "nobody"`, err) - } - if len(u.Uid) == 0 || u.Uid == "0" { - return xerr.New(`lookup for "nobody" returned invalid UID "` + u.Uid + `"`) - } - n, err := strconv.ParseInt(u.Uid, 10, 64) - if err != nil { - return xerr.Wrap(`cannot parse "nobody" UID "`+u.Uid+`"`, err) - } - nobody = int(n) - return nil -} func isUnix(s string) bool { if len(s) < 6 { return false @@ -111,19 +89,20 @@ func (m *manager) perms(p string, d fs.DirEntry, _ error) error { if runtime.GOOS == "windows" { return nil } - if err := lookupNobody(); err != nil { + n, err := userid.Nobody() + if err != nil { return err } if !d.IsDir() { if strings.EqualFold(d.Name(), "crl.pem") { - if err := os.Chown(p, 0, nobody); err != nil { + if err := os.Chown(p, 0, n); err != nil { return err } return os.Chmod(p, 0644) } switch strings.ToLower(filepath.Ext(p)) { case ".crt": - if err := os.Chown(p, 0, nobody); err != nil { + if err := os.Chown(p, 0, n); err != nil { return err } return os.Chmod(p, 0440) @@ -141,7 +120,7 @@ func (m *manager) perms(p string, d fs.DirEntry, _ error) error { case strings.EqualFold(d.Name(), "certs"): fallthrough case strings.HasPrefix(m.Config.Dirs.CA, p): - if err := os.Chown(p, 0, nobody); err != nil { + if err := os.Chown(p, 0, n); err != nil { return err } return os.Chmod(p, 0755) diff --git a/vpn/server.go b/vpn/server.go index 4b5280d..a9415f8 100644 --- a/vpn/server.go +++ b/vpn/server.go @@ -32,22 +32,23 @@ import ( "time" "github.com/PurpleSec/logx" + "github.com/iDigitalFlame/akcss/pki" "github.com/iDigitalFlame/akcss/xerr" ) var ( - errBlocked = xerr.New("server is blocked on an operaton") + errBlocked = xerr.New("server is blocked on an operation") errRunning = xerr.New("server is already running") errInvalidID = xerr.New("server ID cannot be empty") errNotRunning = xerr.New("server is not running") ) const ( - debug = false + debug = false // Set to true to prevent Akcss from deleting generated Server files grace = time.Hour * 24 timeout = time.Second * 15 - ipRoute2 = false + ipRoute2 = false // Set to true to enable "iproute2" support ) // Server is a struct that contains the configuration information for a OpenVPN @@ -181,7 +182,7 @@ func (s *Server) CRL() error { return s.crl(false, true, "") } -// Stop will gracefull stop the server and save any stored IP options in the struct. +// Stop will gracefully stop the server and save any stored IP options in the struct. // This will also remove the server runtime directory. func (s *Server) Stop() error { return s.stop(true) @@ -405,7 +406,7 @@ func (s *Server) Print(w writer) { } } -// Restart will gracefull stop the server and save any stored IP options in the +// Restart will gracefully stop the server and save any stored IP options in the // struct. Once complete, this function will regenerate the server configuration // files and will start up the server. func (s *Server) Restart() error { @@ -443,7 +444,7 @@ func (s *Server) stop(n bool) error { if err == os.ErrProcessDone { err = nil } else { - s.log.Warning("[server/stop] %s: Sending stop singal failed: %s!", s.ID, err.Error()) + s.log.Warning("[server/stop] %s: Sending stop signal failed: %s!", s.ID, err.Error()) } } s.log.Debug("[server/stop] %s: Waiting for process to complete.", s.ID) @@ -630,7 +631,7 @@ func (s *Server) notify(a action, m, d string) { } } -// Load will create and setup the initial properties of a Server struct from the +// Load will create and set up the initial properties of a Server struct from the // provided arguments and the data contained in the JSON byte array. This function // returns any errors made during reading/parsing. func Load(b []byte, m manager) (*Server, error) { diff --git a/vpn/vars.go b/vpn/vars.go index 6602b83..d8698fc 100644 --- a/vpn/vars.go +++ b/vpn/vars.go @@ -20,12 +20,11 @@ import ( "io" "io/fs" "os" - "os/user" "runtime" - "strconv" "strings" "time" + "github.com/iDigitalFlame/akcss/userid" "github.com/iDigitalFlame/akcss/xerr" ) @@ -35,8 +34,6 @@ const ( UDP = protocol(false) ) -var nobody = 0 - type protocol bool type action uint16 @@ -170,25 +167,15 @@ func perms(p string, d fs.DirEntry, _ error) error { if runtime.GOOS == "windows" { return nil } - if nobody == 0 { - u, err := user.Lookup("nobody") - if err != nil { - return xerr.Wrap(`cannot lookup user "nobody"`, err) - } - if len(u.Uid) == 0 || u.Uid == "0" { - return xerr.New(`lookup for "nobody" returned invalid UID "` + u.Uid + `"`) - } - n, err := strconv.ParseInt(u.Uid, 10, 64) - if err != nil { - return xerr.Wrap(`cannot parse "nobody" UID "`+u.Uid+`"`, err) - } - nobody = int(n) + n, err := userid.Nobody() + if err != nil { + return err } if d.IsDir() { if err := os.Chmod(p, 0750); err != nil { return err } - return os.Chown(p, 0, nobody) + return os.Chown(p, 0, n) } switch d.Name() { case "ip.log": @@ -197,7 +184,7 @@ func perms(p string, d fs.DirEntry, _ error) error { if err := os.Chmod(p, 0660); err != nil { return err } - return os.Chown(p, 0, nobody) + return os.Chown(p, 0, n) case "server.log": if err := os.Chmod(p, 0600); err != nil { return err @@ -208,7 +195,7 @@ func perms(p string, d fs.DirEntry, _ error) error { if err := os.Chmod(p, 0640); err != nil { return err } - return os.Chown(p, 0, nobody) + return os.Chown(p, 0, n) } func (o override) Get(s *Server, name, value string) string { if len(o) == 0 { diff --git a/z_id_nix.go b/z_id_nix.go new file mode 100644 index 0000000..d0c3d21 --- /dev/null +++ b/z_id_nix.go @@ -0,0 +1,48 @@ +//go:build !windows + +// Copyright (C) 2021 - 2022 iDigitalFlame +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +package akcss + +import ( + "net" + "syscall" + + "github.com/PurpleSec/logx" +) + +func identify(l logx.Log, c net.Conn) error { + n, ok := c.(*net.UnixConn) + if !ok { + return nil + } + var ( + u *syscall.Ucred + ) + f, err := n.File() + if err != nil { + l.Error("[daemon/listen] Could not grab file handle of socket: %s!", err.Error()) + return err + } + u, err = syscall.GetsockoptUcred(int(f.Fd()), syscall.SOL_SOCKET, syscall.SO_PEERCRED) + if f.Close(); err != nil { + l.Error("[daemon/listen] Could get file handle peer creds: %s!", err.Error()) + return err + } + l.Trace("[daemon/listen] Connection established by PID: %d/UID: %d!", u.Pid, u.Uid) + return nil +} diff --git a/z_id_windows.go b/z_id_windows.go new file mode 100644 index 0000000..4015345 --- /dev/null +++ b/z_id_windows.go @@ -0,0 +1,29 @@ +//go:build windows + +// Copyright (C) 2021 - 2022 iDigitalFlame +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +package akcss + +import ( + "net" + + "github.com/PurpleSec/logx" +) + +func identify(_ logx.Log, _ net.Conn) error { + return nil +}