U
    Ӈg                     @   st   d Z ddlZddlZddlZddlZddlZddlmZ ddlm	Z	m
Z
 ddlmZ eeZdZG dd dZdS )	z0gpg.py - Collection of gpg key related functions    N)TemporaryDirectory)DictOptional)subpZ	GNUPGHOMEc                   @   s   e Zd Zdd Zdd Zeeeef dddZdd	 Z	d
dddZ
eee dddZeedddZd"eedddZd#eed
dddZed
dddZd$eeee dddZd
dd d!Zd
S )%GPGc                 C   s   d| _ i | _t | _d S )NF)gpg_started_envr   temp_dirself r   //usr/lib/python3/dist-packages/cloudinit/gpg.py__init__   s    zGPG.__init__c                 C   s   | S Nr   r
   r   r   r   	__enter__   s    zGPG.__enter__)returnc                 C   s&   | j r| j S d| _t| jji| _ | j S )a  when this env property gets invoked, set up our temporary
        directory, and also set gpg_started to tell the cleanup()
        method whether or not

        why put this here and not in __init__? pytest seems unhappy
        and it's not obvious how to work around it
        T)r   r   HOMEr	   namer
   r   r   r   env"   s
    	zGPG.envc                 C   s   |    d S r   )cleanup)r   Zexc_typ	exc_value	tracebackr   r   r   __exit__1   s    zGPG.__exit__Nc                 C   s,   |    | jr(tj| jjr(| j  dS )z0cleanup the gpg temporary directory and kill gpgN)kill_gpgr	   ospathisdirr   r   r
   r   r   r   r   4   s    zGPG.cleanup)keyr   c              
   C   sV   zt j ddd|gd| jdjW S  t jk
rP } ztd|| W 5 d}~X Y nX dS )z*Export gpg key, armoured key gets returnedgpgz--exportz--armourTcapture
update_env&Failed to export armoured key "%s": %sN)r   r   stdoutProcessExecutionErrorLOGdebugr   r   errorr   r   r   export_armour:   s    

 zGPG.export_armourc                 C   s   t j ddg|d| jdjS )zDearmor gpg key, dearmored key gets returned

        note: man gpg(1) makes no mention of an --armour spelling, only --armor
        r   z	--dearmorF)datadecoder!   )r   r   r#   )r   r   r   r   r   dearmorG   s       zGPG.dearmorF)key_filer   c                 C   sT   ddddddg}|s| d | | tj|| jdd	\}}|rPtd
|| |S )zList keys from a keyring with fingerprints. Default to a
        stable machine parseable format.

        @param key_file: a string containing a filepath to a key
        @param human_output: return output intended for human parsing
        r   z--no-optionsz--with-fingerprintz--no-default-keyringz--list-keysz	--keyringz--with-colonsT)r!   r    r"   )appendr   r   r%   warning)r   r-   Zhuman_outputcmdr#   stderrr   r   r   	list_keysP   s$    

  zGPG.list_keys   r4   )r   	keyserverr   c           	   
   C   s   t d|| d}d}t|pg }|d7 }z6tjddd| d|gd	| jd
 t d||| W dS  tjk
r } z|}W 5 d}~X Y nX z&t|}t d|j| t	| W q" t
k
r } ztd||||f |W 5 d}~X Y q"X q"dS )a  Receive gpg key from the specified keyserver.

        Retries are done by default because keyservers can be unreliable.
        Additionally, there is no way to determine the difference between
        a non-existent key and a failure.  In both cases gpg (at least 2.2.4)
        exits with status 2 and stderr: "keyserver receive failed: No data"
        It is assumed that a key provided to cloud-init exists on the keyserver
        so re-trying makes better sense than failing.

        @param key: a string key fingerprint (as passed to gpg --recv-keys).
        @param keyserver: the keyserver to request keys from.
        @param retries: an iterable of sleep lengths for retries.
        Use None to indicate no retries.z&Importing key '%s' from keyserver '%s'r   Nr4   r   z--no-ttyz--keyserver=%sz--recv-keysTr   z/Imported key '%s' from keyserver '%s' on try %dz6Import failed with exit code %d, will try again in %ssz@Failed to import key '%s' from keyserver '%s' after %d tries: %s)r%   r&   iterr   r   r$   nextZ	exit_codetimesleepStopIteration
ValueError)	r   r   r5   ZretriesZtrynumr(   ZsleepseZnaplenr   r   r   recv_keyj   sR    
zGPG.recv_keyc              
   C   sX   z t j dddd|gd| jd W n2 t jk
rR } ztd|| W 5 d}~X Y nX dS )	z0Delete the specified key from the local gpg ringr   z--batchz--yesz--delete-keysTr   zFailed delete key "%s": %sN)r   r   r$   r%   r/   r'   r   r   r   
delete_key   s    
zGPG.delete_keykeyserver.ubuntu.com)keyidr5   r   c              	   C   sd   |  |}|s`zDz| j||d |  |}W n" tk
rN   td|  Y nX W 5 | | X |S )zget gpg keyid from keyserver)r5   zFailed to obtain gpg key %s)r)   r>   r=   r;   r%   Z	exception)r   r@   r5   Zarmourr   r   r   
getkeybyid   s    
zGPG.getkeybyidc              
   C   s   z| j sW dS tdr4tjdddgd| jdj}njtjddd	d
dd
dd
dg	dddgdj}td|}dd |D }|rtd| |D ]}t	
|tj qW n0 tjk
r } ztd| W 5 d}~X Y nX dS )a  killing with gpgconf is best practice, but when it isn't available
        failover is possible

        GH: 4344 - stop gpg-agent/dirmgr daemons spawned by gpg
        key imports. Daemons spawned by cloud-config.service on systemd
        v253 report (running)
        NZgpgconfz--killallTr   Zpsz-ozppid,pidz-CZkeyboxdZdirmngrz	gpg-agentr   r4   )r    Zrcsz(?P<ppid>\d+)\s+(?P<pid>\d+)c                 S   s$   g | ]}|d  dkrt |d qS )r   1r4   )int).0pidr   r   r   
<listcomp>   s     z GPG.kill_gpg.<locals>.<listcomp>z&Killing gpg-agent and dirmngr pids: %sz"Failed to clean up gpg process: %s)r   r   Zwhichr   r#   refindallr%   r&   r   killsignalSIGKILLr$   r/   )r   Zgpg_process_outZgpg_pidsZroot_gpg_pidsZgpg_pidr<   r   r   r   r      sN    

  zGPG.kill_gpg)F)r3   )r?   )__name__
__module____qualname__r   r   propertyr   strr   r   r   r   r)   r,   r2   r=   r>   rA   r   r   r   r   r   r      s$   	7  r   )__doc__Zloggingr   rH   rK   r8   Ztempfiler   typingr   r   Z	cloudinitr   Z	getLoggerrM   r%   r   r   r   r   r   r   <module>   s   
