3
(hoO                 @   s   d Z ddlmZmZ ddlmZmZ ddlZddlZddl	Z	ddl
mZ ddlmZ ddlmZmZmZmZ dd	lmZmZ ejeZG d
d deZG dd deZG dd deZG dd deZG dd deZ G dd deZ!dd Z"dS )z5Implementing support for MySQL Authentication Plugins    )	b64encode	b64decode)sha1sha256N)uuid4   )errors)PY2isstrUNICODE_TYPESSTRING_TYPES)normalize_unicode_string"validate_normalized_unicode_stringc               @   s2   e Zd ZdZdZdZdddZdd Zd	d
 ZdS )BaseAuthPlugina  Base class for authentication plugins


    Classes inheriting from BaseAuthPlugin should implement the method
    prepare_password(). When instantiating, auth_data argument is
    required. The username, password and database are optional. The
    ssl_enabled argument can be used to tell the plugin whether SSL is
    active or not.

    The method auth_response() method is used to retrieve the password
    which was prepared by prepare_password().
    F Nc             C   s"   || _ || _|| _|| _|| _dS )ZInitializationN)
_auth_data	_username	_passwordZ	_database_ssl_enabled)self	auth_datausernamepasswordZdatabaseZssl_enabled r   R/tmp/pip-install-q3hcpn_q/mysql-connector-python/mysql/connector/authentication.py__init__?   s
    zBaseAuthPlugin.__init__c             C   s   t dS )zPrepares and returns password to be send to MySQL

        This method needs to be implemented by classes inheriting from
        this class. It is used by the auth_response() method.

        Raises NotImplementedError.
        N)NotImplementedError)r   r   r   r   prepare_passwordH   s    zBaseAuthPlugin.prepare_passwordc             C   s*   | j r"| j r"tjdj| jd| j S )zReturns the prepared password to send to MySQL

        Raises InterfaceError on errors. For example, when SSL is required
        by not enabled.

        Returns str
        z{name} requires SSL)name)requires_sslr   r   InterfaceErrorformatplugin_namer   )r   r   r   r   auth_responseR   s    zBaseAuthPlugin.auth_response)NNNF)	__name__
__module____qualname____doc__r   r"   r   r   r#   r   r   r   r   r   .   s    

r   c               @   s    e Zd ZdZdZdZdd ZdS )MySQLNativePasswordAuthPluginzBClass implementing the MySQL Native Password authentication pluginFZmysql_native_passwordc       	      C   s*  | j stjd| jsdS | j}t| jr8| jjd}n| j}trzt|}yt| j }W q tk
rv   tjdY qX n
|}| j }d}yht	|j
 }t	|j
 }t	|| j
 }trdd t||D }ndd t||D }tjd| }W n4 tk
r$ } ztjd
j|W Y dd}~X nX |S )z;Prepares and returns password as native MySQL 4.1+ passwordz"Missing authentication data (seed)    zutf-8zAuthentication data incorrectNc             S   s    g | ]\}}t |t |A qS r   )ord).0h1h3r   r   r   
<listcomp>   s    zBMySQLNativePasswordAuthPlugin.prepare_password.<locals>.<listcomp>c             S   s   g | ]\}}||A qS r   r   )r+   r,   r-   r   r   r   r.      s    20BzFailed scrambling password; {0})r/   )r   r   r    r   r
   encoder	   buffer	TypeErrorr   digestzipstructpack	Exceptionr!   )	r   r   r   Zhash4hash1hash2hash3xoredexcr   r   r   r   f   s:    

z.MySQLNativePasswordAuthPlugin.prepare_passwordN)r$   r%   r&   r'   r   r"   r   r   r   r   r   r(   `   s   r(   c               @   s    e Zd ZdZdZdZdd ZdS )MySQLClearPasswordAuthPluginzAClass implementing the MySQL Clear Password authentication pluginTZmysql_clear_passwordc             C   sF   | j s
dS | j }tr*t|tr>|jd}nt|tr>|jd}|d S )z!Returns password as as clear text    utf8)r   r	   
isinstanceunicoder0   str)r   r   r   r   r   r      s    


