Andres Erbsen | b7f95f3 | 2014-07-07 12:07:56 -0700 | [diff] [blame] | 1 | package ipc |
| 2 | |
| 3 | import ( |
| 4 | "sync" |
Mehrdad Afshari | cd9852b | 2014-09-26 11:07:35 -0700 | [diff] [blame] | 5 | |
Jiri Simsa | 764efb7 | 2014-12-25 20:57:03 -0800 | [diff] [blame] | 6 | "v.io/core/veyron/runtimes/google/ipc/stream/vc" |
Ankur | e49a86a | 2014-11-11 18:52:43 -0800 | [diff] [blame] | 7 | |
Jiri Simsa | 764efb7 | 2014-12-25 20:57:03 -0800 | [diff] [blame] | 8 | "v.io/core/veyron2/context" |
| 9 | "v.io/core/veyron2/ipc" |
Jiri Simsa | 764efb7 | 2014-12-25 20:57:03 -0800 | [diff] [blame] | 10 | "v.io/core/veyron2/security" |
Todd Wang | b86b352 | 2015-01-22 13:34:20 -0800 | [diff] [blame] | 11 | "v.io/core/veyron2/vdl" |
Jiri Simsa | 764efb7 | 2014-12-25 20:57:03 -0800 | [diff] [blame] | 12 | "v.io/core/veyron2/vlog" |
| 13 | "v.io/core/veyron2/vtrace" |
Andres Erbsen | b7f95f3 | 2014-07-07 12:07:56 -0700 | [diff] [blame] | 14 | ) |
| 15 | |
Ankur | e49a86a | 2014-11-11 18:52:43 -0800 | [diff] [blame] | 16 | // discharger implements vc.DischargeClient. |
| 17 | type dischargeClient struct { |
Asim Shankar | f4864f4 | 2014-11-25 18:53:05 -0800 | [diff] [blame] | 18 | c ipc.Client |
Matt Rosencrantz | 4f8ac60 | 2014-12-29 14:42:48 -0800 | [diff] [blame] | 19 | defaultCtx *context.T |
Asim Shankar | f4864f4 | 2014-11-25 18:53:05 -0800 | [diff] [blame] | 20 | cache dischargeCache |
Asim Shankar | 8f05c22 | 2014-10-06 22:08:19 -0700 | [diff] [blame] | 21 | } |
| 22 | |
Asim Shankar | f4864f4 | 2014-11-25 18:53:05 -0800 | [diff] [blame] | 23 | // InternalNewDischargeClient creates a vc.DischargeClient that will be used to |
| 24 | // fetch discharges to support blessings presented to a remote process. |
| 25 | // |
| 26 | // defaultCtx is the context used when none (nil) is explicitly provided to the |
| 27 | // PrepareDischarges call. This typically happens when fetching discharges on |
| 28 | // behalf of a server accepting connections, i.e., before any notion of the |
| 29 | // "context" of an API call has been established. |
Suharsh Sivakumar | 1b6683e | 2014-12-30 13:00:38 -0800 | [diff] [blame] | 30 | func InternalNewDischargeClient(defaultCtx *context.T, client ipc.Client) vc.DischargeClient { |
Ankur | e49a86a | 2014-11-11 18:52:43 -0800 | [diff] [blame] | 31 | return &dischargeClient{ |
Suharsh Sivakumar | 1b6683e | 2014-12-30 13:00:38 -0800 | [diff] [blame] | 32 | c: client, |
Asim Shankar | f4864f4 | 2014-11-25 18:53:05 -0800 | [diff] [blame] | 33 | defaultCtx: defaultCtx, |
| 34 | cache: dischargeCache{cache: make(map[string]security.Discharge)}, |
Suharsh Sivakumar | 1b6683e | 2014-12-30 13:00:38 -0800 | [diff] [blame] | 35 | } |
Ankur | e49a86a | 2014-11-11 18:52:43 -0800 | [diff] [blame] | 36 | } |
| 37 | |
Ankur | e49a86a | 2014-11-11 18:52:43 -0800 | [diff] [blame] | 38 | func (*dischargeClient) IPCStreamListenerOpt() {} |
| 39 | func (*dischargeClient) IPCStreamVCOpt() {} |
| 40 | |
| 41 | // PrepareDischarges retrieves the caveat discharges required for using blessings |
| 42 | // at server. The discharges are either found in the dischargeCache, in the call |
Andres Erbsen | b7f95f3 | 2014-07-07 12:07:56 -0700 | [diff] [blame] | 43 | // options, or requested from the discharge issuer indicated on the caveat. |
| 44 | // Note that requesting a discharge is an ipc call, so one copy of this |
| 45 | // function must be able to successfully terminate while another is blocked. |
Asim Shankar | 19da818 | 2015-02-06 01:41:16 -0800 | [diff] [blame] | 46 | func (d *dischargeClient) PrepareDischarges(ctx *context.T, forcaveats []security.Caveat, impetus security.DischargeImpetus) (ret []security.Discharge) { |
Ankur | e49a86a | 2014-11-11 18:52:43 -0800 | [diff] [blame] | 47 | if len(forcaveats) == 0 { |
| 48 | return |
| 49 | } |
Asim Shankar | 8f05c22 | 2014-10-06 22:08:19 -0700 | [diff] [blame] | 50 | // Make a copy since this copy will be mutated. |
Asim Shankar | 19da818 | 2015-02-06 01:41:16 -0800 | [diff] [blame] | 51 | var caveats []security.Caveat |
Asim Shankar | 8f05c22 | 2014-10-06 22:08:19 -0700 | [diff] [blame] | 52 | for _, cav := range forcaveats { |
Asim Shankar | 19da818 | 2015-02-06 01:41:16 -0800 | [diff] [blame] | 53 | // It shouldn't happen, but in case there are non-third-party |
| 54 | // caveats, drop them. |
| 55 | if tp := cav.ThirdPartyDetails(); tp != nil { |
| 56 | caveats = append(caveats, cav) |
| 57 | } |
Andres Erbsen | b7f95f3 | 2014-07-07 12:07:56 -0700 | [diff] [blame] | 58 | } |
Ankur | e49a86a | 2014-11-11 18:52:43 -0800 | [diff] [blame] | 59 | |
| 60 | // Gather discharges from cache. |
Ankur | f044a8d | 2014-09-05 17:05:24 -0700 | [diff] [blame] | 61 | discharges := make([]security.Discharge, len(caveats)) |
Asim Shankar | 77befba | 2015-01-09 12:49:04 -0800 | [diff] [blame] | 62 | if d.cache.Discharges(caveats, discharges) > 0 { |
| 63 | // Fetch discharges for caveats for which no discharges were found |
| 64 | // in the cache. |
| 65 | if ctx == nil { |
| 66 | ctx = d.defaultCtx |
| 67 | } |
| 68 | if ctx != nil { |
| 69 | var span vtrace.Span |
| 70 | ctx, span = vtrace.SetNewSpan(ctx, "Fetching Discharges") |
| 71 | defer span.Finish() |
| 72 | } |
| 73 | d.fetchDischarges(ctx, caveats, impetus, discharges) |
Asim Shankar | f4864f4 | 2014-11-25 18:53:05 -0800 | [diff] [blame] | 74 | } |
Andres Erbsen | b7f95f3 | 2014-07-07 12:07:56 -0700 | [diff] [blame] | 75 | for _, d := range discharges { |
| 76 | if d != nil { |
| 77 | ret = append(ret, d) |
| 78 | } |
| 79 | } |
| 80 | return |
| 81 | } |
Ankur | e49a86a | 2014-11-11 18:52:43 -0800 | [diff] [blame] | 82 | func (d *dischargeClient) Invalidate(discharges ...security.Discharge) { |
| 83 | d.cache.invalidate(discharges...) |
Andres Erbsen | b7f95f3 | 2014-07-07 12:07:56 -0700 | [diff] [blame] | 84 | } |
| 85 | |
| 86 | // fetchDischarges fills in out by fetching discharges for caveats from the |
| 87 | // appropriate discharge service. Since there may be dependencies in the |
| 88 | // caveats, fetchDischarges keeps retrying until either all discharges can be |
| 89 | // fetched or no new discharges are fetched. |
| 90 | // REQUIRES: len(caveats) == len(out) |
Asim Shankar | 19da818 | 2015-02-06 01:41:16 -0800 | [diff] [blame] | 91 | // REQUIRES: caveats[i].ThirdPartyDetails() != nil for 0 <= i < len(caveats) |
| 92 | func (d *dischargeClient) fetchDischarges(ctx *context.T, caveats []security.Caveat, impetus security.DischargeImpetus, out []security.Discharge) { |
Andres Erbsen | b7f95f3 | 2014-07-07 12:07:56 -0700 | [diff] [blame] | 93 | var wg sync.WaitGroup |
| 94 | for { |
| 95 | type fetched struct { |
| 96 | idx int |
Ankur | f044a8d | 2014-09-05 17:05:24 -0700 | [diff] [blame] | 97 | discharge security.Discharge |
Andres Erbsen | b7f95f3 | 2014-07-07 12:07:56 -0700 | [diff] [blame] | 98 | } |
| 99 | discharges := make(chan fetched, len(caveats)) |
Asim Shankar | 77befba | 2015-01-09 12:49:04 -0800 | [diff] [blame] | 100 | want := 0 |
Andres Erbsen | b7f95f3 | 2014-07-07 12:07:56 -0700 | [diff] [blame] | 101 | for i := range caveats { |
| 102 | if out[i] != nil { |
| 103 | continue |
| 104 | } |
Asim Shankar | 77befba | 2015-01-09 12:49:04 -0800 | [diff] [blame] | 105 | want++ |
Andres Erbsen | b7f95f3 | 2014-07-07 12:07:56 -0700 | [diff] [blame] | 106 | wg.Add(1) |
Asim Shankar | 19da818 | 2015-02-06 01:41:16 -0800 | [diff] [blame] | 107 | go func(i int, ctx *context.T, cav security.Caveat) { |
Andres Erbsen | b7f95f3 | 2014-07-07 12:07:56 -0700 | [diff] [blame] | 108 | defer wg.Done() |
Asim Shankar | 19da818 | 2015-02-06 01:41:16 -0800 | [diff] [blame] | 109 | tp := cav.ThirdPartyDetails() |
| 110 | vlog.VI(3).Infof("Fetching discharge for %v", tp) |
| 111 | call, err := d.c.StartCall(ctx, tp.Location(), "Discharge", []interface{}{cav, filteredImpetus(tp.Requirements(), impetus)}, vc.NoDischarges{}) |
Andres Erbsen | b7f95f3 | 2014-07-07 12:07:56 -0700 | [diff] [blame] | 112 | if err != nil { |
Asim Shankar | 19da818 | 2015-02-06 01:41:16 -0800 | [diff] [blame] | 113 | vlog.VI(3).Infof("Discharge fetch for %v failed: %v", tp, err) |
Andres Erbsen | b7f95f3 | 2014-07-07 12:07:56 -0700 | [diff] [blame] | 114 | return |
| 115 | } |
Todd Wang | b86b352 | 2015-01-22 13:34:20 -0800 | [diff] [blame] | 116 | var dAny vdl.AnyRep |
Asim Shankar | 5a82d35 | 2014-10-31 11:36:44 -0700 | [diff] [blame] | 117 | if ierr := call.Finish(&dAny, &err); ierr != nil || err != nil { |
Asim Shankar | c8cfcf1 | 2014-11-20 12:26:58 -0800 | [diff] [blame] | 118 | vlog.VI(3).Infof("Discharge fetch for %v failed: (%v, %v)", cav, err, ierr) |
Andres Erbsen | b7f95f3 | 2014-07-07 12:07:56 -0700 | [diff] [blame] | 119 | return |
| 120 | } |
Ankur | f044a8d | 2014-09-05 17:05:24 -0700 | [diff] [blame] | 121 | d, ok := dAny.(security.Discharge) |
Andres Erbsen | b7f95f3 | 2014-07-07 12:07:56 -0700 | [diff] [blame] | 122 | if !ok { |
Asim Shankar | 19da818 | 2015-02-06 01:41:16 -0800 | [diff] [blame] | 123 | vlog.Errorf("fetchDischarges: server at %s sent a %T (%v) instead of a Discharge", tp.Location(), dAny, dAny) |
Asim Shankar | 77befba | 2015-01-09 12:49:04 -0800 | [diff] [blame] | 124 | } else { |
Asim Shankar | 19da818 | 2015-02-06 01:41:16 -0800 | [diff] [blame] | 125 | vlog.VI(3).Infof("Fetched discharge for %v: %v", tp, d) |
Andres Erbsen | b7f95f3 | 2014-07-07 12:07:56 -0700 | [diff] [blame] | 126 | } |
| 127 | discharges <- fetched{i, d} |
Asim Shankar | f4864f4 | 2014-11-25 18:53:05 -0800 | [diff] [blame] | 128 | }(i, ctx, caveats[i]) |
Andres Erbsen | b7f95f3 | 2014-07-07 12:07:56 -0700 | [diff] [blame] | 129 | } |
| 130 | wg.Wait() |
| 131 | close(discharges) |
| 132 | var got int |
| 133 | for fetched := range discharges { |
Ankur | e49a86a | 2014-11-11 18:52:43 -0800 | [diff] [blame] | 134 | d.cache.Add(fetched.discharge) |
Andres Erbsen | b7f95f3 | 2014-07-07 12:07:56 -0700 | [diff] [blame] | 135 | out[fetched.idx] = fetched.discharge |
| 136 | got++ |
| 137 | } |
Asim Shankar | 77befba | 2015-01-09 12:49:04 -0800 | [diff] [blame] | 138 | if want > 0 { |
| 139 | vlog.VI(3).Infof("fetchDischarges: got %d of %d discharge(s) (total %d caveats)", got, want, len(caveats)) |
| 140 | } |
| 141 | if got == 0 || got == want { |
Andres Erbsen | b7f95f3 | 2014-07-07 12:07:56 -0700 | [diff] [blame] | 142 | return |
| 143 | } |
| 144 | } |
| 145 | } |
| 146 | |
Ankur | e49a86a | 2014-11-11 18:52:43 -0800 | [diff] [blame] | 147 | // dischargeCache is a concurrency-safe cache for third party caveat discharges. |
| 148 | type dischargeCache struct { |
| 149 | mu sync.RWMutex |
| 150 | cache map[string]security.Discharge // GUARDED_BY(RWMutex) |
| 151 | } |
| 152 | |
| 153 | // Add inserts the argument to the cache, possibly overwriting previous |
| 154 | // discharges for the same caveat. |
| 155 | func (dcc *dischargeCache) Add(discharges ...security.Discharge) { |
| 156 | dcc.mu.Lock() |
| 157 | for _, d := range discharges { |
| 158 | dcc.cache[d.ID()] = d |
| 159 | } |
| 160 | dcc.mu.Unlock() |
| 161 | } |
| 162 | |
| 163 | // Discharges takes a slice of caveats and a slice of discharges of the same |
| 164 | // length and fills in nil entries in the discharges slice with discharges |
| 165 | // from the cache (if there are any). |
Asim Shankar | 77befba | 2015-01-09 12:49:04 -0800 | [diff] [blame] | 166 | // |
Ankur | e49a86a | 2014-11-11 18:52:43 -0800 | [diff] [blame] | 167 | // REQUIRES: len(caveats) == len(out) |
Asim Shankar | 19da818 | 2015-02-06 01:41:16 -0800 | [diff] [blame] | 168 | // REQUIRES: caveats[i].ThirdPartyDetails() != nil, for all 0 <= i < len(caveats) |
| 169 | func (dcc *dischargeCache) Discharges(caveats []security.Caveat, out []security.Discharge) (remaining int) { |
Ankur | e49a86a | 2014-11-11 18:52:43 -0800 | [diff] [blame] | 170 | dcc.mu.Lock() |
| 171 | for i, d := range out { |
| 172 | if d != nil { |
| 173 | continue |
| 174 | } |
Asim Shankar | 19da818 | 2015-02-06 01:41:16 -0800 | [diff] [blame] | 175 | if out[i] = dcc.cache[caveats[i].ThirdPartyDetails().ID()]; out[i] == nil { |
Asim Shankar | 77befba | 2015-01-09 12:49:04 -0800 | [diff] [blame] | 176 | remaining++ |
| 177 | } |
Ankur | e49a86a | 2014-11-11 18:52:43 -0800 | [diff] [blame] | 178 | } |
| 179 | dcc.mu.Unlock() |
Asim Shankar | 77befba | 2015-01-09 12:49:04 -0800 | [diff] [blame] | 180 | return |
Ankur | e49a86a | 2014-11-11 18:52:43 -0800 | [diff] [blame] | 181 | } |
| 182 | |
| 183 | func (dcc *dischargeCache) invalidate(discharges ...security.Discharge) { |
| 184 | dcc.mu.Lock() |
| 185 | for _, d := range discharges { |
| 186 | if dcc.cache[d.ID()] == d { |
| 187 | delete(dcc.cache, d.ID()) |
| 188 | } |
| 189 | } |
| 190 | dcc.mu.Unlock() |
| 191 | } |
| 192 | |
Asim Shankar | 8f05c22 | 2014-10-06 22:08:19 -0700 | [diff] [blame] | 193 | // filteredImpetus returns a copy of 'before' after removing any values that are not required as per 'r'. |
| 194 | func filteredImpetus(r security.ThirdPartyRequirements, before security.DischargeImpetus) (after security.DischargeImpetus) { |
| 195 | if r.ReportServer && len(before.Server) > 0 { |
| 196 | after.Server = make([]security.BlessingPattern, len(before.Server)) |
| 197 | for i := range before.Server { |
| 198 | after.Server[i] = before.Server[i] |
Asim Shankar | 1fecbcc | 2014-09-15 00:39:13 -0700 | [diff] [blame] | 199 | } |
Asim Shankar | a94e507 | 2014-08-19 18:18:36 -0700 | [diff] [blame] | 200 | } |
| 201 | if r.ReportMethod { |
Asim Shankar | 8f05c22 | 2014-10-06 22:08:19 -0700 | [diff] [blame] | 202 | after.Method = before.Method |
Asim Shankar | a94e507 | 2014-08-19 18:18:36 -0700 | [diff] [blame] | 203 | } |
Asim Shankar | 8f05c22 | 2014-10-06 22:08:19 -0700 | [diff] [blame] | 204 | if r.ReportArguments && len(before.Arguments) > 0 { |
Todd Wang | b86b352 | 2015-01-22 13:34:20 -0800 | [diff] [blame] | 205 | after.Arguments = make([]vdl.AnyRep, len(before.Arguments)) |
Asim Shankar | 8f05c22 | 2014-10-06 22:08:19 -0700 | [diff] [blame] | 206 | for i := range before.Arguments { |
| 207 | after.Arguments[i] = before.Arguments[i] |
Asim Shankar | a94e507 | 2014-08-19 18:18:36 -0700 | [diff] [blame] | 208 | } |
| 209 | } |
| 210 | return |
| 211 | } |