/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <rights/Ro.h> #include <rights/Constraint.h> #include <rights/OperationPermission.h> #include <util/xml/DomExpatAgent.h> #include <util/domcore/DOMString.h> #include <utils/Log.h> #include <uassert.h> #include <time.h> #include <ofstream.h> using namespace ustl; const char *STR_RO_RIGHTS = "o-ex:rights"; const char *STR_RO_CONTEXT = "o-ex:context"; const char *STR_RO_AGREEMENT = "o-ex:agreement"; const char *STR_RO_ASSET = "o-ex:asset"; const char *STR_RO_INHERIT = "o-ex:inherit"; const char *STR_RO_DIGEST = "o-ex:digest"; const char *STR_RO_KEYINFO = "ds:KeyInfo"; const char *STR_RO_PERMISSION = "o-ex:permission"; const char *STR_RO_ASSET_ID = "o-ex:id"; const char *STR_RO_ASSET_IDREF = "o-ex:idref"; const char *STR_RO_CONTEXT_ID = "o-dd:uid"; const char *STR_RO_CONTEXT_VERSION = "o-dd:version"; const char *STR_RO_DIGEST_VALUE = "ds:DigestValue"; const char *STR_RO_CIPHER_VALUE = "xenc:CipherValue"; const char *STR_RO_RETRIEVAL_METHOD = "ds:RetrievalMethod"; const char *STR_RO_PLAY = "o-dd:play"; const char *STR_RO_DISPLAY = "o-dd:display"; const char *STR_RO_EXECUTE = "o-dd:execute"; const char *STR_RO_PRINT = "o-dd:print"; const char *STR_RO_EXPORT = "o-dd:export"; const char *STR_RO_CONSTRAINT = "o-ex:constraint"; const char *STR_RO_COUNT = "o-dd:count"; const char *STR_RO_TIMEDCOUNT = "o-dd:timed-count"; const char *STR_RO_TIMER = "oma-dd:timer"; const char *STR_RO_INTERVAL = "o-dd:interval"; const char *STR_RO_DATETIME = "o-dd:datetime"; const char *STR_RO_START = "o-dd:start"; const char *STR_RO_END = "o-dd:end"; const char *STR_RO_ACCUMULATED = "o-dd:accumulated"; const char *STR_RO_INDIVIDUAL = "o-dd:individual"; const char *STR_RO_SYSTEM = "o-dd:system"; /** see Ro.h */ Ro::Ro() { mDoc = new XMLDocumentImpl(); mProperRight = NULL; } /** see Ro.h */ Ro::~Ro() { for (vector<Right*>::iterator itr = mRightList.begin(); itr != mRightList.end(); itr++) { delete(*itr); } mRightList.clear(); for (vector<Asset*>::iterator ita = mAssetList.begin(); ita != mAssetList.end(); ita++) { delete(*ita); } mAssetList.clear(); mProperRight = NULL; delete mDoc; } /** see Ro.h */ void Ro::setRoID(string& id) { mRoID = id; } /** see Ro.h */ const string& Ro::getRoID() const { return mRoID; } /** see Ro.h */ void Ro::setRoVersion(string& version) { mRoVersion = version; } /** see Ro.h */ void Ro::addAsset(Asset* asset) { mAssetList.push_back(asset); } /** see Ro.h */ void Ro::addRight(Right* right) { mRightList.push_back(right); } /** see Ro.h */ bool Ro::save() { LOGI("==============Ro save.================="); return true; } /** see Ro.h */ Ro::ERRCODE Ro::parse(istringstream *roStream) { DomExpatAgent xmlAgent(mDoc); if (NULL == roStream) { LOGI("NULL stream"); return RO_NULL_STREAM; } if (xmlAgent.generateDocumentFromXML(roStream) == false) { LOGI("generate xml doc error"); return RO_ERR_BAD_XML; } handleDocument(mDoc); return RO_OK; } /** see Ro.h */ bool Ro::handleDocument(const XMLDocumentImpl* doc) { assert(doc != NULL); NodeImpl* node = doc->getDocumentElement(); return handleRights(node); } /** see Ro.h */ bool Ro::handleRights(const NodeImpl *curNode) { assert(curNode != NULL); NodeImpl *node = curNode->getFirstChild(); while (NULL != node) { const DOMString* name; name = static_cast<const XMLElementImpl*>(node)->getTagName(); if (name->compare(STR_RO_CONTEXT) == 0) { LOGI("rights context"); const DOMString *token = NULL; token = static_cast<const XMLElementImpl*>(node)->getSoloText(STR_RO_CONTEXT_ID); if (token) { LOGI(*token); mRoID = *token; } token = static_cast<const XMLElementImpl*>(node)->getSoloText(STR_RO_CONTEXT_VERSION); if (token) { LOGI(*token); mRoVersion = *token; } } if (name->compare(STR_RO_AGREEMENT) == 0) { LOGI("rights agreement"); if (handleAgreement(node) == false) { return false; } } node = node->getNextSibling(); } return true; } /** see Ro.h */ bool Ro::handleAgreement(const NodeImpl *curNode) { assert(curNode != NULL); NodeImpl *node = curNode->getFirstChild(); while (NULL != node) { const DOMString* name; name = static_cast<const XMLElementImpl*>(node)->getTagName(); if (name->compare(STR_RO_ASSET) == 0) { // do something about asset. LOGI("asset"); if (handleAsset(node) == false) { return false; } } if (name->compare(STR_RO_PERMISSION) == 0) { // do something about permission. LOGI("permission"); if (handlePermission(node) == false) { return false; } } node = node->getNextSibling(); } return true; } /** see Ro.h */ bool Ro::handleAsset(const NodeImpl *curNode) { assert(curNode != NULL); Asset *asset = new Asset(); const XMLElementImpl *curElement = static_cast<const XMLElementImpl*>(curNode); if (curElement->hasAttributes()) { DOMString assetID(STR_RO_ASSET_ID); LOGI("asset id:"); const DOMString *id = curElement->getAttribute(&assetID); if (id) { asset->setID(*id); } } NodeImpl* node = curNode->getFirstChild(); const DOMString *name = NULL; const string *token = NULL; while (NULL != node) { curElement = static_cast<const XMLElementImpl*>(node); name = curElement->getTagName(); if (name->compare(STR_RO_CONTEXT) == 0 || name->compare(STR_RO_INHERIT) == 0) { LOGI("asset context"); token = curElement->getSoloText(STR_RO_CONTEXT_ID); if (token) { LOGI(*token); if (name->compare(STR_RO_CONTEXT) == 0) { asset->setContentID(*token); } else { //parent ID. asset->setParentContentID(*token); } } } if (name->compare(STR_RO_DIGEST) == 0) { LOGI("asset digest"); //digest method is fixed value: //http://www.w3.org/2000/09/xmldisig#sha1 token = curElement->getSoloText(STR_RO_DIGEST_VALUE); if (token) { LOGI(*token); asset->setDCFDigest(*token); } } if (name->compare(STR_RO_KEYINFO) == 0) { LOGI("asset keyinfo"); token = curElement->getSoloText(STR_RO_CIPHER_VALUE); if (token) { LOGI(*token); asset->setEncryptedKey(*token); } const XMLElementImpl *node = curElement->getSoloElement(STR_RO_RETRIEVAL_METHOD); if (node) { if (node->hasAttributes()) { DOMString uri("URI"); token = node->getAttribute(&uri); if (token) { LOGI(*token); asset->setKeyRetrievalMethod(*token); } } } } node = node->getNextSibling(); } this->addAsset(asset); return true; } /** see Ro.h */ bool Ro::handlePermission(const NodeImpl *curNode) { assert(curNode != NULL); Right *right = new Right(); const XMLElementImpl *curElement = static_cast<const XMLElementImpl*>(curNode); NodeImpl* node = curNode->getFirstChild(); while (NULL != node) { const DOMString *name = NULL; NodeListImpl *nodeList = NULL; const string *token = NULL; curElement = static_cast<const XMLElementImpl*>(node); name = curElement->getTagName(); if (name->compare(STR_RO_ASSET) == 0) { LOGI("permission asset"); if (curElement->hasAttributes()) { DOMString assetID(STR_RO_ASSET_IDREF); const DOMString *id = curElement->getAttribute(&assetID); if (id) { right->addAssetID(*id); LOGI(*id); } } } OperationPermission::OPERATION type = OperationPermission::NONE; if (name->compare(STR_RO_PLAY) == 0) { LOGI("permission play constraint"); type = OperationPermission::PLAY; } if (name->compare(STR_RO_DISPLAY) == 0) { LOGI("permission display costraint"); type = OperationPermission::DISPLAY; } if (name->compare(STR_RO_EXECUTE) == 0) { LOGI("permission execute constraint"); type = OperationPermission::EXECUTE; } if (name->compare(STR_RO_EXPORT) == 0) { LOGI("permission export constraint"); type = OperationPermission::EXPORT; } if (name->compare(STR_RO_PRINT) == 0) { LOGI("permission print constraint"); type = OperationPermission::PRINT; } Constraint *cst = NULL; if (name->compare(STR_RO_CONSTRAINT) == 0) { LOGI("permission common constraint"); type = OperationPermission::COMMON; } cst = getConstraint(curElement); if (cst) { OperationPermission *op = new OperationPermission(type, cst); right->addOperationPermission(op); } node = node->getNextSibling(); } this->addRight(right); return true; } /** see Ro.h */ long Ro::convertISO8601DateTimeToLong(const char* ts) { if (NULL == ts) { return -1; } struct tm time; memset(&time, 0, sizeof(struct tm)); strptime(ts, "%FT%T%z", &time); //need timezone support: return mktime(&time) - timezone; //It seems android-sooner doesn't support timezone function. //line below is just for building, value would be wrong if no timezone minus. return mktime(&time); } /** see Ro.h */ long Ro::convertISO8601PeriodToLong(const char* ts) { if (NULL == ts) { return -1; } int date, hour, min, sec; sscanf(ts, "P%dDT%dH%dM%dS", &date, &hour, &min, &sec); LOGI("%d %d %d %d", date, hour, min, sec); return (date*24*60*60 + hour*60*60 + min*60 + sec); } /** see Ro.h */ Constraint* Ro::getConstraint(const NodeImpl* curNode) { assert(curNode != NULL); Constraint *constraint = new Constraint(); const XMLElementImpl *curElement = static_cast<const XMLElementImpl*>(curNode); const string *name = NULL; const string *token = NULL; if ((token = curElement->getSoloText(STR_RO_COUNT))) { LOGI(*token); constraint->setCount(atoi(token->c_str())); } if ((token = curElement->getSoloText(STR_RO_START))) { LOGI(*token); //start Time constraint->setStartTime(convertISO8601DateTimeToLong(token->c_str())); } if ((token = curElement->getSoloText(STR_RO_END))) { LOGI(*token); //end Time constraint->setEndTime(convertISO8601DateTimeToLong(token->c_str())); } if ((token = curElement->getSoloText(STR_RO_INTERVAL))) { LOGI(*token); constraint->setInterval(atoi(token->c_str())); } if ((token = curElement->getSoloText(STR_RO_ACCUMULATED))) { LOGI(*token); //Period constraint->setAccumulated(convertISO8601PeriodToLong(token->c_str())); } if ((token = curElement->getSoloText(STR_RO_TIMEDCOUNT))) { LOGI(*token); constraint->setTimedCount(atoi(token->c_str())); const XMLElementImpl *node = curElement->getSoloElement(STR_RO_TIMEDCOUNT); if (node) { if (node->hasAttributes()) { DOMString timer(STR_RO_TIMER); token = node->getAttribute(&timer); if (token) { LOGI(*token); constraint->setTimer(atoi(token->c_str())); } } } } return constraint; } /** see Ro.h */ void Ro::loadRights(const string& contentID) { for (vector<Right*>::iterator it = this->mRightList.begin(); it != this->mRightList.end(); it++) { if ((*it)->mAssetNameList.empty()) { mContentRightList.push_back(*it); } else { for (vector<Asset*>::iterator ita = this->mAssetList.begin(); ita != this->mAssetList.end(); ita++) { for (vector<string>::iterator its = (*it)->mAssetNameList.begin(); its != (*it)->mAssetNameList.end(); its++) { if ((*its).compare((*ita)->getID()) == 0) { if (contentID.compare((*ita)->getContentID()) == 0) { LOGI("find content right"); mContentRightList.push_back(*it); } } } } } } } /** see Ro.h */ void Ro::freeRights() { mContentRightList.clear(); } /** see Ro.h */ bool Ro::checkPermission(OperationPermission::OPERATION type, const string& contentID) { loadRights(contentID); for (vector<Right*>::iterator it = mContentRightList.begin(); it != mContentRightList.end(); it++) { if ((*it)->checkPermission(type)) { freeRights(); return true; } } freeRights(); return false; } /** see Ro.h */ Ro::ERRCODE Ro::consume(OperationPermission::OPERATION type, const string& contentID) { loadRights(contentID); //check in mRightList vector<Right*>::iterator it; vector<Right*> tmpList; vector<Right*> retList; Constraint *constraint = NULL; long ealiestEnd = -1; bool hasCommonConstraint = false; bool hasUnconstraint = false; bool hasDateTimeConstraint = false; bool hasTimedCountConstraint = false; bool hasIntervalConstraint = false; //apply the RO rule, if do not satisfy the constraint, . //proper right select process for (it = mContentRightList.begin(); it != mContentRightList.end(); it++) { if ((*it)->checkPermission(type)) { constraint = (*it)->getConstraint(OperationPermission::COMMON); if (constraint) { if (!constraint->isValid(time(NULL))) { continue; } hasCommonConstraint = true; tmpList.push_back(*it); } constraint = (*it)->getConstraint(type); assert(constraint != NULL); if (!constraint->isValid(time(NULL))) { continue; } if (constraint->isUnConstraint()) { //use uncontrainted firstly. hasUnconstraint = true; tmpList.push_back(*it); break; } if (constraint->isDateTimeConstraint()) { //use datetime constraint in high priority. //if contain multipe constraints, use the earliest expire time. hasDateTimeConstraint = true; tmpList.push_back(*it); continue; } if (constraint->isTimedCountConstraint()) { //illegal Operation when time counted if (type == OperationPermission::PRINT || type == OperationPermission::EXPORT) { continue; } hasTimedCountConstraint = true; tmpList.push_back(*it); continue; } if (constraint->isIntervalConstraint()) { hasIntervalConstraint = true; tmpList.push_back(*it); continue; } tmpList.push_back(*it); } } for (it = tmpList.begin(); it != tmpList.end(); it++) { if (hasUnconstraint == true) { //delete other constraint constraint = (*it)->getConstraint(type); if (constraint) { if (constraint->isUnConstraint()) { retList.push_back(*it); break; } } continue; } if (hasDateTimeConstraint == true) { //delete other constraint constraint = (*it)->getConstraint(type); if (constraint) { if (constraint->isDateTimeConstraint()) { long tt = constraint->getEndTime(); if (ealiestEnd == -1) { ealiestEnd = tt; retList.push_back(*it); } else if (ealiestEnd > tt) { ealiestEnd = tt; retList.pop_back(); retList.push_back(*it); } } } continue; } if (hasIntervalConstraint == true) { //delete other constraint constraint = (*it)->getConstraint(type); if (constraint) { if (constraint->isIntervalConstraint()) { retList.push_back(*it); } } continue; } if (hasTimedCountConstraint == true) { constraint = (*it)->getConstraint(type); if (constraint) { if (constraint->isTimedCountConstraint()) { retList.push_back(*it); } } continue; } retList.push_back(*it); } if (retList.size() == 0) { freeRights(); return RO_BAD; } LOGI("Proper right has %d", retList.size()); assert(retList.size() == 1); mProperRight = retList[0]; constraint = retList[0]->getConstraint(OperationPermission::COMMON); if (constraint) { if (constraint->consume() == false) { freeRights(); return RO_BAD; } } constraint = retList[0]->getConstraint(type); if (constraint) { if (constraint->consume() == false) { freeRights(); return RO_BAD; } } //update the constraint freeRights(); return RO_OK; } /** see Ro.h */ string Ro::getContentCek(const string& contentID) { for (vector<Asset*>::iterator it = mAssetList.begin(); it != mAssetList.end(); it++) { if (contentID.compare((*it)->getContentID()) == 0) { return (*it)->getCek(); } } return ""; } /** see Ro.h */ string Ro::getContentHash(const string& contentID) { for (vector<Asset*>::iterator it = mAssetList.begin(); it != mAssetList.end(); it++) { if (contentID.compare((*it)->getContentID()) == 0) { return (*it)->getDCFDigest(); } } return ""; }