<commit_before>package main

import (
	"bytes"
	"fmt"
	"math/rand"
	"os"
	"strings"
	"text/template"
	"time"
)

const (
	authRealm       = "cow proxy"
	authRawBodyTmpl = `<!DOCTYPE html>
<html>
	<head> <title>COW Proxy</title> </head>
	<body>
		<h1>407 Proxy authentication required</h1>
		<hr />
		Generated by <i>COW</i>
	</body>
</html>
`
)

var auth struct {
	user   string
	passwd string

	ha1 string // used in request digest

	required bool
	authed   *TimeoutSet // cache authentication based on client i

	template *template.Template
}

func initAuth() {
	if config.UserPasswd == "" {
		return
	}

	// userPasswd validated in config/cmdline parsing
	arr := strings.SplitN(config.UserPasswd, ":", 2)
	auth.user, auth.passwd = arr[0], arr[1]

	debug.Println("requires auth")
	auth.required = true
	auth.authed = NewTimeoutSet(time.Duration(config.AuthTimeout) * time.Hour)

	auth.ha1 = md5sum(auth.user + ":" + authRealm + ":" + auth.passwd)

	rand.Seed(time.Now().Unix())

	var err error
	rawTemplate := "HTTP/1.1 407 Proxy Authentication Required\r\n" +
		"Proxy-Authenticate: Digest realm=\"" + authRealm + "\", nonce=\"{{.Nonce}}\", qop=\"auth\"\r\n" +
		"Content-Type: text/html\r\n" +
		"Cache-Control: no-cache\r\n" +
		"Content-Length: " + fmt.Sprintf("%d", len(authRawBodyTmpl)) + "\r\n\r\n" + authRawBodyTmpl
	auth.template, err = template.New("auth").Parse(rawTemplate)
	if err != nil {
		errl.Println("Internal error generating auth template:", err)
		os.Exit(1)
	}
}

// Return err = nil if authentication succeed. nonce would be not empty if
// authentication is needed, and should be passed back on subsequent call.
func authenticate(conn *clientConn, r *Request, nonce string) (nc string, err error) {
	clientIP, _ := splitHostPort(conn.RemoteAddr().String())
	if auth.authed.has(clientIP) {
		debug.Printf("%s has already authed\n", clientIP)
		return
	}
	nc, err = authUserPasswd(conn, r, nonce)
	if err == nil {
		auth.authed.add(clientIP)
	}
	return
}

func genNonce() string {
	buf := new(bytes.Buffer)
	fmt.Fprintf(buf, "%x", time.Now().Unix())
	fmt.Fprintf(buf, "%x", rand.Int63())
	return buf.String()
}

func calcRequestDigest(kv map[string]string, ha1, method string) string {
	// Refer to rfc2617 section 3.2.2.1 Request-Digest
	buf := bytes.NewBufferString(ha1)
	buf.WriteByte(':')
	buf.WriteString(kv["nonce"])
	buf.WriteByte(':')
	buf.WriteString(kv["nc"])
	buf.WriteByte(':')
	buf.WriteString(kv["cnonce"])
	buf.WriteByte(':')
	buf.WriteString("auth") // qop value
	buf.WriteByte(':')
	buf.WriteString(md5sum(method + ":" + kv["uri"]))

	return md5sum(buf.String())
}

