mmc.c 35.7 KB
Newer Older
1
/* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil -*- */
2
3
/*
 * This file is part of libaacs
4
 * Copyright (C) 2009-2010  Obliter0n
5
 * Copyright (C) 2010-2013  npzacs
6
 *
gates's avatar
gates committed
7
8
9
10
 * 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.
11
 *
gates's avatar
gates committed
12
 * This library is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
gates's avatar
gates committed
14
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
16
 *
gates's avatar
gates committed
17
18
19
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library. If not, see
 * <http://www.gnu.org/licenses/>.
20
21
 */

Accident's avatar
   
Accident committed
22
23
24
#if HAVE_CONFIG_H
#include "config.h"
#endif
npzacs's avatar
npzacs committed
25

26
27
#include "mmc.h"
#include "crypto.h"
npzacs's avatar
npzacs committed
28
#include "file/path.h"
29
30
#include "util/macro.h"
#include "util/logging.h"
31

Accident's avatar
   
Accident committed
32
#include <stdlib.h>
gates's avatar
gates committed
33
#include <errno.h>
34
35
36
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
37

38
39
40
41
42
43
44
45
46
47
48
#include <stdio.h>

#ifdef USE_IOKIT

#include <Carbon/Carbon.h>

#include <IOKit/IOKitLib.h>
#include <IOKit/IOCFPlugIn.h>

#include <DiskArbitration/DiskArbitration.h>

49
50
51
52
53
54
/* need to undefine VERSION as one of the members of struct 
   SCSICmd_INQUIRY_StandardData is named VERSION (see 
   IOKit/scsi/SCSICmds_INQUIRY_Definitions.h) */
#undef VERSION
#include <IOKit/scsi/SCSITaskLib.h>

55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#include <IOKit/storage/IOBDMediaBSDClient.h>

#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif

#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif

#ifdef HAVE_SYS_MOUNT_H
#include <sys/mount.h>
#endif

#ifdef HAVE_LIBGEN_H
#include <libgen.h>
#endif

#endif

gates's avatar
gates committed
75
76
77
#ifdef HAVE_MNTENT_H
#include <mntent.h>
#endif
78

79
80
81
82
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif

83
#ifdef HAVE_LINUX_CDROM_H
84
#include <sys/ioctl.h>
85
#include <linux/cdrom.h>
cRTrn13's avatar
cRTrn13 committed
86
#endif
Accident's avatar
   
Accident committed
87

npzacs's avatar
npzacs committed
88
89
90
91
92
#if defined(_WIN32)
#include <windows.h>
#include <winsock.h>
#endif

npzacs's avatar
npzacs committed
93
94
95
96
#ifndef DEBUG_KEYS
#define DEBUG_KEYS 0
#endif

npzacs's avatar
npzacs committed
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#if defined(_WIN32)
/*
 * from ntddscsi.h, Windows DDK
 */
#   define SCSI_IOCTL_DATA_OUT             0
#   define SCSI_IOCTL_DATA_IN              1
#   define SCSI_IOCTL_DATA_UNSPECIFIED     2
#   define IOCTL_SCSI_PASS_THROUGH_DIRECT  0x4D014
#   define MAX_SENSE_LEN                   18

typedef struct _SCSI_PASS_THROUGH_DIRECT {
    USHORT Length;
    UCHAR  ScsiStatus;
    UCHAR  PathId;
    UCHAR  TargetId;
    UCHAR  Lun;
    UCHAR  CdbLength;
    UCHAR  SenseInfoLength;
    UCHAR  DataIn;
    ULONG  DataTransferLength;
    ULONG  TimeOutValue;
    PVOID  DataBuffer;
    ULONG  SenseInfoOffset;
    UCHAR  Cdb[16];
} SCSI_PASS_THROUGH_DIRECT, *PSCSI_PASS_THROUGH_DIRECT;

#endif // defined(_WIN32)

npzacs's avatar
npzacs committed
125
struct mmc {
126
127
128
129
130
131
132
133
134
135
136
137
#if defined(USE_IOKIT)
    MMCDeviceInterface **mmcInterface;
    SCSITaskDeviceInterface **taskInterface;

    /* device short name (ie disk1) */
    char bsd_name[128];

    /* for mounting/unmounting the disc */
    DADiskRef disk;
    DASessionRef session;
    bool is_mounted;
#elif defined(_WIN32)
npzacs's avatar
npzacs committed
138
139
140
141
    HANDLE fd;
#else
    int    fd;
#endif
npzacs's avatar
npzacs committed
142
    uint8_t host_nonce[20];
143
    uint8_t host_key[20];
npzacs's avatar
npzacs committed
144
    uint8_t host_key_point[40];
145

npzacs's avatar
npzacs committed
146
147
148
    uint8_t drive_cert[92];

    uint8_t read_drive_cert;
npzacs's avatar
npzacs committed
149
150
};

gates's avatar
gates committed
151
152
static int _mmc_send_cmd(MMC *mmc, const uint8_t *cmd, uint8_t *buf, size_t tx,
                         size_t rx)