z-MySQLClearPasswordAuthPlugin.prepare_passwordN)r$   r%   r&   r'   r   r"   r   r   r   r   r   r=      s   r=   c               @   s    e Zd ZdZdZdZdd ZdS )MySQLSHA256PasswordAuthPluginzClass implementing the MySQL SHA256 authentication plugin

    Note that encrypting using RSA is not supported since the Python
    Standard Library does not provide this OpenSSL functionality.
    TZsha256_passwordc             C   sF   | j s
dS | j }tr*t|tr>|jd}nt|tr>|jd}|d S )z!Returns password as as clear textr>   r?   )r   r	   r@   rA   r0   rB   )r   r   r   r   r   r      s    


z.MySQLSHA256PasswordAuthPlugin.prepare_passwordN)r$   r%   r&   r'   r   r"   r   r   r   r   r   rC      s   rC   c               @   s8   e Zd ZdZdZdZdZdZdd Zdd	 Z	d
d Z
dS )"MySQLCachingSHA2PasswordAuthPluginzClass implementing the MySQL caching_sha2_password authentication plugin

    Note that encrypting using RSA is not supported since the Python
    Standard Library does not provide this OpenSSL functionality.
    FZcaching_sha2_password      c             C   s   | j stjd| jsdS t| jtr2| jjdn| j}trtt|}yt| j }W q~ t	k
rp   tjdY q~X n
|}| j }t
|j }t
 }|jt
|j  |j| |j }trdd t||D }ndd t||D }tjd	| }|S )
z Returns a scramble of the password using a Nonce sent by the
        server.

        The scramble is of the form:
        XOR(SHA2(password), SHA2(SHA2(SHA2(password)), Nonce))
        z"Missing authentication data (seed)r)   zutf-8zAuthentication data incorrectc             S   s    g | ]\}}t |t |A qS r   )r*   )r+   r,   h2r   r   r   r.      s    z@MySQLCachingSHA2PasswordAuthPlugin._scramble.<locals>.<listcomp>c             S   s   g | ]\}}||A qS r   r   )r+   r,   rG   r   r   r   r.      s    32B)rH   )r   r   r    r   r@   r   r0   r	   r1   r2   r   r3   updater4   r5   r6   )r   r   r   r8   r9   r;   r:   r   r   r   	_scramble   s.    

