github.com/slspeek/camlistore_namedsearch@v0.0.0-20140519202248-ed6f70f7721a/pkg/jsonsign/signhandler/sig.go (about)

     1  /*
     2  Copyright 2011 Google Inc.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8       http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  // Package signhandler implements the HTTP interface to signing and verifying
    18  // Camlistore JSON blobs.
    19  package signhandler
    20  
    21  import (
    22  	"crypto"
    23  	"fmt"
    24  	"log"
    25  	"net/http"
    26  	"strings"
    27  	"sync"
    28  
    29  	"camlistore.org/pkg/blob"
    30  	"camlistore.org/pkg/blobserver"
    31  	"camlistore.org/pkg/blobserver/gethandler"
    32  	"camlistore.org/pkg/httputil"
    33  	"camlistore.org/pkg/jsonconfig"
    34  	"camlistore.org/pkg/jsonsign"
    35  	"camlistore.org/pkg/osutil"
    36  	"camlistore.org/pkg/schema"
    37  
    38  	"camlistore.org/third_party/code.google.com/p/go.crypto/openpgp"
    39  )
    40  
    41  const kMaxJSONLength = 1024 * 1024
    42  
    43  type Handler struct {
    44  	// Optional path to non-standard secret gpg keyring file
    45  	secretRing string
    46  
    47  	pubKey        string // armored
    48  	pubKeyBlobRef blob.Ref
    49  	pubKeyFetcher blob.Fetcher
    50  
    51  	pubKeyBlobRefServeSuffix string // "camli/sha1-xxxx"
    52  	pubKeyHandler            http.Handler
    53  
    54  	pubKeyDest blobserver.Storage // Where our public key is published
    55  
    56  	pubKeyUploadMu sync.RWMutex
    57  	pubKeyUploaded bool
    58  
    59  	entity *openpgp.Entity
    60  	signer *schema.Signer
    61  }
    62  
    63  func (h *Handler) Signer() *schema.Signer { return h.signer }
    64  
    65  func (h *Handler) secretRingPath() string {
    66  	if h.secretRing != "" {
    67  		return h.secretRing
    68  	}
    69  	return osutil.SecretRingFile()
    70  }
    71  
    72  func init() {
    73  	blobserver.RegisterHandlerConstructor("jsonsign", newJSONSignFromConfig)
    74  }
    75  
    76  func newJSONSignFromConfig(ld blobserver.Loader, conf jsonconfig.Obj) (http.Handler, error) {
    77  	pubKeyDestPrefix := conf.OptionalString("publicKeyDest", "")
    78  
    79  	// either a short form ("26F5ABDA") or one the longer forms.
    80  	keyId := conf.RequiredString("keyId")
    81  
    82  	h := &Handler{
    83  		secretRing: conf.OptionalString("secretRing", ""),
    84  	}
    85  	var err error
    86  	if err = conf.Validate(); err != nil {
    87  		return nil, err
    88  	}
    89  
    90  	h.entity, err = jsonsign.EntityFromSecring(keyId, h.secretRingPath())
    91  	if err != nil {
    92  		return nil, err
    93  	}
    94  
    95  	h.pubKey, err = jsonsign.ArmoredPublicKey(h.entity)
    96  
    97  	ms := new(blob.MemoryStore)
    98  	h.pubKeyBlobRef, err = ms.AddBlob(crypto.SHA1, h.pubKey)
    99  	if err != nil {
   100  		return nil, err
   101  	}
   102  	h.pubKeyFetcher = ms
   103  
   104  	if pubKeyDestPrefix != "" {
   105  		sto, err := ld.GetStorage(pubKeyDestPrefix)
   106  		if err != nil {
   107  			return nil, err
   108  		}
   109  		h.pubKeyDest = sto
   110  	}
   111  	h.pubKeyBlobRefServeSuffix = "camli/" + h.pubKeyBlobRef.String()
   112  	h.pubKeyHandler = &gethandler.Handler{
   113  		Fetcher: ms,
   114  	}
   115  
   116  	h.signer, err = schema.NewSigner(h.pubKeyBlobRef, strings.NewReader(h.pubKey), h.entity)
   117  	if err != nil {
   118  		return nil, err
   119  	}
   120  
   121  	return h, nil
   122  }
   123  
   124  func (h *Handler) uploadPublicKey() error {
   125  	h.pubKeyUploadMu.RLock()
   126  	if h.pubKeyUploaded {
   127  		h.pubKeyUploadMu.RUnlock()
   128  		return nil
   129  	}
   130  	h.pubKeyUploadMu.RUnlock()
   131  
   132  	sto := h.pubKeyDest
   133  
   134  	h.pubKeyUploadMu.Lock()
   135  	defer h.pubKeyUploadMu.Unlock()
   136  	if h.pubKeyUploaded {
   137  		return nil
   138  	}
   139  	_, err := blobserver.StatBlob(sto, h.pubKeyBlobRef)
   140  	if err == nil {
   141  		h.pubKeyUploaded = true
   142  		return nil
   143  	}
   144  	_, err = blobserver.Receive(sto, h.pubKeyBlobRef, strings.NewReader(h.pubKey))
   145  	log.Printf("uploadPublicKey(%T, %v) = %v", sto, h.pubKeyBlobRef, err)
   146  	if err == nil {
   147  		h.pubKeyUploaded = true
   148  	}
   149  	return err
   150  }
   151  
   152  func (h *Handler) DiscoveryMap(base string) map[string]interface{} {
   153  	m := map[string]interface{}{
   154  		"publicKeyId":   h.entity.PrimaryKey.KeyIdString(),
   155  		"signHandler":   base + "camli/sig/sign",
   156  		"verifyHandler": base + "camli/sig/verify",
   157  	}
   158  	if h.pubKeyBlobRef.Valid() {
   159  		m["publicKeyBlobRef"] = h.pubKeyBlobRef.String()
   160  		m["publicKey"] = base + h.pubKeyBlobRefServeSuffix
   161  	}
   162  	return m
   163  }
   164  
   165  func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
   166  	base := httputil.PathBase(req)
   167  	subPath := httputil.PathSuffix(req)
   168  	switch req.Method {
   169  	case "GET", "HEAD":
   170  		switch subPath {
   171  		case "":
   172  			http.Redirect(rw, req, base+"camli/sig/discovery", http.StatusFound)
   173  			return
   174  		case h.pubKeyBlobRefServeSuffix:
   175  			h.pubKeyHandler.ServeHTTP(rw, req)
   176  			return
   177  		case "camli/sig/sign":
   178  			fallthrough
   179  		case "camli/sig/verify":
   180  			http.Error(rw, "POST required", 400)
   181  			return
   182  		case "camli/sig/discovery":
   183  			httputil.ReturnJSON(rw, h.DiscoveryMap(base))
   184  			return
   185  		}
   186  	case "POST":
   187  		switch subPath {
   188  		case "camli/sig/sign":
   189  			h.handleSign(rw, req)
   190  			return
   191  		case "camli/sig/verify":
   192  			h.handleVerify(rw, req)
   193  			return
   194  		}
   195  	}
   196  	http.Error(rw, "Unsupported path or method.", http.StatusBadRequest)
   197  }
   198  
   199  func (h *Handler) handleVerify(rw http.ResponseWriter, req *http.Request) {
   200  	req.ParseForm()
   201  	sjson := req.FormValue("sjson")
   202  	if sjson == "" {
   203  		http.Error(rw, "missing \"sjson\" parameter", http.StatusBadRequest)
   204  		return
   205  	}
   206  
   207  	m := make(map[string]interface{})
   208  
   209  	// TODO: use a different fetcher here that checks memory, disk,
   210  	// the internet, etc.
   211  	fetcher := h.pubKeyFetcher
   212  
   213  	vreq := jsonsign.NewVerificationRequest(sjson, fetcher)
   214  	if vreq.Verify() {
   215  		m["signatureValid"] = 1
   216  		m["signerKeyId"] = vreq.SignerKeyId
   217  		m["verifiedData"] = vreq.PayloadMap
   218  	} else {
   219  		errStr := vreq.Err.Error()
   220  		m["signatureValid"] = 0
   221  		m["errorMessage"] = errStr
   222  	}
   223  
   224  	rw.WriteHeader(http.StatusOK) // no HTTP response code fun, error info in JSON
   225  	httputil.ReturnJSON(rw, m)
   226  }
   227  
   228  func (h *Handler) handleSign(rw http.ResponseWriter, req *http.Request) {
   229  	req.ParseForm()
   230  
   231  	badReq := func(s string) {
   232  		http.Error(rw, s, http.StatusBadRequest)
   233  		log.Printf("bad request: %s", s)
   234  		return
   235  	}
   236  
   237  	jsonStr := req.FormValue("json")
   238  	if jsonStr == "" {
   239  		badReq("missing \"json\" parameter")
   240  		return
   241  	}
   242  	if len(jsonStr) > kMaxJSONLength {
   243  		badReq("parameter \"json\" too large")
   244  		return
   245  	}
   246  
   247  	sreq := &jsonsign.SignRequest{
   248  		UnsignedJSON:      jsonStr,
   249  		Fetcher:           h.pubKeyFetcher,
   250  		ServerMode:        true,
   251  		SecretKeyringPath: h.secretRing,
   252  	}
   253  	signedJSON, err := sreq.Sign()
   254  	if err != nil {
   255  		// TODO: some aren't really a "bad request"
   256  		badReq(fmt.Sprintf("%v", err))
   257  		return
   258  	}
   259  	h.uploadPublicKey()
   260  	rw.Write([]byte(signedJSON))
   261  }
   262  
   263  func (h *Handler) Sign(bb *schema.Builder) (string, error) {
   264  	bb.SetSigner(h.pubKeyBlobRef)
   265  	unsigned, err := bb.JSON()
   266  	if err != nil {
   267  		return "", err
   268  	}
   269  	sreq := &jsonsign.SignRequest{
   270  		UnsignedJSON:      unsigned,
   271  		Fetcher:           h.pubKeyFetcher,
   272  		ServerMode:        true,
   273  		SecretKeyringPath: h.secretRing,
   274  	}
   275  	claimTime, err := bb.Blob().ClaimDate()
   276  	if err != nil {
   277  		if !schema.IsMissingField(err) {
   278  			return "", err
   279  		}
   280  	} else {
   281  		sreq.SignatureTime = claimTime
   282  	}
   283  	h.uploadPublicKey()
   284  	return sreq.Sign()
   285  }