Skip to content

Commit

Permalink
Add support for libassuan file sockets (WinGnuPG support) (jstarks#6)
Browse files Browse the repository at this point in the history
This commit squishes the changeset presented on jstarks#6. That commit is then rebased
onto albertony/npiperelay:fork
  • Loading branch information
NZSmartie authored and ndimiduk committed Feb 27, 2024
1 parent 74a99d8 commit 38d9019
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 2 deletions.
116 changes: 114 additions & 2 deletions npiperelay.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@
package main

import (
"bufio"
"errors"
"flag"
"fmt"
"io"
"log"
"os"
"runtime"
"strconv"
"sync"
"time"

Expand All @@ -20,6 +22,7 @@ const (
cSECURITY_SQOS_PRESENT = 0x100000 //nolint:revive,stylecheck // Don't include revive and stylecheck when running golangci-lint to stop complain about use of underscores in Go names
cSECURITY_ANONYMOUS = 0 //nolint:revive,stylecheck // Don't include revive and stylecheck when running golangci-lint to stop complain about use of underscores in Go names
cPOLL_TIMEOUT = 200 * time.Millisecond //nolint:revive,stylecheck // Don't include revive and stylecheck when running golangci-lint to stop complain about use of underscores in Go names
pollTimeout = 200 * time.Millisecond

Check failure on line 25 in npiperelay.go

View workflow job for this annotation

GitHub Actions / Build

File is not `goimports`-ed (goimports)
)

var (
Expand All @@ -28,6 +31,7 @@ var (
closeOnEOF = flag.Bool("ep", false, "terminate on EOF reading from the pipe, even if there is more data to write")
closeOnStdinEOF = flag.Bool("ei", false, "terminate on EOF reading from stdin, even if there is more data to write")
runInBackground = flag.Bool("bg", false, "hide console window and run in background")
assuan = flag.Bool("a", false, "treat the target as a libassuan file socket (Used by GnuPG)")
verbose = flag.Bool("v", false, "verbose output on stderr")

version = "0.0.0-dev" // Replaced with value from ldflag in build by GoReleaser: Current Git tag with the v prefix stripped
Expand Down Expand Up @@ -80,6 +84,107 @@ func dialPipe(p string, poll bool) (*overlappedFile, error) {
}
}

func dialPort(p int, poll bool) (*overlappedFile, error) {

Check warning on line 87 in npiperelay.go

View workflow job for this annotation

GitHub Actions / Build

unused-parameter: parameter 'poll' seems to be unused, consider removing or renaming it as _ (revive)
if p < 0 || p > 65535 {
return nil, errors.New("Invalid port value")

Check failure on line 89 in npiperelay.go

View workflow job for this annotation

GitHub Actions / Build

ST1005: error strings should not be capitalized (stylecheck)
}

h, err := windows.Socket(windows.AF_INET, windows.SOCK_STREAM, 0)
if err != nil {
return nil, err
}

// Create a SockaddrInet4 for connecting to
sa := &windows.SockaddrInet4{Addr: [4]byte{0x7F, 0x00, 0x00, 0x01}, Port: p}

// Bind to a randomly assigned local port
err = windows.Bind(h, &windows.SockaddrInet4{})
if err != nil {
return nil, err
}

// Wrap our socket up to be properly handled
conn := newOverlappedFile(h)

// Connect to the LibAssuan socket using overlapped ConnectEx operation
_, err = conn.asyncIo(func(h windows.Handle, n *uint32, o *windows.Overlapped) error {

Check warning on line 110 in npiperelay.go

View workflow job for this annotation

GitHub Actions / Build

unused-parameter: parameter 'n' seems to be unused, consider removing or renaming it as _ (revive)
return windows.ConnectEx(h, sa, nil, 0, nil, o)
})
if err != nil {
return nil, err
}

return conn, nil
}

// LibAssaun file socket: Attempt to read contents of the target file and connect to a TCP port
func dialAssuan(p string, poll bool) (*overlappedFile, error) {
pipeConn, err := dialPipe(p, poll)
if err != nil {
return nil, err
}

var port int
var nonce [16]byte

reader := bufio.NewReader(pipeConn)

// Read the target port number from the first line
tmp, _, err := reader.ReadLine()
if err == nil {
port, err = strconv.Atoi(string(tmp))
}
if err != nil {
return nil, err
}

// Read the rest of the nonce from the file
n, err := reader.Read(nonce[:])
if err != nil {
return nil, err
}

if n != 16 {
err = fmt.Errorf("Read incorrect number of bytes for nonce. Expected 16, got %d (0x%X)", n, nonce)
return nil, err
}

if *verbose {
log.Printf("Port: %d, Nonce: %X", port, nonce)
}

pipeConn.Close()

Check failure on line 156 in npiperelay.go

View workflow job for this annotation

GitHub Actions / Build

Error return value of `pipeConn.Close` is not checked (errcheck)

for {
// Try to connect to the libassaun TCP socket hosted on localhost
conn, err := dialPort(port, poll)

if poll && (err == windows.WSAETIMEDOUT || err == windows.WSAECONNREFUSED || err == windows.WSAENETUNREACH || err == windows.ERROR_CONNECTION_REFUSED) {
time.Sleep(pollTimeout)
continue
}

if err != nil {
err = os.NewSyscallError("ConnectEx", err)
return nil, err
}

_, err = conn.Write(nonce[:])
if err != nil {
return nil, err
}

return conn, nil
}
}

func underlyingError(err error) error {
if serr, ok := err.(*os.SyscallError); ok {
return serr.Err
}
return err
}

func main() {
flag.Usage = func() {
// Custom usage message (default documented here: https://pkg.go.dev/flag#pkg-variables)
Expand Down Expand Up @@ -108,7 +213,14 @@ func main() {
log.Println("connecting to", args[0])
}

conn, err := dialPipe(args[0], *poll)
var conn *overlappedFile
var err error

if !*assuan {
conn, err = dialPipe(args[0], *poll)
} else {
conn, err = dialAssuan(args[0], *poll)
}
if err != nil {
log.Fatalln(err)
}
Expand Down Expand Up @@ -145,7 +257,7 @@ func main() {
}()

_, err = io.Copy(os.Stdout, conn)
if errors.Is(err, windows.ERROR_BROKEN_PIPE) || errors.Is(err, windows.ERROR_PIPE_NOT_CONNECTED) {
if underlyingError(err) == windows.ERROR_BROKEN_PIPE || underlyingError(err) == windows.ERROR_PIPE_NOT_CONNECTED {
// The named pipe is closed and there is no more data to read. Since
// named pipes are not bidirectional, there is no way for the other side
// of the pipe to get more data, so do not wait for the stdin copy to
Expand Down
3 changes: 3 additions & 0 deletions scripts/gpg-relay
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/sh

exec socat UNIX-LISTEN:/home/<your-username>/.gnupg/S.gpg-agent,fork, EXEC:'npiperelay.exe -ei -ep -s -a "C:/Users/<your-user-folder>/AppData/Roaming/gnupg/S.gpg-agent"',nofork

0 comments on commit 38d9019

Please # to comment.