func checkProxyAuthorization(r *Request, nonce string) error {
	errl.Println("authorization:", r.ProxyAuthorization)
	arr := strings.SplitN(r.ProxyAuthorization, " ", 2)
	if len(arr) != 2 {
		errl.Println("auth: malformed ProxyAuthorization header:", r.ProxyAuthorization)
		return errBadRequest
	}
	if strings.ToLower(strings.TrimSpace(arr[0])) != "digest" {
		errl.Println("auth: client using unsupported authenticate method:", arr[0])
		return errBadRequest
	}
	authHeader := parseKeyValueList(arr[1])
	if len(authHeader) == 0 {
		errl.Println("auth: empty authorization list")
		return errBadRequest
	}
	if authHeader["nonce"] != nonce || authHeader["username"] != auth.user {
		errl.Println("auth: nonce or username mismatch:", nonce, authHeader["username"])
		return errAuthRequired
	}
	if authHeader["qop"] != "auth" {
		errl.Println("auth: qop wrong:", authHeader["qop"])
		return errBadRequest
	}
	response, ok := authHeader["response"]
	if !ok {
		errl.Println("auth: no request-digest")
		return errBadRequest
	}

	digest := calcRequestDigest(authHeader, auth.ha1, r.Method)
	if response == digest {
		return nil
	}
	errl.Println("auth: digest not match, maybe password wrong")
	return errAuthRequired
}

func authUserPasswd(conn *clientConn, r *Request, nonce string) (string, error) {
	if r.ProxyAuthorization != "" {
		// client has sent authorization header
		err := checkProxyAuthorization(r, nonce)
		if err == nil {
			return "", nil
		} else if err != errAuthRequired {
			sendErrorPage(conn, errCodeBadReq, "Bad authorization request", "")
			return nonce, err
		}
	}

	if nonce == "" {
		nonce = genNonce()
	}
	data := struct {
		Nonce string
	}{
		nonce,
	}
	buf := new(bytes.Buffer)
	if err := auth.template.Execute(buf, data); err != nil {
		errl.Println("Error generating auth response:", err)
		return "", errInternal
	}
	debug.Println("auth response:")
	debug.Printf(buf.String())
	if _, err := conn.Write(buf.Bytes()); err != nil {
		errl.Println("Sending auth response error:", err)
		return "", errShouldClose
	}
	return nonce, errAuthRequired
}<commit_msg>Generate new nonce on each authorization request.<commit_after>package main

import (
	"bytes"
	"fmt"
	"math/rand"
	"os"
	"strings"
	"text/template"
	"time"
)

const (
	authRealm       = "cow proxy"
	authRawBodyTmpl = `<!DOCTYPE html>
<html>
	<head> <title>COW Proxy</title> </head>
	<body>
		<h1>407 Proxy authentication required</h1>
		<hr />
		Generated by <i>COW</i>
	</body>
</html>
`
)

var auth struct {
	user   string
	passwd string

	ha1 string // used in request digest

	required bool
	authed   *TimeoutSet // cache authentication based on client i

	template *template.Template
}

func initAuth() {
	if config.UserPasswd == "" {
		return
	}

	// userPasswd validated in config/cmdline parsing
	arr := strings.SplitN(config.UserPasswd, ":", 2)
	auth.user, auth.passwd = arr[0], arr[1]

	debug.Println("requires auth")
	auth.required = true
	auth.authed = NewTimeoutSet(time.Duration(config.AuthTimeout) * time.Hour)

	auth.ha1 = md5sum(auth.user + ":" + authRealm + ":" + auth.passwd)

	rand.Seed(time.Now().Unix())

	var err error
	rawTemplate := "HTTP/1.1 407 Proxy Authentication Required\r\n" +
		"Proxy-Authenticate: Digest realm=\"" + authRealm + "\", nonce=\"{{.Nonce}}\", qop=\"auth\"\r\n" +
		"Content-Type: text/html\r\n" +
		"Cache-Control: no-cache\r\n" +
		"Content-Length: " + fmt.Sprintf("%d", len(authRawBodyTmpl)) + "\r\n\r\n" + authRawBodyTmpl
	auth.template, err = template.New("auth").Parse(rawTemplate)
	if err != nil {
		errl.Println("Internal error generating auth template:", err)
		os.Exit(1)
	}
}

