コード例 #1
0
ファイル: room.go プロジェクト: robot0x/heim
func (rb *RoomBinding) MessageKey(ctx scope.Context) (proto.RoomMessageKey, error) {
	var row struct {
		MessageKey
		RoomMessageKey
	}
	err := rb.DbMap.SelectOne(
		&row,
		"SELECT mk.id, mk.encrypted_key, mk.iv, mk.nonce,"+
			" r.room, r.key_id, r.activated, r.expired, r.comment"+
			" FROM master_key mk, room_master_key r"+
			" WHERE r.room = $1 AND mk.id = r.key_id AND r.expired < r.activated"+
			" ORDER BY r.activated DESC LIMIT 1",
		rb.Name)
	if err != nil {
		if err == sql.ErrNoRows {
			return nil, nil
		}
		return nil, err
	}

	msgKey := &security.ManagedKey{
		KeyType:      proto.RoomMessageKeyType,
		IV:           row.MessageKey.IV,
		Ciphertext:   row.MessageKey.EncryptedKey,
		ContextKey:   "room",
		ContextValue: rb.Room.Name,
	}
	var keyID snowflake.Snowflake
	if err := keyID.FromString(row.KeyID); err != nil {
		return nil, err
	}
	return NewRoomMessageKeyBinding(rb, keyID, msgKey, row.Nonce), nil
}
コード例 #2
0
ファイル: client.go プロジェクト: logan/heim
func (c *Client) AuthenticateWithAgent(ctx scope.Context, backend Backend, agent *Agent, agentKey *security.ManagedKey) error {
	if agent.AccountID == "" {
		return nil
	}

	var accountID snowflake.Snowflake
	if err := accountID.FromString(agent.AccountID); err != nil {
		return err
	}

	account, err := backend.AccountManager().Get(ctx, accountID)
	if err != nil {
		if err == ErrAccountNotFound {
			return nil
		}
		return err
	}

	clientKey, err := agent.Unlock(agentKey)
	if err != nil {
		return fmt.Errorf("agent key error: %s", err)
	}

	c.Account = account
	c.Authorization.ClientKey = clientKey
	return nil
}
コード例 #3
0
ファイル: server.go プロジェクト: algustionesa/heim
func (s *Server) handleRoom(w http.ResponseWriter, r *http.Request) {
	ctx := s.rootCtx.Fork()

	// Resolve the room.
	// TODO: support room creation?
	roomName := mux.Vars(r)["room"]
	room, err := s.b.GetRoom(ctx, roomName)
	if err != nil {
		if err == proto.ErrRoomNotFound {
			http.Error(w, "404 page not found", http.StatusNotFound)
			return
		}
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	// Tag the agent. We use an authenticated but un-encrypted cookie.
	agent, cookie, agentKey, err := getAgent(ctx, s, r)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	client := &proto.Client{Agent: agent}
	client.FromRequest(ctx, r)

	// Look up account associated with agent.
	var accountID snowflake.Snowflake
	if err := accountID.FromString(agent.AccountID); agent.AccountID != "" && err == nil {
		if err := client.AuthenticateWithAgent(ctx, s.b, room, agent, agentKey); err != nil {
			fmt.Printf("agent auth failed: %s\n", err)
			switch err {
			case proto.ErrAccessDenied:
				http.Error(w, err.Error(), http.StatusForbidden)
			default:
				http.Error(w, err.Error(), http.StatusInternalServerError)
			}
			return
		}
	}

	// Upgrade to a websocket and set cookie.
	headers := http.Header{}
	if cookie != nil {
		headers.Add("Set-Cookie", cookie.String())
	}
	conn, err := upgrader.Upgrade(w, r, headers)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	defer conn.Close()

	// Serve the session.
	session := newSession(ctx, s, conn, roomName, room, client, agentKey)
	if err = session.serve(); err != nil {
		// TODO: error handling
		return
	}
}
コード例 #4
0
ファイル: agent.go プロジェクト: logan/heim
func getClient(ctx scope.Context, s *Server, r *http.Request) (*proto.Client, *http.Cookie, *security.ManagedKey, error) {
	agent, cookie, agentKey, err := getAgent(ctx, s, r)
	if err != nil {
		return nil, nil, nil, err
	}

	client := &proto.Client{
		Agent: agent,
		Authorization: proto.Authorization{
			ClientKey: agentKey,
		},
	}
	client.FromRequest(ctx, r)

	// Look up account associated with agent.
	var accountID snowflake.Snowflake
	if err := accountID.FromString(agent.AccountID); agent.AccountID != "" && err == nil {
		if err := client.AuthenticateWithAgent(ctx, s.b, agent, agentKey); err != nil {
			fmt.Printf("agent auth failed: %s\n", err)
			switch err {
			case proto.ErrAccessDenied:
				// allow session to proceed, but agent will not be logged into account
				agent.AccountID = ""
			default:
				return nil, nil, nil, err
			}
		}
	}

	return client, cookie, agentKey, nil
}
コード例 #5
0
ファイル: handler.go プロジェクト: robot0x/heim
func (c *console) resolveAccount(ctx scope.Context, ref string) (proto.Account, error) {
	idx := strings.IndexRune(ref, ':')
	if idx < 0 {
		var accountID snowflake.Snowflake
		if err := accountID.FromString(ref); err != nil {
			return nil, err
		}
		return c.backend.AccountManager().Get(ctx, accountID)
	}
	return c.backend.AccountManager().Resolve(ctx, ref[:idx], ref[idx+1:])
}
コード例 #6
0
ファイル: message.go プロジェクト: logan/heim
func parseDeleteMessageArg(arg string) (string, snowflake.Snowflake, error) {
	parts := strings.SplitN(arg, ":", 2)
	if len(parts) != 2 {
		return "", 0, fmt.Errorf("format should be <room>:<message-id>")
	}

	var msgID snowflake.Snowflake
	if err := msgID.FromString(parts[1]); err != nil {
		return "", 0, err
	}

	return parts[0], msgID, nil
}
コード例 #7
0
ファイル: pm.go プロジェクト: logan/heim
func InitiatePM(
	ctx scope.Context, b Backend, kms security.KMS, client *Client, initiatorNick string, receiver UserID,
	receiverNick string) (*PM, error) {

	resolveAccount := func(accountIDStr string) (Account, error) {
		var accountID snowflake.Snowflake
		if err := accountID.FromString(accountIDStr); err != nil {
			return nil, err
		}
		return b.AccountManager().Get(ctx, accountID)
	}

	pm, pmKey, err := NewPM(kms, client, initiatorNick, receiver, receiverNick)
	if err != nil {
		return nil, fmt.Errorf("new pm: %s", err)
	}

	kind, id := receiver.Parse()
	switch kind {
	case "account":
		receiver, err := resolveAccount(id)
		if err != nil {
			return nil, err
		}
		return pm.transmitToAccount(kms, pmKey, receiver)
	case "agent", "bot":
		agent, err := b.AgentTracker().Get(ctx, id)
		if err != nil {
			return nil, err
		}
		if agent.AccountID != "" {
			receiver, err := resolveAccount(agent.AccountID)
			if err != nil {
				return nil, err
			}
			return pm.transmitToAccount(kms, pmKey, receiver)
		}
		// We can't transmit the key to the agent until the agent joins the chat.
		return pm, nil
	default:
		return nil, ErrInvalidUserID
	}
}
コード例 #8
0
ファイル: account.go プロジェクト: NotAMoose/heim
func ParsePasswordResetConfirmation(confirmation string) (snowflake.Snowflake, []byte, error) {
	var id snowflake.Snowflake

	idx := strings.IndexRune(confirmation, '-')
	if idx < 0 {
		return id, nil, ErrInvalidConfirmationCode
	}

	mac, err := hex.DecodeString(confirmation[idx+1:])
	if err != nil {
		return id, nil, ErrInvalidConfirmationCode
	}

	if err := id.FromString(confirmation[:idx]); err != nil {
		return id, nil, ErrInvalidConfirmationCode
	}

	return id, mac, nil
}
コード例 #9
0
ファイル: room.go プロジェクト: logan/heim
func (rb *ManagedRoomBinding) MessageKey(ctx scope.Context) (proto.RoomMessageKey, error) {
	var row struct {
		MessageKey
		RoomMessageKey
	}

	mkCols, err := allColumns(rb.DbMap, row.MessageKey, "mk")
	if err != nil {
		return nil, err
	}
	rCols, err := allColumns(rb.DbMap, row.RoomMessageKey, "r")
	if err != nil {
		return nil, err
	}

	err = rb.DbMap.SelectOne(
		&row,
		fmt.Sprintf("SELECT %s, %s FROM master_key mk, room_master_key r"+
			" WHERE r.room = $1 AND mk.id = r.key_id AND r.expired < r.activated"+
			" ORDER BY r.activated DESC LIMIT 1",
			mkCols, rCols),
		rb.RoomName)
	if err != nil {
		if err == sql.ErrNoRows {
			return nil, nil
		}
		return nil, err
	}

	msgKey := &security.ManagedKey{
		KeyType:      proto.RoomMessageKeyType,
		IV:           row.MessageKey.IV,
		Ciphertext:   row.MessageKey.EncryptedKey,
		ContextKey:   "room",
		ContextValue: rb.RoomName,
	}
	var keyID snowflake.Snowflake
	if err := keyID.FromString(row.KeyID); err != nil {
		return nil, err
	}
	return NewRoomMessageKeyBinding(rb, keyID, msgKey, row.Nonce), nil
}
コード例 #10
0
ファイル: account.go プロジェクト: NotAMoose/heim
func (b *AccountManagerBinding) resolve(
	db gorp.SqlExecutor, namespace, id string) (*AccountBinding, error) {

	var pid PersonalIdentity
	err := db.SelectOne(
		&pid,
		"SELECT account_id FROM personal_identity WHERE namespace = $1 AND id = $2",
		namespace, id)
	if err != nil {
		if err == sql.ErrNoRows {
			return nil, proto.ErrAccountNotFound
		}
		return nil, err
	}

	var accountID snowflake.Snowflake
	if err := accountID.FromString(pid.AccountID); err != nil {
		return nil, err
	}

	return b.get(db, accountID)
}
コード例 #11
0
ファイル: handlers.go プロジェクト: logan/heim
func (s *Server) resolveRoom(ctx scope.Context, prefix, roomName string, client *proto.Client) (room proto.Room, err error) {
	// TODO: support room creation?
	switch prefix {
	case "pm:":
		var (
			sf      snowflake.Snowflake
			roomKey *security.ManagedKey
		)
		if err := sf.FromString(roomName); err != nil {
			return nil, proto.ErrRoomNotFound
		}
		room, roomKey, err = s.b.PMTracker().Room(ctx, s.kms, sf, client)
		if err != nil {
			switch err {
			case proto.ErrAccessDenied, proto.ErrPMNotFound:
				return nil, proto.ErrRoomNotFound
			default:
				return nil, err
			}
		}
		client.Authorization.AddMessageKey("pm:"+roomName, roomKey)
		return room, nil
	case "":
		room, err = s.b.GetRoom(ctx, roomName)
		if s.allowRoomCreation && err == proto.ErrRoomNotFound {
			room, err = s.b.CreateRoom(ctx, s.kms, false, roomName)
		}
		if err != nil {
			return nil, err
		}
		if err := client.RoomAuthorize(ctx, room); err != nil {
			return nil, err
		}
		return room, nil
	default:
		return nil, proto.ErrRoomNotFound
	}
}
コード例 #12
0
ファイル: account.go プロジェクト: NotAMoose/heim
func (ab *AccountBinding) ID() snowflake.Snowflake {
	var id snowflake.Snowflake
	_ = id.FromString(ab.Account.ID)
	return id
}
コード例 #13
0
ファイル: client.go プロジェクト: robot0x/heim
func (c *Client) AuthenticateWithAgent(
	ctx scope.Context, backend Backend, room Room, agent *Agent, agentKey *security.ManagedKey) error {

	if agent.AccountID == "" {
		return nil
	}

	var accountID snowflake.Snowflake
	if err := accountID.FromString(agent.AccountID); err != nil {
		return err
	}

	account, err := backend.AccountManager().Get(ctx, accountID)
	if err != nil {
		if err == ErrAccountNotFound {
			return nil
		}
		return err
	}

	clientKey, err := agent.Unlock(agentKey)
	if err != nil {
		return fmt.Errorf("agent key error: %s", err)
	}

	c.Account = account
	c.Authorization.ClientKey = clientKey

	holderKey, err := account.Unlock(clientKey)
	if err != nil {
		return fmt.Errorf("client key error: %s", err)
	}

	managerKey, err := room.ManagerKey(ctx)
	if err != nil {
		return fmt.Errorf("manager key error: %s", err)
	}

	managerCap, err := room.ManagerCapability(ctx, account)
	if err != nil && err != ErrManagerNotFound {
		return err
	}
	if err == nil {
		subjectKey := managerKey.KeyPair()
		pc := &security.PublicKeyCapability{Capability: managerCap}
		secretJSON, err := pc.DecryptPayload(&subjectKey, holderKey)
		if err != nil {
			return fmt.Errorf("manager capability decrypt error: %s", err)
		}

		c.Authorization.ManagerKeyEncryptingKey = &security.ManagedKey{
			KeyType: RoomManagerKeyType,
		}
		err = json.Unmarshal(secretJSON, &c.Authorization.ManagerKeyEncryptingKey.Plaintext)
		if err != nil {
			return fmt.Errorf("manager key unmarshal error: %s", err)
		}

		managerKeyPair, err := managerKey.Unlock(c.Authorization.ManagerKeyEncryptingKey)
		if err != nil {
			return fmt.Errorf("manager key unlock error: %s", err)
		}

		c.Authorization.ManagerKeyPair = managerKeyPair
	}

	// Look for message key grants to this account.
	messageKey, err := room.MessageKey(ctx)
	if err != nil {
		return err
	}
	if messageKey != nil {
		capability, err := messageKey.AccountCapability(ctx, account)
		if err != nil {
			return fmt.Errorf("access capability error: %s", err)
		}
		if capability != nil {
			subjectKey := managerKey.KeyPair()
			roomKeyJSON, err := capability.DecryptPayload(&subjectKey, holderKey)
			if err != nil {
				return fmt.Errorf("access capability decrypt error: %s", err)
			}
			roomKey := &security.ManagedKey{
				KeyType: security.AES128,
			}
			if err := json.Unmarshal(roomKeyJSON, &roomKey.Plaintext); err != nil {
				return fmt.Errorf("access capability unmarshal error: %s", err)
			}
			c.Authorization.AddMessageKey(messageKey.KeyID(), roomKey)
			c.Authorization.CurrentMessageKeyID = messageKey.KeyID()
		}
	}

	return nil
}
コード例 #14
0
ファイル: pm.go プロジェクト: logan/heim
func (t *PMTracker) Initiate(
	ctx scope.Context, kms security.KMS, room proto.Room, client *proto.Client, recipient proto.UserID) (
	snowflake.Snowflake, error) {

	initiatorNick, ok, err := room.ResolveNick(ctx, proto.UserID(fmt.Sprintf("account:%s", client.Account.ID())))
	if err != nil {
		return 0, err
	}
	if !ok {
		initiatorNick = fmt.Sprintf("account:%s", client.Account.ID())
	}

	recipientNick, ok, err := room.ResolveNick(ctx, recipient)
	if err != nil {
		return 0, err
	}
	if !ok {
		recipientNick = string(recipient)
	}

	pm, err := proto.InitiatePM(ctx, t.Backend, kms, client, initiatorNick, recipient, recipientNick)
	if err != nil {
		return 0, err
	}
	row := &PM{
		ID:                    pm.ID.String(),
		Initiator:             pm.Initiator.String(),
		InitiatorNick:         pm.InitiatorNick,
		Receiver:              string(pm.Receiver),
		ReceiverNick:          pm.ReceiverNick,
		ReceiverMAC:           pm.ReceiverMAC,
		IV:                    pm.IV,
		EncryptedSystemKey:    pm.EncryptedSystemKey.Ciphertext,
		EncryptedInitiatorKey: pm.EncryptedInitiatorKey.Ciphertext,
	}
	if pm.EncryptedReceiverKey != nil {
		row.EncryptedReceiverKey = pm.EncryptedReceiverKey.Ciphertext
	}

	// Look for existing PM to reuse.
	tx, err := t.DbMap.Begin()
	if err != nil {
		return 0, err
	}

	var existingRow PM
	err = tx.SelectOne(
		&existingRow,
		"SELECT id FROM pm WHERE initiator = $1 AND receiver = $2",
		client.Account.ID().String(), string(recipient))
	if err != nil && err != sql.ErrNoRows {
		rollback(ctx, tx)
		return 0, err
	}
	if err == nil {
		rollback(ctx, tx)
		var pmID snowflake.Snowflake
		if err := pmID.FromString(existingRow.ID); err != nil {
			return 0, err
		}
		return pmID, nil
	}

	kind, id := recipient.Parse()
	if kind == "account" {
		var existingRow PM
		err = tx.SelectOne(
			&existingRow,
			"SELECT id FROM pm WHERE initiator = $1 AND receiver = $2",
			id, string(client.UserID()))
		if err != nil && err != sql.ErrNoRows {
			rollback(ctx, tx)
			return 0, err
		}
		if err == nil {
			rollback(ctx, tx)
			var pmID snowflake.Snowflake
			if err := pmID.FromString(existingRow.ID); err != nil {
				return 0, err
			}
			return pmID, nil
		}
	}

	if err := tx.Insert(row); err != nil {
		rollback(ctx, tx)
		return 0, err
	}

	if err := tx.Commit(); err != nil {
		return 0, err
	}

	return pm.ID, nil
}