#!/usr/bin/perl -w # Add, remove, or test a pg_hba.conf entry. # # (C) 2005-2009 Martin Pitt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. use strict; use PgCommon; use Getopt::Long; use Net::CIDR; # global variables my $ip = ''; # default to local unix socket my $force_ssl = 0; my ($method, $ver_cluster, $db, $user); my $mode; my @hba; # Print an error message to stderr and exit with status 2 sub error2 { print STDERR 'Error: ', $_[0], "\n"; exit 2; } # Check if s1 is equal to s2 or s2 is 'all'. # Arguments: sub match_all { return ($_[1] eq 'all' || $_[0] eq $_[1]); } # Check if given IP matches the specification in the HBA record. # Arguments: sub match_ip { my ($ip, $hba) = @_; # Don't try to mix IPv4 and IPv6 addresses since that will make cidrlookup # croak return 0 if ((index $ip, ':') < 0) ^ ((index $$hba{'ip'}, ':') < 0); return Net::CIDR::cidrlookup ($ip, $$hba{'ip'}); } # Check if arguments match any line # Return: 1 if match was found, 0 otherwise. sub mode_test { foreach my $hbarec (@hba) { if (!defined($$hbarec{'type'})) { next; } next if $$hbarec{'type'} eq 'comment'; next unless match_all ($user, $$hbarec{'user'}) && match_all ($db, $$hbarec{'db'}) && $$hbarec{'method'} eq $method; if ($ip) { return 1 if (($force_ssl && $$hbarec{'type'} eq 'hostssl') || $$hbarec{'type'} =~ /^host/) && match_ip ($ip, $hbarec); } else { return 1 if $$hbarec{'type'} eq 'local'; } } return 0; } # Print hba conf. sub mode_print { foreach my $hbarec (@hba) { print "$$hbarec{'line'}\n"; } } # Generate a pg_hba.conf line that matches the command line args. sub create_hba_line { if ($ip) { return sprintf "%-7s %-11s %-11s %-35s %s\n", $force_ssl ? 'hostssl' : 'host', $db, $user, $ip, $method; } else { return sprintf "%-7s %-11s %-47s %s\n", 'local', $db, $user, $method; } } # parse arguments my $ip_arg; exit 3 unless GetOptions ( 'cluster=s' => \$ver_cluster, 'ip=s' => \$ip_arg, 'method=s' => \$method, 'force-ssl' => \$force_ssl ); if ($#ARGV != 2) { print STDERR "Usage: $0 mode [options] \n"; exit 2; } ($mode, $db, $user) = @ARGV; error2 '--cluster must be specified' unless $ver_cluster; my ($version, $cluster) = split ('/', $ver_cluster); error2 'No version specified with --cluster' unless $version; error2 'No cluster specified with --cluster' unless $cluster; error2 'Cluster does not exist' unless cluster_exists $version, $cluster; if (defined $ip_arg) { $ip = Net::CIDR::cidrvalidate $ip_arg; error2 'Invalid --ip argument' unless defined $ip; } unless (defined $method) { $method = ($ip ? 'md5' : 'ident sameuser'); } error2 'Invalid --method argument' unless PgCommon::valid_hba_method($method); # parse file my $hbafile = "/etc/postgresql/$version/$cluster/pg_hba.conf"; @hba = read_pg_hba $hbafile; error2 "Could not read $hbafile" unless $#hba; if ($mode eq 'pg_test_hba') { if (mode_test) { exit 0; } else { print create_hba_line(); exit 1; } } elsif ($mode eq 'pg_print_hba') { mode_print(); } else { error2 "Unknown mode: $mode"; }