diff options
Diffstat (limited to 'roseh.moe.go')
| -rw-r--r-- | roseh.moe.go | 504 |
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 { |
