/* $Id: PasswordInput.cpp $ */ /** @file * Frontend shared bits - Password file and console input helpers. */ /* * Copyright (C) 2012-2023 Oracle and/or its affiliates. * * This file is part of VirtualBox base platform packages, as * available from https://www.virtualbox.org. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation, in version 3 of the * License. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * SPDX-License-Identifier: GPL-3.0-only */ /********************************************************************************************************************************* * Header Files * *********************************************************************************************************************************/ #include "PasswordInput.h" #include #include #include #include #include /** * Reads a password from the password file. * * Only first line is used. The passwords length must be less than 512 bytes * * @param pszFilename The path to file containing the password * @param pPasswd The string where password will be returned * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + msg. */ RTEXITCODE readPasswordFile(const char *pszFilename, com::Utf8Str *pPasswd) { size_t cbFile; char szPasswd[512] = { 0 }; int vrc = VINF_SUCCESS; RTEXITCODE rcExit = RTEXITCODE_SUCCESS; bool fStdIn = !strcmp(pszFilename, "stdin"); PRTSTREAM pStrm; if (!fStdIn) vrc = RTStrmOpen(pszFilename, "r", &pStrm); else pStrm = g_pStdIn; if (RT_SUCCESS(vrc)) { vrc = RTStrmReadEx(pStrm, szPasswd, sizeof(szPasswd)-1, &cbFile); if (RT_SUCCESS(vrc)) { size_t cbSize = RT_MIN(sizeof(szPasswd)-1, cbFile); unsigned i; for (i = 0; i < cbSize && !RTLocCIsCntrl(szPasswd[i]); i++) ; szPasswd[i] = '\0'; /* If the line containing password doesn't fit into buffer */ if (i >= sizeof(szPasswd)-1 && cbFile >= sizeof(szPasswd)) rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Provided password in file '%s' is too long", pszFilename); else *pPasswd = szPasswd; } else rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Cannot read password from file '%s': %Rrc", pszFilename, vrc); if (!fStdIn) RTStrmClose(pStrm); } else rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Cannot open password file '%s' (%Rrc)", pszFilename, vrc); return rcExit; } /** * Sets password for settings from password file * * Only first line is used. The passwords length must be less than 512 bytes * * @param virtualBox The IVirtualBox interface the settings password will be set for * @param pszFilename The path to file containing the password * @return RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + msg. */ RTEXITCODE settingsPasswordFile(ComPtr virtualBox, const char *pszFilename) { com::Utf8Str passwd; RTEXITCODE rcExit = readPasswordFile(pszFilename, &passwd); if (rcExit == RTEXITCODE_SUCCESS) { HRESULT hrc; CHECK_ERROR(virtualBox, SetSettingsSecret(com::Bstr(passwd).raw())); if (FAILED(hrc)) rcExit = RTEXITCODE_FAILURE; } return rcExit; } /** * Gets the password from the user input * * * @param pPassword The string where password will be returned * @param pszPrompt The prompt string for user * @return RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + msg. */ RTEXITCODE readPasswordFromConsole(com::Utf8Str *pPassword, const char *pszPrompt, ...) { RTEXITCODE rcExit = RTEXITCODE_SUCCESS; char aszPwdInput[_1K] = { 0 }; va_list vaArgs; va_start(vaArgs, pszPrompt); int vrc = RTStrmPrintfV(g_pStdOut, pszPrompt, vaArgs); if (RT_SUCCESS(vrc)) { bool fEchoOld = false; vrc = RTStrmInputGetEchoChars(g_pStdIn, &fEchoOld); if (RT_SUCCESS(vrc)) { vrc = RTStrmInputSetEchoChars(g_pStdIn, false); if (RT_SUCCESS(vrc)) { vrc = RTStrmGetLine(g_pStdIn, &aszPwdInput[0], sizeof(aszPwdInput)); if (RT_SUCCESS(vrc)) { #ifdef RT_OS_WINDOWS /* * Returned string encoded in console code page (e.g. Win-125X or CP-XXX). * Convert it to Utf-8 */ char *pszPassword = NULL; vrc = RTStrConsoleCPToUtf8(&pszPassword, aszPwdInput); if (RT_SUCCESS(vrc) && pszPassword) { *pPassword = pszPassword; RTMemFree(pszPassword); } else rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to convert password from windows console codepage to Utf-8 (%Rrc)", vrc); #else *pPassword = aszPwdInput; #endif } else rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed read password from command line (%Rrc)", vrc); int vrc2 = RTStrmInputSetEchoChars(g_pStdIn, fEchoOld); AssertRC(vrc2); } else rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to disable echoing typed characters (%Rrc)", vrc); } else rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to retrieve echo setting (%Rrc)", vrc); RTStrmPutStr(g_pStdOut, "\n"); } else rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to print prompt (%Rrc)", vrc); va_end(vaArgs); return rcExit; }