| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,349 @@ | ||
| # | ||
| # Copyright (c) 2017 Red Hat | ||
| # | ||
| # Authors: Jakub Jelen <jjelen@redhat.com> | ||
| # | ||
| # Permission to use, copy, modify, and distribute this software for any | ||
| # purpose with or without fee is hereby granted, provided that the above | ||
| # copyright notice and this permission notice appear in all copies. | ||
| # | ||
| # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
| # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
| # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
| # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
| # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
| # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
| # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
|
|
||
| tid="pkcs11 tests with soft token" | ||
|
|
||
| try_token_libs() { | ||
| for _lib in "$@" ; do | ||
| if test -f "$_lib" ; then | ||
| verbose "Using token library $_lib" | ||
| TEST_SSH_PKCS11="$_lib" | ||
| return | ||
| fi | ||
| done | ||
| echo "skipped: Unable to find PKCS#11 token library" | ||
| exit 0 | ||
| } | ||
|
|
||
| try_token_libs \ | ||
| /usr/local/lib/softhsm/libsofthsm2.so \ | ||
| /usr/lib64/pkcs11/libsofthsm2.so \ | ||
| /usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so | ||
|
|
||
| TEST_SSH_PIN=1234 | ||
| TEST_SSH_SOPIN=12345678 | ||
| if [ "x$TEST_SSH_SSHPKCS11HELPER" != "x" ]; then | ||
| SSH_PKCS11_HELPER="${TEST_SSH_SSHPKCS11HELPER}" | ||
| export SSH_PKCS11_HELPER | ||
| fi | ||
|
|
||
| test -f "$TEST_SSH_PKCS11" || fatal "$TEST_SSH_PKCS11 does not exist" | ||
|
|
||
| # setup environment for softhsm token | ||
| DIR=$OBJ/SOFTHSM | ||
| rm -rf $DIR | ||
| TOKEN=$DIR/tokendir | ||
| mkdir -p $TOKEN | ||
| SOFTHSM2_CONF=$DIR/softhsm2.conf | ||
| export SOFTHSM2_CONF | ||
| cat > $SOFTHSM2_CONF << EOF | ||
| # SoftHSM v2 configuration file | ||
| directories.tokendir = ${TOKEN} | ||
| objectstore.backend = file | ||
| # ERROR, WARNING, INFO, DEBUG | ||
| log.level = DEBUG | ||
| # If CKF_REMOVABLE_DEVICE flag should be set | ||
| slots.removable = false | ||
| EOF | ||
| out=$(softhsm2-util --init-token --free --label token-slot-0 --pin "$TEST_SSH_PIN" --so-pin "$TEST_SSH_SOPIN") | ||
| slot=$(echo -- $out | sed 's/.* //') | ||
|
|
||
| # prevent ssh-agent from calling ssh-askpass | ||
| SSH_ASKPASS=/usr/bin/true | ||
| export SSH_ASKPASS | ||
| unset DISPLAY | ||
| # We need interactive access to test PKCS# since it prompts for PIN | ||
| sed -i 's/.*BatchMode.*//g' $OBJ/ssh_proxy | ||
|
|
||
| # start command w/o tty, so ssh accepts pin from stdin (from agent-pkcs11.sh) | ||
| notty() { | ||
| perl -e 'use POSIX; POSIX::setsid(); | ||
| if (fork) { wait; exit($? >> 8); } else { exec(@ARGV) }' "$@" | ||
| } | ||
|
|
||
| trace "generating keys" | ||
| ID1="02" | ||
| ID2="04" | ||
| RSA=${DIR}/RSA | ||
| EC=${DIR}/EC | ||
| openssl genpkey -algorithm rsa > $RSA | ||
| openssl pkcs8 -nocrypt -in $RSA |\ | ||
| softhsm2-util --slot "$slot" --label "SSH RSA Key $ID1" --id $ID1 \ | ||
| --pin "$TEST_SSH_PIN" --import /dev/stdin | ||
| openssl genpkey \ | ||
| -genparam \ | ||
| -algorithm ec \ | ||
| -pkeyopt ec_paramgen_curve:prime256v1 |\ | ||
| openssl genpkey \ | ||
| -paramfile /dev/stdin > $EC | ||
| openssl pkcs8 -nocrypt -in $EC |\ | ||
| softhsm2-util --slot "$slot" --label "SSH ECDSA Key $ID2" --id $ID2 \ | ||
| --pin "$TEST_SSH_PIN" --import /dev/stdin | ||
|
|
||
| trace "List the keys in the ssh-keygen with PKCS#11 URIs" | ||
| ${SSHKEYGEN} -D ${TEST_SSH_PKCS11} > $OBJ/token_keys | ||
| if [ $? -ne 0 ]; then | ||
| fail "FAIL: keygen fails to enumerate keys on PKCS#11 token" | ||
| fi | ||
| grep "pkcs11:" $OBJ/token_keys > /dev/null | ||
| if [ $? -ne 0 ]; then | ||
| fail "FAIL: The keys from ssh-keygen do not contain PKCS#11 URI as a comment" | ||
| fi | ||
|
|
||
| # Set the ECDSA key to authorized keys | ||
| grep "ECDSA" $OBJ/token_keys > $OBJ/authorized_keys_$USER | ||
|
|
||
| trace "Simple connect with ssh (without PKCS#11 URI)" | ||
| echo ${TEST_SSH_PIN} | notty ${SSH} -I ${TEST_SSH_PKCS11} \ | ||
| -F $OBJ/ssh_proxy somehost exit 5 | ||
| r=$? | ||
| if [ $r -ne 5 ]; then | ||
| fail "FAIL: ssh connect with pkcs11 failed (exit code $r)" | ||
| fi | ||
|
|
||
| trace "Connect with PKCS#11 URI" | ||
| trace " (ECDSA key should succeed)" | ||
| echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \ | ||
| -i "pkcs11:id=%${ID2}?module-path=${TEST_SSH_PKCS11}" somehost exit 5 | ||
| r=$? | ||
| if [ $r -ne 5 ]; then | ||
| fail "FAIL: ssh connect with PKCS#11 URI failed (exit code $r)" | ||
| fi | ||
|
|
||
| trace " (RSA key should fail)" | ||
| echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \ | ||
| -i "pkcs11:id=%${ID1}?module-path=${TEST_SSH_PKCS11}" somehost exit 5 | ||
| r=$? | ||
| if [ $r -eq 5 ]; then | ||
| fail "FAIL: ssh connect with PKCS#11 URI succeeded (should fail)" | ||
| fi | ||
|
|
||
| trace "Connect with PKCS#11 URI including PIN should not prompt" | ||
| trace " (ECDSA key should succeed)" | ||
| ${SSH} -F $OBJ/ssh_proxy -i \ | ||
| "pkcs11:id=%${ID2}?module-path=${TEST_SSH_PKCS11}&pin-value=${TEST_SSH_PIN}" somehost exit 5 | ||
| r=$? | ||
| if [ $r -ne 5 ]; then | ||
| fail "FAIL: ssh connect with PKCS#11 URI failed (exit code $r)" | ||
| fi | ||
|
|
||
| trace " (RSA key should fail)" | ||
| ${SSH} -F $OBJ/ssh_proxy -i \ | ||
| "pkcs11:id=%${ID1}?module-path=${TEST_SSH_PKCS11}&pin-value=${TEST_SSH_PIN}" somehost exit 5 | ||
| r=$? | ||
| if [ $r -eq 5 ]; then | ||
| fail "FAIL: ssh connect with PKCS#11 URI succeeded (should fail)" | ||
| fi | ||
|
|
||
| trace "Connect with various filtering options in PKCS#11 URI" | ||
| trace " (by object label, ECDSA should succeed)" | ||
| echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \ | ||
| -i "pkcs11:object=SSH%20ECDSA%20Key%2004?module-path=${TEST_SSH_PKCS11}" somehost exit 5 | ||
| r=$? | ||
| if [ $r -ne 5 ]; then | ||
| fail "FAIL: ssh connect with PKCS#11 URI failed (exit code $r)" | ||
| fi | ||
|
|
||
| trace " (by object label, RSA key should fail)" | ||
| echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \ | ||
| -i "pkcs11:object=SSH%20RSA%20Key%2002?module-path=${TEST_SSH_PKCS11}" somehost exit 5 | ||
| r=$? | ||
| if [ $r -eq 5 ]; then | ||
| fail "FAIL: ssh connect with PKCS#11 URI succeeded (should fail)" | ||
| fi | ||
|
|
||
| trace " (by token label, ECDSA key should succeed)" | ||
| echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \ | ||
| -i "pkcs11:id=%${ID2};token=token-slot-0?module-path=${TEST_SSH_PKCS11}" somehost exit 5 | ||
| r=$? | ||
| if [ $r -ne 5 ]; then | ||
| fail "FAIL: ssh connect with PKCS#11 URI failed (exit code $r)" | ||
| fi | ||
|
|
||
| trace " (by wrong token label, should fail)" | ||
| echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \ | ||
| -i "pkcs11:token=token-slot-99?module-path=${TEST_SSH_PKCS11}" somehost exit 5 | ||
| r=$? | ||
| if [ $r -eq 5 ]; then | ||
| fail "FAIL: ssh connect with PKCS#11 URI succeeded (should fail)" | ||
| fi | ||
|
|
||
|
|
||
|
|
||
|
|
||
| trace "Test PKCS#11 URI specification in configuration files" | ||
| echo "IdentityFile \"pkcs11:id=%${ID2}?module-path=${TEST_SSH_PKCS11}\"" \ | ||
| >> $OBJ/ssh_proxy | ||
| trace " (ECDSA key should succeed)" | ||
| echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy somehost exit 5 | ||
| r=$? | ||
| if [ $r -ne 5 ]; then | ||
| fail "FAIL: ssh connect with PKCS#11 URI in config failed (exit code $r)" | ||
| fi | ||
|
|
||
| # Set the RSA key as authorized | ||
| grep "RSA" $OBJ/token_keys > $OBJ/authorized_keys_$USER | ||
|
|
||
| trace " (RSA key should fail)" | ||
| echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy somehost exit 5 | ||
| r=$? | ||
| if [ $r -eq 5 ]; then | ||
| fail "FAIL: ssh connect with PKCS#11 URI in config succeeded (should fail)" | ||
| fi | ||
| sed -i -e "/IdentityFile/d" $OBJ/ssh_proxy | ||
|
|
||
| trace "Test PKCS#11 URI specification in configuration files with bogus spaces" | ||
| echo "IdentityFile \" pkcs11:?module-path=${TEST_SSH_PKCS11} \"" \ | ||
| >> $OBJ/ssh_proxy | ||
| echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy somehost exit 5 | ||
| r=$? | ||
| if [ $r -ne 5 ]; then | ||
| fail "FAIL: ssh connect with PKCS#11 URI with bogus spaces in config failed" \ | ||
| "(exit code $r)" | ||
| fi | ||
| sed -i -e "/IdentityFile/d" $OBJ/ssh_proxy | ||
|
|
||
|
|
||
| trace "Combination of PKCS11Provider and PKCS11URI on commandline" | ||
| trace " (RSA key should succeed)" | ||
| echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \ | ||
| -i "pkcs11:id=%${ID1}" -I ${TEST_SSH_PKCS11} somehost exit 5 | ||
| r=$? | ||
| if [ $r -ne 5 ]; then | ||
| fail "FAIL: ssh connect with PKCS#11 URI and provider combination" \ | ||
| "failed (exit code $r)" | ||
| fi | ||
|
|
||
| trace "Regress: Missing provider in PKCS11URI option" | ||
| ${SSH} -F $OBJ/ssh_proxy \ | ||
| -o IdentityFile=\"pkcs11:token=segfault\" somehost exit 5 | ||
| r=$? | ||
| if [ $r -eq 139 ]; then | ||
| fail "FAIL: ssh connect with missing provider_id from configuration option" \ | ||
| "crashed (exit code $r)" | ||
| fi | ||
|
|
||
|
|
||
| trace "SSH Agent can work with PKCS#11 URI" | ||
| trace "start the agent" | ||
| eval `${SSHAGENT} -s` > /dev/null | ||
|
|
||
| r=$? | ||
| if [ $r -ne 0 ]; then | ||
| fail "could not start ssh-agent: exit code $r" | ||
| else | ||
| trace "add whole provider to agent" | ||
| echo ${TEST_SSH_PIN} | notty ${SSHADD} \ | ||
| "pkcs11:?module-path=${TEST_SSH_PKCS11}" #> /dev/null 2>&1 | ||
| r=$? | ||
| if [ $r -ne 0 ]; then | ||
| fail "FAIL: ssh-add failed with whole provider: exit code $r" | ||
| fi | ||
|
|
||
| trace " pkcs11 list via agent (all keys)" | ||
| ${SSHADD} -l > /dev/null 2>&1 | ||
| r=$? | ||
| if [ $r -ne 0 ]; then | ||
| fail "FAIL: ssh-add -l failed with whole provider: exit code $r" | ||
| fi | ||
|
|
||
| trace " pkcs11 connect via agent (all keys)" | ||
| ${SSH} -F $OBJ/ssh_proxy somehost exit 5 | ||
| r=$? | ||
| if [ $r -ne 5 ]; then | ||
| fail "FAIL: ssh connect failed with whole provider (exit code $r)" | ||
| fi | ||
|
|
||
| trace " remove pkcs11 keys (all keys)" | ||
| ${SSHADD} -d "pkcs11:?module-path=${TEST_SSH_PKCS11}" > /dev/null 2>&1 | ||
| r=$? | ||
| if [ $r -ne 0 ]; then | ||
| fail "FAIL: ssh-add -d failed with whole provider: exit code $r" | ||
| fi | ||
|
|
||
| trace "add only RSA key to the agent" | ||
| echo ${TEST_SSH_PIN} | notty ${SSHADD} \ | ||
| "pkcs11:id=%${ID1}?module-path=${TEST_SSH_PKCS11}" > /dev/null 2>&1 | ||
| r=$? | ||
| if [ $r -ne 0 ]; then | ||
| fail "FAIL ssh-add failed with RSA key: exit code $r" | ||
| fi | ||
|
|
||
| trace " pkcs11 connect via agent (RSA key)" | ||
| ${SSH} -F $OBJ/ssh_proxy somehost exit 5 | ||
| r=$? | ||
| if [ $r -ne 5 ]; then | ||
| fail "FAIL: ssh connect failed with RSA key (exit code $r)" | ||
| fi | ||
|
|
||
| trace " remove RSA pkcs11 key" | ||
| ${SSHADD} -d "pkcs11:id=%${ID1}?module-path=${TEST_SSH_PKCS11}" \ | ||
| > /dev/null 2>&1 | ||
| r=$? | ||
| if [ $r -ne 0 ]; then | ||
| fail "FAIL: ssh-add -d failed with RSA key: exit code $r" | ||
| fi | ||
|
|
||
| trace "add only ECDSA key to the agent" | ||
| echo ${TEST_SSH_PIN} | notty ${SSHADD} \ | ||
| "pkcs11:id=%${ID2}?module-path=${TEST_SSH_PKCS11}" > /dev/null 2>&1 | ||
| r=$? | ||
| if [ $r -ne 0 ]; then | ||
| fail "FAIL: ssh-add failed with second key: exit code $r" | ||
| fi | ||
|
|
||
| trace " pkcs11 connect via agent (ECDSA key should fail)" | ||
| ${SSH} -F $OBJ/ssh_proxy somehost exit 5 | ||
| r=$? | ||
| if [ $r -eq 5 ]; then | ||
| fail "FAIL: ssh connect passed with ECDSA key (should fail)" | ||
| fi | ||
|
|
||
| trace "add also the RSA key to the agent" | ||
| echo ${TEST_SSH_PIN} | notty ${SSHADD} \ | ||
| "pkcs11:id=%${ID1}?module-path=${TEST_SSH_PKCS11}" > /dev/null 2>&1 | ||
| r=$? | ||
| if [ $r -ne 0 ]; then | ||
| fail "FAIL: ssh-add failed with first key: exit code $r" | ||
| fi | ||
|
|
||
| trace " remove ECDSA pkcs11 key" | ||
| ${SSHADD} -d "pkcs11:id=%${ID2}?module-path=${TEST_SSH_PKCS11}" \ | ||
| > /dev/null 2>&1 | ||
| r=$? | ||
| if [ $r -ne 0 ]; then | ||
| fail "ssh-add -d failed with ECDSA key: exit code $r" | ||
| fi | ||
|
|
||
| trace " remove already-removed pkcs11 key should fail" | ||
| ${SSHADD} -d "pkcs11:id=%${ID2}?module-path=${TEST_SSH_PKCS11}" \ | ||
| > /dev/null 2>&1 | ||
| r=$? | ||
| if [ $r -eq 0 ]; then | ||
| fail "FAIL: ssh-add -d passed with non-existing key (should fail)" | ||
| fi | ||
|
|
||
| trace " pkcs11 connect via agent (the RSA key should be still usable)" | ||
| ${SSH} -F $OBJ/ssh_proxy somehost exit 5 | ||
| r=$? | ||
| if [ $r -ne 5 ]; then | ||
| fail "ssh connect failed with RSA key (after removing ECDSA): exit code $r" | ||
| fi | ||
|
|
||
| trace "kill agent" | ||
| ${SSHAGENT} -k > /dev/null | ||
| fi |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,337 @@ | ||
| /* | ||
| * Copyright (c) 2017 Red Hat | ||
| * | ||
| * Authors: Jakub Jelen <jjelen@redhat.com> | ||
| * | ||
| * Permission to use, copy, modify, and distribute this software for any | ||
| * purpose with or without fee is hereby granted, provided that the above | ||
| * copyright notice and this permission notice appear in all copies. | ||
| * | ||
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
| * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
| * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
| * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
| * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
| * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
| * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
| */ | ||
|
|
||
| #include "includes.h" | ||
|
|
||
| #include <locale.h> | ||
| #include <string.h> | ||
|
|
||
| #include "../test_helper/test_helper.h" | ||
|
|
||
| #include "sshbuf.h" | ||
| #include "ssh-pkcs11-uri.h" | ||
|
|
||
| #define EMPTY_URI compose_uri(NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL) | ||
|
|
||
| /* prototypes are not public -- specify them here internally for tests */ | ||
| struct sshbuf *percent_encode(const char *, size_t, char *); | ||
| int percent_decode(char *, char **); | ||
|
|
||
| void | ||
| compare_uri(struct pkcs11_uri *a, struct pkcs11_uri *b) | ||
| { | ||
| ASSERT_PTR_NE(a, NULL); | ||
| ASSERT_PTR_NE(b, NULL); | ||
| ASSERT_SIZE_T_EQ(a->id_len, b->id_len); | ||
| ASSERT_MEM_EQ(a->id, b->id, a->id_len); | ||
| if (b->object != NULL) | ||
| ASSERT_STRING_EQ(a->object, b->object); | ||
| else /* both should be null */ | ||
| ASSERT_PTR_EQ(a->object, b->object); | ||
| if (b->module_path != NULL) | ||
| ASSERT_STRING_EQ(a->module_path, b->module_path); | ||
| else /* both should be null */ | ||
| ASSERT_PTR_EQ(a->module_path, b->module_path); | ||
| if (b->token != NULL) | ||
| ASSERT_STRING_EQ(a->token, b->token); | ||
| else /* both should be null */ | ||
| ASSERT_PTR_EQ(a->token, b->token); | ||
| if (b->manuf != NULL) | ||
| ASSERT_STRING_EQ(a->manuf, b->manuf); | ||
| else /* both should be null */ | ||
| ASSERT_PTR_EQ(a->manuf, b->manuf); | ||
| if (b->lib_manuf != NULL) | ||
| ASSERT_STRING_EQ(a->lib_manuf, b->lib_manuf); | ||
| else /* both should be null */ | ||
| ASSERT_PTR_EQ(a->lib_manuf, b->lib_manuf); | ||
| } | ||
|
|
||
| void | ||
| check_parse_rv(char *uri, struct pkcs11_uri *expect, int expect_rv) | ||
| { | ||
| char *buf = NULL, *str; | ||
| struct pkcs11_uri *pkcs11uri = NULL; | ||
| int rv; | ||
|
|
||
| if (expect_rv == 0) | ||
| str = "Valid"; | ||
| else | ||
| str = "Invalid"; | ||
| asprintf(&buf, "%s PKCS#11 URI parsing: %s", str, uri); | ||
| TEST_START(buf); | ||
| free(buf); | ||
| pkcs11uri = pkcs11_uri_init(); | ||
| rv = pkcs11_uri_parse(uri, pkcs11uri); | ||
| ASSERT_INT_EQ(rv, expect_rv); | ||
| if (rv == 0) /* in case of failure result is undefined */ | ||
| compare_uri(pkcs11uri, expect); | ||
| pkcs11_uri_cleanup(pkcs11uri); | ||
| free(expect); | ||
| TEST_DONE(); | ||
| } | ||
|
|
||
| void | ||
| check_parse(char *uri, struct pkcs11_uri *expect) | ||
| { | ||
| check_parse_rv(uri, expect, 0); | ||
| } | ||
|
|
||
| struct pkcs11_uri * | ||
| compose_uri(unsigned char *id, size_t id_len, char *token, char *lib_manuf, | ||
| char *manuf, char *module_path, char *object, char *pin) | ||
| { | ||
| struct pkcs11_uri *uri = pkcs11_uri_init(); | ||
| if (id_len > 0) { | ||
| uri->id_len = id_len; | ||
| uri->id = id; | ||
| } | ||
| uri->module_path = module_path; | ||
| uri->token = token; | ||
| uri->lib_manuf = lib_manuf; | ||
| uri->manuf = manuf; | ||
| uri->object = object; | ||
| uri->pin = pin; | ||
| return uri; | ||
| } | ||
|
|
||
| static void | ||
| test_parse_valid(void) | ||
| { | ||
| /* path arguments */ | ||
| check_parse("pkcs11:id=%01", | ||
| compose_uri("\x01", 1, NULL, NULL, NULL, NULL, NULL, NULL)); | ||
| check_parse("pkcs11:id=%00%01", | ||
| compose_uri("\x00\x01", 2, NULL, NULL, NULL, NULL, NULL, NULL)); | ||
| check_parse("pkcs11:token=SSH%20Keys", | ||
| compose_uri(NULL, 0, "SSH Keys", NULL, NULL, NULL, NULL, NULL)); | ||
| check_parse("pkcs11:library-manufacturer=OpenSC", | ||
| compose_uri(NULL, 0, NULL, "OpenSC", NULL, NULL, NULL, NULL)); | ||
| check_parse("pkcs11:manufacturer=piv_II", | ||
| compose_uri(NULL, 0, NULL, NULL, "piv_II", NULL, NULL, NULL)); | ||
| check_parse("pkcs11:object=SIGN%20Key", | ||
| compose_uri(NULL, 0, NULL, NULL, NULL, NULL, "SIGN Key", NULL)); | ||
| /* query arguments */ | ||
| check_parse("pkcs11:?module-path=/usr/lib64/p11-kit-proxy.so", | ||
| compose_uri(NULL, 0, NULL, NULL, NULL, "/usr/lib64/p11-kit-proxy.so", NULL, NULL)); | ||
| check_parse("pkcs11:?pin-value=123456", | ||
| compose_uri(NULL, 0, NULL, NULL, NULL, NULL, NULL, "123456")); | ||
|
|
||
| /* combinations */ | ||
| /* ID SHOULD be percent encoded */ | ||
| check_parse("pkcs11:token=SSH%20Key;id=0", | ||
| compose_uri("0", 1, "SSH Key", NULL, NULL, NULL, NULL, NULL)); | ||
| check_parse( | ||
| "pkcs11:manufacturer=CAC?module-path=/usr/lib64/p11-kit-proxy.so", | ||
| compose_uri(NULL, 0, NULL, NULL, "CAC", | ||
| "/usr/lib64/p11-kit-proxy.so", NULL, NULL)); | ||
| check_parse( | ||
| "pkcs11:object=RSA%20Key?module-path=/usr/lib64/pkcs11/opencryptoki.so", | ||
| compose_uri(NULL, 0, NULL, NULL, NULL, | ||
| "/usr/lib64/pkcs11/opencryptoki.so", "RSA Key", NULL)); | ||
| check_parse("pkcs11:?module-path=/usr/lib64/p11-kit-proxy.so&pin-value=123456", | ||
| compose_uri(NULL, 0, NULL, NULL, NULL, "/usr/lib64/p11-kit-proxy.so", NULL, "123456")); | ||
|
|
||
| /* empty path component matches everything */ | ||
| check_parse("pkcs11:", EMPTY_URI); | ||
|
|
||
| /* empty string is a valid to match against (and different from NULL) */ | ||
| check_parse("pkcs11:token=", | ||
| compose_uri(NULL, 0, "", NULL, NULL, NULL, NULL, NULL)); | ||
| /* Percent character needs to be percent-encoded */ | ||
| check_parse("pkcs11:token=%25", | ||
| compose_uri(NULL, 0, "%", NULL, NULL, NULL, NULL, NULL)); | ||
| } | ||
|
|
||
| static void | ||
| test_parse_invalid(void) | ||
| { | ||
| /* Invalid percent encoding */ | ||
| check_parse_rv("pkcs11:id=%0", EMPTY_URI, -1); | ||
| /* Invalid percent encoding */ | ||
| check_parse_rv("pkcs11:id=%ZZ", EMPTY_URI, -1); | ||
| /* Space MUST be percent encoded -- XXX not enforced yet */ | ||
| check_parse("pkcs11:token=SSH Keys", | ||
| compose_uri(NULL, 0, "SSH Keys", NULL, NULL, NULL, NULL, NULL)); | ||
| /* MUST NOT contain duplicate attributes of the same name */ | ||
| check_parse_rv("pkcs11:id=%01;id=%02", EMPTY_URI, -1); | ||
| /* MUST NOT contain duplicate attributes of the same name */ | ||
| check_parse_rv("pkcs11:?pin-value=111111&pin-value=123456", EMPTY_URI, -1); | ||
| /* Unrecognized attribute in path are ignored with log message */ | ||
| check_parse("pkcs11:key_name=SSH", EMPTY_URI); | ||
| /* Unrecognized attribute in query SHOULD be ignored */ | ||
| check_parse("pkcs11:?key_name=SSH", EMPTY_URI); | ||
| } | ||
|
|
||
| void | ||
| check_gen(char *expect, struct pkcs11_uri *uri) | ||
| { | ||
| char *buf = NULL, *uri_str; | ||
|
|
||
| asprintf(&buf, "Valid PKCS#11 URI generation: %s", expect); | ||
| TEST_START(buf); | ||
| free(buf); | ||
| uri_str = pkcs11_uri_get(uri); | ||
| ASSERT_PTR_NE(uri_str, NULL); | ||
| ASSERT_STRING_EQ(uri_str, expect); | ||
| free(uri_str); | ||
| TEST_DONE(); | ||
| } | ||
|
|
||
| static void | ||
| test_generate_valid(void) | ||
| { | ||
| /* path arguments */ | ||
| check_gen("pkcs11:id=%01", | ||
| compose_uri("\x01", 1, NULL, NULL, NULL, NULL, NULL, NULL)); | ||
| check_gen("pkcs11:id=%00%01", | ||
| compose_uri("\x00\x01", 2, NULL, NULL, NULL, NULL, NULL, NULL)); | ||
| check_gen("pkcs11:token=SSH%20Keys", /* space must be percent encoded */ | ||
| compose_uri(NULL, 0, "SSH Keys", NULL, NULL, NULL, NULL, NULL)); | ||
| /* library-manufacturer is not implmented now */ | ||
| /*check_gen("pkcs11:library-manufacturer=OpenSC", | ||
| compose_uri(NULL, 0, NULL, "OpenSC", NULL, NULL, NULL, NULL));*/ | ||
| check_gen("pkcs11:manufacturer=piv_II", | ||
| compose_uri(NULL, 0, NULL, NULL, "piv_II", NULL, NULL, NULL)); | ||
| check_gen("pkcs11:object=RSA%20Key", | ||
| compose_uri(NULL, 0, NULL, NULL, NULL, NULL, "RSA Key", NULL)); | ||
| /* query arguments */ | ||
| check_gen("pkcs11:?module-path=/usr/lib64/p11-kit-proxy.so", | ||
| compose_uri(NULL, 0, NULL, NULL, NULL, "/usr/lib64/p11-kit-proxy.so", NULL, NULL)); | ||
|
|
||
| /* combinations */ | ||
| check_gen("pkcs11:id=%02;token=SSH%20Keys", | ||
| compose_uri("\x02", 1, "SSH Keys", NULL, NULL, NULL, NULL, NULL)); | ||
| check_gen("pkcs11:id=%EE%02?module-path=/usr/lib64/p11-kit-proxy.so", | ||
| compose_uri("\xEE\x02", 2, NULL, NULL, NULL, "/usr/lib64/p11-kit-proxy.so", NULL, NULL)); | ||
| check_gen("pkcs11:object=Encryption%20Key;manufacturer=piv_II", | ||
| compose_uri(NULL, 0, NULL, NULL, "piv_II", NULL, "Encryption Key", NULL)); | ||
|
|
||
| /* empty path component matches everything */ | ||
| check_gen("pkcs11:", EMPTY_URI); | ||
|
|
||
| } | ||
|
|
||
| void | ||
| check_encode(char *source, size_t len, char *whitelist, char *expect) | ||
| { | ||
| char *buf = NULL; | ||
| struct sshbuf *b; | ||
|
|
||
| asprintf(&buf, "percent_encode: expected %s", expect); | ||
| TEST_START(buf); | ||
| free(buf); | ||
|
|
||
| b = percent_encode(source, len, whitelist); | ||
| ASSERT_STRING_EQ(sshbuf_ptr(b), expect); | ||
| sshbuf_free(b); | ||
| TEST_DONE(); | ||
| } | ||
|
|
||
| static void | ||
| test_percent_encode_multibyte(void) | ||
| { | ||
| /* SHOULD be encoded as octets according to the UTF-8 character encoding */ | ||
|
|
||
| /* multi-byte characters are "for free" */ | ||
| check_encode("$", 1, "", "%24"); | ||
| check_encode("¢", 2, "", "%C2%A2"); | ||
| check_encode("€", 3, "", "%E2%82%AC"); | ||
| check_encode("𐍈", 4, "", "%F0%90%8D%88"); | ||
|
|
||
| /* CK_UTF8CHAR is unsigned char (1 byte) */ | ||
| /* labels SHOULD be normalized to NFC [UAX15] */ | ||
|
|
||
| } | ||
|
|
||
| static void | ||
| test_percent_encode(void) | ||
| { | ||
| /* Without whitelist encodes everything (for CKA_ID) */ | ||
| check_encode("A*", 2, "", "%41%2A"); | ||
| check_encode("\x00", 1, "", "%00"); | ||
| check_encode("\x7F", 1, "", "%7F"); | ||
| check_encode("\x80", 1, "", "%80"); | ||
| check_encode("\xff", 1, "", "%FF"); | ||
|
|
||
| /* Default whitelist encodes anything but safe letters */ | ||
| check_encode("test" "\x00" "0alpha", 11, PKCS11_URI_WHITELIST, | ||
| "test%000alpha"); | ||
| check_encode(" ", 1, PKCS11_URI_WHITELIST, | ||
| "%20"); /* Space MUST be percent encoded */ | ||
| check_encode("/", 1, PKCS11_URI_WHITELIST, | ||
| "%2F"); /* '/' delimiter MUST be percent encoded (in the path) */ | ||
| check_encode("?", 1, PKCS11_URI_WHITELIST, | ||
| "%3F"); /* delimiter '?' MUST be percent encoded (in the path) */ | ||
| check_encode("#", 1, PKCS11_URI_WHITELIST, | ||
| "%23"); /* '#' MUST be always percent encoded */ | ||
| check_encode("key=value;separator?query&#anch", 35, PKCS11_URI_WHITELIST, | ||
| "key%3Dvalue%3Bseparator%3Fquery%26amp%3B%23anch"); | ||
|
|
||
| /* Components in query can have '/' unencoded (useful for paths) */ | ||
| check_encode("/path/to.file", 13, PKCS11_URI_WHITELIST "/", | ||
| "/path/to.file"); | ||
| } | ||
|
|
||
| void | ||
| check_decode(char *source, char *expect, int expect_len) | ||
| { | ||
| char *buf = NULL, *out = NULL; | ||
| int rv; | ||
|
|
||
| asprintf(&buf, "percent_decode: %s", source); | ||
| TEST_START(buf); | ||
| free(buf); | ||
|
|
||
| rv = percent_decode(source, &out); | ||
| ASSERT_INT_EQ(rv, expect_len); | ||
| if (rv >= 0) | ||
| ASSERT_MEM_EQ(out, expect, expect_len); | ||
| free(out); | ||
| TEST_DONE(); | ||
| } | ||
|
|
||
| static void | ||
| test_percent_decode(void) | ||
| { | ||
| /* simple valid cases */ | ||
| check_decode("%00", "\x00", 1); | ||
| check_decode("%FF", "\xFF", 1); | ||
|
|
||
| /* normal strings shold be kept intact */ | ||
| check_decode("strings are left", "strings are left", 16); | ||
| check_decode("10%25 of trees", "10% of trees", 12); | ||
|
|
||
| /* make sure no more than 2 bytes are parsed */ | ||
| check_decode("%222", "\x22" "2", 2); | ||
|
|
||
| /* invalid expects failure */ | ||
| check_decode("%0", "", -1); | ||
| check_decode("%Z", "", -1); | ||
| check_decode("%FG", "", -1); | ||
| } | ||
|
|
||
| void | ||
| tests(void) | ||
| { | ||
| test_percent_encode(); | ||
| test_percent_encode_multibyte(); | ||
| test_percent_decode(); | ||
| test_parse_valid(); | ||
| test_parse_invalid(); | ||
| test_generate_valid(); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| /* | ||
| * Copyright (c) 2017 Red Hat | ||
| * | ||
| * Authors: Jakub Jelen <jjelen@redhat.com> | ||
| * | ||
| * Permission to use, copy, modify, and distribute this software for any | ||
| * purpose with or without fee is hereby granted, provided that the above | ||
| * copyright notice and this permission notice appear in all copies. | ||
| * | ||
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
| * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
| * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
| * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
| * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
| * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
| * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
| */ | ||
|
|
||
| #define PKCS11_URI_SCHEME "pkcs11:" | ||
| #define PKCS11_URI_WHITELIST "abcdefghijklmnopqrstuvwxyz" \ | ||
| "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ | ||
| "0123456789_-.()" | ||
|
|
||
| struct pkcs11_uri { | ||
| /* path */ | ||
| char *id; | ||
| size_t id_len; | ||
| char *token; | ||
| char *object; | ||
| char *lib_manuf; | ||
| char *manuf; | ||
| /* query */ | ||
| char *module_path; | ||
| char *pin; /* Only parsed, but not printed */ | ||
| }; | ||
|
|
||
| struct pkcs11_uri *pkcs11_uri_init(); | ||
| void pkcs11_uri_cleanup(struct pkcs11_uri *); | ||
| int pkcs11_uri_parse(const char *, struct pkcs11_uri *); | ||
| struct pkcs11_uri *pkcs11_uri_init(); | ||
| char *pkcs11_uri_get(struct pkcs11_uri *uri); | ||
|
|