/* * Copyright (c) 2023 Wind River Systems, Inc. * * SPDX-License-Identifier: Apache-2.0 * */ /** * @file * StarlingX Luks FileSystem Management Service * */ #include #include #include #include #include #include #include #include #include #include #include #include #include "PassphraseGenerator.h" #define INOTIFY_THREAD_SLEEP 15 #define CONTROLLER_0 "controller-0" #define CONTROLLER_1 "controller-1" #define SLEEP_DURATION 60 #define BUFFER 1024 #define FAIL_FILE_WRITE (11) #define FAIL_PID_OPEN (9) using namespace std; // Global constants const char *configFile = "/etc/luks-fs-mgr.d/luks_config.json"; const char *defaultDirectoryPath = "/var/luks/stx"; const char *defaultMountPath = "/var/luks/stx/luks_fs"; const char *createdConfigFile = "/etc/luks-fs-mgr.d/created_luks.json"; const char *luksControllerDataPath = "/var/luks/stx/luks_fs/controller/"; const char *pidFileName = "/var/run/luks-fs-mgr.pid"; // Define a struct to hold configuration variables struct LuksConfig { const char *vaultFile; const char *vaultSize; const char *volName; const char *mountPath; }; // Define a struct to hold configuration variables for created volume struct CreatedLuksConfig { const char *vaultFile; const char *vaultSize; const char *volName; const char *mountPath; const char *passphraseType; }; /* *********************************************************************** * * Name : log * * Description: Defined to log info/error messages using the 'syslog' utility. * * ************************************************************************/ void log(const string &message, int logType) { openlog("luks-fs-mgr", LOG_PID | LOG_NDELAY, LOG_DAEMON); syslog(logType, "%s", message.c_str()); closelog(); } // A helper function for parsing passphraseType when ConfigType // is CreatedLuksConfig template typename enable_if::value, bool>::type parsePassphraseType(ConfigType &config, json_object *passPhraseObj) { config.passphraseType = json_object_get_string(passPhraseObj); return true; } // A helper function for parsing passphraseType when ConfigType // is LuksConfig template typename enable_if::value, bool>::type parsePassphraseType(ConfigType &, json_object *) { // No-op when ConfigType is not CreatedLuksConfig return true; } /* *********************************************************************** * * Name : parseJSONConfig * * Description: This function parses a JSON configuration file (luks_config.json) * and extracts LUKS volume attributes, such as vault file, * vault size, volume name, and mount path. * * ************************************************************************/ template bool parseJSONConfig(const char *configFile, ConfigType &config, json_object **jsonConfig) { log("Parsing " + string(configFile), LOG_INFO); bool valid = true; *jsonConfig = json_object_from_file(configFile); if (*jsonConfig == nullptr) { log("Error opening or parsing config file", LOG_ERR); return false; } json_object *luksvolumes; if (!json_object_object_get_ex(*jsonConfig, "luksvolumes", &luksvolumes)) { log("Unable to get 'luksvolumes' array from JSON", LOG_ERR); return false; } if (!json_object_is_type(luksvolumes, json_type_array)) { log("'luksvolumes' is not an array", LOG_ERR); return false; } int numVolumes = json_object_array_length(luksvolumes); if (numVolumes == 0) { log("'luksvolumes' array is empty", LOG_ERR); return false; } json_object *volumeObj = json_object_array_get_idx(luksvolumes, 0); json_object *vaultFileObj = json_object_object_get(volumeObj, "VAULT_FILE"); json_object *vaultSizeObj = json_object_object_get(volumeObj, "VAULT_SIZE"); json_object *volNameObj = json_object_object_get(volumeObj, "VOL_NAME"); json_object *mountPathObj = json_object_object_get(volumeObj, "MOUNT_PATH"); json_object *passPhraseObj = json_object_object_get(volumeObj, "PASSPHRASE_TYPE"); if (!vaultFileObj || json_object_get_type(vaultFileObj) != json_type_string) { log(" - 'VAULT_FILE' attribute is missing or not a string.", LOG_ERR); valid = false; } if (!vaultSizeObj || json_object_get_type(vaultSizeObj) != json_type_string) { log(" - 'VAULT_SIZE' attribute is missing or not a string.", LOG_ERR); valid = false; } if (!volNameObj || json_object_get_type(volNameObj) != json_type_string) { log(" - 'VOL_NAME' attribute is missing or not a string.", LOG_ERR); valid = false; } if (!mountPathObj || json_object_get_type(mountPathObj) != json_type_string) { log(" - 'MOUNT_PATH' attribute is missing or not a string.", LOG_ERR); valid = false; } if (!valid) { log("Missing or invalid attributes in JSON configuration", LOG_ERR); return false; } config.vaultFile = json_object_get_string(vaultFileObj); config.vaultSize = json_object_get_string(vaultSizeObj); config.volName = json_object_get_string(volNameObj); config.mountPath = json_object_get_string(mountPathObj); parsePassphraseType(config, passPhraseObj); return true; } /* *********************************************************************** * * Name : createDefaultDirectory * * Description: This function is to establish the directory structure required * for mounting the LUKS-encrypted filesystem. It helps ensure * that the specified directory path (defaultDirectoryPath) exists. * * ************************************************************************/ bool createDefaultDirectory(const char *defaultDirectoryPath) { if (access(defaultDirectoryPath, F_OK) == 0) { // Default directory already exists return true; } else { string mkdirCommand = "/usr/bin/mkdir -p " + string(defaultDirectoryPath); int status = system(mkdirCommand.c_str()); // An exit status of zero indicates success, and a nonzero value // indicates failure. if (status != 0) { log("Error creating default mount directory: " + string(defaultDirectoryPath), LOG_ERR); log("Create directory failed with status: " + to_string(status), LOG_ERR); return false; } return true; } } /* *********************************************************************** * * Name : createDirectory * * Description: This function is to ensure that a directory exists before * creating a LUKS vault file within it. It handles the creation * of both the specified directory and any parent directories * if necessary. * * ************************************************************************/ bool createDirectory(const char *directoryPath) { if (access(directoryPath, F_OK) == 0) { // Directory already exists return true; } else { string vaultDirectory = directoryPath; size_t lastSlashPos = vaultDirectory.rfind('/'); if (lastSlashPos != string::npos) { string directoryPath = vaultDirectory.substr(0, lastSlashPos); string mkdirCommand = "/usr/bin/mkdir -p " + directoryPath; int status = system(mkdirCommand.c_str()); // An exit status of zero indicates success, and a nonzero value // indicates failure. if (status != 0) { log("Error creating directory for vault file: " + string(directoryPath), LOG_ERR); log("Create directory failed with status: " + to_string(status), LOG_ERR); return false; } } return true; } } /* *********************************************************************** * * Name : checkVaultSize * * Description: This function is responsible for checking the vaultSize * * Note: If the size is not specified or is invalid, it sets a default * size of 256 megabytes. * * ************************************************************************/ int checkVaultSize(const char *vaultSize) { int size = 256; // Convert const char* to string string vaultSizeStr = vaultSize; // Find the first non-numeric character size_t firstNonNumeric = vaultSizeStr.find_first_not_of("0123456789"); if (firstNonNumeric != string::npos) { // Extract the numeric portion and the suffix string sizeStr = vaultSizeStr.substr(0, firstNonNumeric); string suffix = vaultSizeStr.substr(firstNonNumeric); // Convert the extracted string to an integer size = stoi(sizeStr); // Determine the multiplier based on the suffix if (suffix == "M") { // Megabytes size *= 1; } else if (suffix == "G") { // Gigabytes size *= 1024; } else { string log_message = "Invalid vault file size provided: " + string(vaultSize) + ".Using default size for vault file creation."; log(log_message, LOG_INFO); // Set to the default size size = 256; } // Set a minimum size of 256MB if (size < 256) { log("Vault file size below default: The specified size is less" " than the default size of 256MB. Using default size for" " vault file creation.", LOG_INFO); // Set to the minimum default size size = 256; } } else { log("Invalid vault file size format: No size type found." " Using default size of 256MB", LOG_INFO); } return size; } /* *********************************************************************** * * Name : createVaultFile * * Description: This function is responsible for creating a LUKS vault file * with a specified size and a random key using the dd and * cryptsetup utilities. * * ************************************************************************/ bool createVaultFile(const string &modifiedVaultFile, int vaultSize) { // Create the directory path if it doesn't exist if (!createDirectory(modifiedVaultFile.c_str())) { // Directory creation failed return false; } string command = "dd if=/dev/urandom of=" + string(modifiedVaultFile) + " bs=1M count=" + to_string(vaultSize); int status = system(command.c_str()); // An exit status of zero indicates success, and a nonzero value // indicates failure. if (status == 0) { // Command executed successfully return true; } else { log("Error creating LUKS vault image file: " + string(modifiedVaultFile), LOG_ERR); log("Command failed with return status: " + to_string(status), LOG_ERR); return false; } } /* *********************************************************************** * * Name : setupLUKSEncryption * * Description: This function is to secure the data within the vault file by * encrypting it using LUKS encryption. It prepares the vault * file for use as an encrypted volume. * * ************************************************************************/ bool setupLUKSEncryption(const string &modifiedVaultFile, const string &passphrase) { log("Encrypting LUKS Volume", LOG_INFO); string command = "echo -n \"" + string(passphrase) + "\" | cryptsetup luksFormat " + string(modifiedVaultFile) + " -"; int status = system(command.c_str()); // Cryptsetup returns 0 on success and a non-zero value on error. // Return codes on failure: // 1 wrong parameters, 2 no permission (badpassphrase), // 3 out of memory, 4 wrong device specified, // 5 device already exists or device is busy. if (status == 0) { // Command executed successfully return true; } else { // Command failed log("Error setting up LUKS encryption for vault file: " + string(modifiedVaultFile), LOG_ERR); log("Command failed with return status: " + to_string(status), LOG_ERR); return false; } } /* *********************************************************************** * * Name : openLUKSVolume * * Description: This function is to make the encrypted data within the LUKS * vault accessible as a device that can be used for file * operations. * * ************************************************************************/ bool openLUKSVolume(const string &modifiedVaultFile, const char *volName, const string &passphrase) { log("Unsealing LUKS Volume", LOG_INFO); string command = "echo -n \"" + string(passphrase) + "\" | cryptsetup luksOpen " + string(modifiedVaultFile) + " " + string(volName); int status = system(command.c_str()); // Cryptsetup returns 0 on success and a non-zero value on error. // Return codes on failure: // 1 wrong parameters, 2 no permission (badpassphrase), // 3 out of memory, 4 wrong device specified, // 5 device already exists or device is busy. if (status == 0) { // Command executed successfully return true; } else { // Command failed log("Error opening LUKS volume for volume: " + string(volName), LOG_ERR); log("Command failed with return status: " + to_string(status), LOG_ERR); return false; } } /* *********************************************************************** * * Name : createFilesystem * * Description: This function is responsible for creating an Ext4 filesystem * on a specified LUKS volume. * * ************************************************************************/ bool createFilesystem(const char *volName) { log("Creating EXT4 Filesystem", LOG_INFO); string mkfs_command = "mkfs.ext4 /dev/mapper/" + string(volName); // Execute the mkfs command and capture the return status int status = system(mkfs_command.c_str()); // The exit status returned by mkfs is 0 on success and 1 on failure. if (status == 0) { // Command executed successfully return true; } else { // Command failed log("Error creating filesystem for volume: " + string(volName), LOG_ERR); log("Command failed with return status: " + to_string(status), LOG_ERR); return false; } } /* *********************************************************************** * * Name : isMountPathValid * * Description: This function is to determine whether the provided mount path * is within the expected directory structure or if it needs to * be adjusted to a default mount path. * * ************************************************************************/ bool isMountPathValid(const char *mountPath, const char *defaultDirectoryPath) { // Check if the provided mount path starts with the default directory path if (strncmp(mountPath, defaultDirectoryPath, strlen(defaultDirectoryPath)) != 0) { return false; } return true; } /* *********************************************************************** * * Name : mountFilesystem * * Description: This function is responsible for mounting the LUKS-encrypted * filesystem to a specified or default mount path. * * ************************************************************************/ bool mountFilesystem(const char *volName, const char *mountPath, const char *defaultDirectoryPath) { log("Mounting Filesystem", LOG_INFO); if (!isMountPathValid(mountPath, defaultDirectoryPath)) { log("Mount path is not valid, using default mount path.", LOG_INFO); mountPath = defaultMountPath; // Use default mount path } string mkdir_command = "/usr/bin/mkdir -p " + string(mountPath); int status_check = system(mkdir_command.c_str()); // An exit status of zero indicates success, and a nonzero value // indicates failure. if (status_check != 0) { log("Creation of mount path directory failed with return" "status: " + to_string(status_check), LOG_ERR); return false; } string mount_command = "/usr/bin/mount /dev/mapper/" + string(volName) + " " + string(mountPath); int status = system(mount_command.c_str()); // On success, zero is returned. On error, -1 is returned, and // errno is set to indicate the error. if (status == 0) { log("Mounting filesystem successful.", LOG_INFO); return true; } else { log("Error mounting filesystem for volume: " + string(volName), LOG_ERR); log("Mount command failed with return status: " + to_string(status), LOG_ERR); return false; } } /* *********************************************************************** * * Name : writeJSONToFile * * Description: This function writes a provided JSON object to a specified file * It handles file opening, JSON-to-string conversion, * error detection, and file closure, returning true on successful * write and false on failure. * * ************************************************************************/ bool writeJSONToFile(const char *filePath, json_object *jsonObj) { log("Creating json config file", LOG_INFO); FILE *file = fopen(filePath, "w"); if (file == nullptr) { log("Error opening file for writing JSON.", LOG_ERR); return false; } // Convert JSON object to a string const char *jsonStr = json_object_to_json_string_ext(jsonObj, JSON_C_TO_STRING_PRETTY); if (jsonStr == nullptr) { log("Error converting JSON to string.", LOG_ERR); fclose(file); return false; } // Write the JSON string to the file if (fprintf(file, "%s\n", jsonStr) < 0) { log("Error writing JSON to file.", LOG_ERR); fclose(file); return false; } fclose(file); return true; } /* *********************************************************************** * * Name : getPassPhraseType * * Description: This function simply returns "HWID" * indicating the type of passphrase as "Hardware Identifier." * * ************************************************************************/ string getPassPhraseType() { return "HWID"; } /* *********************************************************************** * * Name : passPhraseType * * Description: This function simply returns passPhraseType * indicating the type of passphrase as "Hardware Identifier." * * ************************************************************************/ PassphraseMechanism passPhraseType() { return HWID_Firmware; } /* *********************************************************************** * * Name : unmountFilesystem * * Description: This function is responsible to umount filesystem at * specified 'mountPath'. * * ************************************************************************/ bool unmountFilesystem(const char* mountPath) { log("Unmounting Filesystem", LOG_INFO); // Check if the filesystem is already unmounted string check_mount_command = "/usr/bin/mount | grep " + string(mountPath); if (system(check_mount_command.c_str()) == 0) { string umount_command = "/usr/bin/umount " + string(mountPath); if (system(umount_command.c_str()) != 0) { log("Error unmounting the filesystem.", LOG_ERR); return false; } } else { // The filesystem is already unmounted, no action needed log("Filesystem is already unmounted.", LOG_INFO); } return true; } /* *********************************************************************** * * Name : increaseVaultSize * * Description: This function is resposible to increase the size of vaultfile * to a given defaultSize in megabytes. * * ************************************************************************/ bool increaseVaultSize(const char* vaultFile, int defaultSize) { log("Increasing Vault file size", LOG_INFO); string increase_size_command = "/usr/bin/truncate -s " + to_string(defaultSize) + "M " + string(vaultFile); if (system(increase_size_command.c_str()) != 0) { log("Error increasing the size of the vault file.", LOG_ERR); return false; } return true; } /* *********************************************************************** * * Name : resizeLUKSVolume * * Description: This function resizes a LUKS-encrypted * volume specified by volName using the provided passphrase * * ************************************************************************/ bool resizeLUKSVolume(const char* volName, const char* passphrase) { log("Resizing LUKS Volume", LOG_INFO); string resize_command = "echo -n \"" + string(passphrase) + "\" | /usr/sbin/cryptsetup resize " + string(volName) + " -"; if (system(resize_command.c_str()) != 0) { log("Error resizing the LUKS volume.", LOG_ERR); return false; } return true; } /* *********************************************************************** * * Name : checkFilesystem * * Description: This function performs a filesystem check using e2fsck on a * specified volume (volName). * * ************************************************************************/ bool checkFilesystem(const char* volName) { log("Checking Filesystem for errors", LOG_INFO); string e2fsck_command = "/usr/sbin/e2fsck -fy /dev/mapper/" + string(volName); int status = system(e2fsck_command.c_str()); if ((status == 0) || (status == 2048)) { // Command executed successfully return true; } else { log("Error checking the filesystem.", LOG_ERR); return true; } } /* *********************************************************************** * * Name : resizeFilesystem * * Description: This function resizes the filesystem on a specified * volume (volName). * * ************************************************************************/ bool resizeFilesystem(const char* volName) { log("Resizing Filesystem", LOG_INFO); string resize_fs_command = "/usr/sbin/resize2fs /dev/mapper/" + string(volName); if (system(resize_fs_command.c_str()) != 0) { log("Error resizing the filesystem.", LOG_ERR); return false; } return true; } /* *********************************************************************** * * Name : remountFilesystem * * Description: This function is used to remount a previously * unmounted filesystem onto a specified mountPath * * ************************************************************************/ bool remountFilesystem(const char* volName, const char* mountPath) { log("Re-mounting Filesystem", LOG_INFO); string mount_command = "/usr/bin/mount /dev/mapper/" + string(volName) + " " + string(mountPath); if (system(mount_command.c_str()) != 0) { log("Error mounting the filesystem back.", LOG_ERR); return false; } return true; } /* *********************************************************************** * * Name : resizeVault * * Description: This function function orchestrates the process of resizing * a LUKS-encrypted vault file and its associated filesystem. * It performs a sequence of steps, including unmounting the * filesystem, increasing the vault size, checking the * filesystem, resizing the LUKS volume, checking the filesystem * again, resizing the filesystem, and remounting the filesystem. * * ************************************************************************/ bool resizeVault(const char* vaultFile, int defaultSize, const char* volName, const char* mountPath, const char* passphrase) { if (unmountFilesystem(mountPath) && increaseVaultSize(vaultFile, defaultSize) && checkFilesystem(volName) && resizeLUKSVolume(volName, passphrase) && checkFilesystem(volName) && resizeFilesystem(volName) && remountFilesystem(volName, mountPath)) { log("Resize successful for LUKS volume", LOG_INFO); return true; } else { log("Resize failed for LUKS Volume", LOG_ERR); return false; } } /* *********************************************************************** * * Name : monitorLUKSVolume * * Description: This function monitors the LUKS volume status and runs * in loop until there's any issue with the LUKS volume. * * ************************************************************************/ void monitorLUKSVolume(const string& volumeName) { while (1) { string statusCommand = "cryptsetup status " + volumeName + " 2>/dev/null"; int status = system(statusCommand.c_str()); if (status != 0) { string errorMessage = "LUKS volume is not in use. " "Error code: " + to_string(status); log(errorMessage, LOG_ERR); break; } sleep(SLEEP_DURATION); } } bool execCmd(const string &cmd, string &result) { const int MAX_BUF = 256; char buf[MAX_BUF]; result = ""; int cmdStatus; bool retVal = false; FILE *fstream = popen(cmd.c_str(), "r"); if (!fstream) return retVal; if (fstream) { while (!feof(fstream)) { if (fgets(buf, MAX_BUF, fstream) != NULL) result.append(buf); } cmdStatus = pclose(fstream); } if (!result.empty()) result = result.substr(0, result.size() - 1); if (WIFEXITED(cmdStatus) && WEXITSTATUS(cmdStatus) == 0) { retVal = true; } return retVal; } void syncLuksVolume() { string output = ""; string hostname = ""; string logMsg = ""; string standbyHostname = ""; string facterActiveCmd = "FACTERLIB=/usr/share/puppet/modules/" "platform/lib/facter/ facter | egrep \"active\""; string facterStandAloneCmd = "FACTERLIB=/usr/share/puppet/modules/" "platform/lib/facter/ facter | egrep \"standalone\""; int isStandAlone = 0, isActive = 0; int maxRetries = 3; int retryDelay = 5; size_t strFound = 0; try { // Get the hostname if (!execCmd("/usr/bin/hostname", hostname)) { logMsg = "Command failed: Unable to retrieve hostname: "; log(logMsg, LOG_ERR); throw std::runtime_error("Command Hostname failed."); } // Check if controller is not standalone/simplex if (!execCmd(facterStandAloneCmd, output)) { logMsg = "Command failed: Unable to fetch FACTER standalone"; log(logMsg, LOG_ERR); throw std::runtime_error("Command: FACTER standalone failed."); } strFound = output.find("is_standalone_controller => false"); if (strFound != string::npos) { isStandAlone = 1; } // Check if the controller is active // Use the FACTER to get the active status of controller if (!execCmd(facterActiveCmd, output)) { logMsg = "Command failed: Unable to fetch FACTER."; log(logMsg, LOG_ERR); throw std::runtime_error("Command: FACTER active failed."); } // Check if controller is active and then // rsync the changes to standby controller. strFound = output.find("is_controller_active => true"); if (strFound != string::npos) { isActive = 1; } if (isActive && isStandAlone) { logMsg = "Active controller found is " + hostname; log(logMsg, LOG_INFO); // Check the name of standby controller if (strcmp(CONTROLLER_0, hostname.c_str()) == 0) { standbyHostname = CONTROLLER_1; } else { standbyHostname = CONTROLLER_0; } // Loop to retry rsyncing for (int attempt = 1; attempt <= maxRetries; ++attempt) { string rsyncCmd = "rsync -v -acv --delete " "/var/luks/stx/luks_fs/controller/ rsync://"; rsyncCmd.append(standbyHostname); rsyncCmd += "/luksdata/"; if (!execCmd(rsyncCmd, output)) { logMsg = "rysnc failed: Command execution " "failed: " + rsyncCmd; log(logMsg, LOG_ERR); if (attempt < maxRetries) { log("Retrying...", LOG_INFO); sleep(retryDelay); // Wait for 5 secs continue; // Retry rsync } else { throw runtime_error("rsync failed after " "multiple retries"); } } else { logMsg = "rysnc successful: " + rsyncCmd; log(logMsg, LOG_INFO); break; // exit on rysnc success } } // loop ends } } catch (const exception &ex) { string errorStr = "rsync failed, error: " + string(ex.what()); log(errorStr, LOG_ERR); } return; } void notifyLuksVolumeChange(const char* luksPath) { char buffer[4096]; ssize_t totalBufferBytes; // Sleep for 15 secs while the luks_volume mounted. // This is to avoid the race condition between inotifyThread // and luksVolume creation. This sleep will give that extra bit // of time for luksVolume to be created and mounted. sleep(INOTIFY_THREAD_SLEEP); // Initialize iNotify int inotify_fd = inotify_init(); if (inotify_fd < 0) { log("inotify_init failed to initialize", LOG_ERR); close(inotify_fd); return; } // Adding luksVolumeDirectory to watch int wd = inotify_add_watch(inotify_fd, luksPath, IN_MODIFY | IN_CREATE | IN_DELETE); if (wd < 0) { string errMsg = "inotify_add_watch failed: Failed to add " "watch for dir: " + string(luksPath); log(errMsg, LOG_ERR); close(inotify_fd); return; } while (true) { // Listen for notification event // The read() method blocks until one or more alerts arrive totalBufferBytes = read(inotify_fd, buffer, sizeof(buffer)); if (totalBufferBytes < 0) { log("Failed to read event.", LOG_ERR); continue; } for (char* ptr = buffer; ptr < buffer + totalBufferBytes;) { struct inotify_event* event = reinterpret_cast(ptr); if (event->mask & (IN_MODIFY | IN_CREATE | IN_DELETE)) { log("Change detected: " + string(event->name), LOG_INFO); // Initiate rsync when change detected syncLuksVolume(); } ptr += sizeof(struct inotify_event) + event->len; } // ending for loop } // While close(inotify_fd); } /* Creates PID file and adds the pid*/ int daemon_create_pidfile ( void ) { FILE * pid_file_stream = (FILE *)(NULL); string errorMessage = ""; /* Create PID file */ pid_t mypid = getpid(); /* Check for another instance running by trying to open in read only mode. * If it opens then there "may" be another process running. * If it opens then read the pid and see if that pID exists. * If it does then this is a duplicate process so exit. */ pid_file_stream = fopen (pidFileName, "r" ) ; if ( pid_file_stream ) { int rc = 0 ; pid_t pid = 0 ; char buffer[BUFFER]; if ( fgets ( buffer, BUFFER, pid_file_stream) != NULL ) { rc = sscanf ( &buffer[0], "%d", &pid ); if ( rc == 1 ) { rc = kill ( pid, 0 ); if ( rc == 0 ) { errorMessage = "Refusing to start duplicate process pid: " + to_string(pid); log(errorMessage, LOG_ERR); fclose (pid_file_stream); exit (0); } } } } if ( pid_file_stream ) fclose (pid_file_stream); /* if we got here then we are ok to run */ pid_file_stream = fopen (pidFileName, "w" ) ; if ( pid_file_stream == NULL ) { syslog ( LOG_ERR, "Failed to open or create %s\n", pidFileName); return ( FAIL_PID_OPEN ); } else if (!fprintf (pid_file_stream,"%d", mypid)) { syslog ( LOG_ERR, "Failed to write pid file for %s\n", pidFileName ); fclose ( pid_file_stream ) ; return ( FAIL_FILE_WRITE ) ; } syslog ( LOG_INFO, "opened and written PID file:(pid:%d) FileName: %s\n",mypid, pidFileName); fflush (pid_file_stream); fclose (pid_file_stream); return (0); } /* Signal handler to handle termination signals */ void signal_handler(int signo) { if (signo == SIGTERM) { // Cleanup tasks and exit the daemon log("luks daemon: Received SIGTERM. Exiting", LOG_INFO); exit(EXIT_SUCCESS); } } int main() { int rc = 0; int ret = daemon(0, 0); if (ret != 0) { string errorMessage = "Failed to run luks-fs-mgr as daemon service. " "Error code: " + to_string(ret); log(errorMessage, LOG_ERR); return 1; } /* create PID file */ ret = daemon_create_pidfile(); if (ret != 0) { return ret; } /* Install signal handler for termination signals */ signal(SIGTERM, signal_handler); LuksConfig luksConfig; CreatedLuksConfig createdLuksConfig; string passphrase; // create a JSON object to store the used attributes. json_object *jsonConfig; json_object *usedAttributes = json_object_new_object(); json_object *luksvolumesArray = json_object_new_array(); json_object *attributesObj = json_object_new_object(); // Getting Passphrase from passphraseGenerator PassphraseMechanism selectedMechanism = passPhraseType(); auto passphraseGenerator = PassphraseGeneratorFactory::createPassphraseGenerator(selectedMechanism); bool passStatus = passphraseGenerator->generatePassphrase(passphrase); // Create a thread for luks volume monitoring std::thread notifyThread(notifyLuksVolumeChange, luksControllerDataPath); // Validating if passphrase is empty if (passphrase.empty() || passStatus == false) { log("Passphrase generation failed or" " returned an empty passphrase.", LOG_ERR); rc = 1; goto cleanup; } // Parse default configuration and extract volume attributes if (!parseJSONConfig(configFile, luksConfig, &jsonConfig)) { rc = 1; goto cleanup; } if (access(createdConfigFile, F_OK) == 0) { // Parse the createdConfigfile and extract volume attributes if (!parseJSONConfig(createdConfigFile, createdLuksConfig, &jsonConfig)) { rc = 1; goto cleanup; } else { log("created_luks.json exists", LOG_INFO); } // Logging the successfully parsed attributes of created_luks.json string log_message = "Vault File: " + string(createdLuksConfig.vaultFile) + ", Vault Size: " + string(createdLuksConfig.vaultSize) + ", Volume Name: " + string(createdLuksConfig.volName) + ", Mount Path: " + string(createdLuksConfig.mountPath) + ", Passphrase Type: " + string(createdLuksConfig.passphraseType); log(log_message, LOG_INFO); // Resizing logic begin here string statusCommand = "cryptsetup status " + string(createdLuksConfig.volName) + " 2>/dev/null"; int status = system(statusCommand.c_str()); // Cryptsetup returns 0 on success and a non-zero value on error. // Return codes on failure: // 1 wrong parameters, 2 no permission (badpassphrase), // 3 out of memory, 4 wrong device specified, // 5 device already exists or device is busy. if (status == 0) { log("LUKS device is already open", LOG_INFO); } else { log("LUKS device is not open. Try opening", LOG_INFO); if (!openLUKSVolume(createdLuksConfig.vaultFile, createdLuksConfig.volName, passphrase.c_str())) { rc = 1; goto cleanup; } log("LUKS device is successfully opened", LOG_INFO); } int defaultsize = 0; int createdsize = 0; defaultsize = checkVaultSize(luksConfig.vaultSize); createdsize = checkVaultSize(createdLuksConfig.vaultSize); string volName = string(createdLuksConfig.volName); if (defaultsize > createdsize) { log("Resizing the vault file.", LOG_INFO); if (resizeVault(createdLuksConfig.vaultFile, defaultsize, createdLuksConfig.volName, createdLuksConfig.mountPath, passphrase.c_str())) { // Assigning values to attributes used to write // created_luks.json string sizeStr = to_string(defaultsize); luksConfig.vaultSize = (sizeStr + "M").c_str(); json_object_object_add(attributesObj, "PASSPHRASE_TYPE", json_object_new_string(createdLuksConfig.passphraseType)); json_object_object_add(attributesObj, "VAULT_FILE", json_object_new_string(createdLuksConfig.vaultFile)); json_object_object_add(attributesObj, "VAULT_SIZE", json_object_new_string(luksConfig.vaultSize)); json_object_object_add(attributesObj, "VOL_NAME", json_object_new_string(createdLuksConfig.volName)); json_object_object_add(attributesObj, "MOUNT_PATH", json_object_new_string(createdLuksConfig.mountPath)); json_object_array_add(luksvolumesArray, attributesObj); json_object_object_add(usedAttributes, "luksvolumes", luksvolumesArray); if (!writeJSONToFile(createdConfigFile, usedAttributes)) { log("Error writing JSON file.", LOG_ERR); rc = 1; goto cleanup; } } else { rc = 1; goto cleanup; } } else { log("No resizing required", LOG_INFO); // No Reszing to handle, start the service normally // Check if the mount path exists if (access(luksConfig.mountPath, F_OK) == 0) { string mount_command = "/usr/bin/mountpoint -q " + string(luksConfig.mountPath); int mountpoint_status = system(mount_command.c_str()); // mountpoint has the following exit status values: // 0: success; the directory is a mountpoint, // or device is block device // 1: failure; incorrect invocation, permissions or system error // 32: failure; the directory is not a mountpoint, // or device is not a block device if (mountpoint_status != 0) { // Mount path directory is not mount point, // proceed to mount it if (!mountFilesystem(luksConfig.volName, luksConfig.mountPath, defaultDirectoryPath)) { rc = 1; goto cleanup; } log("Encrypted vault is mounted.", LOG_INFO); } else { log("Encrypted vault is already mounted.", LOG_INFO); } } else { // Mount path does not exist, create filesystem and then mount if (!createFilesystem(luksConfig.volName)) { log("Error creating filesystem", LOG_ERR); rc = 1; goto cleanup; } if (!mountFilesystem(luksConfig.volName, luksConfig.mountPath, defaultDirectoryPath)) { rc = 1; goto cleanup; } log("Encrypted vault is mounted.", LOG_INFO); } } monitorLUKSVolume(volName); rc = 0; goto cleanup; } else { // Execute the below code when service start during first boot // Create default directory for the service to create FS and mount if (!createDefaultDirectory(defaultDirectoryPath)) { rc = 1; goto cleanup; } string log_message = "Vault File: " + string(luksConfig.vaultFile) + ", Vault Size: " + string(luksConfig.vaultSize) + ", Volume Name: " + string(luksConfig.volName) + ", Mount Path: " + string(luksConfig.mountPath); log(log_message, LOG_INFO); // Create a new string to hold the created values string modifiedVaultFile = luksConfig.vaultFile; string mountPath = luksConfig.mountPath; string volName = luksConfig.volName; // Check if directory path is provided in vaultFile size_t lastSlashPos = modifiedVaultFile.rfind('/'); if (lastSlashPos == string::npos) { // No directory path provided, use default directory modifiedVaultFile = "/var/luks/stx/" + modifiedVaultFile; } // Check if the vault file exists if ((access(luksConfig.vaultFile, F_OK) == 0) || (access(modifiedVaultFile.c_str(), F_OK) == 0)) { // The vault file exists, proceed to unseal string statusCommand = "cryptsetup status " + string(luksConfig.volName) + " 2>/dev/null"; int status = system(statusCommand.c_str()); // Cryptsetup returns 0 on success and a non-zero value on error. // Return codes on failure: // 1 wrong parameters, 2 no permission (badpassphrase), // 3 out of memory, 4 wrong device specified, // 5 device already exists or device is busy. if (status == 0) { log("LUKS device is already open", LOG_INFO); } else { log("LUKS device is not open. Try opening", LOG_INFO); if (!openLUKSVolume(modifiedVaultFile.c_str(), luksConfig.volName, passphrase.c_str())) { rc = 1; goto cleanup; } log("LUKS device is successfully opened", LOG_INFO); } // Check if the mount path exists if (access(luksConfig.mountPath, F_OK) == 0) { string mount_command = "/usr/bin/mountpoint -q " + string(luksConfig.mountPath); int mountpoint_status = system(mount_command.c_str()); // mountpoint has the following exit status values: // 0: success; the directory is a mountpoint, // or device is block device // 1: failure; incorrect invocation, permissions or system error // 32: failure; the directory is not a mountpoint, // or device is not a block device on if (mountpoint_status != 0) { // Mount path directory is not mount point, // proceed to mount it if (!mountFilesystem(luksConfig.volName, luksConfig.mountPath, defaultDirectoryPath)) { rc = 1; goto cleanup; } log("Encrypted vault is mounted.", LOG_INFO); } else { log("Encrypted vault is already mounted.", LOG_INFO); } } else { // Mount path does not exist, create filesystem and then mount if (!createFilesystem(luksConfig.volName)) { log("Error creating filesystem", LOG_ERR); rc = 1; goto cleanup; } if (!mountFilesystem(luksConfig.volName, luksConfig.mountPath, defaultDirectoryPath)) { rc = 1; goto cleanup; } log("Encrypted vault is mounted.", LOG_INFO); } } else { // The vault file does not exist, create it // Create the necessary directories if they don't exist log("The vault image file does not exist, creating one", LOG_INFO); int size = checkVaultSize(luksConfig.vaultSize); if (!createVaultFile(modifiedVaultFile.c_str(), size)) { log("Error creating LUKS vault image file", LOG_ERR); rc = 1; goto cleanup; } // Set up LUKS encryption if (!setupLUKSEncryption(modifiedVaultFile.c_str(), passphrase.c_str())) { log("Error setting up LUKS encryption", LOG_ERR); rc = 1; goto cleanup; } // Open LUKS Volume if (!openLUKSVolume(modifiedVaultFile.c_str(), luksConfig.volName, passphrase.c_str())) { log("Error opening LUKS volume", LOG_ERR); rc = 1; goto cleanup; } // Create filesystem if (!createFilesystem(luksConfig.volName)) { log("Error creating filesystem", LOG_ERR); rc = 1; goto cleanup; } // Mount filesystem if (!mountFilesystem(luksConfig.volName, luksConfig.mountPath, defaultDirectoryPath)) { rc = 1; goto cleanup; } log("Encrypted vault is set up and mounted.", LOG_INFO); } if (!isMountPathValid(luksConfig.mountPath, defaultDirectoryPath)) { log("Mount path is not valid, using default mount path.", LOG_INFO); mountPath = defaultMountPath; // Use default mount path } // Assigning values to attributes used to write created_luks.json int size = checkVaultSize(luksConfig.vaultSize); string sizeStr = to_string(size); string ppType = getPassPhraseType(); createdLuksConfig.vaultSize = (sizeStr + "M").c_str(); createdLuksConfig.vaultFile = modifiedVaultFile.c_str(); createdLuksConfig.volName = luksConfig.volName; createdLuksConfig.mountPath = mountPath.c_str(); createdLuksConfig.passphraseType = ppType.c_str(); json_object_object_add(attributesObj, "PASSPHRASE_TYPE", json_object_new_string(createdLuksConfig.passphraseType)); json_object_object_add(attributesObj, "VAULT_FILE", json_object_new_string(createdLuksConfig.vaultFile)); json_object_object_add(attributesObj, "VAULT_SIZE", json_object_new_string(createdLuksConfig.vaultSize)); json_object_object_add(attributesObj, "VOL_NAME", json_object_new_string(createdLuksConfig.volName)); json_object_object_add(attributesObj, "MOUNT_PATH", json_object_new_string(createdLuksConfig.mountPath)); json_object_array_add(luksvolumesArray, attributesObj); json_object_object_add(usedAttributes, "luksvolumes", luksvolumesArray); if (!writeJSONToFile(createdConfigFile, usedAttributes)) { log("Error writing JSON file.", LOG_ERR); rc = 1; goto cleanup; } monitorLUKSVolume(volName); rc = 0; goto cleanup; } notifyThread.join(); cleanup: if (notifyThread.joinable()) { notifyThread.join(); } json_object_put(jsonConfig); json_object_put(usedAttributes); json_object_put(luksvolumesArray); json_object_put(attributesObj); return (rc); }