| 50 | | static int |
| 51 | | ctapi_reset(ct_handle *h, char p1, char p2, |
| | 44 | struct CardTerminal; |
| | 45 | |
| | 46 | struct CardTerminalFile |
| | 47 | { |
| | 48 | unsigned int id; |
| | 49 | int (*gen)(struct CardTerminal *ct, ct_buf_t *buf, off_t start, size_t length, size_t *size); |
| | 50 | struct CardTerminalFile *dir[20]; |
| | 51 | }; |
| | 52 | |
| | 53 | static struct CardTerminal |
| | 54 | { |
| | 55 | unsigned short ctn; |
| | 56 | ct_handle *h; |
| | 57 | unsigned int slots; |
| | 58 | ct_lock_handle lock; |
| | 59 | unsigned char sync; |
| | 60 | struct CardTerminalFile mf; |
| | 61 | struct CardTerminalFile ctcf; |
| | 62 | struct CardTerminalFile ctdir; |
| | 63 | struct CardTerminalFile iccdir[16]; |
| | 64 | struct CardTerminalFile hostcf; |
| | 65 | struct CardTerminalFile hoststatus; |
| | 66 | struct CardTerminalFile *cwd; |
| | 67 | struct CardTerminal *next; |
| | 68 | } *cardTerminals; |
| | 69 | |
| | 70 | static int |
| | 71 | put(ct_buf_t *buf, |
| | 72 | off_t *start, size_t *length, size_t *size, const unsigned char *data, size_t data_len) |
| | 73 | { |
| | 74 | *size+=data_len; |
| | 75 | while (data_len--) { |
| | 76 | if (*start==0) { |
| | 77 | if (*length>0) { |
| | 78 | if (buf!=(ct_buf_t*)0 && ct_buf_put(buf,data,1)<0) |
| | 79 | return -1; |
| | 80 | ++data; |
| | 81 | --(*length); |
| | 82 | } |
| | 83 | } |
| | 84 | else --(*start); |
| | 85 | } |
| | 86 | return 0; |
| | 87 | } |
| | 88 | |
| | 89 | static int |
| | 90 | dir(struct CardTerminal *ct, |
| | 91 | ct_buf_t *buf, off_t start, size_t length, size_t *size) |
| | 92 | { |
| | 93 | struct CardTerminalFile **entry; |
| | 94 | |
| | 95 | if (size!=(size_t*)0) |
| | 96 | *size=0; |
| | 97 | for (entry=&ct->cwd->dir[0]; *entry; ++entry) { |
| | 98 | char r[5]; |
| | 99 | int rc; |
| | 100 | |
| | 101 | r[0]=((*entry)->id>>8)&0xff; |
| | 102 | r[1]=((*entry)->id)&0xff; |
| | 103 | r[2]=0x01; |
| | 104 | r[3]=0x00; |
| | 105 | r[4]=0x00; |
| | 106 | if ((rc=put(buf,&start,&length,size,r,5))<0) return rc; |
| | 107 | } |
| | 108 | return 0; |
| | 109 | } |
| | 110 | |
| | 111 | static int |
| | 112 | ctcf(struct CardTerminal *ct, |
| | 113 | ct_buf_t *buf, off_t start, size_t length, size_t *size) |
| | 114 | { |
| | 115 | return 0; |
| | 116 | } |
| | 117 | |
| | 118 | static int |
| | 119 | hostcf(struct CardTerminal *ct, |
| | 120 | ct_buf_t *buf, off_t start, size_t length, size_t *size) |
| | 121 | { |
| | 122 | char data[2]; |
| | 123 | const char *version="OpenCT"; |
| | 124 | int rc; |
| | 125 | |
| | 126 | if (size!=(size_t*)0) *size=0; |
| | 127 | data[0]=0x01; |
| | 128 | data[1]=strlen(version); |
| | 129 | if ((rc=put(buf,&start,&length,size,data,2))<0) |
| | 130 | return rc; |
| | 131 | if ((rc=put(buf,&start,&length,size,version,strlen(version)))<0) |
| | 132 | return rc; |
| | 133 | return 0; |
| | 134 | } |
| | 135 | |
| | 136 | static int |
| | 137 | hoststatus(struct CardTerminal *ct, |
| | 138 | ct_buf_t *buf, off_t start, size_t length, size_t *size) |
| | 139 | { |
| | 140 | return 0; |
| | 141 | } |
| | 142 | |
| | 143 | static int |
| | 144 | CardTerminalFile_read(struct CardTerminal *ct, |
| | 145 | ct_buf_t *buf, off_t offset, size_t len) |
| | 146 | { |
| | 147 | int rc; |
| | 148 | size_t size; |
| | 149 | |
| | 150 | if ((rc=ct->cwd->gen(ct,buf,offset,len,&size))<0) |
| | 151 | return rc; |
| | 152 | if (offset>size) { |
| | 153 | return ctapi_error(buf,0x6b00); |
| | 154 | } |
| | 155 | else if (offset+len>=size) { |
| | 156 | if (ctapi_put_sw(buf,0x9000)<0) return ctapi_error(buf,CTBCS_SW_BAD_LENGTH); |
| | 157 | return 0; |
| | 158 | } |
| | 159 | else |
| | 160 | { |
| | 161 | if (ctapi_put_sw(buf,0x6282)<0) return ctapi_error(buf,CTBCS_SW_BAD_LENGTH); |
| | 162 | else return 0; |
| | 163 | } |
| | 164 | } |
| | 165 | |
| | 166 | static int |
| | 167 | CardTerminalFile_select(struct CardTerminal *ct, |
| | 168 | int id, ct_buf_t *buf) |
| | 169 | { |
| | 170 | struct CardTerminalFile *cur=(struct CardTerminalFile*)0; |
| | 171 | char r[12]={ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x00 }; |
| | 172 | size_t size=0; |
| | 173 | |
| | 174 | if (id==0x3f00) |
| | 175 | cur=&ct->mf; |
| | 176 | else if (id==0xff10) |
| | 177 | cur=&ct->hostcf; |
| | 178 | else if (id==0xff11) |
| | 179 | cur=&ct->hoststatus; |
| | 180 | else { |
| | 181 | struct CardTerminalFile **entry; |
| | 182 | |
| | 183 | for (entry=&ct->cwd->dir[0]; *entry && (*entry)->id!=id; ++entry); |
| | 184 | cur=*entry; |
| | 185 | } |
| | 186 | if (cur==(struct CardTerminalFile*)0) |
| | 187 | return ctapi_error(buf, 0x6a82); |
| | 188 | ct->cwd=cur; |
| | 189 | ct->cwd->gen(ct,(ct_buf_t*)0,0,1024,&size); |
| | 190 | r[2]=r[0]=size>>8; |
| | 191 | r[3]=r[1]=size&0xff; |
| | 192 | r[4]=ct->cwd->dir[0]!=(struct CardTerminalFile*)0?0x88:0x08; |
| | 193 | return ct_buf_put(buf,r,12); |
| | 194 | } |
| | 195 | |
| | 196 | static int |
| | 197 | ctapi_reset(struct CardTerminal *ct, char p1, char p2, |
| 216 | | if (cmd[0] != CTBCS_CLA) { |
| 217 | | ct_error("Bad CTBCS APDU, cla=0x%02x", cmd[0]); |
| 218 | | ctapi_error(&rbuf, CTBCS_SW_BAD_CLASS); |
| 219 | | goto out; |
| 220 | | } |
| 221 | | |
| 222 | | switch (cmd[1]) { |
| 223 | | case CTBCS_INS_RESET: /* old reset command */ |
| 224 | | case 0x10: /* new reset command */ |
| 225 | | rc = ctapi_reset(h, cmd[2], cmd[3], &rbuf, 0, NULL); |
| 226 | | break; |
| 227 | | case CTBCS_INS_REQUEST_ICC: |
| 228 | | rc = ctapi_request_icc(h, cmd[2], cmd[3], &sbuf, &rbuf); |
| 229 | | break; |
| 230 | | case CTBCS_INS_STATUS: |
| 231 | | rc = ctapi_status(h, &rbuf); |
| 232 | | break; |
| 233 | | default: |
| 234 | | ct_error("Bad CTBCS APDU, ins=0x%02x", cmd[1]); |
| 235 | | rc = ctapi_error(&rbuf, CTBCS_SW_BAD_INS); |
| | 367 | switch ((cmd[0]<<8)|cmd[1]) { |
| | 368 | case (CTBCS_CLA<<8)|CTBCS_INS_RESET: /* compatibility reset command */ |
| | 369 | case (CTBCS_CLA<<8)|0x10: /* native reset command */ |
| | 370 | rc = ctapi_reset(ct, cmd[2], cmd[3], &rbuf, 0, NULL); |
| | 371 | break; |
| | 372 | case (CTBCS_CLA<<8)|CTBCS_INS_REQUEST_ICC: |
| | 373 | rc = ctapi_request_icc(ct, cmd[2], cmd[3], &sbuf, &rbuf); |
| | 374 | break; |
| | 375 | case (CTBCS_CLA<<8)|CTBCS_INS_STATUS: |
| | 376 | rc = ctapi_status(ct->h, &rbuf); |
| | 377 | break; |
| | 378 | case (0x00<<8)|0xb0: |
| | 379 | rc = CardTerminalFile_read(ct, &rbuf, (cmd[2]<<8)|cmd[3], le); |
| | 380 | break; |
| | 381 | case (0x00<<8)|0xa4: |
| | 382 | if (cmd[4]!=2 || ct_buf_get(&sbuf, id, 2)==-1) { |
| | 383 | ct_error("Bad SELECT FILE ID"); |
| | 384 | rc = ctapi_error(&rbuf, CTBCS_SW_BAD_CLASS); |
| | 385 | } else |
| | 386 | rc = CardTerminalFile_select(ct, (id[0]<<8)|id[1],&rbuf); |
| | 387 | break; |
| | 388 | default: |
| | 389 | if (cmd[0]!=CTBCS_CLA && cmd[0]!=0x00) { |
| | 390 | ct_error("Bad CTBCS APDU, cla=0x%02x", cmd[0]); |
| | 391 | rc = ctapi_error(&rbuf, CTBCS_SW_BAD_CLASS); |
| | 392 | } |
| | 393 | else { |
| | 394 | ct_error("Bad CTBCS APDU, ins=0x%02x", cmd[1]); |
| | 395 | rc = ctapi_error(&rbuf, CTBCS_SW_BAD_INS); |
| | 396 | } |
| 242 | | ctapi_error(&rbuf, CTBCS_SW_BAD_LENGTH); |
| 243 | | |
| 244 | | out: return ct_buf_avail(&rbuf); |
| | 403 | return ctapi_error(&rbuf, CTBCS_SW_BAD_LENGTH); |
| | 404 | |
| | 405 | return ct_buf_avail(&rbuf); |
| | 406 | } |
| | 407 | |
| | 408 | /* |
| | 409 | * Handle card transactions |
| | 410 | */ |
| | 411 | static int |
| | 412 | ctapi_transact(struct CardTerminal *ct, int nslot, |
| | 413 | const char *cmd, size_t cmd_len, |
| | 414 | void *rsp, size_t rsp_len) |
| | 415 | { |
| | 416 | static const unsigned char select_kvk[11]={ 0x00, 0xa4, 0x04, 0x00, 0x06, 0xd2, 0x80, 0x00, 0x00, 0x01, 0x01 }; |
| | 417 | static const unsigned char read_binary[2]={ 0x00, 0xb0 }; |
| | 418 | ct_buf_t sbuf, rbuf; |
| | 419 | int rc; |
| | 420 | int le = 0; |
| | 421 | |
| | 422 | ct_buf_set(&sbuf, (void *) cmd, cmd_len); |
| | 423 | ct_buf_init(&rbuf, rsp, rsp_len); |
| | 424 | |
| | 425 | if (cmd_len == 4) |
| | 426 | { |
| | 427 | le = 0; |
| | 428 | ct_buf_get(&sbuf, NULL, 4); |
| | 429 | } |
| | 430 | else if (cmd_len == 5+(unsigned char)cmd[4]) |
| | 431 | { |
| | 432 | le = 0; |
| | 433 | ct_buf_get(&sbuf, NULL, 5); |
| | 434 | } |
| | 435 | else |
| | 436 | { |
| | 437 | le = (unsigned char)cmd[4]; |
| | 438 | ct_buf_get(&sbuf, NULL, 5); |
| | 439 | } |
| | 440 | if (le == 0) le = 256; |
| | 441 | |
| | 442 | if (cmd_len == 11 && memcmp(cmd, select_kvk, 11)==0) { |
| | 443 | rc = 0; |
| | 444 | if (rc < 0) |
| | 445 | return rc; |
| | 446 | if (ctapi_put_sw(&rbuf, 0x9000) < 0) |
| | 447 | return ctapi_error(&rbuf, CTBCS_SW_BAD_LENGTH); |
| | 448 | return ct_buf_avail(&rbuf); |
| | 449 | } |
| | 450 | else if ((ct->sync & (1 << nslot)) && cmd_len >= 5 && memcmp(cmd, read_binary, 2)==0) { |
| | 451 | unsigned char buf[256]; |
| | 452 | |
| | 453 | if ((rc = ct_card_read_memory(ct->h, nslot, (((unsigned char)cmd[2])<<8)|((unsigned char)cmd[3]), buf, le)) < 0) |
| | 454 | { |
| | 455 | #if 0 |
| | 456 | printf("rc is %d\n",rc); |
| | 457 | #endif |
| | 458 | return rc; |
| | 459 | } |
| | 460 | if (ct_buf_put(&rbuf, buf, rc) < 0 |
| | 461 | || ctapi_put_sw(&rbuf, 0x9000) < 0) |
| | 462 | return ctapi_error(&rbuf, CTBCS_SW_BAD_LENGTH); |
| | 463 | return ct_buf_avail(&rbuf); |
| | 464 | } |
| | 465 | else |
| | 466 | return ct_card_transact(ct->h, 0, |
| | 467 | cmd, cmd_len, rsp, rsp_len); |