z,MySQLCachingSHA2PasswordAuthPlugin._scramblec             C   s2   t | jdkr| j S | jd | jkr.| j S d S )Nr   r   )lenr   rJ   perform_full_authentication_full_authentication)r   r   r   r   r      s
    z3MySQLCachingSHA2PasswordAuthPlugin.prepare_passwordc             C   s`   | j stjdj| jd| js$dS | j}trDt|trX|j	d}nt|t
rX|j	d}|d S )z!Returns password as as clear textz{name} requires SSL)r   r>   r?   )r   r   r    r!   r"   r   r	   r@   r   r0   rB   )r   r   r   r   r   rM      s    


z7MySQLCachingSHA2PasswordAuthPlugin._full_authenticationN)r$   r%   r&   r'   r   r"   rL   Zfast_auth_successrJ   r   rM   r   r   r   r   rD      s   'rD   c               @   s   e Zd ZdZdZdZeZdZdZ	dZ
dZdZdd Zdd	 Zd
d Zdd Zdd Zdd Zdd Zdd Zdd Zdd Zdd ZdS )MySQLLdapSaslPasswordAuthPlugina  Class implementing the MySQL ldap sasl authentication plugin.

    The MySQL's ldap sasl authentication plugin support two authentication
    methods SCRAM-SHA-1 and GSSAPI (using Kerberos). This implementation only
    support SCRAM-SHA-1.

    SCRAM-SHA-1
        This method requires 2 messages from client and 2 responses from
        server.

        The first message from client will be generated by prepare_password(),
        after receive the response from the server, it is required that this
        response is passed back to auth_continue() which will return the
        second message from the client. After send this second message to the
        server, the second server respond needs to be passed to auth_finalize()
        to finish the authentication process.
    FZauthentication_ldap_sasl_clientNr   c             C   sL   t r0dd t||D }djdd |D }|S tdd t||D S d S )Nc             S   s    g | ]\}}t |t |A qS r   )r*   )r+   b1b2r   r   r   r.   &  s    z8MySQLLdapSaslPasswordAuthPlugin._xor.<locals>.<listcomp>r)   c             S   s   g | ]}t |qS r   )chr)r+   ir   r   r   r.   '  s    c             S   s   g | ]\}}||A qS r   r   )r+   rO   rP   r   r   r   r.   *  s    )r	   r4   joinbytes)r   Zbytes1Zbytes2xorr   r   r   _xor$  s
    z$MySQLLdapSaslPasswordAuthPlugin._xorc             C   s   t j||| j}|j S )N)hmacnewdef_digest_moder3   )r   r   saltZdigest_makerr   r   r   _hmac,  s    z%MySQLLdapSaslPasswordAuthPlugin._hmacc             C   sN   |j  }| j||d }|}x,t|d D ]}| j||}| j||}q*W |S )zPrepares Hi
        Hi(password, salt, iterations) where Hi(p,s,i) is defined as
        PBKDF2 (HMAC, p, s, i, output length of H).

        s      r   )r0   r[   rangerV   )r   r   rZ   countpwhiZaux_r   r   r   _hi0  s    z#MySQLLdapSaslPasswordAuthPlugin._hic             C   sH   t |}t|}|d k	rDtjdj||\}}tjdj||||S )Nzbroken_rule: {}z5Unable to normalice character: `{}` in `{}` due to {})	norm_ustr
valid_normr   r    r!   )r   stringZnorm_strZbroken_rulecharZruler   r   r   
_normalize>  s    z*MySQLLdapSaslPasswordAuthPlugin._normalizec             C   sb   d}t t jdd| _|j| j| j| jd}trJt|t	r^|j
d}nt|t r^|j
d}|S )aq  This method generates the first message to the server to start the

        The client-first message consists of a gs2-header,
        the desired username, and a randomly generated client nonce cnonce.

        The first message from the server has the form:
            b'n,a=<user_name>,n=<user_name>,r=<client_nonce>

        Returns client's first message
        z.n,a={user_name},n={user_name},r={client_nonce}-r   )Z	user_nameclient_noncer?   )rB   r   replacerh   r!   rf   r   r	   r@   r   r0   )r   Z
cfm_fprnatZcfmr   r   r   _first_messageI  s    



z.MySQLLdapSaslPasswordAuthPlugin._first_messagec             C   s8   t jd| jj  | jdkr0tjdj| jd| j S )zThis method will prepare the fist message to the server.

        Returns bytes to send to the server as the first message.
        z read_method_name_from_server: %ss   SCRAM-SHA-1zfThe sasl authentication method "{}" requested from the server is not supported. Only "{}" is supportedzSCRAM-SHA-1)_LOGGERdebugr   decoder   r    r!   rj   )r   r   r   r   r#   `  s    
z-MySQLLdapSaslPasswordAuthPlugin.auth_responsec             C   s  | j stjd| j| j}| j|t| j| j}t	j
dt|j  | j|d}t	j
dt|j  | j|j }t	j
dt|j  | j|d}t	j
dt|j  djd	j| j| jd
j| jg}t	j
d| dj|| jdjtdj| j| jj j d
j| jg}t	j
d| | j||j }t	j
dt|j  | j||}	t	j
dt|	j  t| j||j j | _t	j
d| j tdj| j| jj j }
djdj|
d
j| jdjt|	j g}t	j
d| |j S )a  This method generates the second message to the server

        Second message consist on the concatenation of the client and the
        server nonce, and cproof.

        c=<n,a=<user_name>>,r=<server_nonce>,p=<client_proof>
        where:
            <client_proof>: xor(<client_key>, <client_signature>)

            <client_key>: hmac(salted_password, b"Client Key")
            <client_signature>: hmac(<stored_key>, <auth_msg>)
            <stored_key>: h(<client_key>)
            <auth_msg>: <client_first_no_header>,<servers_first>,
                        c=<client_header>,r=<server_nonce>
            <client_first_no_header>: n=<username>r=<client_nonce>
        z"Missing authentication data (seed)zsalted_password: %ss
   Client Keyzclient_key: %szstored_key: %ss
   Server Keyzserver_key: %s,zn={}zr={}zclient_first_no_header: %szc={}zn,a={},zauth_msg: %szclient_signature: %szclient_proof: %szserver_auth_var: %szp={}zsecond_message: %s)r   r   r    rf   r   ra   r   server_salt
