The sudo source tree separates front-end programs, plugin implementations, reusable libraries, generated language parsers, and portability support. Start from an executable’s entry point, then follow the plugin interface; reading the tree by directory name alone obscures the policy boundary.
Front Ends and Command Flow #
src/sudo.c is the sudo front end. Its early path establishes invocation details, parses command-line options, initializes configuration and logging, and hands authorization and execution decisions to the configured plugins. The front end is deliberately distinct from policy: the policy plugin decides whether a command is allowed, while the I/O plugin can record or mediate execution.
Other entry points include visudo, cvtsudoers, sudoreplay, and testsudoers. Their source lives with shared front-end code rather than in isolated program directories. Identify the executable target in the local Makefile.in before tracing a call, because a file in src/ is not necessarily linked into every binary.
include/sudo_plugin.h defines the boundary between the front end and plugins. The default policy implementation is under plugins/sudoers/; that tree contains sudoers parsing, matching, policy decisions, logging, and associated plugin hooks. I/O logging and replay code are also plugin-oriented concerns. Porting or policy work should begin at these interfaces, not by changing front-end privilege handling.
Queue Macros #
include/sudo_queue.h carries BSD-style intrusive queue macros. A node embeds an entry link, while a head stores the first element and, for tail queues, a tail reference. The major families differ by linkage and whether the head tracks a tail:
| Node links | Head layout | Macro family |
|---|---|---|
| next | head | SLIST_* |
| next | head and tail | STAILQ_* |
| next and previous | head | LIST_* |
| next and previous | head and tail | TAILQ_* |
The macros generate no allocation, locking, lifetime management, or type-safe iterator object. Code using a queue must keep embedded elements alive and protect concurrent mutation. Prefer the local header’s macros and semantics over a host <sys/queue.h> because BSD-derived headers have diverged.
Libraries and Portability #
lib/ contains reusable components consumed across front ends and plugins, while lib/util/ supplies low-level utility and compatibility routines. The latter includes wrappers, bounded string and path handling, allocation helpers, logging support, reference-counted strings, and fallbacks for interfaces that a target platform lacks. Public declarations in include/ make the reuse boundary visible; the sudo_ prefix often identifies project-owned wrappers:
# define getdelim(_a, _b, _c, _d) sudo_getdelim((_a), (_b), (_c), (_d))Do not assume every sudo_ function is a simple alias. A wrapper can supply fallback semantics, input validation, or a platform workaround. Trace its declaration and implementation before substituting a native libc call.
Autoconf probes in configure.ac and m4/ select available platform features. That is the portability boundary for headers, functions, privileges, and operating-system behavior; #ifdef branches in a caller are often consequences of those generated configuration results.
Generated Parsers and Configuration Data #
The sudoers plugin checks in generated outputs so release builds do not require parser generators. Follow source-to-output relationships instead of editing generated files:
| Generated files | Authoritative input |
|---|---|
plugins/sudoers/gram.c, gram.h | plugins/sudoers/gram.y |
plugins/sudoers/toke.c | plugins/sudoers/toke.l |
plugins/sudoers/getdate.c | plugins/sudoers/getdate.y |
plugins/sudoers/def_data.c, def_data.h | plugins/sudoers/def_data.in |
The build’s developer or regeneration rules run the configured parser, lexer, and data-generation tools. Regenerate through those rules after changing .y, .l, or .in inputs; otherwise a source checkout can contain inputs and outputs that disagree. Parser symbol prefixes and regeneration prerequisites are build configuration details, so inspect the relevant Makefile.in and configure output rather than invoking a generator with guessed flags.
Navigation Strategy #
For a policy question, begin in plugins/sudoers/ and follow the plugin callbacks declared in include/sudo_plugin.h. For command launch or option handling, begin in the appropriate src/ entry point. For I/O recording, follow the I/O plugin interfaces and sudoreplay reader path. For an operating-system discrepancy, search lib/util/, m4/, configure.ac, and generated configuration headers.
The upstream sudo source tree and its checked-in build files are the authoritative map because generated names and plugin composition change between releases.
Comments
Read or comment on GitHub (sign-in required to comment)