/* Update() searches LDAP for the current user set that supports the necessary properties for Hologram. TODO: call this at some point during verification failure so that keys that have been recently added to LDAP work, instead of requiring a server restart. */ func (luc *ldapUserCache) Update() error { start := time.Now() filter := "(sshPublicKey=*)" searchRequest := ldap.NewSearchRequest( luc.baseDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, filter, []string{"sshPublicKey", luc.userAttr}, nil, ) searchResult, err := luc.server.Search(searchRequest) if err != nil { return err } for _, entry := range searchResult.Entries { username := entry.GetAttributeValue(luc.userAttr) userKeys := []ssh.PublicKey{} for _, eachKey := range entry.GetAttributeValues("sshPublicKey") { sshKeyBytes, _ := base64.StdEncoding.DecodeString(eachKey) userSSHKey, err := ssh.ParsePublicKey(sshKeyBytes) if err != nil { userSSHKey, _, _, _, err = ssh.ParseAuthorizedKey([]byte(eachKey)) if err != nil { log.Warning("SSH key parsing for user %s failed (key was '%s')! This key will not be added into LDAP.", username, eachKey) continue } } userKeys = append(userKeys, userSSHKey) } luc.users[username] = &User{ SSHKeys: userKeys, Username: username, } log.Debug("Information on %s (re-)generated.", username) } log.Debug("LDAP information re-cached.") luc.stats.Timing(1.0, "ldapCacheUpdate", time.Since(start)) return nil }
/* HandleServerRequest handles the flow for messages that this server accepts from clients. */ func (sm *server) HandleServerRequest(m protocol.MessageReadWriteCloser, r *protocol.ServerRequest) { if assumeRoleMsg := r.GetAssumeRole(); assumeRoleMsg != nil { log.Debug("Handling an assumeRole request.") sm.stats.Counter(1.0, "messages.assumeRole", 1) role := assumeRoleMsg.GetRole() user, err := sm.SSHChallenge(m) if err != nil { log.Errorf("Error trying to handle AssumeRole: %s", err.Error()) m.Close() return } if user != nil { creds, err := sm.credentials.AssumeRole(user, role, sm.enableLDAPRoles) if err != nil { // error message from Amazon, so forward that on to the client errStr := err.Error() errMsg := &protocol.Message{ Error: &errStr, } log.Errorf("Error from AWS for AssumeRole: %s", err.Error()) m.Write(errMsg) sm.stats.Counter(1.0, "errors.assumeRole", 1) //m.Close() return } m.Write(makeCredsResponse(creds)) return } } else if getUserCredentialsMsg := r.GetGetUserCredentials(); getUserCredentialsMsg != nil { sm.stats.Counter(1.0, "messages.getUserCredentialsMsg", 1) user, err := sm.SSHChallenge(m) if err != nil { log.Errorf("Error trying to handle GetUserCredentials: %s", err.Error()) m.Close() return } if user != nil { creds, err := sm.credentials.AssumeRole(user, sm.DefaultRole, sm.enableLDAPRoles) if err != nil { log.Errorf("Error trying to handle GetUserCredentials: %s", err.Error()) m.Close() return } m.Write(makeCredsResponse(creds)) return } } else if addSSHKeyMsg := r.GetAddSSHkey(); addSSHKeyMsg != nil { sm.stats.Counter(1.0, "messages.addSSHKeyMsg", 1) // Search for the user specified in this request. sr := ldap.NewSearchRequest( sm.baseDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, fmt.Sprintf("(%s=%s)", sm.userAttr, addSSHKeyMsg.GetUsername()), []string{"sshPublicKey", sm.userAttr, "userPassword"}, nil) user, err := sm.ldapServer.Search(sr) if err != nil { log.Errorf("Error trying to handle addSSHKeyMsg: %s", err.Error()) return } if len(user.Entries) == 0 { log.Errorf("User %s not found!", addSSHKeyMsg.GetUsername()) return } // Check their password. password := user.Entries[0].GetAttributeValue("userPassword") if password != addSSHKeyMsg.GetPasswordhash() { log.Errorf("Provided password for user %s does not match %s!", addSSHKeyMsg.GetUsername(), password) return } // Check to see if this SSH key already exists. for _, k := range user.Entries[0].GetAttributeValues("sshPublicKey") { if k == addSSHKeyMsg.GetSshkeybytes() { log.Warning("User %s already has this SSH key. Doing nothing.", addSSHKeyMsg.GetUsername()) successMsg := &protocol.Message{Success: &protocol.Success{}} m.Write(successMsg) return } } mr := ldap.NewModifyRequest(user.Entries[0].DN) mr.Add("sshPublicKey", []string{addSSHKeyMsg.GetSshkeybytes()}) err = sm.ldapServer.Modify(mr) if err != nil { log.Errorf("Could not modify LDAP user: %s", err.Error()) return } successMsg := &protocol.Message{Success: &protocol.Success{}} m.Write(successMsg) return } }
/* Update() searches LDAP for the current user set that supports the necessary properties for Hologram. TODO: call this at some point during verification failure so that keys that have been recently added to LDAP work, instead of requiring a server restart. */ func (luc *ldapUserCache) Update() error { start := time.Now() if luc.enableLDAPRoles { groupSearchRequest := ldap.NewSearchRequest( luc.baseDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, "(objectClass=groupOfNames)", []string{luc.roleAttribute}, nil, ) groupSearchResult, err := luc.server.Search(groupSearchRequest) if err != nil { return err } for _, entry := range groupSearchResult.Entries { dn := entry.DN arns := entry.GetAttributeValues(luc.roleAttribute) log.Debug("Adding %s to %s", arns, dn) luc.groups[dn] = arns } } filter := "(sshPublicKey=*)" searchRequest := ldap.NewSearchRequest( luc.baseDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, filter, []string{"sshPublicKey", luc.userAttr, "memberOf"}, nil, ) searchResult, err := luc.server.Search(searchRequest) if err != nil { return err } for _, entry := range searchResult.Entries { username := entry.GetAttributeValue(luc.userAttr) userKeys := []ssh.PublicKey{} for _, eachKey := range entry.GetAttributeValues("sshPublicKey") { sshKeyBytes, _ := base64.StdEncoding.DecodeString(eachKey) userSSHKey, err := ssh.ParsePublicKey(sshKeyBytes) if err != nil { userSSHKey, _, _, _, err = ssh.ParseAuthorizedKey([]byte(eachKey)) if err != nil { log.Warning("SSH key parsing for user %s failed (key was '%s')! This key will not be added into LDAP.", username, eachKey) continue } } userKeys = append(userKeys, userSSHKey) } arns := []string{} if luc.enableLDAPRoles { for _, groupDN := range entry.GetAttributeValues("memberOf") { log.Debug(groupDN) arns = append(arns, luc.groups[groupDN]...) } } luc.users[username] = &User{ SSHKeys: userKeys, Username: username, ARNs: arns, } log.Debug("Information on %s (re-)generated.", username) } log.Debug("LDAP information re-cached.") luc.stats.Timing(1.0, "ldapCacheUpdate", time.Since(start)) return nil }