From bc3c7ef79f2841bc70a21148c8569bb2754b34dc Mon Sep 17 00:00:00 2001 From: Pat Riehecky Date: Wed, 16 Sep 2026 13:14:42 -0500 Subject: [PATCH] Split up mkdir from make keytab --- doc/kcron.doc | 11 +- fermilab-util_kcron.spec | 8 +- src/C/CMakeLists.txt | 49 ++- src/C/create-empty-client-keytab.c | 247 +++++++++++++++ src/C/init-kcron-keytab.c | 467 ----------------------------- src/C/init-kerberos-client-dir.c | 260 ++++++++++++++++ src/shell/kcron.sysconfig | 5 +- src/shell/kcrondestroy | 10 +- src/shell/kcroninit | 33 +- 9 files changed, 598 insertions(+), 492 deletions(-) create mode 100644 src/C/create-empty-client-keytab.c delete mode 100644 src/C/init-kcron-keytab.c create mode 100644 src/C/init-kerberos-client-dir.c diff --git a/doc/kcron.doc b/doc/kcron.doc index 89f7823..2d2644c 100644 --- a/doc/kcron.doc +++ b/doc/kcron.doc @@ -68,8 +68,11 @@ Configuration variables (sourced from these files): **FULLPRINCIPAL**:: Complete principal name in the form +username/cron/host.domain@REALM+. -**KEYTAB_INIT**:: - Path to the keytab initialization utility (default: +/usr/libexec/kcron/init-kcron-keytab+). +**KEYTAB_DIR_INIT**:: + Path to the keytab directory initialization utility (default: +/usr/libexec/kcron/init-kerberos-client-dir+). + +**KEYTAB_CREATE**:: + Path to the empty keytab creation utility (default: +/usr/libexec/kcron/create-empty-client-keytab+). **KEYTAB_NAME_UTIL**:: Path to the keytab name utility (default: +/usr/libexec/kcron/client-keytab-name+). @@ -106,9 +109,9 @@ Do you want to continue? (y/n)y == LIMITATIONS -The kcron helper utilities utilize filesystem capabilities(7) stored as xattr(7) values on the binaries. The application will not run as expected if your system does not support them. +The kcron helper utilities utilize filesystem capabilities(7) stored as xattr(7) values on the binaries. The application will not run as expected if your file system does not support them. -On modern Linux systems, these are enabled by default. +On modern Linux systems, these are enabled by default for most volumes. NFS does not support capabilities(7) nor should it. However, if you set this SUID 'root', it will continue to work as expected. It is a terrible idea to permit SUID applications to run SUID from network file systems. diff --git a/fermilab-util_kcron.spec b/fermilab-util_kcron.spec index 74062ae..a13e3ea 100644 --- a/fermilab-util_kcron.spec +++ b/fermilab-util_kcron.spec @@ -10,7 +10,7 @@ Name: fermilab-util_kcron -Version: 2.0 +Version: 2.1 Release: 1%{?dist} Summary: A utility for getting Kerberos credentials in scheduled jobs @@ -136,12 +136,16 @@ done %config(noreplace) %{_sysconfdir}/sysconfig/kcron %attr(0755,root,root) %{_bindir}/* %attr(0755,root,root) /usr/libexec/kcron/client-keytab-name +%attr(0755,root,root) /usr/libexec/kcron/create-empty-client-keytab # If you can edit the memory this allocates, you can redirect the caps # so we still suid to prevent this. user 'bin' is basically unusable anyway. -%attr(4755,bin,root) %caps(cap_chown=p cap_dac_override=p) %{_libexecdir}/kcron/init-kcron-keytab +%attr(4755,bin,root) %caps(cap_chown=p cap_dac_override=p) %{_libexecdir}/kcron/init-kerberos-client-dir %changelog +* Wed Sep 16 2026 Pat Riehecky - 2.1 +- Break out helper tools into smaller parts + * Sat Jan 31 2026 Pat Riehecky - 2.0 - libcap now mandatory diff --git a/src/C/CMakeLists.txt b/src/C/CMakeLists.txt index 8ee93ea..2c7e7f3 100644 --- a/src/C/CMakeLists.txt +++ b/src/C/CMakeLists.txt @@ -128,6 +128,9 @@ add_compile_options( # Additional hardening for formats add_compile_options(-D_GLIBCXX_ASSERTIONS) +# Enable source fortification for standard library functions +add_compile_options(-D_FORTIFY_SOURCE=2) + # Additional GCC features (not supported by all compilers) if(CMAKE_C_COMPILER_ID STREQUAL "GNU") add_compile_options( @@ -261,13 +264,16 @@ endif() ############################# # Build Targets -add_executable(init-kcron-keytab) +add_executable(init-kerberos-client-dir) +add_executable(create-empty-client-keytab) add_executable(client-keytab-name) ############################# # Installation Configuration -install(TARGETS init-kcron-keytab DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/kcron +install(TARGETS init-kerberos-client-dir DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/kcron + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE) +install(TARGETS create-empty-client-keytab DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/kcron PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE) install(TARGETS client-keytab-name DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/kcron PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE) @@ -276,24 +282,45 @@ message(STATUS "Install prefix: ${CMAKE_INSTALL_PREFIX}") message(STATUS "Install libexec: ${CMAKE_INSTALL_LIBEXECDIR}") ############################# -# init-kcron-keytab Target Configuration +# init-kerberos-client-dir Target Configuration -target_compile_features(init-kcron-keytab PRIVATE +target_compile_features(init-kerberos-client-dir PRIVATE c_std_17 c_restrict c_function_prototypes c_static_assert ) -target_sources(init-kcron-keytab PRIVATE - ${PROJECT_SOURCE_DIR}/src/C/init-kcron-keytab.c +target_sources(init-kerberos-client-dir PRIVATE + ${PROJECT_SOURCE_DIR}/src/C/init-kerberos-client-dir.c ) # Link required libraries -target_link_libraries(init-kcron-keytab PRIVATE cap) +target_link_libraries(init-kerberos-client-dir PRIVATE cap) if (USE_SECCOMP) - target_link_libraries(init-kcron-keytab PRIVATE seccomp) + target_link_libraries(init-kerberos-client-dir PRIVATE seccomp) +endif(USE_SECCOMP) + +############################# +# create-empty-client-keytab Target Configuration + +target_compile_features(create-empty-client-keytab PRIVATE + c_std_17 + c_restrict + c_function_prototypes + c_static_assert +) + +target_sources(create-empty-client-keytab PRIVATE + ${PROJECT_SOURCE_DIR}/src/C/create-empty-client-keytab.c +) + +# Link libcap so the runtime hardening path can drop any inherited capabilities. +target_link_libraries(create-empty-client-keytab PRIVATE cap) + +if (USE_SECCOMP) + target_link_libraries(create-empty-client-keytab PRIVATE seccomp) endif(USE_SECCOMP) ############################# @@ -330,10 +357,8 @@ include_directories(${PROJECT_SOURCE_DIR}/src/C/) message(STATUS "") message(STATUS "=== SECURITY NOTICE ===") -message(STATUS "init-kcron-keytab MUST be installed with file capabilities:") -message(STATUS " capabilities: setcap cap_chown,cap_dac_override=ep ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBEXECDIR}/kcron/init-kcron-keytab") -message(STATUS "") -message(STATUS "The program will drop privileges immediately after creating the keytab file.") +message(STATUS "init-kerberos-client-dir MUST be installed with file capabilities:") +message(STATUS " capabilities: setcap cap_chown,cap_dac_override=ep ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBEXECDIR}/kcron/init-kerberos-client-dir") message(STATUS "======================") message(STATUS "") diff --git a/src/C/create-empty-client-keytab.c b/src/C/create-empty-client-keytab.c new file mode 100644 index 0000000..1f2b9ac --- /dev/null +++ b/src/C/create-empty-client-keytab.c @@ -0,0 +1,247 @@ +/* + * + * A simple program that creates an empty client keytab file. + * + * It runs with NO special privileges. The per-user keytab directory must + * already exist and be owned by the calling user. + * + */ +#include "autoconf.h" /* for our automatic config bits */ +/* + + Copyright 2023 Fermi Research Alliance, LLC + + This software was produced under U.S. Government contract DE-AC02-07CH11359 + for Fermi National Accelerator Laboratory (Fermilab), which is operated by + Fermi Research Alliance, LLC for the U.S. Department of Energy. The U.S. + Government has rights to use, reproduce, and distribute this software. + NEITHER THE GOVERNMENT NOR FERMI RESEARCH ALLIANCE, LLC MAKES ANY WARRANTY, + EXPRESS OR IMPLIED, OR ASSUMES ANY LIABILITY FOR THE USE OF THIS SOFTWARE. + If software is modified to produce derivative works, such modified software + should be clearly marked, so as not to confuse it with the version available + from Fermilab. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR FERMI RESEARCH ALLIANCE, LLC BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. + +*/ + +#ifndef __PROGRAM_NAME +#define __PROGRAM_NAME "create-empty-client-keytab" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kcron_empty_keytab_file.h" +#include "kcron_filename.h" + +#ifndef _0600 +#define _0600 (S_IRUSR | S_IWUSR) +#endif + +/* + * Cleanup helper to free all allocated buffers. + * Ensures consistent cleanup on all exit paths. + */ +static void free_buffers(char *keytab, char *keytab_dirname, char *keytab_filename) __attribute__((flatten)) __attribute__((cold)); +static void free_buffers(char *keytab, char *keytab_dirname, char *keytab_filename) { + /* NULL checks required for explicitness even though free() accepts NULL */ + if (keytab != NULL) { + (void)free(keytab); + } + if (keytab_dirname != NULL) { + (void)free(keytab_dirname); + } + if (keytab_filename != NULL) { + (void)free(keytab_filename); + } +} + + +static int validate_keytab_directory(const char *keytab_dirname) __attribute__((access(read_only, 1))) __attribute__((warn_unused_result)); +static int validate_keytab_directory(const char *keytab_dirname) { + struct stat lst = {0}; + struct stat st = {0}; + + if (keytab_dirname == NULL) { + (void)fprintf(stderr, "%s: Keytab directory pointer is NULL.\n", __PROGRAM_NAME); + return 1; + } + + /* Check symlink on keytab_dirname before stat to prevent symlink attacks */ + if (lstat(keytab_dirname, &lst) != 0) { + (void)fprintf(stderr, "%s: Keytab directory does not exist: %s.\n", __PROGRAM_NAME, keytab_dirname); + (void)fprintf(stderr, "%s: Run init-kerberos-client-dir first.\n", __PROGRAM_NAME); + return 1; + } + if (S_ISLNK(lst.st_mode)) { + (void)fprintf(stderr, "%s: Keytab directory %s is a symlink, not allowed.\n", __PROGRAM_NAME, keytab_dirname); + return 1; + } + + if (stat(keytab_dirname, &st) == -1) { + (void)fprintf(stderr, "%s: Keytab directory does not exist: %s.\n", __PROGRAM_NAME, keytab_dirname); + (void)fprintf(stderr, "%s: Run init-kerberos-client-dir first.\n", __PROGRAM_NAME); + return 1; + } + + if (!S_ISDIR(st.st_mode)) { + (void)fprintf(stderr, "%s: Keytab directory %s is not a directory.\n", __PROGRAM_NAME, keytab_dirname); + return 1; + } + + return 0; +} + +static int create_keytab_file(const char *keytab_dirname, const char *keytab_filename, const char *keytab) __attribute__((warn_unused_result)); +static int create_keytab_file(const char *keytab_dirname, const char *keytab_filename, const char *keytab) { + struct stat st = {0}; + struct stat lst = {0}; + DIR *keytab_dir = NULL; + int filedescriptor = -1; + + /* Validate non-null input pointers */ + if (keytab_dirname == NULL) { + (void)fprintf(stderr, "%s: keytab_dirname pointer is NULL.\n", __PROGRAM_NAME); + return 1; + } + if (keytab_filename == NULL) { + (void)fprintf(stderr, "%s: keytab_filename pointer is NULL.\n", __PROGRAM_NAME); + return 1; + } + if (keytab == NULL) { + (void)fprintf(stderr, "%s: keytab pointer is NULL.\n", __PROGRAM_NAME); + return 1; + } + + /* Check if keytab_dirname exists and is not a symlink (to avoid TOCTOU symlink attacks) */ + if (lstat(keytab_dirname, &lst) != 0) { + (void)fprintf(stderr, "%s: %s does not exist.\n", __PROGRAM_NAME, keytab_dirname); + return 1; + } + if (S_ISLNK(lst.st_mode)) { + (void)fprintf(stderr, "%s: %s is a symlink, not allowed.\n", __PROGRAM_NAME, keytab_dirname); + return 1; + } + + /* Open the directory handle to perform file operations safely on the directory inode */ + keytab_dir = opendir(keytab_dirname); + if (keytab_dir == NULL) { + (void)fprintf(stderr, "%s: Unable to open %s.\n", __PROGRAM_NAME, keytab_dirname); + (void)fprintf(stderr, "%s: This may be a permissions error?\n", __PROGRAM_NAME); + return 1; + } + + /* Verify the opened directory is indeed a directory */ + if (fstat(dirfd(keytab_dir), &st) != 0) { + (void)fprintf(stderr, "%s: %s could not be read.\n", __PROGRAM_NAME, keytab_dirname); + (void)closedir(keytab_dir); + return 1; + } + + if (!S_ISDIR(st.st_mode)) { + (void)fprintf(stderr, "%s: %s is not a directory.\n", __PROGRAM_NAME, keytab_dirname); + (void)closedir(keytab_dir); + return 1; + } + + /* Open the keytab file with O_NOFOLLOW to prevent symlink attacks, + * O_CREAT | O_EXCL to create only if missing and atomically detect existence, + * and O_CLOEXEC for descriptor safety. Permissions set to 0600 for security. + */ + filedescriptor = openat(dirfd(keytab_dir), keytab_filename, O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW | O_CLOEXEC, _0600); + if (filedescriptor < 0) { + if (errno == EEXIST) { + (void)fprintf(stderr, "%s: %s already exists.\n", __PROGRAM_NAME, keytab); + } else { + (void)fprintf(stderr, "%s: %s cannot be created: %s\n", __PROGRAM_NAME, keytab, strerror(errno)); + } + (void)closedir(keytab_dir); + return 1; + } + (void)closedir(keytab_dir); + + /* Verify that the created file is a regular file */ + if (fstat(filedescriptor, &st) != 0) { + (void)close(filedescriptor); + (void)fprintf(stderr, "%s: %s could not be created.\n", __PROGRAM_NAME, keytab); + return 1; + } + if (!S_ISREG(st.st_mode)) { + (void)close(filedescriptor); + (void)fprintf(stderr, "%s: %s is not a regular file.\n", __PROGRAM_NAME, keytab); + return 1; + } + + /* Write empty keytab content to the file */ + if (write_empty_keytab(filedescriptor) != 0) { + (void)close(filedescriptor); + (void)fprintf(stderr, "%s: Cannot create keytab : %s.\n", __PROGRAM_NAME, keytab); + return 1; + } + + /* Ensure permissions are exactly 0600 */ + if (fchmod(filedescriptor, _0600) != 0) { + (void)close(filedescriptor); + (void)fprintf(stderr, "%s: Unable to chmod %o %s\n", __PROGRAM_NAME, _0600, keytab); + return 1; + } + + (void)close(filedescriptor); + return 0; +} + +int main(void) { + char *keytab = calloc(FILE_PATH_MAX_LENGTH + 3, sizeof(char)); + char *keytab_dirname = calloc(FILE_PATH_MAX_LENGTH + 3, sizeof(char)); + char *keytab_filename = calloc(FILE_PATH_MAX_LENGTH + 3, sizeof(char)); + + if ((keytab == NULL) || (keytab_dirname == NULL) || (keytab_filename == NULL)) { + (void)fprintf(stderr, "%s: Unable to allocate memory.\n", __PROGRAM_NAME); + (void)free_buffers(keytab, keytab_dirname, keytab_filename); + exit(EXIT_FAILURE); + } + + if (get_filenames(keytab_dirname, keytab_filename, keytab) != 0) { + (void)fprintf(stderr, "%s: Cannot determine keytab filename.\n", __PROGRAM_NAME); + (void)free_buffers(keytab, keytab_dirname, keytab_filename); + exit(EXIT_FAILURE); + } + + if (validate_keytab_directory(keytab_dirname) != 0) { + (void)free_buffers(keytab, keytab_dirname, keytab_filename); + exit(EXIT_FAILURE); + } + + if (create_keytab_file(keytab_dirname, keytab_filename, keytab) != 0) { + (void)free_buffers(keytab, keytab_dirname, keytab_filename); + exit(EXIT_FAILURE); + } + + (void)free_buffers(keytab, keytab_dirname, keytab_filename); + + exit(EXIT_SUCCESS); +} diff --git a/src/C/init-kcron-keytab.c b/src/C/init-kcron-keytab.c deleted file mode 100644 index 623e1b5..0000000 --- a/src/C/init-kcron-keytab.c +++ /dev/null @@ -1,467 +0,0 @@ -/* - * - * A simple program that generates a blank keytab in a deterministic location. - * - * It should be SETUID(3p) root or have the right CAPABILITIES(7). - * - */ -#include "autoconf.h" /* for our automatic config bits */ -/* - - Copyright 2023 Fermi Research Alliance, LLC - - This software was produced under U.S. Government contract DE-AC02-07CH11359 - for Fermi National Accelerator Laboratory (Fermilab), which is operated by - Fermi Research Alliance, LLC for the U.S. Department of Energy. The U.S. - Government has rights to use, reproduce, and distribute this software. - NEITHER THE GOVERNMENT NOR FERMI RESEARCH ALLIANCE, LLC MAKES ANY WARRANTY, - EXPRESS OR IMPLIED, OR ASSUMES ANY LIABILITY FOR THE USE OF THIS SOFTWARE. - If software is modified to produce derivative works, such modified software - should be clearly marked, so as not to confuse it with the version available - from Fermilab. - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR FERMI RESEARCH ALLIANCE, LLC BE LIABLE FOR ANY CLAIM, DAMAGES OR - OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - IN THE SOFTWARE. - -*/ - -#ifndef __PROGRAM_NAME -#define __PROGRAM_NAME "init-kcron-keytab" -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "kcron_caps.h" -#include "kcron_empty_keytab_file.h" -#include "kcron_filename.h" -#include "kcron_setup.h" - -#ifndef _0600 -#define _0600 (S_IRUSR | S_IWUSR) -#endif -#ifndef _0700 -#define _0700 (S_IRWXU) -#endif - -static inline int mkdir_if_missing(const char *dir, uid_t owner, gid_t group, mode_t mode) __attribute__((access(read_only, 1))) __attribute__((warn_unused_result)); -static inline int mkdir_if_missing(const char *dir, uid_t owner, gid_t group, mode_t mode) { - const cap_value_t caps[] = {CAP_CHOWN, CAP_DAC_OVERRIDE}; - const int num_caps = sizeof(caps) / sizeof(cap_value_t); - - struct stat st = {0}; - struct stat lst = {0}; - - if (dir == NULL) { - /* nothing to do - no dir passed */ - return 0; - } - - /* Check for symlink to prevent TOCTOU symlink attacks */ - if (lstat(dir, &lst) == 0) { - if (S_ISLNK(lst.st_mode)) { - (void)fprintf(stderr, "%s: %s is a symlink, not allowed.\n", __PROGRAM_NAME, dir); - return 1; - } - } - - if (stat(dir, &st) == 0) { - /* exists */ - if (S_ISDIR(st.st_mode)) { - /* and is a directory */ - return 0; - } else { - /* whatever this is, it is not a directory */ - (void)fprintf(stderr, "%s: %s is not a directory.\n", __PROGRAM_NAME, dir); - return 1; - } - } - - if (enable_capabilities(caps, num_caps) != 0) { - (void)fprintf(stderr, "%s: Cannot enable capabilities.\n", __PROGRAM_NAME); - return 1; - } - - /* use of CAP_DAC_OVERRIDE to bypass discretionary access controls for mkdir */ - if (mkdir(dir, mode) != 0) { - (void)disable_capabilities(); - (void)fprintf(stderr, "%s: Unable to mkdir %s\n", __PROGRAM_NAME, dir); - return 1; - } - - /* use of CAP_DAC_OVERRIDE as we might not be able to write to dir */ - DIR *my_dir = opendir(dir); - if (my_dir == NULL) { - (void)disable_capabilities(); - (void)fprintf(stderr, "%s: Unable to locate %s ?\n", __PROGRAM_NAME, dir); - (void)fprintf(stderr, "%s: This may be a permissions error?\n", __PROGRAM_NAME); - return 1; - } - - /* use of CAP_DAC_OVERRIDE as we might not be able to list dir */ - if (fstat(dirfd(my_dir), &st) != 0) { - (void)closedir(my_dir); - (void)disable_capabilities(); - (void)fprintf(stderr, "%s: %s could not be created.\n", __PROGRAM_NAME, dir); - (void)fprintf(stderr, "%s: This may be a permissions error?\n", __PROGRAM_NAME); - return 1; - } - - disable_capabilities(); - - if (!S_ISDIR(st.st_mode)) { - (void)closedir(my_dir); - (void)disable_capabilities(); - (void)fprintf(stderr, "%s: %s is not a directory.\n", __PROGRAM_NAME, dir); - return 1; - } - - if (enable_capabilities(caps, num_caps) != 0) { - (void)fprintf(stderr, "%s: Cannot enable capabilities.\n", __PROGRAM_NAME); - return 1; - } - - /* use of CAP_CHOWN to change ownership of the directory */ - if (fchown(dirfd(my_dir), owner, group) != 0) { - (void)closedir(my_dir); - (void)disable_capabilities(); - (void)fprintf(stderr, "%s: Unable to chown %i:%i %s\n", __PROGRAM_NAME, owner, group, dir); - (void)fprintf(stderr, "%s: This may be a permissions error?\n", __PROGRAM_NAME); - return 1; - } - - disable_capabilities(); - - (void)closedir(my_dir); - return 0; -} - -static inline int chown_chmod_keytab(int filedescriptor, const char *keytab) __attribute__((access(read_only, 2))) __attribute__((warn_unused_result)); -static inline int chown_chmod_keytab(int filedescriptor, const char *keytab) { - - const cap_value_t keytab_caps[] = {CAP_CHOWN, CAP_DAC_OVERRIDE}; - const int num_caps = sizeof(keytab_caps) / sizeof(cap_value_t); - - const uid_t uid = getuid(); - const gid_t gid = getgid(); - - struct stat st = {0}; - - if (filedescriptor == 0) { - (void)fprintf(stderr, "%s: Invalid file %s.\n", __PROGRAM_NAME, keytab); - return 1; - } - - if (enable_capabilities(keytab_caps, num_caps) != 0) { - (void)fprintf(stderr, "%s: Cannot enable capabilities.\n", __PROGRAM_NAME); - return 1; - } - - /* use of CAP_DAC_OVERRIDE to bypass access checks for fstat on keytab file */ - if (fstat(filedescriptor, &st) != 0) { - (void)fprintf(stderr, "%s: Cannot stat file %s.\n", __PROGRAM_NAME, keytab); - (void)disable_capabilities(); - return 1; - } - - disable_capabilities(); - - if (!S_ISREG(st.st_mode)) { - (void)fprintf(stderr, "%s: %s is not a regular file.\n", __PROGRAM_NAME, keytab); - return 1; - } - - /* ensure permissions are as expected on keytab file */ - /* no capabilities required for fchmod, we already own it */ - if (fchmod(filedescriptor, _0600) != 0) { - (void)disable_capabilities(); - (void)fprintf(stderr, "%s: Unable to chmod %o %s\n", __PROGRAM_NAME, _0600, keytab); - return 1; - } - - /* Set the right owner of our keytab */ - /* Don't switch euid to uid as that may permit write to program memory */ - if (st.st_uid != uid || st.st_gid != gid) { - if (enable_capabilities(keytab_caps, num_caps) != 0) { - (void)fprintf(stderr, "%s: Cannot enable capabilities.\n", __PROGRAM_NAME); - return 1; - } - - /* use of CAP_CHOWN to set ownership of the keytab file */ - if (fchown(filedescriptor, uid, gid) != 0) { - (void)disable_capabilities(); - (void)fprintf(stderr, "%s: Unable to chown %d:%d %s\n", __PROGRAM_NAME, uid, gid, keytab); - return 1; - } - - disable_capabilities(); - } - - return 0; -} - -static int validate_client_dirname(char *client_keytab_dirname) __attribute__((warn_unused_result)); -static int validate_client_dirname(char *client_keytab_dirname) { - struct stat lst = {0}; - struct stat st = {0}; - - if (client_keytab_dirname == NULL) { - (void)fprintf(stderr, "%s: Client keytab directory pointer is NULL.\n", __PROGRAM_NAME); - return 1; - } - - /* Check symlink on client_keytab_dirname before stat */ - if (lstat(client_keytab_dirname, &lst) != 0) { - (void)fprintf(stderr, "%s: Client keytab directory does not exist: %s.\n", __PROGRAM_NAME, client_keytab_dirname); - (void)fprintf(stderr, "%s: Contact your admin to have it created correctly.\n", __PROGRAM_NAME); - return 1; - } - if (S_ISLNK(lst.st_mode)) { - (void)fprintf(stderr, "%s: Client keytab directory %s is a symlink, not allowed.\n", __PROGRAM_NAME, client_keytab_dirname); - return 1; - } - - if (stat(client_keytab_dirname, &st) == -1) { - (void)fprintf(stderr, "%s: Client keytab directory does not exist: %s.\n", __PROGRAM_NAME, client_keytab_dirname); - (void)fprintf(stderr, "%s: Contact your admin to have it created.\n", __PROGRAM_NAME); - return 1; - } - return 0; -} - -static int create_keytab_file(const char *keytab_dirname, const char *keytab_filename, const char *keytab) __attribute__((warn_unused_result)); -static int create_keytab_file(const char *keytab_dirname, const char *keytab_filename, const char *keytab) { - struct stat st = {0}; - struct stat lst = {0}; - DIR *keytab_dir = NULL; - int filedescriptor = -1; - - const cap_value_t caps[] = {CAP_DAC_OVERRIDE}; - const int num_caps = sizeof(caps) / sizeof(cap_value_t); - - /* Validate non-null input pointers */ - if (keytab_dirname == NULL) { - (void)fprintf(stderr, "%s: keytab_dirname pointer is NULL.\n", __PROGRAM_NAME); - return 1; - } - if (keytab_filename == NULL) { - (void)fprintf(stderr, "%s: keytab_filename pointer is NULL.\n", __PROGRAM_NAME); - return 1; - } - if (keytab == NULL) { - (void)fprintf(stderr, "%s: keytab pointer is NULL.\n", __PROGRAM_NAME); - return 1; - } - - /* enable CAP_DAC_OVERRIDE to bypass permission checks temporarily for - * directory access and file creation. - * This capability is used only around operations that need it and dropped immediately after. - */ - if (enable_capabilities(caps, num_caps) != 0) { - (void)fprintf(stderr, "%s: Cannot enable capabilities.\n", __PROGRAM_NAME); - return 1; - } - - /* Check if keytab_dirname exists and is not a symlink (to avoid TOCTOU symlink attacks) */ - if (lstat(keytab_dirname, &lst) != 0) { - (void)fprintf(stderr, "%s: %s does not exist.\n", __PROGRAM_NAME, keytab_dirname); - (void)disable_capabilities(); - return 1; - } - if (S_ISLNK(lst.st_mode)) { - (void)fprintf(stderr, "%s: %s is a symlink, not allowed.\n", __PROGRAM_NAME, keytab_dirname); - (void)disable_capabilities(); - return 1; - } - - /* Open the directory handle to perform file operations safely on the directory inode */ - keytab_dir = opendir(keytab_dirname); - if (keytab_dir == NULL) { - (void)fprintf(stderr, "%s: Unable to locate %s ?\n", __PROGRAM_NAME, keytab_dirname); - (void)fprintf(stderr, "%s: This may be a permissions error?\n", __PROGRAM_NAME); - (void)disable_capabilities(); - return 1; - } - - /* Verify the opened directory is indeed a directory */ - if (fstat(dirfd(keytab_dir), &st) != 0) { - (void)fprintf(stderr, "%s: %s could not be read.\n", __PROGRAM_NAME, keytab_dirname); - (void)closedir(keytab_dir); - (void)disable_capabilities(); - return 1; - } - - (void)disable_capabilities(); - - if (!S_ISDIR(st.st_mode)) { - (void)fprintf(stderr, "%s: %s is not a directory.\n", __PROGRAM_NAME, keytab_dirname); - (void)closedir(keytab_dir); - (void)disable_capabilities(); - return 1; - } - - /* Open the keytab file with O_NOFOLLOW to prevent symlink attacks, - * O_CREAT to create if missing, and O_CLOEXEC for descriptor safety. - * Permissions set to 0600 for security. - */ - filedescriptor = openat(dirfd(keytab_dir), keytab_filename, O_WRONLY | O_CREAT | O_NOFOLLOW | O_CLOEXEC, _0600); - if (filedescriptor < 0) { - (void)closedir(keytab_dir); - (void)fprintf(stderr, "%s: %s is missing, cannot create: %s\n", __PROGRAM_NAME, keytab, strerror(errno)); - return 1; - } - (void)closedir(keytab_dir); - - /* Verify that the created file is a regular file */ - if (fstat(filedescriptor, &st) != 0) { - (void)close(filedescriptor); - (void)disable_capabilities(); - (void)fprintf(stderr, "%s: %s could not be created.\n", __PROGRAM_NAME, keytab); - return 1; - } - if (!S_ISREG(st.st_mode)) { - (void)close(filedescriptor); - (void)disable_capabilities(); - (void)fprintf(stderr, "%s: %s is not a regular file.\n", __PROGRAM_NAME, keytab); - return 1; - } - - /* Write empty keytab content to the file */ - if (write_empty_keytab(filedescriptor) != 0) { - (void)close(filedescriptor); - (void)fprintf(stderr, "%s: Cannot create keytab : %s.\n", __PROGRAM_NAME, keytab); - return 1; - } - - /* Set ownership and permission on the keytab file */ - if (chown_chmod_keytab(filedescriptor, keytab) != 0) { - (void)close(filedescriptor); - (void)fprintf(stderr, "%s: Cannot set permissions on keytab : %s.\n", __PROGRAM_NAME, keytab); - return 1; - } - - (void)close(filedescriptor); - return 0; -} - -void constructor(void) __attribute__((constructor)); -void constructor(void) { - /* Setup runtime hardening /before/ main() is even called */ - (void)harden_runtime(); -} - -int main(void) { - struct stat st = {0}; - - const cap_value_t caps[] = {CAP_DAC_OVERRIDE}; - const int num_caps = sizeof(caps) / sizeof(cap_value_t); - - const uid_t uid = getuid(); - const gid_t gid = getgid(); - - char *keytab = calloc(FILE_PATH_MAX_LENGTH + 3, sizeof(char)); - char *keytab_dirname = calloc(FILE_PATH_MAX_LENGTH + 3, sizeof(char)); - char *keytab_filename = calloc(FILE_PATH_MAX_LENGTH + 3, sizeof(char)); - char *client_keytab_dirname = calloc(FILE_PATH_MAX_LENGTH + 3, sizeof(char)); - - if ((keytab == NULL) || (keytab_dirname == NULL) || (keytab_filename == NULL) || (client_keytab_dirname == NULL)) { - (void)fprintf(stderr, "%s: Unable to allocate memory.\n", __PROGRAM_NAME); - (void)free(keytab); - (void)free(keytab_dirname); - (void)free(keytab_filename); - (void)free(client_keytab_dirname); - exit(EXIT_FAILURE); - } - - if (get_client_dirname(client_keytab_dirname) != 0) { - (void)fprintf(stderr, "%s: Client keytab directory not set.\n", __PROGRAM_NAME); - (void)free(keytab); - (void)free(keytab_dirname); - (void)free(keytab_filename); - (void)free(client_keytab_dirname); - exit(EXIT_FAILURE); - } - - if (validate_client_dirname(client_keytab_dirname) != 0) { - (void)free(keytab); - (void)free(keytab_dirname); - (void)free(keytab_filename); - (void)free(client_keytab_dirname); - exit(EXIT_FAILURE); - } - - if (get_filenames(keytab_dirname, keytab_filename, keytab) != 0) { - (void)fprintf(stderr, "%s: Cannot determine keytab filename.\n", __PROGRAM_NAME); - (void)free(keytab); - (void)free(keytab_dirname); - (void)free(keytab_filename); - (void)free(client_keytab_dirname); - exit(EXIT_FAILURE); - } - - if (mkdir_if_missing(keytab_dirname, uid, gid, _0700) != 0) { - (void)fprintf(stderr, "%s: Cannot make dir %s.\n", __PROGRAM_NAME, keytab_dirname); - (void)free(keytab); - (void)free(keytab_dirname); - (void)free(keytab_filename); - (void)free(client_keytab_dirname); - exit(EXIT_FAILURE); - } - - /* use of CAP_DAC_OVERRIDE as we may not be able to chdir otherwise */ - if (enable_capabilities(caps, num_caps) != 0) { - (void)fprintf(stderr, "%s: Cannot enable capabilities.\n", __PROGRAM_NAME); - (void)free(keytab); - (void)free(keytab_dirname); - (void)free(keytab_filename); - (void)free(client_keytab_dirname); - exit(EXIT_FAILURE); - } - - int stat_code = stat(keytab, &st); - - disable_capabilities(); - - /* create keytab if missing */ - if (stat_code == -1) { - if (create_keytab_file(keytab_dirname, keytab_filename, keytab) != 0) { - (void)free(keytab); - (void)free(keytab_dirname); - (void)free(keytab_filename); - (void)free(client_keytab_dirname); - exit(EXIT_FAILURE); - } - } - - (void)printf("%s\n", keytab); - - (void)free(keytab); - (void)free(keytab_dirname); - (void)free(keytab_filename); - (void)free(client_keytab_dirname); - - exit(EXIT_SUCCESS); -} diff --git a/src/C/init-kerberos-client-dir.c b/src/C/init-kerberos-client-dir.c new file mode 100644 index 0000000..efee8b1 --- /dev/null +++ b/src/C/init-kerberos-client-dir.c @@ -0,0 +1,260 @@ +/* + * + * A simple program that generates a blank keytab in a deterministic location. + * + * It should be SETUID(3p) root or have the right CAPABILITIES(7). + * + */ +#include "autoconf.h" /* for our automatic config bits */ +/* + + Copyright 2023 Fermi Research Alliance, LLC + + This software was produced under U.S. Government contract DE-AC02-07CH11359 + for Fermi National Accelerator Laboratory (Fermilab), which is operated by + Fermi Research Alliance, LLC for the U.S. Department of Energy. The U.S. + Government has rights to use, reproduce, and distribute this software. + NEITHER THE GOVERNMENT NOR FERMI RESEARCH ALLIANCE, LLC MAKES ANY WARRANTY, + EXPRESS OR IMPLIED, OR ASSUMES ANY LIABILITY FOR THE USE OF THIS SOFTWARE. + If software is modified to produce derivative works, such modified software + should be clearly marked, so as not to confuse it with the version available + from Fermilab. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR FERMI RESEARCH ALLIANCE, LLC BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. + +*/ + +#ifndef __PROGRAM_NAME +#define __PROGRAM_NAME "init-kcron-keytab" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kcron_caps.h" +#include "kcron_filename.h" +#include "kcron_setup.h" + +#ifndef _0600 +#define _0600 (S_IRUSR | S_IWUSR) +#endif +#ifndef _0700 +#define _0700 (S_IRWXU) +#endif + +/* + * Cleanup helper to free all allocated buffers. + * Ensures consistent cleanup on all exit paths. + */ +static void free_buffers(char *keytab, char *keytab_dirname, char *keytab_filename, char *client_keytab_dirname) __attribute__((flatten)) __attribute__((cold)); +static void free_buffers(char *keytab, char *keytab_dirname, char *keytab_filename, char *client_keytab_dirname) { + /* NULL checks required for explicitness even though free() accepts NULL */ + if (keytab != NULL) { + (void)free(keytab); + } + if (keytab_dirname != NULL) { + (void)free(keytab_dirname); + } + if (keytab_filename != NULL) { + (void)free(keytab_filename); + } + if (client_keytab_dirname != NULL) { + (void)free(client_keytab_dirname); + } +} + +static int mkdir_if_missing(const char *dir, uid_t owner, gid_t group, mode_t mode) __attribute__((access(read_only, 1))) __attribute__((warn_unused_result)); +static int mkdir_if_missing(const char *dir, uid_t owner, gid_t group, mode_t mode) { + const cap_value_t caps_dac_override[] = {CAP_DAC_OVERRIDE}; + const int num_caps_dac_override = sizeof(caps_dac_override) / sizeof(cap_value_t); + const cap_value_t caps_chown[] = {CAP_CHOWN}; + const int num_caps_chown = sizeof(caps_chown) / sizeof(cap_value_t); + + struct stat st = {0}; + struct stat lst = {0}; + + if (dir == NULL) { + /* nothing to do - no dir passed */ + return 0; + } + + /* Check for symlink to prevent TOCTOU symlink attacks */ + if (lstat(dir, &lst) == 0) { + if (S_ISLNK(lst.st_mode)) { + (void)fprintf(stderr, "%s: %s is a symlink, not allowed.\n", __PROGRAM_NAME, dir); + return 1; + } + } + + if (stat(dir, &st) == 0) { + /* exists */ + if (S_ISDIR(st.st_mode)) { + /* and is a directory */ + return 0; + } else { + /* whatever this is, it is not a directory */ + (void)fprintf(stderr, "%s: %s is not a directory.\n", __PROGRAM_NAME, dir); + return 1; + } + } + + if (enable_capabilities(caps_dac_override, num_caps_dac_override) != 0) { + (void)fprintf(stderr, "%s: Cannot enable capabilities.\n", __PROGRAM_NAME); + return 1; + } + + /* use of CAP_DAC_OVERRIDE to bypass discretionary access controls for mkdir */ + if (mkdir(dir, mode) != 0) { + (void)disable_capabilities(); + (void)fprintf(stderr, "%s: Unable to mkdir %s\n", __PROGRAM_NAME, dir); + return 1; + } + + /* use of CAP_DAC_OVERRIDE as we might not be able to write to dir */ + DIR *my_dir = opendir(dir); + if (my_dir == NULL) { + (void)disable_capabilities(); + (void)fprintf(stderr, "%s: Unable to locate %s ?\n", __PROGRAM_NAME, dir); + (void)fprintf(stderr, "%s: This may be a permissions error?\n", __PROGRAM_NAME); + return 1; + } + + /* use of CAP_DAC_OVERRIDE as we might not be able to list dir */ + if (fstat(dirfd(my_dir), &st) != 0) { + (void)closedir(my_dir); + (void)disable_capabilities(); + (void)fprintf(stderr, "%s: %s could not be created.\n", __PROGRAM_NAME, dir); + (void)fprintf(stderr, "%s: This may be a permissions error?\n", __PROGRAM_NAME); + return 1; + } + + disable_capabilities(); + + if (!S_ISDIR(st.st_mode)) { + (void)closedir(my_dir); + (void)disable_capabilities(); + (void)fprintf(stderr, "%s: %s is not a directory.\n", __PROGRAM_NAME, dir); + return 1; + } + + if (enable_capabilities(caps_chown, num_caps_chown) != 0) { + (void)fprintf(stderr, "%s: Cannot enable capabilities.\n", __PROGRAM_NAME); + return 1; + } + + /* use of CAP_CHOWN to change ownership of the directory */ + if (fchown(dirfd(my_dir), owner, group) != 0) { + (void)closedir(my_dir); + (void)disable_capabilities(); + (void)fprintf(stderr, "%s: Unable to chown %i:%i %s\n", __PROGRAM_NAME, owner, group, dir); + (void)fprintf(stderr, "%s: This may be a permissions error?\n", __PROGRAM_NAME); + return 1; + } + + disable_capabilities(); + + (void)closedir(my_dir); + return 0; +} + +static int validate_client_dirname(char *client_keytab_dirname) __attribute__((warn_unused_result)); +static int validate_client_dirname(char *client_keytab_dirname) { + struct stat lst = {0}; + struct stat st = {0}; + + if (client_keytab_dirname == NULL) { + (void)fprintf(stderr, "%s: Client keytab directory pointer is NULL.\n", __PROGRAM_NAME); + return 1; + } + + /* Check symlink on client_keytab_dirname before stat */ + if (lstat(client_keytab_dirname, &lst) != 0) { + (void)fprintf(stderr, "%s: Client keytab directory does not exist: %s.\n", __PROGRAM_NAME, client_keytab_dirname); + (void)fprintf(stderr, "%s: Contact your admin to have it created correctly.\n", __PROGRAM_NAME); + return 1; + } + if (S_ISLNK(lst.st_mode)) { + (void)fprintf(stderr, "%s: Client keytab directory %s is a symlink, not allowed.\n", __PROGRAM_NAME, client_keytab_dirname); + return 1; + } + + if (stat(client_keytab_dirname, &st) == -1) { + (void)fprintf(stderr, "%s: Client keytab directory does not exist: %s.\n", __PROGRAM_NAME, client_keytab_dirname); + (void)fprintf(stderr, "%s: Contact your admin to have it created.\n", __PROGRAM_NAME); + return 1; + } + return 0; +} + +void constructor(void) __attribute__((constructor)); +void constructor(void) { + /* Setup runtime hardening /before/ main() is even called */ + (void)harden_runtime(); +} + +int main(void) { + const uid_t uid = getuid(); + const gid_t gid = getgid(); + + char *keytab = calloc(FILE_PATH_MAX_LENGTH + 3, sizeof(char)); + char *keytab_dirname = calloc(FILE_PATH_MAX_LENGTH + 3, sizeof(char)); + char *keytab_filename = calloc(FILE_PATH_MAX_LENGTH + 3, sizeof(char)); + char *client_keytab_dirname = calloc(FILE_PATH_MAX_LENGTH + 3, sizeof(char)); + + if ((keytab == NULL) || (keytab_dirname == NULL) || (keytab_filename == NULL) || (client_keytab_dirname == NULL)) { + (void)fprintf(stderr, "%s: Unable to allocate memory.\n", __PROGRAM_NAME); + (void)free_buffers(keytab, keytab_dirname, keytab_filename, client_keytab_dirname); + exit(EXIT_FAILURE); + } + + if (get_client_dirname(client_keytab_dirname) != 0) { + (void)fprintf(stderr, "%s: Client keytab directory not set.\n", __PROGRAM_NAME); + (void)free_buffers(keytab, keytab_dirname, keytab_filename, client_keytab_dirname); + exit(EXIT_FAILURE); + } + + if (validate_client_dirname(client_keytab_dirname) != 0) { + (void)free_buffers(keytab, keytab_dirname, keytab_filename, client_keytab_dirname); + exit(EXIT_FAILURE); + } + + if (get_filenames(keytab_dirname, keytab_filename, keytab) != 0) { + (void)fprintf(stderr, "%s: Cannot determine keytab filename.\n", __PROGRAM_NAME); + (void)free_buffers(keytab, keytab_dirname, keytab_filename, client_keytab_dirname); + exit(EXIT_FAILURE); + } + + if (mkdir_if_missing(keytab_dirname, uid, gid, _0700) != 0) { + (void)fprintf(stderr, "%s: Cannot make dir %s.\n", __PROGRAM_NAME, keytab_dirname); + (void)free_buffers(keytab, keytab_dirname, keytab_filename, client_keytab_dirname); + exit(EXIT_FAILURE); + } + + (void)free_buffers(keytab, keytab_dirname, keytab_filename, client_keytab_dirname); + + exit(EXIT_SUCCESS); +} diff --git a/src/shell/kcron.sysconfig b/src/shell/kcron.sysconfig index 91c82b5..a9e09d8 100644 --- a/src/shell/kcron.sysconfig +++ b/src/shell/kcron.sysconfig @@ -12,5 +12,6 @@ WHOAMI=${KCRON_WHOAMI:-$(basename "$(whoami)")} NODENAME=${KCRON_NODENAME:-$(basename "$(hostname)")} FULLPRINCIPAL=${KCRON_FULLPRINCIPAL:-"${WHOAMI}/cron/${NODENAME}@${REALM}"} -KEYTAB_NAME_UTIL='/usr/libexec/kcron/client-keytab-name' -KEYTAB_INIT='/usr/libexec/kcron/init-kcron-keytab' +#KEYTAB_NAME_UTIL='/usr/libexec/kcron/client-keytab-name' +#KEYTAB_DIR_INIT='/usr/libexec/kcron/init-kerberos-client-dir' +#KEYTAB_CREATE='/usr/libexec/kcron/create-empty-client-keytab' diff --git a/src/shell/kcrondestroy b/src/shell/kcrondestroy index f341851..cd0b23b 100644 --- a/src/shell/kcrondestroy +++ b/src/shell/kcrondestroy @@ -39,7 +39,7 @@ usage() { ########################################################### destroy() { # Destroy credential cache - ${DESTROY_CACHE} + ${kdestroy} echo '' echo DESTROYED administration credentials. } @@ -82,6 +82,12 @@ if ! which kinit >/dev/null 2>&1; then echo "Consider installing krb5-workstation" >&2 exit 2 fi +if ! which kdestroy >/dev/null 2>&1; then + echo '' + echo "Could not find 'kdestroy'" >&2 + echo "Consider installing krb5-workstation" >&2 + exit 2 +fi if ! which logger >/dev/null 2>&1; then echo '' echo "Could not find 'logger'" >&2 @@ -103,7 +109,7 @@ fi kadmin=$(which kadmin) kinit=$(which kinit) -DESTROY_CACHE=$(which kdestroy) +kdestroy=$(which kdestroy) ########################################################### # SET UP CREDENTIAL CACHE diff --git a/src/shell/kcroninit b/src/shell/kcroninit index f29edee..4ab59c1 100644 --- a/src/shell/kcroninit +++ b/src/shell/kcroninit @@ -39,7 +39,7 @@ usage() { ########################################################### destroy() { # Destroy credential cache - ${DESTROY_CACHE} + ${kdestroy} echo '' echo 'DESTROYED administration credentials.' } @@ -98,6 +98,12 @@ if ! which klist >/dev/null 2>&1; then echo "Consider installing krb5-workstation" >&2 exit 2 fi +if ! which kdestroy >/dev/null 2>&1; then + echo '' + echo "Could not find 'kdestroy'" >&2 + echo "Consider installing krb5-workstation" >&2 + exit 2 +fi if ! which logger >/dev/null 2>&1; then echo '' echo "Could not find 'logger'" >&2 @@ -120,7 +126,7 @@ fi kadmin=$(which kadmin) kinit=$(which kinit) klist=$(which klist) -DESTROY_CACHE=$(which kdestroy) +kdestroy=$(which kdestroy) ########################################################### # CONFIRM @@ -155,11 +161,32 @@ SCRAMBLE=$(echo "$( )" | md5sum | /bin/cut -f1 -d" ") export KRB5CCNAME="KEYRING:session:${MYUID}:${SCRAMBLE}" +########################################################### +# Set up keytab directory and file +########################################################### +echo 'Initializing keytab directory...' +if ! ${KEYTAB_DIR_INIT:-/usr/libexec/kcron/init-kerberos-client-dir}; then + echo '' + echo 'Unable to initialize client keytab directory.' >&2 + exit 2 +fi + +KEYTAB=$(${KEYTAB_NAME:-/usr/libexec/kcron/client-keytab-name}) + +if [[ ! -e ${KEYTAB} ]]; then + echo 'Creating empty keytab...' + if ! ${KEYTAB_CREATE:-/usr/libexec/kcron/create-empty-client-keytab}; then + echo '' + echo 'Unable to create empty client keytab.' >&2 + exit 2 + fi +fi + ########################################################### # Can I write to the keytab? ########################################################### echo 'Is the keytab writable?' -if ! KEYTAB=$(${KEYTAB_INIT:-/usr/libexec/kcron/init-kcron-keytab}); then +if [[ ! -w ${KEYTAB} ]]; then echo '' echo 'Keytab is not writable to this user:' >&2 id >&2