]> git.wincent.com - passage.git/blob - passage.go
Add basic supporting material prior to release
[passage.git] / passage.go
1 // Copyright 2016-present Greg Hurrell. All rights reserved.
2 // Licensed under the terms of the BSD 2-clause license.
3
4 package main
5
6 import (
7         "encoding/json"
8         "github.com/keybase/go-keychain"
9         "log"
10         "net"
11         "os"
12         "os/signal"
13         "os/user"
14         "syscall"
15 )
16
17 type Request struct {
18         Account string
19         Service string
20 }
21
22 var cache map[string][]byte
23
24 func main() {
25         resetCache()
26         path := getSockPath()
27         syscall.Umask(0077)
28         listener, err := net.Listen("unix", path)
29         if err != nil {
30                 log.Fatal(err)
31         }
32         defer listener.Close()
33
34         go func() {
35                 for {
36                         conn, err := listener.Accept()
37                         if err != nil {
38                                 log.Print(err)
39                                 return
40                         }
41                         go handleConnection(conn)
42                 }
43         }()
44         log.Print("Open and ready for business on UNIX socket at ", path)
45
46         reload := make(chan os.Signal, 1)
47         signal.Notify(reload, syscall.SIGUSR1)
48         go func() {
49                 for {
50                         sig := <-reload
51                         log.Print("Got signal ", sig, ": resetting")
52                         resetCache()
53                 }
54         }()
55
56         // Need to catch signals in order for `defer`-ed clean-up items to run.
57         term := make(chan os.Signal, 1)
58         signal.Notify(term, os.Interrupt, os.Kill, syscall.SIGTERM)
59         sig := <-term
60         log.Print("Got signal ", sig)
61 }
62
63 func resetCache() {
64         cache = make(map[string][]byte)
65 }
66
67 func getSockPath() string {
68         user, err := user.Current()
69         if err != nil {
70                 log.Fatal(err)
71         }
72         return user.HomeDir + "/.passage.sock"
73 }
74
75 func handleConnection(conn net.Conn) {
76         defer conn.Close()
77         decoder := json.NewDecoder(conn)
78         var request Request
79         err := decoder.Decode(&request)
80         if err != nil {
81                 log.Print(err)
82                 return
83         }
84
85         cache_key, err := json.Marshal(request)
86         if err != nil {
87                 log.Fatal(err)
88         }
89         if cached, exists := cache[string(cache_key)]; exists {
90                 conn.Write(cached)
91                 log.Print("Wrote result from cache")
92                 return
93         }
94
95         query := keychain.NewItem()
96         query.SetSecClass(keychain.SecClassGenericPassword)
97         query.SetService(request.Service)
98         query.SetAccount(request.Account)
99         query.SetMatchLimit(keychain.MatchLimitOne)
100         query.SetReturnData(true)
101         results, err := keychain.QueryItem(query)
102         if err != nil {
103                 log.Print(err)
104         } else if len(results) != 1 {
105                 log.Print("Item not found")
106         } else {
107                 password := results[0].Data
108                 conn.Write(password)
109                 cache[string(cache_key)] = password
110                 log.Print("Wrote result from keychain")
111         }
112 }