Re: [PATCH v3 2/2] Implement SSH commands in QEMU GA for Windows

2024-03-27 Thread Philippe Mathieu-Daudé

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


On 22/3/24 18:46, aidan_le...@selinc.com wrote:

From: Aidan Leuck 

Signed-off-by: Aidan Leuck 
---
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.



RE: [PATCH v3 2/2] Implement SSH commands in QEMU GA for Windows

2024-03-27 Thread Aidan Leuck


-Original Message-
From: Philippe Mathieu-Daudé  
Sent: Wednesday, March 27, 2024 9:38 AM
To: Aidan Leuck ; qemu-devel@nongnu.org; Markus 
Armbruster 
Cc: kkost...@redhat.com; berra...@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é 
> Sent: Monday, March 25, 2024 11:51 AM
> To: Aidan Leuck ; qemu-devel@nongnu.org
> Cc: kkost...@redhat.com; berra...@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_le...@selinc.com wrote:
>> From: Aidan Leuck 
>>
>> Signed-off-by: Aidan Leuck 
>> ---
>>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.


Re: [PATCH v3 2/2] Implement SSH commands in QEMU GA for Windows

2024-03-27 Thread Philippe Mathieu-Daudé

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é 
Sent: Monday, March 25, 2024 11:51 AM
To: Aidan Leuck ; qemu-devel@nongnu.org
Cc: kkost...@redhat.com; berra...@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_le...@selinc.com wrote:

From: Aidan Leuck 

Signed-off-by: Aidan Leuck 
---
   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:







RE: [PATCH v3 2/2] Implement SSH commands in QEMU GA for Windows

2024-03-27 Thread Aidan Leuck
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é  
Sent: Monday, March 25, 2024 11:51 AM
To: Aidan Leuck ; qemu-devel@nongnu.org
Cc: kkost...@redhat.com; berra...@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_le...@selinc.com wrote:
> From: Aidan Leuck 
>
> Signed-off-by: Aidan Leuck 
> ---
>   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:



Re: [PATCH v3 2/2] Implement SSH commands in QEMU GA for Windows

2024-03-25 Thread Philippe Mathieu-Daudé

On 22/3/24 18:46, aidan_le...@selinc.com wrote:

From: Aidan Leuck 

Signed-off-by: Aidan Leuck 
---
  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:





[PATCH v3 2/2] Implement SSH commands in QEMU GA for Windows

2024-03-22 Thread aidan_leuck
From: Aidan Leuck 

Signed-off-by: Aidan Leuck 
---
 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 00..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 
+ *
+ * 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 
+#include 
+
+#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(_ProgramData, 0, NULL, );
+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, );
+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, );
+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