Back to home page

Project CMSSW displayed by LXR

 
 

    


Warning, /Utilities/ReleaseScripts/scripts/cernsso is written in an unsupported language. File is not indexed.

0001 #!/usr/bin/env python3
0002 
0003 from ctypes import *
0004 from optparse import OptionParser
0005 import random
0006 import hashlib
0007 import sys
0008 import urllib2
0009 import re
0010 import HTMLParser
0011 import sys
0012 import os
0013 import stat
0014 import string
0015 
0016 LONG, OBJECTPOINT, FUNCTIONPOINT, OFF_T = (0, 10000, 20000, 30000)
0017 
0018 CURLOPTTYPE = {
0019   LONG: 0,
0020   OBJECTPOINT: 10000,
0021   FUNCTIONPOINT: 20000,
0022   OFF_T: 30000
0023 }
0024 
0025 CURLOPT = {}
0026 
0027 def CINIT (na, t, nu):
0028   global CURLOPT
0029   CURLOPT[na] = CURLOPTTYPE[t] + nu
0030 
0031 CINIT("SSL_VERIFYPEER", LONG, 64)
0032 CINIT("USERAGENT", OBJECTPOINT, 18)
0033 CINIT("HTTPAUTH", LONG, 107)
0034 CINIT("USERPWD", OBJECTPOINT, 5)
0035 CINIT("COOKIEFILE", OBJECTPOINT, 31)
0036 CINIT("COOKIEJAR", OBJECTPOINT, 82)
0037 CINIT("COOKIELIST", OBJECTPOINT, 135)
0038 CINIT("COOKIESESSION", LONG, 96)
0039 CINIT("COOKIE", OBJECTPOINT, 22)
0040 CINIT("FOLLOWLOCATION", LONG, 52)
0041 CINIT("UNRESTRICTED_AUTH", LONG, 105)
0042 CINIT("VERBOSE", LONG, 41)
0043 CINIT("HEADER", LONG, 42)
0044 CINIT("TIMEOUT", LONG, 13)
0045 CINIT("CONNECTTIMEOUT", LONG, 78)
0046 CINIT("URL", OBJECTPOINT, 2)
0047 CINIT("POSTFIELDS", OBJECTPOINT, 15)
0048 CINIT("POSTFIELDSIZE", LONG, 60)
0049 CINIT("POST", LONG, 47)
0050 CINIT("FILE", OBJECTPOINT, 1)
0051 CINIT("WRITEDATA", OBJECTPOINT, 1) # alias to FILE
0052 CINIT("WRITEHEADER", OBJECTPOINT, 29)
0053 
0054 CURLAUTH = {
0055   'GSSNEGOTIATE': c_uint64(1 << 2)
0056 }
0057 
0058 curl_infotype = {
0059   'TEXT': 0,
0060   'HEADER_IN': 1, 
0061   'HEADER_OUT': 2,
0062   'DATA_IN': 3,
0063   'DATA_OUT': 4,
0064   'SSL_DATA_IN': 5,
0065   'SSL_DATA_OUT': 6,
0066   'END': 7
0067 }
0068 
0069 CURLINFO = {}
0070 CURLINFO['STRING'] = 0x100000
0071 CURLINFO['EFFECTIVE_URL'] = CURLINFO['STRING'] + 1
0072 
0073 CURLcode = {
0074   'OK': 0
0075 }
0076 
0077 CERN_SSO_TOOL_VERSION = "0.0.2"
0078 CERN_SSO_CURL_USER_AGENT_KRB = "curl-sso-kerberos/{0}".format(CERN_SSO_TOOL_VERSION)
0079 CERN_SSO_CURL_ADFS_EP = "/adfs/ls/auth"
0080 CERN_SSO_CURL_AUTHERR = "401.2"
0081 CERN_SSO_CURL_ADFS_SIGNIN = "wa=wsignin1.0"
0082 
0083 def fetch_cookie(url, debug=False, verbose=False, cookie=None, libc=None, libcurl=None): 
0084   # Select C and cURL libraries
0085   libc_path = _find_libc() if libc is None else libc
0086   libcurl_path = _find_libcurl() if libcurl is None else libcurl
0087 
0088   cookie_path = cookie if cookie is not None else "{0}.cookie".format("".join(random.choice(string.ascii_uppercase + string.digits) for _ in range(7)))
0089 
0090   if verbose:
0091     sys.stderr.write("libc selected: {0}\n".format(libc_path))
0092     sys.stderr.write("libcurl selected: {0}\n".format(libcurl_path))
0093     sys.stderr.write("cookie file: {0}\n".format(cookie_path))
0094 
0095   # Load cURL library
0096   cdll.LoadLibrary(libcurl_path)
0097   libcurl = CDLL(libcurl_path)
0098 
0099   # Load C library
0100   cdll.LoadLibrary(libc_path)
0101   libc = CDLL(libc_path)
0102 
0103   # cURL functions prototypes
0104   libcurl.curl_easy_init.argtypes = []
0105   libcurl.curl_easy_init.restype = c_void_p
0106   
0107   # No control for argument list types
0108   libcurl.curl_easy_setopt.restype = c_int
0109   
0110   libcurl.curl_easy_perform.argtypes = [c_void_p]
0111   libcurl.curl_easy_perform.restype = c_int 
0112   
0113   libcurl.curl_easy_getinfo.argtypes = [c_void_p, c_int]
0114   libcurl.curl_easy_getinfo.restype = c_int
0115   
0116   # libc functions prototypes
0117   libc.fopen.argtypes = [c_char_p, c_char_p]
0118   libc.fopen.restype = c_void_p # return void pointer instead of struct FILE pointer
0119 
0120   libc.fclose.argtypes = [c_void_p] # take void pointer instead of struct FILE pointer
0121   libc.fclose.restype = c_int 
0122  
0123   # General cURL settings  
0124   curl = libcurl.curl_easy_init()
0125   libcurl.curl_easy_setopt(c_void_p(curl), CURLOPT['SSL_VERIFYPEER'], 0)
0126   libcurl.curl_easy_setopt(c_void_p(curl), CURLOPT['USERAGENT'], CERN_SSO_CURL_USER_AGENT_KRB)
0127   libcurl.curl_easy_setopt(c_void_p(curl), CURLOPT['HTTPAUTH'], CURLAUTH['GSSNEGOTIATE'])
0128   libcurl.curl_easy_setopt(c_void_p(curl), CURLOPT['USERPWD'], ":")
0129   libcurl.curl_easy_setopt(c_void_p(curl), CURLOPT['COOKIEFILE'], cookie_path)
0130   libcurl.curl_easy_setopt(c_void_p(curl), CURLOPT['COOKIEJAR'], cookie_path)
0131   libcurl.curl_easy_setopt(c_void_p(curl), CURLOPT['COOKIESESSION'], 1)
0132   m = hashlib.md5()
0133   random.seed()
0134   m.update(str(random.randint(0, 99999999)))
0135   perlsessid = m.hexdigest()
0136   libcurl.curl_easy_setopt(c_void_p(curl), CURLOPT['COOKIE'], "PERLSESSID={0}".format(perlsessid))
0137   libcurl.curl_easy_setopt(c_void_p(curl), CURLOPT['FOLLOWLOCATION'], 1)
0138   libcurl.curl_easy_setopt(c_void_p(curl), CURLOPT['UNRESTRICTED_AUTH'], 1)
0139   libcurl.curl_easy_setopt(c_void_p(curl), CURLOPT['HEADER'], 0)
0140   libcurl.curl_easy_setopt(c_void_p(curl), curl_infotype['HEADER_OUT'], 1)
0141   libcurl.curl_easy_setopt(c_void_p(curl), CURLOPT['TIMEOUT'], 10)
0142   libcurl.curl_easy_setopt(c_void_p(curl), CURLOPT['CONNECTTIMEOUT'], 10)
0143 
0144   # Enable cURL debug mode if requested
0145   if debug:
0146     libcurl.curl_easy_setopt(c_void_p(curl), CURLOPT['VERBOSE'], 1)
0147   else:
0148     libcurl.curl_easy_setopt(c_void_p(curl), CURLOPT['VERBOSE'], 0)
0149 
0150   # Create files for storing headers and data
0151   fn_writedata = "data.{0}.out".format(perlsessid)
0152   fn_writeheader = "header.{0}.out".format(perlsessid)
0153   fd_writedata = libc.fopen(fn_writedata, "w")
0154   libcurl.curl_easy_setopt(c_void_p(curl), CURLOPT['WRITEDATA'], c_void_p(fd_writedata))
0155   fd_writeheader = libc.fopen(fn_writeheader, "w")
0156   libcurl.curl_easy_setopt(c_void_p(curl), CURLOPT['WRITEHEADER'], c_void_p(fd_writeheader)) 
0157 
0158   if verbose:
0159     sys.stderr.write("file created for data: {0}\n".format(fn_writedata))
0160     sys.stderr.write("file created for headers: {0}\n".format(fn_writeheader))
0161 
0162   # Step 1
0163   if verbose:
0164     sys.stderr.write("requesting... {0}\n".format(url))
0165   use_url = c_char_p(url)
0166   libcurl.curl_easy_setopt(c_void_p(curl), CURLOPT['URL'], use_url)
0167   ret = libcurl.curl_easy_perform(c_void_p(curl))
0168   if ret != CURLcode['OK']:
0169     sys.stderr.write("failed to request {0}\n".format(url))
0170     sys.stderr.write("enable cURL debug mode (--debug) for more details.\n")
0171     sys.exit(1000 + ret)
0172   c_url = c_char_p(None)
0173   ret = libcurl.curl_easy_getinfo(c_void_p(curl), CURLINFO['EFFECTIVE_URL'], byref(c_url)) 
0174   if ret != CURLcode['OK']:
0175     sys.stderr("failed fetch effective URL.\n")
0176     sys.exit(1000 + ret)
0177   if verbose:
0178     sys.stderr.write("effective URL: {0}\n".format(urllib2.unquote(c_url.value)))
0179   if c_url.value.find(CERN_SSO_CURL_ADFS_EP) == -1:
0180     sys.stderr.write("error: redirected to a wrong location.\n")
0181     sys.exit(1)  
0182 
0183   # Step 2
0184   if verbose:
0185     sys.stderr.write("requesting... {0}\n".format(urllib2.unquote(c_url.value)))
0186   use_url = c_char_p(urllib2.unquote(c_url.value))
0187   libcurl.curl_easy_setopt(c_void_p(curl), CURLOPT['URL'], use_url)
0188   libcurl.curl_easy_setopt(c_void_p(curl), CURLOPT['POST'], 1)
0189   ret = libcurl.curl_easy_perform(c_void_p(curl))
0190   if ret != CURLcode['OK']:
0191     sys.stderr.write("error: failed to follow redirect.\n")
0192     sys.stderr.write("enable cURL debug mode (--debug) for more details.\n")
0193     sys.exit(1000 + ret)
0194 
0195   # Step 3
0196   post_body = ""
0197   c_url_sp = ""
0198   with open(fn_writedata) as dataout:
0199     content = dataout.readline()
0200     if content.find(CERN_SSO_CURL_AUTHERR) != -1:
0201       sys.stderr.write("error: IDP authentication failed.\n")
0202       sys.exit(1)
0203     if verbose:
0204       sys.stderr.write("IDP authentication successful.\n")
0205     ret = re.search("<form .+? action=\"(.+?)\">", content)
0206     if ret == None:
0207       sys.stderr.write("error: reditected to a wrong location.\n")
0208       sys.exit(1)
0209     c_url_sp = ret.group(1)
0210     h = HTMLParser.HTMLParser()
0211     for pair in re.finditer("input type=\"hidden\" name=\"(.+?)\" value=\"(.+?)\"", content, re.I | re.M):
0212       post_body += "&{0}={1}".format(pair.group(1), urllib2.quote(h.unescape(pair.group(2))))
0213       
0214   # Step 4
0215   if verbose:
0216     sys.stderr.write("requesting... {0}\n".format(urllib2.unquote(c_url_sp)))
0217   use_url = c_char_p(urllib2.unquote(c_url_sp))
0218   libcurl.curl_easy_setopt(c_void_p(curl), CURLOPT['URL'], use_url)
0219   post_body = post_body[1:]
0220   libcurl.curl_easy_setopt(c_void_p(curl), CURLOPT['POSTFIELDS'], post_body)
0221   libcurl.curl_easy_setopt(c_void_p(curl), CURLOPT['POSTFIELDSIZE'], len(post_body))
0222   ret = libcurl.curl_easy_perform(c_void_p(curl))
0223   if ret != CURLcode['OK']:
0224     sys.stderr.write("error: failed to acquire SSO cookie.\n")
0225     sys.stderr.write("enable cURL debug mode (--debug) for more details.\n")
0226     sys.exit(1000 + ret)
0227   c_url = c_char_p(None)
0228   ret = libcurl.curl_easy_getinfo(c_void_p(curl), CURLINFO['EFFECTIVE_URL'], byref(c_url))
0229   if verbose:
0230     sys.stderr.write("effective URL: {0}\n".format(urllib2.unquote(c_url.value)))
0231   if ret != CURLcode['OK']:
0232     sys.stderr.write("error: failed to fetch effective URL. Cannot confirm if cookie is correct.\n")
0233     sys.exit(1000 + ret)
0234   if c_url.value.find(CERN_SSO_CURL_ADFS_SIGNIN) != -1:
0235     sys.std.err("error: wrong redirect location. Cannot confirm if cookie is correct.\n")
0236     sys.exit(1)
0237 
0238   # Cleanup and flush cookies to the file
0239   libcurl.curl_easy_cleanup(c_void_p(curl))
0240   
0241   # Close & delete output files  
0242   libc.fclose(c_void_p(fd_writeheader))
0243   libc.fclose(c_void_p(fd_writedata))
0244   if not debug:
0245     os.unlink(fn_writeheader)
0246     os.unlink(fn_writedata)
0247 
0248   # chmod cookie file
0249   os.chmod(cookie_path, stat.S_IRUSR)
0250 
0251   if cookie is None:
0252     sys.stdout.write("{0}\n".format(cookie_path))
0253     sys.exit(0)
0254 
0255 def _get_platform():
0256   if sys.platform.startswith("linux"):
0257     with open("/etc/redhat-release") as verfile:
0258       if verfile.readline().find("Scientific Linux CERN") != -1:
0259         return "linux-cern"
0260     return "linux-unknown"
0261   elif sys.platform.startswith("darwin"):
0262     return "darwin"
0263 
0264 def _find_libc():
0265   platform = _get_platform() 
0266   if platform == "linux-cern":
0267     # For SLC{5,6} to avoid picking /usr/lib/libc.so (ld script)
0268     return "/lib64/libc.so.6"
0269   elif platform == "linux-unknown":
0270     return "libc.so"
0271   elif platform == "darwin":
0272     return "libc.dylib"
0273   else:
0274     return None
0275 
0276 def _find_libcurl():
0277   platform = _get_platform()
0278   if platform == "linux-cern" or platform == "linux-unknown":
0279     return "libcurl.so"
0280   elif platform == "darwin":
0281     return "libcurl.dylib"
0282   else:
0283     return None
0284 
0285 if __name__ == "__main__":
0286   parser = OptionParser(usage="%prog [-c COOKIE_FILENAME] [-d] [-v] [--with-libc LIBC_PATH] [--with-libcurl LIBCURL_PATH] URL", version=CERN_SSO_TOOL_VERSION) 
0287   parser.add_option("-c", "--cookie", dest="cookie_filename", help="Path to cookie file name. If not specified a random name is used and printed to stdout.", action="store")
0288   parser.add_option("-d", "--debug", dest="debug", help="Enable cURL debugging. Prints to stderr and keeps data and header files.", action="store_true", default=False)
0289   parser.add_option("-v", "--verbose", dest="verbose", help="Verbose output. Prints to stderr.", action="store_true", default=False)
0290   parser.add_option("--with-libc", dest="libc_path", help="Absolute path to libc. Default: auto-detected.", action="store")
0291   parser.add_option("--with-libcurl", dest="libcurl_path", help="Absolute path to libcurl. Default: auto-detected.", action="store")
0292 
0293   (options, args) = parser.parse_args()
0294   if len(args) != 1:
0295     sys.stderr.write("error: A single argumnet required, an URL.\n")
0296     sys.exit(1)
0297 
0298   fetch_cookie(url=args[0], debug=options.debug, verbose=options.verbose, cookie=options.cookie_filename, libc=options.libc_path, libcurl=options.libcurl_path)