diff --git a/config.go b/config.go index 7477c91..efef53e 100644 --- a/config.go +++ b/config.go @@ -20,6 +20,7 @@ type Config struct { MimeOverrides map[string]string CGIPaths []string SCGIPaths map[string]string + CertificateZones map[string]string DirectorySort string DirectoryReverse bool DirectoryTitles bool diff --git a/dynamic.go b/dynamic.go index e5bbc49..1627147 100644 --- a/dynamic.go +++ b/dynamic.go @@ -5,6 +5,7 @@ import ( "context" "crypto/sha256" "crypto/tls" + "crypto/x509" "encoding/hex" "io" "net" @@ -145,13 +146,17 @@ func prepareGatewayVariables(config Config, URL *url.URL, conn net.Conn) map[str clientCerts := connState.PeerCertificates if len(clientCerts) > 0 { cert := clientCerts[0] - fingerprint := sha256.Sum256(cert.Raw) - vars["TLS_CLIENT_HASH"] = hex.EncodeToString(fingerprint[:]) + vars["TLS_CLIENT_HASH"] = getCertFingerprint(cert) vars["TLS_CLIENT_ISSUER"] = cert.Issuer.String() vars["TLS_CLIENT_ISSUER_CN"] = cert.Issuer.CommonName vars["TLS_CLIENT_SUBJECT"] = cert.Subject.String() vars["TLS_CLIENT_SUBJECT_CN"] = cert.Subject.CommonName } - return vars } + +func getCertFingerprint(cert *x509.Certificate) string { + hash := sha256.Sum256(cert.Raw) + fingerprint := hex.EncodeToString(hash[:]) + return fingerprint +} diff --git a/handler.go b/handler.go index 4d25a1f..6755c3f 100644 --- a/handler.go +++ b/handler.go @@ -92,6 +92,32 @@ func handleGeminiRequest(conn net.Conn, config Config, logEntries chan LogEntry) } } + // Check whether this URL is in a certificate zone + authorised := true + for zone, allowed_fingerprint := range config.CertificateZones { + matched, err := regexp.Match(zone, []byte(URL.Path)) + if !matched || err != nil { + continue + } + authorised = false + for _, cert := range clientCerts { + if getCertFingerprint(cert) == allowed_fingerprint { + authorised = true + break + } + } + } + if !authorised { + if len(clientCerts) > 0 { + conn.Write([]byte("61 Provided certificate not authorised for this resource\r\n")) + log.Status = 61 + } else { + conn.Write([]byte("60 A pre-authorised certificate is required to access this resource\r\n")) + log.Status = 60 + } + return + } + // Check whether this URL is mapped to an SCGI app for scgi_url, scgi_socket := range config.SCGIPaths { matched, err := regexp.Match(scgi_url, []byte(URL.Path))