Skip to content

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

Open
@michal-josef-spacek

Description

@michal-josef-spacek

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?

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions