Skip to content

Commit

Permalink
Revoked user should not be able to make requests
Browse files Browse the repository at this point in the history
Once a user has been revoked or if it's certificate has
been revoked, it should not be able to continue to make requests
such as requesting a tcert batch or registering a new user.

This will add the necessary logic to check the status of the
certificate in the token to make sure that it is not revoked.

https://jira.hyperledger.org/browse/FAB-2668

Change-Id: I6b9d66796d968cf44e2aeb49fd3324ea2e75a7e8
Signed-off-by: Saad Karim <skarim@us.ibm.com>
  • Loading branch information
Saad Karim committed Mar 23, 2017
1 parent 4f0314e commit e9bc7ff
Show file tree
Hide file tree
Showing 8 changed files with 116 additions and 15 deletions.
1 change: 1 addition & 0 deletions cmd/fabric-ca-client/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ func testConfigFileTypes(t *testing.T) {

// Reset the config file name
cfgFileName = util.GetDefaultConfigFile("fabric-ca-client")
os.RemoveAll("./config")
}

// TestGetCACert tests fabric-ca-client getcacert
Expand Down
6 changes: 5 additions & 1 deletion lib/certdbaccessor.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"errors"
"fmt"
"math/big"
"strings"
"time"

"github.com/cloudflare/cfssl/certdb"
Expand Down Expand Up @@ -101,11 +102,14 @@ func (d *CertDBAccessor) InsertCertificate(cr certdb.CertificateRecord) error {
ip.SetString(cr.Serial, 10) //base 10

serial := util.GetSerialAsHex(ip)
aki := strings.TrimLeft(cr.AKI, "0")

log.Debug("Saved serial number as hex ", serial)

var record = new(CertRecord)
record.ID = id
record.Serial = serial
record.AKI = cr.AKI
record.AKI = aki
record.CALabel = cr.CALabel
record.Status = cr.Status
record.Reason = cr.Reason
Expand Down
2 changes: 1 addition & 1 deletion lib/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,7 @@ func (c *Client) SendReq(req *http.Request, result interface{}) (err error) {
body = new(cfsslapi.Response)
err = json.Unmarshal(respBody, body)
if err != nil {
return fmt.Errorf("Failed to parse response [%s] for request:\n%s", err, reqStr)
return fmt.Errorf("Failed to parse response: %s\n%s", err, respBody)
}
if len(body.Errors) > 0 {
msg := body.Errors[0].Message
Expand Down
2 changes: 1 addition & 1 deletion lib/dbaccessor.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ DELETE FROM users

updateUser = `
UPDATE users
SET token = :token, type = :type, affiliation = :affiliation, attributes = :attributes
SET token = :token, type = :type, affiliation = :affiliation, attributes = :attributes, state = :state
WHERE (id = :id);`

getUser = `
Expand Down
83 changes: 81 additions & 2 deletions lib/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ limitations under the License.
package lib_test

import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
"os"
"path"
"strconv"
Expand All @@ -28,6 +31,7 @@ import (
. "github.com/hyperledger/fabric-ca/lib"
"github.com/hyperledger/fabric-ca/lib/tls"
"github.com/hyperledger/fabric-ca/util"
"github.com/hyperledger/fabric/bccsp/factory"
)

const (
Expand Down Expand Up @@ -140,12 +144,11 @@ func TestRootServer(t *testing.T) {
t.Fatalf("Failed to revoke user1's identity: %s", err)
}
// User1 should not be allowed to get tcerts now that it is revoked
/* FIXME: The call to revoke.VerifyCertificate in serverauth.go should fail
_, err = user1.GetTCertBatch(&api.GetTCertBatchRequest{Count: 1})
if err == nil {
t.Errorf("User1 should have failed to get tcerts since it is revoked")
}
*/

// Stop the server
err = server.Stop()
if err != nil {
Expand Down Expand Up @@ -236,7 +239,83 @@ func TestDefaultDatabase(t *testing.T) {
if !exist {
t.Error("Failed to create default sqlite fabric-ca-server.db")
}
}

func TestBadAuthHeader(t *testing.T) {
// Start the server
server := getRootServer(t)
if server == nil {
return
}
err := server.Start()
if err != nil {
t.Fatalf("Server start failed: %s", err)
}

time.Sleep(time.Second)

invalidTokenAuthorization(t)
invalidBasicAuthorization(t)

err = server.Stop()
if err != nil {
t.Errorf("Server stop failed: %s", err)
}

}

func invalidTokenAuthorization(t *testing.T) {
client := getRootClient()

emptyByte := make([]byte, 0)

req, err := http.NewRequest("POST", "http://localhost:7055/enroll", bytes.NewReader(emptyByte))
if err != nil {
t.Error(err)
}

CSP := factory.GetDefault()

cert, err := ioutil.ReadFile("../testdata/ec.pem")
if err != nil {
t.Error(err)
}

key, err := ioutil.ReadFile("../testdata/ec-key.pem")
if err != nil {
t.Error(err)
}

token, err := util.CreateToken(CSP, cert, key, emptyByte)
if err != nil {
t.Errorf("Failed to add token authorization header: %s", err)
}

req.Header.Set("authorization", token)

err = client.SendReq(req, nil)

if err.Error() != "Error response from server was: Authorization failure" {
t.Error("Incorrect auth type set, request should have failed with authorization error")
}
}

func invalidBasicAuthorization(t *testing.T) {
client := getRootClient()

emptyByte := make([]byte, 0)

req, err := http.NewRequest("POST", "http://localhost:7055/register", bytes.NewReader(emptyByte))
if err != nil {
t.Error(err)
}

req.SetBasicAuth("admin", "adminpw")

err = client.SendReq(req, nil)
if err.Error() != "Error response from server was: Authorization failure" {
t.Error("Incorrect auth type set, request should have failed with authorization error")
}
}

func testIntermediateServer(idx int, t *testing.T) {
Expand Down
27 changes: 25 additions & 2 deletions lib/serverauth.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package lib

import (
"bytes"
"encoding/hex"
"errors"
"io/ioutil"
"net/http"
Expand Down Expand Up @@ -110,23 +111,45 @@ func (ah *fcaAuthHandler) serveHTTP(w http.ResponseWriter, r *http.Request) erro
}
id := util.GetEnrollmentIDFromX509Certificate(cert)
log.Debugf("Checking for revocation/expiration of certificate owned by '%s'", id)
// Check for certificate revocation and expiration

// VerifyCertificate ensures that the certificate passed in hasn't
// expired and checks the CRL for the server.
revokedOrExpired, checked := revoke.VerifyCertificate(cert)
if revokedOrExpired {
log.Debugf("Certificate was either revoked or has expired owned by '%s'", id)
log.Debugf("Certificate owned by '%s' has expired", id)
return authError
}
if !checked {
log.Debug("A failure occurred while checking for revocation and expiration")
return authError
}

aki := hex.EncodeToString(cert.AuthorityKeyId)
serial := util.GetSerialAsHex(cert.SerialNumber)

certs, err := ah.server.CertDBAccessor().GetCertificate(serial, aki)
if err != nil {
return authError
}

if len(certs) == 0 {
return authError
}

for _, certificate := range certs {
if certificate.Status == "revoked" {
return authError
}
}

log.Debugf("Successful authentication of '%s'", id)
r.Header.Set(enrollmentIDHdrName, util.GetEnrollmentIDFromX509Certificate(cert))
return nil
default: // control should never reach here
log.Errorf("No handler for the authentication type: %d", ah.authType)
return authError
}

}

func wrappedPath(path string) string {
Expand Down
4 changes: 2 additions & 2 deletions lib/serverrevoke.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ func (h *revokeHandler) Handle(w http.ResponseWriter, r *http.Request) error {

log.Debugf("Revoke request: %+v", req)

req.AKI = strings.ToLower(req.AKI)
req.Serial = strings.ToLower(req.Serial)
req.AKI = strings.TrimLeft(strings.ToLower(req.AKI), "0")
req.Serial = strings.TrimLeft(strings.ToLower(req.Serial), "0")

certDBAccessor := h.server.certDBAccessor
registry := h.server.registry
Expand Down
6 changes: 0 additions & 6 deletions util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ import (
"regexp"
"strings"
"time"
"unicode/utf8"

"golang.org/x/crypto/ocsp"

Expand Down Expand Up @@ -558,11 +557,6 @@ func GetKeyFromBytes(csp bccsp.BCCSP, key []byte) (bccsp.Key, error) {
// GetSerialAsHex returns the serial number from certificate as hex format
func GetSerialAsHex(serial *big.Int) string {
hex := fmt.Sprintf("%x", serial)

if utf8.RuneCountInString(hex) < 80 {
hex = fmt.Sprintf("0%s", hex)
}

return hex
}

Expand Down

0 comments on commit e9bc7ff

Please sign in to comment.