Package: libpam-runtime
Version: 1.5.2-6+rpt2+deb12u1
Severity: normal

Dear Maintainer,

pam-auth-update parses a specific profile[1] incorrectly and produces
invalid output into /etc/pam.d/common-account. The problematic input
line is the line 11[2].

Instead of producing a valid line like this:

account [success=5 default=ignore]      pam_succeed_if.so uid eq 0 service in 
chfn:chpasswd:chsh:cron:login quiet

It appends an extra " =" at the end like so:

account [success=5 default=ignore]      pam_succeed_if.so uid eq 0 service in 
chfn:chpasswd:chsh:cron:login quiet =

The problem is reproducable and can be also seen in my GH project
Actions in the "authentication" part. Even though that particular test
runs Ubuntu, but it behaves exactly the same as in Debian. The Action
also archives the /etc/pam.d/common-account file and should be visible
there too.

To reproduce the issue the following steps can be taken:

* Copy uid_ge_1000.new[1] into /usr/share/pam-configs/uid_ge_1000
* Run: sed -i 's/quiet =$/quiet/' /etc/pam.d/common-account; 
pam-auth-update-fix --package --enable uid_ge_1000
* Observe invalid " =" appendix in /etc/pam.d/common-account

I'm not an expert in Perl, but I tried debugging this with Copilot and
it suggested the following fix into the merge_one_line function at the
end before returing:

# Filter %{$adds} to exclude invalid keys and standalone tokens
foreach my $key (keys %{$adds}) {
        if ($key eq '=' || $key eq '' || grep { $_ eq $key } @opts) {
                print STDERR "Removing invalid or duplicate key from Adds: 
$key\n";
                delete $adds->{$key};
        }
}

The full function that Copilot produced is included as an attachment.

That seemed to work, but I'm sure there's a more proper and elegant
solution for this.

[1] 
https://github.com/pyllyukko/harden.yml/blob/master/files/pam-configs/uid_ge_1000.new
[2] 
https://github.com/pyllyukko/harden.yml/blob/11cf63ed7d82d8e6bf7f9c85449cb2ff6de20716/files/pam-configs/uid_ge_1000.new#L11
[3] 
https://github.com/pyllyukko/harden.yml/actions/workflows/ansible-playbook.yml

-- System Information:
Debian Release: 12.10
  APT prefers stable-updates
  APT policy: (500, 'stable-updates'), (500, 'stable-security'), (500, 'stable')
Architecture: arm64 (aarch64)
Foreign Architectures: armhf

Kernel: Linux 6.12.20+rpt-rpi-2712 (SMP w/4 CPU threads; PREEMPT)
Locale: LANG=en_US.UTF-8, LC_CTYPE=en_US.UTF-8 (charmap=UTF-8) (ignored: LC_ALL 
set to en_US.UTF-8), LANGUAGE=en_US.UTF-8
Shell: /bin/sh linked to /usr/bin/dash
Init: systemd (via /run/systemd/system)
LSM: AppArmor: enabled

Versions of packages libpam-runtime depends on:
ii  debconf [debconf-2.0]  1.5.82
ii  libpam-modules         1.5.2-6+rpt2+deb12u1

libpam-runtime recommends no packages.

libpam-runtime suggests no packages.

-- Configuration Files:
/etc/pam.d/other changed [not included]

-- debconf information excluded

-- debsums errors found:
debsums: changed file /usr/sbin/pam-auth-update (from libpam-runtime package)
debsums: changed file /usr/share/pam-configs/unix (from libpam-runtime package)
sub merge_one_line {
    my ($line, $diff, $count) = @_;
    my (@opts, $modline);

    # Parse the line into module and options
    $line =~ /^((\[[^]]+\]|\w+)\s+\S+)\s*(.*)/;
    @opts = split(/\s+/, $3);
    $modline = $1;
    $modline =~ s/end/$count/g;

    my ($adds, $removes);
    if ($diff) {
        my $mod = $modline;
        $mod =~ s/(\[[^0-9]*)[0-9]+(.*\])/$1$2/g;
        $adds = \%{$diff->{'add'}{$mod}};
        $removes = \%{$diff->{'remove'}{$mod}};
    } else {
        $adds = $removes = undef;
    }

    # Remove options marked for removal
    for (my $i = 0; $i <= $#opts; $i++) {
        if ($removes->{$opts[$i]}) {
            splice(@opts, $i, 1);
            $i--;
        }
    }

    # Filter %{$adds} to exclude invalid keys and standalone tokens
    foreach my $key (keys %{$adds}) {
        if ($key eq '=' || $key eq '' || grep { $_ eq $key } @opts) {
            print STDERR "Removing invalid or duplicate key from Adds: $key\n";
            delete $adds->{$key};
        }
    }

    # Debugging output
    use Data::Dumper;
    print STDERR "Processing line: $line\n";
    print STDERR "Module line: $modline\n";
    print STDERR "Original Options: ", Dumper(\@opts);
    print STDERR "Adds After Filtering: ", Dumper($adds);

    # Construct the final line
    my $final_line = $modline . " " . join(' ', @opts, sort keys(%{$adds})) . 
"\n";

    # Debug the final constructed line
    print STDERR "Final Constructed Line: $final_line\n";

    return $final_line;
}

Reply via email to