bx509d: Add /get-tgts batch end-point
[heimdal.git] / tests / kdc / check-bx509.in
blob5109854fc26027cda1c07630773ec55fb19e3238
1 #!/bin/sh
3 # Copyright (c) 2019 Kungliga Tekniska Högskolan
4 # (Royal Institute of Technology, Stockholm, Sweden).
5 # All rights reserved.
7 # Redistribution and use in source and binary forms, with or without
8 # modification, are permitted provided that the following conditions
9 # are met:
11 # 1. Redistributions of source code must retain the above copyright
12 # notice, this list of conditions and the following disclaimer.
14 # 2. Redistributions in binary form must reproduce the above copyright
15 # notice, this list of conditions and the following disclaimer in the
16 # documentation and/or other materials provided with the distribution.
18 # 3. Neither the name of the Institute nor the names of its contributors
19 # may be used to endorse or promote products derived from this software
20 # without specific prior written permission.
22 # THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
23 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 # ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
26 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 # SUCH DAMAGE.
34 top_builddir="@top_builddir@"
35 env_setup="@env_setup@"
36 objdir="@objdir@"
38 testfailed="echo test failed; cat messages.log; exit 1"
40 . ${env_setup}
42 # If there is no useful db support compiled in, disable test
43 ${have_db} || exit 77
45 umask 077
47 R=TEST.H5L.SE
48 DCs="DC=test,DC=h5l,DC=se"
50 port=@port@
51 bx509port=@bx509port@
53 server=datan.test.h5l.se
54 otherserver=other.test.h5l.se
56 kadmin="${kadmin} -l -r $R"
57 bx509d="${bx509d} --allow-GET --reverse-proxied -p $bx509port -H $server --cert=${objdir}/bx509.pem -t"
58 kdc="${kdc} --addresses=localhost -P $port"
60 cachefile="${objdir}/cache.krb5"
61 cache="FILE:${cachefile}"
62 cachefile2="${objdir}/cache2.krb5"
63 cache2="FILE:${cachefile2}"
64 keyfile="${hx509_data}/key.der"
65 keyfile2="${hx509_data}/key2.der"
66 kt=${objdir}/kt
67 keytab=FILE:${kt}
68 ukt=${objdir}/ukt
69 ukeytab=FILE:${ukt}
71 kinit="${kinit} -c $cache ${afs_no_afslog}"
72 klist2="${klist} --hidden -v -c $cache2"
73 klistjson="${klist} --json -c $cache"
74 klist="${klist} --hidden -v -c $cache"
75 kgetcred="${kgetcred} -c $cache"
76 kdestroy="${kdestroy} -c $cache ${afs_no_unlog}"
77 kx509="${kx509} -c $cache"
79 KRB5_CONFIG="${objdir}/krb5-bx509.conf"
80 export KRB5_CONFIG
82 rsa=yes
83 pkinit=no
84 if ${hxtool} info | grep 'rsa: hx509 null RSA' > /dev/null ; then
85 rsa=no
87 if ${hxtool} info | grep 'rand: not available' > /dev/null ; then
88 rsa=no
91 if ${kinit} --help 2>&1 | grep "CA certificates" > /dev/null; then
92 pkinit=yes
95 # If we doesn't support pkinit and have RSA, give up
96 if test "$pkinit" != yes -o "$rsa" != yes ; then
97 exit 77
101 rm -f current-db*
102 rm -f out-*
103 rm -f mkey.file*
104 rm -f *.pem *.crt *.der
105 rm -rf simple_csr_authz
107 mkdir -p simple_csr_authz
109 > messages.log
111 # We'll avoid using a KDC for now. For testing /bx509 we only need keys for
112 # Negotiate tokens, and we'll use ktutil and kimpersonate to make it possible
113 # to create and accept those without a KDC. When we test /bnegotiate, however,
114 # we'll start a KDC.
116 # csr_grant ext-type value grantee_principal
117 csr_grant() {
118 mkdir -p "${objdir}/simple_csr_authz/${3}"
119 touch "${objdir}/simple_csr_authz/${3}/${1}-${2}"
122 csr_revoke() {
123 rm -rf "${objdir}/simple_csr_authz"
124 mkdir -p "${objdir}/simple_csr_authz"
127 # get_cert "" curl-opts
128 # get_cert "&qparams" curl-opts
129 get_cert() {
130 url="http://${server}:${bx509port}/bx509?csr=$csr${1}"
131 shift
132 curl -g --resolve ${server}:${bx509port}:127.0.0.1 \
133 -H "Authorization: Negotiate $token" \
134 "$@" "$url"
137 get_with_token() {
138 if [ -n "$csr" ]; then
139 url="http://${server}:${bx509port}/${1}?csr=$csr${2}"
140 else
141 url="http://${server}:${bx509port}/${1}?${2}"
143 shift 2
145 curl -fg --resolve ${server}:${bx509port}:127.0.0.1 \
146 -H "Authorization: Negotiate $token" \
147 -D response-headers \
148 "$@" "$url" &&
149 { echo "GET w/o CSRF token succeeded!"; exit 2; }
150 curl -g --resolve ${server}:${bx509port}:127.0.0.1 \
151 -H "Authorization: Negotiate $token" \
152 -D response-headers \
153 "$@" "$url"
154 grep ^X-CSRF-Token: response-headers >/dev/null ||
155 { echo "GET w/o CSRF token did not output a CSRF token!"; exit 2; }
156 curl -fg --resolve ${server}:${bx509port}:127.0.0.1 \
157 -H "Authorization: Negotiate $token" \
158 -H "$(sed -e 's/\r//' response-headers | grep ^X-CSRF-Token:)" \
159 "$@" "$url" ||
160 { echo "GET w/ CSRF failed"; exit 2; }
163 get_via_POST() {
164 endpoint=$1
165 shift
167 curl -fg --resolve ${server}:${bx509port}:127.0.0.1 \
168 -H "Authorization: Negotiate $token" \
169 -X POST -D response-headers \
170 "$@" "http://${server}:${bx509port}/${endpoint}" &&
171 { echo "POST w/o CSRF token succeeded!"; exit 2; }
172 curl -g --resolve ${server}:${bx509port}:127.0.0.1 \
173 -H "Authorization: Negotiate $token" \
174 -X POST -D response-headers \
175 "$@" "http://${server}:${bx509port}/${endpoint}"
176 grep ^X-CSRF-Token: response-headers >/dev/null ||
177 { echo "POST w/o CSRF token did not output a CSRF token!"; exit 2; }
178 curl -fg --resolve ${server}:${bx509port}:127.0.0.1 \
179 -H "Authorization: Negotiate $token" \
180 -H "$(sed -e 's/\r//' response-headers | grep ^X-CSRF-Token:)" \
181 -X POST \
182 "$@" "http://${server}:${bx509port}/${endpoint}" ||
183 { echo "POST w/ CSRF failed"; exit 2; }
186 rm -f $kt $ukt
187 $ktutil -k $keytab add -r -V 1 -e aes128-cts-hmac-sha1-96 \
188 -p HTTP/datan.test.h5l.se@${R} ||
189 { echo "failed to setup kimpersonate credentials"; exit 2; }
190 $ktutil -k $keytab list ||
191 { echo "failed to setup kimpersonate credentials"; exit 2; }
192 $kimpersonate --ccache=$cache -k $keytab -R -t aes128-cts-hmac-sha1-96 \
193 -c foo@${R} -s HTTP/datan.test.h5l.se@${R} ||
194 { echo "failed to setup kimpersonate credentials"; exit 2; }
195 $klist ||
196 { echo "failed to setup kimpersonate credentials"; exit 2; }
198 echo "Setting up certificates"
199 # We need:
201 # - a CA certificate for issuing client certificates
202 # - a CA certificate for issuing server certificates
203 # - a CA certificate for issuing mixed certificates
204 # - a certificate for bx509 itself (well, not in reverse proxy mode, but we'll
205 # make one anyways)
207 # Make the realm's user cert issuer CA certificate.
209 # NOTE WELL: We need all three KeyUsage values listed below!
210 # We also need this to be of type "pkinit-kdc",
211 # which means we'll get an appropriate EKU OID as
212 # well.
213 $hxtool ca --issue-ca --self-signed --type=pkinit-kdc \
214 --ku=digitalSignature --ku=keyCertSign --ku=cRLSign \
215 --pk-init-principal=krbtgt/${R}@${R} \
216 --generate-key=rsa --key-bits=1024 \
217 --subject="OU=Users,CN=KDC,${DCs}" \
218 --certificate=PEM-FILE:"${objdir}/user-issuer.pem" ||
219 { echo "failed to setup CA certificate"; exit 2; }
221 # We'll use the user cert issuer as the PKINIT anchor, allowing bx509-issued
222 # certificates to be used for PKINIT. Though we won't be testing PKINIT here
223 # -- we test kx509->PKINIT in check-pkinit.
224 cp ${objdir}/user-issuer.pem ${objdir}/pkinit-anchor.pem
226 # Put the cert alone in the trust anchors file
227 ex "${objdir}/pkinit-anchor.pem" <<"EOF"
228 /-----BEGIN CERTIFICATE-----
229 1,.-1 d
233 $hxtool ca --issue-ca --self-signed \
234 --ku=digitalSignature --ku=keyCertSign --ku=cRLSign \
235 --generate-key=rsa --key-bits=1024 \
236 --subject="OU=Servers,CN=KDC,${DCs}" \
237 --certificate=PEM-FILE:"${objdir}/server-issuer.pem" ||
238 { echo "failed to setup CA certificate"; exit 2; }
240 $hxtool ca --issue-ca --self-signed \
241 --ku=digitalSignature --ku=keyCertSign --ku=cRLSign \
242 --generate-key=rsa --key-bits=1024 \
243 --subject="OU=Users,CN=KDC,${DCs}" \
244 --certificate=PEM-FILE:"${objdir}/mixed-issuer.pem" ||
245 { echo "failed to setup CA certificate"; exit 2; }
247 $hxtool ca --issue-ca --type=https-negotiate-server \
248 --ca-certificate=PEM-FILE:"${objdir}/server-issuer.pem" \
249 --ku=digitalSignature --pk-init-principal=HTTP/${server}@${R}\
250 --generate-key=rsa --key-bits=1024 --subject="" \
251 --certificate=PEM-FILE:"${objdir}/bx509.pem" ||
252 { echo "failed to setup CA certificate"; exit 2; }
254 # XXX Before starting bx509d let us use kdc test programs to check that:
256 # - the negotiate token validator plugin works
257 # - the simple CSR authorizer plugin works
258 # - the KDC CA tester program works
260 echo "Check gss-token and Negotiate token validator plugin"
261 token=$(KRB5CCNAME=$cache $gsstoken HTTP@$server | tr A B)
262 $test_token_validator -a datan.test.h5l.se Negotiate "$token" &&
263 { echo "Negotiate token validator accepted invalid token"; exit 2; }
264 token=$(KRB5CCNAME=$cache $gsstoken HTTP@$server)
265 $test_token_validator -a datan.test.h5l.se Negotiate "$token" ||
266 { echo "Negotiate token validator failed to validate valid token"; exit 2; }
268 echo "Making a plain CSR"
269 $hxtool request-create --subject='' --generate-key=rsa --key-bits=1024 \
270 --key=FILE:"${objdir}/k.der" "${objdir}/req" ||
271 { echo "Failed to make a CSR"; exit 2; }
273 rm -f trivial.pem server.pem email.pem
275 echo "Testing plain user cert issuance KDC CA"
276 $test_kdc_ca -a bx509 -A foo@${R} PKCS10:${objdir}/req \
277 PEM-FILE:${objdir}/trivial.pem ||
278 { echo "Trivial offline CA test failed"; exit 2; }
279 $hxtool print --content PEM-FILE:${objdir}/trivial.pem ||
280 { echo "Trivial offline CA test failed"; exit 2; }
281 $hxtool acert --end-entity \
282 --expr="%{certificate.subject} == \"CN=foo,$DCs\"" \
283 -P "foo@${R}" "FILE:${objdir}/trivial.pem" ||
284 { echo "Trivial offline CA test failed"; exit 2; }
285 $hxtool acert --expr="%{certificate.subject} == \"OU=Users,CN=KDC,$DCs\"" \
286 --lacks-private-key "FILE:${objdir}/trivial.pem" ||
287 { echo "Trivial offline CA test failed (issuer private keys included!!)"; exit 2; }
289 echo "Testing other cert issuance KDC CA"
290 csr_revoke
291 # https server cert
292 $hxtool request-create --subject='' --generate-key=rsa --key-bits=1024 \
293 --key=FILE:"${objdir}/k.der" \
294 --eku=id_pkix_kp_serverAuth \
295 --dnsname=foo.test.h5l.se "${objdir}/req" ||
296 { echo "Failed to make a CSR with a dNSName SAN request"; exit 2; }
297 $test_kdc_ca -a bx509 foo@${R} PKCS10:${objdir}/req \
298 PEM-FILE:${objdir}/server.pem &&
299 { echo "Trivial offline CA test failed: unauthorized issuance (dNSName)"; exit 2; }
300 csr_grant dnsname foo.test.h5l.se foo@${R}
301 csr_grant eku 1.3.6.1.5.5.7.3.1 foo@${R}
302 $test_kdc_ca -a bx509 foo@${R} PKCS10:${objdir}/req \
303 PEM-FILE:${objdir}/server.pem ||
304 { echo "Offline CA test failed for explicitly authorized dNSName"; exit 2; }
305 $hxtool print --content PEM-FILE:${objdir}/server.pem ||
306 { echo "Offline CA test failed for explicitly authorized dNSName"; exit 2; }
307 $hxtool acert --expr="%{certificate.subject} == \"OU=Servers,CN=KDC,$DCs\"" \
308 --lacks-private-key "FILE:${objdir}/server.pem" ||
309 { echo "Trivial offline CA test failed (issuer private keys included!!)"; exit 2; }
310 # email cert
311 $hxtool request-create --subject='' --generate-key=rsa --key-bits=1024 \
312 --key=FILE:"${objdir}/k.der" \
313 --eku=id_pkix_kp_clientAuth \
314 --email=foo@test.h5l.se "${objdir}/req" ||
315 { echo "Failed to make a CSR with an rfc822Name SAN request"; exit 2; }
316 $test_kdc_ca -a bx509 foo@${R} PKCS10:${objdir}/req \
317 PEM-FILE:${objdir}/email.pem &&
318 { echo "Offline CA test failed: unauthorized issuance (dNSName)"; exit 2; }
319 csr_grant email foo@test.h5l.se foo@${R}
320 csr_grant eku 1.3.6.1.5.5.7.3.2 foo@${R}
321 $test_kdc_ca -a bx509 foo@${R} PKCS10:${objdir}/req \
322 PEM-FILE:${objdir}/email.pem ||
323 { echo "Offline CA test failed for explicitly authorized dNSName"; exit 2; }
324 $hxtool print --content PEM-FILE:${objdir}/email.pem ||
325 { echo "Offline CA test failed for explicitly authorized dNSName"; exit 2; }
326 $hxtool acert --expr="%{certificate.subject} == \"OU=Users,CN=KDC,$DCs\"" \
327 --lacks-private-key "FILE:${objdir}/email.pem" ||
328 { echo "Offline CA test failed (issuer private keys included!!)"; exit 2; }
330 if ! which curl; then
331 echo "curl is not available -- not testing bx509d"
332 exit 77
335 if ! test -x ${objdir}/../../kdc/bx509d; then
336 echo "Configured w/o libmicrohttpd -- not testing bx509d"
337 exit 77
340 echo "Creating database"
341 ${kadmin} init \
342 --realm-max-ticket-life=1day \
343 --realm-max-renewable-life=1month \
344 ${R} || exit 1
345 ${kadmin} add -r --use-defaults foo@${R} || exit 1
346 ${kadmin} add -r --use-defaults bar@${R} || exit 1
347 ${kadmin} add -r --use-defaults baz@${R} || exit 1
348 ${kadmin} modify --pkinit-acl="CN=foo,DC=test,DC=h5l,DC=se" foo@${R} || exit 1
351 echo "Starting bx509d"
352 ${bx509d} --daemon || { echo "bx509 failed to start"; exit 2; }
353 bx509pid=`getpid bx509d`
355 trap 'kill -9 ${bx509pid}; echo signal killing bx509d; exit 1;' EXIT
356 ec=0
358 rm -f trivial.pem server.pem email.pem
360 echo "Making a plain CSR"
361 csr_revoke
362 $hxtool request-create --subject='' --generate-key=rsa --key-bits=1024 \
363 --key=FILE:"${objdir}/k.der" "${objdir}/req" ||
364 { echo "Failed to make a CSR"; exit 2; }
366 # XXX Add autoconf check for curl?
367 # Create a barebones bx509 HTTP/1.1 client test program?
369 echo "Fetching a trivial user certificate (no authentication, must fail)"
370 # Encode the CSR in base64, then URL-encode it
371 csr=$($rkbase64 -- ${objdir}/req | $rkvis -h --stdin)
372 if (set -vx;
373 curl -g --resolve ${server}:${bx509port}:127.0.0.1 \
374 -sf -o "${objdir}/trivial.pem" \
375 "http://${server}:${bx509port}/bx509?csr=$csr"); then
376 $hxtool print --content "FILE:${objdir}/trivial.pem"
377 echo 'Got a certificate without authenticating!'
378 exit 1
381 echo "Fetching a trivial user certificate"
382 # Encode the CSR in base64, then URL-encode it
383 csr=$($rkbase64 -- ${objdir}/req | $rkvis -h --stdin)
384 token=$(KRB5CCNAME=$cache $gsstoken HTTP@$server)
385 if (set -vx; get_cert '' -sf -o "${objdir}/trivial.pem"); then
386 $hxtool print --content "FILE:${objdir}/trivial.pem"
387 if $hxtool acert --end-entity \
388 --expr="%{certificate.subject} == \"CN=foo,$DCs\"" \
389 -P "foo@${R}" "FILE:${objdir}/trivial.pem"; then
390 echo 'Successfully obtained a trivial client certificate!'
391 else
392 echo 'FAIL: Obtained a trivial client certificate w/o expected PKINIT SAN)'
393 exit 1
395 if $hxtool acert --expr="%{certificate.subject} == \"OU=Users,$DCs\"" \
396 --has-private-key "FILE:${objdir}/trivial.pem"; then
397 echo 'Successfully obtained a trivial client certificate!'
399 else
400 echo 'Failed to get a certificate!'
401 exit 1
404 echo "Fetching a trivial user certificate (with POST, no auth, must fail)"
405 # Encode the CSR in base64; curl will URL-encode it for us
406 csr=$($rkbase64 -- ${objdir}/req)
407 if (set -vx;
408 curl -fg --resolve ${server}:${bx509port}:127.0.0.1 \
409 -X POST -D response-headers \
410 -F csr="$csr" -o "${objdir}/trivial.pem" \
411 "http://${server}:${bx509port}/bx509" ); then
412 $hxtool print --content "FILE:${objdir}/trivial.pem"
413 echo 'Got a certificate without authenticating!'
414 exit 1
417 echo "Fetching a trivial user certificate (with POST)"
418 # Encode the CSR in base64; curl will URL-encode it for us
419 csr=$($rkbase64 -- ${objdir}/req)
420 token=$(KRB5CCNAME=$cache $gsstoken HTTP@$server)
421 if (set -vx;
422 get_via_POST bx509 -F csr="$csr" -o "${objdir}/trivial.pem"); then
423 $hxtool print --content "FILE:${objdir}/trivial.pem"
424 if $hxtool acert --end-entity \
425 --expr="%{certificate.subject} == \"CN=foo,$DCs\"" \
426 -P "foo@${R}" "FILE:${objdir}/trivial.pem"; then
427 echo 'Successfully obtained a trivial client certificate!'
428 else
429 echo 'FAIL: Obtained a trivial client certificate w/o expected PKINIT SAN)'
430 exit 1
432 if $hxtool acert --expr="%{certificate.subject} == \"OU=Users,$DCs\"" \
433 --has-private-key "FILE:${objdir}/trivial.pem"; then
434 echo 'Successfully obtained a trivial client certificate!'
436 else
437 echo 'Failed to get a certificate!'
438 exit 1
441 echo "Checking that authorization is enforced"
442 csr_revoke
443 get_cert '&rfc822Name=foo@bar.example' -vvv -o "${objdir}/bad1.pem"
444 if (set -vx; get_cert '&rfc822Name=foo@bar.example' -sf -o "${objdir}/trivial.pem"); then
445 $hxtool print --content "FILE:${objdir}/bad1.pem"
446 echo 'Obtained a client certificate for a non-granted name!'
447 exit 1
448 else
449 echo 'Correctly failed to get a client certificate for a non-granted name'
452 if (set -vx; get_cert "&dNSName=$server" -sf -o "${objdir}/bad2.pem"); then
453 $hxtool print --content "FILE:${objdir}/bad2.pem"
454 echo 'Obtained a server certificate for a non-granted name!'
455 exit 1
456 else
457 echo 'Correctly failed to get a server certificate for a non-granted name'
460 echo "Fetching a server certificate with one dNSName SAN"
461 csr_grant dnsname $server foo@${R}
462 if (set -vx; get_cert "&dNSName=$server" -sf -o "${objdir}/server.pem"); then
463 $hxtool print --content "FILE:${objdir}/server.pem"
464 if (set -vx; $hxtool acert --expr="%{certificate.subject} == \"\"" \
465 --end-entity -P foo@${R} \
466 "FILE:${objdir}/server.pem"); then
467 echo 'Got a broken server certificate (has PKINIT SAN)'
468 exit 1
469 elif $hxtool acert --end-entity -D $server "FILE:${objdir}/server.pem"; then
470 echo 'Successfully obtained a server certificate!'
471 else
472 echo 'Got a broken server certificate'
473 exit 1
475 else
476 echo 'Failed to get a server certificate!'
477 exit 1
480 echo "Fetching a server certificate with two dNSName SANs"
481 csr_grant dnsname "second-$server" foo@${R}
482 if (set -vx;
483 get_cert "&dNSName=${server}&dNSName=second-$server" -sf \
484 -o "${objdir}/server2.pem"); then
485 $hxtool print --content "FILE:${objdir}/server2.pem"
486 if $hxtool acert --expr="%{certificate.subject} == \"\"" \
487 --end-entity -P foo@${R} \
488 "FILE:${objdir}/server2.pem"; then
489 echo 'Got a broken server certificate (has PKINIT SAN)'
490 exit 1
491 elif $hxtool acert --end-entity -D "$server" \
492 -D "second-$server" \
493 "FILE:${objdir}/server2.pem"; then
494 echo 'Successfully obtained a server certificate with two dNSName SANs!'
495 else
496 echo 'Got a broken server certificate (wanted two dNSName SANs)'
497 exit 1
499 else
500 echo 'Failed to get a server certificate with two dNSName SANs!'
501 exit 1
504 echo "Fetching an email certificate"
505 csr_grant email foo@bar.example foo@${R}
506 if (set -vx; get_cert "&rfc822Name=foo@bar.example" -sf -o "${objdir}/email.pem"); then
507 $hxtool print --content "FILE:${objdir}/email.pem"
508 if $hxtool acert --end-entity -P "foo@${R}" "FILE:${objdir}/email.pem"; then
509 echo 'Got a broken email certificate (has PKINIT SAN)'
510 exit 1
511 elif $hxtool acert --expr="%{certificate.subject} == \"\"" \
512 --end-entity -M foo@bar.example \
513 "FILE:${objdir}/email.pem"; then
514 echo 'Successfully obtained a email certificate!'
515 else
516 echo 'Got a broken email certificate'
517 exit 1
519 else
520 echo 'Failed to get an email certificate!'
521 exit 1
524 # Need to start a KDC to test this.
525 rm -f $kt $ukt
526 ${kdestroy}
527 ${kadmin} add -r --use-defaults HTTP/${server}@${R} || exit 1
528 ${kadmin} ext_keytab -r -k $keytab HTTP/${server}@${R} || exit 1
529 ${kadmin} add -r --use-defaults HTTP/${otherserver}@${R} || exit 1
530 ${kadmin} ext_keytab -r -k $ukeytab foo@${R} || exit 1
532 echo "Starting kdc";
533 ${kdc} --detach --testing || { echo "kdc failed to start"; cat messages.log; exit 1; }
534 kdcpid=`getpid kdc`
535 trap 'kill -9 ${kdcpid} ${bx509pid}; echo signal killing kdc and bx509d; exit 1;' EXIT
537 ${kinit} -kt $ukeytab foo@${R} || exit 1
538 $klist || { echo "failed to kinit"; exit 2; }
540 echo "Fetch TGT (not granted for other)"
541 token=$(KRB5CCNAME=$cache $gsstoken HTTP@$server)
542 if (set -vx;
543 curl -o "${cachefile2}" -Lgsf \
544 --resolve ${server}:${bx509port}:127.0.0.1 \
545 -H "Authorization: Negotiate $token" \
546 "http://${server}:${bx509port}/get-tgt?cname=bar@${R}&address=8.8.8.8"); then
547 echo "Got a TGT with /get-tgt end-point when not granted!"
548 exit 2
551 echo "Fetch TGT"
552 (set -vx; csr_grant pkinit foo@${R} foo@${R})
553 (set -vx; csr_grant eku 1.3.6.1.5.2.3.4 foo@${R})
554 token=$(KRB5CCNAME=$cache $gsstoken HTTP@$server)
555 if ! (set -vx;
556 curl -o "${cachefile2}" -Lgsf \
557 --resolve ${server}:${bx509port}:127.0.0.1 \
558 -H "Authorization: Negotiate $token" \
559 "http://${server}:${bx509port}/get-tgt?address=8.8.8.8"); then
560 echo "Failed to get a TGT with /get-tgt end-point"
561 exit 2
564 ${klist2} | grep Addresses:.IPv4:8.8.8.8 ||
565 { echo "Failed to get a TGT with /get-tgt end-point with addresses"; exit 2; }
567 echo "Fetch TGT (inception)"
568 ${kdestroy}
569 token=$(KRB5CCNAME=$cache2 $gsstoken HTTP@$server)
570 if ! (set -vx;
571 curl -o "${cachefile}" -Lgsf \
572 --resolve ${server}:${bx509port}:127.0.0.1 \
573 -H "Authorization: Negotiate $token" \
574 "http://${server}:${bx509port}/get-tgt?address=8.8.8.8"); then
575 echo "Failed to get a TGT with /get-tgt end-point"
576 exit 2
578 ${kgetcred} -H HTTP/${server}@${R} ||
579 { echo "Fetched TGT didn't work"; exit 2; }
580 ${klist} | grep Addresses:.IPv4:8.8.8.8 ||
581 { echo "Failed to get a TGT with /get-tgt end-point with addresses"; exit 2; }
583 echo "Fetch TGT (for other)"
584 (set -vx; csr_grant pkinit bar@${R} foo@${R})
585 ${kdestroy}
586 token=$(KRB5CCNAME=$cache2 $gsstoken HTTP@$server)
587 if ! (set -vx;
588 curl -o "${cachefile}" -Lgsf \
589 --resolve ${server}:${bx509port}:127.0.0.1 \
590 -H "Authorization: Negotiate $token" \
591 "http://${server}:${bx509port}/get-tgt?cname=bar@${R}&address=8.8.8.8"); then
592 echo "Failed to get a TGT with /get-tgt end-point"
593 exit 2
595 ${kgetcred} -H HTTP/${server}@${R} ||
596 { echo "Fetched TGT didn't work"; exit 2; }
597 ${klist} | grep Addresses:.IPv4:8.8.8.8 ||
598 { echo "Failed to get a TGT with /get-tgt end-point with addresses"; exit 2; }
600 echo "Fetch TGT (for other, w/ lifetime req under max)"
601 ${kadmin} modify --max-ticket-life=10d krbtgt/${R}@${R}
602 (set -vx; csr_grant pkinit bar@${R} foo@${R})
603 ${kdestroy}
604 token=$(KRB5CCNAME=$cache2 $gsstoken HTTP@$server)
605 if ! (set -vx;
606 curl -o "${cachefile}" -Lgsf \
607 --resolve ${server}:${bx509port}:127.0.0.1 \
608 -H "Authorization: Negotiate $token" \
609 "http://${server}:${bx509port}/get-tgt?cname=bar@${R}&address=8.8.8.8&lifetime=3d"); then
610 echo "Failed to get a TGT with /get-tgt end-point"
611 exit 2
613 ${kgetcred} -H HTTP/${server}@${R} ||
614 { echo "Fetched TGT didn't work"; exit 2; }
615 if which jq >/dev/null; then
616 if ! ${klistjson} | jq -e '
617 (reduce (.tickets[0]|(.Issued,.Expires)|
618 strptime("%b %e %H:%M:%S %Y")|mktime) as $t
619 (0; if .==0 then $t else $t - . end) / 86400) | floor |
620 . == 3'; then
621 echo "Incorrect lifetime"
622 exit 2
626 echo "Fetch TGT (for other, w/ lifetime req over max)"
627 ${kadmin} modify --max-ticket-life=10d krbtgt/${R}@${R}
628 (set -vx; csr_grant pkinit bar@${R} foo@${R})
629 ${kdestroy}
630 token=$(KRB5CCNAME=$cache2 $gsstoken HTTP@$server)
631 if ! (set -vx;
632 curl -o "${cachefile}" -Lgsf \
633 --resolve ${server}:${bx509port}:127.0.0.1 \
634 -H "Authorization: Negotiate $token" \
635 "http://${server}:${bx509port}/get-tgt?cname=bar@${R}&address=8.8.8.8&lifetime=10d"); then
636 echo "Failed to get a TGT with /get-tgt end-point"
637 exit 2
639 ${kgetcred} -H HTTP/${server}@${R} ||
640 { echo "Fetched TGT didn't work"; exit 2; }
641 if which jq >/dev/null; then
642 if ! ${klistjson} | jq -e '
643 (reduce (.tickets[0]|(.Issued,.Expires)|
644 strptime("%b %e %H:%M:%S %Y")|mktime) as $t
645 (0; if .==0 then $t else $t - . end) / 86400) | floor |
646 . == 5'; then
647 echo "Incorrect lifetime"
648 exit 2
652 echo "Fetch TGT (for other, w/ lifetime req under max)"
653 ${kadmin} modify --max-ticket-life=10d krbtgt/${R}@${R}
654 (set -vx; csr_grant pkinit bar@${R} foo@${R})
655 ${kdestroy}
656 token=$(KRB5CCNAME=$cache2 $gsstoken HTTP@$server)
657 if ! (set -vx;
658 curl -o "${cachefile}" -Lgsf \
659 --resolve ${server}:${bx509port}:127.0.0.1 \
660 -H "Authorization: Negotiate $token" \
661 "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
662 echo "Failed to get a TGT with /get-tgt end-point"
663 exit 2
665 ${kgetcred} -H HTTP/${server}@${R} ||
666 { echo "Fetched TGT didn't work"; exit 2; }
667 if which jq >/dev/null; then
668 if ! ${klistjson} | jq -e '
669 (reduce (.tickets[0]|(.Issued,.Expires)|
670 strptime("%b %e %H:%M:%S %Y")|mktime) as $t
671 (0; if .==0 then $t else $t - . end) / 86400) |
672 . >= 4'; then
673 echo "Failed to get a TGT with /get-tgt end-point with addresses"
674 exit 2
678 echo "Fetch TGTs (batch, authz fail)"
679 ${kadmin} modify --max-ticket-life=10d krbtgt/${R}@${R}
680 (set -vx; csr_grant pkinit bar@${R} foo@${R})
681 ${kdestroy}
682 token=$(KRB5CCNAME=$cache2 $gsstoken HTTP@$server)
683 if (set -vx;
684 curl -o "${cachefile}.json" -Lgsf \
685 --resolve ${server}:${bx509port}:127.0.0.1 \
686 -H "Authorization: Negotiate $token" \
687 "http://${server}:${bx509port}/get-tgts?cname=bar@${R}&cname=baz@${R}"); then
688 echo "Got TGTs with /get-tgts end-point that should have been denied"
689 exit 2
692 echo "Fetch TGTs (batch, authz pass)"
693 ${kadmin} modify --max-ticket-life=10d krbtgt/${R}@${R}
694 (csr_grant pkinit bar@${R} foo@${R})
695 (csr_grant pkinit baz@${R} foo@${R})
696 ${kdestroy}
697 token=$(KRB5CCNAME=$cache2 $gsstoken HTTP@$server)
698 if ! (set -vx;
699 curl -vvvo "${cachefile}.json" -Lgsf \
700 --resolve ${server}:${bx509port}:127.0.0.1 \
701 -H "Authorization: Negotiate $token" \
702 "http://${server}:${bx509port}/get-tgts?cname=bar@${R}&cname=baz@${R}"); then
703 echo "Failed to get TGTs batch"
704 exit 2
706 if which jq >/dev/null; then
707 jq -e . "${cachefile}.json" > /dev/null ||
708 { echo "/get-tgts produced non-JSON"; exit 2; }
710 # Check bar@$R's tickets:
711 jq -r 'select(.name|startswith("bar@")).ccache' "${cachefile}.json" |
712 $rkbase64 -d -- - > "${cachefile}"
713 ${kgetcred} -H HTTP/${server}@${R} ||
714 { echo "Fetched TGT didn't work"; exit 2; }
715 ${klistjson} | jq -e --arg p bar@$R '.principal == $p' > /dev/null ||
716 { echo "/get-tgts produced wrong TGTs"; exit 2; }
718 # Check baz@$R's tickets:
719 jq -r 'select(.name|startswith("baz@")).ccache' "${cachefile}.json" |
720 $rkbase64 -d -- - > "${cachefile}"
721 ${kgetcred} -H HTTP/${server}@${R} ||
722 { echo "Fetched TGT didn't work"; exit 2; }
723 ${klistjson} | jq -e --arg p baz@$R '.principal == $p' > /dev/null ||
724 { echo "/get-tgts produced wrong TGTs"; exit 2; }
727 echo "Fetch TGTs (batch, authz pass, one non-existent principal)"
728 ${kadmin} modify --max-ticket-life=10d krbtgt/${R}@${R}
729 (csr_grant pkinit bar@${R} foo@${R})
730 (csr_grant pkinit baz@${R} foo@${R})
731 (csr_grant pkinit not@${R} foo@${R})
732 ${kdestroy}
733 token=$(KRB5CCNAME=$cache2 $gsstoken HTTP@$server)
734 if ! (set -vx;
735 curl -vvvo "${cachefile}.json" -Lgsf \
736 --resolve ${server}:${bx509port}:127.0.0.1 \
737 -H "Authorization: Negotiate $token" \
738 "http://${server}:${bx509port}/get-tgts?cname=not@${R}&cname=bar@${R}&cname=baz@${R}"); then
739 echo "Failed to get TGTs batch including non-existent principal"
740 exit 2
742 if which jq >/dev/null; then
743 set -vx
744 jq -e . "${cachefile}.json" > /dev/null ||
745 { echo "/get-tgts produced non-JSON"; exit 2; }
746 jq -es '.[]|select(.name|startswith("not@"))|(.error_code//empty)' "${cachefile}.json" > /dev/null ||
747 { echo "No error was reported for not@${R}!"; exit 2; }
749 # Check bar@$R's tickets:
750 jq -r 'select(.name|startswith("bar@")).ccache' "${cachefile}.json" |
751 $rkbase64 -d -- - > "${cachefile}"
752 ${kgetcred} -H HTTP/${server}@${R} ||
753 { echo "Fetched TGT didn't work"; exit 2; }
754 ${klistjson} | jq -e --arg p bar@$R '.principal == $p' > /dev/null ||
755 { echo "/get-tgts produced wrong TGTs"; exit 2; }
757 # Check baz@$R's tickets:
758 jq -r 'select(.name|startswith("baz@")).ccache' "${cachefile}.json" |
759 $rkbase64 -d -- - > "${cachefile}"
760 ${kgetcred} -H HTTP/${server}@${R} ||
761 { echo "Fetched TGT didn't work"; exit 2; }
762 ${klistjson} | jq -e --arg p baz@$R '.principal == $p' > /dev/null ||
763 { echo "/get-tgts produced wrong TGTs"; exit 2; }
766 echo "killing bx509d (${bx509pid})"
767 sh ${leaks_kill} bx509d $bx509pid || ec=1
769 echo "Starting bx509d (csrf-protection-type=GET-with-token, POST-with-header)"
770 ${bx509d} --csrf-protection-type=GET-with-token \
771 --csrf-protection-type=POST-with-header --daemon || {
772 echo "bx509 failed to start"
773 exit 2
775 bx509pid=`getpid bx509d`
777 ${kinit} -kt $ukeytab foo@${R} || exit 1
778 $klist || { echo "failed to kinit"; exit 2; }
780 echo "Fetching a trivial user certificate (GET with CSRF token)"
781 csr=$($rkbase64 -- ${objdir}/req | $rkvis -h --stdin)
782 token=$(KRB5CCNAME=$cache $gsstoken HTTP@$server)
783 if (set -vx; get_with_token get-cert '' -o "${objdir}/trivial.pem"); then
784 $hxtool print --content "FILE:${objdir}/trivial.pem"
785 if $hxtool acert --end-entity \
786 --expr="%{certificate.subject} == \"CN=foo,$DCs\"" \
787 -P "foo@${R}" "FILE:${objdir}/trivial.pem"; then
788 echo 'Successfully obtained a trivial client certificate!'
789 else
790 echo 'FAIL: Obtained a trivial client certificate w/o expected PKINIT SAN)'
791 exit 1
793 if $hxtool acert --expr="%{certificate.subject} == \"OU=Users,$DCs\"" \
794 --has-private-key "FILE:${objdir}/trivial.pem"; then
795 echo 'Successfully obtained a trivial client certificate!'
797 else
798 echo 'Failed to get a certificate!'
799 exit 1
802 echo "Fetching a trivial user certificate (POST with X-CSRF header, no token)"
803 # Encode the CSR in base64, then URL-encode it
804 csr=$($rkbase64 -- ${objdir}/req | $rkvis -h --stdin)
805 token=$(KRB5CCNAME=$cache $gsstoken HTTP@$server)
806 if (set -vx; get_cert '' -H 'X-CSRF: junk' -X POST -sf -o "${objdir}/trivial.pem"); then
807 $hxtool print --content "FILE:${objdir}/trivial.pem"
808 if $hxtool acert --end-entity \
809 --expr="%{certificate.subject} == \"CN=foo,$DCs\"" \
810 -P "foo@${R}" "FILE:${objdir}/trivial.pem"; then
811 echo 'Successfully obtained a trivial client certificate!'
812 else
813 echo 'FAIL: Obtained a trivial client certificate w/o expected PKINIT SAN)'
814 exit 1
816 if $hxtool acert --expr="%{certificate.subject} == \"OU=Users,$DCs\"" \
817 --has-private-key "FILE:${objdir}/trivial.pem"; then
818 echo 'Successfully obtained a trivial client certificate!'
820 else
821 echo 'Failed to get a certificate!'
822 exit 1
825 echo "Fetch negotiate token (pre-test)"
826 # Do what /bnegotiate does, roughly, prior to testing /bnegotiate
827 $hxtool request-create --subject='' --generate-key=rsa --key-bits=1024 \
828 --key=PEM-FILE:"${objdir}/k.pem" "${objdir}/req" ||
829 { echo "Failed to make a CSR"; exit 2; }
830 $test_kdc_ca -a bx509 -A foo@${R} PKCS10:${objdir}/req \
831 PEM-FILE:${objdir}/pkinit-test.pem ||
832 { echo "Trivial offline CA test failed (CA)"; exit 2; }
833 cat ${objdir}/k.pem >> ${objdir}/pkinit-test.pem
834 ${kinit} -C PEM-FILE:${objdir}/pkinit-test.pem foo@${R} ||
835 { echo "Trivial offline CA test failed (PKINIT)"; exit 2; }
836 ${kgetcred} -H HTTP/${server}@${R} ||
837 { echo "Trivial offline CA test failed (TGS)"; exit 2; }
838 KRB5CCNAME=$cache $gsstoken HTTP@$server | KRB5_KTNAME="$keytab" $gsstoken -r ||
839 { echo "Trivial offline CA test failed (gss-token)"; exit 2; }
841 # Check that we get up to three tixaddrs k/v in the log
842 grep 'REQ.*wrongaddr=true' ${objdir}/messages.log |
843 grep 'tixaddrs=IPv4:11.11.11.11' ||
844 { echo "KDC not warning about requests from wrong address"; exit 2; }
846 echo "Fetching a Negotiate token"
847 token=$(KRB5CCNAME=$cache $gsstoken HTTP@$server)
848 csr=
849 if (set -vx;
850 get_with_token get-negotiate-token "target=HTTP%40${server}" -o "${objdir}/negotiate-token"); then
851 # bx509 sends us a token w/o a newline for now; we add one because
852 # gss-token expects it.
853 test -s negotiate-token && echo >> negotiate-token
854 if test -s negotiate-token && KRB5_KTNAME="$keytab" $gsstoken -Nr < negotiate-token; then
855 echo 'Successfully obtained a Negotiate token!'
856 else
857 echo 'Failed to get a Negotiate token (got an unacceptable token)!'
858 exit 1
860 else
861 echo 'Failed to get a Negotiate token!'
862 exit 1
865 referer=https://${otherserver}/blah
866 redirect=$(${rkvis} -h https://${otherserver}/blah?q=whatever)
867 if (set -vx;
868 curl -o negotiate-token -Lgsf \
869 --resolve ${server}:${bx509port}:127.0.0.1 \
870 -H "Authorization: Negotiate $token" \
871 "http://${server}:${bx509port}/bnegotiate?target=HTTP%40${server}&redirect=${redirect}"); then
872 echo "Error: /bnegotiate with target and redirect succeeded"
873 exit 1
876 if (set -vx;
877 curl -o negotiate-token -Lgsf \
878 --resolve ${server}:${bx509port}:127.0.0.1 \
879 -H "Authorization: Negotiate $token" \
880 "http://${server}:${bx509port}/bnegotiate?redirect=${redirect}"); then
881 echo "Error: /bnegotiate with redirect but no Referer succeeded"
882 exit 1
885 referer=http://${otherserver}/blah
886 redirect=$(${rkvis} -h http://${otherserver}/blah?q=whatever)
887 if (set -vx;
888 curl -gsf \
889 --resolve ${server}:${bx509port}:127.0.0.1 \
890 -H "Authorization: Negotiate $token" \
891 -H "Referer: $referer" \
892 "http://${server}:${bx509port}/bnegotiate?redirect=${redirect}"); then
893 echo "Error: redirect for non-https referer"
894 exit 1
897 referer=https://${otherserver}/blah
898 redirect=$(${rkvis} -h https://${otherserver}/blah?q=whatever)
899 if (set -vx;
900 curl -gfs -D curlheaders \
901 --resolve ${server}:${bx509port}:127.0.0.1 \
902 -H "Authorization: Negotiate $token" \
903 -H "Referer: $referer" \
904 "http://${server}:${bx509port}/bnegotiate?redirect=${redirect}"); then
905 read junk code junk < curlheaders
906 if test "$code" = 307; then
907 echo "Got a proper redirect"
908 else
909 echo "Error: unexpected status code $code (wanted 307)"
911 else
912 echo "Error: no redirect"
913 exit 1
916 echo "killing kdc (${kdcpid}) and bx509d (${bx509pid})"
917 sh ${leaks_kill} kdc $kdcpid || ec=1
918 sh ${leaks_kill} bx509d $bx509pid || ec=1
920 trap "" EXIT
922 exit $ec