• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2022 - The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14r"""Mkcert entry point.
15
16Mkcert will handle the SSL certificates process to secure WEB browser of
17a local or remote instance of an Android Virtual Device.
18"""
19
20import filecmp
21import logging
22import os
23import platform
24import shutil
25import stat
26
27from acloud.internal import constants
28from acloud.internal.lib import utils
29
30logger = logging.getLogger(__name__)
31
32_CA_NAME = constants.SSL_CA_NAME
33_CERT_DIR = os.path.join(os.path.expanduser("~"), constants.SSL_DIR)
34_CA_KEY_PATH = os.path.join(_CERT_DIR, f"{_CA_NAME}.key")
35_CA_CRT_PATH = os.path.join(_CERT_DIR, f"{_CA_NAME}.pem")
36_CERT_KEY_PATH = os.path.join(_CERT_DIR, "server.key")
37_CERT_CSR_PATH = os.path.join(_CERT_DIR, "server.csr")
38_CERT_CRT_PATH = os.path.join(_CERT_DIR, "server.crt")
39_CA_EXT = "keyUsage=critical,keyCertSign"
40_CA_SUBJ="/OU=acloud/O=acloud development CA/CN=localhost"
41_CERT_SUBJ = "/OU=%s/O=acloud development CA" % platform.node()
42_TRUST_CA_PATH = os.path.join(constants.SSL_TRUST_CA_DIR,
43                              f"{_CA_NAME}.crt")
44_CERT_CRT_EXT = ";".join(f"echo \"{ext}\"" for ext in [
45    "keyUsage = critical, digitalSignature, keyEncipherment",
46    "extendedKeyUsage = serverAuth",
47    "subjectAltName = DNS.1:localhost, IP.1:0.0.0.0, IP.2:::1"])
48
49# Generate a Root SSL Certificate.
50_CA_CMD = (f"openssl req -new -x509 -days 9999 -newkey rsa:2048 "
51           f"-sha256 -nodes -keyout \"{_CA_KEY_PATH}\" "
52           f"-out \"{_CA_CRT_PATH}\" -extensions v3_ca "
53           f"-subj \"{_CA_SUBJ}\" -addext \"{_CA_EXT}\"")
54
55# Trust the Root SSL Certificate.
56_TRUST_CA_COPY_CMD = f"sudo cp -p {_CA_CRT_PATH} {_TRUST_CA_PATH}"
57_UPDATE_TRUST_CA_CMD = "sudo update-ca-certificates"
58_TRUST_CHROME_CMD = (
59    "certutil -d sql:$HOME/.pki/nssdb -A -t TC "
60    f"-n \"{_CA_NAME}\" -i \"{_TRUST_CA_PATH}\"")
61
62# Generate an SSL SAN Certificate with the Root Certificate.
63_CERT_KEY_CMD = f"openssl genrsa -out \"{_CERT_KEY_PATH}\" 2048"
64_CERT_CSR_CMD = (f"openssl req -new -key \"{_CERT_KEY_PATH}\" "
65                 f"-out \"{_CERT_CSR_PATH}\" -subj \"{_CERT_SUBJ}\"")
66_CERT_CRT_CMD = (
67    f"openssl x509 -req -days 9999 -in \"{_CERT_CSR_PATH}\" "
68    f"-CA \"{_CA_CRT_PATH}\" -CAkey \"{_CA_KEY_PATH}\" "
69    f"-CAcreateserial -out \"{_CERT_CRT_PATH}\" "
70    f"-extfile <({_CERT_CRT_EXT};)")
71
72# UnInstall the Root SSL Certificate.
73_UNDO_TRUST_CA_CMD = f"sudo rm {_TRUST_CA_PATH}"
74_UNDO_TRUST_CHROME_CMD = f"certutil -D -d sql:$HOME/.pki/nssdb -n \"{_CA_NAME}\""
75
76
77def Install():
78    """Install Root SSL Certificates by the openssl tool.
79
80    Generates a Root SSL Certificates and setup the host environment
81    to build a secure browser for WebRTC AVD.
82
83    Returns:
84        True when the Root SSL Certificates are generated and setup.
85    """
86    if os.path.isdir(_CERT_DIR):
87        shutil.rmtree(_CERT_DIR)
88    os.mkdir(_CERT_DIR)
89
90    if os.path.exists(_TRUST_CA_PATH):
91        UnInstall()
92
93    utils.Popen(_CA_CMD, shell=True)
94    # The rootCA.pem file should grant READ permission to others.
95    if not os.stat(_CA_CRT_PATH).st_mode & stat.S_IROTH:
96        os.chmod(_CA_CRT_PATH, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH)
97    utils.Popen(_TRUST_CA_COPY_CMD, shell=True)
98    utils.Popen(_UPDATE_TRUST_CA_CMD, shell=True)
99    utils.Popen(_TRUST_CHROME_CMD, shell=True)
100
101    return IsRootCAReady()
102
103
104def AllocateLocalHostCert():
105    """Allocate localhost certificate by the openssl tool.
106
107    Generate an SSL SAN Certificate with the Root Certificate.
108
109    Returns:
110        True if the certificates is exist.
111    """
112    if not IsRootCAReady():
113        logger.debug("Can't load CA files.")
114        return False
115
116    if not os.path.exists(_CERT_KEY_PATH):
117        utils.Popen(_CERT_KEY_CMD, shell=True)
118    if not os.path.exists(_CERT_CSR_PATH):
119        utils.Popen(_CERT_CSR_CMD, shell=True)
120    if not os.path.exists(_CERT_CRT_PATH):
121        utils.Popen(_CERT_CRT_CMD, shell=True)
122
123    return IsCertificateReady()
124
125
126def IsRootCAReady():
127    """Check if the Root SSL Certificates are all ready.
128
129    Returns:
130        True if the Root SSL Certificates are exist.
131    """
132    for cert_file_name in [_CA_KEY_PATH, _CA_CRT_PATH, _TRUST_CA_PATH]:
133        if not os.path.exists(cert_file_name):
134            logger.debug("Root SSL Certificate: %s, does not exist",
135                         cert_file_name)
136            return False
137    # TODO: this check can be delete when the mkcert mechanism is stable.
138    if not os.stat(_TRUST_CA_PATH).st_mode & stat.S_IROTH:
139        return False
140
141    if not filecmp.cmp(_CA_CRT_PATH, _TRUST_CA_PATH):
142        logger.debug("The trusted CA %s file is not the same with %s ",
143                     _TRUST_CA_PATH, _CA_CRT_PATH)
144        return False
145    return True
146
147
148def IsCertificateReady():
149    """Check if the SSL SAN Certificates files are all ready.
150
151    Returns:
152        True if the SSL SAN Certificates files existed.
153    """
154    for cert_file_name in [_CERT_KEY_PATH, _CERT_CRT_PATH]:
155        if not os.path.exists(cert_file_name):
156            logger.debug("SSL SAN Certificate: %s, does not exist",
157                         cert_file_name)
158            return False
159    return True
160
161
162def UnInstall():
163    """Uninstall a Root SSL Certificate.
164
165    Undo the Root SSL Certificate host setup.
166    """
167    utils.Popen(_UNDO_TRUST_CA_CMD, shell=True)
168    utils.Popen(_UPDATE_TRUST_CA_CMD, shell=True)
169    utils.Popen(_UNDO_TRUST_CHROME_CMD, shell=True)
170