1#!/usr/bin/perl 2# 3# Written 2008 by J Kunkel [jkunkel@aplusg.de] for the OpenXPKI project 4# (C) Copyright 2008 by The OpenXPKI Project 5# 6# Licensed under the Apache License, Version 2.0 (the "License"); 7# you may not use this file except in compliance with the License. 8# You may obtain a copy of the License at 9# 10# http://www.apache.org/licenses/LICENSE-2.0 11# 12# Unless required by applicable law or agreed to in writing, software 13# distributed under the License is distributed on an "AS IS" BASIS, 14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15# See the License for the specific language governing permissions and 16# limitations under the License. 17# 18# test: 19# ===== 20# export LOGIN=jkunkel && export PASSWD=topsecret \ 21# && ./auth_MS-ADS_over_LDAP-SASL.pl 22# 23# 24# use this handler in auth.xml: 25# ============================= 26# <handler name="MS-ADS LDAP-SASL" type="External"> 27# <description> 28# Login with your Microsoft ADS User-Account. 29# </description> 30# <command>auth_MS-ADS_over_LDAP-SASL.pl</command> 31# <role></role> 32# <pattern>x</pattern> 33# <replacement>x</replacement> 34# <env> 35# <name>LOGIN</name> 36# <value>__USER__</value> 37# </env> 38# <env> 39# <name>PASSWD</name> 40# <value>__PASSWD__</value> 41# </env> 42# </handler> 43# 44use warnings; 45use strict; 46use Net::LDAP; 47use Authen::SASL; 48use Carp; 49use URI; 50use Getopt::Std; 51 52######################### 53# configuration 54######################### 55# set to 1 56my $DEBUG = 0; 57 58my $HOST = 'ag-dom-003'; 59my $BASEDN = "dc=a-g,dc=de"; 60 61my $USER = $ENV{'LOGIN'}; 62my $PASSWD = $ENV{'PASSWD'}; 63 64print "==>" . $USER . "\n" if $DEBUG; 65print "==>" . $PASSWD . "\n" if $DEBUG; 66 67# AD groups how represent the OpenXPKI roles 68my $GROUP_CA = "OpenXPKI_CA"; 69my $GROUP_RA = "OpenXPKI_RA"; 70my $GROUP_USER = "OpenXPKI_USER"; 71 72 73my @attrs = ["dn"]; 74my $ldap = 0; 75my $count = 0; 76 77######################### 78#LDAP connect over SASL 79######################### 80until($ldap = Net::LDAP->new($HOST)) { 81 die "Can not connect to ldap://$HOST/" if ++$count > 10; 82 sleep 1; 83} 84 85my $sasl = Authen::SASL->new( 86 mechanism => 'DIGEST-MD5', 87 callback => { 88 user => $USER, 89 pass => $PASSWD 90 } 91); 92 93my $mesg = $ldap->bind($BASEDN, sasl => $sasl, version => 3); 94exit $mesg->code if $mesg->code; 95 96######################### 97#get group DN's 98######################### 99my $group_ca_dn = &getGroupDN($GROUP_CA); 100my $group_ra_dn = &getGroupDN($GROUP_RA); 101my $group_user_dn = &getGroupDN($GROUP_USER); 102 103######################### 104#get user DN 105######################### 106my $userDN = &getUserDN($USER); 107 108print "==>" . $group_user_dn . "\n" if $DEBUG; 109print "==>" . $userDN . "\n" if $DEBUG; 110 111######################### 112#check IsMember 113#print OpenXPKI role 114######################### 115 116&setRoleAndExit("CA Operator") if &getIsMember($group_ca_dn, $userDN); 117&setRoleAndExit("RA Operator") if &getIsMember($group_ra_dn, $userDN); 118&setRoleAndExit("User") if &getIsMember($group_user_dn, $userDN); 119 120exit 1; # if not member of a group 121 122######################### 123#functions 124######################### 125 126sub setRoleAndExit 127{ 128 my ($role) = @_; 129 print $role; 130 $ldap->unbind(); 131 exit 0; 132} 133 134 135 136# get DN of given group 137sub getGroupDN 138{ 139 my ($group) = @_; 140 #get original group DN 141 $mesg = $ldap->search( 142 base => $BASEDN, 143 filter => "(&(cn=$group)(objectclass=group))", 144 attrs => @attrs 145 ); 146 my $entry = $mesg->pop_entry(); 147 print "==> group is ",$entry->dn(),"\n" if $DEBUG; 148 return $entry->dn(); 149} 150 151# get DN of given user 152sub getUserDN 153{ 154 my ($user) = @_; 155 $mesg = $ldap->search( 156 base => $BASEDN, 157 filter => "samaccountname=$user", 158 attrs => @attrs 159 ); 160 my $entry = $mesg->pop_entry(); 161 print "==> user is ",$entry->dn(),"\n" if $DEBUG; 162 return $entry->dn(); 163} 164 165#function userdn is member of groudn 166sub getIsMember 167{ 168 my ($groupDN,$userDN) = @_; 169 my $return = 0; 170 171 print "==> in getIsMember:$groupDN\n" if $DEBUG; 172 #if user is a member then return true 173 my $mesg = $ldap->compare( 174 $groupDN, 175 attr => "member", 176 value => $userDN 177 ); 178 179 180 #0x06 == LDAP_COMPARE_TRUE 181 if ($mesg->code() == 0x06) { 182 return 1; 183 } 184 185 #is also a group and perhaps a member of that group 186 my @groupattrs = ["member","objectclass","memberurl"]; 187 eval{ 188 $mesg = $ldap->search( 189 base => $groupDN, 190 filter => "(|(objectclass=group)(objectclass=groupOfUrls))", 191 attrs => @groupattrs 192 ); 193 my $entry = $mesg->pop_entry(); 194 #check is a member then return true 195 my $urlvalues = $entry->get_value("memberurl", asref => 1); 196 foreach my $urlval (@{$urlvalues}) 197 { 198 my $uri = new URI ($urlval); 199 my $filter = $uri->filter(); 200 my @attrs = $uri->attributes(); 201 eval { 202 $mesg = $ldap->search( 203 base => $userDN, 204 scope => "base", 205 filter => $filter, 206 attrs => \@attrs 207 ); 208 #if we find an entry it returns true 209 #else keep searching 210 $entry = $mesg->pop_entry(); 211 print "ldapurl",$entry->dn,"\n" if $DEBUG; 212 if ($entry->dn) 213 { 214 $return = 1 ; 215 return 1; 216 } 217 }; 218 } #end foreach 219 220 my $membervalues = $entry->get_value("member", asref => 1); 221 foreach my $val (@{$membervalues}) 222 { 223 #stop as soon as we have a match 224 if (&getIsMember($val,$userDN)) 225 { 226 $return=1; 227 return 1; 228 } 229 } 230 }; 231 die $mesg->error if $mesg->code; 232 #if make it this far then you must be a member 233 234 # retrun 0 if a fault 235 if ($@) 236 { 237 return 0; 238 } 239 else 240 { 241 return $return; 242 } 243 244} 245 246