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 |