SASC
DVBSoftCAM

sc-viaccess.c

Go to the documentation of this file.
00001 /*
00002  * Softcam plugin to VDR (C++)
00003  *
00004  * This code is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License
00006  * as published by the Free Software Foundation; either version 2
00007  * of the License, or (at your option) any later version.
00008  *
00009  * This code is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00017  * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
00018  */
00019 
00020 #include <stdio.h>
00021 #include <stdlib.h>
00022 #include <string.h>
00023 #include <ctype.h>
00024 
00025 #include "system-common.h"
00026 #include "smartcard.h"
00027 #include "crypto.h"
00028 #include "parse.h"
00029 #include "opts.h"
00030 #include "misc.h"
00031 #include "log-sc.h"
00032 #include "log-core.h"
00033 #include "version.h"
00034 
00035 SCAPIVERSTAG();
00036 
00037 #define SYSTEM_VIACCESS      0x0500
00038 
00039 #define SYSTEM_NAME          "SC-Viaccess"
00040 #define SYSTEM_PRI           -5
00041 #define SYSTEM_CAN_HANDLE(x) ((x)==SYSTEM_VIACCESS)
00042 
00043 #define SC_NAME "Viaccess"
00044 #define SC_ID   MAKE_SC_ID('V','i','a','s')
00045 
00046 #define L_SC        13
00047 #define L_SC_ALL    LALL(L_SC_LASTDEF)
00048 
00049 static const struct LogModule lm_sc = {
00050   (LMOD_ENABLE|L_SC_ALL)&LOPT_MASK,
00051   (LMOD_ENABLE|L_SC_DEFDEF)&LOPT_MASK,
00052   "sc-viaccess",
00053   { L_SC_DEFNAMES }
00054   };
00055 ADD_MODULE(L_SC,lm_sc)
00056 
00057 static int disableParental=0;
00058 
00059 // -- cSystemScViaccess ------------------------------------------------------------------
00060 
00061 class cSystemScViaccess : public cSystemScCore { //, private cTPS {
00062 public:
00063   cSystemScViaccess(void);
00064   virtual void ParseCADescriptor(cSimpleList<cEcmInfo> *ecms, unsigned short sysId, int source, const unsigned char *data, int len);
00065   };
00066 
00067 cSystemScViaccess::cSystemScViaccess(void)
00068 :cSystemScCore(SYSTEM_NAME,SYSTEM_PRI,SC_ID,"SC Viaccess")
00069 {
00070   hasLogger=true;
00071 }
00072 
00073 void cSystemScViaccess::ParseCADescriptor(cSimpleList<cEcmInfo> *ecms, unsigned short sysId, int source, const unsigned char *data, int len)
00074 {
00075   const int pid=WORD(data,2,0x1FFF);
00076   if(pid>=0xAA && pid<=0xCF) {
00077     PRINTF(L_CORE_ECMPROC,"sc-viaccess: dropped \"fake\" ecm pid 0x%04xn",pid);
00078     return;
00079     }
00080   cSystem::ParseCADescriptor(ecms,sysId,source,data,len);
00081 }
00082 
00083 // -- cSystemLinkScViaccess --------------------------------------------------------------
00084 
00085 class cSystemLinkScViaccess : public cSystemLink {
00086 public:
00087   cSystemLinkScViaccess(void);
00088   virtual bool CanHandle(unsigned short SysId);
00089   virtual cSystem *Create(void) { return new cSystemScViaccess; }
00090   };
00091 
00092 static cSystemLinkScViaccess staticInit;
00093 
00094 cSystemLinkScViaccess::cSystemLinkScViaccess(void)
00095 :cSystemLink(SYSTEM_NAME,SYSTEM_PRI)
00096 {
00097   static const char *rat[] = {
00098     trNOOP("don't touch"),
00099     trNOOP("disable")
00100     };
00101 
00102   opts=new cOpts(SYSTEM_NAME,1);
00103   opts->Add(new cOptSel("DisableParental",trNOOP("SC-Viaccess: Parental lock"),&disableParental,sizeof(rat)/sizeof(char *),rat));
00104   Feature.NeedsSmartCard();
00105 }
00106 
00107 bool cSystemLinkScViaccess::CanHandle(unsigned short SysId)
00108 {
00109   return smartcards.HaveCard(SC_ID) && SYSTEM_CAN_HANDLE(SysId);
00110 }
00111 
00112 // -- cSmartCardDataViaccess ---------------------------------------------------
00113 
00114 enum eDataType { dtPIN, dtAES };
00115 
00116 class cSmartCardDataViaccess : public cSmartCardData {
00117 private:
00118   int IdLen(void) const { return 5; }
00119 public:
00120   eDataType type;
00121   unsigned char id[5], pin[8], aes[16];
00122   //
00123   cSmartCardDataViaccess(void);
00124   cSmartCardDataViaccess(eDataType Type, unsigned char *Id);
00125   virtual bool Parse(const char *line);
00126   virtual bool Matches(cSmartCardData *param);
00127   };
00128 
00129 cSmartCardDataViaccess::cSmartCardDataViaccess(void)
00130 :cSmartCardData(SC_ID)
00131 {
00132   memset(id,0,sizeof(id));
00133 }
00134 
00135 cSmartCardDataViaccess::cSmartCardDataViaccess(eDataType Type, unsigned char *Id)
00136 :cSmartCardData(SC_ID)
00137 {
00138   type=Type;
00139   memset(id,0,sizeof(id));
00140   memcpy(id,Id,IdLen());
00141 }
00142 
00143 bool cSmartCardDataViaccess::Matches(cSmartCardData *param)
00144 {
00145   cSmartCardDataViaccess *cd=(cSmartCardDataViaccess *)param;
00146   return cd->type==type && !memcmp(cd->id,id,IdLen());
00147 }
00148 
00149 bool cSmartCardDataViaccess::Parse(const char *line)
00150 {
00151   line=skipspace(line);
00152   if(!strncasecmp(line,"PIN",3))       { type=dtPIN; line+=3; }
00153   else if(!strncasecmp(line,"AES",3))  { type=dtAES; line+=3; }
00154   else {
00155     PRINTF(L_CORE_LOAD,"smartcarddataviaccess: format error: datatype");
00156     return false;
00157     }
00158   line=skipspace(line);
00159   if(GetHex(line,id,IdLen())!=IdLen()) {
00160     PRINTF(L_CORE_LOAD,"smartcarddataviaccess: format error: serial");
00161     return false;
00162     }
00163   line=skipspace(line);
00164   if(type==dtPIN) {
00165     memset(pin,0,sizeof(pin));
00166     int i, shift=4;
00167     for(i=0; i<16; i++) {
00168       if(!isdigit(*line)) {
00169         PRINTF(L_CORE_LOAD,"smartcarddataviaccess: format error: pin");
00170         return false;
00171         }
00172       pin[i]|=(*line++ - '0')<<shift;
00173       shift^=4;
00174       }
00175     i=(i+1)/2; // num bytes
00176     memmove(&pin[8-i],pin,i);
00177 //XXX
00178 LDUMP(L_CORE_LOAD,pin,sizeof(pin),"smartcarddataviaccess: pin ");
00179     }
00180   else {
00181     if(GetHex(line,aes,16)!=16) {
00182       PRINTF(L_CORE_LOAD,"smartcarddataviaccess: format error: aes");
00183       return false;
00184       }
00185     }
00186   return true;
00187 }
00188 
00189 // -- cProviderScViaccess ----------------------------------------------------------
00190 
00191 class cProviderScViaccess : public cProviderViaccess {
00192 public:
00193   unsigned char availKeys[16];
00194   char name[33];
00195   //
00196   cProviderScViaccess(const unsigned char *id, const unsigned char *s):cProviderViaccess(id,s) {}
00197   };
00198 
00199 // -- cSmartCardViaccess -----------------------------------------------------------------
00200 
00201 class cSmartCardViaccess : public cSmartCard, public cIdSet, private cAES {
00202 private:
00203   unsigned char ua[5], sa0[4], lastId[3];
00204   //
00205   unsigned char GetSW1(void) { return sb[1]; }
00206   bool CheckKey(cProviderScViaccess *p, const unsigned char keynr);
00207   bool SetProvider(const unsigned char *id);
00208 public:
00209   cSmartCardViaccess(void);
00210   virtual bool Init(void);
00211   virtual bool Decode(const cEcmInfo *ecm, const unsigned char *data, unsigned char *cw);
00212   virtual bool Update(int pid, int caid, const unsigned char *data);
00213   };
00214 
00215 static const struct StatusMsg msgs[] = {
00216   { { 0x6b,0x00 }, "Instruction not supported", false },
00217   { { 0x6d,0x00 }, "Instruction not supported", false },
00218   { { 0x90,0x00 }, "Instruction executed without errors", true },
00219   { { 0x90,0x08 }, "Instruction executed without errors", true },
00220   { { 0xFF,0xFF }, 0, false }
00221   };
00222 
00223 static const struct CardConfig cardCfg = {
00224   SM_8O2,500,300
00225   };
00226 
00227 cSmartCardViaccess::cSmartCardViaccess(void)
00228 :cSmartCard(&cardCfg,msgs)
00229 {
00230   memset(lastId,0,sizeof(lastId));
00231 }
00232 
00233 bool cSmartCardViaccess::Init(void)
00234 {
00235   static const unsigned char verifyBytes[] = { 0x90,0x00 };
00236   if(atr->T!=0 || atr->histLen<7 || memcmp(&atr->hist[5],verifyBytes,sizeof(verifyBytes))) {
00237     PRINTF(L_SC_INIT,"doesn't look like a Viaccess card");
00238     return false;
00239     }
00240 
00241   infoStr.Begin();
00242   infoStr.Strcat("Viaccess smartcard\n");
00243   const char *ver=0;
00244   switch((atr->hist[3]<<8)|atr->hist[4]) {
00245     case 0x6268: ver="2.3"; break;
00246     case 0x6468:
00247     case 0x6668: ver="2.4"; break;
00248     case 0xa268: ver="2.5"; break;
00249     case 0xc168: ver="2.6"; break;
00250     default: ver="unknown"; break;
00251     }
00252       
00253   PRINTF(L_SC_INIT,"card v.%s",ver);
00254   snprintf(idStr,sizeof(idStr),"%s (V.%s)",SC_NAME,ver);
00255 
00256   static unsigned char insac[] = { 0xca, 0xac, 0x00, 0x00, 0x00 }; // select data
00257   static unsigned char insb8[] = { 0xca, 0xb8, 0x00, 0x00, 0x00 }; // read selected data
00258   static unsigned char insa4[] = { 0xca, 0xa4, 0x00, 0x00, 0x00 }; // select issuer
00259   static unsigned char insc0[] = { 0xca, 0xc0, 0x00, 0x00, 0x00 }; // read data item
00260   unsigned char buff[MAX_LEN];
00261 
00262   ResetIdSet();
00263   insac[2]=0xa4; // request unique id
00264   if(!IsoWrite(insac,buff) || !Status()) {
00265     PRINTF(L_SC_ERROR,"failed to request ua");
00266     return false;
00267     }
00268   insb8[4]=0x07; // read unique id
00269   if(!IsoRead(insb8,buff) || !Status() || buff[1]!=0x05) {
00270     PRINTF(L_SC_ERROR,"failed to read ua");
00271     return false;
00272     }
00273   memcpy(ua,&buff[2],sizeof(ua));
00274   SetCard(new cCardViaccess(ua));
00275   PRINTF(L_SC_INIT,"card UA: %llu",Bin2LongLong(ua,5));
00276   infoStr.Printf("Card v.%s UA %010llu\n",ver,Bin2LongLong(ua,5));
00277 
00278   insa4[2]=0x00; // select issuer 0
00279   if(!IsoWrite(insa4,buff) || !Status()) {
00280     PRINTF(L_SC_ERROR,"failed to select issuer 0");
00281     return false;
00282     }
00283   int issuer=0;
00284   do {
00285     insc0[4]=0x1a; // show provider properties
00286     if(!IsoRead(insc0,buff) || !Status()) {
00287       PRINTF(L_SC_ERROR,"failed to read prov properties");
00288       return false;
00289       }
00290 
00291     unsigned char buff2[MAX_LEN];
00292     insac[2]=0xa5; // request sa
00293     if(!IsoWrite(insac,buff2) || !Status()) {
00294       PRINTF(L_SC_ERROR,"failed to request sa");
00295       return false;
00296       }
00297     insb8[4]=0x06; // read sa
00298     if(!IsoRead(insb8,buff2) || !Status()) {
00299       PRINTF(L_SC_ERROR,"failed to read sa");
00300       return false;
00301       }
00302     if(issuer==0) memcpy(sa0,&buff[2],sizeof(sa0));
00303     cProviderScViaccess *p=new cProviderScViaccess(&buff[0],&buff2[2]);
00304     if(p) {
00305       AddProv(p);
00306       memcpy(p->availKeys,buff+10,sizeof(p->availKeys));
00307 
00308       insac[2]=0xa7; // request name
00309       if(!IsoWrite(insac,buff) || !Status()) {
00310         PRINTF(L_SC_ERROR,"failed to request prov name");
00311         return false;
00312         }
00313       insb8[4]=0x02; // read name nano + len
00314       if(!IsoRead(insb8,buff) || !Status()) {
00315         PRINTF(L_SC_ERROR,"failed to read prov name length");
00316         return false;
00317         }
00318       unsigned int nameLen=buff[1];
00319       if(nameLen>=sizeof(p->name)) {
00320         PRINTF(L_SC_ERROR,"provider name buffer overflow");
00321         nameLen=sizeof(p->name)-1;
00322         }
00323       insb8[4]=nameLen;
00324       if(!IsoRead(insb8,buff) || !Status()) {
00325         PRINTF(L_SC_ERROR,"failed to read prov name");
00326         return false;
00327         }
00328       memcpy(p->name,buff,nameLen); p->name[nameLen]=0;
00329 
00330       PRINTF(L_SC_INIT,"provider %06x (%s)",(int)p->ProvId(),p->name);
00331       infoStr.Printf("Prov %06x (%s) SA %08u\n",(int)p->ProvId(),p->name,Bin2Int(&buff2[2],4));
00332       }
00333     else PRINTF(L_SC_ERROR,"no memory for provider");
00334 
00335     insa4[2]=0x02; // next issuer
00336     if(!IsoWrite(insa4,buff) || !Status()) {
00337       PRINTF(L_SC_ERROR,"failed to select next issuer");
00338       return false;
00339       }
00340     issuer++;
00341     } while(GetSW1()==0x00);
00342   infoStr.Finish();
00343 
00344   if(disableParental) {
00345       static unsigned char ins24[] = { 0xca,0x24,0x02,0x00,0x09 };
00346       static unsigned char pindt[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F };
00347       cSmartCardDataViaccess cd(dtPIN,ua);
00348       cSmartCardDataViaccess *entry=(cSmartCardDataViaccess *)smartcards.FindCardData(&cd);
00349       if(entry) {
00350         PRINTF(L_SC_INIT,"got PIN from smartcard.conf.");
00351         memcpy(&pindt[0],entry->pin,8);
00352         if(!IsoWrite(ins24,pindt) || !Status())
00353           PRINTF(L_SC_ERROR,"failed to disable parental lock");
00354         }
00355       else PRINTF(L_SC_INIT,"no PIN available.");
00356     }
00357   cSmartCardDataViaccess cd(dtAES,ua);
00358   cSmartCardDataViaccess *entry=(cSmartCardDataViaccess *)smartcards.FindCardData(&cd);
00359   if(entry) {
00360     PRINTF(L_SC_INIT,"got AES key from smartcard.conf.");
00361     SetKey(entry->aes);
00362     }
00363 
00364   return true;
00365 }
00366 
00367 bool cSmartCardViaccess::CheckKey(cProviderScViaccess *p, const unsigned char keynr)
00368 {
00369   for(unsigned int j=0; j<sizeof(p->availKeys); j++)
00370     if(p->availKeys[j]==keynr) return true;
00371   return false;
00372 }
00373 
00374 bool cSmartCardViaccess::SetProvider(const unsigned char *id)
00375 {
00376   static const unsigned char insa4[] = { 0xca,0xa4,0x04,0x00,0x03 };
00377 
00378   if(id[0]!=lastId[0] || id[1]!=lastId[1] || (id[2]&0xF0)!=lastId[2]) {
00379     memcpy(lastId,id,3);
00380     lastId[2]&=0xF0;
00381     if(!IsoWrite(insa4,lastId) || !Status()) return false;
00382     }
00383   return true;
00384 }
00385 
00386 bool cSmartCardViaccess::Decode(const cEcmInfo *ecm, const unsigned char *data, unsigned char *cw)
00387 {
00388   static unsigned char ins88[] = { 0xca,0x88,0x00,0x00,0x00 }; // set ecm
00389   static unsigned char insf8[] = { 0xca,0xf8,0x00,0x00,0x00 }; // set geographic info 
00390   static unsigned char insc0[] = { 0xca,0xc0,0x00,0x00,0x12 }; // read dcw
00391 
00392   cProviderScViaccess *p=(cProviderScViaccess *)FindProv(data);
00393   if(p) {
00394     unsigned char keynr=cParseViaccess::KeyNr(data);
00395     if(CheckKey(p,keynr)) {
00396       if(!SetProvider(cParseViaccess::ProvIdPtr(data))) return false;
00397 
00398       const unsigned char *start=cParseViaccess::PayloadStart(data);
00399       bool hasD2=false;
00400       if(start[0]==0xD2) { // TPS alike, TNTSAT/TNTop
00401         start+=start[1]+2;
00402         hasD2=true;
00403         }
00404       start+=5;
00405 
00406       const unsigned char *DE04=0;
00407       if(start[0]==0xDE && start[1]==0x04) {
00408         DE04=start;
00409         start+=6;
00410         }
00411       const unsigned char *ecm88Data=start;
00412       int ecm88Len=SCT_LEN(data)-(start-data);
00413       int ecmf8Len=0;
00414       while(ecm88Len>0 && ecm88Data[0]<0xA0) {
00415         const int nanoLen=ecm88Data[1]+2;
00416         ecmf8Len+=nanoLen;
00417         ecm88Len-=nanoLen; ecm88Data+=nanoLen;
00418         }
00419       if(ecmf8Len) {
00420         insf8[3]=keynr;
00421         insf8[4]=ecmf8Len;
00422         if(!IsoWrite(insf8,start) || !Status()) return false;
00423         }
00424 
00425       ins88[2]=ecmf8Len?1:0;
00426       ins88[3]=keynr;
00427       ins88[4]=ecm88Len;
00428       if(DE04) {
00429         unsigned char *mem=AUTOMEM(ecm88Len+6);
00430         memcpy(mem,DE04,6);
00431         memcpy(mem+6,ecm88Data,ecm88Len);
00432         ins88[4]+=6;
00433         ecm88Data=mem;
00434         }
00435       if(!IsoWrite(ins88,ecm88Data) || !Status()) return false; // request dcw
00436       unsigned char buff[MAX_LEN];
00437       if(!IsoRead(insc0,buff) || !Status()) return false; // read dcw
00438       unsigned char cwbuff[16];
00439       int cwmask=0;
00440       switch(buff[0]) {
00441         case 0xe8: // even
00442           if(buff[1]==8) { memcpy(cwbuff,buff+2,8); cwmask=1; }
00443           break;
00444         case 0xe9: // odd
00445           if(buff[1]==8) { memcpy(cwbuff+8,buff+2,8); cwmask=2; }
00446           break;
00447         case 0xea: // complete
00448           if(buff[1]==16) { memcpy(cwbuff,buff+2,16); cwmask=3; }
00449           break;
00450         }
00451       if(cwmask) {
00452         if(hasD2 && !Decrypt(cwbuff,16))
00453           PRINTF(L_SC_ERROR,"no AES key for this card");
00454         if(cwmask&1) memcpy(cw,cwbuff,8);
00455         if(cwmask&2) memcpy(cw+8,cwbuff+8,8);
00456         return true;
00457         }
00458       }
00459     else PRINTF(L_SC_ERROR,"key not found on card");
00460     }
00461   else PRINTF(L_SC_ERROR,"provider not found on card");
00462   return false;
00463 }
00464 
00465 bool cSmartCardViaccess::Update(int pid, int caid, const unsigned char *data)
00466 {
00467   static unsigned char insf0[] = { 0xca,0xf0,0x00,0x00,0x00 };
00468   static unsigned char ins18[] = { 0xca,0x18,0x00,0x00,0x00 };
00469   static unsigned char insf4[] = { 0xca,0xf4,0x00,0x00,0x00 };
00470   static unsigned char ins1c[] = { 0xca,0x1c,0x01,0x00,0x00 };
00471 
00472   int updtype;
00473   cAssembleData ad(data);
00474   if(MatchAndAssemble(&ad,&updtype,0)) {
00475     while((data=ad.Assembled())) {
00476       const unsigned char *start=cParseViaccess::NanoStart(data);
00477       int nanolen=SCT_LEN(data)-(start-data);
00478 
00479       if(start && nanolen>=5) {
00480         const unsigned char *nano81=0, *nano91=0, *nano92=0, *nano9e=0, *nanof0=0;
00481         unsigned char *nanos18=AUTOMEM(nanolen+10);
00482         bool provOK=false;
00483         int len18=0, keynr=0;
00484         
00485         while(nanolen>0) {
00486           switch(start[0]) {
00487             case 0x90:
00488               if(SetProvider(&start[2])) {
00489                 provOK=true;
00490                 keynr=cParseViaccess::KeyNrFromNano(start);
00491                 }
00492               break;
00493             case 0x9E: // ADF
00494               if(!nano91) { // not crypted
00495                 unsigned char custwp=sa0[3];
00496                 const unsigned char *afd=&start[2];
00497                 if(!(afd[31-custwp/8] & (1<<(custwp&7)))) { // not for this card
00498                   provOK=false; nanolen=0; // skip, next assemble data
00499                   break;
00500                   }
00501                 }
00502               nano9e=start;
00503               break;
00504             case 0x81: nano81=start; break;
00505             case 0x91: nano91=start; break;
00506             case 0x92: nano92=start; break;
00507             case 0xF0: nanof0=start; break;
00508             default:
00509               {
00510               int l=start[1]+2;
00511               memcpy(nanos18+len18,start,l);
00512               len18+=l;
00513               }
00514             }
00515           int l=start[1]+2;
00516           start+=l; nanolen-=l;
00517           }
00518 
00519         if(provOK && nanof0) {
00520           unsigned char buff[MAX_LEN*2];
00521           if(nano9e) {
00522             int l9e=nano9e[1]+2;
00523             if(!nano91) {
00524               insf0[3]=keynr;
00525               insf0[4]=l9e;
00526               if(!IsoWrite(insf0,nano9e) || !Status()) continue;
00527               }
00528             else {
00529               int l91=nano91[1]+2;
00530               insf4[3]=keynr;
00531               insf4[4]=l91+l9e;
00532               memcpy(buff,nano91,l91);
00533               memcpy(buff+l91,nano9e,l9e);
00534               if(!IsoWrite(insf4,buff) || !Status()) continue;
00535               }
00536             }
00537           if(!nano92) {
00538             int lf0=nanof0[1]+2;
00539             ins18[2]=updtype>0 ? updtype-1 : updtype;
00540             ins18[3]=keynr;
00541             ins18[4]=len18+lf0;
00542             memcpy(buff,nanos18,len18);
00543             memcpy(buff+len18,nanof0,lf0);
00544             if(IsoWrite(ins18,buff)) Status();
00545             }
00546           else if(nano81) {
00547             int l92=nano92[1]+2, l81=nano81[1]+2, lf0=nanof0[1]+2;
00548             ins1c[3]=keynr;
00549             ins1c[4]=l92+l81+lf0;
00550             memcpy(buff,nano92,l92);
00551             memcpy(buff+l92,nano81,l81);
00552             memcpy(buff+l92+l81,nanof0,lf0);
00553             if(IsoWrite(ins1c,buff)) Status();
00554             }
00555           }
00556         }
00557       }
00558     return true;
00559     }
00560   return false;
00561 }
00562 
00563 // -- cSmartCardLinkViaccess -------------------------------------------------------------
00564 
00565 class cSmartCardLinkViaccess : public cSmartCardLink {
00566 public:
00567   cSmartCardLinkViaccess(void):cSmartCardLink(SC_NAME,SC_ID) {}
00568   virtual cSmartCard *Create(void) { return new cSmartCardViaccess(); }
00569   };
00570 
00571 static cSmartCardLinkViaccess staticScInit;
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines