Skip to content

Commit 72a87e3

Browse files
author
Keith Smith
committed
Enforce validity period in COP for ECerts/TCerts
This change set verifies that a certificate has neither expired nor been revoked. The main change is a relatively small change in auth.go which calls cfssl code to verify the cert. The test case uses a special signing profile which expires after 1 second. To support this, I had to make a change so that the enroll and reenroll commands support using a non-default signing profile. Therefore, I changed what goes across the network of an enroll and reenroll request. Prior to this change, it only sends a CSR. After this change, it sends JSON with a CSR and an optional signing profile name (along with other optional fields). I have notified the SDK folks of this change. https://jira.hyperledger.org/browse/FAB-145 Change-Id: Idceeedadf9e5a42d995feee5bdba1353b685e9f1 Signed-off-by: Keith Smith <bksmith@us.ibm.com>
1 parent e27d3ef commit 72a87e3

File tree

11 files changed

+143
-156
lines changed

11 files changed

+143
-156
lines changed

cli/server/auth.go

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,21 @@ package server
1818

1919
import (
2020
"bytes"
21-
"encoding/hex"
2221
"errors"
2322
"io/ioutil"
2423
"net/http"
2524

2625
"github.com/cloudflare/cfssl/api"
2726
cerr "github.com/cloudflare/cfssl/errors"
2827
"github.com/cloudflare/cfssl/log"
28+
"github.com/cloudflare/cfssl/revoke"
2929
"github.com/hyperledger/fabric-cop/util"
3030
)
3131

