package connect import ( "bytes" "encoding/base64" "encoding/json" "math" "net/http" "amadan.net/oficina/home/handoff" ) const homeReturnCookie = "__Host-oficina-home-return" const homeReturnLimit = 4096 type homeReturnIntent struct { Path string `json:"path"` Issued int64 `json:"issued"` Expires int64 `json:"expires"` } // Resource navigation is separate from the fixed-action local reauth marker. // Neither cookie authorizes linking or access to the requested document. func (n *localLinkNavigation) rememberReturn(w http.ResponseWriter, path string) bool { if !handoff.SafePath(path) { path = "/" } now := n.now().Unix() if now <= 0 || now > math.MaxInt64-localLinkNavigationLifetime { return false } raw, err := json.Marshal(homeReturnIntent{path, now, now + localLinkNavigationLifetime}) if err != nil { return false } sealed, err := n.sealer.Seal(n.namespace+".home-return.v1:"+n.origin, raw) if err != nil { return false } value := base64.RawURLEncoding.EncodeToString(sealed) if len(value) > homeReturnLimit { return false } localNavigationCookie(w, homeReturnCookie, value, int(localLinkNavigationLifetime)) return true } func (n *localLinkNavigation) returnPath(r *http.Request) string { value := "" if cookieCount(r, homeReturnCookie) != 1 { return "/" } cookie, err := r.Cookie(homeReturnCookie) if err != nil { return "/" } value = cookie.Value if value == "" || len(value) > homeReturnLimit { return "/" } sealed, err := base64.RawURLEncoding.Strict().DecodeString(value) if err != nil || base64.RawURLEncoding.EncodeToString(sealed) != value { return "/" } raw, err := n.sealer.Open(n.namespace+".home-return.v1:"+n.origin, sealed) if err != nil || len(raw) > homeReturnLimit { return "/" } var intent homeReturnIntent if json.Unmarshal(raw, &intent) != nil { return "/" } canonical, _ := json.Marshal(intent) now := n.now().Unix() if !bytes.Equal(raw, canonical) || intent.Issued <= 0 || intent.Issued > now || intent.Expires <= now || intent.Expires <= intent.Issued || intent.Expires-intent.Issued > localLinkNavigationLifetime || !handoff.SafePath(intent.Path) { return "/" } return intent.Path }