#!/bin/sh # # Copyright (c) 2019 Kungliga Tekniska Högskolan # (Royal Institute of Technology, Stockholm, Sweden). # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # 3. Neither the name of the Institute nor the names of its contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. top_builddir="@top_builddir@" env_setup="@env_setup@" objdir="@objdir@" testfailed="echo test failed; cat messages.log; exit 1" . ${env_setup} # If there is no useful db support compiled in, disable test ${have_db} || exit 77 umask 077 R=TEST.H5L.SE DCs="DC=test,DC=h5l,DC=se" port=@port@ bx509port=@bx509port@ server=datan.test.h5l.se otherserver=other.test.h5l.se kadmin="${kadmin} -l -r $R" bx509d="${bx509d} --allow-GET --reverse-proxied -p $bx509port -H $server --cert=${objdir}/bx509.pem -t" kdc="${kdc} --addresses=localhost -P $port" cachefile="${objdir}/cache.krb5" cache="FILE:${cachefile}" cachefile2="${objdir}/cache2.krb5" cache2="FILE:${cachefile2}" keyfile="${hx509_data}/key.der" keyfile2="${hx509_data}/key2.der" kt=${objdir}/kt keytab=FILE:${kt} ukt=${objdir}/ukt ukeytab=FILE:${ukt} kinit="${kinit} -c $cache ${afs_no_afslog}" klist2="${klist} --hidden -v -c $cache2" klistjson="${klist} --json -c $cache" klist="${klist} --hidden -v -c $cache" kgetcred="${kgetcred} -c $cache" kdestroy="${kdestroy} -c $cache ${afs_no_unlog}" test_csr_authorizer="$test_csr_authorizer -A $objdir/authz_dir -S $objdir" kx509="${kx509} -c $cache" KRB5_CONFIG="${objdir}/krb5-bx509.conf" export KRB5_CONFIG HEIM_PIDFILE_DIR="${objdir}/" export HEIM_PIDFILE_DIR HEIM_IPC_DIR=$objdir export HEIM_IPC_DIR rsa=yes pkinit=no if ${hxtool} info | grep 'rsa: hx509 null RSA' > /dev/null ; then rsa=no fi if ${hxtool} info | grep 'rand: not available' > /dev/null ; then rsa=no fi if ${kinit} --help 2>&1 | grep "CA certificates" > /dev/null; then pkinit=yes fi # If we doesn't support pkinit and have RSA, give up if test "$pkinit" != yes -o "$rsa" != yes ; then exit 77 fi rm -f current-db* rm -f out-* rm -f mkey.file* rm -f *.pem *.crt *.der rm -rf authz_dir mkdir -p authz_dir > messages.log kdcpid= bx509pid= test_csr_authorizer_pid= trap 'kill -9 ${kdcpid} ${bx509pid} ${test_csr_authorizer_pid}; echo signal killing kdc, bx509d, and test_csr_authorizer; exit 1;' EXIT # csr_grant ext-type value grantee_principal csr_grant() { mkdir -p "${objdir}/authz_dir/${3}" touch "${objdir}/authz_dir/${3}/${1}=${2}" } csr_revoke() { rm -rf "${objdir}/authz_dir" mkdir -p "${objdir}/authz_dir" } # get_cert "" curl-opts # get_cert "&qparams" curl-opts get_cert() { url="http://${server}:${bx509port}/bx509?csr=$csr${1}" shift curl -g --resolve ${server}:${bx509port}:127.0.0.1 \ -H "Authorization: Negotiate $token" \ "$@" "$url" } get_with_token() { if [ -n "$csr" ]; then url="http://${server}:${bx509port}/${1}?csr=$csr${2}" else url="http://${server}:${bx509port}/${1}?${2}" fi shift 2 curl -fg --resolve ${server}:${bx509port}:127.0.0.1 \ -H "Authorization: Negotiate $token" \ -D response-headers \ "$@" "$url" && { echo "GET w/o CSRF token succeeded!"; exit 2; } curl -g --resolve ${server}:${bx509port}:127.0.0.1 \ -H "Authorization: Negotiate $token" \ -D response-headers \ "$@" "$url" grep ^X-CSRF-Token: response-headers >/dev/null || { echo "GET w/o CSRF token did not output a CSRF token!"; exit 2; } curl -fg --resolve ${server}:${bx509port}:127.0.0.1 \ -H "Authorization: Negotiate $token" \ -H "$(sed -e 's/\r//' response-headers | grep ^X-CSRF-Token:)" \ "$@" "$url" || { echo "GET w/ CSRF failed"; exit 2; } } get_via_POST() { endpoint=$1 shift curl -fg --resolve ${server}:${bx509port}:127.0.0.1 \ -H "Authorization: Negotiate $token" \ -X POST -D response-headers \ "$@" "http://${server}:${bx509port}/${endpoint}" && { echo "POST w/o CSRF token succeeded!"; exit 2; } curl -g --resolve ${server}:${bx509port}:127.0.0.1 \ -H "Authorization: Negotiate $token" \ -X POST -D response-headers \ "$@" "http://${server}:${bx509port}/${endpoint}" grep ^X-CSRF-Token: response-headers >/dev/null || { echo "POST w/o CSRF token did not output a CSRF token!"; exit 2; } curl -fg --resolve ${server}:${bx509port}:127.0.0.1 \ -H "Authorization: Negotiate $token" \ -H "$(sed -e 's/\r//' response-headers | grep ^X-CSRF-Token:)" \ -X POST \ "$@" "http://${server}:${bx509port}/${endpoint}" || { echo "POST w/ CSRF failed"; exit 2; } } rm -f $kt $ukt $ktutil -k $keytab add -r -V 1 -e aes128-cts-hmac-sha1-96 \ -p HTTP/datan.test.h5l.se@${R} || { echo "failed to setup kimpersonate credentials"; exit 2; } $ktutil -k $keytab list || { echo "failed to setup kimpersonate credentials"; exit 2; } $kimpersonate --ccache=$cache -k $keytab -R -t aes128-cts-hmac-sha1-96 \ -c foo@${R} -s HTTP/datan.test.h5l.se@${R} || { echo "failed to setup kimpersonate credentials"; exit 2; } $klist || { echo "failed to setup kimpersonate credentials"; exit 2; } echo "Setting up certificates" # We need: # # - a CA certificate for issuing client certificates # - a CA certificate for issuing server certificates # - a CA certificate for issuing mixed certificates # - a certificate for bx509 itself (well, not in reverse proxy mode, but we'll # make one anyways) # Make the realm's user cert issuer CA certificate. # # NOTE WELL: We need all three KeyUsage values listed below! # We also need this to be of type "pkinit-kdc", # which means we'll get an appropriate EKU OID as # well. $hxtool ca --issue-ca --self-signed --type=pkinit-kdc \ --ku=digitalSignature --ku=keyCertSign --ku=cRLSign \ --pk-init-principal=krbtgt/${R}@${R} \ --generate-key=rsa --key-bits=1024 \ --subject="OU=Users,CN=KDC,${DCs}" \ --certificate=PEM-FILE:"${objdir}/user-issuer.pem" || { echo "failed to setup CA certificate"; exit 2; } # We'll use the user cert issuer as the PKINIT anchor, allowing bx509-issued # certificates to be used for PKINIT. Though we won't be testing PKINIT here # -- we test kx509->PKINIT in check-pkinit. cp ${objdir}/user-issuer.pem ${objdir}/pkinit-anchor.pem # Put the cert alone in the trust anchors file ex "${objdir}/pkinit-anchor.pem" <<"EOF" /-----BEGIN CERTIFICATE----- 1,.-1 d wq EOF $hxtool ca --issue-ca --self-signed \ --ku=digitalSignature --ku=keyCertSign --ku=cRLSign \ --generate-key=rsa --key-bits=1024 \ --subject="OU=Servers,CN=KDC,${DCs}" \ --certificate=PEM-FILE:"${objdir}/server-issuer.pem" || { echo "failed to setup CA certificate"; exit 2; } $hxtool ca --issue-ca --self-signed \ --ku=digitalSignature --ku=keyCertSign --ku=cRLSign \ --generate-key=rsa --key-bits=1024 \ --subject="OU=Users,CN=KDC,${DCs}" \ --certificate=PEM-FILE:"${objdir}/mixed-issuer.pem" || { echo "failed to setup CA certificate"; exit 2; } $hxtool ca --issue-ca --type=https-negotiate-server \ --ca-certificate=PEM-FILE:"${objdir}/server-issuer.pem" \ --ku=digitalSignature --pk-init-principal=HTTP/${server}@${R}\ --generate-key=rsa --key-bits=1024 --subject="" \ --certificate=PEM-FILE:"${objdir}/bx509.pem" || { echo "failed to setup CA certificate"; exit 2; } # XXX Before starting bx509d let us use kdc test programs to check that: # # - the negotiate token validator plugin works # - the authz_dir CSR authorizer plugin works # - the KDC CA tester program works echo "Check gss-token and Negotiate token validator plugin" token=$(KRB5CCNAME=$cache $gsstoken HTTP@$server | tr A B) $test_token_validator -a datan.test.h5l.se Negotiate "$token" && { echo "Negotiate token validator accepted invalid token"; exit 2; } token=$(KRB5CCNAME=$cache $gsstoken HTTP@$server) $test_token_validator -a datan.test.h5l.se Negotiate "$token" || { echo "Negotiate token validator failed to validate valid token"; exit 2; } echo "Starting CSR authorizer IPC service" $test_csr_authorizer --server --daemon || { echo "Failed to start test_csr_authorizer service"; exit 2; } test_csr_authorizer_pid=`getpid test_csr_authorizer` # Make a CSR for foo@$R $hxtool request-create --subject='' --generate-key=rsa --key-bits=1024 \ --key=FILE:"${objdir}/k.der" --kerberos=foo@$R \ ${objdir}/req || { echo "Failed to make a CSR"; exit 2; } echo "Test CSR authorizer IPC service (deny foo@$R to san_pkinit=foo@$R)" csr_revoke $test_csr_authorizer PKCS10:${objdir}/req foo@$R && { echo "CSR authorizer IPC service granted foo@$R"; exit 2; } echo "Test CSR authorizer IPC service (grant foo@$R to san_pkinit=foo@$R)" csr_grant san_pkinit foo@$R foo@${R} $test_csr_authorizer PKCS10:${objdir}/req foo@$R || { echo "CSR authorizer IPC service rejected foo@$R"; exit 2; } # Make a CSR for bar@$R $hxtool request-create --subject='' --key-bits=1024 \ --key=FILE:"${objdir}/k.der" --kerberos=bar@$R \ ${objdir}/req || { echo "Failed to make a CSR"; exit 2; } echo "Test CSR authorizer IPC service (deny foo@$R to san_pkinit=bar@$R)" $test_csr_authorizer PKCS10:${objdir}/req foo@$R && { echo "CSR authorizer IPC service accepted foo@$R"; exit 2; } echo "Test CSR authorizer IPC service (grant foo@$R to san_pkinit=bar@$R)" csr_grant san_pkinit foo@$R bar@${R} $test_csr_authorizer PKCS10:${objdir}/req foo@$R && { echo "CSR authorizer IPC service accepted foo@$R"; exit 2; } # Make a CSR for foo@$R and bar@$R $hxtool request-create --subject='' --key-bits=1024 \ --key=FILE:"${objdir}/k.der" \ --kerberos=foo@$R --kerberos=bar@$R \ ${objdir}/req || { echo "Failed to make a CSR"; exit 2; } # Check that the authorizer does mark foo@$R as approved even though it denies # the overall request because it rejects bar@$R echo "Test CSR authorizer IPC service (partial authz)" csr_revoke csr_grant san_pkinit foo@$R foo@${R} # Check that the authorizer grants foo@$R $test_csr_authorizer PKCS10:${objdir}/req foo@$R san_pkinit=foo@$R || { echo "CSR authorizer IPC service partial approval check fail"; exit 2; } # Check that the authorizer rejects bar@$R $test_csr_authorizer PKCS10:${objdir}/req foo@$R san_pkinit=bar@$R && { echo "CSR authorizer IPC service partial approval check fail"; exit 2; } $test_csr_authorizer PKCS10:${objdir}/req foo@$R san_pkinit=foo@$R san_pkinit=bar@$R && { echo "CSR authorizer IPC service partial approval check fail"; exit 2; } echo "Making a plain CSR" $hxtool request-create --subject='' --generate-key=rsa --key-bits=1024 \ --key=FILE:"${objdir}/k.der" "${objdir}/req" || { echo "Failed to make a CSR"; exit 2; } rm -f trivial.pem server.pem email.pem echo "Testing plain user cert issuance KDC CA" $test_kdc_ca -a bx509 -A foo@${R} PKCS10:${objdir}/req \ PEM-FILE:${objdir}/trivial.pem || { echo "Trivial offline CA test failed"; exit 2; } $hxtool print --content PEM-FILE:${objdir}/trivial.pem || { echo "Trivial offline CA test failed"; exit 2; } $hxtool acert --end-entity \ --expr="%{certificate.subject} == \"CN=foo,$DCs\"" \ -P "foo@${R}" "FILE:${objdir}/trivial.pem" || { echo "Trivial offline CA test failed"; exit 2; } $hxtool acert --expr="%{certificate.subject} == \"OU=Users,CN=KDC,$DCs\"" \ --lacks-private-key "FILE:${objdir}/trivial.pem" || { echo "Trivial offline CA test failed (issuer private keys included!!)"; exit 2; } echo "Testing other cert issuance KDC CA" csr_revoke # https server cert $hxtool request-create --subject='' --generate-key=rsa --key-bits=1024 \ --key=FILE:"${objdir}/k.der" \ --eku=id_pkix_kp_serverAuth \ --dnsname=foo.test.h5l.se "${objdir}/req" || { echo "Failed to make a CSR with a dNSName SAN request"; exit 2; } $test_kdc_ca -a bx509 foo@${R} PKCS10:${objdir}/req \ PEM-FILE:${objdir}/server.pem && { echo "Trivial offline CA test failed: unauthorized issuance (dNSName)"; exit 2; } csr_grant san_dnsname foo.test.h5l.se foo@${R} csr_grant eku 1.3.6.1.5.5.7.3.1 foo@${R} $test_kdc_ca -a bx509 foo@${R} PKCS10:${objdir}/req \ PEM-FILE:${objdir}/server.pem || { echo "Offline CA test failed for explicitly authorized dNSName"; exit 2; } $hxtool print --content PEM-FILE:${objdir}/server.pem || { echo "Offline CA test failed for explicitly authorized dNSName"; exit 2; } $hxtool acert --expr="%{certificate.subject} == \"OU=Servers,CN=KDC,$DCs\"" \ --lacks-private-key "FILE:${objdir}/server.pem" || { echo "Trivial offline CA test failed (issuer private keys included!!)"; exit 2; } # email cert $hxtool request-create --subject='' --generate-key=rsa --key-bits=1024 \ --key=FILE:"${objdir}/k.der" \ --eku=id_pkix_kp_clientAuth \ --email=foo@test.h5l.se "${objdir}/req" || { echo "Failed to make a CSR with an rfc822Name SAN request"; exit 2; } $test_kdc_ca -a bx509 foo@${R} PKCS10:${objdir}/req \ PEM-FILE:${objdir}/email.pem && { echo "Offline CA test failed: unauthorized issuance (dNSName)"; exit 2; } csr_grant san_email foo@test.h5l.se foo@${R} csr_grant eku 1.3.6.1.5.5.7.3.2 foo@${R} $test_kdc_ca -a bx509 foo@${R} PKCS10:${objdir}/req \ PEM-FILE:${objdir}/email.pem || { echo "Offline CA test failed for explicitly authorized dNSName"; exit 2; } $hxtool print --content PEM-FILE:${objdir}/email.pem || { echo "Offline CA test failed for explicitly authorized dNSName"; exit 2; } $hxtool acert --expr="%{certificate.subject} == \"OU=Users,CN=KDC,$DCs\"" \ --lacks-private-key "FILE:${objdir}/email.pem" || { echo "Offline CA test failed (issuer private keys included!!)"; exit 2; } if ! which curl; then echo "curl is not available -- not testing bx509d" sh ${leaks_kill} test_csr_authorizer $test_csr_authorizer_pid || ec=1 trap '' EXIT exit 77 fi if ! test -x ${objdir}/../../kdc/bx509d; then echo "Configured w/o libmicrohttpd -- not testing bx509d" sh ${leaks_kill} test_csr_authorizer $test_csr_authorizer_pid || ec=1 trap '' EXIT exit 77 fi echo "Creating database" rm -f $kt $ukt ${kadmin} </dev/null; then if ! ${klistjson} | jq -e ' (reduce (.tickets[0]|(.Issued,.Expires)| strptime("%b %e %H:%M:%S %Y")|mktime) as $t (0; if .==0 then $t else $t - . end) / 86400) | floor | . == 3'; then echo "Incorrect lifetime" exit 2 fi fi echo "Fetch TGT (for other, w/ lifetime req over max)" ${kadmin} modify --max-ticket-life=10d krbtgt/${R}@${R} csr_grant san_pkinit bar@${R} foo@${R} ${kdestroy} token=$(KRB5CCNAME=$cache2 $gsstoken HTTP@$server) if ! (set -vx; curl -o "${cachefile}" -Lgsf \ --resolve ${server}:${bx509port}:127.0.0.1 \ -H "Authorization: Negotiate $token" \ "http://${server}:${bx509port}/get-tgt?cname=bar@${R}&address=8.8.8.8&lifetime=10d"); then echo "Failed to get a TGT with /get-tgt end-point" exit 2 fi ${kgetcred} -H HTTP/${server}@${R} || { echo "Fetched TGT didn't work"; exit 2; } if which jq >/dev/null; then if ! ${klistjson} | jq -e ' (.tickets[0].Issued | strptime("%b %e %H:%M:%S %Y")|mktime) as $iat | (.tickets[0].Expires | strptime("%b %e %H:%M:%S %Y")|mktime) as $exp | (($exp - $iat) / 86400) as $life_days | ($life_days > 4 and $life_days <= 5)'; then echo "Incorrect lifetime" exit 2 fi fi echo "Fetch TGT (for other, w/ lifetime req under max)" ${kadmin} modify --max-ticket-life=10d krbtgt/${R}@${R} csr_grant san_pkinit bar@${R} foo@${R} ${kdestroy} token=$(KRB5CCNAME=$cache2 $gsstoken HTTP@$server) if ! (set -vx; curl -o "${cachefile}" -Lgsf \ --resolve ${server}:${bx509port}:127.0.0.1 \ -H "Authorization: Negotiate $token" \ "http://${server}:${bx509port}/get-tgt?cname=bar@${R}&address=8.8.8.8&address=8.9.10.11&address=11.11.11.11&address=12.12.12.12&lifetime=5d"); then echo "Failed to get a TGT with /get-tgt end-point" exit 2 fi ${kgetcred} -H HTTP/${server}@${R} || { echo "Fetched TGT didn't work"; exit 2; } if which jq >/dev/null; then if ! ${klistjson} | jq -e ' (reduce (.tickets[0]|(.Issued,.Expires)| strptime("%b %e %H:%M:%S %Y")|mktime) as $t (0; if .==0 then $t else $t - . end) / 86400) | . >= 4'; then echo "Failed to get a TGT with /get-tgt end-point with addresses" exit 2 fi fi echo "Fetch TGTs (batch, authz fail)" ${kadmin} modify --max-ticket-life=10d krbtgt/${R}@${R} csr_revoke ${kdestroy} token=$(KRB5CCNAME=$cache2 $gsstoken HTTP@$server) rm -f "${cachefile}.json" if (set -vx; curl -o "${cachefile}.json" -Lgsf \ --resolve ${server}:${bx509port}:127.0.0.1 \ -H "Authorization: Negotiate $token" \ "http://${server}:${bx509port}/get-tgts?cname=bar@${R}&cname=baz@${R}"); then # 200 Ok is not a problem. We have to check that the result is sane. true else if grep ccache "${cachefile}.json"; then echo "Got TGTs with /get-tgts end-point that should have been denied" exit 2; fi if ! grep error_code "${cachefile}.json" > /dev/null; then cat "${cachefile}.json" echo "Request failed w/o error information" exit 2; fi fi cat "${cachefile}.json" if grep ccache "${cachefile}.json"; then echo "Got TGTs with /get-tgts end-point that should have been denied" exit 2; fi echo "Fetch TGTs (batch, partial authz with IPC authorizer)" ${kadmin} modify --max-ticket-life=10d krbtgt/${R}@${R} csr_revoke csr_grant san_pkinit bar@${R} foo@${R} csr_grant san_pkinit baz@${R} foo@${R} ${kdestroy} token=$(KRB5CCNAME=$cache2 $gsstoken HTTP@$server) if ! (set -vx; curl -vvvo "${cachefile}.json" -Lgsf \ --resolve ${server}:${bx509port}:127.0.0.1 \ -H "Authorization: Negotiate $token" \ "http://${server}:${bx509port}/get-tgts?cname=bar@${R}&cname=raz@${R}&cname=baz@${R}"); then echo "Failed to get TGTs batch including non-existent principal" exit 2 fi if which jq >/dev/null; then set -vx jq -e . "${cachefile}.json" > /dev/null || { echo "/get-tgts produced non-JSON"; exit 2; } jq -es '.[]|select(.name|startswith("raz@"))|(.error_code//empty)' "${cachefile}.json" > /dev/null || { echo "No error was reported for raz@${R}!"; exit 2; } jq -es '.[]|select(.name|startswith("raz@"))|(.ccache//"")|(length==0)' "${cachefile}.json" > /dev/null || { echo "Non-empty ccache included for raz@${R}!"; exit 2; } # Check bar@$R's tickets: jq -r 'select(.name|startswith("bar@")).ccache' "${cachefile}.json" | $rkbase64 -d -- - > "${cachefile}" ${kgetcred} -H HTTP/${server}@${R} || { echo "Fetched TGT didn't work"; exit 2; } ${klistjson} | jq -e --arg p bar@$R '.principal == $p' > /dev/null || { echo "/get-tgts produced wrong TGTs"; exit 2; } # Check baz@$R's tickets: jq -r 'select(.name|startswith("baz@")).ccache' "${cachefile}.json" | $rkbase64 -d -- - > "${cachefile}" ${kgetcred} -H HTTP/${server}@${R} || { echo "Fetched TGT didn't work"; exit 2; } ${klistjson} | jq -e --arg p baz@$R '.principal == $p' > /dev/null || { echo "/get-tgts produced wrong TGTs"; exit 2; } fi echo "Fetch TGTs (batch, partial authz)" ${kadmin} modify --max-ticket-life=10d krbtgt/${R}@${R} csr_revoke csr_grant san_pkinit bar@${R} foo@${R} ${kdestroy} token=$(KRB5CCNAME=$cache2 $gsstoken HTTP@$server) if ! (set -vx; curl -vvvo "${cachefile}.json" -Lgsf \ --resolve ${server}:${bx509port}:127.0.0.1 \ -H "Authorization: Negotiate $token" \ "http://${server}:${bx509port}/get-tgts?cname=not@${R}&cname=bar@${R}&cname=baz@${R}"); then echo "Failed to get TGTs batch including non-existent principal" exit 2 fi if which jq >/dev/null; then set -vx jq -e . "${cachefile}.json" > /dev/null || { echo "/get-tgts produced non-JSON"; exit 2; } jq -es '.[]|select(.name|startswith("not@"))|(.error_code//empty)' "${cachefile}.json" > /dev/null || { echo "No error was reported for not@${R}!"; exit 2; } jq -es '.[]|select(.name|startswith("not@"))|(.ccache//"")|(length==0)' "${cachefile}.json" > /dev/null || { echo "Non-empty ccache included for not@${R}!"; exit 2; } jq -es '.[]|select(.name|startswith("baz@"))|(.error_code//empty)' "${cachefile}.json" > /dev/null || { echo "No error was reported for baz@${R}!"; exit 2; } jq -es '.[]|select(.name|startswith("baz@"))|(.ccache//"")|(length==0)' "${cachefile}.json" > /dev/null || { echo "Non-empty ccache included for baz@${R}!"; exit 2; } # Check bar@$R's tickets: jq -r 'select(.name|startswith("bar@")).ccache' "${cachefile}.json" | $rkbase64 -d -- - > "${cachefile}" ${kgetcred} -H HTTP/${server}@${R} || { echo "Fetched TGT didn't work"; exit 2; } ${klistjson} | jq -e --arg p bar@$R '.principal == $p' > /dev/null || { echo "/get-tgts produced wrong TGTs"; exit 2; } fi echo "Fetch TGTs (batch, authz pass)" ${kadmin} modify --max-ticket-life=10d krbtgt/${R}@${R} csr_grant san_pkinit bar@${R} foo@${R} csr_grant san_pkinit baz@${R} foo@${R} ${kdestroy} token=$(KRB5CCNAME=$cache2 $gsstoken HTTP@$server) if ! (set -vx; curl -vvvo "${cachefile}.json" -Lgsf \ --resolve ${server}:${bx509port}:127.0.0.1 \ -H "Authorization: Negotiate $token" \ "http://${server}:${bx509port}/get-tgts?cname=bar@${R}&cname=baz@${R}"); then echo "Failed to get TGTs batch" exit 2 fi if which jq >/dev/null; then jq -e . "${cachefile}.json" > /dev/null || { echo "/get-tgts produced non-JSON"; exit 2; } # Check bar@$R's tickets: jq -r 'select(.name|startswith("bar@")).ccache' "${cachefile}.json" | $rkbase64 -d -- - > "${cachefile}" ${kgetcred} -H HTTP/${server}@${R} || { echo "Fetched TGT didn't work"; exit 2; } ${klistjson} | jq -e --arg p bar@$R '.principal == $p' > /dev/null || { echo "/get-tgts produced wrong TGTs"; exit 2; } # Check baz@$R's tickets: jq -r 'select(.name|startswith("baz@")).ccache' "${cachefile}.json" | $rkbase64 -d -- - > "${cachefile}" ${kgetcred} -H HTTP/${server}@${R} || { echo "Fetched TGT didn't work"; exit 2; } ${klistjson} | jq -e --arg p baz@$R '.principal == $p' > /dev/null || { echo "/get-tgts produced wrong TGTs"; exit 2; } fi echo "Fetch TGTs (batch, authz pass, one non-existent principal)" ${kadmin} modify --max-ticket-life=10d krbtgt/${R}@${R} csr_grant san_pkinit bar@${R} foo@${R} csr_grant san_pkinit baz@${R} foo@${R} csr_grant san_pkinit not@${R} foo@${R} ${kdestroy} token=$(KRB5CCNAME=$cache2 $gsstoken HTTP@$server) if ! (set -vx; curl -vvvo "${cachefile}.json" -Lgsf \ --resolve ${server}:${bx509port}:127.0.0.1 \ -H "Authorization: Negotiate $token" \ "http://${server}:${bx509port}/get-tgts?cname=not@${R}&cname=bar@${R}&cname=baz@${R}"); then echo "Failed to get TGTs batch including non-existent principal" exit 2 fi if which jq >/dev/null; then set -vx jq -e . "${cachefile}.json" > /dev/null || { echo "/get-tgts produced non-JSON"; exit 2; } jq -es '.[]|select(.name|startswith("not@"))|(.error_code//empty)' "${cachefile}.json" > /dev/null || { echo "No error was reported for not@${R}!"; exit 2; } # Check bar@$R's tickets: jq -r 'select(.name|startswith("bar@")).ccache' "${cachefile}.json" | $rkbase64 -d -- - > "${cachefile}" ${kgetcred} -H HTTP/${server}@${R} || { echo "Fetched TGT didn't work"; exit 2; } ${klistjson} | jq -e --arg p bar@$R '.principal == $p' > /dev/null || { echo "/get-tgts produced wrong TGTs"; exit 2; } # Check baz@$R's tickets: jq -r 'select(.name|startswith("baz@")).ccache' "${cachefile}.json" | $rkbase64 -d -- - > "${cachefile}" ${kgetcred} -H HTTP/${server}@${R} || { echo "Fetched TGT didn't work"; exit 2; } ${klistjson} | jq -e --arg p baz@$R '.principal == $p' > /dev/null || { echo "/get-tgts produced wrong TGTs"; exit 2; } fi echo "killing bx509d (${bx509pid})" sh ${leaks_kill} bx509d $bx509pid || ec=1 echo "Starting bx509d (csrf-protection-type=GET-with-token, POST-with-header)" ${bx509d} --csrf-protection-type=GET-with-token \ --csrf-protection-type=POST-with-header --daemon || { echo "bx509 failed to start" exit 2 } bx509pid=`getpid bx509d` ${kinit} -kt $ukeytab foo@${R} || exit 1 $klist || { echo "failed to kinit"; exit 2; } echo "Fetching a trivial user certificate (GET with CSRF token)" csr=$($rkbase64 -- ${objdir}/req | $rkvis -h --stdin) token=$(KRB5CCNAME=$cache $gsstoken HTTP@$server) if (set -vx; get_with_token get-cert '' -o "${objdir}/trivial.pem"); then $hxtool print --content "FILE:${objdir}/trivial.pem" if $hxtool acert --end-entity \ --expr="%{certificate.subject} == \"CN=foo,$DCs\"" \ -P "foo@${R}" "FILE:${objdir}/trivial.pem"; then echo 'Successfully obtained a trivial client certificate!' else echo 'FAIL: Obtained a trivial client certificate w/o expected PKINIT SAN)' exit 1 fi if $hxtool acert --expr="%{certificate.subject} == \"OU=Users,$DCs\"" \ --has-private-key "FILE:${objdir}/trivial.pem"; then echo 'Successfully obtained a trivial client certificate!' fi else echo 'Failed to get a certificate!' exit 1 fi echo "Fetching a trivial user certificate (POST with X-CSRF header, no token)" # Encode the CSR in base64, then URL-encode it csr=$($rkbase64 -- ${objdir}/req | $rkvis -h --stdin) token=$(KRB5CCNAME=$cache $gsstoken HTTP@$server) if (set -vx; get_cert '' -H 'X-CSRF: junk' -X POST -sf -o "${objdir}/trivial.pem"); then $hxtool print --content "FILE:${objdir}/trivial.pem" if $hxtool acert --end-entity \ --expr="%{certificate.subject} == \"CN=foo,$DCs\"" \ -P "foo@${R}" "FILE:${objdir}/trivial.pem"; then echo 'Successfully obtained a trivial client certificate!' else echo 'FAIL: Obtained a trivial client certificate w/o expected PKINIT SAN)' exit 1 fi if $hxtool acert --expr="%{certificate.subject} == \"OU=Users,$DCs\"" \ --has-private-key "FILE:${objdir}/trivial.pem"; then echo 'Successfully obtained a trivial client certificate!' fi else echo 'Failed to get a certificate!' exit 1 fi echo "Fetch negotiate token (pre-test)" # Do what /bnegotiate does, roughly, prior to testing /bnegotiate $hxtool request-create --subject='' --generate-key=rsa --key-bits=1024 \ --key=PEM-FILE:"${objdir}/k.pem" "${objdir}/req" || { echo "Failed to make a CSR"; exit 2; } $test_kdc_ca -a bx509 -A foo@${R} PKCS10:${objdir}/req \ PEM-FILE:${objdir}/pkinit-test.pem || { echo "Trivial offline CA test failed (CA)"; exit 2; } cat ${objdir}/k.pem >> ${objdir}/pkinit-test.pem ${kinit} -C PEM-FILE:${objdir}/pkinit-test.pem foo@${R} || { echo "Trivial offline CA test failed (PKINIT)"; exit 2; } ${kgetcred} -H HTTP/${server}@${R} || { echo "Trivial offline CA test failed (TGS)"; exit 2; } KRB5CCNAME=$cache $gsstoken HTTP@$server | KRB5_KTNAME="$keytab" $gsstoken -r || { echo "Trivial offline CA test failed (gss-token)"; exit 2; } # Check that we get up to three tixaddrs k/v in the log grep 'REQ.*wrongaddr=true' ${objdir}/messages.log | grep 'tixaddrs=IPv4:11.11.11.11' || { echo "KDC not warning about requests from wrong address"; exit 2; } echo "Fetching a Negotiate token" token=$(KRB5CCNAME=$cache $gsstoken HTTP@$server) csr= if (set -vx; get_with_token get-negotiate-token "target=HTTP%40${server}" -o "${objdir}/negotiate-token"); then # bx509 sends us a token w/o a newline for now; we add one because # gss-token expects it. test -s negotiate-token && echo >> negotiate-token if test -s negotiate-token && KRB5_KTNAME="$keytab" $gsstoken -Nr < negotiate-token; then echo 'Successfully obtained a Negotiate token!' else echo 'Failed to get a Negotiate token (got an unacceptable token)!' exit 1 fi else echo 'Failed to get a Negotiate token!' exit 1 fi referer=https://${otherserver}/blah redirect=$(${rkvis} -h https://${otherserver}/blah?q=whatever) if (set -vx; curl -o negotiate-token -Lgsf \ --resolve ${server}:${bx509port}:127.0.0.1 \ -H "Authorization: Negotiate $token" \ "http://${server}:${bx509port}/bnegotiate?target=HTTP%40${server}&redirect=${redirect}"); then echo "Error: /bnegotiate with target and redirect succeeded" exit 1 fi if (set -vx; curl -o negotiate-token -Lgsf \ --resolve ${server}:${bx509port}:127.0.0.1 \ -H "Authorization: Negotiate $token" \ "http://${server}:${bx509port}/bnegotiate?redirect=${redirect}"); then echo "Error: /bnegotiate with redirect but no Referer succeeded" exit 1 fi referer=http://${otherserver}/blah redirect=$(${rkvis} -h http://${otherserver}/blah?q=whatever) if (set -vx; curl -gsf \ --resolve ${server}:${bx509port}:127.0.0.1 \ -H "Authorization: Negotiate $token" \ -H "Referer: $referer" \ "http://${server}:${bx509port}/bnegotiate?redirect=${redirect}"); then echo "Error: redirect for non-https referer" exit 1 fi referer=https://${otherserver}/blah redirect=$(${rkvis} -h https://${otherserver}/blah?q=whatever) if (set -vx; curl -gfs -D curlheaders \ --resolve ${server}:${bx509port}:127.0.0.1 \ -H "Authorization: Negotiate $token" \ -H "Referer: $referer" \ "http://${server}:${bx509port}/bnegotiate?redirect=${redirect}"); then read junk code junk < curlheaders if test "$code" = 307; then echo "Got a proper redirect" else echo "Error: unexpected status code $code (wanted 307)" fi else echo "Error: no redirect" exit 1 fi echo "killing kdc (${kdcpid}) and bx509d (${bx509pid}) and test_csr_authorizer (${test_csr_authorizer_pid})" sh ${leaks_kill} kdc $kdcpid || ec=1 sh ${leaks_kill} bx509d $bx509pid || ec=1 sh ${leaks_kill} test_csr_authorizer $test_csr_authorizer_pid || ec=1 trap "" EXIT exit $ec