@@ -29,6 +29,7 @@ import (
2929 "reflect"
3030 "regexp"
3131 "strings"
32+ "sync"
3233 "time"
3334)
3435
@@ -121,6 +122,7 @@ type certRequestHandler struct {
121122 Config map [string ]ssh_ca_util.SignerdConfig
122123 state map [string ]certRequest
123124 sshAgentConn io.ReadWriter
125+ stateMutex sync.RWMutex
124126}
125127
126128type signingRequest struct {
@@ -325,11 +327,15 @@ func (h *certRequestHandler) saveSigningRequest(config ssh_ca_util.SignerdConfig
325327 if len (requestIDStr ) < 12 {
326328 return false , fmt .Errorf ("Request id is too short to be useful." )
327329 }
330+ h .stateMutex .RLock ()
328331 _ , ok = h .state [requestIDStr ]
332+ h .stateMutex .RUnlock ()
329333 if ok {
330334 return false , fmt .Errorf ("Request id '%s' already in use." , requestIDStr )
331335 }
336+ h .stateMutex .Lock ()
332337 h .state [requestIDStr ] = certRequest
338+ h .stateMutex .Unlock ()
333339
334340 // This is the special case of supporting auto-signing.
335341 if config .NumberSignersRequired < 0 {
@@ -401,7 +407,7 @@ type listResponseElement struct {
401407 Serial uint64
402408 Environment string
403409 Reason string
404- Cert * ssh.Certificate
410+ Cert * ssh.Certificate `json:"-"`
405411}
406412type certRequestResponse map [string ]listResponseElement
407413
@@ -458,6 +464,8 @@ func (h *certRequestHandler) listPendingRequests(rw http.ResponseWriter, req *ht
458464
459465 foundSomething := false
460466 results := make (certRequestResponse )
467+ h .stateMutex .RLock ()
468+ defer h .stateMutex .RUnlock ()
461469 for k , v := range h .state {
462470 encodedCert := base64 .StdEncoding .EncodeToString (v .request .Marshal ())
463471 element := newResponseElement (v .request , encodedCert , v .certSigned , v .certRejected , len (v .signatures ), h .Config [v .environment ].NumberSignersRequired , v .request .Serial , v .reason , v .environment )
@@ -497,6 +505,8 @@ func (h *certRequestHandler) getRequestStatus(rw http.ResponseWriter, req *http.
497505 certRejected bool
498506 cert string
499507 }
508+ h .stateMutex .RLock ()
509+ defer h .stateMutex .RUnlock ()
500510 if h .state [requestID ].certSigned {
501511 rw .Write ([]byte (h .state [requestID ].request .Type ()))
502512 rw .Write ([]byte (" " ))
@@ -511,7 +521,9 @@ func (h *certRequestHandler) getRequestStatus(rw http.ResponseWriter, req *http.
511521
512522func (h * certRequestHandler ) signOrRejectRequest (rw http.ResponseWriter , req * http.Request ) {
513523 requestID := mux .Vars (req )["requestID" ]
524+ h .stateMutex .RLock ()
514525 originalRequest , ok := h .state [requestID ]
526+ h .stateMutex .RUnlock ()
515527 if ! ok {
516528 http .Error (rw , "Unknown request id" , http .StatusNotFound )
517529 return
@@ -555,7 +567,9 @@ func (h *certRequestHandler) signOrRejectRequest(rw http.ResponseWriter, req *ht
555567 // Verifying that the cert being posted to us here matches the one in the
556568 // request. That is, that an attacker isn't using an old signature to sign a
557569 // new/different request id
570+ h .stateMutex .RLock ()
558571 requestedCert := h .state [requestID ].request
572+ h .stateMutex .RUnlock ()
559573 if ! compareCerts (requestedCert , signedCert ) {
560574 log .Printf ("Signature was valid, but cert didn't match from %s." , req .RemoteAddr )
561575 log .Printf ("Orig req: %#v\n " , requestedCert )
@@ -577,6 +591,8 @@ func (h *certRequestHandler) signOrRejectRequest(rw http.ResponseWriter, req *ht
577591
578592func (h * certRequestHandler ) rejectRequest (requestID string , signerFp string , envConfig ssh_ca_util.SignerdConfig ) error {
579593 log .Printf ("Reject received for id %s" , requestID )
594+ h .stateMutex .Lock ()
595+ defer h .stateMutex .Unlock ()
580596 stateInfo := h .state [requestID ]
581597 stateInfo .certRejected = true
582598 // this is weird. see: https://code.google.com/p/go/issues/detail?id=3117
@@ -585,10 +601,15 @@ func (h *certRequestHandler) rejectRequest(requestID string, signerFp string, en
585601}
586602
587603func (h * certRequestHandler ) addConfirmation (requestID string , signerFp string , envConfig ssh_ca_util.SignerdConfig ) error {
588- if h .state [requestID ].certRejected {
604+ h .stateMutex .RLock ()
605+ certRejected := h .state [requestID ].certRejected
606+ h .stateMutex .RUnlock ()
607+ if certRejected {
589608 return fmt .Errorf ("Attempt to sign a rejected cert." )
590609 }
610+ h .stateMutex .Lock ()
591611 h .state [requestID ].signatures [signerFp ] = true
612+ h .stateMutex .Unlock ()
592613
593614 if envConfig .SlackUrl != "" {
594615 slackMsg := fmt .Sprintf ("SSH cert %s signed by %s making %d/%d signatures." ,
@@ -611,6 +632,8 @@ func (h *certRequestHandler) addConfirmation(requestID string, signerFp string,
611632}
612633
613634func (h * certRequestHandler ) maybeSignWithCa (requestID string , numSignersRequired int , signingKeyFingerprint string ) (bool , error ) {
635+ h .stateMutex .Lock ()
636+ defer h .stateMutex .Unlock ()
614637 if len (h .state [requestID ].signatures ) >= numSignersRequired {
615638 if h .sshAgentConn == nil {
616639 // This is used for testing. We're effectively disabling working
@@ -664,8 +687,8 @@ func signdFlags() []cli.Flag {
664687 Usage : "HTTP service address" ,
665688 },
666689 cli.BoolFlag {
667- Name : "reverse-proxy" ,
668- Usage : "Set when service is behind a reverse proxy, like nginx" ,
690+ Name : "reverse-proxy" ,
691+ Usage : "Set when service is behind a reverse proxy, like nginx" ,
669692 EnvVar : "SSH_CERT_AUTHORITY_PROXY" ,
670693 },
671694 }
0 commit comments