153
{
npzacs's avatar
npzacs committed
154
#if defined(HAVE_LINUX_CDROM_H)
npzacs's avatar
npzacs committed
155
    if (mmc->fd >= 0) {
156
157
158
159
160
161
        struct cdrom_generic_command cgc;
        struct request_sense sense;

        memset(&cgc, 0, sizeof(cgc));
        memcpy(cgc.cmd, cmd, CDROM_PACKET_SIZE);
        cgc.sense = &sense;
cRTrn13's avatar
cRTrn13 committed
162
        cgc.timeout = 5000;
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178

        if (buf) {
            if (tx) {
                cgc.data_direction = CGC_DATA_WRITE;
                cgc.buflen = tx;
                cgc.buffer = buf;
            } else if (rx) {
                cgc.data_direction = CGC_DATA_READ;
                cgc.buflen = rx;
                cgc.buffer = buf;
            }
        } else {
            cgc.data_direction = CGC_DATA_NONE;
            cgc.buflen = 0;
            cgc.buffer = NULL;
        }
179

180
        int a = ioctl(mmc->fd, CDROM_SEND_PACKET, &cgc);
181

182
        char str[512];
183
184
        DEBUG(DBG_MMC, "Send LINUX MMC cmd %s:\n",
              print_hex(str, cmd, 16));
185
        if (tx) {
186
            DEBUG(DBG_MMC, "  Buffer: %s ->\n", print_hex(str, buf, tx>255?255:tx));
187
        } else {
188
            DEBUG(DBG_MMC, "  Buffer: %s <-\n", print_hex(str, buf, rx>255?255:rx));
189
190
        }

npzacs's avatar
npzacs committed
191
        if (a >= 0) {
192
            DEBUG(DBG_MMC, "  Send succeeded! [%d]\n", a);
193
194
            return 1;
        }
npzacs's avatar
npzacs committed
195

196
        DEBUG(DBG_MMC, "  Send failed! [%d] %s\n", a, strerror(errno));
197
    }
npzacs's avatar
npzacs committed
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241

#elif defined(_WIN32)

    DWORD dwBytesReturned;

    struct {
        SCSI_PASS_THROUGH_DIRECT sptd;
        UCHAR                    SenseBuf[MAX_SENSE_LEN];
    } sptd_sb;

    if (mmc->fd == INVALID_HANDLE_VALUE) {
        return 0;
    }

    sptd_sb.sptd.Length          = sizeof(SCSI_PASS_THROUGH_DIRECT);
    sptd_sb.sptd.PathId          = 0;
    sptd_sb.sptd.TargetId        = 0;
    sptd_sb.sptd.Lun             = 0;
    sptd_sb.sptd.CdbLength       = 12;
    sptd_sb.sptd.SenseInfoLength = MAX_SENSE_LEN;
    sptd_sb.sptd.TimeOutValue    = 5;
    sptd_sb.sptd.SenseInfoOffset = sizeof(SCSI_PASS_THROUGH_DIRECT);

    if (buf) {
        if (tx) {
            sptd_sb.sptd.DataIn = SCSI_IOCTL_DATA_OUT;
            sptd_sb.sptd.DataTransferLength = tx;
            sptd_sb.sptd.DataBuffer = buf;
        } else if (rx) {
            sptd_sb.sptd.DataIn = SCSI_IOCTL_DATA_IN;
            sptd_sb.sptd.DataTransferLength = rx;
            sptd_sb.sptd.DataBuffer = buf;
        }
    } else {
        sptd_sb.sptd.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED;
        sptd_sb.sptd.DataTransferLength = 0;
        sptd_sb.sptd.DataBuffer = NULL;
    }

    memcpy(sptd_sb.sptd.Cdb, cmd, 16);

    ZeroMemory(sptd_sb.SenseBuf, MAX_SENSE_LEN);

    if (DeviceIoControl(mmc->fd,
npzacs's avatar
npzacs committed
242
                        IOCTL_SCSI_PASS_THROUGH_DIRECT,
npzacs's avatar
npzacs committed
243
244
245
246
247
                        (void*)&sptd_sb, sizeof(sptd_sb),
                        (void*)&sptd_sb, sizeof(sptd_sb),
                        &dwBytesReturned, NULL)) {

        if (sptd_sb.sptd.ScsiStatus == 0 /* STATUS_GOOD */) {
248
            DEBUG(DBG_MMC, "  Send succeeded!\n");
npzacs's avatar
npzacs committed
249
250
251
252
            return 1;
        }
    }

253
    DEBUG(DBG_MMC, "  Send failed!\n");
npzacs's avatar
npzacs committed
254

255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
#elif defined(USE_IOKIT)
    SCSITaskInterface **task = NULL;
    SCSI_Sense_Data sense;
    SCSITaskStatus status;
    SCSITaskSGElement iov;
    UInt8 direction;
    UInt64 sent;
    int rc;

    if (NULL == mmc->taskInterface) {
        return 0;
    }

    do {
        task = (*mmc->taskInterface)->CreateSCSITask (mmc->taskInterface);
        if (NULL == task) {
271
            DEBUG(DBG_MMC, "Could not create SCSI Task\n");
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
            break;
        }

        iov.address = (uintptr_t) buf;
        iov.length  = tx ? tx : rx;

        if (buf) {
            direction = tx ? kSCSIDataTransfer_FromInitiatorToTarget :
                kSCSIDataTransfer_FromTargetToInitiator;
        } else {
            direction = kSCSIDataTransfer_NoDataTransfer;
        }

        rc = (*task)->SetCommandDescriptorBlock (task, cmd, 16);
        if (kIOReturnSuccess != rc) {
287
            DEBUG(DBG_MMC, "Error setting SCSI command\n");
288
289
290
291
292
            break;
        }

        rc = (*task)->SetScatterGatherEntries (task, &iov, 1, iov.length, direction);
        if (kIOReturnSuccess != rc) {
293
            DEBUG(DBG_MMC, "Error setting SCSI scatter gather entries\n");
294
295
296
297
298
            break;
        }

        rc = (*task)->SetTimeoutDuration (task, 5000000);
        if (kIOReturnSuccess != rc) {
299
            DEBUG(DBG_MMC, "Error setting SCSI command timeout\n");
300
301
302
303
304
305
306
307
            break;
        }

        memset (&sense, 0, sizeof (sense));

        rc = (*task)->ExecuteTaskSync (task, &sense, &status, &sent);

        char str[512];
308
        DEBUG(DBG_MMC, "Send SCSI MMC cmd %s:\n", print_hex(str, cmd, 16));
309
        if (tx) {
310
            DEBUG(DBG_MMC, "  Buffer: %s ->\n", print_hex(str, buf, tx>255?255:tx));
311
        } else {
312
            DEBUG(DBG_MMC, "  Buffer: %s <-\n", print_hex(str, buf, rx>255?255:rx));
313
314
315
        }

        if (kIOReturnSuccess != rc || status != 0) {
316
            DEBUG(DBG_MMC, "  Send failed!\n");
317
318
            break;
        } else {
319
            DEBUG(DBG_MMC, "  Send succeeded! sent = %lld status = %u. response = %x\n",
320
321
322
323
324
325
326
327
328
329
330
                  (unsigned long long) sent, status, sense.VALID_RESPONSE_CODE);
        }

        (*task)->Release (task);

        return 1;
    } while (0);

    if (task) {
        (*task)->Release (task);
    }
cRTrn13's avatar
cRTrn13 committed
331
#endif
332

333
    return 0;
334
335
}

