github.com/lalkh/containerd@v1.4.3/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  }