func send(req string, extendTimeout bool) ([]string, error) { conn, err := net.DialUnix("unix", nil, &net.UnixAddr{socketPath, "unix"}) if err != nil { return nil, err } defer conn.Close() writeDeadline := time.Now().Add(clientTimeout) conn.SetWriteDeadline(writeDeadline) _, err = conn.Write([]byte(req)) if err != nil { logger.Errorf("Failed to write request: %v.", err) return nil, err } readDeadline := time.Now().Add(clientTimeout) if extendTimeout { readDeadline = time.Now().Add(extendedTimeout) } conn.SetReadDeadline(readDeadline) data, err := ioutil.ReadAll(conn) if err != nil { logger.Errorf("Failed to read response: %v.", err) return nil, err } resp := strings.Split(string(data), "\n") err = checkHeader(resp) if err != nil { logger.Errorf("Request failed: %v.", err) return nil, err } return resp[1:], nil }
func (s *Server) respond(req string) string { parts := strings.Split(req, " ") cmd := parts[0] args := parts[1:] switch cmd { case "user_by_name": return s.userByName(args) case "user_by_uid": return s.userByUID(args) case "users": return s.users() case "group_by_name": return s.groupByName(args) case "group_by_gid": return s.groupByGID(args) case "groups": return s.groups() case "names": return s.names() case "is_name": return s.isName(args) case "keys": return s.authorizedKeys(args) default: logger.Errorf("Invalid request: %v.", req) return "400" } }
func (s *Server) handle(conn net.Conn) { defer conn.Close() deadline := time.Now().Add(serverTimeout) conn.SetReadDeadline(deadline) data := make([]byte, maxRequestSize) n, err := conn.Read(data) if err != nil { logger.Errorf("Failed to read request: %v.", err) return } resp := s.respond(string(data[:n])) deadline = time.Now().Add(serverTimeout) conn.SetWriteDeadline(deadline) _, err = conn.Write([]byte(resp)) if err != nil { logger.Errorf("Failed to write response: %v.", err) } }
func (s *Server) groupByGID(args []string) string { gid, err := parseID(args) if err != nil { logger.Errorf("Invalid GID for group: %v.", err) return "400" } logger.Infof("Getting group by GID: %v.", gid) group, err := s.Provider.GroupByGID(gid) if err != nil { return marshalError(err) } return fmt.Sprintf("200\n%v", marshalGroup(group)) }
func (s *Server) groupByName(args []string) string { name, err := parseName(args) if err != nil { logger.Errorf("Invalid name for group: %v.", err) return "400" } logger.Infof("Getting group by name: %v.", name) group, err := s.Provider.GroupByName(name) if err != nil { return marshalError(err) } return fmt.Sprintf("200\n%v", marshalGroup(group)) }
func (s *Server) userByUID(args []string) string { uid, err := parseID(args) if err != nil { logger.Errorf("Invalid UID for user: %v.", err) return "400" } logger.Infof("Getting user by UID: %v.", uid) user, err := s.Provider.UserByUID(uid) if err != nil { return marshalError(err) } return fmt.Sprintf("200\n%v", marshalUser(user)) }
func (s *Server) userByName(args []string) string { name, err := parseName(args) if err != nil { logger.Errorf("Invalid name for user: %v.", err) return "400" } logger.Infof("Getting user by name: %v.", name) user, err := s.Provider.UserByName(name) if err != nil { return marshalError(err) } return fmt.Sprintf("200\n%v", marshalUser(user)) }
func (s *Server) isName(args []string) string { name, err := parseName(args) if err != nil { logger.Errorf("Invalid name: %v.", err) return "400" } logger.Infof("Checking name: %v.", name) is, err := s.Provider.IsName(name) if err != nil { return marshalError(err) } else if is { logger.Info("Valid name.") return "200" } else { logger.Info("Invalid name.") return "404" } }
func (s *Server) authorizedKeys(args []string) string { username, err := parseName(args) if err != nil { logger.Errorf("Invalid username for keys: %v.", err) return "400" } logger.Infof("Getting keys for user: %v.", username) keys, err := s.Provider.AuthorizedKeys(username) if err != nil { return marshalError(err) } var buf bytes.Buffer buf.WriteString("200") for _, k := range keys { buf.WriteString("\n") buf.WriteString(k) } return buf.String() }
func updateAccounts(s *cachingStore) { users, groups, err := s.apiClient.UsersAndGroups() if err != nil { logger.Errorf("Failed refresh: %v.", err) return } s.Lock() defer s.Unlock() oldUsers := s.usersByName s.usersByName = make(map[string]*cachedUser) s.usersByUID = make(map[uint32]*cachedUser) s.groupsByName = make(map[string]*accounts.Group) s.groupsByGID = make(map[uint32]*accounts.Group) for _, u := range users { user := &accounts.User{ Name: u.Username, UID: uint32(u.Uid), GID: uint32(u.Gid), Gecos: u.Gecos, HomeDirectory: u.HomeDirectory, Shell: u.Shell, } cu := &cachedUser{user: user} if old, ok := oldUsers[user.Name]; ok { cu.keyRefreshTime = old.keyRefreshTime cu.keys = old.keys cu.sudoer = old.sudoer } s.usersByName[user.Name] = cu s.usersByUID[user.UID] = cu } for _, g := range groups { group := &accounts.Group{ Name: g.GroupName, GID: uint32(g.Gid), Members: g.Members, } s.groupsByName[group.Name] = group s.groupsByGID[group.GID] = group } logger.Info("Refreshing users and groups succeeded.") }
// Serve begins serving accounts information through a socket forever. func (s *Server) Serve() error { os.Remove(socketPath) sock, err := net.ListenUnix("unix", &net.UnixAddr{socketPath, "unix"}) if err != nil { return err } // Make the socket readable and writeable by all. os.Chmod(socketPath, os.ModePerm) listeningCallback() logger.Infof("Listening for connections at %v.", socketPath) for { conn, err := sock.Accept() if err != nil { logger.Errorf("Failed to accept connection: %v.", err) continue } logger.Info("Accepted connection.") go s.handle(conn) } }
func updateKeys(s *cachingStore) { type update struct { name string view *cua.AuthorizedKeysView err error time time.Time } ch := make(chan update) workers := 0 for _, name := range keysRequiringRefresh(s) { go func(n string) { view, err := s.apiClient.AuthorizedKeys(n) ch <- update{n, view, err, timeNow()} }(name) workers += 1 } refreshedKeys := make([]update, workers) for workers != 0 { up := <-ch workers -= 1 if up.err != nil { logger.Errorf("Failed key refresh for %v: %v.", up.name, up.err) continue } logger.Infof("Refreshed keys for %v.", up.name) refreshedKeys = append(refreshedKeys, up) } s.Lock() defer s.Unlock() for _, rk := range refreshedKeys { if cu, ok := s.usersByName[rk.name]; ok { cu.keys = rk.view.Keys cu.sudoer = rk.view.Sudoer cu.keyRefreshTime = rk.time } } refreshCallback(s.config) }