Skip to content

Commit

Permalink
Patches and Fixes
Browse files Browse the repository at this point in the history
- Spelling and Grammar fixes
- Build Script small fixes
- Created new package "userid" to deduplicate code for getting the "nobody" GID
- Updated the CA to use "x509.CreateRevocationList" over "CreateCRL" which is depericated.
- Added proper Windows support (why you'd use Windows is beyond me)
- Added new compile time flag
  - rootok: Disables the errors when "nobody" cannot be found, useful for containers
    - Default off
  • Loading branch information
iDigitalFlame committed Sep 13, 2022
1 parent 3ace63d commit 88aff8b
Show file tree
Hide file tree
Showing 17 changed files with 281 additions and 96 deletions.
4 changes: 2 additions & 2 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ 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
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!"
4 changes: 2 additions & 2 deletions doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 <network> The network that clients will use.
--net-start <IP> The starting address for leases.
--net-end <IP> The end address for leases.
Expand Down Expand Up @@ -136,7 +136,7 @@ Server Options (for --new and --edit)
over stored DHParams data.
--dh-file <file path> 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.
Expand Down
2 changes: 1 addition & 1 deletion mail.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
30 changes: 8 additions & 22 deletions manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -417,32 +418,17 @@ 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
}
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)
}
Expand Down
2 changes: 1 addition & 1 deletion message.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 6 additions & 1 deletion pki/authority.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
6 changes: 3 additions & 3 deletions pki/certfificate.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
}
Expand Down Expand Up @@ -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
Expand Down
20 changes: 10 additions & 10 deletions process.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -316,15 +316,15 @@ 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)
s.lock.Lock()
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()
Expand All @@ -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.
Expand All @@ -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)
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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 {
Expand All @@ -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!")
Expand Down
75 changes: 75 additions & 0 deletions userid/lookup_nix.go
Original file line number Diff line number Diff line change
@@ -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 <https://www.gnu.org/licenses/>.
//

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
}
33 changes: 33 additions & 0 deletions userid/lookup_windows.go
Original file line number Diff line number Diff line change
@@ -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 <https://www.gnu.org/licenses/>.
//

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
}
21 changes: 21 additions & 0 deletions userid/z_no_priv.go
Original file line number Diff line number Diff line change
@@ -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 <https://www.gnu.org/licenses/>.
//

package userid

const rootOk = true
21 changes: 21 additions & 0 deletions userid/z_priv.go
Original file line number Diff line number Diff line change
@@ -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 <https://www.gnu.org/licenses/>.
//

package userid

const rootOk = true
Loading

0 comments on commit 88aff8b

Please sign in to comment.