All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 0/2] Implement SSH commands in QEMU GA for Windows
@ 2024-03-22 17:46 aidan_leuck
  2024-03-22 17:46 ` [PATCH v3 1/2] Refactor common functions between POSIX and Windows implementation aidan_leuck
  2024-03-22 17:46 ` [PATCH v3 2/2] Implement SSH commands in QEMU GA for Windows aidan_leuck
  0 siblings, 2 replies; 9+ messages in thread
From: aidan_leuck @ 2024-03-22 17:46 UTC (permalink / raw)
  To: qemu-devel; +Cc: kkostiuk, berrange, Aidan Leuck

From: Aidan Leuck <aidan_leuck@selinc.com>

This patch aims to implement guest-ssh-add-authorized-keys, guest-ssh-remove-authorized-keys, and guest-ssh-get-authorized-keys
for Windows. This PR is based on Microsoft's OpenSSH implementation https://github.com/PowerShell/Win32-OpenSSH. The guest agents 
will support Kubevirt and allow guest agent propagation to be used to dynamically inject SSH keys. 
https://kubevirt.io/user-guide/virtual_machines/accessing_virtual_machines/#dynamic-ssh-public-key-injection-via-qemu-guest-agent

Changes since v2
* Set indent to 4 spaces
* Moved all comments to C style comments
* Fixed a segfault bug in get_user_info function related to non zeroed memory when a user did not exist.
* Used g_new0 instead of g_malloc where applicable
* Modified newlines in qapi-schema.json
* Added newlines at the end of all files
* GError functions now use g_autoptr instead of being freed manually.
* Refactored get_ssh_folder to remove goto error statement
* Fixed uninitialized variable pgDataW
* Modified patch order so that the generalization patch is the first patch
* Removed unnecssary ZeroMemory calls

Changes since v1
* Fixed styling errors
* Moved from wcstombs to g_utf functions
* Removed unnecessary if checks on calls to free
* Fixed copyright headers
* Refactored create_acl functions into base function, admin function and user function
* Removed unused user count function
* Split up refactor of existing code into a separate patch

Aidan Leuck (2):
  Refactor common functions between POSIX and Windows implementation
  Implement SSH commands in QEMU GA for Windows

 qga/commands-posix-ssh.c   |  47 +--
 qga/commands-ssh-core.c    |  57 +++
 qga/commands-ssh-core.h    |   8 +
 qga/commands-windows-ssh.c | 791 +++++++++++++++++++++++++++++++++++++
 qga/commands-windows-ssh.h |  26 ++
 qga/meson.build            |   6 +-
 qga/qapi-schema.json       |  17 +-
 7 files changed, 895 insertions(+), 57 deletions(-)
 create mode 100644 qga/commands-ssh-core.c
 create mode 100644 qga/commands-ssh-core.h
 create mode 100644 qga/commands-windows-ssh.c
 create mode 100644 qga/commands-windows-ssh.h

-- 
2.44.0



^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH v3 1/2] Refactor common functions between POSIX and Windows implementation
  2024-03-22 17:46 [PATCH v3 0/2] Implement SSH commands in QEMU GA for Windows aidan_leuck
@ 2024-03-22 17:46 ` aidan_leuck
  2024-03-25 17:47   ` Philippe Mathieu-Daudé
  2024-03-22 17:46 ` [PATCH v3 2/2] Implement SSH commands in QEMU GA for Windows aidan_leuck
  1 sibling, 1 reply; 9+ messages in thread
From: aidan_leuck @ 2024-03-22 17:46 UTC (permalink / raw)
  To: qemu-devel; +Cc: kkostiuk, berrange, Aidan Leuck

From: Aidan Leuck <aidan_leuck@selinc.com>

Signed-off-by: Aidan Leuck <aidan_leuck@selinc.com>
---
 qga/commands-posix-ssh.c | 47 +--------------------------------
 qga/commands-ssh-core.c  | 57 ++++++++++++++++++++++++++++++++++++++++
 qga/commands-ssh-core.h  |  8 ++++++
 qga/meson.build          |  1 +
 4 files changed, 67 insertions(+), 46 deletions(-)
 create mode 100644 qga/commands-ssh-core.c
 create mode 100644 qga/commands-ssh-core.h

diff --git a/qga/commands-posix-ssh.c b/qga/commands-posix-ssh.c
index 236f80de44..9a71b109f9 100644
--- a/qga/commands-posix-ssh.c
+++ b/qga/commands-posix-ssh.c
@@ -9,6 +9,7 @@
 #include <locale.h>
 #include <pwd.h>
 
+#include "commands-ssh-core.h"
 #include "qapi/error.h"
 #include "qga-qapi-commands.h"
 
@@ -80,37 +81,6 @@ mkdir_for_user(const char *path, const struct passwd *p,
     return true;
 }
 
-static bool
-check_openssh_pub_key(const char *key, Error **errp)
-{
-    /* simple sanity-check, we may want more? */
-    if (!key || key[0] == '#' || strchr(key, '\n')) {
-        error_setg(errp, "invalid OpenSSH public key: '%s'", key);
-        return false;
-    }
-
-    return true;
-}
-
-static bool
-check_openssh_pub_keys(strList *keys, size_t *nkeys, Error **errp)
-{
-    size_t n = 0;
-    strList *k;
-
-    for (k = keys; k != NULL; k = k->next) {
-        if (!check_openssh_pub_key(k->value, errp)) {
-            return false;
-        }
-        n++;
-    }
-
-    if (nkeys) {
-        *nkeys = n;
-    }
-    return true;
-}
-
 static bool
 write_authkeys(const char *path, const GStrv keys,
                const struct passwd *p, Error **errp)
@@ -139,21 +109,6 @@ write_authkeys(const char *path, const GStrv keys,
     return true;
 }
 
-static GStrv
-read_authkeys(const char *path, Error **errp)
-{
-    g_autoptr(GError) err = NULL;
-    g_autofree char *contents = NULL;
-
-    if (!g_file_get_contents(path, &contents, NULL, &err)) {
-        error_setg(errp, "failed to read '%s': %s", path, err->message);
-        return NULL;
-    }
-
-    return g_strsplit(contents, "\n", -1);
-
-}
-
 void
 qmp_guest_ssh_add_authorized_keys(const char *username, strList *keys,
                                   bool has_reset, bool reset,
diff --git a/qga/commands-ssh-core.c b/qga/commands-ssh-core.c
new file mode 100644
index 0000000000..f165c4a337
--- /dev/null
+++ b/qga/commands-ssh-core.c
@@ -0,0 +1,57 @@
+/*
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include <qga-qapi-types.h>
+#include <stdbool.h>
+#include "qapi/error.h"
+#include "commands-ssh-core.h"
+
+GStrv read_authkeys(const char *path, Error **errp)
+{
+    g_autoptr(GError) err = NULL;
+    g_autofree char *contents = NULL;
+
+    if (!g_file_get_contents(path, &contents, NULL, &err))
+    {
+        error_setg(errp, "failed to read '%s': %s", path, err->message);
+        return NULL;
+    }
+
+    return g_strsplit(contents, "\n", -1);
+}
+
+bool check_openssh_pub_keys(strList *keys, size_t *nkeys, Error **errp)
+{
+    size_t n = 0;
+    strList *k;
+
+    for (k = keys; k != NULL; k = k->next)
+    {
+        if (!check_openssh_pub_key(k->value, errp))
+        {
+            return false;
+        }
+        n++;
+    }
+
+    if (nkeys)
+    {
+        *nkeys = n;
+    }
+    return true;
+}
+
+bool check_openssh_pub_key(const char *key, Error **errp)
+{
+    /* simple sanity-check, we may want more? */
+    if (!key || key[0] == '#' || strchr(key, '\n'))
+    {
+        error_setg(errp, "invalid OpenSSH public key: '%s'", key);
+        return false;
+    }
+
+    return true;
+}
diff --git a/qga/commands-ssh-core.h b/qga/commands-ssh-core.h
new file mode 100644
index 0000000000..ef9f600d4d
--- /dev/null
+++ b/qga/commands-ssh-core.h
@@ -0,0 +1,8 @@
+/*
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+GStrv read_authkeys(const char *path, Error **errp);
+bool check_openssh_pub_keys(strList *keys, size_t *nkeys, Error **errp);
+bool check_openssh_pub_key(const char *key, Error **errp);
diff --git a/qga/meson.build b/qga/meson.build
index 1c3d2a3d1b..d32b401507 100644
--- a/qga/meson.build
+++ b/qga/meson.build
@@ -66,6 +66,7 @@ qga_ss.add(files(
   'guest-agent-command-state.c',
   'main.c',
   'cutils.c',
+  'commands-ssh-core.c'
 ))
 if host_os == 'windows'
   qga_ss.add(files(
-- 
2.44.0



^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v3 2/2] Implement SSH commands in QEMU GA for Windows
  2024-03-22 17:46 [PATCH v3 0/2] Implement SSH commands in QEMU GA for Windows aidan_leuck
  2024-03-22 17:46 ` [PATCH v3 1/2] Refactor common functions between POSIX and Windows implementation aidan_leuck
