Upgrade yubkey for 5.0 release. Fixes #1587

This commit is contained in:
Jay Lee
2022-12-17 16:28:41 +00:00
parent e66744e3f1
commit 8bd2e7f879
3 changed files with 62 additions and 43 deletions

View File

@@ -1314,6 +1314,10 @@ def doCheckServiceAccount(users):
3, 3,
'Invalid private key in oauth2service.json. Please delete the file and then\nrecreate with "gam create project" or "gam use project"' 'Invalid private key in oauth2service.json. Please delete the file and then\nrecreate with "gam create project" or "gam use project"'
) )
key_type = GM_Globals[GM_OAUTH2SERVICE_JSON_DATA].get('key_type', 'default')
if key_type == 'yubikey':
printPassFail('Skipping age check. YubiKey rotation not necessary.', test_pass)
else:
print( print(
'Checking key age. Google recommends rotating keys on a routine basis...' 'Checking key age. Google recommends rotating keys on a routine basis...'
) )
@@ -4473,15 +4477,7 @@ def getImap(users):
soft_errors=True, soft_errors=True,
userId='me') userId='me')
if result: if result:
enabled = result['enabled'] display.print_json(result)
if enabled:
print(
f'User: {user}, IMAP Enabled: {enabled}, autoExpunge: {result["autoExpunge"]}, expungeBehavior: {result["expungeBehavior"]}, maxFolderSize: {result["maxFolderSize"]}{currentCount(i, count)}'
)
else:
print(
f'User: {user}, IMAP Enabled: {enabled}{currentCount(i, count)}'
)
def doPop(users): def doPop(users):
@@ -7912,6 +7908,7 @@ def doCreateOrRotateServiceAccountKeys(iam=None,
yk = yubikey.YubiKey(new_data) yk = yubikey.YubiKey(new_data)
if 'yubikey_serial_number' not in new_data: if 'yubikey_serial_number' not in new_data:
new_data['yubikey_serial_number'] = yk.get_serial_number() new_data['yubikey_serial_number'] = yk.get_serial_number()
yk = yubikey.YubiKey(new_data)
if 'yubikey_slot' not in new_data: if 'yubikey_slot' not in new_data:
new_data['yubikey_slot'] = 'AUTHENTICATION' new_data['yubikey_slot'] = 'AUTHENTICATION'
publicKeyData = yk.get_certificate() publicKeyData = yk.get_certificate()
@@ -12069,6 +12066,7 @@ def ProcessGAMCommand(args):
action = sys.argv[2].lower().replace('_', '') action = sys.argv[2].lower().replace('_', '')
if action == 'resetpiv': if action == 'resetpiv':
yk = yubikey.YubiKey() yk = yubikey.YubiKey()
yk.serial_number = yk.get_serial_number()
yk.reset_piv() yk.reset_piv()
sys.exit(0) sys.exit(0)
users = getUsersToModify() users = getUsersToModify()

View File

@@ -8,7 +8,7 @@ from threading import Timer
from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding from cryptography.hazmat.primitives.asymmetric import padding
from smartcard.Exceptions import CardConnectionException from smartcard.Exceptions import CardConnectionException
from ykman.device import connect_to_device from ykman.device import list_all_devices
from ykman.piv import generate_self_signed_certificate, \ from ykman.piv import generate_self_signed_certificate, \
generate_chuid generate_chuid
from yubikit.piv import DEFAULT_MANAGEMENT_KEY, \ from yubikit.piv import DEFAULT_MANAGEMENT_KEY, \
@@ -20,11 +20,14 @@ from yubikit.piv import DEFAULT_MANAGEMENT_KEY, \
OBJECT_ID, \ OBJECT_ID, \
SLOT, \ SLOT, \
TOUCH_POLICY TOUCH_POLICY
from yubikit.core.smartcard import ApduError from yubikit.core.smartcard import ApduError, \
SmartCardConnection
from gam import controlflow from gam import controlflow
class YubiKey(): class YubiKey():
def __init__(self, service_account_info=None): def __init__(self, service_account_info=None):
self.key_type = None self.key_type = None
self.slot = None self.slot = None
@@ -46,12 +49,16 @@ class YubiKey():
self.pin = service_account_info.get('yubikey_pin') self.pin = service_account_info.get('yubikey_pin')
self.key_id = service_account_info.get('private_key_id') self.key_id = service_account_info.get('private_key_id')
def _connect(self): def _connect(self):
try: try:
conn, _, _ = connect_to_device(self.serial_number) devices = list_all_devices()
for (device, info) in devices:
if info.serial == self.serial_number:
return device.open_connection(SmartCardConnection)
except CardConnectionException as err: except CardConnectionException as err:
controlflow.system_error_exit(9, f'YubiKey - {err}') controlflow.system_error_exit(9, f'YubiKey - {err}')
return conn
def get_certificate(self): def get_certificate(self):
try: try:
@@ -79,11 +86,22 @@ class YubiKey():
def get_serial_number(self): def get_serial_number(self):
try: try:
_, _, info = connect_to_device(self.serial_number) devices = list_all_devices()
if self.serial_number:
for (device, info) in devices:
if info.serial == self.serial_number:
return info.serial return info.serial
msg = f'Could not find YubiKey with serial {self.serial_number}'
controlflow.system_error_exit(3, msg)
if len(devices) > 1:
serials = ', '.join([str(info.serial) for (_, info) in devices])
msg = f'Multiple YubiKeys connected. Specify yubikey_serial_number and one of {serials}'
controlflow.system_error_exit(4, msg)
return devices[0][1].serial
except ValueError as err: except ValueError as err:
controlflow.system_error_exit(9, f'YubiKey - {err}') controlflow.system_error_exit(9, f'YubiKey - {err}')
def reset_piv(self): def reset_piv(self):
'''Resets YubiKey PIV app and generates new key for GAM to use.''' '''Resets YubiKey PIV app and generates new key for GAM to use.'''
reply = str(input('This will wipe all PIV keys and configuration from your YubiKey. Are you sure? (y/N) ').lower().strip()) reply = str(input('This will wipe all PIV keys and configuration from your YubiKey. Are you sure? (y/N) ').lower().strip())
@@ -95,7 +113,9 @@ class YubiKey():
piv = PivSession(conn) piv = PivSession(conn)
piv.reset() piv.reset()
rnd = SystemRandom() rnd = SystemRandom()
pin_puk_chars = string.ascii_letters + string.digits + string.punctuation pin_puk_chars = string.ascii_letters + \
string.digits + \
string.punctuation
new_puk = ''.join(rnd.choice(pin_puk_chars) for _ in range(8)) new_puk = ''.join(rnd.choice(pin_puk_chars) for _ in range(8))
new_pin = ''.join(rnd.choice(pin_puk_chars) for _ in range(8)) new_pin = ''.join(rnd.choice(pin_puk_chars) for _ in range(8))
piv.change_puk('12345678', new_puk) piv.change_puk('12345678', new_puk)
@@ -155,3 +175,4 @@ class YubiKey():
if 'mplock' in globals(): if 'mplock' in globals():
mplock.release() mplock.release()
return signed return signed

View File

@@ -10,4 +10,4 @@ importlib.metadata; python_version < '3.8'
passlib>=1.7.2 passlib>=1.7.2
pathvalidate pathvalidate
python-dateutil python-dateutil
yubikey-manager>=4.0.0 yubikey-manager>=5.0