o
    x[hú  ã                   @   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)ÚsubpÚ	GNUPGHOMEc                   @   sÖ   e Zd Zdd„ Zdd„ Zedeeef fdd„ƒZdd	„ Z	d"dd„Z
dedee fdd„Zdedefdd„Zd#dedefdd„Zd$dededd
fdd„Zdedd
fdd„Z	d%dededee fdd„Z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   Úexc_typÚ	exc_valueÚ	tracebackr   r   r   Ú__exit__1   s   zGPG.__exit__Nc                 C   s4   |   ¡  | jrtj | jj¡r| j ¡  dS dS 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Úkeyc              
   C   sV   zt j ddd|gd| jd�jW S  t jy* } zt d||¡ W Y d}~dS d}~ww )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 )z†Dearmor 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_filec                 C   sL   g d¢}|s|  d¡ |  |¡ tj|| jdd�\}}|r$t d||¡ |S )zòList 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/   Úhuman_outputÚcmdr%   Ústderrr   r   r   Ú	list_keysP   s   

ÿzGPG.list_keys©é   r7   Ú	keyserverc           	   
   C   sæ   t  d||¡ d}d}t|pg ƒ}	 |d7 }ztjddd| d	|gd| jd
� t  d|||¡ W dS  tjyF } z|}W Y d}~nd}~ww zt|ƒ}t  d|j|¡ t 	|¡ W n t
yq } ztd||||f ƒ|‚d}~ww q)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   NTr7   r    z--no-ttyz--keyserver=%sz--recv-keysr!   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&   ÚnextÚ	exit_codeÚtimeÚsleepÚStopIterationÚ
ValueError)	r   r   r8   ÚretriesÚtrynumr*   ÚsleepsÚeÚnaplenr   r   r   Úrecv_keyj   s^   û÷ü€ÿý
ÿÿý€ÿázGPG.recv_keyc              
   C   sZ   zt j dddd|gd| jd� W dS  t jy, } zt d||¡ W Y d}~dS d}~ww )	z0Delete the specified key from the local gpg ringr    z--batchz--yesz--delete-keysTr!   zFailed delete key "%s": %sN)r   r   r&   r'   r1   r)   r   r   r   Ú
delete_key¡   s   ý€ÿzGPG.delete_keyúkeyserver.ubuntu.comÚkeyidc              	   C   sj   |   |¡}|s3z%z| j||d� |   |¡}W n ty$   t d|¡ ‚ w W |  |¡ |S |  |¡ w |S )zget gpg keyid from keyserver)r8   zFailed to obtain gpg key %s)r+   rE   r?   r'   Ú	exceptionrF   )r   rH   r8   Úarmourr   r   r   Ú
getkeybyid¬   s   
þÿ
ÿzGPG.getkeybyidc              
   C   sÌ   zK| j sW dS t d¡rtjg d¢d| jd�j}W dS tjg d¢dddgd	�j}t d
|¡}dd„ |D ƒ}|r=t d|¡ |D ]	}t	 
|tj¡ q?W dS  tjye } zt d|¡ W Y d}~dS d}~ww )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)
        NÚgpgconf)rL   z--killÚallTr!   )	Úpsz-ozppid,pidú-CÚkeyboxdrO   ÚdirmngrrO   z	gpg-agentr   r7   )r"   Úrcsz(?P<ppid>\d+)\s+(?P<pid>\d+)c                 S   s$   g | ]}|d  dkrt |d ƒ‘qS )r   Ú1r7   )Ú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   Úwhichr   r%   ÚreÚfindallr'   r(   r   ÚkillÚsignalÚSIGKILLr&   r1   )r   Úgpg_process_outÚgpg_pidsÚroot_gpg_pidsÚgpg_pidrC   r   r   r   r   ¾   sF   
ýüóòÿÿÿÿ€ÿzGPG.kill_gpg)r   N)F)r6   )rG   )Ú__name__Ú
__module__Ú__qualname__r   r   Úpropertyr   Ústrr   r   r   r   r+   r.   r5   rE   rF   rK   r   r   r   r   r   r      s*    
	7ÿÿÿ
þr   )Ú__doc__Úloggingr   rY   r\   r<   Útempfiler   Útypingr   r   Ú	cloudinitr   Ú	getLoggerrb   r'   r   r   r   r   r   r   Ú<module>   s   
