895 lines
24 KiB
C
895 lines
24 KiB
C
|
|
#define MODULE_LOG_PREFIX "emu"
|
||
|
|
|
||
|
|
#include "globals.h"
|
||
|
|
|
||
|
|
#ifdef WITH_EMU
|
||
|
|
|
||
|
|
#include "module-streamrelay.h"
|
||
|
|
#include "module-emulator-osemu.h"
|
||
|
|
#include "module-emulator-biss.h"
|
||
|
|
#include "module-emulator-irdeto.h"
|
||
|
|
#include "module-emulator-powervu.h"
|
||
|
|
#include "oscam-conf-chk.h"
|
||
|
|
#include "oscam-config.h"
|
||
|
|
#include "oscam-reader.h"
|
||
|
|
#include "oscam-string.h"
|
||
|
|
|
||
|
|
/*
|
||
|
|
* Readers in OSCam consist of 2 basic parts.
|
||
|
|
* The hardware or the device part. This is where physical smart cards are inserted
|
||
|
|
* and made available to OSCam.
|
||
|
|
* The software or the emulation part. This is where the actual card reading is done,
|
||
|
|
* including ecm and emm processing (i.e emulation of the various cryptosystems).
|
||
|
|
* In the Emu reader, the device part has no meaning, but we have to create it in
|
||
|
|
* order to be compatible with OSCam's reader structure.
|
||
|
|
*/
|
||
|
|
|
||
|
|
/*
|
||
|
|
* Create the Emu "emulation" part. This is of type s_cardsystem.
|
||
|
|
* Similar structures are found in the main sources folder (files reader-xxxxxx.c)
|
||
|
|
* for every cryptosystem supported by OSCam.
|
||
|
|
* Here we read keys from our virtual card (aka the SoftCam.Key file) and we inform
|
||
|
|
* OSCam about them. This is done with the emu_card_info() function. Keep in mind
|
||
|
|
* that Emu holds all its keys to separate structures for faster access.
|
||
|
|
* In addition, ECM and EMM requests are processed here, with the emu_do_ecm() and
|
||
|
|
* emu_do_emm() functions.
|
||
|
|
*/
|
||
|
|
|
||
|
|
#define CS_OK 1
|
||
|
|
#define CS_ERROR 0
|
||
|
|
|
||
|
|
extern char cs_confdir[128];
|
||
|
|
#ifdef MODULE_STREAMRELAY
|
||
|
|
static int8_t emu_key_data_mutex_init = 0;
|
||
|
|
#endif
|
||
|
|
pthread_mutex_t emu_key_data_mutex;
|
||
|
|
|
||
|
|
static void set_hexserial_to_version(struct s_reader *rdr)
|
||
|
|
{
|
||
|
|
char cVersion[32];
|
||
|
|
uint32_t version = EMU_VERSION;
|
||
|
|
uint8_t hversion[2];
|
||
|
|
memset(hversion, 0, 2);
|
||
|
|
snprintf(cVersion, sizeof(cVersion), "%04d", version);
|
||
|
|
char_to_bin(hversion, cVersion, 4);
|
||
|
|
rdr->hexserial[3] = hversion[0];
|
||
|
|
rdr->hexserial[4] = hversion[1];
|
||
|
|
}
|
||
|
|
|
||
|
|
static void set_prids(struct s_reader *rdr)
|
||
|
|
{
|
||
|
|
int32_t i, j;
|
||
|
|
|
||
|
|
rdr->nprov = 0;
|
||
|
|
|
||
|
|
for (i = 0; (i < rdr->emu_auproviders.nfilts) && (rdr->nprov < CS_MAXPROV); i++)
|
||
|
|
{
|
||
|
|
for (j = 0; (j < rdr->emu_auproviders.filts[i].nprids) && (rdr->nprov < CS_MAXPROV); j++)
|
||
|
|
{
|
||
|
|
i2b_buf(4, rdr->emu_auproviders.filts[i].prids[j], rdr->prid[i]);
|
||
|
|
rdr->nprov++;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static void emu_add_entitlement(struct s_reader *rdr, uint16_t caid, uint32_t provid, uint8_t *key, char *keyName, uint32_t keyLength, uint8_t isData)
|
||
|
|
{
|
||
|
|
if (!rdr->ll_entitlements)
|
||
|
|
{
|
||
|
|
rdr->ll_entitlements = ll_create("ll_entitlements");
|
||
|
|
}
|
||
|
|
|
||
|
|
S_ENTITLEMENT *item;
|
||
|
|
if (cs_malloc(&item, sizeof(S_ENTITLEMENT)))
|
||
|
|
{
|
||
|
|
// fill item
|
||
|
|
item->caid = caid;
|
||
|
|
item->provid = provid;
|
||
|
|
item->id = 0;
|
||
|
|
item->class = 0;
|
||
|
|
item->start = 0;
|
||
|
|
item->end = 2147472000;
|
||
|
|
item->type = 0;
|
||
|
|
item->isKey = 1;
|
||
|
|
memcpy(item->name, keyName, 8);
|
||
|
|
item->key = key;
|
||
|
|
item->keyLength = keyLength;
|
||
|
|
item->isData = isData;
|
||
|
|
|
||
|
|
// add item
|
||
|
|
ll_append(rdr->ll_entitlements, item);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static void refresh_entitlements(struct s_reader *rdr)
|
||
|
|
{
|
||
|
|
uint32_t i;
|
||
|
|
uint16_t caid;
|
||
|
|
KeyData *tmpKeyData;
|
||
|
|
LL_ITER itr;
|
||
|
|
biss2_rsa_key_t *item;
|
||
|
|
|
||
|
|
cs_clear_entitlement(rdr);
|
||
|
|
|
||
|
|
for (i = 0; i < StreamKeys.keyCount; i++)
|
||
|
|
{
|
||
|
|
emu_add_entitlement(rdr, b2i(2, StreamKeys.EmuKeys[i].key), StreamKeys.EmuKeys[i].provider, StreamKeys.EmuKeys[i].key,
|
||
|
|
StreamKeys.EmuKeys[i].keyName, StreamKeys.EmuKeys[i].keyLength, 1);
|
||
|
|
}
|
||
|
|
|
||
|
|
for (i = 0; i < ViKeys.keyCount; i++)
|
||
|
|
{
|
||
|
|
emu_add_entitlement(rdr, 0x0500, ViKeys.EmuKeys[i].provider, ViKeys.EmuKeys[i].key,
|
||
|
|
ViKeys.EmuKeys[i].keyName, ViKeys.EmuKeys[i].keyLength, 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
for (i = 0; i < IrdetoKeys.keyCount; i++)
|
||
|
|
{
|
||
|
|
tmpKeyData = &IrdetoKeys.EmuKeys[i];
|
||
|
|
do
|
||
|
|
{
|
||
|
|
emu_add_entitlement(rdr, tmpKeyData->provider >> 8, tmpKeyData->provider & 0xFF,
|
||
|
|
tmpKeyData->key, tmpKeyData->keyName, tmpKeyData->keyLength, 0);
|
||
|
|
|
||
|
|
tmpKeyData = tmpKeyData->nextKey;
|
||
|
|
}
|
||
|
|
while (tmpKeyData != NULL);
|
||
|
|
}
|
||
|
|
|
||
|
|
for (i = 0; i < CwKeys.keyCount; i++)
|
||
|
|
{
|
||
|
|
emu_add_entitlement(rdr, CwKeys.EmuKeys[i].provider >> 8, CwKeys.EmuKeys[i].provider & 0xFF,
|
||
|
|
CwKeys.EmuKeys[i].key, CwKeys.EmuKeys[i].keyName, CwKeys.EmuKeys[i].keyLength, 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
for (i = 0; i < PowervuKeys.keyCount; i++)
|
||
|
|
{
|
||
|
|
emu_add_entitlement(rdr, 0x0E00, PowervuKeys.EmuKeys[i].provider, PowervuKeys.EmuKeys[i].key,
|
||
|
|
PowervuKeys.EmuKeys[i].keyName, PowervuKeys.EmuKeys[i].keyLength, 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
for (i = 0; i < TandbergKeys.keyCount; i++)
|
||
|
|
{
|
||
|
|
emu_add_entitlement(rdr, 0x1010, TandbergKeys.EmuKeys[i].provider, TandbergKeys.EmuKeys[i].key,
|
||
|
|
TandbergKeys.EmuKeys[i].keyName, TandbergKeys.EmuKeys[i].keyLength, 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
for (i = 0; i < NagraKeys.keyCount; i++)
|
||
|
|
{
|
||
|
|
emu_add_entitlement(rdr, 0x1801, NagraKeys.EmuKeys[i].provider, NagraKeys.EmuKeys[i].key,
|
||
|
|
NagraKeys.EmuKeys[i].keyName, NagraKeys.EmuKeys[i].keyLength, 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Session words for BISS1 mode 1/E (caid 2600) and BISS2 mode 1/E (caid 2602)
|
||
|
|
for (i = 0; i < BissSWs.keyCount; i++)
|
||
|
|
{
|
||
|
|
caid = (BissSWs.EmuKeys[i].keyLength == 8) ? 0x2600 : 0x2602;
|
||
|
|
emu_add_entitlement(rdr, caid, BissSWs.EmuKeys[i].provider, BissSWs.EmuKeys[i].key,
|
||
|
|
BissSWs.EmuKeys[i].keyName, BissSWs.EmuKeys[i].keyLength, 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Session keys (ECM keys) for BISS2 mode CA
|
||
|
|
for (i = 0; i < Biss2Keys.keyCount; i++)
|
||
|
|
{
|
||
|
|
emu_add_entitlement(rdr, 0x2610, Biss2Keys.EmuKeys[i].provider, Biss2Keys.EmuKeys[i].key,
|
||
|
|
Biss2Keys.EmuKeys[i].keyName, Biss2Keys.EmuKeys[i].keyLength, 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
// RSA keys (EMM keys) for BISS2 mode CA
|
||
|
|
itr = ll_iter_create(rdr->ll_biss2_rsa_keys);
|
||
|
|
while ((item = ll_iter_next(&itr)))
|
||
|
|
{
|
||
|
|
emu_add_entitlement(rdr, 0x2610, 0, item->ekid, "RSAPRI", 8, 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
for (i = 0; i < OmnicryptKeys.keyCount; i++)
|
||
|
|
{
|
||
|
|
emu_add_entitlement(rdr, 0x00FF, OmnicryptKeys.EmuKeys[i].provider, OmnicryptKeys.EmuKeys[i].key,
|
||
|
|
OmnicryptKeys.EmuKeys[i].keyName, OmnicryptKeys.EmuKeys[i].keyLength, 0);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static int32_t emu_do_ecm(struct s_reader *rdr, const ECM_REQUEST *er, struct s_ecm_answer *ea)
|
||
|
|
{
|
||
|
|
if (!emu_process_ecm(rdr, er, ea->cw, &ea->cw_ex))
|
||
|
|
{
|
||
|
|
return CS_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
return CS_ERROR;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int32_t emu_do_emm(struct s_reader *rdr, EMM_PACKET *emm)
|
||
|
|
{
|
||
|
|
uint32_t keysAdded = 0;
|
||
|
|
|
||
|
|
if (emm->emmlen < 3)
|
||
|
|
{
|
||
|
|
return CS_ERROR;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (SCT_LEN(emm->emm) > emm->emmlen)
|
||
|
|
{
|
||
|
|
return CS_ERROR;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!emu_process_emm(rdr, b2i(2, emm->caid), emm->emm, &keysAdded))
|
||
|
|
{
|
||
|
|
if (keysAdded > 0)
|
||
|
|
{
|
||
|
|
refresh_entitlements(rdr);
|
||
|
|
}
|
||
|
|
|
||
|
|
return CS_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
return CS_ERROR;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int32_t emu_card_info(struct s_reader *rdr)
|
||
|
|
{
|
||
|
|
SAFE_MUTEX_LOCK(&emu_key_data_mutex);
|
||
|
|
|
||
|
|
// Delete keys from Emu's memory
|
||
|
|
emu_clear_keydata();
|
||
|
|
|
||
|
|
// Delete BISS2 mode CA RSA keys
|
||
|
|
ll_destroy_data(&rdr->ll_biss2_rsa_keys);
|
||
|
|
|
||
|
|
// Read keys built in the OSCam-Emu binary
|
||
|
|
emu_read_keymemory(rdr);
|
||
|
|
|
||
|
|
// Read keys from SoftCam.Key file
|
||
|
|
emu_set_keyfile_path(cs_confdir);
|
||
|
|
|
||
|
|
if (!emu_read_keyfile(rdr, cs_confdir))
|
||
|
|
{
|
||
|
|
if (emu_read_keyfile(rdr, "/var/keys/"))
|
||
|
|
{
|
||
|
|
emu_set_keyfile_path("/var/keys/");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Read BISS2 mode CA RSA keys from PEM files
|
||
|
|
biss_read_pem(rdr, BISS2_MAX_RSA_KEYS);
|
||
|
|
|
||
|
|
cs_log("Total keys in memory: W:%d V:%d N:%d I:%d F:%d G:%d O:%d P:%d T:%d A:%d",
|
||
|
|
CwKeys.keyCount, ViKeys.keyCount, NagraKeys.keyCount, IrdetoKeys.keyCount, BissSWs.keyCount,
|
||
|
|
Biss2Keys.keyCount, OmnicryptKeys.keyCount, PowervuKeys.keyCount, TandbergKeys.keyCount,
|
||
|
|
StreamKeys.keyCount);
|
||
|
|
|
||
|
|
// Inform OSCam about all available keys.
|
||
|
|
// This is used for listing the "entitlements" in the webif's reader page.
|
||
|
|
refresh_entitlements(rdr);
|
||
|
|
|
||
|
|
SAFE_MUTEX_UNLOCK(&emu_key_data_mutex);
|
||
|
|
|
||
|
|
set_prids(rdr);
|
||
|
|
|
||
|
|
set_hexserial_to_version(rdr);
|
||
|
|
|
||
|
|
return CS_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
static int32_t emu_card_init(struct s_reader *UNUSED(rdr), struct s_ATR *UNUSED(atr))
|
||
|
|
{
|
||
|
|
return CS_ERROR;
|
||
|
|
}
|
||
|
|
*/
|
||
|
|
|
||
|
|
int32_t emu_get_via3_emm_type(EMM_PACKET *ep, struct s_reader *rdr)
|
||
|
|
{
|
||
|
|
uint32_t provid = 0;
|
||
|
|
|
||
|
|
if(ep->emm[3] == 0x90 && ep->emm[4] == 0x03)
|
||
|
|
{
|
||
|
|
provid = b2i(3, ep->emm + 5);
|
||
|
|
provid &= 0xFFFFF0;
|
||
|
|
i2b_buf(4, provid, ep->provid);
|
||
|
|
}
|
||
|
|
|
||
|
|
switch (ep->emm[0])
|
||
|
|
{
|
||
|
|
case 0x88:
|
||
|
|
ep->type = UNIQUE;
|
||
|
|
memset(ep->hexserial, 0, 8);
|
||
|
|
memcpy(ep->hexserial, ep->emm + 4, 4);
|
||
|
|
rdr_log_dbg(rdr, D_EMM, "UNIQUE");
|
||
|
|
return 1;
|
||
|
|
|
||
|
|
case 0x8A:
|
||
|
|
case 0x8B:
|
||
|
|
ep->type = GLOBAL;
|
||
|
|
rdr_log_dbg(rdr, D_EMM, "GLOBAL");
|
||
|
|
return 1;
|
||
|
|
|
||
|
|
case 0x8C:
|
||
|
|
case 0x8D:
|
||
|
|
ep->type = SHARED;
|
||
|
|
rdr_log_dbg(rdr, D_EMM, "SHARED (part)");
|
||
|
|
// We need those packets to pass otherwise we would never
|
||
|
|
// be able to complete EMM reassembly
|
||
|
|
return 1;
|
||
|
|
|
||
|
|
case 0x8E:
|
||
|
|
ep->type = SHARED;
|
||
|
|
rdr_log_dbg(rdr, D_EMM, "SHARED");
|
||
|
|
memset(ep->hexserial, 0, 8);
|
||
|
|
memcpy(ep->hexserial, ep->emm + 3, 3);
|
||
|
|
return 1;
|
||
|
|
|
||
|
|
default:
|
||
|
|
ep->type = UNKNOWN;
|
||
|
|
rdr_log_dbg(rdr, D_EMM, "UNKNOWN");
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
int32_t emu_get_ird2_emm_type(EMM_PACKET *ep, struct s_reader *rdr)
|
||
|
|
{
|
||
|
|
int32_t l = (ep->emm[3] & 0x07);
|
||
|
|
int32_t base = (ep->emm[3] >> 3);
|
||
|
|
char dumprdrserial[l * 3], dumpemmserial[l * 3];
|
||
|
|
|
||
|
|
switch (l)
|
||
|
|
{
|
||
|
|
case 0:
|
||
|
|
// global emm, 0 bytes addressed
|
||
|
|
ep->type = GLOBAL;
|
||
|
|
rdr_log_dbg(rdr, D_EMM, "GLOBAL base = %02x", base);
|
||
|
|
return 1;
|
||
|
|
|
||
|
|
case 2:
|
||
|
|
// shared emm, 2 bytes addressed
|
||
|
|
ep->type = SHARED;
|
||
|
|
memset(ep->hexserial, 0, 8);
|
||
|
|
memcpy(ep->hexserial, ep->emm + 4, l);
|
||
|
|
cs_hexdump(1, rdr->hexserial, l, dumprdrserial, sizeof(dumprdrserial));
|
||
|
|
cs_hexdump(1, ep->hexserial, l, dumpemmserial, sizeof(dumpemmserial));
|
||
|
|
rdr_log_dbg_sensitive(rdr, D_EMM, "SHARED l = %d ep = {%s} rdr = {%s} base = %02x",
|
||
|
|
l, dumpemmserial, dumprdrserial, base);
|
||
|
|
return 1;
|
||
|
|
|
||
|
|
case 3:
|
||
|
|
// unique emm, 3 bytes addressed
|
||
|
|
ep->type = UNIQUE;
|
||
|
|
memset(ep->hexserial, 0, 8);
|
||
|
|
memcpy(ep->hexserial, ep->emm + 4, l);
|
||
|
|
cs_hexdump(1, rdr->hexserial, l, dumprdrserial, sizeof(dumprdrserial));
|
||
|
|
cs_hexdump(1, ep->hexserial, l, dumpemmserial, sizeof(dumpemmserial));
|
||
|
|
rdr_log_dbg_sensitive(rdr, D_EMM, "UNIQUE l = %d ep = {%s} rdr = {%s} base = %02x",
|
||
|
|
l, dumpemmserial, dumprdrserial, base);
|
||
|
|
return 1;
|
||
|
|
|
||
|
|
default:
|
||
|
|
ep->type = UNKNOWN;
|
||
|
|
rdr_log_dbg(rdr, D_EMM, "UNKNOWN");
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
int32_t emu_get_pvu_emm_type(EMM_PACKET *ep, struct s_reader *rdr)
|
||
|
|
{
|
||
|
|
if (ep->emm[0] == 0x82)
|
||
|
|
{
|
||
|
|
ep->type = UNIQUE;
|
||
|
|
memset(ep->hexserial, 0, 8);
|
||
|
|
memcpy(ep->hexserial, ep->emm + 12, 4);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
ep->type = UNKNOWN;
|
||
|
|
rdr_log_dbg(rdr, D_EMM, "UNKNOWN");
|
||
|
|
}
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
int32_t emu_get_tan_emm_type(EMM_PACKET *ep, struct s_reader *rdr)
|
||
|
|
{
|
||
|
|
if (ep->emm[0] == 0x82 || ep->emm[0] == 0x83)
|
||
|
|
{
|
||
|
|
ep->type = GLOBAL;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
ep->type = UNKNOWN;
|
||
|
|
rdr_log_dbg(rdr, D_EMM, "UNKNOWN");
|
||
|
|
}
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
int32_t emu_get_biss_emm_type(EMM_PACKET *ep, struct s_reader *rdr)
|
||
|
|
{
|
||
|
|
switch (ep->emm[0])
|
||
|
|
{
|
||
|
|
case 0x81: // Spec say this is for EMM, but oscam (and all other crypto systems) use it for ECM
|
||
|
|
case 0x82:
|
||
|
|
case 0x83:
|
||
|
|
case 0x84:
|
||
|
|
case 0x85:
|
||
|
|
case 0x86:
|
||
|
|
case 0x87:
|
||
|
|
case 0x88:
|
||
|
|
case 0x89:
|
||
|
|
case 0x8A:
|
||
|
|
case 0x8B:
|
||
|
|
case 0x8C:
|
||
|
|
case 0x8D:
|
||
|
|
case 0x8E:
|
||
|
|
case 0x8F:
|
||
|
|
ep->type = GLOBAL;
|
||
|
|
return 1;
|
||
|
|
|
||
|
|
default:
|
||
|
|
ep->type = UNKNOWN;
|
||
|
|
rdr_log_dbg(rdr, D_EMM, "UNKNOWN");
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static int32_t emu_get_emm_type(struct emm_packet_t *ep, struct s_reader *rdr)
|
||
|
|
{
|
||
|
|
uint16_t caid = b2i(2, ep->caid);
|
||
|
|
|
||
|
|
if (caid_is_viaccess(caid)) return emu_get_via3_emm_type(ep, rdr);
|
||
|
|
if (caid_is_irdeto(caid)) return emu_get_ird2_emm_type(ep, rdr);
|
||
|
|
if (caid_is_powervu(caid)) return emu_get_pvu_emm_type(ep, rdr);
|
||
|
|
if (caid_is_director(caid)) return emu_get_tan_emm_type(ep, rdr);
|
||
|
|
if (caid_is_biss_dynamic(caid)) return emu_get_biss_emm_type(ep, rdr);
|
||
|
|
|
||
|
|
return CS_ERROR;
|
||
|
|
}
|
||
|
|
|
||
|
|
FILTER *get_emu_prids_for_caid(struct s_reader *rdr, uint16_t caid)
|
||
|
|
{
|
||
|
|
int32_t i;
|
||
|
|
|
||
|
|
for (i = 0; i < rdr->emu_auproviders.nfilts; i++)
|
||
|
|
{
|
||
|
|
if (caid == rdr->emu_auproviders.filts[i].caid)
|
||
|
|
{
|
||
|
|
return &rdr->emu_auproviders.filts[i];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int32_t emu_get_via3_emm_filter(struct s_reader *UNUSED(rdr), struct s_csystem_emm_filter **emm_filters, unsigned int *filter_count, uint16_t UNUSED(caid), uint32_t UNUSED(provid))
|
||
|
|
{
|
||
|
|
if (*emm_filters == NULL)
|
||
|
|
{
|
||
|
|
const unsigned int max_filter_count = 1;
|
||
|
|
if (!cs_malloc(emm_filters, max_filter_count * sizeof(struct s_csystem_emm_filter)))
|
||
|
|
{
|
||
|
|
return CS_ERROR;
|
||
|
|
}
|
||
|
|
|
||
|
|
struct s_csystem_emm_filter *filters = *emm_filters;
|
||
|
|
*filter_count = 0;
|
||
|
|
|
||
|
|
int32_t idx = 0;
|
||
|
|
|
||
|
|
filters[idx].type = EMM_GLOBAL;
|
||
|
|
filters[idx].enabled = 1;
|
||
|
|
filters[idx].filter[0] = 0x8A;
|
||
|
|
filters[idx].mask[0] = 0xFE;
|
||
|
|
filters[idx].filter[3] = 0x80;
|
||
|
|
filters[idx].mask[3] = 0x80;
|
||
|
|
idx++;
|
||
|
|
|
||
|
|
*filter_count = idx;
|
||
|
|
}
|
||
|
|
|
||
|
|
return CS_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int32_t emu_get_ird2_emm_filter(struct s_reader *rdr, struct s_csystem_emm_filter **emm_filters, unsigned int *filter_count, uint16_t caid, uint32_t UNUSED(provid))
|
||
|
|
{
|
||
|
|
uint8_t hexserial[3], prid[4];
|
||
|
|
FILTER *emu_provids;
|
||
|
|
int8_t have_provid = 0, have_serial = 0;
|
||
|
|
int32_t i;
|
||
|
|
|
||
|
|
SAFE_MUTEX_LOCK(&emu_key_data_mutex);
|
||
|
|
if(irdeto2_get_hexserial(caid, hexserial))
|
||
|
|
{
|
||
|
|
have_serial = 1;
|
||
|
|
}
|
||
|
|
SAFE_MUTEX_UNLOCK(&emu_key_data_mutex);
|
||
|
|
|
||
|
|
emu_provids = get_emu_prids_for_caid(rdr, caid);
|
||
|
|
if (emu_provids != NULL && emu_provids->nprids > 0)
|
||
|
|
{
|
||
|
|
have_provid = 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (*emm_filters == NULL)
|
||
|
|
{
|
||
|
|
const unsigned int max_filter_count = have_serial + (2 * (have_provid ? emu_provids->nprids : 0));
|
||
|
|
if (!cs_malloc(emm_filters, max_filter_count * sizeof(struct s_csystem_emm_filter)))
|
||
|
|
{
|
||
|
|
return CS_ERROR;
|
||
|
|
}
|
||
|
|
|
||
|
|
struct s_csystem_emm_filter *filters = *emm_filters;
|
||
|
|
*filter_count = 0;
|
||
|
|
|
||
|
|
unsigned int idx = 0;
|
||
|
|
|
||
|
|
if (have_serial)
|
||
|
|
{
|
||
|
|
filters[idx].type = EMM_UNIQUE;
|
||
|
|
filters[idx].enabled = 1;
|
||
|
|
filters[idx].filter[0] = 0x82;
|
||
|
|
filters[idx].mask[0] = 0xFF;
|
||
|
|
filters[idx].filter[1] = 0xFB;
|
||
|
|
filters[idx].mask[1] = 0x07;
|
||
|
|
memcpy(&filters[idx].filter[2], hexserial, 3);
|
||
|
|
memset(&filters[idx].mask[2], 0xFF, 3);
|
||
|
|
idx++;
|
||
|
|
}
|
||
|
|
|
||
|
|
for (i = 0; have_provid && i < emu_provids->nprids; i++)
|
||
|
|
{
|
||
|
|
i2b_buf(4, emu_provids->prids[i], prid);
|
||
|
|
|
||
|
|
filters[idx].type = EMM_UNIQUE;
|
||
|
|
filters[idx].enabled = 1;
|
||
|
|
filters[idx].filter[0] = 0x82;
|
||
|
|
filters[idx].mask[0] = 0xFF;
|
||
|
|
filters[idx].filter[1] = 0xFB;
|
||
|
|
filters[idx].mask[1] = 0x07;
|
||
|
|
memcpy(&filters[idx].filter[2], &prid[1], 3);
|
||
|
|
memset(&filters[idx].mask[2], 0xFF, 3);
|
||
|
|
idx++;
|
||
|
|
|
||
|
|
filters[idx].type = EMM_SHARED;
|
||
|
|
filters[idx].enabled = 1;
|
||
|
|
filters[idx].filter[0] = 0x82;
|
||
|
|
filters[idx].mask[0] = 0xFF;
|
||
|
|
filters[idx].filter[1] = 0xFA;
|
||
|
|
filters[idx].mask[1] = 0x07;
|
||
|
|
memcpy(&filters[idx].filter[2], &prid[1], 2);
|
||
|
|
memset(&filters[idx].mask[2], 0xFF, 2);
|
||
|
|
idx++;
|
||
|
|
}
|
||
|
|
|
||
|
|
*filter_count = idx;
|
||
|
|
}
|
||
|
|
|
||
|
|
return CS_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int32_t emu_get_pvu_emm_filter(struct s_csystem_emm_filter **emm_filters, unsigned int *filter_count,
|
||
|
|
uint16_t caid, uint16_t srvid, uint16_t tsid, uint16_t onid, uint32_t ens)
|
||
|
|
{
|
||
|
|
uint8_t hexserials[32][4];
|
||
|
|
uint32_t i, count = 0;
|
||
|
|
|
||
|
|
SAFE_MUTEX_LOCK(&emu_key_data_mutex);
|
||
|
|
count = powervu_get_hexserials_new(hexserials, 32, caid, tsid, onid, ens);
|
||
|
|
if (count == 0)
|
||
|
|
{
|
||
|
|
count = powervu_get_hexserials(hexserials, 32, srvid);
|
||
|
|
if (count == 0)
|
||
|
|
{
|
||
|
|
SAFE_MUTEX_UNLOCK(&emu_key_data_mutex);
|
||
|
|
return CS_ERROR;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
SAFE_MUTEX_UNLOCK(&emu_key_data_mutex);
|
||
|
|
|
||
|
|
if (*emm_filters == NULL)
|
||
|
|
{
|
||
|
|
const unsigned int max_filter_count = count;
|
||
|
|
if (!cs_malloc(emm_filters, max_filter_count * sizeof(struct s_csystem_emm_filter)))
|
||
|
|
{
|
||
|
|
return CS_ERROR;
|
||
|
|
}
|
||
|
|
|
||
|
|
struct s_csystem_emm_filter *filters = *emm_filters;
|
||
|
|
*filter_count = 0;
|
||
|
|
|
||
|
|
int32_t idx = 0;
|
||
|
|
|
||
|
|
for (i = 0; i < count; i++)
|
||
|
|
{
|
||
|
|
filters[idx].type = EMM_UNIQUE;
|
||
|
|
filters[idx].enabled = 1;
|
||
|
|
filters[idx].filter[0] = 0x82;
|
||
|
|
filters[idx].filter[10] = hexserials[i][0];
|
||
|
|
filters[idx].filter[11] = hexserials[i][1];
|
||
|
|
filters[idx].filter[12] = hexserials[i][2];
|
||
|
|
filters[idx].filter[13] = hexserials[i][3];
|
||
|
|
filters[idx].mask[0] = 0xFF;
|
||
|
|
filters[idx].mask[10] = 0xFF;
|
||
|
|
filters[idx].mask[11] = 0xFF;
|
||
|
|
filters[idx].mask[12] = 0xFF;
|
||
|
|
filters[idx].mask[13] = 0xFF;
|
||
|
|
idx++;
|
||
|
|
}
|
||
|
|
|
||
|
|
*filter_count = idx;
|
||
|
|
}
|
||
|
|
|
||
|
|
return CS_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int32_t emu_get_tan_emm_filter(struct s_reader *UNUSED(rdr), struct s_csystem_emm_filter **emm_filters, unsigned int *filter_count, uint16_t UNUSED(caid), uint32_t UNUSED(provid))
|
||
|
|
{
|
||
|
|
if (*emm_filters == NULL)
|
||
|
|
{
|
||
|
|
const unsigned int max_filter_count = 2;
|
||
|
|
uint8_t buf[8];
|
||
|
|
|
||
|
|
if (!emu_find_key('T', 0x40, 0, "MK", buf, 8, 0, 0, 0, NULL) &&
|
||
|
|
!emu_find_key('T', 0x40, 0, "MK01", buf, 8, 0, 0, 0, NULL))
|
||
|
|
{
|
||
|
|
return CS_ERROR;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!cs_malloc(emm_filters, max_filter_count * sizeof(struct s_csystem_emm_filter)))
|
||
|
|
{
|
||
|
|
return CS_ERROR;
|
||
|
|
}
|
||
|
|
|
||
|
|
struct s_csystem_emm_filter *filters = *emm_filters;
|
||
|
|
*filter_count = 0;
|
||
|
|
|
||
|
|
int32_t idx = 0;
|
||
|
|
|
||
|
|
filters[idx].type = EMM_GLOBAL;
|
||
|
|
filters[idx].enabled = 1;
|
||
|
|
filters[idx].filter[0] = 0x82;
|
||
|
|
filters[idx].mask[0] = 0xFF;
|
||
|
|
idx++;
|
||
|
|
|
||
|
|
filters[idx].type = EMM_GLOBAL;
|
||
|
|
filters[idx].enabled = 1;
|
||
|
|
filters[idx].filter[0] = 0x83;
|
||
|
|
filters[idx].mask[0] = 0xFF;
|
||
|
|
idx++;
|
||
|
|
|
||
|
|
*filter_count = idx;
|
||
|
|
}
|
||
|
|
|
||
|
|
return CS_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int32_t emu_get_biss_emm_filter(struct s_reader *UNUSED(rdr), struct s_csystem_emm_filter **emm_filters, unsigned int *filter_count, uint16_t UNUSED(caid), uint32_t UNUSED(provid))
|
||
|
|
{
|
||
|
|
if (*emm_filters == NULL)
|
||
|
|
{
|
||
|
|
const unsigned int max_filter_count = 15;
|
||
|
|
if (!cs_malloc(emm_filters, max_filter_count * sizeof(struct s_csystem_emm_filter)))
|
||
|
|
{
|
||
|
|
return CS_ERROR;
|
||
|
|
}
|
||
|
|
|
||
|
|
struct s_csystem_emm_filter *filters = *emm_filters;
|
||
|
|
*filter_count = 0;
|
||
|
|
|
||
|
|
int32_t idx = 0;
|
||
|
|
uint8_t i;
|
||
|
|
|
||
|
|
for (i = 0; i < max_filter_count; i++)
|
||
|
|
{
|
||
|
|
filters[idx].type = EMM_GLOBAL;
|
||
|
|
filters[idx].enabled = 1;
|
||
|
|
filters[idx].filter[0] = 0x81 + i; // What about table 0x81?
|
||
|
|
filters[idx].mask[0] = 0xFF;
|
||
|
|
idx++;
|
||
|
|
|
||
|
|
*filter_count = idx;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return CS_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int32_t emu_get_emm_filter(struct s_reader *UNUSED(rdr), struct s_csystem_emm_filter **UNUSED(emm_filters), unsigned int *UNUSED(filter_count))
|
||
|
|
{
|
||
|
|
return CS_ERROR;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int32_t emu_get_emm_filter_adv(struct s_reader *rdr, struct s_csystem_emm_filter **emm_filters, unsigned int *filter_count,
|
||
|
|
uint16_t caid, uint32_t provid, uint16_t srvid, uint16_t tsid, uint16_t onid, uint32_t ens)
|
||
|
|
{
|
||
|
|
if (caid_is_viaccess(caid)) return emu_get_via3_emm_filter(rdr, emm_filters, filter_count, caid, provid);
|
||
|
|
if (caid_is_irdeto(caid)) return emu_get_ird2_emm_filter(rdr, emm_filters, filter_count, caid, provid);
|
||
|
|
if (caid_is_powervu(caid)) return emu_get_pvu_emm_filter(emm_filters, filter_count, caid, srvid, tsid, onid, ens);
|
||
|
|
if (caid_is_director(caid)) return emu_get_tan_emm_filter(rdr, emm_filters, filter_count, caid, provid);
|
||
|
|
if (caid_is_biss_dynamic(caid)) return emu_get_biss_emm_filter(rdr, emm_filters, filter_count, caid, provid);
|
||
|
|
|
||
|
|
return CS_ERROR;
|
||
|
|
}
|
||
|
|
|
||
|
|
const struct s_cardsystem reader_emu =
|
||
|
|
{
|
||
|
|
.desc = "emu",
|
||
|
|
.caids = (uint16_t[]){ 0x05, 0x06, 0x0D, 0x0E, 0x10, 0x18, 0x26, 0 },
|
||
|
|
.do_ecm = emu_do_ecm,
|
||
|
|
.do_emm = emu_do_emm,
|
||
|
|
.card_info = emu_card_info,
|
||
|
|
//.card_init = emu_card_init, // apparently this is not needed at all
|
||
|
|
.get_emm_type = emu_get_emm_type,
|
||
|
|
.get_emm_filter = emu_get_emm_filter, // needed to pass checks
|
||
|
|
.get_emm_filter_adv = emu_get_emm_filter_adv,
|
||
|
|
};
|
||
|
|
|
||
|
|
/*
|
||
|
|
* Create the Emu virtual "device" part. This is of type s_cardreader.
|
||
|
|
* Similar structures are found in the csctapi (Card System Card Terminal API)
|
||
|
|
* folder for every IFD (InterFace Device), aka smart card reader.
|
||
|
|
* Since we have no hardware to initialize, we start our Stream Relay server
|
||
|
|
* with the emu_reader_init() function.
|
||
|
|
* At Emu shutdown, we remove keys from memory with the emu_close() function.
|
||
|
|
*/
|
||
|
|
|
||
|
|
#define CR_OK 0
|
||
|
|
#define CR_ERROR 1
|
||
|
|
|
||
|
|
static int32_t emu_reader_init(struct s_reader *UNUSED(reader))
|
||
|
|
{
|
||
|
|
#ifdef MODULE_STREAMRELAY
|
||
|
|
if (cfg.stream_relay_enabled && (stream_server_thread_init == 0))
|
||
|
|
{
|
||
|
|
int32_t i;
|
||
|
|
stream_server_thread_init = 1;
|
||
|
|
SAFE_MUTEX_INIT(&emu_fixed_key_srvid_mutex, NULL);
|
||
|
|
|
||
|
|
for (i = 0; i < EMU_STREAM_SERVER_MAX_CONNECTIONS; i++)
|
||
|
|
{
|
||
|
|
SAFE_MUTEX_INIT(&emu_fixed_key_data_mutex[i], NULL);
|
||
|
|
ll_emu_stream_delayed_keys[i] = ll_create("ll_emu_stream_delayed_keys");
|
||
|
|
memset(&emu_fixed_key_data[i], 0, sizeof(emu_stream_client_key_data));
|
||
|
|
}
|
||
|
|
|
||
|
|
start_thread("stream_key_delayer", stream_key_delayer, NULL, NULL, 1, 1);
|
||
|
|
cs_log("Stream key delayer initialized");
|
||
|
|
}
|
||
|
|
|
||
|
|
// Initialize mutex for exclusive access to key database and key file
|
||
|
|
if (!emu_key_data_mutex_init)
|
||
|
|
{
|
||
|
|
SAFE_MUTEX_INIT(&emu_key_data_mutex, NULL);
|
||
|
|
emu_key_data_mutex_init = 1;
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
return CR_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int32_t emu_close(struct s_reader *UNUSED(reader))
|
||
|
|
{
|
||
|
|
cs_log("Reader is shutting down");
|
||
|
|
|
||
|
|
// Delete keys from Emu's memory
|
||
|
|
SAFE_MUTEX_LOCK(&emu_key_data_mutex);
|
||
|
|
emu_clear_keydata();
|
||
|
|
SAFE_MUTEX_UNLOCK(&emu_key_data_mutex);
|
||
|
|
|
||
|
|
return CR_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int32_t emu_get_status(struct s_reader *UNUSED(reader), int32_t *in) { *in = 1; return CR_OK; }
|
||
|
|
static int32_t emu_activate(struct s_reader *UNUSED(reader), struct s_ATR *UNUSED(atr)) { return CR_OK; }
|
||
|
|
static int32_t emu_transmit(struct s_reader *UNUSED(reader), uint8_t *UNUSED(buffer), uint32_t UNUSED(size), uint32_t UNUSED(expectedlen), uint32_t UNUSED(delay), uint32_t UNUSED(timeout)) { return CR_OK; }
|
||
|
|
static int32_t emu_receive(struct s_reader *UNUSED(reader), uint8_t *UNUSED(buffer), uint32_t UNUSED(size), uint32_t UNUSED(delay), uint32_t UNUSED(timeout)) { return CR_OK; }
|
||
|
|
static int32_t emu_write_settings(struct s_reader *UNUSED(reader), struct s_cardreader_settings *UNUSED(s)) { return CR_OK; }
|
||
|
|
static int32_t emu_card_write(struct s_reader *UNUSED(pcsc_reader), const uint8_t *UNUSED(buf), uint8_t *UNUSED(cta_res), uint16_t *UNUSED(cta_lr), int32_t UNUSED(l)) { return CR_OK; }
|
||
|
|
static int32_t emu_set_protocol(struct s_reader *UNUSED(rdr), uint8_t *UNUSED(params), uint32_t *UNUSED(length), uint32_t UNUSED(len_request)) { return CR_OK; }
|
||
|
|
|
||
|
|
const struct s_cardreader cardreader_emu =
|
||
|
|
{
|
||
|
|
.desc = "emu",
|
||
|
|
.typ = R_EMU,
|
||
|
|
.skip_extra_atr_parsing = 1,
|
||
|
|
.reader_init = emu_reader_init,
|
||
|
|
.get_status = emu_get_status,
|
||
|
|
.activate = emu_activate,
|
||
|
|
.transmit = emu_transmit,
|
||
|
|
.receive = emu_receive,
|
||
|
|
.close = emu_close,
|
||
|
|
.write_settings = emu_write_settings,
|
||
|
|
.card_write = emu_card_write,
|
||
|
|
.set_protocol = emu_set_protocol,
|
||
|
|
};
|
||
|
|
|
||
|
|
void add_emu_reader(void)
|
||
|
|
{
|
||
|
|
// This function is called inside oscam.c and creates an emu [reader] with default
|
||
|
|
// settings in oscam.server file. If an emu [reader] already exists, it uses that.
|
||
|
|
|
||
|
|
LL_ITER itr;
|
||
|
|
struct s_reader *rdr;
|
||
|
|
int8_t haveEmuReader = 0;
|
||
|
|
char emuName[] = "emulator";
|
||
|
|
char *ctab, *ftab, *emu_auproviders, *disablecrccws_only_for;
|
||
|
|
|
||
|
|
// Check if emu [reader] entry already exists in oscam.server file and get it
|
||
|
|
itr = ll_iter_create(configured_readers);
|
||
|
|
while ((rdr = ll_iter_next(&itr)))
|
||
|
|
{
|
||
|
|
if (rdr->typ == R_EMU)
|
||
|
|
{
|
||
|
|
haveEmuReader = 1;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
rdr = NULL;
|
||
|
|
|
||
|
|
// If there's no emu [reader] in oscam.server, create one with default settings
|
||
|
|
if (!haveEmuReader)
|
||
|
|
{
|
||
|
|
if (!cs_malloc(&rdr, sizeof(struct s_reader)))
|
||
|
|
{
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
reader_set_defaults(rdr);
|
||
|
|
|
||
|
|
rdr->enable = 1;
|
||
|
|
rdr->typ = R_EMU;
|
||
|
|
cs_strncpy(rdr->label, emuName, sizeof(emuName));
|
||
|
|
cs_strncpy(rdr->device, emuName, sizeof(emuName));
|
||
|
|
|
||
|
|
// CAIDs
|
||
|
|
ctab = strdup("0500,0604,0D00,0E00,1010,1801,2600,2602,2610");
|
||
|
|
chk_caidtab(ctab, &rdr->ctab);
|
||
|
|
NULLFREE(ctab);
|
||
|
|
|
||
|
|
// Idents
|
||
|
|
ftab = strdup("0500:020A00,021110;"
|
||
|
|
"0604:000000;"
|
||
|
|
"0D00:0000C0;"
|
||
|
|
"0E00:000000;"
|
||
|
|
"1010:000000;"
|
||
|
|
"1801:000000,001101,002111,007301;"
|
||
|
|
"2600:000000;"
|
||
|
|
"2602:000000;"
|
||
|
|
"2610:000000;"
|
||
|
|
);
|
||
|
|
chk_ftab(ftab, &rdr->ftab);
|
||
|
|
NULLFREE(ftab);
|
||
|
|
|
||
|
|
// AU providers
|
||
|
|
emu_auproviders = strdup("0604:010200;0E00:000000;1010:000000;2610:000000;");
|
||
|
|
chk_ftab(emu_auproviders, &rdr->emu_auproviders);
|
||
|
|
NULLFREE(emu_auproviders);
|
||
|
|
|
||
|
|
// EMM cache
|
||
|
|
rdr->cachemm = 2;
|
||
|
|
rdr->rewritemm = 1;
|
||
|
|
rdr->logemm = 2;
|
||
|
|
rdr->deviceemm = 1;
|
||
|
|
|
||
|
|
// User group
|
||
|
|
rdr->grp = 0x1ULL;
|
||
|
|
|
||
|
|
// Add the "device" part to our emu reader
|
||
|
|
rdr->crdr = &cardreader_emu;
|
||
|
|
|
||
|
|
// Disable CW checksum test for PowerVu
|
||
|
|
disablecrccws_only_for = strdup("0E00:000000");
|
||
|
|
chk_ftab(disablecrccws_only_for, &rdr->disablecrccws_only_for);
|
||
|
|
NULLFREE(disablecrccws_only_for);
|
||
|
|
|
||
|
|
reader_fixups_fn(rdr);
|
||
|
|
ll_append(configured_readers, rdr);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Set DVB Api delayer option
|
||
|
|
#ifdef HAVE_DVBAPI
|
||
|
|
if (cfg.dvbapi_enabled && cfg.dvbapi_delayer < 60)
|
||
|
|
{
|
||
|
|
cfg.dvbapi_delayer = 60;
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
|
||
|
|
cs_log("OSCam-Emu version %d", EMU_VERSION);
|
||
|
|
}
|
||
|
|
|
||
|
|
#endif // WITH_EMU
|