Script should explicitly set LANG=C to avoid problems with localized output #184

Closed
opened 2026-01-19 18:29:34 +00:00 by michael · 6 comments
Owner

Originally created by @cvgs on GitHub.

Originally assigned to: @brodjieski on GitHub.

Problem to solve

We should try to make the compliance checking script as robust as possible to avoid false positives or other reporting errors.

Further details

Right now, "sudo -V" might return localized number formats, which will cause the timeout parsing section to fail (e.g. "timeout: 5,0" instead of the expected "timeout: 5.0").

Proposal

If we set the LANG environment variable to "C" at start of the script, we might avoid some of those localized outputs and standardize in one format.

Testing

A global change might affect every single script block, so this might have unintended side effects.

Originally created by @cvgs on GitHub. Originally assigned to: @brodjieski on GitHub. ### Problem to solve We should try to make the compliance checking script as robust as possible to avoid false positives or other reporting errors. ### Further details Right now, "sudo -V" might return localized number formats, which will cause the timeout parsing section to fail (e.g. "timeout: 5,0" instead of the expected "timeout: 5.0"). ### Proposal If we set the LANG environment variable to "C" at start of the script, we might avoid some of those localized outputs and standardize in one format. ### Testing A global change might affect every single script block, so this might have unintended side effects.
Author
Owner

@brodjieski commented on GitHub:

This type of issue speaks to localization, which we currently do not have support for.

In trying to recreate a scenario where the checks, as written, fail due to this concern... but I cannot get my system to provide a false positive/negative. It seems that the default locale for parsing sudoers is C, which allows it to parse correctly.

Can you provide a configuration and setup that is failing the checks so that I can try to recreate?

I'm not sure that setting the LANG to C will solve the issue, especially is sudo is parsing the config files using a different locale altogether. Certainly would like to have this on our radar as we explore localization options.

@brodjieski commented on GitHub: This type of issue speaks to localization, which we currently do not have support for. In trying to recreate a scenario where the checks, as written, fail due to this concern... but I cannot get my system to provide a false positive/negative. It seems that the default locale for parsing `sudoers` is C, which allows it to parse correctly. Can you provide a configuration and setup that is failing the checks so that I can try to recreate? I'm not sure that setting the LANG to C will solve the issue, especially is `sudo` is parsing the config files using a different locale altogether. Certainly would like to have this on our radar as we explore localization options.
Author
Owner

@brodjieski commented on GitHub:

Thank you for providing some additional information. While I understand that the output is different based on locale, when running the actual checks with a different locale set, it produces the expected result.

If you have sudoers configured with Defaults timestamp_timeout=0 the compliance check would be:

# sudo sudo -V | grep -c "Authentication timestamp timeout: 0.0 minutes"

and the expected result is 1.

# echo $LANG; sudo sudo -V | grep -c "Authentication timestamp timeout: 0.0 minutes"
en_US.UTF-8
1

The check result remains 1 even when setting LANG=de_DE.UTF-8

# export LANG=de_DE.UTF-8                                                           
#  echo $LANG; sudo sudo -V | grep -c "Authentication timestamp timeout: 0.0 minutes"
de_DE.UTF-8
1

Even when exporting LC_ALL as suggested, the result is the same:

# export LC_ALL=de_DE.UTF-8                                                           
# locale 
LANG="de_DE.UTF-8"
LC_COLLATE="de_DE.UTF-8"
LC_CTYPE="de_DE.UTF-8"
LC_MESSAGES="de_DE.UTF-8"
LC_MONETARY="de_DE.UTF-8"
LC_NUMERIC="de_DE.UTF-8"
LC_TIME="de_DE.UTF-8"
LC_ALL="de_DE.UTF-8"
# echo $LANG; sudo sudo -V | grep -c "Authentication timestamp timeout: 0.0 minutes"  
de_DE.UTF-8
1

In any of these situations, the compliance check will pass if sudoers is configured correctly. This is due to the pattern we are looking for with the grep command and that the . in the pattern is actually representing a wildcard character, and would match on either a , or . when running the check.

@brodjieski commented on GitHub: Thank you for providing some additional information. While I understand that the output is different based on `locale`, when running the actual checks with a different `locale` set, it produces the expected result. If you have `sudoers` configured with `Defaults timestamp_timeout=0` the compliance check would be: ``` # sudo sudo -V | grep -c "Authentication timestamp timeout: 0.0 minutes" ``` and the expected result is `1`. ``` # echo $LANG; sudo sudo -V | grep -c "Authentication timestamp timeout: 0.0 minutes" en_US.UTF-8 1 ``` The check result remains `1` even when setting `LANG=de_DE.UTF-8` ``` # export LANG=de_DE.UTF-8 # echo $LANG; sudo sudo -V | grep -c "Authentication timestamp timeout: 0.0 minutes" de_DE.UTF-8 1 ``` Even when exporting `LC_ALL` as suggested, the result is the same: ``` # export LC_ALL=de_DE.UTF-8 # locale LANG="de_DE.UTF-8" LC_COLLATE="de_DE.UTF-8" LC_CTYPE="de_DE.UTF-8" LC_MESSAGES="de_DE.UTF-8" LC_MONETARY="de_DE.UTF-8" LC_NUMERIC="de_DE.UTF-8" LC_TIME="de_DE.UTF-8" LC_ALL="de_DE.UTF-8" # echo $LANG; sudo sudo -V | grep -c "Authentication timestamp timeout: 0.0 minutes" de_DE.UTF-8 1 ``` In any of these situations, the compliance check will pass if `sudoers` is configured correctly. This is due to the pattern we are looking for with the `grep` command and that the `.` in the pattern is actually representing a wildcard character, and would match on either a `,` or `.` when running the check.
Author
Owner

