1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define TLOG_TAG "metrics-consumer"
18 #include "consumer.h"
19 
20 #include <interface/metrics/consumer.h>
21 #include <lib/tipc/tipc.h>
22 #include <lib/tipc/tipc_srv.h>
23 #include <metrics_consts.h>
24 #include <openssl/sha.h>
25 #include <stddef.h>
26 #include <string.h>
27 #include <trusty_log.h>
28 #include <uapi/err.h>
29 
30 /*
31 Current use cases:
32 1) Metrics daemon
33 2) Trusty kernel metrics reporter
34 3) Storage metrics
35 4) Android metrics test
36 */
37 #define MAX_METRICS_TA_CONNECTIONS 4
38 
39 static const struct uuid zero_uuid = UUID_INITIAL_VALUE(zero_uuid);
40 
is_zero_uuid(const struct uuid * peer)41 static bool is_zero_uuid(const struct uuid* peer) {
42     return equal_uuid(peer, &zero_uuid);
43 }
44 
on_connect(const struct tipc_port * port,handle_t chan,const struct uuid * peer,void ** ctx_p)45 static int on_connect(const struct tipc_port* port,
46                       handle_t chan,
47                       const struct uuid* peer,
48                       void** ctx_p) {
49 
50     struct srv_state* state = get_srv_state(port);
51     if(is_zero_uuid(peer))
52     {
53         TLOGD("Updating metrics daemon handle :%d\n", chan);
54         if(state->ns_handle != INVALID_IPC_HANDLE) {
55                 close(state->ns_handle);
56         }
57         state->ns_handle = chan;
58     }
59 
60     return NO_ERROR;
61 }
62 
hash_trusty_metrics(uint64_t metric,char * app_id,uint8_t * output)63 void hash_trusty_metrics(uint64_t metric, char *app_id, uint8_t *output) {
64 
65     const unsigned char CONST_SALT[] = {
66     0xf2, 0xe7, 0x8c, 0x19, 0xa4, 0xd3, 0x5b, 0x68
67     };
68 
69     /* Convert the metric to an array of uint8_t prepended with salt*/
70     uint8_t metric_arr[8 + UUID_STR_SIZE + sizeof(CONST_SALT)];
71 
72     memcpy(metric_arr, app_id, UUID_STR_SIZE);
73     memcpy(metric_arr+UUID_STR_SIZE, CONST_SALT, sizeof(CONST_SALT));
74 
75     for (size_t i = 0; i < 8; ++i) {
76         metric_arr[i+ UUID_STR_SIZE + sizeof(CONST_SALT)] = (metric >> (8 * i)) & 0xFF;
77     }
78 
79     SHA512(metric_arr, sizeof(metric_arr), output);
80 }
81 
on_message(const struct tipc_port * port,handle_t chan,void * ctx)82 static int on_message(const struct tipc_port* port, handle_t chan, void* ctx) {
83     int rc;
84     struct metrics_req req;
85     uint8_t msg[METRICS_MAX_MSG_SIZE];
86 
87     memset(msg, 0, sizeof(msg));
88     int msg_size = tipc_recv1(chan, sizeof(req),  msg, sizeof(msg));
89     if (msg_size < 0) {
90         TLOGE("failed (%d) to receive metrics event\n", msg_size);
91         return msg_size;
92     }
93 
94     uint32_t cmd;
95     cmd = ((struct metrics_req*)msg)->cmd;
96 
97     if(cmd == METRICS_CMD_REPORT_CRASH) {
98         struct metrics_crash_msg *crash_msg = (struct metrics_crash_msg *)msg;
99         if (crash_msg->crash_args.is_hash) {
100             hash_trusty_metrics(crash_msg->crash_args.far, crash_msg->crash_args.app_id, crash_msg->crash_args.far_hash);
101             hash_trusty_metrics(crash_msg->crash_args.elr, crash_msg->crash_args.app_id, crash_msg->crash_args.elr_hash);
102             crash_msg->crash_args.far = 0;
103             crash_msg->crash_args.elr = 0;
104         }
105     }
106 
107     // Check if NS metricsd connected, if so forward it there.
108     struct srv_state* state = get_srv_state(port);
109     if(is_ns_connected(state)) {
110         rc = tipc_send1(state->ns_handle, msg, msg_size);
111         if (rc < 0) {
112             TLOGE("failed (%d) to send metrics event tp NS metricsd\n", rc);
113             return rc;
114         }
115     }
116     else {
117         TLOGD("NS metrics daemon not connected\n");
118     }
119 
120     struct metrics_resp resp = {
121         .cmd = (cmd | METRICS_CMD_RESP_BIT)
122     };
123 
124     rc = tipc_send1(chan, &resp, sizeof(resp));
125     if (rc < 0) {
126         TLOGE("failed (%d) to send metrics event response\n", rc);
127         return rc;
128     }
129 
130     if ((size_t)rc != sizeof(resp)) {
131         TLOGE("unexpected number of bytes sent: %d\n", rc);
132         return ERR_BAD_LEN;
133     }
134 
135     return NO_ERROR;
136 }
137 
add_metrics_consumer_service(struct tipc_hset * hset,struct srv_state * state)138 int add_metrics_consumer_service(struct tipc_hset* hset, struct srv_state* state) {
139     static struct tipc_port_acl port_acl = {
140             .flags = IPC_PORT_ALLOW_TA_CONNECT | IPC_PORT_ALLOW_NS_CONNECT,
141     };
142     static struct tipc_port port = {
143             .name = METRICS_CONSUMER_PORT,
144             .msg_max_size = METRICS_MAX_MSG_SIZE,
145             .msg_queue_len = 1,
146             .acl = &port_acl,
147     };
148     static struct tipc_srv_ops ops = {
149             .on_message = on_message,
150             .on_connect = on_connect,
151     };
152     set_srv_state(&port, state);
153 
154     return tipc_add_service(hset, &port, 1, MAX_METRICS_TA_CONNECTIONS, &ops);
155 }