// Return err = nil if authentication succeed. nonce would be not empty if
// authentication is needed, and should be passed back on subsequent call.
func authenticate(conn *clientConn, r *Request, nonce string) (nc string, err error) {
	clientIP, _ := splitHostPort(conn.RemoteAddr().String())
	if auth.authed.has(clientIP) {
		debug.Printf("%s has already authed\n", clientIP)
		return
	}
	nc, err = authUserPasswd(conn, r, nonce)
	if err == nil {
		auth.authed.add(clientIP)
	}
	return
}

func genNonce() string {
	buf := new(bytes.Buffer)
	fmt.Fprintf(buf, "%x", time.Now().Unix())
	fmt.Fprintf(buf, "%x", rand.Int63())
	return buf.String()
}

func calcRequestDigest(kv map[string]string, ha1, method string) string {
	// Refer to rfc2617 section 3.2.2.1 Request-Digest
	buf := bytes.NewBufferString(ha1)
	buf.WriteByte(':')
	buf.WriteString(kv["nonce"])
	buf.WriteByte(':')
	buf.WriteString(kv["nc"])
	buf.WriteByte(':')
	buf.WriteString(kv["cnonce"])
	buf.WriteByte(':')
	buf.WriteString("auth") // qop value
	buf.WriteByte(':')
	buf.WriteString(md5sum(method + ":" + kv["uri"]))

	return md5sum(buf.String())
}

func checkProxyAuthorization(r *Request, nonce string) error {
	errl.Println("authorization:", r.ProxyAuthorization)
	arr := strings.SplitN(r.ProxyAuthorization, " ", 2)
	if len(arr) != 2 {
		errl.Println("auth: malformed ProxyAuthorization header:", r.ProxyAuthorization)
		return errBadRequest
	}
	if strings.ToLower(strings.TrimSpace(arr[0])) != "digest" {
		errl.Println("auth: client using unsupported authenticate method:", arr[0])
		return errBadRequest
	}
	authHeader := parseKeyValueList(arr[1])
	if len(authHeader) == 0 {
		errl.Println("auth: empty authorization list")
		return errBadRequest
	}
	if authHeader["nonce"] != nonce || authHeader["username"] != auth.user {
		errl.Println("auth: nonce or username mismatch:", nonce, authHeader["username"])
		return errAuthRequired
	}
	if authHeader["qop"] != "auth" {
		errl.Println("auth: qop wrong:", authHeader["qop"])
		return errBadRequest
	}
	response, ok := authHeader["response"]
	if !ok {
		errl.Println("auth: no request-digest")
		return errBadRequest
	}

	digest := calcRequestDigest(authHeader, auth.ha1, r.Method)
	if response == digest {
		return nil
	}
	errl.Println("auth: digest not match, maybe password wrong")
	return errAuthRequired
}

func authUserPasswd(conn *clientConn, r *Request, nonce string) (string, error) {
	if r.ProxyAuthorization != "" {
		// client has sent authorization header
		err := checkProxyAuthorization(r, nonce)
		if err == nil {
			return "", nil
		} else if err != errAuthRequired {
			sendErrorPage(conn, errCodeBadReq, "Bad authorization request", "")
			return nonce, err
		}
	}

	nonce = genNonce()
	data := struct {
		Nonce string
	}{
		nonce,
	}
	buf := new(bytes.Buffer)
	if err := auth.template.Execute(buf, data); err != nil {
		errl.Println("Error generating auth response:", err)
		return "", errInternal
	}
	debug.Printf("authorization response:", buf.String())
	if _, err := conn.Write(buf.Bytes()); err != nil {
		errl.Println("Sending auth response error:", err)
		return "", errShouldClose
	}
	return nonce, errAuthRequired
}
{'fraction_non_alphanumeric': 0.15701511603150947, 'fraction_numerical': 0.008090270385352353, 'mean_word_length': 4.353276353276353, 'pattern_counts': {'":': 8, '<': 31, '<?xml version=': 0, '>': 31, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 2, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5458556', 'n_tokens_mistral': 3468, 'n_tokens_neox': 3311, 'n_words': 792}