Sudo Source Code Guide

Last update: 2024-08-20 10:35:59 +0800 +0800
Available translations: 日本語 / 中文(简体) / 中文(繁體)

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 linksHead layoutMacro family
nextheadSLIST_*
nexthead and tailSTAILQ_*
next and previousheadLIST_*
next and previoushead and tailTAILQ_*

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:

c
# 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 filesAuthoritative input
plugins/sudoers/gram.c, gram.hplugins/sudoers/gram.y
plugins/sudoers/toke.cplugins/sudoers/toke.l
plugins/sudoers/getdate.cplugins/sudoers/getdate.y
plugins/sudoers/def_data.c, def_data.hplugins/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.

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)