proxy: update vendor

Signed-off-by: Evan Hazlett <ejhazlett@gmail.com>
This commit is contained in:
Evan Hazlett 2017-07-29 22:50:48 -04:00
parent 7574b334cc
commit 15619b08f8
No known key found for this signature in database
GPG key ID: A519480096146526
592 changed files with 148411 additions and 20678 deletions

21
vendor/github.com/jimstudt/http-authentication/LICENSE generated vendored Normal file
View file

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014 Jim Studt
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,13 @@
http-authentication
===================
Go implementation of RFC 2617 HTTP Authentication: Basic and Digest Access Authentication
You will want to include one or both of the packages depending on your requirements.
~~~~ go
import "github/jimstudt/http-authentication/basic"
import "github/jimstudt/http-authentication/digest"
~~~~
The packages are documented in their own directories.

View file

@ -0,0 +1,65 @@
# auth/htpasswd [![GoDoc](https://godoc.org/github.com/jimstudt/http-authentication/basic?status.png)](http://godoc.org/github.com/jimstudt/http-authentication/basic)
Authenticate using Apache-style htpasswd files and HTTP Basic Authentication.
`htpasswd` has supported a number of different password hashing schemes over the
decades. Most common and sane ones are supported directly. For some you will need
to add another package.
| Style | Status |
|-------|--------|
| plain | yes+ |
| md5 | yes |
| sha | yes |
| crypt | no (conflicts with plain) |
| bcrypt | no (can add with another package) |
The standard set of systems will use *Plain*, *Sha*, and *MD5* systems while filtering out *bcrypt*.
Because of its complexity, *bcrypt* will be in another package which you can import and
add if you need it. *Plain* accepts both Apache style plain text and nginx style where the
password is preceded by {PLAIN}.
## Usage
~~~ go
import (
"github.com/codegangsta/martini"
"github.com/jimstudt/http-authentication/basic"
"log"
)
func main() {
m := martini.Classic()
pw,err := basic.New("My Realm", "./my-htpasswd-file", htpasswd.DefaultSystems, nil)
if ( err != nil) {
log.Fatalf("Unable to read my htpassword file: %s", err.Error())
}
// authenticate every request
m.Use( pw.ServeHTTP)
// You will also want to call pw.Reload(nil) to reprocess the password file when it changes.
// You can use pw.ReloadOn( syscall.SIGHUP, nil ) to make it automatically
// reload on a HUP signal.
// And those 'nil' arguments are where you pass a function to be notified about illegally
// formatted entries, or unsupported hash systems. See the API documents.
// If you only want to authenticate some requests, then it goes like this...
// m.Get("/secure/thing", pw.ServeHTTP, myRealHandler)
// ... if pw.ServeHTTP does the 401 then your handler will not be called
m.Run()
}
~~~
## API Documentation
The API is documented using godoc and also available at [godoc.org](http://godoc.org/github.com/jimstudt/http-authentication/basic)
~~~

View file

@ -0,0 +1,15 @@
package basic
import (
"fmt"
"strings"
)
// Reject any password encoded using bcrypt.
func RejectBcrypt(src string) (EncodedPasswd, error) {
if strings.HasPrefix(src, "$2y$") {
return nil, fmt.Errorf("bcrypt passwords are not accepted: %s", src)
}
return nil, nil
}

View file

@ -0,0 +1,208 @@
// Package htpasswd provides HTTP Basic Authentication using Apache-style htpasswd files
// for the user and password data.
//
// It supports most common hashing systems used over the decades and can be easily extended
// by the programmer to support others. (See the sha.go source file as a guide.)
//
// You will want to use something like...
// myauth := htpasswd.New("My Realm", "./my-htpasswd-file", htpasswd.DefaultSystems, nil)
// m.Use(myauth.Handler)
// ...to configure your authentication and then use the myauth.Handler as a middleware handler in your Martini stack.
// You should read about that nil, as well as Reread() too.
package basic
import (
"bufio"
"encoding/base64"
"fmt"
"net/http"
"os"
"os/signal"
"strings"
"sync"
)
// An EncodedPasswd is created from the encoded password in a password file by a PasswdParser.
//
// The password files consist of lines like "user:passwd-encoding". The user part is stripped off and
// the passwd-encoding part is captured in an EncodedPasswd.
type EncodedPasswd interface {
// Return true if the string matches the password.
// This may cache the result in the case of expensive comparison functions.
MatchesPassword(pw string) bool
}
// Examine an encoded password, and if it is formatted correctly and sane, return an
// EncodedPasswd which will recognize it.
//
// If the format is not understood, then return nil
// so that another parser may have a chance. If the format is understood but not sane,
// return an error to prevent other formats from possibly claiming it
//
// You may write and supply one of these functions to support a format (e.g. bcrypt) not
// already included in this package. Use sha.c as a template, it is simple but not too simple.
type PasswdParser func(pw string) (EncodedPasswd, error)
type passwdTable map[string]EncodedPasswd
// A BadLineHandler is used to notice bad lines in a password file. If not nil, it will be
// called for each bad line with a descriptive error. Think about what you do with these, they
// will sometimes contain hashed passwords.
type BadLineHandler func(err error)
// An HtpasswdFile encompasses an Apache-style htpasswd file for HTTP Basic authentication
type HtpasswdFile struct {
realm string
filePath string
mutex sync.Mutex
passwds passwdTable
parsers []PasswdParser
}
// An array of PasswdParser including all builtin parsers. Notice that Plain is last, since it accepts anything
var DefaultSystems []PasswdParser = []PasswdParser{AcceptMd5, AcceptSha, RejectBcrypt, AcceptPlain}
// New creates an HtpasswdFile from an Apache-style htpasswd file for HTTP Basic Authentication.
//
// The realm is presented to the user in the login dialog.
//
// The filename must exist and be accessible to the process, as well as being a valid htpasswd file.
//
// parsers is a list of functions to handle various hashing systems. In practice you will probably
// just pass htpasswd.DefaultSystems, but you could make your own to explicitly reject some formats or
// implement your own.
//
// bad is a function, which if not nil will be called for each malformed or rejected entry in
// the password file.
func New(realm string, filename string, parsers []PasswdParser, bad BadLineHandler) (*HtpasswdFile, error) {
bf := HtpasswdFile{
realm: realm,
filePath: filename,
parsers: parsers,
}
if err := bf.Reload(bad); err != nil {
return nil, err
}
return &bf, nil
}
// A Martini middleware handler to enforce HTTP Basic Auth using the policy read from the htpasswd file.
func (bf *HtpasswdFile) ServeHTTP(res http.ResponseWriter, req *http.Request) {
// if everything works, we return, otherwise we get to the
// end where we do an http.Error to stop the request
auth := req.Header.Get("Authorization")
if auth != "" {
userPassword, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(auth, "Basic "))
if err == nil {
parts := strings.SplitN(string(userPassword), ":", 2)
if len(parts) == 2 {
user := parts[0]
pw := parts[1]
bf.mutex.Lock()
matcher, ok := bf.passwds[user]
bf.mutex.Unlock()
if ok && matcher.MatchesPassword(pw) {
// we are good
return
}
}
}
}
res.Header().Set("WWW-Authenticate", "Basic realm=\""+bf.realm+"\"")
http.Error(res, "Not Authorized", http.StatusUnauthorized)
}
// Reread the password file for this HtpasswdFile.
// You will need to call this to notice any changes to the password file.
// This function is thread safe. Someone versed in fsnotify might make it
// happen automatically. Likewise you might also connect a SIGHUP handler to
// this function.
func (bf *HtpasswdFile) Reload(bad BadLineHandler) error {
// with the file...
f, err := os.Open(bf.filePath)
if err != nil {
return err
}
defer f.Close()
// ... and a new map ...
newPasswdMap := passwdTable{}
// ... for each line ...
scanner := bufio.NewScanner(f)
for scanner.Scan() {
line := scanner.Text()
// ... add it to the map, noting errors along the way
if perr := bf.addHtpasswdUser(&newPasswdMap, line); perr != nil && bad != nil {
bad(perr)
}
}
if err := scanner.Err(); err != nil {
return fmt.Errorf("Error scanning htpasswd file: %s", err.Error())
}
// .. finally, safely swap in the new map
bf.mutex.Lock()
bf.passwds = newPasswdMap
bf.mutex.Unlock()
return nil
}
// Reload the htpasswd file on a signal. If there is an error, the old data will be kept instead.
// Typically you would use syscall.SIGHUP for the value of "when"
func (bf *HtpasswdFile) ReloadOn(when os.Signal, onbad BadLineHandler) {
// this is rather common with code in digest, but I don't have a common area...
c := make(chan os.Signal, 1)
signal.Notify(c, when)
go func() {
for {
_ = <-c
bf.Reload(onbad)
}
}()
}
// Process a line from an htpasswd file and add it to the user/password map. We may
// encounter some malformed lines, this will not be an error, but we will log them if
// the caller has given us a logger.
func (bf *HtpasswdFile) addHtpasswdUser(pwmap *passwdTable, rawLine string) error {
// ignore white space lines
line := strings.TrimSpace(rawLine)
if line == "" {
return nil
}
// split "user:encoding" at colon
parts := strings.SplitN(line, ":", 2)
if len(parts) != 2 {
return fmt.Errorf("malformed line, no colon: %s", line)
}
user := parts[0]
encoding := parts[1]
// give each parser a shot. The first one to produce a matcher wins.
// If one produces an error then stop (to prevent Plain from catching it)
for _, p := range bf.parsers {
matcher, err := p(encoding)
if err != nil {
return err
}
if matcher != nil {
(*pwmap)[user] = matcher
return nil // we are done, we took to first match
}
}
// No one liked this line
return fmt.Errorf("unable to recognize password for %s in %s", user, encoding)
}

View file

@ -0,0 +1,143 @@
package basic
import (
"bytes"
"crypto/md5"
"fmt"
"strings"
)
type md5Password struct {
salt string
hashed string
}
// Accept valid MD5 encoded passwords
func AcceptMd5(src string) (EncodedPasswd, error) {
if !strings.HasPrefix(src, "$apr1$") {
return nil, nil
}
rest := strings.TrimPrefix(src, "$apr1$")
mparts := strings.SplitN(rest, "$", 2)
if len(mparts) != 2 {
return nil, fmt.Errorf("malformed md5 password: %s", src)
}
salt, hashed := mparts[0], mparts[1]
return &md5Password{salt, hashed}, nil
}
// Reject any MD5 encoded password
func RejectMd5(src string) (EncodedPasswd, error) {
if !strings.HasPrefix(src, "$apr1$") {
return nil, nil
}
return nil, fmt.Errorf("md5 password rejected: %s", src)
}
// This is the MD5 hashing function out of Apache's htpasswd program. The algorithm
// is insane, but we have to match it. Mercifully I found a PHP variant of it at
// http://stackoverflow.com/questions/2994637/how-to-edit-htpasswd-using-php
// in an answer. That reads better than the original C, and is easy to instrument.
// We will eventually go back to the original apr_md5.c for inspiration when the
// PHP gets too weird.
// The algorithm makes more sense if you imagine the original authors in a pub,
// drinking beer and rolling dice as the fundamental design process.
func apr1Md5(password string, salt string) string {
// start with a hash of password and salt
initBin := md5.Sum([]byte(password + salt + password))
// begin an initial string with hash and salt
initText := bytes.NewBufferString(password + "$apr1$" + salt)
// add crap to the string willy-nilly
for i := len(password); i > 0; i -= 16 {
lim := i
if lim > 16 {
lim = 16
}
initText.Write(initBin[0:lim])
}
// add more crap to the string willy-nilly
for i := len(password); i > 0; i >>= 1 {
if (i & 1) == 1 {
initText.WriteByte(byte(0))
} else {
initText.WriteByte(password[0])
}
}
// Begin our hashing in earnest using our initial string
bin := md5.Sum(initText.Bytes())
n := bytes.NewBuffer([]byte{})
for i := 0; i < 1000; i++ {
// prepare to make a new muddle
n.Reset()
// alternate password+crap+bin with bin+crap+password
if (i & 1) == 1 {
n.WriteString(password)
} else {
n.Write(bin[:])
}
// usually add the salt, but not always
if i%3 != 0 {
n.WriteString(salt)
}
// usually add the password but not always
if i%7 != 0 {
n.WriteString(password)
}
// the back half of that alternation
if (i & 1) == 1 {
n.Write(bin[:])
} else {
n.WriteString(password)
}
// replace bin with the md5 of this muddle
bin = md5.Sum(n.Bytes())
}
// At this point we stop transliterating the PHP code and flip back to
// reading the Apache source. The PHP uses their base64 library, but that
// uses the wrong character set so needs to be repaired afterwards and reversed
// and it is just really weird to read.
result := bytes.NewBuffer([]byte{})
// This is our own little similar-to-base64-but-not-quite filler
fill := func(a byte, b byte, c byte) {
v := (uint(a) << 16) + (uint(b) << 8) + uint(c) // take our 24 input bits
for i := 0; i < 4; i++ { // and pump out a character for each 6 bits
result.WriteByte("./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"[v&0x3f])
v >>= 6
}
}
// The order of these indices is strange, be careful
fill(bin[0], bin[6], bin[12])
fill(bin[1], bin[7], bin[13])
fill(bin[2], bin[8], bin[14])
fill(bin[3], bin[9], bin[15])
fill(bin[4], bin[10], bin[5]) // 5? Yes.
fill(0, 0, bin[11])
resultString := string(result.Bytes()[0:22]) // we wrote two extras since we only need 22.
return resultString
}
func (m *md5Password) MatchesPassword(pw string) bool {
hashed := apr1Md5(pw, m.salt)
return constantTimeEquals(hashed, m.hashed)
}