336
337
338
339
/*
 *
 */

gates's avatar
gates committed
340
341
342
static int _mmc_report_key(MMC *mmc, uint8_t agid, uint32_t addr,
                           uint8_t blocks, uint8_t format, uint8_t *buf,
                           uint16_t len)
343
344
{
    uint8_t cmd[16];
345
    memset(cmd, 0, sizeof(cmd));
346
347
    memset(buf, 0, len);

348
    DEBUG(DBG_MMC, "MMC report key...\n");
349

350
351
352
353
354
355
356
357
358
    cmd[0] = 0xa4;
    cmd[2] = (addr >> 24) & 0xff;
    cmd[3] = (addr >> 16) & 0xff;
    cmd[4] = (addr >> 8) & 0xff;
    cmd[5] = addr & 0xff;
    cmd[6] = blocks;
    cmd[7] = 0x02;
    cmd[8] = (len >> 8) & 0xff;
    cmd[9] = len & 0xff;
npzacs's avatar
npzacs committed
359
    cmd[10] = (agid << 6) | (format & 0x3f);
360

npzacs's avatar
npzacs committed
361
    return _mmc_send_cmd(mmc, cmd, buf, 0, len);
362
363
}

gates's avatar
gates committed
364
365
static int _mmc_send_key(MMC *mmc, uint8_t agid, uint8_t format, uint8_t *buf,
                         uint16_t len)
366
367
{
    uint8_t cmd[16];
368
    char str[512];
369
    memset(cmd, 0, sizeof(cmd));
370

371
    DEBUG(DBG_MMC, "MMC send key [%d] %s...\n", len, print_hex(str, buf, len));
372

373
374
375
376
    cmd[0] = 0xa3;
    cmd[7] = 0x02;
    cmd[8] = (len >> 8) & 0xff;
    cmd[9] = len & 0xff;
npzacs's avatar
npzacs committed
377
    cmd[10] = (agid << 6) | (format & 0x3f);
378

379
    DEBUG(DBG_MMC, "cmd: %s\n", print_hex(str, cmd, 16));
npzacs's avatar
npzacs committed
380
381
382
    return _mmc_send_cmd(mmc, cmd, buf, len, 0);
}