@ 2024-03-22 17:46 ` aidan_leuck
  2024-03-25 17:50   ` Philippe Mathieu-Daudé
  1 sibling, 1 reply; 9+ messages in thread
From: aidan_leuck @ 2024-03-22 17:46 UTC (permalink / raw)
  To: qemu-devel; +Cc: kkostiuk, berrange, Aidan Leuck

From: Aidan Leuck <aidan_leuck@selinc.com>

Signed-off-by: Aidan Leuck <aidan_leuck@selinc.com>
---
 qga/commands-windows-ssh.c | 791 +++++++++++++++++++++++++++++++++++++
 qga/commands-windows-ssh.h |  26 ++
 qga/meson.build            |   5 +-
 qga/qapi-schema.json       |  17 +-
 4 files changed, 828 insertions(+), 11 deletions(-)
 create mode 100644 qga/commands-windows-ssh.c
 create mode 100644 qga/commands-windows-ssh.h

diff --git a/qga/commands-windows-ssh.c b/qga/commands-windows-ssh.c
new file mode 100644
index 0000000000..da402e320c
--- /dev/null
+++ b/qga/commands-windows-ssh.c
@@ -0,0 +1,791 @@
+/*
+ * QEMU Guest Agent win32-specific command implementations for SSH keys.
+ * The implementation is opinionated and expects the SSH implementation to
+ * be OpenSSH.
+ *
+ * Copyright Schweitzer Engineering Laboratories. 2024
+ *
+ * Authors:
+ *  Aidan Leuck <aidan_leuck@selinc.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include <aclapi.h>
+#include <qga-qapi-types.h>
+
+#include "commands-ssh-core.h"
+#include "commands-windows-ssh.h"
+#include "guest-agent-core.h"
+#include "limits.h"
+#include "lmaccess.h"
+#include "lmapibuf.h"
+#include "lmerr.h"
+#include "qapi/error.h"
+
+#include "qga-qapi-commands.h"
+#include "sddl.h"
+#include "shlobj.h"
+#include "userenv.h"
+
+#define AUTHORIZED_KEY_FILE "authorized_keys"
+#define AUTHORIZED_KEY_FILE_ADMIN "administrators_authorized_keys"
+#define LOCAL_SYSTEM_SID "S-1-5-18"
+#define ADMIN_SID "S-1-5-32-544"
+#define WORLD_SID "S-1-1-0"
+
+/*
+ * Frees userInfo structure. This implements the g_auto cleanup
+ * for the structure.
+ */
+void free_userInfo(PWindowsUserInfo info)
+{
+    g_free(info->sshDirectory);
+    g_free(info->authorizedKeyFile);
+    LocalFree(info->SSID);
+    g_free(info->username);
+    g_free(info);
+}
+
+/*
+ * Gets the admin SSH folder for OpenSSH. OpenSSH does not store
+ * the authorized_key file in the users home directory for security reasons and
+ * instead stores it at %PROGRAMDATA%/ssh. This function returns the path to
+ * that directory on the users machine
+ *
+ * parameters:
+ * errp -> error structure to set when an error occurs
+ * returns: The path to the ssh folder in %PROGRAMDATA% or NULL if an error
+ * occurred.
+ */
+static char *get_admin_ssh_folder(Error **errp)
+{
+    /* Allocate memory for the program data path */
+    g_autofree char *programDataPath = NULL;
+    char *authkeys_path = NULL;
+    PWSTR pgDataW = NULL;
+    g_autoptr(GError) gerr = NULL;
+
+    /* Get the KnownFolderPath on the machine. */
+    HRESULT folderResult =
+        SHGetKnownFolderPath(&FOLDERID_ProgramData, 0, NULL, &pgDataW);
+    if (folderResult != S_OK) {
+        error_setg(errp, "Failed to retrieve ProgramData folder");
+        return NULL;
+    }
+
+    /* Convert from a wide string back to a standard character string. */
+    programDataPath = g_utf16_to_utf8(pgDataW, -1, NULL, NULL, &gerr);
+    CoTaskMemFree(pgDataW);
+    if (!programDataPath) {
+        error_setg(errp,
+                   "Failed converting ProgramData folder path to UTF-16 %s",
+                   gerr->message);
+        return NULL;
+    }
+
+    /* Build the path to the file. */
+    authkeys_path = g_build_filename(programDataPath, "ssh", NULL);
+    return authkeys_path;
+}
+
+/*
+ * Gets the path to the SSH folder for the specified user. If the user is an
+ * admin it returns the ssh folder located at %PROGRAMDATA%/ssh. If the user is
+ * not an admin it returns %USERPROFILE%/.ssh
+ *
+ * parameters:
+ * username -> Username to get the SSH folder for
+ * isAdmin -> Whether the user is an admin or not
+ * errp -> Error structure to set any errors that occur.
+ * returns: path to the ssh folder as a string.
+ */
+static char *get_ssh_folder(const char *username, const bool isAdmin,
+                            Error **errp)
+{
+    DWORD maxSize = MAX_PATH;
+    g_autofree char *profilesDir = g_new0(char, maxSize);
+
+    if (isAdmin) {
+        return get_admin_ssh_folder(errp);
+    }
+
+    /* If not an Admin the SSH key is in the user directory. */
+    /* Get the user profile directory on the machine. */
+    BOOL ret = GetProfilesDirectory(profilesDir, &maxSize);
+    if (!ret) {
+        error_setg_win32(errp, GetLastError(),
+                         "failed to retrieve profiles directory");
+        return NULL;
+    }
+
+    /* Builds the filename */
+    return g_build_filename(profilesDir, username, ".ssh", NULL);
+}
+
+/*
+ * Creates an entry for the everyone group. This is used when the user is an
+ * Administrator This is consistent with the folder permissions that OpenSSH
+ * creates when it is installed. Anyone can read the file, but only
+ * Administrators and SYSTEM can modify the file.
+ *
+ * parameters:
+ * userInfo -> Information about the current user
+ * pACL -> Pointer to an ACL structure
+ * errp -> Error structure to set any errors that occur
+ * returns: 1 on success, 0 otherwise
+ */
+static bool create_acl_admin(PWindowsUserInfo userInfo, PACL *pACL,
+                             Error **errp)
+{
+    PSID everyonePSID = NULL;
+
+    const int aclSize = 1;
+    EXPLICIT_ACCESS eAccess[1];
+
+    /*
+     * Create an entry for everyone (so they can at least read the folder).
+     * This is consistent with other folders located in %PROGRAMDATA%
+     */
+    bool converted = ConvertStringSidToSid(WORLD_SID, &everyonePSID);
+    if (!converted) {
+        error_setg_win32(errp, GetLastError(), "failed to retrieve Admin SID");
+        goto error;
+    }
+
+    /* Set permissions for everyone group (they can only read the files) */
+    eAccess[0].grfAccessPermissions = GENERIC_READ;
+    eAccess[0].grfAccessMode = SET_ACCESS;
+    eAccess[0].grfInheritance = NO_INHERITANCE;
+    eAccess[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
+    eAccess[0].Trustee.TrusteeType = TRUSTEE_IS_GROUP;
+    eAccess[0].Trustee.ptstrName = (LPTSTR)everyonePSID;
+
+    /* Put the entries in an ACL object. */
+    PACL pNewACL = NULL;
+    DWORD setResult;
+
+    /*
+     * If we are given a pointer that is already initialized, then we can merge
+     * the existing entries instead of overwriting them.
+     */
+    if (*pACL) {
+        setResult = SetEntriesInAcl(aclSize, eAccess, *pACL, &pNewACL);
+    }
+    else {
+        setResult = SetEntriesInAcl(aclSize, eAccess, NULL, &pNewACL);
+    }
+
+    if (setResult != ERROR_SUCCESS) {
+        error_setg_win32(errp, GetLastError(),
+                         "failed to set ACL entries for admin user %s %lu",
+                         userInfo->username, setResult);
+        goto error;
+    }
+
+    LocalFree(everyonePSID);
+
+    /*
+     * Free the old memory since we are going to overwrite the users
+     * pointer
+     */
+    LocalFree(*pACL);
+    *pACL = pNewACL;
+
+    return true;
+
+error:
+    LocalFree(everyonePSID);
+    return false;
+}
+
+/*
+ * Creates an entry for the user so they can access the ssh folder in their
+ * userprofile.
+ *
+ * parameters:
+ * userInfo -> Information about the current user
+ * pACL -> Pointer to an ACL structure
+ * errp -> Error structure to set any errors that occur
+ * returns -> 1 on success, 0 otherwise
+ */
+static bool create_acl_user(PWindowsUserInfo userInfo, PACL *pACL, Error **errp)
+{
+    const int aclSize = 1;
+    PACL newACL = NULL;
+    EXPLICIT_ACCESS eAccess[1];
+    PSID userPSID = NULL;
+
+    /* Get a pointer to the internal SID object in Windows */
+    bool converted = ConvertStringSidToSid(userInfo->SSID, &userPSID);
+    if (!converted) {
+        error_setg_win32(errp, GetLastError(), "failed to retrieve user %s SID",
+                         userInfo->username);
+        goto error;
+    }
+
+    /* Set the permissions for the user. */
+    eAccess[0].grfAccessPermissions = GENERIC_ALL;
+    eAccess[0].grfAccessMode = SET_ACCESS;
+    eAccess[0].grfInheritance = NO_INHERITANCE;
+    eAccess[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
+    eAccess[0].Trustee.TrusteeType = TRUSTEE_IS_USER;
+    eAccess[0].Trustee.ptstrName = (LPTSTR)userPSID;
+
+    /* Set the ACL entries */
+    DWORD setResult;
+
+    /*
+     * If we are given a pointer that is already initialized, then we can merge
+     * the existing entries instead of overwriting them.
+     */
+    if (*pACL) {
+        setResult = SetEntriesInAcl(aclSize, eAccess, *pACL, &newACL);
+    }
+    else {
+        setResult = SetEntriesInAcl(aclSize, eAccess, NULL, &newACL);
+    }
+
+    if (setResult != ERROR_SUCCESS) {
+        error_setg_win32(errp, GetLastError(),
+                         "failed to set ACL entries for user %s %lu",
+                         userInfo->username, setResult);
+        goto error;
+    }
+
+    /* Free any old memory since we are going to overwrite the users pointer. */
+    LocalFree(*pACL);
+    *pACL = newACL;
+
+    LocalFree(userPSID);
+    return true;
+error:
+    LocalFree(userPSID);
+    return false;
+}
+
+/*
+ * Creates a base ACL for both normal users and admins to share
+ * pACL -> Pointer to an ACL structure
+ * errp -> Error structure to set any errors that occur
+ * returns: 1 on success, 0 otherwise
+ */
+static bool create_acl_base(PACL *pACL, Error **errp)
+{
+    PSID adminGroupPSID = NULL;
+    PSID systemPSID = NULL;
+
+    const int aclSize = 2;
+    EXPLICIT_ACCESS eAccess[2];
+
+    /* Create an entry for the system user. */
+    const char *systemSID = LOCAL_SYSTEM_SID;
+    bool converted = ConvertStringSidToSid(systemSID, &systemPSID);
+    if (!converted) {
+        error_setg_win32(errp, GetLastError(), "failed to retrieve system SID");
+        goto error;
+    }
+
+    /* set permissions for system user */
+    eAccess[0].grfAccessPermissions = GENERIC_ALL;
+    eAccess[0].grfAccessMode = SET_ACCESS;
+    eAccess[0].grfInheritance = NO_INHERITANCE;
+    eAccess[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
+    eAccess[0].Trustee.TrusteeType = TRUSTEE_IS_USER;
+    eAccess[0].Trustee.ptstrName = (LPTSTR)systemPSID;
+
+    /* Create an entry for the admin user. */
+    const char *adminSID = ADMIN_SID;
+    converted = ConvertStringSidToSid(adminSID, &adminGroupPSID);
+    if (!converted) {
+        error_setg_win32(errp, GetLastError(), "failed to retrieve Admin SID");
+        goto error;
+    }
+
+    /* Set permissions for admin group. */
+    eAccess[1].grfAccessPermissions = GENERIC_ALL;
+    eAccess[1].grfAccessMode = SET_ACCESS;
+    eAccess[1].grfInheritance = NO_INHERITANCE;
+    eAccess[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
+    eAccess[1].Trustee.TrusteeType = TRUSTEE_IS_GROUP;
+    eAccess[1].Trustee.ptstrName = (LPTSTR)adminGroupPSID;
+
+    /* Put the entries in an ACL object. */
+    PACL pNewACL = NULL;
+    DWORD setResult;
+
+    /*
+     *If we are given a pointer that is already initialized, then we can merge
+     *the existing entries instead of overwriting them.
+     */
+    if (*pACL) {
+        setResult = SetEntriesInAcl(aclSize, eAccess, *pACL, &pNewACL);
+    }
+    else {
+        setResult = SetEntriesInAcl(aclSize, eAccess, NULL, &pNewACL);
+    }
+
+    if (setResult != ERROR_SUCCESS) {
+        error_setg_win32(errp, GetLastError(),
+                         "failed to set base ACL entries for system user and "
+                         "admin group %lu",
+                         setResult);
+        goto error;
+    }
+
+    LocalFree(adminGroupPSID);
+    LocalFree(systemPSID);
+
+    /* Free any old memory since we are going to overwrite the users pointer. */
+    LocalFree(*pACL);
+
+    *pACL = pNewACL;
+
+    return true;
+
+error:
+    LocalFree(adminGroupPSID);
+    LocalFree(systemPSID);
+    return false;
+}
+
+/*
+ * Sets the access control on the authorized_keys file and any ssh folders that
+ * need to be created. For administrators the required permissions on the
+ * file/folders are that only administrators and the LocalSystem account can
+ * access the folders. For normal user accounts only the specified user,
+ * LocalSystem and Administrators can have access to the key.
+ *
+ * parameters:
+ * userInfo -> pointer to structure that contains information about the user
+ * PACL -> pointer to an access control structure that will be set upon
+ * successful completion of the function.
+ * errp -> error structure that will be set upon error.
+ * returns: 1 upon success 0 upon failure.
+ */
+static bool create_acl(PWindowsUserInfo userInfo, PACL *pACL, Error **errp)
+{
+    /*
+     * Creates a base ACL that both admins and users will share
+     * This adds the Administrators group and the SYSTEM group
+     */
+    if (!create_acl_base(pACL, errp)) {
+        return false;
+    }
+
+    /*
+     * If the user is not an admin give the user creating the key permission to
+     * access the file.
+     */
+    if (!userInfo->isAdmin) {
+        if (!create_acl_user(userInfo, pACL, errp)) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /* If the user is an admin allow everyone to read the keys */
+    if (!create_acl_admin(userInfo, pACL, errp)) {
+        return false;
+    }
+
+    return true;
+}
+/*
+ * Create the SSH directory for the user and d sets appropriate permissions.
+ * In general the directory will be %PROGRAMDATA%/ssh if the user is an admin.
+ * %USERPOFILE%/.ssh if not an admin
+ *
+ * parameters:
+ * userInfo -> Contains information about the user
+ * errp -> Structure that will contain errors if the function fails.
+ * returns: zero upon failure, 1 upon success
+ */
+static bool create_ssh_directory(WindowsUserInfo *userInfo, Error **errp)
+{
+    PACL pNewACL = NULL;
+    g_autofree PSECURITY_DESCRIPTOR pSD = NULL;
+
+    /* Gets the appropriate ACL for the user */
+    if (!create_acl(userInfo, &pNewACL, errp)) {
+        goto error;
+    }
+
+    /* Allocate memory for a security descriptor */
+    pSD = g_malloc(SECURITY_DESCRIPTOR_MIN_LENGTH);
+    if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION)) {
+        error_setg_win32(errp, GetLastError(),
+                         "Failed to initialize security descriptor");
+        goto error;
+    }
+
+    /* Associate the security descriptor with the ACL permissions. */
+    if (!SetSecurityDescriptorDacl(pSD, TRUE, pNewACL, FALSE)) {
+        error_setg_win32(errp, GetLastError(),
+                         "Failed to set security descriptor ACL");
+        goto error;
+    }
+
+    /* Set the security attributes on the folder */
+    SECURITY_ATTRIBUTES sAttr;
+    sAttr.bInheritHandle = FALSE;
+    sAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
+    sAttr.lpSecurityDescriptor = pSD;
+
+    /* Create the directory with the created permissions */
+    BOOL created = CreateDirectory(userInfo->sshDirectory, &sAttr);
+    if (!created) {
+        error_setg_win32(errp, GetLastError(), "failed to create directory %s",
+                         userInfo->sshDirectory);
+        goto error;
+    }
+
+    /* Free memory */
+    LocalFree(pNewACL);
+    return true;
+error:
+    LocalFree(pNewACL);
+    return false;
+}
+
+/*
+ * Sets permissions on the authorized_key_file that is created.
+ *
+ * parameters: userInfo -> Information about the user
+ * errp -> error structure that will contain errors upon failure
+ * returns: 1 upon success, zero upon failure.
+ */
+static bool set_file_permissions(PWindowsUserInfo userInfo, Error **errp)
+{
+    PACL pACL = NULL;
+    PSID userPSID;
+
+    /* Creates the access control structure */
+    if (!create_acl(userInfo, &pACL, errp)) {
+        goto error;
+    }
+
+    /* Get the PSID structure for the user based off the string SID. */
+    bool converted = ConvertStringSidToSid(userInfo->SSID, &userPSID);
+    if (!converted) {
+        error_setg_win32(errp, GetLastError(), "failed to retrieve user %s SID",
+                         userInfo->username);
+        goto error;
+    }
+
+    /* Set the ACL on the file. */
+    if (SetNamedSecurityInfo(userInfo->authorizedKeyFile, SE_FILE_OBJECT,
+                             DACL_SECURITY_INFORMATION, userPSID, NULL, pACL,
+                             NULL) != ERROR_SUCCESS) {
+        error_setg_win32(errp, GetLastError(),
+                         "failed to set file security for file %s",
+                         userInfo->authorizedKeyFile);
+        goto error;
+    }
+
+    LocalFree(pACL);
+    LocalFree(userPSID);
+    return true;
+
+error:
+    LocalFree(pACL);
+    LocalFree(userPSID);
+
+    return false;
+}
+
+/*
+ * Writes the specified keys to the authenticated keys file.
+ * parameters:
+ * userInfo: Information about the user we are writing the authkeys file to.
+ * authkeys: Array of keys to write to disk
+ * errp: Error structure that will contain any errors if they occur.
+ * returns: 1 if successful, 0 otherwise.
+ */
+static bool write_authkeys(WindowsUserInfo *userInfo, GStrv authkeys,
+                           Error **errp)
+{
+    g_autofree char *contents = NULL;
+    g_autoptr(GError) err = NULL;
+
+    contents = g_strjoinv("\n", authkeys);
+
+    if (!g_file_set_contents(userInfo->authorizedKeyFile, contents, -1, &err)) {
+        error_setg(errp, "failed to write to '%s': %s",
+                   userInfo->authorizedKeyFile, err->message);
+        return false;
+    }
+
+    if (!set_file_permissions(userInfo, errp)) {
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Retrieves information about a Windows user by their username
+ *
+ * parameters:
+ * userInfo -> Double pointer to a WindowsUserInfo structure. Upon success, it
+ * will be allocated with information about the user and need to be freed.
+ * username -> Name of the user to lookup.
+ * errp -> Contains any errors that occur.
+ * returns: 1 upon success, 0 upon failure.
+ */
+static bool get_user_info(PWindowsUserInfo *userInfo, const char *username,
+                          Error **errp)
+{
+    DWORD infoLevel = 4;
+    LPUSER_INFO_4 uBuf = NULL;
+    g_autofree wchar_t *wideUserName = NULL;
+    g_autoptr(GError) gerr = NULL;
+    PSID psid = NULL;
+
+    /*
+     * Converts a string to a Windows wide string since the GetNetUserInfo
+     * function requires it.
+     */
+    wideUserName = g_utf8_to_utf16(username, -1, NULL, NULL, &gerr);
+    if (!wideUserName) {
+        goto error;
+    }
+
+    /* allocate data */
+    PWindowsUserInfo uData = g_new0(WindowsUserInfo, 1);
+
+    /* Set pointer so it can be cleaned up by the callee, even upon error. */
+    *userInfo = uData;
+
+    /* Find the information */
+    NET_API_STATUS result =
+        NetUserGetInfo(NULL, wideUserName, infoLevel, (LPBYTE *)&uBuf);
+    if (result != NERR_Success) {
+        /* Give a friendlier error message if the user was not found. */
+        if (result == NERR_UserNotFound) {
+            error_setg(errp, "User %s was not found", username);
+            goto error;
+        }
+
+        error_setg(errp,
+                   "Received unexpected error when asking for user info: Error "
+                   "Code %lu",
+                   result);
+        goto error;
+    }
+
+    /* Get information from the buffer returned by NetUserGetInfo. */
+    uData->username = g_strdup(username);
+    uData->isAdmin = uBuf->usri4_priv == USER_PRIV_ADMIN;
+    psid = uBuf->usri4_user_sid;
+
+    char *sidStr = NULL;
+
+    /*
+     * We store the string representation of the SID not SID structure in
+     * memory. Callees wanting to use the SID structure should call
+     * ConvertStringSidToSID.
+     */
+    if (!ConvertSidToStringSid(psid, &sidStr)) {
+        error_setg_win32(errp, GetLastError(),
+                         "failed to get SID string for user %s", username);
+        goto error;
+    }
+
+    /* Store the SSID */
+    uData->SSID = sidStr;
+
+    /* Get the SSH folder for the user. */
+    char *sshFolder = get_ssh_folder(username, uData->isAdmin, errp);
+    if (sshFolder == NULL) {
+        goto error;
+    }
+
+    /* Get the authorized key file path */
+    const char *authorizedKeyFile =
+        uData->isAdmin ? AUTHORIZED_KEY_FILE_ADMIN : AUTHORIZED_KEY_FILE;
+    char *authorizedKeyPath =
+        g_build_filename(sshFolder, authorizedKeyFile, NULL);
+    uData->sshDirectory = sshFolder;
+    uData->authorizedKeyFile = authorizedKeyPath;
+
+    /* Free */
+    NetApiBufferFree(uBuf);
+    return true;
+error:
+    if (uBuf) {
+        NetApiBufferFree(uBuf);
+    }
+
+    return false;
+}
+
+/*
+ * Gets the list of authorized keys for a user.
+ *
+ * parameters:
+ * username -> Username to retrieve the keys for.
+ * errp -> Error structure that will display any errors through QMP.
+ * returns: List of keys associated with the user.
+ */
+GuestAuthorizedKeys *qmp_guest_ssh_get_authorized_keys(const char *username,
+                                                       Error **errp)
+{
+    GuestAuthorizedKeys *keys = NULL;
+    g_auto(GStrv) authKeys = NULL;
+    g_autoptr(GuestAuthorizedKeys) ret = NULL;
+    g_auto(PWindowsUserInfo) userInfo = NULL;
+
+    /* Gets user information */
+    if (!get_user_info(&userInfo, username, errp)) {
+        return NULL;
+    }
+
+    /* Reads authkeys for the user */
+    authKeys = read_authkeys(userInfo->authorizedKeyFile, errp);
+    if (authKeys == NULL) {
+        return NULL;
+    }
+
+    /* Set the GuestAuthorizedKey struct with keys from the file */
+    ret = g_new0(GuestAuthorizedKeys, 1);
+    for (int i = 0; authKeys[i] != NULL; i++) {
+        g_strstrip(authKeys[i]);
+        if (!authKeys[i][0] || authKeys[i][0] == '#') {
+            continue;
+        }
+
+        QAPI_LIST_PREPEND(ret->keys, g_strdup(authKeys[i]));
+    }
+
+    /* Steal the pointer because it is up for the callee to deallocate the
+     * memory. */
+    keys = g_steal_pointer(&ret);
+    return keys;
+}
+
+/*
+ * Adds an ssh key for a user.
+ *
+ * parameters:
+ * username -> User to add the SSH key to
+ * strList -> Array of keys to add to the list
+ * has_reset -> Whether the keys have been reset
+ * reset -> Boolean to reset the keys (If this is set the existing list will be
+ * cleared) and the other key reset. errp -> Pointer to an error structure that
+ * will get returned over QMP if anything goes wrong.
+ */
+void qmp_guest_ssh_add_authorized_keys(const char *username, strList *keys,
+                                       bool has_reset, bool reset, Error **errp)
+{
+    g_auto(PWindowsUserInfo) userInfo = NULL;
+    g_auto(GStrv) authkeys = NULL;
+    strList *k;
+    size_t nkeys, nauthkeys;
+
+    /* Make sure the keys given are valid */
+    if (!check_openssh_pub_keys(keys, &nkeys, errp)) {
+        return;
+    }
+
+    /* Gets user information */
+    if (!get_user_info(&userInfo, username, errp)) {
+        return;
+    }
+
+    /* Determine whether we should reset the keys */
+    reset = has_reset && reset;
+    if (!reset) {
+        /* If we are not resetting the keys, read the existing keys into memory
+         */
+        authkeys = read_authkeys(userInfo->authorizedKeyFile, NULL);
+    }
+
+    /* Check that the SSH key directory exists for the user. */
+    if (!g_file_test(userInfo->sshDirectory, G_FILE_TEST_IS_DIR)) {
+        BOOL success = create_ssh_directory(userInfo, errp);
+        if (!success) {
+            return;
+        }
+    }
+
+    /* Reallocates the buffer to fit the new keys. */
+    nauthkeys = authkeys ? g_strv_length(authkeys) : 0;
+    authkeys = g_realloc_n(authkeys, nauthkeys + nkeys + 1, sizeof(char *));
+
+    /* zero out the memory for the reallocated buffer */
+    memset(authkeys + nauthkeys, 0, (nkeys + 1) * sizeof(char *));
+
+    /* Adds the keys */
+    for (k = keys; k != NULL; k = k->next) {
+        /* Check that the key doesn't already exist */
+        if (g_strv_contains((const gchar *const *)authkeys, k->value)) {
+            continue;
+        }
+
+        authkeys[nauthkeys++] = g_strdup(k->value);
+    }
+
+    /* Write the authkeys to the file. */
+    write_authkeys(userInfo, authkeys, errp);
+}
+
+/*
+ * Removes an SSH key for a user
+ *
+ * parameters:
+ * username -> Username to remove the key from
+ * strList -> List of strings to remove
+ * errp -> Contains any errors that occur.
+ */
+void qmp_guest_ssh_remove_authorized_keys(const char *username, strList *keys,
+                                          Error **errp)
+{
+    g_auto(PWindowsUserInfo) userInfo = NULL;
+    g_autofree struct passwd *p = NULL;
+    g_autofree GStrv new_keys = NULL; /* do not own the strings */
+    g_auto(GStrv) authkeys = NULL;
+    GStrv a;
+    size_t nkeys = 0;
+
+    /* Validates the keys passed in by the user */
+    if (!check_openssh_pub_keys(keys, NULL, errp)) {
+        return;
+    }
+
+    /* Gets user information */
+    if (!get_user_info(&userInfo, username, errp)) {
+        return;
+    }
+
+    /* Reads the authkeys for the user */
+    authkeys = read_authkeys(userInfo->authorizedKeyFile, errp);
+    if (authkeys == NULL) {
+        return;
+    }
+
+    /* Create a new buffer to hold the keys */
+    new_keys = g_new0(char *, g_strv_length(authkeys) + 1);
+    for (a = authkeys; *a != NULL; a++) {
+        strList *k;
+
+        /* Filters out keys that are equal to ones the user specified. */
+        for (k = keys; k != NULL; k = k->next) {
+            if (g_str_equal(k->value, *a)) {
+                break;
+            }
+        }
+
+        if (k != NULL) {
+            continue;
+        }
+
+        new_keys[nkeys++] = *a;
+    }
+
+    /* Write the new authkeys to the file. */
+    write_authkeys(userInfo, new_keys, errp);
+}
diff --git a/qga/commands-windows-ssh.h b/qga/commands-windows-ssh.h
new file mode 100644
index 0000000000..40ac67c4d9
--- /dev/null
+++ b/qga/commands-windows-ssh.h
@@ -0,0 +1,26 @@
+/*
+ * Header file for commands-windows-ssh.c
+ *
+ * Copyright Schweitzer Engineering Laboratories. 2024
+ *
+ * Authors:
+ *  Aidan Leuck <aidan_leuck@selinc.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include <glib/gstrfuncs.h>
+#include <stdbool.h>
+typedef struct WindowsUserInfo {
+    char *sshDirectory;
+    char *authorizedKeyFile;
+    char *username;
+    char *SSID;
+    bool isAdmin;
+} WindowsUserInfo;
+
+typedef WindowsUserInfo *PWindowsUserInfo;
+
+void free_userInfo(PWindowsUserInfo info);
+G_DEFINE_AUTO_CLEANUP_FREE_FUNC(PWindowsUserInfo, free_userInfo, NULL);
diff --git a/qga/meson.build b/qga/meson.build
index d32b401507..e365cd1ed7 100644
--- a/qga/meson.build
+++ b/qga/meson.build
@@ -73,7 +73,8 @@ if host_os == 'windows'
     'channel-win32.c',
     'commands-win32.c',
     'service-win32.c',
-    'vss-win32.c'
+    'vss-win32.c',
+    'commands-windows-ssh.c'
   ))
 else
   qga_ss.add(files(
@@ -94,7 +95,7 @@ gen_tlb = []
 qga_libs = []
 if host_os == 'windows'
   qga_libs += ['-lws2_32', '-lwinmm', '-lpowrprof', '-lwtsapi32', '-lwininet', '-liphlpapi', '-lnetapi32',
-               '-lsetupapi', '-lcfgmgr32']
+               '-lsetupapi', '-lcfgmgr32', '-luserenv']
   if have_qga_vss
     qga_libs += ['-lole32', '-loleaut32', '-lshlwapi', '-lstdc++', '-Wl,--enable-stdcall-fixup']
     subdir('vss-win32')
diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
index 9554b566a7..a64a6d91cf 100644
--- a/qga/qapi-schema.json
+++ b/qga/qapi-schema.json
@@ -1562,9 +1562,8 @@
 { 'struct': 'GuestAuthorizedKeys',
   'data': {
       'keys': ['str']
-  },
-  'if': 'CONFIG_POSIX' }
-
+  }
+}
 
 ##
 # @guest-ssh-get-authorized-keys:
@@ -1580,8 +1579,8 @@
 ##
 { 'command': 'guest-ssh-get-authorized-keys',
   'data': { 'username': 'str' },
-  'returns': 'GuestAuthorizedKeys',
-  'if': 'CONFIG_POSIX' }
+  'returns': 'GuestAuthorizedKeys'
+}
 
 ##
 # @guest-ssh-add-authorized-keys:
@@ -1599,8 +1598,8 @@
 # Since: 5.2
 ##
 { 'command': 'guest-ssh-add-authorized-keys',
-  'data': { 'username': 'str', 'keys': ['str'], '*reset': 'bool' },
-  'if': 'CONFIG_POSIX' }
+  'data': { 'username': 'str', 'keys': ['str'], '*reset': 'bool' }
+}
 
 ##
 # @guest-ssh-remove-authorized-keys:
@@ -1617,8 +1616,8 @@
 # Since: 5.2
 ##
 { 'command': 'guest-ssh-remove-authorized-keys',
-  'data': { 'username': 'str', 'keys': ['str'] },
-  'if': 'CONFIG_POSIX' }
+  'data': { 'username': 'str', 'keys': ['str'] }
+}
 
 ##
 # @GuestDiskStats:
-- 
2.44.0



^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH v3 1/2] Refactor common functions between POSIX and Windows implementation
  2024-03-22 17:46 ` [PATCH v3 1/2] Refactor common functions between POSIX and Windows implementation aidan_leuck
@ 2024-03-25 17:47   ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 9+ messages in thread
From: Philippe Mathieu-Daudé @ 2024-03-25 17:47 UTC (permalink / raw)
  To: aidan_leuck, qemu-devel; +Cc: kkostiuk, berrange

Hi Aidan,

On 22/3/24 18:46, aidan_leuck@selinc.com wrote:
> From: Aidan Leuck <aidan_leuck@selinc.com>
> 
> Signed-off-by: Aidan Leuck <aidan_leuck@selinc.com>
> ---
>   qga/commands-posix-ssh.c | 47 +--------------------------------
>   qga/commands-ssh-core.c  | 57 ++++++++++++++++++++++++++++++++++++++++
>   qga/commands-ssh-core.h  |  8 ++++++
>   qga/meson.build          |  1 +
>   4 files changed, 67 insertions(+), 46 deletions(-)
>   create mode 100644 qga/commands-ssh-core.c
>   create mode 100644 qga/commands-ssh-core.h

We already have:
- commands-common.h
- commands-posix-ssh.c

what about using the same pattern?
- commands-common-ssh.c

> diff --git a/qga/commands-posix-ssh.c b/qga/commands-posix-ssh.c
> index 236f80de44..9a71b109f9 100644
> --- a/qga/commands-posix-ssh.c
> +++ b/qga/commands-posix-ssh.c
> @@ -9,6 +9,7 @@
>   #include <locale.h>
>   #include <pwd.h>
>   
> +#include "commands-ssh-core.h"
>   #include "qapi/error.h"
>   #include "qga-qapi-commands.h"
>   
> @@ -80,37 +81,6 @@ mkdir_for_user(const char *path, const struct passwd *p,
>       return true;
>   }
>   
> -static bool
> -check_openssh_pub_key(const char *key, Error **errp)
> -{
> -    /* simple sanity-check, we may want more? */
> -    if (!key || key[0] == '#' || strchr(key, '\n')) {
> -        error_setg(errp, "invalid OpenSSH public key: '%s'", key);
> -        return false;
> -    }
> -
> -    return true;
> -}
> -
> -static bool
> -check_openssh_pub_keys(strList *keys, size_t *nkeys, Error **errp)
> -{
> -    size_t n = 0;
> -    strList *k;
> -
> -    for (k = keys; k != NULL; k = k->next) {
> -        if (!check_openssh_pub_key(k->value, errp)) {
> -            return false;
> -        }
> -        n++;
> -    }
> -
> -    if (nkeys) {
> -        *nkeys = n;
> -    }
> -    return true;
> -}
> -
>   static bool
>   write_authkeys(const char *path, const GStrv keys,
>                  const struct passwd *p, Error **errp)
> @@ -139,21 +109,6 @@ write_authkeys(const char *path, const GStrv keys,
>       return true;
>   }
>   
> -static GStrv
> -read_authkeys(const char *path, Error **errp)
> -{
> -    g_autoptr(GError) err = NULL;
> -    g_autofree char *contents = NULL;
> -
> -    if (!g_file_get_contents(path, &contents, NULL, &err)) {
> -        error_setg(errp, "failed to read '%s': %s", path, err->message);
> -        return NULL;
> -    }
> -
> -    return g_strsplit(contents, "\n", -1);
> -
> -}
> -
>   void
>   qmp_guest_ssh_add_authorized_keys(const char *username, strList *keys,
>                                     bool has_reset, bool reset,
> diff --git a/qga/commands-ssh-core.c b/qga/commands-ssh-core.c
> new file mode 100644
> index 0000000000..f165c4a337
> --- /dev/null
> +++ b/qga/commands-ssh-core.c
> @@ -0,0 +1,57 @@
> +/*
> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
> + * See the COPYING file in the top-level directory.
> + */
> +
> +#include "qemu/osdep.h"
> +#include <qga-qapi-types.h>
> +#include <stdbool.h>
> +#include "qapi/error.h"
> +#include "commands-ssh-core.h"
> +
> +GStrv read_authkeys(const char *path, Error **errp)
> +{
> +    g_autoptr(GError) err = NULL;
> +    g_autofree char *contents = NULL;
> +
> +    if (!g_file_get_contents(path, &contents, NULL, &err))
> +    {

Please keep QEMU style while moving the code, see
https://www.qemu.org/docs/master/devel/submitting-a-patch.html#use-the-qemu-coding-style 
which explain how to run scripts/checkpatch.pl
before posting your patches.

Otherwise the refactor LGTM.

Regards,

Phil.

> +        error_setg(errp, "failed to read '%s': %s", path, err->message);
> +        return NULL;
> +    }
> +
> +    return g_strsplit(contents, "\n", -1);
> +}



^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v3 2/2] Implement SSH commands in QEMU GA for Windows
  2024-03-22 17:46 ` [PATCH v3 2/2] Implement SSH commands in QEMU GA for Windows aidan_leuck
@ 2024-03-25 17:50   ` Philippe Mathieu-Daudé
  2024-03-27 14:38     ` Aidan Leuck
  0 siblings, 1 reply; 9+ messages in thread
From: Philippe Mathieu-Daudé @ 2024-03-25 17:50 UTC (permalink / raw)
  To: aidan_leuck, qemu-devel; +Cc: kkostiuk, berrange

On 22/3/24 18:46, aidan_leuck@selinc.com wrote:
> From: Aidan Leuck <aidan_leuck@selinc.com>
> 
> Signed-off-by: Aidan Leuck <aidan_leuck@selinc.com>
> ---
>   qga/commands-windows-ssh.c | 791 +++++++++++++++++++++++++++++++++++++

Huge file, I'm skipping it.

>   qga/commands-windows-ssh.h |  26 ++
>   qga/meson.build            |   5 +-
>   qga/qapi-schema.json       |  17 +-
>   4 files changed, 828 insertions(+), 11 deletions(-)
>   create mode 100644 qga/commands-windows-ssh.c
>   create mode 100644 qga/commands-windows-ssh.h


> diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
> index 9554b566a7..a64a6d91cf 100644
> --- a/qga/qapi-schema.json
> +++ b/qga/qapi-schema.json
> @@ -1562,9 +1562,8 @@
>   { 'struct': 'GuestAuthorizedKeys',
>     'data': {
>         'keys': ['str']
> -  },
> -  'if': 'CONFIG_POSIX' }
> -

For Windows you have to check the CONFIG_WIN32 definition,
so you want:

   'if': { 'any': [ 'CONFIG_POSIX',
                    'CONFIG_WIN32' ] },

> +  }
> +}
>   
>   ##
>   # @guest-ssh-get-authorized-keys:
> @@ -1580,8 +1579,8 @@
>   ##
>   { 'command': 'guest-ssh-get-authorized-keys',
>     'data': { 'username': 'str' },
> -  'returns': 'GuestAuthorizedKeys',
> -  'if': 'CONFIG_POSIX' }
> +  'returns': 'GuestAuthorizedKeys'
> +}
>   
>   ##
>   # @guest-ssh-add-authorized-keys:
> @@ -1599,8 +1598,8 @@
>   # Since: 5.2
>   ##
>   { 'command': 'guest-ssh-add-authorized-keys',
> -  'data': { 'username': 'str', 'keys': ['str'], '*reset': 'bool' },
> -  'if': 'CONFIG_POSIX' }
> +  'data': { 'username': 'str', 'keys': ['str'], '*reset': 'bool' }
> +}
>   
>   ##
>   # @guest-ssh-remove-authorized-keys:
> @@ -1617,8 +1616,8 @@
>   # Since: 5.2
>   ##
>   { 'command': 'guest-ssh-remove-authorized-keys',
> -  'data': { 'username': 'str', 'keys': ['str'] },
> -  'if': 'CONFIG_POSIX' }
> +  'data': { 'username': 'str', 'keys': ['str'] }
> +}
>   
>   ##
>   # @GuestDiskStats:



^ permalink raw reply	[flat|nested] 9+ messages in thread

* RE: [PATCH v3 2/2] Implement SSH commands in QEMU GA for Windows
  2024-03-25 17:50   ` Philippe Mathieu-Daudé
@ 2024-03-27 14:38     ` Aidan Leuck
  2024-03-27 15:38       ` Philippe Mathieu-Daudé
  0 siblings, 1 reply; 9+ messages in thread
From: Aidan Leuck @ 2024-03-27 14:38 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé, qemu-devel; +Cc: kkostiuk, berrange

Hi Philippe, 
Thank you for your feedback I will get these issues addressed. I left one small comment on the QAPI schema JSON.

Aidan Leuck

-----Original Message-----
From: Philippe Mathieu-Daudé <philmd@linaro.org> 
Sent: Monday, March 25, 2024 11:51 AM
To: Aidan Leuck <aidan_leuck@selinc.com>; qemu-devel@nongnu.org
Cc: kkostiuk@redhat.com; berrange@redhat.com
Subject: Re: [PATCH v3 2/2] Implement SSH commands in QEMU GA for Windows

[Caution - External]

On 22/3/24 18:46, aidan_leuck@selinc.com wrote:
> From: Aidan Leuck <aidan_leuck@selinc.com>
>
> Signed-off-by: Aidan Leuck <aidan_leuck@selinc.com>
> ---
>   qga/commands-windows-ssh.c | 791 
> +++++++++++++++++++++++++++++++++++++

Huge file, I'm skipping it.

>   qga/commands-windows-ssh.h |  26 ++
>   qga/meson.build            |   5 +-
>   qga/qapi-schema.json       |  17 +-
>   4 files changed, 828 insertions(+), 11 deletions(-)
>   create mode 100644 qga/commands-windows-ssh.c
>   create mode 100644 qga/commands-windows-ssh.h


> diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index 
> 9554b566a7..a64a6d91cf 100644
> --- a/qga/qapi-schema.json
> +++ b/qga/qapi-schema.json
> @@ -1562,9 +1562,8 @@
>   { 'struct': 'GuestAuthorizedKeys',
>     'data': {
>         'keys': ['str']
> -  },
> -  'if': 'CONFIG_POSIX' }
> -

For Windows you have to check the CONFIG_WIN32 definition, so you want:

I don't think this is necessary, the QEMU guest agent is compiled for only POSIX and Windows. I don't see this pattern being used elsewhere in the qapi schema file. I would be interested in what the maintainers think? 

   'if': { 'any': [ 'CONFIG_POSIX',
                    'CONFIG_WIN32' ] },

> +  }
> +}
>
>   ##
>   # @guest-ssh-get-authorized-keys:
> @@ -1580,8 +1579,8 @@
>   ##
>   { 'command': 'guest-ssh-get-authorized-keys',
>     'data': { 'username': 'str' },
> -  'returns': 'GuestAuthorizedKeys',
> -  'if': 'CONFIG_POSIX' }
> +  'returns': 'GuestAuthorizedKeys'
> +}
>
>   ##
>   # @guest-ssh-add-authorized-keys:
> @@ -1599,8 +1598,8 @@
>   # Since: 5.2
>   ##
>   { 'command': 'guest-ssh-add-authorized-keys',
> -  'data': { 'username': 'str', 'keys': ['str'], '*reset': 'bool' },
> -  'if': 'CONFIG_POSIX' }
> +  'data': { 'username': 'str', 'keys': ['str'], '*reset': 'bool' } }
>
>   ##
>   # @guest-ssh-remove-authorized-keys:
> @@ -1617,8 +1616,8 @@
>   # Since: 5.2
>   ##
>   { 'command': 'guest-ssh-remove-authorized-keys',
> -  'data': { 'username': 'str', 'keys': ['str'] },
> -  'if': 'CONFIG_POSIX' }
> +  'data': { 'username': 'str', 'keys': ['str'] } }
>
>   ##
>   # @GuestDiskStats:


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v3 2/2] Implement SSH commands in QEMU GA for Windows
  2024-03-27 14:38     ` Aidan Leuck
@ 2024-03-27 15:38       ` Philippe Mathieu-Daudé
  2024-03-27 15:54         ` Aidan Leuck
  0 siblings, 1 reply; 9+ messages in thread
From: Philippe Mathieu-Daudé @ 2024-03-27 15:38 UTC (permalink / raw)
  To: Aidan Leuck, qemu-devel, Markus Armbruster; +Cc: kkostiuk, berrange

On 27/3/24 15:38, Aidan Leuck wrote:
> Hi Philippe,
> Thank you for your feedback I will get these issues addressed. I left one small comment on the QAPI schema JSON.
> 
> Aidan Leuck
> 
> -----Original Message-----
> From: Philippe Mathieu-Daudé <philmd@linaro.org>
> Sent: Monday, March 25, 2024 11:51 AM
> To: Aidan Leuck <aidan_leuck@selinc.com>; qemu-devel@nongnu.org
> Cc: kkostiuk@redhat.com; berrange@redhat.com
> Subject: Re: [PATCH v3 2/2] Implement SSH commands in QEMU GA for Windows
> 
> [Caution - External]
> 
> On 22/3/24 18:46, aidan_leuck@selinc.com wrote:
>> From: Aidan Leuck <aidan_leuck@selinc.com>
>>
>> Signed-off-by: Aidan Leuck <aidan_leuck@selinc.com>
>> ---
>>    qga/commands-windows-ssh.c | 791
>> +++++++++++++++++++++++++++++++++++++
> 
> Huge file, I'm skipping it.
> 
>>    qga/commands-windows-ssh.h |  26 ++
>>    qga/meson.build            |   5 +-
>>    qga/qapi-schema.json       |  17 +-
>>    4 files changed, 828 insertions(+), 11 deletions(-)
>>    create mode 100644 qga/commands-windows-ssh.c
>>    create mode 100644 qga/commands-windows-ssh.h
> 
> 
>> diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index
>> 9554b566a7..a64a6d91cf 100644
>> --- a/qga/qapi-schema.json
>> +++ b/qga/qapi-schema.json
>> @@ -1562,9 +1562,8 @@
>>    { 'struct': 'GuestAuthorizedKeys',
>>      'data': {
>>          'keys': ['str']
>> -  },
>> -  'if': 'CONFIG_POSIX' }
>> -
> 
> For Windows you have to check the CONFIG_WIN32 definition, so you want:
> 
> I don't think this is necessary, the QEMU guest agent is compiled for only POSIX and Windows. I don't see this pattern being used elsewhere in the qapi schema file. I would be interested in what the maintainers think?

$ git grep -w CONFIG_WIN32 qapi/
qapi/char.json:490:            { 'name': 'console', 'if': 'CONFIG_WIN32' },
qapi/char.json:663:                         'if': 'CONFIG_WIN32' },
qapi/misc.json:293:{ 'command': 'get-win32-socket', 'data': {'info': 
'str', 'fdname': 'str'}, 'if': 'CONFIG_WIN32' }

> 
>     'if': { 'any': [ 'CONFIG_POSIX',
>                      'CONFIG_WIN32' ] },
> 
>> +  }
>> +}
>>
>>    ##
>>    # @guest-ssh-get-authorized-keys:
>> @@ -1580,8 +1579,8 @@
>>    ##
>>    { 'command': 'guest-ssh-get-authorized-keys',
>>      'data': { 'username': 'str' },
>> -  'returns': 'GuestAuthorizedKeys',
>> -  'if': 'CONFIG_POSIX' }
>> +  'returns': 'GuestAuthorizedKeys'
>> +}
>>
>>    ##
>>    # @guest-ssh-add-authorized-keys:
>> @@ -1599,8 +1598,8 @@
>>    # Since: 5.2
>>    ##
>>    { 'command': 'guest-ssh-add-authorized-keys',
>> -  'data': { 'username': 'str', 'keys': ['str'], '*reset': 'bool' },
>> -  'if': 'CONFIG_POSIX' }
>> +  'data': { 'username': 'str', 'keys': ['str'], '*reset': 'bool' } }
>>
>>    ##
>>    # @guest-ssh-remove-authorized-keys:
>> @@ -1617,8 +1616,8 @@
>>    # Since: 5.2
>>    ##
>>    { 'command': 'guest-ssh-remove-authorized-keys',
>> -  'data': { 'username': 'str', 'keys': ['str'] },
>> -  'if': 'CONFIG_POSIX' }
>> +  'data': { 'username': 'str', 'keys': ['str'] } }
>>
>>    ##
>>    # @GuestDiskStats:
> 



^ permalink raw reply	[flat|nested] 9+ messages in thread

* RE: [PATCH v3 2/2] Implement SSH commands in QEMU GA for Windows
  2024-03-27 15:38       ` Philippe Mathieu-Daudé