iterationsrk   rl   r   rm   r[   rY   r3   rS   r!   r   rh   servers_firstr0   server_noncerV   server_auth_var)r   ZpasswZsalted_passwordZ
client_keyZ
stored_keyZ
server_keyZclient_first_no_headerZauth_msgZclient_signatureZclient_proofZclient_headermsgr   r   r   _second_messagep  sP    


z/MySQLLdapSaslPasswordAuthPlugin._second_messagec             C   s>  || _ | st|t r(tjdj|y|jd\}}}W n$ tk
r`   tjdj|Y nX |jd s|jd s|jd rtjdj|| j	|kr|dd | _
tjd	| j
 ntjd
j||dd | _tjd| jt| j y*|dd }tjdj| t|| _W n   tjdj|Y nX dS )zValidates first message from the server.

        Extracts the server's salt and iterations from the servers 1st response.
        First message from the server is in the form:
            <server_salt>,i=<iterations>
        zUnexpected server message: {}rn   zr=zs=zi=z&Incomplete reponse from the server: {}   Nzserver_nonce: %sz<Unable to authenticate response: response not well formed {}zserver_salt: %s length: %sziterations: {}z/Unable to authenticate: iterations not found {})rq   r@   r   r   r    r!   split
ValueError
startswithrh   rr   rk   rl   ro   rK   intrp   )r   rq   Zr_server_nonceZs_saltZ	i_counterr   r   r   _validate_first_reponse  s:    

z7MySQLLdapSaslPasswordAuthPlugin._validate_first_reponsec             C   s   | j | | j S )zwreturn the second message from the client.

        Returns bytes to send to the server as the second message.
        )r{   ru   )r   Zservers_first_responser   r   r   auth_continue  s    
z-MySQLLdapSaslPasswordAuthPlugin.auth_continuec             C   sZ   | s*t |t s*t|dks*|jd r4tjd|dd j }tjd| | j	|kS )aX  Validates second message from the server.

        The client and the server prove to each other they have the same Auth
        variable.

        The second message from the server consist of the server's proof:
            server_proof = HMAC(<server_key>, <auth_msg>)
            where:
                <server_key>: hmac(<salted_password>, b"Server Key")
                <auth_msg>: <client_first_no_header>,<servers_first>,
                            c=<client_header>,r=<server_nonce>

        Our server_proof must be equal to the Auth variable send on this second
        response.
        rv   s   v=z(The server's proof is not well formated.Nzserver auth variable: %s)
r@   	bytearrayrK   ry   r   r    rm   rk   rl   rs   )r   Zservers_secondZ
server_varr   r   r   _validate_second_reponse  s    
z8MySQLLdapSaslPasswordAuthPlugin._validate_second_reponsec             C   s   | j |stjddS )zfinalize the authentication process.

        Raises errors.InterfaceError if the ervers_second_response is invalid.

        Returns True in succesfull authentication False otherwise.
        z7Authentication failed: Unable to proof server identity.T)r~   r   r    )r   Zservers_second_responser   r   r   auth_finalize  s    

z-MySQLLdapSaslPasswordAuthPlugin.auth_finalize)r$   r%   r&   r'   r   r"   r   rY   rh   Zclient_saltro   rp   rs   rV   r[   ra   rf   rj   r#   ru   r{   r|   r~   r   r   r   r   r   rN   	  s(   D'rN   c             C   s4   xt j D ]}|j| kr
|S q
W tjdj| dS )a.  Return authentication class based on plugin name

    This function returns the class for the authentication plugin plugin_name.
    The returned class is a subclass of BaseAuthPlugin.

    Raises errors.NotSupportedError when plugin_name is not supported.

    Returns subclass of BaseAuthPlugin.
    z,Authentication plugin '{0}' is not supportedN)r   __subclasses__r"   r   ZNotSupportedErrorr!   )r"   Z	authclassr   r   r   get_auth_plugin  s
    

r   )#r'   base64r   r   hashlibr   r   rW   loggingr5   uuidr   r   r   Zcatch23r	   r
   r   r   utilsr   rb   r   rc   	getLoggerr$   rk   objectr   r(   r=   rC   rD   rN   r   r   r   r   r   <module>   s&   
2/L   