commands-windows-ssh.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712
  1. /*
  2. * QEMU Guest Agent win32-specific command implementations for SSH keys.
  3. * The implementation is opinionated and expects the SSH implementation to
  4. * be OpenSSH.
  5. *
  6. * Copyright Schweitzer Engineering Laboratories. 2024
  7. *
  8. * Authors:
  9. * Aidan Leuck <aidan_leuck@selinc.com>
  10. *
  11. * This work is licensed under the terms of the GNU GPL, version 2 or later.
  12. * See the COPYING file in the top-level directory.
  13. */
  14. #include "qemu/osdep.h"
  15. #include <aclapi.h>
  16. #include <qga-qapi-types.h>
  17. #include "commands-common-ssh.h"
  18. #include "commands-windows-ssh.h"
  19. #include "guest-agent-core.h"
  20. #include "limits.h"
  21. #include "lmaccess.h"
  22. #include "lmapibuf.h"
  23. #include "lmerr.h"
  24. #include "qapi/error.h"
  25. #include "qga-qapi-commands.h"
  26. #include "sddl.h"
  27. #include "shlobj.h"
  28. #include "userenv.h"
  29. #define AUTHORIZED_KEY_FILE "authorized_keys"
  30. #define AUTHORIZED_KEY_FILE_ADMIN "administrators_authorized_keys"
  31. #define LOCAL_SYSTEM_SID "S-1-5-18"
  32. #define ADMIN_SID "S-1-5-32-544"
  33. /*
  34. * Frees userInfo structure. This implements the g_auto cleanup
  35. * for the structure.
  36. */
  37. void free_userInfo(PWindowsUserInfo info)
  38. {
  39. g_free(info->sshDirectory);
  40. g_free(info->authorizedKeyFile);
  41. LocalFree(info->SSID);
  42. g_free(info->username);
  43. g_free(info);
  44. }
  45. /*
  46. * Gets the admin SSH folder for OpenSSH. OpenSSH does not store
  47. * the authorized_key file in the users home directory for security reasons and
  48. * instead stores it at %PROGRAMDATA%/ssh. This function returns the path to
  49. * that directory on the users machine
  50. *
  51. * parameters:
  52. * errp -> error structure to set when an error occurs
  53. * returns: The path to the ssh folder in %PROGRAMDATA% or NULL if an error
  54. * occurred.
  55. */
  56. static char *get_admin_ssh_folder(Error **errp)
  57. {
  58. /* Allocate memory for the program data path */
  59. g_autofree char *programDataPath = NULL;
  60. char *authkeys_path = NULL;
  61. PWSTR pgDataW = NULL;
  62. g_autoptr(GError) gerr = NULL;
  63. /* Get the KnownFolderPath on the machine. */
  64. HRESULT folderResult =
  65. SHGetKnownFolderPath(&FOLDERID_ProgramData, 0, NULL, &pgDataW);
  66. if (folderResult != S_OK) {
  67. error_setg(errp, "Failed to retrieve ProgramData folder");
  68. return NULL;
  69. }
  70. /* Convert from a wide string back to a standard character string. */
  71. programDataPath = g_utf16_to_utf8(pgDataW, -1, NULL, NULL, &gerr);
  72. CoTaskMemFree(pgDataW);
  73. if (!programDataPath) {
  74. error_setg(errp,
  75. "Failed converting ProgramData folder path to UTF-16 %s",
  76. gerr->message);
  77. return NULL;
  78. }
  79. /* Build the path to the file. */
  80. authkeys_path = g_build_filename(programDataPath, "ssh", NULL);
  81. return authkeys_path;
  82. }
  83. /*
  84. * Gets the path to the SSH folder for the specified user. If the user is an
  85. * admin it returns the ssh folder located at %PROGRAMDATA%/ssh. If the user is
  86. * not an admin it returns %USERPROFILE%/.ssh
  87. *
  88. * parameters:
  89. * username -> Username to get the SSH folder for
  90. * isAdmin -> Whether the user is an admin or not
  91. * errp -> Error structure to set any errors that occur.
  92. * returns: path to the ssh folder as a string.
  93. */
  94. static char *get_ssh_folder(const char *username, const bool isAdmin,
  95. Error **errp)
  96. {
  97. DWORD maxSize = MAX_PATH;
  98. g_autofree char *profilesDir = g_new0(char, maxSize);
  99. if (isAdmin) {
  100. return get_admin_ssh_folder(errp);
  101. }
  102. /* If not an Admin the SSH key is in the user directory. */
  103. /* Get the user profile directory on the machine. */
  104. BOOL ret = GetProfilesDirectory(profilesDir, &maxSize);
  105. if (!ret) {
  106. error_setg_win32(errp, GetLastError(),
  107. "failed to retrieve profiles directory");
  108. return NULL;
  109. }
  110. /* Builds the filename */
  111. return g_build_filename(profilesDir, username, ".ssh", NULL);
  112. }
  113. /*
  114. * Creates an entry for the user so they can access the ssh folder in their
  115. * userprofile.
  116. *
  117. * parameters:
  118. * userInfo -> Information about the current user
  119. * pACL -> Pointer to an ACL structure
  120. * errp -> Error structure to set any errors that occur
  121. * returns -> 1 on success, 0 otherwise
  122. */
  123. static bool create_acl_user(PWindowsUserInfo userInfo, PACL *pACL, Error **errp)
  124. {
  125. const int aclSize = 1;
  126. PACL newACL = NULL;
  127. EXPLICIT_ACCESS eAccess[1];
  128. PSID userPSID = NULL;
  129. /* Get a pointer to the internal SID object in Windows */
  130. bool converted = ConvertStringSidToSid(userInfo->SSID, &userPSID);
  131. if (!converted) {
  132. error_setg_win32(errp, GetLastError(), "failed to retrieve user %s SID",
  133. userInfo->username);
  134. goto error;
  135. }
  136. /* Set the permissions for the user. */
  137. eAccess[0].grfAccessPermissions = GENERIC_ALL;
  138. eAccess[0].grfAccessMode = SET_ACCESS;
  139. eAccess[0].grfInheritance = NO_INHERITANCE;
  140. eAccess[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
  141. eAccess[0].Trustee.TrusteeType = TRUSTEE_IS_USER;
  142. eAccess[0].Trustee.ptstrName = (LPTSTR)userPSID;
  143. /* Set the ACL entries */
  144. DWORD setResult;
  145. /*
  146. * If we are given a pointer that is already initialized, then we can merge
  147. * the existing entries instead of overwriting them.
  148. */
  149. if (*pACL) {
  150. setResult = SetEntriesInAcl(aclSize, eAccess, *pACL, &newACL);
  151. } else {
  152. setResult = SetEntriesInAcl(aclSize, eAccess, NULL, &newACL);
  153. }
  154. if (setResult != ERROR_SUCCESS) {
  155. error_setg_win32(errp, GetLastError(),
  156. "failed to set ACL entries for user %s %lu",
  157. userInfo->username, setResult);
  158. goto error;
  159. }
  160. /* Free any old memory since we are going to overwrite the users pointer. */
  161. LocalFree(*pACL);
  162. *pACL = newACL;
  163. LocalFree(userPSID);
  164. return true;
  165. error:
  166. LocalFree(userPSID);
  167. return false;
  168. }
  169. /*
  170. * Creates a base ACL for both normal users and admins to share
  171. * pACL -> Pointer to an ACL structure
  172. * errp -> Error structure to set any errors that occur
  173. * returns: 1 on success, 0 otherwise
  174. */
  175. static bool create_acl_base(PACL *pACL, Error **errp)
  176. {
  177. PSID adminGroupPSID = NULL;
  178. PSID systemPSID = NULL;
  179. const int aclSize = 2;
  180. EXPLICIT_ACCESS eAccess[2];
  181. /* Create an entry for the system user. */
  182. const char *systemSID = LOCAL_SYSTEM_SID;
  183. bool converted = ConvertStringSidToSid(systemSID, &systemPSID);
  184. if (!converted) {
  185. error_setg_win32(errp, GetLastError(), "failed to retrieve system SID");
  186. goto error;
  187. }
  188. /* set permissions for system user */
  189. eAccess[0].grfAccessPermissions = GENERIC_ALL;
  190. eAccess[0].grfAccessMode = SET_ACCESS;
  191. eAccess[0].grfInheritance = NO_INHERITANCE;
  192. eAccess[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
  193. eAccess[0].Trustee.TrusteeType = TRUSTEE_IS_USER;
  194. eAccess[0].Trustee.ptstrName = (LPTSTR)systemPSID;
  195. /* Create an entry for the admin user. */
  196. const char *adminSID = ADMIN_SID;
  197. converted = ConvertStringSidToSid(adminSID, &adminGroupPSID);
  198. if (!converted) {
  199. error_setg_win32(errp, GetLastError(), "failed to retrieve Admin SID");
  200. goto error;
  201. }
  202. /* Set permissions for admin group. */
  203. eAccess[1].grfAccessPermissions = GENERIC_ALL;
  204. eAccess[1].grfAccessMode = SET_ACCESS;
  205. eAccess[1].grfInheritance = NO_INHERITANCE;
  206. eAccess[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
  207. eAccess[1].Trustee.TrusteeType = TRUSTEE_IS_GROUP;
  208. eAccess[1].Trustee.ptstrName = (LPTSTR)adminGroupPSID;
  209. /* Put the entries in an ACL object. */
  210. PACL pNewACL = NULL;
  211. DWORD setResult;
  212. /*
  213. *If we are given a pointer that is already initialized, then we can merge
  214. *the existing entries instead of overwriting them.
  215. */
  216. if (*pACL) {
  217. setResult = SetEntriesInAcl(aclSize, eAccess, *pACL, &pNewACL);
  218. } else {
  219. setResult = SetEntriesInAcl(aclSize, eAccess, NULL, &pNewACL);
  220. }
  221. if (setResult != ERROR_SUCCESS) {
  222. error_setg_win32(errp, GetLastError(),
  223. "failed to set base ACL entries for system user and "
  224. "admin group %lu",
  225. setResult);
  226. goto error;
  227. }
  228. LocalFree(adminGroupPSID);
  229. LocalFree(systemPSID);
  230. /* Free any old memory since we are going to overwrite the users pointer. */
  231. LocalFree(*pACL);
  232. *pACL = pNewACL;
  233. return true;
  234. error:
  235. LocalFree(adminGroupPSID);
  236. LocalFree(systemPSID);
  237. return false;
  238. }
  239. /*
  240. * Sets the access control on the authorized_keys file and any ssh folders that
  241. * need to be created. For administrators the required permissions on the
  242. * file/folders are that only administrators and the LocalSystem account can
  243. * access the folders. For normal user accounts only the specified user,
  244. * LocalSystem and Administrators can have access to the key.
  245. *
  246. * parameters:
  247. * userInfo -> pointer to structure that contains information about the user
  248. * PACL -> pointer to an access control structure that will be set upon
  249. * successful completion of the function.
  250. * errp -> error structure that will be set upon error.
  251. * returns: 1 upon success 0 upon failure.
  252. */
  253. static bool create_acl(PWindowsUserInfo userInfo, PACL *pACL, Error **errp)
  254. {
  255. /*
  256. * Creates a base ACL that both admins and users will share
  257. * This adds the Administrators group and the SYSTEM group
  258. */
  259. if (!create_acl_base(pACL, errp)) {
  260. return false;
  261. }
  262. /*
  263. * If the user is not an admin give the user creating the key permission to
  264. * access the file.
  265. */
  266. if (!userInfo->isAdmin) {
  267. if (!create_acl_user(userInfo, pACL, errp)) {
  268. return false;
  269. }
  270. return true;
  271. }
  272. return true;
  273. }
  274. /*
  275. * Create the SSH directory for the user and d sets appropriate permissions.
  276. * In general the directory will be %PROGRAMDATA%/ssh if the user is an admin.
  277. * %USERPOFILE%/.ssh if not an admin
  278. *
  279. * parameters:
  280. * userInfo -> Contains information about the user
  281. * errp -> Structure that will contain errors if the function fails.
  282. * returns: zero upon failure, 1 upon success
  283. */
  284. static bool create_ssh_directory(WindowsUserInfo *userInfo, Error **errp)
  285. {
  286. PACL pNewACL = NULL;
  287. g_autofree PSECURITY_DESCRIPTOR pSD = NULL;
  288. /* Gets the appropriate ACL for the user */
  289. if (!create_acl(userInfo, &pNewACL, errp)) {
  290. goto error;
  291. }
  292. /* Allocate memory for a security descriptor */
  293. pSD = g_malloc(SECURITY_DESCRIPTOR_MIN_LENGTH);
  294. if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION)) {
  295. error_setg_win32(errp, GetLastError(),
  296. "Failed to initialize security descriptor");
  297. goto error;
  298. }
  299. /* Associate the security descriptor with the ACL permissions. */
  300. if (!SetSecurityDescriptorDacl(pSD, TRUE, pNewACL, FALSE)) {
  301. error_setg_win32(errp, GetLastError(),
  302. "Failed to set security descriptor ACL");
  303. goto error;
  304. }
  305. /* Set the security attributes on the folder */
  306. SECURITY_ATTRIBUTES sAttr;
  307. sAttr.bInheritHandle = FALSE;
  308. sAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
  309. sAttr.lpSecurityDescriptor = pSD;
  310. /* Create the directory with the created permissions */
  311. BOOL created = CreateDirectory(userInfo->sshDirectory, &sAttr);
  312. if (!created) {
  313. error_setg_win32(errp, GetLastError(), "failed to create directory %s",
  314. userInfo->sshDirectory);
  315. goto error;
  316. }
  317. /* Free memory */
  318. LocalFree(pNewACL);
  319. return true;
  320. error:
  321. LocalFree(pNewACL);
  322. return false;
  323. }
  324. /*
  325. * Sets permissions on the authorized_key_file that is created.
  326. *
  327. * parameters: userInfo -> Information about the user
  328. * errp -> error structure that will contain errors upon failure
  329. * returns: 1 upon success, zero upon failure.
  330. */
  331. static bool set_file_permissions(PWindowsUserInfo userInfo, Error **errp)
  332. {
  333. PACL pACL = NULL;
  334. PSID userPSID = NULL;
  335. /* Creates the access control structure */
  336. if (!create_acl(userInfo, &pACL, errp)) {
  337. goto error;
  338. }
  339. /* Get the PSID structure for the user based off the string SID. */
  340. bool converted = ConvertStringSidToSid(userInfo->SSID, &userPSID);
  341. if (!converted) {
  342. error_setg_win32(errp, GetLastError(), "failed to retrieve user %s SID",
  343. userInfo->username);
  344. goto error;
  345. }
  346. /* Prevents permissions from being inherited and use the DACL provided. */
  347. const SE_OBJECT_TYPE securityBitFlags =
  348. DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION;
  349. /* Set the ACL on the file. */
  350. if (SetNamedSecurityInfo(userInfo->authorizedKeyFile, SE_FILE_OBJECT,
  351. securityBitFlags, userPSID, NULL, pACL,
  352. NULL) != ERROR_SUCCESS) {
  353. error_setg_win32(errp, GetLastError(),
  354. "failed to set file security for file %s",
  355. userInfo->authorizedKeyFile);
  356. goto error;
  357. }
  358. LocalFree(pACL);
  359. LocalFree(userPSID);
  360. return true;
  361. error:
  362. LocalFree(pACL);
  363. LocalFree(userPSID);
  364. return false;
  365. }
  366. /*
  367. * Writes the specified keys to the authenticated keys file.
  368. * parameters:
  369. * userInfo: Information about the user we are writing the authkeys file to.
  370. * authkeys: Array of keys to write to disk
  371. * errp: Error structure that will contain any errors if they occur.
  372. * returns: 1 if successful, 0 otherwise.
  373. */
  374. static bool write_authkeys(WindowsUserInfo *userInfo, GStrv authkeys,
  375. Error **errp)
  376. {
  377. g_autofree char *contents = NULL;
  378. g_autoptr(GError) err = NULL;
  379. contents = g_strjoinv("\n", authkeys);
  380. if (!g_file_set_contents(userInfo->authorizedKeyFile, contents, -1, &err)) {
  381. error_setg(errp, "failed to write to '%s': %s",
  382. userInfo->authorizedKeyFile, err->message);
  383. return false;
  384. }
  385. if (!set_file_permissions(userInfo, errp)) {
  386. return false;
  387. }
  388. return true;
  389. }
  390. /*
  391. * Retrieves information about a Windows user by their username
  392. *
  393. * parameters:
  394. * userInfo -> Double pointer to a WindowsUserInfo structure. Upon success, it
  395. * will be allocated with information about the user and need to be freed.
  396. * username -> Name of the user to lookup.
  397. * errp -> Contains any errors that occur.
  398. * returns: 1 upon success, 0 upon failure.
  399. */
  400. static bool get_user_info(PWindowsUserInfo *userInfo, const char *username,
  401. Error **errp)
  402. {
  403. DWORD infoLevel = 4;
  404. LPUSER_INFO_4 uBuf = NULL;
  405. g_autofree wchar_t *wideUserName = NULL;
  406. g_autoptr(GError) gerr = NULL;
  407. PSID psid = NULL;
  408. /*
  409. * Converts a string to a Windows wide string since the GetNetUserInfo
  410. * function requires it.
  411. */
  412. wideUserName = g_utf8_to_utf16(username, -1, NULL, NULL, &gerr);
  413. if (!wideUserName) {
  414. goto error;
  415. }
  416. /* allocate data */
  417. PWindowsUserInfo uData = g_new0(WindowsUserInfo, 1);
  418. /* Set pointer so it can be cleaned up by the callee, even upon error. */
  419. *userInfo = uData;
  420. /* Find the information */
  421. NET_API_STATUS result =
  422. NetUserGetInfo(NULL, wideUserName, infoLevel, (LPBYTE *)&uBuf);
  423. if (result != NERR_Success) {
  424. /* Give a friendlier error message if the user was not found. */
  425. if (result == NERR_UserNotFound) {
  426. error_setg(errp, "User %s was not found", username);
  427. goto error;
  428. }
  429. error_setg(errp,
  430. "Received unexpected error when asking for user info: Error "
  431. "Code %lu",
  432. result);
  433. goto error;
  434. }
  435. /* Get information from the buffer returned by NetUserGetInfo. */
  436. uData->username = g_strdup(username);
  437. uData->isAdmin = uBuf->usri4_priv == USER_PRIV_ADMIN;
  438. psid = uBuf->usri4_user_sid;
  439. char *sidStr = NULL;
  440. /*
  441. * We store the string representation of the SID not SID structure in
  442. * memory. Callees wanting to use the SID structure should call
  443. * ConvertStringSidToSID.
  444. */
  445. if (!ConvertSidToStringSid(psid, &sidStr)) {
  446. error_setg_win32(errp, GetLastError(),
  447. "failed to get SID string for user %s", username);
  448. goto error;
  449. }
  450. /* Store the SSID */
  451. uData->SSID = sidStr;
  452. /* Get the SSH folder for the user. */
  453. char *sshFolder = get_ssh_folder(username, uData->isAdmin, errp);
  454. if (sshFolder == NULL) {
  455. goto error;
  456. }
  457. /* Get the authorized key file path */
  458. const char *authorizedKeyFile =
  459. uData->isAdmin ? AUTHORIZED_KEY_FILE_ADMIN : AUTHORIZED_KEY_FILE;
  460. char *authorizedKeyPath =
  461. g_build_filename(sshFolder, authorizedKeyFile, NULL);
  462. uData->sshDirectory = sshFolder;
  463. uData->authorizedKeyFile = authorizedKeyPath;
  464. /* Free */
  465. NetApiBufferFree(uBuf);
  466. return true;
  467. error:
  468. if (uBuf) {
  469. NetApiBufferFree(uBuf);
  470. }
  471. return false;
  472. }
  473. /*
  474. * Gets the list of authorized keys for a user.
  475. *
  476. * parameters:
  477. * username -> Username to retrieve the keys for.
  478. * errp -> Error structure that will display any errors through QMP.
  479. * returns: List of keys associated with the user.
  480. */
  481. GuestAuthorizedKeys *qmp_guest_ssh_get_authorized_keys(const char *username,
  482. Error **errp)
  483. {
  484. GuestAuthorizedKeys *keys = NULL;
  485. g_auto(GStrv) authKeys = NULL;
  486. g_autoptr(GuestAuthorizedKeys) ret = NULL;
  487. g_auto(PWindowsUserInfo) userInfo = NULL;
  488. /* Gets user information */
  489. if (!get_user_info(&userInfo, username, errp)) {
  490. return NULL;
  491. }
  492. /* Reads authkeys for the user */
  493. authKeys = read_authkeys(userInfo->authorizedKeyFile, errp);
  494. if (authKeys == NULL) {
  495. return NULL;
  496. }
  497. /* Set the GuestAuthorizedKey struct with keys from the file */
  498. ret = g_new0(GuestAuthorizedKeys, 1);
  499. for (int i = 0; authKeys[i] != NULL; i++) {
  500. g_strstrip(authKeys[i]);
  501. if (!authKeys[i][0] || authKeys[i][0] == '#') {
  502. continue;
  503. }
  504. QAPI_LIST_PREPEND(ret->keys, g_strdup(authKeys[i]));
  505. }
  506. /*
  507. * Steal the pointer because it is up for the callee to deallocate the
  508. * memory.
  509. */
  510. keys = g_steal_pointer(&ret);
  511. return keys;
  512. }
  513. /*
  514. * Adds an ssh key for a user.
  515. *
  516. * parameters:
  517. * username -> User to add the SSH key to
  518. * strList -> Array of keys to add to the list
  519. * has_reset -> Whether the keys have been reset
  520. * reset -> Boolean to reset the keys (If this is set the existing list will be
  521. * cleared) and the other key reset. errp -> Pointer to an error structure that
  522. * will get returned over QMP if anything goes wrong.
  523. */
  524. void qmp_guest_ssh_add_authorized_keys(const char *username, strList *keys,
  525. bool has_reset, bool reset, Error **errp)
  526. {
  527. g_auto(PWindowsUserInfo) userInfo = NULL;
  528. g_auto(GStrv) authkeys = NULL;
  529. strList *k;
  530. size_t nkeys, nauthkeys;
  531. /* Make sure the keys given are valid */
  532. if (!check_openssh_pub_keys(keys, &nkeys, errp)) {
  533. return;
  534. }
  535. /* Gets user information */
  536. if (!get_user_info(&userInfo, username, errp)) {
  537. return;
  538. }
  539. /* Determine whether we should reset the keys */
  540. reset = has_reset && reset;
  541. if (!reset) {
  542. /* Read existing keys into memory */
  543. authkeys = read_authkeys(userInfo->authorizedKeyFile, NULL);
  544. }
  545. /* Check that the SSH key directory exists for the user. */
  546. if (!g_file_test(userInfo->sshDirectory, G_FILE_TEST_IS_DIR)) {
  547. BOOL success = create_ssh_directory(userInfo, errp);
  548. if (!success) {
  549. return;
  550. }
  551. }
  552. /* Reallocates the buffer to fit the new keys. */
  553. nauthkeys = authkeys ? g_strv_length(authkeys) : 0;
  554. authkeys = g_realloc_n(authkeys, nauthkeys + nkeys + 1, sizeof(char *));
  555. /* zero out the memory for the reallocated buffer */
  556. memset(authkeys + nauthkeys, 0, (nkeys + 1) * sizeof(char *));
  557. /* Adds the keys */
  558. for (k = keys; k != NULL; k = k->next) {
  559. /* Check that the key doesn't already exist */
  560. if (g_strv_contains((const gchar *const *)authkeys, k->value)) {
  561. continue;
  562. }
  563. authkeys[nauthkeys++] = g_strdup(k->value);
  564. }
  565. /* Write the authkeys to the file. */
  566. write_authkeys(userInfo, authkeys, errp);
  567. }
  568. /*
  569. * Removes an SSH key for a user
  570. *
  571. * parameters:
  572. * username -> Username to remove the key from
  573. * strList -> List of strings to remove
  574. * errp -> Contains any errors that occur.
  575. */
  576. void qmp_guest_ssh_remove_authorized_keys(const char *username, strList *keys,
  577. Error **errp)
  578. {
  579. g_auto(PWindowsUserInfo) userInfo = NULL;
  580. g_autofree struct passwd *p = NULL;
  581. g_autofree GStrv new_keys = NULL; /* do not own the strings */
  582. g_auto(GStrv) authkeys = NULL;
  583. GStrv a;
  584. size_t nkeys = 0;
  585. /* Validates the keys passed in by the user */
  586. if (!check_openssh_pub_keys(keys, NULL, errp)) {
  587. return;
  588. }
  589. /* Gets user information */
  590. if (!get_user_info(&userInfo, username, errp)) {
  591. return;
  592. }
  593. /* Reads the authkeys for the user */
  594. authkeys = read_authkeys(userInfo->authorizedKeyFile, errp);
  595. if (authkeys == NULL) {
  596. return;
  597. }
  598. /* Create a new buffer to hold the keys */
  599. new_keys = g_new0(char *, g_strv_length(authkeys) + 1);
  600. for (a = authkeys; *a != NULL; a++) {
  601. strList *k;
  602. /* Filters out keys that are equal to ones the user specified. */
  603. for (k = keys; k != NULL; k = k->next) {
  604. if (g_str_equal(k->value, *a)) {
  605. break;
  606. }
  607. }
  608. if (k != NULL) {
  609. continue;
  610. }
  611. new_keys[nkeys++] = *a;
  612. }
  613. /* Write the new authkeys to the file. */
  614. write_authkeys(userInfo, new_keys, errp);
  615. }