summaryrefslogtreecommitdiffstats
path: root/roseh.moe.go
diff options
context:
space:
mode:
Diffstat (limited to 'roseh.moe.go')
-rw-r--r--roseh.moe.go504
1 files changed, 78 insertions, 426 deletions
diff --git a/roseh.moe.go b/roseh.moe.go
index bd07aed..54b4ea6 100644
--- a/roseh.moe.go
+++ b/roseh.moe.go
@@ -19,33 +19,22 @@
package main
import (
- "archive/tar"
"bytes"
- "cmp"
"compress/gzip"
"context"
- "crypto"
- "crypto/ecdsa"
- "crypto/ed25519"
- "crypto/hkdf"
"crypto/hmac"
"crypto/rand"
- "crypto/rsa"
"crypto/sha256"
- "crypto/x509"
"embed"
"encoding/base64"
"encoding/binary"
- "encoding/json"
"encoding/xml"
- "errors"
"flag"
"fmt"
"html/template"
"io"
"io/fs"
"log"
- "mime/multipart"
"net/http"
"net/http/cgi"
"net/http/httputil"
@@ -61,7 +50,8 @@ import (
"unicode"
"github.com/skip2/go-qrcode"
- "roseh.moe/pkg/oae2"
+ "roseh.moe/pkg/roseh.moe/internal/auth"
+ "roseh.moe/pkg/roseh.moe/internal/hole"
"roseh.moe/pkg/wordlist"
)
@@ -71,13 +61,16 @@ var (
configFile = flag.String("config", "config.xml", "configuration file (XML, baby!!)")
https = flag.String("https", "", "directory containing cert.pem and key.pem, or empty string to use unencrypted http")
httpRedirect = flag.String("http-redirect", "", "port to serve HTTP redirect")
- holeTempDir = flag.String("hole-temp-dir", "/tmp/hole", "directory to store temporary files for the wormhole")
nezuko = flag.String("nezuko", "/mnt/nezuko", "nezuko partition")
cgit = flag.String("cgit", "", "directory containing cgit.cgi")
cgitConfig = flag.String("cgit-config", "", "cgit config file")
v = flag.Bool("v", true, "verbose")
)
+func init() {
+ flag.StringVar(&hole.HoleTempDir, "hole-temp-dir", "/tmp/hole", "directory to store temporary files for the wormhole")
+}
+
type urlValue struct {
URL *url.URL
}
@@ -107,44 +100,6 @@ func urlFlag(name, def, doc string) *url.URL {
return u
}
-func sigVerifier(keyBytes []byte) (func(msg, sig []byte) bool, error) {
- key, err := x509.ParsePKIXPublicKey(keyBytes)
- if err != nil {
- return nil, err
- }
- switch key := key.(type) {
- case *ecdsa.PublicKey:
- return func(msg, sig []byte) bool {
- hash := sha256.Sum256(msg)
- return ecdsa.VerifyASN1(key, hash[:], sig)
- }, nil
- case *rsa.PublicKey:
- return func(msg, sig []byte) bool {
- hash := sha256.Sum256(msg)
- return rsa.VerifyPKCS1v15(key, crypto.SHA256, hash[:], sig) == nil
- }, nil
- case ed25519.PublicKey:
- return func(msg, sig []byte) bool {
- return ed25519.Verify(key, msg, sig)
- }, nil
- default:
- return nil, fmt.Errorf("unsupported key type")
- }
-}
-
-func verifyPubKey(pubKey, msg, sig []byte) error {
- verify, err := sigVerifier(pubKey)
- if err != nil {
- return err
- }
- if !verify(msg, sig) {
- return fmt.Errorf("message failed validation")
- }
- return nil
-}
-
-var secretKey []byte
-
func loadSecrets() error {
fileBytes, err := os.ReadFile(*secretsFile)
if err != nil {
@@ -159,7 +114,7 @@ func loadSecrets() error {
if secretsConfig.SecretKey == "" {
return fmt.Errorf("%s: missing SecretKey", *secretsFile)
}
- if secretKey, err = base64.StdEncoding.DecodeString(secretsConfig.SecretKey); err != nil {
+ if auth.SecretKey, err = base64.StdEncoding.DecodeString(secretsConfig.SecretKey); err != nil {
return fmt.Errorf("%s: bad SecretKey (try base64)", *secretsFile)
}
return nil
@@ -180,15 +135,10 @@ type commandConfig struct {
Multi bool `xml:",attr"`
}
-type passkey struct {
- ID string `xml:",attr"`
- Key string `xml:",attr"`
-}
-
type serviceConfiguration struct {
PackageRedirects []packageConfig `xml:"Redirects>Package"`
CommandRedirects []commandConfig `xml:"Redirects>Command"`
- Passkey []passkey
+ Passkey []auth.Passkey
passkeys map[string][]byte
}
@@ -220,30 +170,9 @@ func loadConfig() (*serviceConfiguration, error) {
if err := xml.Unmarshal(fileBytes, config); err != nil {
return nil, err
}
- names := make(map[string]bool)
- config.passkeys = make(map[string][]byte)
- namesByID := make(map[string]string)
- for _, passkey := range config.Passkey {
- if names[passkey.ID] {
- return nil, fmt.Errorf("%s: duplicate name %q", *configFile, passkey.ID)
- }
- names[passkey.ID] = true
- keyBytes, err := base64.RawURLEncoding.DecodeString(passkey.Key)
- if err != nil {
- return nil, fmt.Errorf("%s: invalid key for passkey %q: %s", *configFile, passkey.ID, err)
- }
- if len(keyBytes) < 16 {
- return nil, fmt.Errorf("%s: invalid key for passkey %q: bad user ID: too short", *configFile, passkey.ID)
- }
- id, key := keyBytes[:16], keyBytes[16:]
- if _, err := sigVerifier(key); err != nil {
- return nil, fmt.Errorf("%s: invalid key for passkey %q: %s", *configFile, passkey.ID, err)
- }
- if _, ok := config.passkeys[string(id)]; ok {
- return nil, fmt.Errorf("%s: passkeys %q and %q have duplicate ID %d", *configFile, namesByID[string(id)], passkey.ID, id)
- }
- config.passkeys[string(id)] = key
- namesByID[string(id)] = passkey.ID
+ config.passkeys, err = auth.LoadPasskeys(config.Passkey)
+ if err != nil {
+ return nil, fmt.Errorf("%s: %s", *configFile, err)
}
slices.SortFunc(config.PackageRedirects, func(x, y packageConfig) int { return strings.Compare(x.Name, y.Name) })
slices.SortFunc(config.CommandRedirects, func(x, y commandConfig) int { return strings.Compare(x.Name, y.Name) })
@@ -409,7 +338,7 @@ func sharedFileHandler(w http.ResponseWriter, r *http.Request) {
}
func qrHandler(w http.ResponseWriter, r *http.Request) {
- text, ok := verifyHMAC(r.FormValue("t"), "qr")
+ text, ok := auth.VerifyHMAC(r.FormValue("t"), "qr")
if !ok {
http.Error(w, "Bad signature", http.StatusBadRequest)
return
@@ -433,118 +362,15 @@ type wormholeTemplateArgs struct {
Err string
}
-func makeHole() string {
- const nWords = 10
- buf := make([]byte, 2*nWords)
- rand.Read(buf)
- words := make([]string, nWords)
- for i := range words {
- words[i] = wordlist.Words[binary.NativeEndian.Uint16(buf[2*i:])&0x1fff]
- }
- return strings.Join(words, "-")
-}
-
-const blockSize = 4 * 1024 * 1024
-
-type holeFileWriter struct {
- *oae2.Writer
- f *os.File
-}
-
-func (w *holeFileWriter) Close() error {
- return cmp.Or(w.Writer.Close(), w.f.Close())
-}
-
-func writeHoleFile(filename string) (_ string, _ *holeFileWriter, err error) {
- hole := makeHole()
- primaryKey, err := hkdf.Key(sha256.New, []byte(hole), nil, "", 32)
- if err != nil {
- return "", nil, err
- }
- f, err := os.Create(fmt.Sprintf("%s/%x", *holeTempDir, primaryKey))
- if err != nil {
- return "", nil, err
- }
- defer func() {
- if err != nil {
- f.Close()
- }
- }()
- w := oae2.NewWriter(f, []byte(hole), blockSize, nil)
- buf := make([]byte, 8)
- binary.BigEndian.PutUint64(buf, uint64(len(filename)))
- if _, err := w.Write(buf); err != nil {
- return "", nil, err
- }
- if _, err := io.WriteString(w, filename); err != nil {
- return "", nil, err
- }
- return hole, &holeFileWriter{w, f}, nil
-}
-
-type offsetReader struct {
- io.ReadSeeker
- offset int
-}
-
-func (r *offsetReader) Seek(offset int64, whence int) (int64, error) {
- if whence == io.SeekStart {
- offset += int64(r.offset)
- }
- n, err := r.ReadSeeker.Seek(offset, whence)
- return n - int64(r.offset), err
-}
-
-type holeFileReader struct {
- *offsetReader
- f *os.File
-}
-
-func (r *holeFileReader) Close() error {
- return r.f.Close()
-}
-
-var errNoHole = errors.New("no such hole")
-
-func openHoleFile(hole string) (_ string, _ *holeFileReader, err error) {
- primaryKey, err := hkdf.Key(sha256.New, []byte(hole), nil, "", 32)
- if err != nil {
- return "", nil, err
- }
- f, err := os.Open(fmt.Sprintf("%s/%x", *holeTempDir, primaryKey))
- if err != nil {
- if errors.Is(err, fs.ErrNotExist) {
- return "", nil, errNoHole
- }
- return "", nil, err
- }
- defer func() {
- if err != nil {
- f.Close()
- }
- }()
- r := oae2.NewReader(f, []byte(hole), blockSize, nil)
- buf := make([]byte, 8)
- if _, err := io.ReadFull(r, buf); err != nil {
- return "", nil, err
- }
- fileNameLen := binary.BigEndian.Uint64(buf)
- fileName := make([]byte, fileNameLen)
- if _, err := io.ReadFull(r, fileName); err != nil {
- return "", nil, err
- }
- return string(fileName), &holeFileReader{&offsetReader{r, 8 + int(fileNameLen)}, f}, nil
-}
-
-func readHoleFile(w http.ResponseWriter, req *http.Request, hole string) error {
- fileName, r, err := openHoleFile(hole)
+func readHoleFile(w http.ResponseWriter, req *http.Request, holeName string) error {
+ fileName, r, err := hole.Open(holeName)
if err != nil {
return err
}
defer r.Close()
w.Header().Set("Content-Disposition", "attachment; filename*=UTF-8''"+fileName)
var modTime time.Time
- if stat, err := r.f.Stat(); err == nil {
+ if stat, err := r.F.Stat(); err == nil {
modTime = stat.ModTime()
}
http.ServeContent(w, req, fileName, modTime, r)
@@ -557,83 +383,6 @@ func wormhole(w http.ResponseWriter, r *http.Request) {
}
}
-func writeHoleFileToTarFile(tarWriter *tar.Writer, hole string, now time.Time) error {
- fileName, f, err := openHoleFile(hole)
- if err != nil {
- return fmt.Errorf("read back hole file: %s", err)
- }
- defer f.Close()
- size, err := f.Seek(0, io.SeekEnd)
- if err != nil {
- return fmt.Errorf("read back hole file: %s", err)
- }
- if _, err := f.Seek(0, io.SeekStart); err != nil {
- return fmt.Errorf("read back hole file: %s", err)
- }
- if err := tarWriter.WriteHeader(&tar.Header{Name: "wormhole-files/" + fileName, Mode: 0644, Size: size, ModTime: now, Format: tar.FormatPAX}); err != nil {
- return err
- }
- if _, err := io.Copy(tarWriter, f); err != nil {
- return fmt.Errorf("write file to tar file: %s", err)
- }
- return nil
-}
-
-var errNoFile = errors.New("no file")
-
-func uploadWormhole(reader *multipart.Reader) (string, error) {
- var holes []string
- for {
- part, err := reader.NextPart()
- if err != nil {
- break
- }
- if part.FormName() != "file" || part.FileName() == "" {
- continue
- }
- hole, err := writeSingleHoleFile(part.FileName(), part)
- if err != nil {
- return "", err
- }
- holes = append(holes, hole)
- }
- if len(holes) == 0 {
- return "", errNoFile
- }
- if len(holes) == 1 {
- return holes[0], nil
- }
- tarFileHole, tarHoleWriter, err := writeHoleFile("wormhole-files.tar.gz")
- if err != nil {
- return "", fmt.Errorf("make tar file: %s", err)
- }
- defer tarHoleWriter.Close()
- gzipWriter, err := gzip.NewWriterLevel(tarHoleWriter, gzip.BestSpeed)
- if err != nil {
- return "", err
- }
- tarWriter := tar.NewWriter(gzipWriter)
- now := time.Now()
- if err := tarWriter.WriteHeader(&tar.Header{Name: "wormhole-files/", Mode: 0755, ModTime: now, Format: tar.FormatPAX}); err != nil {
- return "", fmt.Errorf("create tar file main directory: %s", err)
- }
- for _, hole := range holes {
- if err := writeHoleFileToTarFile(tarWriter, hole, now); err != nil {
- return "", err
- }
- }
- if err := tarWriter.Close(); err != nil {
- return "", fmt.Errorf("write tar file: %s", err)
- }
- if err := gzipWriter.Close(); err != nil {
- return "", fmt.Errorf("gzip tar file: %s", err)
- }
- if err := tarHoleWriter.Close(); err != nil {
- return "", fmt.Errorf("write tar hole file: %s", err)
- }
- return tarFileHole, nil
-}
-
var (
//go:embed templates/wormhole-success.html.template
wormholeSuccessTemplateString string
@@ -651,9 +400,9 @@ func wormholeUpload(w http.ResponseWriter, r *http.Request) {
http.Error(w, fmt.Sprintf("Not a multipart/form-data request: %s", err), http.StatusBadRequest)
return
}
- hole, err := uploadWormhole(reader)
+ holeName, err := hole.Upload(reader)
if err != nil {
- if err == errNoFile {
+ if err == hole.ErrNoFile {
w.WriteHeader(http.StatusBadRequest)
if err := wormholeTemplate.Execute(w, wormholeTemplateArgs{Err: "don't forget to pick a file"}); err != nil {
log.Printf("Warning: wormholeTemplate: %s", err)
@@ -664,32 +413,17 @@ func wormholeUpload(w http.ResponseWriter, r *http.Request) {
http.Error(w, "Internal error", http.StatusInternalServerError)
return
}
- downloadURL := selfURL.String() + "/wormhole/" + hole
+ downloadURL := selfURL.String() + "/wormhole/" + holeName
if err := wormholeSuccessTemplate.Execute(w, wormholeSuccessTemplateArgs{
URL: downloadURL,
- QR: "/qr.png?t=" + url.QueryEscape(signMsg(downloadURL, "qr")),
+ QR: "/qr.png?t=" + url.QueryEscape(auth.SignMsg(downloadURL, "qr")),
}); err != nil {
log.Printf("Warning: wormholeSend: %s", err)
}
}
-func writeSingleHoleFile(filename string, r io.Reader) (string, error) {
- hole, holeWriter, err := writeHoleFile(filename)
- if err != nil {
- return "", fmt.Errorf("write hole file: %s", err)
- }
- defer holeWriter.Close()
- if _, err := io.Copy(holeWriter, r); err != nil {
- return "", fmt.Errorf("write hole file: %s", err)
- }
- if err := holeWriter.Close(); err != nil {
- return "", fmt.Errorf("write hole file: %s", err)
- }
- return hole, nil
-}
-
func wormholeAPIUpload(w http.ResponseWriter, r *http.Request) {
- hole, err := writeSingleHoleFile(r.PathValue("name"), r.Body)
+ hole, err := hole.WriteSingleFile(r.PathValue("name"), r.Body)
if err != nil {
log.Printf("Error: failed to write hole file: %s", err)
http.Error(w, "Failed to write hole file", http.StatusInternalServerError)
@@ -704,7 +438,7 @@ func wormholeAPIUpload(w http.ResponseWriter, r *http.Request) {
func wormholeDownload(w http.ResponseWriter, r *http.Request) {
if err := readHoleFile(w, r, r.PathValue("hole")); err != nil {
- if err == errNoHole {
+ if err == hole.ErrNoHole {
notFound(w, r)
return
}
@@ -714,64 +448,6 @@ func wormholeDownload(w http.ResponseWriter, r *http.Request) {
}
}
-func cleanHole() error {
- log.Printf("Cleaning hole...")
- holeDir, err := os.Open(*holeTempDir)
- if err != nil {
- return err
- }
- defer holeDir.Close()
- deadline := time.Now().Add(-time.Hour)
- for {
- files, err := holeDir.ReadDir(1024)
- for _, file := range files {
- stat, err := file.Info()
- if err != nil {
- log.Printf("Error: clean hole: %s", err)
- continue
- }
- if !stat.ModTime().Before(deadline) {
- continue
- }
- if err := os.Remove(*holeTempDir + "/" + file.Name()); err != nil {
- log.Printf("Error: clean hole: %s", err)
- continue
- }
- }
- if err != nil {
- if err != io.EOF {
- return err
- }
- return nil
- }
- }
-}
-
-func mac(msg []byte, purpose string) []byte {
- mac := hmac.New(sha256.New, secretKey)
- io.WriteString(mac, purpose)
- io.WriteString(mac, ":")
- mac.Write(msg)
- return mac.Sum(nil)
-}
-
-func signMsg(msg, purpose string) string {
- return msg + base64.RawURLEncoding.EncodeToString(mac([]byte(msg), purpose))
-}
-
-func verifyHMAC(signedMsg, purpose string) (string, bool) {
- macSize := base64.RawURLEncoding.EncodedLen(sha256.Size)
- if len(signedMsg) < macSize {
- return "", false
- }
- msg, msgMAC := signedMsg[:len(signedMsg)-macSize], signedMsg[len(signedMsg)-macSize:]
- expectedMAC := base64.RawURLEncoding.EncodeToString(mac([]byte(msg), purpose))
- if !hmac.Equal([]byte(msgMAC), []byte(expectedMAC)) {
- return "", false
- }
- return msg, true
-}
-
const (
authVersion = "authv3"
authCookieName = "__Secure-a"
@@ -799,7 +475,7 @@ type authToken struct {
func attachCookie(w http.ResponseWriter, tok *authToken) {
buf := binary.AppendVarint(nil, tok.expiry.Unix())
buf = append(buf, tok.userID...)
- token := signMsg(base64.RawURLEncoding.EncodeToString(buf), authVersion)
+ token := auth.SignMsg(base64.RawURLEncoding.EncodeToString(buf), authVersion)
http.SetCookie(w, makeHTTPCookie(token, tok.expiry))
}
@@ -808,7 +484,7 @@ func cookieAuth(r *http.Request) bool {
if err != nil {
return false
}
- msg, ok := verifyHMAC(cookie.Value, authVersion)
+ msg, ok := auth.VerifyHMAC(cookie.Value, authVersion)
if !ok {
return false
}
@@ -842,11 +518,21 @@ func cookieAuth(r *http.Request) bool {
return true
}
-var (
- challengeMu sync.Mutex
- challenge []byte
- challengePosed time.Time
-)
+func verifyRedirect(redirect string) bool {
+ u, err := url.Parse(redirect)
+ if err != nil {
+ return false
+ }
+ switch u.Host {
+ case
+ "cinema." + selfURL.Host,
+ "downloads." + selfURL.Host,
+ selfURL.Host:
+
+ return true
+ }
+ return false
+}
var (
//go:embed templates/login.html.template
@@ -860,40 +546,26 @@ type loginTemplateArgs struct {
func serveLogin(w http.ResponseWriter, r *http.Request) {
if cookieAuth(r) {
- redirect, ok := verifyHMAC(r.FormValue("redirect"), "redirect")
- if !ok {
+ redirect := r.FormValue("redirect")
+ if !verifyRedirect(redirect) {
redirect = "/"
}
http.Redirect(w, r, redirect, http.StatusSeeOther)
return
}
- buf := make([]byte, 32)
- rand.Read(buf)
- challengeMu.Lock()
- challenge = buf
- challengePosed = time.Now()
- challengeMu.Unlock()
+ challenge, ok := auth.NewChallenge()
+ if !ok {
+ http.Error(w, "Too many active requests", http.StatusTooManyRequests)
+ return
+ }
if err := loginTemplate.Execute(w, loginTemplateArgs{
- Challenge: base64.StdEncoding.EncodeToString(buf),
+ Challenge: base64.StdEncoding.EncodeToString(challenge),
}); err != nil {
log.Printf("Warning: login: %s", err)
}
}
func login(w http.ResponseWriter, r *http.Request) {
- challengeMu.Lock()
- myChallenge := challenge
- challenge = nil
- posed := challengePosed
- challengeMu.Unlock()
- if len(myChallenge) == 0 {
- http.Error(w, "Request already consumed", http.StatusConflict)
- return
- }
- if time.Since(posed) > 10*time.Minute {
- http.Error(w, "Challenge is too old. Please try again", http.StatusConflict)
- return
- }
user, err := base64.StdEncoding.DecodeString(r.FormValue("user"))
if err != nil {
http.Error(w, "Bad user", http.StatusBadRequest)
@@ -909,27 +581,6 @@ func login(w http.ResponseWriter, r *http.Request) {
http.Error(w, "Bad client data: invalid base64", http.StatusBadRequest)
return
}
- var cd struct {
- Type string `json:"type"`
- Challenge string `json:"challenge"`
- Origin string `json:"origin"`
- CrossOrigin bool `json:"crossOrigin"`
- }
- if err := json.Unmarshal(clientData, &cd); err != nil {
- http.Error(w, "Bad client data: invalid JSON", http.StatusBadRequest)
- return
- }
- challengeBase64 := base64.RawURLEncoding.EncodeToString(myChallenge)
- if cd.Type != "webauthn.get" || cd.Challenge != challengeBase64 || cd.Origin != selfURL.String() || cd.CrossOrigin {
- http.Error(w, "Bad client data", http.StatusBadRequest)
- return
- }
- expectedRPHash := sha256.Sum256([]byte(selfURL.Hostname()))
- const flagUserPresence, flagUserVerification = 0x01, 0x04
- if len(authData) < 37 || !bytes.Equal(authData[:32], expectedRPHash[:]) || authData[32]&(flagUserPresence|flagUserVerification) != (flagUserPresence|flagUserVerification) {
- http.Error(w, "Bad authenticator data", http.StatusBadRequest)
- return
- }
sig, err := base64.StdEncoding.DecodeString(r.FormValue("sig"))
if err != nil {
http.Error(w, "Bad sig", http.StatusBadRequest)
@@ -941,22 +592,16 @@ func login(w http.ResponseWriter, r *http.Request) {
http.Error(w, "Internal error", http.StatusInternalServerError)
return
}
- pubKey, ok := config.passkeys[string(user)]
- if !ok {
- http.Error(w, "No user with the given ID", http.StatusBadRequest)
- return
- }
- clientDataHash := sha256.Sum256(clientData)
- if err := verifyPubKey(pubKey, slices.Concat(authData, clientDataHash[:]), sig); err != nil {
- http.Error(w, "Challenge failed", http.StatusBadRequest)
+ if err := auth.Login(user, authData, clientData, sig, config.passkeys, selfURL); err != nil {
+ http.Error(w, "Failed challenge", http.StatusBadRequest)
return
}
attachCookie(w, &authToken{
expiry: time.Now().Add(30 * 24 * time.Hour),
userID: user,
})
- redirect, ok := verifyHMAC(r.FormValue("redirect"), "redirect")
- if !ok {
+ redirect := r.FormValue("redirect")
+ if !verifyRedirect(redirect) {
redirect = "/"
}
http.Redirect(w, r, redirect, http.StatusSeeOther)
@@ -985,6 +630,19 @@ func serveRegister(w http.ResponseWriter, r *http.Request) {
}
}
+func wrap(s string) string {
+ out := new(strings.Builder)
+ for s != "" {
+ if out.Len() > 0 {
+ out.WriteString("\n\t ")
+ }
+ n := min(50, len(s))
+ out.WriteString(s[:n])
+ s = s[n:]
+ }
+ return out.String()
+}
+
var (
//go:embed templates/register/register-success.html.template
registerSuccessTemplateString string
@@ -1014,7 +672,7 @@ func register(w http.ResponseWriter, r *http.Request) {
}
if err := registerSuccessTemplate.Execute(w, registerSuccessTemplateArgs{
ID: r.FormValue("name"),
- Key: base64.RawURLEncoding.EncodeToString(slices.Concat(userID, key)),
+ Key: wrap(base64.RawURLEncoding.EncodeToString(slices.Concat(userID, key))),
}); err != nil {
log.Printf("Warning: register: %s", err)
}
@@ -1053,7 +711,7 @@ func serveOTP(w http.ResponseWriter, r *http.Request) {
otpURL := selfURL.String() + "/otp/" + l
if err := otpTemplate.Execute(w, otpTemplateArgs{
URL: otpURL,
- QR: "/qr.png?t=" + url.QueryEscape(signMsg(otpURL, "qr")),
+ QR: "/qr.png?t=" + url.QueryEscape(auth.SignMsg(otpURL, "qr")),
}); err != nil {
log.Printf("Warning: serveOTP: %s", err)
}
@@ -1344,21 +1002,9 @@ func (h *authHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
http.Error(w, "not logged in", http.StatusUnauthorized)
} else {
u := *r.URL
- switch r.Host {
- case
- "cinema." + selfURL.Host,
- "downloads." + selfURL.Host:
-
- u.Host = r.Host
- default:
- u.Host = selfURL.Host
- }
- u.Scheme = "http"
- if *https != "" {
- u.Scheme = "https"
- }
- redirect := signMsg(u.String(), "redirect")
- http.Redirect(w, r, "//"+selfURL.Host+"/login?redirect="+url.QueryEscape(redirect), http.StatusSeeOther)
+ u.Host = r.Host
+ u.Scheme = selfURL.Scheme
+ http.Redirect(w, r, "//"+selfURL.Host+"/login?redirect="+url.QueryEscape(u.String()), http.StatusSeeOther)
}
return
}
@@ -1414,7 +1060,7 @@ func (h *hstsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
func (h *removeAuthCookieHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- /*var cookies []*http.Cookie
+ var cookies []*http.Cookie
for _, c := range r.Cookies() {
if c.Name != authCookieName {
cookies = append(cookies, c)
@@ -1423,7 +1069,7 @@ func (h *removeAuthCookieHandler) ServeHTTP(w http.ResponseWriter, r *http.Reque
r.Header.Del("Cookie")
for _, c := range cookies {
r.AddCookie(c)
- }*/
+ }
h.h.ServeHTTP(w, r)
}
@@ -1434,18 +1080,24 @@ func main() {
log.Fatal(err)
}
- if err := os.MkdirAll(*holeTempDir, 0700); err != nil {
+ if err := hole.Init(); err != nil {
log.Fatal(err)
}
go func() {
for range time.NewTicker(2 * time.Hour).C {
- if err := cleanHole(); err != nil {
+ if err := hole.Clean(); err != nil {
log.Printf("Error: clean hole: %s", err)
}
}
}()
+ go func() {
+ for range time.NewTicker(time.Hour).C {
+ auth.CleanChallenges()
+ }
+ }()
+
mux := http.NewServeMux()
indexTemplate, err := outlinedTemplate(indexString)
if err != nil {