| OLD | NEW |
| 1 # Copyright 2010 Google Inc. | 1 # Copyright 2010 Google Inc. |
| 2 # Copyright (c) 2011 Mitch Garnaat http://garnaat.org/ | 2 # Copyright (c) 2011 Mitch Garnaat http://garnaat.org/ |
| 3 # Copyright (c) 2011, Eucalyptus Systems, Inc. | 3 # Copyright (c) 2011, Eucalyptus Systems, Inc. |
| 4 # | 4 # |
| 5 # Permission is hereby granted, free of charge, to any person obtaining a | 5 # Permission is hereby granted, free of charge, to any person obtaining a |
| 6 # copy of this software and associated documentation files (the | 6 # copy of this software and associated documentation files (the |
| 7 # "Software"), to deal in the Software without restriction, including | 7 # "Software"), to deal in the Software without restriction, including |
| 8 # without limitation the rights to use, copy, modify, merge, publish, dis- | 8 # without limitation the rights to use, copy, modify, merge, publish, dis- |
| 9 # tribute, sublicense, and/or sell copies of the Software, and to permit | 9 # tribute, sublicense, and/or sell copies of the Software, and to permit |
| 10 # persons to whom the Software is furnished to do so, subject to the fol- | 10 # persons to whom the Software is furnished to do so, subject to the fol- |
| (...skipping 16 matching lines...) Expand all Loading... |
| 27 """ | 27 """ |
| 28 | 28 |
| 29 import base64 | 29 import base64 |
| 30 import boto | 30 import boto |
| 31 import boto.auth_handler | 31 import boto.auth_handler |
| 32 import boto.exception | 32 import boto.exception |
| 33 import boto.plugin | 33 import boto.plugin |
| 34 import boto.utils | 34 import boto.utils |
| 35 import hmac | 35 import hmac |
| 36 import sys | 36 import sys |
| 37 import time | |
| 38 import urllib | 37 import urllib |
| 38 from email.utils import formatdate |
| 39 | 39 |
| 40 from boto.auth_handler import AuthHandler | 40 from boto.auth_handler import AuthHandler |
| 41 from boto.exception import BotoClientError | 41 from boto.exception import BotoClientError |
| 42 # | 42 # |
| 43 # the following is necessary because of the incompatibilities | 43 # the following is necessary because of the incompatibilities |
| 44 # between Python 2.4, 2.5, and 2.6 as well as the fact that some | 44 # between Python 2.4, 2.5, and 2.6 as well as the fact that some |
| 45 # people running 2.4 have installed hashlib as a separate module | 45 # people running 2.4 have installed hashlib as a separate module |
| 46 # this fix was provided by boto user mccormix. | 46 # this fix was provided by boto user mccormix. |
| 47 # see: http://code.google.com/p/boto/issues/detail?id=172 | 47 # see: http://code.google.com/p/boto/issues/detail?id=172 |
| 48 # for more details. | 48 # for more details. |
| (...skipping 21 matching lines...) Expand all Loading... |
| 70 | 70 |
| 71 class HmacKeys(object): | 71 class HmacKeys(object): |
| 72 """Key based Auth handler helper.""" | 72 """Key based Auth handler helper.""" |
| 73 | 73 |
| 74 def __init__(self, host, config, provider): | 74 def __init__(self, host, config, provider): |
| 75 if provider.access_key is None or provider.secret_key is None: | 75 if provider.access_key is None or provider.secret_key is None: |
| 76 raise boto.auth_handler.NotReadyToAuthenticate() | 76 raise boto.auth_handler.NotReadyToAuthenticate() |
| 77 self._provider = provider | 77 self._provider = provider |
| 78 self._hmac = hmac.new(self._provider.secret_key, digestmod=sha) | 78 self._hmac = hmac.new(self._provider.secret_key, digestmod=sha) |
| 79 if sha256: | 79 if sha256: |
| 80 self._hmac_256 = hmac.new(self._provider.secret_key, digestmod=sha25
6) | 80 self._hmac_256 = hmac.new(self._provider.secret_key, |
| 81 digestmod=sha256) |
| 81 else: | 82 else: |
| 82 self._hmac_256 = None | 83 self._hmac_256 = None |
| 83 | 84 |
| 84 def algorithm(self): | 85 def algorithm(self): |
| 85 if self._hmac_256: | 86 if self._hmac_256: |
| 86 return 'HmacSHA256' | 87 return 'HmacSHA256' |
| 87 else: | 88 else: |
| 88 return 'HmacSHA1' | 89 return 'HmacSHA1' |
| 89 | 90 |
| 90 def sign_string(self, string_to_sign): | 91 def sign_string(self, string_to_sign): |
| (...skipping 13 matching lines...) Expand all Loading... |
| 104 def __init__(self, host, config, provider): | 105 def __init__(self, host, config, provider): |
| 105 AuthHandler.__init__(self, host, config, provider) | 106 AuthHandler.__init__(self, host, config, provider) |
| 106 HmacKeys.__init__(self, host, config, provider) | 107 HmacKeys.__init__(self, host, config, provider) |
| 107 self._hmac_256 = None | 108 self._hmac_256 = None |
| 108 | 109 |
| 109 def add_auth(self, http_request, **kwargs): | 110 def add_auth(self, http_request, **kwargs): |
| 110 headers = http_request.headers | 111 headers = http_request.headers |
| 111 method = http_request.method | 112 method = http_request.method |
| 112 auth_path = http_request.auth_path | 113 auth_path = http_request.auth_path |
| 113 if not headers.has_key('Date'): | 114 if not headers.has_key('Date'): |
| 114 headers['Date'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT", | 115 headers['Date'] = formatdate(usegmt=True) |
| 115 time.gmtime()) | |
| 116 | 116 |
| 117 if self._provider.security_token: |
| 118 key = self._provider.security_token_header |
| 119 headers[key] = self._provider.security_token |
| 117 c_string = boto.utils.canonical_string(method, auth_path, headers, | 120 c_string = boto.utils.canonical_string(method, auth_path, headers, |
| 118 None, self._provider) | 121 None, self._provider) |
| 119 b64_hmac = self.sign_string(c_string) | 122 b64_hmac = self.sign_string(c_string) |
| 120 auth_hdr = self._provider.auth_header | 123 auth_hdr = self._provider.auth_header |
| 121 headers['Authorization'] = ("%s %s:%s" % | 124 headers['Authorization'] = ("%s %s:%s" % |
| 122 (auth_hdr, | 125 (auth_hdr, |
| 123 self._provider.access_key, b64_hmac)) | 126 self._provider.access_key, b64_hmac)) |
| 124 | 127 |
| 125 class HmacAuthV2Handler(AuthHandler, HmacKeys): | 128 class HmacAuthV2Handler(AuthHandler, HmacKeys): |
| 126 """ | 129 """ |
| 127 Implements the simplified HMAC authorization used by CloudFront. | 130 Implements the simplified HMAC authorization used by CloudFront. |
| 128 """ | 131 """ |
| 129 capability = ['hmac-v2', 'cloudfront'] | 132 capability = ['hmac-v2', 'cloudfront'] |
| 130 | 133 |
| 131 def __init__(self, host, config, provider): | 134 def __init__(self, host, config, provider): |
| 132 AuthHandler.__init__(self, host, config, provider) | 135 AuthHandler.__init__(self, host, config, provider) |
| 133 HmacKeys.__init__(self, host, config, provider) | 136 HmacKeys.__init__(self, host, config, provider) |
| 134 self._hmac_256 = None | 137 self._hmac_256 = None |
| 135 | 138 |
| 136 def add_auth(self, http_request, **kwargs): | 139 def add_auth(self, http_request, **kwargs): |
| 137 headers = http_request.headers | 140 headers = http_request.headers |
| 138 if not headers.has_key('Date'): | 141 if not headers.has_key('Date'): |
| 139 headers['Date'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT", | 142 headers['Date'] = formatdate(usegmt=True) |
| 140 time.gmtime()) | |
| 141 | 143 |
| 142 b64_hmac = self.sign_string(headers['Date']) | 144 b64_hmac = self.sign_string(headers['Date']) |
| 143 auth_hdr = self._provider.auth_header | 145 auth_hdr = self._provider.auth_header |
| 144 headers['Authorization'] = ("%s %s:%s" % | 146 headers['Authorization'] = ("%s %s:%s" % |
| 145 (auth_hdr, | 147 (auth_hdr, |
| 146 self._provider.access_key, b64_hmac)) | 148 self._provider.access_key, b64_hmac)) |
| 147 | 149 |
| 148 class HmacAuthV3Handler(AuthHandler, HmacKeys): | 150 class HmacAuthV3Handler(AuthHandler, HmacKeys): |
| 149 """Implements the new Version 3 HMAC authorization used by Route53.""" | 151 """Implements the new Version 3 HMAC authorization used by Route53.""" |
| 150 | 152 |
| 151 capability = ['hmac-v3', 'route53', 'ses'] | 153 capability = ['hmac-v3', 'route53', 'ses'] |
| 152 | 154 |
| 153 def __init__(self, host, config, provider): | 155 def __init__(self, host, config, provider): |
| 154 AuthHandler.__init__(self, host, config, provider) | 156 AuthHandler.__init__(self, host, config, provider) |
| 155 HmacKeys.__init__(self, host, config, provider) | 157 HmacKeys.__init__(self, host, config, provider) |
| 156 | 158 |
| 157 def add_auth(self, http_request, **kwargs): | 159 def add_auth(self, http_request, **kwargs): |
| 158 headers = http_request.headers | 160 headers = http_request.headers |
| 159 if not headers.has_key('Date'): | 161 if not headers.has_key('Date'): |
| 160 headers['Date'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT", | 162 headers['Date'] = formatdate(usegmt=True) |
| 161 time.gmtime()) | |
| 162 | 163 |
| 163 b64_hmac = self.sign_string(headers['Date']) | 164 b64_hmac = self.sign_string(headers['Date']) |
| 164 s = "AWS3-HTTPS AWSAccessKeyId=%s," % self._provider.access_key | 165 s = "AWS3-HTTPS AWSAccessKeyId=%s," % self._provider.access_key |
| 165 s += "Algorithm=%s,Signature=%s" % (self.algorithm(), b64_hmac) | 166 s += "Algorithm=%s,Signature=%s" % (self.algorithm(), b64_hmac) |
| 166 headers['X-Amzn-Authorization'] = s | 167 headers['X-Amzn-Authorization'] = s |
| 167 | 168 |
| 168 class QuerySignatureHelper(HmacKeys): | 169 class QuerySignatureHelper(HmacKeys): |
| 169 """Helper for Query signature based Auth handler. | 170 """Helper for Query signature based Auth handler. |
| 170 | 171 |
| 171 Concrete sub class need to implement _calc_sigature method. | 172 Concrete sub class need to implement _calc_sigature method. |
| 172 """ | 173 """ |
| 173 | 174 |
| 174 def add_auth(self, http_request, **kwargs): | 175 def add_auth(self, http_request, **kwargs): |
| 175 headers = http_request.headers | 176 headers = http_request.headers |
| 176 params = http_request.params | 177 params = http_request.params |
| 177 params['AWSAccessKeyId'] = self._provider.access_key | 178 params['AWSAccessKeyId'] = self._provider.access_key |
| 178 params['SignatureVersion'] = self.SignatureVersion | 179 params['SignatureVersion'] = self.SignatureVersion |
| 179 params['Timestamp'] = boto.utils.get_ts() | 180 params['Timestamp'] = boto.utils.get_ts() |
| 180 qs, signature = self._calc_signature( | 181 qs, signature = self._calc_signature( |
| 181 http_request.params, http_request.method, | 182 http_request.params, http_request.method, |
| 182 http_request.path, http_request.host) | 183 http_request.auth_path, http_request.host) |
| 183 boto.log.debug('query_string: %s Signature: %s' % (qs, signature)) | 184 boto.log.debug('query_string: %s Signature: %s' % (qs, signature)) |
| 184 if http_request.method == 'POST': | 185 if http_request.method == 'POST': |
| 185 headers['Content-Type'] = 'application/x-www-form-urlencoded; charse
t=UTF-8' | 186 headers['Content-Type'] = 'application/x-www-form-urlencoded; charse
t=UTF-8' |
| 186 http_request.body = qs + '&Signature=' + urllib.quote(signature) | 187 http_request.body = qs + '&Signature=' + urllib.quote(signature) |
| 188 http_request.headers['Content-Length'] = str(len(http_request.body)) |
| 187 else: | 189 else: |
| 188 http_request.body = '' | 190 http_request.body = '' |
| 189 http_request.path = (http_request.path + '?' + qs + '&Signature=' +
urllib.quote(signature)) | 191 # if this is a retried request, the qs from the previous try will |
| 190 # Now that query params are part of the path, clear the 'params' field | 192 # already be there, we need to get rid of that and rebuild it |
| 191 # in request. | 193 http_request.path = http_request.path.split('?')[0] |
| 192 http_request.params = {} | 194 http_request.path = (http_request.path + '?' + qs + |
| 195 '&Signature=' + urllib.quote(signature)) |
| 193 | 196 |
| 194 class QuerySignatureV0AuthHandler(QuerySignatureHelper, AuthHandler): | 197 class QuerySignatureV0AuthHandler(QuerySignatureHelper, AuthHandler): |
| 195 """Class SQS query signature based Auth handler.""" | 198 """Provides Signature V0 Signing""" |
| 196 | 199 |
| 197 SignatureVersion = 0 | 200 SignatureVersion = 0 |
| 198 capability = ['sign-v0'] | 201 capability = ['sign-v0'] |
| 199 | 202 |
| 200 def _calc_signature(self, params, *args): | 203 def _calc_signature(self, params, *args): |
| 201 boto.log.debug('using _calc_signature_0') | 204 boto.log.debug('using _calc_signature_0') |
| 202 hmac = self._hmac.copy() | 205 hmac = self._hmac.copy() |
| 203 s = params['Action'] + params['Timestamp'] | 206 s = params['Action'] + params['Timestamp'] |
| 204 hmac.update(s) | 207 hmac.update(s) |
| 205 keys = params.keys() | 208 keys = params.keys() |
| 206 keys.sort(cmp = lambda x, y: cmp(x.lower(), y.lower())) | 209 keys.sort(cmp = lambda x, y: cmp(x.lower(), y.lower())) |
| 207 pairs = [] | 210 pairs = [] |
| 208 for key in keys: | 211 for key in keys: |
| 209 val = bot.utils.get_utf8_value(params[key]) | 212 val = boto.utils.get_utf8_value(params[key]) |
| 210 pairs.append(key + '=' + urllib.quote(val)) | 213 pairs.append(key + '=' + urllib.quote(val)) |
| 211 qs = '&'.join(pairs) | 214 qs = '&'.join(pairs) |
| 212 return (qs, base64.b64encode(hmac.digest())) | 215 return (qs, base64.b64encode(hmac.digest())) |
| 213 | 216 |
| 214 class QuerySignatureV1AuthHandler(QuerySignatureHelper, AuthHandler): | 217 class QuerySignatureV1AuthHandler(QuerySignatureHelper, AuthHandler): |
| 215 """ | 218 """ |
| 216 Provides Query Signature V1 Authentication. | 219 Provides Query Signature V1 Authentication. |
| 217 """ | 220 """ |
| 218 | 221 |
| 219 SignatureVersion = 1 | 222 SignatureVersion = 1 |
| (...skipping 11 matching lines...) Expand all Loading... |
| 231 hmac.update(val) | 234 hmac.update(val) |
| 232 pairs.append(key + '=' + urllib.quote(val)) | 235 pairs.append(key + '=' + urllib.quote(val)) |
| 233 qs = '&'.join(pairs) | 236 qs = '&'.join(pairs) |
| 234 return (qs, base64.b64encode(hmac.digest())) | 237 return (qs, base64.b64encode(hmac.digest())) |
| 235 | 238 |
| 236 class QuerySignatureV2AuthHandler(QuerySignatureHelper, AuthHandler): | 239 class QuerySignatureV2AuthHandler(QuerySignatureHelper, AuthHandler): |
| 237 """Provides Query Signature V2 Authentication.""" | 240 """Provides Query Signature V2 Authentication.""" |
| 238 | 241 |
| 239 SignatureVersion = 2 | 242 SignatureVersion = 2 |
| 240 capability = ['sign-v2', 'ec2', 'ec2', 'emr', 'fps', 'ecs', | 243 capability = ['sign-v2', 'ec2', 'ec2', 'emr', 'fps', 'ecs', |
| 241 'sdb', 'iam', 'rds', 'sns', 'sqs'] | 244 'sdb', 'iam', 'rds', 'sns', 'sqs', 'cloudformation'] |
| 242 | 245 |
| 243 def _calc_signature(self, params, verb, path, server_name): | 246 def _calc_signature(self, params, verb, path, server_name): |
| 244 boto.log.debug('using _calc_signature_2') | 247 boto.log.debug('using _calc_signature_2') |
| 245 string_to_sign = '%s\n%s\n%s\n' % (verb, server_name.lower(), path) | 248 string_to_sign = '%s\n%s\n%s\n' % (verb, server_name.lower(), path) |
| 246 if self._hmac_256: | 249 if self._hmac_256: |
| 247 hmac = self._hmac_256.copy() | 250 hmac = self._hmac_256.copy() |
| 248 params['SignatureMethod'] = 'HmacSHA256' | 251 params['SignatureMethod'] = 'HmacSHA256' |
| 249 else: | 252 else: |
| 250 hmac = self._hmac.copy() | 253 hmac = self._hmac.copy() |
| 251 params['SignatureMethod'] = 'HmacSHA1' | 254 params['SignatureMethod'] = 'HmacSHA1' |
| 255 if self._provider.security_token: |
| 256 params['SecurityToken'] = self._provider.security_token |
| 252 keys = params.keys() | 257 keys = params.keys() |
| 253 keys.sort() | 258 keys.sort() |
| 254 pairs = [] | 259 pairs = [] |
| 255 for key in keys: | 260 for key in keys: |
| 256 val = boto.utils.get_utf8_value(params[key]) | 261 val = boto.utils.get_utf8_value(params[key]) |
| 257 pairs.append(urllib.quote(key, safe='') + '=' + | 262 pairs.append(urllib.quote(key, safe='') + '=' + |
| 258 urllib.quote(val, safe='-_~')) | 263 urllib.quote(val, safe='-_~')) |
| 259 qs = '&'.join(pairs) | 264 qs = '&'.join(pairs) |
| 260 boto.log.debug('query string: %s' % qs) | 265 boto.log.debug('query string: %s' % qs) |
| 261 string_to_sign += qs | 266 string_to_sign += qs |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 296 try: | 301 try: |
| 297 ready_handlers.append(handler(host, config, provider)) | 302 ready_handlers.append(handler(host, config, provider)) |
| 298 except boto.auth_handler.NotReadyToAuthenticate: | 303 except boto.auth_handler.NotReadyToAuthenticate: |
| 299 pass | 304 pass |
| 300 | 305 |
| 301 if not ready_handlers: | 306 if not ready_handlers: |
| 302 checked_handlers = auth_handlers | 307 checked_handlers = auth_handlers |
| 303 names = [handler.__name__ for handler in checked_handlers] | 308 names = [handler.__name__ for handler in checked_handlers] |
| 304 raise boto.exception.NoAuthHandlerFound( | 309 raise boto.exception.NoAuthHandlerFound( |
| 305 'No handler was ready to authenticate. %d handlers were checked.' | 310 'No handler was ready to authenticate. %d handlers were checked.' |
| 306 ' %s ' % (len(names), str(names))) | 311 ' %s ' |
| 312 'Check your credentials' % (len(names), str(names))) |
| 307 | 313 |
| 308 if len(ready_handlers) > 1: | 314 if len(ready_handlers) > 1: |
| 309 # NOTE: Even though it would be nice to accept more than one handler | 315 # NOTE: Even though it would be nice to accept more than one handler |
| 310 # by using one of the many ready handlers, we are never sure that each | 316 # by using one of the many ready handlers, we are never sure that each |
| 311 # of them are referring to the same storage account. Since we cannot | 317 # of them are referring to the same storage account. Since we cannot |
| 312 # easily guarantee that, it is always safe to fail, rather than operate | 318 # easily guarantee that, it is always safe to fail, rather than operate |
| 313 # on the wrong account. | 319 # on the wrong account. |
| 314 names = [handler.__class__.__name__ for handler in ready_handlers] | 320 names = [handler.__class__.__name__ for handler in ready_handlers] |
| 315 raise boto.exception.TooManyAuthHandlerReadyToAuthenticate( | 321 raise boto.exception.TooManyAuthHandlerReadyToAuthenticate( |
| 316 '%d AuthHandlers ready to authenticate, ' | 322 '%d AuthHandlers %s ready to authenticate for requested_capabilit
y ' |
| 317 'only 1 expected: %s' % (len(names), str(names))) | 323 '%s, only 1 expected. This happens if you import multiple ' |
| 324 'pluging.Plugin implementations that declare support for the ' |
| 325 'requested_capability.' % (len(names), str(names), |
| 326 requested_capability)) |
| 318 | 327 |
| 319 return ready_handlers[0] | 328 return ready_handlers[0] |
| OLD | NEW |