--- /dev/null
+#!@PERL@ -w
+
+use strict;
+use Pod::Usage;
+use Getopt::Long;
+use File::Basename;
+use Date::Format;
+
+=pod
+
+=head1 NAME
+
+ ext_kerberos_sid_group_acl - external ACL helper for Squid to verify AD Domain group membership using sid.
+
+=head1 SYNOPSIS
+
+ ext_kerberos_sid_group_acl [-d] [-h] -p Principal Name -D Domain Controller -b Base DN -G Group1:Group2
+
+=head1 DESCRIPTION
+
+B<ext_kerberos_sid_group_acl> is an installed executable script.
+It uses B<ldapsearch> from Openldap to lookup the name of a AD group sid.
+
+This helper must be used in with the negotiate_kerberos_auth helper in a
+Microsft AD or Samba environement.
+
+It reads from the standard input the domain username and a list of group sids
+and tries to match the group SIDs to the AD group sids.
+
+=head1 OPTIONS
+
+=over 12
+
+=item B<-d>
+
+Write debug info to stderr.
+
+=item B<-h>
+
+Print the help.
+
+=item B<-p principal name>
+
+Principal name in squid keytab to use for ldap authentication to AD
+
+=item B<-D domain controller>
+
+Domain controller to contact to lookup group SID
+
+=item B<-b base DN>
+
+Base DN for ldap search
+
+=item B<-G AD group name>
+
+AD group name to be used for SID lookup. List separated by a colon (:)
+
+=back
+
+=head1 CONFIGURATION
+
+ auth_param negotiate program /path/to/negotiate_wrapper_auth -d \
+ --ntlm /path/to/ntlm_auth --helper-protocol=squid-2.5-ntlmssp --domain example.com \
+ --kerberos /path/to/negotiate_kerberos_auth -d -s GSS_C_NO_NAME -k /path/to/squid.keytab -t none
+ external_acl_type sid_check %LOGIN %note{group} /path/to/kerberos_sid_group_acl -p principal -D dc1.example.com -b "DC=example,DC=com" -G Group1:Group2
+ acl squid_allow external sid_check
+ acl allowed_group external sid_check
+ http_access allow allowed_group
+
+If the local perl interpreter is in a unusual location it may need to be added:
+
+ external_acl_type sid_check %LOGIN %note{group} /path/to/perl /path/to/kerberos_sid_group_acl -p principal -D dc1.example.com -b "DC=example,DC=com" -G Group1:Group2
+
+=head1 AUTHOR
+
+This program was written by Markus Moeller <markus_moeller@compuserve.com>
+
+This manual was written by Markus Moeller <markus_moeller@compuserve.com>
+
+=head1 COPYRIGHT
+
+ * Copyright (C) 1996-2018 The Squid Software Foundation and contributors
+ *
+ * Squid software is distributed under GPLv2+ license and includes
+ * contributions from numerous individuals and organizations.
+ * Please see the COPYING and CONTRIBUTORS files for details.
+
+ This program is put in the public domain by Markus Moeller
+ <markus_moeller@compuserve.com>. It 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.
+
+=head1 QUESTIONS
+
+Questions on the usage of this program can be sent to the I<Squid Users mailing list <squid-users@lists.squid-cache.org>>
+
+=head1 REPORTING BUGS
+
+Bug reports need to be made in English.
+See http://wiki.squid-cache.org/SquidFaq/BugReporting for details of what you need to include with your bug report.
+
+Report bugs or bug fixes using http://bugs.squid-cache.org/
+
+Report serious security bugs to I<Squid Bugs <squid-bugs@lists.squid-cache.org>>
+
+Report ideas for new improvements to the I<Squid Developers mailing list <squid-dev@lists.squid-cache.org>>
+
+=head1 SEE ALSO
+
+negotiate_kerberos_auth(8)
+
+The Squid FAQ wiki http://wiki.squid-cache.org/SquidFaq
+
+The Squid Configuration Manual http://www.squid-cache.org/Doc/config/
+
+=cut
+
+#
+# Version history:
+# 2018-06-10 Markus Moeller <markus_moeller@compuserve.com>
+# Initial release
+#
+# Globals
+#
+use vars qw/ %opt /;
+
+my $name = basename($0);
+my $principal;
+my $dc;
+my $basedn;
+my $ccname="/tmp/squid_krb5cc";
+my $groupSIDs;
+my @ADgroupSIDs;
+my $user;
+my @groups;
+my $ans;
+
+# Disable output buffering
+$|=1;
+
+sub debug()
+{
+ my @lt = localtime;
+ print STDERR strftime("%Y/%m/%d %H:%M:%S", @lt)." | $name: @_\n" if $opt{d};
+}
+
+sub info()
+{
+ my @lt = localtime;
+ print STDERR strftime("%Y/%m/%d %H:%M:%S", @lt)." | $name: @_\n";
+}
+
+sub check()
+{
+ if ( grep( /^@_$/, @ADgroupSIDs) ) {
+ &debug("DEBUG: Found @_ in AD group SID");
+ return "OK";
+ } else {
+ &debug("DEBUG: Did not find @_ in AD group SID");
+ return "ERR";
+ }
+}
+
+#
+# Command line options processing
+#
+sub init()
+{
+ use Getopt::Std;
+ my $errmsg;
+ my $opt_string = 'hdD:p:b:G:';
+ getopts( "$opt_string", \%opt ) or usage();
+ Pod::Usage::pod2usage(1) if $opt{h};
+ Pod::Usage::pod2usage(1) if not defined $opt{D};
+ Pod::Usage::pod2usage(1) if not defined $opt{b};
+ Pod::Usage::pod2usage(1) if not defined $opt{p};
+ Pod::Usage::pod2usage(1) if not defined $opt{G};
+
+ $ENV{'KRB5CCNAME'} = $ccname;
+
+ @groups = split(/:/,$opt{G});
+ $errmsg=`kinit -k $opt{p} 2>&1`;
+ &info("ERROR: $errmsg") if $errmsg;
+ exit 99 if $errmsg;
+
+ $errmsg="";
+ foreach my $group (@groups) {
+ open(LDAP, "ldapsearch -LLL -Ygssapi -H ldap://$opt{D}:389 -s sub -b \"$opt{b}\" \"(CN=$group)\" objectsid 2>&1 |");
+ my $sid;
+ while (<LDAP>) {
+ chomp($_);
+ if ( $_ =~ /^object/ && defined $sid ) {
+ &info("ERROR: multiple SIDs returned for group $group");
+ } elsif ( $_ =~ /^object/ ) {
+ $sid=$_;
+ $sid=~s/^[^\s]+\s+//;
+ } else {
+ $errmsg=$errmsg.";".$_;
+ }
+ }
+ close(LDAP);
+ if ( ! defined $sid ) {
+ $errmsg=~s/^;//;
+ &info("ERROR: $errmsg");
+ &info("ERROR: no SID returned for group $group");
+ } else {
+ &info("INFO:ldapsearch result Group=$group, SID=$sid");
+ push @ADgroupSIDs, $sid;
+ }
+ }
+ &info("ERROR: Exit as no sid was found for any group") if ! @ADgroupSIDs;
+ exit 99 if ! @ADgroupSIDs;
+}
+
+init();
+&debug("INFO: Debugging mode ON.");
+
+#
+# Main loop
+#
+while (<STDIN>) {
+ chop;
+ &debug("DEBUG: Got $_ from squid");
+ ($user, $groupSIDs) = split(/\s+/);
+ if ( defined $user && defined $groupSIDs ) {
+ &debug("DEBUG: user=$user");
+ &debug("DEBUG: groups=$groupSIDs");
+ # test for each group squid send in it's request
+ foreach my $group (split(/,/,$groupSIDs)) {
+ $ans = &check($group);
+ last if $ans eq "OK";
+ }
+ &debug("DEBUG: Sending $ans to squid");
+ print "$ans\n";
+ } else {
+ &debug("DEBUG: Sending ERR to squid");
+ print "ERR\n";
+ }
+}
+