npzacs's avatar
npzacs committed
383
384
385
386
387
388
389
390
static int _mmc_report_disc_structure(MMC *mmc, uint8_t agid, uint8_t format,
                                      uint8_t layer, uint32_t address,
                                      uint8_t *buf, uint16_t len)
{
    uint8_t cmd[16];
    memset(cmd, 0, sizeof(cmd));
    memset(buf, 0, len);

npzacs's avatar
npzacs committed
391
392
    DEBUG(DBG_MMC, "MMC report disc structure [format 0x%x layer %d address %d] ...\n", format, layer, address);

npzacs's avatar
npzacs committed
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
    cmd[0] = 0xad; // operation code
    cmd[1] = 0x01; // BluRay
    cmd[2] = (address >> 24) & 0xff;
    cmd[3] = (address >> 16) & 0xff;
    cmd[4] = (address >> 8) & 0xff;
    cmd[5] = address & 0xff;
    cmd[6] = layer;
    cmd[7] = format;
    cmd[8] = (len >> 8) & 0xff;
    cmd[9] = len & 0xff;
    cmd[10] = (agid << 6);

    return _mmc_send_cmd(mmc, cmd, buf, 0, len);
}

408
409
410
411
412
413
static int _mmc_get_configuration(MMC *mmc, uint16_t feature, uint8_t *buf, uint16_t len)
{
    uint8_t cmd[16];
    memset(cmd, 0, sizeof(cmd));
    memset(buf, 0, len);

npzacs's avatar
npzacs committed
414
415
    DEBUG(DBG_MMC, "MMC get configuration [feature 0x%x] ...\n", feature);

416
417
418
419
420
421
422
423
424
425
426
427
428
429
    cmd[0] = 0x46; // operation code
    cmd[1] = 0x01; // BluRay
    cmd[2] = (feature >> 8) & 0xff;
    cmd[3] = feature & 0xff;
    cmd[7] = (len >> 8) & 0xff;
    cmd[8] = len & 0xff;

    return _mmc_send_cmd(mmc, cmd, buf, 0, len);
}

/*
 *
 */

npzacs's avatar
npzacs committed
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
static int _mmc_invalidate_agid(MMC *mmc, uint8_t agid)
{
    uint8_t buf[2];
    memset(buf, 0, sizeof(buf));

    return _mmc_report_key(mmc, agid, 0, 0, 0x3f, buf, 2);
}

static void _mmc_invalidate_agids(MMC *mmc)
{
    int     agid;

    /* invalidate all agids */
    for (agid = 0; agid < 4; agid++) {
        _mmc_invalidate_agid(mmc, agid);
    }
}

static int _mmc_report_agid(MMC *mmc, uint8_t *agid)
{
    uint8_t buf[8];
    memset(buf, 0, sizeof(buf));

    int result = _mmc_report_key(mmc, 0, 0, 0, 0, buf, 8);
    if (result) {
        *agid = (buf[7] & 0xff) >> 6;
    }
    return result;
458
459
}

gates's avatar
gates committed
460
461
462
static int _mmc_send_host_cert(MMC *mmc, uint8_t agid,
                               const uint8_t *host_nonce,
                               const uint8_t *host_cert)
463
464
465
466
467
468
469
470
471
472
473
{
    uint8_t buf[116];
    memset(buf, 0, sizeof(buf));

    buf[1] = 0x72;
    memcpy(buf + 4,  host_nonce, 20);
    memcpy(buf + 24, host_cert,  92);

    return _mmc_send_key(mmc, agid, 0x01, buf, 116);
}

474
475
static int _mmc_read_drive_cert_challenge(MMC *mmc, uint8_t agid, uint8_t *drive_nonce,
                                          uint8_t *drive_cert)
476
477
478
479
480
481
482
483
484
485
486
487
{
    uint8_t buf[116];
    memset(buf, 0, sizeof(buf));

    if (_mmc_report_key(mmc, agid, 0, 0, 0x01, buf, 116)) {
        memcpy(drive_nonce, buf + 4,  20);
        memcpy(drive_cert,  buf + 24, 92);
        return 1;
    }
    return 0;
}

gates's avatar
gates committed
488
489
static int _mmc_read_drive_key(MMC *mmc, uint8_t agid, uint8_t *drive_key_point,
                               uint8_t *drive_key_signature)
490
491
492
493
494
495
496
497
498
499
500
501
{
    uint8_t buf[84];
    memset(buf, 0, sizeof(buf));

    if (_mmc_report_key(mmc, agid, 0, 0, 0x02, buf, 84)) {
        memcpy(drive_key_point,     buf + 4,  40);
        memcpy(drive_key_signature, buf + 44, 40);
        return 1;
    }
    return 0;
}

502
503
504
505
506
507
508
509
510
511

