Esempio n. 1
0
// NewKeyManagerAPI creates a new server-side keyupdater API end point.
func NewKeyManagerAPI(st *state.State, resources *common.Resources, authorizer common.Authorizer) (*KeyManagerAPI, error) {
	// Only clients and environment managers can access the key manager service.
	if !authorizer.AuthClient() && !authorizer.AuthEnvironManager() {
		return nil, common.ErrPerm
	}
	// TODO(wallyworld) - replace stub with real canRead function
	// For now, only admins can read authorised ssh keys.
	canRead := func(_ string) bool {
		return authorizer.GetAuthTag() == adminUser
	}
	// TODO(wallyworld) - replace stub with real canWrite function
	// For now, only admins can write authorised ssh keys for users.
	// Machine agents can write the juju-system-key.
	canWrite := func(user string) bool {
		// Are we a machine agent writing the Juju system key.
		if user == config.JujuSystemKey {
			_, ismachinetag := authorizer.GetAuthTag().(names.MachineTag)
			return ismachinetag
		}
		// Are we writing the auth key for a user.
		if _, err := st.User(user); err != nil {
			return false
		}
		return authorizer.GetAuthTag() == adminUser
	}
	return &KeyManagerAPI{
		state:      st,
		resources:  resources,
		authorizer: authorizer,
		canRead:    canRead,
		canWrite:   canWrite}, nil
}
Esempio n. 2
0
// NewKeyManagerAPI creates a new server-side keyupdater API end point.
func NewKeyManagerAPI(
	st *state.State,
	resources *common.Resources,
	authorizer common.Authorizer,
) (*KeyManagerAPI, error) {
	// Only clients and environment managers can access the key manager service.
	if !authorizer.AuthClient() && !authorizer.AuthEnvironManager() {
		return nil, common.ErrPerm
	}
	// TODO(wallyworld) - replace stub with real canRead function
	// For now, only admins can read authorised ssh keys.
	getCanRead := func() (common.AuthFunc, error) {
		return func(_ string) bool {
			return authorizer.GetAuthTag() == adminUser
		}, nil
	}
	// TODO(wallyworld) - replace stub with real canWrite function
	// For now, only admins can write authorised ssh keys for users.
	// Machine agents can write the juju-system-key.
	getCanWrite := func() (common.AuthFunc, error) {
		return func(tag string) bool {
			// Are we a machine agent writing the Juju system key.
			if tag == config.JujuSystemKey {
				// TODO(dfc) this can never be false
				_, err := names.ParseMachineTag(authorizer.GetAuthTag().String())
				return err == nil
			}
			// Are we writing the auth key for a user.
			if _, err := st.User(tag); err != nil {
				return false
			}
			return authorizer.GetAuthTag() == adminUser
		}, nil
	}
	return &KeyManagerAPI{
		state: st, resources: resources, authorizer: authorizer, getCanRead: getCanRead, getCanWrite: getCanWrite}, nil
}
Esempio n. 3
0
// The client will POST to the "/register" endpoint with a JSON-encoded
// params.SecretKeyLoginRequest. This contains the tag of the user they
// are registering, a (supposedly) unique nonce, and a ciphertext which
// is the result of concatenating the user and nonce values, and then
// encrypting and authenticating them with the NaCl Secretbox algorithm.
//
// If the server can decrypt the ciphertext, then it knows the client
// has the required secret key; thus they are authenticated. The client
// does not have the CA certificate for communicating securely with the
// server, and so must also authenticate the server. The server will
// similarly generate a unique nonce and encrypt the response payload
// using the same secret key as the client. If the client can decrypt
// the payload, it knows the server has the required secret key; thus
// it is also authenticated.
//
// NOTE(axw) it is important that the client and server choose their
// own nonces, because reusing a nonce means that the key-stream can
// be revealed.
func (h *registerUserHandler) processPost(req *http.Request, st *state.State) (
	names.UserTag, *params.SecretKeyLoginResponse, error,
) {

	failure := func(err error) (names.UserTag, *params.SecretKeyLoginResponse, error) {
		return names.UserTag{}, nil, err
	}

	data, err := ioutil.ReadAll(req.Body)
	if err != nil {
		return failure(err)
	}
	var loginRequest params.SecretKeyLoginRequest
	if err := json.Unmarshal(data, &loginRequest); err != nil {
		return failure(err)
	}

	// Basic validation: ensure that the request contains a valid user tag,
	// nonce, and ciphertext of the expected length.
	userTag, err := names.ParseUserTag(loginRequest.User)
	if err != nil {
		return failure(err)
	}
	if len(loginRequest.Nonce) != secretboxNonceLength {
		return failure(errors.NotValidf("nonce"))
	}

	// Decrypt the ciphertext with the user's secret key (if it has one).
	user, err := st.User(userTag)
	if err != nil {
		return failure(err)
	}
	if len(user.SecretKey()) != secretboxKeyLength {
		return failure(errors.NotFoundf("secret key for user %q", user.Name()))
	}
	var key [secretboxKeyLength]byte
	var nonce [secretboxNonceLength]byte
	copy(key[:], user.SecretKey())
	copy(nonce[:], loginRequest.Nonce)
	payloadBytes, ok := secretbox.Open(nil, loginRequest.PayloadCiphertext, &nonce, &key)
	if !ok {
		// Cannot decrypt the ciphertext, which implies that the secret
		// key specified by the client is invalid.
		return failure(errors.NotValidf("secret key"))
	}

	// Unmarshal the request payload, which contains the new password to
	// set for the user.
	var requestPayload params.SecretKeyLoginRequestPayload
	if err := json.Unmarshal(payloadBytes, &requestPayload); err != nil {
		return failure(errors.Annotate(err, "cannot unmarshal payload"))
	}
	if err := user.SetPassword(requestPayload.Password); err != nil {
		return failure(errors.Annotate(err, "setting new password"))
	}

	// Respond with the CA-cert and password, encrypted again with the
	// secret key.
	responsePayload, err := h.getSecretKeyLoginResponsePayload(st, userTag)
	if err != nil {
		return failure(errors.Trace(err))
	}
	payloadBytes, err = json.Marshal(responsePayload)
	if err != nil {
		return failure(errors.Trace(err))
	}
	if _, err := rand.Read(nonce[:]); err != nil {
		return failure(errors.Trace(err))
	}
	response := &params.SecretKeyLoginResponse{
		Nonce:             nonce[:],
		PayloadCiphertext: secretbox.Seal(nil, payloadBytes, &nonce, &key),
	}
	return userTag, response, nil
}