Published source
Download source ZIP
CH-J Server Manager
Browse directories and files for a specific application release.
Source is provided under the CH-J Proprietary Software License 1.14. Its availability does not change the license terms or grant additional rights.
1
#!/usr/bin/python32
"""Private one-request helper: fprintd on system D-Bus + libsecret on session D-Bus.3
No PAM changes, root access, plaintext key files or Electron basic_text backend.4
"""5
import json,sys,signal,base646
class Failure(Exception): pass7
def failure(code): raise Failure(code)8
def main():9
request=json.loads(sys.stdin.buffer.read(4097))10
try:11
import gi12
gi.require_version('Secret','1')13
from gi.repository import Gio,GLib,Secret14
except (ImportError,ValueError): failure('BIOMETRIC_KEYRING_UNAVAILABLE')15
op=request.get('op');entry=request.get('entryId');reason=str(request.get('reason','CH-J Server Manager'))[:200]16
schema=Secret.Schema.new('de.ch-j.servermanager.vault.biometry.v1',Secret.SchemaFlags.NONE,{'vault':Secret.SchemaAttributeType.STRING})17
def keyring():18
try:19
service=Secret.Service.get_sync(Secret.ServiceFlags.OPEN_SESSION | Secret.ServiceFlags.LOAD_COLLECTIONS,None)20
if service is None: failure('BIOMETRIC_KEYRING_UNAVAILABLE')21
# Only the explicit Secret Service API is used; there is no fallback backend.22
collection=Secret.Collection.for_alias_sync(service,Secret.COLLECTION_DEFAULT,Secret.CollectionFlags.NONE,None)23
if collection is None: failure('BIOMETRIC_KEYRING_UNAVAILABLE')24
return service25
except GLib.Error: failure('BIOMETRIC_KEYRING_UNAVAILABLE')26
def device():27
try:28
manager=Gio.DBusProxy.new_for_bus_sync(Gio.BusType.SYSTEM,Gio.DBusProxyFlags.NONE,None,'net.reactivated.Fprint','/net/reactivated/Fprint/Manager','net.reactivated.Fprint.Manager',None)29
path=manager.call_sync('GetDefaultDevice',None,Gio.DBusCallFlags.NONE,5000,None).unpack()[0]30
proxy=Gio.DBusProxy.new_for_bus_sync(Gio.BusType.SYSTEM,Gio.DBusProxyFlags.NONE,None,'net.reactivated.Fprint',path,'net.reactivated.Fprint.Device',None)31
fingers=proxy.call_sync('ListEnrolledFingers',GLib.Variant('(s)',('',)),Gio.DBusCallFlags.NONE,5000,None).unpack()[0]32
if not fingers: failure('BIOMETRIC_NO_ENROLLMENT')33
return proxy34
except GLib.Error as e:35
if 'NoEnrolledPrints' in str(e): failure('BIOMETRIC_NO_ENROLLMENT')36
failure('BIOMETRIC_UNAVAILABLE')37
def authenticate():38
proxy=device();loop=GLib.MainLoop();state={'code':'BIOMETRIC_FAILED','claimed':False,'started':False,'timeout':None}39
def call(method,args=None):return proxy.call_sync(method,args,Gio.DBusCallFlags.NONE,5000,None)40
def on_signal(_proxy,_sender,name,params):41
if name!='VerifyStatus':return42
result,done=params.unpack()43
if result=='verify-match':state['code']=None;loop.quit()44
elif result=='verify-no-match':state['code']='BIOMETRIC_FAILED';loop.quit()45
elif result=='verify-disconnected':state['code']='BIOMETRIC_DEVICE_UNAVAILABLE';loop.quit()46
elif done:loop.quit()47
def stop(code):state['code']=code;loop.quit();return False48
handler=proxy.connect('g-signal',on_signal)49
old_handlers={}50
try:51
call('Claim',GLib.Variant('(s)',('',)));state['claimed']=True52
for sig in [signal.SIGTERM,signal.SIGINT]:old_handlers[sig]=signal.signal(sig,lambda _s,_f:stop('BIOMETRIC_CANCELLED'))53
state['timeout']=GLib.timeout_add_seconds(60,lambda:stop('BIOMETRIC_TIMEOUT'))54
call('VerifyStart',GLib.Variant('(s)',('any',)));state['started']=True55
loop.run()56
if state['code']:failure(state['code'])57
except GLib.Error:failure('BIOMETRIC_DEVICE_UNAVAILABLE')58
finally:59
if state['timeout']:60
try:GLib.source_remove(state['timeout'])61
except Exception:pass62
if state['started']:63
try:call('VerifyStop')64
except Exception:pass65
if state['claimed']:66
try:call('Release')67
except Exception:pass68
proxy.disconnect(handler)69
for sig,handler in old_handlers.items():signal.signal(sig,handler)70
if op=='status':71
try:keyring();device();return {'ok':True,'available':True,'code':'BIOMETRIC_AVAILABLE'}72
except Failure as e:return {'ok':True,'available':False,'code':str(e)}73
if op=='authenticate':authenticate();return {'ok':True}74
if not isinstance(entry,str) or len(entry)!=64 or any(c not in '0123456789abcdef' for c in entry):failure('BIOMETRIC_FAILED')75
keyring();attrs={'vault':entry}76
try:77
if op=='store':78
encoded=request.get('key','');key=bytearray(base64.b64decode(encoded,validate=True))79
if len(key)!=32:failure('BIOMETRIC_INVALID_KEY')80
try:81
if not Secret.password_store_sync(schema,attrs,Secret.COLLECTION_DEFAULT,'CH-J Server Manager Vault',encoded,None):failure('BIOMETRIC_CREDENTIAL_FAILED')82
finally:key[:]=b'\0'*len(key)83
return {'ok':True}84
if op=='remove':Secret.password_clear_sync(schema,attrs,None);return {'ok':True}85
if op=='retrieve':86
authenticate()87
value=Secret.password_lookup_sync(schema,attrs,None)88
if value is None:failure('BIOMETRIC_ENROLLMENT_INVALIDATED')89
if len(base64.b64decode(value,validate=True))!=32:failure('BIOMETRIC_INVALID_KEY')90
return {'ok':True,'key':value}91
except GLib.Error:failure('BIOMETRIC_CREDENTIAL_FAILED')92
failure('BIOMETRIC_FAILED')93
try:94
result=main();print(json.dumps(result));sys.exit(0)95
except Failure as e:print(json.dumps({'ok':False,'code':str(e)}));sys.exit(1)96
except Exception:print(json.dumps({'ok':False,'code':'BIOMETRIC_FAILED'}));sys.exit(1)SHA-256: b014f6adff38c73a806a0b0f3c399959d8564732c63b5212c78bf161d0ec5a95
Archive SHA-256: 5ac91caf4fa32a6fdb114f2430deed486fbe7489d5eea343d1f034169fafb5e0