static int _mmc_check_aacs(MMC *mmc)
{
    uint8_t buf[16];
    memset(buf, 0, sizeof(buf));

    if (_mmc_get_configuration(mmc, 0x010d, buf, 16)) {
        uint16_t feature = MKINT_BE16(buf+8);
        if (feature == 0x010d) {
            DEBUG(DBG_MMC, "AACS feature descriptor:\n");
512
            DEBUG(DBG_MMC, "  AACS version: %d\n", buf[7+8]);
513
514
515
            DEBUG(DBG_MMC, "  AACS active: %d\n", buf[2+8] & 1);
            DEBUG(DBG_MMC, "  Binding Nonce generation support: %d\n", buf[4+8] & 1);
            DEBUG(DBG_MMC, "  Binding Nonce block count: %d\n", buf[5+8]);
516
517
            DEBUG(DBG_MMC, "  Bus encryption support: %d\n", !!(buf[4+8] & 2));
            DEBUG(DBG_MMC, "  Read drive certificate: %d\n", !!(buf[4+8] & 0x10));
518
519
            DEBUG(DBG_MMC, "  AGID count: %d\n", buf[6+8] & 0xf);

npzacs's avatar
npzacs committed
520
            mmc->read_drive_cert = !!(buf[4+8] & 0x10);
521

522
523
524
525
526
527
528
529
530
            return buf[2+8] & 1;
        }
        DEBUG(DBG_MMC, "incorrect feature ID %04x\n", feature);
    }

    DEBUG(DBG_MMC, "_mmc_get_configuration() failed\n");
    return 0;
}

npzacs's avatar
npzacs committed
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
static uint8_t *_mmc_read_mkb(MMC *mmc, uint8_t agid, int address, int *size)
{
    const int layer = 0;
    uint8_t *mkb = NULL;
    uint8_t buf[32772];

    *size = 0;
    if (_mmc_report_disc_structure(mmc, agid, 0x83, layer, address, buf, sizeof(buf))) {
        unsigned pack, num_packs = buf[3];
        int32_t  len = MKINT_BE16(buf) - 2;
        mkb = malloc(32768 * num_packs);

        DEBUG(DBG_MMC, "got mkb: pack 0/%d %d bytes\n", num_packs, len);
        memcpy(mkb, buf + 4, len);
       *size += len;

        for (pack = 1; pack < num_packs; pack++) {
            if (_mmc_report_disc_structure(mmc, agid, 0x83, layer, pack, buf, sizeof(buf))) {
                len = MKINT_BE16(buf) - 2;
                DEBUG(DBG_MMC, "got mkb: pack %d/%d %d bytes\n", pack, num_packs, len);

                memcpy(mkb + *size, buf + 4, len);
                *size += len;
            } else {
                break;
            }
        }
    }

    return mkb;
}

gates's avatar
gates committed
563
564
565
static int _mmc_send_host_key(MMC *mmc, uint8_t agid,
                              const uint8_t *host_key_point,
                              const uint8_t *host_key_signature)
566
567
{
    uint8_t buf[84];
568
    memset(buf, 0, sizeof(buf));
569
570
571
572
573
574
575
576

    buf[1] = 0x52;
    memcpy(buf + 4,  host_key_point,     40);
    memcpy(buf + 44, host_key_signature, 40);

    return _mmc_send_key(mmc, agid, 0x02, buf, 84);
}

gates's avatar
gates committed
577
578
static int _mmc_read_vid(MMC *mmc, uint8_t agid, uint8_t *volume_id,
                         uint8_t *mac)
579
580
{
    uint8_t buf[36];
npzacs's avatar
npzacs committed
581
    memset(buf, 0, sizeof(buf));
582

npzacs's avatar
npzacs committed
583
    if (_mmc_report_disc_structure(mmc, agid, 0x80, 0, 0, buf, 36)) {
584
585
586
587
588
589
590
591
        memcpy(volume_id, buf + 4,  16);
        memcpy(mac,       buf + 20, 16);
        return 1;
    }

    return 0;
}

592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
static int _mmc_read_pmsn(MMC *mmc, uint8_t agid, uint8_t *pmsn,
                          uint8_t *mac)
{
    uint8_t buf[36];
    memset(buf, 0, sizeof(buf));

    if (_mmc_report_disc_structure(mmc, agid, 0x81, 0, 0, buf, 36)) {
        memcpy(pmsn, buf + 4,  16);
        memcpy(mac,  buf + 20, 16);
        return 1;
    }

    return 0;
}

607
608
609
610
611
612
613
614
615
616
617
618
619
static int _mmc_read_data_keys(MMC *mmc, uint8_t agid, uint8_t *read_data_key, uint8_t *write_data_key)
{
    uint8_t buf[36];

    if (_mmc_report_disc_structure(mmc, agid, 0x84, 0, 0, buf, 36)) {
        memcpy(read_data_key, buf + 4, 16);
        memcpy(write_data_key, buf + 20, 16);
        return 1;
    }

    return 0;
}

620
621
622
623
624
625
626
627
628
629
#ifdef USE_IOKIT
static int get_mounted_device_from_path (MMC *mmc, const char *path) {
  struct statfs stat_info;
  int rc;

  rc = statfs (path, &stat_info);
  if (0 != rc) {
    return rc;
  }

630
  strncpy (mmc->bsd_name, basename (stat_info.f_mntfromname), sizeof (mmc->bsd_name));
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692

  return 0;
}

