github.com/demonoid81/containerd@v1.3.4/remotes/docker/auth.go (about) 1 /* 2 Copyright The containerd Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package docker 18 19 import ( 20 "net/http" 21 "sort" 22 "strings" 23 ) 24 25 type authenticationScheme byte 26 27 const ( 28 basicAuth authenticationScheme = 1 << iota // Defined in RFC 7617 29 digestAuth // Defined in RFC 7616 30 bearerAuth // Defined in RFC 6750 31 ) 32 33 // challenge carries information from a WWW-Authenticate response header. 34 // See RFC 2617. 35 type challenge struct { 36 // scheme is the auth-scheme according to RFC 2617 37 scheme authenticationScheme 38 39 // parameters are the auth-params according to RFC 2617 40 parameters map[string]string 41 } 42 43 type byScheme []challenge 44 45 func (bs byScheme) Len() int { return len(bs) } 46 func (bs byScheme) Swap(i, j int) { bs[i], bs[j] = bs[j], bs[i] } 47 48 // Sort in priority order: token > digest > basic 49 func (bs byScheme) Less(i, j int) bool { return bs[i].scheme > bs[j].scheme } 50 51 // Octet types from RFC 2616. 52 type octetType byte 53 54 var octetTypes [256]octetType 55 56 const ( 57 isToken octetType = 1 << iota 58 isSpace 59 ) 60 61 func init() { 62 // OCTET = <any 8-bit sequence of data> 63 // CHAR = <any US-ASCII character (octets 0 - 127)> 64 // CTL = <any US-ASCII control character (octets 0 - 31) and DEL (127)> 65 // CR = <US-ASCII CR, carriage return (13)> 66 // LF = <US-ASCII LF, linefeed (10)> 67 // SP = <US-ASCII SP, space (32)> 68 // HT = <US-ASCII HT, horizontal-tab (9)> 69 // <"> = <US-ASCII double-quote mark (34)> 70 // CRLF = CR LF 71 // LWS = [CRLF] 1*( SP | HT ) 72 // TEXT = <any OCTET except CTLs, but including LWS> 73 // separators = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\" | <"> 74 // | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT 75 // token = 1*<any CHAR except CTLs or separators> 76 // qdtext = <any TEXT except <">> 77 78 for c := 0; c < 256; c++ { 79 var t octetType 80 isCtl := c <= 31 || c == 127 81 isChar := 0 <= c && c <= 127 82 isSeparator := strings.ContainsRune(" \t\"(),/:;<=>?@[]\\{}", rune(c)) 83 if strings.ContainsRune(" \t\r\n", rune(c)) { 84 t |= isSpace 85 } 86 if isChar && !isCtl && !isSeparator { 87 t |= isToken 88 } 89 octetTypes[c] = t 90 } 91 } 92 93 func parseAuthHeader(header http.Header) []challenge { 94 challenges := []challenge{} 95 for _, h := range header[http.CanonicalHeaderKey("WWW-Authenticate")] { 96 v, p := parseValueAndParams(h) 97 var s authenticationScheme 98 switch v { 99 case "basic": 100 s = basicAuth 101 case "digest": 102 s = digestAuth 103 case "bearer": 104 s = bearerAuth 105 default: 106 continue 107 } 108 challenges = append(challenges, challenge{scheme: s, parameters: p}) 109 } 110 sort.Stable(byScheme(challenges)) 111 return challenges 112 } 113 114 func parseValueAndParams(header string) (value string, params map[string]string) { 115 params = make(map[string]string) 116 value, s := expectToken(header) 117 if value == "" { 118 return 119 } 120 value = strings.ToLower(value) 121 for { 122 var pkey string 123 pkey, s = expectToken(skipSpace(s)) 124 if pkey == "" { 125 return 126 } 127 if !strings.HasPrefix(s, "=") { 128 return 129 } 130 var pvalue string 131 pvalue, s = expectTokenOrQuoted(s[1:]) 132 if pvalue == "" { 133 return 134 } 135 pkey = strings.ToLower(pkey) 136 params[pkey] = pvalue 137 s = skipSpace(s) 138 if !strings.HasPrefix(s, ",") { 139 return 140 } 141 s = s[1:] 142 } 143 } 144 145 func skipSpace(s string) (rest string) { 146 i := 0 147 for ; i < len(s); i++ { 148 if octetTypes[s[i]]&isSpace == 0 { 149 break 150 } 151 } 152 return s[i:] 153 } 154 155 func expectToken(s string) (token, rest string) { 156 i := 0 157 for ; i < len(s); i++ { 158 if octetTypes[s[i]]&isToken == 0 { 159 break 160 } 161 } 162 return s[:i], s[i:] 163 } 164 165 func expectTokenOrQuoted(s string) (value string, rest string) { 166 if !strings.HasPrefix(s, "\"") { 167 return expectToken(s) 168 } 169 s = s[1:] 170 for i := 0; i < len(s); i++ { 171 switch s[i] { 172 case '"': 173 return s[:i], s[i+1:] 174 case '\\': 175 p := make([]byte, len(s)-1) 176 j := copy(p, s[:i]) 177 escape := true 178 for i = i + 1; i < len(s); i++ { 179 b := s[i] 180 switch { 181 case escape: 182 escape = false 183 p[j] = b 184 j++ 185 case b == '\\': 186 escape = true 187 case b == '"': 188 return string(p[:j]), s[i+1:] 189 default: 190 p[j] = b 191 j++ 192 } 193 } 194 return "", "" 195 } 196 } 197 return "", "" 198 }