Changeset 1820


Ignore:
Timestamp:
05/12/06 16:36:03 (13 years ago)
Author:
pjkersha
Message:

Refactored ready for integration into ndg.security.server.MyProxy?. TODO: write destroy method and possibly
add info and change password utilities. pyOpenSSL refs are not completely removed so that M2Crypto is needed
only. This required customisations to M2Crypto - addition of type keyword to load_cert_string, load_cert_bio
and load_cert.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • TI12-security/trunk/python/Tests/MyProxyClient/m2CryptoMyPxClnt.py

    r1772 r1820  
    11#!/usr/bin/env python 
    2 # 
    3 # myproxy client 
    4 # 
    5 # Tom Uram <turam@mcs.anl.gov> 
    6 # 2005/08/04 
    7 # 
    8  
     2"""MyProxy Client interface 
     3 
     4Based on original program myproxy_logon Tom Uram <turam@mcs.anl.gov> 
     5 
     6NERC Data Grid Project 
     7 
     8@author P J Kershaw 05/12/06 
     9 
     10@copyright (C) 2006 CCLRC & NERC 
     11 
     12@license This software may be distributed under the terms of the Q Public  
     13License, version 1.0 or later. 
     14""" 
    915 
    1016import os 
     
    1420from M2Crypto.SSL.Connection import Connection 
    1521 
    16 from OpenSSL import crypto, SSL 
    1722import re 
    1823import base64 
    1924 
    2025 
    21 class GetException(Exception): pass 
    22 class RetrieveProxyException(Exception): pass 
    23 class StoreCredException(Exception): pass 
     26class MyProxyClientError(Exception): 
     27    """Catch all exception class""" 
     28     
     29class GetError(Exception): 
     30    """Exceptions arising from get request to server""" 
     31     
     32class RetrieveError(Exception): 
     33    """Error recovering a response from MyProxy""" 
    2434 
    2535 
     
    2939    return debug >= level 
    3040 
    31 class MyProxy(object): 
    32      
     41class MyProxyClient(object): 
     42    """MyProxy client interface 
     43     
     44    @cvar __getCmd: get command string 
     45    @cvar __storeCmd: store command string 
     46    @cvar _certReqParamName: names of parameters needed to generate a  
     47    certificate request e.g. CN, OU etc. 
     48    """ 
     49       
    3350    __getCmd="""VERSION=MYPROXYv2 
    3451COMMAND=0 
     
    4360LIFETIME=%d\0""" 
    4461 
    45     def __init__(self): 
    46         pass 
    47      
    48      
     62    _certReqParamName = ('O', 'OU') 
     63 
     64    #_________________________________________________________________________             
     65    def __init__(self,  
     66                 hostname=os.environ.get('MYPROXY_SERVER'),  
     67                 port=7512, 
     68                 **certReqKw): 
     69        """ 
     70        @param hostname string for MyProxy server - defaults to  
     71        MYPROXY_SERVER environment variable 
     72        @param integer port number MyProxy is running on 
     73        """ 
     74        self.hostname = hostname 
     75        self.port = port 
     76         
     77        # Set-up parameter names for certificate request 
     78        self.__certReqParam = {}.fromkeys(MyProxyClient._certReqParamName) 
     79         
     80        # Check for parameter names set from input 
     81        self.certReqParam = certReqKw 
     82 
    4983    #_________________________________________________________________________         
    50     def _createCertReq(self, 
    51                        CN, 
    52                        O='NDG', 
    53                        OU='BADC', 
    54                        bits=1024,  
    55                        messageDigest="md5"): 
    56         """ 
    57         Create certificate request. 
    58          
    59         Returns: certificate request PEM text, private key PEM text 
    60         """ 
    61                          
     84    def __setCertReqParam(self, dict): 
     85        '''certReqParam property set method - forces setting of certificate  
     86        request parameter names to valid values 
     87         
     88        @param dict: dictionary of parameters''' 
     89         
     90        invalidKw = [k for k in dict \ 
     91                     if k not in MyProxyClient._certReqParamName] 
     92        if invalidKw: 
     93            raise MyProxyClientError, \ 
     94    "Invalid certificate request keyword(s): %s.  Valid keywords are: %s" % \ 
     95    (', '.join(invalidKw), ', '.join(MyProxyClient._certReqParamName)) 
     96     
     97        self.__certReqParam.update(dict) 
     98 
     99    #_________________________________________________________________________         
     100    def __getCertReqParam(self): 
     101        """certReqParam property set method - for Certificate request  
     102        parameters dict""" 
     103        return self.__certReqParam 
     104     
     105     
     106    certReqParam = property(fset=__setCertReqParam, 
     107                            fget=__getCertReqParam, 
     108                            doc="Dictionary of parameters for cert. request") 
     109     
     110    #_________________________________________________________________________         
     111    def _createCertReq(self, CN, nBitsForKey=1024, messageDigest="md5"): 
     112        """ 
     113        Create a certificate request. 
     114         
     115        @param CN: Common Name for certificate - effectively the same as the 
     116        username for the MyProxy credential 
     117        @param nBitsForKey: number of bits for private key generation -  
     118        default is 1024 
     119        @param messageDigest: message disgest type - default is MD5 
     120        @return tuple of certificate request PEM text and private key PEM text 
     121        """ 
     122         
     123        # Check all required certifcate request DN parameters are set                 
    62124        # Create certificate request 
    63125        req = X509.Request() 
    64126     
    65127        # Generate keys 
    66         key = RSA.gen_key(bits, m2.RSA_F4) 
     128        key = RSA.gen_key(nBitsForKey, m2.RSA_F4) 
    67129     
    68130        # Create public key object 
     
    71133         
    72134        # Add the public key to the request 
    73         req.set_version(0)# Seems to default to 0, but we can now set it as well 
     135        req.set_version(0) 
    74136        req.set_pubkey(pubKey) 
    75137         
     
    77139        x509Name = X509.X509_Name() 
    78140        x509Name.CN = CN 
    79         x509Name.OU = OU 
    80         x509Name.O = O 
     141        x509Name.OU = self.__certReqParam['OU'] 
     142        x509Name.O = self.__certReqParam['O'] 
    81143        req.set_subject_name(x509Name) 
    82144         
     
    91153        Deserialize a MyProxy server response 
    92154         
    93         Returns: integer response, errorTxt (if any) 
     155        @param msg: string response message from MyProxy server 
     156        @return tuple of integer response and errorTxt string (if any) 
    94157        """ 
    95158         
     
    97160         
    98161        # get response value 
    99         responselines = filter( lambda x: x.startswith('RESPONSE'), lines) 
     162        responselines = filter(lambda x: x.startswith('RESPONSE'), lines) 
    100163        responseline = responselines[0] 
    101         response = int(responseline.split('=')[1]) 
     164        respCode = int(responseline.split('=')[1]) 
    102165         
    103166        # get error text 
    104167        errorTxt = "" 
    105         errorlines = filter( lambda x: x.startswith('ERROR'), lines) 
     168        errorlines = filter(lambda x: x.startswith('ERROR'), lines) 
    106169        for e in errorlines: 
    107170            etext = e.split('=')[1] 
    108171            errorTxt += etext 
    109172         
    110         return response, errorTxt 
     173        return respCode, errorTxt 
    111174      
    112175   
    113176    #_________________________________________________________________________              
    114     def _deserializeCerts(self, inp_dat): 
    115          
    116         pemCerts = [] 
    117          
    118         dat = inp_dat 
    119          
    120         while dat: 
    121      
     177    def _deserializeCerts(self, inputDat): 
     178        """Unpack certificates returned from a get delegation call to the 
     179        server 
     180         
     181        @param inputDat: string containing the proxy cert and private key 
     182        and signing cert all in DER format 
     183         
     184        @return list containing the equivalent to the input in PEM format""" 
     185        pemCerts = []         
     186        dat = inputDat 
     187         
     188        while dat:     
    122189            # find start of cert, get length         
    123190            ind = dat.find('\x30\x82') 
     
    128195     
    129196            # extract der-format cert, and convert to pem 
    130             c = dat[ind:ind+len+4] 
    131             x509 = crypto.load_certificate(crypto.FILETYPE_ASN1,c) 
    132             pem_cert = crypto.dump_certificate(crypto.FILETYPE_PEM,x509) 
    133             pemCerts.append(pem_cert) 
     197            derCert = dat[ind:ind+len+4] 
     198             
     199            x509 = X509.load_cert_string(derCert, type=X509.TYPE_ASN1) 
     200            pemCert = x509.as_pem() 
     201             
     202            pemCerts.append(pemCert) 
    134203     
    135204            # trim cert from data 
    136205            dat = dat[ind + len + 4:] 
    137          
    138      
     206            
    139207        return pemCerts 
    140208 
     
    142210    #_________________________________________________________________________    
    143211    def store(self, 
    144               hostname,  
    145212              username,  
    146213              certFile, 
    147214              keyFile, 
    148               lifetime=43200, 
    149               port=7512): 
    150         """\ 
    151         store credentials in a MyProxy server 
    152          
    153         Exceptions:  GetException, StoreCredException 
    154         """ 
    155         import pdb; pdb.set_trace() 
    156         # Set to version 3? - YES! MyProxy requires it.  See  
    157         # http://grid.ncsa.uiuc.edu/myproxy/protocol/ 
     215              ownerCertFile=None, 
     216              ownerKeyFile=None, 
     217              ownerPassphrase=None, 
     218              lifetime=43200): 
     219        """Upload credentials to the server 
     220         
     221        Exceptions:  GetError, StoreCredError 
     222         
     223        @param username: username selected for credential 
     224        @param certFile: user's X.509 certificate in PEM format 
     225        @param keyFile: equivalent private key file in PEM format 
     226        @param ownerCertFile: certificate used for client authentication with 
     227        the MyProxy server SSL connection.  This ID will be set as the owner 
     228        of the stored credentials.  Only the owner can later remove  
     229        credentials with myproxy-destroy or the destroy method.  If not set, 
     230        this argument defaults to certFile 
     231        @param ownerKeyFile: corresponding private key file.  See explanation 
     232        for ownerCertFile 
     233        @param ownerPassphrase: passphrase for ownerKeyFile.  Omit if the 
     234        private key is not password protected.  Nb. keyFile is expected to 
     235        be passphrase protected as this will be the passphrase used for 
     236        logon / getDelegation. 
     237        @return none 
     238        """ 
     239        ownerCertFile = ownerCertFile or certFile 
     240        ownerKeyFile = ownerKeyFile or keyFile 
     241         
     242        import pdb;pdb.set_trace() 
    158243        context = Context(protocol='sslv3') 
    159         context.load_cert(certFile,#'../hostcert.pem',  
    160                           keyfile=keyFile,#'../hostkey.pem',  
    161                           callback=lambda *ar, **kw: None) 
    162      
    163         # disable for compatibility with myproxy server (er, globus) 
     244        context.load_cert(ownerCertFile, 
     245                          keyfile=ownerKeyFile, 
     246                          callback=lambda *ar, **kw: passphrase) 
     247     
     248        # Disable for compatibility with myproxy server (er, globus) 
    164249        # globus doesn't handle this case, apparently, and instead 
    165250        # chokes in proxy delegation code 
    166         context.set_options(m2.SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS)#0x00000800L) 
     251        context.set_options(m2.SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS) 
    167252         
    168253        # connect to myproxy server 
     
    173258        # host/<hostname> one 
    174259        conn.clientPostConnectionCheck = None 
    175         conn.connect((hostname, port)) 
     260        conn.connect((self.hostname, self.port)) 
    176261         
    177262        # send globus compatibility stuff 
     
    184269            print "debug: send store command" 
    185270             
    186         storeCmd = MyProxy.__storeCmd % (username, lifetime) 
     271        storeCmd = MyProxyClient.__storeCmd % (username, lifetime) 
    187272        conn.write(storeCmd) 
    188273     
     
    195280            print dat 
    196281             
    197         response, errorTxt = self._deserializeResponse(dat) 
    198         if response: 
    199             raise GetException, errorTxt 
     282        respCode, errorTxt = self._deserializeResponse(dat) 
     283        if respCode: 
     284            raise GetError, errorTxt 
    200285        else: 
    201286            if debuglevel(1):    
     
    203288         
    204289        # Send certificate and private key 
    205         pat = re.compile(\ 
    206              '-----BEGIN CERTIFICATE-----.*?-----END CERTIFICATE-----',  
    207              re.S) 
    208          
    209         #certTxt = pat.findall(open(certFile).read())[0] 
    210290        certTxt = X509.load_cert(certFile).as_pem() 
    211291        keyTxt = open(keyFile).read() 
    212     #    PwdCB = lambda *ar, **kw: open('../tmp').read().strip()                                           
    213     #    keyTxt = EVP.load_key(keyFile, callback=PwdCB).as_pem(callback=PwdCB) 
    214292         
    215293        conn.send(certTxt + keyTxt) 
     
    220298            print "debug: get server response for store command completed" 
    221299        resp = conn.recv(8192) 
    222         response, errorTxt = self._deserializeResponse(resp) 
    223         if response: 
    224             raise RetrieveProxyException, errorTxt 
     300        respCode, errorTxt = self._deserializeResponse(resp) 
     301        if respCode: 
     302            raise RetrieveError, errorTxt 
    225303        else: 
    226304            if debuglevel(1): 
     
    228306         
    229307         
    230     def logon(self, 
    231               hostname, 
    232               username, 
    233               passphrase, 
    234               outfile, 
    235               lifetime=43200, 
    236               port=7512): 
    237         """ 
    238         Function to retrieve a proxy credential from a MyProxy server 
    239          
    240         Exceptions:  GetException, RetrieveProxyException 
    241         """ 
    242         import pdb;pdb.set_trace() 
    243      
    244         # Set to version 3? 
    245         context = Context() 
     308    def logon(self, username, passphrase, lifetime=43200): 
     309        """Retrieve a proxy credential from a MyProxy server 
     310         
     311        Exceptions:  GetError, RetrieveError 
     312         
     313        @param username: username of credential 
     314        @param passphrase: pass-phrase for private key of credential held on 
     315        server 
     316        @return list containing the credentials as strings in PEM format: the 
     317        proxy certificate, it's private key and the signing certificate. 
     318        """ 
     319     
     320        context = Context(protocol='sslv3') 
    246321         
    247322        # disable for compatibility with myproxy server (er, globus) 
    248323        # globus doesn't handle this case, apparently, and instead 
    249324        # chokes in proxy delegation code 
    250         context.set_options(m2.SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS)#0x00000800L) 
     325        context.set_options(m2.SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS) 
    251326         
    252327        # connect to myproxy server 
     
    257332        # host/<hostname> one 
    258333        conn.clientPostConnectionCheck = None 
    259         conn.connect((hostname,port)) 
     334        conn.connect((self.hostname, self.port)) 
    260335         
    261336        # send globus compatibility stuff 
     
    265340        # send get command 
    266341        if debuglevel(1):   print "debug: send get command" 
    267         getCmd = MyProxy.__getCmd % (username,passphrase,lifetime) 
     342        getCmd = MyProxyClient.__getCmd % (username,passphrase,lifetime) 
    268343        conn.write(getCmd) 
    269344     
     
    272347        dat = conn.recv(8192) 
    273348        if debuglevel(1):   print dat 
    274         response,errorTxt = self._deserializeResponse(dat) 
    275         if response: 
    276             raise GetException(errorTxt) 
     349        respCode, errorTxt = self._deserializeResponse(dat) 
     350        if respCode: 
     351            raise GetError, errorTxt 
    277352        else: 
    278353            if debuglevel(1):   print "debug: server response ok" 
     
    303378        if debuglevel(1):   print "debug: get server response" 
    304379        resp = conn.recv(8192) 
    305         response, errorTxt = self._deserializeResponse(resp) 
    306         if response: 
    307             raise RetrieveProxyException(errorTxt) 
     380        respCode, errorTxt = self._deserializeResponse(resp) 
     381        if respCode: 
     382            raise RetrieveError, errorTxt 
    308383        else: 
    309384            if debuglevel(1):   print "debug: server response ok" 
     
    312387        pemCerts = self._deserializeCerts(dat) 
    313388        if len(pemCerts) != nCerts: 
    314             print "Warning: %d certs expected, %d received" % (nCerts,len(pemCerts)) 
     389            RetrieveError, "%d certs expected, %d received" % \ 
     390                                                    (nCerts, len(pemCerts)) 
    315391     
    316392        # write certs and private key to file 
     
    319395        # - rest of cert chain 
    320396        if debuglevel(1):   print "debug: write proxy and certs to",outfile 
    321         f = file(outfile,'w') 
    322         f.write(pemCerts[0]) 
    323         f.write(priKey) 
    324         for c in pemCerts[1:]: 
    325             f.write(c) 
    326         f.close() 
     397         
     398        creds = pemCerts[0]+priKey+''.join([cert for cert in pemCerts[1:]]) 
     399         
     400        return creds 
    327401         
    328402 
     
    346420                      help="Get delegation / logon") 
    347421     
    348     parser.add_option("-c", "--store-cert", dest="certFile", default=None, 
    349                        help="Certificate to be stored") 
    350      
    351     parser.add_option("-y", "--store-key", dest="keyFile", default=None, 
    352                        help="Private key to be stored") 
    353  
    354     parser.add_option("-s", "--pshost", dest="host",  
    355                        help="The hostname of the MyProxy server to contact") 
    356     parser.add_option("-p", "--psport", dest="port", default=7512, 
    357                        help="The port of the MyProxy server to contact") 
    358     parser.add_option("-l", "--username", dest="username",  
    359                        help="The username with which the credential is stored on the MyProxy server") 
    360     parser.add_option("-o", "--out", dest="outfile",  
    361                        help="The username with which the credential is stored on the MyProxy server") 
    362     parser.add_option("-t", "--proxy-lifetime", dest="lifetime", default=43200, 
    363                        help="The username with which the credential is stored on the MyProxy server") 
    364     parser.add_option("-d", "--debug", dest="debug", default=0, 
    365                        help="Debug mode: 1=print debug info ; 2=print as in (1), and dump data to myproxy.dump") 
    366  
    367     (options,args) = parser.parse_args() 
     422    parser.add_option("-c",  
     423                      "--certfile",  
     424                      dest="certFile",  
     425                      default=None, 
     426                      help="Certificate to be stored") 
     427     
     428    parser.add_option("-y",  
     429                      "--keyfile",  
     430                      dest="keyFile",  
     431                      default=None, 
     432                      help="Private key to be stored") 
     433 
     434    parser.add_option("-s",  
     435                      "--pshost",  
     436                      dest="host",  
     437                      help="The hostname of the MyProxy server to contact") 
     438     
     439    parser.add_option("-p",  
     440                      "--psport",  
     441                      dest="port",  
     442                      default=7512, 
     443                      type="int", 
     444                      help="The port of the MyProxy server to contact") 
     445     
     446    parser.add_option("-l",  
     447                      "--username",  
     448                      dest="username",  
     449                      help=\ 
     450    "The username with which the credential is stored on the MyProxy server") 
     451 
     452    parser.add_option("-o",  
     453                      "--out",  
     454                      dest="outfile",  
     455                      help=\ 
     456    "The username with which the credential is stored on the MyProxy server") 
     457 
     458    parser.add_option("-t",  
     459                      "--proxy-lifetime",  
     460                      dest="lifetime",  
     461                      default=43200, 
     462                      type="int", 
     463                      help=\ 
     464    "The username with which the credential is stored on the MyProxy server") 
     465 
     466    parser.add_option("-d",  
     467                      "--debug",  
     468                      dest="debug",  
     469                      type="int", 
     470                      default=0, 
     471                      help=\ 
     472"Debug mode: 1=print debug info; 2=print as in (1), and dump data to myproxy.dump") 
     473 
     474    (options, args) = parser.parse_args() 
    368475     
    369476    debug = options.debug 
    370477     
    371     # process options 
    372     host = options.host 
    373     if not host: 
    374         print "Error: MyProxy host not specified" 
    375         sys.exit(1) 
    376     port = int(options.port) 
    377      
     478    # process options     
    378479    username = options.username 
    379480    if not username: 
     
    383484            import pwd 
    384485            username = pwd.getpwuid(os.geteuid())[0] 
    385     lifetime = int(options.lifetime) 
    386  
    387     myProxy = MyProxy() 
     486 
     487    myProxy = MyProxyClient(hostname=options.host,  
     488                            port=options.port, 
     489                            O='NDG', 
     490                            OU='BADC') 
    388491     
    389492    if options.getDelegation: 
     
    401504        # Retrieve proxy cert 
    402505        try: 
    403             myProxy.logon(host, 
    404                           username, 
    405                           passphrase, 
    406                           outfile, 
    407                           lifetime=lifetime, 
    408                           port=port) 
     506            creds = myProxy.logon(username,  
     507                                  passphrase,  
     508                                  lifetime=options.lifetime) 
     509            open(outfile, 'w').write(creds) 
    409510            print "A proxy has been received for user %s in %s." % \ 
    410511                (username, outfile) 
     
    418519    else: 
    419520        try: 
    420             myProxy.store(host,  
    421                          username,  
    422                          options.certFile, 
    423                          options.keyFile, 
    424                          lifetime=lifetime, 
    425                          port=port) 
     521            myProxy.store(username,  
     522                          options.certFile, 
     523                          options.keyFile, 
     524                          lifetime=options.lifetime) 
    426525            
    427         except Exception,e: 
     526        except Exception, e: 
    428527            if debuglevel(1): 
    429528                import traceback 
    430529                traceback.print_exc() 
    431530            else: 
     531                import traceback 
     532                traceback.print_exc() 
    432533                print "Error:", e 
    433534 
Note: See TracChangeset for help on using the changeset viewer.