// PasswordValid returns whether the given password // is valid for the user. func (u *User) PasswordValid(password string) bool { // If the user is deactivated, no point in carrying on if u.IsDeactivated() { return false } // Since these are potentially set by a User, we intentionally use the // slower pbkdf2 style hashing. Also, we don't expect to have thousands // of Users trying to log in at the same time (which we *do* expect of // Unit and Machine agents.) if u.doc.PasswordSalt != "" { return utils.UserPasswordHash(password, u.doc.PasswordSalt) == u.doc.PasswordHash } // In Juju 1.16 and older, we did not set a Salt for the user password, // so check if the password hash matches using CompatSalt. if it // does, then set the password again so that we get a proper salt if utils.UserPasswordHash(password, utils.CompatSalt) == u.doc.PasswordHash { // This will set a new Salt for the password. We ignore if it // fails because we will try again at the next request logger.Debugf("User %s logged in with CompatSalt resetting password for new salt", u.Name()) err := u.SetPassword(password) if err != nil { logger.Errorf("Cannot set resalted password for user %q", u.Name()) } return true } return false }
func (s *UserSuite) TestPasswordValidUpdatesSalt(c *gc.C) { u, err := s.State.AddUser("someuser", "password") c.Assert(err, gc.IsNil) compatHash := utils.UserPasswordHash("foo", utils.CompatSalt) err = u.SetPasswordHash(compatHash, "") c.Assert(err, gc.IsNil) beforeSalt, beforeHash := state.GetUserPasswordSaltAndHash(u) c.Assert(beforeSalt, gc.Equals, "") c.Assert(beforeHash, gc.Equals, compatHash) c.Assert(u.PasswordValid("bar"), jc.IsFalse) // A bad password doesn't trigger a rewrite afterBadSalt, afterBadHash := state.GetUserPasswordSaltAndHash(u) c.Assert(afterBadSalt, gc.Equals, "") c.Assert(afterBadHash, gc.Equals, compatHash) // When we get a valid check, we then add a salt and rewrite the hash c.Assert(u.PasswordValid("foo"), jc.IsTrue) afterSalt, afterHash := state.GetUserPasswordSaltAndHash(u) c.Assert(afterSalt, gc.Not(gc.Equals), "") c.Assert(afterHash, gc.Not(gc.Equals), compatHash) c.Assert(afterHash, gc.Equals, utils.UserPasswordHash("foo", afterSalt)) // running PasswordValid again doesn't trigger another rewrite c.Assert(u.PasswordValid("foo"), jc.IsTrue) lastSalt, lastHash := state.GetUserPasswordSaltAndHash(u) c.Assert(lastSalt, gc.Equals, afterSalt) c.Assert(lastHash, gc.Equals, afterHash) }
func (s *UserSuite) TestSetPasswordHashWithSalt(c *gc.C) { u, err := s.State.AddUser("someuser", "password") c.Assert(err, gc.IsNil) err = u.SetPasswordHash(utils.UserPasswordHash("foo", "salted"), "salted") c.Assert(err, gc.IsNil) c.Assert(u.PasswordValid("foo"), jc.IsTrue) salt, hash := state.GetUserPasswordSaltAndHash(u) c.Assert(salt, gc.Equals, "salted") c.Assert(hash, gc.Not(gc.Equals), utils.UserPasswordHash("foo", utils.CompatSalt)) }
// resetAdminPasswordAndFetchDBNames logs into the database with a // plausible password and returns all the database's db names. We need // to try several passwords because we don't know what state the mongo // server is in when Reset is called. If the test has set a custom // password, we're out of luck, but if they are using // DefaultStatePassword, we can succeed. func resetAdminPasswordAndFetchDBNames(session *mgo.Session) ([]string, bool) { // First try with no password dbnames, err := session.DatabaseNames() if err == nil { return dbnames, true } if !isUnauthorized(err) { panic(err) } // Then try the two most likely passwords in turn. for _, password := range []string{ DefaultMongoPassword, utils.UserPasswordHash(DefaultMongoPassword, utils.CompatSalt), } { admin := session.DB("admin") if err := admin.Login("admin", password); err != nil { logger.Infof("failed to log in with password %q", password) continue } dbnames, err := session.DatabaseNames() if err == nil { if err := admin.RemoveUser("admin"); err != nil { panic(err) } return dbnames, true } if !isUnauthorized(err) { panic(err) } logger.Infof("unauthorized access when getting database names; password %q", password) } return nil, false }
// AddUser adds a user to the state. func (st *State) AddUser(name, password string) (*User, error) { if !names.IsUser(name) { return nil, fmt.Errorf("invalid user name %q", name) } salt, err := utils.RandomSalt() if err != nil { return nil, err } u := &User{ st: st, doc: userDoc{ Name: name, PasswordHash: utils.UserPasswordHash(password, salt), PasswordSalt: salt, }, } ops := []txn.Op{{ C: st.users.Name, Id: name, Assert: txn.DocMissing, Insert: &u.doc, }} err = st.runTransaction(ops) if err == txn.ErrAborted { err = fmt.Errorf("user already exists") } if err != nil { return nil, err } return u, nil }
// SetPassword sets the password associated with the user. func (u *User) SetPassword(password string) error { salt, err := utils.RandomSalt() if err != nil { return err } return u.SetPasswordHash(utils.UserPasswordHash(password, salt), salt) }
func (s *UserSuite) TestAddUserSetsSalt(c *gc.C) { u, err := s.State.AddUser("someuser", "a-password") c.Assert(err, gc.IsNil) salt, hash := state.GetUserPasswordSaltAndHash(u) c.Check(hash, gc.Not(gc.Equals), "") c.Check(salt, gc.Not(gc.Equals), "") c.Check(utils.UserPasswordHash("a-password", salt), gc.Equals, hash) c.Check(u.PasswordValid("a-password"), jc.IsTrue) }
// NewConn returns a new Conn that uses the // given environment. The environment must have already // been bootstrapped. func NewConn(environ environs.Environ) (*Conn, error) { info, _, err := environ.StateInfo() if err != nil { return nil, err } password := environ.Config().AdminSecret() if password == "" { return nil, fmt.Errorf("cannot connect without admin-secret") } err = environs.CheckEnvironment(environ) if err != nil { return nil, err } info.Password = password opts := state.DefaultDialOpts() st, err := state.Open(info, opts, environs.NewStatePolicy()) if errors.IsUnauthorized(err) { logger.Infof("authorization error while connecting to state server; retrying") // We can't connect with the administrator password,; // perhaps this was the first connection and the // password has not been changed yet. info.Password = utils.UserPasswordHash(password, utils.CompatSalt) // We try for a while because we might succeed in // connecting to mongo before the state has been // initialized and the initial password set. for a := redialStrategy.Start(); a.Next(); { st, err = state.Open(info, opts, environs.NewStatePolicy()) if !errors.IsUnauthorized(err) { break } } if err != nil { return nil, err } if err := st.SetAdminMongoPassword(password); err != nil { return nil, err } } else if err != nil { return nil, err } conn := &Conn{ Environ: environ, State: st, } if err := conn.updateSecrets(); err != nil { conn.Close() return nil, fmt.Errorf("unable to push secrets: %v", err) } return conn, nil }
func (s *UserSuite) TestSetPasswordHash(c *gc.C) { u, err := s.State.AddUser("someuser", "password") c.Assert(err, gc.IsNil) err = u.SetPasswordHash(utils.UserPasswordHash("foo", utils.CompatSalt), utils.CompatSalt) c.Assert(err, gc.IsNil) c.Assert(u.PasswordValid("foo"), jc.IsTrue) c.Assert(u.PasswordValid("bar"), jc.IsFalse) // User passwords should *not* use the fast PasswordHash function hash := utils.AgentPasswordHash("foo-12345678901234567890") c.Assert(err, gc.IsNil) err = u.SetPasswordHash(hash, "") c.Assert(err, gc.IsNil) c.Assert(u.PasswordValid("foo-12345678901234567890"), jc.IsFalse) }
// PasswordValid returns whether the given password is valid // for the given unit. func (u *Unit) PasswordValid(password string) bool { agentHash := utils.AgentPasswordHash(password) if agentHash == u.doc.PasswordHash { return true } // In Juju 1.16 and older we used the slower password hash for unit // agents. So check to see if the supplied password matches the old // path, and if so, update it to the new mechanism. // We ignore any error in setting the password hash, as we'll just try // again next time if utils.UserPasswordHash(password, utils.CompatSalt) == u.doc.PasswordHash { logger.Debugf("%s logged in with old password hash, changing to AgentPasswordHash", u.Tag()) u.setPasswordHash(agentHash) return true } return false }
func (s *CloudInitSuite) TestFinishBootstrapConfig(c *gc.C) { attrs := dummySampleConfig().Merge(testing.Attrs{ "authorized-keys": "we-are-the-keys", "admin-secret": "lisboan-pork", "agent-version": "1.2.3", "state-server": false, }) cfg, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) oldAttrs := cfg.AllAttrs() mcfg := &cloudinit.MachineConfig{ Bootstrap: true, } cons := constraints.MustParse("mem=1T cpu-power=999999999") err = environs.FinishMachineConfig(mcfg, cfg, cons) c.Assert(err, gc.IsNil) c.Check(mcfg.AuthorizedKeys, gc.Equals, "we-are-the-keys") c.Check(mcfg.DisableSSLHostnameVerification, jc.IsFalse) password := utils.UserPasswordHash("lisboan-pork", utils.CompatSalt) c.Check(mcfg.APIInfo, gc.DeepEquals, &api.Info{ Password: password, CACert: testing.CACert, }) c.Check(mcfg.StateInfo, gc.DeepEquals, &state.Info{ Password: password, CACert: testing.CACert, }) c.Check(mcfg.StateServingInfo.StatePort, gc.Equals, cfg.StatePort()) c.Check(mcfg.StateServingInfo.APIPort, gc.Equals, cfg.APIPort()) c.Check(mcfg.Constraints, gc.DeepEquals, cons) oldAttrs["ca-private-key"] = "" oldAttrs["admin-secret"] = "" c.Check(mcfg.Config.AllAttrs(), gc.DeepEquals, oldAttrs) srvCertPEM := mcfg.StateServingInfo.Cert srvKeyPEM := mcfg.StateServingInfo.PrivateKey _, _, err = cert.ParseCertAndKey(srvCertPEM, srvKeyPEM) c.Check(err, gc.IsNil) err = cert.Verify(srvCertPEM, testing.CACert, time.Now()) c.Assert(err, gc.IsNil) err = cert.Verify(srvCertPEM, testing.CACert, time.Now().AddDate(9, 0, 0)) c.Assert(err, gc.IsNil) err = cert.Verify(srvCertPEM, testing.CACert, time.Now().AddDate(10, 0, 1)) c.Assert(err, gc.NotNil) }
func testPasswordHash() string { return utils.UserPasswordHash(testPassword, utils.CompatSalt) }
func (e *environ) Bootstrap(ctx environs.BootstrapContext, args environs.BootstrapParams) error { selectedTools, err := common.EnsureBootstrapTools(ctx, e, config.PreferredSeries(e.Config()), args.Constraints.Arch) if err != nil { return err } defer delay() if err := e.checkBroken("Bootstrap"); err != nil { return err } password := e.Config().AdminSecret() if password == "" { return fmt.Errorf("admin-secret is required for bootstrap") } if _, ok := e.Config().CACert(); !ok { return fmt.Errorf("no CA certificate in environment configuration") } logger.Infof("would pick tools from %s", selectedTools) cfg, err := environs.BootstrapConfig(e.Config()) if err != nil { return fmt.Errorf("cannot make bootstrap config: %v", err) } estate, err := e.state() if err != nil { return err } estate.mu.Lock() defer estate.mu.Unlock() if estate.bootstrapped { return fmt.Errorf("environment is already bootstrapped") } // Write the bootstrap file just like a normal provider. However // we need to release the mutex for the save state to work, so regain // it after the call. estate.mu.Unlock() if err := bootstrap.SaveState(e.Storage(), &bootstrap.BootstrapState{StateInstances: []instance.Id{"localhost"}}); err != nil { logger.Errorf("failed to save state instances: %v", err) estate.mu.Lock() // otherwise defered unlock will fail return err } estate.mu.Lock() // back at it if e.ecfg().stateServer() { // TODO(rog) factor out relevant code from cmd/jujud/bootstrap.go // so that we can call it here. info := stateInfo() st, err := state.Initialize(info, cfg, state.DefaultDialOpts(), estate.statePolicy) if err != nil { panic(err) } if err := st.SetEnvironConstraints(args.Constraints); err != nil { panic(err) } if err := st.SetAdminMongoPassword(utils.UserPasswordHash(password, utils.CompatSalt)); err != nil { panic(err) } _, err = st.AddUser("admin", password) if err != nil { panic(err) } estate.apiServer, err = apiserver.NewServer(st, "localhost:0", []byte(testing.ServerCert), []byte(testing.ServerKey), DataDir, LogDir) if err != nil { panic(err) } estate.apiState = st } estate.bootstrapped = true estate.ops <- OpBootstrap{Context: ctx, Env: e.name, Args: args} return nil }