//go:build windows package main // The ask pipe: ask.sock (agentask.go) for a bastion that runs as the Windows // service. // // Installed for the whole machine, the bastion is a LocalSystem service and // its state sits in LocalSystem's profile, where the person's own account can // neither read nor write it. Before this pipe, that account had no way to the // machine's bastion at all: `privatae-bastion ui` signed in with a key of its // own and the console answered 401, the reads and owner acts were refused // beside the service, and an agent could not ask for a stop. The pipe answers // exactly what ask.sock answers, through the same code (askAnswer): a stop // asked by any account, and the console, the reads and the owner's acts for // the account recorded as the machine's owner (approvals-owner --sid), with // the caller taken from the pipe client's token, never from the request. // // The pipe is the approvals pipe's machinery (approvalpipe_windows.go): // overlapped I/O with a timeout on every call, a new instance created before // the one it replaces is handed out, the first instance created with // FILE_FLAG_FIRST_PIPE_INSTANCE, and the asking side checking the pipe's // server is the service's own process before it sends anything, since an act // carries the owner's code. import ( "encoding/json" "errors" "fmt" "io" "time" "golang.org/x/sys/windows" ) // startAskPipe serves the ask pipe until done is closed, retrying with backoff // when it cannot (pipeServeRetrying), as the approvals pipe does. func startAskPipe(done <-chan struct{}) { wait := func(d time.Duration) bool { select { case <-done: return false case <-time.After(d): return true } } go pipeServeRetrying(func(ready func()) error { return servePipe(done, ready, askPipeName, askPipeSDDL, serveAskPipeConn) }, wait, func(f string, a ...any) { fmt.Printf(f, a...) }) } // serveAskPipeConn answers one ask, then waits briefly for the client to // close: closing first would discard an answer it has not read yet. func serveAskPipeConn(c *pipeConn) { client, cerr := pipeClientOf(c.h) raw, err := c.readLine(askMaxBytes, pipeIOTimeout) if err != nil { return } var reply any switch { case cerr != nil: reply = askUnattributable(cerr) default: who := pipeWho(client) var req agentAskRequest if err := json.Unmarshal(raw, &req); err != nil { reply = agentAskReply{User: who, Why: "the request could not be read"} break } peer := askPeer{SID: client.UserSID, Name: who, PID: int(client.PID)} reply = askAnswer(peer, req, func(time.Duration) {}, func() int { return int(client.PID) }) } out, _ := json.Marshal(reply) if err := c.write(append(out, '\n'), pipeIOTimeout); err != nil { return } _, _ = c.read(pipeIOTimeout) } // dialAskLane opens the service's ask pipe; p, ask.sock's path, does not // apply here. func dialAskLane(string) (askConn, error) { c, err := dialServicePipe(askPipeName) if err != nil { return nil, err } if err := pipeServerIsService(c.h); err != nil { c.close() return nil, err } return &askPipeClient{c: c}, nil } // askLaneAddr is where the lane is, as a message names it. func askLaneAddr(string) string { return askPipeName } // askPipeClient is the asking side's end of the pipe, read and written the // way the lane's code reads and writes a socket: a deadline for the whole // exchange, bytes in, bytes out. type askPipeClient struct { c *pipeConn deadline time.Time pending []byte } func (a *askPipeClient) SetDeadline(t time.Time) error { a.deadline = t return nil } // left is what remains of the deadline for one call; 0 waits as long as it // takes, which only a caller that set no deadline gets. func (a *askPipeClient) left() time.Duration { if a.deadline.IsZero() { return 0 } if d := time.Until(a.deadline); d > 0 { return d } return time.Millisecond } func (a *askPipeClient) Read(b []byte) (int, error) { for len(a.pending) == 0 { n, err := a.c.read(a.left()) a.pending = append(a.pending, a.c.buf[:n]...) if err != nil && !errors.Is(err, windows.ERROR_MORE_DATA) { if len(a.pending) > 0 { break } if errors.Is(err, windows.ERROR_BROKEN_PIPE) || errors.Is(err, windows.ERROR_PIPE_NOT_CONNECTED) { return 0, io.EOF } return 0, err } } n := copy(b, a.pending) a.pending = a.pending[n:] return n, nil } func (a *askPipeClient) Write(b []byte) (int, error) { if err := a.c.write(b, a.left()); err != nil { return 0, err } return len(b), nil } func (a *askPipeClient) Close() error { a.c.close() return nil }