@ 2024-03-27 15:54         ` Aidan Leuck
  2024-03-27 16:21           ` Philippe Mathieu-Daudé
  0 siblings, 1 reply; 9+ messages in thread
From: Aidan Leuck @ 2024-03-27 15:54 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé, qemu-devel, Markus Armbruster
  Cc: kkostiuk, berrange



-----Original Message-----
From: Philippe Mathieu-Daudé <philmd@linaro.org> 
Sent: Wednesday, March 27, 2024 9:38 AM
To: Aidan Leuck <aidan_leuck@selinc.com>; qemu-devel@nongnu.org; Markus Armbruster <armbru@redhat.com>
Cc: kkostiuk@redhat.com; berrange@redhat.com
Subject: Re: [PATCH v3 2/2] Implement SSH commands in QEMU GA for Windows

[Caution - External]

On 27/3/24 15:38, Aidan Leuck wrote:
> Hi Philippe,
> Thank you for your feedback I will get these issues addressed. I left one small comment on the QAPI schema JSON.
>
> Aidan Leuck
>
> -----Original Message-----
> From: Philippe Mathieu-Daudé <philmd@linaro.org>
> Sent: Monday, March 25, 2024 11:51 AM
> To: Aidan Leuck <aidan_leuck@selinc.com>; qemu-devel@nongnu.org
> Cc: kkostiuk@redhat.com; berrange@redhat.com
> Subject: Re: [PATCH v3 2/2] Implement SSH commands in QEMU GA for 
> Windows
>
> [Caution - External]
>
> On 22/3/24 18:46, aidan_leuck@selinc.com wrote:
>> From: Aidan Leuck <aidan_leuck@selinc.com>
>>
>> Signed-off-by: Aidan Leuck <aidan_leuck@selinc.com>
>> ---
>>    qga/commands-windows-ssh.c | 791
>> +++++++++++++++++++++++++++++++++++++
>
> Huge file, I'm skipping it.
>
>>    qga/commands-windows-ssh.h |  26 ++
>>    qga/meson.build            |   5 +-
>>    qga/qapi-schema.json       |  17 +-
>>    4 files changed, 828 insertions(+), 11 deletions(-)
>>    create mode 100644 qga/commands-windows-ssh.c
>>    create mode 100644 qga/commands-windows-ssh.h
>
>
>> diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index 
>> 9554b566a7..a64a6d91cf 100644
>> --- a/qga/qapi-schema.json
>> +++ b/qga/qapi-schema.json
>> @@ -1562,9 +1562,8 @@
>>    { 'struct': 'GuestAuthorizedKeys',
>>      'data': {
>>          'keys': ['str']
>> -  },
>> -  'if': 'CONFIG_POSIX' }
>> -
>
> For Windows you have to check the CONFIG_WIN32 definition, so you want:
>
> I don't think this is necessary, the QEMU guest agent is compiled for only POSIX and Windows. I don't see this pattern being used elsewhere in the qapi schema file. I would be interested in what the maintainers think?

