diff --git a/examples/auth.go b/examples/auth.go index cc54b5b..c32e398 100644 --- a/examples/auth.go +++ b/examples/auth.go @@ -3,9 +3,11 @@ package main import ( + "crypto/tls" "crypto/x509" "fmt" "log" + "time" "git.sr.ht/~adnano/go-gemini" ) @@ -44,6 +46,12 @@ func main() { if err := server.Certificates.Load("/var/lib/gemini/certs"); err != nil { log.Fatal(err) } + server.CreateCertificate = func(hostname string) (tls.Certificate, error) { + return gemini.CreateCertificate(gemini.CertificateOptions{ + DNSNames: []string{hostname}, + Duration: time.Hour, + }) + } server.Register("localhost", &mux) if err := server.ListenAndServe(); err != nil { @@ -58,8 +66,7 @@ func getSession(cert *x509.Certificate) (*session, bool) { } func login(w *gemini.ResponseWriter, r *gemini.Request) { - cert := gemini.Certificate(r) - if cert == nil { + if r.Certificate == nil { w.WriteStatus(gemini.StatusCertificateRequired) return } @@ -68,7 +75,7 @@ func login(w *gemini.ResponseWriter, r *gemini.Request) { w.WriteHeader(gemini.StatusInput, "Username") return } - fingerprint := gemini.Fingerprint(cert) + fingerprint := gemini.Fingerprint(r.Certificate.Leaf) sessions[fingerprint] = &session{ username: username, } @@ -76,12 +83,11 @@ func login(w *gemini.ResponseWriter, r *gemini.Request) { } func loginPassword(w *gemini.ResponseWriter, r *gemini.Request) { - cert := gemini.Certificate(r) - if cert == nil { + if r.Certificate == nil { w.WriteStatus(gemini.StatusCertificateRequired) return } - session, ok := getSession(cert) + session, ok := getSession(r.Certificate.Leaf) if !ok { w.WriteStatus(gemini.StatusCertificateNotAuthorized) return @@ -102,23 +108,21 @@ func loginPassword(w *gemini.ResponseWriter, r *gemini.Request) { } func logout(w *gemini.ResponseWriter, r *gemini.Request) { - cert := gemini.Certificate(r) - if cert == nil { + if r.Certificate == nil { w.WriteStatus(gemini.StatusCertificateRequired) return } - fingerprint := gemini.Fingerprint(cert) + fingerprint := gemini.Fingerprint(r.Certificate.Leaf) delete(sessions, fingerprint) fmt.Fprintln(w, "Successfully logged out.") } func profile(w *gemini.ResponseWriter, r *gemini.Request) { - cert := gemini.Certificate(r) - if cert == nil { + if r.Certificate == nil { w.WriteStatus(gemini.StatusCertificateRequired) return } - session, ok := getSession(cert) + session, ok := getSession(r.Certificate.Leaf) if !ok { w.WriteStatus(gemini.StatusCertificateNotAuthorized) return @@ -130,12 +134,11 @@ func profile(w *gemini.ResponseWriter, r *gemini.Request) { } func admin(w *gemini.ResponseWriter, r *gemini.Request) { - cert := gemini.Certificate(r) - if cert == nil { + if r.Certificate == nil { w.WriteStatus(gemini.StatusCertificateRequired) return } - session, ok := getSession(cert) + session, ok := getSession(r.Certificate.Leaf) if !ok { w.WriteStatus(gemini.StatusCertificateNotAuthorized) return diff --git a/request.go b/request.go index 971d774..f55f569 100644 --- a/request.go +++ b/request.go @@ -19,7 +19,9 @@ type Request struct { // Certificate specifies the TLS certificate to use for the request. // Request certificates take precedence over client certificates. - // This field is ignored by the server. + // + // On the server side, if the client provided a certificate then + // Certificate.Leaf is guaranteed to be non-nil. Certificate *tls.Certificate // RemoteAddr allows servers and other software to record the network diff --git a/server.go b/server.go index c95e488..45555fc 100644 --- a/server.go +++ b/server.go @@ -3,7 +3,6 @@ package gemini import ( "bufio" "crypto/tls" - "crypto/x509" "log" "net" "net/url" @@ -199,10 +198,24 @@ func (s *Server) respond(conn net.Conn) { if url.Scheme == "" { url.Scheme = "gemini" } + + // Store information about the TLS connection + connState := conn.(*tls.Conn).ConnectionState() + var cert *tls.Certificate + if len(connState.PeerCertificates) > 0 { + peerCert := connState.PeerCertificates[0] + // Store the TLS certificate + cert = &tls.Certificate{ + Certificate: [][]byte{peerCert.Raw}, + Leaf: peerCert, + } + } + req := &Request{ - URL: url, - RemoteAddr: conn.RemoteAddr(), - TLS: conn.(*tls.Conn).ConnectionState(), + URL: url, + RemoteAddr: conn.RemoteAddr(), + TLS: connState, + Certificate: cert, } resp := s.responder(req) if resp != nil { @@ -330,22 +343,3 @@ func Input(r *Request) (query string, ok bool) { } return "", false } - -// Certificate returns the request certificate. -// It returns nil if no certificate was provided. -// -// Example: -// -// cert := gemini.Certificate(req) -// if cert == nil { -// w.WriteStatus(gemini.StatusCertificateRequired) -// return -// } -// // ... -// -func Certificate(r *Request) *x509.Certificate { - if len(r.TLS.PeerCertificates) == 0 { - return nil - } - return r.TLS.PeerCertificates[0] -}