From d0cb5e2318d1859f2dc9027151b4e4f1c973c6a1 Mon Sep 17 00:00:00 2001 From: Max Resnick Date: Sat, 29 Oct 2022 08:41:15 -0700 Subject: initial commit --- main.go | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 main.go (limited to 'main.go') diff --git a/main.go b/main.go new file mode 100644 index 0000000..0db6ee1 --- /dev/null +++ b/main.go @@ -0,0 +1,126 @@ +package main + +import ( + "crypto/rand" + "encoding/base64" + "encoding/csv" + "flag" + "fmt" + "log" + "math/big" + "net/http" + "net/http/cgi" + "os" + + "golang.org/x/crypto/bcrypt" +) + +var ( + reposDir = flag.String("r", ".", "Directory containing git repositories") + backendCommand = flag.String("c", "git http-backend", "CGI binary to execute") + addr = flag.String("l", ":8080", "Address/port to listen on") + newToken = flag.Bool("t", false, "make a new token") + authMap = map[string]string{} +) + +func LoadTokens() (map[string]string, error) { + tokenMap := make(map[string]string) + contents, err := os.Open("tokens.csv") + if err != nil { + fmt.Println("File reading error", err) + return tokenMap, err + } + defer contents.Close() + r := csv.NewReader(contents) + tokens, err := r.ReadAll() + if err != nil { + fmt.Println("File reading error", err) + return tokenMap, err + } + for _, acctToken := range tokens { + acct, hash := acctToken[0], acctToken[1] + tokenMap[acct] = hash + } + return tokenMap, err +} + +func NewToken() (string, string, error) { + tokenBytes := make([]byte, 28) + for i := range tokenBytes { + max := big.NewInt(int64(255)) + randInt, err := rand.Int(rand.Reader, max) + if err != nil { + return "", "", err + } + tokenBytes[i] = uint8(randInt.Int64()) + } + hashBytes, err := bcrypt.GenerateFromPassword(tokenBytes, bcrypt.DefaultCost) + if err != nil { + return "", "", err + } + token := base64.URLEncoding.EncodeToString(tokenBytes) + hash := string(hashBytes) + return token, hash, nil +} + +type Handler struct { + cgiHandler *cgi.Handler +} + +func NewHandler(reposDir, backendCommand string) *Handler { + return &Handler{ + &cgi.Handler{ + Path: "/bin/sh", + Args: []string{"-c", backendCommand}, + Dir: ".", + Env: []string{ + fmt.Sprintf("GIT_PROJECT_ROOT=%v", reposDir), + "GIT_HTTP_EXPORT_ALL=1", + }, + }, + } +} + +func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) { + u, p, ok := req.BasicAuth() + if !ok { + rw.Header().Set("WWW-Authenticate", `Basic realm="git"`) + http.Error(rw, "Authentication Required", 401) + return + } + hash, ok := authMap[fmt.Sprintf("user:%s", u)] + if !ok { + http.Error(rw, "Bad Request", 400) + return + } + token, err := base64.URLEncoding.DecodeString(p) + if err != nil { + http.Error(rw, "Bad Request", 400) + return + } + if err := bcrypt.CompareHashAndPassword([]byte(hash), token); err != nil { + http.Error(rw, "Bad Request", 400) + return + } + h.cgiHandler.ServeHTTP(rw, req) +} + +func main() { + flag.Parse() + if *newToken { + token, hash, err := NewToken() + if err != nil { + log.Fatal(err) + } + fmt.Printf("token: %s\nhash: %s\n", token, hash) + return + } + tokens, err := LoadTokens() + fmt.Println(tokens) + if err != nil { + log.Fatal(err) + } + authMap = tokens + http.Handle("/", NewHandler(*reposDir, *backendCommand)) + log.Fatal(http.ListenAndServe(":8080", nil)) +} -- cgit v1.2.3