View file

@ -0,0 +1,28 @@
package basic
import (
"fmt"
)
type plainPassword struct {
password string
}
// Accept any password in the plain text encoding.
// Be careful: This matches any line, so it *must* be the last parser in you list.
func AcceptPlain(pw string) (EncodedPasswd, error) {
return &plainPassword{pw}, nil
}
// Reject any plain text encoded passoword.
// Be careful: This matches any line, so it *must* be the last parser in you list.
func RejectPlain(pw string) (EncodedPasswd, error) {
return nil, fmt.Errorf("plain password rejected: %s", pw)
}
func (p *plainPassword) MatchesPassword(pw string) bool {
// Notice: nginx prefixes plain passwords with {PLAIN}, so we see if that would
// let us match too. I'd split {PLAIN} off, but someone probably uses that
// in their password. It's a big planet.
return constantTimeEquals(pw, p.password) || constantTimeEquals("{PLAIN}"+pw, p.password)
}

View file

@ -0,0 +1,43 @@
package basic
import (
"crypto/sha1"
"crypto/subtle"
"encoding/base64"
"fmt"
"strings"
)
type shaPassword struct {
hashed []byte
}
// Accept valid SHA encoded passwords.
func AcceptSha(src string) (EncodedPasswd, error) {
if !strings.HasPrefix(src, "{SHA}") {
return nil, nil
}
b64 := strings.TrimPrefix(src, "{SHA}")
hashed, err := base64.StdEncoding.DecodeString(b64)
if err != nil {
return nil, fmt.Errorf("Malformed sha1(%s): %s", src, err.Error())
}
if len(hashed) != sha1.Size {
return nil, fmt.Errorf("Malformed sha1(%s): wrong length", src)
}
return &shaPassword{hashed}, nil
}
// Reject any password encoded as SHA.
func RejectSha(src string) (EncodedPasswd, error) {
if !strings.HasPrefix(src, "{SHA}") {
return nil, nil
}
return nil, fmt.Errorf("sha password rejected: %s", src)
}
func (s *shaPassword) MatchesPassword(pw string) bool {
h := sha1.Sum([]byte(pw))
return subtle.ConstantTimeCompare(h[:], s.hashed) == 1
}

View file

@ -0,0 +1,18 @@
package basic
import (
"crypto/sha1"
"crypto/subtle"
)
func constantTimeEquals(a string, b string) bool {
// compare SHA-1 as a gatekeeper in constant time
// then check that we didn't get by because of a collision
aSha := sha1.Sum([]byte(a))
bSha := sha1.Sum([]byte(b))
if subtle.ConstantTimeCompare(aSha[:], bSha[:]) == 1 {
// yes, this bit isn't constant, but you had to make a Sha1 collision to get here
return a == b
}
return false
}