diff --git a/cmd/fabric-ca-client/config.go b/cmd/fabric-ca-client/config.go index 004095228..37c0e47bc 100644 --- a/cmd/fabric-ca-client/config.go +++ b/cmd/fabric-ca-client/config.go @@ -267,11 +267,9 @@ func processAttributes() { func checkForEnrollment() error { log.Debug("Checking for enrollment") - client := lib.Client{ HomeDir: filepath.Dir(cfgFileName), Config: clientCfg, } - - return client.Enrollment() + return client.CheckEnrollment() } diff --git a/cmd/fabric-ca-client/enroll.go b/cmd/fabric-ca-client/enroll.go index abfe679cb..9c06cfd4d 100644 --- a/cmd/fabric-ca-client/enroll.go +++ b/cmd/fabric-ca-client/enroll.go @@ -69,11 +69,13 @@ func runEnroll(cmd *cobra.Command) error { return err } - ID, err := clientCfg.Enroll(clientCfg.URL, filepath.Dir(cfgFileName)) + resp, err := clientCfg.Enroll(clientCfg.URL, filepath.Dir(cfgFileName)) if err != nil { return err } + ID := resp.Identity + cfgFile, err := ioutil.ReadFile(cfgFileName) if err != nil { return err @@ -91,8 +93,10 @@ func runEnroll(cmd *cobra.Command) error { return fmt.Errorf("Failed to store enrollment information: %s", err) } - log.Infof("Enrollment information was successfully stored in %s and %s", - ID.GetMyKeyFile(), ID.GetMyCertFile()) + err = storeCAChain(clientCfg, &resp.ServerInfo) + if err != nil { + return err + } return nil } diff --git a/cmd/fabric-ca-client/getcacert.go b/cmd/fabric-ca-client/getcacert.go index b7bbab4d7..94a75440e 100644 --- a/cmd/fabric-ca-client/getcacert.go +++ b/cmd/fabric-ca-client/getcacert.go @@ -64,12 +64,12 @@ func runGetCACert() error { return err } - return storeCAChain(client, si) + return storeCAChain(client.Config, si) } // Store the CAChain in the CACerts folder of MSP (Membership Service Provider) -func storeCAChain(client *lib.Client, si *lib.GetServerInfoResponse) error { - mspDir := client.Config.MSPDir +func storeCAChain(config *lib.ClientConfig, si *lib.GetServerInfoResponse) error { + mspDir := config.MSPDir if !util.FileExists(mspDir) { return fmt.Errorf("Directory does not exist: %s", mspDir) } diff --git a/cmd/fabric-ca-client/main_test.go b/cmd/fabric-ca-client/main_test.go index 78562af3d..719c644c5 100644 --- a/cmd/fabric-ca-client/main_test.go +++ b/cmd/fabric-ca-client/main_test.go @@ -37,6 +37,7 @@ import ( const ( testYaml = "../../testdata/test.yaml" + mspDir = "../../testdata/msp" myhost = "hostname" certfile = "ec.pem" keyfile = "ec-key.pem" @@ -135,7 +136,7 @@ func testGetCACert(t *testing.T) { t.Log("Testing getcacert") defYaml = util.GetDefaultConfigFile("fabric-ca-client") os.Remove(defYaml) // Clean up any left over config file - os.RemoveAll("cacerts") + os.RemoveAll("msp") err := RunMain([]string{cmdName, "getcacert", "-d", "-u", "http://localhost:7054"}) if err != nil { t.Errorf("getcainfo failed: %s", err) @@ -149,6 +150,7 @@ func testGetCACert(t *testing.T) { t.Error("getcacert with no URL should have failed but did not") } os.RemoveAll("cacerts") + os.RemoveAll("msp") os.Remove(defYaml) } @@ -451,6 +453,7 @@ func TestCleanUp(t *testing.T) { os.Remove("../../testdata/key.pem") os.Remove(testYaml) os.Remove(fabricCADB) + os.RemoveAll(mspDir) } func TestRegisterWithoutEnroll(t *testing.T) { diff --git a/cmd/fabric-ca-client/reenroll.go b/cmd/fabric-ca-client/reenroll.go index 331827dad..8e3cd1041 100644 --- a/cmd/fabric-ca-client/reenroll.go +++ b/cmd/fabric-ca-client/reenroll.go @@ -81,18 +81,20 @@ func runReenroll() error { CSR: &clientCfg.CSR, } - newID, err := id.Reenroll(req) + resp, err := id.Reenroll(req) if err != nil { return fmt.Errorf("Failed to store enrollment information: %s", err) } - err = newID.Store() + err = resp.Identity.Store() if err != nil { return err } - log.Infof("Enrollment information was successfully stored in %s and %s", - client.GetMyKeyFile(), client.GetMyCertFile()) + err = storeCAChain(clientCfg, &resp.ServerInfo) + if err != nil { + return err + } return nil } diff --git a/lib/client.go b/lib/client.go index 333afd4a2..30f924b5f 100644 --- a/lib/client.go +++ b/lib/client.go @@ -18,7 +18,6 @@ package lib import ( "bytes" - "encoding/base64" "encoding/json" "errors" "fmt" @@ -96,9 +95,47 @@ func NewClient(configFile string) (*Client, error) { type Client struct { // HomeDir is the home directory HomeDir string `json:"homeDir,omitempty"` - // The client's configuration - Config *ClientConfig + Config *ClientConfig + initialized bool + keyFile, certFile, caCertsDir string +} + +// Init initializes the client +func (c *Client) Init() error { + if !c.initialized { + cfg := c.Config + if cfg.MSPDir == "" { + cfg.MSPDir = "msp" + } + mspDir, err := util.MakeFileAbs(cfg.MSPDir, c.HomeDir) + if err != nil { + return err + } + cfg.MSPDir = mspDir + // Key directory and file + keyDir := path.Join(mspDir, "keystore") + err = os.MkdirAll(keyDir, 0700) + if err != nil { + return fmt.Errorf("Failed to create keystore directory: %s", err) + } + c.keyFile = path.Join(keyDir, "key.pem") + // Cert directory and file + certDir := path.Join(mspDir, "signcerts") + err = os.MkdirAll(certDir, 0755) + if err != nil { + return fmt.Errorf("Failed to create signcerts directory: %s", err) + } + c.certFile = path.Join(certDir, "cert.pem") + // CA certs directory + c.caCertsDir = path.Join(mspDir, "cacerts") + err = os.MkdirAll(c.caCertsDir, 0755) + if err != nil { + return fmt.Errorf("Failed to create cacerts directory: %s", err) + } + c.initialized = true + } + return nil } // GetServerInfoResponse is the response from the GetServerInfo call @@ -112,31 +149,54 @@ type GetServerInfoResponse struct { // GetServerInfo returns generic server information func (c *Client) GetServerInfo() (*GetServerInfoResponse, error) { - req, err := c.NewGet("info") + err := c.Init() if err != nil { return nil, err } - sirn := &serverInfoResponseNet{} - err = c.SendReq(req, sirn) + req, err := c.newGet("info") if err != nil { return nil, err } - caChain, err := util.B64Decode(sirn.CAChain) + netSI := &serverInfoResponseNet{} + err = c.SendReq(req, netSI) if err != nil { return nil, err } - si := &GetServerInfoResponse{ - CAName: sirn.CAName, - CAChain: caChain, + localSI := &GetServerInfoResponse{} + err = c.net2LocalServerInfo(netSI, localSI) + if err != nil { + return nil, err + } + return localSI, nil +} + +// Convert from network to local server information +func (c *Client) net2LocalServerInfo(net *serverInfoResponseNet, local *GetServerInfoResponse) error { + caChain, err := util.B64Decode(net.CAChain) + if err != nil { + return err } - return si, nil + local.CAName = net.CAName + local.CAChain = caChain + return nil +} + +// EnrollmentResponse is the response from Client.Enroll and Identity.Reenroll +type EnrollmentResponse struct { + Identity *Identity + ServerInfo GetServerInfoResponse } // Enroll enrolls a new identity // @param req The enrollment request -func (c *Client) Enroll(req *api.EnrollmentRequest) (*Identity, error) { +func (c *Client) Enroll(req *api.EnrollmentRequest) (*EnrollmentResponse, error) { log.Debugf("Enrolling %+v", &req) + err := c.Init() + if err != nil { + return nil, err + } + // Generate the CSR csrPEM, key, err := c.GenCSR(req.CSR, req.Name) if err != nil { @@ -157,38 +217,50 @@ func (c *Client) Enroll(req *api.EnrollmentRequest) (*Identity, error) { } // Send the CSR to the fabric-ca server with basic auth header - post, err := c.NewPost("enroll", body) + post, err := c.newPost("enroll", body) if err != nil { return nil, err } post.SetBasicAuth(req.Name, req.Secret) - var result string + var result enrollmentResponseNet err = c.SendReq(post, &result) if err != nil { return nil, err } - // Create an identity from the key and certificate in the response - return c.newIdentityFromResponse(result, req.Name, key) + // Create the enrollment response + return c.newEnrollmentResponse(&result, req.Name, key) } -// newIdentityFromResponse returns an Identity for enroll and reenroll responses +// newEnrollmentResponse creates a client enrollment response from a network response // @param result The result from server // @param id Name of identity being enrolled or reenrolled // @param key The private key which was used to sign the request -func (c *Client) newIdentityFromResponse(result interface{}, id string, key []byte) (*Identity, error) { - log.Debugf("newIdentityFromResponse %s", id) - certByte, err := base64.StdEncoding.DecodeString(result.(string)) +func (c *Client) newEnrollmentResponse(result *enrollmentResponseNet, id string, key []byte) (*EnrollmentResponse, error) { + log.Debugf("newEnrollmentResponse %s", id) + certByte, err := util.B64Decode(result.Cert) if err != nil { return nil, fmt.Errorf("Invalid response format from server: %s", err) } - return newIdentity(c, id, key, certByte), nil + resp := &EnrollmentResponse{ + Identity: newIdentity(c, id, key, certByte), + } + err = c.net2LocalServerInfo(&result.ServerInfo, &resp.ServerInfo) + if err != nil { + return nil, err + } + return resp, nil } // GenCSR generates a CSR (Certificate Signing Request) func (c *Client) GenCSR(req *api.CSRInfo, id string) ([]byte, []byte, error) { log.Debugf("GenCSR %+v", &req) + err := c.Init() + if err != nil { + return nil, nil, err + } + cr := c.newCertificateRequest(req) cr.CN = id @@ -230,49 +302,34 @@ func (c *Client) newCertificateRequest(req *api.CSRInfo) *csr.CertificateRequest // LoadMyIdentity loads the client's identity from disk func (c *Client) LoadMyIdentity() (*Identity, error) { - return c.LoadIdentity(c.GetMyKeyFile(), c.GetMyCertFile()) + err := c.Init() + if err != nil { + return nil, err + } + return c.loadIdentity(c.keyFile, c.certFile) } // StoreMyIdentity stores my identity to disk func (c *Client) StoreMyIdentity(key, cert []byte) error { - err := util.WriteFile(c.GetMyKeyFile(), key, 0600) + err := c.Init() if err != nil { return err } - return util.WriteFile(c.GetMyCertFile(), cert, 0644) -} - -// GetMyKeyFile returns the path to this identity's key file -func (c *Client) GetMyKeyFile() string { - file := os.Getenv("FABRIC_CA_KEY_FILE") - if file == "" { - file = path.Join(c.GetMyEnrollmentDir(), "key.pem") - } - log.Debugf("Key file location: %s", file) - return file -} - -// GetMyCertFile returns the path to this identity's certificate file -func (c *Client) GetMyCertFile() string { - file := os.Getenv("FABRIC_CA_CERT_FILE") - if file == "" { - file = path.Join(c.GetMyEnrollmentDir(), "cert.pem") + err = util.WriteFile(c.keyFile, key, 0600) + if err != nil { + return fmt.Errorf("Failed to store my key: %s", err) } - log.Debugf("Cert file location: %s", file) - return file -} - -// GetMyEnrollmentDir returns the path to this identity's enrollment directory -func (c *Client) GetMyEnrollmentDir() string { - dir := os.Getenv("FABRIC_CA_ENROLLMENT_DIR") - if dir == "" { - dir = c.HomeDir + log.Infof("Stored client key at %s", c.keyFile) + err = util.WriteFile(c.certFile, cert, 0644) + if err != nil { + return fmt.Errorf("Failed to store my certificate: %s", err) } - return dir + log.Infof("Stored client certificate at %s", c.certFile) + return nil } -// LoadIdentity loads an identity from disk -func (c *Client) LoadIdentity(keyFile, certFile string) (*Identity, error) { +// loadIdentity loads an identity from disk +func (c *Client) loadIdentity(keyFile, certFile string) (*Identity, error) { key, err := util.ReadFile(keyFile) if err != nil { return nil, err @@ -281,11 +338,11 @@ func (c *Client) LoadIdentity(keyFile, certFile string) (*Identity, error) { if err != nil { return nil, err } - return c.NewIdentity(key, cert) + return c.newIdentity(key, cert) } // NewIdentity creates a new identity -func (c *Client) NewIdentity(key, cert []byte) (*Identity, error) { +func (c *Client) newIdentity(key, cert []byte) (*Identity, error) { name, err := util.GetEnrollmentIDFromPEM(cert) if err != nil { return nil, err @@ -309,7 +366,7 @@ func (c *Client) LoadCSRInfo(path string) (*api.CSRInfo, error) { } // NewGet create a new GET request -func (c *Client) NewGet(endpoint string) (*http.Request, error) { +func (c *Client) newGet(endpoint string) (*http.Request, error) { curl, err := c.getURL(endpoint) if err != nil { return nil, err @@ -322,7 +379,7 @@ func (c *Client) NewGet(endpoint string) (*http.Request, error) { } // NewPost create a new post request -func (c *Client) NewPost(endpoint string, reqBody []byte) (*http.Request, error) { +func (c *Client) newPost(endpoint string, reqBody []byte) (*http.Request, error) { curl, err := c.getURL(endpoint) if err != nil { return nil, err @@ -335,23 +392,29 @@ func (c *Client) NewPost(endpoint string, reqBody []byte) (*http.Request, error) } // SendReq sends a request to the fabric-ca-server and fills in the result -func (c *Client) SendReq(req *http.Request, result interface{}) error { +func (c *Client) SendReq(req *http.Request, result interface{}) (err error) { + reqStr := util.HTTPRequestToString(req) log.Debugf("Sending request\n%s", reqStr) + err = c.Init() + if err != nil { + return err + } + var tr = new(http.Transport) if c.Config.TLS.Enabled { log.Info("TLS Enabled") - err := tls.AbsTLSClient(&c.Config.TLS, c.HomeDir) + err = tls.AbsTLSClient(&c.Config.TLS, c.HomeDir) if err != nil { return err } - tlsConfig, err := tls.GetClientTLSConfig(&c.Config.TLS) - if err != nil { - return fmt.Errorf("Failed to get client TLS config: %s", err) + tlsConfig, err2 := tls.GetClientTLSConfig(&c.Config.TLS) + if err2 != nil { + return fmt.Errorf("Failed to get client TLS config: %s", err2) } tr.TLSClientConfig = tlsConfig @@ -409,9 +472,13 @@ func (c *Client) getURL(endpoint string) (string, error) { return rtn, nil } -// Enrollment checks to see if client is enrolled (i.e. enrollment information exists) -func (c *Client) Enrollment() error { - if !util.FileExists(c.GetMyCertFile()) || !util.FileExists(c.GetMyKeyFile()) { +// CheckEnrollment returns an error if this client is not enrolled +func (c *Client) CheckEnrollment() error { + err := c.Init() + if err != nil { + return err + } + if !util.FileExists(c.certFile) || !util.FileExists(c.keyFile) { return errors.New("Enrollment information does not exist. Please execute enroll command first. Example: fabric-ca-client enroll -u http://user:userpw@serverAddr:serverPort") } return nil diff --git a/lib/client_test.go b/lib/client_test.go index e83e95e9e..59f4a9568 100644 --- a/lib/client_test.go +++ b/lib/client_test.go @@ -90,17 +90,17 @@ func testRegister(c *Client, t *testing.T) { Secret: "adminpw", } - id, err := c.Enroll(enrollReq) + eresp, err := c.Enroll(enrollReq) if err != nil { t.Fatalf("testRegister enroll of admin failed: %s", err) } - err = id.Store() + err = eresp.Identity.Store() if err != nil { t.Fatalf("testRegister failed to store admin identity: %s", err) } - adminID = id + adminID = eresp.Identity // Register as admin registerReq := &api.RegistrationRequest{ @@ -110,7 +110,7 @@ func testRegister(c *Client, t *testing.T) { MaxEnrollments: 1, } - resp, err := id.Register(registerReq) + resp, err := adminID.Register(registerReq) if err != nil { t.Fatalf("Register failed: %s", err) } @@ -120,10 +120,11 @@ func testRegister(c *Client, t *testing.T) { Secret: resp.Secret, } - id, err = c.Enroll(req) + eresp, err = c.Enroll(req) if err != nil { t.Fatalf("Enroll failed: %s", err) } + id := eresp.Identity if id.GetName() != "MyTestUser" { t.Fatal("Incorrect name retrieved") @@ -172,11 +173,12 @@ func testReenroll(c *Client, t *testing.T) { t.Errorf("testReenroll: failed LoadMyIdentity: %s", err) return } - id, err = id.Reenroll(&api.ReenrollmentRequest{}) + eresp, err := id.Reenroll(&api.ReenrollmentRequest{}) if err != nil { t.Errorf("testReenroll: failed reenroll: %s", err) return } + id = eresp.Identity err = id.Store() if err != nil { t.Errorf("testReenroll: failed Store: %s", err) @@ -201,11 +203,12 @@ func testRevocation(c *Client, t *testing.T, user string, withPriv, ecertOnly bo Name: user, Secret: resp.Secret, } - id, err := c.Enroll(req) + eresp, err := c.Enroll(req) if err != nil { t.Errorf("enroll of user '%s' failed", user) return } + id := eresp.Identity if ecertOnly { err = id.GetECert().RevokeSelf() } else { @@ -281,10 +284,11 @@ func testTooManyEnrollments(t *testing.T) { t.Errorf("Failed to enroll: %s", err) } - id, err := clientConfig.Enroll(rawURL, testdataDir) + eresp, err := clientConfig.Enroll(rawURL, testdataDir) if err != nil { t.Errorf("Failed to enroll: %s", err) } + id := eresp.Identity _, err = clientConfig.Enroll(rawURL, testdataDir) if err == nil { @@ -318,15 +322,15 @@ func testIncorrectEnrollment(t *testing.T) { func TestNormalizeUrl(t *testing.T) { _, err := NormalizeURL("") if err != nil { - t.Errorf("NormalizeURL empty: %s", err) + t.Errorf("normalizeURL empty: %s", err) } _, err = NormalizeURL("http://host:7054:x/path") if err != nil { - t.Errorf("NormalizeURL colons: %s", err) + t.Errorf("normalizeURL colons: %s", err) } _, err = NormalizeURL("http://host:7054/path") if err != nil { - t.Errorf("NormalizeURL failed: %s", err) + t.Errorf("normalizeURL failed: %s", err) } } @@ -354,7 +358,6 @@ func getClient() *Client { func TestLast(t *testing.T) { // Cleanup - os.Remove("../testdata/cert.pem") - os.Remove("../testdata/key.pem") + os.RemoveAll("../testdata/msp") os.RemoveAll(serversDir) } diff --git a/lib/clientconfig.go b/lib/clientconfig.go index 444d1ac0b..d90da2d7f 100644 --- a/lib/clientconfig.go +++ b/lib/clientconfig.go @@ -27,7 +27,7 @@ import ( type ClientConfig struct { Debug bool `def:"false" opt:"d" help:"Enable debug level logging"` URL string `def:"http://localhost:7054" opt:"u" help:"URL of fabric-ca-server"` - MSPDir string `def:"." opt:"M" help:"Membership Service Provider directory"` + MSPDir string `def:"msp" opt:"M" help:"Membership Service Provider directory"` TLS tls.ClientTLSConfig Enrollment api.EnrollmentRequest CSR api.CSRInfo @@ -37,7 +37,7 @@ type ClientConfig struct { // Enroll a client given the server's URL and the client's home directory. // The URL may be of the form: http://user:pass@host:port where user and pass // are the enrollment ID and secret, respectively. -func (c *ClientConfig) Enroll(rawurl, home string) (id *Identity, err error) { +func (c *ClientConfig) Enroll(rawurl, home string) (*EnrollmentResponse, error) { purl, err := url.Parse(rawurl) if err != nil { return nil, err diff --git a/lib/identity.go b/lib/identity.go index 85ec04c7b..5bfe91eb2 100644 --- a/lib/identity.go +++ b/lib/identity.go @@ -100,7 +100,7 @@ func (i *Identity) Register(req *api.RegistrationRequest) (rr *api.RegistrationR // Reenroll reenrolls an existing Identity and returns a new Identity // @param req The reenrollment request -func (i *Identity) Reenroll(req *api.ReenrollmentRequest) (*Identity, error) { +func (i *Identity) Reenroll(req *api.ReenrollmentRequest) (*EnrollmentResponse, error) { log.Debugf("Reenrolling %s", &req) csrPEM, key, err := i.client.GenCSR(req.CSR, i.GetName()) @@ -119,12 +119,12 @@ func (i *Identity) Reenroll(req *api.ReenrollmentRequest) (*Identity, error) { if err != nil { return nil, err } - var result string + var result enrollmentResponseNet err = i.Post("reenroll", body, &result) if err != nil { return nil, err } - return i.client.newIdentityFromResponse(result, i.GetName(), key) + return i.client.newEnrollmentResponse(&result, i.GetName(), key) } // Revoke the identity associated with 'id' @@ -165,7 +165,7 @@ func (i *Identity) Store() error { // of this identity over the body and non-signature part of the authorization header. // The return value is the body of the response. func (i *Identity) Post(endpoint string, reqBody []byte, result interface{}) error { - req, err := i.client.NewPost(endpoint, reqBody) + req, err := i.client.newPost(endpoint, reqBody) if err != nil { return err } @@ -190,13 +190,3 @@ func (i *Identity) addTokenAuthHdr(req *http.Request, body []byte) error { req.Header.Set("authorization", token) return nil } - -// GetMyKeyFile returns the path to this identity's key file -func (i *Identity) GetMyKeyFile() string { - return i.client.GetMyKeyFile() -} - -// GetMyCertFile returns the path to this identity's key file -func (i *Identity) GetMyCertFile() string { - return i.client.GetMyCertFile() -} diff --git a/lib/server.go b/lib/server.go index fb0f732fe..6135eb09b 100644 --- a/lib/server.go +++ b/lib/server.go @@ -24,6 +24,7 @@ import ( "net" "net/http" "os" + "path" "path/filepath" "strconv" @@ -210,17 +211,35 @@ func (s *Server) getCACertAndKey() (cert, key []byte, err error) { clientCfg.Enrollment.CSR.CA = &cfcsr.CAConfig{PathLength: 0, PathLenZero: true} } log.Debugf("Intermediate enrollment request: %v", clientCfg.Enrollment) - var id *Identity - id, err = clientCfg.Enroll(s.ParentServerURL, s.HomeDir) + var resp *EnrollmentResponse + resp, err = clientCfg.Enroll(s.ParentServerURL, s.HomeDir) if err != nil { return nil, nil, err } - ecert := id.GetECert() + ecert := resp.Identity.GetECert() if ecert == nil { return nil, nil, errors.New("No ECert from parent server") } cert = ecert.Cert() key = ecert.Key() + // Store the chain file as the concatenation of the parent's chain plus the cert. + chainPath := s.Config.CA.Chainfile + if chainPath == "" { + chainPath, err = util.MakeFileAbs("ca-chain.pem", s.HomeDir) + if err != nil { + return nil, nil, fmt.Errorf("Failed to create intermediate chain file path: %s", err) + } + } + chain := s.concatChain(resp.ServerInfo.CAChain, cert) + err = os.MkdirAll(path.Dir(chainPath), 0755) + if err != nil { + return nil, nil, fmt.Errorf("Failed to create intermediate chain file directory: %s", err) + } + err = util.WriteFile(chainPath, chain, 0644) + if err != nil { + return nil, nil, fmt.Errorf("Failed to create intermediate chain file: %s", err) + } + log.Debugf("Stored intermediate certificate chain at %s", chainPath) } else { // This is a root CA, so call cfssl to get the key and cert. csr := &s.Config.CSR @@ -242,6 +261,14 @@ func (s *Server) getCACertAndKey() (cert, key []byte, err error) { return cert, key, nil } +// Return a chain which is the concatenation of chain and cert +func (s *Server) concatChain(chain []byte, cert []byte) []byte { + result := make([]byte, len(chain)+len(cert)) + copy(result[:len(chain)], chain) + copy(result[len(chain):], cert) + return result +} + // Get the CA chain func (s *Server) getCAChain() (chain []byte, err error) { if s.Config == nil { @@ -708,6 +735,7 @@ func (s *Server) makeFileNamesAbsolute() error { fields := []*string{ &s.Config.CA.Certfile, &s.Config.CA.Keyfile, + &s.Config.CA.Chainfile, &s.Config.TLS.CertFile, &s.Config.TLS.KeyFile, } @@ -758,6 +786,17 @@ func (s *Server) getUserAffiliation(username string) (string, error) { return aff, nil } +// Fill the server info structure appropriately +func (s *Server) fillServerInfo(info *serverInfoResponseNet) error { + caChain, err := s.getCAChain() + if err != nil { + return err + } + info.CAName = s.Config.CA.Name + info.CAChain = util.B64Encode(caChain) + return nil +} + func writeFile(file string, buf []byte, perm os.FileMode) error { err := os.MkdirAll(filepath.Dir(file), 0755) if err != nil { diff --git a/lib/server_test.go b/lib/server_test.go index d713ec0f2..e7f928ee1 100644 --- a/lib/server_test.go +++ b/lib/server_test.go @@ -75,13 +75,14 @@ func TestRootServer(t *testing.T) { defer server.Stop() // Enroll request client := getRootClient() - admin, err = client.Enroll(&api.EnrollmentRequest{ + eresp, err := client.Enroll(&api.EnrollmentRequest{ Name: "admin", Secret: "adminpw", }) if err != nil { t.Fatalf("Failed to enroll admin/adminpw: %s", err) } + admin = eresp.Identity // Register user1 rr, err = admin.Register(&api.RegistrationRequest{ Name: "user1", @@ -92,13 +93,14 @@ func TestRootServer(t *testing.T) { t.Fatalf("Failed to register user1: %s", err) } // Enroll user1 - user1, err = client.Enroll(&api.EnrollmentRequest{ + eresp, err = client.Enroll(&api.EnrollmentRequest{ Name: "user1", Secret: rr.Secret, }) if err != nil { t.Fatalf("Failed to enroll user1: %s", err) } + user1 = eresp.Identity // The admin ID should have 1 cert in the DB now recs, err = server.CertDBAccessor().GetCertificatesByID("admin") if err != nil { @@ -117,10 +119,11 @@ func TestRootServer(t *testing.T) { t.Errorf("Failed to register user1: %s", err) } // User1 renew - user1, err = user1.Reenroll(&api.ReenrollmentRequest{}) + eresp, err = user1.Reenroll(&api.ReenrollmentRequest{}) if err != nil { t.Fatalf("Failed to reenroll user1: %s", err) } + user1 = eresp.Identity // User1 should not be allowed to revoke admin err = user1.Revoke(&api.RevocationRequest{Name: "admin"}) if err == nil { diff --git a/lib/serverenroll.go b/lib/serverenroll.go index bdfb5fb7e..1a0a01188 100644 --- a/lib/serverenroll.go +++ b/lib/serverenroll.go @@ -55,7 +55,15 @@ type signHandler struct { endpoint string } -// newEnrollHandler is the constructor for an enroll or reenroll handler +// The enrollment response from the server +type enrollmentResponseNet struct { + // Base64 encoded PEM-encoded ECert + Cert string + // The server information + ServerInfo serverInfoResponseNet +} + +// newSignHandler is the constructor for an enroll or reenroll handler func newSignHandler(server *Server, endpoint string) (h http.Handler, err error) { // NewHandler is constructor for register handler return &cfapi.HTTPHandler{ @@ -94,6 +102,7 @@ func (sh *signHandler) Handle(w http.ResponseWriter, r *http.Request) error { return err } + // Sign the certificate cert, err := sh.server.enrollSigner.Sign(req) if err != nil { err = fmt.Errorf("Failed signing for endpoint %s: %s", sh.endpoint, err) @@ -101,7 +110,14 @@ func (sh *signHandler) Handle(w http.ResponseWriter, r *http.Request) error { return err } - return cfapi.SendResponse(w, cert) + // Send the response with the cert and the server info + resp := &enrollmentResponseNet{Cert: util.B64Encode(cert)} + err = sh.server.fillServerInfo(&resp.ServerInfo) + if err != nil { + return err + } + + return cfapi.SendResponse(w, resp) } // Make any authorization checks needed, depending on the contents diff --git a/lib/serverinfo.go b/lib/serverinfo.go index 799830055..1e92a4455 100644 --- a/lib/serverinfo.go +++ b/lib/serverinfo.go @@ -21,7 +21,6 @@ import ( cfapi "github.com/cloudflare/cfssl/api" "github.com/cloudflare/cfssl/log" - "github.com/hyperledger/fabric-ca/util" ) // infoHandler handles the GET /info request @@ -47,18 +46,11 @@ type serverInfoResponseNet struct { // Handle is the handler for the GET /info request func (ih *infoHandler) Handle(w http.ResponseWriter, r *http.Request) error { - log.Debug("Received request for server info") - - caChain, err := ih.server.getCAChain() + resp := &serverInfoResponseNet{} + err := ih.server.fillServerInfo(resp) if err != nil { return err } - - resp := &serverInfoResponseNet{ - CAName: ih.server.Config.CA.Name, - CAChain: util.B64Encode(caChain), - } - return cfapi.SendResponse(w, resp) }