1 package s3
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 import (
26 "crypto/hmac"
27 "crypto/sha1"
28 "encoding/base64"
29 "net/http"
30 "net/url"
31 "sort"
32 "strings"
33 "time"
34
35 "github.com/aws/aws-sdk-go/aws/corehandlers"
36 "github.com/aws/aws-sdk-go/aws/credentials"
37 "github.com/aws/aws-sdk-go/aws/request"
38 "github.com/aws/aws-sdk-go/service/s3"
39 log "github.com/sirupsen/logrus"
40 )
41
42 type signer struct {
43
44 Request *http.Request
45 Time time.Time
46 Credentials *credentials.Credentials
47 Query url.Values
48 stringToSign string
49 signature string
50 }
51
52 var s3ParamsToSign = map[string]bool{
53 "acl": true,
54 "location": true,
55 "logging": true,
56 "notification": true,
57 "partNumber": true,
58 "policy": true,
59 "requestPayment": true,
60 "torrent": true,
61 "uploadId": true,
62 "uploads": true,
63 "versionId": true,
64 "versioning": true,
65 "versions": true,
66 "response-content-type": true,
67 "response-content-language": true,
68 "response-expires": true,
69 "response-cache-control": true,
70 "response-content-disposition": true,
71 "response-content-encoding": true,
72 "website": true,
73 "delete": true,
74 }
75
76
77 func setv2Handlers(svc *s3.S3) {
78 svc.Handlers.Build.PushBack(func(r *request.Request) {
79 parsedURL, err := url.Parse(r.HTTPRequest.URL.String())
80 if err != nil {
81 log.Fatalf("Failed to parse URL: %v", err)
82 }
83 r.HTTPRequest.URL.Opaque = parsedURL.Path
84 })
85
86 svc.Handlers.Sign.Clear()
87 svc.Handlers.Sign.PushBack(Sign)
88 svc.Handlers.Sign.PushBackNamed(corehandlers.BuildContentLengthHandler)
89 }
90
91
92
93
94
95
96 func Sign(req *request.Request) {
97
98
99 if req.Config.Credentials == credentials.AnonymousCredentials {
100 return
101 }
102
103 v2 := signer{
104 Request: req.HTTPRequest,
105 Time: req.Time,
106 Credentials: req.Config.Credentials,
107 }
108 v2.Sign()
109 }
110
111 func (v2 *signer) Sign() error {
112 credValue, err := v2.Credentials.Get()
113 if err != nil {
114 return err
115 }
116 accessKey := credValue.AccessKeyID
117 var (
118 md5, ctype, date, xamz string
119 xamzDate bool
120 sarray []string
121 smap map[string]string
122 sharray []string
123 )
124
125 headers := v2.Request.Header
126 params := v2.Request.URL.Query()
127 parsedURL, err := url.Parse(v2.Request.URL.String())
128 if err != nil {
129 return err
130 }
131 host, canonicalPath := parsedURL.Host, parsedURL.Path
132 v2.Request.Header["Host"] = []string{host}
133 v2.Request.Header["date"] = []string{v2.Time.In(time.UTC).Format(time.RFC1123)}
134 if credValue.SessionToken != "" {
135 v2.Request.Header["x-amz-security-token"] = []string{credValue.SessionToken}
136 }
137
138 smap = make(map[string]string)
139 for k, v := range headers {
140 k = strings.ToLower(k)
141 switch k {
142 case "content-md5":
143 md5 = v[0]
144 case "content-type":
145 ctype = v[0]
146 case "date":
147 if !xamzDate {
148 date = v[0]
149 }
150 default:
151 if strings.HasPrefix(k, "x-amz-") {
152 vall := strings.Join(v, ",")
153 smap[k] = k + ":" + vall
154 if k == "x-amz-date" {
155 xamzDate = true
156 date = ""
157 }
158 sharray = append(sharray, k)
159 }
160 }
161 }
162 if len(sharray) > 0 {
163 sort.StringSlice(sharray).Sort()
164 for _, h := range sharray {
165 sarray = append(sarray, smap[h])
166 }
167 xamz = strings.Join(sarray, "\n") + "\n"
168 }
169
170 expires := false
171 if v, ok := params["Expires"]; ok {
172 expires = true
173 date = v[0]
174 params["AWSAccessKeyId"] = []string{accessKey}
175 }
176
177 sarray = sarray[0:0]
178 for k, v := range params {
179 if s3ParamsToSign[k] {
180 for _, vi := range v {
181 if vi == "" {
182 sarray = append(sarray, k)
183 } else {
184 sarray = append(sarray, k+"="+vi)
185 }
186 }
187 }
188 }
189 if len(sarray) > 0 {
190 sort.StringSlice(sarray).Sort()
191 canonicalPath = canonicalPath + "?" + strings.Join(sarray, "&")
192 }
193
194 v2.stringToSign = strings.Join([]string{
195 v2.Request.Method,
196 md5,
197 ctype,
198 date,
199 xamz + canonicalPath,
200 }, "\n")
201 hash := hmac.New(sha1.New, []byte(credValue.SecretAccessKey))
202 hash.Write([]byte(v2.stringToSign))
203 v2.signature = base64.StdEncoding.EncodeToString(hash.Sum(nil))
204
205 if expires {
206 params["Signature"] = []string{v2.signature}
207 } else {
208 headers["Authorization"] = []string{"AWS " + accessKey + ":" + v2.signature}
209 }
210
211 log.WithFields(log.Fields{
212 "string-to-sign": v2.stringToSign,
213 "signature": v2.signature,
214 }).Debugln("request signature")
215 return nil
216 }
217
View as plain text