bx509d: Add /get-tgts batch end-point
In order to support batch jobs systems that run many users' jobs and which jobs need credentials, we add a /get-tgts end-point that is a batched version of the /get-tgt end-point. This end-point returns JSON. Also, we make GETs optional, default to not-allowed in preference of POSTs. We also correct handling of POST (before POSTs with non-zero-length bodies would cause the server to close the connection), and add additional CSRF protection features, including the ability to disable all GET requests for /get-keys and /get-config.
This commit is contained in:
@@ -44,6 +44,7 @@ bx509d_LDADD = -ldl \
|
||||
$(MICROHTTPD_LIBS) \
|
||||
$(LIB_roken) \
|
||||
$(LIB_heimbase) \
|
||||
$(LIB_hcrypto) \
|
||||
$(top_builddir)/lib/sl/libsl.la \
|
||||
$(top_builddir)/lib/asn1/libasn1.la \
|
||||
$(top_builddir)/lib/krb5/libkrb5.la \
|
||||
|
257
kdc/bx509d.8
257
kdc/bx509d.8
@@ -40,7 +40,11 @@
|
||||
.Op Fl Fl version
|
||||
.Op Fl H Ar HOSTNAME
|
||||
.Op Fl d | Fl Fl daemon
|
||||
.Op Fl Fl daemon-child
|
||||
.Op Fl Fl allow-GET
|
||||
.Op Fl Fl no-allow-GET
|
||||
.Op Fl Fl csrf-protection-type= Ns Ar CSRF-PROTECTION-TYPE
|
||||
.Op Fl Fl csrf-header= Ns Ar HEADER-NAME
|
||||
.Op Fl Fl csrf-key-file= Ns Ar FILE
|
||||
.Op Fl Fl reverse-proxied
|
||||
.Op Fl p Ar port number (default: 443)
|
||||
.Op Fl Fl cache-dir= Ns Ar DIRECTORY
|
||||
@@ -53,11 +57,24 @@
|
||||
.Oc
|
||||
.Sh DESCRIPTION
|
||||
Serves RESTful (HTTPS) GETs of
|
||||
.Ar /bx509 and
|
||||
.Ar /bnegotiate ,
|
||||
end-points
|
||||
performing corresponding kx509 and, possibly, PKINIT requests
|
||||
to the KDCs of the requested realms (or just the given REALM).
|
||||
.Ar /get-cert ,
|
||||
.Ar /get-tgt ,
|
||||
.Ar /get-tgts ,
|
||||
and
|
||||
.Ar /get-negotiate-token ,
|
||||
end-points that implement various experimental Heimdal features:
|
||||
.Bl -bullet -compact -offset indent
|
||||
.It
|
||||
.Li an online CA service over HTTPS,
|
||||
.It
|
||||
.Li a kinit-over-HTTPS service, and
|
||||
.It
|
||||
.Li a Negotiate token over HTTPS service.
|
||||
.El
|
||||
.Pp
|
||||
As well, a
|
||||
.Ar /health
|
||||
service can be used for checking service status.
|
||||
.Pp
|
||||
Supported options:
|
||||
.Bl -tag -width Ds
|
||||
@@ -75,6 +92,64 @@ Print version.
|
||||
.Xc
|
||||
Expected audience(s) of bearer tokens (i.e., acceptor name).
|
||||
.It Xo
|
||||
.Fl Fl allow-GET
|
||||
.Xc
|
||||
If given, then HTTP GET will be allowed for the various
|
||||
end-points other than
|
||||
.Ar /health .
|
||||
Otherwise only HEAD and POST will be allowed.
|
||||
By default GETs are allowed, but this will change soon.
|
||||
.It Xo
|
||||
.Fl Fl no-allow-GET
|
||||
.Xc
|
||||
If given then HTTP GETs will be rejected for the various
|
||||
end-points other than
|
||||
.Ar /health .
|
||||
.It Xo
|
||||
.Fl Fl csrf-protection-type= Ns Ar CSRF-PROTECTION-TYPE
|
||||
.Xc
|
||||
Possible values of
|
||||
.Ar CSRF-PROTECTION-TYPE
|
||||
are
|
||||
.Bl -bullet -compact -offset indent
|
||||
.It
|
||||
.Li GET-with-header
|
||||
.It
|
||||
.Li GET-with-token
|
||||
.It
|
||||
.Li POST-with-header
|
||||
.It
|
||||
.Li POST-with-token
|
||||
.El
|
||||
This may be given multiple times.
|
||||
The default is to require CSRF tokens for POST requests, and to
|
||||
require neither a non-simple header nor a CSRF token for GET
|
||||
requests.
|
||||
.Pp
|
||||
See
|
||||
.Sx CROSS-SITE REQUEST FORGERY PROTECTION .
|
||||
.It Xo
|
||||
.Fl Fl csrf-header= Ns Ar HEADER-NAME
|
||||
.Xc
|
||||
If given, then all requests other than to the
|
||||
.Ar /health
|
||||
service must have the given request
|
||||
.Ar HEADER-NAME
|
||||
set (the value is irrelevant).
|
||||
The
|
||||
.Ar HEADER-NAME
|
||||
must be a request header name such that a request having it makes
|
||||
it not a
|
||||
.Dq simple
|
||||
request (see the Cross-Origin Resource Sharing specification).
|
||||
Defaults to
|
||||
.Ar X-CSRF .
|
||||
.It Xo
|
||||
.Fl Fl csrf-key-file= Ns Ar FILE
|
||||
.Xc
|
||||
If given, this file must contain a 16 byte binary key for keying
|
||||
the HMAC used in CSRF token construction.
|
||||
.It Xo
|
||||
.Fl d ,
|
||||
.Fl Fl daemon
|
||||
.Xc
|
||||
@@ -82,7 +157,8 @@ Detach from TTY and run in the background.
|
||||
.It Xo
|
||||
.Fl Fl reverse-proxied
|
||||
.Xc
|
||||
Serves HTTP instead of HTTPS, accepting only looped-back connections.
|
||||
Serves HTTP instead of HTTPS, accepting only looped-back
|
||||
connections.
|
||||
.It Xo
|
||||
.Fl p Ar port number (default: 443)
|
||||
.Xc
|
||||
@@ -90,29 +166,106 @@ PORT
|
||||
.It Xo
|
||||
.Fl Fl cache-dir= Ns Ar DIRECTORY
|
||||
.Xc
|
||||
Directory for various caches. If not specified then a temporary directory will
|
||||
be made.
|
||||
Directory for various caches.
|
||||
If not specified then a temporary directory will be made.
|
||||
.It Xo
|
||||
.Fl Fl cert= Ns Ar HX509-STORE
|
||||
.Xc
|
||||
Certificate file path (PEM) for HTTPS service. May contain private key as
|
||||
well.
|
||||
Certificate file path (PEM) for HTTPS service.
|
||||
May contain private key as well.
|
||||
.It Xo
|
||||
.Fl Fl private-key= Ns Ar HX509-STORE
|
||||
.Xc
|
||||
Private key file path (PEM), if the private key is not stored along with the
|
||||
certificiate.
|
||||
Private key file path (PEM), if the private key is not stored
|
||||
along with the certificiate.
|
||||
.It Xo
|
||||
.Fl t ,
|
||||
.Fl Fl thread-per-client
|
||||
.Xc
|
||||
Uses a thread per-client instead of as many threads as there are CPUs.
|
||||
Uses a thread per-client instead of as many threads as there are
|
||||
CPUs.
|
||||
.It Xo
|
||||
.Fl v ,
|
||||
.Fl Fl verbose= Ns Ar run verbosely
|
||||
.Xc
|
||||
verbose
|
||||
.El
|
||||
.Sh HTTP APIS
|
||||
All HTTP APIs served by this program accept POSTs, with all
|
||||
request parameters given as URI query parameters and/or as
|
||||
form data in the POST request body, in either
|
||||
.Ar application/x-www-form-urlencoded
|
||||
or
|
||||
.Ar multipart/formdata .
|
||||
If request parameters are given both as URI query parameters
|
||||
and as POST forms, then they are merged into a set.
|
||||
.Pp
|
||||
If GETs are enabled, then request parameters must be supplied
|
||||
only as URI query parameters, as GET requests do not have request
|
||||
bodies.
|
||||
.Pp
|
||||
URI query parameters must be of the form
|
||||
.Ar param0=value¶m1=value...
|
||||
.Pp
|
||||
Some request parameters can only have one value.
|
||||
If multiple values are given for such parameters, then either an
|
||||
error will be produced, or only the first URI query parameter
|
||||
value will be used, or the first POST form data parameter will be
|
||||
used.
|
||||
Other request parameters can have multiple values.
|
||||
See below.
|
||||
.Sh CROSS-SITE REQUEST FORGERY PROTECTION
|
||||
.Em None
|
||||
of the resources service by this service are intended to be
|
||||
executed by web pages.
|
||||
.Pp
|
||||
All the resources provided by this service are
|
||||
.Dq safe
|
||||
in the sense that they do not change server-side state besides
|
||||
logging, and in that they are idempotent, but they are
|
||||
only safe to execute
|
||||
.Em if and only if
|
||||
the requesting party is trusted to see the response.
|
||||
Since none of these resources are intended to be used from web
|
||||
pages, it is important that web pages not be able to execute them
|
||||
.Em and
|
||||
observe the responses.
|
||||
.Pp
|
||||
In a web browser context, pages from other origins will be able
|
||||
to attempt requests to this service, but should never be able to
|
||||
see the responses because browsers normally wouldn't allow that.
|
||||
Nonetheless, anti cross site request forgery (CSRF) protection
|
||||
may be desirable.
|
||||
.Pp
|
||||
This service provides the following CSRF protection features:
|
||||
.Bl -tag -width Ds -offset indent
|
||||
.It requests are rejected if they have a
|
||||
.Dq Referer
|
||||
(except the experimental /get-negotiate-token end-point)
|
||||
.It the service can be configured to require a header that would make the
|
||||
request not Dq simple
|
||||
.It GETs can be disabled (see options), thus requiring POSTs
|
||||
.It GETs can be required to have a CSRF token (see below)
|
||||
.It POSTs can be required to have a CSRF token
|
||||
.El
|
||||
.Pp
|
||||
The experimental
|
||||
.Ar /get-negotiate-token
|
||||
end-point, however, always accepts
|
||||
.Dq Referer
|
||||
requests.
|
||||
.Pp
|
||||
To obtain a CSRF token, first execute the request without the
|
||||
CSRF token, and the resulting error
|
||||
response will include a
|
||||
.Ar X-CSRF-Token
|
||||
response header.
|
||||
.Pp
|
||||
To execute a request with a CSRF token, first obtain a CSRF token
|
||||
as described above, then copy the token to the request as the
|
||||
value of the request's
|
||||
.Ar X-CSRF-Token
|
||||
header.
|
||||
.Sh ONLINE CERTIFICATION AUTHORITY HTTP API
|
||||
This service provides an HTTP-based Certification Authority (CA).
|
||||
CA credentials and configuration are specified in the
|
||||
@@ -128,8 +281,8 @@ with the base-63 encoding of a DER encoding of a PKCS#10
|
||||
.Ar CertificationRequest
|
||||
(Certificate Signing Request, or CSR) in a
|
||||
.Ar csr
|
||||
required query parameter.
|
||||
In a successful query, the response body will contain a PEM
|
||||
required request parameter.
|
||||
In a successful request, the response body will contain a PEM
|
||||
encoded end entity certificate and certification chain.
|
||||
.Pp
|
||||
Or
|
||||
@@ -146,9 +299,9 @@ Unauthorized requests will elicit a 403 response.
|
||||
.Pp
|
||||
Subject Alternative Names (SANs) and Extended Key Usage values
|
||||
may be requested, both in-band in the CSR as a requested
|
||||
extensions attribute, and/or via optional query parameters.
|
||||
extensions attribute, and/or via optional request parameters.
|
||||
.Pp
|
||||
Supported query parameters (separated by ampersands)
|
||||
Supported request parameters:
|
||||
.Bl -tag -width Ds -offset indent
|
||||
.It Li csr = Va base64-encoded-DER-encoded-CSR
|
||||
.It Li dNSName = Va hostname
|
||||
@@ -178,20 +331,20 @@ of
|
||||
.Ar /get-negotiate-token
|
||||
with a
|
||||
.Ar target = Ar service@host
|
||||
query parameter.
|
||||
request parameter.
|
||||
.Pp
|
||||
In a successful query, the response body will contain a Negotiate
|
||||
token for the authenticated client principal to the requested
|
||||
target.
|
||||
In a successful request, the response body will contain a
|
||||
Negotiate token for the authenticated client principal to the
|
||||
requested target.
|
||||
.Pp
|
||||
Authentication is required.
|
||||
Unauthenticated requests will elicit a 401 response.
|
||||
.Pp
|
||||
Subject Alternative Names (SANs) and Extended Key Usage values
|
||||
may be requested, both in-band in the CSR as a requested
|
||||
extensions attribute, and/or via optional query parameters.
|
||||
extensions attribute, and/or via optional request parameters.
|
||||
.Pp
|
||||
Supported query parameters (separated by ampersands)
|
||||
Supported request parameters:
|
||||
.Bl -tag -width Ds -offset indent
|
||||
.It Li target = Va service@hostname
|
||||
.It Li redirect = Va URI
|
||||
@@ -221,13 +374,14 @@ The protocol consists of a
|
||||
of
|
||||
.Ar /get-tgt .
|
||||
.Pp
|
||||
Supported query parameters (separated by ampersands)
|
||||
Supported request parameters:
|
||||
.Bl -tag -width Ds -offset indent
|
||||
.It Li cname = Va principal-name
|
||||
.It Li address = Va IP-address
|
||||
.It Li lifetime = Va relative-time
|
||||
.El
|
||||
.Pp
|
||||
In a successful query, the response body will contain a TGT and
|
||||
In a successful request, the response body will contain a TGT and
|
||||
its session key encoded as a "ccache" file contents.
|
||||
.Pp
|
||||
Authentication is required.
|
||||
@@ -239,13 +393,14 @@ same as for
|
||||
by the authenticated client principal to get a certificate with
|
||||
a PKINIT SAN for itself or the requested principal if a
|
||||
.Va cname
|
||||
query parameter was included.
|
||||
request parameter was included.
|
||||
.Pp
|
||||
Unauthorized requests will elicit a 403 response.
|
||||
.Pp
|
||||
Requested IP addresses will be added to the issued TGT if allowed.
|
||||
The IP address of the client will be included if address-less TGTs
|
||||
are not allowed.
|
||||
Requested IP addresses will be added to the issued TGT if
|
||||
allowed.
|
||||
The IP address of the client will be included if address-less
|
||||
TGTs are not allowed.
|
||||
See the
|
||||
.Va [get-tgt]
|
||||
section of
|
||||
@@ -257,6 +412,48 @@ end-point, but as configured in the
|
||||
.Va [get-tgt]
|
||||
section of
|
||||
.Xr krb5.conf 5 .
|
||||
.Sh BATCH TGT HTTP API
|
||||
Some sites may have special users that operate batch jobs systems
|
||||
and that can impersonate many others by obtaining TGTs for them,
|
||||
and which
|
||||
.Dq prestash
|
||||
credentials for those users in their credentials caches.
|
||||
To support these sytems, a
|
||||
.Ar GET
|
||||
of
|
||||
.Ar /get-tgts
|
||||
with multiple
|
||||
.Ar cname
|
||||
request parameters will return those principals' TGTs (if the
|
||||
caller is authorized).
|
||||
.Pp
|
||||
This is similar to the
|
||||
.Ar /get-tgt
|
||||
end-point, but a) multiple
|
||||
.Ar cname
|
||||
request parameter values may be given, and b) the caller's
|
||||
principal name is not used as a default for the
|
||||
.Ar cname
|
||||
request parameter.
|
||||
The
|
||||
.Ar address
|
||||
and
|
||||
.Ar lifetime
|
||||
request parameters are honored.
|
||||
.Pp
|
||||
For successful
|
||||
.Ar GETs
|
||||
the response body is a sequence of JSON texts each of which is a
|
||||
JSON object with two keys:
|
||||
.Bl -tag -width Ds -offset indent
|
||||
.It Ar ccache
|
||||
with a base64-encoded FILE-type ccache;
|
||||
.It Ar name
|
||||
the name of the principal whose credentials are in that ccache.
|
||||
.El
|
||||
.Sh NOTES
|
||||
A future release may split all these end-points into separate
|
||||
services.
|
||||
.Sh ENVIRONMENT
|
||||
.Bl -tag -width Ds
|
||||
.It Ev KRB5_CONFIG
|
||||
|
1031
kdc/bx509d.c
1031
kdc/bx509d.c
File diff suppressed because it is too large
Load Diff
@@ -42,18 +42,21 @@ testfailed="echo test failed; cat messages.log; exit 1"
|
||||
# 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@
|
||||
|
||||
kadmin="${kadmin} -l -r $R"
|
||||
bx509d="${bx509d} --reverse-proxied -p $bx509port"
|
||||
kdc="${kdc} --addresses=localhost -P $port"
|
||||
|
||||
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"
|
||||
@@ -131,6 +134,55 @@ get_cert() {
|
||||
"$@" "$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} ||
|
||||
@@ -292,15 +344,15 @@ ${kadmin} init \
|
||||
${R} || exit 1
|
||||
${kadmin} add -r --use-defaults foo@${R} || exit 1
|
||||
${kadmin} add -r --use-defaults bar@${R} || exit 1
|
||||
${kadmin} add -r --use-defaults baz@${R} || exit 1
|
||||
${kadmin} modify --pkinit-acl="CN=foo,DC=test,DC=h5l,DC=se" foo@${R} || exit 1
|
||||
|
||||
|
||||
echo "Starting bx509d"
|
||||
${bx509d} -H $server --cert=${objdir}/bx509.pem -t --daemon ||
|
||||
{ echo "bx509 failed to start"; exit 2; }
|
||||
${bx509d} --daemon || { echo "bx509 failed to start"; exit 2; }
|
||||
bx509pid=`getpid bx509d`
|
||||
|
||||
trap "kill -9 ${bx509pid}; echo signal killing bx509d; exit 1;" EXIT
|
||||
trap 'kill -9 ${bx509pid}; echo signal killing bx509d; exit 1;' EXIT
|
||||
ec=0
|
||||
|
||||
rm -f trivial.pem server.pem email.pem
|
||||
@@ -310,12 +362,25 @@ csr_revoke
|
||||
$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; }
|
||||
csr=$($rkbase64 -- ${objdir}/req | $rkvis -h --stdin)
|
||||
|
||||
# XXX Add autoconf check for curl?
|
||||
# Create a barebones bx509 HTTP/1.1 client test program?
|
||||
|
||||
echo "Fetching a trivial user certificate (no authentication, must fail)"
|
||||
# Encode the CSR in base64, then URL-encode it
|
||||
csr=$($rkbase64 -- ${objdir}/req | $rkvis -h --stdin)
|
||||
if (set -vx;
|
||||
curl -g --resolve ${server}:${bx509port}:127.0.0.1 \
|
||||
-sf -o "${objdir}/trivial.pem" \
|
||||
"http://${server}:${bx509port}/bx509?csr=$csr"); then
|
||||
$hxtool print --content "FILE:${objdir}/trivial.pem"
|
||||
echo 'Got a certificate without authenticating!'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Fetching a trivial user certificate"
|
||||
# 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 '' -sf -o "${objdir}/trivial.pem"); then
|
||||
$hxtool print --content "FILE:${objdir}/trivial.pem"
|
||||
@@ -336,6 +401,43 @@ else
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Fetching a trivial user certificate (with POST, no auth, must fail)"
|
||||
# Encode the CSR in base64; curl will URL-encode it for us
|
||||
csr=$($rkbase64 -- ${objdir}/req)
|
||||
if (set -vx;
|
||||
curl -fg --resolve ${server}:${bx509port}:127.0.0.1 \
|
||||
-X POST -D response-headers \
|
||||
-F csr="$csr" -o "${objdir}/trivial.pem" \
|
||||
"http://${server}:${bx509port}/bx509" ); then
|
||||
$hxtool print --content "FILE:${objdir}/trivial.pem"
|
||||
echo 'Got a certificate without authenticating!'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Fetching a trivial user certificate (with POST)"
|
||||
# Encode the CSR in base64; curl will URL-encode it for us
|
||||
csr=$($rkbase64 -- ${objdir}/req)
|
||||
token=$(KRB5CCNAME=$cache $gsstoken HTTP@$server)
|
||||
if (set -vx;
|
||||
get_via_POST bx509 -F csr="$csr" -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 "Checking that authorization is enforced"
|
||||
csr_revoke
|
||||
get_cert '&rfc822Name=foo@bar.example' -vvv -o "${objdir}/bad1.pem"
|
||||
@@ -430,10 +532,10 @@ ${kadmin} ext_keytab -r -k $ukeytab foo@${R} || exit 1
|
||||
echo "Starting kdc";
|
||||
${kdc} --detach --testing || { echo "kdc failed to start"; cat messages.log; exit 1; }
|
||||
kdcpid=`getpid kdc`
|
||||
trap "kill -9 ${kdcpid} ${bx509pid}; echo signal killing kdc and bx509d; exit 1;" EXIT
|
||||
trap 'kill -9 ${kdcpid} ${bx509pid}; echo signal killing kdc and bx509d; exit 1;' EXIT
|
||||
|
||||
${kinit} -kt $ukeytab foo@${R} || exit 1
|
||||
$klist || { echo "failed to setup kimpersonate credentials"; exit 2; }
|
||||
$klist || { echo "failed to kinit"; exit 2; }
|
||||
|
||||
echo "Fetch TGT (not granted for other)"
|
||||
token=$(KRB5CCNAME=$cache $gsstoken HTTP@$server)
|
||||
@@ -474,7 +576,7 @@ if ! (set -vx;
|
||||
exit 2
|
||||
fi
|
||||
${kgetcred} -H HTTP/${server}@${R} ||
|
||||
{ echo "Trivial offline CA test failed (TGS)"; exit 2; }
|
||||
{ echo "Fetched TGT didn't work"; exit 2; }
|
||||
${klist} | grep Addresses:.IPv4:8.8.8.8 ||
|
||||
{ echo "Failed to get a TGT with /get-tgt end-point with addresses"; exit 2; }
|
||||
|
||||
@@ -491,7 +593,7 @@ if ! (set -vx;
|
||||
exit 2
|
||||
fi
|
||||
${kgetcred} -H HTTP/${server}@${R} ||
|
||||
{ echo "Trivial offline CA test failed (TGS)"; exit 2; }
|
||||
{ echo "Fetched TGT didn't work"; exit 2; }
|
||||
${klist} | grep Addresses:.IPv4:8.8.8.8 ||
|
||||
{ echo "Failed to get a TGT with /get-tgt end-point with addresses"; exit 2; }
|
||||
|
||||
@@ -509,7 +611,7 @@ if ! (set -vx;
|
||||
exit 2
|
||||
fi
|
||||
${kgetcred} -H HTTP/${server}@${R} ||
|
||||
{ echo "Trivial offline CA test failed (TGS)"; exit 2; }
|
||||
{ echo "Fetched TGT didn't work"; exit 2; }
|
||||
if which jq >/dev/null; then
|
||||
if ! ${klistjson} | jq -e '
|
||||
(reduce (.tickets[0]|(.Issued,.Expires)|
|
||||
@@ -535,7 +637,7 @@ if ! (set -vx;
|
||||
exit 2
|
||||
fi
|
||||
${kgetcred} -H HTTP/${server}@${R} ||
|
||||
{ echo "Trivial offline CA test failed (TGS)"; exit 2; }
|
||||
{ echo "Fetched TGT didn't work"; exit 2; }
|
||||
if which jq >/dev/null; then
|
||||
if ! ${klistjson} | jq -e '
|
||||
(reduce (.tickets[0]|(.Issued,.Expires)|
|
||||
@@ -561,7 +663,7 @@ if ! (set -vx;
|
||||
exit 2
|
||||
fi
|
||||
${kgetcred} -H HTTP/${server}@${R} ||
|
||||
{ echo "Trivial offline CA test failed (TGS)"; exit 2; }
|
||||
{ echo "Fetched TGT didn't work"; exit 2; }
|
||||
if which jq >/dev/null; then
|
||||
if ! ${klistjson} | jq -e '
|
||||
(reduce (.tickets[0]|(.Issued,.Expires)|
|
||||
@@ -573,6 +675,153 @@ if which jq >/dev/null; then
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Fetch TGTs (batch, authz fail)"
|
||||
${kadmin} modify --max-ticket-life=10d krbtgt/${R}@${R}
|
||||
(set -vx; csr_grant pkinit bar@${R} foo@${R})
|
||||
${kdestroy}
|
||||
token=$(KRB5CCNAME=$cache2 $gsstoken HTTP@$server)
|
||||
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
|
||||
echo "Got TGTs with /get-tgts end-point that should have been denied"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
echo "Fetch TGTs (batch, authz pass)"
|
||||
${kadmin} modify --max-ticket-life=10d krbtgt/${R}@${R}
|
||||
(csr_grant pkinit bar@${R} foo@${R})
|
||||
(csr_grant 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 pkinit bar@${R} foo@${R})
|
||||
(csr_grant pkinit baz@${R} foo@${R})
|
||||
(csr_grant 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 \
|
||||
@@ -596,11 +845,9 @@ grep 'REQ.*wrongaddr=true' ${objdir}/messages.log |
|
||||
|
||||
echo "Fetching a Negotiate token"
|
||||
token=$(KRB5CCNAME=$cache $gsstoken HTTP@$server)
|
||||
csr=
|
||||
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}"); then
|
||||
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
|
||||
|
Reference in New Issue
Block a user