aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal/pipewire/securitycontext.go
blob: 42eb16cdaacc4f2f0740723e2ebed061e5ff836d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package pipewire

/* pipewire/extensions/security-context.h */

const (
	PW_TYPE_INTERFACE_SecurityContext = PW_TYPE_INFO_INTERFACE_BASE + "SecurityContext"
	PW_SECURITY_CONTEXT_PERM_MASK     = PW_PERM_RWX
	PW_VERSION_SECURITY_CONTEXT       = 3

	PW_EXTENSION_MODULE_SECURITY_CONTEXT = PIPEWIRE_MODULE_PREFIX + "module-security-context"
)

const (
	PW_SECURITY_CONTEXT_EVENT_NUM = iota

	PW_VERSION_SECURITY_CONTEXT_EVENTS = 0
)

const (
	PW_SECURITY_CONTEXT_METHOD_ADD_LISTENER = iota
	PW_SECURITY_CONTEXT_METHOD_CREATE
	PW_SECURITY_CONTEXT_METHOD_NUM

	PW_VERSION_SECURITY_CONTEXT_METHODS = 0
)

// SecurityContextCreate is sent to create a new security context.
//
// Creates a new security context with a socket listening FD.
// PipeWire will accept new client connections on listen_fd.
//
// listen_fd must be ready to accept new connections when this
// request is sent by the client. In other words, the client must
// call bind(2) and listen(2) before sending the FD.
//
// close_fd is a FD closed by the client when PipeWire should stop
// accepting new connections on listen_fd.
//
// PipeWire must continue to accept connections on listen_fd when
// the client which created the security context disconnects.
//
// After sending this request, closing listen_fd and close_fd
// remains the only valid operation on them.
type SecurityContextCreate struct {
	// The offset in the SCM_RIGHTS msg_control message to
	// the fd to listen on for new connections.
	ListenFd Fd
	// The offset in the SCM_RIGHTS msg_control message to
	// the fd used to stop listening.
	CloseFd Fd

	// Extra properties. These will be copied on the client
	// that connects through this context.
	Properties *SPADict `json:"props"`
}

// Size satisfies [KnownSize] with a value computed at runtime.
func (c *SecurityContextCreate) Size() Word {
	return SizePrefix +
		Size(SizeFd) +
		Size(SizeFd) +
		c.Properties.Size()
}

// MarshalBinary satisfies [encoding.BinaryMarshaler] via [Marshal].
func (c *SecurityContextCreate) MarshalBinary() ([]byte, error) { return Marshal(c) }

// UnmarshalBinary satisfies [encoding.BinaryUnmarshaler] via [Unmarshal].
func (c *SecurityContextCreate) UnmarshalBinary(data []byte) error { return Unmarshal(data, c) }

// SecurityContext holds state of [PW_TYPE_INTERFACE_SecurityContext].
type SecurityContext struct {
	// Proxy id as tracked by [Context].
	ID Int `json:"proxy_id"`
	// Global id as tracked by [Registry].
	GlobalID Int `json:"id"`

	ctx *Context
}

// GetSecurityContext queues a [RegistryBind] message for the PipeWire server
// and returns the address of the newly allocated [SecurityContext].
func (registry *Registry) GetSecurityContext() (securityContext *SecurityContext, err error) {
	securityContext = &SecurityContext{ctx: registry.ctx}
	for globalId, object := range registry.Objects {
		if object.Type == securityContext.String() {
			securityContext.GlobalID = globalId
			securityContext.ID, err = registry.bind(securityContext, securityContext.GlobalID, PW_VERSION_SECURITY_CONTEXT)
			return
		}
	}

	return nil, UnsupportedObjectTypeError(securityContext.String())
}

// Create queues a [SecurityContextCreate] message for the PipeWire server.
func (securityContext *SecurityContext) Create(listenFd, closeFd int, props SPADict) error {
	// queued in reverse based on upstream behaviour, unsure why
	offset := securityContext.ctx.queueFiles(closeFd, listenFd)
	return securityContext.ctx.writeMessage(
		securityContext.ID,
		PW_SECURITY_CONTEXT_METHOD_CREATE,
		&SecurityContextCreate{ListenFd: offset + 1, CloseFd: offset + 0, Properties: &props},
	)
}

func (securityContext *SecurityContext) consume(opcode byte, files []int, _ func(v any)) error {
	closeReceivedFiles(files...)
	switch opcode {
	// SecurityContext does not receive any events

	default:
		return &UnsupportedOpcodeError{opcode, securityContext.String()}
	}

}

func (securityContext *SecurityContext) setBoundProps(event *CoreBoundProps) error {
	if securityContext.ID != event.ID {
		return &InconsistentIdError{Proxy: securityContext, ID: securityContext.ID, ServerID: event.ID}
	}
	if securityContext.GlobalID != event.GlobalID {
		return &InconsistentIdError{Global: true, Proxy: securityContext, ID: securityContext.GlobalID, ServerID: event.GlobalID}
	}
	return nil
}

func (securityContext *SecurityContext) String() string { return PW_TYPE_INTERFACE_SecurityContext }