static void iokit_unmount_complete (DADiskRef disk, DADissenterRef dissenter,
                                    void *context) {
    (void)disk; /* suppress warning */

    if (dissenter) {
        DEBUG(DBG_MMC, "Could not unmount the disc\n");
    } else {
        DEBUG(DBG_MMC, "Disc unmounted\n");
        ((MMC *)context)->is_mounted = 0;
    }
}

static void iokit_mount_complete (DADiskRef disk, DADissenterRef dissenter,
                                  void *context) {
    (void) disk; /* suppress warning */
    (void) dissenter; /* suppress warning */

    /* the disc mounts despite whether there is a dessenter */
    DEBUG(DBG_MMC, "Disc mounted\n");
    ((MMC *)context)->is_mounted = 1;
}

static int iokit_unmount (MMC *mmc) {
    if (0 == mmc->is_mounted) {
        return 0; /* nothing to do */
    }

    DEBUG(DBG_MMC, "Unmounting disk\n");

    mmc->session = DASessionCreate (kCFAllocatorDefault);
    if (NULL == mmc->session) {
        DEBUG(DBG_MMC, "Could not create a disc arbitration session\n");
        return -1;
    }

    mmc->disk = DADiskCreateFromBSDName (kCFAllocatorDefault, mmc->session, mmc->bsd_name);
    if (NULL == mmc->disk) {
        DEBUG(DBG_MMC, "Could not create a disc arbitration disc for the device\n");
        CFRelease (mmc->session);
        mmc->session = NULL;
        return -1;
    }

    DAApprovalSessionScheduleWithRunLoop (mmc->session, CFRunLoopGetCurrent (),
                                          kCFRunLoopDefaultMode);

    DADiskUnmount (mmc->disk, kDADiskUnmountOptionForce, iokit_unmount_complete, mmc);

    CFRunLoopRunInMode (kCFRunLoopDefaultMode, 10, true);

    return mmc->is_mounted ? -1 : 0;
}

static int iokit_mount (MMC *mmc) {
    if (0 == mmc->is_mounted) {
        if (mmc->disk && mmc->session) {
            DADiskMount (mmc->disk, NULL, kDADiskMountOptionDefault, iokit_mount_complete, mmc);

693
            CFRunLoopRunInMode (kCFRunLoopDefaultMode, 10, true);
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736

            DAApprovalSessionUnscheduleFromRunLoop (mmc->session, CFRunLoopGetCurrent (),
                                                    kCFRunLoopDefaultMode);
        }

        if (mmc->disk) {
            CFRelease (mmc->disk);
            mmc->disk = NULL;
        }

        if (mmc->session) {
            CFRelease (mmc->session);
            mmc->session = NULL;
        }
    }

    return mmc->is_mounted ? 0 : -1;
}

static int iokit_find_service_matching (MMC *mmc, io_service_t *servp) {
    CFMutableDictionaryRef matchingDict = IOServiceMatching("IOBDServices");
    io_iterator_t deviceIterator;
    io_service_t service;
    int rc;

    assert (NULL != servp);

    *servp = 0;

    if (!matchingDict) {
        DEBUG(DBG_MMC, "Could not create a matching dictionary for IOBDServices\n");
        return -1;
    }

    /* this call consumes the reference to the matchingDict. we do not need to release it */
    rc = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, &deviceIterator);
    if (kIOReturnSuccess != rc) {
        DEBUG(DBG_MMC, "Could not create device iterator\n");
        return -1;
    }

    while (0 != (service = IOIteratorNext (deviceIterator))) {
        CFStringRef data;
737
        char name[128] = "";
738
739
740
741
742
743

        data = IORegistryEntrySearchCFProperty (service, kIOServicePlane, CFSTR("BSD Name"),
                                                kCFAllocatorDefault, kIORegistryIterateRecursively);

        if (NULL != data) {
            rc = CFStringGetCString (data, name, sizeof (name), kCFStringEncodingASCII);
744
            CFRelease (data);
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
            if (0 == strcmp (name, mmc->bsd_name)) {
                break;
            }
        }

        (void) IOObjectRelease (service);
    }

    IOObjectRelease (deviceIterator);

    *servp = service;

    return (service) ? 0 : -1;
}

static int iokit_find_interfaces (MMC *mmc, io_service_t service) {
    IOCFPlugInInterface **plugInInterface = NULL;
    SInt32 score;
    int rc;

    rc = IOCreatePlugInInterfaceForService (service, kIOMMCDeviceUserClientTypeID,
                                            kIOCFPlugInInterfaceID, &plugInInterface,
                                            &score);
    if (kIOReturnSuccess != rc || NULL == plugInInterface) {
        return -1;
    }

    DEBUG(DBG_MMC, "Getting MMC interface\n");

    rc = (*plugInInterface)->QueryInterface(plugInInterface,
                                            CFUUIDGetUUIDBytes(kIOMMCDeviceInterfaceID),
                                            (LPVOID)&mmc->mmcInterface);
777
778
779
    /* call release instead of IODestroyPlugInInterface to avoid stopping IOBDServices */
    (*plugInInterface)->Release(plugInInterface);
    if (kIOReturnSuccess != rc || NULL == mmc->mmcInterface) {
780
781
782
783
        DEBUG(DBG_MMC, "Could not get multimedia commands (MMC) interface\n");
        return -1;
    }

784
    DEBUG(DBG_MMC, "Have an MMC interface (%p). Getting a SCSI task interface...\n", (void*)mmc->mmcInterface);
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802

    mmc->taskInterface = (*mmc->mmcInterface)->GetSCSITaskDeviceInterface (mmc->mmcInterface);
    if (NULL == mmc->taskInterface) {
        DEBUG(DBG_MMC, "Could not get SCSI task device interface\n");
        return -1;
    }

    return 0;
}

