proxy: update vendor
Signed-off-by: Evan Hazlett <ejhazlett@gmail.com>
This commit is contained in:
parent
7574b334cc
commit
15619b08f8
592 changed files with 148411 additions and 20678 deletions
65
vendor/github.com/jimstudt/http-authentication/basic/README.md
generated
vendored
Normal file
65
vendor/github.com/jimstudt/http-authentication/basic/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
# auth/htpasswd [](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)
|
||||
~~~
|
||||
|
||||
|
||||
|
||||
15
vendor/github.com/jimstudt/http-authentication/basic/bcrypt.go
generated
vendored
Normal file
15
vendor/github.com/jimstudt/http-authentication/basic/bcrypt.go
generated
vendored
Normal 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
|
||||
}
|
||||
208
vendor/github.com/jimstudt/http-authentication/basic/htpasswd.go
generated
vendored
Normal file
208
vendor/github.com/jimstudt/http-authentication/basic/htpasswd.go
generated
vendored
Normal 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)
|
||||
}
|
||||
143
vendor/github.com/jimstudt/http-authentication/basic/md5.go
generated
vendored
Normal file
143
vendor/github.com/jimstudt/http-authentication/basic/md5.go
generated
vendored
Normal 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)
|
||||
}
|
||||
28
vendor/github.com/jimstudt/http-authentication/basic/plain.go
generated
vendored
Normal file
28
vendor/github.com/jimstudt/http-authentication/basic/plain.go
generated
vendored
Normal 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)
|
||||
}
|
||||
43
vendor/github.com/jimstudt/http-authentication/basic/sha.go
generated
vendored
Normal file
43
vendor/github.com/jimstudt/http-authentication/basic/sha.go
generated
vendored
Normal 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
|
||||
}
|
||||
18
vendor/github.com/jimstudt/http-authentication/basic/util.go
generated
vendored
Normal file
18
vendor/github.com/jimstudt/http-authentication/basic/util.go
generated
vendored
Normal 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
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue