diff --git a/info.textgrid.middleware.tgauth.rbac/pwdaemon/etc/mapSIDtoPassPhrase.sys b/info.textgrid.middleware.tgauth.rbac/pwdaemon/etc/mapSIDtoPassPhrase.sys new file mode 100644 index 0000000000000000000000000000000000000000..c0a38e17b1f915027af9f00398efa5901d8418ab --- /dev/null +++ b/info.textgrid.middleware.tgauth.rbac/pwdaemon/etc/mapSIDtoPassPhrase.sys @@ -0,0 +1,122 @@ +progname = "mapSIDtoPassPhrase" + +version = 0.1 + +date = "2010-07-13" +<author> + name = "Martin Haase" + org= "DAASI International GmbH" + mail = "martin.haase@daasi.de" +</author> + +<copyright> +text1 = Copyright (c) 2010 DAASI International GmbH +text2 = This library is free software; you can redistribute it and/or \ +modify it under the same terms as Perl itself. +</copyright> + +progshortdescr = "Provides lookup to PKCS\#12 passphrases based on a TextGrid SessionID" + +<progdescription> +text01 = This Daemon has 3 Methods: +text02 = getCSR (sid) +text03 = - creates a new Certificate Signing Request together with a +text04 = - secret key which can be looked up via the Session ID (sid) +text05 = - the key will be held in memory +text06 = - returns the CSR +text07 = putCRT (sid, CRT) +text08 = - receives a signed certificate +text09 = - creates a PKCStext =12 key pair from the cert and the in-memory key +text10 = - encrypts the p12 with a random passphrase +text11 = - the random passphrase is held in memory, can be looked up via the sid +text12 = - the p12 is stored in LDAP under the users entry (found using the sid) +text13 = - returns true or false +text14 = getPassphrase (sid) +text15 = - returns the Passphrase to the p12 key pair using the sid. +</progdescription> + +<bugs> +text = Please report bugs to martin.haase@daasi.de +</bugs> + +<additions example> +text1 = "For getting this manpage: " +text2 = " mapSIDtoPassPhrase.pl -h" +</additions> + +<additions requirements> +text1 = "Following modules are required: " +text2 = "*IO::Socket" +text3 = "*POSIX" +text4 = "*IPC::Shareable" +text5 = "*Math::Random" +text6 = "*Time::HiRes" +text7 = "*Data::Dump" +text8 = "*Net::LDAP" +text9 = "*Crypt::OpenSSL::PKCS10" +text10 = "*Crypt::OpenSSL::RSA" +text11 = "*Crypt::OpenSSL::X509" +text12 = "*Crypt::OpenSSL::PKCS12" +text13 = "*File::Temp" +</additions> + +<options loglevel> + key = "l" + must = 0 + description = "Loglevel for controlling logmessages." +# Possible values \ +# are: no, all, debug, private, info, warn, error, fatal. Default: error." + arg = 1 + argtype = "skalar" + values = "no, all, debug, info, warn, error, fatal" + default = "error" +</options> + +<options logfile> + key = "L" + must = 0 + description = "Name of the logfile with absolute or relative path. " + arg = 1 + argtype = "filename_add_subdir_log" + default = "mapSIDtoPassPhrase.log" +</options> + +<options debugmode> + key = "d" + must = 0 + description = "sets debug mode to on" + arg = 0 +</options> + +<options verbose> + key = "v" + must = 0 + description = "Sets verbose mode which makes the program quite chatty" + arg = 0 +</options> + +<options printhelp> + key = "h" + must = 0 + description = "prints out the manpage" + arg = 0 +</options> + + +<options helpfeature> + key = "H" + must = 0 + description = "prints out description of the feature referenced by \ + commandline flag or config file token. " + arg = 1 +</options> + +<options configfile> + key = "c" + must = 0 + description = Name of the user config file with absolute or \ +relative path. " + arg = 1 + argtype = "filename_exist_subdir_etc" + default = "./etc/mapSIDtoPassPhrase.conf" +</options> diff --git a/info.textgrid.middleware.tgauth.rbac/pwdaemon/mapSIDtoPassPhrase b/info.textgrid.middleware.tgauth.rbac/pwdaemon/mapSIDtoPassPhrase new file mode 100755 index 0000000000000000000000000000000000000000..b4e8c0f8c60bdf9e78a1bf6cc6f32ba449e32375 --- /dev/null +++ b/info.textgrid.middleware.tgauth.rbac/pwdaemon/mapSIDtoPassPhrase @@ -0,0 +1,419 @@ +#!/usr/bin/perl -W +################################################### +# This Daemon has 3 Methods: +# getCSR (sid) +# - creates a new Certificate Signing Request together with a +# - secret key which can be looked up via the Session ID (sid) +# - the key will be held in memory +# - returns the CSR +# putCRT (sid, CRT) +# - receives a signed certificate +# - creates a PKCS#12 key pair from the cert and the in-memory key +# - encrypts the p12 with a random passphrase +# - the random passphrase is held in memory, can be looked up via the sid +# - the p12 is stored in LDAP under the user's entry (found using the sid) +# - returns true or false +# getPassphrase (sid) +# - returns the Passphrase to the p12 key pair using the sid. +# +# ... and possibly a credential negotiation function for CRUD to follow +# +# The daemon forks with each request and uses System V IPC to access +# the passphrase hash from each child process +# +# The socket is only accessible by RBAC (i.e. on the same machine) +# using proxy functions. RBAC speaks only HttpS and the separate RBAC function +# getPassphrase uses Client Auth from CRUD +# +# author Martin Haase / DAASI International GmbH / Project SLC-Gap +# inspired by gridshib-ca-demo-portal.cgi and the Perl Cookbook +# +# creation date: 2010-03-22 +# +# 2010-03-23: rapid prototyping stubs for every function +# 2010-03-23: getCRT, +# 2010-03-24: putCRT, +# 2010-05-06: getPassphrase; +# 2010-05-19: debugged/fixed, ported to perl 5.10 +# 2010-07-13: introduce configuration file using DAASIlib::CONF +# 2010-07-15: changed from INET to local UNIX socket +# + +use IO::Socket; +use POSIX qw(:sys_wait_h); +use IPC::Shareable; + +use Math::Random; +use Time::HiRes; + +use warnings "all"; +use Data::Dump qw(dump); + +use Net::LDAP qw(LDAP_SUCCESS LDAP_TIMELIMIT_EXCEEDED LDAP_SIZELIMIT_EXCEEDED + LDAP_NO_SUCH_OBJECT LDAP_ALREADY_EXISTS ); +# this one needs IO::Socket::SSL + +use Crypt::OpenSSL::PKCS10 qw( :const ); +use Crypt::OpenSSL::RSA; +use Crypt::OpenSSL::X509; +use Crypt::OpenSSL::PKCS12; +use File::Temp; + +use DAASIlib::CONF qw (is_debug); +use DAASIlib::Data; +# these two need: IO::Prompt, Log::Log4perl, DBI, DAASIlib::Gettext, Config::General + + +my $data = new DAASIlib::Data; +my ($progname, $progpath, $etcdir, $sysconfig) = $data->getProgramFiles($0); + +my $conf = new DAASIlib::CONF; +$conf->loadConfig($sysconfig, $progpath, $etcdir); + + +my %ldap_config; +foreach $k (keys %{$conf->{data}}) { + if ($k =~ /^ldap_conf_(\S+)/) { + $ldap_config{$1} = $conf->{data}{$k}; + } +} + +my $socketfile = $conf->{data}{socketfile}; +my $RBACuser = $conf->{data}{rbacuser}; +my $passphraselength = $conf->{data}{passphraselength}; + + +# dead child process reaper +sub REAPER { + 1 until (-1 == waitpid(-1, WNOHANG)); + $SIG{CHLD} = \&REAPER; +} +$SIG{CHLD} = \&REAPER; + +# child sigint definition +$SIG{INT} = sub { die "$$ dying...\n" }; + +# shared hash in-memory for passphrases +$handle = tie %passphrases, 'IPC::Shareable', undef, {destroy => 1}; + +random_set_seed_from_phrase(Time::HiRes::time); + +unlink $socketfile; +$server = IO::Socket::UNIX->new(Local => $socketfile, + Type => SOCK_STREAM, + Reuse => 1, + Blocking => 1, + Listen => SOMAXCONN ) + or die "Cannot be a UNIX Socket Server with file $socketfile: $@\n"; + +use vars qw($login $pass); +($login,$pass,$uid,$gid) = getpwnam($RBACuser) or die "$RBACuser not in passwd file"; +chown $uid, $gid, $socketfile; + +while (1) { + $client = $server->accept(); + + next unless defined( $client); + + next if $pid = fork; # Parent macht weiter im while + + die "fork: $!" unless defined $pid; # das kommt nicht vor + + # ab hier kindprozess + close $server; # server nicht mehr gebraucht im Kindprozess + doJob ($client); + exit; # das Kind stirbt und macht keine neue accept-Prüfung +} continue { + + close $client if defined( $client );; # im Elternprozess nicht mehr gebraucht + +} + + +sub doJob { + print scalar ( localtime()) . " doing Job ...\n"; + my $client = shift; + + my $routine = <$client>; + chomp $routine; + print "Routine is $routine ...\n"; + + if ($routine eq "getCSR") { + my $sid = <$client>; + chomp $sid; + my ($csr, $key) = &getCSR(); + + $handle->shlock(); + $passphrases{$sid} = $key; # store temporarily the RSA key here, will be replaced by a $passphraselength long pkcs12 passphrase when putCRT is called + $handle->shunlock(); + + print $client "getCSRresult\n"; + print $client $csr; + + } elsif ($routine eq "putCRT") { + my $sid = <$client>; + chomp $sid; + + my $crt = ""; + while ($next = <$client>) { + last if $next =~ />>>EOF<<</; + $crt .= $next; + } + + $result = &putCRT ( $sid, $crt); + + print $client "putCRTresult\n"; + print $client $result; + + } elsif ($routine eq "getPassphrase") { + my $sid = <$client>; + chomp $sid; + + my $passphrase = &getPassphrase($sid); + + print $client "getPassphraseresult\n"; + print $client $passphrase; + + } else { + die "Unknown command: $routine"; + } +# print "returned $routine result to socket client\n...\n"; + print $client "\n".'>>>EOF<<<'."\n"; + # jetzt sagt der server nix mehr (s. exit im while) +} + +######################################################## +# specific functions +################ +sub getCSR { +# print "getCSR doing something...\n"; + # create dummy CSR with key + + my $req = Crypt::OpenSSL::PKCS10->new(); + + # Use dummy DN, GridShib-CA will override with correct value + $req->set_subject("/C=US/O=Dummy/CN=Dummy"); + $req->sign(); + + return ($req->get_pem_req(), $req->get_pem_pk()); +} + + +sub putCRT { +# print "putCRT doing something...\n"; + my ($sid, $crt) = @_; + my $key = $passphrases{$sid}; + + # check whether key and crt match + if ( !defined $key || !matchKeys($key,$crt)) { return "false";} + + # create random passphrase for p12 + my $randompassphrase = createRandomPhrase(); + + # create p12 from key and crt + my $p12 = buildP12 ($crt, $key, $randompassphrase); + + # send p12 to LDAP, first looking up which user entry the $sid belongs to + my $transferToLDAPsuccess = sendPKCS12toLDAP ( $sid, $p12, $crt, \%ldap_config ); + if (!$transferToLDAPsuccess) { return "false"; } + + # finally, given all other operations succeeded, + # register passphrase now for daemon's lifetime, replacing the key + $handle->shlock(); + $passphrases{$sid} = $randompassphrase; + $handle->shunlock(); + + return "true"; +} + +sub getPassphrase { +# print "getPassphrase doing something...\n"; + my $sid = shift; + + if (length $passphrases{$sid} > $passphraselength) { + die "Certificate for SID $sid not yet signed!"; + } + return $passphrases{$sid}; + +} + +######################################################################### +# helper subs +############# +sub createRandomPhrase { + $ps =""; + while(length ($ps) < $passphraselength ) { + $r = chr ( random_uniform_integer (1, 48, 122 )); + $ps .= $r if ($r =~ m/[a-zA-Z0-9]/); + } + return $ps; +} + +sub matchKeys { + my ($key_string, $crt_string) = @_; + + if (length ($key_string) <= $passphraselength) { + # is already passphrase set and no RSA key anymore + return 0; + } + + my $key = Crypt::OpenSSL::RSA->new_private_key($key_string); + my $public_key_string = $key->get_public_key_string; + + my $crt = Crypt::OpenSSL::X509->new_from_string ( $crt_string ); + my $public_crt_string = $crt->pubkey(); + + return ( $public_key_string eq $public_crt_string); + +} + +sub buildP12 { + my ($crt, $key, $pass) = @_; + + my ($fh, $filename) = File::Temp::tempfile(UNLINK => 0, + DIR => "/tmp"); +# close $fh; + + $p12obj = Crypt::OpenSSL::PKCS12->new(); + $p12obj->create( $crt, $key, $pass, $filename ); + + open (P12, "<$filename"); + read P12, $p12contents, 99999; + close P12; + + unlink ( $filename ); + + return $p12contents; +} + + + +sub sendPKCS12toLDAP { + my ($sid,$pkcs12,$crt_string,$rh_ldap_config) = @_; + + my $gridX509subjectDn = Crypt::OpenSSL::X509->new_from_string ( $crt_string )->subject(); + # make the Grid format: ", " -> "/" + $gridX509subjectDn =~ s(^)[/]; + $gridX509subjectDn =~ s(, )[/]g; + + my $ldap = ldapConnect($rh_ldap_config); + if (not defined $ldap) { + return 0; + } + + + ### first search for the session entry + my $filter = "(rbacname=$sid)"; + + my $rbacbase = $rh_ldap_config->{basedn}; + $rh_ldap_config->{basedn} = "ou=sessions," . $rbacbase; + + my $mesg = ldapSearch($ldap, $filter, $rh_ldap_config); + + my $entry; + my $eppn; + + if ( $mesg) { + $entry = $mesg->pop_entry(); + if (!defined ($entry)) { + ### Kein Eintrag gefunden + return 0; + } + $eppn = $entry->get_value( 'rbacSessionUser' ); + } else { + ### Kein Eintrag gefunden + return 0; + } + + ### then search for the ePPN and put the p12 there + $filter = "(uid=$eppn)"; + + $rh_ldap_config->{basedn} = "ou=people," . $rbacbase; + + $mesg = ldapSearch($ldap, $filter, $rh_ldap_config); + + if ( $mesg) { + $entry = $mesg->pop_entry(); + if (!defined ($entry)) { + ### Kein Eintrag gefunden + return 0; + } + $entry->changetype( "modify" ); + + $entry->replace( "userpkcs12" => $pkcs12 ); + + if (! grep( /^gridCertificateUser$/, $entry->get_value('objectclass') ) ) { + $entry->add( "objectclass" => "gridCertificateUser" ); + } + + $entry->replace( "gridX509subject" => $gridX509subjectDn ); + + my $updatemesg = $entry->update($ldap); + if ( $updatemesg->code != Net::LDAP::LDAP_SUCCESS ) { + ## Fehler; + return 0; + } + } else { + ### Kein Eintrag gefunden + return 0; + } + return 1; +} + +sub ldapConnect { + my ($rh_ldapdef) = @_; + + my $uri = 'ldap://'.$rh_ldapdef->{'host'}.':'.$rh_ldapdef->{'port'}; + + my $ldap = Net::LDAP->new( $uri); + + + if ( !$ldap ) { + return(undef); + } + + if ( $rh_ldapdef->{is_tls} ) { + my $tlsmesg = $ldap->start_tls ( verify => $rh_ldapdef->{tls_verify}, + cafile => $rh_ldapdef->{tls_cafile}, + ciphers => $rh_ldapdef->{tls_cypher}); + + if ( $tlsmesg->code != Net::LDAP::LDAP_SUCCESS ) { + # we can do without tls, the server is here. + # return undef; + } + } + + my $mesg = $ldap->bind( $rh_ldapdef->{binddn}, + password => $rh_ldapdef->{bindpw} ); + + unless ( $mesg->code == Net::LDAP::LDAP_SUCCESS ) { + return undef; + } + + return $ldap; +} + + + +sub ldapSearch { + my ( $ldap, $filter, $rh_ldapdef ) = @_; + + if ( ! $$rh_ldapdef{filter}) { + $$rh_ldapdef{filter}="(objectclass=*)"; + } + + my $mesg = $ldap->search( + base => $$rh_ldapdef{basedn}, + scope => $$rh_ldapdef{scope}, + filter => $filter, + ); + + if ( $mesg->code == Net::LDAP::LDAP_SUCCESS ) { + + } else { + return undef; + } + + return ($mesg); +} + diff --git a/info.textgrid.middleware.tgauth.rbac/rbacSoap/examples/xacmlCheckAccess.php b/info.textgrid.middleware.tgauth.rbac/rbacSoap/examples/xacmlCheckAccess.php index f10dd6fffb5ee44c2b959bb01aeb56ddbd007da1..1fdf203842f9ea0f5daf28dc28c4ba2a2a6cbe88 100755 --- a/info.textgrid.middleware.tgauth.rbac/rbacSoap/examples/xacmlCheckAccess.php +++ b/info.textgrid.middleware.tgauth.rbac/rbacSoap/examples/xacmlCheckAccess.php @@ -14,7 +14,7 @@ require_once( "../xacmlTypes.inc.php" ); // ----------------------------------------------------- // You'll need these services // ----------------------------------------------------- -$soapXACML = new SoapClient( "http://rbac.textgrid.daasi.de/wsdl/xacml.wsdl.local", Array( "trace" => 1 ) ); +$soapXACML = new SoapClient( "../wsdl/xacml.wsdl", Array( "trace" => 1 ) ); echo "<BODY><HTML>"; diff --git a/info.textgrid.middleware.tgauth.rbac/rbacSoap/examples/xacmlGridCheckAccess.php b/info.textgrid.middleware.tgauth.rbac/rbacSoap/examples/xacmlGridCheckAccess.php new file mode 100755 index 0000000000000000000000000000000000000000..0a955525fd61409c764f0982e2029a7f6af37378 --- /dev/null +++ b/info.textgrid.middleware.tgauth.rbac/rbacSoap/examples/xacmlGridCheckAccess.php @@ -0,0 +1,105 @@ +<?php +// ####################################################### +// Author: Markus Widmer +// Creation date: 18.07.2007 +// Modification date: 18.07.2007 +// Version: 0.1.0 +// ####################################################### + + +require_once( "../xacmlTypes.inc.php" ); + + + +// ----------------------------------------------------- +// You'll need these services +// ----------------------------------------------------- +$soapXACML = new SoapClient( "../wsdl/xacmlGrid.wsdl", Array( "trace" => 1 ) ); + + +echo "<BODY><HTML>"; + + + + +if( isset( $_POST['session'] ) ) { + + // ----------------------------------------------------- + // If this was successfull you can add a the user you + // wish to create + // ----------------------------------------------------- + + $regReq = new stdClass(); + + $regReq->Version = "2.0"; + $regReq->ID = "abcde1234"; + $regReq->ReturnContext = true; + $regReq->Request = new stdClass(); + $regReq->Request->Subject = new stdClass(); + $regReq->Request->Resource = new stdClass(); + $regReq->Request->Action = new stdClass(); + $regReq->Request->Environment = new stdClass(); + + $regReq->Request->Subject->Attribute = new stdClass(); + $regReq->Request->Subject->Attribute->AttributeId = "urn:oasis:names:tc:xacml:1.0:subject:subject-id"; + $regReq->Request->Subject->Attribute->DataType = "http://www.w3.org/2001/XMLSchema#string"; + $regReq->Request->Subject->Attribute->AttributeValue = new stdClass(); + $regReq->Request->Subject->Attribute->AttributeValue->any = $_POST['session']; + + $regReq->Request->Resource->Attribute = new stdClass(); + $regReq->Request->Resource->Attribute->AttributeId = "urn:oasis:names:tc:xacml:1.0:resource:resource-id"; + $regReq->Request->Resource->Attribute->DataType = "http://www.w3.org/2001/XMLSchema#anyURI"; + $regReq->Request->Resource->Attribute->AttributeValue = new stdClass(); + $regReq->Request->Resource->Attribute->AttributeValue->any = $_POST['resource']; + + $regReq->Request->Action->Attribute = new stdClass(); + $regReq->Request->Action->Attribute->AttributeId = "urn:oasis:names:tc:xacml:1.0:action:action-id"; + $regReq->Request->Action->Attribute->DataType = "http://www.w3.org/2001/XMLSchema#string"; + $regReq->Request->Action->Attribute->AttributeValue = new stdClass(); + $regReq->Request->Action->Attribute->AttributeValue->any = $_POST['operation']; + + + echo "<HR/>"; + echo "checking access...<BR/>"; + echo "Look at the code to see what happens!<BR/>"; + + + try { + + $caResponse = $soapXACML->checkXACMLaccess( $regReq ); + + echo "\n\n" . $soapXACML->__getLastRequest() . "<br><br>\n\n"; + echo "\n\n" . $soapXACML->__getLastResponse() . "<br><br>\n\n"; + + if( preg_match( "/^permit$/i", $caResponse->Response->Result->Decision ) ) { + + echo "<BR><HR><BR>Granted: YES.<BR><HR><BR>"; + + } + else { + + echo "<BR><HR><BR>Granted: NO.<BR><HR><BR>"; + + } + + } + catch( SoapFault $f ) { + + echo "\n\n" . $soapXACML->__getLastRequest(); + echo "\n\n" . $soapXACML->__getLastResponse() . "\n\n"; + echo "SOAP FAULT!: " . $f->faultcode . " / " . $f->faultstring . " / " . $f->detail; + + } + +} + + +echo "<FORM action=\"xacmlGridCheckAccess.php\" method=\"post\" enctype=\"multipart/form-data\">\n"; +echo "Subject-DN: <INPUT type=\"text\" name=\"session\" value=\"\"><BR>\n"; +echo "Resource: <INPUT type=\"text\" name=\"resource\" value=\"\"><BR>\n"; +echo "Operation: <INPUT type=\"text\" name=\"operation\" value=\"\"><BR>\n"; +echo "<INPUT type=\"submit\" value=\"Commit...\">\n"; +echo "</FORM>\n"; + +echo "</BODY></HTML>"; +?> diff --git a/info.textgrid.middleware.tgauth.rbac/rbacSoap/soapTypes.inc.php b/info.textgrid.middleware.tgauth.rbac/rbacSoap/soapTypes.inc.php index c13bed00f1287851a57cb1325882f91f918315e0..9b5c887b9b25cd4edd565bfa8e4fd7cf577b509a 100755 --- a/info.textgrid.middleware.tgauth.rbac/rbacSoap/soapTypes.inc.php +++ b/info.textgrid.middleware.tgauth.rbac/rbacSoap/soapTypes.inc.php @@ -92,6 +92,21 @@ class getOwnerResponse { } + +class getUUIDRequest { + + public $auth; + public $log; + public $resource; + +} + +class getUUIDResponse { + + public $uuid; + +} + class getMembersRequest { public $auth; @@ -713,4 +728,21 @@ class checkXACMLaccessRequest { public $request; } + +class getSLCRequest { + + public $auth; + public $log; + public $secret; + +} + +class getSLCResponse { + + public $slc; + +} + + + ?>