int mmc_open_iokit (const char *path, MMC *mmc) {
    io_service_t service;
    int rc;

    mmc->mmcInterface = NULL;
    mmc->taskInterface = NULL;
    mmc->disk = NULL;
    mmc->session = NULL;
803
    mmc->is_mounted = true;
804

805
    /* get the bsd name associated with this mount */
806
807
808
809
810
811
    rc = get_mounted_device_from_path (mmc, path);
    if (0 != rc) {
        DEBUG(DBG_MMC, "Could not locate mounted device associated with %s\n", path);
        return rc;
    }

812
    /* find a matching io service (IOBDServices) */
813
814
815
816
817
818
    rc = iokit_find_service_matching (mmc, &service);
    if (0 != rc) {
        DEBUG(DBG_MMC, "Could not find matching IOBDServices mounted @ %s\n", path);
        return rc;
    }

819
    /* find mmc and scsi task interfaces */
820
    rc = iokit_find_interfaces (mmc, service);
821
822
823
824

    /* done with the ioservice. release it */
    (void) IOObjectRelease (service);

825
826
827
828
    if (0 != rc) {
        return rc;
    }

829
830
    /* unmount the disk so exclusive access can be obtained (this is required
       to use the scsi task interface) */
831
832
833
834
835
    rc = iokit_unmount (mmc);
    if (0 != rc) {
        return rc;
    }

836
    /* finally, obtain exclusive access */
837
838
839
840
841
842
843
844
845
846
847
848
    rc = (*mmc->taskInterface)->ObtainExclusiveAccess (mmc->taskInterface);
    if (kIOReturnSuccess != rc) {
        DEBUG(DBG_MMC, "Failed to obtain exclusive access. rc = %x\n", rc);
        return -1;
    }

    DEBUG(DBG_MMC, "MMC Open complete\n");

    return 0;
}
#endif

