This is the specification for the privleap protocol. Ambiguities are to be considered bugs, please report them!
- Action - A set of commands and accompanying metadata defined in one of privleap's configuration files. Each action corresponds to a single line of Bash code that is run when the action is triggered. Actions can only be triggered by users and groups authorized to trigger them, and may require the person operating the authorized user to prove their identity. Each action has a specific name.
- Session - A connection between a privleap client and a privleap background process. One or more messages may be sent back and forth in a session.
- Message - A single unit of data passed to privleap by an application, or passed to the application from privleap.
- Signal - A message sent to privleap specifying a particular action the application wishes to trigger. Each action has a corresponding signal with the same name as the action.
The server should assume that user-level clients are malicious and therefore treat any data from them with the utmost caution. Additionally, the server should aim to prevent DoS attacks caused by resource-intensive activity patterns. In general, the server should err on the side of caution and terminate a connection without explanation if the client is not sending data quickly enough or sends unexpected data. This same level of care does not need to be taken with the control socket, as it is accessibly only by root and thus is not a danger.
Each message is formatted as a Pascal-style string that can be up to 2^32-1 bytes long. The length prefix is in big-endian byte order. The string may contain arbitrary binary data (including NUL bytes) and should not be blindly trusted if it comes from an untrusted source. Messages are case-sensitive.
Throughout this spec, placeholders are formatted as <placeholder>
. These
placeholders are expected to be substituted wholesale for the actual data that
belongs there. For instance, CREATE <username>
contains the placeholder
<username>
. If the username john
is being used, the string would be
changed to CREATE john
.
When an application connects to any socket privleapd
has open and is
listening on, it begins a session with privleapd
. Sessions are only held
open for as long as necessary, and are closed as soon as possible.
On startup, privleapd
creates and listens on a socket at
/run/privleapd/control
. This socket is owned by root:root
and uses
permissions 0600
. The containing directory /run/privleapd
is owned by
root:root
and uses permissions 0644
. The control
socket is used to
manage communication sockets for each individual user account.
Any messages sent by the client must be no longer than 4096 bytes long (not
including the four-byte message length header). privleapd
will forcibly
disconnect a client that attempts to send a longer message than this. This is
expected to be enough to allow any practically-sized usernames, signal names,
and authentication data to be passed now and in the future. The server may send
messages longer than this, although it is not generally recommended.
privleapd
understands the following messages passed via the control
socket:
CREATE <username>
- Creates a communication socket for the specified user.privleapd
will respond withOK
if this succeeds,EXISTS
if the socket already exists, andCONTROL_ERROR
if this fails.DESTROY <username>
- Destroys a communication socket for the specified user.privleapd
will respond withOK
if this succeeds,NOUSER
if the specified user does not have a communication socket, andCONTROL_ERROR
if this fails.RELOAD
- Reloads configuration data.privleapd
will respond withOK
if this succeeds, andCONTROL_ERROR
if this fails (due to invalid configuration). The details of the invalid configuration that lead to a failure will be logged.
privleapd
can send the following messages on the control
socket, which
clients must be able to understand:
OK
- The operation specified by the first client-sent message (eitherCREATE
orDESTROY
) in this session succeeded.CONTROL_ERROR
- The operation specified by the first client-sent message (eitherCREATE
orDESTROY
) in this session failed.EXISTS
- The first client-sent message was aCREATE
message, but the username specified in the message already has a communication socket open.NOUSER
- The first client-sent message was aDESTROY
message, but the username specified in the message has no communication socket open.PERSISTENT_USER
- The first client-sent message was aDESTROY
message, but the username specified in the message is a "persistent user" and cannot have their socket destroyed. (See /etc/privleap/conf.d/README for more information on persistent users.)DISALLOWED_USER
- The first client-sent message was aCREATE
message, but the username specified in the message is not an "allowed user" and cannot have a socket created for them. (See /etc/privleap/conf.d/README for more information on allowed users.)
Regardless of the reply, privleapd
will close the session immediately after
sending one of the above messages, and will ignore all but the first message
the client sends.
In actual operation, an application involved in user login is expected to send
CREATE <username>
when <username>
logs in, while an application involved
in user logout is expected to send DESTROY <username>
when the user logs
out. This ensures that only actively logged-in users have open communication
sockets. If using shell scripts for this, leapctl --create <username>
can be
used to send CREATE <username>
, while leapctl --destroy <username>
can be
used to send DESTROY <username>
.
Communication sockets are stored under /run/privleapd/comm
. Each
communication socket is named after the user it is intended to be used by
(i.e. <username>
). It is owned by the user and group corresponding to that
user, and uses permissions 0600
. This ensures that only the authorized user
can communicate with privleapd
over this socket. This allows privleapd
to
identify which user is sending messages to it.
privleapd
understands the following messages passed via a communication
socket:
SIGNAL <signal_name>
- Sends a signal toprivleapd
, requesting the triggering of the action corresponding to<signal_name>
.
privleapd
can send the following messages, which clients must be able to
understand:
TRIGGER
- Indicates that the action requested by the previousSIGNAL
message has been triggered.privleapd
will sendTRIGGER
after beginning execution of the code specified in the action, with no respect to whether the action has completed or yet. If a client is satisfied that the action has started execution, it can disconnect fromprivleapd
immediately, otherwise it should wait to disconnect until receiving aRESULT_EXITCODE
message.TRIGGER_ERROR
- Indicates that the action requested by the previousSIGNAL
message was authorized, but launching it failed. This is usually due to invalid or faulty configuration. Note thatTRIGGER_ERROR
is reserved for actual problems starting the action. If the action starts successfully but exits non-zero,privleapd
will still consider this a success. The client can use the value accompanying theRESULT_EXITCODE
message to determine whether the action actually succeeded or not.RESULT_STDOUT <result_stdout_text>
- Indicates that the action requested by the previousSIGNAL
message wrote the given block of data to STDOUT.<result_stdout_text>
is arbitrary binary data.RESULT_STDERR <result_stderr_text>
- Indicates that the action requested by the previousSIGNAL
message wrote the given block of data to STDERR.<result_stderr_text>
is arbitrary binary data.RESULT_EXITCODE <exit_code>
- Indicates that the action requested by the previousSIGNAL
message has completed execution, and that it exited with code<exit_code>
.<exit_code>
is a string representing a decimal integer between 0 and 255.UNAUTHORIZED
- Indicates that the user attempting to trigger the specified action is not authorized to do so, or that the signal send by the client has no matching action. This response intentionally does not tell the client whether they simply aren't allowed to perform the action, or whether the action doesn't even exist.
privleapd
will only parse the first one of each of the above client-sent
messages per session. privleapd
will terminate the session immediately after
sending UNAUTHORIZED
, or immediately after sending RESULT_EXITCODE
.
privleapd may send multiple RESULT_STDOUT
and RESULT_STDERR
messages in a
single session.
In actual operation, the client (most likely leaprun
) is expected to open a
session with privleapd
and send a SIGNAL
message. If the user is
authorized to trigger the action corresponding to this signal, privleapd
will respond with TRIGGER
upon triggering the action, then will send
RESULT_STDOUT
and RESULT_STDERR
once the action completes.
- Right now privleap is designed only to allow running specific actions as
root without a password, without the dangers of sudo. It would make it much
more useful if it was able to handle identity verification. This could be
done with
CHALLENGE
andCHALLENGE_PASS
messages from the server, and aRESPONSE
message from the client. This would only allow for simple password-based (or similar) auth though. PAM could potentially also be used, but trying to forward PAM auth requests over a socket could be extremely difficult or impossible, so in practice this would probably end up bypassing PAM, which is potentially livable but not ideal.