1 // Copyright 2016, 2017 Thales e-Security, Inc 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining 4 // a copy of this software and associated documentation files (the 5 // "Software"), to deal in the Software without restriction, including 6 // without limitation the rights to use, copy, modify, merge, publish, 7 // distribute, sublicense, and/or sell copies of the Software, and to 8 // permit persons to whom the Software is furnished to do so, subject to 9 // the following conditions: 10 // 11 // The above copyright notice and this permission notice shall be 12 // included in all copies or substantial portions of the Software. 13 // 14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 22 package crypto11 23 24 import ( 25 "context" 26 "errors" 27 28 "github.com/miekg/pkcs11" 29 "github.com/thales-e-security/pool" 30 ) 31 32 // pkcs11Session wraps a PKCS#11 session handle so we can use it in a resource pool. 33 type pkcs11Session struct { 34 ctx *pkcs11.Ctx 35 handle pkcs11.SessionHandle 36 } 37 38 // Close is required to satisfy the pools.Resource interface. It closes the session, but swallows any 39 // errors that occur. 40 func (s pkcs11Session) Close() { 41 // We cannot return an error, so we swallow it 42 _ = s.ctx.CloseSession(s.handle) 43 } 44 45 // withSession executes a function with a session. 46 func (c *Context) withSession(f func(session *pkcs11Session) error) error { 47 session, err := c.getSession() 48 if err != nil { 49 return err 50 } 51 defer c.pool.Put(session) 52 53 return f(session) 54 } 55 56 // getSession retrieves a session from the pool, respecting the timeout defined in the Context config. 57 // Callers are responsible for putting this session back in the pool. 58 func (c *Context) getSession() (*pkcs11Session, error) { 59 ctx := context.Background() 60 61 if c.cfg.PoolWaitTimeout > 0 { 62 var cancel context.CancelFunc 63 ctx, cancel = context.WithTimeout(context.Background(), c.cfg.PoolWaitTimeout) 64 defer cancel() 65 } 66 67 resource, err := c.pool.Get(ctx) 68 if err == pool.ErrClosed { 69 // Our Context must have been closed, return a nicer error. 70 // We don't use errClosed to ensure our tests identify functions that aren't checking for closure 71 // correctly. 72 return nil, errors.New("context is closed") 73 } 74 if err != nil { 75 return nil, err 76 } 77 78 return resource.(*pkcs11Session), nil 79 } 80 81 // resourcePoolFactoryFunc is called by the resource pool when a new session is needed. 82 func (c *Context) resourcePoolFactoryFunc() (pool.Resource, error) { 83 session, err := c.ctx.OpenSession(c.slot, pkcs11.CKF_SERIAL_SESSION|pkcs11.CKF_RW_SESSION) 84 if err != nil { 85 return nil, err 86 } 87 return &pkcs11Session{c.ctx, session}, nil 88 } 89