1SUDO_PLUGIN_PYTHON(5) BSD File Formats Manual SUDO_PLUGIN_PYTHON(5)
2
4 sudo_plugin_python — Sudo Plugin API (Python)
5
7 Starting with version 1.9, sudo plugins can be written in python. The
8 API closely follows the C sudo plugin API described by sudo_plugin(5).
9
10 The supported plugins types are:
11
12 • Policy plugin
13 • I/O plugin
14 • Audit plugin
15 • Approval plugin
16 • Group provider plugin
17
18 Python plugin support needs to be explicitly enabled at build time with
19 the configure option “--enable-python”. Python version 3.0 or higher is
20 required.
21
22 Sudo Python Plugin Base
23 A plugin written in Python should be a class in a python file that inher‐
24 its from sudo.Plugin. The sudo.Plugin base class has no real purpose
25 other than to identify this class as a plugin.
26
27 The only implemented method is a constructor, which stores the keyword
28 arguments it receives as fields (member variables) in the object. This
29 is intended as a convenience to allow you to avoid writing the construc‐
30 tor yourself.
31
32 For example:
33
34 import sudo
35
36 class MySudoPlugin(sudo.Plugin):
37 # example constructor (optional)
38 def __init__(self, *args, **kwargs):
39 super().__init__(*args, **kwargs)
40
41 # example destructor (optional)
42 def __del__(self):
43 pass
44
45 Both the constructor and destructor are optional and can be omitted.
46
47 The customized Plugin class should define a few plugin-specific methods.
48 When the plugin loads, sudo will create an instance of this class and
49 call the methods. The actual methods required depent on the type of the
50 plugin, but most return an int result code, as documented in
51 sudo_plugin(5), that indicates whether or not the method was successful.
52 The Python sudo module defines the following constants to improve read‐
53 ability:
54
55 Define Value
56 sudo.RC.OK 1
57 sudo.RC.ACCEPT 1
58 sudo.RC.REJECT 0
59 sudo.RC.ERROR -1
60 sudo.RC.USAGE_ERROR -2
61
62 If a function returns None (for example, if it does not call return), it
63 will be considered to have returned sudo.RC.OK. If an exception is
64 raised (other than sudo.PluginException), the backtrace will be shown to
65 the user and the plugin function will return sudo.RC.ERROR. If that is
66 not acceptable, you must catch the exception and handle it yourself.
67
68 Instead of just returning sudo.RC.ERROR or sudo.RC.REJECT result code the
69 plugin can also provide a message describing the problem. This can be
70 done by raising one of the special exceptions:
71
72 raise sudo.PluginError("Message")
73 raise sudo.PluginReject("Message")
74
75 This added message will be used by the audit plugins. Both exceptions
76 inherit from sudo.PluginException
77
78 Python Plugin Loader
79 Running the Python interpreter and bridging between C and Python is han‐
80 dled by the sudo plugin python_plugin.so. This shared object can be
81 loaded like any other dynamic sudo plugin and should receive the path and
82 the class name of the Python plugin it is loading as arguments.
83
84 Example usage in sudo.conf(5):
85
86 Plugin python_policy python_plugin.so ModulePath=<path> ClassName=<class>
87 Plugin python_io python_plugin.so ModulePath=<path> ClassName=<class>
88 Plugin python_audit python_plugin.so ModulePath=<path> ClassName=<class>
89 Plugin python_approval python_plugin.so ModulePath=<path> ClassName=<class>
90
91 Example group provider plugin usage in the sudoers file:
92
93 Defaults group_plugin="python_plugin.so ModulePath=<path> ClassName=<class>"
94
95 The plugin arguments are as follows:
96
97 ModulePath
98 The path of a python file which contains the class of the sudo
99 Python plugin. It must be either an absolute path or a path rela‐
100 tive to the sudo Python plugin directory, /usr/libexec/sudo/python.
101 The parent directory of ModulePath will be appended to Python's
102 module search path (there is currently no way to force Python to
103 load a module from a fully-qualified path). It is good practice to
104 use a prefix for the module file that is unlikely to conflict with
105 other installed Python modules, for example, sudo_policy.py. Oth‐
106 erwise, if the there is an installed Python module with the same
107 file name as the sudo Python plugin file (without the directory),
108 the wrong file will be loaded.
109
110 ClassName
111 (Optional.) The name of the class implementing the sudo Python
112 plugin. If not supplied, the one and only sudo.Plugin that is
113 present in the module will be used. If there are multiple such
114 plugins in the module (or none), it will result in an error.
115
116 Policy plugin API
117 Policy plugins must be registered in sudo.conf(5). For example:
118
119 Plugin python_policy python_plugin.so ModulePath=<path> ClassName=<class>
120
121 Currently, only a single policy plugin may be specified in sudo.conf(5).
122
123 A policy plugin may have the following member functions:
124
125 constructor
126 __init__(self, user_env: Tuple[str, ...], settings: Tuple[str, ...],
127 version: str, user_info: Tuple[str, ...],
128 plugin_options: Tuple[str, ...])
129
130 Implementing this function is optional. The default constructor
131 will set the keyword arguments it receives as member variables in
132 the object.
133
134 The constructor matches the open() function in the C sudo plugin
135 API.
136
137 The function arguments are as follows:
138
139 user_env
140 The user's environment as a tuple of strings in “key=value”
141 format.
142
143 settings
144 A tuple of user-supplied sudo settings in the form of
145 “key=value” strings.
146
147 version
148 The version of the Python Policy Plugin API.
149
150 user_info
151 A tuple of information about the user running the command in
152 the form of “key=value” strings.
153
154 plugin_options
155 The plugin options passed as arguments in the sudo.conf(5)
156 plugin registration. This is a tuple of strings, usually
157 (but not necessarily) in “key=value” format.
158
159 The sudo.options_as_dict() convenience function can be used to con‐
160 vert “key=value” pairs to a dictionary. For a list of recognized
161 keys and their supported values, see the policy plugin open() docu‐
162 mentation in sudo_plugin(5).
163
164 check_policy
165 check_policy(self, argv: Tuple[str, ...], env_add: Tuple[str, ...])
166
167 The check_policy() function is called by sudo to determine whether
168 the user is allowed to run the specified command. Implementing
169 this function is mandatory for a policy plugin.
170
171 The function arguments are as follows:
172
173 argv A tuple describing the command the user wishes to run.
174
175 env_add
176 Additional environment variables specified by the user on the
177 command line in the form of a tuple of “key=value” pairs.
178 The sudo.options_as_dict() convenience function can be used
179 to convert them to a dictionary.
180
181 This function should return a result code or a tuple in the follow‐
182 ing format:
183
184 return (rc, command_info_out, argv_out, user_env_out)
185
186 The tuple values are as follows:
187
188 rc The result of the policy check, one of the sudo.RC.* con‐
189 stants. sudo.RC.ACCEPT if the command is allowed,
190 sudo.RC.REJECT if not allowed, sudo.RC.ERROR for a general
191 error, or sudo.RC.USAGE_ERROR for a usage error.
192
193 command_info_out
194 Optional (only required when the command is accepted). In‐
195 formation about the command being run in the form of
196 “key=value” strings.
197
198 To accept a command, at the very minimum the plugin must set
199 in the command, runas_uid, and runas_gid keys.
200
201 For a list of recognized keys and supported values, see the
202 check_policy() documentation in sudo_plugin(5).
203
204 argv_out
205 Optional (only required when the command is accepted). The
206 arguments to pass to the execve(2) system call when executing
207 the command.
208
209 user_env_out
210 Optional (only required when the command is accepted). The
211 environment to use when executing the command in the form of
212 a tuple of strings in “key=value” format.
213
214 init_session
215 init_session(self, user_pwd: Tuple, user_env: Tuple[str, ...])
216
217 Perform session setup (optional). The init_session() function is
218 called before sudo sets up the execution environment for the com‐
219 mand before any user-ID or group-ID changes.
220
221 The function arguments are as follows:
222
223 user_pwd
224 A tuple describing the user's passwd entry. Convertible to
225 pwd.struct_passwd or None if the user is not present in the
226 password database.
227
228 Example conversion:
229 user_pwd = pwd.struct_passwd(user_pwd) if user_pwd else None
230
231 user_env
232 The environment the command will run in. This is a tuple of
233 strings in “key=value” format.
234
235 This function should return a result code or a tuple in the follow‐
236 ing format:
237
238 return (rc, user_env_out)
239
240 The tuple values are as follows:
241
242 rc The result of the session init, one of the sudo.RC.* con‐
243 stants. sudo.RC.OK on success, 0 on failure, or
244 sudo.RC.ERROR if an error occurred.
245
246 user_env_out
247 Optional. If the init_session() function needs to modify the
248 user environment, it can return the new environment in
249 user_env_out. If this is omitted, no changes will be made to
250 user_env.
251
252 list
253 list(self, argv: Tuple[str, ...], is_verbose: int, user: str)
254
255 List available privileges for the invoking user.
256
257 The function arguments are as follows:
258
259 argv If not set to None, an argument vector describing a command
260 the user wishes to check against the policy.
261
262 is_verbose
263 Flag indicating whether to list in verbose mode or not.
264
265 user The name of a different user to list privileges for if the
266 policy allows it. If None, the plugin should list the privi‐
267 leges of the invoking user.
268
269 validate
270 validate(self)
271
272 For policy plugins that cache authentication credentials, this
273 function is used to validate and cache the credentials (optional).
274
275 invalidate
276 invalidate(self, remove: int)
277
278 For policy plugins that cache authentication credentials, this
279 function is used to invalidate the credentials (optional).
280
281 The function arguments are as follows:
282
283 remove
284 If this flag is set, the plugin may remove the credentials
285 instead of simply invalidating them.
286
287 show_version
288 show_version(self, is_verbose: int)
289
290 Display the plugin version information to the user. The
291 sudo.log_info() function should be used.
292
293 The function arguments are as follows:
294
295 is_verbose
296 A flag to indicate displaying more verbose information. Cur‐
297 rently this is 1 if ‘sudo -V’ is run as the root user.
298
299 close
300 close(self, exit_status: int, error: int)
301
302 Called when a command finishes executing.
303
304 Works the same as the close() function in the C sudo plugin API,
305 except that it only gets called if sudo attempts to execute the
306 command.
307
308 The function arguments are as follows:
309
310 exit_status
311 The exit status of the command if was executed, otherwise -1.
312
313 error
314 If the command could not be executed, this is set to the
315 value of errno set by the execve(2) system call, otherwise 0.
316
317 Policy plugin example
318 Sudo ships with an example Python policy plugin. To try it, register it
319 by adding the following lines to /etc/sudo.conf:
320
321 Plugin python_policy python_plugin.so \
322 ModulePath=/usr/share/doc/sudo/examples/example_policy_plugin.py \
323 ClassName=SudoPolicyPlugin
324
325 Only one policy plugin can be enabled at a time so you must disable any
326 other policy plugin listed in /etc/sudo.conf, such as sudoers(5).
327
328 I/O plugin API
329 I/O plugins must be registered in sudo.conf(5). For example:
330
331 Plugin python_io python_plugin.so ModulePath=<path> ClassName=<class>
332
333 Sudo supports loading multiple I/O plugins. Currently only 8 python I/O
334 plugins can be loaded at once.
335
336 An I/O plugin may have the following member functions:
337
338 constructor
339 __init__(self, user_env: Tuple[str, ...], settings: Tuple[str, ...],
340 version: str, user_info: Tuple[str, ...],
341 plugin_options: Tuple[str, ...])
342
343 Implementing this function is optional. The default constructor
344 will set the keyword arguments it receives as member variables in
345 the object.
346
347 The constructor matches the open() function in the C sudo plugin
348 API.
349
350 The function arguments are as follows:
351
352 user_env
353 The user's environment as a tuple of strings in “key=value”
354 format.
355
356 settings
357 A tuple of user-supplied sudo settings in the form of
358 “key=value” strings.
359
360 version
361 The version of the Python I/O Plugin API.
362
363 user_info
364 A tuple of information about the user running the command in
365 the form of “key=value” strings.
366
367 plugin_options
368 The plugin options passed as arguments in the sudo.conf(5)
369 plugin registration. This is a tuple of strings, usually
370 (but not necessarily) in “key=value” format.
371
372 The sudo.options_as_dict() convenience function can be used to con‐
373 vert “key=value” pairs to a dictionary. For a list of recognized
374 keys and their supported values, see the I/O plugin open() documen‐
375 tation in sudo_plugin(5).
376
377 open
378 open(self, argv: Tuple[str, ...],
379 command_info: Tuple[str, ...]) -> int
380
381 Receives the command the user wishes to run.
382
383 Works the same as the open() function in the C sudo plugin API ex‐
384 cept that:
385
386 • It only gets called when there is a command to be executed
387 (and not for a version query for example).
388 • Other arguments of the C API open() function are received
389 through the constructor.
390
391 The function arguments are as follows:
392
393 argv A tuple of the arguments describing the command the user
394 wishes to run.
395
396 command_info
397 Information about the command being run in the form of
398 “key=value” strings.
399
400 The sudo.options_as_dict() convenience function can be used to con‐
401 vert “key=value” pairs to a dictionary. For a list of recognized
402 keys and their supported values, see the I/O plugin open() documen‐
403 tation in sudo_plugin(5).
404
405 The open() function should return a result code, one of the
406 sudo.RC.* constants. If the function returns sudo.RC.REJECT, no
407 I/O will be sent to the plugin.
408
409 log_ttyin, log_ttyout, log_stdin, log_stdout, log_stderr
410 log_ttyin(self, buf: str) -> int
411 log_ttyout(self, buf: str) -> int
412 log_stdin(self, buf: str) -> int
413 log_stdout(self, buf: str) -> int
414 log_stderr(self, buf: str) -> int
415
416 Receive the user input or output of the terminal device and appli‐
417 cation standard input, standard output, or standard error. See the
418 matching calls in sudo_plugin(5).
419
420 The function arguments are as follows:
421
422 buf The input (or output) buffer in the form of a string.
423
424 The function should return a result code, one of the sudo.RC.* con‐
425 stants.
426
427 If sudo.RC.ERROR is returned, the running command will be termi‐
428 nated and all of the plugin's logging functions will be disabled.
429 Other I/O logging plugins will still receive any remaining input or
430 output that has not yet been processed.
431
432 If an input logging function rejects the data by returning
433 sudo.RC.REJECT, the command will be terminated and the data will
434 not be passed to the command, though it will still be sent to any
435 other I/O logging plugins. If an output logging function rejects
436 the data by returning sudo.RC.REJECT, the command will be termi‐
437 nated and the data will not be written to the terminal, though it
438 will still be sent to any other I/O logging plugins.
439
440 change_winsize
441 change_winsize(self, line: int, cols: int) -> int
442
443 Called whenever the window size of the terminal changes. The func‐
444 tion arguments are as follows:
445
446 line The number of lines of the terminal.
447
448 cols The number of columns of the terminal.
449
450 log_suspend
451 log_suspend(self, signo: int) -> int
452 Called whenever a command is suspended or resumed.
453
454 The function arguments are as follows:
455
456 signo
457 The number of the signal that caused the command to be sus‐
458 pended or SIGCONT if the command was resumed.
459
460 show_version
461 show_version(self, is_verbose: int)
462 Display the plugin version information to the user. The
463 sudo.log_info() function should be used.
464
465 The function arguments are as follows:
466
467 is_verbose
468 A flag to indicate displaying more verbose information. Cur‐
469 rently this is 1 if ‘sudo -V’ is run as the root user.
470
471 close
472 close(self, exit_status: int, error: int) -> None
473 Called when a command finishes execution.
474
475 Works the same as the close() function in the C sudo plugin API,
476 except that it only gets called if sudo attempts to execute the
477 command.
478
479 The function arguments are as follows:
480
481 exit_status
482 The exit status of the command if was executed, otherwise -1.
483
484 error
485 If the command could not be executed, this is set to the
486 value of errno set by the execve(2) system call, otherwise 0.
487
488 I/O plugin example
489 Sudo ships with a Python I/O plugin example. To try it, register it by
490 adding the following lines to /etc/sudo.conf:
491
492 Plugin python_io python_plugin.so \
493 ModulePath=/usr/share/doc/sudo/examples/example_io_plugin.py \
494 ClassName=SudoIOPlugin
495
496 Audit plugin API
497 Audit plugins must be registered in sudo.conf(5). For example:
498
499 Plugin python_audit python_plugin.so ModulePath=<path> ClassName=<class>
500
501 Sudo supports loading multiple audit plugins. Currently only 8 python
502 audit plugins can be loaded at once.
503
504 An audit plugin may have the following member functions (all of which are
505 optional):
506
507 constructor
508 __init__(self, user_env: Tuple[str, ...], settings: Tuple[str, ...],
509 version: str, user_info: Tuple[str, ...], plugin_options: Tuple[str, ...])
510
511 The default constructor will set the keyword arguments it receives
512 as member variables in the object.
513
514 The constructor matches the open() function in the C sudo plugin
515 API.
516
517 The function arguments are as follows:
518
519 user_env
520 The user's environment as a tuple of strings in “key=value”
521 format.
522
523 settings
524 A tuple of user-supplied sudo settings in the form of
525 “key=value” strings.
526
527 version
528 The version of the Python Audit Plugin API.
529
530 user_info
531 A tuple of information about the user running the command in
532 the form of “key=value” strings.
533
534 plugin_options
535 The plugin options passed as arguments in the sudo.conf(5)
536 plugin registration. This is a tuple of strings, usually
537 (but not necessarily) in “key=value” format.
538
539 open
540 open(self, submit_optind: int,
541 submit_argv: Tuple[str, ...]) -> int
542
543 The function arguments are as follows:
544
545 submit_optind
546 The index into submit_argv that corresponds to the first en‐
547 try that is not a command line option.
548
549 submit_argv
550 The argument vector sudo was invoked with, including all com‐
551 mand line options.
552
553 close
554 close(self, status_type: int, status: int) -> None
555
556 Called when sudo is finished, shortly before it exits.
557
558 The function arguments are as follows:
559
560 status_type
561 The type of status being passed. One of the
562 sudo.EXIT_REASON.* constants.
563
564 status
565 Depending on the value of status_type, this value is either
566 ignored, the command's exit status as returned by the wait(2)
567 system call, the value of errno set by the execve(2) system
568 call, or the value of errno resulting from an error in the
569 sudo front-end.
570
571 show_version
572 show_version(self, is_verbose: int) -> int
573
574 Display the plugin version information to the user. The
575 sudo.log_info() function should be used.
576
577 The function arguments are as follows:
578
579 is_verbose
580 A flag to indicate displaying more verbose information. Cur‐
581 rently this is 1 if ‘sudo -V’ is run as the root user.
582
583 accept
584 accept(self, plugin_name: str, plugin_type: int, command_info: Tuple[str, ...],
585 run_argv: Tuple[str, ...], run_envp: Tuple[str, ...]) -> int
586
587 This function is called when a command or action is accepted by a
588 policy or approval plugin. The function arguments are as follows:
589
590 plugin_name
591 The name of the plugin that accepted the command or “sudo”
592 for the sudo front-end.
593
594 plugin_type
595 The type of plugin that accepted the command, currently ei‐
596 ther sudo.PLUGIN_TYPE.POLICY, sudo.PLUGIN_TYPE.APPROVAL, or
597 sudo.PLUGIN_TYPE.SUDO. The accept() function is called mul‐
598 tiple times--once for each policy or approval plugin that
599 succeeds and once for the sudo front-end. When called on be‐
600 half of the sudo front-end, command_info may include informa‐
601 tion from an I/O logging plugin as well.
602
603 Typically, an audit plugin is interested in either the accept
604 status from the sudo front-end or from the various policy and
605 approval plugins, but not both. It is possible for the pol‐
606 icy plugin to accept a command that is later rejected by an
607 approval plugin, in which case the audit plugin's accept()
608 and reject() functions will both be called.
609
610 command_info
611 A vector of information describing the command being run.
612 See the sudo_plugin(5) manual for possible values.
613
614 run_argv
615 Argument vector describing a command that will be run.
616
617 run_envp
618 The environment the command will be run with.
619
620 reject
621 reject(self, plugin_name: str, plugin_type: int, audit_msg: str,
622 command_info: Tuple[str, ...]) -> int
623
624 This function is called when a command or action is rejected by the
625 policy plugin. The function arguments are as follows:
626
627 plugin_name
628 The name of the plugin that rejected the command.
629
630 plugin_type
631 The type of plugin that rejected the command, currently ei‐
632 ther sudo.PLUGIN_TYPE.POLICY, sudo.PLUGIN_TYPE.APPROVAL, or
633 sudo.PLUGIN_TYPE.IO.
634
635 Unlike the accept() function, the reject() function is not
636 called on behalf of the sudo front-end.
637
638 audit_msg
639 An optional string describing the reason the command was re‐
640 jected by the plugin. If the plugin did not provide a rea‐
641 son, audit_msg will be None.
642
643 command_info
644 A vector of information describing the rejected command. See
645 the sudo_plugin(5) manual for possible values.
646
647 error
648 error(self, plugin_name: str, plugin_type: int, audit_msg: str,
649 command_info: Tuple[str, ...]) -> int
650
651 This function is called when a plugin or the sudo front-end returns
652 an error. The function arguments are as follows:
653
654 plugin_name
655 The name of the plugin that generated the error or “sudo” for
656 the sudo front-end.
657
658 plugin_type
659 The type of plugin that generated the error, or
660 SUDO_FRONT_END for the sudo front-end.
661
662 audit_msg
663 An optional string describing the plugin error. If the
664 plugin did not provide a description, it will be None.
665
666 command_info
667 A vector of information describing the command. See the
668 sudo_plugin(5) manual for possible values.
669
670 Audit plugin example
671 Sudo ships with a Python Audit plugin example. To try it, register it by
672 adding the following lines to /etc/sudo.conf:
673
674 Plugin python_audit python_plugin.so \
675 ModulePath=/usr/share/doc/sudo/examples/example_audit_plugin.py \
676 ClassName=SudoAuditPlugin
677
678 It will log the plugin accept / reject / error results to the output.
679
680 Approval plugin API
681 Approval plugins must be registered in sudo.conf(5). For example:
682
683 Plugin python_approval python_plugin.so ModulePath=<path> ClassName=<class>
684
685 Sudo supports loading multiple approval plugins. Currently only 8 python
686 approval plugins can be loaded at once.
687
688 An approval plugin may have the following member functions:
689
690 constructor
691 __init__(self, user_env: Tuple[str, ...], settings: Tuple[str, ...],
692 version: str, user_info: Tuple[str, ...], plugin_options: Tuple[str, ...],
693 submit_optind: int, submit_argv: Tuple[str, ...])
694
695 Optional. The default constructor will set the keyword arguments
696 it receives as member variables in the object.
697
698 The constructor matches the open() function in the C sudo plugin
699 API.
700
701 The function arguments are as follows:
702
703 user_env
704 The user's environment as a tuple of strings in “key=value”
705 format.
706
707 settings
708 A tuple of user-supplied sudo settings in the form of
709 “key=value” strings.
710
711 version
712 The version of the Python Approval Plugin API.
713
714 user_info
715 A tuple of information about the user running the command in
716 the form of “key=value” strings.
717
718 plugin_options
719 The plugin options passed as arguments in the sudo.conf(5)
720 plugin registration. This is a tuple of strings, usually
721 (but not necessarily) in “key=value” format.
722
723 submit_optind
724 The index into submit_argv that corresponds to the first en‐
725 try that is not a command line option.
726
727 submit_argv
728 The argument vector sudo was invoked with, including all com‐
729 mand line options.
730
731 show_version
732 show_version(self, is_verbose: int) -> int
733
734 Display the version. (Same as for all the other plugins.)
735
736 check
737 check(self, command_info: Tuple[str, ...], run_argv: Tuple[str, ...],
738 run_env: Tuple[str, ...]) -> int
739
740 This function is called after policy plugin's check_policy has suc‐
741 ceeded. It can reject execution of the command by returning
742 sudo.RC.REJECT or raising the special exception:
743
744 raise sudo.PluginReject("some message")
745
746 with the message describing the problem. In the latter case, the
747 audit plugins will get the description.
748
749 The function arguments are as follows:
750
751 command_info
752 A vector of information describing the command that will run.
753 See the sudo_plugin(5) manual for possible values.
754
755 run_argv
756 Argument vector describing a command that will be run.
757
758 run_env
759 The environment the command will be run with.
760
761 Approval plugin example
762 Sudo ships with a Python Approval plugin example. To try it, register it
763 by adding the following lines to /etc/sudo.conf:
764
765 Plugin python_approval python_plugin.so \
766 ModulePath=/usr/share/doc/sudo/examples/example_approval_plugin.py \
767 ClassName=BusinessHoursApprovalPlugin
768
769 It will only allow execution of commands in the "business hours" (from
770 Monday to Friday between 8:00 and 17:59:59).
771
772 Sudoers group provider plugin API
773 A group provider plugin is registered in the sudoers(5) file. For exam‐
774 ple:
775
776 Defaults group_plugin="python_plugin.so ModulePath=<path> ClassName=<class>"
777
778 Currently, only a single group plugin can be registered in sudoers.
779
780 A group provider plugin may have the following member functions:
781
782 constructor
783 __init__(self, args: Tuple[str, ...], version: str)
784
785 Implementing this function is optional. The default constructor
786 will set the keyword arguments it receives as member variables in
787 the object.
788
789 The function arguments are as follows:
790
791 args The plugin options passed as arguments in the sudoers file
792 plugin registration. All the arguments are free form strings
793 (not necessarily in “key=value” format).
794
795 version
796 The version of the Python Group Plugin API.
797
798 query
799 query(self, user: str, group: str, user_pwd: Tuple)
800
801 The query() function is used to ask the group plugin whether user
802 is a member of group. This method is required.
803
804 The function arguments are as follows:
805
806 user The name of the user being looked up in the external group data‐
807 base.
808
809 group
810 The name of the group being queried.
811
812 user_pwd
813 The password database entry for the user, if any. If user is not
814 present in the password database, user_pwd will be NULL.
815
816 Group plugin example
817 Sudo ships with a Python group plugin example. To try it, register it in
818 the sudoers file by adding the following lines:
819
820 Defaults group_plugin="python_plugin.so \
821 ModulePath=/usr/share/doc/sudo/examples/example_group_plugin.py \
822 ClassName=SudoGroupPlugin"
823
824 The example plugin will tell sudo that the user test is part of the non-
825 Unix group mygroup. If you add a rule that uses this group, it will af‐
826 fect the test user. For example:
827
828 %:mygroup ALL=(ALL) NOPASSWD: ALL
829
830 Will allow user test to run sudo without a password.
831
832 Hook function API
833 The hook function API is currently not supported for plugins written in
834 Python.
835
836 Conversation API
837 A Python plugin can interact with the user using the sudo.conv() function
838 which displays one or more messages described by the sudo.ConvMessage
839 class. This is the Python equivalent of the conversation() function in
840 the C sudo plugin API. A plugin should not attempt to read directly from
841 the standard input or the user's tty (neither of which are guaranteed to
842 exist).
843
844 The sudo.ConvMessage class specifies how the user interaction should oc‐
845 cur:
846
847 sudo.ConvMessage(msg_type: int, msg: str, timeout: int)
848
849 sudo.ConvMessage member variables:
850
851 msg_type
852 Specifies the type of the conversation. See the sudo.CONV.* con‐
853 stants below.
854
855 msg The message to display to the user. The caller must include a
856 trailing newline in msg if one is to be displayed.
857
858 timeout
859 Optional. The maximum amount of time for the conversation in sec‐
860 onds. If the timeout is exceeded, the sudo.conv() function will
861 raise a sudo.ConversationInterrupted exception. The default is to
862 wait forever (no timeout).
863
864 To specify the message type, the following constants are available:
865
866 • sudo.CONV.PROMPT_ECHO_OFF
867 • sudo.CONV.PROMPT_ECHO_ON
868 • sudo.CONV.ERROR_MSG
869 • sudo.CONV.INFO_MSG
870 • sudo.CONV.PROMPT_MASK
871 • sudo.CONV.PROMPT_ECHO_OK
872 • sudo.CONV.PREFER_TTY
873
874 See the sudo_plugin(5) manual for a description of the message types.
875
876 The sudo.conv() function performs the actual user interaction:
877
878 sudo.conv(message(s), on_suspend=suspend_function,
879 on_resume=resume_function)
880
881 The function arguments are as follows:
882
883 message(s)
884 One of more messages (of type sudo.ConvMessage), each describing a
885 conversation. At least one message is required.
886
887 on_suspend
888 An optional callback function which gets called if the conversation
889 is suspended, for example by the user pressing control-Z. The
890 specified function must take a single argument which will be filled
891 with the number of the signal that caused the process to be sus‐
892 pended.
893
894 on_resume
895 An optional callback function which gets called when the previously
896 suspended conversation is resumed. The specified function must
897 take a single argument which will be filled with the number of the
898 signal that caused the process to be suspended.
899
900 The sudo.conv() function can raise the following exceptions:
901
902 sudo.SudoException
903 If the conversation fails, for example when the conversation func‐
904 tion is not available.
905
906 sudo.ConversationInterrupted
907 If the conversation function returns an error, e.g., the timeout
908 passed or the user interrupted the conversation by pressing con‐
909 trol-C.
910
911 Conversation example
912 Sudo ships with an example plugin demonstrating the Python conversation
913 API. To try it, register it by adding the following lines to
914 /etc/sudo.conf:
915
916 Plugin python_io python_plugin.so \
917 ModulePath=/usr/share/doc/sudo/examples/example_conversation.py \
918 ClassName=ReasonLoggerIOPlugin
919
920 Information / error display API
921 sudo.log_info(string(s), sep=" ", end="\n")
922 sudo.log_error(string(s), sep=" ", end="\n")
923
924 To display information to the user, the sudo.log_info() function can be
925 used. To display error messages, use sudo.log_error(). The syntax is
926 similar to the Python print() function.
927
928 The function arguments are as follows:
929
930 string(s)
931 One or more strings to display.
932
933 sep An optional string which will be used as the separator between the
934 specified strings. The default is a space character, (‘ ’).
935
936 end An optional string which will be displayed at the end of the mes‐
937 sage. The default is a new line character (‘\n’).
938
939 Debug API
940 Debug messages are not visible to the user and are only logged debugging
941 is explicitly enabled in sudo.conf(5). Python plugins can use the
942 sudo.debug() function to make use of sudo's debug system.
943
944 Enabling debugging in sudo.conf
945
946 To enable debug messages, add a Debug line to sudo.conf(5) with the pro‐
947 gram set to python_plugin.so. For example, to store debug output in
948 /var/log/sudo_python_debug, use a line like the following:
949
950 Debug python_plugin.so /var/log/sudo_python_debug \
951 plugin@trace,c_calls@trace
952
953 The debug options are in the form of multiple “subsystem@level” strings,
954 separated by commas (‘,’). For example to just see the debug output of
955 sudo.debug() calls, use:
956
957 Debug python_plugin.so /var/log/sudo_python_debug plugin@trace
958
959 See sudo_conf(5) for more details.
960
961 The most interesting subsystems for Python plugin development are:
962
963 plugin
964 Logs each sudo.debug() API call.
965
966 py_calls
967 Logs whenever a C function calls into the python module. For exam‐
968 ple, calling the __init__() function.
969
970 c_calls
971 Logs whenever python calls into a C sudo API function.
972
973 internal
974 Logs internal functions of the python language wrapper plugin.
975
976 sudo_cb
977 Logs when sudo calls into the python plugin API.
978
979 load Logs python plugin loading / unloading events.
980
981 You can also specify “all” as the subsystem name to log debug messages
982 for all subsystems.
983
984 The sudo.debug() function is defined as:
985
986 sudo.debug(level, message(s))
987
988 The function arguments are as follows:
989
990 level
991 an integer, use one of the log level constants below
992
993 message(s)
994 one or more messages to log
995
996 Available log levels:
997
998 sudo.conf name Python constant description
999 crit sudo.DEBUG.CRIT only critical messages
1000 err sudo.DEBUG.ERROR
1001 warn sudo.DEBUG.WARN
1002 notice sudo.DEBUG.NOTICE
1003 diag sudo.DEBUG.DIAG
1004 info sudo.DEBUG.INFO
1005 trace sudo.DEBUG.TRACE
1006 debug sudo.DEBUG.DEBUG very extreme verbose debugging
1007
1008 Using the logging module
1009
1010 Alternatively, a plugin can use the built in logging module of Python as
1011 well. Sudo adds its log handler to the root logger, so by default all
1012 output of a logger will get forwarded to sudo log system, as it would
1013 call sudo.debug.
1014
1015 The log handler of sudo will map each Python log level of a message to
1016 the appropriate sudo debug level. The sudo debug system will only re‐
1017 ceive messages that are not filtered out by the Python loggers. For ex‐
1018 ample, the log level of the python logger will be an additional filter
1019 for the log messages, and is usually very different from what level is
1020 set in sudo.conf for the sudo debug system.
1021
1022 Debug example
1023 Sudo ships with an example debug plugin. To try it, register it by
1024 adding the following lines to /etc/sudo.conf:
1025
1026 Plugin python_io python_plugin.so \
1027 ModulePath=/usr/share/doc/sudo/examples/example_debugging.py \
1028 ClassName=DebugDemoPlugin
1029
1030 Debug python_plugin.so \
1031 /var/log/sudo_python_debug plugin@trace,c_calls@trace
1032
1033 Option conversion API
1034 The Python plugin API includes two convenience functions to convert op‐
1035 tions in “key=value” format to a dictionary and vice versa.
1036
1037 options_as_dict
1038 options_as_dict(options)
1039
1040 The function arguments are as follows:
1041
1042 options
1043 An iterable (tuple, list, etc.) of strings, each in
1044 “key=value” format. This is how the plugin API passes op‐
1045 tions and settings to a Python plugin.
1046
1047 The function returns the resulting dictionary. Each string of the
1048 passed in options will be split at the first equal sign (‘=’) into
1049 a key and value. Dictionary keys will never contain this symbol
1050 (but values may).
1051
1052 options_from_dict
1053 options_from_dict(options_dict)
1054
1055 The function arguments are as follows:
1056
1057 options_dict
1058 A dictionary where both the key and the value are strings.
1059 The key should not contain an equal sign (‘=’), otherwise the
1060 resulting string will have a different meaning. However,
1061 this is not currently enforced.
1062
1063 The function returns a tuple containing the strings in “key=value”
1064 form for each key and value in the options_dict dictionary passed
1065 in. This is how the plugin API accepts options and settings.
1066
1068 None yet
1069
1071 A maximum of 8 python I/O plugins can be loaded at once. If
1072 /etc/sudo.conf contains more, those will be rejected with a warning mes‐
1073 sage.
1074
1075 The Event API and the hook function API is currently not accessible for
1076 Python plugins.
1077
1079 sudo.conf(5), sudo_plugin(5), sudoers(5), sudo(8)
1080
1082 Many people have worked on sudo over the years; this version consists of
1083 code written primarily by:
1084
1085 Todd C. Miller
1086
1087 See the CONTRIBUTORS.md file in the sudo distribution
1088 (https://www.sudo.ws/about/contributors/) for an exhaustive list of peo‐
1089 ple who have contributed to sudo.
1090
1092 Python plugin support is currently considered experimental.
1093
1094 If you believe you have found a bug in sudo, you can submit a bug report
1095 at https://bugzilla.sudo.ws/
1096
1098 All Python plugin handling is implemented inside the python_plugin.so dy‐
1099 namic plugin. Therefore, if no Python plugin is registered in
1100 sudo.conf(5) or the sudoers file, sudo will not load the Python inter‐
1101 preter or the Python libraries.
1102
1103 By default, a Python plugin can only import Python modules which are
1104 owned by root and are only writable by the owner. The reason for this is
1105 to prevent a file getting imported accidentally which is modifiable by a
1106 non-root user. As sudo plugins run as root, accidentally importing such
1107 file would make it possible for any user (having write access) to execute
1108 any code with administrative rights.
1109
1110 However, during development of a plugin this might not be very conve‐
1111 nient. The sudo.conf(5) developer_mode option can be used to disable it.
1112 For example:
1113 Set developer_mode true
1114
1115 This creates a security risk and is not recommended for production sys‐
1116 tems, it is intended to be used in a development environment (VM, con‐
1117 tainer, etc). Before enabling developer mode, be sure that you under‐
1118 stand the implications.
1119
1121 Limited free support is available via the sudo-users mailing list, see
1122 https://www.sudo.ws/mailman/listinfo/sudo-users to subscribe or search
1123 the archives.
1124
1126 sudo is provided “AS IS” and any express or implied warranties, includ‐
1127 ing, but not limited to, the implied warranties of merchantability and
1128 fitness for a particular purpose are disclaimed. See the LICENSE.md file
1129 distributed with sudo or https://www.sudo.ws/about/license/ for complete
1130 details.
1131
1132Sudo 1.9.12p2 September 11, 2022 Sudo 1.9.12p2