$ git grep -w CONFIG_WIN32 qapi/
qapi/char.json:490:            { 'name': 'console', 'if': 'CONFIG_WIN32' },
qapi/char.json:663:                         'if': 'CONFIG_WIN32' },
qapi/misc.json:293:{ 'command': 'get-win32-socket', 'data': {'info':
'str', 'fdname': 'str'}, 'if': 'CONFIG_WIN32' }

>
>     'if': { 'any': [ 'CONFIG_POSIX',
>                      'CONFIG_WIN32' ] },
>
>> +  }
>> +}
>>
>>    ##
>>    # @guest-ssh-get-authorized-keys:
>> @@ -1580,8 +1579,8 @@
>>    ##
>>    { 'command': 'guest-ssh-get-authorized-keys',
>>      'data': { 'username': 'str' },
>> -  'returns': 'GuestAuthorizedKeys',
>> -  'if': 'CONFIG_POSIX' }
>> +  'returns': 'GuestAuthorizedKeys'
>> +}
>>
>>    ##
>>    # @guest-ssh-add-authorized-keys:
>> @@ -1599,8 +1598,8 @@
>>    # Since: 5.2
>>    ##
>>    { 'command': 'guest-ssh-add-authorized-keys',
>> -  'data': { 'username': 'str', 'keys': ['str'], '*reset': 'bool' },
>> -  'if': 'CONFIG_POSIX' }
>> +  'data': { 'username': 'str', 'keys': ['str'], '*reset': 'bool' } }
>>
>>    ##
>>    # @guest-ssh-remove-authorized-keys:
>> @@ -1617,8 +1616,8 @@
>>    # Since: 5.2
>>    ##
>>    { 'command': 'guest-ssh-remove-authorized-keys',
>> -  'data': { 'username': 'str', 'keys': ['str'] },
>> -  'if': 'CONFIG_POSIX' }
>> +  'data': { 'username': 'str', 'keys': ['str'] } }
>>
>>    ##
>>    # @GuestDiskStats:
>
Hi Philippe, thank you for getting back to me so quickly. Looking at the grep you gave me seems to confirm what I was saying if I am not mistaken? It looks like CONFIG_WIN32 and CONFIG_POSIX if conditionals are only used when you need to enable a command on one operating system and not the other. I do believe that your code snippet is correct it is just overly verbose. The QGA has both windows and SSH implementations and looking at the guest agent QAPI file when a command supports both POSIX and Windows the if gate is removed. I am happy to discuss this further if you have more concerns.

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v3 2/2] Implement SSH commands in QEMU GA for Windows
  2024-03-27 15:54         ` Aidan Leuck
@ 2024-03-27 16:21           ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 9+ messages in thread
From: Philippe Mathieu-Daudé @ 2024-03-27 16:21 UTC (permalink / raw)
  To: Aidan Leuck, qemu-devel, Markus Armbruster; +Cc: kkostiuk, berrange

On 27/3/24 16:54, Aidan Leuck wrote:

>> On 22/3/24 18:46, aidan_leuck@selinc.com wrote:
>>> From: Aidan Leuck <aidan_leuck@selinc.com>
>>>
>>> Signed-off-by: Aidan Leuck <aidan_leuck@selinc.com>
>>> ---
>>>     qga/commands-windows-ssh.c | 791
>>> +++++++++++++++++++++++++++++++++++++
>>
>> Huge file, I'm skipping it.
>>
>>>     qga/commands-windows-ssh.h |  26 ++
>>>     qga/meson.build            |   5 +-
>>>     qga/qapi-schema.json       |  17 +-
>>>     4 files changed, 828 insertions(+), 11 deletions(-)
>>>     create mode 100644 qga/commands-windows-ssh.c
>>>     create mode 100644 qga/commands-windows-ssh.h
>>
>>
>>> diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index
>>> 9554b566a7..a64a6d91cf 100644
>>> --- a/qga/qapi-schema.json
>>> +++ b/qga/qapi-schema.json
>>> @@ -1562,9 +1562,8 @@
>>>     { 'struct': 'GuestAuthorizedKeys',
>>>       'data': {
>>>           'keys': ['str']
>>> -  },
>>> -  'if': 'CONFIG_POSIX' }
>>> -
>>
>> For Windows you have to check the CONFIG_WIN32 definition, so you want:
>>
>> I don't think this is necessary, the QEMU guest agent is compiled for only POSIX and Windows. I don't see this pattern being used elsewhere in the qapi schema file. I would be interested in what the maintainers think?
> 
> $ git grep -w CONFIG_WIN32 qapi/
> qapi/char.json:490:            { 'name': 'console', 'if': 'CONFIG_WIN32' },
> qapi/char.json:663:                         'if': 'CONFIG_WIN32' },
> qapi/misc.json:293:{ 'command': 'get-win32-socket', 'data': {'info':
> 'str', 'fdname': 'str'}, 'if': 'CONFIG_WIN32' }
> 
>>
>>      'if': { 'any': [ 'CONFIG_POSIX',
>>                       'CONFIG_WIN32' ] },
>>
>>> +  }
>>> +}
>>>
>>>     ##
>>>     # @guest-ssh-get-authorized-keys:
>>> @@ -1580,8 +1579,8 @@
>>>     ##
>>>     { 'command': 'guest-ssh-get-authorized-keys',
>>>       'data': { 'username': 'str' },
>>> -  'returns': 'GuestAuthorizedKeys',
>>> -  'if': 'CONFIG_POSIX' }
>>> +  'returns': 'GuestAuthorizedKeys'
>>> +}
>>>
>>>     ##
>>>     # @guest-ssh-add-authorized-keys:
>>> @@ -1599,8 +1598,8 @@
>>>     # Since: 5.2
>>>     ##
>>>     { 'command': 'guest-ssh-add-authorized-keys',
>>> -  'data': { 'username': 'str', 'keys': ['str'], '*reset': 'bool' },
>>> -  'if': 'CONFIG_POSIX' }
>>> +  'data': { 'username': 'str', 'keys': ['str'], '*reset': 'bool' } }
>>>
>>>     ##
>>>     # @guest-ssh-remove-authorized-keys:
>>> @@ -1617,8 +1616,8 @@
>>>     # Since: 5.2
>>>     ##
>>>     { 'command': 'guest-ssh-remove-authorized-keys',
>>> -  'data': { 'username': 'str', 'keys': ['str'] },
>>> -  'if': 'CONFIG_POSIX' }
>>> +  'data': { 'username': 'str', 'keys': ['str'] } }
>>>
>>>     ##
>>>     # @GuestDiskStats:
>>
> Hi Philippe, thank you for getting back to me so quickly. Looking at the grep you gave me seems to confirm what I was saying if I am not mistaken? It looks like CONFIG_WIN32 and CONFIG_POSIX if conditionals are only used when you need to enable a command on one operating system and not the other. I do believe that your code snippet is correct it is just overly verbose. The QGA has both windows and SSH implementations and looking at the guest agent QAPI file when a command supports both POSIX and Windows the if gate is removed. I am happy to discuss this further if you have more concerns.

Well, as you said, up to the maintainers.

Regards,

Phil.


^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2024-03-27 16:22 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-03-22 17:46 [PATCH v3 0/2] Implement SSH commands in QEMU GA for Windows aidan_leuck
2024-03-22 17:46 ` [PATCH v3 1/2] Refactor common functions between POSIX and Windows implementation aidan_leuck
2024-03-25 17:47   ` Philippe Mathieu-Daudé
2024-03-22 17:46 ` [PATCH v3 2/2] Implement SSH commands in QEMU GA for Windows aidan_leuck
2024-03-25 17:50   ` Philippe Mathieu-Daudé
2024-03-27 14:38     ` Aidan Leuck
2024-03-27 15:38       ` Philippe Mathieu-Daudé
2024-03-27 15:54         ` Aidan Leuck
2024-03-27 16:21           ` Philippe Mathieu-Daudé

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.