• Bug#1104174: pam-auth-update: Parses profile incorrectly resulting in a

    From pyllyukko@21:1/5 to All on Sat Apr 26 18:40:01 2025
    This is a multi-part MIME message sent by reportbug.


    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;
    }

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)