summaryrefslogtreecommitdiff
path: root/debian/local
diff options
context:
space:
mode:
authorSteve Langasek <vorlon@debian.org>2008-08-19 13:06:36 -0700
committerSteve Langasek <steve.langasek@ubuntu.com>2019-01-03 17:28:23 -0800
commitd91f63093adeb151457e464602c7fe8c5a31ccdd (patch)
tree1d4e49a4b5218c59447c81a2066cbdf6817608cd /debian/local
parent3b5828bda8784d0663bcb204b5a4555b1f39b558 (diff)
new diff_profiles function, which spits out information about any local mods
to the autogenerated config
Diffstat (limited to 'debian/local')
-rwxr-xr-xdebian/local/pam-auth-update168
1 files changed, 167 insertions, 1 deletions
diff --git a/debian/local/pam-auth-update b/debian/local/pam-auth-update
index ddb6cfbd..6c80ea04 100755
--- a/debian/local/pam-auth-update
+++ b/debian/local/pam-auth-update
@@ -33,6 +33,7 @@ my $capb=capb('backup');
my $inputdir = '/usr/share/pam-configs';
my $template = 'libpam-runtime/profiles';
my $errtemplate = 'libpam-runtime/conflicts';
+my $confdir = '/etc/pam.d';
my (%profiles, @sorted, @enabled, @conflicts);
opendir(DIR, $inputdir) || die "could not open config directory: $!";
@@ -53,11 +54,45 @@ subst($template, 'profiles',
join(', ', map { $profiles{$_}->{'Name'} } @sorted));
# this needs to be replaced by proper detection of any profiles that are
-# already enabled.
+# already enabled; i.e., use diff_profiles() to figure out what's
+# currently selected
fset($template,'seen','false');
set($template,
join(', ', grep { $profiles{$_}->{'Default'} eq 'yes' } @sorted));
+my $diff = diff_profiles($confdir);
+
+# we need a commandline '--force' arg to specify that /etc/pam.d should be
+# overwritten; used only on upgrades where the postinst has already
+# determined that the checksums match. Module packages other than
+# libpam-runtime itself must NEVER use this option! Document with big
+# skullses and crossboneses! It needs to be exposed for libpam-runtime
+# because that's the package that decides whether we have a pristine config
+# to be converted, and knows whether the version being upgraded from is one
+# for which the conversion should be done.
+
+# if diff_profiles() fails, and we weren't passed a 'force' argument
+# (either because this isn't an upgrade from an old version, or because the
+# checksum didn't match, or because we're being called by some other module
+# package), prompt the user whether to override. If the user declines
+# (which is the default), we never again manage this config unless manually
+# called with '--force'.
+
+# at the end of a successful write, reset the 'seen' flag and the value of
+# the debconf override question.
+
+# FIXME: none of the above comments are implemented!
+
+if (!$diff) {
+ print STDERR <<EOF;
+
+pam-auth-update: Local modifications to /etc/pam.d/common-*, not updating.
+pam-auth-update: Run pam-auth-config --force to override.
+
+EOF
+ exit;
+}
+
do {
@conflicts = ();
input('high',$template);
@@ -88,6 +123,137 @@ do {
set($template, join(', ', @enabled));
} while (@conflicts);
+# @enabled now contains our list of profiles to use for piecing together
+# a config
+# we have:
+# - templates into which we insert the specialness
+# - magic comments denoting the beginning and end of our managed block;
+# looking at only the functional config lines would potentially let us
+# handle more cases, at the expense of much greater complexity, so
+# pass on this at least for the first round
+# - a representation of the autogenerated config stored in /var/lib/pam,
+# that we can diff against in order to account for changed options or
+# manually dropped modules
+# - a hash describing the local modifications the user has made to the
+# config; these are always preserved unless manually overridden with
+# the --force option
+
+write_profiles(\%profiles, \@enabled, $diff);
+
+# merge a set of module declarations into a set of new config files,
+# using the information returned from diff_profiles().
+sub write_profiles
+{
+}
+
+# reconcile the current config in /etc/pam.d with the saved ones in
+# /var/lib/pam; returns a hash of profile names and the corresponding
+# options that should be added/removed relative to the stock config.
+# returns false if any of the markers are missing that permit a merge,
+# or on any other failure.
+sub diff_profiles
+{
+ my ($sourcedir) = @_;
+ my $savedir = '/var/lib/pam';
+ my (%diff);
+
+ # Load the saved config from /var/lib/pam, then iterate through all
+ # lines in the current config that are in the managed block.
+ # If anything fails here, just return immediately since we then
+ # have nothing to merge; instead, the caller will decide later
+ # whether to force an overwrite.
+ for my $type ('auth','account','password','session') {
+ my (@saved,$modname);
+
+ open(SAVED,$savedir . '/' . $type) || return 0;
+ while (<SAVED>) {
+ if (/^Module: (.*)/) {
+ $modname = $1;
+ next;
+ }
+ chomp;
+ # trim out the destination of any jumps; this saves
+ # us from having to re-parse everything just to fix
+ # up the jump lengths, when changes to these will
+ # already show up as inconsistencies elsewhere
+ s/(end|[0-9]+)//;
+ my (@temp) = ($modname,$_);
+ push(@saved,\@temp);
+ }
+ close(SAVED);
+
+ my $state = 0;
+ my (@prev_opts,$curmod);
+
+ open(CURRENT,$sourcedir . '/common-' . $type) || return 0;
+ while (<CURRENT>) {
+ if ($state == 0) {
+ $state = 1
+ if (/^# here are the per-package modules \(the "Primary" block\)/);
+ next;
+ }
+ if ($state == 1) {
+ s/^$type\s+//;
+ if (/^# here's the fallback if no module succeeds/) {
+ $state = 2;
+ next;
+ }
+ }
+ if ($state == 2) {
+ $state = 3
+ if (/^# and here are more per-package modules \(the "Additional" block\)/);
+ next;
+ }
+ if ($state == 3) {
+ last if (/^# end of pam-auth-update config/);
+ s/^$type\s+//;
+ }
+
+ my $found = 0;
+ do {
+ my $line;
+ ($modname,$line) = shift(@saved);
+ $line =~ /^((\[[^]]+\]|\w+)\s+\S+)\s*(.*)/;
+ @prev_opts = split(/\s+/,$3);
+ $curmod = $1;
+ $curmod =~ s/(end|[0-9]+)//;
+ # check if this is a match for the current line
+ if ($_ =~ /^$curmod\s*(.*)$/) {
+ $found = 1;
+ } else {
+ push(@{$diff{$type}{'del'}},$modname);
+ }
+ } while (!$found && $#saved >= 0);
+
+ # there's a line in the live config that doesn't
+ # correspond to anything from the saved config.
+ # treat this as a failure; it's very error-prone
+ # to decide what to do with an added line that
+ # didn't come from a package.
+ return 0 if (!$found);
+
+ for my $opt (split(/\s+/,$1)) {
+ my $found = 0;
+ for (my $i = 0; $i <= $#prev_opts; $i++) {
+ if ($prev_opts[$i] eq $opt) {
+ $found = 1;
+ splice(@prev_opts,$i,0);
+ }
+ }
+ push(@{$diff{$type}{'add'}{$curmod}},$opt) if (!$found);
+ }
+ for my $opt (@prev_opts) {
+ $diff{$type}{'remove'}{$curmod}{$opt} = 1;
+ }
+ }
+ close(CURRENT);
+
+ # we couldn't parse the config, so the merge fails
+ return 0 if ($state < 3);
+ }
+ return \%diff;
+}
+
# simple function to parse a provided config file, in pseudo-RFC822
# format,
sub parse_pam_profile