diff --git a/examples/run_examples.sh b/examples/run_examples.sh index d40f7075..d33807ef 100755 --- a/examples/run_examples.sh +++ b/examples/run_examples.sh @@ -1013,9 +1013,64 @@ if [ $NO_FILESYSTEM -eq 0 ]; then ./examples/seal/seal sealedkeyblob.bin mySecretMessage >> $TPMPWD/run.out 2>&1 RESULT=$? [ $RESULT -ne 0 ] && echo -e "seal failed! $RESULT" && exit 1 - ./examples/seal/unseal message.raw sealedkeyblob.bin >> $TPMPWD/run.out 2>&1 + rm -f message.raw + for FILE_STATE in new existing; do + if [ "$FILE_STATE" = "existing" ]; then + chmod 666 message.raw + RESULT=$? + [ $RESULT -ne 0 ] && \ + echo -e "chmod unseal output failed! $RESULT" && exit 1 + fi + # Unsealed data must stay owner-only even under a permissive umask. + (umask 000 && ./examples/seal/unseal message.raw sealedkeyblob.bin) \ + >> $TPMPWD/run.out 2>&1 + RESULT=$? + [ $RESULT -ne 0 ] && \ + echo -e "unseal to $FILE_STATE file failed! $RESULT" && exit 1 + grep -qx "mySecretMessage" message.raw + RESULT=$? + [ $RESULT -ne 0 ] && \ + echo -e "unsealed data did not match! $RESULT" && exit 1 + FILE_MODE=$(stat -c %a message.raw 2>/dev/null) + if [ -z "$FILE_MODE" ]; then + FILE_MODE=$(stat -f %Lp message.raw 2>/dev/null) + fi + [ "$FILE_MODE" != "600" ] && \ + echo -e "$FILE_STATE unseal output permissions were $FILE_MODE," \ + "expected 600" && exit 1 + done + + rm -f unseal-target.raw unseal-symlink.raw unseal-hardlink.raw + printf '%s\n' "doNotOverwrite" > unseal-target.raw + ln -s unseal-target.raw unseal-symlink.raw + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "create unseal symlink failed! $RESULT" && \ + exit 1 + ./examples/seal/unseal unseal-symlink.raw sealedkeyblob.bin \ + >> $TPMPWD/run.out 2>&1 + RESULT=$? + [ $RESULT -eq 0 ] && echo -e "unseal to symlink should fail!" && exit 1 + grep -qx "doNotOverwrite" unseal-target.raw + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "unseal followed symlink! $RESULT" && exit 1 + rm -f unseal-symlink.raw + + ln unseal-target.raw unseal-hardlink.raw + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "create unseal hardlink failed! $RESULT" && \ + exit 1 + ./examples/seal/unseal unseal-hardlink.raw sealedkeyblob.bin \ + >> $TPMPWD/run.out 2>&1 + RESULT=$? + [ $RESULT -eq 0 ] && echo -e "unseal to hardlink should fail!" && exit 1 + grep -qx "doNotOverwrite" unseal-target.raw + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "unseal followed hardlink! $RESULT" && exit 1 + rm -f unseal-target.raw unseal-hardlink.raw + + ./examples/seal/unseal . sealedkeyblob.bin >> $TPMPWD/run.out 2>&1 RESULT=$? - [ $RESULT -ne 0 ] && echo -e "unseal failed! $RESULT" && exit 1 + [ $RESULT -eq 0 ] && echo -e "unseal to directory should fail!" && exit 1 rm -f sealedkeyblob.bin if [ $WOLFCRYPT_ENABLE -eq 1 ] && [ $WOLFCRYPT_RSA -eq 1 ]; then diff --git a/examples/seal/unseal.c b/examples/seal/unseal.c index ec126928..6ac6327f 100644 --- a/examples/seal/unseal.c +++ b/examples/seal/unseal.c @@ -28,6 +28,21 @@ #include #include +#if !defined(NO_FILESYSTEM) && !defined(NO_WRITE_TEMP_FILES) && \ + !defined(WOLFTPM_CUSTOM_STDIO) && \ + (defined(WOLFTPM2_NO_WOLFCRYPT) || defined(XFDOPEN)) && \ + (defined(__linux__) || defined(__APPLE__) || defined(__unix__)) + #define WOLFTPM_UNSEAL_POSIX_FILE_IO + #include + #include + #include + #ifndef XFDOPEN + #define XFDOPEN fdopen + #endif + #ifndef XCLOSE + #define XCLOSE close + #endif +#endif #if !defined(WOLFTPM2_NO_WRAPPER) && !defined(NO_FILESYSTEM) @@ -41,6 +56,66 @@ /* --- BEGIN TPM2.0 Unseal example --- */ /******************************************************************************/ +#ifndef NO_WRITE_TEMP_FILES +static XFILE openPrivateFileWrite(const char* filename) +{ +#ifdef WOLFTPM_UNSEAL_POSIX_FILE_IO + int fd; + int openFlags; + struct stat fileStat; + #ifndef O_NOFOLLOW + struct stat pathStat; + #endif + XFILE fp; + + openFlags = O_WRONLY | O_CREAT; + #ifdef O_CLOEXEC + openFlags |= O_CLOEXEC; + #endif + #ifdef O_NOFOLLOW + openFlags |= O_NOFOLLOW; + #endif + + fd = open(filename, openFlags, S_IRUSR | S_IWUSR); + if (fd < 0) + return XBADFILE; + + #ifndef O_NOFOLLOW + /* Confirm open() did not follow a link before changing the file. */ + if (lstat(filename, &pathStat) != 0) + goto exit; + #endif + + /* Validate before truncating so linked and special files stay untouched. */ + if (fstat(fd, &fileStat) != 0 || !S_ISREG(fileStat.st_mode) || + fileStat.st_uid != geteuid() || fileStat.st_nlink != 1) + goto exit; + + #ifndef O_NOFOLLOW + if (!S_ISREG(pathStat.st_mode) || + pathStat.st_dev != fileStat.st_dev || + pathStat.st_ino != fileStat.st_ino) + goto exit; + #endif + + if (fchmod(fd, S_IRUSR | S_IWUSR) != 0 || ftruncate(fd, 0) != 0) + goto exit; + + fp = XFDOPEN(fd, "wb"); + if (fp == XBADFILE) + goto exit; + return fp; + +exit: + (void)XCLOSE(fd); + return XBADFILE; +#else + /* The custom filesystem controls creation permissions on this path. */ + return XFOPEN(filename, "wb"); +#endif +} +#endif /* !NO_WRITE_TEMP_FILES */ + static void usage(void) { printf("Expected usage:\n"); @@ -167,7 +242,7 @@ int TPM2_Unseal_Example(void* userCtx, int argc, char *argv[]) #if !defined(NO_FILESYSTEM) && !defined(NO_WRITE_TEMP_FILES) /* Output the unsealed data to a file */ if (filename) { - fp = XFOPEN(filename, "wb"); + fp = openPrivateFileWrite(filename); if (fp == XBADFILE) { printf("Error opening %s for writing.\n", filename); rc = TPM_RC_FAILURE;