github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/externals/proof_service_web.go (about)

     1  // Copyright 2015 Keybase, Inc. All rights reserved. Use of
     2  // this source code is governed by the included BSD license.
     3  
     4  package externals
     5  
     6  import (
     7  	"fmt"
     8  	"net/url"
     9  	"regexp"
    10  	"strings"
    11  
    12  	libkb "github.com/keybase/client/go/libkb"
    13  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
    14  	jsonw "github.com/keybase/go-jsonw"
    15  )
    16  
    17  // =============================================================================
    18  // Web
    19  //
    20  
    21  type WebChecker struct {
    22  	proof libkb.RemoteProofChainLink
    23  }
    24  
    25  var _ libkb.ProofChecker = (*WebChecker)(nil)
    26  
    27  var webKeybaseFiles = []string{".well-known/keybase.txt", "keybase.txt"}
    28  
    29  func NewWebChecker(p libkb.RemoteProofChainLink) (*WebChecker, libkb.ProofError) {
    30  	return &WebChecker{p}, nil
    31  }
    32  
    33  func (rc *WebChecker) GetTorError() libkb.ProofError {
    34  	urlBase := rc.proof.ToDisplayString()
    35  
    36  	u, err := url.Parse(urlBase)
    37  	if err != nil || u.Scheme != "https" {
    38  		return libkb.ProofErrorHTTPOverTor
    39  	}
    40  
    41  	return nil
    42  }
    43  
    44  func (rc *WebChecker) CheckStatus(mctx libkb.MetaContext, h libkb.SigHint, pcm libkb.ProofCheckerMode,
    45  	pvlU keybase1.MerkleStoreEntry) (*libkb.SigHint, libkb.ProofError) {
    46  	if pcm != libkb.ProofCheckerModeActive {
    47  		mctx.Debug("Web check skipped since proof checking was not in active mode (%s)", h.GetAPIURL())
    48  		return nil, libkb.ProofErrorUnchecked
    49  	}
    50  	// TODO CORE-8951 see if we can populate verifiedHint with anything useful.
    51  	return nil, CheckProofPvl(mctx, keybase1.ProofType_GENERIC_WEB_SITE, rc.proof, h, pvlU)
    52  }
    53  
    54  //
    55  // =============================================================================
    56  
    57  type WebServiceType struct {
    58  	libkb.BaseServiceType
    59  	scheme string
    60  }
    61  
    62  func (t *WebServiceType) Key() string {
    63  	if t.scheme == "" {
    64  		return "web"
    65  	}
    66  	return t.scheme
    67  }
    68  
    69  func (t *WebServiceType) NormalizeUsername(s string) (ret string, err error) {
    70  	// The username is just the (lowercased) hostname.
    71  	if !libkb.IsValidHostname(s) {
    72  		return "", libkb.NewInvalidHostnameError(s)
    73  	}
    74  	return strings.ToLower(s), nil
    75  }
    76  
    77  func ParseWeb(s string) (hostname string, prot string, err error) {
    78  	rxx := regexp.MustCompile("^(http(s?))://(.*)$")
    79  	if v := rxx.FindStringSubmatch(s); v != nil {
    80  		s = v[3]
    81  		prot = v[1]
    82  	}
    83  	if !libkb.IsValidHostname(s) {
    84  		err = libkb.NewInvalidHostnameError(s)
    85  	} else {
    86  		hostname = s
    87  	}
    88  	return
    89  }
    90  
    91  func (t *WebServiceType) NormalizeRemoteName(mctx libkb.MetaContext, s string) (ret string, err error) {
    92  	// The remote name is a full (case-preserved) URL.
    93  	var prot, host string
    94  	if host, prot, err = ParseWeb(s); err != nil {
    95  		return
    96  	}
    97  	var res *libkb.APIRes
    98  	res, err = mctx.G().GetAPI().Get(mctx, libkb.APIArg{
    99  		Endpoint:    "remotes/check",
   100  		SessionType: libkb.APISessionTypeREQUIRED,
   101  		Args: libkb.HTTPArgs{
   102  			"hostname": libkb.S{Val: host},
   103  		},
   104  	})
   105  	if err != nil {
   106  		return
   107  	}
   108  	var found string
   109  	found, err = res.Body.AtPath("results.first").GetString()
   110  	if err != nil {
   111  		err = libkb.NewWebUnreachableError(host)
   112  		return
   113  	}
   114  	if len(t.scheme) > 0 && len(prot) > 0 && prot != t.scheme {
   115  		msg := fmt.Sprintf("You tried to prove ownership of %s over %s but gave a %s link.", host, t.scheme, prot)
   116  		err = libkb.NewProtocolSchemeMismatch(msg)
   117  		return
   118  	}
   119  	protocolAssertsHTTPS := prot == "https"
   120  	proofTypeAssertsHTTPS := t.scheme == "https"
   121  	if (protocolAssertsHTTPS || proofTypeAssertsHTTPS) && found != "https:" {
   122  		msg := fmt.Sprintf("You specified HTTPS for %s but only HTTP is available", host)
   123  		err = libkb.NewProtocolDowngradeError(msg)
   124  		return
   125  	}
   126  	ret = found + "//" + host
   127  
   128  	return
   129  }
   130  
   131  func (t *WebServiceType) GetPrompt() string {
   132  	return "Web site to check"
   133  }
   134  
   135  func (t *WebServiceType) ToServiceJSON(un string) *jsonw.Wrapper {
   136  	h, p, _ := ParseWeb(un)
   137  	ret := jsonw.NewDictionary()
   138  	_ = ret.SetKey("protocol", jsonw.NewString(p+":"))
   139  	_ = ret.SetKey("hostname", jsonw.NewString(h))
   140  	return ret
   141  }
   142  
   143  func (t *WebServiceType) MarkupFilenames(un string, mkp *libkb.Markup) {
   144  	mkp.Append(`<ul>`)
   145  	first := true
   146  	for _, f := range webKeybaseFiles {
   147  		var bullet string
   148  		if first {
   149  			bullet = "   "
   150  			first = false
   151  		} else {
   152  			bullet = "OR "
   153  		}
   154  		mkp.Append(`<li bullet="` + bullet + `"><url>` + un + "/" + f + `</url></li>`)
   155  	}
   156  	mkp.Append(`</ul>`)
   157  }
   158  
   159  func (t *WebServiceType) PreProofWarning(un string) *libkb.Markup {
   160  	mkp := libkb.FmtMarkup(`<p>You will be asked to post a file to:</p>`)
   161  	t.MarkupFilenames(un, mkp)
   162  	return mkp
   163  }
   164  
   165  func (t *WebServiceType) PostInstructions(un string) *libkb.Markup {
   166  	mkp := libkb.FmtMarkup(`<p>Make the following file available at:</p>`)
   167  	t.MarkupFilenames(un, mkp)
   168  	return mkp
   169  }
   170  
   171  func (t *WebServiceType) DisplayName() string   { return "Web" }
   172  func (t *WebServiceType) GetTypeName() string   { return "web" }
   173  func (t *WebServiceType) PickerSubtext() string { return t.GetTypeName() }
   174  
   175  func (t *WebServiceType) RecheckProofPosting(tryNumber int, status keybase1.ProofStatus, _ string) (warning *libkb.Markup, err error) {
   176  	if status == keybase1.ProofStatus_PERMISSION_DENIED {
   177  		warning = libkb.FmtMarkup("Permission denied! Make sure your proof page is <strong>public</strong>.")
   178  	} else {
   179  		warning, err = t.BaseRecheckProofPosting(tryNumber, status)
   180  	}
   181  	return
   182  }
   183  func (t *WebServiceType) GetProofType() string { return "web_service_binding.generic" }
   184  
   185  func (t *WebServiceType) CheckProofText(text string, id keybase1.SigID, sig string) (err error) {
   186  	return t.BaseCheckProofTextFull(text, id, sig)
   187  }
   188  
   189  func (t *WebServiceType) GetAPIArgKey() string { return "remote_host" }
   190  func (t *WebServiceType) LastWriterWins() bool { return false }
   191  
   192  func (t *WebServiceType) MakeProofChecker(l libkb.RemoteProofChainLink) libkb.ProofChecker {
   193  	return &WebChecker{l}
   194  }
   195  
   196  // =============================================================================