summaryrefslogtreecommitdiffstats
path: root/plugins/sudoers/regress
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--plugins/sudoers/regress/check_symbols/check_symbols.c99
-rw-r--r--plugins/sudoers/regress/cvtsudoers/sudoers126
-rwxr-xr-xplugins/sudoers/regress/cvtsudoers/sudoers.defs19
-rw-r--r--plugins/sudoers/regress/cvtsudoers/test1.out.ok14
-rwxr-xr-xplugins/sudoers/regress/cvtsudoers/test1.sh10
-rw-r--r--plugins/sudoers/regress/cvtsudoers/test10.out.ok1
-rwxr-xr-xplugins/sudoers/regress/cvtsudoers/test10.sh10
-rw-r--r--plugins/sudoers/regress/cvtsudoers/test11.out.ok7
-rwxr-xr-xplugins/sudoers/regress/cvtsudoers/test11.sh8
-rw-r--r--plugins/sudoers/regress/cvtsudoers/test12.out.ok8
-rwxr-xr-xplugins/sudoers/regress/cvtsudoers/test12.sh8
-rw-r--r--plugins/sudoers/regress/cvtsudoers/test13.out.ok7
-rwxr-xr-xplugins/sudoers/regress/cvtsudoers/test13.sh8
-rw-r--r--plugins/sudoers/regress/cvtsudoers/test14.out.ok7
-rwxr-xr-xplugins/sudoers/regress/cvtsudoers/test14.sh8
-rw-r--r--plugins/sudoers/regress/cvtsudoers/test15.out.ok1
-rwxr-xr-xplugins/sudoers/regress/cvtsudoers/test15.sh10
-rw-r--r--plugins/sudoers/regress/cvtsudoers/test16.out.ok1
-rwxr-xr-xplugins/sudoers/regress/cvtsudoers/test16.sh10
-rw-r--r--plugins/sudoers/regress/cvtsudoers/test17.out.ok1
-rwxr-xr-xplugins/sudoers/regress/cvtsudoers/test17.sh10
-rw-r--r--plugins/sudoers/regress/cvtsudoers/test18.out.ok1
-rwxr-xr-xplugins/sudoers/regress/cvtsudoers/test18.sh10
-rw-r--r--plugins/sudoers/regress/cvtsudoers/test19.out.ok11
-rwxr-xr-xplugins/sudoers/regress/cvtsudoers/test19.sh8
-rw-r--r--plugins/sudoers/regress/cvtsudoers/test2.out.ok10
-rwxr-xr-xplugins/sudoers/regress/cvtsudoers/test2.sh10
-rw-r--r--plugins/sudoers/regress/cvtsudoers/test20.conf6
-rw-r--r--plugins/sudoers/regress/cvtsudoers/test20.out.ok1
-rwxr-xr-xplugins/sudoers/regress/cvtsudoers/test20.sh13
-rw-r--r--plugins/sudoers/regress/cvtsudoers/test21.conf8
-rw-r--r--plugins/sudoers/regress/cvtsudoers/test21.out.ok24
-rwxr-xr-xplugins/sudoers/regress/cvtsudoers/test21.sh14
-rw-r--r--plugins/sudoers/regress/cvtsudoers/test22.out.ok31
-rwxr-xr-xplugins/sudoers/regress/cvtsudoers/test22.sh73
-rw-r--r--plugins/sudoers/regress/cvtsudoers/test23.out.ok20
-rwxr-xr-xplugins/sudoers/regress/cvtsudoers/test23.sh9
-rw-r--r--plugins/sudoers/regress/cvtsudoers/test24.out.ok89
-rwxr-xr-xplugins/sudoers/regress/cvtsudoers/test24.sh9
-rw-r--r--plugins/sudoers/regress/cvtsudoers/test25.out.ok31
-rwxr-xr-xplugins/sudoers/regress/cvtsudoers/test25.sh53
-rw-r--r--plugins/sudoers/regress/cvtsudoers/test26.err.ok3
-rw-r--r--plugins/sudoers/regress/cvtsudoers/test26.out.ok0
-rwxr-xr-xplugins/sudoers/regress/cvtsudoers/test26.sh49
-rw-r--r--plugins/sudoers/regress/cvtsudoers/test27.out.ok16
-rwxr-xr-xplugins/sudoers/regress/cvtsudoers/test27.sh12
-rw-r--r--plugins/sudoers/regress/cvtsudoers/test28.out.ok10
-rwxr-xr-xplugins/sudoers/regress/cvtsudoers/test28.sh74
-rw-r--r--plugins/sudoers/regress/cvtsudoers/test29.out.ok4
-rwxr-xr-xplugins/sudoers/regress/cvtsudoers/test29.sh61
-rw-r--r--plugins/sudoers/regress/cvtsudoers/test3.out.ok7
-rwxr-xr-xplugins/sudoers/regress/cvtsudoers/test3.sh10
-rw-r--r--plugins/sudoers/regress/cvtsudoers/test30.out.ok26
-rwxr-xr-xplugins/sudoers/regress/cvtsudoers/test30.sh15
-rw-r--r--plugins/sudoers/regress/cvtsudoers/test31.conf9
-rw-r--r--plugins/sudoers/regress/cvtsudoers/test31.out.ok24
-rw-r--r--plugins/sudoers/regress/cvtsudoers/test31.sh14
-rw-r--r--plugins/sudoers/regress/cvtsudoers/test32.err.ok1
-rw-r--r--plugins/sudoers/regress/cvtsudoers/test32.out.ok119
-rw-r--r--plugins/sudoers/regress/cvtsudoers/test32.sh22
-rw-r--r--plugins/sudoers/regress/cvtsudoers/test33.out.ok7
-rwxr-xr-xplugins/sudoers/regress/cvtsudoers/test33.sh62
-rw-r--r--plugins/sudoers/regress/cvtsudoers/test4.out.ok5
-rwxr-xr-xplugins/sudoers/regress/cvtsudoers/test4.sh10
-rw-r--r--plugins/sudoers/regress/cvtsudoers/test5.out.ok6
-rwxr-xr-xplugins/sudoers/regress/cvtsudoers/test5.sh10
-rw-r--r--plugins/sudoers/regress/cvtsudoers/test6.out.ok1
-rwxr-xr-xplugins/sudoers/regress/cvtsudoers/test6.sh10
-rw-r--r--plugins/sudoers/regress/cvtsudoers/test7.out.ok2
-rwxr-xr-xplugins/sudoers/regress/cvtsudoers/test7.sh10
-rw-r--r--plugins/sudoers/regress/cvtsudoers/test8.out.ok1
-rwxr-xr-xplugins/sudoers/regress/cvtsudoers/test8.sh10
-rw-r--r--plugins/sudoers/regress/cvtsudoers/test9.out.ok1
-rwxr-xr-xplugins/sudoers/regress/cvtsudoers/test9.sh10
-rw-r--r--plugins/sudoers/regress/env_match/check_env_pattern.c76
-rw-r--r--plugins/sudoers/regress/env_match/data22
-rw-r--r--plugins/sudoers/regress/exptilde/check_exptilde.c101
-rw-r--r--plugins/sudoers/regress/iolog_plugin/check_iolog_plugin.c430
-rw-r--r--plugins/sudoers/regress/parser/check_addr.c139
-rw-r--r--plugins/sudoers/regress/parser/check_addr.in13
-rw-r--r--plugins/sudoers/regress/parser/check_base64.c114
-rw-r--r--plugins/sudoers/regress/parser/check_digest.c125
-rw-r--r--plugins/sudoers/regress/parser/check_digest.out.ok36
-rw-r--r--plugins/sudoers/regress/parser/check_fill.c189
-rw-r--r--plugins/sudoers/regress/parser/check_gentime.c83
-rw-r--r--plugins/sudoers/regress/parser/check_hexchar.c81
-rw-r--r--plugins/sudoers/regress/starttime/check_starttime.c118
-rw-r--r--plugins/sudoers/regress/sudoers/test1.in12
-rw-r--r--plugins/sudoers/regress/sudoers/test1.json.ok154
-rw-r--r--plugins/sudoers/regress/sudoers/test1.ldif.ok88
-rw-r--r--plugins/sudoers/regress/sudoers/test1.ldif2sudo.ok13
-rw-r--r--plugins/sudoers/regress/sudoers/test1.out.ok6
-rw-r--r--plugins/sudoers/regress/sudoers/test1.toke.ok8
-rw-r--r--plugins/sudoers/regress/sudoers/test10.in1
-rw-r--r--plugins/sudoers/regress/sudoers/test10.json.ok0
-rw-r--r--plugins/sudoers/regress/sudoers/test10.ldif.ok0
-rw-r--r--plugins/sudoers/regress/sudoers/test10.out.ok2
-rw-r--r--plugins/sudoers/regress/sudoers/test10.toke.ok1
-rw-r--r--plugins/sudoers/regress/sudoers/test11.in1
-rw-r--r--plugins/sudoers/regress/sudoers/test11.json.ok0
-rw-r--r--plugins/sudoers/regress/sudoers/test11.ldif.ok0
-rw-r--r--plugins/sudoers/regress/sudoers/test11.out.ok1
-rw-r--r--plugins/sudoers/regress/sudoers/test11.toke.ok2
-rw-r--r--plugins/sudoers/regress/sudoers/test12.in1
-rw-r--r--plugins/sudoers/regress/sudoers/test12.json.ok0
-rw-r--r--plugins/sudoers/regress/sudoers/test12.ldif.ok0
-rw-r--r--plugins/sudoers/regress/sudoers/test12.out.ok1
-rw-r--r--plugins/sudoers/regress/sudoers/test12.toke.ok2
-rw-r--r--plugins/sudoers/regress/sudoers/test13.in1
-rw-r--r--plugins/sudoers/regress/sudoers/test13.json.ok0
-rw-r--r--plugins/sudoers/regress/sudoers/test13.ldif.ok0
-rw-r--r--plugins/sudoers/regress/sudoers/test13.out.ok1
-rw-r--r--plugins/sudoers/regress/sudoers/test13.toke.ok2
-rw-r--r--plugins/sudoers/regress/sudoers/test14.in6
-rw-r--r--plugins/sudoers/regress/sudoers/test14.json.ok62
-rw-r--r--plugins/sudoers/regress/sudoers/test14.ldif.ok20
-rw-r--r--plugins/sudoers/regress/sudoers/test14.ldif2sudo.ok11
-rw-r--r--plugins/sudoers/regress/sudoers/test14.out.ok7
-rw-r--r--plugins/sudoers/regress/sudoers/test14.toke.ok6
-rw-r--r--plugins/sudoers/regress/sudoers/test15.in2
-rw-r--r--plugins/sudoers/regress/sudoers/test15.json.ok19
-rw-r--r--plugins/sudoers/regress/sudoers/test15.ldif.ok9
-rw-r--r--plugins/sudoers/regress/sudoers/test15.ldif2sudo.ok2
-rw-r--r--plugins/sudoers/regress/sudoers/test15.out.ok3
-rw-r--r--plugins/sudoers/regress/sudoers/test15.toke.ok2
-rw-r--r--plugins/sudoers/regress/sudoers/test16.in3
-rw-r--r--plugins/sudoers/regress/sudoers/test16.json.ok24
-rw-r--r--plugins/sudoers/regress/sudoers/test16.ldif.ok9
-rw-r--r--plugins/sudoers/regress/sudoers/test16.ldif2sudo.ok2
-rw-r--r--plugins/sudoers/regress/sudoers/test16.out.ok5
-rw-r--r--plugins/sudoers/regress/sudoers/test16.toke.ok3
-rw-r--r--plugins/sudoers/regress/sudoers/test17.in13
-rw-r--r--plugins/sudoers/regress/sudoers/test17.json.ok180
-rw-r--r--plugins/sudoers/regress/sudoers/test17.ldif.ok104
-rw-r--r--plugins/sudoers/regress/sudoers/test17.ldif2sudo.ok29
-rw-r--r--plugins/sudoers/regress/sudoers/test17.out.ok13
-rw-r--r--plugins/sudoers/regress/sudoers/test17.toke.ok11
-rw-r--r--plugins/sudoers/regress/sudoers/test18.in8
-rw-r--r--plugins/sudoers/regress/sudoers/test18.json.ok0
-rw-r--r--plugins/sudoers/regress/sudoers/test18.ldif.ok0
-rw-r--r--plugins/sudoers/regress/sudoers/test18.out.ok4
-rw-r--r--plugins/sudoers/regress/sudoers/test18.toke.ok10
-rw-r--r--plugins/sudoers/regress/sudoers/test19.in12
-rw-r--r--plugins/sudoers/regress/sudoers/test19.json.ok187
-rw-r--r--plugins/sudoers/regress/sudoers/test19.ldif.ok103
-rw-r--r--plugins/sudoers/regress/sudoers/test19.ldif2sudo.ok30
-rw-r--r--plugins/sudoers/regress/sudoers/test19.out.ok12
-rw-r--r--plugins/sudoers/regress/sudoers/test19.toke.ok12
-rw-r--r--plugins/sudoers/regress/sudoers/test2.in60
-rw-r--r--plugins/sudoers/regress/sudoers/test2.json.ok403
-rw-r--r--plugins/sudoers/regress/sudoers/test2.ldif.ok157
-rw-r--r--plugins/sudoers/regress/sudoers/test2.ldif2sudo.ok38
-rw-r--r--plugins/sudoers/regress/sudoers/test2.out.ok42
-rw-r--r--plugins/sudoers/regress/sudoers/test2.toke.ok60
-rw-r--r--plugins/sudoers/regress/sudoers/test20.in26
-rw-r--r--plugins/sudoers/regress/sudoers/test20.json.ok114
-rw-r--r--plugins/sudoers/regress/sudoers/test20.ldif.ok28
-rw-r--r--plugins/sudoers/regress/sudoers/test20.ldif2sudo.ok22
-rw-r--r--plugins/sudoers/regress/sudoers/test20.out.ok24
-rw-r--r--plugins/sudoers/regress/sudoers/test20.toke.ok26
-rw-r--r--plugins/sudoers/regress/sudoers/test21.in36
-rw-r--r--plugins/sudoers/regress/sudoers/test21.json.ok169
-rw-r--r--plugins/sudoers/regress/sudoers/test21.ldif.ok39
-rw-r--r--plugins/sudoers/regress/sudoers/test21.ldif2sudo.ok33
-rw-r--r--plugins/sudoers/regress/sudoers/test21.out.ok35
-rw-r--r--plugins/sudoers/regress/sudoers/test21.toke.ok36
-rw-r--r--plugins/sudoers/regress/sudoers/test22.in6
-rw-r--r--plugins/sudoers/regress/sudoers/test22.json.ok88
-rw-r--r--plugins/sudoers/regress/sudoers/test22.ldif.ok40
-rw-r--r--plugins/sudoers/regress/sudoers/test22.ldif2sudo.ok11
-rw-r--r--plugins/sudoers/regress/sudoers/test22.out.ok6
-rw-r--r--plugins/sudoers/regress/sudoers/test22.sudo.ok7
-rw-r--r--plugins/sudoers/regress/sudoers/test22.toke.ok6
-rw-r--r--plugins/sudoers/regress/sudoers/test23.in11
-rw-r--r--plugins/sudoers/regress/sudoers/test23.json.ok102
-rw-r--r--plugins/sudoers/regress/sudoers/test23.ldif.ok14
-rw-r--r--plugins/sudoers/regress/sudoers/test23.ldif2sudo.ok8
-rw-r--r--plugins/sudoers/regress/sudoers/test23.out.ok10
-rw-r--r--plugins/sudoers/regress/sudoers/test23.sudo.ok8
-rw-r--r--plugins/sudoers/regress/sudoers/test23.toke.ok11
-rw-r--r--plugins/sudoers/regress/sudoers/test24.in6
-rw-r--r--plugins/sudoers/regress/sudoers/test24.json.ok61
-rw-r--r--plugins/sudoers/regress/sudoers/test24.ldif.ok39
-rw-r--r--plugins/sudoers/regress/sudoers/test24.ldif2sudo.ok8
-rw-r--r--plugins/sudoers/regress/sudoers/test24.out.ok7
-rw-r--r--plugins/sudoers/regress/sudoers/test24.sudo.ok6
-rw-r--r--plugins/sudoers/regress/sudoers/test24.toke.ok6
-rw-r--r--plugins/sudoers/regress/sudoers/test3.in6
-rw-r--r--plugins/sudoers/regress/sudoers/test3.json.ok45
-rw-r--r--plugins/sudoers/regress/sudoers/test3.ldif.ok12
-rw-r--r--plugins/sudoers/regress/sudoers/test3.ldif2sudo.ok0
-rw-r--r--plugins/sudoers/regress/sudoers/test3.out.ok8
-rw-r--r--plugins/sudoers/regress/sudoers/test3.toke.ok6
-rw-r--r--plugins/sudoers/regress/sudoers/test4.in7
-rw-r--r--plugins/sudoers/regress/sudoers/test4.json.ok0
-rw-r--r--plugins/sudoers/regress/sudoers/test4.ldif.ok0
-rw-r--r--plugins/sudoers/regress/sudoers/test4.out.ok3
-rw-r--r--plugins/sudoers/regress/sudoers/test4.toke.ok5
-rw-r--r--plugins/sudoers/regress/sudoers/test5.in3
-rw-r--r--plugins/sudoers/regress/sudoers/test5.json.ok0
-rw-r--r--plugins/sudoers/regress/sudoers/test5.ldif.ok0
-rw-r--r--plugins/sudoers/regress/sudoers/test5.out.ok1
-rw-r--r--plugins/sudoers/regress/sudoers/test5.toke.ok3
-rw-r--r--plugins/sudoers/regress/sudoers/test6.in15
-rw-r--r--plugins/sudoers/regress/sudoers/test6.json.ok158
-rw-r--r--plugins/sudoers/regress/sudoers/test6.ldif.ok70
-rw-r--r--plugins/sudoers/regress/sudoers/test6.ldif2sudo.ok5
-rw-r--r--plugins/sudoers/regress/sudoers/test6.out.ok13
-rw-r--r--plugins/sudoers/regress/sudoers/test6.toke.ok15
-rw-r--r--plugins/sudoers/regress/sudoers/test7.in7
-rw-r--r--plugins/sudoers/regress/sudoers/test7.json.ok0
-rw-r--r--plugins/sudoers/regress/sudoers/test7.ldif.ok0
-rw-r--r--plugins/sudoers/regress/sudoers/test7.out.ok1
-rw-r--r--plugins/sudoers/regress/sudoers/test7.toke.ok7
-rw-r--r--plugins/sudoers/regress/sudoers/test8.in8
-rw-r--r--plugins/sudoers/regress/sudoers/test8.json.ok0
-rw-r--r--plugins/sudoers/regress/sudoers/test8.ldif.ok0
-rw-r--r--plugins/sudoers/regress/sudoers/test8.out.ok4
-rw-r--r--plugins/sudoers/regress/sudoers/test8.toke.ok7
-rw-r--r--plugins/sudoers/regress/sudoers/test9.in0
-rw-r--r--plugins/sudoers/regress/sudoers/test9.json.ok0
-rw-r--r--plugins/sudoers/regress/sudoers/test9.ldif.ok0
-rw-r--r--plugins/sudoers/regress/sudoers/test9.out.ok2
-rw-r--r--plugins/sudoers/regress/sudoers/test9.toke.ok0
-rw-r--r--plugins/sudoers/regress/testsudoers/group15
-rw-r--r--plugins/sudoers/regress/testsudoers/test1.out.ok8
-rwxr-xr-xplugins/sudoers/regress/testsudoers/test1.sh15
-rw-r--r--plugins/sudoers/regress/testsudoers/test10.out.ok51
-rwxr-xr-xplugins/sudoers/regress/testsudoers/test10.sh46
-rw-r--r--plugins/sudoers/regress/testsudoers/test11.out.ok21
-rwxr-xr-xplugins/sudoers/regress/testsudoers/test11.sh28
-rw-r--r--plugins/sudoers/regress/testsudoers/test12.out.ok15
-rwxr-xr-xplugins/sudoers/regress/testsudoers/test12.sh20
-rw-r--r--plugins/sudoers/regress/testsudoers/test13.out.ok22
-rwxr-xr-xplugins/sudoers/regress/testsudoers/test13.sh25
-rw-r--r--plugins/sudoers/regress/testsudoers/test14.out.ok18
-rwxr-xr-xplugins/sudoers/regress/testsudoers/test14.sh25
-rw-r--r--plugins/sudoers/regress/testsudoers/test15.out.ok17
-rwxr-xr-xplugins/sudoers/regress/testsudoers/test15.sh24
-rw-r--r--plugins/sudoers/regress/testsudoers/test2.inc1
-rw-r--r--plugins/sudoers/regress/testsudoers/test2.out.ok25
-rwxr-xr-xplugins/sudoers/regress/testsudoers/test2.sh25
-rw-r--r--plugins/sudoers/regress/testsudoers/test3.out.ok51
-rwxr-xr-xplugins/sudoers/regress/testsudoers/test3.sh48
-rw-r--r--plugins/sudoers/regress/testsudoers/test4.out.ok5
-rwxr-xr-xplugins/sudoers/regress/testsudoers/test4.sh16
-rw-r--r--plugins/sudoers/regress/testsudoers/test5.out.ok10
-rwxr-xr-xplugins/sudoers/regress/testsudoers/test5.sh34
-rw-r--r--plugins/sudoers/regress/testsudoers/test6.out.ok10
-rwxr-xr-xplugins/sudoers/regress/testsudoers/test6.sh13
-rw-r--r--plugins/sudoers/regress/testsudoers/test7.out.ok10
-rwxr-xr-xplugins/sudoers/regress/testsudoers/test7.sh13
-rw-r--r--plugins/sudoers/regress/testsudoers/test8.out.ok25
-rwxr-xr-xplugins/sudoers/regress/testsudoers/test8.sh24
-rw-r--r--plugins/sudoers/regress/testsudoers/test9.out.ok10
-rwxr-xr-xplugins/sudoers/regress/testsudoers/test9.sh15
-rw-r--r--plugins/sudoers/regress/visudo/test1.out.ok1
-rwxr-xr-xplugins/sudoers/regress/visudo/test1.sh14
-rw-r--r--plugins/sudoers/regress/visudo/test10.out.ok1
-rwxr-xr-xplugins/sudoers/regress/visudo/test10.sh13
-rw-r--r--plugins/sudoers/regress/visudo/test2.err.ok1
-rw-r--r--plugins/sudoers/regress/visudo/test2.out.ok0
-rwxr-xr-xplugins/sudoers/regress/visudo/test2.sh17
-rw-r--r--plugins/sudoers/regress/visudo/test3.err.ok2
-rw-r--r--plugins/sudoers/regress/visudo/test3.out.ok1
-rwxr-xr-xplugins/sudoers/regress/visudo/test3.sh37
-rw-r--r--plugins/sudoers/regress/visudo/test4.out.ok1
-rwxr-xr-xplugins/sudoers/regress/visudo/test4.sh16
-rw-r--r--plugins/sudoers/regress/visudo/test5.out.ok1
-rwxr-xr-xplugins/sudoers/regress/visudo/test5.sh10
-rw-r--r--plugins/sudoers/regress/visudo/test6.out.ok1
-rwxr-xr-xplugins/sudoers/regress/visudo/test6.sh27
-rw-r--r--plugins/sudoers/regress/visudo/test7.out.ok1
-rwxr-xr-xplugins/sudoers/regress/visudo/test7.sh31
-rw-r--r--plugins/sudoers/regress/visudo/test8.err.ok1
-rw-r--r--plugins/sudoers/regress/visudo/test8.out.ok1
-rwxr-xr-xplugins/sudoers/regress/visudo/test8.sh32
-rw-r--r--plugins/sudoers/regress/visudo/test9.out.ok1
-rwxr-xr-xplugins/sudoers/regress/visudo/test9.sh14
279 files changed, 7301 insertions, 0 deletions
diff --git a/plugins/sudoers/regress/check_symbols/check_symbols.c b/plugins/sudoers/regress/check_symbols/check_symbols.c
new file mode 100644
index 0000000..5a5e02b
--- /dev/null
+++ b/plugins/sudoers/regress/check_symbols/check_symbols.c
@@ -0,0 +1,99 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * Copyright (c) 2012-2015 Todd C. Miller <Todd.Miller@sudo.ws>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+
+#include "sudo_compat.h"
+#include "sudo_dso.h"
+#include "sudo_util.h"
+#include "sudo_fatal.h"
+
+sudo_dso_public int main(int argc, char *argv[]);
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: %s plugin.so symbols_file\n", getprogname());
+ exit(EXIT_FAILURE);
+}
+
+int
+main(int argc, char *argv[])
+{
+ void *handle, *sym;
+ const char *plugin_path;
+ const char *symbols_file;
+ char *cp, line[LINE_MAX];
+ FILE *fp;
+ int ntests = 0, errors = 0;
+
+ initprogname(argc > 0 ? argv[0] : "check_symbols");
+
+ if (argc != 3)
+ usage();
+ plugin_path = argv[1];
+ symbols_file = argv[2];
+
+ handle = sudo_dso_load(plugin_path, SUDO_DSO_LAZY|SUDO_DSO_GLOBAL);
+ if (handle == NULL) {
+ const char *errstr = sudo_dso_strerror();
+ sudo_fatalx_nodebug("unable to load %s: %s", plugin_path,
+ errstr ? errstr : "unknown error");
+ }
+
+ fp = fopen(symbols_file, "r");
+ if (fp == NULL)
+ sudo_fatal_nodebug("unable to open %s", symbols_file);
+
+ while (fgets(line, sizeof(line), fp) != NULL) {
+ ntests++;
+ if ((cp = strchr(line, '\n')) != NULL)
+ *cp = '\0';
+ sym = sudo_dso_findsym(handle, line);
+ if (sym == NULL) {
+ const char *errstr = sudo_dso_strerror();
+ printf("%s: test %d: unable to resolve symbol %s: %s\n",
+ getprogname(), ntests, line, errstr ? errstr : "unknown error");
+ errors++;
+ }
+ }
+
+ /*
+ * Make sure unexported symbols are not available.
+ */
+ ntests++;
+ sym = sudo_dso_findsym(handle, "user_in_group");
+ if (sym != NULL) {
+ printf("%s: test %d: able to resolve local symbol user_in_group\n",
+ getprogname(), ntests);
+ errors++;
+ }
+
+ sudo_dso_unload(handle);
+
+ printf("%s: %d tests run, %d errors, %d%% success rate\n", getprogname(),
+ ntests, errors, (ntests - errors) * 100 / ntests);
+
+ exit(errors);
+}
diff --git a/plugins/sudoers/regress/cvtsudoers/sudoers b/plugins/sudoers/regress/cvtsudoers/sudoers
new file mode 100644
index 0000000..8a926f8
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/sudoers
@@ -0,0 +1,126 @@
+#
+# Sample /etc/sudoers file.
+#
+# This file MUST be edited with the 'visudo' command as root.
+#
+# See the sudoers man page for the details on how to write a sudoers file.
+
+##
+# Override built-in defaults
+##
+Defaults syslog=auth
+Defaults>root !set_logname
+Defaults:FULLTIMERS !lecture
+Defaults:millert !authenticate
+Defaults@SERVERS log_year, logfile=/var/log/sudo.log
+Defaults!PAGERS noexec
+
+##
+# User alias specification
+##
+User_Alias FULLTIMERS = millert, mikef, dowdy
+User_Alias PARTTIMERS = bostley, jwfox, crawl
+User_Alias WEBADMIN = will, wendy, wim
+
+##
+# Runas alias specification
+##
+Runas_Alias OP = root, operator
+Runas_Alias DB = oracle, sybase
+
+##
+# Host alias specification
+##
+Host_Alias SPARC = bigtime, eclipse, moet, anchor:\
+ SGI = grolsch, dandelion, black:\
+ ALPHA = widget, thalamus, foobar:\
+ HPPA = boa, nag, python
+Host_Alias CUNETS = 128.138.0.0/255.255.0.0
+Host_Alias CSNETS = 128.138.243.0, 128.138.204.0/24, 128.138.242.0
+Host_Alias SERVERS = primary, mail, www, ns
+Host_Alias CDROM = orion, perseus, hercules
+
+##
+# Cmnd alias specification
+##
+Cmnd_Alias DUMPS = /usr/sbin/dump, /usr/sbin/rdump, /usr/sbin/restore, \
+ /usr/sbin/rrestore, /usr/bin/mt, \
+ sha224:0GomF8mNN3wlDt1HD9XldjJ3SNgpFdbjO1+NsQ== \
+ /home/operator/bin/start_backups
+Cmnd_Alias KILL = /usr/bin/kill, /usr/bin/top
+Cmnd_Alias PRINTING = /usr/sbin/lpc, /usr/bin/lprm
+Cmnd_Alias SHUTDOWN = /usr/sbin/shutdown
+Cmnd_Alias HALT = /usr/sbin/halt
+Cmnd_Alias REBOOT = /usr/sbin/reboot
+Cmnd_Alias SHELLS = /sbin/sh, /usr/bin/sh, /usr/bin/csh, /usr/bin/ksh, \
+ /usr/local/bin/tcsh, /usr/bin/rsh, \
+ /usr/local/bin/zsh
+Cmnd_Alias SU = /usr/bin/su
+Cmnd_Alias VIPW = /usr/sbin/vipw, /usr/bin/passwd, /usr/bin/chsh, \
+ /usr/bin/chfn
+Cmnd_Alias PAGERS = /usr/bin/more, /usr/bin/pg, /usr/bin/less
+
+##
+# User specification
+##
+
+# root and users in group wheel can run anything on any machine as any user
+root ALL = (ALL) ALL
+%wheel ALL = (ALL) ALL
+
+# full time sysadmins can run anything on any machine without a password
+FULLTIMERS ALL = NOPASSWD: ALL
+
+# part time sysadmins may run anything but need a password
+PARTTIMERS ALL = ALL
+
+# jack may run anything on machines in CSNETS
+jack CSNETS = ALL
+
+# lisa may run any command on any host in CUNETS (a class B network)
+lisa CUNETS = ALL
+
+# operator may run maintenance commands and anything in /usr/oper/bin/
+operator ALL = DUMPS, KILL, SHUTDOWN, HALT, REBOOT, PRINTING,\
+ sudoedit /etc/printcap, /usr/oper/bin/
+
+# joe may su only to operator
+joe ALL = /usr/bin/su operator
+
+# pete may change passwords for anyone but root on the hp snakes
+pete HPPA = /usr/bin/passwd [A-Za-z]*, !/usr/bin/passwd *root*
+
+# bob may run anything on the sparc and sgi machines as any user
+# listed in the Runas_Alias "OP" (ie: root and operator)
+bob SPARC = (OP) ALL : SGI = (OP) ALL
+
+# fred can run commands as oracle or sybase without a password
+fred ALL = (DB) NOPASSWD: ALL
+
+# on the alphas, john may su to anyone but root and flags are not allowed
+john ALPHA = /usr/bin/su [!-]*, !/usr/bin/su *root*
+
+# jen can run anything on all machines except the ones
+# in the "SERVERS" Host_Alias
+jen ALL, !SERVERS = ALL
+
+# jill can run any commands in the directory /usr/bin/, except for
+# those in the SU and SHELLS aliases.
+jill SERVERS = /usr/bin/, !SU, !SHELLS
+
+# steve can run any command in the directory /usr/local/op_commands/
+# as user operator.
+steve CSNETS = (operator) /usr/local/op_commands/
+
+# matt needs to be able to kill things on his workstation when
+# they get hung.
+matt valkyrie = KILL
+
+# users in the WEBADMIN User_Alias (will, wendy, and wim)
+# may run any command as user www (which owns the web pages)
+# or simply su to www.
+WEBADMIN www = (www) ALL, (root) /usr/bin/su www
+
+# anyone can mount/unmount a cd-rom on the machines in the CDROM alias
+ALL CDROM = NOPASSWD: /sbin/umount /CDROM,\
+ /sbin/mount -o nosuid\,nodev /dev/cd0a /CDROM
diff --git a/plugins/sudoers/regress/cvtsudoers/sudoers.defs b/plugins/sudoers/regress/cvtsudoers/sudoers.defs
new file mode 100755
index 0000000..b374930
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/sudoers.defs
@@ -0,0 +1,19 @@
+Defaults syslog=auth
+Defaults>ROOT !set_logname
+Defaults:FULLTIMERS !lecture
+Defaults:millert !authenticate
+Defaults@SERVERS log_year, logfile=/var/log/sudo.log
+Defaults!PAGERS noexec
+
+User_Alias FULLTIMERS = millert, mikef, dowdy
+User_Alias PARTTIMERS = bostley, jwfox, crawl
+
+Host_Alias SERVERS = primary, mail, www, ns
+Host_Alias CDROM = orion, perseus, hercules
+
+Cmnd_Alias VIPW = /usr/sbin/vipw, /usr/bin/passwd, /usr/bin/chsh, \
+ /usr/bin/chfn
+Cmnd_Alias PAGERS = /usr/bin/more, /usr/bin/pg, /usr/bin/less
+
+Runas_Alias ROOT = root, toor
+Runas_Alias OPERATOR = operator, backup
diff --git a/plugins/sudoers/regress/cvtsudoers/test1.out.ok b/plugins/sudoers/regress/cvtsudoers/test1.out.ok
new file mode 100644
index 0000000..da3f555
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test1.out.ok
@@ -0,0 +1,14 @@
+Defaults syslog=auth
+Defaults>root !set_logname
+Defaults:FULLTIMERS !lecture
+Defaults:millert !authenticate
+Defaults!PAGERS noexec
+
+Host_Alias CDROM = orion, perseus, hercules
+User_Alias FULLTIMERS = millert, mikef, dowdy
+Cmnd_Alias PAGERS = /usr/bin/more, /usr/bin/pg, /usr/bin/less
+
+FULLTIMERS ALL = NOPASSWD: ALL
+
+ALL CDROM = NOPASSWD: /sbin/umount /CDROM, /sbin/mount -o nosuid\,nodev\
+ /dev/cd0a /CDROM
diff --git a/plugins/sudoers/regress/cvtsudoers/test1.sh b/plugins/sudoers/regress/cvtsudoers/test1.sh
new file mode 100755
index 0000000..ba5f87f
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test1.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+#
+# Test user and host filters
+#
+
+: ${CVTSUDOERS=cvtsudoers}
+
+$CVTSUDOERS -c "" -f sudoers -m user=millert,host=hercules $TESTDIR/sudoers
+
+exit 0
diff --git a/plugins/sudoers/regress/cvtsudoers/test10.out.ok b/plugins/sudoers/regress/cvtsudoers/test10.out.ok
new file mode 100644
index 0000000..26a05d2
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test10.out.ok
@@ -0,0 +1 @@
+Defaults!PAGERS noexec
diff --git a/plugins/sudoers/regress/cvtsudoers/test10.sh b/plugins/sudoers/regress/cvtsudoers/test10.sh
new file mode 100755
index 0000000..38550a9
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test10.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+#
+# Test command defaults filtering
+#
+
+: ${CVTSUDOERS=cvtsudoers}
+
+$CVTSUDOERS -c "" -f sudoers -s aliases,privileges -d command $TESTDIR/sudoers
+
+exit 0
diff --git a/plugins/sudoers/regress/cvtsudoers/test11.out.ok b/plugins/sudoers/regress/cvtsudoers/test11.out.ok
new file mode 100644
index 0000000..5c4c4e8
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test11.out.ok
@@ -0,0 +1,7 @@
+Defaults!PAGERS noexec
+
+Host_Alias CDROM = orion, perseus, hercules
+Runas_Alias OPERATOR = operator, backup
+Cmnd_Alias PAGERS = /usr/bin/more, /usr/bin/pg, /usr/bin/less
+User_Alias PARTTIMERS = bostley, jwfox, crawl
+Cmnd_Alias VIPW = /usr/sbin/vipw, /usr/bin/passwd, /usr/bin/chsh, /usr/bin/chfn
diff --git a/plugins/sudoers/regress/cvtsudoers/test11.sh b/plugins/sudoers/regress/cvtsudoers/test11.sh
new file mode 100755
index 0000000..f8d37ef
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test11.sh
@@ -0,0 +1,8 @@
+#!/bin/sh
+#
+# Test that Aliases are removed when filtering by defaults type
+#
+
+: ${CVTSUDOERS=cvtsudoers}
+
+$CVTSUDOERS -c "" -f sudoers -d command $TESTDIR/sudoers.defs
diff --git a/plugins/sudoers/regress/cvtsudoers/test12.out.ok b/plugins/sudoers/regress/cvtsudoers/test12.out.ok
new file mode 100644
index 0000000..7f2b15e
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test12.out.ok
@@ -0,0 +1,8 @@
+Defaults:FULLTIMERS !lecture
+Defaults:millert !authenticate
+
+Host_Alias CDROM = orion, perseus, hercules
+User_Alias FULLTIMERS = millert, mikef, dowdy
+Runas_Alias OPERATOR = operator, backup
+User_Alias PARTTIMERS = bostley, jwfox, crawl
+Cmnd_Alias VIPW = /usr/sbin/vipw, /usr/bin/passwd, /usr/bin/chsh, /usr/bin/chfn
diff --git a/plugins/sudoers/regress/cvtsudoers/test12.sh b/plugins/sudoers/regress/cvtsudoers/test12.sh
new file mode 100755
index 0000000..dd2cab0
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test12.sh
@@ -0,0 +1,8 @@
+#!/bin/sh
+#
+# Test that Aliases are removed when filtering by defaults type
+#
+
+: ${CVTSUDOERS=cvtsudoers}
+
+$CVTSUDOERS -c "" -f sudoers -d user $TESTDIR/sudoers.defs
diff --git a/plugins/sudoers/regress/cvtsudoers/test13.out.ok b/plugins/sudoers/regress/cvtsudoers/test13.out.ok
new file mode 100644
index 0000000..5276327
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test13.out.ok
@@ -0,0 +1,7 @@
+Defaults@SERVERS log_year, logfile=/var/log/sudo.log
+
+Host_Alias CDROM = orion, perseus, hercules
+Runas_Alias OPERATOR = operator, backup
+User_Alias PARTTIMERS = bostley, jwfox, crawl
+Host_Alias SERVERS = primary, mail, www, ns
+Cmnd_Alias VIPW = /usr/sbin/vipw, /usr/bin/passwd, /usr/bin/chsh, /usr/bin/chfn
diff --git a/plugins/sudoers/regress/cvtsudoers/test13.sh b/plugins/sudoers/regress/cvtsudoers/test13.sh
new file mode 100755
index 0000000..8ee6000
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test13.sh
@@ -0,0 +1,8 @@
+#!/bin/sh
+#
+# Test that Aliases are removed when filtering by defaults type
+#
+
+: ${CVTSUDOERS=cvtsudoers}
+
+$CVTSUDOERS -c "" -f sudoers -d host $TESTDIR/sudoers.defs
diff --git a/plugins/sudoers/regress/cvtsudoers/test14.out.ok b/plugins/sudoers/regress/cvtsudoers/test14.out.ok
new file mode 100644
index 0000000..3f7710a
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test14.out.ok
@@ -0,0 +1,7 @@
+Defaults>ROOT !set_logname
+
+Host_Alias CDROM = orion, perseus, hercules
+Runas_Alias OPERATOR = operator, backup
+User_Alias PARTTIMERS = bostley, jwfox, crawl
+Runas_Alias ROOT = root, toor
+Cmnd_Alias VIPW = /usr/sbin/vipw, /usr/bin/passwd, /usr/bin/chsh, /usr/bin/chfn
diff --git a/plugins/sudoers/regress/cvtsudoers/test14.sh b/plugins/sudoers/regress/cvtsudoers/test14.sh
new file mode 100755
index 0000000..4486faa
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test14.sh
@@ -0,0 +1,8 @@
+#!/bin/sh
+#
+# Test that Aliases are removed when filtering by defaults type
+#
+
+: ${CVTSUDOERS=cvtsudoers}
+
+$CVTSUDOERS -c "" -f sudoers -d runas $TESTDIR/sudoers.defs
diff --git a/plugins/sudoers/regress/cvtsudoers/test15.out.ok b/plugins/sudoers/regress/cvtsudoers/test15.out.ok
new file mode 100644
index 0000000..5177139
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test15.out.ok
@@ -0,0 +1 @@
+user1 host1, host2, host3 = ALL
diff --git a/plugins/sudoers/regress/cvtsudoers/test15.sh b/plugins/sudoers/regress/cvtsudoers/test15.sh
new file mode 100755
index 0000000..6d65c78
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test15.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+#
+# Test filters and pruning
+#
+
+: ${CVTSUDOERS=cvtsudoers}
+
+$CVTSUDOERS -c "" -f sudoers -p -m user=user1 <<EOF
+user1, user2, user3, %group1 host1, host2, host3 = ALL
+EOF
diff --git a/plugins/sudoers/regress/cvtsudoers/test16.out.ok b/plugins/sudoers/regress/cvtsudoers/test16.out.ok
new file mode 100644
index 0000000..38359b1
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test16.out.ok
@@ -0,0 +1 @@
+user2 host2 = ALL
diff --git a/plugins/sudoers/regress/cvtsudoers/test16.sh b/plugins/sudoers/regress/cvtsudoers/test16.sh
new file mode 100755
index 0000000..4a1632c
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test16.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+#
+# Test filters and pruning
+#
+
+: ${CVTSUDOERS=cvtsudoers}
+
+$CVTSUDOERS -c "" -f sudoers -p -m user=user2,host=host2 <<EOF
+user1, user2, user3, %group1 host1, host2, host3 = ALL
+EOF
diff --git a/plugins/sudoers/regress/cvtsudoers/test17.out.ok b/plugins/sudoers/regress/cvtsudoers/test17.out.ok
new file mode 100644
index 0000000..d35dd06
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test17.out.ok
@@ -0,0 +1 @@
+%group1 host1 = ALL
diff --git a/plugins/sudoers/regress/cvtsudoers/test17.sh b/plugins/sudoers/regress/cvtsudoers/test17.sh
new file mode 100755
index 0000000..e8e8082
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test17.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+#
+# Test filters and pruning
+#
+
+: ${CVTSUDOERS=cvtsudoers}
+
+$CVTSUDOERS -c "" -f sudoers -p -m group=group1,host=host1 <<EOF
+user1, user2, user3, %group1 host1, host2, host3 = ALL
+EOF
diff --git a/plugins/sudoers/regress/cvtsudoers/test18.out.ok b/plugins/sudoers/regress/cvtsudoers/test18.out.ok
new file mode 100644
index 0000000..3055452
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test18.out.ok
@@ -0,0 +1 @@
+%group1 ALL = ALL
diff --git a/plugins/sudoers/regress/cvtsudoers/test18.sh b/plugins/sudoers/regress/cvtsudoers/test18.sh
new file mode 100755
index 0000000..cceed89
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test18.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+#
+# Test filters and pruning
+#
+
+: ${CVTSUDOERS=cvtsudoers}
+
+$CVTSUDOERS -c "" -f sudoers -p -m group=group1,host=somehost <<EOF
+user1, user2, user3, %group1 ALL = ALL
+EOF
diff --git a/plugins/sudoers/regress/cvtsudoers/test19.out.ok b/plugins/sudoers/regress/cvtsudoers/test19.out.ok
new file mode 100644
index 0000000..66b95ac
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test19.out.ok
@@ -0,0 +1,11 @@
+Defaults syslog=auth
+Defaults>root !set_logname
+Defaults:FULLTIMERS !lecture
+Defaults@SERVERS log_year, logfile=/var/log/sudo.log
+Defaults!PAGERS noexec
+
+User_Alias FULLTIMERS = millert, mikef, dowdy
+Cmnd_Alias PAGERS = /usr/bin/more, /usr/bin/pg, /usr/bin/less
+Host_Alias SERVERS = primary, mail, www, ns
+
+FULLTIMERS ALL = NOPASSWD: ALL
diff --git a/plugins/sudoers/regress/cvtsudoers/test19.sh b/plugins/sudoers/regress/cvtsudoers/test19.sh
new file mode 100755
index 0000000..b3c1bf7
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test19.sh
@@ -0,0 +1,8 @@
+#!/bin/sh
+#
+# Test filters and pruning; alias contents don't get pruned
+#
+
+: ${CVTSUDOERS=cvtsudoers}
+
+$CVTSUDOERS -c "" -f sudoers -p -m user=FULLTIMERS,host=SERVERS $TESTDIR/sudoers
diff --git a/plugins/sudoers/regress/cvtsudoers/test2.out.ok b/plugins/sudoers/regress/cvtsudoers/test2.out.ok
new file mode 100644
index 0000000..d99e0e5
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test2.out.ok
@@ -0,0 +1,10 @@
+Defaults syslog=auth
+Defaults>root !set_logname
+Defaults:millert, mikef, dowdy !lecture
+Defaults:millert !authenticate
+Defaults!/usr/bin/more, /usr/bin/pg, /usr/bin/less noexec
+
+millert, mikef, dowdy ALL = NOPASSWD: ALL
+
+ALL orion, perseus, hercules = NOPASSWD: /sbin/umount /CDROM, /sbin/mount -o\
+ nosuid\,nodev /dev/cd0a /CDROM
diff --git a/plugins/sudoers/regress/cvtsudoers/test2.sh b/plugins/sudoers/regress/cvtsudoers/test2.sh
new file mode 100755
index 0000000..70e9553
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test2.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+#
+# Test user and host filters, expanding aliases
+#
+
+: ${CVTSUDOERS=cvtsudoers}
+
+$CVTSUDOERS -c "" -f sudoers -e -m user=millert,host=hercules $TESTDIR/sudoers
+
+exit 0
diff --git a/plugins/sudoers/regress/cvtsudoers/test20.conf b/plugins/sudoers/regress/cvtsudoers/test20.conf
new file mode 100644
index 0000000..b60725c
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test20.conf
@@ -0,0 +1,6 @@
+defaults = global
+expand_aliases = yes
+input_format = sudoers
+match = user=user2
+output_format = sudoers
+prune_matches = yes
diff --git a/plugins/sudoers/regress/cvtsudoers/test20.out.ok b/plugins/sudoers/regress/cvtsudoers/test20.out.ok
new file mode 100644
index 0000000..79b420b
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test20.out.ok
@@ -0,0 +1 @@
+user2 ALL = /usr/bin/id
diff --git a/plugins/sudoers/regress/cvtsudoers/test20.sh b/plugins/sudoers/regress/cvtsudoers/test20.sh
new file mode 100755
index 0000000..e651e16
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test20.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+#
+# Test cvtsudoers.conf
+#
+
+: ${CVTSUDOERS=cvtsudoers}
+
+$CVTSUDOERS -c $TESTDIR/test20.conf <<EOF
+Defaults:SOMEUSERS authenticate, timestamp_timeout=0
+User_Alias SOMEUSERS = user1, user2, user3
+
+SOMEUSERS ALL = /usr/bin/id
+EOF
diff --git a/plugins/sudoers/regress/cvtsudoers/test21.conf b/plugins/sudoers/regress/cvtsudoers/test21.conf
new file mode 100644
index 0000000..01fd3a3
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test21.conf
@@ -0,0 +1,8 @@
+defaults = all
+expand_aliases = no
+input_format = sudoers
+order_increment = 10
+order_start = 1000
+output_format = ldif
+sudoers_base = ou=SUDOers,dc=my-domain,dc=com
+suppress = defaults
diff --git a/plugins/sudoers/regress/cvtsudoers/test21.out.ok b/plugins/sudoers/regress/cvtsudoers/test21.out.ok
new file mode 100644
index 0000000..78285f1
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test21.out.ok
@@ -0,0 +1,24 @@
+dn: cn=ALL,ou=SUDOers,dc=my-domain,dc=com
+objectClass: top
+objectClass: sudoRole
+cn: ALL
+sudoUser: ALL
+sudoHost: ALL
+sudoRunAsUser:
+sudoOption: !authenticate
+sudoCommand: /usr/bin/id
+sudoOrder: 1000
+
+dn: cn=FULLTIMERS,ou=SUDOers,dc=my-domain,dc=com
+objectClass: top
+objectClass: sudoRole
+cn: FULLTIMERS
+sudoUser: user1
+sudoUser: user2
+sudoUser: user3
+sudoHost: ALL
+sudoRunAsUser: ALL
+sudoRunAsGroup: ALL
+sudoCommand: ALL
+sudoOrder: 1010
+
diff --git a/plugins/sudoers/regress/cvtsudoers/test21.sh b/plugins/sudoers/regress/cvtsudoers/test21.sh
new file mode 100755
index 0000000..836a353
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test21.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+#
+# Test cvtsudoers.conf
+#
+
+: ${CVTSUDOERS=cvtsudoers}
+
+$CVTSUDOERS -c $TESTDIR/test21.conf <<EOF
+Defaults authenticate, timestamp_timeout=0
+User_Alias FULLTIMERS = user1, user2, user3
+
+ALL ALL = (:) NOPASSWD:/usr/bin/id
+FULLTIMERS ALL = (ALL:ALL) ALL
+EOF
diff --git a/plugins/sudoers/regress/cvtsudoers/test22.out.ok b/plugins/sudoers/regress/cvtsudoers/test22.out.ok
new file mode 100644
index 0000000..d404815
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test22.out.ok
@@ -0,0 +1,31 @@
+dn: cn=defaults,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: defaults
+description: Default sudoOption's go here
+sudoOption: log_output
+
+dn: cn=root,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: root
+sudoUser: root
+sudoHost: ALL
+sudoRunAsUser: ALL
+sudoRunAsGroup: ALL
+sudoOption: !authenticate
+sudoCommand: ALL
+sudoOrder: 10
+
+dn: cn=%wheel,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: %wheel
+sudoUser: %wheel
+sudoHost: +sudo-hosts
+sudoRunAsUser: ALL
+sudoRunAsGroup: ALL
+sudoOption: !authenticate
+sudoCommand: ALL
+sudoOrder: 20
+
diff --git a/plugins/sudoers/regress/cvtsudoers/test22.sh b/plugins/sudoers/regress/cvtsudoers/test22.sh
new file mode 100755
index 0000000..9b4899b
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test22.sh
@@ -0,0 +1,73 @@
+#!/bin/sh
+#
+# Test LDAP base filtering.
+#
+
+: ${CVTSUDOERS=cvtsudoers}
+
+$CVTSUDOERS -c "" -i ldif -b "ou=SUDOers,dc=sudo,dc=ws" -I 10 -O 10 <<EOF
+dn: dc=sudo,dc=ws
+objectClass: dcObject
+objectClass: organization
+dc: courtesan
+o: Sudo World Headquarters
+description: Sudo World Headquarters
+
+# Organizational Role for Directory Manager
+dn: cn=Manager,dc=sudo,dc=ws
+objectClass: organizationalRole
+cn: Manager
+description: Directory Manager
+
+# SUDOers, sudo.ws
+dn: ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: organizationalUnit
+description: SUDO Configuration Subtree
+ou: SUDOers
+
+# defaults, SUDOers, sudo.ws
+dn: cn=defaults,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: defaults
+description: Default sudoOption's go here
+sudoOption: log_output
+
+# root, SUDOers, sudo.ws
+dn: cn=root,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: root
+sudoUser: root
+sudoRunAsUser: ALL
+sudoRunAsGroup: ALL
+sudoHost: ALL
+sudoCommand: ALL
+sudoOption: !authenticate
+sudoOrder: 10
+
+# %wheel, SUDOers, sudo.ws
+dn: cn=%wheel,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: %wheel
+sudoUser: %wheel
+sudoRunAsUser: ALL
+sudoRunAsGroup: ALL
+sudoHost: +sudo-hosts
+sudoCommand: ALL
+sudoOption: !authenticate
+sudoOrder: 10
+
+# millert, SUDOers, other-domain.com
+dn: cn=millert,ou=SUDOers,dc=other-domain,dc=com
+objectClass: top
+objectClass: sudoRole
+cn: millert
+sudoUser: millert
+sudoRunAsUser: ALL
+sudoRunAsGroup: ALL
+sudoHost: ALL
+sudoOrder: 5
+EOF
diff --git a/plugins/sudoers/regress/cvtsudoers/test23.out.ok b/plugins/sudoers/regress/cvtsudoers/test23.out.ok
new file mode 100644
index 0000000..7fc33c2
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test23.out.ok
@@ -0,0 +1,20 @@
+Defaults logfile=/var/log/sudo
+
+root ALL = (ALL) ALL
+
+%wheel ALL = (ALL) ALL
+
++admins ALL = NOPASSWD: ALL
+
+jack 128.138.204.0/24, 128.138.242.0, 128.138.243.0 = ALL
+
+lisa 128.138.0.0/255.255.0.0 = ALL
+
+operator ALL = /usr/sbin/dump, /usr/sbin/rdump, /usr/sbin/restore,\
+ /usr/sbin/rrestore, /usr/bin/mt,\
+ sha224:0GomF8mNN3wlDt1HD9XldjJ3SNgpFdbjO1+NsQ==\
+ /home/operator/bin/start_backups, /usr/bin/kill, /usr/bin/top,\
+ /usr/sbin/shutdown, /usr/sbin/halt, /usr/sbin/reboot, /usr/sbin/lpc,\
+ /usr/bin/lprm, sudoedit /etc/printcap, /usr/oper/bin/
+
+joe ALL = /usr/bin/su operator
diff --git a/plugins/sudoers/regress/cvtsudoers/test23.sh b/plugins/sudoers/regress/cvtsudoers/test23.sh
new file mode 100755
index 0000000..02b8238
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test23.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+#
+# Test round-tripping of sudoers -> LDIF -> sudoers
+#
+
+: ${CVTSUDOERS=cvtsudoers}
+
+$CVTSUDOERS -c "" -b "ou=SUDOers,dc=sudo,dc=ws" $TESTDIR/test23.out.ok | \
+ $CVTSUDOERS -c "" -i LDIF -f sudoers | grep -v '^#'
diff --git a/plugins/sudoers/regress/cvtsudoers/test24.out.ok b/plugins/sudoers/regress/cvtsudoers/test24.out.ok
new file mode 100644
index 0000000..0951767
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test24.out.ok
@@ -0,0 +1,89 @@
+dn: cn=defaults,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: defaults
+description: Default sudoOption's go here
+sudoOption: logfile=/var/log/sudo
+
+dn: cn=root,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: root
+sudoUser: root
+sudoHost: ALL
+sudoRunAsUser: ALL
+sudoCommand: ALL
+sudoOrder: 1
+
+dn: cn=%wheel,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: %wheel
+sudoUser: %wheel
+sudoHost: ALL
+sudoRunAsUser: ALL
+sudoCommand: ALL
+sudoOrder: 2
+
+dn: cn=\+admins,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: \+admins
+sudoUser: +admins
+sudoHost: ALL
+sudoOption: !authenticate
+sudoCommand: ALL
+sudoOrder: 3
+
+dn: cn=jack,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: jack
+sudoUser: jack
+sudoHost: 128.138.204.0/24
+sudoHost: 128.138.242.0
+sudoHost: 128.138.243.0
+sudoCommand: ALL
+sudoOrder: 4
+
+dn: cn=lisa,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: lisa
+sudoUser: lisa
+sudoHost: 128.138.0.0/255.255.0.0
+sudoCommand: ALL
+sudoOrder: 5
+
+dn: cn=operator,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: operator
+sudoUser: operator
+sudoHost: ALL
+sudoCommand: /usr/sbin/dump
+sudoCommand: /usr/sbin/rdump
+sudoCommand: /usr/sbin/restore
+sudoCommand: /usr/sbin/rrestore
+sudoCommand: /usr/bin/mt
+sudoCommand: sha224:0GomF8mNN3wlDt1HD9XldjJ3SNgpFdbjO1+NsQ== /home/operator/bin/start_backups
+sudoCommand: /usr/bin/kill
+sudoCommand: /usr/bin/top
+sudoCommand: /usr/sbin/shutdown
+sudoCommand: /usr/sbin/halt
+sudoCommand: /usr/sbin/reboot
+sudoCommand: /usr/sbin/lpc
+sudoCommand: /usr/bin/lprm
+sudoCommand: sudoedit /etc/printcap
+sudoCommand: /usr/oper/bin/
+sudoOrder: 6
+
+dn: cn=joe,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: joe
+sudoUser: joe
+sudoHost: ALL
+sudoCommand: /usr/bin/su operator
+sudoOrder: 7
+
diff --git a/plugins/sudoers/regress/cvtsudoers/test24.sh b/plugins/sudoers/regress/cvtsudoers/test24.sh
new file mode 100755
index 0000000..72d9983
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test24.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+#
+# Test round-tripping of LDIF -> sudoers -> LDIF
+#
+
+: ${CVTSUDOERS=cvtsudoers}
+
+$CVTSUDOERS -c "" -i LDIF -f sudoers $TESTDIR/test24.out.ok | \
+ $CVTSUDOERS -c "" -b "ou=SUDOers,dc=sudo,dc=ws"
diff --git a/plugins/sudoers/regress/cvtsudoers/test25.out.ok b/plugins/sudoers/regress/cvtsudoers/test25.out.ok
new file mode 100644
index 0000000..d404815
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test25.out.ok
@@ -0,0 +1,31 @@
+dn: cn=defaults,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: defaults
+description: Default sudoOption's go here
+sudoOption: log_output
+
+dn: cn=root,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: root
+sudoUser: root
+sudoHost: ALL
+sudoRunAsUser: ALL
+sudoRunAsGroup: ALL
+sudoOption: !authenticate
+sudoCommand: ALL
+sudoOrder: 10
+
+dn: cn=%wheel,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: %wheel
+sudoUser: %wheel
+sudoHost: +sudo-hosts
+sudoRunAsUser: ALL
+sudoRunAsGroup: ALL
+sudoOption: !authenticate
+sudoCommand: ALL
+sudoOrder: 20
+
diff --git a/plugins/sudoers/regress/cvtsudoers/test25.sh b/plugins/sudoers/regress/cvtsudoers/test25.sh
new file mode 100755
index 0000000..bbb9b51
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test25.sh
@@ -0,0 +1,53 @@
+#!/bin/sh
+#
+# Test LDIF base64 attribute parsing
+#
+
+: ${CVTSUDOERS=cvtsudoers}
+
+$CVTSUDOERS -c "" -i ldif -b "ou=SUDOers,dc=sudo,dc=ws" -I 10 -O 10 <<EOF
+# defaults, SUDOers, sudo.ws
+dn:: Y249ZGVmYXVsdHMsb3U9U1VET2VycyxkYz1zdWRvLGRjPXdz
+objectClass: top
+objectClass: sudoRole
+cn: defaults
+description: Default sudoOption's go here
+sudoOption:: bG9nX291dHB1dA==
+
+# root, SUDOers, sudo.ws
+dn:: Y249cm9vdCxvdT1TVURPZXJzLGRjPXN1ZG8sZGM9d3M=
+objectClass: top
+objectClass: sudoRole
+cn: root
+sudoUser: root
+sudoRunAsUser: ALL
+sudoRunAsGroup: ALL
+sudoHost: ALL
+sudoCommand: ALL
+sudoOption: !authenticate
+sudoOrder: 10
+
+# %wheel, SUDOers, sudo.ws
+dn:: Y249JXdoZWVsLG91PVNVRE9lcnMsZGM9c3VkbyxkYz13cw==
+objectClass: top
+objectClass: sudoRole
+cn: %wheel
+sudoUser: %wheel
+sudoRunAsUser: ALL
+sudoRunAsGroup: ALL
+sudoHost: +sudo-hosts
+sudoCommand: ALL
+sudoOption: !authenticate
+sudoOrder: 10
+
+# millert, SUDOers, other-domain.com
+dn:: Y249bWlsbGVydCxvdT1TVURPZXJzLGRjPW90aGVyLWRvbWFpbixkYz1jb20=
+objectClass: top
+objectClass: sudoRole
+cn: millert
+sudoUser: millert
+sudoRunAsUser: ALL
+sudoRunAsGroup: ALL
+sudoHost: ALL
+sudoOrder: 5
+EOF
diff --git a/plugins/sudoers/regress/cvtsudoers/test26.err.ok b/plugins/sudoers/regress/cvtsudoers/test26.err.ok
new file mode 100644
index 0000000..a9c5e6a
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test26.err.ok
@@ -0,0 +1,3 @@
+cvtsudoers: invalid LDIF attribute: sudoOption:: bG9nX29@1dHB1dA==
+cvtsudoers: invalid LDIF attribute: dn:: Y249cm9vdCxvdT1TVURPZXJzLGRjPXN1ZG8sZGM9_d3M=
+cvtsudoers: invalid LDIF attribute: dn:: Y249JXdoZWVsLG91PVNVRE9lcnMsZGM9c3VkbyxkYz13cw!==
diff --git a/plugins/sudoers/regress/cvtsudoers/test26.out.ok b/plugins/sudoers/regress/cvtsudoers/test26.out.ok
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test26.out.ok
diff --git a/plugins/sudoers/regress/cvtsudoers/test26.sh b/plugins/sudoers/regress/cvtsudoers/test26.sh
new file mode 100755
index 0000000..08c0246
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test26.sh
@@ -0,0 +1,49 @@
+#!/bin/sh
+#
+# Test LDIF invalid base64 attribute parsing
+#
+
+: ${CVTSUDOERS=cvtsudoers}
+
+$CVTSUDOERS -c "" -i ldif -b "ou=SUDOers,dc=sudo,dc=ws" -I 10 -O 10 <<EOF
+# defaults, SUDOers, sudo.ws
+dn:: Y249ZGVmYXVsdHMsb3U9U1VET2VycyxkYz1zdWRvLGRjPXdz
+objectClass: top
+objectClass: sudoRole
+cn: defaults
+description: Default sudoOption's go here
+sudoOption:: bG9nX29@1dHB1dA==
+
+# root, SUDOers, sudo.ws
+dn:: Y249cm9vdCxvdT1TVURPZXJzLGRjPXN1ZG8sZGM9_d3M=
+objectClass: top
+objectClass: sudoRole
+cn: root
+sudoUser: root
+sudoRunAsUser: ALL
+sudoRunAsGroup: ALL
+sudoHost: ALL
+sudoCommand: ALL
+sudoOption: !authenticate
+sudoOrder: 10
+
+# %wheel, SUDOers, sudo.ws
+dn:: Y249JXdoZWVsLG91PVNVRE9lcnMsZGM9c3VkbyxkYz13cw!==
+objectClass: top
+objectClass: sudoRole
+cn: %wheel
+sudoUser: %wheel
+sudoRunAsUser: ALL
+sudoRunAsGroup: ALL
+sudoHost: +sudo-hosts
+sudoCommand: ALL
+sudoOption: !authenticate
+sudoOrder: 10
+EOF
+
+# cvtsudoers should exit with an error
+if [ $? -eq 0 ]; then
+ exit 1
+else
+ exit 0
+fi
diff --git a/plugins/sudoers/regress/cvtsudoers/test27.out.ok b/plugins/sudoers/regress/cvtsudoers/test27.out.ok
new file mode 100644
index 0000000..ab9c948
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test27.out.ok
@@ -0,0 +1,16 @@
+dn:: Y249ZGVmYXVsdHMsb3U9U1VET2Vyc8KpLGRjPXN1ZG8sZGM9d3M=
+objectClass: top
+objectClass: sudoRole
+cn: defaults
+description: Default sudoOption's go here
+sudoOption:: YmFkcGFzc19tZXNzYWdlPUJhZCBwYXNzd29yZMKh
+
+dn:: Y249cm9vdCxvdT1TVURPZXJzwqksZGM9c3VkbyxkYz13cw==
+objectClass: top
+objectClass: sudoRole
+cn: root
+sudoUser: root
+sudoHost: ALL
+sudoCommand: ALL
+sudoOrder: 1
+
diff --git a/plugins/sudoers/regress/cvtsudoers/test27.sh b/plugins/sudoers/regress/cvtsudoers/test27.sh
new file mode 100755
index 0000000..c1a2df3
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test27.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+#
+# Test base64 encoding of non-safe strings
+#
+
+: ${CVTSUDOERS=cvtsudoers}
+
+$CVTSUDOERS -c "" -b "ou=SUDOers©,dc=sudo,dc=ws" <<EOF
+Defaults badpass_message="Bad password¡"
+
+root ALL = ALL
+EOF
diff --git a/plugins/sudoers/regress/cvtsudoers/test28.out.ok b/plugins/sudoers/regress/cvtsudoers/test28.out.ok
new file mode 100644
index 0000000..ba19cb9
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test28.out.ok
@@ -0,0 +1,10 @@
+Defaults log_output
+
+# sudoRole millert
+millert ALL = (ALL : ALL) ALL
+
+# sudoRole root
+root ALL = (ALL : ALL) NOPASSWD: ALL
+
+# sudoRole %wheel
+%wheel +sudo-hosts = (ALL : ALL) NOPASSWD: ALL
diff --git a/plugins/sudoers/regress/cvtsudoers/test28.sh b/plugins/sudoers/regress/cvtsudoers/test28.sh
new file mode 100755
index 0000000..0d62ace
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test28.sh
@@ -0,0 +1,74 @@
+#!/bin/sh
+#
+# Test LDAP sudoOrder when converting to sudoers.
+#
+
+: ${CVTSUDOERS=cvtsudoers}
+
+$CVTSUDOERS -c "" -i ldif -f sudoers <<EOF
+dn: dc=sudo,dc=ws
+objectClass: dcObject
+objectClass: organization
+dc: courtesan
+o: Sudo World Headquarters
+description: Sudo World Headquarters
+
+# Organizational Role for Directory Manager
+dn: cn=Manager,dc=sudo,dc=ws
+objectClass: organizationalRole
+cn: Manager
+description: Directory Manager
+
+# SUDOers, sudo.ws
+dn: ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: organizationalUnit
+description: SUDO Configuration Subtree
+ou: SUDOers
+
+# defaults, SUDOers, sudo.ws
+dn: cn=defaults,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: defaults
+description: Default sudoOption's go here
+sudoOption: log_output
+
+# root, SUDOers, sudo.ws
+dn: cn=root,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: root
+sudoUser: root
+sudoRunAsUser: ALL
+sudoRunAsGroup: ALL
+sudoHost: ALL
+sudoCommand: ALL
+sudoOption: !authenticate
+sudoOrder: 10
+
+# %wheel, SUDOers, sudo.ws
+dn: cn=%wheel,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: %wheel
+sudoUser: %wheel
+sudoRunAsUser: ALL
+sudoRunAsGroup: ALL
+sudoHost: +sudo-hosts
+sudoCommand: ALL
+sudoOption: !authenticate
+sudoOrder: 20
+
+# millert, SUDOers, sudo.ws
+dn: cn=millert,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: millert
+sudoUser: millert
+sudoRunAsUser: ALL
+sudoRunAsGroup: ALL
+sudoHost: ALL
+sudoCommand: ALL
+sudoOrder: 5
+EOF
diff --git a/plugins/sudoers/regress/cvtsudoers/test29.out.ok b/plugins/sudoers/regress/cvtsudoers/test29.out.ok
new file mode 100644
index 0000000..c168898
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test29.out.ok
@@ -0,0 +1,4 @@
+Defaults log_output
+
+# sudoRole millert, millert2
+millert ALL = (ALL : ALL) ALL, NOPASSWD: ALL
diff --git a/plugins/sudoers/regress/cvtsudoers/test29.sh b/plugins/sudoers/regress/cvtsudoers/test29.sh
new file mode 100755
index 0000000..983cf91
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test29.sh
@@ -0,0 +1,61 @@
+#!/bin/sh
+#
+# Test LDAP sudoOrder when converting to sudoers.
+#
+
+: ${CVTSUDOERS=cvtsudoers}
+
+$CVTSUDOERS -c "" -i ldif -f sudoers <<EOF
+dn: dc=sudo,dc=ws
+objectClass: dcObject
+objectClass: organization
+dc: courtesan
+o: Sudo World Headquarters
+description: Sudo World Headquarters
+
+# Organizational Role for Directory Manager
+dn: cn=Manager,dc=sudo,dc=ws
+objectClass: organizationalRole
+cn: Manager
+description: Directory Manager
+
+# SUDOers, sudo.ws
+dn: ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: organizationalUnit
+description: SUDO Configuration Subtree
+ou: SUDOers
+
+# defaults, SUDOers, sudo.ws
+dn: cn=defaults,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: defaults
+description: Default sudoOption's go here
+sudoOption: log_output
+
+# millert, SUDOers, sudo.ws
+dn: cn=millert,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: millert
+sudoUser: millert
+sudoRunAsUser: ALL
+sudoRunAsGroup: ALL
+sudoHost: ALL
+sudoCommand: ALL
+sudoOrder: 5
+
+# millert2, SUDOers, sudo.ws
+dn: cn=millert2,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: millert2
+sudoUser: millert
+sudoRunAsUser: ALL
+sudoRunAsGroup: ALL
+sudoHost: ALL
+sudoCommand: ALL
+sudoOption: !authenticate
+sudoOrder: 10
+EOF
diff --git a/plugins/sudoers/regress/cvtsudoers/test3.out.ok b/plugins/sudoers/regress/cvtsudoers/test3.out.ok
new file mode 100644
index 0000000..8a37975
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test3.out.ok
@@ -0,0 +1,7 @@
+Defaults syslog=auth
+Defaults>root !set_logname
+Defaults!PAGERS noexec
+
+Cmnd_Alias PAGERS = /usr/bin/more, /usr/bin/pg, /usr/bin/less
+
+%wheel ALL = (ALL) ALL
diff --git a/plugins/sudoers/regress/cvtsudoers/test3.sh b/plugins/sudoers/regress/cvtsudoers/test3.sh
new file mode 100755
index 0000000..8e42cbc
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test3.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+#
+# Test group and host filters
+#
+
+: ${CVTSUDOERS=cvtsudoers}
+
+$CVTSUDOERS -c "" -f sudoers -m group=wheel,host=blackhole $TESTDIR/sudoers
+
+exit 0
diff --git a/plugins/sudoers/regress/cvtsudoers/test30.out.ok b/plugins/sudoers/regress/cvtsudoers/test30.out.ok
new file mode 100644
index 0000000..009a54e
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test30.out.ok
@@ -0,0 +1,26 @@
+{
+ "User_Specs": [
+ {
+ "User_List": [
+ { "username": "user1" },
+ { "username": "user2" },
+ { "username": "user3" }
+ ],
+ "Host_List": [
+ { "hostname": "ALL" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "Commands": [
+ { "command": "/path/to/cmda" },
+ {
+ "command": "/path/to/cmdb",
+ "negated": true
+ },
+ { "command": "/path/to/cmdc" }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/plugins/sudoers/regress/cvtsudoers/test30.sh b/plugins/sudoers/regress/cvtsudoers/test30.sh
new file mode 100755
index 0000000..c5f7615
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test30.sh
@@ -0,0 +1,15 @@
+#!/bin/sh
+#
+# Test alias expansion when converting to JSON.
+# See https://bugzilla.sudo.ws/show_bug.cgi?id=853
+#
+
+: ${CVTSUDOERS=cvtsudoers}
+
+$CVTSUDOERS -c "" -e -f json <<EOF
+Cmnd_Alias CMDA=/path/to/cmda
+Cmnd_Alias CMDB=/path/to/cmdb
+Cmnd_Alias CMDC=/path/to/cmdc
+User_Alias USERS=user1,user2,user3
+USERS ALL=CMDA,!CMDB,CMDC
+EOF
diff --git a/plugins/sudoers/regress/cvtsudoers/test31.conf b/plugins/sudoers/regress/cvtsudoers/test31.conf
new file mode 100644
index 0000000..345dbfc
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test31.conf
@@ -0,0 +1,9 @@
+defaults = all
+expand_aliases = no
+input_format = sudoers
+order_increment = 5
+order_padding = 2
+order_start = 1000
+output_format = ldif
+sudoers_base = ou=SUDOers,dc=my-domain,dc=com
+suppress = defaults
diff --git a/plugins/sudoers/regress/cvtsudoers/test31.out.ok b/plugins/sudoers/regress/cvtsudoers/test31.out.ok
new file mode 100644
index 0000000..41ffd1b
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test31.out.ok
@@ -0,0 +1,24 @@
+dn: cn=ALL,ou=SUDOers,dc=my-domain,dc=com
+objectClass: top
+objectClass: sudoRole
+cn: ALL
+sudoUser: ALL
+sudoHost: ALL
+sudoRunAsUser:
+sudoOption: !authenticate
+sudoCommand: /usr/bin/id
+sudoOrder: 100000
+
+dn: cn=FULLTIMERS,ou=SUDOers,dc=my-domain,dc=com
+objectClass: top
+objectClass: sudoRole
+cn: FULLTIMERS
+sudoUser: user1
+sudoUser: user2
+sudoUser: user3
+sudoHost: ALL
+sudoRunAsUser: ALL
+sudoRunAsGroup: ALL
+sudoCommand: ALL
+sudoOrder: 100005
+
diff --git a/plugins/sudoers/regress/cvtsudoers/test31.sh b/plugins/sudoers/regress/cvtsudoers/test31.sh
new file mode 100644
index 0000000..38dd93c
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test31.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+#
+# Test cvtsudoers.conf with padding
+#
+
+: ${CVTSUDOERS=cvtsudoers}
+
+$CVTSUDOERS -c $TESTDIR/test31.conf <<EOF
+Defaults authenticate, timestamp_timeout=0
+User_Alias FULLTIMERS = user1, user2, user3
+
+ALL ALL = (:) NOPASSWD:/usr/bin/id
+FULLTIMERS ALL = (ALL:ALL) ALL
+EOF
diff --git a/plugins/sudoers/regress/cvtsudoers/test32.err.ok b/plugins/sudoers/regress/cvtsudoers/test32.err.ok
new file mode 100644
index 0000000..c001436
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test32.err.ok
@@ -0,0 +1 @@
+cvtsudoers: too many sudoers entries, maximum 10
diff --git a/plugins/sudoers/regress/cvtsudoers/test32.out.ok b/plugins/sudoers/regress/cvtsudoers/test32.out.ok
new file mode 100644
index 0000000..9900199
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test32.out.ok
@@ -0,0 +1,119 @@
+dn: cn=user0,ou=SUDOers,dc=my-domain,dc=com
+objectClass: top
+objectClass: sudoRole
+cn: user0
+sudoUser: user0
+sudoHost: ALL
+sudoRunAsUser: ALL
+sudoRunAsGroup: ALL
+sudoCommand: ALL
+sudoOrder: 10000
+
+dn: cn=user1,ou=SUDOers,dc=my-domain,dc=com
+objectClass: top
+objectClass: sudoRole
+cn: user1
+sudoUser: user1
+sudoHost: ALL
+sudoRunAsUser: ALL
+sudoRunAsGroup: ALL
+sudoCommand: ALL
+sudoOrder: 10001
+
+dn: cn=user2,ou=SUDOers,dc=my-domain,dc=com
+objectClass: top
+objectClass: sudoRole
+cn: user2
+sudoUser: user2
+sudoHost: ALL
+sudoRunAsUser: ALL
+sudoRunAsGroup: ALL
+sudoCommand: ALL
+sudoOrder: 10002
+
+dn: cn=user3,ou=SUDOers,dc=my-domain,dc=com
+objectClass: top
+objectClass: sudoRole
+cn: user3
+sudoUser: user3
+sudoHost: ALL
+sudoRunAsUser: ALL
+sudoRunAsGroup: ALL
+sudoCommand: ALL
+sudoOrder: 10003
+
+dn: cn=user4,ou=SUDOers,dc=my-domain,dc=com
+objectClass: top
+objectClass: sudoRole
+cn: user4
+sudoUser: user4
+sudoHost: ALL
+sudoRunAsUser: ALL
+sudoRunAsGroup: ALL
+sudoCommand: ALL
+sudoOrder: 10004
+
+dn: cn=user5,ou=SUDOers,dc=my-domain,dc=com
+objectClass: top
+objectClass: sudoRole
+cn: user5
+sudoUser: user5
+sudoHost: ALL
+sudoRunAsUser: ALL
+sudoRunAsGroup: ALL
+sudoCommand: ALL
+sudoOrder: 10005
+
+dn: cn=user6,ou=SUDOers,dc=my-domain,dc=com
+objectClass: top
+objectClass: sudoRole
+cn: user6
+sudoUser: user6
+sudoHost: ALL
+sudoRunAsUser: ALL
+sudoRunAsGroup: ALL
+sudoCommand: ALL
+sudoOrder: 10006
+
+dn: cn=user7,ou=SUDOers,dc=my-domain,dc=com
+objectClass: top
+objectClass: sudoRole
+cn: user7
+sudoUser: user7
+sudoHost: ALL
+sudoRunAsUser: ALL
+sudoRunAsGroup: ALL
+sudoCommand: ALL
+sudoOrder: 10007
+
+dn: cn=user8,ou=SUDOers,dc=my-domain,dc=com
+objectClass: top
+objectClass: sudoRole
+cn: user8
+sudoUser: user8
+sudoHost: ALL
+sudoRunAsUser: ALL
+sudoRunAsGroup: ALL
+sudoCommand: ALL
+sudoOrder: 10008
+
+dn: cn=user9,ou=SUDOers,dc=my-domain,dc=com
+objectClass: top
+objectClass: sudoRole
+cn: user9
+sudoUser: user9
+sudoHost: ALL
+sudoRunAsUser: ALL
+sudoRunAsGroup: ALL
+sudoCommand: ALL
+sudoOrder: 10009
+
+dn: cn=user10,ou=SUDOers,dc=my-domain,dc=com
+objectClass: top
+objectClass: sudoRole
+cn: user10
+sudoUser: user10
+sudoHost: ALL
+sudoRunAsUser: ALL
+sudoRunAsGroup: ALL
+sudoCommand: ALL
diff --git a/plugins/sudoers/regress/cvtsudoers/test32.sh b/plugins/sudoers/regress/cvtsudoers/test32.sh
new file mode 100644
index 0000000..2119da1
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test32.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+#
+# Test cvtsudoers.conf with invalid padding
+#
+
+: ${CVTSUDOERS=cvtsudoers}
+
+$CVTSUDOERS -c "" -b "ou=SUDOers,dc=my-domain,dc=com" -O 1000 -P 1 <<EOF
+user0 ALL = (ALL:ALL) ALL
+user1 ALL = (ALL:ALL) ALL
+user2 ALL = (ALL:ALL) ALL
+user3 ALL = (ALL:ALL) ALL
+user4 ALL = (ALL:ALL) ALL
+user5 ALL = (ALL:ALL) ALL
+user6 ALL = (ALL:ALL) ALL
+user7 ALL = (ALL:ALL) ALL
+user8 ALL = (ALL:ALL) ALL
+user9 ALL = (ALL:ALL) ALL
+user10 ALL = (ALL:ALL) ALL
+EOF
+
+exit 0
diff --git a/plugins/sudoers/regress/cvtsudoers/test33.out.ok b/plugins/sudoers/regress/cvtsudoers/test33.out.ok
new file mode 100644
index 0000000..6584701
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test33.out.ok
@@ -0,0 +1,7 @@
+Defaults log_output
+
+# sudoRole root
+root ALL = (ALL : ALL) NOPASSWD: ALL
+
+# sudoRole millert
+millert ALL = (ALL, !bin, !root : ALL, !wheel) ALL
diff --git a/plugins/sudoers/regress/cvtsudoers/test33.sh b/plugins/sudoers/regress/cvtsudoers/test33.sh
new file mode 100755
index 0000000..e7c2f2f
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test33.sh
@@ -0,0 +1,62 @@
+#!/bin/sh
+#
+# Test LDAP negated sudoRunAsUser and sudoRunAsGroup converted to sudoers.
+#
+
+: ${CVTSUDOERS=cvtsudoers}
+
+$CVTSUDOERS -c "" -i ldif -f sudoers <<EOF
+dn: dc=sudo,dc=ws
+objectClass: dcObject
+objectClass: organization
+dc: courtesan
+o: Sudo World Headquarters
+description: Sudo World Headquarters
+
+# Organizational Role for Directory Manager
+dn: cn=Manager,dc=sudo,dc=ws
+objectClass: organizationalRole
+cn: Manager
+description: Directory Manager
+
+# SUDOers, sudo.ws
+dn: ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: organizationalUnit
+description: SUDO Configuration Subtree
+ou: SUDOers
+
+# defaults, SUDOers, sudo.ws
+dn: cn=defaults,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: defaults
+description: Default sudoOption's go here
+sudoOption: log_output
+
+# root, SUDOers, sudo.ws
+dn: cn=root,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: root
+sudoUser: root
+sudoRunAsUser: ALL
+sudoRunAsGroup: ALL
+sudoHost: ALL
+sudoCommand: ALL
+sudoOption: !authenticate
+
+# millert, SUDOers, sudo.ws
+dn: cn=millert,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: millert
+sudoUser: millert
+sudoRunAsUser: !bin
+sudoRunAsUser: !root
+sudoRunAsUser: ALL
+sudoRunAsGroup: ALL
+sudoRunAsGroup: !wheel
+sudoHost: ALL
+sudoCommand: ALL
+EOF
diff --git a/plugins/sudoers/regress/cvtsudoers/test4.out.ok b/plugins/sudoers/regress/cvtsudoers/test4.out.ok
new file mode 100644
index 0000000..f8e7d2e
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test4.out.ok
@@ -0,0 +1,5 @@
+Defaults syslog=auth
+Defaults>root !set_logname
+Defaults!/usr/bin/more, /usr/bin/pg, /usr/bin/less noexec
+
+%wheel ALL = (ALL) ALL
diff --git a/plugins/sudoers/regress/cvtsudoers/test4.sh b/plugins/sudoers/regress/cvtsudoers/test4.sh
new file mode 100755
index 0000000..860eff4
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test4.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+#
+# Test group and host filters, expanding aliases
+#
+
+: ${CVTSUDOERS=cvtsudoers}
+
+$CVTSUDOERS -c "" -f sudoers -e -m group=wheel,host=blackhole $TESTDIR/sudoers
+
+exit 0
diff --git a/plugins/sudoers/regress/cvtsudoers/test5.out.ok b/plugins/sudoers/regress/cvtsudoers/test5.out.ok
new file mode 100644
index 0000000..d209fdf
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test5.out.ok
@@ -0,0 +1,6 @@
+Defaults syslog=auth
+Defaults>root !set_logname
+Defaults:FULLTIMERS !lecture
+Defaults:millert !authenticate
+Defaults@SERVERS log_year, logfile=/var/log/sudo.log
+Defaults!PAGERS noexec
diff --git a/plugins/sudoers/regress/cvtsudoers/test5.sh b/plugins/sudoers/regress/cvtsudoers/test5.sh
new file mode 100755
index 0000000..dd7782d
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test5.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+#
+# Test defaults type filtering
+#
+
+: ${CVTSUDOERS=cvtsudoers}
+
+$CVTSUDOERS -c "" -f sudoers -s aliases,privileges -d all $TESTDIR/sudoers
+
+exit 0
diff --git a/plugins/sudoers/regress/cvtsudoers/test6.out.ok b/plugins/sudoers/regress/cvtsudoers/test6.out.ok
new file mode 100644
index 0000000..5e65e61
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test6.out.ok
@@ -0,0 +1 @@
+Defaults syslog=auth
diff --git a/plugins/sudoers/regress/cvtsudoers/test6.sh b/plugins/sudoers/regress/cvtsudoers/test6.sh
new file mode 100755
index 0000000..a6c7ec4
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test6.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+#
+# Test global defaults filtering
+#
+
+: ${CVTSUDOERS=cvtsudoers}
+
+$CVTSUDOERS -c "" -f sudoers -s aliases,privileges -d global $TESTDIR/sudoers
+
+exit 0
diff --git a/plugins/sudoers/regress/cvtsudoers/test7.out.ok b/plugins/sudoers/regress/cvtsudoers/test7.out.ok
new file mode 100644
index 0000000..381de43
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test7.out.ok
@@ -0,0 +1,2 @@
+Defaults:FULLTIMERS !lecture
+Defaults:millert !authenticate
diff --git a/plugins/sudoers/regress/cvtsudoers/test7.sh b/plugins/sudoers/regress/cvtsudoers/test7.sh
new file mode 100755
index 0000000..2f1a301
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test7.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+#
+# Test user defaults filtering
+#
+
+: ${CVTSUDOERS=cvtsudoers}
+
+$CVTSUDOERS -c "" -f sudoers -s aliases,privileges -d user $TESTDIR/sudoers
+
+exit 0
diff --git a/plugins/sudoers/regress/cvtsudoers/test8.out.ok b/plugins/sudoers/regress/cvtsudoers/test8.out.ok
new file mode 100644
index 0000000..7079ee0
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test8.out.ok
@@ -0,0 +1 @@
+Defaults>root !set_logname
diff --git a/plugins/sudoers/regress/cvtsudoers/test8.sh b/plugins/sudoers/regress/cvtsudoers/test8.sh
new file mode 100755
index 0000000..d00f90e
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test8.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+#
+# Test runas defaults filtering
+#
+
+: ${CVTSUDOERS=cvtsudoers}
+
+$CVTSUDOERS -c "" -f sudoers -s aliases,privileges -d runas $TESTDIR/sudoers
+
+exit 0
diff --git a/plugins/sudoers/regress/cvtsudoers/test9.out.ok b/plugins/sudoers/regress/cvtsudoers/test9.out.ok
new file mode 100644
index 0000000..d2a39c4
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test9.out.ok
@@ -0,0 +1 @@
+Defaults@SERVERS log_year, logfile=/var/log/sudo.log
diff --git a/plugins/sudoers/regress/cvtsudoers/test9.sh b/plugins/sudoers/regress/cvtsudoers/test9.sh
new file mode 100755
index 0000000..b668e1f
--- /dev/null
+++ b/plugins/sudoers/regress/cvtsudoers/test9.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+#
+# Test host defaults filtering
+#
+
+: ${CVTSUDOERS=cvtsudoers}
+
+$CVTSUDOERS -c "" -f sudoers -s aliases,privileges -d host $TESTDIR/sudoers
+
+exit 0
diff --git a/plugins/sudoers/regress/env_match/check_env_pattern.c b/plugins/sudoers/regress/env_match/check_env_pattern.c
new file mode 100644
index 0000000..ce2c267
--- /dev/null
+++ b/plugins/sudoers/regress/env_match/check_env_pattern.c
@@ -0,0 +1,76 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * Copyright (c) 2017 Todd C. Miller <Todd.Miller@sudo.ws>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "sudoers.h"
+
+sudo_dso_public int main(int argc, char *argv[]);
+
+int
+main(int argc, char *argv[])
+{
+ FILE *fp = stdin;
+ char pattern[1024], string[1024];
+ int errors = 0, tests = 0, got, want;
+
+ initprogname(argc > 0 ? argv[0] : "check_env_pattern");
+
+ if (argc > 1) {
+ if ((fp = fopen(argv[1], "r")) == NULL) {
+ perror(argv[1]);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ /*
+ * Read in test file, which is formatted thusly:
+ *
+ * pattern string 1/0
+ *
+ */
+ for (;;) {
+ bool full_match = false;
+
+ got = fscanf(fp, "%s %s %d\n", pattern, string, &want);
+ if (got == EOF)
+ break;
+ if (got == 3) {
+ got = matches_env_pattern(pattern, string, &full_match);
+ if (full_match)
+ got++;
+ if (got != want) {
+ fprintf(stderr,
+ "%s: %s %s: want %d, got %d\n",
+ getprogname(), pattern, string, want, got);
+ errors++;
+ }
+ tests++;
+ }
+ }
+ if (tests != 0) {
+ printf("%s: %d test%s run, %d errors, %d%% success rate\n",
+ getprogname(), tests, tests == 1 ? "" : "s", errors,
+ (tests - errors) * 100 / tests);
+ }
+ exit(errors);
+}
diff --git a/plugins/sudoers/regress/env_match/data b/plugins/sudoers/regress/env_match/data
new file mode 100644
index 0000000..ea28b1b
--- /dev/null
+++ b/plugins/sudoers/regress/env_match/data
@@ -0,0 +1,22 @@
+foo=(){false;} foo=(){false;} 2
+foo foo=(){false;} 1
+foo= foo=(){false;} 0
+foo=* foo=(){false;} 1
+foo=(* foo=(){false;} 2
+foo=()* foo=(){false;} 2
+foo=*()* foo=(){false;} 2
+foo() foo()=a 1
+foo*() foo()=b 1
+foo*()* foo()= 1
+foo()* foo()= 1
+foo* foo()= 1
+fo*o*() foo()= 1
+fo*o*() fooo()== 1
+fo*o*() foooo()= 1
+fo*o*() foooo 0
+MYPATH=*:/mydir:* MYPATH=/dir1/subdir1:/mydir:/dir2:/dir3/subdir2 2
+MYPATH=*:/mydir:** MYPATH=/dir1/subdir1:/mydir:/dir2:/dir3/subdir2 2
+MYPATH=*:/mdir:* MYPATH=/dir1/subdir1:/mydir:/dir2:/dir3/subdir2 0
+a*a*a*a*a*a* aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=b 1
+a*a*a*a*a*a*=b* aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=b 2
+a*a*a*a*a*a*=* aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=c 1
diff --git a/plugins/sudoers/regress/exptilde/check_exptilde.c b/plugins/sudoers/regress/exptilde/check_exptilde.c
new file mode 100644
index 0000000..02d4bf0
--- /dev/null
+++ b/plugins/sudoers/regress/exptilde/check_exptilde.c
@@ -0,0 +1,101 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * Copyright (c) 2020 Todd C. Miller <Todd.Miller@sudo.ws>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <config.h>
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#define SUDO_ERROR_WRAP 0
+
+#include "sudoers.h"
+
+#include <def_data.c>
+
+struct sudo_user sudo_user;
+
+struct test_data {
+ char *input;
+ char *output;
+ char *user;
+ bool result;
+} test_data[] = {
+ { "foo/bar", NULL, NULL, false },
+ { "~root", "/", NULL, true },
+ { "~", "/home/millert", "millert", true },
+ { "~millert", "/home/millert", "millert", true },
+ { NULL }
+};
+
+sudo_dso_public int main(int argc, char *argv[]);
+
+int
+main(int argc, char *argv[])
+{
+ int ntests = 0, errors = 0;
+ struct test_data *td;
+ struct passwd *pw;
+ char *path = NULL;
+ bool result;
+
+ initprogname(argc > 0 ? argv[0] : "check_exptilde");
+
+ /* Prime the passwd cache */
+ pw = sudo_mkpwent("root", 0, 0, "/", "/bin/sh");
+ if (pw == NULL)
+ sudo_fatalx("unable to create passwd entry for root");
+ sudo_pw_delref(pw);
+
+ pw = sudo_mkpwent("millert", 8036, 20, "/home/millert", "/bin/tcsh");
+ if (pw == NULL)
+ sudo_fatalx("unable to create passwd entry for millert");
+ sudo_pw_delref(pw);
+
+ for (td = test_data; td->input != NULL; td++) {
+ ntests++;
+ free(path);
+ if ((path = strdup(td->input)) == NULL)
+ sudo_fatal(NULL);
+ result = expand_tilde(&path, td->user);
+ if (result != td->result) {
+ errors++;
+ if (result) {
+ sudo_warnx("unexpected success: input %s, output %s",
+ td->input, path);
+ } else {
+ sudo_warnx("unexpected failure: input %s", td->input);
+ }
+ continue;
+ }
+ if (td->result && strcmp(path, td->output) != 0) {
+ errors++;
+ sudo_warnx("incorrect output for input %s: expected %s, got %s",
+ td->input, td->output, path);
+ continue;
+ }
+ }
+
+ printf("%s: %d tests run, %d errors, %d%% success rate\n", getprogname(),
+ ntests, errors, (ntests - errors) * 100 / ntests);
+
+ exit(errors);
+}
diff --git a/plugins/sudoers/regress/iolog_plugin/check_iolog_plugin.c b/plugins/sudoers/regress/iolog_plugin/check_iolog_plugin.c
new file mode 100644
index 0000000..1d21a4d
--- /dev/null
+++ b/plugins/sudoers/regress/iolog_plugin/check_iolog_plugin.c
@@ -0,0 +1,430 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * Copyright (c) 2018-2020 Todd C. Miller <Todd.Miller@sudo.ws>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <pwd.h>
+#include <unistd.h>
+
+#define SUDO_ERROR_WRAP 0
+
+#include "sudoers.h"
+#include "sudo_eventlog.h"
+#include "sudo_iolog.h"
+#include "sudo_plugin.h"
+
+#include <def_data.c> /* for iolog_path.c */
+
+extern struct io_plugin sudoers_io;
+
+struct sudo_user sudo_user;
+struct passwd *list_pw;
+sudo_printf_t sudo_printf;
+sudo_conv_t sudo_conv;
+
+sudo_dso_public int main(int argc, char *argv[], char *envp[]);
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: %s pathname\n", getprogname());
+ exit(EXIT_FAILURE);
+}
+
+static int
+sudo_printf_int(int msg_type, const char *fmt, ...)
+{
+ va_list ap;
+ int len;
+
+ switch (msg_type) {
+ case SUDO_CONV_INFO_MSG:
+ va_start(ap, fmt);
+ len = vfprintf(stdout, fmt, ap);
+ va_end(ap);
+ break;
+ case SUDO_CONV_ERROR_MSG:
+ va_start(ap, fmt);
+ len = vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ break;
+ default:
+ len = -1;
+ errno = EINVAL;
+ break;
+ }
+
+ return len;
+}
+
+static bool
+validate_iolog_info(const char *log_dir, bool legacy)
+{
+ struct eventlog *evlog;
+ time_t now;
+
+ time(&now);
+
+ /* Parse log file. */
+ if ((evlog = iolog_parse_loginfo(-1, log_dir)) == NULL)
+ return false;
+
+ if (evlog->cwd == NULL || strcmp(evlog->cwd, "/") != 0) {
+ sudo_warnx("bad cwd: want \"/\", got \"%s\"",
+ evlog->cwd ? evlog->cwd : "NULL");
+ return false;
+ }
+
+ /* No host in the legacy log file. */
+ if (!legacy) {
+ if (evlog->submithost == NULL || strcmp(evlog->submithost, "localhost") != 0) {
+ sudo_warnx("bad host: want \"localhost\", got \"%s\"",
+ evlog->submithost ? evlog->submithost : "NULL");
+ return false;
+ }
+ }
+
+ if (evlog->submituser == NULL || strcmp(evlog->submituser, "nobody") != 0) {
+ sudo_warnx("bad user: want \"nobody\" got \"%s\"",
+ evlog->submituser ? evlog->submituser : "NULL");
+ return false;
+ }
+
+ if (evlog->runuser == NULL || strcmp(evlog->runuser, "root") != 0) {
+ sudo_warnx("bad runuser: want \"root\" got \"%s\"",
+ evlog->runuser ? evlog->runuser : "NULL");
+ return false;
+ }
+
+ /* No runas group specified, should be NULL. */
+ if (evlog->rungroup != NULL) {
+ sudo_warnx("bad rungroup: want \"\" got \"%s\"", evlog->rungroup);
+ return false;
+ }
+
+ if (evlog->ttyname == NULL || strcmp(evlog->ttyname, "/dev/console") != 0) {
+ sudo_warnx("bad tty: want \"/dev/console\" got \"%s\"",
+ evlog->ttyname ? evlog->ttyname : "NULL");
+ return false;
+ }
+
+ if (evlog->command == NULL || strcmp(evlog->command, "/usr/bin/id") != 0) {
+ sudo_warnx("bad command: want \"/usr/bin/id\" got \"%s\"",
+ evlog->command ? evlog->command : "NULL");
+ return false;
+ }
+
+ if (evlog->lines != 24) {
+ sudo_warnx("bad lines: want 24 got %d", evlog->lines);
+ return false;
+ }
+
+ if (evlog->columns != 80) {
+ sudo_warnx("bad columns: want 80 got %d", evlog->columns);
+ return false;
+ }
+
+ if (evlog->submit_time.tv_sec < now - 10 || evlog->submit_time.tv_sec > now + 10) {
+ sudo_warnx("bad submit_time: want %lld got %lld", (long long)now,
+ (long long)evlog->submit_time.tv_sec);
+ return false;
+ }
+
+ eventlog_free(evlog);
+
+ return true;
+}
+
+bool
+validate_timing(FILE *fp, int recno, int type, unsigned int p1, unsigned int p2)
+{
+ struct timing_closure timing;
+ char buf[LINE_MAX];
+
+ if (!fgets(buf, sizeof(buf), fp)) {
+ sudo_warn("unable to read timing file");
+ return false;
+ }
+ buf[strcspn(buf, "\n")] = '\0';
+ if (!iolog_parse_timing(buf, &timing)) {
+ sudo_warnx("invalid timing file line: %s", buf);
+ return false;
+ }
+ if (timing.event != type) {
+ sudo_warnx("record %d: want type %d, got type %d", recno, type,
+ timing.event);
+ return false;
+ }
+ if (type == IO_EVENT_WINSIZE) {
+ if (timing.u.winsize.lines != (int)p1) {
+ sudo_warnx("record %d: want %u lines, got %u", recno, p1,
+ timing.u.winsize.lines);
+ return false;
+ }
+ if (timing.u.winsize.cols != (int)p2) {
+ sudo_warnx("record %d: want %u cols, got %u", recno, p2,
+ timing.u.winsize.cols);
+ return false;
+ }
+ } else {
+ if (timing.u.nbytes != p1) {
+ sudo_warnx("record %d: want len %u, got type %zu", recno, p1,
+ timing.u.nbytes);
+ return false;
+ }
+ }
+ if (timing.delay.tv_sec != 0) {
+ sudo_warnx("record %d: got excessive delay %lld.%09ld", recno,
+ (long long)timing.delay.tv_sec, timing.delay.tv_nsec);
+ return false;
+ }
+
+ return true;
+}
+
+
+/*
+ * Test sudoers I/O log plugin endpoints.
+ */
+void
+test_endpoints(int *ntests, int *nerrors, const char *iolog_dir, char *envp[])
+{
+ int rc, cmnd_argc = 1;
+ const char *errstr = NULL;
+ char buf[1024], iolog_path[PATH_MAX];
+ char runas_gid[64], runas_uid[64];
+ FILE *fp;
+ char *cmnd_argv[] = {
+ "/usr/bin/id",
+ NULL
+ };
+ char *user_info[] = {
+ "cols=80",
+ "lines=24",
+ "cwd=/",
+ "host=localhost",
+ "tty=/dev/console",
+ "user=nobody",
+ NULL
+ };
+ char *command_info[] = {
+ "command=/usr/bin/id",
+ iolog_path,
+ "iolog_stdin=true",
+ "iolog_stdout=true",
+ "iolog_stderr=true",
+ "iolog_ttyin=true",
+ "iolog_ttyout=true",
+ "iolog_compress=false",
+ "iolog_mode=0644",
+ runas_gid,
+ runas_uid,
+ NULL
+ };
+ char *settings[] = {
+ NULL
+ };
+ const char output[] = "uid=0(root) gid=0(wheel)\r\n";
+
+ /* Set runas uid/gid to root. */
+ snprintf(runas_uid, sizeof(runas_uid), "runas_uid=%u",
+ (unsigned int)runas_pw->pw_uid);
+ snprintf(runas_gid, sizeof(runas_gid), "runas_gid=%u",
+ (unsigned int)runas_pw->pw_gid);
+
+ /* Set path to the iolog directory the user passed in. */
+ snprintf(iolog_path, sizeof(iolog_path), "iolog_path=%s", iolog_dir);
+
+ /* Test open endpoint. */
+ rc = sudoers_io.open(SUDO_API_VERSION, NULL, sudo_printf_int, settings,
+ user_info, command_info, cmnd_argc, cmnd_argv, envp, NULL, &errstr);
+ (*ntests)++;
+ if (rc != 1) {
+ sudo_warnx("I/O log open endpoint failed");
+ (*nerrors)++;
+ return;
+ }
+
+ /* Test log_ttyout endpoint. */
+ rc = sudoers_io.log_ttyout(output, strlen(output), &errstr);
+ (*ntests)++;
+ if (rc != 1) {
+ sudo_warnx("I/O log_ttyout endpoint failed");
+ (*nerrors)++;
+ return;
+ }
+
+ /* Test change_winsize endpoint (twice). */
+ rc = sudoers_io.change_winsize(32, 128, &errstr);
+ (*ntests)++;
+ if (rc != 1) {
+ sudo_warnx("I/O change_winsize endpoint failed");
+ (*nerrors)++;
+ return;
+ }
+ rc = sudoers_io.change_winsize(24, 80, &errstr);
+ (*ntests)++;
+ if (rc != 1) {
+ sudo_warnx("I/O change_winsize endpoint failed");
+ (*nerrors)++;
+ return;
+ }
+
+ /* Close the plugin. */
+ sudoers_io.close(0, 0);
+
+ /* Validate I/O log info file (json). */
+ (*ntests)++;
+ if (!validate_iolog_info(iolog_dir, false))
+ (*nerrors)++;
+
+ /* Validate I/O log info file (legacy). */
+ snprintf(iolog_path, sizeof(iolog_path), "%s/log.json", iolog_dir);
+ unlink(iolog_path);
+ (*ntests)++;
+ if (!validate_iolog_info(iolog_dir, true))
+ (*nerrors)++;
+
+ /* Validate the timing file. */
+ snprintf(iolog_path, sizeof(iolog_path), "%s/timing", iolog_dir);
+ (*ntests)++;
+ if ((fp = fopen(iolog_path, "r")) == NULL) {
+ sudo_warn("unable to open %s", iolog_path);
+ (*nerrors)++;
+ return;
+ }
+
+ /* Line 1: output of id command. */
+ if (!validate_timing(fp, 1, IO_EVENT_TTYOUT, strlen(output), 0)) {
+ (*nerrors)++;
+ return;
+ }
+
+ /* Line 2: window size change. */
+ if (!validate_timing(fp, 2, IO_EVENT_WINSIZE, 32, 128)) {
+ (*nerrors)++;
+ return;
+ }
+
+ /* Line 3: window size change. */
+ if (!validate_timing(fp, 3, IO_EVENT_WINSIZE, 24, 80)) {
+ (*nerrors)++;
+ return;
+ }
+
+ /* Validate ttyout log file. */
+ snprintf(iolog_path, sizeof(iolog_path), "%s/ttyout", iolog_dir);
+ (*ntests)++;
+ fclose(fp);
+ if ((fp = fopen(iolog_path, "r")) == NULL) {
+ sudo_warn("unable to open %s", iolog_path);
+ (*nerrors)++;
+ return;
+ }
+ if (!fgets(buf, sizeof(buf), fp)) {
+ sudo_warn("unable to read %s", iolog_path);
+ (*nerrors)++;
+ return;
+ }
+ if (strcmp(buf, output) != 0) {
+ sudo_warnx("ttylog mismatch: want \"%s\", got \"%s\"", output, buf);
+ (*nerrors)++;
+ return;
+ }
+}
+
+int
+main(int argc, char *argv[], char *envp[])
+{
+ struct passwd *tpw;
+ int tests = 0, errors = 0;
+ const char *iolog_dir;
+
+ initprogname(argc > 0 ? argv[0] : "check_iolog_plugin");
+
+ if (argc != 2)
+ usage();
+ iolog_dir = argv[1];
+
+ /* Set runas user. */
+ if ((tpw = getpwuid(0)) == NULL) {
+ if ((tpw = getpwnam("root")) == NULL)
+ sudo_fatalx("unable to look up uid 0 or root");
+ }
+ sudo_user._runas_pw = pw_dup(tpw);
+
+ /* Set invoking user. */
+ if ((tpw = getpwuid(geteuid())) == NULL)
+ sudo_fatalx("unable to look up invoking user's uid");
+ sudo_user.pw = pw_dup(tpw);
+
+ /* Set iolog uid/gid to invoking user. */
+ iolog_set_owner(sudo_user.pw->pw_uid, sudo_user.pw->pw_gid);
+
+ test_endpoints(&tests, &errors, iolog_dir, envp);
+
+ if (tests != 0) {
+ printf("check_iolog_plugin: %d test%s run, %d errors, %d%% success rate\n",
+ tests, tests == 1 ? "" : "s", errors,
+ (tests - errors) * 100 / tests);
+ }
+
+ exit(errors);
+}
+
+/* Stub functions */
+
+bool
+set_perms(int perm)
+{
+ return true;
+}
+
+bool
+restore_perms(void)
+{
+ return true;
+}
+
+bool
+log_warning(int flags, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ sudo_vwarn_nodebug(fmt, ap);
+ va_end(ap);
+
+ return true;
+}
+
+bool
+log_warningx(int flags, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ sudo_vwarnx_nodebug(fmt, ap);
+ va_end(ap);
+
+ return true;
+}
diff --git a/plugins/sudoers/regress/parser/check_addr.c b/plugins/sudoers/regress/parser/check_addr.c
new file mode 100644
index 0000000..cf50226
--- /dev/null
+++ b/plugins/sudoers/regress/parser/check_addr.c
@@ -0,0 +1,139 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * Copyright (c) 2011-2013 Todd C. Miller <Todd.Miller@sudo.ws>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#define SUDO_ERROR_WRAP 0
+
+#include "sudoers.h"
+#include "interfaces.h"
+
+sudo_dso_public int main(int argc, char *argv[]);
+
+static int
+check_addr(char *input)
+{
+ int expected, matched;
+ const char *errstr;
+ size_t len;
+ char *cp;
+
+ while (isspace((unsigned char)*input))
+ input++;
+
+ /* input: "addr[/mask] 1/0" */
+ len = strcspn(input, " \t");
+ cp = input + len;
+ while (isspace((unsigned char)*cp))
+ cp++;
+ expected = sudo_strtonum(cp, 0, 1, &errstr);
+ if (errstr != NULL)
+ sudo_fatalx("expecting 0 or 1, got %s", cp);
+ input[len] = '\0';
+
+ matched = addr_matches(input);
+ if (matched != expected) {
+ sudo_warnx("%s %smatched: FAIL", input, matched ? "" : "not ");
+ return 1;
+ }
+ return 0;
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: %s datafile\n", getprogname());
+ exit(EXIT_FAILURE);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int ntests = 0, errors = 0;
+ char *cp, line[2048];
+ size_t len;
+ FILE *fp;
+
+ initprogname(argc > 0 ? argv[0] : "check_addr");
+
+ if (argc != 2)
+ usage();
+
+ fp = fopen(argv[1], "r");
+ if (fp == NULL)
+ sudo_fatalx("unable to open %s", argv[1]);
+
+ /*
+ * Input is in the following format. There are two types of
+ * lines: interfaces, which sets the address and mask of the
+ * locally connected ethernet interfaces for the lines that
+ * follow and, address lines that include and address (with
+ * optional netmask) to match, followed by expected match status
+ * (1 or 0). E.g.
+ *
+ * interfaces: addr1/mask addr2/mask ...
+ * address: addr[/mask] 1/0
+ * address: addr[/mask] 1/0
+ * interfaces: addr3/mask addr4/mask ...
+ * address: addr[/mask] 1/0
+ */
+
+ while (fgets(line, sizeof(line), fp) != NULL) {
+ len = strcspn(line, "\n");
+ line[len] = '\0';
+
+ /* Ignore comments */
+ if ((cp = strchr(line, '#')) != NULL)
+ *cp = '\0';
+
+ /* Skip blank lines. */
+ if (line[0] == '\0')
+ continue;
+
+ if (strncmp(line, "interfaces:", sizeof("interfaces:") - 1) == 0) {
+ if (!set_interfaces(line + sizeof("interfaces:") - 1)) {
+ sudo_warn("unable to parse interfaces list");
+ errors++;
+ }
+ } else if (strncmp(line, "address:", sizeof("address:") - 1) == 0) {
+ errors += check_addr(line + sizeof("address:") - 1);
+ ntests++;
+ } else {
+ sudo_warnx("unexpected data line: %s\n", line);
+ continue;
+ }
+ }
+
+ if (ntests != 0) {
+ printf("check_addr: %d tests run, %d errors, %d%% success rate\n",
+ ntests, errors, (ntests - errors) * 100 / ntests);
+ }
+
+ exit(errors);
+}
diff --git a/plugins/sudoers/regress/parser/check_addr.in b/plugins/sudoers/regress/parser/check_addr.in
new file mode 100644
index 0000000..a3c8612
--- /dev/null
+++ b/plugins/sudoers/regress/parser/check_addr.in
@@ -0,0 +1,13 @@
+#
+interfaces: 10.5.54.73/255.255.240.0
+address: 10.5.48.0 1
+address: 10.5.54.0/20 1
+#
+interfaces: 128.138.243.151/255.255.255.0 128.138.241.53/255.255.255.0
+address: 128.138.243.0 1
+address: 128.138.243.0/24 1
+address: 128.138.241.0 1
+address: 128.138.241.0/24 1
+address: 128.138.242.0/24 0
+address: 128.138.0.0 0
+address: 128.138.0.0/16 1
diff --git a/plugins/sudoers/regress/parser/check_base64.c b/plugins/sudoers/regress/parser/check_base64.c
new file mode 100644
index 0000000..195ada1
--- /dev/null
+++ b/plugins/sudoers/regress/parser/check_base64.c
@@ -0,0 +1,114 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * Copyright (c) 2013-2018 Todd C. Miller <Todd.Miller@sudo.ws>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define SUDO_ERROR_WRAP 0
+
+#include "sudo_compat.h"
+#include "sudo_util.h"
+
+/* From parse.h */
+extern size_t base64_decode(const char *str, unsigned char *dst, size_t dsize);
+extern size_t base64_encode(const unsigned char *in, size_t in_len, char *out, size_t out_len);
+
+sudo_dso_public int main(int argc, char *argv[]);
+
+static unsigned char bstring1[] = { 0xea, 0xb8, 0xa2, 0x71, 0xef, 0x67, 0xc1, 0xcd, 0x0d, 0xd9, 0xa6, 0xaa, 0xa8, 0x24, 0x77, 0x2a, 0xfc, 0x6f, 0x76, 0x37, 0x1b, 0xed, 0x9e, 0x1a, 0x90, 0x5f, 0xcf, 0xbc, 0x00 };
+
+struct base64_test {
+ const char *ascii;
+ const char *encoded;
+} test_strings[] = {
+ {
+ (char *)bstring1,
+ "6riice9nwc0N2aaqqCR3Kvxvdjcb7Z4akF/PvA=="
+ },
+ {
+ "any carnal pleasure.",
+ "YW55IGNhcm5hbCBwbGVhc3VyZS4="
+ },
+ {
+ "any carnal pleasure",
+ "YW55IGNhcm5hbCBwbGVhc3VyZQ=="
+ },
+ {
+ "any carnal pleasur",
+ "YW55IGNhcm5hbCBwbGVhc3Vy"
+ },
+ {
+ "any carnal pleasu",
+ "YW55IGNhcm5hbCBwbGVhc3U="
+ },
+ {
+ "any carnal pleas",
+ "YW55IGNhcm5hbCBwbGVhcw=="
+ }
+};
+
+int
+main(int argc, char *argv[])
+{
+ int ntests = nitems(test_strings);
+ int i, errors = 0;
+ unsigned char buf[64];
+ size_t len;
+
+ initprogname(argc > 0 ? argv[0] : "check_base64");
+
+ for (i = 0; i < ntests; i++) {
+ /* Test decode. */
+ len = base64_decode(test_strings[i].encoded, buf, sizeof(buf));
+ if (len == (size_t)-1) {
+ fprintf(stderr, "check_base64: failed to decode %s\n",
+ test_strings[i].encoded);
+ errors++;
+ } else {
+ buf[len] = '\0';
+ if (strcmp(test_strings[i].ascii, (char *)buf) != 0) {
+ fprintf(stderr, "check_base64: expected %s, got %s\n",
+ test_strings[i].ascii, buf);
+ errors++;
+ }
+ }
+
+ /* Test encode. */
+ len = base64_encode((unsigned char *)test_strings[i].ascii,
+ strlen(test_strings[i].ascii), (char *)buf, sizeof(buf));
+ if (len == (size_t)-1) {
+ fprintf(stderr, "check_base64: failed to encode %s\n",
+ test_strings[i].ascii);
+ errors++;
+ } else {
+ if (strcmp(test_strings[i].encoded, (char *)buf) != 0) {
+ fprintf(stderr, "check_base64: expected %s, got %s\n",
+ test_strings[i].encoded, buf);
+ errors++;
+ }
+ }
+ }
+ ntests *= 2; /* we test in both directions */
+
+ printf("check_base64: %d tests run, %d errors, %d%% success rate\n",
+ ntests, errors, (ntests - errors) * 100 / ntests);
+ exit(errors);
+}
diff --git a/plugins/sudoers/regress/parser/check_digest.c b/plugins/sudoers/regress/parser/check_digest.c
new file mode 100644
index 0000000..c784b2b
--- /dev/null
+++ b/plugins/sudoers/regress/parser/check_digest.c
@@ -0,0 +1,125 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * Copyright (c) 2013-2015 Todd C. Miller <Todd.Miller@sudo.ws>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "sudo_compat.h"
+#include "sudo_fatal.h"
+#include "sudo_queue.h"
+#include "sudo_digest.h"
+#include "sudo_util.h"
+#include "parse.h"
+
+sudo_dso_public int main(int argc, char *argv[]);
+
+#define NUM_TESTS 8
+static const char *test_strings[NUM_TESTS] = {
+ "",
+ "a",
+ "abc",
+ "message digest",
+ "abcdefghijklmnopqrstuvwxyz",
+ "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
+ "12345678901234567890123456789012345678901234567890123456789"
+ "012345678901234567890",
+};
+
+static unsigned char *
+check_digest(int digest_type, const char *buf, size_t buflen, size_t *digest_len)
+{
+ char tfile[] = "digest.XXXXXX";
+ unsigned char *digest = NULL;
+ int tfd;
+
+ /* Write test data to temporary file. */
+ tfd = mkstemp(tfile);
+ if (tfd == -1) {
+ sudo_warn_nodebug("mkstemp");
+ goto done;
+ }
+ if ((size_t)write(tfd, buf, buflen) != buflen) {
+ sudo_warn_nodebug("write");
+ goto done;
+ }
+ lseek(tfd, 0, SEEK_SET);
+
+ /* Get file digest. */
+ digest = sudo_filedigest(tfd, tfile, digest_type, digest_len);
+ if (digest == NULL) {
+ /* Warning (if any) printed by sudo_filedigest() */
+ goto done;
+ }
+done:
+ if (tfd != -1) {
+ close(tfd);
+ unlink(tfile);
+ }
+ return digest;
+}
+
+int
+main(int argc, char *argv[])
+{
+ static const char hex[] = "0123456789abcdef";
+ char buf[1000 * 1000];
+ unsigned char *digest;
+ unsigned int i, j;
+ size_t digest_len;
+ int digest_type;
+
+ initprogname(argc > 0 ? argv[0] : "check_digest");
+
+ for (digest_type = 0; digest_type < SUDO_DIGEST_INVALID; digest_type++) {
+ for (i = 0; i < NUM_TESTS; i++) {
+ digest = check_digest(digest_type, test_strings[i],
+ strlen(test_strings[i]), &digest_len);
+ if (digest != NULL) {
+ printf("%s (\"%s\") = ", digest_type_to_name(digest_type),
+ test_strings[i]);
+ for (j = 0; j < digest_len; j++) {
+ putchar(hex[digest[j] >> 4]);
+ putchar(hex[digest[j] & 0x0f]);
+ }
+ putchar('\n');
+ free(digest);
+ }
+ }
+
+ /* Simulate a string of a million 'a' characters. */
+ memset(buf, 'a', sizeof(buf));
+ digest = check_digest(digest_type, buf, sizeof(buf), &digest_len);
+ if (digest != NULL) {
+ printf("%s (one million 'a' characters) = ",
+ digest_type_to_name(digest_type));
+ for (j = 0; j < digest_len; j++) {
+ putchar(hex[digest[j] >> 4]);
+ putchar(hex[digest[j] & 0x0f]);
+ }
+ putchar('\n');
+ free(digest);
+ }
+ }
+
+ return 0;
+}
diff --git a/plugins/sudoers/regress/parser/check_digest.out.ok b/plugins/sudoers/regress/parser/check_digest.out.ok
new file mode 100644
index 0000000..a353664
--- /dev/null
+++ b/plugins/sudoers/regress/parser/check_digest.out.ok
@@ -0,0 +1,36 @@
+sha224 ("") = d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f
+sha224 ("a") = abd37534c7d9a2efb9465de931cd7055ffdb8879563ae98078d6d6d5
+sha224 ("abc") = 23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7
+sha224 ("message digest") = 2cb21c83ae2f004de7e81c3c7019cbcb65b71ab656b22d6d0c39b8eb
+sha224 ("abcdefghijklmnopqrstuvwxyz") = 45a5f72c39c5cff2522eb3429799e49e5f44b356ef926bcf390dccc2
+sha224 ("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") = 75388b16512776cc5dba5da1fd890150b0c6455cb4f58b1952522525
+sha224 ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") = bff72b4fcb7d75e5632900ac5f90d219e05e97a7bde72e740db393d9
+sha224 ("12345678901234567890123456789012345678901234567890123456789012345678901234567890") = b50aecbe4e9bb0b57bc5f3ae760a8e01db24f203fb3cdcd13148046e
+sha224 (one million 'a' characters) = 20794655980c91d8bbb4c1ea97618a4bf03f42581948b2ee4ee7ad67
+sha256 ("") = e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
+sha256 ("a") = ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb
+sha256 ("abc") = ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
+sha256 ("message digest") = f7846f55cf23e14eebeab5b4e1550cad5b509e3348fbc4efa3a1413d393cb650
+sha256 ("abcdefghijklmnopqrstuvwxyz") = 71c480df93d6ae2f1efad1447c66c9525e316218cf51fc8d9ed832f2daf18b73
+sha256 ("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") = 248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1
+sha256 ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") = db4bfcbd4da0cd85a60c3c37d3fbd8805c77f15fc6b1fdfe614ee0a7c8fdb4c0
+sha256 ("12345678901234567890123456789012345678901234567890123456789012345678901234567890") = f371bc4a311f2b009eef952dd83ca80e2b60026c8e935592d0f9c308453c813e
+sha256 (one million 'a' characters) = cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0
+sha384 ("") = 38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b
+sha384 ("a") = 54a59b9f22b0b80880d8427e548b7c23abd873486e1f035dce9cd697e85175033caa88e6d57bc35efae0b5afd3145f31
+sha384 ("abc") = cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7
+sha384 ("message digest") = 473ed35167ec1f5d8e550368a3db39be54639f828868e9454c239fc8b52e3c61dbd0d8b4de1390c256dcbb5d5fd99cd5
+sha384 ("abcdefghijklmnopqrstuvwxyz") = feb67349df3db6f5924815d6c3dc133f091809213731fe5c7b5f4999e463479ff2877f5f2936fa63bb43784b12f3ebb4
+sha384 ("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") = 3391fdddfc8dc7393707a65b1b4709397cf8b1d162af05abfe8f450de5f36bc6b0455a8520bc4e6f5fe95b1fe3c8452b
+sha384 ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") = 1761336e3f7cbfe51deb137f026f89e01a448e3b1fafa64039c1464ee8732f11a5341a6f41e0c202294736ed64db1a84
+sha384 ("12345678901234567890123456789012345678901234567890123456789012345678901234567890") = b12932b0627d1c060942f5447764155655bd4da0c9afa6dd9b9ef53129af1b8fb0195996d2de9ca0df9d821ffee67026
+sha384 (one million 'a' characters) = 9d0e1809716474cb086e834e310a4a1ced149e9c00f248527972cec5704c2a5b07b8b3dc38ecc4ebae97ddd87f3d8985
+sha512 ("") = cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e
+sha512 ("a") = 1f40fc92da241694750979ee6cf582f2d5d7d28e18335de05abc54d0560e0f5302860c652bf08d560252aa5e74210546f369fbbbce8c12cfc7957b2652fe9a75
+sha512 ("abc") = ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f
+sha512 ("message digest") = 107dbf389d9e9f71a3a95f6c055b9251bc5268c2be16d6c13492ea45b0199f3309e16455ab1e96118e8a905d5597b72038ddb372a89826046de66687bb420e7c
+sha512 ("abcdefghijklmnopqrstuvwxyz") = 4dbff86cc2ca1bae1e16468a05cb9881c97f1753bce3619034898faa1aabe429955a1bf8ec483d7421fe3c1646613a59ed5441fb0f321389f77f48a879c7b1f1
+sha512 ("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") = 204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445
+sha512 ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") = 1e07be23c26a86ea37ea810c8ec7809352515a970e9253c26f536cfc7a9996c45c8370583e0a78fa4a90041d71a4ceab7423f19c71b9d5a3e01249f0bebd5894
+sha512 ("12345678901234567890123456789012345678901234567890123456789012345678901234567890") = 72ec1ef1124a45b047e8b7c75a932195135bb61de24ec0d1914042246e0aec3a2354e093d76f3048b456764346900cb130d2a4fd5dd16abb5e30bcb850dee843
+sha512 (one million 'a' characters) = e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973ebde0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b
diff --git a/plugins/sudoers/regress/parser/check_fill.c b/plugins/sudoers/regress/parser/check_fill.c
new file mode 100644
index 0000000..2ced0f1
--- /dev/null
+++ b/plugins/sudoers/regress/parser/check_fill.c
@@ -0,0 +1,189 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * Copyright (c) 2011-2016 Todd C. Miller <Todd.Miller@sudo.ws>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef HAVE_STDBOOL_H
+# include <stdbool.h>
+#else
+# include "compat/stdbool.h"
+#endif /* HAVE_STDBOOL_H */
+#include <string.h>
+
+#define SUDO_ERROR_WRAP 0
+
+#include "sudo_compat.h"
+#include "sudo_queue.h"
+#include "parse.h"
+#include "toke.h"
+#include "sudo_plugin.h"
+#include "sudo_util.h"
+#include <gram.h>
+
+sudo_dso_public int main(int argc, char *argv[]);
+
+/*
+ * TODO: test realloc
+ */
+
+YYSTYPE sudoerslval;
+bool sudoers_strict;
+
+struct fill_test {
+ const char *input;
+ const char *output;
+ int len;
+ int addspace;
+};
+
+/*
+ * In "normal" fill, anything can be escaped and hex chars are expanded.
+ */
+static struct fill_test txt_data[] = {
+ { "Embedded\\x20Space", "Embedded Space", 0 },
+ { "\\x20Leading", " Leading", 0 },
+ { "Trailing\\x20", "Trailing ", 0 },
+ { "Multiple\\x20\\x20Spaces", "Multiple Spaces", 0 },
+ { "Hexparse\\x200Check", "Hexparse 0Check", 0 },
+ { "Escaped\\\\Escape", "Escaped\\Escape", 0 },
+ { "LongGroupName", "LongGrou", 8 }
+};
+
+/*
+ * The only escaped chars in a command should be [,:= \t#]
+ * The rest are done by glob() or fnmatch().
+ */
+static struct fill_test cmd_data[] = {
+ { "foo\\,bar", "foo,bar", 0 },
+ { "this\\:that", "this:that", 0 },
+ { "foo\\=bar", "foo=bar", 0 },
+ { "tab\\\tstop", "tab\tstop", 0 },
+ { "not a \\#comment", "not a #comment", 0 }
+};
+
+/*
+ * No escaped characters in command line args.
+ * Arguments get appended.
+ */
+static struct fill_test args_data[] = {
+ { "/", "/", 0, 0 },
+ { "-type", "/ -type", 0, 1 },
+ { "f", "/ -type f", 0, 1 },
+ { "-exec", "/ -type f -exec", 0, 1 },
+ { "ls", "/ -type f -exec ls", 0, 1 },
+ { "{}", "/ -type f -exec ls {}", 0, 1 }
+};
+
+static int
+check_fill(const char *input, int len, int addspace, const char *expect, char **resultp)
+{
+ if (sudoerslval.string != NULL) {
+ free(sudoerslval.string);
+ sudoerslval.string = NULL;
+ }
+ if (!fill(input, len))
+ return -1;
+ *resultp = sudoerslval.string;
+ return !strcmp(sudoerslval.string, expect);
+}
+
+static int
+check_fill_cmnd(const char *input, int len, int addspace, const char *expect, char **resultp)
+{
+ if (sudoerslval.command.cmnd != NULL) {
+ free(sudoerslval.command.cmnd);
+ sudoerslval.command.cmnd = NULL;
+ }
+ if (!fill_cmnd(input, len))
+ return -1;
+ *resultp = sudoerslval.command.cmnd;
+ return !strcmp(sudoerslval.command.cmnd, expect);
+}
+
+static int
+check_fill_args(const char *input, int len, int addspace, const char *expect, char **resultp)
+{
+ /* Must not free old sudoerslval.command.args as gets appended to. */
+ if (!fill_args(input, len, addspace))
+ return -1;
+ *resultp = sudoerslval.command.args;
+ return !strcmp(sudoerslval.command.args, expect);
+}
+
+static int
+do_tests(int (*checker)(const char *, int, int, const char *, char **),
+ struct fill_test *data, size_t ntests)
+{
+ int len, errors = 0;
+ unsigned int i;
+ char *result;
+
+ for (i = 0; i < ntests; i++) {
+ if (data[i].len == 0)
+ len = strlen(data[i].input);
+ else
+ len = data[i].len;
+
+ switch ((*checker)(data[i].input, len, data[i].addspace, data[i].output, &result)) {
+ case 0:
+ /* no match */
+ fprintf(stderr, "Failed parsing %.*s: expected [%s], got [%s]\n",
+ (int)data[i].len, data[i].input, data[i].output, result);
+ errors++;
+ break;
+ case 1:
+ /* match */
+ break;
+ default:
+ /* error */
+ fprintf(stderr, "Failed parsing %.*s: fill function failure\n",
+ (int)data[i].len, data[i].input);
+ errors++;
+ break;
+ }
+ }
+
+ return errors;
+}
+
+int
+main(int argc, char *argv[])
+{
+ int ntests, errors = 0;
+
+ initprogname(argc > 0 ? argv[0] : "check_fill");
+
+ errors += do_tests(check_fill, txt_data, nitems(txt_data));
+ errors += do_tests(check_fill_cmnd, cmd_data, nitems(cmd_data));
+ errors += do_tests(check_fill_args, args_data, nitems(args_data));
+
+ ntests = nitems(txt_data) + nitems(cmd_data) + nitems(args_data);
+ printf("%s: %d tests run, %d errors, %d%% success rate\n", getprogname(),
+ ntests, errors, (ntests - errors) * 100 / ntests);
+
+ exit(errors);
+}
+
+/* STUB */
+void
+sudoerserror(const char *s)
+{
+ return;
+}
diff --git a/plugins/sudoers/regress/parser/check_gentime.c b/plugins/sudoers/regress/parser/check_gentime.c
new file mode 100644
index 0000000..899d7e4
--- /dev/null
+++ b/plugins/sudoers/regress/parser/check_gentime.c
@@ -0,0 +1,83 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * Copyright (c) 2017 Todd C. Miller <Todd.Miller@sudo.ws>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#define SUDO_ERROR_WRAP 0
+
+#include "sudo_compat.h"
+#include "sudo_util.h"
+#include "sudoers_debug.h"
+#include "parse.h"
+
+sudo_dso_public int main(int argc, char *argv[]);
+
+const struct gentime_test {
+ char *gentime;
+ time_t unixtime;
+} tests[] = {
+ { "199412161032ZZ", -1 },
+ { "199412161032Z", 787573920 },
+ { "199412160532-0500", 787573920 },
+ { "199412160532-05000", -1 },
+ { "199412160532", 787573920 }, /* local time is EST */
+ { "20170214083000-0500", 1487079000 },
+ { "201702140830-0500", 1487079000 },
+ { "201702140830", 1487079000 }, /* local time is EST */
+ { "201702140830.3-0500", 1487079018 },
+ { "201702140830,3-0500", 1487079018 },
+ { "20170214083000.5Z", 1487061000 },
+ { "20170214083000,5Z", 1487061000 },
+ { "201702142359.4Z", 1487116764 },
+ { "201702142359,4Z", 1487116764 },
+ { "2017021408.5Z", 1487061000 },
+ { "2017021408,5Z", 1487061000 },
+ { "20170214Z", -1 },
+};
+
+int
+main(int argc, char *argv[])
+{
+ const int ntests = nitems(tests);
+ int i, errors = 0;
+ time_t result;
+
+ initprogname(argc > 0 ? argv[0] : "check_gentime");
+
+ /* Do local time tests in Eastern Standard Time. */
+ putenv("TZ=EST5EST5");
+ tzset();
+
+ for (i = 0; i < ntests; i++) {
+ result = parse_gentime(tests[i].gentime);
+ if (result != tests[i].unixtime) {
+ fprintf(stderr, "check_gentime[%d]: %s: expected %lld, got %lld\n",
+ i, tests[i].gentime,
+ (long long)tests[i].unixtime, (long long)result);
+ errors++;
+ }
+ }
+ printf("check_gentime: %d tests run, %d errors, %d%% success rate\n",
+ ntests, errors, (ntests - errors) * 100 / ntests);
+ exit(errors);
+}
diff --git a/plugins/sudoers/regress/parser/check_hexchar.c b/plugins/sudoers/regress/parser/check_hexchar.c
new file mode 100644
index 0000000..593ef72
--- /dev/null
+++ b/plugins/sudoers/regress/parser/check_hexchar.c
@@ -0,0 +1,81 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * Copyright (c) 2014-2015 Todd C. Miller <Todd.Miller@sudo.ws>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define SUDO_ERROR_WRAP 0
+
+#include "sudo_compat.h"
+#include "sudo_util.h"
+
+int hexchar(const char *s);
+
+sudo_dso_public int main(int argc, char *argv[]);
+
+struct hexchar_test {
+ char hex[3];
+ int value;
+};
+
+int
+main(int argc, char *argv[])
+{
+ struct hexchar_test *test_data;
+ int i, ntests, result, errors = 0;
+ static const char xdigs_lower[] = "0123456789abcdef";
+ static const char xdigs_upper[] = "0123456789ABCDEF";
+
+ initprogname(argc > 0 ? argv[0] : "check_hexchar");
+
+ /* Build up test data. */
+ ntests = 256 + 256 + 3;
+ test_data = calloc(sizeof(*test_data), ntests);
+ for (i = 0; i < 256; i++) {
+ /* lower case */
+ test_data[i].value = i;
+ test_data[i].hex[1] = xdigs_lower[ (i & 0x0f)];
+ test_data[i].hex[0] = xdigs_lower[((i & 0xf0) >> 4)];
+ /* upper case */
+ test_data[i + 256].value = i;
+ test_data[i + 256].hex[1] = xdigs_upper[ (i & 0x0f)];
+ test_data[i + 256].hex[0] = xdigs_upper[((i & 0xf0) >> 4)];
+ }
+ /* Also test invalid data */
+ test_data[ntests - 3].hex[0] = '\0';
+ test_data[ntests - 3].value = -1;
+ strlcpy(test_data[ntests - 2].hex, "AG", sizeof(test_data[ntests - 2].hex));
+ test_data[ntests - 2].value = -1;
+ strlcpy(test_data[ntests - 1].hex, "-1", sizeof(test_data[ntests - 1].hex));
+ test_data[ntests - 1].value = -1;
+
+ for (i = 0; i < ntests; i++) {
+ result = hexchar(test_data[i].hex);
+ if (result != test_data[i].value) {
+ fprintf(stderr, "check_hexchar: expected %d, got %d\n",
+ test_data[i].value, result);
+ errors++;
+ }
+ }
+ printf("check_hexchar: %d tests run, %d errors, %d%% success rate\n",
+ ntests, errors, (ntests - errors) * 100 / ntests);
+ exit(errors);
+}
diff --git a/plugins/sudoers/regress/starttime/check_starttime.c b/plugins/sudoers/regress/starttime/check_starttime.c
new file mode 100644
index 0000000..8951443
--- /dev/null
+++ b/plugins/sudoers/regress/starttime/check_starttime.c
@@ -0,0 +1,118 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * Copyright (c) 2017 Todd C. Miller <Todd.Miller@sudo.ws>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <config.h>
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "sudo_compat.h"
+#include "sudo_util.h"
+#include "sudo_fatal.h"
+#include "check.h"
+
+sudo_dso_public int main(int argc, char *argv[]);
+
+#ifdef __linux__
+static int
+get_now(struct timespec *now)
+{
+ const char *errstr;
+ char buf[1024];
+ time_t seconds;
+ int ret = -1;
+ FILE *fp;
+
+ /* Linux process start time is relative to boot time. */
+ fp = fopen("/proc/stat", "r");
+ if (fp != NULL) {
+ while (fgets(buf, sizeof(buf), fp) != NULL) {
+ if (strncmp(buf, "btime ", 6) != 0)
+ continue;
+ buf[strcspn(buf, "\n")] = '\0';
+
+ /* Boot time is in seconds since the epoch. */
+ seconds = sudo_strtonum(buf + 6, 0, TIME_T_MAX, &errstr);
+ if (errstr != NULL)
+ return -1;
+
+ /* Instead of the real time, "now" is relative to boot time. */
+ if (sudo_gettime_real(now) == -1)
+ return -1;
+ now->tv_sec -= seconds;
+ ret = 0;
+ break;
+ }
+ fclose(fp);
+ }
+ return ret;
+}
+#else
+static int
+get_now(struct timespec *now)
+{
+ /* Process start time is relative to wall clock time. */
+ return sudo_gettime_real(now);
+}
+#endif
+
+int
+main(int argc, char *argv[])
+{
+ int ntests = 0, errors = 0;
+ struct timespec now, then, delta;
+ pid_t pids[2];
+ int i;
+
+ initprogname(argc > 0 ? argv[0] : "check_starttime");
+
+ if (get_now(&now) == -1)
+ sudo_fatal_nodebug("unable to get current time");
+
+ pids[0] = getpid();
+ pids[1] = getppid();
+
+ for (i = 0; i < 2; i++) {
+ ntests++;
+ if (get_starttime(pids[i], &then) == -1) {
+ printf("%s: test %d: unable to get start time for pid %d\n",
+ getprogname(), ntests, (int)pids[i]);
+ errors++;
+ }
+ if (i != 0)
+ continue;
+
+ /* Verify our own process start time, allowing for some drift. */
+ ntests++;
+ sudo_timespecsub(&then, &now, &delta);
+ if (delta.tv_sec > 30 || delta.tv_sec < -30) {
+ printf("%s: test %d: unexpected start time for pid %d: %s",
+ getprogname(), ntests, (int)pids[i], ctime(&then.tv_sec));
+ errors++;
+ }
+ }
+
+ printf("%s: %d tests run, %d errors, %d%% success rate\n", getprogname(),
+ ntests, errors, (ntests - errors) * 100 / ntests);
+
+ exit(errors);
+}
diff --git a/plugins/sudoers/regress/sudoers/test1.in b/plugins/sudoers/regress/sudoers/test1.in
new file mode 100644
index 0000000..d87c872
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test1.in
@@ -0,0 +1,12 @@
+#
+# Verify that all command tags are parsed OK.
+# See https://bugzilla.sudo.ws/show_bug.cgi?id=437
+#
+user1 ALL = LOG_INPUT: LOG_OUTPUT: /usr/bin/su -:\
+ ALL = NOLOG_INPUT: NOLOG_OUTPUT: /usr/bin/id
+user2 ALL = NOPASSWD: NOEXEC: SETENV: /usr/bin/vi:\
+ ALL = PASSWD: EXEC: NOSETENV: /usr/bin/echo
+user3 ALL = MAIL: /bin/sh:\
+ ALL = NOMAIL: /usr/bin/id
+user4 ALL = FOLLOW: sudoedit /etc/motd:\
+ ALL = NOFOLLOW: sudoedit /home/*/*
diff --git a/plugins/sudoers/regress/sudoers/test1.json.ok b/plugins/sudoers/regress/sudoers/test1.json.ok
new file mode 100644
index 0000000..9523e4a
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test1.json.ok
@@ -0,0 +1,154 @@
+{
+ "User_Specs": [
+ {
+ "User_List": [
+ { "username": "user1" }
+ ],
+ "Host_List": [
+ { "hostname": "ALL" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "Options": [
+ { "log_input": true },
+ { "log_output": true }
+ ],
+ "Commands": [
+ { "command": "/usr/bin/su -" }
+ ]
+ }
+ ]
+ },
+ {
+ "User_List": [
+ { "username": "user1" }
+ ],
+ "Host_List": [
+ { "hostname": "ALL" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "Options": [
+ { "log_input": false },
+ { "log_output": false }
+ ],
+ "Commands": [
+ { "command": "/usr/bin/id" }
+ ]
+ }
+ ]
+ },
+ {
+ "User_List": [
+ { "username": "user2" }
+ ],
+ "Host_List": [
+ { "hostname": "ALL" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "Options": [
+ { "authenticate": false },
+ { "noexec": true },
+ { "setenv": true }
+ ],
+ "Commands": [
+ { "command": "/usr/bin/vi" }
+ ]
+ }
+ ]
+ },
+ {
+ "User_List": [
+ { "username": "user2" }
+ ],
+ "Host_List": [
+ { "hostname": "ALL" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "Options": [
+ { "authenticate": true },
+ { "noexec": false },
+ { "setenv": false }
+ ],
+ "Commands": [
+ { "command": "/usr/bin/echo" }
+ ]
+ }
+ ]
+ },
+ {
+ "User_List": [
+ { "username": "user3" }
+ ],
+ "Host_List": [
+ { "hostname": "ALL" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "Options": [
+ { "send_mail": true }
+ ],
+ "Commands": [
+ { "command": "/bin/sh" }
+ ]
+ }
+ ]
+ },
+ {
+ "User_List": [
+ { "username": "user3" }
+ ],
+ "Host_List": [
+ { "hostname": "ALL" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "Options": [
+ { "send_mail": false }
+ ],
+ "Commands": [
+ { "command": "/usr/bin/id" }
+ ]
+ }
+ ]
+ },
+ {
+ "User_List": [
+ { "username": "user4" }
+ ],
+ "Host_List": [
+ { "hostname": "ALL" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "Options": [
+ { "sudoedit_follow": true }
+ ],
+ "Commands": [
+ { "command": "sudoedit /etc/motd" }
+ ]
+ }
+ ]
+ },
+ {
+ "User_List": [
+ { "username": "user4" }
+ ],
+ "Host_List": [
+ { "hostname": "ALL" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "Options": [
+ { "sudoedit_follow": false }
+ ],
+ "Commands": [
+ { "command": "sudoedit /home/*/*" }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/plugins/sudoers/regress/sudoers/test1.ldif.ok b/plugins/sudoers/regress/sudoers/test1.ldif.ok
new file mode 100644
index 0000000..7f3fcfc
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test1.ldif.ok
@@ -0,0 +1,88 @@
+dn: cn=user1,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: user1
+sudoUser: user1
+sudoHost: ALL
+sudoOption: log_input
+sudoOption: log_output
+sudoCommand: /usr/bin/su -
+sudoOrder: 1
+
+dn: cn=user1_1,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: user1_1
+sudoUser: user1
+sudoHost: ALL
+sudoOption: !log_input
+sudoOption: !log_output
+sudoCommand: /usr/bin/id
+sudoOrder: 2
+
+dn: cn=user2,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: user2
+sudoUser: user2
+sudoHost: ALL
+sudoOption: !authenticate
+sudoOption: noexec
+sudoOption: setenv
+sudoCommand: /usr/bin/vi
+sudoOrder: 3
+
+dn: cn=user2_1,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: user2_1
+sudoUser: user2
+sudoHost: ALL
+sudoOption: authenticate
+sudoOption: !noexec
+sudoOption: !setenv
+sudoCommand: /usr/bin/echo
+sudoOrder: 4
+
+dn: cn=user3,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: user3
+sudoUser: user3
+sudoHost: ALL
+sudoOption: mail_all_cmnds
+sudoCommand: /bin/sh
+sudoOrder: 5
+
+dn: cn=user3_1,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: user3_1
+sudoUser: user3
+sudoHost: ALL
+sudoOption: !mail_all_cmnds
+sudoOption: !mail_always
+sudoOption: !mail_no_perms
+sudoCommand: /usr/bin/id
+sudoOrder: 6
+
+dn: cn=user4,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: user4
+sudoUser: user4
+sudoHost: ALL
+sudoOption: sudoedit_follow
+sudoCommand: sudoedit /etc/motd
+sudoOrder: 7
+
+dn: cn=user4_1,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: user4_1
+sudoUser: user4
+sudoHost: ALL
+sudoOption: !sudoedit_follow
+sudoCommand: sudoedit /home/*/*
+sudoOrder: 8
+
diff --git a/plugins/sudoers/regress/sudoers/test1.ldif2sudo.ok b/plugins/sudoers/regress/sudoers/test1.ldif2sudo.ok
new file mode 100644
index 0000000..126fe91
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test1.ldif2sudo.ok
@@ -0,0 +1,13 @@
+# sudoRole user1, user1_1
+user1 ALL = LOG_INPUT: LOG_OUTPUT: /usr/bin/su -, NOLOG_INPUT: NOLOG_OUTPUT:\
+ /usr/bin/id
+
+# sudoRole user2, user2_1
+user2 ALL = SETENV: NOEXEC: NOPASSWD: /usr/bin/vi, NOSETENV: EXEC: PASSWD:\
+ /usr/bin/echo
+
+# sudoRole user3, user3_1
+user3 ALL = MAIL: /bin/sh, NOMAIL: /usr/bin/id
+
+# sudoRole user4, user4_1
+user4 ALL = FOLLOW: sudoedit /etc/motd, NOFOLLOW: sudoedit /home/*/*
diff --git a/plugins/sudoers/regress/sudoers/test1.out.ok b/plugins/sudoers/regress/sudoers/test1.out.ok
new file mode 100644
index 0000000..3d6bab4
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test1.out.ok
@@ -0,0 +1,6 @@
+Parses OK
+
+user1 ALL = LOG_INPUT: LOG_OUTPUT: /usr/bin/su - : ALL = NOLOG_INPUT: NOLOG_OUTPUT: /usr/bin/id
+user2 ALL = SETENV: NOEXEC: NOPASSWD: /usr/bin/vi : ALL = NOSETENV: EXEC: PASSWD: /usr/bin/echo
+user3 ALL = MAIL: /bin/sh : ALL = NOMAIL: /usr/bin/id
+user4 ALL = FOLLOW: sudoedit /etc/motd : ALL = NOFOLLOW: sudoedit /home/*/*
diff --git a/plugins/sudoers/regress/sudoers/test1.toke.ok b/plugins/sudoers/regress/sudoers/test1.toke.ok
new file mode 100644
index 0000000..28c8592
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test1.toke.ok
@@ -0,0 +1,8 @@
+#
+#
+#
+#
+WORD(6) ALL = LOG_INPUT LOG_OUTPUT COMMAND ARG : ALL = NOLOG_INPUT NOLOG_OUTPUT COMMAND
+WORD(6) ALL = NOPASSWD NOEXEC SETENV COMMAND : ALL = PASSWD EXEC NOSETENV COMMAND
+WORD(6) ALL = MAIL COMMAND : ALL = NOMAIL COMMAND
+WORD(6) ALL = FOLLOW COMMAND ARG : ALL = NOFOLLOW COMMAND ARG
diff --git a/plugins/sudoers/regress/sudoers/test10.in b/plugins/sudoers/regress/sudoers/test10.in
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test10.in
@@ -0,0 +1 @@
+
diff --git a/plugins/sudoers/regress/sudoers/test10.json.ok b/plugins/sudoers/regress/sudoers/test10.json.ok
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test10.json.ok
diff --git a/plugins/sudoers/regress/sudoers/test10.ldif.ok b/plugins/sudoers/regress/sudoers/test10.ldif.ok
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test10.ldif.ok
diff --git a/plugins/sudoers/regress/sudoers/test10.out.ok b/plugins/sudoers/regress/sudoers/test10.out.ok
new file mode 100644
index 0000000..5af5c53
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test10.out.ok
@@ -0,0 +1,2 @@
+Parses OK
+
diff --git a/plugins/sudoers/regress/sudoers/test10.toke.ok b/plugins/sudoers/regress/sudoers/test10.toke.ok
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test10.toke.ok
@@ -0,0 +1 @@
+
diff --git a/plugins/sudoers/regress/sudoers/test11.in b/plugins/sudoers/regress/sudoers/test11.in
new file mode 100644
index 0000000..5ffba7b
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test11.in
@@ -0,0 +1 @@
+bogus
diff --git a/plugins/sudoers/regress/sudoers/test11.json.ok b/plugins/sudoers/regress/sudoers/test11.json.ok
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test11.json.ok
diff --git a/plugins/sudoers/regress/sudoers/test11.ldif.ok b/plugins/sudoers/regress/sudoers/test11.ldif.ok
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test11.ldif.ok
diff --git a/plugins/sudoers/regress/sudoers/test11.out.ok b/plugins/sudoers/regress/sudoers/test11.out.ok
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test11.out.ok
@@ -0,0 +1 @@
+
diff --git a/plugins/sudoers/regress/sudoers/test11.toke.ok b/plugins/sudoers/regress/sudoers/test11.toke.ok
new file mode 100644
index 0000000..bfef7a7
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test11.toke.ok
@@ -0,0 +1,2 @@
+WORD(6)
+<*> \ No newline at end of file
diff --git a/plugins/sudoers/regress/sudoers/test12.in b/plugins/sudoers/regress/sudoers/test12.in
new file mode 100644
index 0000000..23bda4a
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test12.in
@@ -0,0 +1 @@
+user ALL = (ALL)
diff --git a/plugins/sudoers/regress/sudoers/test12.json.ok b/plugins/sudoers/regress/sudoers/test12.json.ok
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test12.json.ok
diff --git a/plugins/sudoers/regress/sudoers/test12.ldif.ok b/plugins/sudoers/regress/sudoers/test12.ldif.ok
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test12.ldif.ok
diff --git a/plugins/sudoers/regress/sudoers/test12.out.ok b/plugins/sudoers/regress/sudoers/test12.out.ok
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test12.out.ok
@@ -0,0 +1 @@
+
diff --git a/plugins/sudoers/regress/sudoers/test12.toke.ok b/plugins/sudoers/regress/sudoers/test12.toke.ok
new file mode 100644
index 0000000..0d79959
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test12.toke.ok
@@ -0,0 +1,2 @@
+WORD(6) ALL = ( ALL )
+<*> \ No newline at end of file
diff --git a/plugins/sudoers/regress/sudoers/test13.in b/plugins/sudoers/regress/sudoers/test13.in
new file mode 100644
index 0000000..b8002bc
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test13.in
@@ -0,0 +1 @@
+user ALL = (ALL) \ No newline at end of file
diff --git a/plugins/sudoers/regress/sudoers/test13.json.ok b/plugins/sudoers/regress/sudoers/test13.json.ok
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test13.json.ok
diff --git a/plugins/sudoers/regress/sudoers/test13.ldif.ok b/plugins/sudoers/regress/sudoers/test13.ldif.ok
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test13.ldif.ok
diff --git a/plugins/sudoers/regress/sudoers/test13.out.ok b/plugins/sudoers/regress/sudoers/test13.out.ok
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test13.out.ok
@@ -0,0 +1 @@
+
diff --git a/plugins/sudoers/regress/sudoers/test13.toke.ok b/plugins/sudoers/regress/sudoers/test13.toke.ok
new file mode 100644
index 0000000..0d79959
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test13.toke.ok
@@ -0,0 +1,2 @@
+WORD(6) ALL = ( ALL )
+<*> \ No newline at end of file
diff --git a/plugins/sudoers/regress/sudoers/test14.in b/plugins/sudoers/regress/sudoers/test14.in
new file mode 100644
index 0000000..985f6e3
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test14.in
@@ -0,0 +1,6 @@
+Cmnd_Alias LS = sha224:d06a2617c98d377c250edd470fd5e576327748d82915d6e33b5f8db1, sha224:d7910e1967342b4605cb73a550944044c631cd3514001900966962ac /bin/ls
+Cmnd_Alias SH = sha256:hOtoe/iK6SlGg7w4BfZBBdSsXjUmTJ5+ts51yjh7vkM=, sha256:1IXHRCxXgSnIEnb+xBz4PAfWaPdXIBWKFF0QCwxJ5G4= /bin/sh
+
+millert ALL = LS, SH, sha512:srzYEQ2aqzm+it3f74opTMkIImZRLxBARVpb0g9RSouJYdLt7DTRMEY4Ry9NyaOiDoUIplpNjqYH0JMYPVdFnw /bin/kill
+
+operator ALL = sha256:hOtoe/iK6SlGg7w4BfZBBdSsXjUmTJ5+ts51yjh7vkM=, sha256:1IXHRCxXgSnIEnb+xBz4PAfWaPdXIBWKFF0QCwxJ5G4= ALL
diff --git a/plugins/sudoers/regress/sudoers/test14.json.ok b/plugins/sudoers/regress/sudoers/test14.json.ok
new file mode 100644
index 0000000..e772d6e
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test14.json.ok
@@ -0,0 +1,62 @@
+{
+ "Command_Aliases": {
+ "LS": [
+ {
+ "command": "/bin/ls",
+ "sha224": "d06a2617c98d377c250edd470fd5e576327748d82915d6e33b5f8db1",
+ "sha224": "d7910e1967342b4605cb73a550944044c631cd3514001900966962ac"
+ }
+ ],
+ "SH": [
+ {
+ "command": "/bin/sh",
+ "sha256": "hOtoe/iK6SlGg7w4BfZBBdSsXjUmTJ5+ts51yjh7vkM=",
+ "sha256": "1IXHRCxXgSnIEnb+xBz4PAfWaPdXIBWKFF0QCwxJ5G4="
+ }
+ ]
+ },
+ "User_Specs": [
+ {
+ "User_List": [
+ { "username": "millert" }
+ ],
+ "Host_List": [
+ { "hostname": "ALL" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "Commands": [
+ { "cmndalias": "LS" },
+ { "cmndalias": "SH" },
+ {
+ "command": "/bin/kill",
+ "sha512": "srzYEQ2aqzm+it3f74opTMkIImZRLxBARVpb0g9RSouJYdLt7DTRMEY4Ry9NyaOiDoUIplpNjqYH0JMYPVdFnw"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "User_List": [
+ { "username": "operator" }
+ ],
+ "Host_List": [
+ { "hostname": "ALL" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "Options": [
+ { "setenv": true }
+ ],
+ "Commands": [
+ {
+ "command": "ALL",
+ "sha256": "hOtoe/iK6SlGg7w4BfZBBdSsXjUmTJ5+ts51yjh7vkM=",
+ "sha256": "1IXHRCxXgSnIEnb+xBz4PAfWaPdXIBWKFF0QCwxJ5G4="
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/plugins/sudoers/regress/sudoers/test14.ldif.ok b/plugins/sudoers/regress/sudoers/test14.ldif.ok
new file mode 100644
index 0000000..ea9e957
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test14.ldif.ok
@@ -0,0 +1,20 @@
+dn: cn=millert,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: millert
+sudoUser: millert
+sudoHost: ALL
+sudoCommand: sha224:d06a2617c98d377c250edd470fd5e576327748d82915d6e33b5f8db1, sha224:d7910e1967342b4605cb73a550944044c631cd3514001900966962ac /bin/ls
+sudoCommand: sha256:hOtoe/iK6SlGg7w4BfZBBdSsXjUmTJ5+ts51yjh7vkM=, sha256:1IXHRCxXgSnIEnb+xBz4PAfWaPdXIBWKFF0QCwxJ5G4= /bin/sh
+sudoCommand: sha512:srzYEQ2aqzm+it3f74opTMkIImZRLxBARVpb0g9RSouJYdLt7DTRMEY4Ry9NyaOiDoUIplpNjqYH0JMYPVdFnw /bin/kill
+sudoOrder: 1
+
+dn: cn=operator,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: operator
+sudoUser: operator
+sudoHost: ALL
+sudoCommand: sha256:hOtoe/iK6SlGg7w4BfZBBdSsXjUmTJ5+ts51yjh7vkM=, sha256:1IXHRCxXgSnIEnb+xBz4PAfWaPdXIBWKFF0QCwxJ5G4= ALL
+sudoOrder: 2
+
diff --git a/plugins/sudoers/regress/sudoers/test14.ldif2sudo.ok b/plugins/sudoers/regress/sudoers/test14.ldif2sudo.ok
new file mode 100644
index 0000000..b916cca
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test14.ldif2sudo.ok
@@ -0,0 +1,11 @@
+# sudoRole millert
+millert ALL = sha224:d06a2617c98d377c250edd470fd5e576327748d82915d6e33b5f8db1,\
+ sha224:d7910e1967342b4605cb73a550944044c631cd3514001900966962ac /bin/ls,\
+ sha256:hOtoe/iK6SlGg7w4BfZBBdSsXjUmTJ5+ts51yjh7vkM=,\
+ sha256:1IXHRCxXgSnIEnb+xBz4PAfWaPdXIBWKFF0QCwxJ5G4= /bin/sh,\
+ sha512:srzYEQ2aqzm+it3f74opTMkIImZRLxBARVpb0g9RSouJYdLt7DTRMEY4Ry9NyaOiDoUIplpNjqYH0JMYPVdFnw\
+ /bin/kill
+
+# sudoRole operator
+operator ALL = sha256:hOtoe/iK6SlGg7w4BfZBBdSsXjUmTJ5+ts51yjh7vkM=,\
+ sha256:1IXHRCxXgSnIEnb+xBz4PAfWaPdXIBWKFF0QCwxJ5G4= ALL
diff --git a/plugins/sudoers/regress/sudoers/test14.out.ok b/plugins/sudoers/regress/sudoers/test14.out.ok
new file mode 100644
index 0000000..05d0a20
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test14.out.ok
@@ -0,0 +1,7 @@
+Parses OK
+
+Cmnd_Alias LS = sha224:d06a2617c98d377c250edd470fd5e576327748d82915d6e33b5f8db1, sha224:d7910e1967342b4605cb73a550944044c631cd3514001900966962ac /bin/ls
+Cmnd_Alias SH = sha256:hOtoe/iK6SlGg7w4BfZBBdSsXjUmTJ5+ts51yjh7vkM=, sha256:1IXHRCxXgSnIEnb+xBz4PAfWaPdXIBWKFF0QCwxJ5G4= /bin/sh
+
+millert ALL = LS, SH, sha512:srzYEQ2aqzm+it3f74opTMkIImZRLxBARVpb0g9RSouJYdLt7DTRMEY4Ry9NyaOiDoUIplpNjqYH0JMYPVdFnw /bin/kill
+operator ALL = sha256:hOtoe/iK6SlGg7w4BfZBBdSsXjUmTJ5+ts51yjh7vkM=, sha256:1IXHRCxXgSnIEnb+xBz4PAfWaPdXIBWKFF0QCwxJ5G4= ALL
diff --git a/plugins/sudoers/regress/sudoers/test14.toke.ok b/plugins/sudoers/regress/sudoers/test14.toke.ok
new file mode 100644
index 0000000..0b4f36a
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test14.toke.ok
@@ -0,0 +1,6 @@
+CMNDALIAS ALIAS = SHA224_TOK : DIGEST , SHA224_TOK : DIGEST COMMAND
+CMNDALIAS ALIAS = SHA256_TOK : DIGEST , SHA256_TOK : DIGEST COMMAND
+
+WORD(6) ALL = ALIAS , ALIAS , SHA512_TOK : DIGEST COMMAND
+
+WORD(6) ALL = SHA256_TOK : DIGEST , SHA256_TOK : DIGEST ALL
diff --git a/plugins/sudoers/regress/sudoers/test15.in b/plugins/sudoers/regress/sudoers/test15.in
new file mode 100644
index 0000000..11bcb13
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test15.in
@@ -0,0 +1,2 @@
+# Test parsing of sudoedit rule
+user ALL = sudoedit /etc/motd
diff --git a/plugins/sudoers/regress/sudoers/test15.json.ok b/plugins/sudoers/regress/sudoers/test15.json.ok
new file mode 100644
index 0000000..ff1795a
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test15.json.ok
@@ -0,0 +1,19 @@
+{
+ "User_Specs": [
+ {
+ "User_List": [
+ { "username": "user" }
+ ],
+ "Host_List": [
+ { "hostname": "ALL" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "Commands": [
+ { "command": "sudoedit /etc/motd" }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/plugins/sudoers/regress/sudoers/test15.ldif.ok b/plugins/sudoers/regress/sudoers/test15.ldif.ok
new file mode 100644
index 0000000..ac35ba0
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test15.ldif.ok
@@ -0,0 +1,9 @@
+dn: cn=user,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: user
+sudoUser: user
+sudoHost: ALL
+sudoCommand: sudoedit /etc/motd
+sudoOrder: 1
+
diff --git a/plugins/sudoers/regress/sudoers/test15.ldif2sudo.ok b/plugins/sudoers/regress/sudoers/test15.ldif2sudo.ok
new file mode 100644
index 0000000..775d59e
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test15.ldif2sudo.ok
@@ -0,0 +1,2 @@
+# sudoRole user
+user ALL = sudoedit /etc/motd
diff --git a/plugins/sudoers/regress/sudoers/test15.out.ok b/plugins/sudoers/regress/sudoers/test15.out.ok
new file mode 100644
index 0000000..b230cf2
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test15.out.ok
@@ -0,0 +1,3 @@
+Parses OK
+
+user ALL = sudoedit /etc/motd
diff --git a/plugins/sudoers/regress/sudoers/test15.toke.ok b/plugins/sudoers/regress/sudoers/test15.toke.ok
new file mode 100644
index 0000000..08bb2b8
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test15.toke.ok
@@ -0,0 +1,2 @@
+#
+WORD(6) ALL = COMMAND ARG
diff --git a/plugins/sudoers/regress/sudoers/test16.in b/plugins/sudoers/regress/sudoers/test16.in
new file mode 100644
index 0000000..d2a79ea
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test16.in
@@ -0,0 +1,3 @@
+# Test parsing of sudoedit rule in a Cmnd_Alias
+Cmnd_Alias EDIT = sudoedit /etc/motd
+user ALL = EDIT
diff --git a/plugins/sudoers/regress/sudoers/test16.json.ok b/plugins/sudoers/regress/sudoers/test16.json.ok
new file mode 100644
index 0000000..7c42654
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test16.json.ok
@@ -0,0 +1,24 @@
+{
+ "Command_Aliases": {
+ "EDIT": [
+ { "command": "sudoedit /etc/motd" }
+ ]
+ },
+ "User_Specs": [
+ {
+ "User_List": [
+ { "username": "user" }
+ ],
+ "Host_List": [
+ { "hostname": "ALL" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "Commands": [
+ { "cmndalias": "EDIT" }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/plugins/sudoers/regress/sudoers/test16.ldif.ok b/plugins/sudoers/regress/sudoers/test16.ldif.ok
new file mode 100644
index 0000000..ac35ba0
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test16.ldif.ok
@@ -0,0 +1,9 @@
+dn: cn=user,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: user
+sudoUser: user
+sudoHost: ALL
+sudoCommand: sudoedit /etc/motd
+sudoOrder: 1
+
diff --git a/plugins/sudoers/regress/sudoers/test16.ldif2sudo.ok b/plugins/sudoers/regress/sudoers/test16.ldif2sudo.ok
new file mode 100644
index 0000000..775d59e
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test16.ldif2sudo.ok
@@ -0,0 +1,2 @@
+# sudoRole user
+user ALL = sudoedit /etc/motd
diff --git a/plugins/sudoers/regress/sudoers/test16.out.ok b/plugins/sudoers/regress/sudoers/test16.out.ok
new file mode 100644
index 0000000..7b8c918
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test16.out.ok
@@ -0,0 +1,5 @@
+Parses OK
+
+Cmnd_Alias EDIT = sudoedit /etc/motd
+
+user ALL = EDIT
diff --git a/plugins/sudoers/regress/sudoers/test16.toke.ok b/plugins/sudoers/regress/sudoers/test16.toke.ok
new file mode 100644
index 0000000..debc4c7
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test16.toke.ok
@@ -0,0 +1,3 @@
+#
+CMNDALIAS ALIAS = COMMAND ARG
+WORD(6) ALL = ALIAS
diff --git a/plugins/sudoers/regress/sudoers/test17.in b/plugins/sudoers/regress/sudoers/test17.in
new file mode 100644
index 0000000..37d066c
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test17.in
@@ -0,0 +1,13 @@
+# Test parsing of command_timeout and TIMEOUT syntax
+Defaults command_timeout=2d8h10m59s
+user0 ALL = TIMEOUT=7D4H10M30S /usr/bin/id, /usr/bin/who, TIMEOUT=0 /bin/ls
+user1 ALL = TIMEOUT=7d4h10m30s /usr/bin/id
+user2 ALL = TIMEOUT=4h10m30s /usr/bin/id
+user3 ALL = TIMEOUT=10m30s /usr/bin/id
+user4 ALL = TIMEOUT=14d /usr/bin/id
+user5 ALL = TIMEOUT=5m /usr/bin/id
+user6 ALL = TIMEOUT=30s /usr/bin/id
+user7 ALL = TIMEOUT=45 /usr/bin/id
+user8 ALL = TIMEOUT=7d4h10m30s /usr/bin/id, TIMEOUT=4h10m30s /usr/bin/id, \
+ TIMEOUT=10m30s /usr/bin/id, TIMEOUT=14d /usr/bin/id, \
+ TIMEOUT=5m /usr/bin/id, TIMEOUT=30s /usr/bin/id
diff --git a/plugins/sudoers/regress/sudoers/test17.json.ok b/plugins/sudoers/regress/sudoers/test17.json.ok
new file mode 100644
index 0000000..2f39a37
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test17.json.ok
@@ -0,0 +1,180 @@
+{
+ "Defaults": [
+ {
+ "Options": [
+ { "command_timeout": "2d8h10m59s" }
+ ]
+ }
+ ],
+ "User_Specs": [
+ {
+ "User_List": [
+ { "username": "user0" }
+ ],
+ "Host_List": [
+ { "hostname": "ALL" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "Options": [
+ { "command_timeout": 619830 }
+ ],
+ "Commands": [
+ { "command": "/usr/bin/id" },
+ { "command": "/usr/bin/who" },
+ { "command": "/bin/ls" }
+ ]
+ }
+ ]
+ },
+ {
+ "User_List": [
+ { "username": "user1" }
+ ],
+ "Host_List": [
+ { "hostname": "ALL" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "Options": [
+ { "command_timeout": 619830 }
+ ],
+ "Commands": [
+ { "command": "/usr/bin/id" }
+ ]
+ }
+ ]
+ },
+ {
+ "User_List": [
+ { "username": "user2" }
+ ],
+ "Host_List": [
+ { "hostname": "ALL" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "Options": [
+ { "command_timeout": 15030 }
+ ],
+ "Commands": [
+ { "command": "/usr/bin/id" }
+ ]
+ }
+ ]
+ },
+ {
+ "User_List": [
+ { "username": "user3" }
+ ],
+ "Host_List": [
+ { "hostname": "ALL" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "Options": [
+ { "command_timeout": 630 }
+ ],
+ "Commands": [
+ { "command": "/usr/bin/id" }
+ ]
+ }
+ ]
+ },
+ {
+ "User_List": [
+ { "username": "user4" }
+ ],
+ "Host_List": [
+ { "hostname": "ALL" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "Options": [
+ { "command_timeout": 1209600 }
+ ],
+ "Commands": [
+ { "command": "/usr/bin/id" }
+ ]
+ }
+ ]
+ },
+ {
+ "User_List": [
+ { "username": "user5" }
+ ],
+ "Host_List": [
+ { "hostname": "ALL" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "Options": [
+ { "command_timeout": 300 }
+ ],
+ "Commands": [
+ { "command": "/usr/bin/id" }
+ ]
+ }
+ ]
+ },
+ {
+ "User_List": [
+ { "username": "user6" }
+ ],
+ "Host_List": [
+ { "hostname": "ALL" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "Options": [
+ { "command_timeout": 30 }
+ ],
+ "Commands": [
+ { "command": "/usr/bin/id" }
+ ]
+ }
+ ]
+ },
+ {
+ "User_List": [
+ { "username": "user7" }
+ ],
+ "Host_List": [
+ { "hostname": "ALL" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "Options": [
+ { "command_timeout": 45 }
+ ],
+ "Commands": [
+ { "command": "/usr/bin/id" }
+ ]
+ }
+ ]
+ },
+ {
+ "User_List": [
+ { "username": "user8" }
+ ],
+ "Host_List": [
+ { "hostname": "ALL" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "Options": [
+ { "command_timeout": 619830 }
+ ],
+ "Commands": [
+ { "command": "/usr/bin/id" },
+ { "command": "/usr/bin/id" },
+ { "command": "/usr/bin/id" },
+ { "command": "/usr/bin/id" },
+ { "command": "/usr/bin/id" },
+ { "command": "/usr/bin/id" }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/plugins/sudoers/regress/sudoers/test17.ldif.ok b/plugins/sudoers/regress/sudoers/test17.ldif.ok
new file mode 100644
index 0000000..bdc784c
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test17.ldif.ok
@@ -0,0 +1,104 @@
+dn: cn=defaults,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: defaults
+description: Default sudoOption's go here
+sudoOption: command_timeout=2d8h10m59s
+
+dn: cn=user0,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: user0
+sudoUser: user0
+sudoHost: ALL
+sudoOption: command_timeout=619830
+sudoCommand: /usr/bin/id
+sudoCommand: /usr/bin/who
+sudoCommand: /bin/ls
+sudoOrder: 1
+
+dn: cn=user1,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: user1
+sudoUser: user1
+sudoHost: ALL
+sudoOption: command_timeout=619830
+sudoCommand: /usr/bin/id
+sudoOrder: 2
+
+dn: cn=user2,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: user2
+sudoUser: user2
+sudoHost: ALL
+sudoOption: command_timeout=15030
+sudoCommand: /usr/bin/id
+sudoOrder: 3
+
+dn: cn=user3,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: user3
+sudoUser: user3
+sudoHost: ALL
+sudoOption: command_timeout=630
+sudoCommand: /usr/bin/id
+sudoOrder: 4
+
+dn: cn=user4,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: user4
+sudoUser: user4
+sudoHost: ALL
+sudoOption: command_timeout=1209600
+sudoCommand: /usr/bin/id
+sudoOrder: 5
+
+dn: cn=user5,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: user5
+sudoUser: user5
+sudoHost: ALL
+sudoOption: command_timeout=300
+sudoCommand: /usr/bin/id
+sudoOrder: 6
+
+dn: cn=user6,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: user6
+sudoUser: user6
+sudoHost: ALL
+sudoOption: command_timeout=30
+sudoCommand: /usr/bin/id
+sudoOrder: 7
+
+dn: cn=user7,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: user7
+sudoUser: user7
+sudoHost: ALL
+sudoOption: command_timeout=45
+sudoCommand: /usr/bin/id
+sudoOrder: 8
+
+dn: cn=user8,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: user8
+sudoUser: user8
+sudoHost: ALL
+sudoOption: command_timeout=619830
+sudoCommand: /usr/bin/id
+sudoCommand: /usr/bin/id
+sudoCommand: /usr/bin/id
+sudoCommand: /usr/bin/id
+sudoCommand: /usr/bin/id
+sudoCommand: /usr/bin/id
+sudoOrder: 9
+
diff --git a/plugins/sudoers/regress/sudoers/test17.ldif2sudo.ok b/plugins/sudoers/regress/sudoers/test17.ldif2sudo.ok
new file mode 100644
index 0000000..608f52f
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test17.ldif2sudo.ok
@@ -0,0 +1,29 @@
+Defaults command_timeout=2d8h10m59s
+
+# sudoRole user0
+user0 ALL = TIMEOUT=619830 /usr/bin/id, /usr/bin/who, /bin/ls
+
+# sudoRole user1
+user1 ALL = TIMEOUT=619830 /usr/bin/id
+
+# sudoRole user2
+user2 ALL = TIMEOUT=15030 /usr/bin/id
+
+# sudoRole user3
+user3 ALL = TIMEOUT=630 /usr/bin/id
+
+# sudoRole user4
+user4 ALL = TIMEOUT=1209600 /usr/bin/id
+
+# sudoRole user5
+user5 ALL = TIMEOUT=300 /usr/bin/id
+
+# sudoRole user6
+user6 ALL = TIMEOUT=30 /usr/bin/id
+
+# sudoRole user7
+user7 ALL = TIMEOUT=45 /usr/bin/id
+
+# sudoRole user8
+user8 ALL = TIMEOUT=619830 /usr/bin/id, /usr/bin/id, /usr/bin/id, /usr/bin/id,\
+ /usr/bin/id, /usr/bin/id
diff --git a/plugins/sudoers/regress/sudoers/test17.out.ok b/plugins/sudoers/regress/sudoers/test17.out.ok
new file mode 100644
index 0000000..f0c8086
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test17.out.ok
@@ -0,0 +1,13 @@
+Parses OK
+
+Defaults command_timeout=2d8h10m59s
+
+user0 ALL = TIMEOUT=619830 /usr/bin/id, /usr/bin/who, /bin/ls
+user1 ALL = TIMEOUT=619830 /usr/bin/id
+user2 ALL = TIMEOUT=15030 /usr/bin/id
+user3 ALL = TIMEOUT=630 /usr/bin/id
+user4 ALL = TIMEOUT=1209600 /usr/bin/id
+user5 ALL = TIMEOUT=300 /usr/bin/id
+user6 ALL = TIMEOUT=30 /usr/bin/id
+user7 ALL = TIMEOUT=45 /usr/bin/id
+user8 ALL = TIMEOUT=619830 /usr/bin/id, TIMEOUT=15030 /usr/bin/id, TIMEOUT=630 /usr/bin/id, TIMEOUT=1209600 /usr/bin/id, TIMEOUT=300 /usr/bin/id, TIMEOUT=30 /usr/bin/id
diff --git a/plugins/sudoers/regress/sudoers/test17.toke.ok b/plugins/sudoers/regress/sudoers/test17.toke.ok
new file mode 100644
index 0000000..d0a82ca
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test17.toke.ok
@@ -0,0 +1,11 @@
+#
+DEFAULTS DEFVAR = WORD(2)
+WORD(6) ALL = CMND_TIMEOUT = WORD(6) COMMAND , COMMAND , CMND_TIMEOUT = WORD(6) COMMAND
+WORD(6) ALL = CMND_TIMEOUT = WORD(6) COMMAND
+WORD(6) ALL = CMND_TIMEOUT = WORD(6) COMMAND
+WORD(6) ALL = CMND_TIMEOUT = WORD(6) COMMAND
+WORD(6) ALL = CMND_TIMEOUT = WORD(6) COMMAND
+WORD(6) ALL = CMND_TIMEOUT = WORD(6) COMMAND
+WORD(6) ALL = CMND_TIMEOUT = WORD(6) COMMAND
+WORD(6) ALL = CMND_TIMEOUT = WORD(6) COMMAND
+WORD(6) ALL = CMND_TIMEOUT = WORD(6) COMMAND , CMND_TIMEOUT = WORD(6) COMMAND , CMND_TIMEOUT = WORD(6) COMMAND , CMND_TIMEOUT = WORD(6) COMMAND , CMND_TIMEOUT = WORD(6) COMMAND , CMND_TIMEOUT = WORD(6) COMMAND
diff --git a/plugins/sudoers/regress/sudoers/test18.in b/plugins/sudoers/regress/sudoers/test18.in
new file mode 100644
index 0000000..8d94ec7
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test18.in
@@ -0,0 +1,8 @@
+# Test command_timeout and TIMEOUT syntax errors
+Defaults command_timeout=2d8h10m59ss
+Defaults:root command_timeout=15f
+user0 ALL = TIMEOUT=7dd4h10m30s /usr/bin/id, /usr/bin/who, TIMEOUT=0 /bin/ls
+user1 ALL = TIMEOUT=7d4h10mm30s /usr/bin/id
+user2 ALL = TIMEOUT=4hg10m30s /usr/bin/id
+user3 ALL = TIMEOUT=10m30ss /usr/bin/id
+user4 ALL = TIMEOUT=14g /usr/bin/id
diff --git a/plugins/sudoers/regress/sudoers/test18.json.ok b/plugins/sudoers/regress/sudoers/test18.json.ok
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test18.json.ok
diff --git a/plugins/sudoers/regress/sudoers/test18.ldif.ok b/plugins/sudoers/regress/sudoers/test18.ldif.ok
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test18.ldif.ok
diff --git a/plugins/sudoers/regress/sudoers/test18.out.ok b/plugins/sudoers/regress/sudoers/test18.out.ok
new file mode 100644
index 0000000..eefdc3a
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test18.out.ok
@@ -0,0 +1,4 @@
+Problem with defaults entries
+
+Defaults command_timeout=2d8h10m59ss
+Defaults:root command_timeout=15f
diff --git a/plugins/sudoers/regress/sudoers/test18.toke.ok b/plugins/sudoers/regress/sudoers/test18.toke.ok
new file mode 100644
index 0000000..78e9ba6
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test18.toke.ok
@@ -0,0 +1,10 @@
+#
+DEFAULTS DEFVAR = WORD(2)
+DEFAULTS_USER WORD(6) DEFVAR = WORD(2)
+WORD(6) ALL = CMND_TIMEOUT = WORD(6) <*> COMMAND , COMMAND , CMND_TIMEOUT = WORD(6) COMMAND
+WORD(6) ALL = CMND_TIMEOUT = WORD(6) <*> COMMAND
+WORD(6) ALL = CMND_TIMEOUT = WORD(6) <*> COMMAND
+WORD(6) ALL = CMND_TIMEOUT = WORD(6) <*> COMMAND
+WORD(6) ALL = CMND_TIMEOUT = WORD(6) <*> COMMAND
+testsudoers: sudoers:2:26: value "2d8h10m59ss" is invalid for option "command_timeout"
+testsudoers: sudoers:3:31: value "15f" is invalid for option "command_timeout"
diff --git a/plugins/sudoers/regress/sudoers/test19.in b/plugins/sudoers/regress/sudoers/test19.in
new file mode 100644
index 0000000..5f637a7
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test19.in
@@ -0,0 +1,12 @@
+# Test parsing of NOTBEFORE and NOTAFTER syntax
+# Local time zone parsing is checked in visudo/test10.sh
+user0 ALL = NOTBEFORE=20170214083000Z NOTAFTER=20170301083000Z /usr/bin/id, /bin/ls
+user1 ALL = NOTBEFORE=201702140830Z /usr/bin/id, NOTAFTER=20170301083000Z /bin/ls
+user2 ALL = NOTBEFORE=201702140830.3Z /usr/bin/id
+user3 ALL = NOTBEFORE=2017021408Z /usr/bin/id
+user4 ALL = NOTBEFORE=2017021408.4Z /usr/bin/id
+user5 ALL = NOTBEFORE=20170214083000.5Z /usr/bin/id
+user6 ALL = NOTBEFORE=20170214083000\,5Z /usr/bin/id
+user7 ALL = NOTBEFORE=20170214033000-0500 /usr/bin/id
+user8 ALL = NOTBEFORE=20170214033000.0-0500 /usr/bin/id
+user9 ALL = NOTBEFORE=20170214033000\,0-0500 /usr/bin/id
diff --git a/plugins/sudoers/regress/sudoers/test19.json.ok b/plugins/sudoers/regress/sudoers/test19.json.ok
new file mode 100644
index 0000000..c9a1bfd
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test19.json.ok
@@ -0,0 +1,187 @@
+{
+ "User_Specs": [
+ {
+ "User_List": [
+ { "username": "user0" }
+ ],
+ "Host_List": [
+ { "hostname": "ALL" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "Options": [
+ { "notbefore": "20170214083000Z" },
+ { "notafter": "20170301083000Z" }
+ ],
+ "Commands": [
+ { "command": "/usr/bin/id" },
+ { "command": "/bin/ls" }
+ ]
+ }
+ ]
+ },
+ {
+ "User_List": [
+ { "username": "user1" }
+ ],
+ "Host_List": [
+ { "hostname": "ALL" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "Options": [
+ { "notbefore": "20170214083000Z" }
+ ],
+ "Commands": [
+ { "command": "/usr/bin/id" },
+ { "command": "/bin/ls" }
+ ]
+ }
+ ]
+ },
+ {
+ "User_List": [
+ { "username": "user2" }
+ ],
+ "Host_List": [
+ { "hostname": "ALL" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "Options": [
+ { "notbefore": "20170214083018Z" }
+ ],
+ "Commands": [
+ { "command": "/usr/bin/id" }
+ ]
+ }
+ ]
+ },
+ {
+ "User_List": [
+ { "username": "user3" }
+ ],
+ "Host_List": [
+ { "hostname": "ALL" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "Options": [
+ { "notbefore": "20170214080000Z" }
+ ],
+ "Commands": [
+ { "command": "/usr/bin/id" }
+ ]
+ }
+ ]
+ },
+ {
+ "User_List": [
+ { "username": "user4" }
+ ],
+ "Host_List": [
+ { "hostname": "ALL" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "Options": [
+ { "notbefore": "20170214082400Z" }
+ ],
+ "Commands": [
+ { "command": "/usr/bin/id" }
+ ]
+ }
+ ]
+ },
+ {
+ "User_List": [
+ { "username": "user5" }
+ ],
+ "Host_List": [
+ { "hostname": "ALL" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "Options": [
+ { "notbefore": "20170214083000Z" }
+ ],
+ "Commands": [
+ { "command": "/usr/bin/id" }
+ ]
+ }
+ ]
+ },
+ {
+ "User_List": [
+ { "username": "user6" }
+ ],
+ "Host_List": [
+ { "hostname": "ALL" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "Options": [
+ { "notbefore": "20170214083000Z" }
+ ],
+ "Commands": [
+ { "command": "/usr/bin/id" }
+ ]
+ }
+ ]
+ },
+ {
+ "User_List": [
+ { "username": "user7" }
+ ],
+ "Host_List": [
+ { "hostname": "ALL" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "Options": [
+ { "notbefore": "20170214083000Z" }
+ ],
+ "Commands": [
+ { "command": "/usr/bin/id" }
+ ]
+ }
+ ]
+ },
+ {
+ "User_List": [
+ { "username": "user8" }
+ ],
+ "Host_List": [
+ { "hostname": "ALL" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "Options": [
+ { "notbefore": "20170214083000Z" }
+ ],
+ "Commands": [
+ { "command": "/usr/bin/id" }
+ ]
+ }
+ ]
+ },
+ {
+ "User_List": [
+ { "username": "user9" }
+ ],
+ "Host_List": [
+ { "hostname": "ALL" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "Options": [
+ { "notbefore": "20170214083000Z" }
+ ],
+ "Commands": [
+ { "command": "/usr/bin/id" }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/plugins/sudoers/regress/sudoers/test19.ldif.ok b/plugins/sudoers/regress/sudoers/test19.ldif.ok
new file mode 100644
index 0000000..362aa9e
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test19.ldif.ok
@@ -0,0 +1,103 @@
+dn: cn=user0,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: user0
+sudoUser: user0
+sudoHost: ALL
+sudoNotBefore: 20170214083000Z
+sudoNotAfter: 20170301083000Z
+sudoCommand: /usr/bin/id
+sudoCommand: /bin/ls
+sudoOrder: 1
+
+dn: cn=user1,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: user1
+sudoUser: user1
+sudoHost: ALL
+sudoNotBefore: 20170214083000Z
+sudoCommand: /usr/bin/id
+sudoCommand: /bin/ls
+sudoOrder: 2
+
+dn: cn=user2,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: user2
+sudoUser: user2
+sudoHost: ALL
+sudoNotBefore: 20170214083018Z
+sudoCommand: /usr/bin/id
+sudoOrder: 3
+
+dn: cn=user3,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: user3
+sudoUser: user3
+sudoHost: ALL
+sudoNotBefore: 20170214080000Z
+sudoCommand: /usr/bin/id
+sudoOrder: 4
+
+dn: cn=user4,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: user4
+sudoUser: user4
+sudoHost: ALL
+sudoNotBefore: 20170214082400Z
+sudoCommand: /usr/bin/id
+sudoOrder: 5
+
+dn: cn=user5,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: user5
+sudoUser: user5
+sudoHost: ALL
+sudoNotBefore: 20170214083000Z
+sudoCommand: /usr/bin/id
+sudoOrder: 6
+
+dn: cn=user6,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: user6
+sudoUser: user6
+sudoHost: ALL
+sudoNotBefore: 20170214083000Z
+sudoCommand: /usr/bin/id
+sudoOrder: 7
+
+dn: cn=user7,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: user7
+sudoUser: user7
+sudoHost: ALL
+sudoNotBefore: 20170214083000Z
+sudoCommand: /usr/bin/id
+sudoOrder: 8
+
+dn: cn=user8,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: user8
+sudoUser: user8
+sudoHost: ALL
+sudoNotBefore: 20170214083000Z
+sudoCommand: /usr/bin/id
+sudoOrder: 9
+
+dn: cn=user9,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: user9
+sudoUser: user9
+sudoHost: ALL
+sudoNotBefore: 20170214083000Z
+sudoCommand: /usr/bin/id
+sudoOrder: 10
+
diff --git a/plugins/sudoers/regress/sudoers/test19.ldif2sudo.ok b/plugins/sudoers/regress/sudoers/test19.ldif2sudo.ok
new file mode 100644
index 0000000..1aef1bc
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test19.ldif2sudo.ok
@@ -0,0 +1,30 @@
+# sudoRole user0
+user0 ALL = NOTBEFORE=20170214083000Z NOTAFTER=20170301083000Z /usr/bin/id,\
+ /bin/ls
+
+# sudoRole user1
+user1 ALL = NOTBEFORE=20170214083000Z /usr/bin/id, /bin/ls
+
+# sudoRole user2
+user2 ALL = NOTBEFORE=20170214083018Z /usr/bin/id
+
+# sudoRole user3
+user3 ALL = NOTBEFORE=20170214080000Z /usr/bin/id
+
+# sudoRole user4
+user4 ALL = NOTBEFORE=20170214082400Z /usr/bin/id
+
+# sudoRole user5
+user5 ALL = NOTBEFORE=20170214083000Z /usr/bin/id
+
+# sudoRole user6
+user6 ALL = NOTBEFORE=20170214083000Z /usr/bin/id
+
+# sudoRole user7
+user7 ALL = NOTBEFORE=20170214083000Z /usr/bin/id
+
+# sudoRole user8
+user8 ALL = NOTBEFORE=20170214083000Z /usr/bin/id
+
+# sudoRole user9
+user9 ALL = NOTBEFORE=20170214083000Z /usr/bin/id
diff --git a/plugins/sudoers/regress/sudoers/test19.out.ok b/plugins/sudoers/regress/sudoers/test19.out.ok
new file mode 100644
index 0000000..dacfefd
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test19.out.ok
@@ -0,0 +1,12 @@
+Parses OK
+
+user0 ALL = NOTBEFORE=20170214083000Z NOTAFTER=20170301083000Z /usr/bin/id, /bin/ls
+user1 ALL = NOTBEFORE=20170214083000Z /usr/bin/id, NOTAFTER=20170301083000Z /bin/ls
+user2 ALL = NOTBEFORE=20170214083018Z /usr/bin/id
+user3 ALL = NOTBEFORE=20170214080000Z /usr/bin/id
+user4 ALL = NOTBEFORE=20170214082400Z /usr/bin/id
+user5 ALL = NOTBEFORE=20170214083000Z /usr/bin/id
+user6 ALL = NOTBEFORE=20170214083000Z /usr/bin/id
+user7 ALL = NOTBEFORE=20170214083000Z /usr/bin/id
+user8 ALL = NOTBEFORE=20170214083000Z /usr/bin/id
+user9 ALL = NOTBEFORE=20170214083000Z /usr/bin/id
diff --git a/plugins/sudoers/regress/sudoers/test19.toke.ok b/plugins/sudoers/regress/sudoers/test19.toke.ok
new file mode 100644
index 0000000..04461d9
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test19.toke.ok
@@ -0,0 +1,12 @@
+#
+#
+WORD(6) ALL = NOTBEFORE = WORD(6) NOTAFTER = WORD(6) COMMAND , COMMAND
+WORD(6) ALL = NOTBEFORE = WORD(6) COMMAND , NOTAFTER = WORD(6) COMMAND
+WORD(6) ALL = NOTBEFORE = WORD(6) COMMAND
+WORD(6) ALL = NOTBEFORE = WORD(6) COMMAND
+WORD(6) ALL = NOTBEFORE = WORD(6) COMMAND
+WORD(6) ALL = NOTBEFORE = WORD(6) COMMAND
+WORD(6) ALL = NOTBEFORE = WORD(6) COMMAND
+WORD(6) ALL = NOTBEFORE = WORD(6) COMMAND
+WORD(6) ALL = NOTBEFORE = WORD(6) COMMAND
+WORD(6) ALL = NOTBEFORE = WORD(6) COMMAND
diff --git a/plugins/sudoers/regress/sudoers/test2.in b/plugins/sudoers/regress/sudoers/test2.in
new file mode 100644
index 0000000..cfdfaa3
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test2.in
@@ -0,0 +1,60 @@
+# Check quoted user name in User_Alias
+User_Alias UA1 = "foo"
+User_Alias UA2 = "foo.bar"
+User_Alias UA3 = "foo\""
+User_Alias UA4 = "foo:bar"
+User_Alias UA5 = "foo:bar\""
+
+# Check quoted group name in User_Alias
+User_Alias UA6 = "%baz"
+User_Alias UA7 = "%baz.biz"
+
+# Check quoted non-Unix group name in User_Alias
+User_Alias UA8 = "%:C/non UNIX 0 c"
+User_Alias UA9 = "%:C/non\'UNIX\'1 c"
+User_Alias UA10 = "%:C/non\"UNIX\"0 c"
+User_Alias UA11 = "%:C/non_UNIX_0 c"
+User_Alias UA12 = "%:C/non\'UNIX_3 c"
+
+# Check quoted user name in Runas_Alias
+Runas_Alias RA1 = "foo"
+Runas_Alias RA2 = "foo\""
+Runas_Alias RA3 = "foo:bar"
+Runas_Alias RA4 = "foo:bar\""
+
+# Check quoted host name in Defaults
+Defaults@"somehost" set_home
+Defaults@"quoted\"" set_home
+
+# Check quoted user name in Defaults
+Defaults:"you" set_home
+Defaults:"us\"" set_home
+Defaults:"%them" set_home
+Defaults:"%: non UNIX 0 c" set_home
+Defaults:"+net" set_home
+
+# Check quoted runas name in Defaults
+Defaults>"someone" set_home
+Defaults>"some one" set_home
+
+# Check quoted command in Defaults
+# XXX - not currently supported
+#Defaults!"/bin/ls -l" set_home
+#Defaults!"/bin/ls -l \"foo\"" set_home
+
+# Check quoted user, runas and host name in Cmnd_Spec
+"foo" "hosta" = ("root") ALL
+"foo.bar" "hostb" = ("root") ALL
+"foo\"" "hostc" = ("root") ALL
+"foo:bar" "hostd" = ("root") ALL
+"foo:bar\"" "hoste" = ("root") ALL
+
+# Check quoted group/netgroup name in Cmnd_Spec
+"%baz" "hosta" = ("root") ALL
+"%baz.biz" "hostb" = ("root") ALL
+"%:C/non UNIX 0 c" "hostc" = ("root") ALL
+"%:C/non\'UNIX\'1 c" "hostd" = ("root") ALL
+"%:C/non\"UNIX\"0 c" "hoste" = ("root") ALL
+"%:C/non_UNIX_0 c" "hostf" = ("root") ALL
+"%:C/non\'UNIX_3 c" "hostg" = ("root") ALL
+"+netgr" "hosth" = ("root") ALL
diff --git a/plugins/sudoers/regress/sudoers/test2.json.ok b/plugins/sudoers/regress/sudoers/test2.json.ok
new file mode 100644
index 0000000..769c3fd
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test2.json.ok
@@ -0,0 +1,403 @@
+{
+ "Defaults": [
+ {
+ "Binding": [
+ { "hostname": "somehost" }
+ ],
+ "Options": [
+ { "set_home": true }
+ ]
+ },
+ {
+ "Binding": [
+ { "hostname": "quoted\"" }
+ ],
+ "Options": [
+ { "set_home": true }
+ ]
+ },
+ {
+ "Binding": [
+ { "username": "you" }
+ ],
+ "Options": [
+ { "set_home": true }
+ ]
+ },
+ {
+ "Binding": [
+ { "username": "us\"" }
+ ],
+ "Options": [
+ { "set_home": true }
+ ]
+ },
+ {
+ "Binding": [
+ { "usergroup": "them" }
+ ],
+ "Options": [
+ { "set_home": true }
+ ]
+ },
+ {
+ "Binding": [
+ { "nonunixgroup": " non UNIX 0 c" }
+ ],
+ "Options": [
+ { "set_home": true }
+ ]
+ },
+ {
+ "Binding": [
+ { "netgroup": "net" }
+ ],
+ "Options": [
+ { "set_home": true }
+ ]
+ },
+ {
+ "Binding": [
+ { "username": "someone" }
+ ],
+ "Options": [
+ { "set_home": true }
+ ]
+ },
+ {
+ "Binding": [
+ { "username": "some one" }
+ ],
+ "Options": [
+ { "set_home": true }
+ ]
+ }
+ ],
+ "User_Aliases": {
+ "UA1": [
+ { "username": "foo" }
+ ],
+ "UA10": [
+ { "nonunixgroup": "C/non\"UNIX\"0 c" }
+ ],
+ "UA11": [
+ { "nonunixgroup": "C/non_UNIX_0 c" }
+ ],
+ "UA12": [
+ { "nonunixgroup": "C/non\\'UNIX_3 c" }
+ ],
+ "UA2": [
+ { "username": "foo.bar" }
+ ],
+ "UA3": [
+ { "username": "foo\"" }
+ ],
+ "UA4": [
+ { "username": "foo:bar" }
+ ],
+ "UA5": [
+ { "username": "foo:bar\"" }
+ ],
+ "UA6": [
+ { "usergroup": "baz" }
+ ],
+ "UA7": [
+ { "usergroup": "baz.biz" }
+ ],
+ "UA8": [
+ { "nonunixgroup": "C/non UNIX 0 c" }
+ ],
+ "UA9": [
+ { "nonunixgroup": "C/non\\'UNIX\\'1 c" }
+ ]
+ },
+ "Runas_Aliases": {
+ "RA1": [
+ { "username": "foo" }
+ ],
+ "RA2": [
+ { "username": "foo\"" }
+ ],
+ "RA3": [
+ { "username": "foo:bar" }
+ ],
+ "RA4": [
+ { "username": "foo:bar\"" }
+ ]
+ },
+ "User_Specs": [
+ {
+ "User_List": [
+ { "username": "foo" }
+ ],
+ "Host_List": [
+ { "hostname": "hosta" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "runasusers": [
+ { "username": "root" }
+ ],
+ "Options": [
+ { "setenv": true }
+ ],
+ "Commands": [
+ { "command": "ALL" }
+ ]
+ }
+ ]
+ },
+ {
+ "User_List": [
+ { "username": "foo.bar" }
+ ],
+ "Host_List": [
+ { "hostname": "hostb" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "runasusers": [
+ { "username": "root" }
+ ],
+ "Options": [
+ { "setenv": true }
+ ],
+ "Commands": [
+ { "command": "ALL" }
+ ]
+ }
+ ]
+ },
+ {
+ "User_List": [
+ { "username": "foo\"" }
+ ],
+ "Host_List": [
+ { "hostname": "hostc" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "runasusers": [
+ { "username": "root" }
+ ],
+ "Options": [
+ { "setenv": true }
+ ],
+ "Commands": [
+ { "command": "ALL" }
+ ]
+ }
+ ]
+ },
+ {
+ "User_List": [
+ { "username": "foo:bar" }
+ ],
+ "Host_List": [
+ { "hostname": "hostd" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "runasusers": [
+ { "username": "root" }
+ ],
+ "Options": [
+ { "setenv": true }
+ ],
+ "Commands": [
+ { "command": "ALL" }
+ ]
+ }
+ ]
+ },
+ {
+ "User_List": [
+ { "username": "foo:bar\"" }
+ ],
+ "Host_List": [
+ { "hostname": "hoste" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "runasusers": [
+ { "username": "root" }
+ ],
+ "Options": [
+ { "setenv": true }
+ ],
+ "Commands": [
+ { "command": "ALL" }
+ ]
+ }
+ ]
+ },
+ {
+ "User_List": [
+ { "usergroup": "baz" }
+ ],
+ "Host_List": [
+ { "hostname": "hosta" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "runasusers": [
+ { "username": "root" }
+ ],
+ "Options": [
+ { "setenv": true }
+ ],
+ "Commands": [
+ { "command": "ALL" }
+ ]
+ }
+ ]
+ },
+ {
+ "User_List": [
+ { "usergroup": "baz.biz" }
+ ],
+ "Host_List": [
+ { "hostname": "hostb" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "runasusers": [
+ { "username": "root" }
+ ],
+ "Options": [
+ { "setenv": true }
+ ],
+ "Commands": [
+ { "command": "ALL" }
+ ]
+ }
+ ]
+ },
+ {
+ "User_List": [
+ { "nonunixgroup": "C/non UNIX 0 c" }
+ ],
+ "Host_List": [
+ { "hostname": "hostc" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "runasusers": [
+ { "username": "root" }
+ ],
+ "Options": [
+ { "setenv": true }
+ ],
+ "Commands": [
+ { "command": "ALL" }
+ ]
+ }
+ ]
+ },
+ {
+ "User_List": [
+ { "nonunixgroup": "C/non\\'UNIX\\'1 c" }
+ ],
+ "Host_List": [
+ { "hostname": "hostd" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "runasusers": [
+ { "username": "root" }
+ ],
+ "Options": [
+ { "setenv": true }
+ ],
+ "Commands": [
+ { "command": "ALL" }
+ ]
+ }
+ ]
+ },
+ {
+ "User_List": [
+ { "nonunixgroup": "C/non\"UNIX\"0 c" }
+ ],
+ "Host_List": [
+ { "hostname": "hoste" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "runasusers": [
+ { "username": "root" }
+ ],
+ "Options": [
+ { "setenv": true }
+ ],
+ "Commands": [
+ { "command": "ALL" }
+ ]
+ }
+ ]
+ },
+ {
+ "User_List": [
+ { "nonunixgroup": "C/non_UNIX_0 c" }
+ ],
+ "Host_List": [
+ { "hostname": "hostf" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "runasusers": [
+ { "username": "root" }
+ ],
+ "Options": [
+ { "setenv": true }
+ ],
+ "Commands": [
+ { "command": "ALL" }
+ ]
+ }
+ ]
+ },
+ {
+ "User_List": [
+ { "nonunixgroup": "C/non\\'UNIX_3 c" }
+ ],
+ "Host_List": [
+ { "hostname": "hostg" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "runasusers": [
+ { "username": "root" }
+ ],
+ "Options": [
+ { "setenv": true }
+ ],
+ "Commands": [
+ { "command": "ALL" }
+ ]
+ }
+ ]
+ },
+ {
+ "User_List": [
+ { "netgroup": "netgr" }
+ ],
+ "Host_List": [
+ { "hostname": "hosth" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "runasusers": [
+ { "username": "root" }
+ ],
+ "Options": [
+ { "setenv": true }
+ ],
+ "Commands": [
+ { "command": "ALL" }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/plugins/sudoers/regress/sudoers/test2.ldif.ok b/plugins/sudoers/regress/sudoers/test2.ldif.ok
new file mode 100644
index 0000000..48ba655
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test2.ldif.ok
@@ -0,0 +1,157 @@
+# Unable to translate stdin:26:29:
+# Defaults@somehost set_home
+
+# Unable to translate stdin:27:29:
+# Defaults@quoted\" set_home
+
+# Unable to translate stdin:30:24:
+# Defaults:you set_home
+
+# Unable to translate stdin:31:25:
+# Defaults:us\" set_home
+
+# Unable to translate stdin:32:26:
+# Defaults:%them set_home
+
+# Unable to translate stdin:33:36:
+# Defaults:"%: non UNIX 0 c" set_home
+
+# Unable to translate stdin:34:25:
+# Defaults:+net set_home
+
+# Unable to translate stdin:37:28:
+# Defaults>someone set_home
+
+# Unable to translate stdin:38:29:
+# Defaults>"some one" set_home
+
+dn: cn=foo,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: foo
+sudoUser: foo
+sudoHost: hosta
+sudoRunAsUser: root
+sudoCommand: ALL
+sudoOrder: 1
+
+dn: cn=foo.bar,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: foo.bar
+sudoUser: foo.bar
+sudoHost: hostb
+sudoRunAsUser: root
+sudoCommand: ALL
+sudoOrder: 2
+
+dn: cn=foo\",ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: foo\"
+sudoUser: foo"
+sudoHost: hostc
+sudoRunAsUser: root
+sudoCommand: ALL
+sudoOrder: 3
+
+dn: cn=foo:bar,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: foo:bar
+sudoUser: foo:bar
+sudoHost: hostd
+sudoRunAsUser: root
+sudoCommand: ALL
+sudoOrder: 4
+
+dn: cn=foo:bar\",ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: foo:bar\"
+sudoUser: foo:bar"
+sudoHost: hoste
+sudoRunAsUser: root
+sudoCommand: ALL
+sudoOrder: 5
+
+dn: cn=%baz,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: %baz
+sudoUser: %baz
+sudoHost: hosta
+sudoRunAsUser: root
+sudoCommand: ALL
+sudoOrder: 6
+
+dn: cn=%baz.biz,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: %baz.biz
+sudoUser: %baz.biz
+sudoHost: hostb
+sudoRunAsUser: root
+sudoCommand: ALL
+sudoOrder: 7
+
+dn: cn=%:C/non UNIX 0 c,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: %:C/non UNIX 0 c
+sudoUser: %:C/non UNIX 0 c
+sudoHost: hostc
+sudoRunAsUser: root
+sudoCommand: ALL
+sudoOrder: 8
+
+dn: cn=%:C/non\\'UNIX\\'1 c,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: %:C/non\\'UNIX\\'1 c
+sudoUser: %:C/non\'UNIX\'1 c
+sudoHost: hostd
+sudoRunAsUser: root
+sudoCommand: ALL
+sudoOrder: 9
+
+dn: cn=%:C/non\"UNIX\"0 c,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: %:C/non\"UNIX\"0 c
+sudoUser: %:C/non"UNIX"0 c
+sudoHost: hoste
+sudoRunAsUser: root
+sudoCommand: ALL
+sudoOrder: 10
+
+dn: cn=%:C/non_UNIX_0 c,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: %:C/non_UNIX_0 c
+sudoUser: %:C/non_UNIX_0 c
+sudoHost: hostf
+sudoRunAsUser: root
+sudoCommand: ALL
+sudoOrder: 11
+
+dn: cn=%:C/non\\'UNIX_3 c,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: %:C/non\\'UNIX_3 c
+sudoUser: %:C/non\'UNIX_3 c
+sudoHost: hostg
+sudoRunAsUser: root
+sudoCommand: ALL
+sudoOrder: 12
+
+dn: cn=\+netgr,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: \+netgr
+sudoUser: +netgr
+sudoHost: hosth
+sudoRunAsUser: root
+sudoCommand: ALL
+sudoOrder: 13
+
diff --git a/plugins/sudoers/regress/sudoers/test2.ldif2sudo.ok b/plugins/sudoers/regress/sudoers/test2.ldif2sudo.ok
new file mode 100644
index 0000000..7039523
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test2.ldif2sudo.ok
@@ -0,0 +1,38 @@
+# sudoRole foo
+foo hosta = (root) ALL
+
+# sudoRole foo.bar
+foo.bar hostb = (root) ALL
+
+# sudoRole foo"
+foo\" hostc = (root) ALL
+
+# sudoRole foo:bar
+foo\:bar hostd = (root) ALL
+
+# sudoRole foo:bar"
+foo\:bar\" hoste = (root) ALL
+
+# sudoRole %baz
+%baz hosta = (root) ALL
+
+# sudoRole %baz.biz
+%baz.biz hostb = (root) ALL
+
+# sudoRole %:C/non UNIX 0 c
+"%:C/non UNIX 0 c" hostc = (root) ALL
+
+# sudoRole %:C/non\'UNIX\'1 c
+"%:C/non\'UNIX\'1 c" hostd = (root) ALL
+
+# sudoRole %:C/non"UNIX"0 c
+"%:C/non\"UNIX\"0 c" hoste = (root) ALL
+
+# sudoRole %:C/non_UNIX_0 c
+"%:C/non_UNIX_0 c" hostf = (root) ALL
+
+# sudoRole %:C/non\'UNIX_3 c
+"%:C/non\'UNIX_3 c" hostg = (root) ALL
+
+# sudoRole +netgr
++netgr hosth = (root) ALL
diff --git a/plugins/sudoers/regress/sudoers/test2.out.ok b/plugins/sudoers/regress/sudoers/test2.out.ok
new file mode 100644
index 0000000..c99f6d0
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test2.out.ok
@@ -0,0 +1,42 @@
+Parses OK
+
+Defaults@somehost set_home
+Defaults@quoted\" set_home
+Defaults:you set_home
+Defaults:us\" set_home
+Defaults:%them set_home
+Defaults:"%: non UNIX 0 c" set_home
+Defaults:+net set_home
+Defaults>someone set_home
+Defaults>"some one" set_home
+
+Runas_Alias RA1 = foo
+Runas_Alias RA2 = foo\"
+Runas_Alias RA3 = foo\:bar
+Runas_Alias RA4 = foo\:bar\"
+User_Alias UA1 = foo
+User_Alias UA10 = "%:C/non\"UNIX\"0 c"
+User_Alias UA11 = "%:C/non_UNIX_0 c"
+User_Alias UA12 = "%:C/non\'UNIX_3 c"
+User_Alias UA2 = foo.bar
+User_Alias UA3 = foo\"
+User_Alias UA4 = foo\:bar
+User_Alias UA5 = foo\:bar\"
+User_Alias UA6 = %baz
+User_Alias UA7 = %baz.biz
+User_Alias UA8 = "%:C/non UNIX 0 c"
+User_Alias UA9 = "%:C/non\'UNIX\'1 c"
+
+foo hosta = (root) ALL
+foo.bar hostb = (root) ALL
+foo\" hostc = (root) ALL
+foo\:bar hostd = (root) ALL
+foo\:bar\" hoste = (root) ALL
+%baz hosta = (root) ALL
+%baz.biz hostb = (root) ALL
+"%:C/non UNIX 0 c" hostc = (root) ALL
+"%:C/non\'UNIX\'1 c" hostd = (root) ALL
+"%:C/non\"UNIX\"0 c" hoste = (root) ALL
+"%:C/non_UNIX_0 c" hostf = (root) ALL
+"%:C/non\'UNIX_3 c" hostg = (root) ALL
++netgr hosth = (root) ALL
diff --git a/plugins/sudoers/regress/sudoers/test2.toke.ok b/plugins/sudoers/regress/sudoers/test2.toke.ok
new file mode 100644
index 0000000..63e1648
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test2.toke.ok
@@ -0,0 +1,60 @@
+#
+USERALIAS ALIAS = BEGINSTR STRBODY ENDSTR WORD(4)
+USERALIAS ALIAS = BEGINSTR STRBODY ENDSTR WORD(4)
+USERALIAS ALIAS = BEGINSTR STRBODY ENDSTR WORD(4)
+USERALIAS ALIAS = BEGINSTR STRBODY ENDSTR WORD(4)
+USERALIAS ALIAS = BEGINSTR STRBODY ENDSTR WORD(4)
+
+#
+USERALIAS ALIAS = BEGINSTR STRBODY ENDSTR USERGROUP
+USERALIAS ALIAS = BEGINSTR STRBODY ENDSTR USERGROUP
+
+#
+USERALIAS ALIAS = BEGINSTR STRBODY ENDSTR USERGROUP
+USERALIAS ALIAS = BEGINSTR STRBODY BACKSLASH STRBODY BACKSLASH STRBODY ENDSTR USERGROUP
+USERALIAS ALIAS = BEGINSTR STRBODY ENDSTR USERGROUP
+USERALIAS ALIAS = BEGINSTR STRBODY ENDSTR USERGROUP
+USERALIAS ALIAS = BEGINSTR STRBODY BACKSLASH STRBODY ENDSTR USERGROUP
+
+#
+RUNASALIAS ALIAS = BEGINSTR STRBODY ENDSTR WORD(4)
+RUNASALIAS ALIAS = BEGINSTR STRBODY ENDSTR WORD(4)
+RUNASALIAS ALIAS = BEGINSTR STRBODY ENDSTR WORD(4)
+RUNASALIAS ALIAS = BEGINSTR STRBODY ENDSTR WORD(4)
+
+#
+DEFAULTS_HOST BEGINSTR STRBODY ENDSTR WORD(4) DEFVAR
+DEFAULTS_HOST BEGINSTR STRBODY ENDSTR WORD(4) DEFVAR
+
+#
+DEFAULTS_USER BEGINSTR STRBODY ENDSTR WORD(4) DEFVAR
+DEFAULTS_USER BEGINSTR STRBODY ENDSTR WORD(4) DEFVAR
+DEFAULTS_USER BEGINSTR STRBODY ENDSTR USERGROUP DEFVAR
+DEFAULTS_USER BEGINSTR STRBODY ENDSTR USERGROUP DEFVAR
+DEFAULTS_USER BEGINSTR STRBODY ENDSTR NETGROUP DEFVAR
+
+#
+DEFAULTS_RUNAS BEGINSTR STRBODY ENDSTR WORD(4) DEFVAR
+DEFAULTS_RUNAS BEGINSTR STRBODY ENDSTR WORD(4) DEFVAR
+
+#
+#
+#
+#
+
+#
+BEGINSTR STRBODY ENDSTR WORD(4) BEGINSTR STRBODY ENDSTR WORD(4) = ( BEGINSTR STRBODY ENDSTR WORD(4) ) ALL
+BEGINSTR STRBODY ENDSTR WORD(4) BEGINSTR STRBODY ENDSTR WORD(4) = ( BEGINSTR STRBODY ENDSTR WORD(4) ) ALL
+BEGINSTR STRBODY ENDSTR WORD(4) BEGINSTR STRBODY ENDSTR WORD(4) = ( BEGINSTR STRBODY ENDSTR WORD(4) ) ALL
+BEGINSTR STRBODY ENDSTR WORD(4) BEGINSTR STRBODY ENDSTR WORD(4) = ( BEGINSTR STRBODY ENDSTR WORD(4) ) ALL
+BEGINSTR STRBODY ENDSTR WORD(4) BEGINSTR STRBODY ENDSTR WORD(4) = ( BEGINSTR STRBODY ENDSTR WORD(4) ) ALL
+
+#
+BEGINSTR STRBODY ENDSTR USERGROUP BEGINSTR STRBODY ENDSTR WORD(4) = ( BEGINSTR STRBODY ENDSTR WORD(4) ) ALL
+BEGINSTR STRBODY ENDSTR USERGROUP BEGINSTR STRBODY ENDSTR WORD(4) = ( BEGINSTR STRBODY ENDSTR WORD(4) ) ALL
+BEGINSTR STRBODY ENDSTR USERGROUP BEGINSTR STRBODY ENDSTR WORD(4) = ( BEGINSTR STRBODY ENDSTR WORD(4) ) ALL
+BEGINSTR STRBODY BACKSLASH STRBODY BACKSLASH STRBODY ENDSTR USERGROUP BEGINSTR STRBODY ENDSTR WORD(4) = ( BEGINSTR STRBODY ENDSTR WORD(4) ) ALL
+BEGINSTR STRBODY ENDSTR USERGROUP BEGINSTR STRBODY ENDSTR WORD(4) = ( BEGINSTR STRBODY ENDSTR WORD(4) ) ALL
+BEGINSTR STRBODY ENDSTR USERGROUP BEGINSTR STRBODY ENDSTR WORD(4) = ( BEGINSTR STRBODY ENDSTR WORD(4) ) ALL
+BEGINSTR STRBODY BACKSLASH STRBODY ENDSTR USERGROUP BEGINSTR STRBODY ENDSTR WORD(4) = ( BEGINSTR STRBODY ENDSTR WORD(4) ) ALL
+BEGINSTR STRBODY ENDSTR NETGROUP BEGINSTR STRBODY ENDSTR WORD(4) = ( BEGINSTR STRBODY ENDSTR WORD(4) ) ALL
diff --git a/plugins/sudoers/regress/sudoers/test20.in b/plugins/sudoers/regress/sudoers/test20.in
new file mode 100644
index 0000000..c24f88a
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test20.in
@@ -0,0 +1,26 @@
+# Test parsing of tuples
+Defaults lecture
+Defaults !lecture
+Defaults lecture=never
+Defaults lecture=once
+Defaults lecture=always
+
+Defaults listpw
+Defaults !listpw
+Defaults listpw=never
+Defaults listpw=any
+Defaults listpw=all
+Defaults listpw=always
+
+Defaults verifypw
+Defaults !verifypw
+Defaults verifypw=never
+Defaults verifypw=any
+Defaults verifypw=all
+Defaults verifypw=always
+
+Defaults fdexec
+Defaults !fdexec
+Defaults fdexec=never
+Defaults fdexec=digest_only
+Defaults fdexec=always
diff --git a/plugins/sudoers/regress/sudoers/test20.json.ok b/plugins/sudoers/regress/sudoers/test20.json.ok
new file mode 100644
index 0000000..f2f1d55
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test20.json.ok
@@ -0,0 +1,114 @@
+{
+ "Defaults": [
+ {
+ "Options": [
+ { "lecture": true }
+ ]
+ },
+ {
+ "Options": [
+ { "lecture": false }
+ ]
+ },
+ {
+ "Options": [
+ { "lecture": "never" }
+ ]
+ },
+ {
+ "Options": [
+ { "lecture": "once" }
+ ]
+ },
+ {
+ "Options": [
+ { "lecture": "always" }
+ ]
+ },
+ {
+ "Options": [
+ { "listpw": true }
+ ]
+ },
+ {
+ "Options": [
+ { "listpw": false }
+ ]
+ },
+ {
+ "Options": [
+ { "listpw": "never" }
+ ]
+ },
+ {
+ "Options": [
+ { "listpw": "any" }
+ ]
+ },
+ {
+ "Options": [
+ { "listpw": "all" }
+ ]
+ },
+ {
+ "Options": [
+ { "listpw": "always" }
+ ]
+ },
+ {
+ "Options": [
+ { "verifypw": true }
+ ]
+ },
+ {
+ "Options": [
+ { "verifypw": false }
+ ]
+ },
+ {
+ "Options": [
+ { "verifypw": "never" }
+ ]
+ },
+ {
+ "Options": [
+ { "verifypw": "any" }
+ ]
+ },
+ {
+ "Options": [
+ { "verifypw": "all" }
+ ]
+ },
+ {
+ "Options": [
+ { "verifypw": "always" }
+ ]
+ },
+ {
+ "Options": [
+ { "fdexec": true }
+ ]
+ },
+ {
+ "Options": [
+ { "fdexec": false }
+ ]
+ },
+ {
+ "Options": [
+ { "fdexec": "never" }
+ ]
+ },
+ {
+ "Options": [
+ { "fdexec": "digest_only" }
+ ]
+ },
+ {
+ "Options": [
+ { "fdexec": "always" }
+ ]
+ }
+ ]
+}
diff --git a/plugins/sudoers/regress/sudoers/test20.ldif.ok b/plugins/sudoers/regress/sudoers/test20.ldif.ok
new file mode 100644
index 0000000..de01cde
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test20.ldif.ok
@@ -0,0 +1,28 @@
+dn: cn=defaults,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: defaults
+description: Default sudoOption's go here
+sudoOption: lecture
+sudoOption: !lecture
+sudoOption: lecture=never
+sudoOption: lecture=once
+sudoOption: lecture=always
+sudoOption: listpw
+sudoOption: !listpw
+sudoOption: listpw=never
+sudoOption: listpw=any
+sudoOption: listpw=all
+sudoOption: listpw=always
+sudoOption: verifypw
+sudoOption: !verifypw
+sudoOption: verifypw=never
+sudoOption: verifypw=any
+sudoOption: verifypw=all
+sudoOption: verifypw=always
+sudoOption: fdexec
+sudoOption: !fdexec
+sudoOption: fdexec=never
+sudoOption: fdexec=digest_only
+sudoOption: fdexec=always
+
diff --git a/plugins/sudoers/regress/sudoers/test20.ldif2sudo.ok b/plugins/sudoers/regress/sudoers/test20.ldif2sudo.ok
new file mode 100644
index 0000000..e1c743c
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test20.ldif2sudo.ok
@@ -0,0 +1,22 @@
+Defaults lecture
+Defaults !lecture
+Defaults lecture=never
+Defaults lecture=once
+Defaults lecture=always
+Defaults listpw
+Defaults !listpw
+Defaults listpw=never
+Defaults listpw=any
+Defaults listpw=all
+Defaults listpw=always
+Defaults verifypw
+Defaults !verifypw
+Defaults verifypw=never
+Defaults verifypw=any
+Defaults verifypw=all
+Defaults verifypw=always
+Defaults fdexec
+Defaults !fdexec
+Defaults fdexec=never
+Defaults fdexec=digest_only
+Defaults fdexec=always
diff --git a/plugins/sudoers/regress/sudoers/test20.out.ok b/plugins/sudoers/regress/sudoers/test20.out.ok
new file mode 100644
index 0000000..260be81
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test20.out.ok
@@ -0,0 +1,24 @@
+Parses OK
+
+Defaults lecture
+Defaults !lecture
+Defaults lecture=never
+Defaults lecture=once
+Defaults lecture=always
+Defaults listpw
+Defaults !listpw
+Defaults listpw=never
+Defaults listpw=any
+Defaults listpw=all
+Defaults listpw=always
+Defaults verifypw
+Defaults !verifypw
+Defaults verifypw=never
+Defaults verifypw=any
+Defaults verifypw=all
+Defaults verifypw=always
+Defaults fdexec
+Defaults !fdexec
+Defaults fdexec=never
+Defaults fdexec=digest_only
+Defaults fdexec=always
diff --git a/plugins/sudoers/regress/sudoers/test20.toke.ok b/plugins/sudoers/regress/sudoers/test20.toke.ok
new file mode 100644
index 0000000..1847149
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test20.toke.ok
@@ -0,0 +1,26 @@
+#
+DEFAULTS DEFVAR
+DEFAULTS !DEFVAR
+DEFAULTS DEFVAR = WORD(2)
+DEFAULTS DEFVAR = WORD(2)
+DEFAULTS DEFVAR = WORD(2)
+
+DEFAULTS DEFVAR
+DEFAULTS !DEFVAR
+DEFAULTS DEFVAR = WORD(2)
+DEFAULTS DEFVAR = WORD(2)
+DEFAULTS DEFVAR = WORD(2)
+DEFAULTS DEFVAR = WORD(2)
+
+DEFAULTS DEFVAR
+DEFAULTS !DEFVAR
+DEFAULTS DEFVAR = WORD(2)
+DEFAULTS DEFVAR = WORD(2)
+DEFAULTS DEFVAR = WORD(2)
+DEFAULTS DEFVAR = WORD(2)
+
+DEFAULTS DEFVAR
+DEFAULTS !DEFVAR
+DEFAULTS DEFVAR = WORD(2)
+DEFAULTS DEFVAR = WORD(2)
+DEFAULTS DEFVAR = WORD(2)
diff --git a/plugins/sudoers/regress/sudoers/test21.in b/plugins/sudoers/regress/sudoers/test21.in
new file mode 100644
index 0000000..65416cf
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test21.in
@@ -0,0 +1,36 @@
+# Test parsing of syslog settings
+Defaults syslog
+Defaults !syslog
+Defaults syslog=auth
+Defaults syslog=daemon
+Defaults syslog=user
+Defaults syslog=local0
+Defaults syslog=local1
+Defaults syslog=local2
+Defaults syslog=local3
+Defaults syslog=local4
+Defaults syslog=local5
+Defaults syslog=local6
+Defaults syslog=local7
+
+Defaults !syslog_goodpri
+Defaults syslog_goodpri=alert
+Defaults syslog_goodpri=crit
+Defaults syslog_goodpri=debug
+Defaults syslog_goodpri=emerg
+Defaults syslog_goodpri=err
+Defaults syslog_goodpri=info
+Defaults syslog_goodpri=notice
+Defaults syslog_goodpri=warning
+Defaults syslog_goodpri=none
+
+Defaults !syslog_badpri
+Defaults syslog_badpri=alert
+Defaults syslog_badpri=crit
+Defaults syslog_badpri=debug
+Defaults syslog_badpri=emerg
+Defaults syslog_badpri=err
+Defaults syslog_badpri=info
+Defaults syslog_badpri=notice
+Defaults syslog_badpri=warning
+Defaults syslog_badpri=none
diff --git a/plugins/sudoers/regress/sudoers/test21.json.ok b/plugins/sudoers/regress/sudoers/test21.json.ok
new file mode 100644
index 0000000..7896965
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test21.json.ok
@@ -0,0 +1,169 @@
+{
+ "Defaults": [
+ {
+ "Options": [
+ { "syslog": true }
+ ]
+ },
+ {
+ "Options": [
+ { "syslog": false }
+ ]
+ },
+ {
+ "Options": [
+ { "syslog": "auth" }
+ ]
+ },
+ {
+ "Options": [
+ { "syslog": "daemon" }
+ ]
+ },
+ {
+ "Options": [
+ { "syslog": "user" }
+ ]
+ },
+ {
+ "Options": [
+ { "syslog": "local0" }
+ ]
+ },
+ {
+ "Options": [
+ { "syslog": "local1" }
+ ]
+ },
+ {
+ "Options": [
+ { "syslog": "local2" }
+ ]
+ },
+ {
+ "Options": [
+ { "syslog": "local3" }
+ ]
+ },
+ {
+ "Options": [
+ { "syslog": "local4" }
+ ]
+ },
+ {
+ "Options": [
+ { "syslog": "local5" }
+ ]
+ },
+ {
+ "Options": [
+ { "syslog": "local6" }
+ ]
+ },
+ {
+ "Options": [
+ { "syslog": "local7" }
+ ]
+ },
+ {
+ "Options": [
+ { "syslog_goodpri": false }
+ ]
+ },
+ {
+ "Options": [
+ { "syslog_goodpri": "alert" }
+ ]
+ },
+ {
+ "Options": [
+ { "syslog_goodpri": "crit" }
+ ]
+ },
+ {
+ "Options": [
+ { "syslog_goodpri": "debug" }
+ ]
+ },
+ {
+ "Options": [
+ { "syslog_goodpri": "emerg" }
+ ]
+ },
+ {
+ "Options": [
+ { "syslog_goodpri": "err" }
+ ]
+ },
+ {
+ "Options": [
+ { "syslog_goodpri": "info" }
+ ]
+ },
+ {
+ "Options": [
+ { "syslog_goodpri": "notice" }
+ ]
+ },
+ {
+ "Options": [
+ { "syslog_goodpri": "warning" }
+ ]
+ },
+ {
+ "Options": [
+ { "syslog_goodpri": "none" }
+ ]
+ },
+ {
+ "Options": [
+ { "syslog_badpri": false }
+ ]
+ },
+ {
+ "Options": [
+ { "syslog_badpri": "alert" }
+ ]
+ },
+ {
+ "Options": [
+ { "syslog_badpri": "crit" }
+ ]
+ },
+ {
+ "Options": [
+ { "syslog_badpri": "debug" }
+ ]
+ },
+ {
+ "Options": [
+ { "syslog_badpri": "emerg" }
+ ]
+ },
+ {
+ "Options": [
+ { "syslog_badpri": "err" }
+ ]
+ },
+ {
+ "Options": [
+ { "syslog_badpri": "info" }
+ ]
+ },
+ {
+ "Options": [
+ { "syslog_badpri": "notice" }
+ ]
+ },
+ {
+ "Options": [
+ { "syslog_badpri": "warning" }
+ ]
+ },
+ {
+ "Options": [
+ { "syslog_badpri": "none" }
+ ]
+ }
+ ]
+}
diff --git a/plugins/sudoers/regress/sudoers/test21.ldif.ok b/plugins/sudoers/regress/sudoers/test21.ldif.ok
new file mode 100644
index 0000000..b3bede8
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test21.ldif.ok
@@ -0,0 +1,39 @@
+dn: cn=defaults,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: defaults
+description: Default sudoOption's go here
+sudoOption: syslog
+sudoOption: !syslog
+sudoOption: syslog=auth
+sudoOption: syslog=daemon
+sudoOption: syslog=user
+sudoOption: syslog=local0
+sudoOption: syslog=local1
+sudoOption: syslog=local2
+sudoOption: syslog=local3
+sudoOption: syslog=local4
+sudoOption: syslog=local5
+sudoOption: syslog=local6
+sudoOption: syslog=local7
+sudoOption: !syslog_goodpri
+sudoOption: syslog_goodpri=alert
+sudoOption: syslog_goodpri=crit
+sudoOption: syslog_goodpri=debug
+sudoOption: syslog_goodpri=emerg
+sudoOption: syslog_goodpri=err
+sudoOption: syslog_goodpri=info
+sudoOption: syslog_goodpri=notice
+sudoOption: syslog_goodpri=warning
+sudoOption: syslog_goodpri=none
+sudoOption: !syslog_badpri
+sudoOption: syslog_badpri=alert
+sudoOption: syslog_badpri=crit
+sudoOption: syslog_badpri=debug
+sudoOption: syslog_badpri=emerg
+sudoOption: syslog_badpri=err
+sudoOption: syslog_badpri=info
+sudoOption: syslog_badpri=notice
+sudoOption: syslog_badpri=warning
+sudoOption: syslog_badpri=none
+
diff --git a/plugins/sudoers/regress/sudoers/test21.ldif2sudo.ok b/plugins/sudoers/regress/sudoers/test21.ldif2sudo.ok
new file mode 100644
index 0000000..56e09ff
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test21.ldif2sudo.ok
@@ -0,0 +1,33 @@
+Defaults syslog
+Defaults !syslog
+Defaults syslog=auth
+Defaults syslog=daemon
+Defaults syslog=user
+Defaults syslog=local0
+Defaults syslog=local1
+Defaults syslog=local2
+Defaults syslog=local3
+Defaults syslog=local4
+Defaults syslog=local5
+Defaults syslog=local6
+Defaults syslog=local7
+Defaults !syslog_goodpri
+Defaults syslog_goodpri=alert
+Defaults syslog_goodpri=crit
+Defaults syslog_goodpri=debug
+Defaults syslog_goodpri=emerg
+Defaults syslog_goodpri=err
+Defaults syslog_goodpri=info
+Defaults syslog_goodpri=notice
+Defaults syslog_goodpri=warning
+Defaults syslog_goodpri=none
+Defaults !syslog_badpri
+Defaults syslog_badpri=alert
+Defaults syslog_badpri=crit
+Defaults syslog_badpri=debug
+Defaults syslog_badpri=emerg
+Defaults syslog_badpri=err
+Defaults syslog_badpri=info
+Defaults syslog_badpri=notice
+Defaults syslog_badpri=warning
+Defaults syslog_badpri=none
diff --git a/plugins/sudoers/regress/sudoers/test21.out.ok b/plugins/sudoers/regress/sudoers/test21.out.ok
new file mode 100644
index 0000000..136ec64
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test21.out.ok
@@ -0,0 +1,35 @@
+Parses OK
+
+Defaults syslog
+Defaults !syslog
+Defaults syslog=auth
+Defaults syslog=daemon
+Defaults syslog=user
+Defaults syslog=local0
+Defaults syslog=local1
+Defaults syslog=local2
+Defaults syslog=local3
+Defaults syslog=local4
+Defaults syslog=local5
+Defaults syslog=local6
+Defaults syslog=local7
+Defaults !syslog_goodpri
+Defaults syslog_goodpri=alert
+Defaults syslog_goodpri=crit
+Defaults syslog_goodpri=debug
+Defaults syslog_goodpri=emerg
+Defaults syslog_goodpri=err
+Defaults syslog_goodpri=info
+Defaults syslog_goodpri=notice
+Defaults syslog_goodpri=warning
+Defaults syslog_goodpri=none
+Defaults !syslog_badpri
+Defaults syslog_badpri=alert
+Defaults syslog_badpri=crit
+Defaults syslog_badpri=debug
+Defaults syslog_badpri=emerg
+Defaults syslog_badpri=err
+Defaults syslog_badpri=info
+Defaults syslog_badpri=notice
+Defaults syslog_badpri=warning
+Defaults syslog_badpri=none
diff --git a/plugins/sudoers/regress/sudoers/test21.toke.ok b/plugins/sudoers/regress/sudoers/test21.toke.ok
new file mode 100644
index 0000000..871584b
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test21.toke.ok
@@ -0,0 +1,36 @@
+#
+DEFAULTS DEFVAR
+DEFAULTS !DEFVAR
+DEFAULTS DEFVAR = WORD(2)
+DEFAULTS DEFVAR = WORD(2)
+DEFAULTS DEFVAR = WORD(2)
+DEFAULTS DEFVAR = WORD(2)
+DEFAULTS DEFVAR = WORD(2)
+DEFAULTS DEFVAR = WORD(2)
+DEFAULTS DEFVAR = WORD(2)
+DEFAULTS DEFVAR = WORD(2)
+DEFAULTS DEFVAR = WORD(2)
+DEFAULTS DEFVAR = WORD(2)
+DEFAULTS DEFVAR = WORD(2)
+
+DEFAULTS !DEFVAR
+DEFAULTS DEFVAR = WORD(2)
+DEFAULTS DEFVAR = WORD(2)
+DEFAULTS DEFVAR = WORD(2)
+DEFAULTS DEFVAR = WORD(2)
+DEFAULTS DEFVAR = WORD(2)
+DEFAULTS DEFVAR = WORD(2)
+DEFAULTS DEFVAR = WORD(2)
+DEFAULTS DEFVAR = WORD(2)
+DEFAULTS DEFVAR = WORD(2)
+
+DEFAULTS !DEFVAR
+DEFAULTS DEFVAR = WORD(2)
+DEFAULTS DEFVAR = WORD(2)
+DEFAULTS DEFVAR = WORD(2)
+DEFAULTS DEFVAR = WORD(2)
+DEFAULTS DEFVAR = WORD(2)
+DEFAULTS DEFVAR = WORD(2)
+DEFAULTS DEFVAR = WORD(2)
+DEFAULTS DEFVAR = WORD(2)
+DEFAULTS DEFVAR = WORD(2)
diff --git a/plugins/sudoers/regress/sudoers/test22.in b/plugins/sudoers/regress/sudoers/test22.in
new file mode 100644
index 0000000..ecf2fd9
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test22.in
@@ -0,0 +1,6 @@
+# Test parsing of empty Runas_List
+
+user1 ALL = ( : ) ALL
+user2 ALL = (:) ALL
+user3 ALL = ( ) ALL
+user4 ALL = () ALL
diff --git a/plugins/sudoers/regress/sudoers/test22.json.ok b/plugins/sudoers/regress/sudoers/test22.json.ok
new file mode 100644
index 0000000..22141a1
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test22.json.ok
@@ -0,0 +1,88 @@
+{
+ "User_Specs": [
+ {
+ "User_List": [
+ { "username": "user1" }
+ ],
+ "Host_List": [
+ { "hostname": "ALL" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "runasusers": [
+ { "username": "" }
+ ],
+ "Options": [
+ { "setenv": true }
+ ],
+ "Commands": [
+ { "command": "ALL" }
+ ]
+ }
+ ]
+ },
+ {
+ "User_List": [
+ { "username": "user2" }
+ ],
+ "Host_List": [
+ { "hostname": "ALL" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "runasusers": [
+ { "username": "" }
+ ],
+ "Options": [
+ { "setenv": true }
+ ],
+ "Commands": [
+ { "command": "ALL" }
+ ]
+ }
+ ]
+ },
+ {
+ "User_List": [
+ { "username": "user3" }
+ ],
+ "Host_List": [
+ { "hostname": "ALL" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "runasusers": [
+ { "username": "" }
+ ],
+ "Options": [
+ { "setenv": true }
+ ],
+ "Commands": [
+ { "command": "ALL" }
+ ]
+ }
+ ]
+ },
+ {
+ "User_List": [
+ { "username": "user4" }
+ ],
+ "Host_List": [
+ { "hostname": "ALL" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "runasusers": [
+ { "username": "" }
+ ],
+ "Options": [
+ { "setenv": true }
+ ],
+ "Commands": [
+ { "command": "ALL" }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/plugins/sudoers/regress/sudoers/test22.ldif.ok b/plugins/sudoers/regress/sudoers/test22.ldif.ok
new file mode 100644
index 0000000..14c3df4
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test22.ldif.ok
@@ -0,0 +1,40 @@
+dn: cn=user1,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: user1
+sudoUser: user1
+sudoHost: ALL
+sudoRunAsUser:
+sudoCommand: ALL
+sudoOrder: 1
+
+dn: cn=user2,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: user2
+sudoUser: user2
+sudoHost: ALL
+sudoRunAsUser:
+sudoCommand: ALL
+sudoOrder: 2
+
+dn: cn=user3,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: user3
+sudoUser: user3
+sudoHost: ALL
+sudoRunAsUser:
+sudoCommand: ALL
+sudoOrder: 3
+
+dn: cn=user4,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: user4
+sudoUser: user4
+sudoHost: ALL
+sudoRunAsUser:
+sudoCommand: ALL
+sudoOrder: 4
+
diff --git a/plugins/sudoers/regress/sudoers/test22.ldif2sudo.ok b/plugins/sudoers/regress/sudoers/test22.ldif2sudo.ok
new file mode 100644
index 0000000..e0c98e0
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test22.ldif2sudo.ok
@@ -0,0 +1,11 @@
+# sudoRole user1
+user1 ALL = () ALL
+
+# sudoRole user2
+user2 ALL = () ALL
+
+# sudoRole user3
+user3 ALL = () ALL
+
+# sudoRole user4
+user4 ALL = () ALL
diff --git a/plugins/sudoers/regress/sudoers/test22.out.ok b/plugins/sudoers/regress/sudoers/test22.out.ok
new file mode 100644
index 0000000..7117e18
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test22.out.ok
@@ -0,0 +1,6 @@
+Parses OK
+
+user1 ALL = (root) ALL
+user2 ALL = (root) ALL
+user3 ALL = (root) ALL
+user4 ALL = (root) ALL
diff --git a/plugins/sudoers/regress/sudoers/test22.sudo.ok b/plugins/sudoers/regress/sudoers/test22.sudo.ok
new file mode 100644
index 0000000..879e1bd
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test22.sudo.ok
@@ -0,0 +1,7 @@
+user1 ALL = () ALL
+
+user2 ALL = () ALL
+
+user3 ALL = () ALL
+
+user4 ALL = () ALL
diff --git a/plugins/sudoers/regress/sudoers/test22.toke.ok b/plugins/sudoers/regress/sudoers/test22.toke.ok
new file mode 100644
index 0000000..9eeb964
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test22.toke.ok
@@ -0,0 +1,6 @@
+#
+
+WORD(6) ALL = ( : ) ALL
+WORD(6) ALL = ( : ) ALL
+WORD(6) ALL = ( ) ALL
+WORD(6) ALL = ( ) ALL
diff --git a/plugins/sudoers/regress/sudoers/test23.in b/plugins/sudoers/regress/sudoers/test23.in
new file mode 100644
index 0000000..5e03d9e
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test23.in
@@ -0,0 +1,11 @@
+# Test parsing of env_check, env_delete and env_keep
+Defaults env_check = "COLORTERM LANG LANGUAGE LC_* LINGUAS"
+Defaults env_check += "TERM TZ"
+
+Defaults env_delete = "IFS CDPATH LOCALDOMAIN RES_OPTIONS HOSTALIASES"
+Defaults env_delete += "NLSPATH PATH_LOCALE LD_* _RLD*"
+
+Defaults env_keep += "LANG LANGUAGE LINGUAS LC_* _XKB_CHARSET"
+Defaults env_keep -= _XKB_CHARSET
+Defaults env_keep += "XAPPLRESDIR XFILESEARCHPATH XUSERFILESEARCHPATH"
+Defaults env_keep += XDG_SESSION_COOKIE
diff --git a/plugins/sudoers/regress/sudoers/test23.json.ok b/plugins/sudoers/regress/sudoers/test23.json.ok
new file mode 100644
index 0000000..5e8b0da
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test23.json.ok
@@ -0,0 +1,102 @@
+{
+ "Defaults": [
+ {
+ "Options": [
+ {
+ "operation": "list_assign",
+ "env_check": [
+ "COLORTERM",
+ "LANG",
+ "LANGUAGE",
+ "LC_*",
+ "LINGUAS"
+ ]
+ }
+ ]
+ },
+ {
+ "Options": [
+ {
+ "operation": "list_add",
+ "env_check": [
+ "TERM",
+ "TZ"
+ ]
+ }
+ ]
+ },
+ {
+ "Options": [
+ {
+ "operation": "list_assign",
+ "env_delete": [
+ "IFS",
+ "CDPATH",
+ "LOCALDOMAIN",
+ "RES_OPTIONS",
+ "HOSTALIASES"
+ ]
+ }
+ ]
+ },
+ {
+ "Options": [
+ {
+ "operation": "list_add",
+ "env_delete": [
+ "NLSPATH",
+ "PATH_LOCALE",
+ "LD_*",
+ "_RLD*"
+ ]
+ }
+ ]
+ },
+ {
+ "Options": [
+ {
+ "operation": "list_add",
+ "env_keep": [
+ "LANG",
+ "LANGUAGE",
+ "LINGUAS",
+ "LC_*",
+ "_XKB_CHARSET"
+ ]
+ }
+ ]
+ },
+ {
+ "Options": [
+ {
+ "operation": "list_remove",
+ "env_keep": [
+ "_XKB_CHARSET"
+ ]
+ }
+ ]
+ },
+ {
+ "Options": [
+ {
+ "operation": "list_add",
+ "env_keep": [
+ "XAPPLRESDIR",
+ "XFILESEARCHPATH",
+ "XUSERFILESEARCHPATH"
+ ]
+ }
+ ]
+ },
+ {
+ "Options": [
+ {
+ "operation": "list_add",
+ "env_keep": [
+ "XDG_SESSION_COOKIE"
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/plugins/sudoers/regress/sudoers/test23.ldif.ok b/plugins/sudoers/regress/sudoers/test23.ldif.ok
new file mode 100644
index 0000000..97c1fbd
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test23.ldif.ok
@@ -0,0 +1,14 @@
+dn: cn=defaults,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: defaults
+description: Default sudoOption's go here
+sudoOption: env_check=COLORTERM LANG LANGUAGE LC_* LINGUAS
+sudoOption: env_check+=TERM TZ
+sudoOption: env_delete=IFS CDPATH LOCALDOMAIN RES_OPTIONS HOSTALIASES
+sudoOption: env_delete+=NLSPATH PATH_LOCALE LD_* _RLD*
+sudoOption: env_keep+=LANG LANGUAGE LINGUAS LC_* _XKB_CHARSET
+sudoOption: env_keep-=_XKB_CHARSET
+sudoOption: env_keep+=XAPPLRESDIR XFILESEARCHPATH XUSERFILESEARCHPATH
+sudoOption: env_keep+=XDG_SESSION_COOKIE
+
diff --git a/plugins/sudoers/regress/sudoers/test23.ldif2sudo.ok b/plugins/sudoers/regress/sudoers/test23.ldif2sudo.ok
new file mode 100644
index 0000000..bb9f2e9
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test23.ldif2sudo.ok
@@ -0,0 +1,8 @@
+Defaults env_check="COLORTERM LANG LANGUAGE LC_* LINGUAS"
+Defaults env_check+="TERM TZ"
+Defaults env_delete="IFS CDPATH LOCALDOMAIN RES_OPTIONS HOSTALIASES"
+Defaults env_delete+="NLSPATH PATH_LOCALE LD_* _RLD*"
+Defaults env_keep+="LANG LANGUAGE LINGUAS LC_* _XKB_CHARSET"
+Defaults env_keep-=_XKB_CHARSET
+Defaults env_keep+="XAPPLRESDIR XFILESEARCHPATH XUSERFILESEARCHPATH"
+Defaults env_keep+=XDG_SESSION_COOKIE
diff --git a/plugins/sudoers/regress/sudoers/test23.out.ok b/plugins/sudoers/regress/sudoers/test23.out.ok
new file mode 100644
index 0000000..fe6e415
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test23.out.ok
@@ -0,0 +1,10 @@
+Parses OK
+
+Defaults env_check="COLORTERM LANG LANGUAGE LC_* LINGUAS"
+Defaults env_check+="TERM TZ"
+Defaults env_delete="IFS CDPATH LOCALDOMAIN RES_OPTIONS HOSTALIASES"
+Defaults env_delete+="NLSPATH PATH_LOCALE LD_* _RLD*"
+Defaults env_keep+="LANG LANGUAGE LINGUAS LC_* _XKB_CHARSET"
+Defaults env_keep-=_XKB_CHARSET
+Defaults env_keep+="XAPPLRESDIR XFILESEARCHPATH XUSERFILESEARCHPATH"
+Defaults env_keep+=XDG_SESSION_COOKIE
diff --git a/plugins/sudoers/regress/sudoers/test23.sudo.ok b/plugins/sudoers/regress/sudoers/test23.sudo.ok
new file mode 100644
index 0000000..bb9f2e9
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test23.sudo.ok
@@ -0,0 +1,8 @@
+Defaults env_check="COLORTERM LANG LANGUAGE LC_* LINGUAS"
+Defaults env_check+="TERM TZ"
+Defaults env_delete="IFS CDPATH LOCALDOMAIN RES_OPTIONS HOSTALIASES"
+Defaults env_delete+="NLSPATH PATH_LOCALE LD_* _RLD*"
+Defaults env_keep+="LANG LANGUAGE LINGUAS LC_* _XKB_CHARSET"
+Defaults env_keep-=_XKB_CHARSET
+Defaults env_keep+="XAPPLRESDIR XFILESEARCHPATH XUSERFILESEARCHPATH"
+Defaults env_keep+=XDG_SESSION_COOKIE
diff --git a/plugins/sudoers/regress/sudoers/test23.toke.ok b/plugins/sudoers/regress/sudoers/test23.toke.ok
new file mode 100644
index 0000000..3d7cd84
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test23.toke.ok
@@ -0,0 +1,11 @@
+#
+DEFAULTS DEFVAR = BEGINSTR STRBODY ENDSTR WORD(4)
+DEFAULTS DEFVAR += BEGINSTR STRBODY ENDSTR WORD(4)
+
+DEFAULTS DEFVAR = BEGINSTR STRBODY ENDSTR WORD(4)
+DEFAULTS DEFVAR += BEGINSTR STRBODY ENDSTR WORD(4)
+
+DEFAULTS DEFVAR += BEGINSTR STRBODY ENDSTR WORD(4)
+DEFAULTS DEFVAR -= WORD(2)
+DEFAULTS DEFVAR += BEGINSTR STRBODY ENDSTR WORD(4)
+DEFAULTS DEFVAR += WORD(2)
diff --git a/plugins/sudoers/regress/sudoers/test24.in b/plugins/sudoers/regress/sudoers/test24.in
new file mode 100644
index 0000000..3fc3bbc
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test24.in
@@ -0,0 +1,6 @@
+# Test parsing of CHROOT and CWD syntax
+Defaults runcwd=~
+Defaults runchroot=/
+#
+user0 ALL = CHROOT=/var/www CWD=/htdocs /bin/ksh
+user1 ALL = CWD=~root /usr/bin/id, CWD=/tmp /bin/ls
diff --git a/plugins/sudoers/regress/sudoers/test24.json.ok b/plugins/sudoers/regress/sudoers/test24.json.ok
new file mode 100644
index 0000000..964359c
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test24.json.ok
@@ -0,0 +1,61 @@
+{
+ "Defaults": [
+ {
+ "Options": [
+ { "runcwd": "~" }
+ ]
+ },
+ {
+ "Options": [
+ { "runchroot": "/" }
+ ]
+ }
+ ],
+ "User_Specs": [
+ {
+ "User_List": [
+ { "username": "user0" }
+ ],
+ "Host_List": [
+ { "hostname": "ALL" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "Options": [
+ "runchroot": "/var/www",
+ "runcwd": "/htdocs"
+ ],
+ "Commands": [
+ { "command": "/bin/ksh" }
+ ]
+ }
+ ]
+ },
+ {
+ "User_List": [
+ { "username": "user1" }
+ ],
+ "Host_List": [
+ { "hostname": "ALL" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "Options": [
+ "runcwd": "~root"
+ ],
+ "Commands": [
+ { "command": "/usr/bin/id" }
+ ]
+ },
+ {
+ "Options": [
+ "runcwd": "/tmp"
+ ],
+ "Commands": [
+ { "command": "/bin/ls" }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/plugins/sudoers/regress/sudoers/test24.ldif.ok b/plugins/sudoers/regress/sudoers/test24.ldif.ok
new file mode 100644
index 0000000..aeb5f7a
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test24.ldif.ok
@@ -0,0 +1,39 @@
+dn: cn=defaults,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: defaults
+description: Default sudoOption's go here
+sudoOption: runcwd=~
+sudoOption: runchroot=/
+
+dn: cn=user0,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: user0
+sudoUser: user0
+sudoHost: ALL
+sudoOption: runchroot=/var/www
+sudoOption: runcwd=/htdocs
+sudoCommand: /bin/ksh
+sudoOrder: 1
+
+dn: cn=user1,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: user1
+sudoUser: user1
+sudoHost: ALL
+sudoOption: runcwd=~root
+sudoCommand: /usr/bin/id
+sudoOrder: 2
+
+dn: cn=user1_1,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: user1_1
+sudoUser: user1
+sudoHost: ALL
+sudoOption: runcwd=/tmp
+sudoCommand: /bin/ls
+sudoOrder: 3
+
diff --git a/plugins/sudoers/regress/sudoers/test24.ldif2sudo.ok b/plugins/sudoers/regress/sudoers/test24.ldif2sudo.ok
new file mode 100644
index 0000000..4c09657
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test24.ldif2sudo.ok
@@ -0,0 +1,8 @@
+Defaults runcwd=~
+Defaults runchroot=/
+
+# sudoRole user0
+user0 ALL = CHROOT=/var/www CWD=/htdocs /bin/ksh
+
+# sudoRole user1, user1_1
+user1 ALL = CWD=~root /usr/bin/id, CWD=/tmp /bin/ls
diff --git a/plugins/sudoers/regress/sudoers/test24.out.ok b/plugins/sudoers/regress/sudoers/test24.out.ok
new file mode 100644
index 0000000..530b700
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test24.out.ok
@@ -0,0 +1,7 @@
+Parses OK
+
+Defaults runcwd=~
+Defaults runchroot=/
+
+user0 ALL = CHROOT=/var/www CWD=/htdocs /bin/ksh
+user1 ALL = CWD=~root /usr/bin/id, CWD=/tmp /bin/ls
diff --git a/plugins/sudoers/regress/sudoers/test24.sudo.ok b/plugins/sudoers/regress/sudoers/test24.sudo.ok
new file mode 100644
index 0000000..8203b0c
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test24.sudo.ok
@@ -0,0 +1,6 @@
+Defaults runcwd=~
+Defaults runchroot=/
+
+user0 ALL = CHROOT=/var/www CWD=/htdocs /bin/ksh
+
+user1 ALL = CWD=~root /usr/bin/id, CWD=/tmp /bin/ls
diff --git a/plugins/sudoers/regress/sudoers/test24.toke.ok b/plugins/sudoers/regress/sudoers/test24.toke.ok
new file mode 100644
index 0000000..47842f4
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test24.toke.ok
@@ -0,0 +1,6 @@
+#
+DEFAULTS DEFVAR = WORD(2)
+DEFAULTS DEFVAR = WORD(2)
+#
+WORD(6) ALL = CHROOT = WORD(5) CWD = WORD(5) COMMAND
+WORD(6) ALL = CWD = WORD(5) COMMAND , CWD = WORD(5) COMMAND
diff --git a/plugins/sudoers/regress/sudoers/test3.in b/plugins/sudoers/regress/sudoers/test3.in
new file mode 100644
index 0000000..82fcd83
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test3.in
@@ -0,0 +1,6 @@
+# Test whitespace in User_List as part of a per-user Defaults entry
+User_Alias FOO = foo, bar
+Defaults:FOO env_reset
+Defaults:foo,bar env_reset
+Defaults:foo,\ bar env_reset
+Defaults:foo, bar env_reset
diff --git a/plugins/sudoers/regress/sudoers/test3.json.ok b/plugins/sudoers/regress/sudoers/test3.json.ok
new file mode 100644
index 0000000..fc69eb1
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test3.json.ok
@@ -0,0 +1,45 @@
+{
+ "Defaults": [
+ {
+ "Binding": [
+ { "useralias": "FOO" }
+ ],
+ "Options": [
+ { "env_reset": true }
+ ]
+ },
+ {
+ "Binding": [
+ { "username": "foo" },
+ { "username": "bar" }
+ ],
+ "Options": [
+ { "env_reset": true }
+ ]
+ },
+ {
+ "Binding": [
+ { "username": "foo" },
+ { "username": " bar" }
+ ],
+ "Options": [
+ { "env_reset": true }
+ ]
+ },
+ {
+ "Binding": [
+ { "username": "foo" },
+ { "username": "bar" }
+ ],
+ "Options": [
+ { "env_reset": true }
+ ]
+ }
+ ],
+ "User_Aliases": {
+ "FOO": [
+ { "username": "foo" },
+ { "username": "bar" }
+ ]
+ }
+}
diff --git a/plugins/sudoers/regress/sudoers/test3.ldif.ok b/plugins/sudoers/regress/sudoers/test3.ldif.ok
new file mode 100644
index 0000000..783cde5
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test3.ldif.ok
@@ -0,0 +1,12 @@
+# Unable to translate stdin:3:23:
+# Defaults:foo, bar env_reset
+
+# Unable to translate stdin:4:27:
+# Defaults:foo, bar env_reset
+
+# Unable to translate stdin:5:29:
+# Defaults:foo, " bar" env_reset
+
+# Unable to translate stdin:6:28:
+# Defaults:foo, bar env_reset
+
diff --git a/plugins/sudoers/regress/sudoers/test3.ldif2sudo.ok b/plugins/sudoers/regress/sudoers/test3.ldif2sudo.ok
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test3.ldif2sudo.ok
diff --git a/plugins/sudoers/regress/sudoers/test3.out.ok b/plugins/sudoers/regress/sudoers/test3.out.ok
new file mode 100644
index 0000000..7f620c4
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test3.out.ok
@@ -0,0 +1,8 @@
+Parses OK
+
+Defaults:FOO env_reset
+Defaults:foo, bar env_reset
+Defaults:foo, " bar" env_reset
+Defaults:foo, bar env_reset
+
+User_Alias FOO = foo, bar
diff --git a/plugins/sudoers/regress/sudoers/test3.toke.ok b/plugins/sudoers/regress/sudoers/test3.toke.ok
new file mode 100644
index 0000000..028f333
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test3.toke.ok
@@ -0,0 +1,6 @@
+#
+USERALIAS ALIAS = WORD(6) , WORD(6)
+DEFAULTS_USER ALIAS DEFVAR
+DEFAULTS_USER WORD(6) , WORD(6) DEFVAR
+DEFAULTS_USER WORD(6) , WORD(6) DEFVAR
+DEFAULTS_USER WORD(6) , WORD(6) DEFVAR
diff --git a/plugins/sudoers/regress/sudoers/test4.in b/plugins/sudoers/regress/sudoers/test4.in
new file mode 100644
index 0000000..b8df454
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test4.in
@@ -0,0 +1,7 @@
+# Test line continuation with anchored matches
+User_Alias FOO = foo \
+: BAR = bar
+
+# This used to pass for sudo < 1.8.1 (though it should not have)
+User_Alias FOO = foo \
+User_Alias BAR = bar
diff --git a/plugins/sudoers/regress/sudoers/test4.json.ok b/plugins/sudoers/regress/sudoers/test4.json.ok
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test4.json.ok
diff --git a/plugins/sudoers/regress/sudoers/test4.ldif.ok b/plugins/sudoers/regress/sudoers/test4.ldif.ok
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test4.ldif.ok
diff --git a/plugins/sudoers/regress/sudoers/test4.out.ok b/plugins/sudoers/regress/sudoers/test4.out.ok
new file mode 100644
index 0000000..de27db3
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test4.out.ok
@@ -0,0 +1,3 @@
+
+User_Alias BAR = bar
+User_Alias FOO = foo
diff --git a/plugins/sudoers/regress/sudoers/test4.toke.ok b/plugins/sudoers/regress/sudoers/test4.toke.ok
new file mode 100644
index 0000000..307945f
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test4.toke.ok
@@ -0,0 +1,5 @@
+#
+USERALIAS ALIAS = WORD(6) : ALIAS = WORD(6)
+
+#
+USERALIAS ALIAS = WORD(6) <*> ERROR ALIAS = WORD(6)
diff --git a/plugins/sudoers/regress/sudoers/test5.in b/plugins/sudoers/regress/sudoers/test5.in
new file mode 100644
index 0000000..354f589
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test5.in
@@ -0,0 +1,3 @@
+# Test empty string in User_Alias and Command_Spec
+User_Alias FOO = ""
+"" ALL = ALL
diff --git a/plugins/sudoers/regress/sudoers/test5.json.ok b/plugins/sudoers/regress/sudoers/test5.json.ok
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test5.json.ok
diff --git a/plugins/sudoers/regress/sudoers/test5.ldif.ok b/plugins/sudoers/regress/sudoers/test5.ldif.ok
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test5.ldif.ok
diff --git a/plugins/sudoers/regress/sudoers/test5.out.ok b/plugins/sudoers/regress/sudoers/test5.out.ok
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test5.out.ok
@@ -0,0 +1 @@
+
diff --git a/plugins/sudoers/regress/sudoers/test5.toke.ok b/plugins/sudoers/regress/sudoers/test5.toke.ok
new file mode 100644
index 0000000..fb8152f
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test5.toke.ok
@@ -0,0 +1,3 @@
+#
+USERALIAS ALIAS = BEGINSTR ENDSTR <*> ERROR
+BEGINSTR ENDSTR <*> ERROR ALL = ALL
diff --git a/plugins/sudoers/regress/sudoers/test6.in b/plugins/sudoers/regress/sudoers/test6.in
new file mode 100644
index 0000000..e804571
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test6.in
@@ -0,0 +1,15 @@
+# Check that uids work in per-user and per-runas Defaults
+Defaults:#123 set_home
+Defaults>#123 set_home
+Defaults:"#123" set_home
+Defaults>"#123" set_home
+
+# Check that uids work in a Command_Spec
+#0 ALL = ALL
+#0 ALL = (#0 : #0) ALL
+"#0" ALL = ALL
+"#0" ALL = ("#0" : "#0") ALL
+
+# Check that gids work in a Command_Spec
+%#0 ALL = ALL
+"%#0" ALL = ALL
diff --git a/plugins/sudoers/regress/sudoers/test6.json.ok b/plugins/sudoers/regress/sudoers/test6.json.ok
new file mode 100644
index 0000000..be1f80f
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test6.json.ok
@@ -0,0 +1,158 @@
+{
+ "Defaults": [
+ {
+ "Binding": [
+ { "userid": 123 }
+ ],
+ "Options": [
+ { "set_home": true }
+ ]
+ },
+ {
+ "Binding": [
+ { "userid": 123 }
+ ],
+ "Options": [
+ { "set_home": true }
+ ]
+ },
+ {
+ "Binding": [
+ { "userid": 123 }
+ ],
+ "Options": [
+ { "set_home": true }
+ ]
+ },
+ {
+ "Binding": [
+ { "userid": 123 }
+ ],
+ "Options": [
+ { "set_home": true }
+ ]
+ }
+ ],
+ "User_Specs": [
+ {
+ "User_List": [
+ { "userid": 0 }
+ ],
+ "Host_List": [
+ { "hostname": "ALL" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "Options": [
+ { "setenv": true }
+ ],
+ "Commands": [
+ { "command": "ALL" }
+ ]
+ }
+ ]
+ },
+ {
+ "User_List": [
+ { "userid": 0 }
+ ],
+ "Host_List": [
+ { "hostname": "ALL" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "runasusers": [
+ { "userid": 0 }
+ ],
+ "runasgroups": [
+ { "usergroup": "#0" }
+ ],
+ "Options": [
+ { "setenv": true }
+ ],
+ "Commands": [
+ { "command": "ALL" }
+ ]
+ }
+ ]
+ },
+ {
+ "User_List": [
+ { "userid": 0 }
+ ],
+ "Host_List": [
+ { "hostname": "ALL" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "Options": [
+ { "setenv": true }
+ ],
+ "Commands": [
+ { "command": "ALL" }
+ ]
+ }
+ ]
+ },
+ {
+ "User_List": [
+ { "userid": 0 }
+ ],
+ "Host_List": [
+ { "hostname": "ALL" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "runasusers": [
+ { "userid": 0 }
+ ],
+ "runasgroups": [
+ { "usergroup": "#0" }
+ ],
+ "Options": [
+ { "setenv": true }
+ ],
+ "Commands": [
+ { "command": "ALL" }
+ ]
+ }
+ ]
+ },
+ {
+ "User_List": [
+ { "usergid": 0 }
+ ],
+ "Host_List": [
+ { "hostname": "ALL" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "Options": [
+ { "setenv": true }
+ ],
+ "Commands": [
+ { "command": "ALL" }
+ ]
+ }
+ ]
+ },
+ {
+ "User_List": [
+ { "usergid": 0 }
+ ],
+ "Host_List": [
+ { "hostname": "ALL" }
+ ],
+ "Cmnd_Specs": [
+ {
+ "Options": [
+ { "setenv": true }
+ ],
+ "Commands": [
+ { "command": "ALL" }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/plugins/sudoers/regress/sudoers/test6.ldif.ok b/plugins/sudoers/regress/sudoers/test6.ldif.ok
new file mode 100644
index 0000000..046b334
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test6.ldif.ok
@@ -0,0 +1,70 @@
+# Unable to translate stdin:2:23:
+# Defaults:#123 set_home
+
+# Unable to translate stdin:3:23:
+# Defaults>#123 set_home
+
+# Unable to translate stdin:4:25:
+# Defaults:#123 set_home
+
+# Unable to translate stdin:5:25:
+# Defaults>#123 set_home
+
+dn: cn=\#0,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: \#0
+sudoUser: #0
+sudoHost: ALL
+sudoCommand: ALL
+sudoOrder: 1
+
+dn: cn=\#0_1,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: \#0_1
+sudoUser: #0
+sudoHost: ALL
+sudoRunAsUser: #0
+sudoRunAsGroup: #0
+sudoCommand: ALL
+sudoOrder: 2
+
+dn: cn=\#0_2,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: \#0_2
+sudoUser: #0
+sudoHost: ALL
+sudoCommand: ALL
+sudoOrder: 3
+
+dn: cn=\#0_3,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: \#0_3
+sudoUser: #0
+sudoHost: ALL
+sudoRunAsUser: #0
+sudoRunAsGroup: #0
+sudoCommand: ALL
+sudoOrder: 4
+
+dn: cn=%\#0,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: %\#0
+sudoUser: %#0
+sudoHost: ALL
+sudoCommand: ALL
+sudoOrder: 5
+
+dn: cn=%\#0_1,ou=SUDOers,dc=sudo,dc=ws
+objectClass: top
+objectClass: sudoRole
+cn: %\#0_1
+sudoUser: %#0
+sudoHost: ALL
+sudoCommand: ALL
+sudoOrder: 6
+
diff --git a/plugins/sudoers/regress/sudoers/test6.ldif2sudo.ok b/plugins/sudoers/regress/sudoers/test6.ldif2sudo.ok
new file mode 100644
index 0000000..bfe40bb
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test6.ldif2sudo.ok
@@ -0,0 +1,5 @@
+# sudoRole #0, #0_1, #0_2, #0_3
+#0 ALL = ALL, (#0 : #0) ALL, ALL, (#0 : #0) ALL
+
+# sudoRole %#0, %#0_1
+%#0 ALL = ALL, ALL
diff --git a/plugins/sudoers/regress/sudoers/test6.out.ok b/plugins/sudoers/regress/sudoers/test6.out.ok
new file mode 100644
index 0000000..73b8fe0
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test6.out.ok
@@ -0,0 +1,13 @@
+Parses OK
+
+Defaults:#123 set_home
+Defaults>#123 set_home
+Defaults:#123 set_home
+Defaults>#123 set_home
+
+#0 ALL = ALL
+#0 ALL = (#0 : #0) ALL
+#0 ALL = ALL
+#0 ALL = (#0 : #0) ALL
+%#0 ALL = ALL
+%#0 ALL = ALL
diff --git a/plugins/sudoers/regress/sudoers/test6.toke.ok b/plugins/sudoers/regress/sudoers/test6.toke.ok
new file mode 100644
index 0000000..db8e1c5
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test6.toke.ok
@@ -0,0 +1,15 @@
+#
+DEFAULTS_USER WORD(6) DEFVAR
+DEFAULTS_RUNAS WORD(6) DEFVAR
+DEFAULTS_USER BEGINSTR STRBODY ENDSTR WORD(4) DEFVAR
+DEFAULTS_RUNAS BEGINSTR STRBODY ENDSTR WORD(4) DEFVAR
+
+#
+WORD(6) ALL = ALL
+WORD(6) ALL = ( WORD(6) : WORD(6) ) ALL
+BEGINSTR STRBODY ENDSTR WORD(4) ALL = ALL
+BEGINSTR STRBODY ENDSTR WORD(4) ALL = ( BEGINSTR STRBODY ENDSTR WORD(4) : BEGINSTR STRBODY ENDSTR WORD(4) ) ALL
+
+#
+USERGROUP ALL = ALL
+BEGINSTR STRBODY ENDSTR USERGROUP ALL = ALL
diff --git a/plugins/sudoers/regress/sudoers/test7.in b/plugins/sudoers/regress/sudoers/test7.in
new file mode 100644
index 0000000..7b241d0
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test7.in
@@ -0,0 +1,7 @@
+# These should all be syntax errors
+User_Alias FOO1 = "%"
+User_Alias FOO2 = "%:"
+User_Alias FOO3 = "+"
+User_Alias FOO4 = %
+User_Alias FOO5 = %:
+User_Alias FOO6 = +
diff --git a/plugins/sudoers/regress/sudoers/test7.json.ok b/plugins/sudoers/regress/sudoers/test7.json.ok
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test7.json.ok
diff --git a/plugins/sudoers/regress/sudoers/test7.ldif.ok b/plugins/sudoers/regress/sudoers/test7.ldif.ok
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test7.ldif.ok
diff --git a/plugins/sudoers/regress/sudoers/test7.out.ok b/plugins/sudoers/regress/sudoers/test7.out.ok
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test7.out.ok
@@ -0,0 +1 @@
+
diff --git a/plugins/sudoers/regress/sudoers/test7.toke.ok b/plugins/sudoers/regress/sudoers/test7.toke.ok
new file mode 100644
index 0000000..b0da147
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test7.toke.ok
@@ -0,0 +1,7 @@
+#
+USERALIAS ALIAS = BEGINSTR STRBODY ENDSTR <*> ERROR
+USERALIAS ALIAS = BEGINSTR STRBODY ENDSTR <*> ERROR
+USERALIAS ALIAS = BEGINSTR STRBODY ENDSTR <*> ERROR
+USERALIAS ALIAS = <*> ERROR
+USERALIAS ALIAS = <*> ERROR
+USERALIAS ALIAS = <*> ERROR
diff --git a/plugins/sudoers/regress/sudoers/test8.in b/plugins/sudoers/regress/sudoers/test8.in
new file mode 100644
index 0000000..d25e834
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test8.in
@@ -0,0 +1,8 @@
+# Test quoted strings
+User_Alias UA1 = "xy"
+User_Alias UA2 = "x\
+y"
+User_Alias UA3 = x\"y
+
+# A newline in the middle of a string is an error
+User_Alias UA4 = "x
diff --git a/plugins/sudoers/regress/sudoers/test8.json.ok b/plugins/sudoers/regress/sudoers/test8.json.ok
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test8.json.ok
diff --git a/plugins/sudoers/regress/sudoers/test8.ldif.ok b/plugins/sudoers/regress/sudoers/test8.ldif.ok
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test8.ldif.ok
diff --git a/plugins/sudoers/regress/sudoers/test8.out.ok b/plugins/sudoers/regress/sudoers/test8.out.ok
new file mode 100644
index 0000000..e62f97f
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test8.out.ok
@@ -0,0 +1,4 @@
+
+User_Alias UA1 = xy
+User_Alias UA2 = xy
+User_Alias UA3 = x\"y
diff --git a/plugins/sudoers/regress/sudoers/test8.toke.ok b/plugins/sudoers/regress/sudoers/test8.toke.ok
new file mode 100644
index 0000000..f775645
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test8.toke.ok
@@ -0,0 +1,7 @@
+#
+USERALIAS ALIAS = BEGINSTR STRBODY ENDSTR WORD(4)
+USERALIAS ALIAS = BEGINSTR STRBODY STRBODY ENDSTR WORD(4)
+USERALIAS ALIAS = WORD(6)
+
+#
+USERALIAS ALIAS = BEGINSTR STRBODY <*> ERROR
diff --git a/plugins/sudoers/regress/sudoers/test9.in b/plugins/sudoers/regress/sudoers/test9.in
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test9.in
diff --git a/plugins/sudoers/regress/sudoers/test9.json.ok b/plugins/sudoers/regress/sudoers/test9.json.ok
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test9.json.ok
diff --git a/plugins/sudoers/regress/sudoers/test9.ldif.ok b/plugins/sudoers/regress/sudoers/test9.ldif.ok
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test9.ldif.ok
diff --git a/plugins/sudoers/regress/sudoers/test9.out.ok b/plugins/sudoers/regress/sudoers/test9.out.ok
new file mode 100644
index 0000000..5af5c53
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test9.out.ok
@@ -0,0 +1,2 @@
+Parses OK
+
diff --git a/plugins/sudoers/regress/sudoers/test9.toke.ok b/plugins/sudoers/regress/sudoers/test9.toke.ok
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/sudoers/regress/sudoers/test9.toke.ok
diff --git a/plugins/sudoers/regress/testsudoers/group b/plugins/sudoers/regress/testsudoers/group
new file mode 100644
index 0000000..e2202d6
--- /dev/null
+++ b/plugins/sudoers/regress/testsudoers/group
@@ -0,0 +1,15 @@
+wheel:*:0:root
+daemon:*:1:daemon
+kmem:*:2:root
+sys:*:3:root
+tty:*:4:root
+operator:*:5:root
+bin:*:7:
+wsrc:*:9:
+users:*:10:
+auth:*:11:
+games:*:13:
+staff:*:20:root
+guest:*:31:root
+nogroup:*:32766:
+nobody:*:32767:
diff --git a/plugins/sudoers/regress/testsudoers/test1.out.ok b/plugins/sudoers/regress/testsudoers/test1.out.ok
new file mode 100644
index 0000000..5c292be
--- /dev/null
+++ b/plugins/sudoers/regress/testsudoers/test1.out.ok
@@ -0,0 +1,8 @@
+Parses OK
+
+Entries for user root:
+
+ALL = ALL
+ host matched
+
+Command unmatched
diff --git a/plugins/sudoers/regress/testsudoers/test1.sh b/plugins/sudoers/regress/testsudoers/test1.sh
new file mode 100755
index 0000000..495f237
--- /dev/null
+++ b/plugins/sudoers/regress/testsudoers/test1.sh
@@ -0,0 +1,15 @@
+#!/bin/sh
+#
+# Test for NULL dereference with "sudo -g group" when the sudoers rule
+# has no runas user or group listed.
+# This is RedHat bug Bug 667103.
+#
+
+: ${TESTSUDOERS=testsudoers}
+
+exec 2>&1
+$TESTSUDOERS -g bin -P ${TESTDIR}/group root id <<EOF
+root ALL = ALL
+EOF
+
+exit 0
diff --git a/plugins/sudoers/regress/testsudoers/test10.out.ok b/plugins/sudoers/regress/testsudoers/test10.out.ok
new file mode 100644
index 0000000..3515094
--- /dev/null
+++ b/plugins/sudoers/regress/testsudoers/test10.out.ok
@@ -0,0 +1,51 @@
+Testing @include of a path with escaped white space
+
+Parses OK
+
+Entries for user root:
+
+ALL = ALL
+ host matched
+ runas matched
+ cmnd allowed
+
+Command allowed
+
+Testing @include of a double-quoted path with white space
+
+Parses OK
+
+Entries for user root:
+
+ALL = ALL
+ host matched
+ runas matched
+ cmnd allowed
+
+Command allowed
+
+Testing #include of a path with escaped white space
+
+Parses OK
+
+Entries for user root:
+
+ALL = ALL
+ host matched
+ runas matched
+ cmnd allowed
+
+Command allowed
+
+Testing #include of a double-quoted path with white space
+
+Parses OK
+
+Entries for user root:
+
+ALL = ALL
+ host matched
+ runas matched
+ cmnd allowed
+
+Command allowed
diff --git a/plugins/sudoers/regress/testsudoers/test10.sh b/plugins/sudoers/regress/testsudoers/test10.sh
new file mode 100755
index 0000000..c4f0f2d
--- /dev/null
+++ b/plugins/sudoers/regress/testsudoers/test10.sh
@@ -0,0 +1,46 @@
+#!/bin/sh
+#
+# Test @include of a file with embedded white space
+#
+
+: ${TESTSUDOERS=testsudoers}
+
+# Create test file
+TESTDIR="`pwd`/regress/testsudoers"
+cat >"$TESTDIR/test 10.inc" <<EOF
+root ALL = ALL
+EOF
+
+MYUID=`\ls -lnd "$TESTDIR/test 10.inc" | awk '{print $3}'`
+MYGID=`\ls -lnd "$TESTDIR/test 10.inc" | awk '{print $4}'`
+exec 2>&1
+
+echo "Testing @include of a path with escaped white space"
+echo ""
+$TESTSUDOERS -U $MYUID -G $MYGID root id <<-EOF
+ @include $TESTDIR/test\ 10.inc
+EOF
+
+echo ""
+echo "Testing @include of a double-quoted path with white space"
+echo ""
+$TESTSUDOERS -U $MYUID -G $MYGID root id <<-EOF
+ @include "$TESTDIR/test 10.inc"
+EOF
+
+echo ""
+echo "Testing #include of a path with escaped white space"
+echo ""
+$TESTSUDOERS -U $MYUID -G $MYGID root id <<-EOF
+ #include $TESTDIR/test\ 10.inc
+EOF
+
+echo ""
+echo "Testing #include of a double-quoted path with white space"
+echo ""
+$TESTSUDOERS -U $MYUID -G $MYGID root id <<-EOF
+ #include "$TESTDIR/test 10.inc"
+EOF
+
+rm -f "$TESTDIR/test 10.inc"
+exit 0
diff --git a/plugins/sudoers/regress/testsudoers/test11.out.ok b/plugins/sudoers/regress/testsudoers/test11.out.ok
new file mode 100644
index 0000000..987ab65
--- /dev/null
+++ b/plugins/sudoers/regress/testsudoers/test11.out.ok
@@ -0,0 +1,21 @@
+Testing @include with garbage after the path name
+
+sudoers:1:24: syntax error
+@include sudoers.local womp womp
+ ^~~~
+testsudoers: unable to stat sudoers.local: No such file or directory
+
+Entries for user root:
+
+Command unmatched
+
+Testing #include with garbage after the path name
+
+sudoers:1:24: syntax error
+#include sudoers.local womp womp
+ ^~~~
+testsudoers: unable to stat sudoers.local: No such file or directory
+
+Entries for user root:
+
+Command unmatched
diff --git a/plugins/sudoers/regress/testsudoers/test11.sh b/plugins/sudoers/regress/testsudoers/test11.sh
new file mode 100755
index 0000000..79bdadd
--- /dev/null
+++ b/plugins/sudoers/regress/testsudoers/test11.sh
@@ -0,0 +1,28 @@
+#!/bin/sh
+#
+# Test @include with garbage after the path name
+# The standard error output is dup'd to the standard output.
+#
+
+: ${TESTSUDOERS=testsudoers}
+
+# Avoid warnings about memory leaks when there is a syntax error
+ASAN_OPTIONS=detect_leaks=0; export ASAN_OPTIONS
+
+MYUID=`\ls -ln $TESTDIR/test2.inc | awk '{print $3}'`
+MYGID=`\ls -ln $TESTDIR/test2.inc | awk '{print $4}'`
+
+echo "Testing @include with garbage after the path name"
+echo ""
+$TESTSUDOERS -U $MYUID -G $MYGID root id <<EOF 2>&1 | sed 's/\(syntax error\), .*/\1/'
+@include sudoers.local womp womp
+EOF
+
+echo ""
+echo "Testing #include with garbage after the path name"
+echo ""
+$TESTSUDOERS -U $MYUID -G $MYGID root id <<EOF 2>&1 | sed 's/\(syntax error\), .*/\1/'
+#include sudoers.local womp womp
+EOF
+
+exit 0
diff --git a/plugins/sudoers/regress/testsudoers/test12.out.ok b/plugins/sudoers/regress/testsudoers/test12.out.ok
new file mode 100644
index 0000000..93205dd
--- /dev/null
+++ b/plugins/sudoers/regress/testsudoers/test12.out.ok
@@ -0,0 +1,15 @@
+Testing sudoers with multiple syntax errors
+
+sudoers:1:20: syntax error
+User_Alias A1 = u1 u2 : A2 = u3, u4
+ ^~
+sudoers:3:26: syntax error
+millert ALL = /fail : foo
+ ^
+sudoers:5:16: syntax error
+root ALL = ALL bar
+ ^~~
+
+User_Alias A1 = u1
+
+millert ALL = /fail
diff --git a/plugins/sudoers/regress/testsudoers/test12.sh b/plugins/sudoers/regress/testsudoers/test12.sh
new file mode 100755
index 0000000..d496e3d
--- /dev/null
+++ b/plugins/sudoers/regress/testsudoers/test12.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+#
+# Test sudoers file with multiple syntax errors
+# The standard error output is dup'd to the standard output.
+#
+
+: ${TESTSUDOERS=testsudoers}
+
+# Avoid warnings about memory leaks when there is a syntax error
+ASAN_OPTIONS=detect_leaks=0; export ASAN_OPTIONS
+
+echo "Testing sudoers with multiple syntax errors"
+echo ""
+$TESTSUDOERS -d <<EOF 2>&1 | sed 's/\(syntax error\), .*/\1/'
+User_Alias A1 = u1 u2 : A2 = u3, u4
+
+millert ALL = /fail : foo
+
+root ALL = ALL bar
+EOF
diff --git a/plugins/sudoers/regress/testsudoers/test13.out.ok b/plugins/sudoers/regress/testsudoers/test13.out.ok
new file mode 100644
index 0000000..bfb9f53
--- /dev/null
+++ b/plugins/sudoers/regress/testsudoers/test13.out.ok
@@ -0,0 +1,22 @@
+Testing alias definitions using reserved words
+
+sudoers:1:12: syntax error, reserved word ALL used as an alias name
+Cmnd_Alias ALL=ALL
+ ^~~
+sudoers:2:12: syntax error, reserved word CHROOT used as an alias name
+Cmnd_Alias CHROOT=foo
+ ^~~~~~
+sudoers:3:12: syntax error, reserved word CMND_TIMEOUT used as an alias name
+User_Alias TIMEOUT=foo
+ ^~~~~~~
+sudoers:4:13: syntax error, reserved word CWD used as an alias name
+Runas_Alias CWD=bar
+ ^~~
+sudoers:5:12: syntax error, reserved word NOTBEFORE used as an alias name
+Host_Alias NOTBEFORE=baz
+ ^~~~~~~~~
+sudoers:6:12: syntax error, reserved word NOTAFTER used as an alias name
+Host_Alias NOTAFTER=biff
+ ^~~~~~~~
+
+root ALL = ALL
diff --git a/plugins/sudoers/regress/testsudoers/test13.sh b/plugins/sudoers/regress/testsudoers/test13.sh
new file mode 100755
index 0000000..e33cbb1
--- /dev/null
+++ b/plugins/sudoers/regress/testsudoers/test13.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+#
+# Test sudoers file with reserved words as alias names.
+# The standard error output is dup'd to the standard output.
+#
+
+: ${TESTSUDOERS=testsudoers}
+
+# Avoid warnings about memory leaks when there is a syntax error
+ASAN_OPTIONS=detect_leaks=0; export ASAN_OPTIONS
+
+echo "Testing alias definitions using reserved words"
+echo ""
+$TESTSUDOERS -d <<EOF 2>&1
+Cmnd_Alias ALL=ALL
+Cmnd_Alias CHROOT=foo
+User_Alias TIMEOUT=foo
+Runas_Alias CWD=bar
+Host_Alias NOTBEFORE=baz
+Host_Alias NOTAFTER=biff
+
+root ALL = ALL
+EOF
+
+exit 0
diff --git a/plugins/sudoers/regress/testsudoers/test14.out.ok b/plugins/sudoers/regress/testsudoers/test14.out.ok
new file mode 100644
index 0000000..add1bb2
--- /dev/null
+++ b/plugins/sudoers/regress/testsudoers/test14.out.ok
@@ -0,0 +1,18 @@
+
+Testing user privilege without a newline
+
+Parses OK
+
+millert ALL = ALL
+
+Testing alias without a newline
+
+Parses OK
+
+Cmnd_Alias FOO = /bin/bar
+
+Testing Defaults without a newline
+
+Parses OK
+
+Defaults log_output
diff --git a/plugins/sudoers/regress/testsudoers/test14.sh b/plugins/sudoers/regress/testsudoers/test14.sh
new file mode 100755
index 0000000..7739c67
--- /dev/null
+++ b/plugins/sudoers/regress/testsudoers/test14.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+#
+# Test entries with no trailing newline.
+#
+
+: ${TESTSUDOERS=testsudoers}
+
+exec 2>&1
+
+echo ""
+echo "Testing user privilege without a newline"
+echo ""
+printf "millert ALL = ALL" | $TESTSUDOERS -d
+
+echo ""
+echo "Testing alias without a newline"
+echo ""
+printf "Cmnd_Alias FOO=/bin/bar" | $TESTSUDOERS -d
+
+echo ""
+echo "Testing Defaults without a newline"
+echo ""
+printf "Defaults log_output" | $TESTSUDOERS -d
+
+exit 0
diff --git a/plugins/sudoers/regress/testsudoers/test15.out.ok b/plugins/sudoers/regress/testsudoers/test15.out.ok
new file mode 100644
index 0000000..ca02857
--- /dev/null
+++ b/plugins/sudoers/regress/testsudoers/test15.out.ok
@@ -0,0 +1,17 @@
+Testing @include of a file with a missing newline
+
+Parses OK
+
+Entries for user root:
+
+ALL = /usr/bin/id
+ host matched
+ runas matched
+ cmnd unmatched
+
+ALL = ALL
+ host matched
+ runas matched
+ cmnd allowed
+
+Command allowed
diff --git a/plugins/sudoers/regress/testsudoers/test15.sh b/plugins/sudoers/regress/testsudoers/test15.sh
new file mode 100755
index 0000000..a4596cd
--- /dev/null
+++ b/plugins/sudoers/regress/testsudoers/test15.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+#
+# Test @include of a file with a missing newline
+#
+
+: ${TESTSUDOERS=testsudoers}
+
+# Create test file
+TESTDIR="`pwd`/regress/testsudoers"
+printf "root ALL = ALL" >"$TESTDIR/test15.inc"
+
+MYUID=`\ls -lnd "$TESTDIR/test15.inc" | awk '{print $3}'`
+MYGID=`\ls -lnd "$TESTDIR/test15.inc" | awk '{print $4}'`
+exec 2>&1
+
+echo "Testing @include of a file with a missing newline"
+echo ""
+$TESTSUDOERS -U $MYUID -G $MYGID root id <<-EOF
+ @include $TESTDIR/test15.inc
+ ALL ALL = /usr/bin/id
+EOF
+
+rm -f "$TESTDIR/test15.inc"
+exit 0
diff --git a/plugins/sudoers/regress/testsudoers/test2.inc b/plugins/sudoers/regress/testsudoers/test2.inc
new file mode 100644
index 0000000..52ca040
--- /dev/null
+++ b/plugins/sudoers/regress/testsudoers/test2.inc
@@ -0,0 +1 @@
+root ALL = ALL
diff --git a/plugins/sudoers/regress/testsudoers/test2.out.ok b/plugins/sudoers/regress/testsudoers/test2.out.ok
new file mode 100644
index 0000000..661f22e
--- /dev/null
+++ b/plugins/sudoers/regress/testsudoers/test2.out.ok
@@ -0,0 +1,25 @@
+Testing @include
+
+Parses OK
+
+Entries for user root:
+
+ALL = ALL
+ host matched
+ runas matched
+ cmnd allowed
+
+Command allowed
+
+Testing #include
+
+Parses OK
+
+Entries for user root:
+
+ALL = ALL
+ host matched
+ runas matched
+ cmnd allowed
+
+Command allowed
diff --git a/plugins/sudoers/regress/testsudoers/test2.sh b/plugins/sudoers/regress/testsudoers/test2.sh
new file mode 100755
index 0000000..0b0b3f8
--- /dev/null
+++ b/plugins/sudoers/regress/testsudoers/test2.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+#
+# Test @include facility
+#
+
+: ${TESTSUDOERS=testsudoers}
+
+MYUID=`\ls -ln $TESTDIR/test2.inc | awk '{print $3}'`
+MYGID=`\ls -ln $TESTDIR/test2.inc | awk '{print $4}'`
+exec 2>&1
+
+echo "Testing @include"
+echo ""
+$TESTSUDOERS -U $MYUID -G $MYGID root id <<EOF
+@include $TESTDIR/test2.inc
+EOF
+
+echo ""
+echo "Testing #include"
+echo ""
+$TESTSUDOERS -U $MYUID -G $MYGID root id <<EOF
+#include $TESTDIR/test2.inc
+EOF
+
+exit 0
diff --git a/plugins/sudoers/regress/testsudoers/test3.out.ok b/plugins/sudoers/regress/testsudoers/test3.out.ok
new file mode 100644
index 0000000..788f2ea
--- /dev/null
+++ b/plugins/sudoers/regress/testsudoers/test3.out.ok
@@ -0,0 +1,51 @@
+Testing @includedir of an unquoted path
+
+Parses OK
+
+Entries for user root:
+
+ALL = ALL
+ host matched
+ runas matched
+ cmnd allowed
+
+Command allowed
+
+Testing @includedir of a double-quoted path
+
+Parses OK
+
+Entries for user root:
+
+ALL = ALL
+ host matched
+ runas matched
+ cmnd allowed
+
+Command allowed
+
+Testing #includedir of an unquoted path
+
+Parses OK
+
+Entries for user root:
+
+ALL = ALL
+ host matched
+ runas matched
+ cmnd allowed
+
+Command allowed
+
+Testing #includedir of a double-quoted path
+
+Parses OK
+
+Entries for user root:
+
+ALL = ALL
+ host matched
+ runas matched
+ cmnd allowed
+
+Command allowed
diff --git a/plugins/sudoers/regress/testsudoers/test3.sh b/plugins/sudoers/regress/testsudoers/test3.sh
new file mode 100755
index 0000000..d166de9
--- /dev/null
+++ b/plugins/sudoers/regress/testsudoers/test3.sh
@@ -0,0 +1,48 @@
+#!/bin/sh
+#
+# Test @includedir facility
+#
+
+: ${TESTSUDOERS=testsudoers}
+
+TESTDIR="`pwd`/regress/testsudoers"
+# make sure include file is owned by current user
+rm -rf "$TESTDIR/test3.d"
+mkdir "$TESTDIR/test3.d"
+cat >"$TESTDIR/test3.d/root" <<-EOF
+ root ALL = ALL
+EOF
+
+MYUID=`\ls -lnd $TESTDIR/test3.d | awk '{print $3}'`
+MYGID=`\ls -lnd $TESTDIR/test3.d | awk '{print $4}'`
+exec 2>&1
+
+echo "Testing @includedir of an unquoted path"
+echo ""
+$TESTSUDOERS -U $MYUID -G $MYGID root id <<-EOF
+ @includedir $TESTDIR/test3.d
+EOF
+
+echo ""
+echo "Testing @includedir of a double-quoted path"
+echo ""
+$TESTSUDOERS -U $MYUID -G $MYGID root id <<-EOF
+ @includedir "$TESTDIR/test3.d"
+EOF
+
+echo ""
+echo "Testing #includedir of an unquoted path"
+echo ""
+$TESTSUDOERS -U $MYUID -G $MYGID root id <<-EOF
+ #includedir $TESTDIR/test3.d
+EOF
+
+echo ""
+echo "Testing #includedir of a double-quoted path"
+echo ""
+$TESTSUDOERS -U $MYUID -G $MYGID root id <<-EOF
+ #includedir "$TESTDIR/test3.d"
+EOF
+
+rm -rf "$TESTDIR/test3.d"
+exit 0
diff --git a/plugins/sudoers/regress/testsudoers/test4.out.ok b/plugins/sudoers/regress/testsudoers/test4.out.ok
new file mode 100644
index 0000000..cf68da5
--- /dev/null
+++ b/plugins/sudoers/regress/testsudoers/test4.out.ok
@@ -0,0 +1,5 @@
+testsudoers: test2.inc should be owned by uid 1
+
+Entries for user root:
+
+Command unmatched
diff --git a/plugins/sudoers/regress/testsudoers/test4.sh b/plugins/sudoers/regress/testsudoers/test4.sh
new file mode 100755
index 0000000..2ec47ce
--- /dev/null
+++ b/plugins/sudoers/regress/testsudoers/test4.sh
@@ -0,0 +1,16 @@
+#!/bin/sh
+#
+# Test sudoers owner check
+#
+
+: ${TESTSUDOERS=testsudoers}
+
+# Avoid warnings about memory leaks when there is a syntax error
+ASAN_OPTIONS=detect_leaks=0; export ASAN_OPTIONS
+
+exec 2>&1
+$TESTSUDOERS -U 1 root id <<EOF
+@include $TESTDIR/test2.inc
+EOF
+
+exit 0
diff --git a/plugins/sudoers/regress/testsudoers/test5.out.ok b/plugins/sudoers/regress/testsudoers/test5.out.ok
new file mode 100644
index 0000000..a4ead0e
--- /dev/null
+++ b/plugins/sudoers/regress/testsudoers/test5.out.ok
@@ -0,0 +1,10 @@
+testsudoers: test5.inc is world writable
+
+Entries for user root:
+
+Command unmatched
+testsudoers: test5.inc should be owned by gid 4294967294
+
+Entries for user root:
+
+Command unmatched
diff --git a/plugins/sudoers/regress/testsudoers/test5.sh b/plugins/sudoers/regress/testsudoers/test5.sh
new file mode 100755
index 0000000..8ace372
--- /dev/null
+++ b/plugins/sudoers/regress/testsudoers/test5.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+#
+# Test sudoers file mode check
+#
+
+: ${TESTSUDOERS=testsudoers}
+
+# Avoid warnings about memory leaks when there is a syntax error
+ASAN_OPTIONS=detect_leaks=0; export ASAN_OPTIONS
+
+# Create test file
+TESTFILE="`pwd`/regress/testsudoers/test5.inc"
+cat >"$TESTFILE" <<EOF
+root ALL = ALL
+EOF
+
+MYUID=`\ls -ln $TESTFILE | awk '{print $3}'`
+MYGID=`\ls -ln $TESTFILE | awk '{print $4}'`
+exec 2>&1
+
+# Test world writable
+chmod 666 $TESTFILE
+$TESTSUDOERS -U $MYUID -G $MYGID root id <<EOF
+@include $TESTFILE
+EOF
+
+# Test group writable
+chmod 664 $TESTFILE
+$TESTSUDOERS -U $MYUID -G -2 root id <<EOF
+@include $TESTFILE
+EOF
+
+rm -f $TESTFILE
+exit 0
diff --git a/plugins/sudoers/regress/testsudoers/test6.out.ok b/plugins/sudoers/regress/testsudoers/test6.out.ok
new file mode 100644
index 0000000..3ec84bd
--- /dev/null
+++ b/plugins/sudoers/regress/testsudoers/test6.out.ok
@@ -0,0 +1,10 @@
+Parses OK
+
+Entries for user root:
+
+ALL = ALL
+ host matched
+ runas matched
+ cmnd allowed
+
+Command allowed
diff --git a/plugins/sudoers/regress/testsudoers/test6.sh b/plugins/sudoers/regress/testsudoers/test6.sh
new file mode 100755
index 0000000..f3b54f8
--- /dev/null
+++ b/plugins/sudoers/regress/testsudoers/test6.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+#
+# Verify sudoers matching by uid.
+#
+
+: ${TESTSUDOERS=testsudoers}
+
+exec 2>&1
+$TESTSUDOERS root id <<EOF
+#0 ALL = ALL
+EOF
+
+exit 0
diff --git a/plugins/sudoers/regress/testsudoers/test7.out.ok b/plugins/sudoers/regress/testsudoers/test7.out.ok
new file mode 100644
index 0000000..3ec84bd
--- /dev/null
+++ b/plugins/sudoers/regress/testsudoers/test7.out.ok
@@ -0,0 +1,10 @@
+Parses OK
+
+Entries for user root:
+
+ALL = ALL
+ host matched
+ runas matched
+ cmnd allowed
+
+Command allowed
diff --git a/plugins/sudoers/regress/testsudoers/test7.sh b/plugins/sudoers/regress/testsudoers/test7.sh
new file mode 100755
index 0000000..9e28c1a
--- /dev/null
+++ b/plugins/sudoers/regress/testsudoers/test7.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+#
+# Verify sudoers matching by gid.
+#
+
+: ${TESTSUDOERS=testsudoers}
+
+exec 2>&1
+$TESTSUDOERS root id <<EOF
+%#0 ALL = ALL
+EOF
+
+exit 0
diff --git a/plugins/sudoers/regress/testsudoers/test8.out.ok b/plugins/sudoers/regress/testsudoers/test8.out.ok
new file mode 100644
index 0000000..b1ed931
--- /dev/null
+++ b/plugins/sudoers/regress/testsudoers/test8.out.ok
@@ -0,0 +1,25 @@
+Testing @include without a newline
+
+Parses OK
+
+Entries for user root:
+
+ALL = ALL
+ host matched
+ runas matched
+ cmnd allowed
+
+Command allowed
+
+Testing #include without a newline
+
+Parses OK
+
+Entries for user root:
+
+ALL = ALL
+ host matched
+ runas matched
+ cmnd allowed
+
+Command allowed
diff --git a/plugins/sudoers/regress/testsudoers/test8.sh b/plugins/sudoers/regress/testsudoers/test8.sh
new file mode 100755
index 0000000..c22b590
--- /dev/null
+++ b/plugins/sudoers/regress/testsudoers/test8.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+#
+# Test @include facility w/o a final newline.
+# Same as test2.sh but missing the final newline.
+#
+
+: ${TESTSUDOERS=testsudoers}
+
+MYUID=`\ls -ln $TESTDIR/test2.inc | awk '{print $3}'`
+MYGID=`\ls -ln $TESTDIR/test2.inc | awk '{print $4}'`
+exec 2>&1
+
+echo "Testing @include without a newline"
+echo ""
+printf "@include $TESTDIR/test2.inc" | \
+ $TESTSUDOERS -U $MYUID -G $MYGID root id
+
+echo ""
+echo "Testing #include without a newline"
+echo ""
+printf "#include $TESTDIR/test2.inc" | \
+ $TESTSUDOERS -U $MYUID -G $MYGID root id
+
+exit 0
diff --git a/plugins/sudoers/regress/testsudoers/test9.out.ok b/plugins/sudoers/regress/testsudoers/test9.out.ok
new file mode 100644
index 0000000..3ec84bd
--- /dev/null
+++ b/plugins/sudoers/regress/testsudoers/test9.out.ok
@@ -0,0 +1,10 @@
+Parses OK
+
+Entries for user root:
+
+ALL = ALL
+ host matched
+ runas matched
+ cmnd allowed
+
+Command allowed
diff --git a/plugins/sudoers/regress/testsudoers/test9.sh b/plugins/sudoers/regress/testsudoers/test9.sh
new file mode 100755
index 0000000..850bbac
--- /dev/null
+++ b/plugins/sudoers/regress/testsudoers/test9.sh
@@ -0,0 +1,15 @@
+#!/bin/sh
+#
+# Test #include facility
+#
+
+: ${TESTSUDOERS=testsudoers}
+
+MYUID=`\ls -ln $TESTDIR/test2.inc | awk '{print $3}'`
+MYGID=`\ls -ln $TESTDIR/test2.inc | awk '{print $4}'`
+exec 2>&1
+$TESTSUDOERS -U $MYUID -G $MYGID root id <<EOF
+#include $TESTDIR/test2.inc
+EOF
+
+exit 0
diff --git a/plugins/sudoers/regress/visudo/test1.out.ok b/plugins/sudoers/regress/visudo/test1.out.ok
new file mode 100644
index 0000000..e5c355c
--- /dev/null
+++ b/plugins/sudoers/regress/visudo/test1.out.ok
@@ -0,0 +1 @@
+stdin: parsed OK
diff --git a/plugins/sudoers/regress/visudo/test1.sh b/plugins/sudoers/regress/visudo/test1.sh
new file mode 100755
index 0000000..5676eea
--- /dev/null
+++ b/plugins/sudoers/regress/visudo/test1.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+#
+# Sudo Bug 519:
+# Visudo in strict mode reports "parse error" even if there is no error
+#
+
+: ${VISUDO=visudo}
+
+$VISUDO -csf - <<EOF
+User_Alias FOO = nobody
+FOO ALL=(ALL) NOPASSWD: ALL
+EOF
+
+exit 0
diff --git a/plugins/sudoers/regress/visudo/test10.out.ok b/plugins/sudoers/regress/visudo/test10.out.ok
new file mode 100644
index 0000000..e5c355c
--- /dev/null
+++ b/plugins/sudoers/regress/visudo/test10.out.ok
@@ -0,0 +1 @@
+stdin: parsed OK
diff --git a/plugins/sudoers/regress/visudo/test10.sh b/plugins/sudoers/regress/visudo/test10.sh
new file mode 100755
index 0000000..38b7f3e
--- /dev/null
+++ b/plugins/sudoers/regress/visudo/test10.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+#
+# Test parsing of NOTBEFORE/NOTAFTER using local time zone
+#
+
+: ${VISUDO=visudo}
+
+$VISUDO -cf - <<-EOF
+ user1 ALL = NOTBEFORE=20151201235900 /usr/bin/id
+ user2 ALL = NOTBEFORE=20151201235900.2 /usr/bin/id
+ user3 ALL = NOTBEFORE=20151201235900\,2 /usr/bin/id
+ user4 ALL = NOTBEFORE=2015120123 /usr/bin/id
+ EOF
diff --git a/plugins/sudoers/regress/visudo/test2.err.ok b/plugins/sudoers/regress/visudo/test2.err.ok
new file mode 100644
index 0000000..e6a275d
--- /dev/null
+++ b/plugins/sudoers/regress/visudo/test2.err.ok
@@ -0,0 +1 @@
+Error: stdin:1:12: cycle in User_Alias "FOO"
diff --git a/plugins/sudoers/regress/visudo/test2.out.ok b/plugins/sudoers/regress/visudo/test2.out.ok
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/sudoers/regress/visudo/test2.out.ok
diff --git a/plugins/sudoers/regress/visudo/test2.sh b/plugins/sudoers/regress/visudo/test2.sh
new file mode 100755
index 0000000..8ab2382
--- /dev/null
+++ b/plugins/sudoers/regress/visudo/test2.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+#
+# Test cycle detection
+# Prior to sudo 1.8.6p5 this resulted in a core dump (stack smash)
+# The names of the aliases (or rather their lexical order) is important.
+#
+
+: ${VISUDO=visudo}
+
+$VISUDO -csf - <<EOF
+User_Alias YYY = FOO
+User_Alias XXX = nobody
+User_Alias FOO = XXX, YYY
+FOO ALL = ALL
+EOF
+
+exit 0
diff --git a/plugins/sudoers/regress/visudo/test3.err.ok b/plugins/sudoers/regress/visudo/test3.err.ok
new file mode 100644
index 0000000..38fe9b8
--- /dev/null
+++ b/plugins/sudoers/regress/visudo/test3.err.ok
@@ -0,0 +1,2 @@
+Warning: stdin:1:12: unused User_Alias "A"
+Warning: stdin:2:12: unused User_Alias "B"
diff --git a/plugins/sudoers/regress/visudo/test3.out.ok b/plugins/sudoers/regress/visudo/test3.out.ok
new file mode 100644
index 0000000..e5c355c
--- /dev/null
+++ b/plugins/sudoers/regress/visudo/test3.out.ok
@@ -0,0 +1 @@
+stdin: parsed OK
diff --git a/plugins/sudoers/regress/visudo/test3.sh b/plugins/sudoers/regress/visudo/test3.sh
new file mode 100755
index 0000000..e556e63
--- /dev/null
+++ b/plugins/sudoers/regress/visudo/test3.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+#
+# Sudo Bug 361:
+# Excerises a bug in the redblack tree code.
+#
+
+: ${VISUDO=visudo}
+
+$VISUDO -cf - <<EOF
+User_Alias A=a
+User_Alias B=a
+User_Alias C=a
+User_Alias D=a
+User_Alias E=a
+User_Alias F=a
+User_Alias G=a
+User_Alias H=a
+User_Alias I=a
+User_Alias J=a
+User_Alias K=a
+User_Alias L=a
+User_Alias M=a
+
+C ALL=(ALL) ALL
+E ALL=(ALL) ALL
+J ALL=(ALL) ALL
+D ALL=(ALL) ALL
+L ALL=(ALL) ALL
+H ALL=(ALL) ALL
+F ALL=(ALL) ALL
+G ALL=(ALL) ALL
+M ALL=(ALL) ALL
+K ALL=(ALL) ALL
+I ALL=(ALL) ALL
+EOF
+
+exit 0
diff --git a/plugins/sudoers/regress/visudo/test4.out.ok b/plugins/sudoers/regress/visudo/test4.out.ok
new file mode 100644
index 0000000..e5c355c
--- /dev/null
+++ b/plugins/sudoers/regress/visudo/test4.out.ok
@@ -0,0 +1 @@
+stdin: parsed OK
diff --git a/plugins/sudoers/regress/visudo/test4.sh b/plugins/sudoers/regress/visudo/test4.sh
new file mode 100755
index 0000000..465b91c
--- /dev/null
+++ b/plugins/sudoers/regress/visudo/test4.sh
@@ -0,0 +1,16 @@
+#!/bin/sh
+#
+# Test cycle detection and duplicate entries.
+# Prior to sudo 1.8.7 this resulted in a false positive.
+#
+
+: ${VISUDO=visudo}
+
+$VISUDO -csf - <<EOF
+Host_Alias H1 = host1
+Host_Alias H2 = H1, host2
+Host_Alias H3 = H1, H2
+root H3 = ALL
+EOF
+
+exit 0
diff --git a/plugins/sudoers/regress/visudo/test5.out.ok b/plugins/sudoers/regress/visudo/test5.out.ok
new file mode 100644
index 0000000..e5c355c
--- /dev/null
+++ b/plugins/sudoers/regress/visudo/test5.out.ok
@@ -0,0 +1 @@
+stdin: parsed OK
diff --git a/plugins/sudoers/regress/visudo/test5.sh b/plugins/sudoers/regress/visudo/test5.sh
new file mode 100755
index 0000000..c870df9
--- /dev/null
+++ b/plugins/sudoers/regress/visudo/test5.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+#
+# Test comment on the last line with no newline
+#
+
+: ${VISUDO=visudo}
+
+printf "# one comment\n#two comments" | $VISUDO -csf -
+
+exit 0
diff --git a/plugins/sudoers/regress/visudo/test6.out.ok b/plugins/sudoers/regress/visudo/test6.out.ok
new file mode 100644
index 0000000..e5c355c
--- /dev/null
+++ b/plugins/sudoers/regress/visudo/test6.out.ok
@@ -0,0 +1 @@
+stdin: parsed OK
diff --git a/plugins/sudoers/regress/visudo/test6.sh b/plugins/sudoers/regress/visudo/test6.sh
new file mode 100755
index 0000000..5f7adee
--- /dev/null
+++ b/plugins/sudoers/regress/visudo/test6.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+#
+# Verify parsing of Defaults syntax
+#
+
+: ${VISUDO=visudo}
+
+$VISUDO -csf - <<EOF
+Defaults syslog=auth
+Defaults>root !set_logname
+Defaults:FULLTIMERS !lecture
+Defaults:millert !authenticate
+Defaults@SERVERS log_year, logfile=/var/log/sudo.log
+Defaults!PAGERS noexec
+
+Defaults env_keep -= "HOME"
+Defaults env_keep = "COLORS DISPLAY HOSTNAME HISTSIZE KDEDIR LS_COLORS"
+Defaults env_keep += "MAIL PS1 PS2 QTDIR LANG LC_ADDRESS LC_CTYPE"
+
+User_Alias FULLTIMERS = millert, mikef, dowdy
+
+Cmnd_Alias PAGERS = /usr/bin/more, /usr/bin/pg, /usr/bin/less
+
+Host_Alias SERVERS = primary, mail, www, ns
+EOF
+
+exit 0
diff --git a/plugins/sudoers/regress/visudo/test7.out.ok b/plugins/sudoers/regress/visudo/test7.out.ok
new file mode 100644
index 0000000..e5c355c
--- /dev/null
+++ b/plugins/sudoers/regress/visudo/test7.out.ok
@@ -0,0 +1 @@
+stdin: parsed OK
diff --git a/plugins/sudoers/regress/visudo/test7.sh b/plugins/sudoers/regress/visudo/test7.sh
new file mode 100755
index 0000000..b993fe7
--- /dev/null
+++ b/plugins/sudoers/regress/visudo/test7.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+#
+# Test sudoers_locale early Defaults
+#
+
+: ${VISUDO=visudo}
+
+LANG=C; export LANG
+LC_NUMERIC=fr_FR.UTF-8; export LC_NUMERIC
+
+# First check that visudo supports non-C locales
+# Note that older versions of sudo did not set the locale
+# until sudoers was read so this check will fail on them.
+$VISUDO -csf - >/dev/null 2>&1 <<-EOF
+ Defaults sudoers_locale = fr_FR.UTF-8
+ Defaults passwd_timeout = "2,5"
+ EOF
+
+# Now make sure we can set passwd_timeout to a floating point value
+# using a non-C locale.
+if [ $? -eq 0 ]; then
+ $VISUDO -csf - <<-EOF
+ Defaults passwd_timeout = "2,5"
+ Defaults sudoers_locale = fr_FR.UTF-8
+ EOF
+else
+ # No support for LC_NUMERIC?
+ echo "stdin: parsed OK"
+fi
+
+exit 0
diff --git a/plugins/sudoers/regress/visudo/test8.err.ok b/plugins/sudoers/regress/visudo/test8.err.ok
new file mode 100644
index 0000000..3e71f62
--- /dev/null
+++ b/plugins/sudoers/regress/visudo/test8.err.ok
@@ -0,0 +1 @@
+visudo: stdin:1: value "2.5" is invalid for option "passwd_timeout"
diff --git a/plugins/sudoers/regress/visudo/test8.out.ok b/plugins/sudoers/regress/visudo/test8.out.ok
new file mode 100644
index 0000000..16ebc45
--- /dev/null
+++ b/plugins/sudoers/regress/visudo/test8.out.ok
@@ -0,0 +1 @@
+parse error in stdin near line 1
diff --git a/plugins/sudoers/regress/visudo/test8.sh b/plugins/sudoers/regress/visudo/test8.sh
new file mode 100755
index 0000000..85bdd9f
--- /dev/null
+++ b/plugins/sudoers/regress/visudo/test8.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+#
+# Test sudoers_locale early Defaults
+#
+
+: ${VISUDO=visudo}
+
+LANG=C; export LANG
+LC_NUMERIC=fr_FR.UTF-8; export LC_NUMERIC
+
+# First check that visudo supports non-C locales
+# Note that older versions of sudo did not set the locale
+# until sudoers was read so this check will fail on them.
+$VISUDO -csf - >/dev/null 2>&1 <<-EOF
+ Defaults sudoers_locale = fr_FR.UTF-8
+ Defaults passwd_timeout = "2,5"
+ EOF
+
+# Now make sure we can set passwd_timeout to a floating point value
+# using a non-C locale.
+if [ $? -eq 0 ]; then
+ $VISUDO -csf - <<-EOF
+ Defaults passwd_timeout = "2.5"
+ Defaults sudoers_locale = fr_FR.UTF-8
+ EOF
+else
+ # No support for LC_NUMERIC?
+ echo "parse error in stdin near line 1"
+ echo 'visudo: stdin:1: value "2.5" is invalid for option "passwd_timeout"' 1>&2
+fi
+
+exit 0
diff --git a/plugins/sudoers/regress/visudo/test9.out.ok b/plugins/sudoers/regress/visudo/test9.out.ok
new file mode 100644
index 0000000..e5c355c
--- /dev/null
+++ b/plugins/sudoers/regress/visudo/test9.out.ok
@@ -0,0 +1 @@
+stdin: parsed OK
diff --git a/plugins/sudoers/regress/visudo/test9.sh b/plugins/sudoers/regress/visudo/test9.sh
new file mode 100755
index 0000000..175ad6e
--- /dev/null
+++ b/plugins/sudoers/regress/visudo/test9.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+#
+# Test IP and network address in host-based Defaults statements
+# Bugzilla #766
+#
+
+: ${VISUDO=visudo}
+
+$VISUDO -cf - <<-EOF
+ Defaults@127.0.0.1 !authenticate
+ Defaults@10.0.0.0/8 !always_set_home
+ EOF
+
+exit 0