@@ -2,7 +2,10 @@ package gohpts
22
33import (
44 "context"
5+ "crypto/sha256"
6+ "crypto/subtle"
57 "crypto/tls"
8+ "encoding/base64"
69 "errors"
710 "fmt"
811 "io"
@@ -106,6 +109,8 @@ type proxyApp struct {
106109 certFile string
107110 keyFile string
108111 httpServerAddr string
112+ user string
113+ pass string
109114 proxychain Chain
110115 proxylist []proxyEntry
111116 rrIndex uint32
@@ -499,6 +504,50 @@ func (p *proxyApp) transfer(wg *sync.WaitGroup, destination io.Writer, source io
499504 p .logger .Debug ().Msgf ("copied %s from %s to %s" , written , srcName , destName )
500505}
501506
507+ func parseProxyAuth (auth string ) (username , password string , ok bool ) {
508+ if auth == "" {
509+ return "" , "" , false
510+ }
511+ const prefix = "Basic "
512+ if len (auth ) < len (prefix ) || strings .ToLower (prefix ) != strings .ToLower (auth [:len (prefix )]) {
513+ return "" , "" , false
514+ }
515+ c , err := base64 .StdEncoding .DecodeString (auth [len (prefix ):])
516+ if err != nil {
517+ return "" , "" , false
518+ }
519+ cs := string (c )
520+ username , password , ok = strings .Cut (cs , ":" )
521+ if ! ok {
522+ return "" , "" , false
523+ }
524+ return username , password , true
525+ }
526+
527+ func (p * proxyApp ) proxyAuth (next http.HandlerFunc ) http.HandlerFunc {
528+ return http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
529+ auth := r .Header .Get ("Proxy-Authorization" )
530+ r .Header .Del ("Proxy-Authorization" )
531+ username , password , ok := parseProxyAuth (auth )
532+ if ok {
533+ usernameHash := sha256 .Sum256 ([]byte (username ))
534+ passwordHash := sha256 .Sum256 ([]byte (password ))
535+ expectedUsernameHash := sha256 .Sum256 ([]byte (p .user ))
536+ expectedPasswordHash := sha256 .Sum256 ([]byte (p .pass ))
537+
538+ usernameMatch := (subtle .ConstantTimeCompare (usernameHash [:], expectedUsernameHash [:]) == 1 )
539+ passwordMatch := (subtle .ConstantTimeCompare (passwordHash [:], expectedPasswordHash [:]) == 1 )
540+
541+ if usernameMatch && passwordMatch {
542+ next .ServeHTTP (w , r )
543+ return
544+ }
545+ }
546+ w .Header ().Set ("Proxy-Authenticate" , `Basic realm="restricted", charset="UTF-8"` )
547+ http .Error (w , "Proxy Authentication Required" , http .StatusProxyAuthRequired )
548+ })
549+ }
550+
502551func (p * proxyApp ) handler () http.HandlerFunc {
503552 return func (w http.ResponseWriter , r * http.Request ) {
504553 if r .Method == http .MethodConnect {
@@ -535,7 +584,11 @@ func (p *proxyApp) Run() {
535584 }
536585 close (done )
537586 }()
538- p .httpServer .Handler = p .handler ()
587+ if p .user != "" && p .pass != "" {
588+ p .httpServer .Handler = p .proxyAuth (p .handler ())
589+ } else {
590+ p .httpServer .Handler = p .handler ()
591+ }
539592 if p .certFile != "" && p .keyFile != "" {
540593 if err := p .httpServer .ListenAndServeTLS (p .certFile , p .keyFile ); err != nil && err != http .ErrServerClosed {
541594 p .logger .Fatal ().Err (err ).Msg ("Unable to start HTTPS server" )
@@ -668,6 +721,8 @@ func New(conf *Config) *proxyApp {
668721 p .httpServerAddr = addrHTTP
669722 certFile = expandPath (sconf .Server .CertFile )
670723 keyFile = expandPath (sconf .Server .KeyFile )
724+ p .user = sconf .Server .Username
725+ p .pass = sconf .Server .Password
671726 p .proxychain = sconf .Chain
672727 p .proxylist = sconf .ProxyList
673728 p .availProxyList = make ([]proxyEntry , 0 , len (p .proxylist ))
@@ -696,6 +751,8 @@ func New(conf *Config) *proxyApp {
696751 p .httpServerAddr = addrHTTP
697752 certFile = expandPath (conf .CertFile )
698753 keyFile = expandPath (conf .KeyFile )
754+ p .user = conf .ServerUser
755+ p .pass = conf .ServerPass
699756 auth := proxy.Auth {
700757 User : conf .User ,
701758 Password : conf .Pass ,
0 commit comments