From 697dd9fcbfe800be3520fa9cb957c82a0c8746a1 Mon Sep 17 00:00:00 2001
From: Timothee 'TTimo' Besset <ttimo@ttimo.net>
Date: Thu, 24 Feb 2022 14:53:17 -0600
Subject: [PATCH] add in service hooks for identify and registration

---
 hooks/approve-ssh-key   |  63 +++++++++++++++++
 hooks/devkit-1-identify | 151 ++++++++++++++++++++++++++++++++++++++++
 hooks/install-ssh-key   |  74 ++++++++++++++++++++
 3 files changed, 288 insertions(+)
 create mode 100755 hooks/approve-ssh-key
 create mode 100755 hooks/devkit-1-identify
 create mode 100755 hooks/install-ssh-key

diff --git a/hooks/approve-ssh-key b/hooks/approve-ssh-key
new file mode 100755
index 0000000..8e93726
--- /dev/null
+++ b/hooks/approve-ssh-key
@@ -0,0 +1,63 @@
+#!/usr/bin/env python3
+
+import sys
+import os
+import logging
+import tempfile
+from urllib.parse import quote_plus as urllib_quote_plus
+import subprocess
+
+ENABLE_SSHD = '/usr/bin/steamos-polkit-helpers/steamos-enable-sshd'
+
+logger = logging.getLogger(os.path.realpath(__file__))
+
+try:
+    import steam_devkit.utils
+except:
+    sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
+    import steam_devkit.utils
+
+if __name__ == '__main__':
+    logger.setLevel(logging.INFO)
+    ch = logging.StreamHandler()
+    ch.setLevel(logging.INFO)
+    ch.setFormatter(logging.Formatter('%(name)s:%(message)s'))
+    logger.addHandler(ch)
+
+    request = open(sys.argv[1], 'rt').read()
+    requestIP = 'Unknown'
+    try:
+        requestIP = sys.argv[2]
+    except NameError:
+        logger.debug('request IP not given as argument to hook')
+        pass
+
+    if os.environ.get('DEVKIT_BYPASS_STEAM_PROMPT', None) == '1':
+        # Still using for the standalone dev-kit service application
+        logger.warning('Bypassing the Steam client approval - this is potentially insecure!')
+        MAGIC_PHRASE = '900b919520e4cf601998a71eec318fec'
+        tokens = request.split(' ')
+        if tokens[-1].strip('\n') != MAGIC_PHRASE:
+            raise Exception('Invalid request')
+        sys.exit(0)
+
+    key_identifier = request.split(' ')[2]
+    request = f'Development host at IP {requestIP} key: {key_identifier!r}'
+
+    steam_devkit.utils.validate_steam_client()
+
+    with tempfile.TemporaryDirectory(prefix='approve-ssh-key') as tempdir:
+        logger.debug('Issue approve-ssh-key request to the running Steam client')
+        response = os.path.join(tempdir, 'response')
+        cmd = 'approve-ssh-key?response={}&request={}'.format(
+            urllib_quote_plus(response),
+            urllib_quote_plus(request),
+        )
+        steam_devkit.utils.execute_steam_client_command(cmd)
+        with steam_devkit.utils.wait_on_file_response(response, timeout=30) as f:
+            logger.debug('Got response from Steam client')
+            # Make sure sshd is enabled to support development features
+            if os.path.exists(ENABLE_SSHD):
+                subprocess.check_call(ENABLE_SSHD)
+            else:
+                subprocess.check_call('sudo systemctl enable --now sshd', shell=True)
diff --git a/hooks/devkit-1-identify b/hooks/devkit-1-identify
new file mode 100755
index 0000000..301583d
--- /dev/null
+++ b/hooks/devkit-1-identify
@@ -0,0 +1,151 @@
+#!/usr/bin/env python3
+# encoding: utf-8
+
+# This file is part of steamos-devkit
+# SPDX-License-Identifier: LGPL-2.1+
+#
+# Copyright 2017-2018 Collabora Ltd
+#
+# This package is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This package is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this package.  If not, see
+# <http://www.gnu.org/licenses/>.
+
+# Sample script run to identify the machine we are running on. This
+# is used by the devkit daemon to choose an identity for the machine,
+# and can also be run directly.
+#
+# A script or binary (written in any language) named devkit-1-identify
+# can be placed in the same directory as this sample, and it will be
+# used preferentially.
+#
+# Execution environment:
+# - Runs as root, or as an ordinary user who can run games
+# - Part of a non-interactive secure shell session
+# Input:
+# - None
+# Output:
+# - Exit 0 on success or nonzero on error
+# - On success, must print JSON to stdout containing one object with
+#   the following keys and string values, all of which are optional:
+#   - "machine_id": The hexadecimal systemd/D-Bus machine-id(5)
+#     (in the case of a conflict between systemd's /etc/machine-id and
+#     D-Bus' traditional /var/lib/dbus/machine-id, the systemd version
+#     should be preferred)
+#   - "hostname": The machine-readable hostname as set by sethostname(2)
+#   - "pretty_hostname": A human-friendly version of the hostname as
+#     found in systemd's machine-info(5)
+#   - "product_family", "product_name", "product_serial", "product_uuid",
+#     "sys_vendor": as for /sys/devices/virtual/dmi/id
+#   - "product": The best human-friendly description of the machine
+#     hardware we can devise, for example "Alienware ASM100"
+#   - "serial": The best hardware serial number we can devise
+#   - "machine_name": The best unique name for the machine that we can
+#     devise
+# - Any diagnostic messages must be printed to stderr only
+
+import json
+import os
+import re
+import socket
+import subprocess
+import sys
+
+_MACHINE_ID_RE = re.compile(r'^[0-9A-Fa-f]{32,32}$')
+_USELESS_HOSTNAMES = frozenset((
+    'debian',
+    'host',
+    'localhost',
+    'steamos',
+    'ubuntu',
+))
+# The case combination sometimes varies, so this is lowercased and we
+# use lower() to compare.
+_USELESS_DMI_NAMES = frozenset((
+    'to be filled by o.e.m.',
+))
+
+if __name__ == '__main__':
+
+    info = {}
+    steam_serialnumber = None
+
+    for k in ('product_family', 'product_name', 'product_serial',
+              'product_uuid', 'sys_vendor'):
+        try:
+            with open('/sys/devices/virtual/dmi/id/{}'.format(k)) as reader:
+                v = reader.read().strip()
+
+                if v and v.lower() not in _USELESS_DMI_NAMES:
+                    info[k] = v
+        except (IOError, OSError, UnicodeError):
+            pass
+
+    try:
+        for f in ('/etc/machine-id', '/var/lib/dbus/machine-id'):
+            with open(f) as reader:
+                v = reader.read().strip()
+
+                if _MACHINE_ID_RE.match(v):
+                    info['machine_id'] = v
+    except (IOError, OSError, UnicodeError):
+        pass
+
+    if 'product_family' in info and 'sys_vendor' in info:
+        info['product'] = '{sys_vendor} {product_family}'.format(**info)
+    elif 'product_name' in info and 'sys_vendor' in info:
+        info['product'] = '{sys_vendor} {product_name}'.format(**info)
+    elif 'product_family' in info:
+        info['product'] = info['product_family']
+    elif 'product_name' in info:
+        info['product'] = info['product_name']
+
+    if os.access('/usr/bin/hostnamectl', os.X_OK):
+        try:
+            v = subprocess.check_output([
+                'hostnamectl', '--pretty',
+            ]).decode('utf-8').strip()
+        except (subprocess.CalledProcessError, UnicodeError):
+            pass
+        else:
+            if v:
+                info['pretty_hostname'] = v
+
+            v = subprocess.check_output([
+                'hostnamectl', '--static',
+            ]).decode('utf-8').strip()
+
+            if v:
+                info['hostname'] = v
+
+    if 'hostname' not in info:
+        try:
+            info['hostname'] = socket.gethostname()
+        except (IOError, OSError, UnicodeError):
+            pass
+
+    if steam_serialnumber:
+        info['serial'] = steam_serialnumber
+    elif 'product_serial' in info:
+        info['serial'] = info['product_serial']
+
+    if 'product' in info:
+        info['machine_name'] = info['product']
+
+    if 'hostname' in info and info['hostname'] not in _USELESS_HOSTNAMES:
+        info['machine_name'] = info['hostname']
+
+    if 'pretty_hostname' in info:
+        info['machine_name'] = info['pretty_hostname']
+
+    json.dump(info, sys.stdout, indent=4, sort_keys=True)
+    print()
diff --git a/hooks/install-ssh-key b/hooks/install-ssh-key
new file mode 100755
index 0000000..db89036
--- /dev/null
+++ b/hooks/install-ssh-key
@@ -0,0 +1,74 @@
+#!/bin/sh
+#
+# This file is part of steamos-devkit
+# SPDX-License-Identifier: LGPL-2.1+
+#
+# Copyright © 2017-2018 Collabora Ltd
+#
+# This package is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This package is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this package.  If not, see
+# <http://www.gnu.org/licenses/>.
+
+# Sample script to install ssh keys that were approved by the
+# approve-ssh-key script.
+#
+# A script or binary (written in any language) named install-ssh-key
+# can be placed in the same directory as this sample, and it will be
+# used preferentially.
+#
+# Execution environment:
+# - Runs as root
+# Input:
+# - The public key is in a temporary file accessible via argv[1], in
+#   OpenSSH format
+# - argv[2], ... are the users to which we should grant access
+# Output:
+# - Exit 0 to accept the key, or nonzero to reject or on error
+#
+# This sample implementation should be suitable for any machine that
+# has the specified users. A production implementation would be similar
+# or even identical.
+
+set -e
+
+public_key="$1"
+shift
+
+echo "Installing public key '$public_key' for users: $*"
+
+for user in "$@"
+do
+    if pwent="$(getent passwd "$user")"
+    then
+        home="$(echo "$pwent" | cut -d: -f6)"
+        group="$(id -gn "$user")"
+        install -d -m 0755 -o "$user" -g "$group" "$home/.ssh"
+        if ! [ -e "$home/.ssh/authorized_keys" ]
+        then
+            install -m 0600 -o "$user" -g "$group" "$public_key" "$home/.ssh/authorized_keys"
+        else
+            if ! grep -qF "$(cut -d " " -f2 "$public_key")" "$home/.ssh/authorized_keys"
+            then
+                if [ -n "$(tail -1c "$home/.ssh/authorized_keys")" ]
+                then
+                    echo >> "$home/.ssh/authorized_keys"
+                fi
+                cat "$public_key" >> "$home/.ssh/authorized_keys"
+            fi
+        fi
+    fi
+done
+
+echo "Public key installed"
+
+exit 0
-- 
GitLab