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)