@georgalis commented on GitHub:

@brodjieski the available locales can be determined with locale -a
current environment can be determined with locale alone.
France uses comma notation, and this confirms
( export LC_ALL=fr_FR ; awk 'BEGIN{print (16 / 7)}' )
notably, this doesn't achieve the desired effect...
( export LC_NUMERIC="fr_FR" ; locale ; awk 'BEGIN{print (16 / 7)}' )
my understanding is the LC_* can be set independently, maybe LC_ALL must be null?
Any event, this should reproduce the issue:
( export LC_ALL=fr_FR ; sudo locale ; sudo awk 'BEGIN{print (16 / 7)}' )

@georgalis commented on GitHub: @brodjieski the available locales can be determined with `locale -a` current environment can be determined with `locale` alone. France uses comma notation, and this confirms `( export LC_ALL=fr_FR ; awk 'BEGIN{print (16 / 7)}' )` notably, this doesn't achieve the desired effect... ( export LC_NUMERIC="fr_FR" ; locale ; awk 'BEGIN{print (16 / 7)}' ) my understanding is the LC_* can be set independently, maybe LC_ALL must be null? Any event, this should reproduce the issue: ( export LC_ALL=fr_FR ; sudo locale ; sudo awk 'BEGIN{print (16 / 7)}' )
Author
Owner

@cvgs commented on GitHub:

You can reproduce the issue by setting your system's language to german and then use the compliance.sh script to evaluate rule os_sudo_timeout_configure.

The simplified version to reproduce the issue would be:
Open a shell, do sudo -s to elevate your privileges, and then do:

# echo $LANG; /usr/bin/sudo /usr/bin/sudo -V | /usr/bin/grep "Authentication timestamp timeout: "

Note the formatting of the returned timeout value, which will be dependent on the general language configuration of your system. Then simulate a german system using this command:

# export LANG=de_DE.UTF-8
# echo $LANG; /usr/bin/sudo /usr/bin/sudo -V | /usr/bin/grep "Authentication timestamp timeout: "
de_DE.UTF-8
Authentication timestamp timeout: 5,0 minutes

Note that the timeout period is now formatted using a comma instead of a period, which will cause the check command for os_sudo_timeout_configure to fail. Now we explicitly set the simplest language specification and repeat the check:

# export LANG=C
echo $LANG; /usr/bin/sudo /usr/bin/sudo -V | /usr/bin/grep "Authentication timestamp timeout: "          
C
Authentication timestamp timeout: 5.0 minutes

Note that the timeout is formatted using a period, which will cause the check command to return the expected result.

So setting LANG=C makes the result of the check script predictable across multiple systems which could be set to different languages. Of course, there might be a better way to do this. In fact, using LC_ALL instead of LANG might be a better approach to address this behaviour.

@cvgs commented on GitHub: You can reproduce the issue by setting your system's language to german and then use the compliance.sh script to evaluate rule `os_sudo_timeout_configure`. The simplified version to reproduce the issue would be: Open a shell, do `sudo -s` to elevate your privileges, and then do: ``` # echo $LANG; /usr/bin/sudo /usr/bin/sudo -V | /usr/bin/grep "Authentication timestamp timeout: " ``` Note the formatting of the returned timeout value, which will be dependent on the general language configuration of your system. Then simulate a german system using this command: ``` # export LANG=de_DE.UTF-8 # echo $LANG; /usr/bin/sudo /usr/bin/sudo -V | /usr/bin/grep "Authentication timestamp timeout: " de_DE.UTF-8 Authentication timestamp timeout: 5,0 minutes ``` Note that the timeout period is now formatted using a comma instead of a period, which will cause the check command for `os_sudo_timeout_configure to` fail. Now we explicitly set the simplest language specification and repeat the check: ``` # export LANG=C echo $LANG; /usr/bin/sudo /usr/bin/sudo -V | /usr/bin/grep "Authentication timestamp timeout: " C Authentication timestamp timeout: 5.0 minutes ``` Note that the timeout is formatted using a period, which will cause the check command to return the expected result. So setting LANG=C makes the result of the check script predictable across multiple systems which could be set to different languages. Of course, there might be a better way to do this. In fact, using `LC_ALL` instead of `LANG` might be a better approach to address this behaviour.
Author
Owner

@cvgs commented on GitHub:

You are completely right, the timeout is being set correctly due to the wildcard character being used. I tried to reproduce the issue, but wasn't able to do so anymore in my tests.

Some time ago, I had experienced an error with this timeout value on some machines, dug into the code and assumed that the localization was the problem. But apparently i jumped to the wrong conclusion there, and there was something else involved in the issue. Sorry for the trouble…

However the fact that the output changes based on the users' locale setting might introduce other issues, so it still might be a good idea to try to prevent those locale-based changes.

@cvgs commented on GitHub: You are completely right, the timeout is being set correctly due to the wildcard character being used. I tried to reproduce the issue, but wasn't able to do so anymore in my tests. Some time ago, I had experienced an error with this timeout value on some machines, dug into the code and assumed that the localization was the problem. But apparently i jumped to the wrong conclusion there, and there was something else involved in the issue. Sorry for the trouble… However the fact that the output changes based on the users' locale setting might introduce other issues, so it still might be a good idea to try to prevent those locale-based changes.
Author
Owner

@brodjieski commented on GitHub:

Will keep this in mind as we explore localization implementations in the project.

@brodjieski commented on GitHub: Will keep this in mind as we explore localization implementations in the project.
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: usnistgov/macos_security#184