func (h *shareHandler) serveHTTP(rw http.ResponseWriter, req *http.Request) error { var err error pathSuffix := httputil.PathSuffix(req) if len(pathSuffix) == 0 { // This happens during testing because we don't go through PrefixHandler pathSuffix = strings.TrimLeft(req.URL.Path, "/") } pathParts := strings.SplitN(pathSuffix, "/", 2) blobRef, ok := blob.Parse(pathParts[0]) if !ok { err = &shareError{code: invalidURL, response: badRequest, message: fmt.Sprintf("Malformed share pathSuffix: %s", pathSuffix)} } else { err = handleGetViaSharing(rw, req, blobRef, h.fetcher) } if se, ok := err.(*shareError); ok { switch se.response { case badRequest: httputil.BadRequestError(rw, err.Error()) case unauthorizedRequest: log.Print(err) auth.SendUnauthorized(rw, req) } } return err }
func (a *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if r.URL.Path == a.masterqueryURLPath { a.handleMasterQuery(w, r) return } if a.configURLPath != "" && r.URL.Path == a.configURLPath { if a.auth.AllowedAccess(r)&auth.OpGet == auth.OpGet { camhttputil.ReturnJSON(w, a.appConfig) } else { auth.SendUnauthorized(w, r) } return } trimmedPath := strings.TrimPrefix(r.URL.Path, a.prefix) if strings.HasPrefix(trimmedPath, "/search") { a.handleSearch(w, r) return } if a.proxy == nil { http.Error(w, "no proxy for the app", 500) return } a.proxy.ServeHTTP(w, r) }
func (a *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) { if camhttputil.PathSuffix(req) == "config.json" { if a.auth.AllowedAccess(req)&auth.OpGet == auth.OpGet { camhttputil.ReturnJSON(rw, a.appConfig) } else { auth.SendUnauthorized(rw, req) } return } if a.proxy == nil { http.Error(rw, "no proxy for the app", 500) return } a.proxy.ServeHTTP(rw, req) }
// handleMasterQuery allows an app to register the master query that defines the // domain limiting all subsequent search queries. func (a *Handler) handleMasterQuery(w http.ResponseWriter, r *http.Request) { if !(a.auth.AllowedAccess(r)&auth.OpAll == auth.OpAll) { auth.SendUnauthorized(w, r) return } if r.Method != http.MethodPost { http.Error(w, "not a POST", http.StatusMethodNotAllowed) return } if a.sh == nil { http.Error(w, "app proxy has no search handler", 500) return } if refresh, _ := strconv.ParseBool(r.FormValue("refresh")); refresh { if err := a.refreshDomainBlobs(); err != nil { if err == errRefreshSuppress { http.Error(w, "too many refresh requests", http.StatusTooManyRequests) } else { http.Error(w, fmt.Sprintf("%v", err), 500) } return } w.Write([]byte("OK")) return } sq := new(search.SearchQuery) if err := sq.FromHTTP(r); err != nil { http.Error(w, fmt.Sprintf("error reading master query: %v", err), 500) return } var masterQuery search.SearchQuery = *(sq) des := *(masterQuery.Describe) masterQuery.Describe = &des sr, err := a.sh.Query(sq) if err != nil { http.Error(w, fmt.Sprintf("error running master query: %v", err), 500) return } a.masterQueryMu.Lock() defer a.masterQueryMu.Unlock() a.masterQuery = &masterQuery a.domainBlobs = make(map[blob.Ref]bool, len(sr.Describe.Meta)) for _, v := range sr.Describe.Meta { a.domainBlobs[v.BlobRef] = true } a.domainBlobsRefresh = time.Now() w.Write([]byte("OK")) }
func (h *Handler) ServeHTTP(conn http.ResponseWriter, req *http.Request) { blobRef := blobFromUrlPath(req.URL.Path) if blobRef == nil { http.Error(conn, "Malformed GET URL.", 400) return } switch { case h.AllowGlobalAccess || auth.Allowed(req, auth.OpGet): serveBlobRef(conn, req, blobRef, h.Fetcher) case auth.TriedAuthorization(req): log.Printf("Attempted authorization failed on %s", req.URL) auth.SendUnauthorized(conn, req) default: handleGetViaSharing(conn, req, blobRef, h.Fetcher) } }
func (rh *RootHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if wantsDiscovery(r) { if auth.Allowed(r, auth.OpDiscovery) { rh.serveDiscovery(w, r) return } if !rh.Stealth { auth.SendUnauthorized(w, r) } return } if rh.Stealth { return } if r.RequestURI == "/" && rh.ui != nil { http.Redirect(w, r, "/ui/", http.StatusMovedPermanently) return } if r.URL.Path == "/favicon.ico" { ServeStaticFile(w, r, Files, "favicon.ico") return } f := func(p string, a ...interface{}) { fmt.Fprintf(w, p, a...) } f("<html><body><p>This is camlistored (%s), a "+ "<a href='http://camlistore.org'>Camlistore</a> server.</p>", buildinfo.Version()) if rh.ui != nil { f("<p>To manage your content, access the <a href='%s'>%s</a>.</p>", rh.ui.prefix, rh.ui.prefix) } if rh.statusRoot != "" { f("<p>To view status, see <a href='%s'>%s</a>.</p>", rh.statusRoot, rh.statusRoot) } if rh.helpRoot != "" { f("<p>To view more information on accessing the server, see <a href='%s'>%s</a>.</p>", rh.helpRoot, rh.helpRoot) } fmt.Fprintf(w, "</body></html>") }
// Unauthenticated user. Be paranoid. func handleGetViaSharing(conn http.ResponseWriter, req *http.Request, blobRef *blobref.BlobRef, fetcher blobref.StreamingFetcher) { if w, ok := fetcher.(blobserver.ContextWrapper); ok { fetcher = w.WrapContext(req) } viaPathOkay := false startTime := time.Now() defer func() { if !viaPathOkay { // Insert a delay, to hide timing attacks probing // for the existence of blobs. sleep := fetchFailureDelayNs - (time.Now().Sub(startTime)) if sleep > 0 { time.Sleep(sleep) } } }() viaBlobs := make([]*blobref.BlobRef, 0) if via := req.FormValue("via"); via != "" { for _, vs := range strings.Split(via, ",") { if br := blobref.Parse(vs); br == nil { httputil.BadRequestError(conn, "Malformed blobref in via param") return } else { viaBlobs = append(viaBlobs, br) } } } fetchChain := make([]*blobref.BlobRef, 0) fetchChain = append(fetchChain, viaBlobs...) fetchChain = append(fetchChain, blobRef) for i, br := range fetchChain { switch i { case 0: file, size, err := fetcher.FetchStreaming(br) if err != nil { log.Printf("Fetch chain 0 of %s failed: %v", br.String(), err) auth.SendUnauthorized(conn, req) return } defer file.Close() if size > maxJSONSize { log.Printf("Fetch chain 0 of %s too large", br.String()) auth.SendUnauthorized(conn, req) return } jd := json.NewDecoder(file) m := make(map[string]interface{}) if err := jd.Decode(&m); err != nil { log.Printf("Fetch chain 0 of %s wasn't JSON: %v", br.String(), err) auth.SendUnauthorized(conn, req) return } if m["camliType"].(string) != "share" { log.Printf("Fetch chain 0 of %s wasn't a share", br.String()) auth.SendUnauthorized(conn, req) return } if len(fetchChain) > 1 && fetchChain[1].String() != m["target"].(string) { log.Printf("Fetch chain 0->1 (%s -> %q) unauthorized, expected hop to %q", br.String(), fetchChain[1].String(), m["target"]) auth.SendUnauthorized(conn, req) return } case len(fetchChain) - 1: // Last one is fine (as long as its path up to here has been proven, and it's // not the first thing in the chain) continue default: file, _, err := fetcher.FetchStreaming(br) if err != nil { log.Printf("Fetch chain %d of %s failed: %v", i, br.String(), err) auth.SendUnauthorized(conn, req) return } defer file.Close() lr := io.LimitReader(file, maxJSONSize) slurpBytes, err := ioutil.ReadAll(lr) if err != nil { log.Printf("Fetch chain %d of %s failed in slurp: %v", i, br.String(), err) auth.SendUnauthorized(conn, req) return } saught := fetchChain[i+1].String() if bytes.IndexAny(slurpBytes, saught) == -1 { log.Printf("Fetch chain %d of %s failed; no reference to %s", i, br.String(), saught) auth.SendUnauthorized(conn, req) return } } } viaPathOkay = true serveBlobRef(conn, req, blobRef, fetcher) }
// Unauthenticated user. Be paranoid. func handleGetViaSharing(conn http.ResponseWriter, req *http.Request, blobRef blob.Ref, fetcher blob.StreamingFetcher) { if req.Method != "GET" && req.Method != "HEAD" { httputil.BadRequestError(conn, "Invalid method") return } viaPathOkay := false startTime := time.Now() defer func() { if !viaPathOkay { // Insert a delay, to hide timing attacks probing // for the existence of blobs. sleep := fetchFailureDelay - (time.Now().Sub(startTime)) time.Sleep(sleep) } }() viaBlobs := make([]blob.Ref, 0) if via := req.FormValue("via"); via != "" { for _, vs := range strings.Split(via, ",") { if br, ok := blob.Parse(vs); ok { viaBlobs = append(viaBlobs, br) } else { httputil.BadRequestError(conn, "Malformed blobref in via param") return } } } fetchChain := make([]blob.Ref, 0) fetchChain = append(fetchChain, viaBlobs...) fetchChain = append(fetchChain, blobRef) for i, br := range fetchChain { switch i { case 0: file, size, err := fetcher.FetchStreaming(br) if err != nil { log.Printf("Fetch chain 0 of %s failed: %v", br.String(), err) auth.SendUnauthorized(conn, req) return } defer file.Close() if size > schema.MaxSchemaBlobSize { log.Printf("Fetch chain 0 of %s too large", br.String()) auth.SendUnauthorized(conn, req) return } blob, err := schema.BlobFromReader(br, file) if err != nil { log.Printf("Can't create a blob from %v: %v", br.String(), err) auth.SendUnauthorized(conn, req) return } share, ok := blob.AsShare() if !ok { log.Printf("Fetch chain 0 of %s wasn't a valid Share", br.String()) auth.SendUnauthorized(conn, req) return } if len(fetchChain) > 1 && fetchChain[1].String() != share.Target().String() { log.Printf("Fetch chain 0->1 (%s -> %q) unauthorized, expected hop to %q", br.String(), fetchChain[1].String(), share.Target().String()) auth.SendUnauthorized(conn, req) return } case len(fetchChain) - 1: // Last one is fine (as long as its path up to here has been proven, and it's // not the first thing in the chain) continue default: file, _, err := fetcher.FetchStreaming(br) if err != nil { log.Printf("Fetch chain %d of %s failed: %v", i, br.String(), err) auth.SendUnauthorized(conn, req) return } defer file.Close() lr := io.LimitReader(file, schema.MaxSchemaBlobSize) slurpBytes, err := ioutil.ReadAll(lr) if err != nil { log.Printf("Fetch chain %d of %s failed in slurp: %v", i, br.String(), err) auth.SendUnauthorized(conn, req) return } saught := fetchChain[i+1].String() if bytes.IndexAny(slurpBytes, saught) == -1 { log.Printf("Fetch chain %d of %s failed; no reference to %s", i, br.String(), saught) auth.SendUnauthorized(conn, req) return } } } viaPathOkay = true gethandler.ServeBlobRef(conn, req, blobRef, fetcher) }