41
41
import sys
42
42
import stat
43
43
import time
44
+ import json
44
45
import ctypes
45
46
import string
46
47
import urllib
47
48
import StringIO
48
49
import tempfile
49
50
import calendar
50
-
51
+ import hashlib
51
52
import pycurl
53
+ import base64
52
54
import M2Crypto
53
55
54
56
def logLine (text ):
@@ -88,7 +90,7 @@ def secondsToHHMMSS(seconds):
88
90
mm , ss = divmod (ss , 60 )
89
91
return '%02d:%02d:%02d' % (hh , mm , ss )
90
92
91
- def createUserData (shutdownTime , machinetypePath , options , versionString , spaceName , machinetypeName , userDataPath , hostName , uuidStr ,
93
+ def createUserData (shutdownTime , machinetypesPath , options , versionString , spaceName , machinetypeName , userDataPath , hostName , uuidStr ,
92
94
machinefeaturesURL = None , jobfeaturesURL = None , joboutputsURL = None ):
93
95
94
96
# Get raw user_data template file, either from network ...
@@ -121,7 +123,7 @@ def createUserData(shutdownTime, machinetypePath, options, versionString, spaceN
121
123
if userDataPath [0 ] == '/' :
122
124
userDataFile = userDataPath
123
125
else :
124
- userDataFile = machinetypePath + '/' + userDataPath
126
+ userDataFile = machinetypesPath + '/' + machinetypeName + '/' + userDataPath
125
127
126
128
try :
127
129
u = open (userDataFile , 'r' )
@@ -161,12 +163,12 @@ def createUserData(shutdownTime, machinetypePath, options, versionString, spaceN
161
163
if options ['user_data_proxy_cert' ][0 ] == '/' :
162
164
certPath = options ['user_data_proxy_cert' ]
163
165
else :
164
- certPath = machinetypePath + '/' + options ['user_data_proxy_cert' ]
166
+ certPath = machinetypesPath + '/' + machinetypeName + '/' + options ['user_data_proxy_cert' ]
165
167
166
168
if options ['user_data_proxy_key' ][0 ] == '/' :
167
169
keyPath = options ['user_data_proxy_key' ]
168
170
else :
169
- keyPath = machinetypePath + '/' + options ['user_data_proxy_key' ]
171
+ keyPath = machinetypesPath + '/' + machinetypeName + '/' + options ['user_data_proxy_key' ]
170
172
171
173
try :
172
174
if ('legacy_proxy' in options ) and options ['legacy_proxy' ]:
@@ -187,7 +189,7 @@ def createUserData(shutdownTime, machinetypePath, options, versionString, spaceN
187
189
if oneValue [0 ] == '/' :
188
190
f = open (oneValue , 'r' )
189
191
else :
190
- f = open (machinetypePath + '/' + oneValue , 'r' )
192
+ f = open (machinetypesPath + '/' + machinetypeName + '/' + oneValue , 'r' )
191
193
192
194
userDataContents = userDataContents .replace ('##' + oneOption + '##' , f .read ())
193
195
f .close ()
@@ -306,6 +308,96 @@ def makeX509Proxy(certPath, keyPath, expirationTime, isLegacyProxy=False):
306
308
307
309
return proxyString
308
310
311
+ def getCernvmImageData (fileName ):
312
+
313
+ data = { 'verified' : False , 'dn' : None }
314
+
315
+ try :
316
+ length = os .stat (fileName ).st_size
317
+ except Exception as e :
318
+ logLine ('Failed to get CernVM image size (' + str (e ) + ')' )
319
+ return data
320
+
321
+ if length <= 65536 :
322
+ logLine ('CernVM image only ' + str (length ) + ' bytes long: must be more than 65536' )
323
+ return data
324
+
325
+ try :
326
+ f = open (fileName , 'r' )
327
+ except Exception as e :
328
+ logLine ('Failed to open CernVM image (' + str (e ) + ')' )
329
+ return data
330
+
331
+ try :
332
+ f .seek (- 64 * 1024 , os .SEEK_END )
333
+ metadataBlock = f .read (32 * 1024 ).rstrip ("\x00 " )
334
+ # Quick hack until the metadata section is fixed in the CernVM images (extra comma)
335
+ metadataBlock = metadataBlock .replace ('HEAD",\n ' , 'HEAD"\n ' )
336
+ metadataDict = json .loads (metadataBlock )
337
+
338
+ if 'ucernvm-version' in metadataDict :
339
+ data ['version' ] = metadataDict ['ucernvm-version' ]
340
+ except Exception as e :
341
+ logLine ('Failed to load Metadata Block JSON from CernVM image (' + str (e ) + ')' )
342
+
343
+ try :
344
+ f .seek (- 32 * 1024 , os .SEEK_END )
345
+ signatureBlock = f .read (32 * 1024 ).rstrip ("\x00 " )
346
+ # Quick hack until the howto-verify section is fixed in the CernVM images (missing commas)
347
+ signatureBlock = signatureBlock .replace ('signature>"\n ' , 'signature>",\n ' ).replace ('cvm-sign01.cern.ch"\n ' , 'cvm-sign01.cern.ch",\n ' )
348
+ signatureDict = json .loads (signatureBlock )
349
+ except Exception as e :
350
+ logLine ('Failed to load Signature Block JSON from CernVM image (' + str (e ) + ')' )
351
+ return data
352
+
353
+ try :
354
+ f .seek (0 , os .SEEK_SET )
355
+ digestableImage = f .read (length - 32 * 1024 )
356
+ hash = hashlib .sha256 (digestableImage )
357
+ digest = hash .digest ()
358
+ except Exception as e :
359
+ logLine ('Failed to make digest of CernVM image (' + str (e ) + ')' )
360
+ return data
361
+
362
+ try :
363
+ certificate = base64 .b64decode (signatureDict ['certificate' ])
364
+ x509 = M2Crypto .X509 .load_cert_string (certificate )
365
+ rsaPubkey = x509 .get_pubkey ().get_rsa ()
366
+ except Exception as e :
367
+ logLine ('Failed to get X.509 certificate and RSA public key (' + str (e ) + ')' )
368
+ return data
369
+
370
+ try :
371
+ signature = base64 .b64decode (signatureDict ['signature' ])
372
+ except :
373
+ logLine ('Failed to get signature from CernVM Signature Block' )
374
+ return data
375
+
376
+ if not rsaPubkey .verify (digest , signature , 'sha256' ):
377
+ logLine ('Certificate and calculated hash do not match given signature' )
378
+ return data
379
+
380
+ try :
381
+ # This isn't provided by M2Crypto, so we use openssl command
382
+ p = os .popen ('/usr/bin/openssl verify -CApath /etc/grid-security/certificates >/dev/null' , 'w' )
383
+ p .write (certificate )
384
+
385
+ if p .close () is None :
386
+ try :
387
+ dn = str (x509 .get_subject ())
388
+ except Exception as e :
389
+ logLine ('Failed to get X.509 Subject DN (' + str (e ) + ')' )
390
+ return data
391
+ else :
392
+ data ['verified' ] = True
393
+ data ['dn' ] = dn
394
+
395
+ except Exception as e :
396
+ logLine ('Failed to run /usr/bin/openssl verify command (' + str (e ) + ')' )
397
+ return data
398
+
399
+ return data
400
+
309
401
def getRemoteRootImage (url , imageCache , tmpDir ):
310
402
311
403
try :
0 commit comments