blob: 38e94abd392666882f86aa098baec0ac8570c80b [file] [log] [blame]
/*
*
* seeld - Secure Element Manager
*
* Copyright (C) 2013 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <glib.h>
#include <gdbus.h>
#include "driver.h"
#include "seel.h"
struct seel_channel {
char *path;
struct seel_se *se;
uint8_t channel;
uint8_t *aid;
size_t aid_len;
bool basic;
};
static DBusMessage *get_properties(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct seel_channel *channel = data;
DBusMessage *reply;
DBusMessageIter array, dict;
DBG("conn %p", conn);
reply = dbus_message_new_method_return(msg);
if (reply == NULL)
return NULL;
dbus_message_iter_init_append(reply, &array);
near_dbus_dict_open(&array, &dict);
near_dbus_dict_append_basic(&dict, "Basic",
DBUS_TYPE_BOOLEAN, &channel->basic);
near_dbus_dict_append_fixed_array(&dict, "AID", DBUS_TYPE_BYTE,
&channel->aid, channel->aid_len);
near_dbus_dict_close(&array, &dict);
return reply;
}
static void send_apdu_cb(void *context,
uint8_t *apdu, size_t apdu_length,
int err)
{
DBusMessage *pending_msg = context;
DBusConnection *conn;
conn = near_dbus_get_connection();
if (err) {
DBusMessage *reply;
reply = __near_error_failed(pending_msg, -err);
g_dbus_send_message(conn, reply);
dbus_message_unref(pending_msg);
return;
}
g_dbus_send_reply(conn, pending_msg,
DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
&apdu, apdu_length,
DBUS_TYPE_INVALID);
dbus_message_unref(pending_msg);
DBG("");
}
static DBusMessage *send_apdu(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct seel_channel *channel = data;
DBusMessage *pending_msg;
struct seel_apdu *send_apdu;
uint8_t *apdu, *app_hash;
const GSList *app_hashes, *list;
size_t apdu_len;
const char *sender;
int err;
DBG("conn %p", conn);
if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
&apdu, &apdu_len, DBUS_TYPE_INVALID))
return __near_error_invalid_arguments(msg);
sender = dbus_message_get_sender(msg);
app_hashes = __seel_se_get_hashes(channel->se, sender);
for (list = app_hashes; list; list = list->next) {
app_hash = list->data;
if (__seel_ace_apdu_allowed(channel, app_hash, apdu, apdu_len))
goto send_data;
}
near_error("*** APDU not allowed ***");
return __near_error_permission_denied(msg);
send_data:
pending_msg = dbus_message_ref(msg);
send_apdu = __seel_apdu_build(apdu, apdu_len, channel->channel);
if (!send_apdu) {
dbus_message_unref(pending_msg);
return __near_error_out_of_memory(msg);
}
err = __seel_se_queue_io(channel->se, send_apdu,
send_apdu_cb, pending_msg);
if (err < 0) {
near_error("send apdu error %d", err);
return NULL;
}
return NULL;
}
static const GDBusMethodTable channel_methods[] = {
{ GDBUS_METHOD("GetProperties",
NULL, GDBUS_ARGS({"properties", "a{sv}"}),
get_properties) },
{ GDBUS_ASYNC_METHOD("SendAPDU",
GDBUS_ARGS({"apdu", "ay"}),
GDBUS_ARGS({"resp", "ay"}),
send_apdu) },
{ },
};
static const GDBusSignalTable channel_signals[] = {
{ GDBUS_SIGNAL("PropertyChanged",
GDBUS_ARGS({"name", "s"}, {"value", "v"})) },
{ }
};
struct seel_channel *__seel_channel_add(struct seel_se *se, uint8_t chn,
uint8_t *aid, size_t aid_len, bool basic)
{
struct seel_channel *channel;
const char *se_path;
char *path;
DBusConnection *conn;
DBG("");
conn = near_dbus_get_connection();
se_path = __seel_se_get_path(se);
if (!se_path)
return NULL;
path = g_try_malloc0(strlen(se_path) + 16);
if (!path)
return NULL;
g_snprintf(path, strlen(se_path) + 16, "%s/channel%d", se_path, chn);
DBG("%s", path);
channel = g_try_malloc0(sizeof(struct seel_channel));
if (!channel) {
g_free(path);
return NULL;
}
if (aid && aid_len) {
channel->aid = g_try_malloc0(aid_len);
if (!channel->aid) {
g_free(path);
g_free(channel);
return NULL;
}
memcpy(channel->aid, aid, aid_len);
}
channel->aid_len = aid_len;
channel->se = se;
channel->path = path;
channel->basic = basic;
channel->channel = chn;
g_dbus_register_interface(conn, channel->path,
SEEL_CHANNEL_INTERFACE,
channel_methods, channel_signals,
NULL, channel, NULL);
return channel;
}
void __seel_channel_remove(struct seel_channel *channel)
{
DBusConnection *conn;
DBG("");
conn = near_dbus_get_connection();
g_dbus_unregister_interface(conn, channel->path,
SEEL_CHANNEL_INTERFACE);
g_free(channel->path);
g_free(channel->aid);
g_free(channel);
}
char *__seel_channel_get_path(struct seel_channel *channel)
{
return channel->path;
}
uint8_t __seel_channel_get_channel(struct seel_channel *channel)
{
return channel->channel;
}
uint8_t *__seel_channel_get_aid(struct seel_channel *channel, size_t *aid_len)
{
if (!channel->aid || !channel->aid_len)
return NULL;
*aid_len = channel->aid_len;
return channel->aid;
}
struct seel_se *__seel_channel_get_se(struct seel_channel *channel)
{
return channel->se;
}
bool __seel_channel_is_basic(struct seel_channel *channel)
{
return channel->basic;
}