1""" 2Install certificates into the keychain on Mac OS 3 4.. versionadded:: 2016.3.0 5 6""" 7 8import logging 9import re 10import shlex 11 12import salt.utils.platform 13 14try: 15 import pipes 16 17 HAS_DEPS = True 18except ImportError: 19 HAS_DEPS = False 20 21if hasattr(shlex, "quote"): 22 _quote = shlex.quote 23elif HAS_DEPS and hasattr(pipes, "quote"): 24 _quote = pipes.quote 25else: 26 _quote = None 27 28log = logging.getLogger(__name__) 29 30__virtualname__ = "keychain" 31 32 33def __virtual__(): 34 """ 35 Only work on Mac OS 36 """ 37 if salt.utils.platform.is_darwin() and _quote is not None: 38 return __virtualname__ 39 return (False, "Only available on Mac OS systems with pipes") 40 41 42def install( 43 cert, 44 password, 45 keychain="/Library/Keychains/System.keychain", 46 allow_any=False, 47 keychain_password=None, 48): 49 """ 50 Install a certificate 51 52 cert 53 The certificate to install 54 55 password 56 The password for the certificate being installed formatted in the way 57 described for openssl command in the PASS PHRASE ARGUMENTS section. 58 59 Note: The password given here will show up as plaintext in the job returned 60 info. 61 62 keychain 63 The keychain to install the certificate to, this defaults to 64 /Library/Keychains/System.keychain 65 66 allow_any 67 Allow any application to access the imported certificate without warning 68 69 keychain_password 70 If your keychain is likely to be locked pass the password and it will be unlocked 71 before running the import 72 73 Note: The password given here will show up as plaintext in the returned job 74 info. 75 76 CLI Example: 77 78 .. code-block:: bash 79 80 salt '*' keychain.install test.p12 test123 81 """ 82 if keychain_password is not None: 83 unlock_keychain(keychain, keychain_password) 84 85 cmd = "security import {} -P {} -k {}".format(cert, password, keychain) 86 if allow_any: 87 cmd += " -A" 88 return __salt__["cmd.run"](cmd) 89 90 91def uninstall( 92 cert_name, keychain="/Library/Keychains/System.keychain", keychain_password=None 93): 94 """ 95 Uninstall a certificate from a keychain 96 97 cert_name 98 The name of the certificate to remove 99 100 keychain 101 The keychain to install the certificate to, this defaults to 102 /Library/Keychains/System.keychain 103 104 keychain_password 105 If your keychain is likely to be locked pass the password and it will be unlocked 106 before running the import 107 108 Note: The password given here will show up as plaintext in the returned job 109 info. 110 111 CLI Example: 112 113 .. code-block:: bash 114 115 salt '*' keychain.install test.p12 test123 116 """ 117 if keychain_password is not None: 118 unlock_keychain(keychain, keychain_password) 119 120 cmd = 'security delete-certificate -c "{}" {}'.format(cert_name, keychain) 121 return __salt__["cmd.run"](cmd) 122 123 124def list_certs(keychain="/Library/Keychains/System.keychain"): 125 """ 126 List all of the installed certificates 127 128 keychain 129 The keychain to install the certificate to, this defaults to 130 /Library/Keychains/System.keychain 131 132 CLI Example: 133 134 .. code-block:: bash 135 136 salt '*' keychain.list_certs 137 """ 138 cmd = ( 139 'security find-certificate -a {} | grep -o "alis".*\\" | ' 140 "grep -o '\\\"[-A-Za-z0-9.:() ]*\\\"'".format(_quote(keychain)) 141 ) 142 out = __salt__["cmd.run"](cmd, python_shell=True) 143 return out.replace('"', "").split("\n") 144 145 146def get_friendly_name(cert, password): 147 """ 148 Get the friendly name of the given certificate 149 150 cert 151 The certificate to install 152 153 password 154 The password for the certificate being installed formatted in the way 155 described for openssl command in the PASS PHRASE ARGUMENTS section 156 157 Note: The password given here will show up as plaintext in the returned job 158 info. 159 160 CLI Example: 161 162 .. code-block:: bash 163 164 salt '*' keychain.get_friendly_name /tmp/test.p12 test123 165 """ 166 cmd = ( 167 "openssl pkcs12 -in {} -passin pass:{} -info -nodes -nokeys 2> /dev/null | " 168 "grep friendlyName:".format(_quote(cert), _quote(password)) 169 ) 170 out = __salt__["cmd.run"](cmd, python_shell=True) 171 return out.replace("friendlyName: ", "").strip() 172 173 174def get_default_keychain(user=None, domain="user"): 175 """ 176 Get the default keychain 177 178 user 179 The user to check the default keychain of 180 181 domain 182 The domain to use valid values are user|system|common|dynamic, the default is user 183 184 CLI Example: 185 186 .. code-block:: bash 187 188 salt '*' keychain.get_default_keychain 189 """ 190 cmd = "security default-keychain -d {}".format(domain) 191 return __salt__["cmd.run"](cmd, runas=user) 192 193 194def set_default_keychain(keychain, domain="user", user=None): 195 """ 196 Set the default keychain 197 198 keychain 199 The location of the keychain to set as default 200 201 domain 202 The domain to use valid values are user|system|common|dynamic, the default is user 203 204 user 205 The user to set the default keychain as 206 207 CLI Example: 208 209 .. code-block:: bash 210 211 salt '*' keychain.set_keychain /Users/fred/Library/Keychains/login.keychain 212 """ 213 cmd = "security default-keychain -d {} -s {}".format(domain, keychain) 214 return __salt__["cmd.run"](cmd, runas=user) 215 216 217def unlock_keychain(keychain, password): 218 """ 219 Unlock the given keychain with the password 220 221 keychain 222 The keychain to unlock 223 224 password 225 The password to use to unlock the keychain. 226 227 Note: The password given here will show up as plaintext in the returned job 228 info. 229 230 CLI Example: 231 232 .. code-block:: bash 233 234 salt '*' keychain.unlock_keychain /tmp/test.p12 test123 235 """ 236 cmd = "security unlock-keychain -p {} {}".format(password, keychain) 237 __salt__["cmd.run"](cmd) 238 239 240def get_hash(name, password=None): 241 """ 242 Returns the hash of a certificate in the keychain. 243 244 name 245 The name of the certificate (which you can get from keychain.get_friendly_name) or the 246 location of a p12 file. 247 248 password 249 The password that is used in the certificate. Only required if your passing a p12 file. 250 Note: This will be outputted to logs 251 252 CLI Example: 253 254 .. code-block:: bash 255 256 salt '*' keychain.get_hash /tmp/test.p12 test123 257 """ 258 259 if ".p12" in name[-4:]: 260 cmd = "openssl pkcs12 -in {0} -passin pass:{1} -passout pass:{1}".format( 261 name, password 262 ) 263 else: 264 cmd = 'security find-certificate -c "{}" -m -p'.format(name) 265 266 out = __salt__["cmd.run"](cmd) 267 matches = re.search( 268 "-----BEGIN CERTIFICATE-----(.*)-----END CERTIFICATE-----", 269 out, 270 re.DOTALL | re.MULTILINE, 271 ) 272 if matches: 273 return matches.group(1) 274 else: 275 return False 276