%code requires {
#include "file/keydbcfg.h"
}
%code {
/*
* This file is part of libaacs
* Copyright (C) 2010 gates
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* .
*/
#include "util/macro.h"
#include
#include
#include
/* Disable some warnings triggered by generated parser */
#if defined __GNUC__
#pragma GCC diagnostic ignored "-Wimplicit-function-declaration"
#pragma GCC diagnostic ignored "-Wunused-parameter"
#pragma GCC visibility push(hidden)
#endif
/* Fix some warnings trigger by -Wundef which can't be ignored */
#define YYENABLE_NLS 0
#define YYLTYPE_IS_TRIVIAL 0
#define DIGIT_KEY_PAIR_LIST_FREE(X) do \
{ \
while (X) \
{ \
digit_key_pair_list *pnext = X->next;\
X_FREE(X->key_pair.key); \
X_FREE(X); \
X = pnext; \
} \
} while (0);
/* enum used in certain functions to add proper title entry */
enum
{
ENTRY_TYPE_DISCID,
/*ENTRY_TYPE_TITLE,*/
/*ENTRY_TYPE_DATE,*/
ENTRY_TYPE_MEK,
ENTRY_TYPE_VID,
/*ENTRY_TYPE_BN,*/
ENTRY_TYPE_VUK,
/*ENTRY_TYPE_PAK,*/
/*ENTRY_TYPE_TK,*/
ENTRY_TYPE_UK
};
static dk_list *new_dk_list(void);
static pk_list *new_pk_list(void);
static cert_list *new_cert_list(void);
static void add_dk_entry(config_file *cf, char *key, char *node, char *uv, char *u_mask_shift);
static void add_pk_entry(config_file *cf, char *key);
static void add_cert_entry(config_file *cf, char *host_priv_key, char *host_cert);
static title_entry_list *new_title_entry_list(void);
static int add_entry(title_entry_list *list, int type, char *entry);
static digit_key_pair_list *new_digit_key_pair_list(void);
static digit_key_pair_list *add_digit_key_pair_entry(digit_key_pair_list *list,
int type, unsigned int digit, char *key);
/*
static int add_date_entry(title_entry_list *list, unsigned int year,
unsigned int month, unsigned int day);
*/
void yyerror (void *scanner, config_file *cf,
title_entry_list *celist, digit_key_pair_list *dkplist,
const char *msg);
extern int libaacs_yyget_lineno (void *scanner);
/* uncomment the line below for debugging */
// int yydebug = 1;
}
/* Options set to generate a reentrant parser that is POSIX yacc compatible
* The basic 'scanner' parameters are set. Also, another parameter is set
* to pass in a title entry list struct used to hold all title entries.
* Most of these options are bison specific, but some BSD's have bison
* compatibility support for these options in byacc.
*/
%pure-parser
%yacc
%lex-param{void *scanner}
%parse-param{void *scanner}
%parse-param{config_file *cf}
%parse-param{title_entry_list *celist}
%parse-param{digit_key_pair_list *dkplist}
%union
{
char *string;
unsigned int digit;
}
%token HEXSTRING
%token DISC_TITLE
%token DIGIT
%token KEYWORD_DEVICE_KEY
%token KEYWORD_DEVICE_NODE
%token KEYWORD_KEY_UV
%token KEYWORD_KEY_U_MASK_SHIFT
%token KEYWORD_HOST_PRIV_KEY
%token KEYWORD_HOST_CERT
%token KEYWORD_HOST_NONCE
%token KEYWORD_HOST_KEY_POINT
%token PUNCT_EQUALS_SIGN
%token PUNCT_VERTICAL_BAR
%token PUNCT_HYPHEN
%token ENTRY_ID_DK
%token ENTRY_ID_PK
%token ENTRY_ID_HC
%token ENTRY_ID_DATE
%token ENTRY_ID_MEK
%token ENTRY_ID_VID
%token ENTRY_ID_BN
%token ENTRY_ID_VUK
%token ENTRY_ID_PAK
%token ENTRY_ID_TK
%token ENTRY_ID_UK
%token NEWLINE
%token BAD_ENTRY
%type discid disc_title
%type host_priv_key host_cert host_nonce host_key_point hexstring_list
%type device_key device_node key_uv key_u_mask_shift
%%
config_file
: config_entry_list newline_list
| config_entry_list
| newline_list
| /* empty string */
;
config_entry_list
: config_entry_list config_entry
| config_entry
;
config_entry
: dk_entry
| pk_entry
| host_cert_entry
| title_entry
| error NEWLINE
{
fprintf(stderr, "bad entry at or around line %d\n",
libaacs_yyget_lineno(scanner) - 1);
yyerrok;
}
;
dk_entry
: newline_list ENTRY_ID_DK device_key PUNCT_VERTICAL_BAR device_node PUNCT_VERTICAL_BAR key_uv PUNCT_VERTICAL_BAR key_u_mask_shift NEWLINE
{
add_dk_entry(cf, $3, $5, $7, $9);
}
| newline_list ENTRY_ID_DK device_key PUNCT_VERTICAL_BAR device_node NEWLINE
{
add_dk_entry(cf, $3, $5, NULL, NULL);
}
| ENTRY_ID_DK device_key PUNCT_VERTICAL_BAR device_node PUNCT_VERTICAL_BAR key_uv PUNCT_VERTICAL_BAR key_u_mask_shift NEWLINE
{
add_dk_entry(cf, $2, $4, $6, $8);
}
| ENTRY_ID_DK device_key PUNCT_VERTICAL_BAR device_node NEWLINE
{
add_dk_entry(cf, $2, $4, NULL, NULL);
}
;
device_key
: KEYWORD_DEVICE_KEY hexstring_list
{ $$ = $2; }
;
device_node
: KEYWORD_DEVICE_NODE hexstring_list
{ $$ = $2; }
;
key_uv
: KEYWORD_KEY_UV hexstring_list
{ $$ = $2; }
;
key_u_mask_shift
: KEYWORD_KEY_U_MASK_SHIFT hexstring_list
{ $$ = $2; }
;
pk_entry
: newline_list ENTRY_ID_PK hexstring_list NEWLINE
{
add_pk_entry(cf, $3);
}
| ENTRY_ID_PK hexstring_list NEWLINE
{
add_pk_entry(cf, $2);
}
;
host_cert_entry
: newline_list ENTRY_ID_HC host_priv_key PUNCT_VERTICAL_BAR host_cert PUNCT_VERTICAL_BAR host_nonce PUNCT_VERTICAL_BAR host_key_point NEWLINE
{
/* host_nonce and host_key_point are ignored, keep this for backward compatibility */
X_FREE($7);
X_FREE($9);
add_cert_entry(cf, $3, $5);
}
| ENTRY_ID_HC host_priv_key PUNCT_VERTICAL_BAR host_cert PUNCT_VERTICAL_BAR host_nonce PUNCT_VERTICAL_BAR host_key_point NEWLINE
{
/* host_nonce and host_key_point are ignored, keep this for backward compatibility */
X_FREE($6);
X_FREE($8);
add_cert_entry(cf, $2, $4);
}
| newline_list ENTRY_ID_HC host_priv_key PUNCT_VERTICAL_BAR host_cert NEWLINE
{
add_cert_entry(cf, $3, $5);
}
| ENTRY_ID_HC host_priv_key PUNCT_VERTICAL_BAR host_cert NEWLINE
{
add_cert_entry(cf, $2, $4);
}
;
host_priv_key
: KEYWORD_HOST_PRIV_KEY hexstring_list
{ $$ = $2; }
;
host_cert
: KEYWORD_HOST_CERT hexstring_list
{ $$ = $2; }
;
host_nonce
: KEYWORD_HOST_NONCE hexstring_list
{ $$ = $2; }
;
host_key_point
: KEYWORD_HOST_KEY_POINT hexstring_list
{ $$ = $2; }
;
title_entry
: newline_list disc_info entry_list NEWLINE
| disc_info entry_list NEWLINE
;
newline_list
: newline_list NEWLINE
| NEWLINE
;
disc_info
: discid PUNCT_EQUALS_SIGN disc_title
{
if (!cf->list) {
celist = cf->list = new_title_entry_list();
} else {
celist->next = new_title_entry_list();
celist = celist->next;
}
add_entry(celist, ENTRY_TYPE_DISCID, $1);
/*add_entry(celist, ENTRY_TYPE_TITLE, $3);*/
}
;
discid
: hexstring_list
;
disc_title
: DISC_TITLE
;
entry_list
: entry_list entry
| entry
;
entry
: date_entry
| mek_entry
| vid_entry
| bn_entry
| vuk_entry
| pak_entry
| tk_entry
| uk_entry
;
date_entry
: ENTRY_ID_DATE DIGIT PUNCT_HYPHEN DIGIT PUNCT_HYPHEN DIGIT
/*
{
add_date_entry(celist, $2, $4, $6);
}
*/
;
mek_entry
: ENTRY_ID_MEK hexstring_list
{
add_entry(celist, ENTRY_TYPE_MEK, $2);
}
;
vid_entry
: ENTRY_ID_VID hexstring_list
{
add_entry(celist, ENTRY_TYPE_VID, $2);
}
;
bn_entry
: ENTRY_ID_BN bn_data_list
{
dkplist = NULL;
}
;
bn_data_list
: bn_data_list PUNCT_VERTICAL_BAR bn_data
| bn_data
;
bn_data
: DIGIT PUNCT_HYPHEN hexstring_list
/*
{
if (!dkplist)
{
dkplist = new_digit_key_pair_list();
celist->entry.bn = dkplist;
}
dkplist = add_digit_key_pair_entry(dkplist, ENTRY_TYPE_BN, $1, $3);
}
*/
;
vuk_entry
: ENTRY_ID_VUK hexstring_list
{
add_entry(celist, ENTRY_TYPE_VUK, $2);
}
;
pak_entry
: ENTRY_ID_PAK pak_data_list
{
dkplist = NULL;
}
;
pak_data_list
: pak_data_list PUNCT_VERTICAL_BAR pak_data
| pak_data
;
pak_data
: DIGIT PUNCT_HYPHEN hexstring_list
/*
{
if (!dkplist)
{
dkplist = new_digit_key_pair_list();
celist->entry.pak = dkplist;
}
dkplist = add_digit_key_pair_entry(dkplist, ENTRY_TYPE_PAK, $1, $3);
}
*/
;
tk_entry
: ENTRY_ID_TK tk_data_list
{
dkplist = NULL;
}
;
tk_data_list
: tk_data_list PUNCT_VERTICAL_BAR tk_data
| tk_data
;
tk_data
: DIGIT PUNCT_HYPHEN hexstring_list
/*
{
if (!dkplist)
{
dkplist = new_digit_key_pair_list();
celist->entry.tk = dkplist;
}
dkplist = add_digit_key_pair_entry(dkplist, ENTRY_TYPE_TK, $1, $3);
}
*/
;
uk_entry
: ENTRY_ID_UK uk_data_list
{
dkplist = NULL;
}
;
uk_data_list
: uk_data_list PUNCT_VERTICAL_BAR uk_data
| uk_data
;
uk_data
: DIGIT PUNCT_HYPHEN hexstring_list
{
if (!dkplist)
{
dkplist = new_digit_key_pair_list();
celist->entry.uk = dkplist;
}
dkplist = add_digit_key_pair_entry(dkplist, ENTRY_TYPE_UK, $1, $3);
}
;
hexstring_list
: hexstring_list HEXSTRING
{
char *str = (char*)malloc(strlen($1) + strlen($2) + 1);
strcpy(str, $1);
strcat(str, $2);
$$ = str;
X_FREE($1);
}
| HEXSTRING
{
char *str = (char*)malloc(strlen($1) + 1);
strcpy(str, $1);
$$ = str;
}
;
%%
/* Function to parse a config file */
int keydbcfg_parse_config(config_file *cfgfile, const char *path)
{
if (!cfgfile || !path)
return 0;
#ifdef _WIN32
wchar_t wfilename[MAX_PATH];
if (!MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, filename, -1, wfilename, MAX_PATH))
return 0;
}
fp = _wfopen(wfilename, L"r");
#else
FILE * fp = fopen(path, "r");
#endif
if (!fp)
return 0;
void *scanner;
libaacs_yylex_init(&scanner);
libaacs_yyset_in(fp, scanner);
int retval = yyparse(scanner, cfgfile, NULL, NULL);
libaacs_yylex_destroy(scanner);
fclose(fp);
if (retval)
return 0;
return 1;
}
/* Function that returns pointer to new config file object */
config_file *keydbcfg_new_config_file(void)
{
config_file *cfgfile = (config_file *)malloc(sizeof(*cfgfile));
memset(cfgfile, 0, sizeof(*cfgfile));
return cfgfile;
}
/* Function that closes and frees a config file object */
int keydbcfg_config_file_close(config_file *cfgfile)
{
if (!cfgfile) {
return 0;
}
/* free pk list */
while (cfgfile->pkl)
{
pk_list *next = cfgfile->pkl->next;
X_FREE(cfgfile->pkl);
cfgfile->pkl = next;
}
/* free dk list */
while (cfgfile->dkl)
{
dk_list *next = cfgfile->dkl->next;
X_FREE(cfgfile->dkl);
cfgfile->dkl = next;
}
/* free host cert list */
while (cfgfile->host_cert_list)
{
cert_list *next = cfgfile->host_cert_list->next;
X_FREE(cfgfile->host_cert_list);
cfgfile->host_cert_list = next;
}
/* free title entries */
while (cfgfile->list)
{
title_entry_list *next = cfgfile->list->next;
/*X_FREE(cfgfile->list->entry.title);*/
X_FREE(cfgfile->list->entry.mek);
X_FREE(cfgfile->list->entry.vid);
/*DIGIT_KEY_PAIR_LIST_FREE(cfgfile->list->entry.bn);*/
/*DIGIT_KEY_PAIR_LIST_FREE(cfgfile->list->entry.pak);*/
/*DIGIT_KEY_PAIR_LIST_FREE(cfgfile->list->entry.tk);*/
DIGIT_KEY_PAIR_LIST_FREE(cfgfile->list->entry.uk);
X_FREE(cfgfile->list);
cfgfile->list = next;
}
/* free the config file object */
X_FREE(cfgfile);
return 1;
}
/* Function to return new dk_list object */
static dk_list *new_dk_list(void)
{
dk_list *dkl = (dk_list *)malloc(sizeof(*dkl));
memset(dkl, 0, sizeof(*dkl));
return dkl;
}
/* Function to add dk to config file */
static void add_dk_entry(config_file *cf, char *key, char *node, char *uv, char *u_mask_shift)
{
if (strlen(key) != 32) {
fprintf(stderr, "ignoring bad DK entry %s\n", key);
X_FREE(key);
return;
}
dk_list *entry = cf->dkl;
if (!entry) {
entry = cf->dkl = new_dk_list();
} else {
for (; entry->next; entry = entry->next);
entry->next = new_dk_list();
entry = entry->next;
}
hexstring_to_hex_array(entry->key, 16, key);
X_FREE(key);
entry->node = strtoul(node, NULL, 16);
X_FREE(node);
if (uv) {
entry->uv = strtoul(uv, NULL, 16);
X_FREE(uv);
}
if (u_mask_shift) {
entry->u_mask_shift = strtoul(u_mask_shift, NULL, 16);
X_FREE(u_mask_shift);
}
}
/* Function to return new pk_list object */
static pk_list *new_pk_list(void)
{
pk_list *pkl = (pk_list *)malloc(sizeof(*pkl));
memset(pkl, 0, sizeof(*pkl));
return pkl;
}
/* Function to add pk to config file */
static void add_pk_entry(config_file *cf, char *key)
{
if (strlen(key) != 32) {
fprintf(stderr, "ignoring bad PK entry %s\n", key);
X_FREE(key);
return;
}
pk_list *entry = cf->pkl;
if (!entry) {
entry = cf->pkl = new_pk_list();
} else {
for (; entry->next; entry = entry->next);
entry->next = new_pk_list();
entry = entry->next;
}
hexstring_to_hex_array(entry->key, 16, key);
X_FREE(key);
}
/* Function to create new certificate list */
static cert_list *new_cert_list(void)
{
cert_list *list = (cert_list *)malloc(sizeof(*list));
if (!list)
{
printf("Error allocating memory for new certificate list!\n");
return NULL;
}
memset(list, 0, sizeof(*list));
return list;
}
/* Function to add certificate list entry into config file object */
static void add_cert_entry(config_file *cf, char *host_priv_key, char *host_cert)
{
if (strlen(host_priv_key) != 40) {
fprintf(stderr, "ignoring bad private key entry %s\n", host_priv_key);
X_FREE(host_priv_key);
X_FREE(host_cert);
return;
}
if (strlen(host_cert) != 184) {
fprintf(stderr, "ignoring bad certificate entry %s\n", host_cert);
X_FREE(host_priv_key);
X_FREE(host_cert);
return;
}
cert_list *entry = cf->host_cert_list;
if (!entry) {
entry = cf->host_cert_list = new_cert_list();
} else {
for (; entry->next; entry = entry->next);
entry->next = new_cert_list();
entry = entry->next;
}
hexstring_to_hex_array(entry->host_priv_key, 20, host_priv_key);
X_FREE(host_priv_key);
hexstring_to_hex_array(entry->host_cert, 92, host_cert);
X_FREE(host_cert);
}
/* Function that returns pointer to new title entry list */
title_entry_list *new_title_entry_list(void)
{
title_entry_list *list = (title_entry_list *)malloc(sizeof(*list));
if (!list)
{
printf("Error allocating memory for new title entry list!\n");
return NULL;
}
memset(list, 0, sizeof(*list));
return list;
}
#define CHECK_KEY_LENGTH(name, len) \
if (strlen(entry) != len) { \
fprintf(stderr, "Ignoring bad "name" entry %s\n", entry); \
X_FREE(entry); \
break; \
}
/* Function to add standard string entries to a config entry */
static int add_entry(title_entry_list *list, int type, char *entry)
{
if (!list)
{
printf("Error: No title list passed as parameter.\n");
return 0;
}
switch (type)
{
case ENTRY_TYPE_DISCID:
CHECK_KEY_LENGTH("discid", 40)
hexstring_to_hex_array(list->entry.discid, 20, entry);
X_FREE(entry);
break;
#if 0
case ENTRY_TYPE_TITLE:
X_FREE(list->entry.title);
list->entry.title = (char*)malloc(strlen(entry) + 1);
strcpy(list->entry.title, entry);
break;
#endif
case ENTRY_TYPE_MEK:
CHECK_KEY_LENGTH("mek", 32)
X_FREE(list->entry.mek);
list->entry.mek = entry;
break;
case ENTRY_TYPE_VID:
CHECK_KEY_LENGTH("vid", 32)
X_FREE(list->entry.vid);
list->entry.vid = entry;
break;
case ENTRY_TYPE_VUK:
CHECK_KEY_LENGTH("vuk", 32)
hexstring_to_hex_array(list->entry.vuk, 16, entry);
X_FREE(entry);
break;
default:
X_FREE(entry);
printf("WARNING: entry type passed in unknown\n");
return 0;
}
return 1;
}
/* Function that returns pointer to new digit key pair list */
static digit_key_pair_list *new_digit_key_pair_list(void)
{
digit_key_pair_list *list = (digit_key_pair_list *)malloc(sizeof(*list));
if (!list)
{
printf("Error allocating memory for new digit key pair entry list!\n");
return NULL;
}
memset(list, 0, sizeof(*list));
return list;
}
/* Function used to add a digit/key pair to a list of digit key pair entries */
static digit_key_pair_list *add_digit_key_pair_entry(digit_key_pair_list *list,
int type, unsigned int digit, char *key)
{
if (!list)
{
printf("Error: No digit key pair list passed as parameter.\n");
return NULL;
}
list->key_pair.digit = digit;
list->key_pair.key = key;
list->next = new_digit_key_pair_list();
return list->next;
}
/* Function to add a date entry */
#if 0
static int add_date_entry(title_entry_list *list, unsigned int year,
unsigned int month, unsigned int day)
{
if (!list)
{
printf("Error: No title list passed as parameter.\n");
return 0;
}
list->entry.date.year = year;
list->entry.date.month = month;
list->entry.date.day = day;
return 1;
}
#endif
/* Our definition of yyerror */
void yyerror (void *scanner, config_file *cf,
title_entry_list *celist, digit_key_pair_list *dkplist,
const char *msg)
{
fprintf(stderr, "%s: line %d\n", msg, libaacs_yyget_lineno(scanner));
}