auth.go 4.6 KB

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