32+
const (
33+
enrollmentIDHdrName = "__eid__"
34+
)
35+
3236
// AuthHandler
3337
type copAuthHandler struct {
3438
basic bool
@@ -106,8 +110,8 @@ func (ah *copAuthHandler) serveHTTP(w http.ResponseWriter, r *http.Request) erro
106110
if err != nil {
107111
return err
108112
}
109-
110113
log.Debug("User/Pass was correct")
114+
r.Header.Set(enrollmentIDHdrName, user)
111115
// TODO: Do the following
112116
// 2) Update state of 'user' in DB as enrolled and return true.
113117
return nil
@@ -125,22 +129,17 @@ func (ah *copAuthHandler) serveHTTP(w http.ResponseWriter, r *http.Request) erro
125129
if err2 != nil {
126130
return authError
127131
}
128-
// check status of certificate
129-
serial := cert.SerialNumber.String()
130-
aki := hex.EncodeToString(cert.AuthorityKeyId)
131-
certs, err := certDBAccessor.GetCertificate(serial, aki)
132-
if err != nil {
133-
log.Debugf("GetCertificate failed: %s", err)
134-
return authError
135-
}
136-
if len(certs) != 1 {
137-
log.Debugf("Expecting 1 certificate but found %d; serial=%s, aki=%s", len(certs), serial, aki)
132+
// Check for certificate revocation and expiration
133+
revokedOrExpired, checked := revoke.VerifyCertificate(cert)
134+
if revokedOrExpired {
135+
log.Debug("Certificate was either revoked or has expired")
138136
return authError
139137
}
140-
if certs[0].Status != "good" {
141-
log.Debugf("Auth failure - certificate status is %s", certs[0].Status)
138+
if !checked {
139+
log.Debug("A failure occurred while checking for revocation and expiration")
142140
return authError
143141
}
142+
r.Header.Set(enrollmentIDHdrName, util.GetEnrollmentIDFromX509Certificate(cert))
144143
}
145144
return nil
146145
}

cli/server/certdbaccessor.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,6 @@ func (d *CertDBAccessor) InsertCertificate(cr certdb.CertificateRecord) error {
8989
return err
9090
}
9191
id, err := util.GetEnrollmentIDFromPEM([]byte(cr.PEM))
92-
93-
err = d.checkDB()
9492
if err != nil {
9593
return err
9694
}

cli/server/enroll.go

Lines changed: 35 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -17,96 +17,68 @@ limitations under the License.
1717
package server
1818

1919
import (
20-
"errors"
20+
"fmt"
2121
"io/ioutil"
2222
"net/http"
2323

2424
"github.com/cloudflare/cfssl/api"
25-
cerr "github.com/cloudflare/cfssl/errors"
2625
"github.com/cloudflare/cfssl/log"
2726
"github.com/cloudflare/cfssl/signer"
28-
cop "github.com/hyperledger/fabric-cop/api"
27+
"github.com/hyperledger/fabric-cop/util"
2928
)
3029

31-
// enrollHandler for register requests
32-
type enrollHandler struct {
30+
// NewEnrollHandler is the constructor for the enroll handler
31+
func NewEnrollHandler() (h http.Handler, err error) {
32+
return newSignHandler("enroll")
3333
}
3434

35-
// NewEnrollHandler is constructor for register handler
36-
func NewEnrollHandler() (h http.Handler, err error) {
35+
// NewReenrollHandler is the constructor for the reenroll handler
36+
func NewReenrollHandler() (h http.Handler, err error) {
37+
return newSignHandler("reenroll")
38+
}
39+
40+
// signHandler for enroll or reenroll requests
41+
type signHandler struct {
42+
// "enroll" or "reenroll"
43+
endpoint string
44+
}
45+
46+
// newEnrollHandler is the constructor for an enroll or reenroll handler
47+
func newSignHandler(endpoint string) (h http.Handler, err error) {
3748
// NewHandler is constructor for register handler
3849
return &api.HTTPHandler{
39-
Handler: &enrollHandler{},
50+
Handler: &signHandler{endpoint: endpoint},
4051
Methods: []string{"POST"},
4152
}, nil
4253
}
4354

44-
// Handle a enroll request
45-
func (h *enrollHandler) Handle(w http.ResponseWriter, r *http.Request) error {
46-
log.Debug("enroll request received")
55+
// Handle an enroll or reenroll request.
56+
// Authentication has already occurred for both enroll and reenroll prior
57+
// to calling this function in auth.go.
58+
func (sh *signHandler) Handle(w http.ResponseWriter, r *http.Request) error {
4759

60+
log.Debugf("Received request for endpoint %s", sh.endpoint)
61+
62+
// Read the request's body
4863
body, err := ioutil.ReadAll(r.Body)
4964
if err != nil {
50-
log.Errorf("failed to read body: %s", err)
51-
return cerr.NewBadRequest(errors.New("failed to read request body"))
65+
return err
5266
}
5367
r.Body.Close()
5468

55-
user, token, ok := r.BasicAuth()
56-
if !ok {
57-
log.Error("No authorization header set")
58-
return cerr.NewBadRequest(errors.New("missing authorization header"))
59-
}
60-
61-
enroll := NewEnrollUser()
62-
cert, err := enroll.Enroll(user, []byte(token), body)
69+
// Unmarshall the request body
70+
var req signer.SignRequest
71+
err = util.Unmarshal(body, &req, sh.endpoint)
6372
if err != nil {
64-
return cerr.NewBadRequest(err)
73+
return err
6574
}
6675

67-
return api.SendResponse(w, cert)
68-
}
69-
70-
// Enroll is for enrolling a user
71-
type Enroll struct {
72-
cfg *Config
73-
}
74-
75-
// NewEnrollUser returns an pointer to an allocated enroll struct, populated
76-
// with the DB information (that set in the config) or nil on error.
77-
func NewEnrollUser() *Enroll {
78-
e := new(Enroll)
79-
e.cfg = CFG
80-
return e
81-
}
82-
83-
// Enroll will enroll an already registered user and provided an enrollment cert
84-
func (e *Enroll) Enroll(id string, token []byte, csrPEM []byte) ([]byte, cop.Error) {
85-
log.Debugf("Received request to enroll user with id: %s\n", id)
86-
87-
cert, signErr := e.signKey(csrPEM)
88-
if signErr != nil {
89-
log.Error("Failed to sign CSR - Enroll Failed")
90-
return nil, signErr
91-
}
92-
93-
return cert, nil
94-
}
95-
96-
func (e *Enroll) signKey(csrPEM []byte) ([]byte, cop.Error) {
97-
log.Debugf("signKey")
98-
req := signer.SignRequest{
99-
// Hosts: signer.SplitHosts(c.Hostname),
100-
Request: string(csrPEM),
101-
// Profile: c.Profile,
102-
// Label: c.Label,
103-
}
10476
cert, err := enrollSigner.Sign(req)
10577
if err != nil {
106-
log.Errorf("Sign error: %s", err)
107-
return nil, cop.WrapError(err, cop.CFSSL, "Failed in Sign")
78+
err = fmt.Errorf("Failed signing for endpoint %s: %s", sh.endpoint, err)
79+
log.Error(err.Error())
80+
return err
10881
}
109-
log.Debug("Sign success")
110-
return cert, nil
11182

83+
return api.SendResponse(w, cert)
11284
}

cli/server/init.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ var initFlags = []string{"remote", "u"}
4040

4141
// initMain creates the private key and self-signed certificate needed to start COP Server
4242
func initMain(args []string, c cli.Config) (err error) {
43-
csrFile, args, err := cli.PopFirstArgument(args)
43+
csrFile, _, err := cli.PopFirstArgument(args)
4444
if err != nil {
4545
return errors.New(err.Error())
4646
}

cli/server/reenroll.go

Lines changed: 0 additions & 60 deletions
This file was deleted.

cli/server/revoke.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ func (h *revokeHandler) Handle(w http.ResponseWriter, r *http.Request) error {
6363
return authErr(w, err)
6464
}
6565

66+
// Make sure that the user has the "hf.Revoker" attribute in order to be authorized
67+
// to revoke a certificate. This attribute comes from the user registry, which
68+
// is either in the DB if LDAP is not configured, or comes from LDAP if LDAP is
69+
// configured.
6670
err = userHasAttribute(cert.Subject.CommonName, "hf.Revoker")
6771
if err != nil {
6872
return authErr(w, err)

cli/server/server_test.go

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ func startServer() {
5757
}
5858

5959
if !serverStarted {
60+
os.RemoveAll(dir)
6061
serverStarted = true
6162
fmt.Println("starting COP server ...")
6263
os.Setenv("COP_DEBUG", "true")
@@ -216,10 +217,6 @@ func TestRevoke(t *testing.T) {
216217
return
217218
}
218219

219-
err = id.RevokeSelf()
220-
if err == nil {
221-
t.Error("RevokeSelf twice should have failed but did not")
222-
}
223220
}
224221

225222
func TestGetTCerts(t *testing.T) {
@@ -301,14 +298,12 @@ func TestMaxEnrollment(t *testing.T) {
301298
}
302299

303300
func TestEnroll(t *testing.T) {
304-
e := NewEnrollUser()
305-
306-
testUnregisteredUser(e, t)
307-
testIncorrectToken(e, t)
308-
testEnrollingUser(e, t)
301+
testUnregisteredUser(t)
302+
testIncorrectToken(t)
303+
testEnrollingUser(t)
309304
}
310305

311-
func testUnregisteredUser(e *Enroll, t *testing.T) {
306+
func testUnregisteredUser(t *testing.T) {
312307
copServer := `{"serverURL":"https://localhost:8888"}`
313308
c, _ := lib.NewClient(copServer)
314309

@@ -324,7 +319,7 @@ func testUnregisteredUser(e *Enroll, t *testing.T) {
324319
}
325320
}
326321

327-
func testIncorrectToken(e *Enroll, t *testing.T) {
322+
func testIncorrectToken(t *testing.T) {
328323
copServer := `{"serverURL":"https://localhost:8888"}`
329324
c, _ := lib.NewClient(copServer)
330325

@@ -340,7 +335,7 @@ func testIncorrectToken(e *Enroll, t *testing.T) {
340335
}
341336
}
342337

343-
func testEnrollingUser(e *Enroll, t *testing.T) {
338+
func testEnrollingUser(t *testing.T) {
344339
copServer := `{"serverURL":"https://localhost:8888"}`
345340
c, _ := lib.NewClient(copServer)
346341

@@ -389,6 +384,34 @@ func TestUpdateField(t *testing.T) {
389384
}
390385
}
391386

387+
func TestExpiration(t *testing.T) {
388+
389+
copServer := `{"serverURL":"https://localhost:8888"}`
390+
c, _ := lib.NewClient(copServer)
391+
392+
// Enroll this user using the "expiry" profile which is configured
393+
// to expire after 1 second
394+
regReq := &idp.EnrollmentRequest{
395+
Name: "expiryUser",
396+
Secret: "expirypw",
397+
Profile: "expiry",
398+
}
399+
400+
id, err := c.Enroll(regReq)
401+
if err != nil {
402+
t.Error("enroll of user 'admin' with password 'adminpw' failed")
403+
return
404+
}
405+
406+
t.Log("Sleeping 5 seconds waiting for certificate to expire")
407+
time.Sleep(5 * time.Second)
408+
t.Log("Done sleeping")
409+
err = id.RevokeSelf()
410+
if err == nil {
411+
t.Error("certificate should have expired but did not")
412+
}
413+
}
414+
392415
func TestUserRegistry(t *testing.T) {
393416

394417
err := InitUserRegistry(&Config{DBdriver: "postgres", DataSource: "dbname=cop sslmode=disable"})

0 commit comments

Comments
 (0)