SUDO_LOGSRV.PROTO(5)
๐ NAME ๐
sudo_logsrv.proto โ Sudo log server protocol, which lives in the File Formats Manual as SUDO_LOGSRV.PROTO(5) ๐โจ
๐ธ DESCRIPTION ๐ธ
okay so starting with version 1.9.0, sudo can send event and I/O logs off to a log server ๐ฐ๏ธ๐ฅ The protocol is written in Google's Protocol Buffers domain specific language, and the โEXAMPLESโ section has the full protocol written out in Protocol Buffers format ๐
bestie, Protocol Buffers has no built-in way to tell where one message ends and the next begins ๐ญ So every message gets its wire size sent right before it, as a 32-bit unsigned integer in network byte order. Google calls this โlength-prefix framing,โ and it's literally how they suggest handling the lack of message delimiters ๐ซถ
The whole protocol is built on two messages, ClientMessage and ServerMessage, which we'll go through below ๐ฆ The server has to accept messages up to two megabytes in size โจ The server is allowed to send back an error if the client tries to send something bigger than two megabytes ๐
๐ Client Messages ๐
A ClientMessage is the container that wraps up every type of message a client can send to the server ๐
message ClientMessage {
oneof type {
AcceptMessage accept_msg = 1;
RejectMessage reject_msg = 2;
ExitMessage exit_msg = 3;
RestartMessage restart_msg = 4;
AlertMessage alert_msg = 5;
IoBuffer ttyin_buf = 6;
IoBuffer ttyout_buf = 7;
IoBuffer stdin_buf = 8;
IoBuffer stdout_buf = 9;
IoBuffer stderr_buf = 10;
ChangeWindowSize winsize_event = 11;
CommandSuspend suspend_event = 12;
ClientHello hello_msg = 13;
}
}
Here are the different ClientMessage sub-messages the client may send to the server, all explained down below ๐ง
โฑ๏ธ TimeSpec
message TimeSpec {
int64 tv_sec = 1;
int32 tv_nsec = 2;
}
A TimeSpec is basically the same thing as a POSIX struct timespec, with seconds and nanoseconds members ๐ฐ๏ธ The tv_sec member is a 64-bit integer, which means it can handle dates after the year 2038 ๐๐
๐ InfoMessage
message InfoMessage {
message StringList {
repeated string strings = 1;
}
message NumberList {
repeated int64 numbers = 1;
}
string key = 1;
oneof value {
int64 numval = 2;
string strval = 3;
StringList strlistval = 4;
NumberList numlistval = 5;
}
}
An InfoMessage holds info about the user who invoked the command, plus the environment the command runs in, all stored as key-value pairs ๐ The key is always a string. The value can be a 64-bit integer, a string, an array of strings, or an array of 64-bit integers ๐ The event log data is made up of InfoMessage entries. Check out the โEVENT LOG VARIABLESโ section for more deets ๐ฅ
๐ค ClientHello hello_msg
message ClientHello {
string client_id = 1;
}
A ClientHello message carries client information that can be sent to the server the moment the client first connects ๐
client_id : A free-form client description. This usually includes the name and version of the client implementation โจ
Hmm, that label line above is just the field name; see the bullets below for the same info in a cleaner way:
client_idโ a free-form client description ๐ This usually includes the name and version of the client implementation ๐
โ AcceptMessage accept_msg
message AcceptMessage {
TimeSpec submit_time = 1;
repeated InfoMessage info_msgs = 2;
bool expect_iobufs = 3;
}
An AcceptMessage gets sent by the client when the security policy says a command is allowed ๐ฅณ It has these members:
submit_timeโ the wall clock time when the command was submitted to the security policy โฐinfo_msgsโ an array of InfoMessage describing the user who submitted the command plus the command's execution environment ๐งพ This info is used to make an event log entry, and the server might also use it to figure out where and how the I/O log gets stored ๐๏ธexpect_iobufsโ set totrueif the server should expect IoBuffer messages to follow (for I/O logging), orfalseif the server should only store the event log ๐ซง
bestie, if an AcceptMessage is sent, the client must NOT send a RejectMessage or RestartMessage ๐ซ๐
๐ RejectMessage reject_msg
message RejectMessage {
TimeSpec submit_time = 1;
string reason = 2;
repeated InfoMessage info_msgs = 3;
}
A RejectMessage gets sent by the client when the security policy denies a command ๐ค It has these members:
submit_timeโ the wall clock time when the command was submitted to the security policy โฐreasonโ the reason the security policy gave for denying the command ๐info_msgsโ an array of InfoMessage describing the user who submitted the command plus the command's execution environment ๐งพ This info is used to make an event log entry.
if a RejectMessage is sent, the client must NOT send an AcceptMessage or RestartMessage ๐ โโ๏ธ
๐ ExitMessage exit_msg
message ExitMessage {
TimeSpec run_time = 1;
int32 exit_value = 2;
bool dumped_core = 3;
string signal = 4;
string error = 5;
}
An ExitMessage gets sent by the client once the command has exited or been killed by a signal ๐ชฆโจ It has these members:
run_timeโ the total elapsed time since the command started, measured with a monotonic clock where possible โณ This is NOT the wall clock time.exit_valueโ the command's exit value, in the range 0โ255 ๐ขdumped_coreโtrueif the command was terminated by a signal and dumped core ๐ฅsignalโ if the command was terminated by a signal, this is set to the name of the signal without the leading โSIGโ. For example: INT, TERM, KILL, SEGV ๐ชerrorโ a message from the client saying the command was terminated unexpectedly because of an error ๐ญ
When doing I/O logging, the client should wait for a commit_point that matches the final IoBuffer before closing the connection, unless the final commit_point has already been received ๐ซถ Don't dip early, bestie ๐
๐ RestartMessage restart_msg
message RestartMessage {
string log_id = 1;
TimeSpec resume_point = 2;
}
A RestartMessage gets sent by the client to pick up sending an existing I/O log that got interrupted earlier ๐ It has these members:
log_idโ the server-side name for an I/O log that the server previously sent to the client ๐ท๏ธ This might be a path name on the server, or some other kind of server-side identifier.resume_pointโ the point in time to resume the I/O log after ๐ฐ๏ธ This is a TimeSpec showing how much time has passed since the command started, NOT the wall clock time. The resume_point should match a commit_point the server already sent to the client. If the server gets a RestartMessage with a resume_point it has never seen before, an error gets returned to the client and the connection gets dropped ๐ต
if a RestartMessage is sent, the client must NOT send an AcceptMessage or RejectMessage ๐ซ
๐จ AlertMessage alert_msg
message AlertMessage {
TimeSpec alert_time = 1;
string reason = 2;
repeated InfoMessage info_msgs = 3;
}
An AlertMessage gets sent by the client to flag a problem the security policy spotted while the command was running, one that should be saved in the event log ๐ It has these members:
alert_timeโ the wall clock time when the alert happened โฐreasonโ the reason for the alert ๐ฃinfo_msgsโ an optional array of InfoMessage describing the user who submitted the command plus the command's execution environment ๐งพ This info is used to make an event log entry.
๐๏ธ IoBuffer ttyin_buf | ttyout_buf | stdin_buf | stdout_buf | stderr_buf
message IoBuffer {
TimeSpec delay = 1;
bytes data = 2;
}
An IoBuffer represents data from terminal input, terminal output, standard input, standard output, or standard error ๐ฅ๏ธ It has these members:
delayโ the elapsed time since the last record, as a TimeSpec โฑ๏ธ The delay should be calculated with a monotonic clock where possible.dataโ the binary I/O log data from terminal input, terminal output, standard input, standard output, or standard error ๐ซง
๐ ChangeWindowSize winsize_event
message ChangeWindowSize {
TimeSpec delay = 1;
int32 rows = 2;
int32 cols = 3;
}
A ChangeWindowSize message gets sent by the client whenever the terminal running the command changes size ๐ช It has these members:
delayโ the elapsed time since the last record, as a TimeSpec โฑ๏ธ The delay should be calculated with a monotonic clock where possible.rowsโ the new number of terminal rows ๐colsโ the new number of terminal columns ๐
โธ๏ธ CommandSuspend suspend_event
message CommandSuspend {
TimeSpec delay = 1;
string signal = 2;
}
A CommandSuspend message gets sent by the client when the command is either suspended or resumed ๐ซ It has these members:
delayโ the elapsed time since the last record, as a TimeSpec โฑ๏ธ The delay should be calculated with a monotonic clock where possible.signalโ the signal name without the leading โSIGโ. For example: STOP, TSTP, CONT ๐ฆ
โจ Server Messages โจ
okay so a ServerMessage is the container that wraps up every single message type the server can send to a client ๐
message ServerMessage {
oneof type {
ServerHello hello = 1;
TimeSpec commit_point = 2;
string log_id = 3;
string error = 4;
string abort = 5;
}
}
the sub-messages the server can send to the client are broken down below, so let's go ๐ง
ServerHello hello
message ServerHello {
string server_id = 1;
string redirect = 2;
repeated string servers = 3;
bool subcommands = 4;
}
this is the server info that shows up the moment a client first connects ๐ it has these members:
server_idโ a free-form server description, usually the name and version of the implementation running on the log server ๐ this one is always there, no exceptionsredirectโ a host and port separated by a colon (โโ), which the client should connect to instead ๐ฆ the host can be a host name, an IPv4 address, or an IPv6 address in square brackets. this is how server load balancing can happen โจ and heads up: the server disconnects right after sending the ServerHello when it includes a redirect ๐serversโ a list of other known log servers ๐ธ this is what makes log server redundancy work, and it lets the client find every other log server just by connecting to one it already knows. you can leave this out when there's only a single log serversubcommandsโ if this is set, the server supports logging extra commands during a session ๐ฅ the client may send anAcceptMessageorRejectMessagewhen sudo is running in intercept mode. in that mode, commands spawned from the initial command sudo authorized are subject to policy restrictions and/or get logged. ifsubcommandsis false, the client must not try to log additional commands ๐
TimeSpec commit_point
a periodic timestamp the server sends to say when I/O log buffers have been committed to storage ๐ฐ๏ธ it's not sent after every single IoBuffer. it shows up at an interval the server configures itself โญ when the server gets an ExitMessage, it replies with a commit_point that matches the last IoBuffer it received, right before it closes the connection ๐
string log_id
the server-side ID of the I/O log being stored ๐๏ธ it gets sent back in response to an AcceptMessage where expect_iobufs is true.
string error
a fatal server-side error ๐ญ the server closes the connection right after sending it.
string abort
an abort message basically tells the client to kill the command and end the session ๐ the server can use it to enforce a bit of simple server-side policy. the server closes the connection right after sending it.
โจ Protocol flow of control โจ
this is the order things are supposed to happen in, bestie ๐
- The client connects to the first available server ๐ if the client is set up to use TLS, a TLS handshake gets attempted.
- The client sends ClientHello ๐ this is currently optional, but it lets the server spot a non-TLS connection on the TLS port.
- The server sends ServerHello ๐
- The client replies with either AcceptMessage, RejectMessage, or RestartMessage ๐ซถ
- If the client sent an AcceptMessage with
expect_iobufsset, the server creates a new I/O log and replies with a log_id ๐ - The client sends zero or more IoBuffer messages ๐
- The server periodically replies to IoBuffer messages with a commit_point โฐ
- The client sends an ExitMessage when the command exits or gets killed ๐
- The server sends the final commit_point if one is still pending โจ
- The server closes the connection. After receiving the final commit_point, the client shuts down its side of the TLS connection (if TLS is in use) and closes the connection ๐
- The server shuts down its side of the TLS connection (if TLS is in use) and closes the connection ๐
at any point, the server may send an error or abort message to the client, and then the server closes the connection ๐ฌ if the client gets an abort message, it should terminate the running command. no drama, just do it ๐
โจ EVENT LOG VARIABLES โจ
the AcceptMessage, AlertMessage and RejectMessage classes each contain an array of InfoMessage. that array should hold info about the user who submitted the command, plus info about the execution environment of the command if it was accepted ๐
some variables have a client, run, or submit prefix ๐งโโ๏ธ these prefixes clear up confusion for variables that could apply to the client program, the user submitting the command, or the command being run. variables with a client prefix are about the program making the connection to the log server, like sudo. variables with a run prefix are about the command the user asked to run. variables with a submit prefix are about the user submitting the request (the user running sudo) ๐
the following InfoMessage entries are required ๐
command(string) โ the command that was submittedrunuser(string) โ the name of the user the command was run assubmithost(string) โ the name of the host the command was submitted onsubmituser(string) โ the name of the user submitting the command
the following InfoMessage entries are recognized, but not required ๐ธ
clientargv(StringList) โ the client's original argument vectorclientpid(int64) โ the client's process IDclientppid(int64) โ the client's parent process IDclientsid(int64) โ the client's terminal session IDcolumns(int64) โ the number of columns in the terminallines(int64) โ the number of lines in the terminalrunargv(StringList) โ the argument vector of the command to runrunchroot(string) โ the root directory of the command to runruncwd(string) โ the running command's working directoryrunenv(StringList) โ the running command's environmentrungid(int64) โ the primary group-ID of the commandrungids(NumberList) โ the supplementary group-IDs for the commandrungroup(string) โ the primary group name of the commandrungroups(StringList) โ the supplementary group names for the commandrunuid(int64) โ the run user's user-IDsubmitcwd(string) โ the submit user's current working directorysubmitenv(StringList) โ the submit user's environmentsubmitgid(int64) โ the submit user's primary group-IDsubmitgids(NumberList) โ the submit user's supplementary group-IDssubmitgroup(string) โ the submitting user's primary group namesubmitgroups(StringList) โ the submit user's supplementary group namessubmituid(int64) โ the submit user's user-IDttyname(string) โ the terminal the command was submitted from
the server must accept other variables that aren't listed above, but it's allowed to ignore them ๐ซถ
โจ EXAMPLES โจ
okay so the whole Protocol Buffers description of the log server protocol is right here, written in proto3 syntax, and it's all included below ๐ no skipping, bestie ๐ซถ
syntax = "proto3";
/*
* Client message to the server. Messages on the wire are
* prefixed with a 32-bit size in network byte order.
*/
message ClientMessage {
oneof type {
AcceptMessage accept_msg = 1;
RejectMessage reject_msg = 2;
ExitMessage exit_msg = 3;
RestartMessage restart_msg = 4;
AlertMessage alert_msg = 5;
IoBuffer ttyin_buf = 6;
IoBuffer ttyout_buf = 7;
IoBuffer stdin_buf = 8;
IoBuffer stdout_buf = 9;
IoBuffer stderr_buf = 10;
ChangeWindowSize winsize_event = 11;
CommandSuspend suspend_event = 12;
}
}
/* Equivalent of POSIX struct timespec */
message TimeSpec {
int64 tv_sec = 1; /* seconds */
int32 tv_nsec = 2; /* nanoseconds */
}
/* I/O buffer with keystroke data */
message IoBuffer {
TimeSpec delay = 1; /* elapsed time since last record */
bytes data = 2; /* keystroke data */
}
/*
* Key/value pairs, like Privilege Manager struct info.
* The value may be a number, a string, or a list of strings.
*/
message InfoMessage {
message StringList {
repeated string strings = 1;
}
message NumberList {
repeated int64 numbers = 1;
}
string key = 1;
oneof value {
int64 numval = 2;
string strval = 3;
StringList strlistval = 4;
NumberList numlistval = 5;
}
}
/*
* Event log data for command accepted by the policy.
*/
message AcceptMessage {
TimeSpec submit_time = 1; /* when command was submitted */
repeated InfoMessage info_msgs = 2; /* key,value event log data */
bool expect_iobufs = 3; /* true if I/O logging enabled */
}
/*
* Event log data for command rejected by the policy.
*/
message RejectMessage {
TimeSpec submit_time = 1; /* when command was submitted */
string reason = 2; /* reason command was rejected */
repeated InfoMessage info_msgs = 3; /* key,value event log data */
}
/* Message sent by client when command exits. */
/* Might revisit runtime and use end_time instead */
message ExitMessage {
TimeSpec run_time = 1; /* total elapsed run time */
int32 exit_value = 2; /* 0โ255 */
bool dumped_core = 3; /* true if command dumped core */
string signal = 4; /* signal name if killed by signal */
string error = 5; /* if killed due to other error */
}
/* Alert message, policy module-specific. */
message AlertMessage {
TimeSpec alert_time = 1; /* time alert message occurred */
string reason = 2; /* policy alert error string */
repeated InfoMessage info_msgs = 3; /* key,value event log data */
}
/* Used to restart an existing I/O log on the server. */
message RestartMessage {
string log_id = 1; /* ID of log being restarted */
TimeSpec resume_point = 2; /* resume point (elapsed time) */
}
/* Window size change event. */
message ChangeWindowSize {
TimeSpec delay = 1; /* elapsed time since last record */
int32 rows = 2; /* new number of rows */
int32 cols = 3; /* new number of columns */
}
/* Command suspend/resume event. */
message CommandSuspend {
TimeSpec delay = 1; /* elapsed time since last record */
string signal = 2; /* signal that caused suspend/resume */
}
/*
* Server messages to the client. Messages on the wire are
* prefixed with a 32-bit size in network byte order.
*/
message ServerMessage {
oneof type {
ServerHello hello = 1; /* server hello message */
TimeSpec commit_point = 2; /* cumulative time of records stored */
string log_id = 3; /* ID of server-side I/O log */
string error = 4; /* error message from server */
string abort = 5; /* abort message, kill command */
}
}
/* Hello message from server when client connects. */
message ServerHello {
string server_id = 1; /* free-form server description */
string redirect = 2; /* optional redirect if busy */
repeated string servers = 3; /* optional list of known servers */
}
โจ SEE ALSO โจ
ily bestie, go check these out too ๐ฆ
Protocol Buffers lives at https://protobuf.dev/ ๐ง
โจ AUTHORS โจ
so a whole bunch of people have worked on sudo over the years ๐ this version is mostly code written by:
Todd C. Miller ๐
for the full list of everyone who's contributed (and I mean EVERYONE ๐ญ), check out the CONTRIBUTORS.md file in the sudo distribution, or https://www.sudo.ws/about/contributors/ ๐ธ
โจ BUGS โจ
if you think you found a bug in sudo_logsrv.proto, you can file a report in the sudo bug database at https://bugzilla.sudo.ws/, or open an issue at https://github.com/sudo-project/sudo/issues ๐ if email is more your thing, you can send messages to the sudo-workers mailing list, https://www.sudo.ws/mailman/listinfo/sudo-workers (public), or to <sudo@sudo.ws> (private) ๐
โ ๏ธ please do NOT report security vulnerabilities through public GitHub issues, Bugzilla, or mailing lists ๐ instead, email them to <Todd.Miller@sudo.ws>. you're welcome to encrypt your message with PGP if you want, using the key found at https://www.sudo.ws/dist/PGPKEYS ๐
โจ SUPPORT โจ
limited free support is available through the sudo-users mailing list ๐ subscribe or search the archives at https://www.sudo.ws/mailman/listinfo/sudo-users โจ
โจ DISCLAIMER โจ
sudo is provided โAS ISโ and any express or implied warranties, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose are disclaimed ๐ญ for all the details, check the LICENSE.md file distributed with sudo, or https://www.sudo.ws/about/license/ ๐
that's a wrap on SUDO_LOGSRV.PROTO(5), from Sudo 1.9.17p2, September 13, 2022 ๐ you're literally a whole main character now ๐งโจ
๐ฆ where this page comes from
- alpine 3.24: sudo-doc 1.9.17_p2-r2 ยท custom ISC
- arch rolling: sudo 1.9.17.p2-6 ยท custom
- fedora 43: sudo-logsrvd 1.9.17-7.p2.fc43 ยท ISC
- fedora 44: sudo-logsrvd 1.9.17-10.p2.fc44 ยท ISC
- fedora 44: sudo-logsrvd 1.9.17-7.p2.fc44 ยท ISC
- nixos 26.05: sudo 1.9.17p2
- nixpkgs unstable: sudo 1.9.17p2
- ubuntu questing: sudo 1.9.17p2-1ubuntu1
- ubuntu questing: sudo 1.9.17p2-1ubuntu1.1 ยท ISC
- ubuntu resolute: sudo 1.9.17p2-1ubuntu3 ยท ISC
- ubuntu resolute: sudo 1.9.17p2-1ubuntu3.2 ยท ISC
- ubuntu stonking: sudo 1.9.17p2-7ubuntu5 ยท ISC
- ๐ the page's license: ISC
- Copyright (c) 2019-2022 Todd C. Miller <Todd.Miller@sudo.ws>
- ๐ girlified by anthropic/claude-haiku-5.5 on : a language model's rewrite of the original, under its terms