|
@@ -2,6 +2,7 @@ from binascii import hexlify, a2b_base64, b2a_base64
|
|
import configparser
|
|
import configparser
|
|
import getpass
|
|
import getpass
|
|
import os
|
|
import os
|
|
|
|
+import subprocess
|
|
import sys
|
|
import sys
|
|
import textwrap
|
|
import textwrap
|
|
from hmac import HMAC, compare_digest
|
|
from hmac import HMAC, compare_digest
|
|
@@ -23,7 +24,11 @@ PREFIX = b'\0' * 8
|
|
|
|
|
|
|
|
|
|
class PassphraseWrong(Error):
|
|
class PassphraseWrong(Error):
|
|
- """passphrase supplied in BORG_PASSPHRASE is incorrect"""
|
|
|
|
|
|
+ """passphrase supplied in BORG_PASSPHRASE or by BORG_PASSCOMMAND is incorrect."""
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+class PasscommandFailure(Error):
|
|
|
|
+ """passcommand supplied in BORG_PASSCOMMAND failed: {}"""
|
|
|
|
|
|
|
|
|
|
class PasswordRetriesExceeded(Error):
|
|
class PasswordRetriesExceeded(Error):
|
|
@@ -303,11 +308,30 @@ class AESKeyBase(KeyBase):
|
|
|
|
|
|
class Passphrase(str):
|
|
class Passphrase(str):
|
|
@classmethod
|
|
@classmethod
|
|
- def env_passphrase(cls, default=None):
|
|
|
|
- passphrase = os.environ.get('BORG_PASSPHRASE', default)
|
|
|
|
|
|
+ def _env_passphrase(cls, env_var, default=None):
|
|
|
|
+ passphrase = os.environ.get(env_var, default)
|
|
if passphrase is not None:
|
|
if passphrase is not None:
|
|
return cls(passphrase)
|
|
return cls(passphrase)
|
|
|
|
|
|
|
|
+ @classmethod
|
|
|
|
+ def env_passphrase(cls, default=None):
|
|
|
|
+ passphrase = cls._env_passphrase('BORG_PASSPHRASE', default)
|
|
|
|
+ if passphrase is not None:
|
|
|
|
+ return passphrase
|
|
|
|
+ passphrase = cls.env_passcommand()
|
|
|
|
+ if passphrase is not None:
|
|
|
|
+ return passphrase
|
|
|
|
+
|
|
|
|
+ @classmethod
|
|
|
|
+ def env_passcommand(cls, default=None):
|
|
|
|
+ passcommand = os.environ.get('BORG_PASSCOMMAND', None)
|
|
|
|
+ if passcommand is not None:
|
|
|
|
+ try:
|
|
|
|
+ passphrase = subprocess.check_output(passcommand.split(), universal_newlines=True)
|
|
|
|
+ except (subprocess.CalledProcessError, FileNotFoundError) as e:
|
|
|
|
+ raise PasscommandFailure(e)
|
|
|
|
+ return cls(passphrase.rstrip('\n'))
|
|
|
|
+
|
|
@classmethod
|
|
@classmethod
|
|
def getpass(cls, prompt):
|
|
def getpass(cls, prompt):
|
|
return cls(getpass.getpass(prompt))
|
|
return cls(getpass.getpass(prompt))
|