Skip to content

Issue with $PROGRAM_NAME (sudo + checksum verification) #23351

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
michal-josef-spacek opened this issue Jun 2, 2025 · 1 comment
Open

Issue with $PROGRAM_NAME (sudo + checksum verification) #23351

michal-josef-spacek opened this issue Jun 2, 2025 · 1 comment

Comments

@michal-josef-spacek
Copy link

The issue

The Red Hat customer reported an interesting issue with $PROGRAM_NAME ($0).
There is $PROGRAM_NAME with result like /dev/fd/6 when the script is running under sudo with checksum verification.

We have /usr/local/bin/script.pl

#!/usr/bin/perl

use strict;
use warnings;

use Cwd qw( abs_path );

print "\$0: $0\n";
print "abs_path(\$0): " . abs_path($0) . "\n";

Configuration of sudo:

$ echo "user ALL=NOPASSWD: sha256:$(sha256sum /usr/local/bin/script.pl)" > /etc/sudoers.d/user

Example user:

useradd user

Running as user.

root> su user
user> sudo /usr/local/bin/script.pl

Result like (on Fedora Rawhide):

$0: /dev/fd/6
abs_path($0): /proc/1961/fd/6

I investigated the issue, and there are some consequences:

  1. Perl doesn't user prctl API for get of $PROGRAM_NAME
  2. C code with prctl works fine in this case.
    C code in ex1.c:
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <sys/prctl.h>

int main(void) {
    char name[17] = {0};

    if (prctl(PR_GET_NAME, (unsigned long)name, 0, 0, 0) == -1) {
        perror("prctl(PR_GET_NAME)");
        return 1;
    }

    printf("Name of program: %s\n", name);
    return 0;
}

Compilation:

gcc -o ex1  ex1.c

The result is:

Name of program: ex1
  1. When I looked at the code, it is in perl.c:
...
        /* if find_script() returns, it returns a malloc()-ed value */
        scriptname = PL_origfilename = find_script(scriptname, dosearch, NULL, 1);
        s = scriptname + strlen(scriptname);

        if (strBEGINs(scriptname, "/dev/fd/")
            && isDIGIT(scriptname[8])
            && grok_atoUV(scriptname + 8, &uv, &s)
            && uv <= PERL_INT_MAX
        ) {
            fdscript = (int)uv;
            if (*s) {
                /* PSz 18 Feb 04
                 * Tell apart "normal" usage of fdscript, e.g.
                 * with bash on FreeBSD:
                 *   perl <( echo '#!perl -DA'; echo 'print "$0\n"')
                 * from usage in suidperl.
                 * Does any "normal" usage leave garbage after the number???
                 * Is it a mistake to use a similar /dev/fd/ construct for
                 * suidperl?
                 */
                *suidscript = TRUE;
                /* PSz 20 Feb 04
                 * Be supersafe and do some sanity-checks.
                 * Still, can we be sure we got the right thing?
                 */
                if (*s != '/') {
                    Perl_croak(aTHX_ "Wrong syntax (suid) fd script name \"%s\"\n", s);
                }
                if (! *(s+1)) {
                    Perl_croak(aTHX_ "Missing (suid) fd script name\n");
                }
                scriptname = savepv(s + 1);
                Safefree(PL_origfilename);
                PL_origfilename = (char *)scriptname;
            }
        }
    }
... 

The condition:

if (strBEGINs(scriptname, "/dev/fd/")
            && isDIGIT(scriptname[8])
            && grok_atoUV(scriptname + 8, &uv, &s)
            && uv <= PERL_INT_MAX
        ) {

hit the code, but there is no implementation for this situation.

  1. I found another situation with similar output
perl <( echo '#!perl -DA'; echo 'print "$0\n"');

The resolution

I tried to implement the resolving of /dev/fd/ to program name via readlink like:

...
                PL_origfilename = (char *)scriptname;
+            } else {
+                char proc_fd_path[64];
+                snprintf(proc_fd_path, sizeof(proc_fd_path), "/proc/self/fd/%d", fdscript);
+                char target_path[PATH_MAX];
+                ssize_t len = readlink(proc_fd_path, target_path, sizeof(target_path) - 1);
+                if (len != -1) {
+                    target_path[len] = '\0';
+                    PL_origfilename = savepv(target_path);
+                }
            }
...

The result in 3) is /usr/local/bin/script.pl
The result in 4) is pipe:number

Questions

What do you think about this?

@michal-josef-spacek
Copy link
Author

There is another possible solution in the prctl implementation for get of $PROGRAM_NAME.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant