Example #1
0
// Accepts an incoming tunneling request from a remote, initializes and stores
// the new tunnel into the connection state.
func (c *Connection) buildTunnel(remote uint64, id uint64, key []byte, addrs []string, timeout time.Duration) (*Tunnel, error) {
	deadline := time.Now().Add(timeout)

	// Create the local tunnel endpoint
	c.tunLock.Lock()
	tunId := c.tunIdx
	tun := &Tunnel{
		id:    tunId,
		owner: c,
		term:  make(chan struct{}),
	}
	c.tunIdx++
	c.tunLive[tunId] = tun
	c.tunLock.Unlock()

	// Dial the remote tunnel listener
	var err error
	var strm *stream.Stream
	for _, addr := range addrs {
		strm, err = stream.Dial(addr, timeout)
		if err == nil {
			break
		}
	}
	// If no error occurred, initialize the client endpoint
	if err == nil {
		var conn *link.Link
		conn, err = c.initClientTunnel(strm, remote, id, key, deadline)
		if err != nil {
			if err := strm.Close(); err != nil {
				log.Printf("iris: failed to close uninitialized client tunnel stream: %v.", err)
			}
		} else {
			// Make sure the tunnel wasn't terminated since (init/close race)
			tun.lock.Lock()
			select {
			case <-tun.term:
				conn.Close()
				err = ErrTerminating
			default:
				tun.conn = conn
			}
			tun.lock.Unlock()
		}
	}
	// Tunneling failed, clean up and report error
	if err != nil {
		c.tunLock.Lock()
		delete(c.tunLive, tunId)
		c.tunLock.Unlock()
		return nil, err
	}
	return tun, nil
}
Example #2
0
// Initializes a stream into an encrypted tunnel link.
func (c *Connection) initClientTunnel(strm *stream.Stream, remote uint64, id uint64, key []byte, deadline time.Time) (*link.Link, error) {
	// Set a socket deadline for finishing the handshake
	strm.Sock().SetDeadline(deadline)
	defer strm.Sock().SetDeadline(time.Time{})

	// Send the unencrypted tunnel id to associate with the remote tunnel
	init := &initPacket{ConnId: remote, TunId: id}
	if err := strm.Send(init); err != nil {
		return nil, err
	}
	// Create the encrypted link and authorize it
	hasher := func() hash.Hash { return config.HkdfHash.New() }
	hkdf := hkdf.New(hasher, key, config.HkdfSalt, config.HkdfInfo)
	conn := link.New(strm, hkdf, false)

	// Send and retrieve an authorization to verify both directions
	auth := &proto.Message{
		Head: proto.Header{
			Meta: &authPacket{Id: id},
		},
	}
	if err := conn.SendDirect(auth); err != nil {
		return nil, err
	}
	if msg, err := conn.RecvDirect(); err != nil {
		return nil, err
	} else if auth, ok := msg.Head.Meta.(*authPacket); !ok || auth.Id != id {
		return nil, errors.New("protocol violation")
	}
	conn.Start(config.IrisTunnelBuffer)

	// Return the initialized link
	return conn, nil
}
Example #3
0
// Executes the server side authentication and returns either the agreed secret
// session key or the a failure reason.
func (l *Listener) serverAuth(strm *stream.Stream, req *authRequest) ([]byte, error) {
	// Create a new STS session
	stsSess, err := sts.New(rand.Reader, config.StsGroup, config.StsGenerator,
		config.StsCipher, config.StsCipherBits, config.StsSigHash)
	if err != nil {
		return nil, fmt.Errorf("failed to create STS session: %v", err)
	}
	// Accept the incoming key exchange request and send back own exp + auth token
	exp, token, err := stsSess.Accept(rand.Reader, l.key, req.Exp)
	if err != nil {
		return nil, fmt.Errorf("failed to accept incoming exchange: %v", err)
	}
	if err = strm.Send(authChallenge{exp, token}); err != nil {
		return nil, fmt.Errorf("failed to encode auth challenge: %v", err)
	}
	if err = strm.Flush(); err != nil {
		return nil, fmt.Errorf("failed to flush auth challenge: %v", err)
	}
	// Receive the foreign auth token and if verifies conclude session
	resp := new(authResponse)
	if err = strm.Recv(resp); err != nil {
		return nil, fmt.Errorf("failed to decode auth response: %v", err)
	}
	if err = stsSess.Finalize(&l.key.PublicKey, resp.Token); err != nil {
		return nil, fmt.Errorf("failed to finalize exchange: %v", err)
	}
	return stsSess.Secret()
}
Example #4
0
// Initializes a stream into an encrypted tunnel link.
func (o *Overlay) initServerTunnel(strm *stream.Stream) error {
	// Set a socket deadline for finishing the handshake
	strm.Sock().SetDeadline(time.Now().Add(config.IrisTunnelInitTimeout))
	defer strm.Sock().SetDeadline(time.Time{})

	// Fetch the unencrypted client initiator
	init := new(initPacket)
	if err := strm.Recv(init); err != nil {
		return err
	}
	o.lock.RLock()
	c, ok := o.conns[init.ConnId]
	o.lock.RUnlock()
	if !ok {
		return errors.New("connection not found")
	}
	c.tunLock.RLock()
	tun, ok := c.tunLive[init.TunId]
	c.tunLock.RUnlock()
	if !ok {
		return errors.New("tunnel not found")
	}
	// Create the encrypted link
	hasher := func() hash.Hash { return config.HkdfHash.New() }
	hkdf := hkdf.New(hasher, tun.secret, config.HkdfSalt, config.HkdfInfo)
	conn := link.New(strm, hkdf, true)

	// Send and retrieve an authorization to verify both directions
	auth := &proto.Message{
		Head: proto.Header{
			Meta: &authPacket{Id: tun.id},
		},
	}
	if err := conn.SendDirect(auth); err != nil {
		return err
	}
	if msg, err := conn.RecvDirect(); err != nil {
		return err
	} else if auth, ok := msg.Head.Meta.(*authPacket); !ok || auth.Id != tun.id {
		return errors.New("protocol violation")
	}
	conn.Start(config.IrisTunnelBuffer)

	// Send back the initialized link to the pending tunnel
	select {
	case tun.initDone <- conn:
		// Connection handled by initiator
		return nil
	case <-tun.initStop:
		// Initiator timed out or terminated, close
		conn.Close()
		return nil // No error, since tunnel was handled, albeit not as expected
	}
}
Example #5
0
// Client side of the STS session negotiation.
func clientAuth(strm *stream.Stream, key *rsa.PrivateKey) ([]byte, error) {
	// Set an overall time limit for the handshake to complete
	strm.Sock().SetDeadline(time.Now().Add(config.SessionShakeTimeout))
	defer strm.Sock().SetDeadline(time.Time{})

	// Create a new empty session
	stsSess, err := sts.New(rand.Reader, config.StsGroup, config.StsGenerator, config.StsCipher, config.StsCipherBits, config.StsSigHash)
	if err != nil {
		return nil, fmt.Errorf("failed to create new session: %v", err)
	}
	// Initiate a key exchange, send the exponential
	exp, err := stsSess.Initiate()
	if err != nil {
		return nil, fmt.Errorf("failed to initiate key exchange: %v", err)
	}
	req := &initRequest{
		Auth: &authRequest{exp},
	}
	if err = strm.Send(req); err != nil {
		return nil, fmt.Errorf("failed to send auth request: %v", err)
	}
	if err = strm.Flush(); err != nil {
		return nil, fmt.Errorf("failed to flush auth request: %v", err)
	}
	// Receive the foreign exponential and auth token and if verifies, send own auth
	chall := new(authChallenge)
	if err = strm.Recv(chall); err != nil {
		return nil, fmt.Errorf("failed to receive auth challenge: %v", err)
	}
	token, err := stsSess.Verify(rand.Reader, key, &key.PublicKey, chall.Exp, chall.Token)
	if err != nil {
		return nil, fmt.Errorf("failed to verify acceptor auth token: %v", err)
	}
	if err = strm.Send(authResponse{token}); err != nil {
		return nil, fmt.Errorf("failed to send auth response: %v", err)
	}
	if err = strm.Flush(); err != nil {
		return nil, fmt.Errorf("failed to flush auth response: %v", err)
	}
	return stsSess.Secret()
}
Example #6
0
// Server side of the STS session negotiation.
func (l *Listener) serverHandle(strm *stream.Stream, timeout time.Duration) {
	// Make sure the authentication is synced with the sink
	defer l.pendWait.Done()

	// Set an overall time limit for the handshake to complete
	strm.Sock().SetDeadline(time.Now().Add(config.SessionShakeTimeout))
	defer strm.Sock().SetDeadline(time.Time{})

	// Fetch the session request and multiplex on the contents
	req := new(initRequest)
	if err := strm.Recv(req); err != nil {
		log.Printf("session: failed to retrieve initiation request: %v", err)
		if err = strm.Close(); err != nil {
			log.Printf("session: failed to close uninitialized stream: %v.", err)
		}
		return
	}
	switch {
	case req.Auth != nil:
		// Authenticate and clean up if unsuccessful
		secret, err := l.serverAuth(strm, req.Auth)
		if err != nil {
			log.Printf("session: failed to authenticate remote stream: %v.", err)
			if err = strm.Close(); err != nil {
				log.Printf("session: failed to close unauthenticated stream: %v.", err)
			}
			return
		}
		// Create the session and link a data channel to it
		sess := newSession(strm, secret, true)
		if err = l.serverLink(sess); err != nil {
			log.Printf("session: failed to retrieve data link: %v.", err)
			if err = strm.Close(); err != nil {
				log.Printf("session: failed to close unlinked stream: %v.", err)
			}
			return
		}
		// Session setup complete, send upstream
		select {
		case l.Sink <- sess:
			// Ok
		case <-time.After(timeout):
			log.Printf("session: established session not handled in %v, dropping.", timeout)
			if err = sess.Close(); err != nil {
				log.Printf("session: failed to close established session: %v.", err)
			}
		}
	case req.Link != nil:
		// Extract the temporary session id and link this stream to it
		l.pendLock.Lock()
		res, ok := l.pends[req.Link.Id]
		l.pendLock.Unlock()

		if ok {
			select {
			case res <- strm:
				// Ok, link succeeded
			default:
				log.Printf("session: established data stream not handled.")
				if err := strm.Close(); err != nil {
					log.Printf("session: failed to close established data stream: %v.", err)
				}
			}
		}
	}
}