npzacs's avatar
npzacs committed
849
MMC *mmc_open(const char *path)
850
{
npzacs's avatar
npzacs committed
851
    MMC *mmc = calloc(1, sizeof(MMC));
npzacs's avatar
npzacs committed
852

npzacs's avatar
npzacs committed
853
    crypto_create_nonce(mmc->host_nonce, sizeof(mmc->host_nonce));
854

npzacs's avatar
npzacs committed
855
    if (DEBUG_KEYS) {
856
857
858
859
860
        char str[sizeof(mmc->host_nonce)*2 + 1];
        DEBUG(DBG_MMC, "Created host nonce (Hn): %s\n",
              print_hex(str, mmc->host_nonce, sizeof(mmc->host_nonce)));
    }

npzacs's avatar
npzacs committed
861
    crypto_create_host_key_pair(mmc->host_key, mmc->host_key_point);
862

npzacs's avatar
npzacs committed
863
    if (DEBUG_KEYS) {
864
865
866
867
868
869
870
        char    str[sizeof(mmc->host_key_point)*2 + 1];
        DEBUG(DBG_MMC, "Created host key (Hk): %s\n",
              print_hex(str, mmc->host_key, sizeof(mmc->host_key)));
        DEBUG(DBG_MMC, "Created host key point (Hv): %s\n",
              print_hex(str, mmc->host_key_point, sizeof(mmc->host_key_point)));
    }

871
#if defined(USE_IOKIT)
Ano Nymous's avatar
Ano Nymous committed
872
    int rc = mmc_open_iokit (path, mmc);
873
874
875
876
877
878
    if (0 != rc) {
        mmc_close (mmc);
        return NULL;
    }

#elif defined(HAVE_MNTENT_H)
879

npzacs's avatar
npzacs committed
880
881
    char file_path [AACS_PATH_MAX];
    if (!aacs_resolve_path(path, file_path)) {
882
        DEBUG(DBG_MMC | DBG_CRIT, "Failed resolving path %s\n", path);
883
884
885
        X_FREE(mmc);
        return NULL;
    }
npzacs's avatar
npzacs committed
886

887
888
889
    int   path_len  = strlen(file_path);
    FILE *proc_mounts;

npzacs's avatar
npzacs committed
890
    mmc->fd = -1;
npzacs's avatar
npzacs committed
891
892

    // strip trailing '/'s
893
894
    while (path_len > 0 && file_path[--path_len] == '/') {
        file_path[path_len] = '\0';
npzacs's avatar
npzacs committed
895
    }
896

897
    DEBUG(DBG_MMC, "Opening LINUX MMC drive %s...\n", file_path);
898

899
900
901
902
    if ((proc_mounts = setmntent("/proc/mounts", "r"))) {
        struct mntent* mount_entry;

        while ((mount_entry = getmntent(proc_mounts)) != NULL) {
903
            if (strcmp(mount_entry->mnt_dir, file_path) == 0) {
npzacs's avatar
npzacs committed
904
905
                mmc->fd = open(mount_entry->mnt_fsname, O_RDONLY | O_NONBLOCK);
                if (mmc->fd >= 0) {
906
907
                    DEBUG(DBG_MMC, "LINUX MMC drive %s opened - fd: %d\n",
                          mount_entry->mnt_fsname, mmc->fd);
npzacs's avatar
npzacs committed
908
                    break;
npzacs's avatar
npzacs committed
909
                }
npzacs's avatar
npzacs committed
910

911
912
                DEBUG(DBG_MMC, "Failed opening LINUX MMC drive %s mounted to %s\n",
                      mount_entry->mnt_fsname, file_path);
npzacs's avatar
npzacs committed
913
            }
914
915
916
        }

        endmntent(proc_mounts);
npzacs's avatar
npzacs committed
917
    } else {
918
        DEBUG(DBG_MMC, "Error opening /proc/mounts\n");
919
920
    }

npzacs's avatar
npzacs committed
921
    if (mmc->fd < 0) {
922
        DEBUG(DBG_MMC | DBG_CRIT, "Error opening LINUX MMC drive mounted to %s\n", file_path);
npzacs's avatar
npzacs committed
923
924
925
        X_FREE(mmc);
    }

npzacs's avatar
npzacs committed
926
927
928
929
#elif defined(_WIN32)
    char drive[] = { path[0], ':', '\\', 0 };
    char volume[] = {'\\', '\\', '.', '\\', path[0], ':', 0};

930
    DEBUG(DBG_MMC, "Opening Windows MMC drive %s...\n", drive);
npzacs's avatar
npzacs committed
931
932
933
934

    UINT type = GetDriveType(drive);

    if (type != DRIVE_CDROM) {
npzacs's avatar
npzacs committed
935
        DEBUG(DBG_MMC | DBG_CRIT, "Drive %s is not CD/DVD drive\n", drive);
npzacs's avatar
npzacs committed
936
937
938
939
940
941
942
943
944
945
946
947
        X_FREE(mmc);
        return NULL;
    }

    mmc->fd = CreateFile(volume, GENERIC_READ | GENERIC_WRITE,
                         FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
                         OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (mmc->fd == INVALID_HANDLE_VALUE) {
        mmc->fd = CreateFile(volume, GENERIC_READ,
                             FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
                             OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
        if (mmc->fd == INVALID_HANDLE_VALUE) {
948
            DEBUG(DBG_MMC, "Failed opening Windows MMC drive %s\n", volume);
npzacs's avatar
npzacs committed
949
950
951
952
953
            X_FREE(mmc);
            return NULL;
        }
    }

954
    DEBUG(DBG_MMC, "Windows MMC drive %s opened\n", volume);
npzacs's avatar
npzacs committed
955

956
957
#else

npzacs's avatar
npzacs committed
958
    DEBUG(DBG_MMC | DBG_CRIT, "No MMC drive support\n");
959
960
    X_FREE(mmc);

cRTrn13's avatar
cRTrn13 committed
961
#endif
cRTrn13's avatar
cRTrn13 committed
962

963
964
    if (mmc && !_mmc_check_aacs(mmc)) {
        DEBUG(DBG_MMC | DBG_CRIT, "AACS not active or supported by the drive\n");
965
#ifndef _WIN32
966
967
        mmc_close (mmc);
        return NULL;
968
#endif
969
    }
970

971
972
973
974
    if (mmc && mmc->read_drive_cert) {
        mmc_read_drive_cert(mmc, mmc->drive_cert);
    }

975
    return mmc;
976
977
978
979
}

void mmc_close(MMC *mmc)
{
npzacs's avatar
npzacs committed
980
    if (mmc) {
981

npzacs's avatar
npzacs committed
982
#if defined(HAVE_LINUX_CDROM_H)
npzacs's avatar
npzacs committed
983
984
985
        if (mmc->fd >= 0) {
            close(mmc->fd);
        }
986

npzacs's avatar
npzacs committed
987
988
989
990
#elif defined(_WIN32)
        if (mmc->fd != INVALID_HANDLE_VALUE) {
            CloseHandle(mmc->fd);
        }
991
992
993
994
995
996
997
998
999
1000
#elif defined(USE_IOKIT)
        if (mmc->taskInterface) {
            (*mmc->taskInterface)->ReleaseExclusiveAccess (mmc->taskInterface);
            (*mmc->taskInterface)->Release (mmc->taskInterface);
            mmc->taskInterface = NULL;
        }

        if (mmc->mmcInterface) {
            (*mmc->mmcInterface)->Release (mmc->mmcInterface);
            mmc->mmcInterface = NULL;